Raumvariablen standardisieren (Baumstruktur)

Hallo Allerseits

Ich habe ein Projekt, das klein angefangen hat, aber jetzt ein unscheinbares Ausmaß annimmt. Doch kurz worum geht es?
Ich wollte alle Räume unabhängig von der verbauten Hardware standardisieren. D.h. Jeder Raum hat je eine Variable für Temperatur, Feuchtigkeit, Fenster, Helligkeit usw.

Dies hat soweit erstmal geklappt:

Alle Daten sind als Links unterhalb gespeichert. Über ein Script werden diese Variablen mit echten Werten gefüllt. (Hier gerade noch exemplarisch) Falls mehrere Links z.B. auf Temperaturfühler vorhanden sind wird der Mittelwert berechnet und dort geloggt. Das soll auch der Hauptvorteil sind. Dass wenn z.B. ein Temperaturfühler ausgetauscht wird (FS20 zu Homematic) muss nur der Link darunter angepasst werden. Die Visu und auch die Scripte arbeiten nur mit der „Raum Standard Variable“

Vorteile:

  • Einfache Wartung
  • Austausch der Hardware macht minimalen Aufwand. (Nur einen Link anpassen)
  • Variablen werden nur an einer Stelle geloggt. Also in dem Raumstandard
  • Variablen können auf Nachkommastellen getrimmt werden damit nicht unnötig oft geloggt wird. Beispielsweise bei 23,039832 °C und einen Minute später 23,039838 °C
  • Intervalle vom Loggen sind Herstellerunabhängig. (Manche alle 10 Sek andere 5 Min) dies würde auch wegfallen
  • Funktionen die mit dem Raum zu tun haben sind auch dort mit abgelegt.
  • Durchschnittswerte können von Haus zu Etage zu Raum heruntergebrochen werden.

Analog dazu funktioniert es auch mit Feuchtigkeit, Helligkeit usw.

Auch für Beleuchtung habe ich eigentlich schon ein fertig funktionierendes Dynamisches Script, das Räume Gruppenweise Herstellerunabhänigig schalten kann.

Damit habe ich mir ähnlich wie das IPS-Light Modul die Funktion im Endeffekt sehr schlank nachgebaut.
Mit Erdgeschoss aus macht alles was unterhalb der Gruppe ist aus oder auch ein. Je nachdem wie man will.

Meine Hürden

  • Aufbau der Baumstucktur. Wie macht es am meisten Sinn? gerade bei Verschachtelungen
  • Werte sollen nach oben hin weitergegeben werden und verrechnet werden. (z.B. Temperaturen Durchschnitt, Stromverbrauch addieren)
  • Stuktur soll ohne festgelegte IDs auskommen. d.h. alles Dynamisch (fast komplett umgesetzt)

Jetzt würde ich diese ganzen Funktionen gerne in einen Standardbaukasten abbilden. Hätte jemand Lust dabei zu helfen? bzw. vielleicht sogar ein Modul mit zu bauen. Da fehlt mir nämlich noch der Umgang.

Die Scripte stelle ich natürlich zur Verfügung

Auf eine Rückmeldung würde ich mich sehr freuen.
Die IPS Light Module kenne ich, aber eher etwas Overload mit dem Logger usw. bzw. werden die Funktionen die ich mir wünsche nicht unterstützt.

Keiner eine Idee?

Ich verstehe was du machst, mache es technisch anders, aber mit selben Gründen zum Großteil (siehe hier: Nützliche Programmierpattern, Strukturierungen etc.)

aber ich vermute du wirst hier wenig Beistand bekommen, denn ich denke in dieser Art wird es von wenigen genutzt.

Wie gesagt habe ich es für mich anders umgesetzt, Virtuelle Geräte wo nur einmalig die ID auf das original-Gerät verzweigt, keine „Standards“ für Räume, denn dafür sie sie mir zu unterschiedlich, aber auch ähnliche Funktionen, z.B. Licht einer ganzen Etage aus. Für diese gleichartigen Aktionen habe ich aber eigene Strukturen (einmal Licht, einmal Beschattung etc. ) und arbeite dort mit Links auf meine virtuellen Geräte.

Vielen Dank für die Antwort, könnte man sich das mal etwas genauer anschauen?

aktuell hab ich es so gemacht des Funktioniert halt mit Lichtern als Gruppenschaltung.

Das funktioniert super und zuverlässig. Ich würde das halt gerne mit Temperatur Feuchte usw auch so verschachteln.
Vermutlich muss ich die Zimmerstruktur einfach parallel für jede Art von Messung aufbauen.

Warun nutzt Du nicht die Funktion Objekt verlinken ?

Weil verlinken bei den Problemen so gar nicht hilft, zumindest wenn der Link auf die echten Hardware-Module geht. Wenn man einmal die vielen Links, wo man auch plötzlich nicht mal mehr weiss worauf sie gezeigt haben austauschen soll oder alle Scripte und Ereignisse durchgehen muss und die IDs tauschen muss, nur weil Aktor sich verabschiedet hat und ausgetauscht wurde, weiss man warum es nicht hilft.

@tommy86: Klar kann ich da noch genauer drauf eingehen. Eigentlich wollte ich für meine „virtuellen Geräte“ auch ein Modul bauen, dass diese automatisch anlegt, bzw. habe ich das sogar, aber es mangelt an den letzten 5% fertig stellen und austesten.

ok das klingt ja schon mal echt gut.

Ich hab das bei mir dynamisch in functionen gelöst, die sich automatisch selbst ineinander verschachteln, bis sie das Ende erreicht haben.

Ablauf:
Schalten eines Geräts macht folgendes
Im Namen steht der Hersteller, die Art des Aktors und ID des Gerätes.
also wird dann der für den Hersteller benötigten Befehl geschalten.

anschließend werden alle „Nachbar“ Geräte auf der selben Ebene angeschaut und der Status auf den „Vater“ Vererbt. Anschließend wird die Function wieder aufgerufen bis das letzte Element erreicht ist.

Natürlich kann auch die Gruppe ausgeschalten werden. Dazu wird der Status nach unten weiter vererbt, bis man auf eine Variable für ein Gerät stößt. Dieses wird dann geschalten. Wenn das Gerät schon den Zielstatus hat wird dieser übersprungen.

Funktioniert absolut zuverlässig.

Script anlegen mit Namen „function Gruppenschaltung“

<?
// -----------------------------------------------------------------------------
// Die Hauptfunktion, die die unteren Funktionen aufruft
// by Thomas Pößnecker
// -----------------------------------------------------------------------------

function Schalten ($StartID, $ValueNew){
	set_time_limit(200); 								// Timeout setzen damit auch alle Lichter geschalten werden	
	setvalue($StartID, $ValueNew);

	// Debug Zeit
	//$beginn = microtime(true); 

	
	NachUntenAktualisieren ($StartID, $ValueNew);
	IPS_Sleep(0);
	SchalterAbfrage ($StartID, $ValueNew);
	IPS_Sleep(0);
	NachObenAktualisieren  ($StartID);
	
	// Nochmal aktualisieren wegen überschneidungen
	IPS_Sleep (100);
	setvalue($StartID, $ValueNew);
	//NachObenAktualisieren  ($StartID);
	
	// Debug Zeit	
	//$dauer = round(((microtime(true) - $beginn) * 1000),2); 
	//setvalue (13655 /*[System Dienste\Debug Varialben Info\Debug Variable]*/, "$dauer ms");round($VorgabeID / 100 * 255 , 0)
}

// Schaltvorgänge
// -----------------------------------------------------------------------------
function SchaltenHersteller ($SchalterID, $ValueNew){
		$Bezeichnung = IPS_GetName ($SchalterID);
		list($Name, $Rest) = explode(" @ ", $Bezeichnung);
		list($Aktorart, $Hersteller, $ZielID) = explode(" - ", $Rest);

		// Zusätzliche Aufteilung, wenn die Lampe eine RGB Lampe ist dann Zustätzlich schauen, welche Einschaltfarbe
		if ($Aktorart == "RGBW" or $Aktorart == "RGBC") {list($ZielID, $VorgabeID) = explode(" | ", $ZielID);}else{}


	// Je Nach Typ und Hersteller schalten
	switch ($Hersteller){
		case "HOMEMATIC":
			$Wartezeit = 5;
			
			switch ($Aktorart){
			   case "SWITCH":
			   		if (getvalue (IPS_GetVariableIDByName("STATE", $ZielID)) == $ValueNew){goto Ueberspringen;}else{}
			   			HM_WriteValueBoolean ($ZielID, "STATE", $ValueNew);
					break;
			   case "DIMMER":
			   		break;
			}
			break;


		case "HOMEMATICIP":
			$Wartezeit = 5;
			
			switch ($Aktorart){
				case "SWITCH":
					if (getvalue (IPS_GetVariableIDByName("STATE", $ZielID)) == $ValueNew){goto Ueberspringen;}else{}
						HM_WriteValueBoolean ($ZielID, "STATE", $ValueNew);
					break;
				case "DIMMER":
					break;
			}
			break;


		case "SHELLY":
			$Wartezeit = 0;
			
			switch ($Aktorart){
				case "PLUG":
					if (getvalue (IPS_GetVariableIDByName("Status", $ZielID)) == $ValueNew){goto Ueberspringen;}else{}
						Shelly_SwitchMode ($ZielID, 0, $ValueNew);
					break;
				case "SWITCH":
					break;
			}
			break;


		case "FS20":
			$Wartezeit = 250;
			
			switch ($Aktorart){
				case "SWITCH": 
					if (getvalue (IPS_GetVariableIDByName("Status", $ZielID)) == $ValueNew){goto Ueberspringen;}else{}
						FS20_SwitchMode ($ZielID,$ValueNew);
						FS20_SwitchMode ($ZielID,$ValueNew);
					break;
				case "DIMMER":
					if (getvalue (IPS_GetVariableIDByName("Status", $ZielID)) == $ValueNew){goto Ueberspringen;}else{}
						FS20_SwitchMode ($ZielID,$ValueNew);
						FS20_SwitchMode ($ZielID,$ValueNew);
					break;
			}
			break;


		case "PROJET":
			$Wartezeit = 0;


			switch ($Aktorart){
				case "RGBC":
					$White = getvalue (IPS_GetVariableIDByName("White", $ZielID));
					$Color = getvalue (IPS_GetVariableIDByName("Color", $ZielID));
					if ($ValueNew == true){
						// Einschaltfarbe aus Variable lesen umrechnen und dann setzen
						$zahl = dechex(getvalue ($VorgabeID));
						$zahl = str_pad($zahl, 6 ,'0', STR_PAD_LEFT);
						$r    = hexdec(substr($zahl, 0, 2));
						$g    = hexdec(substr($zahl, 2, 2));
						$b    = hexdec(substr($zahl, 4, 2));
						PJ_SetRGBW ($ZielID, $r, $g, $b, $White);
						}

					if ($ValueNew == false) {
						PJ_SetRGBW ($ZielID, 0, 0, 0, $White);
						}

					break;
			
				case "RGBW":
					// Bei Weis warten bis Farbe erstmal fertig ist
					IPS_Sleep(100);
					$White = getvalue (IPS_GetVariableIDByName("White", $ZielID));
					$Color = getvalue (IPS_GetVariableIDByName("Color", $ZielID));
					if ($ValueNew == false) {
						$zahl = dechex($Color);
						$r    = hexdec(substr($zahl, 0, 2));
						$g    = hexdec(substr($zahl, 2, 2));
						$b    = hexdec(substr($zahl, 4, 2));
						PJ_SetRGBW ($ZielID, $r, $g, $b, 0);
						}

					if ($ValueNew == true){
						$zahl = dechex($Color);
						$r    = hexdec(substr($zahl, 0, 2));
						$g    = hexdec(substr($zahl, 2, 2));
						$b    = hexdec(substr($zahl, 4, 2));
						PJ_SetRGBW ($ZielID, $r, $g, $b, round(getvalue($VorgabeID) / 100 * 255 , 0));
						}

					break;

			}
			break;



		case "MAGICHOME":
			$Wartezeit = 0;
			switch ($Aktorart){
				case "RGB":
					if (getvalue (IPS_GetVariableIDByName("Aktiv", $ZielID)) == $ValueNew){goto Ueberspringen;}else{}
						if ($ValueNew == true){
						MHC_SetPower($ZielID, true);
						}
						
						if ($ValueNew == false){
						MHC_SetPower($ZielID, false);
						}
						
					break;
			}
			break;
	}


goto Ende;


Ueberspringen:
	// Wenn der Aktor schon den richtigen Zustand hat, dann wird dieser übersprungen.
	$Wartezeit = 0;	

Ende:
	IPS_Sleep ($Wartezeit);
// Ende der Funktion	

}


// Abfrage ob, das Ziel ein Aktor ist.
// -----------------------------------------------------------------------------
function SchalterAbfrage ($SchalterID, $ValueNew){

	// Bezeichnung auslesen
	$Bezeichnung = IPS_GetName ($SchalterID);
	$WertAktuell = getvalue ($SchalterID);

	// Abfrage ob Codezeichen @ vorhanden ist. Dieses Steht für einen Schalter
	if(preg_match("/@/",$Bezeichnung)) {

		// Trennen nach Bezeichnung und nach ID
		list($Name, $Rest) = explode(" @ ", $Bezeichnung);
		list($Art, $Hersteller, $ZielID) = explode(" - ", $Rest);

		SchaltenHersteller ($SchalterID, $ValueNew);
  	}

// Ende der Funktion
}


// Aktualisiert die Werte in der Baumstruktur nach unten
// -----------------------------------------------------------------------------
function NachUntenAktualisieren ($IDStart, $ValueNew){
	// Schaltet alle nach Unten
	foreach (IPS_GetChildrenIDs($IDStart) as &$Unterobjekt) {

		// Abfrage ob Unterobjekt ein Schalter ist
		SchalterAbfrage ($Unterobjekt, $ValueNew);

		// Setzt den neuen Wert an alle unterlegenen Values
		setvalue ($Unterobjekt, $ValueNew);

		// Ruft die Funktion in sich wieder auf bis es nichts mehr gibt
		NachUntenAktualisieren ($Unterobjekt, $ValueNew);

	}
}


// Aktualisiert die Werte in der Baumstruktur nach oben
// -----------------------------------------------------------------------------
function NachObenAktualisieren ($StartID){
	// Eltern IDs abfragen
	$Eltern   = IPS_GetParent ($StartID);
	$Nachbarn = IPS_GetChildrenIDs ($Eltern);
	$AnzahlAn = 0;

	// Wenn der Oberste Ebene geschalten wird, dann sofort beenden
	if (IPS_GetName ($StartID) == "Alle"){goto EndeFunktion;}else{}
//	if ($StartID == $ObereID){goto EndeFunktion;}else{}


	// Für jede Nachbar Variable abfragen ob diese true ist
	foreach ($Nachbarn as &$value) {
		if (getvalue ($value) == true){$AnzahlAn ++;}else{}
	}

	// Wenn Anzahl >0 ist dann die Übergeordnete Variable einschalten
	setvalue ($Eltern, $AnzahlAn);

	// Funktion wieder erneut aufrufen
   NachObenAktualisieren ($Eltern);

EndeFunktion:
}

?>

Script anlegen mit dem Namen „schalter script“
!! Achtung: include ID muss angepasst werden an die vom Script „function Gruppenschaltung“ !!

// Funktionen für Gruppenschaltung einbinden
include '35953 /*[System Dienste\Globale Funktionen\globale Functionen]*/.ips.php';			// Globale Funktionen einbinden


// Welche Gruppe geschalten werden soll
$ID 			= $_IPS['VARIABLE'];
$Schaltwert 	= $_IPS['VALUE'];


// Schaltvorgang ausführen
Schalten ($ID, $Schaltwert);


Baum anlegen:

oberste Bool Variable muss „Alle“ heißen. Damit ist das Obere Ende benannt.
in der Variable bei eigene Aktion das script mit dem Namen „schalter script“ auswählen.
aa.png

diese Variable kann nun einfach kopiert werden und dann nach belieben verschachtelt werden. Keine weitere Begrenzung.
Benennen kann man diese auch wie man will.

Damit sind die Gruppen angelegt.

Will man ein Gerät schalten auch einfach die Variable kopieren und am Ende des Namen folgendes Anhängen
@ Aktorart - Hersteller - ID

Welche es gibt findet man ganz einfach im ersten Script.
Dies kann auch ganz einfach für andere Hersteller und Typen erweitert werden.

Leider kann ich kein Modul schreiben. Würde mich aber freuen wenn jemand das kann. Bzw über Änderungen und Optimierungen freue ich mich auch.
Wer will kann es einfach mal ausprobieren.

Hallo

Habe jetzt eine Lösung gefunden wie ich es integrieren kann.

Das ganze funktioniert wieder dynamisch.
d.h. die Verschachtelung läuft nach unten, bis ein Link gefunden wird. Anschließend wird der Wert nach oben mit den Nachbarn verrechnet.

Es gibt die Möglichkeit Durchschnitt oder Summe.

Der oberste muss „Alles“ heißen.

<?
// Name des Skiptes ändern, folgende Möglichkeiten
// Durchschnitt		= Rechnet für alle den Durschnitt aus
// Summe			= Alles zusammenzählen

$NameLetzte		= "Alles";


$IDStart		= IPS_GetVariableIDByName($NameLetzte, IPS_GetParent($_IPS['SELF']));
$Rechenart		= IPS_GetName($_IPS['SELF']);


NachUnten ($IDStart, $NameLetzte, $Rechenart);


function NachUnten ($ID, $NameLetzte, $Rechenart) {
	$Children	= IPS_GetChildrenIDs($ID);

	foreach ($Children as $ID){
		//Abfrage ob das Ziel ein Link ist
		if (IPS_GetObject($ID)['ObjectType'] == 6 ){
		
		// Wert von Link in die Variable schreiben
		setvalue (IPS_GetParent ($ID), round(getvalue (IPS_GetLink ($ID)['TargetID']),1));
		
		// Variablen nach oben durchrechnen und aktualisieren
		NachOben (IPS_GetParent ($ID), $NameLetzte, $Rechenart);
		}

	// Weitere Links nach unten suchen
	NachUnten ($ID, $NameLetzte, $Rechenart);
	}
}


function NachOben ($ID, $NameLetzte, $Rechenart) {
	// Werte initialisieren
	$NachbarIDs		= IPS_GetChildrenIDs (IPS_GetParent ($ID));
	$Anzahl			= 0;
	$Value			= 0;

	// Wenn der Oberste Ebene geschalten wird, dann sofort beenden
	if (IPS_GetName ($ID) == $NameLetzte){goto EndeFunktion;}else{}

	// Für jede Nachbar Variable die Messwerte abfragen
	foreach ($NachbarIDs as $ID) {
		$Anzahl		= $Anzahl + 1;
		$Value		= $Value + getvalue ($ID);
	}

	// Durchschnitt oder Summe berechnen und runden
	switch ($Rechenart) {
	case "Durchschnitt":
	$Ergebnis 		= round ($Value / $Anzahl, 1);
	break;
	
	case "Summe":
	$Ergebnis 		= $Value;
	break;
	
	}

	// Wert in Variable speichern
	setvalue (IPS_Getparent($ID), $Ergebnis);

	// Funktion wieder erneut aufrufen, bis oberste Ebene erreicht ist.
	NachOben (IPS_Getparent($ID), $NameLetzte, $Rechenart);


EndeFunktion:
}

?>