Sammlung nützlicher Funktionen zur Wiederverwendung von Code

Hallo allerseits,

ich war in den letzten Monaten damit beschäftigt, meine zahlreichen Scripte zu optimieren und reorganisieren. Dabei habe ich mir selbst den Anspruch auferlegt, verwendeten PHP-Code so zu gestalteten, dass er möglichst oft - insbesondere für gleichartige Devices - wiederverwendet werden kann; also ohne direkten Bezug auf Instanzen oder Variablen auskommt.

Der Vorteil ist, dass man Scripte einfach per Copy und Paste irgendwo unterhalb einer bestimmten Instanz oder Variable legt und diese dann dynamisch referenzieren kann.

Herausgekommen ist eine Sammlung von Hilfsfunktionen, an denen ich Euch gerne teilhaben lassen möchte. Vielleicht ist es für den ein- oder anderen nützlich. Ich werde kontinuierlich weitere Funktionen veröffentlichen, die mir das Leben etwas leichter machen :wink:

Zur Verwendung: Am Besten ihr kopiert den Code der Funktionen, die ihr verwenden wollt, alle in ein einziges Script, welches ihr dann jeweils in eure Scripte includen könnt. Bei Fragen und Anregungen bitte melden :D.

Diese Funktion liefert die nächste im Baum übergeordnete InstanceID eines beliebigen Objektes (welches eben irgendwo ggf. verschachtelt unterhalb dieser Instanz liegt).

Beispiel:

$instanceID = SYS_GetParentInstanceID($_IPS["SELF"]);
$instanceID = SYS_GetParentInstanceID(12345);

Funktion:

function SYS_GetParentInstanceID($objectID)
 //Ermittelt die erste übergordnete Instanz der übergebenen ObjektID
 //Übergabe: [INT]$objectID = ID des Objektes, zu welcher die erste übergeordnete Instanz ermittelt werden soll
 //Rückgabe: [INT] = InstanzID der ersten gefundenen übergeordneten Instanz, Wert 0 bedeutet, dass keine übergeordnete Instanz ermittelt werden kann
 {
    //Fehlerbehandlung
  if(!isset($objectID)) die("Error in Function ".__FUNCTION__." - Missing argument for \$objectID");
  if(!is_int($objectID)) die("Error in Function ".__FUNCTION__." - Value for parameter \$objectID is not integer");
  if($objectID < 10000 or $objectID > 59999) die("Error in Function ".__FUNCTION__." - The value for \$objectID must between 10000 and 59999");  
//Prüfen, ob das Übergebene Objekt selbst eine Instanz ist
  $instanceID = 0;
  $object = IPS_GetObject($objectID);
  if($object["ObjectType"] == 1) $instanceID = $objectID;
  //Alle übergeordneten Objekte durchgehen
  while($instanceID == 0)
  {
   //ParentID holen
   $parentID = @IPS_GetParent($objectID);
   
   //ParentID gefunden
   if($parentID)
   {
    //Prüfen, ob es sich hierbei um eine Instanz handelt.
    $object = IPS_GetObject($parentID);
    if($object["ObjectType"] == 1) $instanceID = $parentID; else $objectID = $parentID;
   }
   else
   {
    //Schleife abbrechen
    break;
   }
  }
  
  //Rückgabe
  return $instanceID;
 }

… das Gleiche für eine irgendwo im Baum übergeordnete Variable.

Beispiel:

$variableID = SYS_GetParentVariableID($_IPS["SELF"]);
$variableID = SYS_GetParentVariableID(12345);

Funktion:

 function SYS_GetParentVariableID($objectID)
 //Ermittelt die erste übergordnete Variable der übergebenen ObjektID
 //Übergabe: [INT]$objectID = ID des Objektes, zu welcher die erste übergeordnete Variable ermittelt werden soll
 //Rückgabe: [INT] = VariableID der ersten gefundenen übergeordneten Variable
 {
    //Fehlerbehandlung
  if(!isset($objectID)) die("Error in Function ".__FUNCTION__." - Missing argument for \$objectID");
  if(!is_int($objectID)) die("Error in Function ".__FUNCTION__." - Value for parameter \$objectID is not integer");
  if($objectID < 10000 or $objectID > 59999) die("Error in Function ".__FUNCTION__." - The value for \$objectID must between 10000 and 59999");
  //Prüfen, ob das Übergebene Objekt selbst eine Variable ist
  $variableID = 0;
  $object = IPS_GetObject($objectID);
  if($object["ObjectType"] == 2) $variableID = $objectID;
  //Alle übergeordneten Objekte durchgehen
  while($variableID == 0)
  {
   //ParentID holen
   $parentID = @IPS_GetParent($objectID);
   
   //ParentID gefunden
   if($parentID)
   {
    //Prüfen, ob es sich hierbei um eine Variable handelt.
    $object = IPS_GetObject($parentID);
    if($object["ObjectType"] == 2) $variableID = $parentID; else $objectID = $parentID;
   }
   else
   {
      //Schleife abbrechen
    break;
   }
  }
  
  //Rückgabe
  return $variableID;
 }

Manchmal möchte man in einem Script entsprechend reagieren, wenn es innerhalb einer bestimmten Zeit schon einmal ausgeführt wurde. Ein Beispiel: Man möchte bei einem Türklingel-Script verhindern, dass jemand sturm klingelt. Oder man will erzwingen, dass eine Push-Nachricht bei einem bestimmten Ereignis maximal alle 5 Minuten gesendet wird. Das nachfolgende Beispiel prüft, ob das eigene Script innerhalb der letzten 60 Sekunden schon einmal ausgeführt wurde. Ist dies der Fall, wird true zurückgegeben, ansonsten false.

Beispiel:

$executed = SYS_CheckLastScriptExecute($_IPS["SELF"],60);

Funktion:

 function SYS_CheckLastScriptExecute($scriptID,$time)
 //Überprüft, ob das Script in der angegebenen Zeit (Sekunden) ausgeführt wurde
 //Übergabe: [INT]$scriptID = ID des Scriptes, dessen Ausführung ermittelt werden soll, [INT]$time = Anzahl Sekunden, die geprüft werden
 //Rückgabe: [BOOL] = {true=Script wurde innerhalb der übergebenen Zeit schon einmal ausgeführt; false=Script wurde innerhalb der übergebenen Zeit nicht ausgeführt}
 {
    //Fehlerbehandlung
    if(!isset($scriptID) or !isset($time)) die("Error in Function ".__FUNCTION__." - Missing arguments, function expects 2 values");
  if(!is_int($scriptID)) die("Error in Function ".__FUNCTION__." - Value for parameter \$scriptID is not integer");
  if(!is_int($time)) die("Error in Function ".__FUNCTION__." - Value for parameter \$time is not integer");
  if($scriptID < 10000 or $scriptID > 59999) die("Error in Function ".__FUNCTION__." - The value for \$scriptID must be between 10000 and 59999");
  if(!IPS_ScriptExists($scriptID)) die("Error in Function ".__FUNCTION__." - \$scriptID ".$scriptID." is not a script");
  
  //Auslesen der des LastExecute Parameters des Objekts
  $lastExecute = IPS_GetScript($scriptID)["ScriptExecuted"];
  
  //Wurde das Script innerhalb der übermittelten Zeit ausgeführt
  if((time() - $lastExecute) <= $time) return true; else return false;
 }

Diese Funktion erzeugt ein individuelles Logfile im Standard-Log-Ordner von IP-Symcon oder einem absoluten Pfad. Verwendet werden kann dies z.B. für das „Wegschreiben“ von Wetterdaten, Aufzeichnen einer Anruferliste, etc.

Nachfolgendes Beispiel legt im IP-Symcon-Log-Ordner ein Logfile an, welches die Spaltenbezeichner Datum, Rufnummer und Gesprächsdauer beinhaltet.

Beispiel:

SYS_WriteLogFile("Anrufliste.csv",array("Datum","Rufnummer","Gesprächsdauer"),array(date("d.m.Y",time()),"01234/56789","2:35"));
SYS_WriteLogFile("Anrufliste.csv",array("Datum","Rufnummer","Gesprächsdauer"),array(date("d.m.Y",time()),"12345/67890","1:28"));
SYS_WriteLogFile("Anrufliste.csv",array("Datum","Rufnummer","Gesprächsdauer"),array(date("d.m.Y",time()),"0987/65432","13:05"));

Funktion:

 function SYS_WriteLogFile($fileName,$columns,$data,$absolutePath = false)
 //Schreibt einen tabulatorgetrennten Datensatz in das Logfile im Standard-Log-Ordner von IP-Symcon. Wenn die Datei nicht existiert, wird sie angelegt.
 //Übergabe: [STRING]$fileName = Name der zu erzeugenden Log-Datei, [ARRAY]$colums = Spaltenbezeichner, [ARRAY]$data = Datensatz
 //Übergabe: [BOOL]$absolutePath = {true=Angabe des absoluten Pfades; false=Datei wird im Standard-Log-Ordner von IP-Symcon abgelegt}
 //Rückgabe: [BOOL] = {true=Erfolgreich; false=Fehlgeschlagen}
 {
    //Fehlerbehandlung
    if(!isset($fileName) or !isset($columns) or !isset($data)) die("Error in Function ".__FUNCTION__." - Missing arguments, function expects 3 values");
  if(!is_string($fileName)) die("Error in Function ".__FUNCTION__." - Value for parameter \$fileName is not a string");
  if(!is_array($columns)) die("Error in Function ".__FUNCTION__." - Value for parameter \$columns must be an array");
  if(!is_array($data)) die("Error in Function ".__FUNCTION__." - Value for parameter \$data must be an array");
  //Pfad bestimmen
  if($absolutePath) $filePath = $fileName; else $filePath = IPS_GetLogDir().$fileName;
  //Wenn die Datei nicht existiert, wird sie angelegt
  if(!file_exists($filePath))
  {
    //Datei zum Schreiben öffnen
      if(!$handle = fopen($filePath,"w")) die("Error in Function ".__FUNCTION__." - Can not create ".$filePath);
   //String für Spaltenbezeichner
   $caption = "";
   //Spaltenbezeichner aus dem übergebenen Array als tabulatorgetrennter String abbilden
      foreach($columns as $value) $caption .= $value.chr(9);
   //Zeile mit Spaltenbezeichner schreiben
   if(!fwrite($handle,utf8_decode($caption).chr(13))) die("Error in Function ".__FUNCTION__." - Can not write caption in File ".$filePath);
   //Datei schliessen
   fclose($handle);
   
   //Kurze Wartezeit, um die Datei zu schliessen
   IPS_Sleep(1000);
  }
  //Prüfen, ob die Datei schreibbar ist
  if(is_writable($filePath))
  {
      if(!$handle = fopen($filePath,"a")) die("Error in Function ".__FUNCTION__." - Can not open ".$filePath);
   //String für Daten
   $line = "";
   //Datensatz aus dem Array in tabulatorgetrennten String schreiben
   foreach($data as $value) $line .= $value.chr(9);
   //Zeile mit Datensatz schreiben
      if (!fwrite($handle,utf8_decode($line).chr(13))) die("Error in Function ".__FUNCTION__." - Can not write data in File ".$filePath);
     
      //Datei schliessen
   fclose($handle);
  }
  else die("Error in Function ".__FUNCTION__." - ".$filePath." is not writeable or does not exist");
  
  //Rückgabe
  return true;
 }

Manchmal benötigt man individuelle Parameter für eine Instanz, um diese in Scripten weiter zu verarbeiten. Zwei Beispiele:

Ihr habt mehrere Rollladenaktoren und wollt diese per Script auf eine bestimmte Stellung fahren (z.B. halb oder dreiviertel). Bei den meisten mir bekannten Aktoren wird eine Rollladenstellung aber in % angegben. Nun ist aber - je nach Länge der Rollladen - 75 % rein visuell nicht dreiviertel. Hat man jetzt mehrere unterschiedlich lange Rollladen (z.B. Terrassentür und ein normales Fenster), dann sind bei dem einen Aktor z.B. eine dreiviertel Stellung gleich 60%, bei dem anderen vielleicht 85%. Man müsste jetzt also pro „Rollladentyp“ jeweils ein angepasstes Script verwenden, oder irgendwo eine Parametertabelle pflegen, aus der man dann in einem universellen Script darauf zugreifen kann.

Oder ihr habt Dummy-Instanzen von Netzwerk-Objekten (z.B. Computer, Switch, etc.), wo ihr den jeweiligen Netzwerknamen oder IP-Adressen hinterlegen wollt, um mit einem universellen Script darauf zuzugreifen (z.B. um zu reagieren, ob das Gerät per Ping erreichbar ist). Auch hier müsstet Ihr die IP-Adresse oder den FQDN fest im Script hinterlegen, damit müsstet Ihr wieder für jedes Gerät ein angepasstes Script vorhalten.

Bei diesen Szenarien wäre es super, wann man bei einer Instanz einfach weitere individuelle Parameter hinterlegen könnte. Es gibt aber hier keine „Custom-Attributes“ :slight_smile: … deswegen habe ich einfach das „Beschreibungsfeld“ der Instanz auserkoren, welches ich sowieso nicht verwende und mir eine Funktion gebaut, die diese Parameter per Key-Value-Paare im Script bereitstellen kann.

Ihr müsst, um dies ebenfalls zu verwenden, das Beschreibungsfeld z.B. wie folgt füllen (Beispiel Netzwerkinformationen):

FQDN: computer1.domain.local
IPAddress: 192.168.0.55
MacAddress: 0123456789AB
Description: Computer Micha

Danach legt Ihr unterhalb dieser Instanz ein Script an und könnt mit folgenden Beispielen auf die Parameter zugreifen.

Beispiel:

$parameterArray = SYS_GetParentInstanceParameter($_IPS["SELF"]);
$ip = SYS_GetParentInstanceParameter($_IPS["SELF"])["IPAddress"];

Selbstverständlich könnt ihr auch eine Instanz-ID oder eine Objekt-ID, welche irgendwo unterhalb der Instanz liegt, angeben, falls das Script nicht direkt unterhalb dieser Instanz liegt.

Beispiel:

$fqdn = SYS_GetParentInstanceParameter(12345)["FQDN"];

Und hier die Funktion dazu. Diese verwendet allerdings die ebenfalls hier gepostete Funktion SYS_GetParentInstanceID, welche ihr dann ebenfalls im Include-File bereitstellen müsst.

Funktion:

 function SYS_GetParentInstanceParameter($objectID)
 //Ermittelt die Übergeordnete Instanz und liefert deren Parameter aus dem Beschreibungsfeld
 //Übergabe: [INT]$objectID = ID des Objektes, aus welcher der ersten übergeordneten Instanz die Parameter ermittelt werden sollen
 //Rückgabe: [ARRAY] = Parameter als key => value Paare, [STRING] = Inhalt des Beschreibungsfeldes, [BOOL] = {false=Keine Parameter enthalten oder keine übergeordnete Instanz gefunden}
 {
    //Fehlerbehandlung
  if(!isset($objectID)) die("Error in Function ".__FUNCTION__." - Missing argument for \$objectID");
  if(!is_int($objectID)) die("Error in Function ".__FUNCTION__." - Value for parameter \$objectID is not integer");
  if($objectID < 10000 or $objectID > 59999) die("Error in Function ".__FUNCTION__." - The value for \$objectID must be between 10000 and 59999");
  
  //Parent InstanceID ermitteln
  $parentInstanceID = SYS_GetParentInstanceID($objectID);
  
  //Keine übergeordnete Instanz gefunden
  if(!$parentInstanceID) return false;
  
  //Beschreibungsfeld lesen
  $object = IPS_GetObject($parentInstanceID);
  $description = $object["ObjectInfo"];
  
  //Falls keine Beschreibung existiert, false zurückgeben
  if(!$description) return false;
  
  //Falls : gefunden wurde, handelt es sich (vermutlich) um Parameter
  if(strpos($description,":"))
  {
   //Rückgabevariable ist ein Array
   $return = array();
   //Parameter vereinzeln
   $parameterArray = explode("
",$description);
   //Parameter durchzählen
   foreach($parameterArray as $parameter)
   {
    //Key => Value ermitteln und in Array schreiben
    $keyValue = explode(":",$parameter);
    
    //Parameter zum Array hinzufügen
    $return[trim($keyValue[0])] = trim($keyValue[1]);
   }
  }
  //Es ist (vermutlich) ein einfacher String
  else
  {
   //String als Rückgabeparameter setzen
   $return = strval($description);
  }
  //Rückgabe
  return $return;
 }