31_HUEDevice.pm 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672
  1. # $Id: 31_HUEDevice.pm 17560 2018-10-18 08:12:38Z justme1968 $
  2. # "Hue Personal Wireless Lighting" is a trademark owned by Koninklijke Philips Electronics N.V.,
  3. # see www.meethue.com for more information.
  4. # I am in no way affiliated with the Philips organization.
  5. package main;
  6. use strict;
  7. use warnings;
  8. use Color;
  9. use POSIX;
  10. use JSON;
  11. use SetExtensions;
  12. #require "30_HUEBridge.pm";
  13. #require "$attr{global}{modpath}/FHEM/30_HUEBridge.pm";
  14. use vars qw(%FW_webArgs); # all arguments specified in the GET
  15. my %hueModels = (
  16. LCT001 => {name => 'Hue Bulb' ,type => 'Extended color light' ,subType => 'extcolordimmer',
  17. gamut => 'B', icon => 'hue_filled_white_and_color_e27_b22', },
  18. LCT002 => {name => 'Hue Spot BR30' ,type => 'Extended color light' ,subType => 'extcolordimmer',
  19. gamut => 'B', icon => 'hue_filled_br30.svg', },
  20. LCT003 => {name => 'Hue Spot GU10' ,type => 'Extended color light' ,subType => 'extcolordimmer',
  21. gamut => 'B', icon => 'hue_filled_gu10_par16', },
  22. LCT007 => {name => 'Hue Bulb V2' ,type => 'Extended color light' ,subType => 'extcolordimmer',
  23. gamut => 'B', icon => 'hue_filled_white_and_color_e27_b22', },
  24. LCT010 => {name => 'Hue Bulb V3' ,type => 'Extended color light' ,subType => 'extcolordimmer',
  25. gamut => 'C', icon => 'hue_filled_white_and_color_e27_b22', },
  26. LCT011 => {name => 'Hue BR30' ,type => 'Extended color light' ,subType => 'extcolordimmer',
  27. gamut => 'C', icon => 'hue_filled_br30.svg', },
  28. LCT012 => {name => 'Hue color candle' ,type => 'Extended color light' ,subType => 'extcolordimmer',
  29. gamut => 'C', },
  30. LCT014 => {name => 'Hue Bulb V3' ,type => 'Extended color light' ,subType => 'extcolordimmer',
  31. gamut => 'C', icon => 'hue_filled_white_and_color_e27_b22', },
  32. LLC001 => {name => 'Living Colors G2' ,type => 'Color light' ,subType => 'colordimmer',
  33. gamut => 'A', icon => 'hue_filled_iris', },
  34. LLC005 => {name => 'Living Colors Bloom' ,type => 'Color light' ,subType => 'colordimmer',
  35. gamut => 'A', icon => 'hue_filled_bloom', },
  36. LLC006 => {name => 'Living Colors Gen3 Iris' ,type => 'Color light' ,subType => 'colordimmer',
  37. gamut => 'A', icon => 'hue_filled_iris', },
  38. LLC007 => {name => 'Living Colors Gen3 Bloom' ,type => 'Color light' ,subType => 'colordimmer',
  39. gamut => 'A', icon => 'hue_filled_bloom', },
  40. LLC010 => {name => 'Hue Living Colors Iris' ,type => 'Color light' ,subType => 'colordimmer',
  41. gamut => 'A', icon => 'hue_filled_iris', },
  42. LLC011 => {name => 'Hue Living Colors Bloom' ,type => 'Color light' ,subType => 'colordimmer',
  43. gamut => 'A', icon => 'hue_filled_bloom', },
  44. LLC012 => {name => 'Hue Living Colors Bloom' ,type => 'Color light' ,subType => 'colordimmer',
  45. gamut => 'A', icon => 'hue_filled_bloom', },
  46. LLC013 => {name => 'Disney Living Colors' ,type => 'Color light' ,subType => 'colordimmer',
  47. gamut => 'A', icon => 'hue_filled_storylight', },
  48. LLC014 => {name => 'Living Colors Aura' ,type => 'Color light' ,subType => 'colordimmer',
  49. gamut => 'A', icon => 'hue_filled_aura', },
  50. LLC020 => {name => 'Hue Go' ,type => 'Color light' ,subType => 'extcolordimmer',
  51. gamut => 'C', icon => 'hue_filled_go', },
  52. LST001 => {name => 'Hue LightStrips' ,type => 'Color light' ,subType => 'colordimmer',
  53. gamut => 'A', icon => 'hue_filled_lightstrip', },
  54. LST002 => {name => 'Hue LightStrips Plus' ,type => 'Extended color light' ,subType => 'extcolordimmer',
  55. gamut => 'C', icon => 'hue_filled_lightstrip', },
  56. LWB001 => {name => 'Living Whites Bulb' ,type => 'Dimmable light' ,subType => 'dimmer',
  57. icon => 'hue_filled_living_whites', },
  58. LWB003 => {name => 'Living Whites Bulb' ,type => 'Dimmable light' ,subType => 'dimmer',
  59. icon => 'hue_filled_living_whites', },
  60. LWB004 => {name => 'Hue Lux' ,type => 'Dimmable light' ,subType => 'dimmer',
  61. icon => 'hue_filled_white_and_color_e27_b22', },
  62. LWB006 => {name => 'Hue Lux' ,type => 'Dimmable light' ,subType => 'dimmer',
  63. icon => 'hue_filled_white_and_color_e27_b22', },
  64. LWB007 => {name => 'Hue Lux' ,type => 'Dimmable light' ,subType => 'dimmer',
  65. icon => 'hue_filled_white_and_color_e27_b22', },
  66. LWB010 => {name => 'Hue Lux' ,type => 'Dimmable light' ,subType => 'dimmer',
  67. icon => 'hue_filled_white_and_color_e27_b22', },
  68. LWB014 => {name => 'Hue Lux' ,type => 'Dimmable light' ,subType => 'dimmer',
  69. icon => 'hue_filled_white_and_color_e27_b22', },
  70. LTW001 => {name => 'Hue A19 White Ambience' ,type => 'Color temperature light' ,subType => 'ctdimmer',
  71. icon => 'hue_filled_white_and_color_e27_b22', },
  72. LTW004 => {name => 'Hue A19 White Ambience' ,type => 'Color temperature light' ,subType => 'ctdimmer', },
  73. LTW012 => {name => 'Hue ambiance candle' ,type => 'Color temperature light' ,subType => 'ctdimmer',
  74. icon => 'hue_filled_gu10_par16', },
  75. LTW013 => {name => 'Hue GU10 White Ambience' ,type => 'Color temperature light' ,subType => 'ctdimmer',
  76. icon => 'hue_filled_gu10_par16', },
  77. LTW014 => {name => 'Hue GU10 White Ambience' ,type => 'Color temperature light' ,subType => 'ctdimmer',
  78. icon => 'hue_filled_gu10_par16', },
  79. LLM001 => {name => 'Color Light Module' ,type => 'Extended color light' ,subType => 'extcolordimmer',
  80. gamut => 'B', },
  81. LLM010 => {name => 'Color Temperature Module' ,type => 'Color temperature light' ,subType => 'ctdimmer', },
  82. LLM011 => {name => 'Color Temperature Module' ,type => 'Color temperature light' ,subType => 'ctdimmer', },
  83. LLM012 => {name => 'Color Temperature Module' ,type => 'Color temperature light' ,subType => 'ctdimmer', },
  84. LWL001 => {name => 'LivingWhites Outlet' ,type => 'Dimmable plug-in unit' ,subType => 'dimmer',
  85. icon => 'hue_filled_outlet', },
  86. RWL020 => {name => 'Hue Dimmer Switch' ,type => 'ZLLSwitch' ,subType => 'sensor',
  87. icon => 'hue_filled_hds', },
  88. RWL021 => {name => 'Hue Dimmer Switch' ,type => 'ZLLSwitch' ,subType => 'sensor',
  89. icon => 'hue_filled_hds', },
  90. ZGPSWITCH => {name => 'Hue Tap' ,type => 'ZGPSwitch' ,subType => 'sensor',
  91. icon => 'hue_filled_tap', },
  92. 'FLS-H3' => {name => 'dresden elektronik FLS-H lp' ,type => 'Color temperature light' ,subType => 'ctdimmer',},
  93. 'FLS-PP3' => {name => 'dresden elektronik FLS-PP lp' ,type => 'Extended color light' ,subType => 'extcolordimmer', },
  94. 'Flex RGBW' => {name => 'LIGHTIFY Flex RGBW' ,type => 'Extended color light' ,subType => 'extcolordimmer', },
  95. 'Classic A60 RGBW' => {name => 'LIGHTIFY Classic A60 RGBW' ,type => 'Extended color light' ,subType => 'extcolordimmer', },
  96. 'Gardenspot RGB' => {name => 'LIGHTIFY Gardenspot Mini RGB' ,type => 'Color light' ,subType => 'colordimmer', },
  97. 'Surface Light TW' => {name => 'LIGHTIFY Surface light tunable white' ,type => 'Color temperature light' ,subType => 'ctdimmer', },
  98. 'Classic A60 TW' => {name => 'LIGHTIFY Classic A60 tunable white' ,type => 'Color temperature light' ,subType => 'ctdimmer', },
  99. 'Classic B40 TW' => {name => 'LIGHTIFY Classic B40 tunable white' ,type => 'Color temperature light' ,subType => 'ctdimmer', },
  100. 'PAR16 50 TW' => {name => 'LIGHTIFY PAR16 50 tunable white' ,type => 'Color temperature light' ,subType => 'ctdimmer', },
  101. 'Classic A60' => {name => 'LIGHTIFY Classic A60 dimmable light' ,type => 'Dimmable Light' ,subType => 'dimmer', },
  102. 'Plug - LIGHTIFY' => {name => 'LIGHTIFY Plug' ,type => 'On/Off plug-in unit ' ,subType => 'switch', },
  103. 'Plug 01' => {name => 'LIGHTIFY Plug' ,type => 'On/Off plug-in unit ' ,subType => 'switch', },
  104. 'RM01' => {name => 'Busch-Jaeger ZigBee Light Link Relais', type => 'On/Off light' ,subType => 'switch', },
  105. 'DM01' => {name => 'Busch-Jaeger ZigBee Light Link Dimmer', type => 'Dimmable light' ,subType => 'dimmer', },
  106. );
  107. my %gamut = (
  108. A => { r => { hue => 0, x => 0.704, y => 0.296 },
  109. g => { hue => 100, x => 0.2151, y => 0.7106 },
  110. b => { hue => 184, x => 0.138, y => 0.08 }, },
  111. B => { r => { hue => 0, x => 0.675, y => 0.322 },
  112. g => { hue => 100, x => 0.409, y => 0.518 },
  113. b => { hue => 184, x => 0.167, y => 0.04 }, },
  114. C => { r => { hue => 0, x => 0.692, y => 0.308 },
  115. g => { hue => 100, x => 0.17, y => 0.7 },
  116. b => { hue => 184, x => 0.153, y => 0.048 }, },
  117. );
  118. my %dim_values = (
  119. 0 => "dim06%",
  120. 1 => "dim12%",
  121. 2 => "dim18%",
  122. 3 => "dim25%",
  123. 4 => "dim31%",
  124. 5 => "dim37%",
  125. 6 => "dim43%",
  126. 7 => "dim50%",
  127. 8 => "dim56%",
  128. 9 => "dim62%",
  129. 10 => "dim68%",
  130. 11 => "dim75%",
  131. 12 => "dim81%",
  132. 13 => "dim87%",
  133. 14 => "dim93%",
  134. );
  135. my $HUEDevice_hasDataDumper = 1;
  136. sub HUEDevice_Initialize($)
  137. {
  138. my ($hash) = @_;
  139. # Provide
  140. #Consumer
  141. $hash->{DefFn} = "HUEDevice_Define";
  142. $hash->{UndefFn} = "HUEDevice_Undefine";
  143. $hash->{SetFn} = "HUEDevice_Set";
  144. $hash->{GetFn} = "HUEDevice_Get";
  145. $hash->{AttrFn} = "HUEDevice_Attr";
  146. $hash->{AttrList} = "IODev ".
  147. "delayedUpdate:1 ".
  148. "ignoreReachable:1,0 ".
  149. "realtimePicker:1,0 ".
  150. "color-icons:1,2 ".
  151. "transitiontime ".
  152. "model:".join(",", sort map { $_ =~ s/ /#/g ;$_} keys %hueModels)." ".
  153. "setList:textField-long ".
  154. "subType:extcolordimmer,colordimmer,ctdimmer,dimmer,switch ".
  155. $readingFnAttributes;
  156. #$hash->{FW_summaryFn} = "HUEDevice_summaryFn";
  157. FHEM_colorpickerInit();
  158. eval "use Data::Dumper";
  159. $HUEDevice_hasDataDumper = 0 if($@);
  160. }
  161. sub
  162. HUEDevice_devStateIcon($)
  163. {
  164. my($hash) = @_;
  165. $hash = $defs{$hash} if( ref($hash) ne 'HASH' );
  166. return undef if( !$hash );
  167. my $name = $hash->{NAME};
  168. if( $hash->{helper}->{devtype} && $hash->{helper}->{devtype} eq 'G' ) {
  169. if( $hash->{IODev} ) {
  170. my $createGroupReadings = AttrVal($hash->{IODev}{NAME},"createGroupReadings",undef);
  171. if( defined($createGroupReadings) ) {
  172. return undef if( $createGroupReadings && !AttrVal($hash->{NAME},"createGroupReadings", 1) );
  173. return undef if( !$createGroupReadings && !AttrVal($hash->{NAME},"createGroupReadings", undef) );
  174. return ".*:light_question:toggle" if( !$hash->{helper}{reachable} );
  175. return ".*:off:toggle" if( ReadingsVal($name,"onoff","0") eq "0" );
  176. my $pct = ReadingsVal($name,"pct","100");
  177. my $s = $dim_values{int($pct/7)};
  178. $s="on" if( $pct eq "100" );
  179. return ".*:$s:toggle";
  180. }
  181. }
  182. #return ".*:off:toggle" if( !ReadingsVal($name,'any_on',0) );
  183. #return ".*:on:toggle" if( ReadingsVal($name,'any_on',0) );
  184. return undef;
  185. }
  186. return undef if( $hash->{helper}->{devtype} );
  187. return ".*:light_question:toggle" if( !$hash->{helper}{reachable} );
  188. return ".*:off:toggle" if( ReadingsVal($name,"state","off") eq "off" );
  189. my $pct = ReadingsVal($name,"pct","100");
  190. my $s = $dim_values{int($pct/7)};
  191. $s="on" if( $pct eq "100" );
  192. return ".*:$s:toggle" if( AttrVal($name, "model", "") eq "LWL001" );
  193. return ".*:$s:toggle" if( AttrVal($name, "subType", "") eq "dimmer" );
  194. #return ".*:$s:toggle" if( AttrVal($name, "model", "") eq "LWB001" );
  195. #return ".*:$s:toggle" if( AttrVal($name, "model", "") eq "LWB003" );
  196. #return ".*:$s:toggle" if( AttrVal($name, "model", "") eq "LWB004" );
  197. return ".*:$s@#".CommandGet("","$name RGB").":toggle" if( $pct < 100 && AttrVal($name, "color-icons", 0) == 2 );
  198. return ".*:on@#".CommandGet("","$name rgb").":toggle" if( AttrVal($name, "color-icons", 0) != 0 );
  199. return '<div style="width:32px;height:19px;'.
  200. 'border:1px solid #fff;border-radius:8px;background-color:#'.CommandGet("","$name rgb").';"></div>';
  201. }
  202. sub
  203. HUEDevice_summaryFn($$$$)
  204. {
  205. my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
  206. my $hash = $defs{$d};
  207. my $name = $hash->{NAME};
  208. return HUEDevice_devStateIcon($hash);
  209. }
  210. sub HUEDevice_Define($$)
  211. {
  212. my ($hash, $def) = @_;
  213. my @args = split("[ \t]+", $def);
  214. $hash->{helper}->{devtype} = "";
  215. if( $args[2] eq "group" ) {
  216. $hash->{helper}->{devtype} = "G";
  217. splice( @args, 2, 1 );
  218. } elsif( $args[2] eq "sensor" ) {
  219. $hash->{helper}->{devtype} = "S";
  220. splice( @args, 2, 1 );
  221. }
  222. my $iodev;
  223. my $i = 0;
  224. foreach my $param ( @args ) {
  225. if( $param =~ m/IODev=([^\s]*)/ ) {
  226. $iodev = $1;
  227. splice( @args, $i, 1 );
  228. last;
  229. }
  230. $i++;
  231. }
  232. return "Usage: define <name> HUEDevice [group|sensor] <id> [interval]" if(@args < 3);
  233. my ($name, $type, $id, $interval) = @args;
  234. $hash->{STATE} = 'Initialized';
  235. $hash->{ID} = $hash->{helper}->{devtype}.$id;
  236. AssignIoPort($hash,$iodev) if( !$hash->{IODev} );
  237. if(defined($hash->{IODev})) {
  238. Log3 $name, 3, "$name: I/O device is " . $hash->{IODev}->{NAME};
  239. } else {
  240. Log3 $name, 1, "$name: no I/O device";
  241. }
  242. $iodev = $hash->{IODev}->{NAME} if( defined($hash->{IODev}) );
  243. my $code = $hash->{ID};
  244. $code = $iodev ."-". $code if( defined($iodev) );
  245. my $d = $modules{HUEDevice}{defptr}{$code};
  246. return "HUEDevice device $hash->{ID} on HUEBridge $iodev already defined as $d->{NAME}."
  247. if( defined($d)
  248. && $d->{IODev} && $hash->{IODev} && $d->{IODev} == $hash->{IODev}
  249. && $d->{NAME} ne $name );
  250. $modules{HUEDevice}{defptr}{$code} = $hash;
  251. if( AttrVal($iodev, "pollDevices", 1) ) {
  252. $interval = undef unless defined($interval);
  253. } elsif( !$hash->{helper}->{devtype} || $hash->{helper}->{devtype} ne 'G' ) {
  254. $interval = 60 unless defined($interval);
  255. }
  256. $args[3] = "" if( !defined( $args[3] ) );
  257. if( !$hash->{helper}->{devtype} ) {
  258. $hash->{DEF} = "$id $args[3] IODev=$iodev" if( $iodev );
  259. $interval = 60 if( defined($interval) && $interval < 10 );
  260. $hash->{INTERVAL} = $interval;
  261. $hash->{helper}{on} = -1;
  262. $hash->{helper}{reachable} = undef;
  263. $hash->{helper}{colormode} = '';
  264. $hash->{helper}{bri} = -1;
  265. $hash->{helper}{ct} = -1;
  266. $hash->{helper}{hue} = -1;
  267. $hash->{helper}{sat} = -1;
  268. $hash->{helper}{xy} = '';
  269. $hash->{helper}{alert} = '';
  270. $hash->{helper}{effect} = '';
  271. $hash->{helper}{pct} = -1;
  272. $hash->{helper}{rgb} = "";
  273. $attr{$name}{devStateIcon} = '{(HUEDevice_devStateIcon($name),"toggle")}' if( !defined( $attr{$name}{devStateIcon} ) );
  274. my $icon_path = AttrVal("WEB", "iconPath", "default:fhemSVG:openautomation" );
  275. $attr{$name}{'color-icons'} = 2 if( !defined( $attr{$name}{'color-icons'} ) && $icon_path =~ m/openautomation/ );
  276. } elsif( $hash->{helper}->{devtype} eq 'G' ) {
  277. $hash->{DEF} = "group $id $args[3] IODev=$iodev" if( $iodev );
  278. $interval = 60 if( defined($interval) && $interval < 10 );
  279. $hash->{INTERVAL} = $interval;
  280. $hash->{helper}{all_on} = -1;
  281. $hash->{helper}{any_on} = -1;
  282. $attr{$name}{delayedUpdate} = 1 if( !defined( $attr{$name}{delayedUpdate} ) );
  283. $attr{$name}{devStateIcon} = '{(HUEDevice_devStateIcon($name),"toggle")}' if( !defined( $attr{$name}{devStateIcon} ) );
  284. my $icon_path = AttrVal("WEB", "iconPath", "default:fhemSVG:openautomation" );
  285. $attr{$name}{'color-icons'} = 2 if( !defined( $attr{$name}{'color-icons'} ) && $icon_path =~ m/openautomation/ );
  286. addToDevAttrList($name, "createActionReadings:1,0");
  287. addToDevAttrList($name, "createGroupReadings:1,0");
  288. } elsif( $hash->{helper}->{devtype} eq 'S' ) {
  289. $hash->{DEF} = "sensor $id $args[3] IODev=$iodev" if( $iodev );
  290. $interval = 60 if( defined($interval) && $interval < 1 );
  291. $hash->{INTERVAL} = $interval;
  292. }
  293. RemoveInternalTimer($hash);
  294. if( $init_done ) {
  295. HUEDevice_GetUpdate($hash);
  296. } else {
  297. InternalTimer(gettimeofday()+10, "HUEDevice_GetUpdate", $hash, 0);
  298. }
  299. return undef;
  300. }
  301. sub HUEDevice_Undefine($$)
  302. {
  303. my ($hash,$arg) = @_;
  304. RemoveInternalTimer($hash);
  305. my $code = $hash->{ID};
  306. $code = $hash->{IODev}->{NAME} ."-". $code if( defined($hash->{IODev}->{NAME}) );
  307. delete($modules{HUEDevice}{defptr}{$code});
  308. return undef;
  309. }
  310. sub
  311. HUEDevice_SetParam($$@)
  312. {
  313. my ($name, $obj, $cmd, $value, $value2) = @_;
  314. if( $cmd eq "color" ) {
  315. $value = int(1000000/$value);
  316. $cmd = 'ct';
  317. } elsif( $name && $cmd eq "toggle" ) {
  318. $cmd = ReadingsVal($name,"onoff",1) ? "off" :"on";
  319. } elsif( $cmd =~ m/^dim(\d+)/ ) {
  320. $value2 = $value;
  321. $value = $1;
  322. $value = 0 if( $value < 0 );
  323. $value = 100 if( $value > 100 );
  324. $cmd = 'pct';
  325. } elsif( !defined($value) && $cmd =~ m/^(\d+)/) {
  326. $value2 = $value;
  327. $value = $1;
  328. $value = 0 if( $value < 0 );
  329. $value = 254 if( $value > 254 );
  330. $cmd = 'bri';
  331. }
  332. if($cmd eq "pct" && $value == 0 ) {
  333. $cmd = "off";
  334. $value = $value2;
  335. }
  336. if($cmd eq 'on') {
  337. $obj->{'on'} = JSON::true;
  338. # temporary disablea for everything. hast do be disabled for groups.
  339. # see https://forum.fhem.de/index.php/topic,11020.msg497825.html#msg497825
  340. #$obj->{'bri'} = 254 if( $name && ReadingsVal($name,"bri","0") eq 0 && AttrVal($name, 'subType', 'dimmer') ne 'switch' );
  341. $obj->{'transitiontime'} = $value * 10 if( defined($value) );
  342. } elsif($cmd eq 'off') {
  343. $obj->{'on'} = JSON::false;
  344. $obj->{'transitiontime'} = $value * 10 if( defined($value) );
  345. } elsif($cmd eq "pct") {
  346. my $bri;
  347. if( $value > 50 ) {
  348. $bri = 2.57 * ($value-50) + 128;
  349. } else {
  350. $bri = 2.59 * ($value-50) + 128;
  351. }
  352. $bri = 0 if( $bri < 0 );
  353. $bri = 254 if( $bri > 254 );
  354. #$value = 3.5 if( $value < 3.5 && AttrVal($name, "model", "") eq "LWL001" );
  355. $obj->{'on'} = JSON::true;
  356. #$obj->{'bri'} = int(2.55 * $value);
  357. $obj->{'bri'} = int($bri);
  358. $obj->{'transitiontime'} = $value2 * 10 if( defined($value2) );
  359. } elsif($cmd eq "bri") {
  360. #$value = 8 if( $value < 8 && AttrVal($name, "model", "") eq "LWL001" );
  361. $obj->{'on'} = JSON::true;
  362. $obj->{'bri'} = 0+$value;
  363. $obj->{'transitiontime'} = $value2 * 10 if( defined($value2) );
  364. } elsif($name && $cmd eq "dimUp") {
  365. if( $defs{$name}->{IODev}->{helper}{apiversion} && $defs{$name}->{IODev}->{helper}{apiversion} >= (1<<16) + (7<<8) ) {
  366. $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} );
  367. $obj->{'bri_inc'} = 25;
  368. $obj->{'bri_inc'} = 0+$value if( defined($value) );
  369. #$obj->{'transitiontime'} = 1;
  370. #$defs{$name}->{helper}->{update_timeout} = 0;
  371. } else {
  372. my $bri = ReadingsVal($name,"bri","0");
  373. $bri += 25;
  374. $bri = 254 if( $bri > 254 );
  375. $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} );
  376. $obj->{'bri'} = 0+$bri;
  377. $obj->{'transitiontime'} = 1;
  378. #$obj->{'transitiontime'} = $value * 10 if( defined($value) );
  379. $defs{$name}->{helper}->{update_timeout} = 0;
  380. }
  381. } elsif($name && $cmd eq "dimDown") {
  382. if( $defs{$name}->{IODev}->{helper}{apiversion} && $defs{$name}->{IODev}->{helper}{apiversion} >= (1<<16) + (7<<8) ) {
  383. $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} );
  384. $obj->{'bri_inc'} = -25;
  385. $obj->{'bri_inc'} = 0-$value if( defined($value) );
  386. #$obj->{'transitiontime'} = 1;
  387. #$defs{$name}->{helper}->{update_timeout} = 0;
  388. } else {
  389. my $bri = ReadingsVal($name,"bri","0");
  390. $bri -= 25;
  391. $bri = 0 if( $bri < 0 );
  392. $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} );
  393. $obj->{'bri'} = 0+$bri;
  394. $obj->{'transitiontime'} = 1;
  395. #$obj->{'transitiontime'} = $value * 10 if( defined($value) );
  396. $defs{$name}->{helper}->{update_timeout} = 0;
  397. }
  398. } elsif($cmd eq "satUp") {
  399. $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} );
  400. $obj->{'sat_inc'} = 25;
  401. $obj->{'sat_inc'} = 0+$value if( defined($value) );
  402. } elsif($cmd eq "satDown") {
  403. $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} );
  404. $obj->{'sat_inc'} = -25;
  405. $obj->{'sat_inc'} = 0+$value if( defined($value) );
  406. } elsif($cmd eq "hueUp") {
  407. $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} );
  408. $obj->{'hue_inc'} = 6553;
  409. $obj->{'hue_inc'} = 0+$value if( defined($value) );
  410. } elsif($cmd eq "hueDown") {
  411. $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} );
  412. $obj->{'hue_inc'} = -6553;
  413. $obj->{'hue_inc'} = 0+$value if( defined($value) );
  414. } elsif($cmd eq "ctUp") {
  415. $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} );
  416. $obj->{'ct_inc'} = 16;
  417. $obj->{'ct_inc'} = 0+$value if( defined($value) );
  418. } elsif($cmd eq "ctDown") {
  419. $obj->{'on'} = JSON::true if( !$defs{$name}->{helper}{on} );
  420. $obj->{'ct_inc'} = -16;
  421. $obj->{'ct_inc'} = 0+$value if( defined($value) );
  422. } elsif($cmd eq "ct") {
  423. $obj->{'on'} = JSON::true;
  424. $value = int(1000000/$value) if( $value > 1000 );
  425. $obj->{'ct'} = 0+$value;
  426. $obj->{'transitiontime'} = $value2 * 10 if( defined($value2) );
  427. } elsif($cmd eq "hue") {
  428. $obj->{'on'} = JSON::true;
  429. $obj->{'hue'} = 0+$value;
  430. $obj->{'transitiontime'} = $value2 * 10 if( defined($value2) );
  431. } elsif($cmd eq "sat") {
  432. $obj->{'on'} = JSON::true;
  433. $obj->{'sat'} = 0+$value;
  434. $obj->{'transitiontime'} = $value2 * 10 if( defined($value2) );
  435. } elsif($cmd eq "xy" && $value =~ m/^(.+),(.+)/) {
  436. my ($x,$y) = ($1, $2);
  437. $obj->{'on'} = JSON::true;
  438. $obj->{'xy'} = [0+$x, 0+$y];
  439. $obj->{'transitiontime'} = $value2 * 10 if( defined($value2) );
  440. } elsif( $cmd eq "rgb" && $value =~ m/^(..)(..)(..)/) {
  441. my( $r, $g, $b ) = (hex($1)/255.0, hex($2)/255.0, hex($3)/255.0);
  442. if( $name && ( !defined( AttrVal($name, "model", undef) )
  443. || AttrVal($name, "model", undef) eq 'LLC020') ) {
  444. my( $h, $s, $v ) = Color::rgb2hsv($r,$g,$b);
  445. $obj->{'on'} = JSON::true;
  446. $obj->{'hue'} = int( $h * 65535 );
  447. $obj->{'sat'} = int( $s * 254 );
  448. $obj->{'bri'} = int( $v * 254 );
  449. } else {
  450. # calculation from http://www.everyhue.com/vanilla/discussion/94/rgb-to-xy-or-hue-sat-values/p1
  451. my $X = 1.076450 * $r - 0.237662 * $g + 0.161212 * $b;
  452. my $Y = 0.410964 * $r + 0.554342 * $g + 0.034694 * $b;
  453. my $Z = -0.010954 * $r - 0.013389 * $g + 1.024343 * $b;
  454. #Log3 $name, 3, "rgb: ". $r . " " . $g ." ". $b;
  455. #Log3 $name, 3, "XYZ: ". $X . " " . $Y ." ". $Y;
  456. if( $X != 0
  457. || $Y != 0
  458. || $Z != 0 ) {
  459. my $x = $X / ($X + $Y + $Z);
  460. my $y = $Y / ($X + $Y + $Z);
  461. #Log3 $name, 3, "xyY:". $x . " " . $y ." ". $Y;
  462. $Y = 1 if( $Y > 1 );
  463. $x = 0 if( $x < 0);
  464. $x = 1 if( $x > 1);
  465. $y = 0 if( $y < 0);
  466. $y = 1 if( $y > 1);
  467. my $bri = maxNum($r,$g,$b);
  468. #my $bri = $Y;
  469. $obj->{'on'} = JSON::true;
  470. $obj->{'xy'} = [0+$x, 0+$y];
  471. $obj->{'bri'} = int(254*$bri);
  472. } else {
  473. $obj->{'on'} = JSON::false;
  474. }
  475. }
  476. } elsif( $cmd eq "hsv" && $value =~ m/^(..)(..)(..)/) {
  477. my( $h, $s, $v ) = (hex($1), hex($2), hex($3));
  478. $s = 254 if( $s > 254 );
  479. $v = 254 if( $v > 254 );
  480. $obj->{'on'} = JSON::true;
  481. $obj->{'hue'} = int($h*256);
  482. $obj->{'sat'} = 0+$s;
  483. $obj->{'bri'} = 0+$v;
  484. } elsif( $cmd eq "alert" ) {
  485. $obj->{'alert'} = $value;
  486. } elsif( $cmd eq "effect" ) {
  487. $obj->{'effect'} = $value;
  488. } elsif( $cmd eq "transitiontime" ) {
  489. $obj->{'transitiontime'} = 0+$value;
  490. } elsif( $name && $cmd eq "delayedUpdate" ) {
  491. $defs{$name}->{helper}->{update_timeout} = 1;
  492. } elsif( $name && $cmd eq "immediateUpdate" ) {
  493. $defs{$name}->{helper}->{update_timeout} = 0;
  494. } elsif( $name && $cmd eq "noUpdate" ) {
  495. $defs{$name}->{helper}->{update_timeout} = -1;
  496. } else {
  497. return 0;
  498. }
  499. return 1;
  500. }
  501. sub HUEDevice_Set($@);
  502. sub
  503. HUEDevice_Set($@)
  504. {
  505. my ($hash, $name, @aa) = @_;
  506. my ($cmd, @args) = @aa;
  507. my %obj;
  508. $hash->{helper}->{update_timeout} = AttrVal($name, "delayedUpdate", 1);
  509. if( $hash->{helper}->{devtype} eq 'G' ) {
  510. if( $cmd eq 'lights' ) {
  511. return "usage: lights <lights>" if( @args != 1 );
  512. my $obj = { 'lights' => HUEBridge_string2array($args[0]), };
  513. my $result = HUEDevice_ReadFromServer($hash,$hash->{ID},$obj);
  514. if( $result->{success} ) {
  515. RemoveInternalTimer($hash);
  516. HUEDevice_GetUpdate($hash);
  517. }
  518. return $result->{error}{description} if( $result->{error} );
  519. return undef;
  520. } elsif( $cmd eq 'savescene' ) {
  521. if( $defs{$name}->{IODev}->{helper}{apiversion} && $defs{$name}->{IODev}->{helper}{apiversion} >= (1<<16) + (11<<8) ) {
  522. return "usage: savescene <name>" if( @args < 1 );
  523. return fhem( "set $hash->{IODev}{NAME} savescene ". join( ' ', @aa[1..@aa-1]). " $hash->{NAME}" );
  524. } else {
  525. return "usage: savescene <id>" if( @args != 1 );
  526. return fhem( "set $hash->{IODev}{NAME} savescene $aa[1] $aa[1] $hash->{NAME}" );
  527. }
  528. } elsif( $cmd eq 'deletescene' ) {
  529. return "usage: deletescene <id>" if( @args != 1 );
  530. return fhem( "set $hash->{IODev}{NAME} deletescene $aa[1]" );
  531. } elsif( $cmd eq 'scene' ) {
  532. return "usage: scene <id>" if( @args != 1 );
  533. my $obj = {'scene' => $aa[1]};
  534. $hash->{helper}->{update} = 1;
  535. my $result = HUEDevice_ReadFromServer($hash,"$hash->{ID}/action",$obj);
  536. return $result->{error}{description} if( $result->{error} );
  537. if( defined($result) && $result->{'error'} ) {
  538. $hash->{STATE} = $result->{'error'}->{'description'};
  539. return undef;
  540. }
  541. return undef if( !defined($result) );
  542. if( $hash->{helper}->{update_timeout} == -1 ) {
  543. } elsif( $hash->{helper}->{update_timeout} ) {
  544. RemoveInternalTimer($hash);
  545. InternalTimer(gettimeofday()+$hash->{helper}->{update_timeout}, "HUEDevice_GetUpdate", $hash, 0);
  546. } else {
  547. RemoveInternalTimer($hash);
  548. HUEDevice_GetUpdate( $hash );
  549. }
  550. return undef;
  551. }
  552. } elsif( $hash->{helper}->{devtype} eq 'S' ) {
  553. my $shash = $defs{$name}->{IODev};
  554. my $id = $hash->{ID};
  555. $id = $1 if( $id =~ m/^S(\d.*)/ );
  556. $hash->{".triggerUsed"} = 1;
  557. if( $cmd eq "statusRequest" ) {
  558. RemoveInternalTimer($hash);
  559. HUEDevice_GetUpdate($hash);
  560. return undef;
  561. } elsif( $cmd eq 'json' ) {
  562. return HUEBridge_Set( $shash, $shash->{NAME}, 'setsensor', $id, @args );
  563. return undef;
  564. } elsif( my @match = grep { $cmd eq $_ } keys %{($hash->{helper}{setList}{cmds}?$hash->{helper}{setList}{cmds}:{})} ) {
  565. return HUEBridge_Set( $shash, $shash->{NAME}, 'setsensor', $id, $hash->{helper}{setList}{cmds}{$match[0]} );
  566. } elsif( my $entries = $hash->{helper}{setList}{regex} ) {
  567. foreach my $entry (@{$entries}) {
  568. if( join(' ', @aa) =~ /$entry->{regex}/ ) {
  569. my $VALUE1 = $1;
  570. my $VALUE2 = $2;
  571. my $VALUE3 = $3;
  572. my $json = $entry->{json};
  573. $json =~ s/\$1/$VALUE1/;
  574. $json =~ s/\$2/$VALUE2/;
  575. $json =~ s/\$3/$VALUE3/;
  576. return HUEBridge_Set( $shash, $shash->{NAME}, 'setsensor', $id, $json );
  577. }
  578. }
  579. }
  580. my $list = 'statusRequest:noArg';
  581. $list .= ' json' if( $hash->{type} && $hash->{type} =~ /^CLIP/ );
  582. $list .= ' '. join( ':noArg ', keys %{$hash->{helper}{setList}{cmds}} ) if( $hash->{helper}{setList}{cmds} );
  583. $list .= ':noArg' if( $hash->{helper}{setList}{cmds} );
  584. if( my $entries = $hash->{helper}{setList}{regex} ) {
  585. foreach my $entry (@{$entries}) {
  586. $list .= ' ';
  587. $list .= (split( ' ', $entry->{regex} ))[0];
  588. }
  589. }
  590. return SetExtensions($hash, $list, $name, @aa);
  591. }
  592. if( $cmd eq 'rename' ) {
  593. my $new_name = join( ' ', @aa[1..@aa-1]);
  594. my $obj = { 'name' => $new_name, };
  595. my $result = HUEDevice_ReadFromServer($hash,$hash->{ID},$obj);
  596. if( $result->{success} ) {
  597. RemoveInternalTimer($hash);
  598. HUEDevice_GetUpdate($hash);
  599. CommandAttr(undef,"$name alias $new_name");
  600. CommandSave(undef,undef) if( AttrVal( "autocreate", "autosave", 1 ) );
  601. }
  602. return $result->{error}{description} if( $result->{error} );
  603. return undef;
  604. }
  605. if( (my $joined = join(" ", @aa)) =~ /:/ ) {
  606. $joined =~ s/on-till\s+[^\s]+//g; #bad workaround for: https://forum.fhem.de/index.php/topic,61636.msg728557.html#msg728557
  607. my @cmds = split(":", $joined);
  608. for( my $i = 0; $i <= $#cmds; ++$i ) {
  609. HUEDevice_SetParam($name, \%obj, split(" ", $cmds[$i]) );
  610. }
  611. } else {
  612. my ($cmd, $value, $value2, @a) = @aa;
  613. if( $cmd eq "statusRequest" ) {
  614. RemoveInternalTimer($hash);
  615. HUEDevice_GetUpdate($hash);
  616. return undef;
  617. }
  618. HUEDevice_SetParam($name, \%obj, $cmd, $value, $value2);
  619. }
  620. if( %obj ) {
  621. if( defined($obj{on}) ) {
  622. $hash->{desired} = $obj{on}?1:0;
  623. }
  624. if( !defined($obj{transitiontime}) ) {
  625. my $transitiontime = AttrVal($name, "transitiontime", undef);
  626. $obj{transitiontime} = 0 + $transitiontime if( defined( $transitiontime ) );
  627. }
  628. }
  629. # if( $hash->{helper}->{update_timeout} == -1 ) {
  630. # my $diff;
  631. # my ($seconds, $microseconds) = gettimeofday();
  632. # if( $hash->{helper}->{timestamp} ) {
  633. # my ($seconds2, $microseconds2) = @{$hash->{helper}->{timestamp}};
  634. #
  635. # $diff = (($seconds-$seconds2)*1000000 + $microseconds-$microseconds2)/1000;
  636. # }
  637. # $hash->{helper}->{timestamp} = [$seconds, $microseconds];
  638. #
  639. # return undef if( $diff < 100 );
  640. # }
  641. if( scalar keys %obj ) {
  642. my $result;
  643. if( $hash->{helper}->{devtype} eq 'G' ) {
  644. $hash->{helper}->{update} = 1;
  645. $result = HUEDevice_ReadFromServer($hash,"$hash->{ID}/action",\%obj);
  646. } else {
  647. $result = HUEDevice_ReadFromServer($hash,"$hash->{ID}/state",\%obj);
  648. }
  649. SetExtensionsCancel($hash);
  650. if( defined($result) && $result->{'error'} ) {
  651. $hash->{STATE} = $result->{'error'}->{'description'};
  652. return undef;
  653. }
  654. $hash->{".triggerUsed"} = 1;
  655. return undef if( !defined($result) );
  656. if( $hash->{helper}->{update_timeout} == -1 ) {
  657. } elsif( $hash->{helper}->{update_timeout} ) {
  658. RemoveInternalTimer($hash);
  659. InternalTimer(gettimeofday()+$hash->{helper}->{update_timeout}, "HUEDevice_GetUpdate", $hash, 0);
  660. } else {
  661. RemoveInternalTimer($hash);
  662. HUEDevice_GetUpdate( $hash );
  663. }
  664. return undef;
  665. }
  666. my $subtype = AttrVal($name, "subType", "extcolordimmer");
  667. my $list = "off:noArg on:noArg toggle:noArg statusRequest:noArg";
  668. $list .= " pct:colorpicker,BRI,0,1,100 bri:colorpicker,BRI,0,1,254" if( $subtype =~ m/dimmer/ );
  669. $list .= " rgb:colorpicker,RGB" if( $subtype =~ m/color/ );
  670. $list .= " color:colorpicker,CT,2000,1,6500 ct:colorpicker,CT,154,1,500" if( $subtype =~ m/ct|ext/ );
  671. $list .= " hue:colorpicker,HUE,0,1,65535 sat:slider,0,1,254 xy effect:none,colorloop" if( $subtype =~ m/color/ );
  672. if( $defs{$name}->{IODev}->{helper}{apiversion} && $defs{$name}->{IODev}->{helper}{apiversion} >= (1<<16) + (7<<8) ) {
  673. $list .= " dimUp:noArg dimDown:noArg" if( $subtype =~ m/dimmer/ );
  674. $list .= " ctUp:noArg ctDown:noArg" if( $subtype =~ m/ct|ext/ );
  675. $list .= " hueUp:noArg hueDown:noArg satUp:noArg satDown:noArg" if( $subtype =~ m/color/ );
  676. } elsif( !$hash->{helper}->{devtype} && $subtype =~ m/dimmer/ ) {
  677. $list .= " dimUp:noArg dimDown:noArg";
  678. }
  679. $list .= " alert:none,select,lselect";
  680. #$list .= " dim06% dim12% dim18% dim25% dim31% dim37% dim43% dim50% dim56% dim62% dim68% dim75% dim81% dim87% dim93% dim100%" if( $subtype =~ m/dimmer/ );
  681. $list .= " lights" if( $hash->{helper}->{devtype} eq 'G' );
  682. $list .= " savescene deletescene scene" if( $hash->{helper}->{devtype} eq 'G' );
  683. $list .= " rename";
  684. return SetExtensions($hash, $list, $name, @aa);
  685. }
  686. sub
  687. cttorgb($)
  688. {
  689. my ($ct) = @_;
  690. # calculation from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code
  691. # adjusted by 1000K
  692. my $temp = (1000000/$ct)/100 + 10;
  693. my $r = 0;
  694. my $g = 0;
  695. my $b = 0;
  696. $r = 255;
  697. $r = 329.698727446 * ($temp - 60) ** -0.1332047592 if( $temp > 66 );
  698. $r = 0 if( $r < 0 );
  699. $r = 255 if( $r > 255 );
  700. if( $temp <= 66 ) {
  701. $g = 99.4708025861 * log($temp) - 161.1195681661;
  702. } else {
  703. $g = 288.1221695283 * ($temp - 60) ** -0.0755148492;
  704. }
  705. $g = 0 if( $g < 0 );
  706. $g = 255 if( $g > 255 );
  707. $b = 255;
  708. $b = 0 if( $temp <= 19 );
  709. if( $temp < 66 ) {
  710. $b = 138.5177312231 * log($temp-10) - 305.0447927307;
  711. }
  712. $b = 0 if( $b < 0 );
  713. $b = 255 if( $b > 255 );
  714. return( $r, $g, $b );
  715. }
  716. sub
  717. xyYtorgb($$$)
  718. {
  719. # calculation from http://www.brucelindbloom.com/index.html
  720. my ($x,$y,$Y) = @_;
  721. #Log 3, "xyY:". $x . " " . $y ." ". $Y;
  722. my $r = 0;
  723. my $g = 0;
  724. my $b = 0;
  725. if( $y > 0 ) {
  726. my $X = $x * $Y / $y;
  727. my $Z = (1 - $x - $y)*$Y / $y;
  728. if( $X > 1
  729. || $Y > 1
  730. || $Z > 1 ) {
  731. my $f = maxNum($X,$Y,$Z);
  732. $X /= $f;
  733. $Y /= $f;
  734. $Z /= $f;
  735. }
  736. #Log 3, "XYZ: ". $X . " " . $Y ." ". $Y;
  737. $r = 0.7982 * $X + 0.3389 * $Y - 0.1371 * $Z;
  738. $g = -0.5918 * $X + 1.5512 * $Y + 0.0406 * $Z;
  739. $b = 0.0008 * $X + 0.0239 * $Y + 0.9753 * $Z;
  740. if( $r > 1
  741. || $g > 1
  742. || $b > 1 ) {
  743. my $f = maxNum($r,$g,$b);
  744. $r /= $f;
  745. $g /= $f;
  746. $b /= $f;
  747. }
  748. #Log 3, "rgb: ". $r . " " . $g ." ". $b;
  749. $r *= 255;
  750. $g *= 255;
  751. $b *= 255;
  752. }
  753. return( $r, $g, $b );
  754. }
  755. sub
  756. HUEDevice_Get($@)
  757. {
  758. my ($hash, @a) = @_;
  759. my $name = $a[0];
  760. return "$name: get needs at least one parameter" if(@a < 2);
  761. my $cmd= $a[1];
  762. if($cmd eq "rgb") {
  763. my $r = 0;
  764. my $g = 0;
  765. my $b = 0;
  766. my $cm = ReadingsVal($name,"colormode","");
  767. if( $cm eq "ct" ) {
  768. if( ReadingsVal($name,"ct","") =~ m/(\d+) .*/ ) {
  769. ($r,$g,$b) = cttorgb($1);
  770. }
  771. } elsif( $cm eq "hs" ) {
  772. my $h = ReadingsVal($name,"hue",0) / 65535.0;
  773. my $s = ReadingsVal($name,"sat",0) / 254.0;
  774. my $v = ReadingsVal($name,"bri",0) / 254.0;
  775. ($r,$g,$b) = Color::hsv2rgb($h,$s,$v);
  776. $r *= 255;
  777. $g *= 255;
  778. $b *= 255;
  779. } elsif( ReadingsVal($name,"xy","") =~ m/(.+),(.+)/ ) {
  780. my ($x,$y) = ($1, $2);
  781. my $Y = ReadingsVal($name,"bri","") / 254.0;
  782. ($r,$g,$b) = xyYtorgb($x,$y,$Y);
  783. }
  784. return sprintf( "%02x%02x%02x", $r+0.5, $g+0.5, $b+0.5 );
  785. } elsif($cmd eq "RGB") {
  786. my $r = 0;
  787. my $g = 0;
  788. my $b = 0;
  789. my $cm = ReadingsVal($name,"colormode","");
  790. if( $cm eq "ct" ) {
  791. if( ReadingsVal($name,"ct","") =~ m/(\d+) .*/ ) {
  792. ($r,$g,$b) = cttorgb($1);
  793. }
  794. } elsif( $cm eq "hs" ) {
  795. my $h = ReadingsVal($name,"hue",0) / 65535.0;
  796. my $s = ReadingsVal($name,"sat",0) / 254.0;
  797. my $v = 1;
  798. ($r,$g,$b) = Color::hsv2rgb($h,$s,$v);
  799. $r *= 255;
  800. $g *= 255;
  801. $b *= 255;
  802. } elsif( ReadingsVal($name,"xy","") =~ m/(.+),(.+)/ ) {
  803. my ($x,$y) = ($1, $2);
  804. my $Y = 1;
  805. ($r,$g,$b) = xyYtorgb($x,$y,$Y);
  806. }
  807. return sprintf( "%02x%02x%02x", $r+0.5, $g+0.5, $b+0.5 );
  808. } elsif ( $cmd eq "devStateIcon" ) {
  809. return HUEDevice_devStateIcon($hash);
  810. }
  811. return "Unknown argument $cmd, choose one of rgb:noArg RGB:noArg devStateIcon:noArg";
  812. }
  813. ###################################
  814. # This could be IORead in fhem, But there is none.
  815. # Read http://forum.fhem.de/index.php?t=tree&goto=54027&rid=10#msg_54027
  816. # to find out why.
  817. sub
  818. HUEDevice_ReadFromServer($@)
  819. {
  820. my ($hash,@a) = @_;
  821. my $name = $hash->{NAME};
  822. #return if(IsDummy($name) || IsIgnored($name));
  823. no strict "refs";
  824. my $ret;
  825. unshift(@a,$name);
  826. #$ret = IOWrite($hash, @a);
  827. $ret = IOWrite($hash,$hash,@a);
  828. use strict "refs";
  829. return $ret;
  830. }
  831. sub
  832. HUEDevice_GetUpdate($)
  833. {
  834. my ($hash) = @_;
  835. my $name = $hash->{NAME};
  836. if( $hash->{helper}->{devtype} eq 'G' ) {
  837. my $result = HUEDevice_ReadFromServer($hash,$hash->{ID});
  838. if( !defined($result) ) {
  839. $hash->{STATE} = "unknown";
  840. return;
  841. } elsif( $result->{'error'} ) {
  842. $hash->{STATE} = $result->{'error'}->{'description'};
  843. return;
  844. }
  845. HUEDevice_Parse($hash,$result);
  846. } elsif( $hash->{helper}->{devtype} eq 'S' ) {
  847. }
  848. if(!$hash->{LOCAL}) {
  849. RemoveInternalTimer($hash);
  850. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "HUEDevice_GetUpdate", $hash, 0) if( $hash->{INTERVAL} );
  851. }
  852. return undef if( $hash->{helper}->{devtype} eq 'G' );
  853. my $result = HUEDevice_ReadFromServer($hash,$hash->{ID});
  854. if( !defined($result) ) {
  855. $hash->{helper}{reachable} = 0;
  856. #$hash->{STATE} = "unknown";
  857. return;
  858. } elsif( $result->{'error'} ) {
  859. $hash->{helper}{reachable} = 0;
  860. $hash->{STATE} = $result->{'error'}->{'description'};
  861. return;
  862. }
  863. HUEDevice_Parse($hash,$result);
  864. HUEBridge_updateGroups($hash->{IODev}, $hash->{ID}) if( $hash->{IODev}{TYPE} eq 'HUEBridge' );
  865. }
  866. sub
  867. HUEDeviceSetIcon($;$)
  868. {
  869. my ($hash,$force) = @_;
  870. $hash = $defs{$hash} if( ref($hash) ne 'HASH' );
  871. return undef if( !$hash );
  872. my $name = $hash->{NAME};
  873. return if( defined($attr{$name}{icon}) && !$force );
  874. if( $hash->{modelid} ) {
  875. my $model = $hueModels{$hash->{modelid}};
  876. return undef if( !$model );
  877. my $icon = $model->{icon};
  878. return undef if( !$icon );
  879. $attr{$name}{icon} = $icon;
  880. } elsif( $hash->{class} ) {
  881. my $class = lc( $hash->{class} );
  882. $class =~ s/ room//;
  883. $attr{$name}{icon} = "hue_room_$class";
  884. }
  885. }
  886. sub
  887. HUEDevice_Parse($$)
  888. {
  889. my($hash,$result) = @_;
  890. my $name = $hash->{NAME};
  891. if( ref($result) ne "HASH" ) {
  892. if( ref($result) && $HUEDevice_hasDataDumper) {
  893. Log3 $name, 2, "$name: got wrong status message for $name: ". Dumper $result;
  894. } else {
  895. Log3 $name, 2, "$name: got wrong status message for $name: $result";
  896. }
  897. return undef;
  898. }
  899. Log3 $name, 4, "parse status message for $name";
  900. Log3 $name, 5, Dumper $result if($HUEDevice_hasDataDumper);
  901. $hash->{name} = $result->{name} if( defined($result->{name}) );
  902. $hash->{type} = $result->{type} if( defined($result->{type}) );
  903. $hash->{class} = $result->{class} if( defined($result->{class}) );
  904. $hash->{uniqueid} = $result->{uniqueid} if( defined($result->{uniqueid}) );
  905. if( $hash->{helper}->{devtype} eq 'G' ) {
  906. if( $result->{lights} ) {
  907. $hash->{lights} = join( ",", sort { $a <=> $b } @{$result->{lights}} );
  908. } else {
  909. $hash->{lights} = '';
  910. }
  911. if( ref($result->{state}) eq 'HASH' ) {
  912. my %readings;
  913. if( $result->{state} ) {
  914. $readings{all_on} = $result->{state}{all_on};
  915. $readings{any_on} = $result->{state}{any_on};
  916. }
  917. if( AttrVal($name, 'createActionReadings', 0) ) {
  918. if( my $state = $result->{action} ) {
  919. $readings{ct} = $state->{ct}; $readings{ct} .= " (".int(1000000/$readings{ct})."K)" if( $readings{ct} );
  920. $readings{hue} = $state->{hue};
  921. $readings{sat} = $state->{sat};
  922. $readings{bri} = $state->{bri}; $readings{bri} = $hash->{helper}{bri} if( !defined($readings{bri}) );
  923. $readings{xy} = $state->{'xy'}->[0] .",". $state->{'xy'}->[1] if( defined($state->{'xy'}) );
  924. $readings{colormode} = $state->{colormode};
  925. $readings{alert} = $state->{alert};
  926. $readings{effect} = $state->{effect};
  927. $readings{reachable} = $state->{reachable}?1:0 if( defined($state->{reachable}) );
  928. my $s = '';
  929. my $pct = -1;
  930. my $on = $state->{on}; $readings{on} = $hash->{helper}{onoff} if( !defined($on) );
  931. if( $on ) {
  932. $s = 'on';
  933. $readings{onoff} = 1;
  934. if( !defined($readings{bri}) || AttrVal($name, 'subType', 'dimmer') eq 'switch' ) {
  935. $pct = 100;
  936. } else {
  937. $pct = int($readings{bri} * 99 / 254 + 1);
  938. if( $pct > 0
  939. && $pct < 100 ) {
  940. $s = $dim_values{int($pct/7)};
  941. }
  942. $s = 'off' if( $pct == 0 );
  943. }
  944. } else {
  945. $on = 0;
  946. $s = 'off';
  947. $pct = 0;
  948. $readings{onoff} = 0;
  949. }
  950. $readings{pct} = $pct;
  951. $s = 'unreachable' if( defined($readings{reachable}) && !$readings{reachable} );
  952. #$readings{state} = $s;
  953. }
  954. }
  955. readingsBeginUpdate($hash);
  956. foreach my $key ( keys %readings ) {
  957. if( defined($readings{$key}) ) {
  958. readingsBulkUpdate($hash, $key, $readings{$key}, 1) if( !defined($hash->{helper}{$key}) || $hash->{helper}{$key} ne $readings{$key} );
  959. $hash->{helper}{$key} = $readings{$key};
  960. }
  961. }
  962. readingsEndUpdate($hash,1);
  963. }
  964. if( defined($hash->{helper}->{update}) ) {
  965. delete $hash->{helper}->{update};
  966. fhem( "set $hash->{IODev}{NAME} statusRequest" );
  967. return undef;
  968. }
  969. return undef;
  970. }
  971. $hash->{modelid} = $result->{modelid} if( defined($result->{modelid}) );
  972. $hash->{productid} = $result->{productid} if( defined($result->{productid}) );
  973. $hash->{swversion} = $result->{swversion} if( defined($result->{swversion}) );
  974. $hash->{swconfigid} = $result->{swconfigid} if( defined($result->{swconfigid}) );
  975. $hash->{manufacturername} = $result->{manufacturername} if( defined($result->{manufacturername}) );
  976. $hash->{luminaireuniqueid} = $result->{luminaireuniqueid} if( defined($result->{luminaireuniqueid}) );
  977. if( $hash->{helper}->{devtype} eq 'S' ) {
  978. my %readings;
  979. if( my $config = $result->{config} ) {
  980. $hash->{on} = $config->{on}?1:0 if( defined($config->{on}) );
  981. $hash->{reachable} = $config->{reachable}?1:0 if( defined($config->{reachable}) );
  982. $hash->{url} = $config->{url} if( defined($config->{url}) );
  983. $hash->{lat} = $config->{lat} if( defined($config->{lat}) );
  984. $hash->{long} = $config->{long} if( defined($config->{long}) );
  985. $hash->{sunriseoffset} = $config->{sunriseoffset} if( defined($config->{sunriseoffset}) );
  986. $hash->{sunsetoffset} = $config->{sunsetoffset} if( defined($config->{sunsetoffset}) );
  987. $hash->{tholddark} = $config->{tholddark} if( defined($config->{tholddark}) );
  988. $hash->{sensitivity} = $config->{sensitivity} if( defined($config->{sensitivity}) );
  989. $readings{battery} = $config->{battery} if( defined($config->{battery}) );
  990. $readings{reachable} = $config->{reachable} if( defined($config->{reachable}) );
  991. }
  992. my $lastupdated;
  993. if( my $state = $result->{state} ) {
  994. $lastupdated = $state->{lastupdated};
  995. return undef if( !$lastupdated );
  996. return undef if( $lastupdated eq 'none' );
  997. substr( $lastupdated, 10, 1, ' ' ) if($lastupdated);
  998. my $offset = 0;
  999. if( my $iohash = $hash->{IODev} ) {
  1000. substr( $lastupdated, 10, 1, '_' );
  1001. my $sec = SVG_time_to_sec($lastupdated);
  1002. if( my $offset = $iohash->{helper}{offsetUTC} ) {
  1003. $sec += $offset;
  1004. Log3 $name, 4, "$name: offsetUTC: $offset";
  1005. }
  1006. $lastupdated = FmtDateTime($sec);
  1007. }
  1008. $hash->{lastupdated} = ReadingsVal( $name, '.lastupdated', undef ) if( !$hash->{lastupdated} );
  1009. return undef if( $hash->{lastupdated} && $hash->{lastupdated} eq $lastupdated );
  1010. Log3 $name, 4, "$name: lastupdated: $lastupdated, hash->{lastupdated}: $hash->{lastupdated}";
  1011. Log3 $name, 5, "$name: ". Dumper $result if($HUEDevice_hasDataDumper);
  1012. $hash->{lastupdated} = $lastupdated;
  1013. $readings{state} = $state->{status} if( defined($state->{status}) );
  1014. $readings{state} = $state->{flag}?'1':'0' if( defined($state->{flag}) );
  1015. $readings{state} = $state->{open}?'open':'closed' if( defined($state->{open}) );
  1016. $readings{state} = $state->{lightlevel} if( defined($state->{lightlevel}) && !defined($state->{lux}) );
  1017. $readings{state} = $state->{buttonevent} if( defined($state->{buttonevent}) );
  1018. $readings{state} = $state->{presence}?'motion':'nomotion' if( defined($state->{presence}) );
  1019. $readings{dark} = $state->{dark}?'1':'0' if( defined($state->{dark}) );
  1020. $readings{humidity} = $state->{humidity} * 0.01 if( defined($state->{humidity}) );
  1021. $readings{daylight} = $state->{daylight}?'1':'0' if( defined($state->{daylight}) );
  1022. $readings{temperature} = $state->{temperature} * 0.01 if( defined($state->{temperature}) );
  1023. $readings{pressure} = $state->{pressure} if( defined($state->{pressure}) );
  1024. $readings{lightlevel} = $state->{lightlevel} if( defined($state->{lightlevel}) );
  1025. $readings{lux} = $state->{lux} if( defined($state->{lux}) );
  1026. $readings{power} = $state->{power} if( defined($state->{power}) );
  1027. $readings{voltage} = $state->{voltage} if( defined($state->{voltage}) );
  1028. $readings{current} = $state->{current} if( defined($state->{current}) );
  1029. $readings{consumption} = $state->{consumption} if( defined($state->{consumption}) );
  1030. $readings{water} = $state->{water} if( defined($state->{water}) );
  1031. }
  1032. if( scalar keys %readings ) {
  1033. readingsBeginUpdate($hash);
  1034. my $i = 0;
  1035. foreach my $key ( keys %readings ) {
  1036. if( defined($readings{$key}) ) {
  1037. if( $lastupdated ) {
  1038. $hash->{'.updateTimestamp'} = $lastupdated;
  1039. $hash->{CHANGETIME}[$i] = $lastupdated;
  1040. }
  1041. readingsBulkUpdate($hash, $key, $readings{$key}, 1);
  1042. ++$i;
  1043. }
  1044. }
  1045. if( $lastupdated ) {
  1046. $hash->{'.updateTimestamp'} = $lastupdated;
  1047. $hash->{CHANGETIME}[$i] = $lastupdated;
  1048. readingsBulkUpdate($hash, '.lastupdated', $lastupdated, 0);
  1049. }
  1050. readingsEndUpdate($hash,1);
  1051. delete $hash->{CHANGETIME};
  1052. }
  1053. return undef;
  1054. }
  1055. $attr{$name}{model} = $result->{modelid} if( !defined($attr{$name}{model}) && $result->{modelid} );
  1056. if( !defined($attr{$name}{subType}) ) {
  1057. if( defined($attr{$name}{model}) ) {
  1058. if( defined($hueModels{$attr{$name}{model}}{subType}) ) {
  1059. $attr{$name}{subType} = $hueModels{$attr{$name}{model}}{subType};
  1060. HUEDeviceSetIcon($hash) if( $hash->{helper}{fromAutocreate} );
  1061. } elsif( $attr{$name}{model} =~ m/TW$/ ) {
  1062. $attr{$name}{subType} = 'ctdimmer';
  1063. } elsif( $attr{$name}{model} =~ m/RGB$/ ) {
  1064. $attr{$name}{subType} = 'colordimmer';
  1065. } elsif( $attr{$name}{model} =~ m/RGBW$/ ) {
  1066. $attr{$name}{subType} = 'extcolordimmer';
  1067. }
  1068. delete $hash->{helper}{fromAutocreate};
  1069. }
  1070. if( !defined($attr{$name}{subType}) && $hash->{type} ) {
  1071. if( $hash->{type} eq "Extended color light" ) {
  1072. $attr{$name}{subType} = 'extcolordimmer';
  1073. } elsif( $hash->{type} eq "Color light" ) {
  1074. $attr{$name}{subType} = 'colordimmer';
  1075. } elsif( $hash->{type} eq "Color temperature light" ) {
  1076. $attr{$name}{subType} = 'ctdimmer';
  1077. } elsif( $hash->{type} =~ m/Dimmable/ ) {
  1078. $attr{$name}{subType} = 'dimmer';
  1079. } elsif( $hash->{type} =~ m/On.Off/ ) {
  1080. $attr{$name}{subType} = 'switch';
  1081. }
  1082. }
  1083. } elsif( $attr{$name}{subType} eq "colordimmer" && defined($attr{$name}{model}) ) {
  1084. $attr{$name}{subType} = $hueModels{$attr{$name}{model}}{subType} if( defined($hueModels{$attr{$name}{model}}{subType}) );
  1085. }
  1086. $attr{$name}{devStateIcon} = '{(HUEDevice_devStateIcon($name),"toggle")}' if( !defined( $attr{$name}{devStateIcon} ) );
  1087. if( !defined($attr{$name}{webCmd}) && defined($attr{$name}{subType}) ) {
  1088. my $subtype = $attr{$name}{subType};
  1089. if( !$hash->{helper}->{devtype} ) {
  1090. $attr{$name}{webCmd} = 'rgb:rgb ff0000:rgb DEFF26:rgb 0000ff:ct 490:ct 380:ct 270:ct 160:toggle:on:off' if( $subtype eq "extcolordimmer" );
  1091. $attr{$name}{webCmd} = 'hue:rgb:rgb ff0000:rgb 98FF23:rgb 0000ff:toggle:on:off' if( $subtype eq "colordimmer" );
  1092. $attr{$name}{webCmd} = 'ct:ct 490:ct 380:ct 270:ct 160:toggle:on:off' if( $subtype eq "ctdimmer" );
  1093. $attr{$name}{webCmd} = 'pct:toggle:on:off' if( $subtype eq "dimmer" );
  1094. $attr{$name}{webCmd} = 'toggle:on:off' if( $subtype eq "switch" );
  1095. } elsif( $hash->{helper}->{devtype} eq 'G' ) {
  1096. $attr{$name}{webCmd} = 'on:off';
  1097. }
  1098. }
  1099. readingsBeginUpdate($hash);
  1100. my $state = $result->{'state'};
  1101. my $on = $state->{on};
  1102. $on = $hash->{helper}{on} if( !defined($on) );
  1103. my $reachable = $state->{reachable}?1:0;
  1104. $reachable = $hash->{helper}{reachable} if( !defined($state->{reachable}) );
  1105. $reachable = 1 if( !$reachable && AttrVal($name, 'ignoreReachable', 0) );
  1106. my $colormode = $state->{'colormode'};
  1107. my $bri = $state->{'bri'};
  1108. $bri = $hash->{helper}{bri} if( !defined($bri) );
  1109. my $ct = $state->{'ct'};
  1110. my $hue = $state->{'hue'};
  1111. my $sat = $state->{'sat'};
  1112. my $xy = undef;
  1113. $xy = $state->{'xy'}->[0] .",". $state->{'xy'}->[1] if( defined($state->{'xy'}) );
  1114. my $alert = $state->{alert};
  1115. my $effect = $state->{effect};
  1116. if( defined($colormode) && $colormode ne $hash->{helper}{colormode} ) {readingsBulkUpdate($hash,"colormode",$colormode);}
  1117. if( defined($bri) && $bri != $hash->{helper}{bri} ) {readingsBulkUpdate($hash,"bri",$bri);}
  1118. if( defined($ct) && $ct != $hash->{helper}{ct} ) {
  1119. if( $ct == 0 ) {
  1120. readingsBulkUpdate($hash,"ct",$ct);
  1121. }
  1122. else {
  1123. readingsBulkUpdate($hash,"ct",$ct . " (".int(1000000/$ct)."K)");
  1124. }
  1125. }
  1126. if( defined($hue) && $hue != $hash->{helper}{hue} ) {readingsBulkUpdate($hash,"hue",$hue);}
  1127. if( defined($sat) && $sat != $hash->{helper}{sat} ) {readingsBulkUpdate($hash,"sat",$sat);}
  1128. if( defined($xy) && $xy ne $hash->{helper}{xy} ) {readingsBulkUpdate($hash,"xy",$xy);}
  1129. if( !defined($hash->{helper}{reachable}) || $reachable != $hash->{helper}{reachable} ) {readingsBulkUpdate($hash,"reachable",$reachable?1:0);}
  1130. if( defined($alert) && $alert ne $hash->{helper}{alert} ) {readingsBulkUpdate($hash,"alert",$alert);}
  1131. if( defined($effect) && $effect ne $hash->{helper}{effect} ) {readingsBulkUpdate($hash,"effect",$effect);}
  1132. my $s = '';
  1133. my $pct = -1;
  1134. if( $on )
  1135. {
  1136. $s = 'on';
  1137. if( $on != $hash->{helper}{on} ) {readingsBulkUpdate($hash,"onoff",1);}
  1138. if( $bri < 0 || AttrVal($name, 'subType', 'dimmer') eq 'switch' ) {
  1139. $pct = 100;
  1140. } else {
  1141. $pct = int($bri * 99 / 254 + 1);
  1142. if( $pct > 0
  1143. && $pct < 100 ) {
  1144. $s = $dim_values{int($pct/7)};
  1145. }
  1146. $s = 'off' if( $pct == 0 );
  1147. }
  1148. }
  1149. else
  1150. {
  1151. $on = 0;
  1152. $s = 'off';
  1153. $pct = 0;
  1154. if( $on != $hash->{helper}{on} ) {readingsBulkUpdate($hash,"onoff",0);}
  1155. }
  1156. if( $pct != $hash->{helper}{pct} ) {readingsBulkUpdate($hash,"pct", $pct);}
  1157. #if( $pct != $hash->{helper}{pct} ) {readingsBulkUpdate($hash,"level", $pct . ' %');}
  1158. $s = 'unreachable' if( !$reachable );
  1159. $hash->{helper}{on} = $on if( defined($on) );
  1160. $hash->{helper}{reachable} = $reachable if( defined($reachable) );
  1161. $hash->{helper}{colormode} = $colormode if( defined($colormode) );
  1162. $hash->{helper}{bri} = $bri if( defined($bri) );
  1163. $hash->{helper}{ct} = $ct if( defined($ct) );
  1164. $hash->{helper}{hue} = $hue if( defined($hue) );
  1165. $hash->{helper}{sat} = $sat if( defined($sat) );
  1166. $hash->{helper}{xy} = $xy if( defined($xy) );
  1167. $hash->{helper}{alert} = $alert if( defined($alert) );
  1168. $hash->{helper}{effect} = $effect if( defined($effect) );
  1169. $hash->{helper}{pct} = $pct;
  1170. my $changed = $hash->{CHANGED}?1:0;
  1171. if( $s ne $hash->{STATE} ) {readingsBulkUpdate($hash,"state",$s);}
  1172. readingsEndUpdate($hash,1);
  1173. if( defined($colormode) ) {
  1174. my $rgb = CommandGet("","$name rgb");
  1175. if( $rgb ne $hash->{helper}{rgb} ) { readingsSingleUpdate($hash,"rgb", $rgb,1); };
  1176. $hash->{helper}{rgb} = $rgb;
  1177. }
  1178. $hash->{helper}->{update_timeout} = -1;
  1179. RemoveInternalTimer($hash);
  1180. return $changed;
  1181. }
  1182. sub
  1183. HUEDevice_Attr($$$;$)
  1184. {
  1185. my ($cmd, $name, $attrName, $attrVal) = @_;
  1186. if( $attrName eq "setList" ) {
  1187. my $hash = $defs{$name};
  1188. delete $hash->{helper}{setList};
  1189. return "$name is not a sensor device" if( $hash->{helper}->{devtype} ne 'S' );
  1190. return "$name is not a CLIP sensor device" if( $hash->{type} && $hash->{type} !~ m/^CLIP/ );
  1191. if( $cmd eq "set" && $attrVal ) {
  1192. foreach my $line ( split( "\n", $attrVal ) ) {
  1193. my($cmd,$json) = split( ":", $line,2 );
  1194. if( $cmd =~ m'^/(.*)/$' ) {
  1195. my $regex = $1;
  1196. $hash->{helper}{setList}{'regex'} = [] if( !$hash->{helper}{setList}{':regex'} );
  1197. push @{$hash->{helper}{setList}{'regex'}}, { regex => $regex, json => $json };
  1198. } else {
  1199. $hash->{helper}{setList}{cmds}{$cmd} = $json;
  1200. }
  1201. }
  1202. }
  1203. }
  1204. return;
  1205. }
  1206. 1;
  1207. =pod
  1208. =item summary devices connected to a phillips hue bridge or a osram lightify gateway
  1209. =item summary_DE Geräte an einer Philips HUE Bridge oder einem Osram LIGHTIFY Gateway
  1210. =begin html
  1211. <a name="HUEDevice"></a>
  1212. <h3>HUEDevice</h3>
  1213. <ul>
  1214. <br>
  1215. <a name="HUEDevice_Define"></a>
  1216. <b>Define</b>
  1217. <ul>
  1218. <code>define &lt;name&gt; HUEDevice [group|sensor] &lt;id&gt; [&lt;interval&gt;]</code><br>
  1219. <br>
  1220. Defines a device connected to a <a href="#HUEBridge">HUEBridge</a>.<br><br>
  1221. This can be a hue bulb, a living colors light or a living whites bulb or dimmer plug.<br><br>
  1222. The device status will be updated every &lt;interval&gt; seconds. 0 means no updates.
  1223. The default and minimum is 60 if the IODev has not set pollDevices to 1.
  1224. The default ist 0 if the IODev has set pollDevices to 1.
  1225. Groups are updated only on definition and statusRequest<br><br>
  1226. Examples:
  1227. <ul>
  1228. <code>define bulb HUEDevice 1</code><br>
  1229. <code>define LC HUEDevice 2</code><br>
  1230. <code>define allLights HUEDevice group 0</code><br>
  1231. </ul>
  1232. </ul><br>
  1233. <a name="HUEDevice_Readings"></a>
  1234. <b>Readings</b>
  1235. <ul>
  1236. <li>bri<br>
  1237. the brightness reported from the device. the value can be betwen 1 and 254</li>
  1238. <li>colormode<br>
  1239. the current colormode</li>
  1240. <li>ct<br>
  1241. the colortemperature in mireds and kelvin</li>
  1242. <li>hue<br>
  1243. the current hue</li>
  1244. <li>pct<br>
  1245. the current brightness in percent</li>
  1246. <li>onoff<br>
  1247. the current on/off state as 0 or 1</li>
  1248. <li>sat<br>
  1249. the current saturation</li>
  1250. <li>xy<br>
  1251. the current xy color coordinates</li>
  1252. <li>state<br>
  1253. the current state</li>
  1254. <br>
  1255. Notes:
  1256. <ul>
  1257. <li>with current bridge firware versions groups have <code>all_on</code> and <code>any_on</code> readings,
  1258. with older firmware versions groups have no readings.</li>
  1259. <li>not all readings show the actual device state. all readings not related to the current colormode have to be ignored.</li>
  1260. <li>the actual state of a device controlled by a living colors or living whites remote can be different and will
  1261. be updated after some time.</li>
  1262. </ul><br>
  1263. </ul><br>
  1264. <a name="HUEDevice_Set"></a>
  1265. <b>Set</b>
  1266. <ul>
  1267. <li>on [&lt;ramp-time&gt;]</li>
  1268. <li>off [&lt;ramp-time&gt;]</li>
  1269. <li>toggle [&lt;ramp-time&gt;]</li>
  1270. <li>statusRequest<br>
  1271. Request device status update.</li>
  1272. <li>pct &lt;value&gt; [&lt;ramp-time&gt;]<br>
  1273. dim to &lt;value&gt;<br>
  1274. Note: the FS20 compatible dimXX% commands are also accepted.</li>
  1275. <li>color &lt;value&gt;<br>
  1276. set colortemperature to &lt;value&gt; kelvin.</li>
  1277. <li>bri &lt;value&gt; [&lt;ramp-time&gt;]<br>
  1278. set brighness to &lt;value&gt;; range is 0-254.</li>
  1279. <li>dimUp [delta]</li>
  1280. <li>dimDown [delta]</li>
  1281. <li>ct &lt;value&gt; [&lt;ramp-time&gt;]<br>
  1282. set colortemperature to &lt;value&gt; in mireds (range is 154-500) or kelvin (range is 2000-6493).</li>
  1283. <li>ctUp [delta]</li>
  1284. <li>ctDown [delta]</li>
  1285. <li>hue &lt;value&gt; [&lt;ramp-time&gt;]<br>
  1286. set hue to &lt;value&gt;; range is 0-65535.</li>
  1287. <li>hueUp [delta]</li>
  1288. <li>hueDown [delta]</li>
  1289. <li>sat &lt;value&gt; [&lt;ramp-time&gt;]<br>
  1290. set saturation to &lt;value&gt;; range is 0-254.</li>
  1291. <li>satUp [delta]</li>
  1292. <li>satDown [delta]</li>
  1293. <li>xy &lt;x&gt;,&lt;y&gt; [&lt;ramp-time&gt;]<br>
  1294. set the xy color coordinates to &lt;x&gt;,&lt;y&gt;</li>
  1295. <li>alert [none|select|lselect]</li>
  1296. <li>effect [none|colorloop]</li>
  1297. <li>transitiontime &lt;time&gt;<br>
  1298. set the transitiontime to &lt;time&gt; 1/10s</li>
  1299. <li>rgb &lt;rrggbb&gt;<br>
  1300. set the color to (the nearest equivalent of) &lt;rrggbb&gt;</li>
  1301. <br>
  1302. <li>delayedUpdate</li>
  1303. <li>immediateUpdate</li>
  1304. <br>
  1305. <li>savescene &lt;id&gt;</li>
  1306. <li>deletescene &lt;id&gt;</li>
  1307. <li>scene</li>
  1308. <br>
  1309. <li>lights &lt;lights&gt;<br>
  1310. Only valid for groups. Changes the list of lights in this group.
  1311. The lights are given as a comma sparated list of fhem device names or bridge light numbers.</li>
  1312. <li>rename &lt;new name&gt;<br>
  1313. Renames the device in the bridge and changes the fhem alias.</li>
  1314. <br>
  1315. <li><a href="#setExtensions"> set extensions</a> are supported.</li>
  1316. <br>
  1317. Note:
  1318. <ul>
  1319. <li>&lt;ramp-time&gt; is given in seconds</li>
  1320. <li>multiple paramters can be set at once separated by <code>:</code><br>
  1321. Examples:<br>
  1322. <code>set LC on : transitiontime 100</code><br>
  1323. <code>set bulb on : bri 100 : color 4000</code><br></li>
  1324. </ul>
  1325. </ul><br>
  1326. <a name="HUEDevice_Get"></a>
  1327. <b>Get</b>
  1328. <ul>
  1329. <li>rgb</li>
  1330. <li>RGB</li>
  1331. <li>devStateIcon<br>
  1332. returns html code that can be used to create an icon that represents the device color in the room overview.</li>
  1333. </ul><br>
  1334. <a name="HUEDevice_Attr"></a>
  1335. <b>Attributes</b>
  1336. <ul>
  1337. <li>color-icon<br>
  1338. 1 -> use lamp color as icon color and 100% shape as icon shape<br>
  1339. 2 -> use lamp color scaled to full brightness as icon color and dim state as icon shape</li>
  1340. <li>createActionReadings<br>
  1341. create readings for the last action in group devices</li>
  1342. <li>createGroupReadings<br>
  1343. create 'artificial' readings for group devices. default depends on the createGroupReadings setting in the bridge device.</li>
  1344. <li>ignoreReachable<br>
  1345. ignore the reachable state that is reported by the hue bridge. assume the device is allways reachable.</li>
  1346. <li>setList<br>
  1347. The list of know set commands for sensor type devices. one command per line, eg.: <code><br>
  1348. attr mySensor setList present:{&lt;json&gt;}\<br>
  1349. absent:{&lt;json&gt;}</code></li>
  1350. <li>subType<br>
  1351. extcolordimmer -> device has rgb and color temperatur control<br>
  1352. colordimmer -> device has rgb controll<br>
  1353. ctdimmer -> device has color temperature control<br>
  1354. dimmer -> device has brightnes controll<br>
  1355. switch -> device has on/off controll<br></li>
  1356. <li>transitiontime<br>
  1357. default transitiontime for all set commands if not specified directly in the set.</li>
  1358. <li>delayedUpdate<br>
  1359. 1 -> the update of the device status after a set command will be delayed for 1 second. usefull if multiple devices will be switched.
  1360. </li>
  1361. <li>devStateIcon<br>
  1362. will be initialized to <code>{(HUEDevice_devStateIcon($name),"toggle")}</code> to show device color as default in room overview.</li>
  1363. <li>webCmd<br>
  1364. will be initialized to a device specific value according to subType.</li>
  1365. </ul>
  1366. </ul><br>
  1367. =end html
  1368. =cut