2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-09 20:57:11 +00:00

36_Shelly.pm:bug fix update interval,on/off-for-timer

git-svn-id: https://svn.fhem.de/fhem/trunk@28109 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
Starkstrombastler 2023-10-30 23:05:56 +00:00
parent a3d4b2230d
commit 79257893a8
2 changed files with 81 additions and 66 deletions

View File

@ -1,5 +1,8 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- bugfix: 36_Shelly: Refresh: removed fixed name of FHEMWEB device
Gen.2: on/off-for-timer
update interval of GetStatus call
- bugfix: 36_Shelly: see Forum msg #1291182
- feature: 76_SolarForecast: see Forum:#?msg=1291035
- change: 70_PylonLowVoltage: add needed data format to commandref

View File

@ -31,6 +31,9 @@
# added wifi data to Gen.1 devices
# Bug Fix: inttemp for ShellyPro2
# 5.02 Bug Fix: on/off-for-timer for ShellyPlus2 temporarely taken out
# 5.03 Bug Fix: Refresh: removed fixed name of FHEMWEB device
# Bug Fix: Gen.2: on/off-for-timer
# Bug Fix: update interval of GetStatus call
package main;
@ -45,7 +48,7 @@ use vars qw{%attr %defs};
sub Log($$);
#-- globals on start
my $version = "5.02 30.10.2023";
my $version = "5.03 31.10.2023";
#-- support differtent readings-names for energy & power metering
my %mapping = (
@ -655,7 +658,7 @@ sub Shelly_Define($$) {
InternalTimer(time()+5, "Shelly_get_model", $hash,0);
#-- perform status update in a minute or so
InternalTimer(time()+10, "Shelly_status", $hash,0); #Debug $hash->{NAME}."646 calling Timer Shelly_status in 14 sec"; #14
InternalTimer(time()+10, "Shelly_status", $hash,0);
InternalTimer(time()+12, "Shelly_EMData", $hash,0);
InternalTimer(time()+14, "Shelly_shelly", $hash,0);
@ -900,7 +903,7 @@ sub Shelly_get_model {
}
sub Refresh {
fhem("trigger WEB JS:location.reload(true)"); # try a browser refresh ??
fhem("trigger $FW_wname JS:location.reload(true)"); # try a browser refresh ??
}
#######################################################################################
@ -1222,7 +1225,7 @@ sub Shelly_Attr(@) {
return $error;
}
# perform an update of the position related readings
RemoveInternalTimer($hash,"Shelly_status"); #Debug "$name 1207 removed Timer Shelly_status, now calling in 1 sec";
RemoveInternalTimer($hash,"Shelly_status");
InternalTimer(gettimeofday()+1, "Shelly_status", $hash); ##ü
#---------------------------------------
@ -1405,7 +1408,7 @@ sub Shelly_Attr(@) {
$hash->{INTERVAL}=60;
}
if ($init_done) {
RemoveInternalTimer($hash,"Shelly_status"); #Debug "$name 1389 removed Timer Shelly_status, now calling in Intv sec";
RemoveInternalTimer($hash,"Shelly_status");
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "Shelly_status", $hash, 0)
if( $hash->{INTERVAL} != 0 );
}
@ -1597,7 +1600,7 @@ sub Shelly_Set ($@) {
}elsif( $model =~ /shelly(rgbw|bulb).*/ && $mode eq "color" ){
$newkeys .= " ".join(" ", sort keys %setsrgbwc) ;
}
if( $shelly_models{$model}[0]>0 && $shelly_models{$model}[4]==1 ){
if(0 && $shelly_models{$model}[0]>0 && $shelly_models{$model}[4]==1 ){
# xx-for-timer does not work
$newkeys =~ s/on-for-timer//;
$newkeys =~ s/off-for-timer//;
@ -1616,15 +1619,15 @@ sub Shelly_Set ($@) {
my $isWhat=$2;
my $subs;
Log3 $name,3,"[Shelly_Set] calling for device $name with command $cmd".( defined($value)?" and channel $value":", without channel" );
readingsBeginUpdate($hash); #Debug "$name $signal $mode";
readingsBeginUpdate($hash);
if( $signal eq "out" && $mode eq "relay"){ #change of device output
$subs = ($shelly_models{$model}[0] == 1) ? "" : "_".$value;
readingsBulkUpdateMonitored($hash,"relay$subs",$isWhat); #Debug "relay$subs";
readingsBulkUpdateMonitored($hash,"relay$subs",$isWhat);
readingsBulkUpdateMonitored($hash,"state",$isWhat)
if( $shelly_models{$model}[0]==1 ); ## do not set state on multichannel-devices
}elsif( $signal eq "out" && $mode eq "white"){ #change of bulb device output
$subs = ($shelly_models{$model}[2] == 1) ? "" : "_".$value; # no of dimmers
readingsBulkUpdateMonitored($hash,"state$subs",$isWhat);#Debug "state$subs";
readingsBulkUpdateMonitored($hash,"state$subs",$isWhat);
}elsif( $signal eq "out" && !$value ){ #change of single channel device output
#$subs = "";
readingsBulkUpdateMonitored($hash,"state",$isWhat);
@ -2440,7 +2443,7 @@ sub Shelly_status {
#-- get status of component (relay, roller, input, EM, ...); we need to submit the call several times
for( $id=0; $id<$chn; $id++){
#$url = $url_."?id=".$id;
Log3 $name,4,"[Shelly_status] issue a non-blocking call to $url$id, callback to proc2G for comp=$comp";
Log3 $name,4,"[Shelly_status] issue a non-blocking call to $url$id, callback to proc2G for comp=$comp"; #4
HttpUtils_NonblockingGet({
url => $url.$id,
timeout => $timeout,
@ -2473,7 +2476,8 @@ sub Shelly_status {
}else{
$next=Shelly_proc2G($hash,$comp,$jhash);
}
Log3 $name,4,"[Shelly_status] $name: proc.G returned next update in $next seconds ($comp)";
Log3 $name,4,"[Shelly_status] $name: proc.G returned with value=$next for comp $comp"; #4
return undef if( $next == -1 );
#-- cyclic update (or update close to on/off-for-timer command)
@ -2483,8 +2487,8 @@ sub Shelly_status {
$next = 0.75*$hash->{INTERVAL};
}
return undef if( $next == 125 );
Log3 $name,4,"[Shelly_status] $name: next update in $next seconds ";
Log3 $name,4,"[Shelly_status] $name: next update for comp=$comp in $next seconds "; #4
RemoveInternalTimer($hash,"Shelly_status");
InternalTimer(gettimeofday()+$next, "Shelly_status", $hash, 1)
if( $hash->{INTERVAL} != 0 );
@ -2502,7 +2506,7 @@ return undef if( $next == 125 );
#
########################################################################################
sub Shelly_shelly {#Debug "aborting Shelly_shelly";return undef;
sub Shelly_shelly {
my ($hash) = @_;
my $name = $hash->{NAME};
my $state = $hash->{READINGS}{state}{VAL};
@ -2518,12 +2522,12 @@ sub Shelly_shelly {#Debug "aborting Shelly_shelly";return undef;
Log3 $name,4,"[Shelly_shelly] intentionally aborting, $name is not 2nd Gen";
return undef ;
}
#if( $model eq "shellyplusi4" ){Debug "aborting";return undef;}
my $url_ = $url."/rpc/";
#-- get status of Shelly (updates, status, ...)
$url = $url_."Shelly.GetStatus";
Log3 $name,4,"[Shelly_shelly] issue a non-blocking call to ".$url;
Log3 $name,4,"[Shelly_shelly] issue a non-blocking call to ".$url; #4
HttpUtils_NonblockingGet({
url => $url,
timeout => $timeout,
@ -2532,7 +2536,7 @@ sub Shelly_shelly {#Debug "aborting Shelly_shelly";return undef;
#-- get config of Shelly -
$url = $url_."Shelly.GetConfig";
Log3 $name,4,"[Shelly_shelly] issue a non-blocking call to ".$url;
Log3 $name,4,"[Shelly_shelly] issue a non-blocking call to ".$url; #4
HttpUtils_NonblockingGet({
url => $url,
timeout => $timeout,
@ -2541,7 +2545,7 @@ sub Shelly_shelly {#Debug "aborting Shelly_shelly";return undef;
#-- get device info of Shelly
$url = $url_."Shelly.GetDeviceInfo";
Log3 $name,4,"[Shelly_shelly] issue a non-blocking call to ".$url;
Log3 $name,4,"[Shelly_shelly] issue a non-blocking call to ".$url; #4
HttpUtils_NonblockingGet({
url => $url,
timeout => $timeout,
@ -2556,14 +2560,14 @@ sub Shelly_shelly {#Debug "aborting Shelly_shelly";return undef;
return undef
if( $hash->{INTERVAL} == 0 );
my $offset = $hash->{INTERVAL};
my $offset = 120; #$hash->{INTERVAL};
if( $model eq "shellypro3em" ){
# updates at every multiple of 60 seconds
$offset = $hash->{INTERVAL}<=60 ? 60 : int($hash->{INTERVAL}/60)*60 ;
# adjust to get readings 2sec after full minute
$offset = $offset + 2 - gettimeofday() % 60;
}
Log3 $name,5,"[Shelly_shelly] $name: long update in $offset seconds, Timer is Shelly_shelly"; #4
Log3 $name,4,"[Shelly_shelly] $name: long update in $offset seconds, Timer is Shelly_shelly"; #4
InternalTimer(gettimeofday()+$offset, "Shelly_shelly", $hash, 1);
return undef;
}
@ -3024,6 +3028,7 @@ sub Shelly_proc2G {
my $meters = $shelly_models{$model}[3];
my ($subs,$ison,$overpower,$voltage,$current,$power,$energy,$pfactor,$freq,$minutes,$errors); ##R minutes errors
my $timer = $hash->{INTERVAL};
# check we have a second gen device
if( $shelly_models{$model}[4]!=1 ){
@ -3044,10 +3049,10 @@ sub Shelly_proc2G {
$ison = $jhash->{'output'};
$ison =~ s/0|(false)/off/;
$ison =~ s/1|(true)/on/;
readingsBulkUpdateMonitored($hash,"relay".$subs,$ison);#Debug "$name 2ndGen relay >$ison\<";
readingsBulkUpdateMonitored($hash,"relay".$subs,$ison);
# Switch Reason: Trigger for switching
# --> init, http <-fhemWEB, WS_in, timer, loopback (=schedule?), HTTP <-Shelly-App
readingsBulkUpdateMonitored($hash,"reason".$subs,$jhash->{'source'});
readingsBulkUpdateMonitored($hash,"source".$subs,$jhash->{'source'});
readingsBulkUpdateMonitored($hash,"state",($shelly_models{$model}[0] == 1)?$ison:"OK");
@ -3401,7 +3406,7 @@ if(0){ # check calculation of reactive power!
# Energy consumption by minute (in Milliwatt-hours) for the last minute
$minutes = shelly_energy_fmt($hash,$jhash->{'aenergy'}{'by_minute'}[0],"mWh");
Log3 $name,4,"[Shelly_proc2G] $name $comp voltage$subs=$voltage, current$subs=$current, power$subs=$power";
Log3 $name,4,"[Shelly_proc2G] $name $comp voltage$subs=$voltage, current$subs=$current, power$subs=$power"; #4
readingsBulkUpdateMonitored($hash,"voltage".$subs,$voltage);
readingsBulkUpdateMonitored($hash,"current".$subs,$current);
@ -3424,13 +3429,25 @@ if(0){ # check calculation of reactive power!
}elsif( defined($jhash->{'temperature:0'}{'tC'}) ){
readingsBulkUpdateMonitored($hash,"inttemp",$jhash->{'temperature:0'}{'tC'}.$si_units{tempC}[$hash->{units}]);
}
# processing timers
if( $jhash->{'timer_started_at'} ){
if( $jhash->{'timer_remaining'} ){
$timer = $jhash->{'timer_remaining'};
}else{
$timer = $jhash->{'timer_started_at'} + $jhash->{'timer_duration'} - time();
$timer = round($timer,1);
}
readingsBulkUpdateMonitored($hash,"timer".$subs,$timer.$si_units{time}[$hash->{units}]);
}elsif(ReadingsVal($name,"timer$subs",undef) ){
readingsBulkUpdateMonitored($hash,"timer".$subs,'-');
}
}
readingsEndUpdate($hash,1);
if ($comp eq "status" || $comp eq "config" || $comp eq "info"){
return 125;
return -1;
}else{
return $hash->{INTERVAL};
return $timer;
}
}
@ -3869,7 +3886,7 @@ sub shelly_energy_fmt {
readingsEndUpdate($hash,1);
#-- Call status after switch.
RemoveInternalTimer($hash,"Shelly_status"); #Debug "$name 3683 removed Timer Shelly_status, now calling in 0.5 sec";
RemoveInternalTimer($hash,"Shelly_status");
InternalTimer(gettimeofday()+0.5, "Shelly_status", $hash,0);
return undef;
@ -3951,14 +3968,14 @@ if(0){
#-- after 1 second call power measurement
InternalTimer(gettimeofday()+1, "Shelly_updown2", $hash,1);
}else{ #2Gen
Log3 $name,5,"[Shelly_updown] calling Shelly_status for Metering of Gen2-Device"; #Debug "$name 3765 calling Timer Shelly_status";
Log3 $name,5,"[Shelly_updown] calling Shelly_status for Metering of Gen2-Device";
InternalTimer(gettimeofday()+1, "Shelly_status", $hash);
}
}else{
#-- perform two updates: after starting of drive, after expected stopping of drive
if( $hash->{INTERVAL}>0 ){
$hash->{DURATION} = 5 if( !$hash->{DURATION} ); # duration not provided by Gen2
RemoveInternalTimer($hash,"Shelly_status"); #Debug "$name 3772 removed Timer Shelly_status, now calling in 0.5 sec";
RemoveInternalTimer($hash,"Shelly_status");
InternalTimer(gettimeofday()+0.5, "Shelly_status", $hash); # after that: next update in reduced interval sec
InternalTimer(gettimeofday()+$hash->{DURATION}+0.5, "Shelly_interval", $hash,1); # reset interval
}
@ -3994,9 +4011,9 @@ sub Shelly_interval($){
my $state = $hash->{READINGS}{state}{VAL};
my $net = $hash->{READINGS}{network}{VAL};
Log3 $name,6,"[Shelly_onoff] try to execute command $cmd channel $channel for device $name ($state, $net)";
Log3 $name,6,"[Shelly_onoff] try to execute command $cmd channel $channel for device $name ($state, $net)";
return "Unsuccessful: Network Error for device $name, try device get status "
if( !$net || $net !~ /connected to/ );
if( !$net || $net !~ /connected to/ );
my $model = AttrVal($name,"model","generic");
my $timeout = AttrVal($name,"timeout",4);
@ -4007,7 +4024,7 @@ sub Shelly_interval($){
my $url = "http://$creds".$hash->{TCPIP};
if(1){
$url .= "/relay/$channel$cmd";
$callback=$cmd;
$callback="/relay/$channel$cmd";
}else{
$cmd =~ /(id=\d)/;
$callback= $url."/rpc/Switch.GetStatus?$1";
@ -4026,7 +4043,7 @@ if(1){
}
#processing incoming data
Log3 $name,5,"[Shelly_onoff] device $name has returned data $data";
Log3 $name,5,"[Shelly_onoff:callback] device $name has returned data \n$data";
my $json = JSON->new->utf8;
my $jhash = eval{ $json->decode( $data ) };
@ -4041,64 +4058,59 @@ if(1){
return;
}
### if($shelly_models{$model}[4] < 2){
my $ison = $jhash->{'ison'};
my $hastimer = $jhash->{'has_timer'};
my $onofftimer = $jhash->{'timer_remaining'};
my $hastimer = undef;
my $onofftimer = 0;
my $timerstr = "-";
my $source = $jhash->{'source'};
my $overpower = $jhash->{'overpower'};
$ison =~ s/0|(false)/off/;
$ison =~ s/1|(true)/on/;
if( $jhash->{'has_timer'} ){
$hastimer = $jhash->{'has_timer'};
$onofftimer = $jhash->{'timer_remaining'};
$timerstr = $onofftimer.$si_units{time}[$hash->{units}];
}
# check on successful execution
$cmd =~ /\?turn=(on|off)(\&timer=)?(\d+)?/;
$cmd =~ /\/relay\/(\d)\?turn=(on|off)(\&timer=)?(\d+)?/;
$channel = $1;
Log3 $name,1,"[Shelly_onoff] returns with problem for device $name, timer not set" if( $3 && $hastimer ne "1");
Log3 $name,1,"[Shelly_onoff] returns without success for device $name, cmd=$cmd but ison=$ison" if( $ison ne $1 );
Log3 $name,1,"[Shelly_onoff] returns with problem for device $name, timer not set" if( $4 && $hastimer ne "1");
Log3 $name,1,"[Shelly_onoff] returns without success for device $name, cmd=$cmd but ison=$ison" if( $ison ne $2 );
if( defined($overpower) && $overpower eq "1") {
Log3 $name,1,"[Shelly_onoff] device $name switched off automatically because of overpower signal";
}
if(0){### }else{
Log3 $name,4,"[Shelly_onoff] $name returns with command $cmd ";
# $jhash->{'was_on'}; #receiving this message means success
if( !$jhash ){
Log3 $name,1,"[Shelly_onoff] returns without success for device $name, cmd=$cmd ";
}else{ # on success we are requesting full status
#Log3 $name,0,$cmd;
fhem("sleep 0.75");
HttpUtils_NonblockingGet({
url => $cmd,
timeout => $timeout,
callback => sub($$$){ Shelly_status($hash,"relay",$_[1],$_[2]) } # "relay"
});
}
}
#--
my $subs = ($shelly_models{$model}[0] == 1) ? "" : "_".$channel;
if(0){ #my ($onofftimer,$source,$ison,$overpower); ##need this my... only for testing
Log3 $name,0,"$name $onofftimer $source ison:$ison\<";
Log3 $name,4,"[Shelly_onoff:callback] received callback from $name channel $channel is switched $ison, "
.($hastimer?"timer is set to $onofftimer sec":"no timer set");
readingsBeginUpdate($hash);
readingsBulkUpdateMonitored($hash,"timer",$onofftimer);
readingsBulkUpdateMonitored($hash,"source",$source);
readingsBulkUpdateMonitored($hash,"timer".$subs,$timerstr);
readingsBulkUpdateMonitored($hash,"source".$subs,$source);
if( $shelly_models{$model}[0] == 1 ){
readingsBulkUpdateMonitored($hash,"state",$ison);
}else{
readingsBulkUpdateMonitored($hash,"state","OK");
}
readingsBulkUpdateMonitored($hash,"relay".$subs,$ison);#Debug "$name if 0 proc";
readingsBulkUpdateMonitored($hash,"relay".$subs,$ison);
readingsBulkUpdateMonitored($hash,"overpower".$subs,$overpower)
if( $shelly_models{$model}[3]>0 && $model ne "shelly1" );
if( $shelly_models{$model}[3]>0 && $model ne "shelly1" );
readingsEndUpdate($hash,1);
}else{
#-- Call status after switch.
RemoveInternalTimer($hash,"Shelly_status"); #Debug "$name 3882 removed Timer Shelly_status, now calling in 0.5 sec";
InternalTimer(gettimeofday()+0.5, "Shelly_status", $hash,0);
}
#-- Call status after switch.
if( $hash->{INTERVAL}>0 ){
$onofftimer = ($onofftimer % $hash->{INTERVAL}) + 0.5; #modulus
}
RemoveInternalTimer($hash,"Shelly_status");
InternalTimer(gettimeofday()+$onofftimer, "Shelly_status", $hash,0);
return undef;
}