From 761aaccb3f7a7551733174d5dfeecb7e6ca2f834 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Fri, 12 Nov 2021 15:19:43 +0000 Subject: [PATCH 01/16] adding EasyMeter Q3D support --- dsmr_parser/__main__.py | 40 +++++---- dsmr_parser/clients/protocol.py | 71 +++++++++------ dsmr_parser/obis_name_mapping.py | 97 +++++++++++---------- dsmr_parser/obis_references.py | 115 ++++++++++++++----------- dsmr_parser/telegram_specifications.py | 113 +++++++++++++++--------- 5 files changed, 255 insertions(+), 181 deletions(-) diff --git a/dsmr_parser/__main__.py b/dsmr_parser/__main__.py index a24a6a2..1a127cf 100644 --- a/dsmr_parser/__main__.py +++ b/dsmr_parser/__main__.py @@ -10,15 +10,20 @@ def console(): """Output DSMR data to console.""" parser = argparse.ArgumentParser(description=console.__doc__) - parser.add_argument('--device', default='/dev/ttyUSB0', - help='port to read DSMR data from') - parser.add_argument('--host', default=None, - help='alternatively connect using TCP host.') - parser.add_argument('--port', default=None, - help='TCP port to use for connection') - parser.add_argument('--version', default='2.2', choices=['2.2', '4', '5', '5B', '5L', '5S'], - help='DSMR version (2.2, 4, 5, 5B, 5L, 5S)') - parser.add_argument('--verbose', '-v', action='count') + parser.add_argument( + "--device", default="/dev/ttyUSB0", help="port to read DSMR data from" + ) + parser.add_argument( + "--host", default=None, help="alternatively connect using TCP host." + ) + parser.add_argument("--port", default=None, help="TCP port to use for connection") + parser.add_argument( + "--version", + default="2.2", + choices=["2.2", "4", "5", "5B", "5L", "5S", "Q3D"], + help="DSMR version (2.2, 4, 5, 5B, 5L, 5S, Q3D)", + ) + parser.add_argument("--verbose", "-v", action="count") args = parser.parse_args() @@ -39,13 +44,18 @@ def console(): # create tcp or serial connection depending on args if args.host and args.port: - create_connection = partial(create_tcp_dsmr_reader, - args.host, args.port, args.version, - print_callback, loop=loop) + create_connection = partial( + create_tcp_dsmr_reader, + args.host, + args.port, + args.version, + print_callback, + loop=loop, + ) else: - create_connection = partial(create_dsmr_reader, - args.device, args.version, - print_callback, loop=loop) + create_connection = partial( + create_dsmr_reader, args.device, args.version, print_callback, loop=loop + ) try: # connect and keep connected until interrupted by ctrl-c diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py index fce549d..560c0aa 100644 --- a/dsmr_parser/clients/protocol.py +++ b/dsmr_parser/clients/protocol.py @@ -10,23 +10,26 @@ from dsmr_parser import telegram_specifications from dsmr_parser.clients.telegram_buffer import TelegramBuffer from dsmr_parser.exceptions import ParseError, InvalidChecksumError from dsmr_parser.parsers import TelegramParser -from dsmr_parser.clients.settings import SERIAL_SETTINGS_V2_2, \ - SERIAL_SETTINGS_V4, SERIAL_SETTINGS_V5 +from dsmr_parser.clients.settings import ( + SERIAL_SETTINGS_V2_2, + SERIAL_SETTINGS_V4, + SERIAL_SETTINGS_V5, +) def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): """Creates a DSMR asyncio protocol.""" - if dsmr_version == '2.2': + if dsmr_version == "2.2": specification = telegram_specifications.V2_2 serial_settings = SERIAL_SETTINGS_V2_2 - elif dsmr_version == '4': + elif dsmr_version == "4": specification = telegram_specifications.V4 serial_settings = SERIAL_SETTINGS_V4 - elif dsmr_version == '5': + elif dsmr_version == "5": specification = telegram_specifications.V5 serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == '5B': + elif dsmr_version == "5B": specification = telegram_specifications.BELGIUM_FLUVIUS serial_settings = SERIAL_SETTINGS_V5 elif dsmr_version == "5L": @@ -35,12 +38,21 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): elif dsmr_version == "5S": specification = telegram_specifications.SWEDEN serial_settings = SERIAL_SETTINGS_V5 + elif dsmr_version == "Q3D": + specification = telegram_specifications.Q3D + serial_settings = SERIAL_SETTINGS_V5 else: - raise NotImplementedError("No telegram parser found for version: %s", - dsmr_version) + raise NotImplementedError( + "No telegram parser found for version: %s", dsmr_version + ) - protocol = partial(DSMRProtocol, loop, TelegramParser(specification), - telegram_callback=telegram_callback, **kwargs) + protocol = partial( + DSMRProtocol, + loop, + TelegramParser(specification), + telegram_callback=telegram_callback, + **kwargs + ) return protocol, serial_settings @@ -48,22 +60,26 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): def create_dsmr_reader(port, dsmr_version, telegram_callback, loop=None): """Creates a DSMR asyncio protocol coroutine using serial port.""" protocol, serial_settings = create_dsmr_protocol( - dsmr_version, telegram_callback, loop=None) - serial_settings['url'] = port + dsmr_version, telegram_callback, loop=None + ) + serial_settings["url"] = port conn = create_serial_connection(loop, protocol, **serial_settings) return conn -def create_tcp_dsmr_reader(host, port, dsmr_version, - telegram_callback, loop=None, - keep_alive_interval=None): +def create_tcp_dsmr_reader( + host, port, dsmr_version, telegram_callback, loop=None, keep_alive_interval=None +): """Creates a DSMR asyncio protocol coroutine using TCP connection.""" if not loop: loop = asyncio.get_event_loop() protocol, _ = create_dsmr_protocol( - dsmr_version, telegram_callback, loop=loop, - keep_alive_interval=keep_alive_interval) + dsmr_version, + telegram_callback, + loop=loop, + keep_alive_interval=keep_alive_interval, + ) conn = loop.create_connection(protocol, host, port) return conn @@ -74,8 +90,9 @@ class DSMRProtocol(asyncio.Protocol): transport = None telegram_callback = None - def __init__(self, loop, telegram_parser, - telegram_callback=None, keep_alive_interval=None): + def __init__( + self, loop, telegram_parser, telegram_callback=None, keep_alive_interval=None + ): """Initialize class.""" self.loop = loop self.log = logging.getLogger(__name__) @@ -92,16 +109,16 @@ class DSMRProtocol(asyncio.Protocol): def connection_made(self, transport): """Just logging for now.""" self.transport = transport - self.log.debug('connected') + self.log.debug("connected") self._active = False if self.loop and self._keep_alive_interval: self.loop.call_later(self._keep_alive_interval, self.keep_alive) def data_received(self, data): """Add incoming data to buffer.""" - data = data.decode('ascii') + data = data.decode("ascii") self._active = True - self.log.debug('received data: %s', data) + self.log.debug("received data: %s", data) self.telegram_buffer.append(data) for telegram in self.telegram_buffer.get_all(): @@ -109,26 +126,26 @@ class DSMRProtocol(asyncio.Protocol): def keep_alive(self): if self._active: - self.log.debug('keep-alive checked') + self.log.debug("keep-alive checked") self._active = False if self.loop: self.loop.call_later(self._keep_alive_interval, self.keep_alive) else: - self.log.warning('keep-alive check failed') + self.log.warning("keep-alive check failed") if self.transport: self.transport.close() def connection_lost(self, exc): """Stop when connection is lost.""" if exc: - self.log.exception('disconnected due to exception', exc_info=exc) + self.log.exception("disconnected due to exception", exc_info=exc) else: - self.log.info('disconnected because of close/abort.') + self.log.info("disconnected because of close/abort.") self._closed.set() def handle_telegram(self, telegram): """Send off parsed telegram to handling callback.""" - self.log.debug('got telegram: %s', telegram) + self.log.debug("got telegram: %s", telegram) try: parsed_telegram = self.telegram_parser.parse(telegram) diff --git a/dsmr_parser/obis_name_mapping.py b/dsmr_parser/obis_name_mapping.py index 4028890..f7aff77 100644 --- a/dsmr_parser/obis_name_mapping.py +++ b/dsmr_parser/obis_name_mapping.py @@ -8,53 +8,56 @@ This module contains a mapping of obis references to names. """ EN = { - obis.P1_MESSAGE_HEADER: 'P1_MESSAGE_HEADER', - obis.P1_MESSAGE_TIMESTAMP: 'P1_MESSAGE_TIMESTAMP', - obis.ELECTRICITY_IMPORTED_TOTAL: 'ELECTRICITY_IMPORTED_TOTAL', - obis.ELECTRICITY_USED_TARIFF_1: 'ELECTRICITY_USED_TARIFF_1', - obis.ELECTRICITY_USED_TARIFF_2: 'ELECTRICITY_USED_TARIFF_2', - obis.ELECTRICITY_DELIVERED_TARIFF_1: 'ELECTRICITY_DELIVERED_TARIFF_1', - obis.ELECTRICITY_DELIVERED_TARIFF_2: 'ELECTRICITY_DELIVERED_TARIFF_2', - obis.ELECTRICITY_ACTIVE_TARIFF: 'ELECTRICITY_ACTIVE_TARIFF', - obis.EQUIPMENT_IDENTIFIER: 'EQUIPMENT_IDENTIFIER', - obis.CURRENT_ELECTRICITY_USAGE: 'CURRENT_ELECTRICITY_USAGE', - obis.CURRENT_ELECTRICITY_DELIVERY: 'CURRENT_ELECTRICITY_DELIVERY', - obis.LONG_POWER_FAILURE_COUNT: 'LONG_POWER_FAILURE_COUNT', - obis.SHORT_POWER_FAILURE_COUNT: 'SHORT_POWER_FAILURE_COUNT', - obis.POWER_EVENT_FAILURE_LOG: 'POWER_EVENT_FAILURE_LOG', - obis.VOLTAGE_SAG_L1_COUNT: 'VOLTAGE_SAG_L1_COUNT', - obis.VOLTAGE_SAG_L2_COUNT: 'VOLTAGE_SAG_L2_COUNT', - obis.VOLTAGE_SAG_L3_COUNT: 'VOLTAGE_SAG_L3_COUNT', - obis.VOLTAGE_SWELL_L1_COUNT: 'VOLTAGE_SWELL_L1_COUNT', - obis.VOLTAGE_SWELL_L2_COUNT: 'VOLTAGE_SWELL_L2_COUNT', - obis.VOLTAGE_SWELL_L3_COUNT: 'VOLTAGE_SWELL_L3_COUNT', - obis.INSTANTANEOUS_VOLTAGE_L1: 'INSTANTANEOUS_VOLTAGE_L1', - obis.INSTANTANEOUS_VOLTAGE_L2: 'INSTANTANEOUS_VOLTAGE_L2', - obis.INSTANTANEOUS_VOLTAGE_L3: 'INSTANTANEOUS_VOLTAGE_L3', - obis.INSTANTANEOUS_CURRENT_L1: 'INSTANTANEOUS_CURRENT_L1', - obis.INSTANTANEOUS_CURRENT_L2: 'INSTANTANEOUS_CURRENT_L2', - obis.INSTANTANEOUS_CURRENT_L3: 'INSTANTANEOUS_CURRENT_L3', - obis.TEXT_MESSAGE_CODE: 'TEXT_MESSAGE_CODE', - obis.TEXT_MESSAGE: 'TEXT_MESSAGE', - obis.DEVICE_TYPE: 'DEVICE_TYPE', - obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE', - obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE', - obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE', - obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE', - obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE', - obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE', - obis.EQUIPMENT_IDENTIFIER_GAS: 'EQUIPMENT_IDENTIFIER_GAS', - obis.HOURLY_GAS_METER_READING: 'HOURLY_GAS_METER_READING', - obis.GAS_METER_READING: 'GAS_METER_READING', - obis.ACTUAL_TRESHOLD_ELECTRICITY: 'ACTUAL_TRESHOLD_ELECTRICITY', - obis.ACTUAL_SWITCH_POSITION: 'ACTUAL_SWITCH_POSITION', - obis.VALVE_POSITION_GAS: 'VALVE_POSITION_GAS', - obis.BELGIUM_HOURLY_GAS_METER_READING: 'BELGIUM_HOURLY_GAS_METER_READING', - obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: 'LUXEMBOURG_EQUIPMENT_IDENTIFIER', - obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL', - obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL', - obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: 'SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL', - obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL', + obis.P1_MESSAGE_HEADER: "P1_MESSAGE_HEADER", + obis.P1_MESSAGE_TIMESTAMP: "P1_MESSAGE_TIMESTAMP", + obis.ELECTRICITY_IMPORTED_TOTAL: "ELECTRICITY_IMPORTED_TOTAL", + obis.ELECTRICITY_USED_TARIFF_1: "ELECTRICITY_USED_TARIFF_1", + obis.ELECTRICITY_USED_TARIFF_2: "ELECTRICITY_USED_TARIFF_2", + obis.ELECTRICITY_DELIVERED_TARIFF_1: "ELECTRICITY_DELIVERED_TARIFF_1", + obis.ELECTRICITY_DELIVERED_TARIFF_2: "ELECTRICITY_DELIVERED_TARIFF_2", + obis.ELECTRICITY_ACTIVE_TARIFF: "ELECTRICITY_ACTIVE_TARIFF", + obis.EQUIPMENT_IDENTIFIER: "EQUIPMENT_IDENTIFIER", + obis.CURRENT_ELECTRICITY_USAGE: "CURRENT_ELECTRICITY_USAGE", + obis.CURRENT_ELECTRICITY_DELIVERY: "CURRENT_ELECTRICITY_DELIVERY", + obis.LONG_POWER_FAILURE_COUNT: "LONG_POWER_FAILURE_COUNT", + obis.SHORT_POWER_FAILURE_COUNT: "SHORT_POWER_FAILURE_COUNT", + obis.POWER_EVENT_FAILURE_LOG: "POWER_EVENT_FAILURE_LOG", + obis.VOLTAGE_SAG_L1_COUNT: "VOLTAGE_SAG_L1_COUNT", + obis.VOLTAGE_SAG_L2_COUNT: "VOLTAGE_SAG_L2_COUNT", + obis.VOLTAGE_SAG_L3_COUNT: "VOLTAGE_SAG_L3_COUNT", + obis.VOLTAGE_SWELL_L1_COUNT: "VOLTAGE_SWELL_L1_COUNT", + obis.VOLTAGE_SWELL_L2_COUNT: "VOLTAGE_SWELL_L2_COUNT", + obis.VOLTAGE_SWELL_L3_COUNT: "VOLTAGE_SWELL_L3_COUNT", + obis.INSTANTANEOUS_VOLTAGE_L1: "INSTANTANEOUS_VOLTAGE_L1", + obis.INSTANTANEOUS_VOLTAGE_L2: "INSTANTANEOUS_VOLTAGE_L2", + obis.INSTANTANEOUS_VOLTAGE_L3: "INSTANTANEOUS_VOLTAGE_L3", + obis.INSTANTANEOUS_CURRENT_L1: "INSTANTANEOUS_CURRENT_L1", + obis.INSTANTANEOUS_CURRENT_L2: "INSTANTANEOUS_CURRENT_L2", + obis.INSTANTANEOUS_CURRENT_L3: "INSTANTANEOUS_CURRENT_L3", + obis.TEXT_MESSAGE_CODE: "TEXT_MESSAGE_CODE", + obis.TEXT_MESSAGE: "TEXT_MESSAGE", + obis.DEVICE_TYPE: "DEVICE_TYPE", + obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE", + obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE", + obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE", + obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE", + obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE", + obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE", + obis.EQUIPMENT_IDENTIFIER_GAS: "EQUIPMENT_IDENTIFIER_GAS", + obis.HOURLY_GAS_METER_READING: "HOURLY_GAS_METER_READING", + obis.GAS_METER_READING: "GAS_METER_READING", + obis.ACTUAL_TRESHOLD_ELECTRICITY: "ACTUAL_TRESHOLD_ELECTRICITY", + obis.ACTUAL_SWITCH_POSITION: "ACTUAL_SWITCH_POSITION", + obis.VALVE_POSITION_GAS: "VALVE_POSITION_GAS", + obis.BELGIUM_HOURLY_GAS_METER_READING: "BELGIUM_HOURLY_GAS_METER_READING", + obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: "LUXEMBOURG_EQUIPMENT_IDENTIFIER", + obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: "LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL", + obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: "LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL", + obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: "SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL", + obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: "SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL", + obis.Q3D_EQUIPMENT_IDENTIFIER: "Q3D_EQUIPMENT_IDENTIFIER", + obis.Q3D_EQUIPMENT_STATE: "Q3D_EQUIPMENT_STATE", + obis.Q3D_EQUIPMENT_SERIALNUMBER: "Q3D_EQUIPMENT_SERIALNUMBER", } REVERSE_EN = dict([(v, k) for k, v in EN.items()]) diff --git a/dsmr_parser/obis_references.py b/dsmr_parser/obis_references.py index 5ac3b66..e355ead 100644 --- a/dsmr_parser/obis_references.py +++ b/dsmr_parser/obis_references.py @@ -6,65 +6,76 @@ refactored to full line signatures to maintain backwards compatibility. Might be refactored in a backwards incompatible way as soon as proper telegram objects are introduced. """ -P1_MESSAGE_HEADER = r'\d-\d:0\.2\.8.+?\r\n' -P1_MESSAGE_TIMESTAMP = r'\d-\d:1\.0\.0.+?\r\n' -ELECTRICITY_IMPORTED_TOTAL = r'\d-\d:1\.8\.0.+?\r\n' -ELECTRICITY_USED_TARIFF_1 = r'\d-\d:1\.8\.1.+?\r\n' -ELECTRICITY_USED_TARIFF_2 = r'\d-\d:1\.8\.2.+?\r\n' -ELECTRICITY_DELIVERED_TARIFF_1 = r'\d-\d:2\.8\.1.+?\r\n' -ELECTRICITY_DELIVERED_TARIFF_2 = r'\d-\d:2\.8\.2.+?\r\n' -ELECTRICITY_ACTIVE_TARIFF = r'\d-\d:96\.14\.0.+?\r\n' -EQUIPMENT_IDENTIFIER = r'\d-\d:96\.1\.1.+?\r\n' -CURRENT_ELECTRICITY_USAGE = r'\d-\d:1\.7\.0.+?\r\n' -CURRENT_ELECTRICITY_DELIVERY = r'\d-\d:2\.7\.0.+?\r\n' -LONG_POWER_FAILURE_COUNT = r'96\.7\.9.+?\r\n' -SHORT_POWER_FAILURE_COUNT = r'96\.7\.21.+?\r\n' -POWER_EVENT_FAILURE_LOG = r'99\.97\.0.+?\r\n' -VOLTAGE_SAG_L1_COUNT = r'\d-\d:32\.32\.0.+?\r\n' -VOLTAGE_SAG_L2_COUNT = r'\d-\d:52\.32\.0.+?\r\n' -VOLTAGE_SAG_L3_COUNT = r'\d-\d:72\.32\.0.+?\r\n' -VOLTAGE_SWELL_L1_COUNT = r'\d-\d:32\.36\.0.+?\r\n' -VOLTAGE_SWELL_L2_COUNT = r'\d-\d:52\.36\.0.+?\r\n' -VOLTAGE_SWELL_L3_COUNT = r'\d-\d:72\.36\.0.+?\r\n' -INSTANTANEOUS_VOLTAGE_L1 = r'\d-\d:32\.7\.0.+?\r\n' -INSTANTANEOUS_VOLTAGE_L2 = r'\d-\d:52\.7\.0.+?\r\n' -INSTANTANEOUS_VOLTAGE_L3 = r'\d-\d:72\.7\.0.+?\r\n' -INSTANTANEOUS_CURRENT_L1 = r'\d-\d:31\.7\.0.+?\r\n' -INSTANTANEOUS_CURRENT_L2 = r'\d-\d:51\.7\.0.+?\r\n' -INSTANTANEOUS_CURRENT_L3 = r'\d-\d:71\.7\.0.+?\r\n' -TEXT_MESSAGE_CODE = r'\d-\d:96\.13\.1.+?\r\n' -TEXT_MESSAGE = r'\d-\d:96\.13\.0.+?\r\n' -DEVICE_TYPE = r'\d-\d:24\.1\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE = r'\d-\d:21\.7\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE = r'\d-\d:41\.7\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE = r'\d-\d:61\.7\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE = r'\d-\d:22\.7\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE = r'\d-\d:42\.7\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE = r'\d-\d:62\.7\.0.+?\r\n' -EQUIPMENT_IDENTIFIER_GAS = r'\d-\d:96\.1\.0.+?\r\n' +P1_MESSAGE_HEADER = r"\d-\d:0\.2\.8.+?\r\n" +P1_MESSAGE_TIMESTAMP = r"\d-\d:1\.0\.0.+?\r\n" +ELECTRICITY_IMPORTED_TOTAL = r"\d-\d:1\.8\.0.+?\r\n" +ELECTRICITY_USED_TARIFF_1 = r"\d-\d:1\.8\.1.+?\r\n" +ELECTRICITY_USED_TARIFF_2 = r"\d-\d:1\.8\.2.+?\r\n" +ELECTRICITY_DELIVERED_TARIFF_1 = r"\d-\d:2\.8\.1.+?\r\n" +ELECTRICITY_DELIVERED_TARIFF_2 = r"\d-\d:2\.8\.2.+?\r\n" +ELECTRICITY_ACTIVE_TARIFF = r"\d-\d:96\.14\.0.+?\r\n" +EQUIPMENT_IDENTIFIER = r"\d-\d:96\.1\.1.+?\r\n" +CURRENT_ELECTRICITY_USAGE = r"\d-\d:1\.7\.0.+?\r\n" +CURRENT_ELECTRICITY_DELIVERY = r"\d-\d:2\.7\.0.+?\r\n" +LONG_POWER_FAILURE_COUNT = r"96\.7\.9.+?\r\n" +SHORT_POWER_FAILURE_COUNT = r"96\.7\.21.+?\r\n" +POWER_EVENT_FAILURE_LOG = r"99\.97\.0.+?\r\n" +VOLTAGE_SAG_L1_COUNT = r"\d-\d:32\.32\.0.+?\r\n" +VOLTAGE_SAG_L2_COUNT = r"\d-\d:52\.32\.0.+?\r\n" +VOLTAGE_SAG_L3_COUNT = r"\d-\d:72\.32\.0.+?\r\n" +VOLTAGE_SWELL_L1_COUNT = r"\d-\d:32\.36\.0.+?\r\n" +VOLTAGE_SWELL_L2_COUNT = r"\d-\d:52\.36\.0.+?\r\n" +VOLTAGE_SWELL_L3_COUNT = r"\d-\d:72\.36\.0.+?\r\n" +INSTANTANEOUS_VOLTAGE_L1 = r"\d-\d:32\.7\.0.+?\r\n" +INSTANTANEOUS_VOLTAGE_L2 = r"\d-\d:52\.7\.0.+?\r\n" +INSTANTANEOUS_VOLTAGE_L3 = r"\d-\d:72\.7\.0.+?\r\n" +INSTANTANEOUS_CURRENT_L1 = r"\d-\d:31\.7\.0.+?\r\n" +INSTANTANEOUS_CURRENT_L2 = r"\d-\d:51\.7\.0.+?\r\n" +INSTANTANEOUS_CURRENT_L3 = r"\d-\d:71\.7\.0.+?\r\n" +TEXT_MESSAGE_CODE = r"\d-\d:96\.13\.1.+?\r\n" +TEXT_MESSAGE = r"\d-\d:96\.13\.0.+?\r\n" +DEVICE_TYPE = r"\d-\d:24\.1\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE = r"\d-\d:21\.7\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE = r"\d-\d:41\.7\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE = r"\d-\d:61\.7\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE = r"\d-\d:22\.7\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE = r"\d-\d:42\.7\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE = r"\d-\d:62\.7\.0.+?\r\n" +EQUIPMENT_IDENTIFIER_GAS = r"\d-\d:96\.1\.0.+?\r\n" # TODO differences between gas meter readings in v3 and lower and v4 and up -HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.1.+?\r\n' -GAS_METER_READING = r'\d-\d:24\.3\.0.+?\r\n.+?\r\n' -ACTUAL_TRESHOLD_ELECTRICITY = r'\d-\d:17\.0\.0.+?\r\n' -ACTUAL_SWITCH_POSITION = r'\d-\d:96\.3\.10.+?\r\n' -VALVE_POSITION_GAS = r'\d-\d:24\.4\.0.+?\r\n' +HOURLY_GAS_METER_READING = r"\d-\d:24\.2\.1.+?\r\n" +GAS_METER_READING = r"\d-\d:24\.3\.0.+?\r\n.+?\r\n" +ACTUAL_TRESHOLD_ELECTRICITY = r"\d-\d:17\.0\.0.+?\r\n" +ACTUAL_SWITCH_POSITION = r"\d-\d:96\.3\.10.+?\r\n" +VALVE_POSITION_GAS = r"\d-\d:24\.4\.0.+?\r\n" # TODO 17.0.0 # TODO 96.3.10 -ELECTRICITY_USED_TARIFF_ALL = ( - ELECTRICITY_USED_TARIFF_1, - ELECTRICITY_USED_TARIFF_2 -) +ELECTRICITY_USED_TARIFF_ALL = (ELECTRICITY_USED_TARIFF_1, ELECTRICITY_USED_TARIFF_2) ELECTRICITY_DELIVERED_TARIFF_ALL = ( ELECTRICITY_DELIVERED_TARIFF_1, - ELECTRICITY_DELIVERED_TARIFF_2 + ELECTRICITY_DELIVERED_TARIFF_2, ) # Alternate codes for foreign countries. -BELGIUM_HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.3.+?\r\n' # Different code, same format. -LUXEMBOURG_EQUIPMENT_IDENTIFIER = r'\d-\d:42\.0\.0.+?\r\n' # Logical device name -LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+) -LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-) -SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+) -SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-) +BELGIUM_HOURLY_GAS_METER_READING = ( + r"\d-\d:24\.2\.3.+?\r\n" # Different code, same format. +) +LUXEMBOURG_EQUIPMENT_IDENTIFIER = r"\d-\d:42\.0\.0.+?\r\n" # Logical device name +LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = ( + r"\d-\d:1\.8\.0.+?\r\n" # Total imported energy register (P+) +) +LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = ( + r"\d-\d:2\.8\.0.+?\r\n" # Total exported energy register (P-) +) +SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL = ( + r"\d-\d:1\.8\.0.+?\r\n" # Total imported energy register (P+) +) +SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = ( + r"\d-\d:2\.8\.0.+?\r\n" # Total exported energy register (P-) +) + +Q3D_EQUIPMENT_IDENTIFIER = r"\d-\d:0\.0\.0.+?\r\n" # Logical device name +Q3D_EQUIPMENT_STATE = r"\d-\d:96\.5\.5.+?\r\n" # Device state (hexadecimal) +Q3D_EQUIPMENT_SERIALNUMBER = r"\d-\d:96\.1\.255.+?\r\n" # Device Serialnumber diff --git a/dsmr_parser/telegram_specifications.py b/dsmr_parser/telegram_specifications.py index 4e59f51..0e3ed61 100644 --- a/dsmr_parser/telegram_specifications.py +++ b/dsmr_parser/telegram_specifications.py @@ -2,9 +2,18 @@ from decimal import Decimal from copy import deepcopy from dsmr_parser import obis_references as obis -from dsmr_parser.parsers import CosemParser, ValueParser, MBusParser, ProfileGenericParser +from dsmr_parser.parsers import ( + CosemParser, + ValueParser, + MBusParser, + ProfileGenericParser, +) from dsmr_parser.value_types import timestamp -from dsmr_parser.profile_generic_specifications import BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS +from dsmr_parser.profile_generic_specifications import ( + BUFFER_TYPES, + PG_HEAD_PARSERS, + PG_UNIDENTIFIED_BUFFERTYPE_PARSERS, +) """ dsmr_parser.telegram_specifications @@ -15,8 +24,8 @@ how the telegram lines are parsed. """ V2_2 = { - 'checksum_support': False, - 'objects': { + "checksum_support": False, + "objects": { obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), obis.ELECTRICITY_USED_TARIFF_1: CosemParser(ValueParser(Decimal)), obis.ELECTRICITY_USED_TARIFF_2: CosemParser(ValueParser(Decimal)), @@ -41,14 +50,14 @@ V2_2 = { ValueParser(str), # unit, position 5 ValueParser(Decimal), # meter reading, position 6 ), - } + }, } V3 = V2_2 V4 = { - 'checksum_support': True, - 'objects': { + "checksum_support": True, + "objects": { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), @@ -61,10 +70,9 @@ V4 = { obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.SHORT_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), obis.LONG_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), - obis.POWER_EVENT_FAILURE_LOG: - ProfileGenericParser(BUFFER_TYPES, - PG_HEAD_PARSERS, - PG_UNIDENTIFIED_BUFFERTYPE_PARSERS), + obis.POWER_EVENT_FAILURE_LOG: ProfileGenericParser( + BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS + ), obis.VOLTAGE_SAG_L1_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L2_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L3_COUNT: CosemParser(ValueParser(int)), @@ -85,15 +93,14 @@ V4 = { obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: CosemParser(ValueParser(Decimal)), obis.EQUIPMENT_IDENTIFIER_GAS: CosemParser(ValueParser(str)), obis.HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), - ValueParser(Decimal) - ) - } + ValueParser(timestamp), ValueParser(Decimal) + ), + }, } V5 = { - 'checksum_support': True, - 'objects': { + "checksum_support": True, + "objects": { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), @@ -107,10 +114,9 @@ V5 = { obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.LONG_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), obis.SHORT_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), - obis.POWER_EVENT_FAILURE_LOG: - ProfileGenericParser(BUFFER_TYPES, - PG_HEAD_PARSERS, - PG_UNIDENTIFIED_BUFFERTYPE_PARSERS), + obis.POWER_EVENT_FAILURE_LOG: ProfileGenericParser( + BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS + ), obis.VOLTAGE_SAG_L1_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L2_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L3_COUNT: CosemParser(ValueParser(int)), @@ -133,38 +139,46 @@ V5 = { obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: CosemParser(ValueParser(Decimal)), obis.EQUIPMENT_IDENTIFIER_GAS: CosemParser(ValueParser(str)), obis.HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), - ValueParser(Decimal) - ) - } + ValueParser(timestamp), ValueParser(Decimal) + ), + }, } ALL = (V2_2, V3, V4, V5) BELGIUM_FLUVIUS = deepcopy(V5) -BELGIUM_FLUVIUS['objects'].update({ - obis.BELGIUM_HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), - ValueParser(Decimal) - ) -}) +BELGIUM_FLUVIUS["objects"].update( + { + obis.BELGIUM_HOURLY_GAS_METER_READING: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ) + } +) LUXEMBOURG_SMARTY = deepcopy(V5) -LUXEMBOURG_SMARTY['objects'].update({ - obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), - obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), - obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), -}) +LUXEMBOURG_SMARTY["objects"].update( + { + obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser( + ValueParser(Decimal) + ), + obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser( + ValueParser(Decimal) + ), + } +) # Source: https://www.energiforetagen.se/globalassets/energiforetagen/det-erbjuder-vi/kurser-och-konferenser/elnat/branschrekommendation-lokalt-granssnitt-v2_0-201912.pdf SWEDEN = { - 'checksum_support': True, - 'objects': { + "checksum_support": True, + "objects": { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), - obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), + obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser( + ValueParser(Decimal) + ), obis.CURRENT_ELECTRICITY_USAGE: CosemParser(ValueParser(Decimal)), obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: CosemParser(ValueParser(Decimal)), @@ -179,5 +193,24 @@ SWEDEN = { obis.INSTANTANEOUS_CURRENT_L1: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_CURRENT_L2: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_CURRENT_L3: CosemParser(ValueParser(Decimal)), - } + }, +} + +Q3D = { + "checksum_support": False, + "objects": { + obis.Q3D_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser( + ValueParser(Decimal) + ), + obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser( + ValueParser(Decimal) + ), + obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: CosemParser(ValueParser(Decimal)), + obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: CosemParser(ValueParser(Decimal)), + obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: CosemParser(ValueParser(Decimal)), + obis.CURRENT_ELECTRICITY_USAGE: CosemParser(ValueParser(Decimal)), + obis.Q3D_EQUIPMENT_STATE: CosemParser(ValueParser(str)), + obis.Q3D_EQUIPMENT_SERIALNUMBER: CosemParser(ValueParser(str)), + }, } From 007b3ea0895ecd91441fb918d48290cee94d1949 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Fri, 12 Nov 2021 18:13:48 +0100 Subject: [PATCH 02/16] ignoring trailing \xff in byte stream --- dsmr_parser/clients/protocol.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py index 560c0aa..56ed26d 100644 --- a/dsmr_parser/clients/protocol.py +++ b/dsmr_parser/clients/protocol.py @@ -116,7 +116,7 @@ class DSMRProtocol(asyncio.Protocol): def data_received(self, data): """Add incoming data to buffer.""" - data = data.decode("ascii") + data = data.decode("ascii", errors="ignore") self._active = True self.log.debug("received data: %s", data) self.telegram_buffer.append(data) diff --git a/setup.py b/setup.py index 7ad7c68..3852064 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup( author_email='nigel@nldr.net', license='MIT', url='https://github.com/ndokter/dsmr_parser', - version='0.30', + version='0.31', packages=find_packages(exclude=('test', 'test.*')), install_requires=[ 'pyserial>=3,<4', From 8b8b952ce15447cde6e2d0e18c53b37500de845a Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Fri, 12 Nov 2021 17:38:16 +0000 Subject: [PATCH 03/16] clean-up re-format --- dsmr_parser/__main__.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/dsmr_parser/__main__.py b/dsmr_parser/__main__.py index 1a127cf..24ed65d 100644 --- a/dsmr_parser/__main__.py +++ b/dsmr_parser/__main__.py @@ -10,20 +10,15 @@ def console(): """Output DSMR data to console.""" parser = argparse.ArgumentParser(description=console.__doc__) - parser.add_argument( - "--device", default="/dev/ttyUSB0", help="port to read DSMR data from" - ) - parser.add_argument( - "--host", default=None, help="alternatively connect using TCP host." - ) - parser.add_argument("--port", default=None, help="TCP port to use for connection") - parser.add_argument( - "--version", - default="2.2", - choices=["2.2", "4", "5", "5B", "5L", "5S", "Q3D"], - help="DSMR version (2.2, 4, 5, 5B, 5L, 5S, Q3D)", - ) - parser.add_argument("--verbose", "-v", action="count") + parser.add_argument('--device', default='/dev/ttyUSB0', + help='port to read DSMR data from') + parser.add_argument('--host', default=None, + help='alternatively connect using TCP host.') + parser.add_argument('--port', default=None, + help='TCP port to use for connection') + parser.add_argument('--version', default='2.2', choices=['2.2', '4', '5', '5B', '5L', '5S', 'Q3D'], + help='DSMR version (2.2, 4, 5, 5B, 5L, 5S, Q3D)') + parser.add_argument('--verbose', '-v', action='count') args = parser.parse_args() From e4f384c2b76615b68f32f6d6df24017b0a383fcf Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Fri, 12 Nov 2021 17:48:21 +0000 Subject: [PATCH 04/16] clean-up re-format --- dsmr_parser/__main__.py | 17 +++----- dsmr_parser/clients/protocol.py | 74 +++++++++++++-------------------- 2 files changed, 36 insertions(+), 55 deletions(-) diff --git a/dsmr_parser/__main__.py b/dsmr_parser/__main__.py index 24ed65d..9169318 100644 --- a/dsmr_parser/__main__.py +++ b/dsmr_parser/__main__.py @@ -39,18 +39,13 @@ def console(): # create tcp or serial connection depending on args if args.host and args.port: - create_connection = partial( - create_tcp_dsmr_reader, - args.host, - args.port, - args.version, - print_callback, - loop=loop, - ) + create_connection = partial(create_tcp_dsmr_reader, + args.host, args.port, args.version, + print_callback, loop=loop) else: - create_connection = partial( - create_dsmr_reader, args.device, args.version, print_callback, loop=loop - ) + create_connection = partial(create_dsmr_reader, + args.device, args.version, + print_callback, loop=loop) try: # connect and keep connected until interrupted by ctrl-c diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py index 56ed26d..6a7a17e 100644 --- a/dsmr_parser/clients/protocol.py +++ b/dsmr_parser/clients/protocol.py @@ -10,49 +10,40 @@ from dsmr_parser import telegram_specifications from dsmr_parser.clients.telegram_buffer import TelegramBuffer from dsmr_parser.exceptions import ParseError, InvalidChecksumError from dsmr_parser.parsers import TelegramParser -from dsmr_parser.clients.settings import ( - SERIAL_SETTINGS_V2_2, - SERIAL_SETTINGS_V4, - SERIAL_SETTINGS_V5, -) +from dsmr_parser.clients.settings import SERIAL_SETTINGS_V2_2, \ + SERIAL_SETTINGS_V4, SERIAL_SETTINGS_V5 def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): """Creates a DSMR asyncio protocol.""" - if dsmr_version == "2.2": + if dsmr_version == '2.2': specification = telegram_specifications.V2_2 serial_settings = SERIAL_SETTINGS_V2_2 - elif dsmr_version == "4": + elif dsmr_version == '4': specification = telegram_specifications.V4 serial_settings = SERIAL_SETTINGS_V4 - elif dsmr_version == "5": + elif dsmr_version == '5': specification = telegram_specifications.V5 serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == "5B": + elif dsmr_version == '5B': specification = telegram_specifications.BELGIUM_FLUVIUS serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == "5L": + elif dsmr_version == '5L': specification = telegram_specifications.LUXEMBOURG_SMARTY serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == "5S": + elif dsmr_version == '5S': specification = telegram_specifications.SWEDEN serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == "Q3D": + elif dsmr_version == 'Q3D': specification = telegram_specifications.Q3D serial_settings = SERIAL_SETTINGS_V5 else: - raise NotImplementedError( - "No telegram parser found for version: %s", dsmr_version - ) + raise NotImplementedError("No telegram parser found for version: %s", + dsmr_version) - protocol = partial( - DSMRProtocol, - loop, - TelegramParser(specification), - telegram_callback=telegram_callback, - **kwargs - ) + protocol = partial(DSMRProtocol, loop, TelegramParser(specification), + telegram_callback=telegram_callback, **kwargs) return protocol, serial_settings @@ -60,26 +51,22 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): def create_dsmr_reader(port, dsmr_version, telegram_callback, loop=None): """Creates a DSMR asyncio protocol coroutine using serial port.""" protocol, serial_settings = create_dsmr_protocol( - dsmr_version, telegram_callback, loop=None - ) - serial_settings["url"] = port + dsmr_version, telegram_callback, loop=None) + serial_settings['url'] = port conn = create_serial_connection(loop, protocol, **serial_settings) return conn -def create_tcp_dsmr_reader( - host, port, dsmr_version, telegram_callback, loop=None, keep_alive_interval=None -): +def create_tcp_dsmr_reader(host, port, dsmr_version, + telegram_callback, loop=None, + keep_alive_interval=None): """Creates a DSMR asyncio protocol coroutine using TCP connection.""" if not loop: loop = asyncio.get_event_loop() protocol, _ = create_dsmr_protocol( - dsmr_version, - telegram_callback, - loop=loop, - keep_alive_interval=keep_alive_interval, - ) + dsmr_version, telegram_callback, loop=loop, + keep_alive_interval=keep_alive_interval) conn = loop.create_connection(protocol, host, port) return conn @@ -90,9 +77,8 @@ class DSMRProtocol(asyncio.Protocol): transport = None telegram_callback = None - def __init__( - self, loop, telegram_parser, telegram_callback=None, keep_alive_interval=None - ): + def __init__(self, loop, telegram_parser, + telegram_callback=None, keep_alive_interval=None): """Initialize class.""" self.loop = loop self.log = logging.getLogger(__name__) @@ -109,16 +95,16 @@ class DSMRProtocol(asyncio.Protocol): def connection_made(self, transport): """Just logging for now.""" self.transport = transport - self.log.debug("connected") + self.log.debug('connected') self._active = False if self.loop and self._keep_alive_interval: self.loop.call_later(self._keep_alive_interval, self.keep_alive) def data_received(self, data): """Add incoming data to buffer.""" - data = data.decode("ascii", errors="ignore") + data = data.decode('ascii', errors='ignore') self._active = True - self.log.debug("received data: %s", data) + self.log.debug('received data: %s', data) self.telegram_buffer.append(data) for telegram in self.telegram_buffer.get_all(): @@ -126,26 +112,26 @@ class DSMRProtocol(asyncio.Protocol): def keep_alive(self): if self._active: - self.log.debug("keep-alive checked") + self.log.debug('keep-alive checked') self._active = False if self.loop: self.loop.call_later(self._keep_alive_interval, self.keep_alive) else: - self.log.warning("keep-alive check failed") + self.log.warning('keep-alive check failed') if self.transport: self.transport.close() def connection_lost(self, exc): """Stop when connection is lost.""" if exc: - self.log.exception("disconnected due to exception", exc_info=exc) + self.log.exception('disconnected due to exception', exc_info=exc) else: - self.log.info("disconnected because of close/abort.") + self.log.info('disconnected because of close/abort.') self._closed.set() def handle_telegram(self, telegram): """Send off parsed telegram to handling callback.""" - self.log.debug("got telegram: %s", telegram) + self.log.debug('got telegram: %s', telegram) try: parsed_telegram = self.telegram_parser.parse(telegram) From 17b56ae07bff4080c27524872046cf377f8f9ccb Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Fri, 12 Nov 2021 17:51:24 +0000 Subject: [PATCH 05/16] clean-up re-format --- dsmr_parser/obis_name_mapping.py | 100 +++++++++++++------------- dsmr_parser/obis_references.py | 118 ++++++++++++++----------------- 2 files changed, 105 insertions(+), 113 deletions(-) diff --git a/dsmr_parser/obis_name_mapping.py b/dsmr_parser/obis_name_mapping.py index f7aff77..fcba570 100644 --- a/dsmr_parser/obis_name_mapping.py +++ b/dsmr_parser/obis_name_mapping.py @@ -8,56 +8,56 @@ This module contains a mapping of obis references to names. """ EN = { - obis.P1_MESSAGE_HEADER: "P1_MESSAGE_HEADER", - obis.P1_MESSAGE_TIMESTAMP: "P1_MESSAGE_TIMESTAMP", - obis.ELECTRICITY_IMPORTED_TOTAL: "ELECTRICITY_IMPORTED_TOTAL", - obis.ELECTRICITY_USED_TARIFF_1: "ELECTRICITY_USED_TARIFF_1", - obis.ELECTRICITY_USED_TARIFF_2: "ELECTRICITY_USED_TARIFF_2", - obis.ELECTRICITY_DELIVERED_TARIFF_1: "ELECTRICITY_DELIVERED_TARIFF_1", - obis.ELECTRICITY_DELIVERED_TARIFF_2: "ELECTRICITY_DELIVERED_TARIFF_2", - obis.ELECTRICITY_ACTIVE_TARIFF: "ELECTRICITY_ACTIVE_TARIFF", - obis.EQUIPMENT_IDENTIFIER: "EQUIPMENT_IDENTIFIER", - obis.CURRENT_ELECTRICITY_USAGE: "CURRENT_ELECTRICITY_USAGE", - obis.CURRENT_ELECTRICITY_DELIVERY: "CURRENT_ELECTRICITY_DELIVERY", - obis.LONG_POWER_FAILURE_COUNT: "LONG_POWER_FAILURE_COUNT", - obis.SHORT_POWER_FAILURE_COUNT: "SHORT_POWER_FAILURE_COUNT", - obis.POWER_EVENT_FAILURE_LOG: "POWER_EVENT_FAILURE_LOG", - obis.VOLTAGE_SAG_L1_COUNT: "VOLTAGE_SAG_L1_COUNT", - obis.VOLTAGE_SAG_L2_COUNT: "VOLTAGE_SAG_L2_COUNT", - obis.VOLTAGE_SAG_L3_COUNT: "VOLTAGE_SAG_L3_COUNT", - obis.VOLTAGE_SWELL_L1_COUNT: "VOLTAGE_SWELL_L1_COUNT", - obis.VOLTAGE_SWELL_L2_COUNT: "VOLTAGE_SWELL_L2_COUNT", - obis.VOLTAGE_SWELL_L3_COUNT: "VOLTAGE_SWELL_L3_COUNT", - obis.INSTANTANEOUS_VOLTAGE_L1: "INSTANTANEOUS_VOLTAGE_L1", - obis.INSTANTANEOUS_VOLTAGE_L2: "INSTANTANEOUS_VOLTAGE_L2", - obis.INSTANTANEOUS_VOLTAGE_L3: "INSTANTANEOUS_VOLTAGE_L3", - obis.INSTANTANEOUS_CURRENT_L1: "INSTANTANEOUS_CURRENT_L1", - obis.INSTANTANEOUS_CURRENT_L2: "INSTANTANEOUS_CURRENT_L2", - obis.INSTANTANEOUS_CURRENT_L3: "INSTANTANEOUS_CURRENT_L3", - obis.TEXT_MESSAGE_CODE: "TEXT_MESSAGE_CODE", - obis.TEXT_MESSAGE: "TEXT_MESSAGE", - obis.DEVICE_TYPE: "DEVICE_TYPE", - obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE", - obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE", - obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE", - obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE", - obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE", - obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE", - obis.EQUIPMENT_IDENTIFIER_GAS: "EQUIPMENT_IDENTIFIER_GAS", - obis.HOURLY_GAS_METER_READING: "HOURLY_GAS_METER_READING", - obis.GAS_METER_READING: "GAS_METER_READING", - obis.ACTUAL_TRESHOLD_ELECTRICITY: "ACTUAL_TRESHOLD_ELECTRICITY", - obis.ACTUAL_SWITCH_POSITION: "ACTUAL_SWITCH_POSITION", - obis.VALVE_POSITION_GAS: "VALVE_POSITION_GAS", - obis.BELGIUM_HOURLY_GAS_METER_READING: "BELGIUM_HOURLY_GAS_METER_READING", - obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: "LUXEMBOURG_EQUIPMENT_IDENTIFIER", - obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: "LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL", - obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: "LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL", - obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: "SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL", - obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: "SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL", - obis.Q3D_EQUIPMENT_IDENTIFIER: "Q3D_EQUIPMENT_IDENTIFIER", - obis.Q3D_EQUIPMENT_STATE: "Q3D_EQUIPMENT_STATE", - obis.Q3D_EQUIPMENT_SERIALNUMBER: "Q3D_EQUIPMENT_SERIALNUMBER", + obis.P1_MESSAGE_HEADER: 'P1_MESSAGE_HEADER', + obis.P1_MESSAGE_TIMESTAMP: 'P1_MESSAGE_TIMESTAMP', + obis.ELECTRICITY_IMPORTED_TOTAL: 'ELECTRICITY_IMPORTED_TOTAL', + obis.ELECTRICITY_USED_TARIFF_1: 'ELECTRICITY_USED_TARIFF_1', + obis.ELECTRICITY_USED_TARIFF_2: 'ELECTRICITY_USED_TARIFF_2', + obis.ELECTRICITY_DELIVERED_TARIFF_1: 'ELECTRICITY_DELIVERED_TARIFF_1', + obis.ELECTRICITY_DELIVERED_TARIFF_2: 'ELECTRICITY_DELIVERED_TARIFF_2', + obis.ELECTRICITY_ACTIVE_TARIFF: 'ELECTRICITY_ACTIVE_TARIFF', + obis.EQUIPMENT_IDENTIFIER: 'EQUIPMENT_IDENTIFIER', + obis.CURRENT_ELECTRICITY_USAGE: 'CURRENT_ELECTRICITY_USAGE', + obis.CURRENT_ELECTRICITY_DELIVERY: 'CURRENT_ELECTRICITY_DELIVERY', + obis.LONG_POWER_FAILURE_COUNT: 'LONG_POWER_FAILURE_COUNT', + obis.SHORT_POWER_FAILURE_COUNT: 'SHORT_POWER_FAILURE_COUNT', + obis.POWER_EVENT_FAILURE_LOG: 'POWER_EVENT_FAILURE_LOG', + obis.VOLTAGE_SAG_L1_COUNT: 'VOLTAGE_SAG_L1_COUNT', + obis.VOLTAGE_SAG_L2_COUNT: 'VOLTAGE_SAG_L2_COUNT', + obis.VOLTAGE_SAG_L3_COUNT: 'VOLTAGE_SAG_L3_COUNT', + obis.VOLTAGE_SWELL_L1_COUNT: 'VOLTAGE_SWELL_L1_COUNT', + obis.VOLTAGE_SWELL_L2_COUNT: 'VOLTAGE_SWELL_L2_COUNT', + obis.VOLTAGE_SWELL_L3_COUNT: 'VOLTAGE_SWELL_L3_COUNT', + obis.INSTANTANEOUS_VOLTAGE_L1: 'INSTANTANEOUS_VOLTAGE_L1', + obis.INSTANTANEOUS_VOLTAGE_L2: 'INSTANTANEOUS_VOLTAGE_L2', + obis.INSTANTANEOUS_VOLTAGE_L3: 'INSTANTANEOUS_VOLTAGE_L3', + obis.INSTANTANEOUS_CURRENT_L1: 'INSTANTANEOUS_CURRENT_L1', + obis.INSTANTANEOUS_CURRENT_L2: 'INSTANTANEOUS_CURRENT_L2', + obis.INSTANTANEOUS_CURRENT_L3: 'INSTANTANEOUS_CURRENT_L3', + obis.TEXT_MESSAGE_CODE: 'TEXT_MESSAGE_CODE', + obis.TEXT_MESSAGE: 'TEXT_MESSAGE', + obis.DEVICE_TYPE: 'DEVICE_TYPE', + obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE', + obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE', + obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE', + obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE', + obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE', + obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE', + obis.EQUIPMENT_IDENTIFIER_GAS: 'EQUIPMENT_IDENTIFIER_GAS', + obis.HOURLY_GAS_METER_READING: 'HOURLY_GAS_METER_READING', + obis.GAS_METER_READING: 'GAS_METER_READING', + obis.ACTUAL_TRESHOLD_ELECTRICITY: 'ACTUAL_TRESHOLD_ELECTRICITY', + obis.ACTUAL_SWITCH_POSITION: 'ACTUAL_SWITCH_POSITION', + obis.VALVE_POSITION_GAS: 'VALVE_POSITION_GAS', + obis.BELGIUM_HOURLY_GAS_METER_READING: 'BELGIUM_HOURLY_GAS_METER_READING', + obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: 'LUXEMBOURG_EQUIPMENT_IDENTIFIER', + obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL', + obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL', + obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: 'SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL', + obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL', + obis.Q3D_EQUIPMENT_IDENTIFIER: 'Q3D_EQUIPMENT_IDENTIFIER', + obis.Q3D_EQUIPMENT_STATE: 'Q3D_EQUIPMENT_STATE', + obis.Q3D_EQUIPMENT_SERIALNUMBER: 'Q3D_EQUIPMENT_SERIALNUMBER', } REVERSE_EN = dict([(v, k) for k, v in EN.items()]) diff --git a/dsmr_parser/obis_references.py b/dsmr_parser/obis_references.py index e355ead..3808848 100644 --- a/dsmr_parser/obis_references.py +++ b/dsmr_parser/obis_references.py @@ -6,76 +6,68 @@ refactored to full line signatures to maintain backwards compatibility. Might be refactored in a backwards incompatible way as soon as proper telegram objects are introduced. """ -P1_MESSAGE_HEADER = r"\d-\d:0\.2\.8.+?\r\n" -P1_MESSAGE_TIMESTAMP = r"\d-\d:1\.0\.0.+?\r\n" -ELECTRICITY_IMPORTED_TOTAL = r"\d-\d:1\.8\.0.+?\r\n" -ELECTRICITY_USED_TARIFF_1 = r"\d-\d:1\.8\.1.+?\r\n" -ELECTRICITY_USED_TARIFF_2 = r"\d-\d:1\.8\.2.+?\r\n" -ELECTRICITY_DELIVERED_TARIFF_1 = r"\d-\d:2\.8\.1.+?\r\n" -ELECTRICITY_DELIVERED_TARIFF_2 = r"\d-\d:2\.8\.2.+?\r\n" -ELECTRICITY_ACTIVE_TARIFF = r"\d-\d:96\.14\.0.+?\r\n" -EQUIPMENT_IDENTIFIER = r"\d-\d:96\.1\.1.+?\r\n" -CURRENT_ELECTRICITY_USAGE = r"\d-\d:1\.7\.0.+?\r\n" -CURRENT_ELECTRICITY_DELIVERY = r"\d-\d:2\.7\.0.+?\r\n" -LONG_POWER_FAILURE_COUNT = r"96\.7\.9.+?\r\n" -SHORT_POWER_FAILURE_COUNT = r"96\.7\.21.+?\r\n" -POWER_EVENT_FAILURE_LOG = r"99\.97\.0.+?\r\n" -VOLTAGE_SAG_L1_COUNT = r"\d-\d:32\.32\.0.+?\r\n" -VOLTAGE_SAG_L2_COUNT = r"\d-\d:52\.32\.0.+?\r\n" -VOLTAGE_SAG_L3_COUNT = r"\d-\d:72\.32\.0.+?\r\n" -VOLTAGE_SWELL_L1_COUNT = r"\d-\d:32\.36\.0.+?\r\n" -VOLTAGE_SWELL_L2_COUNT = r"\d-\d:52\.36\.0.+?\r\n" -VOLTAGE_SWELL_L3_COUNT = r"\d-\d:72\.36\.0.+?\r\n" -INSTANTANEOUS_VOLTAGE_L1 = r"\d-\d:32\.7\.0.+?\r\n" -INSTANTANEOUS_VOLTAGE_L2 = r"\d-\d:52\.7\.0.+?\r\n" -INSTANTANEOUS_VOLTAGE_L3 = r"\d-\d:72\.7\.0.+?\r\n" -INSTANTANEOUS_CURRENT_L1 = r"\d-\d:31\.7\.0.+?\r\n" -INSTANTANEOUS_CURRENT_L2 = r"\d-\d:51\.7\.0.+?\r\n" -INSTANTANEOUS_CURRENT_L3 = r"\d-\d:71\.7\.0.+?\r\n" -TEXT_MESSAGE_CODE = r"\d-\d:96\.13\.1.+?\r\n" -TEXT_MESSAGE = r"\d-\d:96\.13\.0.+?\r\n" -DEVICE_TYPE = r"\d-\d:24\.1\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE = r"\d-\d:21\.7\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE = r"\d-\d:41\.7\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE = r"\d-\d:61\.7\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE = r"\d-\d:22\.7\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE = r"\d-\d:42\.7\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE = r"\d-\d:62\.7\.0.+?\r\n" -EQUIPMENT_IDENTIFIER_GAS = r"\d-\d:96\.1\.0.+?\r\n" +P1_MESSAGE_HEADER = r'\d-\d:0\.2\.8.+?\r\n' +P1_MESSAGE_TIMESTAMP = r'\d-\d:1\.0\.0.+?\r\n' +ELECTRICITY_IMPORTED_TOTAL = r'\d-\d:1\.8\.0.+?\r\n' +ELECTRICITY_USED_TARIFF_1 = r'\d-\d:1\.8\.1.+?\r\n' +ELECTRICITY_USED_TARIFF_2 = r'\d-\d:1\.8\.2.+?\r\n' +ELECTRICITY_DELIVERED_TARIFF_1 = r'\d-\d:2\.8\.1.+?\r\n' +ELECTRICITY_DELIVERED_TARIFF_2 = r'\d-\d:2\.8\.2.+?\r\n' +ELECTRICITY_ACTIVE_TARIFF = r'\d-\d:96\.14\.0.+?\r\n' +EQUIPMENT_IDENTIFIER = r'\d-\d:96\.1\.1.+?\r\n' +CURRENT_ELECTRICITY_USAGE = r'\d-\d:1\.7\.0.+?\r\n' +CURRENT_ELECTRICITY_DELIVERY = r'\d-\d:2\.7\.0.+?\r\n' +LONG_POWER_FAILURE_COUNT = r'96\.7\.9.+?\r\n' +SHORT_POWER_FAILURE_COUNT = r'96\.7\.21.+?\r\n' +POWER_EVENT_FAILURE_LOG = r'99\.97\.0.+?\r\n' +VOLTAGE_SAG_L1_COUNT = r'\d-\d:32\.32\.0.+?\r\n' +VOLTAGE_SAG_L2_COUNT = r'\d-\d:52\.32\.0.+?\r\n' +VOLTAGE_SAG_L3_COUNT = r'\d-\d:72\.32\.0.+?\r\n' +VOLTAGE_SWELL_L1_COUNT = r'\d-\d:32\.36\.0.+?\r\n' +VOLTAGE_SWELL_L2_COUNT = r'\d-\d:52\.36\.0.+?\r\n' +VOLTAGE_SWELL_L3_COUNT = r'\d-\d:72\.36\.0.+?\r\n' +INSTANTANEOUS_VOLTAGE_L1 = r'\d-\d:32\.7\.0.+?\r\n' +INSTANTANEOUS_VOLTAGE_L2 = r'\d-\d:52\.7\.0.+?\r\n' +INSTANTANEOUS_VOLTAGE_L3 = r'\d-\d:72\.7\.0.+?\r\n' +INSTANTANEOUS_CURRENT_L1 = r'\d-\d:31\.7\.0.+?\r\n' +INSTANTANEOUS_CURRENT_L2 = r'\d-\d:51\.7\.0.+?\r\n' +INSTANTANEOUS_CURRENT_L3 = r'\d-\d:71\.7\.0.+?\r\n' +TEXT_MESSAGE_CODE = r'\d-\d:96\.13\.1.+?\r\n' +TEXT_MESSAGE = r'\d-\d:96\.13\.0.+?\r\n' +DEVICE_TYPE = r'\d-\d:24\.1\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE = r'\d-\d:21\.7\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE = r'\d-\d:41\.7\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE = r'\d-\d:61\.7\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE = r'\d-\d:22\.7\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE = r'\d-\d:42\.7\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE = r'\d-\d:62\.7\.0.+?\r\n' +EQUIPMENT_IDENTIFIER_GAS = r'\d-\d:96\.1\.0.+?\r\n' # TODO differences between gas meter readings in v3 and lower and v4 and up -HOURLY_GAS_METER_READING = r"\d-\d:24\.2\.1.+?\r\n" -GAS_METER_READING = r"\d-\d:24\.3\.0.+?\r\n.+?\r\n" -ACTUAL_TRESHOLD_ELECTRICITY = r"\d-\d:17\.0\.0.+?\r\n" -ACTUAL_SWITCH_POSITION = r"\d-\d:96\.3\.10.+?\r\n" -VALVE_POSITION_GAS = r"\d-\d:24\.4\.0.+?\r\n" +HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.1.+?\r\n' +GAS_METER_READING = r'\d-\d:24\.3\.0.+?\r\n.+?\r\n' +ACTUAL_TRESHOLD_ELECTRICITY = r'\d-\d:17\.0\.0.+?\r\n' +ACTUAL_SWITCH_POSITION = r'\d-\d:96\.3\.10.+?\r\n' +VALVE_POSITION_GAS = r'\d-\d:24\.4\.0.+?\r\n' # TODO 17.0.0 # TODO 96.3.10 -ELECTRICITY_USED_TARIFF_ALL = (ELECTRICITY_USED_TARIFF_1, ELECTRICITY_USED_TARIFF_2) +ELECTRICITY_USED_TARIFF_ALL = ( + ELECTRICITY_USED_TARIFF_1, + ELECTRICITY_USED_TARIFF_2 +) ELECTRICITY_DELIVERED_TARIFF_ALL = ( ELECTRICITY_DELIVERED_TARIFF_1, - ELECTRICITY_DELIVERED_TARIFF_2, + ELECTRICITY_DELIVERED_TARIFF_2 ) # Alternate codes for foreign countries. -BELGIUM_HOURLY_GAS_METER_READING = ( - r"\d-\d:24\.2\.3.+?\r\n" # Different code, same format. -) -LUXEMBOURG_EQUIPMENT_IDENTIFIER = r"\d-\d:42\.0\.0.+?\r\n" # Logical device name -LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = ( - r"\d-\d:1\.8\.0.+?\r\n" # Total imported energy register (P+) -) -LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = ( - r"\d-\d:2\.8\.0.+?\r\n" # Total exported energy register (P-) -) -SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL = ( - r"\d-\d:1\.8\.0.+?\r\n" # Total imported energy register (P+) -) -SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = ( - r"\d-\d:2\.8\.0.+?\r\n" # Total exported energy register (P-) -) - -Q3D_EQUIPMENT_IDENTIFIER = r"\d-\d:0\.0\.0.+?\r\n" # Logical device name -Q3D_EQUIPMENT_STATE = r"\d-\d:96\.5\.5.+?\r\n" # Device state (hexadecimal) -Q3D_EQUIPMENT_SERIALNUMBER = r"\d-\d:96\.1\.255.+?\r\n" # Device Serialnumber +BELGIUM_HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.3.+?\r\n' # Different code, same format. +LUXEMBOURG_EQUIPMENT_IDENTIFIER = r'\d-\d:42\.0\.0.+?\r\n' # Logical device name +LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+) +LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-) +SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+) +SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-) +Q3D_EQUIPMENT_IDENTIFIER = r'\d-\d:0\.0\.0.+?\r\n' # Logical device name +Q3D_EQUIPMENT_STATE = r'\d-\d:96\.5\.5.+?\r\n' # Device state (hexadecimal) +Q3D_EQUIPMENT_SERIALNUMBER = r'\d-\d:96\.1\.255.+?\r\n' # Device Serialnumber From 2b724b87c3a18f9edda64b1ea5b79a1eef80bfd1 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Fri, 12 Nov 2021 19:21:32 +0000 Subject: [PATCH 06/16] clean-up re-format --- dsmr_parser/telegram_specifications.py | 104 ++++++++++--------------- 1 file changed, 43 insertions(+), 61 deletions(-) diff --git a/dsmr_parser/telegram_specifications.py b/dsmr_parser/telegram_specifications.py index 0e3ed61..bb82868 100644 --- a/dsmr_parser/telegram_specifications.py +++ b/dsmr_parser/telegram_specifications.py @@ -2,30 +2,20 @@ from decimal import Decimal from copy import deepcopy from dsmr_parser import obis_references as obis -from dsmr_parser.parsers import ( - CosemParser, - ValueParser, - MBusParser, - ProfileGenericParser, -) +from dsmr_parser.parsers import CosemParser, ValueParser, MBusParser, ProfileGenericParser from dsmr_parser.value_types import timestamp -from dsmr_parser.profile_generic_specifications import ( - BUFFER_TYPES, - PG_HEAD_PARSERS, - PG_UNIDENTIFIED_BUFFERTYPE_PARSERS, -) +from dsmr_parser.profile_generic_specifications import BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS """ dsmr_parser.telegram_specifications ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - This module contains DSMR telegram specifications. Each specifications describes how the telegram lines are parsed. """ V2_2 = { - "checksum_support": False, - "objects": { + 'checksum_support': False, + 'objects': { obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), obis.ELECTRICITY_USED_TARIFF_1: CosemParser(ValueParser(Decimal)), obis.ELECTRICITY_USED_TARIFF_2: CosemParser(ValueParser(Decimal)), @@ -50,14 +40,14 @@ V2_2 = { ValueParser(str), # unit, position 5 ValueParser(Decimal), # meter reading, position 6 ), - }, + } } V3 = V2_2 V4 = { - "checksum_support": True, - "objects": { + 'checksum_support': True, + 'objects': { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), @@ -70,9 +60,10 @@ V4 = { obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.SHORT_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), obis.LONG_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), - obis.POWER_EVENT_FAILURE_LOG: ProfileGenericParser( - BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS - ), + obis.POWER_EVENT_FAILURE_LOG: + ProfileGenericParser(BUFFER_TYPES, + PG_HEAD_PARSERS, + PG_UNIDENTIFIED_BUFFERTYPE_PARSERS), obis.VOLTAGE_SAG_L1_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L2_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L3_COUNT: CosemParser(ValueParser(int)), @@ -93,14 +84,15 @@ V4 = { obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: CosemParser(ValueParser(Decimal)), obis.EQUIPMENT_IDENTIFIER_GAS: CosemParser(ValueParser(str)), obis.HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), ValueParser(Decimal) - ), - }, + ValueParser(timestamp), + ValueParser(Decimal) + ) + } } V5 = { - "checksum_support": True, - "objects": { + 'checksum_support': True, + 'objects': { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), @@ -114,9 +106,10 @@ V5 = { obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.LONG_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), obis.SHORT_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), - obis.POWER_EVENT_FAILURE_LOG: ProfileGenericParser( - BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS - ), + obis.POWER_EVENT_FAILURE_LOG: + ProfileGenericParser(BUFFER_TYPES, + PG_HEAD_PARSERS, + PG_UNIDENTIFIED_BUFFERTYPE_PARSERS), obis.VOLTAGE_SAG_L1_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L2_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L3_COUNT: CosemParser(ValueParser(int)), @@ -139,46 +132,38 @@ V5 = { obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: CosemParser(ValueParser(Decimal)), obis.EQUIPMENT_IDENTIFIER_GAS: CosemParser(ValueParser(str)), obis.HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), ValueParser(Decimal) - ), - }, + ValueParser(timestamp), + ValueParser(Decimal) + ) + } } ALL = (V2_2, V3, V4, V5) BELGIUM_FLUVIUS = deepcopy(V5) -BELGIUM_FLUVIUS["objects"].update( - { - obis.BELGIUM_HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), ValueParser(Decimal) - ) - } -) +BELGIUM_FLUVIUS['objects'].update({ + obis.BELGIUM_HOURLY_GAS_METER_READING: MBusParser( + ValueParser(timestamp), + ValueParser(Decimal) + ) +}) LUXEMBOURG_SMARTY = deepcopy(V5) -LUXEMBOURG_SMARTY["objects"].update( - { - obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), - obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser( - ValueParser(Decimal) - ), - obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser( - ValueParser(Decimal) - ), - } -) +LUXEMBOURG_SMARTY['objects'].update({ + obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), + obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), +}) # Source: https://www.energiforetagen.se/globalassets/energiforetagen/det-erbjuder-vi/kurser-och-konferenser/elnat/branschrekommendation-lokalt-granssnitt-v2_0-201912.pdf SWEDEN = { - "checksum_support": True, - "objects": { + 'checksum_support': True, + 'objects': { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), - obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser( - ValueParser(Decimal) - ), + obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), obis.CURRENT_ELECTRICITY_USAGE: CosemParser(ValueParser(Decimal)), obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: CosemParser(ValueParser(Decimal)), @@ -193,19 +178,16 @@ SWEDEN = { obis.INSTANTANEOUS_CURRENT_L1: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_CURRENT_L2: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_CURRENT_L3: CosemParser(ValueParser(Decimal)), - }, + } } + Q3D = { "checksum_support": False, "objects": { obis.Q3D_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), - obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser( - ValueParser(Decimal) - ), - obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser( - ValueParser(Decimal) - ), + obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), + obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: CosemParser(ValueParser(Decimal)), From 8e2bdd32792949fc35898c94d402570aac509287 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Fri, 12 Nov 2021 19:23:38 +0000 Subject: [PATCH 07/16] clean-up re-format --- dsmr_parser/telegram_specifications.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dsmr_parser/telegram_specifications.py b/dsmr_parser/telegram_specifications.py index bb82868..83f6bd8 100644 --- a/dsmr_parser/telegram_specifications.py +++ b/dsmr_parser/telegram_specifications.py @@ -9,6 +9,7 @@ from dsmr_parser.profile_generic_specifications import BUFFER_TYPES, PG_HEAD_PAR """ dsmr_parser.telegram_specifications ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + This module contains DSMR telegram specifications. Each specifications describes how the telegram lines are parsed. """ From 3092ba8b1f0a1895d12b7de2a3de7f4e4a538dd3 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Fri, 12 Nov 2021 19:24:44 +0000 Subject: [PATCH 08/16] clean-up re-format --- dsmr_parser/clients/protocol.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py index 6a7a17e..88b25fb 100644 --- a/dsmr_parser/clients/protocol.py +++ b/dsmr_parser/clients/protocol.py @@ -29,13 +29,13 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): elif dsmr_version == '5B': specification = telegram_specifications.BELGIUM_FLUVIUS serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == '5L': + elif dsmr_version == "5L": specification = telegram_specifications.LUXEMBOURG_SMARTY serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == '5S': + elif dsmr_version == "5S": specification = telegram_specifications.SWEDEN serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == 'Q3D': + elif dsmr_version == 'Q3D": specification = telegram_specifications.Q3D serial_settings = SERIAL_SETTINGS_V5 else: From c1202f33e9dc90283acaf8ad884aaabb83ca03d6 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Fri, 12 Nov 2021 20:12:47 +0000 Subject: [PATCH 09/16] clean-up re-format --- dsmr_parser/clients/protocol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py index 88b25fb..45f6d48 100644 --- a/dsmr_parser/clients/protocol.py +++ b/dsmr_parser/clients/protocol.py @@ -35,7 +35,7 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): elif dsmr_version == "5S": specification = telegram_specifications.SWEDEN serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == 'Q3D": + elif dsmr_version == "Q3D": specification = telegram_specifications.Q3D serial_settings = SERIAL_SETTINGS_V5 else: From 4073917d3e269d6fb7175fbbd18e61fb835010f8 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Sun, 14 Nov 2021 21:29:24 +0000 Subject: [PATCH 10/16] decode latin1 and added ELECTRICITY_EXPORTED_TOTAL --- dsmr_parser/clients/protocol.py | 68 ++++++++------ dsmr_parser/obis_name_mapping.py | 101 ++++++++++----------- dsmr_parser/obis_references.py | 118 +++++++++++++------------ dsmr_parser/telegram_specifications.py | 99 ++++++++++++--------- 4 files changed, 211 insertions(+), 175 deletions(-) diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py index 45f6d48..3b4c062 100644 --- a/dsmr_parser/clients/protocol.py +++ b/dsmr_parser/clients/protocol.py @@ -10,23 +10,26 @@ from dsmr_parser import telegram_specifications from dsmr_parser.clients.telegram_buffer import TelegramBuffer from dsmr_parser.exceptions import ParseError, InvalidChecksumError from dsmr_parser.parsers import TelegramParser -from dsmr_parser.clients.settings import SERIAL_SETTINGS_V2_2, \ - SERIAL_SETTINGS_V4, SERIAL_SETTINGS_V5 +from dsmr_parser.clients.settings import ( + SERIAL_SETTINGS_V2_2, + SERIAL_SETTINGS_V4, + SERIAL_SETTINGS_V5, +) def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): """Creates a DSMR asyncio protocol.""" - if dsmr_version == '2.2': + if dsmr_version == "2.2": specification = telegram_specifications.V2_2 serial_settings = SERIAL_SETTINGS_V2_2 - elif dsmr_version == '4': + elif dsmr_version == "4": specification = telegram_specifications.V4 serial_settings = SERIAL_SETTINGS_V4 - elif dsmr_version == '5': + elif dsmr_version == "5": specification = telegram_specifications.V5 serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == '5B': + elif dsmr_version == "5B": specification = telegram_specifications.BELGIUM_FLUVIUS serial_settings = SERIAL_SETTINGS_V5 elif dsmr_version == "5L": @@ -39,11 +42,17 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): specification = telegram_specifications.Q3D serial_settings = SERIAL_SETTINGS_V5 else: - raise NotImplementedError("No telegram parser found for version: %s", - dsmr_version) + raise NotImplementedError( + "No telegram parser found for version: %s", dsmr_version + ) - protocol = partial(DSMRProtocol, loop, TelegramParser(specification), - telegram_callback=telegram_callback, **kwargs) + protocol = partial( + DSMRProtocol, + loop, + TelegramParser(specification), + telegram_callback=telegram_callback, + **kwargs + ) return protocol, serial_settings @@ -51,22 +60,26 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): def create_dsmr_reader(port, dsmr_version, telegram_callback, loop=None): """Creates a DSMR asyncio protocol coroutine using serial port.""" protocol, serial_settings = create_dsmr_protocol( - dsmr_version, telegram_callback, loop=None) - serial_settings['url'] = port + dsmr_version, telegram_callback, loop=None + ) + serial_settings["url"] = port conn = create_serial_connection(loop, protocol, **serial_settings) return conn -def create_tcp_dsmr_reader(host, port, dsmr_version, - telegram_callback, loop=None, - keep_alive_interval=None): +def create_tcp_dsmr_reader( + host, port, dsmr_version, telegram_callback, loop=None, keep_alive_interval=None +): """Creates a DSMR asyncio protocol coroutine using TCP connection.""" if not loop: loop = asyncio.get_event_loop() protocol, _ = create_dsmr_protocol( - dsmr_version, telegram_callback, loop=loop, - keep_alive_interval=keep_alive_interval) + dsmr_version, + telegram_callback, + loop=loop, + keep_alive_interval=keep_alive_interval, + ) conn = loop.create_connection(protocol, host, port) return conn @@ -77,8 +90,9 @@ class DSMRProtocol(asyncio.Protocol): transport = None telegram_callback = None - def __init__(self, loop, telegram_parser, - telegram_callback=None, keep_alive_interval=None): + def __init__( + self, loop, telegram_parser, telegram_callback=None, keep_alive_interval=None + ): """Initialize class.""" self.loop = loop self.log = logging.getLogger(__name__) @@ -95,16 +109,16 @@ class DSMRProtocol(asyncio.Protocol): def connection_made(self, transport): """Just logging for now.""" self.transport = transport - self.log.debug('connected') + self.log.debug("connected") self._active = False if self.loop and self._keep_alive_interval: self.loop.call_later(self._keep_alive_interval, self.keep_alive) def data_received(self, data): """Add incoming data to buffer.""" - data = data.decode('ascii', errors='ignore') + data = data.decode("latin1") self._active = True - self.log.debug('received data: %s', data) + self.log.debug("received data: %s", data) self.telegram_buffer.append(data) for telegram in self.telegram_buffer.get_all(): @@ -112,26 +126,26 @@ class DSMRProtocol(asyncio.Protocol): def keep_alive(self): if self._active: - self.log.debug('keep-alive checked') + self.log.debug("keep-alive checked") self._active = False if self.loop: self.loop.call_later(self._keep_alive_interval, self.keep_alive) else: - self.log.warning('keep-alive check failed') + self.log.warning("keep-alive check failed") if self.transport: self.transport.close() def connection_lost(self, exc): """Stop when connection is lost.""" if exc: - self.log.exception('disconnected due to exception', exc_info=exc) + self.log.exception("disconnected due to exception", exc_info=exc) else: - self.log.info('disconnected because of close/abort.') + self.log.info("disconnected because of close/abort.") self._closed.set() def handle_telegram(self, telegram): """Send off parsed telegram to handling callback.""" - self.log.debug('got telegram: %s', telegram) + self.log.debug("got telegram: %s", telegram) try: parsed_telegram = self.telegram_parser.parse(telegram) diff --git a/dsmr_parser/obis_name_mapping.py b/dsmr_parser/obis_name_mapping.py index fcba570..5a05f00 100644 --- a/dsmr_parser/obis_name_mapping.py +++ b/dsmr_parser/obis_name_mapping.py @@ -8,56 +8,57 @@ This module contains a mapping of obis references to names. """ EN = { - obis.P1_MESSAGE_HEADER: 'P1_MESSAGE_HEADER', - obis.P1_MESSAGE_TIMESTAMP: 'P1_MESSAGE_TIMESTAMP', - obis.ELECTRICITY_IMPORTED_TOTAL: 'ELECTRICITY_IMPORTED_TOTAL', - obis.ELECTRICITY_USED_TARIFF_1: 'ELECTRICITY_USED_TARIFF_1', - obis.ELECTRICITY_USED_TARIFF_2: 'ELECTRICITY_USED_TARIFF_2', - obis.ELECTRICITY_DELIVERED_TARIFF_1: 'ELECTRICITY_DELIVERED_TARIFF_1', - obis.ELECTRICITY_DELIVERED_TARIFF_2: 'ELECTRICITY_DELIVERED_TARIFF_2', - obis.ELECTRICITY_ACTIVE_TARIFF: 'ELECTRICITY_ACTIVE_TARIFF', - obis.EQUIPMENT_IDENTIFIER: 'EQUIPMENT_IDENTIFIER', - obis.CURRENT_ELECTRICITY_USAGE: 'CURRENT_ELECTRICITY_USAGE', - obis.CURRENT_ELECTRICITY_DELIVERY: 'CURRENT_ELECTRICITY_DELIVERY', - obis.LONG_POWER_FAILURE_COUNT: 'LONG_POWER_FAILURE_COUNT', - obis.SHORT_POWER_FAILURE_COUNT: 'SHORT_POWER_FAILURE_COUNT', - obis.POWER_EVENT_FAILURE_LOG: 'POWER_EVENT_FAILURE_LOG', - obis.VOLTAGE_SAG_L1_COUNT: 'VOLTAGE_SAG_L1_COUNT', - obis.VOLTAGE_SAG_L2_COUNT: 'VOLTAGE_SAG_L2_COUNT', - obis.VOLTAGE_SAG_L3_COUNT: 'VOLTAGE_SAG_L3_COUNT', - obis.VOLTAGE_SWELL_L1_COUNT: 'VOLTAGE_SWELL_L1_COUNT', - obis.VOLTAGE_SWELL_L2_COUNT: 'VOLTAGE_SWELL_L2_COUNT', - obis.VOLTAGE_SWELL_L3_COUNT: 'VOLTAGE_SWELL_L3_COUNT', - obis.INSTANTANEOUS_VOLTAGE_L1: 'INSTANTANEOUS_VOLTAGE_L1', - obis.INSTANTANEOUS_VOLTAGE_L2: 'INSTANTANEOUS_VOLTAGE_L2', - obis.INSTANTANEOUS_VOLTAGE_L3: 'INSTANTANEOUS_VOLTAGE_L3', - obis.INSTANTANEOUS_CURRENT_L1: 'INSTANTANEOUS_CURRENT_L1', - obis.INSTANTANEOUS_CURRENT_L2: 'INSTANTANEOUS_CURRENT_L2', - obis.INSTANTANEOUS_CURRENT_L3: 'INSTANTANEOUS_CURRENT_L3', - obis.TEXT_MESSAGE_CODE: 'TEXT_MESSAGE_CODE', - obis.TEXT_MESSAGE: 'TEXT_MESSAGE', - obis.DEVICE_TYPE: 'DEVICE_TYPE', - obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE', - obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE', - obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE', - obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE', - obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE', - obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE', - obis.EQUIPMENT_IDENTIFIER_GAS: 'EQUIPMENT_IDENTIFIER_GAS', - obis.HOURLY_GAS_METER_READING: 'HOURLY_GAS_METER_READING', - obis.GAS_METER_READING: 'GAS_METER_READING', - obis.ACTUAL_TRESHOLD_ELECTRICITY: 'ACTUAL_TRESHOLD_ELECTRICITY', - obis.ACTUAL_SWITCH_POSITION: 'ACTUAL_SWITCH_POSITION', - obis.VALVE_POSITION_GAS: 'VALVE_POSITION_GAS', - obis.BELGIUM_HOURLY_GAS_METER_READING: 'BELGIUM_HOURLY_GAS_METER_READING', - obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: 'LUXEMBOURG_EQUIPMENT_IDENTIFIER', - obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL', - obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL', - obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: 'SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL', - obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL', - obis.Q3D_EQUIPMENT_IDENTIFIER: 'Q3D_EQUIPMENT_IDENTIFIER', - obis.Q3D_EQUIPMENT_STATE: 'Q3D_EQUIPMENT_STATE', - obis.Q3D_EQUIPMENT_SERIALNUMBER: 'Q3D_EQUIPMENT_SERIALNUMBER', + obis.P1_MESSAGE_HEADER: "P1_MESSAGE_HEADER", + obis.P1_MESSAGE_TIMESTAMP: "P1_MESSAGE_TIMESTAMP", + obis.ELECTRICITY_IMPORTED_TOTAL: "ELECTRICITY_IMPORTED_TOTAL", + obis.ELECTRICITY_USED_TARIFF_1: "ELECTRICITY_USED_TARIFF_1", + obis.ELECTRICITY_USED_TARIFF_2: "ELECTRICITY_USED_TARIFF_2", + obis.ELECTRICITY_EXPORTED_TOTAL: "ELECTRICITY_EXPORTED_TOTAL", + obis.ELECTRICITY_DELIVERED_TARIFF_1: "ELECTRICITY_DELIVERED_TARIFF_1", + obis.ELECTRICITY_DELIVERED_TARIFF_2: "ELECTRICITY_DELIVERED_TARIFF_2", + obis.ELECTRICITY_ACTIVE_TARIFF: "ELECTRICITY_ACTIVE_TARIFF", + obis.EQUIPMENT_IDENTIFIER: "EQUIPMENT_IDENTIFIER", + obis.CURRENT_ELECTRICITY_USAGE: "CURRENT_ELECTRICITY_USAGE", + obis.CURRENT_ELECTRICITY_DELIVERY: "CURRENT_ELECTRICITY_DELIVERY", + obis.LONG_POWER_FAILURE_COUNT: "LONG_POWER_FAILURE_COUNT", + obis.SHORT_POWER_FAILURE_COUNT: "SHORT_POWER_FAILURE_COUNT", + obis.POWER_EVENT_FAILURE_LOG: "POWER_EVENT_FAILURE_LOG", + obis.VOLTAGE_SAG_L1_COUNT: "VOLTAGE_SAG_L1_COUNT", + obis.VOLTAGE_SAG_L2_COUNT: "VOLTAGE_SAG_L2_COUNT", + obis.VOLTAGE_SAG_L3_COUNT: "VOLTAGE_SAG_L3_COUNT", + obis.VOLTAGE_SWELL_L1_COUNT: "VOLTAGE_SWELL_L1_COUNT", + obis.VOLTAGE_SWELL_L2_COUNT: "VOLTAGE_SWELL_L2_COUNT", + obis.VOLTAGE_SWELL_L3_COUNT: "VOLTAGE_SWELL_L3_COUNT", + obis.INSTANTANEOUS_VOLTAGE_L1: "INSTANTANEOUS_VOLTAGE_L1", + obis.INSTANTANEOUS_VOLTAGE_L2: "INSTANTANEOUS_VOLTAGE_L2", + obis.INSTANTANEOUS_VOLTAGE_L3: "INSTANTANEOUS_VOLTAGE_L3", + obis.INSTANTANEOUS_CURRENT_L1: "INSTANTANEOUS_CURRENT_L1", + obis.INSTANTANEOUS_CURRENT_L2: "INSTANTANEOUS_CURRENT_L2", + obis.INSTANTANEOUS_CURRENT_L3: "INSTANTANEOUS_CURRENT_L3", + obis.TEXT_MESSAGE_CODE: "TEXT_MESSAGE_CODE", + obis.TEXT_MESSAGE: "TEXT_MESSAGE", + obis.DEVICE_TYPE: "DEVICE_TYPE", + obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE", + obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE", + obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE", + obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE", + obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE", + obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE", + obis.EQUIPMENT_IDENTIFIER_GAS: "EQUIPMENT_IDENTIFIER_GAS", + obis.HOURLY_GAS_METER_READING: "HOURLY_GAS_METER_READING", + obis.GAS_METER_READING: "GAS_METER_READING", + obis.ACTUAL_TRESHOLD_ELECTRICITY: "ACTUAL_TRESHOLD_ELECTRICITY", + obis.ACTUAL_SWITCH_POSITION: "ACTUAL_SWITCH_POSITION", + obis.VALVE_POSITION_GAS: "VALVE_POSITION_GAS", + obis.BELGIUM_HOURLY_GAS_METER_READING: "BELGIUM_HOURLY_GAS_METER_READING", + obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: "LUXEMBOURG_EQUIPMENT_IDENTIFIER", + obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: "LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL", + obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: "LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL", + obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: "SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL", + obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: "SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL", + obis.Q3D_EQUIPMENT_IDENTIFIER: "Q3D_EQUIPMENT_IDENTIFIER", + obis.Q3D_EQUIPMENT_STATE: "Q3D_EQUIPMENT_STATE", + obis.Q3D_EQUIPMENT_SERIALNUMBER: "Q3D_EQUIPMENT_SERIALNUMBER", } REVERSE_EN = dict([(v, k) for k, v in EN.items()]) diff --git a/dsmr_parser/obis_references.py b/dsmr_parser/obis_references.py index 3808848..02e1e33 100644 --- a/dsmr_parser/obis_references.py +++ b/dsmr_parser/obis_references.py @@ -6,68 +6,76 @@ refactored to full line signatures to maintain backwards compatibility. Might be refactored in a backwards incompatible way as soon as proper telegram objects are introduced. """ -P1_MESSAGE_HEADER = r'\d-\d:0\.2\.8.+?\r\n' -P1_MESSAGE_TIMESTAMP = r'\d-\d:1\.0\.0.+?\r\n' -ELECTRICITY_IMPORTED_TOTAL = r'\d-\d:1\.8\.0.+?\r\n' -ELECTRICITY_USED_TARIFF_1 = r'\d-\d:1\.8\.1.+?\r\n' -ELECTRICITY_USED_TARIFF_2 = r'\d-\d:1\.8\.2.+?\r\n' -ELECTRICITY_DELIVERED_TARIFF_1 = r'\d-\d:2\.8\.1.+?\r\n' -ELECTRICITY_DELIVERED_TARIFF_2 = r'\d-\d:2\.8\.2.+?\r\n' -ELECTRICITY_ACTIVE_TARIFF = r'\d-\d:96\.14\.0.+?\r\n' -EQUIPMENT_IDENTIFIER = r'\d-\d:96\.1\.1.+?\r\n' -CURRENT_ELECTRICITY_USAGE = r'\d-\d:1\.7\.0.+?\r\n' -CURRENT_ELECTRICITY_DELIVERY = r'\d-\d:2\.7\.0.+?\r\n' -LONG_POWER_FAILURE_COUNT = r'96\.7\.9.+?\r\n' -SHORT_POWER_FAILURE_COUNT = r'96\.7\.21.+?\r\n' -POWER_EVENT_FAILURE_LOG = r'99\.97\.0.+?\r\n' -VOLTAGE_SAG_L1_COUNT = r'\d-\d:32\.32\.0.+?\r\n' -VOLTAGE_SAG_L2_COUNT = r'\d-\d:52\.32\.0.+?\r\n' -VOLTAGE_SAG_L3_COUNT = r'\d-\d:72\.32\.0.+?\r\n' -VOLTAGE_SWELL_L1_COUNT = r'\d-\d:32\.36\.0.+?\r\n' -VOLTAGE_SWELL_L2_COUNT = r'\d-\d:52\.36\.0.+?\r\n' -VOLTAGE_SWELL_L3_COUNT = r'\d-\d:72\.36\.0.+?\r\n' -INSTANTANEOUS_VOLTAGE_L1 = r'\d-\d:32\.7\.0.+?\r\n' -INSTANTANEOUS_VOLTAGE_L2 = r'\d-\d:52\.7\.0.+?\r\n' -INSTANTANEOUS_VOLTAGE_L3 = r'\d-\d:72\.7\.0.+?\r\n' -INSTANTANEOUS_CURRENT_L1 = r'\d-\d:31\.7\.0.+?\r\n' -INSTANTANEOUS_CURRENT_L2 = r'\d-\d:51\.7\.0.+?\r\n' -INSTANTANEOUS_CURRENT_L3 = r'\d-\d:71\.7\.0.+?\r\n' -TEXT_MESSAGE_CODE = r'\d-\d:96\.13\.1.+?\r\n' -TEXT_MESSAGE = r'\d-\d:96\.13\.0.+?\r\n' -DEVICE_TYPE = r'\d-\d:24\.1\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE = r'\d-\d:21\.7\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE = r'\d-\d:41\.7\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE = r'\d-\d:61\.7\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE = r'\d-\d:22\.7\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE = r'\d-\d:42\.7\.0.+?\r\n' -INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE = r'\d-\d:62\.7\.0.+?\r\n' -EQUIPMENT_IDENTIFIER_GAS = r'\d-\d:96\.1\.0.+?\r\n' +P1_MESSAGE_HEADER = r"\d-\d:0\.2\.8.+?\r\n" +P1_MESSAGE_TIMESTAMP = r"\d-\d:1\.0\.0.+?\r\n" +ELECTRICITY_IMPORTED_TOTAL = r"\d-\d:1\.8\.0.+?\r\n" +ELECTRICITY_USED_TARIFF_1 = r"\d-\d:1\.8\.1.+?\r\n" +ELECTRICITY_USED_TARIFF_2 = r"\d-\d:1\.8\.2.+?\r\n" +ELECTRICITY_EXPORTED_TOTAL = r"\d-\d:2\.8\.0.+?\r\n" +ELECTRICITY_DELIVERED_TARIFF_1 = r"\d-\d:2\.8\.1.+?\r\n" +ELECTRICITY_DELIVERED_TARIFF_2 = r"\d-\d:2\.8\.2.+?\r\n" +ELECTRICITY_ACTIVE_TARIFF = r"\d-\d:96\.14\.0.+?\r\n" +EQUIPMENT_IDENTIFIER = r"\d-\d:96\.1\.1.+?\r\n" +CURRENT_ELECTRICITY_USAGE = r"\d-\d:1\.7\.0.+?\r\n" +CURRENT_ELECTRICITY_DELIVERY = r"\d-\d:2\.7\.0.+?\r\n" +LONG_POWER_FAILURE_COUNT = r"96\.7\.9.+?\r\n" +SHORT_POWER_FAILURE_COUNT = r"96\.7\.21.+?\r\n" +POWER_EVENT_FAILURE_LOG = r"99\.97\.0.+?\r\n" +VOLTAGE_SAG_L1_COUNT = r"\d-\d:32\.32\.0.+?\r\n" +VOLTAGE_SAG_L2_COUNT = r"\d-\d:52\.32\.0.+?\r\n" +VOLTAGE_SAG_L3_COUNT = r"\d-\d:72\.32\.0.+?\r\n" +VOLTAGE_SWELL_L1_COUNT = r"\d-\d:32\.36\.0.+?\r\n" +VOLTAGE_SWELL_L2_COUNT = r"\d-\d:52\.36\.0.+?\r\n" +VOLTAGE_SWELL_L3_COUNT = r"\d-\d:72\.36\.0.+?\r\n" +INSTANTANEOUS_VOLTAGE_L1 = r"\d-\d:32\.7\.0.+?\r\n" +INSTANTANEOUS_VOLTAGE_L2 = r"\d-\d:52\.7\.0.+?\r\n" +INSTANTANEOUS_VOLTAGE_L3 = r"\d-\d:72\.7\.0.+?\r\n" +INSTANTANEOUS_CURRENT_L1 = r"\d-\d:31\.7\.0.+?\r\n" +INSTANTANEOUS_CURRENT_L2 = r"\d-\d:51\.7\.0.+?\r\n" +INSTANTANEOUS_CURRENT_L3 = r"\d-\d:71\.7\.0.+?\r\n" +TEXT_MESSAGE_CODE = r"\d-\d:96\.13\.1.+?\r\n" +TEXT_MESSAGE = r"\d-\d:96\.13\.0.+?\r\n" +DEVICE_TYPE = r"\d-\d:24\.1\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE = r"\d-\d:21\.7\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE = r"\d-\d:41\.7\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE = r"\d-\d:61\.7\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE = r"\d-\d:22\.7\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE = r"\d-\d:42\.7\.0.+?\r\n" +INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE = r"\d-\d:62\.7\.0.+?\r\n" +EQUIPMENT_IDENTIFIER_GAS = r"\d-\d:96\.1\.0.+?\r\n" # TODO differences between gas meter readings in v3 and lower and v4 and up -HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.1.+?\r\n' -GAS_METER_READING = r'\d-\d:24\.3\.0.+?\r\n.+?\r\n' -ACTUAL_TRESHOLD_ELECTRICITY = r'\d-\d:17\.0\.0.+?\r\n' -ACTUAL_SWITCH_POSITION = r'\d-\d:96\.3\.10.+?\r\n' -VALVE_POSITION_GAS = r'\d-\d:24\.4\.0.+?\r\n' +HOURLY_GAS_METER_READING = r"\d-\d:24\.2\.1.+?\r\n" +GAS_METER_READING = r"\d-\d:24\.3\.0.+?\r\n.+?\r\n" +ACTUAL_TRESHOLD_ELECTRICITY = r"\d-\d:17\.0\.0.+?\r\n" +ACTUAL_SWITCH_POSITION = r"\d-\d:96\.3\.10.+?\r\n" +VALVE_POSITION_GAS = r"\d-\d:24\.4\.0.+?\r\n" # TODO 17.0.0 # TODO 96.3.10 -ELECTRICITY_USED_TARIFF_ALL = ( - ELECTRICITY_USED_TARIFF_1, - ELECTRICITY_USED_TARIFF_2 -) +ELECTRICITY_USED_TARIFF_ALL = (ELECTRICITY_USED_TARIFF_1, ELECTRICITY_USED_TARIFF_2) ELECTRICITY_DELIVERED_TARIFF_ALL = ( ELECTRICITY_DELIVERED_TARIFF_1, - ELECTRICITY_DELIVERED_TARIFF_2 + ELECTRICITY_DELIVERED_TARIFF_2, ) # Alternate codes for foreign countries. -BELGIUM_HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.3.+?\r\n' # Different code, same format. -LUXEMBOURG_EQUIPMENT_IDENTIFIER = r'\d-\d:42\.0\.0.+?\r\n' # Logical device name -LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+) -LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-) -SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+) -SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-) -Q3D_EQUIPMENT_IDENTIFIER = r'\d-\d:0\.0\.0.+?\r\n' # Logical device name -Q3D_EQUIPMENT_STATE = r'\d-\d:96\.5\.5.+?\r\n' # Device state (hexadecimal) -Q3D_EQUIPMENT_SERIALNUMBER = r'\d-\d:96\.1\.255.+?\r\n' # Device Serialnumber +BELGIUM_HOURLY_GAS_METER_READING = ( + r"\d-\d:24\.2\.3.+?\r\n" # Different code, same format. +) +LUXEMBOURG_EQUIPMENT_IDENTIFIER = r"\d-\d:42\.0\.0.+?\r\n" # Logical device name +LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = ( + r"\d-\d:1\.8\.0.+?\r\n" # Total imported energy register (P+) +) +LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = ( + r"\d-\d:2\.8\.0.+?\r\n" # Total exported energy register (P-) +) +SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL = ( + r"\d-\d:1\.8\.0.+?\r\n" # Total imported energy register (P+) +) +SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = ( + r"\d-\d:2\.8\.0.+?\r\n" # Total exported energy register (P-) +) +Q3D_EQUIPMENT_IDENTIFIER = r"\d-\d:0\.0\.0.+?\r\n" # Logical device name +Q3D_EQUIPMENT_STATE = r"\d-\d:96\.5\.5.+?\r\n" # Device state (hexadecimal) +Q3D_EQUIPMENT_SERIALNUMBER = r"\d-\d:96\.1\.255.+?\r\n" # Device Serialnumber diff --git a/dsmr_parser/telegram_specifications.py b/dsmr_parser/telegram_specifications.py index 83f6bd8..77ea49e 100644 --- a/dsmr_parser/telegram_specifications.py +++ b/dsmr_parser/telegram_specifications.py @@ -2,9 +2,18 @@ from decimal import Decimal from copy import deepcopy from dsmr_parser import obis_references as obis -from dsmr_parser.parsers import CosemParser, ValueParser, MBusParser, ProfileGenericParser +from dsmr_parser.parsers import ( + CosemParser, + ValueParser, + MBusParser, + ProfileGenericParser, +) from dsmr_parser.value_types import timestamp -from dsmr_parser.profile_generic_specifications import BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS +from dsmr_parser.profile_generic_specifications import ( + BUFFER_TYPES, + PG_HEAD_PARSERS, + PG_UNIDENTIFIED_BUFFERTYPE_PARSERS, +) """ dsmr_parser.telegram_specifications @@ -15,8 +24,8 @@ how the telegram lines are parsed. """ V2_2 = { - 'checksum_support': False, - 'objects': { + "checksum_support": False, + "objects": { obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), obis.ELECTRICITY_USED_TARIFF_1: CosemParser(ValueParser(Decimal)), obis.ELECTRICITY_USED_TARIFF_2: CosemParser(ValueParser(Decimal)), @@ -41,14 +50,14 @@ V2_2 = { ValueParser(str), # unit, position 5 ValueParser(Decimal), # meter reading, position 6 ), - } + }, } V3 = V2_2 V4 = { - 'checksum_support': True, - 'objects': { + "checksum_support": True, + "objects": { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), @@ -61,10 +70,9 @@ V4 = { obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.SHORT_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), obis.LONG_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), - obis.POWER_EVENT_FAILURE_LOG: - ProfileGenericParser(BUFFER_TYPES, - PG_HEAD_PARSERS, - PG_UNIDENTIFIED_BUFFERTYPE_PARSERS), + obis.POWER_EVENT_FAILURE_LOG: ProfileGenericParser( + BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS + ), obis.VOLTAGE_SAG_L1_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L2_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L3_COUNT: CosemParser(ValueParser(int)), @@ -85,15 +93,14 @@ V4 = { obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: CosemParser(ValueParser(Decimal)), obis.EQUIPMENT_IDENTIFIER_GAS: CosemParser(ValueParser(str)), obis.HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), - ValueParser(Decimal) - ) - } + ValueParser(timestamp), ValueParser(Decimal) + ), + }, } V5 = { - 'checksum_support': True, - 'objects': { + "checksum_support": True, + "objects": { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), @@ -107,10 +114,9 @@ V5 = { obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.LONG_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), obis.SHORT_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), - obis.POWER_EVENT_FAILURE_LOG: - ProfileGenericParser(BUFFER_TYPES, - PG_HEAD_PARSERS, - PG_UNIDENTIFIED_BUFFERTYPE_PARSERS), + obis.POWER_EVENT_FAILURE_LOG: ProfileGenericParser( + BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS + ), obis.VOLTAGE_SAG_L1_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L2_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L3_COUNT: CosemParser(ValueParser(int)), @@ -133,38 +139,46 @@ V5 = { obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: CosemParser(ValueParser(Decimal)), obis.EQUIPMENT_IDENTIFIER_GAS: CosemParser(ValueParser(str)), obis.HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), - ValueParser(Decimal) - ) - } + ValueParser(timestamp), ValueParser(Decimal) + ), + }, } ALL = (V2_2, V3, V4, V5) BELGIUM_FLUVIUS = deepcopy(V5) -BELGIUM_FLUVIUS['objects'].update({ - obis.BELGIUM_HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), - ValueParser(Decimal) - ) -}) +BELGIUM_FLUVIUS["objects"].update( + { + obis.BELGIUM_HOURLY_GAS_METER_READING: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ) + } +) LUXEMBOURG_SMARTY = deepcopy(V5) -LUXEMBOURG_SMARTY['objects'].update({ - obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), - obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), - obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), -}) +LUXEMBOURG_SMARTY["objects"].update( + { + obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser( + ValueParser(Decimal) + ), + obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser( + ValueParser(Decimal) + ), + } +) # Source: https://www.energiforetagen.se/globalassets/energiforetagen/det-erbjuder-vi/kurser-och-konferenser/elnat/branschrekommendation-lokalt-granssnitt-v2_0-201912.pdf SWEDEN = { - 'checksum_support': True, - 'objects': { + "checksum_support": True, + "objects": { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), - obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), + obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser( + ValueParser(Decimal) + ), obis.CURRENT_ELECTRICITY_USAGE: CosemParser(ValueParser(Decimal)), obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: CosemParser(ValueParser(Decimal)), @@ -179,16 +193,15 @@ SWEDEN = { obis.INSTANTANEOUS_CURRENT_L1: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_CURRENT_L2: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_CURRENT_L3: CosemParser(ValueParser(Decimal)), - } + }, } - Q3D = { "checksum_support": False, "objects": { obis.Q3D_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), - obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), - obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_IMPORTED_TOTAL: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_EXPORTED_TOTAL: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: CosemParser(ValueParser(Decimal)), From df59f3a4989bacb70e8748b4ad56f51b14c8f011 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Sun, 14 Nov 2021 21:49:43 +0000 Subject: [PATCH 11/16] decode latin1 and added ELECTRICITY_EXPORTED_TOTAL --- dsmr_parser/clients/protocol.py | 66 ++++++-------- dsmr_parser/obis_name_mapping.py | 102 ++++++++++----------- dsmr_parser/obis_references.py | 119 ++++++++++++------------- dsmr_parser/telegram_specifications.py | 94 +++++++++---------- 4 files changed, 173 insertions(+), 208 deletions(-) diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py index 3b4c062..208ed1b 100644 --- a/dsmr_parser/clients/protocol.py +++ b/dsmr_parser/clients/protocol.py @@ -10,26 +10,23 @@ from dsmr_parser import telegram_specifications from dsmr_parser.clients.telegram_buffer import TelegramBuffer from dsmr_parser.exceptions import ParseError, InvalidChecksumError from dsmr_parser.parsers import TelegramParser -from dsmr_parser.clients.settings import ( - SERIAL_SETTINGS_V2_2, - SERIAL_SETTINGS_V4, - SERIAL_SETTINGS_V5, -) +from dsmr_parser.clients.settings import SERIAL_SETTINGS_V2_2, \ + SERIAL_SETTINGS_V4, SERIAL_SETTINGS_V5 def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): """Creates a DSMR asyncio protocol.""" - if dsmr_version == "2.2": + if dsmr_version == '2.2': specification = telegram_specifications.V2_2 serial_settings = SERIAL_SETTINGS_V2_2 - elif dsmr_version == "4": + elif dsmr_version == '4': specification = telegram_specifications.V4 serial_settings = SERIAL_SETTINGS_V4 - elif dsmr_version == "5": + elif dsmr_version == '5': specification = telegram_specifications.V5 serial_settings = SERIAL_SETTINGS_V5 - elif dsmr_version == "5B": + elif dsmr_version == '5B': specification = telegram_specifications.BELGIUM_FLUVIUS serial_settings = SERIAL_SETTINGS_V5 elif dsmr_version == "5L": @@ -42,17 +39,11 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): specification = telegram_specifications.Q3D serial_settings = SERIAL_SETTINGS_V5 else: - raise NotImplementedError( - "No telegram parser found for version: %s", dsmr_version - ) + raise NotImplementedError("No telegram parser found for version: %s", + dsmr_version) - protocol = partial( - DSMRProtocol, - loop, - TelegramParser(specification), - telegram_callback=telegram_callback, - **kwargs - ) + protocol = partial(DSMRProtocol, loop, TelegramParser(specification), + telegram_callback=telegram_callback, **kwargs) return protocol, serial_settings @@ -60,26 +51,22 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs): def create_dsmr_reader(port, dsmr_version, telegram_callback, loop=None): """Creates a DSMR asyncio protocol coroutine using serial port.""" protocol, serial_settings = create_dsmr_protocol( - dsmr_version, telegram_callback, loop=None - ) - serial_settings["url"] = port + dsmr_version, telegram_callback, loop=None) + serial_settings['url'] = port conn = create_serial_connection(loop, protocol, **serial_settings) return conn -def create_tcp_dsmr_reader( - host, port, dsmr_version, telegram_callback, loop=None, keep_alive_interval=None -): +def create_tcp_dsmr_reader(host, port, dsmr_version, + telegram_callback, loop=None, + keep_alive_interval=None): """Creates a DSMR asyncio protocol coroutine using TCP connection.""" if not loop: loop = asyncio.get_event_loop() protocol, _ = create_dsmr_protocol( - dsmr_version, - telegram_callback, - loop=loop, - keep_alive_interval=keep_alive_interval, - ) + dsmr_version, telegram_callback, loop=loop, + keep_alive_interval=keep_alive_interval) conn = loop.create_connection(protocol, host, port) return conn @@ -90,9 +77,8 @@ class DSMRProtocol(asyncio.Protocol): transport = None telegram_callback = None - def __init__( - self, loop, telegram_parser, telegram_callback=None, keep_alive_interval=None - ): + def __init__(self, loop, telegram_parser, + telegram_callback=None, keep_alive_interval=None): """Initialize class.""" self.loop = loop self.log = logging.getLogger(__name__) @@ -109,7 +95,7 @@ class DSMRProtocol(asyncio.Protocol): def connection_made(self, transport): """Just logging for now.""" self.transport = transport - self.log.debug("connected") + self.log.debug('connected') self._active = False if self.loop and self._keep_alive_interval: self.loop.call_later(self._keep_alive_interval, self.keep_alive) @@ -118,7 +104,7 @@ class DSMRProtocol(asyncio.Protocol): """Add incoming data to buffer.""" data = data.decode("latin1") self._active = True - self.log.debug("received data: %s", data) + self.log.debug('received data: %s', data) self.telegram_buffer.append(data) for telegram in self.telegram_buffer.get_all(): @@ -126,26 +112,26 @@ class DSMRProtocol(asyncio.Protocol): def keep_alive(self): if self._active: - self.log.debug("keep-alive checked") + self.log.debug('keep-alive checked') self._active = False if self.loop: self.loop.call_later(self._keep_alive_interval, self.keep_alive) else: - self.log.warning("keep-alive check failed") + self.log.warning('keep-alive check failed') if self.transport: self.transport.close() def connection_lost(self, exc): """Stop when connection is lost.""" if exc: - self.log.exception("disconnected due to exception", exc_info=exc) + self.log.exception('disconnected due to exception', exc_info=exc) else: - self.log.info("disconnected because of close/abort.") + self.log.info('disconnected because of close/abort.') self._closed.set() def handle_telegram(self, telegram): """Send off parsed telegram to handling callback.""" - self.log.debug("got telegram: %s", telegram) + self.log.debug('got telegram: %s', telegram) try: parsed_telegram = self.telegram_parser.parse(telegram) diff --git a/dsmr_parser/obis_name_mapping.py b/dsmr_parser/obis_name_mapping.py index 5a05f00..87c720d 100644 --- a/dsmr_parser/obis_name_mapping.py +++ b/dsmr_parser/obis_name_mapping.py @@ -8,57 +8,57 @@ This module contains a mapping of obis references to names. """ EN = { - obis.P1_MESSAGE_HEADER: "P1_MESSAGE_HEADER", - obis.P1_MESSAGE_TIMESTAMP: "P1_MESSAGE_TIMESTAMP", - obis.ELECTRICITY_IMPORTED_TOTAL: "ELECTRICITY_IMPORTED_TOTAL", - obis.ELECTRICITY_USED_TARIFF_1: "ELECTRICITY_USED_TARIFF_1", - obis.ELECTRICITY_USED_TARIFF_2: "ELECTRICITY_USED_TARIFF_2", - obis.ELECTRICITY_EXPORTED_TOTAL: "ELECTRICITY_EXPORTED_TOTAL", - obis.ELECTRICITY_DELIVERED_TARIFF_1: "ELECTRICITY_DELIVERED_TARIFF_1", - obis.ELECTRICITY_DELIVERED_TARIFF_2: "ELECTRICITY_DELIVERED_TARIFF_2", - obis.ELECTRICITY_ACTIVE_TARIFF: "ELECTRICITY_ACTIVE_TARIFF", - obis.EQUIPMENT_IDENTIFIER: "EQUIPMENT_IDENTIFIER", - obis.CURRENT_ELECTRICITY_USAGE: "CURRENT_ELECTRICITY_USAGE", - obis.CURRENT_ELECTRICITY_DELIVERY: "CURRENT_ELECTRICITY_DELIVERY", - obis.LONG_POWER_FAILURE_COUNT: "LONG_POWER_FAILURE_COUNT", - obis.SHORT_POWER_FAILURE_COUNT: "SHORT_POWER_FAILURE_COUNT", - obis.POWER_EVENT_FAILURE_LOG: "POWER_EVENT_FAILURE_LOG", - obis.VOLTAGE_SAG_L1_COUNT: "VOLTAGE_SAG_L1_COUNT", - obis.VOLTAGE_SAG_L2_COUNT: "VOLTAGE_SAG_L2_COUNT", - obis.VOLTAGE_SAG_L3_COUNT: "VOLTAGE_SAG_L3_COUNT", - obis.VOLTAGE_SWELL_L1_COUNT: "VOLTAGE_SWELL_L1_COUNT", - obis.VOLTAGE_SWELL_L2_COUNT: "VOLTAGE_SWELL_L2_COUNT", - obis.VOLTAGE_SWELL_L3_COUNT: "VOLTAGE_SWELL_L3_COUNT", - obis.INSTANTANEOUS_VOLTAGE_L1: "INSTANTANEOUS_VOLTAGE_L1", - obis.INSTANTANEOUS_VOLTAGE_L2: "INSTANTANEOUS_VOLTAGE_L2", - obis.INSTANTANEOUS_VOLTAGE_L3: "INSTANTANEOUS_VOLTAGE_L3", - obis.INSTANTANEOUS_CURRENT_L1: "INSTANTANEOUS_CURRENT_L1", - obis.INSTANTANEOUS_CURRENT_L2: "INSTANTANEOUS_CURRENT_L2", - obis.INSTANTANEOUS_CURRENT_L3: "INSTANTANEOUS_CURRENT_L3", - obis.TEXT_MESSAGE_CODE: "TEXT_MESSAGE_CODE", - obis.TEXT_MESSAGE: "TEXT_MESSAGE", - obis.DEVICE_TYPE: "DEVICE_TYPE", - obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE", - obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE", - obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: "INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE", - obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE", - obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE", - obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: "INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE", - obis.EQUIPMENT_IDENTIFIER_GAS: "EQUIPMENT_IDENTIFIER_GAS", - obis.HOURLY_GAS_METER_READING: "HOURLY_GAS_METER_READING", - obis.GAS_METER_READING: "GAS_METER_READING", - obis.ACTUAL_TRESHOLD_ELECTRICITY: "ACTUAL_TRESHOLD_ELECTRICITY", - obis.ACTUAL_SWITCH_POSITION: "ACTUAL_SWITCH_POSITION", - obis.VALVE_POSITION_GAS: "VALVE_POSITION_GAS", - obis.BELGIUM_HOURLY_GAS_METER_READING: "BELGIUM_HOURLY_GAS_METER_READING", - obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: "LUXEMBOURG_EQUIPMENT_IDENTIFIER", - obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: "LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL", - obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: "LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL", - obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: "SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL", - obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: "SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL", - obis.Q3D_EQUIPMENT_IDENTIFIER: "Q3D_EQUIPMENT_IDENTIFIER", - obis.Q3D_EQUIPMENT_STATE: "Q3D_EQUIPMENT_STATE", - obis.Q3D_EQUIPMENT_SERIALNUMBER: "Q3D_EQUIPMENT_SERIALNUMBER", + obis.P1_MESSAGE_HEADER: 'P1_MESSAGE_HEADER', + obis.P1_MESSAGE_TIMESTAMP: 'P1_MESSAGE_TIMESTAMP', + obis.ELECTRICITY_IMPORTED_TOTAL: 'ELECTRICITY_IMPORTED_TOTAL', + obis.ELECTRICITY_USED_TARIFF_1: 'ELECTRICITY_USED_TARIFF_1', + obis.ELECTRICITY_USED_TARIFF_2: 'ELECTRICITY_USED_TARIFF_2', + obis.ELECTRICITY_EXPORTED_TOTAL: 'ELECTRICITY_EXPORTED_TOTAL', + obis.ELECTRICITY_DELIVERED_TARIFF_1: 'ELECTRICITY_DELIVERED_TARIFF_1', + obis.ELECTRICITY_DELIVERED_TARIFF_2: 'ELECTRICITY_DELIVERED_TARIFF_2', + obis.ELECTRICITY_ACTIVE_TARIFF: 'ELECTRICITY_ACTIVE_TARIFF', + obis.EQUIPMENT_IDENTIFIER: 'EQUIPMENT_IDENTIFIER', + obis.CURRENT_ELECTRICITY_USAGE: 'CURRENT_ELECTRICITY_USAGE', + obis.CURRENT_ELECTRICITY_DELIVERY: 'CURRENT_ELECTRICITY_DELIVERY', + obis.LONG_POWER_FAILURE_COUNT: 'LONG_POWER_FAILURE_COUNT', + obis.SHORT_POWER_FAILURE_COUNT: 'SHORT_POWER_FAILURE_COUNT', + obis.POWER_EVENT_FAILURE_LOG: 'POWER_EVENT_FAILURE_LOG', + obis.VOLTAGE_SAG_L1_COUNT: 'VOLTAGE_SAG_L1_COUNT', + obis.VOLTAGE_SAG_L2_COUNT: 'VOLTAGE_SAG_L2_COUNT', + obis.VOLTAGE_SAG_L3_COUNT: 'VOLTAGE_SAG_L3_COUNT', + obis.VOLTAGE_SWELL_L1_COUNT: 'VOLTAGE_SWELL_L1_COUNT', + obis.VOLTAGE_SWELL_L2_COUNT: 'VOLTAGE_SWELL_L2_COUNT', + obis.VOLTAGE_SWELL_L3_COUNT: 'VOLTAGE_SWELL_L3_COUNT', + obis.INSTANTANEOUS_VOLTAGE_L1: 'INSTANTANEOUS_VOLTAGE_L1', + obis.INSTANTANEOUS_VOLTAGE_L2: 'INSTANTANEOUS_VOLTAGE_L2', + obis.INSTANTANEOUS_VOLTAGE_L3: 'INSTANTANEOUS_VOLTAGE_L3', + obis.INSTANTANEOUS_CURRENT_L1: 'INSTANTANEOUS_CURRENT_L1', + obis.INSTANTANEOUS_CURRENT_L2: 'INSTANTANEOUS_CURRENT_L2', + obis.INSTANTANEOUS_CURRENT_L3: 'INSTANTANEOUS_CURRENT_L3', + obis.TEXT_MESSAGE_CODE: 'TEXT_MESSAGE_CODE', + obis.TEXT_MESSAGE: 'TEXT_MESSAGE', + obis.DEVICE_TYPE: 'DEVICE_TYPE', + obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE', + obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE', + obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE', + obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE', + obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE', + obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE', + obis.EQUIPMENT_IDENTIFIER_GAS: 'EQUIPMENT_IDENTIFIER_GAS', + obis.HOURLY_GAS_METER_READING: 'HOURLY_GAS_METER_READING', + obis.GAS_METER_READING: 'GAS_METER_READING', + obis.ACTUAL_TRESHOLD_ELECTRICITY: 'ACTUAL_TRESHOLD_ELECTRICITY', + obis.ACTUAL_SWITCH_POSITION: 'ACTUAL_SWITCH_POSITION', + obis.VALVE_POSITION_GAS: 'VALVE_POSITION_GAS', + obis.BELGIUM_HOURLY_GAS_METER_READING: 'BELGIUM_HOURLY_GAS_METER_READING', + obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: 'LUXEMBOURG_EQUIPMENT_IDENTIFIER', + obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL', + obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL', + obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: 'SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL', + obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL', + obis.Q3D_EQUIPMENT_IDENTIFIER: 'Q3D_EQUIPMENT_IDENTIFIER', + obis.Q3D_EQUIPMENT_STATE: 'Q3D_EQUIPMENT_STATE', + obis.Q3D_EQUIPMENT_SERIALNUMBER: 'Q3D_EQUIPMENT_SERIALNUMBER', } REVERSE_EN = dict([(v, k) for k, v in EN.items()]) diff --git a/dsmr_parser/obis_references.py b/dsmr_parser/obis_references.py index 02e1e33..ad3bd98 100644 --- a/dsmr_parser/obis_references.py +++ b/dsmr_parser/obis_references.py @@ -6,76 +6,69 @@ refactored to full line signatures to maintain backwards compatibility. Might be refactored in a backwards incompatible way as soon as proper telegram objects are introduced. """ -P1_MESSAGE_HEADER = r"\d-\d:0\.2\.8.+?\r\n" -P1_MESSAGE_TIMESTAMP = r"\d-\d:1\.0\.0.+?\r\n" -ELECTRICITY_IMPORTED_TOTAL = r"\d-\d:1\.8\.0.+?\r\n" -ELECTRICITY_USED_TARIFF_1 = r"\d-\d:1\.8\.1.+?\r\n" -ELECTRICITY_USED_TARIFF_2 = r"\d-\d:1\.8\.2.+?\r\n" -ELECTRICITY_EXPORTED_TOTAL = r"\d-\d:2\.8\.0.+?\r\n" -ELECTRICITY_DELIVERED_TARIFF_1 = r"\d-\d:2\.8\.1.+?\r\n" -ELECTRICITY_DELIVERED_TARIFF_2 = r"\d-\d:2\.8\.2.+?\r\n" -ELECTRICITY_ACTIVE_TARIFF = r"\d-\d:96\.14\.0.+?\r\n" -EQUIPMENT_IDENTIFIER = r"\d-\d:96\.1\.1.+?\r\n" -CURRENT_ELECTRICITY_USAGE = r"\d-\d:1\.7\.0.+?\r\n" -CURRENT_ELECTRICITY_DELIVERY = r"\d-\d:2\.7\.0.+?\r\n" -LONG_POWER_FAILURE_COUNT = r"96\.7\.9.+?\r\n" -SHORT_POWER_FAILURE_COUNT = r"96\.7\.21.+?\r\n" -POWER_EVENT_FAILURE_LOG = r"99\.97\.0.+?\r\n" -VOLTAGE_SAG_L1_COUNT = r"\d-\d:32\.32\.0.+?\r\n" -VOLTAGE_SAG_L2_COUNT = r"\d-\d:52\.32\.0.+?\r\n" -VOLTAGE_SAG_L3_COUNT = r"\d-\d:72\.32\.0.+?\r\n" -VOLTAGE_SWELL_L1_COUNT = r"\d-\d:32\.36\.0.+?\r\n" -VOLTAGE_SWELL_L2_COUNT = r"\d-\d:52\.36\.0.+?\r\n" -VOLTAGE_SWELL_L3_COUNT = r"\d-\d:72\.36\.0.+?\r\n" -INSTANTANEOUS_VOLTAGE_L1 = r"\d-\d:32\.7\.0.+?\r\n" -INSTANTANEOUS_VOLTAGE_L2 = r"\d-\d:52\.7\.0.+?\r\n" -INSTANTANEOUS_VOLTAGE_L3 = r"\d-\d:72\.7\.0.+?\r\n" -INSTANTANEOUS_CURRENT_L1 = r"\d-\d:31\.7\.0.+?\r\n" -INSTANTANEOUS_CURRENT_L2 = r"\d-\d:51\.7\.0.+?\r\n" -INSTANTANEOUS_CURRENT_L3 = r"\d-\d:71\.7\.0.+?\r\n" -TEXT_MESSAGE_CODE = r"\d-\d:96\.13\.1.+?\r\n" -TEXT_MESSAGE = r"\d-\d:96\.13\.0.+?\r\n" -DEVICE_TYPE = r"\d-\d:24\.1\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE = r"\d-\d:21\.7\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE = r"\d-\d:41\.7\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE = r"\d-\d:61\.7\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE = r"\d-\d:22\.7\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE = r"\d-\d:42\.7\.0.+?\r\n" -INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE = r"\d-\d:62\.7\.0.+?\r\n" -EQUIPMENT_IDENTIFIER_GAS = r"\d-\d:96\.1\.0.+?\r\n" +P1_MESSAGE_HEADER = r'\d-\d:0\.2\.8.+?\r\n' +P1_MESSAGE_TIMESTAMP = r'\d-\d:1\.0\.0.+?\r\n' +ELECTRICITY_IMPORTED_TOTAL = r'\d-\d:1\.8\.0.+?\r\n' +ELECTRICITY_USED_TARIFF_1 = r'\d-\d:1\.8\.1.+?\r\n' +ELECTRICITY_USED_TARIFF_2 = r'\d-\d:1\.8\.2.+?\r\n' +ELECTRICITY_EXPORTED_TOTAL = r'\d-\d:2\.8\.0.+?\r\n' +ELECTRICITY_DELIVERED_TARIFF_1 = r'\d-\d:2\.8\.1.+?\r\n' +ELECTRICITY_DELIVERED_TARIFF_2 = r'\d-\d:2\.8\.2.+?\r\n' +ELECTRICITY_ACTIVE_TARIFF = r'\d-\d:96\.14\.0.+?\r\n' +EQUIPMENT_IDENTIFIER = r'\d-\d:96\.1\.1.+?\r\n' +CURRENT_ELECTRICITY_USAGE = r'\d-\d:1\.7\.0.+?\r\n' +CURRENT_ELECTRICITY_DELIVERY = r'\d-\d:2\.7\.0.+?\r\n' +LONG_POWER_FAILURE_COUNT = r'96\.7\.9.+?\r\n' +SHORT_POWER_FAILURE_COUNT = r'96\.7\.21.+?\r\n' +POWER_EVENT_FAILURE_LOG = r'99\.97\.0.+?\r\n' +VOLTAGE_SAG_L1_COUNT = r'\d-\d:32\.32\.0.+?\r\n' +VOLTAGE_SAG_L2_COUNT = r'\d-\d:52\.32\.0.+?\r\n' +VOLTAGE_SAG_L3_COUNT = r'\d-\d:72\.32\.0.+?\r\n' +VOLTAGE_SWELL_L1_COUNT = r'\d-\d:32\.36\.0.+?\r\n' +VOLTAGE_SWELL_L2_COUNT = r'\d-\d:52\.36\.0.+?\r\n' +VOLTAGE_SWELL_L3_COUNT = r'\d-\d:72\.36\.0.+?\r\n' +INSTANTANEOUS_VOLTAGE_L1 = r'\d-\d:32\.7\.0.+?\r\n' +INSTANTANEOUS_VOLTAGE_L2 = r'\d-\d:52\.7\.0.+?\r\n' +INSTANTANEOUS_VOLTAGE_L3 = r'\d-\d:72\.7\.0.+?\r\n' +INSTANTANEOUS_CURRENT_L1 = r'\d-\d:31\.7\.0.+?\r\n' +INSTANTANEOUS_CURRENT_L2 = r'\d-\d:51\.7\.0.+?\r\n' +INSTANTANEOUS_CURRENT_L3 = r'\d-\d:71\.7\.0.+?\r\n' +TEXT_MESSAGE_CODE = r'\d-\d:96\.13\.1.+?\r\n' +TEXT_MESSAGE = r'\d-\d:96\.13\.0.+?\r\n' +DEVICE_TYPE = r'\d-\d:24\.1\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE = r'\d-\d:21\.7\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE = r'\d-\d:41\.7\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE = r'\d-\d:61\.7\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE = r'\d-\d:22\.7\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE = r'\d-\d:42\.7\.0.+?\r\n' +INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE = r'\d-\d:62\.7\.0.+?\r\n' +EQUIPMENT_IDENTIFIER_GAS = r'\d-\d:96\.1\.0.+?\r\n' # TODO differences between gas meter readings in v3 and lower and v4 and up -HOURLY_GAS_METER_READING = r"\d-\d:24\.2\.1.+?\r\n" -GAS_METER_READING = r"\d-\d:24\.3\.0.+?\r\n.+?\r\n" -ACTUAL_TRESHOLD_ELECTRICITY = r"\d-\d:17\.0\.0.+?\r\n" -ACTUAL_SWITCH_POSITION = r"\d-\d:96\.3\.10.+?\r\n" -VALVE_POSITION_GAS = r"\d-\d:24\.4\.0.+?\r\n" +HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.1.+?\r\n' +GAS_METER_READING = r'\d-\d:24\.3\.0.+?\r\n.+?\r\n' +ACTUAL_TRESHOLD_ELECTRICITY = r'\d-\d:17\.0\.0.+?\r\n' +ACTUAL_SWITCH_POSITION = r'\d-\d:96\.3\.10.+?\r\n' +VALVE_POSITION_GAS = r'\d-\d:24\.4\.0.+?\r\n' # TODO 17.0.0 # TODO 96.3.10 -ELECTRICITY_USED_TARIFF_ALL = (ELECTRICITY_USED_TARIFF_1, ELECTRICITY_USED_TARIFF_2) +ELECTRICITY_USED_TARIFF_ALL = ( + ELECTRICITY_USED_TARIFF_1, + ELECTRICITY_USED_TARIFF_2 +) ELECTRICITY_DELIVERED_TARIFF_ALL = ( ELECTRICITY_DELIVERED_TARIFF_1, - ELECTRICITY_DELIVERED_TARIFF_2, + ELECTRICITY_DELIVERED_TARIFF_2 ) # Alternate codes for foreign countries. -BELGIUM_HOURLY_GAS_METER_READING = ( - r"\d-\d:24\.2\.3.+?\r\n" # Different code, same format. -) -LUXEMBOURG_EQUIPMENT_IDENTIFIER = r"\d-\d:42\.0\.0.+?\r\n" # Logical device name -LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = ( - r"\d-\d:1\.8\.0.+?\r\n" # Total imported energy register (P+) -) -LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = ( - r"\d-\d:2\.8\.0.+?\r\n" # Total exported energy register (P-) -) -SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL = ( - r"\d-\d:1\.8\.0.+?\r\n" # Total imported energy register (P+) -) -SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = ( - r"\d-\d:2\.8\.0.+?\r\n" # Total exported energy register (P-) -) -Q3D_EQUIPMENT_IDENTIFIER = r"\d-\d:0\.0\.0.+?\r\n" # Logical device name -Q3D_EQUIPMENT_STATE = r"\d-\d:96\.5\.5.+?\r\n" # Device state (hexadecimal) -Q3D_EQUIPMENT_SERIALNUMBER = r"\d-\d:96\.1\.255.+?\r\n" # Device Serialnumber +BELGIUM_HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.3.+?\r\n' # Different code, same format. +LUXEMBOURG_EQUIPMENT_IDENTIFIER = r'\d-\d:42\.0\.0.+?\r\n' # Logical device name +LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+) +LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-) +SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+) +SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-) +Q3D_EQUIPMENT_IDENTIFIER = r'\d-\d:0\.0\.0.+?\r\n' # Logical device name +Q3D_EQUIPMENT_STATE = r'\d-\d:96\.5\.5.+?\r\n' # Device state (hexadecimal) +Q3D_EQUIPMENT_SERIALNUMBER = r'\d-\d:96\.1\.255.+?\r\n' # Device Serialnumber diff --git a/dsmr_parser/telegram_specifications.py b/dsmr_parser/telegram_specifications.py index 77ea49e..00d0c31 100644 --- a/dsmr_parser/telegram_specifications.py +++ b/dsmr_parser/telegram_specifications.py @@ -2,18 +2,9 @@ from decimal import Decimal from copy import deepcopy from dsmr_parser import obis_references as obis -from dsmr_parser.parsers import ( - CosemParser, - ValueParser, - MBusParser, - ProfileGenericParser, -) +from dsmr_parser.parsers import CosemParser, ValueParser, MBusParser, ProfileGenericParser from dsmr_parser.value_types import timestamp -from dsmr_parser.profile_generic_specifications import ( - BUFFER_TYPES, - PG_HEAD_PARSERS, - PG_UNIDENTIFIED_BUFFERTYPE_PARSERS, -) +from dsmr_parser.profile_generic_specifications import BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS """ dsmr_parser.telegram_specifications @@ -24,8 +15,8 @@ how the telegram lines are parsed. """ V2_2 = { - "checksum_support": False, - "objects": { + 'checksum_support': False, + 'objects': { obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), obis.ELECTRICITY_USED_TARIFF_1: CosemParser(ValueParser(Decimal)), obis.ELECTRICITY_USED_TARIFF_2: CosemParser(ValueParser(Decimal)), @@ -50,14 +41,14 @@ V2_2 = { ValueParser(str), # unit, position 5 ValueParser(Decimal), # meter reading, position 6 ), - }, + } } V3 = V2_2 V4 = { - "checksum_support": True, - "objects": { + 'checksum_support': True, + 'objects': { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), @@ -70,9 +61,10 @@ V4 = { obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.SHORT_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), obis.LONG_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), - obis.POWER_EVENT_FAILURE_LOG: ProfileGenericParser( - BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS - ), + obis.POWER_EVENT_FAILURE_LOG: + ProfileGenericParser(BUFFER_TYPES, + PG_HEAD_PARSERS, + PG_UNIDENTIFIED_BUFFERTYPE_PARSERS), obis.VOLTAGE_SAG_L1_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L2_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L3_COUNT: CosemParser(ValueParser(int)), @@ -93,14 +85,15 @@ V4 = { obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: CosemParser(ValueParser(Decimal)), obis.EQUIPMENT_IDENTIFIER_GAS: CosemParser(ValueParser(str)), obis.HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), ValueParser(Decimal) - ), - }, + ValueParser(timestamp), + ValueParser(Decimal) + ) + } } V5 = { - "checksum_support": True, - "objects": { + 'checksum_support': True, + 'objects': { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), @@ -114,9 +107,10 @@ V5 = { obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.LONG_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), obis.SHORT_POWER_FAILURE_COUNT: CosemParser(ValueParser(int)), - obis.POWER_EVENT_FAILURE_LOG: ProfileGenericParser( - BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS - ), + obis.POWER_EVENT_FAILURE_LOG: + ProfileGenericParser(BUFFER_TYPES, + PG_HEAD_PARSERS, + PG_UNIDENTIFIED_BUFFERTYPE_PARSERS), obis.VOLTAGE_SAG_L1_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L2_COUNT: CosemParser(ValueParser(int)), obis.VOLTAGE_SAG_L3_COUNT: CosemParser(ValueParser(int)), @@ -139,46 +133,38 @@ V5 = { obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: CosemParser(ValueParser(Decimal)), obis.EQUIPMENT_IDENTIFIER_GAS: CosemParser(ValueParser(str)), obis.HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), ValueParser(Decimal) - ), - }, + ValueParser(timestamp), + ValueParser(Decimal) + ) + } } ALL = (V2_2, V3, V4, V5) BELGIUM_FLUVIUS = deepcopy(V5) -BELGIUM_FLUVIUS["objects"].update( - { - obis.BELGIUM_HOURLY_GAS_METER_READING: MBusParser( - ValueParser(timestamp), ValueParser(Decimal) - ) - } -) +BELGIUM_FLUVIUS['objects'].update({ + obis.BELGIUM_HOURLY_GAS_METER_READING: MBusParser( + ValueParser(timestamp), + ValueParser(Decimal) + ) +}) LUXEMBOURG_SMARTY = deepcopy(V5) -LUXEMBOURG_SMARTY["objects"].update( - { - obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), - obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser( - ValueParser(Decimal) - ), - obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser( - ValueParser(Decimal) - ), - } -) +LUXEMBOURG_SMARTY['objects'].update({ + obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), + obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), +}) # Source: https://www.energiforetagen.se/globalassets/energiforetagen/det-erbjuder-vi/kurser-och-konferenser/elnat/branschrekommendation-lokalt-granssnitt-v2_0-201912.pdf SWEDEN = { - "checksum_support": True, - "objects": { + 'checksum_support': True, + 'objects': { obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), - obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser( - ValueParser(Decimal) - ), + obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)), obis.CURRENT_ELECTRICITY_USAGE: CosemParser(ValueParser(Decimal)), obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: CosemParser(ValueParser(Decimal)), @@ -193,7 +179,7 @@ SWEDEN = { obis.INSTANTANEOUS_CURRENT_L1: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_CURRENT_L2: CosemParser(ValueParser(Decimal)), obis.INSTANTANEOUS_CURRENT_L3: CosemParser(ValueParser(Decimal)), - }, + } } Q3D = { From 6866e7d585bca78839e881737c5d4827710cc825 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Sun, 14 Nov 2021 21:52:38 +0000 Subject: [PATCH 12/16] leaving version as it was --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3852064..7ad7c68 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup( author_email='nigel@nldr.net', license='MIT', url='https://github.com/ndokter/dsmr_parser', - version='0.31', + version='0.30', packages=find_packages(exclude=('test', 'test.*')), install_requires=[ 'pyserial>=3,<4', From de4dc2ec9823f8ee4c83af5cc0cc76a514638878 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Sun, 14 Nov 2021 22:05:27 +0000 Subject: [PATCH 13/16] Q3D COM-1 test telegrams --- test/example_telegrams.py | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/example_telegrams.py b/test/example_telegrams.py index f74ed16..c59280c 100644 --- a/test/example_telegrams.py +++ b/test/example_telegrams.py @@ -128,3 +128,43 @@ TELEGRAM_V5 = ( '0-2:96.1.0()\r\n' '!6EEE\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 = ( # Easymeter an Hauptstromzähler + '/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 = ( # Easymeter an Wärmepumpe + '/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' +) From 7a89d6e97be0a2798d33dfdae16398efa6aaa87d Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Sun, 14 Nov 2021 22:20:00 +0000 Subject: [PATCH 14/16] re-adding ascii decoding at telegram level --- dsmr_parser/clients/protocol.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py index 208ed1b..a9a9205 100644 --- a/dsmr_parser/clients/protocol.py +++ b/dsmr_parser/clients/protocol.py @@ -134,6 +134,7 @@ class DSMRProtocol(asyncio.Protocol): self.log.debug('got telegram: %s', telegram) try: + telegram = telegram.decode("ascii") parsed_telegram = self.telegram_parser.parse(telegram) except InvalidChecksumError as e: self.log.warning(str(e)) From 26ac27c347c39d473c7f034eab2bdb295927d2df Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Sun, 14 Nov 2021 22:29:37 +0000 Subject: [PATCH 15/16] re-adding ascii decoding at telegram level --- dsmr_parser/clients/protocol.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py index a9a9205..c2d032f 100644 --- a/dsmr_parser/clients/protocol.py +++ b/dsmr_parser/clients/protocol.py @@ -134,7 +134,10 @@ class DSMRProtocol(asyncio.Protocol): self.log.debug('got telegram: %s', telegram) try: - telegram = telegram.decode("ascii") + # we accepted 8-bit at transport level (e.g. tcp) + telegram_data = telegram.encode("latin1") + # we need to ensure 7-bit at telegram level (IEC 646 required in section 5.4 of IEC 62056-21) + telegram = telegram_data.decode("ascii") parsed_telegram = self.telegram_parser.parse(telegram) except InvalidChecksumError as e: self.log.warning(str(e)) From 45cc88942a6394a1bca9dd984b86903cfda97690 Mon Sep 17 00:00:00 2001 From: Gunnar Klauberg Date: Sun, 21 Nov 2021 11:25:05 +0000 Subject: [PATCH 16/16] latin-1 changes folded into data_received --- dsmr_parser/clients/protocol.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py index c2d032f..bbdcff6 100644 --- a/dsmr_parser/clients/protocol.py +++ b/dsmr_parser/clients/protocol.py @@ -102,12 +102,16 @@ class DSMRProtocol(asyncio.Protocol): def data_received(self, data): """Add incoming data to buffer.""" + + # accept latin-1 (8-bit) on the line, to allow for non-ascii transport or padding data = data.decode("latin1") self._active = True self.log.debug('received data: %s', data) self.telegram_buffer.append(data) for telegram in self.telegram_buffer.get_all(): + # ensure actual telegram is ascii (7-bit) only (IEC 646 required in section 5.4 of IEC 62056-21) + telegram = telegram.encode("latin1").decode("ascii") self.handle_telegram(telegram) def keep_alive(self): @@ -134,10 +138,6 @@ class DSMRProtocol(asyncio.Protocol): self.log.debug('got telegram: %s', telegram) try: - # we accepted 8-bit at transport level (e.g. tcp) - telegram_data = telegram.encode("latin1") - # we need to ensure 7-bit at telegram level (IEC 646 required in section 5.4 of IEC 62056-21) - telegram = telegram_data.decode("ascii") parsed_telegram = self.telegram_parser.parse(telegram) except InvalidChecksumError as e: self.log.warning(str(e))