67_ECMDDevice.pm 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. # $Id: 67_ECMDDevice.pm 12877 2016-12-26 09:15:55Z neubert $
  2. ##############################################################################
  3. #
  4. # 67_ECMDDevice.pm
  5. # Copyright by Dr. Boris Neubert
  6. # e-mail: omega at online dot de
  7. #
  8. # This file is part of fhem.
  9. #
  10. # Fhem is free software: you can redistribute it and/or modify
  11. # it under the terms of the GNU General Public License as published by
  12. # the Free Software Foundation, either version 2 of the License, or
  13. # (at your option) any later version.
  14. #
  15. # Fhem is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU General Public License
  21. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  22. #
  23. ##############################################################################
  24. package main;
  25. use strict;
  26. use warnings;
  27. use Time::HiRes qw(gettimeofday);
  28. sub ECMDDevice_Get($@);
  29. sub ECMDDevice_Set($@);
  30. sub ECMDDevice_Attr($@);
  31. sub ECMDDevice_Define($$);
  32. ###################################
  33. sub
  34. ECMDDevice_Initialize($)
  35. {
  36. my ($hash) = @_;
  37. $hash->{Match} = ".+";
  38. $hash->{GetFn} = "ECMDDevice_Get";
  39. $hash->{SetFn} = "ECMDDevice_Set";
  40. $hash->{DefFn} = "ECMDDevice_Define";
  41. $hash->{ParseFn} = "ECMDDevice_Parse";
  42. $hash->{AttrFn} = "ECMDDevice_Attr";
  43. $hash->{AttrList} = "IODev class noState ".
  44. $readingFnAttributes;
  45. }
  46. ###################################
  47. sub
  48. ECMDDevice_AnalyzeCommand($$)
  49. {
  50. my ($hash, $ecmd)= @_;
  51. Log3 $hash, 5, "ECMDDevice: Analyze command >$ecmd<";
  52. return AnalyzePerlCommand(undef, $ecmd);
  53. }
  54. #############################
  55. sub
  56. ECMDDevice_GetDeviceParams($)
  57. {
  58. my ($hash)= @_;
  59. my $classname= $hash->{fhem}{classname};
  60. my $IOhash= $hash->{IODev};
  61. if(defined($IOhash->{fhem}{classDefs}{$classname}{params})) {
  62. my $params= $IOhash->{fhem}{classDefs}{$classname}{params};
  63. return split("[ \t]+", $params);
  64. }
  65. return;
  66. }
  67. #############################
  68. sub
  69. ECMDDevice_DeviceParams2Specials($)
  70. {
  71. my ($hash)= @_;
  72. my %specials= (
  73. "%NAME" => $hash->{NAME},
  74. "%TYPE" => $hash->{TYPE}
  75. );
  76. my @deviceparams= ECMDDevice_GetDeviceParams($hash);
  77. foreach my $param (@deviceparams) {
  78. $specials{"%".$param}= $hash->{fhem}{params}{$param};
  79. }
  80. return %specials;
  81. }
  82. sub
  83. ECMDDevice_GetCachedSpecials($)
  84. {
  85. my($hash)= @_;
  86. if(!defined($hash->{fhem}{cache}{specials})) {
  87. my %specials= ECMDDevice_DeviceParams2Specials($hash);
  88. $hash->{fhem}{cache}{specials}= \%specials;
  89. }
  90. return %{$hash->{fhem}{cache}{specials}}
  91. }
  92. sub
  93. ECMDDevice_ReplaceSpecials($%)
  94. {
  95. my ($s, %specials)= @_;
  96. return $s unless(defined($s));
  97. # perform macro substitution
  98. foreach my $special (keys %specials) {
  99. $s =~ s/$special/$specials{$special}/g;
  100. }
  101. return $s;
  102. }
  103. ###################################
  104. sub
  105. ECMDDevice_Changed($$$)
  106. {
  107. my ($hash, $cmd, $value)= @_;
  108. #Debug "Device changed: $cmd $value";
  109. readingsBeginUpdate($hash);
  110. readingsBulkUpdate($hash, $cmd, $value) if(defined($value) && $value ne "");
  111. my $state;
  112. my $classname= $hash->{fhem}{classname};
  113. my $IOhash= $hash->{IODev};
  114. if(defined($IOhash->{fhem}{classDefs}{$classname}{state})) {
  115. if($cmd eq $IOhash->{fhem}{classDefs}{$classname}{state}) {
  116. $state= defined($value) ? $value : "?";
  117. #Debug "cmd= $cmd, setting state to $state (OVERRIDE)";
  118. }
  119. } else {
  120. $state= $cmd;
  121. $state.= " $value" if(defined($value) && $value ne "");
  122. #Debug "cmd= $cmd, setting state to $state (DEFAULT)";
  123. }
  124. if(!AttrVal($hash->{NAME}, "noState", 0)) {
  125. readingsBulkUpdate($hash, "state", $state) if(defined($state));
  126. }
  127. readingsEndUpdate($hash, 1);
  128. my $name= $hash->{NAME};
  129. #Log3 $hash, 4 , "ECMDDevice $name $state" if(defined($state));
  130. return $state;
  131. }
  132. ###################################
  133. sub
  134. ECMDDevice_PostProc($$$%)
  135. {
  136. my ($hash, $postproc, $value, %specials)= @_;
  137. if($postproc) {
  138. my $command= ECMDDevice_ReplaceSpecials($postproc, %specials);
  139. $_= $value;
  140. Log3 $hash, 5, "Postprocessing \"" . dq($value) . "\" with perl command $command.";
  141. $value= AnalyzePerlCommand(undef, $command);
  142. Log3 $hash, 5, "Postprocessed value is \"" . dq($value) . "\".";
  143. }
  144. return $value;
  145. }
  146. sub
  147. ECMDDevice_EvalCommand($$$)
  148. {
  149. my ($hash, $command, $value)= @_;
  150. if($command) {
  151. $_= $value;
  152. Log3 $hash, 5, "Postprocessing \"" . dq($value) . "\" with perl command $command.";
  153. $value= AnalyzePerlCommand(undef, $command);
  154. Log3 $hash, 5, "Postprocessed value is \"" . dq($value) . "\".";
  155. }
  156. return $value;
  157. }
  158. sub
  159. ECMDDevice_GetCachedReadingsCommand($$$)
  160. {
  161. my ($hash, $classDef, $reading)= @_;
  162. my $command= $hash->{fhem}{cache}{readings}{command}{$reading};
  163. if(!defined($command)) {
  164. my %specials= ECMDDevice_GetCachedSpecials($hash);
  165. my $postproc= $classDef->{readings}{$reading}{postproc};
  166. if($postproc) {
  167. $command= ECMDDevice_ReplaceSpecials($postproc, %specials);
  168. } else {
  169. $command= undef;
  170. }
  171. $hash->{fhem}{cache}{readings}{command}{$reading}= $command;
  172. }
  173. return $command;
  174. }
  175. ###################################
  176. sub
  177. ECMDDevice_Get($@)
  178. {
  179. my ($hash, @a)= @_;
  180. my $name= $hash->{NAME};
  181. my $type= $hash->{TYPE};
  182. return "get $name needs at least one argument" if(int(@a) < 2);
  183. my $cmdname= $a[1];
  184. my $IOhash= $hash->{IODev};
  185. my $classname= $hash->{fhem}{classname};
  186. if(!defined($IOhash->{fhem}{classDefs}{$classname}{gets}{$cmdname})) {
  187. my $gets= $IOhash->{fhem}{classDefs}{$classname}{gets};
  188. return "$name error: unknown argument $cmdname, choose one of " .
  189. (join " ", sort keys %$gets);
  190. }
  191. my $ecmd= $IOhash->{fhem}{classDefs}{$classname}{gets}{$cmdname}{cmd};
  192. my $expect= $IOhash->{fhem}{classDefs}{$classname}{gets}{$cmdname}{expect};
  193. my $params= $IOhash->{fhem}{classDefs}{$classname}{gets}{$cmdname}{params};
  194. my $postproc= $IOhash->{fhem}{classDefs}{$classname}{gets}{$cmdname}{postproc};
  195. my %specials= ECMDDevice_GetCachedSpecials($hash);
  196. # add specials for command
  197. if($params) {
  198. shift @a; shift @a;
  199. my @params= split('[\s]+', $params);
  200. return "Wrong number of parameters." if($#a != $#params);
  201. my $i= 0;
  202. foreach my $param (@params) {
  203. Log3 $hash, 5, "Parameter %". $param . " is " . $a[$i];
  204. $specials{"%".$param}= $a[$i++];
  205. }
  206. }
  207. $ecmd= ECMDDevice_ReplaceSpecials($ecmd, %specials);
  208. $expect= ECMDDevice_ReplaceSpecials($expect, %specials);
  209. my $r = ECMDDevice_AnalyzeCommand($hash, $ecmd);
  210. my $v= IOWrite($hash, $r, $expect);
  211. $v= ECMDDevice_PostProc($hash, $postproc, $v, %specials);
  212. return ECMDDevice_Changed($hash, $cmdname, $v);
  213. }
  214. #############################
  215. sub
  216. ECMDDevice_Set($@)
  217. {
  218. my ($hash, @a)= @_;
  219. my $name= $hash->{NAME};
  220. my $type= $hash->{TYPE};
  221. return "set $name needs at least one argument" if(int(@a) < 2);
  222. my $cmdname= $a[1];
  223. my $IOhash= $hash->{IODev};
  224. my $classname= $hash->{fhem}{classname};
  225. if(!defined($IOhash->{fhem}{classDefs}{$classname}{sets}{$cmdname})) {
  226. my $sets= $IOhash->{fhem}{classDefs}{$classname}{sets};
  227. return "Unknown argument $cmdname, choose one of " . join(' ', sort keys %$sets);
  228. }
  229. my $ecmd= $IOhash->{fhem}{classDefs}{$classname}{sets}{$cmdname}{cmd};
  230. my $expect= $IOhash->{fhem}{classDefs}{$classname}{sets}{$cmdname}{expect};
  231. my $params= $IOhash->{fhem}{classDefs}{$classname}{sets}{$cmdname}{params};
  232. my $postproc= $IOhash->{fhem}{classDefs}{$classname}{sets}{$cmdname}{postproc};
  233. my %specials= ECMDDevice_GetCachedSpecials($hash);
  234. # add specials for command
  235. if($params) {
  236. shift @a; shift @a;
  237. my @params= split('[\s]+', $params);
  238. return "Wrong number of parameters." if($#a != $#params);
  239. my $i= 0;
  240. foreach my $param (@params) {
  241. $specials{"%".$param}= $a[$i++];
  242. }
  243. }
  244. $ecmd= ECMDDevice_ReplaceSpecials($ecmd, %specials);
  245. $expect= ECMDDevice_ReplaceSpecials($expect, %specials);
  246. my $r = ECMDDevice_AnalyzeCommand($hash, $ecmd);
  247. my $v= IOWrite($hash, $r, $expect);
  248. $v= ECMDDevice_PostProc($hash, $postproc, $v, %specials);
  249. ECMDDevice_Changed($hash, $cmdname, $v); # was: return ECMDDevice_Changed($hash, $cmdname, $v);
  250. return undef;
  251. }
  252. #############################
  253. sub
  254. ECMDDevice_GetCachedReadingsMatch($$$)
  255. {
  256. my ($hash, $classDef, $r)= @_;
  257. my $regex= $hash->{fhem}{cache}{readings}{match}{$r};
  258. if(!defined($regex)) {
  259. my %specials= ECMDDevice_GetCachedSpecials($hash);
  260. $regex= ECMDDevice_ReplaceSpecials($classDef->{readings}{$r}{match}, %specials);
  261. $hash->{fhem}{cache}{readings}{match}{$r}= $regex;
  262. }
  263. return $regex;
  264. }
  265. #############################
  266. sub
  267. ECMDDevice_Parse($$)
  268. {
  269. # we never come here if $msg does not match $hash->{MATCH} in the first place
  270. # NOTE: we will update all matching readings for all devices, not just the first!
  271. my ($IOhash, $message) = @_; # IOhash points to the ECMD, not to the ECMDDevice
  272. my @matches;
  273. my $name= $IOhash->{NAME};
  274. my $ts= gettimeofday();
  275. if(defined(AttrVal($name, "partial", undef))) {
  276. if(!defined($IOhash->{fhem}{partial})) {
  277. $IOhash->{fhem}{partial}{ts}= $ts;
  278. $IOhash->{fhem}{partial}{msg}= "";
  279. }
  280. my $partial= $IOhash->{fhem}{partial}{msg};
  281. #Debug "$name: partial message \"" . escapeLogLine($IOhash->{fhem}{partial}{msg}) . "\" recorded at $ts";
  282. if($partial ne "") {
  283. # clear partial message if expired
  284. my $timeout= AttrVal($name, "partial", 1);
  285. my $t0= $IOhash->{fhem}{partial}{ts};
  286. if($ts-$t0> $timeout) {
  287. Log3 $IOhash, 5, "$name: partial message " . dq($partial) . " expired.";
  288. $partial= "";
  289. $IOhash->{fhem}{partial}{msg}= $partial;
  290. }
  291. }
  292. # prepend to recently received message
  293. $IOhash->{fhem}{partial}{ts}= $ts;
  294. if($partial ne "") {
  295. Log3 $IOhash, 5, "$name: merging partial message " . dq($partial) . " and " . dq($message);
  296. $message= $partial . $message;
  297. $IOhash->{fhem}{partial}{msg}= "";
  298. }
  299. } else {
  300. # clean the partial stuff for clarity
  301. if(defined($IOhash->{fhem}{partial})) {
  302. delete($IOhash->{fhem}{partial})
  303. }
  304. }
  305. #Debug "$name: analyzing \"" . escapeLogLine($message) . "\".";
  306. my @msgs;
  307. my $splitter= $IOhash->{fhem}{".split"};
  308. if(defined($splitter)) {
  309. #Debug "Splitting " . dq($message) . " at " . dq($splitter);
  310. @msgs= split(/(?<=$splitter)/, $message); # http://stackoverflow.com/questions/14907772/split-but-keep-delimiter
  311. #Debug scalar(@msgs) . " part(s)";
  312. Log3 $IOhash, 5, "$name: " . dq($message) . " split into " . scalar(@msgs) . " parts"
  313. if(scalar(@msgs)>1);
  314. #Debug "Split done.";
  315. } else {
  316. push @msgs, $message;
  317. }
  318. #my @unmatchedMsgs; # future use
  319. my $lastMsg= "";
  320. my $msgMatched;
  321. foreach my $msg (@msgs) {
  322. #Debug "$name: trying to find a match for \"" . escapeLogLine($msg) ."\"";
  323. Log3 $IOhash, 5, "$name: trying to match message " . dq($msg);
  324. $msgMatched= 0;
  325. # walk over all clients
  326. foreach my $d (keys %defs) {
  327. my $hash= $defs{$d};
  328. if($hash->{TYPE} eq "ECMDDevice" && $hash->{IODev} eq $IOhash) {
  329. my $classname= $hash->{fhem}{classname};
  330. next unless($classname);
  331. my $classDef= $IOhash->{fhem}{classDefs}{$classname};
  332. #Debug " Checking device $d with class $classname...";
  333. next unless(defined($classDef->{readings}));
  334. #Debug " Trying to find a match in class $classname...";
  335. # we run over all readings in that classdef
  336. foreach my $r (keys %{$classDef->{readings}}) {
  337. my $regex= ECMDDevice_GetCachedReadingsMatch($hash, $classDef, $r);
  338. #Debug " Trying to match reading $r with regular expression \"$regex\" (device $d, classdef $classname, reading $r).";
  339. if($msg =~ m/^$regex$/) {
  340. # we found a match
  341. Log3 $IOhash, 5, "$name: " . dq($msg) . " matches regex $regex for reading $r of device $d with class $classname";
  342. $msgMatched++;
  343. push @matches, $d;
  344. my $command= ECMDDevice_GetCachedReadingsCommand($hash, $classDef, $r);
  345. my $value= ECMDDevice_EvalCommand($hash, $command, $msg);
  346. #Log3 $hash, 5, "postprocessed value is $value";
  347. ECMDDevice_Changed($hash, $r, $value);
  348. }
  349. }
  350. }
  351. }
  352. #push @unmatchedMsgs, $msg unless($msgMatched); # future use
  353. }
  354. $lastMsg= $msgs[$#msgs] unless($msgMatched); # contains the last message if the last message is unmatched
  355. if(defined(AttrVal($name, "partial", undef)) && $lastMsg ne "") {
  356. # we come here if the last message was unmatched and we want partial messages
  357. if($#msgs>= 0) {
  358. # we had more messages; therefore the partial message belonged to the first message and needs
  359. # to be cleared
  360. $IOhash->{fhem}{partial}{msg}= "";
  361. }
  362. $IOhash->{fhem}{partial}{msg}.= $lastMsg; # append unmatched message
  363. Log3 $IOhash, 5, "$name: partial message " . dq($lastMsg) . " kept";
  364. #Debug "$name: partial message \"" . escapeLogLine($IOhash->{fhem}{partial}{msg}) . "\" kept.";
  365. }
  366. return @matches if(@matches);
  367. # we come here if no match is found
  368. # NOTE: In a split message, undefined messages are not reported if there was at least one match.
  369. # return "UNDEFINED ECMDDevice message $message";
  370. return "";
  371. }
  372. #####################################
  373. sub
  374. ECMDDevice_AssignClass($$@)
  375. {
  376. my ($hash,$classname,@a)= @_;
  377. my $name= $hash->{NAME};
  378. my $IOhash= $hash->{IODev};
  379. if(!defined($IOhash)) {
  380. my $err= "ECMDDevice $name error: no I/O device.";
  381. Log3 $hash, 1, $err;
  382. return $err;
  383. }
  384. if(!defined($IOhash->{fhem}{classDefs}{$classname}{filename})) {
  385. my $err= "ECMDDevice $name error: unknown class $classname (I/O device is "
  386. . $IOhash->{NAME} . ").";
  387. Log3 $hash, 1, $err;
  388. return $err;
  389. }
  390. $hash->{fhem}{classname}= $classname;
  391. my @prms= ECMDDevice_GetDeviceParams($hash);
  392. my $numparams= 0;
  393. $numparams= $#prms+1 if(defined($prms[0]));
  394. #Log 5, "ECMDDevice $classname requires $numparams parameter(s): ". join(" ", @prms);
  395. # verify identical number of parameters
  396. if($numparams != $#a+1) {
  397. my $err= "$name error: wrong number of parameters";
  398. Log3 $hash, 1, $err;
  399. return $err;
  400. }
  401. # set parameters
  402. for(my $i= 0; $i< $numparams; $i++) {
  403. $hash->{fhem}{params}{$prms[$i]}= $a[$i];
  404. }
  405. return undef; # OK
  406. }
  407. #####################################
  408. sub
  409. ECMDDevice_Attr($@)
  410. {
  411. my @a = @_;
  412. my $hash= $defs{$a[1]};
  413. if($a[0] eq "set" && $a[2] eq "class") {
  414. my ($classname,@prms)= split " ", $a[3];
  415. return ECMDDevice_AssignClass($hash, $classname, @prms);
  416. } else {
  417. return undef;
  418. }
  419. }
  420. #############################
  421. sub
  422. ECMDDevice_Define($$)
  423. {
  424. my ($hash, $def) = @_;
  425. my @a = split("[ \t]+", $def);
  426. return "Usage: define <name> ECMDDevice [<classname> [...]]" if(int(@a) < 2);
  427. my $name= $a[0];
  428. AssignIoPort($hash);
  429. if(int(@a)> 2) {
  430. my $classname= $a[2];
  431. shift @a; shift @a; shift @a;
  432. return ECMDDevice_AssignClass($hash, $classname, @a);
  433. } else {
  434. return undef;
  435. }
  436. # create cache stubs
  437. $hash->{fhem}{cache}{specials}= ();
  438. $hash->{fhem}{cache}{readings}{match}= ();
  439. $hash->{fhem}{cache}{readings}{command}= ();
  440. }
  441. #############################
  442. 1;
  443. #############################
  444. =pod
  445. =item device
  446. =item summary user-defined device communicating through ECMD (logical device)
  447. =item summary_DE benutzerdefiniertes via ECMD kommunizierendes Ger&auml;t (logisches Ger&auml;t)
  448. =begin html
  449. <a name="ECMDDevice"></a>
  450. <h3>ECMDDevice</h3>
  451. <ul>
  452. <br>
  453. <a name="ECMDDevicedefine"></a>
  454. <b>Define</b>
  455. <ul>
  456. <code>define &lt;name&gt; ECMDDevice [&lt;classname&gt; [&lt;parameter1&gt; [&lt;parameter2&gt; [&lt;parameter3&gt; ... ]]]]</code>
  457. <br><br>
  458. Defines a logical ECMD device. The number of given parameters must match those given in
  459. the <a href="#ECMDClassdef">class definition</a> of the device class <code>&lt;classname&gt;</code>.<p>
  460. Normally, the logical ECMDDevice is attached to the latest previously defined physical ECMD device
  461. for I/O. Use the <code>IODev</code> attribute of the logical ECMDDevice to attach to any
  462. physical ECMD device, e.g. <code>attr myRelais2 IODev myAVRNETIO</code>. In such a case the correct
  463. reference to the class cannot be made at the time of definition of the device. Thus, you need to
  464. omit the &lt;classname&gt; and &lt;parameter&gt; references in the definition of the device and use the
  465. <code>class</code> <a href="#ECMDDeviceattr">attribute</a> instead.
  466. <br><br>
  467. Examples:
  468. <ul>
  469. <code>define myADC ECMDDevice ADC</code><br>
  470. <code>define myRelais1 ECMDDevice relais 8</code><br>
  471. <code>define myRelais2 ECMDDevice</code><br>
  472. <code>attr myRelais2 IODev myAVRNETIO</code><br>
  473. <code>attr myRelais2 class relais 8</code>
  474. </ul>
  475. <br>
  476. </ul>
  477. <a name="ECMDDeviceset"></a>
  478. <b>Set</b>
  479. <ul>
  480. <code>set &lt;name&gt; &lt;commandname&gt; [&lt;parameter1&gt; [&lt;parameter2&gt; [&lt;parameter3&gt; ... ]]]</code>
  481. <br><br>
  482. The number of given parameters must match those given for the set command <code>&lt;commandname&gt;</code> definition in
  483. the <a href="#ECMDClassdef">class definition</a>.<br><br>
  484. If <code>set &lt;commandname&gt;</code> is invoked the perl special in curly brackets from the command definition
  485. is evaluated and the result is sent to the physical ECMD device.
  486. <br><br>
  487. Example:
  488. <ul>
  489. <code>set myRelais1 on</code><br>
  490. <code>set myDisplay text This\x20text\x20has\x20blanks!</code><br>
  491. </ul>
  492. <br>
  493. </ul>
  494. <a name="ECMDDeviceget"></a>
  495. <b>Get</b>
  496. <ul>
  497. <code>get &lt;name&gt; &lt;commandname&gt; [&lt;parameter1&gt; [&lt;parameter2&gt; [&lt;parameter3&gt; ... ]]]</code>
  498. <br><br>
  499. The number of given parameters must match those given for the get command <code>&lt;commandname&gt;</code> definition in
  500. the <a href="#ECMDClassdef">class definition</a>.<br><br>
  501. If <code>get &lt;commandname&gt;</code> is invoked the perl special in curly brackets from the command definition
  502. is evaluated and the result is sent to the physical ECMD device. The response from the physical ECMD device is returned
  503. and the state of the logical ECMD device is updated accordingly.
  504. <br><br>
  505. Example:
  506. <ul>
  507. <code>get myADC value 3</code><br>
  508. </ul>
  509. <br>
  510. </ul>
  511. <a name="ECMDDeviceattr"></a>
  512. <b>Attributes</b>
  513. <ul>
  514. <li>class<br>
  515. If you omit the &lt;classname&gt; and &lt;parameter&gt; references in the
  516. <a href="#ECMDDevicedefine">definition</a> of the device, you have to add them
  517. separately as an attribute. Example: <code>attr myRelais2 class relais 8</code>.</li>
  518. <li>noState<br>
  519. Changes of readings do not change the state reading if this attribute is set to a non-zero value.
  520. For example, this is desirable if you need to avoid the second event created by changing the state
  521. reading. Previously created state readings can be deleted by means of <a href="#deletereading">deletereading</a>.
  522. The user can define the value shown in the state of the device by means
  523. of the <a href="#stateFormat">stateFormat</a> attribute.</li>
  524. <li><a href="#verbose">verbose</a></li>
  525. <li><a href="#eventMap">eventMap</a></li>
  526. <li><a href="#IODev">IODev</a></li>
  527. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  528. </ul>
  529. <br><br>
  530. <b>Example 1</b>
  531. <br><br>
  532. <ul>
  533. The following example shows how to access the ADC of the AVR-NET-IO board from
  534. <a href="http://www.pollin.de">Pollin</a> with
  535. <a href="http://www.ethersex.de/index.php/ECMD">ECMD</a>-enabled
  536. <a href="http://www.ethersex.de">Ethersex</a> firmware.<br><br>
  537. The class definition file <code>/etc/fhem/ADC.classdef</code> looks as follows:<br><br>
  538. <code>
  539. get value cmd {"adc get %channel\n"} <br>
  540. get value params channel<br>
  541. get value expect "\d+\n"<br>
  542. get value postproc { s/^(\d+)\n$/$1/;; $_ }<br>
  543. </code>
  544. <br>
  545. In the fhem configuration file or on the fhem command line we do the following:<br><br>
  546. <code>
  547. define AVRNETIO ECMD telnet 192.168.0.91:2701 # define the physical device<br>
  548. attr AVRNETIO classdefs ADC=/etc/fhem/ADC.classdef # define the device class ADC<br>
  549. define myADC ECDMDevice ADC # define the logical device myADC with device class ADC<br>
  550. get myADC value 1 # retrieve the value of analog/digital converter number 1<br>
  551. </code>
  552. <br>
  553. The get command is evaluated as follows: <code>get value</code> has one named parameter
  554. <code>channel</code>. In the example the literal <code>1</code> is given and thus <code>%channel</code>
  555. is replaced by <code>1</code> to yield <code>"adc get 1\n"</code> after macro substitution. Perl
  556. evaluates this to a literal string which is send as a plain ethersex command to the AVR-NET-IO. The
  557. board returns something like <code>024\n</code> for the current value of analog/digital converter number 1. The postprocessor keeps only the digits.
  558. <br><br>
  559. </ul>
  560. <b>Example 2</b>
  561. <br><br>
  562. <ul>
  563. The following example shows how to switch a relais driven by pin 3 (bit mask 0x08) of I/O port 2 on for
  564. one second and then off again.<br><br>
  565. The class definition file <code>/etc/fhem/relais.classdef</code> looks as follows:<br><br>
  566. <code>
  567. params pinmask<br>
  568. set on cmd {"io set ddr 2 ff\n\000ioset port 2 0%pinmask\n\000wait 1000\n\000io set port 2 00\n"}<br>
  569. set on expect ".*"<br>
  570. set on postproc {s/^OK\nOK\nOK\nOK\n$/success/; "$_" eq "success" ? "ok" : "error"; }<br>
  571. </code>
  572. <br>
  573. In the fhem configuration file or on the fhem command line we do the following:<br><br>
  574. <code>
  575. define AVRNETIO ECMD telnet 192.168.0.91:2701 # define the physical device<br>
  576. attr AVRNETIO classdefs relais=/etc/fhem/relais.classdef # define the device class relais<br>
  577. attr AVRNETIO requestSeparator \000<br>
  578. define myRelais ECMDDevice 8 # define the logical device myRelais with pin mask 8<br>
  579. set myRelais on # execute the "on" command<br>
  580. </code>
  581. <br>
  582. The set command is evaluated as follows: <code>%pinmask</code>
  583. is replaced by <code>8</code> to yield
  584. <code>"io set ddr 2 ff\n\000io set port 2 08\n\000wait 1000\n\000io set port 2 00\n\000"</code> after macro substitution. Perl
  585. evaluates this to a literal string. This string is split into lines (with trailing newline characters)
  586. <code>
  587. <ul>
  588. <li>io set ddr 2 ff\n</li>
  589. <li>ioset port 2 08\n</li>
  590. <li>wait 1000\n</li>
  591. <li>io set port 2 00\n</li>
  592. </ul>
  593. </code>
  594. These lines are sent as a plain ethersex commands to the AVR-NET-IO one by one. After
  595. each line the answer from the physical device is read back. They are concatenated and returned
  596. for further processing by the <code>postproc</code> command.
  597. For any of the four plain ethersex commands, the AVR-NET-IO returns the string <code>OK\n</code>. They are
  598. concatenated. The postprocessor takes the result from <code>$_</code>,
  599. substitutes it by the string <code>success</code> if it is <code>OK\nOK\nOK\nOK\n</code>, and then either
  600. returns the string <code>ok</code> or the string <code>error</code>. If the responseSeparator was set to \000,
  601. the result string would be <code>OK\n\000OK\n\000OK\n\000OK\n\000</code> instead of <code>OK\nOK\nOK\nOK\n</code>.
  602. <br><br>
  603. </ul>
  604. <b>Example 3</b>
  605. <br><br>
  606. <ul>
  607. The following example shows how to implement a sandbox.<br><br>
  608. The class definition file <code>/etc/fhem/DummyServer.classdef</code> looks as follows:<br><br>
  609. <code>
  610. reading foo match "\d+\n"<br>
  611. reading foo postproc { s/^(\d+).*$/$1/;; $_ }<br>
  612. </code>
  613. <br>
  614. In the fhem configuration file or on the fhem command line we do the following:<br><br>
  615. <code>
  616. define myDummyServer ECMD telnet localhost:9999 # define the physical device<br>
  617. set myDummyServer classdef DummyServer /etc/fhem/DummyServer.classdef # define the device class DummyServer<br>
  618. define myDummyClient ECDMDevice DummyServer # define a logical device with device class DummyServer<br>
  619. </code>
  620. <p>
  621. On a Unix command line, run <code>netcat -l 9999</code>. This makes netcat listening on port 9999. Data received on that port are printed on stdout. Data input from stdin is sent to the other end of an incoming connection.<p>
  622. Start FHEM.<p>
  623. Then enter the number 4711 at the stdin of the running netcat server.<p>
  624. FHEM sees <code>4711\n</code> coming in from the netcat dummy server. The incoming string matches the regular expression of the <code>foo</code> reading. The postprocessor is used to strip any trailing garbage from the digits. The result 4711 is used to update the <code>foo</code> reading.
  625. <br><br>
  626. </ul>
  627. </ul>
  628. =end html
  629. =cut