From 4254c7d9528c894c9495cdec64c114cad81f2609 Mon Sep 17 00:00:00 2001 From: justme-1968 Date: Tue, 27 Aug 2013 11:08:47 +0000 Subject: [PATCH] added new modules 36_JeeLink.pm and 36_PCA301.pm and arduino sketch contrib/arduino/36_PCA301-pcaSerial.zip git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@3805 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- CHANGED | 4 + FHEM/36_JeeLink.pm | 558 ++++++++++++++++++++++++ FHEM/36_PCA301.pm | 310 +++++++++++++ MAINTAINER.txt | 2 + contrib/arduino/36_PCA301-pcaSerial.zip | Bin 0 -> 10643 bytes 5 files changed, 874 insertions(+) create mode 100644 FHEM/36_JeeLink.pm create mode 100644 FHEM/36_PCA301.pm create mode 100644 contrib/arduino/36_PCA301-pcaSerial.zip diff --git a/CHANGED b/CHANGED index 4e03241a5..ab886942e 100644 --- a/CHANGED +++ b/CHANGED @@ -1,5 +1,9 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. - SVN + - feature: new modules JeeLink and PCA301 and for the ELV PCA 301 power + meter with a JeeLabs JeeLink as RF modem. The matching JeeNode + sketch can be found in .../contrib/36_PCA301-pcaSerial.zip + (by justme1968) - change: PRESENCE: changing ping method for Windows systems to "tcp" - feature: install FHEM as Windows service by T.E., see docs/HOWTO_Windows.txt - feature: new module 33_readingsGroup to display a collection of readings diff --git a/FHEM/36_JeeLink.pm b/FHEM/36_JeeLink.pm new file mode 100644 index 000000000..7834bf980 --- /dev/null +++ b/FHEM/36_JeeLink.pm @@ -0,0 +1,558 @@ + +# $Id$ + +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +sub JeeLink_Attr(@); +sub JeeLink_Clear($); +sub JeeLink_HandleWriteQueue($); +sub JeeLink_Parse($$$$); +sub JeeLink_Read($); +sub JeeLink_ReadAnswer($$$$); +sub JeeLink_Ready($); +sub JeeLink_Write($$); + +sub JeeLink_SimpleWrite(@); + +my $clientsJeeLink = ":PCA301:EC3000:RoomNode:"; + +my %matchListPCA301 = ( + "1:PCA301" => "^\\S+\\s+24", + "2:EC3000" => "^\\S+\\s+22", + "3:RoomNode" => "^\\S+\\s+11", +); + +sub +JeeLink_Initialize($) +{ + my ($hash) = @_; + + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + +# Provider + $hash->{ReadFn} = "JeeLink_Read"; + $hash->{WriteFn} = "JeeLink_Write"; + $hash->{ReadyFn} = "JeeLink_Ready"; + +# Normal devices + $hash->{DefFn} = "JeeLink_Define"; + $hash->{FingerprintFn} = "JeeLink_Fingerprint"; + $hash->{UndefFn} = "JeeLink_Undef"; + $hash->{GetFn} = "JeeLink_Get"; + $hash->{SetFn} = "JeeLink_Set"; + #$hash->{AttrFn} = "JeeLink_Attr"; + #$hash->{AttrList}= ""; + + $hash->{ShutdownFn} = "JeeLink_Shutdown"; +} +sub +JeeLink_Fingerprint($$) +{ +} + +##################################### +sub +JeeLink_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + if(@a != 3) { + my $msg = "wrong syntax: define JeeLink {devicename[\@baudrate] ". + "| devicename\@directio}"; + Log3 undef, 2, $msg; + return $msg; + } + + DevIo_CloseDev($hash); + + my $name = $a[0]; + + my $dev = $a[2]; + $dev .= "\@57600" if( $dev !~ m/\@/ ); + + $hash->{Clients} = $clientsJeeLink; + $hash->{MatchList} = \%matchListPCA301; + + $hash->{DeviceName} = $dev; + + $hash->{nonce} = 0; + + my $ret = DevIo_OpenDev($hash, 0, "JeeLink_DoInit"); + return $ret; +} + +##################################### +sub +JeeLink_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}; + } + } + + JeeLink_Shutdown($hash); + DevIo_CloseDev($hash); + return undef; +} + +##################################### +sub +JeeLink_Shutdown($) +{ + my ($hash) = @_; + ###JeeLink_SimpleWrite($hash, "X00"); + return undef; +} + +##################################### +sub +JeeLink_Set($@) +{ + my ($hash, @a) = @_; + + my $name = shift @a; + my $cmd = shift @a; + my $arg = join("", @a); + + my $list = "raw:noArg"; + return $list if( $cmd eq '?' ); + + if($cmd eq "raw") { + return "\"set JeeLink $cmd\" needs exactly one parameter" if(@_ != 4); + return "Expecting a even length hex number" if((length($arg)&1) == 1 || $arg !~ m/^[\dA-F]{12,}$/ ); + Log3 $name, 4, "set $name $cmd $arg"; + JeeLink_SimpleWrite($hash, $arg); + + } else { + return "Unknown argument $cmd, choose one of ".$list; + } + + return undef; +} + +##################################### +sub +JeeLink_Get($@) +{ + my ($hash, $name, $cmd ) = @_; + + my $list = "devices:noArg initJeeLink:noArg"; + + if( $cmd eq "devices" ) { + JeeLink_SimpleWrite($hash, "l"); + } elsif( $cmd eq "initJeeLink" ) { + JeeLink_SimpleWrite($hash, "0c"); + JeeLink_SimpleWrite($hash, "2c"); + } else { + return "Unknown argument $cmd, choose one of ".$list; + } + + return undef; +} + +sub +JeeLink_Clear($) +{ + my $hash = shift; + + # Clear the pipe + $hash->{RA_Timeout} = 0.1; + for(;;) { + my ($err, undef) = JeeLink_ReadAnswer($hash, "Clear", 0, undef); + last if($err && $err =~ m/^Timeout/); + } + delete($hash->{RA_Timeout}); +} + +##################################### +sub +JeeLink_DoInit($) +{ + my $hash = shift; + my $name = $hash->{NAME}; + my $err; + my $msg = undef; + + my $val; + + #JeeLink_Clear($hash); + + JeeLink_SimpleWrite($hash, "1a" ); # led on + JeeLink_SimpleWrite($hash, "1q" ); # quiet mode + JeeLink_SimpleWrite($hash, "0x" ); # hex mode + JeeLink_SimpleWrite($hash, "0a" ); # led off + + JeeLink_SimpleWrite($hash, "l"); # list known devices + + $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 +JeeLink_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("JeeLink_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, "JeeLink/RAW (ReadAnswer): $buf"; + $mpandata .= $buf; + } + + chop($mpandata); + chop($mpandata); + + return (undef, $mpandata) + } + +} + +##################################### +# Check if the 1% limit is reached and trigger notifies +sub +JeeLink_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 JeeLink modulation + + my $name = $hash->{NAME}; + Log3 $name, 2, "JeeLink TRANSMIT LIMIT EXCEEDED"; + DoTrigger($name, "TRANSMIT LIMIT EXCEEDED"); + + } else { + + push(@b, $now); + + } + $hash->{XMIT_TIME} = \@b; + $hash->{NR_CMD_LAST_H} = int(@b); +} + +##################################### +sub +JeeLink_Write($$) +{ + my ($hash,$msg) = @_; + my $name = $hash->{NAME}; + + Log3 $name, 5, "$name sending $msg"; + + JeeLink_AddQueue($hash, $msg); + #JeeLink_SimpleWrite($hash, $msg); +} + +sub +JeeLink_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 JeeLink-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, "JeeLink_HandleWriteQueue", $hash, 1); + return; + } + } + } + + JeeLink_XmitLimitCheck($hash,$bstring); + JeeLink_SimpleWrite($hash, $bstring); + } + + InternalTimer(gettimeofday()+$to, "JeeLink_HandleWriteQueue", $hash, 1); +} + +sub +JeeLink_AddQueue($$) +{ + my ($hash, $bstring) = @_; + if(!$hash->{QUEUE}) { + $hash->{QUEUE} = [ $bstring ]; + JeeLink_SendFromQueue($hash, $bstring); + + } else { + push(@{$hash->{QUEUE}}, $bstring); + } +} + +##################################### +sub +JeeLink_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 "") { + JeeLink_HandleWriteQueue($hash); + } else { + JeeLink_SendFromQueue($hash, $bstring); + } + } +} + +##################################### +# called from the global loop, when the select for hash->{FD} reports data +sub +JeeLink_Read($) +{ + my ($hash) = @_; + + my $buf = DevIo_SimpleRead($hash); + return "" if(!defined($buf)); + + my $name = $hash->{NAME}; + + my $pandata = $hash->{PARTIAL}; + Log3 $name, 5, "JeeLink/RAW: $pandata/$buf"; + $pandata .= $buf; + + while($pandata =~ m/\n/) { + my $rmsg; + ($rmsg,$pandata) = split("\n", $pandata, 2); + $rmsg =~ s/\r//; + JeeLink_Parse($hash, $hash, $name, $rmsg) if($rmsg); + } + $hash->{PARTIAL} = $pandata; +} + +sub +JeeLink_Parse($$$$) +{ + my ($hash, $iohash, $name, $rmsg) = @_; + + my $dmsg = $rmsg; + #my $l = length($dmsg); + my $rssi; + #my $rssi = hex(substr($dmsg, 1, 2)); + #$rssi = ($rssi>=128 ? (($rssi-256)/2-74) : ($rssi/2-74)); + my $lqi; + #my $lqi = hex(substr($dmsg, 3, 2)); + #$dmsg = substr($dmsg, 6, $l-6); + #Log3, $name, 5, "$name: $dmsg $rssi $lqi"; + + next if(!$dmsg || length($dmsg) < 1); # Bogus messages + next if($dmsg =~ m/^-> ack/ ); # ignore send ack + + $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; + } + + if( $rmsg =~ m/(\S* )(\d+)(.*)/ ) { + my $node = $2 & 0x1F; #mask HDR -> it is handled by the skech + $dmsg = $1.$node.$3; + } + + Dispatch($hash, $dmsg, \%addvals); +} + + +##################################### +sub +JeeLink_Ready($) +{ + my ($hash) = @_; + + return DevIo_OpenDev($hash, 1, "JeeLink_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 +JeeLink_SimpleWrite(@) +{ + my ($hash, $msg, $nocr) = @_; + return if(!$hash); + + my $name = $hash->{NAME}; + Log3 $name, 5, "SW: $msg"; + + $msg .= "\n" 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 +JeeLink_Attr(@) +{ + my @a = @_; + + return undef; +} + +1; + +=pod +=begin html + + +

JeeLink

+ + +=end html +=cut diff --git a/FHEM/36_PCA301.pm b/FHEM/36_PCA301.pm new file mode 100644 index 000000000..56f26219a --- /dev/null +++ b/FHEM/36_PCA301.pm @@ -0,0 +1,310 @@ + +# $Id: 34_PCA301.pm 3515 2013-07-28 09:00:56Z justme1968 $ +# +# TODO: + +package main; + +use strict; +use warnings; +use SetExtensions; + +sub PCA301_Parse($$); +sub PCA301_Send($$@); + +sub +PCA301_Initialize($) +{ + my ($hash) = @_; + + $hash->{Match} = "^\\S+\\s+24"; + $hash->{SetFn} = "PCA301_Set"; + #$hash->{GetFn} = "PCA301_Get"; + $hash->{DefFn} = "PCA301_Define"; + $hash->{UndefFn} = "PCA301_Undef"; + $hash->{FingerprintFn} = "PCA301_Fingerprint"; + $hash->{ParseFn} = "PCA301_Parse"; + $hash->{AttrFn} = "PCA301_Attr"; + $hash->{AttrList} = "IODev". + " $readingFnAttributes"; +} + +sub +PCA301_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + if(@a != 4 ) { + my $msg = "wrong syntax: define PCA301 "; + Log3 undef, 2, $msg; + return $msg; + } + + $a[2] =~ m/^([\da-f]{6})$/i; + return "$a[2] is not a valid PCA301 address" if( !defined($1) ); + + $a[3] =~ m/^([\da-f]{2})$/i; + return "$a[3] is not a valid PCA301 channel" if( !defined($1) ); + + my $name = $a[0]; + my $addr = $a[2]; + my $channel = $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( $addr eq "00" ); + + return "PCA301 device $addr already used for $modules{PCA301}{defptr}{$addr}->{NAME}." if( $modules{PCA301}{defptr}{$addr} + && $modules{PCA301}{defptr}{$addr}->{NAME} ne $name ); + + $hash->{addr} = $addr; + $hash->{channel} = $channel; + + $modules{PCA301}{defptr}{$addr} = $hash; + + AssignIoPort($hash); + if(defined($hash->{IODev}->{NAME})) { + Log3 $name, 3, "$name: I/O device is " . $hash->{IODev}->{NAME}; + } else { + Log3 $name, 1, "$name: no I/O device"; + } + + $attr{$name}{devStateIcon} = 'on:on:toggle off:off:toggle .*:light_question:off' if( !defined( $attr{$name}{devStateIcon} ) ); + $attr{$name}{webCmd} = 'on:off:toggle:statusRequest' if( !defined( $attr{$name}{webCmd} ) ); + CommandAttr( undef, "$name userReadings consumptionTotal:consumption monotonic {ReadingsVal(\$name,'consumption',0)}" ) if( !defined( $attr{$name}{userReadings} ) ); + + #PCA301_Send($hash, $addr, "00" ); + + return undef; +} + +##################################### +sub +PCA301_Undef($$) +{ + my ($hash, $arg) = @_; + my $name = $hash->{NAME}; + my $addr = $hash->{addr}; + + delete( $modules{PCA301}{defptr}{$addr} ); + + return undef; +} + +##################################### +sub +PCA301_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 $list = "identify:noArg off:noArg on:noArg toggle:noArg reset:noArg statusRequest:noArg"; + + if( $cmd eq 'toggle' ) { + $cmd = ReadingsVal($name,"state","on") eq "off" ? "on" :"off"; + } + + if( $cmd eq 'off' ) { + readingsSingleUpdate($hash, "state", "set-$cmd", 1); + PCA301_Send( $hash, 0x05, 0x00 ); + } elsif( $cmd eq 'on' ) { + readingsSingleUpdate($hash, "state", "set-$cmd", 1); + PCA301_Send( $hash, 0x05, 0x01 ); + } elsif( $cmd eq 'statusRequest' ) { + readingsSingleUpdate($hash, "state", "set-$cmd", 1); + PCA301_Send( $hash, 0x04, 0x00 ); + } elsif( $cmd eq 'reset' ) { + readingsSingleUpdate($hash, "state", "set-$cmd", 1); + PCA301_Send( $hash, 0x04, 0x01 ); + } elsif( $cmd eq 'identify' ) { + PCA301_Send( $hash, 0x06, 0x00 ); + } else { + return SetExtensions($hash, $list, $name, @aa); + } + + return undef; +} + +##################################### +sub +PCA301_Get($@) +{ + my ($hash, $name, $cmd, @args) = @_; + + return "\"get $name\" needs at least one parameter" if(@_ < 3); + + my $list = ""; + + return "Unknown argument $cmd, choose one of $list"; +} + +sub +PCA301_Fingerprint($$) +{ + my ($name, $msg) = @_; + + return ( "", $msg ); +} + + +sub +PCA301_Parse($$) +{ + my ($hash, $msg) = @_; + my $name = $hash->{NAME}; + + #return undef if( $msg !~ m/^[\dA-F]{12,}$/ ); + + if( $msg =~ m/^L/ ) { + my @parts = split( ' ', substr($msg, 5), 4 ); + $msg = "OK 24 $parts[3]"; + } + + my( @bytes, $channel,$cmd,$addr,$data,$power,$consumption ); + if( $msg =~ m/^OK/ ) { + @bytes = split( ' ', substr($msg, 6) ); + + $channel = sprintf( "%02X", $bytes[0] ); + $cmd = $bytes[1]; + $addr = sprintf( "%02X%02X%02X", $bytes[2], $bytes[3], $bytes[4] ); + $data = $bytes[5]; + return "" if( $cmd == 0x04 && $bytes[6] == 170 && $bytes[7] == 170 && $bytes[8] == 170 && $bytes[9] == 170 ); # ignore commands from display unit + return "" if( $cmd == 0x05 && ( $bytes[6] != 170 || $bytes[7] != 170 || $bytes[8] != 170 || $bytes[9] != 170 ) ); # ignore commands not from the plug + } elsif ( $msg =~ m/^TX/ ) { + # ignore TX + return ""; + } else { + DoTrigger($name, "UNKNOWNCODE $msg"); + Log3 $name, 3, "$name: Unknown code $msg, help me!"; + return undef; + } + + my $raddr = $addr; + my $rhash = $modules{PCA301}{defptr}{$raddr}; + my $rname = $rhash?$rhash->{NAME}:$raddr; + + if( !$modules{PCA301}{defptr}{$raddr} ) { + Log3 $name, 3, "PCA301 Unknown device $rname, please define it"; + + return "UNDEFINED PCA301_$rname PCA301 $raddr $channel"; + } + + #CommandAttr( undef, "$rname userReadings consumptionTotal:consumption monotonic {ReadingsVal($rname,'consumption',0)}" ) if( !defined( $attr{$rname}{userReadings} ) ); + + my @list; + push(@list, $rname); + + $rhash->{PCA301_lastRcv} = TimeNow(); + + if( $cmd eq 0x04 ) { + my $state = $data==0x00?"off":"on"; + my $power = ($bytes[6]*256 + $bytes[7]) / 10.0; + my $consumption = ($bytes[8]*256 + $bytes[9]) / 100.0; + readingsBeginUpdate($rhash); + readingsBulkUpdate($rhash, "power", $power) if( $data != 0x00 ); + readingsBulkUpdate($rhash, "consumption", $consumption) if( $data != 0x00 ); + readingsBulkUpdate($rhash, "state", $state) if( Value($rname) ne $state ); + readingsEndUpdate($rhash,1); + } elsif( $cmd eq 0x05 ) { + my $state = $data==00?"off":"on"; + + readingsSingleUpdate($rhash, "state", $state, 1) + } + + return @list; +} +sub +PCA301_Send($$@) +{ + my ($hash, $cmd, $data) = @_; + + $hash->{PCA301_lastSend} = TimeNow(); + + my $msg = sprintf( "%i,%i,%i,%i,%i,%i,255,255,255,255s", hex($hash->{channel}), + $cmd, + hex(substr($hash->{addr},0,2)), hex(substr($hash->{addr},2,2)), hex(substr($hash->{addr},4,2)), + $data ); + + IOWrite( $hash, $msg ); +} + +sub +PCA301_Attr(@) +{ + my ($cmd, $name, $attrName, $attrVal) = @_; + + return undef; +} + +1; + +=pod +=begin html + + +

PCA301

+
    + + + The PCA301 is a RF controlled AC mains plug with integrated power meter functionality from ELV.

    + + It can be integrated in to FHEM via a JeeLink as the IODevice.

    + + The JeeNode sketch required for this module can be found in .../contrib/36_PCA301-pcaSerial.zip.

    + + + Define +
      + define <name> PCA301 <addr> <channel>
      +
      + addr is a 6 digit hex number to identify the PCA301 device. + channel is a 2 digit hex number to identify the PCA301 device.

      + Note: devices are autocreated on reception of the first message.
      +
    +
    + + + Set +
      +
    • on
    • +
    • off
    • +
    • identify
      + Blink the status led for ~5 seconds.
    • +
    • reset
      + Reset consumption counters
    • +
    • statusRequest
      + Request device status update.
    • +
    • set extensions are supported.
    • +

    + + + Get +
      +

    + + + Readings +
      +
    • power
    • +
    • consumption
    • +
    • consumptionTotal
      + will be created as a default user reading to have a continous consumption value that is not influenced + by the regualar reset or overflow of the normal consumption reading
    • +

    + + + Attributes +
      +

    +
+ +=end html +=cut diff --git a/MAINTAINER.txt b/MAINTAINER.txt index 4661775db..6fa20b9d0 100644 --- a/MAINTAINER.txt +++ b/MAINTAINER.txt @@ -89,6 +89,8 @@ 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/35_SWAP_0000002200000003.pm justme1968 http://forum.fhem.de Sonstiges Systeme +FHEM/36_JeeLink.pm justme1968 http://forum.fhem.de Sonstiges Systeme +FHEM/36_PCA301.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 diff --git a/contrib/arduino/36_PCA301-pcaSerial.zip b/contrib/arduino/36_PCA301-pcaSerial.zip new file mode 100644 index 0000000000000000000000000000000000000000..4f8ac658f5d8f6068d5c20f04e821b165082a952 GIT binary patch literal 10643 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