diff --git a/homeassistant/components/abode/__init__.py b/homeassistant/components/abode/__init__.py index 525fc60e9307..195f501fb242 100644 --- a/homeassistant/components/abode/__init__.py +++ b/homeassistant/components/abode/__init__.py @@ -30,7 +30,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import config_validation as cv from homeassistant.helpers.typing import ConfigType -from .const import CONF_POLLING, DOMAIN, DOMAIN_DATA, LOGGER +from .const import CONF_POLLING, DOMAIN, LOGGER from .services import async_setup_services ATTR_DEVICE_NAME = "device_name" @@ -67,13 +67,16 @@ class AbodeSystem: logout_listener: CALLBACK_TYPE | None = None +type AbodeConfigEntry = ConfigEntry[AbodeSystem] + + async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Abode component.""" async_setup_services(hass) return True -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_setup_entry(hass: HomeAssistant, entry: AbodeConfigEntry) -> bool: """Set up Abode integration from a config entry.""" username = entry.data[CONF_USERNAME] password = entry.data[CONF_PASSWORD] @@ -99,50 +102,54 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except (AbodeException, ConnectTimeout, HTTPError) as ex: raise ConfigEntryNotReady(f"Unable to connect to Abode: {ex}") from ex - hass.data[DOMAIN_DATA] = AbodeSystem(abode, polling) + entry.runtime_data = AbodeSystem(abode, polling) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - await setup_hass_events(hass) - await hass.async_add_executor_job(setup_abode_events, hass) + await setup_hass_events(hass, entry) + await hass.async_add_executor_job(setup_abode_events, hass, entry) return True -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +def _shutdown_client(abode: Abode) -> None: + """Shutdown client.""" + abode.events.stop() + abode.logout() + + +async def async_unload_entry(hass: HomeAssistant, entry: AbodeConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) - await hass.async_add_executor_job(hass.data[DOMAIN_DATA].abode.events.stop) - await hass.async_add_executor_job(hass.data[DOMAIN_DATA].abode.logout) + await hass.async_add_executor_job(_shutdown_client, entry.runtime_data.abode) - if logout_listener := hass.data[DOMAIN_DATA].logout_listener: + if logout_listener := entry.runtime_data.logout_listener: logout_listener() - hass.data.pop(DOMAIN_DATA) return unload_ok -async def setup_hass_events(hass: HomeAssistant) -> None: +async def setup_hass_events(hass: HomeAssistant, entry: AbodeConfigEntry) -> None: """Home Assistant start and stop callbacks.""" def logout(event: Event) -> None: """Logout of Abode.""" - if not hass.data[DOMAIN_DATA].polling: - hass.data[DOMAIN_DATA].abode.events.stop() + if not entry.runtime_data.polling: + entry.runtime_data.abode.events.stop() - hass.data[DOMAIN_DATA].abode.logout() + entry.runtime_data.abode.logout() LOGGER.info("Logged out of Abode") - if not hass.data[DOMAIN_DATA].polling: - await hass.async_add_executor_job(hass.data[DOMAIN_DATA].abode.events.start) + if not entry.runtime_data.polling: + await hass.async_add_executor_job(entry.runtime_data.abode.events.start) - hass.data[DOMAIN_DATA].logout_listener = hass.bus.async_listen_once( + entry.runtime_data.logout_listener = hass.bus.async_listen_once( EVENT_HOMEASSISTANT_STOP, logout ) -def setup_abode_events(hass: HomeAssistant) -> None: +def setup_abode_events(hass: HomeAssistant, entry: AbodeConfigEntry) -> None: """Event callbacks.""" def event_callback(event: str, event_json: dict[str, str]) -> None: @@ -179,6 +186,6 @@ def setup_abode_events(hass: HomeAssistant) -> None: ] for event in events: - hass.data[DOMAIN_DATA].abode.events.add_event_callback( + entry.runtime_data.abode.events.add_event_callback( event, partial(event_callback, event) ) diff --git a/homeassistant/components/abode/alarm_control_panel.py b/homeassistant/components/abode/alarm_control_panel.py index 161ef315b806..327a63badaa8 100644 --- a/homeassistant/components/abode/alarm_control_panel.py +++ b/homeassistant/components/abode/alarm_control_panel.py @@ -9,21 +9,20 @@ from homeassistant.components.alarm_control_panel import ( AlarmControlPanelEntityFeature, AlarmControlPanelState, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import DOMAIN_DATA +from . import AbodeConfigEntry from .entity import AbodeDevice async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: AbodeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Abode alarm control panel device.""" - data = hass.data[DOMAIN_DATA] + data = entry.runtime_data async_add_entities( [AbodeAlarm(data, await hass.async_add_executor_job(data.abode.get_alarm))] ) diff --git a/homeassistant/components/abode/binary_sensor.py b/homeassistant/components/abode/binary_sensor.py index a3fce63ddf22..a866e1dd9f00 100644 --- a/homeassistant/components/abode/binary_sensor.py +++ b/homeassistant/components/abode/binary_sensor.py @@ -10,22 +10,21 @@ from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, BinarySensorEntity, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.util.enum import try_parse_enum -from .const import DOMAIN_DATA +from . import AbodeConfigEntry from .entity import AbodeDevice async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: AbodeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Abode binary sensor devices.""" - data = hass.data[DOMAIN_DATA] + data = entry.runtime_data device_types = [ "connectivity", diff --git a/homeassistant/components/abode/camera.py b/homeassistant/components/abode/camera.py index 4d81fba91728..a7ae3cd0b1fb 100644 --- a/homeassistant/components/abode/camera.py +++ b/homeassistant/components/abode/camera.py @@ -12,14 +12,13 @@ import requests from requests.models import Response from homeassistant.components.camera import Camera -from homeassistant.config_entries import ConfigEntry from homeassistant.core import Event, HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.util import Throttle -from . import AbodeSystem -from .const import DOMAIN_DATA, LOGGER +from . import AbodeConfigEntry, AbodeSystem +from .const import LOGGER from .entity import AbodeDevice MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90) @@ -27,11 +26,11 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90) async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: AbodeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Abode camera devices.""" - data = hass.data[DOMAIN_DATA] + data = entry.runtime_data async_add_entities( AbodeCamera(data, device, timeline.CAPTURE_IMAGE) diff --git a/homeassistant/components/abode/const.py b/homeassistant/components/abode/const.py index 0279b89f7d43..5cd2393af70d 100644 --- a/homeassistant/components/abode/const.py +++ b/homeassistant/components/abode/const.py @@ -3,17 +3,10 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING - -from homeassistant.util.hass_dict import HassKey - -if TYPE_CHECKING: - from . import AbodeSystem LOGGER = logging.getLogger(__package__) DOMAIN = "abode" -DOMAIN_DATA: HassKey[AbodeSystem] = HassKey(DOMAIN) ATTRIBUTION = "Data provided by goabode.com" CONF_POLLING = "polling" diff --git a/homeassistant/components/abode/cover.py b/homeassistant/components/abode/cover.py index dd70ea765ba8..1a81d04b09e5 100644 --- a/homeassistant/components/abode/cover.py +++ b/homeassistant/components/abode/cover.py @@ -5,21 +5,20 @@ from typing import Any from jaraco.abode.devices.cover import Cover from homeassistant.components.cover import CoverEntity -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import DOMAIN_DATA +from . import AbodeConfigEntry from .entity import AbodeDevice async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: AbodeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Abode cover devices.""" - data = hass.data[DOMAIN_DATA] + data = entry.runtime_data async_add_entities( AbodeCover(data, device) diff --git a/homeassistant/components/abode/entity.py b/homeassistant/components/abode/entity.py index d74e7990328a..087a87d2ce72 100644 --- a/homeassistant/components/abode/entity.py +++ b/homeassistant/components/abode/entity.py @@ -7,7 +7,7 @@ from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity import Entity from . import AbodeSystem -from .const import ATTRIBUTION, DOMAIN, DOMAIN_DATA +from .const import ATTRIBUTION, DOMAIN class AbodeEntity(Entity): @@ -29,7 +29,7 @@ class AbodeEntity(Entity): self._update_connection_status, ) - self.hass.data[DOMAIN_DATA].entity_ids.add(self.entity_id) + self._data.entity_ids.add(self.entity_id) async def async_will_remove_from_hass(self) -> None: """Unsubscribe from Abode connection status updates.""" diff --git a/homeassistant/components/abode/light.py b/homeassistant/components/abode/light.py index cee402d3cb6e..1d0f2f7275e3 100644 --- a/homeassistant/components/abode/light.py +++ b/homeassistant/components/abode/light.py @@ -16,21 +16,20 @@ from homeassistant.components.light import ( ColorMode, LightEntity, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import DOMAIN_DATA +from . import AbodeConfigEntry from .entity import AbodeDevice async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: AbodeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Abode light devices.""" - data = hass.data[DOMAIN_DATA] + data = entry.runtime_data async_add_entities( AbodeLight(data, device) diff --git a/homeassistant/components/abode/lock.py b/homeassistant/components/abode/lock.py index 9e4c45453e5e..aad7838cab57 100644 --- a/homeassistant/components/abode/lock.py +++ b/homeassistant/components/abode/lock.py @@ -5,21 +5,20 @@ from typing import Any from jaraco.abode.devices.lock import Lock from homeassistant.components.lock import LockEntity -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import DOMAIN_DATA +from . import AbodeConfigEntry from .entity import AbodeDevice async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: AbodeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Abode lock devices.""" - data = hass.data[DOMAIN_DATA] + data = entry.runtime_data async_add_entities( AbodeLock(data, device) diff --git a/homeassistant/components/abode/sensor.py b/homeassistant/components/abode/sensor.py index 72eee7eccf28..a1597d7d41f6 100644 --- a/homeassistant/components/abode/sensor.py +++ b/homeassistant/components/abode/sensor.py @@ -14,13 +14,11 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.const import LIGHT_LUX, PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from . import AbodeSystem -from .const import DOMAIN_DATA +from . import AbodeConfigEntry, AbodeSystem from .entity import AbodeDevice ABODE_TEMPERATURE_UNIT_HA_UNIT = { @@ -66,11 +64,11 @@ SENSOR_TYPES: tuple[AbodeSensorDescription, ...] = ( async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: AbodeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Abode sensor devices.""" - data = hass.data[DOMAIN_DATA] + data = entry.runtime_data async_add_entities( AbodeSensor(data, device, description) diff --git a/homeassistant/components/abode/services.py b/homeassistant/components/abode/services.py index 5b2a05f52287..70ff5ab16964 100644 --- a/homeassistant/components/abode/services.py +++ b/homeassistant/components/abode/services.py @@ -2,15 +2,21 @@ from __future__ import annotations +from typing import TYPE_CHECKING + from jaraco.abode.exceptions import Exception as AbodeException import voluptuous as vol from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant, ServiceCall, callback +from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers import config_validation as cv from homeassistant.helpers.dispatcher import dispatcher_send -from .const import DOMAIN, DOMAIN_DATA, LOGGER +from .const import DOMAIN, LOGGER + +if TYPE_CHECKING: + from . import AbodeConfigEntry, AbodeSystem ATTR_SETTING = "setting" ATTR_VALUE = "value" @@ -25,13 +31,21 @@ CAPTURE_IMAGE_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids}) AUTOMATION_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids}) +def _get_abode_system(hass: HomeAssistant) -> AbodeSystem: + """Return the Abode system for the loaded config entry.""" + entries: list[AbodeConfigEntry] = hass.config_entries.async_loaded_entries(DOMAIN) + if not entries: + raise ServiceValidationError("Abode integration is not loaded") + return entries[0].runtime_data + + def _change_setting(call: ServiceCall) -> None: """Change an Abode system setting.""" setting = call.data[ATTR_SETTING] value = call.data[ATTR_VALUE] try: - call.hass.data[DOMAIN_DATA].abode.set_setting(setting, value) + _get_abode_system(call.hass).abode.set_setting(setting, value) except AbodeException as ex: LOGGER.warning(ex) @@ -42,7 +56,7 @@ def _capture_image(call: ServiceCall) -> None: target_entities = [ entity_id - for entity_id in call.hass.data[DOMAIN_DATA].entity_ids + for entity_id in _get_abode_system(call.hass).entity_ids if entity_id in entity_ids ] @@ -57,7 +71,7 @@ def _trigger_automation(call: ServiceCall) -> None: target_entities = [ entity_id - for entity_id in call.hass.data[DOMAIN_DATA].entity_ids + for entity_id in _get_abode_system(call.hass).entity_ids if entity_id in entity_ids ] diff --git a/homeassistant/components/abode/switch.py b/homeassistant/components/abode/switch.py index 7eecea514fed..751c6c9b3d20 100644 --- a/homeassistant/components/abode/switch.py +++ b/homeassistant/components/abode/switch.py @@ -7,12 +7,11 @@ from typing import Any, cast from jaraco.abode.devices.switch import Switch from homeassistant.components.switch import SwitchEntity -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import DOMAIN_DATA +from . import AbodeConfigEntry from .entity import AbodeAutomation, AbodeDevice DEVICE_TYPES = ["switch", "valve"] @@ -20,11 +19,11 @@ DEVICE_TYPES = ["switch", "valve"] async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: AbodeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Abode switch devices.""" - data = hass.data[DOMAIN_DATA] + data = entry.runtime_data entities: list[SwitchEntity] = [ AbodeSwitch(data, device)