2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

36_WMBUS: add preliminary support for Letrika solar inverters

git-svn-id: https://svn.fhem.de/fhem/trunk@18847 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
kaihs 2019-03-10 12:48:20 +00:00
parent ac9d29375c
commit 8009dc6143
3 changed files with 189 additions and 12 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- feature: 36_WMBUS: add preliminary support for Letrika solar inverters
- bugfix: 14_SD_WS07.pm: fix bug autocreate define loop,
broken definitions will be repaired. You need so save your config!
removed rssi, slider for offsets and battery reading

View File

@ -34,6 +34,7 @@ sub WMBUS_Initialize($) {
" ignore:0,1".
" rawmsg_as_reading:0,1".
" ignoreUnknownDataBlocks:0,1".
" ignoreMasterMessages:0,1".
" $readingFnAttributes";
}
@ -238,6 +239,11 @@ WMBUS_Parse($$)
($msg, $rssi, $hash->{MessageEncoding}) = WMBUS_HandleEncoding($mb, $rawMsg);
if (uc(substr($msg, 0, 8)) eq "1144FF03") {
Log3 $name, 2, "received possible KNX-RF message, ignoring it";
return undef;
}
if ($mb->parseLinkLayer(pack('H*',substr($msg,1)))) {
$addr = join("_", $mb->{manufacturer}, $mb->{afield_id}, $mb->{afield_ver}, $mb->{afield_type});
@ -329,7 +335,11 @@ sub WMBUS_SetReadings($$$)
readingsBeginUpdate($hash);
if ($mb->{decrypted}) {
if ($mb->{decrypted} &&
# decode messages sent from master to slave/meter only if it is explictly enabled
( $mb->{sent_from_master} == 0 || AttrVal($name, "ignoreMasterMessages", 1) )
)
{
my $dataBlocks = $mb->{datablocks};
my $dataBlock;
@ -533,6 +543,10 @@ WMBUS_Attr(@)
formats of which some can be interpreted and some not. This prevents the unknown data overwriting the readings of the data that can be
interpreted.
</li>
<li>ignoreMasterMessages
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>
</ul>
<br>
<a name="WMBUSreadings"></a>
@ -647,6 +661,10 @@ WMBUS_Attr(@)
Wenn auf 1 gesetzt so werden Datenblocks die unbekannte/herstellerspezifische Daten enthalten ignoriert. Das ist hilfreich wenn ein Z&auml;hler Daten in unterschiedlichen
Formaten sendet von denen einige nicht interpretiert werden k&ouml;nnen. Es verhindert, dass die unbekannten Daten die Readings der interpretierbaren Daten &uuml;berschreiben.
</li>
<li>ignoreMasterMessages
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>
</ul>
<br>
<a name="WMBUSreadings"></a>

View File

@ -50,6 +50,7 @@ use constant {
CI_AFL => 0x90, # Authentification and Fragmentation Layer, variable size
CI_RESP_SML_4 => 0x7e, # Response from device, 4 Bytes, application layer SML encoded
CI_RESP_SML_12 => 0x7f, # Response from device, 12 Bytes, application layer SML encoded
CI_SND_UD_MODE_1 => 0x51, # The master can send data to a slave using a SND_UD with CI-Field 51h for mode 1 or 55h for mode 2
# DIF types (Data Information Field), see page 32
DIF_NONE => 0x00,
@ -546,6 +547,22 @@ my %VIFInfo_FD = (
unit => '',
calcFunc => \&valueCalcNumeric,
},
VIF_MANUFACTURER => { # Manufacturer (as in fixed header)
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00001010,
bias => 0,
unit => '',
calcFunc => \&valueCalcNumeric,
},
VIF_PARAMETER_SET_ID => { # Parameter set identification
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00001011,
bias => 0,
unit => '',
calcFunc => \&valueCalcNumeric,
},
VIF_MODEL_VERSION => { # Model / Version
typeMask => 0b01111111,
expMask => 0b00000000,
@ -554,6 +571,30 @@ my %VIFInfo_FD = (
unit => '',
calcFunc => \&valueCalcNumeric,
},
VIF_HARDWARE_VERSION => { # Hardware version #
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00001101,
bias => 0,
unit => '',
calcFunc => \&valueCalcNumeric,
},
VIF_FIRMWARE_VERSION => { # Firmware version #
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00001110,
bias => 0,
unit => '',
calcFunc => \&valueCalcNumeric,
},
VIF_SOFTWARE_VERSION => { # Software version #
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00001111,
bias => 0,
unit => '',
calcFunc => \&valueCalcNumeric,
},
VIF_ERROR_FLAGS => { # Error flags (binary)
typeMask => 0b01111111,
expMask => 0b00000000,
@ -586,7 +627,7 @@ my %VIFInfo_FD = (
unit => 'A',
calcFunc => \&valueCalcNumeric,
},
VIF_RECEPTION_LEVEL => { # reception level of a received radio device.
VIF_RECEPTION_LEVEL => { # reception level of a received radio device.
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b01110001,
@ -594,6 +635,23 @@ my %VIFInfo_FD = (
unit => 'dBm',
calcFunc => \&valueCalcNumeric,
},
VIF_STATE_PARAMETER_ACTIVATION => { # State of parameter activation
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b01100110,
bias => 0,
unit => '',
calcFunc => \&valueCalcNumeric,
},
VIF_SPECIAL_SUPPLIER_INFORMATION => { # Special supplier information
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b01100111,
bias => 0,
unit => '',
calcFunc => \&valueCalcNumeric,
},
VIF_FD_RESERVED => { # Reserved
typeMask => 0b01110000,
expMask => 0b00000000,
@ -642,6 +700,13 @@ my %VIFInfo_other = (
unit => 'Illegal VIF-Group',
},
VIF_DATA_UNDERFLOW => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00010111,
bias => 0,
unit => 'Data underflow',
},
VIF_PER_SECOND => {
@ -723,6 +788,90 @@ my %VIFInfo_other = (
bias => 0,
unit => 'per liter',
},
VIF_PER_M3 => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00101101,
bias => 0,
unit => 'per m³',
},
VIF_PER_KG => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00101110,
bias => 0,
unit => 'per kg',
},
VIF_PER_K => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00101111,
bias => 0,
unit => 'per K',
},
VIF_PER_KWH => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00110000,
bias => 0,
unit => 'per kWh',
},
VIF_PER_GJ => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00110001,
bias => 0,
unit => 'per GJ',
},
VIF_PER_KW => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00110010,
bias => 0,
unit => 'per kW',
},
VIF_PER_KL => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00110011,
bias => 0,
unit => 'per (K*l)',
},
VIF_PER_V => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00110100,
bias => 0,
unit => 'per V',
},
VIF_PER_A => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00110101,
bias => 0,
unit => 'per A',
},
VIF_PER_MULT_S => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00110110,
bias => 0,
unit => 'multiplied by sek',
},
VIF_PER_MULT_SV => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00110111,
bias => 0,
unit => 'multiplied by sek / V',
},
VIF_PER_MULT_SA => {
typeMask => 0b01111111,
expMask => 0b00000000,
type => 0b00111000,
bias => 0,
unit => 'multiplied by sek / A',
},
VIF_START_DATE_TIME => {
typeMask => 0b01111111,
@ -1162,7 +1311,7 @@ sub decodeValueInformationBlock($$$) {
# Plaintext VIF
my $vifLength = unpack('C', substr($vib,$offset++,1));
$dataBlockRef->{type} = "see unit";
$dataBlockRef->{unit} = unpack(sprintf("C%d",$vifLength), substr($vib, $offset, $vifLength));
$dataBlockRef->{unit} = substr($vib, $offset, $vifLength);
$offset += $vifLength;
$analyzeVIF = 0;
last EXTENSION;
@ -1207,7 +1356,13 @@ sub decodeValueInformationBlock($$$) {
}
if ($analyzeVIF) {
if (findVIF($vif, $vifInfoRef, $dataBlockRef) == 0) {
if ($vif == 0x7C) {
# Plaintext VIF
my $vifLength = unpack('C', substr($vib,$offset++,1));
$dataBlockRef->{type} = "see unit";
$dataBlockRef->{unit} = substr($vib, $offset, $vifLength);
$offset += $vifLength;
} elsif (findVIF($vif, $vifInfoRef, $dataBlockRef) == 0) {
$dataBlockRef->{errormsg} = "unknown VIF " . sprintf("%x", $vifExtension) . " at offset " . ($offset-1);
$dataBlockRef->{errorcode} = ERR_UNKNOWN_VIFE;
}
@ -1327,11 +1482,9 @@ sub decodePayload($$) {
$offset += $self->decodeDataRecordHeader(substr($payload,$offset), $dataBlock);
#printf("No. %d, type %x at offset %d\n", $dataBlockNo, $dataBlock->{dataField}, $offset-1);
if ($dataBlock->{dataField} == DIF_NONE) {
} elsif ($dataBlock->{dataField} == DIF_READOUT) {
$self->{errormsg} = "in datablock $dataBlockNo: unexpected DIF_READOUT";
$self->{errorcode} = ERR_UNKNOWN_DATAFIELD;
return 0;
if ($dataBlock->{dataField} == DIF_NONE or $dataBlock->{dataField} == DIF_READOUT) {
$dataBlockNo--;
$offset++;
} elsif ($dataBlock->{dataField} == DIF_BCD2) {
$value = $self->decodeBCD(2, substr($payload,$offset,1));
$offset += 1;
@ -1407,7 +1560,7 @@ sub decodePayload($$) {
#print "DIF_SPECIAL at $offset\n";
$value = unpack("H*", substr($payload,$offset));
last PAYLOAD;
} else {
} else {
$self->{errormsg} = "in datablock $dataBlockNo: unhandled datafield " . sprintf("%x",$dataBlock->{dataField});
$self->{errorcode} = ERR_UNKNOWN_DATAFIELD;
return 0;
@ -1744,20 +1897,23 @@ sub decodeApplicationLayer($) {
$self->{status} = 0;
$self->{statusstring} = "";
$self->{access_no} = 0;
$self->{sent_from_master} = 0;
#printf("CI Field %02x\n", $self->{cifield});
if ($self->{cifield} == CI_RESP_4 || $self->{cifield} == CI_RESP_SML_4) {
# Short header
#print "short header\n";
($self->{access_no}, $self->{status}, $self->{cw_1}, $self->{cw_2}) = unpack('CCCC', substr($applicationlayer,$offset));
#printf("Short header access_no %x\n", $self->{access_no});
$offset += 4;
} elsif ($self->{cifield} == CI_RESP_12 || $self->{cifield} == CI_RESP_SML_12) {
# Long header
#print "Long header\n";
($self->{meter_id}, $self->{meter_man}, $self->{meter_vers}, $self->{meter_dev}, $self->{access_no}, $self->{status}, $self->{cw_1}, $self->{cw_2})
= unpack('VvCCCCCC', 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}));
#printf("Long header access_no %x\n", $self->{access_no});
$offset += 12;
} elsif ($self->{cifield} == CI_RESP_0) {
# no header
@ -1802,6 +1958,8 @@ sub decodeApplicationLayer($) {
$self->{errorcode} = ERR_CRC_FAILED;
return 0;
}
} elsif ($self->{cifield} == CI_SND_UD_MODE_1) {
$self->{sent_from_master} = 1;
} else {
# unsupported
$self->decodeConfigword();