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:
parent
49e4678921
commit
ee39392e14
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user