| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754 |
- # $Id: 67_ECMDDevice.pm 12877 2016-12-26 09:15:55Z neubert $
- ##############################################################################
- #
- # 67_ECMDDevice.pm
- # Copyright by Dr. Boris Neubert
- # e-mail: omega at online dot de
- #
- # This file is part of fhem.
- #
- # Fhem is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 2 of the License, or
- # (at your option) any later version.
- #
- # Fhem is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with fhem. If not, see <http://www.gnu.org/licenses/>.
- #
- ##############################################################################
- package main;
- use strict;
- use warnings;
- use Time::HiRes qw(gettimeofday);
- sub ECMDDevice_Get($@);
- sub ECMDDevice_Set($@);
- sub ECMDDevice_Attr($@);
- sub ECMDDevice_Define($$);
- ###################################
- sub
- ECMDDevice_Initialize($)
- {
- my ($hash) = @_;
- $hash->{Match} = ".+";
-
- $hash->{GetFn} = "ECMDDevice_Get";
- $hash->{SetFn} = "ECMDDevice_Set";
- $hash->{DefFn} = "ECMDDevice_Define";
- $hash->{ParseFn} = "ECMDDevice_Parse";
- $hash->{AttrFn} = "ECMDDevice_Attr";
- $hash->{AttrList} = "IODev class noState ".
- $readingFnAttributes;
- }
- ###################################
- sub
- ECMDDevice_AnalyzeCommand($$)
- {
- my ($hash, $ecmd)= @_;
- Log3 $hash, 5, "ECMDDevice: Analyze command >$ecmd<";
- return AnalyzePerlCommand(undef, $ecmd);
- }
- #############################
- sub
- ECMDDevice_GetDeviceParams($)
- {
- my ($hash)= @_;
- my $classname= $hash->{fhem}{classname};
- my $IOhash= $hash->{IODev};
- if(defined($IOhash->{fhem}{classDefs}{$classname}{params})) {
- my $params= $IOhash->{fhem}{classDefs}{$classname}{params};
- return split("[ \t]+", $params);
- }
- return;
- }
- #############################
- sub
- ECMDDevice_DeviceParams2Specials($)
- {
- my ($hash)= @_;
- my %specials= (
- "%NAME" => $hash->{NAME},
- "%TYPE" => $hash->{TYPE}
- );
- my @deviceparams= ECMDDevice_GetDeviceParams($hash);
- foreach my $param (@deviceparams) {
- $specials{"%".$param}= $hash->{fhem}{params}{$param};
- }
- return %specials;
- }
- sub
- ECMDDevice_GetCachedSpecials($)
- {
- my($hash)= @_;
-
- if(!defined($hash->{fhem}{cache}{specials})) {
- my %specials= ECMDDevice_DeviceParams2Specials($hash);
- $hash->{fhem}{cache}{specials}= \%specials;
- }
-
- return %{$hash->{fhem}{cache}{specials}}
- }
- sub
- ECMDDevice_ReplaceSpecials($%)
- {
- my ($s, %specials)= @_;
- return $s unless(defined($s));
- # perform macro substitution
- foreach my $special (keys %specials) {
- $s =~ s/$special/$specials{$special}/g;
- }
- return $s;
- }
- ###################################
- sub
- ECMDDevice_Changed($$$)
- {
- my ($hash, $cmd, $value)= @_;
- #Debug "Device changed: $cmd $value";
-
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, $cmd, $value) if(defined($value) && $value ne "");
-
- my $state;
- my $classname= $hash->{fhem}{classname};
- my $IOhash= $hash->{IODev};
- if(defined($IOhash->{fhem}{classDefs}{$classname}{state})) {
- if($cmd eq $IOhash->{fhem}{classDefs}{$classname}{state}) {
- $state= defined($value) ? $value : "?";
- #Debug "cmd= $cmd, setting state to $state (OVERRIDE)";
- }
- } else {
- $state= $cmd;
- $state.= " $value" if(defined($value) && $value ne "");
- #Debug "cmd= $cmd, setting state to $state (DEFAULT)";
- }
- if(!AttrVal($hash->{NAME}, "noState", 0)) {
- readingsBulkUpdate($hash, "state", $state) if(defined($state));
- }
- readingsEndUpdate($hash, 1);
- my $name= $hash->{NAME};
- #Log3 $hash, 4 , "ECMDDevice $name $state" if(defined($state));
- return $state;
- }
- ###################################
- sub
- ECMDDevice_PostProc($$$%)
- {
- my ($hash, $postproc, $value, %specials)= @_;
- if($postproc) {
- my $command= ECMDDevice_ReplaceSpecials($postproc, %specials);
- $_= $value;
- Log3 $hash, 5, "Postprocessing \"" . dq($value) . "\" with perl command $command.";
- $value= AnalyzePerlCommand(undef, $command);
- Log3 $hash, 5, "Postprocessed value is \"" . dq($value) . "\".";
- }
- return $value;
- }
- sub
- ECMDDevice_EvalCommand($$$)
- {
- my ($hash, $command, $value)= @_;
- if($command) {
- $_= $value;
- Log3 $hash, 5, "Postprocessing \"" . dq($value) . "\" with perl command $command.";
- $value= AnalyzePerlCommand(undef, $command);
- Log3 $hash, 5, "Postprocessed value is \"" . dq($value) . "\".";
- }
- return $value;
- }
- sub
- ECMDDevice_GetCachedReadingsCommand($$$)
- {
- my ($hash, $classDef, $reading)= @_;
- my $command= $hash->{fhem}{cache}{readings}{command}{$reading};
- if(!defined($command)) {
- my %specials= ECMDDevice_GetCachedSpecials($hash);
- my $postproc= $classDef->{readings}{$reading}{postproc};
- if($postproc) {
- $command= ECMDDevice_ReplaceSpecials($postproc, %specials);
- } else {
- $command= undef;
- }
- $hash->{fhem}{cache}{readings}{command}{$reading}= $command;
- }
- return $command;
- }
- ###################################
- sub
- ECMDDevice_Get($@)
- {
- my ($hash, @a)= @_;
- my $name= $hash->{NAME};
- my $type= $hash->{TYPE};
- return "get $name needs at least one argument" if(int(@a) < 2);
- my $cmdname= $a[1];
- my $IOhash= $hash->{IODev};
- my $classname= $hash->{fhem}{classname};
- if(!defined($IOhash->{fhem}{classDefs}{$classname}{gets}{$cmdname})) {
- my $gets= $IOhash->{fhem}{classDefs}{$classname}{gets};
- return "$name error: unknown argument $cmdname, choose one of " .
- (join " ", sort keys %$gets);
- }
- my $ecmd= $IOhash->{fhem}{classDefs}{$classname}{gets}{$cmdname}{cmd};
- my $expect= $IOhash->{fhem}{classDefs}{$classname}{gets}{$cmdname}{expect};
- my $params= $IOhash->{fhem}{classDefs}{$classname}{gets}{$cmdname}{params};
- my $postproc= $IOhash->{fhem}{classDefs}{$classname}{gets}{$cmdname}{postproc};
- my %specials= ECMDDevice_GetCachedSpecials($hash);
- # add specials for command
- if($params) {
- shift @a; shift @a;
- my @params= split('[\s]+', $params);
- return "Wrong number of parameters." if($#a != $#params);
- my $i= 0;
- foreach my $param (@params) {
- Log3 $hash, 5, "Parameter %". $param . " is " . $a[$i];
- $specials{"%".$param}= $a[$i++];
- }
- }
- $ecmd= ECMDDevice_ReplaceSpecials($ecmd, %specials);
- $expect= ECMDDevice_ReplaceSpecials($expect, %specials);
- my $r = ECMDDevice_AnalyzeCommand($hash, $ecmd);
- my $v= IOWrite($hash, $r, $expect);
- $v= ECMDDevice_PostProc($hash, $postproc, $v, %specials);
- return ECMDDevice_Changed($hash, $cmdname, $v);
- }
- #############################
- sub
- ECMDDevice_Set($@)
- {
- my ($hash, @a)= @_;
- my $name= $hash->{NAME};
- my $type= $hash->{TYPE};
- return "set $name needs at least one argument" if(int(@a) < 2);
- my $cmdname= $a[1];
- my $IOhash= $hash->{IODev};
- my $classname= $hash->{fhem}{classname};
- if(!defined($IOhash->{fhem}{classDefs}{$classname}{sets}{$cmdname})) {
- my $sets= $IOhash->{fhem}{classDefs}{$classname}{sets};
- return "Unknown argument $cmdname, choose one of " . join(' ', sort keys %$sets);
- }
- my $ecmd= $IOhash->{fhem}{classDefs}{$classname}{sets}{$cmdname}{cmd};
- my $expect= $IOhash->{fhem}{classDefs}{$classname}{sets}{$cmdname}{expect};
- my $params= $IOhash->{fhem}{classDefs}{$classname}{sets}{$cmdname}{params};
- my $postproc= $IOhash->{fhem}{classDefs}{$classname}{sets}{$cmdname}{postproc};
- my %specials= ECMDDevice_GetCachedSpecials($hash);
- # add specials for command
- if($params) {
- shift @a; shift @a;
- my @params= split('[\s]+', $params);
- return "Wrong number of parameters." if($#a != $#params);
- my $i= 0;
- foreach my $param (@params) {
- $specials{"%".$param}= $a[$i++];
- }
- }
- $ecmd= ECMDDevice_ReplaceSpecials($ecmd, %specials);
- $expect= ECMDDevice_ReplaceSpecials($expect, %specials);
- my $r = ECMDDevice_AnalyzeCommand($hash, $ecmd);
- my $v= IOWrite($hash, $r, $expect);
- $v= ECMDDevice_PostProc($hash, $postproc, $v, %specials);
- ECMDDevice_Changed($hash, $cmdname, $v); # was: return ECMDDevice_Changed($hash, $cmdname, $v);
- return undef;
- }
- #############################
- sub
- ECMDDevice_GetCachedReadingsMatch($$$)
- {
- my ($hash, $classDef, $r)= @_;
-
- my $regex= $hash->{fhem}{cache}{readings}{match}{$r};
- if(!defined($regex)) {
- my %specials= ECMDDevice_GetCachedSpecials($hash);
- $regex= ECMDDevice_ReplaceSpecials($classDef->{readings}{$r}{match}, %specials);
- $hash->{fhem}{cache}{readings}{match}{$r}= $regex;
- }
- return $regex;
- }
- #############################
- sub
- ECMDDevice_Parse($$)
- {
- # we never come here if $msg does not match $hash->{MATCH} in the first place
-
- # NOTE: we will update all matching readings for all devices, not just the first!
-
- my ($IOhash, $message) = @_; # IOhash points to the ECMD, not to the ECMDDevice
- my @matches;
- my $name= $IOhash->{NAME};
-
- my $ts= gettimeofday();
-
- if(defined(AttrVal($name, "partial", undef))) {
-
- if(!defined($IOhash->{fhem}{partial})) {
- $IOhash->{fhem}{partial}{ts}= $ts;
- $IOhash->{fhem}{partial}{msg}= "";
- }
- my $partial= $IOhash->{fhem}{partial}{msg};
- #Debug "$name: partial message \"" . escapeLogLine($IOhash->{fhem}{partial}{msg}) . "\" recorded at $ts";
- if($partial ne "") {
- # clear partial message if expired
- my $timeout= AttrVal($name, "partial", 1);
- my $t0= $IOhash->{fhem}{partial}{ts};
- if($ts-$t0> $timeout) {
- Log3 $IOhash, 5, "$name: partial message " . dq($partial) . " expired.";
- $partial= "";
- $IOhash->{fhem}{partial}{msg}= $partial;
- }
- }
- # prepend to recently received message
- $IOhash->{fhem}{partial}{ts}= $ts;
- if($partial ne "") {
- Log3 $IOhash, 5, "$name: merging partial message " . dq($partial) . " and " . dq($message);
- $message= $partial . $message;
- $IOhash->{fhem}{partial}{msg}= "";
- }
-
- } else {
-
- # clean the partial stuff for clarity
- if(defined($IOhash->{fhem}{partial})) {
- delete($IOhash->{fhem}{partial})
- }
- }
-
- #Debug "$name: analyzing \"" . escapeLogLine($message) . "\".";
-
- my @msgs;
- my $splitter= $IOhash->{fhem}{".split"};
- if(defined($splitter)) {
- #Debug "Splitting " . dq($message) . " at " . dq($splitter);
- @msgs= split(/(?<=$splitter)/, $message); # http://stackoverflow.com/questions/14907772/split-but-keep-delimiter
-
- #Debug scalar(@msgs) . " part(s)";
- Log3 $IOhash, 5, "$name: " . dq($message) . " split into " . scalar(@msgs) . " parts"
- if(scalar(@msgs)>1);
- #Debug "Split done.";
- } else {
- push @msgs, $message;
- }
-
- #my @unmatchedMsgs; # future use
- my $lastMsg= "";
- my $msgMatched;
-
- foreach my $msg (@msgs) {
- #Debug "$name: trying to find a match for \"" . escapeLogLine($msg) ."\"";
- Log3 $IOhash, 5, "$name: trying to match message " . dq($msg);
- $msgMatched= 0;
- # walk over all clients
- foreach my $d (keys %defs) {
- my $hash= $defs{$d};
- if($hash->{TYPE} eq "ECMDDevice" && $hash->{IODev} eq $IOhash) {
- my $classname= $hash->{fhem}{classname};
- next unless($classname);
- my $classDef= $IOhash->{fhem}{classDefs}{$classname};
- #Debug " Checking device $d with class $classname...";
- next unless(defined($classDef->{readings}));
- #Debug " Trying to find a match in class $classname...";
- # we run over all readings in that classdef
- foreach my $r (keys %{$classDef->{readings}}) {
- my $regex= ECMDDevice_GetCachedReadingsMatch($hash, $classDef, $r);
- #Debug " Trying to match reading $r with regular expression \"$regex\" (device $d, classdef $classname, reading $r).";
- if($msg =~ m/^$regex$/) {
- # we found a match
- Log3 $IOhash, 5, "$name: " . dq($msg) . " matches regex $regex for reading $r of device $d with class $classname";
- $msgMatched++;
- push @matches, $d;
- my $command= ECMDDevice_GetCachedReadingsCommand($hash, $classDef, $r);
- my $value= ECMDDevice_EvalCommand($hash, $command, $msg);
- #Log3 $hash, 5, "postprocessed value is $value";
- ECMDDevice_Changed($hash, $r, $value);
- }
- }
- }
- }
- #push @unmatchedMsgs, $msg unless($msgMatched); # future use
- }
-
- $lastMsg= $msgs[$#msgs] unless($msgMatched); # contains the last message if the last message is unmatched
- if(defined(AttrVal($name, "partial", undef)) && $lastMsg ne "") {
- # we come here if the last message was unmatched and we want partial messages
- if($#msgs>= 0) {
- # we had more messages; therefore the partial message belonged to the first message and needs
- # to be cleared
- $IOhash->{fhem}{partial}{msg}= "";
- }
- $IOhash->{fhem}{partial}{msg}.= $lastMsg; # append unmatched message
- Log3 $IOhash, 5, "$name: partial message " . dq($lastMsg) . " kept";
- #Debug "$name: partial message \"" . escapeLogLine($IOhash->{fhem}{partial}{msg}) . "\" kept.";
- }
-
- return @matches if(@matches);
-
- # we come here if no match is found
-
- # NOTE: In a split message, undefined messages are not reported if there was at least one match.
- # return "UNDEFINED ECMDDevice message $message";
- return "";
-
- }
- #####################################
- sub
- ECMDDevice_AssignClass($$@)
- {
- my ($hash,$classname,@a)= @_;
-
- my $name= $hash->{NAME};
-
- my $IOhash= $hash->{IODev};
- if(!defined($IOhash)) {
- my $err= "ECMDDevice $name error: no I/O device.";
- Log3 $hash, 1, $err;
- return $err;
- }
-
-
- if(!defined($IOhash->{fhem}{classDefs}{$classname}{filename})) {
- my $err= "ECMDDevice $name error: unknown class $classname (I/O device is "
- . $IOhash->{NAME} . ").";
- Log3 $hash, 1, $err;
- return $err;
- }
- $hash->{fhem}{classname}= $classname;
-
- my @prms= ECMDDevice_GetDeviceParams($hash);
- my $numparams= 0;
- $numparams= $#prms+1 if(defined($prms[0]));
- #Log 5, "ECMDDevice $classname requires $numparams parameter(s): ". join(" ", @prms);
- # verify identical number of parameters
- if($numparams != $#a+1) {
- my $err= "$name error: wrong number of parameters";
- Log3 $hash, 1, $err;
- return $err;
- }
- # set parameters
- for(my $i= 0; $i< $numparams; $i++) {
- $hash->{fhem}{params}{$prms[$i]}= $a[$i];
- }
-
- return undef; # OK
- }
- #####################################
- sub
- ECMDDevice_Attr($@)
- {
- my @a = @_;
- my $hash= $defs{$a[1]};
- if($a[0] eq "set" && $a[2] eq "class") {
- my ($classname,@prms)= split " ", $a[3];
- return ECMDDevice_AssignClass($hash, $classname, @prms);
- } else {
- return undef;
- }
- }
- #############################
- sub
- ECMDDevice_Define($$)
- {
- my ($hash, $def) = @_;
- my @a = split("[ \t]+", $def);
- return "Usage: define <name> ECMDDevice [<classname> [...]]" if(int(@a) < 2);
- my $name= $a[0];
-
- AssignIoPort($hash);
- if(int(@a)> 2) {
- my $classname= $a[2];
- shift @a; shift @a; shift @a;
- return ECMDDevice_AssignClass($hash, $classname, @a);
- } else {
- return undef;
- }
-
- # create cache stubs
- $hash->{fhem}{cache}{specials}= ();
- $hash->{fhem}{cache}{readings}{match}= ();
- $hash->{fhem}{cache}{readings}{command}= ();
- }
- #############################
- 1;
- #############################
- =pod
- =item device
- =item summary user-defined device communicating through ECMD (logical device)
- =item summary_DE benutzerdefiniertes via ECMD kommunizierendes Gerät (logisches Gerät)
- =begin html
- <a name="ECMDDevice"></a>
- <h3>ECMDDevice</h3>
- <ul>
- <br>
- <a name="ECMDDevicedefine"></a>
- <b>Define</b>
- <ul>
- <code>define <name> ECMDDevice [<classname> [<parameter1> [<parameter2> [<parameter3> ... ]]]]</code>
- <br><br>
- Defines a logical ECMD device. The number of given parameters must match those given in
- the <a href="#ECMDClassdef">class definition</a> of the device class <code><classname></code>.<p>
-
- Normally, the logical ECMDDevice is attached to the latest previously defined physical ECMD device
- for I/O. Use the <code>IODev</code> attribute of the logical ECMDDevice to attach to any
- physical ECMD device, e.g. <code>attr myRelais2 IODev myAVRNETIO</code>. In such a case the correct
- reference to the class cannot be made at the time of definition of the device. Thus, you need to
- omit the <classname> and <parameter> references in the definition of the device and use the
- <code>class</code> <a href="#ECMDDeviceattr">attribute</a> instead.
- <br><br>
- Examples:
- <ul>
- <code>define myADC ECMDDevice ADC</code><br>
- <code>define myRelais1 ECMDDevice relais 8</code><br>
- <code>define myRelais2 ECMDDevice</code><br>
- <code>attr myRelais2 IODev myAVRNETIO</code><br>
- <code>attr myRelais2 class relais 8</code>
- </ul>
- <br>
- </ul>
- <a name="ECMDDeviceset"></a>
- <b>Set</b>
- <ul>
- <code>set <name> <commandname> [<parameter1> [<parameter2> [<parameter3> ... ]]]</code>
- <br><br>
- The number of given parameters must match those given for the set command <code><commandname></code> definition in
- the <a href="#ECMDClassdef">class definition</a>.<br><br>
- If <code>set <commandname></code> is invoked the perl special in curly brackets from the command definition
- is evaluated and the result is sent to the physical ECMD device.
- <br><br>
- Example:
- <ul>
- <code>set myRelais1 on</code><br>
- <code>set myDisplay text This\x20text\x20has\x20blanks!</code><br>
- </ul>
- <br>
- </ul>
- <a name="ECMDDeviceget"></a>
- <b>Get</b>
- <ul>
- <code>get <name> <commandname> [<parameter1> [<parameter2> [<parameter3> ... ]]]</code>
- <br><br>
- The number of given parameters must match those given for the get command <code><commandname></code> definition in
- the <a href="#ECMDClassdef">class definition</a>.<br><br>
- If <code>get <commandname></code> is invoked the perl special in curly brackets from the command definition
- is evaluated and the result is sent to the physical ECMD device. The response from the physical ECMD device is returned
- and the state of the logical ECMD device is updated accordingly.
- <br><br>
- Example:
- <ul>
- <code>get myADC value 3</code><br>
- </ul>
- <br>
- </ul>
- <a name="ECMDDeviceattr"></a>
- <b>Attributes</b>
- <ul>
- <li>class<br>
- If you omit the <classname> and <parameter> references in the
- <a href="#ECMDDevicedefine">definition</a> of the device, you have to add them
- separately as an attribute. Example: <code>attr myRelais2 class relais 8</code>.</li>
- <li>noState<br>
- Changes of readings do not change the state reading if this attribute is set to a non-zero value.
- For example, this is desirable if you need to avoid the second event created by changing the state
- reading. Previously created state readings can be deleted by means of <a href="#deletereading">deletereading</a>.
- The user can define the value shown in the state of the device by means
- of the <a href="#stateFormat">stateFormat</a> attribute.</li>
- <li><a href="#verbose">verbose</a></li>
- <li><a href="#eventMap">eventMap</a></li>
- <li><a href="#IODev">IODev</a></li>
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- </ul>
- <br><br>
- <b>Example 1</b>
- <br><br>
- <ul>
- The following example shows how to access the ADC of the AVR-NET-IO board from
- <a href="http://www.pollin.de">Pollin</a> with
- <a href="http://www.ethersex.de/index.php/ECMD">ECMD</a>-enabled
- <a href="http://www.ethersex.de">Ethersex</a> firmware.<br><br>
- The class definition file <code>/etc/fhem/ADC.classdef</code> looks as follows:<br><br>
- <code>
- get value cmd {"adc get %channel\n"} <br>
- get value params channel<br>
- get value expect "\d+\n"<br>
- get value postproc { s/^(\d+)\n$/$1/;; $_ }<br>
- </code>
- <br>
- In the fhem configuration file or on the fhem command line we do the following:<br><br>
- <code>
- define AVRNETIO ECMD telnet 192.168.0.91:2701 # define the physical device<br>
- attr AVRNETIO classdefs ADC=/etc/fhem/ADC.classdef # define the device class ADC<br>
- define myADC ECDMDevice ADC # define the logical device myADC with device class ADC<br>
- get myADC value 1 # retrieve the value of analog/digital converter number 1<br>
- </code>
- <br>
- The get command is evaluated as follows: <code>get value</code> has one named parameter
- <code>channel</code>. In the example the literal <code>1</code> is given and thus <code>%channel</code>
- is replaced by <code>1</code> to yield <code>"adc get 1\n"</code> after macro substitution. Perl
- evaluates this to a literal string which is send as a plain ethersex command to the AVR-NET-IO. The
- board returns something like <code>024\n</code> for the current value of analog/digital converter number 1. The postprocessor keeps only the digits.
- <br><br>
- </ul>
- <b>Example 2</b>
- <br><br>
- <ul>
- The following example shows how to switch a relais driven by pin 3 (bit mask 0x08) of I/O port 2 on for
- one second and then off again.<br><br>
- The class definition file <code>/etc/fhem/relais.classdef</code> looks as follows:<br><br>
- <code>
- params pinmask<br>
- 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>
- set on expect ".*"<br>
- set on postproc {s/^OK\nOK\nOK\nOK\n$/success/; "$_" eq "success" ? "ok" : "error"; }<br>
- </code>
- <br>
- In the fhem configuration file or on the fhem command line we do the following:<br><br>
- <code>
- define AVRNETIO ECMD telnet 192.168.0.91:2701 # define the physical device<br>
- attr AVRNETIO classdefs relais=/etc/fhem/relais.classdef # define the device class relais<br>
- attr AVRNETIO requestSeparator \000<br>
- define myRelais ECMDDevice 8 # define the logical device myRelais with pin mask 8<br>
- set myRelais on # execute the "on" command<br>
- </code>
- <br>
- The set command is evaluated as follows: <code>%pinmask</code>
- is replaced by <code>8</code> to yield
- <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
- evaluates this to a literal string. This string is split into lines (with trailing newline characters)
- <code>
- <ul>
- <li>io set ddr 2 ff\n</li>
- <li>ioset port 2 08\n</li>
- <li>wait 1000\n</li>
- <li>io set port 2 00\n</li>
- </ul>
- </code>
- These lines are sent as a plain ethersex commands to the AVR-NET-IO one by one. After
- each line the answer from the physical device is read back. They are concatenated and returned
- for further processing by the <code>postproc</code> command.
- For any of the four plain ethersex commands, the AVR-NET-IO returns the string <code>OK\n</code>. They are
- concatenated. The postprocessor takes the result from <code>$_</code>,
- substitutes it by the string <code>success</code> if it is <code>OK\nOK\nOK\nOK\n</code>, and then either
- returns the string <code>ok</code> or the string <code>error</code>. If the responseSeparator was set to \000,
- 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>.
- <br><br>
- </ul>
- <b>Example 3</b>
- <br><br>
- <ul>
- The following example shows how to implement a sandbox.<br><br>
- The class definition file <code>/etc/fhem/DummyServer.classdef</code> looks as follows:<br><br>
- <code>
- reading foo match "\d+\n"<br>
- reading foo postproc { s/^(\d+).*$/$1/;; $_ }<br>
- </code>
- <br>
- In the fhem configuration file or on the fhem command line we do the following:<br><br>
- <code>
- define myDummyServer ECMD telnet localhost:9999 # define the physical device<br>
- set myDummyServer classdef DummyServer /etc/fhem/DummyServer.classdef # define the device class DummyServer<br>
- define myDummyClient ECDMDevice DummyServer # define a logical device with device class DummyServer<br>
- </code>
- <p>
- 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>
-
- Start FHEM.<p>
-
- Then enter the number 4711 at the stdin of the running netcat server.<p>
-
- 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.
- <br><br>
- </ul>
- </ul>
- =end html
- =cut
|