Verwendet wird ein Homematic Bewegungsmelder HM-SEC-MDIR-2 und eine mit 254 Texten programmierbare Sprachausgabe HM-OU-CFM-PI.
Aufgabenstellung:
Bewegungsmelder triggert zwei DOIFs, wenn Uhrzeit zwischen 04:30 Uhr und 09:00 Uhr.
Das 1. DOIF ruft in der Sprachausgabe den entsprechenden Text für die Tageshöchsttemperatur auf, die von PROPLANTA bezogen wird:
([OG_Bad_Bewegungsmelder:motion] =~ "on" and [04:30-09:00] and [OUT_Wetter:fc0_tempMax] < -10)
(set OG_Bad_MelderMP3 playTone 189)
DOELSEIF
([OG_Bad_Bewegungsmelder:motion] =~ "on" and [04:30-09:00] and [OUT_Wetter:fc0_tempMax] == -10)
(set OG_Bad_MelderMP3 playTone 190)
DOELSEIF
([OG_Bad_Bewegungsmelder:motion] =~ "on" and [04:30-09:00] and [OUT_Wetter:fc0_tempMax] == -9)
(set OG_Bad_MelderMP3 playTone 191)
... u.s.w.
Das 2. DOIF ruft in der Sprachausgabe den entsprechenden Text für die Tageswetteraussicht (Regen, sonnig, bewölkt, u.s.w.) auf, die von PROPLANTA bezogen wird:
([OG_Bad_Bewegungsmelder:motion] =~ "on" and [04:30-09:00] and [OUT_Wetter:fc0_weatherDay] eq "heiter")
(set OG_Bad_MelderMP3 playTone 243)
DOELSEIF
([OG_Bad_Bewegungsmelder:motion] =~ "on" and [04:30-09:00] and [OUT_Wetter:fc0_weatherDay] eq "wolkenlos")
(set OG_Bad_MelderMP3 playTone 244)
DOELSEIF
([OG_Bad_Bewegungsmelder:motion] =~ "on" and [04:30-09:00] and [OUT_Wetter:fc0_weatherDay] eq "sonnig")
(set OG_Bad_MelderMP3 playTone 245)
DOELSEIF
([OG_Bad_Bewegungsmelder:motion] =~ "on" and [04:30-09:00] and [OUT_Wetter:fc0_weatherDay] eq "klar")
(set OG_Bad_MelderMP3 playTone 246)
... u.s.w.
Problem:
Da die beiden DOIFs durch den Bewegungsmelder aber ziemlich zeitgleich getriggert werden, wird in der Regel nur eine der beiden Sprachausgaben gemacht, also entweder Temperatur oder Wetterlage.
Habt Ihr eine Idee, wie man das machen kann, damit regelmäßig beide Ansagen gemacht werden?
EIN notify was auf motion triggert und dann deine ganzen if/elsif/etc. in Perl programmieren...
Also zuerst if/elsif/... für Temperatur mit Ansage (und evtl. Pause) und dann eben if/elsif/... für Wetteraussicht inkl. Ansage...
...oder umgekehrt...
Gruß, Joachim
Danke @MadMax-FHEM für Deinen Vorschlag.
Ich habe jetzt eine Lösung gefunden, die ich im folgenden vorstellen möchte, da ich aus eigener Erfahrung weiß, daß gerade Anfänger in Sachen FHEM händeringend nach Lösungsvorschlägen suchen.
Vorteil dieser Lösung:
Tatsächlich werden regelmäßig beide Ansagen vollständig vom HM-OU-CFM-PI wiedergegeben.
Nachteil dieser Lösung:
Die FHEM-Zentrale, in meinem Fall ein Raspberry, wird beim Trigger durch das 1. DOIF für 5s blockiert.
Ausführung:
Ich bin bei den beiden DOIFs geblieben, wie zu Beginn des Themas beschrieben.
Ich habe festgestellt, daß beiden Texte ohne Überschneidung komplett angesagt werden, wenn der zweite Text 5 Sekunden nach dem ersten Text startet.
Nunmehr löst das Wetterlage-DOIF nach 5s durch Setzen des Reading der Variablen (=dummy) TemperaturAnsagen auf "on" das Temperatur-DOIF aus, welches nach getaner Arbeit das Reading von TemperaturAnsagen wieder zurücksetzt auf "off".
Mit
define TemperaturAnsagen dummy
habe ich mir also zuallererst einmalig ein neues dummy (=Variable) erzeugt und dieses dann in beiden DOIFs verwendet. Da ich jeweils bei den Aktionen der DOIFs zwei "sets" gebraucht habe, habe ich die Reaktion mit Perl implementiert {}, während die Triggerbedingung selbst direkte FHEM-Codierung ist.
Modifiziertes Wetterlageansage-DOIF:
([OG_Bad_Bewegungsmelder:motion] =~ "on" and [04:30-09:00] and [OUT_Wetter:fc0_weatherDay] eq "heiter")
{
fhem ('set OG_Bad_MelderMP3 playTone 243');
sleep(5);
fhem ('set TemperaturAnsagen on');
}
DOELSEIF
([OG_Bad_Bewegungsmelder:motion] =~ "on" and [04:30-09:00] and [OUT_Wetter:fc0_weatherDay] eq "wolkenlos")
{
fhem('set OG_Bad_MelderMP3 playTone 244');
sleep(5);
fhem ('set TemperaturAnsagen on');
}
DOELSEIF
([OG_Bad_Bewegungsmelder:motion] =~ "on" and [04:30-09:00] and [OUT_Wetter:fc0_weatherDay] eq "sonnig")
{
fhem ('set OG_Bad_MelderMP3 playTone 245');
sleep(5);
fhem ('set TemperaturAnsagen on');
}
DOELSEIF
... u.s.w.
Modifiziertes Temperaturansage-DOIF:
([TemperaturAnsagen:state] eq "on" and [OUT_Wetter:fc0_tempMax] < -10)
{
fhem ('set OG_Bad_MelderMP3 playTone 189');
fhem ('set TemperaturAnsagen off');
}
DOELSEIF
([TemperaturAnsagen:state] eq "on" and [OUT_Wetter:fc0_tempMax] == -10)
{
fhem ('set OG_Bad_MelderMP3 playTone 190');
fhem ('set TemperaturAnsagen off');
}
DOELSEIF
([TemperaturAnsagen:state] eq "on" and [OUT_Wetter:fc0_tempMax] == -9)
{
fhem ('set OG_Bad_MelderMP3 playTone 191');
fhem ('set TemperaturAnsagen off');
}
DOELSEIF
... u.s.w.
In beiden DOIFs habe ich übrigens das Attribut "do always" gesetzt. (verbessert 31.01.21)
Wie könnte man es nach meinem derzeitige Kenntnisstand noch besser machen?:
Die Idee ist da, allerdings fehlt mir das Perl-Wissen. Man könnte beim Triggern des Wetterlageansage-DOIFs sich in diesem DOIF die Systemzeit holen und in einen dummy schreiben.
Mit einem Notify könnte man anschließend triggern, wenn die Zeit dummy+5s erreicht ist und damit die Temperaturansage starten.
Falls Ihr Vorschläge habt, dies zu konkretisieren oder entsprechenden Perl-Code, danke ich im Voraus.
Zitat
Danke @MadMax-FHEM für Deinen Vorschlag.
Gerne, wenn auch total ignoriert... ;)
Zitat
Ich habe jetzt eine Lösung gefunden, die ich im folgenden vorstellen möchte, da ich aus eigener Erfahrung weiß, daß gerade Anfänger in Sachen FHEM händeringend nach Lösungsvorschlägen suchen.
Ja, danke. Sollte man tun... :)
ABER: keine SO komplizierte Umsetzung. Und v.a. keine die fhem (bis zu) 5s blockiert. (auch wenn es als Nachteil geschrieben wird)
Zitat
Ich habe festgestellt, daß beiden Texte ohne Überschneidung komplett angesagt werden, wenn der zweite Text 5 Sekunden nach dem ersten Text startet.
Kann man bei meinem Vorschlag mit EINEM notify (meinetwegen auch mit EINEM DOIF) auch OHNE blockieren umsetzen...
Ich habe ja nicht genau nachgeprüft bzgl. deinem sleep.
Aber wenn dein sleep blockiert, dann ist es verm. Zufall, wenn überhaupt etwas passiert.
Weil wenn fhem steht muss die eine Ansage schon beim MP3-Player angekommen sein, also das set MP3-Player spiele XYZ schon abgesetzt sein, weil wenn fhem steht geht nicht mal mehr der Befehl des 2ten DOIF raus.
Wie geschrieben ohne große nachprüfung: wenn es bei dir "immer" klappt, denke ich hast du einfach Glück. Oder es wird KEIN blockierendes sleep genommen (kenne DOIF etc. zu wenig) und dann blockiert fhem eben nicht... ;)
Zitat
In beiden DOIFs habe ich übrigens das Attribut "set do always" gesetzt.
Das Attribut "set do always" gibt es nicht.
Es heißt (mWn) nur "do always"... ;)
Zitat
Habt Ihr eine Idee, wie man das machen kann, damit regelmäßig beide Ansagen gemacht werden?
Hatte ich ja bereits geschrieben...
...aber dann halt mit etwas Code... ;)
EDIT: ich habe mich mal an dem orientiert was ich deinem DOIF (so ich das verstehe: ich nutze das NICHT) entnehmen konnte... Und (mangels vorhandener Geräte etc.) nicht wirklich "geprüft" und mangles kompletter if/elsif-Orgie nat. KEIN "copy/past-Code"...
define nAnsage notify OG_Bad_Bewegungsmelder:motion {MeineAnsage($EVENT)}
EDIT: wenn man die Events kennen würde, dann könnte man auch das notify "besser" machen und sich das Vergleichen mit "on" in der Sub bestimmt auch "sparen"...
Dann eben eine Sub in myUtils:
EDIT: https://wiki.fhem.de/wiki/99_myUtils_anlegen
EDIT: kein "MUSS" aber (finde ich) übersichtlicher. Kann/könnte nat. auch direkt ins notify...
sub MeineAnsage($)
{
my ($Event) = @_;
my $actTime = strftime("%H:%M", localtime);
# diese Variablen könnte man auch erst "in" der ersten "if-Klammer" setzen, dann aber die Logausgabe anpassen!
# weil diese werden ja bei jeder Motion "angelegt" und die Readings ausgelesen
# aber ich denke dieser Performance-Verlust wäre/ist verkraftbar... ;-)
my $TempMax = ReadingsNum("OUT_Wetter", "fc0_tempMax", 0);
my $Wetter = ReadingsVal("OUT_Wetter", "fc0_weatherDay", "n.a.");
# evtl. auch eine "Fallback-Meldung setzen statt 0...
my $AnsageTemp = 0;
my $AnsageWetter = 0;
# kann irgendwann wieder raus ;-) / je nachdem wie oft Bewegung kommt kann das nat. schon mal das Log "zumüllen". Während der "Testphase" habe ich aber "gerne" solche Ausgaben im Log um "was zu sehen"...
Log3(undef, 1, "MeineAnsage Event:$Event actTime: $actTime TempMax: $TempMax Wetter: $Wetter");
# weil das kommt doch bei deinem DOIF IMMER, also "on" und die selbe Uhrzeit!?
# wenn man die Events kennen würde, dann könnte man auch das notify "besser" machen und sich das Vergleichen mit "on" hier bestimmt auch "sparen"
if($Event =~ m/on/ && ($actTime gt "04:30" && $actTime lt "09:00"))
{
# erst mal die Temp rausfinden
if($TempMax < -10)
{
$AnsageTemp = 189;
}
elsif($TempMax == -10)
{
$AnsageTemp = 190;
}
# usw. / wobei das bestimmt auch geschickter geht, da ja ab -10 immer fortlaufend einfach eins drauf gezählt wird, oder!?
# Ansage Wetter
if($Wetter eq "wolkenlos")
{
$AnsageWetter = 244;
}
elsif($Wetter eq "sonnig")
{
$AnsageWetter = 245;
}
# usw. / wobei das bestimmt auch geschickter geht, z.B. mit einem hash oder einem array o.ä. ;-)
# nach dem nun bekannt ist was angesagt werden soll -> die Ansagen / wenn andersrum, dann einfach umdrehen ;-)
# dieser sleep blockiert NICHT! :-)
# wichtig: "doppelte Anführungszeichen"! Damit die Variablen ausgewertet werden...
fhem("set OG_Bad_MelderMP3 playTone $AnsageTemp; sleep 5; set OG_Bad_MelderMP3 playTone $AnsageWetter");
}
}
Gruß, Joachim
Herzlichen Dank an MadMax-FHEM für die gelungene Einführung in die Perl-Programmierung von eigenen Skripten in der dafür vorgesehenen Datei 99_myUtils.
Den freundlicherweise zur Verfügung gestellten Code habe ich noch angepaßt an die als durchnummerierte mp3-Dateien im eQ-3 HM-OU-CFM-PI vorliegenden Sprachtexte.
sub MeineAnsage($)
{
my ($Event) = @_;
my $actTime = strftime("%H:%M", localtime);
# Es folgt ein Hash %Wetterlage zur Zuordnung der Dateinummern zur Wetterlage:
my %Wetterlage= (
"heiter" => 243,
"wolkenlos" => 244,
"sonnig" => 245,
"klar" => 246,
"wolkig" => 247,
"stark bewölkt" => 248,
"bedeckt" => 249,
"Regenschauer" => 250,
"Regen" => 251,
"Schneeregen" => 252,
"Schneeschauer" => 253,
"Schneefall" => 254
);
# diese Variablen könnte man auch erst "in" der ersten "if-Klammer" setzen, dann aber die Logausgabe anpassen!
# weil diese werden ja bei jeder Motion "angelegt" und die Readings ausgelesen
# aber ich denke dieser Performance-Verlust wäre/ist verkraftbar... ;-)
my $TempMax = ReadingsNum("OUT_Wetter", "fc0_tempMax", 0);
my $Wetter = ReadingsVal("OUT_Wetter", "fc0_weatherDay", "n.a.");
# evtl. auch eine "Fallback-Meldung setzen statt 0...
my $AnsageTemp = 0;
my $AnsageWetter = 0;
# kann irgendwann wieder raus ;-) / je nachdem wie oft Bewegung kommt kann das nat. schon mal das Log "zumüllen". Während der "Testphase" habe ich aber "gerne" solche Ausgaben im Log um "was zu sehen"...
Log3(undef, 1, "MeineAnsage Event:$Event actTime: $actTime TempMax: $TempMax Wetter: $Wetter");
# weil das kommt doch bei deinem DOIF IMMER, also "on" und die selbe Uhrzeit!?
# wenn man die Events kennen würde, dann könnte man auch das notify "besser" machen und sich das Vergleichen mit "on" hier bestimmt auch "sparen"
if($Event =~ m/on/ && ($actTime gt "04:30" && $actTime lt "09:00"))
{
# erst mal die Temp rausfinden
if($TempMax < -10)
{
$AnsageTemp = 189;
}
elsif($TempMax > 35)
{
$AnsageTemp = 236;
}
else
{
# Zwischen -10°C <= Tageshoechsttemperatur <= 35°C sind die Temperaturen fortlaufend mit offset 200 nummeriert.
$AnsageTemp = $TempMax + 200;
}
# Ansage Wetter
# Richtige Anssagenummer aus Hash ziehen:
$AnsageWetter = $Wetterlage{$Wetter};
# Sprachausgabe beauftragen:
fhem("set OG_Bad_MelderMP3 playTone $AnsageTemp; sleep 5; set OG_Bad_MelderMP3 playTone $AnsageWetter");
}
}
Das Unterprogramm wird mit einem Notify getriggert:
OG_Bad_Bewegungsmelder:motion:.* {MeineAnsage($EVENT)}
und sorgt nun dafür, daß man im Bad jeden Morgen zur Aufstehzeit hört, wie kalt/warm es draußen werden wird und sich entsprechend anziehen kann.
Gerne.
D.h. es geht!?
Wenn ja (klingt danach), dann noch ein [gelöst] o.ä. vor den ersten Post, danke.
Viel Spaß noch, Joachim
Aktuellste Version
Ich habe jetzt noch eingefügt, daß trotz weiterer Bewegung im Badezimmer die beiden Voice-Meldungen fühestens nach einer einstellbaren Zeit in Sekunden (Variable $pauseZeitInSekunden) erneut angesagt werden:
sub MeineAnsage($)
{
# 30.01.21/SW:
# Es handelt sich um die Wetteransage und Ansage der Tageshöchsttemperatur frueh im OB Bad, getriggert durch den Bewegungsmelder
# Ersetzt zwei kurz zuvor geschriebene DOIfs.
# Grundgeruest von MadMax-FHEM im FHEM-Forum
#
# 31.01.21/SW:
# Erweiterung: Die Ansage der Wetterlage und der Tageshöchststemperatur wiederholt sich frühestens nach $pauseZeitInSekunden
#
my ($Event) = @_;
my $actTime = strftime("%H:%M", localtime);
my $time = time; # Sekunden seit 01.01.1970/00:00 Uhr holen
my $pauseZeitInSekunden = 300; # 5 min Pause zwischen den Wiederholungen der Ansage
my $sekundenSpeicherError = "";
my $sekundenSpeicherKey = "sekundenSpeicher";
my $sekundenSpeicherValue = 0;
# Es folgt ein Hash %Wetterlage zur Zuordnung der Dateinummern zur Wetterlage:
my %Wetterlage= (
"heiter" => 243,
"wolkenlos" => 244,
"sonnig" => 245,
"klar" => 246,
"wolkig" => 247,
"stark bewölkt" => 248,
"bedeckt" => 249,
"Regenschauer" => 250,
"Regen" => 251,
"Schneeregen" => 252,
"Schneeschauer" => 253,
"Schneefall" => 254
);
# diese Variablen könnte man auch erst "in" der ersten "if-Klammer" setzen, dann aber die Logausgabe anpassen!
# weil diese werden ja bei jeder Motion "angelegt" und die Readings ausgelesen
# aber ich denke dieser Performance-Verlust wäre/ist verkraftbar... ;-)
my $TempMax = ReadingsNum("OUT_Wetter", "fc0_tempMax", 0);
my $Wetter = ReadingsVal("OUT_Wetter", "fc0_weatherDay", "n.a.");
# evtl. auch eine "Fallback-Meldung setzen statt 0...
my $AnsageTemp = 0;
my $AnsageWetter = 0;
# weil das kommt doch bei deinem DOIF IMMER, also "on" und die selbe Uhrzeit!?
# wenn man die Events kennen würde, dann könnte man auch das notify "besser" machen und sich das Vergleichen mit "on" hier bestimmt auch "sparen"
if($Event =~ m/on/ && ($actTime gt "04:30" && $actTime lt "09:00"))
{
# Erst mal die Temp rausfinden
if($TempMax < -10)
{
$AnsageTemp = 189;
}
elsif($TempMax > 35)
{
$AnsageTemp = 236;
}
else
{
# Zwischen -10°C <= Tageshoechsttemperatur <= 35°C sind die Temperaturen fortlaufend mit offset 200 nummeriert.
$AnsageTemp = $TempMax + 200;
}
# Ansage Wetter
$AnsageWetter = $Wetterlage{$Wetter};
#if($Wetter eq "wolkenlos")
#{
# $AnsageWetter = 244;
#}
#elsif($Wetter eq "sonnig")
#{
# $AnsageWetter = 245;
#}
# usw. / wobei das bestimmt auch geschickter geht, z.B. mit einem hash oder einem array o.ä. ;-)
($sekundenSpeicherError, $sekundenSpeicherValue) = getKeyValue($sekundenSpeicherKey);
if($sekundenSpeicherValue == undef) # Die Variable wurde bisher noch nie aufgerufen ==> Deshalb jetzt setzen!
{
$sekundenSpeicherError = setKeyValue($sekundenSpeicherKey, $time+$pauseZeitInSekunden);
}
elsif($time >= $sekundenSpeicherValue) # Es sind mehr als $pauseZeitInSekunden nach der letzten Ansage vergangen
{
$sekundenSpeicherError = setKeyValue($sekundenSpeicherKey, $time); # Neue Zeit in static Variable setzen!
# Jetzt Sprachausgabe beauftragen:
# Nach dem nun bekannt ist was angesagt werden soll -> die Ansagen / wenn andersrum, dann einfach umdrehen ;-)
# Dieser sleep blockiert NICHT! :-)
# Wichtig: "doppelte Anführungszeichen"! Damit die Variablen ausgewertet werden...
fhem("set OG_Bad_MelderMP3 playTone $AnsageTemp; sleep 5; set OG_Bad_MelderMP3 playTone $AnsageWetter");
# In Logdatei schreiben, zumindest in der Versuchphase:
Log3(undef, 1, "MeineAnsage|Event:$Event|actTime: $actTime|TempMax: $TempMax|Wetter: $Wetter|AnsageTemp: $AnsageTemp|AnsageWetter: $AnsageWetter");
}
# In Logdatei schreiben, zumindest in der Versuchphase:
Log3(undef, 1, "MeineAnsage|time: $time|sekundenSpeicherError: $sekundenSpeicherError|sekundenSpeicherKey: $sekundenSpeicherKey|sekundenSpeicherValue: $sekundenSpeicherValue");
}
}
Das kannst du auch beim notify machen: Attribut disableAfterTrigger ;)
Gruß, Joachim
Zitat von: MadMax-FHEM am 31 Januar 2021, 09:35:07
Das kannst du auch beim notify machen: Attribut disableAfterTrigger ;)
Gruß, Joachim
Danke, Joachim :D!