2
0
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:
kaihs 2014-09-06 17:21:57 +00:00
parent aca8e4e04e
commit 65e0d7f1ef
2 changed files with 79 additions and 40 deletions

View File

@ -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";
}
my $rname = $rhash->{NAME};
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 {

View File

@ -1,4 +1,4 @@
# $Id: $
# $Id$
package WMBus;
@ -20,7 +20,12 @@ sub manId2ascii($$);
use constant {
# sent by meter
# 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
ACC_NR => 0x47,
@ -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++) {
$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))) {
$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, $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
@ -1061,4 +1079,4 @@ sub parseApplicationLayer($)
return $self->decodeApplicationLayer();
}
1;
1;