Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """Support for control of Elk-M1 connected thermostats."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from elkm1_lib.const import ThermostatFan, ThermostatMode, ThermostatSetting
8 from elkm1_lib.elements import Element
9 from elkm1_lib.thermostats import Thermostat
10 
12  ATTR_TARGET_TEMP_HIGH,
13  ATTR_TARGET_TEMP_LOW,
14  FAN_AUTO,
15  FAN_ON,
16  ClimateEntity,
17  ClimateEntityFeature,
18  HVACMode,
19 )
20 from homeassistant.const import PRECISION_WHOLE
21 from homeassistant.core import HomeAssistant
22 from homeassistant.helpers.entity_platform import AddEntitiesCallback
23 from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
24 
25 from . import ElkM1ConfigEntry
26 from .const import DOMAIN
27 from .entity import ElkEntity, create_elk_entities
28 
29 SUPPORT_HVAC = [
30  HVACMode.OFF,
31  HVACMode.HEAT,
32  HVACMode.COOL,
33  HVACMode.HEAT_COOL,
34  HVACMode.FAN_ONLY,
35 ]
36 HASS_TO_ELK_HVAC_MODES = {
37  HVACMode.OFF: (ThermostatMode.OFF, ThermostatFan.AUTO),
38  HVACMode.HEAT: (ThermostatMode.HEAT, None),
39  HVACMode.COOL: (ThermostatMode.COOL, None),
40  HVACMode.HEAT_COOL: (ThermostatMode.AUTO, None),
41  HVACMode.FAN_ONLY: (ThermostatMode.OFF, ThermostatFan.ON),
42 }
43 ELK_TO_HASS_HVAC_MODES = {
44  ThermostatMode.OFF: HVACMode.OFF,
45  ThermostatMode.COOL: HVACMode.COOL,
46  ThermostatMode.HEAT: HVACMode.HEAT,
47  ThermostatMode.EMERGENCY_HEAT: HVACMode.HEAT,
48  ThermostatMode.AUTO: HVACMode.HEAT_COOL,
49 }
50 HASS_TO_ELK_FAN_MODES = {
51  FAN_AUTO: (None, ThermostatFan.AUTO),
52  FAN_ON: (None, ThermostatFan.ON),
53 }
54 ELK_TO_HASS_FAN_MODES = {
55  ThermostatFan.AUTO: FAN_AUTO,
56  ThermostatFan.ON: FAN_ON,
57 }
58 
59 
61  hass: HomeAssistant,
62  config_entry: ElkM1ConfigEntry,
63  async_add_entities: AddEntitiesCallback,
64 ) -> None:
65  """Create the Elk-M1 thermostat platform."""
66  elk_data = config_entry.runtime_data
67  elk = elk_data.elk
68  entities: list[ElkEntity] = []
70  elk_data, elk.thermostats, "thermostat", ElkThermostat, entities
71  )
72  async_add_entities(entities)
73 
74 
76  """Representation of an Elk-M1 Thermostat."""
77 
78  _attr_precision = PRECISION_WHOLE
79  _attr_supported_features = (
80  ClimateEntityFeature.FAN_MODE
81  | ClimateEntityFeature.AUX_HEAT
82  | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
83  | ClimateEntityFeature.TURN_OFF
84  | ClimateEntityFeature.TURN_ON
85  )
86  _attr_min_temp = 1
87  _attr_max_temp = 99
88  _attr_hvac_modes = SUPPORT_HVAC
89  _attr_hvac_mode: HVACMode | None = None
90  _attr_target_temperature_step = 1
91  _attr_fan_modes = [FAN_AUTO, FAN_ON]
92  _element: Thermostat
93  _enable_turn_on_off_backwards_compatibility = False
94 
95  @property
96  def temperature_unit(self) -> str:
97  """Return the temperature unit."""
98  return self._temperature_unit
99 
100  @property
101  def current_temperature(self) -> float | None:
102  """Return the current temperature."""
103  return self._element_element.current_temp
104 
105  @property
106  def target_temperature(self) -> float | None:
107  """Return the temperature we are trying to reach."""
108  if self._element_element.mode in (
109  ThermostatMode.HEAT,
110  ThermostatMode.EMERGENCY_HEAT,
111  ):
112  return self._element_element.heat_setpoint
113  if self._element_element.mode == ThermostatMode.COOL:
114  return self._element_element.cool_setpoint
115  return None
116 
117  @property
118  def target_temperature_high(self) -> float | None:
119  """Return the high target temperature."""
120  return self._element_element.cool_setpoint
121 
122  @property
123  def target_temperature_low(self) -> float | None:
124  """Return the low target temperature."""
125  return self._element_element.heat_setpoint
126 
127  @property
128  def current_humidity(self) -> int | None:
129  """Return the current humidity."""
130  return self._element_element.humidity
131 
132  @property
133  def is_aux_heat(self) -> bool:
134  """Return if aux heater is on."""
135  return self._element_element.mode == ThermostatMode.EMERGENCY_HEAT
136 
137  @property
138  def fan_mode(self) -> str | None:
139  """Return the fan setting."""
140  if self._element_element.fan is None:
141  return None
142  return ELK_TO_HASS_FAN_MODES[self._element_element.fan]
143 
144  def _elk_set(self, mode: ThermostatMode | None, fan: ThermostatFan | None) -> None:
145  if mode is not None:
146  self._element_element.set(ThermostatSetting.MODE, mode)
147  if fan is not None:
148  self._element_element.set(ThermostatSetting.FAN, fan)
149 
150  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
151  """Set thermostat operation mode."""
152  thermostat_mode, fan_mode = HASS_TO_ELK_HVAC_MODES[hvac_mode]
153  self._elk_set_elk_set(thermostat_mode, fan_mode)
154 
155  async def async_turn_aux_heat_on(self) -> None:
156  """Turn auxiliary heater on."""
158  self.hasshass,
159  DOMAIN,
160  "migrate_aux_heat",
161  breaks_in_ha_version="2025.4.0",
162  is_fixable=True,
163  is_persistent=True,
164  translation_key="migrate_aux_heat",
165  severity=IssueSeverity.WARNING,
166  )
167  self._elk_set_elk_set(ThermostatMode.EMERGENCY_HEAT, None)
168 
169  async def async_turn_aux_heat_off(self) -> None:
170  """Turn auxiliary heater off."""
172  self.hasshass,
173  DOMAIN,
174  "migrate_aux_heat",
175  breaks_in_ha_version="2025.4.0",
176  is_fixable=True,
177  is_persistent=True,
178  translation_key="migrate_aux_heat",
179  severity=IssueSeverity.WARNING,
180  )
181  self._elk_set_elk_set(ThermostatMode.HEAT, None)
182 
183  async def async_set_fan_mode(self, fan_mode: str) -> None:
184  """Set new target fan mode."""
185  thermostat_mode, elk_fan_mode = HASS_TO_ELK_FAN_MODES[fan_mode]
186  self._elk_set_elk_set(thermostat_mode, elk_fan_mode)
187 
188  async def async_set_temperature(self, **kwargs: Any) -> None:
189  """Set new target temperature."""
190  low_temp = kwargs.get(ATTR_TARGET_TEMP_LOW)
191  high_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH)
192  if low_temp is not None:
193  self._element_element.set(ThermostatSetting.HEAT_SETPOINT, round(low_temp))
194  if high_temp is not None:
195  self._element_element.set(ThermostatSetting.COOL_SETPOINT, round(high_temp))
196 
197  def _element_changed(self, element: Element, changeset: Any) -> None:
198  if self._element_element.mode is None:
199  self._attr_hvac_mode_attr_hvac_mode = None
200  else:
201  self._attr_hvac_mode_attr_hvac_mode = ELK_TO_HASS_HVAC_MODES[self._element_element.mode]
202  if (
203  self._attr_hvac_mode_attr_hvac_mode == HVACMode.OFF
204  and self._element_element.fan == ThermostatFan.ON
205  ):
206  self._attr_hvac_mode_attr_hvac_mode = HVACMode.FAN_ONLY
None async_set_hvac_mode(self, HVACMode hvac_mode)
Definition: climate.py:150
None _elk_set(self, ThermostatMode|None mode, ThermostatFan|None fan)
Definition: climate.py:144
None _element_changed(self, Element element, Any changeset)
Definition: climate.py:197
None async_setup_entry(HomeAssistant hass, ElkM1ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:64
list[ElkEntity]|None create_elk_entities(ELKM1Data elk_data, Iterable[Element] elk_elements, str element_type, Any class_, list[ElkEntity] entities)
Definition: entity.py:30
None async_create_issue(HomeAssistant hass, str entry_id)
Definition: repairs.py:69