";
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,"color/0","?turn=".$cmd);
#Shelly_onoff($hash,"color/0","?turn=".$cmd);
}
if( $cmd eq "hsv" ){
my($hue,$saturation,$value)=split(',',$value);
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));
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);
Shelly_dim($hash,"color/0","?".$cmd);
}elsif( $cmd eq "rgbw" ){
my $red=hex(substr($value,0,2));
my $green=hex(substr($value,2,2));
my $blue=hex(substr($value,4,2));
my $white=hex(substr($value,4,2));
$cmd=sprintf("red=%d&green=%d&blue=%d&white=%d",$red,$green,$blue,$white);
Shelly_dim($hash,"color/0","?".$cmd);
}elsif( $cmd eq "white" ){
$cmd=sprintf("white=%d",$value);
Shelly_dim($hash,"color/0","?".$cmd);
}
}
#-- configuration register
if($cmd eq "config") {
my $reg = $value;
my ($val,$chan);
if( int(@a) == 2 ){
$chan = $a[1];
$val = $a[0];
}elsif( int(@a) == 1 ){
$chan = 0;
$val = $a[0];
}else{
my $msg = "Error: wrong number of parameters";
Log3 $name,1,"[Shelly_Set] ".$msg;
return $msg;
}
my $pre = "settings/";
if( ($model =~ /shelly2.*/) && ($mode eq "roller") ){
$pre .= "roller/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?";
}
$v = Shelly_configure($hash,$pre.$reg."=".$val);
}
#-- password
if($cmd eq "password") {
my $user = AttrVal($name, "shellyuser", '');
if(!$user){
my $msg = "Error: password can be set only if attribute shellyuser is set";
Log3 $name,1,"[Shelly_Set] ".$msg;
return $msg;
}
setKeyValue("SHELLY_PASSWORD_$name", $value);
}
return undef;
}
########################################################################################
#
# Shelly_pwd - retrieve the credentials if set
#
# Parameter hash
#
########################################################################################
sub Shelly_pwd($){
my ($hash) = @_;
my $name = $hash->{NAME};
my $user = AttrVal($name, "shellyuser", '');
return "" if(!$user);
my ($err, $pw) = getKeyValue("SHELLY_PASSWORD_$name");
return $user.":".$pw."@";
}
########################################################################################
#
# Shelly_configure - Configure Shelly device
# acts as callable program Shelly_configure($hash,$channel,$cmd)
# and as callback program Shelly_configure($hash,$channel,$cmd,$err,$data)
#
# Parameter hash, channel = 0,1 cmd = command
#
########################################################################################
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 $creds = Shelly_pwd($hash);
if ( $hash && !$err && !$data ){
$url = "http://$creds".$hash->{TCPIP}."/".$cmd;
Log3 $name, 5,"[Shelly_configure] Issue a non-blocking call to $url";
HttpUtils_NonblockingGet({
url => $url,
callback=>sub($$$){ Shelly_configure($hash,$cmd,$_[1],$_[2]) }
});
return undef;
}elsif ( $hash && $err ){
#Log3 $name, 1,"[Shelly_configure] has error $err";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
Log3 $name, 5,"[Shelly_configure] has obtained data $data";
my $json = JSON->new->utf8;
my $jhash = eval{ $json->decode( $data ) };
if( !$jhash ){
Log3 $name,1,"[Shelly_configure] has invalid JSON data";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
#-- isolate register name
my $reg = substr($cmd,index($cmd,"?")+1);
my $chan= substr($cmd,index($cmd,"?")-1,1);
$reg = substr($reg,0,index($reg,"="))
if(index($reg,"=") > 0);
my $val = $jhash->{$reg};
$val = ""
if(!defined($val));
$chan = " [channel $chan]";
readingsSingleUpdate($hash,"config",$reg."=".$val.$chan,1);
return undef;
}
########################################################################################
#
# Shelly_status - Retrieve data from device
# acts as callable program Shelly_status($hash)
# and as callback program Shelly_status($hash,$err,$data)
#
# Parameter hash
#
########################################################################################
sub Shelly_status {
my ($hash, $err, $data) = @_;
my $name = $hash->{NAME};
my $url;
my $state = $hash->{READINGS}{state}{VAL};
my $creds = Shelly_pwd($hash);
if ( $hash && !$err && !$data ){
$url = "http://$creds".$hash->{TCPIP}."/status";
Log3 $name, 5,"[Shelly_status] Issue a non-blocking call to $url";
HttpUtils_NonblockingGet({
url => $url,
callback=>sub($$$){ Shelly_status($hash,$_[1],$_[2]) }
});
return undef;
}elsif ( $hash && $err ){
Log3 $name, 1,"[Shelly_status] has error $err";
readingsSingleUpdate($hash,"state","Error",1);
readingsSingleUpdate($hash,"network","not connected",1);
#-- cyclic update nevertheless
RemoveInternalTimer($hash);
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "Shelly_status", $hash, 1)
if( $hash->{INTERVAL} ne "0" );
return $err;
}
Log3 $name, 5,"[Shelly_status] has obtained data $data";
my $json = JSON->new->utf8;
my $jhash = eval{ $json->decode( $data ) };
if( !$jhash ){
Log3 $name,1,"[Shelly_status] invalid JSON data";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
my $model = AttrVal($name,"model","shelly1");
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);
readingsBulkUpdateIfChanged($hash,"network","connected to {TCPIP}."\">".$hash->{TCPIP}."",1);
#-- we have a Shelly 1/1pw, Shelly 4, Shelly 2/2.5 or ShellyPlug switch type device
if( ($model =~ /shelly1.*/) || ($model eq "shellyplug") || ($model eq "shelly4") || (($model =~ /shelly2.*/) && ($mode eq "relay")) ){
for( my $i=0;$i<$channels;$i++){
$subs = (($channels == 1) ? "" : "_".$i);
$ison = $jhash->{'relays'}[$i]{'ison'};
$ison =~ s/0|(false)/off/;
$ison =~ s/1|(true)/on/;
$overpower = $jhash->{'relays'}[$i]{'overpower'};
readingsBulkUpdateIfChanged($hash,"relay".$subs,$ison);
readingsBulkUpdateIfChanged($hash,"overpower".$subs,$overpower);
if($model =~ /shelly(1|(plug)).*/){
readingsBulkUpdateIfChanged($hash,"state",$ison)
}else{
readingsBulkUpdateIfChanged($hash,"state","OK");
}
}
for( my $i=0;$i<$meters;$i++){
$subs = ($meters == 1) ? "" : "_".$i;
$power = $jhash->{'meters'}[$i]{'power'};
$energy = int($jhash->{'meters'}[$i]{'total'}/6)/10;
readingsBulkUpdateIfChanged($hash,"power".$subs,$power);
readingsBulkUpdateIfChanged($hash,"energy".$subs,$energy);
}
#-- we have a Shelly 2 roller type device
}elsif( ($model =~ /shelly2.*/) && ($mode eq "roller") ){
for( my $i=0;$i<$rollers;$i++){
$subs = ($rollers == 1) ? "" : "_".$i;
#-- weird data: stop, close or open
$rstate = $jhash->{'rollers'}[$i]{'state'};
$rstate =~ s/stop/stopped/;
$rstate =~ s/close/moving_down/;
$rstate =~ s/open/moving_up/;
$hash->{MOVING} = $rstate;
$hash->{DURATION} = 0;
#-- weird data: close or open
$rlastdir = $jhash->{'rollers'}[$i]{'last_direction'};
$rlastdir =~ s/close/down/;
$rlastdir =~ s/open/up/;
$rpower = $jhash->{'rollers'}[$i]{'power'};
$rstopreason = $jhash->{'rollers'}[$i]{'stop_reason'};
#-- open 100% or 0% ?
$pctnormal = (AttrVal($name,"pct100","open") eq "open");
#-- possibly no data
$rcurrpos = $jhash->{'rollers'}[$i]{'current_pos'};
#-- we have data from the device, take that one
if( defined($rcurrpos) && ($rcurrpos =~ /\d\d?\d?/) ){
$pct = $pctnormal ? $rcurrpos : 100-$rcurrpos;
$position = ($rcurrpos==100) ? "open" : ($rcurrpos==0 ? "closed" : $pct);
#-- we have no data from the device
}else{
Log3 $name,1,"[Shelly_status] device $name with model=$model returns no blind position, consider chosing a different model=shelly2/2.5"
if( $model !~ /shelly2.*/ );
$pct = ReadingsVal($name,"pct",undef);
#-- we have a reading
if( defined($pct) && $pct =~ /\d\d?\d?/ ){
$rcurrpos = $pctnormal ? $pct : 100-$pct;
$position = ($rcurrpos==100) ? "open" : ($rcurrpos==0 ? "closed" : $pct);
#-- we have no reading
}else{
if( $rstate eq "stopped" && $rstopreason eq "normal"){
if($rlastdir eq "up" ){
$rcurrpos = 100;
$pct = $pctnormal?100:0;
$position = "open"
}else{
$rcurrpos = 0;
$pct = $pctnormal?0:100;
$position = "closed";
}
}
}
}
readingsBulkUpdateIfChanged($hash,"state".$subs,$rstate);
readingsBulkUpdateIfChanged($hash,"pct".$subs,$pct);
readingsBulkUpdateIfChanged($hash,"position".$subs,$position);
readingsBulkUpdateIfChanged($hash,"power".$subs,$rpower);
readingsBulkUpdateIfChanged($hash,"stop_reason".$subs,$rstopreason);
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") ){
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'};
$power = $jhash->{'lights'}[$i]{'power'};
$overpower = $jhash->{'lights'}[$i]{'overpower'};
readingsBulkUpdateIfChanged($hash,"state".$subs,$ison);
readingsBulkUpdateIfChanged($hash,"pct".$subs,$bri);
readingsBulkUpdateIfChanged($hash,"power".$subs,$power);
readingsBulkUpdateIfChanged($hash,"overpower".$subs,$overpower);
}
readingsBulkUpdateIfChanged($hash,"state","OK")
if ($dimmers > 1);
#-- we have a Shelly RGBW color device
}elsif( $model eq "shellyrgbw" && $mode eq "color" ){
$ison = $jhash->{'lights'}[0]{'ison'};
$ison =~ s/0|(false)/off/;
$ison =~ s/1|(true)/on/;
$overpower = $jhash->{'lights'}[0]{'overpower'};
my $red = $jhash->{'lights'}[0]{'red'};
my $green = $jhash->{'lights'}[0]{'green'};
my $blue = $jhash->{'lights'}[0]{'blue'};
my $white = $jhash->{'lights'}[0]{'white'};
my $rgb = sprintf("%02X%02X%02X", $red,$green,$blue);
readingsBulkUpdateIfChanged($hash,"rgb",$rgb);
readingsBulkUpdateIfChanged($hash,"L-red",$red);
readingsBulkUpdateIfChanged($hash,"L-green",$green);
readingsBulkUpdateIfChanged($hash,"L-blue",$blue);
readingsBulkUpdateIfChanged($hash,"L-white",$white);
readingsBulkUpdateIfChanged($hash,"overpower",$overpower);
readingsBulkUpdateIfChanged($hash,"state",$ison);
for( my $i=0;$i<$meters;$i++){
$subs = ($meters == 1) ? "" : "_".$i;
$power = $jhash->{'meters'}[$i]{'power'};
$energy = (defined($jhash->{'meters'}[$i]{'total'}))?int($jhash->{'meters'}[$i]{'total'}/6)/10:"undefined";
readingsBulkUpdateIfChanged($hash,"power".$subs,$power);
readingsBulkUpdateIfChanged($hash,"energy".$subs,$energy);
}
}
#-- common to all Shelly models
my $hasupdate = $jhash->{'update'}{'has_update'};
my $firmware = $jhash->{'update'}{'old_version'};
$firmware =~ /.*\/(.*)\@.*/;
$firmware = $1;
if( $hasupdate ){
my $newfw = $jhash->{'update'}{'new_version'};
$newfw =~ /.*\/(.*)\@.*/;
$newfw = $1;
$firmware .= "(update needed to $newfw)";
}
readingsBulkUpdateIfChanged($hash,"firmware",$firmware);
my $hascloud = $jhash->{'cloud'}{'enabled'};
if( $hascloud ){
my $hasconn = ($jhash->{'cloud'}{'connected'}) ? "connected" : "not connected";
readingsBulkUpdateIfChanged($hash,"cloud","enabled($hasconn)");
}else{
readingsBulkUpdateIfChanged($hash,"cloud","disabled");
}
readingsEndUpdate($hash,1);
#-- cyclic update
RemoveInternalTimer($hash);
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "Shelly_status", $hash, 1)
if( $hash->{INTERVAL} ne "0" );
return undef;
}
########################################################################################
#
# Shelly_dim - Set Shelly dimmer state
# acts as callable program Shelly_dim($hash,$channel,$cmd)
# and as callback program Shelly_dim($hash,$channel,$cmd,$err,$data)
#
# Parameter hash, channel = 0,1 cmd = command
#
########################################################################################
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 $creds = Shelly_pwd($hash);
if ( $hash && !$err && !$data ){
$url = "http://$creds".$hash->{TCPIP}."/$channel$cmd";
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]) }
});
return undef;
}elsif ( $hash && $err ){
#Log3 $name, 1,"[Shelly_dim] has error $err";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
Log3 $name, 5,"[Shelly_dim] has obtained data $data";
my $json = JSON->new->utf8;
my $jhash = eval{ $json->decode( $data ) };
if( !$jhash ){
if( ($model =~ /shellyrgbw.*/) && ($data =~ /Device mode is not dimmer!/) ){
Log3 $name,1,"[Shelly_dim] Device $name is not a dimmer";
readingsSingleUpdate($hash,"state","Error",1);
return
}else{
Log3 $name,1,"[Shelly_dim] has invalid JSON data";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
}
my $ison = $jhash->{'ison'};
my $bright = $jhash->{'brightness'};
my $hastimer = $jhash->{'has_timer'};
my $overpower = $jhash->{'overpower'};
if( $cmd =~ /\?turn=((on)|(off))/ ){
my $cmd2 = $1;
$ison =~ s/0|(false)/off/;
$ison =~ s/1|(true)/on/;
#-- timer command
if( index($cmd,"&") ne "-1"){
$cmd = substr($cmd,0,index($cmd,"&"));
if( $hastimer && $hastimer ne "1" ){
Log3 $name,1,"[Shelly_dim] returns with problem, timer not set";
}
}
if( $ison ne $cmd2 ) {
Log3 $name,1,"[Shelly_dim] returns without success, cmd=$cmd but ison=$ison";
}
}elsif( $cmd =~ /\?brightness=(.*)/){
my $cmd2 = $1;
if( $bright ne $cmd2 ) {
Log3 $name,1,"[Shelly_dim] returns without success, desired brightness $cmd, but device brightness=$bright";
}
}
if( defined($overpower) && $overpower eq "1") {
Log3 $name,1,"[Shelly_dim] switched off automatically because of overpower signal";
}
readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"overpower",$overpower)
if( $shelly_models{$model}[3] > 0);
readingsEndUpdate($hash,1);
#-- Call status after switch.
InternalTimer(int(gettimeofday()+1.5), "Shelly_status", $hash,0);
return undef;
}
########################################################################################
#
# Shelly_updown - Move roller blind
# acts as callable program Shelly_updown($hash,$cmd)
# and as callback program Shelly_updown($hash,$cmd,$err,$data)
#
# Parameter hash, channel = 0,1 cmd = command
#
########################################################################################
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 $creds = Shelly_pwd($hash);
#-- empty cmd parameter
$cmd = ""
if( !defined($cmd) );
if ( $hash && !$err && !$data ){
$url = "http://$creds".$hash->{TCPIP}."/roller/0".$cmd;
Log3 $name, 5,"[Shelly_updown] Issue a non-blocking call to $url";
HttpUtils_NonblockingGet({
url => $url,
callback=>sub($$$){ Shelly_updown($hash,$cmd,$_[1],$_[2]) }
});
return undef;
}elsif ( $hash && $err ){
#Log3 $name, 1,"[Shelly_updown] has error $err";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
Log3 $name, 5,"[Shelly_updown] has obtained data $data";
my $json = JSON->new->utf8;
my $jhash = eval{ $json->decode( $data ) };
if( !$jhash ){
if( ($model =~ /shelly2.*/) && ($data =~ /Device mode is not roller!/) ){
Log3 $name,1,"[Shelly_updown] Device $name is not in roller mode";
readingsSingleUpdate($hash,"state","Error",1);
return
}else{
Log3 $name,1,"[Shelly_updown] has invalid JSON data";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
}
#-- immediately after starting movement
if( $cmd ne ""){
#-- open 100% or 0% ?
my $pctnormal = (AttrVal($name,"pct100","open") eq "open");
my $targetpct = $hash->{TARGETPCT};
my $targetposition = $targetpct;
if( $targetpct == 100 ){
$targetposition = $pctnormal ? "open" : "closed";
}elsif( $targetpct == 0 ){
$targetposition = $pctnormal ? "closed" : "open";
}
readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"state",$hash->{MOVING});
readingsBulkUpdate($hash,"pct",$targetpct);
readingsBulkUpdate($hash,"position",$targetposition);
readingsEndUpdate($hash,1);
#-- after 1 second call power measurement
InternalTimer(gettimeofday()+1, "Shelly_updown2", $hash,1);
}
return undef;
}
sub Shelly_updown2($){
my ($hash) =@_;
Shelly_meter($hash,0);
InternalTimer(gettimeofday()+$hash->{DURATION}, "Shelly_status", $hash,1);
}
########################################################################################
#
# Shelly_onoff - Switch Shelly relay
# acts as callable program Shelly_onoff($hash,$channel,$cmd)
# and as callback program Shelly_onoff($hash,$channel,$cmd,$err,$data)
#
# Parameter hash, channel = 0,1 cmd = command
#
########################################################################################
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 $creds = Shelly_pwd($hash);
if ( $hash && !$err && !$data ){
$url = "http://$creds".$hash->{TCPIP}."/relay/".$channel.$cmd;
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]) }
});
return undef;
}elsif ( $hash && $err ){
#Log3 $name, 1,"[Shelly_onoff] has error $err";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
Log3 $name, 5,"[Shelly_onoff] has obtained data $data";
my $json = JSON->new->utf8;
my $jhash = eval{ $json->decode( $data ) };
if( !$jhash ){
if( ($model =~ /shelly2.*/) && ($data =~ /Device mode is not relay!/) ){
Log3 $name,1,"[Shelly_onoff] Device $name is not in relay mode";
readingsSingleUpdate($hash,"state","Error",1);
return
}else{
Log3 $name,1,"[Shelly_onoff] has invalid JSON data";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
}
my $ison = $jhash->{'ison'};
my $hastimer = $jhash->{'has_timer'};
my $overpower = $jhash->{'overpower'};
$ison =~ s/0|(false)/off/;
$ison =~ s/1|(true)/on/;
$cmd =~ s/\?turn=//;
#-- timer command
if( index($cmd,"&") ne "-1"){
$cmd = substr($cmd,0,index($cmd,"&"));
if( $hastimer ne "1" ){
Log3 $name,1,"[Shelly_onoff] returns with problem, timer not set";
}
}
if( $ison ne $cmd ) {
Log3 $name,1,"[Shelly_onoff] returns without success, cmd=$cmd but ison=$ison";
}
if( defined($overpower) && $overpower eq "1") {
Log3 $name,1,"[Shelly_onoff] switched off automatically because of overpower signal";
}
#--
my $subs = ($shelly_models{$model}[0] ==1) ? "" : "_".$channel;
readingsBeginUpdate($hash);
if($model =~ /shelly(1|(plug)).*/){
readingsBulkUpdateIfChanged($hash,"state",$ison)
}else{
readingsBulkUpdate($hash,"state","OK");
}
readingsBulkUpdate($hash,"relay".$subs,$ison);
readingsBulkUpdate($hash,"overpower".$subs,$overpower)
if( $shelly_models{$model}[3] > 0);
readingsEndUpdate($hash,1);
#-- Call status after switch.
InternalTimer(int(gettimeofday()+1.5), "Shelly_status", $hash,0);
return undef;
}
########################################################################################
#
# Shelly_meter - Retrieve data from meter
# acts as callable program Shelly_meter($hash,$channel,cmd)
# and as callback program Shelly_meter0($hash,$channel,$cmd,$err,$data)
#
# Parameter hash, channel, cmd = command
#
########################################################################################
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 $creds = Shelly_pwd($hash);
if ( $hash && !$err && !$data ){
$url = "http://$creds".$hash->{TCPIP}."/meter/".$channel;
Log3 $name, 5,"[Shelly_meter] Issue a non-blocking call to $url";
HttpUtils_NonblockingGet({
url => $url,
callback=>sub($$$){ Shelly_meter($hash,$channel,$_[1],$_[2]) }
});
return undef;
}elsif ( $hash && $err ){
Log3 $name, 1,"[Shelly_meter has error $err";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
Log3 $name, 5,"[Shelly_meter] has obtained data $data";
my $json = JSON->new->utf8;
my $jhash = eval{ $json->decode( $data ) };
if( !$jhash ){
Log3 $name,1,"[Shelly_meter] invalid JSON data";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
my $subs = ($shelly_models{$model}[3] ==1) ? "" : "_".$channel;
my $power = $jhash->{'power'};
my $energy = int($jhash->{'total'}/6)/10;
readingsSingleUpdate($hash,"power".$subs,$power,1);
readingsSingleUpdate($hash,"energy".$subs,$energy,1);
return undef;
}
1;
=pod
=item device
=item summary to communicate with a Shelly switch/roller actuator
=begin html
Shelly
FHEM module to communicate with a Shelly switch/roller actuator/dimmer/RGBW controller
Define
define <name> Shelly <IP address>
Defines the Shelly device.
Notes:
- The attribute
model
must be set
- This module needs the JSON package
- In Shelly switch devices or the Shelly dimmer device one may set URL values that are "hit" when the input or output status changes. Here one must set
- For Button switched ON url: http://<FHEM IP address>:<Port>/fhem?XHR=1&cmd=set%20<Devicename>%20button_on%20[<channel>]
- For Button switched OFF url: http://<FHEM IP address>:<Port>/fhem?XHR=1&cmd=set%20<Devicename>%20button_off%20[<channel>]
- For Output switched ON url: http://<FHEM IP address>:<Port>/fhem?XHR=1&cmd=set%20<Devicename>%20out_on%20[<channel>]
- For Output switched OFF url: http://<FHEM IP address>:<Port>/fhem?XHR=1&cmd=set%20<Devicename>%20out_off%20[<channel>]
Attention: Of course, a csrfToken must be included as well - or a proper allowed device declared.
Set
For all Shelly devices
set <name> config <registername> <value> [<channel>]
set the value of a configuration register
- password <password>
This is the only way to set the password for the Shelly web interface
For Shelly switching devices (model=shelly1|shelly1pm|shelly4|shellyplug or (model=shelly2/2.5 and mode=relay))
-
set <name> on|off|toggle [<channel>]
switches channel <channel> on or off. Channel numbers are 0 and 1 for model=shelly2/2.5, 0..3 for model=shelly4. If the channel parameter is omitted, the module will switch the channel defined in the defchannel attribute.
-
set <name> on-for-timer|off-for-timer <time> [<channel>]
switches <channel> on or off for <time> seconds. Channel numbers are 0 and 1 for model=shelly2/2.5, and 0..3 model=shelly4. If the channel parameter is omitted, the module will switch the channel defined in the defchannel attribute.
-
set <name> xtrachannels
create readingsProxy devices for switching device with more than one channel
For Shelly roller blind devices (model=shelly2/2.5 and mode=roller)
-
set <name> open|closed|stop
drives the roller blind open, closed or to a stop.
-
set <name> pct <integer percent value>
drives the roller blind to a partially closed position (100=open, 0=closed)
-
set <name> zero
calibration of roller device (only for model=shelly2/2.5)
For Shelly dimmer devices model=shellydimmer or (model=shellyrgbw and mode=white)
-
set <name> on|off [<channel>]
switches channel <channel> on or off. Channel numbers are 0..3 for model=shellyrgbw. If the channel parameter is omitted, the module will switch the channel defined in the defchannel attribute.
-
set <name> on-for-timer|off-for-timer <time> [<channel>]
switches <channel> on or off for <time> seconds. Channel numbers 0..3 for model=shellyrgbw. If the channel parameter is omitted, the module will switch the channel defined in the defchannel attribute.
-
set <name> pct <0..100> [<channel>]
percent value to set brightness value. Channel numbers 0..3 for model=shellyrgbw. If the channel parameter is omitted, the module will dim the channel defined in the defchannel attribute.
For Shelly RGBW devices (model=shellyrgbw and mode=color)
-
set <name> on|off
switches device <channel> on or off
-
set <name> on-for-timer|off-for-timer <time>
switches device on or off for <time> seconds.
-
set <name> hsv <hue value 0..360>,<saturation value 0..1>,<brightness value 0..1>
comma separated list of hue, saturation and value to set the color
-
set <name> rgb <rrggbb>
6-digit hex string to set the color
-
set <name> rgbw <rrggbbww>
8-digit hex string to set the color and white value
-
set <name> white <integer>
number 0..255 to set the white value
Get
-
get <name> config <registername> [<channel>]
get the value of a configuration register and writes it in reading config
-
get <name> registers
displays the names of the configuration registers for this device
-
get <name> status
returns the current devices status.
-
get <name> version
display the version of the module
Attributes
attr <name> shellyuser <shellyuser>
username for addressing the Shelly web interface
- <
attr <name> model shelly1|shelly1pm|shelly2|shelly2.5|shelly4|shellyplug|shellydimmer|shellyrgbw
type of the Shelly device
attr <name> mode relay|roller (only for model=shelly2/2.5) mode white|color (only for model=shellyrgbw)
type of the Shelly device
-
<interval>
Update interval for reading in seconds. The default is 60 seconds, a value of 0 disables the automatic update.
For Shelly switching devices (mode=relay for model=shelly2/2.5, standard for all other switching models)
attr <name> defchannel
only for model=shelly2|shelly2.5|shelly4 or multi-channel switches: Which channel will be switched, if a command is received without channel number
For Shelly roller blind devices (mode=roller for model=shelly2/2.5)
attr <name> maxtime <float>
time needed for a complete drive upward or downward
attr <name> pct100 open|closed (default:open)
is pct=100 open or closed ?
Standard attributes
=end html
=begin html_DE
Shelly
Absichtlich keine deutsche Dokumentation vorhanden, die englische Version gibt es hier: Shelly
=end html_DE
=cut