diff --git a/fhem/FHEM/70_PHTV.pm b/fhem/FHEM/70_PHTV.pm index 4fcb2d1a1..8111b632e 100644 --- a/fhem/FHEM/70_PHTV.pm +++ b/fhem/FHEM/70_PHTV.pm @@ -24,9 +24,14 @@ # along with fhem. If not, see . # # -# Version: 1.0.0 +# Version: 1.1.0 # # Major Version History: +# - 1.1.0 - 2014-03-07 +# -- bugfixes +# -- additional commands: ambiMode,rgb,pause,play,record,volumeStraight +# -- additional readings for Ambilight state +# # - 1.0.0 - 2014-03-06 # -- First release # @@ -144,6 +149,14 @@ sub PHTV_GetStatus($;$) { # read ambilight mode PHTV_SendCommand( $hash, "ambilight/mode" ) if ( !$update ); + + # read ambilight RGB value + PHTV_SendCommand( $hash, "ambilight/cached" ) + if ( + defined( $hash->{READINGS}{ambiMode}{VAL} ) + && ( $hash->{READINGS}{ambiMode}{VAL} eq "manual" + || $hash->{READINGS}{ambiMode}{VAL} eq "expert" ) + ); } # Input alias handling @@ -180,7 +193,7 @@ sub PHTV_Get($@) { $what = $a[1]; - if ( $what =~ /^(power|input|volume|mute)$/ ) { + if ( $what =~ /^(power|input|volume|mute|rgb)$/ ) { if ( defined( $hash->{READINGS}{$what} ) ) { return $hash->{READINGS}{$what}{VAL}; } @@ -191,7 +204,7 @@ sub PHTV_Get($@) { else { return -"Unknown argument $what, choose one of power:noArg input:noArg volume:noArg mute:noArg "; +"Unknown argument $what, choose one of power:noArg input:noArg volume:noArg mute:noArg rgb:noArg "; } } @@ -269,7 +282,13 @@ sub PHTV_Set($@) { my $usage = "Unknown argument " . $a[1] - . ", choose one of statusRequest:noArg toggle:noArg on:noArg off:noArg volume:slider,1,1,60 volumeUp:noArg volumeDown:noArg channelUp:noArg channelDown:noArg remoteControl ambiHue:off,on"; + . ", 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"; + $usage .= + " volumeStraight:slider," + . $hash->{helper}{audio}{min} . ",1," + . $hash->{helper}{audio}{max} + if ( defined( $hash->{helper}{audio}{min} ) + && defined( $hash->{helper}{audio}{max} ) ); $usage .= " mute:-,on,off" if ( defined( $hash->{READINGS}{mute}{VAL} ) && $hash->{READINGS}{mute}{VAL} eq "-" ); @@ -305,24 +324,9 @@ sub PHTV_Set($@) { } - # on - elsif ( $a[1] eq "on" ) { - Log3 $name, 2, "PHTV set $name " . $a[1]; - - return -"Sorry, Philips Television devices currently do not seem to reliably support WoWLAN or WOL packages which is essential to be woken up from standby mode."; - - if ( $hash->{READINGS}{state}{VAL} ne "absent" ) { - $cmd = PHTV_GetRemotecontrolCommand("STANDBY"); - PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"' ); - } - else { - return "Device needs to be reachable to be set to standby mode."; - } - } - # off - elsif ( $a[1] eq "off" ) { + # on + elsif ( $a[1] eq "off" || $a[1] eq "on" ) { Log3 $name, 2, "PHTV set $name " . $a[1]; if ( $hash->{READINGS}{state}{VAL} ne "absent" ) { @@ -330,7 +334,7 @@ sub PHTV_Set($@) { PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"' ); } else { - return "Device needs to be reachable to be set to standby mode."; + return "Device needs to be reachable to toggle standby mode."; } } @@ -364,25 +368,144 @@ sub PHTV_Set($@) { } } + # ambiMode + elsif ( lc( $a[1] ) eq "ambimode" ) { + 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 ( $a[2] eq "internal" || $a[2] eq "manual" || $a[2] eq "expert" ) + { + PHTV_SendCommand( $hash, "ambilight/mode", + '"current": "' . $a[2] . '"', $a[2] ); + + readingsSingleUpdate( $hash, "rgb", "000000", 1 ) + if ( $a[2] eq "internal" ); + } + else { + return +"Unknown argument given, choose one of internal manual expert"; + } + } + else { + return "Device needs to be ON to control Ambilight mode."; + } + } + + # rgb + elsif ( $a[1] eq "rgb" ) { + 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 ( uc( $a[2] ) =~ /^(..)(..)(..)$/ ) { + my $json; + my ( $r, $g, $b ) = ( hex($1), hex($2), hex($3) ); + my $rgbsum = $r + $g + $b; + + $json .= '"r": ' . $r . ','; + $json .= '"g": ' . $g . ','; + $json .= '"b": ' . $b; + PHTV_SendCommand( $hash, "ambilight/cached", $json, + uc( $a[2] ) ); + + # enable manual Ambilight color if RGB!=000000 + PHTV_SendCommand( $hash, "ambilight/mode", + '"current": "manual"', "manual" ) + if ( $hash->{READINGS}{ambiMode}{VAL} ne "manual" + && $rgbsum > 0 ); + + # disable manual Ambilight color if RGB=000000 + PHTV_SendCommand( $hash, "ambilight/mode", + '"current": "internal"', "internal" ) + if ( $hash->{READINGS}{ambiMode}{VAL} ne "internal" + && $rgbsum == 0 ); + + readingsSingleUpdate( $hash, "rgb", uc( $a[2] ), 1 ) + if ( $hash->{READINGS}{rgb}{VAL} ne uc( $a[2] ) ); + } + else { + return "Invalid RGB code"; + } + } + 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]; return "No argument given" if ( !defined( $a[2] ) ); + my $vol; if ( $hash->{READINGS}{state}{VAL} eq "on" ) { my $_ = $a[2]; - if ( m/^\d+$/ && $_ >= 0 && $_ <= 100 ) { - $cmd = '"current": ' . $a[2]; + if ( m/^\d+$/ && $_ >= 1 && $_ <= 100 ) { + if ( defined( $hash->{helper}{audio}{min} ) + && defined( $hash->{helper}{audio}{max} ) ) + { + $vol = int( + ( $a[2] / 100 * $hash->{helper}{audio}{max} ) + 0.5 ); + } + else { + $vol = $a[2]; + } + $cmd = '"current": ' . $vol; } else { return "Argument does not seem to be a valid integer between 0 and 100"; } - $result = PHTV_SendCommand( $hash, "audio/volume", $cmd ); + PHTV_SendCommand( $hash, "audio/volume", $cmd ); - readingsSingleUpdate( $hash, "volume", $a[2], 1 ) + readingsBeginUpdate($hash); + readingsBulkUpdate( $hash, "volume", $a[2], 1 ) if ( $hash->{READINGS}{volume}{VAL} ne $a[2] ); + readingsBulkUpdate( $hash, "volumeStraight", $vol, 1 ) + if ( defined($vol) + && $hash->{READINGS}{volumeStraight}{VAL} ne $vol ); + readingsEndUpdate( $hash, 1 ); + } + else { + return "Device needs to be ON to adjust volume."; + } + } + + # volumeStraight + elsif ( lc( $a[1] ) eq "volumestraight" ) { + Log3 $name, 2, "PHTV set $name " . $a[1] . " " . $a[2]; + + return "No argument given" if ( !defined( $a[2] ) ); + + my $vol; + if ( $hash->{READINGS}{state}{VAL} eq "on" ) { + my $_ = $a[2]; + if ( m/^\d+$/ + && $_ >= $hash->{helper}{audio}{min} + && $_ <= $hash->{helper}{audio}{max} ) + { + $vol = + int( ( $a[2] / $hash->{helper}{audio}{max} * 100 ) + 0.5 ); + $cmd = '"current": ' . $a[2]; + } + else { + return + "Argument does not seem to be a valid integer between " + . $hash->{helper}{audio}{min} . " and " + . $hash->{helper}{audio}{max}; + } + PHTV_SendCommand( $hash, "audio/volume", $cmd ); + + readingsBeginUpdate($hash); + readingsBulkUpdate( $hash, "volume", $vol, 1 ) + if ( $hash->{READINGS}{volume}{VAL} ne $vol ); + readingsBulkUpdate( $hash, "volumeStraight", $a[2], 1 ) + if ( $hash->{READINGS}{volumeStraight}{VAL} ne $a[2] ); + readingsEndUpdate( $hash, 1 ); } else { return "Device needs to be ON to adjust volume."; @@ -527,6 +650,10 @@ sub PHTV_Set($@) { my $_ = $a[2]; if ( defined( $hash->{helper}{device}{channelID}{$_}{id} ) ) { $cmd = $hash->{helper}{device}{channelID}{$_}{id}; + + if ( $hash->{READINGS}{channel}{VAL} ne $_ ) { + readingsSingleUpdate( $hash, "channel", $_, 1 ); + } } elsif ( /^(\d+):(.*):$/ && defined( $hash->{helper}{device}{channelPreset}{$_}{id} ) ) @@ -597,15 +724,57 @@ sub PHTV_Set($@) { } if ( $hash->{READINGS}{state}{VAL} eq "on" ) { - $result = - 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 ); + } } else { return "Device needs to be present to switch input."; } } + # play / pause + elsif ( $a[1] =~ /^(play|pause)$/ ) { + Log3 $name, 2, "PHTV set $name " . $a[1]; + + if ( $hash->{READINGS}{state}{VAL} eq "on" ) { + $cmd = PHTV_GetRemotecontrolCommand("PLAYPAUSE"); + PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"' ); + } + else { + return "Device needs to be ON to play or pause video."; + } + } + + # stop + elsif ( $a[1] eq "stop" ) { + Log3 $name, 2, "PHTV set $name " . $a[1]; + + if ( $hash->{READINGS}{state}{VAL} eq "on" ) { + $cmd = PHTV_GetRemotecontrolCommand("STOP"); + PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"' ); + } + else { + return "Device needs to be ON to stop video."; + } + } + + # record + elsif ( $a[1] eq "record" ) { + Log3 $name, 2, "PHTV set $name " . $a[1]; + + if ( $hash->{READINGS}{state}{VAL} eq "on" ) { + $cmd = PHTV_GetRemotecontrolCommand("RECORD"); + PHTV_SendCommand( $hash, "input/key", '"key": "' . $cmd . '"' ); + } + else { + return "Device needs to be ON to start instant recording."; + } + } + # return usage hint else { return $usage; @@ -646,7 +815,7 @@ sub PHTV_Define($$) { if ( defined( $hash->{READINGS}{".model"}{VAL} ) ); unless ( defined( AttrVal( $name, "webCmd", undef ) ) ) { - $attr{$name}{webCmd} = 'volume:input'; + $attr{$name}{webCmd} = 'volume:input:rgb'; } unless ( defined( AttrVal( $name, "devStateIcon", undef ) ) ) { $attr{$name}{devStateIcon} = @@ -841,19 +1010,34 @@ sub PHTV_ReceiveCommand($$$) { # audio/volume if ( $service eq "audio/volume" ) { if ( ref($return) eq "HASH" ) { - $hash->{helper}{audio}{min} = $return->{min} - if ( defined( $return->{min} ) ); - $hash->{helper}{audio}{max} = $return->{max} - if ( defined( $return->{max} ) ); + # calculate volume + my $vol = ( $return->{current} ) ? $return->{current} : 0; + if ( defined( $return->{min} ) && defined( $return->{max} ) ) { + $hash->{helper}{audio}{min} = $return->{min}; + $hash->{helper}{audio}{max} = $return->{max}; + + $vol = int( + ( $return->{current} / $return->{max} * 100 ) + 0.5 ); + } + + # volume + if ( !defined( $hash->{READINGS}{volume}{VAL} ) + || $hash->{READINGS}{volume}{VAL} ne $vol ) + { + readingsBulkUpdate( $hash, "volume", $vol ); + } + + # volumeStraight if ( defined( $return->{current} ) - && ( !defined( $hash->{READINGS}{volume}{VAL} ) - || $hash->{READINGS}{volume}{VAL} ne + && ( !defined( $hash->{READINGS}{volumeStraight}{VAL} ) + || $hash->{READINGS}{volumeStraight}{VAL} ne $return->{current} ) ) { - readingsBulkUpdate( $hash, "volume", $return->{current} ); + readingsBulkUpdate( $hash, "volumeStraight", + $return->{current} ); } if ( defined( $return->{muted} ) ) { @@ -1356,6 +1540,121 @@ sub PHTV_ReceiveCommand($$$) { } } + elsif ( $return eq "ok" ) { + if ( !defined( $hash->{READINGS}{ambiMode}{VAL} ) + || $hash->{READINGS}{ambiMode}{VAL} ne $type ) + { + readingsBulkUpdate( $hash, "ambiMode", $type ); + } + } + } + + # ambilight/cached (rgb) + elsif ( $service eq "ambilight/cached" ) { + if ( ref($return) eq "HASH" ) { + foreach my $layer ( keys $return ) { + foreach my $side ( keys $return->{$layer} ) { + foreach my $led ( keys $return->{$layer}{$side} ) { + my $rgb = ""; + my $l = $layer; + my $s = $side; + $l =~ s/layer/L/; + $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" ); + + 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} ) + ); + + if ( + !defined( + $hash->{READINGS}{$readingname}{VAL} + ) + || $hash->{READINGS}{$readingname}{VAL} ne $rgb + ) + { + readingsBulkUpdate( $hash, $readingname, $rgb ); + } + } + } + } + } + elsif ( $return eq "ok" ) { + if ( $type =~ /^(..)(..)(..)$/ + && defined( $hash->{READINGS}{ambiLEDLayers}{VAL} ) ) + { + if ( !defined( $hash->{READINGS}{rgb}{VAL} ) + || $hash->{READINGS}{rgb}{VAL} ne $type ) + { + readingsBulkUpdate( $hash, "rgb", $type ); + } + + 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 $type + ) + { + readingsBulkUpdate( $hash, + $readingname, $type ); + } + + $led++; + } + } + } + + $layer++; + } + } + + } + } } # ambilight/processed (ambiHue) @@ -1364,11 +1663,6 @@ sub PHTV_ReceiveCommand($$$) { readingsBulkUpdate( $hash, "ambiHue", "on" ) if ( $type eq "init" ); - if ( !defined( $attr{$name}{ambiHueLeft} ) - && !defined( $attr{$name}{ambiHueRight} ) - && !defined( $attr{$name}{ambiHueTop} ) - && !defined( $attr{$name}{ambiHueBottom} ) ); - # run ambiHue if ( ( @@ -1571,9 +1865,11 @@ sub PHTV_ReceiveCommand($$$) { || $newstate eq "undefined" ) { foreach ( - 'mute', 'volume', 'input', 'channel', - 'currentMedia', 'servicename', 'frequency', 'onid', - 'tsid', 'sid', 'receiveMode', 'ambiMode' + 'mute', 'volume', 'volumeStraight', + 'input', 'channel', 'currentMedia', + 'servicename', 'frequency', 'onid', + 'tsid', 'sid', 'receiveMode', + 'ambiMode' ) { if ( !defined( $hash->{READINGS}{$_}{VAL} ) @@ -1799,6 +2095,7 @@ sub PHTV_GetRemotecontrolCommand($) {
  • channelUp   -   zap to next channel
  • channelDown   -   zap to previous channel
  • volume 0...100   -   set the volume level in percentage
  • +
  • volumeStraight 1...60   -   set the volume level in device specific range
  • volumeUp   -   increases the volume level
  • volumeDown   -   decreases the volume level
  • mute on,off,toggle   -   controls volume mute
  • @@ -1806,6 +2103,12 @@ sub PHTV_GetRemotecontrolCommand($) {
  • statusRequest   -   requests the current status of the device
  • 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
  • +
  • play   -   starts/resumes playback
  • +
  • pause   -   starts/resumes playback
  • +
  • stop   -   stops current playback
  • +
  • record   -   starts recording of current channel

  • @@ -1823,6 +2126,7 @@ sub PHTV_GetRemotecontrolCommand($) { power
    input
    volume
    + rgb

    @@ -1863,6 +2167,7 @@ sub PHTV_GetRemotecontrolCommand($) {
  • state - Reports current power state and an absence of the device (can be "on", "off" or "absent")
  • tsid - The TS ID
  • volume - Reports current volume level of the receiver in percentage values (between 0 and 100 %)
  • +
  • volumeStraight - Reports current volume level of the receiver in device specific range