31_PLAYBULB.pm 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. ###############################################################################
  2. #
  3. # Developed with Kate
  4. #
  5. # (c) 2016-2017 Copyright: Marko Oldenburg (leongaultier at gmail dot com)
  6. # All rights reserved
  7. #
  8. # Special thanks goes to comitters:
  9. # - Christoph (pc1246) commandref writer
  10. #
  11. #
  12. # This script is free software; you can redistribute it and/or modify
  13. # it under the terms of the GNU General Public License as published by
  14. # the Free Software Foundation; either version 2 of the License, or
  15. # any later version.
  16. #
  17. # The GNU General Public License can be found at
  18. # http://www.gnu.org/copyleft/gpl.html.
  19. # A copy is found in the textfile GPL.txt and important notices to the license
  20. # from the author is found in LICENSE.txt distributed with these scripts.
  21. #
  22. # This script is distributed in the hope that it will be useful,
  23. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  25. # GNU General Public License for more details.
  26. #
  27. #
  28. # $Id: 31_PLAYBULB.pm 15650 2017-12-20 05:41:22Z CoolTux $
  29. #
  30. ###############################################################################
  31. package main;
  32. use strict;
  33. use warnings;
  34. use POSIX;
  35. use JSON;
  36. use Blocking;
  37. use SetExtensions;
  38. my $version = "1.4.0";
  39. my %playbulbModels = (
  40. BTL300_v5 => {'aColor' => '0x16' ,'aEffect' => '0x14' ,'aBattery' => '0x1f' ,'aDevicename' => '0x3'}, # Candle Firmware 5
  41. BTL300_v6 => {'aColor' => '0x19' ,'aEffect' => '0x17' ,'aBattery' => '0x22' ,'aDevicename' => '0x3'}, # Candle Firmware 6
  42. BTL305_v14 => {'aColor' => '0x29' ,'aEffect' => '0x27' ,'aBattery' => '0x34' ,'aDevicename' => '0x3'}, # Candle S Firmware 1.4
  43. BTL201_v2 => {'aColor' => '0x1b' ,'aEffect' => '0x19' ,'aBattery' => 'none' ,'aDevicename' => 'none'}, # Smart
  44. BTL201M_V16 => {'aColor' => '0x25' ,'aEffect' => '0x23' ,'aBattery' => 'none' ,'aDevicename' => '0x7'}, # Smart (1/2017)
  45. BTL505_v1 => {'aColor' => '0x23' ,'aEffect' => '0x21' ,'aBattery' => 'none' ,'aDevicename' => '0x29'}, # Stripe
  46. BTL400M_v18 => {'aColor' => '0x23' ,'aEffect' => '0x21' ,'aBattery' => '0x2e' ,'aDevicename' => '0x7'}, # Garden
  47. BTL400M_v37 => {'aColor' => '0x1b' ,'aEffect' => '0x19' ,'aBattery' => '0x24' ,'aDevicename' => '0x7'}, # Garden neue Version
  48. BTL100C_v10 => {'aColor' => '0x1b' ,'aEffect' => '0x19' ,'aBattery' => 'none' ,'aDevicename' => 'none'}, # Color LED
  49. BTL301W => {'aColor' => '0x29' ,'aEffect' => '0x27' ,'aBattery' => '0x34' ,'aDevicename' => '0x3'}, # Sphere Model BTL 301 W
  50. );
  51. my %effects = (
  52. 'Flash' => '00',
  53. 'Pulse' => '01',
  54. 'RainbowJump' => '02',
  55. 'RainbowFade' => '03',
  56. 'Candle' => '04',
  57. 'none' => 'FF',
  58. );
  59. my %effectsHex = (
  60. '00' => 'Flash',
  61. '01' => 'Pulse',
  62. '02' => 'RainbowJump',
  63. '03' => 'RainbowFade',
  64. '04' => 'Candle',
  65. 'ff' => 'none',
  66. );
  67. sub PLAYBULB_Initialize($);
  68. sub PLAYBULB_Define($$);
  69. sub PLAYBULB_Undef($$);
  70. sub PLAYBULB_Attr(@);
  71. sub PLAYBULB_firstRun($);
  72. sub PLAYBULB_Set($$@);
  73. sub PLAYBULB_Run($$$);
  74. sub PLAYBULB_BlockingRun($);
  75. sub PLAYBULB_gattCharWrite($$$$$$$$$$);
  76. sub PLAYBULB_gattCharRead($$$$);
  77. sub PLAYBULB_readBattery($$$);
  78. sub PLAYBULB_stateOnOff($$);
  79. sub PLAYBULB_readDevicename($$$);
  80. sub PLAYBULB_writeDevicename($$$$);
  81. sub PLAYBULB_forRun_encodeJSON($$$$$$$$$$$$$);
  82. sub PLAYBULB_forDone_encodeJSON($$$$$$$);
  83. sub PLAYBULB_BlockingDone($);
  84. sub PLAYBULB_BlockingAborted($);
  85. sub PLAYBULB_Initialize($) {
  86. my ($hash) = @_;
  87. $hash->{SetFn} = "PLAYBULB_Set";
  88. $hash->{DefFn} = "PLAYBULB_Define";
  89. $hash->{UndefFn} = "PLAYBULB_Undef";
  90. $hash->{AttrFn} = "PLAYBULB_Attr";
  91. $hash->{AttrList} = "model:BTL300_v5,BTL300_v6,BTL201_v2,BTL201M_V16,BTL505_v1,BTL400M_v18,BTL400M_v37,BTL100C_v10,BTL305_v14,BTL301W ".
  92. "sshHost ".
  93. $readingFnAttributes;
  94. foreach my $d(sort keys %{$modules{PLAYBULB}{defptr}}) {
  95. my $hash = $modules{PLAYBULB}{defptr}{$d};
  96. $hash->{VERSION} = $version;
  97. }
  98. }
  99. sub PLAYBULB_Define($$) {
  100. my ( $hash, $def ) = @_;
  101. my @a = split( "[ \t][ \t]*", $def );
  102. return "too few parameters: define <name> PLAYBULB <BTMAC>" if( @a != 3 );
  103. my $name = $a[0];
  104. my $mac = $a[2];
  105. $hash->{BTMAC} = $mac;
  106. $hash->{VERSION} = $version;
  107. readingsSingleUpdate ($hash,"state","Unknown", 0);
  108. $attr{$name}{room} = "PLAYBULB" if( !defined($attr{$name}{room}) );
  109. $attr{$name}{devStateIcon} = "unreachable:light_question" if( !defined($attr{$name}{devStateIcon}) );
  110. $attr{$name}{webCmd} = "rgb:rgb FF0000:rgb 00FF00:rgb 0000FF:rgb FFFFFF:rgb F7FF00:rgb 00FFFF:rgb F700FF:effect" if( !defined($attr{$name}{webCmd}) );
  111. $hash->{helper}{effect} = ReadingsVal($name,"effect","Candle");
  112. $hash->{helper}{onoff} = ReadingsVal($name,"onoff",0);
  113. $hash->{helper}{rgb} = ReadingsVal($name,"rgb","ff0000");
  114. $hash->{helper}{sat} = ReadingsVal($name,"sat",0);
  115. $hash->{helper}{speed} = ReadingsVal($name,"speed",120);
  116. $hash->{helper}{color} = ReadingsVal($name,"color","on");
  117. if( $init_done ) {
  118. PLAYBULB_firstRun($hash);
  119. } else {
  120. InternalTimer( gettimeofday()+30, "PLAYBULB_firstRun", $hash, 0 ) ;
  121. }
  122. $modules{PLAYBULB}{defptr}{$hash->{BTMAC}} = $hash;
  123. return undef;
  124. }
  125. sub PLAYBULB_Undef($$) {
  126. my ( $hash, $arg ) = @_;
  127. my $name = $hash->{NAME};
  128. Log3 $name, 3, "PLAYBULB ($name) - undefined";
  129. delete($modules{PLAYBULB}{defptr}{$hash->{BTMAC}});
  130. return undef;
  131. }
  132. sub PLAYBULB_Attr(@) {
  133. my ( $cmd, $name, $attrName, $attrVal ) = @_;
  134. my $hash = $defs{$name};
  135. my $orig = $attrVal;
  136. if( $attrName eq "model" ) {
  137. if( $cmd eq "set" ) {
  138. PLAYBULB_Run($hash,"statusRequest",undef) if( $init_done );
  139. }
  140. }
  141. }
  142. sub PLAYBULB_firstRun($) {
  143. my ($hash) = @_;
  144. my $name = $hash->{NAME};
  145. RemoveInternalTimer($hash);
  146. PLAYBULB_Run($hash,"statusRequest",undef);
  147. ### Für kurze Zeit da neue Readings
  148. CommandDeleteReading(undef,"$name battery") if( defined(ReadingsVal($name,'battery',undef)) );
  149. }
  150. sub PLAYBULB_Set($$@) {
  151. my ($hash, $name, @aa) = @_;
  152. my ($cmd, $arg) = @aa;
  153. my $action;
  154. if( $cmd eq 'on' ) {
  155. $action = "onoff";
  156. $arg = 1;
  157. } elsif( $cmd eq 'off' ) {
  158. $action = "onoff";
  159. $arg = 0;
  160. } elsif( $cmd eq 'effect' ) {
  161. $action = $cmd;
  162. } elsif( $cmd eq 'rgb' ) {
  163. $action = $cmd;
  164. } elsif( $cmd eq 'sat' ) {
  165. $action = $cmd;
  166. } elsif( $cmd eq 'speed' ) {
  167. $action = $cmd;
  168. } elsif( $cmd eq 'color' ) {
  169. $action = $cmd;
  170. } elsif( $cmd eq 'deviceName' ) {
  171. my $wordlenght = length($arg);
  172. return "to many character for Devicename" if($wordlenght > 20 );
  173. $action = $cmd;
  174. } elsif( $cmd eq 'statusRequest' ) {
  175. $action = $cmd;
  176. $arg = undef;
  177. } else {
  178. my $list = "on:noArg off:noArg rgb:colorpicker,RGB sat:slider,0,5,255 effect:Flash,Pulse,RainbowJump,RainbowFade,Candle,none speed:slider,170,50,20 color:on,off statusRequest:noArg ";
  179. $list .= "deviceName " if( defined($attr{$name}{model}) and ($attr{$name}{model} ne "BTL400M_v18"
  180. or $attr{$name}{model} ne "BTL100C_v10"
  181. or $attr{$name}{model} ne "BTL400M_v37") );
  182. return SetExtensions($hash, $list, $name, $cmd, $arg);
  183. }
  184. PLAYBULB_Run($hash,$action,$arg);
  185. return undef;
  186. }
  187. sub PLAYBULB_Run($$$) {
  188. my ( $hash, $cmd, $arg ) = @_;
  189. my $name = $hash->{NAME};
  190. unless( defined($attr{$name}{model}) ) {
  191. readingsSingleUpdate($hash,'state','set attribut model',1);
  192. return;
  193. }
  194. my $mac = $hash->{BTMAC};
  195. my $dname;
  196. $hash->{helper}{$cmd} = $arg if( $cmd ne "deviceName" );
  197. $hash->{helper}{setDeviceName} = 1 if( $cmd eq "deviceName" );
  198. my $rgb = $hash->{helper}{rgb};
  199. my $sat = sprintf("%02x", $hash->{helper}{sat});
  200. my $effect = $hash->{helper}{effect};
  201. my $speed = sprintf("%02x", $hash->{helper}{speed});
  202. my $stateOnoff = $hash->{helper}{onoff};
  203. my $stateEffect = ReadingsVal($name,"effect","Candle");
  204. my $ac = $playbulbModels{$attr{$name}{model}}{aColor};
  205. my $ae = $playbulbModels{$attr{$name}{model}}{aEffect};
  206. my $ab = $playbulbModels{$attr{$name}{model}}{aBattery};
  207. my $adname = $playbulbModels{$attr{$name}{model}}{aDevicename};
  208. $dname = $arg if( $cmd eq "deviceName" );
  209. if( $cmd eq "color" and $arg eq "off") {
  210. $rgb = "000000";
  211. $sat = "FF";
  212. }
  213. BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID}));
  214. my $response_encode = PLAYBULB_forRun_encodeJSON($cmd,$mac,$stateOnoff,$sat,$rgb,$effect,$speed,$stateEffect,$ac,$ae,$ab,$adname,$dname);
  215. $hash->{helper}{RUNNING_PID} = BlockingCall("PLAYBULB_BlockingRun", $name."|".$response_encode, "PLAYBULB_BlockingDone", 10, "PLAYBULB_BlockingAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID}));
  216. Log3 $name, 4, "(Sub PLAYBULB - $name) - Call BlockingRun";
  217. }
  218. sub PLAYBULB_BlockingRun($) {
  219. my ($string) = @_;
  220. my ($name,$data) = split("\\|", $string);
  221. my $data_json = decode_json($data);
  222. my $mac = $data_json->{mac};
  223. my $stateOnoff = $data_json->{stateOnoff};
  224. my $sat = $data_json->{sat};
  225. my $rgb = $data_json->{rgb};
  226. my $effect = $data_json->{effect};
  227. my $speed = $data_json->{speed};
  228. my $stateEffect = $data_json->{stateEffect};
  229. my $ac = $data_json->{ac};
  230. my $ae = $data_json->{ae};
  231. my $ab = $data_json->{ab};
  232. my $cmd = $data_json->{cmd};
  233. my $adname = $data_json->{adname};
  234. my $dname = $data_json->{dname};
  235. my $blevel = 1000;
  236. my $cc;
  237. my $ec;
  238. my $response_encode;
  239. Log3 $name, 4, "(Sub PLAYBULB_Run - $name) - Running nonBlocking";
  240. ##### Abruf des aktuellen Status
  241. #### das c vor den bekannten Variablen steht für current
  242. my ($ccc,$cec,$csat,$crgb,$ceffect,$cspeed) = PLAYBULB_gattCharRead($name,$mac,$ac,$ae);
  243. if( defined($ccc) and defined($cec) ) {
  244. ### Regeln für die aktuellen Values
  245. if( $cmd eq "statusRequest" ) {
  246. ###### Batteriestatus einlesen
  247. $blevel = PLAYBULB_readBattery($name,$mac,$ab) if( $ab ne "none" );
  248. ###### Status ob An oder Aus
  249. $stateOnoff = PLAYBULB_stateOnOff($ccc,$cec);
  250. ###### Devicename ermitteln #######
  251. my $dname = PLAYBULB_readDevicename($name,$mac,$adname) if( $adname ne "none" );
  252. Log3 $name, 4, "(Sub PLAYBULB_Run StatusRequest - $name) - Rückgabe an Auswertungsprogramm beginnt";
  253. $response_encode = PLAYBULB_forDone_encodeJSON($blevel,$stateOnoff,$csat,$crgb,$ceffect,$cspeed,$dname);
  254. return "$name|$response_encode";
  255. }
  256. $stateEffect = "none" if( $ceffect eq "ff" );
  257. ##### Schreiben der neuen Char values
  258. PLAYBULB_gattCharWrite($name,$sat,$rgb,$effect,$speed,$stateEffect,$stateOnoff,$mac,$ac,$ae) if( !defined($dname) );
  259. PLAYBULB_writeDevicename($name,$mac,$adname,$dname) if( defined($dname) );
  260. ##### Statusabruf nach dem schreiben der neuen Char Values
  261. ($cc,$ec,$sat,$rgb,$effect,$speed) = PLAYBULB_gattCharRead($name,$mac,$ac,$ae) if( !defined($dname) );
  262. $dname = PLAYBULB_readDevicename($name,$mac,$adname) if( defined($dname) and $adname ne "none" );
  263. $stateOnoff = PLAYBULB_stateOnOff($cc,$ec) if( !defined($dname) );
  264. ###### Batteriestatus einlesen
  265. $blevel = PLAYBULB_readBattery($name,$mac,$ab) if( $ab ne "none" and !defined($dname) );
  266. Log3 $name, 4, "(Sub PLAYBULB_Run - $name) - Rückgabe an Auswertungsprogramm beginnt";
  267. $response_encode = PLAYBULB_forDone_encodeJSON($blevel,$stateOnoff,$sat,$rgb,$effect,$speed,undef) if( !defined($dname) );
  268. $response_encode = PLAYBULB_forDone_encodeJSON(undef,undef,undef,undef,undef,undef,$dname) if( defined($dname) );
  269. return "$name|$response_encode";
  270. }
  271. Log3 $name, 4, "(Sub PLAYBULB_Run - $name) - Rückgabe an Auswertungsprogramm beginnt";
  272. return "$name|err"
  273. unless( defined($cc) and defined($ec) );
  274. }
  275. sub PLAYBULB_gattCharWrite($$$$$$$$$$) {
  276. my ($name,$sat,$rgb,$effect,$speed,$stateEffect,$stateOnoff,$mac,$ac,$ae) = @_;
  277. my $sshHost = AttrVal($name,"sshHost","none");
  278. my $loop = 0;
  279. if( $sshHost ne 'none') {
  280. while ( (qx(ssh $sshHost 'ps ax | grep -v grep | grep "gatttool -b $mac"') and $loop = 0) or (qx(ssh $sshHost 'ps ax | grep -v grep | grep "gatttool -b $mac"') and $loop < 5) ) {
  281. #printf "\n(Sub PLAYBULB_Run) - gatttool noch aktiv, wait 0.5s for new check\n";
  282. sleep 0.5;
  283. $loop++;
  284. }
  285. } else {
  286. while ( (qx(ps ax | grep -v grep | grep "gatttool -b $mac") and $loop = 0) or (qx(ps ax | grep -v grep | grep "gatttool -b $mac") and $loop < 5) ) {
  287. #printf "\n(Sub PLAYBULB_Run) - gatttool noch aktiv, wait 0.5s for new check\n";
  288. sleep 0.5;
  289. $loop++;
  290. }
  291. }
  292. $speed = "01" if( $effect eq "Candle" );
  293. if( $sshHost ne 'none') {
  294. if( $stateOnoff == 0 ) {
  295. qx(ssh $sshHost 'gatttool -b $mac --char-write -a $ac -n 00000000');
  296. } else {
  297. qx(ssh $sshHost 'gatttool -b $mac --char-write -a $ac -n ${sat}${rgb}') if( $stateEffect eq "none" and $effect eq "none" );
  298. qx(ssh $sshHost 'gatttool -b $mac --char-write -a $ae -n ${sat}${rgb}${effects{$effect}}00${speed}00') if( $stateEffect ne "none" or $effect ne "none" );
  299. }
  300. } else {
  301. if( $stateOnoff == 0 ) {
  302. qx(gatttool -b $mac --char-write -a $ac -n 00000000);
  303. } else {
  304. qx(gatttool -b $mac --char-write -a $ac -n ${sat}${rgb}) if( $stateEffect eq "none" and $effect eq "none" );
  305. qx(gatttool -b $mac --char-write -a $ae -n ${sat}${rgb}${effects{$effect}}00${speed}00) if( $stateEffect ne "none" or $effect ne "none" );
  306. }
  307. }
  308. }
  309. sub PLAYBULB_gattCharRead($$$$) {
  310. my ($name,$mac,$ac,$ae) = @_;
  311. my $sshHost = AttrVal($name,"sshHost","none");
  312. my $loop = 0;
  313. if( $sshHost ne 'none') {
  314. while ( (qx(ssh $sshHost 'ps ax | grep -v grep | grep "gatttool -b $mac"') and $loop = 0) or (qx(ssh $sshHost 'ps ax | grep -v grep | grep "gatttool -b $mac"') and $loop < 5) ) {
  315. #printf "\n(Sub PLAYBULB_Run) - gatttool noch aktiv, wait 0.5s for new check\n";
  316. sleep 0.5;
  317. $loop++;
  318. }
  319. } else {
  320. while ( (qx(ps ax | grep -v grep | grep "gatttool -b $mac") and $loop = 0) or (qx(ps ax | grep -v grep | grep "gatttool -b $mac") and $loop < 5) ) {
  321. #printf "\n(Sub PLAYBULB_Run) - gatttool noch aktiv, wait 0.5s for new check\n";
  322. sleep 0.5;
  323. $loop++;
  324. }
  325. }
  326. my @cc;
  327. my @ec;
  328. if( $sshHost ne 'none') {
  329. @cc = split(": ",qx(ssh $sshHost 'gatttool -b $mac --char-read -a $ac'));
  330. @ec = split(": ",qx(ssh $sshHost 'gatttool -b $mac --char-read -a $ae'));
  331. } else {
  332. @cc = split(": ",qx(gatttool -b $mac --char-read -a $ac));
  333. @ec = split(": ",qx(gatttool -b $mac --char-read -a $ae));
  334. }
  335. return (undef,undef,undef,undef,undef,undef)
  336. unless( defined($cc[1]) and defined($ec[1]) );
  337. my $cc = join("",split(" ",$cc[1]));
  338. my $ec = substr(join("",split(" ",$ec[1])),0,8);
  339. my $sat = hex("0x".substr(join("",split(" ",$ec[1])),0,2));
  340. my $rgb = substr(join("",split(" ",$ec[1])),2,6);
  341. my $effect = $effectsHex{substr(join("",split(" ",$ec[1])),8,2)};
  342. my $speed = hex("0x".substr(join("",split(" ",$ec[1])),12,2));
  343. if( $effect eq "none" ) {
  344. $sat = hex("0x".substr(join("",split(" ",$cc[1])),0,2));
  345. $rgb = substr(join("",split(" ",$cc[1])),2,6);
  346. }
  347. return ($cc,$ec,$sat,$rgb,$effect,$speed);
  348. }
  349. sub PLAYBULB_readBattery($$$) {
  350. my ($name,$mac,$ab) = @_;
  351. my $sshHost = AttrVal($name,"sshHost","none");
  352. my $blevel;
  353. my @blevel;
  354. if( $sshHost ne 'none') {
  355. chomp(@blevel = split(": ",qx(ssh $sshHost 'gatttool -b $mac --char-read -a $ab')));
  356. } else {
  357. chomp(@blevel = split(": ",qx(gatttool -b $mac --char-read -a $ab)));
  358. }
  359. ### Bei den Garden Bulbs gibt es noch den Status wird geladen oder wird nicht geladen
  360. ### Beispielausgabe "Characteristic value/descriptor: 51 01" in diesem Beispiel steht 01 für wird geladen
  361. if( AttrVal($name,'model','none') eq 'BTL400M_v18' or AttrVal($name,'model','none') eq 'BTL400M_v37' ) {
  362. $blevel = substr(join("",split(" ",$blevel[1])),0,4);
  363. } else {
  364. $blevel = substr(join("",split(" ",$blevel[1])),0,2);
  365. }
  366. return ($blevel);
  367. }
  368. sub PLAYBULB_stateOnOff($$) {
  369. my ($cc,$ec) = @_;
  370. my $state;
  371. if( $cc eq "00000000" and $ec eq "00000000" ) {
  372. $state = "0";
  373. } else {
  374. $state = "1";
  375. }
  376. return $state;
  377. }
  378. sub PLAYBULB_readDevicename($$$) {
  379. my ($name,$mac,$adname) = @_;
  380. my $sshHost = AttrVal($name,"sshHost","none");
  381. my @dname;
  382. if( $sshHost ne 'none') {
  383. chomp(@dname = split(": ",qx(ssh $sshHost 'gatttool -b $mac --char-read -a $adname')));
  384. } else {
  385. chomp(@dname = split(": ",qx(gatttool -b $mac --char-read -a $adname)));
  386. }
  387. my $dname = join("",split(" ",$dname[1]));
  388. return pack('H*', $dname);
  389. }
  390. sub PLAYBULB_writeDevicename($$$$) {
  391. my ($name,$mac,$adname,$dname) = @_;
  392. my $sshHost = AttrVal($name,"sshHost","none");
  393. my $hexDname = unpack("H*", $dname);
  394. if( $sshHost ne 'none') {
  395. qx(ssh $sshHost 'gatttool -b $mac --char-write-req -a $adname -n $hexDname');
  396. } else {
  397. qx(gatttool -b $mac --char-write-req -a $adname -n $hexDname);
  398. }
  399. }
  400. sub PLAYBULB_forRun_encodeJSON($$$$$$$$$$$$$) {
  401. my ($cmd,$mac,$stateOnoff,$sat,$rgb,$effect,$speed,$stateEffect,$ac,$ae,$ab,$adname,$dname) = @_;
  402. my %data = (
  403. 'cmd' => $cmd,
  404. 'mac' => $mac,
  405. 'stateOnoff' => $stateOnoff,
  406. 'sat' => $sat,
  407. 'rgb' => $rgb,
  408. 'effect' => $effect,
  409. 'speed' => $speed,
  410. 'stateEffect' => $stateEffect,
  411. 'ac' => $ac,
  412. 'ae' => $ae,
  413. 'ab' => $ab,
  414. 'adname' => $adname,
  415. 'dname' => $dname
  416. );
  417. return encode_json \%data;
  418. }
  419. sub PLAYBULB_forDone_encodeJSON($$$$$$$) {
  420. my ($blevel,$stateOnoff,$sat,$rgb,$effect,$speed,$dname) = @_;
  421. my %response = (
  422. 'blevel' => $blevel,
  423. 'stateOnoff' => $stateOnoff,
  424. 'sat' => $sat,
  425. 'rgb' => $rgb,
  426. 'effect' => $effect,
  427. 'speed' => $speed,
  428. 'dname' => $dname
  429. );
  430. return encode_json \%response;
  431. }
  432. sub PLAYBULB_BlockingDone($) {
  433. my ($string) = @_;
  434. my ($name,$response) = split("\\|",$string);
  435. my $hash = $defs{$name};
  436. my $state;
  437. my $color;
  438. my $powerLevel;
  439. my $powerCharge;
  440. delete($hash->{helper}{RUNNING_PID});
  441. Log3 $name, 3, "(Sub PLAYBULB_Done - $name) - Der Helper ist diabled. Daher wird hier abgebrochen" if($hash->{helper}{DISABLED});
  442. return if($hash->{helper}{DISABLED});
  443. if( $response eq "err" ) {
  444. readingsSingleUpdate($hash,"state","unreachable", 1);
  445. return undef;
  446. }
  447. my $response_json = decode_json($response);
  448. if( !defined($hash->{helper}{setDeviceName}) ) {
  449. if( $response_json->{stateOnoff} == 1 ) { $state = "on" } else { $state = "off" } ;
  450. if( ($response_json->{sat} eq "255" and $response_json->{rgb} eq "000000") or
  451. ($response_json->{sat} eq "0" and $response_json->{rgb} eq "ffffff") ) {
  452. $color = "off"; } else { $color = "on"; }
  453. }
  454. if( AttrVal($name,'model','none') eq 'BTL400M_v18' or AttrVal($name,'model','none') eq 'BTL400M_v37' ) {
  455. $powerLevel = hex(substr($response_json->{blevel},0,2));
  456. $powerCharge = hex(substr($response_json->{blevel},3,3));
  457. } else {
  458. $powerLevel = hex($response_json->{blevel});
  459. }
  460. readingsBeginUpdate($hash);
  461. readingsBulkUpdate($hash, "color", "$color") if( !defined($hash->{helper}{setDeviceName}) );
  462. readingsBulkUpdate($hash, "powerLevel", $powerLevel) if( $powerLevel != 1000 and !defined($hash->{helper}{setDeviceName}) );
  463. readingsBulkUpdate($hash, "powerCharge", $powerCharge) if( $powerLevel != 1000 and !defined($hash->{helper}{setDeviceName}) and defined($powerCharge) );
  464. readingsBulkUpdate($hash, "deviceName", $response_json->{dname});
  465. readingsBulkUpdate($hash, "onoff", $response_json->{stateOnoff});
  466. readingsBulkUpdate($hash, "sat", $response_json->{sat}) if( $response_json->{stateOnoff} != 0 and $color ne "off" );
  467. readingsBulkUpdate($hash, "rgb", $response_json->{rgb}) if( $response_json->{stateOnoff} != 0 and $color ne "off" and !defined($hash->{helper}{setDeviceName}) );
  468. readingsBulkUpdate($hash, "effect", $response_json->{effect});
  469. readingsBulkUpdate($hash, "speed", $response_json->{speed});
  470. readingsBulkUpdate($hash, "state", $state);
  471. readingsEndUpdate($hash,1);
  472. $hash->{helper}{onoff} = $response_json->{stateOnoff};
  473. $hash->{helper}{sat} = $response_json->{sat} if( $response_json->{stateOnoff} != 0 and $color ne "off" );
  474. $hash->{helper}{rgb} = $response_json->{rgb} if( $response_json->{stateOnoff} != 0 and $color ne "off" and !defined($hash->{helper}{setDeviceName}) );
  475. $hash->{helper}{effect} = $response_json->{effect};
  476. $hash->{helper}{speed} = $response_json->{speed};
  477. delete $hash->{helper}{setDeviceName} if( defined($hash->{helper}{setDeviceName}) );
  478. Log3 $name, 4, "(Sub PLAYBULB_Done - $name) - Abschluss!";
  479. }
  480. sub PLAYBULB_BlockingAborted($) {
  481. my ($hash) = @_;
  482. my $name = $hash->{NAME};
  483. delete($hash->{helper}{RUNNING_PID});
  484. readingsSingleUpdate($hash,"state","unreachable", 1);
  485. Log3 $name, 4, "($name) - The BlockingCall Process terminated unexpectedly. Timedout";
  486. }
  487. 1;
  488. =pod
  489. =item device
  490. =item summary Modul to control MiPow Playbulb products
  491. =item summary_DE Modul zum steuern der MiPow Playbulb Produkte
  492. =begin html
  493. <a name="PLAYBULB"></a>
  494. <h3>MiPow Playbulb</h3>
  495. <p><span style="text-decoration: underline;"><strong>PLAYBULB -Smart Light from MIPOW.COM</strong></span></p>
  496. <p>This module integrates different smart lights from MIPOW into FHEM and displays several settings</p>
  497. <p>&nbsp;</p>
  498. <p><a name="PLAYBULBdefine"></a><strong>Define</strong><br /><code>define &lt;name&gt; PLAYBULB &lt;MAC-ADDRESS&gt;</code></p>
  499. <p>Example: <code>define PlaybulbCandle PLAYBULB 0B:0B:0C:D0:E0:F0</code></p>
  500. <p>&nbsp;</p>
  501. <p>With this command a new PLAYBULB device in a room called PLAYBULB is created. The parameter &lt;MAC-ADDRESS&gt; defines the bluetooth MAC address of your mipow smart light.</p>
  502. <p>&nbsp;</p>
  503. <p style="padding-left: 90px;"><strong>pre-requesites</strong>: a BT LE 4.0 key and a working bluez installation or equivalent is necessary. (find a good guide at <a href="http://www.elinux.org/RPi_Bluetooth_LE)">http://www.elinux.org/RPi_Bluetooth_LE)</a></p>
  504. <p style="padding-left: 90px;">It seems like the devices accept only one active connection.</p>
  505. <p><br /><a name="PLAYBULBreadings"></a><strong>Readings</strong></p>
  506. <ul>
  507. <ul>
  508. <ul>
  509. <li>powerLevel - percentage of batterylevel</li>
  510. <li>powerCharge - state of charging (Playbulb Garden only)</li>
  511. <li>color - indicates if colormode is on or off</li>
  512. <li>devicename - given name of the device</li>
  513. <li>effect - indicates which effect is selected (Flash; Pulse; RainbowJump; RainbowFade; Candle; none)</li>
  514. <li>onoff - indicates if the device is turned on (1) or off (0)</li>
  515. <li>rgb - shows the selected color in hex by rgb (example: FF0000 is full red)</li>
  516. <li>sat - shows the selected saturation from 0 to 255 (seems to be inverted; 0 is full saturation)</li>
  517. <li>speed - shows the selected effect speed (possible: 20; 70; 120; 170)</li>
  518. <li>state - current state of PLAYBULB device</li>
  519. </ul>
  520. </ul>
  521. </ul>
  522. <p><a name="PLAYBULBset"></a><strong>Set</strong></p>
  523. <ul>
  524. <ul>
  525. <ul>
  526. <li>on - turns device on</li>
  527. <li>off - turns device off</li>
  528. <li>rgb - colorpicker,RGB; gives the possibility to set any hue, saturation, brightness</li>
  529. <li>sat - saturation slider from 0 to 255 steps 5</li>
  530. <li>effect - Flash,Pulse,RainbowJump,RainbowFade,Candle,none; activates the selected effect</li>
  531. <li>speed - slider: values are 170, 120, 70, 20</li>
  532. <li>color - on,of; switches the device to rgb or white</li>
  533. <li>statusRequest - is necessary to request state of the device</li>
  534. <li>deviceName - changes the name of the bluetoothdevice e.g. "PlaybulbCandle"</li>
  535. </ul>
  536. </ul>
  537. </ul>
  538. <p><br /><strong>Get na</strong></p>
  539. <p>&nbsp;</p>
  540. <p><strong>Attributes&nbsp; </strong></p>
  541. <ul>
  542. <ul>
  543. <ul>
  544. <li>model<br />BTL300_v5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Candle Firmware 5<br />BTL300_v6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Candle Firmware 6<br />BTL201_v2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Smart<br />BTL201M_V16&nbsp; # Smart (1/2017)<br />BTL505_v1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Stripe<br />BTL400M_v18&nbsp; # Garden<br />BTL100C_v10&nbsp; # Color LED</li>
  545. <li>sshHost - IP or FQDN for SSH remote control</li>
  546. </ul>
  547. </ul>
  548. </ul>
  549. <p><br /><a name="PLAYBULBstate"></a><strong>state</strong></p>
  550. <ul>
  551. <ul>
  552. <ul>
  553. <li>set attribut model&nbsp;- is shown after initial define</li>
  554. <li>on - device is on</li>
  555. <li>off - device is off</li>
  556. <li>unreachable - connection to device lost</li>
  557. </ul>
  558. </ul>
  559. </ul>
  560. <p><br /><br /><br /><span style="text-decoration: underline;"><strong>Further examples and readings:</strong></span><br /><a href="https://forum.fhem.de/index.php/topic,60829.msg522226.html#msg522226">Forum thread (german only)</a><br /><br /></p>
  561. =end html
  562. =begin html_DE
  563. <a name="PLAYBULB"></a>
  564. <h3>MiPow Playbulb</h3>
  565. <p><span style="text-decoration: underline;"><strong>PLAYBULB -Smart Light von MIPOW.COM</strong></span></p>
  566. <p>Dieses Modul integriert diverse Smart Leuchten von MIPOW in FHEM und zeigt ihre Einstellungen an.</p>
  567. <p>&nbsp;</p>
  568. <p><a name="PLAYBULBdefine"></a><strong>Define</strong><br /><code>define &lt;name&gt; PLAYBULB &lt;MAC-ADDRESS&gt;</code></p>
  569. <p>Beispiel: <code>define PlaybulbCandle PLAYBULB 0B:0B:0C:D0:E0:F0</code></p>
  570. <p>&nbsp;</p>
  571. <p>Mit diesem Kommando wird ein neues PLAYBULB Device im Raum PLAYBULB angelegt. Der Parameter &lt;MAC-ADDRESS&gt; definiert die Bluetooth MAC Address der Mipow Smart Leuchte.</p>
  572. <p>&nbsp;</p>
  573. <p style="padding-left: 90px;"><strong>Vorbedingungen</strong>: Es wird ein Bluetooth LE 4.0 Stick, sowie eine funktionierende bluez Installation oder aehnlich vorausgesetzt. (Eine gute Anleitung findedsich hier: <a href="http://www.elinux.org/RPi_Bluetooth_LE)">http://www.elinux.org/RPi_Bluetooth_LE)</a></p>
  574. <p style="padding-left: 90px;">Derzeit sieht es so aus, als ob die Devices nur eine aktive Verbindung akzeptieren.</p>
  575. <p><br /><a name="PLAYBULBreadings"></a><strong>Readings</strong></p>
  576. <ul>
  577. <ul>
  578. <ul>
  579. <li>battery - Batterielevel in Prozent</li>
  580. <li>color - Zeigt an ob der Farbmodus an oder aus ist</li>
  581. <li>devicename - Geraetename</li>
  582. <li>effect - Zeigt an, welcher effect ausgewaehlt ist: (Flash; Pulse; RainbowJump; RainbowFade; Candle; none)</li>
  583. <li>onoff - Zeigt an, ob das Geraet an (1) oder aus (0) ist</li>
  584. <li>rgb - Zeigt die gewaehlte Farbe in HEX vom Typ rgb (Beispiel: FF0000 ist satt rot)</li>
  585. <li>sat - Zeigt die gewaehlte Saettigung von 0 bis 255 (scheint invertiert zu sein; 0 ist volle Saettigung)</li>
  586. <li>speed - Zeigt die gewaehlte Effektgeschwindigkeit (moeglich sind: 20; 70; 120; 170)</li>
  587. <li>state - Aktueller Zustand des Devices</li>
  588. </ul>
  589. </ul>
  590. </ul>
  591. <p><a name="PLAYBULBset"></a><strong>Set</strong></p>
  592. <ul>
  593. <ul>
  594. <ul>
  595. <li>on - Schaltet das Geraet ein</li>
  596. <li>off - Schaltet das Geraet aus</li>
  597. <li>rgb - Farbwaehler,RGB; ermoeglicht jede Farbe, Saettigung und Helligkeit einzustellen</li>
  598. <li>sat - Schieber zur Einstellung der Saettigungvon 0 bis 255, Schrittweite 5</li>
  599. <li>effect - Flash,Pulse,RainbowJump,RainbowFade,Candle,none; aktiviert den gewaehlten Effekt</li>
  600. <li>speed - Schieberegler: Werte sind 170, 120, 70, 20</li>
  601. <li>color - on,of; Schaltet das Geraet auf RGB oder weiss</li>
  602. <li>statusRequest - Ist notwendig, um den Zustand des Geraetes abzufragen</li>
  603. <li>deviceName - Aendert den Namen des Bluetoothdevice z.B. "PlaybulbCandle"</li>
  604. </ul>
  605. </ul>
  606. </ul>
  607. <p><br /><strong>Get na</strong></p>
  608. <p>&nbsp;</p>
  609. <p><strong>Attributes&nbsp; </strong></p>
  610. <ul>
  611. <ul>
  612. <ul>
  613. <li>model<br />BTL300_v5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Candle Firmware 5<br />BTL300_v6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Candle Firmware 6<br />BTL201_v2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Smart<br />BTL201M_V16&nbsp; # Smart (1/2017)<br />BTL505_v1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Stripe<br />BTL400M_v18&nbsp; # Garden<br />BTL100C_v10&nbsp; # Color LED</li>
  614. <li>sshHost - IP oder FQDN für SSH remote Kontrolle</li>
  615. </ul>
  616. </ul>
  617. </ul>
  618. <p><br /><a name="PLAYBULBstate"></a><strong>state</strong></p>
  619. <ul>
  620. <ul>
  621. <ul>
  622. <li>set attribut model&nbsp;- wird nach der Erstdefinition angezeigt</li>
  623. <li>on - Device ist an</li>
  624. <li>off - Device ist aus</li>
  625. <li>unreachable - Verbindung zum device verloren</li>
  626. </ul>
  627. </ul>
  628. </ul>
  629. <p><br /><br /><br /><span style="text-decoration: underline;"><strong>Weitere Beispiel und readings:</strong></span><br /><a href="https://forum.fhem.de/index.php/topic,60829.msg522226.html#msg522226">Forum thread</a></p>
  630. <p>&nbsp;</p>
  631. =end html_DE
  632. =cut