"""The Honeywell Lyric integration.""" from __future__ import annotations from datetime import timedelta import logging from aiohttp.client_exceptions import ClientResponseError from aiolyric import Lyric from aiolyric.exceptions import LyricAuthenticationException, LyricException from aiolyric.objects.device import LyricDevice from aiolyric.objects.location import LyricLocation import async_timeout import voluptuous as vol from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers import ( aiohttp_client, config_entry_oauth2_flow, config_validation as cv, device_registry as dr, ) from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, UpdateFailed, ) from .api import ConfigEntryLyricClient, LyricLocalOAuth2Implementation from .config_flow import OAuth2FlowHandler from .const import DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.Schema( { vol.Required(CONF_CLIENT_ID): cv.string, vol.Required(CONF_CLIENT_SECRET): cv.string, } ) }, extra=vol.ALLOW_EXTRA, ) _LOGGER = logging.getLogger(__name__) PLATFORMS = ["climate", "sensor"] async def async_setup(hass: HomeAssistant, config: dict): """Set up the Honeywell Lyric component.""" hass.data[DOMAIN] = {} if DOMAIN not in config: return True hass.data[DOMAIN][CONF_CLIENT_ID] = config[DOMAIN][CONF_CLIENT_ID] OAuth2FlowHandler.async_register_implementation( hass, LyricLocalOAuth2Implementation( hass, DOMAIN, config[DOMAIN][CONF_CLIENT_ID], config[DOMAIN][CONF_CLIENT_SECRET], OAUTH2_AUTHORIZE, OAUTH2_TOKEN, ), ) return True async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Honeywell Lyric from a config entry.""" implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( hass, entry ) ) session = aiohttp_client.async_get_clientsession(hass) oauth_session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) client = ConfigEntryLyricClient(session, oauth_session) client_id = hass.data[DOMAIN][CONF_CLIENT_ID] lyric = Lyric(client, client_id) async def async_update_data() -> Lyric: """Fetch data from Lyric.""" try: async with async_timeout.timeout(60): await lyric.get_locations() return lyric except LyricAuthenticationException as exception: raise ConfigEntryAuthFailed from exception except (LyricException, ClientResponseError) as exception: raise UpdateFailed(exception) from exception coordinator = DataUpdateCoordinator( hass, _LOGGER, # Name of the data. For logging purposes. name="lyric_coordinator", update_method=async_update_data, # Polling interval. Will only be polled if there are subscribers. update_interval=timedelta(seconds=120), ) hass.data[DOMAIN][entry.entry_id] = coordinator # Fetch initial data so we have data when entities subscribe await coordinator.async_config_entry_first_refresh() hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) if unload_ok: hass.data[DOMAIN].pop(entry.entry_id) return unload_ok class LyricEntity(CoordinatorEntity): """Defines a base Honeywell Lyric entity.""" def __init__( self, coordinator: DataUpdateCoordinator, location: LyricLocation, device: LyricDevice, key: str, name: str, icon: str | None, ) -> None: """Initialize the Honeywell Lyric entity.""" super().__init__(coordinator) self._key = key self._name = name self._icon = icon self._location = location self._mac_id = device.macID self._device_name = device.name self._device_model = device.deviceModel self._update_thermostat = coordinator.data.update_thermostat @property def unique_id(self) -> str: """Return the unique ID for this entity.""" 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 def location(self) -> LyricLocation: """Get the Lyric Location.""" return self.coordinator.data.locations_dict[self._location.locationID] @property def device(self) -> LyricDevice: """Get the Lyric Device.""" return self.location.devices_dict[self._mac_id] class LyricDeviceEntity(LyricEntity): """Defines a Honeywell Lyric device entity.""" @property def device_info(self) -> DeviceInfo: """Return device information about this Honeywell Lyric instance.""" return { "connections": {(dr.CONNECTION_NETWORK_MAC, self._mac_id)}, "manufacturer": "Honeywell", "model": self._device_model, "name": self._device_name, }