diff --git a/README.rst b/README.rst index a19d2f6..ec5d3a1 100644 --- a/README.rst +++ b/README.rst @@ -112,10 +112,8 @@ However, if we construct a mock TelegramParser that just returns the already par import asyncio import logging - #from dsmr_parser import obis_references - #from dsmr_parser import telegram_specifications - #from dsmr_parser.clients.protocol import create_dsmr_reader, create_tcp_dsmr_reader - #from dsmr_parser.objects import Telegram + from dsmr_parser import telegram_specifications + from dsmr_parser.clients.protocol import create_tcp_dsmr_reader logging.basicConfig(level=logging.INFO, format='%(message)s') @@ -141,19 +139,18 @@ However, if we construct a mock TelegramParser that just returns the already par except ParseError as e: logger.error('Failed to parse telegram: %s', e) - async def main(): try: logger.debug("Getting loop") loop = asyncio.get_event_loop() logger.debug("Creating reader") - await create_tcp_dsmr_reader( - HOST, - PORT, - DSMR_VERSION, - printTelegram, - loop - ) + await create_tcp_dsmr_reader( + HOST, + PORT, + DSMR_VERSION, + printTelegram, + loop + ) logger.debug("Reader created going to sleep now") while True: await asyncio.sleep(1) @@ -172,7 +169,7 @@ However, if we construct a mock TelegramParser that just returns the already par Parsing module usage -------------------- The parsing module accepts complete unaltered telegram strings and parses these -into a dictionary. +into a Telegram object. This previously was a dictionary, but the Telegram object is mostly compatible. .. code-block:: python @@ -207,61 +204,8 @@ into a dictionary. parser = TelegramParser(telegram_specifications.V3) + # see 'Telegram object' docs below telegram = parser.parse(telegram_str) - print(telegram) # see 'Telegram object' docs below - -Telegram dictionary -------------------- - -A dictionary of which the key indicates the field type. These regex values -correspond to one of dsmr_parser.obis_reference constants. - -The value is either a CosemObject or MBusObject. These have a 'value' and 'unit' -property. MBusObject's additionally have a 'datetime' property. The 'value' can -contain any python type (int, str, Decimal) depending on the field. The 'unit' -contains 'kW', 'A', 'kWh' or 'm3'. - -.. code-block:: python - - # Contents of a parsed DSMR v3 telegram - {'\\d-\\d:17\\.0\\.0.+?\\r\\n': , - '\\d-\\d:1\\.7\\.0.+?\\r\\n': , - '\\d-\\d:1\\.8\\.1.+?\\r\\n': , - '\\d-\\d:1\\.8\\.2.+?\\r\\n': , - '\\d-\\d:24\\.1\\.0.+?\\r\\n': , - '\\d-\\d:24\\.3\\.0.+?\\r\\n.+?\\r\\n': , - '\\d-\\d:24\\.4\\.0.+?\\r\\n': , - '\\d-\\d:2\\.7\\.0.+?\\r\\n': , - '\\d-\\d:2\\.8\\.1.+?\\r\\n': , - '\\d-\\d:2\\.8\\.2.+?\\r\\n': , - '\\d-\\d:96\\.13\\.0.+?\\r\\n': , - '\\d-\\d:96\\.13\\.1.+?\\r\\n': , - '\\d-\\d:96\\.14\\.0.+?\\r\\n': , - '\\d-\\d:96\\.1\\.0.+?\\r\\n': , - '\\d-\\d:96\\.1\\.1.+?\\r\\n': , - '\\d-\\d:96\\.3\\.10.+?\\r\\n': } - -Example to get some of the values: - -.. code-block:: python - - from dsmr_parser import obis_references - - # The telegram message timestamp. - message_datetime = telegram[obis_references.P1_MESSAGE_TIMESTAMP] - - # Using the active tariff to determine the electricity being used and - # delivered for the right tariff. - active_tariff = telegram[obis_references.ELECTRICITY_ACTIVE_TARIFF] - active_tariff = int(tariff.value) - - electricity_used_total = telegram[obis_references.ELECTRICITY_USED_TARIFF_ALL[active_tariff - 1]] - electricity_delivered_total = telegram[obis_references.ELECTRICITY_DELIVERED_TARIFF_ALL[active_tariff - 1]] - - gas_reading = telegram[obis_references.HOURLY_GAS_METER_READING] - - # See dsmr_reader.obis_references for all readable telegram values. - # Note that the available values differ per DSMR version. Telegram object --------------------- @@ -276,122 +220,31 @@ Telegram object parser = TelegramParser(telegram_specifications.V5) telegram = parser.parse(TELEGRAM_V5) - # Get telegram message timestamp. - telegram.get(obis_references.P1_MESSAGE_TIMESTAMP) + # Print contents of all available values + # See dsmr_parser.obis_name_mapping for all readable telegram values. + # The available values differ per DSMR version and meter. + print(telegram) + # P1_MESSAGE_HEADER: 42 [None] + # P1_MESSAGE_TIMESTAMP: 2016-11-13 19:57:57+00:00 [None] + # EQUIPMENT_IDENTIFIER: 3960221976967177082151037881335713 [None] + # ELECTRICITY_USED_TARIFF_1: 1581.123 [kWh] + # etc. - # Get current electricity usage - telegram.get(obis_references.CURRENT_ELECTRICITY_USAGE) + # Example to get current electricity usage + print(telegram.CURRENT_ELECTRICITY_USAGE) + print(telegram.CURRENT_ELECTRICITY_USAGE.value) + print(telegram.CURRENT_ELECTRICITY_USAGE.unit) + # + # Decimal('2.027') + # 'kW' - # Get gas meter readings. Note that this returns a list if multiple gas meter readings are found. - # These gas reading have a channel attribute that can be used to filter them. Or you can supply a channel - # as an argument: - gas_readings = telegram.get(obis_references.HOURLY_GAS_METER_READING) - gas_reading_channel_1 = telegram.get(obis_references.HOURLY_GAS_METER_READING, channel=1) - -.. code-block:: python - - # DSMR v4.2 p1 using dsmr_parser and telegram objects - - from dsmr_parser import telegram_specifications - from dsmr_parser.clients import SerialReader, SERIAL_SETTINGS_V5 - from dsmr_parser.objects import CosemObject, MBusObject, Telegram - from dsmr_parser.parsers import TelegramParser - import os - - serial_reader = SerialReader( - device='/dev/ttyUSB0', - serial_settings=SERIAL_SETTINGS_V5, - telegram_specification=telegram_specifications.V4 - ) - - # telegram = next(serial_reader.read_as_object()) - # print(telegram) - - for telegram in serial_reader.read_as_object(): - os.system('clear') - print(telegram) - -Example of output of print of the telegram object: - -.. code-block:: console - - P1_MESSAGE_HEADER: 42 [None] - P1_MESSAGE_TIMESTAMP: 2016-11-13 19:57:57+00:00 [None] - EQUIPMENT_IDENTIFIER: 3960221976967177082151037881335713 [None] - ELECTRICITY_USED_TARIFF_1: 1581.123 [kWh] - ELECTRICITY_USED_TARIFF_2: 1435.706 [kWh] - ELECTRICITY_DELIVERED_TARIFF_1: 0.000 [kWh] - ELECTRICITY_DELIVERED_TARIFF_2: 0.000 [kWh] - ELECTRICITY_ACTIVE_TARIFF: 0002 [None] - CURRENT_ELECTRICITY_USAGE: 2.027 [kW] - CURRENT_ELECTRICITY_DELIVERY: 0.000 [kW] - LONG_POWER_FAILURE_COUNT: 7 [None] - VOLTAGE_SAG_L1_COUNT: 0 [None] - VOLTAGE_SAG_L2_COUNT: 0 [None] - VOLTAGE_SAG_L3_COUNT: 0 [None] - VOLTAGE_SWELL_L1_COUNT: 0 [None] - VOLTAGE_SWELL_L2_COUNT: 0 [None] - VOLTAGE_SWELL_L3_COUNT: 0 [None] - TEXT_MESSAGE_CODE: None [None] - TEXT_MESSAGE: None [None] - DEVICE_TYPE: 3 [None] - INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 0.170 [kW] - INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 1.247 [kW] - INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 0.209 [kW] - INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 0.000 [kW] - INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 0.000 [kW] - INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 0.000 [kW] - EQUIPMENT_IDENTIFIER_GAS: 4819243993373755377509728609491464 [None] - HOURLY_GAS_METER_READING: 981.443 [m3] - -Accessing the telegrams information as attributes directly: - -.. code-block:: python - - telegram - Out[3]: - telegram.CURRENT_ELECTRICITY_USAGE - Out[4]: - telegram.CURRENT_ELECTRICITY_USAGE.value - Out[5]: Decimal('2.027') - telegram.CURRENT_ELECTRICITY_USAGE.unit - Out[6]: 'kW' - -The telegram object has an iterator, can be used to find all the information elements in the current telegram: - -.. code-block:: python - - [attr for attr, value in telegram] - Out[11]: - ['P1_MESSAGE_HEADER', - 'P1_MESSAGE_TIMESTAMP', - 'EQUIPMENT_IDENTIFIER', - 'ELECTRICITY_USED_TARIFF_1', - 'ELECTRICITY_USED_TARIFF_2', - 'ELECTRICITY_DELIVERED_TARIFF_1', - 'ELECTRICITY_DELIVERED_TARIFF_2', - 'ELECTRICITY_ACTIVE_TARIFF', - 'CURRENT_ELECTRICITY_USAGE', - 'CURRENT_ELECTRICITY_DELIVERY', - 'LONG_POWER_FAILURE_COUNT', - 'VOLTAGE_SAG_L1_COUNT', - 'VOLTAGE_SAG_L2_COUNT', - 'VOLTAGE_SAG_L3_COUNT', - 'VOLTAGE_SWELL_L1_COUNT', - 'VOLTAGE_SWELL_L2_COUNT', - 'VOLTAGE_SWELL_L3_COUNT', - 'TEXT_MESSAGE_CODE', - 'TEXT_MESSAGE', - 'DEVICE_TYPE', - 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE', - 'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE', - 'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE', - 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE', - 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE', - 'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE', - 'EQUIPMENT_IDENTIFIER_GAS', - 'HOURLY_GAS_METER_READING'] + # All Mbus device readings like gas meters and water meters can be retrieved as follows: + mbus_devices = telegram.get_mbus_devices() + # A specific device based on the channel the device is connected to can be retrieved as follows: + mbus_device = telegram.get_mbus_device_by_channel(1) + print(mbus_device.EQUIPMENT_IDENTIFIER_GAS.value) # '4730303339303031393336393930363139' + print(mbus_device.HOURLY_GAS_METER_READING.value) # Decimal('246.138') Installation ------------ diff --git a/dsmr_parser/obis_name_mapping.py b/dsmr_parser/obis_name_mapping.py index 0f1d7ed..44ebb82 100644 --- a/dsmr_parser/obis_name_mapping.py +++ b/dsmr_parser/obis_name_mapping.py @@ -20,10 +20,8 @@ EN = { obis.ELECTRICITY_DELIVERED_TARIFF_2: 'ELECTRICITY_DELIVERED_TARIFF_2', obis.ELECTRICITY_ACTIVE_TARIFF: 'ELECTRICITY_ACTIVE_TARIFF', obis.CURRENT_REACTIVE_EXPORTED: 'CURRENT_REACTIVE_EXPORTED', - obis.ELECTRICITY_REACTIVE_IMPORTED_TOTAL: 'ELECTRICITY_REACTIVE_IMPORTED_TOTAL', obis.ELECTRICITY_REACTIVE_IMPORTED_TARIFF_1: 'ELECTRICITY_REACTIVE_IMPORTED_TARIFF_1', obis.ELECTRICITY_REACTIVE_IMPORTED_TARIFF_2: 'ELECTRICITY_REACTIVE_IMPORTED_TARIFF_2', - obis.ELECTRICITY_REACTIVE_EXPORTED_TOTAL: 'ELECTRICITY_REACTIVE_EXPORTED_TOTAL', obis.ELECTRICITY_REACTIVE_EXPORTED_TARIFF_1: 'ELECTRICITY_REACTIVE_EXPORTED_TARIFF_1', obis.ELECTRICITY_REACTIVE_EXPORTED_TARIFF_2: 'ELECTRICITY_REACTIVE_EXPORTED_TARIFF_2', obis.CURRENT_REACTIVE_IMPORTED: 'CURRENT_REACTIVE_IMPORTED', diff --git a/dsmr_parser/objects.py b/dsmr_parser/objects.py index 008da34..c79e964 100644 --- a/dsmr_parser/objects.py +++ b/dsmr_parser/objects.py @@ -316,7 +316,7 @@ class MbusDevice: def add(self, obis_reference, dsmr_object): self._telegram_data[obis_reference] = dsmr_object - # Update name mapping used to get value by attribute. Example: telegram.P1_MESSAGE_HEADER + # Update name mapping used to get value by attribute. Example: device.HOURLY_GAS_METER_READING self._item_names.append(self._obis_name_mapping[obis_reference]) def __getattr__(self, name): diff --git a/test/test_telegram.py b/test/test_telegram.py index d65d5c1..2f00396 100644 --- a/test/test_telegram.py +++ b/test/test_telegram.py @@ -329,14 +329,12 @@ class TelegramTest(unittest.TestCase): def test_get_mbus_devices(self): parser = TelegramParser(telegram_specifications.V5) telegram = parser.parse(TELEGRAM_V5_TWO_MBUS) - mbus_devices = telegram.get_mbus_devices() self.assertEqual(len(mbus_devices), 2) mbus_device_1 = mbus_devices[0] self.assertEqual(mbus_device_1.EQUIPMENT_IDENTIFIER_GAS.value, None) - self.assertEqual(mbus_device_1.HOURLY_GAS_METER_READING.value, Decimal('0')) mbus_device_2 = mbus_devices[1]