2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

59_Twilight.pm: bugfix in timer admin

git-svn-id: https://svn.fhem.de/fhem/trunk@23015 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
Beta-User 2020-10-24 06:43:39 +00:00
parent d1e0ff720f
commit 1602ea563a

View File

@ -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"
}
}
},