Skip to content

Commit

Permalink
A sml read is considered as valid when at least one OBIS code delivers
Browse files Browse the repository at this point in the history
valid values.
  • Loading branch information
die-bauerei committed Mar 27, 2024
1 parent be1d005 commit cf2da84
Show file tree
Hide file tree
Showing 7 changed files with 24 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ sudo systemctl status smart_meter_to_openhab.service
The USB infrared adapter sometimes seems to lose its connection. It helps to just unplug and again plug the adapter. The same can be achieved if you Power off and on usb ports. This can be configured by the argument *uhubctl*. The current implementation powers off and on all usb ports on a raspberry pi (change it to your needs). The package uhubctl (https://github.com/mvp/uhubctl) can be installed via *sudo apt install uhubctl* on a debian based system.

## Development ##
Development is done in wsl2 on ubuntu 20.4.
Development is done in wsl2 on ubuntu 22.04.
Setting up the development environment on Windows is not supported. But in principal it could be setup as well since no OS specific functionalities are used.

### Setup ###
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "smart_meter_to_openhab"
version = "0.3.1"
version = "0.3.2"
description = "Pushing data of ISKRA MT175 smart meter to openhab"
authors = ["Heiko Bauer <heiko_bauer@icloud.com>"]
repository = "https://github.com/die-bauerei/smart-meter-to-openhab"
Expand Down
2 changes: 1 addition & 1 deletion smart_meter_to_openhab/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def __iter__(self) -> Iterator[OhItemAndValue]:

def is_invalid(self) -> bool:
# consider only the values that really will be used (oh_item name not empty)
return any(oh_item_value.value is None for oh_item_value in self._oh_items_and_values if oh_item_value.oh_item)
return all(oh_item_value.value is None for oh_item_value in self._oh_items_and_values if oh_item_value.oh_item)

def value_list(self) -> List[Any]:
# consider only the values that really will be used (oh_item name not empty)
Expand Down
8 changes: 3 additions & 5 deletions smart_meter_to_openhab/openhab.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,9 @@ def _get_persistence_values(self, oh_item_names : Tuple[str, ...], timedelta : d
def check_if_updated(self, oh_item_names : Tuple[str, ...], timedelta : datetime.timedelta) -> bool:
pers_values=self._get_persistence_values(oh_item_names, timedelta)
for values in pers_values:
if not values:
return False
elif values[0] > 5.0 and all(i == values[0] for i in values):
return False
return True
if any(i != values[0] for i in values):
return True
return False

def get_median_from_items(self, oh_item_names : SmartMeterOhItemNames, timedelta : datetime.timedelta = datetime.timedelta(minutes=30)) -> SmartMeterValues:
smart_meter_values : List[OhItemAndValue] = []
Expand Down
5 changes: 4 additions & 1 deletion smart_meter_to_openhab/sml_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
MIN_REF_VALUE_IN_WATT=50
def _has_outlier(value_list : List[Any], ref_value_list : List[Any]) -> bool:
for i in range(len(value_list)):
if ref_value_list[i] is not None and value_list[i]*0.001 > max(ref_value_list[i], MIN_REF_VALUE_IN_WATT):
if value_list[i] is not None and ref_value_list[i] is not None and value_list[i]*0.001 > max(ref_value_list[i], MIN_REF_VALUE_IN_WATT):
return True
return False

Expand Down Expand Up @@ -110,5 +110,8 @@ def read_avg_from_sml_and_compute_extended_values(self, read_func : SmlReadFunct
avg_values=self.read_avg_from_sml(read_func, read_count, ref_values)
extended_values=ExtendedSmartMeterValues()
if avg_values.overall_consumption.value is not None:
# TODO: in case of an error (service restart, ...) the computed time delta would not be realistic.
# A possibility would be to take the timestamp of the last value from openHAB.
# Even better would be to resolve errors (checkout libsml)
extended_values.overall_consumption_wh.value=compute_watt_h(avg_values.overall_consumption.value, datetime.now() - time_start)
return (avg_values, extended_values)
10 changes: 10 additions & 0 deletions tests/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,20 @@ def test_creation_average(self) -> None:
new_values=SmartMeterValues.create_avg([values_1, values_2])
self.assertEqual(SmartMeterValues(150, 250, 350, 650, 3.0), new_values)

values_incl_none_1=SmartMeterValues(200, 300, None, 300, None)
new_values=SmartMeterValues.create_avg([values_1, values_incl_none_1])
self.assertEqual(SmartMeterValues(150, 250, 300, 450, 2.5), new_values)

values_incl_none_2=SmartMeterValues(100, None, 100, 100, None)
new_values=SmartMeterValues.create_avg([values_incl_none_1, values_incl_none_2])
self.assertEqual(SmartMeterValues(150, 300, 100, 200, None), new_values)

def test_is_invalid(self) -> None:
values=SmartMeterValues(100, 200, 300, 600, 2.5)
self.assertFalse(values.is_invalid())
values.phase_1_consumption.value=None
self.assertFalse(values.is_invalid())
values.reset()
self.assertTrue(values.is_invalid())

# NOTE: Unspecified values should NOT be tested against None.
Expand Down
6 changes: 4 additions & 2 deletions tests/test_sml.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ def test_read(self) -> None:
reader=SmlReader(logger)
read_values=reader.read_from_sml(read_func=TestSml._return_test_values, max_read_count=1)
self.assertEqual(TestSml._test_values[0], read_values)
# If there is at least one invalid (None) value, all returned values should be None

# check for single invalid (None) value. The other values should stay valid
TestSml._function_call_count=0
TestSml._test_values[0].phase_1_consumption.value=None
read_values=reader.read_from_sml(read_func=TestSml._return_test_values, max_read_count=1)
self.assertTrue(all(value is None for value in read_values.value_list()))
self.assertIsNone(read_values.phase_1_consumption.value)
self.assertEqual(TestSml._test_values[0], read_values)

def test_read_with_ref_values(self) -> None:
TestSml._function_call_count=0
Expand Down

0 comments on commit cf2da84

Please sign in to comment.