From ab304490be8ae33af0344ab5d7f85c8c84e5c258 Mon Sep 17 00:00:00 2001 From: nobbi1991 <48419518+nobbi1991@users.noreply.github.com> Date: Fri, 29 Nov 2024 23:05:32 +0100 Subject: [PATCH 1/4] first thoughts --- habapp_rules/media/__init__.py | 0 habapp_rules/media/config/__init__.py | 0 habapp_rules/media/todos.md | 23 +++++++++++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 habapp_rules/media/__init__.py create mode 100644 habapp_rules/media/config/__init__.py create mode 100644 habapp_rules/media/todos.md diff --git a/habapp_rules/media/__init__.py b/habapp_rules/media/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/habapp_rules/media/config/__init__.py b/habapp_rules/media/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/habapp_rules/media/todos.md b/habapp_rules/media/todos.md new file mode 100644 index 0000000..253d351 --- /dev/null +++ b/habapp_rules/media/todos.md @@ -0,0 +1,23 @@ +# Todos + +## states + +- power_off +- starting +- standby +- playing + - Radio + - TV + - Music + - .... + - Sleep-music? +- stopping + +## things + +mainly sonos, but also add possibility for logitech harmony + +## features + +- startup volume +- couple with other room(s) \ No newline at end of file From e92e89fc6daaed8e15f17ec2781b84259fe6973f Mon Sep 17 00:00:00 2001 From: nobbi1991 <48419518+nobbi1991@users.noreply.github.com> Date: Fri, 29 Nov 2024 23:06:30 +0100 Subject: [PATCH 2/4] fmt --- habapp_rules/media/todos.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/habapp_rules/media/todos.md b/habapp_rules/media/todos.md index 253d351..16c5435 100644 --- a/habapp_rules/media/todos.md +++ b/habapp_rules/media/todos.md @@ -6,11 +6,11 @@ - starting - standby - playing - - Radio - - TV - - Music - - .... - - Sleep-music? + - Radio + - TV + - Music + - .... + - Sleep-music? - stopping ## things @@ -20,4 +20,4 @@ mainly sonos, but also add possibility for logitech harmony ## features - startup volume -- couple with other room(s) \ No newline at end of file +- couple with other room(s) From 96fcde3571c917c9c49d42c59472abf0bdc446bc Mon Sep 17 00:00:00 2001 From: nobbi1991 <48419518+nobbi1991@users.noreply.github.com> Date: Sat, 30 Nov 2024 16:46:28 +0100 Subject: [PATCH 3/4] added rules to avoid sun protection if heating period is active --- changelog.md | 2 + habapp_rules/actors/config/heating.py | 23 +++++ habapp_rules/actors/heating.py | 118 +++++++++++++++++++++----- habapp_rules/sensors/config/sun.py | 16 ++++ habapp_rules/sensors/sun.py | 75 ++++++++++++++++ tests/actors/heating.py | 79 +++++++++++++++++ tests/sensors/sun.py | 62 ++++++++++++++ 7 files changed, 356 insertions(+), 19 deletions(-) diff --git a/changelog.md b/changelog.md index 4d80dbd..c49cf4f 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,8 @@ - added rule `habapp_rules.actors.light_bathroom.BathroomLight` to control bathroom light - added the python version to `habapp_rules.core.version.SetVersions` +- added `habapp_rules.sensors.sun.WinterFilter` to filter the sun signal depending on heating state. This can be used to avoid sun protection when heating is active +- added `habapp_rules.actors.heating.HeatingActive` which can be used to set a heating flag if one of the heating actors is active - improved `habapp_rules.core.timeout_list` # Bugfix diff --git a/habapp_rules/actors/config/heating.py b/habapp_rules/actors/config/heating.py index 561db2b..37eb314 100644 --- a/habapp_rules/actors/config/heating.py +++ b/habapp_rules/actors/config/heating.py @@ -1,5 +1,7 @@ """Config models for heating rules.""" +import datetime + import HABApp.openhab.items import pydantic @@ -19,3 +21,24 @@ class KnxHeatingConfig(habapp_rules.core.pydantic_base.ConfigBase): items: KnxHeatingItems = pydantic.Field(..., description="items for heating rule") parameter: None = None + + +class HeatingActiveItems(habapp_rules.core.pydantic_base.ItemBase): + """Items for active heating rule.""" + + control_values: list[HABApp.openhab.items.NumberItem] = pydantic.Field(..., description="list of control value items") + output: HABApp.openhab.items.SwitchItem = pydantic.Field(..., description="output item, which is ON when at least one control value is above the threshold") + + +class HeatingActiveParameter(habapp_rules.core.pydantic_base.ParameterBase): + """Parameters for active heating rule.""" + + threshold: float = pydantic.Field(0, description="control value threshold") + extended_active_time: datetime.timedelta = pydantic.Field(datetime.timedelta(days=1), description="extended time to keep the output item ON, after last control value change below the threshold") + + +class HeatingActiveConfig(habapp_rules.core.pydantic_base.ConfigBase): + """Config for active heating rule.""" + + items: HeatingActiveItems = pydantic.Field(..., description="items for active heating rule") + parameter: HeatingActiveParameter = pydantic.Field(HeatingActiveParameter(), description="parameters for active heating rule") diff --git a/habapp_rules/actors/heating.py b/habapp_rules/actors/heating.py index 45bf7d7..802164f 100644 --- a/habapp_rules/actors/heating.py +++ b/habapp_rules/actors/heating.py @@ -1,37 +1,42 @@ """Heating rules.""" +import logging + import HABApp import habapp_rules.actors.config.heating +from habapp_rules.core.helper import send_if_different + +LOGGER = logging.getLogger(__name__) class KnxHeating(HABApp.Rule): """Rule which can be used to control a heating actor which only supports temperature offsets (e.g. MDT). - This rule uses a virtual temperature OpenHAB item for the target temperature. If this changes, the new offset is calculated and sent to the actor. - If the actor feedback temperature changes (e.g. through mode change), the new target temperature is updated to the virtual temperature item. + This rule uses a virtual temperature OpenHAB item for the target temperature. If this changes, the new offset is calculated and sent to the actor. + If the actor feedback temperature changes (e.g. through mode change), the new target temperature is updated to the virtual temperature item. - # KNX-things: - Thing device heating_actor "KNX heating actor"{ + # KNX-things: + Thing device heating_actor "KNX heating actor"{ Type number : target_temperature "Target Temperature" [ ga="9.001:<3/6/11"] Type number : temperature_offset "Temperature Offset" [ ga="9.002:3/6/22" ] } - # Items: - Number:Temperature target_temperature_OH "Target Temperature" ["Setpoint", "Temperature"] {unit="°C", stateDescription=""[pattern="%.1f %unit%", min=5, max=27, step=0.5]} - Number:Temperature target_temperature_KNX "Target Temperature KNX" {channel="knx:device:bridge:heating_actor:target_temperature", unit="°C", stateDescription=""[pattern="%.1f %unit%"]} - Number temperature_offset "Temperature Offset" {channel="knx:device:bridge:heating_actor:temperature_offset", stateDescription=""[pattern="%.1f °C", min=-5, max=5, step=0.5]} - - # Config: - config = habapp_rules.actors.config.heating.KnxHeatingConfig( - items=habapp_rules.actors.config.heating.KnxHeatingItems( - virtual_temperature="target_temperature_OH", - actor_feedback_temperature="target_temperature_KNX", - temperature_offset="temperature_offset" - )) - - # Rule init: - habapp_rules.actors.heating.KnxHeating(config) + # Items: + Number:Temperature target_temperature_OH "Target Temperature" ["Setpoint", "Temperature"] {unit="°C", stateDescription=""[pattern="%.1f %unit%", min=5, max=27, step=0.5]} + Number:Temperature target_temperature_KNX "Target Temperature KNX" {channel="knx:device:bridge:heating_actor:target_temperature", unit="°C", stateDescription=""[pattern="%.1f %unit%"]} + Number temperature_offset "Temperature Offset" {channel="knx:device:bridge:heating_actor:temperature_offset", stateDescription=""[pattern="%.1f °C", min=-5, max=5, step=0.5]} + + # Config: + config = habapp_rules.actors.config.heating.KnxHeatingConfig( + items=habapp_rules.actors.config.heating.KnxHeatingItems( + virtual_temperature="target_temperature_OH", + actor_feedback_temperature="target_temperature_KNX", + temperature_offset="temperature_offset" + )) + + # Rule init: + habapp_rules.actors.heating.KnxHeating(config) """ def __init__(self, config: habapp_rules.actors.config.heating.KnxHeatingConfig) -> None: @@ -77,3 +82,78 @@ def _cb_virtual_temperature_command(self, event: HABApp.openhab.events.ItemComma offset_new = event.value - self._temperature + self._config.items.temperature_offset.value self._config.items.temperature_offset.oh_send_command(offset_new) self._temperature = event.value + + +class HeatingActive(HABApp.Rule): + """Rule sets a switch item to ON if any of the heating control values are above 0. + + # Items: + Number control_value_1 "Control Value 1" + Number control_value_2 "Control Value 2" + Switch heating_active "Heating Active" + + # Config: + config = habapp_rules.actors.config.heating.HeatingActiveConfig( + items=habapp_rules.actors.config.heating.HeatingActiveItems( + control_values=["control_value_1", "control_value_2"] + output="heating_active" + )) + + # Rule init: + habapp_rules.actors.heating.HeatingActive(config) + """ + + def __init__(self, config: habapp_rules.actors.config.heating.HeatingActiveConfig) -> None: + """Initialize the HeatingActive rule. + + Args: + config: Config of the HeatingActive rule + """ + HABApp.Rule.__init__(self) + self._config = config + + self._extended_lock = self.run.countdown(self._config.parameter.extended_active_time, self._cb_lock_end) + + # callbacks + self._config.items.output.listen_event(self._cb_output, HABApp.openhab.events.ItemStateChangedEventFilter()) + for itm in self._config.items.control_values: + itm.listen_event(self._cb_control_value, HABApp.openhab.events.ItemStateChangedEventFilter()) + + # reset extended lock if output is on + if self._config.items.output.is_on(): + # start countdown if the output is already on + self._extended_lock.reset() + + # set initial value + elif self._config.items.output.value is None: + ctr_values = [itm.value for itm in self._config.items.control_values if itm.value is not None] + target_state = any(value > self._config.parameter.threshold for value in ctr_values) if ctr_values else False + send_if_different(self._config.items.output, "ON" if target_state else "OFF") + + @staticmethod + def _cb_output(event: HABApp.openhab.events.ItemStateChangedEvent) -> None: + """Callback which is triggered if the output changed. + + Args: + event: original trigger event + """ + LOGGER.debug(f"Heating active output changed to {event.value}") + + def _cb_control_value(self, event: HABApp.openhab.events.ItemStateChangedEvent) -> None: + """Callback which is triggered if any of the control values changed. + + If the received value of the event or any of the other control values is above 0, it sets the output item to ON. + Otherwise, it sets the output item to OFF. + """ + if event.value > self._config.parameter.threshold or any(itm.value > self._config.parameter.threshold for itm in self._config.items.control_values if itm.value is not None): + send_if_different(self._config.items.output, "ON") + self._extended_lock.reset() + elif not self._extended_lock.next_run_datetime: + send_if_different(self._config.items.output, "OFF") + + def _cb_lock_end(self) -> None: + """Callback function that is triggered when the extended lock period ends. + + Sets the output item to OFF if the lock period has expired. + """ + send_if_different(self._config.items.output, "OFF") diff --git a/habapp_rules/sensors/config/sun.py b/habapp_rules/sensors/config/sun.py index df3095a..0f49ed3 100644 --- a/habapp_rules/sensors/config/sun.py +++ b/habapp_rules/sensors/config/sun.py @@ -178,3 +178,19 @@ class SunPositionConfig(habapp_rules.core.pydantic_base.ConfigBase): items: SunPositionItems = pydantic.Field(..., description="items for sun position filter") parameter: SunPositionParameter = pydantic.Field(..., description="parameter for sun position filter") + + +class WinterFilterItems(habapp_rules.core.pydantic_base.ItemBase): + """Items for WinterFilter.""" + + sun: HABApp.openhab.items.SwitchItem = pydantic.Field(..., description="sun is shining") + output: HABApp.openhab.items.SwitchItem = pydantic.Field(..., description="output item") + heating_active: HABApp.openhab.items.SwitchItem = pydantic.Field(..., description="heating is active") + presence_state: HABApp.openhab.items.StringItem | None = pydantic.Field(None, description="presence state") + + +class WinterFilterConfig(habapp_rules.core.pydantic_base.ConfigBase): + """Config model for WinterFilter.""" + + items: WinterFilterItems = pydantic.Field(..., description="items for sun position filter") + parameter: None = pydantic.Field(None, description="parameter for sun position filter") diff --git a/habapp_rules/sensors/sun.py b/habapp_rules/sensors/sun.py index 8dd80b0..856dda1 100644 --- a/habapp_rules/sensors/sun.py +++ b/habapp_rules/sensors/sun.py @@ -11,6 +11,8 @@ import habapp_rules.core.logger import habapp_rules.core.state_machine_rule import habapp_rules.sensors.config.sun +from habapp_rules.core.helper import send_if_different +from habapp_rules.system import PresenceState LOGGER = logging.getLogger(__name__) @@ -238,3 +240,76 @@ def _update_output(self, _: HABApp.openhab.events.ItemStateChangedEvent | None) if filter_output != self._config.items.output.value: self._config.items.output.oh_send_command(filter_output) + + +class WinterFilter(HABApp.Rule): + """Rule to filter the sun sensor depending on heating and presence state. + + # Items: + Switch sun "Sun is shining" + Switch heating_active "Heating is active" + Switch sun_filtered "Sun filtered" + + # Config: + config = habapp_rules.sensors.config.sun.WinterFilterConfig( + items=habapp_rules.sensors.config.sun.WinterFilterItems( + sun="sun", + heating_active="heating_active", + output="sun_filtered", + ) + ) + + # Rule init: + habapp_rules.sensors.sun.WinterFilter(config) + """ + + def __init__(self, config: habapp_rules.sensors.config.sun.WinterFilterConfig) -> None: + """Init of sun position filter. + + Args: + config: config for the sun position filter + """ + HABApp.Rule.__init__(self) + self._config = config + + # callbacks + config.items.sun.listen_event(self._cb_sun, HABApp.openhab.events.ItemStateChangedEventFilter()) + if config.items.heating_active is not None: + config.items.heating_active.listen_event(self._cb_heating, HABApp.openhab.events.ItemStateChangedEventFilter()) + if config.items.presence_state is not None: + config.items.presence_state.listen_event(self.cb_presence_state, HABApp.openhab.events.ItemStateChangedEventFilter()) + + def _check_conditions_and_set_output(self) -> None: + """Check conditions and set output. + + The output will be on, if the sun is up, the heating is off and somebody is at home. + """ + heating_on = self._config.items.heating_active.is_on() + absence = self._config.items.presence_state.value != PresenceState.PRESENCE.value if self._config.items.presence_state is not None else True + + target_state = self._config.items.sun.is_on() and (not heating_on or not absence) + send_if_different(self._config.items.output, "ON" if target_state else "OFF") + + def _cb_sun(self, event: HABApp.openhab.events.ItemStateChangedEvent) -> None: # noqa: ARG002 + """Callback which is triggered if sun state changed. + + Args: + event: original trigger event + """ + self._check_conditions_and_set_output() + + def _cb_heating(self, event: HABApp.openhab.events.ItemStateChangedEvent) -> None: # noqa: ARG002 + """Callback which is triggered if heating state changed. + + Args: + event: original trigger event + """ + self._check_conditions_and_set_output() + + def cb_presence_state(self, event: HABApp.openhab.events.ItemStateChangedEvent) -> None: # noqa: ARG002 + """Callback which is triggered if presence_state changed. + + Args: + event: original trigger event + """ + self._check_conditions_and_set_output() diff --git a/tests/actors/heating.py b/tests/actors/heating.py index d37d7c3..d8fb555 100644 --- a/tests/actors/heating.py +++ b/tests/actors/heating.py @@ -1,8 +1,11 @@ """Test heating rules.""" import collections +import datetime +import unittest.mock import HABApp +import HABApp.rule.scheduler.job_ctrl import habapp_rules.actors.config.heating import habapp_rules.actors.heating @@ -78,3 +81,79 @@ def test_virtual_temperature_command(self) -> None: self.assertEqual(test_case.expected_new_offset, round(self._rule._config.items.temperature_offset.value, 1)) self.assertEqual(test_case.event_value, self._rule._temperature) + + +class TestHeatingActive(tests.helper.test_case_base.TestCaseBase): + """Test HeatingActive.""" + + def setUp(self) -> None: + """Setup test case.""" + tests.helper.test_case_base.TestCaseBase.setUp(self) + tests.helper.oh_item.add_mock_item(HABApp.openhab.items.NumberItem, "Unittest_ctr_value_1", None) + tests.helper.oh_item.add_mock_item(HABApp.openhab.items.NumberItem, "Unittest_ctr_value_2", None) + tests.helper.oh_item.add_mock_item(HABApp.openhab.items.SwitchItem, "Unittest_heating_active_1", None) + tests.helper.oh_item.add_mock_item(HABApp.openhab.items.SwitchItem, "Unittest_heating_active_2", None) + + self._config_1 = habapp_rules.actors.config.heating.HeatingActiveConfig(items=habapp_rules.actors.config.heating.HeatingActiveItems(control_values=["Unittest_ctr_value_1", "Unittest_ctr_value_2"], output="Unittest_heating_active_1")) + self._config_2 = habapp_rules.actors.config.heating.HeatingActiveConfig( + items=habapp_rules.actors.config.heating.HeatingActiveItems(control_values=["Unittest_ctr_value_1", "Unittest_ctr_value_2"], output="Unittest_heating_active_2"), + parameter=habapp_rules.actors.config.heating.HeatingActiveParameter(extended_active_time=datetime.timedelta(seconds=10), threshold=20), + ) + + self._rule_1 = habapp_rules.actors.heating.HeatingActive(self._config_1) + self._rule_2 = habapp_rules.actors.heating.HeatingActive(self._config_2) + + def test_init(self) -> None: + """Test __init__.""" + self.assertTrue(isinstance(self._rule_1._extended_lock, HABApp.rule.scheduler.job_ctrl.CountdownJobControl)) + self.assertIsNone(self._rule_1._extended_lock.next_run_datetime) + tests.helper.oh_item.assert_value("Unittest_heating_active_1", "OFF") + tests.helper.oh_item.assert_value("Unittest_heating_active_2", "OFF") + self.assertIn("_cb_lock_end", self._rule_1._extended_lock._job.executor._func.name) + + def test_init_when_output_is_on(self) -> None: + """Test __init__ when output is on.""" + TestCase = collections.namedtuple("TestCase", "state, reset_called") + + test_cases = [TestCase("ON", True), TestCase("OFF", False), TestCase(None, False)] + + for test_case in test_cases: + with self.subTest(test_case=test_case): + tests.helper.oh_item.set_state("Unittest_heating_active_1", test_case.state) + with unittest.mock.patch("HABApp.rule.scheduler.job_builder.HABAppJobBuilder.countdown") as run_countdown_mock: + habapp_rules.actors.heating.HeatingActive(self._config_1) + if test_case.reset_called: + run_countdown_mock.return_value.reset.assert_called_once() + else: + run_countdown_mock.return_value.reset.assert_not_called() + + def test_set_output(self) -> None: + """Test _set_output.""" + tests.helper.oh_item.assert_value("Unittest_heating_active_1", "OFF") + + # ctr_value_1 changes to 0 + tests.helper.oh_item.item_state_change_event("Unittest_ctr_value_1", 0) + tests.helper.oh_item.assert_value("Unittest_heating_active_1", "OFF") + tests.helper.oh_item.assert_value("Unittest_heating_active_2", "OFF") + + # ctr_value_1 changes to 10 + tests.helper.oh_item.item_state_change_event("Unittest_ctr_value_1", 10) + tests.helper.oh_item.assert_value("Unittest_heating_active_1", "ON") + tests.helper.oh_item.assert_value("Unittest_heating_active_2", "OFF") + + # ctr_value_2 changes to 21 + tests.helper.oh_item.item_state_change_event("Unittest_ctr_value_2", 21) + tests.helper.oh_item.assert_value("Unittest_heating_active_1", "ON") + tests.helper.oh_item.assert_value("Unittest_heating_active_2", "ON") + + # both change to 0 + tests.helper.oh_item.item_state_change_event("Unittest_ctr_value_1", 0) + tests.helper.oh_item.item_state_change_event("Unittest_ctr_value_2", 0) + tests.helper.oh_item.assert_value("Unittest_heating_active_1", "ON") + tests.helper.oh_item.assert_value("Unittest_heating_active_2", "ON") + + # lock period ends + self._rule_1._cb_lock_end() + self._rule_2._cb_lock_end() + tests.helper.oh_item.assert_value("Unittest_heating_active_1", "OFF") + tests.helper.oh_item.assert_value("Unittest_heating_active_2", "OFF") diff --git a/tests/sensors/sun.py b/tests/sensors/sun.py index bf3fdb1..f828e84 100644 --- a/tests/sensors/sun.py +++ b/tests/sensors/sun.py @@ -10,6 +10,7 @@ import tests.helper.graph_machines import tests.helper.oh_item import tests.helper.test_case_base +from habapp_rules.system import PresenceState class TestSensorTemperatureDifference(tests.helper.test_case_base.TestCaseBase): @@ -259,3 +260,64 @@ def test_filter(self) -> None: else: log_1_mock.warning.assert_not_called() log_2_mock.warning.assert_not_called() + + +class TestWinterFilter(tests.helper.test_case_base.TestCaseBase): + """Tests cases WinterFilter rule.""" + + def setUp(self) -> None: + """Setup test case.""" + tests.helper.test_case_base.TestCaseBase.setUp(self) + + tests.helper.oh_item.add_mock_item(HABApp.openhab.items.SwitchItem, "Unittest_Sun", None) + tests.helper.oh_item.add_mock_item(HABApp.openhab.items.SwitchItem, "Unittest_Winter", None) + tests.helper.oh_item.add_mock_item(HABApp.openhab.items.StringItem, "Unittest_Presence_state", None) + + tests.helper.oh_item.add_mock_item(HABApp.openhab.items.SwitchItem, "Unittest_Output_1", None) + tests.helper.oh_item.add_mock_item(HABApp.openhab.items.SwitchItem, "Unittest_Output_2", None) + + config_full = habapp_rules.sensors.config.sun.WinterFilterConfig( + items=habapp_rules.sensors.config.sun.WinterFilterItems( + sun="Unittest_Sun", + heating_active="Unittest_Winter", + presence_state="Unittest_Presence_state", + output="Unittest_Output_1", + ) + ) + + config_only_heating = habapp_rules.sensors.config.sun.WinterFilterConfig( + items=habapp_rules.sensors.config.sun.WinterFilterItems( + sun="Unittest_Sun", + heating_active="Unittest_Winter", + output="Unittest_Output_2", + ) + ) + + self._rule_full = habapp_rules.sensors.sun.WinterFilter(config_full) + self._rule_winter = habapp_rules.sensors.sun.WinterFilter(config_only_heating) + + def test_filter(self) -> None: + """Test WinterFilter rule.""" + TestCase = collections.namedtuple("TestCase", "sun, heating_active, presence_state, out_full, out_winter") + + test_cases = [ + # sun off + TestCase(sun="OFF", heating_active="OFF", presence_state=PresenceState.PRESENCE, out_full="OFF", out_winter="OFF"), + TestCase(sun="OFF", heating_active="OFF", presence_state=PresenceState.ABSENCE, out_full="OFF", out_winter="OFF"), + TestCase(sun="OFF", heating_active="ON", presence_state=PresenceState.PRESENCE, out_full="OFF", out_winter="OFF"), + TestCase(sun="OFF", heating_active="ON", presence_state=PresenceState.ABSENCE, out_full="OFF", out_winter="OFF"), + # sun on + TestCase(sun="ON", heating_active="OFF", presence_state=PresenceState.PRESENCE, out_full="ON", out_winter="ON"), + TestCase(sun="ON", heating_active="OFF", presence_state=PresenceState.ABSENCE, out_full="ON", out_winter="ON"), + TestCase(sun="ON", heating_active="ON", presence_state=PresenceState.PRESENCE, out_full="ON", out_winter="OFF"), + TestCase(sun="ON", heating_active="ON", presence_state=PresenceState.ABSENCE, out_full="OFF", out_winter="OFF"), + ] + + for test_case in test_cases: + with self.subTest(test_case=test_case): + tests.helper.oh_item.item_state_change_event("Unittest_Sun", test_case.sun) + tests.helper.oh_item.item_state_change_event("Unittest_Winter", test_case.heating_active) + tests.helper.oh_item.item_state_change_event("Unittest_Presence_state", test_case.presence_state.value) + + tests.helper.oh_item.assert_value("Unittest_Output_1", test_case.out_full) + tests.helper.oh_item.assert_value("Unittest_Output_2", test_case.out_winter) From 347d42711b593a4a5a6a7ef155b23db813689c39 Mon Sep 17 00:00:00 2001 From: nobbi1991 <48419518+nobbi1991@users.noreply.github.com> Date: Sat, 30 Nov 2024 21:54:46 +0100 Subject: [PATCH 4/4] removed media stuff --- habapp_rules/media/__init__.py | 0 habapp_rules/media/config/__init__.py | 0 habapp_rules/media/todos.md | 23 ----------------------- 3 files changed, 23 deletions(-) delete mode 100644 habapp_rules/media/__init__.py delete mode 100644 habapp_rules/media/config/__init__.py delete mode 100644 habapp_rules/media/todos.md diff --git a/habapp_rules/media/__init__.py b/habapp_rules/media/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/habapp_rules/media/config/__init__.py b/habapp_rules/media/config/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/habapp_rules/media/todos.md b/habapp_rules/media/todos.md deleted file mode 100644 index 16c5435..0000000 --- a/habapp_rules/media/todos.md +++ /dev/null @@ -1,23 +0,0 @@ -# Todos - -## states - -- power_off -- starting -- standby -- playing - - Radio - - TV - - Music - - .... - - Sleep-music? -- stopping - -## things - -mainly sonos, but also add possibility for logitech harmony - -## features - -- startup volume -- couple with other room(s)