diff --git a/CHANGELOG.md b/CHANGELOG.md index 43cf9e472..47d6847b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ # Changelog -## Ongoing +## v1.10.0 -- Improve testing: compare internal and code testcounters, line up fixture and test-json format, fix a missed count. +- New feature: implement setting Adam zone profile via PR [#814](https://github.com/plugwise/python-plugwise/pull/814) +- Improve testing: compare internal and code testcounters, line up fixture and test-json format, fix a missed count via PR [#815](https://github.com/plugwise/python-plugwise/pull/815) ## v1.9.0 diff --git a/fixtures/adam_heatpump_cooling/data.json b/fixtures/adam_heatpump_cooling/data.json index 04b033f2a..71bfad7e2 100644 --- a/fixtures/adam_heatpump_cooling/data.json +++ b/fixtures/adam_heatpump_cooling/data.json @@ -14,6 +14,7 @@ "name": "Slaapkamer SJ", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -29,7 +30,8 @@ "primary": ["d3a276aeb3114a509bab1e4bf8c40348"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "0ca13e8176204ca7bf6f09de59f81c83": { "available": true, @@ -132,6 +134,7 @@ "name": "Slaapkamer DB", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -147,7 +150,8 @@ "primary": ["47e2c550a33846b680725aa3fb229473"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "2e0fc4db2a6d4cbeb7cf786143543961": { "available": true, @@ -246,6 +250,7 @@ "name": "Badkamer 2", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "Werkdag schema", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "temperature": 21.9 @@ -260,7 +265,8 @@ "primary": ["f04c985c11ad4848b8fcd710343f9dcf"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "5ead63c65e5f44e7870ba2bd680ceb9e": { "available": true, @@ -388,6 +394,7 @@ "name": "Badkamer 1", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "Werkdag schema", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -403,7 +410,8 @@ "primary": ["eac5db95d97241f6b17790897847ccf5"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "93ac3f7bf25342f58cbb77c4a99ac0b3": { "active_preset": "away", @@ -420,6 +428,7 @@ "name": "Slaapkamer RB", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 3.13, "temperature": 20.7 @@ -434,7 +443,8 @@ "primary": ["c4ed311d54e341f58b4cdd201d1fde7e"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "96714ad90fc948bcbcb5021c4b9f5ae9": { "available": true, @@ -471,6 +481,7 @@ "name": "Slaapkamer SQ", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -486,7 +497,8 @@ "primary": ["beb32da072274e698146db8b022f3c36"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "a03b6e8e76dd4646af1a77c31dd9370c": { "available": true, @@ -523,6 +535,7 @@ "name": "Keuken", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "Werkdag schema", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 2.13, "electricity_produced": 0.0, @@ -538,7 +551,8 @@ "primary": ["ea8372c0e3ad4622ad45a041d02425f5"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "b52908550469425b812c87f766fe5303": { "active_preset": "away", @@ -555,6 +569,7 @@ "name": "Bijkeuken", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -570,7 +585,8 @@ "primary": ["1053c8bbf8be43c6921742b146a625f1"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "bbcffa48019f4b09b8368bbaf9559e68": { "available": true, @@ -685,6 +701,7 @@ "name": "Slaapkamer JM", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -700,7 +717,8 @@ "primary": ["7fda9f84f01342f8afe9ebbbbff30c0f"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "ea8372c0e3ad4622ad45a041d02425f5": { "available": true, @@ -787,6 +805,7 @@ "name": "Woonkamer", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "Werkdag schema", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -802,6 +821,7 @@ "primary": ["ca79d23ae0094120b877558734cff85c"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] } } diff --git a/fixtures/adam_jip/data.json b/fixtures/adam_jip/data.json index d8caff4b4..da7df0e27 100644 --- a/fixtures/adam_jip/data.json +++ b/fixtures/adam_jip/data.json @@ -9,6 +9,7 @@ "name": "Slaapkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 24.2 }, @@ -22,7 +23,8 @@ "primary": ["1346fbd8498d4dbcab7e18d51b771f3d"], "secondary": ["356b65335e274d769c338223e7af9c33"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "13228dab8ce04617af318a2888b3c548": { "active_preset": "home", @@ -34,6 +36,7 @@ "name": "Woonkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 27.4 }, @@ -47,7 +50,8 @@ "primary": ["f61f1a2535f54f52ad006a3d18e459ca"], "secondary": ["833de10f269c4deab58fb9df69901b4e"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "1346fbd8498d4dbcab7e18d51b771f3d": { "available": true, @@ -249,6 +253,7 @@ "name": "Kinderkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 30.0 }, @@ -262,7 +267,8 @@ "primary": ["6f3e9d7084214c21b9dfa46f6eeb8700"], "secondary": ["d4496250d0e942cfa7aea3476e9070d5"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "d4496250d0e942cfa7aea3476e9070d5": { "available": true, @@ -298,6 +304,7 @@ "name": "Logeerkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 30.0 }, @@ -311,7 +318,8 @@ "primary": ["a6abc6a129ee499c88a4d420cc413b47"], "secondary": ["1da4d325838e4ad8aac12177214505c9"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "e4684553153b44afbef2200885f379dc": { "available": true, diff --git a/fixtures/adam_onoff_cooling_fake_firmware/data.json b/fixtures/adam_onoff_cooling_fake_firmware/data.json index 4803aad72..f822e33d8 100644 --- a/fixtures/adam_onoff_cooling_fake_firmware/data.json +++ b/fixtures/adam_onoff_cooling_fake_firmware/data.json @@ -92,6 +92,7 @@ "name": "Woonkamer", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "Werkdag schema", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -107,6 +108,7 @@ "primary": ["ca79d23ae0094120b877558734cff85c"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] } } diff --git a/fixtures/adam_plus_anna_new/data.json b/fixtures/adam_plus_anna_new/data.json index 421d5387d..ee4ecd743 100644 --- a/fixtures/adam_plus_anna_new/data.json +++ b/fixtures/adam_plus_anna_new/data.json @@ -280,6 +280,7 @@ "name": "Living room", "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], "select_schedule": "Weekschema", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 60.8, "electricity_produced": 0.0, @@ -299,7 +300,8 @@ ], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "f871b8c4d63549319221e294e4f88074": { "active_preset": "vacation", @@ -317,6 +319,7 @@ "name": "Bathroom", "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -332,6 +335,7 @@ "primary": ["e2f4322d57924fa090fbbc48b3a140dc"], "secondary": ["1772a4ea304041adb83f357b751341ff"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] } } diff --git a/fixtures/adam_plus_anna_new_regulation_off/data.json b/fixtures/adam_plus_anna_new_regulation_off/data.json index 93710a1fa..8d07fdc92 100644 --- a/fixtures/adam_plus_anna_new_regulation_off/data.json +++ b/fixtures/adam_plus_anna_new_regulation_off/data.json @@ -233,6 +233,7 @@ "name": "Living room", "preset_modes": ["no_frost", "asleep", "vacation", "home", "away"], "select_schedule": "off", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 149.9, "electricity_produced": 0.0, @@ -248,7 +249,8 @@ "primary": ["ad4838d7d35c4d6ea796ee12ae5aedf8"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "f871b8c4d63549319221e294e4f88074": { "active_preset": "home", @@ -266,6 +268,7 @@ "name": "Bathroom", "preset_modes": ["no_frost", "asleep", "vacation", "home", "away"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -281,6 +284,7 @@ "primary": ["e2f4322d57924fa090fbbc48b3a140dc"], "secondary": ["1772a4ea304041adb83f357b751341ff"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] } } diff --git a/fixtures/m_adam_cooling/data.json b/fixtures/m_adam_cooling/data.json index 162bc1d2c..fdd52b1ef 100644 --- a/fixtures/m_adam_cooling/data.json +++ b/fixtures/m_adam_cooling/data.json @@ -196,6 +196,7 @@ "name": "Living room", "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], "select_schedule": "off", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 60.8, "electricity_produced": 0.0, @@ -215,7 +216,8 @@ ], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "f871b8c4d63549319221e294e4f88074": { "active_preset": "vacation", @@ -233,6 +235,7 @@ "name": "Bathroom", "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -248,6 +251,7 @@ "primary": ["e2f4322d57924fa090fbbc48b3a140dc"], "secondary": ["1772a4ea304041adb83f357b751341ff"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] } } diff --git a/fixtures/m_adam_heating/data.json b/fixtures/m_adam_heating/data.json index 87378f5b2..2f3951c0c 100644 --- a/fixtures/m_adam_heating/data.json +++ b/fixtures/m_adam_heating/data.json @@ -195,6 +195,7 @@ "name": "Living room", "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], "select_schedule": "off", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 60.8, "electricity_produced": 0.0, @@ -214,7 +215,8 @@ ], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "f871b8c4d63549319221e294e4f88074": { "active_preset": "vacation", @@ -232,6 +234,7 @@ "name": "Bathroom", "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -247,6 +250,7 @@ "primary": ["e2f4322d57924fa090fbbc48b3a140dc"], "secondary": ["1772a4ea304041adb83f357b751341ff"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] } } diff --git a/fixtures/m_adam_jip/data.json b/fixtures/m_adam_jip/data.json index 50b9a8109..c9ffb7395 100644 --- a/fixtures/m_adam_jip/data.json +++ b/fixtures/m_adam_jip/data.json @@ -8,6 +8,7 @@ "name": "Slaapkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 24.2 }, @@ -21,7 +22,8 @@ "primary": ["1346fbd8498d4dbcab7e18d51b771f3d"], "secondary": ["356b65335e274d769c338223e7af9c33"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "13228dab8ce04617af318a2888b3c548": { "active_preset": "home", @@ -33,6 +35,7 @@ "name": "Woonkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 27.4 }, @@ -46,7 +49,8 @@ "primary": ["f61f1a2535f54f52ad006a3d18e459ca"], "secondary": ["833de10f269c4deab58fb9df69901b4e"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "1346fbd8498d4dbcab7e18d51b771f3d": { "available": true, @@ -248,6 +252,7 @@ "name": "Kinderkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 30.0 }, @@ -261,7 +266,8 @@ "primary": ["6f3e9d7084214c21b9dfa46f6eeb8700"], "secondary": ["d4496250d0e942cfa7aea3476e9070d5"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "d4496250d0e942cfa7aea3476e9070d5": { "available": true, @@ -297,6 +303,7 @@ "name": "Logeerkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 30.0 }, @@ -310,7 +317,8 @@ "primary": ["a6abc6a129ee499c88a4d420cc413b47"], "secondary": ["1da4d325838e4ad8aac12177214505c9"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "e4684553153b44afbef2200885f379dc": { "available": true, diff --git a/plugwise/constants.py b/plugwise/constants.py index 82db53377..d68842d5f 100644 --- a/plugwise/constants.py +++ b/plugwise/constants.py @@ -33,6 +33,7 @@ VOLUME_CUBIC_METERS_PER_HOUR: Final = "m³/h" ADAM: Final = "Adam" +ALLOWED_ZONE_PROFILES: Final[list[str]] = ["active", "off", "passive"] ANNA: Final = "Smile Anna" ANNA_P1: Final = "Smile Anna P1" DEFAULT_TIMEOUT: Final = 10 @@ -250,8 +251,20 @@ ] ACTIVE_ACTUATORS: Final[tuple[str, ...]] = get_args(ActuatorType) +ACTIVE_KEYS: Final[tuple[str, ...]] = ( + "control_state", + "lower_bound", + "offset", + "regulation_control", + "resolution", + "setpoint", + "upper_bound", +) + ActuatorDataType = Literal[ + "control_state", "lower_bound", + "regulation_control", "resolution", "setpoint", "setpoint_high", @@ -286,14 +299,6 @@ ] BINARY_SENSORS: Final[tuple[str, ...]] = get_args(BinarySensorType) -LIMITS: Final[tuple[str, ...]] = ( - "lower_bound", - "offset", - "resolution", - "setpoint", - "upper_bound", -) - SensorType = Literal[ "battery", "cooling_activation_outdoor_temperature", @@ -502,7 +507,9 @@ class ThermoLoc(TypedDict, total=False): class ActuatorData(TypedDict, total=False): """Actuator data for thermostat types.""" + control_state: str lower_bound: float + regulation_control: str resolution: float setpoint: float setpoint_high: float @@ -551,7 +558,9 @@ class GwEntityData(TypedDict, total=False): select_regulation_mode: str # Thermostat-related + select_zone_profile: str thermostats: dict[str, list[str]] + zone_profiles: list[str] # Presets: active_preset: str | None preset_modes: list[str] | None diff --git a/plugwise/data.py b/plugwise/data.py index 70a4bd3c0..3ab9f53f4 100644 --- a/plugwise/data.py +++ b/plugwise/data.py @@ -161,9 +161,11 @@ def _get_location_data(self, loc_id: str) -> GwEntityData: """ zone = self._zones[loc_id] data = self._get_zone_data(loc_id) + self._regulation_control(data) + data["control_state"] = "idle" self._count += 1 - if (ctrl_state := self._control_state(data, loc_id)) and str(ctrl_state) in ( + if (ctrl_state := self._control_state(data)) and ctrl_state in ( "cooling", "heating", "preheating", diff --git a/plugwise/helper.py b/plugwise/helper.py index e4bfb1230..4f1de9258 100644 --- a/plugwise/helper.py +++ b/plugwise/helper.py @@ -10,8 +10,10 @@ from plugwise.common import SmileCommon from plugwise.constants import ( ACTIVE_ACTUATORS, + ACTIVE_KEYS, ACTUATOR_CLASSES, ADAM, + ALLOWED_ZONE_PROFILES, ANNA, ATTR_NAME, DATA, @@ -20,7 +22,6 @@ DOMAIN_OBJECTS, ENERGY_WATT_HOUR, HEATER_CENTRAL_MEASUREMENTS, - LIMITS, LOCATIONS, LOGGER, MODULE_LOCATOR, @@ -503,7 +504,7 @@ def _get_actuator_functionalities( ) is not None and updated_date_key.text is None: continue - for key in LIMITS: + for key in ACTIVE_KEYS: locator = ( f'.//actuator_functionalities/{functionality}[type="{item}"]/{key}' ) @@ -519,8 +520,13 @@ def _get_actuator_functionalities( key = "setpoint" act_key = cast(ActuatorDataType, key) - temp_dict[act_key] = format_measure(pw_function.text, TEMP_CELSIUS) self._count += 1 + try: + temp_dict[act_key] = format_measure( + pw_function.text, TEMP_CELSIUS + ) + except ValueError: + temp_dict[act_key] = str(pw_function.text) if temp_dict: # If domestic_hot_water_setpoint is present as actuator, @@ -790,16 +796,18 @@ def _rank_thermostat( else: thermo_loc["secondary"].append(appliance_id) - def _control_state(self, data: GwEntityData, loc_id: str) -> str | bool: - """Helper-function for _get_adam_data(). + def _control_state(self, data: GwEntityData) -> str | bool: + """Helper-function for _get_location_data(). - Adam: find the thermostat control_state of a location, from DOMAIN_OBJECTS. + Adam: collect the thermostat control_state of a location. Represents the heating/cooling demand-state of the local primary thermostat. Note: heating or cooling can still be active when the setpoint has been reached. """ - locator = f'location[@id="{loc_id}"]/actuator_functionalities/thermostat_functionality[type="thermostat"]/control_state' - if (ctrl_state := self._domain_objects.find(locator)) is not None: - return str(ctrl_state.text) + + if (ctrl_state := data["thermostat"].get("control_state")) is not None: + data["thermostat"].pop("control_state") + self._count -= 1 + return ctrl_state # Handle missing control_state in regulation_mode off for firmware >= 3.2.0 (issue #776) # In newer firmware versions, default to "off" when control_state is not present @@ -833,6 +841,17 @@ def _heating_valves(self) -> int | bool: return False if loc_found == 0 else open_valve_count + def _regulation_control(self, data: GwEntityData) -> None: + """Helper-function for smile.py: _get_location_data(). + + Adam: collect the thermostat regulation_control state of a location. + """ + if (reg_control := data["thermostat"].get("regulation_control")) is not None: + data["select_zone_profile"] = reg_control + data["zone_profiles"] = ALLOWED_ZONE_PROFILES + data["thermostat"].pop("regulation_control") + self._count += 1 # Add 2, remove 1 + def _preset(self, loc_id: str) -> str | None: """Helper-function for smile.py: device_data_climate(). diff --git a/plugwise/legacy/helper.py b/plugwise/legacy/helper.py index 87e3d951b..f095466d8 100644 --- a/plugwise/legacy/helper.py +++ b/plugwise/legacy/helper.py @@ -10,6 +10,7 @@ from plugwise.common import SmileCommon from plugwise.constants import ( ACTIVE_ACTUATORS, + ACTIVE_KEYS, ACTUATOR_CLASSES, APPLIANCES, ATTR_NAME, @@ -19,7 +20,6 @@ FAKE_APPL, FAKE_LOC, HEATER_CENTRAL_MEASUREMENTS, - LIMITS, NONE, OFF, P1_LEGACY_MEASUREMENTS, @@ -365,7 +365,7 @@ def _get_actuator_functionalities( ) is not None and updated_date_key.text is None: continue # pragma: no cover - for key in LIMITS: + for key in ACTIVE_KEYS: locator = ( f'.//actuator_functionalities/{functionality}[type="{item}"]/{key}' ) diff --git a/plugwise/smile.py b/plugwise/smile.py index 362a2316d..ad6a93544 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -11,6 +11,7 @@ from plugwise.constants import ( ADAM, + ALLOWED_ZONE_PROFILES, ANNA, APPLIANCES, DOMAIN_OBJECTS, @@ -240,6 +241,8 @@ async def set_select( case "select_schedule": # schedule name corresponds to select option await self.set_schedule_state(loc_id, state, option) + case "select_zone_profile": + await self.set_zone_profile(loc_id, option) async def set_dhw_mode(self, mode: str) -> None: """Set the domestic hot water heating regulation mode.""" @@ -304,6 +307,19 @@ async def set_regulation_mode(self, mode: str) -> None: uri = f"{APPLIANCES};type=gateway/regulation_mode_control" await self.call_request(uri, method="put", data=data) + async def set_zone_profile(self, loc_id: str, profile: str) -> None: + """Set the Adam thermoszone heating profile.""" + if profile not in ALLOWED_ZONE_PROFILES: + raise PlugwiseError("Plugwise: invalid zone profile.") + + data = ( + "" + f"{profile}" + "" + ) + uri = f"{LOCATIONS};id={loc_id}/thermostat" + await self.call_request(uri, method="post", data=data) + async def set_schedule_state( self, loc_id: str, diff --git a/pyproject.toml b/pyproject.toml index f7addd0d0..bb78312fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise" -version = "1.9.0" +version = "1.10.0" license = "MIT" description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3." readme = "README.md" diff --git a/tests/data/adam/adam_heatpump_cooling.json b/tests/data/adam/adam_heatpump_cooling.json index 04b033f2a..71bfad7e2 100644 --- a/tests/data/adam/adam_heatpump_cooling.json +++ b/tests/data/adam/adam_heatpump_cooling.json @@ -14,6 +14,7 @@ "name": "Slaapkamer SJ", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -29,7 +30,8 @@ "primary": ["d3a276aeb3114a509bab1e4bf8c40348"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "0ca13e8176204ca7bf6f09de59f81c83": { "available": true, @@ -132,6 +134,7 @@ "name": "Slaapkamer DB", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -147,7 +150,8 @@ "primary": ["47e2c550a33846b680725aa3fb229473"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "2e0fc4db2a6d4cbeb7cf786143543961": { "available": true, @@ -246,6 +250,7 @@ "name": "Badkamer 2", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "Werkdag schema", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "temperature": 21.9 @@ -260,7 +265,8 @@ "primary": ["f04c985c11ad4848b8fcd710343f9dcf"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "5ead63c65e5f44e7870ba2bd680ceb9e": { "available": true, @@ -388,6 +394,7 @@ "name": "Badkamer 1", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "Werkdag schema", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -403,7 +410,8 @@ "primary": ["eac5db95d97241f6b17790897847ccf5"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "93ac3f7bf25342f58cbb77c4a99ac0b3": { "active_preset": "away", @@ -420,6 +428,7 @@ "name": "Slaapkamer RB", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 3.13, "temperature": 20.7 @@ -434,7 +443,8 @@ "primary": ["c4ed311d54e341f58b4cdd201d1fde7e"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "96714ad90fc948bcbcb5021c4b9f5ae9": { "available": true, @@ -471,6 +481,7 @@ "name": "Slaapkamer SQ", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -486,7 +497,8 @@ "primary": ["beb32da072274e698146db8b022f3c36"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "a03b6e8e76dd4646af1a77c31dd9370c": { "available": true, @@ -523,6 +535,7 @@ "name": "Keuken", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "Werkdag schema", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 2.13, "electricity_produced": 0.0, @@ -538,7 +551,8 @@ "primary": ["ea8372c0e3ad4622ad45a041d02425f5"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "b52908550469425b812c87f766fe5303": { "active_preset": "away", @@ -555,6 +569,7 @@ "name": "Bijkeuken", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -570,7 +585,8 @@ "primary": ["1053c8bbf8be43c6921742b146a625f1"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "bbcffa48019f4b09b8368bbaf9559e68": { "available": true, @@ -685,6 +701,7 @@ "name": "Slaapkamer JM", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -700,7 +717,8 @@ "primary": ["7fda9f84f01342f8afe9ebbbbff30c0f"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "ea8372c0e3ad4622ad45a041d02425f5": { "available": true, @@ -787,6 +805,7 @@ "name": "Woonkamer", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "Werkdag schema", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -802,6 +821,7 @@ "primary": ["ca79d23ae0094120b877558734cff85c"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] } } diff --git a/tests/data/adam/adam_jip.json b/tests/data/adam/adam_jip.json index d8caff4b4..da7df0e27 100644 --- a/tests/data/adam/adam_jip.json +++ b/tests/data/adam/adam_jip.json @@ -9,6 +9,7 @@ "name": "Slaapkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 24.2 }, @@ -22,7 +23,8 @@ "primary": ["1346fbd8498d4dbcab7e18d51b771f3d"], "secondary": ["356b65335e274d769c338223e7af9c33"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "13228dab8ce04617af318a2888b3c548": { "active_preset": "home", @@ -34,6 +36,7 @@ "name": "Woonkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 27.4 }, @@ -47,7 +50,8 @@ "primary": ["f61f1a2535f54f52ad006a3d18e459ca"], "secondary": ["833de10f269c4deab58fb9df69901b4e"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "1346fbd8498d4dbcab7e18d51b771f3d": { "available": true, @@ -249,6 +253,7 @@ "name": "Kinderkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 30.0 }, @@ -262,7 +267,8 @@ "primary": ["6f3e9d7084214c21b9dfa46f6eeb8700"], "secondary": ["d4496250d0e942cfa7aea3476e9070d5"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "d4496250d0e942cfa7aea3476e9070d5": { "available": true, @@ -298,6 +304,7 @@ "name": "Logeerkamer", "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], "select_schedule": null, + "select_zone_profile": "active", "sensors": { "temperature": 30.0 }, @@ -311,7 +318,8 @@ "primary": ["a6abc6a129ee499c88a4d420cc413b47"], "secondary": ["1da4d325838e4ad8aac12177214505c9"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "e4684553153b44afbef2200885f379dc": { "available": true, diff --git a/tests/data/adam/adam_onoff_cooling_fake_firmware.json b/tests/data/adam/adam_onoff_cooling_fake_firmware.json index 4803aad72..f822e33d8 100644 --- a/tests/data/adam/adam_onoff_cooling_fake_firmware.json +++ b/tests/data/adam/adam_onoff_cooling_fake_firmware.json @@ -92,6 +92,7 @@ "name": "Woonkamer", "preset_modes": ["no_frost", "vacation", "away", "home", "asleep"], "select_schedule": "Werkdag schema", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -107,6 +108,7 @@ "primary": ["ca79d23ae0094120b877558734cff85c"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] } } diff --git a/tests/data/adam/adam_plus_anna_new.json b/tests/data/adam/adam_plus_anna_new.json index 421d5387d..ee4ecd743 100644 --- a/tests/data/adam/adam_plus_anna_new.json +++ b/tests/data/adam/adam_plus_anna_new.json @@ -280,6 +280,7 @@ "name": "Living room", "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], "select_schedule": "Weekschema", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 60.8, "electricity_produced": 0.0, @@ -299,7 +300,8 @@ ], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "f871b8c4d63549319221e294e4f88074": { "active_preset": "vacation", @@ -317,6 +319,7 @@ "name": "Bathroom", "preset_modes": ["vacation", "no_frost", "asleep", "home", "away"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -332,6 +335,7 @@ "primary": ["e2f4322d57924fa090fbbc48b3a140dc"], "secondary": ["1772a4ea304041adb83f357b751341ff"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] } } diff --git a/tests/data/adam/adam_plus_anna_new_regulation_off.json b/tests/data/adam/adam_plus_anna_new_regulation_off.json index 93710a1fa..8d07fdc92 100644 --- a/tests/data/adam/adam_plus_anna_new_regulation_off.json +++ b/tests/data/adam/adam_plus_anna_new_regulation_off.json @@ -233,6 +233,7 @@ "name": "Living room", "preset_modes": ["no_frost", "asleep", "vacation", "home", "away"], "select_schedule": "off", + "select_zone_profile": "active", "sensors": { "electricity_consumed": 149.9, "electricity_produced": 0.0, @@ -248,7 +249,8 @@ "primary": ["ad4838d7d35c4d6ea796ee12ae5aedf8"], "secondary": [] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] }, "f871b8c4d63549319221e294e4f88074": { "active_preset": "home", @@ -266,6 +268,7 @@ "name": "Bathroom", "preset_modes": ["no_frost", "asleep", "vacation", "home", "away"], "select_schedule": "off", + "select_zone_profile": "passive", "sensors": { "electricity_consumed": 0.0, "electricity_produced": 0.0, @@ -281,6 +284,7 @@ "primary": ["e2f4322d57924fa090fbbc48b3a140dc"], "secondary": ["1772a4ea304041adb83f357b751341ff"] }, - "vendor": "Plugwise" + "vendor": "Plugwise", + "zone_profiles": ["active", "off", "passive"] } } diff --git a/tests/test_adam.py b/tests/test_adam.py index a8ef91155..c45f5a354 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -34,7 +34,7 @@ async def test_connect_adam_plus_anna_new(self): test_items = await self.device_test(api, "2025-10-12 00:00:01", testdata) assert api.gateway_id == "da224107914542988a88561b4452b0f6" - assert self.entity_items == 216 + assert self.entity_items == 220 assert test_items == self.entity_items assert self.entity_list == [ "da224107914542988a88561b4452b0f6", @@ -102,7 +102,9 @@ async def test_connect_adam_plus_anna_new(self): ) assert switch_change switch_change = await self.tinker_switch( - api, "056ee145a816487eaa69243c3280f8bf", model="dhw_cm_switch" + api, + "056ee145a816487eaa69243c3280f8bf", + model="dhw_cm_switch", ) assert switch_change # Test relay without lock-attribute @@ -112,7 +114,8 @@ async def test_connect_adam_plus_anna_new(self): ) assert not switch_change switch_change = await self.tinker_switch( - api, "2568cc4b9c1e401495d4741a5f89bee1" + api, + "2568cc4b9c1e401495d4741a5f89bee1", ) assert not switch_change switch_change = await self.tinker_switch( @@ -136,6 +139,11 @@ async def test_connect_adam_plus_anna_new(self): tinkered = await self.tinker_max_boiler_temp(api) assert not tinkered + assert not await self.tinker_zone_profile( + api, + "f2bf9048bef64cc5b6d5110154e33c81", + ) + # Now change some data and change directory reading xml from # emulating reading newer dataset after an update_interval testdata_updated = await self.load_testdata( @@ -179,6 +187,12 @@ async def test_connect_adam_plus_anna_new(self): tinkered = await self.tinker_regulation_mode(api, unhappy=True) assert tinkered + assert await self.tinker_zone_profile( + api, + "f2bf9048bef64cc5b6d5110154e33c81", + unhappy=True, + ) + await api.close_connection() await self.disconnect(server, client) @@ -324,7 +338,7 @@ async def test_adam_heatpump_cooling(self): server, api, client = await self.connect_wrapper() test_items = await self.device_test(api, "2022-01-02 00:00:01", testdata) - assert self.entity_items == 519 + assert self.entity_items == 539 assert test_items == self.entity_items assert self.cooling_present assert self._cooling_enabled @@ -348,7 +362,7 @@ async def test_connect_adam_onoff_cooling_fake_firmware(self): ) test_items = await self.device_test(api, "2022-01-02 00:00:01", testdata) - assert self.entity_items == 68 + assert self.entity_items == 70 assert test_items == self.entity_items assert self.cooling_present # assert self._cooling_enabled - no cooling_enabled indication present @@ -414,7 +428,7 @@ async def test_adam_plus_jip(self): test_items = await self.device_test(api, "2021-06-20 00:00:01", testdata) assert api.gateway_id == "b5c2386c6f6342669e50fe49dd05b188" - assert self.entity_items == 261 + assert self.entity_items == 269 assert test_items == self.entity_items # Negative test diff --git a/tests/test_init.py b/tests/test_init.py index fbb540c92..1f8f3c803 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -117,6 +117,7 @@ def setup_app( if not raise_timeout: app.router.add_route("POST", CORE_GATEWAYS_TAIL, self.smile_http_accept) app.router.add_route("PUT", CORE_LOCATIONS_TAIL, self.smile_http_accept) + app.router.add_route("POST", CORE_LOCATIONS_TAIL, self.smile_http_accept) app.router.add_route( "DELETE", CORE_NOTIFICATIONS_TAIL, self.smile_http_accept ) @@ -125,6 +126,7 @@ def setup_app( else: app.router.add_route("POST", CORE_GATEWAYS_TAIL, self.smile_timeout) app.router.add_route("PUT", CORE_LOCATIONS_TAIL, self.smile_timeout) + app.router.add_route("POST", CORE_LOCATIONS_TAIL, self.smile_timeout) app.router.add_route("PUT", CORE_RULES_TAIL, self.smile_timeout) app.router.add_route("PUT", CORE_APPLIANCES_TAIL, self.smile_timeout) app.router.add_route("DELETE", CORE_NOTIFICATIONS_TAIL, self.smile_timeout) @@ -986,6 +988,35 @@ async def tinker_gateway_mode(api, unhappy=False): return tinker_gateway_mode_passed + @staticmethod + async def tinker_zone_profile(api, loc_id, unhappy=False): + """Toggle gateway_mode to test functionality.""" + tinker_zone_profile_passed = False + for profile in ["active", "off", "passive", "!bogus"]: + warning = "" + if profile[0] == "!": + warning = " Negative test" + profile = profile[1:] + _LOGGER.info("%s", f"- Adjusting zone_profile to {profile}{warning}") + try: + await api.set_select("select_zone_profile", loc_id, profile) + _LOGGER.info(" + worked as intended") + tinker_zone_profile_passed = True + except pw_exceptions.PlugwiseError: + _LOGGER.info(" + found invalid mode, as expected") + tinker_zone_profile_passed = False + except ( + pw_exceptions.ConnectionFailedError + ): # leave for-loop at connect-error + if unhappy: + _LOGGER.info(" + failed as expected before intended failure") + return True + else: # pragma: no cover + _LOGGER.info(" - succeeded unexpectedly for some reason") + return False + + return tinker_zone_profile_passed + @staticmethod def validate_test_basics( parent_logger,