diff --git a/fhem/FHEM/59_Twilight.pm b/fhem/FHEM/59_Twilight.pm index a39c14e40..7d2d66a89 100644 --- a/fhem/FHEM/59_Twilight.pm +++ b/fhem/FHEM/59_Twilight.pm @@ -41,7 +41,7 @@ use Math::Trig; use Time::Local qw(timelocal_nocheck); use List::Util qw(max min); use GPUtils qw(GP_Import GP_Export); -eval { use FHEM::Core::Timer::Helper qw(addTimer removeTimer); 1 }; +#eval { use FHEM::Core::Timer::Helper qw( addTimer removeTimer optimizeLOT ); 1 }; use FHEM::Meta; #-- Run before package compilation @@ -71,6 +71,7 @@ BEGIN { AttrVal ReadingsVal ReadingsNum + ReadingsAge InternalVal IsDisabled Log3 @@ -81,6 +82,9 @@ BEGIN { FmtTime FmtDateTime strftime + perlSyntaxCheck + EvalSpecials + AnalyzePerlCommand stacktrace ) ); @@ -105,7 +109,6 @@ sub Initialize { return FHEM::Meta::InitMod( __FILE__, $hash ); } - ################################################################################ sub Twilight_Define { my $hash = shift; @@ -158,6 +161,8 @@ sub Twilight_Define { $attr{$name}{verbose} = 4 if ( $name =~ m/^tst.*$/x ); + #deleteAllRegisteredInternalTimer($hash); + Log3( $hash, 1, "[$hash->{NAME}] Note: Twilight formerly used weather info from yahoo, but source is offline. Using a guessed Weather type device instead if available!" ) if looks_like_number($weather); @@ -174,23 +179,19 @@ sub Twilight_Define { sub Twilight_Undef { my $hash = shift; my $arg = shift // return; + + deleteAllRegisteredInternalTimer($hash); - for my $key ( keys %{ $hash->{TW} } ) { - Twilight_RemoveInternalTimer( $key, $hash ); - } - Twilight_RemoveInternalTimer( "Midnight", $hash ); - Twilight_RemoveInternalTimer( "weather", $hash ); - Twilight_RemoveInternalTimer( "sunpos", $hash ); notifyRegexpChanged( $hash, "" ); - delete $hash->{helper}{extWeather}{regexp}; - delete $hash->{helper}{extWeather}{dispatch}; - delete $hash->{helper}{extWeather}{Device}; - delete $hash->{helper}{extWeather}{Reading}; + for my $key ( keys %{ $hash->{helper}{extWeather} } ) { + delete $hash->{helper}{extWeather}{$key}; + } delete $hash->{helper}{extWeather}; return; } + ################################################################################ sub Twilight_Change_DEF { my $hash = shift // return; @@ -205,8 +206,8 @@ sub Twilight_Change_DEF { } $newdef = "$hash->{helper}{'.LATITUDE'} $hash->{helper}{'.LONGITUDE'}" if $hash->{helper}{'.LATITUDE'} != AttrVal( 'global', 'latitude', 50.112 ) || $hash->{helper}{'.LONGITUDE'} != AttrVal( 'global', 'longitude', 8.686 ); $newdef .= " $hash->{INDOOR_HORIZON} $weather"; - - return CommandModify(undef, "$name $newdef"); + $hash->{DEF} = $newdef; + return; # CommandModify(undef, "$name $newdef"); } ################################################################################ @@ -232,91 +233,169 @@ sub Twilight_Notify { $s = "" if(!defined($s)); my $found = ($wname =~ m/^$re$/x || "$wname:$s" =~ m/^$re$/sx); - if($found) { - my $extWeather = ReadingsNum($hash->{helper}{extWeather}{Device}, $hash->{helper}{extWeather}{Reading},-1); - my $last = ReadingsNum($name, "cloudCover", -1); - - #here we have to split up for extended forecast handling... - - return if abs ($last - $extWeather) < 6; - #my ($cond, $condText) = [-1,"not known"]; - my $dispatch = defined $hash->{helper}{extWeather} && defined $hash->{helper}{extWeather}{dispatch} ? 1 : 0; - - #$cond = ReadingsNum($hash->{helper}{extWeather}{Device}, $hash->{helper}{extWeather}{dispatch}{cond_code},-2) if $dispatch; - #$condText = ReadingsVal($hash->{helper}{extWeather}{Device}, $hash->{helper}{extWeather}{dispatch}{cond_text},"unknown") if $dispatch; - - my $weather_horizon = Twilight_getWeatherHorizon( $hash, $extWeather, 1); - #add $sr_weather_horizon and $ss_weather_horizon here, then we will have to do some more Twilight_calc... - - my ($sr, $ss) = Twilight_calc( $hash, $weather_horizon, "7" ); ##these are numbers - my $now = time(); - - #$hash->{SR_TEST} = $sr; - #$hash->{SS_TEST} = $ss; - - #done for today? - return if $now > min($ss , $hash->{TW}{ss_weather}{TIME}); - - #set potential dates in the past to now - $sr = max( $sr, $now - 0.01 ); - $ss = max( $ss, $now - 0.01 ); - - #renew dates and timers?, fire events? - my $nextevent = ReadingsVal($name,"nextEvent","none"); - my $nextEventTime = FmtTime( $sr ); - - readingsBeginUpdate( $hash ); - readingsBulkUpdate( $hash, "cloudCover", $extWeather ); - #readingsBulkUpdate( $hash, "condition_code", $cond ) if $dispatch; - #readingsBulkUpdate( $hash, "condition_txt", $condText ) if $dispatch; - - if ($now < $sr ) { - $hash->{TW}{sr_weather}{TIME} = $sr; - Twilight_RemoveInternalTimer( "sr_weather", $hash ); - Twilight_InternalTimer( "sr_weather", $sr, \&Twilight_fireEvent, $hash, 0 ); - readingsBulkUpdate( $hash, "sr_weather", $nextEventTime ); - readingsBulkUpdate( $hash, "nextEventTime", $nextEventTime ) if $nextevent eq "sr_weather"; - } - if ($now < $ss ) { - $nextEventTime = FmtTime( $ss ); - $hash->{TW}{ss_weather}{TIME} = $ss; - Twilight_RemoveInternalTimer( "ss_weather", $hash ); - Twilight_InternalTimer( "ss_weather", $ss, \&Twilight_fireEvent, $hash, 0 ); - readingsBulkUpdate( $hash, "ss_weather", $nextEventTime ); - readingsBulkUpdate( $hash, "nextEventTime", $nextEventTime ) if $nextevent eq "ss_weather"; - } - - readingsEndUpdate( $hash, defined( $hash->{LOCAL} ? 0 : 1 ) ); - #my $swip = $hash->{SWIP}; - #$hash->{SWIP} = 1 if !$swip; - #Twilight_TwilightTimes( $hash, "weather", $extWeather ); - #$hash->{SWIP} = 0 if !$swip; - Twilight_RemoveInternalTimer ("sunpos", $hash); - Twilight_InternalTimer ("sunpos", time()+1, \&Twilight_sunpos, $hash, 0); - } + return Twilight_HandleWeatherData( $hash, 1) if $found; } return; } +sub Twilight_HandleWeatherData { + my $hash = shift // return; + my $inNotify = shift // 0; + my $wname = $hash->{helper}{extWeather}{Device} // "none"; + my $name = $hash->{NAME}; + my ($extWeather, $sr_extWeather, $ss_extWeather); + my $dispatch = defined $hash->{helper}{extWeather} && defined $hash->{helper}{extWeather}{dispatch} ? 1 : 0; + + if (!$dispatch + || $dispatch && !defined $hash->{helper}{extWeather}{dispatch}{function} && !defined $hash->{helper}{extWeather}{dispatch}{userfunction} ) + { + $extWeather = ReadingsNum($wname, $hash->{helper}{extWeather}{Reading},-1); + } elsif (defined $hash->{helper}{extWeather}{dispatch}{function} ) { + #Log3( $hash, 5, "[$hash->{NAME}] before dispatch" ); + return if ref $hash->{helper}{extWeather}{dispatch}->{function} ne 'CODE'; + ( $extWeather, $sr_extWeather, $ss_extWeather ) = $hash->{helper}{extWeather}{dispatch}->{function}->($hash, $wname); + Log3( $hash, 5, "[$hash->{NAME}] after dispatch. results: $extWeather $sr_extWeather $ss_extWeather" ); + + } elsif (defined $hash->{helper}{extWeather}{dispatch}{userfunction} ) { + #Log3( $hash, 5, "[$hash->{NAME}] before dispatch" ); + my %specials = ( + '$WEATHERDEV' => $extWeather, + '$WEATHERREADING' => $hash->{helper}{extWeather}{dispatch}{trigger} + ); + my $evalcode = EvalSpecials ($hash->{helper}{extWeather}{dispatch}->{function}, %specials); + + ( $extWeather, $sr_extWeather, $ss_extWeather ) = + AnalyzePerlCommand( $hash, $evalcode ); + + Log3( $hash, 5, "[$hash->{NAME}] external code results: $extWeather $sr_extWeather $ss_extWeather" ); + + } + + my $last = ReadingsNum($name, "cloudCover", -1); + + #here we have to split up for extended forecast handling... + + $inNotify ? Log3( $hash, 5, "[$name] NotifyFn called, reading is $extWeather, last is $last" ) + : Log3( $hash, 5, "[$name] timer based weather update called, reading is $extWeather, last is $last" ); + + return if $inNotify && abs ($last - $extWeather) < 6; + + my $weather_horizon = Twilight_getWeatherHorizon( $hash, $extWeather, 1); + + my ($sr, $ss) = Twilight_calc( $hash, $weather_horizon, "7" ); #these are numbers + my ($sr_wh,$ss_wh); + if (defined $ss_extWeather) { + Log3( $hash, 5, "[$name] ss_extWeather exists" ); + $sr_wh = Twilight_getWeatherHorizon( $hash, $sr_extWeather, 0); + $ss_wh = Twilight_getWeatherHorizon( $hash, $ss_extWeather, 0); + my ($srt_wh, $sst_wh) = Twilight_calc( $hash, $sr_wh, "7" ); + $sr = $srt_wh; + ($srt_wh, $sst_wh) = Twilight_calc( $hash, $ss_wh, "7" ); + $ss = $sst_wh; + } + + my $now = time(); + Log3( $hash, 5, "[$name] extW-update fn: calc sr_w is ".FmtTime( $sr) .", ss_w is ".FmtTime( $ss) ); + + #done for today? + return if $inNotify && $now > min($ss , $hash->{TW}{ss_weather}{TIME}); + Log3( $hash, 5, "[$name] not yet done for today" ); + + #set potential dates in the past to now + my $sr_passed = 0; + my $ss_passed = 0; + if ($inNotify) { + $sr = max( $sr, $now - 0.01 ); + $ss = max( $ss, $now - 0.01 ); + $sr_passed = $hash->{TW}{sr_weather}{TIME} > $sr ? 1 : 0; + $ss_passed = $hash->{TW}{ss_weather}{TIME} > $ss ? 1 : 0; + } + + #renew dates and timers?, fire events? + my $nextevent = ReadingsVal($name,"nextEvent","none"); + my $nextEventTime = FmtTime( $sr ); + + readingsBeginUpdate( $hash ); + readingsBulkUpdate( $hash, "cloudCover", $extWeather ); + readingsBulkUpdate( $hash, "cloudCover_sr", $sr_extWeather ) if $sr_wh && $now < $sr; + readingsBulkUpdate( $hash, "cloudCover_ss", $ss_extWeather ) if $ss_wh && $now < $ss; + + if ( $now < $sr ) { + #deleteSingleRegisteredInternalTimer( "sr_weather", $hash ); + $hash->{TW}{sr_weather}{TIME} = $sr; + resetRegisteredInternalTimer( "sr_weather", $sr, \&Twilight_fireEvent, $hash, 0 ); + readingsBulkUpdate( $hash, "sr_weather", $nextEventTime ) if $inNotify; + readingsBulkUpdate( $hash, "nextEventTime", $nextEventTime ) if $nextevent eq "sr_weather"; + } elsif ( $sr_passed ) { + #deleteSingleRegisteredInternalTimer( "sr_weather", $hash ); + deleteSingleRegisteredInternalTimer( "sr_weather", $hash, \&Twilight_fireEvent); + readingsBulkUpdate( $hash, "sr_weather", $nextEventTime ); + if ( $nextevent eq "sr_weather" ) { + readingsBulkUpdate( $hash, "nextEvent", "ss_weather" ) ; + readingsBulkUpdate( $hash, "nextEventTime", FmtTime( $ss ) ) ; + readingsBulkUpdate( $hash, "state", "6" ); + readingsBulkUpdate( $hash, "light", "6" ); + readingsBulkUpdate( $hash, "aktEvent", "sr_weather" ); + #deleteSingleRegisteredInternalTimer( "ss_weather", $hash ); + resetRegisteredInternalTimer( "ss_weather", $ss, \&Twilight_fireEvent, $hash, 0 ); + } + } + + if ( $now < $ss ) { + $nextEventTime = FmtTime( $ss ); + #deleteSingleRegisteredInternalTimer( "ss_weather", $hash ); + $hash->{TW}{ss_weather}{TIME} = $ss; + #$hash->{TW}{ss_weather}{SWIP} = 1; + resetRegisteredInternalTimer( "ss_weather", $ss, \&Twilight_fireEvent, $hash, 0 ); + readingsBulkUpdate( $hash, "ss_weather", $nextEventTime ) if $inNotify; + readingsBulkUpdate( $hash, "nextEventTime", $nextEventTime ) if $nextevent eq "ss_weather" && !$ss_passed; + } elsif ( $ss_passed ) { + deleteSingleRegisteredInternalTimer( "ss_weather", $hash, \&Twilight_fireEvent ); + readingsBulkUpdate( $hash, "ss_weather", $nextEventTime ); + if ( $nextevent eq "ss_weather" ) { + readingsBulkUpdate( $hash, "nextEvent", "ss_indoor" ) ; + readingsBulkUpdate( $hash, "nextEventTime", FmtTime( $hash->{TW}{ss_indoor}{TIME} ) ) ; + readingsBulkUpdate( $hash, "aktEvent", "ss_weather" ); + readingsBulkUpdate( $hash, "state", "8" ); + readingsBulkUpdate( $hash, "light", "4" ); + } + } + + readingsEndUpdate( $hash, defined( $hash->{LOCAL} ? 0 : 1 ) ); + #my $swip = $hash->{SWIP}; + #$hash->{SWIP} = 1 if !$swip; + #Twilight_TwilightTimes( $hash, "weather", $extWeather ); + #$hash->{SWIP} = 0 if !$swip; + #deleteSingleRegisteredInternalTimer ("sunpos", $hash); + return resetRegisteredInternalTimer("sunpos", time()+1, \&Twilight_sunpos, $hash, 0); +} + sub Twilight_Firstrun { my $hash = shift // return; my $name = $hash->{NAME}; - $hash->{SWIP} = 0; + #$hash->{SWIP} = 0; my $attrVal = AttrVal( $name,'useExtWeather', $hash->{DEFINE}); $attrVal = "$hash->{helper}{extWeather}{Device}:$hash->{helper}{extWeather}{Reading}" if !$attrVal && defined $hash->{helper} && defined $hash->{helper}{extWeather}{Device} && defined $hash->{helper}{extWeather}{Reading}; + $attrVal = "$hash->{helper}{extWeather}{Device}:$hash->{helper}{extWeather}{trigger}" if !$attrVal && defined $hash->{helper} && defined $hash->{helper}{extWeather}{Device} && defined $hash->{helper}{extWeather}{trigger}; + + my $extWeatherVal = 0; if ($attrVal) { Twilight_init_ExtWeather_usage( $hash, $attrVal ); - my $extWeatherVal = ReadingsVal($hash->{helper}{extWeather}{Device}, $hash->{helper}{extWeather}{Reading},"-1"); - readingsSingleUpdate ($hash, "cloudCover", $extWeatherVal, 0); - Twilight_getWeatherHorizon( $hash, $extWeatherVal ); - Twilight_TwilightTimes( $hash, "weather", $extWeatherVal ); + $extWeatherVal = ReadingsNum( $name, "cloudCover", ReadingsNum( $hash->{helper}{extWeather}{Device}, $hash->{helper}{extWeather}{Reading}, 0 ) ); + readingsSingleUpdate ( $hash, "cloudCover", $extWeatherVal, 0 ) if $extWeatherVal; } - - my $mHash = { HASH => $hash }; - Twilight_sunpos($mHash); - Twilight_Midnight($mHash); + Twilight_getWeatherHorizon( $hash, $extWeatherVal ); + + #next 3: Materials only + #Twilight_TwilightTimes( $hash, "mid") if !defined $hash->{helper}{extWeather}{Device}; + #Twilight_TwilightTimes( $hash, "weather"); + #return Twilight_HandleWeatherData( $hash, 0); + + my $fnHash = { HASH => $hash }; + Twilight_sunpos($fnHash) if !$attrVal; + Twilight_Midnight($fnHash, 1); delete $hash->{DEFINE}; return; @@ -334,9 +413,9 @@ sub Twilight_Attr { return Twilight_init_ExtWeather_usage($hash, $attrVal); } elsif ($cmd eq "del") { notifyRegexpChanged( $hash, "" ); - delete $hash->{helper}{extWeather}{regexp}; - delete $hash->{helper}{extWeather}{Device}; - delete $hash->{helper}{extWeather}{Reading}; + for my $key ( keys %{ $hash->{helper}{extWeather} } ) { + delete $hash->{helper}{extWeather}{$key}; + } delete $hash->{helper}{extWeather}; } } @@ -346,16 +425,28 @@ sub Twilight_init_ExtWeather_usage { my $hash = shift // return; my $devreading = shift // 1000; my $useTimer = shift // 0; - my ($extWeather, $extWReading, $err) ; + my ($extWeather, $extWReading, $err); + my @parts; if (!looks_like_number($devreading)) { - ($extWeather, $extWReading) = split( ":", $devreading ); + @parts = split( " ", $devreading, 2 ); + ($extWeather, $extWReading) = split( ":", $parts[0] ); return "External weather device seems not to exist" if (!defined $defs{$extWeather} && $init_done); + + ### This is the place to allow external dispatch functions... + if ($parts[1] && 0) { #&& 0 to disable this part atm... + my %specials = ( + '$WEATHERDEV' => $extWeather, + '$WEATHERREADING' => $extWReading + ); + my $err = perlSyntaxCheck( $parts[1], %specials ); + return $err if ( $err ); + } } else { #conversion code, try to guess the ext. weather device to replace yahoo my @devices=devspec2array("TYPE=Weather"); return "No Weather-Type device found if !$devices[0]"; $extWeather = $devices[0]; - $extWReading = "cloudCover"; + #$extWReading = "cloudCover"; } ($err, $extWReading) = Twilight_disp_ExtWeather($hash, $extWeather) if !$extWReading; @@ -364,7 +455,9 @@ sub Twilight_init_ExtWeather_usage { notifyRegexpChanged($hash, $extWregex); $hash->{helper}{extWeather}{regexp} = $extWregex; $hash->{helper}{extWeather}{Device} = $extWeather; - $hash->{helper}{extWeather}{Reading} = $extWReading; + $hash->{helper}{extWeather}{Reading} = $extWReading if !$parts[1] && !exists $hash->{helper}{extWeather}{dispatch}; + $hash->{helper}{extWeather}{trigger} = $extWReading if $parts[1]; + $hash->{helper}{extWeather}{userfunction} = $parts[1] if $parts[1]; return InternalTimer( time(), \&Twilight_Firstrun,$hash,0) if $init_done && $useTimer; return; } @@ -375,9 +468,21 @@ sub Twilight_disp_ExtWeather { my ( $err, $extWReading ); my $wtype = InternalVal( $extWeather, 'TYPE', undef ); return ("No type info about extWeather available!", "none") if !$wtype; + for my $key ( keys %{ $hash->{helper}{extWeather} } ) { + delete $hash->{helper}{extWeather}{$key}; + } + delete $hash->{helper}{extWeather}{dispatch}; + my $dispatch = { - "Weather" => {"cloudCover" => "cloudCover", "cond_code" => "code", "cond_text" => "condition"}, + "Weather" => { + "cloudCover" => "cloudCover", + "function" => \&getwTYPE_Weather + }, + "PROPLANTA" => { + "cloudCover" => "fc0_cloud06", + "function" => \&getwTYPE_PROPLANTA + } }; if (ref $dispatch->{$wtype} eq 'HASH') { $extWReading = $dispatch->{$wtype}{cloudCover}; @@ -407,61 +512,66 @@ sub Twilight_Get { } ################################################################################ -sub Twilight_InternalTimer { - my ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone ) = @_; - - my $timerName = "$hash->{NAME}_$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) ); - addTimer($timerName,$tim, $callback, $mHash, $waitIfInitNotDone ); - #InternalTimer( $tim, $callback, $mHash, $waitIfInitNotDone ); - return $mHash; +sub resetRegisteredInternalTimer { + my ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone, $oldTime ) = @_; + deleteSingleRegisteredInternalTimer( $modifier, $hash, $callback ); + setRegisteredInternalTimer ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone ); + return; } ################################################################################ -sub Twilight_RemoveInternalTimer { - my $modifier = shift; - my $hash = shift // return; +sub setRegisteredInternalTimer { + my ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone ) = @_; my $timerName = "$hash->{NAME}_$modifier"; - my $myHash = $hash->{TIMER}{$timerName}; - if ( defined($myHash) ) { - delete $hash->{TIMER}{$timerName}; + my $fnHash = { + HASH => $hash, + NAME => $timerName, + MODIFIER => $modifier + }; + if ( defined( $hash->{TIMER}{$timerName} ) ) { + Log3( $hash, 1, "[$hash->{NAME}] possible overwriting of timer $timerName - please delete it first" ); + stacktrace(); + } + else { + $hash->{TIMER}{$timerName} = $fnHash; + } + + Log3( $hash, 5, "[$hash->{NAME}] setting Timer: $timerName " . FmtDateTime($tim) ); + InternalTimer( $tim, $callback, $fnHash, $waitIfInitNotDone ); + return $fnHash; +} + +################################################################################ +sub deleteSingleRegisteredInternalTimer { + my $modifier = shift; + my $hash = shift // return; + my $callback = shift; + + my $timerName = "$hash->{NAME}_$modifier"; + my $fnHash = $hash->{TIMER}{$timerName}; + if ( defined($fnHash) ) { Log3( $hash, 5, "[$hash->{NAME}] removing Timer: $timerName" ); - removeTimer($timerName); - #RemoveInternalTimer($myHash); + RemoveInternalTimer($fnHash); + delete $hash->{TIMER}{$timerName}; } return; } ################################################################################ -sub Twilight_GetHashIndirekt { - my $myHash = shift; - my $function = shift // return; - - if ( !defined( $myHash->{HASH} ) ) { - - #Log3 ($hash, 5, "[$function] myHash not valid"); - return; +sub deleteAllRegisteredInternalTimer { + my $hash = shift // return; + + for my $key ( keys %{ $hash->{TIMER} } ) { + deleteSingleRegisteredInternalTimer( $hash->{TIMER}{$key}{MODIFIER}, $hash ); } - return $myHash->{HASH}; + return; } -################################################################################ -sub Twilight_midnight_seconds { - my $now = shift // return; + +################################################################################ +sub secondsSinceMidnight { + my $now = shift // time(); my @time = localtime($now); my $secs = ( $time[2] * 3600 ) + ( $time[1] * 60 ) + $time[0]; return $secs; @@ -474,11 +584,10 @@ sub Twilight_calc { my $idx = shift // return; my $now = time(); - my $midnight = $now - Twilight_midnight_seconds( $now ); + my $midnight = $now - secondsSinceMidnight( $now ); my $lat = $hash->{helper}{'.LATITUDE'}; my $long = $hash->{helper}{'.LONGITUDE'}; - #my $sr = sunrise_abs("Horizon=$deg"); my $sr = sr_alt( $now, 1, 0, 0, 0, "Horizon=$deg", undef, undef, undef, $lat, $long ); @@ -501,13 +610,13 @@ sub Twilight_calc { ################################################################################ sub Twilight_TwilightTimes { - my ( $hash, $whitchTimes, $xml ) = @_; + my $hash = shift; + my $whitchTimes = shift // return; + my $firstrun = shift // 0; my $name = $hash->{NAME}; - #my $horizon = $hash->{HORIZON}; - #my $horizon = ReadingsNum($name,"horizon",0); - my $swip = $hash->{SWIP}; + my $swip = !$firstrun; my $lat = $hash->{helper}{'.LATITUDE'}; my $long = $hash->{helper}{'.LONGITUDE'}; @@ -535,8 +644,8 @@ sub Twilight_TwilightTimes { $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}{SWIP} = $swip; + #$hash->{TW}{$ss}{SWIP} = $swip; ( $hash->{TW}{$sr}{TIME}, $hash->{TW}{$ss}{TIME} ) = Twilight_calc( $hash, $deg, $idx ); @@ -570,25 +679,20 @@ sub Twilight_TwilightTimes { 0 .. $#ereignisse - 1; # ------------------------------------------------------------------------------ - my $myHash; my $now = time(); - my $secSinceMidnight = Twilight_midnight_seconds($now); - 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"; + #my @keyListe = qw "DEG LIGHT STATE SWIP TIME NAMENEXT"; + my @keyListe = qw "DEG LIGHT STATE TIME NAMENEXT"; for my $ereignis ( sort keys %{ $hash->{TW} } ) { next if ( $whitchTimes eq "weather" && !( $ereignis =~ m/weather/ ) ); - Twilight_RemoveInternalTimer( $ereignis, $hash ); # if(!$jetztIstMitternacht); - if ( defined $hash->{TW}{$ereignis}{TIME} && $hash->{TW}{$ereignis}{TIME} > 0 ) { - $myHash = Twilight_InternalTimer( $ereignis, $hash->{TW}{$ereignis}{TIME}, + deleteSingleRegisteredInternalTimer( $ereignis, $hash, \&Twilight_fireEvent ); + + if ( defined $hash->{TW}{$ereignis}{TIME} && $hash->{TW}{$ereignis}{TIME} > $now ) { # had been > 0 + #my $fnHash = + setRegisteredInternalTimer( $ereignis, $hash->{TW}{$ereignis}{TIME}, \&Twilight_fireEvent, $hash, 0 ); - map { $myHash->{$_} = $hash->{TW}{$ereignis}{$_} } @keyListe; + #map { $fnHash->{$_} = $hash->{TW}{$ereignis}{$_} } @keyListe; } } @@ -597,18 +701,23 @@ sub Twilight_TwilightTimes { } ################################################################################ sub Twilight_fireEvent { - my $myHash = shift // return; + my $fnHash = shift // return; + + my ($hash, $modifier) = ($fnHash->{HASH}, $fnHash->{MODIFIER}); - my $hash = Twilight_GetHashIndirekt( $myHash, ( caller(0) )[3] ); + #my $hash = Twilight_GetHashIndirekt( $fnHash, ( caller(0) )[3] ); return if ( !defined($hash) ); + my $name = $hash->{NAME}; - my $event = $myHash->{MODIFIER}; + #my $modifier = $fnHash->{MODIFIER}; + my $event = $modifier; + my $myHash =$hash->{TW}{$modifier}; my $deg = $myHash->{DEG}; my $light = $myHash->{LIGHT}; my $state = $myHash->{STATE}; - my $swip = $myHash->{SWIP}; + #my $swip = $myHash->{SWIP}; my $eventTime = $myHash->{TIME}; my $nextEvent = $myHash->{NAMENEXT}; @@ -622,7 +731,8 @@ sub Twilight_fireEvent { : "undefined"; my $doTrigger = !( defined( $hash->{LOCAL} ) ) - && ( abs($delta) < 6 || $swip && $state gt $oldState ); + #&& ( abs($delta) < 6 || $swip && $state gt $oldState ); + && ( abs($delta) < 6 || $state gt $oldState ); Log3( $hash, 4, @@ -646,93 +756,34 @@ sub Twilight_fireEvent { ################################################################################ sub Twilight_Midnight { - my $myHash = shift // return; + my $fnHash = shift // return; my $firstrun = shift // 0; - my $hash = Twilight_GetHashIndirekt( $myHash, ( caller(0) )[3] ); - return if ( !defined($hash) ); - - return Twilight_HandleWeatherData( $hash, "Mid", $firstrun, ReadingsNum($hash->{NAME},"cloudCover",0) ); -} - -################################################################################ -sub Twilight_WeatherTimerUpdate { - my $myHash = shift // return; - my $swip = shift // 1; + my ($hash, $modifier) = ($fnHash->{HASH}, $fnHash->{MODIFIER}); - my $hash = Twilight_GetHashIndirekt( $myHash, ( caller(0) )[3] ); + #my $hash = Twilight_GetHashIndirekt( $fnHash, ( caller(0) )[3] ); return if ( !defined($hash) ); - - return Twilight_HandleWeatherData( $hash, "weather", $swip ); -} - -################################################################################ - -sub Twilight_HandleWeatherData { - my $hash = shift; - my $mode = shift; - my $swip = shift // return; - my $cloudCover = shift // ReadingsNum($hash->{NAME},"cloudCover",0); - - $hash->{SWIP} = $swip; - - Twilight_getWeatherHorizon( $hash, $cloudCover ); - Twilight_TwilightTimes( $hash, $mode, $cloudCover ); - return Twilight_StandardTimerSet( $hash ); - -} - -################################################################################ -sub Twilight_RepeatTimerSet { - my $hash = shift; - my $mode = shift // return; - - my $midnight = time() + 60; - - Twilight_RemoveInternalTimer( "Midnight", $hash ); - return Twilight_InternalTimer( "Midnight", $midnight, \&Twilight_Midnight, $hash, - 0 ) - if $mode eq "Mid"; - - return Twilight_InternalTimer( "Midnight", $midnight, - \&Twilight_WeatherTimerUpdate, $hash, 0 ); -} - -################################################################################ -sub Twilight_StandardTimerSet { - my $hash = shift // return; - my $midnight = time() - Twilight_midnight_seconds( time() ) + 24 * 3600 + 1; - - Twilight_RemoveInternalTimer( "Midnight", $hash ); - Twilight_InternalTimer( "Midnight", $midnight, \&Twilight_Midnight, $hash, 0 ); - return Twilight_WeatherTimerSet($hash); -} - -################################################################################ -sub Twilight_WeatherTimerSet { - my $hash = shift // return; - my $now = time(); - - Twilight_RemoveInternalTimer( "weather", $hash ); - for my $key ( "sr_weather", "ss_weather" ) { - my $tim = $hash->{TW}{$key}{TIME}; - if ( $tim - 60 * 60 > $now + 60 ) { - Twilight_InternalTimer( "weather", $tim - 60 * 60, - \&Twilight_WeatherTimerUpdate, $hash, 0 ); - last; - } + + if (!defined $hash->{helper}{extWeather}{Device} || $firstrun) { + Twilight_TwilightTimes( $hash, "mid", $firstrun); + Twilight_sunposTimerSet($hash); + } else { + Twilight_HandleWeatherData( $hash, 0); + Twilight_TwilightTimes( $hash, "mid", $firstrun); } - - return; + my $now = time(); + my $midnight = $now - secondsSinceMidnight( $now ) + DAYSECONDS + 1; + return resetRegisteredInternalTimer( "Midnight", $midnight, \&Twilight_Midnight, $hash, 0 ); + } + + ################################################################################ sub Twilight_sunposTimerSet { my $hash = shift // return; - Twilight_RemoveInternalTimer( "sunpos", $hash ); - return Twilight_InternalTimer( "sunpos", time() + $hash->{SUNPOS_OFFSET}, - \&Twilight_sunpos, $hash, 0 ); + return resetRegisteredInternalTimer( "sunpos", time() + $hash->{SUNPOS_OFFSET}, \&Twilight_sunpos, $hash, 0 ); } ################################################################################ @@ -756,9 +807,11 @@ sub Twilight_getWeatherHorizon { ################################################################################ sub Twilight_sunpos { - my $myHash = shift // return; - - my $hash = Twilight_GetHashIndirekt( $myHash, ( caller(0) )[3] ); + my $fnHash = shift // return; + + my ($hash, $modifier) = ($fnHash->{HASH}, $fnHash->{MODIFIER}); + + #my $hash = Twilight_GetHashIndirekt( $fnHash, ( caller(0) )[3] ); return if ( !defined($hash) ); my $hashName = $hash->{NAME}; @@ -870,20 +923,16 @@ sub Twilight_sunpos { $twilight_weather = int( ( $dElevation - $hash->{WEATHER_HORIZON} + 12.0 ) / 18.0 * 1000 ) / 10; - Log3( $hash, 5, "[$hash->{NAME}] Original weather readings" ); + Log3( $hash, 5, "[$hashName] Original weather readings" ); } else { - my $extDev = $hash->{helper}{extWeather}{Device}; - my $extReading = $hash->{helper}{extWeather}{Reading}; + #my $extDev = $hash->{helper}{extWeather}{Device}; + #my $extReading = $hash->{helper}{extWeather}{Reading} ; #my ( $extDev, $extReading ) = split( ":", $ExtWeather ); - my $extWeatherHorizont = ReadingsNum($extDev ,$extReading , -1 ); + my $extWeatherHorizont = ReadingsNum($hashName,'cloudCover' , -1 ); if ( $extWeatherHorizont >= 0 ) { $extWeatherHorizont = min (100, $extWeatherHorizont); Log3( $hash, 5, - "[$hash->{NAME}] " - . "New weather readings from: " - . $extDev . ":" - . $extReading . ":" - . $extWeatherHorizont ); + "[$hashName] Using cloudCover value $extWeatherHorizont" ); $twilight_weather = $twilight - int( 0.007 * ( $extWeatherHorizont**2 ) ) ; ## SCM: 100% clouds => 30% light (rough estimation) @@ -893,11 +942,7 @@ sub Twilight_sunpos { 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" ); + "[$hashName] No useable cloudCover value available: ${$extWeatherHorizont}, taking existant weather horizon." ); } } @@ -957,6 +1002,60 @@ sub twilight { return h2hms_fmt($t); } +########### +# Dispatch functions + +sub getTwilightHours { + my $hash = shift // return; + my $hour = ( localtime )[2]; + my $sr_hour = ( localtime( $hash->{TW}{sr_weather}{TIME}))[2]; + my $ss_hour = ( localtime( $hash->{TW}{ss_weather}{TIME}))[2]; + return $hour, $sr_hour, $ss_hour; +} + +sub getwTYPE_Weather { + my $hash = shift // return; + + my @ret; + my $extDev = $hash->{helper}{extWeather}{Device}; + my ($hour, $sr_hour, $ss_hour) = getTwilightHours($hash); + + my $rAge = int(ReadingsAge("extDev","cloudCover",0)/3600); + + $ret[0] = $rAge < 24 ? ReadingsNum($extDev,"cloudCover",0) : 50; + Log3( $hash, 5, "[$hash->{NAME}] function is called, cc is $ret[0], hours sr: $sr_hour, ss: $ss_hour" ); + + my $hfc_sr = max( 0 , $sr_hour - $hour ) + $rAge; #remark: needs some additionals logic for midnight updates! (ReadingsAge()?) + my $hfc_ss = max( 0 , $ss_hour - $hour ) + $rAge; + + $ret[1] = $hfc_sr && $rAge < 24 ? ReadingsNum($extDev,"hfc${hfc_sr}_cloudCover",0) : $ret[0]; + $ret[2] = $hfc_ss && $rAge < 24 ? ReadingsNum($extDev,"hfc${hfc_ss}_cloudCover",0) : $ret[0]; + + return @ret; +} + + +sub getwTYPE_PROPLANTA { + my $hash = shift // return; + + + my $extDev = $hash->{helper}{extWeather}{Device}; + my @hour = getTwilightHours($hash); + + my $fc_day0 = secondsSinceMidnight( time() ) > 60 ? 0 : 1; + my $fc_day1 = $fc_day0 + 1; + + my @ret; + for (my $i = 0; $i < 3 ; $i++) { + $hour[$i] <= 4 ? $ret[$i] = ReadingsNum($extDev,"fc${fc_day0}_cloud03",0) : + $hour[$i] <= 7 ? $ret[$i] = ReadingsNum($extDev,"fc${fc_day0}_cloud06",0) : + $hour[$i] <= 10 ? $ret[$i] = ReadingsNum($extDev,"fc${fc_day0}_cloud09",0) : + $hour[$i] <= 19 ? $ret[$i] = ReadingsNum($extDev,"fc${fc_day0}_cloud18",0) : + $hour[$i] <= 22 ? $ret[$i] = ReadingsNum($extDev,"fc${fc_day0}_cloud21",0) : + $ret[$i] = ReadingsNum($extDev,"fc${fc_day1}_cloud03",0); + } + return @ret;; +} 1; __END__ @@ -1292,8 +1391,7 @@ Anwendungsbeispiel: "Math::Trig" : "0", "Time::Local" : "0", "strict" : "0", - "warnings" : "0", - "FHEM::Core::Timer::Helper" : "0" + "warnings" : "0" } } },