1 """Support for Honeywell Lyric climate platform."""
3 from __future__
import annotations
8 from time
import localtime, strftime, time
11 from aiolyric
import Lyric
12 from aiolyric.objects.device
import LyricDevice
13 from aiolyric.objects.location
import LyricLocation
14 import voluptuous
as vol
17 ATTR_TARGET_TEMP_HIGH,
23 ClimateEntityDescription,
48 PRESET_PERMANENT_HOLD,
49 PRESET_TEMPORARY_HOLD,
52 from .entity
import LyricDeviceEntity
54 _LOGGER = logging.getLogger(__name__)
58 ClimateEntityFeature.TARGET_TEMPERATURE
59 | ClimateEntityFeature.PRESET_MODE
60 | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
63 ClimateEntityFeature.TARGET_TEMPERATURE
64 | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
67 LYRIC_HVAC_ACTION_OFF =
"EquipmentOff"
68 LYRIC_HVAC_ACTION_HEAT =
"Heat"
69 LYRIC_HVAC_ACTION_COOL =
"Cool"
71 LYRIC_HVAC_MODE_OFF =
"Off"
72 LYRIC_HVAC_MODE_HEAT =
"Heat"
73 LYRIC_HVAC_MODE_COOL =
"Cool"
74 LYRIC_HVAC_MODE_HEAT_COOL =
"Auto"
76 LYRIC_FAN_MODE_ON =
"On"
77 LYRIC_FAN_MODE_AUTO =
"Auto"
78 LYRIC_FAN_MODE_DIFFUSE =
"Circulate"
81 HVACMode.OFF: LYRIC_HVAC_MODE_OFF,
82 HVACMode.HEAT: LYRIC_HVAC_MODE_HEAT,
83 HVACMode.COOL: LYRIC_HVAC_MODE_COOL,
84 HVACMode.HEAT_COOL: LYRIC_HVAC_MODE_HEAT_COOL,
88 LYRIC_HVAC_MODE_OFF: HVACMode.OFF,
89 LYRIC_HVAC_MODE_HEAT: HVACMode.HEAT,
90 LYRIC_HVAC_MODE_COOL: HVACMode.COOL,
91 LYRIC_HVAC_MODE_HEAT_COOL: HVACMode.HEAT_COOL,
95 FAN_ON: LYRIC_FAN_MODE_ON,
96 FAN_AUTO: LYRIC_FAN_MODE_AUTO,
97 FAN_DIFFUSE: LYRIC_FAN_MODE_DIFFUSE,
101 LYRIC_FAN_MODE_ON: FAN_ON,
102 LYRIC_FAN_MODE_AUTO: FAN_AUTO,
103 LYRIC_FAN_MODE_DIFFUSE: FAN_DIFFUSE,
107 LYRIC_HVAC_ACTION_OFF: HVACAction.OFF,
108 LYRIC_HVAC_ACTION_HEAT: HVACAction.HEATING,
109 LYRIC_HVAC_ACTION_COOL: HVACAction.COOLING,
112 SERVICE_HOLD_TIME =
"set_hold_time"
113 ATTR_TIME_PERIOD =
"time_period"
115 SCHEMA_HOLD_TIME: VolDictType = {
116 vol.Required(ATTR_TIME_PERIOD, default=
"01:00:00"): vol.All(
118 cv.positive_timedelta,
119 lambda td: strftime(
"%H:%M:%S", localtime(
time() + td.total_seconds())),
125 hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
127 """Set up the Honeywell Lyric climate platform based on a config entry."""
128 coordinator: DataUpdateCoordinator[Lyric] = hass.data[DOMAIN][entry.entry_id]
135 key=f
"{device.mac_id}_thermostat",
141 for location
in coordinator.data.locations
142 for device
in location.devices
147 platform = entity_platform.async_get_current_platform()
149 platform.async_register_entity_service(
152 "async_set_hold_time",
157 """Lyric thermostats are classified as TCC or LCC devices."""
164 """Defines a Honeywell Lyric climate entity."""
166 coordinator: DataUpdateCoordinator[Lyric]
167 entity_description: ClimateEntityDescription
170 _attr_preset_modes = [
173 PRESET_PERMANENT_HOLD,
174 PRESET_TEMPORARY_HOLD,
175 PRESET_VACATION_HOLD,
177 _enable_turn_on_off_backwards_compatibility =
False
181 coordinator: DataUpdateCoordinator[Lyric],
182 description: ClimateEntityDescription,
183 location: LyricLocation,
186 """Initialize Honeywell Lyric climate entity."""
188 if device.changeable_values.thermostat_setpoint_status:
194 if device.units ==
"Fahrenheit":
205 if LYRIC_HVAC_MODE_HEAT
in device.allowed_modes:
208 if LYRIC_HVAC_MODE_COOL
in device.allowed_modes:
213 if LYRIC_HVAC_MODE_HEAT_COOL
in device.allowed_modes
or (
215 and LYRIC_HVAC_MODE_HEAT
in device.allowed_modes
216 and LYRIC_HVAC_MODE_COOL
in device.allowed_modes
227 if device_fan_modes := device.settings.attributes.get(
"fan", {}).
get(
231 FAN_MODES[device_fan_mode]
232 for device_fan_mode
in device_fan_modes
233 if device_fan_mode
in FAN_MODES
241 ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
248 f
"{device.mac_id}_thermostat",
254 """Return the current temperature."""
255 return self.
devicedevice.indoor_temperature
259 """Return the current hvac action."""
260 action = HVAC_ACTIONS.get(self.
devicedevice.operation_status.mode,
None)
262 action = HVACAction.IDLE
267 """Return the hvac mode."""
268 return HVAC_MODES[self.
devicedevice.changeable_values.mode]
272 """Return the temperature we try to reach."""
273 device = self.
devicedevice
275 device.changeable_values.auto_changeover_active
276 or HVAC_MODES[device.changeable_values.mode] == HVACMode.OFF
280 return device.changeable_values.cool_setpoint
281 return device.changeable_values.heat_setpoint
285 """Return the highbound target temperature we try to reach."""
286 device = self.
devicedevice
288 not device.changeable_values.auto_changeover_active
289 or HVAC_MODES[device.changeable_values.mode] == HVACMode.OFF
292 return device.changeable_values.cool_setpoint
296 """Return the lowbound target temperature we try to reach."""
297 device = self.
devicedevice
299 not device.changeable_values.auto_changeover_active
300 or HVAC_MODES[device.changeable_values.mode] == HVACMode.OFF
303 return device.changeable_values.heat_setpoint
307 """Return current preset mode."""
308 return self.
devicedevice.changeable_values.thermostat_setpoint_status
312 """Identify min_temp in Lyric API or defaults if not available."""
313 device = self.
devicedevice
314 if LYRIC_HVAC_MODE_COOL
in device.allowed_modes:
315 return device.min_cool_setpoint
316 return device.min_heat_setpoint
320 """Identify max_temp in Lyric API or defaults if not available."""
321 device = self.
devicedevice
322 if LYRIC_HVAC_MODE_HEAT
in device.allowed_modes:
323 return device.max_heat_setpoint
324 return device.max_cool_setpoint
328 """Return current fan mode."""
329 device = self.
devicedevice
330 return FAN_MODES.get(
331 device.settings.attributes.get(
"fan", {})
332 .
get(
"changeableValues", {})
337 """Set new target temperature."""
341 device = self.
devicedevice
342 target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
343 target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
345 if device.changeable_values.mode == LYRIC_HVAC_MODE_HEAT_COOL:
346 if target_temp_low
is None or target_temp_high
is None:
348 "Could not find target_temp_low and/or target_temp_high in"
355 mode = HVAC_MODES[device.changeable_values.heat_cool_mode]
359 _LOGGER.debug(
"Set temperature: %s - %s", target_temp_low, target_temp_high)
364 cool_setpoint=target_temp_high,
365 heat_setpoint=target_temp_low,
368 except LYRIC_EXCEPTIONS
as exception:
369 _LOGGER.error(exception)
372 temp = kwargs.get(ATTR_TEMPERATURE)
373 _LOGGER.debug(
"Set temperature: %s", temp)
377 self.
locationlocation, device, cool_setpoint=temp
381 self.
locationlocation, device, heat_setpoint=temp
383 except LYRIC_EXCEPTIONS
as exception:
384 _LOGGER.error(exception)
389 _LOGGER.debug(
"HVAC mode: %s", hvac_mode)
392 case LyricThermostatType.TCC:
394 case LyricThermostatType.LCC:
396 except LYRIC_EXCEPTIONS
as exception:
397 _LOGGER.error(exception)
401 """Set hvac mode for TCC devices (e.g., Lyric round)."""
402 if LYRIC_HVAC_MODES[hvac_mode] == LYRIC_HVAC_MODE_HEAT_COOL:
407 if HVAC_MODES[self.
devicedevice.changeable_values.mode] == HVACMode.OFF:
409 "HVAC mode passed to lyric: %s",
410 HVAC_MODES[LYRIC_HVAC_MODE_COOL],
415 mode=HVAC_MODES[LYRIC_HVAC_MODE_HEAT],
416 auto_changeover_active=
False,
419 await asyncio.sleep(3)
421 "HVAC mode passed to lyric: %s",
422 HVAC_MODES[LYRIC_HVAC_MODE_HEAT],
427 mode=HVAC_MODES[LYRIC_HVAC_MODE_HEAT],
428 auto_changeover_active=
True,
432 "HVAC mode passed to lyric: %s",
433 HVAC_MODES[self.
devicedevice.changeable_values.mode],
436 self.
locationlocation, self.
devicedevice, auto_changeover_active=
True
439 _LOGGER.debug(
"HVAC mode passed to lyric: %s", LYRIC_HVAC_MODES[hvac_mode])
443 mode=LYRIC_HVAC_MODES[hvac_mode],
444 auto_changeover_active=
False,
448 """Set hvac mode for LCC devices (e.g., T5,6)."""
449 _LOGGER.debug(
"HVAC mode passed to lyric: %s", LYRIC_HVAC_MODES[hvac_mode])
453 LYRIC_HVAC_MODES[hvac_mode] == LYRIC_HVAC_MODE_HEAT_COOL
454 and not self.
devicedevice.changeable_values.auto_changeover_active
456 auto_changeover =
True
458 auto_changeover =
None
463 mode=LYRIC_HVAC_MODES[hvac_mode],
464 auto_changeover_active=auto_changeover,
468 """Set preset (PermanentHold, HoldUntil, NoHold, VacationHold) mode."""
469 _LOGGER.debug(
"Set preset mode: %s", preset_mode)
472 self.
locationlocation, self.
devicedevice, thermostat_setpoint_status=preset_mode
474 except LYRIC_EXCEPTIONS
as exception:
475 _LOGGER.error(exception)
479 """Set the time to hold until."""
480 _LOGGER.debug(
"set_hold_time: %s", time_period)
485 thermostat_setpoint_status=PRESET_HOLD_UNTIL,
486 next_period_time=time_period,
488 except LYRIC_EXCEPTIONS
as exception:
489 _LOGGER.error(exception)
494 _LOGGER.debug(
"Set fan mode: %s", fan_mode)
496 _LOGGER.debug(
"Fan mode passed to lyric: %s", LYRIC_FAN_MODES[fan_mode])
498 self.
locationlocation, self.
devicedevice, mode=LYRIC_FAN_MODES[fan_mode]
500 except LYRIC_EXCEPTIONS
as exception:
501 _LOGGER.error(exception)
504 "The fan mode requested does not have a corresponding mode in lyric: %s",
HVACMode|None hvac_mode(self)
list[HVACMode] hvac_modes(self)
str|None preset_mode(self)
None async_set_temperature(self, **Any kwargs)
None async_set_preset_mode(self, str preset_mode)
None async_set_hvac_mode(self, HVACMode hvac_mode)
float|None target_temperature(self)
None _async_set_hvac_mode_tcc(self, HVACMode hvac_mode)
float|None target_temperature_high(self)
None async_set_fan_mode(self, str fan_mode)
None async_set_hold_time(self, str time_period)
HVACAction|None hvac_action(self)
float|None target_temperature_low(self)
None _async_set_hvac_mode_lcc(self, HVACMode hvac_mode)
None __init__(self, DataUpdateCoordinator[Lyric] coordinator, ClimateEntityDescription description, LyricLocation location, LyricDevice device)
float|None current_temperature(self)
LyricLocation location(self)
web.Response get(self, web.Request request, str config_key)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
bool time(HomeAssistant hass, dt_time|str|None before=None, dt_time|str|None after=None, str|Container[str]|None weekday=None)