Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """Pyaehw4a1 platform to control of Hisense AEH-W4A1 Climate Devices."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from pyaehw4a1.aehw4a1 import AehW4a1
9 import pyaehw4a1.exceptions
10 
12  FAN_AUTO,
13  FAN_HIGH,
14  FAN_LOW,
15  FAN_MEDIUM,
16  PRESET_BOOST,
17  PRESET_ECO,
18  PRESET_NONE,
19  PRESET_SLEEP,
20  SWING_BOTH,
21  SWING_HORIZONTAL,
22  SWING_OFF,
23  SWING_VERTICAL,
24  ClimateEntity,
25  ClimateEntityFeature,
26  HVACMode,
27 )
28 from homeassistant.config_entries import ConfigEntry
29 from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
30 from homeassistant.core import HomeAssistant
31 from homeassistant.helpers.entity_platform import AddEntitiesCallback
32 
33 from . import CONF_IP_ADDRESS, DOMAIN
34 
35 MIN_TEMP_C = 16
36 MAX_TEMP_C = 32
37 
38 MIN_TEMP_F = 61
39 MAX_TEMP_F = 90
40 
41 HVAC_MODES = [
42  HVACMode.OFF,
43  HVACMode.HEAT,
44  HVACMode.COOL,
45  HVACMode.DRY,
46  HVACMode.FAN_ONLY,
47 ]
48 
49 FAN_MODES = [
50  "mute",
51  FAN_LOW,
52  FAN_MEDIUM,
53  FAN_HIGH,
54  FAN_AUTO,
55 ]
56 
57 SWING_MODES = [
58  SWING_OFF,
59  SWING_VERTICAL,
60  SWING_HORIZONTAL,
61  SWING_BOTH,
62 ]
63 
64 PRESET_MODES = [
65  PRESET_NONE,
66  PRESET_ECO,
67  PRESET_BOOST,
68  PRESET_SLEEP,
69  "sleep_2",
70  "sleep_3",
71  "sleep_4",
72 ]
73 
74 AC_TO_HA_STATE = {
75  "0001": HVACMode.HEAT,
76  "0010": HVACMode.COOL,
77  "0011": HVACMode.DRY,
78  "0000": HVACMode.FAN_ONLY,
79 }
80 
81 HA_STATE_TO_AC = {
82  HVACMode.OFF: "off",
83  HVACMode.HEAT: "mode_heat",
84  HVACMode.COOL: "mode_cool",
85  HVACMode.DRY: "mode_dry",
86  HVACMode.FAN_ONLY: "mode_fan",
87 }
88 
89 AC_TO_HA_FAN_MODES = {
90  "00000000": FAN_AUTO, # fan value for heat mode
91  "00000001": FAN_AUTO,
92  "00000010": "mute",
93  "00000100": FAN_LOW,
94  "00000110": FAN_MEDIUM,
95  "00001000": FAN_HIGH,
96 }
97 
98 HA_FAN_MODES_TO_AC = {
99  "mute": "speed_mute",
100  FAN_LOW: "speed_low",
101  FAN_MEDIUM: "speed_med",
102  FAN_HIGH: "speed_max",
103  FAN_AUTO: "speed_auto",
104 }
105 
106 AC_TO_HA_SWING = {
107  "00": SWING_OFF,
108  "10": SWING_VERTICAL,
109  "01": SWING_HORIZONTAL,
110  "11": SWING_BOTH,
111 }
112 
113 _LOGGER = logging.getLogger(__name__)
114 
115 
116 def _build_entity(device):
117  _LOGGER.debug("Found device at %s", device)
118  return ClimateAehW4a1(device)
119 
120 
122  hass: HomeAssistant,
123  config_entry: ConfigEntry,
124  async_add_entities: AddEntitiesCallback,
125 ) -> None:
126  """Set up the AEH-W4A1 climate platform."""
127  # Priority 1: manual config
128  if hass.data[DOMAIN].get(CONF_IP_ADDRESS):
129  devices = hass.data[DOMAIN][CONF_IP_ADDRESS]
130  else:
131  # Priority 2: scanned interfaces
132  devices = await AehW4a1().discovery()
133 
134  entities = [_build_entity(device) for device in devices]
135  async_add_entities(entities, True)
136 
137 
139  """Representation of a Hisense AEH-W4A1 module for climate device."""
140 
141  _attr_hvac_modes = HVAC_MODES
142  _attr_precision = PRECISION_WHOLE
143  _attr_supported_features = (
144  ClimateEntityFeature.TARGET_TEMPERATURE
145  | ClimateEntityFeature.FAN_MODE
146  | ClimateEntityFeature.SWING_MODE
147  | ClimateEntityFeature.PRESET_MODE
148  | ClimateEntityFeature.TURN_OFF
149  | ClimateEntityFeature.TURN_ON
150  )
151  _attr_fan_modes = FAN_MODES
152  _attr_swing_modes = SWING_MODES
153  _attr_preset_modes = PRESET_MODES
154  _attr_available = False
155  _attr_target_temperature_step = 1
156  _previous_state: HVACMode | str | None = None
157  _on: str | None = None
158  _enable_turn_on_off_backwards_compatibility = False
159 
160  def __init__(self, device):
161  """Initialize the climate device."""
162  self._attr_unique_id_attr_unique_id = device
163  self._attr_name_attr_name = device
164  self._device_device = AehW4a1(device)
165 
166  async def async_update(self) -> None:
167  """Pull state from AEH-W4A1."""
168  try:
169  status = await self._device_device.command("status_102_0")
170  except pyaehw4a1.exceptions.ConnectionError as library_error:
171  _LOGGER.warning(
172  "Unexpected error of %s: %s", self._attr_unique_id_attr_unique_id, library_error
173  )
174  self._attr_available_attr_available_attr_available = False
175  return
176 
177  self._attr_available_attr_available_attr_available = True
178 
179  self._on_on = status["run_status"]
180 
181  if status["temperature_Fahrenheit"] == "0":
182  self._attr_temperature_unit_attr_temperature_unit = UnitOfTemperature.CELSIUS
183  self._attr_min_temp_attr_min_temp = MIN_TEMP_C
184  self._attr_max_temp_attr_max_temp = MAX_TEMP_C
185  else:
186  self._attr_temperature_unit_attr_temperature_unit = UnitOfTemperature.FAHRENHEIT
187  self._attr_min_temp_attr_min_temp = MIN_TEMP_F
188  self._attr_max_temp_attr_max_temp = MAX_TEMP_F
189 
190  self._attr_current_temperature_attr_current_temperature = int(status["indoor_temperature_status"], 2)
191 
192  if self._on_on == "1":
193  device_mode = status["mode_status"]
194  self._attr_hvac_mode_attr_hvac_mode = AC_TO_HA_STATE[device_mode]
195 
196  fan_mode = status["wind_status"]
197  self._attr_fan_mode_attr_fan_mode = AC_TO_HA_FAN_MODES[fan_mode]
198 
199  swing_mode = f'{status["up_down"]}{status["left_right"]}'
200  self._attr_swing_mode_attr_swing_mode = AC_TO_HA_SWING[swing_mode]
201 
202  if self._attr_hvac_mode_attr_hvac_mode in (HVACMode.COOL, HVACMode.HEAT):
203  self._attr_target_temperature_attr_target_temperature = int(
204  status["indoor_temperature_setting"], 2
205  )
206  else:
207  self._attr_target_temperature_attr_target_temperature = None
208 
209  if status["efficient"] == "1":
210  self._attr_preset_mode_attr_preset_mode = PRESET_BOOST
211  elif status["low_electricity"] == "1":
212  self._attr_preset_mode_attr_preset_mode = PRESET_ECO
213  elif status["sleep_status"] == "0000001":
214  self._attr_preset_mode_attr_preset_mode = PRESET_SLEEP
215  elif status["sleep_status"] == "0000010":
216  self._attr_preset_mode_attr_preset_mode = "sleep_2"
217  elif status["sleep_status"] == "0000011":
218  self._attr_preset_mode_attr_preset_mode = "sleep_3"
219  elif status["sleep_status"] == "0000100":
220  self._attr_preset_mode_attr_preset_mode = "sleep_4"
221  else:
222  self._attr_preset_mode_attr_preset_mode = PRESET_NONE
223  else:
224  self._attr_hvac_mode_attr_hvac_mode = HVACMode.OFF
225  self._attr_fan_mode_attr_fan_mode = None
226  self._attr_swing_mode_attr_swing_mode = None
227  self._attr_target_temperature_attr_target_temperature = None
228  self._attr_preset_mode_attr_preset_mode = None
229 
230  async def async_set_temperature(self, **kwargs: Any) -> None:
231  """Set new target temperatures."""
232  if self._on_on != "1":
233  _LOGGER.warning(
234  "AC at %s is off, could not set temperature", self._attr_unique_id_attr_unique_id
235  )
236  return
237  if (temp := kwargs.get(ATTR_TEMPERATURE)) is not None:
238  _LOGGER.debug("Setting temp of %s to %s", self._attr_unique_id_attr_unique_id, temp)
239  if self._attr_preset_mode_attr_preset_mode != PRESET_NONE:
240  await self.async_set_preset_modeasync_set_preset_modeasync_set_preset_mode(PRESET_NONE)
241  if self._attr_temperature_unit_attr_temperature_unit == UnitOfTemperature.CELSIUS:
242  await self._device_device.command(f"temp_{int(temp)}_C")
243  else:
244  await self._device_device.command(f"temp_{int(temp)}_F")
245 
246  async def async_set_fan_mode(self, fan_mode: str) -> None:
247  """Set new fan mode."""
248  if self._on_on != "1":
249  _LOGGER.warning(
250  "AC at %s is off, could not set fan mode", self._attr_unique_id_attr_unique_id
251  )
252  return
253  if self._attr_hvac_mode_attr_hvac_mode in (HVACMode.COOL, HVACMode.FAN_ONLY) and (
254  self._attr_hvac_mode_attr_hvac_mode != HVACMode.FAN_ONLY or fan_mode != FAN_AUTO
255  ):
256  _LOGGER.debug(
257  "Setting fan mode of %s to %s", self._attr_unique_id_attr_unique_id, fan_mode
258  )
259  await self._device_device.command(HA_FAN_MODES_TO_AC[fan_mode])
260 
261  async def async_set_swing_mode(self, swing_mode: str) -> None:
262  """Set new target swing operation."""
263  if self._on_on != "1":
264  _LOGGER.warning(
265  "AC at %s is off, could not set swing mode", self._attr_unique_id_attr_unique_id
266  )
267  return
268 
269  _LOGGER.debug(
270  "Setting swing mode of %s to %s", self._attr_unique_id_attr_unique_id, swing_mode
271  )
272  swing_act = self._attr_swing_mode_attr_swing_mode
273 
274  if swing_mode == SWING_OFF and swing_act != SWING_OFF:
275  if swing_act in (SWING_HORIZONTAL, SWING_BOTH):
276  await self._device_device.command("hor_dir")
277  if swing_act in (SWING_VERTICAL, SWING_BOTH):
278  await self._device_device.command("vert_dir")
279 
280  if swing_mode == SWING_BOTH and swing_act != SWING_BOTH:
281  if swing_act in (SWING_OFF, SWING_HORIZONTAL):
282  await self._device_device.command("vert_swing")
283  if swing_act in (SWING_OFF, SWING_VERTICAL):
284  await self._device_device.command("hor_swing")
285 
286  if swing_mode == SWING_VERTICAL and swing_act != SWING_VERTICAL:
287  if swing_act in (SWING_OFF, SWING_HORIZONTAL):
288  await self._device_device.command("vert_swing")
289  if swing_act in (SWING_BOTH, SWING_HORIZONTAL):
290  await self._device_device.command("hor_dir")
291 
292  if swing_mode == SWING_HORIZONTAL and swing_act != SWING_HORIZONTAL:
293  if swing_act in (SWING_BOTH, SWING_VERTICAL):
294  await self._device_device.command("vert_dir")
295  if swing_act in (SWING_OFF, SWING_VERTICAL):
296  await self._device_device.command("hor_swing")
297 
298  async def async_set_preset_mode(self, preset_mode: str) -> None:
299  """Set new preset mode."""
300  if self._on_on != "1":
301  if preset_mode == PRESET_NONE:
302  return
303  await self.async_turn_onasync_turn_onasync_turn_on()
304 
305  _LOGGER.debug(
306  "Setting preset mode of %s to %s", self._attr_unique_id_attr_unique_id, preset_mode
307  )
308 
309  if preset_mode == PRESET_ECO:
310  await self._device_device.command("energysave_on")
311  self._previous_state_previous_state = preset_mode
312  elif preset_mode == PRESET_BOOST:
313  await self._device_device.command("turbo_on")
314  self._previous_state_previous_state = preset_mode
315  elif preset_mode == PRESET_SLEEP:
316  await self._device_device.command("sleep_1")
317  self._previous_state_previous_state = self._attr_hvac_mode_attr_hvac_mode
318  elif preset_mode == "sleep_2":
319  await self._device_device.command("sleep_2")
320  self._previous_state_previous_state = self._attr_hvac_mode_attr_hvac_mode
321  elif preset_mode == "sleep_3":
322  await self._device_device.command("sleep_3")
323  self._previous_state_previous_state = self._attr_hvac_mode_attr_hvac_mode
324  elif preset_mode == "sleep_4":
325  await self._device_device.command("sleep_4")
326  self._previous_state_previous_state = self._attr_hvac_mode_attr_hvac_mode
327  elif self._previous_state_previous_state is not None:
328  if self._previous_state_previous_state == PRESET_ECO:
329  await self._device_device.command("energysave_off")
330  elif self._previous_state_previous_state == PRESET_BOOST:
331  await self._device_device.command("turbo_off")
332  elif self._previous_state_previous_state in HA_STATE_TO_AC and isinstance(
333  self._previous_state_previous_state, HVACMode
334  ):
335  await self._device_device.command(HA_STATE_TO_AC[self._previous_state_previous_state])
336  self._previous_state_previous_state = None
337 
338  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
339  """Set new operation mode."""
340  _LOGGER.debug(
341  "Setting operation mode of %s to %s", self._attr_unique_id_attr_unique_id, hvac_mode
342  )
343  if hvac_mode == HVACMode.OFF:
344  await self.async_turn_offasync_turn_offasync_turn_off()
345  else:
346  await self._device_device.command(HA_STATE_TO_AC[hvac_mode])
347  if self._on_on != "1":
348  await self.async_turn_onasync_turn_onasync_turn_on()
349 
350  async def async_turn_on(self) -> None:
351  """Turn on."""
352  _LOGGER.debug("Turning %s on", self._attr_unique_id_attr_unique_id)
353  await self._device_device.command("on")
354 
355  async def async_turn_off(self) -> None:
356  """Turn off."""
357  _LOGGER.debug("Turning %s off", self._attr_unique_id_attr_unique_id)
358  await self._device_device.command("off")
None async_set_preset_mode(self, str preset_mode)
Definition: __init__.py:861
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:125