36_EleroDrive.pm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. # $Id: 36_EleroDrive.pm 15168 2017-10-01 21:45:29Z HCS $
  2. package main;
  3. use strict;
  4. use warnings;
  5. use SetExtensions;
  6. #=======================================================================================
  7. sub EleroDrive_Initialize($) {
  8. my ($hash) = @_;
  9. $hash->{Match} = ".*";
  10. $hash->{DefFn} = "EleroDrive_Define";
  11. $hash->{UndefFn} = "EleroDrive_Undef";
  12. $hash->{FingerprintFn} = "EleroDrive_Fingerprint";
  13. $hash->{ParseFn} = "EleroDrive_Parse";
  14. $hash->{SetFn} = "EleroDrive_Set";
  15. $hash->{GetFn} = "EleroDrive_Get";
  16. $hash->{AttrFn} = "EleroDrive_Attr";
  17. $hash->{AttrList} = "IODev " .
  18. "TopToBottomTime " .
  19. "TiltPercent " .
  20. "IntermediatePercent " .
  21. "$readingFnAttributes ";
  22. $hash->{noAutocreatedFilelog} = 1;
  23. }
  24. #=======================================================================================
  25. sub EleroDrive_Define($$) {
  26. my ( $hash, $def ) = @_;
  27. my @a = split( "[ \t][ \t]*", $def );
  28. return "Usage: define <name> EleroDrive <Channel>" if(@a < 3);
  29. my $devName = $a[0];
  30. my $type = $a[1];
  31. my $channel = $a[2];
  32. $hash->{STATE} = 'Initialized';
  33. $hash->{NAME} = $devName;
  34. $hash->{TYPE} = $type;
  35. $hash->{channel} = $channel;
  36. $modules{EleroDrive}{defptr}{$channel} = $hash;
  37. my $ioDev = undef;
  38. my @parts = split("_", $devName);
  39. if(@parts == 3) {
  40. $ioDev = $parts[1];
  41. }
  42. if($ioDev) {
  43. AssignIoPort($hash, $ioDev);
  44. }
  45. else {
  46. AssignIoPort($hash);
  47. }
  48. if(defined($hash->{IODev}->{NAME})) {
  49. Log3 $devName, 4, "$devName: I/O device is " . $hash->{IODev}->{NAME};
  50. }
  51. else {
  52. Log3 $devName, 1, "$devName: no I/O device";
  53. }
  54. return undef;
  55. }
  56. #=======================================================================================
  57. sub EleroDrive_Undef($$) {
  58. my ($hash, $arg) = @_;
  59. my $channel = $hash->{channel};
  60. RemoveInternalTimer($hash);
  61. delete( $modules{EleroDrive}{defptr}{$channel} );
  62. return undef;
  63. }
  64. #=======================================================================================
  65. sub EleroDrive_Get($@) {
  66. return undef;
  67. }
  68. #=======================================================================================
  69. sub EleroDrive_ToFixPosition($$) {
  70. my ( $hash, $position) = @_;
  71. my $channel = $hash->{channel};
  72. my $head = 'aa';
  73. my $msgLength = '05';
  74. my $msgCmd = '4c';
  75. my $firstBits = '';
  76. my $firstChannels = '';
  77. my $secondBits = '';
  78. my $secondChannels = '';
  79. my $checksum = '';
  80. my $payload = '';
  81. if($position eq 'bottom'){
  82. $payload = '40';
  83. }
  84. elsif($position eq 'top'){
  85. $payload = '20';
  86. }
  87. elsif($position eq 'stop'){
  88. $payload = '10';
  89. }
  90. elsif($position eq 'intermediate'){
  91. $payload = '44';
  92. }
  93. elsif($position eq 'tilt'){
  94. $payload = '24';
  95. }
  96. if($payload) {
  97. if($channel <= 8){
  98. $firstChannels = '00';
  99. $secondChannels = 2**($channel-1);
  100. $secondChannels = sprintf('%02x', $secondChannels);
  101. }
  102. else {
  103. $secondChannels = '00';
  104. $firstChannels = 2**($channel-1-8);
  105. $firstChannels = sprintf('%02x', $firstChannels);
  106. }
  107. my $checksumNumber = hex($head) + hex($msgLength) + hex($msgCmd) + hex($firstChannels) + hex($secondChannels) + hex($payload);
  108. my $byteUpperBound = 256;
  109. my $upperBound = $byteUpperBound;
  110. while($checksumNumber > $upperBound){
  111. $upperBound = $upperBound + $byteUpperBound;
  112. }
  113. $checksumNumber = $upperBound - $checksumNumber;
  114. $checksum = sprintf('%02x', $checksumNumber);
  115. my $byteMsg = $head.$msgLength.$msgCmd.$firstChannels.$secondChannels.$payload.$checksum;
  116. IOWrite($hash, "send", $byteMsg);
  117. }
  118. }
  119. #=======================================================================================
  120. sub EleroDrive_ToAnyPosition($$) {
  121. my ( $hash, $position) = @_;
  122. my $name = $hash->{NAME};
  123. }
  124. #=======================================================================================
  125. sub EleroDrive_Set($@) {
  126. my ( $hash, $name, $cmd, @params ) = @_;
  127. my $channel = $hash->{channel};
  128. my $iodev = $hash->{IODev}->{NAME};
  129. my $commands=("stop:noArg moveDown:noArg moveUp:noArg moveIntermediate:noArg moveTilt:noArg refresh:noArg");
  130. return $commands if( $cmd eq '?' || $cmd eq '');
  131. my $doRefresh = '0';
  132. if($cmd eq 'refresh'){
  133. IOWrite($hash, "refresh", $channel);
  134. }
  135. elsif($cmd eq 'moveDown'){
  136. EleroDrive_ToFixPosition($hash, "bottom");
  137. $doRefresh = '1';
  138. }
  139. elsif($cmd eq 'moveUp'){
  140. EleroDrive_ToFixPosition($hash, "top");
  141. $doRefresh = '1';
  142. }
  143. elsif($cmd eq 'stop'){
  144. EleroDrive_ToFixPosition($hash, "stop");
  145. }
  146. elsif($cmd eq 'moveIntermediate'){
  147. EleroDrive_ToFixPosition($hash, "intermediate");
  148. $doRefresh = '1';
  149. }
  150. elsif($cmd eq 'moveTilt'){
  151. EleroDrive_ToFixPosition($hash, "tilt");
  152. $doRefresh = '1';
  153. }
  154. elsif($cmd eq 'moveTo' && scalar @params eq 1){
  155. EleroDrive_ToAnyPosition($hash, $params[0]);
  156. $doRefresh = '1';
  157. }
  158. else {
  159. return "Unknown argument $cmd, choose one of $commands";
  160. }
  161. # Start a one time timer that refreshes the position for this drive
  162. my $refreshDelay = AttrVal($name, "TopToBottomTime", 0);
  163. if($doRefresh && $refreshDelay) {
  164. RemoveInternalTimer($hash);
  165. InternalTimer(gettimeofday() + $refreshDelay + 2, "EleroDrive_OnRefreshTimer", $hash, 0);
  166. }
  167. return undef;
  168. }
  169. #=======================================================================================
  170. sub EleroDrive_Fingerprint($$) {
  171. my ($name, $msg) = @_;
  172. return ("", $msg);
  173. }
  174. #=======================================================================================
  175. sub EleroDrive_Parse($$) {
  176. my ($hash, $msg) = @_;
  177. my $name = $hash->{NAME};
  178. my $buffer = $msg;
  179. # aa054d00010102 : channel 1 top
  180. # aa054d00010202 : channel 1 bottom
  181. # aa054d00020102 : channel 2 top
  182. # aa054d00020202 : channel 2 bottom
  183. # aa 05 4d 00 01 01 02
  184. # ----- -- -- -- -- --
  185. # | | | | | |
  186. # | | | | | Checksum
  187. # | | | | State (top, bottom, ...)
  188. # | | | Lower channel bits (1 - 8)
  189. # | | Upper channel bits (9 - 15)
  190. # | 4d = Easy_Ack (answer on Easy_Send or Easy_Info)
  191. # Fix aa 05
  192. # State: 0x01 = top
  193. # 0x02 = bottom
  194. # 0x03 = intermediate
  195. # 0x04 = tilt
  196. # get the channel
  197. my $firstChannels = substr($buffer,6,2);
  198. my $secondChannels = substr($buffer,8,2);
  199. my $bytes = $firstChannels.$secondChannels ;
  200. $bytes = hex ($bytes);
  201. my $channel = 1;
  202. while ($bytes != 1 and $channel <= 15) {
  203. $bytes = $bytes >> 1;
  204. $channel++;
  205. }
  206. if($channel <= 15) {
  207. # Check if it is defined as a switch device
  208. my $switchChannels = AttrVal($name, "SwitchChannels", undef);
  209. if(defined $switchChannels) {
  210. my @channelList = split /,/, $switchChannels;
  211. if ($channel ~~ @channelList) {
  212. return undef;
  213. }
  214. }
  215. my $rhash = undef;
  216. foreach my $d (keys %defs) {
  217. my $h = $defs{$d};
  218. my $type = $h->{TYPE};
  219. if($type eq "EleroDrive") {
  220. if (defined($h->{IODev}->{NAME})) {
  221. my $ioDev = $h->{IODev}->{NAME};
  222. my $def = $h->{DEF};
  223. if ($ioDev eq $name && $def eq $channel) {
  224. $rhash = $h;
  225. last;
  226. }
  227. }
  228. }
  229. }
  230. if($rhash) {
  231. my $rname = $rhash->{NAME};
  232. # get status
  233. my $statusByte = substr($buffer,10,2);
  234. my %deviceStati = ('00' => "no_information",
  235. '01' => "top_position",
  236. '02' => "bottom_position",
  237. '03' => "intermediate_position",
  238. '04' => "tilt_position",
  239. '05' => "blocking",
  240. '06' => "overheated",
  241. '07' => "timeout",
  242. '08' => "move_up_started",
  243. '09' => "move_down_started",
  244. '0a' => "moving_up",
  245. '0b' => "moving_down",
  246. '0d' => "stopped_in_undefined_position",
  247. '0e' => "top_tilt_stop",
  248. '0f' => "bottom_intermediate_stop",
  249. '10' => "switching_device_switched_off",
  250. '11' => "switching_device_switched_on"
  251. );
  252. my %percentDefinitions = ('00' => 50,
  253. '01' => 0,
  254. '02' => 100,
  255. '03' => AttrVal($rname, "IntermediatePercent", 50),
  256. '04' => AttrVal($rname, "TiltPercent", 50),
  257. '05' => -1,
  258. '06' => -1,
  259. '07' => -1,
  260. '08' => -1,
  261. '09' => -1,
  262. '0a' => -1,
  263. '0b' => -1,
  264. '0d' => 50,
  265. '0e' => 0,
  266. '0f' => 100,
  267. '10' => -1,
  268. '11' => -1
  269. );
  270. my $newstate = $deviceStati{$statusByte};
  271. my $percentClosed = $percentDefinitions{$statusByte};
  272. readingsBeginUpdate($rhash);
  273. readingsBulkUpdate($rhash, "state", $newstate);
  274. readingsBulkUpdate($rhash, "position", $newstate);
  275. if($percentClosed ne -1) {
  276. readingsBulkUpdate($rhash, "percentClosed", $percentClosed);
  277. }
  278. readingsEndUpdate($rhash,1);
  279. my @list;
  280. push(@list, $rname);
  281. return @list;
  282. }
  283. else {
  284. return "UNDEFINED EleroDrive_" . $name . "_" . $channel . " EleroDrive " . $channel;
  285. }
  286. }
  287. }
  288. #=======================================================================================
  289. sub EleroDrive_Attr(@) {
  290. }
  291. #=======================================================================================
  292. sub EleroDrive_OnRefreshTimer($$) {
  293. my ($hash, @params) = @_;
  294. my $name = $hash->{NAME};
  295. my $channel = $hash->{channel};
  296. IOWrite($hash, "refresh", $channel);
  297. return undef;
  298. }
  299. 1;
  300. =pod
  301. =item summary Represents on elero drive
  302. =item summary_DE Repräsentiert ein elero drive
  303. =begin html
  304. <a name="EleroDrive"></a>
  305. <h3>EleroDrive</h3>
  306. <ul>
  307. This mudule implements an Elero drive. It uses EleroStick as IO-Device.
  308. <br><br>
  309. <a name="EleroDrive_Define"></a>
  310. <b>Define</b>
  311. <ul>
  312. <code>define &lt;name&gt; EleroDrive &lt;channel&gt;</code> <br>
  313. &lt;channel&gt; specifies the channel of the transmitter stick that shall be used.
  314. <br><br>
  315. </ul>
  316. <a name="EleroDrive_Set"></a>
  317. <b>Set</b>
  318. <ul>
  319. <li>moveDown<br>
  320. </li>
  321. <li>moveUp<br>
  322. </li>
  323. <li>stop<br>
  324. </li>
  325. <li>moveIntermediate<br>
  326. </li>
  327. <li>moveTilt<br>
  328. </li>
  329. <li>refresh<br>
  330. </li>
  331. </ul>
  332. <br>
  333. <a name="EleroDrive_Get"></a>
  334. <b>Get</b>
  335. <ul>
  336. <li>no gets<br>
  337. </li><br>
  338. </ul>
  339. <a name="EleroDrive_Attr"></a>
  340. <b>Attributes</b>
  341. <ul>
  342. <li>IODev<br>
  343. The name of the IO-Device, normally the name of the EleroStick definition</li>
  344. <li>TopToBottomTime<br>
  345. The time in seconds this drive needs for a complete run from the top to the bottom or vice versa</li>
  346. <li>IntermediatePercent<br>
  347. Percent open when in intermediate position</li>
  348. <li>TiltPercent<br>
  349. Percent open when in tilt position</li>
  350. </ul><br>
  351. <a name="EleroDrive_Readings"></a>
  352. <b>Readings</b>
  353. <ul>
  354. <li>position<br>
  355. Current position of the drive (top_position, bottom_position, ...)</li>
  356. <li>percentClosed<br>
  357. 0 ... 100<br>
  358. 100 is completely closed, 0 is completely open</li>
  359. </ul><br>
  360. </ul>
  361. =end html
  362. =cut