mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-04 11:26:55 +00:00
36-Shelly.pm: Neue version 1.6 mit neuen features
git-svn-id: https://svn.fhem.de/fhem/trunk@17846 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
464db73ae6
commit
7f2fc61ae1
@ -38,7 +38,7 @@ use vars qw{%attr %defs};
|
|||||||
sub Log($$);
|
sub Log($$);
|
||||||
|
|
||||||
#-- globals on start
|
#-- globals on start
|
||||||
my $version = "1.5";
|
my $version = "1.6";
|
||||||
|
|
||||||
#-- these we may get on request
|
#-- these we may get on request
|
||||||
my %gets = (
|
my %gets = (
|
||||||
@ -54,7 +54,7 @@ my %setssw = (
|
|||||||
"off" => "F",
|
"off" => "F",
|
||||||
"on-for-timer" => "T",
|
"on-for-timer" => "T",
|
||||||
"off-for-timer" => "E",
|
"off-for-timer" => "E",
|
||||||
"config" => "C",
|
"config" => "K",
|
||||||
"password" => "W"
|
"password" => "W"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -62,6 +62,7 @@ my %setsrol = (
|
|||||||
"closed:noArg" => "C",
|
"closed:noArg" => "C",
|
||||||
"open:noArg" => "O",
|
"open:noArg" => "O",
|
||||||
"stop:noArg" => "S",
|
"stop:noArg" => "S",
|
||||||
|
"config" => "K",
|
||||||
"password" => "W",
|
"password" => "W",
|
||||||
"pct:slider,0,1,100" => "P"
|
"pct:slider,0,1,100" => "P"
|
||||||
);
|
);
|
||||||
@ -70,6 +71,7 @@ my %shelly_models = (
|
|||||||
#(relays,rollers,meters)
|
#(relays,rollers,meters)
|
||||||
"shelly1" => [1,0,0,],
|
"shelly1" => [1,0,0,],
|
||||||
"shelly2" => [2,1,1],
|
"shelly2" => [2,1,1],
|
||||||
|
"shelly2beta" => [2,1,1],
|
||||||
"shellyplug" => [1,0,1],
|
"shellyplug" => [1,0,1],
|
||||||
"shelly4" => [4,0,4]
|
"shelly4" => [4,0,4]
|
||||||
);
|
);
|
||||||
@ -134,7 +136,7 @@ sub Shelly_Define($$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$hash->{DURATION} = 0;
|
$hash->{DURATION} = 0;
|
||||||
$hash->{MOVING} = 0;
|
$hash->{MOVING} = "stopped";
|
||||||
delete $hash->{BLOCKED};
|
delete $hash->{BLOCKED};
|
||||||
$hash->{INTERVAL} = 60;
|
$hash->{INTERVAL} = 60;
|
||||||
|
|
||||||
@ -226,8 +228,8 @@ sub Shelly_Attr(@) {
|
|||||||
#}
|
#}
|
||||||
#---------------------------------------
|
#---------------------------------------
|
||||||
}elsif ( ($cmd eq "set") && ($attrName =~ /mode/) ) {
|
}elsif ( ($cmd eq "set") && ($attrName =~ /mode/) ) {
|
||||||
if( $model ne "shelly2" ){
|
if( $model !~ /shelly2.*/ ){
|
||||||
Log3 $name,1,"[Shelly_Attr] setting the mode attribute only works for model=shelly2";
|
Log3 $name,1,"[Shelly_Attr] setting the mode attribute only works for model=shelly2[xxx]";
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if( $attrVal !~ /((relay)|(roller))/){
|
if( $attrVal !~ /((relay)|(roller))/){
|
||||||
@ -246,16 +248,16 @@ sub Shelly_Attr(@) {
|
|||||||
|
|
||||||
#---------------------------------------
|
#---------------------------------------
|
||||||
}elsif ( ($cmd eq "set") && ($attrName eq "maxtime") ) {
|
}elsif ( ($cmd eq "set") && ($attrName eq "maxtime") ) {
|
||||||
if( ($model ne "shelly2") || ($mode ne "roller" ) ){
|
if( ($model !~ /shelly2.*/) || ($mode ne "roller" ) ){
|
||||||
Log3 $name,1,"[Shelly_Attr] setting the maxtime attribute only works for model=shelly2 and mode=roller";
|
Log3 $name,1,"[Shelly_Attr] setting the maxtime attribute only works for model=shelly2[xxx] and mode=roller";
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Shelly_configure($hash,"settings?maxtime=".$attrVal);
|
Shelly_configure($hash,"settings?maxtime=".$attrVal);
|
||||||
|
|
||||||
#---------------------------------------
|
#---------------------------------------
|
||||||
}elsif ( ($cmd eq "set") && ($attrName eq "pct100") ) {
|
}elsif ( ($cmd eq "set") && ($attrName eq "pct100") ) {
|
||||||
if( ($model ne "shelly2") || ($mode ne "roller" ) ){
|
if( ($model !~ /shelly2.*/) || ($mode ne "roller" ) ){
|
||||||
Log3 $name,1,"[Shelly_Attr] setting the pct100 attribute only works for model=shelly2 and mode=roller";
|
Log3 $name,1,"[Shelly_Attr] setting the pct100 attribute only works for model=shelly2[xxx] and mode=roller";
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +304,7 @@ sub Shelly_Get ($@) {
|
|||||||
}elsif($a[1] eq "registers") {
|
}elsif($a[1] eq "registers") {
|
||||||
my $txt = "relay";
|
my $txt = "relay";
|
||||||
$txt = "roller"
|
$txt = "roller"
|
||||||
if( ($model eq "shelly2") && ($mode eq "roller") );
|
if( ($model =~ /shelly2.*/) && ($mode eq "roller") );
|
||||||
return $shelly_regs{$txt}."\n\nSet/Get these registers by calling set/get $name config <registername> [<channel>] <value>";
|
return $shelly_regs{$txt}."\n\nSet/Get these registers by calling set/get $name config <registername> [<channel>] <value>";
|
||||||
|
|
||||||
#-- configuration register
|
#-- configuration register
|
||||||
@ -321,7 +323,7 @@ sub Shelly_Get ($@) {
|
|||||||
return $msg;
|
return $msg;
|
||||||
}
|
}
|
||||||
my $pre = "settings/";
|
my $pre = "settings/";
|
||||||
if( ($model eq "shelly2") && ($mode eq "roller") ){
|
if( ($model =~ /shelly2.*/) && ($mode eq "roller") ){
|
||||||
$pre .= "roller/$chan?";
|
$pre .= "roller/$chan?";
|
||||||
}else{
|
}else{
|
||||||
$pre .= "relay/$chan?";
|
$pre .= "relay/$chan?";
|
||||||
@ -365,13 +367,11 @@ sub Shelly_Set ($@) {
|
|||||||
|
|
||||||
#-- we have a Shelly 1,4 or ShellyPlug switch type device
|
#-- we have a Shelly 1,4 or ShellyPlug switch type device
|
||||||
#-- or we have a Shelly 2 switch type device
|
#-- or we have a Shelly 2 switch type device
|
||||||
if( ($model eq "shelly1") || ($model eq "shelly4") || ($model eq "shellyplug") || (($model eq "shelly2") && ($mode eq "relay")) ){
|
if( ($model eq "shelly1") || ($model eq "shelly4") || ($model eq "shellyplug") || (($model =~ /shelly2.*/) && ($mode eq "relay")) ){
|
||||||
|
|
||||||
#-- WEB asking for command list
|
#-- WEB asking for command list
|
||||||
if( $cmd eq "?" ) {
|
if( $cmd eq "?" ) {
|
||||||
$newkeys = join(" ", sort keys %setssw);
|
$newkeys = join(" ", sort keys %setssw);
|
||||||
#$newkeys =~ s/on\s/on:0,1 /;
|
|
||||||
#$newkeys =~ s/off\s/off:0,1 /;
|
|
||||||
return "[Shelly_Set] Unknown argument " . $cmd . ", choose one of ".$newkeys;
|
return "[Shelly_Set] Unknown argument " . $cmd . ", choose one of ".$newkeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,85 +410,95 @@ sub Shelly_Set ($@) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#-- we have a Shelly 2 roller type device
|
#-- we have a Shelly 2 roller type device
|
||||||
}elsif( ($model eq "shelly2") && ($mode eq "roller") ){
|
}elsif( ($model =~ /shelly2.*/) && ($mode eq "roller") ){
|
||||||
my $channel = $value;
|
my $channel = $value;
|
||||||
my $max=AttrVal($name,"maxtime",undef);
|
my $max=AttrVal($name,"maxtime",undef);
|
||||||
#-- WEB asking for command list
|
#-- WEB asking for command list
|
||||||
if( $cmd eq "?" ) {
|
if( $cmd eq "?" ) {
|
||||||
$newkeys = join(" ", sort keys %setsrol);
|
$newkeys = join(" ", sort keys %setsrol);
|
||||||
|
if( $model eq "shelly2beta" ){
|
||||||
|
$newkeys .= " zero:noArg";
|
||||||
|
}
|
||||||
return "[Shelly_Set] Unknown argument " . $cmd . ", choose one of ".$newkeys;
|
return "[Shelly_Set] Unknown argument " . $cmd . ", choose one of ".$newkeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( $hash->{MOVING} ){
|
if( $cmd eq "zero" ) {
|
||||||
|
Shelly_configure($hash,"rc");
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $hash->{MOVING} ne "stopped" ){
|
||||||
$msg = "Error: roller blind still moving, wait for some time";
|
$msg = "Error: roller blind still moving, wait for some time";
|
||||||
Log3 $name,1,"[Shelly_Set] ".$msg;
|
Log3 $name,1,"[Shelly_Set] ".$msg;
|
||||||
return $msg
|
return $msg
|
||||||
}
|
}
|
||||||
|
|
||||||
if( $cmd eq "closed" ){
|
#-- open 100% or 0% ?
|
||||||
Shelly_updown($hash,"?go=close");
|
my $pctnormal = (AttrVal($name,"pct100","open") eq "open");
|
||||||
$hash->{DURATION} = $max;
|
|
||||||
}elsif( $cmd eq "open" ){
|
if( $cmd eq "stop" ){
|
||||||
Shelly_updown($hash,"?go=open");
|
|
||||||
$hash->{DURATION} = $max;
|
|
||||||
}elsif( $cmd eq "stop" ){
|
|
||||||
Shelly_updown($hash,"?go=stop");
|
Shelly_updown($hash,"?go=stop");
|
||||||
|
# -- estimate pos here ???
|
||||||
$hash->{DURATION} = 0;
|
$hash->{DURATION} = 0;
|
||||||
|
}elsif( $cmd eq "closed" ){
|
||||||
|
$hash->{MOVING} = "moving_down";
|
||||||
|
$hash->{DURATION} = $max;
|
||||||
|
$hash->{TARGETPCT} = $pctnormal ? 0 : 100;
|
||||||
|
Shelly_updown($hash,"?go=close");
|
||||||
|
}elsif( $cmd eq "open" ){
|
||||||
|
$hash->{MOVING} = "moving_up";
|
||||||
|
$hash->{DURATION} = $max;
|
||||||
|
$hash->{TARGETPCT} = $pctnormal ? 100 : 0;
|
||||||
|
Shelly_updown($hash,"?go=open");
|
||||||
}elsif( $cmd eq "pct" ){
|
}elsif( $cmd eq "pct" ){
|
||||||
my $tpct = $value;
|
my $targetpct = $value;
|
||||||
my $pos = ReadingsVal($name,"position","");
|
my $pos = ReadingsVal($name,"position","");
|
||||||
my $pct = ReadingsVal($name,"pct",undef);
|
my $pct = ReadingsVal($name,"pct",undef);
|
||||||
|
|
||||||
|
#-- shelly2 cannot memorize position, commands are only "go"
|
||||||
|
if( $model eq "shelly2" ){
|
||||||
if( !$max ){
|
if( !$max ){
|
||||||
$msg = "Error: pct value can be set only if maxtime attribute is set properly";
|
$msg = "Error: pct value can be set for model=shelly2 only if maxtime attribute is set properly";
|
||||||
Log3 $name,1,"[Shelly_Set] ".$msg;
|
Log3 $name,1,"[Shelly_Set] ".$msg;
|
||||||
return $msg
|
return $msg
|
||||||
}
|
}
|
||||||
my $normal = (AttrVal($name,"pct100","open") eq "open");
|
|
||||||
if( $pos eq "open" ){
|
if( $pos eq "open" ){
|
||||||
#-- 100% = open
|
$time = int( ($pctnormal ? $max*(100-$targetpct) : $max*$targetpct)/10)/10;
|
||||||
if($normal){
|
|
||||||
$time = int(($max*(100-$tpct))/10)/10;
|
|
||||||
}else{
|
|
||||||
$time = int(($max*$pct)/10)/10;
|
|
||||||
}
|
|
||||||
$cmd = "?go=close&duration=".$time;
|
$cmd = "?go=close&duration=".$time;
|
||||||
|
$hash->{MOVING} = "moving_down";
|
||||||
}elsif( $pos eq "closed" ){
|
}elsif( $pos eq "closed" ){
|
||||||
#-- 100% = open
|
$time = int( ($pctnormal ? $max*$targetpct : $max*(100-$targetpct))/10)/10;
|
||||||
if($normal){
|
$cmd = "?go=open&duration=".int($time/10)/10;
|
||||||
$time = int(($max*$tpct)/10)/10;
|
$hash->{MOVING} = "moving_up";
|
||||||
}else{
|
}else{
|
||||||
$time = int(($max*(100-$tpct))/10)/10;
|
if( !defined($pct) || ($pct !~ /\d\d?\d?/) ){
|
||||||
}
|
|
||||||
$cmd = "?go=open&duration=".$time;
|
|
||||||
}else{
|
|
||||||
if( !defined($pct) ){
|
|
||||||
$msg = "Error: current pct value unknown. Open or close roller blind before";
|
$msg = "Error: current pct value unknown. Open or close roller blind before";
|
||||||
Log3 $name,1,"[Shelly_Set] ".$msg;
|
Log3 $name,1,"[Shelly_Set] ".$msg;
|
||||||
return $msg;
|
return $msg;
|
||||||
}
|
}
|
||||||
if( $tpct > $pct ){
|
if( $targetpct > $pct ){
|
||||||
$time = int(($max*($tpct-$pct))/10)/10;
|
$time = int( ($max*($targetpct-$pct))/10 )/10;
|
||||||
#-- 100% = open
|
$cmd = $pctnormal ? "?go=open&duration=".$time : "?go=close&duration=".$time;
|
||||||
if($normal){
|
$hash->{MOVING} = $pctnormal ? "moving_up" : "moving_down";
|
||||||
$cmd = "?go=open&duration=".$time;
|
|
||||||
}else{
|
}else{
|
||||||
$cmd = "?go=close&duration=".$time;
|
$time = int(($max*($pct-$targetpct))/10)/10;
|
||||||
|
$cmd = $pctnormal ? "?go=close&duration=".$time : "?go=open&duration=".$time;
|
||||||
|
$hash->{MOVING} = $pctnormal ? "moving_down" : "moving_up";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
$time = int(($max*($pct-$tpct))/10)/10;
|
if( !$max ){
|
||||||
#-- 100% = open
|
Log3 $name,1,"[Shelly_Set] please set the maxtime attribute for proper operation";
|
||||||
if($normal){
|
$max = 20;
|
||||||
$cmd = "?go=close&duration=".$time;
|
|
||||||
}else{
|
|
||||||
$cmd = "?go=open&duration=".$time;
|
|
||||||
}
|
}
|
||||||
|
$time = int(abs($targetpct-$pct)/100*$max);
|
||||||
|
$cmd = "?go=to_pos&roller_pos=".$targetpct;
|
||||||
|
$hash->{MOVING} = $pctnormal ? (($targetpct > $pct) ? "moving_up" : "moving_down") : (($targetpct > $pct) ? "moving_down" : "moving_up");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
$hash->{MOVING} = 1;
|
|
||||||
$hash->{DURATION} = $time;
|
$hash->{DURATION} = $time;
|
||||||
|
$hash->{TARGETPCT} = $targetpct;
|
||||||
Shelly_updown($hash,$cmd);
|
Shelly_updown($hash,$cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#-- configuration register
|
#-- configuration register
|
||||||
@ -570,7 +580,7 @@ sub Shelly_pwd($){
|
|||||||
|
|
||||||
if ( $hash && !$err && !$data ){
|
if ( $hash && !$err && !$data ){
|
||||||
$url = "http://$creds".$hash->{TCPIP}."/".$cmd;
|
$url = "http://$creds".$hash->{TCPIP}."/".$cmd;
|
||||||
Log3 $name, 5,"[Shelly_configure] called with only hash => Issue a non-blocking call to $url";
|
Log3 $name, 5,"[Shelly_configure] Issue a non-blocking call to $url";
|
||||||
HttpUtils_NonblockingGet({
|
HttpUtils_NonblockingGet({
|
||||||
url => $url,
|
url => $url,
|
||||||
callback=>sub($$$){ Shelly_configure($hash,$cmd,$_[1],$_[2]) }
|
callback=>sub($$$){ Shelly_configure($hash,$cmd,$_[1],$_[2]) }
|
||||||
@ -619,7 +629,7 @@ sub Shelly_pwd($){
|
|||||||
|
|
||||||
if ( $hash && !$err && !$data ){
|
if ( $hash && !$err && !$data ){
|
||||||
$url = "http://$creds".$hash->{TCPIP}."/status";
|
$url = "http://$creds".$hash->{TCPIP}."/status";
|
||||||
Log3 $name, 5,"[Shelly_status] called with only hash => Issue a non-blocking call to $url";
|
Log3 $name, 5,"[Shelly_status] Issue a non-blocking call to $url";
|
||||||
HttpUtils_NonblockingGet({
|
HttpUtils_NonblockingGet({
|
||||||
url => $url,
|
url => $url,
|
||||||
callback=>sub($$$){ Shelly_status($hash,$_[1],$_[2]) }
|
callback=>sub($$$){ Shelly_status($hash,$_[1],$_[2]) }
|
||||||
@ -652,14 +662,15 @@ sub Shelly_pwd($){
|
|||||||
my $channels = $shelly_models{$model}[0];
|
my $channels = $shelly_models{$model}[0];
|
||||||
my $rollers = $shelly_models{$model}[1];
|
my $rollers = $shelly_models{$model}[1];
|
||||||
my $meters = $shelly_models{$model}[2];
|
my $meters = $shelly_models{$model}[2];
|
||||||
my ($subs,$ison,$overpower,$power,$rstate,$rpower,$rstopreason,$rlastdir);
|
|
||||||
|
my ($subs,$ison,$overpower,$rpower,$rstate,$power,$rstopreason,$rcurrpos,$position,$rlastdir,$pct,$pctnormal);
|
||||||
|
|
||||||
readingsBeginUpdate($hash);
|
readingsBeginUpdate($hash);
|
||||||
readingsBulkUpdateIfChanged($hash,"state","OK");
|
readingsBulkUpdateIfChanged($hash,"state","OK");
|
||||||
readingsBulkUpdateIfChanged($hash,"network","connected",1);
|
readingsBulkUpdateIfChanged($hash,"network","connected",1);
|
||||||
|
|
||||||
#-- we have a Shelly 1, Shelly 4, Shelly 2 or ShellyPlug switch type device
|
#-- we have a Shelly 1, Shelly 4, Shelly 2 or ShellyPlug switch type device
|
||||||
if( ($model eq "shelly1") || ($model eq "shellyplug") || ($model eq "shelly4") || (($model eq "shelly2") && ($mode eq "relay")) ){
|
if( ($model eq "shelly1") || ($model eq "shellyplug") || ($model eq "shelly4") || (($model =~ /shelly2.*/) && ($mode eq "relay")) ){
|
||||||
for( my $i=0;$i<$channels;$i++){
|
for( my $i=0;$i<$channels;$i++){
|
||||||
$subs = (($channels == 1) ? "" : "_".$i);
|
$subs = (($channels == 1) ? "" : "_".$i);
|
||||||
$ison = $jhash->{'relays'}[$i]{'ison'};
|
$ison = $jhash->{'relays'}[$i]{'ison'};
|
||||||
@ -677,44 +688,70 @@ sub Shelly_pwd($){
|
|||||||
}
|
}
|
||||||
|
|
||||||
#-- we have a Shelly 2 roller type device
|
#-- we have a Shelly 2 roller type device
|
||||||
}elsif( ($model eq "shelly2") && ($mode eq "roller") ){
|
}elsif( ($model =~ /shelly2.*/) && ($mode eq "roller") ){
|
||||||
#-- reset blocking due to existing movement
|
|
||||||
$hash->{MOVING} = 0;
|
|
||||||
$hash->{DURATION} = 0;
|
|
||||||
for( my $i=0;$i<$rollers;$i++){
|
for( my $i=0;$i<$rollers;$i++){
|
||||||
$subs = ($rollers == 1) ? "" : "_".$i;
|
$subs = ($rollers == 1) ? "" : "_".$i;
|
||||||
|
|
||||||
|
#-- weird data: stop, close or open
|
||||||
$rstate = $jhash->{'rollers'}[$i]{'state'};
|
$rstate = $jhash->{'rollers'}[$i]{'state'};
|
||||||
$rstate =~ s/close/closed/;
|
$rstate =~ s/stop/stopped/;
|
||||||
$rpower = $jhash->{'rollers'}[$i]{'power'};
|
$rstate =~ s/close/moving_down/;
|
||||||
$rstopreason = $jhash->{'rollers'}[$i]{'stop_reason'};
|
$rstate =~ s/open/moving_up/;
|
||||||
|
$hash->{MOVING} = $rstate;
|
||||||
|
$hash->{DURATION} = 0;
|
||||||
|
|
||||||
|
#-- weird data: close or open
|
||||||
$rlastdir = $jhash->{'rollers'}[$i]{'last_direction'};
|
$rlastdir = $jhash->{'rollers'}[$i]{'last_direction'};
|
||||||
$rlastdir =~ s/close/down/;
|
$rlastdir =~ s/close/down/;
|
||||||
$rlastdir =~ s/open/up/;
|
$rlastdir =~ s/open/up/;
|
||||||
|
|
||||||
my $pct;
|
$rpower = $jhash->{'rollers'}[$i]{'power'};
|
||||||
#-- renormalize position
|
$rstopreason = $jhash->{'rollers'}[$i]{'stop_reason'};
|
||||||
my $normal = (AttrVal($name,"pct100","open") eq "open");
|
|
||||||
if( $rstate eq "open" ){
|
#-- open 100% or 0% ?
|
||||||
#-- 100% = open in case normal
|
$pctnormal = (AttrVal($name,"pct100","open") eq "open");
|
||||||
$pct = $normal?100:0;
|
|
||||||
}elsif( $rstate eq "closed" ){
|
#-- possibly no data
|
||||||
#-- 100% = open in case normal
|
$rcurrpos = $jhash->{'rollers'}[$i]{'current_pos'};
|
||||||
$pct = $normal?0:100;
|
|
||||||
|
#-- we have data from the device, take that one
|
||||||
|
if( defined($rcurrpos) && ($rcurrpos =~ /\d\d?\d?/) ){
|
||||||
|
Log3 $name,1,"[Shelly_status] device $name with model=shelly2 returns a blind position, consider chosing a different model=shelly2[xxx]"
|
||||||
|
if( $model eq "shelly2" );
|
||||||
|
$pct = $pctnormal ? $rcurrpos : 100-$rcurrpos;
|
||||||
|
$position = ($rcurrpos==100) ? "open" : ($rcurrpos==0 ? "closed" : $pct);
|
||||||
|
|
||||||
|
#-- we have no data from the device
|
||||||
}else{
|
}else{
|
||||||
|
Log3 $name,1,"[Shelly_status] device $name with model=$model returns no blind position, consider chosing a different model=shelly2"
|
||||||
|
if( $model ne "shelly2" );
|
||||||
$pct = ReadingsVal($name,"pct",undef);
|
$pct = ReadingsVal($name,"pct",undef);
|
||||||
$pct = "unknown"
|
#-- we have a reading
|
||||||
if( !defined($pct) );
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#-- just in case we have leftover readings from relay devices
|
readingsBulkUpdateIfChanged($hash,"state".$subs,$rstate);
|
||||||
#fhem("deletereading ".$name." channel.*");
|
readingsBulkUpdateIfChanged($hash,"pct".$subs,$pct);
|
||||||
#fhem("deletereading ".$name." overpower.*");
|
readingsBulkUpdateIfChanged($hash,"position".$subs,$position);
|
||||||
|
|
||||||
readingsBulkUpdateIfChanged($hash,"position".$subs,$rstate);
|
|
||||||
readingsBulkUpdateIfChanged($hash,"power".$subs,$rpower);
|
readingsBulkUpdateIfChanged($hash,"power".$subs,$rpower);
|
||||||
readingsBulkUpdateIfChanged($hash,"stop_reason".$subs,$rstopreason);
|
readingsBulkUpdateIfChanged($hash,"stop_reason".$subs,$rstopreason);
|
||||||
readingsBulkUpdateIfChanged($hash,"last_dir".$subs,$rlastdir);
|
readingsBulkUpdateIfChanged($hash,"last_dir".$subs,$rlastdir);
|
||||||
readingsBulkUpdateIfChanged($hash,"pct".$subs,$pct);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#-- common to all Shelly models
|
#-- common to all Shelly models
|
||||||
@ -750,7 +787,7 @@ sub Shelly_pwd($){
|
|||||||
|
|
||||||
########################################################################################
|
########################################################################################
|
||||||
#
|
#
|
||||||
# Shelly_updown - Move rollere blind
|
# Shelly_updown - Move roller blind
|
||||||
# acts as callable program Shelly_updown($hash,$cmd)
|
# acts as callable program Shelly_updown($hash,$cmd)
|
||||||
# and as callback program Shelly_updown($hash,$cmd,$err,$data)
|
# and as callback program Shelly_updown($hash,$cmd,$err,$data)
|
||||||
#
|
#
|
||||||
@ -776,7 +813,7 @@ sub Shelly_pwd($){
|
|||||||
|
|
||||||
if ( $hash && !$err && !$data ){
|
if ( $hash && !$err && !$data ){
|
||||||
$url = "http://$creds".$hash->{TCPIP}."/roller/0".$cmd;
|
$url = "http://$creds".$hash->{TCPIP}."/roller/0".$cmd;
|
||||||
Log3 $name, 5,"[Shelly_updown] called with only hash => Issue a non-blocking call to $url";
|
Log3 $name, 1,"[Shelly_updown] Issue a non-blocking call to $url";
|
||||||
HttpUtils_NonblockingGet({
|
HttpUtils_NonblockingGet({
|
||||||
url => $url,
|
url => $url,
|
||||||
callback=>sub($$$){ Shelly_updown($hash,$cmd,$_[1],$_[2]) }
|
callback=>sub($$$){ Shelly_updown($hash,$cmd,$_[1],$_[2]) }
|
||||||
@ -792,7 +829,7 @@ sub Shelly_pwd($){
|
|||||||
my $json = JSON->new->utf8;
|
my $json = JSON->new->utf8;
|
||||||
my $jhash = eval{ $json->decode( $data ) };
|
my $jhash = eval{ $json->decode( $data ) };
|
||||||
if( !$jhash ){
|
if( !$jhash ){
|
||||||
if( ($model eq "shelly2") && ($data =~ /Device mode is not roller!/) ){
|
if( ($model =~ /shelly2.*/) && ($data =~ /Device mode is not roller!/) ){
|
||||||
Log3 $name,1,"[Shelly_updown] Device $name is not in roller mode";
|
Log3 $name,1,"[Shelly_updown] Device $name is not in roller mode";
|
||||||
readingsSingleUpdate($hash,"state","Error",1);
|
readingsSingleUpdate($hash,"state","Error",1);
|
||||||
return
|
return
|
||||||
@ -803,83 +840,35 @@ sub Shelly_pwd($){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my ($rstate,$rpower,$rstopreason,$rlastdir,$pct,$normal,$pctopen,$pctclose);
|
#-- immediately after starting movement
|
||||||
|
|
||||||
#-- immediately after moving blind
|
|
||||||
if( $cmd ne ""){
|
if( $cmd ne ""){
|
||||||
$rstate = "moving";
|
#-- open 100% or 0% ?
|
||||||
$pct = ReadingsVal($name,"pct",undef);
|
my $pctnormal = (AttrVal($name,"pct100","open") eq "open");
|
||||||
$normal = (AttrVal($name,"pct100","open") eq "open");
|
my $targetpct = $hash->{TARGETPCT};
|
||||||
$pctopen = ($normal && ($pct == 100)) || (!$normal && ($pct == 0));
|
my $targetposition = $targetpct;
|
||||||
$pctclose= ($normal && ($pct == 0)) || (!$normal && ($pct == 100));
|
if( $targetpct == 100 ){
|
||||||
#-- timer command
|
$targetposition = $pctnormal ? "open" : "closed";
|
||||||
if( index($cmd,"&") ne "-1"){
|
}elsif( $targetpct == 0 ){
|
||||||
my $max = AttrVal($name,"maxtime",undef);
|
$targetposition = $pctnormal ? "closed" : "open";
|
||||||
my $dir = substr($cmd,4,index($cmd,"&")-4);
|
|
||||||
my $dur = substr($cmd,index($cmd,"&")+10);
|
|
||||||
if( (!defined($pct) && ($dir eq "close")) || $pctopen ){
|
|
||||||
#-- 100% = open
|
|
||||||
if( $normal ){
|
|
||||||
$pct = 100-int((100*$dur)/$max);
|
|
||||||
}else{
|
|
||||||
$pct = int((100*$dur)/$max);
|
|
||||||
}
|
}
|
||||||
}elsif( $dir eq "close" ){
|
readingsBeginUpdate($hash);
|
||||||
#-- 100% = open
|
readingsBulkUpdate($hash,"state",$hash->{MOVING});
|
||||||
if( $normal ){
|
readingsBulkUpdate($hash,"pct",$targetpct);
|
||||||
$pct = $pct-int((100*$dur)/$max);
|
readingsBulkUpdate($hash,"position",$targetposition);
|
||||||
}else{
|
readingsEndUpdate($hash,1);
|
||||||
$pct = $pct+int((100*$dur)/$max);
|
|
||||||
}
|
|
||||||
}elsif( (!defined($pct) && ($dir eq "open")) || $pctclose ){
|
|
||||||
#-- 100% = open
|
|
||||||
if( $normal ){
|
|
||||||
$pct = int((100*$dur)/$max);
|
|
||||||
}else{
|
|
||||||
$pct = 100-int((100*$dur)/$max);
|
|
||||||
}
|
|
||||||
}elsif( $dir eq "open" ){
|
|
||||||
#-- 100% = open
|
|
||||||
if( $normal ){
|
|
||||||
$pct = $pct+int((100*$dur)/$max);
|
|
||||||
}else{
|
|
||||||
$pct = $pct-int((100*$dur)/$max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$pct = 0
|
|
||||||
if( $pct < 0);
|
|
||||||
$pct = 100
|
|
||||||
if( $pct > 100);
|
|
||||||
|
|
||||||
readingsBeginUpdate($hash);
|
#-- after 1 second call power measurement
|
||||||
readingsBulkUpdate($hash,"state","OK");
|
InternalTimer(gettimeofday()+1, "Shelly_updown2", $hash,1);
|
||||||
readingsBulkUpdate($hash,"position",$rstate);
|
|
||||||
readingsBulkUpdate($hash,"pct",$pct);
|
|
||||||
readingsEndUpdate($hash,1);
|
|
||||||
#-- Call us in 1 second again.
|
|
||||||
InternalTimer(gettimeofday()+ 1, "Shelly_updown", $hash,0);
|
|
||||||
#--after 1 second
|
|
||||||
}else{
|
|
||||||
$rstate = "moving";
|
|
||||||
$rpower = $jhash->{'power'};
|
|
||||||
$rstopreason = $jhash->{'stop_reason'};
|
|
||||||
$rlastdir = $jhash->{'last_direction'};
|
|
||||||
$rlastdir =~ s/close/down/;
|
|
||||||
$rlastdir =~ s/open/up/;
|
|
||||||
readingsBeginUpdate($hash);
|
|
||||||
readingsBulkUpdate($hash,"state","OK");
|
|
||||||
readingsBulkUpdate($hash,"position",$rstate);
|
|
||||||
readingsBulkUpdate($hash,"power",$rpower);
|
|
||||||
readingsBulkUpdate($hash,"stop_reason",$rstopreason);
|
|
||||||
readingsBulkUpdate($hash,"last_dir",$rlastdir);
|
|
||||||
readingsEndUpdate($hash,1);
|
|
||||||
#-- Call status after movement.
|
|
||||||
InternalTimer(gettimeofday()+int($hash->{DURATION}+0.5), "Shelly_status", $hash,0);
|
|
||||||
}
|
}
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub Shelly_updown2($){
|
||||||
|
my ($hash) =@_;
|
||||||
|
Shelly_meter($hash,0);
|
||||||
|
InternalTimer(gettimeofday()+$hash->{DURATION}, "Shelly_status", $hash,1);
|
||||||
|
}
|
||||||
|
|
||||||
########################################################################################
|
########################################################################################
|
||||||
#
|
#
|
||||||
# Shelly_onoff - Switch Shelly relay
|
# Shelly_onoff - Switch Shelly relay
|
||||||
@ -904,7 +893,7 @@ sub Shelly_pwd($){
|
|||||||
|
|
||||||
if ( $hash && !$err && !$data ){
|
if ( $hash && !$err && !$data ){
|
||||||
$url = "http://$creds".$hash->{TCPIP}."/relay/".$channel.$cmd;
|
$url = "http://$creds".$hash->{TCPIP}."/relay/".$channel.$cmd;
|
||||||
Log3 $name, 1,"[Shelly_onoff] called with only hash => Issue a non-blocking call to $url";
|
Log3 $name, 5,"[Shelly_onoff] Issue a non-blocking call to $url";
|
||||||
HttpUtils_NonblockingGet({
|
HttpUtils_NonblockingGet({
|
||||||
url => $url,
|
url => $url,
|
||||||
callback=>sub($$$){ Shelly_onoff($hash,$channel,$cmd,$_[1],$_[2]) }
|
callback=>sub($$$){ Shelly_onoff($hash,$channel,$cmd,$_[1],$_[2]) }
|
||||||
@ -920,7 +909,7 @@ sub Shelly_pwd($){
|
|||||||
my $json = JSON->new->utf8;
|
my $json = JSON->new->utf8;
|
||||||
my $jhash = eval{ $json->decode( $data ) };
|
my $jhash = eval{ $json->decode( $data ) };
|
||||||
if( !$jhash ){
|
if( !$jhash ){
|
||||||
if( ($model eq "shelly2") && ($data =~ /Device mode is not relay!/) ){
|
if( ($model =~ /shelly2.*/) && ($data =~ /Device mode is not relay!/) ){
|
||||||
Log3 $name,1,"[Shelly_onoff] Device $name is not in relay mode";
|
Log3 $name,1,"[Shelly_onoff] Device $name is not in relay mode";
|
||||||
readingsSingleUpdate($hash,"state","Error",1);
|
readingsSingleUpdate($hash,"state","Error",1);
|
||||||
return
|
return
|
||||||
@ -962,8 +951,6 @@ sub Shelly_pwd($){
|
|||||||
if( $shelly_models{$model}[2] > 0);
|
if( $shelly_models{$model}[2] > 0);
|
||||||
readingsEndUpdate($hash,1);
|
readingsEndUpdate($hash,1);
|
||||||
|
|
||||||
#InternalTimer(gettimeofday()+ 1, "Shelly_meter", $hash,0)
|
|
||||||
# if( $shelly_models{$model}[2] > 0);
|
|
||||||
#-- Call status after switch.
|
#-- Call status after switch.
|
||||||
InternalTimer(int(gettimeofday()+1.5), "Shelly_status", $hash,0);
|
InternalTimer(int(gettimeofday()+1.5), "Shelly_status", $hash,0);
|
||||||
|
|
||||||
@ -990,11 +977,12 @@ sub Shelly_pwd($){
|
|||||||
if( $net ne "connected" );
|
if( $net ne "connected" );
|
||||||
|
|
||||||
my $model = AttrVal($name,"model","");
|
my $model = AttrVal($name,"model","");
|
||||||
|
|
||||||
my $creds = Shelly_pwd($hash);
|
my $creds = Shelly_pwd($hash);
|
||||||
|
|
||||||
if ( $hash && !$err && !$data ){
|
if ( $hash && !$err && !$data ){
|
||||||
$url = "http://$creds".$hash->{TCPIP}."/meter/".$channel;
|
$url = "http://$creds".$hash->{TCPIP}."/meter/".$channel;
|
||||||
Log3 $name, 5,"[Shelly_meter] called with only hash => Issue a non-blocking call to $url";
|
Log3 $name, 1,"[Shelly_meter] Issue a non-blocking call to $url";
|
||||||
HttpUtils_NonblockingGet({
|
HttpUtils_NonblockingGet({
|
||||||
url => $url,
|
url => $url,
|
||||||
callback=>sub($$$){ Shelly_meter($hash,$channel,$_[1],$_[2]) }
|
callback=>sub($$$){ Shelly_meter($hash,$channel,$_[1],$_[2]) }
|
||||||
@ -1005,7 +993,7 @@ sub Shelly_pwd($){
|
|||||||
readingsSingleUpdate($hash,"state","Error",1);
|
readingsSingleUpdate($hash,"state","Error",1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log3 $name, 5,"[Shelly_meter] has obtained data $data";
|
Log3 $name, 1,"[Shelly_meter] has obtained data $data";
|
||||||
|
|
||||||
my $json = JSON->new->utf8;
|
my $json = JSON->new->utf8;
|
||||||
my $jhash = eval{ $json->decode( $data ) };
|
my $jhash = eval{ $json->decode( $data ) };
|
||||||
@ -1053,16 +1041,16 @@ sub Shelly_pwd($){
|
|||||||
<br />set the value of a configuration register</li>
|
<br />set the value of a configuration register</li>
|
||||||
<li><a name="shelly_password">password <password></a><br>This is the only way to set the password for the Shelly web interface</li>
|
<li><a name="shelly_password">password <password></a><br>This is the only way to set the password for the Shelly web interface</li>
|
||||||
</ul>
|
</ul>
|
||||||
For Shelly switching devices (mode=relay for model=shelly2, standard for all other models)
|
For Shelly switching devices (mode=relay for model=shelly2[xxx], standard for all other models)
|
||||||
<ul>
|
<ul>
|
||||||
<li><a name="shelly_onoff"></a>
|
<li><a name="shelly_onoff"></a>
|
||||||
<code>set <name> on|off [<channel>] </code>
|
<code>set <name> on|off [<channel>] </code>
|
||||||
<br />switches channel <channel> on or off. Only if model=shelly2/4: If the channel parameter is omitted, the module will switch the channel defined in the defchannel attribute.</li>
|
<br />switches channel <channel> on or off. Channel numbers are 0 and 1 for model=shelly2[xxx], 0..3 model=shelly4. If the channel parameter is omitted, the module will switch the channel defined in the defchannel attribute.</li>
|
||||||
<li><a name="shelly_onofftimer"></a>
|
<li><a name="shelly_onofftimer"></a>
|
||||||
<code>set <name> on-for-timer|off-for-timer <time> [<channel>] </code>
|
<code>set <name> on-for-timer|off-for-timer <time> [<channel>] </code>
|
||||||
<br />switches <channel> on or off for <time> seconds. Only if model=shelly2/4: If the channel parameter is omitted, the module will switch the channel defined in the defchannel attribute.</li>
|
<br />switches <channel> on or off for <time> seconds. Channel numbers are 0 and 1 for model=shelly2[xxx], 0..3 model=shelly4. If the channel parameter is omitted, the module will switch the channel defined in the defchannel attribute.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<br/>For Shelly roller blind devices (mode=roller for model=shelly2)
|
<br/>For Shelly roller blind devices (mode=roller for model=shelly2[xxx])
|
||||||
<ul>
|
<ul>
|
||||||
<li><a name="shelly_updown"></a>
|
<li><a name="shelly_updown"></a>
|
||||||
<code>set <name> open|closed|stop </code>
|
<code>set <name> open|closed|stop </code>
|
||||||
@ -1070,6 +1058,9 @@ sub Shelly_pwd($){
|
|||||||
<li><a name="shelly_pct"></a>
|
<li><a name="shelly_pct"></a>
|
||||||
<code>set <name> pct <integer percent value> </code>
|
<code>set <name> pct <integer percent value> </code>
|
||||||
<br />drives the roller blind to a partially closed position (100=open, 0=closed)</li>
|
<br />drives the roller blind to a partially closed position (100=open, 0=closed)</li>
|
||||||
|
<li><a name="shelly_zero"></a>
|
||||||
|
<code>set <name> zero </code>
|
||||||
|
<br />calibration of roller device (only for model=shelly2beta)</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a name="Shellyget"></a>
|
<a name="Shellyget"></a>
|
||||||
<h4>Get</h4>
|
<h4>Get</h4>
|
||||||
@ -1091,20 +1082,20 @@ sub Shelly_pwd($){
|
|||||||
<h4>Attributes</h4>
|
<h4>Attributes</h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a name="shelly_shellyuser"><code>attr <name> shellyuser <shellyuser></code><br>username for addressing the Shelly web interface</li>
|
<li><a name="shelly_shellyuser"><code>attr <name> shellyuser <shellyuser></code><br>username for addressing the Shelly web interface</li>
|
||||||
<li><a name="shelly_model"><code>attr <name> model shelly1|shelly2|shelly4|shellyplug </code></a>
|
<li><a name="shelly_model"><code>attr <name> model shelly1|shelly2|shelly2beta|shelly4|shellyplug </code></a>
|
||||||
<br />type of the Shelly device</li>
|
<br />type of the Shelly device</li>
|
||||||
<li><a name="shelly_mode"><code>attr <name> mode relay|roller (only for model=shelly2)</code></a>
|
<li><a name="shelly_mode"><code>attr <name> mode relay|roller (only for model=shelly2[xxx])</code></a>
|
||||||
<br />type of the Shelly device</li>
|
<br />type of the Shelly device</li>
|
||||||
<li><a name="shelly_interval">
|
<li><a name="shelly_interval">
|
||||||
<code><interval></code>
|
<code><interval></code>
|
||||||
<br />Update interval for reading in seconds. The default is 60 seconds, a value of 0 disables the automatic update. </li>
|
<br />Update interval for reading in seconds. The default is 60 seconds, a value of 0 disables the automatic update. </li>
|
||||||
</ul>
|
</ul>
|
||||||
<br/>For Shelly switching devices (mode=relay for model=shelly2, standard for all other models)
|
<br/>For Shelly switching devices (mode=relay for model=shelly2[xxx], standard for all other models)
|
||||||
<ul>
|
<ul>
|
||||||
<li><a name="shelly_defchannel"><code>attr <name> defchannel <integer> (only for model=shelly2|shelly4)</code></a>
|
<li><a name="shelly_defchannel"><code>attr <name> defchannel <integer> </code></a>
|
||||||
<br />for multi-channel switches: Which channel will be switched, if a command is received without channel number</li>
|
<br />only for model=shelly2[xxx]|shelly4or multi-channel switches: Which channel will be switched, if a command is received without channel number</li>
|
||||||
</ul>
|
</ul>
|
||||||
<br/>For Shelly roller blind devices (mode=roller for model=shelly2)
|
<br/>For Shelly roller blind devices (mode=roller for model=shelly2[xxx])
|
||||||
<ul>
|
<ul>
|
||||||
<li><a name="shelly_maxtime"><code>attr <name> maxtime <float> </code></a>
|
<li><a name="shelly_maxtime"><code>attr <name> maxtime <float> </code></a>
|
||||||
<br />time needed for a complete drive upward or downward</li>
|
<br />time needed for a complete drive upward or downward</li>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user