2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-28 17:12:32 +00:00

PHTV: ambiHue performance, ambiHue custom settings, new commands hue, sat, bri

git-svn-id: https://svn.fhem.de/fhem/trunk@5204 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
jpawlowski 2014-03-12 05:46:23 +00:00
parent bd1a1d4f68
commit 1a0bee4572

View File

@ -24,9 +24,12 @@
# along with fhem. If not, see <http://www.gnu.org/licenses/>. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
# #
# #
# Version: 1.1.4 # Version: 1.2.0
# #
# Major Version History: # Major Version History:
# - 1.2.0 - 2014-03-12
# -- extended AmbiHue support
#
# - 1.1.0 - 2014-03-07 # - 1.1.0 - 2014-03-07
# -- bugfixes # -- bugfixes
# -- additional commands: ambiMode,rgb,pause,play,record,volumeStraight # -- additional commands: ambiMode,rgb,pause,play,record,volumeStraight
@ -42,6 +45,7 @@ package main;
use strict; use strict;
use warnings; use warnings;
use Data::Dumper; use Data::Dumper;
use Time::HiRes qw(gettimeofday);
use JSON; use JSON;
use HttpUtils; use HttpUtils;
use Color; use Color;
@ -71,7 +75,7 @@ sub PHTV_Initialize($) {
$hash->{UndefFn} = "PHTV_Undefine"; $hash->{UndefFn} = "PHTV_Undefine";
$hash->{AttrList} = $hash->{AttrList} =
"disable:0,1 timeout inputs ambiHueLeft ambiHueRight ambiHueTop ambiHueBottom ambiHueLatency:100,125,150,175,200,225,250,275,300,325,350,375,400,425,450,475,500" "disable:0,1 timeout inputs ambiHueLeft ambiHueRight ambiHueTop ambiHueBottom "
. $readingFnAttributes; . $readingFnAttributes;
$data{RC_layout}{PHTV_SVG} = "PHTV_RClayout_SVG"; $data{RC_layout}{PHTV_SVG} = "PHTV_RClayout_SVG";
@ -92,6 +96,10 @@ sub PHTV_GetStatus($;$) {
Log3 $name, 5, "PHTV $name: called function PHTV_GetStatus()"; Log3 $name, 5, "PHTV $name: called function PHTV_GetStatus()";
$interval = $interval * 1.6
if ( defined( $hash->{READINGS}{ambiHue}{VAL} )
&& $hash->{READINGS}{ambiHue}{VAL} eq "on" );
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
InternalTimer( gettimeofday() + $interval, "PHTV_GetStatus", $hash, 0 ); InternalTimer( gettimeofday() + $interval, "PHTV_GetStatus", $hash, 0 );
@ -266,8 +274,10 @@ sub PHTV_Set($@) {
if ( defined( $hash->{helper}{device}{channelPreset} ) if ( defined( $hash->{helper}{device}{channelPreset} )
&& ref( $hash->{helper}{device}{channelPreset} ) eq "HASH" ) && ref( $hash->{helper}{device}{channelPreset} ) eq "HASH" )
{ {
my $i = 1; my $i = 1;
while ( $i < 81 ) { my $count = scalar( keys $hash->{helper}{device}{channelPreset} );
$count = 80 if ( $count > 80 );
while ( $i <= $count ) {
$channels .= $channels .=
$hash->{helper}{device}{channelPreset}{$i}{name} . ","; $hash->{helper}{device}{channelPreset}{$i}{name} . ",";
$i++; $i++;
@ -283,7 +293,7 @@ sub PHTV_Set($@) {
my $usage = my $usage =
"Unknown argument " "Unknown argument "
. $a[1] . $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 ambiPreset:rainbow,rainbow-pastel rgb:colorpicker,rgb"; . ", 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 hue:slider,0,1,65534 sat:slider,0,1,255 pct:slider,0,1,100 bri:slider,0,1,255";
$usage .= $usage .=
" volumeStraight:slider," " volumeStraight:slider,"
. $hash->{helper}{audio}{min} . ",1," . $hash->{helper}{audio}{min} . ",1,"
@ -564,7 +574,7 @@ sub PHTV_Set($@) {
# set all LEDs at once # set all LEDs at once
if ( uc( $a[2] ) =~ /^(..)(..)(..)$/ ) { if ( uc( $a[2] ) =~ /^(..)(..)(..)$/ ) {
my $json; my $json;
my $hsv; my $hsb;
my $hue; my $hue;
my $sat; my $sat;
my $bri; my $bri;
@ -575,10 +585,10 @@ sub PHTV_Set($@) {
$json .= '"r": ' . $r . ','; $json .= '"r": ' . $r . ',';
$json .= '"g": ' . $g . ','; $json .= '"g": ' . $g . ',';
$json .= '"b": ' . $b; $json .= '"b": ' . $b;
$hsv = PHTV_rgb2hsv( $r, $g, $b ); $hsb = PHTV_rgb2hsb( $r, $g, $b );
$hue = $hsv->{h}; $hue = $hsb->{h};
$sat = int( $hsv->{s} * 100 + 0.5 ); $sat = $hsb->{s};
$bri = $hsv->{v}; $bri = $hsb->{b};
$pct = PHTV_bri2pct($bri); $pct = PHTV_bri2pct($bri);
PHTV_SendCommand( $hash, "ambilight/cached", $json, PHTV_SendCommand( $hash, "ambilight/cached", $json,
uc( $a[2] ) ); uc( $a[2] ) );
@ -696,7 +706,7 @@ sub PHTV_Set($@) {
&& $rgbsum > 0 ); && $rgbsum > 0 );
} }
else { else {
return "Invalid RGB code"; return "Invalid RGB code " . $a[2];
} }
} }
else { else {
@ -704,6 +714,119 @@ sub PHTV_Set($@) {
} }
} }
# hue
elsif ( $a[1] eq "hue" ) {
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 $hsb;
my $hex;
if ( m/^\d+$/ && $_ >= 0 && $_ <= 65534 ) {
$hsb = PHTV_hex2hsb( $hash->{READINGS}{rgb}{VAL} );
$hex = PHTV_hsb2hex( $_, $hsb->{s}, $hsb->{b} );
Log3 $name, 4,
"PHTV $name hue - old: "
. $hash->{READINGS}{rgb}{VAL}
. " new: $hex(h=$_ s="
. $hsb->{s} . " b="
. $hsb->{b};
return PHTV_Set( $hash, $name, "rgb", $hex );
}
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.";
}
}
# sat
elsif ( $a[1] eq "sat" ) {
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 $hsb;
my $hex;
if ( m/^\d+$/ && $_ >= 0 && $_ <= 255 ) {
$hsb = PHTV_hex2hsb( $hash->{READINGS}{rgb}{VAL} );
$hex = PHTV_hsb2hex( $hsb->{h}, $_, $hsb->{b} );
Log3 $name, 4,
"PHTV $name sat - old: "
. $hash->{READINGS}{rgb}{VAL}
. " new: $hex(h="
. $hsb->{h}
. " s=$_ b="
. $hsb->{b};
return PHTV_Set( $hash, $name, "rgb", $hex );
}
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.";
}
}
# bri
elsif ( $a[1] eq "bri" ) {
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 $hsb;
my $hex;
if ( m/^\d+$/ && $_ >= 0 && $_ <= 255 ) {
$hsb = PHTV_hex2hsb( $hash->{READINGS}{rgb}{VAL} );
$hex = PHTV_hsb2hex( $hsb->{h}, $hsb->{s}, $_ );
Log3 $name, 4,
"PHTV $name bri - old: "
. $hash->{READINGS}{rgb}{VAL}
. " new: $hex(h="
. $hsb->{h} . " s="
. $hsb->{s}
. " b=$_)";
return PHTV_Set( $hash, $name, "rgb", $hex );
}
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.";
}
}
# pct # pct
elsif ( $a[1] eq "pct" ) { elsif ( $a[1] eq "pct" ) {
Log3 $name, 2, "PHTV set $name " . $a[1] . " " . $a[2]; Log3 $name, 2, "PHTV set $name " . $a[1] . " " . $a[2];
@ -715,12 +838,23 @@ sub PHTV_Set($@) {
&& $hash->{READINGS}{rgb}{VAL} ne "" ) && $hash->{READINGS}{rgb}{VAL} ne "" )
{ {
my $_ = $a[2]; my $_ = $a[2];
my $rgb; my $hsb;
my $rgbnew; my $bri;
my $hex;
if ( m/^\d+$/ && $_ >= 0 && $_ <= 100 ) { if ( m/^\d+$/ && $_ >= 0 && $_ <= 100 ) {
$rgb = PHTV_hex2hsv( $hash->{READINGS}{rgb}{VAL} ); $hsb = PHTV_hex2hsb( $hash->{READINGS}{rgb}{VAL} );
$rgbnew = PHTV_hsv2hex( $rgb->{h}, $rgb->{s}, $_ ); $bri = PHTV_pct2bri($_);
return PHTV_Set( $hash, $name, "rgb", $rgbnew ); $hex = PHTV_hsb2hex( $hsb->{h}, $hsb->{s}, $bri );
Log3 $name, 4,
"PHTV $name pct - old: "
. $hash->{READINGS}{rgb}{VAL}
. " new: $hex(h="
. $hsb->{h} . " s="
. $hsb->{s}
. " b=$bri)";
return PHTV_Set( $hash, $name, "rgb", $hex );
} }
else { else {
return return
@ -1107,8 +1241,8 @@ sub PHTV_Define($$) {
$hash->{helper}{PORT} = 1925; $hash->{helper}{PORT} = 1925;
readingsSingleUpdate( $hash, "ambiHue", "off", 0 ); readingsSingleUpdate( $hash, "ambiHue", "off", 0 )
if ( defined( $hash->{READINGS}{ambiHue}{VAL} ) if ( defined( $hash->{READINGS}{ambiHue}{VAL} )
&& $hash->{READINGS}{ambiHue}{VAL} ne "off" ); && $hash->{READINGS}{ambiHue}{VAL} ne "off" );
$hash->{model} = $hash->{READINGS}{model}{VAL} $hash->{model} = $hash->{READINGS}{model}{VAL}
@ -1144,9 +1278,10 @@ sub PHTV_Define($$) {
################################### ###################################
sub PHTV_SendCommand($$;$$) { sub PHTV_SendCommand($$;$$) {
my ( $hash, $service, $cmd, $type ) = @_; my ( $hash, $service, $cmd, $type ) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $address = $hash->{helper}{ADDRESS}; my $address = $hash->{helper}{ADDRESS};
my $port = $hash->{helper}{PORT}; my $port = $hash->{helper}{PORT};
my $timestamp = gettimeofday();
my $data; my $data;
my $timeout; my $timeout;
@ -1207,6 +1342,7 @@ sub PHTV_SendCommand($$;$$) {
service => $service, service => $service,
cmd => $cmd, cmd => $cmd,
type => $type, type => $type,
timestamp => $timestamp,
callback => \&PHTV_ReceiveCommand, callback => \&PHTV_ReceiveCommand,
} }
); );
@ -1221,6 +1357,7 @@ sub PHTV_ReceiveCommand($$$) {
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $service = $param->{service}; my $service = $param->{service};
my $cmd = $param->{cmd}; my $cmd = $param->{cmd};
my $state = my $state =
( $hash->{READINGS}{state}{VAL} ) ( $hash->{READINGS}{state}{VAL} )
? $hash->{READINGS}{state}{VAL} ? $hash->{READINGS}{state}{VAL}
@ -1909,7 +2046,7 @@ sub PHTV_ReceiveCommand($$$) {
# ambilight/cached (rgb) # ambilight/cached (rgb)
elsif ( $service eq "ambilight/cached" ) { elsif ( $service eq "ambilight/cached" ) {
if ( ref($return) eq "HASH" ) { if ( ref($return) eq "HASH" ) {
my $rgb = ""; my $hexsum = "";
foreach my $layer ( keys $return ) { foreach my $layer ( keys $return ) {
foreach my $side ( keys $return->{$layer} ) { foreach my $side ( keys $return->{$layer} ) {
foreach my $led ( keys $return->{$layer}{$side} ) { foreach my $led ( keys $return->{$layer}{$side} ) {
@ -1929,8 +2066,8 @@ sub PHTV_ReceiveCommand($$$) {
$return->{$layer}{$side}{$led}{b} $return->{$layer}{$side}{$led}{b}
); );
$rgb = $hex if ( $rgb eq "" ); $hexsum = $hex if ( $hexsum eq "" );
$rgb = "diff" if ( $rgb ne $hex ); $hexsum = "diff" if ( $hexsum ne $hex );
if ( if (
!defined( !defined(
@ -1945,17 +2082,17 @@ sub PHTV_ReceiveCommand($$$) {
} }
} }
if ( $rgb ne "diff" ) { if ( $hexsum ne "diff" ) {
my $hsv = PHTV_hex2hsv($rgb); my $hsb = PHTV_hex2hsb($hexsum);
my $hue = $hsv->{h}; my $hue = $hsb->{h};
my $sat = int( $hsv->{s} * 100 + 0.5 ); my $sat = $hsb->{s};
my $bri = $hsv->{v}; my $bri = $hsb->{b};
my $pct = PHTV_bri2pct($bri); my $pct = PHTV_bri2pct($bri);
if ( !defined( $hash->{READINGS}{rgb}{VAL} ) if ( !defined( $hash->{READINGS}{rgb}{VAL} )
|| $hash->{READINGS}{rgb}{VAL} ne $rgb ) || $hash->{READINGS}{rgb}{VAL} ne $hexsum )
{ {
readingsBulkUpdate( $hash, "rgb", $rgb ); readingsBulkUpdate( $hash, "rgb", $hexsum );
} }
if ( !defined( $hash->{READINGS}{hue}{VAL} ) if ( !defined( $hash->{READINGS}{hue}{VAL} )
@ -1988,10 +2125,10 @@ sub PHTV_ReceiveCommand($$$) {
if ( $type =~ /^(..)(..)(..)$/ if ( $type =~ /^(..)(..)(..)$/
&& defined( $hash->{READINGS}{ambiLEDLayers}{VAL} ) ) && defined( $hash->{READINGS}{ambiLEDLayers}{VAL} ) )
{ {
my $hsv = PHTV_hex2hsv($type); my $hsb = PHTV_hex2hsb($type);
my $hue = $hsv->{h}; my $hue = $hsb->{h};
my $sat = int( $hsv->{s} * 100 + 0.5 ); my $sat = $hsb->{s};
my $bri = $hsv->{v}; my $bri = $hsb->{b};
my $pct = PHTV_bri2pct($bri); my $pct = PHTV_bri2pct($bri);
if ( !defined( $hash->{READINGS}{rgb}{VAL} ) if ( !defined( $hash->{READINGS}{rgb}{VAL} )
@ -2103,92 +2240,148 @@ sub PHTV_ReceiveCommand($$$) {
foreach my $side ( 'Left', 'Top', 'Right', 'Bottom' ) { foreach my $side ( 'Left', 'Top', 'Right', 'Bottom' ) {
my $ambiHue = "ambiHue$side"; my $ambiHue = "ambiHue$side";
my $ambiLED = "ambiLED$side"; my $ambiLED = "ambiLED$side";
my $sidelc = lc($side); my $s = lc($side);
# $ambiHue # $ambiHue
if ( defined( $attr{$name}{$ambiHue} ) if ( defined( $attr{$name}{$ambiHue} )
&& $attr{$name}{$ambiHue} ne "" && $attr{$name}{$ambiHue} ne ""
&& defined( $return->{layer1}->{$sidelc} ) && defined( $return->{layer1}->{$s} )
&& ref( $return->{layer1}->{$sidelc} ) eq "HASH" ) && ref( $return->{layer1}->{$s} ) eq "HASH"
&& defined( $hash->{READINGS}{$ambiLED}{VAL} )
&& $hash->{READINGS}{$ambiLED}{VAL} > 0 )
{ {
my @devices = my @devices =
split( " ", $attr{$name}{$ambiHue} ); split( " ", $attr{$name}{$ambiHue} );
foreach my $devled (@devices) { foreach my $devled (@devices) {
my ( $dev, $led ) = split( /:/, $devled ); my ( $dev, $led, $sat, $bri ) =
split( /:/, $devled );
my @leds;
# determine reference LED # next for if HUE device is not ready
if ( !defined( $defs{$dev} )
|| !defined( $defs{$dev}{TYPE} )
|| $defs{$dev}{TYPE} ne "HUEDevice"
|| $defs{$dev}{READINGS}{reachable}{VAL} ne
"true" )
{
next;
}
# determine reference LEDs
if ( !defined($led) || $led eq "" ) { if ( !defined($led) || $led eq "" ) {
if ( my $led_middle = int(
defined( $hash->{READINGS}{$ambiLED}{VAL} / 2 +
$hash->{READINGS}{$ambiLED}{VAL} 0.5 ) - 1;
# take the middle LED and
# one left and right each
push(
@leds,
(
$led_middle,
$led_middle - 1,
$led_middle + 1
) )
&& $hash->{READINGS}{$ambiLED}{VAL} > 0 );
) }
{
$led = int( # user named reference LED(s)
$hash->{READINGS}{$ambiLED}{VAL} / else {
2 + 0.5 ) - 1; my ( $ledB, $ledE ) = split( /-/, $led );
$ledB -= 1;
$ledE -= 1
if ( defined($ledE) && $ledE ne "" );
if ( !defined($ledE) || $ledE eq "" ) {
push( @leds, ($ledB) );
} }
else { else {
$led = ""; my $i = $ledB;
while ( $i <= $ledE ) {
push( @leds, ($i) );
$i++;
}
} }
} }
# copy color from reference LED # get current RGB values
if ( my ( $Hsum, $Ssum, $Bsum );
defined( $defs{$dev} && $led ne "" ) foreach my $l (@leds) {
&& $defs{$dev}{TYPE} eq "HUEDevice"
&& defined(
$return->{layer1}->{$sidelc}->{$led}
->{r}
)
&& defined(
$return->{layer1}->{$sidelc}->{$led}
->{g}
)
&& defined(
$return->{layer1}->{$sidelc}->{$led}
->{b}
)
)
{
my $r = sprintf( "%02x",
$return->{layer1}->{$sidelc}->{$led}
->{r} );
my $g = sprintf( "%02x",
$return->{layer1}->{$sidelc}->{$led}
->{g} );
my $b = sprintf( "%02x",
$return->{layer1}->{$sidelc}->{$led}
->{b} );
# temp. disable event triggers for HUEDevice
if ( if (
!defined( defined(
$attr{$dev} $return->{layer1}->{$s}->{$l}
{"event-on-change-reading"}
) )
|| $attr{$dev}
{"event-on-change-reading"} ne "none"
) )
{ {
$attr{$dev}{"event-on-change-reading"}
= "none";
}
# send command my $hsb = PHTV_rgb2hsb(
fhem("set $dev rgb $r$g$b"); $return->{layer1}->{$s}->{$l}->{r},
$return->{layer1}->{$s}->{$l}->{g},
$return->{layer1}->{$s}->{$l}->{b}
);
$Hsum += $hsb->{h};
$Ssum += $hsb->{s};
$Bsum += $hsb->{b};
}
} }
# consider user defined values
my $satF =
( $sat && $sat > 0 && $sat < 100 )
? $sat / 100
: 1;
my $briF =
( $bri && $bri > 0 && $bri < 100 )
? $bri / 100
: 1;
my $countLEDs = scalar @leds;
my $h = sprintf( "%02x",
int( $Hsum / $countLEDs / 256 + 0.5 ) );
my $s = sprintf( "%02x",
int( $Ssum / $countLEDs * $satF + 0.5 ) );
my $b = sprintf( "%02x",
int( $Bsum / $countLEDs * $briF + 0.5 ) );
# temp. disable event triggers for HUEDevice
if (
!defined(
$attr{$dev}{"event-on-change-reading"}
)
|| $attr{$dev}{"event-on-change-reading"}
ne "none"
)
{
$attr{$dev}{"event-on-change-reading"} =
"none";
}
$hash->{helper}{ambiHueColor} = "$h$s$b";
# switch HUE to color
if ( $b ne "00" ) {
fhem(
"set $dev transitiontime 1 : noUpdate : hsv $h$s$b"
);
}
# switch HUE off if brightness is 0
else {
fhem(
"set $dev transitiontime 0 : noUpdate : off"
);
}
} }
} }
} }
my $latency = $hash->{helper}{ambiHueDelay} =
( $attr{$name}{ambiHueLatency} ) int(
? $attr{$name}{ambiHueLatency} / 100 ( gettimeofday() - $param->{timestamp} ) * 1000 + 0.5 );
: 0.2;
fhem("sleep $latency");
PHTV_SendCommand( $hash, "ambilight/processed" ); PHTV_SendCommand( $hash, "ambilight/processed" );
} }
@ -2201,6 +2394,8 @@ sub PHTV_ReceiveCommand($$$) {
&& !defined( $attr{$name}{ambiHueBottom} ) ) && !defined( $attr{$name}{ambiHueBottom} ) )
) )
{ {
delete $hash->{helper}{ambiHueDelay};
delete $hash->{helper}{ambiHueColor};
readingsBulkUpdate( $hash, "ambiHue", "off" ) readingsBulkUpdate( $hash, "ambiHue", "off" )
if ( $hash->{READINGS}{ambiHue}{VAL} ne "off" ); if ( $hash->{READINGS}{ambiHue}{VAL} ne "off" );
@ -2566,6 +2761,20 @@ sub PHTV_isinteger {
defined $_[0] && $_[0] =~ /^[+-]?\d+$/; defined $_[0] && $_[0] =~ /^[+-]?\d+$/;
} }
###################################
sub PHTV_bri2pct($) {
my ($bri) = @_;
return 0 if ( $bri <= 0 );
return int( $bri / 255 * 100 + 0.5 );
}
###################################
sub PHTV_pct2bri($) {
my ($pct) = @_;
return 0 if ( $pct <= 0 );
return int( $pct / 100 * 255 + 0.5 );
}
################################### ###################################
sub PHTV_hex2rgb($) { sub PHTV_hex2rgb($) {
my ($hex) = @_; my ($hex) = @_;
@ -2590,16 +2799,16 @@ sub PHTV_rgb2hex($$$) {
} }
################################### ###################################
sub PHTV_hex2hsv($;$) { sub PHTV_hex2hsb($;$) {
my ( $hex, $type ) = @_; my ( $hex, $type ) = @_;
$type = lc($type) if ( defined( ($type) && $type ne "" ) ); $type = lc($type) if ( defined( ($type) && $type ne "" ) );
my $rgb = PHTV_hex2rgb($hex); my $rgb = PHTV_hex2rgb($hex);
my $return = PHTV_rgb2hsv( $rgb->{r}, $rgb->{g}, $rgb->{b} ); my $return = PHTV_rgb2hsb( $rgb->{r}, $rgb->{g}, $rgb->{b} );
if ( defined($type) ) { if ( defined($type) ) {
return $return->{h} if ( $type eq "h" ); return $return->{h} if ( $type eq "h" );
return $return->{s} if ( $type eq "s" ); return $return->{s} if ( $type eq "s" );
return $return->{v} if ( $type eq "v" ); return $return->{b} if ( $type eq "b" );
} }
else { else {
return $return; return $return;
@ -2607,113 +2816,136 @@ sub PHTV_hex2hsv($;$) {
} }
################################### ###################################
sub PHTV_bri2pct($) { sub PHTV_hsb2hex($$$) {
my ($bri) = @_; my ( $h, $s, $b ) = @_;
return 0 if ( $bri <= 0 ); my $rgb = PHTV_hsb2rgb( $h, $s, $b );
return int( ( $bri / 255 * 100 ) + 0.5 );
}
###################################
sub PHTV_pct2bri($) {
my ($pct) = @_;
return 0 if ( $pct <= 0 );
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} ); return PHTV_rgb2hex( $rgb->{r}, $rgb->{g}, $rgb->{b} );
} }
################################### ###################################
sub PHTV_rgb2hsv($$$;$) { sub PHTV_rgb2hsb ($$$) {
my ( $r, $g, $b, $type ) = @_; my ( $r, $g, $b ) = @_;
$type = lc($type) if ( defined( ($type) && $type ne "" ) );
my ( $M, $m, $C, $H, $S, $V ); my $r2 = $r / 255.0;
my $g2 = $g / 255.0;
my $b2 = $b / 255.0;
my $hsv = PHTV_rgb2hsv( $r2, $g2, $b2 );
my $h = int( $hsv->{h} * 65535 );
my $s = int( $hsv->{s} * 255 );
my $bri = int( $hsv->{v} * 255 );
Log3 undef, 5, "PHTV rgb2hsb: $r $g $b > $h $s $bri";
return { "h" => $h, "s" => $s, "b" => $bri };
}
###################################
sub PHTV_hsb2rgb ($$$) {
my ( $h, $s, $bri ) = @_;
my $h2 = $h / 65535.0;
my $s2 = $s / 255.0;
my $bri2 = $bri / 255.0;
my $rgb = PHTV_hsv2rgb( $h2, $s2, $bri2 );
my $r = int( $rgb->{r} * 255 );
my $g = int( $rgb->{g} * 255 );
my $b = int( $rgb->{b} * 255 );
Log3 undef, 5, "PHTV hsb2rgb: $h $s $bri > $r $g $b";
return { "r" => $r, "g" => $g, "b" => $b };
}
###################################
sub PHTV_rgb2hsv($$$) {
my ( $r, $g, $b ) = @_;
my ( $M, $m, $c, $h, $s, $v );
$M = PHTV_max( $r, $g, $b ); $M = PHTV_max( $r, $g, $b );
$m = PHTV_min( $r, $g, $b ); $m = PHTV_min( $r, $g, $b );
$C = ( $M - $m ) if ( $M > 0 || $m > 0 ); $c = $M - $m;
$C = 0 if ( $M == 0 && $m == 0 );
if ( $C == 0 ) { if ( $c == 0 ) {
$H = 0; $h = 0;
$S = 0; }
elsif ( $M == $r ) {
$h = ( 60 * ( ( $g - $b ) / $c ) % 360 ) / 360;
}
elsif ( $M == $g ) {
$h = ( 60 * ( ( $b - $r ) / $c ) + 120 ) / 360;
}
elsif ( $M == $b ) {
$h = ( 60 * ( ( $r - $g ) / $c ) + 240 ) / 360;
}
if ( $M == 0 ) {
$s = 0;
} }
else { else {
if ( $r == $M ) { $s = $c / $M;
$H = ( $g - $b ) / $C;
$H += 6.0
if ( $H < 0.0 );
}
elsif ( $g == $M ) {
$H = ( ( $b - $r ) / $C ) + 2.0;
}
elsif ( $b == $M ) {
$H = ( ( $r - $g ) / $C ) + 4.0;
}
$H *= 60.0;
$S = $C / $M;
} }
$v = $M;
$V = $M; Log3 undef, 5, "PHTV rgb2hsv: $r $g $b > $h $s $v";
Log3 undef, 5, "PHTV rgb2hsv: $r $g $b > $H $S $V"; return { "h" => $h, "s" => $s, "v" => $v };
if ( defined($type) ) {
return $H if ( $type eq "h" );
return $S if ( $type eq "s" );
return $V if ( $type eq "v" );
}
else {
return { "h" => $H, "s" => $S, "v" => $V };
}
} }
################################### ###################################
sub PHTV_hsv2rgb($$$) { sub PHTV_hsv2rgb($$$) {
my ( $H, $S, $V ) = @_; my ( $h, $s, $v ) = @_;
my ( $r, $g, $b, $C, $Hdash, $X, $m ); my $r = 0.0;
my $g = 0.0;
my $b = 0.0;
$C = $S * $V; if ( $s == 0 ) {
$Hdash = $H / 60.0; $r = $v;
$X = $C * ( 1.0 - int( ( $Hdash % 2.0 ) - 1.0 ) ); $g = $v;
$b = $v;
}
else {
my $i = int( $h * 6.0 );
my $f = ( $h * 6.0 ) - $i;
my $p = $v * ( 1.0 - $s );
my $q = $v * ( 1.0 - $s * $f );
my $t = $v * ( 1.0 - $s * ( 1.0 - $f ) );
$i = $i % 6;
if ( $Hdash < 1.0 ) { if ( $i == 0 ) {
$r = $C; $r = $v;
$g = $X; $g = $t;
} $b = $p;
elsif ( $Hdash < 2.0 ) { }
$r = $X; elsif ( $i == 1 ) {
$g = $C; $r = $q;
} $g = $v;
elsif ( $Hdash < 3.0 ) { $b = $p;
$g = $C; }
$b = $X; elsif ( $i == 2 ) {
} $r = $p;
elsif ( $Hdash < 4.0 ) { $g = $v;
$g = $X; $b = $t;
$b = $C; }
} elsif ( $i == 3 ) {
elsif ( $Hdash < 5.0 ) { $r = $p;
$r = $X; $g = $q;
$b = $C; $b = $v;
} }
elsif ( $Hdash <= 6.0 ) { elsif ( $i == 4 ) {
$r = $C; $r = $t;
$b = $X; $g = $p;
$b = $v;
}
elsif ( $i == 5 ) {
$r = $v;
$g = $p;
$b = $q;
}
} }
$m = $V - $C; Log3 undef, 5, "PHTV hsv2rgb: $h $s $v > $r $g $b";
$r += $m;
$g += $m;
$b += $m;
Log3 undef, 5, "PHTV hsv2rgb: $H $S $V > $r $g $b";
return { "r" => $r, "g" => $g, "b" => $b }; return { "r" => $r, "g" => $g, "b" => $b };
} }
@ -2722,7 +2954,8 @@ sub PHTV_hsv2rgb($$$) {
sub PHTV_max { sub PHTV_max {
my ( $max, @vars ) = @_; my ( $max, @vars ) = @_;
for (@vars) { for (@vars) {
$max = $_ if $_ > $max; $max = $_
if $_ > $max;
} }
return $max; return $max;
} }
@ -2792,6 +3025,9 @@ sub PHTV_min {
<li><b>ambiMode</b> internal,manual,expert &nbsp;&nbsp;-&nbsp;&nbsp; set source register for Ambilight</li> <li><b>ambiMode</b> internal,manual,expert &nbsp;&nbsp;-&nbsp;&nbsp; set source register for Ambilight</li>
<li><b>ambiPreset</b> &nbsp;&nbsp;-&nbsp;&nbsp; set Ambilight to predefined state</li> <li><b>ambiPreset</b> &nbsp;&nbsp;-&nbsp;&nbsp; set Ambilight to predefined state</li>
<li><b>rgb</b> HEX,LED address &nbsp;&nbsp;-&nbsp;&nbsp; set an RGB value for Ambilight</li> <li><b>rgb</b> HEX,LED address &nbsp;&nbsp;-&nbsp;&nbsp; set an RGB value for Ambilight</li>
<li><b>hue</b> 0-65534 &nbsp;&nbsp;-&nbsp;&nbsp; set the color hue value Ambilight</li>
<li><b>sat</b> 0-255 &nbsp;&nbsp;-&nbsp;&nbsp; set the saturation value for Ambilight</li>
<li><b>bri</b> 0-255 &nbsp;&nbsp;-&nbsp;&nbsp; set the brightness value for Ambilight</li>
<li><b>play</b> &nbsp;&nbsp;-&nbsp;&nbsp; starts/resumes playback</li> <li><b>play</b> &nbsp;&nbsp;-&nbsp;&nbsp; starts/resumes playback</li>
<li><b>pause</b> &nbsp;&nbsp;-&nbsp;&nbsp; starts/resumes playback</li> <li><b>pause</b> &nbsp;&nbsp;-&nbsp;&nbsp; starts/resumes playback</li>
<li><b>stop</b> &nbsp;&nbsp;-&nbsp;&nbsp; stops current playback</li> <li><b>stop</b> &nbsp;&nbsp;-&nbsp;&nbsp; stops current playback</li>
@ -2826,6 +3062,44 @@ sub PHTV_min {
<br> <br>
<br> <br>
<br>
<br>
<div style="margin-left: 2em">
<u>Advanced Ambilight+HUE Control</u><br>
<br>
<div style="margin-left: 2em">
Linking to your HUE devices within attributes ambiHueLeft, ambiHueTop, ambiHueRight and ambiHueBottom uses some defaults to calculate the actual color.<br>
The following settings can be fine tuned:<br>
<br>
<li>LED(s) to be used as color source<br>
either 1 single LED or a few in a raw like 2-4. Defaults to use the middle LED and it's left and right partners. Counter starts at 1. See readings ambiLED* for how many LED's your TV has.</li>
<li>saturation in percent of the original value (1-99, default=100)</li>
<li>brightness in percent of the original value (1-99, default=100)</li>
<br><br>
Use the following addressing format for fine tuning:<br>
<code>devicename:&lt;LEDs$gt;&lt;saturation$gt;&lt;brightness$gt;</code>
<br><br>
<u>Examples:</u><br>
<div style="margin-left: 2em">
<code># to use only LED 4 from the top as source
attr PhilipsTV ambiHueTop HUEDevice0:4<br><br>
# to use a combination of LED's 1+2 as source
attr PhilipsTV ambiHueTop HUEDevice0:1-2<br><br>
# to use LED's 1+2 and only 90% of their saturation
attr PhilipsTV ambiHueTop HUEDevice0:1-2:90<br><br>
# to use LED's 1+2 and only 50% of their brightness
attr PhilipsTV ambiHueTop HUEDevice0:1-2::50<br><br>
# to use LED's 1+2, 90% saturation and 50% brightness
attr PhilipsTV ambiHueTop HUEDevice0:1-2:90:50
# to use default LED settings but only adjust their brightness to 50%
attr PhilipsTV ambiHueTop HUEDevice0:::50</code>
</div><br>
</div>
</div>
<br>
<br>
<a name="PHTVget"></a> <a name="PHTVget"></a>
<b>Get</b> <b>Get</b>
<ul> <ul>
@ -2847,11 +3121,10 @@ sub PHTV_min {
<a name="PHTVattr"></a> <a name="PHTVattr"></a>
<b>Attributes</b><br> <b>Attributes</b><br>
<ul><ul> <ul><ul>
<li><b>ambiHueLeft</b> - HUE devices that should get the color from left Ambilight. Add ":0"-":x" if you would like to use a specific LED as color reference</li> <li><b>ambiHueLeft</b> - HUE devices that should get the color from left Ambilight.</li>
<li><b>ambiHueTop</b> - HUE devices that should get the color from top Ambilight. Add ":0"-":x" if you would like to use a specific LED as color reference</li> <li><b>ambiHueTop</b> - HUE devices that should get the color from top Ambilight.</li>
<li><b>ambiHueRight</b> - HUE devices that should get the color from right Ambilight. Add ":0"-":x" if you would like to use a specific LED as color reference</li> <li><b>ambiHueRight</b> - HUE devices that should get the color from right Ambilight.</li>
<li><b>ambiHueBottom</b> - HUE devices that should get the color from bottom Ambilight. Add ":0"-":x" if you would like to use a specific LED as color reference</li> <li><b>ambiHueBottom</b> - HUE devices that should get the color from bottom Ambilight.</li>
<li><b>ambiHueLatency</b> - Controls the update interval for HUE devices in milliseconds; defaults to 200 ms. Note: This has huge impact on the performance of your FHEM installation!</li>
<li><b>disable</b> - Disable polling (true/false)</li> <li><b>disable</b> - Disable polling (true/false)</li>
<li><b>inputs</b> - Presents the inputs read from device. Inputs can be renamed by adding <code>,NewName</code> right after the original name.</li> <li><b>inputs</b> - Presents the inputs read from device. Inputs can be renamed by adding <code>,NewName</code> right after the original name.</li>
<li><b>timeout</b> - Set different polling timeout in seconds (default=7)</li> <li><b>timeout</b> - Set different polling timeout in seconds (default=7)</li>