Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """AirTouch 4 component to control of AirTouch 4 Climate Devices."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
9  FAN_AUTO,
10  FAN_DIFFUSE,
11  FAN_FOCUS,
12  FAN_HIGH,
13  FAN_LOW,
14  FAN_MEDIUM,
15  ClimateEntity,
16  ClimateEntityFeature,
17  HVACMode,
18 )
19 from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
20 from homeassistant.core import HomeAssistant, callback
21 from homeassistant.helpers.device_registry import DeviceInfo
22 from homeassistant.helpers.entity_platform import AddEntitiesCallback
23 from homeassistant.helpers.update_coordinator import CoordinatorEntity
24 
25 from . import AirTouch4ConfigEntry
26 from .const import DOMAIN
27 
28 AT_TO_HA_STATE = {
29  "Heat": HVACMode.HEAT,
30  "Cool": HVACMode.COOL,
31  "AutoHeat": HVACMode.AUTO, # airtouch reports either autoheat or autocool
32  "AutoCool": HVACMode.AUTO,
33  "Auto": HVACMode.AUTO,
34  "Dry": HVACMode.DRY,
35  "Fan": HVACMode.FAN_ONLY,
36 }
37 
38 HA_STATE_TO_AT = {
39  HVACMode.HEAT: "Heat",
40  HVACMode.COOL: "Cool",
41  HVACMode.AUTO: "Auto",
42  HVACMode.DRY: "Dry",
43  HVACMode.FAN_ONLY: "Fan",
44  HVACMode.OFF: "Off",
45 }
46 
47 AT_TO_HA_FAN_SPEED = {
48  "Quiet": FAN_DIFFUSE,
49  "Low": FAN_LOW,
50  "Medium": FAN_MEDIUM,
51  "High": FAN_HIGH,
52  "Powerful": FAN_FOCUS,
53  "Auto": FAN_AUTO,
54  "Turbo": "turbo",
55 }
56 
57 AT_GROUP_MODES = [HVACMode.OFF, HVACMode.FAN_ONLY]
58 
59 HA_FAN_SPEED_TO_AT = {value: key for key, value in AT_TO_HA_FAN_SPEED.items()}
60 
61 _LOGGER = logging.getLogger(__name__)
62 
63 
65  hass: HomeAssistant,
66  config_entry: AirTouch4ConfigEntry,
67  async_add_entities: AddEntitiesCallback,
68 ) -> None:
69  """Set up the Airtouch 4."""
70  coordinator = config_entry.runtime_data
71  info = coordinator.data
72  entities: list[ClimateEntity] = [
73  AirtouchGroup(coordinator, group["group_number"], info)
74  for group in info["groups"]
75  ]
76  entities.extend(
77  AirtouchAC(coordinator, ac["ac_number"], info) for ac in info["acs"]
78  )
79 
80  _LOGGER.debug(" Found entities %s", entities)
81 
82  async_add_entities(entities)
83 
84 
86  """Representation of an AirTouch 4 ac."""
87 
88  _attr_has_entity_name = True
89  _attr_name = None
90 
91  _attr_supported_features = (
92  ClimateEntityFeature.TARGET_TEMPERATURE
93  | ClimateEntityFeature.FAN_MODE
94  | ClimateEntityFeature.TURN_OFF
95  | ClimateEntityFeature.TURN_ON
96  )
97  _attr_temperature_unit = UnitOfTemperature.CELSIUS
98  _enable_turn_on_off_backwards_compatibility = False
99 
100  def __init__(self, coordinator, ac_number, info):
101  """Initialize the climate device."""
102  super().__init__(coordinator)
103  self._ac_number_ac_number = ac_number
104  self._airtouch_airtouch = coordinator.airtouch
105  self._info_info = info
106  self._unit_unit = self._airtouch_airtouch.GetAcs()[ac_number]
107  self._attr_unique_id_attr_unique_id = f"ac_{ac_number}"
108  self._attr_device_info_attr_device_info = DeviceInfo(
109  identifiers={(DOMAIN, f"ac_{ac_number}")},
110  name=f"AC {ac_number}",
111  manufacturer="Airtouch",
112  model="Airtouch 4",
113  )
114 
115  @callback
117  self._unit_unit = self._airtouch_airtouch.GetAcs()[self._ac_number_ac_number]
118  return super()._handle_coordinator_update()
119 
120  @property
122  """Return the current temperature."""
123  return self._unit_unit.Temperature
124 
125  @property
126  def fan_mode(self):
127  """Return fan mode of the AC this group belongs to."""
128  return AT_TO_HA_FAN_SPEED[self._airtouch_airtouch.acs[self._ac_number_ac_number].AcFanSpeed]
129 
130  @property
131  def fan_modes(self):
132  """Return the list of available fan modes."""
133  airtouch_fan_speeds = self._airtouch_airtouch.GetSupportedFanSpeedsForAc(self._ac_number_ac_number)
134  return [AT_TO_HA_FAN_SPEED[speed] for speed in airtouch_fan_speeds]
135 
136  @property
137  def hvac_mode(self):
138  """Return hvac target hvac state."""
139  is_off = self._unit_unit.PowerState == "Off"
140  if is_off:
141  return HVACMode.OFF
142 
143  return AT_TO_HA_STATE[self._airtouch_airtouch.acs[self._ac_number_ac_number].AcMode]
144 
145  @property
146  def hvac_modes(self):
147  """Return the list of available operation modes."""
148  airtouch_modes = self._airtouch_airtouch.GetSupportedCoolingModesForAc(self._ac_number_ac_number)
149  modes = [AT_TO_HA_STATE[mode] for mode in airtouch_modes]
150  modes.append(HVACMode.OFF)
151  return modes
152 
153  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
154  """Set new operation mode."""
155  if hvac_mode not in HA_STATE_TO_AT:
156  raise ValueError(f"Unsupported HVAC mode: {hvac_mode}")
157 
158  if hvac_mode == HVACMode.OFF:
159  await self.async_turn_offasync_turn_offasync_turn_off()
160  return
161  await self._airtouch_airtouch.SetCoolingModeForAc(
162  self._ac_number_ac_number, HA_STATE_TO_AT[hvac_mode]
163  )
164  # in case it isn't already, unless the HVAC mode was off, then the ac should be on
165  await self.async_turn_onasync_turn_onasync_turn_on()
166  self._unit_unit = self._airtouch_airtouch.GetAcs()[self._ac_number_ac_number]
167  _LOGGER.debug("Setting operation mode of %s to %s", self._ac_number_ac_number, hvac_mode)
168  self.async_write_ha_stateasync_write_ha_state()
169 
170  async def async_set_fan_mode(self, fan_mode: str) -> None:
171  """Set new fan mode."""
172  if fan_mode not in self.fan_modesfan_modesfan_modes:
173  raise ValueError(f"Unsupported fan mode: {fan_mode}")
174 
175  _LOGGER.debug("Setting fan mode of %s to %s", self._ac_number_ac_number, fan_mode)
176  await self._airtouch_airtouch.SetFanSpeedForAc(
177  self._ac_number_ac_number, HA_FAN_SPEED_TO_AT[fan_mode]
178  )
179  self._unit_unit = self._airtouch_airtouch.GetAcs()[self._ac_number_ac_number]
180  self.async_write_ha_stateasync_write_ha_state()
181 
182  async def async_turn_on(self) -> None:
183  """Turn on."""
184  _LOGGER.debug("Turning %s on", self.unique_idunique_id)
185  # in case ac is not on. Airtouch turns itself off if no groups are turned on
186  # (even if groups turned back on)
187  await self._airtouch_airtouch.TurnAcOn(self._ac_number_ac_number)
188 
189  async def async_turn_off(self) -> None:
190  """Turn off."""
191  _LOGGER.debug("Turning %s off", self.unique_idunique_id)
192  await self._airtouch_airtouch.TurnAcOff(self._ac_number_ac_number)
193  self.async_write_ha_stateasync_write_ha_state()
194 
195 
197  """Representation of an AirTouch 4 group."""
198 
199  _attr_has_entity_name = True
200  _attr_name = None
201  _attr_supported_features = (
202  ClimateEntityFeature.TARGET_TEMPERATURE
203  | ClimateEntityFeature.TURN_OFF
204  | ClimateEntityFeature.TURN_ON
205  )
206  _attr_temperature_unit = UnitOfTemperature.CELSIUS
207  _attr_hvac_modes = AT_GROUP_MODES
208  _enable_turn_on_off_backwards_compatibility = False
209 
210  def __init__(self, coordinator, group_number, info):
211  """Initialize the climate device."""
212  super().__init__(coordinator)
213  self._group_number_group_number = group_number
214  self._attr_unique_id_attr_unique_id = group_number
215  self._airtouch_airtouch = coordinator.airtouch
216  self._info_info = info
217  self._unit_unit = self._airtouch_airtouch.GetGroupByGroupNumber(group_number)
218  self._attr_device_info_attr_device_info = DeviceInfo(
219  identifiers={(DOMAIN, group_number)},
220  manufacturer="Airtouch",
221  model="Airtouch 4",
222  name=self._unit_unit.GroupName,
223  )
224 
225  @callback
227  self._unit_unit = self._airtouch_airtouch.GetGroupByGroupNumber(self._group_number_group_number)
228  return super()._handle_coordinator_update()
229 
230  @property
231  def min_temp(self):
232  """Return Minimum Temperature for AC of this group."""
233  return self._airtouch_airtouch.acs[self._unit_unit.BelongsToAc].MinSetpoint
234 
235  @property
236  def max_temp(self):
237  """Return Max Temperature for AC of this group."""
238  return self._airtouch_airtouch.acs[self._unit_unit.BelongsToAc].MaxSetpoint
239 
240  @property
242  """Return the current temperature."""
243  return self._unit_unit.Temperature
244 
245  @property
247  """Return the temperature we are trying to reach."""
248  return self._unit_unit.TargetSetpoint
249 
250  @property
251  def hvac_mode(self):
252  """Return hvac target hvac state."""
253  # there are other power states that aren't 'on' but still count as on (eg. 'Turbo')
254  is_off = self._unit_unit.PowerState == "Off"
255  if is_off:
256  return HVACMode.OFF
257 
258  return HVACMode.FAN_ONLY
259 
260  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
261  """Set new operation mode."""
262  if hvac_mode not in HA_STATE_TO_AT:
263  raise ValueError(f"Unsupported HVAC mode: {hvac_mode}")
264 
265  if hvac_mode == HVACMode.OFF:
266  await self.async_turn_offasync_turn_offasync_turn_off()
267  return
268  if self.hvac_modehvac_modehvac_modehvac_modehvac_mode == HVACMode.OFF:
269  await self.async_turn_onasync_turn_onasync_turn_on()
270  self._unit_unit = self._airtouch_airtouch.GetGroups()[self._group_number_group_number]
271  _LOGGER.debug(
272  "Setting operation mode of %s to %s", self._group_number_group_number, hvac_mode
273  )
274  self.async_write_ha_stateasync_write_ha_state()
275 
276  @property
277  def fan_mode(self):
278  """Return fan mode of the AC this group belongs to."""
279  return AT_TO_HA_FAN_SPEED[self._airtouch_airtouch.acs[self._unit_unit.BelongsToAc].AcFanSpeed]
280 
281  @property
282  def fan_modes(self):
283  """Return the list of available fan modes."""
284  airtouch_fan_speeds = self._airtouch_airtouch.GetSupportedFanSpeedsByGroup(
285  self._group_number_group_number
286  )
287  return [AT_TO_HA_FAN_SPEED[speed] for speed in airtouch_fan_speeds]
288 
289  async def async_set_temperature(self, **kwargs: Any) -> None:
290  """Set new target temperatures."""
291  if (temp := kwargs.get(ATTR_TEMPERATURE)) is None:
292  _LOGGER.debug("Argument `temperature` is missing in set_temperature")
293  return
294 
295  _LOGGER.debug("Setting temp of %s to %s", self._group_number_group_number, str(temp))
296  self._unit_unit = await self._airtouch_airtouch.SetGroupToTemperature(
297  self._group_number_group_number, int(temp)
298  )
299  self.async_write_ha_stateasync_write_ha_state()
300 
301  async def async_set_fan_mode(self, fan_mode: str) -> None:
302  """Set new fan mode."""
303  if fan_mode not in self.fan_modesfan_modesfan_modes:
304  raise ValueError(f"Unsupported fan mode: {fan_mode}")
305 
306  _LOGGER.debug("Setting fan mode of %s to %s", self._group_number_group_number, fan_mode)
307  self._unit_unit = await self._airtouch_airtouch.SetFanSpeedByGroup(
308  self._group_number_group_number, HA_FAN_SPEED_TO_AT[fan_mode]
309  )
310  self.async_write_ha_stateasync_write_ha_state()
311 
312  async def async_turn_on(self) -> None:
313  """Turn on."""
314  _LOGGER.debug("Turning %s on", self.unique_idunique_id)
315  await self._airtouch_airtouch.TurnGroupOn(self._group_number_group_number)
316 
317  # in case ac is not on. Airtouch turns itself off if no groups are turned on
318  # (even if groups turned back on)
319  await self._airtouch_airtouch.TurnAcOn(
320  self._airtouch_airtouch.GetGroupByGroupNumber(self._group_number_group_number).BelongsToAc
321  )
322  # this might cause the ac object to be wrong, so force the shared data
323  # store to update
324  await self.coordinator.async_request_refresh()
325  self.async_write_ha_stateasync_write_ha_state()
326 
327  async def async_turn_off(self) -> None:
328  """Turn off."""
329  _LOGGER.debug("Turning %s off", self.unique_idunique_id)
330  await self._airtouch_airtouch.TurnGroupOff(self._group_number_group_number)
331  # this will cause the ac object to be wrong
332  # (ac turns off automatically if no groups are running)
333  # so force the shared data store to update
334  await self.coordinator.async_request_refresh()
335  self.async_write_ha_stateasync_write_ha_state()
None async_set_hvac_mode(self, HVACMode hvac_mode)
Definition: climate.py:153
def __init__(self, coordinator, ac_number, info)
Definition: climate.py:100
def __init__(self, coordinator, group_number, info)
Definition: climate.py:210
None async_set_hvac_mode(self, HVACMode hvac_mode)
Definition: climate.py:260
None async_setup_entry(HomeAssistant hass, AirTouch4ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:68