From 176b733473775265f458d4eee84263ab7675ac19 Mon Sep 17 00:00:00 2001 From: loredo Date: Sat, 8 Mar 2014 21:52:34 +0000 Subject: [PATCH] PHTV: add ambiPreset and individual control of Ambilight LEDs git-svn-id: https://svn.fhem.de/fhem/trunk@5168 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/70_PHTV.pm | 664 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 611 insertions(+), 53 deletions(-) diff --git a/fhem/FHEM/70_PHTV.pm b/fhem/FHEM/70_PHTV.pm index c46e7766f..28ece4343 100644 --- a/fhem/FHEM/70_PHTV.pm +++ b/fhem/FHEM/70_PHTV.pm @@ -24,7 +24,7 @@ # along with fhem. If not, see . # # -# Version: 1.1.1 +# Version: 1.1.2 # # Major Version History: # - 1.1.0 - 2014-03-07 @@ -286,7 +286,7 @@ sub PHTV_Set($@) { my $usage = "Unknown argument " . $a[1] - . ", choose one of statusRequest:noArg toggle:noArg on:noArg off:noArg play:noArg pause:noArg stop:noArg record:noArg volume:slider,1,1,100 volumeUp:noArg volumeDown:noArg channelUp:noArg channelDown:noArg remoteControl ambiHue:off,on ambiMode:internal,manual,expert rgb:colorpicker,RGB pct:slider,0,1,100"; + . ", choose one of statusRequest:noArg toggle:noArg on:noArg off:noArg play:noArg pause:noArg stop:noArg record:noArg volume:slider,1,1,100 volumeUp:noArg volumeDown:noArg channelUp:noArg channelDown:noArg remoteControl ambiHue:off,on ambiMode:internal,manual,expert ambiPreset:rainbow,rainbow-pastel rgb:colorpicker,rgb"; $usage .= " volumeStraight:slider," . $hash->{helper}{audio}{min} . ",1," @@ -311,6 +311,8 @@ sub PHTV_Set($@) { delete $hash->{helper}{device} if ( defined( $hash->{helper}{device} ) ); + delete $hash->{helper}{supportedAPIcmds} + if ( defined( $hash->{helper}{supportedAPIcmds} ) ); PHTV_GetStatus($hash); } @@ -397,15 +399,177 @@ sub PHTV_Set($@) { } } - # rgb - elsif ( $a[1] eq "rgb" ) { + # ambiPreset + elsif ( lc( $a[1] ) eq "ambipreset" ) { Log3 $name, 2, "PHTV set $name " . $a[1] . " " . $a[2]; return "No argument given" if ( !defined( $a[2] ) ); if ( $hash->{READINGS}{state}{VAL} eq "on" ) { + + if ( defined( $hash->{READINGS}{ambiLEDLayers}{VAL} ) ) { + my $json; + + # rainbow + if ( $a[2] eq "rainbow" ) { + my $layer = ( $a[3] ) ? $a[3] : 1; + + return "Layer $layer is not numeric" + if ( !PHTV_isinteger($layer) ); + + return "Layer $layer is not existing" + if ( $layer > $hash->{READINGS}{ambiLEDLayers}{VAL} ); + + while ( $layer <= $hash->{READINGS}{ambiLEDLayers}{VAL} ) { + my $rgb; + + foreach my $side ( 'Left', 'Top', 'Right', 'Bottom' ) { + my $ambiLED = "ambiLED$side"; + my $side = lc($side); + + my $l = "layer" . $layer; + + if ( defined( $hash->{READINGS}{$ambiLED}{VAL} ) + && $hash->{READINGS}{$ambiLED}{VAL} > 0 ) + { + $rgb = { "r" => 255, "g" => 0, "b" => 0 } + if ( $side eq "left" || $side eq "right" ); + + # run clockwise for left and top + if ( $side eq "left" || $side eq "top" ) { + my $led = 0; + while ( $led <= + $hash->{READINGS}{$ambiLED}{VAL} - 1 ) + { + $json->{$l}{$side}{$led}{r} = $rgb->{r}; + $json->{$l}{$side}{$led}{g} = $rgb->{g}; + $json->{$l}{$side}{$led}{b} = $rgb->{b}; + + if ( $rgb->{r} == 255 ) { + $rgb = { + "r" => 0, + "g" => 255, + "b" => 0 + }; + } + elsif ( $rgb->{g} == 255 ) { + $rgb = { + "r" => 0, + "g" => 0, + "b" => 255 + }; + } + elsif ( $rgb->{b} == 255 ) { + $rgb = { + "r" => 255, + "g" => 0, + "b" => 0 + }; + } + + $led++; + } + } + + # run anti-clockwise for right and bottom + elsif ( $side eq "right" || $side eq "bottom" ) + { + my $led = + $hash->{READINGS}{$ambiLED}{VAL} - 1; + while ( $led >= 0 ) { + $json->{$l}{$side}{$led}{r} = $rgb->{r}; + $json->{$l}{$side}{$led}{g} = $rgb->{g}; + $json->{$l}{$side}{$led}{b} = $rgb->{b}; + + if ( $rgb->{r} == 255 ) { + $rgb = { + "r" => 0, + "g" => 255, + "b" => 0 + }; + } + elsif ( $rgb->{g} == 255 ) { + $rgb = { + "r" => 0, + "g" => 0, + "b" => 255 + }; + } + elsif ( $rgb->{b} == 255 ) { + $rgb = { + "r" => 255, + "g" => 0, + "b" => 0 + }; + } + + $led--; + } + } + + } + } + + last if ( defined( $a[3] ) ); + $layer++; + } + + # enable manual Ambilight color + PHTV_SendCommand( $hash, "ambilight/mode", + '"current": "manual"', "manual" ) + if ( $hash->{READINGS}{ambiMode}{VAL} ne "manual" ); + } + + # rainbow-pastel + elsif ( $a[2] eq "rainbow-pastel" ) { + my $layer = ( $a[3] ) ? $a[3] : 1; + + return "Layer $layer is not numeric" + if ( !PHTV_isinteger($layer) ); + + return "Layer $layer is not existing" + if ( $layer > $hash->{READINGS}{ambiLEDLayers}{VAL} ); + + PHTV_Set( $hash, $name, "ambiPreset", "rainbow" ); + fhem("sleep 0.5"); + + # enable manual Ambilight color + PHTV_SendCommand( $hash, "ambilight/mode", + '"current": "expert"', "expert" ) + if ( $hash->{READINGS}{ambiMode}{VAL} ne "expert" ); + } + + # unknown preset + else { + return "Unknown preset, choose one of rainbow"; + } + + PHTV_SendCommand( $hash, "ambilight/cached", $json ); + } + else { + return "Devices does not seem to support Ambilight."; + } + + } + else { + return "Device needs to be ON to control Ambilight mode."; + } + } + + # rgb + elsif ( $a[1] eq "rgb" ) { + Log3 $name, 4, "PHTV set $name " . $a[1] . " " . $a[2]; + + return "No argument given" if ( !defined( $a[2] ) ); + + if ( $hash->{READINGS}{state}{VAL} eq "on" ) { + + # set all LEDs at once if ( uc( $a[2] ) =~ /^(..)(..)(..)$/ ) { my $json; + my $hsv; + my $hue; + my $sat; my $bri; my $pct; my ( $r, $g, $b ) = ( hex($1), hex($2), hex($3) ); @@ -414,7 +578,10 @@ sub PHTV_Set($@) { $json .= '"r": ' . $r . ','; $json .= '"g": ' . $g . ','; $json .= '"b": ' . $b; - $bri = PHTV_rgb2hsv( $r, $g, $b, "v" ); + $hsv = PHTV_rgb2hsv( $r, $g, $b ); + $hue = $hsv->{h}; + $sat = int( $hsv->{s} * 100 + 0.5 ); + $bri = $hsv->{v}; $pct = PHTV_bri2pct($bri); PHTV_SendCommand( $hash, "ambilight/cached", $json, uc( $a[2] ) ); @@ -422,7 +589,7 @@ sub PHTV_Set($@) { # enable manual Ambilight color if RGB!=000000 PHTV_SendCommand( $hash, "ambilight/mode", '"current": "manual"', "manual" ) - if ( $hash->{READINGS}{ambiMode}{VAL} ne "manual" + if ( $hash->{READINGS}{ambiMode}{VAL} eq "internal" && $rgbsum > 0 ); # disable manual Ambilight color if RGB=000000 @@ -436,12 +603,101 @@ sub PHTV_Set($@) { if ( $hash->{READINGS}{pct}{VAL} ne $pct ); readingsBulkUpdate( $hash, "level", $pct . " %" ) if ( $hash->{READINGS}{level}{VAL} ne $pct . " %" ); + readingsBulkUpdate( $hash, "hue", $hue ) + if ( $hash->{READINGS}{hue}{VAL} ne $hue ); + readingsBulkUpdate( $hash, "sat", $sat ) + if ( $hash->{READINGS}{sat}{VAL} ne $sat ); readingsBulkUpdate( $hash, "bri", $bri ) if ( $hash->{READINGS}{bri}{VAL} ne $bri ); readingsBulkUpdate( $hash, "rgb", uc( $a[2] ) ) if ( $hash->{READINGS}{rgb}{VAL} ne uc( $a[2] ) ); readingsEndUpdate( $hash, 1 ); } + + # direct control per LED + elsif ( uc( $a[2] ) =~ /^L[1-9].*/ ) { + my $json; + my $rgbsum = 0; + my $i = 2; + + while ( exists( $a[$i] ) ) { + my ( $layer, $side, $led, $rgb ); + my ( $addr, $hex ) = split( ':', $a[$i] ); + + # calculate LED address + $layer = "layer" . substr( $addr, 1, 1 ) + if ( length($addr) > 1 + && PHTV_isinteger( substr( $addr, 1, 1 ) ) ); + if ( length($addr) > 2 ) { + $side = "left" if ( substr( $addr, 2, 1 ) eq "L" ); + $side = "top" if ( substr( $addr, 2, 1 ) eq "T" ); + $side = "right" if ( substr( $addr, 2, 1 ) eq "R" ); + $side = "bottom" if ( substr( $addr, 2, 1 ) eq "B" ); + } + $led = substr( $addr, 3 ) + if ( length($addr) > 3 + && PHTV_isinteger( substr( $addr, 3 ) ) ); + + # get desired color + if ( defined($hex) ) { + if ( $hex =~ /^(..)(..)(..)$/ ) { + $rgb = PHTV_hex2rgb($hex); + } + else { + return + "Color " + . $hex + . " for address " + . $addr + . " is not in HEX format"; + } + } + else { + return + "Please add color in HEX format for address $addr"; + } + + # update json hash + if ( defined( $rgb->{r} ) + && defined( $rgb->{g} ) + && defined( $rgb->{b} ) ) + { + $rgbsum += $rgb->{r} + $rgb->{g} + $rgb->{b}; + + if ( defined($led) + && defined($side) + && defined($layer) ) + { + $json->{$layer}{$side}{$led}{r} = $rgb->{r}; + $json->{$layer}{$side}{$led}{g} = $rgb->{g}; + $json->{$layer}{$side}{$led}{b} = $rgb->{b}; + } + elsif ( defined($side) && defined($layer) ) { + $json->{$layer}{$side}{r} = $rgb->{r}; + $json->{$layer}{$side}{g} = $rgb->{g}; + $json->{$layer}{$side}{b} = $rgb->{b}; + } + elsif ( defined($layer) ) { + $json->{$layer}{r} = $rgb->{r}; + $json->{$layer}{g} = $rgb->{g}; + $json->{$layer}{b} = $rgb->{b}; + } + else { + return "Invalid LED address format " . $addr; + } + } + + $i++; + } + + PHTV_SendCommand( $hash, "ambilight/cached", $json ); + + # enable manual Ambilight color if RGB!=000000 + PHTV_SendCommand( $hash, "ambilight/mode", + '"current": "manual"', "manual" ) + if ( $hash->{READINGS}{ambiMode}{VAL} eq "internal" + && $rgbsum > 0 ); + } else { return "Invalid RGB code"; } @@ -451,6 +707,35 @@ sub PHTV_Set($@) { } } + # pct + elsif ( $a[1] eq "pct" ) { + Log3 $name, 2, "PHTV set $name " . $a[1] . " " . $a[2]; + + return "No argument given" if ( !defined( $a[2] ) ); + + if ( $hash->{READINGS}{state}{VAL} eq "on" ) { + if ( defined( $hash->{READINGS}{rgb}{VAL} ) + && $hash->{READINGS}{rgb}{VAL} ne "" ) + { + my $_ = $a[2]; + my $rgb; + my $rgbnew; + if ( m/^\d+$/ && $_ >= 0 && $_ <= 100 ) { + $rgb = PHTV_hex2hsv( $hash->{READINGS}{rgb}{VAL} ); + $rgbnew = PHTV_hsv2hex( $rgb->{h}, $rgb->{s}, $_ ); + return PHTV_Set( $hash, $name, "rgb", $rgbnew ); + } + else { + return +"Argument does not seem to be a valid integer between 0 and 100"; + } + } + } + else { + return "Device needs to be ON to set Ambilight color."; + } + } + # volume elsif ( $a[1] eq "volume" ) { Log3 $name, 2, "PHTV set $name " . $a[1] . " " . $a[2]; @@ -474,7 +759,7 @@ sub PHTV_Set($@) { } else { return -"Argument does not seem to be a valid integer between 0 and 100"; +"Argument does not seem to be a valid integer between 1 and 100"; } PHTV_SendCommand( $hash, "audio/volume", $cmd ); @@ -740,8 +1025,8 @@ sub PHTV_Set($@) { } if ( $hash->{READINGS}{state}{VAL} eq "on" ) { - PHTV_SendCommand( $hash, "sources/current", '"id": ' . $input_id, - $input_id ); + PHTV_SendCommand( $hash, "sources/current", + '"id": ' . $input_id, $input_id ); if ( $hash->{READINGS}{input}{VAL} ne $a[2] ) { readingsSingleUpdate( $hash, "input", $a[2], 1 ); @@ -827,8 +1112,8 @@ sub PHTV_Define($$) { readingsSingleUpdate( $hash, "ambiHue", "off", 0 ); - $hash->{model} = $hash->{READINGS}{".model"}{VAL} - if ( defined( $hash->{READINGS}{".model"}{VAL} ) ); + $hash->{model} = $hash->{READINGS}{model}{VAL} + if ( defined( $hash->{READINGS}{model}{VAL} ) ); unless ( defined( AttrVal( $name, "webCmd", undef ) ) ) { $attr{$name}{webCmd} = 'volume:input:rgb'; @@ -860,8 +1145,15 @@ sub PHTV_SendCommand($$;$$) { my $name = $hash->{NAME}; my $address = $hash->{helper}{ADDRESS}; my $port = $hash->{helper}{PORT}; + my $data; my $timeout; - $cmd = ( defined($cmd) ) ? "{ " . $cmd . " }" : ""; + + if ( defined($cmd) && ref($cmd) eq "HASH" ) { + $data = encode_json($cmd); + } + elsif ( defined($cmd) && $cmd !~ /^{/ ) { + $data = "{ " . $cmd . " }"; + } Log3 $name, 5, "PHTV $name: called function PHTV_SendCommand()"; @@ -869,11 +1161,19 @@ sub PHTV_SendCommand($$;$$) { my $response; my $return; - if ( !defined($cmd) || $cmd eq "" ) { + if ( defined( $hash->{helper}{supportedAPIcmds}{$service} ) + && $hash->{helper}{supportedAPIcmds}{$service} == 0 ) + { + Log3 $name, 5, + "PHTV $name: API command '" . $service . "' not supported by device."; + return; + } + + if ( !defined($data) || ref($cmd) eq "HASH" && $data eq "" ) { Log3 $name, 4, "PHTV $name: REQ $service"; } else { - Log3 $name, 4, "PHTV $name: REQ $service/" . urlDecode($cmd); + Log3 $name, 4, "PHTV $name: REQ $service/" . urlDecode($data); } $URL = "http://" . $address . ":" . $port . "/1/" . $service; @@ -888,14 +1188,19 @@ sub PHTV_SendCommand($$;$$) { } # send request via HTTP-POST method - Log3 $name, 5, "PHTV $name: GET " . $URL . " (" . urlDecode($cmd) . ")"; + Log3 $name, 5, "PHTV $name: GET " . $URL . " (" . urlDecode($data) . ")" + if ( defined($data) && ref($cmd) ne "HASH" ); + Log3 $name, 5, "PHTV $name: GET " . $URL . " (#HASH)" + if ( defined($data) && ref($cmd) eq "HASH" ); + Log3 $name, 5, "PHTV $name: GET " . $URL + if ( !defined($data) ); HttpUtils_NonblockingGet( { url => $URL, timeout => $timeout, noshutdown => 1, - data => $cmd, + data => $data, hash => $hash, service => $service, cmd => $cmd, @@ -931,7 +1236,7 @@ sub PHTV_ReceiveCommand($$$) { $newstate = "absent"; - if ( !defined($cmd) || $cmd eq "" ) { + if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) { Log3 $name, 4, "PHTV $name: RCV TIMEOUT $service"; } else { @@ -962,7 +1267,7 @@ sub PHTV_ReceiveCommand($$$) { readingsBulkUpdate( $hash, "presence", "present" ); } - if ( !defined($cmd) || $cmd eq "" ) { + if ( !defined($cmd) || ref($cmd) eq "HASH" | $cmd eq "" ) { Log3 $name, 4, "PHTV $name: RCV $service"; } else { @@ -971,7 +1276,7 @@ sub PHTV_ReceiveCommand($$$) { if ( $data ne "" ) { if ( $data =~ /^{/ || $data =~ /^\[/ ) { - if ( !defined($cmd) || $cmd eq "" ) { + if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) { Log3 $name, 5, "PHTV $name: RES $service\n" . $data; } else { @@ -981,13 +1286,18 @@ sub PHTV_ReceiveCommand($$$) { . $data; } + $hash->{helper}{supportedAPIcmds}{$service} = 1 + if ( !defined( $hash->{helper}{supportedAPIcmds}{$service} ) + && $service !~ /^channels\/.*/ + && $service !~ /^channellists\/.*/ ); + $return = decode_json( Encode::encode_utf8($data) ); } elsif ( $data eq "OkOk" ) { - if ( !defined($cmd) || $cmd eq "" ) { + if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) { Log3 $name, 4, "PHTV $name: RES $service - ok"; } else { @@ -995,11 +1305,16 @@ sub PHTV_ReceiveCommand($$$) { "PHTV $name: RES $service/" . urlDecode($cmd) . " - ok"; } + $hash->{helper}{supportedAPIcmds}{$service} = 1 + if ( !defined( $hash->{helper}{supportedAPIcmds}{$service} ) + && $service !~ /^channels\/.*/ + && $service !~ /^channellists\/.*/ ); + $return = "ok"; } else { - if ( !defined($cmd) || $cmd eq "" ) { + if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) { Log3 $name, 5, "PHTV $name: RES ERROR $service\n" . $data; } else { @@ -1009,6 +1324,14 @@ sub PHTV_ReceiveCommand($$$) { . $data; } + if ( !defined( $hash->{helper}{supportedAPIcmds}{$service} ) ) { + $hash->{helper}{supportedAPIcmds}{$service} = 0; + Log3 $name, 3, + "PHTV $name: API command '" + . $service + . "' not supported by device."; + } + return undef; } } @@ -1029,7 +1352,9 @@ sub PHTV_ReceiveCommand($$$) { # calculate volume my $vol = ( $return->{current} ) ? $return->{current} : 0; - if ( defined( $return->{min} ) && defined( $return->{max} ) ) { + if ( defined( $return->{min} ) + && defined( $return->{max} ) ) + { $hash->{helper}{audio}{min} = $return->{min}; $hash->{helper}{audio}{max} = $return->{max}; @@ -1147,12 +1472,11 @@ sub PHTV_ReceiveCommand($$$) { # model if ( defined( $return->{model} ) - && ( !defined( $hash->{READINGS}{".model"}{VAL} ) - || $hash->{READINGS}{".model"}{VAL} ne - $return->{model} ) + && ( !defined( $hash->{READINGS}{model}{VAL} ) + || $hash->{READINGS}{model}{VAL} ne $return->{model} ) ) { - readingsBulkUpdate( $hash, ".model", $return->{model} ); + readingsBulkUpdate( $hash, "model", $return->{model} ); $hash->{model} = $return->{model}; } } @@ -1262,10 +1586,10 @@ sub PHTV_ReceiveCommand($$$) { $return->{id} =~ s/^\s+//; $return->{id} =~ s/\s+$//; $cmd = - ( $hash->{helper}{device}{channelName}{ $return->{id} } - {name} ) - ? $hash->{helper}{device}{channelName}{ $return->{id} } - {name} + ( $hash->{helper}{device}{channelName} + { $return->{id} }{name} ) + ? $hash->{helper}{device}{channelName} + { $return->{id} }{name} : "-"; } else { @@ -1568,10 +1892,11 @@ sub PHTV_ReceiveCommand($$$) { # ambilight/cached (rgb) elsif ( $service eq "ambilight/cached" ) { if ( ref($return) eq "HASH" ) { + my $rgb = ""; foreach my $layer ( keys $return ) { foreach my $side ( keys $return->{$layer} ) { foreach my $led ( keys $return->{$layer}{$side} ) { - my $rgb = ""; + my $hex = ""; my $l = $layer; my $s = $side; $l =~ s/layer/L/; @@ -1581,37 +1906,75 @@ sub PHTV_ReceiveCommand($$$) { $s =~ s/bottom/B/ if ( $side eq "bottom" ); my $readingname = "rgb_" . $l . $s . $led; - $rgb .= uc( - sprintf( "%02x", - $return->{$layer}{$side}{$led}{r} ) - ); - $rgb .= uc( - sprintf( "%02x", - $return->{$layer}{$side}{$led}{g} ) - ); - $rgb .= uc( - sprintf( "%02x", - $return->{$layer}{$side}{$led}{b} ) + $hex = PHTV_rgb2hex( + $return->{$layer}{$side}{$led}{r}, + $return->{$layer}{$side}{$led}{g}, + $return->{$layer}{$side}{$led}{b} ); + $rgb = $hex if ( $rgb eq "" ); + $rgb = "diff" if ( $rgb ne $hex ); + if ( !defined( $hash->{READINGS}{$readingname}{VAL} ) - || $hash->{READINGS}{$readingname}{VAL} ne $rgb + || $hash->{READINGS}{$readingname}{VAL} ne $hex ) { - readingsBulkUpdate( $hash, $readingname, $rgb ); + readingsBulkUpdate( $hash, $readingname, $hex ); } } } } + + if ( $rgb ne "diff" ) { + my $hsv = PHTV_hex2hsv($rgb); + my $hue = $hsv->{h}; + my $sat = int( $hsv->{s} * 100 + 0.5 ); + my $bri = $hsv->{v}; + my $pct = PHTV_bri2pct($bri); + + if ( !defined( $hash->{READINGS}{rgb}{VAL} ) + || $hash->{READINGS}{rgb}{VAL} ne $rgb ) + { + readingsBulkUpdate( $hash, "rgb", $rgb ); + } + + if ( !defined( $hash->{READINGS}{hue}{VAL} ) + || $hash->{READINGS}{hue}{VAL} ne $hue ) + { + readingsBulkUpdate( $hash, "hue", $hue ); + } + + if ( !defined( $hash->{READINGS}{sat}{VAL} ) + || $hash->{READINGS}{sat}{VAL} ne $sat ) + { + readingsBulkUpdate( $hash, "sat", $sat ); + } + + if ( !defined( $hash->{READINGS}{bri}{VAL} ) + || $hash->{READINGS}{bri}{VAL} ne $bri ) + { + readingsBulkUpdate( $hash, "bri", $bri ); + } + + if ( !defined( $hash->{READINGS}{pct}{VAL} ) + || $hash->{READINGS}{pct}{VAL} ne $pct ) + { + readingsBulkUpdate( $hash, "pct", $pct ); + readingsBulkUpdate( $hash, "level", $pct . " %" ); + } + } } elsif ( $return eq "ok" ) { if ( $type =~ /^(..)(..)(..)$/ && defined( $hash->{READINGS}{ambiLEDLayers}{VAL} ) ) { - my $bri = PHTV_hex2hsv( $type, "v" ); + my $hsv = PHTV_hex2hsv($type); + my $hue = $hsv->{h}; + my $sat = int( $hsv->{s} * 100 + 0.5 ); + my $bri = $hsv->{v}; my $pct = PHTV_bri2pct($bri); if ( !defined( $hash->{READINGS}{rgb}{VAL} ) @@ -1620,6 +1983,18 @@ sub PHTV_ReceiveCommand($$$) { readingsBulkUpdate( $hash, "rgb", $type ); } + if ( !defined( $hash->{READINGS}{hue}{VAL} ) + || $hash->{READINGS}{hue}{VAL} ne $hue ) + { + readingsBulkUpdate( $hash, "hue", $hue ); + } + + if ( !defined( $hash->{READINGS}{sat}{VAL} ) + || $hash->{READINGS}{sat}{VAL} ne $sat ) + { + readingsBulkUpdate( $hash, "sat", $sat ); + } + if ( !defined( $hash->{READINGS}{bri}{VAL} ) || $hash->{READINGS}{bri}{VAL} ne $bri ) { @@ -1665,11 +2040,11 @@ sub PHTV_ReceiveCommand($$$) { if ( !defined( - $hash->{READINGS}{$readingname} - {VAL} + $hash->{READINGS} + {$readingname}{VAL} ) - || $hash->{READINGS}{$readingname} - {VAL} ne $type + || $hash->{READINGS} + {$readingname}{VAL} ne $type ) { readingsBulkUpdate( $hash, @@ -1813,7 +2188,8 @@ sub PHTV_ReceiveCommand($$$) { if ( defined( $attr{$name}{ambiHueLeft} ) && $attr{$name}{ambiHueLeft} ne "" ) { - my @devices = split( " ", $attr{$name}{ambiHueLeft} ); + my @devices = + split( " ", $attr{$name}{ambiHueLeft} ); foreach (@devices) { my ( $dev, $led ) = split( /:/, $_ ); @@ -1825,7 +2201,8 @@ sub PHTV_ReceiveCommand($$$) { if ( defined( $attr{$name}{ambiHueTop} ) && $attr{$name}{ambiHueTop} ne "" ) { - my @devices = split( " ", $attr{$name}{ambiHueTop} ); + my @devices = + split( " ", $attr{$name}{ambiHueTop} ); foreach (@devices) { my ( $dev, $led ) = split( /:/, $_ ); @@ -1909,6 +2286,88 @@ sub PHTV_ReceiveCommand($$$) { readingsBulkUpdate( $hash, $_, "-" ); } } + + if ( !defined( $hash->{READINGS}{ambiMode}{VAL} ) + || $hash->{READINGS}{ambiMode}{VAL} ne "internal" ) + { + readingsBulkUpdate( $hash, "ambiMode", "internal" ); + } + + if ( !defined( $hash->{READINGS}{rgb}{VAL} ) + || $hash->{READINGS}{rgb}{VAL} ne "000000" ) + { + readingsBulkUpdate( $hash, "rgb", "000000" ); + } + + if ( !defined( $hash->{READINGS}{hue}{VAL} ) + || $hash->{READINGS}{hue}{VAL} ne "0" ) + { + readingsBulkUpdate( $hash, "hue", "0" ); + } + + if ( !defined( $hash->{READINGS}{sat}{VAL} ) + || $hash->{READINGS}{sat}{VAL} ne "0" ) + { + readingsBulkUpdate( $hash, "sat", "0" ); + } + + if ( !defined( $hash->{READINGS}{bri}{VAL} ) + || $hash->{READINGS}{bri}{VAL} ne "0" ) + { + readingsBulkUpdate( $hash, "bri", "0" ); + } + + if ( !defined( $hash->{READINGS}{pct}{VAL} ) + || $hash->{READINGS}{pct}{VAL} ne "0" ) + { + readingsBulkUpdate( $hash, "pct", "0" ); + readingsBulkUpdate( $hash, "level", "0 %" ); + } + + if ( defined( $hash->{READINGS}{ambiLEDLayers}{VAL} ) ) { + my $layer = 1; + while ( $layer <= $hash->{READINGS}{ambiLEDLayers}{VAL} ) { + + foreach my $side ( 'Left', 'Top', 'Right', 'Bottom' ) { + my $ambiLED = "ambiLED$side"; + my $side = lc($side); + + my $l = "L" . $layer; + my $s = $side; + $s =~ s/left/L/ if ( $side eq "left" ); + $s =~ s/top/T/ if ( $side eq "top" ); + $s =~ s/right/R/ if ( $side eq "right" ); + $s =~ s/bottom/B/ if ( $side eq "bottom" ); + + if ( defined( $hash->{READINGS}{$ambiLED}{VAL} ) + && $hash->{READINGS}{$ambiLED}{VAL} > 0 ) + { + my $led = 0; + + while ( $led <= $hash->{READINGS}{$ambiLED}{VAL} - 1 ) { + my $readingname = "rgb_" . $l . $s . $led; + + if ( + !defined( + $hash->{READINGS}{$readingname}{VAL} + ) + || $hash->{READINGS}{$readingname}{VAL} ne + "000000" + ) + { + readingsBulkUpdate( $hash, + $readingname, "000000" ); + } + + $led++; + } + } + } + + $layer++; + } + } + } readingsEndUpdate( $hash, 1 ); @@ -2081,15 +2540,34 @@ sub PHTV_GetRemotecontrolCommand($) { } } +################################### +sub PHTV_isinteger { + defined $_[0] && $_[0] =~ /^[+-]?\d+$/; +} + ################################### sub PHTV_hex2rgb($) { my ($hex) = @_; if ( uc($hex) =~ /^(..)(..)(..)$/ ) { my ( $r, $g, $b ) = ( hex($1), hex($2), hex($3) ); - return { "r" => $r, "g" => $g, "b" => $b }; + my $return = { "r" => $r, "g" => $g, "b" => $b }; + Log3 undef, 5, + "PHTV hex2rgb: $hex > " + . $return->{r} . " " + . $return->{g} . " " + . $return->{b}; + return $return; } } +################################### +sub PHTV_rgb2hex($$$) { + my ( $r, $g, $b ) = @_; + my $return = sprintf( "%2.2X%2.2X%2.2X", $r, $g, $b ); + Log3 undef, 5, "PHTV rgb2hex: $r $g $b > $return"; + return uc($return); +} + ################################### sub PHTV_hex2hsv($;$) { my ( $hex, $type ) = @_; @@ -2121,6 +2599,13 @@ sub PHTV_pct2bri($) { return int( ( $pct / 100 * 255 ) + 0.5 ); } +################################### +sub PHTV_hsv2hex($$$) { + my ( $h, $s, $v ) = @_; + my $rgb = PHTV_hsv2rgb( $h, $s, $v ); + return PHTV_rgb2hex( $rgb->{r}, $rgb->{g}, $rgb->{b} ); +} + ################################### sub PHTV_rgb2hsv($$$;$) { my ( $r, $g, $b, $type ) = @_; @@ -2155,6 +2640,8 @@ sub PHTV_rgb2hsv($$$;$) { $V = $M; + Log3 undef, 5, "PHTV rgb2hsv: $r $g $b > $H $S $V"; + if ( defined($type) ) { return $H if ( $type eq "h" ); return $S if ( $type eq "s" ); @@ -2165,6 +2652,51 @@ sub PHTV_rgb2hsv($$$;$) { } } +################################### +sub PHTV_hsv2rgb($$$) { + my ( $H, $S, $V ) = @_; + my ( $r, $g, $b, $C, $Hdash, $X, $m ); + + $C = $S * $V; + $Hdash = $H / 60.0; + $X = $C * ( 1.0 - int( ( $Hdash % 2.0 ) - 1.0 ) ); + + if ( $Hdash < 1.0 ) { + $r = $C; + $g = $X; + } + elsif ( $Hdash < 2.0 ) { + $r = $X; + $g = $C; + } + elsif ( $Hdash < 3.0 ) { + $g = $C; + $b = $X; + } + elsif ( $Hdash < 4.0 ) { + $g = $X; + $b = $C; + } + elsif ( $Hdash < 5.0 ) { + $r = $X; + $b = $C; + } + elsif ( $Hdash <= 6.0 ) { + $r = $C; + $b = $X; + } + + $m = $V - $C; + + $r += $m; + $g += $m; + $b += $m; + + Log3 undef, 5, "PHTV hsv2rgb: $H $S $V > $r $g $b"; + + return { "r" => $r, "g" => $g, "b" => $b }; +} + ################################### sub PHTV_max { my ( $max, @vars ) = @_; @@ -2237,7 +2769,8 @@ sub PHTV_min {
  • remoteControl UP,DOWN,...   -   sends remote control commands; see remoteControl help
  • ambiHue on,off   -   activates/disables Ambilight+Hue function
  • ambiMode internal,manual,expert   -   set source register for Ambilight
  • -
  • rgb internal,manual,expert   -   set an RGB value for Ambilight
  • +
  • ambiPreset   -   set Ambilight to predefined state
  • +
  • rgb HEX,LED address   -   set an RGB value for Ambilight
  • play   -   starts/resumes playback
  • pause   -   starts/resumes playback
  • stop   -   stops current playback
  • @@ -2247,6 +2780,31 @@ sub PHTV_min {

    +
    + Advanced Ambilight Control
    +
    +
    + If you would like to specificly control color for individual sides or even individual LEDs, you may use special addressing to be used with set command 'rgb':
    +

    + LED addressing format:
    + <Layer$gt;<Side$gt;<LED number$gt; +

    + Examples:
    +
    + # set LED 0 on left side within layer 1 to color RED + set PhilipsTV rgb L1L0:FF0000

    + # set LED 0, 2 and 4 on left side within layer 1 to color RED + set PhilipsTV rgb L1L0:FF0000 L1L2:FF0000 L1L4:FF0000

    + # set complete right side within layer 1 to color GREEN + set PhilipsTV rgb L1R:00FF00

    + # set complete layer 1 to color BLUE + set PhilipsTV rgb L1:0000FF
    +

    +
    +
    +
    +
    + Get