1 """Support for Netatmo Smart thermostats."""
3 from __future__
import annotations
6 from typing
import Any, cast
8 from pyatmo.modules
import NATherm1
9 from pyatmo.modules.device_types
import DeviceType
10 import voluptuous
as vol
38 ATTR_HEATING_POWER_REQUEST,
40 ATTR_SELECTED_SCHEDULE,
41 ATTR_TARGET_TEMPERATURE,
45 EVENT_TYPE_CANCEL_SET_POINT,
48 EVENT_TYPE_THERM_MODE,
49 NETATMO_CREATE_CLIMATE,
50 SERVICE_CLEAR_TEMPERATURE_SETTING,
51 SERVICE_SET_PRESET_MODE_WITH_END_DATETIME,
53 SERVICE_SET_TEMPERATURE_WITH_END_DATETIME,
54 SERVICE_SET_TEMPERATURE_WITH_TIME_PERIOD,
56 from .data_handler
import HOME, SIGNAL_NAME, NetatmoRoom
57 from .entity
import NetatmoRoomEntity
59 _LOGGER = logging.getLogger(__name__)
61 PRESET_FROST_GUARD =
"frost_guard"
62 PRESET_SCHEDULE =
"schedule"
63 PRESET_MANUAL =
"manual"
66 ClimateEntityFeature.TARGET_TEMPERATURE
67 | ClimateEntityFeature.PRESET_MODE
68 | ClimateEntityFeature.TURN_OFF
69 | ClimateEntityFeature.TURN_ON
71 SUPPORT_PRESET = [PRESET_AWAY, PRESET_BOOST, PRESET_FROST_GUARD, PRESET_SCHEDULE]
73 THERM_MODES = (PRESET_SCHEDULE, PRESET_FROST_GUARD, PRESET_AWAY)
75 STATE_NETATMO_SCHEDULE =
"schedule"
76 STATE_NETATMO_HG =
"hg"
77 STATE_NETATMO_MAX =
"max"
78 STATE_NETATMO_AWAY = PRESET_AWAY
79 STATE_NETATMO_OFF = STATE_OFF
80 STATE_NETATMO_MANUAL =
"manual"
81 STATE_NETATMO_HOME =
"home"
83 PRESET_MAP_NETATMO = {
84 PRESET_FROST_GUARD: STATE_NETATMO_HG,
85 PRESET_BOOST: STATE_NETATMO_MAX,
86 PRESET_SCHEDULE: STATE_NETATMO_SCHEDULE,
87 PRESET_AWAY: STATE_NETATMO_AWAY,
88 STATE_NETATMO_OFF: STATE_NETATMO_OFF,
91 NETATMO_MAP_PRESET = {
92 STATE_NETATMO_HG: PRESET_FROST_GUARD,
93 STATE_NETATMO_MAX: PRESET_BOOST,
94 STATE_NETATMO_SCHEDULE: PRESET_SCHEDULE,
95 STATE_NETATMO_AWAY: PRESET_AWAY,
96 STATE_NETATMO_OFF: STATE_NETATMO_OFF,
97 STATE_NETATMO_MANUAL: STATE_NETATMO_MANUAL,
98 STATE_NETATMO_HOME: PRESET_SCHEDULE,
102 PRESET_SCHEDULE: HVACMode.AUTO,
103 STATE_NETATMO_HG: HVACMode.AUTO,
104 PRESET_FROST_GUARD: HVACMode.AUTO,
105 PRESET_BOOST: HVACMode.HEAT,
106 STATE_NETATMO_OFF: HVACMode.OFF,
107 STATE_NETATMO_MANUAL: HVACMode.AUTO,
108 PRESET_MANUAL: HVACMode.AUTO,
109 STATE_NETATMO_AWAY: HVACMode.AUTO,
112 CURRENT_HVAC_MAP_NETATMO = {
True: HVACAction.HEATING,
False: HVACAction.IDLE}
114 DEFAULT_MAX_TEMP = 30
116 NA_THERM = DeviceType.NATherm1
117 NA_VALVE = DeviceType.NRV
121 hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
123 """Set up the Netatmo energy platform."""
126 def _create_entity(netatmo_device: NetatmoRoom) ->
None:
127 if not netatmo_device.room.climate_type:
128 msg = f
"No climate type found for this room: {netatmo_device.room.name}"
134 entry.async_on_unload(
138 platform = entity_platform.async_get_current_platform()
139 platform.async_register_entity_service(
140 SERVICE_SET_SCHEDULE,
141 {vol.Required(ATTR_SCHEDULE_NAME): cv.string},
142 "_async_service_set_schedule",
144 platform.async_register_entity_service(
145 SERVICE_SET_PRESET_MODE_WITH_END_DATETIME,
147 vol.Required(ATTR_PRESET_MODE): vol.In(THERM_MODES),
148 vol.Required(ATTR_END_DATETIME): cv.datetime,
150 "_async_service_set_preset_mode_with_end_datetime",
152 platform.async_register_entity_service(
153 SERVICE_SET_TEMPERATURE_WITH_END_DATETIME,
155 vol.Required(ATTR_TARGET_TEMPERATURE): vol.All(
156 vol.Coerce(float), vol.Range(min=7, max=30)
158 vol.Required(ATTR_END_DATETIME): cv.datetime,
160 "_async_service_set_temperature_with_end_datetime",
162 platform.async_register_entity_service(
163 SERVICE_SET_TEMPERATURE_WITH_TIME_PERIOD,
165 vol.Required(ATTR_TARGET_TEMPERATURE): vol.All(
166 vol.Coerce(float), vol.Range(min=7, max=30)
168 vol.Required(ATTR_TIME_PERIOD): vol.All(
170 cv.positive_timedelta,
173 "_async_service_set_temperature_with_time_period",
175 platform.async_register_entity_service(
176 SERVICE_CLEAR_TEMPERATURE_SETTING,
178 "_async_service_clear_temperature_setting",
183 """Representation a Netatmo thermostat."""
185 _attr_hvac_mode = HVACMode.AUTO
186 _attr_max_temp = DEFAULT_MAX_TEMP
187 _attr_preset_modes = SUPPORT_PRESET
188 _attr_supported_features = SUPPORT_FLAGS
189 _attr_target_temperature_step = PRECISION_HALVES
190 _attr_temperature_unit = UnitOfTemperature.CELSIUS
191 _attr_translation_key =
"thermostat"
193 _away: bool |
None =
None
194 _connected: bool |
None =
None
195 _enable_turn_on_off_backwards_compatibility =
False
197 _away_temperature: float |
None =
None
198 _hg_temperature: float |
None =
None
199 _boilerstatus: bool |
None =
None
202 """Initialize the sensor."""
206 self._publishers.extend(
210 "home_id": self.
homehome.entity_id,
225 """Entity created."""
229 EVENT_TYPE_SET_POINT,
230 EVENT_TYPE_THERM_MODE,
231 EVENT_TYPE_CANCEL_SET_POINT,
237 f
"signal-{DOMAIN}-webhook-{event_type}",
244 """Handle webhook events."""
247 if self.
homehome.entity_id != data[
"home_id"]:
250 if data[
"event_type"] == EVENT_TYPE_SCHEDULE
and "schedule_id" in data:
252 self.
hasshass.data[DOMAIN][DATA_SCHEDULES][self.
homehome.entity_id].
get(
267 if self.
homehome.entity_id != home[
"id"]:
270 if data[
"event_type"] == EVENT_TYPE_THERM_MODE:
283 for room
in home.get(
"rooms", []):
285 data[
"event_type"] == EVENT_TYPE_SET_POINT
286 and self.
devicedevice.entity_id == room[
"id"]
288 if room[
"therm_setpoint_mode"] == STATE_NETATMO_OFF:
292 elif room[
"therm_setpoint_mode"] == STATE_NETATMO_MAX:
296 elif room[
"therm_setpoint_mode"] == STATE_NETATMO_MANUAL:
307 data[
"event_type"] == EVENT_TYPE_CANCEL_SET_POINT
308 and self.
devicedevice.entity_id == room[
"id"]
320 """Return the current running hvac operation if supported."""
322 return CURRENT_HVAC_MAP_NETATMO[self.
_boilerstatus_boilerstatus]
325 heating_req := getattr(self.
devicedevice,
"heating_power_request", 0)
326 )
is not None and heating_req > 0:
327 return HVACAction.HEATING
328 return HVACAction.IDLE
331 """Set new target hvac mode."""
332 if hvac_mode == HVACMode.OFF:
334 elif hvac_mode == HVACMode.AUTO:
336 elif hvac_mode == HVACMode.HEAT:
340 """Set new preset mode."""
342 preset_mode
in (PRESET_BOOST, STATE_NETATMO_MAX)
346 await self.
devicedevice.async_therm_set(
350 preset_mode
in (PRESET_BOOST, STATE_NETATMO_MAX)
353 await self.
devicedevice.async_therm_set(
354 STATE_NETATMO_MANUAL,
358 preset_mode
in (PRESET_BOOST, STATE_NETATMO_MAX)
361 await self.
devicedevice.async_therm_set(STATE_NETATMO_HOME)
362 elif preset_mode
in (PRESET_BOOST, STATE_NETATMO_MAX):
363 await self.
devicedevice.async_therm_set(PRESET_MAP_NETATMO[preset_mode])
364 elif preset_mode
in THERM_MODES:
365 await self.
devicedevice.home.async_set_thermmode(PRESET_MAP_NETATMO[preset_mode])
367 _LOGGER.error(
"Preset mode '%s' not available", preset_mode)
372 """Set new target temperature for 2 hours."""
373 await self.
devicedevice.async_therm_set(
374 STATE_NETATMO_MANUAL,
min(kwargs[ATTR_TEMPERATURE], DEFAULT_MAX_TEMP)
379 """Turn the entity off."""
381 await self.
devicedevice.async_therm_set(
382 STATE_NETATMO_MANUAL,
386 await self.
devicedevice.async_therm_set(STATE_NETATMO_OFF)
390 """Turn the entity on."""
391 await self.
devicedevice.async_therm_set(STATE_NETATMO_HOME)
396 """If the device hasn't been able to connect, mark as unavailable."""
401 """Update the entity's state."""
402 if not self.
devicedevice.reachable:
414 getattr(self.
devicedevice,
"therm_setpoint_mode", STATE_NETATMO_SCHEDULE)
420 self.
homehome.get_selected_schedule(),
"name",
None
428 self.
devicedevice.heating_power_request
431 for module
in self.
devicedevice.modules.values():
432 if hasattr(module,
"boiler_status"):
433 module = cast(NATherm1, module)
434 if module.boiler_status
is not None:
439 schedule_name = kwargs.get(ATTR_SCHEDULE_NAME)
441 for sid, schedule
in self.
hasshass.data[DOMAIN][DATA_SCHEDULES][
442 self.
homehome.entity_id
444 if schedule.name == schedule_name:
449 _LOGGER.error(
"%s is not a valid schedule", kwargs.get(ATTR_SCHEDULE_NAME))
452 await self.
homehome.async_switch_schedule(schedule_id=schedule_id)
454 "Setting %s schedule to %s (%s)",
455 self.
homehome.entity_id,
456 kwargs.get(ATTR_SCHEDULE_NAME),
463 preset_mode = kwargs[ATTR_PRESET_MODE]
464 end_datetime = kwargs[ATTR_END_DATETIME]
465 end_timestamp =
int(dt_util.as_timestamp(end_datetime))
467 await self.
homehome.async_set_thermmode(
468 mode=PRESET_MAP_NETATMO[preset_mode], end_time=end_timestamp
471 "Setting %s preset to %s with end datetime %s",
472 self.
homehome.entity_id,
480 target_temperature = kwargs[ATTR_TARGET_TEMPERATURE]
481 end_datetime = kwargs[ATTR_END_DATETIME]
482 end_timestamp =
int(dt_util.as_timestamp(end_datetime))
485 "Setting %s to target temperature %s with end datetime %s",
486 self.
devicedevice.entity_id,
490 await self.
devicedevice.async_therm_manual(target_temperature, end_timestamp)
495 target_temperature = kwargs[ATTR_TARGET_TEMPERATURE]
496 time_period = kwargs[ATTR_TIME_PERIOD]
499 "Setting %s to target temperature %s with time period %s",
500 self.
devicedevice.entity_id,
505 now_timestamp = dt_util.as_timestamp(dt_util.utcnow())
506 end_timestamp =
int(now_timestamp + time_period.seconds)
507 await self.
devicedevice.async_therm_manual(target_temperature, end_timestamp)
510 _LOGGER.debug(
"Clearing %s temperature setting", self.
devicedevice.entity_id)
511 await self.
devicedevice.async_therm_home()
None async_set_preset_mode(self, str preset_mode)
None async_turn_off(self)
None _async_service_set_temperature_with_end_datetime(self, **Any kwargs)
None _async_service_set_schedule(self, **Any kwargs)
None async_set_temperature(self, **Any kwargs)
None async_added_to_hass(self)
None handle_event(self, dict event)
HVACAction hvac_action(self)
None async_set_hvac_mode(self, HVACMode hvac_mode)
None async_update_callback(self)
None __init__(self, NetatmoRoom room)
_attr_current_temperature
None _async_service_set_temperature_with_time_period(self, **Any kwargs)
None _async_service_clear_temperature_setting(self, **Any kwargs)
None async_set_preset_mode(self, str preset_mode)
None async_turn_off(self)
None _async_service_set_preset_mode_with_end_datetime(self, **Any kwargs)
_attr_extra_state_attributes
None async_update_callback(self)
DeviceType device_type(self)
DeviceType device_type(self)
None async_write_ha_state(self)
None async_on_remove(self, CALLBACK_TYPE func)
web.Response get(self, web.Request request, str config_key)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)