From b97ce89798e382a3a6602c2a21a8f26cf7d69452 Mon Sep 17 00:00:00 2001 From: Guzz-T Date: Tue, 27 Jan 2026 21:01:57 +0100 Subject: [PATCH 1/2] Refactor the definition data_type to field_type --- luxtronik/definitions/__init__.py | 10 +++++----- luxtronik/shi/__init__.py | 4 ++-- tests/shi/test_shi_interface.py | 6 +++--- tests/test_definition_list.py | 10 +++++----- tests/test_definitions.py | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/luxtronik/definitions/__init__.py b/luxtronik/definitions/__init__.py index 1541e53..3e0d6e5 100644 --- a/luxtronik/definitions/__init__.py +++ b/luxtronik/definitions/__init__.py @@ -61,7 +61,7 @@ def __init__(self, data_dict, type_name, offset): self._valid = index >= 0 self._index = index if self._valid else 0 self._count = int(data_dict["count"]) - self._data_type = data_dict["type"] + self._field_type = data_dict["type"] self._writeable = bool(data_dict["writeable"]) names = data_dict["names"] if not isinstance(names, list): @@ -108,7 +108,7 @@ def __bool__(self): return self._valid def __repr__(self): - return f"(name={self.name}, data_type={self.data_type}," \ + return f"(name={self.name}, field_type={self.field_type}," \ + f" index={self.index}, count={self.count})" @property @@ -138,8 +138,8 @@ def count(self): return self._count @property - def data_type(self): - return self._data_type + def field_type(self): + return self._field_type @property def writeable(self): @@ -173,7 +173,7 @@ def create_field(self): Returns: Base | None: Field instance or None if invalid. """ - return self.data_type(self.names, self.writeable) if self.valid else None + return self.field_type(self.names, self.writeable) if self.valid else None ############################################################################### diff --git a/luxtronik/shi/__init__.py b/luxtronik/shi/__init__.py index 506fa51..774693b 100644 --- a/luxtronik/shi/__init__.py +++ b/luxtronik/shi/__init__.py @@ -36,12 +36,12 @@ def get_version_definitions(definitions): definitions (LuxtronikDefinitionsList): List of definitions Returns: - list[LuxtronikDefinition]: List of definitions whose data_type + list[LuxtronikDefinition]: List of definitions whose field_type is either FullVersion or MajorMinorVersion. """ version_definitions = [] for d in definitions: - if d.data_type in (FullVersion, MajorMinorVersion): + if d.field_type in (FullVersion, MajorMinorVersion): version_definitions.append(d) return version_definitions diff --git a/tests/shi/test_shi_interface.py b/tests/shi/test_shi_interface.py index 11c47ac..527e542 100644 --- a/tests/shi/test_shi_interface.py +++ b/tests/shi/test_shi_interface.py @@ -155,7 +155,7 @@ def test_try_create(self): assert definition.index == 4 assert definition.count == 1 assert not definition.writeable - assert definition.data_type is Unknown + assert definition.field_type is Unknown # fail by name definition = self.interface._try_create_definition("unKnOWn_foo4", HOLDINGS_DEFINITIONS) @@ -171,7 +171,7 @@ def test_try_create(self): assert definition.index == 9 assert definition.count == 1 assert not definition.writeable - assert definition.data_type is Unknown + assert definition.field_type is Unknown # fail by else definition = self.interface._try_create_definition(Base, HOLDINGS_DEFINITIONS) @@ -184,7 +184,7 @@ def test_try_create(self): assert definition.index == 14 assert definition.count == 1 assert not definition.writeable - assert definition.data_type is Unknown + assert definition.field_type is Unknown def test_create_telegram(self): block = ContiguousDataBlock() diff --git a/tests/test_definition_list.py b/tests/test_definition_list.py index e749d89..8dae03d 100644 --- a/tests/test_definition_list.py +++ b/tests/test_definition_list.py @@ -65,7 +65,7 @@ def test_structure(self): assert isinstance(name, str), f"Entry of {KEY_NAMES} " \ f"must be of type 'int': {definition}" - # data_type + # field_type if KEY_TYPE in definition: assert issubclass(definition[KEY_TYPE], Base), \ f"{KEY_TYPE} must be inherit from 'Base': {definition}" @@ -176,13 +176,13 @@ def test_name_unique(self): f"this = {i_def}" \ f"other = {j_def}" - def test_data_type(self): + def test_field_type(self): for definition in self.definitions: - data_type = definition.get(KEY_TYPE, None) - assert issubclass(data_type, Base), \ + field_type = definition.get(KEY_TYPE, None) + assert issubclass(field_type, Base), \ f"Type must be set: {definition}" - def test_data_types(self): + def test_data_type(self): for definition in self.definitions: if KEY_DATATYPE in definition: data_type = definition[KEY_DATATYPE] diff --git a/tests/test_definitions.py b/tests/test_definitions.py index 2babfa1..6d6416a 100644 --- a/tests/test_definitions.py +++ b/tests/test_definitions.py @@ -31,7 +31,7 @@ def test_init(self): names = self.TEST_DATA['names'] assert definition.index == self.TEST_DATA['index'] assert definition.count == self.TEST_DATA['count'] - assert definition.data_type == self.TEST_DATA['type'] + assert definition.field_type == self.TEST_DATA['type'] assert definition.writeable == self.TEST_DATA['writeable'] assert definition.names == names assert definition.name == names[0] @@ -59,7 +59,7 @@ def test_init_unknown(self): names = ['unknown_foo_2'] assert definition.index == 2 assert definition.count == self.DEFAULT_DATA['count'] - assert definition.data_type == self.DEFAULT_DATA['type'] + assert definition.field_type == self.DEFAULT_DATA['type'] assert definition.writeable == self.DEFAULT_DATA['writeable'] assert definition.names == names assert definition.name == names[0] @@ -394,7 +394,7 @@ def test_create(self): definition = definitions.create_unknown_definition(4) assert definition.index == 4 assert definition.count == 1 - assert definition.data_type is Unknown + assert definition.field_type is Unknown assert not definition.writeable assert definition.names == ['unknown_foo_4'] assert definition.valid From d34fa254c970f82821f656128fb33895e8dba1ad Mon Sep 17 00:00:00 2001 From: Guzz-T Date: Tue, 27 Jan 2026 20:55:14 +0100 Subject: [PATCH 2/2] Generalize integrate_data and get_data_arr implementation so that it can be used for CFI and SHI The number of bits to be used is derived from the data type of the definitions. --- luxtronik/cfi/calculations.py | 9 +- luxtronik/cfi/constants.py | 5 +- luxtronik/cfi/parameters.py | 9 +- luxtronik/cfi/visibilities.py | 9 +- luxtronik/collections.py | 41 ++++---- luxtronik/constants.py | 4 + luxtronik/definitions/__init__.py | 50 +++++++-- luxtronik/definitions/calculations.py | 1 + luxtronik/definitions/holdings.py | 1 + luxtronik/definitions/inputs.py | 1 + luxtronik/definitions/parameters.py | 1 + luxtronik/definitions/visibilities.py | 1 + luxtronik/shi/constants.py | 4 - luxtronik/shi/contiguous.py | 5 +- luxtronik/shi/holdings.py | 9 +- luxtronik/shi/inputs.py | 9 +- luxtronik/shi/interface.py | 7 +- luxtronik/shi/vector.py | 4 +- tests/shi/test_shi_contiguous.py | 37 ++++--- tests/shi/test_shi_interface.py | 12 +-- tests/shi/test_shi_vector.py | 2 +- tests/test_collections.py | 139 ++++++++++++++++++++------ tests/test_definition_list.py | 5 +- tests/test_definitions.py | 16 +-- 24 files changed, 270 insertions(+), 111 deletions(-) diff --git a/luxtronik/cfi/calculations.py b/luxtronik/cfi/calculations.py index e552aa2..a547d32 100644 --- a/luxtronik/cfi/calculations.py +++ b/luxtronik/cfi/calculations.py @@ -4,7 +4,11 @@ from typing import Final from luxtronik.definitions import LuxtronikDefinitionsList -from luxtronik.definitions.calculations import CALCULATIONS_DEFINITIONS_LIST, CALCULATIONS_OFFSET +from luxtronik.definitions.calculations import ( + CALCULATIONS_DEFINITIONS_LIST, + CALCULATIONS_OFFSET, + CALCULATIONS_DEFAULT_DATA_TYPE, +) from luxtronik.cfi.constants import CALCULATIONS_FIELD_NAME from luxtronik.data_vector import DataVector @@ -16,7 +20,8 @@ CALCULATIONS_DEFINITIONS: Final = LuxtronikDefinitionsList( CALCULATIONS_DEFINITIONS_LIST, CALCULATIONS_FIELD_NAME, - CALCULATIONS_OFFSET + CALCULATIONS_OFFSET, + CALCULATIONS_DEFAULT_DATA_TYPE ) class Calculations(DataVector): diff --git a/luxtronik/cfi/constants.py b/luxtronik/cfi/constants.py index 5e0301b..ade2098 100644 --- a/luxtronik/cfi/constants.py +++ b/luxtronik/cfi/constants.py @@ -26,4 +26,7 @@ # Wait time (in seconds) after writing parameters to give controller # some time to re-calculate values, etc. -WAIT_TIME_AFTER_PARAMETER_WRITE = 1 \ No newline at end of file +WAIT_TIME_AFTER_PARAMETER_WRITE = 1 + +# The data from the config interface are transmitted in 32-bit chunks. +LUXTRONIK_CFI_REGISTER_BIT_SIZE: Final = 32 \ No newline at end of file diff --git a/luxtronik/cfi/parameters.py b/luxtronik/cfi/parameters.py index 1ffa9be..a27a51c 100644 --- a/luxtronik/cfi/parameters.py +++ b/luxtronik/cfi/parameters.py @@ -4,7 +4,11 @@ from typing import Final from luxtronik.definitions import LuxtronikDefinitionsList -from luxtronik.definitions.parameters import PARAMETERS_DEFINITIONS_LIST, PARAMETERS_OFFSET +from luxtronik.definitions.parameters import ( + PARAMETERS_DEFINITIONS_LIST, + PARAMETERS_OFFSET, + PARAMETERS_DEFAULT_DATA_TYPE, +) from luxtronik.cfi.constants import PARAMETERS_FIELD_NAME from luxtronik.data_vector import DataVector @@ -15,7 +19,8 @@ PARAMETERS_DEFINITIONS: Final = LuxtronikDefinitionsList( PARAMETERS_DEFINITIONS_LIST, PARAMETERS_FIELD_NAME, - PARAMETERS_OFFSET + PARAMETERS_OFFSET, + PARAMETERS_DEFAULT_DATA_TYPE ) class Parameters(DataVector): diff --git a/luxtronik/cfi/visibilities.py b/luxtronik/cfi/visibilities.py index a582250..f250564 100644 --- a/luxtronik/cfi/visibilities.py +++ b/luxtronik/cfi/visibilities.py @@ -4,7 +4,11 @@ from typing import Final from luxtronik.definitions import LuxtronikDefinitionsList -from luxtronik.definitions.visibilities import VISIBILITIES_DEFINITIONS_LIST, VISIBILITIES_OFFSET +from luxtronik.definitions.visibilities import ( + VISIBILITIES_DEFINITIONS_LIST, + VISIBILITIES_OFFSET, + VISIBILITIES_DEFAULT_DATA_TYPE, +) from luxtronik.cfi.constants import VISIBILITIES_FIELD_NAME from luxtronik.data_vector import DataVector @@ -15,7 +19,8 @@ VISIBILITIES_DEFINITIONS: Final = LuxtronikDefinitionsList( VISIBILITIES_DEFINITIONS_LIST, VISIBILITIES_FIELD_NAME, - VISIBILITIES_OFFSET + VISIBILITIES_OFFSET, + VISIBILITIES_DEFAULT_DATA_TYPE, ) class Visibilities(DataVector): diff --git a/luxtronik/collections.py b/luxtronik/collections.py index ed6a5da..523e7b2 100644 --- a/luxtronik/collections.py +++ b/luxtronik/collections.py @@ -5,9 +5,6 @@ from luxtronik.datatypes import Base from luxtronik.definitions import LuxtronikDefinition, LuxtronikDefinitionsDictionary -# TODO: Remove SHI dependency -LUXTRONIK_SHI_REGISTER_BIT_SIZE = 16 -LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE = 0x7FFF LOGGER = logging.getLogger(__name__) @@ -16,14 +13,13 @@ # Common methods ############################################################################### -VALUE_MASK = (1 << LUXTRONIK_SHI_REGISTER_BIT_SIZE) - 1 - -def pack_values(values, reverse=True): +def pack_values(values, num_bits, reverse=True): """ Packs a list of data chunks into one integer. Args: values (list[int]): raw data; distributed across multiple registers. + num_bits (int): Number of bits per chunk. reverse (bool): Use big-endian/MSB-first if true, otherwise use little-endian/LSB-first order. @@ -34,6 +30,7 @@ def pack_values(values, reverse=True): The smart home interface uses a chunk size of 16 bits. """ count = len(values) + mask = (1 << num_bits) - 1 result = 0 for idx, value in enumerate(values): @@ -41,17 +38,18 @@ def pack_values(values, reverse=True): # reversed index: highest chunk first bit_index = (count - 1 - idx) if reverse else idx - result |= (value & VALUE_MASK) << (LUXTRONIK_SHI_REGISTER_BIT_SIZE * bit_index) + result |= (value & mask) << (num_bits * bit_index) return result -def unpack_values(packed, count, reverse=True): +def unpack_values(packed, count, num_bits, reverse=True): """ Unpacks 'count' chunks from a packed integer. Args: packed (int): Packed raw data as a single integer value. count (int): Number of chunks to unpack. + num_bits (int): Number of bits per chunk. reverse (bool): Use big-endian/MSB-first if true, otherwise use little-endian/LSB-first order. @@ -62,24 +60,26 @@ def unpack_values(packed, count, reverse=True): The smart home interface uses a chunk size of 16 bits. """ values = [] + mask = (1 << num_bits) - 1 for idx in range(count): # normal: idx = 0..n-1 # reversed: highest chunk first bit_index = (count - 1 - idx) if reverse else idx - chunk = (packed >> (LUXTRONIK_SHI_REGISTER_BIT_SIZE * bit_index)) & VALUE_MASK + chunk = (packed >> (num_bits * bit_index)) & mask values.append(chunk) return values -def get_data_arr(definition, field): +def get_data_arr(definition, field, num_bits): """ Normalize the field's data to a list of the correct size. Args: definition (LuxtronikDefinition): Meta-data of the field. field (Base): Field object that contains data to get. + num_bits (int): Number of bits per register. Returns: list[int] | None: List of length `definition.count`, @@ -92,12 +92,12 @@ def get_data_arr(definition, field): and definition.count > 1 if should_unpack and not isinstance(data, list): # Usually big-endian (reverse=True) is used - data = unpack_values(data, definition.count) + data = unpack_values(data, definition.count, num_bits) if not isinstance(data, list): data = [data] return data if len(data) == definition.count else None -def integrate_data(definition, field, raw_data, data_offset=-1): +def integrate_data(definition, field, raw_data, num_bits, data_offset=-1): """ Integrate the related parts of the `raw_data` into the field. @@ -105,6 +105,7 @@ def integrate_data(definition, field, raw_data, data_offset=-1): definition (LuxtronikDefinition): Meta-data of the field. field (Base): Field object where to integrate the data. raw_data (list): Source array of register values. + num_bits (int): Number of bits per register. data_offset (int): Optional offset. Defaults to `definition.index`. """ # Use data_offset if provided, otherwise the index @@ -114,15 +115,15 @@ def integrate_data(definition, field, raw_data, data_offset=-1): raw = None elif definition.count == 1: raw = raw_data[data_offset] - raw = raw if raw != LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE else None else: raw = raw_data[data_offset : data_offset + definition.count] - raw = raw if len(raw) == definition.count and \ - not any(data == LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE for data in raw) else None + raw = raw if len(raw) == definition.count else None should_pack = field.concatenate_multiple_data_chunks if should_pack and raw is not None : # Usually big-endian (reverse=True) is used - raw = pack_values(raw) + raw = pack_values(raw, num_bits) + + raw = raw if definition.check_raw_not_none(raw) else None field.raw = raw ############################################################################### @@ -176,19 +177,19 @@ def count(self): """ return self.definition.count - def get_data_arr(self): + def get_data_arr(self, num_bits): """ Forward the `get_data_arr` method with the stored objects. Please check its documentation. """ - return get_data_arr(self.definition, self.field) + return get_data_arr(self.definition, self.field, num_bits) - def integrate_data(self, raw_data, data_offset=-1): + def integrate_data(self, raw_data, num_bits, data_offset=-1): """ Forward the `integrate_data` method with the stored objects. Please check its documentation. """ - integrate_data(self.definition, self.field, raw_data, data_offset) + integrate_data(self.definition, self.field, raw_data, num_bits, data_offset) ############################################################################### # Field dictionary for data vectors diff --git a/luxtronik/constants.py b/luxtronik/constants.py index 7434ea7..894b3d3 100644 --- a/luxtronik/constants.py +++ b/luxtronik/constants.py @@ -14,6 +14,10 @@ # Content of response that is contained in responses to discovery broadcast LUXTRONIK_DISCOVERY_RESPONSE_PREFIX: Final = "2500;111;" +# Since version 3.92.0, all unavailable 16 bit signed data fields +# have been returning this value (0x7FFF) +LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE: Final = 32767 + LUXTRONIK_NAME_CHECK_NONE: Final = "none" LUXTRONIK_NAME_CHECK_PREFERRED: Final = "preferred" LUXTRONIK_NAME_CHECK_OBSOLETE: Final = "obsolete" diff --git a/luxtronik/definitions/__init__.py b/luxtronik/definitions/__init__.py index 3e0d6e5..a467216 100644 --- a/luxtronik/definitions/__init__.py +++ b/luxtronik/definitions/__init__.py @@ -9,6 +9,7 @@ import logging +from luxtronik.constants import LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE from luxtronik.common import ( parse_version, version_in_range @@ -37,9 +38,15 @@ class LuxtronikDefinition: "names": [], "since": "", "until": "", + "datatype": "", "description": "", } + # It is permissible not to specify a data type. + # In this case, all functions based on it will be disabled. + VALID_DATA_TYPES = ("", "UINT16", "UINT32", "UINT64", "INT16", "INT32", "INT64") + + def __init__(self, data_dict, type_name, offset): """ Initialize a definition from a data-dictionary. @@ -80,13 +87,19 @@ def __init__(self, data_dict, type_name, offset): self._valid &= len(self._type_name) > 0 self._offset = int(offset) self._addr = self._offset + self._index + self._data_type = data_dict["datatype"] + data_type_valid = self._data_type in self.VALID_DATA_TYPES + self._valid &= data_type_valid + data_type_valid &= self._data_type != "" + self._num_bits = int(self._data_type.replace('U', '').replace('INT', '')) \ + if data_type_valid else 0 except Exception as e: self._valid = False self._index = 0 LOGGER.error(f"Failed to create LuxtronikDefinition: '{e}' with {data_dict}") @classmethod - def unknown(cls, index, type_name, offset): + def unknown(cls, index, type_name, offset, data_type=""): """ Create an "unknown" definition. @@ -94,13 +107,15 @@ def unknown(cls, index, type_name, offset): index (int): The register index of the "unknown" definition. type_name (str): The type name e.g. 'holding', 'input', ... . offset (str): Offset of the address from the specified index. + data_type (str): Data type of the field (see VALID_DATA_TYPES). Returns: LuxtronikDefinition: A definition marked as unknown. """ return cls({ "index": index, - "names": [f"unknown_{type_name.lower()}_{index}"] + "names": [f"unknown_{type_name.lower()}_{index}"], + "datatype": data_type, }, type_name, offset) def __bool__(self): @@ -145,6 +160,14 @@ def field_type(self): def writeable(self): return self._writeable + @property + def data_type(self): + return self._data_type + + @property + def num_bits(self): + return self._num_bits + @property def names(self): return self._names @@ -175,6 +198,18 @@ def create_field(self): """ return self.field_type(self.names, self.writeable) if self.valid else None + def check_raw_not_none(self, raw): + """ + Check if the related raw value to this definition represents not 'not available'. + + Args: + raw (int): Raw-value to check. + """ + # TODO: Check if there are other magic values + if isinstance(raw, int) and self._data_type in ['INT16']: + return raw != LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE + return True + ############################################################################### # LuxtronikDefinitionsDictionary @@ -391,16 +426,17 @@ class LuxtronikDefinitionsList: (locally = only valid for that dictionary). """ - def _init_instance(self, name, offset, version): + def _init_instance(self, name, offset, default_data_type, version): """Re-usable method to initialize all instance variables.""" self._name = name self._offset = offset + self._default_data_type = default_data_type self._version = version # sorted list of all definitions self._definitions = [] self._lookup = LuxtronikDefinitionsDictionary() - def __init__(self, definitions_list, name, offset): + def __init__(self, definitions_list, name, offset, default_data_type): """ Initialize the (by index sorted) definitions list. @@ -417,7 +453,7 @@ def __init__(self, definitions_list, name, offset): - The value of count must always be greater than or equal to 1 - All names should be unique """ - self._init_instance(name, offset, None) + self._init_instance(name, offset, default_data_type, None) # Add definition objects only for valid items. # The correct sorting has already been ensured by the pytest @@ -439,7 +475,7 @@ def filtered(cls, definitions, version): If None is passed, all available fields are added. """ obj = cls.__new__(cls) # this don't call __init__() - obj._init_instance(definitions.name, definitions.offset, version) + obj._init_instance(definitions.name, definitions.offset, definitions._default_data_type, version) for d in definitions: if d.valid and version_in_range(obj._version, d.since, d.until): @@ -473,7 +509,7 @@ def create_unknown_definition(self, index): Returns: LuxtronikDefinition: A definition marked as unknown. """ - return LuxtronikDefinition.unknown(index, self._name, self._offset) + return LuxtronikDefinition.unknown(index, self._name, self._offset, self._default_data_type) def register_alias(self, def_name_or_idx, alias): """ diff --git a/luxtronik/definitions/calculations.py b/luxtronik/definitions/calculations.py index eda7834..4ca70ae 100644 --- a/luxtronik/definitions/calculations.py +++ b/luxtronik/definitions/calculations.py @@ -47,6 +47,7 @@ # Offset which must be added to the calculation indices # to obtain the correct address of the data fields CALCULATIONS_OFFSET: Final = 0 +CALCULATIONS_DEFAULT_DATA_TYPE: Final = 'INT32' CALCULATIONS_DEFINITIONS_LIST: Final = [ { diff --git a/luxtronik/definitions/holdings.py b/luxtronik/definitions/holdings.py index 119b096..2c56d9d 100644 --- a/luxtronik/definitions/holdings.py +++ b/luxtronik/definitions/holdings.py @@ -29,6 +29,7 @@ # Offset which must be added to the holding indices # to obtain the correct address of the data fields HOLDINGS_OFFSET: Final = 10000 +HOLDINGS_DEFAULT_DATA_TYPE: Final = 'INT16' HOLDINGS_DEFINITIONS_LIST: Final = [ { diff --git a/luxtronik/definitions/inputs.py b/luxtronik/definitions/inputs.py index 6a4322a..c266f52 100644 --- a/luxtronik/definitions/inputs.py +++ b/luxtronik/definitions/inputs.py @@ -33,6 +33,7 @@ # Offset which must be added to the input indices # to obtain the correct address of the data fields INPUTS_OFFSET: Final = 10000 +INPUTS_DEFAULT_DATA_TYPE: Final = 'INT16' INPUTS_DEFINITIONS_LIST: Final = [ { diff --git a/luxtronik/definitions/parameters.py b/luxtronik/definitions/parameters.py index b6fd5bf..ce5d3f3 100644 --- a/luxtronik/definitions/parameters.py +++ b/luxtronik/definitions/parameters.py @@ -41,6 +41,7 @@ # Offset which must be added to the parameter indices # to obtain the correct address of the data fields PARAMETERS_OFFSET: Final = 0 +PARAMETERS_DEFAULT_DATA_TYPE: Final = 'INT32' PARAMETERS_DEFINITIONS_LIST: Final = [ { diff --git a/luxtronik/definitions/visibilities.py b/luxtronik/definitions/visibilities.py index d386d24..8f5d0b6 100644 --- a/luxtronik/definitions/visibilities.py +++ b/luxtronik/definitions/visibilities.py @@ -18,6 +18,7 @@ # Offset which must be added to the visibility indices # to obtain the correct address of the data fields VISIBILITIES_OFFSET: Final = 0 +VISIBILITIES_DEFAULT_DATA_TYPE: Final = 'INT32' VISIBILITIES_DEFINITIONS_LIST: Final = [ { diff --git a/luxtronik/shi/constants.py b/luxtronik/shi/constants.py index 93248a5..5258b7f 100644 --- a/luxtronik/shi/constants.py +++ b/luxtronik/shi/constants.py @@ -21,10 +21,6 @@ # The data from the smart home interface are transmitted in 16-bit chunks. LUXTRONIK_SHI_REGISTER_BIT_SIZE: Final = 16 -# Since version 3.92.0, all unavailable data fields -# have been returning this value (0x7FFF) -LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE: Final = 32767 - # First Luxtronik firmware version that supports the smart home interface (SHI) LUXTRONIK_FIRST_VERSION_WITH_SHI: Final = (3, 90, 1, 0) diff --git a/luxtronik/shi/contiguous.py b/luxtronik/shi/contiguous.py index 52ec1f8..fcb6c50 100644 --- a/luxtronik/shi/contiguous.py +++ b/luxtronik/shi/contiguous.py @@ -7,6 +7,7 @@ import logging from luxtronik.collections import LuxtronikDefFieldPair +from luxtronik.shi.constants import LUXTRONIK_SHI_REGISTER_BIT_SIZE LOGGER = logging.getLogger(__name__) @@ -170,7 +171,7 @@ def integrate_data(self, data_arr): first = self.first_index for part in self._parts: data_offset = part.index - first - part.integrate_data(data_arr, data_offset) + part.integrate_data(data_arr, LUXTRONIK_SHI_REGISTER_BIT_SIZE, data_offset) return True @@ -191,7 +192,7 @@ def get_data_arr(self): valid = True for part in self._parts: data_offset = part.index - first - data = part.get_data_arr() + data = part.get_data_arr(LUXTRONIK_SHI_REGISTER_BIT_SIZE) if data is None: valid = False diff --git a/luxtronik/shi/holdings.py b/luxtronik/shi/holdings.py index 33ffcac..c824a8e 100644 --- a/luxtronik/shi/holdings.py +++ b/luxtronik/shi/holdings.py @@ -4,7 +4,11 @@ from typing import Final from luxtronik.definitions import LuxtronikDefinitionsList -from luxtronik.definitions.holdings import HOLDINGS_DEFINITIONS_LIST, HOLDINGS_OFFSET +from luxtronik.definitions.holdings import ( + HOLDINGS_DEFINITIONS_LIST, + HOLDINGS_OFFSET, + HOLDINGS_DEFAULT_DATA_TYPE, +) from luxtronik.shi.constants import HOLDINGS_FIELD_NAME from luxtronik.shi.vector import DataVectorSmartHome @@ -15,7 +19,8 @@ HOLDINGS_DEFINITIONS: Final = LuxtronikDefinitionsList( HOLDINGS_DEFINITIONS_LIST, HOLDINGS_FIELD_NAME, - HOLDINGS_OFFSET + HOLDINGS_OFFSET, + HOLDINGS_DEFAULT_DATA_TYPE, ) class Holdings(DataVectorSmartHome): diff --git a/luxtronik/shi/inputs.py b/luxtronik/shi/inputs.py index e5db66e..7bac711 100644 --- a/luxtronik/shi/inputs.py +++ b/luxtronik/shi/inputs.py @@ -4,7 +4,11 @@ from typing import Final from luxtronik.definitions import LuxtronikDefinitionsList -from luxtronik.definitions.inputs import INPUTS_DEFINITIONS_LIST, INPUTS_OFFSET +from luxtronik.definitions.inputs import ( + INPUTS_DEFINITIONS_LIST, + INPUTS_OFFSET, + INPUTS_DEFAULT_DATA_TYPE, +) from luxtronik.shi.constants import INPUTS_FIELD_NAME from luxtronik.shi.vector import DataVectorSmartHome @@ -15,7 +19,8 @@ INPUTS_DEFINITIONS: Final = LuxtronikDefinitionsList( INPUTS_DEFINITIONS_LIST, INPUTS_FIELD_NAME, - INPUTS_OFFSET + INPUTS_OFFSET, + INPUTS_DEFAULT_DATA_TYPE, ) class Inputs(DataVectorSmartHome): diff --git a/luxtronik/shi/interface.py b/luxtronik/shi/interface.py index f377c5e..025bbc0 100644 --- a/luxtronik/shi/interface.py +++ b/luxtronik/shi/interface.py @@ -9,7 +9,10 @@ LuxtronikDefinition, LuxtronikDefinitionsList, ) -from luxtronik.shi.constants import LUXTRONIK_LATEST_SHI_VERSION +from luxtronik.shi.constants import ( + LUXTRONIK_LATEST_SHI_VERSION, + LUXTRONIK_SHI_REGISTER_BIT_SIZE +) from luxtronik.shi.common import ( LuxtronikSmartHomeReadHoldingsTelegram, LuxtronikSmartHomeReadInputsTelegram, @@ -387,7 +390,7 @@ def _prepare_write_field(self, definition, field, safe, data): field.value = data # Abort if insufficient data is provided - if get_data_arr(definition, field) is None: + if not get_data_arr(definition, field, LUXTRONIK_SHI_REGISTER_BIT_SIZE): LOGGER.warning("Data error / insufficient data provided: " \ + f"name={definition.name}, data={field.raw}") return False diff --git a/luxtronik/shi/vector.py b/luxtronik/shi/vector.py index 7e087af..1f333e9 100644 --- a/luxtronik/shi/vector.py +++ b/luxtronik/shi/vector.py @@ -7,7 +7,7 @@ from luxtronik.datatypes import Base, Unknown from luxtronik.definitions import LuxtronikDefinition -from luxtronik.shi.constants import LUXTRONIK_LATEST_SHI_VERSION +from luxtronik.shi.constants import LUXTRONIK_LATEST_SHI_VERSION, LUXTRONIK_SHI_REGISTER_BIT_SIZE from luxtronik.shi.contiguous import ContiguousDataBlockList @@ -304,7 +304,7 @@ def parse(self, raw_data): for definition, field in self._data.pairs(): if definition.index + definition.count >= raw_len: continue - integrate_data(definition, field, raw_data) + integrate_data(definition, field, raw_data, LUXTRONIK_SHI_REGISTER_BIT_SIZE) def get(self, def_name_or_idx, default=None): """ diff --git a/tests/shi/test_shi_contiguous.py b/tests/shi/test_shi_contiguous.py index cc655da..62a27d5 100644 --- a/tests/shi/test_shi_contiguous.py +++ b/tests/shi/test_shi_contiguous.py @@ -1,7 +1,8 @@ +from luxtronik.constants import LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE from luxtronik.datatypes import Base from luxtronik.definitions import LuxtronikDefinition -from luxtronik.shi.constants import LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE +from luxtronik.shi.constants import LUXTRONIK_SHI_REGISTER_BIT_SIZE from luxtronik.shi.contiguous import ( ContiguousDataPart, ContiguousDataBlock, @@ -19,26 +20,32 @@ def_a1 = LuxtronikDefinition({ 'index': 1, 'count': 1, + 'datatype': 'INT16', }, 'test', 100) def_a = LuxtronikDefinition({ 'index': 1, 'count': 2, + 'datatype': 'INT16', }, 'test', 100) def_b = LuxtronikDefinition({ 'index': 3, 'count': 1, + 'datatype': 'INT16', }, 'test', 100) def_c = LuxtronikDefinition({ 'index': 4, 'count': 3, + 'datatype': 'INT16', }, 'test', 100) def_c1 = LuxtronikDefinition({ 'index': 4, 'count': 1, + 'datatype': 'INT16', }, 'test', 100) def_c2 = LuxtronikDefinition({ 'index': 5, 'count': 1, + 'datatype': 'INT16', }, 'test', 100) defs = [] @@ -67,49 +74,49 @@ def test_repr(self): def test_get_data(self): part = ContiguousDataPart(def_a, field_a) field_a.raw = [4, 2] - assert part.get_data_arr() == [4, 2] + assert part.get_data_arr(LUXTRONIK_SHI_REGISTER_BIT_SIZE) == [4, 2] field_a.raw = [1, 3, 5] - assert part.get_data_arr() is None + assert part.get_data_arr(LUXTRONIK_SHI_REGISTER_BIT_SIZE) is None field_a.raw = [9] - assert part.get_data_arr() is None + assert part.get_data_arr(LUXTRONIK_SHI_REGISTER_BIT_SIZE) is None part = ContiguousDataPart(def_a1, field_a1) field_a1.raw = [8] - assert part.get_data_arr() == [8] + assert part.get_data_arr(LUXTRONIK_SHI_REGISTER_BIT_SIZE) == [8] field_a1.raw = 7 - assert part.get_data_arr() == [7] + assert part.get_data_arr(LUXTRONIK_SHI_REGISTER_BIT_SIZE) == [7] def test_integrate_data(self): part = ContiguousDataPart(def_a, field_a) - part.integrate_data([1, 5, 7, 9], 0) + part.integrate_data([1, 5, 7, 9], LUXTRONIK_SHI_REGISTER_BIT_SIZE, 0) assert part.field.raw == [1, 5] - part.integrate_data([1, 5, 7, 9]) + part.integrate_data([1, 5, 7, 9], LUXTRONIK_SHI_REGISTER_BIT_SIZE) assert part.field.raw == [5, 7] - part.integrate_data([1, 5, 7, 9], 2) + part.integrate_data([1, 5, 7, 9], LUXTRONIK_SHI_REGISTER_BIT_SIZE, 2) assert part.field.raw == [7, 9] - part.integrate_data([1, 5, 7, 9], 3) + part.integrate_data([1, 5, 7, 9], LUXTRONIK_SHI_REGISTER_BIT_SIZE, 3) assert part.field.raw is None - part.integrate_data([1, 5, LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE, 9], 1) - assert part.field.raw is None + part.integrate_data([1, 5, LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE, 9], LUXTRONIK_SHI_REGISTER_BIT_SIZE, 1) + assert part.field.raw == [5, LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE] part = ContiguousDataPart(def_c1, field_c1) - part.integrate_data([2, 4, 6], 1) + part.integrate_data([2, 4, 6], LUXTRONIK_SHI_REGISTER_BIT_SIZE, 1) assert part.field.raw == 4 - part.integrate_data([2, 4, LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE], 2) + part.integrate_data([2, 4, LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE], LUXTRONIK_SHI_REGISTER_BIT_SIZE, 2) assert part.field.raw is None - part.integrate_data([2, 4, 6], 5) + part.integrate_data([2, 4, 6], LUXTRONIK_SHI_REGISTER_BIT_SIZE, 5) assert part.field.raw is None diff --git a/tests/shi/test_shi_interface.py b/tests/shi/test_shi_interface.py index 527e542..dd1596a 100644 --- a/tests/shi/test_shi_interface.py +++ b/tests/shi/test_shi_interface.py @@ -1,13 +1,13 @@ import pytest from unittest.mock import patch +from luxtronik.constants import LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE from luxtronik.datatypes import Base, Unknown from luxtronik.definitions import LuxtronikDefinition from luxtronik.shi.constants import ( LUXTRONIK_LATEST_SHI_VERSION, LUXTRONIK_FIRST_VERSION_WITH_SHI, - LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE, ) from luxtronik.shi.common import ( LuxtronikSmartHomeReadHoldingsTelegram, @@ -255,7 +255,7 @@ def test_create_telegrams(self): blocks.append(block) # block 2 - blocks.append_single(HOLDINGS_DEFINITIONS[13], HOLDINGS_DEFINITIONS[13].create_field()) + blocks.append_single(HOLDINGS_DEFINITIONS[17], HOLDINGS_DEFINITIONS[17].create_field()) # block 3 blocks.append_single(HOLDINGS_DEFINITIONS[10], HOLDINGS_DEFINITIONS[10].create_field()) @@ -267,8 +267,8 @@ def test_create_telegrams(self): # invalid block blocks.append_single(HOLDINGS_DEFINITIONS[12], HOLDINGS_DEFINITIONS[12].create_field()) # block 4 - field3 = HOLDINGS_DEFINITIONS[13].create_field() - blocks.append_single(HOLDINGS_DEFINITIONS[13], field3) + field3 = HOLDINGS_DEFINITIONS[17].create_field() + blocks.append_single(HOLDINGS_DEFINITIONS[17], field3) blocks_list.append(blocks) @@ -281,13 +281,13 @@ def test_create_telegrams(self): assert telegram_data[0][0].first_index == 10 assert telegram_data[0][0].overall_count == 2 assert len(telegram_data[1][0]) == 1 - assert telegram_data[1][0].first_index == 13 + assert telegram_data[1][0].first_index == 17 assert telegram_data[1][0].overall_count == 1 assert len(telegram_data[2][0]) == 1 assert telegram_data[2][0].first_index == 10 assert telegram_data[2][0].overall_count == 1 assert len(telegram_data[3][0]) == 1 - assert telegram_data[3][0].first_index == 13 + assert telegram_data[3][0].first_index == 17 assert telegram_data[3][0].overall_count == 1 # telegrams assert telegram_data[0][1].count == 2 diff --git a/tests/shi/test_shi_vector.py b/tests/shi/test_shi_vector.py index a3a4137..23aa781 100644 --- a/tests/shi/test_shi_vector.py +++ b/tests/shi/test_shi_vector.py @@ -59,7 +59,7 @@ "until": "3.3", }, ] -TEST_DEFINITIONS = LuxtronikDefinitionsList(def_list, 'foo', 100) +TEST_DEFINITIONS = LuxtronikDefinitionsList(def_list, 'foo', 100, 'INT16') class DataVectorTest(DataVectorSmartHome): name = 'foo' diff --git a/tests/test_collections.py b/tests/test_collections.py index 7cd218a..a1f41ad 100644 --- a/tests/test_collections.py +++ b/tests/test_collections.py @@ -10,7 +10,7 @@ Base, Unknown, ) -from luxtronik.shi.constants import LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE +from luxtronik.constants import LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE ############################################################################### @@ -40,53 +40,74 @@ def test_data_arr(self): # get from value definition._count = 1 field.raw = 5 - arr = get_data_arr(definition, field) + arr = get_data_arr(definition, field, 32) assert arr == [5] - assert arr == pair.get_data_arr() + assert arr == pair.get_data_arr(32) + + # get from value + definition._count = 1 + field.raw = 5 + arr = get_data_arr(definition, field, 16) + assert arr == [5] + assert arr == pair.get_data_arr(16) # get from array definition._count = 2 field.raw = [7, 3] - arr = get_data_arr(definition, field) + arr = get_data_arr(definition, field, 32) assert arr == [7, 3] - assert arr == pair.get_data_arr() + assert arr == pair.get_data_arr(32) + + # get from array + definition._count = 2 + field.raw = [7, 3] + arr = get_data_arr(definition, field, 16) + assert arr == [7, 3] + assert arr == pair.get_data_arr(16) # too much data definition._count = 2 field.raw = [4, 8, 1] - arr = get_data_arr(definition, field) + arr = get_data_arr(definition, field, 16) assert arr is None - assert arr == pair.get_data_arr() + assert arr == pair.get_data_arr(16) # insufficient data definition._count = 2 field.raw = [9] - arr = get_data_arr(definition, field) + arr = get_data_arr(definition, field, 16) assert arr is None - assert arr == pair.get_data_arr() + assert arr == pair.get_data_arr(16) field.concatenate_multiple_data_chunks = True + # get from array + definition._count = 2 + field.raw = 0x00000007_00000003 + arr = get_data_arr(definition, field, 32) + assert arr == [7, 3] + assert arr == pair.get_data_arr(32) + # get from array definition._count = 2 field.raw = 0x0007_0003 - arr = get_data_arr(definition, field) + arr = get_data_arr(definition, field, 16) assert arr == [7, 3] - assert arr == pair.get_data_arr() + assert arr == pair.get_data_arr(16) # too much data definition._count = 2 field.raw = 0x0004_0008_0001 - arr = get_data_arr(definition, field) + arr = get_data_arr(definition, field, 16) assert arr == [8, 1] - assert arr == pair.get_data_arr() + assert arr == pair.get_data_arr(16) # insufficient data definition._count = 2 field.raw = 0x0009 - arr = get_data_arr(definition, field) + arr = get_data_arr(definition, field, 16) assert arr == [0, 9] - assert arr == pair.get_data_arr() + assert arr == pair.get_data_arr(16) def test_integrate(self): definition = LuxtronikDefinition.unknown(2, 'Foo', 30) @@ -98,50 +119,106 @@ def test_integrate(self): # set array definition._count = 2 - integrate_data(definition, field, data) + definition._data_type = 'INT64' + integrate_data(definition, field, data, 32) + assert field.raw == [3, 4] + pair.integrate_data(data, 32, 4) + assert field.raw == [5, 6] + integrate_data(definition, field, data, 32, 7) + assert field.raw is None + pair.integrate_data(data, 32, 0) + assert field.raw == [1, LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE] + + # set array + definition._count = 2 + definition._data_type = 'INT32' + integrate_data(definition, field, data, 16) assert field.raw == [3, 4] - pair.integrate_data(data, 4) + pair.integrate_data(data, 16, 4) assert field.raw == [5, 6] - integrate_data(definition, field, data, 7) + integrate_data(definition, field, data, 16, 7) assert field.raw is None - pair.integrate_data(data, 0) + pair.integrate_data(data, 16, 0) + assert field.raw == [1, LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE] + + # set value + definition._count = 1 + definition._data_type = 'INT32' + integrate_data(definition, field, data, 32) + assert field.raw == 3 + pair.integrate_data(data, 32, 5) + assert field.raw == 6 + integrate_data(definition, field, data, 32, 9) assert field.raw is None + pair.integrate_data(data, 32, 1) + # Currently there is no magic "not available" value for 32 bit values -> not None + # This applies also to similar lines below + assert field.raw == LUXTRONIK_VALUE_FUNCTION_NOT_AVAILABLE # set value definition._count = 1 - integrate_data(definition, field, data) + definition._data_type = 'INT16' + integrate_data(definition, field, data, 16) assert field.raw == 3 - pair.integrate_data(data, 5) + pair.integrate_data(data, 16, 5) assert field.raw == 6 - integrate_data(definition, field, data, 9) + integrate_data(definition, field, data, 16, 9) assert field.raw is None - pair.integrate_data(data, 1) + pair.integrate_data(data, 16, 1) assert field.raw is None field.concatenate_multiple_data_chunks = True # set array definition._count = 2 - integrate_data(definition, field, data) + definition._data_type = 'INT64' + integrate_data(definition, field, data, 32) + assert field.raw == 0x00000003_00000004 + pair.integrate_data(data, 32, 4) + assert field.raw == 0x00000005_00000006 + integrate_data(definition, field, data, 32, 7) + assert field.raw is None + pair.integrate_data(data, 32, 0) + assert field.raw == 0x00000001_00007FFF + + # set array + definition._count = 2 + definition._data_type = 'INT32' + integrate_data(definition, field, data, 16) assert field.raw == 0x0003_0004 - pair.integrate_data(data, 4) + pair.integrate_data(data, 16, 4) assert field.raw == 0x0005_0006 - integrate_data(definition, field, data, 7) + integrate_data(definition, field, data, 16, 7) assert field.raw is None - pair.integrate_data(data, 0) + pair.integrate_data(data, 16, 0) + assert field.raw == 0x0001_7FFF + + # set value + definition._count = 1 + definition._data_type = 'INT32' + integrate_data(definition, field, data, 32) + assert field.raw == 0x00000003 + pair.integrate_data(data, 32, 5) + assert field.raw == 0x00000006 + integrate_data(definition, field, data, 32, 9) assert field.raw is None + pair.integrate_data(data, 32, 1) + assert field.raw == 0x00007FFF # set value definition._count = 1 - integrate_data(definition, field, data) + definition._data_type = 'INT16' + integrate_data(definition, field, data, 16) assert field.raw == 0x0003 - pair.integrate_data(data, 5) + pair.integrate_data(data, 16, 5) assert field.raw == 0x0006 - integrate_data(definition, field, data, 9) + integrate_data(definition, field, data, 16, 9) assert field.raw is None - pair.integrate_data(data, 1) + pair.integrate_data(data, 16, 1) assert field.raw is None + field.concatenate_multiple_data_chunks = False + class TestLuxtronikFieldsDictionary: diff --git a/tests/test_definition_list.py b/tests/test_definition_list.py index 8dae03d..a84c7f0 100644 --- a/tests/test_definition_list.py +++ b/tests/test_definition_list.py @@ -2,6 +2,7 @@ from luxtronik.common import parse_version from luxtronik.datatypes import Base +from luxtronik.definitions import LuxtronikDefinition from luxtronik.definitions.calculations import CALCULATIONS_DEFINITIONS_LIST from luxtronik.definitions.holdings import HOLDINGS_DEFINITIONS_LIST from luxtronik.definitions.inputs import INPUTS_DEFINITIONS_LIST @@ -24,8 +25,6 @@ KEY_UNTIL = "until" KEY_DESC = "description" -VALID_DATA_TYPES = ("", "UINT16", "UINT32", "INT16", "INT32") - class RunTestDefinitionList: @@ -186,7 +185,7 @@ def test_data_type(self): for definition in self.definitions: if KEY_DATATYPE in definition: data_type = definition[KEY_DATATYPE] - assert data_type in VALID_DATA_TYPES, \ + assert data_type in LuxtronikDefinition.VALID_DATA_TYPES, \ f"Datatype must be set correctly: {definition}" def test_since(self): diff --git a/tests/test_definitions.py b/tests/test_definitions.py index 6d6416a..bcf3d79 100644 --- a/tests/test_definitions.py +++ b/tests/test_definitions.py @@ -17,6 +17,7 @@ class TestDefinition: 'type': Base, 'writeable': True, 'names': ['test1', 'test2'], + 'datatype': 'INT16', 'since': '1.1', 'until': '3.16.7', 'description': 'foo', @@ -40,6 +41,7 @@ def test_init(self): assert definition.type_name == 'bar' assert definition.offset == 20 assert definition.addr == 20 + self.TEST_DATA['index'] + assert definition.num_bits == 16 assert definition.aliases == [] assert definition.since == (1, 1, 0, 0) assert definition.until == (3, 16, 7, 0) @@ -336,7 +338,7 @@ class TestDefinitionsList: ] def test_init(self): - definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100) + definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100, '') # only valid assert len(definitions) == 4 @@ -354,7 +356,7 @@ def test_init(self): assert definitions.get(9).name == "field_9" def test_filtered(self): - definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100) + definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100, '') filtered1 = LuxtronikDefinitionsList.filtered(definitions, (1, 1, 0, 0)) assert filtered1.name == 'foo' @@ -375,7 +377,7 @@ def test_filtered(self): assert 'field_invalid' not in filtered1 # invalid def test_iter(self): - definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100) + definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100, '') for index, d in enumerate(definitions): if index == 0: @@ -388,7 +390,7 @@ def test_iter(self): assert d.index == 9 def test_create(self): - definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100) + definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100, '') # create unknown definition definition = definitions.create_unknown_definition(4) @@ -406,7 +408,7 @@ def test_create(self): assert definition.until is None def test_alias(self): - definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100) + definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100, '') # add alias d = definitions.register_alias(5, 'bar') @@ -421,7 +423,7 @@ def test_alias(self): assert d is None def test_add(self): - definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100) + definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100, '') assert len(definitions) == 4 # add custom definition @@ -461,6 +463,6 @@ def test_add(self): assert added_4 is None def test_repr(self): - definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100) + definitions = LuxtronikDefinitionsList(self.def_list, 'foo', 100, '') text = repr(definitions) assert text \ No newline at end of file