Home Assistant Unofficial Reference 2024.12.1
atlantic_pass_apc_heating_zone.py
Go to the documentation of this file.
1 """Support for Atlantic Pass APC Heating Control."""
2 
3 from __future__ import annotations
4 
5 from typing import Any, cast
6 
7 from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState
8 
10  PRESET_AWAY,
11  PRESET_COMFORT,
12  PRESET_ECO,
13  PRESET_HOME,
14  PRESET_SLEEP,
15  ClimateEntity,
16  ClimateEntityFeature,
17  HVACMode,
18 )
19 from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
20 
21 from ..const import DOMAIN
22 from ..coordinator import OverkizDataUpdateCoordinator
23 from ..entity import OverkizEntity
24 
25 OVERKIZ_TO_HVAC_MODE: dict[str, HVACMode] = {
26  OverkizCommandParam.AUTO: HVACMode.AUTO,
27  OverkizCommandParam.ECO: HVACMode.AUTO,
28  OverkizCommandParam.MANU: HVACMode.HEAT,
29  OverkizCommandParam.HEATING: HVACMode.HEAT,
30  OverkizCommandParam.STOP: HVACMode.OFF,
31  OverkizCommandParam.EXTERNAL_SCHEDULING: HVACMode.AUTO,
32  OverkizCommandParam.INTERNAL_SCHEDULING: HVACMode.AUTO,
33  OverkizCommandParam.COMFORT: HVACMode.HEAT,
34 }
35 
36 HVAC_MODE_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_HVAC_MODE.items()}
37 
38 PRESET_EXTERNAL = "external"
39 PRESET_FROST_PROTECTION = "frost_protection"
40 
41 OVERKIZ_TO_PRESET_MODES: dict[str, str] = {
42  OverkizCommandParam.OFF: PRESET_ECO,
43  OverkizCommandParam.STOP: PRESET_ECO,
44  OverkizCommandParam.MANU: PRESET_COMFORT,
45  OverkizCommandParam.COMFORT: PRESET_COMFORT,
46  OverkizCommandParam.ABSENCE: PRESET_AWAY,
47  OverkizCommandParam.ECO: PRESET_ECO,
48  OverkizCommandParam.FROSTPROTECTION: PRESET_FROST_PROTECTION,
49  OverkizCommandParam.EXTERNAL_SCHEDULING: PRESET_EXTERNAL,
50  OverkizCommandParam.INTERNAL_SCHEDULING: PRESET_HOME,
51 }
52 
53 PRESET_MODES_TO_OVERKIZ: dict[str, str] = {
54  PRESET_COMFORT: OverkizCommandParam.COMFORT,
55  PRESET_AWAY: OverkizCommandParam.ABSENCE,
56  PRESET_ECO: OverkizCommandParam.ECO,
57  PRESET_FROST_PROTECTION: OverkizCommandParam.FROSTPROTECTION,
58  PRESET_EXTERNAL: OverkizCommandParam.EXTERNAL_SCHEDULING,
59  PRESET_HOME: OverkizCommandParam.INTERNAL_SCHEDULING,
60 }
61 
62 
63 OVERKIZ_TO_PROFILE_MODES: dict[str, str] = {
64  OverkizCommandParam.OFF: PRESET_SLEEP,
65  OverkizCommandParam.STOP: PRESET_SLEEP,
66  OverkizCommandParam.ECO: PRESET_ECO,
67  OverkizCommandParam.ABSENCE: PRESET_AWAY,
68  OverkizCommandParam.MANU: PRESET_COMFORT,
69  OverkizCommandParam.DEROGATION: PRESET_COMFORT,
70  OverkizCommandParam.EXTERNAL_SETPOINT: PRESET_EXTERNAL,
71  OverkizCommandParam.FROSTPROTECTION: PRESET_FROST_PROTECTION,
72  OverkizCommandParam.COMFORT: PRESET_COMFORT,
73 }
74 
75 OVERKIZ_TEMPERATURE_STATE_BY_PROFILE: dict[str, str] = {
76  OverkizCommandParam.ECO: OverkizState.CORE_ECO_HEATING_TARGET_TEMPERATURE,
77  OverkizCommandParam.COMFORT: OverkizState.CORE_COMFORT_HEATING_TARGET_TEMPERATURE,
78  OverkizCommandParam.DEROGATION: OverkizState.CORE_DEROGATED_TARGET_TEMPERATURE,
79 }
80 
81 
83  """Representation of Atlantic Pass APC Heating Zone Control."""
84 
85  _attr_hvac_modes = [*HVAC_MODE_TO_OVERKIZ]
86  _attr_preset_modes = [*PRESET_MODES_TO_OVERKIZ]
87  _attr_supported_features = (
88  ClimateEntityFeature.TARGET_TEMPERATURE
89  | ClimateEntityFeature.PRESET_MODE
90  | ClimateEntityFeature.TURN_OFF
91  | ClimateEntityFeature.TURN_ON
92  )
93  _attr_temperature_unit = UnitOfTemperature.CELSIUS
94  _attr_translation_key = DOMAIN
95  _enable_turn_on_off_backwards_compatibility = False
96 
97  def __init__(
98  self, device_url: str, coordinator: OverkizDataUpdateCoordinator
99  ) -> None:
100  """Init method."""
101  super().__init__(device_url, coordinator)
102 
103  # Temperature sensor use the same base_device_url and use the n+1 index
104  self.temperature_devicetemperature_device = self.executorexecutor.linked_device(
105  int(self.index_device_urlindex_device_url) + 1
106  )
107 
108  @property
109  def current_temperature(self) -> float | None:
110  """Return the current temperature."""
111  if self.temperature_devicetemperature_device is not None and (
112  temperature := self.temperature_devicetemperature_device.states[OverkizState.CORE_TEMPERATURE]
113  ):
114  return cast(float, temperature.value)
115 
116  return None
117 
118  @property
119  def hvac_mode(self) -> HVACMode:
120  """Return hvac operation ie. heat, cool mode."""
121  return OVERKIZ_TO_HVAC_MODE[
122  cast(str, self.executorexecutor.select_state(OverkizState.IO_PASS_APC_HEATING_MODE))
123  ]
124 
125  @property
126  def current_heating_profile(self) -> str:
127  """Return current heating profile."""
128  return cast(
129  str,
130  self.executorexecutor.select_state(OverkizState.IO_PASS_APC_HEATING_PROFILE),
131  )
132 
133  async def async_set_heating_mode(self, mode: str) -> None:
134  """Set new heating mode and refresh states."""
135  await self.executorexecutor.async_execute_command(
136  OverkizCommand.SET_PASS_APC_HEATING_MODE, mode
137  )
138 
139  if self.current_heating_profilecurrent_heating_profilecurrent_heating_profile == OverkizCommandParam.DEROGATION:
140  # If current mode is in derogation, disable it
141  await self.executorexecutor.async_execute_command(
142  OverkizCommand.SET_DEROGATION_ON_OFF_STATE, OverkizCommandParam.OFF
143  )
144 
145  # We also needs to execute these 2 commands to make it work correctly
146  await self.executorexecutor.async_execute_command(
147  OverkizCommand.REFRESH_PASS_APC_HEATING_MODE
148  )
149  await self.executorexecutor.async_execute_command(
150  OverkizCommand.REFRESH_PASS_APC_HEATING_PROFILE
151  )
152 
153  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
154  """Set new target hvac mode."""
155  await self.async_set_heating_modeasync_set_heating_mode(HVAC_MODE_TO_OVERKIZ[hvac_mode])
156 
157  async def async_set_preset_mode(self, preset_mode: str) -> None:
158  """Set new preset mode."""
159  await self.async_set_heating_modeasync_set_heating_mode(PRESET_MODES_TO_OVERKIZ[preset_mode])
160 
161  @property
162  def preset_mode(self) -> str | None:
163  """Return the current preset mode, e.g., home, away, temp."""
164  heating_mode = cast(
165  str, self.executorexecutor.select_state(OverkizState.IO_PASS_APC_HEATING_MODE)
166  )
167 
168  if heating_mode == OverkizCommandParam.INTERNAL_SCHEDULING:
169  # In Internal scheduling, it could be comfort or eco
170  return OVERKIZ_TO_PROFILE_MODES[
171  cast(
172  str,
173  self.executorexecutor.select_state(
174  OverkizState.IO_PASS_APC_HEATING_PROFILE
175  ),
176  )
177  ]
178 
179  return OVERKIZ_TO_PRESET_MODES[heating_mode]
180 
181  @property
182  def target_temperature(self) -> float | None:
183  """Return hvac target temperature."""
184  current_heating_profile = self.current_heating_profilecurrent_heating_profilecurrent_heating_profile
185  if current_heating_profile in OVERKIZ_TEMPERATURE_STATE_BY_PROFILE:
186  return cast(
187  float,
188  self.executorexecutor.select_state(
189  OVERKIZ_TEMPERATURE_STATE_BY_PROFILE[current_heating_profile]
190  ),
191  )
192  return cast(
193  float, self.executorexecutor.select_state(OverkizState.CORE_TARGET_TEMPERATURE)
194  )
195 
196  async def async_set_temperature(self, **kwargs: Any) -> None:
197  """Set new temperature."""
198  temperature = kwargs[ATTR_TEMPERATURE]
199 
200  if self.hvac_modehvac_modehvac_modehvac_modehvac_mode == HVACMode.AUTO:
201  await self.executorexecutor.async_execute_command(
202  OverkizCommand.SET_COMFORT_HEATING_TARGET_TEMPERATURE,
203  temperature,
204  )
205  await self.executorexecutor.async_execute_command(
206  OverkizCommand.REFRESH_COMFORT_HEATING_TARGET_TEMPERATURE
207  )
208  await self.executorexecutor.async_execute_command(
209  OverkizCommand.REFRESH_TARGET_TEMPERATURE
210  )
211  else:
212  await self.executorexecutor.async_execute_command(
213  OverkizCommand.SET_DEROGATED_TARGET_TEMPERATURE,
214  temperature,
215  )
216  await self.executorexecutor.async_execute_command(
217  OverkizCommand.SET_DEROGATION_ON_OFF_STATE,
218  OverkizCommandParam.ON,
219  )
220  await self.executorexecutor.async_execute_command(
221  OverkizCommand.REFRESH_TARGET_TEMPERATURE
222  )
223  await self.executorexecutor.async_execute_command(
224  OverkizCommand.REFRESH_PASS_APC_HEATING_MODE
225  )
226  await self.executorexecutor.async_execute_command(
227  OverkizCommand.REFRESH_PASS_APC_HEATING_PROFILE
228  )