diff --git a/fhem/FHEM/59_Twilight.pm b/fhem/FHEM/59_Twilight.pm index 05abb8682..da21a3897 100644 --- a/fhem/FHEM/59_Twilight.pm +++ b/fhem/FHEM/59_Twilight.pm @@ -4,6 +4,7 @@ # 59_Twilight.pm # Copyright by Sebastian Stuecker # erweitert von Dietmar Ortmann +# Maintained by igami since 02-2018 # # used algorithm see: http://lexikon.astronomie.info/zeitgleichung/ # @@ -37,7 +38,7 @@ use warnings; use POSIX; use HttpUtils; use Math::Trig; -use Time::Local 'timelocal_nocheck'; +use Time::Local 'timelocal_nocheck'; sub Twilight_calc($$); sub Twilight_my_gmt_offset(); @@ -108,30 +109,30 @@ sub Twilight_Define($$) if(int(@a)>4) { if ($a[4] =~ /^[\+-]*[0-9]*\.*[0-9]*$/ && $a[4] !~ /^[\. ]*$/ ) { $indoor_horizon = $a[4]; if($indoor_horizon > 20) { $indoor_horizon=20;} - # minimal indoor_horizon makes values like civil_sunset and civil_sunrise + # minimal indoor_horizon makes values like civil_sunset and civil_sunrise if($indoor_horizon < -6) { $indoor_horizon= -6;} }else{ return "Argument Indoor_Horizon is not a valid number";} } - + $hash->{WEATHER_HORIZON} = 0; $hash->{INDOOR_HORIZON} = $indoor_horizon; $hash->{LATITUDE} = $latitude; $hash->{LONGITUDE} = $longitude; $hash->{WEATHER} = $weather; - $hash->{VERSUCHE} = 0; + $hash->{VERSUCHE} = 0; $hash->{DEFINE} = 1; $hash->{CONDITION} = 50; $hash->{SUNPOS_OFFSET} = 5*60; - + $attr{$name}{verbose} = 4 if ($name =~ /^tst.*$/ ); - + my $mHash = { HASH=>$hash }; - Twilight_sunpos($mHash); + Twilight_sunpos($mHash); Twilight_Midnight($mHash); delete $hash->{DEFINE}; - + return undef; } ################################################################################ @@ -152,14 +153,14 @@ sub myInternalTimer($$$$$) { my ($modifier, $tim, $callback, $hash, $waitIfInitNotDone) = @_; my $timerName = "$hash->{NAME}_$modifier"; - my $mHash = { HASH=>$hash, NAME=>"$hash->{NAME}_$modifier", MODIFIER=>$modifier}; + my $mHash = { HASH=>$hash, NAME=>"$hash->{NAME}_$modifier", MODIFIER=>$modifier}; if (defined($hash->{TIMER}{$timerName})) { Log3 $hash, 1, "[$hash->{NAME}] possible overwriting of timer $timerName - please delete first"; stacktrace(); } else { $hash->{TIMER}{$timerName} = $mHash; } - + Log3 $hash, 5, "[$hash->{NAME}] setting Timer: $timerName " . FmtDateTime($tim); InternalTimer($tim, $callback, $mHash, $waitIfInitNotDone); return $mHash; @@ -191,12 +192,12 @@ sub myRemoveInternalTimer($$) { ################################################################################ sub myGetHashIndirekt ($$) { my ($myHash, $function) = @_; - + if (!defined($myHash->{HASH})) { - Log 3, "[$function] myHash not valid"; + Log 3, "[$function] myHash not valid"; return undef; - }; - return $myHash->{HASH}; + }; + return $myHash->{HASH}; } ################################################################################ sub Twilight_midnight_seconds($) { @@ -207,12 +208,12 @@ sub Twilight_midnight_seconds($) { } ################################################################################ #sub Twilight_ssTimeAsEpoch($) { -# my ($zeit) = @_; +# my ($zeit) = @_; # my ($hour, $min, $sec) = split(":",$zeit); # # my $days=0; -# if ($hour>=24) {$days = 1; $hour -=24}; -# +# if ($hour>=24) {$days = 1; $hour -=24}; +# # my @jetzt_arr = localtime(time()); # #Stunden Minuten Sekunden # $jetzt_arr[2] = $hour; $jetzt_arr[1] = $min; $jetzt_arr[0] = $sec; @@ -224,21 +225,21 @@ sub Twilight_midnight_seconds($) { ################################################################################ sub Twilight_calc($$) { my ($deg, $idx) = @_; - + my $midnight = time() - Twilight_midnight_seconds(time()); my $sr = sunrise_abs("Horizon=$deg"); my $ss = sunset_abs ("Horizon=$deg"); my ($srhour, $srmin, $srsec) = split(":",$sr); $srhour -= 24 if($srhour>=24); - my ($sshour, $ssmin, $sssec) = split(":",$ss); $sshour -= 24 if($sshour>=24); + my ($sshour, $ssmin, $sssec) = split(":",$ss); $sshour -= 24 if($sshour>=24); my $sr1 = $midnight + 3600*$srhour+60*$srmin+$srsec; my $ss1 = $midnight + 3600*$sshour+60*$ssmin+$sssec; - - return (0,0) if (abs ($sr1 - $ss1) < 30); + + return (0,0) if (abs ($sr1 - $ss1) < 30); #return Twilight_ssTimeAsEpoch($sr) + 0.01*$idx, - # Twilight_ssTimeAsEpoch($ss) - 0.01*$idx; + # Twilight_ssTimeAsEpoch($ss) - 0.01*$idx; return ($sr1 + 0.01*$idx), ($ss1 - 0.01*$idx); } ################################################################################ @@ -258,32 +259,32 @@ sub Twilight_TwilightTimes(@) { foreach my $horizon (@horizons) { $idx++; next if ($whitchTimes eq "weather" && !($horizon =~ m/weather/) ); - my ($name, $deg) = split(":", $horizon); - my $sr = "sr$name"; my $ss = "ss$name"; + my ($name, $deg) = split(":", $horizon); + my $sr = "sr$name"; my $ss = "ss$name"; $hash->{TW}{$sr}{NAME} = $sr; $hash->{TW}{$ss}{NAME} = $ss; $hash->{TW}{$sr}{DEG} = $deg; $hash->{TW}{$ss}{DEG} = $deg; $hash->{TW}{$sr}{LIGHT} = $idx+1;$hash->{TW}{$ss}{LIGHT} = $idx; $hash->{TW}{$sr}{STATE} = $idx+1;$hash->{TW}{$ss}{STATE} = 12 - $idx; $hash->{TW}{$sr}{SWIP} = $swip; $hash->{TW}{$ss}{SWIP} = $swip; - + ($hash->{TW}{$sr}{TIME}, $hash->{TW}{$ss}{TIME}) = Twilight_calc ($deg, $idx); - + if ($hash->{TW}{$sr}{TIME} == 0) { Log3 $hash, 4, "[$Name] hint: $hash->{TW}{$sr}{NAME}, $hash->{TW}{$ss}{NAME} are not defined(HORIZON=$deg)"; } } - $attr{global}{latitude} = $lat; - $attr{global}{longitude} = $long; + $attr{global}{latitude} = $lat; + $attr{global}{longitude} = $long; # ------------------------------------------------------------------------------ readingsBeginUpdate ($hash); foreach my $ereignis (keys %{$hash->{TW}}) { next if ($whitchTimes eq "weather" && !($ereignis =~ m/weather/) ); readingsBulkUpdate($hash, $ereignis, $hash->{TW}{$ereignis}{TIME} == 0 ? "undefined" : FmtTime($hash->{TW}{$ereignis}{TIME})); - } + } if ($hash->{CONDITION} != 50 ) { readingsBulkUpdate ($hash,"condition", $hash->{CONDITION}); readingsBulkUpdate ($hash,"condition_txt",$hash->{CONDITION_TXT}); - } + } readingsEndUpdate ($hash, defined($hash->{LOCAL} ? 0 : 1)); # ------------------------------------------------------------------------------ my @horizonsOhneDeg = map {my($e, $deg)=split(":",$_); "$e"} @horizons; @@ -296,18 +297,18 @@ sub Twilight_TwilightTimes(@) { my $lastMitternacht = $now-$secSinceMidnight; my $nextMitternacht = ($secSinceMidnight > 12*3600) ? $lastMitternacht+24*3600 : $lastMitternacht; my $jetztIstMitternacht = abs($now+5-$nextMitternacht)<=10; - + my @keyListe = qw "DEG LIGHT STATE SWIP TIME NAMENEXT"; foreach my $ereignis (sort keys %{$hash->{TW}}) { next if ($whitchTimes eq "weather" && !($ereignis =~ m/weather/) ); - + myRemoveInternalTimer($ereignis, $hash); # if(!$jetztIstMitternacht); if($hash->{TW}{$ereignis}{TIME} > 0) { $myHash = myInternalTimer($ereignis, $hash->{TW}{$ereignis}{TIME}, "Twilight_fireEvent", $hash, 0); - map {$myHash->{$_} = $hash->{TW}{$ereignis}{$_} } @keyListe; + map {$myHash->{$_} = $hash->{TW}{$ereignis}{$_} } @keyListe; } } -# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ return 1; } ################################################################################ @@ -318,7 +319,7 @@ sub Twilight_fireEvent($) { return if (!defined($hash)); my $name = $hash->{NAME}; - + my $event = $myHash->{MODIFIER}; my $deg = $myHash->{DEG}; my $light = $myHash->{LIGHT}; @@ -330,18 +331,18 @@ sub Twilight_fireEvent($) { my $delta = int($eventTime - time()); my $oldState = ReadingsVal($name,"state","0"); - + my $nextEventTime = ($hash->{TW}{$nextEvent}{TIME} > 0) ? FmtTime($hash->{TW}{$nextEvent}{TIME}) : "undefined"; - + my $doTrigger = !(defined($hash->{LOCAL})) && ( abs($delta)<6 || $swip && $state gt $oldState); #Log3 $hash, 3, "[$hash->{NAME}] swip-delta-oldState-doTrigger===>$swip/$delta/$oldState/$doTrigger"; - + Log3 $hash, 4, sprintf ("[$hash->{NAME}] %-10s %-19s ", $event, FmtDateTime($eventTime)). - sprintf ("(%2d/$light/%+5.1f°/$doTrigger) ", $state, $deg). + sprintf ("(%2d/$light/%+5.1f°/$doTrigger) ", $state, $deg). sprintf ("===> %-10s %-19s ", $nextEvent, $nextEventTime); - + readingsBeginUpdate($hash); readingsBulkUpdate ($hash, "state", $state); readingsBulkUpdate ($hash, "light", $light); @@ -355,11 +356,11 @@ sub Twilight_fireEvent($) { } ################################################################################ sub Twilight_Midnight($) { - my ($myHash) = @_; + my ($myHash) = @_; my $hash = myGetHashIndirekt($myHash, (caller(0))[3]); - return if (!defined($hash)); - - $hash->{SWIP} = 0; + return if (!defined($hash)); + + $hash->{SWIP} = 0; my $param = Twilight_CreateHttpParameterAndGetData($myHash, "Mid"); } ################################################################################ @@ -367,24 +368,24 @@ sub Twilight_Midnight($) { sub Twilight_WeatherTimerUpdate($) { my ($myHash) = @_; my $hash = myGetHashIndirekt($myHash, (caller(0))[3]); - return if (!defined($hash)); + return if (!defined($hash)); - $hash->{SWIP} = 1; + $hash->{SWIP} = 1; my $param = Twilight_CreateHttpParameterAndGetData($myHash, "weather"); -} +} ################################################################################ sub Twilight_CreateHttpParameterAndGetData($$) { my ($myHash, $mode) = @_; my $hash = myGetHashIndirekt($myHash, (caller(0))[3]); - return if (!defined($hash)); - + return if (!defined($hash)); + my $location = $hash->{WEATHER}; my $verbose = AttrVal($hash->{NAME}, "verbose", 3 ); - + my $URL = "http://query.yahooapis.com/v1/public/yql?q=select%%20*%%20from%%20weather.forecast%%20where%%20woeid=%s%%20and%%20u=%%27c%%27&format=%s&env=store%%3A%%2F%%2Fdatatables.org%%2Falltableswithkeys"; my $url = sprintf($URL, $location, "json"); Log3 $hash, 4, "[$hash->{NAME}] url=$url"; - + my $param = { url => $url, timeout => defined($hash->{DEFINE}) ? 10 :10, @@ -394,23 +395,23 @@ sub Twilight_CreateHttpParameterAndGetData($$) { header => "User-Agent: Mozilla/5.0\r\nAccept: application/xml", callback => \&Twilight_WeatherCallback, mode => $mode }; - + if (defined($hash->{DEFINE})) { delete $param->{callback}; my ($err, $result) = HttpUtils_BlockingGet($param); Twilight_WeatherCallback($param, $err, $result); } else { HttpUtils_NonblockingGet($param); - } - + } + } ################################################################################ sub Twilight_WeatherCallback(@) { my ($param, $err, $result) = @_; - + my $hash = $param->{hash}; return if (!defined($hash)); - + if ($err) { Log3 $hash, 3, "[$hash->{NAME}] got no weather info from yahoo. Error code: $err"; $result = undef; @@ -418,22 +419,22 @@ sub Twilight_WeatherCallback(@) { Log3 $hash, 4, "[$hash->{NAME}] got weather info from yahoo for $hash->{WEATHER}"; Log3 $hash, 5, "[$hash->{NAME}] answer=$result" if defined $result; } - + Twilight_getWeatherHorizon($hash, $result); - #$hash->{CONDITION} = 50; - + #$hash->{CONDITION} = 50; + if ($hash->{CONDITION} == 50 && $hash->{VERSUCHE} <= 10) { $hash->{VERSUCHE} += 1; Twilight_RepeatTimerSet($hash, $param->{mode}); return; } - + Twilight_TwilightTimes ($hash, $param->{mode}, $result); - + Log3 $hash, 3, "[$hash->{NAME}] " . ($hash->{VERSUCHE}+1) . " attempt(s) needed to get valid weather data from yahoo" if ($hash->{CONDITION} != 50 && $hash->{VERSUCHE} > 0); Log3 $hash, 3, "[$hash->{NAME}] " . ($hash->{VERSUCHE}+1) . " attempt(s) needed got NO valid weather data from yahoo" if ($hash->{CONDITION} == 50 && $hash->{VERSUCHE} > 0); - $hash->{VERSUCHE} = 0; - + $hash->{VERSUCHE} = 0; + Twilight_StandardTimerSet ($hash); } ################################################################################ @@ -446,8 +447,8 @@ sub Twilight_RepeatTimerSet($$) { myInternalTimer ("Midnight", $midnight, "Twilight_Midnight", $hash, 0); } else { myInternalTimer ("Midnight", $midnight, "Twilight_WeatherTimerUpdate", $hash, 0); - } - + } + } ################################################################################ sub Twilight_StandardTimerSet($) { @@ -484,13 +485,13 @@ sub Twilight_sunposTimerSet($) { sub Twilight_getWeatherHorizon(@) { my ($hash, $result) = @_; - + my $location=$hash->{WEATHER}; if ($location == 0) { $hash->{WEATHER_HORIZON}="0"; - $hash->{CONDITION}="0"; + $hash->{CONDITION}="0"; return 1; - } + } my $mod = "[".$hash->{NAME} ."] "; my @faktor_cond_code = (10,10,10,10, 9, 7, 7, 7, 7, 7, @@ -504,39 +505,39 @@ sub Twilight_getWeatherHorizon(@) my ($cond_code, $cond_txt, $temperatur, $aktTemp); if (defined($result)) { - + # ersetze in result(json) ": durch "=> # dadurch entsteht ein Perlausdruck, der direkt geparst werden kann - + my $perlAusdruck = $result; #$perlAusdruck = "

could"; $perlAusdruck =~ s/("[\w ]+")(\s*)(:)/$1=>/g; $perlAusdruck =~ s/null/undef/g; $perlAusdruck =~ s/true/1/g; $perlAusdruck =~ s/false/0/g; - $perlAusdruck = 'return ' .$perlAusdruck; - + $perlAusdruck = 'return ' .$perlAusdruck; + my $anonymSub = eval "sub {$perlAusdruck}"; Log3 $hash, 3, "[$hash->{NAME}] error $@ parsing $result" if($@); if (!$@) { - my $resHash = $anonymSub->() if ($anonymSub gt ""); + my $resHash = $anonymSub->() if ($anonymSub gt ""); Log3 $hash, 3, "[$hash->{NAME}] error $@ parsing $result" if($@); #Log3 $hash, 3, "jsonAsPerl". Dumper $resHash->{query}{results}{channel}{item}{condition}; if (!$@) { - + $cond_code = $resHash->{query}{results}{channel}{item}{condition}{code}; $cond_txt = $resHash->{query}{results}{channel}{item}{condition}{text}; $temperatur = $resHash->{query}{results}{channel}{item}{condition}{temp}; - } + } } } - + # wenn kein Code ermittelt werden kann, wird ein Pseudocode gesetzt if (!defined($cond_code) ) { $cond_code = "50"; # eigener neutraler Code $cond_txt = "undefined"; $temperatur = "undefined"; - } else { + } else { $hash->{WEATHER_CORRECTION} = $faktor_cond_code[$cond_code] / 25 * 20; $hash->{WEATHER_HORIZON} = $hash->{WEATHER_CORRECTION} + $hash->{INDOOR_HORIZON}; $hash->{CONDITION} = $cond_code; @@ -544,15 +545,15 @@ sub Twilight_getWeatherHorizon(@) $hash->{TEMPERATUR} = $temperatur; Log3 $hash, 4, "[$hash->{NAME}] $cond_code=$cond_txt $temperatur, correction: $hash->{WEATHER_CORRECTION}°"; } - - my $doy = strftime("%j",localtime); + + my $doy = strftime("%j",localtime); my $declination = 0.4095*sin(0.016906*($doy-80.086)); if($hash->{WEATHER_HORIZON} > (89-$hash->{LATITUDE}+$declination) ){ $hash->{WEATHER_HORIZON} = 89-$hash->{LATITUDE}+$declination; } - + return 1; - + } ################################################################################ sub Twilight_sunpos($) @@ -640,7 +641,7 @@ sub Twilight_sunpos($) my $twilight = int(($dElevation+12.0)/18.0 * 1000)/10; $twilight = 100 if ($twilight>100); $twilight = 0 if ($twilight< 0); - + my $twilight_weather ; if( (my $ExtWeather = AttrVal($hashName, "useExtWeather", "")) eq "") { @@ -648,17 +649,17 @@ sub Twilight_sunpos($) Log3 $hash, 5, "[$hash->{NAME}] " . "Original weather readings"; } else { my($extDev,$extReading) = split(":",$ExtWeather); - my $extWeatherHorizont = ReadingsVal($extDev,$extReading,-1); + my $extWeatherHorizont = ReadingsVal($extDev,$extReading,-1); if ($extWeatherHorizont >= 0){ $extWeatherHorizont = 100 if ($extWeatherHorizont > 100); - Log3 $hash, 5, "[$hash->{NAME}] " . "New weather readings from: ".$extDev.":".$extReading.":".$extWeatherHorizont; + Log3 $hash, 5, "[$hash->{NAME}] " . "New weather readings from: ".$extDev.":".$extReading.":".$extWeatherHorizont; $twilight_weather = $twilight - int(0.007 * ($extWeatherHorizont ** 2)); ## SCM: 100% clouds => 30% light (rough estimation) } else { $twilight_weather = int(($dElevation-$hash->{WEATHER_HORIZON}+12.0)/18.0 * 1000)/10; - Log3 $hash, 3, "[$hash->{NAME}] " . "Error with external readings from: ".$extDev.":".$extReading." , taking original weather readings"; + Log3 $hash, 3, "[$hash->{NAME}] " . "Error with external readings from: ".$extDev.":".$extReading." , taking original weather readings"; } - } - + } + $twilight_weather = 100 if ($twilight_weather>100); $twilight_weather = 0 if ($twilight_weather< 0); @@ -759,7 +760,7 @@ sub twilight($$$$) {

indoor_horizon
- The parameter indoor_horizon gives a virtual horizon, that shall be used for calculation of indoor twilight. Minimal value -6 means indoor values are the same like civil values. + The parameter indoor_horizon gives a virtual horizon, that shall be used for calculation of indoor twilight. Minimal value -6 means indoor values are the same like civil values. indoor_horizon 0 means indoor values are the same as real values. indoor_horizon > 0 means earlier indoor sunset resp. later indoor sunrise.

Weather_Position @@ -899,8 +900,8 @@ Example:

indoor_horizon
- Der Parameter indoor_horizon bestimmt einen virtuellen Horizont, der für die Berechnung der Dämmerung innerhalb von Rämen genutzt werden kann. Minimalwert ist -6 (ergibt gleichen Wert wie Zivile Dämmerung). Bei 0 fallen - indoor- und realer Dämmerungswert zusammen. Werte grösser 0 ergeben frühere Werte für den Abend bzw. spätere für den Morgen. + Der Parameter indoor_horizon bestimmt einen virtuellen Horizont, der für die Berechnung der Dämmerung innerhalb von Rämen genutzt werden kann. Minimalwert ist -6 (ergibt gleichen Wert wie Zivile Dämmerung). Bei 0 fallen + indoor- und realer Dämmerungswert zusammen. Werte grösser 0 ergeben frühere Werte für den Abend bzw. spätere für den Morgen.

Weather_Position
@@ -925,7 +926,7 @@ Example: Azimut, Elevation, Twilight (Seitenwinkel, Höhenwinkel, Dämmerung)
Das Modul berechnet zusätzlich Azimuth und Elevation der Sonne. Diese Werte können zur Rolladensteuerung verwendet werden.

- + Das Reading Twilight wird als neuer "(twi)light" Wert hinzugefügt. Er wird aus der Elevation der Sonne mit folgender Formel abgeleitet: (Elevation+12)/18 * 100). Das erlaubt eine detailliertere Kontrolle der Lampen während Sonnenauf - und untergang. Dieser Wert ist zwischen 0% und 100% wenn die Elevation zwischen -12° und 6°

@@ -990,7 +991,7 @@ Wissenswert dazu ist, dass die Sonne, abhägnig vom Breitengrad, bestimmte E
  • useExtWeather <device>:<reading>
  • Nutzt Daten von einem anderen Device um twilight_weather zu berechnen.
    Das Reading sollte sich im Intervall zwischen 0 und 100 bewegen, z.B. das Reading c_clouds in einemopenweathermap device, bei dem 0 heiteren und 100 bedeckten Himmel bedeuten. - Wird diese Attribut genutzt , werden Wettereffekte wie Starkregen oder Gewitter fuer die Berechnung von twilight_weather nicht mehr herangezogen. + Wird diese Attribut genutzt , werden Wettereffekte wie Starkregen oder Gewitter fuer die Berechnung von twilight_weather nicht mehr herangezogen.
    diff --git a/fhem/FHEM/98_Heating_Control.pm b/fhem/FHEM/98_Heating_Control.pm index d850f970a..27a566d1f 100644 --- a/fhem/FHEM/98_Heating_Control.pm +++ b/fhem/FHEM/98_Heating_Control.pm @@ -3,6 +3,7 @@ # # 98_Heating_Control.pm # written by Dietmar Ortmann +# Maintained by igami since 02-2018 # # This file is part of fhem. # @@ -36,7 +37,7 @@ sub Heating_Control_Initialize($) # Consumer $hash->{SetFn} = "Heating_Control_Set"; - $hash->{AttrFn} = "Heating_Control_Attr"; + $hash->{AttrFn} = "Heating_Control_Attr"; $hash->{DefFn} = "Heating_Control_Define"; $hash->{UndefFn} = "Heating_Control_Undef"; $hash->{GetFn} = "Heating_Control_Get"; @@ -47,21 +48,21 @@ sub Heating_Control_Initialize($) ################################################################################ sub Heating_Control_Set($@) { my ($hash, @a) = @_; - + return "no set value specified" if(int(@a) < 2); return "Unknown argument $a[1], choose one of enable disable " if($a[1] eq "?"); - + my $name = shift @a; my $v = join(" ", @a); Log3 $hash, 3, "[$name] set $name $v"; - + if ($v eq "enable") { - fhem("attr $name disable 0"); + fhem("attr $name disable 0"); } elsif ($v eq "disable") { - fhem("attr $name disable 1"); + fhem("attr $name disable 1"); } - return undef; + return undef; } ######################################################################## sub Heating_Control_Get($@) { @@ -92,8 +93,8 @@ sub Heating_Control_SetTimerOfDay($) { ######################################################################## sub Heating_Control_Attr($$$$) { my ($cmd, $name, $attrName, $attrVal) = @_; - - WeekdayTimer_Attr($cmd, $name, $attrName, $attrVal); + + WeekdayTimer_Attr($cmd, $name, $attrName, $attrVal); return undef; } ######################################################################## @@ -101,15 +102,15 @@ sub Heating_Control_SetTimer($) { my ($hash) = @_; WeekdayTimer_DeleteTimer($hash); WeekdayTimer_SetTimer($hash); -} +} ######################################################################## sub Heating_Control_SetTemp($) { my ($name) = @_; - + my $hash = $modules{Heating_Control}{defptr}{$name}; if(defined $hash) { Heating_Control_SetTimer($hash); - } + } } ######################################################################## sub Heating_Control_SetAllTemps() { # {Heating_Control_SetAllTemps()} @@ -170,12 +171,12 @@ sub Heating_Control_SetAllTemps() { # {Heating_Control_SetAllTemps()} - + Get
    @@ -522,33 +523,33 @@ sub Heating_Control_SetAllTemps() { # {Heating_Control_SetAllTemps()} Attributes =end html -=cut \ No newline at end of file +=cut diff --git a/fhem/FHEM/98_WeekdayTimer.pm b/fhem/FHEM/98_WeekdayTimer.pm index 79ac41ca4..9812ad4a3 100644 --- a/fhem/FHEM/98_WeekdayTimer.pm +++ b/fhem/FHEM/98_WeekdayTimer.pm @@ -4,6 +4,7 @@ # 98_WeekdayTimer.pm # written by Dietmar Ortmann # modified by Tobias Faust +# Maintained by igami since 02-2018 # # This file is part of fhem. # @@ -45,7 +46,7 @@ sub WeekdayTimer_Initialize($){ $hash->{DefFn} = "WeekdayTimer_Define"; $hash->{UndefFn} = "WeekdayTimer_Undef"; $hash->{GetFn} = "WeekdayTimer_Get"; - $hash->{AttrFn} = "WeekdayTimer_Attr"; + $hash->{AttrFn} = "WeekdayTimer_Attr"; $hash->{UpdFn} = "WeekdayTimer_Update"; $hash->{AttrList}= "disable:0,1 delayedExecutionCond switchInThePast:0,1 commandTemplate ". $readingFnAttributes; @@ -53,30 +54,30 @@ sub WeekdayTimer_Initialize($){ ################################################################################ sub WeekdayTimer_InitHelper($) { my ($hash) = @_; - + $hash->{longDays} = { "de" => ["Sonntag", "Montag","Dienstag","Mittwoch", "Donnerstag","Freitag", "Samstag", "Wochenende", "Werktags" ], "en" => ["Sunday", "Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "weekend", "weekdays" ], "fr" => ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi","Samedi", "weekend", "jours de la semaine"]}; - $hash->{shortDays} = { "de" => ["so", "mo", "di", "mi", "do", "fr", "sa", '$we', '!$we' ], + $hash->{shortDays} = { "de" => ["so", "mo", "di", "mi", "do", "fr", "sa", '$we', '!$we' ], "en" => ["su", "mo", "tu", "we", "th", "fr", "sa", '$we', '!$we' ], "fr" => ["di", "lu", "ma", "me", "je", "ve", "sa", '$we', '!$we' ]}; } ################################################################################ sub WeekdayTimer_Set($@) { my ($hash, @a) = @_; - + return "no set value specified" if(int(@a) < 2); return "Unknown argument $a[1], choose one of enable disable " if($a[1] eq "?"); - + my $name = shift @a; my $v = join(" ", @a); Log3 $hash, 3, "[$name] set $name $v"; - + if ($v eq "enable") { - fhem("attr $name disable 0"); + fhem("attr $name disable 0"); } elsif ($v eq "disable") { - fhem("attr $name disable 1"); + fhem("attr $name disable 1"); } return undef; } @@ -107,7 +108,7 @@ sub WeekdayTimer_Undef($$) { myRemoveInternalTimer("SetTimerOfDay", $hash); delete $modules{$hash->{TYPE}}{defptr}{$hash->{NAME}}; return undef; -} +} ################################################################################ sub WeekdayTimer_Define($$) { my ($hash, $def) = @_; @@ -124,32 +125,32 @@ sub WeekdayTimer_Define($$) { my $name = shift @a; my $type = shift @a; my $device = shift @a; - + WeekdayTimer_DeleteTimer($hash); - my $delVariables = "(CONDITION|COMMAND|profile|Profil)"; + my $delVariables = "(CONDITION|COMMAND|profile|Profil)"; map { delete $hash->{$_} if($_=~ m/^$delVariables.*/g) } keys %{$hash}; - + my $language = WeekdayTimer_Language ($hash, \@a); - - my $idx = 0; - $hash->{dayNumber} = {map {$_ => $idx++} @{$hash->{shortDays}{$language}}}; + + my $idx = 0; + $hash->{dayNumber} = {map {$_ => $idx++} @{$hash->{shortDays}{$language}}}; $hash->{helper}{daysRegExp} = '(' . join ("|", @{$hash->{shortDays}{$language}}) . ")"; $hash->{helper}{daysRegExpMessage} = $hash->{helper}{daysRegExp}; - - $hash->{helper}{daysRegExp} =~ s/\$/\\\$/g; - $hash->{helper}{daysRegExp} =~ s/\!/\\\!/g; + + $hash->{helper}{daysRegExp} =~ s/\$/\\\$/g; + $hash->{helper}{daysRegExp} =~ s/\!/\\\!/g; WeekdayTimer_GlobalDaylistSpec ($hash, \@a); $hash->{NAME} = $name; $hash->{DEVICE} = $device; - + my @switchingtimes = WeekdayTimer_gatherSwitchingTimes ($hash, \@a); my $conditionOrCommand = join (" ", @a); # test if device is defined Log3 ($hash, 3, "[$name] device <$device> in fhem not defined, but accepted") if(!$defs{$device}); - + # wenn keine switchintime angegeben ist, dann Fehler Log3 ($hash, 3, "[$name] no valid Switchingtime found in <$conditionOrCommand>, check first parameter") if (@switchingtimes == 0); @@ -159,66 +160,66 @@ sub WeekdayTimer_Define($$) { $defs{$device}{STILLDONETIME} = 0 if($defs{$device}); $modules{$hash->{TYPE}}{defptr}{$hash->{NAME}} = $hash; - + $hash->{CONDITION} = ""; $hash->{COMMAND} = ""; if($conditionOrCommand =~ m/^\(.*\)$/g) { #condition (*) $hash->{CONDITION} = $conditionOrCommand; } elsif(length($conditionOrCommand) > 0 ) { $hash->{COMMAND} = $conditionOrCommand; } - + WeekdayTimer_Profile ($hash); delete $hash->{VERZOEGRUNG}; delete $hash->{VERZOEGRUNG_IDX}; - - $attr{$name}{commandTemplate} = + + $attr{$name}{commandTemplate} = 'set $NAME '. WeekdayTimer_isHeizung($hash) .' $EVENT' if (!defined $attr{$name}{commandTemplate}); - + InternalTimer(time(), "$hash->{TYPE}_SetTimer", $hash, 0); WeekdayTimer_SetTimerForMidnightUpdate( { HASH => $hash} ); - + return undef; } ################################################################################ -sub WeekdayTimer_Profile($) { +sub WeekdayTimer_Profile($) { my $hash = shift; - + my $language = $hash->{LANGUAGE}; - my %longDays = %{$hash->{longDays}}; - + my %longDays = %{$hash->{longDays}}; + delete $hash->{profil}; - my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time()); - - my $now = time(); + my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time()); + + my $now = time(); # ---- Zeitpunkte den Tagen zuordnen ----------------------------------- my $idx = 0; foreach my $st (@{$hash->{SWITCHINGTIMES}}) { my ($tage,$time,$parameter) = WeekdayTimer_SwitchingTime ($hash, $st); - + $idx++; foreach my $d (@{$tage}) { my @listeDerTage = ($d); push (@listeDerTage, WeekdayTimer_getListeDerTage($d, $time)) if ($d>=7); - - map { my $day = $_; + + map { my $day = $_; my $dayOfEchteZeit = $day; - $dayOfEchteZeit = ($wday>=1&&$wday<=5) ? 6 : $wday if ($day==7); # ggf. Samstag $wday ~~ [1..5] + $dayOfEchteZeit = ($wday>=1&&$wday<=5) ? 6 : $wday if ($day==7); # ggf. Samstag $wday ~~ [1..5] $dayOfEchteZeit = ($wday==0||$wday==6) ? 1 : $wday if ($day==8); # ggf. Montag $wday ~~ [0, 6] - my $echtZeit = WeekdayTimer_EchteZeit($hash, $dayOfEchteZeit, $time); + my $echtZeit = WeekdayTimer_EchteZeit($hash, $dayOfEchteZeit, $time); $hash->{profile} {$day}{$echtZeit} = $parameter; $hash->{profile_IDX}{$day}{$echtZeit} = $idx; - } @listeDerTage; + } @listeDerTage; } } # ---- Zeitpunkte des aktuellen Tages mit EPOCH ermitteln -------------- $idx = 0; foreach my $st (@{$hash->{SWITCHINGTIMES}}) { my ($tage,$time,$parameter) = WeekdayTimer_SwitchingTime ($hash, $st); - my $echtZeit = WeekdayTimer_EchteZeit ($hash, $wday, $time); - my ($stunde, $minute, $sekunde) = split (":",$echtZeit); - + my $echtZeit = WeekdayTimer_EchteZeit ($hash, $wday, $time); + my ($stunde, $minute, $sekunde) = split (":",$echtZeit); + $idx++; $hash->{profil}{$idx}{TIME} = $time; $hash->{profil}{$idx}{PARA} = $parameter; @@ -226,41 +227,41 @@ sub WeekdayTimer_Profile($) { $hash->{profil}{$idx}{TAGE} = $tage; } # ---- Texte Readings aufbauen ----------------------------------------- - Log3 $hash, 4, "[$hash->{NAME}] " . sunrise_abs() . " " . sunset_abs() . " " . $longDays{$language}[$wday]; + Log3 $hash, 4, "[$hash->{NAME}] " . sunrise_abs() . " " . sunset_abs() . " " . $longDays{$language}[$wday]; foreach my $d (sort keys %{$hash->{profile}}) { my $profiltext = ""; foreach my $t (sort keys %{$hash->{profile}{$d}}) { - $profiltext .= "$t " . $hash->{profile}{$d}{$t} . ", "; + $profiltext .= "$t " . $hash->{profile}{$d}{$t} . ", "; } my $profilKey = "Profil $d: $longDays{$language}[$d]"; $profiltext =~ s/, $//; $hash->{$profilKey} = $profiltext; - Log3 $hash, 4, "[$hash->{NAME}] $profiltext ($profilKey)"; + Log3 $hash, 4, "[$hash->{NAME}] $profiltext ($profilKey)"; } # für logProxy umhaengen $hash->{helper}{SWITCHINGTIME} = $hash->{profile}; delete $hash->{profile}; } -################################################################################ +################################################################################ sub WeekdayTimer_getListeDerTage($$) { my ($d, $time) = @_; my %hdays=(); @hdays{(0, 6)} = undef if ($d==7); # sa,so ( $we) @hdays{(1..5)} = undef if ($d==8); # mo-fr (!$we) - + my $wday; my $now = time(); my ($sec,$min,$hour,$mday,$mon,$year,$nowWday,$yday,$isdst) = localtime($now); - - my @realativeWdays = (0..6); + + my @realativeWdays = (0..6); for (my $i=0;$i<=6;$i++) { my $relativeDay = $i-$nowWday; #Log 3, "relativeDay------------>$relativeDay"; - my ($stunde, $minute, $sekunde) = split (":",$time); - + my ($stunde, $minute, $sekunde) = split (":",$time); + my $echteZeit = WeekdayTimer_zeitErmitteln ($now, $stunde, $minute, $sekunde, $relativeDay); #Log 3, "echteZeit---$i---->>>$relativeDay<<<----->".FmtDateTime($echteZeit); ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($echteZeit); @@ -273,23 +274,23 @@ sub WeekdayTimer_getListeDerTage($$) { delete $hdays{$i} if ($d==8); # !$we Tag herausnehmen } } - } - + } + #Log 3, "result------------>" . join (" ", sort keys %hdays); return keys %hdays; } -################################################################################ +################################################################################ sub WeekdayTimer_SwitchingTime($$) { my ($hash, $switchingtime) = @_; - + my $name = $hash->{NAME}; my $globalDaylistSpec = $hash->{GlobalDaylistSpec}; - my @tageGlobal = @{WeekdayTimer_daylistAsArray($hash, $globalDaylistSpec)}; - + my @tageGlobal = @{WeekdayTimer_daylistAsArray($hash, $globalDaylistSpec)}; + my (@st, $daylist, $time, $timeString, $para); @st = split(/\|/, $switchingtime); - - if ( @st == 2) { + + if ( @st == 2) { $daylist = ($globalDaylistSpec gt "") ? $globalDaylistSpec : "0123456"; $time = $st[0]; $para = $st[1]; @@ -298,39 +299,39 @@ sub WeekdayTimer_SwitchingTime($$) { $time = $st[1]; $para = $st[2]; } - - my @tage = @{WeekdayTimer_daylistAsArray($hash, $daylist)}; + + my @tage = @{WeekdayTimer_daylistAsArray($hash, $daylist)}; my $tage=@tage; if ( $tage==0 ) { Log3 ($hash, 1, "[$name] invalid daylist in $name <$daylist> use one of 012345678 or $hash->{helper}{daysRegExpMessage}"); - } + } my %hdays=(); @hdays{@tageGlobal} = undef; @hdays{@tage} = undef; @tage = sort keys %hdays; - + #Log3 $hash, 3, "Tage: " . Dumper \@tage; return (\@tage,$time,$para); } -################################################################################ +################################################################################ sub WeekdayTimer_daylistAsArray($$){ my ($hash, $daylist) = @_; - + my $name = $hash->{NAME}; my @days; - + my %hdays=(); - $daylist = lc($daylist); + $daylist = lc($daylist); # Angaben der Tage verarbeiten # Aufzaehlung 1234 ... if ( $daylist =~ m/^[0-8]{0,9}$/g) { - - Log3 ($hash, 3, "[$name] " . '"7" in daylist now means $we(weekend) - see dokumentation!!!' ) + + Log3 ($hash, 3, "[$name] " . '"7" in daylist now means $we(weekend) - see dokumentation!!!' ) if (index($daylist, '7') != -1); - + @days = split("", $daylist); @hdays{@days} = undef; @@ -340,11 +341,11 @@ sub WeekdayTimer_daylistAsArray($$){ my @aufzaehlungen = split (",", $daylist); foreach my $einzelAufzaehlung (@aufzaehlungen) { my @days = split ("-", $einzelAufzaehlung); - my $days = @days; + my $days = @days; if ($days == 1) { #einzelner Tag: Sa - $hdays{$hash->{dayNumber}{$days[0]}} = undef; - } else { + $hdays{$hash->{dayNumber}{$days[0]}} = undef; + } else { # von bis Angabe: Mo-Di my $von = $hash->{dayNumber}{$days[0]}; my $bis = $hash->{dayNumber}{$days[1]}; @@ -354,38 +355,38 @@ sub WeekdayTimer_daylistAsArray($$){ #@subDays = ($dayNumber{so} .. $bis, $von .. $dayNumber{sa}); @subDays = ( 00 .. $bis, $von .. 06); } - @hdays{@subDays}=undef; + @hdays{@subDays}=undef; } } } else{ %hdays = (); } - + my @tage = sort keys %hdays; return \@tage; } -################################################################################ +################################################################################ sub WeekdayTimer_EchteZeit($$$) { - my ($hash, $d, $time) = @_; - + my ($hash, $d, $time) = @_; + my $name = $hash->{NAME}; - + my $now = time(); - my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($now); + my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($now); my $listOfDays = ""; - # Zeitangabe verarbeiten. - $time = '"' . "$time" . '"' if($time !~ m/^\{.*\}$/g); + # Zeitangabe verarbeiten. + $time = '"' . "$time" . '"' if($time !~ m/^\{.*\}$/g); my $date = $now+($d-$wday)*86400; - my $timeString = '{ my $date='."$date;" .$time."}"; + my $timeString = '{ my $date='."$date;" .$time."}"; my $eTimeString = eval( $timeString ); # must deliver HH:MM[:SS] if ($@) { - $@ =~ s/\n/ /g; + $@ =~ s/\n/ /g; Log3 ($hash, 3, "[$name] " . $@ . ">>>$timeString<<<"); $eTimeString = "00:00:00"; } - + if ($eTimeString =~ m/^[0-2][0-9]:[0-5][0-9]$/g) { # HH:MM $eTimeString .= ":00"; # HH:MM:SS erzeugen } elsif ($eTimeString =~ m/^[0-2][0-9](:[0-5][0-9]){2,2}$/g) { # HH:MM:SS @@ -415,7 +416,7 @@ sub WeekdayTimer_gatherSwitchingTimes { my $name = $hash->{NAME}; my @switchingtimes = (); my $conditionOrCommand; - + # switchingtime einsammeln while (@$a > 0) { @@ -423,16 +424,16 @@ sub WeekdayTimer_gatherSwitchingTimes { my $element = ""; my @restoreElements = (); E: while (@$a > 0) { - + my $actualElement = shift @$a; - push @restoreElements, $actualElement; + push @restoreElements, $actualElement; $element = $element . $actualElement . " "; Log3 $hash, 5, "[$name] $element - trying to accept as a switchtime"; - + # prüfen ob Anführungszeichen paarig sind - my @quotes = ('"', "'" ); + my @quotes = ('"', "'" ); foreach my $quote (@quotes){ - my $balancedSign = eval "((\$element =~ tr/$quote//))"; + my $balancedSign = eval "((\$element =~ tr/$quote//))"; if ($balancedSign % 2) { # ungerade Anzahl quotes, dann verlängern Log3 $hash, 5, "[$name] $element - unbalanced quotes: $balancedSign $quote found"; next E; @@ -440,10 +441,10 @@ E: while (@$a > 0) { } # prüfen ob öffnende/schliessende Klammern paarig sind - my %signs = ('('=>')', '{'=>'}'); + my %signs = ('('=>')', '{'=>'}'); foreach my $signOpened (keys(%signs)) { my $signClosed = $signs{$signOpened}; - my $balancedSign = eval "((\$element =~ tr/$signOpened//) - (\$element =~ tr/$signClosed//))"; + my $balancedSign = eval "((\$element =~ tr/$signOpened//) - (\$element =~ tr/$signClosed//))"; if ($balancedSign) { # öffnende/schließende Klammern nicht gleich, dann verlängern Log3 $hash, 5, "[$name] $element - unbalanced brackets $signOpened$signClosed:$balancedSign"; next E; @@ -451,18 +452,18 @@ E: while (@$a > 0) { } last; } - + # ein space am Ende wieder abschneiden $element = substr ($element, 0, length($element)-1); my @t = split(/\|/, $element); my $anzahl = @t; - + if ( ($anzahl == 2 || $anzahl == 3) && $t[0] gt "" && $t[1] gt "" ) { Log3 $hash, 4, "[$name] $element - accepted"; push(@switchingtimes, $element); } else { Log3 $hash, 4, "[$name] $element - NOT accepted, must be command or condition"; - unshift @$a, @restoreElements; + unshift @$a, @restoreElements; last; } } @@ -471,8 +472,8 @@ E: while (@$a > 0) { ################################################################################ sub WeekdayTimer_Language { my ($hash, $a) = @_; - - my $name = $hash->{NAME}; + + my $name = $hash->{NAME}; # ggf. language optional Parameter my $langRegExp = "(" . join ("|", keys(%{$hash->{shortDays}})) . ")"; @@ -481,7 +482,7 @@ sub WeekdayTimer_Language { if ($language =~ m/^$langRegExp$/g) { } else { Log3 ($hash, 3, "[$name] language: $language not recognized, use one of $langRegExp") if (length($language) == 2); - unshift @$a, $language; + unshift @$a, $language; $language = "de"; } $hash->{LANGUAGE} = $language; @@ -492,18 +493,18 @@ sub WeekdayTimer_Language { ################################################################################ sub WeekdayTimer_GlobalDaylistSpec { my ($hash, $a) = @_; - + my $daylist = shift @$a; - my @tage = @{WeekdayTimer_daylistAsArray($hash, $daylist)}; - my $tage = @tage; + my @tage = @{WeekdayTimer_daylistAsArray($hash, $daylist)}; + my $tage = @tage; if ($tage > 0) { ; } else { - unshift (@$a,$daylist); + unshift (@$a,$daylist); $daylist = ""; } - + $hash->{GlobalDaylistSpec} = $daylist; } ################################################################################ @@ -514,7 +515,7 @@ sub WeekdayTimer_SetTimerForMidnightUpdate($) { my $now = time(); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now); - + my $midnightPlus5Seconds = WeekdayTimer_zeitErmitteln ($now, 0, 0, 5, 1); #Log3 $hash, 3, "midnightPlus5Seconds------------>".FmtDateTime($midnightPlus5Seconds); @@ -528,120 +529,120 @@ sub WeekdayTimer_SetTimerOfDay($) { my ($myHash) = @_; my $hash = myGetHashIndirekt($myHash, (caller(0))[3]); return if (!defined($hash)); - + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time()); - my $secSinceMidnight = 3600*$hour + 60*$min + $sec; - + my $secSinceMidnight = 3600*$hour + 60*$min + $sec; + $hash->{SETTIMERATMIDNIGHT} = $myHash->{SETTIMERATMIDNIGHT}; WeekdayTimer_DeleteTimer($hash); WeekdayTimer_Profile ($hash); WeekdayTimer_SetTimer ($hash); delete $hash->{SETTIMERATMIDNIGHT}; - + WeekdayTimer_SetTimerForMidnightUpdate( { HASH => $hash} ); } ################################################################################ sub WeekdayTimer_SetTimer($) { - my $hash = shift; + my $hash = shift; my $name = $hash->{NAME}; - - my $now = time(); - + + my $now = time(); + my $isHeating = WeekdayTimer_isHeizung($hash); my $swip = AttrVal($name, "switchInThePast", 0); my $switchInThePast = ($swip || $isHeating); - + Log3 $hash, 4, "[$name] Heating recognized - switch in the past activated" if ($isHeating); Log3 $hash, 4, "[$name] no switch in the yesterdays because of the devices type($hash->{DEVICE} is not recognized as heating) - use attr switchInThePast" if (!$switchInThePast && !defined $hash->{SETTIMERATMIDNIGHT}); - + my @switches = sort keys %{$hash->{profil}}; if ($#switches < 0) { Log3 $hash, 3, "[$name] no switches to send, due to possible errors."; - return; - } - + return; + } + readingsSingleUpdate ($hash, "state", "inactive", 1) if (!defined $hash->{SETTIMERATMIDNIGHT}); for(my $i=0; $i<=$#switches; $i++) { - + my $idx = $switches[$i]; - + my $time = $hash->{profil}{$idx}{TIME}; my $timToSwitch = $hash->{profil}{$idx}{EPOCH}; my $tage = $hash->{profil}{$idx}{TAGE}; my $para = $hash->{profil}{$idx}{PARA}; - + my $secondsToSwitch = $timToSwitch - $now; - + my $isActiveTimer = WeekdayTimer_isAnActiveTimer ($hash, $tage, $para); - readingsSingleUpdate ($hash, "state", "active", 1) + readingsSingleUpdate ($hash, "state", "active", 1) if (!defined $hash->{SETTIMERATMIDNIGHT} && $isActiveTimer); - + if ($secondsToSwitch>-5 || defined $hash->{SETTIMERATMIDNIGHT} ) { - if($isActiveTimer) { + if($isActiveTimer) { Log3 $hash, 4, "[$name] setTimer - timer seems to be active today: ".join("",@$tage)."|$time|$para"; - } else { + } else { Log3 $hash, 4, "[$name] setTimer - timer seems to be NOT active today: ".join("",@$tage)."|$time|$para ". $hash->{CONDITION}; } - myRemoveInternalTimer("$idx", $hash); + myRemoveInternalTimer("$idx", $hash); myInternalTimer ("$idx", $timToSwitch, "$hash->{TYPE}_Update", $hash, 0); } } - + if (defined $hash->{SETTIMERATMIDNIGHT}) { return; } - - my ($aktIdx,$aktTime,$aktParameter,$nextTime,$nextParameter) = + + my ($aktIdx,$aktTime,$aktParameter,$nextTime,$nextParameter) = WeekdayTimer_searchAktNext($hash, time()+5); - if(!defined $aktTime) { - Log3 $hash, 3, "[$name] can not compute past switching time"; - } - + if(!defined $aktTime) { + Log3 $hash, 3, "[$name] can not compute past switching time"; + } + readingsSingleUpdate ($hash, "nextUpdate", FmtDateTime($nextTime), 1); - readingsSingleUpdate ($hash, "nextValue", $nextParameter, 1); - readingsSingleUpdate ($hash, "currValue", $aktParameter, 1); # HB - - if ($switchInThePast && defined $aktTime) { + readingsSingleUpdate ($hash, "nextValue", $nextParameter, 1); + readingsSingleUpdate ($hash, "currValue", $aktParameter, 1); # HB + + if ($switchInThePast && defined $aktTime) { # Fensterkontakte abfragen - wenn einer im Status closed, dann Schaltung um 60 Sekunden verzögern if (WeekdayTimer_FensterOffen($hash, $aktParameter, $aktIdx)) { return; } - - # alle in der Vergangenheit liegenden Schaltungen sammeln und - # nach 5 Sekunden in der Reihenfolge der Schaltzeiten + + # alle in der Vergangenheit liegenden Schaltungen sammeln und + # nach 5 Sekunden in der Reihenfolge der Schaltzeiten # durch WeekdayTimer_delayedTimerInPast() als Timer einstellen - # die Parameter merken wir uns kurzzeitig im hash + # die Parameter merken wir uns kurzzeitig im hash # modules{WeekdayTimer}{timerInThePast} my $device = $hash->{DEVICE}; - Log3 $hash, 4, "[$name] past timer on $hash->{DEVICE} at ". FmtDateTime($aktTime). " with $aktParameter activated"; - + Log3 $hash, 4, "[$name] past timer on $hash->{DEVICE} at ". FmtDateTime($aktTime). " with $aktParameter activated"; + my $parameter = $modules{WeekdayTimer}{timerInThePast}{$device}{$aktTime}; $parameter = [] if (!defined $parameter); push (@$parameter,["$aktIdx", $aktTime, "$hash->{TYPE}_Update", $hash, 0]); $modules{WeekdayTimer}{timerInThePast}{$device}{$aktTime} = $parameter; - + my $tipHash = $modules{WeekdayTimer}{timerInThePastHash}; $tipHash = $hash if (!defined $tipHash); $modules{WeekdayTimer}{timerInThePastHash} = $tipHash; - + myRemoveInternalTimer("delayed", $tipHash); myInternalTimer ("delayed", time()+5, "WeekdayTimer_delayedTimerInPast", $tipHash, 0); - - } + + } } ################################################################################ sub WeekdayTimer_delayedTimerInPast($) { my ($myHash) = @_; my $hash = myGetHashIndirekt($myHash, (caller(0))[3]); return if (!defined($hash)); - + my $tim = time(); my $tipIpHash = $modules{WeekdayTimer}{timerInThePast}; foreach my $device ( keys %$tipIpHash ) { foreach my $time ( sort keys %{$tipIpHash->{$device}} ) { Log3 $hash, 4, "$device ".FmtDateTime($time)." ".($tim-$time)."s "; - + foreach my $para ( @{$tipIpHash->{$device}{$time}} ) { myRemoveInternalTimer(@$para[0], @$para[3]); my $mHash =myInternalTimer (@$para[0],@$para[1],@$para[2],@$para[3],@$para[4]); @@ -651,55 +652,55 @@ sub WeekdayTimer_delayedTimerInPast($) { } delete $modules{WeekdayTimer}{timerInThePast}; delete $modules{WeekdayTimer}{timerInThePastHash} -} +} ################################################################################ sub WeekdayTimer_searchAktNext($$) { my ($hash, $now) = @_; my $name = $hash->{NAME}; - + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now); #Log3 $hash, 3, "[$name] such--->".FmtDateTime($now); - + my ($oldTag, $oldTime, $oldPara , $oldIdx); my ($nextTag, $nextTime, $nextPara, $nextIdx); - + my $language = $hash->{LANGUAGE}; my %shortDays = %{$hash->{shortDays}}; - - my @realativeWdays = ($wday..6,0..$wday-1,$wday..6,0..6); + + my @realativeWdays = ($wday..6,0..$wday-1,$wday..6,0..6); for (my $i=0;$i<=$#realativeWdays;$i++) { - + my $relativeDay = $i-7; my $relWday = $realativeWdays[$i]; - + foreach my $time (sort keys %{$hash->{helper}{SWITCHINGTIME}{$relWday}}) { - my ($stunde, $minute, $sekunde) = split (":",$time); - + my ($stunde, $minute, $sekunde) = split (":",$time); + $oldTime = $nextTime; - $oldPara = $nextPara; + $oldPara = $nextPara; $oldIdx = $nextIdx; $oldTag = $nextTag; - + $nextTime = WeekdayTimer_zeitErmitteln ($now, $stunde, $minute, $sekunde, $relativeDay); $nextPara = $hash->{helper}{SWITCHINGTIME}{$relWday}{$time}; $nextIdx = $hash->{profile_IDX}{$relWday}{$time}; $nextTag = $relWday; - + #Log3 $hash, 3, $shortDays{$language}[$nextTag]." ".FmtDateTime($nextTime)." ".$nextPara." ".$nextIdx; - + if ($nextTime >= $now) { #Log3 $hash, 3, "oldIdx------------->$oldIdx"; #Log3 $hash, 3, "oldTime------------>".FmtDateTime($oldTime); #Log3 $hash, 3, "oldPara------------>$oldPara"; return ($oldIdx, $oldTime, $oldPara, $nextTime, $nextPara); } - } - } - return (undef,undef,undef,undef); -} + } + } + return (undef,undef,undef,undef); +} ################################################################################ sub WeekdayTimer_DeleteTimer($) { - my $hash = shift; + my $hash = shift; map {myRemoveInternalTimer ($_, $hash)} keys %{$hash->{profil}}; } ################################################################################ @@ -707,13 +708,13 @@ sub WeekdayTimer_Update($) { my ($myHash) = @_; my $hash = myGetHashIndirekt($myHash, (caller(0))[3]); return if (!defined($hash)); - + my $name = $hash->{NAME}; my $idx = $myHash->{MODIFIER}; my $now = time(); - + #my $sollZeit = $myHash->{TIME}; - + #my $setModifier = WeekdayTimer_isHeizung($hash); #my $isHeating = $setModifier gt ""; @@ -722,22 +723,22 @@ sub WeekdayTimer_Update($) { my $time = $hash->{profil}{$idx}{TIME}; #my $newParam = WeekdayTimer_evalAndcleanupParam($hash,$time,$hash->{profil}{$idx}{PARA}, $isHeating ); my $newParam = $hash->{profil}{$idx}{PARA}; - + #Log3 $hash, 3, "[$name] $idx ". $time . " " . $newParam . " " . join("",@$tage); - + # Fenserkontakte abfragen - wenn einer im Status closed, dann Schaltung um 60 Sekunden verzögern if (WeekdayTimer_FensterOffen($hash, $newParam, $idx)) { readingsSingleUpdate ($hash, "state", "open window", 1); return; } - + my $dieGanzeWoche = [7,8]; my ($activeTimer, $activeTimerState); if (defined $myHash->{immerSchalten}) { $activeTimer = WeekdayTimer_isAnActiveTimer ($hash, $dieGanzeWoche, $newParam); $activeTimerState = WeekdayTimer_isAnActiveTimer ($hash, $tage, $newParam); Log3 $hash, 4, "[$name] Update - past timer activated"; - } else { + } else { $activeTimer = WeekdayTimer_isAnActiveTimer ($hash, $tage, $newParam); $activeTimerState = $activeTimer; Log3 $hash, 4, "[$name] Update - timer seems to be active today: ".join("",@$tage)."|$time|$newParam" if($activeTimer); @@ -746,13 +747,13 @@ sub WeekdayTimer_Update($) { #Log3 $hash, 3, "activeTimerState------->$activeTimerState"; my ($aktIdx, $aktTime, $aktParameter, $nextTime, $nextParameter) = WeekdayTimer_searchAktNext($hash, time()+5); - - my $device = $hash->{DEVICE}; + + my $device = $hash->{DEVICE}; my $disabled = AttrVal($hash->{NAME}, "disable", 0); - + # ggf. Device schalten WeekdayTimer_Device_Schalten($hash, $newParam, $tage) if($activeTimer); - + readingsBeginUpdate($hash); readingsBulkUpdate ($hash, "nextUpdate", FmtDateTime($nextTime)); readingsBulkUpdate ($hash, "nextValue", $nextParameter); @@ -760,22 +761,22 @@ sub WeekdayTimer_Update($) { readingsBulkUpdate ($hash, "state", $newParam ) if($activeTimerState); readingsEndUpdate ($hash, defined($hash->{LOCAL} ? 0 : 1)); - return 1; - + return 1; + } ################################################################################ sub WeekdayTimer_isAnActiveTimer ($$$) { my ($hash, $tage, $newParam) = @_; - + my $name = $hash->{NAME}; my %specials = ( "%NAME" => $hash->{DEVICE}, "%EVENT" => $newParam); - + my $condition = WeekdayTimer_Condition ($hash, $tage); my $tageAsHash = WeekdayTimer_tageAsHash($hash, $tage); my $xPression = "{".$tageAsHash.";;".$condition ."}"; - $xPression = EvalSpecials($xPression, %specials); + $xPression = EvalSpecials($xPression, %specials); Log3 $hash, 5, "[$name] condition: $xPression"; - + my $ret = AnalyzeCommandChain(undef, $xPression); #Log3 $hash, 3, "[$name] condition:>>>$ret<<< $xPression"; Log3 $hash, 5, "[$name] result of condition:$ret"; @@ -787,32 +788,32 @@ sub WeekdayTimer_isHeizung($) { my ($hash) = @_; my $name = $hash->{NAME}; - - my $dHash = $defs{$hash->{DEVICE}}; + + my $dHash = $defs{$hash->{DEVICE}}; return "" if (!defined $dHash); # vorzeitiges Ende wenn das device nicht existiert my $dType = $dHash->{TYPE}; - return "" if (!defined($dType) || $dType eq "dummy" ); - + return "" if (!defined($dType) || $dType eq "dummy" ); + my $dName = $dHash->{NAME}; - - my @tempSet = ("desired-temp", "desiredTemperature", "desired", "thermostatSetpointSet"); + + my @tempSet = ("desired-temp", "desiredTemperature", "desired", "thermostatSetpointSet"); my $allSets = getAllSets($dName); - + foreach my $ts (@tempSet) { if ($allSets =~ m/$ts/) { - Log3 $hash, 4, "[$name] device type heating recognized, setModifier:$ts"; - return $ts + Log3 $hash, 4, "[$name] device type heating recognized, setModifier:$ts"; + return $ts } - } - + } + } ################################################################################ # sub WeekdayTimer_FensterOffen ($$$) { my ($hash, $event, $time) = @_; my $name = $hash->{NAME}; - + my %specials = ( '%HEATING_CONTROL' => $hash->{NAME}, '%WEEKDAYTIMER' => $hash->{NAME}, @@ -825,26 +826,26 @@ sub WeekdayTimer_FensterOffen ($$$) { '$EVENT' => $event, '$TIME' => $hash->{profil}{$time}{TIME}, ); - + my $verzoegerteAusfuehrungCond = AttrVal($hash->{NAME}, "delayedExecutionCond", "0"); #$verzoegerteAusfuehrungCond = 'xxx(%WEEKDAYTIMER,%NAME,%HEATING_CONTROL,$WEEKDAYTIMER,$EVENT,$NAME,$HEATING_CONTROL)'; - my $nextRetry = time()+55+int(rand(10)); + my $nextRetry = time()+55+int(rand(10)); my $epoch = $hash->{profil}{$time}{EPOCH}; my $delay = int(time()) - $epoch; my $nextDelay = int($delay/60.+1.5)*60; # round to multiple of 60sec $nextRetry = $epoch + $nextDelay; Log3 $hash, 4, "[$name] time=".$hash->{profil}{$time}{TIME}."/$epoch delay=$delay, nextDelay=$nextDelay, nextRetry=$nextRetry"; - + map { my $key = $_; $key =~ s/\$/\\\$/g; - my $val = $specials{$_}; - $verzoegerteAusfuehrungCond =~ s/$key/$val/g + my $val = $specials{$_}; + $verzoegerteAusfuehrungCond =~ s/$key/$val/g } keys %specials; Log3 $hash, 4, "[$name] delayedExecutionCond:$verzoegerteAusfuehrungCond"; - + my $verzoegerteAusfuehrung = eval($verzoegerteAusfuehrungCond); Log3 $hash, 4, "[$name] result of delayedExecutionCond:$verzoegerteAusfuehrung"; - + if ($verzoegerteAusfuehrung) { if (!defined($hash->{VERZOEGRUNG})) { Log3 $hash, 3, "[$name] switch of $hash->{DEVICE} delayed - delayedExecutionCond: '$verzoegerteAusfuehrungCond' is TRUE"; @@ -859,7 +860,7 @@ sub WeekdayTimer_FensterOffen ($$$) { $hash->{VERZOEGRUNG} = 1; return 1; } - + my %contacts = ( "CUL_FHTTK" => { "READING" => "Window", "STATUS" => "(Open)", "MODEL" => "r" }, "CUL_HM" => { "READING" => "state", "STATUS" => "(open|tilted)", "MODEL" => "r" }, "EnOcean" => { "READING" => "state", "STATUS" => "(open)", "MODEL" => "r" }, @@ -868,11 +869,11 @@ sub WeekdayTimer_FensterOffen ($$$) { "WeekdayTimer" => { "READING" => "delayedExecution","STATUS" => "^1\$", "MODEL" => "a" }, "Heating_Control" => { "READING" => "delayedExecution","STATUS" => "^1\$", "MODEL" => "a" } ); - + my $fensterKontakte = AttrVal($hash->{NAME}, "windowSensor", "")." ".$hash->{NAME}; $fensterKontakte =~ s/^\s+//; $fensterKontakte =~ s/\s+$//; - + Log3 $hash, 4, "[$name] list of window sensors found: '$fensterKontakte'"; if ($fensterKontakte ne "" ) { my @kontakte = split("[ \t]+", $fensterKontakte); @@ -885,18 +886,18 @@ sub WeekdayTimer_FensterOffen ($$$) { if (!defined($contacts{$fk_typ})) { Log3 $hash, 3, "[$name] TYPE '$fk_typ' of $fk not yet supported, $fk ignored - inform maintainer"; } else { - + my $reading = $contacts{$fk_typ}{READING}; my $statusReg = $contacts{$fk_typ}{STATUS}; my $model = $contacts{$fk_typ}{MODEL}; - + my $windowStatus; if ($model eq "r") { ### Reading, sonst Attribut $windowStatus = ReadingsVal($fk,$reading,"nF"); }else{ - $windowStatus = AttrVal ($fk,$reading,"nF"); + $windowStatus = AttrVal ($fk,$reading,"nF"); } - + if ($windowStatus eq "nF") { Log3 $hash, 3, "[$name] Reading/Attribute '$reading' of $fk not found, $fk ignored - inform maintainer" if ($model eq "r"); } else { @@ -931,19 +932,19 @@ sub WeekdayTimer_FensterOffen ($$$) { ################################################################################ sub WeekdayTimer_evalAndcleanupParam($$$$) { my ($hash,$time,$param,$isHeating) = @_; - + my $name = $hash->{DEVICE} ; my $wdName = $hash->{NAME}; - + my $newParam = $param; if ($param =~ m/^{.*}$/) { - + Log3 $hash, 4, "[$wdName] calculating dynamic param before all: ....... $newParam"; - + $newParam =~ s/\$NAME/$hash->{DEVICE}/g; - $newParam =~ s/\$TIME/$time/g; + $newParam =~ s/\$TIME/$time/g; Log3 $hash, 4, "[$wdName] calculating dynamic param after substitutions: $newParam"; - + $newParam = eval $newParam; if ($@ || not defined $newParam) { Log3 $hash, 1, "[$wdName] problem calculating dynamic param: ........... $param"; @@ -951,7 +952,7 @@ sub WeekdayTimer_evalAndcleanupParam($$$$) { } else { Log3 $hash, 4, "[$wdName] calculating dynamic param after eval: ........ $newParam"; } - + }elsif($isHeating && $param =~ m/^\d{1,3}$/){ $newParam = sprintf("%.1f", $param); } @@ -963,20 +964,20 @@ sub WeekdayTimer_Device_Schalten($$$) { my ($command, $condition, $tageAsHash) = ""; my $name = $hash->{NAME}; ### - my $dummy = ""; - + my $dummy = ""; + my $now = time(); #modifier des Zieldevices auswaehlen my $setModifier = WeekdayTimer_isHeizung($hash); - - $attr{$name}{commandTemplate} = + + $attr{$name}{commandTemplate} = 'set $NAME '. $setModifier .' $EVENT' if (!defined $attr{$name}{commandTemplate}); $command = AttrVal($hash->{NAME}, "commandTemplate", "commandTemplate not found"); $command = $hash->{COMMAND} if ($hash->{COMMAND} gt ""); my $activeTimer = 1; - + my $isHeating = $setModifier gt ""; my $aktParam = ReadingsVal($hash->{DEVICE}, $setModifier, ""); $aktParam = sprintf("%.1f", $aktParam) if ($isHeating && $aktParam =~ m/^[0-9]{1,3}$/i); @@ -989,93 +990,93 @@ sub WeekdayTimer_Device_Schalten($$$) { Log3 $hash, 4, "[$name] aktParam:$aktParam newParam:$newParam - is $disabled_txt disabled"; #Kommando ausführen - if ($command && !$disabled && $activeTimer - && $aktParam ne $newParam + if ($command && !$disabled && $activeTimer + && $aktParam ne $newParam ) { $newParam =~ s/\\:/|/g; $newParam =~ s/:/ /g; $newParam =~ s/\|/:/g; - + my %specials = ( "%NAME" => $hash->{DEVICE}, "%EVENT" => $newParam); $command= EvalSpecials($command, %specials); - + Log3 $hash, 4, "[$name] command: '$command' executed with ".join(",", map { "$_=>$specials{$_}" } keys %specials); my $ret = AnalyzeCommandChain(undef, $command); Log3 ($hash, 3, $ret) if($ret); - } + } } ################################################################################ sub WeekdayTimer_tageAsHash($$) { - my ($hash, $tage) = @_; - + my ($hash, $tage) = @_; + my %days = map {$_ => 1} @$tage; - map {delete $days{$_}} (7,8); - + map {delete $days{$_}} (7,8); + return 'my $days={};map{$days->{$_}=1}'.'('.join (",", sort keys %days).')'; } ################################################################################ -sub WeekdayTimer_Condition($$) { +sub WeekdayTimer_Condition($$) { my ($hash, $tage) = @_; - - my $name = $hash->{NAME}; + + my $name = $hash->{NAME}; Log3 $hash, 4, "[$name] condition:$hash->{CONDITION} - Tage:".join(",",@$tage); - + my $condition = "( "; - $condition .= ($hash->{CONDITION} gt "") ? $hash->{CONDITION} : 1 ; + $condition .= ($hash->{CONDITION} gt "") ? $hash->{CONDITION} : 1 ; $condition .= " && " . WeekdayTimer_TageAsCondition($tage); $condition .= ")"; - + return $condition; - + } ################################################################################ sub WeekdayTimer_TageAsCondition ($) { my $tage = shift; - + my %days = map {$_ => 1} @$tage; - + my $we = $days{7}; delete $days{7}; # $we my $notWe = $days{8}; delete $days{8}; #!$we - + my $tageExp = '(defined $days->{$wday}'; - $tageExp .= ' || $we' if defined $we; + $tageExp .= ' || $we' if defined $we; $tageExp .= ' || !$we' if defined $notWe; $tageExp .= ')'; - + return $tageExp; - + } ################################################################################ sub WeekdayTimer_Attr($$$$) { my ($cmd, $name, $attrName, $attrVal) = @_; - $attrVal = 0 if(!defined $attrVal); - + $attrVal = 0 if(!defined $attrVal); + my $hash = $defs{$name}; if( $attrName eq "disable" ) { readingsSingleUpdate ($hash, "disabled", $attrVal, 1); } elsif ( $attrName eq "enable" ) { - WeekdayTimer_SetTimerOfDay({ HASH => $hash}); + WeekdayTimer_SetTimerOfDay({ HASH => $hash}); } elsif ( $attrName eq "switchInThePast" ) { - $attr{$name}{$attrName} = $attrVal; - WeekdayTimer_SetTimerOfDay({ HASH => $hash}); + $attr{$name}{$attrName} = $attrVal; + WeekdayTimer_SetTimerOfDay({ HASH => $hash}); } return undef; } ######################################################################## sub WeekdayTimer_SetParm($) { my ($name) = @_; - + my $hash = $modules{WeekdayTimer}{defptr}{$name}; if(defined $hash) { WeekdayTimer_DeleteTimer($hash); WeekdayTimer_SetTimer($hash); - } + } } ################################################################################ sub WeekdayTimer_SetAllParms() { # {WeekdayTimer_SetAllParms()} - my @wdNamen = sort keys %{$modules{WeekdayTimer}{defptr}}; + my @wdNamen = sort keys %{$modules{WeekdayTimer}{defptr}}; foreach my $wdName ( @wdNamen ) { WeekdayTimer_SetParm($wdName); } @@ -1087,7 +1088,7 @@ sub WeekdayTimer_SetAllParms() { # {WeekdayTimer_SetAllParms()} =pod =item device =item summary sends parameter to devices at defined times -=item summary_DE sendet Parameter an devices zu einer Liste mit festen Zeiten +=item summary_DE sendet Parameter an devices zu einer Liste mit festen Zeiten =begin html @@ -1129,12 +1130,12 @@ sub WeekdayTimer_SetAllParms() { # {WeekdayTimer_SetAllParms()} - + Get
    @@ -1233,33 +1234,33 @@ sub WeekdayTimer_SetAllParms() { # {WeekdayTimer_SetAllParms()} Attributes