diff --git a/homeassistant/components/blink/__init__.py b/homeassistant/components/blink/__init__.py index d21994ecc8f..cdc2da9afdf 100644 --- a/homeassistant/components/blink/__init__.py +++ b/homeassistant/components/blink/__init__.py @@ -2,6 +2,7 @@ from copy import deepcopy import logging +from typing import Any from aiohttp import ClientError from blinkpy.auth import Auth @@ -9,7 +10,7 @@ from blinkpy.blinkpy import Blink import voluptuous as vol from homeassistant.components import persistent_notification -from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry +from homeassistant.config_entries import SOURCE_REAUTH from homeassistant.const import ( CONF_FILE_PATH, CONF_FILENAME, @@ -24,7 +25,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.typing import ConfigType from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, PLATFORMS -from .coordinator import BlinkUpdateCoordinator +from .coordinator import BlinkConfigEntry, BlinkUpdateCoordinator from .services import setup_services _LOGGER = logging.getLogger(__name__) @@ -40,7 +41,7 @@ SERVICE_SAVE_RECENT_CLIPS_SCHEMA = vol.Schema( CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) -async def _reauth_flow_wrapper(hass, data): +async def _reauth_flow_wrapper(hass: HomeAssistant, data: dict[str, Any]) -> None: """Reauth flow wrapper.""" hass.add_job( hass.config_entries.flow.async_init( @@ -57,7 +58,7 @@ async def _reauth_flow_wrapper(hass, data): ) -async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_migrate_entry(hass: HomeAssistant, entry: BlinkConfigEntry) -> bool: """Handle migration of a previous version config entry.""" _LOGGER.debug("Migrating from version %s", entry.version) data = {**entry.data} @@ -79,10 +80,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_setup_entry(hass: HomeAssistant, entry: BlinkConfigEntry) -> bool: """Set up Blink via config entry.""" - hass.data.setdefault(DOMAIN, {}) - _async_import_options_from_data_if_missing(hass, entry) session = async_get_clientsession(hass) blink = Blink(session=session) @@ -104,7 +103,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady await coordinator.async_config_entry_first_refresh() - hass.data[DOMAIN][entry.entry_id] = coordinator + + entry.runtime_data = coordinator await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -113,7 +113,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @callback def _async_import_options_from_data_if_missing( - hass: HomeAssistant, entry: ConfigEntry + hass: HomeAssistant, entry: BlinkConfigEntry ) -> None: options = dict(entry.options) if CONF_SCAN_INTERVAL not in entry.options: @@ -123,8 +123,6 @@ def _async_import_options_from_data_if_missing( hass.config_entries.async_update_entry(entry, options=options) -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_unload_entry(hass: HomeAssistant, entry: BlinkConfigEntry) -> bool: """Unload Blink entry.""" - if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): - hass.data[DOMAIN].pop(entry.entry_id) - return unload_ok + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/blink/alarm_control_panel.py b/homeassistant/components/blink/alarm_control_panel.py index 629747365a8..bfb8aa9a3a0 100644 --- a/homeassistant/components/blink/alarm_control_panel.py +++ b/homeassistant/components/blink/alarm_control_panel.py @@ -11,7 +11,6 @@ from homeassistant.components.alarm_control_panel import ( AlarmControlPanelEntityFeature, AlarmControlPanelState, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError @@ -20,16 +19,18 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DEFAULT_ATTRIBUTION, DEFAULT_BRAND, DOMAIN -from .coordinator import BlinkUpdateCoordinator +from .coordinator import BlinkConfigEntry, BlinkUpdateCoordinator _LOGGER = logging.getLogger(__name__) async def async_setup_entry( - hass: HomeAssistant, config: ConfigEntry, async_add_entities: AddEntitiesCallback + hass: HomeAssistant, + config_entry: BlinkConfigEntry, + async_add_entities: AddEntitiesCallback, ) -> None: """Set up the Blink Alarm Control Panels.""" - coordinator: BlinkUpdateCoordinator = hass.data[DOMAIN][config.entry_id] + coordinator = config_entry.runtime_data sync_modules = [] for sync_name, sync_module in coordinator.api.sync.items(): diff --git a/homeassistant/components/blink/binary_sensor.py b/homeassistant/components/blink/binary_sensor.py index 2f0a56a901c..c11d4cfea23 100644 --- a/homeassistant/components/blink/binary_sensor.py +++ b/homeassistant/components/blink/binary_sensor.py @@ -9,7 +9,6 @@ from homeassistant.components.binary_sensor import ( BinarySensorEntity, BinarySensorEntityDescription, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceInfo @@ -23,7 +22,7 @@ from .const import ( TYPE_CAMERA_ARMED, TYPE_MOTION_DETECTED, ) -from .coordinator import BlinkUpdateCoordinator +from .coordinator import BlinkConfigEntry, BlinkUpdateCoordinator _LOGGER = logging.getLogger(__name__) @@ -47,11 +46,13 @@ BINARY_SENSORS_TYPES: tuple[BinarySensorEntityDescription, ...] = ( async def async_setup_entry( - hass: HomeAssistant, config: ConfigEntry, async_add_entities: AddEntitiesCallback + hass: HomeAssistant, + config_entry: BlinkConfigEntry, + async_add_entities: AddEntitiesCallback, ) -> None: """Set up the blink binary sensors.""" - coordinator: BlinkUpdateCoordinator = hass.data[DOMAIN][config.entry_id] + coordinator = config_entry.runtime_data entities = [ BlinkBinarySensor(coordinator, camera, description) diff --git a/homeassistant/components/blink/camera.py b/homeassistant/components/blink/camera.py index cce9100a0bd..56a84135a9b 100644 --- a/homeassistant/components/blink/camera.py +++ b/homeassistant/components/blink/camera.py @@ -10,7 +10,6 @@ from requests.exceptions import ChunkedEncodingError import voluptuous as vol from homeassistant.components.camera import Camera -from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_FILE_PATH, CONF_FILENAME from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError, ServiceValidationError @@ -28,7 +27,7 @@ from .const import ( SERVICE_SAVE_VIDEO, SERVICE_TRIGGER, ) -from .coordinator import BlinkUpdateCoordinator +from .coordinator import BlinkConfigEntry, BlinkUpdateCoordinator _LOGGER = logging.getLogger(__name__) @@ -38,11 +37,13 @@ PARALLEL_UPDATES = 1 async def async_setup_entry( - hass: HomeAssistant, config: ConfigEntry, async_add_entities: AddEntitiesCallback + hass: HomeAssistant, + config_entry: BlinkConfigEntry, + async_add_entities: AddEntitiesCallback, ) -> None: """Set up a Blink Camera.""" - coordinator: BlinkUpdateCoordinator = hass.data[DOMAIN][config.entry_id] + coordinator = config_entry.runtime_data entities = [ BlinkCamera(coordinator, name, camera) for name, camera in coordinator.api.cameras.items() diff --git a/homeassistant/components/blink/coordinator.py b/homeassistant/components/blink/coordinator.py index e71ff4e449e..7278dabe083 100644 --- a/homeassistant/components/blink/coordinator.py +++ b/homeassistant/components/blink/coordinator.py @@ -8,6 +8,7 @@ from typing import Any from blinkpy.blinkpy import Blink +from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator @@ -16,6 +17,8 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = 300 +type BlinkConfigEntry = ConfigEntry[BlinkUpdateCoordinator] + class BlinkUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): """BlinkUpdateCoordinator - In charge of downloading the data for a site.""" diff --git a/homeassistant/components/blink/diagnostics.py b/homeassistant/components/blink/diagnostics.py index 88ff2aff928..255f58fc369 100644 --- a/homeassistant/components/blink/diagnostics.py +++ b/homeassistant/components/blink/diagnostics.py @@ -4,24 +4,21 @@ from __future__ import annotations from typing import Any -from blinkpy.blinkpy import Blink - from homeassistant.components.diagnostics import async_redact_data -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from .const import DOMAIN +from .coordinator import BlinkConfigEntry TO_REDACT = {"serial", "macaddress", "username", "password", "token", "unique_id"} async def async_get_config_entry_diagnostics( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: BlinkConfigEntry, ) -> dict[str, Any]: """Return diagnostics for a config entry.""" - api: Blink = hass.data[DOMAIN][config_entry.entry_id].api + api = config_entry.runtime_data.api data = { camera.name: dict(camera.attributes.items()) diff --git a/homeassistant/components/blink/sensor.py b/homeassistant/components/blink/sensor.py index 8a807b9303e..f20f8188b42 100644 --- a/homeassistant/components/blink/sensor.py +++ b/homeassistant/components/blink/sensor.py @@ -10,7 +10,6 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.const import EntityCategory, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceInfo @@ -18,7 +17,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DEFAULT_BRAND, DOMAIN, TYPE_TEMPERATURE, TYPE_WIFI_STRENGTH -from .coordinator import BlinkUpdateCoordinator +from .coordinator import BlinkConfigEntry, BlinkUpdateCoordinator _LOGGER = logging.getLogger(__name__) @@ -40,11 +39,13 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( async def async_setup_entry( - hass: HomeAssistant, config: ConfigEntry, async_add_entities: AddEntitiesCallback + hass: HomeAssistant, + config_entry: BlinkConfigEntry, + async_add_entities: AddEntitiesCallback, ) -> None: """Initialize a Blink sensor.""" - coordinator: BlinkUpdateCoordinator = hass.data[DOMAIN][config.entry_id] + coordinator = config_entry.runtime_data entities = [ BlinkSensor(coordinator, camera, description) for camera in coordinator.api.cameras diff --git a/homeassistant/components/blink/services.py b/homeassistant/components/blink/services.py index bb2cbf575dd..5f51598e721 100644 --- a/homeassistant/components/blink/services.py +++ b/homeassistant/components/blink/services.py @@ -11,6 +11,7 @@ from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.helpers import config_validation as cv from .const import ATTR_CONFIG_ENTRY_ID, DOMAIN, SERVICE_SEND_PIN +from .coordinator import BlinkConfigEntry SERVICE_UPDATE_SCHEMA = vol.Schema( { @@ -30,6 +31,7 @@ def setup_services(hass: HomeAssistant) -> None: async def send_pin(call: ServiceCall): """Call blink to send new pin.""" + config_entry: BlinkConfigEntry | None for entry_id in call.data[ATTR_CONFIG_ENTRY_ID]: if not (config_entry := hass.config_entries.async_get_entry(entry_id)): raise ServiceValidationError( @@ -43,7 +45,7 @@ def setup_services(hass: HomeAssistant) -> None: translation_key="not_loaded", translation_placeholders={"target": config_entry.title}, ) - coordinator = hass.data[DOMAIN][entry_id] + coordinator = config_entry.runtime_data await coordinator.api.auth.send_auth_key( coordinator.api, call.data[CONF_PIN], diff --git a/homeassistant/components/blink/switch.py b/homeassistant/components/blink/switch.py index ab9b825ded1..8eabd5c0e59 100644 --- a/homeassistant/components/blink/switch.py +++ b/homeassistant/components/blink/switch.py @@ -9,7 +9,6 @@ from homeassistant.components.switch import ( SwitchEntity, SwitchEntityDescription, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.device_registry import DeviceInfo @@ -17,7 +16,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DEFAULT_BRAND, DOMAIN, TYPE_CAMERA_ARMED -from .coordinator import BlinkUpdateCoordinator +from .coordinator import BlinkConfigEntry, BlinkUpdateCoordinator SWITCH_TYPES: tuple[SwitchEntityDescription, ...] = ( SwitchEntityDescription( @@ -30,11 +29,11 @@ SWITCH_TYPES: tuple[SwitchEntityDescription, ...] = ( async def async_setup_entry( hass: HomeAssistant, - config: ConfigEntry, + config_entry: BlinkConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up the Blink switches.""" - coordinator: BlinkUpdateCoordinator = hass.data[DOMAIN][config.entry_id] + coordinator = config_entry.runtime_data async_add_entities( BlinkSwitch(coordinator, camera, description) diff --git a/tests/components/blink/test_init.py b/tests/components/blink/test_init.py index 3cd2cd51ebd..6d4a93e58ab 100644 --- a/tests/components/blink/test_init.py +++ b/tests/components/blink/test_init.py @@ -66,18 +66,17 @@ async def test_setup_not_ready_authkey_required( assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR -async def test_unload_entry_multiple( +async def test_unload_entry( hass: HomeAssistant, mock_blink_api: MagicMock, mock_blink_auth_api: MagicMock, mock_config_entry: MockConfigEntry, ) -> None: - """Test being able to unload one of 2 entries.""" + """Test unload doesn't un-register services.""" mock_config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.async_block_till_done() - hass.data[DOMAIN]["dummy"] = {1: 2} assert mock_config_entry.state is ConfigEntryState.LOADED assert await hass.config_entries.async_unload(mock_config_entry.entry_id) assert mock_config_entry.state is ConfigEntryState.NOT_LOADED