mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-25 03:39:21 +00:00
WMBUS: correct decoding of hour in timestamp, convert manufacturer to upper case, implemented more VIFs
git-svn-id: https://svn.fhem.de/fhem/trunk@6418 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
f9f71cf54c
commit
9b559ebb93
@ -114,7 +114,7 @@ WMBUS_Define($$)
|
|||||||
if ($mb->parseApplicationLayer()) {
|
if ($mb->parseApplicationLayer()) {
|
||||||
if ($mb->{cifield} == WMBus::CI_RESP_12) {
|
if ($mb->{cifield} == WMBus::CI_RESP_12) {
|
||||||
$hash->{Meter_Id} = $mb->{meter_id};
|
$hash->{Meter_Id} = $mb->{meter_id};
|
||||||
$hash->{Meter_Manufacturer} = $mb->manId2ascii($mb->{meter_man});
|
$hash->{Meter_Manufacturer} = $mb->{meter_manufacturer};
|
||||||
$hash->{Meter_Version} = $mb->{meter_vers};
|
$hash->{Meter_Version} = $mb->{meter_vers};
|
||||||
$hash->{Meter_Dev} = $mb->{meter_devtypestring};
|
$hash->{Meter_Dev} = $mb->{meter_devtypestring};
|
||||||
$hash->{Access_No} = $mb->{access_no};
|
$hash->{Access_No} = $mb->{access_no};
|
||||||
@ -241,6 +241,9 @@ sub WMBUS_SetReadings($$$)
|
|||||||
readingsBulkUpdate($hash, "$dataBlock->{number}:type", $dataBlock->{type});
|
readingsBulkUpdate($hash, "$dataBlock->{number}:type", $dataBlock->{type});
|
||||||
readingsBulkUpdate($hash, "$dataBlock->{number}:value", $dataBlock->{value});
|
readingsBulkUpdate($hash, "$dataBlock->{number}:value", $dataBlock->{value});
|
||||||
readingsBulkUpdate($hash, "$dataBlock->{number}:unit", $dataBlock->{unit});
|
readingsBulkUpdate($hash, "$dataBlock->{number}:unit", $dataBlock->{unit});
|
||||||
|
if ($dataBlock->{errormsg}) {
|
||||||
|
readingsBulkUpdate($hash, "$dataBlock->{number}:errormsg", $dataBlock->{errormsg});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ require Exporter;
|
|||||||
my @ISA = qw(Exporter);
|
my @ISA = qw(Exporter);
|
||||||
my @EXPORT = qw(new parse parseLinkLayer parseApplicationLayer manId2ascii type2string);
|
my @EXPORT = qw(new parse parseLinkLayer parseApplicationLayer manId2ascii type2string);
|
||||||
|
|
||||||
|
sub manId2ascii($$);
|
||||||
|
|
||||||
|
|
||||||
use constant {
|
use constant {
|
||||||
# sent by meter
|
# sent by meter
|
||||||
@ -128,10 +130,17 @@ sub valueCalcDateTime($$) {
|
|||||||
|
|
||||||
|
|
||||||
my $datePart = $value >> 16;
|
my $datePart = $value >> 16;
|
||||||
my $min = ($value & 0b111111);
|
my $timeInvalid = $value & 0b10000000;
|
||||||
my $hour = ($value & 0b11111) >> 6;
|
|
||||||
|
|
||||||
return valueCalcDate($datePart, $dataBlock) . sprintf(' %02d:%02d', $hour, $min);
|
my $dateTime = valueCalcDate($datePart, $dataBlock);
|
||||||
|
if ($timeInvalid == 0) {
|
||||||
|
my $min = ($value & 0b111111);
|
||||||
|
my $hour = ($value >> 6) & 0b11111;
|
||||||
|
my $su = ($value & 0b10000000000000000);
|
||||||
|
$dateTime .= sprintf(' %02d:%02d %s', $hour, $min, $su ? 'DST' : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub valueCalcHex($$) {
|
sub valueCalcHex($$) {
|
||||||
@ -331,8 +340,24 @@ my %VIFInfo = (
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
# Codes used with extension indicator $FD
|
# Codes used with extension indicator $FD, see 8.4.4 on page 80
|
||||||
my %VIFInfo_FD = (
|
my %VIFInfo_FD = (
|
||||||
|
VIF_CREDIT => { # Credit of 10nn-3 of the nominal local legal currency units
|
||||||
|
typeMask => 0b01111100,
|
||||||
|
expMask => 0b00000011,
|
||||||
|
type => 0b00000000,
|
||||||
|
bias => -3,
|
||||||
|
unit => '€',
|
||||||
|
calcFunc => \&valueCalcNumeric,
|
||||||
|
},
|
||||||
|
VIF_DEBIT => { # Debit of 10nn-3 of the nominal local legal currency units
|
||||||
|
typeMask => 0b01111100,
|
||||||
|
expMask => 0b00000011,
|
||||||
|
type => 0b00000100,
|
||||||
|
bias => -3,
|
||||||
|
unit => '€',
|
||||||
|
calcFunc => \&valueCalcNumeric,
|
||||||
|
},
|
||||||
VIF_ACCESS_NO => { # Access number (transmission count)
|
VIF_ACCESS_NO => { # Access number (transmission count)
|
||||||
typeMask => 0b01111111,
|
typeMask => 0b01111111,
|
||||||
expMask => 0b00000000,
|
expMask => 0b00000000,
|
||||||
@ -341,6 +366,14 @@ my %VIFInfo_FD = (
|
|||||||
unit => '',
|
unit => '',
|
||||||
calcFunc => \&valueCalcNumeric,
|
calcFunc => \&valueCalcNumeric,
|
||||||
},
|
},
|
||||||
|
VIF_MEDIUM => { # Medium (as in fixed header)
|
||||||
|
typeMask => 0b01111111,
|
||||||
|
expMask => 0b00000000,
|
||||||
|
type => 0b00001001,
|
||||||
|
bias => 0,
|
||||||
|
unit => '',
|
||||||
|
calcFunc => \&valueCalcNumeric,
|
||||||
|
},
|
||||||
VIF_MODEL_VERSION => { # Model / Version
|
VIF_MODEL_VERSION => { # Model / Version
|
||||||
typeMask => 0b01111111,
|
typeMask => 0b01111111,
|
||||||
expMask => 0b00000000,
|
expMask => 0b00000000,
|
||||||
@ -357,6 +390,30 @@ my %VIFInfo_FD = (
|
|||||||
unit => '',
|
unit => '',
|
||||||
calcFunc => \&valueCalcHex,
|
calcFunc => \&valueCalcHex,
|
||||||
},
|
},
|
||||||
|
VIF_VOLTAGE => { # 10nnnn-9 Volts
|
||||||
|
typeMask => 0b01110000,
|
||||||
|
expMask => 0b00001111,
|
||||||
|
type => 0b01000000,
|
||||||
|
bias => -9,
|
||||||
|
unit => 'V',
|
||||||
|
calcFunc => \&valueCalcNumeric,
|
||||||
|
},
|
||||||
|
VIF_ELECTRICAL_CURRENT => { # 10nnnn-12 Ampere
|
||||||
|
typeMask => 0b01110000,
|
||||||
|
expMask => 0b00001111,
|
||||||
|
type => 0b01010000,
|
||||||
|
bias => -12,
|
||||||
|
unit => 'A',
|
||||||
|
calcFunc => \&valueCalcNumeric,
|
||||||
|
},
|
||||||
|
VIF_RECEPTION_LEVEL => { # reception level of a received radio device.
|
||||||
|
typeMask => 0b01111111,
|
||||||
|
expMask => 0b00000000,
|
||||||
|
type => 0b01110001,
|
||||||
|
bias => 0,
|
||||||
|
unit => 'dBm',
|
||||||
|
calcFunc => \&valueCalcNumeric,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
# Codes used with extension indicator $FB
|
# Codes used with extension indicator $FB
|
||||||
@ -506,7 +563,7 @@ sub removeCRC($$)
|
|||||||
|
|
||||||
sub manId2hex($$)
|
sub manId2hex($$)
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $class = shift;
|
||||||
my $idascii = shift;
|
my $idascii = shift;
|
||||||
|
|
||||||
return (ord(substr($idascii,1,1))-64) << 10 | (ord(substr($idascii,2,1))-64) << 5 | (ord(substr($idascii,3,1))-64);
|
return (ord(substr($idascii,1,1))-64) << 10 | (ord(substr($idascii,2,1))-64) << 5 | (ord(substr($idascii,3,1))-64);
|
||||||
@ -514,7 +571,7 @@ sub manId2hex($$)
|
|||||||
|
|
||||||
sub manId2ascii($$)
|
sub manId2ascii($$)
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $class = shift;
|
||||||
my $idhex = shift;
|
my $idhex = shift;
|
||||||
|
|
||||||
return chr(($idhex >> 10) + 64) . chr((($idhex >> 5) & 0b00011111) + 64) . chr(($idhex & 0b00011111) + 64);
|
return chr(($idhex >> 10) + 64) . chr((($idhex >> 5) & 0b00011111) + 64) . chr(($idhex & 0b00011111) + 64);
|
||||||
@ -576,6 +633,7 @@ sub decodeValueInformationBlock($$$) {
|
|||||||
my $bias;
|
my $bias;
|
||||||
my $exponent;
|
my $exponent;
|
||||||
my $vifInfoRef = \%VIFInfo;
|
my $vifInfoRef = \%VIFInfo;
|
||||||
|
my $vifExtension = 0;
|
||||||
|
|
||||||
$vif = unpack('C', $vib);
|
$vif = unpack('C', $vib);
|
||||||
$offset = 1;
|
$offset = 1;
|
||||||
@ -584,18 +642,20 @@ sub decodeValueInformationBlock($$$) {
|
|||||||
|
|
||||||
if ($isExtension) {
|
if ($isExtension) {
|
||||||
# switch to extension codes
|
# switch to extension codes
|
||||||
if ($vif == 0xFD) {
|
$vifExtension = $vif;
|
||||||
|
if ($vifExtension == 0xFD) {
|
||||||
$vifInfoRef = \%VIFInfo_FD;
|
$vifInfoRef = \%VIFInfo_FD;
|
||||||
} elsif ($vif == 0xFB) {
|
} elsif ($vifExtension == 0xFB) {
|
||||||
$vifInfoRef = \%VIFInfo_FB;
|
$vifInfoRef = \%VIFInfo_FB;
|
||||||
} elsif ($vif == 0xFF) {
|
} elsif ($vifExtension == 0xFF) {
|
||||||
# manufacturer specific data, can't be interpreted
|
# manufacturer specific data, can't be interpreted
|
||||||
$dataBlockRef->{type} = "MANUFACTURER SPECIFIC";
|
$dataBlockRef->{type} = "MANUFACTURER SPECIFIC";
|
||||||
$dataBlockRef->{unit} = "";
|
$dataBlockRef->{unit} = "";
|
||||||
|
$dataBlockRef->{value} = "";
|
||||||
return $offset;
|
return $offset;
|
||||||
} else {
|
} else {
|
||||||
$self->{errormsg} = "unknown VIFE " . sprintf("%x", $vif) . " at offset $offset-1";
|
$dataBlockRef->{errormsg} = "unknown VIFE " . sprintf("%x", $vifExtension) . " at offset $offset-1";
|
||||||
$self->{errorcode} = ERR_UNKNOWN_VIFE;
|
$dataBlockRef->{errorcode} = ERR_UNKNOWN_VIFE;
|
||||||
}
|
}
|
||||||
$vif = unpack('C', substr($vib,$offset++,1));
|
$vif = unpack('C', substr($vib,$offset++,1));
|
||||||
}
|
}
|
||||||
@ -626,8 +686,8 @@ sub decodeValueInformationBlock($$$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($dataBlockRef->{type} eq '') {
|
if ($dataBlockRef->{type} eq '') {
|
||||||
$self->{errormsg} = "unknown VIF " . sprintf("%x",$vif);
|
$dataBlockRef->{errormsg} = sprintf("in VIFExtension %x unknown VIF %x",$vifExtension, $vif);
|
||||||
$self->{errorcode} = ERR_UNKNOWN_VIF;
|
$dataBlockRef->{errorcode} = ERR_UNKNOWN_VIF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $offset;
|
return $offset;
|
||||||
@ -663,8 +723,8 @@ sub decodeDataInformationBlock($$$) {
|
|||||||
$isExtension = $dif & DIF_EXTENSION_BIT;
|
$isExtension = $dif & DIF_EXTENSION_BIT;
|
||||||
$difExtNo++;
|
$difExtNo++;
|
||||||
if ($difExtNo > 10) {
|
if ($difExtNo > 10) {
|
||||||
$self->{errormsg} = 'too many DIFE';
|
$dataBlockRef->{errormsg} = 'too many DIFE';
|
||||||
$self->{errorcode} = ERR_TOO_MANY_DIFE;
|
$dataBlockRef->{errorcode} = ERR_TOO_MANY_DIFE;
|
||||||
last EXTENSION;
|
last EXTENSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -720,6 +780,7 @@ sub decodePayload($$) {
|
|||||||
# create a new anonymous hash reference
|
# create a new anonymous hash reference
|
||||||
$dataBlock = {};
|
$dataBlock = {};
|
||||||
$dataBlock->{number} = $dataBlockNo;
|
$dataBlock->{number} = $dataBlockNo;
|
||||||
|
$dataBlock->{unit} = '';
|
||||||
|
|
||||||
while (unpack('C',substr($payload,$offset,1)) == 0x2f) {
|
while (unpack('C',substr($payload,$offset,1)) == 0x2f) {
|
||||||
# skip filler bytes
|
# skip filler bytes
|
||||||
@ -731,7 +792,6 @@ sub decodePayload($$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$offset += $self->decodeDataRecordHeader(substr($payload,$offset), $dataBlock);
|
$offset += $self->decodeDataRecordHeader(substr($payload,$offset), $dataBlock);
|
||||||
|
|
||||||
#printf("No. %d, type %x at offset %d\n", $dataBlockNo, $dataBlock->{dataField}, $offset-1);
|
#printf("No. %d, type %x at offset %d\n", $dataBlockNo, $dataBlock->{dataField}, $offset-1);
|
||||||
|
|
||||||
if ($dataBlock->{dataField} == DIF_NONE || $dataBlock->{dataField} == DIF_READOUT) {
|
if ($dataBlock->{dataField} == DIF_NONE || $dataBlock->{dataField} == DIF_READOUT) {
|
||||||
@ -787,6 +847,8 @@ sub decodePayload($$) {
|
|||||||
if (defined $dataBlock->{calcFunc}) {
|
if (defined $dataBlock->{calcFunc}) {
|
||||||
$dataBlock->{value} = $dataBlock->{calcFunc}->($value, $dataBlock);
|
$dataBlock->{value} = $dataBlock->{calcFunc}->($value, $dataBlock);
|
||||||
#print "Value raw " . $value . " value calc " . $dataBlock->{value} ."\n";
|
#print "Value raw " . $value . " value calc " . $dataBlock->{value} ."\n";
|
||||||
|
} else {
|
||||||
|
$dataBlock->{value} = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
push @dataBlocks, $dataBlock;
|
push @dataBlocks, $dataBlock;
|
||||||
@ -840,7 +902,7 @@ sub decodeApplicationLayer($) {
|
|||||||
($self->{meter_id}, $self->{meter_man}, $self->{meter_vers}, $self->{meter_dev}, $self->{access_no}, $self->{status}, $self->{cw})
|
($self->{meter_id}, $self->{meter_man}, $self->{meter_vers}, $self->{meter_dev}, $self->{access_no}, $self->{status}, $self->{cw})
|
||||||
= unpack('VvCCCCn', substr($applicationlayer,$offset));
|
= unpack('VvCCCCn', substr($applicationlayer,$offset));
|
||||||
$self->{meter_devtypestring} = $validDeviceTypes{$self->{meter_dev}} || 'unknown';
|
$self->{meter_devtypestring} = $validDeviceTypes{$self->{meter_dev}} || 'unknown';
|
||||||
$self->{meter_manufacturer} = $self->manId2ascii($self->{meter_man});
|
# $self->{meter_manufacturer} = uc($self->manId2ascii($self->{meter_man}));
|
||||||
$offset += 12;
|
$offset += 12;
|
||||||
} else {
|
} else {
|
||||||
# unsupported
|
# unsupported
|
||||||
@ -919,7 +981,10 @@ sub decodeLinkLayer($$)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->{manufacturer} = $self->manId2ascii($self->{mfield});
|
# 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
|
||||||
|
$self->{manufacturer} = uc($self->manId2ascii($self->{mfield}));
|
||||||
$self->{typestring} = $validDeviceTypes{$self->{afield_type}} || 'unknown';
|
$self->{typestring} = $validDeviceTypes{$self->{afield_type}} || 'unknown';
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user