From b519a780bfce6df50dc3ea5a59bf7a312d6a300d Mon Sep 17 00:00:00 2001 From: mrsidey <> Date: Thu, 22 Oct 2015 20:54:49 +0000 Subject: [PATCH] 00_SIGNALduino: Added module git-svn-id: https://svn.fhem.de/fhem/trunk@9598 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 3 + fhem/FHEM/00_SIGNALduino.pm | 2230 +++++++++++++++++++++++++++++++++++ fhem/HISTORY | 3 + fhem/MAINTAINER.txt | 4 + 4 files changed, 2240 insertions(+) create mode 100644 fhem/FHEM/00_SIGNALduino.pm diff --git a/fhem/CHANGED b/fhem/CHANGED index 2cee540b5..7e30223c1 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,8 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: new module 00_SIGNALduino.pm and 90_SIGNALduino_un: added SIGNALduino module and firmware + - feature: new module 14_Hideki.pm: added module for sensors with hideki protocol + - feature: new module 14_SD_WS07.pm: added module for sensor devices with protocol id7 - feature: 32_yowsup.pm: allow comma separated receiver list on master send - bugfix: YAMAHA_AVR: fixing not working remoteControl commands for several models diff --git a/fhem/FHEM/00_SIGNALduino.pm b/fhem/FHEM/00_SIGNALduino.pm new file mode 100644 index 000000000..57e560cbe --- /dev/null +++ b/fhem/FHEM/00_SIGNALduino.pm @@ -0,0 +1,2230 @@ +############################################## +# $Id: 00_SIGNALduino.pm 72788 2015-10-20 v3.1 +# The file is taken from the FHEMduino project and modified in serval ways for processing the incomming messages +# see http://www.fhemwiki.de/wiki/SIGNALDuino +# It was modified also to provide support for raw message handling which it's send from the SIGNALduino +# The purpos is to use it as addition to the SIGNALduino which runs on an arduno nano or arduino uno. +# It routes Messages serval Modules which are already integrated in FHEM. But there are also modules which comes with it. +# N. Butzek, S. Butzek, 2014-2015 +# + + +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); +use Data::Dumper qw(Dumper); + +use POSIX qw( floor); # can be removed +#use Math::Round qw(); + +sub SIGNALduino_Attr(@); +sub SIGNALduino_Clear($); +#sub SIGNALduino_HandleCurRequest($$); +#sub SIGNALduino_HandleWriteQueue($); +sub SIGNALduino_Parse($$$$@); +sub SIGNALduino_Read($); +sub SIGNALduino_ReadAnswer($$$$); +sub SIGNALduino_Ready($); +sub SIGNALduino_Write($$$); + +sub SIGNALduino_SimpleWrite(@); + +my $debug=0; + +my %gets = ( # Name, Data to send to the SIGNALduino, Regexp for the answer + "version" => ["V", '^V\s.*SIGNALduino.*'], + "freeram" => ["R", '^[0-9]+'], + "raw" => ["", '.*'], + "uptime" => ["t", '^[0-9]+' ], + "cmds" => ["?", '.*Use one of[ 0-9A-Za-z]+[\r\n]*$' ], + "ITParms" => ["ip",'.*'], +# "ITClock" => ["ic", '\d+'], +# "FAParms" => ["fp", '.*' ], +# "TCParms" => ["dp", '.*' ], +# "HXParms" => ["hp", '.*' ] +); + + +my %sets = ( + "raw" => "", + "flash" => "noarg", + "reset" => "noarg", + #"disablereceiver" => "", + "ITClock" => "200,300,400", + +); + +## Supported Clients per default +my $clientsSIGNALduino = ":IT:" + ."CUL_TCM97001:" +# ."SIGNALduino_RSL:" + ."OREGON:" + ."CUL_TX:" +# ."SD_AS:" + ."SIGNALduino_un:" + ."Hideki:" + ."SD_WS07:" + ; + +## default regex match List for dispatching message to logical modules, can be updated during runtime because it is referenced +my %matchListSIGNALduino = ( + "1:IT" => "^i......", # Intertechno Format + "2:CUL_TCM97001" => "^s[A-Fa-f0-9]+", # Any hex string beginning with s +# "3:SIGNALduino_RSL" => "^rA-Fa-f0-9]+", # Any hex string beginning with r + "5:CUL_TX" => "^TX..........", # Need TX to avoid FHTTK +# "6:SD_AS" => "^P2#[A-Fa-f0-9]{7,8}", # Arduino based Sensors, should not be default + "4:OREGON" => "^(3[8-9A-F]|[4-6][0-9A-F]|7[0-8]).*", + "7:Hideki" => "^P12#75[A-F0-9]", + "10:SD_WS07" => "^P7#[A-Fa-f0-9]{6}F[A-Fa-f0-9]{2}", + "X:SIGNALduino_un" => '^[uP]\d+#.*', +); + + +my %ProtocolListSIGNALduino = ( + "0" => + { + name => 'weather1', # Logilink, NC, WS, TCM97001 etc. + id => '0', + one => [1,-8], + zero => [1,-4], + sync => [1,-18], + clockabs => '500', # not used now + format => 'twostate', # not used now + preamble => 's', # prepend to converted message + postamble => '00', # Append to converted message + clientmodule => 'CUL_TCM97001', # not used now + modulematch => '^s[A-Fa-f0-9]+', # not used now + length_min => '24', + length_max => '40', + paddingbits => '8', # pad up to 8 bits, default is 4 + }, + "1" => + { + name => 'ConradRSL', # + id => '1', + one => [2,-1], + zero => [1,-2], + sync => [1,-11], + clockabs => '560', # not used now + format => 'twostate', # not used now + preamble => 'r', # prepend to converted message + postamble => '', # Append to converted message + clientmodule => 'SIGNALduino_RSL', # not used now + modulematch => '^r[A-Fa-f0-9]+', # not used now + }, + + "2" => + { + name => 'AS', # Self build arduino sensor + id => '2', + one => [1,-2], + zero => [1,-1], + sync => [1,-20], + clockabs => '500', # not used now + format => 'twostate', + preamble => 'P2#', # prepend to converted message + clientmodule => 'SD_AS', # not used now + modulematch => '^P2#[A-Fa-f0-9]{7,8}', # not used now + length_min => '32', + length_max => '34', # Don't know maximal lenth of a valid message + paddingbits => '8', # pad up to 8 bits, default is 4 + + }, + "3" => + { + name => 'itv1', + id => '3', + one => [3,-1], + zero => [1,-3], + #float => [-1,3], # not full supported now later use + sync => [1,-31], + clockabs => -1, # -1=auto + format => 'twostate', # not used now + preamble => 'i', + clientmodule => 'IT', # not used now + modulematch => '^i......', # not used now + length_min => '24', + #length_max => '800', # Don't know maximal lenth of a valid message + + }, + "4" => + { + name => 'itv3', + id => '4', + one => [3,-1], + zero => [1,-3], + #float => [-1,3], # not full supported now, for later use + sync => [1,-30], + clockabs => -1, # -1 = auto + format => 'tristate', # tristate can't be migrated from bin into hex! + preamble => 'i', # Append to converted message + clientmodule => 'IT', # not used now + modulematch => '^i......', # not used now + length_min => '68', + #length_max => '76', # Don't know maximal lenth of a valid message + + + }, + "5" => ## Similar protocol as intertechno, but without sync + { + name => 'unitec', + id => '5', + one => [3,-1], + zero => [1,-3], + #float => [-1,3], # not full supported now, for later use + #sync => [0,0], # This special device has no sync + clockabs => -1, # -1 = auto + format => 'twostate', # tristate can't be migrated from bin into hex! + preamble => 'i', # Append to converted message + clientmodule => 'IT', # not used now + modulematch => '^i......', # not used now + length_min => '24', + + }, + + "6" => ## Eurochron Protocol + { + name => 'weatherID6', + id => '6', + one => [1,-10], + zero => [1,-5], + sync => [1,-36], # This special device has no sync + clockabs => 220, # -1 = auto + format => 'twostate', # tristate can't be migrated from bin into hex! + preamble => 'u6#', # Append to converted message + clientmodule => 'undef', # not used now + modulematch => '^u......', # not used now + length_min => '24', + + }, + "7" => ## weather sensors like EAS800z + { + name => 'weatherID7', + id => '7', + one => [1,-4], + zero => [1,-2], + sync => [1,-8], + clockabs => 484, + format => 'twostate', + preamble => 'P7#', # prepend to converted message + clientmodule => 'undef', # not used now + modulematch => '^P7#......', # not used now + length_min => '35', + length_max => '40', + + }, + "8" => ## TX3 (ITTX) Protocol + { + name => 'TX3 Protocol', + id => '8', + one => [1,-2], + zero => [2,-2], + #float => [-1,3], # not full supported now, for later use + #sync => [1,-8], # + clockabs => 470, # -1 = auto undef=noclock + format => 'pwm', # tristate can't be migrated from bin into hex! + preamble => 'TX', # prepend to converted message + clientmodule => 'ittx', # not used now + modulematch => '^TX......', # not used now + length_min => '44', + length_max => '45', + remove_zero => 1, # Removes leading zeros from output + + }, + "9" => ## Funk Wetterstation CTW600 + { + name => 'CTW 600', + id => '9', + zero => [3,-2], + one => [1,-2], + #float => [-1,3], # not full supported now, for later use + #sync => [1,-8], # + clockabs => 480, # -1 = auto undef=noclock + format => 'pwm', # tristate can't be migrated from bin into hex! + preamble => 'u9#', # prepend to converted message + clientmodule => 'undef', # not used now + modulematch => '^u9#.....', # not used now + length_min => '70', + length_max => '120', + + }, + "10" => ## Oregon Scientific 2 + { + name => 'OSV2', + id => '10', + #one => [3,-2], + #zero => [1,-2], + #float => [-1,3], # not full supported now, for later use + #sync => [1,-8], # + #clockabs => 480, # -1 = auto undef=noclock + clockrange => [390,520], # min , max + format => 'manchester', # tristate can't be migrated from bin into hex! + #preamble => '', # prepend to converted message + #clientmodule => '41_OREGON', # not used now + #modulematch => '', # not used now + length_min => '64', + length_max => '220', + method => \&SIGNALduino_OSV2 # Call to process this message + + + }, + "11" => ## Arduino Sensor + { + name => 'AS', + id => '11', + clockrange => [380,425], # min , max + format => 'manchester', # tristate can't be migrated from bin into hex! + preamble => 'P2#', # prepend to converted message + #clientmodule => '14_SD_AS', # not used now + #modulematch => '^P2#[A-Fa-f0-9]{7,8}, # not used now + length_min => '52', + length_max => '56', + method => \&SIGNALduino_AS # Call to process this message + + }, + "12" => ## hideki + { + name => 'Hideki protocol', + id => '12', + clockrange => [420,510], # min, max better for Bresser Sensors, OK for hideki/Hideki/TFA too + format => 'manchester', + preamble => 'P12#', # prepend to converted message + #clientmodule => '14_hideki', # not used now + #modulematch => '^P12#75[A-F0-9]{17,30}', # not used now + length_min => '72', + length_max => '104', + method => \&SIGNALduino_Hideki # Call to process this message + }, + "13" => ## FA21RF + { + name => '21RF', + id => '13', + one => [1,-2], + zero => [1,-4], + sync => [10,-1], + clockabs => 800, + format => 'twostate', + preamble => 'u13#', # prepend to converted message + #clientmodule => '', # not used now + #modulematch => '', # not used now + length_min => '20', + length_max => '40', + }, + "14" => ## Heidemann HX + { + name => 'Heidemann HX', + id => '14', + one => [1,-2], + zero => [1,-1], + #float => [-1,3], # not full supported now, for later use + sync => [1,-14], # + clockabs => 350, + format => 'twostate', + preamble => 'u14#', # prepend to converted message + #clientmodule => '', # not used now + #modulematch => '', # not used now + length_min => '10', + length_max => '20', + #method => \&SIGNALduino_Cresta # Call to process this message + }, + "15" => ## TCM234759 + { + name => 'TCM Bell', + id => '15', + one => [1,-1], + zero => [1,-2], + #float => [-1,3], # not full supported now, for later use + sync => [1,-45], # + clockabs => 700, + format => 'twostate', + preamble => 'u15#', # prepend to converted message + #clientmodule => '', # not used now + #modulematch => '', # not used now + length_min => '10', + length_max => '20', + #method => \&SIGNALduino_Cresta # Call to process this message + }, + "16" => # Rohrmotor24 und andere Funk Rolladen / Markisen Motoren + { + name => 'Dooya shutter', + id => '16', + one => [-3,1], + zero => [1,-3], + #sync => [18,-6], # protocol has a sync, but is detected as MU + clockabs => 250, + format => 'twostate', + preamble => 'u16#', # prepend to converted message + #clientmodule => '', # not used now + #modulematch => '', # not used now + length_min => '30', + length_max => '40', + }, + #"17" => # nothing known about this MS;P0=-506;P1=444;P2=12860;P3=-8923;P4=-1041;P5=12838;P6=1371;D=13101010101014141410101010101010101010101010101010101010101010101012;CP=1;SP=3; + # { + # name => 'unknown17', + # id => '17', + # one => [1,-2], + # zero => [1,-1], + # sync => [1,-22], # footer [1,-50] + # clockabs => 400, + # format => 'twostate', + # preamble => 'u17#', # prepend to converted message + # #clientmodule => '', # not used now + # #modulematch => '', # not used now + # length_min => '30', + # #length_max => '38', + # }, + + "18" => ## Oregon Scientific v1 + { + name => 'OSV1', + id => '18', + clockrange => [1550,1650], # min , max + format => 'manchester', # tristate can't be migrated from bin into hex! + #preamble => '', # prepend to converted message + #clientmodule => 'to be written', # not used now + #modulematch => '', # not used now + length_min => '8', + length_max => '8', + method => \&SIGNALduino_OSV1 # Call to process this message + }, + #"19" => # nothing knowing about this 2015-09-28 01:25:40-MS;P0=-8916;P1=-19904;P2=390;P3=-535;P4=-1020;P5=12846;P6=1371;D=2120232323232324242423232323232323232320239;CP=2;SP=1; + # + # { + # name => 'unknown19', + # id => '19', + # one => [1,-2], + # zero => [1,-1], + # sync => [1,-50,1,-22], + # clockabs => 395, + # format => 'twostate', + # preamble => 'u19#', # prepend to converted message + # #clientmodule => '', # not used now + # #modulematch => '', # not used now + # length_min => '16', + # length_max => '32', + # }, + "20" => #Livolo + { + name => 'livolo', + id => '20', + one => [2,-1], + zero => [1,-1], + sync => [1,-3], + clockabs => -1, #can be 140-190 + format => 'twostate', + preamble => 'u20#', # prepend to converted message + #clientmodule => '', # not used now + #modulematch => '', # not used now + length_min => '16', + }, + "21" => #Einhell Garagentor + { + name => 'einhell garagedoor', + id => '21', + one => [-3,1], + zero => [-1,3], + #sync => [-50,1], + clockabs => 400, #ca 400us + format => 'twostate', + preamble => 'u21#', # prepend to converted message + #clientmodule => '', # not used now + #modulematch => '', # not used now + length_min => '32', + length_max => '32', + paddingbits => '1', # This will disable padding + }, + "22" => #TX-EZ6 / Meteo + { + name => 'TX-EZ6', + id => '22', + one => [1,-8], + zero => [1,-3], + sync => [1,16], + clockabs => 500, #ca 400us + format => 'twostate', + preamble => 'u22#', # prepend to converted message + #clientmodule => '', # not used now + #modulematch => '', # not used now + length_min => '40', + #length_max => '', # must be tested + + }, + "23" => # Pearl Sensor + { + name => 'perl unknown', + id => '23', + one => [1,-6], + zero => [1,-1], + sync => [1,-50], + clockabs => 200, #ca 200us + format => 'twostate', + preamble => 'u23#', # prepend to converted message + #clientmodule => '', # not used now + #modulematch => '', # not used now + length_min => '36', + length_max => '44', + + }, +); + + + + +sub +SIGNALduino_Initialize($) +{ + my ($hash) = @_; + + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + +# Provider + $hash->{ReadFn} = "SIGNALduino_Read"; + $hash->{WriteFn} = "SIGNALduino_Write"; + $hash->{ReadyFn} = "SIGNALduino_Ready"; + +# Normal devices + $hash->{DefFn} = "SIGNALduino_Define"; + $hash->{FingerprintFn} = "SIGNALduino_FingerprintFn"; + $hash->{UndefFn} = "SIGNALduino_Undef"; + $hash->{GetFn} = "SIGNALduino_Get"; + $hash->{SetFn} = "SIGNALduino_Set"; + $hash->{AttrFn} = "SIGNALduino_Attr"; + $hash->{AttrList} = + "Clients MatchList do_not_notify:1,0 dummy:1,0" + ." hexFile" + ." initCommands" + ." flashCommand" + ." hardware:nano328,uno,promini328" + ." debug:0,1" + ." longids" + ." minsecs" + ." whitelist_IDs" + ." $readingFnAttributes"; + + $hash->{ShutdownFn} = "SIGNALduino_Shutdown"; + +} + +sub +SIGNALduino_FingerprintFn($$) +{ + my ($name, $msg) = @_; + + # Store only the "relevant" part, as the Signalduino won't compute the checksum + $msg = substr($msg, 8) if($msg =~ m/^81/ && length($msg) > 8); + + return ($name, $msg); +} + +##################################### +sub +SIGNALduino_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + if(@a != 3) { + my $msg = "wrong syntax: define SIGNALduino {none | devicename[\@baudrate] | devicename\@directio | hostname:port}"; + Log3 undef, 2, $msg; + return $msg; + } + + DevIo_CloseDev($hash); + my $name = $a[0]; + + + if (!exists &round) + { + Log3 $name, 1, "$name Signalduino can't be activated (sub round not found). Please update Fhem via update command"; + return undef; + } + + my $dev = $a[2]; + #Debug "dev: $dev" if ($debug); + #my $hardware=AttrVal($name,"hardware","nano328"); + #Debug "hardware: $hardware" if ($debug); + + if($dev eq "none") { + Log3 $name, 1, "$name device is none, commands will be echoed only"; + $attr{$name}{dummy} = 1; + #return undef; + } + + + $dev .= "\@57600" if( $dev ne "none" && $dev !~ m/\@/ ); + + + $hash->{CMDS} = ""; + $hash->{Clients} = $clientsSIGNALduino; + $hash->{MatchList} = \%matchListSIGNALduino; + + + #if( !defined( $attr{$name}{hardware} ) ) { + # $attr{$name}{hardware} = "nano328"; + #} + + + if( !defined( $attr{$name}{flashCommand} ) ) { +# $attr{$name}{flashCommand} = "avrdude -p atmega328P -c arduino -P [PORT] -D -U flash:w:[HEXFILE] 2>[LOGFILE]" + $attr{$name}{flashCommand} = "avrdude -c arduino -b 57600 -P [PORT] -p atmega328p -vv -U flash:w:[HEXFILE] 2>[LOGFILE]" + } + + $hash->{DeviceName} = $dev; + if($dev eq "none") { + return undef; + } + my $ret = DevIo_OpenDev($hash, 0, "SIGNALduino_DoInit"); + + ## + $hash->{Interval} = "300"; + InternalTimer(gettimeofday()+2, "SIGNALduino_GetUpdate", $hash, 0); + + $hash->{"DMSG"}="nothing"; + $hash->{"TIME"}=time(); + + + my %WhitelistIDs = map { $_ => 1 } split(",", AttrVal($name,"whitelist_IDs","")); + $hash->{"whitelisthash"} = \%WhitelistIDs; + undef($hash->{"whitelisthash"}) if (scalar(keys %WhitelistIDs) <= 0); + return $ret; +} + +##################################### +sub +SIGNALduino_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, "deleting port for $d"; + delete $defs{$d}{IODev}; + } + } + + SIGNALduino_Shutdown($hash); + + DevIo_CloseDev($hash); + RemoveInternalTimer($hash); + return undef; +} + +##################################### +sub +SIGNALduino_Shutdown($) +{ + my ($hash) = @_; + SIGNALduino_SimpleWrite($hash, "X00"); # Switch reception off, it may hang up the SIGNALduino + return undef; +} + +##################################### +sub +SIGNALduino_Set($@) +{ + my ($hash, @a) = @_; + + return "\"set SIGNALduino\" needs at least one parameter" if(@a < 2); + return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets) + if(!defined($sets{$a[1]})); + + my $name = shift @a; + my $cmd = shift @a; + my $arg = join(" ", @a); + + + if($cmd eq "raw") { + Log3 $name, 4, "set $name $cmd $arg"; + SIGNALduino_SimpleWrite($hash, $arg); + } elsif( $cmd eq "flash" ) { + my @args = split(' ', $arg); + my $log = ""; + my $hexFile = ""; + my @deviceName = split('@', $hash->{DeviceName}); + my $port = $deviceName[0]; + my $hardware=AttrVal($name,"hardware","nano328"); + + my $defaultHexFile = "./FHEM/firmware/$hash->{TYPE}_$hardware.hex"; + my $logFile = AttrVal("global", "logdir", "./log/") . "$hash->{TYPE}-Flash.log"; + + if(!$arg || $args[0] !~ m/^(\w|\/|.)+$/) { + $hexFile = AttrVal($name, "hexFile", ""); + if ($hexFile eq "") { + $hexFile = $defaultHexFile; + } + } + else { + $hexFile = $args[0]; + } + + return "Usage: set $name flash [filename]\n\nor use the hexFile attribute" if($hexFile !~ m/^(\w|\/|.)+$/); + + $log .= "flashing Arduino $name\n"; + $log .= "hex file: $hexFile\n"; + $log .= "port: $port\n"; + $log .= "log file: $logFile\n"; + + my $flashCommand = AttrVal($name, "flashCommand", ""); + + if($flashCommand ne "") { + if (-e $logFile) { + unlink $logFile; + } + + DevIo_CloseDev($hash); + $hash->{STATE} = "disconnected"; + $log .= "$name closed\n"; + + my $avrdude = $flashCommand; + $avrdude =~ s/\Q[PORT]\E/$port/g; + $avrdude =~ s/\Q[HEXFILE]\E/$hexFile/g; + $avrdude =~ s/\Q[LOGFILE]\E/$logFile/g; + + $log .= "command: $avrdude\n\n"; + `$avrdude`; + + local $/=undef; + if (-e $logFile) { + open FILE, $logFile; + my $logText = ; + close FILE; + $log .= "--- AVRDUDE ---------------------------------------------------------------------------------\n"; + $log .= $logText; + $log .= "--- AVRDUDE ---------------------------------------------------------------------------------\n\n"; + } + else { + $log .= "WARNING: avrdude created no log file\n\n"; + } + + } + else { + $log .= "\n\nNo flashCommand found. Please define this attribute.\n\n"; + } + + DevIo_OpenDev($hash, 0, "SIGNALduino_DoInit"); + $log .= "$name opened\n"; + + return $log; + + } elsif ($cmd =~ m/reset/i) { + return SIGNALduino_ResetDevice($hash); + } elsif( $cmd eq "ITClock" ) { + Log3 $name, 4, "set $name $cmd $arg"; + my $clock = shift @a; + + $clock=300 if ($clock eq "" ); + + if ($clock =~ /^\d+$/ ) { + $arg="ic$clock"; + SIGNALduino_SimpleWrite($hash, $arg); + Log3 $name, 3, "Set - no value passed - setting ITClock to default ($clock)"; + + #SIGNALduino_Get($hash,$hash->{NAME},"ITParms"); + + } else { + return "argument $arg, is not numeric for set it base duration".$hash->{CMDS}; + } + + } else { + Log3 $name, 5, "set $name $cmd $arg"; + #SIGNALduino_SimpleWrite($hash, $arg); + return "Unknown argument $cmd, choose one of ".$hash->{CMDS}; + } + + return undef; +} + +##################################### +sub +SIGNALduino_Get($@) +{ + my ($hash, @a) = @_; + my $type = $hash->{TYPE}; + my $name = $a[0]; + + Log3 $name, 5, "\"get $type\" needs at least one parameter" if(@a < 2); + return "\"get $type\" needs at least one parameter" if(@a < 2); + if(!defined($gets{$a[1]})) { + my @cList = map { $_ =~ m/^(file|raw)$/ ? $_ : "$_:noArg" } sort keys %gets; + return "Unknown argument $a[1], choose one of " . join(" ", @cList); + } + + my $arg = ($a[2] ? $a[2] : ""); + my ($msg, $err); + + if (IsDummy($name)) + { + return SIGNALduino_Parse($hash, $hash, $hash->{NAME}, $arg); + } + return "No $a[1] for dummies" if(IsDummy($name)); + + Log3 $name, 5, "$name: command for gets: " . $gets{$a[1]}[0] . " " . $arg; + + SIGNALduino_SimpleWrite($hash, $gets{$a[1]}[0] . $arg); + + ($err, $msg) = SIGNALduino_ReadAnswer($hash, $a[1], 0, $gets{$a[1]}[1]); + Log3 $name, 5, "$name: received message for gets: " . $msg if ($msg); + + if(!defined($msg)) { + DevIo_Disconnected($hash); + $msg = "No answer"; + + } elsif($a[1] eq "cmds") { # nice it up + $msg =~ s/.*Use one of//g; + + } elsif($a[1] eq "uptime") { # decode it + $msg =~ s/[\r\n]//g; + #$msg = hex($msg); # /125; only for col or coc + $msg = sprintf("%d %02d:%02d:%02d", $msg/86400, ($msg%86400)/3600, ($msg%3600)/60, $msg%60); + } + + $msg =~ s/[\r\n]//g; + + #$hash->{READINGS}{$a[1]}{VAL} = $msg; + #$hash->{READINGS}{$a[1]}{TIME} = time(); + readingsSingleUpdate($hash, $a[1], $msg, 0); + + + return "$a[0] $a[1] => $msg"; +} + +sub +SIGNALduino_Clear($) +{ + my $hash = shift; + + # Clear the pipe + $hash->{RA_Timeout} = 0.1; + for(;;) { + my ($err, undef) = SIGNALduino_ReadAnswer($hash, "Clear", 0, undef); + last if($err && $err =~ m/^Timeout/); + } + delete($hash->{RA_Timeout}); +} + +##################################### +sub +SIGNALduino_ResetDevice($) +{ + my ($hash) = @_; + + DevIo_CloseDev($hash); + my $ret = DevIo_OpenDev($hash, 0, "SIGNALduino_DoInit"); + + return $ret; +} + +##################################### +sub +SIGNALduino_DoInit($) +{ + my $hash = shift; + my $name = $hash->{NAME}; + my $err; + my $msg = undef; + + my ($ver, $try) = ("", 0); + #Dirty hack to allow initialisation of DirectIO Device for some debugging and tesing + Log3 $name, 1, $hash->{DEF}; + + if ((!$hash->{DEF} =~ m/\@DirectIO/) and (!$hash->{DEF} =~ m/none/) ) + { + SIGNALduino_Clear($hash); + + + # Try to get version from Arduino + while ($try++ < 3 && $ver !~ m/^V/) { + SIGNALduino_SimpleWrite($hash, "V"); + ($err, $ver) = SIGNALduino_ReadAnswer($hash, "Version", 0, undef); + return "$name: $err" if($err && ($err !~ m/Timeout/ || $try == 3)); + $ver = "" if(!$ver); + } + # Check received string + if($ver !~ m/^V/) { + $attr{$name}{dummy} = 1; + $msg = "Not an SIGNALduino device, got for V: $ver"; + Log3 $name, 1, $msg; + return $msg; + } + $ver =~ s/[\r\n]//g; + $hash->{VERSION} = $ver; + + #$debug = AttrVal($name, "verbose", 3) == 5; + #Log3 $name, 3, "$name: setting debug to: " . $debug; + + + # Cmd-String feststellen + + my $cmds = SIGNALduino_Get($hash, $name, "cmds", 0); + $cmds =~ s/$name cmds =>//g; + $cmds =~ s/ //g; + $hash->{CMDS} = $cmds; + Log3 $name, 3, "$name: Possible commands: " . $hash->{CMDS}; + readingsSingleUpdate($hash, "state", "Programming", 1); + + } + # if( my $initCommandsString = AttrVal($name, "initCommands", undef) ) { + # my @initCommands = split(' ', $initCommandsString); + # foreach my $command (@initCommands) { + # SIGNALduino_SimpleWrite($hash, $command); + # } + # } + # $hash->{STATE} = "Initialized"; + readingsSingleUpdate($hash, "state", "Initialized", 1); + + # Reset the counter + delete($hash->{XMIT_TIME}); + delete($hash->{NR_CMD_LAST_H}); + return undef; +} + +##################################### +# This is a direct read for commands like get +# Anydata is used by read file to get the filesize +sub +SIGNALduino_ReadAnswer($$$$) +{ + my ($hash, $arg, $anydata, $regexp) = @_; + my $type = $hash->{TYPE}; + + while($hash->{TYPE} eq "SIGNALduino_RFR") { # Look for the first "real" SIGNALduino + $hash = $hash->{IODev}; + } + + return ("No FD", undef) + if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD}))); + + my ($mSIGNALduinodata, $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("SIGNALduino_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, "SIGNALduino/RAW (ReadAnswer): $buf"; + $mSIGNALduinodata .= $buf; + } + $mSIGNALduinodata = SIGNALduino_RFR_DelPrefix($mSIGNALduinodata) if($type eq "SIGNALduino_RFR"); + + # \n\n is socat special + if($mSIGNALduinodata =~ m/\r\n$/ || $anydata || $mSIGNALduinodata =~ m/\n\n$/ ) { + if($regexp && $mSIGNALduinodata !~ m/$regexp/) { + SIGNALduino_Parse($hash, $hash, $hash->{NAME}, $mSIGNALduinodata); + } else { + return (undef, $mSIGNALduinodata) + } + } + } + +} + +##################################### +# Check if the 1% limit is reached and trigger notifies +sub +SIGNALduino_XmitLimitCheck($$) +{ + my ($hash,$fn) = @_; + my $now = time(); + + if(!$hash->{XMIT_TIME}) { + $hash->{XMIT_TIME}[0] = $now; + $hash->{NR_CMD_LAST_H} = 1; + return; + } + + my $nowM1h = $now-3600; + my @b = grep { $_ > $nowM1h } @{$hash->{XMIT_TIME}}; + + if(@b > 163) { # Maximum nr of transmissions per hour (unconfirmed). + + my $name = $hash->{NAME}; + Log3 $name, 2, "SIGNALduino TRANSMIT LIMIT EXCEEDED"; + DoTrigger($name, "TRANSMIT LIMIT EXCEEDED"); + + } else { + + push(@b, $now); + + } + $hash->{XMIT_TIME} = \@b; + $hash->{NR_CMD_LAST_H} = int(@b); +} + + +##################################### +sub +SIGNALduino_Write($$$) +{ + my ($hash,$fn,$msg) = @_; + + my $name = $hash->{NAME}; + + Log3 $name, 5, "$hash->{NAME} sending $fn$msg"; + my $bstring = "$fn$msg"; + + SIGNALduino_SimpleWrite($hash, $bstring); + +} + +#sub +#SIGNALduino_SendFromQueue($$) +#{ +# my ($hash, $bstring) = @_; +# my $name = $hash->{NAME}; +# +# if($bstring ne "") { +# SIGNALduino_XmitLimitCheck($hash,$bstring); +# SIGNALduino_SimpleWrite($hash, $bstring); +# } + + ############## + # Write the next buffer not earlier than 0.23 seconds + # = 3* (12*0.8+1.2+1.0*5*9+0.8+10) = 226.8ms + # else it will be sent too early by the SIGNALduino, resulting in a collision +# InternalTimer(gettimeofday()+0.3, "SIGNALduino_HandleWriteQueue", $hash, 1); +#} + +##################################### +#sub +#SIGNALduino_HandleWriteQueue($) +#{ +# my $hash = shift; +# my $arr = $hash->{QUEUE}; +# if(defined($arr) && @{$arr} > 0) { +# shift(@{$arr}); +# if(@{$arr} == 0) { +# delete($hash->{QUEUE}); +# return; +# } +# my $bstring = $arr->[0]; +# if($bstring eq "") { +# SIGNALduino_HandleWriteQueue($hash); +# } else { +# SIGNALduino_SendFromQueue($hash, $bstring); +# } +# } +#} + +##################################### +# called from the global loop, when the select for hash->{FD} reports data +sub +SIGNALduino_Read($) +{ + my ($hash) = @_; + + my $buf = DevIo_SimpleRead($hash); + return "" if(!defined($buf)); + my $name = $hash->{NAME}; + + my $SIGNALduinodata = $hash->{PARTIAL}; + Log3 $name, 5, "SIGNALduino/RAW READ: $SIGNALduinodata/$buf"; + $SIGNALduinodata .= $buf; + + while($SIGNALduinodata =~ m/\n/) { + my $rmsg; + ($rmsg,$SIGNALduinodata) = split("\n", $SIGNALduinodata, 2); + $rmsg =~ s/\r//; + Log3 $name, 4, "SIGNALduino/msg READ: $rmsg"; + + SIGNALduino_Parse($hash, $hash, $name, $rmsg) if($rmsg); + } + $hash->{PARTIAL} = $SIGNALduinodata; +} + + + +sub SIGNALduino_GetUpdate($){ + my ($hash) = @_; + my $name = $hash->{NAME}; + + Log3 $name, 4, "$name: Get freeram called ..."; + SIGNALduino_Get($hash,$name, "freeram"); + + InternalTimer(gettimeofday()+$hash->{Interval}, "SIGNALduino_GetUpdate", $hash, 1); +} + + +### Helper Subs >>> + +sub SIGNALduino_splitMsg +{ + my $txt = shift; + my $delim = shift; + my @msg_parts = split(/$delim/,$txt); + + return @msg_parts; +} +# $value - $set <= $tolerance +sub SIGNALduino_inTol($$$) +{ + #Debug "sduino abs \($_[0] - $_[1]\) <= $_[2] "; + return (abs($_[0]-$_[1])<=$_[2]); +} + + + # - - - - - - - - - - - - + #=item SIGNALduino_PatternExists() + #This functons, needs reference to $hash, @array of values to search and %patternList where to find the matches. +# +# Will return -1 if pattern is not found or a string, containing the indexes which are in tolerance and have the smallest gap to what we searched +# =cut + +sub SIGNALduino_PatternExists +{ + my ($hash,$search,$patternList) = @_; + #my %patternList=$arg3; + #Debug "plist: ".Dumper($patternList) if($debug); + #Debug "searchlist: ".Dumper($search) if($debug); + + my $searchpattern; + my $valid=1; + my $pstr=""; + + foreach $searchpattern (@{$search}) # z.B. [1, -4] + { + #my $patt_id; + # Calculate tolernace for search + #my $tol=abs(abs($searchpattern)>=2 ?$searchpattern*0.3:$searchpattern*1.5); + my $tol=abs(abs($searchpattern)>3 ? abs($searchpattern)>16 ? $searchpattern*0.17 : $searchpattern*0.3 : 1); #tol is minimum 1 or higer, depending on our searched pulselengh + + Debug "tol (+- $tol) for ($searchpattern)" if($debug); + + my %pattern_gap ; #= {}; + # Find and store the gap of every pattern, which is in tolerance + %pattern_gap = map { $_ => abs($patternList->{$_}-$searchpattern) } grep { abs($patternList->{$_}-$searchpattern) <= $tol} (keys %$patternList); + if (scalar keys %pattern_gap > 0) + { + Debug "index => gap in tol (+- $tol) of pulse ($searchpattern) : ".Dumper(\%pattern_gap) if($debug); + # Extract fist pattern, which is nearst to our searched value + my $closestidx = (sort {$pattern_gap{$a} <=> $pattern_gap{$b}} keys %pattern_gap)[0]; + + $pstr="$pstr$closestidx"; + $valid=1; + Debug "closest pattern has index: $closestidx" if($debug); + } else { + # search is not found, return + $valid=0; + last; + } + #return ($valid ? $pstr : -1); # return $pstr if $valid or -1 + + + + #foreach $patt_id (keys %$patternList) { + #Debug "$patt_id. chk ->intol $patternList->{$patt_id} $searchpattern $tol"; + #$valid = SIGNALduino_inTol($patternList->{$patt_id}, $searchpattern, $tol); + #if ( $valid) #one pulse found in tolerance, search next one + #{ + # $pstr="$pstr$patt_id"; + # # provide this index for further lookup table -> {$patt_id = $searchpattern} + # Debug "pulse found"; + # last ; ## Exit foreach loop if searched pattern matches pattern in list + #} + #} + #last if (!$valid); ## Exit loop if a complete iteration has not found anything + } + + return ($valid ? $pstr : -1); # return $pstr if $valid or -1 +} + +#SIGNALduino_MatchSignalPattern{$hash,@array, %hash, @array, $scalar}; not used >v3.1.3 +sub SIGNALduino_MatchSignalPattern($\@\%\@$){ + + my ( $hash, $signalpattern, $patternList, $data_array, $idx) = @_; + my $name = $hash->{NAME}; + #print Dumper($patternList); + #print Dumper($idx); + #Debug Dumper($signalpattern) if ($debug); + my $tol="0.2"; # Tolerance factor + my $found=0; + foreach ( @{$signalpattern} ) + { + #Debug " $idx check: ".$patternList->{$data_array->[$idx]}." == ".$_; + Debug "$name: idx: $idx check: abs(". $patternList->{$data_array->[$idx]}." - ".$_.") > ". ceil(abs($patternList->{$data_array->[$idx]}*$tol)) if ($debug); + + #print "\n";; + #if ($patternList->{$data_array->[$idx]} ne $_ ) + ### Nachkommastelle von ceil!!! + if (!defined( $patternList->{$data_array->[$idx]})){ + Debug "$name: Error index ($idx) does not exist!!" if ($debug); + + return -1; + } + if (abs($patternList->{$data_array->[$idx]} - $_) > ceil(abs($patternList->{$data_array->[$idx]}*$tol))) + { + return -1; ## Pattern does not match, return -1 = not matched + } + $found=1; + $idx++; + } + if ($found) + { + return $idx; ## Return new Index Position + } + +} + + + + +sub SIGNALduino_b2h { + my $num = shift; + my $WIDTH = 4; + my $index = length($num) - $WIDTH; + my $hex = ''; + do { + my $width = $WIDTH; + if ($index < 0) { + $width += $index; + $index = 0; + } + my $cut_string = substr($num, $index, $width); + $hex = sprintf('%X', oct("0b$cut_string")) . $hex; + $index -= $WIDTH; + } while ($index > (-1 * $WIDTH)); + return $hex; +} + +sub SIGNALduino_Split_Message($$) +{ + my $rmsg = shift; + my $name = shift; + my %patternList; + my $clockidx; + my $syncidx; + my $rawData; + my $clockabs; + + my @msg_parts = SIGNALduino_splitMsg($rmsg,';'); ## Split message parts by ";" + my %ret; + + foreach (@msg_parts) + { + #Debug "$name: checking msg part:( $_ )" if ($debug); + + if ($_ =~ m/^MS/ or $_ =~ m/^MC/ or $_ =~ m/^MU/) #### Synced Message start + { + $ret{messagetype} = $_; + } + elsif ($_ =~ m/^P\d=-?\d{2,}/ or $_ =~ m/^[SL][LH]=-?\d{2,}/) #### Extract Pattern List from array + { + $_ =~ s/^P+//; + $_ =~ s/^P\d//; + my @pattern = split(/=/,$_); + + $patternList{$pattern[0]} = $pattern[1]; + Debug "$name: extracted pattern @pattern \n" if ($debug); + } + elsif($_ =~ m/D=\d+/ or $_ =~ m/^D=[A-F0-9]+/) #### Message from array + + { + $_ =~ s/D=//; + $rawData = $_ ; + Debug "$name: extracted data $rawData\n" if ($debug); + $ret{rawData} = $rawData; + + } + elsif($_ =~ m/^SP=\d{1}/) #### Sync Pulse Index + { + (undef, $syncidx) = split(/=/,$_); + Debug "$name: extracted syncidx $syncidx\n" if ($debug); + #return undef if (!defined($patternList{$syncidx})); + $ret{syncidx} = $syncidx; + + } + elsif($_ =~ m/^CP=\d{1}/) #### Clock Pulse Index + { + (undef, $clockidx) = split(/=/,$_); + Debug "$name: extracted clockidx $clockidx\n" if ($debug);; + #return undef if (!defined($patternList{$clockidx})); + $ret{clockidx} = $clockidx; + } + elsif($_ =~ m/^C=\d+/) #### Message from array + { + $_ =~ s/C=//; + $clockabs = $_ ; + Debug "$name: extracted absolute clock $clockabs \n" if ($debug); + $ret{clockabs} = $clockabs; + } else { + Debug "$name: unknown Message part $_" if ($debug);; + } + #print "$_\n"; + } + $ret{pattern} = {%patternList}; + return %ret; +} + + + +# Function which dispatches a message if needed. +sub SIGNALduno_Dispatch($$$) +{ + my ($hash, $rmsg,$dmsg) = @_; + my $name = $hash->{NAME}; + + Log3 $name, 5, "converted Data to ($dmsg)"; + + #Dispatch only if $dmsg is different from last $dmsg, or if 2 seconds are between transmits + if ( ($hash->{DMSG} ne $dmsg) || ($hash->{TIME}+1 < time()) ) { + $hash->{MSGCNT}++; + $hash->{TIME} = time(); + $hash->{DMSG} = $dmsg; + readingsSingleUpdate($hash, "state", $hash->{READINGS}{state}{VAL}, 0); + $hash->{RAWMSG} = $rmsg; + my %addvals = (RAWMSG => $rmsg, DMSG => $dmsg); + Dispatch($hash, $dmsg, \%addvals); ## Dispatch to other Modules + } else { + Log3 $name, 4, "Dropped ($dmsg) due to short time or equal msg"; + } +} + +sub +SIGNALduino_Parse_MS($$$$%) +{ + my ($hash, $iohash, $name, $rmsg,%msg_parts) = @_; + + my $protocolid; + my $syncidx=$msg_parts{syncidx}; + my $clockidx=$msg_parts{clockidx}; + my $protocol=undef; + my $rawData=$msg_parts{rawData}; + my %patternList; + #$patternList{$_} = $msg_parts{rawData}{$_] for keys %msg_parts{rawData}; + + #$patternList = \%msg_parts{pattern}; + + #Debug "Message splitted:"; + #Debug Dumper(\@msg_parts); + + + + if (defined($clockidx) and defined($syncidx)) + { + + ## Make a lookup table for our pattern index ids + #Debug "List of pattern:"; + my $clockabs= $msg_parts{pattern}{$msg_parts{clockidx}}; + return undef if ($clockabs == 0); + $patternList{$_} = round($msg_parts{pattern}{$_}/$clockabs,1) for keys $msg_parts{pattern}; + + + #Debug Dumper(\%patternList); + + #my $syncfact = $patternList{$syncidx}/$patternList{$clockidx}; + #$syncfact=$patternList{$syncidx}; + #Debug "SF=$syncfact"; + #### Convert rawData in Message + my $signal_length = length($rawData); # Length of data array + + ## Iterate over the data_array and find zero, one, float and sync bits with the signalpattern + ## Find matching protocols + my $id; + my $message_dispatched=0; + foreach $id ( keys %ProtocolListSIGNALduino) { + if (defined($hash->{"whitelisthash"}) && !defined($hash->{"whitelisthash"}{$id})) { + Log3 $name, 4, "skip ID $id"; + next; + } + + next if !(exists $ProtocolListSIGNALduino{$id}{sync}); + + my $valid=1; + #$debug=1; + Debug "Testing against Protocol id $id -> $ProtocolListSIGNALduino{$id}{name}" if ($debug); + + # Check Clock if is it in range + $valid=SIGNALduino_inTol($ProtocolListSIGNALduino{$id}{clockabs},$clockabs,$clockabs*0.30) if ($ProtocolListSIGNALduino{$id}{clockabs} > 0); + Debug "validclock = $valid" if ($debug); + + next if (!$valid) ; + + my $bit_length = ($signal_length-(scalar @{$ProtocolListSIGNALduino{$id}{sync}}))/((scalar @{$ProtocolListSIGNALduino{$id}{one}} + scalar @{$ProtocolListSIGNALduino{$id}{zero}})/2); + + #Check calculated min length + $valid = $valid && $ProtocolListSIGNALduino{$id}{length_min} <= $bit_length if (exists $ProtocolListSIGNALduino{$id}{length_min}); + #Check calculated max length + $valid = $valid && $ProtocolListSIGNALduino{$id}{length_max} >= $bit_length if (exists $ProtocolListSIGNALduino{$id}{length_max}); + + next if (!$valid) ; + Debug "expecting $bit_length bits in signal" if ($debug && $valid); + + #Debug Dumper(@{$ProtocolListSIGNALduino{$id}{sync}}); + Debug "Searching in patternList: ".Dumper(\%patternList) if($debug); + + Debug "searching sync: @{$ProtocolListSIGNALduino{$id}{sync}}[0] @{$ProtocolListSIGNALduino{$id}{sync}}[1]" if($debug); # z.B. [1, -18] + #$valid = $valid && SIGNALduino_inTol($patternList{$clockidx}, @{$ProtocolListSIGNALduino{$id}{sync}}[0], 3); #sync in tolerance + #$valid = $valid && SIGNALduino_inTol($patternList{$syncidx}, @{$ProtocolListSIGNALduino{$id}{sync}}[1], 3); #sync in tolerance + + my $pstr; + my %patternLookupHash=(); + + $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{sync}},\%patternList)) >=0; + Debug "Found matched sync with indexes: ($pstr)" if ($debug && $valid); + $patternLookupHash{$pstr}="" if ($valid); ## Append Sync to our lookuptable + my $syncstr=$pstr; # Store for later start search + + Debug "sync not found " if (!$valid && $debug); # z.B. [1, -18] + + next if (!$valid) ; + + $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList)) >=0; + Debug "Found matched one with indexes: ($pstr)" if ($debug && $valid); + $patternLookupHash{$pstr}="1" if ($valid); ## Append Sync to our lookuptable + #Debug "added $pstr " if ($debug && $valid); + Debug "one pattern not found" if ($debug && !$valid); + + + $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList)) >=0; + Debug "Found matched zero with indexes: ($pstr)" if ($debug && $valid); + $patternLookupHash{$pstr}="0" if ($valid); ## Append Sync to our lookuptable + Debug "zero pattern not found" if ($debug && !$valid); + + #Debug "added $pstr " if ($debug && $valid); + + next if (!$valid) ; + #Debug "Pattern Lookup Table".Dumper(%patternLookupHash); + ## Check somethin else + + + #Anything seems to be valid, we can start decoding this. + + Log3 $name, 4, "Found matched Protocol id $id -> $ProtocolListSIGNALduino{$id}{name}" if ($valid); + my $signal_width= @{$ProtocolListSIGNALduino{$id}{one}}; + #Debug $signal_width; + + + my @bit_msg; # array to store decoded signal bits + + #for (my $i=index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{sync}}))+$signal_width;$i 0) ## will pad up full nibbles per default or full byte if specified in protocol + { + push(@bit_msg,'0'); + $i++; + } + Debug "$name padded $i bits to bit_msg array" if ($debug); + + #my $logmsg = SIGNALduino_padbits(@bit_msg,$padwith); + + #Check converted message against lengths + $valid = $valid && $ProtocolListSIGNALduino{$id}{length_min} <= scalar @bit_msg if (defined($ProtocolListSIGNALduino{$id}{length_min})); + $valid = $valid && $ProtocolListSIGNALduino{$id}{length_max} >= scalar @bit_msg if (defined($ProtocolListSIGNALduino{$id}{length_max})); + next if (!$valid); + + #my $dmsg = sprintf "%02x", oct "0b" . join "", @bit_msg; ## Array -> String -> bin -> hex + my $dmsg = SIGNALduino_b2h(join "", @bit_msg); + $dmsg = "$dmsg"."$ProtocolListSIGNALduino{$id}{postamble}" if (defined($ProtocolListSIGNALduino{$id}{postamble})); + $dmsg = "$ProtocolListSIGNALduino{$id}{preamble}"."$dmsg" if (defined($ProtocolListSIGNALduino{$id}{preamble})); + + Debug "$name: dispatching now msg: $dmsg" if ($debug); + + SIGNALduno_Dispatch($hash,$rmsg,$dmsg); + $message_dispatched=1; + + } + + return 0 if (!$message_dispatched); + + return 1; + + + } +} +## //Todo: check list as reference +sub SIGNALduino_padbits(\@$) +{ + my $i=@{$_[0]} % $_[1]; + while (@{$_[0]} % $_[1] > 0) ## will pad up full nibbles per default or full byte if specified in protocol + { + push(@{$_[0]},'0'); + } + return " padded $i bits to bit_msg array"; +} + +sub SIGNALduino_Parse_MU($$$$@) +{ + #my ($hash, $iohash, $name, $rmsg,@msg_parts) = @_; + #my $rawData; + #my $clockidx; + #my %patternList; + #my %patternListRaw; + ## Check for each received message part and parse it + + my ($hash, $iohash, $name, $rmsg,%msg_parts) = @_; + + my $protocolid; + my $clockidx=$msg_parts{clockidx}; + my $protocol=undef; + my $rawData=$msg_parts{rawData}; + my %patternListRaw; + my $message_dispatched=0; + + Debug "$name: processing unsynced message\n" if ($debug); + + my $clockabs; #Clock will be fetched from Protocol + #$patternListRaw{$_} = floor($msg_parts{pattern}{$_}/$clockabs) for keys $msg_parts{pattern}; + $patternListRaw{$_} = $msg_parts{pattern}{$_} for keys $msg_parts{pattern}; + + + if (defined($clockidx)) + { + + ## Make a lookup table for our pattern index ids + #Debug "List of pattern:"; #Debug Dumper(\%patternList); + my $signal_length = length($rawData); # Length of data array + + ## Find matching protocols + my $id; + foreach $id ( keys %ProtocolListSIGNALduino) { + if (defined($hash->{"whitelisthash"}) && !defined($hash->{"whitelisthash"}{$id})) { + Log3 $name, 4, "skip ID $id"; + next; + } + + next if (exists $ProtocolListSIGNALduino{$id}{sync}); ## We can skip messages with sync defined + next if (defined($ProtocolListSIGNALduino{id}{format} && $ProtocolListSIGNALduino{id}{format} eq "manchester")); + next if (!defined($ProtocolListSIGNALduino{$id}{clockabs})); + + my $valid=1; + my $clockabs= $ProtocolListSIGNALduino{$id}{clockabs}; + my %patternList; + + %patternList = map { $_ => round($patternListRaw{$_}/$clockabs,1) } keys %patternListRaw; + my @keys = sort { $patternList{$a} <=> $patternList{$b} } keys %patternList; + + #Debug Dumper(\%patternList); + #Debug Dumper(@keys); + + #$debug=1; + + + Debug "Testing against Protocol id $id -> $ProtocolListSIGNALduino{$id}{name}" if ($debug); + + + +# $valid=SIGNALduino_inTol($ProtocolListSIGNALduino{$id}{clockabs},$clockabs,$clockabs*0.30) if ($ProtocolListSIGNALduino{$id}{clockabs} > 0); + + next if (!$valid) ; + + my $bit_length = ($signal_length/((scalar @{$ProtocolListSIGNALduino{$id}{one}} + scalar @{$ProtocolListSIGNALduino{$id}{zero}})/2)); + Debug "Expect $bit_length bits in message" if ($valid && $debug); + + #Check calculated min length + #$valid = $valid && $ProtocolListSIGNALduino{$id}{length_min} <= $bit_length if (exists $ProtocolListSIGNALduino{$id}{length_min}); + #Check calculated max length + #$valid = $valid && $ProtocolListSIGNALduino{$id}{length_max} >= $bit_length if (exists $ProtocolListSIGNALduino{$id}{length_max}); + + #next if (!$valid) ; + #Debug "expecting $bit_length bits in signal" if ($debug && $valid); + + Debug "Searching in patternList: ".Dumper(\%patternList) if($debug); + next if (!$valid) ; + + + my %patternLookupHash=(); + + #Debug "phash:".Dumper(%patternLookupHash); + my $pstr=""; + $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList)) >=0; + Debug "Found matched one" if ($debug && $valid); + $patternLookupHash{$pstr}="1" if ($valid); ## Append one to our lookuptable + Debug "added $pstr " if ($debug && $valid); + + $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList)) >=0; + Debug "Found matched zero" if ($debug && $valid); + $patternLookupHash{$pstr}="0" if ($valid); ## Append zero to our lookuptable + Debug "added $pstr " if ($debug && $valid); + + next if (!$valid) ; + #Debug "Pattern Lookup Table".Dumper(%patternLookupHash); + ## Check somethin else + + + #Anything seems to be valid, we can start decoding this. + + Log3 $name, 4, "Found matched Protocol id $id -> $ProtocolListSIGNALduino{$id}{name}" if ($valid); + my $signal_width= @{$ProtocolListSIGNALduino{$id}{one}}; + #Debug $signal_width; + + my @bit_msg=(); # array to store decoded signal bits + my $message_start = (index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList)) < index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList)) ? index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList)) : index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList))); + #for (my $i=index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{sync}}))+$signal_width;$ilength($rawData)) ## Dispatch if last signal or unknown data + { + Debug "$name: demodulated message raw (@bit_msg), ".@bit_msg." bits\n" if ($debug);; + while (scalar @bit_msg % $padwith > 0) ## will pad up full nibbles per default or full byte if specified in protocol + { + push(@bit_msg,'0'); + Debug "$name: padding 0 bit to bit_msg array" if ($debug); + } + #Check converted message against lengths + $valid = $valid && $ProtocolListSIGNALduino{$id}{length_min} <= scalar @bit_msg if (defined($ProtocolListSIGNALduino{$id}{length_min})); + $valid = $valid && $ProtocolListSIGNALduino{$id}{length_max} >= scalar @bit_msg if (defined($ProtocolListSIGNALduino{$id}{length_max})); + + #next if (!$valid); ## Last chance to try next protocol if there is somethin invalid + if ($valid) { + Log3 $name, 5, "dispatching bits: @bit_msg"; + my $dmsg = SIGNALduino_b2h(join "", @bit_msg); + $dmsg =~ s/^0+// if (defined($ProtocolListSIGNALduino{$id}{remove_zero})); + $dmsg = "$dmsg"."$ProtocolListSIGNALduino{$id}{postamble}" if (defined($ProtocolListSIGNALduino{$id}{postamble})); + $dmsg = "$ProtocolListSIGNALduino{$id}{preamble}"."$dmsg" if (defined($ProtocolListSIGNALduino{$id}{preamble})); + + SIGNALduno_Dispatch($hash,$rmsg,$dmsg); + $message_dispatched=1; + + } else { + Debug "$name: length ($ProtocolListSIGNALduino{$id}{length_min})/$ProtocolListSIGNALduino{$id}{length_max} does not match (@bit_msg), ".@bit_msg." bits\n" if ($debug);; + + + } + @bit_msg=(); # clear bit_msg array + + #Find next position of valid signal (skip invalid pieces) + $i = (index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList),$i) < index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList),$i) ? index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList),$i) : index($rawData,SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList),$i)); + $i-=$signal_width; + last if ($i <=-1); + Log3 $name, 5, "restarting demodulation at Position $i+$signal_width"; + + } + } + + #my $dmsg = sprintf "%02x", oct "0b" . join "", @bit_msg; ## Array -> String -> bin -> hex + } + return 0 if (!$message_dispatched); + + return 1; + } +} + + +sub +SIGNALduino_Parse_MC($$$$@) +{ + + my ($hash, $iohash, $name, $rmsg,%msg_parts) = @_; + my $clock=$msg_parts{clockabs}; ## absolute clock + my $rawData=$msg_parts{rawData}; + my $bitData; + my $dmsg; + my $message_dispatched=0; + + return undef if (!$clock); + #my $protocol=undef; + #my %patternListRaw = %msg_parts{patternList}; + + Debug "$name: processing manchester messag len:".length($rawData) if ($debug); + + my $hlen = length($rawData); + my $blen = $hlen * 4; + $bitData= unpack("B$blen", pack("H$hlen", $rawData)); + Debug "$name: extracted data $bitData (bin)\n" if ($debug); ## Convert Message from hex to bits + my $id; + foreach $id ( keys %ProtocolListSIGNALduino) { + if (defined($hash->{"whitelisthash"}) && !defined($hash->{"whitelisthash"}{$id})) { + Log3 $name, 4, "skip ID $id"; + next; + } + + next if (!defined($ProtocolListSIGNALduino{$id}{format}) or $ProtocolListSIGNALduino{$id}{format} ne "manchester"); + Debug "Testing against Protocol id $id -> $ProtocolListSIGNALduino{$id}{name}" if ($debug); + + if ( $clock >$ProtocolListSIGNALduino{$id}{clockrange}[0] and $clock <$ProtocolListSIGNALduino{$id}{clockrange}[1] and length($rawData)*4 >= $ProtocolListSIGNALduino{$id}{length_min} ) + { + Debug "clock and min length matched" if ($debug); + + my $method = $ProtocolListSIGNALduino{$id}{method}; + if (!exists &$method) + { + Log3 $name, 5, "$name: Error: Unknown function=$method. Please define it in file $0"; + } else { + my ($rcode,$res) = $method->($name,$bitData,$id); + if ($rcode != -1) { + $dmsg = $res; + $dmsg=$ProtocolListSIGNALduino{$id}{preamble}.$dmsg if (defined($ProtocolListSIGNALduino{$id}{preamble})); + SIGNALduno_Dispatch($hash,$rmsg,$dmsg); + $message_dispatched=1; + } else { + Log3 $name, 5, "protocol does not match return from method: ($res)" if ($debug); + + } + } + } + + } + return 0 if (!$message_dispatched); + return 1; +} + + +sub +SIGNALduino_Parse($$$$@) +{ + my ($hash, $iohash, $name, $rmsg, $initstr) = @_; + + #print Dumper(\%ProtocolListSIGNALduino); + + return undef if !($rmsg=~ m/^\002M.;.*;\003/); ## Check if a Data Message arrived and if it's complete (start & end control char are received) + $rmsg=~ s/^\002(M.;.*;)\003/$1/; # cut off start end end character from message for further processing they are not needed + Debug "$name: incomming message: ($rmsg)\n" if ($debug); + + my %signal_parts=SIGNALduino_Split_Message($rmsg,$name); ## Split message and save anything in an hash %signal_parts + #Debug "raw data ". $signal_parts{rawData}; + + + my @msg_parts = SIGNALduino_splitMsg($rmsg,';'); ## Split message parts by ";" + my $dispatched; + # Message Synced type -> M# + if ($rmsg=~ m/^M\d+;(P\d=-?\d+;){4,7}D=\d+;CP=\d;SP=\d;/) + { + Log3 $name, 3, "You are using an outdated version of signalduino code on your arduino. Please update"; + return undef; + } + if ($rmsg=~ m/^MS;(P\d=-?\d+;){4,7}D=\d+;CP=\d;SP=\d;/) + { + $dispatched= SIGNALduino_Parse_MS($hash, $iohash, $name, $rmsg,%signal_parts); + } + + # Message unsynced type -> MU + elsif ($rmsg=~ m/^MU;(P\d=-?\d+;){4,7}D=\d+;CP=\d;/) + { + #return SIGNALduino_Parse_MU($hash, $iohash, $name, $rmsg,@msg_parts); + $dispatched= SIGNALduino_Parse_MU($hash, $iohash, $name, $rmsg,%signal_parts); + + } + # Manchester encoded Data -> MC + elsif ($rmsg=~ m/^MC;.*;/) + { + #return SIGNALduino_Parse_MC($hash, $iohash, $name, $rmsg,@msg_parts); + $dispatched= SIGNALduino_Parse_MC($hash, $iohash, $name, $rmsg,%signal_parts); + + } + else { + Debug "$name: unknown Messageformat, aborting\n" if ($debug); + return undef; + } + + if ( AttrVal($hash->{NAME},"verbose","0") > 4 && !$dispatched) + { + my $notdisplist; + my @lines; + if (defined($hash->{unknownmessages})) + { + $notdisplist=$hash->{unknownmessages}; + @lines = split ('#', $notdisplist); # or whatever + } + push(@lines,FmtDateTime(time())."-".$rmsg); + shift(@lines)if (scalar @lines >25); + $notdisplist = join('#',@lines); + + $hash->{unknownmessages}=$notdisplist; + return undef; + #Todo compare Sync/Clock fact and length of D= if equal, then it's the same protocol! + } + + +} + + +##################################### +sub +SIGNALduino_Ready($) +{ + my ($hash) = @_; + + return DevIo_OpenDev($hash, 1, "SIGNALduino_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 +SIGNALduino_SimpleWrite(@) +{ + my ($hash, $msg, $nonl) = @_; + return if(!$hash); + if($hash->{TYPE} eq "SIGNALduino_RFR") { + # Prefix $msg with RRBBU and return the corresponding SIGNALduino hash. + ($hash, $msg) = SIGNALduino_RFR_AddPrefix($hash, $msg); + } + + my $name = $hash->{NAME}; + Log3 $name, 5, "SW: $msg"; + + $msg .= "\n" unless($nonl); + + $hash->{USBDev}->write($msg) if($hash->{USBDev}); + syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev}); + 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); +} + +sub +SIGNALduino_Attr(@) +{ + my ($cmd,$name,$aName,$aVal) = @_; + my $hash = $defs{$name}; + + Log3 $name, 5, "Calling Getting Attr sub with args: $cmd $aName = $aVal"; + + if( $aName eq "Clients" ) { ## Change clientList + $hash->{Clients} = $aVal; + $hash->{Clients} = $clientsSIGNALduino if( !$hash->{Clients}) ; ## Set defaults + return "Setting defaults"; + } elsif( $aName eq "MatchList" ) { ## Change matchList + my $match_list; + if( $cmd eq "set" ) { + $match_list = eval $aVal; + if( $@ ) { + Log3 $name, 2, $name .": $aVal: ". $@; + } + } + + if( ref($match_list) eq 'HASH' ) { + $hash->{MatchList} = $match_list; + } else { + $hash->{MatchList} = \%matchListSIGNALduino; ## Set defaults + Log3 $name, 2, $name .": $aVal: not a HASH using defaults" if( $aVal ); + } + } + elsif ($aName eq "verbose") + { + Log3 $name, 3, "$name: setting Verbose to: " . $aVal; + $hash->{unknownmessages}="" if $aVal <4; + + } + elsif ($aName eq "debug") + { + $debug = $aVal; + Log3 $name, 3, "$name: setting debug to: " . $debug; + } + elsif ($aName eq "whitelist_IDs" && $cmd=="set") + { + + my %WhitelistIDs; + if (defined($aVal) && length($aVal)>0) + { + %WhitelistIDs = map { $_ => 1 } split(",", $aVal); + $hash->{"whitelisthash"} = \%WhitelistIDs; + } else { + delete $hash->{"whitelisthash"}; + delete($attr{$name}{$aName}); + + Log3 $name, 5, "$name: deleting $aName"; + return "$name: deleting $aName"; + } + } + + return undef; +} + + + +sub SIGNALduino_OSV2() +{ + my ($name,$bitData,$id) = @_; + + if (index($bitData,"10011001",24) >= 24 and $bitData =~ m/^.?(10){12,16}/) + { # Valid OSV2 detected! + + Debug "$name: OSV2 protocol detected \n" if ($debug); + my $preamble_pos=index($bitData,"10011001",24); + + return return (-1," sync not found") if ($preamble_pos <=24); + + my $message_end=index($bitData,"10011001",$preamble_pos+44); + $message_end = length($bitData) if ($message_end == -1); + my $message_length = $message_end - $preamble_pos; + + return (-1," message is to short") if (defined($ProtocolListSIGNALduino{$id}{length_min}) && $message_length < $ProtocolListSIGNALduino{$id}{length_min} ); + return (-1," message is to long") if (defined($ProtocolListSIGNALduino{$id}{length_max}) && $message_length > $ProtocolListSIGNALduino{$id}{length_max} ); + + my $idx=0; + my $osv2bits=""; + my $osv2hex =""; + + for ($idx=$preamble_pos;$idx= 0) # $rawData =~ m/^A{2,3}/) + { # Valid AS detected! + my $message_start = index($bitData,"1100",16); + Debug "$name: AS protocol detected \n" if ($debug); + + my $message_end=index($bitData,"1100",$message_start+16); + $message_end = length($bitData) if ($message_end == -1); + my $message_length = $message_end - $message_start; + + return (-1," message is to short") if (defined($ProtocolListSIGNALduino{$id}{length_min}) && $message_length < $ProtocolListSIGNALduino{$id}{length_min} ); + return (-1," message is to long") if (defined($ProtocolListSIGNALduino{$id}{length_max}) && $message_length > $ProtocolListSIGNALduino{$id}{length_max} ); + + + my $msgbits =substr($bitData,$message_start); + + my $ashex=sprintf('%02X', oct("0b$msgbits")); + Log3 $name, 5, "$name: AS protocol converted to hex: ($ashex) with length ($message_length) bits \n"; + + return (1,$bitData); + } + return (-1,undef); +} + +sub SIGNALduino_Hideki() +{ + my ($name,$bitData,$id) = @_; + Debug "$name: search in $bitData \n" if ($debug); + my $message_start = index($bitData,"10101110"); + if ($message_start >= 0 ) # 0x75 but in reverse order + { + Debug "$name: Hideki protocol detected \n" if ($debug); + + # Todo: Mindest Länge für startpunkt vorspringen + # Todo: Wiederholung auch an das Modul weitergeben, damit es dort geprüft werden kann + my $message_end = index($bitData,"10101110",$message_start+18); # pruefen auf ein zweites 0x75, mindestens 18 bit nach 1. 0x75 + $message_end = length($bitData) if ($message_end == -1); + my $message_length = $message_end - $message_start; + + return (-1,"message is to short") if (defined($ProtocolListSIGNALduino{$id}{length_min}) && $message_length < $ProtocolListSIGNALduino{$id}{length_min} ); + return (-1,"message is to long") if (defined($ProtocolListSIGNALduino{$id}{length_max}) && $message_length > $ProtocolListSIGNALduino{$id}{length_max} ); + + + my $hidekihex; + my $idx; + + for ($idx=$message_start; $idx<$message_end; $idx=$idx+9) + { + my $byte = ""; + $byte= substr($bitData,$idx,8); ## Ignore every 9th bit + Debug "$name: byte in order $byte " if ($debug); + $byte = scalar reverse $byte; + Debug "$name: byte reversed $byte , as hex: ".sprintf('%X', oct("0b$byte"))."\n" if ($debug); + + $hidekihex=$hidekihex.sprintf('%02X', oct("0b$byte")); + } + Log3 $name, 4, "$name: hideki protocol converted to hex: $hidekihex with " .$message_length ." bits, messagestart $message_start"; + + return (1,$hidekihex); ## Return only the original bits, include length + } + return (-1,""); +} + + + +1; + +=pod +=begin html + + +

SIGNALduino

+ + + +
+ The SIGNALduino ia based on an idea from mdorenka published at FHEM Forum. + + With the opensource firmware (see this link) it is capable + to receive and send different protocols over different medias. Currently are 433Mhz protocols implemented. +

+ + The following devices are currently available: +

+ + + Wireless switches
+ IT/Elro and other brands --> uses IT.pm
+ +

+
    +
  • Temperatur / humidity sensors suppored by 14_CUL_TCM97001
  • +
  • PEARL NC7159, LogiLink WS0002,GT-WT-02,AURIOL,TCM97001, TCM27 and many more
  • +
  • Oregon Scientific v2 Sensors --> 41_OREGON.pm
  • +
  • Temperatur / humidity sensors suppored by 14_SD_WS07
  • +
  • technoline WS 6750 and TX70DTH
  • +
  • Eurochon EAS 800z
  • +
+

+ + It is possible to attach more than one device in order to get better + reception, fhem will filter out duplicate messages.

+ + Note: this module require the Device::SerialPort or Win32::SerialPort + module. It can currently only attatched via USB. + +
+ +
+ + Define
+ define <name> SIGNALduino <device>
+
+ USB-connected devices (SIGNALduino):
+
  • + <device> specifies the serial port to communicate with the SIGNALduino. + The name of the serial-device depends on your distribution, under + linux the cdc_acm kernel module is responsible, and usually a + /dev/ttyACM0 or /dev/ttyUSB0 device will be created. If your distribution does not have a + cdc_acm module, you can force usbserial to handle the SIGNALduino by the + following command: +
      + modprobe usbserial + vendor=0x03eb + product=0x204b +
    In this case the device is most probably + /dev/ttyUSB0.

    + + You can also specify a baudrate if the device name contains the @ + character, e.g.: /dev/ttyACM0@57600

    This is also the default baudrate + + It is recommended to specify the device via a name which does not change: + e.g. via by-id devicename: /dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0@57600 + + If the baudrate is "directio" (e.g.: /dev/ttyACM0@directio), then the + perl module Device::SerialPort is not needed, and fhem opens the device + with simple file io. This might work if the operating system uses sane + defaults for the serial parameters, e.g. some Linux distributions and + OSX.

    +
  • + +
+
+ + +
  • do_not_notify
  • +
  • dummy
  • +
  • debug
    + This will bring the module in a very verbose debug output. Usefull to find new signals and verify if the demodulation works correctly. +
  • +
  • flashCommand
    + This is the command, that is executed to performa the firmware flash. Do not edit, if you don't know what you are doing.
    + The default is: avrdude -p atmega328P -c arduino -P [PORT] -D -U flash:w:[HEXFILE] 2>[LOGFILE]
    + It contains some place-holders that automatically get filled with the according values:
    +
      +
    • [PORT]
      + is the port the Signalduino is connectd to (e.g. /dev/ttyUSB0) and will be used from the defenition
    • +
    • [HEXFILE]
      + is the .hex file that shall get flashed. There are three options (applied in this order):
      + - passed in set flash
      + - taken from the hexFile attribute
      + - the default value defined in the module
      +
    • +
    • [LOGFILE]
      + The logfile that collects information about the flash process. It gets displayed in FHEM after finishing the flash process
    • +
    + +
  • +
  • hardware
    + When using the flash command, you should specify whar hardware you have connected to the usbport. Doing not, can cause failures of the device. +
  • +
  • minsecs
    + This is a very special attribute. It is provided to other modules. minsecs should act like a threshold. All logic must be done in the logical module. + If specified, then supported modules will discard new messages if minsecs isn't past. +
  • +
  • longids
    + Comma separated list of device-types for SIGNALduino that should be handled using long IDs. This additional ID allows it to differentiate some weather sensors, if they are sending on the same channel. Therfor a random generated id is added. If you choose to use longids, then you'll have to define a different device after battery change.
    + Default is to not to use long IDs for all devices. +

    + Examples:
    +# Do not use any long IDs for any devices:
    +attr sduino longids 0
    +# Use any long IDs for all devices (this is default):
    +attr sduino longids 1
    +# Use longids for BTHR918N devices.
    +# Will generate devices names like BTHR918N_f3.
    +attr sduino longids BTHR918N
    +
    +

  • + + + Get +
      +
    • version
      + return the SIGNALduino firmware version +

    • +
    • raw
      + Issue a SIGNALduino firmware command, and wait for one line of data returned by + the SIGNALduino. See the SIGNALduino firmware code for details on SIGNALduino + commands. With this line, you can send almost any signal via a transmitter connected +

    • +
    • cmds
      + Depending on the firmware installed, SIGNALduinos have a different set of + possible commands. Please refer to the sourcecode of the firmware of your + SIGNALduino to interpret the response of this command. See also the raw- + command. +

    • +
    • ITParms
      + For sending IT Signals for wireless switches, the number of repeats and the base duration can be set. + With the get command, you can verify what is programmed into the uC. +

    • + +
    + + SET +
      +
    • ITClock
      + Sets the clock which is used to send the signal for IT switches. (Default is 300) +

    • +
    • raw
      + Issue a SIGNALduino firmware command, without waiting data returned by + the SIGNALduino. See the SIGNALduino firmware code for details on SIGNALduino + commands. With this line, you can send almost any signal via a transmitter connected +

    • +
    • reset
      + This will do a reset of the usb port and normaly causes to reset the uC connected. +

    • +
    • flash [hexFile]
      + The SIGNALduino needs the right firmware to be able to receive and deliver the sensor data to fhem. In addition to the way using the + arduino IDE to flash the firmware into the SIGNALduino this provides a way to flash it directly from FHEM. + + There are some requirements: +
        +
      • avrdude must be installed on the host
        + On a Raspberry PI this can be done with: sudo apt-get install avrdude
      • +
      • the hardware attribute must be set if using any other hardware as an Arduino nano
        + This attribute defines the command, that gets sent to avrdude to flash the uC.
      • +
        + +
      +
    • + + +
    + + + + + + +=end html +=cut diff --git a/fhem/HISTORY b/fhem/HISTORY index 560125f5e..21844e274 100644 --- a/fhem/HISTORY +++ b/fhem/HISTORY @@ -636,3 +636,6 @@ - Mon Aug 23 2015 (rapster) - added new module 74_Unifi for the Ubiquiti Networks (UBNT) - Unifi Controller. + +- Wed Okt 21 2015 (sidey) + - added new module 00_SIGNALduino, 90_SIGNALduino_un, 14_Hideki, 14_SD_WS07 diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index b85290d19..69fd3d37d 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -30,6 +30,7 @@ FHEM/00_NetzerI2C.pm klausw http://forum.fhem.de Sonstige FHEM/00_OWX.pm pahenning/ntruchsess http://forum.fhem.de 1Wire FHEM/00_OWX_ASYNC ntruchsess http://forum.fhem.de 1Wire FHEM/00_RPII2C klausw http://forum.fhem.de Einplatinencomputer +FHEM/00_SIGNALduino.pm Sidey http://forum.fhem.de Sonstige Systeme FHEM/00_SONOS.pm Reinerlein http://forum.fhem.de Multimedia FHEM/00_TCM.pm klaus-schauer http://forum.fhem.de EnOcean FHEM/00_THZ.pm immiimmi http://forum.fhem.de Heizungssteuerung/Raumklima @@ -65,6 +66,7 @@ FHEM/10_pilight_ctrl.pm risiko http://forum.fhem.de Sonstige FHEM/11_FHT.pm rudolfkoenig http://forum.fhem.de SlowRF FHEM/11_FHT8V.pm rudolfkoenig http://forum.fhem.de SlowRF FHEM/11_OWDevice.pm borisneubert/mfr69bs http://forum.fhem.de 1Wire +FHEM/14_Hideki.pm Sidey/Ralf9 http://forum.fhem.de Sonstige Systeme FHEM/12_HMS.pm rudolfkoenig http://forum.fhem.de SlowRF FHEM/13_KS300.pm rudolfkoenig http://forum.fhem.de SlowRF FHEM/14_CUL_MAX.pm mgehre http://forum.fhem.de MAX @@ -75,6 +77,7 @@ FHEM/15_CUL_EM.pm rudolfkoenig http://forum.fhem.de SlowRF FHEM/16_CUL_RFR.pm rudolfkoenig http://forum.fhem.de SlowRF FHEM/16_STACKABLE_CC.pm rudolfkoenig http://forum.fhem.de SlowRF FHEM/17_EGPM2LAN.pm alexus http://forum.fhem.de Sonstiges +FHEM/14_SD_WS07.pm Sidey/Ralf9 http://forum.fhem.de Sonstige Systeme FHEM/17_SIS_PMS.pm painseeker http://forum.fhem.de Sonstiges FHEM/18_CUL_HOERMANN.pm rudolfkoenig http://forum.fhem.de SlowRF FHEM/19_Revolt.pm martinppp/mehf http://forum.fhem.de SlowRF @@ -247,6 +250,7 @@ FHEM/88_WEBCOUNT.pm sachag http://forum.fhem.de Sonstiges FHEM/89_HEATRONIC.pm heikoranft http://forum.fhem.de Sonstige Systeme FHEM/89_VCONTROL.pm adamwit http://forum.fhem.de Heizungssteuerung/Raumklima FHEM/90_at.pm rudolfkoenig http://forum.fhem.de Automatisierung +FHEM/14_SIGNALduino_un.pm Side http://forum.fhem.de Sonstige Systeme FHEM/91_eventTypes.pm rudolfkoenig http://forum.fhem.de Frontends FHEM/91_notify.pm rudolfkoenig http://forum.fhem.de Automatisierung FHEM/91_sequence.pm rudolfkoenig http://forum.fhem.de Automatisierung