2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-02-25 22:15:09 +00:00

36_Shelly.pm:bug fix relay-types and additions

git-svn-id: https://svn.fhem.de/fhem/trunk@28106 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
Starkstrombastler 2023-10-30 03:29:06 +00:00
parent 164a6e8d9c
commit f0dbcba123

View File

@ -25,6 +25,13 @@
# #
######################################################################################## ########################################################################################
# 5.01 attr defchannel for single mode devices with multiple relays (eg Shelly4Pro)
# improved support for wall display
# Bug Fix: Shelly_onoff (causing delays)
# 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
package main; package main;
use strict; use strict;
@ -38,7 +45,7 @@ use vars qw{%attr %defs};
sub Log($$); sub Log($$);
#-- globals on start #-- globals on start
my $version = "5.00 26.10.2023"; my $version = "5.02 30.10.2023";
#-- support differtent readings-names for energy & power metering #-- support differtent readings-names for energy & power metering
my %mapping = ( my %mapping = (
@ -320,7 +327,7 @@ my %shelly_vendor_ids = (
"SNSW-001P8EU" => "shellyplus1pm", # Shelly Plus 1 PM Mini "SNSW-001P8EU" => "shellyplus1pm", # Shelly Plus 1 PM Mini
"SNPM-001PCEU16" => "shellypluspm", # Shelly Plus PM Mini "SNPM-001PCEU16" => "shellypluspm", # Shelly Plus PM Mini
# Misc # Misc
"SAWD1" => "walldisplay1" "SAWD-0A1XX10EU1" => "walldisplay1"
); );
@ -358,7 +365,7 @@ my %shelly_models = (
"shellyproem50" => [1,0,0, 1,1,0], # has two single-phase meter and one relay "shellyproem50" => [1,0,0, 1,1,0], # has two single-phase meter and one relay
"shellypro3em" => [0,0,0, 1,1,0], # has one (1) three-phase meter "shellypro3em" => [0,0,0, 1,1,0], # has one (1) three-phase meter
"shellyprodual" => [0,2,0, 4,1,4], "shellyprodual" => [0,2,0, 4,1,4],
"walldisplay1" => [1,0,0, 1,1,1] # similar to ShellyPlus1PM "walldisplay1" => [1,0,0, 0,1,1] # similar to ShellyPlus1PM
); );
my %shelly_events = ( # events, that can be used by webhooks; key is mode, value is shelly-event my %shelly_events = ( # events, that can be used by webhooks; key is mode, value is shelly-event
@ -577,6 +584,7 @@ my %si_units = (
"tempC" => [""," °C"], # Celsius "tempC" => [""," °C"], # Celsius
"relHumidity" => [""," %"], "relHumidity" => [""," %"],
"pct" => [""," %"], "pct" => [""," %"],
"illum" => [""," lux"], # illuminace eg WallDisplay
"ct" => [""," K"], # color temperature / Kelvin "ct" => [""," K"], # color temperature / Kelvin
"rssi" => [""," dB"] "rssi" => [""," dB"]
); );
@ -686,9 +694,9 @@ sub Shelly_get_model {
return; return;
} }
if( $hash && !$err && !$data ){ if( $hash && !$err && !$data ){
my $creds = Shelly_pwd($hash); # Log3 $hash->{NAME},2,$creds; my $creds = Shelly_pwd($hash);
#-- try to get type/model and profile/mode of Shelly - first gen #-- try to get type/model and profile/mode of Shelly - first gen
Log3 $name,5,"[Shelly_get_model] try to get model for device $name as first gen";
HttpUtils_NonblockingGet({ HttpUtils_NonblockingGet({
url => "http://$creds".$hash->{TCPIP}."/settings", url => "http://$creds".$hash->{TCPIP}."/settings",
timeout => 4, timeout => 4,
@ -696,6 +704,7 @@ sub Shelly_get_model {
}); });
$count++; $count++;
#-- try to get type/model and profile/mode of Shelly - second gen #-- try to get type/model and profile/mode of Shelly - second gen
Log3 $name,5,"[Shelly_get_model] try to get model for device $name as second gen";
HttpUtils_NonblockingGet({ HttpUtils_NonblockingGet({
url => "http://$creds".$hash->{TCPIP}."/rpc/Shelly.GetDeviceInfo", url => "http://$creds".$hash->{TCPIP}."/rpc/Shelly.GetDeviceInfo",
timeout => 4, timeout => 4,
@ -713,7 +722,8 @@ sub Shelly_get_model {
my ($json,$jhash); my ($json,$jhash);
if( $hash && $data ){ if( $hash && $data ){
Log3 $name,5,"[Shelly_get_model] $name: received ".length($data)." byte of data from device"; Log3 $name,4,"[Shelly_get_model] $name: received ".length($data)." byte of data from device";
Log3 $name,5,"[Shelly_get_model] $name: received data is: \n$data ";
if( $data eq "Not Found" ){ if( $data eq "Not Found" ){
# when checking out Shellies software generation, this is intentionally no error # when checking out Shellies software generation, this is intentionally no error
@ -803,7 +813,10 @@ sub Shelly_get_model {
if( defined($attr{$name}{model}) ){ if( defined($attr{$name}{model}) ){
my $model_old = $attr{$name}{model}; my $model_old = $attr{$name}{model};
if( AttrVal($name,"model",undef) ne $model ){ if( $model_old eq "generic" && $model ne "generic" ){
Log3 $name,2,"[Shelly_get_model] the model of device $name is already defined as generic, and will be redefined as \'$model\' ";
$attr{$hash->{NAME}}{model} = $model;
}else{
Log3 $name,2,"[Shelly_get_model] the model of device $name is already defined as \'$model_old\' "; Log3 $name,2,"[Shelly_get_model] the model of device $name is already defined as \'$model_old\' ";
readingsSingleUpdate($hash,"state","model already defined as \'$model_old\', might be $model",1); readingsSingleUpdate($hash,"state","model already defined as \'$model_old\', might be $model",1);
} }
@ -849,7 +862,7 @@ sub Shelly_get_model {
if( $shelly_models{$model}[3]==0 ){ #no metering, eg. shellyi3 if( $shelly_models{$model}[3]==0 ){ #no metering, eg. shellyi3
$AttrList =~ s/$attributes{'metering'}/""/e; $AttrList =~ s/$attributes{'metering'}/""/e;
if( $model eq "shellyuni" ){ if( $model eq "shellyuni" || $model =~ /walldisplay/ ){
$AttrList =~ s/$attributes{'showunits'}/" showunits:none,original"/e; # shellyuni measures voltage $AttrList =~ s/$attributes{'showunits'}/" showunits:none,original"/e; # shellyuni measures voltage
}else{ }else{
$AttrList =~ s/$attributes{'showunits'}/""/e; $AttrList =~ s/$attributes{'showunits'}/""/e;
@ -861,8 +874,8 @@ sub Shelly_get_model {
$AttrList =~ s/$attributes{'input'}/""/e; $AttrList =~ s/$attributes{'input'}/""/e;
} }
if( !$mode ){ if( !$mode && $shelly_models{$model}[0]<2 ){
# delete 'defchannel' from attribute list # delete 'defchannel' from attribute list for single-mode devices with less than 2 relays
$AttrList =~ s/\sdefchannel//; $AttrList =~ s/\sdefchannel//;
}elsif( ($mode ne "roller" && $shelly_models{$model}[0]>1) || #more than one relay }elsif( ($mode ne "roller" && $shelly_models{$model}[0]>1) || #more than one relay
($mode ne "relay" && $shelly_models{$model}[1]>1) || #more than one roller ($mode ne "relay" && $shelly_models{$model}[1]>1) || #more than one roller
@ -1584,6 +1597,12 @@ sub Shelly_Set ($@) {
}elsif( $model =~ /shelly(rgbw|bulb).*/ && $mode eq "color" ){ }elsif( $model =~ /shelly(rgbw|bulb).*/ && $mode eq "color" ){
$newkeys .= " ".join(" ", sort keys %setsrgbwc) ; $newkeys .= " ".join(" ", sort keys %setsrgbwc) ;
} }
if( $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//;
}
$msg = "unknown argument $cmd choose one of $newkeys"; $msg = "unknown argument $cmd choose one of $newkeys";
###Log3 "YY",5,"[Shelly_Set] $name model=$model: $msg"; ###Log3 "YY",5,"[Shelly_Set] $name model=$model: $msg";
return $msg; return $msg;
@ -1947,8 +1966,8 @@ sub Shelly_Set ($@) {
Log3 $name,4,"[Shelly_Set] switching channel $channel for device $name with command $cmd, FF=$ff"; Log3 $name,4,"[Shelly_Set] switching channel $channel for device $name with command $cmd, FF=$ff";
if( $ff==0 ){ if( $ff==0 ){
if( $shelly_models{$model}[4] == 0 ){ if( $shelly_models{$model}[4] < 2 ){
$cmd = "/relay/$channel?turn=$cmd"; $cmd = "?turn=$cmd"; ##"/relay/$channel?turn=$cmd";
}else{ }else{
$cmd =~ s/on/true/; $cmd =~ s/on/true/;
$cmd =~ s/off/false/; $cmd =~ s/off/false/;
@ -2454,7 +2473,7 @@ sub Shelly_status {
}else{ }else{
$next=Shelly_proc2G($hash,$comp,$jhash); $next=Shelly_proc2G($hash,$comp,$jhash);
} }
Log3 $name,5,"[Shelly_status] $name: proc.G returned next update in $next seconds "; Log3 $name,4,"[Shelly_status] $name: proc.G returned next update in $next seconds ($comp)";
#-- cyclic update (or update close to on/off-for-timer command) #-- cyclic update (or update close to on/off-for-timer command)
@ -2465,7 +2484,7 @@ sub Shelly_status {
} }
return undef if( $next == 125 ); return undef if( $next == 125 );
Log3 $name,3,"[Shelly_status] $name: next update in $next seconds "; Log3 $name,4,"[Shelly_status] $name: next update in $next seconds ";
RemoveInternalTimer($hash,"Shelly_status"); RemoveInternalTimer($hash,"Shelly_status");
InternalTimer(gettimeofday()+$next, "Shelly_status", $hash, 1) InternalTimer(gettimeofday()+$next, "Shelly_status", $hash, 1)
if( $hash->{INTERVAL} != 0 ); if( $hash->{INTERVAL} != 0 );
@ -2544,7 +2563,7 @@ sub Shelly_shelly {#Debug "aborting Shelly_shelly";return undef;
# adjust to get readings 2sec after full minute # adjust to get readings 2sec after full minute
$offset = $offset + 2 - gettimeofday() % 60; $offset = $offset + 2 - gettimeofday() % 60;
} }
Log3 $name,3,"[Shelly_shelly] $name: long update in $offset seconds, Timer is Shelly_shelly"; #4 Log3 $name,5,"[Shelly_shelly] $name: long update in $offset seconds, Timer is Shelly_shelly"; #4
InternalTimer(gettimeofday()+$offset, "Shelly_shelly", $hash, 1); InternalTimer(gettimeofday()+$offset, "Shelly_shelly", $hash, 1);
return undef; return undef;
} }
@ -2579,6 +2598,10 @@ sub Shelly_proc1G {
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
Shelly_readingsBulkUpdate($hash,"network","<html>connected to <a href=\"http://".$hash->{TCPIP}."\">".$hash->{TCPIP}."</a></html>",1); Shelly_readingsBulkUpdate($hash,"network","<html>connected to <a href=\"http://".$hash->{TCPIP}."\">".$hash->{TCPIP}."</a></html>",1);
readingsBulkUpdateMonitored($hash,"network_rssi",$jhash->{'wifi_sta'}{'rssi'} )
if( $jhash->{'wifi_sta'}{'rssi'} );
readingsBulkUpdateMonitored($hash,"network_ssid",$jhash->{'wifi_sta'}{'ssid'} )
if( $jhash->{'wifi_sta'}{'ssid'} );
#-- for all models set internal temperature reading and status #-- for all models set internal temperature reading and status
if ($jhash->{'temperature'}) { if ($jhash->{'temperature'}) {
@ -3021,13 +3044,14 @@ sub Shelly_proc2G {
$ison = $jhash->{'output'}; $ison = $jhash->{'output'};
$ison =~ s/0|(false)/off/; $ison =~ s/0|(false)/off/;
$ison =~ s/1|(true)/on/; $ison =~ s/1|(true)/on/;
readingsBulkUpdateMonitored($hash,"relay".$subs,$ison);#Debug "$name 2ndGen relay"; readingsBulkUpdateMonitored($hash,"relay".$subs,$ison);#Debug "$name 2ndGen relay >$ison\<";
# Switch Reason: Trigger for switching # Switch Reason: Trigger for switching
# --> init, http <-fhemWEB, WS_in, timer, loopback (=schedule?), HTTP <-Shelly-App # --> init, http <-fhemWEB, WS_in, timer, loopback (=schedule?), HTTP <-Shelly-App
readingsBulkUpdateMonitored($hash,"reason".$subs,$jhash->{'source'}); readingsBulkUpdateMonitored($hash,"reason".$subs,$jhash->{'source'});
readingsBulkUpdateMonitored($hash,"state",($shelly_models{$model}[0] == 1)?$ison:"OK"); readingsBulkUpdateMonitored($hash,"state",($shelly_models{$model}[0] == 1)?$ison:"OK");
############retrieving the roller state and position ############retrieving the roller state and position
}elsif( $rollers>0 && $comp eq "roller") { }elsif( $rollers>0 && $comp eq "roller") {
Log3 $name,4,"[Shelly_proc2G:roller] Processing roller state for device $name (we have $rollers rollers)"; Log3 $name,4,"[Shelly_proc2G:roller] Processing roller state for device $name (we have $rollers rollers)";
@ -3272,6 +3296,18 @@ if(0){ # check calculation of reactive power!
} }
readingsBulkUpdateMonitored($hash,"state","OK") if( $model eq "shellyplusi4" ); readingsBulkUpdateMonitored($hash,"state","OK") if( $model eq "shellyplusi4" );
} }
#********
if( $model =~ /walldisplay/ ){
$subs ="";
# readings supported by the /Shelly.GetStatus call or by individual calls eg /Temperature.GetStatus
readingsBulkUpdateMonitored($hash,"temperature" .$subs,$jhash->{'temperature:0'}{'tC'}.$si_units{tempC}[$hash->{units}] );
readingsBulkUpdateMonitored($hash,"humidity" .$subs,$jhash->{'humidity:0'}{'rh'}.$si_units{relHumidity}[$hash->{units}] );
readingsBulkUpdateMonitored($hash,"illuminance" .$subs,$jhash->{'illuminance:0'}{'lux'}.$si_units{illum}[$hash->{units}] );
readingsBulkUpdateMonitored($hash,"illumination".$subs,$jhash->{'illuminance:0'}{'illumination'} );
}
#********
################ Shelly.GetConfig ################ Shelly.GetConfig
}elsif ($comp eq "config"){ }elsif ($comp eq "config"){
Log3 $name,4,"[Shelly_proc2G:config] $name: processing the answer rpc/Shelly.GetConfig from Shelly_shelly()"; Log3 $name,4,"[Shelly_proc2G:config] $name: processing the answer rpc/Shelly.GetConfig from Shelly_shelly()";
@ -3380,6 +3416,8 @@ if(0){ # check calculation of reactive power!
readingsBulkUpdateMonitored($hash,"energy" .$subs,$energy); readingsBulkUpdateMonitored($hash,"energy" .$subs,$energy);
readingsBulkUpdateMonitored($hash,"energy_lastMinute".$subs,$minutes); readingsBulkUpdateMonitored($hash,"energy_lastMinute".$subs,$minutes);
readingsBulkUpdateMonitored($hash,"protection".$subs,$errors); readingsBulkUpdateMonitored($hash,"protection".$subs,$errors);
}
# temperature not provided by all devices # temperature not provided by all devices
if( defined($jhash->{'temperature'}{'tC'}) ){ if( defined($jhash->{'temperature'}{'tC'}) ){
readingsBulkUpdateMonitored($hash,"inttemp",$jhash->{'temperature'}{'tC'}.$si_units{tempC}[$hash->{units}]); readingsBulkUpdateMonitored($hash,"inttemp",$jhash->{'temperature'}{'tC'}.$si_units{tempC}[$hash->{units}]);
@ -3387,7 +3425,6 @@ if(0){ # check calculation of reactive power!
readingsBulkUpdateMonitored($hash,"inttemp",$jhash->{'temperature:0'}{'tC'}.$si_units{tempC}[$hash->{units}]); readingsBulkUpdateMonitored($hash,"inttemp",$jhash->{'temperature:0'}{'tC'}.$si_units{tempC}[$hash->{units}]);
} }
} }
}
readingsEndUpdate($hash,1); readingsEndUpdate($hash,1);
if ($comp eq "status" || $comp eq "config" || $comp eq "info"){ if ($comp eq "status" || $comp eq "config" || $comp eq "info"){
@ -3644,7 +3681,7 @@ if(0){ # calculate the suppliers meter value
my $dst = fhem('{$isdst}'); # is daylight saving time (Sommerzeit) ? gets a log entry {$isdst} at level 3 my $dst = fhem('{$isdst}'); # is daylight saving time (Sommerzeit) ? gets a log entry {$isdst} at level 3
foreach my $TP (@TPs) foreach my $TP (@TPs)
{ {
Log3 $name,3,"[Shelly__proc2G:status] calling shelly_delta_energy for period \'$TP\' "; Log3 $name,5,"[Shelly__proc2G:status] calling shelly_delta_energy for period \'$TP\' ";
shelly_delta_energy($hash,"Total_Energy_S_", $TP,$timestamp,$active_energy-$return_energy,$TimeStamp,$dst); shelly_delta_energy($hash,"Total_Energy_S_", $TP,$timestamp,$active_energy-$return_energy,$TimeStamp,$dst);
shelly_delta_energy($hash,"Purchased_Energy_S_",$TP,$timestamp,$active_energy,$TimeStamp,$dst); shelly_delta_energy($hash,"Purchased_Energy_S_",$TP,$timestamp,$active_energy,$TimeStamp,$dst);
shelly_delta_energy($hash,"Returned_Energy_S_", $TP,$timestamp,$return_energy,$TimeStamp,$dst); shelly_delta_energy($hash,"Returned_Energy_S_", $TP,$timestamp,$return_energy,$TimeStamp,$dst);
@ -3966,10 +4003,16 @@ sub Shelly_interval($){
my $creds = Shelly_pwd($hash); my $creds = Shelly_pwd($hash);
if ( $hash && !$err && !$data ){ if ( $hash && !$err && !$data ){
my $callback;
my $url = "http://$creds".$hash->{TCPIP}; my $url = "http://$creds".$hash->{TCPIP};
if(1){
$url .= "/relay/$channel$cmd";
$callback=$cmd;
}else{
$cmd =~ /(id=\d)/; $cmd =~ /(id=\d)/;
my $callback= $url."/rpc/Switch.GetStatus?$1"; $callback= $url."/rpc/Switch.GetStatus?$1";
$url .= $cmd; $url .= $cmd;
}
Log3 $name,4,"[Shelly_onoff] issue a non-blocking call to $url; callback to Shelly_onoff with command $callback"; Log3 $name,4,"[Shelly_onoff] issue a non-blocking call to $url; callback to Shelly_onoff with command $callback";
HttpUtils_NonblockingGet({ HttpUtils_NonblockingGet({
url => $url, url => $url,
@ -3998,7 +4041,7 @@ sub Shelly_interval($){
return; return;
} }
if($shelly_models{$model}[4] == 0){ ### if($shelly_models{$model}[4] < 2){
my $ison = $jhash->{'ison'}; my $ison = $jhash->{'ison'};
my $hastimer = $jhash->{'has_timer'}; my $hastimer = $jhash->{'has_timer'};
my $onofftimer = $jhash->{'timer_remaining'}; my $onofftimer = $jhash->{'timer_remaining'};
@ -4017,7 +4060,7 @@ sub Shelly_interval($){
if( defined($overpower) && $overpower eq "1") { if( defined($overpower) && $overpower eq "1") {
Log3 $name,1,"[Shelly_onoff] device $name switched off automatically because of overpower signal"; Log3 $name,1,"[Shelly_onoff] device $name switched off automatically because of overpower signal";
} }
}else{ if(0){### }else{
Log3 $name,4,"[Shelly_onoff] $name returns with command $cmd "; Log3 $name,4,"[Shelly_onoff] $name returns with command $cmd ";
# $jhash->{'was_on'}; #receiving this message means success # $jhash->{'was_on'}; #receiving this message means success
if( !$jhash ){ if( !$jhash ){
@ -4035,7 +4078,8 @@ sub Shelly_interval($){
#-- #--
my $subs = ($shelly_models{$model}[0] == 1) ? "" : "_".$channel; my $subs = ($shelly_models{$model}[0] == 1) ? "" : "_".$channel;
if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for testing if(0){ #my ($onofftimer,$source,$ison,$overpower); ##need this my... only for testing
Log3 $name,0,"$name $onofftimer $source ison:$ison\<";
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
readingsBulkUpdateMonitored($hash,"timer",$onofftimer); readingsBulkUpdateMonitored($hash,"timer",$onofftimer);
readingsBulkUpdateMonitored($hash,"source",$source); readingsBulkUpdateMonitored($hash,"source",$source);
@ -4050,7 +4094,7 @@ if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for t
readingsBulkUpdateMonitored($hash,"overpower".$subs,$overpower) 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); readingsEndUpdate($hash,1);
}elsif(0){ }else{
#-- Call status after switch. #-- Call status after switch.
RemoveInternalTimer($hash,"Shelly_status"); #Debug "$name 3882 removed Timer Shelly_status, now calling in 0.5 sec"; 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); InternalTimer(gettimeofday()+0.5, "Shelly_status", $hash,0);
@ -4089,9 +4133,9 @@ if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for t
Log3 $name,6,"[Shelly_webhook] was called for device $name, but without command (Create..., Update, Delete, List)"; Log3 $name,6,"[Shelly_webhook] was called for device $name, but without command (Create..., Update, Delete, List)";
$cmd = $hash->{CMD}; $cmd = $hash->{CMD};
return if( !$cmd); return if( !$cmd);
Log3 $name,3,"[Shelly_webhook] proceeding with command \'$cmd\' for device $name"; #Log3 $name,3,"[Shelly_webhook] Proceeding with command \'$cmd\' for device $name";
} }
Log3 $name,3,"[Shelly_webhook] proceeding with command \'$cmd\' for device $name"; Log3 $name,5,"[Shelly_webhook] proceeding with command \'$cmd\' for device $name";
my $model = AttrVal($name,"model","generic"); my $model = AttrVal($name,"model","generic");
my $gen = $shelly_models{$model}[4]; # 0 is Gen1, 1 is Gen2 my $gen = $shelly_models{$model}[4]; # 0 is Gen1, 1 is Gen2
@ -4099,7 +4143,7 @@ if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for t
my $timeout = AttrVal($name,"timeout",4); my $timeout = AttrVal($name,"timeout",4);
my $mode = AttrVal($name,"mode","relay"); # we need mode=relay for Shelly*1-devices my $mode = AttrVal($name,"mode","relay"); # we need mode=relay for Shelly*1-devices
Log3 $name,4,"[Shelly_webhook] device $name was called with command=$cmd"; Log3 $name,5,"[Shelly_webhook] device $name was called with command=$cmd";
# Calling as callable program: check if err and data are empty # Calling as callable program: check if err and data are empty
if ( $hash && !$err && !$data ){ if ( $hash && !$err && !$data ){
@ -4124,7 +4168,7 @@ if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for t
#---------CHECK, UPDATE, DELETE Webhooks------------- #---------CHECK, UPDATE, DELETE Webhooks-------------
if( $cmd ne "Create" ){ #Check, Count, Update, Delete if( $cmd ne "Create" ){ #Check, Count, Update, Delete
Log3 $name,4,"[Shelly_webhook] issue a non-blocking call to $URL, callback with command \'$cmd\' "; Log3 $name,5,"[Shelly_webhook] issue a non-blocking call to $URL, callback with command \'$cmd\' ";
HttpUtils_NonblockingGet({ HttpUtils_NonblockingGet({
url => $URL, url => $URL,
timeout => $timeout, timeout => $timeout,
@ -4143,7 +4187,7 @@ if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for t
my ($component,$element); my ($component,$element);
foreach $component ( $mode, "input", "emeter" ){ foreach $component ( $mode, "input", "emeter" ){
Log3 $name,4,"[Shelly_webhook] $name: check number of components for component=$component"; Log3 $name,5,"[Shelly_webhook] $name: check number of components for component=$component";
if( $component eq "relay" ){ if( $component eq "relay" ){
$compCount = $shelly_models{$model}[0]; $compCount = $shelly_models{$model}[0];
}elsif( $component eq "roller" ){ }elsif( $component eq "roller" ){
@ -4159,10 +4203,10 @@ if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for t
}else{ }else{
$compCount = 0 ; $compCount = 0 ;
} }
Log3 $name,4,"[Shelly_webhook] $name: the number of \'$component\' is compCount=$compCount"; Log3 $name,5,"[Shelly_webhook] $name: the number of \'$component\' is compCount=$compCount";
for( my $c=0;$c<$compCount;$c++ ){ for( my $c=0;$c<$compCount;$c++ ){
Log3 $name,4,"[Shelly_webhook] $name: processing component $c of $component"; Log3 $name,5,"[Shelly_webhook] $name: processing component $c of $component";
if( $component eq "input"){ if( $component eq "input"){
my $subs= $compCount>1 ? "_$c\_mode" : "_mode" ; my $subs= $compCount>1 ? "_$c\_mode" : "_mode" ;
$element = ReadingsVal($name,"input$subs","none"); #input type: switch or button $element = ReadingsVal($name,"input$subs","none"); #input type: switch or button
@ -4170,7 +4214,7 @@ if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for t
Log3 $name,3,"[Shelly_webhook] $name: Error: Reading \'input.*mode\' not found"; Log3 $name,3,"[Shelly_webhook] $name: Error: Reading \'input.*mode\' not found";
return; return;
} }
Log3 $name,4,"[Shelly_webhook] $name: input mode no $c is \'$element\' "; Log3 $name,5,"[Shelly_webhook] $name: input mode no $c is \'$element\' ";
$element =~ /(\S+?)\s.*/; # get only characters from first string of reading $element =~ /(\S+?)\s.*/; # get only characters from first string of reading
$element = $1; $element = $1;
}elsif( $component eq "emeter"){ }elsif( $component eq "emeter"){
@ -4181,7 +4225,7 @@ if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for t
$element = $component; $element = $component;
} }
$eventsCount = $#{$shelly_events{$element}}; # $#{...} gives highest index of array $eventsCount = $#{$shelly_events{$element}}; # $#{...} gives highest index of array
Log3 $name,4,"[Shelly_webhook] $name: processing evtsCnt=$eventsCount events for \'$element\'"; Log3 $name,5,"[Shelly_webhook] $name: processing evtsCnt=$eventsCount events for \'$element\'";
for( my $e=0; $e<=$eventsCount; $e++ ){ for( my $e=0; $e<=$eventsCount; $e++ ){
if( $cmd eq "Create" ){ if( $cmd eq "Create" ){
@ -4205,8 +4249,8 @@ if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for t
$URL =~ s/enable\=true/enable\=false/ if( $e > 0 ); $URL =~ s/enable\=true/enable\=false/ if( $e > 0 );
} }
} }
Log3 $name,4,"[Shelly_webhook] $name $cmd component=$element count=$c event=$e"; Log3 $name,5,"[Shelly_webhook] $name $cmd component=$element count=$c event=$e";
Log3 $name,4,"[Shelly_webhook] issue a non-blocking call to \n$URL$urls"; Log3 $name,5,"[Shelly_webhook] issue a non-blocking call to \n$URL$urls";
HttpUtils_NonblockingGet({ HttpUtils_NonblockingGet({
url => $URL.$urls, url => $URL.$urls,
timeout => $timeout, timeout => $timeout,
@ -4240,7 +4284,7 @@ if(0){ my ($onofftimer,$source,$ison,$overpower); ##need this my... only for t
my $hooksCount = ( defined($jhash->{'hooks'}) ? @{$jhash->{'hooks'}} : ReadingsVal($name,"webhook_cnt",0) ); my $hooksCount = ( defined($jhash->{'hooks'}) ? @{$jhash->{'hooks'}} : ReadingsVal($name,"webhook_cnt",0) );
#processing the webhook data for the different commands #processing the webhook data for the different commands
Log3 $name,4,"[Shelly_webhook] processing the webhook data for command=$cmd"; Log3 $name,5,"[Shelly_webhook] processing the webhook data for command=$cmd";
#---------------------Read number of webhooks from 'List' #---------------------Read number of webhooks from 'List'
if( $cmd eq "Count" ){ if( $cmd eq "Count" ){
@ -4256,7 +4300,7 @@ if(1){ # first of all, get number of webhooks
#return if ($hooksCount == 0); #return if ($hooksCount == 0);
$hooksCount = 0; $hooksCount = 0;
} }
Log3 $name,4,"[Shelly_webhook] our counter says $hooksCount webhooks on shelly"; Log3 $name,5,"[Shelly_webhook] our counter says $hooksCount webhooks on shelly";
} }
# parsing all webhooks and check for csrf-token # parsing all webhooks and check for csrf-token
@ -4266,8 +4310,8 @@ if(1){ # first of all, get number of webhooks
my ($current_url_part1, $current_url_command); my ($current_url_part1, $current_url_command);
my $urls; # the new urls string my $urls; # the new urls string
# are there webhooks on the shelly we don't know about?
if( !AttrVal($name,"webhook",undef) || AttrVal($name,"webhook",undef) eq "none" ){ if( (!AttrVal($name,"webhook",undef) || AttrVal($name,"webhook",undef) eq "none" ) && $jhash->{'hooks'}[0]{'urls'}[0] ){
$current_url = $jhash->{'hooks'}[0]{'urls'}[0]; # get the first webhook url from shelly $current_url = $jhash->{'hooks'}[0]{'urls'}[0]; # get the first webhook url from shelly
$current_url =~ m/.*:([0-9]*)\/.*/; $current_url =~ m/.*:([0-9]*)\/.*/;
my $fhemwebport = $1; my $fhemwebport = $1;
@ -4277,7 +4321,7 @@ if(1){ # first of all, get number of webhooks
my ($webhook_url_pre, $webhook_url_part1, $webhook_url_command,$webhook_url_post) = Shelly_webhookurl($hash,"",0,0,0); # a 'status' call my ($webhook_url_pre, $webhook_url_part1, $webhook_url_command,$webhook_url_post) = Shelly_webhookurl($hash,"",0,0,0); # a 'status' call
Log3 $name,4,"[Shelly_webhook] $name: webhook shall be: pre=$webhook_url_pre, part1=$webhook_url_part1, command=$webhook_url_command, post=$webhook_url_post"; Log3 $name,5,"[Shelly_webhook] $name: webhook shall be: pre=$webhook_url_pre, part1=$webhook_url_part1, command=$webhook_url_command, post=$webhook_url_post";
# preparing the calling-url, we need this when looping # preparing the calling-url, we need this when looping
my $URL = "http://$creds".$hash->{TCPIP}."/rpc/Webhook.Update?"; my $URL = "http://$creds".$hash->{TCPIP}."/rpc/Webhook.Update?";
@ -4285,12 +4329,12 @@ if(1){ # first of all, get number of webhooks
my $host_ip = qx("\"hostname -I\""); # local my $host_ip = qx("\"hostname -I\""); # local
$host_ip =~ m/(\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?).*/; #extract ipv4-address $host_ip =~ m/(\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?).*/; #extract ipv4-address
$host_ip = $1; $host_ip = $1;
Log3 $name,4,"[Shelly_webhook] looking for webhooks forwarding to $host_ip"; Log3 $name,5,"[Shelly_webhook] looking for webhooks forwarding to $host_ip";
for (my $i=0;$i<$hooksCount;$i++){ for (my $i=0;$i<$hooksCount;$i++){
$current_url = $jhash->{'hooks'}[$i]{'urls'}[0]; # get the url from shelly $current_url = $jhash->{'hooks'}[$i]{'urls'}[0]; # get the url from shelly
$current_url =~ s/\%/\%25/g; # to make it compareable to webhook_url $current_url =~ s/\%/\%25/g; # to make it compareable to webhook_url
Log3 $name,4,"[Shelly_webhook] shellies $name webhook $i is $current_url"; Log3 $name,5,"[Shelly_webhook] shellies $name webhook $i is $current_url";
if($current_url !~ /$host_ip/ ){ #skip this hook when refering to another host if($current_url !~ /$host_ip/ ){ #skip this hook when refering to another host
Log3 $name,5,"[Shelly_webhook] shellies $name webhook $i is refering to another host $current_url -> skipping"; Log3 $name,5,"[Shelly_webhook] shellies $name webhook $i is refering to another host $current_url -> skipping";
next; next;
@ -4311,17 +4355,17 @@ if(1){ # first of all, get number of webhooks
Log3 $name,5,"[Shelly_webhook] $i checking part1 of url: \n$current_url_part1 against\n$webhook_url_part1\n command is: \"$current_url_command\" "; Log3 $name,5,"[Shelly_webhook] $i checking part1 of url: \n$current_url_part1 against\n$webhook_url_part1\n command is: \"$current_url_command\" ";
if ($current_url_part1 ne $webhook_url_part1 ){ if ($current_url_part1 ne $webhook_url_part1 ){
Log3 $name,4,"[Shelly_webhook] $i we have to update $current_url_part1 to $webhook_url_part1"; Log3 $name,5,"[Shelly_webhook] $i we have to update $current_url_part1 to $webhook_url_part1";
$urls = "urls=[%22$webhook_url_part1$current_url_command%22]&"; # %22 = " $urls = "urls=[%22$webhook_url_part1$current_url_command%22]&"; # %22 = "
$urls = $URL.$urls."id=$current_id"; # will be issued $urls = $URL.$urls."id=$current_id"; # will be issued
Log3 $name,4,"[Shelly_webhook] $name: $i issue a non-blocking call with callback-command \"Updated\":\n$urls"; Log3 $name,5,"[Shelly_webhook] $name: $i issue a non-blocking call with callback-command \"Updated\":\n$urls";
HttpUtils_NonblockingGet({ HttpUtils_NonblockingGet({
url => $urls, url => $urls,
timeout => $timeout, timeout => $timeout,
callback => sub($$$){ Shelly_webhook($hash,"Updated",$_[1],$_[2]) } callback => sub($$$){ Shelly_webhook($hash,"Updated",$_[1],$_[2]) }
}); });
}else{ }else{
Log3 $name,4,"[Shelly_webhook] $i check is OK, nothing to do"; Log3 $name,5,"[Shelly_webhook] $i check is OK, nothing to do";
} }
} }
@ -4336,9 +4380,9 @@ if(1){ # first of all, get number of webhooks
$h_id = $jhash->{'hooks'}[$i]{'id'}; $h_id = $jhash->{'hooks'}[$i]{'id'};
$h_urls0 = $jhash->{'hooks'}[$i]{'urls'}[0]; # we need this, when checking for forward ip-adress $h_urls0 = $jhash->{'hooks'}[$i]{'urls'}[0]; # we need this, when checking for forward ip-adress
Log3 $name,4,"[Shelly_webhook] ++++ $name: checking webhook id=$h_id $h_name for deleting+++++++++++"; Log3 $name,5,"[Shelly_webhook] ++++ $name: checking webhook id=$h_id $h_name for deleting+++++++++++";
if( $h_name =~ /^_/ ){ if( $h_name =~ /^_/ ){
Log3 $name,4,"[Shelly_webhook] ++++ $name: deleting webhook $h_name +++++++++++"; Log3 $name,5,"[Shelly_webhook] ++++ $name: deleting webhook $h_name +++++++++++";
my $url_ = "http://$creds".$hash->{TCPIP}."/rpc/Webhook.Delete?id=$h_id"; my $url_ = "http://$creds".$hash->{TCPIP}."/rpc/Webhook.Delete?id=$h_id";
Log3 $name,5,"[Shelly_webhook] url: $url_"; Log3 $name,5,"[Shelly_webhook] url: $url_";
HttpUtils_NonblockingGet({ HttpUtils_NonblockingGet({
@ -4365,7 +4409,7 @@ if(1){ # first of all, get number of webhooks
#retrieving webhook id #retrieving webhook id
my $webhook_id = $jhash->{'id'}; my $webhook_id = $jhash->{'id'};
$hooksCount++; $hooksCount++;
Log3 $name,4,"[Shelly_webhook] $name hook created with id=".($webhook_id?$webhook_id:"").($webhook_v?", version is $webhook_v":""); Log3 $name,5,"[Shelly_webhook] $name hook created with id=".($webhook_id?$webhook_id:"").($webhook_v?", version is $webhook_v":"");
}else{ }else{
my $error = "Error ".$jhash->{'code'}." occured: ".$jhash->{'message'}; my $error = "Error ".$jhash->{'code'}." occured: ".$jhash->{'message'};
Log3 $name,1,"[Shelly_webhook] $name: $error" if $error; Log3 $name,1,"[Shelly_webhook] $name: $error" if $error;
@ -4375,7 +4419,7 @@ if(1){ # first of all, get number of webhooks
readingsBulkUpdateMonitored($hash,"webhook_ver",$webhook_v); readingsBulkUpdateMonitored($hash,"webhook_ver",$webhook_v);
readingsBulkUpdateMonitored($hash,"webhook_cnt",$hooksCount); readingsBulkUpdateMonitored($hash,"webhook_cnt",$hooksCount);
readingsEndUpdate($hash,1); readingsEndUpdate($hash,1);
Log3 $name,4,"[Shelly_webhook] shelly $name has $hooksCount webhooks".($webhook_v?", latest rev is $webhook_v":""); # No of hooks in Shelly-device Log3 $name,5,"[Shelly_webhook] shelly $name has $hooksCount webhooks".($webhook_v?", latest rev is $webhook_v":""); # No of hooks in Shelly-device
return undef; return undef;
} }
@ -4404,7 +4448,7 @@ sub Shelly_webhookurl ($@) {
my $channel = shift @a; my $channel = shift @a;
my $noe = shift @a; # number of event of entity my $noe = shift @a; # number of event of entity
my $V = 4; my $V = 5;
Log3 $name,$V,"[Shelly_webhookurl] calling url-builder with args: $name $cmd $entity ch:$channel noe:$noe"; Log3 $name,$V,"[Shelly_webhookurl] calling url-builder with args: $name $cmd $entity ch:$channel noe:$noe";
my $host_ip = qx("\"hostname -I\""); # local my $host_ip = qx("\"hostname -I\""); # local
@ -4522,9 +4566,9 @@ sub Shelly_error_handling {
}else{ }else{
$err = "Error"; $err = "Error";
} }
Log3 $name,1,"[Shelly_error_handling] Device $name has Error \'$err\' ";
readingsBulkUpdate($hash,"network_disconnects",ReadingsNum($name,"network_disconnects",0)+1) if( ReadingsVal($name,"state","") ne $err ); readingsBulkUpdate($hash,"network_disconnects",ReadingsNum($name,"network_disconnects",0)+1) if( ReadingsVal($name,"state","") ne $err );
readingsBulkUpdateMonitored($hash,"state",$err,1); readingsBulkUpdateMonitored($hash,"state",$err, 1 );
readingsEndUpdate($hash,1); readingsEndUpdate($hash,1);
return undef; return undef;
} }
@ -4552,6 +4596,7 @@ readingsBulkUpdateMonitored($$$@) # derived from fhem.pl readingsBulkUpdateIfCha
my ($hash,$reading,$value,$changed)= @_; my ($hash,$reading,$value,$changed)= @_;
my $MaxAge=AttrVal($hash->{NAME},"maxAge",21600); # default 6h my $MaxAge=AttrVal($hash->{NAME},"maxAge",21600); # default 6h
if( ReadingsAge($hash->{NAME},$reading,$MaxAge)>=$MaxAge || $value ne ReadingsVal($hash->{NAME},$reading,"") ){ if( ReadingsAge($hash->{NAME},$reading,$MaxAge)>=$MaxAge || $value ne ReadingsVal($hash->{NAME},$reading,"") ){
Log3 $hash->{NAME},6,"$reading: maxAge=$MaxAge ReadingsAge=".ReadingsAge($hash->{NAME},$reading,0)." value=$value ? ".ReadingsVal($hash->{NAME},$reading,"");
return readingsBulkUpdate($hash,$reading,$value,$changed); return readingsBulkUpdate($hash,$reading,$value,$changed);
}else{ }else{
return undef; return undef;