37_SHC.pm 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. ##########################################################################
  2. # This file is part of the smarthomatic module for FHEM.
  3. #
  4. # Copyright (c) 2014 Stefan Baumann
  5. #
  6. # You can find smarthomatic at www.smarthomatic.org.
  7. # You can find FHEM at www.fhem.de.
  8. #
  9. # This file is free software: you can redistribute it and/or modify it
  10. # under the terms of the GNU General Public License as published by the
  11. # Free Software Foundation, either version 3 of the License, or (at your
  12. # option) any later version.
  13. #
  14. # This file is distributed in the hope that it will be useful, but
  15. # WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
  17. # Public License for more details.
  18. #
  19. # You should have received a copy of the GNU General Public License along
  20. # with smarthomatic. If not, see <http://www.gnu.org/licenses/>.
  21. ###########################################################################
  22. # $Id: 37_SHC.pm 8190 2015-03-10 21:23:03Z rr2000 $
  23. package main;
  24. use strict;
  25. use warnings;
  26. use Time::HiRes qw(gettimeofday);
  27. sub SHC_Parse($$$$);
  28. sub SHC_Read($);
  29. sub SHC_ReadAnswer($$$$);
  30. sub SHC_Ready($);
  31. sub SHC_SimpleWrite(@);
  32. my $clientsSHC = ":SHCdev:BASE:xxx:";
  33. my %matchListSHC = (
  34. "1:SHCdev" => "^Packet Data: SenderID=[1-9]|0[1-9]|[1-9][0-9]|[0-9][0-9][0-9]|[0-3][0-9][0-9][0-9]|40[0-8][0-9]|409[0-6]", #1-4096 with leading zeros
  35. "2:xxx" => "^\\S+\\s+22",
  36. "3:xxx" => "^\\S+\\s+11",
  37. "4:xxx" => "^\\S+\\s+9 ",
  38. );
  39. sub SHC_Initialize($)
  40. {
  41. my ($hash) = @_;
  42. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  43. # Provider
  44. $hash->{ReadFn} = "SHC_Read";
  45. $hash->{WriteFn} = "SHC_Write";
  46. $hash->{ReadyFn} = "SHC_Ready";
  47. # Normal devices
  48. $hash->{DefFn} = "SHC_Define";
  49. $hash->{UndefFn} = "SHC_Undef";
  50. $hash->{GetFn} = "SHC_Get";
  51. $hash->{SetFn} = "SHC_Set";
  52. $hash->{ShutdownFn} = "SHC_Shutdown";
  53. }
  54. #####################################
  55. sub SHC_Define($$)
  56. {
  57. my ($hash, $def) = @_;
  58. my @a = split("[ \t][ \t]*", $def);
  59. if (@a != 3) {
  60. my $msg = "wrong syntax: define <name> SHC {devicename[\@baudrate] " . "| devicename\@directio}";
  61. Log3 undef, 2, $msg;
  62. return $msg;
  63. }
  64. DevIo_CloseDev($hash);
  65. my $name = $a[0];
  66. my $dev = $a[2];
  67. $dev .= "\@19200" if ($dev !~ m/\@/);
  68. $hash->{Clients} = $clientsSHC;
  69. $hash->{MatchList} = \%matchListSHC;
  70. $hash->{DeviceName} = $dev;
  71. my $ret = DevIo_OpenDev($hash, 0, "SHC_DoInit");
  72. return $ret;
  73. }
  74. #####################################
  75. sub SHC_Undef($$)
  76. {
  77. my ($hash, $arg) = @_;
  78. my $name = $hash->{NAME};
  79. foreach my $d (sort keys %defs) {
  80. if ( defined($defs{$d})
  81. && defined($defs{$d}{IODev})
  82. && $defs{$d}{IODev} == $hash)
  83. {
  84. my $lev = ($reread_active ? 4 : 2);
  85. Log3 $name, $lev, "$name: deleting port for $d";
  86. delete $defs{$d}{IODev};
  87. }
  88. }
  89. SHC_Shutdown($hash);
  90. DevIo_CloseDev($hash);
  91. return undef;
  92. }
  93. #####################################
  94. sub SHC_Shutdown($)
  95. {
  96. my ($hash) = @_;
  97. return undef;
  98. }
  99. #####################################
  100. sub SHC_Set($@)
  101. {
  102. my ($hash, @a) = @_;
  103. my $name = shift @a;
  104. my $cmd = shift @a;
  105. my $arg = join("", @a);
  106. my $list = "raw:noArg";
  107. return $list if ($cmd eq '?');
  108. if ($cmd eq "raw") {
  109. #return "\"set SHC $cmd\" needs exactly one parameter" if(@_ != 4);
  110. #return "Expecting a even length hex number" if((length($arg)&1) == 1 || $arg !~ m/^[\dA-F]{12,}$/ );
  111. Log3 $name, 4, "$name: set $name $cmd $arg";
  112. SHC_SimpleWrite($hash, $arg);
  113. } else {
  114. return "Unknown argument $cmd, choose one of " . $list;
  115. }
  116. return undef;
  117. }
  118. #####################################
  119. sub SHC_Get($@)
  120. {
  121. my ($hash, $name, $cmd) = @_;
  122. return undef;
  123. }
  124. #####################################
  125. sub SHC_DoInit($)
  126. {
  127. my $hash = shift;
  128. my $name = $hash->{NAME};
  129. my $err;
  130. my $msg = undef;
  131. $hash->{STATE} = "Initialized";
  132. return undef;
  133. }
  134. #####################################
  135. # This is a direct read for commands like get
  136. # Anydata is used by read file to get the filesize
  137. sub SHC_ReadAnswer($$$$)
  138. {
  139. # TODO: Not adapted to SHC, copy from 36_JeeLink.pm
  140. my ($hash, $arg, $anydata, $regexp) = @_;
  141. my $type = $hash->{TYPE};
  142. my $name = $hash->{NAME};
  143. return ("No FD", undef)
  144. if (!$hash || ($^O !~ /Win/ && !defined($hash->{FD})));
  145. my ($mpandata, $rin) = ("", '');
  146. my $buf;
  147. my $to = 3; # 3 seconds timeout
  148. $to = $hash->{RA_Timeout} if ($hash->{RA_Timeout}); # ...or less
  149. for (; ;) {
  150. if ($^O =~ m/Win/ && $hash->{USBDev}) {
  151. $hash->{USBDev}->read_const_time($to * 1000); # set timeout (ms)
  152. # Read anstatt input sonst funzt read_const_time nicht.
  153. $buf = $hash->{USBDev}->read(999);
  154. return ("Timeout reading answer for get $arg", undef)
  155. if (length($buf) == 0);
  156. } else {
  157. return ("Device lost when reading answer for get $arg", undef)
  158. if (!$hash->{FD});
  159. vec($rin, $hash->{FD}, 1) = 1;
  160. my $nfound = select($rin, undef, undef, $to);
  161. if ($nfound < 0) {
  162. next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
  163. my $err = $!;
  164. DevIo_Disconnected($hash);
  165. return ("SHC_ReadAnswer $arg: $err", undef);
  166. }
  167. return ("Timeout reading answer for get $arg", undef)
  168. if ($nfound == 0);
  169. $buf = DevIo_SimpleRead($hash);
  170. return ("No data", undef) if (!defined($buf));
  171. }
  172. if ($buf) {
  173. Log3 $hash->{NAME}, 5, "$name: SHC/RAW (ReadAnswer): $buf";
  174. $mpandata .= $buf;
  175. }
  176. chop($mpandata);
  177. chop($mpandata);
  178. return (
  179. undef, $mpandata
  180. );
  181. }
  182. }
  183. #####################################
  184. sub SHC_Write($$)
  185. {
  186. # TODO: Not adapted to SHC, copy from 36_JeeLink.pm
  187. my ($hash, $msg) = @_;
  188. my $name = $hash->{NAME};
  189. Log3 $name, 5, "$name: sending $msg";
  190. SHC_SimpleWrite($hash, $msg);
  191. }
  192. #####################################
  193. # called from the global loop, when the select for hash->{FD} reports data
  194. sub SHC_Read($)
  195. {
  196. # TODO: Verify if partial data handling is required for SHC
  197. my ($hash) = @_;
  198. my $buf = DevIo_SimpleRead($hash);
  199. return "" if (!defined($buf));
  200. my $name = $hash->{NAME};
  201. my $pandata = $hash->{PARTIAL};
  202. Log3 $name, 5, "$name: SHC/RAW: $pandata/$buf";
  203. $pandata .= $buf;
  204. while ($pandata =~ m/\n/) {
  205. my $rmsg;
  206. ($rmsg, $pandata) = split("\n", $pandata, 2);
  207. $rmsg =~ s/\r//;
  208. SHC_Parse($hash, $hash, $name, $rmsg) if ($rmsg);
  209. }
  210. $hash->{PARTIAL} = $pandata;
  211. }
  212. #####################################
  213. sub SHC_Parse($$$$)
  214. {
  215. my ($hash, $iohash, $name, $rmsg) = @_;
  216. my $dmsg = $rmsg;
  217. next if (!$dmsg || length($dmsg) < 1); # Bogus messages
  218. if ($dmsg !~ m/^Packet Data: SenderID=/) {
  219. # Messages just to dipose
  220. if ( $dmsg =~ m/^\*\*\* Enter AES key nr/
  221. || $dmsg =~ m/^\*\*\* Received character/)
  222. {
  223. return;
  224. }
  225. # -Verbosity level 5
  226. if ( $dmsg =~ m/^Received \(AES key/
  227. || $dmsg =~ m/^Received garbage/
  228. || $dmsg =~ m/^Before encryption/
  229. || $dmsg =~ m/^After encryption/
  230. || $dmsg =~ m/^Repeating request./
  231. || $dmsg =~ m/^Request Queue empty/
  232. || $dmsg =~ m/^Removing request from request buffer/)
  233. {
  234. Log3 $name, 5, "$name: $dmsg";
  235. return;
  236. }
  237. # -Verbosity level 4
  238. if ( $dmsg =~ m/^Request added to queue/
  239. || $dmsg =~ m/^Request Buffer/
  240. || $dmsg =~ m/^Request (q|Q)ueue/)
  241. {
  242. Log3 $name, 4, "$name: $dmsg";
  243. return;
  244. }
  245. # Anything else in verbosity level 3
  246. Log3 $name, 3, "$name: $dmsg";
  247. return;
  248. }
  249. $hash->{"${name}_MSGCNT"}++;
  250. $hash->{"${name}_TIME"} = TimeNow();
  251. $hash->{RAWMSG} = $rmsg;
  252. my %addvals = (RAWMSG => $rmsg);
  253. Dispatch($hash, $dmsg, \%addvals);
  254. }
  255. #####################################
  256. sub SHC_Ready($)
  257. {
  258. my ($hash) = @_;
  259. return DevIo_OpenDev($hash, 1, "SHC_DoInit")
  260. if ($hash->{STATE} eq "disconnected");
  261. # This is relevant for windows/USB only
  262. my $po = $hash->{USBDev};
  263. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
  264. if ($po) {
  265. ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  266. }
  267. return ($InBytes && $InBytes > 0);
  268. }
  269. ########################
  270. sub SHC_SimpleWrite(@)
  271. {
  272. my ($hash, $msg) = @_;
  273. return if (!$hash);
  274. my $name = $hash->{NAME};
  275. Log3 $name, 5, "$name: SW: $msg";
  276. $msg .= "\r";
  277. $hash->{USBDev}->write($msg) if ($hash->{USBDev});
  278. syswrite($hash->{DIODev}, $msg) if ($hash->{DIODev});
  279. # Some linux installations are broken with 0.001, T01 returns no answer
  280. select(undef, undef, undef, 0.01);
  281. }
  282. 1;
  283. =pod
  284. =begin html
  285. <a name="SHC"></a>
  286. <h3>SHC</h3>
  287. <ul>
  288. SHC is the basestation module that supports a family of RF devices available
  289. at <a href="http://http://www.smarthomatic.org">www.smarthomatic.org</a>.
  290. This module provides the IODevice for the <a href="#SHCdev">SHCdev</a>
  291. modules that implement the SHCdev protocol.<br><br>
  292. Note: this module may require the Device::SerialPort or Win32::SerialPort
  293. module if you attach the device via USB and the OS sets strange default
  294. parameters for serial devices.<br><br>
  295. <a name="SHC_Define"></a>
  296. <b>Define</b>
  297. <ul>
  298. <code>define &lt;name&gt; SHC &lt;device&gt;</code><br>
  299. <br>
  300. &lt;device&gt; specifies the serial port to communicate with the SHC.
  301. The name of the serial-device depends on your distribution, under
  302. linux usually a /dev/ttyUSB0 device will be created.<br><br>
  303. You can also specify a baudrate if the device name contains the @
  304. character, e.g.: /dev/ttyUSB0@57600. Please note that the default
  305. baudrate for the SHC base station is 19200 baud.<br><br>
  306. Example:<br>
  307. <ul>
  308. <code>define shc_base SHC /dev/ttyUSB0</code><br><br>
  309. </ul>
  310. </ul>
  311. <a name="SHC_Set"></a>
  312. <b>Set</b>
  313. <ul>
  314. <li>raw &lt;data&gt;<br>
  315. not supported yet
  316. </li><br>
  317. </ul>
  318. <a name="SHC_Get"></a>
  319. <b>Get</b>
  320. <ul>
  321. <li>
  322. N/A
  323. </li><br>
  324. </ul>
  325. <a name="SHC_Attr"></a>
  326. <b>Attributes</b>
  327. <ul>
  328. <li>
  329. N/A
  330. </li><br>
  331. </ul>
  332. </ul>
  333. =end html
  334. =cut