Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """Support for interface with a Gree climate systems."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from greeclimate.device import (
9  TEMP_MAX,
10  TEMP_MAX_F,
11  TEMP_MIN,
12  TEMP_MIN_F,
13  FanSpeed,
14  HorizontalSwing,
15  Mode,
16  TemperatureUnits,
17  VerticalSwing,
18 )
19 
21  ATTR_HVAC_MODE,
22  FAN_AUTO,
23  FAN_HIGH,
24  FAN_LOW,
25  FAN_MEDIUM,
26  PRESET_AWAY,
27  PRESET_BOOST,
28  PRESET_ECO,
29  PRESET_NONE,
30  PRESET_SLEEP,
31  SWING_BOTH,
32  SWING_HORIZONTAL,
33  SWING_OFF,
34  SWING_VERTICAL,
35  ClimateEntity,
36  ClimateEntityFeature,
37  HVACMode,
38 )
39 from homeassistant.config_entries import ConfigEntry
40 from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
41 from homeassistant.core import HomeAssistant, callback
42 from homeassistant.helpers.dispatcher import async_dispatcher_connect
43 from homeassistant.helpers.entity_platform import AddEntitiesCallback
44 
45 from .const import (
46  COORDINATORS,
47  DISPATCH_DEVICE_DISCOVERED,
48  DOMAIN,
49  FAN_MEDIUM_HIGH,
50  FAN_MEDIUM_LOW,
51  TARGET_TEMPERATURE_STEP,
52 )
53 from .coordinator import DeviceDataUpdateCoordinator
54 from .entity import GreeEntity
55 
56 _LOGGER = logging.getLogger(__name__)
57 
58 HVAC_MODES = {
59  Mode.Auto: HVACMode.AUTO,
60  Mode.Cool: HVACMode.COOL,
61  Mode.Dry: HVACMode.DRY,
62  Mode.Fan: HVACMode.FAN_ONLY,
63  Mode.Heat: HVACMode.HEAT,
64 }
65 HVAC_MODES_REVERSE = {v: k for k, v in HVAC_MODES.items()}
66 
67 PRESET_MODES = [
68  PRESET_ECO, # Power saving mode
69  PRESET_AWAY, # Steady heat, or 8C mode on gree units
70  PRESET_BOOST, # Turbo mode
71  PRESET_NONE, # Default operating mode
72  PRESET_SLEEP, # Sleep mode
73 ]
74 
75 FAN_MODES = {
76  FanSpeed.Auto: FAN_AUTO,
77  FanSpeed.Low: FAN_LOW,
78  FanSpeed.MediumLow: FAN_MEDIUM_LOW,
79  FanSpeed.Medium: FAN_MEDIUM,
80  FanSpeed.MediumHigh: FAN_MEDIUM_HIGH,
81  FanSpeed.High: FAN_HIGH,
82 }
83 FAN_MODES_REVERSE = {v: k for k, v in FAN_MODES.items()}
84 
85 SWING_MODES = [SWING_OFF, SWING_VERTICAL, SWING_HORIZONTAL, SWING_BOTH]
86 
87 
89  hass: HomeAssistant,
90  entry: ConfigEntry,
91  async_add_entities: AddEntitiesCallback,
92 ) -> None:
93  """Set up the Gree HVAC device from a config entry."""
94 
95  @callback
96  def init_device(coordinator):
97  """Register the device."""
99 
100  for coordinator in hass.data[DOMAIN][COORDINATORS]:
101  init_device(coordinator)
102 
103  entry.async_on_unload(
104  async_dispatcher_connect(hass, DISPATCH_DEVICE_DISCOVERED, init_device)
105  )
106 
107 
109  """Representation of a Gree HVAC device."""
110 
111  _attr_precision = PRECISION_WHOLE
112  _attr_supported_features = (
113  ClimateEntityFeature.TARGET_TEMPERATURE
114  | ClimateEntityFeature.FAN_MODE
115  | ClimateEntityFeature.PRESET_MODE
116  | ClimateEntityFeature.SWING_MODE
117  | ClimateEntityFeature.TURN_OFF
118  | ClimateEntityFeature.TURN_ON
119  )
120  _attr_target_temperature_step = TARGET_TEMPERATURE_STEP
121  _attr_hvac_modes = [*HVAC_MODES_REVERSE, HVACMode.OFF]
122  _attr_preset_modes = PRESET_MODES
123  _attr_fan_modes = [*FAN_MODES_REVERSE]
124  _attr_swing_modes = SWING_MODES
125  _attr_name = None
126  _attr_temperature_unit = UnitOfTemperature.CELSIUS
127  _attr_min_temp = TEMP_MIN
128  _attr_max_temp = TEMP_MAX
129  _enable_turn_on_off_backwards_compatibility = False
130 
131  def __init__(self, coordinator: DeviceDataUpdateCoordinator) -> None:
132  """Initialize the Gree device."""
133  super().__init__(coordinator)
134  self._attr_unique_id_attr_unique_id_attr_unique_id = coordinator.device.device_info.mac
135 
136  @property
137  def current_temperature(self) -> float:
138  """Return the reported current temperature for the device."""
139  return self.coordinator.device.current_temperature
140 
141  @property
142  def target_temperature(self) -> float:
143  """Return the target temperature for the device."""
144  return self.coordinator.device.target_temperature
145 
146  async def async_set_temperature(self, **kwargs: Any) -> None:
147  """Set new target temperature."""
148  if ATTR_TEMPERATURE not in kwargs:
149  raise ValueError(f"Missing parameter {ATTR_TEMPERATURE}")
150 
151  if hvac_mode := kwargs.get(ATTR_HVAC_MODE):
152  await self.async_set_hvac_modeasync_set_hvac_modeasync_set_hvac_mode(hvac_mode)
153 
154  temperature = kwargs[ATTR_TEMPERATURE]
155  _LOGGER.debug(
156  "Setting temperature to %d for %s",
157  temperature,
158  self._attr_name_attr_name,
159  )
160 
161  self.coordinator.device.target_temperature = temperature
162  await self.coordinator.push_state_update()
163  self.async_write_ha_stateasync_write_ha_state()
164 
165  @property
166  def hvac_mode(self) -> HVACMode | None:
167  """Return the current HVAC mode for the device."""
168  if not self.coordinator.device.power:
169  return HVACMode.OFF
170 
171  return HVAC_MODES.get(self.coordinator.device.mode)
172 
173  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
174  """Set new target hvac mode."""
175  if hvac_mode not in self.hvac_modeshvac_modes:
176  raise ValueError(f"Invalid hvac_mode: {hvac_mode}")
177 
178  _LOGGER.debug(
179  "Setting HVAC mode to %s for device %s",
180  hvac_mode,
181  self._attr_name_attr_name,
182  )
183 
184  if hvac_mode == HVACMode.OFF:
185  self.coordinator.device.power = False
186  await self.coordinator.push_state_update()
187  self.async_write_ha_stateasync_write_ha_state()
188  return
189 
190  if not self.coordinator.device.power:
191  self.coordinator.device.power = True
192 
193  self.coordinator.device.mode = HVAC_MODES_REVERSE.get(hvac_mode)
194  await self.coordinator.push_state_update()
195  self.async_write_ha_stateasync_write_ha_state()
196 
197  async def async_turn_on(self) -> None:
198  """Turn on the device."""
199  _LOGGER.debug("Turning on HVAC for device %s", self._attr_name_attr_name)
200 
201  self.coordinator.device.power = True
202  await self.coordinator.push_state_update()
203  self.async_write_ha_stateasync_write_ha_state()
204 
205  async def async_turn_off(self) -> None:
206  """Turn off the device."""
207  _LOGGER.debug("Turning off HVAC for device %s", self._attr_name_attr_name)
208 
209  self.coordinator.device.power = False
210  await self.coordinator.push_state_update()
211  self.async_write_ha_stateasync_write_ha_state()
212 
213  @property
214  def preset_mode(self) -> str:
215  """Return the current preset mode for the device."""
216  if self.coordinator.device.steady_heat:
217  return PRESET_AWAY
218  if self.coordinator.device.power_save:
219  return PRESET_ECO
220  if self.coordinator.device.sleep:
221  return PRESET_SLEEP
222  if self.coordinator.device.turbo:
223  return PRESET_BOOST
224  return PRESET_NONE
225 
226  async def async_set_preset_mode(self, preset_mode: str) -> None:
227  """Set new preset mode."""
228  if preset_mode not in PRESET_MODES:
229  raise ValueError(f"Invalid preset mode: {preset_mode}")
230 
231  _LOGGER.debug(
232  "Setting preset mode to %s for device %s",
233  preset_mode,
234  self._attr_name_attr_name,
235  )
236 
237  self.coordinator.device.steady_heat = False
238  self.coordinator.device.power_save = False
239  self.coordinator.device.turbo = False
240  self.coordinator.device.sleep = False
241 
242  if preset_mode == PRESET_AWAY:
243  self.coordinator.device.steady_heat = True
244  elif preset_mode == PRESET_ECO:
245  self.coordinator.device.power_save = True
246  elif preset_mode == PRESET_BOOST:
247  self.coordinator.device.turbo = True
248  elif preset_mode == PRESET_SLEEP:
249  self.coordinator.device.sleep = True
250 
251  await self.coordinator.push_state_update()
252  self.async_write_ha_stateasync_write_ha_state()
253 
254  @property
255  def fan_mode(self) -> str | None:
256  """Return the current fan mode for the device."""
257  speed = self.coordinator.device.fan_speed
258  return FAN_MODES.get(speed)
259 
260  async def async_set_fan_mode(self, fan_mode: str) -> None:
261  """Set new target fan mode."""
262  if fan_mode not in FAN_MODES_REVERSE:
263  raise ValueError(f"Invalid fan mode: {fan_mode}")
264 
265  self.coordinator.device.fan_speed = FAN_MODES_REVERSE.get(fan_mode)
266  await self.coordinator.push_state_update()
267  self.async_write_ha_stateasync_write_ha_state()
268 
269  @property
270  def swing_mode(self) -> str:
271  """Return the current swing mode for the device."""
272  h_swing = self.coordinator.device.horizontal_swing == HorizontalSwing.FullSwing
273  v_swing = self.coordinator.device.vertical_swing == VerticalSwing.FullSwing
274 
275  if h_swing and v_swing:
276  return SWING_BOTH
277  if h_swing:
278  return SWING_HORIZONTAL
279  if v_swing:
280  return SWING_VERTICAL
281  return SWING_OFF
282 
283  async def async_set_swing_mode(self, swing_mode: str) -> None:
284  """Set new target swing operation."""
285  if swing_mode not in SWING_MODES:
286  raise ValueError(f"Invalid swing mode: {swing_mode}")
287 
288  _LOGGER.debug(
289  "Setting swing mode to %s for device %s",
290  swing_mode,
291  self._attr_name_attr_name,
292  )
293 
294  self.coordinator.device.horizontal_swing = HorizontalSwing.Center
295  self.coordinator.device.vertical_swing = VerticalSwing.FixedMiddle
296  if swing_mode in (SWING_BOTH, SWING_HORIZONTAL):
297  self.coordinator.device.horizontal_swing = HorizontalSwing.FullSwing
298  if swing_mode in (SWING_BOTH, SWING_VERTICAL):
299  self.coordinator.device.vertical_swing = VerticalSwing.FullSwing
300 
301  await self.coordinator.push_state_update()
302  self.async_write_ha_stateasync_write_ha_state()
303 
304  def _handle_coordinator_update(self) -> None:
305  """Update the state of the entity."""
306  units = self.coordinator.device.temperature_units
307  if (
308  units == TemperatureUnits.C
309  and self._attr_temperature_unit_attr_temperature_unit != UnitOfTemperature.CELSIUS
310  ):
311  _LOGGER.debug("Setting temperature unit to Celsius")
312  self._attr_temperature_unit_attr_temperature_unit = UnitOfTemperature.CELSIUS
313  self._attr_min_temp_attr_min_temp = TEMP_MIN
314  self._attr_max_temp_attr_max_temp = TEMP_MAX
315  elif (
316  units == TemperatureUnits.F
317  and self._attr_temperature_unit_attr_temperature_unit != UnitOfTemperature.FAHRENHEIT
318  ):
319  _LOGGER.debug("Setting temperature unit to Fahrenheit")
320  self._attr_temperature_unit_attr_temperature_unit = UnitOfTemperature.FAHRENHEIT
321  self._attr_min_temp_attr_min_temp = TEMP_MIN_F
322  self._attr_max_temp_attr_max_temp = TEMP_MAX_F
323 
None async_set_hvac_mode(self, HVACMode hvac_mode)
Definition: __init__.py:813
None __init__(self, DeviceDataUpdateCoordinator coordinator)
Definition: climate.py:131
None async_set_hvac_mode(self, HVACMode hvac_mode)
Definition: climate.py:173
None async_set_preset_mode(self, str preset_mode)
Definition: climate.py:226
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:92
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103