2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-19 18:56:03 +00:00

37_SHC.pm: Updated for current smarthomatic v0.12.0 (Forum #129298)

git-svn-id: https://svn.fhem.de/fhem/trunk@26451 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
breaker27 2022-09-28 19:51:36 +00:00
parent 49e4678921
commit ee39392e14
6 changed files with 288 additions and 53 deletions

View File

@ -1,5 +1,8 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- change: 37_SHC: Support current smarthomatic version.
Basestation uses 115200 Baud by default and CRC.
EnvSensor supports particulate matter sensor.
- bugfix: 73_NUKIBridge: Fix 503 Unavailable message - bugfix: 73_NUKIBridge: Fix 503 Unavailable message
- change: 88_HMCCU: Switched close to closed - change: 88_HMCCU: Switched close to closed
- bugfix: 70_Klafs: package main was removed from source code - bugfix: 70_Klafs: package main was removed from source code

View File

@ -2,6 +2,7 @@
# This file is part of the smarthomatic module for FHEM. # This file is part of the smarthomatic module for FHEM.
# #
# Copyright (c) 2014 Stefan Baumann # Copyright (c) 2014 Stefan Baumann
# 2015, 2022 Uwe Freese
# #
# You can find smarthomatic at www.smarthomatic.org. # You can find smarthomatic at www.smarthomatic.org.
# You can find FHEM at www.fhem.de. # You can find FHEM at www.fhem.de.
@ -26,6 +27,8 @@ package main;
use strict; use strict;
use warnings; use warnings;
use Time::HiRes qw(gettimeofday); use Time::HiRes qw(gettimeofday);
use Digest::CRC qw(crc32); # linux packet libdigest-crc-perl
use DevIo;
sub SHC_Parse($$$$); sub SHC_Parse($$$$);
sub SHC_Read($); sub SHC_Read($);
@ -37,7 +40,7 @@ sub SHC_SimpleWrite(@);
my $clientsSHC = ":SHCdev:BASE:xxx:"; my $clientsSHC = ":SHCdev:BASE:xxx:";
my %matchListSHC = ( my %matchListSHC = (
"1:SHCdev" => "^Packet Data: SenderID=[1-9]|0[1-9]|[1-9][0-9]|[0-9][0-9][0-9]|[0-3][0-9][0-9][0-9]|40[0-8][0-9]|409[0-6]", #1-4096 with leading zeros "1:SHCdev" => "^PKT:SID=([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-3][0-9][0-9][0-9]|40[0-8][0-9]|409[0-6]);", #1-4096
"2:xxx" => "^\\S+\\s+22", "2:xxx" => "^\\S+\\s+22",
"3:xxx" => "^\\S+\\s+11", "3:xxx" => "^\\S+\\s+11",
"4:xxx" => "^\\S+\\s+9 ", "4:xxx" => "^\\S+\\s+9 ",
@ -47,8 +50,6 @@ sub SHC_Initialize($)
{ {
my ($hash) = @_; my ($hash) = @_;
require "$attr{global}{modpath}/FHEM/DevIo.pm";
# Provider # Provider
$hash->{ReadFn} = "SHC_Read"; $hash->{ReadFn} = "SHC_Read";
$hash->{WriteFn} = "SHC_Write"; $hash->{WriteFn} = "SHC_Write";
@ -69,7 +70,7 @@ sub SHC_Define($$)
my @a = split("[ \t][ \t]*", $def); my @a = split("[ \t][ \t]*", $def);
if (@a != 3) { if (@a != 3) {
my $msg = "wrong syntax: define <name> SHC {devicename[\@baudrate] " . "| devicename\@directio}"; my $msg = "wrong syntax: define <name> SHC {devicename[\@baudrate]}";
Log3 undef, 2, $msg; Log3 undef, 2, $msg;
return $msg; return $msg;
} }
@ -78,7 +79,7 @@ sub SHC_Define($$)
my $name = $a[0]; my $name = $a[0];
my $dev = $a[2]; my $dev = $a[2];
$dev .= "\@19200" if ($dev !~ m/\@/); $dev .= "\@115200" if ($dev !~ m/\@/);
$hash->{Clients} = $clientsSHC; $hash->{Clients} = $clientsSHC;
$hash->{MatchList} = \%matchListSHC; $hash->{MatchList} = \%matchListSHC;
@ -221,7 +222,6 @@ sub SHC_ReadAnswer($$$$)
undef, $mpandata undef, $mpandata
); );
} }
} }
##################################### #####################################
@ -269,11 +269,15 @@ sub SHC_Parse($$$$)
next if (!$dmsg || length($dmsg) < 1); # Bogus messages next if (!$dmsg || length($dmsg) < 1); # Bogus messages
if ($dmsg !~ m/^Packet Data: SenderID=/) { if ($dmsg =~ m/^PKT:SID=0;/) { # "echo" from message sent by FHEM itself
return;
}
if ($dmsg !~ m/^PKT:SID=/) {
# Messages just to dipose # Messages just to dipose
if ( $dmsg =~ m/^\*\*\* Enter AES key nr/ if ( $dmsg =~ m/^\*\*\* Enter data/
|| $dmsg =~ m/^\*\*\* Received character/) || $dmsg =~ m/^\*\*\* 0x/)
{ {
return; return;
} }
@ -294,17 +298,34 @@ sub SHC_Parse($$$$)
# -Verbosity level 4 # -Verbosity level 4
if ( $dmsg =~ m/^Request added to queue/ if ( $dmsg =~ m/^Request added to queue/
|| $dmsg =~ m/^Request Buffer/ || $dmsg =~ m/^Request Buffer/
|| $dmsg =~ m/^Request (q|Q)ueue/) || $dmsg =~ m/^Request Queue/)
{ {
Log3 $name, 4, "$name: $dmsg"; Log3 $name, 4, "$name: $dmsg";
return; return;
} }
# -Verbosity level 1
if ( $dmsg =~ m/^CRC Error/ )
{
Log3 $name, 1, "$name: $dmsg";
return;
}
# Anything else in verbosity level 3 # Anything else in verbosity level 3
Log3 $name, 3, "$name: $dmsg"; Log3 $name, 3, "$name: $dmsg";
return; return;
} }
# check CRC of "PKT:..." message and ignore message if necessary
my $crc = crc32(substr($dmsg, 4, length($dmsg) - 12));
$crc = sprintf("%08x", $crc);
if ($crc ne substr($dmsg, length($dmsg) - 8))
{
Log3 $name, 1, "$name: CRC Error (" . $crc . ") $dmsg";
return;
}
$hash->{"${name}_MSGCNT"}++; $hash->{"${name}_MSGCNT"}++;
$hash->{"${name}_TIME"} = TimeNow(); $hash->{"${name}_TIME"} = TimeNow();
$hash->{RAWMSG} = $rmsg; $hash->{RAWMSG} = $rmsg;
@ -345,12 +366,17 @@ sub SHC_SimpleWrite(@)
syswrite($hash->{DIODev}, $msg) if ($hash->{DIODev}); syswrite($hash->{DIODev}, $msg) if ($hash->{DIODev});
# Some linux installations are broken with 0.001, T01 returns no answer # Some linux installations are broken with 0.001, T01 returns no answer
select(undef, undef, undef, 0.01); #select(undef, undef, undef, 0.01);
# Sleep for 250 milliseconds to make sure the base station can process the command before the next is sent
select(undef, undef, undef, 0.25);
} }
1; 1;
=pod =pod
=item summary support the basestation of smarthomatic (www.smarthomatic.org)
=item summary_DE Unterstützung der Basisstation von smarthomatic (www.smarthomatic.org)
=begin html =begin html
<a name="SHC"></a> <a name="SHC"></a>
@ -364,7 +390,9 @@ sub SHC_SimpleWrite(@)
Note: this module may require the Device::SerialPort or Win32::SerialPort 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 module if you attach the device via USB and the OS sets strange default
parameters for serial devices.<br><br> parameters for serial devices.<br>
It also requires Digest::CRC because the communication to the basestation
is secured by a CRC.<br><br>
<a name="SHC_Define"></a> <a name="SHC_Define"></a>
<b>Define</b> <b>Define</b>
@ -377,7 +405,7 @@ sub SHC_SimpleWrite(@)
You can also specify a baudrate if the device name contains the @ You can also specify a baudrate if the device name contains the @
character, e.g.: /dev/ttyUSB0@57600. Please note that the default character, e.g.: /dev/ttyUSB0@57600. Please note that the default
baudrate for the SHC base station is 19200 baud.<br><br> baudrate for the SHC base station is 115200 baud.<br><br>
Example:<br> Example:<br>
<ul> <ul>

View File

@ -1,7 +1,8 @@
########################################################################## ##########################################################################
# This file is part of the smarthomatic module for FHEM. # This file is part of the smarthomatic module for FHEM.
# #
# Copyright (c) 2014 Stefan Baumann, Uwe Freese # Copyright (c) 2014 Stefan Baumann
# 2014, 2015, 2019 Uwe Freese
# #
# You can find smarthomatic at www.smarthomatic.org. # You can find smarthomatic at www.smarthomatic.org.
# You can find FHEM at www.fhem.de. # You can find FHEM at www.fhem.de.
@ -63,7 +64,10 @@ my %dev_state_format = (
"port", "Port: ", "port", "Port: ",
"ains", "Ain: " "ains", "Ain: "
], ],
"RGBDimmer" => ["color", "Color: "], "RGBDimmer" => [
"color", "Color: ",
"brightness", "Brightness: "
],
"SoilMoistureMeter" => ["humidity", "H: "] "SoilMoistureMeter" => ["humidity", "H: "]
); );
@ -78,13 +82,14 @@ my %sets = (
"DigitalPort " . "DigitalPort " .
"DigitalPortTimeout " . "DigitalPortTimeout " .
"DigitalPin " . "DigitalPin " .
"DigitalPinTimeout ", "DigitalPinTimeout",
"Dimmer" => "on:noArg off:noArg toggle:noArg statusRequest:noArg pct:slider,0,1,100 ani " . "Dimmer" => "on:noArg off:noArg toggle:noArg statusRequest:noArg pct:slider,0,1,100 ani " .
# Used from SetExtensions.pm # Used from SetExtensions.pm
"blink on-for-timer on-till off-for-timer off-till intervals", "blink on-for-timer on-till off-for-timer off-till intervals",
"EnvSensor" => "", "EnvSensor" => "",
"RGBDimmer" => "Color " . "RGBDimmer" => "Color " .
"ColorAnimation", "ColorAnimation " .
"Dimmer.Brightness:slider,0,1,100",
"SoilMoistureMeter" => "", "SoilMoistureMeter" => "",
"Custom" => "Dimmer.Brightness " . "Custom" => "Dimmer.Brightness " .
"Dimmer.Animation" "Dimmer.Animation"
@ -107,7 +112,7 @@ sub SHCdev_Initialize($)
{ {
my ($hash) = @_; my ($hash) = @_;
$hash->{Match} = "^Packet Data: SenderID=[1-9]|0[1-9]|[1-9][0-9]|[0-9][0-9][0-9]|[0-3][0-9][0-9][0-9]|40[0-8][0-9]|409[0-6]"; $hash->{Match} = "^PKT:SID=([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-3][0-9][0-9][0-9]|40[0-8][0-9]|409[0-6]);";
$hash->{SetFn} = "SHCdev_Set"; $hash->{SetFn} = "SHCdev_Set";
$hash->{GetFn} = "SHCdev_Get"; $hash->{GetFn} = "SHCdev_Get";
$hash->{DefFn} = "SHCdev_Define"; $hash->{DefFn} = "SHCdev_Define";
@ -185,19 +190,20 @@ sub SHCdev_Parse($$)
my $name = $hash->{NAME}; my $name = $hash->{NAME};
if (!$parser->parse($msg)) { if (!$parser->parse($msg)) {
Log3 $hash, 4, "SHC_TEMP: parser error: $msg"; Log3 $name, 1, "$name: Parser error: $msg";
return ""; return "";
} }
my $msgtypename = $parser->getMessageTypeName(); my $msgtypename = $parser->getMessageTypeName();
my $msggroupname = $parser->getMessageGroupName(); my $msggroupname = $parser->getMessageGroupName();
my $msgname = $parser->getMessageName(); my $msgname = $parser->getMessageName();
my $raddr = $parser->getSenderID(); my $packetcounter = $parser->getPacketCounter();
my $rhash = $modules{SHCdev}{defptr}{$raddr}; my $raddr = $parser->getSenderID();
my $rname = $rhash ? $rhash->{NAME} : $raddr; my $rhash = $modules{SHCdev}{defptr}{$raddr};
my $rname = $rhash ? $rhash->{NAME} : $raddr;
if (!$modules{SHCdev}{defptr}{$raddr}) { if (!$modules{SHCdev}{defptr}{$raddr}) {
Log3 $name, 3, "SHC_TEMP: Unknown device $rname, please define it"; Log3 $name, 3, "$name: Unknown device $rname, please define it";
return "UNDEFINED SHCdev_$rname SHCdev $raddr"; return "UNDEFINED SHCdev_$rname SHCdev $raddr";
} }
@ -218,6 +224,9 @@ sub SHCdev_Parse($$)
readingsBeginUpdate($rhash); readingsBeginUpdate($rhash);
# remember PacketCounter (which every message provides)
readingsBulkUpdate($rhash, "packetCounter", $packetcounter);
given ($msggroupname) { given ($msggroupname) {
when ('Generic') { when ('Generic') {
given ($msgname) { given ($msgname) {
@ -244,6 +253,9 @@ sub SHCdev_Parse($$)
} }
readingsBulkUpdate($rhash, "version", "$major.$minor.$patch-$vhash"); readingsBulkUpdate($rhash, "version", "$major.$minor.$patch-$vhash");
}
when ('HardwareError') {
readingsBulkUpdate($rhash, "hardwareErrorCode", $parser->getField("ErrorCode"));
} }
when ('BatteryStatus') { when ('BatteryStatus') {
readingsBulkUpdate($rhash, "battery", $parser->getField("Percentage")); readingsBulkUpdate($rhash, "battery", $parser->getField("Percentage"));
@ -334,6 +346,35 @@ sub SHCdev_Parse($$)
my $brt = $parser->getField("Distance"); my $brt = $parser->getField("Distance");
readingsBulkUpdate($rhash, "distance", $brt); readingsBulkUpdate($rhash, "distance", $brt);
} }
when ('ParticulateMatter') {
my $size = $parser->getField("TypicalParticleSize");
if ($size != 1023) # 1023 means invalid
{
readingsBulkUpdate($rhash, "typicalParticleSize", $size / 100); # value was in 1/100 µm
}
for (my $i = 0 ; $i < 5 ; $i++) {
$size = $parser->getField("Size", $i);
if ($size) # 0 means array element not used
{
my $pmStr = int($size / 10) . "." . ($size % 10);
my $massConcentration = $parser->getField("MassConcentration", $i);
my $numberConcentration = $parser->getField("NumberConcentration", $i);
if ($massConcentration != 1023) # 1023 means invalid
{
readingsBulkUpdate($rhash, "massConcentration_PM" . $pmStr, $massConcentration / 10); # value was in 1/10 µm
}
if ($numberConcentration != 4095) # 4095 means invalid
{
readingsBulkUpdate($rhash, "numberConcentration_PM" . $pmStr, $numberConcentration / 10); # value was in 1/10 µm
}
}
}
}
} }
} }
when ('Dimmer') { when ('Dimmer') {
@ -605,6 +646,16 @@ sub SHCdev_Set($@)
} }
readingsSingleUpdate($hash, "state", "set-coloranimation", 1); readingsSingleUpdate($hash, "state", "set-coloranimation", 1);
SHCdev_Send($hash); SHCdev_Send($hash);
} elsif ($cmd eq 'Dimmer.Brightness') {
my $brightness = $arg;
# DEBUG
# Log3 $name, 3, "$name: Args: $arg, $arg2, $arg3, $brightness";
readingsSingleUpdate($hash, "state", "set-brightness:$brightness", 1);
$parser->initPacket("Dimmer", "Brightness", "SetGet");
$parser->setField("Dimmer", "Brightness", "Brightness", $brightness);
SHCdev_Send($hash);
} else { } else {
return SetExtensions($hash, "", $name, @aa); return SetExtensions($hash, "", $name, @aa);
} }
@ -713,6 +764,8 @@ sub SHCdev_Send($)
1; 1;
=pod =pod
=item summary support of several smarthomatic devices (www.smarthomatic.org)
=item summary_DE Unterstützung verschiedener smarthomatic-Geräte (www.smarthomatic.org)
=begin html =begin html
<a name="SHCdev"></a> <a name="SHCdev"></a>

View File

@ -3,7 +3,7 @@
########################################################################## ##########################################################################
# This file is part of the smarthomatic module for FHEM. # This file is part of the smarthomatic module for FHEM.
# #
# Copyright (c) 2014 Uwe Freese # Copyright (c) 2014, 2019 Uwe Freese
# #
# You can find smarthomatic at www.smarthomatic.org. # You can find smarthomatic at www.smarthomatic.org.
# You can find FHEM at www.fhem.de. # You can find FHEM at www.fhem.de.
@ -226,6 +226,56 @@ sub setValue
SHC_util::setInt($byteArrayRef, $self->{_offset} + $self->{_arrayElementBits} * $index, $self->{_bits}, $value); SHC_util::setInt($byteArrayRef, $self->{_offset} + $self->{_arrayElementBits} * $index, $self->{_bits}, $value);
} }
# ----------- FloatValue class -----------
package FloatValue;
sub new
{
my $class = shift;
my $self = {
_id => shift,
_offset => shift,
_length => shift,
_arrayElementBits => shift
};
bless $self, $class;
return $self;
}
# reinterpret a float value as a 4-byte unsigned int which can be stored into a message
sub floatToUint32($)
{
my $b = pack 'f', shift();
my $str = reverse unpack "h*", $b;
return hex($str);
}
# reinterpret a 4-byte unsigned int received from a device as a float value
sub uintToFloat($)
{
my $u = shift();
my @bytes = ( $u >> 24, ($u >> 16) & 0xff, ($u >> 8) & 0xff, $u & 0xff);
return unpack 'f', pack 'C4', reverse @bytes;
}
sub getValue
{
my ($self, $byteArrayRef, $index) = @_;
my $uint32 = SHC_util::getUInt($byteArrayRef, $self->{_offset} + $self->{_arrayElementBits} * $index, 32);
my $float = uintToFloat($uint32);
return $float;
}
sub setValue
{
my ($self, $byteArrayRef, $value, $index) = @_;
my $uint32 = floatToUint32($value);
SHC_util::setUInt($byteArrayRef, $self->{_offset} + $self->{_arrayElementBits} * $index, 32, $uint32);
}
# ----------- BoolValue class ----------- # ----------- BoolValue class -----------
package BoolValue; package BoolValue;
@ -262,8 +312,8 @@ sub setValue
package EnumValue; package EnumValue;
my %name2value = (); # my %name2value = ();
my %value2name = (); # my %value2name = ();
sub new sub new
{ {
@ -273,7 +323,9 @@ sub new
_offset => shift, _offset => shift,
_bits => shift, _bits => shift,
_length => shift, _length => shift,
_arrayElementBits => shift _arrayElementBits => shift,
_name2value => {},
_value2name => {}
}; };
bless $self, $class; bless $self, $class;
return $self; return $self;
@ -283,8 +335,8 @@ sub addValue
{ {
my ($self, $name, $value) = @_; my ($self, $name, $value) = @_;
$name2value{$name} = $value; $self->{_name2value}{$name} = $value;
$value2name{$value} = $name; $self->{_value2name}{$value} = $name;
} }
sub getValue sub getValue
@ -292,14 +344,14 @@ sub getValue
my ($self, $byteArrayRef, $index) = @_; my ($self, $byteArrayRef, $index) = @_;
my $value = SHC_util::getUInt($byteArrayRef, $self->{_offset} + $self->{_arrayElementBits} * $index, $self->{_bits}); my $value = SHC_util::getUInt($byteArrayRef, $self->{_offset} + $self->{_arrayElementBits} * $index, $self->{_bits});
return $value2name{$value}; return $self->{_value2name}{$value};
} }
sub setValue sub setValue
{ {
my ($self, $byteArrayRef, $name, $index) = @_; my ($self, $byteArrayRef, $name, $index) = @_;
my $value = $name2value{$name}; my $value = $self->{_name2value}{$name};
SHC_util::setUInt($byteArrayRef, $self->{_offset} + $self->{_arrayElementBits} * $index, $self->{_bits}, $value); SHC_util::setUInt($byteArrayRef, $self->{_offset} + $self->{_arrayElementBits} * $index, $self->{_bits}, $value);
} }

View File

@ -3,7 +3,7 @@
########################################################################## ##########################################################################
# This file is part of the smarthomatic module for FHEM. # This file is part of the smarthomatic module for FHEM.
# #
# Copyright (c) 2014 Uwe Freese # Copyright (c) 2014, 2015, 2019 Uwe Freese
# #
# You can find smarthomatic at www.smarthomatic.org. # You can find smarthomatic at www.smarthomatic.org.
# You can find FHEM at www.fhem.de. # You can find FHEM at www.fhem.de.
@ -30,11 +30,12 @@
# Receiving packets: # Receiving packets:
# ------------------ # ------------------
# 1.) Receive string from base station (over UART). # 1.) Receive string from base station (over UART).
# 2.) Parse received string: # 2.) Check CRC (last 8 characters, optional).
# $parser->parse("Packet Data: SenderID=22;..."); # 3.) Parse received string:
# 3.) Get MessageGroupName: my $grp = $parser->getMessageGroupName(); # $parser->parse("PKT:SID=22;...");
# 4.) Get MessageName: my $msg = $parser->getMessageName(); # 4.) Get MessageGroupName: my $grp = $parser->getMessageGroupName();
# 5.) Get data fields depending on MessageGroupName and MessageName, e.g. # 5.) Get MessageName: my $msg = $parser->getMessageName();
# 6.) Get data fields depending on MessageGroupName and MessageName, e.g.
# $val = $parser->getField("Temperature"); # $val = $parser->getField("Temperature");
# #
# Sending packets: # Sending packets:
@ -44,6 +45,7 @@
# 2.) Set fields: # 2.) Set fields:
# $parser->setField("PowerSwitch", "SwitchState", "TimeoutSec", 8); # $parser->setField("PowerSwitch", "SwitchState", "TimeoutSec", 8);
# 3.) Get send string: $str = $parser->getSendString($receiverID); # 3.) Get send string: $str = $parser->getSendString($receiverID);
# It includes a CRC32 as last 8 characters.
# 4.) Send string to base station (over UART). # 4.) Send string to base station (over UART).
########################################################################## ##########################################################################
# $Id$ # $Id$
@ -54,6 +56,7 @@ use strict;
use feature qw(switch); use feature qw(switch);
use XML::LibXML; use XML::LibXML;
use SHC_datafields; use SHC_datafields;
use Digest::CRC qw(crc32); # linux packet libdigest-crc-perl
# Hash for data field definitions. # Hash for data field definitions.
my %dataFields = (); my %dataFields = ();
@ -123,6 +126,18 @@ sub init_datafield_positions_noarray($$$$$)
$offset += $bits; $offset += $bits;
} }
when ('FloatValue') {
my $id = ($field->findnodes("ID"))[0]->textContent;
my $bits = 32;
# print "Data field " . $id . " starts at " . $offset . " with " . $bits . " bits.\n";
$dataFields{$messageGroupID . "-" . $messageID . "-" . $id} =
new FloatValue($id, $offset, $arrayLength, $arrayElementBits);
$offset += $bits;
}
when ('BoolValue') { when ('BoolValue') {
my $id = ($field->findnodes("ID"))[0]->textContent; my $id = ($field->findnodes("ID"))[0]->textContent;
my $bits = 1; my $bits = 1;
@ -149,6 +164,8 @@ sub init_datafield_positions_noarray($$$$$)
my $name = ($element->findnodes("Name"))[0]->textContent; my $name = ($element->findnodes("Name"))[0]->textContent;
$object->addValue($name, $value); $object->addValue($name, $value);
# print "Enum value " . $value . " -> " . $name . "\n";
} }
$offset += $bits; $offset += $bits;
@ -167,7 +184,7 @@ sub init_datafield_positions_array($$$)
calc_array_bits_ovr($field); # number of bits for one struct ("set of sub-elements") in a structured array calc_array_bits_ovr($field); # number of bits for one struct ("set of sub-elements") in a structured array
# print "Next field is an array with " . $arrayLength . " elements (" . $arrayElementBits . " ovr bits per array element)!\n"; # print "Next field is an array with " . $arrayLength . " elements (" . $arrayElementBits . " ovr bits per array element)!\n";
for my $subfield ($field->findnodes("UIntValue|IntValue|BoolValue|EnumValue")) { for my $subfield ($field->findnodes("UIntValue|IntValue|FloatValue|BoolValue|EnumValue")) {
my $bits = my $bits =
init_datafield_positions_noarray($messageGroupID, $messageID, $subfield, $arrayLength, $arrayElementBits); init_datafield_positions_noarray($messageGroupID, $messageID, $subfield, $arrayLength, $arrayElementBits);
} }
@ -189,6 +206,10 @@ sub calc_array_bits_ovr($)
$bits += ($subfield->findnodes("Bits"))[0]->textContent; $bits += ($subfield->findnodes("Bits"))[0]->textContent;
} }
for my $subfield ($field->findnodes("FloatValue")) {
$bits += 32;
}
return $bits; return $bits;
} }
@ -223,7 +244,7 @@ sub init_datafield_positions()
$offset = 0; $offset = 0;
for my $field ($message->findnodes("Array|UIntValue|IntValue|BoolValue|EnumValue")) { for my $field ($message->findnodes("Array|UIntValue|IntValue|FloatValue|BoolValue|EnumValue")) {
# When an array is detected, remember the array length and change the current field node # When an array is detected, remember the array length and change the current field node
# to the inner node for further processing. # to the inner node for further processing.
@ -250,10 +271,10 @@ sub parse
if ( if (
( (
$msg =~ $msg =~
/^Packet Data: SenderID=(\d*);PacketCounter=(\d*);MessageType=(\d*);MessageGroupID=(\d*);MessageID=(\d*);MessageData=([^;]*);.*/ /^PKT:SID=(\d+);PC=(\d+);MT=(\d+);MGID=(\d+);MID=(\d+);MD=([^;]+);.*/
) )
|| ($msg =~ || ($msg =~
/^Packet Data: SenderID=(\d*);PacketCounter=(\d*);MessageType=(\d*);AckSenderID=\d*;AckPacketCounter=\d*;Error=\d*;MessageGroupID=(\d*);MessageID=(\d*);MessageData=([^;]*);.*/ /^PKT:SID=(\d+);PC=(\d+);MT=(\d+);ASID=\d+;APC=\d+;E=\d+;MGID=(\d+);MID=(\d+);MD=([^;]+);.*/
) )
) )
{ {
@ -311,6 +332,9 @@ sub getMessageData
$res .= sprintf("%02X", $_); $res .= sprintf("%02X", $_);
} }
# strip trailing zeros (pairwise)
$res =~ s/(00)+$//;
return $res; return $res;
} else { } else {
return $self->{_messageData}; return $self->{_messageData};
@ -365,8 +389,8 @@ sub setField
$obj->setValue(\@msgData, $value, $index); $obj->setValue(\@msgData, $value, $index);
} }
# sKK01RRRRGGMMDD # cKK01RRRRGGMMDD{CRC32}
# s0001003D3C0164 = SET Dimmer Switch Brightness 50% # c0001003D3C0164 = SET Dimmer Switch Brightness 50%
sub getSendString sub getSendString
{ {
my ($self, $receiverID, $aesKeyNr) = @_; my ($self, $receiverID, $aesKeyNr) = @_;
@ -382,13 +406,15 @@ sub getSendString
$aesKeyNr = 0; $aesKeyNr = 0;
} }
my $s = "s" my $s = "c"
. sprintf("%02X", $aesKeyNr) . sprintf("%02X", $aesKeyNr)
. sprintf("%02X", $self->{_messageTypeID}) . sprintf("%02X", $self->{_messageTypeID})
. sprintf("%04X", $receiverID) . sprintf("%04X", $receiverID)
. sprintf("%02X", $self->{_messageGroupID}) . sprintf("%02X", $self->{_messageGroupID})
. sprintf("%02X", $self->{_messageID}) . sprintf("%02X", $self->{_messageID})
. getMessageData(); . getMessageData();
return $s . sprintf("%08x", crc32($s));
} }
1; 1;

View File

@ -260,6 +260,10 @@
<Value>80</Value> <Value>80</Value>
<Name>Thermostat</Name> <Name>Thermostat</Name>
</Element> </Element>
<Element>
<Value>90</Value>
<Name>TeaMaker</Name>
</Element>
</EnumValue> </EnumValue>
<UIntValue> <UIntValue>
<ID>VersionMajor</ID> <ID>VersionMajor</ID>
@ -290,6 +294,34 @@
<MaxVal>4294967295</MaxVal> <MaxVal>4294967295</MaxVal>
</UIntValue> </UIntValue>
</Message> </Message>
<Message>
<Name>HardwareError</Name>
<Description>Reports detected problems with the hardware.</Description>
<MessageID>3</MessageID>
<MessageType>8</MessageType>
<Validity>test</Validity>
<EnumValue>
<ID>ErrorCode</ID>
<Description>Lists the type of error that occurred.</Description>
<Bits>8</Bits>
<Element>
<Value>0</Value>
<Name>ExternalReset</Name>
</Element>
<Element>
<Value>1</Value>
<Name>BrownOutReset</Name>
</Element>
<Element>
<Value>2</Value>
<Name>WatchdogReset</Name>
</Element>
<Element>
<Value>3</Value>
<Name>TransceiverWatchdogReset</Name>
</Element>
</EnumValue>
</Message>
<Message> <Message>
<Name>BatteryStatus</Name> <Name>BatteryStatus</Name>
<Description>Tells the current battery status in percent. Please note that the "Get" may not be answered because a device does not listen to requests.</Description> <Description>Tells the current battery status in percent. Please note that the "Get" may not be answered because a device does not listen to requests.</Description>
@ -555,6 +587,47 @@
<MaxVal>16383</MaxVal> <MaxVal>16383</MaxVal>
</UIntValue> </UIntValue>
</Message> </Message>
<Message>
<Name>ParticulateMatter</Name>
<Description/>
<MessageID>3</MessageID>
<MessageType>0</MessageType>
<MessageType>8</MessageType>
<MessageType>10</MessageType>
<Validity>test</Validity>
<UIntValue>
<ID>TypicalParticleSize</ID>
<Description>Typical Particle Size [1/100 μm]. Use 1023 when value invalid.</Description>
<Bits>10</Bits>
<MinVal>0</MinVal>
<MaxVal>1023</MaxVal>
</UIntValue>
<Array>
<Length>5</Length>
<UIntValue>
<ID>Size</ID>
<Description>Maximum particle size [1/10 µm] which is considered in the MassConcentration and NumberConcentration. Use 0 when the array element is not used.</Description>
<Bits>8</Bits>
<MinVal>0</MinVal>
<MaxVal>255</MaxVal>
<DefaultVal>0</DefaultVal>
</UIntValue>
<UIntValue>
<ID>MassConcentration</ID>
<Description>Mass concentration [1/10 μg/m3], considering the defined size. Use 1023 when the value is invalid.</Description>
<Bits>10</Bits>
<MinVal>0</MinVal>
<MaxVal>1023</MaxVal>
</UIntValue>
<UIntValue>
<ID>NumberConcentration</ID>
<Description>Number concentration [1/10 #/cm3], considering the defined size. Use 4095 when the value is invalid.</Description>
<Bits>12</Bits>
<MinVal>0</MinVal>
<MaxVal>4095</MaxVal>
</UIntValue>
</Array>
</Message>
</MessageGroup> </MessageGroup>
<MessageGroup> <MessageGroup>
<Name>PowerSwitch</Name> <Name>PowerSwitch</Name>