diff --git a/fhem/CHANGED b/fhem/CHANGED index 0c4c10706..8f00b0449 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,9 @@ # Add changes at the top of the list. Keep it in ASCII - SVN + - feature: new module 34_SWAP for generic SWAP protocoll support using + a panStick and 34_panStamp as IODevice(by justme1968) + - feature: new module 34_panStamp to use a panStick as a RF modem + for panStamps using the SWAP protocoll (by justme1968) - feature: EGPM2LAN module added for Gembird (R) Energenie LAN Support - feature: FHEMWEB use widget module to update colorpicker in longpoll - feature: FHEMWEB widget (slider/etc) javascript handler modularized diff --git a/fhem/FHEM/34_SWAP.pm b/fhem/FHEM/34_SWAP.pm new file mode 100755 index 000000000..95675e60f --- /dev/null +++ b/fhem/FHEM/34_SWAP.pm @@ -0,0 +1,1050 @@ + +# $Id$ +# +# TODO: +# transmitted queue -> remove if status is received +# remove old command from send queue if new command for the same register is send +# merge send and trasmit queue +# rename $hash->{product} to $hash->{'.product'} +# rename $hash->{devices} to $hash->{'.devices'} + +package main; + +use strict; +use warnings; +use SetExtensions; + +use Data::Dumper; +use XML::Simple qw(:strict); + +sub SWAP_Parse($$); +sub SWAP_Send($$$@); +sub SWAP_PushCmdStack($$$@); +sub SWAP_ProcessCmdStack($); + +my %models = ( +); + +my %function_codes = ( + '00' => 'status', + '01' => 'query', + '02' => 'command', +); + + +use constant { STATUS => '00', + QUERY => '01', + COMMAND => '02', + BIN => 1, + NUM => 2, + STRING => 3, + STREAM => 4, + IN => 1, + OUT => 2, }; + +my %default_registers = ( + 0x00 => { name => 'ProductCode', size => 8, endpoints => [ { name => 'ProductCode', size => 8 }, + { name => 'ManufacturerID', position => 0, size => 4, }, + { name => 'ProductID', position => 4, size => 4, }, ], }, + 0x01 => { name => 'HardwareVersion', }, + 0x02 => { name => 'FirmwareVersion', }, + 0x03 => { name => 'SystemState', }, + 0x04 => { name => 'FrequencyChannel', }, + 0x05 => { name => 'SecurityOption', }, + 0x06 => { name => 'SecurityPassword', }, + 0x07 => { name => 'SecurityNonce', }, + 0x08 => { name => 'NetworkID', }, + 0x09 => { name => 'DeviceAddress', size => 1, direction => OUT }, + 0x0A => { name => 'PeriodicTxInterval', size => 2, direction => OUT }, +); + +my %system_sate = ( + 0x00 => 'RESTART', + 0x01 => 'RXON', + 0x02 => 'RXOFF', + 0x03 => 'SYNC', + 0x04 => 'LOWBAT', +); + +my $developers = {}; +my $products = {}; + +sub +SWAP_Initialize($) +{ + my ($hash) = @_; + + ($developers,$products) = SWAP_loadDevices(); + + $hash->{Match} = ".*"; + $hash->{SetFn} = "SWAP_Set"; + $hash->{GetFn} = "SWAP_Get"; + $hash->{DefFn} = "SWAP_Define"; + $hash->{UndefFn} = "SWAP_Undef"; + $hash->{FingerprintFn} = "SWAP_Fingerprint"; + $hash->{ParseFn} = "SWAP_Parse"; + $hash->{AttrFn} = "SWAP_Attr"; + $hash->{AttrList} = "IODev ". + "ignore:1,0 ". + "loglevel:0,1,2,3,4,5,6 $readingFnAttributes " . + "ProductCode:".join(",", sort keys %{$products}); + + #$hash->{FW_summaryFn} = "SWAP_summaryFn"; +} + +sub +SWAP_loadDevices() +{ + my $_developers = {}; + my $_products = {}; + + my $file_name = "$attr{global}{modpath}/FHEM/lib/SWAP/devices.xml"; + + if( ! -e $file_name ){ + Log 2, "could not read $file_name"; + return ($_developers,$_products); + } + + my $developers = XMLin($file_name, KeyAttr => { }, ForceArray => [ 'dev' ]); + + foreach my $developer (@{$developers->{developer}}){ + my $developer_id = $developer->{id}; + + my $_developer = $_developers->{$developer_id} = { name => $developer->{name}, devices => {}, }; + + foreach my $device (@{$developer->{dev}}){ + my $id = $device->{id}; + my $name = $device->{name}; + my $label = $device->{label}; + + $_developer->{devices}->{$id} = { name => $name, label => $label, }; + + my $productcode = sprintf("%08X%08X", $developer_id, $id); + $_products->{$productcode} = { name => $name, label => $label, }; + + #readDeviceXaML( $_products->{$productcode}, "$attr{global}{modpath}/FHEM/lib/SWAP/$_developer->{name}/$name.xml" ); + } + } + + return ($_developers,$_products); +} +sub +readDeviceXML($$) +{ + my ($product, $file_name) = @_; + my $map = { bin => BIN, + num => NUM, + string => STRING, + stream => STREAM, + inp => IN, + out => OUT, }; + + if( ! -e $file_name ) { + $product = undef; + Log 2, "could not read $file_name"; + return; + } + + my $device = XMLin($file_name, KeyAttr => {}, ForceArray => [ 'reg', 'param', 'endpoint', 'unit', ]); + + $product->{pwrdownmode} = $device->{pwrdownmode} eq "true"?1:0; + foreach my $register (@{$device->{config}->{reg}}){ + my $id = $register->{id}; + $product->{registers}->{$id} = { name => $register->{name}, type => "config" }; + + my $i = 0; + foreach my $param (@{$register->{param}}){ + push @{$product->{registers}->{$id}->{endpoints}}, (); + my $_endpoint = $product->{registers}->{$id}->{endpoints}->[$i] = {}; + $_endpoint->{name} = $param->{name}; + $_endpoint->{name} =~ s/ /_/g; + $_endpoint->{position} = 0+$param->{position} if( defined($param->{position}) ); + $_endpoint->{size} = 0+$param->{size} if( defined($param->{size}) ); + $_endpoint->{direction} = OUT; + $_endpoint->{type} = $map->{$param->{type}}; + $_endpoint->{default} = $param->{default}; + $_endpoint->{verif} = $param->{verif}; + ++$i; + } + } + foreach my $register (@{$device->{regular}->{reg}}){ + my $id = $register->{id}; + $product->{registers}->{$id} = { name => $register->{name}, type => "regular" }; + + my $i = 0; + foreach my $endpoint (@{$register->{endpoint}}){ + push @{$product->{registers}->{$id}->{endpoints}}, (); + my $_endpoint = $product->{registers}->{$id}->{endpoints}->[$i] = {}; + $_endpoint->{name} = $endpoint->{name}; + $_endpoint->{name} =~ s/ /_/g; + $_endpoint->{position} = 0+$endpoint->{position} if( defined($endpoint->{position}) ); + $_endpoint->{size} = 0+$endpoint->{size} if( defined($endpoint->{size}) ); + $_endpoint->{direction} = $map->{$endpoint->{dir}}; + $_endpoint->{type} = $map->{$endpoint->{type}}; + + $_endpoint->{size} = 1 if( !defined($_endpoint->{size}) && $_endpoint->{direction} == OUT ); + + if( defined($endpoint->{units}) && defined($endpoint->{units}->{unit}) ) { + foreach my $unit (@{$endpoint->{units}->{unit}}){ + push @{$_endpoint->{units}}, $unit; + } + } + ++$i; + } + } +} + +sub +SWAP_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + if(@a != 3 && @a != 4 ) { + my $msg = "wrong syntax: define SWAP [.] []"; + Log 2, $msg; + return $msg; + } + + $a[2] =~ m/^([\da-f]{2})(\.([\da-f]{2}))?$/i; + #$a[2] =~ m/^([\da-f]{2})(\.([\da-f]{2}(\.[\da-f]+)?))?$/i; + return "$a[2] is not a valid SWAP address" if( !defined($1) ); + + my $name = $a[0]; + my $addr = $1; #substr( $a[2], 0, 2 ); + my $reg = $3; #substr( $a[2], 3, 2 ); + my $productcode = $a[3]; + + return "$addr is not a 1 byte hex value" if( $addr !~ /^[\da-f]{2}$/i ); + return "$addr is not an allowed address" if( $address eq "00" ); + + return "$reg not allowed" if( $reg && hex($reg) <= 0x0A ); + #return "please define a SWAP device with $addr first" if( $reg && !$modules{SWAP}{defptr}{$addr} ); + + my $id = $addr; + $id .= ".". $reg if( $reg ); + + return "SWAP device $id already used for $modules{SWAP}{defptr}{$id}->{NAME}." if( $modules{SWAP}{defptr}{$id} + && $modules{SWAP}{defptr}{$id}->{NAME} ne $name ); +# return "SWAP device $addr already used for $modules{SWAP}{defptr}{$addr}->{NAME}." if( $modules{SWAP}{defptr}{$addr} +# && $modules{SWAP}{defptr}{$addr}->{NAME} ne $name ); + + delete( $hash->{reg} ); + delete( $hash->{product} ) if( defined($attr{$name}{ProductCode}) && $attr{$name}{ProductCode} ne $productcode ); + delete( $attr{$name}{ProductCode} ) if( defined($attr{$name}{ProductCode}) && $attr{$name}{ProductCode} ne $productcode ); + + $hash->{addr} = $addr; + $hash->{reg} = $reg if( $reg ); + $hash->{devices} = () if( !$reg ); + + $modules{SWAP}{defptr}{$id} = $hash; + $modules{SWAP}{defptr}{$addr}->{devices}{$reg} = $hash if( $reg ); + + my $type = $hash->{TYPE}; + $hash->{TYPE} = "SWAP"; + AssignIoPort($hash); + $hash->{TYPE} = $type; + if(defined($hash->{IODev}->{NAME})) { + Log 3, "$name: I/O device is " . $hash->{IODev}->{NAME}; + } else { + Log 1, "$name: no I/O device"; + } + + CommandAttr(undef, "$name ProductCode $productcode") if( $productcode ); + if( defined($productcode) && defined($products->{$productcode}) + && !defined($products->{$productcode}->{registers}) ){ + SWAP_readDeviceXML( $hash, $productcode ); + } + + $hash->{product} = $products->{$productcode} if( defined($productcode) && defined($products->{$productcode} ) ); + + SWAP_Send($hash, $addr, QUERY, "00" ); + + return undef; +} + +##################################### +sub +SWAP_Undef($$) +{ + my ($hash, $arg) = @_; + my $name = $hash->{NAME}; + my $addr = $hash->{addr}; + my $reg = $hash->{reg}; + + my $id = $addr; + $id .= ".". $reg if( $reg ); + delete( $modules{SWAP}{defptr}{$id} ); + delete($modules{SWAP}{defptr}{$addr}->{devices}{$reg}) if( $reg ); + + foreach my $reg (keys %{$hash->{devices}}){ + CommandDelete(undef,$modules{SWAP}{defptr}{$addr.".".$reg}->{NAME}) + } + + return undef; +} + +##################################### +sub +SWAP_Set($@) +{ + my ($hash, $name, @aa) = @_; + + my $cnt = @aa; + + return "\"set $name\" needs at least one parameter" if($cnt < 1); + + my $cmd = $aa[0]; + my $arg = $aa[1]; + my $arg2 = $aa[2]; + my $arg3 = $aa[3]; + my $ll = GetLogLevel($name,3); + + my $list = "regGet regSet"; + $list .= " statusRequest"; + $list .= " readDeviceXML"; + $list .= " clearUnconfirmed"; + + if( my $sl = $modules{$hash->{TYPE}}{SWAP_SetList} ) { + + if( $cmd ne '?' ) { + if( my @a = grep( $_ =~ /^$cmd($|:)/, keys %{$sl} ) ) { + return "set $cmd requires $sl->{$a[0]} parameter(s)" if( $sl->{$a[0]} != -99 && $sl->{$a[0]} != $cnt-1 ); + + if( my $set = $modules{$hash->{TYPE}}{SWAP_SetFn} ) { + no strict "refs"; + ($cmd, $arg, $arg2, $arg3) = &{$set}(@_); + use strict "refs"; + + return $arg if( !defined($cmd) ); + + $cnt = 2 if( defined($arg) ); + $cnt = 3 if( defined($arg2) ); + } + } + } + + $list .= " " . join(" ", sort keys %{$sl}); + } + + if( $hash->{reg} ) { + my $reg = hex($hash->{reg}); + if( defined($hash->{product}->{registers}->{$reg}) + && defined($hash->{product}->{registers}->{$reg}->{endpoints}->[0]) + && $hash->{product}->{registers}->{$reg}->{endpoints}->[0]->{size} == 1 ) { + + my $hasOn = ($list =~ m/\bon\b/); + my $hasOff = ($list =~ m/\boff\b/); + my $hasPct = ($list =~ m/\bpct\b/) || ($hash->{product}->{registers}->{$reg}->{endpoints}->[0]->{type} != NUM); + + $list .= " on" if( !$hasOn ); + $list .= " off" if( !$hasOff ); + $list .= " pct:slider,0,1,100" if( !$hasPct ); + + if( !$hasOn && $cmd eq "on" ) { + $cmd = "regSet"; + $arg = $hash->{reg}; + $arg2 = "FF"; + $cnt = 3; + } elsif( !$hasOff && $cmd eq "off" ) { + $cmd = "regSet"; + $arg = $hash->{reg}; + $arg2 = "00"; + $cnt = 3; + } elsif( !$hasPct && $cmd eq "pct" ) { + $cmd = "regSet"; + $arg2 = sprintf("%02X",$arg*255/100); + $arg = $hash->{reg}.".1"; + $cnt = 3; + } + } + } + + return "\"set $name $cmd\" needs one argument" if( $cnt < 2 && ( $cmd eq 'regGet' ) ); + return "\"set $name $cmd\" needs two arguments" if( $cnt < 3 && ( $cmd eq 'regSet' ) ); + + if( $cmd eq "regSet" ) { + $arg =~ m/^([\da-f]{2})(\.([\da-f]+))?$/i; + return "$arg is not a valid register name for $cmd" if( !defined($1) ); + + my $reg = hex($1); + if( $reg <= 0x0A ) { + return "register $arg is readonly" if( !defined($default_registers{$reg}->{direction}) ); + my $len = $default_registers{$reg}->{size}; + return "value has to be ". $len ." byte(s) in size" if( $len*2 != length( $arg2 ) ); + } else { + return "register $arg is not known" if( $hash->{reg} && hex($hash->{reg}) != $reg ); + return "register $arg is not known" if( !defined($hash->{product}->{registers}->{$reg}) ); + return "register $arg is readonly" if( $hash->{product}->{registers}->{$reg}->{endpoints}->[0]->{direction} != OUT ); + + if( defined($3) ) { + my $ep = hex($3); + + #return "can't write endpoint for sleeping devices" if( $hash->{product}->{pwrdownmode} == 1 ); + + return "endpoint $1.0 is not known" if( !defined($hash->{product}->{registers}->{$reg}->{endpoints}->[0]) ); + my $endpoint = $hash->{product}->{registers}->{$reg}->{endpoints}->[0]; + return "reading for $1 is not available" if( !defined(ReadingsVal( $name, $1."-".$endpoint->{name}, undef)) ); + + return "endpoint $1.$3 is not known" if( !defined($hash->{product}->{registers}->{$reg}->{endpoints}->[$ep]) ); + return "endpint $1.$3 is readonly" if( $hash->{product}->{registers}->{$reg}->{endpoints}->[$ep]->{direction} != OUT ); + + my $len = $hash->{product}->{registers}->{$reg}->{endpoints}->[$ep]->{size}; + return "value has to be ". $len ." byte(s) in size" if( $len*2 != length( $arg2 ) ); + } else { + my $len = $hash->{product}->{registers}->{$reg}->{endpoints}->[0]->{size}; + return "value has to be ". $len ." byte(s) in size" if( $len*2 != length( $arg2 ) ); + } + } + } elsif( $cmd eq "regGet" ) { + $arg =~ m/^([\da-f]{2})$/i; + return "$arg is not a valid register name for $cmd" if( !defined($1) ); + + my $reg = hex($1); + return "register $arg is not known" if( $hash->{reg} && hex($hash->{reg}) != $reg ); + } + + readingsSingleUpdate($hash, "state", "set-".$cmd, 1) if( $cmd ne "?" ); + + my $addr = $hash->{addr}; + if( $cmd eq "regGet" ) { + + SWAP_Send($hash, $addr, QUERY, sprintf("%02s",$arg) ); + + } elsif ( $cmd eq "regSet" ) { + + $arg =~ m/^([\da-f]{2})(\.([\da-f]+))?$/i; + my $reg = hex($1); + if( defined($3) ) { + my $ep = hex($3); + + if( defined($hash->{product}->{registers}->{$reg}->{endpoints}) + && defined($hash->{product}->{registers}->{$reg}->{endpoints}->[$ep]) ) { + my $endpoint = $hash->{product}->{registers}->{$reg}->{endpoints}->[0]; + my $value = ReadingsVal( $name, $1."-".$endpoint->{name}, undef ); + + if( defined($hash->{product}->{registers}->{$reg}->{endpoints}->[$ep]) ) { + $endpoint = $hash->{product}->{registers}->{$reg}->{endpoints}->[$ep]; + + if( defined( $endpoint->{position} ) ) { + substr( $value, $endpoint->{position}*2, $endpoint->{size}*2, $arg2 ); + + $arg2 = $value; + } + } + } + } + + #if( defined($hash->{product}->{registers}->{$reg}->{endpoints}) + # && defined($hash->{product}->{registers}->{$reg}->{endpoints}->[0]) ) { + # if( my $verif = $hash->{product}->{registers}->{$reg}->{endpoints}->[0]->{verif} ) { + # } + #} + + if( $hash->{product}->{pwrdownmode} == 1 + && $hash->{"SWAP_03-SystemState"} ne "03" ) { + SWAP_PushCmdStack($hash, $addr, COMMAND, sprintf("%02X",$reg), sprintf("%02s",$arg2) ); + } else { + SWAP_Send($hash, $addr, COMMAND, sprintf("%02X",$reg), sprintf("%02s",$arg2) ); + } + +# #change device address +# if( $reg == 0x09 +# && $hash->{product}->{pwrdownmode} == 0 ) { +# delete( $modules{SWAP}{defptr}{$addr} ); +# +# $addr = sprintf( "%02s", $arg2 ); +# +# $hash->{DEF} = $addr; +# $hash->{addr} = $addr; +# $hash->{"SWAP_09-DeviceAddress"} = $addr; +# +# $modules{SWAP}{defptr}{$addr} = $hash; +# } + + #readingsSingleUpdate($hash, "0B-RGBlevel", $arg2, 1) if( defined($attr{$name}{ProductCode}) && $attr{$name}{ProductCode} eq '0000002200000003' && $reg == 0x0B ); + + } elsif( $cmd eq "getConfig" ) { + + foreach my $reg ( sort { $a <=> $b } keys ( %default_registers ) ) { + SWAP_Send($hash, $addr, QUERY, sprintf( "%02X", $reg ) ); + } + + } elsif( $cmd eq "statusRequest" ) { + + foreach my $reg ( sort { $a <=> $b } keys ( %default_registers ) ) { + SWAP_Send($hash, $addr, QUERY, sprintf( "%02X", $reg ) ); + } + + if( defined($hash->{product}->{registers} ) ) { + foreach my $reg ( sort { $a <=> $b } keys ( %{$hash->{product}->{registers}} ) ) { + next if( $hash->{reg} && hex($hash->{reg}) != $reg ); + SWAP_Send($hash, $addr, QUERY, sprintf( "%02X", $reg ) ); + } + } + + + } elsif( $cmd eq "readDeviceXML" ) { + my $productcode = $attr{$name}{ProductCode} if( defined($attr{$name}{ProductCode} ) ); + if( defined($products->{$productcode} ) ) { + SWAP_readDeviceXML( $hash, $productcode ); + $hash->{product} = $products->{$productcode} if( defined($productcode) && defined($products->{$productcode} ) ); + } else { + return "can't read deviceXML for unknown ProductCode"; + } + } elsif( $cmd eq "clearUnconfirmed" ) { + delete( $hash->{sentList} ); + delete ($hash->{SWAP_Sent_unconfirmed}); + } else { + return SetExtensions($hash, $list, $name, @aa); + return "Unknown argument $cmd, choose one of ".$list; + } + + return undef; +} + +##################################### +sub +SWAP_Get($@) +{ + my ($hash, $name, $cmd, @args) = @_; + + return "\"get $name\" needs at least one parameter" if(@_ < 3); + + my $list = "listUnconfirmed regList deviceXML products"; + + if( my $gl = $modules{$hash->{TYPE}}{SWAP_GetList} ) { + + if(defined($gl->{$cmd}) ) { + return "get $cmd requires $gl->{$cmd} parameter(s)" if( $gl->{$cmd} != int(@args) ); + + if( my $get = $modules{$hash->{TYPE}}{SWAP_GetFn} ) { + no strict "refs"; + my $ret = &{$get}(@_); + use strict "refs"; + + return $ret; + } + } + + $list .= " " . join(" ", sort keys %{$gl}); + } + + return $list if( $cmd eq '?' ); + + if( $cmd eq 'regList' || $cmd eq 'regListAll' ) { + my $ret = ""; + + $ret .= sprintf( "reg.\t| size\t| dir.\t| name\n"); + + if( $cmd eq 'regListAll' ) { + foreach my $reg ( sort { $a <=> $b } keys ( %default_registers ) ) { + my $register = $default_registers{$reg}; + $ret .= sprintf( "%02X\t| %s\t| %s\t|%s\n", $reg, + defined($register->{size})?$register->{size}:"", + defined($register->{direction})?($register->{direction}==OUT?"set":"get"):"", + $register->{name} ); + + my $i = 0; + foreach my $endpoint ( @{$register->{endpoints}} ) { + $ret .= sprintf( " .%i\t| %s\t| %s\t| %s\n", $i, + defined($endpoint->{size})?$endpoint->{size}:"", + defined($endpoint->{direction})?($endpoint->{direction}==OUT?"set":"get"):"", + $endpoint->{name} ) if($i > 0); + ++$i; + } + } + } + + if( defined($hash->{product}->{registers} ) ) { + foreach my $reg ( sort { $a <=> $b } keys ( %{$hash->{product}->{registers}} ) ) { + next if( $hash->{reg} && hex($hash->{reg}) != $reg ); + my $register = $hash->{product}->{registers}->{$reg}; + + my $i = 0; + foreach my $endpoint ( @{$register->{endpoints}} ) { + $ret .= sprintf( "%02X\t| %s\t| %s\t|%s\n", $reg, + defined($register->{size})?$register->{size}:"", + defined($register->{direction})?($register->{direction}==OUT?"set":"get"):"", + $register->{name} ) if($i == 0 && defined($endpoint->{position})); + $ret .= sprintf( "%02X\t| %s\t| %s\t|%s\n", $reg, + $endpoint->{size}, + $endpoint->{direction}==OUT?"set":"get", + $endpoint->{name} ) if($i == 0 && !defined($endpoint->{position})); + $ret .= sprintf( " .%i\t| %s\t| %s\t| %s\n", $i, + $endpoint->{size}, + $endpoint->{direction}==OUT?"set":"get", + $endpoint->{name} ) if($i == 0 && defined($endpoint->{position})); + $ret .= sprintf( " .%i\t| %s\t| %s\t| %s\n", $i, + $endpoint->{size}, + $endpoint->{direction}==OUT?"set":"get", + $endpoint->{name} ) if($i > 0); + ++$i; + } + } + } + + return $ret; + } elsif( $cmd eq "listUnconfirmed" ) { + my $ret; + + foreach my $params (@{$hash->{sentList}}) { + $ret .= $params->[0] ."\t". $params->[1] ."\t". $params->[2] ."\t". ($params->[3]?$params->[3]:"") ."\n"; + #$ret .= $params->[0] ."\t". $function_codes{$params->[1]} ."\t". $params->[2] ."\t". ($params->[3]?$params->[3]:"") ."\n"; + } + + $ret = "no unconfirmed messages" if( !$ret ); + return $ret; + } elsif( $cmd eq "deviceXML" ) { + return Dumper $hash->{product}; + } elsif( $cmd eq "products" ) { + return Dumper $products; + } + + return "Unknown argument $cmd, choose one of ".$list; +} + +sub +SWAP_regName($$$) +{ + my ($rid, $ep, $endpoint) = @_; + + if( !defined($endpoint) ) { + return $rid if( $ep == 0 && !defined($endpoint->{position}) ); + return $rid .'.'. $ep; + } + + return $rid .'-'. $endpoint->{name} if( $ep == 0 && !defined($endpoint->{position}) ); + return $rid .'.'. $ep .'-'. $endpoint->{name}; +} +sub +SWAP_readDeviceXML($$) +{ + my ($hash, $productcode) = @_; + + my $developer = $developers->{hex(substr($productcode, 0, 8 ))}; + $hash->{Developer} = $developer->{name} if( defined($developer) ); + + my $product = $developer->{devices}->{hex(substr($productcode, 8, 8 ))}; + $hash->{Product} = $product->{label} if( defined($product->{label}) ); + + if( defined($developer->{name}) && defined($product->{name}) ) { + readDeviceXML( $products->{$productcode}, "$attr{global}{modpath}/FHEM/lib/SWAP/$developer->{name}/$product->{name}.xml" ); + } else { + Log 2, "no device xml found for productcode $productcode"; + } +} + +sub +SWAP_Fingerprint($$) +{ + my ($name, $msg) = @_; + + substr( $msg, 2, 2, "--" ); # ignore sender + substr( $msg, 4, 1, "-" ); # ignore hop count + + return ( "", $msg ); +} + + +sub +SWAP_updateReadings($$$) +{ + my($hash, $rid, $data) = @_; + + my $reg = hex($rid); + my $name = $hash->{NAME}; + + if( $hash->{reg} && hex($hash->{reg}) != $reg ) { + # ignore + } elsif( defined($hash->{product}->{registers}->{$reg}) + && defined($hash->{product}->{registers}->{$reg}->{endpoints} ) ) { + my $i = 0; + readingsBeginUpdate($hash); + foreach my $endpoint (@{$hash->{product}->{registers}->{$reg}->{endpoints}}) { + my $position = 0; + $position = $endpoint->{position} * 2 if( defined($endpoint->{position}) ); + my $value = substr($data, $position, $endpoint->{size}*2); + readingsBulkUpdate($hash, SWAP_regName($rid,$i,$endpoint), $value); + ++$i; + } + readingsBulkUpdate($hash, "state", ($data eq "000000"?"off":$data)) if( defined($attr{$name}{ProductCode}) && $attr{$name}{ProductCode} eq '0000002200000003' && $reg == 0x0B ); + readingsBulkUpdate($hash, "state", $data) if( $hash->{reg} && hex($hash->{reg}) == $reg ); + readingsEndUpdate($hash,1); + } else { + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, $rid, $data); + readingsBulkUpdate($hash, "state", $data) if( $hash->{reg} && hex($hash->{reg}) == $reg ); + readingsEndUpdate($hash,1); + } +} +sub +SWAP_Parse($$) +{ + my ($hash, $msg) = @_; + + my $dest = substr($msg, 0, 2); + my $src = substr($msg, 2, 2); + my $hop = substr($msg, 4, 1); + my $secu = substr($msg, 5, 1); + my $nonce = substr($msg, 6, 2); + my $func = substr($msg, 8, 2); + my $raddr = substr($msg, 10, 2); + my $rid = substr($msg, 12, 2); + my $data = substr($msg, 14); + + my $shash = $modules{SWAP}{defptr}{$src}; + my $dhash = $modules{SWAP}{defptr}{$dest}; + my $rhash = $modules{SWAP}{defptr}{$raddr}; + + my $sname = $shash?$shash->{NAME}:$src; + my $dname = $dest eq "00" ? "broadcast" : ($dhash?$dhash->{NAME}:$dest); + my $rname = $rhash?$rhash->{NAME}:$raddr; + + my $reg = hex($rid); + + my $regname = $rid; + #$regname = $default_registers{$reg}->{name} if( defined($default_registers{$reg}) ); + #$regname = $rhash->{product}->{registers}->{$reg}->{name} if( defined($rhash->{product}->{registers}->{$reg}) ); + + #device address changed + if( $reg == 0x09 + && $func == STATUS + && $raddr ne $data ) { + Log 4, "addr change: ". $raddr ." -> ". $data; + + if( defined( $modules{SWAP}{defptr}{$raddr} ) ) { + delete( $modules{SWAP}{defptr}{$raddr} ); + + my $addr = $data; + + $rhash->{DEF} = $addr; + $rhash->{addr} = $addr; + $rhash->{"SWAP_09-DeviceAddress"} = $addr; + + $modules{SWAP}{defptr}{$addr} = $rhash; + } + + $raddr = $data; + $rhash = $modules{SWAP}{defptr}{$raddr}; + $rname = $rhash?$rhash->{NAME}:$raddr; + } + + if( defined($rhash->{SWAP_nonce}) + && hex($nonce) == hex($rhash->{SWAP_nonce}) ) { + Log 4, "DUP: ". $sname ." -> ". $dname ." ($hop,$secu-$nonce): ". $function_codes{$func} . " ". $rname . " ". $regname . ($data?":":"") . $data; + return $rname; + } + + Log 4, $sname ." -> ". $dname ." ($hop,$secu-$nonce): ". $function_codes{$func} . " ". $rname . " ". $regname . ($data?":":"") . $data; + + return $rname if( $raddr eq "01" ); + return $rname if( $func == QUERY ); + + if( !$modules{SWAP}{defptr}{$raddr} ) { + Log 3, "SWAP Unknown device $rname, please define it"; + return undef if( $raddr eq "00" ); + return "UNDEFINED SWAP_$rname SWAP_$data $raddr $data" if( $reg == 0x00 && defined($modules{"SWAP_$data"}) ); + return "UNDEFINED SWAP_$rname SWAP $raddr $data" if( $reg == 0x00 ); + return "UNDEFINED SWAP_$rname SWAP $raddr"; + } + + $rhash->{SWAP_lastRcv} = TimeNow(); + + return $rname if( $func != STATUS ); + + #product code + if( $reg == 0x00 ) { + my $first = !defined($rhash->{"SWAP_00-ProductCode"}); + + my $productcode = $data; + SWAP_Attr( "set", $rname, "ProductCode", $productcode ) if( !defined( $attr{$rname}{ProductCode} ) ); + + if( !defined($products->{$productcode}->{registers}) ){ + SWAP_readDeviceXML( $rhash, $productcode ); + } + + $rhash->{product} = $products->{$productcode} if( defined($productcode) && defined($products->{$productcode} ) ); + + SWAP_Set( $rhash, $rname, "statusRequest" ) if( $first ); + } + + if( my $parse = $modules{$rhash->{TYPE}}{SWAP_ParseFn} ) { + no strict "refs"; + &{$parse}($rhash,$reg,$func,$data); + use strict "refs"; + } + + my @list; + push(@list, $rname); + if( $reg <= 0x0A ) { + if( defined( $default_registers{$reg}->{endpoints} ) ) { + my $i = 0; + foreach my $endpoint (@{$default_registers{$reg}->{endpoints}}) { + my $position = 0; + $position = $endpoint->{position} if( defined($endpoint->{position}) ); + my $value = substr($data, $position*2, $endpoint->{size}*2); + $rhash->{"SWAP_".SWAP_regName($rid,$i,$endpoint)} = $value; + ++$i; + } + } else { + $rhash->{"SWAP_".$rid."-".$default_registers{$reg}->{name}} = $data; + } + } else { + SWAP_updateReadings( $rhash, $rid, $data ); + + if( my $rhash = $modules{SWAP}{defptr}{$raddr.".".$rid} ) { + push(@list, $rhash->{NAME}); + + if( my $parse = $modules{$rhash->{TYPE}}{SWAP_ParseFn} ) { + no strict "refs"; + &{$parse}($rhash,$reg,$func,$data); + use strict "refs"; + } + + SWAP_updateReadings( $modules{SWAP}{defptr}{$raddr.".".$rid}, $rid, $data ); + } + } + + if( !defined($rhash->{SWAP_nonce}) + || hex($nonce) < hex($rhash->{SWAP_nonce}) ) { + delete( $rhash->{SWAP_MISSED} ); + } elsif( !defined($rhash->{SWAP_MISSED}) ) { + $rhash->{SWAP_MISSED} = 0; + } else { + $rhash->{SWAP_MISSED} += hex($nonce) - hex($rhash->{SWAP_nonce}) - 1; + } + $rhash->{SWAP_nonce} = $nonce; + + if( $reg == 0x03 + && $data eq "03" ) { + SWAP_ProcessCmdStack( $rhash ); + } + + if($rhash->{sentList}){ + my $size = scalar @{$rhash->{sentList}}; + for( my $i = $size-1; $i >= 0; --$i ) { + my $params = $rhash->{sentList}->[$i]; + if( $params->[0] eq $rhash->{addr} + && $params->[2] eq $rid ) { + splice @{$rhash->{sentList}}, $i, 1; + } + } + + $rhash->{SWAP_Sent_unconfirmed} = scalar @{$rhash->{sentList}}." Sent_unconfirmed"; + } else { + delete ($rhash->{SWAP_Sent_unconfirmed}); + } + + return @list; +} +sub +SWAP_Send($$$@) +{ + my ($hash, $dest, $func, $reg, $data) = @_; + + $hash = $modules{SWAP}{defptr}{$hash->{addr}} if( $hash->{reg} ); + + $hash->{SWAP_lastSend} = TimeNow(); + + my @arr = (); + $hash->{sentList} = \@arr if(!$hash->{sentList}); + + push(@{$hash->{sentList}}, [$dest, $func, $reg, $data]); + $hash->{SWAP_Sent_unconfirmed} = scalar @{$hash->{sentList}}." Sent_unconfirmed"; + + my $nonce = 0; + if( $func != QUERY ) { + $nonce = $hash->{IODev}->{nonce}; + $hash->{nonce}++; + $hash->{nonce} &= 0xFF; + } + + IOWrite( $hash, $dest, "00". sprintf( "%02X", $nonce) . $func . $dest . $reg ) if( !defined($data) ); + IOWrite( $hash, $dest, "00". sprintf( "%02X", $nonce) . $func . $dest . $reg . $data ) if( $data ); + + $hash->{IODev}->{nonce} = $nonce if( $func != QUERY ); +} +sub +SWAP_PushCmdStack($$$@) +{ + my ($hash, $dest, $func, $reg, $data) = @_; + #my $name = $hash->{NAME}; + + my @arr = (); + $hash->{cmdStack} = \@arr if(!$hash->{cmdStack}); + + push(@{$hash->{cmdStack}}, [$dest, $func, $reg, $data]); + $hash->{SWAP_CMDsPending} = scalar @{$hash->{cmdStack}}." CMDs_pending"; +} +sub +SWAP_ProcessCmdStack($) +{ + my ($hash) = @_; + #my $name = $hash->{NAME}; + + my $sent; + if($hash->{cmdStack}) { + if(@{$hash->{cmdStack}}) { + my $params = shift @{$hash->{cmdStack}}; + SWAP_Send($hash, $params->[0], $params->[1], $params->[2], $params->[3]); + $sent = 1; + $hash->{SWAP_CMDsPending} = scalar @{$hash->{cmdStack}}." CMDs_pending"; + } elsif(!@{$hash->{cmdStack}}) { + delete($hash->{cmdStack}); + delete($hash->{SWAP_CMDsPending}); + } + } + + return $sent; +} + + +sub +SWAP_Attr(@) +{ + my ($cmd, $name, $attrName, $attrVal) = @_; + + if( $cmd eq "set" && $attrName eq "ProductCode" && defined($attrVal) ) { + my $hash = $defs{$name}; + my $productcode = $attrVal; + + if( !defined($products->{$productcode}->{registers}) ){ + SWAP_readDeviceXML( $hash, $productcode ); + } + + $hash->{product} = $products->{$productcode} if( defined($productcode) && defined($products->{$productcode} ) ); + + if( !defined($attr{$name}{userReadings}) + && defined($hash->{product}->{registers} ) ) { + my $str; + + foreach my $reg ( sort { $a <=> $b } keys ( %{$hash->{product}->{registers}} ) ) { + next if( $hash->{reg} && hex($hash->{reg}) != $reg ); + my $register = $hash->{product}->{registers}->{$reg}; + + my $i = 0; + foreach my $endpoint ( @{$register->{endpoints}} ) { + if( $endpoint->{units} ) { + my $factor = $endpoint->{units}->[0]->{factor} if( defined($endpoint->{units}->[0]->{factor}) ); + my $offset = $endpoint->{units}->[0]->{offset} if( defined($endpoint->{units}->[0]->{offset}) ); + my $func = ""; + $func .= "*$factor" if( defined($factor) && $factor != 1 ); + $func .= "+$offset" if( defined($offset) && $offset > 0 ); + $func .= "$offset" if( defined($offset) && $offset < 0 ); + + $str .= ", " if( $str ); + my $regname = SWAP_regName(sprintf("%02X",$reg),$i,$endpoint); + $str .= lc($endpoint->{name}) .":". $regname ." ". "{hex(ReadingsVal(\$name,\"$regname\",\"0\"))$func}"; + } + ++$i; + } + } + + CommandAttr(undef, "$name userReadings $str") if( $str ); + } + + } + + return undef; +} + +1; + +=pod +=begin html + + +

SWAP

+
    + + + The SWAP protocoll is used by panStamps (panstamp.com).

    + + This is a generic module that will handle all SWAP devices with known device description files via + a panStick as the IODevice.

    + + All communication is done on the SWAP register level. FHEM readings are created for all user registers + and userReadings are created to map low level SWAP registers to 'human readable' format with the + mapping from the device descriprion files.

    + + For higher level features like "on,off,on-for-timer,..." specialized modules have to be used.

    + + Messages for devices in power-down-state are queued and send when the device enters SYNC state. + This typicaly happens during device startup after a reset. + +

    + Note: This module requires XML::Simple.
    + +
    +
    + + + Define +
      + define <name> SWAP <ID>
      +
      +
      + The ID is a 2 digit hex number to identify the moth in the panStamp network. +
    +
    + + + Set +
      +
    • regGet <reg>
      + request status message from register <reg>. +

    • + +
    • regSet <reg> <data>
      + write <data> to register <reg>. +

    • + +
    • regSet <reg>.<ep> <data>
      + write <data> to endpoint <ep> of register <reg>. will not work if no reading for register <reg> is available as all nibbles that are not part of endpoint <ep> will be filled from this reading. +

    • + +
    • statusRequest
      + request transmision of all registers. +

    • +
    • readDeviceXML
      + reload the device description xml file. +

    • +
    • clearUnconfirmed
      + clears the list of unconfirmed messages. +

    • +
    + + + Get +
      +
    • regList
      + list all non-system registers of this device. +

    • +
    • regListAll
      + list all registers of this device. +

    • +
    • listUnconfirmed
      + list all unconfirmed messages. +

    • +
    • products
      + dumps all known devices. +

    • +
    • deviceXML
      + dumps the device xml data. +

    • +
    + + + Attributes +
      +
    • ProductCode
      + ProductCode of the device. used to read the register configuration from the device definition file. + hast to be set manualy for devices that are in sleep mode during definition. +

    • +
    +
    +
+ +=end html +=cut diff --git a/fhem/FHEM/34_panStamp.pm b/fhem/FHEM/34_panStamp.pm new file mode 100755 index 000000000..537dd4038 --- /dev/null +++ b/fhem/FHEM/34_panStamp.pm @@ -0,0 +1,604 @@ + +# $Id$ + +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +sub panStamp_Attr(@); +sub panStamp_Clear($); +sub panStamp_HandleWriteQueue($); +sub panStamp_Parse($$$$); +sub panStamp_Read($); +sub panStamp_ReadAnswer($$$$); +sub panStamp_Ready($); +sub panStamp_Write($$$); + +sub panStamp_SimpleWrite(@); + +my $clientsPanStamp = ":SWAP:"; + +my %matchListSWAP = ( + "1:SWAP" => "^.*", +); + +sub +panStamp_Initialize($) +{ + my ($hash) = @_; + + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + +# Provider + $hash->{ReadFn} = "panStamp_Read"; + $hash->{WriteFn} = "panStamp_Write"; + $hash->{ReadyFn} = "panStamp_Ready"; + +# Normal devices + $hash->{DefFn} = "panStamp_Define"; + $hash->{FingerprintFn} = "panStamp_Fingerprint"; + $hash->{UndefFn} = "panStamp_Undef"; + #$hash->{GetFn} = "panStamp_Get"; + $hash->{SetFn} = "panStamp_Set"; + #$hash->{AttrFn} = "panStamp_Attr"; + $hash->{AttrList}= "model:panStamp loglevel:0,1,2,3,4,5,6"; + + $hash->{ShutdownFn} = "panStamp_Shutdown"; +} +sub +panStamp_Fingerprint($$) +{ +} + +##################################### +sub +panStamp_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + if(@a < 3 || @a > 6) { + my $msg = "wrong syntax: define panStamp {devicename[\@baudrate] ". + "| devicename\@directio} [
[ ["; + Log 2, $msg; + return $msg; + } + + my $address = $a[3]; + $address = "01" if( !defined($address) ); + my $channel = $a[4]; + $channel = "00" if( !defined($channel) ); + my $syncword = $a[5]; + $syncword = 'B547' if( !defined($syncword) ); + + return "$address is not a 1 byte hex value" if( $address !~ /^[\da-f]{2}$/i ); + return "$address is not an allowed address" if( $address eq "00" ); + return "$channel is not a 1 byte hex value" if( $channel !~ /^[\da-f]{2}$/i ); + return "$syncword is not a 2 byte hex value" if( $syncword !~ /^[\da-f]{4}$/i ); + + DevIo_CloseDev($hash); + + my $name = $a[0]; + my $dev = $a[2]; + + $hash->{address} = uc($address); + $hash->{channel} = uc($channel); + $hash->{syncword} = uc($syncword); + + $hash->{Clients} = $clientsPanStamp; + $hash->{MatchList} = \%matchListSWAP; + + $hash->{DeviceName} = $dev; + + $hash->{nonce} = 0; + + my $ret = DevIo_OpenDev($hash, 0, "panStamp_DoInit"); + return $ret; +} + +##################################### +sub +panStamp_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); + Log GetLogLevel($name,$lev), "deleting port for $d"; + delete $defs{$d}{IODev}; + } + } + + panStamp_Shutdown($hash); + DevIo_CloseDev($hash); + return undef; +} + +##################################### +sub +panStamp_Shutdown($) +{ + my ($hash) = @_; + ###panStamp_SimpleWrite($hash, "X00"); + return undef; +} + +##################################### +sub +panStamp_Set($@) +{ + my ($hash, @a) = @_; + + return "\"set panStamp\" needs at least one parameter" if(@a < 2); + + my $name = shift @a; + my $cmd = shift @a; + my $arg = join("", @a); + my $ll = GetLogLevel($name,3); + + my $list = "raw"; + return $list if( $cmd eq '?' ); + + if($cmd eq "raw") { + return "Expecting a 0-padded hex number" + if((length($arg)&1) == 1 && $cmd ne "raw"); + Log $ll, "set $name $cmd $arg"; + panStamp_SimpleWrite($hash, $arg); + + } else { + return "Unknown argument $cmd, choose one of ".$list; + } + + return undef; +} + +##################################### +sub +panStamp_Get($@) +{ + my ($hash, @a) = @_; + + #$hash->{READINGS}{$a[1]}{VAL} = $msg; + $hash->{READINGS}{$a[1]}{TIME} = TimeNow(); + + #return "$a[0] $a[1] => $msg"; +} + +sub +panStamp_Clear($) +{ + my $hash = shift; + + # Clear the pipe + $hash->{RA_Timeout} = 0.1; + for(;;) { + my ($err, undef) = panStamp_ReadAnswer($hash, "Clear", 0, undef); + last if($err && $err =~ m/^Timeout/); + } + delete($hash->{RA_Timeout}); +} + +##################################### +sub +panStamp_DoInit($) +{ + my $hash = shift; + my $name = $hash->{NAME}; + my $err; + my $msg = undef; + + my $val; + + panStamp_Clear($hash); + panStamp_ReadAnswer($hash, "ready?", 0, undef); + panStamp_SimpleWrite($hash, "+++", 1 ); + sleep 2; + panStamp_ReadAnswer($hash, "cmd mode?", 0, undef); + panStamp_SimpleWrite($hash, "ATHV?" ); + ($err, $val) = panStamp_ReadAnswer($hash, "HW Version", 0, undef); + return "$name: $err" if($err && ($err !~ m/Timeout/)); + $hash->{HWVersion} = $val; + + panStamp_SimpleWrite($hash, "ATFV?" ); + ($err, $val) = panStamp_ReadAnswer($hash, "FW Version", 0, undef); + return "$name: $err" if($err && ($err !~ m/Timeout/)); + $hash->{FWVersion} = $val; + + panStamp_SimpleWrite($hash, "ATSW=$hash->{syncword}" ); + ($err, $val) = panStamp_ReadAnswer($hash, "sync word", 0, undef); + return "$name: $err" if($err && ($err !~ m/Timeout/)); + + panStamp_SimpleWrite($hash, "ATSW?" ); + ($err, $val) = panStamp_ReadAnswer($hash, "sync word", 0, undef); + return "$name: $err" if($err && ($err !~ m/Timeout/)); + $hash->{syncword} = sprintf( "%04s", $val ); + + panStamp_SimpleWrite($hash, "ATCH=$hash->{channel}" ); + ($err, $val) = panStamp_ReadAnswer($hash, "address", 0, undef); + return "$name: $err" if($err && ($err !~ m/Timeout/)); + + panStamp_SimpleWrite($hash, "ATCH?" ); + ($err, $val) = panStamp_ReadAnswer($hash, "channel", 0, undef); + return "$name: $err" if($err && ($err !~ m/Timeout/)); + $hash->{channel} = sprintf( "%02s", $val); + + panStamp_SimpleWrite($hash, "ATDA=$hash->{address}" ); + ($err, $val) = panStamp_ReadAnswer($hash, "address", 0, undef); + return "$name: $err" if($err && ($err !~ m/Timeout/)); + + panStamp_SimpleWrite($hash, "ATDA?" ); + ($err, $val) = panStamp_ReadAnswer($hash, "address", 0, undef); + return "$name: $err" if($err && ($err !~ m/Timeout/)); + $hash->{address} = sprintf( "%02s", $val); + + panStamp_SimpleWrite($hash, "ATO" ); + panStamp_ReadAnswer($hash, "data mode?", 0, undef); + + panStamp_SimpleWrite($hash, "00010000010000" ); + + $hash->{STATE} = "Initialized"; + + # 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 +panStamp_ReadAnswer($$$$) +{ + my ($hash, $arg, $anydata, $regexp) = @_; + my $type = $hash->{TYPE}; + + 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("panStamp_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) { + Log 5, "panStamp/RAW (ReadAnswer): $buf"; + $mpandata .= $buf; + } + + chop($mpandata); + chop($mpandata); + + return (undef, $mpandata) + } + +} + +##################################### +# Check if the 1% limit is reached and trigger notifies +sub +panStamp_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) { # 163 comes from fs20. todo: verify if correct for panstamp modulation + + my $name = $hash->{NAME}; + Log GetLogLevel($name,2), "panStamp TRANSMIT LIMIT EXCEEDED"; + DoTrigger($name, "TRANSMIT LIMIT EXCEEDED"); + + } else { + + push(@b, $now); + + } + $hash->{XMIT_TIME} = \@b; + $hash->{NR_CMD_LAST_H} = int(@b); +} + +##################################### +sub +panStamp_Write($$$) +{ + my ($hash,$addr,$msg) = @_; + + Log 5, "$hash->{NAME} sending $addr $msg"; + my $bstring = $addr.$hash->{"address"}.$msg; + + panStamp_AddQueue($hash, $bstring); + #panStamp_SimpleWrite($hash, $bstring); +} + +sub +panStamp_SendFromQueue($$) +{ + my ($hash, $bstring) = @_; + my $name = $hash->{NAME}; + my $to = 0.05; + + if($bstring ne "") { + my $sp = AttrVal($name, "sendpool", undef); + if($sp) { # Is one of the panStamp-fellows sending data? + my @fellows = split(",", $sp); + foreach my $f (@fellows) { + if($f ne $name && + $defs{$f} && + $defs{$f}{QUEUE} && + $defs{$f}{QUEUE}->[0] ne "") + { + unshift(@{$hash->{QUEUE}}, ""); + InternalTimer(gettimeofday()+$to, "panStamp_HandleWriteQueue", $hash, 1); + return; + } + } + } + + panStamp_XmitLimitCheck($hash,$bstring); + panStamp_SimpleWrite($hash, $bstring); + } + + InternalTimer(gettimeofday()+$to, "panStamp_HandleWriteQueue", $hash, 1); +} + +sub +panStamp_AddQueue($$) +{ + my ($hash, $bstring) = @_; + if(!$hash->{QUEUE}) { + $hash->{QUEUE} = [ $bstring ]; + panStamp_SendFromQueue($hash, $bstring); + + } else { + push(@{$hash->{QUEUE}}, $bstring); + } +} + +##################################### +sub +panStamp_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 "") { + panStamp_HandleWriteQueue($hash); + } else { + panStamp_SendFromQueue($hash, $bstring); + } + } +} + +##################################### +# called from the global loop, when the select for hash->{FD} reports data +sub +panStamp_Read($) +{ + my ($hash) = @_; + + my $buf = DevIo_SimpleRead($hash); + return "" if(!defined($buf)); + + my $name = $hash->{NAME}; + + my $pandata = $hash->{PARTIAL}; + Log 5, "panStamp/RAW: $pandata/$buf"; + $pandata .= $buf; + + while($pandata =~ m/\n/) { + my $rmsg; + ($rmsg,$pandata) = split("\n", $pandata, 2); + $rmsg =~ s/\r//; + panStamp_Parse($hash, $hash, $name, $rmsg) if($rmsg); + } + $hash->{PARTIAL} = $pandata; +} + +sub +panStamp_Parse($$$$) +{ + my ($hash, $iohash, $name, $rmsg) = @_; + + my $dmsg = $rmsg; + my $l = length($dmsg); + my $rssi = hex(substr($dmsg, 1, 2)); + $rssi = ($rssi>=128 ? (($rssi-256)/2-74) : ($rssi/2-74)); + my $lqi = hex(substr($dmsg, 3, 2)); + $dmsg = substr($dmsg, 6, $l-6); + Log GetLogLevel($name,5), "$name: $dmsg $rssi $lqi"; + + next if(!$dmsg || length($dmsg) < 1); # Bogus messages + + $hash->{"${name}_MSGCNT"}++; + $hash->{"${name}_TIME"} = TimeNow(); + $hash->{RAWMSG} = $rmsg; + my %addvals = (RAWMSG => $rmsg); + if(defined($rssi)) { + $hash->{RSSI} = $rssi; + $addvals{RSSI} = $rssi; + } + if(defined($lqi)) { + $hash->{LQI} = $lqi; + $addvals{LQI} = $lqi; + } + Dispatch($hash, $dmsg, \%addvals); +} + + +##################################### +sub +panStamp_Ready($) +{ + my ($hash) = @_; + + return DevIo_OpenDev($hash, 1, "panStamp_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 +panStamp_SimpleWrite(@) +{ + my ($hash, $msg, $nocr) = @_; + return if(!$hash); + + my $name = $hash->{NAME}; + my $ll5 = GetLogLevel($name,5); + Log $ll5, "SW: $msg"; + + $msg .= "\r" unless($nocr); + + $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); +} + +sub +panStamp_Attr(@) +{ + my @a = @_; + + return undef; +} + +1; + +=pod +=begin html + + +

panStamp

+
    + The panStamp is a family of RF devices sold by panstamp.com. + + It is possible to attach more than one device in order to get better + reception, fhem will filter out duplicate messages.

    + + This module provides the IODevice for the SWAP modules that implement the SWAP protocoll + to communicate with the individual moths in a panStamp network.

    + + Note: currently only panSticks are know to work. The panStamp shield for a Rasperry Pi is untested. +

    + + 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. + +

    + + + Define +
      + define <name> panStamp <device> [<address> [<channel> [<syncword>]]]
      +
      + USB-connected devices:
        + <device> specifies the serial port to communicate with the panStamp. + The name of the serial-device depends on your distribution, under + linux the cdc_acm kernel module is responsible, and usually a + /dev/ttyACM0 device will be created. If your distribution does not have a + cdc_acm module, you can force usbserial to handle the panStamp by the + following command:
          modprobe usbserial vendor=0x0403 + product=0x6001
        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@38400

        + + 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.

        + +
      +
      + The address is a 2 digit hex number to identify the moth in the panStamp network. The default is 01.
      + The channel is a 2 digit hex number to define the channel. the default is 00.
      + The syncword is a 4 digit hex number to identify the panStamp network. The default is B547.

      + + Uppon initialization a broadcast message is send to the panStamp network to try to + autodetect and autocreate all listening SWAP devices (i.e. all devices not in power down mode). +
    +
    + + + Set +
      +
    • raw data
      + send raw data to the panStamp to be transmitted over the RF link. +

    • +
    + + + Get +
      +
    + + + Attributes +
      +
    +
    +
+ +=end html +=cut diff --git a/fhem/FHEM/lib/SWAP/IulianV/onewire.xml b/fhem/FHEM/lib/SWAP/IulianV/onewire.xml new file mode 100644 index 000000000..ff2c4b16f --- /dev/null +++ b/fhem/FHEM/lib/SWAP/IulianV/onewire.xml @@ -0,0 +1,63 @@ + + + IulianV + OneWire Temperature DS18B20x3 + true + + + + 2 + + + + + + + + 0 + 2 + + + + + + + + 2 + 2 + + + + + + + + 0 + 2 + + + + + + + + 2 + 2 + + + + + + + + 4 + 2 + + + + + + + + + \ No newline at end of file diff --git a/fhem/FHEM/lib/SWAP/Luka Mustafa - Musti/sfpddm.xml b/fhem/FHEM/lib/SWAP/Luka Mustafa - Musti/sfpddm.xml new file mode 100644 index 000000000..7794d3992 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/Luka Mustafa - Musti/sfpddm.xml @@ -0,0 +1,197 @@ + + + Luka Mustafa - Musti + sfpddm + false + + + + 0.0 + 0.1 + + + + + 0.1 + 0.1 + + + + + 0.2 + 0.1 + + + + + 0.3 + 0.1 + + + + + 0.4 + 0.1 + + + + + + + 0.0 + 0.1 + + + + + 0.1 + 0.1 + + + + + 0.2 + 0.2 + + + + + 0.4 + 0.1 + + + + + 0.5 + 0.1 + + + + + 0.6 + 0.1 + + + + + 0.7 + 0.1 + + + + + 1.0 + 0.1 + + + + + 1.1 + 0.1 + + + + + + + 1.0 + 2.0 + + + + + + 4.0 + 2.0 + + + + + + 7.0 + 2.0 + + + + + + 10.0 + 2.0 + + + + + + 13.0 + 2.0 + + + + + + + + 0.0 + 0.2 + + + + + 0.2 + 0.2 + + + + + 0.4 + 0.2 + + + + + 0.6 + 0.2 + + + + + 1.0 + 0.2 + + + + + + + 0.0 + 0.2 + + + + + 0.2 + 0.2 + + + + + 0.4 + 0.2 + + + + + 0.6 + 0.2 + + + + + 1.0 + 0.2 + + + + + + diff --git a/fhem/FHEM/lib/SWAP/Miom/temphumlight.xml b/fhem/FHEM/lib/SWAP/Miom/temphumlight.xml new file mode 100644 index 000000000..13ae7b597 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/Miom/temphumlight.xml @@ -0,0 +1,41 @@ + + + Miom + Temperature/Humidity and Light sensor + true + + + + 2 + + + + + + + + 0 + 2 + + + + + + + + 2 + 2 + + + + + + 4 + 1 + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/Robomotic/EmmaTI.xml b/fhem/FHEM/lib/SWAP/Robomotic/EmmaTI.xml new file mode 100644 index 000000000..e08a9a257 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/Robomotic/EmmaTI.xml @@ -0,0 +1,440 @@ + + + Robomotic + Emma Chronos + true + + + + 0 + 1 + 1 + + + 1 + 2 + 0 + [-+]?[0-9]*\.[0-9]{1} + + + 3 + 2 + ^(\d*)$ + + + + + 0 + 1 + HOUR + + + 1 + 1 + MINUTE + + + 2 + 1 + SECOND + + + 3 + 2 + 2012 + YEAR + + + 5 + 1 + 1 + MONTH + + + 6 + 1 + 1 + MDAY + + + + + 0 + 1 + HOUR + + + 1 + 1 + MINUTE + + + + + 0 + 5 + PAGE0 + ^([A-Z0-9_-]){5}$ + + + 5 + 1 + 255 + RANGE(1,255) + + + 6 + 1 + 255 + RANGE(1,255) + + + 7 + 1 + 10 + RANGE(10,255) + + + 8.7 + 0.1 + + + 8.6 + 0.1 + + + 8.5 + 0.1 + + + 8.4 + 0.1 + + + 8.3 + 0.1 + + + 9 + 1 + ^(\d*)$ + + + 10.0 + 0.2 + 1 + ^([0-3])$ + + + 10.2 + 0.6 + ^(\d*)$ + + + + + 0 + 5 + PAGE1 + ^([A-Z0-9_-]){5}$ + + + 5 + 1 + 255 + RANGE(1,255) + + + 6 + 1 + 255 + RANGE(1,255) + + + 7 + 1 + 10 + RANGE(10,255) + + + 8.7 + 0.1 + + + 8.6 + 0.1 + + + 8.5 + 0.1 + + + 8.4 + 0.1 + + + 8.3 + 0.1 + + + 9 + 1 + ^(\d*)$ + + + 10.0 + 0.2 + 1 + ^([0-3])$ + + + 10.2 + 0.6 + ^(\d*)$ + + + + + 0 + 5 + PAGE2 + ^([A-Z0-9_-]){5}$ + + + 5 + 1 + 255 + RANGE(1,255) + + + 6 + 1 + 255 + RANGE(1,255) + + + 7 + 1 + 10 + RANGE(10,255) + + + 8.7 + 0.1 + + + 8.6 + 0.1 + + + 8.5 + 0.1 + + + 8.4 + 0.1 + + + 8.3 + 0.1 + + + 9 + 1 + ^(\d*)$ + + + 10.0 + 0.2 + 1 + ^([0-3])$ + + + 10.2 + 0.6 + ^(\d*)$ + + + + + 0 + 5 + PAGE3 + ^([A-Z0-9_-]){5}$ + + + 5 + 1 + 255 + RANGE(1,255) + + + 6 + 1 + 255 + RANGE(1,255) + + + 7 + 1 + 10 + RANGE(10,255) + + + 8.7 + 0.1 + + + 8.6 + 0.1 + + + 8.5 + 0.1 + + + 8.4 + 0.1 + + + 8.3 + 0.1 + + + 9 + 1 + ^(\d*)$ + + + 10.0 + 0.2 + 1 + ^([0-3])$ + + + 10.2 + 0.6 + ^(\d*)$ + + + + + 0 + 5 + PAGE4 + ^([A-Z0-9_-]){5}$ + + + 5 + 1 + 255 + RANGE(1,255) + + + 6 + 1 + 255 + RANGE(1,255) + + + 7 + 1 + 10 + RANGE(10,255) + + + 8.7 + 0.1 + + + 8.6 + 0.1 + + + 8.5 + 0.1 + + + 8.4 + 0.1 + + + 8.3 + 0.1 + + + 9 + 1 + ^(\d*)$ + + + 10.0 + 0.2 + 1 + ^([0-3])$ + + + 10.2 + 0.6 + ^(\d*)$ + + + + + + + 0 + 2 + + + + + + + + 2 + 4 + + + + + + + + 6 + 2 + + + + + + + + 0 + 2 + + + + + + 2 + 2 + + + + + + + + 0 + 1 + + + + + 0 + 3 + + + + diff --git a/fhem/FHEM/lib/SWAP/Robomotic/EnvPIR.xml b/fhem/FHEM/lib/SWAP/Robomotic/EnvPIR.xml new file mode 100644 index 000000000..8e0ef9b2e --- /dev/null +++ b/fhem/FHEM/lib/SWAP/Robomotic/EnvPIR.xml @@ -0,0 +1,43 @@ + + + Robomotic + Temp-Humidity-PIR sensor + true + + + + 2 + + + + + + + + 0 + 2 + + + + + + + + 2 + 2 + + + + + + + + 1 + + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/Robomotic/LuminoPod.xml b/fhem/FHEM/lib/SWAP/Robomotic/LuminoPod.xml new file mode 100644 index 000000000..5a112437e --- /dev/null +++ b/fhem/FHEM/lib/SWAP/Robomotic/LuminoPod.xml @@ -0,0 +1,55 @@ + + + Robomotic + LuminoPod + true + + + + 1.0 + 1 + + + + + 0.0 + 0.2 + + + 0.2 + 0.1 + + + + + + + 2.0 + + + + + + + + 2.0 + + + + + + + 4.0 + + + + + + + 2.0 + + + + + + diff --git a/fhem/FHEM/lib/SWAP/Robomotic/Repeater.xml b/fhem/FHEM/lib/SWAP/Robomotic/Repeater.xml new file mode 100644 index 000000000..ec31ae83f --- /dev/null +++ b/fhem/FHEM/lib/SWAP/Robomotic/Repeater.xml @@ -0,0 +1,54 @@ + + + Robomotic + Repeater node + false + + + + 0 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/Robomotic/SeismoFloor.xml b/fhem/FHEM/lib/SWAP/Robomotic/SeismoFloor.xml new file mode 100644 index 000000000..b1c675b15 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/Robomotic/SeismoFloor.xml @@ -0,0 +1,130 @@ + + + Robomotic + SeismoFloor simple + false + + + + 2 + + + + + + + + 0 + 2 + + + + + + 2 + 2 + + + + + + 4 + 2 + + + + + + + + 0.7 + 0.1 + + + 0.6 + 0.1 + + + 0.5 + 0.1 + + + 0.4 + 0.1 + + + 0.3 + 0.1 + + + 0.2 + 0.1 + + + 0.1 + 0.1 + + + 0.0 + 0.1 + + + + + + + 0.0 + 0.2 + ^(\d*)$ + + + 0.2 + 0.2 + ^(\d*)$ + + + 0.4 + 0.4 + ^(\d*)$ + 4 + + + + + 0 + 1 + 2 + ^(\d*)$ + + + + + 0 + 1 + ^(\d*)$ + + + + + 0 + 1 + ^(\d*)$ + + + 1 + 1 + ^(\d*)$ + + + 2 + 1 + ^(\d*)$ + + + 3 + 1 + ^(\d*)$ + + + + diff --git a/fhem/FHEM/lib/SWAP/Robomotic/temphum.xml b/fhem/FHEM/lib/SWAP/Robomotic/temphum.xml new file mode 100644 index 000000000..b2af5ddca --- /dev/null +++ b/fhem/FHEM/lib/SWAP/Robomotic/temphum.xml @@ -0,0 +1,34 @@ + + + panStamp + Dual Temperature-Humidity sensor + true + + + + 2 + + + + + + + + 0 + 2 + + + + + + + + 2 + 2 + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/Robomotic/temppress.xml b/fhem/FHEM/lib/SWAP/Robomotic/temppress.xml new file mode 100644 index 000000000..e8d6dc22d --- /dev/null +++ b/fhem/FHEM/lib/SWAP/Robomotic/temppress.xml @@ -0,0 +1,41 @@ + + + Robomotic + Dual Temperature-Barometric pressure and motion sensor + true + + + + 2 + + + + + + + + 0 + 2 + + + + + + + + 2 + 4 + + + + + + + + + + 1 + + + + diff --git a/fhem/FHEM/lib/SWAP/devices.xml b/fhem/FHEM/lib/SWAP/devices.xml new file mode 100644 index 000000000..a7e8f40b2 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/devices.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/justme/panfirmata.xml b/fhem/FHEM/lib/SWAP/justme/panfirmata.xml new file mode 100644 index 000000000..459af4a4a --- /dev/null +++ b/fhem/FHEM/lib/SWAP/justme/panfirmata.xml @@ -0,0 +1,13 @@ + + + justme + PanStreamFirmata + false + + + + 32 + + + + diff --git a/fhem/FHEM/lib/SWAP/justme/rgbdriver.xml b/fhem/FHEM/lib/SWAP/justme/rgbdriver.xml new file mode 100644 index 000000000..a50be7840 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/justme/rgbdriver.xml @@ -0,0 +1,86 @@ + + + justme + RGB LED controller + false + + + + 5 + + + 0 + 1 + 0 + (0|1|2|3) + + + 1 + 1 + + + 2 + 3 + + + + + 1 + 0 + ([1-9]|1[0-5]) + + + + + + + 3 + + + 1 + + + 1 + 1 + + + 2 + 1 + + + + + 5 + + + 0 + 1 + + + 1 + 4 + + + + + 4 + + + + + + + + 6 + + + 0 + 1 + + + 1 + 5 + + + + diff --git a/fhem/FHEM/lib/SWAP/justme/streamtest.xml b/fhem/FHEM/lib/SWAP/justme/streamtest.xml new file mode 100644 index 000000000..24debdf17 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/justme/streamtest.xml @@ -0,0 +1,13 @@ + + + justme + PanStream Test + false + + + + 32 + + + + diff --git a/fhem/FHEM/lib/SWAP/panMillenium/panmillenium1.xml b/fhem/FHEM/lib/SWAP/panMillenium/panmillenium1.xml new file mode 100644 index 000000000..aa807ccf9 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panMillenium/panmillenium1.xml @@ -0,0 +1,1107 @@ + + + panMillenium + panmillenium1 + false + + + + 0.0 + 2.0 + + + + + 2.0 + 2.0 + + + + + 4.0 + 2.0 + + + + + 6.0 + 2.0 + + + + + 8.0 + 2.0 + + + + + 10.0 + 2.0 + + + + + 12.0 + 2.0 + + + + + 14.0 + 2.0 + + + + + + + 0.0 + 2.0 + + + + + 2.0 + 2.0 + + + + + 4.0 + 2.0 + + + + + 6.0 + 2.0 + + + + + 8.0 + 2.0 + + + + + 10.0 + 2.0 + + + + + 12.0 + 2.0 + + + + + 14.0 + 2.0 + + + + + + + 0.0 + 2.0 + + + + + 2.0 + 2.0 + + + + + 4.0 + 2.0 + + + + + 6.0 + 2.0 + + + + + 8.0 + 2.0 + + + + + 10.0 + 2.0 + + + + + 12.0 + 2.0 + + + + + 14.0 + 2.0 + + + + + + + 0.0 + 2.0 + + + + + 2.0 + 2.0 + + + + + 4.0 + 2.0 + + + + + 6.0 + 2.0 + + + + + 8.0 + 2.0 + + + + + 10.0 + 2.0 + + + + + 12.0 + 2.0 + + + + + 14.0 + 2.0 + + + + + + + 0.0 + 2.0 + + + + + 2.0 + 2.0 + + + + + 4.0 + 2.0 + + + + + 6.0 + 2.0 + + + + + 8.0 + 2.0 + + + + + 10.0 + 2.0 + + + + + 12.0 + 2.0 + + + + + 14.0 + 2.0 + + + + + + + 0.0 + 2.0 + + + + + 2.0 + 2.0 + + + + + 4.0 + 2.0 + + + + + 6.0 + 2.0 + + + + + 8.0 + 2.0 + + + + + 10.0 + 2.0 + + + + + 12.0 + 2.0 + + + + + 14.0 + 2.2 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 2.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 1.0 + + + + + + + 0.0 + 0.1 + + + + + 0.1 + 0.1 + + + + + 0.2 + 0.1 + + + + + 0.3 + 0.1 + + + + + 0.4 + 0.1 + + + + + 0.5 + 0.1 + + + + + 0.6 + 0.1 + + + + + 0.7 + 0.1 + + + + + 1.0 + 0.1 + + + + + 1.1 + 0.1 + + + + + 1.2 + 0.1 + + + + + 1.3 + 0.1 + + + + + 1.4 + 0.1 + + + + + 1.5 + 0.1 + + + + + 1.6 + 0.1 + + + + + 1.7 + 0.1 + + + + + + + 2.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 1.0 + + + + + 3.0 + 1.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 1.0 + + + + + 3.0 + 1.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 1.0 + + + + + 3.0 + 1.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 1.0 + + + + + 3.0 + 1.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 1.0 + + + + + 3.0 + 1.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 1.0 + + + + + 3.0 + 1.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 1.0 + + + + + 3.0 + 1.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 1.0 + + + + + 3.0 + 1.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 1.0 + + + + + 3.0 + 1.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 2.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 2.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 2.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 2.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 2.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 2.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 2.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 2.0 + + + + + + + 0.0 + 1.0 + + + + + 1.0 + 1.0 + + + + + 2.0 + 2.0 + + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/bininps.xml b/fhem/FHEM/lib/SWAP/panStamp/bininps.xml new file mode 100644 index 000000000..23abaf8e0 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/bininps.xml @@ -0,0 +1,85 @@ + + + panStamp + Binary/Counter input module + true + + + + 2 + + + + + + + + 1.7 + 0.1 + + + 1.6 + 0.1 + + + 1.5 + 0.1 + + + 1.4 + 0.1 + + + 1.3 + 0.1 + + + 1.2 + 0.1 + + + 1.1 + 0.1 + + + 1.0 + 0.1 + + + 0.7 + 0.1 + + + 0.6 + 0.1 + + + 0.5 + 0.1 + + + 0.4 + 0.1 + + + + + 3 + 1 + + + 2 + 1 + + + 1 + 1 + + + 0 + 1 + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/binouts.xml b/fhem/FHEM/lib/SWAP/panStamp/binouts.xml new file mode 100644 index 000000000..2638ca212 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/binouts.xml @@ -0,0 +1,61 @@ + + + panStamp + Binary/PWM output module + false + + + + 0.7 + 0.1 + + + 0.6 + 0.1 + + + 0.5 + 0.1 + + + 0.4 + 0.1 + + + 0.3 + 0.1 + + + 0.2 + 0.1 + + + 0.1 + 0.1 + + + 0.0 + 0.1 + + + + + 3 + 1 + + + 2 + 1 + + + 1 + 1 + + + 0 + 1 + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/binouts2.xml b/fhem/FHEM/lib/SWAP/panStamp/binouts2.xml new file mode 100644 index 000000000..665ec24fb --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/binouts2.xml @@ -0,0 +1,55 @@ + + + panStamp + Advanced Binary/PWM output module + repeater + false + + + + 0 + 1 + 0 + ([1-9]|1[0-5]) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/chronos.xml b/fhem/FHEM/lib/SWAP/panStamp/chronos.xml new file mode 100644 index 000000000..a73662440 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/chronos.xml @@ -0,0 +1,407 @@ + + + panStamp + SWAP-Chronos + true + + + + 0 + 1 + 1 + + + 1 + 2 + 0 + [-+]?[0-9]*\.[0-9]{1} + + + 3 + 2 + ^(\d*)$ + + + + + 0 + 1 + HOUR + + + 1 + 1 + MINUTE + + + 3 + 2 + 2011 + YEAR + + + 5 + 1 + 1 + MONTH + + + 6 + 1 + 1 + MDAY + + + + + 0 + 1 + HOUR + + + 1 + 1 + MINUTE + + + + + 0 + 5 + PAGE0 + ^([A-Z0-9_-]){5}$ + + + 5 + 1 + 255 + RANGE(1,255) + + + 6 + 1 + 255 + RANGE(1,255) + + + 7 + 1 + 10 + RANGE(10,255) + + + 8.7 + 0.1 + + + 8.6 + 0.1 + + + 8.5 + 0.1 + + + 8.4 + 0.1 + + + 8.3 + 0.1 + + + 9 + 1 + ^(\d*)$ + + + 10.0 + 0.2 + 1 + ^([0-3])$ + + + 10.2 + 0.6 + ^(\d*)$ + + + + + 0 + 5 + PAGE1 + ^([A-Z0-9_-]){5}$ + + + 5 + 1 + 255 + RANGE(1,255) + + + 6 + 1 + 255 + RANGE(1,255) + + + 7 + 1 + 10 + RANGE(10,255) + + + 8.7 + 0.1 + + + 8.6 + 0.1 + + + 8.5 + 0.1 + + + 8.4 + 0.1 + + + 8.3 + 0.1 + + + 9 + 1 + ^(\d*)$ + + + 10.0 + 0.2 + 1 + ^([0-3])$ + + + 10.2 + 0.6 + ^(\d*)$ + + + + + 0 + 5 + PAGE2 + ^([A-Z0-9_-]){5}$ + + + 5 + 1 + 255 + RANGE(1,255) + + + 6 + 1 + 255 + RANGE(1,255) + + + 7 + 1 + 10 + RANGE(10,255) + + + 8.7 + 0.1 + + + 8.6 + 0.1 + + + 8.5 + 0.1 + + + 8.4 + 0.1 + + + 8.3 + 0.1 + + + 9 + 1 + ^(\d*)$ + + + 10.0 + 0.2 + 1 + ^([0-3])$ + + + 10.2 + 0.6 + ^(\d*)$ + + + + + 0 + 5 + PAGE3 + ^([A-Z0-9_-]){5}$ + + + 5 + 1 + 255 + RANGE(1,255) + + + 6 + 1 + 255 + RANGE(1,255) + + + 7 + 1 + 10 + RANGE(10,255) + + + 8.7 + 0.1 + + + 8.6 + 0.1 + + + 8.5 + 0.1 + + + 8.4 + 0.1 + + + 8.3 + 0.1 + + + 9 + 1 + ^(\d*)$ + + + 10.0 + 0.2 + 1 + ^([0-3])$ + + + 10.2 + 0.6 + ^(\d*)$ + + + + + 0 + 5 + PAGE4 + ^([A-Z0-9_-]){5}$ + + + 5 + 1 + 255 + RANGE(1,255) + + + 6 + 1 + 255 + RANGE(1,255) + + + 7 + 1 + 10 + RANGE(10,255) + + + 8.7 + 0.1 + + + 8.6 + 0.1 + + + 8.5 + 0.1 + + + 8.4 + 0.1 + + + 8.3 + 0.1 + + + 9 + 1 + ^(\d*)$ + + + 10.0 + 0.2 + 1 + ^([0-3])$ + + + 10.2 + 0.6 + ^(\d*)$ + + + + + + + 0 + 2 + + + + + + + + 2 + 4 + + + + + + + + 6 + 2 + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/lcddriver.xml b/fhem/FHEM/lib/SWAP/panStamp/lcddriver.xml new file mode 100644 index 000000000..2bd4107c3 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/lcddriver.xml @@ -0,0 +1,42 @@ + + + panStamp + Alphanumeric 2x16 LCD driver + false + + + + 16 + + + + + 16 + + + + + 0.1 + + + + + 0.7 + 0.1 + + + 0.6 + 0.1 + + + 0.5 + 0.1 + + + 0.4 + 0.1 + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/meter.xml b/fhem/FHEM/lib/SWAP/panStamp/meter.xml new file mode 100644 index 000000000..182b00cf6 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/meter.xml @@ -0,0 +1,617 @@ + + + panStamp + Energy meter + false + + + + 0 + 2 + 1700 + ^(\d*)$ + + + 2 + 2 + 500 + ^(\d*)$ + + + 4 + 1 + 0 + ^(\d*)$ + + + 5 + 1 + 1 + + + + + 0 + 2 + 1700 + ^(\d*)$ + + + 2 + 2 + 500 + ^(\d*)$ + + + 4 + 1 + 0 + ^(\d*)$ + + + 5 + 1 + 1 + + + + + 0 + 2 + 1700 + ^(\d*)$ + + + 2 + 2 + 500 + ^(\d*)$ + + + 4 + 1 + 0 + ^(\d*)$ + + + 5 + 1 + 1 + + + + + 0 + 2 + 1700 + ^(\d*)$ + + + 2 + 2 + 500 + ^(\d*)$ + + + 4 + 1 + 0 + ^(\d*)$ + + + 5 + 1 + 1 + + + + + 0 + 2 + 1700 + ^(\d*)$ + + + 2 + 2 + 500 + ^(\d*)$ + + + 4 + 1 + 0 + ^(\d*)$ + + + 5 + 1 + 1 + + + + + 0 + 2 + 1700 + ^(\d*)$ + + + 2 + 2 + 500 + ^(\d*)$ + + + 4 + 1 + 0 + ^(\d*)$ + + + 5 + 1 + 1 + + + + + 0 + 2 + 1700 + ^(\d*)$ + + + 2 + 2 + 500 + ^(\d*)$ + + + 4 + 1 + 0 + ^(\d*)$ + + + 5 + 1 + 1 + + + + + 0 + 4 + 0 + ^(\d*)$ + + + 4 + 2 + 1 + ^(\d*)$ + + + + + 0 + 4 + 0 + ^(\d*)$ + + + 4 + 2 + 1 + ^(\d*)$ + + + + + 0 + 4 + 0 + ^(\d*)$ + + + 4 + 2 + 1 + ^(\d*)$ + + + + + + + 0 + 1 + + + + + + 1 + 2 + + + + + + 3 + 2 + + + + + + 5 + 2 + + + + + + 7 + 2 + + + + + + 9 + 1 + + + + + + 10 + 4 + + + + + + + + 0 + 1 + + + + + + 1 + 2 + + + + + + 3 + 2 + + + + + + 5 + 2 + + + + + + 7 + 2 + + + + + + 9 + 1 + + + + + + 10 + 4 + + + + + + + + 0 + 1 + + + + + + 1 + 2 + + + + + + 3 + 2 + + + + + + 5 + 2 + + + + + + 7 + 2 + + + + + + 9 + 1 + + + + + + 10 + 4 + + + + + + + + 0 + 1 + + + + + + 1 + 2 + + + + + + 3 + 2 + + + + + + 5 + 2 + + + + + + 7 + 2 + + + + + + 9 + 1 + + + + + + 10 + 4 + + + + + + + + 0 + 1 + + + + + + 1 + 2 + + + + + + 3 + 2 + + + + + + 5 + 2 + + + + + + 7 + 2 + + + + + + 9 + 1 + + + + + + 10 + 4 + + + + + + + + 0 + 1 + + + + + + 1 + 2 + + + + + + 3 + 2 + + + + + + 5 + 2 + + + + + + 7 + 2 + + + + + + 9 + 1 + + + + + + 10 + 4 + + + + + + + + 0 + 1 + + + + + + 1 + 2 + + + + + + 3 + 2 + + + + + + 5 + 2 + + + + + + 7 + 2 + + + + + + 9 + 1 + + + + + + 10 + 4 + + + + + + + + 0 + 4 + + + + + + + + + + 0 + 4 + + + + + + + + + + 0 + 4 + + + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/pulsecounter.xml b/fhem/FHEM/lib/SWAP/panStamp/pulsecounter.xml new file mode 100644 index 000000000..33c1113e9 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/pulsecounter.xml @@ -0,0 +1,23 @@ + + + panStamp + Pulse counter + true + + + + 2 + + + + + + + + 0 + 4 + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/pulsegen.xml b/fhem/FHEM/lib/SWAP/panStamp/pulsegen.xml new file mode 100644 index 000000000..94609839a --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/pulsegen.xml @@ -0,0 +1,33 @@ + + + panStamp + Programmable pulse generator + false + + + + 0 + 1 + + + + + 0 + 1 + + + + + 0 + 1 + + + + + 0 + 1 + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/respira.xml b/fhem/FHEM/lib/SWAP/panStamp/respira.xml new file mode 100644 index 000000000..cece2b216 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/respira.xml @@ -0,0 +1,50 @@ + + + panStamp + RESPIRA air quality monitor + false + + + + 2 + + + + + + + + 0 + 2 + + + + + + + + 2 + 2 + + + + + + + + 0 + 2 + + + + + + 2 + 2 + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/rgbdriver.xml b/fhem/FHEM/lib/SWAP/panStamp/rgbdriver.xml new file mode 100644 index 000000000..4c59dfcdd --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/rgbdriver.xml @@ -0,0 +1,25 @@ + + + panStamp + RGB LED controller + false + + + + 3 + + + 0 + 1 + + + 1 + 1 + + + 2 + 1 + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/soilmoisture.xml b/fhem/FHEM/lib/SWAP/panStamp/soilmoisture.xml new file mode 100644 index 000000000..a3e814a44 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/soilmoisture.xml @@ -0,0 +1,26 @@ + + + panStamp + Soil moisture sensor + true + + + + 2 + + + + + + + + 0 + 2 + + + 2 + 2 + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/temp.xml b/fhem/FHEM/lib/SWAP/panStamp/temp.xml new file mode 100644 index 000000000..3cbca2446 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/temp.xml @@ -0,0 +1,27 @@ + + + panStamp + Temperature sensor + true + + + + 2 + + + + + + + + 0 + 2 + + + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/temphum.xml b/fhem/FHEM/lib/SWAP/panStamp/temphum.xml new file mode 100644 index 000000000..6429fe7f8 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/temphum.xml @@ -0,0 +1,34 @@ + + + panStamp + Dual Temperature-Humidity sensor + true + + + + 2 + + + + + + + + 0 + 2 + + + + + + + + 2 + 2 + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/panStamp/temppress.xml b/fhem/FHEM/lib/SWAP/panStamp/temppress.xml new file mode 100644 index 000000000..75d539139 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/panStamp/temppress.xml @@ -0,0 +1,36 @@ + + + panStamp + Dual Temperature-Barometric pressure sensor + true + + + + 2 + + + + + + + + 0 + 2 + + + + + + + + 2 + 4 + + + + + + + + + diff --git a/fhem/FHEM/lib/SWAP/template.xml b/fhem/FHEM/lib/SWAP/template.xml new file mode 100644 index 000000000..23121d349 --- /dev/null +++ b/fhem/FHEM/lib/SWAP/template.xml @@ -0,0 +1,44 @@ + + + Developer_name + Product name or short description + + true/false + + + + + + + + + B.b + + B.b + + default value + + + + + + + + + + + + + B.b + + B.b + + + + + + + + + diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index edc7ddff1..3ffc03508 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -85,6 +85,8 @@ FHEM/31_HUEDevice.pm justme1968 http://forum.fhem.de Sonstige FHEM/31_LightScene.pm justme1968 http://forum.fhem.de Automatisierung FHEM/32_SYSSTAT.pm justme1968 http://forum.fhem.de Unterstützende Dienste FHEM/32_speedtest.pm justme1968 http://forum.fhem.de Sonstiges +FHEM/34_panStamp.pm justme1968 http://forum.fhem.de Sonstiges Systeme +FHEM/34_SWAP.pm justme1968 http://forum.fhem.de Sonstiges Systeme FHEM/40_RFXCOM.pm wherzig http://forum.fhem.de RFXTRX FHEM/41_OREGON.pm wherzig http://forum.fhem.de Sonstiges FHEM/42_RFXMETER.pm wherzig http://forum.fhem.de RFXTRX