diff --git a/fhem/CHANGED b/fhem/CHANGED index cf4941dd0..decccfde7 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - new: 31_Aurora.pm: first release - bugfix: 21_HEOS: fix missing curl - bugfix: 31_PLAYBULB: code cleaning - bugfix: 21_HEOS: little Bugfixes, code cleanup diff --git a/fhem/FHEM/31_Aurora.pm b/fhem/FHEM/31_Aurora.pm new file mode 100644 index 000000000..7b6983aa9 --- /dev/null +++ b/fhem/FHEM/31_Aurora.pm @@ -0,0 +1,1000 @@ +# $Id$ + +package main; + +use strict; +use warnings; + +use Color; + +use POSIX; +use JSON; +use SetExtensions; + +use vars qw(%FW_webArgs); # all arguments specified in the GET + +my %dim_values = ( + 0 => "dim06%", + 1 => "dim12%", + 2 => "dim18%", + 3 => "dim25%", + 4 => "dim31%", + 5 => "dim37%", + 6 => "dim43%", + 7 => "dim50%", + 8 => "dim56%", + 9 => "dim62%", + 10 => "dim68%", + 11 => "dim75%", + 12 => "dim81%", + 13 => "dim87%", + 14 => "dim93%", +); + + +my $Aurora_hasDataDumper = 1; + +sub +Aurora_Initialize($) +{ + my ($hash) = @_; + + # Provide + + #Consumer + $hash->{DefFn} = "Aurora_Define"; + $hash->{UndefFn} = "Aurora_Undefine"; + $hash->{SetFn} = "Aurora_Set"; + $hash->{GetFn} = "Aurora_Get"; + $hash->{AttrFn} = "Aurora_Attr"; + $hash->{AttrList} = "delayedUpdate:1 ". + "realtimePicker:1,0 ". + "color-icons:1,2 ". + "transitiontime ". + "token ". + "disable:1,0 disabledForIntervals ". + $readingFnAttributes; + + #$hash->{FW_summaryFn} = "Aurora_summaryFn"; + + FHEM_colorpickerInit(); + + eval "use Data::Dumper"; + $Aurora_hasDataDumper = 0 if($@); +} + +sub +Aurora_devStateIcon($) +{ + my($hash) = @_; + $hash = $defs{$hash} if( ref($hash) ne 'HASH' ); + + return undef if( !$hash ); + my $name = $hash->{NAME}; + + return ".*:off:toggle" if( ReadingsVal($name,"state","off") eq "off" ); + return ".*:on:toggle" if( ReadingsVal($name,"effect","*Solid*") ne "*Solid*" ); + + my $pct = ReadingsVal($name,"pct","100"); + my $s = $dim_values{int($pct/7)}; + $s="on" if( $pct eq "100" ); + + #return ".*:$s:toggle" if( AttrVal($name, "model", "") eq "LWB001" ); + #return ".*:$s:toggle" if( AttrVal($name, "model", "") eq "LWB003" ); + #return ".*:$s:toggle" if( AttrVal($name, "model", "") eq "LWB004" ); + + + return ".*:$s@#".CommandGet("","$name RGB").":toggle" if( $pct < 100 && AttrVal($name, "color-icons", 0) == 2 ); + return ".*:on@#".CommandGet("","$name rgb").":toggle" if( AttrVal($name, "color-icons", 0) != 0 ); + + return '
'; +} +sub +Aurora_summaryFn($$$$) +{ + my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn. + my $hash = $defs{$d}; + my $name = $hash->{NAME}; + + return Aurora_devStateIcon($hash); +} + +sub +Aurora_Define($$) +{ + my ($hash, $def) = @_; + + my @args = split("[ \t]+", $def); + + return "Usage: define Aurora [interval]" if(@args < 3); + + my ($name, $type, $ip, $interval) = @args; + + $hash->{STATE} = 'Initialized'; + + #pair & get mac + $hash->{IP} = $ip; + + my $code = $hash->{IP}; + my $d = $modules{Aurora}{defptr}{$code}; + return "Aurora device $hash->{ID} already defined as $d->{NAME}." + if( defined($d) && $d->{NAME} ne $name ); + + $modules{Aurora}{defptr}{$code} = $hash; + + $args[3] = "" if( !defined( $args[3] ) ); + $interval = 60 if( defined($interval) && $interval < 10 ); + $hash->{INTERVAL} = $interval; + + $hash->{helper}{on} = -1; + $hash->{helper}{colormode} = ''; + $hash->{helper}{ct} = -1; + $hash->{helper}{hue} = -1; + $hash->{helper}{sat} = -1; + $hash->{helper}{xy} = ''; + $hash->{helper}{effect} = ''; + + $hash->{helper}{pct} = -1; + $hash->{helper}{rgb} = ""; + + $attr{$name}{devStateIcon} = '{(Aurora_devStateIcon($name),"toggle")}' if( !defined( $attr{$name}{devStateIcon} ) ); + + my $icon_path = AttrVal("WEB", "iconPath", "default:fhemSVG:openautomation" ); + $attr{$name}{'color-icons'} = 2 if( !defined( $attr{$name}{'color-icons'} ) && $icon_path =~ m/openautomation/ ); + + RemoveInternalTimer($hash); + if( $init_done ) { + Aurora_OpenDev($hash); + } else { + #InternalTimer(gettimeofday()+10, "Aurora_GetUpdate", $hash, 0); + } + + return undef; +} + +sub +Aurora_Undefine($$) +{ + my ($hash,$arg) = @_; + + RemoveInternalTimer($hash); + + my $code = $hash->{IP}; + delete($modules{Aurora}{defptr}{$code}); + + return undef; +} + + +sub +Aurora_OpenDev($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + + if( !AttrVal($name, 'token', undef) ) { + Aurora_Pair($hash); + } else { + RemoveInternalTimer($hash); + Aurora_GetUpdate($hash); + } + return undef; + + Aurora_Detect($hash) if( defined($hash->{NUPNP}) ); + + my $result = Aurora_Call($hash, undef, 'config', undef); + if( !defined($result) ) { + Log3 $name, 2, "Aurora_OpenDev: got empty config"; + return undef; + } + Log3 $name, 5, "Aurora_OpenDev: got config " . Dumper $result; + + if( !defined($result->{'linkbutton'}) || !AttrVal($name, 'key', undef) ) + { + Aurora_fillBridgeInfo($hash, $result); + + Aurora_Pair($hash); + return; + } + + $hash->{mac} = $result->{'mac'}; + + readingsSingleUpdate($hash, 'state', 'connected', 1 ); + Aurora_GetUpdate($hash); + + Aurora_Autocreate($hash); + + return undef; +} + +sub +Aurora_Pair($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + + readingsSingleUpdate($hash, 'state', 'pairing', 1 ); + + my($err,$data) = HttpUtils_NonblockingGet({ + url => "http://$hash->{IP}:16021/api/v1/new", + timeout => 2, + method => 'POST', + noshutdown => $hash->{noshutdown}, + hash => $hash, + type => 'pair', + callback => \&Aurora_dispatch, + }); + + return undef; + + + my $result = Aurora_Register($hash); + if( $result->{'error'} ) + { + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+5, "Aurora_Pair", $hash, 0); + + return undef; + } + + $attr{$name}{token} = $result->{success}{username} if( $result->{success}{username} ); + + readingsSingleUpdate($hash, 'state', 'paired', 1 ); + + Aurora_OpenDev($hash); + + return undef; +} + +sub +Aurora_dispatch($$$;$) +{ + my ($param, $err, $data) = @_; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; + + my $json; + $json = eval { decode_json($data) } if( $data ); + Log3 $name, 2, "$name: json error: $@ in $data" if( $@ ); + +#Log 1, " $err"; +#Log 1, " $data"; +#Log 1, " $json"; + + if( $param->{type} eq 'pair' ) { + if( !$json ) { + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+5, "Aurora_Pair", $hash, 0); + + return undef; + } else { + $attr{$name}{token} = $json->{auth_token} if( $json->{auth_token} ); + Aurora_GetUpdate($hash); + } + } + #return undef if( !$json ); + + if( $param->{type} eq 'state' ) { + if( $param->{method} eq 'GET' ) { + Aurora_Parse($hash, $json) if( $json ); + } else { + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+1, "Aurora_GetUpdate", $hash, 0); + } + } elsif( $param->{type} eq 'effects' ) { + $hash->{helper}{effects} = $json->{list}; + if( my $effect = $json->{select} ) { + if( $effect ne $hash->{helper}{effect} ) { readingsSingleUpdate($hash, 'effect', $effect, 1 ) }; + $hash->{helper}{effect} = $effect; + } + } +} + + + + +sub +Aurora_SetParam($$@) +{ + my ($name, $obj, $cmd, $value, $value2) = @_; + + if( $cmd eq "color" ) { + $value = int(1000000/$value); + $cmd = 'ct'; + } elsif( $name && $cmd eq "toggle" ) { + $cmd = ReadingsVal($name,"onoff",1) ? "off" :"on"; + } elsif( $cmd =~ m/^dim(\d+)/ ) { + $value2 = $value; + $value = $1; + $value = 0 if( $value < 0 ); + $value = 100 if( $value > 100 ); + $cmd = 'pct'; + } elsif( !defined($value) && $cmd =~ m/^(\d+)/) { + $value2 = $value; + $value = $1; + $value = 0 if( $value < 0 ); + $value = 100 if( $value > 100 ); + $cmd = 'pct'; + } + + $cmd = "off" if($cmd eq "pct" && $value == 0 ); + + if($cmd eq 'on') { + $obj->{'on'} = JSON::true; + $obj->{'transitiontime'} = $value * 10 if( defined($value) ); + + } elsif($cmd eq 'off') { + $obj->{'on'} = JSON::false; + $obj->{'transitiontime'} = $value * 10 if( defined($value) ); + + } elsif($cmd eq "pct") { + $value = 0 if( $value < 0 ); + $value = 100 if( $value > 100 ); + $obj->{'on'} = JSON::true; + $obj->{'brightness'} = int($value); + $obj->{'transitiontime'} = $value2 * 10 if( defined($value2) ); + + } elsif($name && $cmd eq "dimUp") { + my $pct = ReadingsVal($name,"pct","0"); + $pct += 10; + $pct = 100 if( $pct > 100 ); + $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} ); + $obj->{'brightness'} = 0+$pct; + $obj->{'transitiontime'} = 1; + #$obj->{'transitiontime'} = $value * 10 if( defined($value) ); + $defs{$name}->{helper}->{update_timeout} = 0; + + } elsif($name && $cmd eq "dimDown") { + my $pct = ReadingsVal($name,"pct","0"); + $pct -= 10; + $pct = 0 if( $pct < 0 ); + $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} ); + $obj->{'brightness'} = 0+$pct; + $obj->{'transitiontime'} = 1; + #$obj->{'transitiontime'} = $value * 10 if( defined($value) ); + $defs{$name}->{helper}->{update_timeout} = 0; + + } elsif($cmd eq "satUp") { + $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} ); + $obj->{'sat_inc'} = 10; + $obj->{'sat_inc'} = 0+$value if( defined($value) ); + } elsif($cmd eq "satDown") { + $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} ); + $obj->{'sat_inc'} = -10; + $obj->{'sat_inc'} = 0+$value if( defined($value) ); + + } elsif($cmd eq "hueUp") { + $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} ); + $obj->{'hue_inc'} = 30; + $obj->{'hue_inc'} = 0+$value if( defined($value) ); + } elsif($cmd eq "hueDown") { + $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} ); + $obj->{'hue_inc'} = -30; + $obj->{'hue_inc'} = 0+$value if( defined($value) ); + + } elsif($cmd eq "ctUp") { + $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} ); + $obj->{'ct_inc'} = 16; + $obj->{'ct_inc'} = 0+$value if( defined($value) ); + } elsif($cmd eq "ctDown") { + $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} ); + $obj->{'ct_inc'} = -16; + $obj->{'ct_inc'} = 0+$value if( defined($value) ); + + } elsif($cmd eq "ct") { + $obj->{'on'} = JSON::true; + $value = int(1000000/$value) if( $value < 1000 ); + $obj->{'ct'} = 0+$value; + $obj->{'transitiontime'} = $value2 * 10 if( defined($value2) ); + } elsif($cmd eq "hue") { + $obj->{'on'} = JSON::true; + $obj->{'hue'} = 0+$value; + $obj->{'transitiontime'} = $value2 * 10 if( defined($value2) ); + } elsif($cmd eq "sat") { + $obj->{'on'} = JSON::true; + $obj->{'sat'} = 0+$value; + $obj->{'transitiontime'} = $value2 * 10 if( defined($value2) ); + } elsif( $cmd eq "rgb" && $value =~ m/^(..)(..)(..)/) { + my( $r, $g, $b ) = (hex($1)/255.0, hex($2)/255.0, hex($3)/255.0); + + my( $h, $s, $v ) = Color::rgb2hsv($r,$g,$b); + + $obj->{'on'} = JSON::true; + $obj->{'hue'} = int( $h * 359 ); + $obj->{'sat'} = int( $s * 100 ); + $obj->{'brightness'} = int( $v * 100 ); + } elsif( $cmd eq "hsv" && $value =~ m/^(..)(..)(..)/) { + my( $h, $s, $v ) = (hex($1), hex($2), hex($3)); + + $s = 100 if( $s > 100 ); + $v = 100 if( $v > 100 ); + + $obj->{'on'} = JSON::true; + $obj->{'hue'} = int($h*100); + $obj->{'sat'} = 0+$s; + $obj->{'brightness'} = 0+$v; + } elsif( $cmd eq "effect" ) { + $obj->{'select'} = "$value"; + $obj->{'select'} .= " $value2" if( $value2 ); + } elsif( $cmd eq "transitiontime" ) { + $obj->{'transitiontime'} = 0+$value; + } elsif( $name && $cmd eq "delayedUpdate" ) { + $defs{$name}->{helper}->{update_timeout} = 1; + } elsif( $name && $cmd eq "immediateUpdate" ) { + $defs{$name}->{helper}->{update_timeout} = 0; + } elsif( $name && $cmd eq "noUpdate" ) { + $defs{$name}->{helper}->{update_timeout} = -1; + } else { + return 0; + } + + return 1; +} +sub +Aurora_Set($@) +{ + my ($hash, $name, @aa) = @_; + my ($cmd, @args) = @aa; + + my %obj; + + $hash->{helper}->{update_timeout} = AttrVal($name, "delayedUpdate", 1); + + if( (my $joined = join(" ", @aa)) =~ /:/ ) { + my @cmds = split(":", $joined); + for( my $i = 0; $i <= $#cmds; ++$i ) { + Aurora_SetParam($name, \%obj, split(" ", $cmds[$i]) ); + } + } else { + my ($cmd, $value, $value2, @a) = @aa; + + if( $cmd eq "statusRequest" ) { + RemoveInternalTimer($hash); + Aurora_GetUpdate($hash); + return undef; + } + + Aurora_SetParam($name, \%obj, $cmd, $value, $value2); + } +#Log 1, Dumper \%obj; + + if( %obj ) { + if( defined($obj{on}) ) { + $hash->{desired} = $obj{on}?1:0; + } + + if( !defined($obj{transitiontime}) ) { + my $transitiontime = AttrVal($name, "transitiontime", undef); + + $obj{transitiontime} = 0 + $transitiontime if( defined( $transitiontime ) ); + } + } + + if( scalar keys %obj ) { + my($err,$data) = HttpUtils_NonblockingGet({ + url => "http://$hash->{IP}:16021/api/v1/$attr{$name}{token}/".($obj{select}?"effects":"state"), + timeout => 2, + method => 'PUT', + noshutdown => $hash->{noshutdown}, + hash => $hash, + type => 'state', + data => encode_json(\%obj), + callback => \&Aurora_dispatch, + }); + + SetExtensionsCancel($hash); + + $hash->{".triggerUsed"} = 1; + + return undef; + } + + my $list = "off:noArg on:noArg toggle:noArg statusRequest:noArg"; + $list .= " pct:colorpicker,BRI,0,1,100"; + $list .= " rgb:colorpicker,RGB"; + $list .= " color:colorpicker,CT,1200,10,6500"; + $list .= " hue:colorpicker,HUE,0,1,359 sat:slider,0,1,100"; + + $list .= " dimUp:noArg dimDown:noArg"; + + #$list .= " alert:none,select,lselect"; + + #$list .= " dim06% dim12% dim18% dim25% dim31% dim37% dim43% dim50% dim56% dim62% dim68% dim75% dim81% dim87% dim93% dim100%"; + + if( $hash->{helper}{effects} ) { + my $effects = join(',',@{$hash->{helper}{effects}}); + $effects =~ s/\s/#/g; + $list .= " effect:,$effects"; + } + + return SetExtensions($hash, $list, $name, @aa); +} + +sub +cttorgb($) +{ + my ($ct) = @_; + + # calculation from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code + # adjusted by 1000K + my $temp = (1000000/$ct)/100 + 10; + + my $r = 0; + my $g = 0; + my $b = 0; + + $r = 255; + $r = 329.698727446 * ($temp - 60) ** -0.1332047592 if( $temp > 66 ); + $r = 0 if( $r < 0 ); + $r = 255 if( $r > 255 ); + + if( $temp <= 66 ) { + $g = 99.4708025861 * log($temp) - 161.1195681661; + } else { + $g = 288.1221695283 * ($temp - 60) ** -0.0755148492; + } + $g = 0 if( $g < 0 ); + $g = 255 if( $g > 255 ); + + $b = 255; + $b = 0 if( $temp <= 19 ); + if( $temp < 66 ) { + $b = 138.5177312231 * log($temp-10) - 305.0447927307; + } + $b = 0 if( $b < 0 ); + $b = 255 if( $b > 255 ); + + return( $r, $g, $b ); +} + +sub +xyYtorgb($$$) +{ + # calculation from http://www.brucelindbloom.com/index.html + my ($x,$y,$Y) = @_; +#Log 3, "xyY:". $x . " " . $y ." ". $Y; + + my $r = 0; + my $g = 0; + my $b = 0; + + if( $y > 0 ) { + my $X = $x * $Y / $y; + my $Z = (1 - $x - $y)*$Y / $y; + + if( $X > 1 + || $Y > 1 + || $Z > 1 ) { + my $f = maxNum($X,$Y,$Z); + $X /= $f; + $Y /= $f; + $Z /= $f; + } +#Log 3, "XYZ: ". $X . " " . $Y ." ". $Y; + + $r = 0.7982 * $X + 0.3389 * $Y - 0.1371 * $Z; + $g = -0.5918 * $X + 1.5512 * $Y + 0.0406 * $Z; + $b = 0.0008 * $X + 0.0239 * $Y + 0.9753 * $Z; + + if( $r > 1 + || $g > 1 + || $b > 1 ) { + my $f = maxNum($r,$g,$b); + $r /= $f; + $g /= $f; + $b /= $f; + } +#Log 3, "rgb: ". $r . " " . $g ." ". $b; + + $r *= 255; + $g *= 255; + $b *= 255; + } + + return( $r, $g, $b ); +} + +sub +Aurora_Get($@) +{ + my ($hash, @a) = @_; + + my $name = $a[0]; + return "$name: get needs at least one parameter" if(@a < 2); + + my $cmd= $a[1]; + + if($cmd eq "rgb") { + my $r = 0; + my $g = 0; + my $b = 0; + + my $cm = ReadingsVal($name,"colormode",""); + if( $cm eq "ct" ) { + if( ReadingsVal($name,"ct","") =~ m/(\d+)/ ) { + ($r,$g,$b) = cttorgb(1000000/$1); + } + } else { + my $h = ReadingsVal($name,"hue",0) / 359.0; + my $s = ReadingsVal($name,"sat",0) / 100.0; + my $v = ReadingsVal($name,"pct",0) / 100.0; + ($r,$g,$b) = Color::hsv2rgb($h,$s,$v); + + $r *= 255; + $g *= 255; + $b *= 255; + } + return sprintf( "%02x%02x%02x", $r+0.5, $g+0.5, $b+0.5 ); + } elsif($cmd eq "RGB") { + my $r = 0; + my $g = 0; + my $b = 0; + + my $cm = ReadingsVal($name,"colormode",""); + if( $cm eq "ct" ) { + if( ReadingsVal($name,"ct","") =~ m/(\d+) .*/ ) { + ($r,$g,$b) = cttorgb($1); + } + } else { + my $h = ReadingsVal($name,"hue",0) / 359.0; + my $s = ReadingsVal($name,"sat",0) / 100.0; + my $v = 1; + ($r,$g,$b) = Color::hsv2rgb($h,$s,$v); + + $r *= 255; + $g *= 255; + $b *= 255; + } + return sprintf( "%02x%02x%02x", $r+0.5, $g+0.5, $b+0.5 ); + } elsif ( $cmd eq "devStateIcon" ) { + return Aurora_devStateIcon($hash); + } + + return "Unknown argument $cmd, choose one of rgb:noArg RGB:noArg devStateIcon:noArg"; +} + + +################################### +# This could be IORead in fhem, But there is none. +# Read http://forum.fhem.de/index.php?t=tree&goto=54027&rid=10#msg_54027 +# to find out why. +sub +Aurora_ReadFromServer($@) +{ + my ($hash,@a) = @_; + + my $name = $hash->{NAME}; + no strict "refs"; + my $ret; + unshift(@a,$name); + #$ret = IOWrite($hash, @a); + $ret = IOWrite($hash,$hash,@a); + use strict "refs"; + return $ret; + return if(IsDummy($name) || IsIgnored($name)); + my $iohash = $hash->{IODev}; + if(!$iohash || + !$iohash->{TYPE} || + !$modules{$iohash->{TYPE}} || + !$modules{$iohash->{TYPE}}{ReadFn}) { + Log3 $name, 5, "No I/O device or ReadFn found for $name"; + return; + } + + no strict "refs"; + #my $ret; + unshift(@a,$name); + $ret = &{$modules{$iohash->{TYPE}}{ReadFn}}($iohash, @a); + use strict "refs"; + return $ret; +} + +sub +Aurora_GetUpdate($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + + if(!$hash->{LOCAL}) { + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "Aurora_GetUpdate", $hash, 0) if( $hash->{INTERVAL} ); + } + + return undef if(IsDisabled($name)); + + my($err,$data) = HttpUtils_NonblockingGet({ + url => "http://$hash->{IP}:16021/api/v1/$attr{$name}{token}/state", + timeout => 2, + method => 'GET', + noshutdown => $hash->{noshutdown}, + hash => $hash, + type => 'state', + callback => \&Aurora_dispatch, + }); + + ($err,$data) = HttpUtils_NonblockingGet({ + url => "http://$hash->{IP}:16021/api/v1/$attr{$name}{token}/effects", + timeout => 2, + method => 'GET', + noshutdown => $hash->{noshutdown}, + hash => $hash, + type => 'effects', + callback => \&Aurora_dispatch, + }); + + + return undef; +} + +sub +AuroraSetIcon($;$) +{ + my ($hash,$force) = @_; + $hash = $defs{$hash} if( ref($hash) ne 'HASH' ); + + return undef if( !$hash ); + my $name = $hash->{NAME}; + + return if( defined($attr{$name}{icon}) && !$force ); +} +sub +Aurora_Parse($$) +{ + my($hash,$result) = @_; + my $name = $hash->{NAME}; + + if( ref($result) ne "HASH" ) { + if( ref($result) && $Aurora_hasDataDumper) { + Log3 $name, 2, "$name: got wrong status message for $name: ". Dumper $result; + } else { + Log3 $name, 2, "$name: got wrong status message for $name: $result"; + } + return undef; + } + + Log3 $name, 4, "parse status message for $name"; + Log3 $name, 5, Dumper $result if($Aurora_hasDataDumper); + + $hash->{name} = $result->{name} if( defined($result->{name}) ); + $hash->{type} = $result->{type} if( defined($result->{type}) ); + $hash->{class} = $result->{class} if( defined($result->{class}) ); + $hash->{uniqueid} = $result->{uniqueid} if( defined($result->{uniqueid}) ); + + $hash->{modelid} = $result->{modelid} if( defined($result->{modelid}) ); + $hash->{productid} = $result->{productid} if( defined($result->{productid}) ); + $hash->{swversion} = $result->{swversion} if( defined($result->{swversion}) ); + $hash->{swconfigid} = $result->{swconfigid} if( defined($result->{swconfigid}) ); + $hash->{manufacturername} = $result->{manufacturername} if( defined($result->{manufacturername}) ); + $hash->{luminaireuniqueid} = $result->{luminaireuniqueid} if( defined($result->{luminaireuniqueid}) ); + + $attr{$name}{model} = $result->{modelid} if( !defined($attr{$name}{model}) && $result->{modelid} ); + + $attr{$name}{devStateIcon} = '{(Aurora_devStateIcon($name),"toggle")}' if( !defined( $attr{$name}{devStateIcon} ) ); + + if( !defined($attr{$name}{webCmd}) ) { + $attr{$name}{webCmd} = 'rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:ct 490:ct 380:ct 270:ct 160:effect:on:off'; + #$attr{$name}{webCmd} = 'hue:rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:toggle:on:off'; + #$attr{$name}{webCmd} = 'ct:ct 490:ct 380:ct 270:ct 160:toggle:on:off'; + #$attr{$name}{webCmd} = 'pct:toggle:on:off'; + #$attr{$name}{webCmd} = 'toggle:on:off'; + } + + readingsBeginUpdate($hash); + + my $state = $result; + + my $on = $state->{on}{value}; + $on = $hash->{helper}{on} if( !defined($on) ); + my $colormode = $state->{'colorMode'}; + my $pct = $state->{'brightness'}{value}; + $pct = $hash->{helper}{pct} if( !defined($pct) ); + my $ct = $state->{'ct'}{value}; + my $hue = $state->{'hue'}{value}; + my $sat = $state->{'sat'}{value}; + my $alert = $state->{alert}; + my $effect = $state->{effect}; + + if( defined($colormode) && $colormode ne $hash->{helper}{colormode} ) {readingsBulkUpdate($hash,"colormode",$colormode);} + if( defined($ct) && $ct != $hash->{helper}{ct} ) { + if( $ct == 0 ) { + readingsBulkUpdate($hash,"ct",$ct); + } + else { + readingsBulkUpdate($hash,"ct",$ct); + } + } + if( defined($hue) && $hue != $hash->{helper}{hue} ) {readingsBulkUpdate($hash,"hue",$hue);} + if( defined($sat) && $sat != $hash->{helper}{sat} ) {readingsBulkUpdate($hash,"sat",$sat);} + if( defined($alert) && $alert ne $hash->{helper}{alert} ) {readingsBulkUpdate($hash,"alert",$alert);} + if( defined($effect) && $effect ne $hash->{helper}{effect} ) {readingsBulkUpdate($hash,"effect",$effect);} + + my $s = ''; + if( $on ) + { + $s = 'on'; + if( $on != $hash->{helper}{on} ) {readingsBulkUpdate($hash,"onoff",1);} + + $s = 'off' if( $pct == 0 ); + + } + else + { + $on = 0; + $s = 'off'; + $pct = 0; + if( $on != $hash->{helper}{on} ) {readingsBulkUpdate($hash,"onoff",0);} + } + + if( $pct != $hash->{helper}{pct} ) {readingsBulkUpdate($hash,"pct", $pct);} + #if( $pct != $hash->{helper}{pct} ) {readingsBulkUpdate($hash,"level", $pct . ' %');} + + $hash->{helper}{on} = $on if( defined($on) ); + $hash->{helper}{colormode} = $colormode if( defined($colormode) ); + $hash->{helper}{ct} = $ct if( defined($ct) ); + $hash->{helper}{hue} = $hue if( defined($hue) ); + $hash->{helper}{sat} = $sat if( defined($sat) ); + $hash->{helper}{alert} = $alert if( defined($alert) ); + $hash->{helper}{effect} = $effect if( defined($effect) ); + + $hash->{helper}{pct} = $pct; + + my $changed = $hash->{CHANGED}?1:0; + + if( $s ne $hash->{STATE} ) {readingsBulkUpdate($hash,"state",$s);} + + readingsEndUpdate($hash,1); + + if( defined($colormode) ) { + my $rgb = CommandGet("","$name rgb"); + if( $rgb ne $hash->{helper}{rgb} ) { readingsSingleUpdate($hash,"rgb", $rgb,1); }; + $hash->{helper}{rgb} = $rgb; + } + + $hash->{helper}->{update_timeout} = -1; + #RemoveInternalTimer($hash); + + return $changed; +} + +sub +Aurora_Attr($$$;$) +{ + my ($cmd, $name, $attrName, $attrVal) = @_; + + return; +} + +1; + +=pod +=item summary nanoleaf aurora +=item summary_DE nanoleaf aurora +=begin html + + +

Aurora

+
+ +=end html +=cut diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index ff27da563..3c521a6d9 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -146,6 +146,7 @@ FHEM/30_pilight_temp.pm risiko http://forum.fhem.de Sonstige FHEM/30_pilight_raw.pm risiko http://forum.fhem.de Sonstige Systeme FHEM/30_pilight_smoke.pm risiko http://forum.fhem.de Sonstige Systeme FHEM/30_pilight_contact.pm risiko http://forum.fhem.de Sonstige Systeme +FHEM/31_Aurora.pm justme1968 http://forum.fhem.de Beleuchtung FHEM/31_HUEDevice.pm justme1968 http://forum.fhem.de Beleuchtung FHEM/31_Nello.pm neumann http://forum.fhem.de Sonstige Systeme FHEM/31_PLAYBULB.pm CoolTux http://forum.fhem.de Beleuchtung