00_FBAHA.pm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. ##############################################
  2. # $Id: 00_FBAHA.pm 16293 2018-02-28 21:33:57Z rudolfkoenig $
  3. package main;
  4. use strict;
  5. use warnings;
  6. use Time::HiRes qw(gettimeofday);
  7. sub FBAHA_Read($@);
  8. sub FBAHA_Write($$$);
  9. sub FBAHA_ReadAnswer($$$);
  10. sub FBAHA_Ready($);
  11. sub FBAHA_getDevList($$);
  12. sub
  13. FBAHA_Initialize($)
  14. {
  15. my ($hash) = @_;
  16. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  17. # Provider
  18. $hash->{ReadFn} = "FBAHA_Read";
  19. $hash->{WriteFn} = "FBAHA_Write";
  20. $hash->{ReadyFn} = "FBAHA_Ready";
  21. $hash->{UndefFn} = "FBAHA_Undef";
  22. $hash->{ShutdownFn} = "FBAHA_Undef";
  23. $hash->{ReadAnswerFn} = "FBAHA_ReadAnswer";
  24. $hash->{NotifyFn} = "FBAHA_Notify";
  25. # Normal devices
  26. $hash->{DefFn} = "FBAHA_Define";
  27. $hash->{GetFn} = "FBAHA_Get";
  28. $hash->{SetFn} = "FBAHA_Set";
  29. $hash->{AttrList}= "dummy:1,0";
  30. }
  31. #####################################
  32. sub
  33. FBAHA_Define($$)
  34. {
  35. my ($hash, $def) = @_;
  36. my @a = split("[ \t][ \t]*", $def);
  37. if(@a != 3) {
  38. return "wrong syntax: define <name> FBAHA hostname:2002";
  39. }
  40. my $name = $a[0];
  41. my $dev = $a[2];
  42. $hash->{Clients} = ":FBDECT:";
  43. my %matchList = ( "1:FBDECT" => ".*" );
  44. $hash->{MatchList} = \%matchList;
  45. DevIo_CloseDev($hash);
  46. $hash->{DeviceName} = $dev;
  47. return undef if($dev eq "none"); # DEBUGGING
  48. my $ret = DevIo_OpenDev($hash, 0, "FBAHA_DoInit");
  49. return $ret;
  50. }
  51. #####################################
  52. sub
  53. FBAHA_Notify($$)
  54. {
  55. my ($ntfy, $dev) = @_;
  56. return if($dev->{NAME} ne "global" ||
  57. !grep(m/^INITIALIZED$/, @{$dev->{CHANGED}}));
  58. delete $modules{FBAHA}{NotifyFn};
  59. FBAHA_reassign($ntfy);
  60. return;
  61. }
  62. #####################################
  63. sub
  64. FBAHA_Set($@)
  65. {
  66. my ($hash, @a) = @_;
  67. my $name = shift @a;
  68. my %sets = ("createDevs"=>1, "reregister"=>1, "reopen"=>1);
  69. return "set $name needs at least one parameter" if(@a < 1);
  70. my $type = shift @a;
  71. return "Unknown argument $type, choose one of " . join(" ", sort keys %sets)
  72. if(!defined($sets{$type}));
  73. if($type eq "createDevs") {
  74. my %ex;
  75. foreach my $sdev (devspec2array("TYPE=FBDECT")) {
  76. my @dl = split(" ", $defs{$sdev}{DEF});
  77. $ex{$dl[0]} = 1;
  78. }
  79. my @arg = FBAHA_getDevList($hash,0);
  80. foreach my $arg (@arg) {
  81. if($arg =~ m/ID:(\d+).*PROP:(.*)/) {
  82. my ($i,$p) = ($1,$2,$3);
  83. next if($ex{"$name:$i"});
  84. my $msg = "UNDEFINED FBDECT_$i FBDECT $name:$i $p";
  85. DoTrigger("global", $msg, 1);
  86. Log3 $name, 3, "$msg, please define it";
  87. }
  88. }
  89. }
  90. if($type eq "reregister") {
  91. # Release seems to be deadly on the 546e
  92. FBAHA_Write($hash, "02", "") if($hash->{HANDLE}); # RELEASE
  93. FBAHA_Write($hash, "00", "00022005"); # REGISTER
  94. my ($err, $data) = FBAHA_ReadAnswer($hash, "REGISTER", "^01");
  95. if($err) {
  96. Log3 $name, 1, $err;
  97. $hash->{STATE} =
  98. $hash->{READINGS}{state}{VAL} = "???";
  99. $hash->{READINGS}{state}{TIME} = TimeNow();
  100. return $err;
  101. }
  102. if($data =~ m/^01030010(........)/) {
  103. $hash->{STATE} =
  104. $hash->{READINGS}{state}{VAL} = "Initialized";
  105. $hash->{READINGS}{state}{TIME} = TimeNow();
  106. $hash->{HANDLE} = $1;
  107. Log3 $name, 1,
  108. "FBAHA $hash->{NAME} registered with handle: $hash->{HANDLE}";
  109. } else {
  110. my $msg = "Got bogus answer for REGISTER request: $data";
  111. Log3 $name, 1, $msg;
  112. $hash->{STATE} =
  113. $hash->{READINGS}{state}{VAL} = "???";
  114. $hash->{READINGS}{state}{TIME} = TimeNow();
  115. return $msg;
  116. }
  117. FBAHA_Write($hash, "03", "0000038200000000"); # LISTEN
  118. }
  119. if($type eq "reopen") {
  120. DevIo_CloseDev($hash);
  121. delete $hash->{HANDLE};
  122. return DevIo_OpenDev($hash, 0, "FBAHA_DoInit");
  123. }
  124. return undef;
  125. }
  126. #####################################
  127. sub
  128. FBAHA_Get($@)
  129. {
  130. my ($hash, @a) = @_;
  131. my $name = shift @a;
  132. my %gets = ("devList"=>1);
  133. return "get $name needs at least one parameter" if(@a < 1);
  134. my $type = shift @a;
  135. return "Unknown argument $type, choose one of ". join(" ", sort keys %gets)
  136. if(!defined($gets{$type}));
  137. if($type eq "devList") {
  138. return join("\n", FBAHA_getDevList($hash,0));
  139. }
  140. return undef;
  141. }
  142. sub
  143. FBAHA_getDevList($$)
  144. {
  145. my ($hash, $onlyId) = @_;
  146. FBAHA_Write($hash, "05", "00000000"); # CONFIG_REQ
  147. my $data = "";
  148. for(;;) {
  149. my ($err, $buf) = FBAHA_ReadAnswer($hash, "CONFIG_RSP", "^06");
  150. last if($err && $err =~ m/Timeout/);
  151. return ($err) if($err);
  152. $data .= substr($buf, 32);
  153. last if($buf =~ m/^060[23]/);
  154. }
  155. return FBAHA_configInd($data, $onlyId);
  156. }
  157. sub
  158. FBAHA_configInd($$)
  159. {
  160. my ($data, $onlyId) = @_;
  161. #my $off = 288; #for old Client Id
  162. my $off = 304;
  163. my @answer;
  164. while(length($data) >= $off) {
  165. my $id = hex(substr($data, 0, 4));
  166. my $act = hex(substr($data, 4, 2));
  167. my $typ = hex(substr($data, 8, 8));
  168. my $lsn = hex(substr($data, 16, 8));
  169. my $nam = pack("H*",substr($data,24,160)); $nam =~ s/\x0//g;
  170. $act = ($act == 2 ? "active" : ($act == 1 ? "inactive" : "removed"));
  171. my %tl = ( 2=>"AVM FRITZ!Dect Powerline 546E",
  172. 3=>"Comet DECT",
  173. 9=>"AVM FRITZ!Dect 200");
  174. $typ = $tl{$typ} ? $tl{$typ} : "unknown($typ)";
  175. my %ll = (7=>"powerMeter",9=>"switch");
  176. $lsn = join ",", map { $ll{$_} if((1 << $_) & $lsn) } sort keys %ll;
  177. my $dlen = hex(substr($data, $off-8, 8))*2; # DATA MSG
  178. push @answer, "NAME:$nam, ID:$id, $act, TYPE:$typ PROP:$lsn"
  179. if(!$onlyId || $onlyId == $id);
  180. if($onlyId && $onlyId == $id) {
  181. my $mnf = hex(substr($data,184, 8)); # empty/0
  182. my $idf = substr($data,192,40); $idf =~ s/(00)*$//; $idf =pack("H*",$idf);
  183. my $frm = substr($data,232,40); $frm =~ s/(00)*$//; $frm =pack("H*",$frm);
  184. push @answer, " MANUF:$mnf";
  185. push @answer, " UniqueID:$idf";
  186. push @answer, " Firmware:$frm";
  187. push @answer, substr($data, $off, $dlen);
  188. return @answer;
  189. }
  190. $data = substr($data, $off+$dlen); # rest
  191. }
  192. return @answer;
  193. }
  194. #####################################
  195. # Check all FBDECTs, reorg them if the id has changed and FBNAME is set.
  196. sub
  197. FBAHA_reassign($)
  198. {
  199. my ($me) = @_;
  200. my $myname = $me->{NAME};
  201. my $devList = FBAHA_Get($me, ($myname, "devList"));
  202. my %fbdata;
  203. foreach my $l (split("\n", $devList)) {
  204. next if($l !~ m/NAME:(.*), ID:(.*), (.*), TYPE:(.*) PROP:(.*)/);
  205. if($fbdata{$1}) {
  206. Log 1, "FBAHA: multiple devices are using the same name, wont reorder";
  207. return;
  208. }
  209. $fbdata{$1} = $2;
  210. }
  211. foreach my $sdev (devspec2array("TYPE=FBDECT")) {
  212. my $hash = $defs{$sdev};
  213. my $name = $hash->{NAME};
  214. my $fbname = ReadingsVal($name, "FBNAME", "");
  215. my $fbid = $fbdata{$fbname};
  216. my $oldid = $hash->{id};
  217. next if(!$fbid || $oldid eq $fbid || $hash->{IODev}{NAME} ne $myname);
  218. Log 2, "FBAHA: changing the id of $name/$fbname from $oldid to $fbid";
  219. delete $modules{FBDECT}{defptr}{"$myname:$oldid"};
  220. $modules{FBDECT}{defptr}{"$myname:$fbid"} = $hash;
  221. $hash->{DEF} =~ s/^$myname:$oldid /$myname:$fbid /; # New syntax
  222. $hash->{DEF} =~ s/^$oldid /$myname:$fbid /; # Old Syntax
  223. $hash->{id} = $fbid;
  224. }
  225. return;
  226. }
  227. #####################################
  228. sub
  229. FBAHA_DoInit($)
  230. {
  231. my $hash = shift;
  232. my $name = $hash->{NAME};
  233. delete $hash->{HANDLE}; # else reregister fails / RELEASE is deadly
  234. my $ret = FBAHA_Set($hash, ($name, "reregister"));
  235. FBAHA_reassign($hash) if(!$ret && $init_done);
  236. return $ret;
  237. }
  238. #####################################
  239. sub
  240. FBAHA_Undef($@)
  241. {
  242. my ($hash, $arg) = @_;
  243. FBAHA_Write($hash, "02", ""); # RELEASE
  244. DevIo_CloseDev($hash);
  245. return undef;
  246. }
  247. #####################################
  248. sub
  249. FBAHA_Write($$$)
  250. {
  251. my ($hash,$fn,$msg) = @_;
  252. $msg = sprintf("%s03%04x%s%s", $fn, length($msg)/2+8,
  253. $hash->{HANDLE} ? $hash->{HANDLE} : "00000000", $msg);
  254. DevIo_SimpleWrite($hash, $msg, 1);
  255. }
  256. #####################################
  257. # called from the global loop, when the select for hash->{FD} reports data
  258. sub
  259. FBAHA_Read($@)
  260. {
  261. my ($hash, $local, $regexp) = @_;
  262. my $buf = ($local ? $local : DevIo_SimpleRead($hash));
  263. return "" if(!defined($buf));
  264. my $name = $hash->{NAME};
  265. $buf = unpack('H*', $buf);
  266. my $data = ($hash->{PARTIAL} ? $hash->{PARTIAL} : "");
  267. # drop old data
  268. if($data) {
  269. $data = "" if(gettimeofday() - $hash->{READ_TS} > 5);
  270. delete($hash->{READ_TS});
  271. }
  272. Log3 $name, 5, "FBAHA/RAW: $data/$buf";
  273. $data .= $buf;
  274. my $msg;
  275. while(length($data) >= 16) {
  276. my $len = hex(substr($data, 4,4))*2;
  277. if($len < 16 || $len > 20480) { # Out of Sync
  278. Log3 $name, 1, "FBAHA: resetting buffer as we are out of sync ($len)";
  279. $hash->{PARTIAL} = "";
  280. return "";
  281. }
  282. last if($len > length($data));
  283. $msg = substr($data, 0, $len);
  284. $data = substr($data, $len);
  285. last if(defined($local) && (!defined($regexp) || ($msg =~ m/$regexp/)));
  286. $hash->{"${name}_MSGCNT"}++;
  287. $hash->{"${name}_TIME"} = TimeNow();
  288. $hash->{RAWMSG} = $msg;
  289. my %addvals = (RAWMSG => $msg);
  290. Dispatch($hash, $msg, \%addvals) if($init_done);
  291. $msg = undef;
  292. }
  293. $hash->{PARTIAL} = $data;
  294. $hash->{READ_TS} = gettimeofday() if($data);
  295. return $msg if(defined($local));
  296. return undef;
  297. }
  298. #####################################
  299. # This is a direct read for commands like get
  300. sub
  301. FBAHA_ReadAnswer($$$)
  302. {
  303. my ($hash, $arg, $regexp) = @_;
  304. return ("No FD (dummy device?)", undef)
  305. if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD})));
  306. for(;;) {
  307. return ("Device lost when reading answer for get $arg", undef)
  308. if(!$hash->{FD});
  309. my $rin = '';
  310. vec($rin, $hash->{FD}, 1) = 1;
  311. my $nfound = select($rin, undef, undef, 3);
  312. if($nfound <= 0) {
  313. next if ($! == EAGAIN() || $! == EINTR());
  314. my $err = ($! ? $! : "Timeout");
  315. #$hash->{TIMEOUT} = 1;
  316. #DevIo_Disconnected($hash);
  317. return("FBAHA_ReadAnswer $arg: $err", undef);
  318. }
  319. my $buf = DevIo_SimpleRead($hash);
  320. return ("No data", undef) if(!defined($buf));
  321. my $ret = FBAHA_Read($hash, $buf, $regexp);
  322. return (undef, $ret) if(defined($ret));
  323. }
  324. }
  325. #####################################
  326. sub
  327. FBAHA_Ready($)
  328. {
  329. my ($hash) = @_;
  330. return DevIo_OpenDev($hash, 1, "FBAHA_DoInit")
  331. if($hash->{STATE} eq "disconnected");
  332. return 0;
  333. }
  334. 1;
  335. =pod
  336. =item summary (deprecated) connection to the Fritz!OS AHA Server
  337. =item summary_DE Anbindung des (veralteten) Fritz!OS AHA Servers
  338. =begin html
  339. <a name="FBAHA"></a>
  340. <h3>FBAHA</h3>
  341. <ul>
  342. <br>Note: Fritz!OS 6.90 and later does not offer the AHA service needed by
  343. this module. Use the successor FBAHAHTTP instead of this module.<br>
  344. This module connects to the AHA server (AVM Home Automation) on a FRITZ!Box.
  345. It serves as the "physical" counterpart to the <a href="#FBDECT">FBDECT</a>
  346. devices. Note: you have to enable the access to this feature in the FRITZ!Box
  347. frontend first.
  348. <br><br>
  349. <a name="FBAHAdefine"></a>
  350. <b>Define</b>
  351. <ul>
  352. <code>define &lt;name&gt; FBAHA &lt;device&gt;</code>
  353. <br>
  354. <br>
  355. &lt;device&gt; is either a &lt;host&gt;:&lt;port&gt; combination, where
  356. &lt;host&gt; is normally the address of the FRITZ!Box running the AHA server
  357. (fritz.box or localhost), and &lt;port&gt; 2002, or
  358. UNIX:SEQPACKET:/var/tmp/me_avm_home_external.ctl, the latter only works on
  359. the fritz.box. With FRITZ!OS 5.50 the network port is available, on some
  360. Labor variants only the UNIX socket is available.<br>
  361. Example:
  362. <ul>
  363. <code>define fb1 FBAHA fritz.box:2002</code><br>
  364. <code>define fb1 FBAHA UNIX:SEQPACKET:/var/tmp/me_avm_home_external.ctl</code><br>
  365. </ul>
  366. </ul>
  367. <br>
  368. <a name="FBAHAset"></a>
  369. <b>Set</b>
  370. <ul>
  371. <li>createDevs<br>
  372. create a FHEM device for each DECT device found on the AHA-Host, see also
  373. get devList.
  374. </li>
  375. <li>reopen<br>
  376. close and reopen the connection to the AHA server. Debugging only.
  377. </li>
  378. <li>reregister<br>
  379. release existing registration handle, and get a new one. Debugging only.
  380. </li>
  381. </ul>
  382. <br>
  383. <a name="FBAHAget"></a>
  384. <b>Get</b>
  385. <ul>
  386. <li>devList<br>
  387. return a list of devices with short info.
  388. </li>
  389. </ul>
  390. <br>
  391. <a name="FBAHAattr"></a>
  392. <b>Attributes</b>
  393. <ul>
  394. <li><a href="#dummy">dummy</a></li>
  395. </ul>
  396. <br>
  397. <a name="FBAHAevents"></a>
  398. <b>Generated events:</b>
  399. <ul>
  400. <li>UNDEFINED FBDECT_$ahaName_${NR} FBDECT $id"
  401. </li>
  402. </ul>
  403. <br>
  404. As sometimes the FRITZ!Box reassigns the internal id's of the FBDECT devices,
  405. the FBAHA module compares upon connect/reconnect the stored names (FBNAME)
  406. with the current value. This feature will only work, if you assign each
  407. FBDECT device a unique Name in the FRITZ!Box, and excecute the FHEM "get
  408. FBDECTDEVICE devInfo" command, which saves the FBNAME reading.<br>
  409. </ul>
  410. =end html
  411. =begin html_DE
  412. <a name="FBAHA"></a>
  413. <h3>FBAHA</h3>
  414. <ul>
  415. <br>Achtung: ab Fritz!OS 6.90 ist der ben&ouml;tigte Dienst deaktiviert,
  416. bitte den Nachfolger FBAHAHTTP verwenden.<br>
  417. Dieses Modul verbindet sich mit dem AHA (AVM Home Automation) Server auf
  418. einem FRITZ!Box. Es dient als "physikalisches" Gegenst&uuml;ck zum <a
  419. href="#FBDECT">FBDECT</a> Modul. Als erstes muss der Zugang zu diesen Daten
  420. in der FRITZ!Box Web-Oberfl&auml;che aktiviert werden.
  421. <br><br>
  422. <a name="FBAHAdefine"></a>
  423. <b>Define</b>
  424. <ul>
  425. <code>define &lt;name&gt; FBAHA &lt;device&gt;</code>
  426. <br>
  427. <br>
  428. &lt;host&gt; ist normalerweise die Adresse der FRITZ!Box, wo das AHA Server
  429. l&auml;uft (fritz.box oder localhost), &lt;port&gt; ist 2002.
  430. &lt;device&gt; is entweder a eine Kombianation aus &lt;host&gt;:&lt;port&gt;,
  431. wobei &lt;host&gt; die Adresse der FRITZ!Box ist (localhost AUF dem
  432. FRITZ.BOX) und &lt;port&gt; 2002 ist, oder
  433. UNIX:SEQPACKET:/var/tmp/me_avm_home_external.ctl, wobei das nur fuer
  434. FHEM@FRITZ!BOX zur Verf&uuml;gung steht. Mit FRITZ!OS 5.50 steht auch der
  435. Netzwerkport zur Verf&uuml;gung, auf manchen Laborvarianten nur das UNIX
  436. socket.<br>
  437. Beispiel:
  438. <ul>
  439. <code>define fb1 FBAHA fritz.box:2002</code><br>
  440. <code>define fb1 FBAHA UNIX:SEQPACKET:/var/tmp/me_avm_home_external.ctl</code><br>
  441. </ul>
  442. </ul>
  443. <br>
  444. <a name="FBAHAset"></a>
  445. <b>Set</b>
  446. <ul>
  447. <li>createDevs<br>
  448. legt FHEM Ger&auml;te an f&uuml;r jedes auf dem AHA-Server gefundenen DECT
  449. Eintrag, siehe auch "get devList".
  450. </li>
  451. <li>reopen<br>
  452. Schlie&szlig;t und &ouml;ffnet die Verbindung zum AHA Server. Nur f&uuml;r
  453. debugging.
  454. </li>
  455. <li>reregister<br>
  456. Gibt den AHA handle frei, und registriert sich erneut beim AHA Server. Nur
  457. f&uuml;r debugging.
  458. </li>
  459. </ul>
  460. <br>
  461. <a name="FBAHAget"></a>
  462. <b>Get</b>
  463. <ul>
  464. <li>devList<br>
  465. liefert die Liste aller DECT-Eintr&auml;ge der AHA Server zur&uuml;ck, mit
  466. einem kurzen Info.
  467. </li>
  468. </ul>
  469. <br>
  470. <a name="FBAHAattr"></a>
  471. <b>Attributes</b>
  472. <ul>
  473. <li><a href="#dummy">dummy</a></li>
  474. </ul>
  475. <br>
  476. <a name="FBAHAevents"></a>
  477. <b>Generierte Events:</b>
  478. <ul>
  479. <li>UNDEFINED FBDECT_$ahaName_${NR} FBDECT $id"
  480. </li>
  481. </ul>
  482. <br>
  483. Da manchmal die FRITZ!Box die interne Nummer der FBDECT Ger&auml;te
  484. neu vergibt, werden beim Verbindungsaufbau zum AHA Server die gespeicherten
  485. Namen (FBNAME) mit dem aktuellen Wert verglichen. Damit das funktioniert,
  486. m&uuml;ssen alle FBDECT Ger&auml;te auf dem FRITZ!Box einen eindeutigen Namen
  487. bekommen, und in FHEM muss f&uuml;r alle Ger&auml;te "get FBDECTDEVICE
  488. devInfo" ausgef&uuml;hrt werden, um FBNAME als Reading zu speichern.<br>
  489. </ul>
  490. =end html_DE
  491. =cut