Hallo zusammen,
auf Grund eines nicht funktionierendes DOIFs https://forum.fhem.de/index.php/topic,120183.0.html (https://forum.fhem.de/index.php/topic,120183.0.html), stellt sich mir die Frage, ob sich die Aufgabenstellung überhaupt mit einem einzigen DOIF lösen lässt.
Meine ZWave Heizungsventile (Spirit) erzeugen diverse Events (z.B. Temperaturänderung am Ventil selbst) bzw. durch Schalten per fhem (z.B. in den Energiesparmodus). Problem dabei ist, dass nicht immer alle korrespondierende Werte automatisch gesendet und als Reading gespeichert werden. Konkretes Beispiel: Schalten in den Energiesparmodus ändert das Reading für den Modus, aber das Reading für den Sollwert wird nicht geändert.
Über die Auswertung des Events für den Modus, lässt sich der Sollwert vom Ventil grundsätzlich abfragen.
Meine Idee war es, ein DOIF zu erstellen (FHEM oder Perl-Modus), mit dem ich dann für alle Ventile und für alle Events, die Folgeabfragen auslösen kann. Dazu verwernde ich auch wait / set_exec um Timer zu setzen, damit der Funkverkehr etwas entzerrt wird. Dies funktioniert im Einzeltest auch gut.
In der Realität kommt es natürlich vor, dass zum einen mehrere Ventile fast gleichzeitig Änderungen melden oder ein Schaltvorgang aus fhem parallel zu einem vom Ventil generierten Event läuft. In diesen Fällen sind die Timer nicht alle abgearbeitet und es sieht für mich so aus, als würde das erneute Triggern des DOIF, das vorherige inkl. der Timer unterbrechen.
Selbst wenn ich jetzt für jedes Event ein ein eigenes DOIF erstellen, kann es ja vorkommen, dass mehrere Ventile gleichzeitig das gleiche Event (z.B. Temperaturänderung) auslösen.
Deshalb meine Fragen:
1. Lässt sich dieser Anwendungsfall überhaupt mit DOIF lösen?
2. Lässt er sich mit einem einzigen DOIF lösen oder nur mit mehreren?
Ich komme mit Testen nicht wirklich weiter und brauche Euren Rat.
Viele Grüße
Torsten
Also in FHEM läuft alles sequentiell ab (single task), daher kann nichts verloren gehen. Und auch wenn Events quasi parallel kommen, werden sie sauber nacheinander abgearbeitet, weil sie in Wirklichkeit auch nur nacheinander vom Hauptprozess verarbeitet werden können.
Natürlich muss man sich Gedanken machen, wie man etwas realisiert. Wenn man Timer aufsetzt z. B., dann muss man sie eindeutig definieren, damit sie unabhängig von einander laufen können. Das ist im DOIF-Perl-Modus kein Problem.
Probleme gibt es immer wieder, wenn man direkt oder indirekt auf eigene Events reagieren will - das führt oft zu Problemen.
Das sind natürlich nur allgemeine Aussagen, der Teufel steckt aber oft im Detail.
Hallo Damian,
danke für Dein Feedback, das stimmt mich optimistisch. Dann steckt das Problem vielleicht wirklich im Detail und lässt sich hoffentlich lösen. Nachfolgend das List meines DOIF:
Internals:
DEF subs {
sub tmpName {
my @time = localtime();
$time[4]++;
$time[5]+=1900;
$time[$_] = $time[$_]<10? "0".$time[$_]:$time[$_] for (0..5);
return '123456';
#$time[5].$time[4].$time[3].$time[2].$time[1].$time[0].$$;
}
sub get_value {
my ($dev,$rname)=@_; # Variable $dev und $rname mit dem Parameter belegen
my $timerName = "val_" . tmpName();
if ($rname eq "reportedState") {
$_cmd="get $dev swmStatus";
} elsif ($rname eq "temperature") {
$_cmd="get $dev smStatus";
}
Log 3, "get_value: $dev, $rname, $_cmd";
if (ReadingsAge("$dev","$rname",0) >= 900 and $_cmd ne "") {
set_Exec("$timerName",5,'fhem("$_cmd")');
Log 2, "get_value: $_cmd";
fhem_set("logdb addLog $dev:desired-temp");
Log 2, "log_desTemp: $dev";
}
}
sub set_lastActivity {
my ($dev,$act)=@_; # Variable $dev und $act mit dem Parameter belegen
my $timerName = "act_" . tmpName();
$_cmd="setreading $dev lastActivity $act";
set_Exec("$timerName",0,'fhem("$_cmd")');
Log 4, "set_lastActivity: $dev, $act";
}
sub get_setPoint {
my ($dev,$tmMode)=@_; # Variable $dev und $tmMode mit dem Parameter belegen
$_cmd="get $dev setpoint $tmMode";
my $timerName = "pnt_" . tmpName();
set_Exec("$timerName",5,'fhem("$_cmd")');
Log 2, "get_setPoint: $dev, $tmMode, $_cmd";
}
sub get_tmMode {
my ($dev)=@_; # Variable $dev mit dem Parameter belegen
$_cmd="get $dev thermostatMode";
my $timerName = "tmm_" . tmpName();
set_Exec("$timerName",5,'fhem("$_cmd")');
Log 2, "get_tmMode: $timerName, $dev, $_cmd";
}
}
init { # Vorbelegung von Instanzvariablen
$_cmd=q{}; # cmd leer
}
temp_change { ## Wenn sich der Temperaturwert ändert
if (["THKV.Heizkoerper_(Wand|Fenster)$:^temperature:..*$"]) {
Log 2, "$DEVICE: temperature";
get_value("$DEVICE","reportedState");
set_lastActivity("$DEVICE","temperature");
}
}
valve_change { ## Wenn sich der Ventilwert ändert
if (["THKV.Heizkoerper_(Wand|Fenster)$:^reportedState:..*$"]) {
Log 2, "$DEVICE: reportedState";
get_value("$DEVICE","temperature");
set_lastActivity("$DEVICE","reportedState");
}
}
desTemp_change_heating { ## Wenn sich der Sollwert heating ändert
if (["THKV.Heizkoerper_(Wand|Fenster)$:^thermostatSetpointSet.*.C.1$"]) {
Log 2, "$DEVICE: thermostatSetpointSet heating";
if (ReadingsVal("$DEVICE","thermostatMode","heating") eq "heating") {
get_setPoint("$DEVICE",1);
}
get_value("$DEVICE","reportedState");
get_value("$DEVICE","temperature");
set_lastActivity("$DEVICE","thermostatSetpointSet");
}
}
desTemp_change_energySaveHeating { ## Wenn sich der Sollwert energySaveHeating ändert
if (["THKV.Heizkoerper_(Wand|Fenster)$:^thermostatSetpointSet.*.C.11$"]) {
Log 2, "$DEVICE: thermostatSetpointSet energySaveHeating";
if (ReadingsVal("$DEVICE","thermostatMode","energySaveHeating") eq "energySaveHeating") {
get_setPoint("$DEVICE",11);
}
get_value("$DEVICE","reportedState");
get_value("$DEVICE","temperature");
set_lastActivity("$DEVICE","thermostatSetpointSet");
}
}
tmMode_heating_change { ## Wenn sich thermostatMode ändert
if (["THKV.Heizkoerper_(Wand|Fenster)$:^thermostatMode:.heating$"]) {
Log 2, "$DEVICE: thermostatMode heating";
get_setPoint("$DEVICE",1);
get_value("$DEVICE","reportedState");
get_value("$DEVICE","temperature");
set_lastActivity("$DEVICE","thermostatMode heating");
}
}
tmMode_off_change { ## Wenn sich thermostatMode ändert
if (["THKV.Heizkoerper_(Wand|Fenster)$:^thermostatMode:.off$"]) {
Log 2, "$DEVICE: thermostatMode off";
set_Exec("toch_" . tmpName(),5,'fhem("setreading $DEVICE desired-temp 6.0")');
get_value("$DEVICE","reportedState");
get_value("$DEVICE","temperature");
set_lastActivity("$DEVICE","thermostatMode off");
}
}
tmMode_fullPower_change { ## Wenn sich thermostatMode ändert
if (["THKV.Heizkoerper_(Wand|Fenster)$:^thermostatMode:.fullPower$"]) {
Log 2, "$DEVICE: thermostatMode fullPower";
set_Exec("tfc_" . tmpName(),5,'fhem("get $DEVICE setpoint 1")');
get_value("$DEVICE","reportedState");
get_value("$DEVICE","temperature");
set_lastActivity("$DEVICE","thermostatMode fullPower");
}
}
FUUID 606c3f8f-f33f-2e5f-7292-c05f8d74a8c012f0
FVERSION 98_DOIF.pm:0.241730/2021-04-07
MODEL Perl
NAME test_reported_state
NOTIFYDEV global
NR 252
NTFY_ORDER 50-test_reported_state
STATE deactivated
TYPE DOIF
VERSION 24173 2021-04-07 17:28:37
READINGS:
2021-04-08 14:12:21 mode deactivated
2021-04-08 14:12:21 state deactivated
Regex:
condition:
helper:
DEVFILTER ^global$
NOTIFYDEV global
uiTable:
Attributes:
disable 1
group Notify on Change
room Zentrale Steuerung
Für die Timer hatte ich mir schon eine Funktion gebaut, um diese eindeutig zu bekommen, so dass ein erneuter Aufruf von set_exec den Timer nicht abbricht (so hatte ich das zumindest aus der Doku herausgelesen). Ich vermute, dass es an meiner Instanzvariable $_cmd liegt. Diese brauche ich, weil ich im set_exec eine Variable übergeben muss. Wenn jetzt aber das DOIF durch ein zweites Event (z.B. anderes Ventil) aufgerufen wird, scheint es für mich, dass diese Variable im noch bestehenden Timer "überschrieben" wird. Auf jeden Fall wird der get Befehl aus dem vorherigen Aufruf nicht ausgeführt oder kein Event dazu ausgelöst.
Ich hoffe, ich habe mich verständlich ausgedrückt. Leider ist es mir nicht gelungen dem DOIF ein Log mit verbose 5 zu entlocken.
Viele Grüße und danke für Deine Unterstützung
Torsten
Das Problem könnte dieser Aufruf sein:
set_Exec("$timerName",5,'fhem("$_cmd")');
mit 'fhem("$_cmd")' wird $_cmd ohne Auswertung weiter gegeben und erst zum Ausführungszeitpunkt des Timers ausgewertet. Es könnte sein, dass $_cmd dort nicht bekannt ist - das müsste ich mir im Sourcecode genau anschauen.
mit
"fhem('$_cmd')"
wird dagegen $_cmd direkt ausgewertet und das Ergebnis über set_Exec weiter gegeben.
Das kann unterschiedliches Verhalten hervorrufen.
Wow, das ging ja schnell.
Ich probiere das gleich mal aus, ob sich dann das DOIF dann "besser" verhält.
Hallo Damian,
das war es wohl - zumindest hat das DOIF nach der Änderung nichts mehr "verschluckt" und alle get / setreading Kommandos ausgeführt.
Ich lass das mal jetzt so weiterlaufen und beobachte meine Readings...
Danke und viele Grüße
Torsten