diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py index 4a6f2bd4937..afa9e8dcd9f 100644 --- a/homeassistant/components/deconz/climate.py +++ b/homeassistant/components/deconz/climate.py @@ -4,6 +4,7 @@ from pydeconz.sensor import Thermostat from homeassistant.components.climate import DOMAIN, ClimateEntity from homeassistant.components.climate.const import ( HVAC_MODE_AUTO, + HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF, SUPPORT_TARGET_TEMPERATURE, @@ -16,7 +17,12 @@ from .const import ATTR_OFFSET, ATTR_VALVE, NEW_SENSOR from .deconz_device import DeconzDevice from .gateway import get_gateway_from_config_entry -HVAC_MODES = {HVAC_MODE_AUTO: "auto", HVAC_MODE_HEAT: "heat", HVAC_MODE_OFF: "off"} +HVAC_MODES = { + HVAC_MODE_AUTO: "auto", + HVAC_MODE_COOL: "cool", + HVAC_MODE_HEAT: "heat", + HVAC_MODE_OFF: "off", +} async def async_setup_entry(hass, config_entry, async_add_entities): @@ -61,10 +67,22 @@ class DeconzThermostat(DeconzDevice, ClimateEntity): TYPE = DOMAIN + def __init__(self, device, gateway): + """Set up thermostat device.""" + super().__init__(device, gateway) + + self._hvac_modes = dict(HVAC_MODES) + if "coolsetpoint" not in device.raw["config"]: + self._hvac_modes.pop(HVAC_MODE_COOL) + + self._features = SUPPORT_TARGET_TEMPERATURE + @property def supported_features(self): """Return the list of supported features.""" - return SUPPORT_TARGET_TEMPERATURE + return self._features + + # HVAC control @property def hvac_mode(self): @@ -72,7 +90,7 @@ class DeconzThermostat(DeconzDevice, ClimateEntity): Need to be one of HVAC_MODE_*. """ - for hass_hvac_mode, device_mode in HVAC_MODES.items(): + for hass_hvac_mode, device_mode in self._hvac_modes.items(): if self._device.mode == device_mode: return hass_hvac_mode @@ -84,16 +102,29 @@ class DeconzThermostat(DeconzDevice, ClimateEntity): @property def hvac_modes(self) -> list: """Return the list of available hvac operation modes.""" - return list(HVAC_MODES) + return list(self._hvac_modes) + + async def async_set_hvac_mode(self, hvac_mode: str) -> None: + """Set new target hvac mode.""" + if hvac_mode not in self._hvac_modes: + raise ValueError(f"Unsupported HVAC mode {hvac_mode}") + + data = {"mode": self._hvac_modes[hvac_mode]} + + await self._device.async_set_config(data) + + # Temperature control @property - def current_temperature(self): + def current_temperature(self) -> float: """Return the current temperature.""" return self._device.temperature @property - def target_temperature(self): + def target_temperature(self) -> float: """Return the target temperature.""" + if self._device.mode == "cool": + return self._device.coolsetpoint return self._device.heatsetpoint async def async_set_temperature(self, **kwargs): @@ -102,15 +133,8 @@ class DeconzThermostat(DeconzDevice, ClimateEntity): raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}") data = {"heatsetpoint": kwargs[ATTR_TEMPERATURE] * 100} - - await self._device.async_set_config(data) - - async def async_set_hvac_mode(self, hvac_mode): - """Set new target hvac mode.""" - if hvac_mode not in HVAC_MODES: - raise ValueError(f"Unsupported mode {hvac_mode}") - - data = {"mode": HVAC_MODES[hvac_mode]} + if self._device.mode == "cool": + data = {"coolsetpoint": kwargs[ATTR_TEMPERATURE] * 100} await self._device.async_set_config(data) diff --git a/homeassistant/components/deconz/cover.py b/homeassistant/components/deconz/cover.py index 48218cf893a..c8d9a16d6c8 100644 --- a/homeassistant/components/deconz/cover.py +++ b/homeassistant/components/deconz/cover.py @@ -116,6 +116,7 @@ class DeconzCover(DeconzDevice, CoverEntity): """Return the current tilt position of the cover.""" if self._device.tilt is not None: return 100 - self._device.tilt + return None async def async_set_cover_tilt_position(self, **kwargs): """Tilt the cover to a specific position.""" diff --git a/tests/components/deconz/test_climate.py b/tests/components/deconz/test_climate.py index 5d2e6d614a3..751f1572239 100644 --- a/tests/components/deconz/test_climate.py +++ b/tests/components/deconz/test_climate.py @@ -73,7 +73,7 @@ async def test_no_sensors(hass): assert len(hass.states.async_all()) == 0 -async def test_climate_devices(hass): +async def test_climate_device_without_cooling_support(hass): """Test successful creation of sensor entities.""" data = deepcopy(DECONZ_WEB_REQUEST) data["sensors"] = deepcopy(SENSORS) @@ -81,7 +81,15 @@ async def test_climate_devices(hass): gateway = get_gateway_from_config_entry(hass, config_entry) assert len(hass.states.async_all()) == 2 - assert hass.states.get("climate.thermostat").state == HVAC_MODE_AUTO + climate_thermostat = hass.states.get("climate.thermostat") + assert climate_thermostat.state == HVAC_MODE_AUTO + assert climate_thermostat.attributes["hvac_modes"] == [ + HVAC_MODE_AUTO, + HVAC_MODE_HEAT, + HVAC_MODE_OFF, + ] + assert climate_thermostat.attributes["current_temperature"] == 22.6 + assert climate_thermostat.attributes["temperature"] == 22.0 assert hass.states.get("sensor.thermostat") is None assert hass.states.get("sensor.thermostat_battery_level").state == "100" assert hass.states.get("climate.presence_sensor") is None @@ -221,6 +229,84 @@ async def test_climate_devices(hass): assert len(hass.states.async_all()) == 0 +async def test_climate_device_with_cooling_support(hass): + """Test successful creation of sensor entities.""" + data = deepcopy(DECONZ_WEB_REQUEST) + data["sensors"] = { + "0": { + "config": { + "battery": 25, + "coolsetpoint": None, + "fanmode": None, + "heatsetpoint": 2222, + "mode": "heat", + "offset": 0, + "on": True, + "reachable": True, + }, + "ep": 1, + "etag": "074549903686a77a12ef0f06c499b1ef", + "lastseen": "2020-11-27T13:45Z", + "manufacturername": "Zen Within", + "modelid": "Zen-01", + "name": "Zen-01", + "state": { + "lastupdated": "2020-11-27T13:42:40.863", + "on": False, + "temperature": 2320, + }, + "type": "ZHAThermostat", + "uniqueid": "00:24:46:00:00:11:6f:56-01-0201", + } + } + config_entry = await setup_deconz_integration(hass, get_state_response=data) + gateway = get_gateway_from_config_entry(hass, config_entry) + + assert len(hass.states.async_all()) == 2 + climate_thermostat = hass.states.get("climate.zen_01") + assert climate_thermostat.state == HVAC_MODE_HEAT + assert climate_thermostat.attributes["hvac_modes"] == [ + HVAC_MODE_AUTO, + HVAC_MODE_COOL, + HVAC_MODE_HEAT, + HVAC_MODE_OFF, + ] + assert climate_thermostat.attributes["current_temperature"] == 23.2 + assert climate_thermostat.attributes["temperature"] == 22.2 + assert hass.states.get("sensor.zen_01_battery_level").state == "25" + + # Event signals thermostat state cool + + state_changed_event = { + "t": "event", + "e": "changed", + "r": "sensors", + "id": "0", + "config": {"mode": "cool"}, + } + gateway.api.event_handler(state_changed_event) + await hass.async_block_till_done() + + assert hass.states.get("climate.zen_01").state == HVAC_MODE_COOL + + # Verify service calls + + thermostat_device = gateway.api.sensors["0"] + + # Service set temperature to 20 + + with patch.object(thermostat_device, "_request", return_value=True) as set_callback: + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: "climate.zen_01", ATTR_TEMPERATURE: 20}, + blocking=True, + ) + set_callback.assert_called_with( + "put", "/sensors/0/config", json={"coolsetpoint": 2000.0} + ) + + async def test_clip_climate_device(hass): """Test successful creation of sensor entities.""" data = deepcopy(DECONZ_WEB_REQUEST)