Merged master into autodetecttelegram

This commit is contained in:
2024-08-25 15:26:54 +02:00
39 changed files with 5350 additions and 482 deletions
+281 -4
View File
@@ -1,5 +1,5 @@
TELEGRAM_V2_2 = (
'/ISk5\2MT382-1004\r\n'
'/ISk5\\2MT382-1004\r\n'
'\r\n'
'0-0:96.1.1(00000000000000)\r\n'
'1-0:1.8.1(00001.001*kWh)\r\n'
@@ -22,7 +22,7 @@ TELEGRAM_V2_2 = (
)
TELEGRAM_V3 = (
'/ISk5\2MT382-1000\r\n'
'/ISk5\\2MT382-1000\r\n'
'\r\n'
'0-0:96.1.1(4B384547303034303436333935353037)\r\n'
'1-0:1.8.1(12345.678*kWh)\r\n'
@@ -87,7 +87,7 @@ TELEGRAM_V4_2 = (
)
TELEGRAM_V5 = (
'/ISk5\2MT382-1000\r\n'
'/ISk5\\2MT382-1000\r\n'
'\r\n'
'1-3:0.2.8(50)\r\n'
'0-0:1.0.0(170102192002W)\r\n'
@@ -126,5 +126,282 @@ TELEGRAM_V5 = (
'0-1:24.2.1(170102161005W)(00000.107*m3)\r\n'
'0-2:24.1.0(003)\r\n'
'0-2:96.1.0()\r\n'
'!87B3\r\n'
'!6EEE\r\n'
)
# V5 telegram with 2 MBUS devices
TELEGRAM_V5_TWO_MBUS = (
'/ISK5\\2M550T-1012\r\n'
'\r\n'
'1-3:0.2.8(50)\r\n'
'0-0:1.0.0(200426223325S)\r\n'
'0-0:96.1.1(4530303434303037333832323436303139)\r\n'
'1-0:1.8.1(002130.115*kWh)\r\n'
'1-0:1.8.2(000245.467*kWh)\r\n'
'1-0:2.8.1(000000.000*kWh)\r\n'
'1-0:2.8.2(000000.000*kWh)\r\n'
'0-0:96.14.0(0001)\r\n'
'1-0:1.7.0(00.111*kW)\r\n'
'1-0:2.7.0(00.000*kW)\r\n'
'0-0:96.7.21(00005)\r\n'
'0-0:96.7.9(00003)\r\n'
'1-0:99.97.0(1)(0-0:96.7.19)(190326095015W)(0000002014*s)\r\n'
'1-0:32.32.0(00001)\r\n'
'1-0:52.32.0(00001)\r\n'
'1-0:72.32.0(00192)\r\n'
'1-0:32.36.0(00001)\r\n'
'1-0:52.36.0(00001)\r\n'
'1-0:72.36.0(00001)\r\n'
'0-0:96.13.0()\r\n'
'1-0:32.7.0(229.9*V)\r\n'
'1-0:52.7.0(229.2*V)\r\n'
'1-0:72.7.0(222.9*V)\r\n'
'1-0:31.7.0(000*A)\r\n'
'1-0:51.7.0(000*A)\r\n'
'1-0:71.7.0(001*A)\r\n'
'1-0:21.7.0(00.056*kW)\r\n'
'1-0:41.7.0(00.000*kW)\r\n'
'1-0:61.7.0(00.055*kW)\r\n'
'1-0:22.7.0(00.000*kW)\r\n'
'1-0:42.7.0(00.000*kW)\r\n'
'1-0:62.7.0(00.000*kW)\r\n'
'0-1:24.1.0(003)\r\n'
'0-1:96.1.0()\r\n'
'0-1:24.2.1(700101010000W)(00000000)\r\n'
'0-2:24.1.0(003)\r\n'
'0-2:96.1.0(4730303339303031393336393930363139)\r\n'
'0-2:24.2.1(200426223001S)(00246.138*m3)\r\n'
'!56DD\r\n'
)
TELEGRAM_FLUVIUS_V171 = (
'/FLU5\\253769484_A\r\n'
'\r\n'
'0-0:96.1.4(50217)\r\n'
'0-0:96.1.1(3153414733313031303231363035)\r\n'
'0-0:1.0.0(200512135409S)\r\n'
'1-0:1.8.1(000000.034*kWh)\r\n'
'1-0:1.8.2(000015.758*kWh)\r\n'
'1-0:2.8.1(000000.000*kWh)\r\n'
'1-0:2.8.2(000000.011*kWh)\r\n'
'1-0:1.4.0(02.351*kW)\r\n'
'1-0:1.6.0(200509134558S)(02.589*kW)\r\n'
'0-0:98.1.0(3)(1-0:1.6.0)(1-0:1.6.0)(200501000000S)(200423192538S)(03.695*kW)(200401000000S)(200305122139S)(05.980*kW)(200301000000S)(200210035421W)(04.318*kW)\r\n'
'0-0:96.14.0(0001)\r\n'
'1-0:1.7.0(00.000*kW)\r\n'
'1-0:2.7.0(00.000*kW)\r\n'
'1-0:21.7.0(00.000*kW)\r\n'
'1-0:41.7.0(00.000*kW)\r\n'
'1-0:61.7.0(00.000*kW)\r\n'
'1-0:22.7.0(00.000*kW)\r\n'
'1-0:42.7.0(00.000*kW)\r\n'
'1-0:62.7.0(00.000*kW)\r\n'
'1-0:32.7.0(234.7*V)\r\n'
'1-0:52.7.0(234.7*V)\r\n'
'1-0:72.7.0(234.7*V)\r\n'
'1-0:31.7.0(000.00*A)\r\n'
'1-0:51.7.0(000.00*A)\r\n'
'1-0:71.7.0(000.00*A)\r\n'
'0-0:96.3.10(1)\r\n'
'0-0:17.0.0(999.9*kW)\r\n'
'1-0:31.4.0(999*A)\r\n'
'0-0:96.13.0()\r\n'
'0-1:24.1.0(003)\r\n'
'0-1:96.1.1(37464C4F32313139303333373333)\r\n'
'0-1:24.4.0(1)\r\n'
'0-1:24.2.3(200512134558S)(00112.384*m3)\r\n'
'0-2:24.1.0(007)\r\n'
'0-2:96.1.1(3853414731323334353637383930)\r\n'
'0-2:24.2.1(200512134558S)(00872.234*m3)\r\n'
'!3AD7\r\n'
)
TELEGRAM_FLUVIUS_V171_ALT = (
'/FLU5\\253769484_A\r\n'
'\r\n'
'0-0:96.1.4(50217)\r\n'
'0-0:96.1.1(3153414733313030373231333236)\r\n'
'0-0:1.0.0(231102121548W)\r\n'
'1-0:1.8.1(000301.548*kWh)\r\n'
'1-0:1.8.2(000270.014*kWh)\r\n'
'1-0:2.8.1(000000.005*kWh)\r\n'
'1-0:2.8.2(000000.000*kWh)\r\n'
'0-0:96.14.0(0001)\r\n'
'1-0:1.4.0(00.052*kW)\r\n'
'1-0:1.6.0(231102114500W)(03.064*kW)\r\n'
'0-0:98.1.0(4)(1-0:1.6.0)(1-0:1.6.0)(230801000000S)(632525252525W)(00.000*kW)(230901000000S)(230831181500S)(01.862*kW)(231001000000S)(230910183000S)(04.229*kW)(231101000000W)(231016130000S)(04.927*kW)\r\n'
'1-0:1.7.0(00.338*kW)\r\n'
'1-0:2.7.0(00.000*kW)\r\n'
'1-0:21.7.0(00.047*kW)\r\n'
'1-0:41.7.0(00.179*kW)\r\n'
'1-0:61.7.0(00.111*kW)\r\n'
'1-0:22.7.0(00.000*kW)\r\n'
'1-0:42.7.0(00.000*kW)\r\n'
'1-0:62.7.0(00.000*kW)\r\n'
'1-0:32.7.0(232.9*V)\r\n'
'1-0:52.7.0(228.1*V)\r\n'
'1-0:72.7.0(228.1*V)\r\n'
'1-0:31.7.0(000.27*A)\r\n'
'1-0:51.7.0(000.88*A)\r\n'
'1-0:71.7.0(000.52*A)\r\n'
'0-0:96.3.10(1)\r\n'
'0-0:17.0.0(999.9*kW)\r\n'
'1-0:31.4.0(999*A)\r\n'
'0-0:96.13.0()\r\n'
'0-1:24.1.0(003)\r\n'
'0-1:96.1.1(37464C4F32313233303838303237)\r\n'
'0-1:24.4.0(1)\r\n'
'0-1:24.2.3(231102121002W)(00092.287*m3)\r\n'
'0-2:24.1.0(007)\r\n'
'0-2:96.1.1(3853455430303030393631313733)\r\n'
'0-2:24.2.1(231102121532W)(00008.579*m3)\r\n'
'!C4B0\r\n'
)
# EasyMeter via COM-1 Ethernet Gateway
# Q3D Manual (german) https://www.easymeter.com/downloads/products/zaehler/Q3D/Easymeter_Q3D_DE_2016-06-15.pdf
# - type code on page 8
# - D0-Specs on page 20
#
# last two lines are added by the COM-1 Ethernet Gateway
TELEGRAM_ESY5Q3DB1024_V304 = (
'/ESY5Q3DB1024 V3.04\r\n'
'\r\n'
'1-0:0.0.0*255(0272031312565)\r\n'
'1-0:1.8.0*255(00052185.7825309*kWh)\r\n'
'1-0:2.8.0*255(00019949.3221493*kWh)\r\n'
'1-0:21.7.0*255(000747.85*W)\r\n'
'1-0:41.7.0*255(000737.28*W)\r\n'
'1-0:61.7.0*255(000639.73*W)\r\n'
'1-0:1.7.0*255(002124.86*W)\r\n'
'1-0:96.5.5*255(80)\r\n'
'0-0:96.1.255*255(1ESY1313002565)\r\n'
'!\r\n'
' 25803103\r\n'
'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
'\xff\xff\xff\xff\xff\r\n'
)
TELEGRAM_ESY5Q3DA1004_V304 = (
'/ESY5Q3DA1004 V3.04\r\n'
'\r\n'
'1-0:0.0.0*255(1336001560)\r\n'
'1-0:1.8.0*255(00032549.5061662*kWh)\r\n'
'1-0:21.7.0*255(000557.29*W)\r\n'
'1-0:41.7.0*255(000521.62*W)\r\n'
'1-0:61.7.0*255(000609.30*W)\r\n'
'1-0:1.7.0*255(001688.21*W)\r\n'
'1-0:96.5.5*255(80)\r\n'
'0-0:96.1.255*255(1ESY1336001560)\r\n'
'!\r\n'
' 25818685\r\n'
'DE0000000000000000000000000000003\r\n'
)
TELEGRAM_SAGEMCOM_T210_D_R = (
'/EST5\\253710000_A\r\n'
'\r\n'
'1-3:0.2.8(50)\r\n'
'0-0:1.0.0(221006155014S)\r\n'
'1-0:1.8.0(006545766*Wh)\r\n'
'1-0:1.8.1(005017120*Wh)\r\n'
'1-0:1.8.2(001528646*Wh)\r\n'
'1-0:1.7.0(000000286*W)\r\n'
'1-0:2.8.0(000000058*Wh)\r\n'
'1-0:2.8.1(000000000*Wh)\r\n'
'1-0:2.8.2(000000058*Wh)\r\n'
'1-0:2.7.0(000000000*W)\r\n'
'1-0:3.8.0(000000747*varh)\r\n'
'1-0:3.8.1(000000000*varh)\r\n'
'1-0:3.8.2(000000747*varh)\r\n'
'1-0:3.7.0(000000000*var)\r\n'
'1-0:4.8.0(003897726*varh)\r\n'
'1-0:4.8.1(002692848*varh)\r\n'
'1-0:4.8.2(001204878*varh)\r\n'
'1-0:4.7.0(000000166*var)\r\n'
'!7EF9\r\n'
)
TELEGRAM_ISKRA_IE = (
'/ISk5\2MIE5T-200\r\n'
'\r\n'
'1-0:0.0.0(00000000)\r\n'
'0-0:96.1.0(09610)\r\n'
'0-0:1.0.0(230202132747S)\r\n'
'1-0:1.8.1(000010.181*kWh)\r\n'
'1-0:1.8.2(000010.182*kWh)\r\n'
'1-0:2.8.1(000010.281*kWh)\r\n'
'1-0:2.8.2(000010.282*kWh)\r\n'
'0-0:96.14.0(0001)\r\n'
'1-0:1.7.0(00.170*kW)\r\n'
'1-0:2.7.0(00.270*kW)\r\n'
'1-0:21.7.0(00.217*kW)\r\n'
'1-0:41.7.0(00.417*kW)\r\n'
'1-0:61.7.0(00.617*kW)\r\n'
'1-0:22.7.0(00.227*kW)\r\n'
'1-0:42.7.0(00.427*kW)\r\n'
'1-0:62.7.0(00.627*kW)\r\n'
'1-0:32.7.0(242.5*V)\r\n'
'1-0:52.7.0(241.7*V)\r\n'
'1-0:72.7.0(243.3*V)\r\n'
'1-0:31.7.0(000*A)\r\n'
'1-0:51.7.0(000*A)\r\n'
'1-0:71.7.0(000*A)\r\n'
'0-0:96.3.10(1)\r\n'
'0-0:96.13.0()\r\n'
'0-1:96.1.1()\r\n'
'!AD3B\r\n'
)
# V5 telegram of EON in Hungary
TELEGRAM_V5_EON_HU = (
'/SAG5SAG-METER\r\n'
'\r\n'
'0-0:1.0.0(230724150730S)\r\n'
'0-0:42.0.0(53414733303832323030303032313630)\r\n'
'0-0:96.1.0(383930303832323030303032313630)\r\n'
'0-0:96.14.0(0001)\r\n'
'0-0:96.3.10(1)\r\n'
'0-0:17.0.0(90.000*kW)\r\n'
'1-0:1.8.0(000173.640*kWh)\r\n'
'1-0:1.8.1(000047.719*kWh)\r\n'
'1-0:1.8.2(000125.921*kWh)\r\n'
'1-0:1.8.3(000000.000*kWh)\r\n'
'1-0:1.8.4(000000.000*kWh)\r\n'
'1-0:2.8.0(000627.177*kWh)\r\n'
'1-0:2.8.1(000401.829*kWh)\r\n'
'1-0:2.8.2(000225.348*kWh)\r\n'
'1-0:2.8.3(000000.000*kWh)\r\n'
'1-0:2.8.4(000000.000*kWh)\r\n'
'1-0:3.8.0(000000.123*kvarh)\r\n'
'1-0:4.8.0(000303.131*kvarh)\r\n'
'1-0:5.8.0(000000.668*kvarh)\r\n'
'1-0:6.8.0(000000.071*kvarh)\r\n'
'1-0:7.8.0(000160.487*kvarh)\r\n'
'1-0:8.8.0(000143.346*kvarh)\r\n'
'1-0:15.8.0(000800.817*kWh)\r\n'
'1-0:32.7.0(240.4*V)\r\n'
'1-0:52.7.0(239.1*V)\r\n'
'1-0:72.7.0(241.2*V)\r\n'
'1-0:31.7.0(003*A)\r\n'
'1-0:51.7.0(004*A)\r\n'
'1-0:71.7.0(003*A)\r\n'
'1-0:13.7.0(4.556)\r\n'
'1-0:33.7.0(4.591)\r\n'
'1-0:53.7.0(4.542)\r\n'
'1-0:73.7.0(4.552)\r\n'
'1-0:14.7.0(50.00*Hz)\r\n'
'1-0:1.7.0(00.000*kW)\r\n'
'1-0:2.7.0(02.601*kW)\r\n'
'1-0:5.7.0(00.000*kvar)\r\n'
'1-0:6.7.0(00.000*kvar)\r\n'
'1-0:7.7.0(00.504*kvar)\r\n'
'1-0:8.7.0(00.000*kvar)\r\n'
'1-0:31.4.0(200.00*A)\r\n'
'1-0:51.4.0(200.00*A)\r\n'
'1-0:71.4.0(200.00*A)\r\n'
'0-0:98.1.0(230701000000S)(000040.777*kWh)(000008.950*kWh)(000031.827*kWh)(000142.250*kWh)(000111.164*kWh)(000031.086*kWh)(000000.030*kvarh)(000073.988*kvarh)(000000.205*kvarh)(000000.048*kvarh)(000039.199*kvarh)(000035.020*kvarh)(000183.027*kWh)(03.564*kW)(02.156*kW)(03.564*kW)(04.104*kW)(04.104*kW)(03.400*kW)\r\n'
'0-0:96.13.0()\r\n'
'!99DA\r\n'
)
+7
View File
@@ -0,0 +1,7 @@
from dsmr_parser import telegram_specifications
from dsmr_parser.parsers import TelegramParser
from test.example_telegrams import TELEGRAM_V4_2
parser = TelegramParser(telegram_specifications.V4)
telegram = parser.parse(TELEGRAM_V4_2)
print(telegram)
View File
+73
View File
@@ -0,0 +1,73 @@
from decimal import Decimal
import json
import unittest
from dsmr_parser import telegram_specifications, obis_references
from dsmr_parser.objects import MbusDevice
class MbusDeviceTest(unittest.TestCase):
def setUp(self):
v5_objects = telegram_specifications.V5['objects']
device_type_parser = [
object["value_parser"]
for object in v5_objects
if object["obis_reference"] == obis_references.MBUS_DEVICE_TYPE
][0]
device_type = device_type_parser.parse('0-2:24.1.0(003)\r\n')
equipment_parser = [
object["value_parser"]
for object in v5_objects
if object["obis_reference"] == obis_references.MBUS_EQUIPMENT_IDENTIFIER
][0]
equipment = equipment_parser.parse('0-2:96.1.0(4730303339303031393336393930363139)\r\n')
gas_reading_parser = [
object["value_parser"]
for object in v5_objects
if object["obis_reference"] == obis_references.MBUS_METER_READING
][0]
gas_reading = gas_reading_parser.parse('0-2:24.2.1(200426223001S)(00246.138*m3)\r\n')
mbus_device = MbusDevice(channel_id=2)
mbus_device.add(obis_references.MBUS_DEVICE_TYPE, device_type, "MBUS_DEVICE_TYPE")
mbus_device.add(obis_references.MBUS_EQUIPMENT_IDENTIFIER, equipment, "MBUS_EQUIPMENT_IDENTIFIER")
mbus_device.add(obis_references.MBUS_METER_READING, gas_reading, "MBUS_METER_READING")
self.mbus_device = mbus_device
def test_attributes(self):
self.assertEqual(self.mbus_device.MBUS_DEVICE_TYPE.value, 3)
self.assertEqual(self.mbus_device.MBUS_DEVICE_TYPE.unit, None)
self.assertEqual(self.mbus_device.MBUS_EQUIPMENT_IDENTIFIER.value,
'4730303339303031393336393930363139')
self.assertEqual(self.mbus_device.MBUS_EQUIPMENT_IDENTIFIER.unit, None)
self.assertEqual(self.mbus_device.MBUS_METER_READING.value, Decimal('246.138'))
self.assertEqual(self.mbus_device.MBUS_METER_READING.unit, 'm3')
def test_to_json(self):
self.assertEqual(
json.loads(self.mbus_device.to_json()),
{
'CHANNEL_ID': 2,
'MBUS_DEVICE_TYPE': {'value': 3, 'unit': None},
'MBUS_EQUIPMENT_IDENTIFIER': {'value': '4730303339303031393336393930363139', 'unit': None},
'MBUS_METER_READING': {'datetime': '2020-04-26T20:30:01+00:00', 'value': 246.138, 'unit': 'm3'}}
)
def test_str(self):
self.assertEqual(
str(self.mbus_device),
(
'MBUS DEVICE (channel 2)\n'
'\tMBUS_DEVICE_TYPE: 3 [None]\n'
'\tMBUS_EQUIPMENT_IDENTIFIER: 4730303339303031393336393930363139 [None]\n'
'\tMBUS_METER_READING: 246.138 [m3] at 2020-04-26T20:30:01+00:00\n'
)
)
+88
View File
@@ -0,0 +1,88 @@
import unittest
from dsmr_parser import telegram_specifications
from dsmr_parser.objects import ProfileGenericObject
from dsmr_parser.parsers import TelegramParser
from dsmr_parser.parsers import ProfileGenericParser
from dsmr_parser.profile_generic_specifications import BUFFER_TYPES
from dsmr_parser.profile_generic_specifications import PG_HEAD_PARSERS
from dsmr_parser.profile_generic_specifications import PG_UNIDENTIFIED_BUFFERTYPE_PARSERS
from test.example_telegrams import TELEGRAM_V5
class TestParserCornerCases(unittest.TestCase):
""" Test instantiation of Telegram object """
def test_power_event_log_empty_1(self):
# POWER_EVENT_FAILURE_LOG (1-0:99.97.0)
parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(TELEGRAM_V5)
object_type = ProfileGenericObject
testitem = telegram.POWER_EVENT_FAILURE_LOG
assert isinstance(testitem, object_type)
assert testitem.buffer_length == 0
assert testitem.buffer_type == '0-0:96.7.19'
buffer = testitem.buffer
assert isinstance(testitem.buffer, list)
assert len(buffer) == 0
def test_power_event_log_empty_2(self):
pef_parser = ProfileGenericParser(BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS)
object_type = ProfileGenericObject
# Power Event Log with 0 items and no object type
pefl_line = r'1-0:99.97.0(0)()\r\n'
testitem = pef_parser.parse(pefl_line)
assert isinstance(testitem, object_type)
assert testitem.buffer_length == 0
assert testitem.buffer_type is None
buffer = testitem.buffer
assert isinstance(testitem.buffer, list)
assert len(buffer) == 0
assert testitem.values == [{'value': 0, 'unit': None}, {'value': None, 'unit': None}]
json = testitem.to_json()
assert json == '{"buffer_length": 0, "buffer_type": null, "buffer": []}'
def test_power_event_log_null_values(self):
pef_parser = ProfileGenericParser(BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS)
object_type = ProfileGenericObject
# Power Event Log with 1 item and no object type and nno values for the item
pefl_line = r'1-0:99.97.0(1)()()()\r\n'
testitem = pef_parser.parse(pefl_line)
assert isinstance(testitem, object_type)
assert testitem.buffer_length == 1
assert testitem.buffer_type is None
buffer = testitem.buffer
assert isinstance(testitem.buffer, list)
assert len(buffer) == 1
assert testitem.values == [{'value': 1, 'unit': None}, {'value': None, 'unit': None},
{'value': None, 'unit': None}, {'value': None, 'unit': None}]
json = testitem.to_json()
assert json == \
'{"buffer_length": 1, "buffer_type": null, "buffer": [{"datetime": null, "value": null, "unit": null}]}'
def test_power_event_log_brackets_only(self):
# POWER_EVENT_FAILURE_LOG (1-0:99.97.0)
# Issue 57
# Test of an ill formatted empty POWER_EVENT_FAILURE_LOG, observed on some smartmeters
# The idea is that instead of failing, the parser converts it to an empty POWER_EVENT_FAILURE_LOG
pef_parser = ProfileGenericParser(BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS)
object_type = ProfileGenericObject
pefl_line = r'1-0:99.97.0()\r\n'
testitem = pef_parser.parse(pefl_line)
assert isinstance(testitem, object_type)
assert testitem.buffer_length == 0
assert testitem.buffer_type is None
buffer = testitem.buffer
assert isinstance(testitem.buffer, list)
assert len(buffer) == 0
assert testitem.values == [{'value': 0, 'unit': None}, {'value': None, 'unit': None}]
json = testitem.to_json()
assert json == '{"buffer_length": 0, "buffer_type": null, "buffer": []}'
+486
View File
@@ -0,0 +1,486 @@
import json
import unittest
import datetime
import pytz
from dsmr_parser import telegram_specifications, obis_references
from dsmr_parser.objects import CosemObject
from dsmr_parser.objects import MBusObject
from dsmr_parser.objects import ProfileGenericObject
from dsmr_parser.parsers import TelegramParser
from test.example_telegrams import TELEGRAM_V4_2, TELEGRAM_V5_TWO_MBUS, TELEGRAM_V5
from decimal import Decimal
class TelegramTest(unittest.TestCase):
""" Test instantiation of Telegram object """
def __init__(self, *args, **kwargs):
self.item_names_tested = []
super(TelegramTest, self).__init__(*args, **kwargs)
def verify_telegram_item(self, telegram, testitem_name, object_type, unit_val, value_type, value_val):
testitem = eval("telegram.{}".format(testitem_name))
assert isinstance(testitem, object_type)
assert testitem.unit == unit_val
assert isinstance(testitem.value, value_type)
assert testitem.value == value_val
self.item_names_tested.append(testitem_name)
def test_instantiate(self):
parser = TelegramParser(telegram_specifications.V4)
telegram = parser.parse(TELEGRAM_V4_2)
# P1_MESSAGE_HEADER (1-3:0.2.8)
self.verify_telegram_item(telegram,
'P1_MESSAGE_HEADER',
object_type=CosemObject,
unit_val=None,
value_type=str,
value_val='42')
# P1_MESSAGE_TIMESTAMP (0-0:1.0.0)
self.verify_telegram_item(telegram,
'P1_MESSAGE_TIMESTAMP',
CosemObject,
unit_val=None,
value_type=datetime.datetime,
value_val=datetime.datetime(2016, 11, 13, 19, 57, 57, tzinfo=pytz.UTC))
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
self.verify_telegram_item(telegram,
'ELECTRICITY_USED_TARIFF_1',
object_type=CosemObject,
unit_val='kWh',
value_type=Decimal,
value_val=Decimal('1581.123'))
# ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2)
self.verify_telegram_item(telegram,
'ELECTRICITY_USED_TARIFF_2',
object_type=CosemObject,
unit_val='kWh',
value_type=Decimal,
value_val=Decimal('1435.706'))
# ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1)
self.verify_telegram_item(telegram,
'ELECTRICITY_DELIVERED_TARIFF_1',
object_type=CosemObject,
unit_val='kWh',
value_type=Decimal,
value_val=Decimal('0'))
# ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2)
self.verify_telegram_item(telegram,
'ELECTRICITY_DELIVERED_TARIFF_2',
object_type=CosemObject,
unit_val='kWh',
value_type=Decimal,
value_val=Decimal('0'))
# ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0)
self.verify_telegram_item(telegram,
'ELECTRICITY_ACTIVE_TARIFF',
object_type=CosemObject,
unit_val=None,
value_type=str,
value_val='0002')
# EQUIPMENT_IDENTIFIER (0-0:96.1.1)
self.verify_telegram_item(telegram,
'EQUIPMENT_IDENTIFIER',
object_type=CosemObject,
unit_val=None,
value_type=str,
value_val='3960221976967177082151037881335713')
# CURRENT_ELECTRICITY_USAGE (1-0:1.7.0)
self.verify_telegram_item(telegram,
'CURRENT_ELECTRICITY_USAGE',
object_type=CosemObject,
unit_val='kW',
value_type=Decimal,
value_val=Decimal('2.027'))
# CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0)
self.verify_telegram_item(telegram,
'CURRENT_ELECTRICITY_DELIVERY',
object_type=CosemObject,
unit_val='kW',
value_type=Decimal,
value_val=Decimal('0'))
# SHORT_POWER_FAILURE_COUNT (1-0:96.7.21)
self.verify_telegram_item(telegram,
'SHORT_POWER_FAILURE_COUNT',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=15)
# LONG_POWER_FAILURE_COUNT (96.7.9)
self.verify_telegram_item(telegram,
'LONG_POWER_FAILURE_COUNT',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=7)
# VOLTAGE_SAG_L1_COUNT (1-0:32.32.0)
self.verify_telegram_item(telegram,
'VOLTAGE_SAG_L1_COUNT',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=0)
# VOLTAGE_SAG_L2_COUNT (1-0:52.32.0)
self.verify_telegram_item(telegram,
'VOLTAGE_SAG_L2_COUNT',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=0)
# VOLTAGE_SAG_L3_COUNT (1-0:72.32.0)
self.verify_telegram_item(telegram,
'VOLTAGE_SAG_L3_COUNT',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=0)
# VOLTAGE_SWELL_L1_COUNT (1-0:32.36.0)
self.verify_telegram_item(telegram,
'VOLTAGE_SWELL_L1_COUNT',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=0)
# VOLTAGE_SWELL_L2_COUNT (1-0:52.36.0)
self.verify_telegram_item(telegram,
'VOLTAGE_SWELL_L2_COUNT',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=0)
# VOLTAGE_SWELL_L3_COUNT (1-0:72.36.0)
self.verify_telegram_item(telegram,
'VOLTAGE_SWELL_L3_COUNT',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=0)
# TEXT_MESSAGE_CODE (0-0:96.13.1)
self.verify_telegram_item(telegram,
'TEXT_MESSAGE_CODE',
object_type=CosemObject,
unit_val=None,
value_type=type(None),
value_val=None)
# TEXT_MESSAGE (0-0:96.13.0)
self.verify_telegram_item(telegram,
'TEXT_MESSAGE',
object_type=CosemObject,
unit_val=None,
value_type=type(None),
value_val=None)
# INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0)
self.verify_telegram_item(telegram,
'INSTANTANEOUS_CURRENT_L1',
object_type=CosemObject,
unit_val='A',
value_type=Decimal,
value_val=Decimal('0'))
# INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0)
self.verify_telegram_item(telegram,
'INSTANTANEOUS_CURRENT_L2',
object_type=CosemObject,
unit_val='A',
value_type=Decimal,
value_val=Decimal('6'))
# INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0)
self.verify_telegram_item(telegram,
'INSTANTANEOUS_CURRENT_L3',
object_type=CosemObject,
unit_val='A',
value_type=Decimal,
value_val=Decimal('2'))
# INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0)
self.verify_telegram_item(telegram,
'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE',
object_type=CosemObject,
unit_val='kW',
value_type=Decimal,
value_val=Decimal('0.170'))
# INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0)
self.verify_telegram_item(telegram,
'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE',
object_type=CosemObject,
unit_val='kW',
value_type=Decimal,
value_val=Decimal('1.247'))
# INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0)
self.verify_telegram_item(telegram,
'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE',
object_type=CosemObject,
unit_val='kW',
value_type=Decimal,
value_val=Decimal('0.209'))
# INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0)
self.verify_telegram_item(telegram,
'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE',
object_type=CosemObject,
unit_val='kW',
value_type=Decimal,
value_val=Decimal('0'))
# INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0)
self.verify_telegram_item(telegram,
'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE',
object_type=CosemObject,
unit_val='kW',
value_type=Decimal,
value_val=Decimal('0'))
# INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0)
self.verify_telegram_item(telegram,
'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE',
object_type=CosemObject,
unit_val='kW',
value_type=Decimal,
value_val=Decimal('0'))
# DEVICE_TYPE (0-1:24.1.0)
self.verify_telegram_item(telegram,
'DEVICE_TYPE',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=3)
# EQUIPMENT_IDENTIFIER_GAS (0-1:96.1.0)
self.verify_telegram_item(telegram,
'EQUIPMENT_IDENTIFIER_GAS',
object_type=CosemObject,
unit_val=None,
value_type=str,
value_val='4819243993373755377509728609491464')
# HOURLY_GAS_METER_READING (0-1:24.2.1)
self.verify_telegram_item(telegram,
'HOURLY_GAS_METER_READING',
object_type=MBusObject,
unit_val='m3',
value_type=Decimal,
value_val=Decimal('981.443'))
# POWER_EVENT_FAILURE_LOG (1-0:99.97.0)
testitem_name = 'POWER_EVENT_FAILURE_LOG'
object_type = ProfileGenericObject
testitem = eval("telegram.{}".format(testitem_name))
assert isinstance(testitem, object_type)
assert testitem.buffer_length == 3
assert testitem.buffer_type == '0-0:96.7.19'
buffer = testitem.buffer
assert isinstance(testitem.buffer, list)
assert len(buffer) == 3
assert all([isinstance(item, MBusObject) for item in buffer])
date0 = datetime.datetime(2000, 1, 4, 17, 3, 20, tzinfo=datetime.timezone.utc)
date1 = datetime.datetime(1999, 12, 31, 23, 0, 1, tzinfo=datetime.timezone.utc)
date2 = datetime.datetime(2000, 1, 1, 23, 0, 3, tzinfo=datetime.timezone.utc)
assert buffer[0].datetime == date0
assert buffer[1].datetime == date1
assert buffer[2].datetime == date2
assert buffer[0].value == 237126
assert buffer[1].value == 2147583646
assert buffer[2].value == 2317482647
assert all([isinstance(item.value, int) for item in buffer])
assert all([isinstance(item.unit, str) for item in buffer])
assert all([(item.unit == 's') for item in buffer])
self.item_names_tested.append(testitem_name)
# check if all items in telegram V4 specification are covered
V4_name_list = [object["value_name"] for object in
telegram_specifications.V4['objects']]
V4_name_set = set(V4_name_list)
item_names_tested_set = set(self.item_names_tested)
assert item_names_tested_set == V4_name_set
def test_iter(self):
parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(TELEGRAM_V5)
for obis_name, dsmr_object in telegram:
break
# Verify that the iterator works for at least one value
self.assertEqual(obis_name, "P1_MESSAGE_HEADER")
self.assertEqual(dsmr_object.value, '50')
def test_mbus_devices(self):
parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(TELEGRAM_V5_TWO_MBUS)
mbus_devices = telegram.MBUS_DEVICES
self.assertEqual(len(mbus_devices), 2)
mbus_device_1 = mbus_devices[0]
self.assertEqual(mbus_device_1.MBUS_DEVICE_TYPE.value, 3)
self.assertEqual(mbus_device_1.MBUS_EQUIPMENT_IDENTIFIER.value, None)
self.assertEqual(mbus_device_1.MBUS_METER_READING.value, Decimal('0'))
mbus_device_2 = mbus_devices[1]
self.assertEqual(mbus_device_2.MBUS_DEVICE_TYPE.value, 3)
self.assertEqual(mbus_device_2.MBUS_EQUIPMENT_IDENTIFIER.value, '4730303339303031393336393930363139')
self.assertEqual(mbus_device_2.MBUS_METER_READING.value, Decimal('246.138'))
def test_get_mbus_device_by_channel(self):
parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(TELEGRAM_V5_TWO_MBUS)
mbus_device_1 = telegram.get_mbus_device_by_channel(1)
self.assertEqual(mbus_device_1.MBUS_DEVICE_TYPE.value, 3)
self.assertEqual(mbus_device_1.MBUS_EQUIPMENT_IDENTIFIER.value, None)
self.assertEqual(mbus_device_1.MBUS_METER_READING.value, Decimal('0'))
mbus_device_2 = telegram.get_mbus_device_by_channel(2)
self.assertEqual(mbus_device_2.MBUS_DEVICE_TYPE.value, 3)
self.assertEqual(mbus_device_2.MBUS_EQUIPMENT_IDENTIFIER.value, '4730303339303031393336393930363139')
self.assertEqual(mbus_device_2.MBUS_METER_READING.value, Decimal('246.138'))
def test_without_mbus_devices(self):
parser = TelegramParser(telegram_specifications.V5, apply_checksum_validation=False)
telegram = parser.parse('')
self.assertFalse(hasattr(telegram, 'MBUS_DEVICES'))
self.assertIsNone(telegram.get_mbus_device_by_channel(1))
def test_to_json(self):
parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(TELEGRAM_V5)
json_data = json.loads(telegram.to_json())
self.maxDiff = None
self.assertEqual(
json_data,
{'CURRENT_ELECTRICITY_DELIVERY': {'unit': 'kW', 'value': 0.0},
'CURRENT_ELECTRICITY_USAGE': {'unit': 'kW', 'value': 0.244},
'ELECTRICITY_ACTIVE_TARIFF': {'unit': None, 'value': '0002'},
'ELECTRICITY_DELIVERED_TARIFF_1': {'unit': 'kWh', 'value': 2.444},
'ELECTRICITY_DELIVERED_TARIFF_2': {'unit': 'kWh', 'value': 0.0},
'ELECTRICITY_USED_TARIFF_1': {'unit': 'kWh', 'value': 4.426},
'ELECTRICITY_USED_TARIFF_2': {'unit': 'kWh', 'value': 2.399},
'EQUIPMENT_IDENTIFIER': {'unit': None,
'value': '4B384547303034303436333935353037'},
'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE': {'unit': 'kW', 'value': 0.0},
'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE': {'unit': 'kW', 'value': 0.07},
'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE': {'unit': 'kW', 'value': 0.0},
'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE': {'unit': 'kW', 'value': 0.032},
'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE': {'unit': 'kW', 'value': 0.0},
'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE': {'unit': 'kW', 'value': 0.142},
'INSTANTANEOUS_CURRENT_L1': {'unit': 'A', 'value': 0.48},
'INSTANTANEOUS_CURRENT_L2': {'unit': 'A', 'value': 0.44},
'INSTANTANEOUS_CURRENT_L3': {'unit': 'A', 'value': 0.86},
'INSTANTANEOUS_VOLTAGE_L1': {'unit': 'V', 'value': 230.0},
'INSTANTANEOUS_VOLTAGE_L2': {'unit': 'V', 'value': 230.0},
'INSTANTANEOUS_VOLTAGE_L3': {'unit': 'V', 'value': 229.0},
'LONG_POWER_FAILURE_COUNT': {'unit': None, 'value': 0},
'MBUS_DEVICES': [{'CHANNEL_ID': 1,
'MBUS_DEVICE_TYPE': {'unit': None, 'value': 3},
'MBUS_EQUIPMENT_IDENTIFIER': {'unit': None,
'value': '3232323241424344313233343536373839'},
'MBUS_METER_READING': {'datetime': '2017-01-02T15:10:05+00:00',
'unit': 'm3',
'value': 0.107}},
{'CHANNEL_ID': 2,
'MBUS_DEVICE_TYPE': {'unit': None, 'value': 3},
'MBUS_EQUIPMENT_IDENTIFIER': {'unit': None,
'value': None}}],
'P1_MESSAGE_HEADER': {'unit': None, 'value': '50'},
'P1_MESSAGE_TIMESTAMP': {'unit': None, 'value': '2017-01-02T18:20:02+00:00'},
'POWER_EVENT_FAILURE_LOG': {'buffer': [],
'buffer_length': 0,
'buffer_type': '0-0:96.7.19'},
'SHORT_POWER_FAILURE_COUNT': {'unit': None, 'value': 13},
'TEXT_MESSAGE': {'unit': None, 'value': None},
'VOLTAGE_SAG_L1_COUNT': {'unit': None, 'value': 0},
'VOLTAGE_SAG_L2_COUNT': {'unit': None, 'value': 0},
'VOLTAGE_SAG_L3_COUNT': {'unit': None, 'value': 0},
'VOLTAGE_SWELL_L1_COUNT': {'unit': None, 'value': 0},
'VOLTAGE_SWELL_L2_COUNT': {'unit': None, 'value': 0},
'VOLTAGE_SWELL_L3_COUNT': {'unit': None, 'value': 0}}
)
def test_to_str(self):
parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(TELEGRAM_V5)
self.maxDiff = None
self.assertEqual(
str(telegram),
(
'P1_MESSAGE_HEADER: 50 [None]\n'
'P1_MESSAGE_TIMESTAMP: 2017-01-02T18:20:02+00:00 [None]\n'
'EQUIPMENT_IDENTIFIER: 4B384547303034303436333935353037 [None]\n'
'ELECTRICITY_USED_TARIFF_1: 4.426 [kWh]\n'
'ELECTRICITY_USED_TARIFF_2: 2.399 [kWh]\n'
'ELECTRICITY_DELIVERED_TARIFF_1: 2.444 [kWh]\n'
'ELECTRICITY_DELIVERED_TARIFF_2: 0.000 [kWh]\n'
'ELECTRICITY_ACTIVE_TARIFF: 0002 [None]\n'
'CURRENT_ELECTRICITY_USAGE: 0.244 [kW]\n'
'CURRENT_ELECTRICITY_DELIVERY: 0.000 [kW]\n'
'LONG_POWER_FAILURE_COUNT: 0 [None]\n'
'SHORT_POWER_FAILURE_COUNT: 13 [None]\n'
'POWER_EVENT_FAILURE_LOG: buffer length: 0\n'
' buffer type: 0-0:96.7.19\n'
'VOLTAGE_SAG_L1_COUNT: 0 [None]\n'
'VOLTAGE_SAG_L2_COUNT: 0 [None]\n'
'VOLTAGE_SAG_L3_COUNT: 0 [None]\n'
'VOLTAGE_SWELL_L1_COUNT: 0 [None]\n'
'VOLTAGE_SWELL_L2_COUNT: 0 [None]\n'
'VOLTAGE_SWELL_L3_COUNT: 0 [None]\n'
'INSTANTANEOUS_VOLTAGE_L1: 230.0 [V]\n'
'INSTANTANEOUS_VOLTAGE_L2: 230.0 [V]\n'
'INSTANTANEOUS_VOLTAGE_L3: 229.0 [V]\n'
'INSTANTANEOUS_CURRENT_L1: 0.48 [A]\n'
'INSTANTANEOUS_CURRENT_L2: 0.44 [A]\n'
'INSTANTANEOUS_CURRENT_L3: 0.86 [A]\n'
'TEXT_MESSAGE: None [None]\n'
'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 0.070 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 0.032 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 0.142 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 0.000 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 0.000 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 0.000 [kW]\n'
'MBUS DEVICE (channel 1)\n'
' MBUS_DEVICE_TYPE: 3 [None]\n'
' MBUS_EQUIPMENT_IDENTIFIER: 3232323241424344313233343536373839 [None]\n'
' MBUS_METER_READING: 0.107 [m3] at 2017-01-02T15:10:05+00:00\n'
'MBUS DEVICE (channel 2)\n'
' MBUS_DEVICE_TYPE: 3 [None]\n'
' MBUS_EQUIPMENT_IDENTIFIER: None [None]\n'
)
)
def test_getitem(self):
parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(TELEGRAM_V5)
self.assertEqual(telegram[obis_references.P1_MESSAGE_HEADER].value, '50')
+21
View File
@@ -0,0 +1,21 @@
import unittest
import tempfile
from dsmr_parser.clients.filereader import FileReader
from dsmr_parser.telegram_specifications import V5
from test.example_telegrams import TELEGRAM_V5
class FileReaderTest(unittest.TestCase):
def test_read_as_object(self):
with tempfile.NamedTemporaryFile() as file:
with open(file.name, "w") as f:
f.write(TELEGRAM_V5)
telegrams = []
reader = FileReader(file=file.name, telegram_specification=V5)
# Call
for telegram in reader.read_as_object():
telegrams.append(telegram)
self.assertEqual(len(telegrams), 1)
+413
View File
@@ -0,0 +1,413 @@
from decimal import Decimal
import datetime
import json
import unittest
import pytz
from dsmr_parser import telegram_specifications
from dsmr_parser.exceptions import InvalidChecksumError, ParseError
from dsmr_parser.objects import CosemObject, MBusObject, MBusObjectPeak
from dsmr_parser.parsers import TelegramParser
from test.example_telegrams import TELEGRAM_FLUVIUS_V171, TELEGRAM_FLUVIUS_V171_ALT
class TelegramParserFluviusTest(unittest.TestCase):
""" Test parsing of a DSMR Fluvius telegram. """
def test_parse(self):
parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS)
try:
result = parser.parse(TELEGRAM_FLUVIUS_V171, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# BELGIUM_VERSION_INFORMATION (0-0:96.1.4)
assert isinstance(result.BELGIUM_VERSION_INFORMATION, CosemObject)
assert result.BELGIUM_VERSION_INFORMATION.unit is None
assert isinstance(result.BELGIUM_VERSION_INFORMATION.value, str)
assert result.BELGIUM_VERSION_INFORMATION.value == '50217'
# EQUIPMENT_IDENTIFIER (0-0:96.1.1)
assert isinstance(result.BELGIUM_EQUIPMENT_IDENTIFIER, CosemObject)
assert result.BELGIUM_EQUIPMENT_IDENTIFIER.unit is None
assert isinstance(result.BELGIUM_EQUIPMENT_IDENTIFIER.value, str)
assert result.BELGIUM_EQUIPMENT_IDENTIFIER.value == '3153414733313031303231363035'
# P1_MESSAGE_TIMESTAMP (0-0:1.0.0)
assert isinstance(result.P1_MESSAGE_TIMESTAMP, CosemObject)
assert result.P1_MESSAGE_TIMESTAMP.unit is None
assert isinstance(result.P1_MESSAGE_TIMESTAMP.value, datetime.datetime)
assert result.P1_MESSAGE_TIMESTAMP.value == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 5, 12, 13, 54, 9))
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(result.ELECTRICITY_USED_TARIFF_1, CosemObject)
assert result.ELECTRICITY_USED_TARIFF_1.unit == 'kWh'
assert isinstance(result.ELECTRICITY_USED_TARIFF_1.value, Decimal)
assert result.ELECTRICITY_USED_TARIFF_1.value == Decimal('0.034')
# ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2)
assert isinstance(result.ELECTRICITY_USED_TARIFF_2, CosemObject)
assert result.ELECTRICITY_USED_TARIFF_2.unit == 'kWh'
assert isinstance(result.ELECTRICITY_USED_TARIFF_2.value, Decimal)
assert result.ELECTRICITY_USED_TARIFF_2.value == Decimal('15.758')
# ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1)
assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_1, CosemObject)
assert result.ELECTRICITY_DELIVERED_TARIFF_1.unit == 'kWh'
assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_1.value, Decimal)
assert result.ELECTRICITY_DELIVERED_TARIFF_1.value == Decimal('0.000')
# ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2)
assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_2, CosemObject)
assert result.ELECTRICITY_DELIVERED_TARIFF_2.unit == 'kWh'
assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_2.value, Decimal)
assert result.ELECTRICITY_DELIVERED_TARIFF_2.value == Decimal('0.011')
# ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0)
assert isinstance(result.ELECTRICITY_ACTIVE_TARIFF, CosemObject)
assert result.ELECTRICITY_ACTIVE_TARIFF.unit is None
assert isinstance(result.ELECTRICITY_ACTIVE_TARIFF.value, str)
assert result.ELECTRICITY_ACTIVE_TARIFF.value == '0001'
# BELGIUM_CURRENT_AVERAGE_DEMAND (1-0:1.4.0)
assert isinstance(result.BELGIUM_CURRENT_AVERAGE_DEMAND, CosemObject)
assert result.BELGIUM_CURRENT_AVERAGE_DEMAND.unit == 'kW'
assert isinstance(result.BELGIUM_CURRENT_AVERAGE_DEMAND.value, Decimal)
assert result.BELGIUM_CURRENT_AVERAGE_DEMAND.value == Decimal('2.351')
# BELGIUM_MAXIMUM_DEMAND_MONTH (1-0:1.6.0)
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_MONTH, MBusObject)
assert result.BELGIUM_MAXIMUM_DEMAND_MONTH.unit == 'kW'
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_MONTH.value, Decimal)
assert result.BELGIUM_MAXIMUM_DEMAND_MONTH.value == Decimal('2.589')
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_MONTH.datetime, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_MONTH.datetime == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 5, 9, 13, 45, 58))
# BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 0
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0], MBusObjectPeak)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].unit == 'kW'
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].value, Decimal)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].value == Decimal('3.695')
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].datetime, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].datetime == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 5, 1, 0, 0, 0))
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].occurred, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].occurred == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 4, 23, 19, 25, 38))
# BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 1
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1], MBusObjectPeak)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].unit == 'kW'
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].value, Decimal)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].value == Decimal('5.980')
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].datetime, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].datetime == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 4, 1, 0, 0, 0))
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].occurred, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].occurred == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 3, 5, 12, 21, 39))
# BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 2
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2], MBusObjectPeak)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].unit == 'kW'
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].value, Decimal)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].value == Decimal('4.318')
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].datetime, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].datetime == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 3, 1, 0, 0, 0))
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].occurred, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].occurred == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 2, 10, 3, 54, 21))
# CURRENT_ELECTRICITY_USAGE (1-0:1.7.0)
assert isinstance(result.CURRENT_ELECTRICITY_USAGE, CosemObject)
assert result.CURRENT_ELECTRICITY_USAGE.unit == 'kW'
assert isinstance(result.CURRENT_ELECTRICITY_USAGE.value, Decimal)
assert result.CURRENT_ELECTRICITY_USAGE.value == Decimal('0.000')
# CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0)
assert isinstance(result.CURRENT_ELECTRICITY_DELIVERY, CosemObject)
assert result.CURRENT_ELECTRICITY_DELIVERY.unit == 'kW'
assert isinstance(result.CURRENT_ELECTRICITY_DELIVERY.value, Decimal)
assert result.CURRENT_ELECTRICITY_DELIVERY.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0)
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE, CosemObject)
assert result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.unit == 'kW'
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.value, Decimal)
assert result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0)
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE, CosemObject)
assert result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.unit == 'kW'
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.value, Decimal)
assert result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0)
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE, CosemObject)
assert result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.unit == 'kW'
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.value, Decimal)
assert result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0)
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE, CosemObject)
assert result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.unit == 'kW'
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.value, Decimal)
assert result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0)
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE, CosemObject)
assert result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.unit == 'kW'
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.value, Decimal)
assert result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0)
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE, CosemObject)
assert result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.unit == 'kW'
assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.value, Decimal)
assert result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.value == Decimal('0.000')
# INSTANTANEOUS_VOLTAGE_L1 (1-0:32.7.0)
assert isinstance(result.INSTANTANEOUS_VOLTAGE_L1, CosemObject)
assert result.INSTANTANEOUS_VOLTAGE_L1.unit == 'V'
assert isinstance(result.INSTANTANEOUS_VOLTAGE_L1.value, Decimal)
assert result.INSTANTANEOUS_VOLTAGE_L1.value == Decimal('234.7')
# INSTANTANEOUS_VOLTAGE_L2 (1-0:52.7.0)
assert isinstance(result.INSTANTANEOUS_VOLTAGE_L2, CosemObject)
assert result.INSTANTANEOUS_VOLTAGE_L2.unit == 'V'
assert isinstance(result.INSTANTANEOUS_VOLTAGE_L2.value, Decimal)
assert result.INSTANTANEOUS_VOLTAGE_L2.value == Decimal('234.7')
# INSTANTANEOUS_VOLTAGE_L3 (1-0:72.7.0)
assert isinstance(result.INSTANTANEOUS_VOLTAGE_L3, CosemObject)
assert result.INSTANTANEOUS_VOLTAGE_L3.unit == 'V'
assert isinstance(result.INSTANTANEOUS_VOLTAGE_L3.value, Decimal)
assert result.INSTANTANEOUS_VOLTAGE_L3.value == Decimal('234.7')
# INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0)
assert isinstance(result.INSTANTANEOUS_CURRENT_L1, CosemObject)
assert result.INSTANTANEOUS_CURRENT_L1.unit == 'A'
assert isinstance(result.INSTANTANEOUS_CURRENT_L1.value, Decimal)
assert result.INSTANTANEOUS_CURRENT_L1.value == Decimal('0.000')
# INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0)
assert isinstance(result.INSTANTANEOUS_CURRENT_L2, CosemObject)
assert result.INSTANTANEOUS_CURRENT_L2.unit == 'A'
assert isinstance(result.INSTANTANEOUS_CURRENT_L2.value, Decimal)
assert result.INSTANTANEOUS_CURRENT_L2.value == Decimal('0.000')
# INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0)
assert isinstance(result.INSTANTANEOUS_CURRENT_L3, CosemObject)
assert result.INSTANTANEOUS_CURRENT_L3.unit == 'A'
assert isinstance(result.INSTANTANEOUS_CURRENT_L3.value, Decimal)
assert result.INSTANTANEOUS_CURRENT_L3.value == Decimal('0.000')
# ACTUAL_SWITCH_POSITION (0-0:96.3.10)
assert isinstance(result.ACTUAL_SWITCH_POSITION, CosemObject)
assert result.ACTUAL_SWITCH_POSITION.unit is None
assert isinstance(result.ACTUAL_SWITCH_POSITION.value, int)
assert result.ACTUAL_SWITCH_POSITION.value == 1
# ACTUAL_TRESHOLD_ELECTRICITY (0-0:17.0.0)
assert isinstance(result.ACTUAL_TRESHOLD_ELECTRICITY, CosemObject)
assert result.ACTUAL_TRESHOLD_ELECTRICITY.unit == 'kW'
assert isinstance(result.ACTUAL_TRESHOLD_ELECTRICITY.value, Decimal)
assert result.ACTUAL_TRESHOLD_ELECTRICITY.value == Decimal('999.9')
# FUSE_THRESHOLD_L1 (1-0:31.4.0)
assert isinstance(result.FUSE_THRESHOLD_L1, CosemObject)
assert result.FUSE_THRESHOLD_L1.unit == 'A'
assert isinstance(result.FUSE_THRESHOLD_L1.value, Decimal)
assert result.FUSE_THRESHOLD_L1.value == Decimal('999')
# TEXT_MESSAGE (0-0:96.13.0)
assert isinstance(result.TEXT_MESSAGE, CosemObject)
assert result.TEXT_MESSAGE.unit is None
assert result.TEXT_MESSAGE.value is None
# MBUS DEVICE 1
mbus1 = result.get_mbus_device_by_channel(1)
# MBUS_DEVICE_TYPE (0-1:24.1.0)
assert isinstance(mbus1.MBUS_DEVICE_TYPE, CosemObject)
assert mbus1.MBUS_DEVICE_TYPE.unit is None
assert isinstance(mbus1.MBUS_DEVICE_TYPE.value, int)
assert mbus1.MBUS_DEVICE_TYPE.value == 3
# MBUS_EQUIPMENT_IDENTIFIER (0-1:96.1.1)
assert isinstance(mbus1.MBUS_EQUIPMENT_IDENTIFIER, CosemObject)
assert mbus1.MBUS_EQUIPMENT_IDENTIFIER.unit is None
assert isinstance(mbus1.MBUS_EQUIPMENT_IDENTIFIER.value, str)
assert mbus1.MBUS_EQUIPMENT_IDENTIFIER.value == '37464C4F32313139303333373333'
# MBUS_VALVE_POSITION (0-1:24.4.0)
assert isinstance(result.MBUS_VALVE_POSITION, CosemObject)
assert result.MBUS_VALVE_POSITION.unit is None
assert isinstance(result.MBUS_VALVE_POSITION.value, int)
assert result.MBUS_VALVE_POSITION.value == 1
# MBUS_METER_READING (0-1:24.2.3)
assert isinstance(mbus1.MBUS_METER_READING, MBusObject)
assert mbus1.MBUS_METER_READING.unit == 'm3'
assert isinstance(mbus1.MBUS_METER_READING.value, Decimal)
assert mbus1.MBUS_METER_READING.value == Decimal('112.384')
# MBUS DEVICE 2
mbus2 = result.get_mbus_device_by_channel(2)
# MBUS_DEVICE_TYPE (0-2:24.1.0)
assert isinstance(mbus2.MBUS_DEVICE_TYPE, CosemObject)
assert mbus2.MBUS_DEVICE_TYPE.unit is None
assert isinstance(mbus2.MBUS_DEVICE_TYPE.value, int)
assert mbus2.MBUS_DEVICE_TYPE.value == 7
# MBUS_EQUIPMENT_IDENTIFIER (0-2:96.1.1)
assert isinstance(mbus2.MBUS_EQUIPMENT_IDENTIFIER, CosemObject)
assert mbus2.MBUS_EQUIPMENT_IDENTIFIER.unit is None
assert isinstance(mbus2.MBUS_EQUIPMENT_IDENTIFIER.value, str)
assert mbus2.MBUS_EQUIPMENT_IDENTIFIER.value == '3853414731323334353637383930'
# MBUS_METER_READING (0-1:24.2.1)
assert isinstance(mbus2.MBUS_METER_READING, MBusObject)
assert mbus2.MBUS_METER_READING.unit == 'm3'
assert isinstance(mbus2.MBUS_METER_READING.value, Decimal)
assert mbus2.MBUS_METER_READING.value == Decimal('872.234')
def test_checksum_valid(self):
# No exception is raised.
TelegramParser.validate_checksum(TELEGRAM_FLUVIUS_V171)
def test_checksum_invalid(self):
# Remove the electricty used data value. This causes the checksum to
# not match anymore.
corrupted_telegram = TELEGRAM_FLUVIUS_V171.replace(
'1-0:1.8.1(000000.034*kWh)\r\n',
''
)
with self.assertRaises(InvalidChecksumError):
TelegramParser.validate_checksum(corrupted_telegram)
def test_checksum_missing(self):
# Remove the checksum value causing a ParseError.
corrupted_telegram = TELEGRAM_FLUVIUS_V171.replace('!3AD7\r\n', '')
with self.assertRaises(ParseError):
TelegramParser.validate_checksum(corrupted_telegram)
def test_to_json(self):
parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS)
telegram = parser.parse(TELEGRAM_FLUVIUS_V171_ALT)
json_data = json.loads(telegram.to_json())
self.maxDiff = None
self.assertEqual(
json_data,
{'BELGIUM_VERSION_INFORMATION': {'value': '50217', 'unit': None},
'BELGIUM_EQUIPMENT_IDENTIFIER': {'value': '3153414733313030373231333236', 'unit': None},
'P1_MESSAGE_TIMESTAMP': {'value': '2023-11-02T11:15:48+00:00', 'unit': None},
'ELECTRICITY_USED_TARIFF_1': {'value': 301.548, 'unit': 'kWh'},
'ELECTRICITY_USED_TARIFF_2': {'value': 270.014, 'unit': 'kWh'},
'ELECTRICITY_DELIVERED_TARIFF_1': {'value': 0.005, 'unit': 'kWh'},
'ELECTRICITY_DELIVERED_TARIFF_2': {'value': 0.0, 'unit': 'kWh'},
'ELECTRICITY_ACTIVE_TARIFF': {'value': '0001', 'unit': None},
'BELGIUM_CURRENT_AVERAGE_DEMAND': {'value': 0.052, 'unit': 'kW'},
'BELGIUM_MAXIMUM_DEMAND_MONTH': {'datetime': '2023-11-02T10:45:00+00:00',
'value': 3.064, 'unit': 'kW'},
'BELGIUM_MAXIMUM_DEMAND_13_MONTHS': [{'datetime': '2023-07-31T22:00:00+00:00',
'occurred': None, 'value': 0.0, 'unit': 'kW'},
{'datetime': '2023-08-31T22:00:00+00:00',
'occurred': '2023-08-31T16:15:00+00:00',
'value': 1.862, 'unit': 'kW'},
{'datetime': '2023-09-30T22:00:00+00:00',
'occurred': '2023-09-10T16:30:00+00:00',
'value': 4.229, 'unit': 'kW'},
{'datetime': '2023-10-31T23:00:00+00:00',
'occurred': '2023-10-16T11:00:00+00:00',
'value': 4.927, 'unit': 'kW'}],
'CURRENT_ELECTRICITY_USAGE': {'value': 0.338, 'unit': 'kW'},
'CURRENT_ELECTRICITY_DELIVERY': {'value': 0.0, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE': {'value': 0.047, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE': {'value': 0.179, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE': {'value': 0.111, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE': {'value': 0.0, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE': {'value': 0.0, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE': {'value': 0.0, 'unit': 'kW'},
'INSTANTANEOUS_VOLTAGE_L1': {'value': 232.9, 'unit': 'V'},
'INSTANTANEOUS_VOLTAGE_L2': {'value': 228.1, 'unit': 'V'},
'INSTANTANEOUS_VOLTAGE_L3': {'value': 228.1, 'unit': 'V'},
'INSTANTANEOUS_CURRENT_L1': {'value': 0.27, 'unit': 'A'},
'INSTANTANEOUS_CURRENT_L2': {'value': 0.88, 'unit': 'A'},
'INSTANTANEOUS_CURRENT_L3': {'value': 0.52, 'unit': 'A'},
'ACTUAL_SWITCH_POSITION': {'value': 1, 'unit': None},
'ACTUAL_TRESHOLD_ELECTRICITY': {'value': 999.9, 'unit': 'kW'},
'FUSE_THRESHOLD_L1': {'value': 999.0, 'unit': 'A'},
'TEXT_MESSAGE': {'value': None, 'unit': None},
'MBUS_DEVICES': [{'MBUS_DEVICE_TYPE': {'value': 3, 'unit': None},
'MBUS_EQUIPMENT_IDENTIFIER': {'value': '37464C4F32313233303838303237',
'unit': None},
'MBUS_VALVE_POSITION': {'value': 1, 'unit': None},
'MBUS_METER_READING': {'datetime': '2023-11-02T11:10:02+00:00',
'value': 92.287, 'unit': 'm3'},
'CHANNEL_ID': 1},
{'MBUS_DEVICE_TYPE': {'value': 7, 'unit': None},
'MBUS_EQUIPMENT_IDENTIFIER': {'value': '3853455430303030393631313733',
'unit': None},
'MBUS_METER_READING': {'datetime': '2023-11-02T11:15:32+00:00',
'value': 8.579, 'unit': 'm3'},
'CHANNEL_ID': 2}]}
)
def test_to_str(self):
parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS)
telegram = parser.parse(TELEGRAM_FLUVIUS_V171_ALT)
self.assertEqual(
str(telegram),
(
'BELGIUM_VERSION_INFORMATION: 50217 [None]\n'
'BELGIUM_EQUIPMENT_IDENTIFIER: 3153414733313030373231333236 [None]\n'
'P1_MESSAGE_TIMESTAMP: 2023-11-02T11:15:48+00:00 [None]\n'
'ELECTRICITY_USED_TARIFF_1: 301.548 [kWh]\n'
'ELECTRICITY_USED_TARIFF_2: 270.014 [kWh]\n'
'ELECTRICITY_DELIVERED_TARIFF_1: 0.005 [kWh]\n'
'ELECTRICITY_DELIVERED_TARIFF_2: 0.000 [kWh]\n'
'ELECTRICITY_ACTIVE_TARIFF: 0001 [None]\n'
'BELGIUM_CURRENT_AVERAGE_DEMAND: 0.052 [kW]\n'
'BELGIUM_MAXIMUM_DEMAND_MONTH: 3.064 [kW] at 2023-11-02T10:45:00+00:00\n'
'0.0 [kW] at 2023-07-31T22:00:00+00:00 occurred None'
'1.862 [kW] at 2023-08-31T22:00:00+00:00 occurred 2023-08-31T16:15:00+00:00'
'4.229 [kW] at 2023-09-30T22:00:00+00:00 occurred 2023-09-10T16:30:00+00:00'
'4.927 [kW] at 2023-10-31T23:00:00+00:00 occurred 2023-10-16T11:00:00+00:00'
'CURRENT_ELECTRICITY_USAGE: 0.338 [kW]\n'
'CURRENT_ELECTRICITY_DELIVERY: 0.000 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 0.047 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 0.179 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 0.111 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 0.000 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 0.000 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 0.000 [kW]\n'
'INSTANTANEOUS_VOLTAGE_L1: 232.9 [V]\n'
'INSTANTANEOUS_VOLTAGE_L2: 228.1 [V]\n'
'INSTANTANEOUS_VOLTAGE_L3: 228.1 [V]\n'
'INSTANTANEOUS_CURRENT_L1: 0.27 [A]\n'
'INSTANTANEOUS_CURRENT_L2: 0.88 [A]\n'
'INSTANTANEOUS_CURRENT_L3: 0.52 [A]\n'
'ACTUAL_SWITCH_POSITION: 1 [None]\n'
'ACTUAL_TRESHOLD_ELECTRICITY: 999.9 [kW]\n'
'FUSE_THRESHOLD_L1: 999 [A]\n'
'TEXT_MESSAGE: None [None]\n'
'MBUS DEVICE (channel 1)\n'
' MBUS_DEVICE_TYPE: 3 [None]\n'
' MBUS_EQUIPMENT_IDENTIFIER: 37464C4F32313233303838303237 [None]\n'
' MBUS_VALVE_POSITION: 1 [None]\n'
' MBUS_METER_READING: 92.287 [m3] at 2023-11-02T11:10:02+00:00\n'
'MBUS DEVICE (channel 2)\n'
' MBUS_DEVICE_TYPE: 7 [None]\n'
' MBUS_EQUIPMENT_IDENTIFIER: 3853455430303030393631313733 [None]\n'
' MBUS_METER_READING: 8.579 [m3] at 2023-11-02T11:15:32+00:00\n'
)
)
+177
View File
@@ -0,0 +1,177 @@
import unittest
from decimal import Decimal
from dsmr_parser.exceptions import InvalidChecksumError, ParseError
from dsmr_parser.objects import CosemObject
from dsmr_parser.parsers import TelegramParser
from dsmr_parser import telegram_specifications
from dsmr_parser import obis_references as obis
from test.example_telegrams import TELEGRAM_ISKRA_IE
class TelegramParserIskraIETest(unittest.TestCase):
""" Test parsing of a Iskra IE5 telegram. """
def test_parse(self):
parser = TelegramParser(telegram_specifications.ISKRA_IE)
try:
result = parser.parse(TELEGRAM_ISKRA_IE, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# EQUIPMENT_IDENTIFIER_GAS (0-0:96.1.0)
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS], CosemObject)
assert result[obis.EQUIPMENT_IDENTIFIER_GAS].unit is None
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS].value, str)
assert result[obis.EQUIPMENT_IDENTIFIER_GAS].value == '09610'
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject)
assert result[obis.ELECTRICITY_USED_TARIFF_1].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1].value, Decimal)
assert result[obis.ELECTRICITY_USED_TARIFF_1].value == Decimal('10.181')
# ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2], CosemObject)
assert result[obis.ELECTRICITY_USED_TARIFF_2].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2].value, Decimal)
assert result[obis.ELECTRICITY_USED_TARIFF_2].value == Decimal('10.182')
# ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1)
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1], CosemObject)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value, Decimal)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value == Decimal('10.281')
# ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2)
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2], CosemObject)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value, Decimal)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value == Decimal('10.282')
# ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0)
assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF], CosemObject)
assert result[obis.ELECTRICITY_ACTIVE_TARIFF].unit is None
assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF].value, str)
assert result[obis.ELECTRICITY_ACTIVE_TARIFF].value == '0001'
# CURRENT_ELECTRICITY_USAGE (1-0:1.7.0)
assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE], CosemObject)
assert result[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW'
assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE].value, Decimal)
assert result[obis.CURRENT_ELECTRICITY_USAGE].value == Decimal('0.170')
# CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0)
assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY], CosemObject)
assert result[obis.CURRENT_ELECTRICITY_DELIVERY].unit == 'kW'
assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY].value, Decimal)
assert result[obis.CURRENT_ELECTRICITY_DELIVERY].value == Decimal('0.270')
# INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value == Decimal('0.217')
# INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value == Decimal('0.417')
# INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value == Decimal('0.617')
# INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value == Decimal('0.227')
# INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value == Decimal('0.427')
# INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value == Decimal('0.627')
# INSTANTANEOUS_VOLTAGE_L1 (1-0:32.7.0)
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L1], CosemObject)
assert result[obis.INSTANTANEOUS_VOLTAGE_L1].unit == 'V'
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L1].value, Decimal)
assert result[obis.INSTANTANEOUS_VOLTAGE_L1].value == Decimal('242.5')
# INSTANTANEOUS_VOLTAGE_L2 (1-0:52.7.0)
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L2], CosemObject)
assert result[obis.INSTANTANEOUS_VOLTAGE_L2].unit == 'V'
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L2].value, Decimal)
assert result[obis.INSTANTANEOUS_VOLTAGE_L2].value == Decimal('241.7')
# INSTANTANEOUS_VOLTAGE_L3 (1-0:72.7.0)
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L3], CosemObject)
assert result[obis.INSTANTANEOUS_VOLTAGE_L3].unit == 'V'
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L3].value, Decimal)
assert result[obis.INSTANTANEOUS_VOLTAGE_L3].value == Decimal('243.3')
# INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0)
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L1], CosemObject)
assert result[obis.INSTANTANEOUS_CURRENT_L1].unit == 'A'
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L1].value, Decimal)
assert result[obis.INSTANTANEOUS_CURRENT_L1].value == Decimal('0.000')
# INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0)
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L2], CosemObject)
assert result[obis.INSTANTANEOUS_CURRENT_L2].unit == 'A'
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L2].value, Decimal)
assert result[obis.INSTANTANEOUS_CURRENT_L2].value == Decimal('0.000')
# INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0)
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L3], CosemObject)
assert result[obis.INSTANTANEOUS_CURRENT_L3].unit == 'A'
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L3].value, Decimal)
assert result[obis.INSTANTANEOUS_CURRENT_L3].value == Decimal('0.000')
# ACTUAL_SWITCH_POSITION (0-0:96.3.10)
assert isinstance(result[obis.ACTUAL_SWITCH_POSITION], CosemObject)
assert result[obis.ACTUAL_SWITCH_POSITION].unit is None
assert isinstance(result[obis.ACTUAL_SWITCH_POSITION].value, str)
assert result[obis.ACTUAL_SWITCH_POSITION].value == '1'
# TEXT_MESSAGE (0-0:96.13.0)
assert isinstance(result[obis.TEXT_MESSAGE], CosemObject)
assert result[obis.TEXT_MESSAGE].unit is None
assert result[obis.TEXT_MESSAGE].value is None
# EQUIPMENT_IDENTIFIER (0-0:96.1.1)
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER], CosemObject)
assert result[obis.EQUIPMENT_IDENTIFIER].unit is None
assert result[obis.EQUIPMENT_IDENTIFIER].value is None
def test_checksum_valid(self):
# No exception is raised.
TelegramParser.validate_checksum(TELEGRAM_ISKRA_IE)
def test_checksum_invalid(self):
# Remove the electricty used data value. This causes the checksum to not match anymore.
corrupted_telegram = TELEGRAM_ISKRA_IE.replace(
'1-0:1.8.1(000010.181*kWh)\r\n',
''
)
with self.assertRaises(InvalidChecksumError):
TelegramParser.validate_checksum(corrupted_telegram)
def test_checksum_missing(self):
# Remove the checksum value causing a ParseError.
corrupted_telegram = TELEGRAM_ISKRA_IE.replace('!AD3B\r\n', '')
with self.assertRaises(ParseError):
TelegramParser.validate_checksum(corrupted_telegram)
+107
View File
@@ -0,0 +1,107 @@
from binascii import unhexlify
from copy import deepcopy
import unittest
from dlms_cosem.exceptions import DecryptionError
from dlms_cosem.protocol.xdlms import GeneralGlobalCipher
from dlms_cosem.security import SecurityControlField, encrypt
from dsmr_parser import telegram_specifications
from dsmr_parser.exceptions import ParseError
from dsmr_parser.parsers import TelegramParser
from test.example_telegrams import TELEGRAM_SAGEMCOM_T210_D_R
class TelegramParserEncryptedTest(unittest.TestCase):
""" Test parsing of a DSML encypted DSMR v5.x telegram. """
DUMMY_ENCRYPTION_KEY = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
DUMMY_AUTHENTICATION_KEY = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
def __generate_encrypted(self, security_suite=0, authenticated=True, encrypted=True):
security_control = SecurityControlField(
security_suite=security_suite, authenticated=authenticated, encrypted=encrypted
)
encryption_key = unhexlify(self.DUMMY_ENCRYPTION_KEY)
authentication_key = unhexlify(self.DUMMY_AUTHENTICATION_KEY)
system_title = "SYSTEMID".encode("ascii")
invocation_counter = int.from_bytes(bytes.fromhex("10000001"), "big")
plain_data = TELEGRAM_SAGEMCOM_T210_D_R.encode("ascii")
encrypted = encrypt(
security_control=security_control,
key=encryption_key,
auth_key=authentication_key,
system_title=system_title,
invocation_counter=invocation_counter,
plain_text=plain_data,
)
full_frame = bytearray(GeneralGlobalCipher.TAG.to_bytes(1, "big", signed=False))
full_frame.extend(len(system_title).to_bytes(1, "big", signed=False))
full_frame.extend(system_title)
full_frame.extend([0x82]) # Length of the following length bytes
# https://github.com/pwitab/dlms-cosem/blob/739f81a58e5f07663a512d4a128851333a0ed5e6/dlms_cosem/a_xdr.py#L33
security_control = security_control.to_bytes()
invocation_counter = invocation_counter.to_bytes(4, "big", signed=False)
full_frame.extend((len(encrypted)
+ len(invocation_counter)
+ len(security_control)).to_bytes(2, "big", signed=False))
full_frame.extend(security_control)
full_frame.extend(invocation_counter)
full_frame.extend(encrypted)
return full_frame
def test_parse(self):
parser = TelegramParser(telegram_specifications.SAGEMCOM_T210_D_R)
result = parser.parse(self.__generate_encrypted().hex(),
self.DUMMY_ENCRYPTION_KEY,
self.DUMMY_AUTHENTICATION_KEY)
self.assertEqual(len(result), 18)
def test_damaged_frame(self):
# If the frame is damaged decrypting fails (crc is technically not needed)
parser = TelegramParser(telegram_specifications.SAGEMCOM_T210_D_R)
generated = self.__generate_encrypted()
generated[150] = 0x00
generated = generated.hex()
with self.assertRaises(DecryptionError):
parser.parse(generated, self.DUMMY_ENCRYPTION_KEY, self.DUMMY_AUTHENTICATION_KEY)
def test_plain(self):
# If a plain request is parsed with "general_global_cipher": True it fails
parser = TelegramParser(telegram_specifications.SAGEMCOM_T210_D_R)
with self.assertRaises(Exception):
parser.parse(TELEGRAM_SAGEMCOM_T210_D_R, self.DUMMY_ENCRYPTION_KEY, self.DUMMY_AUTHENTICATION_KEY)
def test_general_global_cipher_not_specified(self):
# If a GGC frame is detected but general_global_cipher is not set it fails
parser = TelegramParser(telegram_specifications.SAGEMCOM_T210_D_R)
parser = deepcopy(parser) # We do not want to change the module value
parser.telegram_specification['general_global_cipher'] = False
with self.assertRaises(ParseError):
parser.parse(self.__generate_encrypted().hex(), self.DUMMY_ENCRYPTION_KEY, self.DUMMY_AUTHENTICATION_KEY)
def test_only_encrypted(self):
# Not implemented by dlms_cosem
parser = TelegramParser(telegram_specifications.SAGEMCOM_T210_D_R)
only_auth = self.__generate_encrypted(0, authenticated=False, encrypted=True).hex()
with self.assertRaises(ValueError):
parser.parse(only_auth, self.DUMMY_ENCRYPTION_KEY)
def test_only_auth(self):
# Not implemented by dlms_cosem
parser = TelegramParser(telegram_specifications.SAGEMCOM_T210_D_R)
only_auth = self.__generate_encrypted(0, authenticated=True, encrypted=False).hex()
with self.assertRaises(ValueError):
parser.parse(only_auth, authentication_key=self.DUMMY_AUTHENTICATION_KEY)
+4 -1
View File
@@ -23,7 +23,10 @@ class TelegramParserV2_2Test(unittest.TestCase):
def test_parse(self):
parser = TelegramParser(telegram_specifications.V2_2)
result = parser.parse(TELEGRAM_V2_2)
try:
result = parser.parse(TELEGRAM_V2_2, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject)
+4 -1
View File
@@ -23,7 +23,10 @@ class TelegramParserV3Test(unittest.TestCase):
def test_parse(self):
parser = TelegramParser(telegram_specifications.V3)
result = parser.parse(TELEGRAM_V3)
try:
result = parser.parse(TELEGRAM_V3, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject)
+29 -2
View File
@@ -26,7 +26,10 @@ class TelegramParserV4_2Test(unittest.TestCase):
def test_parse(self):
parser = TelegramParser(telegram_specifications.V4)
result = parser.parse(TELEGRAM_V4_2)
try:
result = parser.parse(TELEGRAM_V4_2, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# P1_MESSAGE_HEADER (1-3:0.2.8)
assert isinstance(result[obis.P1_MESSAGE_HEADER], CosemObject)
@@ -89,6 +92,12 @@ class TelegramParserV4_2Test(unittest.TestCase):
assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY].value, Decimal)
assert result[obis.CURRENT_ELECTRICITY_DELIVERY].value == Decimal('0')
# SHORT_POWER_FAILURE_COUNT (1-0:96.7.21)
assert isinstance(result[obis.SHORT_POWER_FAILURE_COUNT], CosemObject)
assert result[obis.SHORT_POWER_FAILURE_COUNT].unit is None
assert isinstance(result[obis.SHORT_POWER_FAILURE_COUNT].value, int)
assert result[obis.SHORT_POWER_FAILURE_COUNT].value == 15
# LONG_POWER_FAILURE_COUNT (96.7.9)
assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT], CosemObject)
assert result[obis.LONG_POWER_FAILURE_COUNT].unit is None
@@ -141,8 +150,26 @@ class TelegramParserV4_2Test(unittest.TestCase):
assert result[obis.TEXT_MESSAGE].unit is None
assert result[obis.TEXT_MESSAGE].value is None
# INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0)
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L1], CosemObject)
assert result[obis.INSTANTANEOUS_CURRENT_L1].unit == 'A'
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L1].value, Decimal)
assert result[obis.INSTANTANEOUS_CURRENT_L1].value == Decimal('0')
# INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0)
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L2], CosemObject)
assert result[obis.INSTANTANEOUS_CURRENT_L2].unit == 'A'
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L2].value, Decimal)
assert result[obis.INSTANTANEOUS_CURRENT_L2].value == Decimal('6')
# INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0)
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L3], CosemObject)
assert result[obis.INSTANTANEOUS_CURRENT_L3].unit == 'A'
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L3].value, Decimal)
assert result[obis.INSTANTANEOUS_CURRENT_L3].value == Decimal('2')
# DEVICE_TYPE (0-x:24.1.0)
assert isinstance(result[obis.TEXT_MESSAGE], CosemObject)
assert isinstance(result[obis.DEVICE_TYPE], CosemObject)
assert result[obis.DEVICE_TYPE].unit is None
assert isinstance(result[obis.DEVICE_TYPE].value, int)
assert result[obis.DEVICE_TYPE].value == 3
+185 -116
View File
@@ -5,7 +5,6 @@ import unittest
import pytz
from dsmr_parser import obis_references as obis
from dsmr_parser import telegram_specifications
from dsmr_parser.exceptions import InvalidChecksumError, ParseError
from dsmr_parser.objects import CosemObject, MBusObject
@@ -27,169 +26,219 @@ class TelegramParserV5Test(unittest.TestCase):
def test_parse(self):
parser = TelegramParser(telegram_specifications.V5)
result = parser.parse(TELEGRAM_V5)
try:
telegram = parser.parse(TELEGRAM_V5, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
print('test: ', type(telegram.P1_MESSAGE_HEADER), telegram.P1_MESSAGE_HEADER.__dict__)
# P1_MESSAGE_HEADER (1-3:0.2.8)
assert isinstance(result[obis.P1_MESSAGE_HEADER], CosemObject)
assert result[obis.P1_MESSAGE_HEADER].unit is None
assert isinstance(result[obis.P1_MESSAGE_HEADER].value, str)
assert result[obis.P1_MESSAGE_HEADER].value == '50'
assert isinstance(telegram.P1_MESSAGE_HEADER, CosemObject)
assert telegram.P1_MESSAGE_HEADER.unit is None
assert isinstance(telegram.P1_MESSAGE_HEADER.value, str)
assert telegram.P1_MESSAGE_HEADER.value == '50'
# P1_MESSAGE_TIMESTAMP (0-0:1.0.0)
assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP], CosemObject)
assert result[obis.P1_MESSAGE_TIMESTAMP].unit is None
assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP].value, datetime.datetime)
assert result[obis.P1_MESSAGE_TIMESTAMP].value == \
assert isinstance(telegram.P1_MESSAGE_TIMESTAMP, CosemObject)
assert telegram.P1_MESSAGE_TIMESTAMP.unit is None
assert isinstance(telegram.P1_MESSAGE_TIMESTAMP.value, datetime.datetime)
assert telegram.P1_MESSAGE_TIMESTAMP.value == \
datetime.datetime(2017, 1, 2, 18, 20, 2, tzinfo=pytz.UTC)
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject)
assert result[obis.ELECTRICITY_USED_TARIFF_1].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1].value, Decimal)
assert result[obis.ELECTRICITY_USED_TARIFF_1].value == Decimal('4.426')
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_1, CosemObject)
assert telegram.ELECTRICITY_USED_TARIFF_1.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_1.value, Decimal)
assert telegram.ELECTRICITY_USED_TARIFF_1.value == Decimal('4.426')
# ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2], CosemObject)
assert result[obis.ELECTRICITY_USED_TARIFF_2].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2].value, Decimal)
assert result[obis.ELECTRICITY_USED_TARIFF_2].value == Decimal('2.399')
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_2, CosemObject)
assert telegram.ELECTRICITY_USED_TARIFF_2.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_2.value, Decimal)
assert telegram.ELECTRICITY_USED_TARIFF_2.value == Decimal('2.399')
# ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1)
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1], CosemObject)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value, Decimal)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value == Decimal('2.444')
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_1, CosemObject)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_1.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_1.value, Decimal)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_1.value == Decimal('2.444')
# ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2)
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2], CosemObject)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value, Decimal)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value == Decimal('0')
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_2, CosemObject)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_2.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_2.value, Decimal)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_2.value == Decimal('0')
# ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0)
assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF], CosemObject)
assert result[obis.ELECTRICITY_ACTIVE_TARIFF].unit is None
assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF].value, str)
assert result[obis.ELECTRICITY_ACTIVE_TARIFF].value == '0002'
assert isinstance(telegram.ELECTRICITY_ACTIVE_TARIFF, CosemObject)
assert telegram.ELECTRICITY_ACTIVE_TARIFF.unit is None
assert isinstance(telegram.ELECTRICITY_ACTIVE_TARIFF.value, str)
assert telegram.ELECTRICITY_ACTIVE_TARIFF.value == '0002'
# EQUIPMENT_IDENTIFIER (0-0:96.1.1)
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER], CosemObject)
assert result[obis.EQUIPMENT_IDENTIFIER].unit is None
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER].value, str)
assert result[obis.EQUIPMENT_IDENTIFIER].value == '4B384547303034303436333935353037'
assert isinstance(telegram.EQUIPMENT_IDENTIFIER, CosemObject)
assert telegram.EQUIPMENT_IDENTIFIER.unit is None
assert isinstance(telegram.EQUIPMENT_IDENTIFIER.value, str)
assert telegram.EQUIPMENT_IDENTIFIER.value == '4B384547303034303436333935353037'
# CURRENT_ELECTRICITY_USAGE (1-0:1.7.0)
assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE], CosemObject)
assert result[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW'
assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE].value, Decimal)
assert result[obis.CURRENT_ELECTRICITY_USAGE].value == Decimal('0.244')
assert isinstance(telegram.CURRENT_ELECTRICITY_USAGE, CosemObject)
assert telegram.CURRENT_ELECTRICITY_USAGE.unit == 'kW'
assert isinstance(telegram.CURRENT_ELECTRICITY_USAGE.value, Decimal)
assert telegram.CURRENT_ELECTRICITY_USAGE.value == Decimal('0.244')
# CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0)
assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY], CosemObject)
assert result[obis.CURRENT_ELECTRICITY_DELIVERY].unit == 'kW'
assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY].value, Decimal)
assert result[obis.CURRENT_ELECTRICITY_DELIVERY].value == Decimal('0')
assert isinstance(telegram.CURRENT_ELECTRICITY_DELIVERY, CosemObject)
assert telegram.CURRENT_ELECTRICITY_DELIVERY.unit == 'kW'
assert isinstance(telegram.CURRENT_ELECTRICITY_DELIVERY.value, Decimal)
assert telegram.CURRENT_ELECTRICITY_DELIVERY.value == Decimal('0')
# LONG_POWER_FAILURE_COUNT (96.7.9)
assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT], CosemObject)
assert result[obis.LONG_POWER_FAILURE_COUNT].unit is None
assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT].value, int)
assert result[obis.LONG_POWER_FAILURE_COUNT].value == 0
assert isinstance(telegram.LONG_POWER_FAILURE_COUNT, CosemObject)
assert telegram.LONG_POWER_FAILURE_COUNT.unit is None
assert isinstance(telegram.LONG_POWER_FAILURE_COUNT.value, int)
assert telegram.LONG_POWER_FAILURE_COUNT.value == 0
# SHORT_POWER_FAILURE_COUNT (1-0:96.7.21)
assert isinstance(telegram.SHORT_POWER_FAILURE_COUNT, CosemObject)
assert telegram.SHORT_POWER_FAILURE_COUNT.unit is None
assert isinstance(telegram.SHORT_POWER_FAILURE_COUNT.value, int)
assert telegram.SHORT_POWER_FAILURE_COUNT.value == 13
# VOLTAGE_SAG_L1_COUNT (1-0:32.32.0)
assert isinstance(result[obis.VOLTAGE_SAG_L1_COUNT], CosemObject)
assert result[obis.VOLTAGE_SAG_L1_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SAG_L1_COUNT].value, int)
assert result[obis.VOLTAGE_SAG_L1_COUNT].value == 0
assert isinstance(telegram.VOLTAGE_SAG_L1_COUNT, CosemObject)
assert telegram.VOLTAGE_SAG_L1_COUNT.unit is None
assert isinstance(telegram.VOLTAGE_SAG_L1_COUNT.value, int)
assert telegram.VOLTAGE_SAG_L1_COUNT.value == 0
# VOLTAGE_SAG_L2_COUNT (1-0:52.32.0)
assert isinstance(result[obis.VOLTAGE_SAG_L2_COUNT], CosemObject)
assert result[obis.VOLTAGE_SAG_L2_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SAG_L2_COUNT].value, int)
assert result[obis.VOLTAGE_SAG_L2_COUNT].value == 0
assert isinstance(telegram.VOLTAGE_SAG_L2_COUNT, CosemObject)
assert telegram.VOLTAGE_SAG_L2_COUNT.unit is None
assert isinstance(telegram.VOLTAGE_SAG_L2_COUNT.value, int)
assert telegram.VOLTAGE_SAG_L2_COUNT.value == 0
# VOLTAGE_SAG_L3_COUNT (1-0:72.32.0)
assert isinstance(result[obis.VOLTAGE_SAG_L3_COUNT], CosemObject)
assert result[obis.VOLTAGE_SAG_L3_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SAG_L3_COUNT].value, int)
assert result[obis.VOLTAGE_SAG_L3_COUNT].value == 0
assert isinstance(telegram.VOLTAGE_SAG_L3_COUNT, CosemObject)
assert telegram.VOLTAGE_SAG_L3_COUNT.unit is None
assert isinstance(telegram.VOLTAGE_SAG_L3_COUNT.value, int)
assert telegram.VOLTAGE_SAG_L3_COUNT.value == 0
# VOLTAGE_SWELL_L1_COUNT (1-0:32.36.0)
assert isinstance(result[obis.VOLTAGE_SWELL_L1_COUNT], CosemObject)
assert result[obis.VOLTAGE_SWELL_L1_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SWELL_L1_COUNT].value, int)
assert result[obis.VOLTAGE_SWELL_L1_COUNT].value == 0
assert isinstance(telegram.VOLTAGE_SWELL_L1_COUNT, CosemObject)
assert telegram.VOLTAGE_SWELL_L1_COUNT.unit is None
assert isinstance(telegram.VOLTAGE_SWELL_L1_COUNT.value, int)
assert telegram.VOLTAGE_SWELL_L1_COUNT.value == 0
# VOLTAGE_SWELL_L2_COUNT (1-0:52.36.0)
assert isinstance(result[obis.VOLTAGE_SWELL_L2_COUNT], CosemObject)
assert result[obis.VOLTAGE_SWELL_L2_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SWELL_L2_COUNT].value, int)
assert result[obis.VOLTAGE_SWELL_L2_COUNT].value == 0
assert isinstance(telegram.VOLTAGE_SWELL_L2_COUNT, CosemObject)
assert telegram.VOLTAGE_SWELL_L2_COUNT.unit is None
assert isinstance(telegram.VOLTAGE_SWELL_L2_COUNT.value, int)
assert telegram.VOLTAGE_SWELL_L2_COUNT.value == 0
# VOLTAGE_SWELL_L3_COUNT (1-0:72.36.0)
assert isinstance(result[obis.VOLTAGE_SWELL_L3_COUNT], CosemObject)
assert result[obis.VOLTAGE_SWELL_L3_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SWELL_L3_COUNT].value, int)
assert result[obis.VOLTAGE_SWELL_L3_COUNT].value == 0
assert isinstance(telegram.VOLTAGE_SWELL_L3_COUNT, CosemObject)
assert telegram.VOLTAGE_SWELL_L3_COUNT.unit is None
assert isinstance(telegram.VOLTAGE_SWELL_L3_COUNT.value, int)
assert telegram.VOLTAGE_SWELL_L3_COUNT.value == 0
# INSTANTANEOUS_VOLTAGE_L1 (1-0:32.7.0)
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L1, CosemObject)
assert telegram.INSTANTANEOUS_VOLTAGE_L1.unit == 'V'
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L1.value, Decimal)
assert telegram.INSTANTANEOUS_VOLTAGE_L1.value == Decimal('230.0')
# INSTANTANEOUS_VOLTAGE_L2 (1-0:52.7.0)
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L2, CosemObject)
assert telegram.INSTANTANEOUS_VOLTAGE_L2.unit == 'V'
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L2.value, Decimal)
assert telegram.INSTANTANEOUS_VOLTAGE_L2.value == Decimal('230.0')
# INSTANTANEOUS_VOLTAGE_L3 (1-0:72.7.0)
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L3, CosemObject)
assert telegram.INSTANTANEOUS_VOLTAGE_L3.unit == 'V'
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L3.value, Decimal)
assert telegram.INSTANTANEOUS_VOLTAGE_L3.value == Decimal('229.0')
# INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0)
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L1, CosemObject)
assert telegram.INSTANTANEOUS_CURRENT_L1.unit == 'A'
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L1.value, Decimal)
assert telegram.INSTANTANEOUS_CURRENT_L1.value == Decimal('0.48')
# INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0)
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L2, CosemObject)
assert telegram.INSTANTANEOUS_CURRENT_L2.unit == 'A'
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L2.value, Decimal)
assert telegram.INSTANTANEOUS_CURRENT_L2.value == Decimal('0.44')
# INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0)
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L3, CosemObject)
assert telegram.INSTANTANEOUS_CURRENT_L3.unit == 'A'
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L3.value, Decimal)
assert telegram.INSTANTANEOUS_CURRENT_L3.value == Decimal('0.86')
# TEXT_MESSAGE (0-0:96.13.0)
assert isinstance(result[obis.TEXT_MESSAGE], CosemObject)
assert result[obis.TEXT_MESSAGE].unit is None
assert result[obis.TEXT_MESSAGE].value is None
# DEVICE_TYPE (0-x:24.1.0)
assert isinstance(result[obis.TEXT_MESSAGE], CosemObject)
assert result[obis.DEVICE_TYPE].unit is None
assert isinstance(result[obis.DEVICE_TYPE].value, int)
assert result[obis.DEVICE_TYPE].value == 3
assert isinstance(telegram.TEXT_MESSAGE, CosemObject)
assert telegram.TEXT_MESSAGE.unit is None
assert telegram.TEXT_MESSAGE.value is None
# INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value == Decimal('0.070')
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE, CosemObject)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.unit == 'kW'
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.value, Decimal)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.value == Decimal('0.070')
# INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value == Decimal('0.032')
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE, CosemObject)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.unit == 'kW'
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.value, Decimal)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.value == Decimal('0.032')
# INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value == Decimal('0.142')
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE, CosemObject)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.unit == 'kW'
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.value, Decimal)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.value == Decimal('0.142')
# INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value == Decimal('0')
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE, CosemObject)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.unit == 'kW'
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.value, Decimal)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.value == Decimal('0')
# INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value == Decimal('0')
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE, CosemObject)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.unit == 'kW'
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.value, Decimal)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.value == Decimal('0')
# INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value == Decimal('0')
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE, CosemObject)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.unit == 'kW'
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.value, Decimal)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.value == Decimal('0')
# EQUIPMENT_IDENTIFIER_GAS (0-x:96.1.0)
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS], CosemObject)
assert result[obis.EQUIPMENT_IDENTIFIER_GAS].unit is None
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS].value, str)
assert result[obis.EQUIPMENT_IDENTIFIER_GAS].value == '3232323241424344313233343536373839'
# There's only one Mbus device (gas meter) in this case. Alternatively
# use get_mbus_device_by_channel
gas_meter_devices = telegram.MBUS_DEVICES
gas_meter_device = gas_meter_devices[0]
# HOURLY_GAS_METER_READING (0-1:24.2.1)
assert isinstance(result[obis.HOURLY_GAS_METER_READING], MBusObject)
assert result[obis.HOURLY_GAS_METER_READING].unit == 'm3'
assert isinstance(result[obis.HOURLY_GAS_METER_READING].value, Decimal)
assert result[obis.HOURLY_GAS_METER_READING].value == Decimal('0.107')
# MBUS_DEVICE_TYPE (0-1:96.1.0)
assert isinstance(gas_meter_device.MBUS_DEVICE_TYPE, CosemObject)
assert gas_meter_device.MBUS_DEVICE_TYPE.unit is None
assert isinstance(gas_meter_device.MBUS_DEVICE_TYPE.value, int)
assert gas_meter_device.MBUS_DEVICE_TYPE.value == 3
# MBUS_EQUIPMENT_IDENTIFIER (0-1:96.1.0)
assert isinstance(gas_meter_device.MBUS_EQUIPMENT_IDENTIFIER, CosemObject)
assert gas_meter_device.MBUS_EQUIPMENT_IDENTIFIER.unit is None
assert isinstance(gas_meter_device.MBUS_EQUIPMENT_IDENTIFIER.value, str)
assert gas_meter_device.MBUS_EQUIPMENT_IDENTIFIER.value == '3232323241424344313233343536373839'
# MBUS_METER_READING (0-1:24.2.1)
assert isinstance(gas_meter_device.MBUS_METER_READING, MBusObject)
assert gas_meter_device.MBUS_METER_READING.unit == 'm3'
assert isinstance(telegram.MBUS_METER_READING.value, Decimal)
assert gas_meter_device.MBUS_METER_READING.value == Decimal('0.107')
def test_checksum_valid(self):
# No exception is raised.
@@ -208,7 +257,27 @@ class TelegramParserV5Test(unittest.TestCase):
def test_checksum_missing(self):
# Remove the checksum value causing a ParseError.
corrupted_telegram = TELEGRAM_V5.replace('!87B3\r\n', '')
corrupted_telegram = TELEGRAM_V5.replace('!6EEE\r\n', '')
with self.assertRaises(ParseError):
TelegramParser.validate_checksum(corrupted_telegram)
def test_gas_timestamp_invalid(self):
# Issue 120
# Sometimes a MBUS device (For ex a Gas Meter) returns an invalid timestamp
# Instead of failing, we should just ignore the timestamp
invalid_date_telegram = TELEGRAM_V5.replace(
'0-1:24.2.1(170102161005W)(00000.107*m3)\r\n',
'0-1:24.2.1(632525252525S)(00000.000)\r\n'
)
invalid_date_telegram = invalid_date_telegram.replace('!6EEE\r\n', '!90C2\r\n')
parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(invalid_date_telegram)
# MBUS DEVICE 1
mbus1 = telegram.get_mbus_device_by_channel(1)
# MBUS_METER_READING (0-1:24.2.1)
assert isinstance(mbus1.MBUS_METER_READING, MBusObject)
assert mbus1.MBUS_METER_READING.unit is None
assert isinstance(mbus1.MBUS_METER_READING.value, Decimal)
assert mbus1.MBUS_METER_READING.value == Decimal('0.000')
+308
View File
@@ -0,0 +1,308 @@
from decimal import Decimal
import datetime
import unittest
import pytz
from dsmr_parser import telegram_specifications
from dsmr_parser.exceptions import InvalidChecksumError, ParseError
from dsmr_parser.objects import CosemObject
from dsmr_parser.parsers import TelegramParser
from test.example_telegrams import TELEGRAM_V5_EON_HU
class TelegramParserV5EONHUTest(unittest.TestCase):
""" Test parsing of a DSMR v5 EON Hungary telegram. """
def test_parse(self):
parser = TelegramParser(telegram_specifications.EON_HUNGARY)
try:
telegram = parser.parse(TELEGRAM_V5_EON_HU, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# P1_MESSAGE_TIMESTAMP (0-0:1.0.0)
assert isinstance(telegram.P1_MESSAGE_TIMESTAMP, CosemObject)
assert telegram.P1_MESSAGE_TIMESTAMP.unit is None
assert isinstance(telegram.P1_MESSAGE_TIMESTAMP.value, datetime.datetime)
assert telegram.P1_MESSAGE_TIMESTAMP.value == \
pytz.timezone("Europe/Budapest").localize(datetime.datetime(2023, 7, 24, 15, 7, 30))
# EON_HU_COSEM_LOGICAL_DEVICE_NAME (0-0:42.0.0)
assert isinstance(telegram.COSEM_LOGICAL_DEVICE_NAME, CosemObject)
assert telegram.COSEM_LOGICAL_DEVICE_NAME.unit is None
assert isinstance(telegram.COSEM_LOGICAL_DEVICE_NAME.value, str)
assert telegram.COSEM_LOGICAL_DEVICE_NAME.value == '53414733303832323030303032313630'
# EON_HU_EQUIPMENT_SERIAL_NUMBER (0-0:96.1.0)
assert isinstance(telegram.EQUIPMENT_SERIAL_NUMBER, CosemObject)
assert telegram.EQUIPMENT_SERIAL_NUMBER.unit is None
assert isinstance(telegram.EQUIPMENT_SERIAL_NUMBER.value, str)
assert telegram.EQUIPMENT_SERIAL_NUMBER.value == '383930303832323030303032313630'
# ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0)
assert isinstance(telegram.ELECTRICITY_ACTIVE_TARIFF, CosemObject)
assert telegram.ELECTRICITY_ACTIVE_TARIFF.unit is None
assert isinstance(telegram.ELECTRICITY_ACTIVE_TARIFF.value, str)
assert telegram.ELECTRICITY_ACTIVE_TARIFF.value == '0001'
# ACTUAL_SWITCH_POSITION (0-0:96.3.10)
assert isinstance(telegram.ACTUAL_SWITCH_POSITION, CosemObject)
assert telegram.ACTUAL_SWITCH_POSITION.unit is None
assert isinstance(telegram.ACTUAL_SWITCH_POSITION.value, str)
assert telegram.ACTUAL_SWITCH_POSITION.value == '1'
# ACTUAL_TRESHOLD_ELECTRICITY (0-0:17.0.0)
assert isinstance(telegram.ACTUAL_TRESHOLD_ELECTRICITY, CosemObject)
assert telegram.ACTUAL_TRESHOLD_ELECTRICITY.unit == 'kW'
assert isinstance(telegram.ACTUAL_TRESHOLD_ELECTRICITY.value, Decimal)
assert telegram.ACTUAL_TRESHOLD_ELECTRICITY.value == Decimal('90.000')
# ELECTRICITY_IMPORTED_TOTAL (1-0:1.8.0)
assert isinstance(telegram.ELECTRICITY_IMPORTED_TOTAL, CosemObject)
assert telegram.ELECTRICITY_IMPORTED_TOTAL.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_IMPORTED_TOTAL.value, Decimal)
assert telegram.ELECTRICITY_IMPORTED_TOTAL.value == Decimal('000173.640')
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_1, CosemObject)
assert telegram.ELECTRICITY_USED_TARIFF_1.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_1.value, Decimal)
assert telegram.ELECTRICITY_USED_TARIFF_1.value == Decimal('000047.719')
# ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2)
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_2, CosemObject)
assert telegram.ELECTRICITY_USED_TARIFF_2.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_2.value, Decimal)
assert telegram.ELECTRICITY_USED_TARIFF_2.value == Decimal('000125.921')
# EON_HU_ELECTRICITY_USED_TARIFF_3 (1-0:1.8.3)
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_3, CosemObject)
assert telegram.ELECTRICITY_USED_TARIFF_3.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_3.value, Decimal)
assert telegram.ELECTRICITY_USED_TARIFF_3.value == Decimal('000000.000')
# EON_HU_ELECTRICITY_USED_TARIFF_4 (1-0:1.8.4)
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_4, CosemObject)
assert telegram.ELECTRICITY_USED_TARIFF_4.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_4.value, Decimal)
assert telegram.ELECTRICITY_USED_TARIFF_4.value == Decimal('000000.000')
# ELECTRICITY_EXPORTED_TOTAL (1-0:2.8.0)
assert isinstance(telegram.ELECTRICITY_EXPORTED_TOTAL, CosemObject)
assert telegram.ELECTRICITY_EXPORTED_TOTAL.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_EXPORTED_TOTAL.value, Decimal)
assert telegram.ELECTRICITY_EXPORTED_TOTAL.value == Decimal('000627.177')
# ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1)
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_1, CosemObject)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_1.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_1.value, Decimal)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_1.value == Decimal('000401.829')
# ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2)
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_2, CosemObject)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_2.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_2.value, Decimal)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_2.value == Decimal('000225.348')
# EON_HU_ELECTRICITY_DELIVERED_TARIFF_3 (1-0:2.8.3)
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_3, CosemObject)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_3.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_3.value, Decimal)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_3.value == Decimal('000000.000')
# EON_HU_ELECTRICITY_DELIVERED_TARIFF_4 (1-0:2.8.4)
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_4, CosemObject)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_4.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_4.value, Decimal)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_4.value == Decimal('000000.000')
# ELECTRICITY_REACTIVE_IMPORTED_TOTAL (1-0:3.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_IMPORTED_TOTAL, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_IMPORTED_TOTAL.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_IMPORTED_TOTAL.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_IMPORTED_TOTAL.value == Decimal('000000.123')
# ELECTRICITY_REACTIVE_EXPORTED_TOTAL (1-0:4.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_EXPORTED_TOTAL, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_EXPORTED_TOTAL.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_EXPORTED_TOTAL.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_EXPORTED_TOTAL.value == Decimal('000303.131')
# EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q1 (1-0:5.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q1, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q1.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q1.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q1.value == Decimal('000000.668')
# EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q2 (1-0:6.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q2, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q2.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q2.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q2.value == Decimal('000000.071')
# EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q3 (1-0:7.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q3, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q3.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q3.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q3.value == Decimal('000160.487')
# EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q4 (1-0:8.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q4, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q4.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q4.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q4.value == Decimal('000143.346')
# EON_HU_ELECTRICITY_COMBINED (1-0:15.8.0)
assert isinstance(telegram.ELECTRICITY_COMBINED, CosemObject)
assert telegram.ELECTRICITY_COMBINED.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_COMBINED.value, Decimal)
assert telegram.ELECTRICITY_COMBINED.value == Decimal('000800.817')
# INSTANTANEOUS_VOLTAGE_L2 (1-0:32.7.0)
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L1, CosemObject)
assert telegram.INSTANTANEOUS_VOLTAGE_L1.unit == 'V'
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L1.value, Decimal)
assert telegram.INSTANTANEOUS_VOLTAGE_L1.value == Decimal('240.4')
# INSTANTANEOUS_VOLTAGE_L2 (1-0:52.7.0)
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L2, CosemObject)
assert telegram.INSTANTANEOUS_VOLTAGE_L2.unit == 'V'
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L2.value, Decimal)
assert telegram.INSTANTANEOUS_VOLTAGE_L2.value == Decimal('239.1')
# INSTANTANEOUS_VOLTAGE_L3 (1-0:72.7.0)
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L3, CosemObject)
assert telegram.INSTANTANEOUS_VOLTAGE_L3.unit == 'V'
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L3.value, Decimal)
assert telegram.INSTANTANEOUS_VOLTAGE_L3.value == Decimal('241.2')
# INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0)
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L1, CosemObject)
assert telegram.INSTANTANEOUS_CURRENT_L1.unit == 'A'
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L1.value, Decimal)
assert telegram.INSTANTANEOUS_CURRENT_L1.value == Decimal('003')
# INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0)
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L2, CosemObject)
assert telegram.INSTANTANEOUS_CURRENT_L2.unit == 'A'
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L2.value, Decimal)
assert telegram.INSTANTANEOUS_CURRENT_L2.value == Decimal('004')
# INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0)
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L3, CosemObject)
assert telegram.INSTANTANEOUS_CURRENT_L3.unit == 'A'
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L3.value, Decimal)
assert telegram.INSTANTANEOUS_CURRENT_L3.value == Decimal('003')
# EON_HU_INSTANTANEOUS_POWER_FACTOR_TOTAL (1-0:13.7.0)
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_TOTAL, CosemObject)
assert telegram.INSTANTANEOUS_POWER_FACTOR_TOTAL.unit is None
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_TOTAL.value, Decimal)
assert telegram.INSTANTANEOUS_POWER_FACTOR_TOTAL.value == Decimal('4.556')
# EON_HU_INSTANTANEOUS_POWER_FACTOR_L1 (1-0:33.7.0)
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L1, CosemObject)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L1.unit is None
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L1.value, Decimal)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L1.value == Decimal('4.591')
# EON_HU_INSTANTANEOUS_POWER_FACTOR_L2 (1-0:53.7.0)
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L2, CosemObject)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L2.unit is None
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L2.value, Decimal)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L2.value == Decimal('4.542')
# EON_HU_INSTANTANEOUS_POWER_FACTOR_L3 (1-0:73.7.0)
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L3, CosemObject)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L3.unit is None
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L3.value, Decimal)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L3.value == Decimal('4.552')
# EON_HU_FREQUENCY (1-0:14.7.0)
assert isinstance(telegram.FREQUENCY, CosemObject)
assert telegram.FREQUENCY.unit == "Hz"
assert isinstance(telegram.FREQUENCY.value, Decimal)
assert telegram.FREQUENCY.value == Decimal('50.00')
# CURRENT_ELECTRICITY_USAGE (1-0:1.7.0)
assert isinstance(telegram.CURRENT_ELECTRICITY_USAGE, CosemObject)
assert telegram.CURRENT_ELECTRICITY_USAGE.unit == 'kW'
assert isinstance(telegram.CURRENT_ELECTRICITY_USAGE.value, Decimal)
assert telegram.CURRENT_ELECTRICITY_USAGE.value == Decimal('00.000')
# CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0)
assert isinstance(telegram.CURRENT_ELECTRICITY_DELIVERY, CosemObject)
assert telegram.CURRENT_ELECTRICITY_DELIVERY.unit == 'kW'
assert isinstance(telegram.CURRENT_ELECTRICITY_DELIVERY.value, Decimal)
assert telegram.CURRENT_ELECTRICITY_DELIVERY.value == Decimal('02.601')
# EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q1 (1-0:5.7.0)
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q1, CosemObject)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q1.unit == 'kvar'
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q1.value, Decimal)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q1.value == Decimal('00.000')
# EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q2 (1-0:6.7.0)
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q2, CosemObject)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q2.unit == 'kvar'
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q2.value, Decimal)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q2.value == Decimal('00.000')
# EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q3 (1-0:7.7.0)
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q3, CosemObject)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q3.unit == 'kvar'
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q3.value, Decimal)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q3.value == Decimal('00.504')
# EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q4 (1-0:8.7.0)
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q4, CosemObject)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q4.unit == 'kvar'
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q4.value, Decimal)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q4.value == Decimal('00.000')
# FUSE_THRESHOLD_L1 (1-0:31.4.0)
assert isinstance(telegram.FUSE_THRESHOLD_L1, CosemObject)
assert telegram.FUSE_THRESHOLD_L1.unit == 'A'
assert isinstance(telegram.FUSE_THRESHOLD_L1.value, Decimal)
assert telegram.FUSE_THRESHOLD_L1.value == Decimal('200.00')
# FUSE_THRESHOLD_L2 (1-0:31.4.0)
assert isinstance(telegram.FUSE_THRESHOLD_L2, CosemObject)
assert telegram.FUSE_THRESHOLD_L2.unit == 'A'
assert isinstance(telegram.FUSE_THRESHOLD_L2.value, Decimal)
assert telegram.FUSE_THRESHOLD_L2.value == Decimal('200.00')
# FUSE_THRESHOLD_L3 (1-0:31.4.0)
assert isinstance(telegram.FUSE_THRESHOLD_L3, CosemObject)
assert telegram.FUSE_THRESHOLD_L3.unit == 'A'
assert isinstance(telegram.FUSE_THRESHOLD_L3.value, Decimal)
assert telegram.FUSE_THRESHOLD_L3.value == Decimal('200.00')
# TEXT_MESSAGE (0-0:96.13.0)
assert isinstance(telegram.TEXT_MESSAGE, CosemObject)
assert telegram.TEXT_MESSAGE.unit is None
assert telegram.TEXT_MESSAGE.value is None
def test_checksum_valid(self):
# No exception is raised.
TelegramParser.validate_checksum(TELEGRAM_V5_EON_HU)
def test_checksum_invalid(self):
# Remove the electricty used data value. This causes the checksum to
# not match anymore.
corrupted_telegram = TELEGRAM_V5_EON_HU.replace(
'1-0:1.8.1(000047.719*kWh)\r\n',
''
)
with self.assertRaises(InvalidChecksumError):
TelegramParser.validate_checksum(corrupted_telegram)
def test_checksum_missing(self):
# Remove the checksum value causing a ParseError.
corrupted_telegram = TELEGRAM_V5_EON_HU.replace('!99DA\r\n', '')
with self.assertRaises(ParseError):
TelegramParser.validate_checksum(corrupted_telegram)
+27 -8
View File
@@ -3,10 +3,8 @@ from unittest.mock import Mock
import unittest
from dsmr_parser import obis_references as obis
from dsmr_parser import telegram_specifications
from dsmr_parser.parsers import TelegramParser
from dsmr_parser.clients.protocol import DSMRProtocol
from dsmr_parser.clients.protocol import create_dsmr_protocol
from dsmr_parser.objects import Telegram
TELEGRAM_V2_2 = (
'/ISk5\2MT382-1004\r\n'
@@ -35,9 +33,10 @@ TELEGRAM_V2_2 = (
class ProtocolTest(unittest.TestCase):
def setUp(self):
telegram_parser = TelegramParser(telegram_specifications.V2_2)
self.protocol = DSMRProtocol(None, telegram_parser,
telegram_callback=Mock())
new_protocol, _ = create_dsmr_protocol('2.2',
telegram_callback=Mock(),
keep_alive_interval=1)
self.protocol = new_protocol()
def test_complete_packet(self):
"""Protocol should assemble incoming lines into complete packet."""
@@ -45,10 +44,30 @@ class ProtocolTest(unittest.TestCase):
self.protocol.data_received(TELEGRAM_V2_2.encode('ascii'))
telegram = self.protocol.telegram_callback.call_args_list[0][0][0]
assert isinstance(telegram, dict)
assert isinstance(telegram, Telegram)
assert float(telegram[obis.CURRENT_ELECTRICITY_USAGE].value) == 1.01
assert telegram[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW'
assert float(telegram[obis.GAS_METER_READING].value) == 1.001
assert telegram[obis.GAS_METER_READING].unit == 'm3'
def test_receive_packet(self):
"""Protocol packet reception."""
mock_transport = Mock()
self.protocol.connection_made(mock_transport)
assert not self.protocol._active
self.protocol.data_received(TELEGRAM_V2_2.encode('ascii'))
assert self.protocol._active
# 1st call of keep_alive resets 'active' flag
self.protocol.keep_alive()
assert not self.protocol._active
# 2nd call of keep_alive should close the transport
self.protocol.keep_alive()
mock_transport.close.assert_called_once()
self.protocol.connection_lost(None)
+77
View File
@@ -0,0 +1,77 @@
from unittest.mock import Mock
import unittest
from dsmr_parser import obis_references as obis
from dsmr_parser.clients.rfxtrx_protocol import create_rfxtrx_dsmr_protocol, PACKETTYPE_DSMR, SUBTYPE_P1
from dsmr_parser.objects import Telegram
TELEGRAM_V2_2 = (
'/ISk5\2MT382-1004\r\n'
'\r\n'
'0-0:96.1.1(00000000000000)\r\n'
'1-0:1.8.1(00001.001*kWh)\r\n'
'1-0:1.8.2(00001.001*kWh)\r\n'
'1-0:2.8.1(00001.001*kWh)\r\n'
'1-0:2.8.2(00001.001*kWh)\r\n'
'0-0:96.14.0(0001)\r\n'
'1-0:1.7.0(0001.01*kW)\r\n'
'1-0:2.7.0(0000.00*kW)\r\n'
'0-0:17.0.0(0999.00*kW)\r\n'
'0-0:96.3.10(1)\r\n'
'0-0:96.13.1()\r\n'
'0-0:96.13.0()\r\n'
'0-1:24.1.0(3)\r\n'
'0-1:96.1.0(000000000000)\r\n'
'0-1:24.3.0(161107190000)(00)(60)(1)(0-1:24.2.1)(m3)\r\n'
'(00001.001)\r\n'
'0-1:24.4.0(1)\r\n'
'!\r\n'
)
OTHER_RF_PACKET = b'\x03\x01\x02\x03'
def encode_telegram_as_RF_packets(telegram):
data = b''
for line in telegram.split('\n'):
packet_data = (line + '\n').encode('ascii')
packet_header = bytes(bytearray([
len(packet_data) + 3, # excluding length byte
PACKETTYPE_DSMR,
SUBTYPE_P1,
0 # seq num (ignored)
]))
data += packet_header + packet_data
# other RF packets can pass by on the line
data += OTHER_RF_PACKET
return data
class RFXtrxProtocolTest(unittest.TestCase):
def setUp(self):
new_protocol, _ = create_rfxtrx_dsmr_protocol('2.2',
telegram_callback=Mock(),
keep_alive_interval=1)
self.protocol = new_protocol()
def test_complete_packet(self):
"""Protocol should assemble incoming lines into complete packet."""
data = encode_telegram_as_RF_packets(TELEGRAM_V2_2)
# send data broken up in two parts
self.protocol.data_received(data[0:200])
self.protocol.data_received(data[200:])
telegram = self.protocol.telegram_callback.call_args_list[0][0][0]
assert isinstance(telegram, Telegram)
assert float(telegram[obis.CURRENT_ELECTRICITY_USAGE].value) == 1.01
assert telegram[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW'
assert float(telegram[obis.GAS_METER_READING].value) == 1.001
assert telegram[obis.GAS_METER_READING].unit == 'm3'