Add preset support to deCONZ climate platform (#43722)

This commit is contained in:
Robert Svensson 2020-12-02 17:14:05 +01:00 committed by GitHub
parent edb246d696
commit 25db1dac23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 180 additions and 0 deletions

View File

@ -1,4 +1,6 @@
"""Support for deCONZ climate devices.""" """Support for deCONZ climate devices."""
from typing import Optional
from pydeconz.sensor import Thermostat from pydeconz.sensor import Thermostat
from homeassistant.components.climate import DOMAIN, ClimateEntity from homeassistant.components.climate import DOMAIN, ClimateEntity
@ -7,6 +9,10 @@ from homeassistant.components.climate.const import (
HVAC_MODE_COOL, HVAC_MODE_COOL,
HVAC_MODE_HEAT, HVAC_MODE_HEAT,
HVAC_MODE_OFF, HVAC_MODE_OFF,
PRESET_BOOST,
PRESET_COMFORT,
PRESET_ECO,
SUPPORT_PRESET_MODE,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE,
) )
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
@ -24,6 +30,21 @@ HVAC_MODES = {
HVAC_MODE_OFF: "off", HVAC_MODE_OFF: "off",
} }
DECONZ_PRESET_AUTO = "auto"
DECONZ_PRESET_COMPLEX = "complex"
DECONZ_PRESET_HOLIDAY = "holiday"
DECONZ_PRESET_MANUAL = "manual"
PRESET_MODES = {
DECONZ_PRESET_AUTO: "auto",
PRESET_BOOST: "boost",
PRESET_COMFORT: "comfort",
DECONZ_PRESET_COMPLEX: "complex",
PRESET_ECO: "eco",
DECONZ_PRESET_HOLIDAY: "holiday",
DECONZ_PRESET_MANUAL: "manual",
}
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the deCONZ climate devices. """Set up the deCONZ climate devices.
@ -82,6 +103,9 @@ class DeconzThermostat(DeconzDevice, ClimateEntity):
self._features = SUPPORT_TARGET_TEMPERATURE self._features = SUPPORT_TARGET_TEMPERATURE
if "preset" in device.raw["config"]:
self._features |= SUPPORT_PRESET_MODE
@property @property
def supported_features(self): def supported_features(self):
"""Return the list of supported features.""" """Return the list of supported features."""
@ -120,6 +144,31 @@ class DeconzThermostat(DeconzDevice, ClimateEntity):
await self._device.async_set_config(data) await self._device.async_set_config(data)
# Preset control
@property
def preset_mode(self) -> Optional[str]:
"""Return preset mode."""
for hass_preset_mode, preset_mode in PRESET_MODES.items():
if self._device.preset == preset_mode:
return hass_preset_mode
return None
@property
def preset_modes(self) -> list:
"""Return the list of available preset modes."""
return list(PRESET_MODES)
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""
if preset_mode not in PRESET_MODES:
raise ValueError(f"Unsupported preset mode {preset_mode}")
data = {"preset": PRESET_MODES[preset_mode]}
await self._device.async_set_config(data)
# Temperature control # Temperature control
@property @property

View File

@ -7,17 +7,21 @@ import pytest
from homeassistant.components.climate import ( from homeassistant.components.climate import (
DOMAIN as CLIMATE_DOMAIN, DOMAIN as CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE, SERVICE_SET_HVAC_MODE,
SERVICE_SET_PRESET_MODE,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
) )
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
ATTR_HVAC_MODE, ATTR_HVAC_MODE,
ATTR_PRESET_MODE,
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_LOW,
HVAC_MODE_AUTO, HVAC_MODE_AUTO,
HVAC_MODE_COOL, HVAC_MODE_COOL,
HVAC_MODE_HEAT, HVAC_MODE_HEAT,
HVAC_MODE_OFF, HVAC_MODE_OFF,
PRESET_COMFORT,
) )
from homeassistant.components.deconz.climate import DECONZ_PRESET_MANUAL
from homeassistant.components.deconz.const import ( from homeassistant.components.deconz.const import (
CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_CLIP_SENSOR,
DOMAIN as DECONZ_DOMAIN, DOMAIN as DECONZ_DOMAIN,
@ -428,6 +432,133 @@ async def test_climate_device_with_cooling_support(hass):
) )
async def test_climate_device_with_preset(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",
"preset": "auto",
"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_zen_01 = hass.states.get("climate.zen_01")
assert climate_zen_01.state == HVAC_MODE_HEAT
assert climate_zen_01.attributes["current_temperature"] == 23.2
assert climate_zen_01.attributes["temperature"] == 22.2
assert climate_zen_01.attributes["preset_mode"] == "auto"
assert climate_zen_01.attributes["preset_modes"] == [
"auto",
"boost",
"comfort",
"complex",
"eco",
"holiday",
"manual",
]
# Event signals deCONZ preset
state_changed_event = {
"t": "event",
"e": "changed",
"r": "sensors",
"id": "0",
"config": {"preset": "manual"},
}
gateway.api.event_handler(state_changed_event)
await hass.async_block_till_done()
assert (
hass.states.get("climate.zen_01").attributes["preset_mode"]
== DECONZ_PRESET_MANUAL
)
# Event signals unknown preset
state_changed_event = {
"t": "event",
"e": "changed",
"r": "sensors",
"id": "0",
"config": {"preset": "unsupported"},
}
gateway.api.event_handler(state_changed_event)
await hass.async_block_till_done()
assert hass.states.get("climate.zen_01").attributes["preset_mode"] is None
# Verify service calls
thermostat_device = gateway.api.sensors["0"]
# Service set preset to HASS preset
with patch.object(thermostat_device, "_request", return_value=True) as set_callback:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: "climate.zen_01", ATTR_PRESET_MODE: PRESET_COMFORT},
blocking=True,
)
await hass.async_block_till_done()
set_callback.assert_called_with(
"put", "/sensors/0/config", json={"preset": "comfort"}
)
# Service set preset to custom deCONZ preset
with patch.object(thermostat_device, "_request", return_value=True) as set_callback:
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: "climate.zen_01", ATTR_PRESET_MODE: DECONZ_PRESET_MANUAL},
blocking=True,
)
await hass.async_block_till_done()
set_callback.assert_called_with(
"put", "/sensors/0/config", json={"preset": "manual"}
)
# Service set preset to unsupported value
with patch.object(
thermostat_device, "_request", return_value=True
) as set_callback, pytest.raises(ValueError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: "climate.zen_01", ATTR_PRESET_MODE: "unsupported"},
blocking=True,
)
async def test_clip_climate_device(hass): async def test_clip_climate_device(hass):
"""Test successful creation of sensor entities.""" """Test successful creation of sensor entities."""
data = deepcopy(DECONZ_WEB_REQUEST) data = deepcopy(DECONZ_WEB_REQUEST)