############################################## # $Id$ # TODO # versions # 00 POC # .. # 50 Stable # 51 new milight color converter # 52 timing for transitions: drop frames if required # 53 transition names and events # 54 drop frames ll add-on / lock ll queue # 55 add ll queue lock count # 56 RGB in # 57 bridge v2 # 58 gamma correction # 59 fix "off" with ramp # 60 add dimup/dimdown # 61 introduce dimSteps # 62 introduce defaultColor # 63 LW12 define if lw12 is unavailable at startup # 64 transition with ramp 0 fixed # 65 some typos with impact to RGBW1 and RGBW2 # 66 readings: lower camelCase and limited trigger # 67 restore state after startup # 68 LW12 reconnect after timeout # 69 RGBW1 timing improved # 70 colorpicker # 71 default ramp attrib # 72 add LD316 # 73 add LD382 # 74 add color calibration (hue intersections) for RGB type controller # 75 add white point adjustment for RGB type controller # 76 add LW12 HX001 # 77 milight RGBW2: critical cmds sendout repeatly # 78 add attrib for color managment (rgb types) # 79 add LD382 RGB ony mode # 80 HSV2fourChannel bug fixed (thnx to lexorius) # 81 LW12FC added # 82 LD382A (FW 1.0.6) # 83 fixed ramp handling (thnx to henryk) # 84 sengled boost added (thnx to scooty) # 85 milight white, improved reliability # 86 milight white, improved reliability / mark II # 87 milight rgbw2, dim bug # 88 readingFnAttributes # 89 add LD316A # 90 Sunricher poc # 91 milight colorcast fixed, more robust tcp re-connect # verbose level # 0: quit # 1: error # 2: warning # 3: user command # 4: 1st technical level (detailed internal reporting) # 5: 2nd technical level (full internal reporting) package main; use strict; use warnings; use IO::Handle; use IO::Socket; use IO::Select; use Time::HiRes; use Data::Dumper; use Color; sub WifiLight_Initialize(@) { my ($hash) = @_; FHEM_colorpickerInit(); $hash->{DefFn} = "WifiLight_Define"; $hash->{UndefFn} = "WifiLight_Undef"; $hash->{ShutdownFn} = "WifiLight_Undef"; $hash->{SetFn} = "WifiLight_Set"; $hash->{GetFn} = "WifiLight_Get"; $hash->{AttrFn} = "WifiLight_Attr"; $hash->{NotifyFn} = "WifiLight_Notify"; $hash->{AttrList} = "gamma dimStep defaultColor defaultRamp colorCast whitePoint" ." $readingFnAttributes"; return undef; } sub WifiLight_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); my $name = $a[0]; my $key; return "wrong syntax: define WifiLight " if(@a != 4); # return "unknown LED type ($a[2]): choose one of RGB, RGBW, RGBW1, RGBW2, White" unless (grep /$a[2]/, ('RGB', 'RGBW', 'RGBW1', 'RGBW2', 'White')); $hash->{LEDTYPE} = $a[2]; my $otherLights; if ($a[3] =~ m/(bridge-V2):([^:]+):*(\d+)*/g) { $hash->{CONNECTION} = $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:50000; $hash->{PROTO} = 0; #my @hlCmdQueue = []; @{$hash->{helper}->{hlCmdQueue}} = (); #\@hlCmdQueue; # $hash->{SERVICE} = 48899; unkown for v2 # search if this bridge is already defined # if so, we need a shared buffer (llCmdQueue), shared socket and we need to check if the requied slot is free foreach $key (keys %defs) { if (($defs{$key}{TYPE} eq 'WifiLight') && ($defs{$key}{IP} eq $hash->{IP}) && ($key ne $name)) { #bridge is in use Log3 (undef, 3, "WifiLight: requested bridge $hash->{CONNECTION} at $hash->{IP} already in use by $key, copy llCmdQueue"); $hash->{helper}->{llCmdQueue} = $defs{$key}{helper}{llCmdQueue}; $hash->{helper}->{llLock} = 0; $hash->{helper}->{SOCKET} = $defs{$key}{helper}{SOCKET}; $hash->{helper}->{SELECT} = $defs{$key}{helper}{SELECT}; my $slotInUse = $defs{$key}{SLOT}; $otherLights->{$slotInUse} = $defs{$key}; } } if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => 48899, Blocking => 0, Proto => 'udp', Broadcast => 1) or return "can't bind: $@"; my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = {}; $hash->{helper}->{llLock} = 0; } } if ($a[3] =~ m/(bridge-V3):([^:]+):*(\d+)*/g) { $hash->{CONNECTION} = $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:8899; $hash->{PROTO} = 0; #my @hlCmdQueue = []; @{$hash->{helper}->{hlCmdQueue}} = (); #\@hlCmdQueue; # $hash->{SERVICE} = 48899; # search if this bridge is already defined # if so, we need a shared buffer (llCmdQueue), shared socket and we need to check if the requied slot is free foreach $key (keys %defs) { if (($defs{$key}{TYPE} eq 'WifiLight') && ($defs{$key}{IP} eq $hash->{IP}) && ($defs{$key}{PORT} eq $hash->{PORT}) && ($key ne $name)) { #bridge is in use Log3 (undef, 3, "WifiLight: requested bridge $hash->{CONNECTION} at $hash->{IP} already in use by $key, copy llCmdQueue"); $hash->{helper}->{llCmdQueue} = $defs{$key}{helper}{llCmdQueue}; $hash->{helper}->{llLock} = 0; $hash->{helper}->{SOCKET} = $defs{$key}{helper}{SOCKET}; $hash->{helper}->{SELECT} = $defs{$key}{helper}{SELECT}; my $slotInUse = $defs{$key}{SLOT}; $otherLights->{$slotInUse} = $defs{$key}; } } if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => 48899, Blocking => 0, Proto => 'udp', Broadcast => 1) or return "can't bind: $@"; my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; } } if ($a[3] =~ m/(LW12):([^:]+):*(\d+)*/g) { return "only RGB supported by LW12" if ($a[2] ne "RGB"); $hash->{CONNECTION} = $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:5577; $hash->{PROTO} = 1; #$hash->{SERVICE} = 48899; $hash->{SLOT} = 0; @{$hash->{helper}->{hlCmdQueue}} = (); if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => $hash->{PORT}, PeerAddr => $hash->{IP}, Timeout => 1, Blocking => 0, Proto => 'tcp') or Log3 ($hash, 3, "define $hash->{NAME}: can't reach ($@)"); my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; } } if ($a[3] =~ m/(LW12HX):([^:]+):*(\d+)*/g) { return "only RGB supported by LW12HX" if ($a[2] ne "RGB"); $hash->{CONNECTION} = $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:5000; $hash->{PROTO} = 1; #$hash->{SERVICE} = 48899; $hash->{SLOT} = 0; @{$hash->{helper}->{hlCmdQueue}} = (); if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => $hash->{PORT}, PeerAddr => $hash->{IP}, Timeout => 1, Blocking => 0, Proto => 'tcp') or Log3 ($hash, 3, "define $hash->{NAME}: can't reach ($@)"); my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; } } if ($a[3] =~ m/(LW12FC):([^:]+):*(\d+)*/g) { return "only RGB supported by LW12FC" if ($a[2] ne "RGB"); $hash->{CONNECTION} = $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:5000; #$hash->{PROTO} = 1; #$hash->{SERVICE} = 48899; $hash->{SLOT} = 0; @{$hash->{helper}->{hlCmdQueue}} = (); if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => $hash->{PORT}, PeerAddr => $hash->{IP}, Blocking => 0, Proto => 'udp') or Log3 ($hash, 3, "define $hash->{NAME}: can't reach ($@)"); my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; } } if ($a[3] =~ m/(LD316):([^:]+):*(\d+)*/g) { return "only RGBW supported by LD316" if ($a[2] ne "RGBW"); $hash->{CONNECTION} = $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:5577; $hash->{PROTO} = 1; $hash->{SLOT} = 0; @{$hash->{helper}->{hlCmdQueue}} = (); if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => $hash->{PORT}, PeerAddr => $hash->{IP}, Timeout => 1, Blocking => 0, Proto => 'tcp') or Log3 ($hash, 3, "define $hash->{NAME}: can't reach ($@)"); my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; } } if ($a[3] =~ m/(LD316A):([^:]+):*(\d+)*/g) { return "only RGBW supported by LD316A" if ($a[2] ne "RGBW"); $hash->{CONNECTION} = $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:5577; $hash->{PROTO} = 1; $hash->{SLOT} = 0; @{$hash->{helper}->{hlCmdQueue}} = (); if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => $hash->{PORT}, PeerAddr => $hash->{IP}, Timeout => 1, Blocking => 0, Proto => 'tcp') or Log3 ($hash, 3, "define $hash->{NAME}: can't reach ($@)"); my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; } } if ($a[3] =~ m/(LD382):([^:]+):*(\d+)*/g) { return "only RGB and RGBW supported by LD382" if (($a[2] ne "RGB") && ($a[2] ne "RGBW")); $hash->{CONNECTION} = $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:5577; $hash->{PROTO} = 1; #$hash->{SERVICE} = 48899; $hash->{SLOT} = 0; @{$hash->{helper}->{hlCmdQueue}} = (); if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => $hash->{PORT}, PeerAddr => $hash->{IP}, Timeout => 1, Blocking => 0, Proto => 'tcp') or Log3 ($hash, 3, "define $hash->{NAME}: can't reach ($@)"); my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; } } if ($a[3] =~ m/(LD382A):([^:]+):*(\d+)*/g) { return "only RGB and RGBW supported by LD382A" if (($a[2] ne "RGB") && ($a[2] ne "RGBW")); $hash->{CONNECTION} = $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:5577; $hash->{PROTO} = 1; #$hash->{SERVICE} = 48899; $hash->{SLOT} = 0; @{$hash->{helper}->{hlCmdQueue}} = (); if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => $hash->{PORT}, PeerAddr => $hash->{IP}, Timeout => 1, Blocking => 0, Proto => 'tcp') or Log3 ($hash, 3, "define $hash->{NAME}: can't reach ($@)"); my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; } } if ($a[3] =~ m/(SENGLED):([^:]+):*(\d+)*/g) { return "only White supported by SENGLED" if ($a[2] ne "White"); $hash->{CONNECTION} = $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:9060; @{$hash->{helper}->{hlCmdQueue}} = (); if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => $hash->{PORT}, PeerAddr => $hash->{IP}, Blocking => 0, Proto => 'udp') or Log3 ($hash, 3, "define $hash->{NAME}: can't reach ($@)"); my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; } } if ($a[3] =~ m/(SUNRICHER):([^:]+):*(\d+)*/gi) { # return "only White, DualWhite, RGB, RGBW supported by Sunricher" if ($a[2] ne 'RGBW'); $hash->{CONNECTION} = uc $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:8899; $hash->{PROTO} = 1; $hash->{SLOT} = 0; @{$hash->{helper}->{hlCmdQueue}} = (); @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => $hash->{PORT}, PeerAddr => $hash->{IP}, Timeout => 1, Blocking => 0, Proto => 'tcp') or Log3 ($hash, 3, "define $hash->{NAME}: can't reach ($@)"); my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; } } if ($a[3] =~ m/(SUNRICHERA):([^:]+):*(\d+)*/gi) { # return "only White, DualWhite, RGB, RGBW supported by Sunricher" if ($a[2] ne 'RGBW'); $hash->{CONNECTION} = uc $1; $hash->{IP} = $2; $hash->{PORT} = $3?$3:8899; $hash->{PROTO} = 1; $hash->{SLOT} = 0; @{$hash->{helper}->{hlCmdQueue}} = (); if (!defined($hash->{helper}->{SOCKET})) { my $sock = IO::Socket::INET-> new ( PeerPort => $hash->{PORT}, PeerAddr => $hash->{IP}, Timeout => 1, Blocking => 0, Proto => 'tcp') or Log3 ($hash, 3, "define $hash->{NAME}: can't reach ($@)"); my $select = IO::Select->new($sock); $hash->{helper}->{SOCKET} = $sock; $hash->{helper}->{SELECT} = $select; @{$hash->{helper}->{llCmdQueue}} = (); $hash->{helper}->{llLock} = 0; } } return "unknown connection type, see documentation" if !(defined($hash->{CONNECTION})); Log3 ($hash, 4, "define $a[0] $a[1] $a[2] $a[3]"); if (($hash->{LEDTYPE} eq 'RGB') && ($hash->{CONNECTION} =~ 'LW12')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -20, -20, -25, 0, -10'; $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 0.75, 0.25'; return undef; } if (($hash->{LEDTYPE} eq 'RGB') && ($hash->{CONNECTION} =~ 'LW12HX')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -20, -20, -25, 0, -10'; $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 0.75, 0.25'; return undef; } if (($hash->{LEDTYPE} eq 'RGB') && ($hash->{CONNECTION} =~ 'LW12FC')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.85); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -20, -20, -25, 0, -10'; $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 0.85, 0.55'; return undef; } if (($hash->{LEDTYPE} eq 'RGBW') && ($hash->{CONNECTION} =~ 'LD316')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -25, -15, -25, 0, -20'; $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1.0, 0.6, 0.065'; return undef; } if (($hash->{LEDTYPE} eq 'RGBW') && ($hash->{CONNECTION} =~ 'LD316A')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -25, -15, -25, 0, -20'; $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1.0, 0.6, 0.065'; return undef; } if (($hash->{LEDTYPE} eq 'RGBW') && ($hash->{CONNECTION} =~ 'LD382')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -20, -20, -25, 0, -10'; $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 1, 1'; return undef; } if (($hash->{LEDTYPE} eq 'RGB') && ($hash->{CONNECTION} =~ 'LD382')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -20, -20, -25, 0, -10'; $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 0.75, 0.25'; return undef; } if (($hash->{LEDTYPE} eq 'RGBW') && ($hash->{CONNECTION} =~ 'LD382A')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -20, -20, -25, 0, -10'; $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 1, 1'; return undef; } if (($hash->{LEDTYPE} eq 'RGB') && ($hash->{CONNECTION} =~ 'LD382A')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -20, -20, -25, 0, -10'; $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 0.75, 0.25'; return undef; } if ((($hash->{LEDTYPE} eq 'RGB') || ($hash->{LEDTYPE} eq 'RGBW1')) && ($hash->{CONNECTION} =~ 'bridge-V[2|3]')) { return "no free slot at $hash->{CONNECTION} ($hash->{IP}) for $hash->{LEDTYPE}" if (defined($otherLights->{0})); $hash->{SLOT} = 0; $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 1); $hash->{helper}->{COLORMAP} = WifiLight_Milight_ColorConverter($hash); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB sync pair unpair"; #if we are allready paired: sync to get a defined state return WifiLight_RGB_Sync($hash) if ($hash->{LEDTYPE} eq 'RGB'); return WifiLight_RGBW1_Sync($hash) if ($hash->{LEDTYPE} eq 'RGBW1'); } elsif (($hash->{LEDTYPE} eq 'RGBW2') && ($hash->{CONNECTION} =~ 'bridge-V3')) { # find a free slot my $i = 5; while (defined($otherLights->{$i})) { $i++; } if ( grep { $i == $_ } 5..8 ) { $hash->{SLOT} = $i; $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.73); $hash->{helper}->{COLORMAP} = WifiLight_Milight_ColorConverter($hash); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB sync pair unpair"; return WifiLight_RGBW2_Sync($hash); } else { return "no free slot at $hash->{CONNECTION} ($hash->{IP}) for $hash->{LEDTYPE}"; } } elsif (($hash->{LEDTYPE} eq 'White') && ($hash->{CONNECTION} =~ 'bridge-V[2|3]')) { # find a free slot my $i = 1; while (defined($otherLights->{$i})) { $i++; } if ( grep { $i == $_ } 1..4 ) { $hash->{SLOT} = $i; $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.8); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown sync pair unpair"; return WifiLight_White_Sync($hash); } else { return "no free slot at $hash->{CONNECTION} ($hash->{IP}) for $hash->{LEDTYPE}"; } } if (($hash->{LEDTYPE} eq 'White') && ($hash->{CONNECTION} =~ 'SENGLED')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 1); $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown"; return undef; } if (($hash->{LEDTYPE} eq 'DualWhite') && ($hash->{CONNECTION} eq 'SUNRICHER')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); # TODO CHECK VALUES $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown"; $hash->{helper}->{wLevel} = 0; # color cast defaults in r,y, g, c, b, m: +/-30° # my $cc = '0, -20, -20, -25, 0, -10'; # TODO CHECK VALUES # $attr{$name}{"colorCast"} = $cc; # WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b # $attr{$name}{"whitePoint"} = '1, 1, 1'; return undef; } if (($hash->{LEDTYPE} eq 'RGB') && ($hash->{CONNECTION} eq 'SUNRICHER')) { $hash->{helper}->{COMMANDSET} = 'on off dim dimup dimdown HSV HSVK CT RGB'; # init helper $hash->{helper}->{rLevel} = 0; $hash->{helper}->{gLevel} = 0; $hash->{helper}->{bLevel} = 0; # init converter $hash->{helper}->{cmd_pwr} = 'RGBSunricher_setPWR'; $hash->{helper}->{cmd_hsv} = 'RGBSunricher_setHSV'; # defaults $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); # TODO CHECK VALUES # color cast defaults my $cc = '0, -20, -20, -25, 0, -10'; # TODO CHECK VALUES $attr{$name}{"colorCast"} = '0, -20, -20, -25, 0, -10'; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 1, 1'; # TODO init readings return undef; } if (($hash->{LEDTYPE} eq 'RGB') && ($hash->{CONNECTION} eq 'SUNRICHERA')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); # TODO CHECK VALUES $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; $hash->{helper}->{rLevel} = 0; $hash->{helper}->{gLevel} = 0; $hash->{helper}->{bLevel} = 0; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -20, -20, -25, 0, -10'; # TODO CHECK VALUES $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 1, 1'; return undef; } if (($hash->{LEDTYPE} eq 'RGBW') && ($hash->{CONNECTION} eq 'SUNRICHER')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); # TODO CHECK VALUES $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; $hash->{helper}->{rLevel} = 0; $hash->{helper}->{gLevel} = 0; $hash->{helper}->{bLevel} = 0; $hash->{helper}->{wLevel} = 0; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -20, -20, -25, 0, -10'; # TODO CHECK VALUES $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 1, 1'; return undef; } if (($hash->{LEDTYPE} eq 'RGBW') && ($hash->{CONNECTION} eq 'SUNRICHERA')) { $hash->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($hash, 0.65); # TODO CHECK VALUES $hash->{helper}->{COMMANDSET} = "on off dim dimup dimdown HSV RGB"; $hash->{helper}->{rLevel} = 0; $hash->{helper}->{gLevel} = 0; $hash->{helper}->{bLevel} = 0; $hash->{helper}->{wLevel} = 0; # color cast defaults in r,y, g, c, b, m: +/-30° my $cc = '0, -20, -20, -25, 0, -10'; # TODO CHECK VALUES $attr{$name}{"colorCast"} = $cc; WifiLight_RGB_ColorConverter($hash, split(',', $cc)); # white point defaults in r,g,b $attr{$name}{"whitePoint"} = '1, 1, 1'; return undef; } return "$hash->{LEDTYPE} is not supported at $hash->{CONNECTION} ($hash->{IP})"; } sub WifiLight_Undef(@) { return undef; } sub WifiLight_Set(@) { my ($ledDevice, $name, $cmd, @args) = @_; my $descriptor = ''; # remove descriptor from @args for (my $i = $#args; $i >= 0; --$i ) { if ($args[$i] =~ /\/d\:(.*)/) { $descriptor = $1; splice (@args, $i, 1); } } my $cnt = @args; my $ramp = 0; my $flags = ""; my $event = undef; my $cmdSet = $ledDevice->{helper}->{COMMANDSET}; return "unknown command ($cmd): choose one of ".join(", ", $cmdSet) if ($cmd eq "?"); return "unknown command ($cmd): choose one of ".$ledDevice->{helper}->{COMMANDSET} if ($cmd ne 'RGB') and not ( grep { $cmd eq $_ } split(" ", $ledDevice->{helper}->{COMMANDSET} )); if ($cmd eq 'pair') { WifiLight_HighLevelCmdQueue_Clear($ledDevice); if (defined($args[0])) { return "usage: set $name pair [seconds]" if ($args[0] !~ /^\d+$/); $ramp = $args[0]; } return WifiLight_RGB_Pair($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW1_Pair($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW1') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW2_Pair($ledDevice, $ramp) if ($ledDevice->{LEDTYPE} eq 'RGBW2'); return WifiLight_White_Pair($ledDevice, $ramp) if ($ledDevice->{LEDTYPE} eq 'White'); } if ($cmd eq 'unpair') { WifiLight_HighLevelCmdQueue_Clear($ledDevice); if (defined($args[0])) { return "usage: set $name unpair [seconds]" if ($args[0] !~ /^\d+$/); $ramp = $args[0]; } return WifiLight_RGB_UnPair($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW1_UnPair($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW1') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW2_UnPair($ledDevice, $ramp) if ($ledDevice->{LEDTYPE} eq 'RGBW2'); return WifiLight_White_UnPair($ledDevice, $ramp) if ($ledDevice->{LEDTYPE} eq 'White'); } if ($cmd eq 'sync') { WifiLight_HighLevelCmdQueue_Clear($ledDevice); return WifiLight_RGB_Sync($ledDevice) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW1_Sync($ledDevice) if (($ledDevice->{LEDTYPE} eq 'RGBW1') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW2_Sync($ledDevice) if ($ledDevice->{LEDTYPE} eq 'RGBW2'); return WifiLight_White_Sync($ledDevice) if ($ledDevice->{LEDTYPE} eq 'White'); } if (($cmd eq 'HSV') || ($cmd eq 'RGB') || ($cmd eq 'dim')) { $args[1] = AttrVal($ledDevice->{NAME}, "defaultRamp", 0) if !defined($args[1]); } else { $args[0] = AttrVal($ledDevice->{NAME}, "defaultRamp", 0) if !defined($args[0]); } if ($cmd eq 'on') { WifiLight_HighLevelCmdQueue_Clear($ledDevice); if (defined($args[0])) { return "usage: set $name on [seconds]" if ($args[0] !~ /^\d?.?\d+$/); $ramp = $args[0]; } return WifiLight_RGBWLD316_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316')); return WifiLight_RGBWLD316A_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316A')); return WifiLight_RGBWLD382_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBLD382_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBWLD382A_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLD382A_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLW12_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12')); return WifiLight_RGBLW12HX_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12HX')); return WifiLight_RGBLW12FC_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12FC')); return WifiLight_WhiteSENGLED_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} eq 'SENGLED')); return WifiLight_RGB_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW1_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW1') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW2_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW2') && ($ledDevice->{CONNECTION} eq 'bridge-V3')); return WifiLight_White_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_DualWhiteSunricher_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'DualWhite') && ($ledDevice->{CONNECTION} eq 'SUNRICHER')); return WifiLight_RGBSunricher_On($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} =~ /^RGB.?$/) && ($ledDevice->{CONNECTION} =~ /^SUNRICHER.?$/)); } if ($cmd eq 'off') { WifiLight_HighLevelCmdQueue_Clear($ledDevice); if (defined($args[0])) { return "usage: set $name off [seconds]" if ($args[0] !~ /^\d?.?\d+$/); $ramp = $args[0]; } return WifiLight_RGBWLD316_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316')); return WifiLight_RGBWLD316A_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316A')); return WifiLight_RGBWLD382_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBLD382_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBWLD382A_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLD382A_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLW12_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12')); return WifiLight_RGBLW12HX_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12HX')); return WifiLight_RGBLW12FC_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12FC')); return WifiLight_WhiteSENGLED_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} eq 'SENGLED')); return WifiLight_RGB_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW1_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW1') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW2_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'RGBW2') && ($ledDevice->{CONNECTION} eq 'bridge-V3')); return WifiLight_White_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_DualWhiteSunricher_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} eq 'DualWhite') && ($ledDevice->{CONNECTION} eq 'SUNRICHER')); return WifiLight_RGBSunricher_Off($ledDevice, $ramp) if (($ledDevice->{LEDTYPE} =~ /^RGB.?$/) && ($ledDevice->{CONNECTION} =~ /^SUNRICHER.?$/)); } if ($cmd eq 'dimup') { return "usage: set $name dimup" if (defined($args[1])); WifiLight_HighLevelCmdQueue_Clear($ledDevice); my $v = ReadingsVal($ledDevice->{NAME}, "brightness", 0) + AttrVal($ledDevice->{NAME}, "dimStep", 7); $v = 100 if $v > 100; return WifiLight_RGBWLD316_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316')); return WifiLight_RGBWLD316A_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316A')); return WifiLight_RGBWLD382_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBLD382_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBWLD382A_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLD382A_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLW12_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12')); return WifiLight_RGBLW12HX_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12HX')); return WifiLight_RGBLW12FC_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12FC')); return WifiLight_WhiteSENGLED_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} eq 'SENGLED')); return WifiLight_RGB_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW1_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW1') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW2_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW2') && ($ledDevice->{CONNECTION} eq 'bridge-V3')); return WifiLight_White_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_DualWhiteSunricher_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'DualWhite') && ($ledDevice->{CONNECTION} eq 'SUNRICHER')); return WifiLight_RGBSunricher_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} =~ /^RGB.?$/) && ($ledDevice->{CONNECTION} =~ /^SUNRICHER.?$/)); } if ($cmd eq 'dimdown') { return "usage: set $name dimdown" if (defined($args[1])); WifiLight_HighLevelCmdQueue_Clear($ledDevice); my $v = ReadingsVal($ledDevice->{NAME}, "brightness", 0) - AttrVal($ledDevice->{NAME}, "dimStep", 7); $v = 0 if $v < 0; return WifiLight_RGBWLD316_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316')); return WifiLight_RGBWLD316A_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316A')); return WifiLight_RGBWLD382_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBLD382_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBWLD382A_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLD382A_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLW12_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12')); return WifiLight_RGBLW12HX_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12HX')); return WifiLight_RGBLW12FC_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12FC')); return WifiLight_WhiteSENGLED_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} eq 'SENGLED')); return WifiLight_RGB_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW1_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW1') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW2_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'RGBW2') && ($ledDevice->{CONNECTION} eq 'bridge-V3')); return WifiLight_White_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_DualWhiteSunricher_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} eq 'DualWhite') && ($ledDevice->{CONNECTION} eq 'SUNRICHER')); return WifiLight_RGBSunricher_Dim($ledDevice, $v, 0, '') if (($ledDevice->{LEDTYPE} =~ /^RGB.?$/) && ($ledDevice->{CONNECTION} =~ /^SUNRICHER.?$/)); } if ($cmd eq 'dim') { return "usage: set $name dim level [seconds]" if ($args[0] !~ /^\d+$/); return "usage: set $name dim level [seconds]" if (($args[0] < 0) || ($args[0] > 100)); if (defined($args[1])) { return "usage: set $name dim level [seconds] [q]" if ($args[1] !~ /^\d?.?\d+$/); $ramp = $args[1]; } if (defined($args[2])) { return "usage: set $name dim level seconds [q]" if ($args[2] !~ m/.*[qQ].*/); $flags = $args[2]; } WifiLight_HighLevelCmdQueue_Clear($ledDevice) if ($flags !~ m/.*[qQ].*/); return WifiLight_RGBWLD316_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316')); return WifiLight_RGBWLD316A_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316A')); return WifiLight_RGBWLD382_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBLD382_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBWLD382A_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLD382A_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLW12_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12')); return WifiLight_RGBLW12HX_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12HX')); return WifiLight_RGBLW12FC_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LW12FC')); return WifiLight_WhiteSENGLED_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} eq 'SENGLED')); return WifiLight_RGB_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW1_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGBW1') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW2_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'RGBW2') && ($ledDevice->{CONNECTION} eq 'bridge-V3')); return WifiLight_White_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_DualWhiteSunricher_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} eq 'DualWhite') && ($ledDevice->{CONNECTION} eq 'SUNRICHER')); return WifiLight_RGBSunricher_Dim($ledDevice, $args[0], $ramp, $flags) if (($ledDevice->{LEDTYPE} =~ /^RGB.?$/) && ($ledDevice->{CONNECTION} =~ /^SUNRICHER.?$/)); } if (($cmd eq 'HSV') || ($cmd eq 'RGB')) { my ($hue, $sat, $val); if ($cmd eq 'HSV') { return "HSV is required as h,s,v" if (defined($args[0]) && $args[0] !~ /^\d{1,3},\d{1,3},\d{1,3}$/); ($hue, $sat, $val) = split(',', $args[0]); return "wrong hue ($hue): valid range 0..360" if !(($hue >= 0) && ($hue <= 360)); return "wrong saturation ($sat): valid range 0..100" if !(($sat >= 0) && ($sat <= 100)); return "wrong brightness ($val): valid range 0..100" if !(($val >= 0) && ($val <= 100)); } elsif ($cmd eq 'RGB') { return "RGB is required hex RRGGBB" if (defined($args[0]) && $args[0] !~ /^[0-9A-Fa-f]{6}$/); ($hue, $sat, $val) = WifiLight_RGB2HSV($ledDevice, $args[0]); } if (defined($args[1])) { return "usage: set $name HSV H,S,V seconds flags programm" if ($args[1] !~ /^\d?.?\d+$/); $ramp = $args[1]; } if (defined($args[2])) { return "usage: set $name HSV H,S,V seconds [slq] programm" if ($args[2] !~ m/.*[sSlLqQ].*/); $flags = $args[2]; } if (defined($args[3])) { return "usage: set $name HSV H,S,V seconds flags programm=[A-Za-z_0-9]" if ($args[3] !~ m/[A-Za-z_0-9]*/); $event = $args[3]; } WifiLight_HighLevelCmdQueue_Clear($ledDevice) if ($flags !~ m/.*[qQ].*/); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 100, $event) if ($ledDevice->{CONNECTION} eq 'LD316'); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 100, $event) if ($ledDevice->{CONNECTION} eq 'LD316A'); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 100, $event) if ($ledDevice->{CONNECTION} eq 'LD382'); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 100, $event) if ($ledDevice->{CONNECTION} eq 'LD382A'); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 100, $event) if ($ledDevice->{CONNECTION} eq 'LW12'); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 100, $event) if ($ledDevice->{CONNECTION} eq 'LW12HX'); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 100, $event) if ($ledDevice->{CONNECTION} eq 'LW12FC'); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 500, $event) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 1000, $event) if (($ledDevice->{LEDTYPE} eq 'RGBW1') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 200, $event) if (($ledDevice->{LEDTYPE} eq 'RGBW2') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); WifiLight_HSV_Transition($ledDevice, $hue, $sat, $val, $ramp, $flags, 100, $event) if (($ledDevice->{LEDTYPE} =~ /^RGB.?$/) && ($ledDevice->{CONNECTION} =~ /^SUNRICHER.?$/)); return WifiLight_SetHSV_Target($ledDevice, $hue, $sat, $val); } } sub WifiLight_Get(@) { my ($ledDevice, $name, $cmd, @args) = @_; my $cnt = @args; return undef; } sub WifiLight_Attr(@) { my ($cmd, $device, $attribName, $attribVal) = @_; my $ledDevice = $defs{$device}; if ($cmd eq 'set' && $attribName eq 'gamma') { return "gamma is required as numerical value (eg. 0.5 or 2.2)" if ($attribVal !~ /^\d*\.\d*$/); $ledDevice->{helper}->{GAMMAMAP} = WifiLight_CreateGammaMapping($ledDevice, $attribVal); } if ($cmd eq 'set' && $attribName eq 'dimStep') { return "dimStep is required as numerical value [1..100]" if ($attribVal !~ /^\d*$/) || (($attribVal < 1) || ($attribVal > 100)); } if ($cmd eq 'set' && $attribName eq 'defaultColor') { return "defaultColor is required as HSV" if ($attribVal !~ /^\d{1,3},\d{1,3},\d{1,3}$/); my ($hue, $sat, $val) = split(',', $attribVal); return "defaultColor: wrong hue ($hue): valid range 0..360" if !(($hue >= 0) && ($hue <= 360)); return "defaultColor: wrong saturation ($sat): valid range 0..100" if !(($sat >= 0) && ($sat <= 100)); return "defaultColor: wrong brightness ($val): valid range 0..100" if !(($val >= 0) && ($val <= 100)); } my @a = (); if ($cmd eq 'set' && $attribName eq 'colorCast') { @a = split(',', $attribVal); my $msg = "colorCast: correction require red, yellow, green ,cyan, blue, magenta (each in a range of -29 .. 29)"; return $msg unless (@a == 6); foreach my $tc (@a) { return $msg unless ($tc =~ m/^\s*[\-]{0,1}[0-9]+[\.]{0,1}[0-9]*\s*$/g); return $msg if (abs($tc) >= 30); } WifiLight_RGB_ColorConverter($ledDevice, @a) if ($ledDevice->{CONNECTION} eq 'LD316'); WifiLight_RGB_ColorConverter($ledDevice, @a) if ($ledDevice->{CONNECTION} eq 'LD316A'); WifiLight_RGB_ColorConverter($ledDevice, @a) if ($ledDevice->{CONNECTION} eq 'LD382'); WifiLight_RGB_ColorConverter($ledDevice, @a) if ($ledDevice->{CONNECTION} eq 'LD382A'); WifiLight_RGB_ColorConverter($ledDevice, @a) if ($ledDevice->{CONNECTION} eq 'LW12'); WifiLight_RGB_ColorConverter($ledDevice, @a) if ($ledDevice->{CONNECTION} eq 'LW12HX'); WifiLight_RGB_ColorConverter($ledDevice, @a) if ($ledDevice->{CONNECTION} eq 'LW12FC'); WifiLight_Milight_ColorConverter($ledDevice, @a) if ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]'); WifiLight_RGB_ColorConverter($ledDevice, @a) if (($ledDevice->{LEDTYPE} =~ /^RGB.?$/) && ($ledDevice->{CONNECTION} =~ /^SUNRICHER.?$/)); if ($init_done && !(@{$ledDevice->{helper}->{hlCmdQueue}})) { my $hue = $ledDevice->{READINGS}->{hue}->{VAL}; my $sat = $ledDevice->{READINGS}->{saturation}->{VAL}; my $val = $ledDevice->{READINGS}->{brightness}->{VAL}; WifiLight_setHSV($ledDevice, $hue, $sat, $val, 1); } } if ($cmd eq 'set' && $attribName eq 'whitePoint') { @a = split(',', $attribVal); my $msg = "whitePoint: correction require red, green, blue (each in a range of 0.0 ..1.0)"; return $msg unless (@a == 3); foreach my $tc (@a) { return $msg unless ($tc =~ m/^\s*[0-9]+?[\.]{0,1}[0-9]*\s*$/g); return $msg if (($tc < 0) || ($tc > 1)); } if ($init_done && !(@{$ledDevice->{helper}->{hlCmdQueue}})) { $attr{$device}{"whitePoint"} = $attribVal; my $hue = $ledDevice->{READINGS}->{hue}->{VAL}; my $sat = $ledDevice->{READINGS}->{saturation}->{VAL}; my $val = $ledDevice->{READINGS}->{brightness}->{VAL}; WifiLight_setHSV($ledDevice, $hue, $sat, $val, 1); } } Log3 ($ledDevice, 4, "$ledDevice->{NAME} attrib $attribName $cmd $attribVal") if $attribVal; return undef; } # restore previous settings (as set statefile) sub WifiLight_Notify(@) { my ($ledDevice, $eventSrc) = @_; my $events = deviceEvents($eventSrc, 1); my ($hue, $sat, $val); # wait for global: INITIALIZED after start up if ($eventSrc->{NAME} eq 'global' && @{$events}[0] eq 'INITIALIZED') { ####################################################### # TODO remove in a few weeks. its here for convenience delete($ledDevice->{READINGS}->{HUE}); delete($ledDevice->{READINGS}->{SATURATION}); delete($ledDevice->{READINGS}->{BRIGHTNESS}); ####################################################### if ($ledDevice->{CONNECTION} eq 'LW12') { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:0; return WifiLight_RGBLW12_setHSV($ledDevice, $hue, $sat, $val); } elsif ($ledDevice->{CONNECTION} eq 'LW12HX') { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:0; return WifiLight_RGBLW12HX_setHSV($ledDevice, $hue, $sat, $val); } elsif ($ledDevice->{CONNECTION} eq 'LW12FC') { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:0; return WifiLight_RGBLW12FC_setHSV($ledDevice, $hue, $sat, $val); } elsif ($ledDevice->{CONNECTION} eq 'LD316') { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:0; return WifiLight_RGBWLD316_setHSV($ledDevice, $hue, $sat, $val); } elsif ($ledDevice->{CONNECTION} eq 'LD316A') { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:0; return WifiLight_RGBWLD316A_setHSV($ledDevice, $hue, $sat, $val); } elsif ($ledDevice->{CONNECTION} eq 'LD382') { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:0; return WifiLight_RGBWLD382_setHSV($ledDevice, $hue, $sat, $val); } elsif ($ledDevice->{CONNECTION} eq 'LD382A') { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:0; return WifiLight_RGBWLD382A_setHSV($ledDevice, $hue, $sat, $val); } elsif (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')) { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:60; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:100; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:100; return WifiLight_RGB_setHSV($ledDevice, $hue, $sat, $val); } elsif (($ledDevice->{LEDTYPE} eq 'RGBW1') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')) { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:50; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:100; return WifiLight_RGBW1_setHSV($ledDevice, $hue, $sat, $val); } elsif (($ledDevice->{LEDTYPE} eq 'RGBW2') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')) { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:0; return WifiLight_RGBW2_setHSV($ledDevice, $hue, $sat, $val); } elsif (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')) { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:100; return WifiLight_White_setHSV($ledDevice, $hue, $sat, $val); } elsif (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} eq 'SENGLED')) { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:100; return WifiLight_WhiteSENGLED_setHSV($ledDevice, $hue, $sat, $val); } elsif (($ledDevice->{LEDTYPE} =~ /^RGB.?$/) && ($ledDevice->{CONNECTION} =~ /^SUNRICHER.?$/)) { $hue = defined($ledDevice->{READINGS}->{hue}->{VAL})?$ledDevice->{READINGS}->{hue}->{VAL}:0; $sat = defined($ledDevice->{READINGS}->{saturation}->{VAL})?$ledDevice->{READINGS}->{saturation}->{VAL}:0; $val = defined($ledDevice->{READINGS}->{brightness}->{VAL})?$ledDevice->{READINGS}->{brightness}->{VAL}:100; return WifiLight_RGBSunricher_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'SUNRICHER')); return WifiLight_RGBSunricherA_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'SUNRICHERA')); return WifiLight_RGBWSunricher_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'SUNRICHER')); return WifiLight_RGBWSunricherA_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'SUNRICHERA')); } # else { } return } } ############################################################################### # # generic device types # RGB device # # ############################################################################### sub WifiLight_RGBDevice_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x02, 0x12, 0xAB, 0x00, 0xAA, 0xAA ); my $receiver; $on = WifiLight_Sunricher_Checksum($ledDevice, $on); WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); # TODO device specific on my ($h, $s, $v, $k) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100, 3200")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} $ledDevice->{LEDTYPE} $ledDevice->{CONNECTION} set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_RGBDevice_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} $ledDevice->{LEDTYPE} $ledDevice->{CONNECTION} set off $ramp"); return WifiLight_RGBDevice_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBDevice_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} $ledDevice->{LEDTYPE} $ledDevice->{CONNECTION} dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } ############################################################################### # # device specific controller functions DualWhite SUNRICHER # # # ############################################################################### sub WifiLight_DualWhiteSunricher_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x02, 0x12, 0xAB, 0x00, 0xAA, 0xAA ); my $receiver; $on = WifiLight_Sunricher_Checksum($ledDevice, $on); WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} DualWhite Sunricher set on ($v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_DualWhiteSunricher_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} DualWhite Sunricher set off $ramp"); return WifiLight_DualWhiteSunricher_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_DualWhiteSunricher_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} DualWhite Sunricher dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } sub WifiLight_DualWhiteSunricher_setHSV(@) { my ($ledDevice, $hue, $sat, $val, $ct) = @_; my $receiver; # = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 0; Log3 ($ledDevice, 4, "$ledDevice->{NAME} DualWhite Sunricher set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; my $wt = int($gammaVal * 0x40 / 100); my $msg; # Me$msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x36, 0x10, 0x00, 0xAA, 0xAA)); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x33, $wt, 0x00, 0xAA, 0xAA)); # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } ############################################################################### # # device specific controller functions RGBW SUNRICHER # device range 0x00 0xff # # ############################################################################### sub WifiLight_RGBSunricher_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x02, 0x12, 0xAB, 0x00, 0xAA, 0xAA ); my $receiver; $on = WifiLight_Sunricher_Checksum($ledDevice, $on); WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} $ledDevice->{LEDTYPE} $ledDevice->{CONNECTION} set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_RGBSunricher_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} $ledDevice->{LEDTYPE} $ledDevice->{CONNECTION} set off $ramp"); return WifiLight_RGBWLD382_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBSunricher_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} $ledDevice->{LEDTYPE} $ledDevice->{CONNECTION} dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } ############################################################################### # # SUNRICHER Color conversation functions # # # ############################################################################### sub WifiLight_RGBSunricher_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver; # = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 0; my $msg = ''; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW Sunricher set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # convert to device 4 channels (remaining r,g,b after substract white, white, rgb) my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); $rr = int( $rr * 0x80 / 0xFF ); $rg = int( $rg * 0x80 / 0xFF ); $rb = int( $rb * 0x80 / 0xFF ); $white = int( $white * 0x80 / 0xFF ); # TODO CT and white point correction $rr += $white; $rg += $white; $rb += $white; $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x18, $rr, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{rLevel} != $rr); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x19, $rg, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{gLevel} != $rg); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x20, $rb, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{bLevel} != $rb); $ledDevice->{helper}->{rLevel} = $rr; $ledDevice->{helper}->{rLevel} = $rg; $ledDevice->{helper}->{rLevel} = $rb; # leave here if nothing to tell return unless $msg; # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } sub WifiLight_RGBSunricherA_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver; # = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 0; my $msg = ''; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW Sunricher set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # convert to device 4 channels (remaining r,g,b after substract white, white, rgb) my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); # TODO CT and white point correction $rr += $white; $rg += $white; $rb += $white; $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x18, $rr, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{rLevel} != $rr); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x19, $rg, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{gLevel} != $rg); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x20, $rb, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{bLevel} != $rb); $ledDevice->{helper}->{rLevel} = $rr; $ledDevice->{helper}->{rLevel} = $rg; $ledDevice->{helper}->{rLevel} = $rb; # leave here if nothing to tell return unless $msg; # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } sub WifiLight_RGBWSunricher_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver; # = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 0; my $msg = ''; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW Sunricher set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # convert to device 4 channels (remaining r,g,b after substract white, white, rgb) my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); $rr = int( $rr * 0x80 / 0xFF ); $rg = int( $rg * 0x80 / 0xFF ); $rb = int( $rb * 0x80 / 0xFF ); $white = int( $white * 0x80 / 0xFF ); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x18, $rr, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{rLevel} != $rr); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x19, $rg, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{gLevel} != $rg); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x20, $rb, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{bLevel} != $rb); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x21, $white, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{wLevel} != $white); $ledDevice->{helper}->{rLevel} = $rr; $ledDevice->{helper}->{rLevel} = $rg; $ledDevice->{helper}->{rLevel} = $rb; $ledDevice->{helper}->{rLevel} = $white; # leave here if nothing to tell return unless $msg; # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } sub WifiLight_RGBWSunricherA_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver; # = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 0; my $msg = ''; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW Sunricher set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # convert to device 4 channels (remaining r,g,b after substract white, white, rgb) my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); $rr *= 0x80 / 0xFF; $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x18, $rr, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{rLevel} != $rr); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x19, $rg, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{gLevel} != $rg); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x20, $rb, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{bLevel} != $rb); $msg .= WifiLight_Sunricher_Checksum($ledDevice, pack('C*', 0x55, 0x00, 0x00, 0x00, 0x02, 0xFF, 0x08, 0x21, $white, 0x00, 0xAA, 0xAA)) if ($ledDevice->{helper}->{wLevel} != $white); $ledDevice->{helper}->{rLevel} = $rr; $ledDevice->{helper}->{rLevel} = $rg; $ledDevice->{helper}->{rLevel} = $rb; $ledDevice->{helper}->{rLevel} = $white; # leave here if nothing to tell return unless $msg; # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } ############################################################################### # # SUNRICHER helper functions # # ############################################################################### sub WifiLight_Sunricher_Checksum(@) { my ($ledDevice, $msg) = @_; my @byteStream = unpack('C*', $msg); my $l = @byteStream; my $c = 0; for (my $i=4; $i<($l-3); $i++) { $c += $byteStream[$i]; } $c %= 0x100; $byteStream[$l -3] = $c; $msg = pack('C*', @byteStream); return $msg; } ############################################################################### # # device specific controller functions RGBW LD316 # aka XScource # # ############################################################################### sub WifiLight_RGBWLD316_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = sprintf("%c%c%c", 0xCC, 0x23, 0x33); my $msg = sprintf("%c%c%c%c%c", 0x56, 0, 0, 0, 0xAA); my $receiver; WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD316 set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_RGBWLD316_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD316 set off $ramp"); return WifiLight_RGBWLD316_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBWLD316_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD316 dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } sub WifiLight_RGBWLD316_setHSV(@) { my ($ledDevice, $hue, $sat, $val, $k) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 50; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW LD316 set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction, may be doing it after wb more ok my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; ########################################## # sat is spread by 10% so there is room # for a smoth switch to white and adapt to # higher brightness of white led ########################################## $sat = ($sat * 1.1) -10; my $wl = ($sat<0)?$sat * -1:0; $sat = ($sat<0)?0:$sat; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # convert to device 4 channels (remaining r,g,b after substract white, white, rgb) my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); my $msg; ########################################## # experimental white temp adjustment # G - 50% # B - 04% # sat is spread by 10% so there is room # for a smoth switch to white and adapt to # higher brightness of whte led ########################################## my ($wr, $wg, $wb) = split(',', AttrVal($ledDevice->{NAME}, 'whitePoint', '1, 1, 1')); # rgb mode if (($val > 0) && ($wl == 0)) { #replace the removed part of white light and apply white balance $rr += int(($white * $wr) + 0.5); $rg += int(($white * $wg) + 0.5); $rb += int(($white * $wb) + 0.5); #new proto 0x56, r, g, b, white level, f0 (color) || 0f (white), 0xaa (terminator) $msg = sprintf("%c%c%c%c%c%c%c", 0x56, $rr, $rg, $rb, 0x00, 0xF0, 0xAA); } elsif ($wl > 0) { #smoth brightness adaption of white led my $wo = $gammaVal - ($gammaVal * (10-$wl) * 0.08); #0.07 $wo = int(0.5 + ($wo * 2.55)); $msg = sprintf("%c%c%c%c%c%c%c", 0x56, 0, 0, 0, $wo, 0x0F, 0xAA); } else { $msg = sprintf("%c%c%c%c%c%c%c", 0x56, 0, 0, 0, 0x00, 0xF0, 0xAA); } # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } ############################################################################### # # device specific controller functions RGBW LD316A - new fw. # thnx raspklaus # ############################################################################### sub WifiLight_RGBWLD316A_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = sprintf("%c%c%c%c", 0x71, 0x23, 0x0F, 0xA3); my $msg = sprintf("%c%c%c%c%c", 0x56, 0, 0, 0, 0xAA); my $receiver; WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD316A set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_RGBWLD316A_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD316A set off $ramp"); return WifiLight_RGBWLD316_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBWLD316A_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD316A dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } sub WifiLight_RGBWLD316A_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 50; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW LD316A set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction, may be doing it after wb more ok my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; ########################################## # sat is spread by 10% so there is room # for a smoth switch to white and adapt to # higher brightness of white led ########################################## $sat = ($sat * 1.1) -10; my $wl = ($sat<0)?$sat * -1:0; $sat = ($sat<0)?0:$sat; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # convert to device 4 channels (remaining r,g,b after substract white, white, rgb) my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); my $msg; ########################################## # experimental white temp adjustment # G - 50% # B - 04% # sat is spread by 10% so there is room # for a smoth switch to white and adapt to # higher brightness of whte led ########################################## my ($wr, $wg, $wb) = split(',', AttrVal($ledDevice->{NAME}, 'whitePoint', '1, 1, 1')); # rgb mode if (($val > 0) && ($wl == 0)) { #replace the removed part of white light and apply white balance $rr += int(($white * $wr) + 0.5); $rg += int(($white * $wg) + 0.5); $rb += int(($white * $wb) + 0.5); #new proto 0x56, r, g, b, white level, f0 (color) || 0f (white), 0xaa (terminator) $msg = sprintf("%c%c%c%c%c%c%c", 0x31, $rr, $rg, $rb, 0x00, 0xF0, 0x0F); } elsif ($wl > 0) { #smoth brightness adaption of white led my $wo = $gammaVal - ($gammaVal * (10-$wl) * 0.08); #0.07 $wo = int(0.5 + ($wo * 2.55)); $msg = sprintf("%c%c%c%c%c%c%c", 0x31, 0, 0, 0, $wo, 0x0F, 0x0F); } else { $msg = sprintf("%c%c%c%c%c%c%c", 0x31, 0, 0, 0, 0x00, 0xF0, 0x0F); } #add checksum $msg = WifiLight_RGBWLD382_Checksum($ledDevice, $msg); # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } ############################################################################### # # device specific controller functions LD382 aka Magic UFO # with RGBW stripe (RGB and white) # # ############################################################################### sub WifiLight_RGBWLD382_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = sprintf("%c%c%c", 0x71, 0x23, 0x94); my $msg = sprintf("%c%c%c%c%c", 0x56, 0, 0, 0, 0xAA); my $receiver; WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD382 set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_RGBWLD382_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD382 set off $ramp"); return WifiLight_RGBWLD382_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBWLD382_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD382 dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } sub WifiLight_RGBWLD382_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 50; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW LD382 set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # convert to device 4 channels (remaining r,g,b after substract white, white, rgb) my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); my $msg = sprintf("%c%c%c%c%c%c%c", 0x31, $rr, $rg, $rb, $white, 0x00, 0x00); #add checksum $msg = WifiLight_RGBWLD382_Checksum($ledDevice, $msg); # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } sub WifiLight_RGBWLD382_Checksum(@) { my ($ledDevice, $msg) = @_; my $c = 0; foreach my $w (split //, $msg) { $c += ord($w); } $c %= 0x100; $msg .= sprintf("%c", $c); return $msg; } ############################################################################### # # device specific controller functions LD382 aka Magic UFO # with RGB stripe (mixed white) # # ############################################################################### sub WifiLight_RGBLD382_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = sprintf("%c%c%c", 0x71, 0x23, 0x94); my $msg = sprintf("%c%c%c%c%c", 0x56, 0, 0, 0, 0xAA); my $receiver; WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LD382 set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_RGBLD382_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LD382 set off $ramp"); return WifiLight_RGBLD382_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBLD382_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LD382 dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } sub WifiLight_RGBLD382_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 50; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGB LD382 set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # convert to device 4 channels (remaining r,g,b after substract white, white, rgb) my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); my ($wr, $wg, $wb) = split(',', AttrVal($ledDevice->{NAME}, 'whitePoint', '1, 1, 1')); #replace the removed part of white light and apply white balance $rr += int(($white * $wr) + 0.5); $rg += int(($white * $wg) + 0.5); $rb += int(($white * $wb) + 0.5); my $msg = sprintf("%c%c%c%c%c%c%c", 0x31, $rr, $rg, $rb, 0x00, 0x00, 0x00); #add checksum $msg = WifiLight_RGBWLD382_Checksum($ledDevice, $msg); # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } ############################################################################### # # device specific controller functions LD382A aka Magic UFO # with RGBW stripe (RGB and white) # LD382A is a LD382 with fw 1.0.6 # ############################################################################### sub WifiLight_RGBWLD382A_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = sprintf("%c%c%c%c", 0x71, 0x23, 0x0F, 0xA3); # my $msg = sprintf("%c%c%c%c%c", 0x56, 0, 0, 0, 0xAA); my $receiver; WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD382A set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_RGBWLD382A_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD382A set off $ramp"); return WifiLight_RGBWLD382A_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBWLD382A_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW LD382A dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } sub WifiLight_RGBWLD382A_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 50; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW LD382A set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # convert to device 4 channels (remaining r,g,b after substract white, white, rgb) my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); my $msg = sprintf("%c%c%c%c%c%c%c", 0x31, $rr, $rg, $rb, $white, 0x00, 0x0F); #add checksum $msg = WifiLight_RGBWLD382_Checksum($ledDevice, $msg); # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } ############################################################################### # # device specific controller functions LD382A aka Magic UFO # with RGB stripe (mixed white) # LD382A is a LD382 with fw 1.0.6 # ############################################################################### sub WifiLight_RGBLD382A_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = sprintf("%c%c%c%c", 0x71, 0x23, 0x0F, 0xA3); # my $msg = sprintf("%c%c%c%c%c", 0x56, 0, 0, 0, 0xAA); my $receiver; WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LD382A set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_RGBLD382A_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LD382A set off $ramp"); return WifiLight_RGBLD382A_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBLD382A_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LD382A dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } sub WifiLight_RGBLD382A_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 50; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGB LD382A set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # convert to device 4 channels (remaining r,g,b after substract white, white, rgb) my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); my ($wr, $wg, $wb) = split(',', AttrVal($ledDevice->{NAME}, 'whitePoint', '1, 1, 1')); #replace the removed part of white light and apply white balance $rr += int(($white * $wr) + 0.5); $rg += int(($white * $wg) + 0.5); $rb += int(($white * $wb) + 0.5); my $msg = sprintf("%c%c%c%c%c%c%c", 0x31, $rr, $rg, $rb, 0x00, 0x00, 0x0F); #add checksum $msg = WifiLight_RGBWLD382_Checksum($ledDevice, $msg); # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } ############################################################################### # # device specific controller functions RGB LW12 # LED Stripe controller LW12 # # ############################################################################### sub WifiLight_RGBLW12_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = sprintf("%c%c%c", 0xCC, 0x23, 0x33); my $msg = sprintf("%c%c%c%c%c", 0x56, 0, 0, 0, 0xAA); my $receiver; WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); # WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LW12 set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } #TODO set physical off: my $off = sprintf("%c%c%c", 0xCC, 0x24, 0x33); sub WifiLight_RGBLW12_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LW12 set off $ramp"); return WifiLight_RGBLW12_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBLW12_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LW12 dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } sub WifiLight_RGBLW12_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 50; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGB LW12 set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; #new style converter with white point correction my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); my ($wr, $wg, $wb) = split(',', AttrVal($ledDevice->{NAME}, 'whitePoint', '1, 1, 1')); #replace the removed part of white light and apply white balance $rr += int(($white * $wr) + 0.5); $rg += int(($white * $wg) + 0.5); $rb += int(($white * $wb) + 0.5); my $msg = sprintf("%c%c%c%c%c", 0x56, $rr, $rg, $rb, 0xAA); # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } ############################################################################### # # device specific controller functions RGB LW12 HX001 Version # LED Stripe controller LW12 # # ############################################################################### sub WifiLight_RGBLW12HX_On(@) { my ($ledDevice, $ramp) = @_; # my $delay = 50; # my $on = sprintf("%c%c%c", 0xCC, 0x23, 0x33); # my $msg = sprintf("%c%c%c%c%c", 0x56, 0, 0, 0, 0xAA); # my $receiver; # WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); # WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LW12HX set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_RGBLW12HX_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LW12HX set off $ramp"); return WifiLight_RGBLW12HX_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBLW12HX_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBHX LW12 dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } sub WifiLight_RGBLW12HX_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 50; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGB LW12 set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; #new style converter with white point correction my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); my ($wr, $wg, $wb) = split(',', AttrVal($ledDevice->{NAME}, 'whitePoint', '1, 1, 1')); #replace the removed part of white light and apply white balance $rr += int(($white * $wr) + 0.5); $rg += int(($white * $wg) + 0.5); $rb += int(($white * $wb) + 0.5); my $on = ($gammaVal > 0)?1:0; my $dim = 100; # supported by ichichich my @sendData = (0x9D, 0x62, 0x00, 0x01, 0x01, $on, $dim, $rr, $rg, $rb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); my $chkSum = 0xFF; $chkSum += $_ for @sendData[3, 5..9]; unless ($chkSum == 0) { $chkSum %= 0xFF; $chkSum = 0xFF if ($chkSum == 0); } push (@sendData, $chkSum); for (my $i=2; $i<11; $i++) { my $h = ($sendData[$i] & 0xF0) + ($sendData[21-$i] >> 4); my $l = (($sendData[$i] & 0x0F) << 4) + ($sendData[21-$i] & 0x0F); $sendData[$i] = $h; $sendData[21-$i] = $l; } my $msg = pack('C*', @sendData); # $dbgStr = unpack("H*", $msg); # print "lw12HX $dbgStr \n"; # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } ############################################################################### # # device specific controller functions RGB LW12 FC Version # LED Stripe controller LW12 # # ############################################################################### sub WifiLight_RGBLW12FC_On(@) { my ($ledDevice, $ramp) = @_; my $delay = 50; my $on = sprintf("%c%c%c%c%c%c%c%c%c", 0x7E, 0x04, 0x04, 0x01, 0xFF, 0xFF, 0xFF, 0x00, 0xEF); my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LW12FC set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_RGBLW12FC_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LW12FC set off $ramp"); return WifiLight_RGBLW12FC_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBLW12FC_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LW12FC dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 100, undef); } sub WifiLight_RGBLW12FC_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 50; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGB LW12FC set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # color cast correction my $h = $ledDevice->{helper}->{COLORMAP}[$hue]; # new style converter with white point correction my ($rr, $rg, $rb, $white) = WifiLight_HSV2fourChannel($h, $sat, $gammaVal); my ($wr, $wg, $wb) = split(',', AttrVal($ledDevice->{NAME}, 'whitePoint', '1, 1, 1')); # replace the removed part of white light and apply white balance $rr += int(($white * $wr) + 0.5); $rg += int(($white * $wg) + 0.5); $rb += int(($white * $wb) + 0.5); my $on = ($gammaVal > 0)?1:0; my $dim = 100; my $msg = sprintf("%c%c%c%c%c%c%c%c%c", 0x7E, 0x07, 0x05, 0x03, $rr, $rg, $rb, 0x00, 0xEF); # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue return WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } ############################################################################### # # device specific controller functions White SENGLED # E27 LED Bulb with # # ############################################################################### sub WifiLight_WhiteSENGLED_On(@) { my ($ledDevice, $ramp) = @_; # my $delay = 50; # my $on = sprintf("%c%c%c%c%c%c%c%c%c", 0x7E, 0x04, 0x04, 0x01, 0xFF, 0xFF, 0xFF, 0x00, 0xEF); # my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); # WifiLight_LowLevelCmdQueue_Add($ledDevice, $on, $receiver, $delay); my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} White SENGLED set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 100, undef); } sub WifiLight_WhiteSENGLED_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} White SENGLED set off $ramp"); return WifiLight_WhiteSENGLED_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_WhiteSENGLED_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; # my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); # my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} White SENGLED dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, 0, 0, $level, $ramp, $flags, 100, undef); } sub WifiLight_WhiteSENGLED_setHSV(@) { my ($ledDevice, $hue, $sat, $val, $isLast) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 50; Log3 ($ledDevice, 4, "$ledDevice->{NAME} White SENGLED set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; my @remote = split(/\./, $ledDevice->{helper}->{SOCKET}->peerhost()); # intro my $msg = sprintf("%c%c%c%c%c", 0x0d, 0x00, 0x02, 0x00, 0x01); # sender, lazy 0x00 $msg .= sprintf("%c%c%c%c", 0x00, 0x00, 0x00, 0x00); # destinations $msg .= sprintf("%c%c%c%c", $remote[0], $remote[1], $remote[2], $remote[3] ); # sender, lazy 0x00 $msg .= sprintf("%c%c%c%c", 0x00, 0x00, 0x00, 0x00); # destinations $msg .= sprintf("%c%c%c%c", $remote[0], $remote[1], $remote[2], $remote[3] ); # intro 2 $msg .= sprintf("%c%c%c%c%c%c", 0x01, 0x00, 0x01, 0x00, 0x00, 0x00); # cmd level $msg .= sprintf("%c%c", $gammaVal, 0x64); # for safety of tranmission (udp): repeat cmd if its stand-alone or first or last in transition my $repeat = ($isLast)?3:1; for (my $i=0; $i<$repeat; $i++) { # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $msg, $receiver, $delay); # unlock ll queue after complete cmd is send WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } return undef; } ############################################################################### # # device specific controller functions RGB # LED Stripe or bulb, no white, controller V2 # ############################################################################### sub WifiLight_RGB_Pair(@) { my ($ledDevice, $numSeconds) = @_; $numSeconds = 3 if (($numSeconds || 0) == 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LED slot $ledDevice->{SLOT} pair $numSeconds"); # find my slot and get my group-all-on cmd my $ctrl = "\x25\x00\x55"; for (my $i = 0; $i < $numSeconds; $i++) { WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 500, undef, undef); WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 500, undef, undef); } return undef; } sub WifiLight_RGB_UnPair(@) { my ($ledDevice) = @_; my $numSeconds = 8; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB LED slot $ledDevice->{SLOT} unpair $numSeconds"); # find my slot and get my group-all-on cmd my $ctrl = "\x25\x00\x55"; for (my $i = 0; $i < $numSeconds; $i++) { WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 500, undef, undef); } return undef; } sub WifiLight_RGB_Sync(@) { my ($ledDevice) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 100; $ledDevice->{helper}->{whiteLevel} =9; $ledDevice->{helper}->{colorLevel} =9; $ledDevice->{helper}->{colorValue} =127; $ledDevice->{helper}->{mode} =2; # mode 0: off, 1: mixed "white", 2: color WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x22\x00\x55", $receiver, 500); # on for (my $i = 0; $i < 22; $i++) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x28\x00\x55", $receiver, $delay); # mode up (to "pure white" ;-) } for (my $i = 0; $i < 10; $i++) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up (to "pure white" ;-) } WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x20\x7F\x55", $receiver, $delay); # color yellow (auto jump to mode 2) for (my $i = 0; $i < 10; $i++) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up (yellow max brightness) } WifiLight_setHSV_Readings($ledDevice, 60, 100, 100) if $init_done; return undef; } sub WifiLight_RGB_On(@) { my ($ledDevice, $ramp) = @_; my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "40,100,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB slot $ledDevice->{SLOT} set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 500, undef); } sub WifiLight_RGB_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB slot $ledDevice->{SLOT} set off $ramp"); return WifiLight_RGB_Dim($ledDevice, 0, $ramp, ''); #TODO remove if tested #return WifiLight_HSV_Transition($ledDevice, 0, 100, 0, $ramp, undef, 500, undef); } sub WifiLight_RGB_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGB slot $ledDevice->{SLOT} dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 500, undef); } sub WifiLight_RGB_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGB slot $ledDevice->{SLOT} set h:$hue, s:$sat, v:$val"); $sat = 100; WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # convert to device specs my ($cv, $cl, $wl) = WifiLight_RGBW1_ColorConverter($ledDevice, $hue, $sat, $val); Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGB slot $ledDevice->{SLOT} set levels: $cv, $cl, $wl"); return WifiLight_RGB_setLevels($ledDevice, $cv, $cl, $wl); } sub WifiLight_RGB_setLevels(@) { my ($ledDevice, $cv, $cl, $wl) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 100; my $lock = 0; # mode 0: off, 1: mixed "white", 2: color # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable if ((($ledDevice->{helper}->{colorValue} != $cv) && ($cl > 0)) || ($ledDevice->{helper}->{colorLevel} != $cl) || ($ledDevice->{helper}->{whiteLevel} != $wl)) { $ledDevice->{helper}->{llLock} += 1; $lock = 1; } # need to touch color value (only if visible) or color level ? if ((($ledDevice->{helper}->{colorValue} != $cv) && ($cl > 0)) || $ledDevice->{helper}->{colorLevel} != $cl) { # if color all off switch on if ($ledDevice->{helper}->{mode} == 0) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x22\x00\x55", $receiver, $delay); # switch on WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x20".chr($cv)."\x55", $receiver, $delay); # set color $ledDevice->{helper}->{colorValue} = $cv; $ledDevice->{helper}->{colorLevel} = 1; $ledDevice->{helper}->{mode} = 2; } elsif ($ledDevice->{helper}->{mode} == 1) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x20".chr($cv)."\x55", $receiver, $delay); # set color $ledDevice->{helper}->{colorValue} = $cv; $ledDevice->{helper}->{mode} = 2; } else { $ledDevice->{helper}->{colorValue} = $cv; WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x20".chr($cv)."\x55", $receiver, $delay); # set color } # cl decrease if ($ledDevice->{helper}->{colorLevel} > $cl) { for (my $i=$ledDevice->{helper}->{colorLevel}; $i > $cl; $i--) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x24\x00\x55", $receiver, $delay); # brightness down $ledDevice->{helper}->{colorLevel} = $i - 1; } if ($cl == 0) { # need to switch off color # if no white is required and no white is active we can must entirely switch off WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x21\x00\x55", $receiver, $delay); # switch off $ledDevice->{helper}->{colorLevel} = 0; $ledDevice->{helper}->{mode} = 0; } } # cl inrease if ($ledDevice->{helper}->{colorLevel} < $cl) { for (my $i=$ledDevice->{helper}->{colorLevel}; $i < $cl; $i++) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # brightness up $ledDevice->{helper}->{colorLevel} = $i + 1; } } } # unlock ll queue WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1) if $lock; return undef; } ############################################################################### # # device specific controller functions RGBW1 # LED Stripe with extra white led, controller V2, bridge V2|bridge V3 # # ############################################################################### sub WifiLight_RGBW1_Pair(@) { my ($ledDevice, $numSeconds) = @_; $numSeconds = 3 if (($numSeconds || 0) == 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW1 LED slot $ledDevice->{SLOT} pair $numSeconds"); # find my slot and get my group-all-on cmd my $ctrl = "\x25\x00\x55"; for (my $i = 0; $i < $numSeconds; $i++) { WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 500, undef, undef); WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 500, undef, undef); } return undef; } sub WifiLight_RGBW1_UnPair(@) { my ($ledDevice) = @_; my $numSeconds = 8; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW1 LED slot $ledDevice->{SLOT} unpair $numSeconds"); # find my slot and get my group-all-on cmd my $ctrl = "\x25\x00\x55"; for (my $i = 0; $i < $numSeconds; $i++) { WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 500, undef, undef); } return undef; } sub WifiLight_RGBW1_Sync(@) { my ($ledDevice) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 250; $ledDevice->{helper}->{whiteLevel} =9; $ledDevice->{helper}->{colorLevel} =9; $ledDevice->{helper}->{colorValue} =170; $ledDevice->{helper}->{mode} =3; # mode 0: c:off, w:off; 1: c:on, w:off; 2: c:off, w:on; 3: c:on, w:on WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x22\x00\x55", $receiver, 500); # on WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x22\x00\x55", $receiver, $delay); # on WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x20\xAA\x55", $receiver, $delay); # color red (auto jump to mode 1 except we are mode 3) WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x28\x00\x55", $receiver, $delay); # mode down WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x28\x00\x55", $receiver, $delay); # mode down (now we are for sure in mode 1) WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #1 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #2 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #3 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #4 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #5 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #6 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #7 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #8 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #9 (highest dim-level color red) WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x27\x00\x55", $receiver, $delay); # mode up (pure white) WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #1 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #2 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #3 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #4 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #5 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #6 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #7 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #8 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # dim up #9 (highest dim-level white) WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x27\x00\x55", $receiver, $delay); # mode up (white and red at highest level: bright warm light) WifiLight_setHSV_Readings($ledDevice, 0, 50, 100) if $init_done; return undef; } sub WifiLight_RGBW1_On(@) { my ($ledDevice, $ramp) = @_; my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW1 slot $ledDevice->{SLOT} set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 1000, undef); } sub WifiLight_RGBW1_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW1 slot $ledDevice->{SLOT} set off $ramp"); return WifiLight_RGBW1_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_RGBW1_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW1 slot $ledDevice->{SLOT} dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 1000, undef); } sub WifiLight_RGBW1_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW1 slot $ledDevice->{SLOT} set h:$hue, s:$sat, v:$val"); WifiLight_setHSV_Readings($ledDevice, $hue, $sat, $val); # convert to device specs my ($cv, $cl, $wl) = WifiLight_RGBW1_ColorConverter($ledDevice, $hue, $sat, $val); Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW1 slot $ledDevice->{SLOT} set levels: $cv, $cl, $wl"); return WifiLight_RGBW1_setLevels($ledDevice, $cv, $cl, $wl); } sub WifiLight_RGBW1_setLevels(@) { my ($ledDevice, $cv, $cl, $wl) = @_; my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 250; my $lock = 0; # need to touch color value or color level? # yes # is color visible ? (we are in mode 1 or 3) # yes: adjust color!, requ level = 1 if cl = 0; new level 0 ? yes: mode 0 if wl == 0 else Mode = 1 (if coming from 0 or 1 then wl =1) # no: # will we need color ? # yes: go into mode #1, (cl jumps to 1) # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable if ((($ledDevice->{helper}->{colorValue} != $cv) && ($cl > 0)) || ($ledDevice->{helper}->{colorLevel} != $cl) || ($ledDevice->{helper}->{whiteLevel} != $wl)) { $ledDevice->{helper}->{llLock} += 1; $lock = 1; } # need to touch color value (only if visible) or color level ? if ((($ledDevice->{helper}->{colorValue} != $cv) && ($cl > 0)) || $ledDevice->{helper}->{colorLevel} != $cl) { # if color all off switch on if ($ledDevice->{helper}->{mode} == 0) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x22\x00\x55", $receiver, $delay); # switch on WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x28\x00\x55", $receiver, $delay); # mode down: 3 > 2 || 2 > 1 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x28\x00\x55", $receiver, $delay); # mode down: 2 > 1 || 1 > 1 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x20".chr($cv)."\x55", $receiver, $delay); # set color $ledDevice->{helper}->{colorValue} = $cv; $ledDevice->{helper}->{colorLevel} = 1; $ledDevice->{helper}->{mode} = 1; } elsif ($ledDevice->{helper}->{mode} == 2) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x27\x00\x55", $receiver, $delay); # mode up: 2 > 3 WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x20".chr($cv)."\x55", $receiver, $delay); # set color $ledDevice->{helper}->{colorValue} = $cv; $ledDevice->{helper}->{colorLevel} = 1; $ledDevice->{helper}->{mode} = 3; } else { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x20".chr($cv)."\x55", $receiver, $delay); # set color $ledDevice->{helper}->{colorValue} = $cv; } # color level decrease if ($ledDevice->{helper}->{colorLevel} > $cl) { for (my $i=$ledDevice->{helper}->{colorLevel}; $i > $cl; $i--) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x24\x00\x55", $receiver, $delay); # brightness down $ledDevice->{helper}->{colorLevel} = $i - 1; } if ($cl == 0) { # need to switch off color # if no white is required and no white is active switch off if (($wl == 0) && ($ledDevice->{helper}->{mode} == 1)) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x21\x00\x55", $receiver, $delay); # switch off $ledDevice->{helper}->{colorLevel} = 0; $ledDevice->{helper}->{mode} = 0; } # if white is required, goto mode 2: pure white if (($wl > 0) || ($ledDevice->{helper}->{mode} == 2) || ($ledDevice->{helper}->{mode} == 3)) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x27\x00\x55", $receiver, $delay) if ($ledDevice->{helper}->{mode} == 1) ; # mode up WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x28\x00\x55", $receiver, $delay) if ($ledDevice->{helper}->{mode} == 3) ; # mode down $ledDevice->{helper}->{colorLevel} = 0; $ledDevice->{helper}->{whiteLevel} = 1 if ($ledDevice->{helper}->{mode} == 1); $ledDevice->{helper}->{mode} = 2; } } } if ($ledDevice->{helper}->{colorLevel} < $cl) { for (my $i=$ledDevice->{helper}->{colorLevel}; $i < $cl; $i++) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # brightness up $ledDevice->{helper}->{colorLevel} = $i + 1; } } } # need to adjust white level ? if ($ledDevice->{helper}->{whiteLevel} != $wl) { # white off but need adjustment ? set it on.. # color processing is finished, so if we are in mode 0, no color required. go to mode 2: pure white if ($ledDevice->{helper}->{mode} == 0) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x22\x00\x55", $receiver, $delay); # switch on WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x28\x00\x55", $receiver, $delay); # mode down (3 -> 2 || 2 -> 1) WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x28\x00\x55", $receiver, $delay); # mode down (2 -> 1 || 1 -> 1) WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x27\x00\x55", $receiver, $delay); # mode up (1 -> 2) $ledDevice->{helper}->{whiteLevel} = 1; $ledDevice->{helper}->{mode} = 2; } # color processing is finished, so if we are at mode 1 color is required. go to mode 2 if ($ledDevice->{helper}->{mode} == 1) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x27\x00\x55", $receiver, $delay); # mode up (1 -> 2) $ledDevice->{helper}->{whiteLevel} = 1; $ledDevice->{helper}->{mode} = 2; } # temporary go to mode 2 while maintain white level if ($ledDevice->{helper}->{mode} == 3) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x28\x00\x55", $receiver, $delay); # mode down (3 -> 2) $ledDevice->{helper}->{mode} = 2; } # white level inrease for (my $i=$ledDevice->{helper}->{whiteLevel}; $i < $wl; $i++) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x23\x00\x55", $receiver, $delay); # brightness up $ledDevice->{helper}->{whiteLevel} = $i + 1; } # white level decrease if ($ledDevice->{helper}->{whiteLevel} > $wl) { for (my $i=$ledDevice->{helper}->{whiteLevel}; $i > $wl; $i--) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x24\x00\x55", $receiver, $delay); # brightness down $ledDevice->{helper}->{whiteLevel} = $i - 1; } } # assume we are at mode 2, finishing to correct mode if (($wl == 0) && ($cl == 0)) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x21\x00\x55", $receiver, $delay); # switch off $ledDevice->{helper}->{whiteLevel} = 0; $ledDevice->{helper}->{mode} = 0; } if (($wl == 0) && ($cl > 0)) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x28\x00\x55", $receiver, $delay); # mode down (2 -> 1) $ledDevice->{helper}->{whiteLevel} = 0; $ledDevice->{helper}->{mode} = 1; } if (($wl > 0) && ($cl > 0)) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x27\x00\x55", $receiver, $delay); # mode up (2 -> 3) $ledDevice->{helper}->{mode} = 3; } } # unlock ll queue WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1) if $lock; return undef; } sub WifiLight_RGBW1_ColorConverter(@) { my ($ledDevice, $h, $s, $v) = @_; my $color = $ledDevice->{helper}->{COLORMAP}[$h % 360]; # there are 0..9 dim level, setup correction my $valueSpread = 100/9; my $totalVal = int(($v / $valueSpread) +0.5); # saturation 100..50: color full, white increase. 50..0 white full, color decrease my $colorVal = ($s >= 50) ? $totalVal : int(($s / 50 * $totalVal) +0.5); my $whiteVal = ($s >= 50) ? int(((100-$s) / 50 * $totalVal) +0.5) : $totalVal; return ($color, $colorVal, $whiteVal); } ############################################################################### # # device specific functions RGBW2 bulb # RGB white, only bridge V3 # # ############################################################################### sub WifiLight_RGBW2_Pair(@) { my ($ledDevice, $numSeconds) = @_; $numSeconds = 3 if (($numSeconds || 0) == 0); my @bulbCmdsOn = ("\x45", "\x47", "\x49", "\x4B"); Log3 ($ledDevice, 3, "$ledDevice->{NAME}, $ledDevice->{LEDTYPE} at $ledDevice->{CONNECTION}, slot $ledDevice->{SLOT}: pair $numSeconds"); # find my slot and get my group-all-on cmd my $ctrl = @bulbCmdsOn[$ledDevice->{SLOT} -5]."\x00\x55"; for (my $i = 0; $i < $numSeconds; $i++) { WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 500, undef, undef); WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 500, undef, undef); } return undef; } sub WifiLight_RGBW2_UnPair(@) { my ($ledDevice, $numSeconds, $releaseFromSlot) = @_; $numSeconds = 5; my @bulbCmdsOn = ("\x45", "\x47", "\x49", "\x4B"); Log3 ($ledDevice, 3, "$ledDevice->{NAME}, $ledDevice->{LEDTYPE} at $ledDevice->{CONNECTION}, slot $ledDevice->{SLOT}: unpair $numSeconds"); # find my slot and get my group-all-on cmd my $ctrl = @bulbCmdsOn[$ledDevice->{SLOT} -5]."\x00\x55"; for (my $i = 0; $i < $numSeconds; $i++) { WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 250, undef, undef); WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 250, undef, undef); WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 250, undef, undef); WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 250, undef, undef); } return undef; } sub WifiLight_RGBW2_Sync(@) { my ($ledDevice) = @_; # force new settings $ledDevice->{helper}->{mode} = -1; $ledDevice->{helper}->{colorValue} = -1; $ledDevice->{helper}->{colorLevel} = -1; $ledDevice->{helper}->{whiteLevel} = -1; return undef; } sub WifiLight_RGBW2_On(@) { my ($ledDevice, $ramp) = @_; my ($h, $s, $v) = split(',', AttrVal($ledDevice->{NAME}, "defaultColor", "0,0,100")); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW2 slot $ledDevice->{SLOT} set on ($h, $s, $v) $ramp"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $v, $ramp, '', 200, undef); } sub WifiLight_RGBW2_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW2 slot $ledDevice->{SLOT} set off $ramp"); return WifiLight_RGBW2_Dim($ledDevice, 0, $ramp, ''); #TODO remove if tested #return WifiLight_HSV_Transition($ledDevice, 0, 0, 0, $ramp, undef, 500, undef); } sub WifiLight_RGBW2_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} RGBW2 slot $ledDevice->{SLOT} dim $level $ramp ". $flags || ''); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 200, undef); } sub WifiLight_RGBW2_setHSV(@) { my ($ledDevice, $hue, $sat, $val, $isLast) = @_; my ($cl, $wl); my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 100; my $cv = $ledDevice->{helper}->{COLORMAP}[$hue % 360]; # apply gamma correction my $gammaVal = $ledDevice->{helper}->{GAMMAMAP}[$val]; # mode 0 = off, 1 = color, 2 = white # brightness 2..27 (x02..x1b) | 25 my $cf = 100 / 26; my $cb = int(($gammaVal / $cf) + 0.5); $cb += ($cb > 0)?1:0; if ($sat < 20) { $wl = $cb; $cl = 0; WifiLight_setHSV_Readings($ledDevice, $hue, 0, $val); } else { $cl = $cb; $wl = 0; WifiLight_setHSV_Readings($ledDevice, $hue, 100, $val); } return WifiLight_RGBW2_setLevelsFast($ledDevice, $receiver, $cv, $cl, $wl) unless ($isLast); return WifiLight_RGBW2_setLevelsSafe($ledDevice, $receiver, $cv, $cl, $wl); } # repeatly send out a full size cmd # the last cmd in a transition or if it is stand alone sub WifiLight_RGBW2_setLevelsSafe(@) { my ($ledDevice, $receiver, $cv, $cl, $wl) = @_; my $delay = 100; my @bulbCmdsOn = ("\x45", "\x47", "\x49", "\x4B"); my @bulbCmdsOff = ("\x46", "\x48", "\x4A", "\x4C"); my @bulbCmdsWT = ("\xC5", "\xC7", "\xC9", "\xCB"); Log3 ($ledDevice, 4, "$ledDevice->{NAME} RGBW2 slot $ledDevice->{SLOT} set safe levels"); Log3 ($ledDevice, 5, "$ledDevice->{NAME} RGBW2 slot $ledDevice->{SLOT} lock queue ".$ledDevice->{helper}->{llLock}); my @cmd = (); # about switching off. dim to prevent a flash if switched on again if (($wl == 0) && ($cl == 0) && ($ledDevice->{helper}->{mode} != 0)) { $ledDevice->{helper}->{llLock} += 1; # lock ... WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsOn[$ledDevice->{SLOT} -5]."\x00\x55", $receiver, $delay); # group on WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x4E\x02\x55", $receiver, $delay); # brightness WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); # ... unlock } if (($wl == 0) && ($cl == 0)) { push (@cmd, @bulbCmdsOff[$ledDevice->{SLOT} -5]."\x00\x55"); $ledDevice->{helper}->{whiteLevel} = 0; $ledDevice->{helper}->{colorLevel} = 0; $ledDevice->{helper}->{mode} = 0; # group off } elsif ($wl > 0) { push (@cmd, @bulbCmdsOn[$ledDevice->{SLOT} -5]."\x00\x55"); push (@cmd, @bulbCmdsWT[$ledDevice->{SLOT} -5]."\x00\x55"); push (@cmd, "\x4E".chr($wl)."\x55"); $ledDevice->{helper}->{whiteLevel} = $wl; $ledDevice->{helper}->{colorLevel} = 0; $ledDevice->{helper}->{mode} = 2; # white } elsif ($cl > 0) { push (@cmd, @bulbCmdsOn[$ledDevice->{SLOT} -5]."\x00\x55"); push (@cmd, "\x40".chr($cv)."\x55"); # color push (@cmd, "\x4E".chr($cl)."\x55"); # brightness $ledDevice->{helper}->{whiteLevel} = 0; $ledDevice->{helper}->{colorLevel} = $cl; $ledDevice->{helper}->{colorValue} = $cv; $ledDevice->{helper}->{mode} = 1; # color } # repeat it three times for (my $i=0; $i<3; $i++) { # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; WifiLight_LowLevelCmdQueue_Add($ledDevice, $_, $receiver, $delay) foreach (@cmd); # unlock ll queue after complete cmd is send WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); } return undef; } # classic optimized version, used by fast color transitions sub WifiLight_RGBW2_setLevelsFast(@) { my ($ledDevice, $receiver, $cv, $cl, $wl) = @_; my $delay = 100; my @bulbCmdsOn = ("\x45", "\x47", "\x49", "\x4B"); my @bulbCmdsOff = ("\x46", "\x48", "\x4A", "\x4C"); my @bulbCmdsWT = ("\xC5", "\xC7", "\xC9", "\xCB"); return if (($ledDevice->{helper}->{colorValue} == $cv) && ($ledDevice->{helper}->{colorLevel} == $cl) && ($ledDevice->{helper}->{whiteLevel} == $wl)); # lock ll queue to prevent a bottleneck within llqueue # in cases where the high level queue fills the low level queue (which should not be interrupted) faster then it is processed (send out) # this lock will cause the hlexec intentionally drop frames which can safely be done because there are further frames for processing avialable $ledDevice->{helper}->{llLock} += 1; Log3 ($ledDevice, 5, "$ledDevice->{NAME} RGBW2 slot $ledDevice->{SLOT} lock queue ".$ledDevice->{helper}->{llLock}); if (($wl == 0) && ($cl == 0) && ($ledDevice->{helper}->{mode} != 0)) { WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsOff[$ledDevice->{SLOT} -5]."\x00\x55", $receiver, $delay); $ledDevice->{helper}->{whiteLevel} = 0; $ledDevice->{helper}->{colorLevel} = 0; $ledDevice->{helper}->{mode} = 0; # group off } else { WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsOn[$ledDevice->{SLOT} -5]."\x00\x55", $receiver, $delay); # group on # WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsOn[$ledDevice->{SLOT} -5]."\x00\x55", $receiver, $delay) if (($wl > 0) || ($cl > 0)); # group on if (($wl > 0) && ($ledDevice->{helper}->{mode} == 2)) # already white { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x4E".chr($wl)."\x55", $receiver, $delay) if ($ledDevice->{helper}->{whiteLevel} != $wl); # brightness } elsif (($wl > 0) && ($ledDevice->{helper}->{mode} != 2)) # not white { WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsWT[$ledDevice->{SLOT} -5]."\x00\x55", $receiver, $delay); # white WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x4E".chr($wl)."\x55", $receiver, $delay); # brightness $ledDevice->{helper}->{mode} = 2; # white } elsif (($cl > 0) && ($ledDevice->{helper}->{mode} == 1)) # already color { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x4E".chr($cl)."\x55", $receiver, $delay) if ($ledDevice->{helper}->{colorLevel} != $cl); # brightness WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x40".chr($cv)."\x55", $receiver, $delay) if ($ledDevice->{helper}->{colorValue} != $cv); # color } elsif (($cl > 0) && ($ledDevice->{helper}->{mode} != 1)) # not color { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x40".chr($cv)."\x55", $receiver, $delay); # color WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x4E".chr($cl)."\x55", $receiver, $delay); # brightness $ledDevice->{helper}->{mode} = 1; # color } $ledDevice->{helper}->{colorValue} = $cv; $ledDevice->{helper}->{colorLevel} = $cl; $ledDevice->{helper}->{whiteLevel} = $wl; } # unlock ll queue after complete cmd is send WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x00", $receiver, 0, 1); return undef; } ############################################################################### # # device specific functions white bulb # warm white / cold white with dim, bridge V2|bridge V3 # # ############################################################################### sub WifiLight_White_Pair(@) { my ($ledDevice, $numSeconds) = @_; $numSeconds = 1 if !(defined($numSeconds)); my @bulbCmdsOn = ("\x38", "\x3D", "\x37", "\x32"); Log3 ($ledDevice, 3, "$ledDevice->{NAME}, $ledDevice->{LEDTYPE} at $ledDevice->{CONNECTION}, slot $ledDevice->{SLOT}: pair $numSeconds"); # find my slot and get my group-all-on cmd my $ctrl = @bulbCmdsOn[$ledDevice->{SLOT} -1]."\x00\x55"; for (my $i = 0; $i < $numSeconds; $i++) { WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 500, undef, undef); WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 500, undef, undef); } return undef; } sub WifiLight_White_UnPair(@) { my ($ledDevice, $numSeconds, $releaseFromSlot) = @_; $numSeconds = 5; my @bulbCmdsOn = ("\x38", "\x3D", "\x37", "\x32"); Log3 ($ledDevice, 3, "$ledDevice->{NAME}, $ledDevice->{LEDTYPE} at $ledDevice->{CONNECTION}, slot $ledDevice->{SLOT}: unpair $numSeconds"); # find my slot and get my group-all-on cmd my $ctrl = @bulbCmdsOn[$ledDevice->{SLOT} -1]."\x00\x55"; for (my $i = 0; $i < $numSeconds; $i++) { WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 250, undef, undef); WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 250, undef, undef); WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 250, undef, undef); WifiLight_HighLevelCmdQueue_Add($ledDevice, undef, undef, undef, $ctrl, 250, undef, undef); } return undef; } sub WifiLight_White_Sync(@) { my ($ledDevice) = @_; my @bulbCmdsOn = ("\x38", "\x3D", "\x37", "\x32"); my @bulbCmdsFB = ("\xB8", "\xBD", "\xB7", "\xB2"); my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 100; $ledDevice->{helper}->{whiteLevel} =11; Log3 ($ledDevice, 3, "$ledDevice->{NAME}, $ledDevice->{LEDTYPE} at $ledDevice->{CONNECTION}, slot $ledDevice->{SLOT}: sync"); WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsOn[$ledDevice->{SLOT} -1]."\x00\x55", $receiver, $delay); # group on WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsFB[$ledDevice->{SLOT} -1]."\x00\x55", $receiver, $delay); # full brightness WifiLight_setHSV_Readings($ledDevice, 0, 0, 100) if $init_done; return undef; } sub WifiLight_White_On(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} white slot $ledDevice->{SLOT} set on $ramp"); return WifiLight_HSV_Transition($ledDevice, 0, 0, 100, $ramp, '', 500, undef); } sub WifiLight_White_Off(@) { my ($ledDevice, $ramp) = @_; Log3 ($ledDevice, 3, "$ledDevice->{NAME} white slot $ledDevice->{SLOT} set off $ramp"); return WifiLight_RGBW2_Dim($ledDevice, 0, $ramp, ''); } sub WifiLight_White_Dim(@) { my ($ledDevice, $level, $ramp, $flags) = @_; my $h = ReadingsVal($ledDevice->{NAME}, "hue", 0); my $s = ReadingsVal($ledDevice->{NAME}, "saturation", 0); Log3 ($ledDevice, 3, "$ledDevice->{NAME} white slot $ledDevice->{SLOT} dim $level $ramp $flags"); return WifiLight_HSV_Transition($ledDevice, $h, $s, $level, $ramp, $flags, 300, undef); } # only val supported, # TODO hue will become colortemp sub WifiLight_White_setHSV(@) { my ($ledDevice, $hue, $sat, $val) = @_; my $wlStep = (100 / 11); WifiLight_setHSV_Readings($ledDevice, 0, 0, $val); $val = int(($val / $wlStep) +0.5); WifiLight_White_setLevels($ledDevice, undef, $val); return undef; } sub WifiLight_White_setLevels(@) { my ($ledDevice, $cv, $wl) = @_; my @bulbCmdsOn = ("\x38", "\x3D", "\x37", "\x32"); my @bulbCmdsOff = ("\x3B", "\x33", "\x3A", "\x36"); my @bulbCmdsFull = ("\xB8", "\xBD", "\xB7", "\xB2"); my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 80; # alert that dump receiver, give it a extra wake up call if ($ledDevice->{helper}->{whiteLevel} != $wl) { WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsOn[$ledDevice->{SLOT} -1]."\x00\x55", $receiver, $delay); WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsOn[$ledDevice->{SLOT} -1]."\x00\x55", $receiver, $delay); } if ($ledDevice->{helper}->{whiteLevel} > $wl) { WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsOn[$ledDevice->{SLOT} -1]."\x00\x55", $receiver, $delay); # group on for (my $i=$ledDevice->{helper}->{whiteLevel}; $i > $wl; $i--) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x34\x00\x55", $receiver, $delay); # brightness down $ledDevice->{helper}->{whiteLevel} = $i - 1; } if ($wl == 0) { # special precaution, giving extra downsteps to do a sync each time you switch off WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x34\x00\x55", $receiver, $delay); # brightness down WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x34\x00\x55", $receiver, $delay); # brightness down WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x34\x00\x55", $receiver, $delay); # brightness down WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsOff[$ledDevice->{SLOT} -1]."\x00\x55", $receiver, $delay); # group off #WifiLight_LowLevelCmdqueue_Add($ledDevice, @bulbCmdsOff[$ledDevice->{SLOT}-1]."\x00\x55", $receiver, $delay); } } if ($ledDevice->{helper}->{whiteLevel} < $wl) { $ledDevice->{helper}->{whiteLevel} = 1 if ($ledDevice->{helper}->{whiteLevel} == 0); WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsOn[$ledDevice->{SLOT} -1]."\x00\x55", $receiver, $delay); # group on for (my $i=$ledDevice->{helper}->{whiteLevel}; $i < $wl; $i++) { WifiLight_LowLevelCmdQueue_Add($ledDevice, "\x3C\x00\x55", $receiver, $delay); # brightness up $ledDevice->{helper}->{whiteLevel} = $i + 1; } WifiLight_LowLevelCmdQueue_Add($ledDevice, @bulbCmdsFull[$ledDevice->{SLOT} -1]."\x00\x55", $receiver, $delay) if ($ledDevice->{helper}->{whiteLevel} == 11); } return undef; } ############################################################################### # # device indepenent routines # ############################################################################### # dispatcher sub WifiLight_setHSV(@) { my ($ledDevice, $hue, $sat, $val, $isLast) = @_; return WifiLight_RGBWLD316_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316')); return WifiLight_RGBWLD316A_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD316A')); return WifiLight_RGBWLD382_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBLD382_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382')); return WifiLight_RGBWLD382A_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLD382A_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'LD382A')); return WifiLight_RGBLW12_setHSV($ledDevice, $hue, $sat, $val) if ($ledDevice->{CONNECTION} eq 'LW12'); return WifiLight_RGBLW12HX_setHSV($ledDevice, $hue, $sat, $val) if ($ledDevice->{CONNECTION} eq 'LW12HX'); return WifiLight_RGBLW12FC_setHSV($ledDevice, $hue, $sat, $val) if ($ledDevice->{CONNECTION} eq 'LW12FC'); return WifiLight_WhiteSENGLED_setHSV($ledDevice, $hue, $sat, $val, $isLast) if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} eq 'SENGLED')); return WifiLight_RGB_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW1_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq "RGBW1") && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_RGBW2_setHSV($ledDevice, $hue, $sat, $val, $isLast) if ($ledDevice->{LEDTYPE} eq "RGBW2"); return WifiLight_White_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'White') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')); return WifiLight_DualWhiteSunricher_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'DualWhite') && ($ledDevice->{CONNECTION} eq 'SUNRICHER')); return WifiLight_RGBSunricher_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'SUNRICHER')); return WifiLight_RGBSunricherA_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} eq 'SUNRICHERA')); return WifiLight_RGBWSunricher_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'SUNRICHER')); return WifiLight_RGBWSunricherA_setHSV($ledDevice, $hue, $sat, $val) if (($ledDevice->{LEDTYPE} eq 'RGBW') && ($ledDevice->{CONNECTION} eq 'SUNRICHERA')); return undef; } # dispatcher sub WifiLight_processEvent(@) { my ($ledDevice, $event, $progress) = @_; Log3 ($ledDevice, 4, "$ledDevice->{NAME} processEvent: $event, progress: $progress") if defined($event); DoTrigger($ledDevice->{NAME}, "programm: $event $progress",0) if defined($event); return undef; } sub WifiLight_HSV_Transition(@) { my ($ledDevice, $hue, $sat, $val, $ramp, $flags, $delay, $event) = @_; my ($hueFrom, $satFrom, $valFrom, $timeFrom); # minimum stepwide my $defaultDelay = $delay; # if queue in progess set start vals to last cached hsv target, else set start to actual hsv if (@{$ledDevice->{helper}->{hlCmdQueue}} > 0) { $hueFrom = $ledDevice->{helper}->{targetHue}; $satFrom = $ledDevice->{helper}->{targetSat}; $valFrom = $ledDevice->{helper}->{targetVal}; $timeFrom = $ledDevice->{helper}->{targetTime}; Log3 ($ledDevice, 5, "$ledDevice->{NAME} prepare start hsv transition (is cached) hsv $hueFrom, $satFrom, $valFrom, $timeFrom"); } else { $hueFrom = $ledDevice->{READINGS}->{hue}->{VAL} || 0; $satFrom = $ledDevice->{READINGS}->{saturation}->{VAL} || 0; $valFrom = $ledDevice->{READINGS}->{brightness}->{VAL} || 0; $timeFrom = gettimeofday(); Log3 ($ledDevice, 5, "$ledDevice->{NAME} prepare start hsv transition (is actual) hsv $hueFrom, $satFrom, $valFrom, $timeFrom"); } Log3 ($ledDevice, 4, "$ledDevice->{NAME} current HSV $hueFrom, $satFrom, $valFrom"); Log3 ($ledDevice, 3, "$ledDevice->{NAME} set HSV $hue, $sat, $val with ramp: $ramp, flags: ". $flags); # if there is no ramp we dont need transition if (($ramp || 0) == 0) { Log3 ($ledDevice, 4, "$ledDevice->{NAME} hsv transition without ramp routed to direct settings, hsv $hue, $sat, $val"); $ledDevice->{helper}->{targetTime} = $timeFrom; return WifiLight_HighLevelCmdQueue_Add($ledDevice, $hue, $sat, $val, undef, $delay, 100, $event, $timeFrom); } # calculate the left and right turn length based # startAngle +360 -endAngle % 360 = counter clock # endAngle +360 -startAngle % 360 = clockwise my $fadeLeft = ($hueFrom + 360 - $hue) % 360; my $fadeRight = ($hue + 360 - $hueFrom) % 360; my $direction = ($fadeLeft <=> $fadeRight); # -1 = counterclock, +1 = clockwise $direction = ($direction == 0)?1:$direction; # in dupt cw Log3 ($ledDevice, 4, "$ledDevice->{NAME} color rotation dev cc:$fadeLeft, cw:$fadeRight, shortest:$direction"); $direction *= -1 if ($flags =~ m/.*[lL].*/); # reverse if long path desired (flag l or L is set) my $rotation = ($direction == 1)?$fadeRight:$fadeLeft; # angle of hue rotation in based on flags my $sFade = abs($sat - $satFrom); my $vFade = abs($val - $valFrom); my ($stepWide, $steps, $hueToSet, $hueStep, $satToSet, $satStep, $valToSet, $valStep); # fix if there is in fact no transition, blocks queue for given ramp time with actual hsv values if ($rotation == 0 && $sFade == 0 && $vFade == 0) { Log3 ($ledDevice, 4, "$ledDevice->{NAME} hsv transition with unchaned settings, hsv $hue, $sat, $val, ramp $ramp"); #TODO remove if tested #WifiLight_HighLevelCmdQueue_Add($ledDevice, $hue, $sat, $val, undef, $ramp * 1000, 0, $event, $timeFrom); $ledDevice->{helper}->{targetTime} = $timeFrom + $ramp; return WifiLight_HighLevelCmdQueue_Add($ledDevice, $hue, $sat, $val, undef, $delay, 100, $event, $timeFrom + $ramp); } if (($rotation >= $sFade) && ($rotation >= $vFade)) { $stepWide = ($ramp * 1000 / $rotation); # how long is one step (set hsv) in ms based on hue $stepWide = $defaultDelay if ($stepWide < $defaultDelay); $steps = int($ramp * 1000 / $stepWide); # how many steps will we need ? Log3 ($ledDevice, 4, "$ledDevice->{NAME} transit (H>S||V) steps: $steps stepwide: $stepWide"); } elsif (($sFade >= $rotation) && ($sFade >= $vFade)) { $stepWide = ($ramp * 1000 / $sFade); # how long is one step (set hsv) in ms based on sat $stepWide = $defaultDelay if ($stepWide < $defaultDelay); $steps = int($ramp * 1000 / $stepWide); # how many steps will we need ? Log3 ($ledDevice, 4, "$ledDevice->{NAME} transit (S>H||V) steps: $steps stepwide: $stepWide"); } else { $stepWide = ($ramp * 1000 / $vFade); # how long is one step (set hsv) in ms based on val $stepWide = $defaultDelay if ($stepWide < $defaultDelay); $steps = int($ramp * 1000 / $stepWide); # how many steps will we need ? Log3 ($ledDevice, 4, "$ledDevice->{NAME} transit (V>H||S) steps: $steps stepwide: $stepWide"); } $hueToSet = $hueFrom; # prepare tmp working hue $hueStep = $rotation / $steps * $direction; # how big is one hue step base on timing choosen $satToSet = $satFrom; # prepare workin sat $satStep = ($sat - $satFrom) / $steps; $valToSet = $valFrom; $valStep = ($val - $valFrom) / $steps; #TODO do something more flexible #TODO remove if tested # $timeFrom += 1; for (my $i=1; $i <= $steps; $i++) { $hueToSet += $hueStep; $hueToSet -= 360 if ($hueToSet > 360); #handle turn over zero $hueToSet += 360 if ($hueToSet < 0); $satToSet += $satStep; $valToSet += $valStep; my $progress = 100 / $steps * $i; Log3 ($ledDevice, 4, "$ledDevice->{NAME} add to hl queue h:".($hueToSet).", s:".($satToSet).", v:".($valToSet)." ($i/$steps)"); WifiLight_HighLevelCmdQueue_Add($ledDevice, int($hueToSet +0.5), int($satToSet +0.5), int($valToSet +0.5), undef, $stepWide, int($progress +0.5), $event, $timeFrom + (($i-1) * $stepWide / 1000) ); } $ledDevice->{helper}->{targetTime} = $timeFrom + $ramp; return undef; } sub WifiLight_SetHSV_Target(@) { my ($ledDevice, $hue, $sat, $val) = @_; $ledDevice->{helper}->{targetHue} = $hue; $ledDevice->{helper}->{targetSat} = $sat; $ledDevice->{helper}->{targetVal} = $val; return undef; } sub WifiLight_setHSV_Readings(@) { my ($ledDevice, $hue, $sat, $val) = @_; my ($r, $g, $b) = WifiLight_HSV2RGB($hue, $sat, $val); readingsBeginUpdate($ledDevice); readingsBulkUpdate($ledDevice, "hue", $hue % 360); readingsBulkUpdate($ledDevice, "saturation", $sat); readingsBulkUpdate($ledDevice, "brightness", $val); readingsBulkUpdate($ledDevice, "RGB", sprintf("%02X%02X%02X",$r,$g,$b)); readingsBulkUpdate($ledDevice, "state", "on") if ($val > 0); readingsBulkUpdate($ledDevice, "state", "off") if ($val == 0); readingsEndUpdate($ledDevice, 1); } sub WifiLight_HSV2RGB(@) { my ($hue, $sat, $val) = @_; if ($sat == 0) { return int(($val * 2.55) +0.5), int(($val * 2.55) +0.5), int(($val * 2.55) +0.5); } $hue %= 360; $hue /= 60; $sat /= 100; $val /= 100; my $i = int($hue); my $f = $hue - $i; my $p = $val * (1 - $sat); my $q = $val * (1 - $sat * $f); my $t = $val * (1 - $sat * (1 - $f)); my ($r, $g, $b); if ( $i == 0 ) { ($r, $g, $b) = ($val, $t, $p); } elsif ( $i == 1 ) { ($r, $g, $b) = ($q, $val, $p); } elsif ( $i == 2 ) { ($r, $g, $b) = ($p, $val, $t); } elsif ( $i == 3 ) { ($r, $g, $b) = ($p, $q, $val); } elsif ( $i == 4 ) { ($r, $g, $b) = ($t, $p, $val); } else { ($r, $g, $b) = ($val, $p, $q); } return (int(($r * 255) +0.5), int(($g * 255) +0.5), int(($b * 255) + 0.5)); } sub WifiLight_RGB2HSV(@) { my ($ledDevice, $in) = @_; my $r = hex substr($in, 0, 2); my $g = hex substr($in, 2, 2); my $b = hex substr($in, 4, 2); my ($max, $min, $delta); my ($h, $s, $v); $max = $r if (($r >= $g) && ($r >= $b)); $max = $g if (($g >= $r) && ($g >= $b)); $max = $b if (($b >= $r) && ($b >= $g)); $min = $r if (($r <= $g) && ($r <= $b)); $min = $g if (($g <= $r) && ($g <= $b)); $min = $b if (($b <= $r) && ($b <= $g)); $v = int(($max / 2.55) + 0.5); $delta = $max - $min; my $currentHue = ReadingsVal($ledDevice->{NAME}, "hue", 0); return ($currentHue, 0, $v) if (($max == 0) || ($delta == 0)); $s = int((($delta / $max) *100) + 0.5); $h = ($g - $b) / $delta if ($r == $max); $h = 2 + ($b - $r) / $delta if ($g == $max); $h = 4 + ($r - $g) / $delta if ($b == $max); $h = int(($h * 60) + 0.5); $h += 360 if ($h < 0); return $h, $s, $v; } sub WifiLight_HSV2fourChannel(@) { my ($h, $s, $v) = @_; my ($r, $g, $b) = WifiLight_HSV2RGB($h, $s, $v); #white part, base 255 my $white = 255; foreach ($r, $g, $b) { $white = $_ if ($_ < $white); } #remaining color part my ($rr, $rg, $rb); $rr = $r - $white; $rg = $g - $white; $rb = $b - $white; return ($rr, $rg, $rb, $white); } sub WifiLight_Milight_ColorConverter(@) { my ($ledDevice, $cr, $cy, $cg, $cc, $cb, $cm) = @_; #my ($ledDevice) = @_; my @colorMap; #my $hueRed = 0; my $adjRed = 0 - ($cr || 0); #my $hueYellow = 60; my $adjYellow = 60 - ($cy || 0); #my $hueGreen = 120; my $adjGreen = 120 - ($cg || 0); #my $hueCyan = 180; my $adjCyan = 180 - ($cc || 0); #my $hueBlue = 240; my $adjBlue = 240 - ($cb || 0); #my $hueLilac = 300; my $adjLilac = 300 - ($cm || 0); #st34 my $devRed = 168; #my $devRed = 176; my $devYellow = 134; #my $devYellow = 144; my $devGreen = 88; #my $devCyan = 48; my $devCyan = 56; my $devBlue = 8; my $devLilac = 208; #224 my $i= 360; # red to yellow $adjRed += 360 if ($adjRed < 0); # in case of negative adjustment $devRed += 256 if ($devRed < $devYellow); $adjYellow += 360 if ($adjYellow < $adjRed); for ($i = $adjRed; $i <= $adjYellow; $i++) { $colorMap[$i % 360] = ($devRed - int((($devRed - $devYellow) / ($adjYellow - $adjRed) * ($i - $adjRed)) +0.5)) % 255; Log3 ($ledDevice, 4, "$ledDevice->{NAME} create colormap h: ".($i % 360)." d: ".$colorMap[$i % 360]); } #yellow to green $devYellow += 256 if ($devYellow < $devGreen); $adjGreen += 360 if ($adjGreen < $adjYellow); for ($i = $adjYellow; $i <= $adjGreen; $i++) { $colorMap[$i % 360] = ($devYellow - int((($devYellow - $devGreen) / ($adjGreen - $adjYellow) * ($i - $adjYellow)) +0.5)) % 255; Log3 ($ledDevice, 4, "$ledDevice->{NAME} create colormap h: ".($i % 360)." d: ".$colorMap[$i % 360]); } #green to cyan $devGreen += 256 if ($devGreen < $devCyan); $adjCyan += 360 if ($adjCyan < $adjGreen); for ($i = $adjGreen; $i <= $adjCyan; $i++) { $colorMap[$i % 360] = ($devGreen - int((($devGreen - $devCyan) / ($adjCyan - $adjGreen) * ($i - $adjGreen)) +0.5)) % 255; Log3 ($ledDevice, 4, "$ledDevice->{NAME} create colormap h: ".($i % 360)." d: ".$colorMap[$i % 360]); } #cyan to blue $devCyan += 256 if ($devCyan < $devCyan); $adjBlue += 360 if ($adjBlue < $adjCyan); for ($i = $adjCyan; $i <= $adjBlue; $i++) { $colorMap[$i % 360] = ($devCyan - int((($devCyan - $devBlue) / ($adjBlue - $adjCyan) * ($i - $adjCyan)) +0.5)) % 255; Log3 ($ledDevice, 4, "$ledDevice->{NAME} create colormap h: ".($i % 360)." d: ".$colorMap[$i % 360]); } #blue to lilac $devBlue += 256 if ($devBlue < $devLilac); $adjLilac += 360 if ($adjLilac < $adjBlue); for ($i = $adjBlue; $i <= $adjLilac; $i++) { $colorMap[$i % 360] = ($devBlue - int((($devBlue - $devLilac) / ($adjLilac - $adjBlue) * ($i- $adjBlue)) +0.5)) % 255; Log3 ($ledDevice, 4, "$ledDevice->{NAME} create colormap h: ".($i % 360)." d: ".$colorMap[$i % 360]); } #lilac to red $devLilac += 256 if ($devLilac < $devRed); $adjRed += 360 if ($adjRed < $adjLilac); for ($i = $adjLilac; $i <= $adjRed; $i++) { $colorMap[$i % 360] = ($devLilac - int((($devLilac - $devRed) / ($adjRed - $adjLilac) * ($i - $adjLilac)) +0.5)) % 255; Log3 ($ledDevice, 4, "$ledDevice->{NAME} create colormap h: ".($i % 360)." d: ".$colorMap[$i % 360]); } @{$ledDevice->{helper}->{COLORMAP}} = @colorMap; return \@colorMap; } sub WifiLight_RGB_ColorConverter(@) { # default correction +/- 29° my ($ledDevice, $cr, $cy, $cg, $cc, $cb, $cm) = @_; #my ($cr, $cy, $cg, $cc, $cb, $cm) = (0, -30, -10, -30, 0, -10); my @colorMap; for (my $i = 0; $i <= 360; $i++) { my $toR = WifiLight_HueDistance(0, $i); my $toY = WifiLight_HueDistance(60, $i); my $toG = WifiLight_HueDistance(120, $i); my $toC = WifiLight_HueDistance(180, $i); my $toB = WifiLight_HueDistance(240, $i); my $toM = WifiLight_HueDistance(300, $i); my $c = 0; # $i; $c += $cr - ($cr * $toR / 60) if (abs($toR) <= 60); $c += $cy - ($cy * $toY / 60) if (abs($toY) <= 60); $c += $cg - ($cg * $toG / 60) if (abs($toG) <= 60); $c += $cc - ($cc * $toC / 60) if (abs($toC) <= 60); $c += $cb - ($cb * $toB / 60) if (abs($toB) <= 60); $c += $cm - ($cm * $toM / 60) if (abs($toM) <= 60); $colorMap[$i] = int($i + $c + 0.5) % 360; #$colorMap[$i] = (int($colorMap[$i] + ($cr - ($cr * $toR / 45)) + 0.5) + 360) % 360 if (abs($toR) <= 45); #$colorMap[$i] = (int($colorMap[$i] + ($cy - ($cy * $toY / 45)) + 0.5) + 360) % 360 if (abs($toY) <= 45); #$colorMap[$i] = (int($colorMap[$i] + ($cg - ($cg * $toG / 45)) + 0.5) + 360) % 360 if (abs($toG) <= 45); } @{$ledDevice->{helper}->{COLORMAP}} = @colorMap; return \@colorMap; } # calculate the distance of two given hue sub WifiLight_HueDistance(@) { my ($hue, $testHue) = @_; my $a = (360 + $hue - $testHue) % 360; my $b = (360 + $testHue - $hue) % 360; return ($a, $b)[$a > $b]; } # helper for easying access to attrib sub WifiLight_ccAttribVal(@) { my ($ledDevice, $dr, $dy, $dg, $dc, $db, $dm) = @_; my $a = AttrVal($ledDevice->{NAME}, 'colorCast', undef); if ($a) { my ($cr, $cy, $cg, $cc, $cb, $cm) = split (',', $a); } else { my ($cr, $cy, $cg, $cc, $cb, $cm) = ($dr, $dy, $dg, $dc, $db, $dm); } return ($dr, $dy, $dg, $dc, $db, $dm); } sub WifiLight_CreateGammaMapping(@) { my ($ledDevice, $gamma) = @_; my @gammaMap; for (my $i = 0; $i <= 100; $i += 1) { my $correction = ($i / 100) ** (1 / $gamma); $gammaMap[$i] = $correction * 100; Log3 ($ledDevice, 5, "$ledDevice->{NAME} create gammamap v-in: ".$i.", v-out: $gammaMap[$i]"); } return \@gammaMap; } ############################################################################### # # high level queue, long running color transitions # ############################################################################### sub WifiLight_HighLevelCmdQueue_Add(@) { my ($ledDevice, $hue, $sat, $val, $ctrl, $delay, $progress, $event, $targetTime) = @_; my $cmd; $cmd->{hue} = $hue; $cmd->{sat} = $sat; $cmd->{val} = $val; # $cmd->{k} = $k; $cmd->{ctrl} = $ctrl; $cmd->{delay} = $delay; $cmd->{progress} = $progress; $cmd->{event} = $event; $cmd->{targetTime} = $targetTime; $cmd->{inProgess} = 0; push @{$ledDevice->{helper}->{hlCmdQueue}}, $cmd; my $dbgStr = unpack("H*", $cmd->{ctrl} || ''); Log3 ($ledDevice, 4, "$ledDevice->{NAME} high level cmd queue add hsv/ctrl $cmd->{hue}, $cmd->{sat}, $cmd->{val}, ctrl $dbgStr, targetTime $cmd->{targetTime}, qlen ".@{$ledDevice->{helper}->{hlCmdQueue}}); my $actualCmd = @{$ledDevice->{helper}->{hlCmdQueue}}[0]; # sender busy ? return undef if (($actualCmd->{inProgess} || 0) == 1); return WifiLight_HighLevelCmdQueue_Exec($ledDevice); } sub WifiLight_HighLevelCmdQueue_Exec(@) { my ($ledDevice) = @_; my $actualCmd = @{$ledDevice->{helper}->{hlCmdQueue}}[0]; # transmission complete, remove shift @{$ledDevice->{helper}->{hlCmdQueue}} if ($actualCmd->{inProgess}); # next in queue $actualCmd = @{$ledDevice->{helper}->{hlCmdQueue}}[0]; my $nextCmd = @{$ledDevice->{helper}->{hlCmdQueue}}[1]; # return if no more elements in queue return undef if (!defined($actualCmd->{inProgess})); # drop frames if next frame is already sceduled for given time. do not drop if it is the last frame or if it is a command while (defined($nextCmd->{targetTime}) && ($nextCmd->{targetTime} < gettimeofday()) && !$actualCmd->{ctrl}) { shift @{$ledDevice->{helper}->{hlCmdQueue}}; $actualCmd = @{$ledDevice->{helper}->{hlCmdQueue}}[0]; $nextCmd = @{$ledDevice->{helper}->{hlCmdQueue}}[1]; Log3 ($ledDevice, 4, "$ledDevice->{NAME} high level cmd queue exec drop frame at hlQueue level. hl qlen: ".@{$ledDevice->{helper}->{hlCmdQueue}}); } Log3 ($ledDevice, 5, "$ledDevice->{NAME} high level cmd queue exec dropper delay: ".($actualCmd->{targetTime} - gettimeofday()) ); # set hsv or if a device ctrl command is sceduled: send it and ignore hsv if ($actualCmd->{ctrl}) { my $dbgStr = unpack("H*", $actualCmd->{ctrl}); Log3 ($ledDevice, 4, "$ledDevice->{NAME} high level cmd queue exec ctrl $dbgStr, qlen ".@{$ledDevice->{helper}->{hlCmdQueue}}); WifiLight_sendCtrl($ledDevice, $actualCmd->{ctrl}); } else { my $isLast = (@{$ledDevice->{helper}->{hlCmdQueue}} == 1)?1:undef; if (($ledDevice->{helper}->{llLock} == 0) || $isLast) { Log3 ($ledDevice, 4, "$ledDevice->{NAME} high level cmd queue exec hsv $actualCmd->{hue}, $actualCmd->{sat}, $actualCmd->{val}, delay $actualCmd->{delay}, hl qlen ".@{$ledDevice->{helper}->{hlCmdQueue}}.", ll qlen ".@{$ledDevice->{helper}->{llCmdQueue}}.", lock ".$ledDevice->{helper}->{llLock}); WifiLight_setHSV($ledDevice, $actualCmd->{hue}, $actualCmd->{sat}, $actualCmd->{val}, $isLast); } else { Log3 ($ledDevice, 5, "$ledDevice->{NAME} high level cmd queue exec drop frame at llQueue level. ll qlen: ".@{$ledDevice->{helper}->{llCmdQueue}}.", lock ".$ledDevice->{helper}->{llLock}); } } $actualCmd->{inProgess} = 1; my $next = defined($nextCmd->{targetTime})?$nextCmd->{targetTime}:gettimeofday() + ($actualCmd->{delay} / 1000); Log3 ($ledDevice, 4, "$ledDevice->{NAME} high level cmd queue ask next $next"); InternalTimer($next, "WifiLight_HighLevelCmdQueue_Exec", $ledDevice, 0); WifiLight_processEvent($ledDevice, $actualCmd->{event}, $actualCmd->{progress}); return undef; } sub WifiLight_HighLevelCmdQueue_Clear(@) { my ($ledDevice) = @_; Log3 ($ledDevice, 4, "$ledDevice->{NAME} high level cmd queue clear"); RemoveInternalTimer($ledDevice, 'WifiLight_HighLevelCmdQueue_Exec'); $ledDevice->{helper}->{hlCmdQueue} = []; } # dispatcher for ctrl cmd sub WifiLight_sendCtrl(@) { my ($ledDevice, $ctrl) = @_; # TODO adjust for all bridge types if (($ledDevice->{LEDTYPE} eq 'RGB') && ($ledDevice->{CONNECTION} =~ 'bridge-V[2|3]')) { my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 100; WifiLight_LowLevelCmdQueue_Add($ledDevice, $ctrl, $receiver, $delay); } if ($ledDevice->{LEDTYPE} eq 'RGBW1') { my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 100; WifiLight_LowLevelCmdQueue_Add($ledDevice, $ctrl, $receiver, $delay); } if ($ledDevice->{LEDTYPE} eq 'RGBW2') { my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 100; WifiLight_LowLevelCmdQueue_Add($ledDevice, $ctrl, $receiver, $delay); } if ($ledDevice->{LEDTYPE} eq 'White') { my $receiver = sockaddr_in($ledDevice->{PORT}, inet_aton($ledDevice->{IP})); my $delay = 10; WifiLight_LowLevelCmdQueue_Add($ledDevice, $ctrl, $receiver, $delay); } } ############################################################################### # # atomic low level udp communication to device # required because there are timing requirements, mostly limitaions in processing speed of the bridge # the commands should never be interrupted or canceled because some fhem readings are set in advance # ############################################################################### sub WifiLight_LowLevelCmdQueue_Add(@) { my ($ledDevice, $command, $receiver, $delay, $unlock) = @_; my $cmd; $cmd->{command} = $command; $cmd->{sender} = $ledDevice; $cmd->{receiver} = $receiver; $cmd->{delay} = $delay; $cmd->{unlock} = $unlock; $cmd->{inProgess} = 0; # push cmd into queue push @{$ledDevice->{helper}->{llCmdQueue}}, $cmd; my $dbgStr = unpack("H*", $cmd->{command}); Log3 ($ledDevice, 5, "$ledDevice->{NAME} low level cmd queue add $dbgStr, qlen ".@{$ledDevice->{helper}->{llCmdQueue}}); my $actualCmd = @{$ledDevice->{helper}->{llCmdQueue}}[0]; # sender busy ? return undef if ($actualCmd->{inProgess}); return WifiLight_LowLevelCmdQueue_Send($ledDevice); } sub WifiLight_LowLevelCmdQueue_Send(@) { my ($ledDevice) = @_; my $actualCmd = @{$ledDevice->{helper}->{llCmdQueue}}[0]; # transmission complete, remove shift @{$ledDevice->{helper}->{llCmdQueue}} if ($actualCmd->{inProgess}); # next in queue $actualCmd = @{$ledDevice->{helper}->{llCmdQueue}}[0]; # remove a low level queue lock if present and get next while (($actualCmd->{unlock} || 0) == 1) { $actualCmd->{sender}->{helper}->{llLock} -= 1; Log3 ($ledDevice, 5, "$ledDevice->{NAME} | $actualCmd->{sender}->{NAME} unlock queue ".$actualCmd->{sender}->{helper}->{llLock}); shift @{$ledDevice->{helper}->{llCmdQueue}}; $actualCmd = @{$ledDevice->{helper}->{llCmdQueue}}[0]; } # return if no more elements in queue return undef if (!defined($actualCmd->{command})); my $dbgStr = unpack("H*", $actualCmd->{command}); Log3 ($ledDevice, 5, "$ledDevice->{NAME} low level cmd queue qlen ".@{$ledDevice->{helper}->{llCmdQueue}}.", send $dbgStr"); # TCP if ($ledDevice->{PROTO}) { if (!$ledDevice->{helper}->{SOCKET} || ($ledDevice->{helper}->{SELECT}->can_read(0) && !$ledDevice->{helper}->{SOCKET}->recv(my $data, 512))) { Log3 ($ledDevice, 4, "$ledDevice->{NAME} low level cmd queue send $dbgStr, qlen ".@{$ledDevice->{helper}->{llCmdQueue}}." connection refused: trying to reconnect"); if ($ledDevice->{helper}->{SOCKET}) { $ledDevice->{helper}->{SOCKET}->shutdown(2); $ledDevice->{helper}->{SOCKET}->close(); } $ledDevice->{helper}->{SOCKET} = IO::Socket::INET-> new ( PeerPort => $ledDevice->{PORT}, PeerAddr => $ledDevice->{IP}, Timeout => 1, Blocking => 0, Proto => 'tcp') or Log3 ($ledDevice, 3, "$ledDevice->{NAME} low level cmd queue send ERROR $dbgStr, qlen ".@{$ledDevice->{helper}->{llCmdQueue}}." (reconnect giving up)"); $ledDevice->{helper}->{SELECT} = IO::Select->new($ledDevice->{helper}->{SOCKET}) if $ledDevice->{helper}->{SOCKET}; } $ledDevice->{helper}->{SOCKET}->send($actualCmd->{command}) if $ledDevice->{helper}->{SOCKET}; } else { # print "send: $ledDevice->{NAME} $dbgStr \n"; send($ledDevice->{helper}->{SOCKET}, $actualCmd->{command}, 0, $actualCmd->{receiver}) or Log3 ($ledDevice, 1, "$ledDevice->{NAME} low level cmd queue send ERROR $@ $dbgStr, qlen ".@{$ledDevice->{helper}->{llCmdQueue}}); } $actualCmd->{inProgess} = 1; my $msec = $actualCmd->{delay} / 1000; InternalTimer(gettimeofday()+$msec, "WifiLight_LowLevelCmdQueue_Send", $ledDevice, 0); return undef; } 1; =pod =item device =item summary controls a large number of different LED types =item summary_DE steuert eine große Anzahl unterschiedlicher LED Typen =begin html

WifiLight

    The module controls a large number of different "no name" LED types and provide a consistent interface.

    Following types will be supported:

    type / bridge type note define signature
    Milight RGB first generation E27, stripe controller *(1,2,a,C) RGB bridge-V2|3
    Milight RGBW1 first generation RGBW stripe controller *(1,2,a) RGBW1 bridge-V2|3
    Milight Dual White E14, E27, GU10, stripe controller, Downlight *(1,2,b,W,nK) White bridge-V2|3
    Milight RGBW2 second generation E14, E27, GU10, stripe controller, Downlight *(2,b,CW,S20) RGBW2 bridge-V3
    LW12 first generation (SSID LEDNet...) RGB stripe controller   RGB LW12
    LW12HX (SSID HX...) RGB stripe controller   RGB LW12HX
    LW12FC (SSID FC...) RGB stripe controller   RGB LW12FC
    LD316 in RGB mode E27   RGB LD316
    LD316 in RGBW mode E27 *(S20) RGBW LD316
    LD316A in RGBW mode E27 *(S20) RGBW LD316A
    LD382 in RGB mode RGB stripe controller   RGB LD382
    LD382 in RGBW mode RGBW stripe controller   RGBW LD382
    LD382A (FW 1.0.6+) in RGB mode RGB stripe controller   RGB LD382
    LD382A (FW 1.0.6+) in RGBW mode RGBW stripe controller   RGBW LD382
    SENGLED E27 bulb with build-in WLAN repeater   White Sengled
    SUNRICHER with RGBW Controller *(!!!) RGBW Sunricher

    (1) milght brigbe V2, V3, V4
    (2) milight bridge V3, V4
    (a) one group per bridge
    (b) four independent group per bridge
    (nK) no color temp support (Kelvin)
    (C) pure color
    (W) pure white
    (CW) pure Color or pure white
    (S20) Saturation <20: switch to pure white channel
    (!!!) EXPERIMENTAL

    Color

    Colors can be specified in RGB or HSV color space.

    Color in color space "HSV" are completely and generally more intuitive than RGB.

    H (HUE: 0..360) are the basic color in a color wheel.

    • Red is at 0 °
    • Green at 120 °
    • Blue at 240 °

    S (Saturation: 0..100) stands for the saturation of the color. A saturation of 100 means the color is "pure" or completely saturated. Blue, for example, with 100% saturation corresponds to RGB # 0000FF.

    V (Value: 0..100) indicates the brightness. A value of 50 states that "half brightness".

    HUE SAT 0° (Red) 60° (Yellow) 120° (Green) 180° (Cyan) 240° (Blue) 300° (Magenta)

    Color: HSV compared to RGB

    Normally, a color may be expressed in the HSV color space as well as in RGB color space.

    Colors in the HSV color space usually seem more understandable. To move a Green in the HSV color space a little more toward CYAN, simply increase the HUE value (angle) slightly. In RGB color space, the same task is less intuitive to achieve by increasing blue.

    Differences become clear in Transitions however. In order to dim BLUE up the HSV Transitions 240,100,0 -> 240,100,100 would be used. To slowly dim RED (brightness 0) to BLUE the Transition in the HSV color space is 0,100,0 -> 240,100,100. In RGB color space (# 000000 -> # 0000FF) can not distinguish between the two versions. Here (correctly, but probably differently than intended) would appear in both cases, a white (brightness 0) as an initial value.

    Define

    • define <name> WifiLight <LED type> <bridgetype>:<IP|FQDN>

      example

        defines a milight RGBW2 (bulb or LED stripe controller) on a milight bridge version 3 or 4. The LED is allocated to a maximum of 4 groups available per bridge in order of definition:
        define wz.licht.decke WifiLight RGBW2 bridge-V3:192.168.178.142

        defines a LD382A Controller with RGBW stripe:
        define wz.licht.decke WifiLight RGBW LD382A:192.168.178.142

        defines a LD382A Controller with RGB stripe:
        define wz.licht.decke WifiLight RGB LD382A:192.168.178.142

      WifiLight has a "color calibration". Ideally, a calibration should be performed every time after a lamp change or after definition.

    Set

    • set <name> on [ramp]

      Turns on the device. It is either chosen 100% White or the color defined by the attribute "default color".

      Advanced options:

      • ramp

    • set <name> off [ramp]

      Turns of the device.

      Advanced options:

      • ramp

    • set <name> dimup

      Increases the brightness by a fixed amount. The attribute "dimStep" or the default "7" is applied.
      This command is useful to increase particularly the brightness by a wall switch or a remote control.

      Advanced options:

      • none

    • set <name> dimdown

      Decreases the brightness by a fixed amount. The attribute "dimStep" or the default "7" is applied.
      This command is useful to reduce particularly the brightness by a wall switch or a remote control.

      Advanced options:

      • none

    • set <name> dim level [ramp] [q]

      Sets the brightness to the specified level (0..100). This command also maintains the preset color even with "dim 0" (off) and then "dim xx" (turned on) at. Therefore, it represents an alternative form to "off" / "on". The latter would always choose the "default color".

      Advanced options:

      • ramp

      Flags:

      • q

    • set <name> HSV H,S,V [ramp] [s|l|q] [event]

      Sets the color in the HSV color space. If the ramp is specified (as a time in seconds), the module calculates a soft color transition from the current color to the newly set.

        For example, sets a saturated blue with half brightness:
        set wz.licht.decke HSV 240,100,50

      Advanced options:

      • ramp

      Flags:

      • s l q event

    • set <name> RGB RRGGBB [ramp] [l|s|q] [event]

      Sets the color in the RGB color space.

      Advanced options:

      • ramp

      Flags:

      • s l q event

    Meaning of Flags

    Certain commands (set) can be marked with special flags.

    • ramp:
        Time in seconds for a soft color or brightness transition. The soft transition starts at the currently visible color and is calculated for the specified.
    • s:
        (short, default). A smooth transition to another color is carried out in the "color wheel" on the shortest path. A transition from red to green lead by the shortest route through yellow.
    • l:
        (long). A smooth transition to another color is carried out in the "color wheel" on the "long" way. A transition from red to green then leads across magenta, blue, and cyan.
    • q:
        (queue). Commands with this flag are cached in an internal queue and will not run before the currently running soft transitions have been processed. Commands without the flag will be processed immediately. In this case all running transitions are stopped immediately and the queue will be cleared.
    • event:
        designator ([A-Za-z_0-9])

        WifiLight creates, when using this flag, during transitions to another color messages (events) in the form:

        WifiLight <NAME> programm: <EVENT> <XX>.

        <EVENT> is the designator as specified in the flag.
        <XX> is the progress (percentage) of the transition.

        Depending on the total duration of the transition, the values from 0 to 100 will not completely go through but for 0% and 100% is guaranteed always a event. To these events can then be reacted within a notify or DOIF to (for example):

        • increase the volume of a radio when a lamp is turned on in the morning slowly
        • A color transition can be restarted in a notify if it is complete (loop it, even complex transitions)
        • Other light sources can be synchronized by individually created color transitions.

    color calibration

    WifiLight supports two different types of color calibrations:

      Correction of saturated colors

      background:

      YELLOW, for example, is defined as a mixture of red and green light in equal parts. Depending on the LED and control used the green channel may be much more luminous. If the red and green LEDs are each fully driven, GREEN predominates in this mixture and the desired YELLOW would get a distinct green tint. In this example, no yellow would be generated (corresponding to 60 ° in the "color wheel") for HSV 60,100,100. Instead GREEN would be generated with yellow tinge, perhaps corresponding to an estimated color angle of 80 °. The required correction for yellow would therefore minus 20° (60° target - 80° result = -20° correction). YELLOW may have to be corrected as to -20 °. Possible values per correction point are +/- 29 °.

      procedure:

      The correction of the full color is controlled by the attribute "color cast". Here 6 (comma separated) values are specified in the range from -29 to 29. These values are in accordance with the angle correction for red (0 °), yellow (60 °), green (120 °), cyan (180 °), blue (240 °) and magenta (300 °). First, the deviation of the mixed colors (60 ° / 180 ° / 300 °) should be determined as in the above example, and stored in the attribute. Following the primary colors (0 ° / 120 ° / 240 °) should be corrected so that the smooth transitions between adjacent pure colors appear as linear as possible. This process may need to be repeated iteratively multiple times until the result is harmonious.

      White Balance

      background:

      Some bulbs produce white light by mixing the RGB channels (for example, LW12). Depending on the light intensity of the RGB channels of the LED strips used, the result is different. One or two colors dominate. In addition, there are various types of white light. Cold light has a higher proportion of blue. In Central Europe mostly warm white light is used for light sources. This has a high red and low blue component.

      WifiLight offers the possibility for mixed RGB white to adapt the composition. The adjustment is carried out via the attribute "white point". The attribute expects a value between 0 and 1 (decimal point with) and the three colors are separated by a comma for each of the three RGB channels.

      procedure:

      A value of "1,1,1" sets all the three channels to 100% each. Assuming that the blue component of the white light should be reduced, a value of "1,1,0.5" sets the third channel (BLUE) in white on 0.5 according to 50%. Before doing a white balance correction the adjusment of the saturated color should be completed.

    Attribute

    • attr <name> colorCast <R,Y,G,C,B,M>

      color calibration of saturated colors. R(ed), Y(ellow), G(reen), C(yan), B(lue), M(agenta) in the range of +/- 29 (degrees)

    • attr <name> defaultColor <H,S,V>

      Specify the light color in HSV which is selected at "on". Default is white.

    • attr <name> defaultRamp <0 bis X>

      Time in seconds. If this attribute is set, a smooth transition is always implicitly generated if no ramp in the set is indicated.

    • attr <name> dimStep <0 bis 100>

      Value by which the brightness at dim up and dim-down is changed. Default is "7"

    • attr <name> gamma <X.X>

      The human eye perceives brightness changes very differently to (logarithmic). At low output brightness even a small change in brightness is perceived as very strong and on the other side strong changes are needed at high luminance. Therefore, a logarithmic correction of brightness increase of lamps is necessary so that the increase is found to be uniform. Some controllers perform this correction internally. In other cases it is necessary to store this correction in the module. A gamma value of 1.0 (default) results in a linear output values. Values less than 1.0 lead to a logarithmic correction.

    • attr <name> whitePoint <R,G,B>

      color calibration for mixed RGB white light.

    • attr <name> readingFnAttributes

    Colored device-icon for FhemWeb

      To activate a colored icon for FhemWeb the following attribute must be set:

    • attr <name> devStateIcon {Color_devStateIcon(ReadingsVal($name,"RGB","000000"))}

    Colorpicker for FhemWeb

      In order for the Color Picker can be used in FhemWeb following attributes need to be set:

    • attr <name> webCmd RGB
    • attr <name> widgetOverride RGB:colorpicker,RGB
=end html =begin html_DE

WifiLight

    Das Modul steuert eine große Anzahl unterschiedlicher "no name" LED Typen und stellt Ihnen einheitliches Interface zur Verfügung.

    Folgende Typen werden unterstützt:

    Leuchtmitteltyp / bridge Type Notiz Signatur im define
    Milight RGB erste Generation E27, stripe controller *(1,2,a,C) RGB bridge-V2|3
    Milight RGBW1 erste Generation RGBW stripe controller *(1,2,a) RGBW1 bridge-V2|3
    Milight White E14, E27, GU10, stripe controller, Downlight *(1,2,b,W,nK) White bridge-V2|3
    Milight RGBW2 zweite Generation E14, E27, GU10, stripe controller, Downlight *(2,b,CW,S20) RGBW2 bridge-V3
    LW12 erste Generation (SSID LEDNet...) RGB stripe controller   RGB LW12
    LW12HX (SSID HX...) RGB stripe controller   RGB LW12HX
    LW12FC (SSID FC...) RGB stripe controller   RGB LW12FC
    LD316 im RGB mode E27   RGB LD316
    LD316 im RGBW mode E27 *(S20) RGBW LD316
    LD316A im RGBW mode E27 *(S20) RGBW LD316A
    LD382 im RGB mode RGB stripe controller   RGB LD382
    LD382 im RGBW mode RGBW stripe controller   RGBW LD382
    LD382A (FW 1.0.6) im RGB mode RGB stripe controller   RGB LD382
    LD382A (FW 1.0.6) im RGBW mode RGBW stripe controller   RGBW LD382
    SENGLED E27 mit WLAN repeater   White Sengled
    SUNRICHER mit RGBW Controller *(!!!) RGBW Sunricher

    (1) milght brigbe V2, V3, V4
    (2) milight bridge V3, V4
    (a) eine Gruppe pro bridge
    (b) vier unabhängige Gruppen pro bridge
    (nK) kein Temperatursupport, Kelvin
    (C) rein Color
    (W) rein White
    (CW) rein Color oder White
    (S20) Saturation <20: umschalten white Channel
    (!!!) EXPERIMENTAL

    Farbangaben

    Farben können im RGB oder im HSV Farbraum angegeben werden.

    Farbangaben im Farbraum "HSV" sind vollständig und in der Regel intuitiver als RGB.

    H (HUE: 0..360) gibt die Grundfarbe in einem Farbkreis an.

    • Rot liegt bei 0°
    • Grün bei 120°
    • Blau bei 240°

    S (Saturation/Sättigung: 0..100) steht für die Sättigung der Farbe. Eine Sättigung von 100 bedeutet die Farbe ist "rein" oder komplett gesättigt. Blau zum Beispiel mit 100% Sättigung entspricht RGB #0000FF.

    V (Value: 0..100) gibt die Helligkeit an. Ein V von 50 heißt: "halbe Helligkeit".

    HUE SAT 0° (Rot) 60° (Gelb) 120° (Grün) 180° (Cyan) 240° (Blau) 300° (Magenta)

    Farbangaben: HSV gegenüber RGB

    Im Normalfall kann eine Farbe im HSV Farbraum genauso wie im RGB Farbraum dargestellt werden.

    Farben im HSV Farbraum wirken meist verständlicher. Um ein Grün im HSV Farbraum etwas mehr in Richtung CYAN zu bewegen wird einfach der HUE Wert (Winkel) etwas erhöht. Im RGB Farbraum ist die gleiche Aufgabe weniger intuitiv durch eine Erhöhung von BLAU zu erreichen.

    Unterschiede werden jedoch bei Transitions deutlich. Um BLAU langsam auf zu dimmen lauten die HSV Transitions 240,100,0 -> 240,100,100. Um von ROT (Helligkeit 0) langsam auf BLAU zu dimmen wird im HSV Farbraum 0,100,0 -> 240,100,100 verwendet. Im RGB Farbraum (#000000 -> #0000FF) kann nicht zwischen den beiden Varianten unterschieden werden. Hier würde (richtiger weise, vermutlich jedoch anders als beabsichtigt) in beiden Fällen ein Weiß (Helligkeit 0) als Startwert erscheinen.

    Define

    • define <name> WifiLight <Leuchtmitteltyp> <bridgetyp>:<IP|FQDN>

      Beispiele

        definiert einen milight RGBW2 Leuchtmittel (Bulb oder LED stripe controller) an einer milight bridge Version 3 oder 4. Die LED wird den maximal 4 verfügbaren Gruppen pro bridge in der Reihenfolge der Definition zugeordnet:
        define wz.licht.decke WifiLight RGBW2 bridge-V3:192.168.178.142

        definiert einen LD382A Controller mit RGBW Stripe:
        define wz.licht.decke WifiLight RGBW LD382A:192.168.178.142

        definiert einen LD382A Controller mit RGB Stripe:
        define wz.licht.decke WifiLight RGB LD382A:192.168.178.142

      WifiLight verfügt über eine "Farbkalibrierung". Sinnvollerweise sollte nach einem Leuchtmitteltausch oder einem define eine Kalibrierung vorgenommen werden.

    Set

    • set <name> on [ramp]

      Schaltet das device ein. Dabei wird entweder 100% Weiß oder die im Attribut "defaultColor" definierte Farbe gewählt.

      Erweiterte Parameter:

      • ramp

    • set <name> off [ramp]

      Schaltet das device aus.

      Erweiterte Parameter:

      • ramp

    • set <name> dimup

      Erhöht die Helligkeit um einen festen Betrag. Dabei wird der im Attribut "dimStep" definierte Wert oder der Default "7" angewendet.
      Dieser Befehl eignet sich besonders um die Helligkeit über einen Wandschalter oder eine Fernbedienung zu erhöhen.

      Erweiterte Parameter:

      • keine

    • set <name> dimdown

      Verringert die Helligkeit um einen festen Betrag. Dabei wird der im Attribut "dimStep" definierte Wert oder der Default "7" angewendet.
      Dieser Befehl eignet sich besonders um die Helligkeit über einen Wandschalter oder eine Fernbedienung zu verringern

      Erweiterte Parameter:

      • keine

    • set <name> dim level [ramp] [q]

      Setzt die Helligkeit auf den angegebenen level (0..100).
      Dieser Befehl behält außerdem die eingestellte Farbe auch bei "dim 0" (ausgeschaltet) und nachfolgendem "dim xx" (eingeschaltet) bei. Daher stellt er eine alternative Form zu "off" / "on" dar. Letzteres würde immer die "defaultColor" wählen.

      Erweiterte Parameter:

      • ramp

      Flags:

      • q

    • set <name> HSV H,S,V [ramp] [s|l|q] [event]

      Setzt die Farbe im HSV Farbraum. Wenn die ramp (als Zeit in Sekunden) angegeben ist, berechnet das modul einen weichen Farbübergang von der aktuellen Farbe zur neu gesetzten.

        Beispiel, setzt ein gesättigtes Blau mit halber Helligkeit:
        set wz.licht.decke HSV 240,100,50

      Erweiterte Parameter:

      • ramp

      Flags:

      • s l q event

    • set <name> RGB RRGGBB [ramp] [l|s|q] [event]

      Setzt die Farbe im RGB Farbraum.

      Erweiterte Parameter:

      • ramp

      Flags:

      • s l q event

    Bedeutung der Flags

    Bestimmte Befehle (set) können mit speziellen Flags versehen werden.

    • ramp:
        Zeit in Sekunden für einen weichen Farb- oder Helligkeitsübergang. Der weiche Übergang startet bei der aktuell sichtbaren Farbe und wird zur angegeben berechnet.
    • s:
        (short, default). Ein weicher Übergang zu einer anderen Farbe wird im "Farbkreis" auf dem kürzesten Weg durchgeführt.
        Eine Transition von ROT nach GRÜN führt auf dem kürzesten Weg über GELB.
    • l:
        (long). Ein weicher Übergang zu einer anderen Farbe wird im "Farbkreis" auf dem "langen" Weg durchgeführt.
        Eine Transition von ROT nach GRÜN führt dann über MAGENTA, BLAU, und CYAN.
    • q:
        (queue). Kommandos mit diesem Flag werden in einer internen Warteschlange zwischengespeichert und erst ausgeführt nachdem die aktuell laufenden weichen Übergänge abgearbeitet wurden. Kommandos ohne das Flag werden sofort abgearbeitet. Dabei werden alle laufenden Übergänge sofort abgebrochen und die Warteschlange wird gelöscht.
    • event:
        Beliebige Bezeichnung ([A-Za-z_0-9])

        WifiLight erzeugt bei Verwendung dieses Flags im Verlauf weicher Übergange zu einer anderen Farbe Nachrichten (events) in der Form:

        WifiLight <NAME> programm: <EVENT> <XX>.

        <EVENT> entspricht dem Namen so wie im Flag angegeben.
        <XX> ist der prozentuale Fortschritt des Übergangs.

        Je nach Gesamtdauer des Übergangs werden die Werte von 0 bis 100 nicht komplett durchlaufen wobei jedoch für 0% und 100% immer ein event garantiert ist. Auf diese events kann dann innerhalb von notify oder DOIF reagiert werden um zum Beispiel:

        • die Lautstärke eines Radios anzupassen wenn eine LED morgens langsam hochgedimmt wird
        • ein Farbübergang kann in einem notify neu gestartet werden wenn er komplett ist (loop)
        • andere Leuchtmittel können mit erstellten Farbübergängen synchronisiert werden

    Farbkalibrierung

    WifiLight unterstützt zwei unterschiedliche Formen der Farbkalibrierungen:

      Korrektur gesättigter Farben

      Hintergrund:

      GELB, zum Beispiel, ist definiert als Mischung aus ROTEM und GRÜNEM Licht zu gleichen Teilen. Je nach verwendeter LED und Ansteuerung ist der GRÜNE Kanal nun möglicherweise viel leuchtstärker. Wenn jetzt also die ROTE und GRÜNE LED jeweils voll angesteuert werden überwiegt GRÜN in dieser Mischung und das gewünschte GELB bekäme einen deutlichen Grünstich. In diesem Beispiel würde jetzt für HSV 60,100,100 kein Gelb (entsprechend 60° im "Farbkreis") erzeugt. Stattdessen würde GRÜN mit GELBSTICH erzeugt das vielleicht einem geschätzten Farbwinkel von 80° entspricht. Die erforderliche Korrektur für GELB würde also minus 20° betragen (60° SOLL - 80° IST = -20° Korrektur). GELB müsste als um -20° korrigiert werden. Mögliche Werte pro Korrektur-Punkt sind +/- 29°.

      Vorgehen:

      Die Korrektur der Vollfarben wird über das Attribut "colorCast" gesteuert. Dabei werden 6 (Komma getrennte) Werte im Bereich -29 bis 29 angegeben. Diese Werte stehen entsprechen der Winkelkorrektur für ROT (0°), GELB (60°), GRÜN (120°), CYAN (180°), BLAU (240°) und MAGENTA (300°). Zuerst sollte die Abweichung für 60°/180°/300° (die Mischfarben) so wie in obigem Beispiel ermittelt und im Attribut hinterlegt werden. Im Anschluss sollten die Primärfarben (0°/120°/240°) so korrigiert werden das die weichen Übergänge zwischen benachbarten reinen Farben möglichst linear erscheinen. Dieser Vorgang muss eventuell iterativ mehrfach wiederholt werden bis das Ergebniss stimmig ist.

      Weißabgleich

      Hintergrund:

      Einige Leuchtmittel erzeugen weißes Licht durch Mischung der RGB Kanäle (zum Beispiel LW12). Je nach Leuchtstärke der RGB Kanäle der verwendeten LED Streifen unterscheidet sich das Ergebnis und eine oder zwei Farben dominieren. Zusätzlich gibt es verschiedene Formen weißen Lichtes. Kaltes Licht hat einen höheren Blauanteil. Dagegen wird in Mitteleuropa für Leuchtmittel meist warm-weiß verwendet welches einen hohen ROT- und geringen BLAU Anteil hat.

      WifiLight bietet die Möglichkeit bei RGB gemischtem Weiß die Zusammensetzung anzupassen. Die Anpassung erfolgt über das Attribut "whitePoint". Dieses erwartet für jeden der drei RGB Kanäle einen Wert zwischen 0 und 1 (ein Komma wird als Punkt angegeben). Die drei Werte werden mit einem normalen Komma getrennt.

      Vorgehen:

      Eine Angabe von "1,1,1" setzt alle die drei Kanäle auf jeweils 100%. Angenommen der BLAU Anteil des weißen Lichtes soll nun verringert werden. Ein Wert von "1,1,0.5" setzt den dritten Kanal (BLAU) bei Weiß auf 0.5 entsprechend 50%. Vor einem Weißabgleich sollte die Korrektur der Vollfarben abgeschlossen sein.

    Attribute

    • attr <name> colorCast <R,Y,G,C,B,M>

      Farbkalibrierung der voll gesättigten Farben. R(ed), Y(ellow), G(reen), C(yan), B(lue), M(agenta) im Bereich +/- 29

    • attr <name> defaultColor <H,S,V>

      HSV Angabe der Lichtfarbe die bei "on" gewählt wird. Default ist Weiß.

    • attr <name> defaultRamp <0 bis X>

      Zeit in Sekunden. Wenn dieses Attribut gesetzt ist wird implizit immer ein weicher Übergang erzeugt wenn keine Ramp im set angegeben ist.

    • attr <name> dimStep <0 bis 100>

      Wert um den die Helligkeit bei dimUp und dimDown verändert wird. Default 7.

    • attr <name> gamma <X.X>

      Das menschliche Auge nimmt Helligkeitsänderungen sehr unterschiedlich wahr (logarithmisch). Bei geringer Ausgangshelligkeit wird schon eine kleine Helligkeitsänderung als sehr stark empfunden und auf der anderen Seite sind bei großer Helligkeit starke Änderungen notwendig. Daher ist eine logarithmische Korrektur des Helligkeitsanstiegs der Leuchtmittel erforderlich damit der Anstieg als gleichmäßig empfunden wird. Einige controller führen diese Korrektur intern durch. In anderen Fällen ist es notwendig diese Korrektur im Modul zu hinterlegen. Ein gamma Wert von 1.0 (default) führt zu einer linearen Ausgabe der Werte. Werte kleiner als 1.0 führen zu einer logarithmischem Korrektur.

    • attr <name> whitePoint <R,G,B>

      Farbkalibrierung für RGB gemischtes weißes Licht.

    • attr <name> readingFnAttributes

    Farbiges Icon für FhemWeb

      Um ein farbiges Icon für FhemWeb zu aktivieren muss das folgende Attribut gesetzt sein:

    • attr <name> devStateIcon {Color_devStateIcon(ReadingsVal($name,"RGB","000000"))}

    Colorpicker für FhemWeb

      Um den Color-Picker für FhemWeb zu aktivieren müssen folgende Attribute gesetzt werden:

    • attr <name> webCmd RGB
    • attr <name> widgetOverride RGB:colorpicker,RGB
=end html_DE