diff --git a/.flake8 b/.flake8 index 29f536a4f..e1de50058 100644 --- a/.flake8 +++ b/.flake8 @@ -1,6 +1,6 @@ [flake8] select = B,C,E,F,I,S,W ignore = B902,B903,E501,B950,E203,S311,W503 -exclude = .venv/*,docs/*,*/migrations/*,*/tests/*,dsmrreader/*,dsmr_plugins/modules/* +exclude = .venv/*,docs/*,*/migrations/*,*/tests/*,dsmrreader/*,dsmr_plugins/modules/*,dsmr_parser/* max-complexity = 10 max-line-length = 120 diff --git a/docs/reference/changelog.rst b/docs/reference/changelog.rst index 5ecce4baf..49be969db 100644 --- a/docs/reference/changelog.rst +++ b/docs/reference/changelog.rst @@ -39,6 +39,7 @@ v5.10.0 - January 2023 - ``Changed`` [`#1725 `_] The value of ``DSMRREADER_REMOTE_DATALOGGER_INPUT_METHOD`` is now restricted to: ``DEBUG``, ``WARNING`` or ``ERROR`` - ``Changed`` [`#1725 `_] The value of ``DSMRREADER_LOGLEVEL`` is now restricted to: ``serial`` or ``ipv4`` +- ``Changed`` [`#1794 `_] [`#1764 `_] Updated dsmr_parser (mostly) with latest version v5.9.0 - November 2022 diff --git a/dsmr_datalogger/services/datalogger.py b/dsmr_datalogger/services/datalogger.py index 8be52a498..da2c44aae 100644 --- a/dsmr_datalogger/services/datalogger.py +++ b/dsmr_datalogger/services/datalogger.py @@ -235,15 +235,15 @@ def _get_dsmrreader_mapping(version: int) -> Dict: if version == DataloggerSettings.DSMR_BELGIUM_FLUVIUS: mapping.update( { - obis_references.BELGIUM_HOURLY_GAS_METER_READING: SPLIT_GAS_FIELD, + obis_references.BELGIUM_MBUS_WILDCARD_METER_READING2: SPLIT_GAS_FIELD, } ) if version == DataloggerSettings.DSMR_LUXEMBOURG_SMARTY: mapping.update( { - obis_references.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: "electricity_delivered_1", - obis_references.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: "electricity_returned_1", + obis_references.ELECTRICITY_IMPORTED_TOTAL: "electricity_delivered_1", + obis_references.ELECTRICITY_EXPORTED_TOTAL: "electricity_returned_1", } ) diff --git a/dsmr_parser/obis_name_mapping.py b/dsmr_parser/obis_name_mapping.py index 8f7148b03..f0703fb6d 100644 --- a/dsmr_parser/obis_name_mapping.py +++ b/dsmr_parser/obis_name_mapping.py @@ -13,6 +13,7 @@ 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", @@ -49,12 +50,34 @@ 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.BELGIUM_VERSION_INFORMATION: "BELGIUM_VERSION_INFORMATION", + obis.BELGIUM_EQUIPMENT_IDENTIFIER: "BELGIUM_EQUIPMENT_IDENTIFIER", + obis.BELGIUM_CURRENT_AVERAGE_DEMAND: "BELGIUM_CURRENT_AVERAGE_DEMAND", + obis.BELGIUM_MAXIMUM_DEMAND_MONTH: "BELGIUM_MAXIMUM_DEMAND_MONTH", + obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS: "BELGIUM_MAXIMUM_DEMAND_13_MONTHS", + obis.BELGIUM_MAX_POWER_PER_PHASE: "BELGIUM_MAX_POWER_PER_PHASE", + obis.BELGIUM_MAX_CURRENT_PER_PHASE: "BELGIUM_MAX_CURRENT_PER_PHASE", + obis.BELGIUM_MBUS1_DEVICE_TYPE: "BELGIUM_MBUS1_DEVICE_TYPE", + obis.BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER: "BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER", + obis.BELGIUM_MBUS1_VALVE_POSITION: "BELGIUM_MBUS1_VALVE_POSITION", + obis.BELGIUM_MBUS1_METER_READING1: "BELGIUM_MBUS1_METER_READING1", + obis.BELGIUM_MBUS1_METER_READING2: "BELGIUM_MBUS1_METER_READING2", + obis.BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER: "BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER", + obis.BELGIUM_MBUS2_VALVE_POSITION: "BELGIUM_MBUS2_VALVE_POSITION", + obis.BELGIUM_MBUS2_METER_READING1: "BELGIUM_MBUS2_METER_READING1", + obis.BELGIUM_MBUS2_METER_READING2: "BELGIUM_MBUS2_METER_READING2", + obis.BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER: "BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER", + obis.BELGIUM_MBUS3_VALVE_POSITION: "BELGIUM_MBUS3_VALVE_POSITION", + obis.BELGIUM_MBUS3_METER_READING1: "BELGIUM_MBUS3_METER_READING1", + obis.BELGIUM_MBUS3_METER_READING2: "BELGIUM_MBUS3_METER_READING2", + obis.BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER: "BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER", + obis.BELGIUM_MBUS4_VALVE_POSITION: "BELGIUM_MBUS4_VALVE_POSITION", + obis.BELGIUM_MBUS4_METER_READING1: "BELGIUM_MBUS4_METER_READING1", + obis.BELGIUM_MBUS4_METER_READING2: "BELGIUM_MBUS4_METER_READING2", 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 dd73c7ada..c4d5f1006 100644 --- a/dsmr_parser/obis_references.py +++ b/dsmr_parser/obis_references.py @@ -8,11 +8,18 @@ """ 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" +CURRENT_REACTIVE_EXPORTED = r"\d-\d:3\.7\.0.+?\r\n" +ELECTRICITY_REACTIVE_IMPORTED_TOTAL = r"\d-\d:3\.8\.0.+?\r\n" +ELECTRICITY_REACTIVE_IMPORTED_TARIFF_1 = r"\d-\d:3\.8\.1.+?\r\n" +ELECTRICITY_REACTIVE_IMPORTED_TARIFF_2 = r"\d-\d:3\.8\.2.+?\r\n" +CURRENT_REACTIVE_IMPORTED = r"\d-\d:4\.7\.0.+?\r\n" +ELECTRICITY_REACTIVE_EXPORTED_TOTAL = r"\d-\d:4\.8\.0.+?\r\n" +ELECTRICITY_REACTIVE_EXPORTED_TARIFF_1 = r"\d-\d:4\.8\.1.+?\r\n" +ELECTRICITY_REACTIVE_EXPORTED_TARIFF_2 = r"\d-\d:4\.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" @@ -58,20 +65,66 @@ 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 = ( +# International generalized additions +ELECTRICITY_IMPORTED_TOTAL = ( r"\d-\d:1\.8\.0.+?\r\n" # Total imported energy register (P+) ) -LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = ( +ELECTRICITY_EXPORTED_TOTAL = ( 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+) + +# International non generalized additions (country specific) / risk for necessary refactoring +BELGIUM_VERSION_INFORMATION = r"\d-\d:96\.1\.4.+?\r\n" +BELGIUM_EQUIPMENT_IDENTIFIER = r"\d-0:96\.1\.1.+?\r\n" +BELGIUM_CURRENT_AVERAGE_DEMAND = r"\d-\d:1\.4\.0.+?\r\n" +BELGIUM_MAXIMUM_DEMAND_MONTH = r"\d-\d:1\.6\.0.+?\r\n" +BELGIUM_MAXIMUM_DEMAND_13_MONTHS = r"\d-\d:98\.1\.0.+?\r\n" +BELGIUM_MAX_POWER_PER_PHASE = ( + r"\d-\d:17\.0\.0.+?\r\n" # Applicable when power limitation is active ) -SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = ( - r"\d-\d:2\.8\.0.+?\r\n" # Total exported energy register (P-) +BELGIUM_MAX_CURRENT_PER_PHASE = ( + r"\d-\d:31\.4\.0.+?\r\n" # Applicable when current limitation is active ) + +# Multiple 'slaves' can be linked to the main device. +# Mostly MBUS1 = GAS METER with values on 24.2.3 +# While WATER METER reports it's values on 24.2.1 +# The GAS METER also reports its valve state on 24.4.0 +# Dev type for gas = 7 and water = 8 +BELGIUM_MBUS1_DEVICE_TYPE = r"\d-1:24\.1\.0.+?\r\n" +BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER = r"\d-1:96\.1\.1.+?\r\n" +BELGIUM_MBUS1_VALVE_POSITION = r"\d-1:24\.4\.0.+?\r\n" +BELGIUM_MBUS1_METER_READING1 = r"\d-1:24\.2\.1.+?\r\n" +BELGIUM_MBUS1_METER_READING2 = r"\d-1:24\.2\.3.+?\r\n" + +BELGIUM_MBUS2_DEVICE_TYPE = r"\d-2:24\.1\.0.+?\r\n" +BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER = r"\d-2:96\.1\.1.+?\r\n" +BELGIUM_MBUS2_VALVE_POSITION = r"\d-2:24\.4\.0.+?\r\n" +BELGIUM_MBUS2_METER_READING1 = r"\d-2:24\.2\.1.+?\r\n" +BELGIUM_MBUS2_METER_READING2 = r"\d-2:24\.2\.3.+?\r\n" + +BELGIUM_MBUS3_DEVICE_TYPE = r"\d-3:24\.1\.0.+?\r\n" +BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER = r"\d-3:96\.1\.1.+?\r\n" +BELGIUM_MBUS3_VALVE_POSITION = r"\d-3:24\.4\.0.+?\r\n" +BELGIUM_MBUS3_METER_READING1 = r"\d-3:24\.2\.1.+?\r\n" +BELGIUM_MBUS3_METER_READING2 = r"\d-3:24\.2\.3.+?\r\n" + +BELGIUM_MBUS4_DEVICE_TYPE = r"\d-4:24\.1\.0.+?\r\n" +BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER = r"\d-4:96\.1\.1.+?\r\n" +BELGIUM_MBUS4_VALVE_POSITION = r"\d-4:24\.4\.0.+?\r\n" +BELGIUM_MBUS4_METER_READING1 = r"\d-4:24\.2\.1.+?\r\n" +BELGIUM_MBUS4_METER_READING2 = r"\d-4:24\.2\.3.+?\r\n" + +# DSMR-reader legacy. Should eventually be dropped or selected automatically in a future release. +BELGIUM_MBUS_WILDCARD_DEVICE_TYPE = r"\d-\d:24\.1\.0.+?\r\n" +BELGIUM_MBUS_WILDCARD_EQUIPMENT_IDENTIFIER = r"\d-\d:96\.1\.1.+?\r\n" +BELGIUM_MBUS_WILDCARD_VALVE_POSITION = r"\d-\d:24\.4\.0.+?\r\n" +BELGIUM_MBUS_WILDCARD_METER_READING1 = r"\d-\d:24\.2\.1.+?\r\n" +BELGIUM_MBUS_WILDCARD_METER_READING2 = r"\d-\d:24\.2\.3.+?\r\n" + + +LUXEMBOURG_EQUIPMENT_IDENTIFIER = r"\d-\d:42\.0\.0.+?\r\n" # Logical device name + +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/objects.py b/dsmr_parser/objects.py index 66097bec0..6fde40896 100644 --- a/dsmr_parser/objects.py +++ b/dsmr_parser/objects.py @@ -114,6 +114,53 @@ def to_json(self): return json.dumps(output) +class MBusObjectPeak(DSMRObject): + @property + def datetime(self): + return self.values[0]["value"] + + @property + def occurred(self): + return self.values[1]["value"] + + @property + def value(self): + return self.values[2]["value"] + + @property + def unit(self): + return self.values[2]["unit"] + + def __str__(self): + output = "{}\t[{}] at {} occurred {}".format( + str(self.value), + str(self.unit), + str(self.datetime.astimezone().isoformat()), + str(self.occurred.astimezone().isoformat()), + ) + return output + + def to_json(self): + timestamp = self.datetime + if isinstance(self.datetime, datetime.datetime): + timestamp = self.datetime.astimezone().isoformat() + timestamp_occurred = self.occurred + if isinstance(self.occurred, datetime.datetime): + timestamp_occurred = self.occurred.astimezone().isoformat() + value = self.value + if isinstance(self.value, datetime.datetime): + value = self.value.astimezone().isoformat() + if isinstance(self.value, Decimal): + value = float(self.value) + output = { + "datetime": timestamp, + "occurred": timestamp_occurred, + "value": value, + "unit": self.unit, + } + return json.dumps(output) + + class CosemObject(DSMRObject): @property def value(self): diff --git a/dsmr_parser/parsers.py b/dsmr_parser/parsers.py index 61940f9e0..3bfb6621f 100644 --- a/dsmr_parser/parsers.py +++ b/dsmr_parser/parsers.py @@ -1,10 +1,21 @@ import logging import re +from binascii import unhexlify from ctypes import c_ushort +from decimal import Decimal -from dsmr_parser.objects import MBusObject, CosemObject, ProfileGenericObject +from dlms_cosem.connection import XDlmsApduFactory +from dlms_cosem.protocol.xdlms import GeneralGlobalCipher + +from dsmr_parser.objects import ( + MBusObject, + MBusObjectPeak, + CosemObject, + ProfileGenericObject, +) from dsmr_parser.exceptions import ParseError, InvalidChecksumError +from dsmr_parser.value_types import timestamp logger = logging.getLogger(__name__) @@ -22,24 +33,71 @@ def __init__(self, telegram_specification, apply_checksum_validation=True): self.telegram_specification = telegram_specification self.apply_checksum_validation = apply_checksum_validation - def parse(self, telegram_data): + def parse( + self, telegram_data, encryption_key="", authentication_key="" + ): # noqa: C901 """ Parse telegram from string to dict. - The telegram str type makes python 2.x integration easier. :param str telegram_data: full telegram from start ('/') to checksum ('!ABCD') including line endings in between the telegram's lines + :param str encryption_key: encryption key + :param str authentication_key: authentication key :rtype: dict :returns: Shortened example: { .. + r'\d-\d:96\.1\.1.+?\r\n': , # EQUIPMENT_IDENTIFIER + r'\d-\d:1\.8\.1.+?\r\n': , # ELECTRICITY_USED_TARIFF_1 + r'\d-\d:24\.3\.0.+?\r\n.+?\r\n': , # GAS_METER_READING .. } :raises ParseError: :raises InvalidChecksumError: """ + if "general_global_cipher" in self.telegram_specification: + if self.telegram_specification["general_global_cipher"]: + enc_key = unhexlify(encryption_key) + auth_key = unhexlify(authentication_key) + telegram_data = unhexlify(telegram_data) + apdu = XDlmsApduFactory.apdu_from_bytes(apdu_bytes=telegram_data) + if apdu.security_control.security_suite != 0: + logger.warning("Untested security suite") + if ( + apdu.security_control.authenticated + and not apdu.security_control.encrypted + ): + logger.warning("Untested authentication only") + if ( + not apdu.security_control.authenticated + and not apdu.security_control.encrypted + ): + logger.warning("Untested not encrypted or authenticated") + if apdu.security_control.compressed: + logger.warning("Untested compression") + if apdu.security_control.broadcast_key: + logger.warning("Untested broadcast key") + telegram_data = apdu.to_plain_apdu(enc_key, auth_key).decode("ascii") + else: + try: + if unhexlify(telegram_data[0:2])[0] == GeneralGlobalCipher.TAG: + raise RuntimeError( + "Looks like a general_global_cipher frame " + "but telegram specification is not matching!" + ) + except Exception: + pass + else: + try: + if unhexlify(telegram_data[0:2])[0] == GeneralGlobalCipher.TAG: + raise RuntimeError( + "Looks like a general_global_cipher frame but telegram specification is not matching!" + ) + except Exception: + pass + if ( self.apply_checksum_validation and self.telegram_specification["checksum_support"] @@ -49,7 +107,7 @@ def parse(self, telegram_data): telegram = {} for signature, parser in self.telegram_specification["objects"].items(): - # DSMR-reader #778: We might hit the same pattern multiple times. The last one matched will be leading. + # DSMR-reader legacy (#778): We might hit the same pattern multiple times. The last one matched will be leading. matches = re.findall(signature, telegram_data, re.DOTALL) for current_match in matches: @@ -90,15 +148,11 @@ def validate_checksum(telegram): calculated_crc = TelegramParser.crc16(checksum_contents.group(0)) expected_crc = int(checksum_hex.group(0), base=16) - calculated_crc_hex = "{:0>4}".format(hex(calculated_crc)[2:].upper()) - expected_crc_hex = "{:0>4}".format(hex(expected_crc)[2:].upper()) if calculated_crc != expected_crc: raise InvalidChecksumError( - "Invalid telegram CRC. The calculated checksum '{}' ({}) does not match the " - "telegram checksum '{}' ({})".format( - calculated_crc, calculated_crc_hex, expected_crc, expected_crc_hex - ) + "Invalid telegram. The CRC checksum '{}' does not match the " + "expected '{}'".format(calculated_crc, expected_crc) ) @staticmethod @@ -113,7 +167,7 @@ def crc16(telegram): if len(TelegramParser.crc16_tab) == 0: for i in range(0, 256): crc = c_ushort(i).value - for _j in range(0, 8): + for j in range(0, 8): if crc & 0x0001: crc = c_ushort(crc >> 1).value ^ 0xA001 else: @@ -181,6 +235,43 @@ def parse(self, line): return MBusObject(self._parse(line)) +class MaxDemandParser(DSMRObjectParser): + """ + Max demand history parser. + + These are lines with multiple values. Each containing 2 timestamps and a value + + Line format: + 'ID (Count) (ID) (ID) (TST) (TST) (Mv1*U1)' + + 1 2 3 4 5 6 7 + + 1) OBIS Reduced ID-code + 2) Amount of values in the response + 3) ID of the source + 4) ^^ + 5) Time Stamp (TST) of the month + 6) Time Stamp (TST) when the max demand occured + 6) Measurement value 1 (most recent entry of buffer attribute without unit) + 7) Unit of measurement values (Unit of capture objects attribute) + """ + + def parse(self, line): + pattern = re.compile(r"((?<=\()[0-9a-zA-Z\.\*\-\:]{0,}(?=\)))") + values = re.findall(pattern, line) + + objects = [] + + count = int(values[0]) + for i in range(1, count + 1): + timestamp_month = ValueParser(timestamp).parse(values[i * 3 + 1]) + timestamp_occurred = ValueParser(timestamp).parse(values[i * 3 + 1]) + value = ValueParser(Decimal).parse(values[i * 3 + 2]) + objects.append(MBusObjectPeak([timestamp_month, timestamp_occurred, value])) + + return objects + + class CosemParser(DSMRObjectParser): """ Cosem object parser. diff --git a/dsmr_parser/telegram_specifications.py b/dsmr_parser/telegram_specifications.py index 5991cd4e9..6274dcb5a 100644 --- a/dsmr_parser/telegram_specifications.py +++ b/dsmr_parser/telegram_specifications.py @@ -7,6 +7,7 @@ ValueParser, MBusParser, ProfileGenericParser, + MaxDemandParser, ) from dsmr_parser.value_types import timestamp from dsmr_parser.profile_generic_specifications import ( @@ -146,26 +147,96 @@ ALL = (V2_2, V3, V4, V5) - -BELGIUM_FLUVIUS = deepcopy(V5) -BELGIUM_FLUVIUS["objects"].update( - { - obis.BELGIUM_HOURLY_GAS_METER_READING: MBusParser( +BELGIUM_FLUVIUS = { + "checksum_support": True, + "objects": { + obis.BELGIUM_VERSION_INFORMATION: CosemParser(ValueParser(str)), + obis.EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), + obis.ELECTRICITY_USED_TARIFF_1: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_USED_TARIFF_2: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_DELIVERED_TARIFF_1: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_DELIVERED_TARIFF_2: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_ACTIVE_TARIFF: CosemParser(ValueParser(str)), + obis.BELGIUM_CURRENT_AVERAGE_DEMAND: CosemParser(ValueParser(Decimal)), + obis.BELGIUM_MAXIMUM_DEMAND_MONTH: MBusParser( ValueParser(timestamp), ValueParser(Decimal) - ) - } -) + ), + obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS: MaxDemandParser(), + obis.CURRENT_ELECTRICITY_USAGE: CosemParser(ValueParser(Decimal)), + obis.CURRENT_ELECTRICITY_DELIVERY: 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.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: CosemParser(ValueParser(Decimal)), + obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: CosemParser(ValueParser(Decimal)), + obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: CosemParser(ValueParser(Decimal)), + obis.INSTANTANEOUS_VOLTAGE_L1: CosemParser(ValueParser(Decimal)), + obis.INSTANTANEOUS_VOLTAGE_L2: CosemParser(ValueParser(Decimal)), + obis.INSTANTANEOUS_VOLTAGE_L3: CosemParser(ValueParser(Decimal)), + obis.INSTANTANEOUS_CURRENT_L1: CosemParser(ValueParser(Decimal)), + obis.INSTANTANEOUS_CURRENT_L2: CosemParser(ValueParser(Decimal)), + obis.INSTANTANEOUS_CURRENT_L3: CosemParser(ValueParser(Decimal)), + obis.ACTUAL_SWITCH_POSITION: CosemParser(ValueParser(int)), + obis.ACTUAL_TRESHOLD_ELECTRICITY: CosemParser(ValueParser(Decimal)), + obis.BELGIUM_MAX_POWER_PER_PHASE: CosemParser(ValueParser(Decimal)), + obis.BELGIUM_MAX_CURRENT_PER_PHASE: CosemParser(ValueParser(Decimal)), + obis.TEXT_MESSAGE: CosemParser(ValueParser(str)), + obis.BELGIUM_MBUS1_DEVICE_TYPE: CosemParser(ValueParser(int)), + obis.BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.BELGIUM_MBUS1_VALVE_POSITION: CosemParser(ValueParser(int)), + obis.BELGIUM_MBUS1_METER_READING1: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ), + obis.BELGIUM_MBUS1_METER_READING2: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ), + obis.BELGIUM_MBUS2_DEVICE_TYPE: CosemParser(ValueParser(int)), + obis.BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.BELGIUM_MBUS2_VALVE_POSITION: CosemParser(ValueParser(int)), + obis.BELGIUM_MBUS2_METER_READING1: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ), + obis.BELGIUM_MBUS2_METER_READING2: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ), + obis.BELGIUM_MBUS3_DEVICE_TYPE: CosemParser(ValueParser(int)), + obis.BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.BELGIUM_MBUS3_VALVE_POSITION: CosemParser(ValueParser(int)), + obis.BELGIUM_MBUS3_METER_READING1: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ), + obis.BELGIUM_MBUS3_METER_READING2: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ), + obis.BELGIUM_MBUS4_DEVICE_TYPE: CosemParser(ValueParser(int)), + obis.BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.BELGIUM_MBUS4_VALVE_POSITION: CosemParser(ValueParser(int)), + obis.BELGIUM_MBUS4_METER_READING1: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ), + obis.BELGIUM_MBUS4_METER_READING2: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ), + # DSMR-reader legacy. Should eventually be dropped or selected automatically in a future release. + obis.BELGIUM_MBUS_WILDCARD_DEVICE_TYPE: CosemParser(ValueParser(int)), + obis.BELGIUM_MBUS_WILDCARD_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + obis.BELGIUM_MBUS_WILDCARD_VALVE_POSITION: CosemParser(ValueParser(int)), + obis.BELGIUM_MBUS_WILDCARD_METER_READING1: MBusParser( + ValueParser(timestamp), ValueParser(Decimal) + ), + obis.BELGIUM_MBUS_WILDCARD_METER_READING2: 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) - ), + obis.ELECTRICITY_IMPORTED_TOTAL: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_EXPORTED_TOTAL: CosemParser(ValueParser(Decimal)), } ) @@ -176,10 +247,8 @@ "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.ELECTRICITY_IMPORTED_TOTAL: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_EXPORTED_TOTAL: 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)), @@ -196,3 +265,45 @@ obis.INSTANTANEOUS_CURRENT_L3: CosemParser(ValueParser(Decimal)), }, } + +Q3D = { + "checksum_support": False, + "objects": { + obis.Q3D_EQUIPMENT_IDENTIFIER: CosemParser(ValueParser(str)), + 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)), + obis.CURRENT_ELECTRICITY_USAGE: CosemParser(ValueParser(Decimal)), + obis.Q3D_EQUIPMENT_STATE: CosemParser(ValueParser(str)), + obis.Q3D_EQUIPMENT_SERIALNUMBER: CosemParser(ValueParser(str)), + }, +} + + +SAGEMCOM_T210_D_R = { + "general_global_cipher": True, + "checksum_support": True, + "objects": { + obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)), + obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)), + obis.ELECTRICITY_IMPORTED_TOTAL: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_USED_TARIFF_1: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_USED_TARIFF_2: CosemParser(ValueParser(Decimal)), + obis.CURRENT_ELECTRICITY_USAGE: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_REACTIVE_EXPORTED_TOTAL: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_REACTIVE_EXPORTED_TARIFF_1: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_REACTIVE_EXPORTED_TARIFF_2: CosemParser(ValueParser(Decimal)), + obis.CURRENT_REACTIVE_IMPORTED: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_EXPORTED_TOTAL: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_DELIVERED_TARIFF_1: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_DELIVERED_TARIFF_2: CosemParser(ValueParser(Decimal)), + obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_REACTIVE_IMPORTED_TOTAL: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_REACTIVE_IMPORTED_TARIFF_1: CosemParser(ValueParser(Decimal)), + obis.ELECTRICITY_REACTIVE_IMPORTED_TARIFF_2: CosemParser(ValueParser(Decimal)), + obis.CURRENT_REACTIVE_EXPORTED: CosemParser(ValueParser(Decimal)), + }, +} +AUSTRIA_ENERGIENETZE_STEIERMARK = SAGEMCOM_T210_D_R diff --git a/dsmrreader/provisioning/requirements/base.txt b/dsmrreader/provisioning/requirements/base.txt index f97233311..9b23c3247 100644 --- a/dsmrreader/provisioning/requirements/base.txt +++ b/dsmrreader/provisioning/requirements/base.txt @@ -10,6 +10,7 @@ django-filter==21.1 django-mathfilters==1.0.0 djangorestframework==3.14.0 django-solo==2.0 +dlms-cosem==21.3.2 dropbox==11.36.0 gunicorn==20.1.0 influxdb-client==1.35.0 diff --git a/poetry.lock b/poetry.lock index 651a999db..2b314c88a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -20,6 +20,14 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] +[[package]] +name = "asn1crypto" +version = "1.5.1" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "attrs" version = "22.2.0" @@ -96,6 +104,17 @@ category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + [[package]] name = "charset-normalizer" version = "3.0.1" @@ -171,6 +190,25 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "cryptography" +version = "39.0.0" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +pep8test = ["black", "ruff"] +sdist = ["setuptools-rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] + [[package]] name = "django" version = "3.2.16" @@ -272,6 +310,27 @@ python-versions = ">=3.6" django = ">=3.0" pytz = "*" +[[package]] +name = "dlms-cosem" +version = "21.3.2" +description = "A Python library for DLMS/COSEM" +category = "main" +optional = false +python-versions = "~=3.6" + +[package.dependencies] +asn1crypto = ">=1.4.0" +attrs = ">=20.3.0" +cryptography = ">=35.0.0" +pyserial = ">=3.5" +python-dateutil = ">=2.8.1" +typing-extensions = ">=3.10" + +[package.extras] +dev = ["mkdocs", "mkdocs-material", "pre-commit", "pytest", "pytest-cov", "pytest-sugar"] +docs = ["mkdocs", "mkdocs-material"] +test = ["pytest", "pytest-cov", "pytest-sugar"] + [[package]] name = "docutils" version = "0.17.1" @@ -707,6 +766,14 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + [[package]] name = "pyflakes" version = "2.4.0" @@ -1295,7 +1362,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "6bbf2ada1ee82ca82b6a57f0e857109a24632ae9d32f9cf1147992dbdcd14502" +content-hash = "03b79339fedbba82b859cc0256e5e6f7c4578d2efd63c7ab9147b23933c75e9c" [metadata.files] alabaster = [ @@ -1306,6 +1373,10 @@ asgiref = [ {file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"}, {file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"}, ] +asn1crypto = [ + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, +] attrs = [ {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, @@ -1336,6 +1407,72 @@ certifi = [ {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, ] +cffi = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] charset-normalizer = [ {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"}, {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"}, @@ -1498,6 +1635,31 @@ coverage = [ crcmod = [ {file = "crcmod-1.7.tar.gz", hash = "sha256:dc7051a0db5f2bd48665a990d3ec1cc305a466a77358ca4492826f41f283601e"}, ] +cryptography = [ + {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288"}, + {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96"}, + {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717"}, + {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df"}, + {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1"}, + {file = "cryptography-39.0.0-cp36-abi3-win32.whl", hash = "sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de"}, + {file = "cryptography-39.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190"}, + {file = "cryptography-39.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8"}, + {file = "cryptography-39.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39"}, + {file = "cryptography-39.0.0.tar.gz", hash = "sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf"}, +] django = [ {file = "Django-3.2.16-py3-none-any.whl", hash = "sha256:18ba8efa36b69cfcd4b670d0fa187c6fe7506596f0ababe580e16909bcdec121"}, {file = "Django-3.2.16.tar.gz", hash = "sha256:3adc285124244724a394fa9b9839cc8cd116faf7d159554c43ecdaa8cdf0b94d"}, @@ -1534,6 +1696,10 @@ djangorestframework = [ {file = "djangorestframework-3.14.0-py3-none-any.whl", hash = "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08"}, {file = "djangorestframework-3.14.0.tar.gz", hash = "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"}, ] +dlms-cosem = [ + {file = "dlms-cosem-21.3.2.tar.gz", hash = "sha256:156c6fbd48c4062085539ac717bb65712b5b45803c03cbb7a27a3b3c28dfeffd"}, + {file = "dlms_cosem-21.3.2-py3-none-any.whl", hash = "sha256:0bd621df33ed838043989441046d9544cec768b143b339d340f3b30a12bea76f"}, +] docutils = [ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, @@ -1887,6 +2053,10 @@ pycodestyle = [ {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, ] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] pyflakes = [ {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, diff --git a/pyproject.toml b/pyproject.toml index eafe73c63..1c9749928 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ pyyaml = "^6.0" requests = "^2.26" urllib3 = "^1.26" attrs = "^22.2.0" +dlms-cosem = "^21.3.2" [tool.poetry.dev-dependencies]