Home Assistant Unofficial Reference 2024.12.1
hitachi_air_to_air_heat_pump_hlrrwifi.py
Go to the documentation of this file.
1 """Support for HitachiAirToAirHeatPump."""
2 
3 from __future__ import annotations
4 
5 from typing import Any, cast
6 
7 from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState
8 
10  FAN_AUTO,
11  FAN_HIGH,
12  FAN_LOW,
13  FAN_MEDIUM,
14  PRESET_NONE,
15  SWING_BOTH,
16  SWING_HORIZONTAL,
17  SWING_OFF,
18  SWING_VERTICAL,
19  ClimateEntity,
20  ClimateEntityFeature,
21  HVACMode,
22 )
23 from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
24 
25 from ..const import DOMAIN
26 from ..coordinator import OverkizDataUpdateCoordinator
27 from ..entity import OverkizEntity
28 
29 PRESET_HOLIDAY_MODE = "holiday_mode"
30 FAN_SILENT = "silent"
31 FAN_SPEED_STATE = OverkizState.HLRRWIFI_FAN_SPEED
32 LEAVE_HOME_STATE = OverkizState.HLRRWIFI_LEAVE_HOME
33 MAIN_OPERATION_STATE = OverkizState.HLRRWIFI_MAIN_OPERATION
34 MODE_CHANGE_STATE = OverkizState.HLRRWIFI_MODE_CHANGE
35 ROOM_TEMPERATURE_STATE = OverkizState.HLRRWIFI_ROOM_TEMPERATURE
36 SWING_STATE = OverkizState.HLRRWIFI_SWING
37 
38 OVERKIZ_TO_HVAC_MODES: dict[str, HVACMode] = {
39  OverkizCommandParam.AUTOHEATING: HVACMode.AUTO,
40  OverkizCommandParam.AUTOCOOLING: HVACMode.AUTO,
41  OverkizCommandParam.ON: HVACMode.HEAT,
42  OverkizCommandParam.OFF: HVACMode.OFF,
43  OverkizCommandParam.HEATING: HVACMode.HEAT,
44  OverkizCommandParam.FAN: HVACMode.FAN_ONLY,
45  OverkizCommandParam.DEHUMIDIFY: HVACMode.DRY,
46  OverkizCommandParam.COOLING: HVACMode.COOL,
47  OverkizCommandParam.AUTO: HVACMode.AUTO,
48 }
49 
50 HVAC_MODES_TO_OVERKIZ: dict[HVACMode, str] = {
51  HVACMode.AUTO: OverkizCommandParam.AUTO,
52  HVACMode.HEAT: OverkizCommandParam.HEATING,
53  HVACMode.OFF: OverkizCommandParam.AUTO,
54  HVACMode.FAN_ONLY: OverkizCommandParam.FAN,
55  HVACMode.DRY: OverkizCommandParam.DEHUMIDIFY,
56  HVACMode.COOL: OverkizCommandParam.COOLING,
57 }
58 
59 OVERKIZ_TO_SWING_MODES: dict[str, str] = {
60  OverkizCommandParam.BOTH: SWING_BOTH,
61  OverkizCommandParam.HORIZONTAL: SWING_HORIZONTAL,
62  OverkizCommandParam.STOP: SWING_OFF,
63  OverkizCommandParam.VERTICAL: SWING_VERTICAL,
64 }
65 
66 SWING_MODES_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_SWING_MODES.items()}
67 
68 OVERKIZ_TO_FAN_MODES: dict[str, str] = {
69  OverkizCommandParam.AUTO: FAN_AUTO,
70  OverkizCommandParam.HIGH: FAN_HIGH,
71  OverkizCommandParam.LOW: FAN_LOW,
72  OverkizCommandParam.MEDIUM: FAN_MEDIUM,
73  OverkizCommandParam.SILENT: FAN_SILENT,
74 }
75 
76 FAN_MODES_TO_OVERKIZ: dict[str, str] = {
77  FAN_AUTO: OverkizCommandParam.AUTO,
78  FAN_HIGH: OverkizCommandParam.HIGH,
79  FAN_LOW: OverkizCommandParam.LOW,
80  FAN_MEDIUM: OverkizCommandParam.MEDIUM,
81  FAN_SILENT: OverkizCommandParam.SILENT,
82 }
83 
84 
86  """Representation of Hitachi Air To Air HeatPump."""
87 
88  _attr_hvac_modes = [*HVAC_MODES_TO_OVERKIZ]
89  _attr_preset_modes = [PRESET_NONE, PRESET_HOLIDAY_MODE]
90  _attr_swing_modes = [*SWING_MODES_TO_OVERKIZ]
91  _attr_target_temperature_step = 1.0
92  _attr_temperature_unit = UnitOfTemperature.CELSIUS
93  _attr_translation_key = DOMAIN
94  _enable_turn_on_off_backwards_compatibility = False
95 
96  def __init__(
97  self, device_url: str, coordinator: OverkizDataUpdateCoordinator
98  ) -> None:
99  """Init method."""
100  super().__init__(device_url, coordinator)
101 
102  self._attr_supported_features_attr_supported_features = (
103  ClimateEntityFeature.TARGET_TEMPERATURE
104  | ClimateEntityFeature.FAN_MODE
105  | ClimateEntityFeature.PRESET_MODE
106  | ClimateEntityFeature.TURN_OFF
107  | ClimateEntityFeature.TURN_ON
108  )
109 
110  if self.devicedevice.states.get(SWING_STATE):
111  self._attr_supported_features_attr_supported_features |= ClimateEntityFeature.SWING_MODE
112 
113  if self._attr_device_info_attr_device_info:
114  self._attr_device_info_attr_device_info["manufacturer"] = "Hitachi"
115 
116  @property
117  def hvac_mode(self) -> HVACMode:
118  """Return hvac operation ie. heat, cool mode."""
119  if (
120  main_op_state := self.devicedevice.states[MAIN_OPERATION_STATE]
121  ) and main_op_state.value_as_str:
122  if main_op_state.value_as_str.lower() == OverkizCommandParam.OFF:
123  return HVACMode.OFF
124 
125  if (
126  mode_change_state := self.devicedevice.states[MODE_CHANGE_STATE]
127  ) and mode_change_state.value_as_str:
128  sanitized_value = mode_change_state.value_as_str.lower()
129  return OVERKIZ_TO_HVAC_MODES[sanitized_value]
130 
131  return HVACMode.OFF
132 
133  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
134  """Set new target hvac mode."""
135  if hvac_mode == HVACMode.OFF:
136  await self._global_control_global_control(main_operation=OverkizCommandParam.OFF)
137  else:
138  await self._global_control_global_control(
139  main_operation=OverkizCommandParam.ON,
140  hvac_mode=HVAC_MODES_TO_OVERKIZ[hvac_mode],
141  )
142 
143  @property
144  def fan_mode(self) -> str | None:
145  """Return the fan setting."""
146  if (state := self.devicedevice.states[FAN_SPEED_STATE]) and state.value_as_str:
147  return OVERKIZ_TO_FAN_MODES[state.value_as_str]
148 
149  return None
150 
151  @property
152  def fan_modes(self) -> list[str] | None:
153  """Return the list of available fan modes."""
154  return [*FAN_MODES_TO_OVERKIZ]
155 
156  async def async_set_fan_mode(self, fan_mode: str) -> None:
157  """Set new target fan mode."""
158  await self._global_control_global_control(fan_mode=FAN_MODES_TO_OVERKIZ[fan_mode])
159 
160  @property
161  def swing_mode(self) -> str | None:
162  """Return the swing setting."""
163  if (state := self.devicedevice.states[SWING_STATE]) and state.value_as_str:
164  return OVERKIZ_TO_SWING_MODES[state.value_as_str]
165 
166  return None
167 
168  async def async_set_swing_mode(self, swing_mode: str) -> None:
169  """Set new target swing operation."""
170  await self._global_control_global_control(swing_mode=SWING_MODES_TO_OVERKIZ[swing_mode])
171 
172  @property
173  def target_temperature(self) -> int | None:
174  """Return the temperature."""
175  if (
176  temperature := self.devicedevice.states[OverkizState.CORE_TARGET_TEMPERATURE]
177  ) and temperature.value_as_int:
178  return temperature.value_as_int
179 
180  return None
181 
182  @property
183  def current_temperature(self) -> int | None:
184  """Return current temperature."""
185  if (state := self.devicedevice.states[ROOM_TEMPERATURE_STATE]) and state.value_as_int:
186  return state.value_as_int
187 
188  return None
189 
190  async def async_set_temperature(self, **kwargs: Any) -> None:
191  """Set new temperature."""
192  temperature = cast(float, kwargs.get(ATTR_TEMPERATURE))
193  await self._global_control_global_control(target_temperature=int(temperature))
194 
195  @property
196  def preset_mode(self) -> str | None:
197  """Return the current preset mode, e.g., home, away, temp."""
198  if (state := self.devicedevice.states[LEAVE_HOME_STATE]) and state.value_as_str:
199  if state.value_as_str == OverkizCommandParam.ON:
200  return PRESET_HOLIDAY_MODE
201 
202  if state.value_as_str == OverkizCommandParam.OFF:
203  return PRESET_NONE
204 
205  return None
206 
207  async def async_set_preset_mode(self, preset_mode: str) -> None:
208  """Set new preset mode."""
209  if preset_mode == PRESET_HOLIDAY_MODE:
210  await self._global_control_global_control(leave_home=OverkizCommandParam.ON)
211 
212  if preset_mode == PRESET_NONE:
213  await self._global_control_global_control(leave_home=OverkizCommandParam.OFF)
214 
216  self, value: str | None, state_name: str, fallback_value: str
217  ) -> str:
218  """Overkiz doesn't accept commands with undefined parameters. This function is guaranteed to return a `str` which is the provided `value` if set, or the current device state if set, or the provided `fallback_value` otherwise."""
219  if value:
220  return value
221  state = self.devicedevice.states[state_name]
222  if state and state.value_as_str:
223  return state.value_as_str
224  return fallback_value
225 
226  async def _global_control(
227  self,
228  main_operation: str | None = None,
229  target_temperature: int | None = None,
230  fan_mode: str | None = None,
231  hvac_mode: str | None = None,
232  swing_mode: str | None = None,
233  leave_home: str | None = None,
234  ) -> None:
235  """Execute globalControl command with all parameters. There is no option to only set a single parameter, without passing all other values."""
236 
237  main_operation = self._control_backfill_control_backfill(
238  main_operation, MAIN_OPERATION_STATE, OverkizCommandParam.ON
239  )
240  target_temperature = target_temperature or self.target_temperaturetarget_temperaturetarget_temperature
241 
242  fan_mode = self._control_backfill_control_backfill(
243  fan_mode,
244  FAN_SPEED_STATE,
245  OverkizCommandParam.AUTO,
246  )
247  hvac_mode = self._control_backfill_control_backfill(
248  hvac_mode,
249  MODE_CHANGE_STATE,
250  OverkizCommandParam.AUTO,
251  ).lower() # Overkiz can return states that have uppercase characters which are not accepted back as commands
252  if (
253  hvac_mode.replace(" ", "")
254  in [ # Overkiz can return states like 'auto cooling' or 'autoHeating' that are not valid commands and need to be converted to 'auto'
255  OverkizCommandParam.AUTOCOOLING,
256  OverkizCommandParam.AUTOHEATING,
257  ]
258  ):
259  hvac_mode = OverkizCommandParam.AUTO
260 
261  swing_mode = self._control_backfill_control_backfill(
262  swing_mode,
263  SWING_STATE,
264  OverkizCommandParam.STOP,
265  )
266 
267  leave_home = self._control_backfill_control_backfill(
268  leave_home,
269  LEAVE_HOME_STATE,
270  OverkizCommandParam.OFF,
271  )
272 
273  command_data = [
274  main_operation, # Main Operation
275  target_temperature, # Target Temperature
276  fan_mode, # Fan Mode
277  hvac_mode, # Mode
278  swing_mode, # Swing Mode
279  leave_home, # Leave Home
280  ]
281 
282  await self.executorexecutor.async_execute_command(
283  OverkizCommand.GLOBAL_CONTROL, *command_data
284  )
None _global_control(self, str|None main_operation=None, int|None target_temperature=None, str|None fan_mode=None, str|None hvac_mode=None, str|None swing_mode=None, str|None leave_home=None)