30_pilight_dimmer.pm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. ##############################################
  2. # $Id: 30_pilight_dimmer.pm 10506 2016-01-14 20:40:45Z risiko79 $
  3. #
  4. # Usage
  5. #
  6. # define <name> pilight_dimmer <protocol> <id> <unit> [protocol]
  7. #
  8. # Changelog
  9. #
  10. # V 0.10 2015-02-26 - initial beta version
  11. # V 0.50 2015-05-20 - NEW: handle screen messages (up,down)
  12. # V 0.50 2015-05-20 - NEW: max dimlevel for gui and device
  13. # V 0.51 2015-05-21 - CHG: modifications for dimers without dimlevel
  14. # V 0.52 2015-05-25 - CHG: attributes dimlevel_on, dimlevel_off
  15. # V 0.53 2015-05-30 - FIX: set dimlevel 0
  16. # V 0.54 2015-05-30 - FIX: StateFn
  17. # V 0.55 2015-07-27 - NEW: SetExtensions on-for-timer
  18. # V 0.56 2015-12-17 - NEW: Attribut IODev to switch IO-Device
  19. ##############################################
  20. package main;
  21. use strict;
  22. use warnings;
  23. use Time::HiRes qw(gettimeofday);
  24. use JSON;
  25. use Switch; #libswitch-perl
  26. use SetExtensions;
  27. sub pilight_dimmer_Initialize($)
  28. {
  29. my ($hash) = @_;
  30. $hash->{DefFn} = "pilight_dimmer_Define";
  31. $hash->{Match} = "^PISWITCH|^PIDIMMER|^PISCREEN";
  32. $hash->{ParseFn} = "pilight_dimmer_Parse";
  33. $hash->{SetFn} = "pilight_dimmer_Set";
  34. $hash->{StateFn} = "pilight_dimmer_State";
  35. $hash->{AttrList} = "dimlevel_max dimlevel_step dimlevel_max_device dimlevel_on dimlevel_off IODev ".$readingFnAttributes;
  36. }
  37. #####################################
  38. sub pilight_dimmer_Define($$)
  39. {
  40. my ($hash, $def) = @_;
  41. my @a = split("[ \t][ \t]*", $def);
  42. if(@a < 5) {
  43. my $msg = "wrong syntax: define <name> pilight_dimmer <protocol> <id> <unit> [protocol]";
  44. Log3 undef, 2, $msg;
  45. return $msg;
  46. }
  47. my $me = $a[0];
  48. my $protocol = $a[2];
  49. my $id = $a[3];
  50. my $unit = $a[4];
  51. $hash->{STATE} = "defined";
  52. $hash->{PROTOCOL} = lc($protocol);
  53. $hash->{ID} = $id;
  54. $hash->{UNIT} = $unit;
  55. $hash->{PROTOCOL2} = lc($a[5]) if (@a == 6);
  56. $hash->{helper}{OWN_DIM} = 1;
  57. $hash->{helper}{OWN_DIM} = 0 if ($hash->{PROTOCOL} =~ /screen/);
  58. $hash->{helper}{ISSCREEN} = 1 if ($hash->{PROTOCOL} =~ /screen/ or (defined($hash->{PROTOCOL2}) and $hash->{PROTOCOL2}) =~ /screen/);
  59. #$attr{$me}{verbose} = 5;
  60. $modules{pilight_dimmer}{defptr}{lc($id)}{$me} = $hash;
  61. AssignIoPort($hash);
  62. return undef;
  63. }
  64. ###########################################
  65. sub pilight_dimmer_Parse($$)
  66. {
  67. my ($mhash, $rmsg, $rawdata) = @_;
  68. my $backend = $mhash->{NAME};
  69. Log3 $backend, 4, "pilight_dimmer_Parse: RCV -> $rmsg";
  70. my ($dev,$protocol,$id,$unit,$state,$dimlevel) = split(",",$rmsg);
  71. return () if($dev !~ m/PISWITCH|PIDIMMER|PISCREEN/);
  72. my $chash;
  73. foreach my $n (keys %{ $modules{pilight_dimmer}{defptr}{lc($id)} }) {
  74. my $lh = $modules{pilight_dimmer}{defptr}{$id}{$n};
  75. next if ( !defined($lh->{UNIT}) );
  76. if ($lh->{ID} eq $id && $lh->{UNIT} eq $unit) {
  77. $chash = $lh;
  78. last;
  79. }
  80. }
  81. return () if (!defined($chash->{NAME}));
  82. my $dimlevel_max_dev = AttrVal($chash->{NAME}, "dimlevel_max_device",15);
  83. my $dimlevel_max = AttrVal($chash->{NAME}, "dimlevel_max",$dimlevel_max_dev);
  84. my $dimlevel_step = AttrVal($chash->{NAME}, "dimlevel_step",1);
  85. my $dimlevel_old = ReadingsVal($chash->{NAME},"dimlevel",0);
  86. my $state_old = ReadingsVal($chash->{NAME},"state",0);
  87. Log3 $chash->{NAME}, 4, "pilight_dimmer_Parse: RCV -> $rmsg";
  88. if ($state eq "up") {
  89. $dimlevel = $dimlevel_old + $dimlevel_step;
  90. $dimlevel = $dimlevel_max if ($dimlevel > $dimlevel_max);
  91. $state="on";
  92. }
  93. if ($state eq "down") {
  94. $dimlevel = $dimlevel_old - $dimlevel_step;
  95. $state="on";
  96. if ($dimlevel <= 0) {
  97. $state="off";
  98. $dimlevel= AttrVal($chash->{NAME}, "dimlevel_off",0);
  99. }
  100. }
  101. readingsBeginUpdate($chash);
  102. readingsBulkUpdate($chash,"state",$state) if ("$state_old" ne "$state");
  103. if (defined($dimlevel)) {
  104. $chash->{helper}{DEV_DIMLEVEL} = $dimlevel;
  105. Log3 $chash->{NAME}, 5, "pilight_dimmer_Parse: $dimlevel $dimlevel_max_dev $dimlevel_max";
  106. $dimlevel = $dimlevel / $dimlevel_max_dev * $dimlevel_max;
  107. Log3 $chash->{NAME}, 5, "pilight_dimmer_Parse: $dimlevel_old $dimlevel";
  108. $dimlevel = int($dimlevel+0.5);
  109. Log3 $chash->{NAME}, 5, "pilight_dimmer_Parse: $dimlevel_old round $dimlevel";
  110. readingsBulkUpdate($chash,"dimlevel",$dimlevel) if ($dimlevel_old != $dimlevel);
  111. }
  112. readingsEndUpdate($chash, 1);
  113. return $chash->{NAME};
  114. }
  115. #####################################
  116. sub pilight_dimmer_Write($$$)
  117. {
  118. my ($hash, $set, $dimlevel) = @_;
  119. my $me = $hash->{NAME};
  120. my $proto = $hash->{PROTOCOL};
  121. if ($set =~ /up|down/ and $proto !~ /screen/) {
  122. $proto = $hash->{PROTOCOL2} if (defined($hash->{PROTOCOL2}));
  123. }
  124. if ($set =~ /on|off/ and $proto =~ /screen/) {
  125. $proto = $hash->{PROTOCOL2} if (defined($hash->{PROTOCOL2}));
  126. }
  127. my $msg = "$me,$set";
  128. $msg = $msg.",".$dimlevel if (defined($dimlevel));
  129. my $help = $hash->{PROTOCOL};
  130. $hash->{PROTOCOL} = $proto;
  131. Log3 $me, 4, "$me(Set): [$proto] $msg";
  132. IOWrite($hash, $msg);
  133. $hash->{PROTOCOL} = $help;
  134. return undef;
  135. }
  136. #####################################
  137. sub pilight_dimmer_ConvDimToDev($$)
  138. {
  139. my ($me,$dimlevel_gui) = @_;
  140. my $dimlevel_max_dev = AttrVal($me, "dimlevel_max_device",15);
  141. my $dimlevel_max = AttrVal($me, "dimlevel_max",$dimlevel_max_dev);
  142. my $dimlevel = $dimlevel_gui / $dimlevel_max * $dimlevel_max_dev;
  143. $dimlevel = int($dimlevel + 0.5);
  144. return $dimlevel;
  145. }
  146. #####################################
  147. sub pilight_dimmer_Set($$)
  148. {
  149. my ($hash, $me, $cmd, @a) = @_;
  150. return "no set value specified" unless defined($cmd);
  151. my $dimlevel_max_dev = AttrVal($me, "dimlevel_max_device",15);
  152. my $dimlevel_step = AttrVal($me, "dimlevel_step",1);
  153. my $dimlevel_max = AttrVal($me, "dimlevel_max",$dimlevel_max_dev);
  154. my %sets = ("on:noArg"=>0, "off:noArg"=>0);
  155. $sets{"dimlevel:slider,0,$dimlevel_step,$dimlevel_max"} = 1;
  156. $sets{"up:noArg"} = 0 if ($hash->{helper}{ISSCREEN});
  157. $sets{"down:noArg"} = 0 if ($hash->{helper}{ISSCREEN});
  158. my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets );
  159. return SetExtensions($hash, join(" ", keys %sets), $me, $cmd, @a) unless @match == 1;
  160. return "$cmd expects $sets{$match[0]} parameters" unless (@a eq $sets{$match[0]});
  161. my $dimlevel = undef;
  162. my $currlevel = ReadingsVal($me,"dimlevel",0);
  163. if ($cmd =~ m/up|down/ and !$hash->{helper}{ISSCREEN}) {
  164. Log3 $me, 1, "$me(Set): up|down not supported";
  165. return undef;
  166. }
  167. if ($hash->{helper}{OWN_DIM} == 1) {
  168. switch($cmd) {
  169. case "dimlevel" {
  170. $dimlevel = pilight_dimmer_ConvDimToDev($me,$a[0]);
  171. $cmd = "on";
  172. }
  173. case "on" {
  174. my $dimlevel_on = AttrVal($me, "dimlevel_on",$currlevel);
  175. $dimlevel_on = $dimlevel_max if ($dimlevel_on eq "dimlevel_max");
  176. $dimlevel = pilight_dimmer_ConvDimToDev($me,$currlevel);
  177. }
  178. }
  179. } else { # device without dimlevel support
  180. switch($cmd) {
  181. case "dimlevel" {
  182. my $newlevel = $a[0];
  183. my $cnt = int(($newlevel - $currlevel) / $dimlevel_step);
  184. return undef if ($cnt==0);
  185. $cmd = "up" if ($cnt>0);
  186. $cmd = "down" if ($cnt<0);
  187. $cnt = abs($cnt) - 1; # correction for loop -1
  188. if ($newlevel == 0) {
  189. $cmd = "off";
  190. $cnt=0; #break for loop
  191. my $dimlevel_off = AttrVal($me, "dimlevel_off",$newlevel);
  192. readingsSingleUpdate($hash,"dimlevel",$dimlevel_off,1);
  193. }
  194. Log3 $me, 5, "$me(Set): cnt $cnt";
  195. for (my $i=0; $i < $cnt; $i++) {
  196. pilight_dimmer_Write($hash,$cmd,undef);
  197. }
  198. }
  199. case "on" {
  200. my $dimlevel_on = AttrVal($me, "dimlevel_on",$currlevel);
  201. $dimlevel_on = $dimlevel_max if ($dimlevel_on eq "dimlevel_max");
  202. readingsSingleUpdate($hash,"dimlevel",$dimlevel_on,1);
  203. }
  204. case "off" {
  205. my $dimlevel_off = AttrVal($me, "dimlevel_off",$currlevel);
  206. readingsSingleUpdate($hash,"dimlevel",$dimlevel_off,1);
  207. }
  208. }
  209. }
  210. if (defined($dimlevel)) {
  211. my $dimOld = $hash->{helper}{DEV_DIMLEVEL};
  212. if (defined($dimOld)) {
  213. return undef if ($dimOld == $dimlevel);
  214. }
  215. }
  216. delete $hash->{helper}{DEV_DIMLEVEL} if ($cmd eq "off");
  217. pilight_dimmer_Write($hash,$cmd,$dimlevel);
  218. #keinen Trigger bei Set auslösen
  219. #Aktualisierung erfolgt in Parse
  220. my $skipTrigger = 1;
  221. return undef,$skipTrigger;
  222. }
  223. #####################################
  224. sub pilight_dimmer_State($$$$)
  225. {
  226. my ($hash, $time, $name, $val) = @_;
  227. my $me = $hash->{NAME};
  228. #$hash->{STATE} wird nur ersetzt, wenn $hash->{STATE} == ??? fhem.pl Z: 2469
  229. #machen wir es also selbst
  230. $hash->{STATE} = $val if ($name eq "state");
  231. return undef;
  232. }
  233. 1;
  234. =pod
  235. =begin html
  236. <a name="pilight_dimmer"></a>
  237. <h3>pilight_dimmer</h3>
  238. <ul>
  239. pilight_dimmer represents a dimmmer controled with\from pilight<br>
  240. You have to define the base device pilight_ctrl first.<br>
  241. Further information to pilight: <a href="http://www.pilight.org/">http://www.pilight.org/</a><br>
  242. Supported dimmers: <a href="http://wiki.pilight.org/doku.php/protocols#dimmers">http://wiki.pilight.org/doku.php/protocols#dimmers</a><br>
  243. <br>
  244. It is possible to add the screen feature to a dimmer. So you can change the dimlevel by set 'up' or 'down'.<br>
  245. If you push up or down on the remote control the dimlevel will be changed by dimlevel_step.<br>
  246. Further it is possible to define a simulated dimmer with a screen and switch protocol. See example three.<br>
  247. That means if you change the dimlevel a up or down command will be send n times to dim the device instead of send a dimlevel directly.<br>
  248. <br>
  249. <a name="pilight_dimmer_define"></a>
  250. <b>Define</b>
  251. <ul>
  252. <code>define &lt;name&gt; pilight_dimmer protocol id unit [protocol]</code>
  253. <br>The second protocol is optional. With it you can add the pilight screen feature (up|down)
  254. Example:
  255. <ul>
  256. <code>define myctrl pilight_dimmer kaku_dimmer 13483668 0</code><br>
  257. <code>define myctrl pilight_dimmer kaku_dimmer 13483668 0 kaku_screen</code> - Dimmer with screen feature<br>
  258. <code>define myctrl pilight_dimmer quigg_screen 1 0 quigg_gt7000</code> - Simulated dimmer with screen feature<br>
  259. </ul>
  260. </ul>
  261. <br>
  262. <a name="pilight_dimmer_set"></a>
  263. <p><b>Set</b></p>
  264. <ul>
  265. <li>
  266. <b>on</b>
  267. </li>
  268. <li>
  269. <b>off</b>
  270. </li>
  271. <li>
  272. <b>up</b> only if defined with screen protocol
  273. </li>
  274. <li>
  275. <b>down</b> only if defined with screen protocol
  276. </li>
  277. <li>
  278. <b>dimlevel</b>
  279. </li>
  280. <li>
  281. <a href="#setExtensions">set extensions</a> are supported<br>
  282. </li>
  283. </ul>
  284. <br>
  285. <a name="pilight_dimmer_readings"></a>
  286. <p><b>Readings</b></p>
  287. <ul>
  288. <li>
  289. state<br>
  290. state of the dimmer on or off
  291. </li>
  292. <li>
  293. dimlevel<br>
  294. dimlevel of the dimmer
  295. </li>
  296. </ul>
  297. <br>
  298. <a name="pilight_dimmer_attr"></a>
  299. <b>Attributes</b>
  300. <ul>
  301. <li><a name="dimlevel_max_device">dimlevel_max_device</a><br>
  302. Maximum of the dimlevel of the device - default 15<br>
  303. Have to be less or equal than dimlevel_max<br>
  304. </li>
  305. <li><a name="dimlevel_max">dimlevel_max</a><br>
  306. Maximum of the dimlevel in FHEM - default dimlevel_max_device<br>
  307. </li>
  308. <li><a name="dimlevel_step">dimlevel_step</a><br>
  309. Step of the dimlevel - default 1<br>
  310. </li>
  311. <li><a name="dimlevel_on">dimlevel_on</a><br>
  312. Change dimlevel to value if on set - default no changing<br>
  313. Could be a numeric value or dimlevel_max<br>
  314. </li>
  315. <li><a name="dimlevel_off">dimlevel_off</a><br>
  316. Change dimlevel to value if off set - default no changing<br>
  317. </li>
  318. </ul>
  319. <br>
  320. </ul>
  321. =end html
  322. =cut