Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """Support for the Daikin HVAC."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
9  ATTR_FAN_MODE,
10  ATTR_HVAC_MODE,
11  ATTR_PRESET_MODE,
12  ATTR_SWING_MODE,
13  PRESET_AWAY,
14  PRESET_BOOST,
15  PRESET_ECO,
16  PRESET_NONE,
17  ClimateEntity,
18  ClimateEntityFeature,
19  HVACAction,
20  HVACMode,
21 )
22 from homeassistant.config_entries import ConfigEntry
23 from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
24 from homeassistant.core import HomeAssistant
25 from homeassistant.helpers.entity_platform import AddEntitiesCallback
26 
27 from . import DOMAIN as DAIKIN_DOMAIN
28 from .const import (
29  ATTR_INSIDE_TEMPERATURE,
30  ATTR_OUTSIDE_TEMPERATURE,
31  ATTR_STATE_OFF,
32  ATTR_STATE_ON,
33  ATTR_TARGET_TEMPERATURE,
34 )
35 from .coordinator import DaikinCoordinator
36 from .entity import DaikinEntity
37 
38 _LOGGER = logging.getLogger(__name__)
39 
40 
41 HA_STATE_TO_DAIKIN = {
42  HVACMode.FAN_ONLY: "fan",
43  HVACMode.DRY: "dry",
44  HVACMode.COOL: "cool",
45  HVACMode.HEAT: "hot",
46  HVACMode.HEAT_COOL: "auto",
47  HVACMode.OFF: "off",
48 }
49 
50 DAIKIN_TO_HA_STATE = {
51  "fan": HVACMode.FAN_ONLY,
52  "dry": HVACMode.DRY,
53  "cool": HVACMode.COOL,
54  "hot": HVACMode.HEAT,
55  "auto": HVACMode.HEAT_COOL,
56  "off": HVACMode.OFF,
57 }
58 
59 HA_STATE_TO_CURRENT_HVAC = {
60  HVACMode.COOL: HVACAction.COOLING,
61  HVACMode.HEAT: HVACAction.HEATING,
62  HVACMode.OFF: HVACAction.OFF,
63 }
64 
65 HA_PRESET_TO_DAIKIN = {
66  PRESET_AWAY: "on",
67  PRESET_NONE: "off",
68  PRESET_BOOST: "powerful",
69  PRESET_ECO: "econo",
70 }
71 
72 HA_ATTR_TO_DAIKIN = {
73  ATTR_PRESET_MODE: "en_hol",
74  ATTR_HVAC_MODE: "mode",
75  ATTR_FAN_MODE: "f_rate",
76  ATTR_SWING_MODE: "f_dir",
77  ATTR_INSIDE_TEMPERATURE: "htemp",
78  ATTR_OUTSIDE_TEMPERATURE: "otemp",
79  ATTR_TARGET_TEMPERATURE: "stemp",
80 }
81 
82 DAIKIN_ATTR_ADVANCED = "adv"
83 
84 
86  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
87 ) -> None:
88  """Set up Daikin climate based on config_entry."""
89  daikin_api = hass.data[DAIKIN_DOMAIN].get(entry.entry_id)
90  async_add_entities([DaikinClimate(daikin_api)])
91 
92 
93 def format_target_temperature(target_temperature: float) -> str:
94  """Format target temperature to be sent to the Daikin unit, rounding to nearest half degree."""
95  return str(round(float(target_temperature) * 2, 0) / 2).rstrip("0").rstrip(".")
96 
97 
99  """Representation of a Daikin HVAC."""
100 
101  _attr_name = None
102  _attr_temperature_unit = UnitOfTemperature.CELSIUS
103  _attr_hvac_modes = list(HA_STATE_TO_DAIKIN)
104  _attr_target_temperature_step = 1
105  _attr_fan_modes: list[str]
106  _attr_swing_modes: list[str]
107  _enable_turn_on_off_backwards_compatibility = False
108 
109  def __init__(self, coordinator: DaikinCoordinator) -> None:
110  """Initialize the climate device."""
111  super().__init__(coordinator)
112  self._attr_fan_modes_attr_fan_modes = self.devicedevicedevice.fan_rate
113  self._attr_swing_modes_attr_swing_modes = self.devicedevicedevice.swing_modes
114  self._list: dict[str, list[Any]] = {
115  ATTR_HVAC_MODE: self._attr_hvac_modes_attr_hvac_modes,
116  ATTR_FAN_MODE: self._attr_fan_modes_attr_fan_modes,
117  ATTR_SWING_MODE: self._attr_swing_modes_attr_swing_modes,
118  }
119 
120  self._attr_supported_features_attr_supported_features = (
121  ClimateEntityFeature.TURN_ON
122  | ClimateEntityFeature.TURN_OFF
123  | ClimateEntityFeature.TARGET_TEMPERATURE
124  )
125 
126  if self.devicedevicedevice.support_away_mode or self.devicedevicedevice.support_advanced_modes:
127  self._attr_supported_features_attr_supported_features |= ClimateEntityFeature.PRESET_MODE
128 
129  if self.devicedevicedevice.support_fan_rate:
130  self._attr_supported_features_attr_supported_features |= ClimateEntityFeature.FAN_MODE
131 
132  if self.devicedevicedevice.support_swing_mode:
133  self._attr_supported_features_attr_supported_features |= ClimateEntityFeature.SWING_MODE
134 
135  async def _set(self, settings: dict[str, Any]) -> None:
136  """Set device settings using API."""
137  values: dict[str, Any] = {}
138 
139  for attr in (ATTR_TEMPERATURE, ATTR_FAN_MODE, ATTR_SWING_MODE, ATTR_HVAC_MODE):
140  if (value := settings.get(attr)) is None:
141  continue
142 
143  if (daikin_attr := HA_ATTR_TO_DAIKIN.get(attr)) is not None:
144  if attr == ATTR_HVAC_MODE:
145  values[daikin_attr] = HA_STATE_TO_DAIKIN[value]
146  elif value in self._list[attr]:
147  values[daikin_attr] = value.lower()
148  else:
149  _LOGGER.error("Invalid value %s for %s", attr, value)
150 
151  # temperature
152  elif attr == ATTR_TEMPERATURE:
153  try:
154  values[HA_ATTR_TO_DAIKIN[ATTR_TARGET_TEMPERATURE]] = (
156  )
157  except ValueError:
158  _LOGGER.error("Invalid temperature %s", value)
159 
160  if values:
161  await self.devicedevicedevice.set(values)
162  await self.coordinator.async_refresh()
163 
164  @property
165  def unique_id(self) -> str:
166  """Return a unique ID."""
167  return self.devicedevicedevice.mac
168 
169  @property
170  def current_temperature(self) -> float | None:
171  """Return the current temperature."""
172  return self.devicedevicedevice.inside_temperature
173 
174  @property
175  def target_temperature(self) -> float | None:
176  """Return the temperature we try to reach."""
177  return self.devicedevicedevice.target_temperature
178 
179  async def async_set_temperature(self, **kwargs: Any) -> None:
180  """Set new target temperature."""
181  await self._set_set(kwargs)
182 
183  @property
184  def hvac_action(self) -> HVACAction | None:
185  """Return the current state."""
186  ret = HA_STATE_TO_CURRENT_HVAC.get(self.hvac_modehvac_modehvac_modehvac_mode)
187  if (
188  ret in (HVACAction.COOLING, HVACAction.HEATING)
189  and self.devicedevicedevice.support_compressor_frequency
190  and self.devicedevicedevice.compressor_frequency == 0
191  ):
192  return HVACAction.IDLE
193  return ret
194 
195  @property
196  def hvac_mode(self) -> HVACMode:
197  """Return current operation ie. heat, cool, idle."""
198  daikin_mode = self.devicedevicedevice.represent(HA_ATTR_TO_DAIKIN[ATTR_HVAC_MODE])[1]
199  return DAIKIN_TO_HA_STATE.get(daikin_mode, HVACMode.HEAT_COOL)
200 
201  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
202  """Set HVAC mode."""
203  await self._set_set({ATTR_HVAC_MODE: hvac_mode})
204 
205  @property
206  def fan_mode(self) -> str:
207  """Return the fan setting."""
208  return self.devicedevicedevice.represent(HA_ATTR_TO_DAIKIN[ATTR_FAN_MODE])[1].title()
209 
210  async def async_set_fan_mode(self, fan_mode: str) -> None:
211  """Set fan mode."""
212  await self._set_set({ATTR_FAN_MODE: fan_mode})
213 
214  @property
215  def swing_mode(self) -> str:
216  """Return the fan setting."""
217  return self.devicedevicedevice.represent(HA_ATTR_TO_DAIKIN[ATTR_SWING_MODE])[1].title()
218 
219  async def async_set_swing_mode(self, swing_mode: str) -> None:
220  """Set new target temperature."""
221  await self._set_set({ATTR_SWING_MODE: swing_mode})
222 
223  @property
224  def preset_mode(self) -> str:
225  """Return the preset_mode."""
226  if (
227  self.devicedevicedevice.represent(HA_ATTR_TO_DAIKIN[ATTR_PRESET_MODE])[1]
228  == HA_PRESET_TO_DAIKIN[PRESET_AWAY]
229  ):
230  return PRESET_AWAY
231  if (
232  HA_PRESET_TO_DAIKIN[PRESET_BOOST]
233  in self.devicedevicedevice.represent(DAIKIN_ATTR_ADVANCED)[1]
234  ):
235  return PRESET_BOOST
236  if (
237  HA_PRESET_TO_DAIKIN[PRESET_ECO]
238  in self.devicedevicedevice.represent(DAIKIN_ATTR_ADVANCED)[1]
239  ):
240  return PRESET_ECO
241  return PRESET_NONE
242 
243  async def async_set_preset_mode(self, preset_mode: str) -> None:
244  """Set preset mode."""
245  if preset_mode == PRESET_AWAY:
246  await self.devicedevicedevice.set_holiday(ATTR_STATE_ON)
247  elif preset_mode == PRESET_BOOST:
248  await self.devicedevicedevice.set_advanced_mode(
249  HA_PRESET_TO_DAIKIN[PRESET_BOOST], ATTR_STATE_ON
250  )
251  elif preset_mode == PRESET_ECO:
252  await self.devicedevicedevice.set_advanced_mode(
253  HA_PRESET_TO_DAIKIN[PRESET_ECO], ATTR_STATE_ON
254  )
255  elif self.preset_modepreset_modepreset_modepreset_mode == PRESET_AWAY:
256  await self.devicedevicedevice.set_holiday(ATTR_STATE_OFF)
257  elif self.preset_modepreset_modepreset_modepreset_mode == PRESET_BOOST:
258  await self.devicedevicedevice.set_advanced_mode(
259  HA_PRESET_TO_DAIKIN[PRESET_BOOST], ATTR_STATE_OFF
260  )
261  elif self.preset_modepreset_modepreset_modepreset_mode == PRESET_ECO:
262  await self.devicedevicedevice.set_advanced_mode(
263  HA_PRESET_TO_DAIKIN[PRESET_ECO], ATTR_STATE_OFF
264  )
265  await self.coordinator.async_refresh()
266 
267  @property
268  def preset_modes(self) -> list[str]:
269  """List of available preset modes."""
270  ret = [PRESET_NONE]
271  if self.devicedevicedevice.support_away_mode:
272  ret.append(PRESET_AWAY)
273  if self.devicedevicedevice.support_advanced_modes:
274  ret += [PRESET_ECO, PRESET_BOOST]
275  return ret
276 
277  async def async_turn_on(self) -> None:
278  """Turn device on."""
279  await self.devicedevicedevice.set({})
280  await self.coordinator.async_refresh()
281 
282  async def async_turn_off(self) -> None:
283  """Turn device off."""
284  await self.devicedevicedevice.set(
285  {HA_ATTR_TO_DAIKIN[ATTR_HVAC_MODE]: HA_STATE_TO_DAIKIN[HVACMode.OFF]}
286  )
287  await self.coordinator.async_refresh()
None async_set_preset_mode(self, str preset_mode)
Definition: climate.py:243
None __init__(self, DaikinCoordinator coordinator)
Definition: climate.py:109
None async_set_hvac_mode(self, HVACMode hvac_mode)
Definition: climate.py:201
None async_set_swing_mode(self, str swing_mode)
Definition: climate.py:219
None _set(self, dict[str, Any] settings)
Definition: climate.py:135
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
str format_target_temperature(float target_temperature)
Definition: climate.py:93
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:87