From bec4bcb0bdf27638b68d7eeb5ae1a0607200f0e0 Mon Sep 17 00:00:00 2001 From: phenning Date: Sat, 16 Jan 2021 11:37:41 +0000 Subject: [PATCH] =?UTF-8?q?36=5FShelly.pm:=20Version=203.0.=20Shellybulb/D?= =?UTF-8?q?uo,=20Timeout=20f=C3=BCr=20Netz=20sowie=20Zusammenarbeit=20mit?= =?UTF-8?q?=2036=5FShellyMonitor.pm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.fhem.de/fhem/trunk@23525 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/36_Shelly.pm | 268 +++++++++++++++++++++++++++++++---------- 1 file changed, 204 insertions(+), 64 deletions(-) diff --git a/fhem/FHEM/36_Shelly.pm b/fhem/FHEM/36_Shelly.pm index 499b5aa84..0ebfa561b 100644 --- a/fhem/FHEM/36_Shelly.pm +++ b/fhem/FHEM/36_Shelly.pm @@ -39,7 +39,7 @@ use vars qw{%attr %defs}; sub Log($$); #-- globals on start -my $version = "2.20"; +my $version = "3.0"; #-- these we may get on request my %gets = ( @@ -82,6 +82,18 @@ my %setsrgbww = ( "pct" => "B", ); +my %setsbulbw = ( + "on" => "O", + "off" => "F", + "toggle" => "T", + "on-for-timer" => "X", + "off-for-timer" => "E", + "config" => "K", + "password" => "P", + "ct:colorpicker,CT,3000,10,6500" => "R", + "pct:slider,0,1,100" => "W" +); + my %setsrgbwc = ( "on" => "O", "off" => "F", @@ -98,6 +110,7 @@ my %setsrgbwc = ( my %shelly_models = ( #(relays,rollers,dimmers,meters) + "generic" => [0,0,0,0], "shelly1" => [1,0,0,0], "shelly1pm" => [1,0,0,1], "shelly2" => [2,1,0,1], @@ -106,7 +119,8 @@ my %shelly_models = ( "shelly4" => [4,0,0,4], "shellyrgbw" => [0,0,4,1], "shellydimmer" => [0,0,1,1], - "shellyem" => [1,0,0,2] + "shellyem" => [1,0,0,2], + "shellybulb" => [0,0,1,1] ); my %shelly_regs = ( @@ -138,7 +152,7 @@ sub Shelly_Initialize ($) { $hash->{SetFn} = "Shelly_Set"; $hash->{RenameFn} = "Shelly_Rename"; - $hash->{AttrList}= "verbose model:".join(",",(keys %shelly_models))." mode:relay,roller,white,color defchannel maxtime maxpower interval pct100:open,closed shellyuser ". + $hash->{AttrList}= "verbose model:".join(",",(keys %shelly_models))." mode:relay,roller,white,color defchannel maxtime maxpower interval timeout pct100:open,closed shellyuser ". $readingFnAttributes; } @@ -171,6 +185,7 @@ sub Shelly_Define($$) { #-- InternalTimer blocks if init_done is not true my $oid = $init_done; $init_done = 1; + readingsBeginUpdate($hash); my $err = Shelly_status($hash); if( !defined($err) ){ @@ -182,6 +197,9 @@ sub Shelly_Define($$) { } readingsEndUpdate($hash,1); + #-- get settings + $err = Shelly_configure($hash,"settings/"); + #-- perform status update in a minute or so InternalTimer(time()+60, "Shelly_status", $hash,0); @@ -255,7 +273,7 @@ sub Shelly_Attr(@) { my $hash = $main::defs{$name}; my $ret; - my $model = AttrVal($name,"model",""); + my $model = AttrVal($name,"model","generic"); my $mode = AttrVal($name,"mode",""); #-- temporary code @@ -318,7 +336,7 @@ sub Shelly_Attr(@) { if( $model =~ /shelly2.*/ ){ $new = $pre." mode:relay,roller ".$pos; - }elsif( $model eq "shellyrgbw" ){ + }elsif( $model =~ /shelly(rgbw|bulb)/){ $new = $pre." mode:white,color ".$pos; }elsif( $model =~ /shelly.*/){ $new = $pre." ".$pos; @@ -327,8 +345,8 @@ sub Shelly_Attr(@) { #--------------------------------------- }elsif ( ($cmd eq "set") && ($attrName =~ /mode/) ) { - if( $model !~ /shelly(2|(rgb)).*/ ){ - Log3 $name,1,"[Shelly_Attr] setting the mode attribute only works for model=shelly2|shelly2.5|shellyrgbw"; + if( $model !~ /shelly(2|(rgb|bulb)).*/ ){ + Log3 $name,1,"[Shelly_Attr] setting the mode attribute only works for model=shelly2|shelly2.5|shellyrgbw|shellybulb"; return } if( $model =~ /shelly2.*/ ){ @@ -350,7 +368,7 @@ sub Shelly_Attr(@) { fhem("deletereading ".$name." power.*"); fhem("deletereading ".$name." energy.*"); fhem("deletereading ".$name." overpower.*"); - }elsif( $model eq "shellyrgbw" ){ + }elsif( $model eq "shellyrgbw" || $model eq "shellybulb" ){ fhem("deletereading ".$name." power.*"); fhem("deletereading ".$name." energy.*"); fhem("deletereading ".$name." overpower.*"); @@ -359,6 +377,7 @@ sub Shelly_Attr(@) { return; }elsif( $attrVal eq "color"){ fhem("deletereading ".$name." pct.*"); + fhem("deletereading ".$name." ct.*"); fhem("deletereading ".$name." state_.*"); }elsif( $attrVal eq "white"){ fhem("deletereading ".$name." L-.*"); @@ -411,7 +430,7 @@ sub Shelly_Get ($@) { my $name = $hash->{NAME}; my $v; - my $model = AttrVal($name,"model",""); + my $model = AttrVal($name,"model","generic"); my $mode = AttrVal($name,"mode",""); #-- get version @@ -429,9 +448,9 @@ sub Shelly_Get ($@) { $txt = "roller"; }elsif( $model eq "shellydimmer" ){ $txt = "light"; - }elsif( ($model eq "shellyrgbw") && ($mode eq "white") ){ + }elsif( ($model eq "shellyrgbw" || $model eq "shellybulb") && ($mode eq "white") ){ $txt = "white"; - }elsif( ($model eq "shellyrgbw") && ($mode eq "color") ){ + }elsif( ($model eq "shellyrgbw" || $model eq "shellybulb") && ($mode eq "color") ){ $txt = "color"; }else{ $txt = "relay"; @@ -446,6 +465,9 @@ sub Shelly_Get ($@) { $chan = $a[3]; }elsif( int(@a) == 3 ){ $chan = 0; + }elsif( int(@a) == 2 ){ + $reg = ""; + $chan = undef; }else{ my $msg = "Error: wrong number of parameters"; Log3 $name,1,"[Shelly_Get] ".$msg; @@ -453,16 +475,18 @@ sub Shelly_Get ($@) { } my $pre = "settings/"; - if( ($model =~ /shelly2.*/) && ($mode eq "roller") ){ - $pre .= "roller/0?"; - }elsif( $model eq "shellydimmer" ){ - $pre .= "light/0?"; - }elsif( ($model eq "shellyrgbw") && ($mode eq "white") ){ - $pre .= "white/0?"; - }elsif( ($model eq "shellyrgbw") && ($mode eq "color") ){ - $pre .= "color/0?"; - }else{ - $pre .= "relay/$chan?"; + if( $reg ne "" ){ + if( ($model =~ /shelly2.*/) && ($mode eq "roller") ){ + $pre .= "roller/0?"; + }elsif( $model eq "shellydimmer" ){ + $pre .= "light/0?"; + }elsif( ($model eq "shellyrgbw" || $model eq "shellybulb") && ($mode eq "white") ){ + $pre .= "white/0?"; + }elsif( ($model eq "shellyrgbw" || $model eq "shellybulb") && ($mode eq "color") ){ + $pre .= "color/0?"; + }elsif( defined($chan)){ + $pre .= "relay/$chan?"; + } } $v = Shelly_configure($hash,$pre.$reg); @@ -531,9 +555,9 @@ sub Shelly_Set ($@) { my $pre = "settings/"; if( ($model =~ /shelly2.*/) && ($mode eq "roller") ){ $pre .= "roller/0?"; - }elsif( ($model eq "shellyrgbw") && ($mode eq "white") ){ + }elsif( ($model eq "shellyrgbw" || $model eq "shellybulb") && ($mode eq "white") ){ $pre .= "white/0?"; - }elsif( ($model eq "shellyrgbw") && ($mode eq "color") ){ + }elsif( ($model eq "shellyrgbw" || $model eq "shellybulb") && ($mode eq "color") ){ $pre .= "color/0?"; }else{ $pre .= "relay/$chan?"; @@ -757,8 +781,73 @@ sub Shelly_Set ($@) { Shelly_dim($hash,"white/$channel","?brightness=".$value); } } + #-- we have a Shelly dimmer type device or rgbw type device in white mode + }elsif( ($model =~ /shellybulb.*/) && ($mode eq "white") ){ + if( $cmd eq "?" ) { + $newkeys = join(" ", sort keys %setsbulbw); + return "[Shelly_Set] Unknown argument " . $cmd . ", choose one of ".$newkeys; + } + + if( $cmd =~ /^((on)|(off)|(toggle)).*/ ){ + $channel = $value; + if( $cmd =~ /(.*)-for-timer/ ){ + $time = $value; + $channel = shift @a; + } + if( $shelly_models{$model}[2] == 1){ + $channel = 0 + }elsif( !defined($channel) || ($channel !~ /[0123]/) || $channel >= $shelly_models{$model}[3] ){ + if( !defined($channel) ){ + $channel = AttrVal($name,"defchannel",undef); + if( !defined($channel) ){ + $msg = "Error: wrong channel $channel given and defchannel attribute not set properly"; + Log3 $name, 1,"[Shelly_Set] ".$msg; + return $msg; + }else{ + Log3 $name, 4,"[Shelly_Set] switching default channel $channel"; + } + } + } + if( $cmd =~ /(.*)-for-timer/ ){ + $cmd = $1; + if( $time !~ /\d+/ ){ + $msg = "Error: wrong time spec $time, must be "; + Log3 $name, 1,"[Shelly_Set] ".$msg; + return $msg; + } + $cmd = $cmd."&timer=$time"; + } + if( $cmd eq "toggle"){ + $cmd = (ReadingsVal($name,"state","off") eq "on") ? "off" : "on"; + } + Shelly_dim($hash,"light/$channel","?turn=".$cmd); + #Shelly_onoff($hash,"white/$channel","?turn=".$cmd); + #-- command received via web to register local changes of the device input + + }elsif( $cmd eq "pct" || $cmd eq "brightness" ){ + $channel = shift @a; + if( $shelly_models{$model}[2] == 1){ + $channel = 0 + }elsif( !defined($channel) || ($channel !~ /[0123]/) || $channel >= $shelly_models{$model}[3] ){ + if( !defined($channel) ){ + $channel = AttrVal($name,"defchannel",undef); + if( !defined($channel) ){ + $msg = "Error: wrong channel $channel given and defchannel attribute not set properly"; + Log3 $name, 1,"[Shelly_Set] ".$msg; + return $msg; + }else{ + Log3 $name, 4,"[Shelly_Set] dimming default channel $channel"; + } + } + } + Shelly_dim($hash,"light/$channel","?brightness=".$value); + }elsif( $cmd eq "ct" ){ + $channel = shift @a; + Shelly_dim($hash,"light/0","?temp=".$value); + } + #-- we have a Shelly rgbw type device in color mode - }elsif( ($model =~ /shellyrgbw.*/) && ($mode eq "color")){ + }elsif( ($model =~ /shelly(rgbw|bulb).*/) && ($mode eq "color")){ my $channel = $value; #-- WEB asking for command list if( $cmd eq "?" ) { @@ -794,12 +883,14 @@ sub Shelly_Set ($@) { } my ($red,$green,$blue)=Color::hsv2rgb($hue,$saturation,$value); $cmd=sprintf("red=%d&green=%d&blue=%d",int($red*255+0.5),int($green*255+0.5),int($blue*255+0.5)); + $cmd .= "&gain=100" if ($model eq "shellybulb"); Shelly_dim($hash,"color/0","?".$cmd); }elsif( $cmd eq "rgb" ){ my $red=hex(substr($value,0,2)); my $green=hex(substr($value,2,2)); my $blue=hex(substr($value,4,2)); $cmd=sprintf("red=%d&green=%d&blue=%d",$red,$green,$blue); + $cmd .= "&gain=100" if ($model eq "shellybulb"); Shelly_dim($hash,"color/0","?".$cmd); }elsif( $cmd eq "rgbw" ){ my $red=hex(substr($value,0,2)); @@ -807,6 +898,7 @@ sub Shelly_Set ($@) { my $blue=hex(substr($value,4,2)); my $white=hex(substr($value,6,2)); $cmd=sprintf("red=%d&green=%d&blue=%d&white=%d",$red,$green,$blue,$white); + $cmd .= "&gain=100" if ($model eq "shellybulb"); Shelly_dim($hash,"color/0","?".$cmd); }elsif( $cmd eq "white" ){ $cmd=sprintf("white=%d",$value); @@ -837,7 +929,7 @@ sub Shelly_pwd($){ ######################################################################################## # -# Shelly_configure - Configure Shelly device +# Shelly_configure - Configure Shelly device or read general configuration # acts as callable program Shelly_configure($hash,$channel,$cmd) # and as callback program Shelly_configure($hash,$channel,$cmd,$err,$data) # @@ -848,21 +940,22 @@ sub Shelly_pwd($){ sub Shelly_configure { my ($hash, $cmd, $err, $data) = @_; my $name = $hash->{NAME}; - my $url; my $state = $hash->{READINGS}{state}{VAL}; my $net = $hash->{READINGS}{network}{VAL}; return if( $net !~ /connected/ ); - my $model = AttrVal($name,"model",""); + my $model = AttrVal($name,"model","generic"); my $creds = Shelly_pwd($hash); if ( $hash && !$err && !$data ){ - $url = "http://$creds".$hash->{TCPIP}."/".$cmd; + my $url = "http://$creds".$hash->{TCPIP}."/".$cmd; + my $timeout = AttrVal($name,"timeout",4); Log3 $name, 5,"[Shelly_configure] Issue a non-blocking call to $url"; HttpUtils_NonblockingGet({ url => $url, - callback=>sub($$$){ Shelly_configure($hash,$cmd,$_[1],$_[2]) } + timeout => $timeout, + callback => sub($$$){ Shelly_configure($hash,$cmd,$_[1],$_[2]) } }); return undef; }elsif ( $hash && $err ){ @@ -880,6 +973,12 @@ sub Shelly_pwd($){ return; } + #-- if settings command, we obtain only basic settings + if( $cmd eq "settings/" ){ + $hash->{SHELLYID} = $jhash->{'device'}{'hostname'}; + return + } + #-- isolate register name my $reg = substr($cmd,index($cmd,"?")+1); my $chan= substr($cmd,index($cmd,"?")-1,1); @@ -907,17 +1006,19 @@ sub Shelly_pwd($){ sub Shelly_status { my ($hash, $err, $data) = @_; my $name = $hash->{NAME}; - my $url; my $state = $hash->{READINGS}{state}{VAL}; + my $model = AttrVal($name,"model","generic"); my $creds = Shelly_pwd($hash); if ( $hash && !$err && !$data ){ - $url = "http://$creds".$hash->{TCPIP}."/status"; + my $url = "http://$creds".$hash->{TCPIP}."/status"; + my $timeout = AttrVal($name,"timeout",4); Log3 $name, 5,"[Shelly_status] Issue a non-blocking call to $url"; HttpUtils_NonblockingGet({ url => $url, - callback=>sub($$$){ Shelly_status($hash,$_[1],$_[2]) } + timeout => $timeout, + callback => sub($$$){ Shelly_status($hash,$_[1],$_[2]) } }); return undef; }elsif ( $hash && $err ){ @@ -941,14 +1042,12 @@ sub Shelly_pwd($){ return; } - my $model = AttrVal($name,"model","shelly1"); - my $mode = AttrVal($name,"mode",""); - + my $mode = AttrVal($name,"mode",""); my $channels = $shelly_models{$model}[0]; my $rollers = $shelly_models{$model}[1]; my $dimmers = $shelly_models{$model}[2]; my $meters = $shelly_models{$model}[3]; - + my ($subs,$ison,$overpower,$rpower,$rstate,$power,$energy,$rstopreason,$rcurrpos,$position,$rlastdir,$pct,$pctnormal); readingsBeginUpdate($hash); @@ -1048,7 +1147,7 @@ sub Shelly_pwd($){ readingsBulkUpdateIfChanged($hash,"last_dir".$subs,$rlastdir); } #-- we have a Shelly dimmer or RGBW white device - }elsif( ($model eq "shellydimmer") || ($model eq "shellyrgbw" && $mode eq "white") ){ + }elsif( ($model eq "shellydimmer") || ($model eq "shellyrgbw" && $mode eq "white") ){ for( my $i=0;$i<$dimmers;$i++){ $subs = (($dimmers == 1) ? "" : "_".$i); $ison = $jhash->{'lights'}[$i]{'ison'}; @@ -1063,12 +1162,34 @@ sub Shelly_pwd($){ readingsBulkUpdateIfChanged($hash,"overpower".$subs,$overpower); readingsBulkUpdateIfChanged($hash,"power".$subs,$power); readingsBulkUpdateIfChanged($hash,"energy".$subs,$energy); - } + } + readingsBulkUpdateIfChanged($hash,"state","OK") + if ($dimmers > 1); + + #-- we have a Shelly Bulb / Duo in white mode + }elsif( $model eq "shellybulb" && $mode eq "white" ){ + for( my $i=0;$i<$dimmers;$i++){ + $subs = (($dimmers == 1) ? "" : "_".$i); + $ison = $jhash->{'lights'}[$i]{'ison'}; + $ison =~ s/0|(false)/off/; + $ison =~ s/1|(true)/on/; + my $bri = $jhash->{'lights'}[$i]{'brightness'}; + my $ct = $jhash->{'lights'}[$i]{'temp'}; + $overpower = $jhash->{'lights'}[$i]{'overpower'}; + $power = $jhash->{'meters'}[$i]{'power'}; + $energy = (defined($jhash->{'meters'}[$i]{'total'}))?int($jhash->{'meters'}[$i]{'total'}/6)/10:"undefined"; + readingsBulkUpdateIfChanged($hash,"state".$subs,$ison); + readingsBulkUpdateIfChanged($hash,"pct".$subs,$bri); + readingsBulkUpdateIfChanged($hash,"ct".$subs,$ct); + readingsBulkUpdateIfChanged($hash,"overpower".$subs,$overpower); + readingsBulkUpdateIfChanged($hash,"power".$subs,$power); + readingsBulkUpdateIfChanged($hash,"energy".$subs,$energy); + } readingsBulkUpdateIfChanged($hash,"state","OK") if ($dimmers > 1); #-- we have a Shelly RGBW color device - }elsif( $model eq "shellyrgbw" && $mode eq "color" ){ + }elsif( $model =~ /shelly(rgbw|bulb)/ && $mode eq "color" ){ $ison = $jhash->{'lights'}[0]{'ison'}; $ison =~ s/0|(false)/off/; $ison =~ s/1|(true)/on/; @@ -1077,6 +1198,13 @@ sub Shelly_pwd($){ my $green = $jhash->{'lights'}[0]{'green'}; my $blue = $jhash->{'lights'}[0]{'blue'}; my $white = $jhash->{'lights'}[0]{'white'}; + my $gain = $jhash->{'lights'}[0]{'gain'}; + if ( defined $gain && $gain != 100 ) { + $red = int ($red*$gain/100.0); + $blue = int ($blue*$gain/100.0); + $green = int ($green*$gain/100.0); + $white = int ($white*$gain/100.0); + } my $rgb = sprintf("%02X%02X%02X", $red,$green,$blue); readingsBulkUpdateIfChanged($hash,"rgb",$rgb); @@ -1138,21 +1266,22 @@ sub Shelly_pwd($){ sub Shelly_dim { my ($hash, $channel, $cmd, $err, $data) = @_; my $name = $hash->{NAME}; - my $url; my $state = $hash->{READINGS}{state}{VAL}; my $net = $hash->{READINGS}{network}{VAL}; return if( $net !~ /connected/ ); - my $model = AttrVal($name,"model",""); + my $model = AttrVal($name,"model","generic"); my $creds = Shelly_pwd($hash); if ( $hash && !$err && !$data ){ - $url = "http://$creds".$hash->{TCPIP}."/$channel$cmd"; + my $url = "http://$creds".$hash->{TCPIP}."/$channel$cmd"; + my $timeout = AttrVal($name,"timeout",4); Log3 $name, 5,"[Shelly_dim] Issue a non-blocking call to $url"; HttpUtils_NonblockingGet({ url => $url, - callback=>sub($$$){ Shelly_dim($hash,$channel,$cmd,$_[1],$_[2]) } + timeout => $timeout, + callback => sub($$$){ Shelly_dim($hash,$channel,$cmd,$_[1],$_[2]) } }); return undef; }elsif ( $hash && $err ){ @@ -1229,13 +1358,12 @@ sub Shelly_pwd($){ sub Shelly_updown { my ($hash, $cmd, $err, $data) = @_; my $name = $hash->{NAME}; - my $url; my $state = $hash->{READINGS}{state}{VAL}; my $net = $hash->{READINGS}{network}{VAL}; return if( $net !~ /connected/ ); - my $model = AttrVal($name,"model",""); + my $model = AttrVal($name,"model","generic"); my $creds = Shelly_pwd($hash); #-- empty cmd parameter @@ -1243,11 +1371,13 @@ sub Shelly_pwd($){ if( !defined($cmd) ); if ( $hash && !$err && !$data ){ - $url = "http://$creds".$hash->{TCPIP}."/roller/0".$cmd; + my $url = "http://$creds".$hash->{TCPIP}."/roller/0".$cmd; + my $timeout = AttrVal($name,"timeout",4); Log3 $name, 5,"[Shelly_updown] Issue a non-blocking call to $url"; HttpUtils_NonblockingGet({ url => $url, - callback=>sub($$$){ Shelly_updown($hash,$cmd,$_[1],$_[2]) } + timeout => $timeout, + callback => sub($$$){ Shelly_updown($hash,$cmd,$_[1],$_[2]) } }); return undef; }elsif ( $hash && $err ){ @@ -1313,21 +1443,22 @@ sub Shelly_updown2($){ sub Shelly_onoff { my ($hash, $channel, $cmd, $err, $data) = @_; my $name = $hash->{NAME}; - my $url; my $state = $hash->{READINGS}{state}{VAL}; my $net = $hash->{READINGS}{network}{VAL}; return if( $net !~ /connected/ ); - my $model = AttrVal($name,"model",""); + my $model = AttrVal($name,"model","generic"); my $creds = Shelly_pwd($hash); if ( $hash && !$err && !$data ){ - $url = "http://$creds".$hash->{TCPIP}."/relay/".$channel.$cmd; + my $url = "http://$creds".$hash->{TCPIP}."/relay/".$channel.$cmd; + my $timeout = AttrVal($name,"timeout",4); Log3 $name, 5,"[Shelly_onoff] Issue a non-blocking call to $url"; HttpUtils_NonblockingGet({ url => $url, - callback=>sub($$$){ Shelly_onoff($hash,$channel,$cmd,$_[1],$_[2]) } + timeout => $timeout, + callback => sub($$$){ Shelly_onoff($hash,$channel,$cmd,$_[1],$_[2]) } }); return undef; }elsif ( $hash && $err ){ @@ -1405,22 +1536,22 @@ sub Shelly_updown2($){ sub Shelly_meter { my ($hash, $channel, $err, $data) = @_; my $name = $hash->{NAME}; - my $url; my $state = $hash->{READINGS}{state}{VAL}; my $net = $hash->{READINGS}{network}{VAL}; return if( $net !~ /connected/ ); - my $model = AttrVal($name,"model",""); - + my $model = AttrVal($name,"model","generic"); my $creds = Shelly_pwd($hash); if ( $hash && !$err && !$data ){ - $url = "http://$creds".$hash->{TCPIP}."/meter/".$channel; + my $url = "http://$creds".$hash->{TCPIP}."/meter/".$channel; + my $timeout = AttrVal($name,"timeout",4); Log3 $name, 5,"[Shelly_meter] Issue a non-blocking call to $url"; HttpUtils_NonblockingGet({ url => $url, - callback=>sub($$$){ Shelly_meter($hash,$channel,$_[1],$_[2]) } + timeout => $timeout, + callback => sub($$$){ Shelly_meter($hash,$channel,$_[1],$_[2]) } }); return undef; }elsif ( $hash && $err ){ @@ -1466,15 +1597,20 @@ sub Shelly_updown2($){ Notes: +

Set

For all Shelly devices @@ -1546,8 +1682,8 @@ sub Shelly_updown2($){

Get