Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """Support for a ScreenLogic heating device."""
2 
3 from dataclasses import dataclass
4 import logging
5 from typing import Any
6 
7 from screenlogicpy.const.common import UNIT, ScreenLogicCommunicationError
8 from screenlogicpy.const.data import ATTR, DEVICE, VALUE
9 from screenlogicpy.const.msg import CODE
10 from screenlogicpy.device_const.heat import HEAT_MODE
11 from screenlogicpy.device_const.system import EQUIPMENT_FLAG
12 
14  ATTR_PRESET_MODE,
15  ClimateEntity,
16  ClimateEntityDescription,
17  ClimateEntityFeature,
18  HVACAction,
19  HVACMode,
20 )
21 from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
22 from homeassistant.core import HomeAssistant
23 from homeassistant.exceptions import HomeAssistantError
24 from homeassistant.helpers.entity_platform import AddEntitiesCallback
25 from homeassistant.helpers.restore_state import RestoreEntity
26 
27 from .entity import ScreenLogicPushEntity, ScreenLogicPushEntityDescription
28 from .types import ScreenLogicConfigEntry
29 
30 _LOGGER = logging.getLogger(__name__)
31 
32 
33 SUPPORTED_MODES = [HVACMode.OFF, HVACMode.HEAT]
34 
35 SUPPORTED_PRESETS = [
36  HEAT_MODE.SOLAR,
37  HEAT_MODE.SOLAR_PREFERRED,
38  HEAT_MODE.HEATER,
39 ]
40 
41 
43  hass: HomeAssistant,
44  config_entry: ScreenLogicConfigEntry,
45  async_add_entities: AddEntitiesCallback,
46 ) -> None:
47  """Set up entry."""
48  coordinator = config_entry.runtime_data
49 
50  gateway = coordinator.gateway
51 
54  coordinator,
56  subscription_code=CODE.STATUS_CHANGED,
57  data_root=(DEVICE.BODY,),
58  key=body_index,
59  ),
60  )
61  for body_index in gateway.get_data(DEVICE.BODY)
62  )
63 
64 
65 @dataclass(frozen=True, kw_only=True)
67  ClimateEntityDescription, ScreenLogicPushEntityDescription
68 ):
69  """Describes a ScreenLogic climate entity."""
70 
71 
73  """Represents a ScreenLogic climate entity."""
74 
75  entity_description: ScreenLogicClimateDescription
76  _attr_hvac_modes = SUPPORTED_MODES
77  _attr_supported_features = (
78  ClimateEntityFeature.TARGET_TEMPERATURE
79  | ClimateEntityFeature.PRESET_MODE
80  | ClimateEntityFeature.TURN_OFF
81  | ClimateEntityFeature.TURN_ON
82  )
83  _enable_turn_on_off_backwards_compatibility = False
84 
85  def __init__(self, coordinator, entity_description) -> None:
86  """Initialize a ScreenLogic climate entity."""
87  super().__init__(coordinator, entity_description)
88  self._configured_heat_modes_configured_heat_modes = []
89  # Is solar listed as available equipment?
90  if EQUIPMENT_FLAG.SOLAR in self.gatewaygatewaygateway.equipment_flags:
91  self._configured_heat_modes_configured_heat_modes.extend(
92  [HEAT_MODE.SOLAR, HEAT_MODE.SOLAR_PREFERRED]
93  )
94  self._configured_heat_modes_configured_heat_modes.append(HEAT_MODE.HEATER)
95  self._attr_preset_modes_attr_preset_modes = [
96  HEAT_MODE(mode_num).title for mode_num in self._configured_heat_modes_configured_heat_modes
97  ]
98 
99  self._attr_min_temp_attr_min_temp = self.entity_dataentity_data[ATTR.MIN_SETPOINT]
100  self._attr_max_temp_attr_max_temp = self.entity_dataentity_data[ATTR.MAX_SETPOINT]
101  self._attr_name_attr_name_attr_name = self.entity_dataentity_data[VALUE.HEAT_STATE][ATTR.NAME]
102  self._last_preset_last_preset = None
103 
104  @property
105  def current_temperature(self) -> float:
106  """Return water temperature."""
107  return self.entity_dataentity_data[VALUE.LAST_TEMPERATURE][ATTR.VALUE]
108 
109  @property
110  def target_temperature(self) -> float:
111  """Target temperature."""
112  return self.entity_dataentity_data[VALUE.HEAT_SETPOINT][ATTR.VALUE]
113 
114  @property
115  def temperature_unit(self) -> str:
116  """Return the unit of measurement."""
117  if self.gatewaygatewaygateway.temperature_unit == UNIT.CELSIUS:
118  return UnitOfTemperature.CELSIUS
119  return UnitOfTemperature.FAHRENHEIT
120 
121  @property
122  def hvac_mode(self) -> HVACMode:
123  """Return the current hvac mode."""
124  if self.entity_dataentity_data[VALUE.HEAT_MODE][ATTR.VALUE] > 0:
125  return HVACMode.HEAT
126  return HVACMode.OFF
127 
128  @property
129  def hvac_action(self) -> HVACAction:
130  """Return the current action of the heater."""
131  if self.entity_dataentity_data[VALUE.HEAT_STATE][ATTR.VALUE] > 0:
132  return HVACAction.HEATING
133  if self.hvac_modehvac_modehvac_modehvac_modehvac_mode == HVACMode.HEAT:
134  return HVACAction.IDLE
135  return HVACAction.OFF
136 
137  @property
138  def preset_mode(self) -> str:
139  """Return current/last preset mode."""
140  if self.hvac_modehvac_modehvac_modehvac_modehvac_mode == HVACMode.OFF:
141  return HEAT_MODE(self._last_preset_last_preset).title
142  return HEAT_MODE(self.entity_dataentity_data[VALUE.HEAT_MODE][ATTR.VALUE]).title
143 
144  async def async_set_temperature(self, **kwargs: Any) -> None:
145  """Change the setpoint of the heater."""
146  if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
147  raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}")
148 
149  try:
150  await self.gatewaygatewaygateway.async_set_heat_temp(
151  int(self._data_key_data_key), int(temperature)
152  )
153  except ScreenLogicCommunicationError as sle:
154  raise HomeAssistantError(
155  f"Failed to set_temperature {temperature} on body"
156  f" {self.entity_data[ATTR.BODY_TYPE][ATTR.VALUE]}:"
157  f" {sle.msg}"
158  ) from sle
159  _LOGGER.debug("Set temperature for body %s to %s", self._data_key_data_key, temperature)
160 
161  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
162  """Set the operation mode."""
163  if hvac_mode == HVACMode.OFF:
164  mode = HEAT_MODE.OFF
165  else:
166  mode = HEAT_MODE.parse(self.preset_modepreset_modepreset_mode)
167 
168  try:
169  await self.gatewaygatewaygateway.async_set_heat_mode(int(self._data_key_data_key), int(mode.value))
170  except ScreenLogicCommunicationError as sle:
171  raise HomeAssistantError(
172  f"Failed to set_hvac_mode {mode.name} on body"
173  f" {self.entity_data[ATTR.BODY_TYPE][ATTR.VALUE]}:"
174  f" {sle.msg}"
175  ) from sle
176  _LOGGER.debug("Set hvac_mode on body %s to %s", self._data_key_data_key, mode.name)
177 
178  async def async_set_preset_mode(self, preset_mode: str) -> None:
179  """Set the preset mode."""
180  mode = HEAT_MODE.parse(preset_mode)
181  _LOGGER.debug("Setting last_preset to %s", mode.name)
182  self._last_preset_last_preset = mode.value
183  if self.hvac_modehvac_modehvac_modehvac_modehvac_mode == HVACMode.OFF:
184  return
185 
186  try:
187  await self.gatewaygatewaygateway.async_set_heat_mode(int(self._data_key_data_key), int(mode.value))
188  except ScreenLogicCommunicationError as sle:
189  raise HomeAssistantError(
190  f"Failed to set_preset_mode {mode.name} on body"
191  f" {self.entity_data[ATTR.BODY_TYPE][ATTR.VALUE]}:"
192  f" {sle.msg}"
193  ) from sle
194  _LOGGER.debug("Set preset_mode on body %s to %s", self._data_key_data_key, mode.name)
195 
196  async def async_added_to_hass(self) -> None:
197  """Run when entity is about to be added."""
198  await super().async_added_to_hass()
199 
200  _LOGGER.debug("Startup last preset is %s", self._last_preset_last_preset)
201  if self._last_preset_last_preset is not None:
202  return
203  prev_state = await self.async_get_last_stateasync_get_last_state()
204  if (
205  prev_state is not None
206  and prev_state.attributes.get(ATTR_PRESET_MODE) is not None
207  ):
208  mode = HEAT_MODE.parse(prev_state.attributes.get(ATTR_PRESET_MODE))
209  _LOGGER.debug(
210  "Startup setting last_preset to %s from prev_state",
211  mode.name,
212  )
213  self._last_preset_last_preset = mode.value
214  else:
215  mode = HEAT_MODE.parse(self._configured_heat_modes_configured_heat_modes[0])
216  _LOGGER.debug(
217  "Startup setting last_preset to default (%s)",
218  mode.name,
219  )
220  self._last_preset_last_preset = mode.value
None __init__(self, coordinator, entity_description)
Definition: climate.py:85
None async_setup_entry(HomeAssistant hass, ScreenLogicConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:46