Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """Support for deCONZ climate devices."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from pydeconz.models.event import EventType
8 from pydeconz.models.sensor.thermostat import (
9  Thermostat,
10  ThermostatFanMode,
11  ThermostatMode,
12  ThermostatPreset,
13 )
14 
16  DOMAIN as CLIMATE_DOMAIN,
17  FAN_AUTO,
18  FAN_HIGH,
19  FAN_LOW,
20  FAN_MEDIUM,
21  FAN_OFF,
22  FAN_ON,
23  PRESET_BOOST,
24  PRESET_COMFORT,
25  PRESET_ECO,
26  ClimateEntity,
27  ClimateEntityFeature,
28  HVACAction,
29  HVACMode,
30 )
31 from homeassistant.config_entries import ConfigEntry
32 from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
33 from homeassistant.core import HomeAssistant, callback
34 from homeassistant.helpers.entity_platform import AddEntitiesCallback
35 
36 from .const import ATTR_LOCKED, ATTR_OFFSET, ATTR_VALVE
37 from .entity import DeconzDevice
38 from .hub import DeconzHub
39 
40 DECONZ_FAN_SMART = "smart"
41 
42 FAN_MODE_TO_DECONZ = {
43  DECONZ_FAN_SMART: ThermostatFanMode.SMART,
44  FAN_AUTO: ThermostatFanMode.AUTO,
45  FAN_HIGH: ThermostatFanMode.HIGH,
46  FAN_MEDIUM: ThermostatFanMode.MEDIUM,
47  FAN_LOW: ThermostatFanMode.LOW,
48  FAN_ON: ThermostatFanMode.ON,
49  FAN_OFF: ThermostatFanMode.OFF,
50 }
51 DECONZ_TO_FAN_MODE = {value: key for key, value in FAN_MODE_TO_DECONZ.items()}
52 
53 HVAC_MODE_TO_DECONZ = {
54  HVACMode.AUTO: ThermostatMode.AUTO,
55  HVACMode.COOL: ThermostatMode.COOL,
56  HVACMode.HEAT: ThermostatMode.HEAT,
57  HVACMode.OFF: ThermostatMode.OFF,
58 }
59 
60 DECONZ_PRESET_AUTO = "auto"
61 DECONZ_PRESET_COMPLEX = "complex"
62 DECONZ_PRESET_HOLIDAY = "holiday"
63 DECONZ_PRESET_MANUAL = "manual"
64 
65 PRESET_MODE_TO_DECONZ = {
66  DECONZ_PRESET_AUTO: ThermostatPreset.AUTO,
67  PRESET_BOOST: ThermostatPreset.BOOST,
68  PRESET_COMFORT: ThermostatPreset.COMFORT,
69  DECONZ_PRESET_COMPLEX: ThermostatPreset.COMPLEX,
70  PRESET_ECO: ThermostatPreset.ECO,
71  DECONZ_PRESET_HOLIDAY: ThermostatPreset.HOLIDAY,
72  DECONZ_PRESET_MANUAL: ThermostatPreset.MANUAL,
73 }
74 DECONZ_TO_PRESET_MODE = {value: key for key, value in PRESET_MODE_TO_DECONZ.items()}
75 
76 
78  hass: HomeAssistant,
79  config_entry: ConfigEntry,
80  async_add_entities: AddEntitiesCallback,
81 ) -> None:
82  """Set up the deCONZ climate devices."""
83  hub = DeconzHub.get_hub(hass, config_entry)
84  hub.entities[CLIMATE_DOMAIN] = set()
85 
86  @callback
87  def async_add_climate(_: EventType, climate_id: str) -> None:
88  """Add climate from deCONZ."""
89  climate = hub.api.sensors.thermostat[climate_id]
90  async_add_entities([DeconzThermostat(climate, hub)])
91 
92  hub.register_platform_add_device_callback(
93  async_add_climate,
94  hub.api.sensors.thermostat,
95  )
96 
97 
98 class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
99  """Representation of a deCONZ thermostat."""
100 
101  TYPE = CLIMATE_DOMAIN
102 
103  _attr_temperature_unit = UnitOfTemperature.CELSIUS
104  _enable_turn_on_off_backwards_compatibility = False
105 
106  def __init__(self, device: Thermostat, hub: DeconzHub) -> None:
107  """Set up thermostat device."""
108  super().__init__(device, hub)
109 
110  self._attr_hvac_modes_attr_hvac_modes = [
111  HVACMode.HEAT,
112  HVACMode.OFF,
113  ]
114  if device.mode:
115  self._attr_hvac_modes_attr_hvac_modes.append(HVACMode.AUTO)
116 
117  if "coolsetpoint" in device.raw["config"]:
118  self._attr_hvac_modes_attr_hvac_modes.append(HVACMode.COOL)
119 
120  self._deconz_to_hvac_mode_deconz_to_hvac_mode = {
121  HVAC_MODE_TO_DECONZ[item]: item for item in self._attr_hvac_modes_attr_hvac_modes
122  }
123 
124  self._attr_supported_features_attr_supported_features = (
125  ClimateEntityFeature.TARGET_TEMPERATURE
126  | ClimateEntityFeature.TURN_OFF
127  | ClimateEntityFeature.TURN_ON
128  )
129 
130  if device.fan_mode:
131  self._attr_supported_features_attr_supported_features |= ClimateEntityFeature.FAN_MODE
132  self._attr_fan_modes_attr_fan_modes = list(FAN_MODE_TO_DECONZ)
133 
134  if device.preset:
135  self._attr_supported_features_attr_supported_features |= ClimateEntityFeature.PRESET_MODE
136  self._attr_preset_modes_attr_preset_modes = list(PRESET_MODE_TO_DECONZ)
137 
138  # Fan control
139 
140  @property
141  def fan_mode(self) -> str:
142  """Return fan operation."""
143  if self._device.fan_mode in DECONZ_TO_FAN_MODE:
144  return DECONZ_TO_FAN_MODE[self._device.fan_mode]
145  return FAN_ON if self._device.state_on else FAN_OFF
146 
147  async def async_set_fan_mode(self, fan_mode: str) -> None:
148  """Set new target fan mode."""
149  if fan_mode not in FAN_MODE_TO_DECONZ:
150  raise ValueError(f"Unsupported fan mode {fan_mode}")
151 
152  await self.hub.api.sensors.thermostat.set_config(
153  id=self._device.resource_id,
154  fan_mode=FAN_MODE_TO_DECONZ[fan_mode],
155  )
156 
157  # HVAC control
158 
159  @property
160  def hvac_mode(self) -> HVACMode:
161  """Return hvac operation ie. heat, cool mode."""
162  if self._device.mode in self._deconz_to_hvac_mode_deconz_to_hvac_mode:
163  return self._deconz_to_hvac_mode_deconz_to_hvac_mode[self._device.mode]
164  return HVACMode.HEAT if self._device.state_on else HVACMode.OFF
165 
166  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
167  """Set new target hvac mode."""
168  if hvac_mode not in self._attr_hvac_modes_attr_hvac_modes:
169  raise ValueError(f"Unsupported HVAC mode {hvac_mode}")
170 
171  if len(self._attr_hvac_modes_attr_hvac_modes) == 2: # Only allow turn on and off thermostat
172  await self.hub.api.sensors.thermostat.set_config(
173  id=self._device.resource_id,
174  on=hvac_mode != HVACMode.OFF,
175  )
176  else:
177  await self.hub.api.sensors.thermostat.set_config(
178  id=self._device.resource_id,
179  mode=HVAC_MODE_TO_DECONZ[hvac_mode],
180  )
181 
182  @property
183  def hvac_action(self) -> HVACAction:
184  """Return current hvac operation ie. heat, cool.
185 
186  Preset 'BOOST' is interpreted as 'state_on'.
187  """
188  if self._device.mode == ThermostatMode.OFF:
189  return HVACAction.OFF
190 
191  if self._device.state_on or self._device.preset == ThermostatPreset.BOOST:
192  if self._device.mode == ThermostatMode.COOL:
193  return HVACAction.COOLING
194  return HVACAction.HEATING
195  return HVACAction.IDLE
196 
197  # Preset control
198 
199  @property
200  def preset_mode(self) -> str | None:
201  """Return preset mode."""
202  if self._device.preset in DECONZ_TO_PRESET_MODE:
203  return DECONZ_TO_PRESET_MODE[self._device.preset]
204  return None
205 
206  async def async_set_preset_mode(self, preset_mode: str) -> None:
207  """Set new preset mode."""
208  if preset_mode not in PRESET_MODE_TO_DECONZ:
209  raise ValueError(f"Unsupported preset mode {preset_mode}")
210 
211  await self.hub.api.sensors.thermostat.set_config(
212  id=self._device.resource_id,
213  preset=PRESET_MODE_TO_DECONZ[preset_mode],
214  )
215 
216  # Temperature control
217 
218  @property
219  def current_temperature(self) -> float:
220  """Return the current temperature."""
221  return self._device.scaled_temperature
222 
223  @property
224  def target_temperature(self) -> float | None:
225  """Return the target temperature."""
226  if self._device.mode == ThermostatMode.COOL and self._device.cooling_setpoint:
227  return self._device.scaled_cooling_setpoint
228 
229  if self._device.heating_setpoint:
230  return self._device.scaled_heating_setpoint
231 
232  return None
233 
234  async def async_set_temperature(self, **kwargs: Any) -> None:
235  """Set new target temperature."""
236  if ATTR_TEMPERATURE not in kwargs:
237  raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}")
238 
239  if self._device.mode == ThermostatMode.COOL:
240  await self.hub.api.sensors.thermostat.set_config(
241  id=self._device.resource_id,
242  cooling_setpoint=kwargs[ATTR_TEMPERATURE] * 100,
243  )
244  else:
245  await self.hub.api.sensors.thermostat.set_config(
246  id=self._device.resource_id,
247  heating_setpoint=kwargs[ATTR_TEMPERATURE] * 100,
248  )
249 
250  @property
251  def extra_state_attributes(self) -> dict[str, bool | int]:
252  """Return the state attributes of the thermostat."""
253  attr = {}
254 
255  if self._device.offset is not None:
256  attr[ATTR_OFFSET] = self._device.offset
257 
258  if self._device.valve is not None:
259  attr[ATTR_VALVE] = self._device.valve
260 
261  if self._device.locked is not None:
262  attr[ATTR_LOCKED] = self._device.locked
263 
264  return attr
None __init__(self, Thermostat device, DeconzHub hub)
Definition: climate.py:106
None async_set_hvac_mode(self, HVACMode hvac_mode)
Definition: climate.py:166
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:81