From d5df2b2ee771c8ce607c87a4b522295676f404b9 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 18 Jun 2022 22:15:44 +0200 Subject: [PATCH] Sensibo Add Pure Boost Service (#73114) * Pure Boost Service * Fix tests * Fix mypy * One service to two services * Minor fix test * Fix issues --- homeassistant/components/sensibo/climate.py | 61 +++++++ homeassistant/components/sensibo/entity.py | 7 + .../components/sensibo/services.yaml | 53 ++++++ tests/components/sensibo/test_climate.py | 156 ++++++++++++++++++ 4 files changed, 277 insertions(+) diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index f0ce7b74c01..c146ed350f3 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -29,6 +29,16 @@ from .entity import SensiboDeviceBaseEntity SERVICE_ASSUME_STATE = "assume_state" SERVICE_TIMER = "timer" ATTR_MINUTES = "minutes" +SERVICE_ENABLE_PURE_BOOST = "enable_pure_boost" +SERVICE_DISABLE_PURE_BOOST = "disable_pure_boost" + +ATTR_AC_INTEGRATION = "ac_integration" +ATTR_GEO_INTEGRATION = "geo_integration" +ATTR_INDOOR_INTEGRATION = "indoor_integration" +ATTR_OUTDOOR_INTEGRATION = "outdoor_integration" +ATTR_SENSITIVITY = "sensitivity" +BOOST_INCLUSIVE = "boost_inclusive" + PARALLEL_UPDATES = 0 FIELD_TO_FLAG = { @@ -95,6 +105,24 @@ async def async_setup_entry( }, "async_set_timer", ) + platform.async_register_entity_service( + SERVICE_ENABLE_PURE_BOOST, + { + vol.Inclusive(ATTR_AC_INTEGRATION, "settings"): bool, + vol.Inclusive(ATTR_GEO_INTEGRATION, "settings"): bool, + vol.Inclusive(ATTR_INDOOR_INTEGRATION, "settings"): bool, + vol.Inclusive(ATTR_OUTDOOR_INTEGRATION, "settings"): bool, + vol.Inclusive(ATTR_SENSITIVITY, "settings"): vol.In( + ["Normal", "Sensitive"] + ), + }, + "async_enable_pure_boost", + ) + platform.async_register_entity_service( + SERVICE_DISABLE_PURE_BOOST, + {}, + "async_disable_pure_boost", + ) class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): @@ -308,3 +336,36 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): if result["status"] == "success": return await self.coordinator.async_request_refresh() raise HomeAssistantError(f"Could not set timer for device {self.name}") + + async def async_enable_pure_boost( + self, + ac_integration: bool | None = None, + geo_integration: bool | None = None, + indoor_integration: bool | None = None, + outdoor_integration: bool | None = None, + sensitivity: str | None = None, + ) -> None: + """Enable Pure Boost Configuration.""" + + params: dict[str, str | bool] = { + "enabled": True, + } + if sensitivity is not None: + params["sensitivity"] = sensitivity[0] + if indoor_integration is not None: + params["measurementsIntegration"] = indoor_integration + if ac_integration is not None: + params["acIntegration"] = ac_integration + if geo_integration is not None: + params["geoIntegration"] = geo_integration + if outdoor_integration is not None: + params["primeIntegration"] = outdoor_integration + + await self.async_send_command("set_pure_boost", params) + await self.coordinator.async_refresh() + + async def async_disable_pure_boost(self) -> None: + """Disable Pure Boost Configuration.""" + + await self.async_send_command("set_pure_boost", {"enabled": False}) + await self.coordinator.async_refresh() diff --git a/homeassistant/components/sensibo/entity.py b/homeassistant/components/sensibo/entity.py index 430d7ac61ac..bf70f499ec6 100644 --- a/homeassistant/components/sensibo/entity.py +++ b/homeassistant/components/sensibo/entity.py @@ -99,6 +99,13 @@ class SensiboDeviceBaseEntity(SensiboBaseEntity): result = await self._client.async_set_timer(self._device_id, params) if command == "del_timer": result = await self._client.async_del_timer(self._device_id) + if command == "set_pure_boost": + if TYPE_CHECKING: + assert params is not None + result = await self._client.async_set_pureboost( + self._device_id, + params, + ) return result diff --git a/homeassistant/components/sensibo/services.yaml b/homeassistant/components/sensibo/services.yaml index 1a64f8703b4..67006074f6b 100644 --- a/homeassistant/components/sensibo/services.yaml +++ b/homeassistant/components/sensibo/services.yaml @@ -44,3 +44,56 @@ timer: min: 0 step: 1 mode: box +enable_pure_boost: + name: Enable Pure Boost + description: Enable and configure Pure Boost settings. + target: + entity: + integration: sensibo + domain: climate + fields: + ac_integration: + name: AC Integration + description: Integrate with Air Conditioner. + required: false + example: true + selector: + boolean: + geo_integration: + name: Geo Integration + description: Integrate with Presence. + required: false + example: true + selector: + boolean: + indoor_integration: + name: Indoor Air Quality + description: Integrate with checking indoor air quality. + required: false + example: true + selector: + boolean: + outdoor_integration: + name: Outdoor Air Quality + description: Integrate with checking outdoor air quality. + required: false + example: true + selector: + boolean: + sensitivity: + name: Sensitivity + description: Set the sensitivity for Pure Boost. + required: false + example: "Normal" + selector: + select: + options: + - "Normal" + - "Sensitive" +disable_pure_boost: + name: Disable Pure Boost + description: Disable Pure Boost. + target: + entity: + integration: sensibo + domain: climate diff --git a/tests/components/sensibo/test_climate.py b/tests/components/sensibo/test_climate.py index 16e83162600..30356f2b00d 100644 --- a/tests/components/sensibo/test_climate.py +++ b/tests/components/sensibo/test_climate.py @@ -21,8 +21,15 @@ from homeassistant.components.climate.const import ( SERVICE_SET_TEMPERATURE, ) from homeassistant.components.sensibo.climate import ( + ATTR_AC_INTEGRATION, + ATTR_GEO_INTEGRATION, + ATTR_INDOOR_INTEGRATION, ATTR_MINUTES, + ATTR_OUTDOOR_INTEGRATION, + ATTR_SENSITIVITY, SERVICE_ASSUME_STATE, + SERVICE_DISABLE_PURE_BOOST, + SERVICE_ENABLE_PURE_BOOST, SERVICE_TIMER, _find_valid_target_temp, ) @@ -905,3 +912,152 @@ async def test_climate_set_timer_failures( blocking=True, ) await hass.async_block_till_done() + + +async def test_climate_pure_boost( + hass: HomeAssistant, + entity_registry_enabled_by_default: AsyncMock, + load_int: ConfigEntry, + monkeypatch: pytest.MonkeyPatch, + get_data: SensiboData, +) -> None: + """Test the Sensibo climate assumed state service.""" + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=get_data, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state_climate = hass.states.get("climate.kitchen") + state2 = hass.states.get("binary_sensor.kitchen_pure_boost_enabled") + assert state2.state == "off" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_pureboost", + ): + with pytest.raises(MultipleInvalid): + await hass.services.async_call( + DOMAIN, + SERVICE_ENABLE_PURE_BOOST, + { + ATTR_ENTITY_ID: state_climate.entity_id, + ATTR_INDOOR_INTEGRATION: True, + ATTR_OUTDOOR_INTEGRATION: True, + ATTR_SENSITIVITY: "Sensitive", + }, + blocking=True, + ) + await hass.async_block_till_done() + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + return_value=get_data, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_pureboost", + return_value={ + "status": "success", + "result": { + "enabled": True, + "sensitivity": "S", + "measurements_integration": True, + "ac_integration": False, + "geo_integration": False, + "prime_integration": True, + }, + }, + ): + await hass.services.async_call( + DOMAIN, + SERVICE_ENABLE_PURE_BOOST, + { + ATTR_ENTITY_ID: state_climate.entity_id, + ATTR_AC_INTEGRATION: False, + ATTR_GEO_INTEGRATION: False, + ATTR_INDOOR_INTEGRATION: True, + ATTR_OUTDOOR_INTEGRATION: True, + ATTR_SENSITIVITY: "Sensitive", + }, + blocking=True, + ) + await hass.async_block_till_done() + + monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_boost_enabled", True) + monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_sensitivity", "s") + monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_measure_integration", True) + monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_prime_integration", True) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=get_data, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("binary_sensor.kitchen_pure_boost_enabled") + state2 = hass.states.get( + "binary_sensor.kitchen_pure_boost_linked_with_indoor_air_quality" + ) + state3 = hass.states.get( + "binary_sensor.kitchen_pure_boost_linked_with_outdoor_air_quality" + ) + state4 = hass.states.get("sensor.kitchen_pure_sensitivity") + assert state1.state == "on" + assert state2.state == "on" + assert state3.state == "on" + assert state4.state == "s" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + return_value=get_data, + ), patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_set_pureboost", + return_value={ + "status": "success", + "result": { + "enabled": False, + "sensitivity": "S", + "measurements_integration": True, + "ac_integration": False, + "geo_integration": False, + "prime_integration": True, + }, + }, + ) as mock_set_pureboost: + await hass.services.async_call( + DOMAIN, + SERVICE_DISABLE_PURE_BOOST, + { + ATTR_ENTITY_ID: state_climate.entity_id, + }, + blocking=True, + ) + await hass.async_block_till_done() + mock_set_pureboost.assert_called_once() + + monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_boost_enabled", False) + monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_sensitivity", "s") + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=get_data, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("binary_sensor.kitchen_pure_boost_enabled") + state4 = hass.states.get("sensor.kitchen_pure_sensitivity") + assert state1.state == "off" + assert state4.state == "s"