def __init__(self, host, port, telegram_specification): self.host = host self.port = port self.telegram_parser = TelegramParser(telegram_specification) self.telegram_buffer = TelegramBuffer() self.telegram_specification = telegram_specification
def analyze(self): key = binascii.unhexlify(self._args.key) additional_data = binascii.unhexlify(self._args.aad) iv = binascii.unhexlify(self._system_title + self._frame_counter) payload = binascii.unhexlify(self._payload) gcm_tag = binascii.unhexlify(self._gcm_tag) try: decryption = self.decrypt(key, additional_data, iv, payload, gcm_tag) if has_dsmr_parser and self._args.parse: try: parser = TelegramParser(telegram_specifications.V5) telegram = parser.parse(decryption.decode()) for key in telegram: print("%s: %s" % (dsmr_parser.obis_name_mapping.EN[key], telegram[key])) except: print("ERROR: Cannot parse DSMR Telegram") print(decryption) else: print(decryption) if self._args.serial_output_port: self.write_to_serial_port(decryption) except InvalidTag: print("ERROR: Invalid Tag.")
def __init__(self, device, serial_settings, telegram_specification): self.serial_settings = serial_settings self.serial_settings[self.PORT_KEY] = device self.telegram_parser = TelegramParser(telegram_specification) self.telegram_buffer = TelegramBuffer() self.telegram_specification = telegram_specification
def test_checksum_invalid(self): # Remove the electricty used data value. This causes the checksum to # not match anymore. corrupted_telegram = TELEGRAM_V5.replace( '1-0:1.8.1(000004.426*kWh)\r\n', '') with self.assertRaises(InvalidChecksumError): TelegramParser.validate_checksum(corrupted_telegram)
class SerialReader(object): PORT_KEY = 'port' def __init__(self, device, serial_settings, telegram_specification): self.serial_settings = serial_settings self.serial_settings[self.PORT_KEY] = device self.telegram_parser = TelegramParser(telegram_specification) self.telegram_buffer = TelegramBuffer() def read_continuously(self): """ Read complete DSMR telegram's from the serial interface and parse it into CosemObject's and MbusObject's :rtype: generator """ with serial.Serial(**self.serial_settings) as serial_handle: while True: data = serial_handle.read( max(1, min(1024, serial_handle.in_waiting))) self.telegram_buffer.append(data.decode('ascii')) for telegram in self.telegram_buffer.get_all(): try: yield self.telegram_parser.parse(telegram) except InvalidChecksumError as e: logger.warning(str(e)) except ParseError as e: logger.error('Failed to parse telegram: %s', e) def read(self): """ Read complete DSMR telegram's from the serial interface and parse it into CosemObject's and MbusObject's :rtype: generator """ with serial.Serial(**self.serial_settings) as serial_handle: data = serial_handle.read( max(1, min(1024, serial_handle.in_waiting))) self.telegram_buffer.append(data.decode('ascii')) for telegram in self.telegram_buffer.get_all(): try: yield self.telegram_parser.parse(telegram) except InvalidChecksumError as e: logger.warning(str(e)) except ParseError as e: logger.error('Failed to parse telegram: %s', e)
def parse(user, telegram): """ Parse a DSMR telegram and log the values in the appropriate places in the database. :param django.contrib.auth.models.User user: :param str telegram: """ parser = TelegramParser(SPECIFICATION) parsed_telegram = parser.parse(telegram.split("\r\n")) log_consumed_electricity(user=user, parsed_telegram=parsed_telegram) log_produced_energy(user=user, parsed_telegram=parsed_telegram) log_consumed_gas(user=user, parsed_telegram=parsed_telegram)
def telegram_to_reading(data: str) -> DsmrReading: """ Converts a P1 telegram to a DSMR reading, which will be stored in database. """ params = get_dsmr_connection_parameters() parser = TelegramParser(params['specifications']) logger.debug("Received telegram:\n%s", data) try: parsed_telegram = parser.parse(data) except (InvalidChecksumError, ParseError) as error: # Hook to keep track of failed readings count. MeterStatistics.objects.all().update( rejected_telegrams=F('rejected_telegrams') + 1) logger.warning('Rejected telegram: %s', error) raise InvalidTelegramError(error) from error return _map_telegram_to_model(parsed_telegram=parsed_telegram, data=data)
def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None): """Creates a DSMR asyncio protocol.""" if dsmr_version == '2.2': specification = telegram_specifications.V2_2 serial_settings = SERIAL_SETTINGS_V2_2 elif dsmr_version == '4': specification = telegram_specifications.V4 serial_settings = SERIAL_SETTINGS_V4 elif dsmr_version == '5': specification = telegram_specifications.V5 serial_settings = SERIAL_SETTINGS_V5 elif dsmr_version == '5B': specification = telegram_specifications.BELGIUM_FLUVIUS serial_settings = SERIAL_SETTINGS_V5 elif dsmr_version == "5L": specification = telegram_specifications.LUXEMBOURG_SMARTY serial_settings = SERIAL_SETTINGS_V5 else: raise NotImplementedError("No telegram parser found for version: %s", dsmr_version) protocol = partial(DSMRProtocol, loop, TelegramParser(specification), telegram_callback=telegram_callback) return protocol, serial_settings
def test_instantiate(self): parser = TelegramParser(telegram_specifications.V4) telegram = Telegram(TELEGRAM_V4_2, parser, telegram_specifications.V4) # P1_MESSAGE_HEADER (1-3:0.2.8) testitem = telegram.P1_MESSAGE_HEADER assert isinstance(testitem, CosemObject) assert testitem.unit is None assert testitem.value == '42'
def initialize(self, *args, **kwargs): try: self.log("Using hass logger!") except Exception: self.log = self._logme self.log("Using test logger!") self.mode = 'tcp' self.host = 'homeassistant.fritz.box' self.port = 3333 self.device = 'COM3' self.dsmr_version = '5' self.terminal_name = 'test' self.stop = False self.transport = None self.log("Starting thread...") self.log("P1 test started") parser = self.test_serial # parser = self.tcp dsmr_version = self.dsmr_version if dsmr_version == '2.2': specification = telegram_specifications.V2_2 serial_settings = SERIAL_SETTINGS_V2_2 elif dsmr_version == '4': specification = telegram_specifications.V4 serial_settings = SERIAL_SETTINGS_V4 elif dsmr_version == '5': specification = telegram_specifications.V5 serial_settings = SERIAL_SETTINGS_V5 elif dsmr_version == '5B': specification = telegram_specifications.BELGIUM_FLUVIUS serial_settings = SERIAL_SETTINGS_V5 else: raise NotImplementedError( "No telegram parser found for version: %s", dsmr_version) self.telegram_parser = TelegramParser(specification) self.serial_settings = serial_settings # buffer to keep incomplete incoming data self.telegram_buffer = TelegramBuffer() self.thr = threading.Thread(target=parser, daemon=True) self.thr.start() self.log("Started!")
def analyze(self): key = binascii.unhexlify(self._args.key) additional_data = binascii.unhexlify(self._args.aad) iv = binascii.unhexlify(self._system_title + self._frame_counter) payload = binascii.unhexlify(self._payload) gcm_tag = binascii.unhexlify(self._gcm_tag) try: decryption = self.decrypt(key, additional_data, iv, payload, gcm_tag) if has_dsmr_parser and self._args.parse: try: parser = TelegramParser(telegram_specifications.V5) telegram = parser.parse(decryption.decode()) for key in telegram: if key in dsmr_parser.obis_name_mapping.EN: print("%s: %s" % (dsmr_parser.obis_name_mapping.EN[key], telegram[key])) if has_mqtt: try: self._mqtt_client.publish( self._args.topic_prefix + '/' + dsmr_parser.obis_name_mapping.EN[key], str(telegram[key].value)) except: print("ERROR: cannot publish to MQTT") else: print("%s: %s" % (key, telegram[key])) except Exception as e: print("ERROR: Cannot parse DSMR Telegram") print("Exception: ", sys.exc_info()[0]) print(decryption) else: print(decryption) if self._args.serial_output_port: self.write_to_serial_port(decryption) except InvalidTag: print("ERROR: Invalid Tag.")
def _get_dsmr_parser(self): dsmr_version = self.dsmr_version if dsmr_version == '2.2': specification = telegram_specifications.V2_2 serial_settings = SERIAL_SETTINGS_V2_2 elif dsmr_version == '4': specification = telegram_specifications.V4 serial_settings = SERIAL_SETTINGS_V4 elif dsmr_version == '5': specification = telegram_specifications.V5 serial_settings = SERIAL_SETTINGS_V5 elif dsmr_version == '5B': specification = telegram_specifications.BELGIUM_FLUVIUS serial_settings = SERIAL_SETTINGS_V5 else: raise NotImplementedError( "No telegram parser found for version: %s", dsmr_version) self.telegram_parser = TelegramParser(specification) serial_settings['timeout'] = 10 self.serial_settings = serial_settings
def test_power_event_log_empty_1(self): # POWER_EVENT_FAILURE_LOG (1-0:99.97.0) parser = TelegramParser(telegram_specifications.V5) telegram = Telegram(TELEGRAM_V5, parser, telegram_specifications.V5) object_type = ProfileGenericObject testitem = telegram.POWER_EVENT_FAILURE_LOG assert isinstance(testitem, object_type) assert testitem.buffer_length == 0 assert testitem.buffer_type == '0-0:96.7.19' buffer = testitem.buffer assert isinstance(testitem.buffer, list) assert len(buffer) == 0
def telegram_to_reading(data): """ Converts a P1 telegram to a DSMR reading, which will be stored in database. """ params = get_dsmr_connection_parameters() parser = TelegramParser(params['specifications']) # We will log the telegrams in base64 for convenience and debugging. base64_data = base64.b64encode(data.encode()) if settings.DSMRREADER_LOG_TELEGRAMS: dsmrreader_logger.info('Received telegram (base64 encoded): %s', base64_data) try: parsed_telegram = parser.parse(data) except (InvalidChecksumError, ParseError) as error: # Hook to keep track of failed readings count. MeterStatistics.objects.all().update( rejected_telegrams=F('rejected_telegrams') + 1) dsmrreader_logger.warning( 'Rejected telegram (%s) (base64 encoded): %s', error, base64_data) dsmrreader_logger.exception(error) raise InvalidTelegramError(error) return _map_telegram_to_model(parsed_telegram=parsed_telegram, data=data)
def test_parse(self): parser = TelegramParser(telegram_specifications.V5) result = parser.parse(TELEGRAM_V5) # P1_MESSAGE_HEADER (1-3:0.2.8) assert isinstance(result[obis.P1_MESSAGE_HEADER], CosemObject) assert result[obis.P1_MESSAGE_HEADER].unit is None assert isinstance(result[obis.P1_MESSAGE_HEADER].value, str) assert result[obis.P1_MESSAGE_HEADER].value == '50' # P1_MESSAGE_TIMESTAMP (0-0:1.0.0) assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP], CosemObject) assert result[obis.P1_MESSAGE_TIMESTAMP].unit is None assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP].value, datetime.datetime) assert result[obis.P1_MESSAGE_TIMESTAMP].value == \ datetime.datetime(2017, 1, 2, 18, 20, 2, tzinfo=datetime.timezone.utc) # ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1) assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject) assert result[obis.ELECTRICITY_USED_TARIFF_1].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1].value, Decimal) assert result[obis.ELECTRICITY_USED_TARIFF_1].value == Decimal('4.426') # ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2) assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2], CosemObject) assert result[obis.ELECTRICITY_USED_TARIFF_2].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2].value, Decimal) assert result[obis.ELECTRICITY_USED_TARIFF_2].value == Decimal('2.399') # ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1) assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1], CosemObject) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value, Decimal) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value == Decimal( '2.444') # ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2) assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2], CosemObject) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value, Decimal) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value == Decimal( '0') # ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0) assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF], CosemObject) assert result[obis.ELECTRICITY_ACTIVE_TARIFF].unit is None assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF].value, str) assert result[obis.ELECTRICITY_ACTIVE_TARIFF].value == '0002' # EQUIPMENT_IDENTIFIER (0-0:96.1.1) assert isinstance(result[obis.EQUIPMENT_IDENTIFIER], CosemObject) assert result[obis.EQUIPMENT_IDENTIFIER].unit is None assert isinstance(result[obis.EQUIPMENT_IDENTIFIER].value, str) assert result[ obis. EQUIPMENT_IDENTIFIER].value == '4B384547303034303436333935353037' # CURRENT_ELECTRICITY_USAGE (1-0:1.7.0) assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE], CosemObject) assert result[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW' assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE].value, Decimal) assert result[obis.CURRENT_ELECTRICITY_USAGE].value == Decimal('0.244') # CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0) assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY], CosemObject) assert result[obis.CURRENT_ELECTRICITY_DELIVERY].unit == 'kW' assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY].value, Decimal) assert result[obis.CURRENT_ELECTRICITY_DELIVERY].value == Decimal('0') # LONG_POWER_FAILURE_COUNT (96.7.9) assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT], CosemObject) assert result[obis.LONG_POWER_FAILURE_COUNT].unit is None assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT].value, int) assert result[obis.LONG_POWER_FAILURE_COUNT].value == 0 # VOLTAGE_SAG_L1_COUNT (1-0:32.32.0) assert isinstance(result[obis.VOLTAGE_SAG_L1_COUNT], CosemObject) assert result[obis.VOLTAGE_SAG_L1_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SAG_L1_COUNT].value, int) assert result[obis.VOLTAGE_SAG_L1_COUNT].value == 0 # VOLTAGE_SAG_L2_COUNT (1-0:52.32.0) assert isinstance(result[obis.VOLTAGE_SAG_L2_COUNT], CosemObject) assert result[obis.VOLTAGE_SAG_L2_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SAG_L2_COUNT].value, int) assert result[obis.VOLTAGE_SAG_L2_COUNT].value == 0 # VOLTAGE_SAG_L3_COUNT (1-0:72.32.0) assert isinstance(result[obis.VOLTAGE_SAG_L3_COUNT], CosemObject) assert result[obis.VOLTAGE_SAG_L3_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SAG_L3_COUNT].value, int) assert result[obis.VOLTAGE_SAG_L3_COUNT].value == 0 # VOLTAGE_SWELL_L1_COUNT (1-0:32.36.0) assert isinstance(result[obis.VOLTAGE_SWELL_L1_COUNT], CosemObject) assert result[obis.VOLTAGE_SWELL_L1_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SWELL_L1_COUNT].value, int) assert result[obis.VOLTAGE_SWELL_L1_COUNT].value == 0 # VOLTAGE_SWELL_L2_COUNT (1-0:52.36.0) assert isinstance(result[obis.VOLTAGE_SWELL_L2_COUNT], CosemObject) assert result[obis.VOLTAGE_SWELL_L2_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SWELL_L2_COUNT].value, int) assert result[obis.VOLTAGE_SWELL_L2_COUNT].value == 0 # VOLTAGE_SWELL_L3_COUNT (1-0:72.36.0) assert isinstance(result[obis.VOLTAGE_SWELL_L3_COUNT], CosemObject) assert result[obis.VOLTAGE_SWELL_L3_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SWELL_L3_COUNT].value, int) assert result[obis.VOLTAGE_SWELL_L3_COUNT].value == 0 # TEXT_MESSAGE (0-0:96.13.0) assert isinstance(result[obis.TEXT_MESSAGE], CosemObject) assert result[obis.TEXT_MESSAGE].unit is None assert result[obis.TEXT_MESSAGE].value is None # DEVICE_TYPE (0-x:24.1.0) assert isinstance(result[obis.TEXT_MESSAGE], CosemObject) assert result[obis.DEVICE_TYPE].unit is None assert isinstance(result[obis.DEVICE_TYPE].value, int) assert result[obis.DEVICE_TYPE].value == 3 # INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value == Decimal( '0.070') # INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value == Decimal( '0.032') # INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value == Decimal( '0.142') # INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value == Decimal('0') # INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value == Decimal('0') # INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value == Decimal('0') # EQUIPMENT_IDENTIFIER_GAS (0-x:96.1.0) assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS], CosemObject) assert result[obis.EQUIPMENT_IDENTIFIER_GAS].unit is None assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS].value, str) assert result[ obis. EQUIPMENT_IDENTIFIER_GAS].value == '3232323241424344313233343536373839' # HOURLY_GAS_METER_READING (0-1:24.2.1) assert isinstance(result[obis.HOURLY_GAS_METER_READING], MBusObject) assert result[obis.HOURLY_GAS_METER_READING].unit == 'm3' assert isinstance(result[obis.HOURLY_GAS_METER_READING].value, Decimal) assert result[obis.HOURLY_GAS_METER_READING].value == Decimal('0.107')
def test_instantiate(self): parser = TelegramParser(telegram_specifications.V4) telegram = Telegram(TELEGRAM_V4_2, parser, telegram_specifications.V4) # P1_MESSAGE_HEADER (1-3:0.2.8) self.verify_telegram_item(telegram, 'P1_MESSAGE_HEADER', object_type=CosemObject, unit_val=None, value_type=str, value_val='42') # P1_MESSAGE_TIMESTAMP (0-0:1.0.0) self.verify_telegram_item(telegram, 'P1_MESSAGE_TIMESTAMP', CosemObject, unit_val=None, value_type=datetime.datetime, value_val=datetime.datetime(2016, 11, 13, 19, 57, 57, tzinfo=pytz.UTC)) # ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1) self.verify_telegram_item(telegram, 'ELECTRICITY_USED_TARIFF_1', object_type=CosemObject, unit_val='kWh', value_type=Decimal, value_val=Decimal('1581.123')) # ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2) self.verify_telegram_item(telegram, 'ELECTRICITY_USED_TARIFF_2', object_type=CosemObject, unit_val='kWh', value_type=Decimal, value_val=Decimal('1435.706')) # ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1) self.verify_telegram_item(telegram, 'ELECTRICITY_DELIVERED_TARIFF_1', object_type=CosemObject, unit_val='kWh', value_type=Decimal, value_val=Decimal('0')) # ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2) self.verify_telegram_item(telegram, 'ELECTRICITY_DELIVERED_TARIFF_2', object_type=CosemObject, unit_val='kWh', value_type=Decimal, value_val=Decimal('0')) # ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0) self.verify_telegram_item(telegram, 'ELECTRICITY_ACTIVE_TARIFF', object_type=CosemObject, unit_val=None, value_type=str, value_val='0002') # EQUIPMENT_IDENTIFIER (0-0:96.1.1) self.verify_telegram_item( telegram, 'EQUIPMENT_IDENTIFIER', object_type=CosemObject, unit_val=None, value_type=str, value_val='3960221976967177082151037881335713') # CURRENT_ELECTRICITY_USAGE (1-0:1.7.0) self.verify_telegram_item(telegram, 'CURRENT_ELECTRICITY_USAGE', object_type=CosemObject, unit_val='kW', value_type=Decimal, value_val=Decimal('2.027')) # CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0) self.verify_telegram_item(telegram, 'CURRENT_ELECTRICITY_DELIVERY', object_type=CosemObject, unit_val='kW', value_type=Decimal, value_val=Decimal('0')) # SHORT_POWER_FAILURE_COUNT (1-0:96.7.21) self.verify_telegram_item(telegram, 'SHORT_POWER_FAILURE_COUNT', object_type=CosemObject, unit_val=None, value_type=int, value_val=15) # LONG_POWER_FAILURE_COUNT (96.7.9) self.verify_telegram_item(telegram, 'LONG_POWER_FAILURE_COUNT', object_type=CosemObject, unit_val=None, value_type=int, value_val=7) # VOLTAGE_SAG_L1_COUNT (1-0:32.32.0) self.verify_telegram_item(telegram, 'VOLTAGE_SAG_L1_COUNT', object_type=CosemObject, unit_val=None, value_type=int, value_val=0) # VOLTAGE_SAG_L2_COUNT (1-0:52.32.0) self.verify_telegram_item(telegram, 'VOLTAGE_SAG_L2_COUNT', object_type=CosemObject, unit_val=None, value_type=int, value_val=0) # VOLTAGE_SAG_L3_COUNT (1-0:72.32.0) self.verify_telegram_item(telegram, 'VOLTAGE_SAG_L3_COUNT', object_type=CosemObject, unit_val=None, value_type=int, value_val=0) # VOLTAGE_SWELL_L1_COUNT (1-0:32.36.0) self.verify_telegram_item(telegram, 'VOLTAGE_SWELL_L1_COUNT', object_type=CosemObject, unit_val=None, value_type=int, value_val=0) # VOLTAGE_SWELL_L2_COUNT (1-0:52.36.0) self.verify_telegram_item(telegram, 'VOLTAGE_SWELL_L2_COUNT', object_type=CosemObject, unit_val=None, value_type=int, value_val=0) # VOLTAGE_SWELL_L3_COUNT (1-0:72.36.0) self.verify_telegram_item(telegram, 'VOLTAGE_SWELL_L3_COUNT', object_type=CosemObject, unit_val=None, value_type=int, value_val=0) # TEXT_MESSAGE_CODE (0-0:96.13.1) self.verify_telegram_item(telegram, 'TEXT_MESSAGE_CODE', object_type=CosemObject, unit_val=None, value_type=type(None), value_val=None) # TEXT_MESSAGE (0-0:96.13.0) self.verify_telegram_item(telegram, 'TEXT_MESSAGE', object_type=CosemObject, unit_val=None, value_type=type(None), value_val=None) # INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0) self.verify_telegram_item(telegram, 'INSTANTANEOUS_CURRENT_L1', object_type=CosemObject, unit_val='A', value_type=Decimal, value_val=Decimal('0')) # INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0) self.verify_telegram_item(telegram, 'INSTANTANEOUS_CURRENT_L2', object_type=CosemObject, unit_val='A', value_type=Decimal, value_val=Decimal('6')) # INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0) self.verify_telegram_item(telegram, 'INSTANTANEOUS_CURRENT_L3', object_type=CosemObject, unit_val='A', value_type=Decimal, value_val=Decimal('2')) # DEVICE_TYPE (0-x:24.1.0) self.verify_telegram_item(telegram, 'DEVICE_TYPE', object_type=CosemObject, unit_val=None, value_type=int, value_val=3) # INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0) self.verify_telegram_item(telegram, 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE', object_type=CosemObject, unit_val='kW', value_type=Decimal, value_val=Decimal('0.170')) # INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0) self.verify_telegram_item(telegram, 'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE', object_type=CosemObject, unit_val='kW', value_type=Decimal, value_val=Decimal('1.247')) # INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0) self.verify_telegram_item(telegram, 'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE', object_type=CosemObject, unit_val='kW', value_type=Decimal, value_val=Decimal('0.209')) # INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0) self.verify_telegram_item(telegram, 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE', object_type=CosemObject, unit_val='kW', value_type=Decimal, value_val=Decimal('0')) # INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0) self.verify_telegram_item(telegram, 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE', object_type=CosemObject, unit_val='kW', value_type=Decimal, value_val=Decimal('0')) # INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0) self.verify_telegram_item(telegram, 'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE', object_type=CosemObject, unit_val='kW', value_type=Decimal, value_val=Decimal('0')) # EQUIPMENT_IDENTIFIER_GAS (0-x:96.1.0) self.verify_telegram_item( telegram, 'EQUIPMENT_IDENTIFIER_GAS', object_type=CosemObject, unit_val=None, value_type=str, value_val='4819243993373755377509728609491464') # HOURLY_GAS_METER_READING (0-1:24.2.1) self.verify_telegram_item(telegram, 'HOURLY_GAS_METER_READING', object_type=MBusObject, unit_val='m3', value_type=Decimal, value_val=Decimal('981.443')) # POWER_EVENT_FAILURE_LOG (1-0:99.97.0) testitem_name = 'POWER_EVENT_FAILURE_LOG' object_type = ProfileGenericObject testitem = eval("telegram.{}".format(testitem_name)) assert isinstance(testitem, object_type) assert testitem.buffer_length == 3 assert testitem.buffer_type == '0-0:96.7.19' buffer = testitem.buffer assert isinstance(testitem.buffer, list) assert len(buffer) == 3 assert all([isinstance(item, MBusObject) for item in buffer]) date0 = datetime.datetime(2000, 1, 4, 17, 3, 20, tzinfo=datetime.timezone.utc) date1 = datetime.datetime(1999, 12, 31, 23, 0, 1, tzinfo=datetime.timezone.utc) date2 = datetime.datetime(2000, 1, 1, 23, 0, 3, tzinfo=datetime.timezone.utc) assert buffer[0].datetime == date0 assert buffer[1].datetime == date1 assert buffer[2].datetime == date2 assert buffer[0].value == 237126 assert buffer[1].value == 2147583646 assert buffer[2].value == 2317482647 assert all([isinstance(item.value, int) for item in buffer]) assert all([isinstance(item.unit, str) for item in buffer]) assert all([(item.unit == 's') for item in buffer]) self.item_names_tested.append(testitem_name) # check if all items in telegram V4 specification are covered V4_name_list = [ obis_name_mapping.EN[signature] for signature, parser in telegram_specifications.V4['objects'].items() ] V4_name_set = set(V4_name_list) item_names_tested_set = set(self.item_names_tested) assert item_names_tested_set == V4_name_set
def setUp(self): telegram_parser = TelegramParser(telegram_specifications.V2_2) self.protocol = DSMRProtocol(None, telegram_parser, telegram_callback=Mock())
def __init__(self, file, telegram_specification): self._file = file self.telegram_parser = TelegramParser(telegram_specification) self.telegram_buffer = TelegramBuffer() self.telegram_specification = telegram_specification
def test_checksum_missing(self): # Remove the checksum value causing a ParseError. corrupted_telegram = TELEGRAM_V5.replace('!87B3\r\n', '') with self.assertRaises(ParseContentError): TelegramParser.validate_checksum(corrupted_telegram)
def test_parse(self): parser = TelegramParser(telegram_specifications.V4) result = parser.parse(TELEGRAM_V4_2) # P1_MESSAGE_HEADER (1-3:0.2.8) assert isinstance(result[obis.P1_MESSAGE_HEADER], CosemObject) assert result[obis.P1_MESSAGE_HEADER].unit is None assert isinstance(result[obis.P1_MESSAGE_HEADER].value, str) assert result[obis.P1_MESSAGE_HEADER].value == '42' # P1_MESSAGE_TIMESTAMP (0-0:1.0.0) assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP], CosemObject) assert result[obis.P1_MESSAGE_TIMESTAMP].unit is None assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP].value, datetime.datetime) assert result[obis.P1_MESSAGE_TIMESTAMP].value == \ datetime.datetime(2016, 11, 13, 19, 57, 57, tzinfo=pytz.UTC) # ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1) assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject) assert result[obis.ELECTRICITY_USED_TARIFF_1].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1].value, Decimal) assert result[obis.ELECTRICITY_USED_TARIFF_1].value == Decimal('1511.267') # ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2) assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2], CosemObject) assert result[obis.ELECTRICITY_USED_TARIFF_2].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2].value, Decimal) assert result[obis.ELECTRICITY_USED_TARIFF_2].value == Decimal('1265.173') # ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1) assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1], CosemObject) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value, Decimal) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value == Decimal('0') # ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2) assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2], CosemObject) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value, Decimal) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value == Decimal('0') # ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0) assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF], CosemObject) assert result[obis.ELECTRICITY_ACTIVE_TARIFF].unit is None assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF].value, str) assert result[obis.ELECTRICITY_ACTIVE_TARIFF].value == '0001' # EQUIPMENT_IDENTIFIER (0-0:96.1.1) assert isinstance(result[obis.EQUIPMENT_IDENTIFIER], CosemObject) assert result[obis.EQUIPMENT_IDENTIFIER].unit is None assert isinstance(result[obis.EQUIPMENT_IDENTIFIER].value, str) assert result[obis.EQUIPMENT_IDENTIFIER].value == '1231231231231231231231231231231231' # CURRENT_ELECTRICITY_USAGE (1-0:1.7.0) assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE], CosemObject) assert result[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW' assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE].value, Decimal) assert result[obis.CURRENT_ELECTRICITY_USAGE].value == Decimal('0.235') # CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0) assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY], CosemObject) assert result[obis.CURRENT_ELECTRICITY_DELIVERY].unit == 'kW' assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY].value, Decimal) assert result[obis.CURRENT_ELECTRICITY_DELIVERY].value == Decimal('0') # LONG_POWER_FAILURE_COUNT (96.7.9) assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT], CosemObject) assert result[obis.LONG_POWER_FAILURE_COUNT].unit is None assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT].value, int) assert result[obis.LONG_POWER_FAILURE_COUNT].value == 7 # VOLTAGE_SAG_L1_COUNT (1-0:32.32.0) assert isinstance(result[obis.VOLTAGE_SAG_L1_COUNT], CosemObject) assert result[obis.VOLTAGE_SAG_L1_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SAG_L1_COUNT].value, int) assert result[obis.VOLTAGE_SAG_L1_COUNT].value == 0 # VOLTAGE_SAG_L2_COUNT (1-0:52.32.0) assert isinstance(result[obis.VOLTAGE_SAG_L2_COUNT], CosemObject) assert result[obis.VOLTAGE_SAG_L2_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SAG_L2_COUNT].value, int) assert result[obis.VOLTAGE_SAG_L2_COUNT].value == 0 # VOLTAGE_SAG_L3_COUNT (1-0:72.32.0) assert isinstance(result[obis.VOLTAGE_SAG_L3_COUNT], CosemObject) assert result[obis.VOLTAGE_SAG_L3_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SAG_L3_COUNT].value, int) assert result[obis.VOLTAGE_SAG_L3_COUNT].value == 0 # VOLTAGE_SWELL_L1_COUNT (1-0:32.36.0) assert isinstance(result[obis.VOLTAGE_SWELL_L1_COUNT], CosemObject) assert result[obis.VOLTAGE_SWELL_L1_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SWELL_L1_COUNT].value, int) assert result[obis.VOLTAGE_SWELL_L1_COUNT].value == 0 # VOLTAGE_SWELL_L2_COUNT (1-0:52.36.0) assert isinstance(result[obis.VOLTAGE_SWELL_L2_COUNT], CosemObject) assert result[obis.VOLTAGE_SWELL_L2_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SWELL_L2_COUNT].value, int) assert result[obis.VOLTAGE_SWELL_L2_COUNT].value == 0 # VOLTAGE_SWELL_L3_COUNT (1-0:72.36.0) assert isinstance(result[obis.VOLTAGE_SWELL_L3_COUNT], CosemObject) assert result[obis.VOLTAGE_SWELL_L3_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SWELL_L3_COUNT].value, int) assert result[obis.VOLTAGE_SWELL_L3_COUNT].value == 0 # TEXT_MESSAGE_CODE (0-0:96.13.1) assert isinstance(result[obis.TEXT_MESSAGE_CODE], CosemObject) assert result[obis.TEXT_MESSAGE_CODE].unit is None assert result[obis.TEXT_MESSAGE_CODE].value is None # TEXT_MESSAGE (0-0:96.13.0) assert isinstance(result[obis.TEXT_MESSAGE], CosemObject) assert result[obis.TEXT_MESSAGE].unit is None assert result[obis.TEXT_MESSAGE].value is None # DEVICE_TYPE (0-x:24.1.0) assert isinstance(result[obis.TEXT_MESSAGE], CosemObject) assert result[obis.DEVICE_TYPE].unit is None assert isinstance(result[obis.DEVICE_TYPE].value, int) assert result[obis.DEVICE_TYPE].value == 3 # INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].unit == 'kW' assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value, Decimal) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value == Decimal('0.095') # INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].unit == 'kW' assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value, Decimal) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value == Decimal('0.025') # INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].unit == 'kW' assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value, Decimal) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value == Decimal('0.115') # INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].unit == 'kW' assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value, Decimal) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value == Decimal('0') # INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].unit == 'kW' assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value, Decimal) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value == Decimal('0') # INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].unit == 'kW' assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value, Decimal) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value == Decimal('0') # EQUIPMENT_IDENTIFIER_GAS (0-x:96.1.0) assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS], CosemObject) assert result[obis.EQUIPMENT_IDENTIFIER_GAS].unit is None assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS].value, str) assert result[obis.EQUIPMENT_IDENTIFIER_GAS].value == '3404856892390357246729543587524029' # HOURLY_GAS_METER_READING (0-1:24.2.1) assert isinstance(result[obis.HOURLY_GAS_METER_READING], MBusObject) assert result[obis.HOURLY_GAS_METER_READING].unit == 'm3' assert isinstance(result[obis.HOURLY_GAS_METER_READING].value, Decimal) assert result[obis.HOURLY_GAS_METER_READING].value == Decimal('915.219')
def test_parse(self): parser = TelegramParser(telegram_specifications.V4) result = parser.parse(TELEGRAM_V4_2) # P1_MESSAGE_HEADER (1-3:0.2.8) assert isinstance(result[obis.P1_MESSAGE_HEADER], CosemObject) assert result[obis.P1_MESSAGE_HEADER].unit is None assert isinstance(result[obis.P1_MESSAGE_HEADER].value, str) assert result[obis.P1_MESSAGE_HEADER].value == '42' # P1_MESSAGE_TIMESTAMP (0-0:1.0.0) assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP], CosemObject) assert result[obis.P1_MESSAGE_TIMESTAMP].unit is None assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP].value, datetime.datetime) assert result[obis.P1_MESSAGE_TIMESTAMP].value == \ datetime.datetime(2016, 11, 13, 19, 57, 57, tzinfo=pytz.UTC) # ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1) assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject) assert result[obis.ELECTRICITY_USED_TARIFF_1].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1].value, Decimal) assert result[obis.ELECTRICITY_USED_TARIFF_1].value == Decimal( '1581.123') # ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2) assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2], CosemObject) assert result[obis.ELECTRICITY_USED_TARIFF_2].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2].value, Decimal) assert result[obis.ELECTRICITY_USED_TARIFF_2].value == Decimal( '1435.706') # ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1) assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1], CosemObject) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value, Decimal) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value == Decimal( '0') # ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2) assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2], CosemObject) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value, Decimal) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value == Decimal( '0') # ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0) assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF], CosemObject) assert result[obis.ELECTRICITY_ACTIVE_TARIFF].unit is None assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF].value, str) assert result[obis.ELECTRICITY_ACTIVE_TARIFF].value == '0002' # EQUIPMENT_IDENTIFIER (0-0:96.1.1) assert isinstance(result[obis.EQUIPMENT_IDENTIFIER], CosemObject) assert result[obis.EQUIPMENT_IDENTIFIER].unit is None assert isinstance(result[obis.EQUIPMENT_IDENTIFIER].value, str) assert result[ obis. EQUIPMENT_IDENTIFIER].value == '3960221976967177082151037881335713' # CURRENT_ELECTRICITY_USAGE (1-0:1.7.0) assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE], CosemObject) assert result[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW' assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE].value, Decimal) assert result[obis.CURRENT_ELECTRICITY_USAGE].value == Decimal('2.027') # CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0) assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY], CosemObject) assert result[obis.CURRENT_ELECTRICITY_DELIVERY].unit == 'kW' assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY].value, Decimal) assert result[obis.CURRENT_ELECTRICITY_DELIVERY].value == Decimal('0') # LONG_POWER_FAILURE_COUNT (96.7.9) assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT], CosemObject) assert result[obis.LONG_POWER_FAILURE_COUNT].unit is None assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT].value, int) assert result[obis.LONG_POWER_FAILURE_COUNT].value == 7 # VOLTAGE_SAG_L1_COUNT (1-0:32.32.0) assert isinstance(result[obis.VOLTAGE_SAG_L1_COUNT], CosemObject) assert result[obis.VOLTAGE_SAG_L1_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SAG_L1_COUNT].value, int) assert result[obis.VOLTAGE_SAG_L1_COUNT].value == 0 # VOLTAGE_SAG_L2_COUNT (1-0:52.32.0) assert isinstance(result[obis.VOLTAGE_SAG_L2_COUNT], CosemObject) assert result[obis.VOLTAGE_SAG_L2_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SAG_L2_COUNT].value, int) assert result[obis.VOLTAGE_SAG_L2_COUNT].value == 0 # VOLTAGE_SAG_L3_COUNT (1-0:72.32.0) assert isinstance(result[obis.VOLTAGE_SAG_L3_COUNT], CosemObject) assert result[obis.VOLTAGE_SAG_L3_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SAG_L3_COUNT].value, int) assert result[obis.VOLTAGE_SAG_L3_COUNT].value == 0 # VOLTAGE_SWELL_L1_COUNT (1-0:32.36.0) assert isinstance(result[obis.VOLTAGE_SWELL_L1_COUNT], CosemObject) assert result[obis.VOLTAGE_SWELL_L1_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SWELL_L1_COUNT].value, int) assert result[obis.VOLTAGE_SWELL_L1_COUNT].value == 0 # VOLTAGE_SWELL_L2_COUNT (1-0:52.36.0) assert isinstance(result[obis.VOLTAGE_SWELL_L2_COUNT], CosemObject) assert result[obis.VOLTAGE_SWELL_L2_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SWELL_L2_COUNT].value, int) assert result[obis.VOLTAGE_SWELL_L2_COUNT].value == 0 # VOLTAGE_SWELL_L3_COUNT (1-0:72.36.0) assert isinstance(result[obis.VOLTAGE_SWELL_L3_COUNT], CosemObject) assert result[obis.VOLTAGE_SWELL_L3_COUNT].unit is None assert isinstance(result[obis.VOLTAGE_SWELL_L3_COUNT].value, int) assert result[obis.VOLTAGE_SWELL_L3_COUNT].value == 0 # TEXT_MESSAGE_CODE (0-0:96.13.1) assert isinstance(result[obis.TEXT_MESSAGE_CODE], CosemObject) assert result[obis.TEXT_MESSAGE_CODE].unit is None assert result[obis.TEXT_MESSAGE_CODE].value is None # TEXT_MESSAGE (0-0:96.13.0) assert isinstance(result[obis.TEXT_MESSAGE], CosemObject) assert result[obis.TEXT_MESSAGE].unit is None assert result[obis.TEXT_MESSAGE].value is None # DEVICE_TYPE (0-x:24.1.0) assert isinstance(result[obis.TEXT_MESSAGE], CosemObject) assert result[obis.DEVICE_TYPE].unit is None assert isinstance(result[obis.DEVICE_TYPE].value, int) assert result[obis.DEVICE_TYPE].value == 3 # INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value == Decimal( '0.170') # INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value == Decimal( '1.247') # INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value == Decimal( '0.209') # INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value == Decimal('0') # INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value == Decimal('0') # INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0) assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE], CosemObject) assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].unit == 'kW' assert isinstance( result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value, Decimal) assert result[ obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value == Decimal('0') # EQUIPMENT_IDENTIFIER_GAS (0-x:96.1.0) assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS], CosemObject) assert result[obis.EQUIPMENT_IDENTIFIER_GAS].unit is None assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS].value, str) assert result[ obis. EQUIPMENT_IDENTIFIER_GAS].value == '4819243993373755377509728609491464' # HOURLY_GAS_METER_READING (0-1:24.2.1) assert isinstance(result[obis.HOURLY_GAS_METER_READING], MBusObject) assert result[obis.HOURLY_GAS_METER_READING].unit == 'm3' assert isinstance(result[obis.HOURLY_GAS_METER_READING].value, Decimal) assert result[obis.HOURLY_GAS_METER_READING].value == Decimal( '981.443')
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry, async_add_entities): """set up entities based on a config entry""" _version = entry.data[DSMRVERSION] _precision = entry.data[PRECISION] _timezone = pytz.timezone(entry.data[TIMEZONE]) # Protocol version specific obis if _version in "4": _gas_obis = obis_ref.HOURLY_GAS_METER_READING _parser = TelegramParser(telegram_specifications.V4) elif _version in "5": _gas_obis = obis_ref.HOURLY_GAS_METER_READING _parser = TelegramParser(telegram_specifications.V5) elif _version in ("5B", ): _gas_obis = obis_ref.BELGIUM_HOURLY_GAS_METER_READING _parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS) else: _gas_obis = obis_ref.GAS_METER_READING _parser = TelegramParser(telegram_specifications.V2_2) # Define mapping for electricity mappings elements = ENTITIES elements += [ [GAS_CONSUMPTION_NAME, 'mdi:fire', _gas_obis], ] # generate smart entities entities = [ ElecticityEntity(name, icon, obis, _precision, _timezone, _parser) for name, icon, obis in elements ] elements = [ [GAS_HOURLY_CONSUMPTION_NAME, 'mdi:fire', _gas_obis], [GAS_HOURLY_LAST_UPDATE_NAME, 'mdi:update', _gas_obis], ] # generate gas entities entities += [ GasEntity(name, icon, obis, _precision, _timezone, _parser) for name, icon, obis in elements ] # Set up the sensor platform async_add_entities(entities) async def async_consume_service(call): """handle calls to the service.""" telegram = call.data.get('telegram') telegram = telegram.replace(" ", "") telegram = telegram.replace("\\r\\n", "\r\n") for entity in entities: entity.set_consumed(telegram) hass.services.async_register( DOMAIN, SERVICE, async_consume_service, schema=ENTITIES_SCHEMA, )
def test_checksum_valid(self): # No exception is raised. TelegramParser.validate_checksum(TELEGRAM_V5)
def test_instantiate(self): parser = TelegramParser(telegram_specifications.V4) #result = parser.parse(TELEGRAM_V4_2) telegram = Telegram(TELEGRAM_V4_2, parser, telegram_specifications.V4)
class SocketReader(object): BUFFER_SIZE = 256 def __init__(self, host, port, telegram_specification): self.host = host self.port = port self.telegram_parser = TelegramParser(telegram_specification) self.telegram_buffer = TelegramBuffer() self.telegram_specification = telegram_specification def read(self): """ Read complete DSMR telegram's from remote interface and parse it into CosemObject's and MbusObject's :rtype: generator """ buffer = b"" with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_handle: socket_handle.connect((self.host, self.port)) while True: buffer += socket_handle.recv(self.BUFFER_SIZE) lines = buffer.splitlines(keepends=True) if len(lines) == 0: continue for data in lines: self.telegram_buffer.append(data.decode('ascii')) for telegram in self.telegram_buffer.get_all(): try: yield self.telegram_parser.parse(telegram) except InvalidChecksumError as e: logger.warning(str(e)) except ParseError as e: logger.error('Failed to parse telegram: %s', e) buffer = b"" def read_as_object(self): """ Read complete DSMR telegram's from remote and return a Telegram object. :rtype: generator """ buffer = b"" with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_handle: socket_handle.connect((self.host, self.port)) while True: buffer += socket_handle.recv(self.BUFFER_SIZE) lines = buffer.splitlines(keepends=True) if len(lines) == 0: continue for data in lines: self.telegram_buffer.append(data.decode('ascii')) for telegram in self.telegram_buffer.get_all(): try: yield Telegram(telegram, self.telegram_parser, self.telegram_specification) except InvalidChecksumError as e: logger.warning(str(e)) except ParseError as e: logger.error('Failed to parse telegram: %s', e) buffer = b""
def test_parse(self): parser = TelegramParser(telegram_specifications.V3) result = parser.parse(TELEGRAM_V3) # ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1) assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject) assert result[obis.ELECTRICITY_USED_TARIFF_1].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1].value, Decimal) assert result[obis.ELECTRICITY_USED_TARIFF_1].value == Decimal( '12345.678') # ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2) assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2], CosemObject) assert result[obis.ELECTRICITY_USED_TARIFF_2].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2].value, Decimal) assert result[obis.ELECTRICITY_USED_TARIFF_2].value == Decimal( '12345.678') # ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1) assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1], CosemObject) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value, Decimal) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value == Decimal( '12345.678') # ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2) assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2], CosemObject) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].unit == 'kWh' assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value, Decimal) assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value == Decimal( '12345.678') # ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0) assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF], CosemObject) assert result[obis.ELECTRICITY_ACTIVE_TARIFF].unit is None assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF].value, str) assert result[obis.ELECTRICITY_ACTIVE_TARIFF].value == '0002' # EQUIPMENT_IDENTIFIER (0-0:96.1.1) assert isinstance(result[obis.EQUIPMENT_IDENTIFIER], CosemObject) assert result[obis.EQUIPMENT_IDENTIFIER].unit is None assert isinstance(result[obis.EQUIPMENT_IDENTIFIER].value, str) assert result[ obis. EQUIPMENT_IDENTIFIER].value == '4B384547303034303436333935353037' # CURRENT_ELECTRICITY_USAGE (1-0:1.7.0) assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE], CosemObject) assert result[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW' assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE].value, Decimal) assert result[obis.CURRENT_ELECTRICITY_USAGE].value == Decimal('1.19') # CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0) assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY], CosemObject) assert result[obis.CURRENT_ELECTRICITY_DELIVERY].unit == 'kW' assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY].value, Decimal) assert result[obis.CURRENT_ELECTRICITY_DELIVERY].value == Decimal('0') # TEXT_MESSAGE_CODE (0-0:96.13.1) assert isinstance(result[obis.TEXT_MESSAGE_CODE], CosemObject) assert result[obis.TEXT_MESSAGE_CODE].unit is None assert isinstance(result[obis.TEXT_MESSAGE_CODE].value, int) assert result[obis.TEXT_MESSAGE_CODE].value == 303132333435363738 # TEXT_MESSAGE (0-0:96.13.0) assert isinstance(result[obis.TEXT_MESSAGE], CosemObject) assert result[obis.TEXT_MESSAGE].unit is None assert isinstance(result[obis.TEXT_MESSAGE].value, str) assert result[obis.TEXT_MESSAGE].value == \ '303132333435363738393A3B3C3D3E3F303132333435363738393A3B3C3D3E3F' \ '303132333435363738393A3B3C3D3E3F303132333435363738393A3B3C3D3E3F' \ '303132333435363738393A3B3C3D3E3F' # DEVICE_TYPE (0-x:24.1.0) assert isinstance(result[obis.TEXT_MESSAGE], CosemObject) assert result[obis.DEVICE_TYPE].unit is None assert isinstance(result[obis.DEVICE_TYPE].value, str) assert result[obis.DEVICE_TYPE].value == '03' # EQUIPMENT_IDENTIFIER_GAS (0-x:96.1.0) assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS], CosemObject) assert result[obis.EQUIPMENT_IDENTIFIER_GAS].unit is None assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS].value, str) assert result[ obis. EQUIPMENT_IDENTIFIER_GAS].value == '3232323241424344313233343536373839' # GAS_METER_READING (0-1:24.3.0) assert isinstance(result[obis.GAS_METER_READING], MBusObject) assert result[obis.GAS_METER_READING].unit == 'm3' assert isinstance(result[obis.GAS_METER_READING].value, Decimal) assert result[obis.GAS_METER_READING].value == Decimal('1.001')
class Terminal(object): def __init__( self, config, logger, hass_api, terminal_name, dsmr_serial_callback, dsmr_version, ): self.config = config self.logger = logger self.hass_api = hass_api self.terminal_name = terminal_name self.dsmr_serial_callback = dsmr_serial_callback self.mode = self.config.get(f"dsmr.{self.terminal_name}", "mode", "device") if self.mode not in ["tcp", "device"]: hybridlogger.ha_log( self.logger, self.hass_api, "ERROR", f"DSMR terminal {self.terminal_name} mode {self.mode} is not valid. " "Should be tcp or device. Ignoring DSMR configuration!", ) return self.device = self.config.get( f"dsmr.{self.terminal_name}", "device", "/dev/ttyUSB0" ) self.host = self.config.get(f"dsmr.{self.terminal_name}", "host", "localhost") self.port = self.config.get(f"dsmr.{self.terminal_name}", "port", "3333") self.dsmr_version = dsmr_version # start terminal self.stop = False hybridlogger.ha_log( self.logger, self.hass_api, "INFO", f"Initializing DSMR termimal '{terminal_name}'. Mode: {self.mode}.", ) if self.mode == "tcp": self.thr = threading.Thread( target=self._run_tcp_terminal, name=self.terminal_name ) elif self.mode == "device": self.thr = threading.Thread( target=self._run_serial_terminal, name=self.terminal_name ) self.thr.start() def terminate(self): self.stop = True # Wait for Thread to shutdown self.thr.join() def _get_dsmr_parser(self): dsmr_version = self.dsmr_version if dsmr_version == "2.2": specification = telegram_specifications.V2_2 serial_settings = SERIAL_SETTINGS_V2_2 elif dsmr_version == "4": specification = telegram_specifications.V4 serial_settings = SERIAL_SETTINGS_V4 elif dsmr_version == "5": specification = telegram_specifications.V5 serial_settings = SERIAL_SETTINGS_V5 elif dsmr_version == "5B": specification = telegram_specifications.BELGIUM_FLUVIUS serial_settings = SERIAL_SETTINGS_V5 else: raise NotImplementedError( "No telegram parser found for version: %s", dsmr_version ) self.telegram_parser = TelegramParser(specification) serial_settings["timeout"] = 10 self.serial_settings = serial_settings def _dsmr_data_received(self, data): """Add incoming data to buffer.""" data = data.decode("ascii") self.telegram_buffer.append(data) for telegram in self.telegram_buffer.get_all(): self._handle_telegram(telegram) def _handle_telegram(self, telegram): """Send off parsed telegram to handling callback.""" try: parsed_telegram = self.telegram_parser.parse(telegram) except InvalidChecksumError as e: self.logger.warning(str(e)) except ParseError: self.logger.exception("failed to parse telegram") else: self.dsmr_serial_callback(parsed_telegram) def _run_tcp_terminal(self): self._get_dsmr_parser() # buffer to keep incomplete incoming data while not self.stop: hybridlogger.ha_log( self.logger, self.hass_api, "INFO", f"DSMR terminal {self.terminal_name} was started.", ) try: self.telegram_buffer = TelegramBuffer() server_address = (self.host, self.port) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(5) self.sock.connect(server_address) while not self.stop: data = self.sock.recv(1024) if not data and not self.stop: hybridlogger.ha_log( self.logger, self.hass_api, "INFO", f"DSMR terminal {self.terminal_name} was interrupted and will be restarted.", ) time.sleep(5) break elif data: self._dsmr_data_received(data) except Exception as e: if not self.stop: hybridlogger.ha_log( self.logger, self.hass_api, "WARNING", f"DSMR terminal {self.terminal_name} was interrupted " f"and will be restarted in a few moments: {e}", ) traceback.print_exception(*sys.exc_info()) time.sleep(5) finally: self.sock.close() def _run_serial_terminal(self): self._get_dsmr_parser() # buffer to keep incomplete incoming data while not self.stop: hybridlogger.ha_log( self.logger, self.hass_api, "INFO", f"DSMR terminal {self.terminal_name} was started.", ) try: self.telegram_buffer = TelegramBuffer() self.sock = serial.Serial(port=self.device, **self.serial_settings) while not self.stop: data = self.sock.read_until() if not data and not self.stop: hybridlogger.ha_log( self.logger, self.hass_api, "INFO", f"DSMR terminal {self.terminal_name} was interrupted and will be restarted.", ) time.sleep(5) break elif data: self._dsmr_data_received(data) except Exception as e: if not self.stop: hybridlogger.ha_log( self.logger, self.hass_api, "WARNING", f"DSMR terminal {self.terminal_name} was interrupted " f"and will be restarted in a few moments: {e.args}", ) time.sleep(5) finally: self.sock.close()
def test_checksum_valid(self): # ParseErrorV4 should be raised. with self.assertRaises(NoChecksumError): TelegramParser.validate_checksum(TELEGRAM_V3)
from dsmr_parser import telegram_specifications from dsmr_parser.objects import Telegram from dsmr_parser.parsers import TelegramParser from example_telegrams import TELEGRAM_V4_2 parser = TelegramParser(telegram_specifications.V4) telegram = Telegram(TELEGRAM_V4_2, parser, telegram_specifications.V4) print(telegram)
class P1test(hass.Hass): def _logme(self, line): print(line) def initialize(self, *args, **kwargs): try: self.log("Using hass logger!") except Exception: self.log = self._logme self.log("Using test logger!") self.mode = 'tcp' self.host = 'homeassistant.fritz.box' self.port = 3333 self.device = 'COM3' self.dsmr_version = '5' self.terminal_name = 'test' self.stop = False self.transport = None self.log("Starting thread...") self.log("P1 test started") parser = self.test_serial # parser = self.tcp dsmr_version = self.dsmr_version if dsmr_version == '2.2': specification = telegram_specifications.V2_2 serial_settings = SERIAL_SETTINGS_V2_2 elif dsmr_version == '4': specification = telegram_specifications.V4 serial_settings = SERIAL_SETTINGS_V4 elif dsmr_version == '5': specification = telegram_specifications.V5 serial_settings = SERIAL_SETTINGS_V5 elif dsmr_version == '5B': specification = telegram_specifications.BELGIUM_FLUVIUS serial_settings = SERIAL_SETTINGS_V5 else: raise NotImplementedError( "No telegram parser found for version: %s", dsmr_version) self.telegram_parser = TelegramParser(specification) self.serial_settings = serial_settings # buffer to keep incomplete incoming data self.telegram_buffer = TelegramBuffer() self.thr = threading.Thread(target=parser, daemon=True) self.thr.start() self.log("Started!") # logging.basicConfig(level=logging.DEBUG) def dsmr_serial_callback(self, telegram): self.log('Telegram received') def terminate(self): self.stop = True self.log("Closing transport...") if self.transport: self.transport.close() self.thr.join(10) # Stopping loop the hard if self.thr.is_alive(): self.log( "Stopping the loop unfortunally did not stop the thread, waiting for completion..." ) else: self.log("Thread exited nicely") self.thr.join() self.log("Thread has stopped!") def test_serial(self): s = serial.Serial(port=self.device, **self.serial_settings) while not self.stop: data = s.read_until() print(f'{data}') self.data_received(data) s.close() def test_tcp(self): server_address = (self.host, self.port) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(server_address) while not self.stop: data = sock.recv(1024) self.data_received(data) sock.close() def data_received(self, data): """Add incoming data to buffer.""" data = data.decode('ascii') self.telegram_buffer.append(data) for telegram in self.telegram_buffer.get_all(): self.handle_telegram(telegram) def handle_telegram(self, telegram): """Send off parsed telegram to handling callback.""" try: parsed_telegram = self.telegram_parser.parse(telegram) except InvalidChecksumError as e: self.log.warning(str(e)) except ParseError: self.log.exception("failed to parse telegram") else: self.dsmr_serial_callback(parsed_telegram)