kopia lustrzana https://github.com/weetmuts/wmbusmeters
Now converted almost all meters.
rodzic
2a47115cb9
commit
fa1e08e7a3
|
@ -2,33 +2,33 @@
|
||||||
|
|
||||||
# full telegram, must come before short, so that the hash of the format signature can be remembered!
|
# full telegram, must come before short, so that the hash of the format signature can be remembered!
|
||||||
telegram=|2A442D2C998734761B168D208971F81821|542F7802FF2071000413F81800004413F4180000615B05616717|
|
telegram=|2A442D2C998734761B168D208971F81821|542F7802FF2071000413F81800004413F4180000615B05616717|
|
||||||
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.392000,"target_m3":6.388000,"max_flow_m3h":0.000000,"flow_temperature":5,"external_temperature":23,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.392,"target_m3":6.388,"max_flow_m3h":0,"flow_temperature_c":5,"external_temperature_c":23,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# short telegram
|
# short telegram
|
||||||
telegram=|23442D2C998734761B168D208870F81821|09EA79EDA869F57100F8180000F41800000318|
|
telegram=|23442D2C998734761B168D208870F81821|09EA79EDA869F57100F8180000F41800000318|
|
||||||
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.392000,"target_m3":6.388000,"max_flow_m3h":0.000000,"flow_temperature":3,"external_temperature":24,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.392,"target_m3":6.388,"max_flow_m3h":0,"flow_temperature_c":3,"external_temperature_c":24,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# Tets Multical21 C1 telegrams with maximum flow configuration
|
# Tets Multical21 C1 telegrams with maximum flow configuration
|
||||||
telegram=|2D442D2C776655441B168D2083B48D3A20|46887802FF20000004132F4E000092013B3D01A1015B028101E7FF0F03|
|
telegram=|2D442D2C776655441B168D2083B48D3A20|46887802FF20000004132F4E000092013B3D01A1015B028101E7FF0F03|
|
||||||
{"media":"cold water","meter":"multical21","name":"Vadden","id":"44556677","total_m3":20.015000,"target_m3":0.000000,"max_flow_m3h":0.317000,"flow_temperature":2,"external_temperature":3,"current_status":"","time_dry":"","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"cold water","meter":"multical21","name":"Vadden","id":"44556677","total_m3":20.015,"target_m3":0,"max_flow_m3h":0.317,"flow_temperature_c":2,"external_temperature_c":3,"current_status":"","time_dry":"","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
telegram=|21442D2C776655441B168D2079CC8C3A20|F4307912C40DFF00002F4E00003D010203|
|
telegram=|21442D2C776655441B168D2079CC8C3A20|F4307912C40DFF00002F4E00003D010203|
|
||||||
{"media":"cold water","meter":"multical21","name":"Vadden","id":"44556677","total_m3":20.015000,"target_m3":0.000000,"max_flow_m3h":0.317000,"flow_temperature":2,"external_temperature":3,"current_status":"","time_dry":"","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"cold water","meter":"multical21","name":"Vadden","id":"44556677","total_m3":20.015,"target_m3":0,"max_flow_m3h":0.317,"flow_temperature_c":2,"external_temperature_c":3,"current_status":"","time_dry":"","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# Test Multical302 C1 telegrams
|
# Test Multical302 C1 telegrams
|
||||||
|
|
||||||
# short telegram, this is not a proper telegram! Please provide the output from --logtelegrams for a Multical302 meter!
|
# short telegram, this is not a proper telegram! Please provide the output from --logtelegrams for a Multical302 meter!
|
||||||
|
|
||||||
telegram=|25442D2C785634121b048D2093E13CBA20|0000790000000000000000000000000000|
|
telegram=|25442D2C785634121b048D2093E13CBA20|0000790000000000000000000000000000|
|
||||||
{"media":"heat","meter":"multical302","name":"MyHeater","id":"12345678","total_kwh":0.000000,"total_volume_m3":0.000000,"current_kw":"0.000000","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"heat","meter":"multical302","name":"MyHeater","id":"12345678","total_kwh":0,"total_volume_m3":0,"current_kw":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# full telegram
|
# full telegram
|
||||||
|
|
||||||
# Test Omnipower C1 telegrams
|
# Test Omnipower C1 telegrams
|
||||||
|
|
||||||
telegram=|1E442D2C0771941501027AB3001080|04833B08340500|
|
telegram=|1E442D2C0771941501027AB3001080|04833B08340500|
|
||||||
{"media":"electricity","meter":"omnipower","name":"MyElectricity","id":"15947107","total_energy_consumption_kwh":341.000000,"current_power_consumption_kw":0.000000,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"electricity","meter":"omnipower","name":"MyElectricity","id":"15947107","total_energy_consumption_kwh":341,"current_power_consumption_kw":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# Test QCaloric C1 telegrams
|
# Test QCaloric C1 telegrams
|
||||||
|
|
||||||
telegram=|314493441234567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000c2086c7f21326cffff046d200b7422|
|
telegram=|314493441234567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000c2086c7f21326cffff046d200b7422|
|
||||||
{"media":"heat_cost_allocation","meter":"qcaloric","name":"MyElement","id":"78563412","current_consumption_hca":127.000000,"set_date":"2018-12-31","consumption_at_set_date_hca":145.000000,"set_date_17":"2019-01-31","consumption_at_set_date_17_hca":79.000000,"error_date":"2127-15-31","device_date_time":"2019-02-20 11:32","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"heat_cost_allocation","meter":"qcaloric","name":"MyElement","id":"78563412","current_consumption_hca":127,"set_date":"2018-12-31","consumption_at_set_date_hca":145,"set_date_17":"2019-01-31","consumption_at_set_date_17_hca":79,"error_date":"2127-15-31","device_date_time":"2019-02-20 11:32","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
telegram=|314493441234567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000c2086c7f21326cffff046d200b7422|
|
telegram=|314493441234567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000c2086c7f21326cffff046d200b7422|
|
||||||
{"media":"heat_cost_allocation","meter":"qcaloric","name":"Element","id":"78563412","current_consumption_hca":127.000000,"set_date":"2018-12-31","consumption_at_set_date_hca":145.000000,"set_date_17":"2019-01-31","consumption_at_set_date_17_hca":79.000000,"error_date":"2127-15-31","device_date_time":"2019-02-20 11:32","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"heat_cost_allocation","meter":"qcaloric","name":"Element","id":"78563412","current_consumption_hca":127,"set_date":"2018-12-31","consumption_at_set_date_hca":145,"set_date_17":"2019-01-31","consumption_at_set_date_17_hca":79,"error_date":"2127-15-31","device_date_time":"2019-02-20 11:32","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
telegram=|314493441334567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000c2086c7f21326cffff046d200b7422|
|
telegram=|314493441334567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000c2086c7f21326cffff046d200b7422|
|
||||||
{"media":"heat_cost_allocation","meter":"qcaloric","name":"Element","id":"78563413","current_consumption_hca":127.000000,"set_date":"2018-12-31","consumption_at_set_date_hca":145.000000,"set_date_17":"2019-01-31","consumption_at_set_date_17_hca":79.000000,"error_date":"2127-15-31","device_date_time":"2019-02-20 11:32","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"heat_cost_allocation","meter":"qcaloric","name":"Element","id":"78563413","current_consumption_hca":127,"set_date":"2018-12-31","consumption_at_set_date_hca":145,"set_date_17":"2019-01-31","consumption_at_set_date_17_hca":79,"error_date":"2127-15-31","device_date_time":"2019-02-20 11:32","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
telegram=|314493441434567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000c2086c7f21326cffff046d200b7422|
|
telegram=|314493441434567835087a740000200b6e2701004b6e450100426c5f2ccb086e790000c2086c7f21326cffff046d200b7422|
|
||||||
{"media":"heat_cost_allocation","meter":"qcaloric","name":"Element","id":"78563414","current_consumption_hca":127.000000,"set_date":"2018-12-31","consumption_at_set_date_hca":145.000000,"set_date_17":"2019-01-31","consumption_at_set_date_17_hca":79.000000,"error_date":"2127-15-31","device_date_time":"2019-02-20 11:32","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"heat_cost_allocation","meter":"qcaloric","name":"Element","id":"78563414","current_consumption_hca":127,"set_date":"2018-12-31","consumption_at_set_date_hca":145,"set_date_17":"2019-01-31","consumption_at_set_date_17_hca":79,"error_date":"2127-15-31","device_date_time":"2019-02-20 11:32","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
telegram=|A244EE4D785634123C067A8F000000|0C1348550000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0D0B5C2B03FD6C5E150082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
telegram=|A244EE4D785634123C067A8F000000|0C1348550000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0D0B5C2B03FD6C5E150082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
||||||
{"media":"warm water","meter":"supercom587","name":"MyWarmWater","id":"12345678","total_m3":5.548000,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"warm water","meter":"supercom587","name":"MyWarmWater","id":"12345678","total_m3":5.548,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
# Test Supercom587 T1 telegrams
|
# Test Supercom587 T1 telegrams
|
||||||
|
|
||||||
telegram=|A244EE4D785634123C067A8F000000|0C1348550000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0D0B5C2B03FD6C5E150082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
telegram=|A244EE4D785634123C067A8F000000|0C1348550000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0D0B5C2B03FD6C5E150082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
||||||
{"media":"warm water","meter":"supercom587","name":"MyWarmWater","id":"12345678","total_m3":5.548000,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"warm water","meter":"supercom587","name":"MyWarmWater","id":"12345678","total_m3":5.548,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
telegram=|A244EE4D111111113C077AAC000000|0C1389490000426CE1F14C130000000082046C21298C0413010000008D04931E3A3CFE0100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000001600000031130000046D0A0C5C2B03FD6C60150082206C5C290BFD0F0200018C4079629885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
telegram=|A244EE4D111111113C077AAC000000|0C1389490000426CE1F14C130000000082046C21298C0413010000008D04931E3A3CFE0100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000001600000031130000046D0A0C5C2B03FD6C60150082206C5C290BFD0F0200018C4079629885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
||||||
{"media":"water","meter":"supercom587","name":"MyColdWater","id":"11111111","total_m3":4.989000,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"water","meter":"supercom587","name":"MyColdWater","id":"11111111","total_m3":4.989,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# Test iPerl T1 telegram, after decryption, its got 2f2f markers.
|
# Test iPerl T1 telegram, after decryption, its got 2f2f markers.
|
||||||
|
|
||||||
telegram=|1E44AE4C9956341268077A36001000|2F2F0413181E0000023B00002F2F2F2F|
|
telegram=|1E44AE4C9956341268077A36001000|2F2F0413181E0000023B00002F2F2F2F|
|
||||||
{"media":"water","meter":"iperl","name":"MoreWater","id":"12345699","total_m3":7.704000,"max_flow_m3h":0.000000,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"water","meter":"iperl","name":"MoreWater","id":"12345699","total_m3":7.704,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# Test iPerl T1 telegram not encrypted, no 2f2f markers.
|
# Test iPerl T1 telegram not encrypted, no 2f2f markers.
|
||||||
|
|
||||||
telegram=|1844AE4C4455223368077A55000000|041389E20100023B0000|
|
telegram=|1844AE4C4455223368077A55000000|041389E20100023B0000|
|
||||||
{"media":"water","meter":"iperl","name":"WaterWater","id":"33225544","total_m3":123.529000,"max_flow_m3h":0.000000,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"water","meter":"iperl","name":"WaterWater","id":"33225544","total_m3":123.529,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# Test at-wmbus-16-2 T1 telegram
|
# Test at-wmbus-16-2 T1 telegram
|
||||||
|
|
||||||
telegram=|6E4401062020202005077A9A006085|2F2F0F0A734393CC0000435B0183001A54E06F630291342510030F00007B013E0B00003E0B00003E0B00003E0B00003E0B00003E0B00003E0B0000650000003D0000003D0000003D00000000000000A0910CB003FFFFFFFFFFFFFFFFFFFFA62B|
|
telegram=|6E4401062020202005077A9A006085|2F2F0F0A734393CC0000435B0183001A54E06F630291342510030F00007B013E0B00003E0B00003E0B00003E0B00003E0B00003E0B00003E0B0000650000003D0000003D0000003D00000000000000A0910CB003FFFFFFFFFFFFFFFFFFFFA62B|
|
||||||
{"media":"water","meter":"apator162","name":"Wasser","id":"20202020","total_m3":3.843000,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"water","meter":"apator162","name":"Wasser","id":"20202020","total_m3":3.843,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# Test amiplus/apator electricity meter
|
# Test amiplus/apator electricity meter
|
||||||
|
|
||||||
telegram=|4E4401061010101002027A00004005|2F2F0E035040691500000B2B300300066D00790C7423400C78371204860BABC8FC100000000E833C8074000000000BAB3C0000000AFDC9FC0136022F2F2F2F2F|
|
telegram=|4E4401061010101002027A00004005|2F2F0E035040691500000B2B300300066D00790C7423400C78371204860BABC8FC100000000E833C8074000000000BAB3C0000000AFDC9FC0136022F2F2F2F2F|
|
||||||
{"media":"electricity","meter":"amiplus","name":"MyElectricity1","id":"10101010","total_energy_consumption_kwh":15694.050000,"current_power_consumption_kw":0.330000,"total_energy_production_kwh":7.480000,"current_power_production_kw":0.000000,"device_date_time":"2019-03-20 12:57","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"electricity","meter":"amiplus","name":"MyElectricity1","id":"10101010","total_energy_consumption_kwh":15694.05,"current_power_consumption_kw":0.33,"total_energy_production_kwh":7.48,"current_power_production_kw":0,"device_date_time":"2019-03-20 12:57","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# Test MKRadio3 T1 telegrams
|
# Test MKRadio3 T1 telegrams
|
||||||
|
|
||||||
telegram=|2F446850313233347462A2|069F255900B029310000000306060906030609070606050509050505050407040605070500|
|
telegram=|2F446850313233347462A2|069F255900B029310000000306060906030609070606050509050505050407040605070500|
|
||||||
{"media":"warm water","meter":"mkradio3","name":"Duschen","id":"34333231","total_m3":13.800000,"target_m3":8.900000,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"warm water","meter":"mkradio3","name":"Duschen","id":"34333231","total_m3":13.8,"target_m3":8.9,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
# Test vario451 T1 telegrams
|
# Test vario451 T1 telegrams
|
||||||
telegram=|374468506549235827C3A2|129F25383300A8622600008200800A2AF862115175552877A36F26C9AB1CB24400000004000000000004908002|
|
telegram=|374468506549235827C3A2|129F25383300A8622600008200800A2AF862115175552877A36F26C9AB1CB24400000004000000000004908002|
|
||||||
|
|
|
@ -66,6 +66,9 @@ MeterAmiplus::MeterAmiplus(WMBus *bus, string& name, string& id, string& key) :
|
||||||
// Oddly, this device has not been configured to send as a electricity meter,
|
// Oddly, this device has not been configured to send as a electricity meter,
|
||||||
// but instead a device/media type that is used for gateway or relays or something?
|
// but instead a device/media type that is used for gateway or relays or something?
|
||||||
addMedia(0x37); // Radio converter (meter side)
|
addMedia(0x37); // Radio converter (meter side)
|
||||||
|
|
||||||
|
setExpectedVersion(0x02);
|
||||||
|
|
||||||
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,8 +104,6 @@ void MeterAmiplus::handleTelegram(Telegram *t) {
|
||||||
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
||||||
t->a_field_address[3]);
|
t->a_field_address[3]);
|
||||||
|
|
||||||
t->expectVersion("amiplus", 0x02);
|
|
||||||
|
|
||||||
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
||||||
warning("(amiplus) warning: telegram is encrypted but no key supplied!\n");
|
warning("(amiplus) warning: telegram is encrypted but no key supplied!\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,9 @@ MeterApator162::MeterApator162(WMBus *bus, string& name, string& id, string& key
|
||||||
{
|
{
|
||||||
addMedia(0x06);
|
addMedia(0x06);
|
||||||
addMedia(0x07);
|
addMedia(0x07);
|
||||||
|
|
||||||
|
setExpectedVersion(0x05);
|
||||||
|
|
||||||
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +102,6 @@ void MeterApator162::handleTelegram(Telegram *t)
|
||||||
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
||||||
t->a_field_address[3]);
|
t->a_field_address[3]);
|
||||||
|
|
||||||
t->expectVersion("apator162", 0x05);
|
|
||||||
|
|
||||||
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
||||||
warning("(apator162) warning: telegram is encrypted but no key supplied!\n");
|
warning("(apator162) warning: telegram is encrypted but no key supplied!\n");
|
||||||
|
|
|
@ -39,91 +39,44 @@ struct MeterIperl : public virtual WaterMeter, public virtual MeterCommonImpleme
|
||||||
// Total water counted through the meter
|
// Total water counted through the meter
|
||||||
double totalWaterConsumption(Unit u);
|
double totalWaterConsumption(Unit u);
|
||||||
bool hasTotalWaterConsumption();
|
bool hasTotalWaterConsumption();
|
||||||
double targetWaterConsumption(Unit u);
|
|
||||||
bool hasTargetWaterConsumption();
|
|
||||||
double maxFlow(Unit u);
|
double maxFlow(Unit u);
|
||||||
bool hasMaxFlow();
|
bool hasMaxFlow();
|
||||||
double flowTemperature(Unit u);
|
|
||||||
bool hasFlowTemperature();
|
|
||||||
double externalTemperature(Unit u);
|
|
||||||
bool hasExternalTemperature();
|
|
||||||
|
|
||||||
string statusHumanReadable();
|
|
||||||
string status();
|
|
||||||
string timeDry();
|
|
||||||
string timeReversed();
|
|
||||||
string timeLeaking();
|
|
||||||
string timeBursting();
|
|
||||||
|
|
||||||
void printMeter(Telegram *t,
|
|
||||||
string *human_readable,
|
|
||||||
string *fields, char separator,
|
|
||||||
string *json,
|
|
||||||
vector<string> *envs);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleTelegram(Telegram *t);
|
|
||||||
void processContent(Telegram *t);
|
void processContent(Telegram *t);
|
||||||
string decodeTime(int time);
|
|
||||||
|
|
||||||
double total_water_consumption_ {};
|
double total_water_consumption_m3_ {};
|
||||||
double max_flow_ {};
|
double max_flow_m3h_ {};
|
||||||
};
|
};
|
||||||
|
|
||||||
MeterIperl::MeterIperl(WMBus *bus, string& name, string& id, string& key) :
|
MeterIperl::MeterIperl(WMBus *bus, string& name, string& id, string& key) :
|
||||||
MeterCommonImplementation(bus, name, id, key, MeterType::IPERL, MANUFACTURER_SEN, LinkMode::T1)
|
MeterCommonImplementation(bus, name, id, key, MeterType::IPERL, MANUFACTURER_SEN, LinkMode::T1)
|
||||||
{
|
{
|
||||||
|
setEncryptionMode(EncryptionMode::AES_CBC);
|
||||||
|
|
||||||
addMedia(0x06);
|
addMedia(0x06);
|
||||||
addMedia(0x07);
|
addMedia(0x07);
|
||||||
|
|
||||||
|
setExpectedVersion(0x68);
|
||||||
|
|
||||||
|
addPrint("total", Quantity::Volume,
|
||||||
|
[&](Unit u){ return totalWaterConsumption(u); },
|
||||||
|
"The total water consumption recorded by this meter.",
|
||||||
|
true, true);
|
||||||
|
|
||||||
|
addPrint("max_flow", Quantity::Flow,
|
||||||
|
[&](Unit u){ return maxFlow(u); },
|
||||||
|
"The maxium flow recorded during previous period.",
|
||||||
|
true, true);
|
||||||
|
|
||||||
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double MeterIperl::totalWaterConsumption(Unit u)
|
|
||||||
{
|
|
||||||
return total_water_consumption_;
|
|
||||||
}
|
|
||||||
|
|
||||||
unique_ptr<WaterMeter> createIperl(WMBus *bus, string& name, string& id, string& key)
|
unique_ptr<WaterMeter> createIperl(WMBus *bus, string& name, string& id, string& key)
|
||||||
{
|
{
|
||||||
return unique_ptr<WaterMeter>(new MeterIperl(bus,name,id,key));
|
return unique_ptr<WaterMeter>(new MeterIperl(bus,name,id,key));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeterIperl::handleTelegram(Telegram *t)
|
|
||||||
{
|
|
||||||
if (!isTelegramForMe(t)) {
|
|
||||||
// This telegram is not intended for this meter.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
verbose("(%s) telegram for %s %02x%02x%02x%02x\n", "iperl",
|
|
||||||
name().c_str(),
|
|
||||||
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
|
||||||
t->a_field_address[3]);
|
|
||||||
|
|
||||||
t->expectVersion("iperl", 0x68);
|
|
||||||
|
|
||||||
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
|
||||||
warning("(iperl) warning: telegram is encrypted but no key supplied!\n");
|
|
||||||
}
|
|
||||||
if (useAes()) {
|
|
||||||
vector<uchar> aeskey = key();
|
|
||||||
decryptMode5_AES_CBC(t, aeskey);
|
|
||||||
} else {
|
|
||||||
t->content = t->payload;
|
|
||||||
}
|
|
||||||
char log_prefix[256];
|
|
||||||
snprintf(log_prefix, 255, "(%s) log", "iperl");
|
|
||||||
logTelegram(log_prefix, t->parsed, t->content);
|
|
||||||
int content_start = t->parsed.size();
|
|
||||||
processContent(t);
|
|
||||||
if (isDebugEnabled()) {
|
|
||||||
snprintf(log_prefix, 255, "(%s)", "iperl");
|
|
||||||
t->explainParse(log_prefix, content_start);
|
|
||||||
}
|
|
||||||
triggerUpdate(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MeterIperl::processContent(Telegram *t)
|
void MeterIperl::processContent(Telegram *t)
|
||||||
{
|
{
|
||||||
map<string,pair<int,DVEntry>> values;
|
map<string,pair<int,DVEntry>> values;
|
||||||
|
@ -133,81 +86,20 @@ void MeterIperl::processContent(Telegram *t)
|
||||||
string key;
|
string key;
|
||||||
|
|
||||||
if(findKey(ValueInformation::Volume, 0, &key, &values)) {
|
if(findKey(ValueInformation::Volume, 0, &key, &values)) {
|
||||||
extractDVdouble(&values, key, &offset, &total_water_consumption_);
|
extractDVdouble(&values, key, &offset, &total_water_consumption_m3_);
|
||||||
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_);
|
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_m3_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(findKey(ValueInformation::VolumeFlow, ANY_STORAGENR, &key, &values)) {
|
if(findKey(ValueInformation::VolumeFlow, ANY_STORAGENR, &key, &values)) {
|
||||||
extractDVdouble(&values, key, &offset, &max_flow_);
|
extractDVdouble(&values, key, &offset, &max_flow_m3h_);
|
||||||
t->addMoreExplanation(offset, " max flow (%f m3/h)", max_flow_);
|
t->addMoreExplanation(offset, " max flow (%f m3/h)", max_flow_m3h_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeterIperl::printMeter(Telegram *t,
|
double MeterIperl::totalWaterConsumption(Unit u)
|
||||||
string *human_readable,
|
|
||||||
string *fields, char separator,
|
|
||||||
string *json,
|
|
||||||
vector<string> *envs)
|
|
||||||
{
|
{
|
||||||
char buf[65536];
|
assertQuantity(u, Quantity::Volume);
|
||||||
buf[65535] = 0;
|
return convert(total_water_consumption_m3_, Unit::M3, u);
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf)-1,
|
|
||||||
"%s\t"
|
|
||||||
"%s\t"
|
|
||||||
"% 3.3f m3\t"
|
|
||||||
"% 3.3f m3/h\t"
|
|
||||||
"%s",
|
|
||||||
name().c_str(),
|
|
||||||
t->id.c_str(),
|
|
||||||
totalWaterConsumption(Unit::M3),
|
|
||||||
maxFlow(Unit::M3H),
|
|
||||||
datetimeOfUpdateHumanReadable().c_str());
|
|
||||||
|
|
||||||
*human_readable = buf;
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf)-1,
|
|
||||||
"%s%c"
|
|
||||||
"%s%c"
|
|
||||||
"%f%c"
|
|
||||||
"%f%c"
|
|
||||||
"%s",
|
|
||||||
name().c_str(), separator,
|
|
||||||
t->id.c_str(), separator,
|
|
||||||
totalWaterConsumption(Unit::M3), separator,
|
|
||||||
maxFlow(Unit::M3H), separator,
|
|
||||||
datetimeOfUpdateRobot().c_str());
|
|
||||||
|
|
||||||
*fields = buf;
|
|
||||||
|
|
||||||
#define Q(x,y) "\""#x"\":"#y","
|
|
||||||
#define QS(x,y) "\""#x"\":\""#y"\","
|
|
||||||
#define QSE(x,y) "\""#x"\":\""#y"\""
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf)-1, "{"
|
|
||||||
QS(media,%s)
|
|
||||||
QS(meter,iperl)
|
|
||||||
QS(name,%s)
|
|
||||||
QS(id,%s)
|
|
||||||
Q(total_m3,%f)
|
|
||||||
Q(max_flow_m3h,%f)
|
|
||||||
QSE(timestamp,%s)
|
|
||||||
"}",
|
|
||||||
mediaTypeJSON(t->a_field_device_type).c_str(),
|
|
||||||
name().c_str(),
|
|
||||||
t->id.c_str(),
|
|
||||||
totalWaterConsumption(Unit::M3),
|
|
||||||
maxFlow(Unit::M3H),
|
|
||||||
datetimeOfUpdateRobot().c_str());
|
|
||||||
|
|
||||||
*json = buf;
|
|
||||||
|
|
||||||
envs->push_back(string("METER_JSON=")+*json);
|
|
||||||
envs->push_back(string("METER_TYPE=iperl"));
|
|
||||||
envs->push_back(string("METER_ID=")+t->id);
|
|
||||||
envs->push_back(string("METER_TOTAL_M3=")+to_string(totalWaterConsumption(Unit::M3)));
|
|
||||||
envs->push_back(string("METER_MAX_FLOW_M3H=")+to_string(maxFlow(Unit::M3H)));
|
|
||||||
envs->push_back(string("METER_TIMESTAMP=")+datetimeOfUpdateRobot());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MeterIperl::hasTotalWaterConsumption()
|
bool MeterIperl::hasTotalWaterConsumption()
|
||||||
|
@ -215,72 +107,13 @@ bool MeterIperl::hasTotalWaterConsumption()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
double MeterIperl::targetWaterConsumption(Unit u)
|
|
||||||
{
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MeterIperl::hasTargetWaterConsumption()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
double MeterIperl::maxFlow(Unit u)
|
double MeterIperl::maxFlow(Unit u)
|
||||||
{
|
{
|
||||||
return max_flow_;
|
assertQuantity(u, Quantity::Flow);
|
||||||
|
return convert(max_flow_m3h_, Unit::M3H, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MeterIperl::hasMaxFlow()
|
bool MeterIperl::hasMaxFlow()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
double MeterIperl::flowTemperature(Unit u)
|
|
||||||
{
|
|
||||||
return 127;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MeterIperl::hasFlowTemperature()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
double MeterIperl::externalTemperature(Unit u)
|
|
||||||
{
|
|
||||||
return 127;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MeterIperl::hasExternalTemperature()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string MeterIperl::statusHumanReadable()
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
string MeterIperl::status()
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
string MeterIperl::timeDry()
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
string MeterIperl::timeReversed()
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
string MeterIperl::timeLeaking()
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
string MeterIperl::timeBursting()
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
|
@ -51,18 +51,22 @@ private:
|
||||||
MKRadio3::MKRadio3(WMBus *bus, string& name, string& id, string& key) :
|
MKRadio3::MKRadio3(WMBus *bus, string& name, string& id, string& key) :
|
||||||
MeterCommonImplementation(bus, name, id, key, MeterType::MKRADIO3, MANUFACTURER_TCH, LinkMode::T1)
|
MeterCommonImplementation(bus, name, id, key, MeterType::MKRADIO3, MANUFACTURER_TCH, LinkMode::T1)
|
||||||
{
|
{
|
||||||
|
setEncryptionMode(EncryptionMode::None);
|
||||||
|
|
||||||
addMedia(0x62);
|
addMedia(0x62);
|
||||||
addMedia(0x72);
|
addMedia(0x72);
|
||||||
|
|
||||||
|
setExpectedVersion(0x74);
|
||||||
|
|
||||||
addPrint("total", Quantity::Volume,
|
addPrint("total", Quantity::Volume,
|
||||||
[&](Unit u){ return totalWaterConsumption(u); },
|
[&](Unit u){ return totalWaterConsumption(u); },
|
||||||
"The total water consumption recorded by this meter.",
|
"The total water consumption recorded by this meter.",
|
||||||
true);
|
true, true);
|
||||||
|
|
||||||
addPrint("target", Quantity::Volume,
|
addPrint("target", Quantity::Volume,
|
||||||
[&](Unit u){ return targetWaterConsumption(u); },
|
[&](Unit u){ return targetWaterConsumption(u); },
|
||||||
"The total water consumption recorded at the beginning of this month.",
|
"The total water consumption recorded at the beginning of this month.",
|
||||||
true);
|
true, true);
|
||||||
|
|
||||||
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,27 +77,21 @@ struct MeterMultical21 : public virtual WaterMeter, public virtual MeterCommonIm
|
||||||
string timeLeaking();
|
string timeLeaking();
|
||||||
string timeBursting();
|
string timeBursting();
|
||||||
|
|
||||||
void printMeter(Telegram *t,
|
|
||||||
string *human_readable,
|
|
||||||
string *fields, char separator,
|
|
||||||
string *json,
|
|
||||||
vector<string> *envs);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleTelegram(Telegram *t);
|
|
||||||
void processContent(Telegram *t);
|
void processContent(Telegram *t);
|
||||||
string decodeTime(int time);
|
string decodeTime(int time);
|
||||||
|
|
||||||
uint16_t info_codes_ {};
|
uint16_t info_codes_ {};
|
||||||
double total_water_consumption_ {};
|
double total_water_consumption_m3_ {};
|
||||||
bool has_total_water_consumption_ {};
|
bool has_total_water_consumption_ {};
|
||||||
double target_volume_ {};
|
double target_water_consumption_m3_ {};
|
||||||
bool has_target_volume_ {};
|
bool has_target_water_consumption_ {};
|
||||||
double max_flow_ {};
|
double max_flow_m3h_ {};
|
||||||
bool has_max_flow_ {};
|
bool has_max_flow_ {};
|
||||||
double flow_temperature_ { 127 };
|
double flow_temperature_c_ { 127 };
|
||||||
bool has_flow_temperature_ {};
|
bool has_flow_temperature_ {};
|
||||||
double external_temperature_ { 127 };
|
double external_temperature_c_ { 127 };
|
||||||
bool has_external_temperature_ {};
|
bool has_external_temperature_ {};
|
||||||
|
|
||||||
const char *meter_name_; // multical21 or flowiq3100
|
const char *meter_name_; // multical21 or flowiq3100
|
||||||
|
@ -107,24 +101,80 @@ private:
|
||||||
MeterMultical21::MeterMultical21(WMBus *bus, string& name, string& id, string& key, MeterType mt) :
|
MeterMultical21::MeterMultical21(WMBus *bus, string& name, string& id, string& key, MeterType mt) :
|
||||||
MeterCommonImplementation(bus, name, id, key, mt, MANUFACTURER_KAM, LinkMode::C1)
|
MeterCommonImplementation(bus, name, id, key, mt, MANUFACTURER_KAM, LinkMode::C1)
|
||||||
{
|
{
|
||||||
|
setEncryptionMode(EncryptionMode::AES_CTR);
|
||||||
|
|
||||||
addMedia(0x16); // Water media
|
addMedia(0x16); // Water media
|
||||||
|
|
||||||
if (type() == MeterType::MULTICAL21) {
|
if (type() == MeterType::MULTICAL21) {
|
||||||
expected_version_ = 0x1b;
|
setExpectedVersion(0x1b);
|
||||||
meter_name_ = "multical21";
|
|
||||||
} else if (type() == MeterType::FLOWIQ3100) {
|
} else if (type() == MeterType::FLOWIQ3100) {
|
||||||
expected_version_ = 0x1d;
|
setExpectedVersion(0x1d);
|
||||||
meter_name_ = "flowiq3100";
|
|
||||||
} else {
|
} else {
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addPrint("total", Quantity::Volume,
|
||||||
|
[&](Unit u){ return totalWaterConsumption(u); },
|
||||||
|
"The total water consumption recorded by this meter.",
|
||||||
|
true, true);
|
||||||
|
|
||||||
|
addPrint("target", Quantity::Volume,
|
||||||
|
[&](Unit u){ return targetWaterConsumption(u); },
|
||||||
|
"The total water consumption recorded at the beginning of this month.",
|
||||||
|
true, true);
|
||||||
|
|
||||||
|
addPrint("max_flow", Quantity::Flow,
|
||||||
|
[&](Unit u){ return maxFlow(u); },
|
||||||
|
"The maxium flow recorded during previous period.",
|
||||||
|
true, true);
|
||||||
|
|
||||||
|
addPrint("flow_temperature", Quantity::Temperature,
|
||||||
|
[&](Unit u){ return flowTemperature(u); },
|
||||||
|
"The water temperature.",
|
||||||
|
true, true);
|
||||||
|
|
||||||
|
addPrint("external_temperature", Quantity::Temperature,
|
||||||
|
[&](Unit u){ return externalTemperature(u); },
|
||||||
|
"The external temperature outside of the meter.",
|
||||||
|
true, true);
|
||||||
|
|
||||||
|
addPrint("", Quantity::Text,
|
||||||
|
[&](){ return statusHumanReadable(); },
|
||||||
|
"Status of meter.",
|
||||||
|
true, false);
|
||||||
|
|
||||||
|
addPrint("current_status", Quantity::Text,
|
||||||
|
[&](){ return status(); },
|
||||||
|
"Status of meter.",
|
||||||
|
false, true);
|
||||||
|
|
||||||
|
addPrint("time_dry", Quantity::Text,
|
||||||
|
[&](){ return timeDry(); },
|
||||||
|
"Amount of time the meter has been dry.",
|
||||||
|
false, true);
|
||||||
|
|
||||||
|
addPrint("time_reversed", Quantity::Text,
|
||||||
|
[&](){ return timeReversed(); },
|
||||||
|
"Amount of time the meter has been reversed.",
|
||||||
|
false, true);
|
||||||
|
|
||||||
|
addPrint("time_leaking", Quantity::Text,
|
||||||
|
[&](){ return timeLeaking(); },
|
||||||
|
"Amount of time the meter has been leaking.",
|
||||||
|
false, true);
|
||||||
|
|
||||||
|
addPrint("time_bursting", Quantity::Text,
|
||||||
|
[&](){ return timeBursting(); },
|
||||||
|
"Amount of time the meter has been bursting.",
|
||||||
|
false, true);
|
||||||
|
|
||||||
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double MeterMultical21::totalWaterConsumption(Unit u)
|
double MeterMultical21::totalWaterConsumption(Unit u)
|
||||||
{
|
{
|
||||||
return total_water_consumption_;
|
assertQuantity(u, Quantity::Volume);
|
||||||
|
return convert(total_water_consumption_m3_, Unit::M3, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MeterMultical21::hasTotalWaterConsumption()
|
bool MeterMultical21::hasTotalWaterConsumption()
|
||||||
|
@ -134,17 +184,19 @@ bool MeterMultical21::hasTotalWaterConsumption()
|
||||||
|
|
||||||
double MeterMultical21::targetWaterConsumption(Unit u)
|
double MeterMultical21::targetWaterConsumption(Unit u)
|
||||||
{
|
{
|
||||||
return target_volume_;
|
assertQuantity(u, Quantity::Volume);
|
||||||
|
return convert(target_water_consumption_m3_, Unit::M3, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MeterMultical21::hasTargetWaterConsumption()
|
bool MeterMultical21::hasTargetWaterConsumption()
|
||||||
{
|
{
|
||||||
return has_target_volume_;
|
return has_target_water_consumption_;
|
||||||
}
|
}
|
||||||
|
|
||||||
double MeterMultical21::maxFlow(Unit u)
|
double MeterMultical21::maxFlow(Unit u)
|
||||||
{
|
{
|
||||||
return max_flow_;
|
assertQuantity(u, Quantity::Flow);
|
||||||
|
return convert(max_flow_m3h_, Unit::M3H, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MeterMultical21::hasMaxFlow()
|
bool MeterMultical21::hasMaxFlow()
|
||||||
|
@ -154,7 +206,8 @@ bool MeterMultical21::hasMaxFlow()
|
||||||
|
|
||||||
double MeterMultical21::flowTemperature(Unit u)
|
double MeterMultical21::flowTemperature(Unit u)
|
||||||
{
|
{
|
||||||
return flow_temperature_;
|
assertQuantity(u, Quantity::Temperature);
|
||||||
|
return convert(flow_temperature_c_, Unit::C, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MeterMultical21::hasFlowTemperature()
|
bool MeterMultical21::hasFlowTemperature()
|
||||||
|
@ -164,7 +217,8 @@ bool MeterMultical21::hasFlowTemperature()
|
||||||
|
|
||||||
double MeterMultical21::externalTemperature(Unit u)
|
double MeterMultical21::externalTemperature(Unit u)
|
||||||
{
|
{
|
||||||
return external_temperature_;
|
assertQuantity(u, Quantity::Temperature);
|
||||||
|
return convert(external_temperature_c_, Unit::C, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MeterMultical21::hasExternalTemperature()
|
bool MeterMultical21::hasExternalTemperature()
|
||||||
|
@ -190,38 +244,6 @@ unique_ptr<WaterMeter> createFlowIQ3100(WMBus *bus, string& name, string& id, st
|
||||||
return createMulticalWaterMeter(bus, name, id, key, MeterType::FLOWIQ3100);
|
return createMulticalWaterMeter(bus, name, id, key, MeterType::FLOWIQ3100);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeterMultical21::handleTelegram(Telegram *t)
|
|
||||||
{
|
|
||||||
if (!isTelegramForMe(t)) {
|
|
||||||
// This telegram is not intended for this meter.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
verbose("(%s) telegram for %s %02x%02x%02x%02x\n", meter_name_,
|
|
||||||
name().c_str(),
|
|
||||||
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
|
||||||
t->a_field_address[3]);
|
|
||||||
|
|
||||||
t->expectVersion(name().c_str(), expected_version_);
|
|
||||||
|
|
||||||
if (useAes()) {
|
|
||||||
vector<uchar> aeskey = key();
|
|
||||||
decryptMode1_AES_CTR(t, aeskey);
|
|
||||||
} else {
|
|
||||||
t->content = t->payload;
|
|
||||||
}
|
|
||||||
char log_prefix[256];
|
|
||||||
snprintf(log_prefix, 255, "(%s) log", meter_name_);
|
|
||||||
logTelegram(log_prefix, t->parsed, t->content);
|
|
||||||
int content_start = t->parsed.size();
|
|
||||||
processContent(t);
|
|
||||||
if (isDebugEnabled()) {
|
|
||||||
snprintf(log_prefix, 255, "(%s)", meter_name_);
|
|
||||||
t->explainParse(log_prefix, content_start);
|
|
||||||
}
|
|
||||||
triggerUpdate(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MeterMultical21::processContent(Telegram *t)
|
void MeterMultical21::processContent(Telegram *t)
|
||||||
{
|
{
|
||||||
// 02 dif (16 Bit Integer/Binary Instantaneous value)
|
// 02 dif (16 Bit Integer/Binary Instantaneous value)
|
||||||
|
@ -335,31 +357,31 @@ void MeterMultical21::processContent(Telegram *t)
|
||||||
t->addMoreExplanation(offset, " info codes (%s)", statusHumanReadable().c_str());
|
t->addMoreExplanation(offset, " info codes (%s)", statusHumanReadable().c_str());
|
||||||
|
|
||||||
if(findKey(ValueInformation::Volume, 0, &key, &values)) {
|
if(findKey(ValueInformation::Volume, 0, &key, &values)) {
|
||||||
extractDVdouble(&values, key, &offset, &total_water_consumption_);
|
extractDVdouble(&values, key, &offset, &total_water_consumption_m3_);
|
||||||
has_total_water_consumption_ = true;
|
has_total_water_consumption_ = true;
|
||||||
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_);
|
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_m3_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(findKey(ValueInformation::Volume, 1, &key, &values)) {
|
if(findKey(ValueInformation::Volume, 1, &key, &values)) {
|
||||||
extractDVdouble(&values, key, &offset, &target_volume_);
|
extractDVdouble(&values, key, &offset, &target_water_consumption_m3_);
|
||||||
has_target_volume_ = true;
|
has_target_water_consumption_ = true;
|
||||||
t->addMoreExplanation(offset, " target consumption (%f m3)", target_volume_);
|
t->addMoreExplanation(offset, " target consumption (%f m3)", target_water_consumption_m3_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(findKey(ValueInformation::VolumeFlow, ANY_STORAGENR, &key, &values)) {
|
if(findKey(ValueInformation::VolumeFlow, ANY_STORAGENR, &key, &values)) {
|
||||||
extractDVdouble(&values, key, &offset, &max_flow_);
|
extractDVdouble(&values, key, &offset, &max_flow_m3h_);
|
||||||
has_max_flow_ = true;
|
has_max_flow_ = true;
|
||||||
t->addMoreExplanation(offset, " max flow (%f m3/h)", max_flow_);
|
t->addMoreExplanation(offset, " max flow (%f m3/h)", max_flow_m3h_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(findKey(ValueInformation::FlowTemperature, ANY_STORAGENR, &key, &values)) {
|
if(findKey(ValueInformation::FlowTemperature, ANY_STORAGENR, &key, &values)) {
|
||||||
has_flow_temperature_ = extractDVdouble(&values, key, &offset, &flow_temperature_);
|
has_flow_temperature_ = extractDVdouble(&values, key, &offset, &flow_temperature_c_);
|
||||||
t->addMoreExplanation(offset, " flow temperature (%f °C)", flow_temperature_);
|
t->addMoreExplanation(offset, " flow temperature (%f °C)", flow_temperature_c_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(findKey(ValueInformation::ExternalTemperature, ANY_STORAGENR, &key, &values)) {
|
if(findKey(ValueInformation::ExternalTemperature, ANY_STORAGENR, &key, &values)) {
|
||||||
has_external_temperature_ = extractDVdouble(&values, key, &offset, &external_temperature_);
|
has_external_temperature_ = extractDVdouble(&values, key, &offset, &external_temperature_c_);
|
||||||
t->addMoreExplanation(offset, " external temperature (%f °C)", external_temperature_);
|
t->addMoreExplanation(offset, " external temperature (%f °C)", external_temperature_c_);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -485,129 +507,3 @@ string MeterMultical21::decodeTime(int time)
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeterMultical21::printMeter(Telegram *t,
|
|
||||||
string *human_readable,
|
|
||||||
string *fields, char separator,
|
|
||||||
string *json,
|
|
||||||
vector<string> *envs)
|
|
||||||
{
|
|
||||||
char buf[65536];
|
|
||||||
buf[65535] = 0;
|
|
||||||
|
|
||||||
char ft[10], et[10];
|
|
||||||
ft[9] = 0;
|
|
||||||
et[9] = 0;
|
|
||||||
|
|
||||||
if (hasFlowTemperature()) {
|
|
||||||
snprintf(ft, sizeof(ft)-1, "% 2.0f", flowTemperature(Unit::C));
|
|
||||||
} else {
|
|
||||||
ft[0] = '-';
|
|
||||||
ft[1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasExternalTemperature()) {
|
|
||||||
snprintf(et, sizeof(et)-1, "% 2.0f", externalTemperature(Unit::C));
|
|
||||||
} else {
|
|
||||||
et[0] = '-';
|
|
||||||
et[1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf)-1,
|
|
||||||
"%s\t"
|
|
||||||
"%s\t"
|
|
||||||
"% 3.3f m3\t"
|
|
||||||
"% 3.3f m3\t"
|
|
||||||
"% 3.3f m3/h\t"
|
|
||||||
"%s°C\t"
|
|
||||||
"%s°C\t"
|
|
||||||
"%s\t"
|
|
||||||
"%s",
|
|
||||||
name().c_str(),
|
|
||||||
t->id.c_str(),
|
|
||||||
totalWaterConsumption(Unit::M3),
|
|
||||||
targetWaterConsumption(Unit::M3),
|
|
||||||
maxFlow(Unit::M3H),
|
|
||||||
ft,
|
|
||||||
et,
|
|
||||||
statusHumanReadable().c_str(),
|
|
||||||
datetimeOfUpdateHumanReadable().c_str());
|
|
||||||
|
|
||||||
*human_readable = buf;
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf)-1,
|
|
||||||
"%s%c"
|
|
||||||
"%s%c"
|
|
||||||
"%f%c"
|
|
||||||
"%f%c"
|
|
||||||
"%f%c"
|
|
||||||
"%.0f%c"
|
|
||||||
"%.0f%c"
|
|
||||||
"%s%c"
|
|
||||||
"%s",
|
|
||||||
name().c_str(), separator,
|
|
||||||
t->id.c_str(), separator,
|
|
||||||
totalWaterConsumption(Unit::M3), separator,
|
|
||||||
targetWaterConsumption(Unit::M3), separator,
|
|
||||||
maxFlow(Unit::M3H), separator,
|
|
||||||
flowTemperature(Unit::C), separator,
|
|
||||||
externalTemperature(Unit::C), separator,
|
|
||||||
statusHumanReadable().c_str(), separator,
|
|
||||||
datetimeOfUpdateRobot().c_str());
|
|
||||||
|
|
||||||
*fields = buf;
|
|
||||||
|
|
||||||
#define Q(x,y) "\""#x"\":"#y","
|
|
||||||
#define QS(x,y) "\""#x"\":\""#y"\","
|
|
||||||
#define QSE(x,y) "\""#x"\":\""#y"\""
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf)-1, "{"
|
|
||||||
QS(media,%s)
|
|
||||||
QS(meter,%s)
|
|
||||||
QS(name,%s)
|
|
||||||
QS(id,%s)
|
|
||||||
Q(total_m3,%f)
|
|
||||||
Q(target_m3,%f)
|
|
||||||
Q(max_flow_m3h,%f)
|
|
||||||
Q(flow_temperature,%.0f)
|
|
||||||
Q(external_temperature,%.0f)
|
|
||||||
QS(current_status,%s)
|
|
||||||
QS(time_dry,%s)
|
|
||||||
QS(time_reversed,%s)
|
|
||||||
QS(time_leaking,%s)
|
|
||||||
QS(time_bursting,%s)
|
|
||||||
QSE(timestamp,%s)
|
|
||||||
"}",
|
|
||||||
mediaTypeJSON(t->a_field_device_type).c_str(),
|
|
||||||
meter_name_,
|
|
||||||
name().c_str(),
|
|
||||||
t->id.c_str(),
|
|
||||||
totalWaterConsumption(Unit::M3),
|
|
||||||
targetWaterConsumption(Unit::M3),
|
|
||||||
maxFlow(Unit::M3H),
|
|
||||||
flowTemperature(Unit::C),
|
|
||||||
externalTemperature(Unit::C),
|
|
||||||
status().c_str(), // DRY REVERSED LEAK BURST
|
|
||||||
timeDry().c_str(),
|
|
||||||
timeReversed().c_str(),
|
|
||||||
timeLeaking().c_str(),
|
|
||||||
timeBursting().c_str(),
|
|
||||||
datetimeOfUpdateRobot().c_str());
|
|
||||||
|
|
||||||
*json = buf;
|
|
||||||
|
|
||||||
envs->push_back(string("METER_JSON=")+*json);
|
|
||||||
envs->push_back(string("METER_TYPE=")+meter_name_);
|
|
||||||
envs->push_back(string("METER_ID=")+t->id);
|
|
||||||
envs->push_back(string("METER_TOTAL_M3=")+to_string(totalWaterConsumption(Unit::M3)));
|
|
||||||
envs->push_back(string("METER_TARGET_M3=")+to_string(targetWaterConsumption(Unit::M3)));
|
|
||||||
envs->push_back(string("METER_MAX_FLOW_M3H=")+to_string(maxFlow(Unit::M3H)));
|
|
||||||
envs->push_back(string("METER_FLOW_TEMPERATURE=")+to_string(flowTemperature(Unit::C)));
|
|
||||||
envs->push_back(string("METER_EXTERNAL_TEMPERATURE=")+to_string(externalTemperature(Unit::C)));
|
|
||||||
envs->push_back(string("METER_STATUS=")+status());
|
|
||||||
envs->push_back(string("METER_TIME_DRY=")+timeDry());
|
|
||||||
envs->push_back(string("METER_TIME_REVERSED=")+timeReversed());
|
|
||||||
envs->push_back(string("METER_TIME_LEAKING=")+timeLeaking());
|
|
||||||
envs->push_back(string("METER_TIME_BURSTING=")+timeBursting());
|
|
||||||
envs->push_back(string("METER_TIMESTAMP=")+datetimeOfUpdateRobot());
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,19 +31,10 @@ struct MeterMultical302 : public virtual HeatMeter, public virtual MeterCommonIm
|
||||||
MeterMultical302(WMBus *bus, string& name, string& id, string& key);
|
MeterMultical302(WMBus *bus, string& name, string& id, string& key);
|
||||||
|
|
||||||
double totalEnergyConsumption(Unit u);
|
double totalEnergyConsumption(Unit u);
|
||||||
double currentPeriodEnergyConsumption(Unit u);
|
|
||||||
double previousPeriodEnergyConsumption(Unit u);
|
|
||||||
double currentPowerConsumption(Unit u);
|
double currentPowerConsumption(Unit u);
|
||||||
double totalVolume(Unit u);
|
double totalVolume(Unit u);
|
||||||
|
|
||||||
void printMeter(Telegram *t,
|
|
||||||
string *human_readable,
|
|
||||||
string *fields, char separator,
|
|
||||||
string *json,
|
|
||||||
vector<string> *envs);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleTelegram(Telegram *t);
|
|
||||||
void processContent(Telegram *t);
|
void processContent(Telegram *t);
|
||||||
|
|
||||||
double total_energy_kwh_ {};
|
double total_energy_kwh_ {};
|
||||||
|
@ -54,69 +45,52 @@ private:
|
||||||
MeterMultical302::MeterMultical302(WMBus *bus, string& name, string& id, string& key) :
|
MeterMultical302::MeterMultical302(WMBus *bus, string& name, string& id, string& key) :
|
||||||
MeterCommonImplementation(bus, name, id, key, MeterType::MULTICAL302, MANUFACTURER_KAM, LinkMode::C1)
|
MeterCommonImplementation(bus, name, id, key, MeterType::MULTICAL302, MANUFACTURER_KAM, LinkMode::C1)
|
||||||
{
|
{
|
||||||
|
setEncryptionMode(EncryptionMode::AES_CTR);
|
||||||
|
|
||||||
addMedia(0x04); // Heat media
|
addMedia(0x04); // Heat media
|
||||||
|
|
||||||
|
addPrint("total", Quantity::Energy,
|
||||||
|
[&](Unit u){ return totalEnergyConsumption(u); },
|
||||||
|
"The total energy consumption recorded by this meter.",
|
||||||
|
true, true);
|
||||||
|
|
||||||
|
addPrint("total_volume", Quantity::Volume,
|
||||||
|
[&](Unit u){ return totalVolume(u); },
|
||||||
|
"Total volume of heat media.",
|
||||||
|
true, true);
|
||||||
|
|
||||||
|
addPrint("current", Quantity::Power,
|
||||||
|
[&](Unit u){ return currentPowerConsumption(u); },
|
||||||
|
"Current power consumption.",
|
||||||
|
true, true);
|
||||||
|
|
||||||
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unique_ptr<HeatMeter> createMultical302(WMBus *bus, string& name, string& id, string& key) {
|
||||||
|
return unique_ptr<HeatMeter>(new MeterMultical302(bus,name,id,key));
|
||||||
|
}
|
||||||
|
|
||||||
double MeterMultical302::totalEnergyConsumption(Unit u)
|
double MeterMultical302::totalEnergyConsumption(Unit u)
|
||||||
{
|
{
|
||||||
assertQuantity(u, Quantity::Energy);
|
assertQuantity(u, Quantity::Energy);
|
||||||
return convert(total_energy_kwh_, Unit::KWH, u);
|
return convert(total_energy_kwh_, Unit::KWH, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
double MeterMultical302::currentPowerConsumption(Unit u)
|
|
||||||
{
|
|
||||||
assertQuantity(u, Quantity::Power);
|
|
||||||
return convert(current_power_kw_, Unit::KW, u);
|
|
||||||
}
|
|
||||||
|
|
||||||
double MeterMultical302::currentPeriodEnergyConsumption(Unit u)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double MeterMultical302::previousPeriodEnergyConsumption(Unit u)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double MeterMultical302::totalVolume(Unit u)
|
double MeterMultical302::totalVolume(Unit u)
|
||||||
{
|
{
|
||||||
assertQuantity(u, Quantity::Volume);
|
assertQuantity(u, Quantity::Volume);
|
||||||
return convert(total_volume_m3_, Unit::M3, u);
|
return convert(total_volume_m3_, Unit::M3, u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeterMultical302::handleTelegram(Telegram *t) {
|
double MeterMultical302::currentPowerConsumption(Unit u)
|
||||||
|
{
|
||||||
if (!isTelegramForMe(t)) {
|
assertQuantity(u, Quantity::Power);
|
||||||
// This telegram is not intended for this meter.
|
return convert(current_power_kw_, Unit::KW, u);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
verbose("(multical302) %s %02x%02x%02x%02x ",
|
|
||||||
name().c_str(),
|
|
||||||
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
|
||||||
t->a_field_address[3]);
|
|
||||||
|
|
||||||
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
|
||||||
warning("(multical302) warning: telegram is encrypted but no key supplied!\n");
|
|
||||||
}
|
|
||||||
if (useAes()) {
|
|
||||||
vector<uchar> aeskey = key();
|
|
||||||
decryptMode1_AES_CTR(t, aeskey);
|
|
||||||
} else {
|
|
||||||
t->content = t->payload;
|
|
||||||
}
|
|
||||||
logTelegram("(multical302) log", t->parsed, t->content);
|
|
||||||
int content_start = t->parsed.size();
|
|
||||||
processContent(t);
|
|
||||||
if (isDebugEnabled()) {
|
|
||||||
t->explainParse("(multical302)", content_start);
|
|
||||||
}
|
|
||||||
triggerUpdate(t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeterMultical302::processContent(Telegram *t) {
|
void MeterMultical302::processContent(Telegram *t)
|
||||||
|
{
|
||||||
vector<uchar>::iterator bytes = t->content.begin();
|
vector<uchar>::iterator bytes = t->content.begin();
|
||||||
|
|
||||||
int crc0 = t->content[0];
|
int crc0 = t->content[0];
|
||||||
|
@ -126,13 +100,6 @@ void MeterMultical302::processContent(Telegram *t) {
|
||||||
t->addExplanation(bytes, 1, "%02x frame type (%s)", frame_type, frameTypeKamstrupC1(frame_type).c_str());
|
t->addExplanation(bytes, 1, "%02x frame type (%s)", frame_type, frameTypeKamstrupC1(frame_type).c_str());
|
||||||
|
|
||||||
if (frame_type == 0x79) {
|
if (frame_type == 0x79) {
|
||||||
/*if (t->content.size() != 17) {
|
|
||||||
|
|
||||||
warning("(multical302) warning: Unexpected length of frame %zu. Expected 17 bytes! ", t->content.size());
|
|
||||||
padWithZeroesTo(&t->content, 17, &t->content);
|
|
||||||
warning("\n");
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// This code should be rewritten to use parseDV see the Multical21 code.
|
// This code should be rewritten to use parseDV see the Multical21 code.
|
||||||
// But I cannot do this without more examples of 302 telegrams.
|
// But I cannot do this without more examples of 302 telegrams.
|
||||||
t->addExplanation(bytes, 4, "%02x%02x%02x%02x unknown", t->content[3], t->content[4], t->content[5], t->content[6]);
|
t->addExplanation(bytes, 4, "%02x%02x%02x%02x unknown", t->content[3], t->content[4], t->content[5], t->content[6]);
|
||||||
|
@ -159,12 +126,6 @@ void MeterMultical302::processContent(Telegram *t) {
|
||||||
}
|
}
|
||||||
else if (frame_type == 0x78)
|
else if (frame_type == 0x78)
|
||||||
{
|
{
|
||||||
/*if (t->content.size() != 26) {
|
|
||||||
warning("(multical302) warning: Unexpected length of frame %zu. Expected 26 bytes! ", t->content.size());
|
|
||||||
padWithZeroesTo(&t->content, 26, &t->content);
|
|
||||||
warning("\n");
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// This code should be rewritten to use parseDV see the Multical21 code.
|
// This code should be rewritten to use parseDV see the Multical21 code.
|
||||||
// But I cannot do this without more examples of 302 telegrams.
|
// But I cannot do this without more examples of 302 telegrams.
|
||||||
vector<uchar> unknowns;
|
vector<uchar> unknowns;
|
||||||
|
@ -184,68 +145,3 @@ void MeterMultical302::processContent(Telegram *t) {
|
||||||
warning("(multical302) warning: unknown frame %02x (did you use the correct encryption key?)\n", frame_type);
|
warning("(multical302) warning: unknown frame %02x (did you use the correct encryption key?)\n", frame_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unique_ptr<HeatMeter> createMultical302(WMBus *bus, string& name, string& id, string& key) {
|
|
||||||
return unique_ptr<HeatMeter>(new MeterMultical302(bus,name,id,key));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MeterMultical302::printMeter(Telegram *t,
|
|
||||||
string *human_readable,
|
|
||||||
string *fields, char separator,
|
|
||||||
string *json,
|
|
||||||
vector<string> *envs)
|
|
||||||
{
|
|
||||||
char buf[65536];
|
|
||||||
buf[65535] = 0;
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf)-1, "%s\t%s\t% 3.3f kwh\t% 3.3f m3\t% 3.3f kwh\t%s",
|
|
||||||
name().c_str(),
|
|
||||||
t->id.c_str(),
|
|
||||||
totalEnergyConsumption(Unit::KWH),
|
|
||||||
totalVolume(Unit::M3),
|
|
||||||
currentPowerConsumption(Unit::KW),
|
|
||||||
datetimeOfUpdateHumanReadable().c_str());
|
|
||||||
|
|
||||||
*human_readable = buf;
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf)-1, "%s%c%s%c%f%c%f%c%f%c%s",
|
|
||||||
name().c_str(), separator,
|
|
||||||
t->id.c_str(), separator,
|
|
||||||
totalEnergyConsumption(Unit::KWH), separator,
|
|
||||||
totalVolume(Unit::M3), separator,
|
|
||||||
currentPowerConsumption(Unit::KW), separator,
|
|
||||||
datetimeOfUpdateRobot().c_str());
|
|
||||||
|
|
||||||
*fields = buf;
|
|
||||||
|
|
||||||
#define Q(x,y) "\""#x"\":"#y","
|
|
||||||
#define QS(x,y) "\""#x"\":\""#y"\","
|
|
||||||
#define QSE(x,y) "\""#x"\":\""#y"\""
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf)-1, "{"
|
|
||||||
QS(media,heat)
|
|
||||||
QS(meter,multical302)
|
|
||||||
QS(name,%s)
|
|
||||||
QS(id,%s)
|
|
||||||
Q(total_kwh,%f)
|
|
||||||
Q(total_volume_m3,%f)
|
|
||||||
QS(current_kw,%f)
|
|
||||||
QSE(timestamp,%s)
|
|
||||||
"}",
|
|
||||||
name().c_str(),
|
|
||||||
t->id.c_str(),
|
|
||||||
totalEnergyConsumption(Unit::KWH),
|
|
||||||
totalVolume(Unit::M3),
|
|
||||||
currentPowerConsumption(Unit::KW),
|
|
||||||
datetimeOfUpdateRobot().c_str());
|
|
||||||
|
|
||||||
*json = buf;
|
|
||||||
|
|
||||||
envs->push_back(string("METER_JSON=")+*json);
|
|
||||||
envs->push_back(string("METER_TYPE=multical302"));
|
|
||||||
envs->push_back(string("METER_ID=")+t->id);
|
|
||||||
envs->push_back(string("METER_TOTAL_KWH=")+to_string(totalEnergyConsumption(Unit::KWH)));
|
|
||||||
envs->push_back(string("METER_TOTAL_VOLUME_M3=")+to_string(totalVolume(Unit::M3)));
|
|
||||||
envs->push_back(string("METER_CURRENT_KW=")+to_string(currentPowerConsumption(Unit::KW)));
|
|
||||||
envs->push_back(string("METER_TIMESTAMP=")+datetimeOfUpdateRobot());
|
|
||||||
}
|
|
||||||
|
|
|
@ -55,6 +55,9 @@ MeterOmnipower::MeterOmnipower(WMBus *bus, string& name, string& id, string& key
|
||||||
MeterCommonImplementation(bus, name, id, key, MeterType::OMNIPOWER, MANUFACTURER_KAM, LinkMode::C1)
|
MeterCommonImplementation(bus, name, id, key, MeterType::OMNIPOWER, MANUFACTURER_KAM, LinkMode::C1)
|
||||||
{
|
{
|
||||||
addMedia(0x02);
|
addMedia(0x02);
|
||||||
|
|
||||||
|
setExpectedVersion(0x01);
|
||||||
|
|
||||||
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +93,6 @@ void MeterOmnipower::handleTelegram(Telegram *t) {
|
||||||
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
||||||
t->a_field_address[3]);
|
t->a_field_address[3]);
|
||||||
|
|
||||||
t->expectVersion("omnipower", 0x01);
|
|
||||||
|
|
||||||
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
||||||
warning("(omnipower) warning: telegram is encrypted but no key supplied!\n");
|
warning("(omnipower) warning: telegram is encrypted but no key supplied!\n");
|
||||||
|
|
|
@ -62,7 +62,12 @@ private:
|
||||||
MeterQCaloric::MeterQCaloric(WMBus *bus, string& name, string& id, string& key) :
|
MeterQCaloric::MeterQCaloric(WMBus *bus, string& name, string& id, string& key) :
|
||||||
MeterCommonImplementation(bus, name, id, key, MeterType::QCALORIC, MANUFACTURER_QDS, LinkMode::C1)
|
MeterCommonImplementation(bus, name, id, key, MeterType::QCALORIC, MANUFACTURER_QDS, LinkMode::C1)
|
||||||
{
|
{
|
||||||
|
setEncryptionMode(EncryptionMode::None);
|
||||||
|
|
||||||
addMedia(0x08);
|
addMedia(0x08);
|
||||||
|
|
||||||
|
setExpectedVersion(0x35);
|
||||||
|
|
||||||
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,8 +98,6 @@ void MeterQCaloric::handleTelegram(Telegram *t) {
|
||||||
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
||||||
t->a_field_address[3]);
|
t->a_field_address[3]);
|
||||||
|
|
||||||
t->expectVersion("qcaloric", 0x35);
|
|
||||||
|
|
||||||
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
||||||
warning("(qcaloric) warning: telegram is encrypted but no key supplied!\n");
|
warning("(qcaloric) warning: telegram is encrypted but no key supplied!\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,7 @@ struct MeterSupercom587 : public virtual WaterMeter, public virtual MeterCommonI
|
||||||
bool hasTotalWaterConsumption();
|
bool hasTotalWaterConsumption();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleTelegram(Telegram *t);
|
|
||||||
void processContent(Telegram *t);
|
void processContent(Telegram *t);
|
||||||
string decodeTime(int time);
|
|
||||||
|
|
||||||
double total_water_consumption_m3_ {};
|
double total_water_consumption_m3_ {};
|
||||||
};
|
};
|
||||||
|
@ -55,59 +53,21 @@ unique_ptr<WaterMeter> createSupercom587(WMBus *bus, string& name, string& id, s
|
||||||
MeterSupercom587::MeterSupercom587(WMBus *bus, string& name, string& id, string& key) :
|
MeterSupercom587::MeterSupercom587(WMBus *bus, string& name, string& id, string& key) :
|
||||||
MeterCommonImplementation(bus, name, id, key, MeterType::SUPERCOM587, MANUFACTURER_SON, LinkMode::T1)
|
MeterCommonImplementation(bus, name, id, key, MeterType::SUPERCOM587, MANUFACTURER_SON, LinkMode::T1)
|
||||||
{
|
{
|
||||||
|
setEncryptionMode(EncryptionMode::AES_CBC);
|
||||||
|
|
||||||
addMedia(0x06);
|
addMedia(0x06);
|
||||||
addMedia(0x07);
|
addMedia(0x07);
|
||||||
|
|
||||||
|
setExpectedVersion(0x3c);
|
||||||
|
|
||||||
addPrint("total", Quantity::Volume,
|
addPrint("total", Quantity::Volume,
|
||||||
[&](Unit u){ return totalWaterConsumption(u); },
|
[&](Unit u){ return totalWaterConsumption(u); },
|
||||||
"The total water consumption recorded by this meter.",
|
"The total water consumption recorded by this meter.",
|
||||||
true);
|
true, true);
|
||||||
|
|
||||||
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
||||||
}
|
}
|
||||||
|
|
||||||
double MeterSupercom587::totalWaterConsumption(Unit u)
|
|
||||||
{
|
|
||||||
assertQuantity(u, Quantity::Volume);
|
|
||||||
return convert(total_water_consumption_m3_, Unit::M3, u);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void MeterSupercom587::handleTelegram(Telegram *t)
|
|
||||||
{
|
|
||||||
if (!isTelegramForMe(t)) {
|
|
||||||
// This telegram is not intended for this meter.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
verbose("(%s) telegram for %s %02x%02x%02x%02x\n", "supercom587",
|
|
||||||
name().c_str(),
|
|
||||||
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
|
||||||
t->a_field_address[3]);
|
|
||||||
|
|
||||||
t->expectVersion("supercom587", 0x3c);
|
|
||||||
|
|
||||||
if (t->isEncrypted() && !useAes() && !t->isSimulated()) {
|
|
||||||
warning("(supercom587) warning: telegram is encrypted but no key supplied!\n");
|
|
||||||
}
|
|
||||||
if (useAes()) {
|
|
||||||
vector<uchar> aeskey = key();
|
|
||||||
decryptMode5_AES_CBC(t, aeskey);
|
|
||||||
} else {
|
|
||||||
t->content = t->payload;
|
|
||||||
}
|
|
||||||
char log_prefix[256];
|
|
||||||
snprintf(log_prefix, 255, "(%s) log", "supercom587");
|
|
||||||
logTelegram(log_prefix, t->parsed, t->content);
|
|
||||||
int content_start = t->parsed.size();
|
|
||||||
processContent(t);
|
|
||||||
if (isDebugEnabled()) {
|
|
||||||
snprintf(log_prefix, 255, "(%s)", "supercom587");
|
|
||||||
t->explainParse(log_prefix, content_start);
|
|
||||||
}
|
|
||||||
triggerUpdate(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MeterSupercom587::processContent(Telegram *t)
|
void MeterSupercom587::processContent(Telegram *t)
|
||||||
{
|
{
|
||||||
// Meter record:
|
// Meter record:
|
||||||
|
@ -121,6 +81,12 @@ void MeterSupercom587::processContent(Telegram *t)
|
||||||
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_m3_);
|
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_m3_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double MeterSupercom587::totalWaterConsumption(Unit u)
|
||||||
|
{
|
||||||
|
assertQuantity(u, Quantity::Volume);
|
||||||
|
return convert(total_water_consumption_m3_, Unit::M3, u);
|
||||||
|
}
|
||||||
|
|
||||||
bool MeterSupercom587::hasTotalWaterConsumption()
|
bool MeterSupercom587::hasTotalWaterConsumption()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -51,23 +51,25 @@ unique_ptr<HeatMeter> createVario451(WMBus *bus, string& name, string& id, strin
|
||||||
MeterVario451::MeterVario451(WMBus *bus, string& name, string& id, string& key) :
|
MeterVario451::MeterVario451(WMBus *bus, string& name, string& id, string& key) :
|
||||||
MeterCommonImplementation(bus, name, id, key, MeterType::VARIO451, MANUFACTURER_TCH, LinkMode::T1)
|
MeterCommonImplementation(bus, name, id, key, MeterType::VARIO451, MANUFACTURER_TCH, LinkMode::T1)
|
||||||
{
|
{
|
||||||
|
setEncryptionMode(EncryptionMode::None);
|
||||||
|
|
||||||
addMedia(0x04); // C telegrams
|
addMedia(0x04); // C telegrams
|
||||||
addMedia(0xC3); // T telegrams
|
addMedia(0xC3); // T telegrams
|
||||||
|
|
||||||
addPrint("total", Quantity::Energy,
|
addPrint("total", Quantity::Energy,
|
||||||
[&](Unit u){ return totalEnergyConsumption(u); },
|
[&](Unit u){ return totalEnergyConsumption(u); },
|
||||||
"The total energy consumption recorded by this meter.",
|
"The total energy consumption recorded by this meter.",
|
||||||
true);
|
true, true);
|
||||||
|
|
||||||
addPrint("current", Quantity::Energy,
|
addPrint("current", Quantity::Energy,
|
||||||
[&](Unit u){ return currentPeriodEnergyConsumption(u); },
|
[&](Unit u){ return currentPeriodEnergyConsumption(u); },
|
||||||
"Energy consumption so far in this billing period.",
|
"Energy consumption so far in this billing period.",
|
||||||
true);
|
true, true);
|
||||||
|
|
||||||
addPrint("previous", Quantity::Energy,
|
addPrint("previous", Quantity::Energy,
|
||||||
[&](Unit u){ return previousPeriodEnergyConsumption(u); },
|
[&](Unit u){ return previousPeriodEnergyConsumption(u); },
|
||||||
"Energy consumption in previous billing period.",
|
"Energy consumption in previous billing period.",
|
||||||
true);
|
true, true);
|
||||||
|
|
||||||
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
MeterCommonImplementation::bus()->onTelegram(calll(this,handleTelegram,Telegram*));
|
||||||
}
|
}
|
||||||
|
|
105
src/meters.cc
105
src/meters.cc
|
@ -66,9 +66,16 @@ void MeterCommonImplementation::addMedia(int m)
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeterCommonImplementation::addPrint(string vname, Quantity vquantity,
|
void MeterCommonImplementation::addPrint(string vname, Quantity vquantity,
|
||||||
function<double(Unit)> getValueFunc, string help, bool field)
|
function<double(Unit)> getValueFunc, string help, bool field, bool json)
|
||||||
{
|
{
|
||||||
prints_.push_back( { vname, vquantity, defaultUnitForQuantity(vquantity), getValueFunc, help, field });
|
prints_.push_back( { vname, vquantity, defaultUnitForQuantity(vquantity), getValueFunc, NULL, help, field, json });
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCommonImplementation::addPrint(string vname, Quantity vquantity,
|
||||||
|
function<string()> getValueFunc,
|
||||||
|
string help, bool field, bool json)
|
||||||
|
{
|
||||||
|
prints_.push_back( { vname, vquantity, defaultUnitForQuantity(vquantity), NULL, getValueFunc, help, field, json } );
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeterCommonImplementation::addManufacturer(int m)
|
void MeterCommonImplementation::addManufacturer(int m)
|
||||||
|
@ -240,13 +247,20 @@ string concatFields(Meter *m, Telegram *t, char c, vector<Print> &prints, vector
|
||||||
{
|
{
|
||||||
if (p.field)
|
if (p.field)
|
||||||
{
|
{
|
||||||
Unit u = replaceWithConversionUnit(p.default_unit, cs);
|
if (p.getValueDouble)
|
||||||
double v = p.getValueFunc(u);
|
{
|
||||||
if (hr) {
|
Unit u = replaceWithConversionUnit(p.default_unit, cs);
|
||||||
s += format3fdot3f(v);
|
double v = p.getValueDouble(u);
|
||||||
s += " "+unitToStringHR(u);
|
if (hr) {
|
||||||
} else {
|
s += valueToString(v, u);
|
||||||
s += to_string(v);
|
s += " "+unitToStringHR(u);
|
||||||
|
} else {
|
||||||
|
s += to_string(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p.getValueString)
|
||||||
|
{
|
||||||
|
s += p.getValueString();
|
||||||
}
|
}
|
||||||
s += c;
|
s += c;
|
||||||
}
|
}
|
||||||
|
@ -268,7 +282,7 @@ void MeterCommonImplementation::handleTelegram(Telegram *t)
|
||||||
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
t->a_field_address[0], t->a_field_address[1], t->a_field_address[2],
|
||||||
t->a_field_address[3]);
|
t->a_field_address[3]);
|
||||||
|
|
||||||
//t->expectVersion("mkradio3", 0x74);
|
t->expectVersion(meterName().c_str(), expectedVersion());
|
||||||
|
|
||||||
if (t->isEncrypted() && !useAes() && !t->isSimulated())
|
if (t->isEncrypted() && !useAes() && !t->isSimulated())
|
||||||
{
|
{
|
||||||
|
@ -277,7 +291,12 @@ void MeterCommonImplementation::handleTelegram(Telegram *t)
|
||||||
}
|
}
|
||||||
if (useAes()) {
|
if (useAes()) {
|
||||||
vector<uchar> aeskey = key();
|
vector<uchar> aeskey = key();
|
||||||
decryptMode1_AES_CTR(t, aeskey);
|
if (encryptionMode() == EncryptionMode::AES_CTR) {
|
||||||
|
decryptMode1_AES_CTR(t, aeskey);
|
||||||
|
}
|
||||||
|
if (encryptionMode() == EncryptionMode::AES_CBC) {
|
||||||
|
decryptMode1_AES_CTR(t, aeskey);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
t->content = t->payload;
|
t->content = t->payload;
|
||||||
}
|
}
|
||||||
|
@ -317,17 +336,22 @@ void MeterCommonImplementation::printMeter(Telegram *t,
|
||||||
s += "\"id\":\""+t->id+"\",";
|
s += "\"id\":\""+t->id+"\",";
|
||||||
for (Print p : prints_)
|
for (Print p : prints_)
|
||||||
{
|
{
|
||||||
if (p.field)
|
if (p.json)
|
||||||
{
|
{
|
||||||
string default_unit = unitToStringLowerCase(p.default_unit);
|
string default_unit = unitToStringLowerCase(p.default_unit);
|
||||||
string var = p.vname;
|
string var = p.vname;
|
||||||
s += "\""+var+"_"+default_unit+"\":"+to_string(p.getValueFunc(p.default_unit))+",";
|
if (p.getValueString) {
|
||||||
|
s += "\""+var+"\":\""+p.getValueString()+"\",";
|
||||||
|
}
|
||||||
|
if (p.getValueDouble) {
|
||||||
|
s += "\""+var+"_"+default_unit+"\":"+valueToString(p.getValueDouble(p.default_unit), p.default_unit)+",";
|
||||||
|
|
||||||
Unit u = replaceWithConversionUnit(p.default_unit, conversions_);
|
Unit u = replaceWithConversionUnit(p.default_unit, conversions_);
|
||||||
if (u != p.default_unit)
|
if (u != p.default_unit)
|
||||||
{
|
{
|
||||||
string unit = unitToStringLowerCase(u);
|
string unit = unitToStringLowerCase(u);
|
||||||
s += "\""+var+"_"+unit+"\":"+to_string(p.getValueFunc(u))+",";
|
s += "\""+var+"_"+unit+"\":"+valueToString(p.getValueDouble(u), u)+",";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,20 +365,25 @@ void MeterCommonImplementation::printMeter(Telegram *t,
|
||||||
|
|
||||||
for (Print p : prints_)
|
for (Print p : prints_)
|
||||||
{
|
{
|
||||||
if (p.field)
|
if (p.json)
|
||||||
{
|
{
|
||||||
string default_unit = unitToStringUpperCase(p.default_unit);
|
string default_unit = unitToStringUpperCase(p.default_unit);
|
||||||
string var = p.vname;
|
string var = p.vname;
|
||||||
std::transform(var.begin(), var.end(), var.begin(), ::toupper);
|
if (p.getValueString) {
|
||||||
string envvar = "METER_"+var+"_"+default_unit+"="+to_string(p.getValueFunc(p.default_unit));
|
s += "\""+var+"\":\""+p.getValueString()+"\",";
|
||||||
envs->push_back(envvar);
|
}
|
||||||
|
if (p.getValueDouble) {
|
||||||
Unit u = replaceWithConversionUnit(p.default_unit, conversions_);
|
std::transform(var.begin(), var.end(), var.begin(), ::toupper);
|
||||||
if (u != p.default_unit)
|
string envvar = "METER_"+var+"_"+default_unit+"="+valueToString(p.getValueDouble(p.default_unit), p.default_unit);
|
||||||
{
|
|
||||||
string unit = unitToStringUpperCase(u);
|
|
||||||
string envvar = "METER_"+var+"_"+unit+"="+to_string(p.getValueFunc(u));
|
|
||||||
envs->push_back(envvar);
|
envs->push_back(envvar);
|
||||||
|
|
||||||
|
Unit u = replaceWithConversionUnit(p.default_unit, conversions_);
|
||||||
|
if (u != p.default_unit)
|
||||||
|
{
|
||||||
|
string unit = unitToStringUpperCase(u);
|
||||||
|
string envvar = "METER_"+var+"_"+unit+"="+valueToString(p.getValueDouble(u), u);
|
||||||
|
envs->push_back(envvar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,3 +422,23 @@ double ElectricityMeter::currentPowerProduction() { return -47.11; }
|
||||||
double HeatCostMeter::currentConsumption() { return -47.11; }
|
double HeatCostMeter::currentConsumption() { return -47.11; }
|
||||||
string HeatCostMeter::setDate() { return "47.11"; }
|
string HeatCostMeter::setDate() { return "47.11"; }
|
||||||
double HeatCostMeter::consumptionAtSetDate() { return -47.11; }
|
double HeatCostMeter::consumptionAtSetDate() { return -47.11; }
|
||||||
|
|
||||||
|
void MeterCommonImplementation::setEncryptionMode(EncryptionMode em)
|
||||||
|
{
|
||||||
|
enc_mode_ = em;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptionMode MeterCommonImplementation::encryptionMode()
|
||||||
|
{
|
||||||
|
return enc_mode_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeterCommonImplementation::setExpectedVersion(int version)
|
||||||
|
{
|
||||||
|
expected_meter_version_ = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MeterCommonImplementation::expectedVersion()
|
||||||
|
{
|
||||||
|
return expected_meter_version_;
|
||||||
|
}
|
||||||
|
|
|
@ -50,7 +50,8 @@ using namespace std;
|
||||||
|
|
||||||
typedef unsigned char uchar;
|
typedef unsigned char uchar;
|
||||||
|
|
||||||
struct Meter {
|
struct Meter
|
||||||
|
{
|
||||||
virtual vector<string> ids() = 0;
|
virtual vector<string> ids() = 0;
|
||||||
virtual string meterName() = 0;
|
virtual string meterName() = 0;
|
||||||
virtual string name() = 0;
|
virtual string name() = 0;
|
||||||
|
@ -75,6 +76,8 @@ struct Meter {
|
||||||
virtual bool isTelegramForMe(Telegram *t) = 0;
|
virtual bool isTelegramForMe(Telegram *t) = 0;
|
||||||
virtual bool useAes() = 0;
|
virtual bool useAes() = 0;
|
||||||
virtual vector<uchar> key() = 0;
|
virtual vector<uchar> key() = 0;
|
||||||
|
virtual EncryptionMode encryptionMode() = 0;
|
||||||
|
virtual int expectedVersion() = 0;
|
||||||
|
|
||||||
// Dynamically access all data received for the meter.
|
// Dynamically access all data received for the meter.
|
||||||
virtual std::vector<std::string> getRecords() = 0;
|
virtual std::vector<std::string> getRecords() = 0;
|
||||||
|
|
|
@ -29,9 +29,11 @@ struct Print
|
||||||
string vname; // Value name, like: total current previous target
|
string vname; // Value name, like: total current previous target
|
||||||
Quantity quantity; // Quantity: Energy, Volume
|
Quantity quantity; // Quantity: Energy, Volume
|
||||||
Unit default_unit; // Default unit for above quantity: KWH, M3
|
Unit default_unit; // Default unit for above quantity: KWH, M3
|
||||||
function<double(Unit)> getValueFunc; // Callback to fetch the value from the meter.
|
function<double(Unit)> getValueDouble; // Callback to fetch the value from the meter.
|
||||||
|
function<string()> getValueString; // Callback to fetch the value from the meter.
|
||||||
string help; // Helpful information on this meters use of this value.
|
string help; // Helpful information on this meters use of this value.
|
||||||
bool field; // If true, print in hr/fields output.
|
bool field; // If true, print in hr/fields output.
|
||||||
|
bool json; // If true, print in json and shell env variables.
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MeterCommonImplementation : public virtual Meter
|
struct MeterCommonImplementation : public virtual Meter
|
||||||
|
@ -49,6 +51,7 @@ struct MeterCommonImplementation : public virtual Meter
|
||||||
void onUpdate(function<void(Telegram*,Meter*)> cb);
|
void onUpdate(function<void(Telegram*,Meter*)> cb);
|
||||||
int numUpdates();
|
int numUpdates();
|
||||||
|
|
||||||
|
EncryptionMode encMode();
|
||||||
bool isTelegramForMe(Telegram *t);
|
bool isTelegramForMe(Telegram *t);
|
||||||
bool useAes();
|
bool useAes();
|
||||||
vector<uchar> key();
|
vector<uchar> key();
|
||||||
|
@ -68,10 +71,17 @@ struct MeterCommonImplementation : public virtual Meter
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void triggerUpdate(Telegram *t);
|
void triggerUpdate(Telegram *t);
|
||||||
|
void setExpectedVersion(int version);
|
||||||
|
int expectedVersion();
|
||||||
void addConversions(std::vector<Unit> cs);
|
void addConversions(std::vector<Unit> cs);
|
||||||
void addMedia(int media);
|
void addMedia(int media);
|
||||||
void addManufacturer(int m);
|
void addManufacturer(int m);
|
||||||
void addPrint(string vname, Quantity vquantity, function<double(Unit)> getValueFunc, string help, bool field);
|
void addPrint(string vname, Quantity vquantity,
|
||||||
|
function<double(Unit)> getValueFunc, string help, bool field, bool json);
|
||||||
|
void addPrint(string vname, Quantity vquantity,
|
||||||
|
function<std::string()> getValueFunc, string help, bool field, bool json);
|
||||||
|
void setEncryptionMode(EncryptionMode em);
|
||||||
|
EncryptionMode encryptionMode();
|
||||||
void handleTelegram(Telegram *t);
|
void handleTelegram(Telegram *t);
|
||||||
void printMeter(Telegram *t,
|
void printMeter(Telegram *t,
|
||||||
string *human_readable,
|
string *human_readable,
|
||||||
|
@ -84,6 +94,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
MeterType type_ {};
|
MeterType type_ {};
|
||||||
|
int expected_meter_version_ {};
|
||||||
vector<int> media_;
|
vector<int> media_;
|
||||||
set<int> manufacturers_;
|
set<int> manufacturers_;
|
||||||
string name_;
|
string name_;
|
||||||
|
@ -95,6 +106,7 @@ private:
|
||||||
bool use_aes_ {};
|
bool use_aes_ {};
|
||||||
time_t datetime_of_update_ {};
|
time_t datetime_of_update_ {};
|
||||||
LinkMode required_link_mode_ {};
|
LinkMode required_link_mode_ {};
|
||||||
|
EncryptionMode enc_mode_ {};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::map<std::string,std::pair<int,std::string>> values_;
|
std::map<std::string,std::pair<int,std::string>> values_;
|
||||||
|
|
13
src/units.cc
13
src/units.cc
|
@ -133,3 +133,16 @@ Unit replaceWithConversionUnit(Unit u, vector<Unit> cs)
|
||||||
}
|
}
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string valueToString(double v, Unit u)
|
||||||
|
{
|
||||||
|
string s = to_string(v);
|
||||||
|
while (s.back() == '0') s.pop_back();
|
||||||
|
if (s.back() == '.') {
|
||||||
|
s.pop_back();
|
||||||
|
if (s.length() == 0) return "0";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
if (s.length() == 0) return "0";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
X(Volume,M3) \
|
X(Volume,M3) \
|
||||||
X(Flow,M3H) \
|
X(Flow,M3H) \
|
||||||
X(Temperature,C) \
|
X(Temperature,C) \
|
||||||
|
X(Text,TXT) \
|
||||||
|
|
||||||
|
|
||||||
#define LIST_OF_UNITS \
|
#define LIST_OF_UNITS \
|
||||||
|
@ -36,7 +37,8 @@
|
||||||
X(L,l,l,Volume,"litre") \
|
X(L,l,l,Volume,"litre") \
|
||||||
X(KW,kw,kW,Power,"kilo Watt") \
|
X(KW,kw,kW,Power,"kilo Watt") \
|
||||||
X(M3H,m3h,m3/h,Flow,"cubic meters per hour") \
|
X(M3H,m3h,m3/h,Flow,"cubic meters per hour") \
|
||||||
X(C,c,°C,Temperature,"celsius") \
|
X(C,c,°C,Temperature,"celsius") \
|
||||||
|
X(TXT,txt,txt,Text,"text") \
|
||||||
|
|
||||||
enum class Unit
|
enum class Unit
|
||||||
{
|
{
|
||||||
|
@ -63,6 +65,7 @@ Unit defaultUnitForQuantity(Quantity q);
|
||||||
std::string unitToStringHR(Unit u);
|
std::string unitToStringHR(Unit u);
|
||||||
std::string unitToStringLowerCase(Unit u);
|
std::string unitToStringLowerCase(Unit u);
|
||||||
std::string unitToStringUpperCase(Unit u);
|
std::string unitToStringUpperCase(Unit u);
|
||||||
|
std::string valueToString(double v, Unit u);
|
||||||
|
|
||||||
Unit replaceWithConversionUnit(Unit u, std::vector<Unit> cs);
|
Unit replaceWithConversionUnit(Unit u, std::vector<Unit> cs);
|
||||||
|
|
||||||
|
|
|
@ -559,7 +559,7 @@ void Telegram::explainParse(string intro, int from)
|
||||||
|
|
||||||
void Telegram::expectVersion(const char *info, int v)
|
void Telegram::expectVersion(const char *info, int v)
|
||||||
{
|
{
|
||||||
if (a_field_version != v) {
|
if (v != 0 && a_field_version != v) {
|
||||||
warning("(%s) expected telegram with version 0x%02x, but got version 0x%02x !\n", info, v, a_field_version);
|
warning("(%s) expected telegram with version 0x%02x, but got version 0x%02x !\n", info, v, a_field_version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,13 @@ LinkMode isLinkMode(const char *arg);
|
||||||
// aka little endian.
|
// aka little endian.
|
||||||
#define SN_ENC_BITS 0xc0
|
#define SN_ENC_BITS 0xc0
|
||||||
|
|
||||||
|
enum class EncryptionMode
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
AES_CBC,
|
||||||
|
AES_CTR
|
||||||
|
};
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
struct Telegram {
|
struct Telegram {
|
||||||
|
|
Ładowanie…
Reference in New Issue