diff --git a/fhem/FHEM/22_HOMEMODE.pm b/fhem/FHEM/22_HOMEMODE.pm index 2f0f9c770..153604c46 100644 --- a/fhem/FHEM/22_HOMEMODE.pm +++ b/fhem/FHEM/22_HOMEMODE.pm @@ -16,7 +16,7 @@ use Time::HiRes qw(gettimeofday); use HttpUtils; use vars qw{%attr %defs %modules $FW_CSRF}; -my $HOMEMODE_version = "1.2.2"; +my $HOMEMODE_version = "1.3.0"; my $HOMEMODE_Daytimes = "05:00|morning 10:00|day 14:00|afternoon 18:00|evening 23:00|night"; my $HOMEMODE_Seasons = "03.01|spring 06.01|summer 09.01|autumn 12.01|winter"; my $HOMEMODE_UserModes = "gotosleep,awoken,asleep"; @@ -35,7 +35,7 @@ sub HOMEMODE_Initialize($) $hash->{SetFn} = "HOMEMODE_Set"; $hash->{UndefFn} = "HOMEMODE_Undef"; $hash->{FW_detailFn} = "HOMEMODE_Details"; - $hash->{AttrList} = HOMEMODE_Attributes($hash); + $hash->{AttrList} = HOMEMODE_Attributes($hash)." $readingFnAttributes"; $hash->{NotifyOrderPrefix} = "51-"; $hash->{FW_deviceOverview} = 1; $hash->{FW_addDetailToSummary} = 1; @@ -68,7 +68,7 @@ sub HOMEMODE_Define($$) $trans = $HOMEMODE_de? "$resdevs[0] existiert nicht": "$resdevs[0] doesn't exists"; - return $trans if (!IsDevice($resdevs[0])); + return $trans if (!HOMEMODE_ID($resdevs[0])); $hash->{DEF} = $resdevs[0]; } elsif (@resdevs > 1) @@ -109,7 +109,7 @@ sub HOMEMODE_Define($$) readingsBulkUpdate($hash,"anyoneElseAtHome","off") if (!defined ReadingsVal($name,"anyoneElseAtHome",undef)); readingsBulkUpdate($hash,"panic","off") if (!defined ReadingsVal($name,"panic",undef)); readingsEndUpdate($hash,0); - HOMEMODE_updateInternals($hash); + HOMEMODE_updateInternals($hash,1); } return; } @@ -170,7 +170,7 @@ sub HOMEMODE_Notify($$) push @commands,$cmd; } CommandAttr(undef,"$dev room ".AttrVal($name,"HomeAtTmpRoom","")) - if ($dev =~ /^atTmp_.*_$name$/ && $defs{$dev}->{TYPE} eq "at" && AttrVal($name,"HomeAtTmpRoom","")); + if ($dev =~ /^atTmp_.*_$name$/ && HOMEMODE_ID($dev,"at") && AttrVal($name,"HomeAtTmpRoom","")); } } elsif (grep /^REREADCFG|MODIFIED\s$name$/,@{$events}) @@ -192,12 +192,16 @@ sub HOMEMODE_Notify($$) { HOMEMODE_Twilight($hash,$devname); } - elsif (AttrVal($name,"HomeEventsHolidayDevices",undef) && grep(/^$devname$/,devspec2array(AttrVal($name,"HomeEventsHolidayDevices",""))) && grep /^state:\s/,@{$events}) + elsif ((AttrVal($name,"HomeEventsHolidayDevices",undef) + && grep(/^$devname$/,devspec2array(AttrVal($name,"HomeEventsHolidayDevices","")))) + || + (AttrVal($name,"HomeEventsCalendarDevices",undef) + && grep(/^$devname$/,devspec2array(AttrVal($name,"HomeEventsCalendarDevices",""))))) { foreach my $evt (@{$events}) { - next unless ($evt =~ /^state:\s(.*)$/); - HOMEMODE_EventCommands($hash,$devname,$1); + next unless ((HOMEMODE_ID($devname,"Calendar") && $evt =~ /^(start|end):\s(.+)$/) || (HOMEMODE_ID($devname,"holiday") && $evt =~ /^(state):\s(.+)$/)); + HOMEMODE_EventCommands($hash,$devname,$1,$2); } } elsif (AttrVal($name,"HomeUWZ",undef) && $devname eq AttrVal($name,"HomeUWZ","") && grep /^WarnCount:\s/,@{$events}) @@ -464,20 +468,20 @@ sub HOMEMODE_Notify($$) return; } -sub HOMEMODE_updateInternals($;$) +sub HOMEMODE_updateInternals($;$$) { - my ($hash,$force) = @_; + my ($hash,$force,$set) = @_; my $name = $hash->{NAME}; my $resdev = $hash->{DEF}; my $trans; - if (!IsDevice($resdev)) + if (!HOMEMODE_ID($resdev)) { $trans = $HOMEMODE_de? "$resdev ist nicht definiert!": "$resdev is not defined!"; readingsSingleUpdate($hash,"state",$trans,0); } - elsif (!IsDevice($resdev,"RESIDENTS")) + elsif (!HOMEMODE_ID($resdev,"RESIDENTS")) { $trans = $HOMEMODE_de? "$resdev ist kein gültiges RESIDENTS Gerät!": @@ -556,7 +560,7 @@ sub HOMEMODE_updateInternals($;$) $hash->{helper}{presdevs}{$resident} = \@residentspresdevs if (@residentspresdevs > 1); } } - if (@logtexte && $force) + if (@logtexte && $set) { $trans = $HOMEMODE_de? "Falls ein oder mehr Anweseheits Geräte falsch zugeordnet wurden, so benenne diese bitte so um dass die Bewohner Namen (".join(",",@residentsshort).") nicht Bestandteil des Namen sind.\nNach dem Umbenennen führe einfach \"set $name updateInternalsForce\" aus um diese Überprüfung zu wiederholen.": @@ -651,16 +655,36 @@ sub HOMEMODE_updateInternals($;$) my $temperature = HOMEMODE_AttrCheck($hash,"HomeSensorTemperatureOutside"); push @allMonitoredDevices,$temperature if ($temperature && !grep /^$temperature$/,@allMonitoredDevices); my $humidity = HOMEMODE_AttrCheck($hash,"HomeSensorHumidityOutside"); - if ($humidity && $temperature ne $humidity) + if ($humidity) { push @allMonitoredDevices,$humidity if (!grep /^$humidity$/,@allMonitoredDevices); } - my $holiday = HOMEMODE_AttrCheck($hash,"HomeEventsHolidayDevices"); - if ($holiday) + my @cals; + CommandDeleteReading(undef,"$name event-.*"); + if (HOMEMODE_AttrCheck($hash,"HomeEventsHolidayDevices")) { - foreach my $c (devspec2array($holiday)) + foreach my $c (devspec2array(HOMEMODE_AttrCheck($hash,"HomeEventsHolidayDevices"))) { - push @allMonitoredDevices,$c if (!grep /^$c$/,@allMonitoredDevices); + push @cals,$c if (!IsDisabled($c) && !grep /^$c$/,@cals); + } + } + if (HOMEMODE_AttrCheck($hash,"HomeEventsCalendarDevices")) + { + foreach my $c (devspec2array(HOMEMODE_AttrCheck($hash,"HomeEventsCalendarDevices"))) + { + push @cals,$c if (!IsDisabled($c) && !grep /^$c$/,@cals); + } + } + foreach my $c (@cals) + { + push @allMonitoredDevices,$c if (!grep /^$c$/,@allMonitoredDevices); + if (HOMEMODE_ID($c,"Calendar")) + { + HOMEMODE_EventCommands($hash,$c,"modeStarted",ReadingsVal($c,"modeStarted","none")); + } + else + { + readingsSingleUpdate($hash,"event-$c",ReadingsVal($c,"state","none"),1); } } my $uwz = HOMEMODE_AttrCheck($hash,"HomeUWZ",""); @@ -695,7 +719,7 @@ sub HOMEMODE_updateInternals($;$) HOMEMODE_GetUpdate($hash); return if (!@allMonitoredDevices); HOMEMODE_RESIDENTS($hash); - HOMEMODE_userattr($hash); + HOMEMODE_userattr($hash) if ($force); HOMEMODE_TriggerState($hash) if ($hash->{SENSORSCONTACT} || $hash->{SENSORSMOTION}); HOMEMODE_Luminance($hash) if ($hash->{SENSORSLUMINANCE}); HOMEMODE_PowerEnergy($hash) if ($hash->{SENSORSENERGY}); @@ -857,7 +881,7 @@ sub HOMEMODE_Set($@) if (AttrNum($name,"HomeAutoArrival",0)) { my $hour = HOMEMODE_hourMaker(AttrNum($name,"HomeAutoArrival",0)); - CommandDelete(undef,"atTmp_set_home_$name") if (IsDevice("atTmp_set_home_$name")); + CommandDelete(undef,"atTmp_set_home_$name") if (HOMEMODE_ID("atTmp_set_home_$name","at")); CommandDefine(undef,"atTmp_set_home_$name at +$hour set $name:FILTER=location=arrival location home"); $location = "arrival"; } @@ -875,7 +899,7 @@ sub HOMEMODE_Set($@) if (AttrNum($name,"HomeModeAbsentBelatedTime",0) && AttrVal($name,"HomeCMDmode-absent-belated",undef)) { my $hour = HOMEMODE_hourMaker(AttrNum($name,"HomeModeAbsentBelatedTime",0)); - CommandDelete(undef,"atTmp_absent_belated_$name") if (IsDevice("atTmp_absent_belated_$name")); + CommandDelete(undef,"atTmp_absent_belated_$name") if (HOMEMODE_ID("atTmp_absent_belated_$name","at")); CommandDefine(undef,"atTmp_absent_belated_$name at +$hour {HOMEMODE_execCMDs_belated(\"$name\",\"HomeCMDmode-absent-belated\",\"$option\")}"); } } @@ -886,7 +910,7 @@ sub HOMEMODE_Set($@) CommandSet(undef,"$name:FILTER=location!=$location location $location"); if (AttrNum($name,"HomeAutoAlarmModes",1)) { - CommandDelete(undef,"atTmp_modeAlarm_delayed_arm_$name") if (IsDevice("atTmp_modeAlarm_delayed_arm_$name")); + CommandDelete(undef,"atTmp_modeAlarm_delayed_arm_$name") if (HOMEMODE_ID("atTmp_modeAlarm_delayed_arm_$name","at")); CommandSet(undef,"$name:FILTER=modeAlarm!=$namode modeAlarm $namode"); } readingsBeginUpdate($hash); @@ -901,9 +925,9 @@ sub HOMEMODE_Set($@) "$cmd benötigt zwei Parameter: einen modeAlarm und die Minuten": "$cmd needs two paramters: a modeAlarm and minutes"; return $trans if (!$option || !$value); - my $timer = $name."_alarmMode_for_timer_$option"; + my $timer = "atTmp_alarmMode_for_timer_$name"; my $time = HOMEMODE_hourMaker($value); - CommandDelete(undef,$timer) if (IsDevice($timer)); + CommandDelete(undef,$timer) if (HOMEMODE_ID($timer,"at")); CommandDefine(undef,"$timer at +$time set $name:FILTER=modeAlarm!=$amode modeAlarm $amode"); CommandSet(undef,"$name:FILTER=modeAlarm!=$option modeAlarm $option"); } @@ -917,9 +941,9 @@ sub HOMEMODE_Set($@) "$name darf nicht im dnd Modus sein um diesen Modus für bestimmte Minuten zu setzen! Bitte deaktiviere den dnd Modus zuerst!": "$name can't be in dnd mode to turn dnd on for minutes! Please disable dnd mode first!"; return $trans if (ReadingsVal($name,"dnd","off") eq "on"); - my $timer = $name."_dnd_for_timer"; + my $timer = "atTmp_dnd_for_timer_$name"; my $time = HOMEMODE_hourMaker($option); - CommandDelete(undef,$timer) if (IsDevice($timer)); + CommandDelete(undef,$timer) if (HOMEMODE_ID($timer,"at")); CommandDefine(undef,"$timer at +$time set $name:FILTER=dnd!=off dnd off"); CommandSet(undef,"$name:FILTER=dnd!=on dnd on"); } @@ -944,7 +968,7 @@ sub HOMEMODE_Set($@) } elsif ($cmd eq "modeAlarm") { - CommandDelete(undef,"atTmp_modeAlarm_delayed_arm_$name") if (IsDevice("atTmp_modeAlarm_delayed_arm_$name")); + CommandDelete(undef,"atTmp_modeAlarm_delayed_arm_$name") if (HOMEMODE_ID("atTmp_modeAlarm_delayed_arm_$name","at")); my $delay; if ($option =~ /^arm/ && AttrVal($name,"HomeModeAlarmArmDelay",0)) { @@ -997,7 +1021,7 @@ sub HOMEMODE_Set($@) } elsif ($cmd eq "updateInternalsForce") { - HOMEMODE_updateInternals($hash,1); + HOMEMODE_updateInternals($hash,1,1); } elsif ($cmd eq "updateHomebridgeMapping") { @@ -1183,7 +1207,7 @@ sub HOMEMODE_RESIDENTS($;$) elsif ($mode eq "awoken") { my $hours = HOMEMODE_hourMaker(AttrNum($name,"HomeAutoAwoken",0)); - CommandDelete(undef,"atTmp_awoken_".$dev."_$name") if (IsDevice("atTmp_awoken_".$dev."_$name")); + CommandDelete(undef,"atTmp_awoken_".$dev."_$name") if (HOMEMODE_ID("atTmp_awoken_".$dev."_$name","at")); CommandDefine(undef,"atTmp_awoken_".$dev."_$name at +$hours set $dev:FILTER=state=awoken state home"); } } @@ -1191,13 +1215,13 @@ sub HOMEMODE_RESIDENTS($;$) { my $hours = HOMEMODE_hourMaker(AttrNum($name,"HomeAutoArrival",0)); AnalyzeCommandChain(undef,"sleep 0.1; set $dev:FILTER=location!=arrival location arrival"); - CommandDelete(undef,"atTmp_location_home_".$dev."_$name") if (IsDevice("atTmp_location_home_".$dev."_$name")); + CommandDelete(undef,"atTmp_location_home_".$dev."_$name") if (HOMEMODE_ID("atTmp_location_home_".$dev."_$name","at")); CommandDefine(undef,"atTmp_location_home_".$dev."_$name at +$hours set $dev:FILTER=location=arrival location home"); } if ($mode eq "gotosleep" && AttrNum($name,"HomeAutoAsleep",0)) { my $hours = HOMEMODE_hourMaker(AttrNum($name,"HomeAutoAsleep",0)); - CommandDelete(undef,"atTmp_asleep_".$dev."_$name") if (IsDevice("atTmp_asleep_".$dev."_$name")); + CommandDelete(undef,"atTmp_asleep_".$dev."_$name") if (HOMEMODE_ID("atTmp_asleep_".$dev."_$name","at")); CommandDefine(undef,"atTmp_asleep_".$dev."_$name at +$hours set $dev:FILTER=state=gotosleep state asleep"); } push @commands,AttrVal($name,"HomeCMDmode-$mode-resident","") if (AttrVal($name,"HomeCMDmode-$mode-resident",undef)); @@ -1317,6 +1341,7 @@ sub HOMEMODE_Attributes($) push @attribs,"HomeCMDuwz-warn-begin:textField-long"; push @attribs,"HomeCMDuwz-warn-end:textField-long"; push @attribs,"HomeDaytimes:textField-long"; + push @attribs,"HomeEventsCalendarDevices"; push @attribs,"HomeEventsHolidayDevices"; push @attribs,"HomeIcewarningOnOffTemps"; push @attribs,"HomeLanguage:DE,EN"; @@ -1369,7 +1394,6 @@ sub HOMEMODE_Attributes($) push @attribs,"HomeTwilightDevice"; push @attribs,"HomeUWZ"; push @attribs,"HomeYahooWeatherDevice"; - push @attribs,$readingFnAttributes; return join(" ",@attribs); } @@ -1381,11 +1405,6 @@ sub HOMEMODE_userattr($) my @userattrAll; my @homeattr; my @stayattr; - my $specialevents = HOMEMODE_AttrCheck($hash,"HomeEventsHolidayDevices"); - my $specialmodes = HOMEMODE_AttrCheck($hash,"HomeSpecialModes"); - my $speciallocations = HOMEMODE_AttrCheck($hash,"HomeSpecialLocations"); - my $daytimes = HOMEMODE_AttrCheck($hash,"HomeDaytimes",$HOMEMODE_Daytimes); - my $seasons = HOMEMODE_AttrCheck($hash,"HomeSeasons",$HOMEMODE_Seasons); foreach (split " ",AttrVal($name,"userattr","")) { if ($_ =~ /^Home/) @@ -1397,30 +1416,51 @@ sub HOMEMODE_userattr($) push @stayattr,$_; } } - foreach (split /,/,$specialmodes) + foreach (split /,/,HOMEMODE_AttrCheck($hash,"HomeSpecialModes")) { push @userattrAll,"HomeCMDmode-$_"; } - foreach (split /,/,$speciallocations) + foreach (split /,/,HOMEMODE_AttrCheck($hash,"HomeSpecialLocations")) { push @userattrAll,"HomeCMDlocation-$_"; } - foreach my $cal (devspec2array($specialevents)) + if (HOMEMODE_AttrCheck($hash,"HomeEventsHolidayDevices")) { - my $events = HOMEMODE_HolidayEvents($cal); - push @userattrAll,"HomeCMDevent-$cal-each"; - if ($adv) + foreach my $cal (devspec2array(HOMEMODE_AttrCheck($hash,"HomeEventsHolidayDevices"))) { - foreach my $evt (@{$events}) + next if (IsDisabled($cal)); + my $events = HOMEMODE_CalendarEvents($name,$cal); + push @userattrAll,"HomeCMDevent-$cal-each"; + if ($adv) { - push @userattrAll,"HomeCMDevent-$cal-$evt-begin"; - push @userattrAll,"HomeCMDevent-$cal-$evt-end"; + foreach my $evt (@{$events}) + { + push @userattrAll,"HomeCMDevent-$cal-$evt-begin"; + push @userattrAll,"HomeCMDevent-$cal-$evt-end"; + } + } + } + } + if (HOMEMODE_AttrCheck($hash,"HomeEventsCalendarDevices")) + { + foreach my $cal (devspec2array(HOMEMODE_AttrCheck($hash,"HomeEventsCalendarDevices"))) + { + next if (IsDisabled($cal)); + my $events = HOMEMODE_CalendarEvents($name,$cal); + push @userattrAll,"HomeCMDevent-$cal-each"; + if ($adv) + { + foreach my $evt (@{$events}) + { + push @userattrAll,"HomeCMDevent-$cal-$evt-begin"; + push @userattrAll,"HomeCMDevent-$cal-$evt-end"; + } } } } foreach my $resident (split /,/,$hash->{RESIDENTS}) { - my $devtype = IsDevice($resident) ? $defs{$resident}->{TYPE} : ""; + my $devtype = HOMEMODE_ID($resident,"ROOMMATE|GUEST") ? $defs{$resident}->{TYPE} : ""; next if (!$devtype); if ($adv) { @@ -1459,7 +1499,7 @@ sub HOMEMODE_userattr($) } } } - foreach (split " ",$daytimes) + foreach (split " ",HOMEMODE_AttrCheck($hash,"HomeDaytimes",$HOMEMODE_Daytimes)) { my $text = (split /\|/)[1]; my $d = "HomeCMDdaytime-$text"; @@ -1467,7 +1507,7 @@ sub HOMEMODE_userattr($) push @userattrAll,$d if (!grep /^$d$/,@userattrAll); push @userattrAll,$m if (!grep /^$m$/,@userattrAll); } - foreach (split " ",$seasons) + foreach (split " ",HOMEMODE_AttrCheck($hash,"HomeSeasons",$HOMEMODE_Seasons)) { my $text = (split /\|/)[1]; my $s = "HomeCMDseason-$text"; @@ -1589,19 +1629,24 @@ sub HOMEMODE_Attr(@) return $err if ($err); } } - elsif ($attr_name eq "HomeEventsHolidayDevices" && $init_done) + elsif ($attr_name =~ /^HomeEvents(Holiday|Calendar)Devices$/ && $init_done) { - my $wd = HOMEMODE_CheckHolidayDevices($attr_value); - if ($wd) + my @wd; + foreach (devspec2array($attr_value)) + { + next unless (!HOMEMODE_ID($_,"holiday|Calendar")); + push @wd,$_ ; + } + if (@wd) { $trans = $HOMEMODE_de? - "Ungültige holiday Geräte gefunden: ": - "Invalid holiday device(s) found: "; - return $trans.join(",",@{$wd}); + "Ungültige Calendar/holiday Geräte gefunden: ": + "Invalid Calendar/holiday device(s) found: "; + return $trans.join(",",@wd); } else { - HOMEMODE_updateInternals($hash); + HOMEMODE_updateInternals($hash,1); } } elsif ($attr_name =~ /^(HomePresenceDeviceType)$/ && $init_done) @@ -1682,7 +1727,7 @@ sub HOMEMODE_Attr(@) "Invalid value $attr_value for attribute $attr_name. Numbers from 0 to 9999 are allowed only!"; return $trans if ($attr_value !~ /^\d{1,4}$/); } - elsif ($attr_name =~ /^(HomeSpecialModes|HomeSpecialLocations)$/ && $init_done) + elsif ($attr_name =~ /^HomeSpecial(Modes|Locations)$/ && $init_done) { $trans = $HOMEMODE_de? "Ungültiger Wert $attr_value für Attribut $attr_name. Muss eine Komma separierte Liste von Wörtern sein!": @@ -1937,8 +1982,10 @@ sub HOMEMODE_Attr(@) { $HOMEMODE_de = AttrVal("global","language","DE") ? 1 : undef; } - elsif ($attr_name =~ /^(HomeAdvancedUserAttr|HomeAutoPresence|HomePresenceDeviceType|HomeEventsHolidayDevices|HomeSensorAirpressure|HomeSensorWindspeed|HomeSensorsBattery|HomeSensorsBatteryReading)$/) + elsif ($attr_name =~ /^(HomeAdvancedUserAttr|HomeAutoPresence|HomePresenceDeviceType|HomeEvents(Holiday|Calendar)Devices|HomeSensorAirpressure|HomeSensorWindspeed|HomeSensorsBattery|HomeSensorsBatteryReading)$/) { + CommandDeleteReading(undef,"$name event-.*") if ($attr_name =~ /^HomeEvents(Holiday|Calendar)Devices$/); + CommandDeleteReading(undef,"$name battery.*") if ($attr_name eq "HomeSensorsBattery"); HOMEMODE_updateInternals($hash,1); } elsif ($attr_name =~ /^(HomeSensorsContact|HomeSensorsMotion)$/) @@ -2102,17 +2149,46 @@ sub HOMEMODE_replacePlaceholders($$;$) $cmd =~ s/%DEVICEA%/$apdevice/g; $cmd =~ s/%DEVICEP%/$ppdevice/g; $cmd =~ s/%DND%/$dnd/g; - if (AttrVal($name,"HomeEventsHolidayDevices",undef)) + if (AttrVal($name,"HomeEventsHolidayDevices",undef) || AttrVal($name,"HomeEventsHolidayDevices",undef)) { - foreach my $cal (devspec2array(AttrVal($name,"HomeEventsHolidayDevices",""))) + my @cals; + if (AttrVal($name,"HomeEventsHolidayDevices","")) { - my $state = ReadingsVal($name,"event-$cal","") ne "none" ? ReadingsVal($name,"event-$cal","") : 0; - $cmd =~ s/%$cal%/$state/g; - my $events = HOMEMODE_HolidayEvents($cal); - foreach my $evt (@{$events}) + foreach (devspec2array(AttrVal($name,"HomeEventsHolidayDevices",""))) { - my $val = $state eq $evt ? 1 : 0; - $cmd =~ s/%$cal-$evt%/$val/g; + push @cals,$_ if (!IsDisabled($_)); + } + } + else + { + foreach (devspec2array(AttrVal($name,"HomeEventsCalendarDevices",""))) + { + push @cals,$_ if (!IsDisabled($_)); + } + } + foreach my $cal (@cals) + { + my $state = ReadingsVal($name,"event-$cal","none") ne "none" ? ReadingsVal($name,"event-$cal","") : ""; + $cmd =~ s/%$cal%/$state/g; + my $events = HOMEMODE_CalendarEvents($name,$cal); + if (HOMEMODE_ID($cal,"holiday")) + { + foreach my $evt (@{$events}) + { + my $val = $state eq $evt ? 1 : ""; + $cmd =~ s/%$cal-$evt%/$val/g; + } + } + else + { + foreach my $evt (@{$events}) + { + foreach my $e (split /,/,$state) + { + my $val = $e eq $evt ? 1 : ""; + $cmd =~ s/%$cal-$evt%/$val/g; + } + } } } } @@ -2292,31 +2368,42 @@ sub HOMEMODE_uwzTXT($;$$) my ($hash,$count,$sl) = @_; my $name = $hash->{NAME}; $count = defined $count ? $count : ReadingsVal($name,"uwz_warnCount",0); - $sl = $sl ? "LongText" : "ShortText"; my $text = ""; for (my $i = 0; $i < $count; $i++) { - my $read = "Warn_$i"; - my $ii = $i + 1; $text .= " " if ($i > 0); - $text .= "$ii. " if ($count > 1); - $text .= ReadingsVal(AttrVal($name,"HomeUWZ",""),$read."_$sl",""); + $text .= $i + 1 . " " if ($count > 1); + $sl = $sl ? "LongText" : "ShortText"; + $text .= ReadingsVal(AttrVal($name,"HomeUWZ",""),"Warn_".$i."_$sl",""); } return $text; } +sub HOMEMODE_ID($;$$$) +{ + my ($devname,$devtype,$devread,$readval) = @_; + return 0 + if (!defined($devname) || !defined($defs{$devname})); + return 0 + if (defined($devtype) && $defs{$devname}{TYPE} !~ /^$devtype$/); + return 0 + if (defined($devread) && !defined(ReadingsVal($devname,$devread,undef))); + return 0 + if (defined($readval) && ReadingsVal($devname,$devread,"") !~ /^$readval$/); + return $devname; +} + sub HOMEMODE_CheckIfIsValidDevspec($;$) { my ($spec,$read) = @_; my @names; foreach (devspec2array($spec)) { - next unless (IsDevice($_)); - next if ($read && !defined ReadingsVal($_,$read,undef)); + next unless (HOMEMODE_ID($_,undef,$read)); push @names,$_; } - return if (!@names); - return \@names; + return \@names if (@names); + return; } sub HOMEMODE_execUserCMDs($) @@ -2603,7 +2690,7 @@ sub HOMEMODE_TriggerState($;$$$) readingsEndUpdate($hash,1); HOMEMODE_ContactCommands($hash,$sensor,"closed",$kind); my $timer = "atTmp_HomeOpenTimer_".$sensor."_$name"; - CommandDelete(undef,$timer) if (IsDevice($timer)); + CommandDelete(undef,$timer) if (HOMEMODE_ID($timer,"at")); } } if ($tread && $tstate =~ /^($otcmd)$/) @@ -2765,7 +2852,7 @@ sub HOMEMODE_ContactOpenCheck($$;$$) } } my $timer = "atTmp_HomeOpenTimer_".$contact."_$name"; - CommandDelete(undef,$timer) if (IsDevice($timer) && ($retrigger || $donttrigger)); + CommandDelete(undef,$timer) if (HOMEMODE_ID($timer,"at") && ($retrigger || $donttrigger)); return if ((!$retrigger && $donttrigger) || $donttrigger); my $season = ReadingsVal($name,"season",""); my $seasons = AttrVal($name,"HomeSeasons",$HOMEMODE_Seasons); @@ -2805,7 +2892,7 @@ sub HOMEMODE_ContactOpenCheck($$;$$) my $opencmds = AttrVal($contact,"HomeValues",AttrVal($name,"HomeSensorsContactValues","open|tilted|on")); if ($state =~ /^($opencmds|open)$/) { - CommandDefine(undef,"$timer at +$waittime $at") if ($at && !IsDevice($timer)); + CommandDefine(undef,"$timer at +$waittime $at") if ($at && !HOMEMODE_ID($timer)); if ($retrigger > 1) { my @commands; @@ -2906,34 +2993,98 @@ sub HOMEMODE_MotionCommands($$$) } } -sub HOMEMODE_EventCommands($$$) +sub HOMEMODE_EventCommands($$$$) { - my ($hash,$cal,$event) = @_; + my ($hash,$cal,$read,$event) = @_; my $name = $hash->{NAME}; my $prevevent = ReadingsVal($name,"event-$cal",""); - if ($event ne $prevevent) + my @cmds; + if ($read ne "modeStarted") { - my $evt = $event; - $evt =~ s/\s+/-/g; - my $pevt = $prevevent; - $pevt =~ s/\s+/-/g; - my @cmds; push @cmds,AttrVal($name,"HomeCMDevent","") if (AttrVal($name,"HomeCMDevent",undef)); push @cmds,AttrVal($name,"HomeCMDevent-$cal-each","") if (AttrVal($name,"HomeCMDevent-$cal-each",undef)); - push @cmds,AttrVal($name,"HomeCMDevent-$cal-$evt-begin","") if (AttrVal($name,"HomeCMDevent-$cal-$evt-begin",undef)); - push @cmds,AttrVal($name,"HomeCMDevent-$cal-$pevt-end","") if (AttrVal($name,"HomeCMDevent-$cal-$pevt-end",undef)); - if (@cmds) + } + if (HOMEMODE_ID($cal,"holiday")) + { + if ($event ne $prevevent) { + $event =~ s/[,;]//g; + my $evt = $event; + $evt =~ s/[\s ]+/-/g; + my $pevt = $prevevent; + $pevt =~ s/[\s ]+/-/g; + push @cmds,AttrVal($name,"HomeCMDevent-$cal-$pevt-end","") if (AttrVal($name,"HomeCMDevent-$cal-$pevt-end",undef)); + push @cmds,AttrVal($name,"HomeCMDevent-$cal-$evt-begin","") if (AttrVal($name,"HomeCMDevent-$cal-$evt-begin",undef)); + readingsSingleUpdate($hash,"event-$cal",$event,1); foreach (@cmds) { - $_ =~ s/%CALENDAR%/$cal/gm; $_ =~ s/%EVENT%/$event/gm; $_ =~ s/%PREVEVENT%/$prevevent/gm; } - HOMEMODE_execCMDs($hash,HOMEMODE_serializeCMD($hash,@cmds)); } - readingsSingleUpdate($hash,"event-$cal",$event,1); } + else + { + my @prevevents; + @prevevents = split /,/,$prevevent if ($prevevent ne "none"); + foreach (split /;/,$event) + { + $event =~ s/[\s ]//g; + my $summary; + foreach (Calendar_GetEvents($defs{$cal},time(),undef,undef)) + { + next if ($_->{uid} ne $event); + $summary = $_->{summary}; + } + $summary =~ s/[,;]//g; + Log3 $name,5,"Calendar_GetEvents event: $summary"; + my $sum = $summary; + $sum =~ s/[\s ]+/-/g; + if ($read eq "start") + { + push @prevevents,$summary if (!grep /^$summary$/,@prevevents); + push @cmds,AttrVal($name,"HomeCMDevent-$cal-$sum-begin","") if (AttrVal($name,"HomeCMDevent-$cal-$sum-begin",undef)); + } + elsif ($read eq "end") + { + push @cmds,AttrVal($name,"HomeCMDevent-$cal-$sum-end","") if (AttrVal($name,"HomeCMDevent-$cal-$sum-end",undef)); + if (grep /^$summary$/,@prevevents) + { + my @sevents; + foreach (@prevevents) + { + push @sevents,$_ if ($_ ne $summary); + } + @prevevents = @sevents; + } + } + elsif ($read eq "modeStarted") + { + push @prevevents,$summary if (!grep /^$summary$/,@prevevents); + } + foreach (@cmds) + { + if ($read eq "start") + { + $_ =~ s/%EVENT%/$summary/gm; + $_ =~ s/%PREVEVENT%/none/gm; + } + elsif ($read eq "end") + { + $_ =~ s/%EVENT%/none/gm; + $_ =~ s/%PREVEVENT%/$summary/gm; + } + } + } + my $update = "none"; + $update = join ",",@prevevents if (@prevevents); + readingsSingleUpdate($hash,"event-$cal",$update,1); + } + foreach (@cmds) + { + $_ =~ s/%CALENDAR%/$cal/gm; + } + HOMEMODE_execCMDs($hash,HOMEMODE_serializeCMD($hash,@cmds)) if (@cmds); } sub HOMEMODE_UWZCommands($$) @@ -3148,44 +3299,51 @@ sub HOMEMODE_Icewarning($) } } -sub HOMEMODE_CheckHolidayDevices($) +sub HOMEMODE_CalendarEvents($$) { - my ($specs) = @_; - my @wrongdevices; - foreach (devspec2array($specs)) - { - push @wrongdevices,$_ if (!IsDevice($_,"holiday")); - } - return \@wrongdevices if (@wrongdevices); - return; -} - -sub HOMEMODE_HolidayEvents($) -{ - my ($calendar) = @_; + my ($name,$cal) = @_; my @events; - my $fname = AttrVal("global","modpath",".")."/FHEM/".$calendar.".holiday"; - my (undef,@holidayfile) = FileRead($fname); - foreach (@holidayfile) + if (HOMEMODE_ID($cal,"holiday")) { - next if ($_ =~ /^\s*(#|$)/); - my @parts = split; - my $part = $parts[0] =~ /^(1|2)$/ ? 2 : $parts[0] == 3 ? 4 : $parts[0] == 4 ? 3 : 5; - for (my $p = 0; $p < $part; $p++) + my $fname = AttrVal("global","modpath",".")."/FHEM/".$cal.".holiday"; + my (undef,@holidayfile) = FileRead($fname); + foreach (@holidayfile) { - shift @parts; + next if ($_ =~ /^\s*(#|$)/); + my @parts = split; + my $part = $parts[0] =~ /^(1|2)$/ ? 2 : $parts[0] == 3 ? 4 : $parts[0] == 4 ? 3 : 5; + for (my $p = 0; $p < $part; $p++) + { + shift @parts; + } + my $evt = join("-",@parts); + push @events,$evt if (!grep /^$evt$/,@events); } - push @events,join("-",@parts); } - return (\@events); + else + { + foreach (Calendar_GetEvents($defs{$cal},time(),undef,undef)) + { + my $evt = $_->{summary}; + Log3 $name,5,"Calendar_GetEvents event: $evt"; + $evt =~ s/[,;]//g; + $evt =~ s/[\s ]+/-/g; + push @events,$evt if (!grep /^$evt$/,@events); + } + } + return \@events; } sub HOMEMODE_checkIP($;$) { my ($hash,$r) = @_; my $name = $hash->{NAME}; - my $ip = GetFileFromURL("http://icanhazip.com/"); - return if (!$ip); + my $url = "http://icanhazip.com/"; + my $ip = GetFileFromURL($url); + if (!$ip || $ip =~ /[<>]/) + { + return $r ? "publicIP service check ($url) is temporary not available" : undef; + } $ip =~ s/\s+//g; chomp $ip; if (ReadingsVal($name,"publicIP","") ne $ip) @@ -3200,8 +3358,7 @@ sub HOMEMODE_checkIP($;$) my $timer = gettimeofday() + 60 * AttrNum($name,"HomePublicIpCheckInterval",0); $hash->{".IP_TRIGGERTIME_NEXT"} = $timer; } - return $ip if ($r); - return; + return $r ? $ip : undef; } sub HOMEMODE_Details($$$) @@ -3700,7 +3857,11 @@ sub HOMEMODE_Details($$$)
  • HomeEventsHolidayDevices
    - devspec of holiday calendars + devspec of Calendar/holiday calendars +
  • +
  • + HomeEventsCalendarDevices
    + devspec of Calendar/holiday calendars
  • HomeIcewarningOnOffTemps
    @@ -3841,7 +4002,7 @@ sub HOMEMODE_Details($$$) default: 10
  • - HomeOpenTimesDividers
    + HomeOpenTimeDividers
    space separated list of trigger time dividers for contact sensor open warnings depending on the season of the HOMEMODE device.
    dividers in same order and same number as seasons in attribute HomeSeasons
    dividers are not used for contact sensors of type doormain and doorinside!
    @@ -4088,6 +4249,18 @@ sub HOMEMODE_Details($$$)

    Readings