mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-22 08:11:44 +00:00
WMBus: some more fixes for Easymeter, new attribute useVIFasReadingName, new set rawmsg
git-svn-id: https://svn.fhem.de/fhem/trunk@20084 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
c69ee18b06
commit
3eacd88eeb
@ -22,7 +22,7 @@ sub WMBUS_Initialize($) {
|
|||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
$hash->{Match} = "^b.*";
|
$hash->{Match} = "^b.*";
|
||||||
#$hash->{SetFn} = "WMBUS_Set";
|
$hash->{SetFn} = "WMBUS_Set";
|
||||||
#$hash->{GetFn} = "WMBUS_Get";
|
#$hash->{GetFn} = "WMBUS_Get";
|
||||||
$hash->{DefFn} = "WMBUS_Define";
|
$hash->{DefFn} = "WMBUS_Define";
|
||||||
$hash->{UndefFn} = "WMBUS_Undef";
|
$hash->{UndefFn} = "WMBUS_Undef";
|
||||||
@ -35,7 +35,9 @@ sub WMBUS_Initialize($) {
|
|||||||
" rawmsg_as_reading:0,1".
|
" rawmsg_as_reading:0,1".
|
||||||
" ignoreUnknownDataBlocks:0,1".
|
" ignoreUnknownDataBlocks:0,1".
|
||||||
" ignoreMasterMessages:0,1".
|
" ignoreMasterMessages:0,1".
|
||||||
" $readingFnAttributes";
|
" useVIFasReadingName:0,1".
|
||||||
|
" $readingFnAttributes"
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub
|
||||||
@ -74,7 +76,7 @@ WMBUS_Define($$)
|
|||||||
my $rssi;
|
my $rssi;
|
||||||
|
|
||||||
if(@a != 6 && @a != 3) {
|
if(@a != 6 && @a != 3) {
|
||||||
my $msg = "wrong syntax: define <name> WMBUS [<ManufacturerID> <SerialNo> <Version> <Type> [<MessageEncoding>]]|b<HexMessage>";
|
my $msg = "wrong syntax: define <name> WMBUS <ManufacturerID> <SerialNo> <Version> <Type> [<MessageEncoding>]|b[<MessageEncoding>]<HexMessage>";
|
||||||
Log3 undef, 2, $msg;
|
Log3 undef, 2, $msg;
|
||||||
return $msg;
|
return $msg;
|
||||||
}
|
}
|
||||||
@ -167,14 +169,7 @@ WMBUS_Define($$)
|
|||||||
if (defined($mb)) {
|
if (defined($mb)) {
|
||||||
|
|
||||||
if ($mb->parseApplicationLayer()) {
|
if ($mb->parseApplicationLayer()) {
|
||||||
if ($mb->{cifield} == WMBus::CI_RESP_12) {
|
|
||||||
$hash->{Meter_Id} = $mb->{meter_id};
|
|
||||||
$hash->{Meter_Manufacturer} = $mb->{meter_manufacturer};
|
|
||||||
$hash->{Meter_Version} = $mb->{meter_vers};
|
|
||||||
$hash->{Meter_Dev} = $mb->{meter_devtypestring};
|
|
||||||
$hash->{Access_No} = $mb->{access_no};
|
|
||||||
$hash->{Status} = $mb->{status};
|
|
||||||
}
|
|
||||||
WMBUS_SetReadings($hash, $name, $mb);
|
WMBUS_SetReadings($hash, $name, $mb);
|
||||||
} else {
|
} else {
|
||||||
$hash->{Error} = $mb->{errormsg};
|
$hash->{Error} = $mb->{errormsg};
|
||||||
@ -235,6 +230,8 @@ WMBUS_Parse($$)
|
|||||||
|
|
||||||
Log3 $name, 5, "WMBUS raw msg " . $rawMsg;
|
Log3 $name, 5, "WMBUS raw msg " . $rawMsg;
|
||||||
|
|
||||||
|
$hash->{internal}{rawMsg} = $rawMsg;
|
||||||
|
|
||||||
my $mb = new WMBus;
|
my $mb = new WMBus;
|
||||||
|
|
||||||
($msg, $rssi, $hash->{MessageEncoding}) = WMBUS_HandleEncoding($mb, $rawMsg);
|
($msg, $rssi, $hash->{MessageEncoding}) = WMBUS_HandleEncoding($mb, $rawMsg);
|
||||||
@ -333,6 +330,15 @@ sub WMBUS_SetReadings($$$)
|
|||||||
my @list;
|
my @list;
|
||||||
push(@list, $name);
|
push(@list, $name);
|
||||||
|
|
||||||
|
if ($mb->{cifield} == WMBus::CI_RESP_12) {
|
||||||
|
$hash->{Meter_Id} = $mb->{meter_id};
|
||||||
|
$hash->{Meter_Manufacturer} = $mb->{meter_manufacturer};
|
||||||
|
$hash->{Meter_Version} = $mb->{meter_vers};
|
||||||
|
$hash->{Meter_Dev} = $mb->{meter_devtypestring};
|
||||||
|
$hash->{Access_No} = $mb->{access_no};
|
||||||
|
$hash->{Status} = $mb->{status};
|
||||||
|
}
|
||||||
|
|
||||||
readingsBeginUpdate($hash);
|
readingsBeginUpdate($hash);
|
||||||
|
|
||||||
if ($mb->{decrypted} &&
|
if ($mb->{decrypted} &&
|
||||||
@ -342,19 +348,33 @@ sub WMBUS_SetReadings($$$)
|
|||||||
{
|
{
|
||||||
my $dataBlocks = $mb->{datablocks};
|
my $dataBlocks = $mb->{datablocks};
|
||||||
my $dataBlock;
|
my $dataBlock;
|
||||||
|
my $readingBase;
|
||||||
|
my $useVIFasReadingName = defined($hash->{internal}{useVIFasReadingName}) ?
|
||||||
|
$hash->{internal}{useVIFasReadingName} : AttrVal($name, "useVIFasReadingName", 0);
|
||||||
|
|
||||||
for $dataBlock ( @$dataBlocks ) {
|
for $dataBlock ( @$dataBlocks ) {
|
||||||
next if AttrVal($name, "ignoreUnknownDataBlocks", 0) && $dataBlock->{type} eq 'MANUFACTURER SPECIFIC'; #WMBus::VIF_TYPE_MANUFACTURER_SPECIFIC
|
next if AttrVal($name, "ignoreUnknownDataBlocks", 0) && $dataBlock->{type} eq 'MANUFACTURER SPECIFIC'; #WMBus::VIF_TYPE_MANUFACTURER_SPECIFIC
|
||||||
readingsBulkUpdate($hash, "$dataBlock->{number}_storage_no", $dataBlock->{storageNo});
|
if ($useVIFasReadingName) {
|
||||||
readingsBulkUpdate($hash, "$dataBlock->{number}_type", $dataBlock->{type});
|
$readingBase = "$dataBlock->{storageNo}_$dataBlock->{type}";
|
||||||
readingsBulkUpdate($hash, "$dataBlock->{number}_value", $dataBlock->{value});
|
if (defined($dataBlock->{extension_value})) {
|
||||||
readingsBulkUpdate($hash, "$dataBlock->{number}_unit", $dataBlock->{unit});
|
$readingBase .= "_$dataBlock->{extension_value}";
|
||||||
readingsBulkUpdate($hash, "$dataBlock->{number}_value_type", $dataBlock->{functionFieldText});
|
}
|
||||||
if (defined($dataBlock->{extension})) {
|
} else {
|
||||||
readingsBulkUpdate($hash, "$dataBlock->{number}_extension", $dataBlock->{extension});
|
$readingBase = $dataBlock->{number};
|
||||||
|
readingsBulkUpdate($hash, "${readingBase}_type", $dataBlock->{type});
|
||||||
|
readingsBulkUpdate($hash, "${readingBase}_storage_no", $dataBlock->{storageNo});
|
||||||
|
if (defined($dataBlock->{extension_value})) {
|
||||||
|
readingsBulkUpdate($hash, "${readingBase}_extension_value", $dataBlock->{extension_value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readingsBulkUpdate($hash, "${readingBase}_value", $dataBlock->{value});
|
||||||
|
readingsBulkUpdate($hash, "${readingBase}_unit", $dataBlock->{unit});
|
||||||
|
readingsBulkUpdate($hash, "${readingBase}_value_type", $dataBlock->{functionFieldText});
|
||||||
|
if (defined($dataBlock->{extension_unit})) {
|
||||||
|
readingsBulkUpdate($hash, "${readingBase}_extension_unit", $dataBlock->{extension_unit});
|
||||||
}
|
}
|
||||||
if ($dataBlock->{errormsg}) {
|
if ($dataBlock->{errormsg}) {
|
||||||
readingsBulkUpdate($hash, "$dataBlock->{number}_errormsg", $dataBlock->{errormsg});
|
readingsBulkUpdate($hash, "${readingBase}_errormsg", $dataBlock->{errormsg});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
readingsBulkUpdate($hash, "batteryState", $mb->{status} & 4 ? "low" : "ok");
|
readingsBulkUpdate($hash, "batteryState", $mb->{status} & 4 ? "low" : "ok");
|
||||||
@ -422,14 +442,18 @@ WMBUS_Set($@)
|
|||||||
my $name = shift @a;
|
my $name = shift @a;
|
||||||
my $cmd = shift @a;
|
my $cmd = shift @a;
|
||||||
my $arg = join(" ", @a);
|
my $arg = join(" ", @a);
|
||||||
|
my $list = "rawmsg";
|
||||||
|
|
||||||
|
# only for Letrika solar inverters
|
||||||
my $list = "resetAccumulatedPower";
|
$list .= " requestCurrentPower requestTotalEnergy" if $hash->{Manufacturer} eq 'LET' and $hash->{DeviceType} == 2;
|
||||||
return $list if( $cmd eq '?' || $cmd eq '');
|
return $list if( $cmd eq '?' || $cmd eq '');
|
||||||
|
|
||||||
|
|
||||||
if($cmd eq "resetAccumulatedPower") {
|
if ($cmd eq 'rawmsg') {
|
||||||
CommandAttr(undef, "$name accumulatedPowerOffset " . $hash->{READINGS}{accumulatedPowerMeasured}{VAL});
|
WMBUS_Parse($hash, 'b'.$arg);
|
||||||
|
} elsif ($cmd eq "requestCurrentPower") {
|
||||||
|
IOWrite($hash, "", "bss");
|
||||||
|
} elsif ($cmd eq "requestTotalEnergy") {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return "Unknown argument $cmd, choose one of ".$list;
|
return "Unknown argument $cmd, choose one of ".$list;
|
||||||
@ -451,7 +475,17 @@ WMBUS_Attr(@)
|
|||||||
} else {
|
} else {
|
||||||
$msg = "AESkey must be a 32 digit hexadecimal value";
|
$msg = "AESkey must be a 32 digit hexadecimal value";
|
||||||
}
|
}
|
||||||
|
} elsif ($attrName eq 'useVIFasReadingName') {
|
||||||
|
if ($attrVal ne AttrVal($name, 'useVIFasReadingName', '0')) {
|
||||||
|
# delete all readings on change of namimg format
|
||||||
|
fhem "deletereading $name .*";
|
||||||
|
# and recreate them
|
||||||
|
if (defined($hash->{internal}{rawMsg})) {
|
||||||
|
$hash->{internal}{useVIFasReadingName} = $attrVal;
|
||||||
|
WMBUS_Parse($hash, $hash->{internal}{rawMsg});
|
||||||
|
delete $hash->{internal}{useVIFasReadingName};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ($msg) ? $msg : undef;
|
return ($msg) ? $msg : undef;
|
||||||
}
|
}
|
||||||
@ -518,7 +552,13 @@ WMBUS_Attr(@)
|
|||||||
<br>
|
<br>
|
||||||
|
|
||||||
<a name="WMBUSset"></a>
|
<a name="WMBUSset"></a>
|
||||||
<b>Set</b> <ul>N/A</ul><br>
|
<b>Set</b>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
rawmsg hexadecimal contents of a raw message (without the leading b)<br>
|
||||||
|
Will be parsed as if the message has been received by the IODev. Mainly useful for debugging.
|
||||||
|
</li>
|
||||||
|
</ul><br>
|
||||||
<a name="WMBUSget"></a>
|
<a name="WMBUSget"></a>
|
||||||
<b>Get</b> <ul>N/A</ul><br>
|
<b>Get</b> <ul>N/A</ul><br>
|
||||||
|
|
||||||
@ -552,6 +592,25 @@ WMBUS_Attr(@)
|
|||||||
<li>ignoreMasterMessages<br>
|
<li>ignoreMasterMessages<br>
|
||||||
Some devices (e.g. Letrika solar inverters) only send data if they have received a special message from a master device.
|
Some devices (e.g. Letrika solar inverters) only send data if they have received a special message from a master device.
|
||||||
The messages sent by the master are ignored unless explictly enabled by this attribute.
|
The messages sent by the master are ignored unless explictly enabled by this attribute.
|
||||||
|
</li><br>
|
||||||
|
<a name="useVIFasReadingName"></a>
|
||||||
|
<li>useVIFasReadingName<br>
|
||||||
|
Some devices send several types of messages with different logical content. As the readings are normally numbered consecutively they will be overwitten
|
||||||
|
by blocks with a different semantic meaning.
|
||||||
|
If ths attribute is set to 1 the naming of the readings will be changed to start with storage number and VIF (Value Information Field) name.
|
||||||
|
Therefor each semantically different value will get a unique reading name.<br>
|
||||||
|
Example:<br>
|
||||||
|
<pre>
|
||||||
|
1_storage_no 0
|
||||||
|
1_type VIF_ENERGY_WATT
|
||||||
|
1_unit Wh
|
||||||
|
1_value 1234.5
|
||||||
|
</pre>
|
||||||
|
will be changed to<br>
|
||||||
|
<pre>
|
||||||
|
0_VIF_ENERGY_WATT_unit Wh
|
||||||
|
0_VIF_ENERGY_WATT_value 1234.5
|
||||||
|
</pre>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
<br>
|
||||||
@ -605,7 +664,7 @@ WMBUS_Attr(@)
|
|||||||
Andernfalls wird die Entschlüsselung fehlschlagen und es können keine relevanten Daten ausgelesen werden. Das Modul kann mit Security Profile A oder B (Mode 5 und 7) verschlüsselte Nachrichten entschlüsseln.
|
Andernfalls wird die Entschlüsselung fehlschlagen und es können keine relevanten Daten ausgelesen werden. Das Modul kann mit Security Profile A oder B (Mode 5 und 7) verschlüsselte Nachrichten entschlüsseln.
|
||||||
<br><br>
|
<br><br>
|
||||||
<b>Voraussetzungen</b><br>
|
<b>Voraussetzungen</b><br>
|
||||||
Dieses Modul benötigt die perl Module Digest::CRC, Crypt::Mode::CBC, Crypt::ModeL::CTR und Digest::CMAC (die letzten drei Module werden nur benötigt wenn verschlüsselte Nachrichten verarbeitet werden sollen).<br>
|
Dieses Modul benötigt die perl Module Digest::CRC, Crypt::Mode::CBC, Crypt::Mode::CTR und Digest::CMAC (die letzten drei Module werden nur benötigt wenn verschlüsselte Nachrichten verarbeitet werden sollen).<br>
|
||||||
Bei einem Debian basierten System können diese so installiert werden<br>
|
Bei einem Debian basierten System können diese so installiert werden<br>
|
||||||
<code>
|
<code>
|
||||||
sudo apt-get install libdigest-crc-perl<br>
|
sudo apt-get install libdigest-crc-perl<br>
|
||||||
@ -642,7 +701,13 @@ WMBUS_Attr(@)
|
|||||||
<br>
|
<br>
|
||||||
|
|
||||||
<a name="WMBUSset"></a>
|
<a name="WMBUSset"></a>
|
||||||
<b>Set</b> <ul>N/A</ul><br>
|
<b>Set</b>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
rawmsg Hexadezimaler Inhalt einer Rohnachricht (ohne führendes b)<br>
|
||||||
|
Wird interpretiert als ob die Nachricht von einem IODev empfangen worden wäre. Hauptsächlich nützlich zum debuggen.
|
||||||
|
</li>
|
||||||
|
</ul><br>
|
||||||
<a name="WMBUSget"></a>
|
<a name="WMBUSget"></a>
|
||||||
<b>Get</b> <ul>N/A</ul><br>
|
<b>Get</b> <ul>N/A</ul><br>
|
||||||
|
|
||||||
@ -676,6 +741,25 @@ WMBUS_Attr(@)
|
|||||||
Einige Geräte (z. B. Letrika Wechselrichter) senden nur dann Daten wenn sie eine spezielle Nachricht von einem Mastergerät erhalten haben.
|
Einige Geräte (z. B. Letrika Wechselrichter) senden nur dann Daten wenn sie eine spezielle Nachricht von einem Mastergerät erhalten haben.
|
||||||
Die Nachrichten von dem Master werden ignoriert es sei denn es wird explizit mit diesem Attribut eingeschaltet.
|
Die Nachrichten von dem Master werden ignoriert es sei denn es wird explizit mit diesem Attribut eingeschaltet.
|
||||||
</li>
|
</li>
|
||||||
|
<a name="useVIFasReadingName"></a>
|
||||||
|
<li>useVIFasReadingName<br>
|
||||||
|
Einige Geräte senden verschiedene Arten von Nachrichten mit logisch unterschiedlichem Inhalt. Da die Readings normalerweise aufsteigend nummeriert werden
|
||||||
|
können Readings durch semantisch unterschiedliche Readings überschrieben werden.
|
||||||
|
Wenn dieses Attribut auf 1 gesetzt ist ändert sich die Namenskonvention der Readings. Die Namen setzen sich dann aus der Storagenumber und dem
|
||||||
|
VIF (Value Information Field) zusammen. Dadurch bekommt jeder semantisch unterschiedliche Wert einen eindeutigen Readingnamen.
|
||||||
|
Beispiel:<br>
|
||||||
|
<pre>
|
||||||
|
1_storage_no 0
|
||||||
|
1_type VIF_ENERGY_WATT
|
||||||
|
1_unit Wh
|
||||||
|
1_value 1234.5
|
||||||
|
</pre>
|
||||||
|
wird zu<br>
|
||||||
|
<pre>
|
||||||
|
0_VIF_ENERGY_WATT_unit Wh
|
||||||
|
0_VIF_ENERGY_WATT_value 1234.5
|
||||||
|
</pre>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
<br>
|
||||||
<a name="WMBUSreadings"></a>
|
<a name="WMBUSreadings"></a>
|
||||||
|
@ -109,6 +109,13 @@ use constant {
|
|||||||
FRAME_TYPE_A => 'A',
|
FRAME_TYPE_A => 'A',
|
||||||
FRAME_TYPE_B => 'B',
|
FRAME_TYPE_B => 'B',
|
||||||
|
|
||||||
|
# content type (CC bits of configuration field)
|
||||||
|
# stored in $self->{cw_parts}{content}
|
||||||
|
CONTENT_STANDARD => 0b00, # Standard data message with unsigned variable meter data
|
||||||
|
CONTENT_STATIC => 0b10, # Static message (consists of parameter, OBIS definitions and other data points
|
||||||
|
# which are not frequently changed – see also 4.3.2.4).
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sub valueCalcNumeric($$) {
|
sub valueCalcNumeric($$) {
|
||||||
@ -193,7 +200,19 @@ sub valueCalcHex($$) {
|
|||||||
my $value = shift;
|
my $value = shift;
|
||||||
my $dataBlock = shift;
|
my $dataBlock = shift;
|
||||||
|
|
||||||
return sprintf("%x", $value);
|
return unpack("H*", $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub valueCalcAscii($$) {
|
||||||
|
my $value = shift;
|
||||||
|
my $dataBlock = shift;
|
||||||
|
|
||||||
|
my $result = unpack('a*',$value);
|
||||||
|
|
||||||
|
# replace non printable chars
|
||||||
|
$result =~ s/[\x00-\x1f\x7f-\xff]/?/g;
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub valueCalcu($$) {
|
sub valueCalcu($$) {
|
||||||
@ -453,7 +472,7 @@ my %VIFInfo = (
|
|||||||
type => 0b01111000,
|
type => 0b01111000,
|
||||||
bias => 0,
|
bias => 0,
|
||||||
unit => '',
|
unit => '',
|
||||||
calcFunc => \&valueCalcNumeric,
|
calcFunc => \&valueCalcAscii,
|
||||||
},
|
},
|
||||||
VIF_OWNER_NO => { # Eigentumsnummer (used by Easymeter even though the standard allows this only for writing to a slave)
|
VIF_OWNER_NO => { # Eigentumsnummer (used by Easymeter even though the standard allows this only for writing to a slave)
|
||||||
typeMask => 0b01111111,
|
typeMask => 0b01111111,
|
||||||
@ -556,7 +575,7 @@ my %VIFInfo_FD = (
|
|||||||
type => 0b00001001,
|
type => 0b00001001,
|
||||||
bias => 0,
|
bias => 0,
|
||||||
unit => '',
|
unit => '',
|
||||||
calcFunc => \&valueCalcNumeric,
|
calcFunc => \&valueCalcAscii,
|
||||||
},
|
},
|
||||||
VIF_MANUFACTURER => { # Manufacturer (as in fixed header)
|
VIF_MANUFACTURER => { # Manufacturer (as in fixed header)
|
||||||
typeMask => 0b01111111,
|
typeMask => 0b01111111,
|
||||||
@ -606,6 +625,57 @@ my %VIFInfo_FD = (
|
|||||||
unit => '',
|
unit => '',
|
||||||
calcFunc => \&valueCalcNumeric,
|
calcFunc => \&valueCalcNumeric,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
VIF_CUSTOMER_LOCATION => { # Customer location
|
||||||
|
typeMask => 0b01111111,
|
||||||
|
expMask => 0b00000000,
|
||||||
|
type => 0b00010000,
|
||||||
|
bias => 0,
|
||||||
|
unit => '',
|
||||||
|
calcFunc => \&valueCalcHex
|
||||||
|
},
|
||||||
|
VIF_CUSTOMER_CUSTOMER => { # Customer
|
||||||
|
typeMask => 0b01111111,
|
||||||
|
expMask => 0b00000000,
|
||||||
|
type => 0b00010001,
|
||||||
|
bias => 0,
|
||||||
|
unit => '',
|
||||||
|
calcFunc => \&valueCalcHex
|
||||||
|
},
|
||||||
|
VIF_ACCESS_CODE_USER => { # Access code user
|
||||||
|
typeMask => 0b01111111,
|
||||||
|
expMask => 0b00000000,
|
||||||
|
type => 0b00010010,
|
||||||
|
bias => 0,
|
||||||
|
unit => '',
|
||||||
|
calcFunc => \&valueCalcHex
|
||||||
|
},
|
||||||
|
VIF_ACCESS_CODE_OPERATOR => { # Access code operator
|
||||||
|
typeMask => 0b01111111,
|
||||||
|
expMask => 0b00000000,
|
||||||
|
type => 0b00010011,
|
||||||
|
bias => 0,
|
||||||
|
unit => '',
|
||||||
|
calcFunc => \&valueCalcHex
|
||||||
|
},
|
||||||
|
VIF_ACCESS_CODE_SYSTEM_OPERATOR => { # Access code system operator
|
||||||
|
typeMask => 0b01111111,
|
||||||
|
expMask => 0b00000000,
|
||||||
|
type => 0b00010100,
|
||||||
|
bias => 0,
|
||||||
|
unit => '',
|
||||||
|
calcFunc => \&valueCalcHex
|
||||||
|
},
|
||||||
|
VIF_PASSWORD => { # Password
|
||||||
|
typeMask => 0b01111111,
|
||||||
|
expMask => 0b00000000,
|
||||||
|
type => 0b00010110,
|
||||||
|
bias => 0,
|
||||||
|
unit => '',
|
||||||
|
calcFunc => \&valueCalcHex
|
||||||
|
},
|
||||||
|
|
||||||
VIF_ERROR_FLAGS => { # Error flags (binary)
|
VIF_ERROR_FLAGS => { # Error flags (binary)
|
||||||
typeMask => 0b01111111,
|
typeMask => 0b01111111,
|
||||||
expMask => 0b00000000,
|
expMask => 0b00000000,
|
||||||
@ -960,6 +1030,9 @@ my %VIFInfo_ESY = (
|
|||||||
unit => 'W',
|
unit => 'W',
|
||||||
calcFunc => \&valueCalcNumeric,
|
calcFunc => \&valueCalcNumeric,
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
my %VIFInfo_ESY2 = (
|
||||||
VIF_ELECTRIC_POWER_PHASE_NO => {
|
VIF_ELECTRIC_POWER_PHASE_NO => {
|
||||||
typeMask => 0b01111110,
|
typeMask => 0b01111110,
|
||||||
expMask => 0b00000000,
|
expMask => 0b00000000,
|
||||||
@ -1346,8 +1419,10 @@ sub decodeValueInformationBlock($$$) {
|
|||||||
if ($self->{manufacturer} eq 'ESY') {
|
if ($self->{manufacturer} eq 'ESY') {
|
||||||
# Easymeter
|
# Easymeter
|
||||||
$vif = unpack('C', substr($vib,$offset++,1));
|
$vif = unpack('C', substr($vib,$offset++,1));
|
||||||
|
#printf("ESY VIF %x\n", $vif);
|
||||||
$vifInfoRef = \%VIFInfo_ESY;
|
$vifInfoRef = \%VIFInfo_ESY;
|
||||||
} elsif ($self->{manufacturer} eq 'KAM') {
|
} elsif ($self->{manufacturer} eq 'KAM') {
|
||||||
|
# Kamstrup
|
||||||
$vif = unpack('C', substr($vib,$offset++,1));
|
$vif = unpack('C', substr($vib,$offset++,1));
|
||||||
$vifInfoRef = \%VIFInfo_KAM;
|
$vifInfoRef = \%VIFInfo_KAM;
|
||||||
} else {
|
} else {
|
||||||
@ -1363,7 +1438,8 @@ sub decodeValueInformationBlock($$$) {
|
|||||||
#print "other extension\n";
|
#print "other extension\n";
|
||||||
$dataBlockExt = {};
|
$dataBlockExt = {};
|
||||||
if ($self->{manufacturer} eq 'ESY') {
|
if ($self->{manufacturer} eq 'ESY') {
|
||||||
$vifInfoRef = \%VIFInfo_ESY;
|
#print "ESY\n";
|
||||||
|
$vifInfoRef = \%VIFInfo_ESY2;
|
||||||
$dataBlockExt->{value} = unpack('C',substr($vib,2,1)) * 100;
|
$dataBlockExt->{value} = unpack('C',substr($vib,2,1)) * 100;
|
||||||
} else {
|
} else {
|
||||||
$dataBlockExt->{value} = $vif;
|
$dataBlockExt->{value} = $vif;
|
||||||
@ -1386,7 +1462,7 @@ sub decodeValueInformationBlock($$$) {
|
|||||||
# Plaintext VIF
|
# Plaintext VIF
|
||||||
$offset = $self->decodePlaintext($vib, $dataBlockRef, $offset);
|
$offset = $self->decodePlaintext($vib, $dataBlockRef, $offset);
|
||||||
} elsif (findVIF($vif, $vifInfoRef, $dataBlockRef) == 0) {
|
} elsif (findVIF($vif, $vifInfoRef, $dataBlockRef) == 0) {
|
||||||
$dataBlockRef->{errormsg} = "unknown VIF " . sprintf("%x", $vifExtension) . " at offset " . ($offset-1);
|
$dataBlockRef->{errormsg} = "unknown VIFE " . sprintf("%x", $vifExtension) . " at offset " . ($offset-1);
|
||||||
$dataBlockRef->{errorcode} = ERR_UNKNOWN_VIFE;
|
$dataBlockRef->{errorcode} = ERR_UNKNOWN_VIFE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1564,10 +1640,7 @@ sub decodePayload($$) {
|
|||||||
#print "VALUE: " . $value . "\n";
|
#print "VALUE: " . $value . "\n";
|
||||||
} else {
|
} else {
|
||||||
# ASCII string with LVAR characters
|
# ASCII string with LVAR characters
|
||||||
$value = unpack('a*',substr($payload, $offset, $lvar));
|
$value = valueCalcAscii(substr($payload, $offset, $lvar), $dataBlock);
|
||||||
|
|
||||||
# replace non printable chars
|
|
||||||
$value =~ s/[\x00-\x1f\x7f]/?/g;
|
|
||||||
|
|
||||||
if ($self->{manufacturer} eq 'ESY') {
|
if ($self->{manufacturer} eq 'ESY') {
|
||||||
# Easymeter stores the string backwards!
|
# Easymeter stores the string backwards!
|
||||||
@ -1610,12 +1683,13 @@ sub decodePayload($$) {
|
|||||||
|
|
||||||
my $VIFExtensions = $dataBlock->{VIFExtensions};
|
my $VIFExtensions = $dataBlock->{VIFExtensions};
|
||||||
for my $VIFExtension (@$VIFExtensions) {
|
for my $VIFExtension (@$VIFExtensions) {
|
||||||
$dataBlock->{extension} = $VIFExtension->{unit};
|
$dataBlock->{extension_unit} = $VIFExtension->{unit};
|
||||||
|
#printf("extension unit %s\n", $dataBlock->{extension_unit});
|
||||||
if (defined $VIFExtension->{calcFunc}) {
|
if (defined $VIFExtension->{calcFunc}) {
|
||||||
#printf("Extension value %d, valueFactor %d\n", $VIFExtension->{value}, $VIFExtension->{valueFactor});
|
#printf("Extension value %d, valueFactor %d\n", $VIFExtension->{value}, $VIFExtension->{valueFactor});
|
||||||
$dataBlock->{extension} .= ", " . $VIFExtension->{calcFunc}->($VIFExtension->{value}, $dataBlock);
|
$dataBlock->{extension_value} = $VIFExtension->{calcFunc}->($VIFExtension->{value}, $dataBlock);
|
||||||
} elsif (defined $VIFExtension->{value}) {
|
} elsif (defined $VIFExtension->{value}) {
|
||||||
$dataBlock->{extension} .= ", " . sprintf("%x",$VIFExtension->{value});
|
$dataBlock->{extension_value} = sprintf("%x",$VIFExtension->{value});
|
||||||
} else {
|
} else {
|
||||||
#$dataBlock->{extension} = "";
|
#$dataBlock->{extension} = "";
|
||||||
}
|
}
|
||||||
@ -2312,4 +2386,62 @@ sub parseApplicationLayer($)
|
|||||||
return $self->decodeApplicationLayer();
|
return $self->decodeApplicationLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub dumpResult($)
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
if ($self->{linkLayerOk}) {
|
||||||
|
printf("Manufacturer %x %s\n", $self->{mfield}, $self->{manufacturer});
|
||||||
|
printf("IdentNumber %s\n", $self->{afield_id});
|
||||||
|
printf("Version %d\n", $self->{afield_ver});
|
||||||
|
printf("Type %x %s\n", $self->{afield_type}, $self->{typestring});
|
||||||
|
printf("IsEncrypted %d\n", $self->{isEncrypted});
|
||||||
|
|
||||||
|
printf("Status: %x %s\n", $self->{status}, $self->{statusstring});
|
||||||
|
if ($self->{cw_parts}{mode} == 5) {
|
||||||
|
print "Codeword:\n";
|
||||||
|
print "bidirectional: ". $self->{cw_parts}{bidirectional} . "\n";
|
||||||
|
print "accessability: ". $self->{cw_parts}{accessability} . "\n";
|
||||||
|
print "synchronous: $self->{cw_parts}{synchronous}\n";
|
||||||
|
print "mode: $self->{cw_parts}{mode}\n";
|
||||||
|
print "encrypted_blocks: $self->{cw_parts}{encrypted_blocks}\n";
|
||||||
|
print "content: $self->{cw_parts}{content}\n";
|
||||||
|
print "hops: $self->{cw_parts}{hops}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($self->{errorcode} == ERR_NO_ERROR) {
|
||||||
|
if ($self->{cifield} == CI_RESP_12) {
|
||||||
|
printf("Meter Id %d\n", $self->{meter_id});
|
||||||
|
printf("Meter Manufacturer %x %s\n", $self->{meter_man}, $self->manId2ascii($self->{meter_man}));
|
||||||
|
printf("Meter Version %d\n", $self->{meter_vers});
|
||||||
|
printf("Meter Dev %x %s\n", $self->{meter_dev}, $self->type2string($self->{meter_dev}));
|
||||||
|
printf("Access No %d\n", $self->{access_no});
|
||||||
|
printf("Status %x\n", $self->{status});
|
||||||
|
}
|
||||||
|
|
||||||
|
my $dataBlocks = $self->{datablocks};
|
||||||
|
my $dataBlock;
|
||||||
|
|
||||||
|
for $dataBlock ( @$dataBlocks ) {
|
||||||
|
#if ( $dataBlock->{type} eq "MANUFACTURER SPECIFIC") {
|
||||||
|
# print $dataBlock->{number} . " " . $dataBlock->{type} . "\n";
|
||||||
|
#} else {
|
||||||
|
print $dataBlock->{number} . ". StorageNo " . $dataBlock->{storageNo} . " " ;
|
||||||
|
print $dataBlock->{functionFieldText} . " ";
|
||||||
|
print $dataBlock->{type} . " " . $dataBlock->{value} . " " . $dataBlock->{unit};
|
||||||
|
if ($dataBlock->{errormsg}) {
|
||||||
|
print "(" . $dataBlock->{errormsg} . ")";
|
||||||
|
}
|
||||||
|
if (defined($dataBlock->{extension_unit})) {
|
||||||
|
print " [" . $dataBlock->{extension_unit} . ", " . $dataBlock->{extension_value} . "]";
|
||||||
|
}
|
||||||
|
print "\n";
|
||||||
|
#}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Error %d: %s\n", $self->{errorcode}, $self->{errormsg});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user