Skip to content

Commit

Permalink
Throw a KeyError exception if obsolete names are used
Browse files Browse the repository at this point in the history
  • Loading branch information
Guzz-T committed Nov 20, 2024
1 parent 7a0eda7 commit de8e5b1
Show file tree
Hide file tree
Showing 8 changed files with 1,823 additions and 1,697 deletions.
476 changes: 240 additions & 236 deletions luxtronik/calculations.py

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions luxtronik/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@

# Content of response that is contained in responses to discovery broadcast
LUXTRONIK_DISCOVERY_RESPONSE_PREFIX = "2500;111;"

LUXTRONIK_NAME_CHECK_NONE = "none"
LUXTRONIK_NAME_CHECK_PREFERRED = "preferred"
LUXTRONIK_NAME_CHECK_OBSOLETE = "obsolete"
15 changes: 14 additions & 1 deletion luxtronik/data_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

import logging

from luxtronik.constants import (
LUXTRONIK_NAME_CHECK_PREFERRED,
LUXTRONIK_NAME_CHECK_OBSOLETE,
)

from luxtronik.datatypes import Unknown


Expand All @@ -11,6 +16,8 @@ class DataVector:
logger = logging.getLogger("Luxtronik.DataVector")
name = "DataVector"

_obsolete = {}

def __init__(self):
"""Initialize DataVector class."""
self._data = {}
Expand Down Expand Up @@ -46,9 +53,15 @@ def _lookup(self, target, with_index=False):
except ValueError:
# Get entry by name
target_index = None
obsolete_entry = self._obsolete.get(target, None)
if obsolete_entry:
raise KeyError(f"The name '{target}' is obsolete! Use '{obsolete_entry}' instead.")
for index, entry in self._data.items():
if entry.name == target:
check_result = entry.check_name(target)
if check_result == LUXTRONIK_NAME_CHECK_PREFERRED:
target_index = index
elif check_result == LUXTRONIK_NAME_CHECK_OBSOLETE:
raise KeyError(f"The name '{target}' is obsolete! Use '{entry.name}' instead.")
elif isinstance(target, int):
# Get entry by id
target_index = target
Expand Down
32 changes: 30 additions & 2 deletions luxtronik/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
import socket
import struct

from luxtronik.constants import (
LUXTRONIK_NAME_CHECK_NONE,
LUXTRONIK_NAME_CHECK_PREFERRED,
LUXTRONIK_NAME_CHECK_OBSOLETE,
)

from functools import total_ordering


Expand All @@ -14,12 +20,17 @@ class Base:
datatype_class = None
datatype_unit = None

def __init__(self, name, writeable=False):
def __init__(self, names, writeable=False):
"""Initialize the base data field class. Set the initial raw value to None"""
# save the raw value only since the user value
# could be build at any time
self._raw = None
self.name = name
if isinstance(names, list):
self._names = names
else:
self._names = [names]
assert len(self._names) > 0, "At least one name is required"
assert all(isinstance(name, str) for name in self._names), "Names must be strings"
self.writeable = writeable

@classmethod
Expand All @@ -32,6 +43,23 @@ def from_heatpump(cls, value):
"""Converts value from heatpump units."""
return value

@property
def name(self):
"""Return the (most common) name of the entry."""
return self._names[0]

def check_name(self, name):
"""
Check whether a name matches one of the supported entry names.
The result string can be used to trigger a exception for obsolete names.
"""
if name == self.name:
return LUXTRONIK_NAME_CHECK_PREFERRED
elif name in self._names:
return LUXTRONIK_NAME_CHECK_OBSOLETE
else:
return LUXTRONIK_NAME_CHECK_NONE

@property
def value(self):
"""Return the stored value converted from heatpump units."""
Expand Down
2,216 changes: 1,108 additions & 1,108 deletions luxtronik/parameters.py

Large diffs are not rendered by default.

700 changes: 350 additions & 350 deletions luxtronik/visibilities.py

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions tests/test_data_vector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Test suite for data_vector module"""

# pylint: disable=too-few-public-methods,invalid-name,too-many-lines

import pytest
from luxtronik.data_vector import DataVector

from luxtronik.datatypes import (
Base,
)


class ObsoleteDataVector(DataVector):

name = "Obsolete"

_obsolete = {
"baz": "foo"
}

def __init__(self):
super().__init__()
self._data = {
0: Base(["foo", "bar"]),
}


class TestDataVector:
"""Test suite for DataVector"""

@pytest.mark.parametrize("name, exception_expected", [
("foo", False),
("bar", True),
("baz", True),
("qux", False),
])
def test_obsolete(self, name, exception_expected):
"""Test cases for initialization"""
obsolete = ObsoleteDataVector()
try:
obsolete.get(name)
assert not exception_expected
except KeyError:
assert exception_expected
33 changes: 33 additions & 0 deletions tests/test_datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

import datetime

from luxtronik.constants import (
LUXTRONIK_NAME_CHECK_NONE,
LUXTRONIK_NAME_CHECK_PREFERRED,
LUXTRONIK_NAME_CHECK_OBSOLETE,
)

from luxtronik.datatypes import (
Base,
SelectionBase,
Expand Down Expand Up @@ -93,6 +99,33 @@ def test_to_heatpump(self):

assert Base.to_heatpump(42) == 42

def test_name(self):
"""Test cases for name property"""
base = Base(["foo", "bar"])

assert base.name == "foo"

def test_empty_name(self):
"""Test cases for name property"""
try:
Base([])
assert False
except Exception:
pass
try:
Base(None)
assert False
except Exception:
pass

def test_check_name(self):
"""Test cases for check_name() function"""
base = Base(["foo", "bar"])

assert base.check_name("foo") == LUXTRONIK_NAME_CHECK_PREFERRED
assert base.check_name("bar") == LUXTRONIK_NAME_CHECK_OBSOLETE
assert base.check_name("baz") == LUXTRONIK_NAME_CHECK_NONE

def test_value_property(self):
"""Test case for value property"""

Expand Down

0 comments on commit de8e5b1

Please sign in to comment.