Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """Support for Insteon Thermostats via ISY Platform."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from pyisy.constants import (
8  CMD_CLIMATE_FAN_SETTING,
9  CMD_CLIMATE_MODE,
10  ISY_VALUE_UNKNOWN,
11  PROP_HEAT_COOL_STATE,
12  PROP_HUMIDITY,
13  PROP_SETPOINT_COOL,
14  PROP_SETPOINT_HEAT,
15  PROP_UOM,
16  PROTO_INSTEON,
17 )
18 from pyisy.nodes import Node
19 
21  ATTR_TARGET_TEMP_HIGH,
22  ATTR_TARGET_TEMP_LOW,
23  FAN_AUTO,
24  FAN_OFF,
25  FAN_ON,
26  ClimateEntity,
27  ClimateEntityFeature,
28  HVACAction,
29  HVACMode,
30 )
31 from homeassistant.config_entries import ConfigEntry
32 from homeassistant.const import (
33  ATTR_TEMPERATURE,
34  PRECISION_TENTHS,
35  Platform,
36  UnitOfTemperature,
37 )
38 from homeassistant.core import HomeAssistant
39 from homeassistant.helpers.device_registry import DeviceInfo
40 from homeassistant.helpers.entity_platform import AddEntitiesCallback
41 from homeassistant.util.enum import try_parse_enum
42 
43 from .const import (
44  _LOGGER,
45  DOMAIN,
46  HA_FAN_TO_ISY,
47  HA_HVAC_TO_ISY,
48  ISY_HVAC_MODES,
49  UOM_FAN_MODES,
50  UOM_HVAC_ACTIONS,
51  UOM_HVAC_MODE_GENERIC,
52  UOM_HVAC_MODE_INSTEON,
53  UOM_ISY_CELSIUS,
54  UOM_ISY_FAHRENHEIT,
55  UOM_ISYV4_NONE,
56  UOM_TO_STATES,
57 )
58 from .entity import ISYNodeEntity
59 from .helpers import convert_isy_value_to_hass
60 from .models import IsyData
61 
62 
64  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
65 ) -> None:
66  """Set up the ISY thermostat platform."""
67 
68  isy_data: IsyData = hass.data[DOMAIN][entry.entry_id]
69  devices: dict[str, DeviceInfo] = isy_data.devices
70 
72  ISYThermostatEntity(node, devices.get(node.primary_node))
73  for node in isy_data.nodes[Platform.CLIMATE]
74  )
75 
76 
78  """Representation of an ISY thermostat entity."""
79 
80  _attr_hvac_modes = ISY_HVAC_MODES
81  _attr_precision = PRECISION_TENTHS
82  _attr_supported_features = (
83  ClimateEntityFeature.FAN_MODE
84  | ClimateEntityFeature.TARGET_TEMPERATURE
85  | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
86  | ClimateEntityFeature.TURN_OFF
87  | ClimateEntityFeature.TURN_ON
88  )
89  _attr_target_temperature_step = 1.0
90  _attr_fan_modes = [FAN_AUTO, FAN_ON]
91  _enable_turn_on_off_backwards_compatibility = False
92 
93  def __init__(self, node: Node, device_info: DeviceInfo | None = None) -> None:
94  """Initialize the ISY Thermostat entity."""
95  super().__init__(node, device_info=device_info)
96  self._uom_uom = self._node_node.uom
97  if isinstance(self._uom_uom, list):
98  self._uom_uom = self._node_node.uom[0]
99 
100  @property
101  def temperature_unit(self) -> str:
102  """Return the unit of measurement."""
103  if not (uom := self._node_node.aux_properties.get(PROP_UOM)):
104  return self.hasshass.config.units.temperature_unit
105  if uom.value == UOM_ISY_CELSIUS:
106  return UnitOfTemperature.CELSIUS
107  if uom.value == UOM_ISY_FAHRENHEIT:
108  return UnitOfTemperature.FAHRENHEIT
109  return UnitOfTemperature.FAHRENHEIT
110 
111  @property
112  def current_humidity(self) -> int | None:
113  """Return the current humidity."""
114  if not (humidity := self._node_node.aux_properties.get(PROP_HUMIDITY)):
115  return None
116  if humidity.value == ISY_VALUE_UNKNOWN:
117  return None
118  return int(humidity.value)
119 
120  @property
121  def hvac_mode(self) -> HVACMode:
122  """Return hvac operation ie. heat, cool mode."""
123  if not (hvac_mode := self._node_node.aux_properties.get(CMD_CLIMATE_MODE)):
124  return HVACMode.OFF
125 
126  # Which state values used depends on the mode property's UOM:
127  uom = hvac_mode.uom
128  # Handle special case for ISYv4 Firmware:
129  if uom in (UOM_ISYV4_NONE, ""):
130  uom = (
131  UOM_HVAC_MODE_INSTEON
132  if self._node_node.protocol == PROTO_INSTEON
133  else UOM_HVAC_MODE_GENERIC
134  )
135  return (
136  try_parse_enum(HVACMode, UOM_TO_STATES[uom].get(hvac_mode.value))
137  or HVACMode.OFF
138  )
139 
140  @property
141  def hvac_action(self) -> HVACAction | None:
142  """Return the current running hvac operation if supported."""
143  hvac_action = self._node_node.aux_properties.get(PROP_HEAT_COOL_STATE)
144  if not hvac_action:
145  return None
146  return try_parse_enum(
147  HVACAction, UOM_TO_STATES[UOM_HVAC_ACTIONS].get(hvac_action.value)
148  )
149 
150  @property
151  def current_temperature(self) -> float | None:
152  """Return the current temperature."""
154  self._node_node.status, self._uom_uom, self._node_node.prec, 1
155  )
156 
157  @property
158  def target_temperature(self) -> float | None:
159  """Return the temperature we try to reach."""
160  if self.hvac_modehvac_modehvac_modehvac_modehvac_mode == HVACMode.COOL:
161  return self.target_temperature_hightarget_temperature_hightarget_temperature_high
162  if self.hvac_modehvac_modehvac_modehvac_modehvac_mode == HVACMode.HEAT:
163  return self.target_temperature_lowtarget_temperature_lowtarget_temperature_low
164  return None
165 
166  @property
167  def target_temperature_high(self) -> float | None:
168  """Return the highbound target temperature we try to reach."""
169  target = self._node_node.aux_properties.get(PROP_SETPOINT_COOL)
170  if not target:
171  return None
172  return convert_isy_value_to_hass(target.value, target.uom, target.prec, 1)
173 
174  @property
175  def target_temperature_low(self) -> float | None:
176  """Return the lowbound target temperature we try to reach."""
177  target = self._node_node.aux_properties.get(PROP_SETPOINT_HEAT)
178  if not target:
179  return None
180  return convert_isy_value_to_hass(target.value, target.uom, target.prec, 1)
181 
182  @property
183  def fan_mode(self) -> str:
184  """Return the current fan mode ie. auto, on."""
185  fan_mode = self._node_node.aux_properties.get(CMD_CLIMATE_FAN_SETTING)
186  if not fan_mode:
187  return FAN_OFF
188  return UOM_TO_STATES[UOM_FAN_MODES].get(fan_mode.value, FAN_OFF)
189 
190  async def async_set_temperature(self, **kwargs: Any) -> None:
191  """Set new target temperature."""
192  target_temp = kwargs.get(ATTR_TEMPERATURE)
193  target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
194  target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
195  if target_temp is not None:
196  if self.hvac_modehvac_modehvac_modehvac_modehvac_mode == HVACMode.COOL:
197  target_temp_high = target_temp
198  if self.hvac_modehvac_modehvac_modehvac_modehvac_mode == HVACMode.HEAT:
199  target_temp_low = target_temp
200  if target_temp_low is not None:
201  await self._node_node.set_climate_setpoint_heat(int(target_temp_low))
202  if target_temp_high is not None:
203  await self._node_node.set_climate_setpoint_cool(int(target_temp_high))
204  self.async_write_ha_stateasync_write_ha_state()
205 
206  async def async_set_fan_mode(self, fan_mode: str) -> None:
207  """Set new target fan mode."""
208  _LOGGER.debug("Requested fan mode %s", fan_mode)
209  await self._node_node.set_fan_mode(HA_FAN_TO_ISY.get(fan_mode))
210  self.async_write_ha_stateasync_write_ha_state()
211 
212  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
213  """Set new target hvac mode."""
214  _LOGGER.debug("Requested operation mode %s", hvac_mode)
215  await self._node_node.set_climate_mode(HA_HVAC_TO_ISY.get(hvac_mode))
216  self.async_write_ha_stateasync_write_ha_state()
None async_set_hvac_mode(self, HVACMode hvac_mode)
Definition: climate.py:212
None __init__(self, Node node, DeviceInfo|None device_info=None)
Definition: climate.py:93
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:65
float|int|None convert_isy_value_to_hass(float|None value, str|None uom, int|str precision, int|None fallback_precision=None)
Definition: helpers.py:438