| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- ##########################################################################
- # This file is part of the smarthomatic module for FHEM.
- #
- # Copyright (c) 2014 Stefan Baumann
- #
- # You can find smarthomatic at www.smarthomatic.org.
- # You can find FHEM at www.fhem.de.
- #
- # This file 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 3 of the License, or (at your
- # option) any later version.
- #
- # This file 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 smarthomatic. If not, see <http://www.gnu.org/licenses/>.
- ###########################################################################
- # $Id: 37_SHC.pm 8190 2015-03-10 21:23:03Z rr2000 $
- package main;
- use strict;
- use warnings;
- use Time::HiRes qw(gettimeofday);
- sub SHC_Parse($$$$);
- sub SHC_Read($);
- sub SHC_ReadAnswer($$$$);
- sub SHC_Ready($);
- sub SHC_SimpleWrite(@);
- my $clientsSHC = ":SHCdev:BASE:xxx:";
- my %matchListSHC = (
- "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
- "2:xxx" => "^\\S+\\s+22",
- "3:xxx" => "^\\S+\\s+11",
- "4:xxx" => "^\\S+\\s+9 ",
- );
- sub SHC_Initialize($)
- {
- my ($hash) = @_;
- require "$attr{global}{modpath}/FHEM/DevIo.pm";
- # Provider
- $hash->{ReadFn} = "SHC_Read";
- $hash->{WriteFn} = "SHC_Write";
- $hash->{ReadyFn} = "SHC_Ready";
- # Normal devices
- $hash->{DefFn} = "SHC_Define";
- $hash->{UndefFn} = "SHC_Undef";
- $hash->{GetFn} = "SHC_Get";
- $hash->{SetFn} = "SHC_Set";
- $hash->{ShutdownFn} = "SHC_Shutdown";
- }
- #####################################
- sub SHC_Define($$)
- {
- my ($hash, $def) = @_;
- my @a = split("[ \t][ \t]*", $def);
- if (@a != 3) {
- my $msg = "wrong syntax: define <name> SHC {devicename[\@baudrate] " . "| devicename\@directio}";
- Log3 undef, 2, $msg;
- return $msg;
- }
- DevIo_CloseDev($hash);
- my $name = $a[0];
- my $dev = $a[2];
- $dev .= "\@19200" if ($dev !~ m/\@/);
- $hash->{Clients} = $clientsSHC;
- $hash->{MatchList} = \%matchListSHC;
- $hash->{DeviceName} = $dev;
- my $ret = DevIo_OpenDev($hash, 0, "SHC_DoInit");
- return $ret;
- }
- #####################################
- sub SHC_Undef($$)
- {
- my ($hash, $arg) = @_;
- my $name = $hash->{NAME};
- foreach my $d (sort keys %defs) {
- if ( defined($defs{$d})
- && defined($defs{$d}{IODev})
- && $defs{$d}{IODev} == $hash)
- {
- my $lev = ($reread_active ? 4 : 2);
- Log3 $name, $lev, "$name: deleting port for $d";
- delete $defs{$d}{IODev};
- }
- }
- SHC_Shutdown($hash);
- DevIo_CloseDev($hash);
- return undef;
- }
- #####################################
- sub SHC_Shutdown($)
- {
- my ($hash) = @_;
- return undef;
- }
- #####################################
- sub SHC_Set($@)
- {
- my ($hash, @a) = @_;
- my $name = shift @a;
- my $cmd = shift @a;
- my $arg = join("", @a);
- my $list = "raw:noArg";
- return $list if ($cmd eq '?');
- if ($cmd eq "raw") {
- #return "\"set SHC $cmd\" needs exactly one parameter" if(@_ != 4);
- #return "Expecting a even length hex number" if((length($arg)&1) == 1 || $arg !~ m/^[\dA-F]{12,}$/ );
- Log3 $name, 4, "$name: set $name $cmd $arg";
- SHC_SimpleWrite($hash, $arg);
- } else {
- return "Unknown argument $cmd, choose one of " . $list;
- }
- return undef;
- }
- #####################################
- sub SHC_Get($@)
- {
- my ($hash, $name, $cmd) = @_;
- return undef;
- }
- #####################################
- sub SHC_DoInit($)
- {
- my $hash = shift;
- my $name = $hash->{NAME};
- my $err;
- my $msg = undef;
- $hash->{STATE} = "Initialized";
- return undef;
- }
- #####################################
- # This is a direct read for commands like get
- # Anydata is used by read file to get the filesize
- sub SHC_ReadAnswer($$$$)
- {
- # TODO: Not adapted to SHC, copy from 36_JeeLink.pm
- my ($hash, $arg, $anydata, $regexp) = @_;
- my $type = $hash->{TYPE};
- my $name = $hash->{NAME};
- return ("No FD", undef)
- if (!$hash || ($^O !~ /Win/ && !defined($hash->{FD})));
- my ($mpandata, $rin) = ("", '');
- my $buf;
- my $to = 3; # 3 seconds timeout
- $to = $hash->{RA_Timeout} if ($hash->{RA_Timeout}); # ...or less
- for (; ;) {
- if ($^O =~ m/Win/ && $hash->{USBDev}) {
- $hash->{USBDev}->read_const_time($to * 1000); # set timeout (ms)
- # Read anstatt input sonst funzt read_const_time nicht.
- $buf = $hash->{USBDev}->read(999);
- return ("Timeout reading answer for get $arg", undef)
- if (length($buf) == 0);
- } else {
- return ("Device lost when reading answer for get $arg", undef)
- if (!$hash->{FD});
- vec($rin, $hash->{FD}, 1) = 1;
- my $nfound = select($rin, undef, undef, $to);
- if ($nfound < 0) {
- next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
- my $err = $!;
- DevIo_Disconnected($hash);
- return ("SHC_ReadAnswer $arg: $err", undef);
- }
- return ("Timeout reading answer for get $arg", undef)
- if ($nfound == 0);
- $buf = DevIo_SimpleRead($hash);
- return ("No data", undef) if (!defined($buf));
- }
- if ($buf) {
- Log3 $hash->{NAME}, 5, "$name: SHC/RAW (ReadAnswer): $buf";
- $mpandata .= $buf;
- }
- chop($mpandata);
- chop($mpandata);
- return (
- undef, $mpandata
- );
- }
- }
- #####################################
- sub SHC_Write($$)
- {
- # TODO: Not adapted to SHC, copy from 36_JeeLink.pm
- my ($hash, $msg) = @_;
- my $name = $hash->{NAME};
- Log3 $name, 5, "$name: sending $msg";
- SHC_SimpleWrite($hash, $msg);
- }
- #####################################
- # called from the global loop, when the select for hash->{FD} reports data
- sub SHC_Read($)
- {
- # TODO: Verify if partial data handling is required for SHC
- my ($hash) = @_;
- my $buf = DevIo_SimpleRead($hash);
- return "" if (!defined($buf));
- my $name = $hash->{NAME};
- my $pandata = $hash->{PARTIAL};
- Log3 $name, 5, "$name: SHC/RAW: $pandata/$buf";
- $pandata .= $buf;
- while ($pandata =~ m/\n/) {
- my $rmsg;
- ($rmsg, $pandata) = split("\n", $pandata, 2);
- $rmsg =~ s/\r//;
- SHC_Parse($hash, $hash, $name, $rmsg) if ($rmsg);
- }
- $hash->{PARTIAL} = $pandata;
- }
- #####################################
- sub SHC_Parse($$$$)
- {
- my ($hash, $iohash, $name, $rmsg) = @_;
- my $dmsg = $rmsg;
- next if (!$dmsg || length($dmsg) < 1); # Bogus messages
- if ($dmsg !~ m/^Packet Data: SenderID=/) {
- # Messages just to dipose
- if ( $dmsg =~ m/^\*\*\* Enter AES key nr/
- || $dmsg =~ m/^\*\*\* Received character/)
- {
- return;
- }
- # -Verbosity level 5
- if ( $dmsg =~ m/^Received \(AES key/
- || $dmsg =~ m/^Received garbage/
- || $dmsg =~ m/^Before encryption/
- || $dmsg =~ m/^After encryption/
- || $dmsg =~ m/^Repeating request./
- || $dmsg =~ m/^Request Queue empty/
- || $dmsg =~ m/^Removing request from request buffer/)
- {
- Log3 $name, 5, "$name: $dmsg";
- return;
- }
- # -Verbosity level 4
- if ( $dmsg =~ m/^Request added to queue/
- || $dmsg =~ m/^Request Buffer/
- || $dmsg =~ m/^Request (q|Q)ueue/)
- {
- Log3 $name, 4, "$name: $dmsg";
- return;
- }
- # Anything else in verbosity level 3
- Log3 $name, 3, "$name: $dmsg";
- return;
- }
- $hash->{"${name}_MSGCNT"}++;
- $hash->{"${name}_TIME"} = TimeNow();
- $hash->{RAWMSG} = $rmsg;
- my %addvals = (RAWMSG => $rmsg);
- Dispatch($hash, $dmsg, \%addvals);
- }
- #####################################
- sub SHC_Ready($)
- {
- my ($hash) = @_;
- return DevIo_OpenDev($hash, 1, "SHC_DoInit")
- if ($hash->{STATE} eq "disconnected");
- # This is relevant for windows/USB only
- my $po = $hash->{USBDev};
- my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
- if ($po) {
- ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
- }
- return ($InBytes && $InBytes > 0);
- }
- ########################
- sub SHC_SimpleWrite(@)
- {
- my ($hash, $msg) = @_;
- return if (!$hash);
- my $name = $hash->{NAME};
- Log3 $name, 5, "$name: SW: $msg";
- $msg .= "\r";
- $hash->{USBDev}->write($msg) if ($hash->{USBDev});
- syswrite($hash->{DIODev}, $msg) if ($hash->{DIODev});
- # Some linux installations are broken with 0.001, T01 returns no answer
- select(undef, undef, undef, 0.01);
- }
- 1;
- =pod
- =begin html
- <a name="SHC"></a>
- <h3>SHC</h3>
- <ul>
- SHC is the basestation module that supports a family of RF devices available
- at <a href="http://http://www.smarthomatic.org">www.smarthomatic.org</a>.
- This module provides the IODevice for the <a href="#SHCdev">SHCdev</a>
- modules that implement the SHCdev protocol.<br><br>
- Note: this module may require the Device::SerialPort or Win32::SerialPort
- module if you attach the device via USB and the OS sets strange default
- parameters for serial devices.<br><br>
- <a name="SHC_Define"></a>
- <b>Define</b>
- <ul>
- <code>define <name> SHC <device></code><br>
- <br>
- <device> specifies the serial port to communicate with the SHC.
- The name of the serial-device depends on your distribution, under
- linux usually a /dev/ttyUSB0 device will be created.<br><br>
- You can also specify a baudrate if the device name contains the @
- character, e.g.: /dev/ttyUSB0@57600. Please note that the default
- baudrate for the SHC base station is 19200 baud.<br><br>
- Example:<br>
- <ul>
- <code>define shc_base SHC /dev/ttyUSB0</code><br><br>
- </ul>
- </ul>
- <a name="SHC_Set"></a>
- <b>Set</b>
- <ul>
- <li>raw <data><br>
- not supported yet
- </li><br>
- </ul>
- <a name="SHC_Get"></a>
- <b>Get</b>
- <ul>
- <li>
- N/A
- </li><br>
- </ul>
- <a name="SHC_Attr"></a>
- <b>Attributes</b>
- <ul>
- <li>
- N/A
- </li><br>
- </ul>
- </ul>
- =end html
- =cut
|