{
"extension": ".py",
"source": "from decimal import Decimal\n\nimport datetime\nimport json\nimport unittest\n\nimport pytz\n\nfrom dsmr_parser import telegram_specifications\nfrom dsmr_parser.exceptions import InvalidChecksumError, ParseError\nfrom dsmr_parser.objects import CosemObject, MBusObject, MBusObjectPeak\nfrom dsmr_parser.parsers import TelegramParser\nfrom test.example_telegrams import TELEGRAM_FLUVIUS_V171, TELEGRAM_FLUVIUS_V171_ALT\n\n\nclass TelegramParserFluviusTest(unittest.TestCase):\n \"\"\" Test parsing of a DSMR Fluvius telegram. \"\"\"\n\n def test_parse(self):\n parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS)\n try:\n result = parser.parse(TELEGRAM_FLUVIUS_V171, throw_ex=True)\n except Exception as ex:\n assert False, f\"parse trigged an exception {ex}\"\n\n # BELGIUM_VERSION_INFORMATION (0-0:96.1.4)\n assert isinstance(result.BELGIUM_VERSION_INFORMATION, CosemObject)\n assert result.BELGIUM_VERSION_INFORMATION.unit is None\n assert isinstance(result.BELGIUM_VERSION_INFORMATION.value, str)\n assert result.BELGIUM_VERSION_INFORMATION.value == '50217'\n\n # EQUIPMENT_IDENTIFIER (0-0:96.1.1)\n assert isinstance(result.BELGIUM_EQUIPMENT_IDENTIFIER, CosemObject)\n assert result.BELGIUM_EQUIPMENT_IDENTIFIER.unit is None\n assert isinstance(result.BELGIUM_EQUIPMENT_IDENTIFIER.value, str)\n assert result.BELGIUM_EQUIPMENT_IDENTIFIER.value == '3153414733313031303231363035'\n\n # P1_MESSAGE_TIMESTAMP (0-0:1.0.0)\n assert isinstance(result.P1_MESSAGE_TIMESTAMP, CosemObject)\n assert result.P1_MESSAGE_TIMESTAMP.unit is None\n assert isinstance(result.P1_MESSAGE_TIMESTAMP.value, datetime.datetime)\n assert result.P1_MESSAGE_TIMESTAMP.value == \\\n pytz.timezone(\"Europe/Brussels\").localize(datetime.datetime(2020, 5, 12, 13, 54, 9))\n\n # ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)\n assert isinstance(result.ELECTRICITY_USED_TARIFF_1, CosemObject)\n assert result.ELECTRICITY_USED_TARIFF_1.unit == 'kWh'\n assert isinstance(result.ELECTRICITY_USED_TARIFF_1.value, Decimal)\n assert result.ELECTRICITY_USED_TARIFF_1.value == Decimal('0.034')\n\n # ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2)\n assert isinstance(result.ELECTRICITY_USED_TARIFF_2, CosemObject)\n assert result.ELECTRICITY_USED_TARIFF_2.unit == 'kWh'\n assert isinstance(result.ELECTRICITY_USED_TARIFF_2.value, Decimal)\n assert result.ELECTRICITY_USED_TARIFF_2.value == Decimal('15.758')\n\n # ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1)\n assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_1, CosemObject)\n assert result.ELECTRICITY_DELIVERED_TARIFF_1.unit == 'kWh'\n assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_1.value, Decimal)\n assert result.ELECTRICITY_DELIVERED_TARIFF_1.value == Decimal('0.000')\n\n # ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2)\n assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_2, CosemObject)\n assert result.ELECTRICITY_DELIVERED_TARIFF_2.unit == 'kWh'\n assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_2.value, Decimal)\n assert result.ELECTRICITY_DELIVERED_TARIFF_2.value == Decimal('0.011')\n\n # ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0)\n assert isinstance(result.ELECTRICITY_ACTIVE_TARIFF, CosemObject)\n assert result.ELECTRICITY_ACTIVE_TARIFF.unit is None\n assert isinstance(result.ELECTRICITY_ACTIVE_TARIFF.value, str)\n assert result.ELECTRICITY_ACTIVE_TARIFF.value == '0001'\n\n # BELGIUM_CURRENT_AVERAGE_DEMAND (1-0:1.4.0)\n assert isinstance(result.BELGIUM_CURRENT_AVERAGE_DEMAND, CosemObject)\n assert result.BELGIUM_CURRENT_AVERAGE_DEMAND.unit == 'kW'\n assert isinstance(result.BELGIUM_CURRENT_AVERAGE_DEMAND.value, Decimal)\n assert result.BELGIUM_CURRENT_AVERAGE_DEMAND.value == Decimal('2.351')\n\n # BELGIUM_MAXIMUM_DEMAND_MONTH (1-0:1.6.0)\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_MONTH, MBusObject)\n assert result.BELGIUM_MAXIMUM_DEMAND_MONTH.unit == 'kW'\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_MONTH.value, Decimal)\n assert result.BELGIUM_MAXIMUM_DEMAND_MONTH.value == Decimal('2.589')\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_MONTH.datetime, datetime.datetime)\n assert result.BELGIUM_MAXIMUM_DEMAND_MONTH.datetime == \\\n pytz.timezone(\"Europe/Brussels\").localize(datetime.datetime(2020, 5, 9, 13, 45, 58))\n\n # BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 0\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0], MBusObjectPeak)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].unit == 'kW'\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].value, Decimal)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].value == Decimal('3.695')\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].datetime, datetime.datetime)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].datetime == \\\n pytz.timezone(\"Europe/Brussels\").localize(datetime.datetime(2020, 5, 1, 0, 0, 0))\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].occurred, datetime.datetime)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].occurred == \\\n pytz.timezone(\"Europe/Brussels\").localize(datetime.datetime(2020, 4, 23, 19, 25, 38))\n # BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 1\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1], MBusObjectPeak)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].unit == 'kW'\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].value, Decimal)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].value == Decimal('5.980')\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].datetime, datetime.datetime)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].datetime == \\\n pytz.timezone(\"Europe/Brussels\").localize(datetime.datetime(2020, 4, 1, 0, 0, 0))\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].occurred, datetime.datetime)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].occurred == \\\n pytz.timezone(\"Europe/Brussels\").localize(datetime.datetime(2020, 3, 5, 12, 21, 39))\n # BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 2\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2], MBusObjectPeak)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].unit == 'kW'\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].value, Decimal)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].value == Decimal('4.318')\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].datetime, datetime.datetime)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].datetime == \\\n pytz.timezone(\"Europe/Brussels\").localize(datetime.datetime(2020, 3, 1, 0, 0, 0))\n assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].occurred, datetime.datetime)\n assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].occurred == \\\n pytz.timezone(\"Europe/Brussels\").localize(datetime.datetime(2020, 2, 10, 3, 54, 21))\n\n # CURRENT_ELECTRICITY_USAGE (1-0:1.7.0)\n assert isinstance(result.CURRENT_ELECTRICITY_USAGE, CosemObject)\n assert result.CURRENT_ELECTRICITY_USAGE.unit == 'kW'\n assert isinstance(result.CURRENT_ELECTRICITY_USAGE.value, Decimal)\n assert result.CURRENT_ELECTRICITY_USAGE.value == Decimal('0.000')\n\n # CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0)\n assert isinstance(result.CURRENT_ELECTRICITY_DELIVERY, CosemObject)\n assert result.CURRENT_ELECTRICITY_DELIVERY.unit == 'kW'\n assert isinstance(result.CURRENT_ELECTRICITY_DELIVERY.value, Decimal)\n assert result.CURRENT_ELECTRICITY_DELIVERY.value == Decimal('0.000')\n\n # INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0)\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE, CosemObject)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.unit == 'kW'\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.value, Decimal)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.value == Decimal('0.000')\n\n # INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0)\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE, CosemObject)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.unit == 'kW'\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.value, Decimal)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.value == Decimal('0.000')\n\n # INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0)\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE, CosemObject)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.unit == 'kW'\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.value, Decimal)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.value == Decimal('0.000')\n\n # INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0)\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE, CosemObject)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.unit == 'kW'\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.value, Decimal)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.value == Decimal('0.000')\n\n # INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0)\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE, CosemObject)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.unit == 'kW'\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.value, Decimal)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.value == Decimal('0.000')\n\n # INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0)\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE, CosemObject)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.unit == 'kW'\n assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.value, Decimal)\n assert result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.value == Decimal('0.000')\n\n # INSTANTANEOUS_VOLTAGE_L1 (1-0:32.7.0)\n assert isinstance(result.INSTANTANEOUS_VOLTAGE_L1, CosemObject)\n assert result.INSTANTANEOUS_VOLTAGE_L1.unit == 'V'\n assert isinstance(result.INSTANTANEOUS_VOLTAGE_L1.value, Decimal)\n assert result.INSTANTANEOUS_VOLTAGE_L1.value == Decimal('234.7')\n\n # INSTANTANEOUS_VOLTAGE_L2 (1-0:52.7.0)\n assert isinstance(result.INSTANTANEOUS_VOLTAGE_L2, CosemObject)\n assert result.INSTANTANEOUS_VOLTAGE_L2.unit == 'V'\n assert isinstance(result.INSTANTANEOUS_VOLTAGE_L2.value, Decimal)\n assert result.INSTANTANEOUS_VOLTAGE_L2.value == Decimal('234.7')\n\n # INSTANTANEOUS_VOLTAGE_L3 (1-0:72.7.0)\n assert isinstance(result.INSTANTANEOUS_VOLTAGE_L3, CosemObject)\n assert result.INSTANTANEOUS_VOLTAGE_L3.unit == 'V'\n assert isinstance(result.INSTANTANEOUS_VOLTAGE_L3.value, Decimal)\n assert result.INSTANTANEOUS_VOLTAGE_L3.value == Decimal('234.7')\n\n # INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0)\n assert isinstance(result.INSTANTANEOUS_CURRENT_L1, CosemObject)\n assert result.INSTANTANEOUS_CURRENT_L1.unit == 'A'\n assert isinstance(result.INSTANTANEOUS_CURRENT_L1.value, Decimal)\n assert result.INSTANTANEOUS_CURRENT_L1.value == Decimal('0.000')\n\n # INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0)\n assert isinstance(result.INSTANTANEOUS_CURRENT_L2, CosemObject)\n assert result.INSTANTANEOUS_CURRENT_L2.unit == 'A'\n assert isinstance(result.INSTANTANEOUS_CURRENT_L2.value, Decimal)\n assert result.INSTANTANEOUS_CURRENT_L2.value == Decimal('0.000')\n\n # INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0)\n assert isinstance(result.INSTANTANEOUS_CURRENT_L3, CosemObject)\n assert result.INSTANTANEOUS_CURRENT_L3.unit == 'A'\n assert isinstance(result.INSTANTANEOUS_CURRENT_L3.value, Decimal)\n assert result.INSTANTANEOUS_CURRENT_L3.value == Decimal('0.000')\n\n # ACTUAL_SWITCH_POSITION (0-0:96.3.10)\n assert isinstance(result.ACTUAL_SWITCH_POSITION, CosemObject)\n assert result.ACTUAL_SWITCH_POSITION.unit is None\n assert isinstance(result.ACTUAL_SWITCH_POSITION.value, int)\n assert result.ACTUAL_SWITCH_POSITION.value == 1\n\n # ACTUAL_TRESHOLD_ELECTRICITY (0-0:17.0.0)\n assert isinstance(result.ACTUAL_TRESHOLD_ELECTRICITY, CosemObject)\n assert result.ACTUAL_TRESHOLD_ELECTRICITY.unit == 'kW'\n assert isinstance(result.ACTUAL_TRESHOLD_ELECTRICITY.value, Decimal)\n assert result.ACTUAL_TRESHOLD_ELECTRICITY.value == Decimal('999.9')\n\n # FUSE_THRESHOLD_L1 (1-0:31.4.0)\n assert isinstance(result.FUSE_THRESHOLD_L1, CosemObject)\n assert result.FUSE_THRESHOLD_L1.unit == 'A'\n assert isinstance(result.FUSE_THRESHOLD_L1.value, Decimal)\n assert result.FUSE_THRESHOLD_L1.value == Decimal('999')\n\n # TEXT_MESSAGE (0-0:96.13.0)\n assert isinstance(result.TEXT_MESSAGE, CosemObject)\n assert result.TEXT_MESSAGE.unit is None\n assert result.TEXT_MESSAGE.value is None\n\n # MBUS DEVICE 1\n mbus1 = result.get_mbus_device_by_channel(1)\n\n # MBUS_DEVICE_TYPE (0-1:24.1.0)\n assert isinstance(mbus1.MBUS_DEVICE_TYPE, CosemObject)\n assert mbus1.MBUS_DEVICE_TYPE.unit is None\n assert isinstance(mbus1.MBUS_DEVICE_TYPE.value, int)\n assert mbus1.MBUS_DEVICE_TYPE.value == 3\n\n # MBUS_EQUIPMENT_IDENTIFIER (0-1:96.1.1)\n assert isinstance(mbus1.MBUS_EQUIPMENT_IDENTIFIER, CosemObject)\n assert mbus1.MBUS_EQUIPMENT_IDENTIFIER.unit is None\n assert isinstance(mbus1.MBUS_EQUIPMENT_IDENTIFIER.value, str)\n assert mbus1.MBUS_EQUIPMENT_IDENTIFIER.value == '37464C4F32313139303333373333'\n\n # MBUS_VALVE_POSITION (0-1:24.4.0)\n assert isinstance(result.MBUS_VALVE_POSITION, CosemObject)\n assert result.MBUS_VALVE_POSITION.unit is None\n assert isinstance(result.MBUS_VALVE_POSITION.value, int)\n assert result.MBUS_VALVE_POSITION.value == 1\n\n # MBUS_METER_READING (0-1:24.2.3)\n assert isinstance(mbus1.MBUS_METER_READING, MBusObject)\n assert mbus1.MBUS_METER_READING.unit == 'm3'\n assert isinstance(mbus1.MBUS_METER_READING.value, Decimal)\n assert mbus1.MBUS_METER_READING.value == Decimal('112.384')\n\n # MBUS DEVICE 2\n mbus2 = result.get_mbus_device_by_channel(2)\n\n # MBUS_DEVICE_TYPE (0-2:24.1.0)\n assert isinstance(mbus2.MBUS_DEVICE_TYPE, CosemObject)\n assert mbus2.MBUS_DEVICE_TYPE.unit is None\n assert isinstance(mbus2.MBUS_DEVICE_TYPE.value, int)\n assert mbus2.MBUS_DEVICE_TYPE.value == 7\n\n # MBUS_EQUIPMENT_IDENTIFIER (0-2:96.1.1)\n assert isinstance(mbus2.MBUS_EQUIPMENT_IDENTIFIER, CosemObject)\n assert mbus2.MBUS_EQUIPMENT_IDENTIFIER.unit is None\n assert isinstance(mbus2.MBUS_EQUIPMENT_IDENTIFIER.value, str)\n assert mbus2.MBUS_EQUIPMENT_IDENTIFIER.value == '3853414731323334353637383930'\n\n # MBUS_METER_READING (0-1:24.2.1)\n assert isinstance(mbus2.MBUS_METER_READING, MBusObject)\n assert mbus2.MBUS_METER_READING.unit == 'm3'\n assert isinstance(mbus2.MBUS_METER_READING.value, Decimal)\n assert mbus2.MBUS_METER_READING.value == Decimal('872.234')\n\n def test_checksum_valid(self):\n # No exception is raised.\n TelegramParser.validate_checksum(TELEGRAM_FLUVIUS_V171)\n\n def test_checksum_invalid(self):\n # Remove the electricty used data value. This causes the checksum to\n # not match anymore.\n corrupted_telegram = TELEGRAM_FLUVIUS_V171.replace(\n '1-0:1.8.1(000000.034*kWh)\\r\\n',\n ''\n )\n\n with self.assertRaises(InvalidChecksumError):\n TelegramParser.validate_checksum(corrupted_telegram)\n\n def test_checksum_missing(self):\n # Remove the checksum value causing a ParseError.\n corrupted_telegram = TELEGRAM_FLUVIUS_V171.replace('!3AD7\\r\\n', '')\n with self.assertRaises(ParseError):\n TelegramParser.validate_checksum(corrupted_telegram)\n\n def test_to_json(self):\n parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS)\n telegram = parser.parse(TELEGRAM_FLUVIUS_V171_ALT)\n json_data = json.loads(telegram.to_json())\n\n self.maxDiff = None\n\n self.assertEqual(\n json_data,\n {'BELGIUM_VERSION_INFORMATION': {'value': '50217', 'unit': None},\n 'BELGIUM_EQUIPMENT_IDENTIFIER': {'value': '3153414733313030373231333236', 'unit': None},\n 'P1_MESSAGE_TIMESTAMP': {'value': '2023-11-02T11:15:48+00:00', 'unit': None},\n 'ELECTRICITY_USED_TARIFF_1': {'value': 301.548, 'unit': 'kWh'},\n 'ELECTRICITY_USED_TARIFF_2': {'value': 270.014, 'unit': 'kWh'},\n 'ELECTRICITY_DELIVERED_TARIFF_1': {'value': 0.005, 'unit': 'kWh'},\n 'ELECTRICITY_DELIVERED_TARIFF_2': {'value': 0.0, 'unit': 'kWh'},\n 'ELECTRICITY_ACTIVE_TARIFF': {'value': '0001', 'unit': None},\n 'BELGIUM_CURRENT_AVERAGE_DEMAND': {'value': 0.052, 'unit': 'kW'},\n 'BELGIUM_MAXIMUM_DEMAND_MONTH': {'datetime': '2023-11-02T10:45:00+00:00',\n 'value': 3.064, 'unit': 'kW'},\n 'BELGIUM_MAXIMUM_DEMAND_13_MONTHS': [{'datetime': '2023-07-31T22:00:00+00:00',\n 'occurred': None, 'value': 0.0, 'unit': 'kW'},\n {'datetime': '2023-08-31T22:00:00+00:00',\n 'occurred': '2023-08-31T16:15:00+00:00',\n 'value': 1.862, 'unit': 'kW'},\n {'datetime': '2023-09-30T22:00:00+00:00',\n 'occurred': '2023-09-10T16:30:00+00:00',\n 'value': 4.229, 'unit': 'kW'},\n {'datetime': '2023-10-31T23:00:00+00:00',\n 'occurred': '2023-10-16T11:00:00+00:00',\n 'value': 4.927, 'unit': 'kW'}],\n 'CURRENT_ELECTRICITY_USAGE': {'value': 0.338, 'unit': 'kW'},\n 'CURRENT_ELECTRICITY_DELIVERY': {'value': 0.0, 'unit': 'kW'},\n 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE': {'value': 0.047, 'unit': 'kW'},\n 'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE': {'value': 0.179, 'unit': 'kW'},\n 'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE': {'value': 0.111, 'unit': 'kW'},\n 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE': {'value': 0.0, 'unit': 'kW'},\n 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE': {'value': 0.0, 'unit': 'kW'},\n 'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE': {'value': 0.0, 'unit': 'kW'},\n 'INSTANTANEOUS_VOLTAGE_L1': {'value': 232.9, 'unit': 'V'},\n 'INSTANTANEOUS_VOLTAGE_L2': {'value': 228.1, 'unit': 'V'},\n 'INSTANTANEOUS_VOLTAGE_L3': {'value': 228.1, 'unit': 'V'},\n 'INSTANTANEOUS_CURRENT_L1': {'value': 0.27, 'unit': 'A'},\n 'INSTANTANEOUS_CURRENT_L2': {'value': 0.88, 'unit': 'A'},\n 'INSTANTANEOUS_CURRENT_L3': {'value': 0.52, 'unit': 'A'},\n 'ACTUAL_SWITCH_POSITION': {'value': 1, 'unit': None},\n 'ACTUAL_TRESHOLD_ELECTRICITY': {'value': 999.9, 'unit': 'kW'},\n 'FUSE_THRESHOLD_L1': {'value': 999.0, 'unit': 'A'},\n 'TEXT_MESSAGE': {'value': None, 'unit': None},\n 'MBUS_DEVICES': [{'MBUS_DEVICE_TYPE': {'value': 3, 'unit': None},\n 'MBUS_EQUIPMENT_IDENTIFIER': {'value': '37464C4F32313233303838303237',\n 'unit': None},\n 'MBUS_VALVE_POSITION': {'value': 1, 'unit': None},\n 'MBUS_METER_READING': {'datetime': '2023-11-02T11:10:02+00:00',\n 'value': 92.287, 'unit': 'm3'},\n 'CHANNEL_ID': 1},\n {'MBUS_DEVICE_TYPE': {'value': 7, 'unit': None},\n 'MBUS_EQUIPMENT_IDENTIFIER': {'value': '3853455430303030393631313733',\n 'unit': None},\n 'MBUS_METER_READING': {'datetime': '2023-11-02T11:15:32+00:00',\n 'value': 8.579, 'unit': 'm3'},\n 'CHANNEL_ID': 2}]}\n )\n\n def test_to_str(self):\n parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS)\n telegram = parser.parse(TELEGRAM_FLUVIUS_V171_ALT)\n\n self.assertEqual(\n str(telegram),\n (\n 'BELGIUM_VERSION_INFORMATION: \t 50217\t[None]\\n'\n 'BELGIUM_EQUIPMENT_IDENTIFIER: \t 3153414733313030373231333236\t[None]\\n'\n 'P1_MESSAGE_TIMESTAMP: \t 2023-11-02T11:15:48+00:00\t[None]\\n'\n 'ELECTRICITY_USED_TARIFF_1: \t 301.548\t[kWh]\\n'\n 'ELECTRICITY_USED_TARIFF_2: \t 270.014\t[kWh]\\n'\n 'ELECTRICITY_DELIVERED_TARIFF_1: \t 0.005\t[kWh]\\n'\n 'ELECTRICITY_DELIVERED_TARIFF_2: \t 0.000\t[kWh]\\n'\n 'ELECTRICITY_ACTIVE_TARIFF: \t 0001\t[None]\\n'\n 'BELGIUM_CURRENT_AVERAGE_DEMAND: \t 0.052\t[kW]\\n'\n 'BELGIUM_MAXIMUM_DEMAND_MONTH: \t 3.064\t[kW] at 2023-11-02T10:45:00+00:00\\n'\n '0.0\t[kW] at 2023-07-31T22:00:00+00:00 occurred None'\n '1.862\t[kW] at 2023-08-31T22:00:00+00:00 occurred 2023-08-31T16:15:00+00:00'\n '4.229\t[kW] at 2023-09-30T22:00:00+00:00 occurred 2023-09-10T16:30:00+00:00'\n '4.927\t[kW] at 2023-10-31T23:00:00+00:00 occurred 2023-10-16T11:00:00+00:00'\n 'CURRENT_ELECTRICITY_USAGE: \t 0.338\t[kW]\\n'\n 'CURRENT_ELECTRICITY_DELIVERY: \t 0.000\t[kW]\\n'\n 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: \t 0.047\t[kW]\\n'\n 'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: \t 0.179\t[kW]\\n'\n 'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: \t 0.111\t[kW]\\n'\n 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: \t 0.000\t[kW]\\n'\n 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: \t 0.000\t[kW]\\n'\n 'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: \t 0.000\t[kW]\\n'\n 'INSTANTANEOUS_VOLTAGE_L1: \t 232.9\t[V]\\n'\n 'INSTANTANEOUS_VOLTAGE_L2: \t 228.1\t[V]\\n'\n 'INSTANTANEOUS_VOLTAGE_L3: \t 228.1\t[V]\\n'\n 'INSTANTANEOUS_CURRENT_L1: \t 0.27\t[A]\\n'\n 'INSTANTANEOUS_CURRENT_L2: \t 0.88\t[A]\\n'\n 'INSTANTANEOUS_CURRENT_L3: \t 0.52\t[A]\\n'\n 'ACTUAL_SWITCH_POSITION: \t 1\t[None]\\n'\n 'ACTUAL_TRESHOLD_ELECTRICITY: \t 999.9\t[kW]\\n'\n 'FUSE_THRESHOLD_L1: \t 999\t[A]\\n'\n 'TEXT_MESSAGE: \t None\t[None]\\n'\n 'MBUS DEVICE (channel 1)\\n'\n '\tMBUS_DEVICE_TYPE: \t 3\t[None]\\n'\n '\tMBUS_EQUIPMENT_IDENTIFIER: \t 37464C4F32313233303838303237\t[None]\\n'\n '\tMBUS_VALVE_POSITION: \t 1\t[None]\\n'\n '\tMBUS_METER_READING: \t 92.287\t[m3] at 2023-11-02T11:10:02+00:00\\n'\n 'MBUS DEVICE (channel 2)\\n'\n '\tMBUS_DEVICE_TYPE: \t 7\t[None]\\n'\n '\tMBUS_EQUIPMENT_IDENTIFIER: \t 3853455430303030393631313733\t[None]\\n'\n '\tMBUS_METER_READING: \t 8.579\t[m3] at 2023-11-02T11:15:32+00:00\\n'\n )\n )\n",
"review": "**Grade: 8**\n\n### Bugs\n- None observed in this segment.\n\n### Optimizations\n- Rather than numerous inline assertions, consider using table-driven tests to streamline data processing and verification.\n- Consider creating separate validation methods for common validation steps to DRY (Don't Repeat Yourself) the code.\n- Utilize `unittest.TestCase` methods like `self.assertIsInstance`, `self.assertEqual` for more readable and better error-reported assertions.\n \n### Good Points\n- Comprehensive test coverage for multiple aspects of parsing a DSMR Fluvius telegram.\n- Any raised exception in `test_parse` is caught, ensuring graceful error handling.\n- Utilizes the `unittest` framework for structured and systematic testing.\n- Tests multiple lines of a telegram, providing confidence in broader coverage and reliability of parsing implementation.\n\n### Summary\nThe provided code efficiently validates and parses DSMR Fluvius telegrams, with robust unit tests that cover numerous aspects and edge cases of the parsing process. The tests ensure that the various components of the telegram are accurately parsed and checked against expected values. Opportunities for improvement include reducing code repetition and utilizing Python's unittest asserts more thoroughly for readability and maintainability improvements.\n\n### Open Source Alternatives\n- **Home Assistant DSMR integration**: An integration that reads out DSMR telegram data as sensors in the Home Assistant ecosystem.\n- **DSMR Reader**: An application to display and graph data from the Dutch Smart Meter Requirement (DSMR) in a user-friendly manner.\n- **datalogger**: A Python library to read data from different types of smart meters and log them.",
"filename": "test_parse_fluvius.py",
"path": "test/test_parse_fluvius.py",
"directory": "test",
"grade": 8,
"size": 24155,
"line_count": 414
}