mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 12:49:34 +00:00
b7ffd63c1b
git-svn-id: https://svn.fhem.de/fhem/trunk@20792 2b470e98-0d58-463d-a4d8-8e2adae1ed80
1506 lines
53 KiB
Perl
Executable File
1506 lines
53 KiB
Perl
Executable File
#################################################################################
|
|
# 45_Plugwise.pm
|
|
#
|
|
# FHEM Module for Plugwise
|
|
#
|
|
# Copyright (C) 2014 Stefan Guttmann
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# Info on protocol:
|
|
# http://roheve.wordpress.com/2011/05/15/plugwise-protocol-analysis-part-3/
|
|
# http://www.domoticaforum.eu/viewtopic.php?f=39&t=4319&start=30
|
|
#
|
|
# The GNU General Public License may also be found at http://www.gnu.org/licenses/gpl-2.0.html .
|
|
#
|
|
# define myPlugwise Plugwise /dev/ttyPlugwise
|
|
#
|
|
#
|
|
###########################
|
|
# # $Id$
|
|
package main;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Time::HiRes qw(gettimeofday);
|
|
use Digest::CRC qw(crc);
|
|
#use Math::Round;
|
|
use Data::Dumper;
|
|
|
|
my $Make2Channels = 1;
|
|
my $testcount=-1;
|
|
my $firstrun=0;
|
|
my $device=0;
|
|
my $data="";
|
|
my $toggle=0;
|
|
my $status="";
|
|
my ($self)=();
|
|
my $lastcircle="";
|
|
my $buffer="";
|
|
my $LastTime=time;
|
|
my $lastSync=-1;
|
|
my @buffer=();
|
|
my $initdone=0;
|
|
my %PW_gets = (
|
|
"features" => 'Z'
|
|
);
|
|
my %PW_sets = (
|
|
"Scan_Circles" => " ",
|
|
"reOpen" => " ",
|
|
"syncTime" => " ",
|
|
# "pwPairForSec" => ""
|
|
);
|
|
my %PWType = (
|
|
"00" => "PW_Circle",
|
|
"01" => "PW_Circle",
|
|
"02" => "PW_Circle",
|
|
"03" => "PW_Switch",
|
|
"04" => "PW_Switch",
|
|
"05" => "PW_Sense",
|
|
"06" => "PW_Scan"
|
|
);
|
|
sub PW_Read($);
|
|
sub PW_Ready($);
|
|
sub PW_Undef($$);
|
|
sub PW_Write;
|
|
sub PW_Parse($$$$);
|
|
sub PW_DoInit($);
|
|
sub Plugwise_Initialize($$)
|
|
{
|
|
my ($hash) = @_;
|
|
$self = bless {
|
|
_buf => '',
|
|
baud => 115200,
|
|
device => '',
|
|
_awaiting_stick_response => 0
|
|
};
|
|
|
|
require "$attr{global}{modpath}/FHEM/DevIo.pm";
|
|
|
|
# Provider
|
|
$hash->{Clients} = ":PW_Circle:PW_Scan:PW_Switch:PW_Sense:";
|
|
my %mc = (
|
|
"1:PW_Circle" => "^PW_Circle",
|
|
"2:PW_Scan" => "^PW_Scan",
|
|
"3:PW_Switch" => "^PW_Switch",
|
|
"4:PW_Sense" => "^PW_Sense"
|
|
);
|
|
$hash->{MatchList} = \%mc;
|
|
# Normal devices
|
|
$hash->{ReadFn} = "PW_Read";
|
|
$hash->{WriteFn} = "PW_Write";
|
|
$hash->{ReadyFn} = "PW_Ready";
|
|
$hash->{DefFn} = "PW_Define";
|
|
$hash->{UndefFn} = "PW_Undef";
|
|
$hash->{SetFn} = "PW_Set";
|
|
$hash->{GetFn} = "PW_Get";
|
|
$hash->{StateFn} = "PW_SetState";
|
|
$hash->{AttrList}= "do_not_notify:1,0 interval circlecount WattFormat showCom autosync ".
|
|
$readingFnAttributes;;
|
|
$hash->{ShutdownFn} = "PW_Shutdown";
|
|
}
|
|
|
|
#####################################
|
|
sub PW_Define($$)
|
|
{
|
|
my ($hash, $def) = @_;
|
|
my @a = split("[ \t][ \t]*", $def);
|
|
my $inter = 10;
|
|
if (@a != 3 ) {
|
|
my $msg = "wrong syntax: define <name> Plugwise devicename";
|
|
Log3 $hash, 2, $msg;
|
|
return $msg;
|
|
}
|
|
my $name = $a[0];
|
|
my $dev = $a[2];
|
|
$hash->{NAME}=$name;
|
|
|
|
DevIo_CloseDev($hash);
|
|
$firstrun=0;
|
|
$hash->{DeviceName} = $dev;
|
|
|
|
# if( $init_done ) {
|
|
# $attr{$name}{room}="Plugwise";
|
|
# $attr{$name}{interval}=10;
|
|
# $attr{$name}{circlecount}=50;
|
|
# $attr{$name}{WattFormat}="%0.f";
|
|
# }
|
|
my $ret = DevIo_OpenDev( $hash, 0, undef);
|
|
InternalTimer(gettimeofday()+5, "PW_GetUpdate", $hash, 0);
|
|
return undef;
|
|
}
|
|
|
|
sub PW_GetUpdate($)
|
|
{
|
|
my $hash = shift;
|
|
my $name = $hash->{NAME};
|
|
my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat, $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time);
|
|
my $n=0;
|
|
if ($firstrun==2) {return undef};
|
|
InternalTimer(gettimeofday()+$attr{$name}{interval}, "PW_GetUpdate", $hash, 1);
|
|
# Log 3,Dumper($hash->{helper});
|
|
if ($firstrun==0) {
|
|
PW_DoInit($hash);
|
|
return undef;
|
|
}
|
|
delete $self->{_waiting};
|
|
|
|
foreach ( keys %{ $hash->{helper}->{circles} } ) {
|
|
$n=$_;
|
|
if (defined $hash->{helper}->{circles}->{$n}->{lastContact}) {
|
|
if (time>$hash->{helper}->{circles}->{$n}->{lastContact} +960) {
|
|
Log3 $hash,3,"Set Circle $n offline";
|
|
my %xplmsg = ( schema => 'plugwise.basic', );
|
|
my $saddr=$hash->{helper}->{circles}->{$n}->{name};
|
|
$xplmsg{dest}=$self->{_plugwise}->{circles}->{$saddr}->{type};
|
|
$xplmsg{type} = 'output';
|
|
$xplmsg{text}= 'offline';
|
|
$xplmsg{short} = $saddr;
|
|
PW_Parse($hash, $hash, $hash->{NAME}, \%xplmsg);
|
|
delete $hash->{helper}->{circles}->{$n};
|
|
} elsif (time > $hash->{helper}->{circles}->{$n}->{lastContact} +900) {
|
|
command($hash,'history',$hash->{helper}->{circles}->{$n}->{name},4);
|
|
Log3 $hash,4,"GetLog offline Circle $n";
|
|
}
|
|
}
|
|
}
|
|
foreach ( keys %{ $self->{_plugwise}->{circles} } ) {
|
|
$n=$_;
|
|
if (!defined $self->{_plugwise}->{circles}->{$n}->{type}) {$self->{_plugwise}->{circles}->{$n}->{type}=""};
|
|
if ($self->{_plugwise}->{circles}->{$n}->{type} eq "" ||
|
|
!defined $self->{_plugwise}->{circles}->{$n}->{type}) {
|
|
command($hash,'status',$n);
|
|
}
|
|
if (defined $attr{$name}{autosync}) {
|
|
if ($attr{$name}{autosync}>0 && time > $lastSync+$attr{$name}{autosync}) {
|
|
$lastSync=time;
|
|
command($hash,'syncTime',$n);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sub MyRead($$)
|
|
{
|
|
my ($hash, $msg) = @_;
|
|
return if (!defined $msg->{dest} );
|
|
PW_Parse($hash, $hash, $hash->{NAME}, $msg) if defined $msg;
|
|
}
|
|
|
|
sub PW_Write
|
|
{
|
|
my ($hash,$reciever,$fn,$a) = @_;
|
|
my $name = $hash->{NAME};
|
|
return if(!defined($fn));
|
|
my $msg = "$hash->{NAME} sending $fn";
|
|
$msg .= " to Adress $a" if defined $a;
|
|
if ($fn =~ /(on|off)/) {
|
|
command($hash,$fn,$reciever,$a);
|
|
}
|
|
elsif ($fn =~ /(syncTime|ping|removeNode|livepower|status)/) {
|
|
command($hash,$fn,$reciever)
|
|
}
|
|
elsif ($fn eq "getLog") {
|
|
command($hash,'history',$reciever,$a) if($a ne -1);
|
|
}
|
|
else
|
|
{ Log3 $hash,4,$msg;
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
|
|
#####################################
|
|
sub PW_Undef($$)
|
|
{
|
|
my ($hash, $arg) = @_;
|
|
$firstrun=2;
|
|
DevIo_CloseDev($hash);
|
|
return undef;
|
|
}
|
|
|
|
#####################################
|
|
sub PW_Shutdown($)
|
|
{
|
|
my ($hash) = @_;
|
|
DevIo_CloseDev($hash);
|
|
return undef;
|
|
}
|
|
|
|
#NeedsEdit
|
|
#####################################
|
|
sub PW_Set($@)
|
|
{
|
|
my ($hash, @a) = @_;
|
|
|
|
my $msg;
|
|
my $name=$a[0]; my $reading= $a[1];
|
|
my $n=1;
|
|
return "\"set X\" needs at least one argument" if ( @a < 2 );
|
|
if ($reading eq "myTest") {
|
|
$testcount=0;
|
|
} elsif ($reading eq "myTestOff") {
|
|
$testcount=-1;
|
|
}
|
|
|
|
elsif(!$PW_sets{$reading}) {
|
|
my @cList = keys %PW_sets;
|
|
return "Unknown argument $reading, choose one of " . join(" ", @cList);
|
|
}
|
|
if ($reading eq "Scan_Circles") {
|
|
PW_DoInit($hash);
|
|
#query_connected_circles($hash);
|
|
}
|
|
elsif ($reading eq "syncTime") {
|
|
foreach ( keys %{ $self->{_plugwise}->{circles} } ) {
|
|
$n=$_;
|
|
command($hash,'syncTime',$n);
|
|
}
|
|
}
|
|
elsif ($reading eq "reOpen") {
|
|
DevIo_CloseDev($hash);
|
|
$hash->{ERRCNT} = 0;
|
|
$firstrun=0;
|
|
my $ret = DevIo_OpenDev( $hash, 0, undef);
|
|
RemoveInternalTimer($hash);
|
|
InternalTimer(gettimeofday()+2, "PW_GetUpdate", $hash, 0);
|
|
return undef;
|
|
}
|
|
elsif ($reading eq "pwPairForSec") {
|
|
|
|
# RemoveInternalTimer("pairTimer");
|
|
# InternalTimer(gettimeofday()+$value, "PW_Circle_OnOffTimer", 'pairTimer'), 1);
|
|
|
|
Mywrite( $hash, "000701" . _addr_s2l( $self->{_plugwise}->{coordinator_MAC} ) ,1 );
|
|
}
|
|
}
|
|
|
|
sub PW_Get($@)
|
|
{
|
|
my ( $hash, @a ) = @_;
|
|
my $n=1;
|
|
return "\"get X\" needs at least one argument" if ( @a < 2 );
|
|
my $name = shift @a;
|
|
my $opt = shift @a;
|
|
if(!$PW_gets{$opt}) {
|
|
my @cList = keys %PW_gets;
|
|
return "Unknown argument $opt, choose one of " . join(" ", @cList);
|
|
}
|
|
if($opt eq 'features') {
|
|
foreach ( keys %{ $self->{_plugwise}->{circles} } ) {
|
|
$n=$_;
|
|
command($hash,'feature',$n);
|
|
}
|
|
return undef;
|
|
}
|
|
}
|
|
#NeedsEdit
|
|
#####################################
|
|
sub PW_SetState($$$$)
|
|
{
|
|
my ($hash, $tim, $vt, $val) = @_;
|
|
return undef;
|
|
}
|
|
|
|
#####################################
|
|
sub PW_DoInit($)
|
|
{
|
|
my $hash = shift;
|
|
Mywrite($hash,"000A",1);
|
|
return undef;
|
|
}
|
|
|
|
#####################################
|
|
# called from the global loop, when the select for hash->{FD} reports data
|
|
sub PW_Read($)
|
|
{
|
|
my ($hash) = @_;
|
|
my $name = $hash->{NAME};
|
|
my $char;
|
|
my $body;
|
|
|
|
# read from serial device
|
|
my $buf = DevIo_SimpleRead($hash);
|
|
my $buf2= $buf;
|
|
$buf2=~s/\r\n/\|/g;
|
|
return "" if ( !defined($buf) );
|
|
$hash->{helper}{buffer} .= $buf;
|
|
return unless ( $hash->{helper}{buffer} =~ s/(.+)\r\n// );
|
|
do {
|
|
my $v=$1;
|
|
if ($v=~/\x83?\x05\x05\x03\x03(\w+)/) {
|
|
$body = process_response($hash,$1);
|
|
if (length $body) {
|
|
# if ($body ne undef) {
|
|
my $str2=AttrVal($hash->{NAME},"showCom","xyz");
|
|
my $showcom=qr/$str2/;
|
|
if ($body->{showCom} =~ $showcom) {readingsSingleUpdate($hash,"communication", $body->{showCom},1)}
|
|
if ($body->{text} eq "Found circle") {
|
|
if ($body->{device} ne "FFFFFFFFFFFFFFFF") {
|
|
PW_Write($hash,$body->{short},"getLog",1);
|
|
|
|
command($hash,'status',$body->{short});
|
|
command($hash,'livepower',$body->{short});
|
|
} else {$body->{type}=""}
|
|
}
|
|
if ($body->{text} eq "Connected") {$hash->{"STATE"}="Connected";}
|
|
if ($body->{type} =~ /output|power|sense|humtemp|energy|ping/) {
|
|
if ($body->{dest} eq "PW_Switch" && $Make2Channels==1) {
|
|
my $dest=$body->{short};
|
|
$body->{short}=$dest . "_Ch".$body->{val3};
|
|
MyRead($hash,$body);
|
|
} else {
|
|
MyRead($hash,$body);
|
|
}
|
|
}
|
|
if ($body->{dest} eq "none"
|
|
&& $body->{type} eq "stat"
|
|
&& $body->{text} ne "ack") {
|
|
readingsSingleUpdate($hash,"LastMsg", $body->{text}." - Device: ".$body->{code},1) ;
|
|
}
|
|
if ($body->{type} eq "err3") {Mywrite($hash, "0026" . $body->{device} ,1);}
|
|
if ($body->{type} eq "err") {readingsSingleUpdate($hash,"LastMsg", $body->{text}." - Device: ".$body->{code},1) ;;
|
|
my $tm=(time - $LastTime);
|
|
if ($tm>600) {$hash->{"ERRCNT"}=0};
|
|
$LastTime=time;
|
|
$hash->{"ERRCNT"}++;
|
|
Log 3,Dumper($body);
|
|
if ($hash->{"ERRCNT"} == 50) {
|
|
Log3 $hash,3,"$name - Too many Errors, giving up. Use 'get $name reOpen' after correcting the Problem";
|
|
DevIo_CloseDev($hash);
|
|
$firstrun=2;
|
|
DevIo_CloseDev($hash);
|
|
Log 3,Dumper($hash->{helper}{buffer});
|
|
Log 3,"$name Disconnected...........................................";
|
|
$hash->{"STATE"}="Disconnected";
|
|
@buffer=();
|
|
return undef;
|
|
}
|
|
}
|
|
# }
|
|
}
|
|
} elsif ($v=~/\x23.*/) {}
|
|
elsif ($v=~/[0-9A-F]{16}/){}
|
|
else
|
|
{
|
|
Log3 $hash,3,"Not processed: $v";
|
|
}
|
|
|
|
} while ( $hash->{helper}{buffer} =~ s/(.+)\r\n// );
|
|
$self->{_awaiting_stick_response} = 0;
|
|
if (@buffer) {real_write($hash)};
|
|
return $body;
|
|
}
|
|
|
|
sub PW_Parse($$$$)
|
|
{
|
|
my ($hash, $iohash, $name, $rmsg) = @_;
|
|
|
|
my %addvals;
|
|
|
|
Log3 $hash, 5, "PW_Parse() ".Dumper($rmsg);
|
|
$hash->{"MSGCNT"}++;
|
|
$hash->{"TIME"} = TimeNow();
|
|
$hash->{RAWMSG} = $rmsg;
|
|
%addvals = (RAWMSG => $rmsg);
|
|
Dispatch($iohash, $rmsg->{'dest'}, \%addvals);
|
|
|
|
}
|
|
|
|
#NeedEdit
|
|
#####################################
|
|
sub PW_Ready($)
|
|
{
|
|
my ($hash) = @_;
|
|
return undef;
|
|
return DevIo_OpenDev($hash, 1, "PW_Ready") if($hash->{STATE} eq "disconnected");
|
|
|
|
# This is relevant for windows/USB only
|
|
my $po = $hash->{USBDev};
|
|
my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
|
|
return ($InBytes>0);
|
|
}
|
|
|
|
############# Plugwise-Tools
|
|
|
|
sub Mywrite {
|
|
my ( $hash, $cmd, $cb, $pri ) = @_;
|
|
my $packet = "\05\05\03\03" . $cmd . _plugwise_crc($cmd) . "\r\n"; # "\05\05\03\03" .
|
|
real_write($hash,$packet,$pri);
|
|
}
|
|
|
|
sub _plugwise_crc {
|
|
my ( $data ) = @_;
|
|
sprintf( "%04X", crc( $data, 16, 0, 0, 0, 0x1021, 0, 0 ) );
|
|
}
|
|
|
|
sub real_write {
|
|
my ( $hash, $str, $pri ) = @_;
|
|
if (!$pri) {$pri=0;}
|
|
if (defined $str) {
|
|
if ($pri==0) {
|
|
push @buffer,$str;
|
|
} else {
|
|
unshift @buffer,$str;
|
|
}
|
|
}
|
|
if ($self->{_awaiting_stick_response} == 1) {
|
|
return undef;
|
|
}
|
|
$str=shift @buffer;
|
|
DevIo_SimpleWrite($hash,$str,undef);
|
|
$str=~s/\r\n//;
|
|
$self->{_awaiting_stick_response} = 1;
|
|
my $str2=AttrVal($hash->{NAME},"showCom","xyz");
|
|
my $showcom=qr/$str2/;
|
|
$str=~s/....([[:xdigit:]]{4})([[:xdigit:]]{4})(.*)/$1 $2 $3/g;
|
|
if ($str =~ $showcom) {readingsSingleUpdate($hash,"communication", ">> $str",1)}
|
|
readingsSingleUpdate($hash,"Buffer",@buffer,1);
|
|
|
|
}
|
|
|
|
# Print the data in hex
|
|
sub _hexdump {
|
|
my $s = shift;
|
|
my $r = unpack 'H*', $s;
|
|
$s =~ s/[^ -~]/./g;
|
|
$r . ' ' . $s;
|
|
}
|
|
|
|
# This function processes a response received from the USB stick.
|
|
#
|
|
# In a first step, the ACK response from the stick is handled. This means that the
|
|
# communication sequence number is captured, and a new entry is made in the response queue.
|
|
#
|
|
# Second step, if we receive an error response from the stick, pass this message back
|
|
#
|
|
# Finally, of course, decode actual useful messages and return their value to the caller
|
|
#
|
|
# The input to this function is the message with CRC, with the header and trailing part removed
|
|
sub process_response {
|
|
# my ( $hash, $frame ) = @_;
|
|
my $hash= shift;
|
|
my $frame= shift;
|
|
if (!defined $frame||$frame eq '') {
|
|
return undef
|
|
}
|
|
|
|
$frame =~ s/\x83*//g;
|
|
Log3 $hash,5, "Response-Processing '$frame'";
|
|
my $name=$hash->{NAME};
|
|
my %xplmsg = ( schema => 'plugwise.basic', );
|
|
# Check if the CRC matches
|
|
if (!( _plugwise_crc( substr( $frame, 0, -4 ) ) eq
|
|
substr( $frame, -4, 4 )
|
|
)
|
|
)
|
|
{
|
|
# Send out notification...
|
|
#$xpl->ouch("PLUGWISE: received a frame with an invalid CRC");
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'err';
|
|
$xplmsg{text}="Received frame with invalid CRC";
|
|
$xplmsg{code} = $frame;
|
|
return \%xplmsg;
|
|
}
|
|
|
|
#Switch:
|
|
if ($testcount>=0){
|
|
$testcount=$testcount+1;
|
|
if ($testcount == 41) { $frame = "002403C1000D6F0002907DC90F09412E0006A17000856539070140264E0844C2030000";Log 3,"Insert 0024";}
|
|
if ($testcount == 39) { $frame = "0061FFFE000D6F0002907DC90000";Log 3,"Insert 0061";}
|
|
if ($testcount == 47) { $frame = "004FFFFE000D6F0002907DC9000000";Log 3,"Insert 004F";}
|
|
if ($testcount == 53) { $frame = "0056FFFF000D6F0002907DC90101xxxx";Log 3,"Insert 0056";}
|
|
if ($testcount == 56) { $frame = "0056FFFF000D6F0002907DC90100xxxx";Log 3,"Insert keypress";}
|
|
if ($testcount == 62) { $frame = "0056FFFF000D6F0002907DC90201xxxx";Log 3,"Insert keypress";}
|
|
if ($testcount == 69) { $frame = "0056FFFF000D6F0002907DC90200xxxx";Log 3,"Insert keypress";}
|
|
|
|
# Circle
|
|
# $testcount=$testcount+1;
|
|
# if ($testcount == 40) { $frame = "0027527B000D6F0002907DC83F807E5AB5DCBDA03D74A00400000000xxxx";Log 3,"Insert Calibration";}
|
|
# if ($testcount == 42) { $frame = "0024527C000D6F0002907DC80F097CA60006A95801856539070140264E0844C202xxxx";Log 3,"Insert Status";}
|
|
# if ($testcount == 48) { $frame = "0013527E000D6F0002907DC800000000xxxx";Log 3,"Insert PowerInfo";}
|
|
# if ($testcount == 54) { $frame = "0024527C000D6F0002907DC80F097CA60006A95800856539070140264E0844C202xxxx";Log 3,"Insert Status";}
|
|
|
|
}
|
|
# Strip CRC, we already know it is correct
|
|
$frame =~ s/(.{4}$)//;
|
|
# my $str2=AttrVal($hash->{NAME},"showCom","xyz");
|
|
# my $showcom=qr/$str2/;
|
|
# if ($frame =~ $showcom) {readingsSingleUpdate($hash,"communication", "<< $frame",1)}
|
|
|
|
Log3 $hash,4,"Frame after CRC-strip: $frame";
|
|
# After a command is sent to the stick, we first receive an 'ACK'. This 'ACK' contains a sequence number that we want to track and that notifies us of errors.
|
|
if ( $frame =~ /^0000([[:xdigit:]]{4})([[:xdigit:]]{4})(.*)/ ) {
|
|
# ack | seq. nr. || response code |
|
|
my $seqnr = $1;
|
|
$xplmsg{showCom}="<< 0000 $1 $2 $3";
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'stat';
|
|
$xplmsg{code} = $frame;
|
|
if ( $2 eq "00C1" ) { $xplmsg{text}="ack"; return \%xplmsg;}
|
|
elsif ( $2 eq "00C2" ) { $xplmsg{text}="Error on Ack-Signal"; $xplmsg{type} = 'err';Log 3,Dumper(%xplmsg);return \%xplmsg;}
|
|
#Mywrite($hash,"000A");
|
|
elsif ( $2 eq "00E1" ) { $xplmsg{text}="Circle out of range"; $xplmsg{text}="ack";return \%xplmsg;}
|
|
elsif ( $2 eq "00DE" ) {
|
|
if ( $initdone == 0) {return undef};
|
|
my $saddr = _addr_l2s($3);
|
|
$xplmsg{dest}=$self->{_plugwise}->{circles}->{$saddr}->{type};
|
|
$xplmsg{type} = 'output';
|
|
$xplmsg{text}= 'off';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $3;
|
|
$xplmsg{short} = $saddr;
|
|
return \%xplmsg;
|
|
}
|
|
elsif ( $2 eq "00D8" ) {
|
|
if ( $initdone == 0) {return undef};
|
|
my $saddr = _addr_l2s($3);
|
|
$xplmsg{dest}=$self->{_plugwise}->{circles}->{$saddr}->{type};
|
|
$xplmsg{type} = 'output';
|
|
$xplmsg{text}= 'on';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $3;
|
|
$xplmsg{short} = $saddr;
|
|
return \%xplmsg;
|
|
}
|
|
elsif ( $2 eq "00F9" ) { $xplmsg{text}="Clear group MAC-Table" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00FA" ) { $xplmsg{text}="Fill Switch-schedule" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00F7" ) { $xplmsg{text}="Request self-removal from network" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00F1" ) { $xplmsg{text}="Set broadcast-time interval" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00F5" ) { $xplmsg{text}="Set handle off" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00F4" ) { $xplmsg{text}="Set handle on" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00E6" ) { $xplmsg{text}="Set PN" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00F8" ) { $xplmsg{text}="Set powerrecording" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00BE" ) { $xplmsg{text}="Set scan-params ACK" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00BF" ) { $xplmsg{text}="Set scan-params NACK" ; $xplmsg{type} = 'err';return \%xplmsg;}
|
|
elsif ( $2 eq "00B5" ) { $xplmsg{text}="Set sense-boundaries ACK" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00B6" ) { $xplmsg{text}="Set sense-boundaries NACK" ; $xplmsg{type} = 'err';return \%xplmsg;}
|
|
elsif ( $2 eq "00B3" ) { $xplmsg{text}="Set sense-interval ACK" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00B4" ) { $xplmsg{text}="Set sense-interval NACK" ; $xplmsg{type} = 'err';return \%xplmsg;}
|
|
elsif ( $2 eq "00F6" ) { $xplmsg{text}="Set sleep-behavior" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00E5" ) { $xplmsg{text}="Activate Switch-schedule on" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00E4" ) { $xplmsg{text}="Activate Switch-schedule off" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00DD" ) { $xplmsg{text}="Allow nodes to join ACK0" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00D9" ) { $xplmsg{text}="Allow nodes to join ACK1" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00C8" ) { $xplmsg{text}="Bootload aborted" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00C9" ) { $xplmsg{text}="Bootload done" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00D5" ) { $xplmsg{text}="Cancel read Powermeter-Info Logdata" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00C4" ) { $xplmsg{text}="Cannot join network" ; $xplmsg{type} = 'err';return \%xplmsg;}
|
|
elsif ( $2 eq "00C3" ) { $xplmsg{text}="Command not allowed" ; $xplmsg{type} = 'err';return \%xplmsg;}
|
|
elsif ( $2 eq "00D1" ) { $xplmsg{text}="Done reading Powermeter-Info Logdata" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00C0" ) { $xplmsg{text}="Ember stack error" ; $xplmsg{type} = 'err';return \%xplmsg;}
|
|
elsif ( $2 eq "00C5" ) { $xplmsg{text}="Exceeding Tableindex" ;$xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00CF" ) { $xplmsg{text}="Flash erased" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00C6" ) { $xplmsg{text}="Flash error" ; $xplmsg{type} = 'err';return \%xplmsg;}
|
|
elsif ( $2 eq "00ED" ) { $xplmsg{text}="Group-MAC added" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00EF" ) { $xplmsg{text}="Group-MAC not added" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00F0" ) { $xplmsg{text}="Group-MAC not removed" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00EE" ) { $xplmsg{text}="Group-MAC removed" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00E8" ) { $xplmsg{text}="Image activate ACK" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00CC" ) { $xplmsg{text}="Image check timeout" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00CB" ) { $xplmsg{text}="Image invalid" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00CA" ) { $xplmsg{text}="Image valid" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00C7" ) { $xplmsg{text}="Node-change accepted" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00CD" ) { $xplmsg{text}="Ping timeout 1sec" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00EB" ) { $xplmsg{text}="Pingrun busy" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00EC" ) { $xplmsg{text}="Pingrun finished" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00CE" ) { $xplmsg{text}="Public network-info complete" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00D0" ) { $xplmsg{text}="Remote flash erased" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00F3" ) { $xplmsg{text}="Reply role changed NOK" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00F2" ) { $xplmsg{text}="Reply role changed OK" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00E0" ) { $xplmsg{text}="Send switchblock NACK" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00DA" ) { $xplmsg{text}="Send calib-params ACK" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00E2" ) { $xplmsg{text}="Set relais denied" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00DF" ) { $xplmsg{text}="Set RTC-Data ACK" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00E7" ) { $xplmsg{text}="Set RTC-Data NACK" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
elsif ( $2 eq "00D7" ) { $xplmsg{text}="Set year, month and flashadress DONE" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00BD" ) { $xplmsg{text}="Start Light-Calibration started" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00E9" ) { $xplmsg{text}="Start Pingrun ACK" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00EA" ) { $xplmsg{text}="Stop Pingrun ACK" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00DC" ) { $xplmsg{text}="Syncronize NC ACK" ; return \%xplmsg;}
|
|
elsif ( $2 eq "00D6" ) { $xplmsg{text}="Timeout Powermeter Logdata" ; $xplmsg{type} = 'err'; return \%xplmsg;}
|
|
else {
|
|
if ( $initdone == 0) {return undef};
|
|
$xplmsg{schema} = 'log.basic';
|
|
|
|
# Default error message
|
|
my $text = 'Received error response: $frame';
|
|
my $error = $2;
|
|
|
|
my $msg_causing_error = $frame;
|
|
if ( $msg_causing_error =~ /^0026([[:xdigit:]]{16}$)/ ) {
|
|
my $device = _addr_l2s($1);
|
|
$text = "No calibration response received for $device";
|
|
delete $self->{_plugwise}->{circles}->{$device};
|
|
}
|
|
Log3 $hash,3, "Received error response: $frame";
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'stat';
|
|
$xplmsg{text}= $text;
|
|
$xplmsg{code} = $frame . ":" . $error;
|
|
return \%xplmsg;
|
|
|
|
}
|
|
}
|
|
|
|
if ( $frame
|
|
=~ /^0011([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{4})/
|
|
)
|
|
#0011 0063 000D6F00029014D 8010 14D0D6F00029C512 2BA4DFF4 6D2
|
|
# init resp | seq. nr.|| stick MAC addr || don't care || network key || short key
|
|
{
|
|
$hash->{"STICK_MAC"} = $2;
|
|
$hash->{"NET_KEY"} = $4;
|
|
$self->{_plugwise}->{stick_MAC} = _addr_l2s( $2 );
|
|
$self->{_plugwise}->{network_key} = $4;
|
|
$self->{_plugwise}->{short_key} = $5;
|
|
$self->{_plugwise}->{connected} = 1;
|
|
$firstrun = 1;
|
|
|
|
Log3 $hash,3, "PLUGWISE: Received a valid response to the init request from the Stick. Connected!";
|
|
query_connected_circles($hash);
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'stat';
|
|
$xplmsg{text}= 'Connected';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{showCom}="<< 0011 $1 $2 $3 $4 $5";
|
|
return \%xplmsg;
|
|
}
|
|
|
|
##############################################################################
|
|
##### Heartbeat
|
|
##### 0061 ???? Circle-MAC CRC
|
|
##############################################################################
|
|
|
|
if ( $frame =~ /^0061([[:xdigit:]]{4})([[:xdigit:]]{16})$/ ) {
|
|
if ( $initdone == 0) {return undef};
|
|
my $saddr = _addr_l2s($2);
|
|
unless ($saddr ~~ $self->{_plugwise}->{circles}->{$saddr}) {
|
|
Log3 $hash,3, "PLUGWISE:Heartbeat from Unknown Device $2";
|
|
$self->{_plugwise}->{circles}->{ _addr_l2s( $2 ) } = {};
|
|
Mywrite($hash, "0026" . $2 ,0);
|
|
command($hash,'feature',_addr_l2s($2));
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'stat';
|
|
$xplmsg{text}= 'Found circle';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{showCom}="<< 0061 $1 $2";
|
|
$xplmsg{short} = $saddr;
|
|
}
|
|
return \%xplmsg;
|
|
}
|
|
|
|
# Process the response on a powerinfo request
|
|
# powerinfo resp | seq. nr. || Circle MAC || pulse1 || pulse8 | other stuff we don't care about
|
|
#0013 0051 000D6F0000994CAA 0000 FFFF 00000000 FFFFF DB9000D
|
|
if ( $frame
|
|
=~ /^0013([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{4})([[:xdigit:]]{4})/
|
|
)
|
|
{
|
|
my $saddr = _addr_l2s($2);
|
|
my $pulse1 = $3;
|
|
my $pulse8 = $4;
|
|
|
|
# Assign the values to the data hash
|
|
$self->{_plugwise}->{circles}->{$saddr}->{pulse1} = $pulse1;
|
|
$self->{_plugwise}->{circles}->{$saddr}->{pulse8} = $pulse8;
|
|
$xplmsg{showCom}="<< 0013 $1 $2 $3 $4";
|
|
if ($4 eq "FFFF") {
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'ignore';
|
|
$xplmsg{text}= 'Ignored Spike';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $saddr;
|
|
|
|
return \%xplmsg;
|
|
}
|
|
# Ensure we have the calibration info before we try to calc the power,
|
|
# if we don't have it, return an error reponse
|
|
if ( !defined $self->{_plugwise}->{circles}->{$saddr}->{gainA} ) {
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'err3';
|
|
$xplmsg{text}= 'Report power failed, calibration data not retrieved yet';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $saddr;
|
|
|
|
return \%xplmsg;
|
|
}
|
|
|
|
# Calculate the live power
|
|
my ( $pow1, $pow8 ) = _calc_live_power($hash,$saddr);
|
|
|
|
$xplmsg{dest}=$self->{_plugwise}->{circles}->{$saddr}->{type};
|
|
$xplmsg{type} = 'power';
|
|
$xplmsg{text}= ' ';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $saddr;
|
|
$xplmsg{val1} = $pow1;
|
|
$xplmsg{val2} = $pow8;
|
|
$xplmsg{unit1} = 'W';
|
|
$xplmsg{unit2} = 'W';
|
|
|
|
return \%xplmsg;
|
|
}
|
|
|
|
# Process the response on a query known circles command
|
|
# circle query resp| seq. nr. || Circle+ MAC || Circle MAC on || memory position
|
|
if ( $frame
|
|
=~ /^0019([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{16})([[:xdigit:]]{2})$/
|
|
)
|
|
{
|
|
$xplmsg{showCom}="<< 0019 $1 $2 $3 $4";
|
|
my $nr=sprintf( "%02X", AttrVal($hash->{NAME},"circlecount",50) -1);
|
|
if ($4 eq $nr) {Log 3,$hash->{NAME} . "Init done, found ". (keys(%{$self->{_plugwise}->{circles}})) . " devices";$initdone=1}
|
|
if ( $3 ne "FFFFFFFFFFFFFFFF" ) {
|
|
$self->{_plugwise}->{circles}->{ _addr_l2s( $3 ) } = {};
|
|
Mywrite($hash, "0026" . $3 ,0);
|
|
}
|
|
|
|
$xplmsg{schema} = 'log.basic';
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'stat';
|
|
$xplmsg{text}='none';
|
|
$xplmsg{text}= 'Found circle' if (defined $self->{_plugwise}->{circles}->{_addr_l2s($3)});
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $3;
|
|
$xplmsg{short} = _addr_l2s($3);
|
|
return \%xplmsg;
|
|
}
|
|
|
|
# Process the response on a status request
|
|
# status response | seq. nr. || Circle+ MAC || year,mon, min || curr_log_addr || powerstate
|
|
if ( $frame
|
|
#0024 0050 000D6F0000994CAA 0F08595B 000440B 80 18 565390701402 24E0844C2 02
|
|
=~ /^0024([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{8})([[:xdigit:]]{8})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{12})([[:xdigit:]]{8})([[:xdigit:]]{2})/
|
|
)
|
|
{
|
|
my $saddr = _addr_l2s($2);
|
|
my $onoff = $5 eq '00' ? 'off' : 'on';
|
|
my $current = $5 eq '00' ? 'LOW' : 'HIGH';
|
|
$self->{_plugwise}->{circles}->{$saddr}->{onoff} = $onoff;
|
|
if(!exists $PWType{$9}) {
|
|
Log3 $name, 2, "$name: Autocreate: unknown familycode '$9' found. Please report this!";
|
|
next;
|
|
} else {
|
|
# Log 3,"Device $saddr now is $PWType{$9}";
|
|
$self->{_plugwise}->{circles}->{$saddr}->{type} = $PWType{$9};
|
|
}
|
|
# $self->{_plugwise}->{circles}->{$saddr}->{type} = 'Circle' if (hex($9) <= 2);
|
|
# $self->{_plugwise}->{circles}->{$saddr}->{type} = 'Switch' if (hex($9) == 3 || hex($9) == 4);
|
|
# $self->{_plugwise}->{circles}->{$saddr}->{type} = 'Sense' if (hex($9) == 5);
|
|
# $self->{_plugwise}->{circles}->{$saddr}->{type} = 'Scan' if (hex($9) == 6);
|
|
$self->{_plugwise}->{circles}->{$saddr}->{curr_logaddr} = ( hex($4) - 278528 ) / 8;
|
|
Log 3, "Unknown Device-Code: $frame" if (!defined $self->{_plugwise}->{circles}->{$saddr}->{type});
|
|
Log 3, "Unknown Circle: $saddr" if (!defined $self->{_plugwise}->{circles}->{$saddr});
|
|
my $circle_date_time = _tstamp2time($hash,$3);
|
|
|
|
$xplmsg{dest}=$self->{_plugwise}->{circles}->{$saddr}->{type};
|
|
$xplmsg{type} = 'output';
|
|
$xplmsg{text}= $onoff;
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $saddr;
|
|
$xplmsg{showCom}="<< 0024 $1 $2 $3 $4 $5 $6 $7 $8 $9";
|
|
$xplmsg{val1} = $self->{_plugwise}->{circles}->{$saddr}->{curr_logaddr};
|
|
$xplmsg{val2} = $circle_date_time;
|
|
$xplmsg{val3} = 0;
|
|
|
|
return \%xplmsg;
|
|
}
|
|
|
|
# Process the response on a calibration request
|
|
if ( $frame
|
|
=~ /^0027([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{8})([[:xdigit:]]{8})([[:xdigit:]]{8})([[:xdigit:]]{8})$/
|
|
)
|
|
{
|
|
# calibration resp | seq. nr. || Circle+ MAC || gainA || gainB || offtot || offruis
|
|
#print "Received for $2 calibration response!\n";
|
|
my $saddr = _addr_l2s($2);
|
|
|
|
#print "Short address = $saddr\n";
|
|
Log3 $hash,5,"PLUGWISE: Received calibration reponse for circle $saddr";
|
|
|
|
$self->{_plugwise}->{circles}->{$saddr}->{gainA}
|
|
= _hex2float($3);
|
|
$self->{_plugwise}->{circles}->{$saddr}->{gainB}
|
|
= _hex2float($4);
|
|
$self->{_plugwise}->{circles}->{$saddr}->{offtot}
|
|
= _hex2float($5);
|
|
$self->{_plugwise}->{circles}->{$saddr}->{offruis}
|
|
= _hex2float($6);
|
|
|
|
Log3 $hash,5,"$2 - Calib: $3 - $4 - $5 - $6";
|
|
$lastcircle=$saddr;
|
|
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'stat';
|
|
$xplmsg{text}= 'Calibration-Info received.';
|
|
$xplmsg{showCom}="<< 0027 $1 $2 $3 $4 $5 $6";
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $saddr;
|
|
return \%xplmsg;
|
|
}
|
|
|
|
# Process a Feature-Request
|
|
if ( $frame
|
|
=~ /^0060([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{16})/
|
|
)
|
|
{
|
|
my $s_id = _addr_l2s($2);
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{showCom}="<< 0060 $1 $2 $3";
|
|
$xplmsg{type} = 'stat';
|
|
$xplmsg{text}= 'Features';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $s_id;
|
|
$xplmsg{val1} = $3;
|
|
Log3 $hash,5,Dumper(%xplmsg);
|
|
|
|
Log3 $hash,3, "PLUGWISE: Features for $s_id are $3";
|
|
|
|
return \%xplmsg;
|
|
}
|
|
|
|
|
|
# Process the response of TempHum-Sensor
|
|
|
|
if ( $frame
|
|
=~ /^0105([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{4})([[:xdigit:]]{4})/
|
|
)
|
|
{
|
|
my $s_id = _addr_l2s($2);
|
|
|
|
$xplmsg{dest}=$self->{_plugwise}->{circles}->{$s_id}->{type};
|
|
$xplmsg{type} = 'humtemp';
|
|
$xplmsg{showCom}="<< 0105 $1 $2 $3 $4";
|
|
$xplmsg{text}= ' ';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $s_id;
|
|
$xplmsg{val1} = (hex($3)-3145)/524.30;
|
|
$xplmsg{val2} = (hex($4)-17473)/372.90;
|
|
$xplmsg{unit1} = 'h';
|
|
$xplmsg{unit2} = 'C';
|
|
|
|
Log3 $hash,5,Dumper(%xplmsg);
|
|
|
|
Log3 $hash,5, "PLUGWISE: Temperature for $s_id set";
|
|
|
|
return \%xplmsg;
|
|
}
|
|
|
|
## RemoveNode response
|
|
if ( $frame
|
|
=~ /^001D([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{16})([[:xdigit:]]{2})/
|
|
)
|
|
#001D 1026 000D6F00029C5122 000D6F00029C5122 00
|
|
{
|
|
Log3 $hash,3,"Removed Node $3 (Index $4) from Network.";
|
|
$xplmsg{showCom}="<< 0000 $1 $2 $3 $4";
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'stat';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{text} = 'Removed Node $3 (Index $4) from Network.';
|
|
return \%xplmsg;
|
|
}
|
|
|
|
## Keypress on Switch
|
|
#0056FFFF000D6F0002769C7D0101
|
|
if ( $frame
|
|
=~ /^0056([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{2})([[:xdigit:]]{2})/
|
|
)
|
|
{
|
|
Log3 $hash,4,"Keypress";
|
|
my $s_id = _addr_l2s($2);
|
|
|
|
$xplmsg{dest}=$self->{_plugwise}->{circles}->{$s_id}->{type};
|
|
$xplmsg{type} = 'sense';
|
|
$xplmsg{showCom}="<< 0056 $1 $2 $3 $4";
|
|
$xplmsg{text}= ' ';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $s_id;
|
|
$xplmsg{val1} = hex($4);
|
|
if ($Make2Channels==0) {
|
|
$xplmsg{val1} = hex($4); #+((hex($3)-1)<<1) if ($self->{_plugwise}->{circles}->{$s_id}->{type} eq "PW_Switch");
|
|
$xplmsg{val2} = (hex($3)-1)<<1;
|
|
} else {
|
|
$xplmsg{val3} = hex($3);
|
|
}
|
|
Log3 $hash,5,Dumper(%xplmsg);
|
|
|
|
Log3 $hash,5, "PLUGWISE: Motion-Signal detected";
|
|
|
|
return \%xplmsg;
|
|
}
|
|
# Ping-Response
|
|
if ( $frame
|
|
=~ /^000E([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{4})/
|
|
)
|
|
{
|
|
if ( $initdone == 0) {return undef};
|
|
Log3 $hash,4,"ping";
|
|
my $s_id = _addr_l2s($2);
|
|
|
|
$xplmsg{dest}=$self->{_plugwise}->{circles}->{$s_id}->{type};
|
|
$xplmsg{type} = 'ping';
|
|
$xplmsg{showCom}="<< 004F $1 $2 $3 $4 $5";
|
|
$xplmsg{text}= ' ';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $s_id;
|
|
$xplmsg{val1} = hex($3);
|
|
$xplmsg{val2} = hex($4);
|
|
$xplmsg{val3} = hex($5);
|
|
Log3 $hash,5,Dumper(%xplmsg);
|
|
|
|
return \%xplmsg;
|
|
|
|
}
|
|
# Pushbuttons
|
|
if ( $frame
|
|
=~ /^004F([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{2})/
|
|
)
|
|
{
|
|
if ( $initdone == 0) {return undef};
|
|
Log3 $hash,4,"Keypress";
|
|
my $s_id = _addr_l2s($2);
|
|
|
|
$xplmsg{dest}=$self->{_plugwise}->{circles}->{$s_id}->{type};
|
|
$xplmsg{type} = 'press';
|
|
$xplmsg{showCom}="<< 004F $1 $2 $3";
|
|
$xplmsg{text}= ' ';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $s_id;
|
|
$xplmsg{val1} = hex($3);
|
|
Log3 $hash,5,Dumper(%xplmsg);
|
|
|
|
return \%xplmsg;
|
|
}
|
|
# Process the response on a historic buffer readout
|
|
if ( $frame
|
|
=~ /^0049([[:xdigit:]]{4})([[:xdigit:]]{16})([[:xdigit:]]{16})([[:xdigit:]]{16})([[:xdigit:]]{16})([[:xdigit:]]{16})([[:xdigit:]]{8})$/
|
|
)
|
|
{
|
|
# history resp | seq. nr. || Circle+ MAC || info 1 || info 2 || info 3 || info 4 || address
|
|
my $s_id = _addr_l2s($2);
|
|
my $log_addr = ( hex($7) - 278528 ) / 8;
|
|
$xplmsg{showCom}="<< 0049 $1 $2 $3 $4 $5 $6 $7";
|
|
|
|
#print "Received history response for $2 and address $log_addr!\n";
|
|
|
|
# Assign the values to the data hash
|
|
$self->{_plugwise}->{circles}->{$s_id}->{history}->{logaddress}
|
|
= $log_addr;
|
|
$self->{_plugwise}->{circles}->{$s_id}->{history}->{info1} = $3;
|
|
$self->{_plugwise}->{circles}->{$s_id}->{history}->{info2} = $4;
|
|
$self->{_plugwise}->{circles}->{$s_id}->{history}->{info3} = $5;
|
|
$self->{_plugwise}->{circles}->{$s_id}->{history}->{info4} = $6;
|
|
|
|
# Ensure we have the calibration info before we try to calc the power,
|
|
# if we don't have it, return an error reponse
|
|
if ( !defined $self->{_plugwise}->{circles}->{$s_id}->{gainA} ) {
|
|
|
|
#$xpl->ouch("Cannot report the power, calibration data not received yet for $s_id\n");
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'err';
|
|
$xplmsg{text}= 'Report power failed, calibration data not retrieved yet';
|
|
$xplmsg{showCom}="<< 0049 $1 $2 $3 $4 $5 $6 $7";
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $s_id;
|
|
|
|
return \%xplmsg;
|
|
}
|
|
my ( $tstamp, $energy ) = _report_history($hash,$s_id);
|
|
|
|
# If the timestamp is no good, we tried to retrieve a field that contains no valid data, generate an error response
|
|
if ( $tstamp eq "000000000000" ) {
|
|
|
|
#$xpl->ouch("Cannot report the power for interval $log_addr of circle $s_id, it is in the future\n");
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'err';
|
|
$xplmsg{text}= 'Report power failed, no valid data in time interval';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $s_id;
|
|
$xplmsg{showCom}="<< 0049 $1 $2 $3 $4 $5 $6 $7";
|
|
return \%xplmsg;
|
|
}
|
|
|
|
$xplmsg{dest}=$self->{_plugwise}->{circles}->{$s_id}->{type};
|
|
$xplmsg{type} = 'energy';
|
|
$xplmsg{text}= ' ';
|
|
$xplmsg{code} = $frame;
|
|
$xplmsg{device} = $2;
|
|
$xplmsg{short} = $s_id;
|
|
$xplmsg{val1} = $energy;
|
|
$xplmsg{val2} = $tstamp;
|
|
$xplmsg{val3} = $log_addr;
|
|
$xplmsg{unit1} = 'kWh';
|
|
|
|
Log3 $hash,5,Dumper(%xplmsg);
|
|
|
|
Log3 $hash,5, "PLUGWISE: Historic energy for $s_id [$log_addr] is $energy kWh on $tstamp";
|
|
|
|
return \%xplmsg;
|
|
}
|
|
|
|
# We should not get here unless we receive responses that are not implemented...
|
|
#$xpl->ouch("Received unknown response: '$frame'");
|
|
Log3 $hash,3,"PLUGWISE: Unknown Frame received: $frame - Please report this";
|
|
|
|
$xplmsg{dest}='none';
|
|
$xplmsg{type} = 'err2';
|
|
$xplmsg{text}= 'Unknown Frame received';
|
|
$xplmsg{code} = $frame;
|
|
return \%xplmsg;
|
|
}
|
|
|
|
sub command {
|
|
my ( $hash, $command, $target, $parameter ) = @_;
|
|
|
|
Log3 $hash,5,"Command=$command - Target=$target";
|
|
if ( !defined($command) || !defined($target) ) {
|
|
Log3 $hash,3,"A command to the stick needs a command and a target ID as parameter";
|
|
return 0;
|
|
}
|
|
|
|
#Log 3,"Set $self->{_plugwise}->{circles}->{$target}->{type} $target $command to $parameter";
|
|
my $packet = "";
|
|
my $pri=0;
|
|
if ( defined $target ) {
|
|
|
|
# Commands that target a specific device might need to be sent multiple times
|
|
# if multiple devices are defined
|
|
my $circle = uc($target);
|
|
if ( $command =~ /(on|off)/ ) {
|
|
if ($self->{_plugwise}->{circles}->{$circle}->{type} eq "PW_Circle") {
|
|
$packet = "0017" . _addr_s2l($circle) . ($1 eq 'on' ? '01' : '00');
|
|
} elsif($self->{_plugwise}->{circles}->{$circle}->{type} eq "PW_Switch"){
|
|
# Log 3,"Set Switch $circle $parameter to $1";
|
|
if ($parameter eq "left") {
|
|
$packet = "0017" . _addr_s2l($circle) . "01" . $1 eq 'on' ? '01' : '00';
|
|
} elsif ($parameter eq "right") {
|
|
$packet = "0017" . _addr_s2l($circle) . "02" . $1 eq 'on' ? '01' : '00';
|
|
}
|
|
|
|
}
|
|
$pri=1;
|
|
|
|
}
|
|
elsif ($command eq "feature") {
|
|
$packet = "005F" . _addr_s2l($circle);
|
|
}
|
|
elsif ($command eq "ping") {
|
|
$packet = "000D" . _addr_s2l($circle);
|
|
}
|
|
elsif ( $command eq 'syncTime' ) {
|
|
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
|
|
$packet = "0016" .
|
|
_addr_s2l($circle) .
|
|
sprintf( "%02X", $year-100) .
|
|
sprintf( "%02X", $mon) .
|
|
sprintf( "%04X", ($mday * 24 * 60) + ($hour * 60) + $min) .
|
|
"FFFFFFFF" .
|
|
sprintf( "%02X", $hour) . sprintf( "%02X", $min) . sprintf( "%02X", $sec) . sprintf( "%02X", $wday) ;
|
|
# Log3 $hash,3,"syncTime-Frame: $packet";
|
|
# return (undef);
|
|
|
|
}
|
|
elsif ( $command eq 'status' ) {
|
|
$packet = "0023" . _addr_s2l($circle);
|
|
}
|
|
elsif ($command eq 'removeNode') {
|
|
$packet = "001C" . _addr_s2l( $self->{_plugwise}->{coordinator_MAC} ) . _addr_s2l($circle);
|
|
}
|
|
elsif ( $command eq 'livepower' ) {
|
|
|
|
# Ensure we have the calibration readings before we send the read command
|
|
# because the processing of the response of the read command required the
|
|
# calibration readings output to calculate the actual power
|
|
# Log3 $hash,3,Dumper( $self->{_plugwise});
|
|
if (!defined(
|
|
$self->{_plugwise}->{circles}->{$circle}->{offruis}
|
|
)
|
|
)
|
|
{
|
|
my $longaddr = _addr_s2l($circle);
|
|
Mywrite( $hash,"0026" . $longaddr ,0)
|
|
; #, "Request calibration info");
|
|
}
|
|
$packet = "0012" . _addr_s2l($circle);
|
|
|
|
}
|
|
elsif ( $command eq 'history' ) {
|
|
|
|
# Ensure we have the calibration readings before we send the read command
|
|
# because the processing of the response of the read command required the
|
|
# calibration readings output to calculate the actual power
|
|
if (!defined(
|
|
$self->{_plugwise}->{circles}->{$circle}->{offruis}
|
|
)
|
|
)
|
|
{
|
|
my $longaddr = _addr_s2l($circle);
|
|
Mywrite($hash, "0026" . $longaddr ,0)
|
|
; #, "Request calibration info");
|
|
}
|
|
|
|
if ( !defined $parameter ) {
|
|
Log3 $hash,3,"The 'history' command needs both a Circle ID and an address to read...";
|
|
return 0;
|
|
}
|
|
Log3 $hash,5,"requesting Log for $parameter";
|
|
my $address = ($parameter * 8) + 278528;#* 8 + 278528;
|
|
$packet
|
|
= "0048"
|
|
. _addr_s2l($circle)
|
|
. sprintf( "%08X", $address );
|
|
Log3 $hash,5,"Write command: $packet";
|
|
}
|
|
else {
|
|
Log3 $hash,3,"Received invalid command '$command'";
|
|
return 0;
|
|
}
|
|
|
|
# Send the packet to the stick!
|
|
Log3 $hash,5,"Write command: $packet";
|
|
Mywrite($hash,$packet,$pri) if ( defined $packet );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
# Interrogate the network coordinator (Circle+) for all connected Circles
|
|
# This sub will generate the requests, and then the response parser function
|
|
# will generate a hash with all known circles
|
|
# When a circle is detected, a calibration request is sent to ge the relevant info
|
|
# required to calculate the power information.
|
|
# Circle info goes into a global hash like this:
|
|
# $object->{_plugwise}->{circles}
|
|
# A single circle entry contains the short id and the following info:
|
|
# short_id => { gainA => xxx,
|
|
# gainB => xxx,
|
|
# offtot => xxx,
|
|
# offruis => xxx }
|
|
sub query_connected_circles {
|
|
|
|
my ($hash) = @_;
|
|
|
|
# In this code we will scan all connected circles to be able to add them to the $self->{_plugwise}->{circles} hash
|
|
my $index = 0;
|
|
Log3 $hash,5,$hash->{NAME} . " - Looking for Circles.....";
|
|
# Interrogate the Circle+ and add its info into the circles hash
|
|
$self->{_plugwise}->{coordinator_MAC}
|
|
= _addr_l2s( $self->{_plugwise}->{network_key} );
|
|
$self->{_plugwise}->{circles} = {}; # Reset known circles hash
|
|
$self->{_plugwise}->{circles}->{ _addr_l2s( $self->{_plugwise}->{network_key} ) }
|
|
= {}; # Add entry for Circle+
|
|
Mywrite( $hash, "0026" . _addr_s2l( $self->{_plugwise}->{coordinator_MAC} ) ,0);
|
|
|
|
# Interrogate the first x connected devices
|
|
while ( $index < AttrVal($hash->{NAME},"circlecount",50) ) {
|
|
my $strindex = sprintf( "%02X", $index++ );
|
|
my $packet
|
|
= "0018"
|
|
. _addr_s2l( $self->{_plugwise}->{coordinator_MAC} )
|
|
. $strindex;
|
|
Mywrite($hash,$packet,0); #, "Query connected device $strindex");
|
|
}
|
|
return;
|
|
}
|
|
|
|
# Convert the long Circle address notation to short
|
|
sub _addr_l2s {
|
|
my ( $address ) = @_;
|
|
my $saddr = substr( $address, -8, 8 );
|
|
|
|
# We will return at least 6 bytes, more if required
|
|
# This is to keep compatibility with existing code that only supports 6 byte short addresses
|
|
return sprintf( "%06X", hex($saddr) );
|
|
}
|
|
|
|
# Convert the short Circle address notation to long
|
|
sub _addr_s2l {
|
|
my ( $address ) = @_;
|
|
# Log 3,Dumper(caller) if ($address eq 0xffffffff);
|
|
return "000D6F00" . sprintf( "%08X", hex($address) );
|
|
}
|
|
|
|
# Convert hex values to float for power readout
|
|
sub _hex2float {
|
|
my ( $hexstr ) = @_;
|
|
my $floater = unpack( 'f', reverse pack( 'H*', $hexstr ) );
|
|
return $floater;
|
|
}
|
|
|
|
sub _report_history {
|
|
my ( $hash, $id ) = @_;
|
|
|
|
# Get the first data entry
|
|
my $data = $self->{_plugwise}->{circles}->{$id}->{history}->{info1};
|
|
|
|
my $energy = 0;
|
|
my $tstamp = 0;
|
|
|
|
if ( $data =~ /^([[:xdigit:]]{8})([[:xdigit:]]{8})$/ ) {
|
|
|
|
# Calculate Wh
|
|
my $corrected_pulses = _pulsecorrection( $hash,$id, hex($2) );
|
|
$energy = $corrected_pulses / 3600 / 468.9385193 * 1000;
|
|
$tstamp = _tstamp2time($hash,$1);
|
|
|
|
# Round to 1 Wh
|
|
$energy = sprintf($attr{$hash->{NAME}}{WattFormat},$energy);
|
|
|
|
# Report kWh
|
|
# $energy = $energy / 1000;
|
|
|
|
#print "info1 date: $tstamp, energy $energy kWh\n";
|
|
}
|
|
|
|
return ( $tstamp, $energy );
|
|
|
|
}
|
|
|
|
# Convert a Plugwise timestamp to a human-readable format
|
|
sub _tstamp2time {
|
|
my ( $hash, $tstamp ) = @_;
|
|
# Return empty time on empty timestamp
|
|
return "000000000000" if ( $tstamp eq "FFFFFFFF" );
|
|
|
|
# Convert
|
|
if ( $tstamp =~ /([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{4})/ ) {
|
|
my $circle_date = sprintf( "%04i-%02i-%02i",
|
|
2000 + hex($1),
|
|
hex($2), int( hex($3) / 60 / 24 ) + 1 );
|
|
my $circle_time = hex($3) % ( 60 * 24 );
|
|
my $circle_hours = int( $circle_time / 60 );
|
|
my $circle_minutes = $circle_time % 60;
|
|
$circle_time = sprintf( " %02i:%02i", $circle_hours, $circle_minutes );
|
|
|
|
return $circle_date . $circle_time;
|
|
}
|
|
else {
|
|
return "000000000000";
|
|
}
|
|
}
|
|
|
|
# Calculate the live power consumption from the last report.
|
|
sub _calc_live_power {
|
|
my ( $hash, $id ) = @_;
|
|
|
|
#my ($pulse1, $pulse8) = $self->pulsecorrection($id);
|
|
my $pulse1 = _pulsecorrection( $hash,$id,
|
|
hex( $self->{_plugwise}->{circles}->{$id}->{pulse1} ) );
|
|
my $pulse8 = _pulsecorrection( $hash,$id,
|
|
hex( $self->{_plugwise}->{circles}->{$id}->{pulse8} ) /8 );
|
|
|
|
my $live1 = $pulse1 * 1000 / 468.9385193;
|
|
my $live8 = $pulse8 * 1000 / 468.9385193;
|
|
# Round
|
|
$live1 = sprintf($attr{$hash->{NAME}}{WattFormat},$live1);
|
|
$live8 = sprintf($attr{$hash->{NAME}}{WattFormat},$live8);
|
|
|
|
return ( $live1, $live8 );
|
|
|
|
}
|
|
|
|
# Correct the reported number of pulses based on the calibration values
|
|
sub _pulsecorrection {
|
|
my ( $hash, $id, $pulses ) = @_;
|
|
|
|
# Get the calibration values for the circle
|
|
my $offnoise = $self->{_plugwise}->{circles}->{$id}->{offruis};
|
|
my $offtot = $self->{_plugwise}->{circles}->{$id}->{offtot};
|
|
my $gainA = $self->{_plugwise}->{circles}->{$id}->{gainA};
|
|
my $gainB = $self->{_plugwise}->{circles}->{$id}->{gainB};
|
|
|
|
# Correct the pulses with the calibration data
|
|
my $out
|
|
= ( ( $pulses + $offnoise ) ^ 2 ) * $gainB
|
|
+ ( ( $pulses + $offnoise ) * $gainA )
|
|
+ $offtot;
|
|
|
|
# Never report negative values, can happen with really small values
|
|
$out = 0 if ( $out < 0 );
|
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
|
|
|
|
"Cogito, ergo sum.";
|
|
|
|
=pod
|
|
=item device
|
|
=item summary Module for controling Plugwise-Devices
|
|
=item summary_DE Modul für das Plugwise-System
|
|
=begin html
|
|
|
|
<a name="Plugwise"></a>
|
|
<h3>Plugwise</h3>
|
|
<ul>
|
|
This module is for the Plugwise-System.
|
|
<br>
|
|
Note: this module requires the Device::SerialPort or Win32::SerialPort module
|
|
if the devices is connected via USB or a serial port.
|
|
Also needed: digest:CRC
|
|
You can install these modules using CPAN.
|
|
<br><br>
|
|
<b>Define</b>
|
|
<ul>
|
|
<code>define <name> Plugwise <device> </code><br>
|
|
</ul>
|
|
<br>
|
|
<device> specifies the serial port to communicate with the Plugwise-Stick.
|
|
Normally on Linux the device will be named /dev/ttyUSBx, where x is a number.
|
|
For example /dev/ttyUSB0. Please note that the Plugwise-Stick normally operates at 115200 baud. You may specify the baudrate used after the @ char.<br>
|
|
<br>
|
|
Example: <br>
|
|
<code>define myPlugwise Plugwise /dev/ttyPlugwise@115200</code>
|
|
<br>
|
|
</ul>
|
|
<br>
|
|
|
|
<a name="PLUGWISEset"></a>
|
|
<b>Set</b>
|
|
<ul>
|
|
<code>Scan_Circles</code>
|
|
<ul>
|
|
Initiates a scan for new devices and defines them.
|
|
</ul><br><br>
|
|
<code>syncTime</code>
|
|
<ul>
|
|
Syncs all reachable devices to the system-time.
|
|
</ul><br><br>
|
|
<code>reOpen</code>
|
|
<ul>
|
|
Closes and reopens the serial-Port. (useful in case of to many Errors)
|
|
</ul><br><br>
|
|
</ul>
|
|
<br><br>
|
|
|
|
<b>Attributes</b>
|
|
<ul>
|
|
<code>circlecount</code><br>
|
|
<ul>
|
|
Max. Number of Circles to be found by the Scan-Command
|
|
<br><br>
|
|
</ul>
|
|
<code>interval</code><br>
|
|
<ul>standard polling-interval for new Circles
|
|
</ul><br><br>
|
|
<code>autosync</code><br>
|
|
<ul>Sends every n seconds a SyncTime to each device
|
|
</ul><br><br>
|
|
<code>WattFormat</code><br>
|
|
<ul>A string representing the format of the power-readings.
|
|
If not defined, it defaults to %0.f
|
|
</ul><br><br>
|
|
<code>showCom</code><br>
|
|
<ul>Writes the complete communication matching a RegEx into the reading "communication"
|
|
(can be viewed in EventMonitor or used with a FileLog)
|
|
</ul><br><br>
|
|
|
|
<br>
|
|
</ul>
|
|
|
|
=end html
|
|
|
|
=begin html_DE
|
|
|
|
<a name="Plugwise"></a>
|
|
<h3>Plugwise</h3>
|
|
<ul>
|
|
Modul für das Plugwise-System.
|
|
<br>
|
|
Achtung: Dieses Modul benötigt folgende Perl-Module:
|
|
<ul><li>Device::SerialPort oder Win32::SerialPort</li>
|
|
<li>digest:CRC</li></ul>
|
|
<br><br>
|
|
<b>Define</b>
|
|
<ul>
|
|
<code>define <name> Plugwise <device> </code><br>
|
|
</ul>
|
|
<br>
|
|
<device> Gibt den COM-Port des Plugwise-Stick an.
|
|
Unter Linux ist dies im Normalfall /dev/ttyUSBx, wobei x eine fortlaufende Nummer ist. (zB /dev/ttyUSB0)
|
|
Wobei es unter Linux sinnvoller ist, den Port mittels UDEV-Regeln oder mittels /dev/by-id/ anzugeben.
|
|
Der Plugwise-Stick läuft fix auf 115200 Baud<br>
|
|
<br>
|
|
Beispiel: <br>
|
|
<code>define myPlugwise Plugwise /dev/ttyPlugwise</code>
|
|
<br>
|
|
</ul>
|
|
<br>
|
|
|
|
<a name="PLUGWISEset"></a>
|
|
<b>Set</b>
|
|
<ul>
|
|
<code>Scan_Circles</code>
|
|
<ul>
|
|
Startet eine Suche nach neuen Geräten und legt diese per Autocreate an.
|
|
</ul><br><br>
|
|
<code>syncTime</code>
|
|
<ul>
|
|
Syncronisiert die internen RTCs der Geräte mit der aktuellen Systemzeit.
|
|
</ul><br><br>
|
|
<code>reOpen</code>
|
|
<ul>
|
|
Öffnet den COM-Port neu (zB bei zu vielen Fehlern, nach deren Behebung)
|
|
</ul><br><br>
|
|
</ul>
|
|
<br><br>
|
|
|
|
<b>Attribute</b>
|
|
<ul>
|
|
<code>circlecount</code><br>
|
|
<ul>
|
|
Maximale Anzahl der Geräte, nach denengesucht wird.
|
|
<br><br>
|
|
</ul>
|
|
<code>interval</code><br>
|
|
<ul>Standard-Abfrageintervall der Circles
|
|
</ul><br><br>
|
|
<code>autosync</code><br>
|
|
<ul>Sendet alle >n< Sekunden ein "syncTime" an alle Geräte
|
|
</ul><br><br>
|
|
<code>WattFormat</code><br>
|
|
<ul>String, mit welchem die Power-Readings formatiert werden
|
|
Standard: %0.f
|
|
</ul><br><br>
|
|
<code>showCom</code><br>
|
|
<ul>Schreibt die gesamte Kommunikation (gefiltern nach >regEx<) in das Reading "communication"
|
|
(Am besten mit FileLog oder dem Eventmonitor anzusehen)
|
|
</ul><br><br>
|
|
|
|
<br>
|
|
</ul>
|
|
|
|
=end html_DE
|
|
=cut
|
|
|