mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-07 22:29:19 +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) = @_;
|
||||
|
||||
$hash->{Match} = "^b.*";
|
||||
#$hash->{SetFn} = "WMBUS_Set";
|
||||
$hash->{SetFn} = "WMBUS_Set";
|
||||
#$hash->{GetFn} = "WMBUS_Get";
|
||||
$hash->{DefFn} = "WMBUS_Define";
|
||||
$hash->{UndefFn} = "WMBUS_Undef";
|
||||
@ -35,7 +35,9 @@ sub WMBUS_Initialize($) {
|
||||
" rawmsg_as_reading:0,1".
|
||||
" ignoreUnknownDataBlocks:0,1".
|
||||
" ignoreMasterMessages:0,1".
|
||||
" $readingFnAttributes";
|
||||
" useVIFasReadingName:0,1".
|
||||
" $readingFnAttributes"
|
||||
;
|
||||
}
|
||||
|
||||
sub
|
||||
@ -74,7 +76,7 @@ WMBUS_Define($$)
|
||||
my $rssi;
|
||||
|
||||
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;
|
||||
return $msg;
|
||||
}
|
||||
@ -167,14 +169,7 @@ WMBUS_Define($$)
|
||||
if (defined($mb)) {
|
||||
|
||||
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);
|
||||
} else {
|
||||
$hash->{Error} = $mb->{errormsg};
|
||||
@ -235,6 +230,8 @@ WMBUS_Parse($$)
|
||||
|
||||
Log3 $name, 5, "WMBUS raw msg " . $rawMsg;
|
||||
|
||||
$hash->{internal}{rawMsg} = $rawMsg;
|
||||
|
||||
my $mb = new WMBus;
|
||||
|
||||
($msg, $rssi, $hash->{MessageEncoding}) = WMBUS_HandleEncoding($mb, $rawMsg);
|
||||
@ -333,6 +330,15 @@ sub WMBUS_SetReadings($$$)
|
||||
my @list;
|
||||
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);
|
||||
|
||||
if ($mb->{decrypted} &&
|
||||
@ -342,19 +348,33 @@ sub WMBUS_SetReadings($$$)
|
||||
{
|
||||
my $dataBlocks = $mb->{datablocks};
|
||||
my $dataBlock;
|
||||
my $readingBase;
|
||||
my $useVIFasReadingName = defined($hash->{internal}{useVIFasReadingName}) ?
|
||||
$hash->{internal}{useVIFasReadingName} : AttrVal($name, "useVIFasReadingName", 0);
|
||||
|
||||
for $dataBlock ( @$dataBlocks ) {
|
||||
next if AttrVal($name, "ignoreUnknownDataBlocks", 0) && $dataBlock->{type} eq 'MANUFACTURER SPECIFIC'; #WMBus::VIF_TYPE_MANUFACTURER_SPECIFIC
|
||||
readingsBulkUpdate($hash, "$dataBlock->{number}_storage_no", $dataBlock->{storageNo});
|
||||
readingsBulkUpdate($hash, "$dataBlock->{number}_type", $dataBlock->{type});
|
||||
readingsBulkUpdate($hash, "$dataBlock->{number}_value", $dataBlock->{value});
|
||||
readingsBulkUpdate($hash, "$dataBlock->{number}_unit", $dataBlock->{unit});
|
||||
readingsBulkUpdate($hash, "$dataBlock->{number}_value_type", $dataBlock->{functionFieldText});
|
||||
if (defined($dataBlock->{extension})) {
|
||||
readingsBulkUpdate($hash, "$dataBlock->{number}_extension", $dataBlock->{extension});
|
||||
if ($useVIFasReadingName) {
|
||||
$readingBase = "$dataBlock->{storageNo}_$dataBlock->{type}";
|
||||
if (defined($dataBlock->{extension_value})) {
|
||||
$readingBase .= "_$dataBlock->{extension_value}";
|
||||
}
|
||||
} else {
|
||||
$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}) {
|
||||
readingsBulkUpdate($hash, "$dataBlock->{number}_errormsg", $dataBlock->{errormsg});
|
||||
readingsBulkUpdate($hash, "${readingBase}_errormsg", $dataBlock->{errormsg});
|
||||
}
|
||||
}
|
||||
readingsBulkUpdate($hash, "batteryState", $mb->{status} & 4 ? "low" : "ok");
|
||||
@ -422,14 +442,18 @@ WMBUS_Set($@)
|
||||
my $name = shift @a;
|
||||
my $cmd = shift @a;
|
||||
my $arg = join(" ", @a);
|
||||
my $list = "rawmsg";
|
||||
|
||||
|
||||
my $list = "resetAccumulatedPower";
|
||||
# only for Letrika solar inverters
|
||||
$list .= " requestCurrentPower requestTotalEnergy" if $hash->{Manufacturer} eq 'LET' and $hash->{DeviceType} == 2;
|
||||
return $list if( $cmd eq '?' || $cmd eq '');
|
||||
|
||||
|
||||
if($cmd eq "resetAccumulatedPower") {
|
||||
CommandAttr(undef, "$name accumulatedPowerOffset " . $hash->{READINGS}{accumulatedPowerMeasured}{VAL});
|
||||
if ($cmd eq 'rawmsg') {
|
||||
WMBUS_Parse($hash, 'b'.$arg);
|
||||
} elsif ($cmd eq "requestCurrentPower") {
|
||||
IOWrite($hash, "", "bss");
|
||||
} elsif ($cmd eq "requestTotalEnergy") {
|
||||
}
|
||||
else {
|
||||
return "Unknown argument $cmd, choose one of ".$list;
|
||||
@ -451,7 +475,17 @@ WMBUS_Attr(@)
|
||||
} else {
|
||||
$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;
|
||||
}
|
||||
@ -518,7 +552,13 @@ WMBUS_Attr(@)
|
||||
<br>
|
||||
|
||||
<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>
|
||||
<b>Get</b> <ul>N/A</ul><br>
|
||||
|
||||
@ -552,6 +592,25 @@ WMBUS_Attr(@)
|
||||
<li>ignoreMasterMessages<br>
|
||||
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.
|
||||
</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>
|
||||
</ul>
|
||||
<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.
|
||||
<br><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>
|
||||
<code>
|
||||
sudo apt-get install libdigest-crc-perl<br>
|
||||
@ -642,7 +701,13 @@ WMBUS_Attr(@)
|
||||
<br>
|
||||
|
||||
<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>
|
||||
<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.
|
||||
Die Nachrichten von dem Master werden ignoriert es sei denn es wird explizit mit diesem Attribut eingeschaltet.
|
||||
</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>
|
||||
<br>
|
||||
<a name="WMBUSreadings"></a>
|
||||
|
@ -109,6 +109,13 @@ use constant {
|
||||
FRAME_TYPE_A => 'A',
|
||||
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($$) {
|
||||
@ -193,7 +200,19 @@ sub valueCalcHex($$) {
|
||||
my $value = 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($$) {
|
||||
@ -453,7 +472,7 @@ my %VIFInfo = (
|
||||
type => 0b01111000,
|
||||
bias => 0,
|
||||
unit => '',
|
||||
calcFunc => \&valueCalcNumeric,
|
||||
calcFunc => \&valueCalcAscii,
|
||||
},
|
||||
VIF_OWNER_NO => { # Eigentumsnummer (used by Easymeter even though the standard allows this only for writing to a slave)
|
||||
typeMask => 0b01111111,
|
||||
@ -556,7 +575,7 @@ my %VIFInfo_FD = (
|
||||
type => 0b00001001,
|
||||
bias => 0,
|
||||
unit => '',
|
||||
calcFunc => \&valueCalcNumeric,
|
||||
calcFunc => \&valueCalcAscii,
|
||||
},
|
||||
VIF_MANUFACTURER => { # Manufacturer (as in fixed header)
|
||||
typeMask => 0b01111111,
|
||||
@ -606,6 +625,57 @@ my %VIFInfo_FD = (
|
||||
unit => '',
|
||||
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)
|
||||
typeMask => 0b01111111,
|
||||
expMask => 0b00000000,
|
||||
@ -960,6 +1030,9 @@ my %VIFInfo_ESY = (
|
||||
unit => 'W',
|
||||
calcFunc => \&valueCalcNumeric,
|
||||
},
|
||||
);
|
||||
|
||||
my %VIFInfo_ESY2 = (
|
||||
VIF_ELECTRIC_POWER_PHASE_NO => {
|
||||
typeMask => 0b01111110,
|
||||
expMask => 0b00000000,
|
||||
@ -1346,8 +1419,10 @@ sub decodeValueInformationBlock($$$) {
|
||||
if ($self->{manufacturer} eq 'ESY') {
|
||||
# Easymeter
|
||||
$vif = unpack('C', substr($vib,$offset++,1));
|
||||
#printf("ESY VIF %x\n", $vif);
|
||||
$vifInfoRef = \%VIFInfo_ESY;
|
||||
} elsif ($self->{manufacturer} eq 'KAM') {
|
||||
# Kamstrup
|
||||
$vif = unpack('C', substr($vib,$offset++,1));
|
||||
$vifInfoRef = \%VIFInfo_KAM;
|
||||
} else {
|
||||
@ -1363,7 +1438,8 @@ sub decodeValueInformationBlock($$$) {
|
||||
#print "other extension\n";
|
||||
$dataBlockExt = {};
|
||||
if ($self->{manufacturer} eq 'ESY') {
|
||||
$vifInfoRef = \%VIFInfo_ESY;
|
||||
#print "ESY\n";
|
||||
$vifInfoRef = \%VIFInfo_ESY2;
|
||||
$dataBlockExt->{value} = unpack('C',substr($vib,2,1)) * 100;
|
||||
} else {
|
||||
$dataBlockExt->{value} = $vif;
|
||||
@ -1386,7 +1462,7 @@ sub decodeValueInformationBlock($$$) {
|
||||
# Plaintext VIF
|
||||
$offset = $self->decodePlaintext($vib, $dataBlockRef, $offset);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
@ -1564,10 +1640,7 @@ sub decodePayload($$) {
|
||||
#print "VALUE: " . $value . "\n";
|
||||
} else {
|
||||
# ASCII string with LVAR characters
|
||||
$value = unpack('a*',substr($payload, $offset, $lvar));
|
||||
|
||||
# replace non printable chars
|
||||
$value =~ s/[\x00-\x1f\x7f]/?/g;
|
||||
$value = valueCalcAscii(substr($payload, $offset, $lvar), $dataBlock);
|
||||
|
||||
if ($self->{manufacturer} eq 'ESY') {
|
||||
# Easymeter stores the string backwards!
|
||||
@ -1610,12 +1683,13 @@ sub decodePayload($$) {
|
||||
|
||||
my $VIFExtensions = $dataBlock->{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}) {
|
||||
#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}) {
|
||||
$dataBlock->{extension} .= ", " . sprintf("%x",$VIFExtension->{value});
|
||||
$dataBlock->{extension_value} = sprintf("%x",$VIFExtension->{value});
|
||||
} else {
|
||||
#$dataBlock->{extension} = "";
|
||||
}
|
||||
@ -2312,4 +2386,62 @@ sub parseApplicationLayer($)
|
||||
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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user