mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-04 11:26:55 +00:00
WMBUS: change identification number to always have 8 digits (add leading
0 if necessary) preparation for decoding of RSSI/LQI (requires updated culfw/00_CUL.pm) git-svn-id: https://svn.fhem.de/fhem/trunk@6509 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
aca8e4e04e
commit
65e0d7f1ef
@ -1,7 +1,7 @@
|
||||
#
|
||||
# kaihs@FHEM_Forum (forum.fhem.de)
|
||||
#
|
||||
# $Id: $
|
||||
# $Id$
|
||||
#
|
||||
#
|
||||
|
||||
@ -39,6 +39,7 @@ use WMBus;
|
||||
|
||||
sub WMBUS_Parse($$);
|
||||
sub WMBUS_SetReadings($$$);
|
||||
sub WMBUS_SetRSSI($$$);
|
||||
|
||||
sub WMBUS_Initialize($) {
|
||||
my ($hash) = @_;
|
||||
@ -63,6 +64,7 @@ WMBUS_Define($$)
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
my $mb;
|
||||
my $rssi;
|
||||
|
||||
if(@a != 6 && @a != 3) {
|
||||
my $msg = "wrong syntax: define <name> WMBUS [<ManufacturerID> <SerialNo> <Version> <Type>]|b<HexMessage>";
|
||||
@ -76,6 +78,8 @@ WMBUS_Define($$)
|
||||
# unparsed message
|
||||
my $msg = $a[2];
|
||||
|
||||
($msg, $rssi) = split(/::/,$msg);
|
||||
|
||||
return "a WMBus message must be a least 12 bytes long" if $msg !~ m/b[a-zA-Z0-9]{24,}/;
|
||||
|
||||
$mb = new WMBus;
|
||||
@ -89,6 +93,7 @@ WMBUS_Define($$)
|
||||
} else {
|
||||
delete $hash->{Error};
|
||||
}
|
||||
WMBUS_SetRSSI($hash, $mb, $rssi);
|
||||
} else {
|
||||
return "failed to parse msg: $mb->{errormsg}";
|
||||
}
|
||||
@ -112,7 +117,7 @@ WMBUS_Define($$)
|
||||
}
|
||||
|
||||
$hash->{Manufacturer} = $a[2];
|
||||
$hash->{IdentNumber} = int($a[3]);
|
||||
$hash->{IdentNumber} = sprintf("%08d",$a[3]);
|
||||
$hash->{Version} = $a[4];
|
||||
$hash->{DeviceType} = $a[5];
|
||||
|
||||
@ -121,7 +126,7 @@ WMBUS_Define($$)
|
||||
|
||||
return "WMBUS device $addr already used for $modules{WMBUS}{defptr}{$addr}->{NAME}." if( $modules{WMBUS}{defptr}{$addr}
|
||||
&& $modules{WMBUS}{defptr}{$addr}->{NAME} ne $name );
|
||||
|
||||
$hash->{addr} = $addr;
|
||||
$modules{WMBUS}{defptr}{$addr} = $hash;
|
||||
|
||||
AssignIoPort($hash);
|
||||
@ -195,6 +200,7 @@ WMBUS_Parse($$)
|
||||
my $name = $hash->{NAME};
|
||||
my $addr;
|
||||
my $rhash;
|
||||
my $rssi;
|
||||
|
||||
# $hash is the hash of the IODev!
|
||||
|
||||
@ -205,6 +211,8 @@ WMBUS_Parse($$)
|
||||
|
||||
my $mb = new WMBus;
|
||||
|
||||
($msg, $rssi) = split(/::/,$msg);
|
||||
|
||||
if ($mb->parseLinkLayer(pack('H*',substr($msg,1)))) {
|
||||
$addr = join("_", $mb->{manufacturer}, $mb->{afield_id}, $mb->{afield_ver}, $mb->{afield_type});
|
||||
|
||||
@ -215,10 +223,11 @@ WMBUS_Parse($$)
|
||||
|
||||
return "UNDEFINED WMBUS_$addr WMBUS $msg";
|
||||
}
|
||||
WMBUS_SetRSSI($rhash, $mb, $rssi);
|
||||
|
||||
my $rname = $rhash->{NAME};
|
||||
my $aeskey;
|
||||
|
||||
|
||||
if ($aeskey = AttrVal($rname, 'AESkey', undef)) {
|
||||
$mb->{aeskey} = pack("H*",$aeskey);
|
||||
} else {
|
||||
@ -243,11 +252,22 @@ WMBUS_Parse($$)
|
||||
}
|
||||
}
|
||||
|
||||
sub WMBUS_SetReadings($$$)
|
||||
sub WMBUS_SetRSSI($$$) {
|
||||
my ($hash, $mb, $rssi) = @_;
|
||||
|
||||
readingsBeginUpdate($hash);
|
||||
# RSSI is decoded by 00_CUL.pm from the last byte of the message
|
||||
readingsBulkUpdate($hash, "RSSI", $rssi ? $rssi : 'unknown');
|
||||
if (defined $mb->{remainingData} && length($mb->{remainingData}) >= 1) {
|
||||
# if there is a trailing byte after the WMBUS message it is the LQI
|
||||
readingsBulkUpdate($hash, "LQI", unpack("C", $mb->{remainingData}));
|
||||
}
|
||||
readingsEndUpdate($hash,1);
|
||||
}
|
||||
|
||||
sub WMBUS_SetReadings($$$$)
|
||||
{
|
||||
my $hash = shift;
|
||||
my $name = shift;
|
||||
my $mb = shift;
|
||||
my ($hash, $name, $mb) = @_;
|
||||
|
||||
my @list;
|
||||
push(@list, $name);
|
||||
@ -270,6 +290,7 @@ sub WMBUS_SetReadings($$$)
|
||||
}
|
||||
readingsBulkUpdate($hash, "is_encrypted", $mb->{isEncrypted});
|
||||
readingsBulkUpdate($hash, "decryption_ok", $mb->{decrypted});
|
||||
|
||||
if ($mb->{decrypted}) {
|
||||
readingsBulkUpdate($hash, "state", $mb->{statusstring});
|
||||
} else {
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: $
|
||||
# $Id$
|
||||
|
||||
package WMBus;
|
||||
|
||||
@ -20,6 +20,11 @@ sub manId2ascii($$);
|
||||
|
||||
|
||||
use constant {
|
||||
# Link Layer block size
|
||||
LL_BLOCK_SIZE => 16,
|
||||
# size of CRC in bytes
|
||||
CRC_SIZE => 2,
|
||||
|
||||
# sent by meter
|
||||
SND_NR => 0x44, # Send, no reply
|
||||
SND_IR => 0x46, # Send installation request, must reply with CNF_IR
|
||||
@ -532,36 +537,38 @@ sub removeCRC($$)
|
||||
my $i;
|
||||
my $res;
|
||||
my $crc;
|
||||
my $blocksize = LL_BLOCK_SIZE;
|
||||
my $blocksize_with_crc = LL_BLOCK_SIZE + CRC_SIZE;
|
||||
my $crcoffset;
|
||||
|
||||
my $msgLen = length($msg);
|
||||
my $noOfBlocks = int($msgLen / 18);
|
||||
my $rest = $msgLen % 18;
|
||||
my $msgLen = $self->{datalen}; # size without CRCs
|
||||
my $noOfBlocks = $self->{datablocks}; # total number of data blocks, each with a CRC appended
|
||||
my $rest = $msgLen % LL_BLOCK_SIZE; # size of the last data block, can be smaller than 16 bytes
|
||||
|
||||
# each block is 16 bytes + 2 bytes CRC
|
||||
|
||||
#print "Länge " . $msgLen . "\n";
|
||||
#print "Länge $msgLen Anz. Blöcke $noOfBlocks rest $rest\n";
|
||||
|
||||
for ($i=0; $i < $noOfBlocks; $i++) {
|
||||
$crcoffset = $blocksize_with_crc * $i + LL_BLOCK_SIZE;
|
||||
#print "crc offset $crcoffset\n";
|
||||
if ($rest > 0 && $crcoffset + CRC_SIZE > ($noOfBlocks - 1) * $blocksize_with_crc + $rest) {
|
||||
# last block is smaller
|
||||
$crcoffset = ($noOfBlocks - 1) * $blocksize_with_crc + $rest;
|
||||
#print "last crc offset $crcoffset\n";
|
||||
$blocksize = $msgLen - ($i * $blocksize);
|
||||
}
|
||||
|
||||
$crc = unpack('n',substr($msg, 18*$i+16, 2));
|
||||
#printf("%d: CRC %x, calc %x\n", $i, $crc, $self->checkCRC(substr($msg, 18*$i, 16)));
|
||||
if ($crc != $self->checkCRC(substr($msg, 18*$i, 16))) {
|
||||
$crc = unpack('n',substr($msg, $crcoffset, CRC_SIZE));
|
||||
#printf("%d: CRC %x, calc %x blocksize $blocksize\n", $i, $crc, $self->checkCRC(substr($msg, $blocksize_with_crc*$i, $blocksize)));
|
||||
if ($crc != $self->checkCRC(substr($msg, $blocksize_with_crc*$i, $blocksize))) {
|
||||
$self->{errormsg} = "crc check failed for block $i";
|
||||
$self->{errorcode} = ERR_CRC_FAILED;
|
||||
return 0;
|
||||
}
|
||||
$res .= substr($msg, 18*$i, 16);
|
||||
$res .= substr($msg, $blocksize_with_crc*$i, $blocksize);
|
||||
}
|
||||
|
||||
if ($rest != 0) {
|
||||
$res .= substr($msg, $noOfBlocks*18, $rest - 2);
|
||||
$crc = unpack('n',substr($msg, $msgLen-2, 2));
|
||||
if ($crc != $self->checkCRC(substr($msg, $noOfBlocks*18, $rest - 2))) {
|
||||
$self->{errormsg} = "crc check failed for block $i";
|
||||
$self->{errorcode} = ERR_CRC_FAILED;
|
||||
#printf("rest %d: CRC %x, calc %x\n", $rest, $crc, $self->checkCRC(substr($msg, $noOfBlocks*18, $rest - 2)));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
@ -937,8 +944,9 @@ sub decodeApplicationLayer($) {
|
||||
#print "Long header\n";
|
||||
($self->{meter_id}, $self->{meter_man}, $self->{meter_vers}, $self->{meter_dev}, $self->{access_no}, $self->{status}, $self->{cw})
|
||||
= unpack('VvCCCCn', substr($applicationlayer,$offset));
|
||||
$self->{meter_id} = sprintf("%08d", $self->{meter_id});
|
||||
$self->{meter_devtypestring} = $validDeviceTypes{$self->{meter_dev}} || 'unknown';
|
||||
# $self->{meter_manufacturer} = uc($self->manId2ascii($self->{meter_man}));
|
||||
$self->{meter_manufacturer} = uc($self->manId2ascii($self->{meter_man}));
|
||||
$offset += 12;
|
||||
} else {
|
||||
# unsupported
|
||||
@ -970,13 +978,13 @@ sub decodeApplicationLayer($) {
|
||||
$self->{decrypted} = 1;
|
||||
} else {
|
||||
# Decryption verification failed
|
||||
$self->{errormsg} = 'Decryption failed';
|
||||
$self->{errormsg} = 'Decryption failed, wrong key?';
|
||||
$self->{errorcode} = ERR_DECRYPTION_FAILED;
|
||||
#printf("%x\n", unpack('n', $payload));
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
$self->{errormsg} = 'encrypted message and no aeskey given';
|
||||
$self->{errormsg} = 'encrypted message and no aeskey provided';
|
||||
$self->{errorcode} = ERR_NO_AESKEY;
|
||||
return 0;
|
||||
}
|
||||
@ -998,15 +1006,14 @@ sub decodeLinkLayer($$)
|
||||
my $self = shift;
|
||||
my $linklayer = shift;
|
||||
|
||||
$self->{datalen} = length($self->{msg}) - 12;
|
||||
$self->{datablocks} = $self->{datalen} / 18; # header block is 12 bytes, each following block is 16 bytes + 2 bytes CRC
|
||||
$self->{datablocks}++ if $self->{datalen} % 18 != 0;
|
||||
|
||||
|
||||
($self->{lfield}, $self->{cfield}, $self->{mfield}) = unpack('CCv', $linklayer);
|
||||
$self->{afield_id} = $self->decodeBCD(8,substr($linklayer,4,4));
|
||||
$self->{afield_id} = sprintf("%08d", $self->decodeBCD(8,substr($linklayer,4,4)));
|
||||
($self->{afield_ver}, $self->{afield_type}, $self->{crc0}) = unpack('CCn', substr($linklayer,8,4));
|
||||
|
||||
|
||||
|
||||
#printf("lfield %d\n", $self->{lfield});
|
||||
#printf("crc0 %x calc %x\n", $self->{crc0}, $self->checkCRC(substr($linklayer,0,10)));
|
||||
|
||||
@ -1017,6 +1024,17 @@ sub decodeLinkLayer($$)
|
||||
return 0;
|
||||
}
|
||||
|
||||
# header block is 12 bytes, each following block is 16 bytes + 2 bytes CRC, the last block may be smaller
|
||||
$self->{datalen} = $self->{lfield} - 9; # this is without CRCs and the lfield itself
|
||||
$self->{datablocks} = int($self->{datalen} / LL_BLOCK_SIZE);
|
||||
$self->{datablocks}++ if $self->{datalen} % LL_BLOCK_SIZE != 0;
|
||||
$self->{msglen} = 12 + $self->{datalen} + $self->{datablocks} * CRC_SIZE;
|
||||
|
||||
#printf("calc len %d, actual %d\n", $self->{msglen}, length($self->{msg}));
|
||||
if (length($self->{msg}) > $self->{msglen}) {
|
||||
$self->{remainingData} = substr($self->{msg},$self->{msglen});
|
||||
}
|
||||
|
||||
# according to the MBus spec only upper case letters are allowed.
|
||||
# some devices send lower case letters none the less
|
||||
# convert to upper case to make them spec conformant
|
||||
|
Loading…
x
Reference in New Issue
Block a user