Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """Support for SwitchBee climate."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from switchbee.api.central_unit import SwitchBeeDeviceOfflineError, SwitchBeeError
8 from switchbee.const import (
9  ApiAttribute,
10  ThermostatFanSpeed,
11  ThermostatMode,
12  ThermostatTemperatureUnit,
13 )
14 from switchbee.device import ApiStateCommand, SwitchBeeThermostat
15 
17  FAN_AUTO,
18  FAN_HIGH,
19  FAN_LOW,
20  FAN_MEDIUM,
21  ClimateEntity,
22  ClimateEntityFeature,
23  HVACAction,
24  HVACMode,
25 )
26 from homeassistant.config_entries import ConfigEntry
27 from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
28 from homeassistant.core import HomeAssistant, callback
29 from homeassistant.exceptions import HomeAssistantError
30 from homeassistant.helpers.entity_platform import AddEntitiesCallback
31 
32 from .const import DOMAIN
33 from .coordinator import SwitchBeeCoordinator
34 from .entity import SwitchBeeDeviceEntity
35 
36 FAN_SB_TO_HASS = {
37  ThermostatFanSpeed.AUTO: FAN_AUTO,
38  ThermostatFanSpeed.LOW: FAN_LOW,
39  ThermostatFanSpeed.MEDIUM: FAN_MEDIUM,
40  ThermostatFanSpeed.HIGH: FAN_HIGH,
41 }
42 
43 FAN_HASS_TO_SB: dict[str | None, str] = {
44  FAN_AUTO: ThermostatFanSpeed.AUTO,
45  FAN_LOW: ThermostatFanSpeed.LOW,
46  FAN_MEDIUM: ThermostatFanSpeed.MEDIUM,
47  FAN_HIGH: ThermostatFanSpeed.HIGH,
48 }
49 
50 HVAC_MODE_SB_TO_HASS = {
51  ThermostatMode.COOL: HVACMode.COOL,
52  ThermostatMode.HEAT: HVACMode.HEAT,
53  ThermostatMode.FAN: HVACMode.FAN_ONLY,
54 }
55 
56 HVAC_MODE_HASS_TO_SB: dict[HVACMode | str | None, str] = {
57  HVACMode.COOL: ThermostatMode.COOL,
58  HVACMode.HEAT: ThermostatMode.HEAT,
59  HVACMode.FAN_ONLY: ThermostatMode.FAN,
60 }
61 
62 HVAC_ACTION_SB_TO_HASS = {
63  ThermostatMode.COOL: HVACAction.COOLING,
64  ThermostatMode.HEAT: HVACAction.HEATING,
65  ThermostatMode.FAN: HVACAction.FAN,
66 }
67 
68 HVAC_UNIT_SB_TO_HASS = {
69  ThermostatTemperatureUnit.CELSIUS: UnitOfTemperature.CELSIUS,
70  ThermostatTemperatureUnit.FAHRENHEIT: UnitOfTemperature.FAHRENHEIT,
71 }
72 
73 SUPPORTED_FAN_MODES = [FAN_AUTO, FAN_HIGH, FAN_MEDIUM, FAN_LOW]
74 
75 
77  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
78 ) -> None:
79  """Set up SwitchBee climate."""
80  coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]
82  SwitchBeeClimateEntity(switchbee_device, coordinator)
83  for switchbee_device in coordinator.data.values()
84  if isinstance(switchbee_device, SwitchBeeThermostat)
85  )
86 
87 
88 class SwitchBeeClimateEntity(SwitchBeeDeviceEntity[SwitchBeeThermostat], ClimateEntity):
89  """Representation of a SwitchBee climate."""
90 
91  _attr_fan_modes = SUPPORTED_FAN_MODES
92  _attr_target_temperature_step = 1
93  _enable_turn_on_off_backwards_compatibility = False
94 
95  def __init__(
96  self,
97  device: SwitchBeeThermostat,
98  coordinator: SwitchBeeCoordinator,
99  ) -> None:
100  """Initialize the Switchbee switch."""
101  super().__init__(device, coordinator)
102  # set HVAC capabilities
103  self._attr_max_temp_attr_max_temp = device.max_temperature
104  self._attr_min_temp_attr_min_temp = device.min_temperature
105  self._attr_temperature_unit_attr_temperature_unit = HVAC_UNIT_SB_TO_HASS[device.temperature_unit]
106  self._attr_hvac_modes_attr_hvac_modes = [HVAC_MODE_SB_TO_HASS[mode] for mode in device.modes]
107  self._attr_hvac_modes_attr_hvac_modes.append(HVACMode.OFF)
108  self._attr_supported_features_attr_supported_features = (
109  ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
110  )
111  if len(self.hvac_modeshvac_modes) > 1:
112  self._attr_supported_features_attr_supported_features |= (
113  ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
114  )
115  self._update_attrs_from_coordinator_update_attrs_from_coordinator()
116 
117  @callback
118  def _handle_coordinator_update(self) -> None:
119  """Handle updated data from the coordinator."""
120  self._update_attrs_from_coordinator_update_attrs_from_coordinator()
122 
124  coordinator_device = self._get_coordinator_device()
125 
126  self._attr_hvac_mode: HVACMode = (
127  HVACMode.OFF
128  if coordinator_device.state == ApiStateCommand.OFF
129  else HVAC_MODE_SB_TO_HASS[coordinator_device.mode]
130  )
131  self._attr_fan_mode_attr_fan_mode = FAN_SB_TO_HASS[coordinator_device.fan]
132  self._attr_current_temperature_attr_current_temperature = coordinator_device.temperature
133  self._attr_target_temperature_attr_target_temperature = coordinator_device.target_temperature
134 
135  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
136  """Set hvac mode."""
137 
138  if hvac_mode == HVACMode.OFF:
139  await self._operate_operate(power=ApiStateCommand.OFF)
140  else:
141  await self._operate_operate(
142  power=ApiStateCommand.ON, mode=HVAC_MODE_HASS_TO_SB[hvac_mode]
143  )
144 
145  async def async_set_temperature(self, **kwargs: Any) -> None:
146  """Set new target temperature."""
147  await self._operate_operate(target_temperature=kwargs[ATTR_TEMPERATURE])
148 
149  async def async_set_fan_mode(self, fan_mode: str) -> None:
150  """Set AC fan mode."""
151  await self._operate_operate(fan=FAN_HASS_TO_SB[fan_mode])
152 
153  async def _operate(
154  self,
155  power: str | None = None,
156  mode: str | None = None,
157  fan: str | None = None,
158  target_temperature: int | None = None,
159  ) -> None:
160  """Send request to central unit."""
161 
162  if power is None:
163  power = ApiStateCommand.ON
164  if self.hvac_modehvac_modehvac_modehvac_mode == HVACMode.OFF:
165  power = ApiStateCommand.OFF
166  if mode is None:
167  mode = HVAC_MODE_HASS_TO_SB[self.hvac_modehvac_modehvac_modehvac_mode]
168  if fan is None:
169  fan = FAN_HASS_TO_SB[self.fan_modefan_mode]
170  if target_temperature is None:
171  target_temperature = int(self.target_temperaturetarget_temperature or 0)
172 
173  state: dict[str, int | str] = {
174  ApiAttribute.POWER: power,
175  ApiAttribute.MODE: mode,
176  ApiAttribute.FAN: fan,
177  ApiAttribute.CONFIGURED_TEMPERATURE: target_temperature,
178  }
179 
180  try:
181  await self.coordinator.api.set_state(self._device.id, state)
182  except (SwitchBeeError, SwitchBeeDeviceOfflineError) as exp:
183  raise HomeAssistantError(
184  f"Failed to set {self.name} state {state}, error: {exp!s}"
185  ) from exp
186 
187  await self.coordinator.async_refresh()
None _operate(self, str|None power=None, str|None mode=None, str|None fan=None, int|None target_temperature=None)
Definition: climate.py:159
None __init__(self, SwitchBeeThermostat device, SwitchBeeCoordinator coordinator)
Definition: climate.py:99
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:78