Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """The Aprilaire climate component."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from pyaprilaire.const import Attribute
8 
10  FAN_AUTO,
11  FAN_ON,
12  PRESET_AWAY,
13  PRESET_NONE,
14  ClimateEntity,
15  ClimateEntityFeature,
16  HVACAction,
17  HVACMode,
18 )
19 from homeassistant.const import PRECISION_HALVES, PRECISION_WHOLE, UnitOfTemperature
20 from homeassistant.core import HomeAssistant
21 from homeassistant.helpers.entity_platform import AddEntitiesCallback
22 
23 from .const import (
24  FAN_CIRCULATE,
25  PRESET_PERMANENT_HOLD,
26  PRESET_TEMPORARY_HOLD,
27  PRESET_VACATION,
28 )
29 from .coordinator import AprilaireConfigEntry
30 from .entity import BaseAprilaireEntity
31 
32 HVAC_MODE_MAP = {
33  1: HVACMode.OFF,
34  2: HVACMode.HEAT,
35  3: HVACMode.COOL,
36  4: HVACMode.HEAT,
37  5: HVACMode.AUTO,
38 }
39 
40 HVAC_MODES_MAP = {
41  1: [HVACMode.OFF, HVACMode.HEAT],
42  2: [HVACMode.OFF, HVACMode.COOL],
43  3: [HVACMode.OFF, HVACMode.HEAT, HVACMode.COOL],
44  4: [HVACMode.OFF, HVACMode.HEAT, HVACMode.COOL],
45  5: [HVACMode.OFF, HVACMode.HEAT, HVACMode.COOL, HVACMode.AUTO],
46  6: [HVACMode.OFF, HVACMode.HEAT, HVACMode.COOL, HVACMode.AUTO],
47 }
48 
49 PRESET_MODE_MAP = {
50  1: PRESET_TEMPORARY_HOLD,
51  2: PRESET_PERMANENT_HOLD,
52  3: PRESET_AWAY,
53  4: PRESET_VACATION,
54 }
55 
56 FAN_MODE_MAP = {
57  1: FAN_ON,
58  2: FAN_AUTO,
59  3: FAN_CIRCULATE,
60 }
61 
62 
64  hass: HomeAssistant,
65  config_entry: AprilaireConfigEntry,
66  async_add_entities: AddEntitiesCallback,
67 ) -> None:
68  """Add climates for passed config_entry in HA."""
69 
71  [AprilaireClimate(config_entry.runtime_data, config_entry.unique_id)]
72  )
73 
74 
76  """Climate entity for Aprilaire."""
77 
78  _attr_fan_modes = [FAN_AUTO, FAN_ON, FAN_CIRCULATE]
79  _attr_min_humidity = 10
80  _attr_max_humidity = 50
81  _attr_temperature_unit = UnitOfTemperature.CELSIUS
82  _attr_translation_key = "thermostat"
83 
84  @property
85  def precision(self) -> float:
86  """Get the precision based on the unit."""
87  return (
88  PRECISION_HALVES
89  if self.hasshasshass.config.units.temperature_unit == UnitOfTemperature.CELSIUS
90  else PRECISION_WHOLE
91  )
92 
93  @property
94  def supported_features(self) -> ClimateEntityFeature:
95  """Get supported features."""
96  features = 0
97 
98  if self.coordinator.data.get(Attribute.MODE) == 5:
99  features = features | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
100  else:
101  features = features | ClimateEntityFeature.TARGET_TEMPERATURE
102 
103  if self.coordinator.data.get(Attribute.HUMIDIFICATION_AVAILABLE) == 2:
104  features = features | ClimateEntityFeature.TARGET_HUMIDITY
105 
106  features = features | ClimateEntityFeature.PRESET_MODE
107 
108  return features | ClimateEntityFeature.FAN_MODE
109 
110  @property
111  def current_humidity(self) -> int | None:
112  """Get current humidity."""
113  return self.coordinator.data.get(
114  Attribute.INDOOR_HUMIDITY_CONTROLLING_SENSOR_VALUE
115  )
116 
117  @property
118  def target_humidity(self) -> int | None:
119  """Get current target humidity."""
120  return self.coordinator.data.get(Attribute.HUMIDIFICATION_SETPOINT)
121 
122  @property
123  def hvac_mode(self) -> HVACMode | None:
124  """Get HVAC mode."""
125 
126  if mode := self.coordinator.data.get(Attribute.MODE):
127  if hvac_mode := HVAC_MODE_MAP.get(mode):
128  return hvac_mode
129 
130  return None
131 
132  @property
133  def hvac_modes(self) -> list[HVACMode]:
134  """Get supported HVAC modes."""
135 
136  if modes := self.coordinator.data.get(Attribute.THERMOSTAT_MODES):
137  if thermostat_modes := HVAC_MODES_MAP.get(modes):
138  return thermostat_modes
139 
140  return []
141 
142  @property
143  def hvac_action(self) -> HVACAction | None:
144  """Get the current HVAC action."""
145 
146  if self.coordinator.data.get(Attribute.HEATING_EQUIPMENT_STATUS, 0):
147  return HVACAction.HEATING
148 
149  if self.coordinator.data.get(Attribute.COOLING_EQUIPMENT_STATUS, 0):
150  return HVACAction.COOLING
151 
152  return HVACAction.IDLE
153 
154  @property
155  def current_temperature(self) -> float | None:
156  """Get current temperature."""
157  return self.coordinator.data.get(
158  Attribute.INDOOR_TEMPERATURE_CONTROLLING_SENSOR_VALUE
159  )
160 
161  @property
162  def target_temperature(self) -> float | None:
163  """Get the target temperature."""
164 
165  hvac_mode = self.hvac_modehvac_modehvac_modehvac_mode
166 
167  if hvac_mode == HVACMode.COOL:
168  return self.target_temperature_hightarget_temperature_hightarget_temperature_high
169  if hvac_mode == HVACMode.HEAT:
170  return self.target_temperature_lowtarget_temperature_lowtarget_temperature_low
171 
172  return None
173 
174  @property
175  def target_temperature_step(self) -> float | None:
176  """Get the step for the target temperature based on the unit."""
177  return (
178  0.5
179  if self.hasshasshass.config.units.temperature_unit == UnitOfTemperature.CELSIUS
180  else 1
181  )
182 
183  @property
184  def target_temperature_high(self) -> float | None:
185  """Get cool setpoint."""
186  return self.coordinator.data.get(Attribute.COOL_SETPOINT)
187 
188  @property
189  def target_temperature_low(self) -> float | None:
190  """Get heat setpoint."""
191  return self.coordinator.data.get(Attribute.HEAT_SETPOINT)
192 
193  @property
194  def preset_mode(self) -> str | None:
195  """Get the current preset mode."""
196  if hold := self.coordinator.data.get(Attribute.HOLD):
197  if preset_mode := PRESET_MODE_MAP.get(hold):
198  return preset_mode
199 
200  return PRESET_NONE
201 
202  @property
203  def preset_modes(self) -> list[str] | None:
204  """Get the supported preset modes."""
205  presets = [PRESET_NONE, PRESET_VACATION]
206 
207  if self.coordinator.data.get(Attribute.AWAY_AVAILABLE) == 1:
208  presets.append(PRESET_AWAY)
209 
210  hold = self.coordinator.data.get(Attribute.HOLD, 0)
211 
212  if hold == 1:
213  presets.append(PRESET_TEMPORARY_HOLD)
214  elif hold == 2:
215  presets.append(PRESET_PERMANENT_HOLD)
216 
217  return presets
218 
219  @property
220  def fan_mode(self) -> str | None:
221  """Get fan mode."""
222 
223  if mode := self.coordinator.data.get(Attribute.FAN_MODE):
224  if fan_mode := FAN_MODE_MAP.get(mode):
225  return fan_mode
226 
227  return None
228 
229  async def async_set_temperature(self, **kwargs: Any) -> None:
230  """Set new target temperature."""
231 
232  cool_setpoint = 0
233  heat_setpoint = 0
234 
235  if temperature := kwargs.get("temperature"):
236  if self.coordinator.data.get(Attribute.MODE) == 3:
237  cool_setpoint = temperature
238  else:
239  heat_setpoint = temperature
240  else:
241  if target_temp_low := kwargs.get("target_temp_low"):
242  heat_setpoint = target_temp_low
243  if target_temp_high := kwargs.get("target_temp_high"):
244  cool_setpoint = target_temp_high
245 
246  if cool_setpoint == 0 and heat_setpoint == 0:
247  return
248 
249  await self.coordinator.client.update_setpoint(cool_setpoint, heat_setpoint)
250 
251  await self.coordinator.client.read_control()
252 
253  async def async_set_humidity(self, humidity: int) -> None:
254  """Set the target humidification setpoint."""
255 
256  await self.coordinator.client.set_humidification_setpoint(humidity)
257 
258  async def async_set_fan_mode(self, fan_mode: str) -> None:
259  """Set the fan mode."""
260 
261  try:
262  fan_mode_value_index = list(FAN_MODE_MAP.values()).index(fan_mode)
263  except ValueError as exc:
264  raise ValueError(f"Unsupported fan mode {fan_mode}") from exc
265 
266  fan_mode_value = list(FAN_MODE_MAP.keys())[fan_mode_value_index]
267 
268  await self.coordinator.client.update_fan_mode(fan_mode_value)
269 
270  await self.coordinator.client.read_control()
271 
272  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
273  """Set the HVAC mode."""
274 
275  try:
276  mode_value_index = list(HVAC_MODE_MAP.values()).index(hvac_mode)
277  except ValueError as exc:
278  raise ValueError(f"Unsupported HVAC mode {hvac_mode}") from exc
279 
280  mode_value = list(HVAC_MODE_MAP.keys())[mode_value_index]
281 
282  await self.coordinator.client.update_mode(mode_value)
283 
284  await self.coordinator.client.read_control()
285 
286  async def async_set_preset_mode(self, preset_mode: str) -> None:
287  """Set the preset mode."""
288 
289  if preset_mode == PRESET_AWAY:
290  await self.coordinator.client.set_hold(3)
291  elif preset_mode == PRESET_VACATION:
292  await self.coordinator.client.set_hold(4)
293  elif preset_mode == PRESET_NONE:
294  await self.coordinator.client.set_hold(0)
295  else:
296  raise ValueError(f"Unsupported preset mode {preset_mode}")
297 
298  await self.coordinator.client.read_scheduling()
None async_set_hvac_mode(self, HVACMode hvac_mode)
Definition: climate.py:272
None async_setup_entry(HomeAssistant hass, AprilaireConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:67