20_FRM_RGB.pm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. ##############################################
  2. # $Id: 20_FRM_RGB.pm 5927 2014-05-21 21:56:37Z ntruchsess $
  3. ##############################################
  4. package main;
  5. use vars qw{%attr %defs $readingFnAttributes};
  6. use strict;
  7. use warnings;
  8. #add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
  9. BEGIN {
  10. if (!grep(/FHEM\/lib$/,@INC)) {
  11. foreach my $inc (grep(/FHEM$/,@INC)) {
  12. push @INC,$inc."/lib";
  13. };
  14. };
  15. };
  16. use Device::Firmata::Constants qw/ :all /;
  17. use Color qw/ :all /;
  18. use SetExtensions qw/ :all /;
  19. #####################################
  20. my %gets = (
  21. "rgb" => 0,
  22. "RGB" => 0,
  23. "pct" => 0,
  24. "devStateIcon" => 0,
  25. );
  26. my %sets = (
  27. "on" => 0,
  28. "off" => 0,
  29. "toggle" => 0,
  30. "rgb:colorpicker,RGB" => 1,
  31. "pct:slider,0,1,100" => 1,
  32. "fadeTo" => 2,
  33. "dimUp" => 0,
  34. "dimDown" => 0,
  35. );
  36. sub
  37. FRM_RGB_Initialize($)
  38. {
  39. my ($hash) = @_;
  40. $hash->{SetFn} = "FRM_RGB_Set";
  41. $hash->{GetFn} = "FRM_RGB_Get";
  42. $hash->{DefFn} = "FRM_RGB_Define";
  43. $hash->{InitFn} = "FRM_RGB_Init";
  44. $hash->{UndefFn} = "FRM_Client_Undef";
  45. $hash->{AttrFn} = "FRM_RGB_Attr";
  46. $hash->{StateFn} = "FRM_RGB_State";
  47. $hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off IODev loglevel:0,1,2,3,4,5 $readingFnAttributes";
  48. LoadModule("FRM");
  49. FHEM_colorpickerInit();
  50. }
  51. sub
  52. FRM_RGB_Define($$)
  53. {
  54. my ($hash, $def) = @_;
  55. $attr{$hash->{NAME}}{webCmd} = "rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:toggle:on:off";
  56. return FRM_Client_Define($hash,$def);
  57. }
  58. sub
  59. FRM_RGB_Init($$)
  60. {
  61. my ($hash,$args) = @_;
  62. my $name = $hash->{NAME};
  63. my $ret = FRM_Init_Pin_Client($hash,$args,PIN_PWM);
  64. return $ret if (defined $ret);
  65. my @pins = ();
  66. eval {
  67. my $firmata = FRM_Client_FirmataDevice($hash);
  68. $hash->{PIN} = "";
  69. foreach my $pin (@{$args}) {
  70. $firmata->pin_mode($pin,PIN_PWM);
  71. push @pins,{
  72. pin => $pin,
  73. "shift" => defined $firmata->{metadata}{pwm_resolutions} ? $firmata->{metadata}{pwm_resolutions}{$pin}-8 : 0,
  74. };
  75. $hash->{PIN} .= $hash->{PIN} eq "" ? $pin : " $pin";
  76. }
  77. $hash->{PINS} = \@pins;
  78. };
  79. if ($@) {
  80. $@ =~ /^(.*)( at.*FHEM.*)$/;
  81. $hash->{STATE} = "error initializing: ".$1;
  82. return "error initializing '".$hash->{NAME}."': ".$1;
  83. }
  84. if (! (defined AttrVal($name,"stateFormat",undef))) {
  85. $attr{$name}{"stateFormat"} = "rgb";
  86. }
  87. my $value = ReadingsVal($name,"rgb",undef);
  88. if (defined $value and AttrVal($hash->{NAME},"restoreOnReconnect","on") eq "on") {
  89. FRM_RGB_Set($hash,$name,"rgb",$value);
  90. }
  91. $hash->{toggle} = "off";
  92. $hash->{".dim"} = {
  93. bri => 50,
  94. channels => [(255) x @{$hash->{PINS}}],
  95. };
  96. readingsSingleUpdate($hash,"state","Initialized",1);
  97. return undef;
  98. }
  99. sub
  100. FRM_RGB_Set($@)
  101. {
  102. my ($hash, $name, $cmd, @a) = @_;
  103. my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets );
  104. #-- check argument
  105. return SetExtensions($hash, join(" ", keys %sets), $name, $cmd, @a) unless @match == 1;
  106. return "$cmd expects $sets{$match[0]} parameters" unless (@a eq $sets{$match[0]});
  107. eval {
  108. SETHANDLER: {
  109. $cmd eq "on" and do {
  110. FRM_RGB_SetChannels($hash,(0xFF) x scalar(@{$hash->{PINS}}));
  111. $hash->{toggle} = "on";
  112. last;
  113. };
  114. $cmd eq "off" and do {
  115. FRM_RGB_SetChannels($hash,(0x00) x scalar(@{$hash->{PINS}}));
  116. $hash->{toggle} = "off";
  117. last;
  118. };
  119. $cmd eq "toggle" and do {
  120. my $toggle = $hash->{toggle};
  121. TOGGLEHANDLER: {
  122. $toggle eq "off" and do {
  123. $hash->{toggle} = "up";
  124. FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
  125. last;
  126. };
  127. $toggle eq "up" and do {
  128. FRM_RGB_SetChannels($hash,(0xFF) x @{$hash->{PINS}});
  129. $hash->{toggle} = "on";
  130. last;
  131. };
  132. $toggle eq "on" and do {
  133. $hash->{toggle} = "down";
  134. FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
  135. last;
  136. };
  137. $toggle eq "down" and do {
  138. FRM_RGB_SetChannels($hash,(0x0) x @{$hash->{PINS}});
  139. $hash->{toggle} = "off";
  140. last;
  141. };
  142. };
  143. last;
  144. };
  145. $cmd eq "rgb" and do {
  146. my $arg = $a[0];
  147. my $numPins = scalar(@{$hash->{PINS}});
  148. my $nybles = $numPins << 1;
  149. die "$arg is not the right format" unless( $arg =~ /^[\da-f]{$nybles}$/i );
  150. my @channels = RgbToChannels($arg,$numPins);
  151. FRM_RGB_SetChannels($hash,@channels);
  152. RGBHANDLER: {
  153. $arg =~ /^0{$nybles}$/ and do {
  154. $hash->{toggle} = "off";
  155. last;
  156. };
  157. $arg =~ /^f{$nybles}$/i and do {
  158. $hash->{toggle} = "on";
  159. last;
  160. };
  161. $hash->{toggle} = "up";
  162. };
  163. $hash->{".dim"} = ChannelsToBrightness(@channels);
  164. last;
  165. };
  166. $cmd eq "pct" and do {
  167. $hash->{".dim"}->{bri} = $a[0];
  168. FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
  169. last;
  170. };
  171. $cmd eq "dimUp" and do {
  172. $hash->{".dim"}->{bri} = $hash->{".dim"}->{bri} > 90 ? 100 : $hash->{".dim"}->{bri}+10;
  173. FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
  174. last;
  175. };
  176. $cmd eq "dimDown" and do {
  177. $hash->{".dim"}->{bri} = $hash->{".dim"}->{bri} < 10 ? 0 : $hash->{".dim"}->{bri}-10;
  178. FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
  179. last;
  180. };
  181. }
  182. };
  183. if ($@) {
  184. $@ =~ /^(.*)( at.*FHEM.*)$/;
  185. $hash->{STATE} = "error setting '$cmd': ".(defined $1 ? $1 : $@);
  186. return "error setting '$hash->{NAME} $cmd': ".(defined $1 ? $1 : $@);
  187. }
  188. return undef;
  189. }
  190. sub
  191. FRM_RGB_Get($@)
  192. {
  193. my ($hash, $name, $cmd, @a) = @_;
  194. return "FRM_RGB: Get with unknown argument $cmd, choose one of ".join(" ", sort keys %gets)
  195. unless defined($gets{$cmd});
  196. GETHANDLER: {
  197. $cmd eq 'rgb' and do {
  198. return ReadingsVal($name,"rgb",undef);
  199. };
  200. $cmd eq 'RGB' and do {
  201. return ChannelsToRgb(@{$hash->{".dim"}->{channels}});
  202. };
  203. $cmd eq 'pct' and do {
  204. return $hash->{".dim"}->{bri};
  205. return undef;
  206. };
  207. }
  208. }
  209. sub
  210. FRM_RGB_SetChannels($$)
  211. {
  212. my ($hash,@channels) = @_;
  213. my $firmata = FRM_Client_FirmataDevice($hash);
  214. my @pins = @{$hash->{PINS}};
  215. my @values = @channels;
  216. while(@values) {
  217. my $pin = shift @pins;
  218. my $value = shift @values;
  219. if ($pin->{"shift"} < 0) {
  220. $value >>= -$pin->{"shift"};
  221. } else {
  222. $value <<= $pin->{"shift"};
  223. }
  224. $firmata->analog_write($pin->{pin},$value);
  225. };
  226. readingsBeginUpdate($hash);
  227. readingsBulkUpdate($hash,"rgb",ChannelsToRgb(@channels),1);
  228. readingsBulkUpdate($hash,"pct",(ChannelsToBrightness(@channels))->{bri},1);
  229. readingsEndUpdate($hash, 1);
  230. }
  231. sub
  232. FRM_RGB_State($$$$)
  233. {
  234. my ($hash, $tim, $sname, $sval) = @_;
  235. if ($sname eq "rgb") {
  236. FRM_RGB_Set($hash,$hash->{NAME},$sname,$sval);
  237. }
  238. }
  239. sub
  240. FRM_RGB_Attr($$$$)
  241. {
  242. my ($command,$name,$attribute,$value) = @_;
  243. my $hash = $main::defs{$name};
  244. eval {
  245. if ($command eq "set") {
  246. ARGUMENT_HANDLER: {
  247. $attribute eq "IODev" and do {
  248. if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $value)) {
  249. FRM_Client_AssignIOPort($hash,$value);
  250. FRM_Init_Client($hash) if (defined ($hash->{IODev}));
  251. }
  252. last;
  253. };
  254. }
  255. }
  256. };
  257. if ($@) {
  258. $@ =~ /^(.*)( at.*FHEM.*)$/;
  259. $hash->{STATE} = "error setting $attribute to $value: ".$1;
  260. return "cannot $command attribute $attribute to $value for $name: ".$1;
  261. }
  262. }
  263. 1;
  264. =pod
  265. =begin html
  266. <a name="FRM_RGB"></a>
  267. <h3>FRM_RGB</h3>
  268. <ul>
  269. allows to drive LED-controllers and other multichannel-devices that use PWM as input by an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
  270. <br>
  271. The value set will be output by the specified pins as pulse-width-modulated signals.<br>
  272. Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
  273. <a name="FRM_RGBdefine"></a>
  274. <b>Define</b>
  275. <ul>
  276. <code>define &lt;name&gt; FRM_RGB &lt;pin&gt; &lt;pin&gt; &lt;pin&gt; [pin...]</code> <br>
  277. Defines the FRM_RGB device. &lt;pin&gt> are the arduino-pin to use.<br>
  278. For rgb-controlled devices first pin drives red, second pin green and third pin blue.
  279. </ul>
  280. <br>
  281. <a name="FRM_RGBset"></a>
  282. <b>Set</b><br>
  283. <ul>
  284. <code>set &lt;name&gt; on</code><br>
  285. sets the pulse-width of all configured pins to 100%</ul><br>
  286. <ul>
  287. <code>set &lt;name&gt; off</code><br>
  288. sets the pulse-width of all configured pins to 0%</ul><br>
  289. <ul>
  290. <a href="#setExtensions">set extensions</a> are supported</ul><br>
  291. <ul>
  292. <code>set &lt;name&gt; toggle</code><br>
  293. toggles in between the last dimmed value, 0% and 100%. If no dimmed value was set before defaults to pulsewidth 50% on all channels</ul><br>
  294. <ul>
  295. <code>set &lt;name&gt; rgb &lt;value&gt;</code><br>
  296. sets the pulse-width of all channels at once. Also sets the value toggle can switch to<br>
  297. Value is encoded as hex-string, 2-digigs per channel (e.g. FFFFFF for reguler rgb)</ul><br>
  298. <ul>
  299. <code>set &lt;name&gt; pct &lt;value&gt;</code><br>
  300. dims all channels at once while leving the ratio in between the channels unaltered.<br>
  301. Range is 0-100 ('pct' stands for 'percent')</ul><br>
  302. <ul>
  303. <code>set &lt;name&gt; dimUp</code><br>
  304. dims up by 10%</ul><br>
  305. <ul>
  306. <code>set &lt;name&gt; dimDown</code><br>
  307. dims down by 10%</ul><br>
  308. <a name="FRM_RGBget"></a>
  309. <b>Get</b><br>
  310. <ul>
  311. <code>get &lt;name&gt; rgb</code><br>
  312. returns the values set for all channels. Format is hex, 2 nybbles per channel.
  313. </ul><br>
  314. <ul>
  315. <code>get &lt;name&gt; RGB</code><br>
  316. returns the values set for all channels in normalized format. Format is hex, 2 nybbles per channel.
  317. Values are scaled such that the channel with the highest value is set to FF. The real values are calculated
  318. by multipying each byte with the value of 'pct'.
  319. </ul><br>
  320. <ul>
  321. <code>get &lt;name&gt; pct</code><br>
  322. returns the value of the channel with the highest value scaled to the range of 0-100 (percent).
  323. </ul><br>
  324. <a name="FRM_RGBattr"></a>
  325. <b>Attributes</b><br>
  326. <ul>
  327. <li>restoreOnStartup &lt;on|off&gt;</li>
  328. <li>restoreOnReconnect &lt;on|off&gt;</li>
  329. <li><a href="#IODev">IODev</a><br>
  330. Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
  331. than one FRM-device defined.)
  332. </li>
  333. <li><a href="#eventMap">eventMap</a><br></li>
  334. <li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
  335. </ul>
  336. </ul>
  337. <br>
  338. =end html
  339. =cut