2
0
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:
kaihs 2019-08-31 16:54:50 +00:00
parent c69ee18b06
commit 3eacd88eeb
2 changed files with 446 additions and 230 deletions

View File

@ -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&uuml;sselung fehlschlagen und es k&ouml;nnen keine relevanten Daten ausgelesen werden. Das Modul kann mit Security Profile A oder B (Mode 5 und 7) verschl&uuml;sselte Nachrichten entschl&uuml;sseln. Andernfalls wird die Entschl&uuml;sselung fehlschlagen und es k&ouml;nnen keine relevanten Daten ausgelesen werden. Das Modul kann mit Security Profile A oder B (Mode 5 und 7) verschl&uuml;sselte Nachrichten entschl&uuml;sseln.
<br><br> <br><br>
<b>Voraussetzungen</b><br> <b>Voraussetzungen</b><br>
Dieses Modul ben&ouml;tigt die perl Module Digest::CRC, Crypt::Mode::CBC, Crypt::ModeL::CTR und Digest::CMAC (die letzten drei Module werden nur ben&ouml;tigt wenn verschl&uuml;sselte Nachrichten verarbeitet werden sollen).<br> Dieses Modul ben&ouml;tigt die perl Module Digest::CRC, Crypt::Mode::CBC, Crypt::Mode::CTR und Digest::CMAC (die letzten drei Module werden nur ben&ouml;tigt wenn verschl&uuml;sselte Nachrichten verarbeitet werden sollen).<br>
Bei einem Debian basierten System k&ouml;nnen diese so installiert werden<br> Bei einem Debian basierten System k&ouml;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&uuml;hrendes b)<br>
Wird interpretiert als ob die Nachricht von einem IODev empfangen worden w&auml;re. Haupts&auml;chlich n&uuml;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&auml;te senden verschiedene Arten von Nachrichten mit logisch unterschiedlichem Inhalt. Da die Readings normalerweise aufsteigend nummeriert werden
k&ouml;nnen Readings durch semantisch unterschiedliche Readings &uuml;berschrieben werden.
Wenn dieses Attribut auf 1 gesetzt ist &auml;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>

View File

@ -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;