Honeywell Lyric - Entity Descriptions (#54956)

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
Aidan Timson 2021-08-25 18:29:34 +01:00 committed by GitHub
parent d4b506e5e4
commit e062d7aec0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 135 additions and 204 deletions

View File

@ -140,18 +140,12 @@ class LyricEntity(CoordinatorEntity):
location: LyricLocation, location: LyricLocation,
device: LyricDevice, device: LyricDevice,
key: str, key: str,
name: str,
icon: str | None,
) -> None: ) -> None:
"""Initialize the Honeywell Lyric entity.""" """Initialize the Honeywell Lyric entity."""
super().__init__(coordinator) super().__init__(coordinator)
self._key = key self._key = key
self._name = name
self._icon = icon
self._location = location self._location = location
self._mac_id = device.macID self._mac_id = device.macID
self._device_name = device.name
self._device_model = device.deviceModel
self._update_thermostat = coordinator.data.update_thermostat self._update_thermostat = coordinator.data.update_thermostat
@property @property
@ -159,16 +153,6 @@ class LyricEntity(CoordinatorEntity):
"""Return the unique ID for this entity.""" """Return the unique ID for this entity."""
return self._key return self._key
@property
def name(self) -> str:
"""Return the name of the entity."""
return self._name
@property
def icon(self) -> str:
"""Return the mdi icon of the entity."""
return self._icon
@property @property
def location(self) -> LyricLocation: def location(self) -> LyricLocation:
"""Get the Lyric Location.""" """Get the Lyric Location."""
@ -189,6 +173,6 @@ class LyricDeviceEntity(LyricEntity):
return { return {
"connections": {(dr.CONNECTION_NETWORK_MAC, self._mac_id)}, "connections": {(dr.CONNECTION_NETWORK_MAC, self._mac_id)},
"manufacturer": "Honeywell", "manufacturer": "Honeywell",
"model": self._device_model, "model": self.device.deviceModel,
"name": self._device_name, "name": self.device.name,
} }

View File

@ -8,7 +8,7 @@ from aiolyric.objects.device import LyricDevice
from aiolyric.objects.location import LyricLocation from aiolyric.objects.location import LyricLocation
import voluptuous as vol import voluptuous as vol
from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate import ClimateEntity, ClimateEntityDescription
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_LOW,
@ -99,7 +99,14 @@ async def async_setup_entry(
for device in location.devices: for device in location.devices:
entities.append( entities.append(
LyricClimate( LyricClimate(
coordinator, location, device, hass.config.units.temperature_unit coordinator,
ClimateEntityDescription(
key=f"{device.macID}_thermostat",
name=device.name,
),
location,
device,
hass.config.units.temperature_unit,
) )
) )
@ -117,9 +124,13 @@ async def async_setup_entry(
class LyricClimate(LyricDeviceEntity, ClimateEntity): class LyricClimate(LyricDeviceEntity, ClimateEntity):
"""Defines a Honeywell Lyric climate entity.""" """Defines a Honeywell Lyric climate entity."""
coordinator: DataUpdateCoordinator
entity_description: ClimateEntityDescription
def __init__( def __init__(
self, self,
coordinator: DataUpdateCoordinator, coordinator: DataUpdateCoordinator,
description: ClimateEntityDescription,
location: LyricLocation, location: LyricLocation,
device: LyricDevice, device: LyricDevice,
temperature_unit: str, temperature_unit: str,
@ -148,9 +159,8 @@ class LyricClimate(LyricDeviceEntity, ClimateEntity):
location, location,
device, device,
f"{device.macID}_thermostat", f"{device.macID}_thermostat",
device.name,
None,
) )
self.entity_description = description
@property @property
def supported_features(self) -> int: def supported_features(self) -> int:

View File

@ -1,10 +1,16 @@
"""Support for Honeywell Lyric sensor platform.""" """Support for Honeywell Lyric sensor platform."""
from dataclasses import dataclass
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Callable, cast
from aiolyric.objects.device import LyricDevice from aiolyric.objects.device import LyricDevice
from aiolyric.objects.location import LyricLocation from aiolyric.objects.location import LyricLocation
from homeassistant.components.sensor import SensorEntity from homeassistant.components.sensor import (
STATE_CLASS_MEASUREMENT,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
@ -12,6 +18,7 @@ from homeassistant.const import (
DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_TIMESTAMP,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
@ -33,6 +40,22 @@ LYRIC_SETPOINT_STATUS_NAMES = {
} }
@dataclass
class LyricSensorEntityDescription(SensorEntityDescription):
"""Class describing Honeywell Lyric sensor entities."""
value: Callable[[LyricDevice], StateType] = round
def get_datetime_from_future_time(time: str) -> datetime:
"""Get datetime from future time provided."""
time = dt_util.parse_time(time)
now = dt_util.utcnow()
if time <= now.time():
now = now + timedelta(days=1)
return dt_util.as_utc(datetime.combine(now.date(), time))
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities
) -> None: ) -> None:
@ -41,27 +64,93 @@ async def async_setup_entry(
entities = [] entities = []
def get_setpoint_status(status: str, time: str) -> str:
if status == PRESET_HOLD_UNTIL:
return f"Held until {time}"
return LYRIC_SETPOINT_STATUS_NAMES.get(status, None)
for location in coordinator.data.locations: for location in coordinator.data.locations:
for device in location.devices: for device in location.devices:
cls_list = []
if device.indoorTemperature: if device.indoorTemperature:
cls_list.append(LyricIndoorTemperatureSensor)
if device.outdoorTemperature:
cls_list.append(LyricOutdoorTemperatureSensor)
if device.displayedOutdoorHumidity:
cls_list.append(LyricOutdoorHumiditySensor)
if device.changeableValues:
if device.changeableValues.nextPeriodTime:
cls_list.append(LyricNextPeriodSensor)
if device.changeableValues.thermostatSetpointStatus:
cls_list.append(LyricSetpointStatusSensor)
for cls in cls_list:
entities.append( entities.append(
cls( LyricSensor(
coordinator, coordinator,
LyricSensorEntityDescription(
key=f"{device.macID}_indoor_temperature",
name="Indoor Temperature",
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
native_unit_of_measurement=hass.config.units.temperature_unit,
value=lambda device: device.indoorTemperature,
),
location,
device,
)
)
if device.outdoorTemperature:
entities.append(
LyricSensor(
coordinator,
LyricSensorEntityDescription(
key=f"{device.macID}_outdoor_temperature",
name="Outdoor Temperature",
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
native_unit_of_measurement=hass.config.units.temperature_unit,
value=lambda device: device.outdoorTemperature,
),
location,
device,
)
)
if device.displayedOutdoorHumidity:
entities.append(
LyricSensor(
coordinator,
LyricSensorEntityDescription(
key=f"{device.macID}_outdoor_humidity",
name="Outdoor Humidity",
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
native_unit_of_measurement="%",
value=lambda device: device.displayedOutdoorHumidity,
),
location,
device,
)
)
if device.changeableValues:
if device.changeableValues.nextPeriodTime:
entities.append(
LyricSensor(
coordinator,
LyricSensorEntityDescription(
key=f"{device.macID}_next_period_time",
name="Next Period Time",
device_class=DEVICE_CLASS_TIMESTAMP,
value=lambda device: get_datetime_from_future_time(
device.changeableValues.nextPeriodTime
).isoformat(),
),
location,
device,
)
)
if device.changeableValues.thermostatSetpointStatus:
entities.append(
LyricSensor(
coordinator,
LyricSensorEntityDescription(
key=f"{device.macID}_setpoint_status",
name="Setpoint Status",
icon="mdi:thermostat",
value=lambda device: get_setpoint_status(
device.changeableValues.thermostatSetpointStatus,
device.changeableValues.nextPeriodTime,
),
),
location, location,
device, device,
hass.config.units.temperature_unit,
) )
) )
@ -69,184 +158,32 @@ async def async_setup_entry(
class LyricSensor(LyricDeviceEntity, SensorEntity): class LyricSensor(LyricDeviceEntity, SensorEntity):
"""Defines a Honeywell Lyric sensor.""" """Define a Honeywell Lyric sensor."""
coordinator: DataUpdateCoordinator
entity_description: LyricSensorEntityDescription
def __init__( def __init__(
self, self,
coordinator: DataUpdateCoordinator, coordinator: DataUpdateCoordinator,
description: LyricSensorEntityDescription,
location: LyricLocation, location: LyricLocation,
device: LyricDevice, device: LyricDevice,
key: str,
name: str,
icon: str,
device_class: str = None,
unit_of_measurement: str = None,
) -> None: ) -> None:
"""Initialize Honeywell Lyric sensor.""" """Initialize."""
self._device_class = device_class
self._unit_of_measurement = unit_of_measurement
super().__init__(coordinator, location, device, key, name, icon)
@property
def device_class(self) -> str:
"""Return the device class of the sensor."""
return self._device_class
@property
def native_unit_of_measurement(self) -> str:
"""Return the unit this state is expressed in."""
return self._unit_of_measurement
class LyricIndoorTemperatureSensor(LyricSensor):
"""Defines a Honeywell Lyric sensor."""
def __init__(
self,
coordinator: DataUpdateCoordinator,
location: LyricLocation,
device: LyricDevice,
unit_of_measurement: str = None,
) -> None:
"""Initialize Honeywell Lyric sensor."""
super().__init__( super().__init__(
coordinator, coordinator,
location, location,
device, device,
f"{device.macID}_indoor_temperature", description.key,
"Indoor Temperature",
None,
DEVICE_CLASS_TEMPERATURE,
unit_of_measurement,
) )
self.entity_description = description
@property @property
def native_value(self) -> str: def native_value(self) -> StateType:
"""Return the state of the sensor.""" """Return the state."""
return self.device.indoorTemperature device: LyricDevice = self.device
try:
return cast(StateType, self.entity_description.value(device))
class LyricOutdoorTemperatureSensor(LyricSensor): except TypeError:
"""Defines a Honeywell Lyric sensor.""" return None
def __init__(
self,
coordinator: DataUpdateCoordinator,
location: LyricLocation,
device: LyricDevice,
unit_of_measurement: str = None,
) -> None:
"""Initialize Honeywell Lyric sensor."""
super().__init__(
coordinator,
location,
device,
f"{device.macID}_outdoor_temperature",
"Outdoor Temperature",
None,
DEVICE_CLASS_TEMPERATURE,
unit_of_measurement,
)
@property
def native_value(self) -> str:
"""Return the state of the sensor."""
return self.device.outdoorTemperature
class LyricOutdoorHumiditySensor(LyricSensor):
"""Defines a Honeywell Lyric sensor."""
def __init__(
self,
coordinator: DataUpdateCoordinator,
location: LyricLocation,
device: LyricDevice,
unit_of_measurement: str = None,
) -> None:
"""Initialize Honeywell Lyric sensor."""
super().__init__(
coordinator,
location,
device,
f"{device.macID}_outdoor_humidity",
"Outdoor Humidity",
None,
DEVICE_CLASS_HUMIDITY,
"%",
)
@property
def native_value(self) -> str:
"""Return the state of the sensor."""
return self.device.displayedOutdoorHumidity
class LyricNextPeriodSensor(LyricSensor):
"""Defines a Honeywell Lyric sensor."""
def __init__(
self,
coordinator: DataUpdateCoordinator,
location: LyricLocation,
device: LyricDevice,
unit_of_measurement: str = None,
) -> None:
"""Initialize Honeywell Lyric sensor."""
super().__init__(
coordinator,
location,
device,
f"{device.macID}_next_period_time",
"Next Period Time",
None,
DEVICE_CLASS_TIMESTAMP,
)
@property
def native_value(self) -> datetime:
"""Return the state of the sensor."""
device = self.device
time = dt_util.parse_time(device.changeableValues.nextPeriodTime)
now = dt_util.utcnow()
if time <= now.time():
now = now + timedelta(days=1)
return dt_util.as_utc(datetime.combine(now.date(), time))
class LyricSetpointStatusSensor(LyricSensor):
"""Defines a Honeywell Lyric sensor."""
def __init__(
self,
coordinator: DataUpdateCoordinator,
location: LyricLocation,
device: LyricDevice,
unit_of_measurement: str = None,
) -> None:
"""Initialize Honeywell Lyric sensor."""
super().__init__(
coordinator,
location,
device,
f"{device.macID}_setpoint_status",
"Setpoint Status",
"mdi:thermostat",
None,
)
@property
def native_value(self) -> str:
"""Return the state of the sensor."""
device = self.device
if device.changeableValues.thermostatSetpointStatus == PRESET_HOLD_UNTIL:
return f"Held until {device.changeableValues.nextPeriodTime}"
return LYRIC_SETPOINT_STATUS_NAMES.get(
device.changeableValues.thermostatSetpointStatus, "Unknown"
)