Home Assistant Unofficial Reference 2024.12.1
climate.py
Go to the documentation of this file.
1 """Python Control of Nobø Hub - Nobø Energy Control."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from pynobo import nobo
8 
10  ATTR_TARGET_TEMP_HIGH,
11  ATTR_TARGET_TEMP_LOW,
12  PRESET_AWAY,
13  PRESET_COMFORT,
14  PRESET_ECO,
15  PRESET_NONE,
16  ClimateEntity,
17  ClimateEntityFeature,
18  HVACMode,
19 )
20 from homeassistant.config_entries import ConfigEntry
21 from homeassistant.const import ATTR_NAME, PRECISION_TENTHS, UnitOfTemperature
22 from homeassistant.core import HomeAssistant, callback
23 from homeassistant.helpers.device_registry import DeviceInfo
24 from homeassistant.helpers.entity_platform import AddEntitiesCallback
25 from homeassistant.util import dt as dt_util
26 
27 from .const import (
28  ATTR_SERIAL,
29  ATTR_TEMP_COMFORT_C,
30  ATTR_TEMP_ECO_C,
31  CONF_OVERRIDE_TYPE,
32  DOMAIN,
33  OVERRIDE_TYPE_NOW,
34 )
35 
36 SUPPORT_FLAGS = (
37  ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
38 )
39 
40 PRESET_MODES = [PRESET_NONE, PRESET_COMFORT, PRESET_ECO, PRESET_AWAY]
41 
42 MIN_TEMPERATURE = 7
43 MAX_TEMPERATURE = 40
44 
45 
47  hass: HomeAssistant,
48  config_entry: ConfigEntry,
49  async_add_entities: AddEntitiesCallback,
50 ) -> None:
51  """Set up the Nobø Ecohub platform from UI configuration."""
52 
53  # Setup connection with hub
54  hub: nobo = hass.data[DOMAIN][config_entry.entry_id]
55 
56  override_type = (
57  nobo.API.OVERRIDE_TYPE_NOW
58  if config_entry.options.get(CONF_OVERRIDE_TYPE) == OVERRIDE_TYPE_NOW
59  else nobo.API.OVERRIDE_TYPE_CONSTANT
60  )
61 
62  # Add zones as entities
63  async_add_entities(NoboZone(zone_id, hub, override_type) for zone_id in hub.zones)
64 
65 
67  """Representation of a Nobø zone.
68 
69  A Nobø zone consists of a group of physical devices that are
70  controlled as a unity.
71  """
72 
73  _attr_name = None
74  _attr_has_entity_name = True
75  _attr_max_temp = MAX_TEMPERATURE
76  _attr_min_temp = MIN_TEMPERATURE
77  _attr_precision = PRECISION_TENTHS
78  _attr_hvac_modes = [HVACMode.HEAT, HVACMode.AUTO]
79  _attr_hvac_mode = HVACMode.AUTO
80  _attr_preset_modes = PRESET_MODES
81  _attr_supported_features = SUPPORT_FLAGS
82  _attr_temperature_unit = UnitOfTemperature.CELSIUS
83  _attr_target_temperature_step = 1
84  # Need to poll to get preset change when in HVACMode.AUTO, so can't set _attr_should_poll = False
85  _enable_turn_on_off_backwards_compatibility = False
86 
87  def __init__(self, zone_id, hub: nobo, override_type) -> None:
88  """Initialize the climate device."""
89  self._id_id = zone_id
90  self._nobo_nobo = hub
91  self._attr_unique_id_attr_unique_id = f"{hub.hub_serial}:{zone_id}"
92  self._override_type_override_type = override_type
93  self._attr_device_info_attr_device_info = DeviceInfo(
94  identifiers={(DOMAIN, f"{hub.hub_serial}:{zone_id}")},
95  name=hub.zones[zone_id][ATTR_NAME],
96  via_device=(DOMAIN, hub.hub_info[ATTR_SERIAL]),
97  suggested_area=hub.zones[zone_id][ATTR_NAME],
98  )
99  self._read_state_read_state()
100 
101  async def async_added_to_hass(self) -> None:
102  """Register callback from hub."""
103  self._nobo_nobo.register_callback(self._after_update_after_update)
104 
105  async def async_will_remove_from_hass(self) -> None:
106  """Deregister callback from hub."""
107  self._nobo_nobo.deregister_callback(self._after_update_after_update)
108 
109  async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
110  """Set new target HVAC mode, if it's supported."""
111  if hvac_mode not in self.hvac_modeshvac_modes:
112  raise ValueError(
113  f"Zone {self._id} '{self._attr_name}' called with unsupported HVAC mode"
114  f" '{hvac_mode}'"
115  )
116  if hvac_mode == HVACMode.AUTO:
117  await self.async_set_preset_modeasync_set_preset_modeasync_set_preset_mode(PRESET_NONE)
118  elif hvac_mode == HVACMode.HEAT:
119  await self.async_set_preset_modeasync_set_preset_modeasync_set_preset_mode(PRESET_COMFORT)
120 
121  async def async_set_preset_mode(self, preset_mode: str) -> None:
122  """Set new zone override."""
123  if preset_mode == PRESET_ECO:
124  mode = nobo.API.OVERRIDE_MODE_ECO
125  elif preset_mode == PRESET_AWAY:
126  mode = nobo.API.OVERRIDE_MODE_AWAY
127  elif preset_mode == PRESET_COMFORT:
128  mode = nobo.API.OVERRIDE_MODE_COMFORT
129  else: # PRESET_NONE
130  mode = nobo.API.OVERRIDE_MODE_NORMAL
131  await self._nobo_nobo.async_create_override(
132  mode,
133  self._override_type_override_type,
134  nobo.API.OVERRIDE_TARGET_ZONE,
135  self._id_id,
136  )
137 
138  async def async_set_temperature(self, **kwargs: Any) -> None:
139  """Set new target temperature."""
140  if ATTR_TARGET_TEMP_LOW in kwargs:
141  low = round(kwargs[ATTR_TARGET_TEMP_LOW])
142  high = round(kwargs[ATTR_TARGET_TEMP_HIGH])
143  low = min(low, high)
144  high = max(low, high)
145  await self._nobo_nobo.async_update_zone(
146  self._id_id, temp_comfort_c=high, temp_eco_c=low
147  )
148 
149  async def async_update(self) -> None:
150  """Fetch new state data for this zone."""
151  self._read_state_read_state()
152 
153  @callback
154  def _read_state(self) -> None:
155  """Read the current state from the hub. These are only local calls."""
156  state = self._nobo_nobo.get_current_zone_mode(self._id_id, dt_util.now())
157  self._attr_hvac_mode_attr_hvac_mode = HVACMode.AUTO
158  self._attr_preset_mode_attr_preset_mode = PRESET_NONE
159 
160  if state == nobo.API.NAME_OFF:
161  self._attr_hvac_mode_attr_hvac_mode = HVACMode.OFF
162  elif state == nobo.API.NAME_AWAY:
163  self._attr_preset_mode_attr_preset_mode = PRESET_AWAY
164  elif state == nobo.API.NAME_ECO:
165  self._attr_preset_mode_attr_preset_mode = PRESET_ECO
166  elif state == nobo.API.NAME_COMFORT:
167  self._attr_preset_mode_attr_preset_mode = PRESET_COMFORT
168 
169  if self._nobo_nobo.get_zone_override_mode(self._id_id) != nobo.API.NAME_NORMAL:
170  self._attr_hvac_mode_attr_hvac_mode = HVACMode.HEAT
171 
172  current_temperature = self._nobo_nobo.get_current_zone_temperature(self._id_id)
173  self._attr_current_temperature_attr_current_temperature = (
174  None if current_temperature is None else float(current_temperature)
175  )
176  self._attr_target_temperature_high_attr_target_temperature_high = int(
177  self._nobo_nobo.zones[self._id_id][ATTR_TEMP_COMFORT_C]
178  )
179  self._attr_target_temperature_low_attr_target_temperature_low = int(
180  self._nobo_nobo.zones[self._id_id][ATTR_TEMP_ECO_C]
181  )
182 
183  @callback
184  def _after_update(self, hub):
185  self._read_state_read_state()
186  self.async_write_ha_stateasync_write_ha_state()
None async_set_preset_mode(self, str preset_mode)
Definition: __init__.py:861
None async_set_hvac_mode(self, HVACMode hvac_mode)
Definition: climate.py:109
None async_set_preset_mode(self, str preset_mode)
Definition: climate.py:121
None async_set_temperature(self, **Any kwargs)
Definition: climate.py:138
None __init__(self, zone_id, nobo hub, override_type)
Definition: climate.py:87
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: climate.py:50