From 99aa68c93fc536f2037eb30a9b0e468bf1be6a07 Mon Sep 17 00:00:00 2001 From: Jan Stienstra <65826735+j-stienstra@users.noreply.github.com> Date: Wed, 24 Jul 2024 08:53:01 +0200 Subject: [PATCH] Use runtime_data instead of hass.data for Jellyfin (#122410) * Use runtime_data instead of hass.data * Process review --- homeassistant/components/jellyfin/__init__.py | 17 ++++++++--------- .../components/jellyfin/config_flow.py | 16 +++++++--------- .../components/jellyfin/coordinator.py | 3 --- .../components/jellyfin/diagnostics.py | 8 +++----- .../components/jellyfin/media_player.py | 7 +++---- .../components/jellyfin/media_source.py | 9 ++++----- homeassistant/components/jellyfin/sensor.py | 10 ++++------ tests/components/jellyfin/test_init.py | 2 -- 8 files changed, 29 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/jellyfin/__init__.py b/homeassistant/components/jellyfin/__init__.py index ade030af9dd..0dc51ebd9b3 100644 --- a/homeassistant/components/jellyfin/__init__.py +++ b/homeassistant/components/jellyfin/__init__.py @@ -12,11 +12,11 @@ from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, PLATFORMS from .coordinator import JellyfinDataUpdateCoordinator, SessionsDataUpdateCoordinator from .models import JellyfinData +type JellyfinConfigEntry = ConfigEntry[JellyfinData] -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + +async def async_setup_entry(hass: HomeAssistant, entry: JellyfinConfigEntry) -> bool: """Set up Jellyfin from a config entry.""" - hass.data.setdefault(DOMAIN, {}) - if CONF_CLIENT_DEVICE_ID not in entry.data: entry_data = entry.data.copy() entry_data[CONF_CLIENT_DEVICE_ID] = entry.entry_id @@ -45,7 +45,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: for coordinator in coordinators.values(): await coordinator.async_config_entry_first_refresh() - hass.data[DOMAIN][entry.entry_id] = JellyfinData( + entry.runtime_data = JellyfinData( client_device_id=entry.data[CONF_CLIENT_DEVICE_ID], jellyfin_client=client, coordinators=coordinators, @@ -56,21 +56,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_unload_entry(hass: HomeAssistant, entry: JellyfinConfigEntry) -> bool: """Unload a config entry.""" unloaded = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) if unloaded: - data = hass.data[DOMAIN].pop(entry.entry_id) - data.jellyfin_client.stop() + entry.runtime_data.jellyfin_client.stop() return unloaded async def async_remove_config_entry_device( - hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry + hass: HomeAssistant, config_entry: JellyfinConfigEntry, device_entry: dr.DeviceEntry ) -> bool: """Remove device from a config entry.""" - data = hass.data[DOMAIN][config_entry.entry_id] + data = config_entry.runtime_data coordinator = data.coordinators["sessions"] return not device_entry.identifiers.intersection( diff --git a/homeassistant/components/jellyfin/config_flow.py b/homeassistant/components/jellyfin/config_flow.py index baecbcfb941..7b5426cffde 100644 --- a/homeassistant/components/jellyfin/config_flow.py +++ b/homeassistant/components/jellyfin/config_flow.py @@ -9,15 +9,15 @@ from typing import Any import voluptuous as vol from homeassistant.config_entries import ( - ConfigEntry, ConfigFlow, ConfigFlowResult, - OptionsFlow, + OptionsFlowWithConfigEntry, ) from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME from homeassistant.core import callback from homeassistant.util.uuid import random_uuid_hex +from . import JellyfinConfigEntry from .client_wrapper import CannotConnect, InvalidAuth, create_client, validate_input from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, SUPPORTED_AUDIO_CODECS @@ -56,7 +56,7 @@ class JellyfinConfigFlow(ConfigFlow, domain=DOMAIN): def __init__(self) -> None: """Initialize the Jellyfin config flow.""" self.client_device_id: str | None = None - self.entry: ConfigEntry | None = None + self.entry: JellyfinConfigEntry | None = None async def async_step_user( self, user_input: dict[str, Any] | None = None @@ -146,18 +146,16 @@ class JellyfinConfigFlow(ConfigFlow, domain=DOMAIN): @staticmethod @callback - def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow: + def async_get_options_flow( + config_entry: JellyfinConfigEntry, + ) -> OptionsFlowWithConfigEntry: """Create the options flow.""" return OptionsFlowHandler(config_entry) -class OptionsFlowHandler(OptionsFlow): +class OptionsFlowHandler(OptionsFlowWithConfigEntry): """Handle an option flow for jellyfin.""" - def __init__(self, config_entry: ConfigEntry) -> None: - """Initialize options flow.""" - self.config_entry = config_entry - async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: diff --git a/homeassistant/components/jellyfin/coordinator.py b/homeassistant/components/jellyfin/coordinator.py index 4d907ac1531..bbd0dfe7496 100644 --- a/homeassistant/components/jellyfin/coordinator.py +++ b/homeassistant/components/jellyfin/coordinator.py @@ -8,7 +8,6 @@ from typing import Any, TypeVar from jellyfin_apiclient_python import JellyfinClient -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator @@ -23,8 +22,6 @@ JellyfinDataT = TypeVar( class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[JellyfinDataT], ABC): """Data update coordinator for the Jellyfin integration.""" - config_entry: ConfigEntry - def __init__( self, hass: HomeAssistant, diff --git a/homeassistant/components/jellyfin/diagnostics.py b/homeassistant/components/jellyfin/diagnostics.py index ecc66868bd0..80bbd78c9ad 100644 --- a/homeassistant/components/jellyfin/diagnostics.py +++ b/homeassistant/components/jellyfin/diagnostics.py @@ -5,21 +5,19 @@ from __future__ import annotations from typing import Any from homeassistant.components.diagnostics import async_redact_data -from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD from homeassistant.core import HomeAssistant -from .const import DOMAIN -from .models import JellyfinData +from . import JellyfinConfigEntry TO_REDACT = {CONF_PASSWORD} async def async_get_config_entry_diagnostics( - hass: HomeAssistant, entry: ConfigEntry + hass: HomeAssistant, entry: JellyfinConfigEntry ) -> dict[str, Any]: """Return diagnostics for a config entry.""" - data: JellyfinData = hass.data[DOMAIN][entry.entry_id] + data = entry.runtime_data sessions = data.coordinators["sessions"] return { diff --git a/homeassistant/components/jellyfin/media_player.py b/homeassistant/components/jellyfin/media_player.py index 954ac7af69e..d24d15f1dfa 100644 --- a/homeassistant/components/jellyfin/media_player.py +++ b/homeassistant/components/jellyfin/media_player.py @@ -12,27 +12,26 @@ from homeassistant.components.media_player import ( MediaType, ) from homeassistant.components.media_player.browse_media import BrowseMedia -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.dt import parse_datetime +from . import JellyfinConfigEntry from .browse_media import build_item_response, build_root_response from .client_wrapper import get_artwork_url from .const import CONTENT_TYPE_MAP, DOMAIN, LOGGER from .coordinator import JellyfinDataUpdateCoordinator from .entity import JellyfinEntity -from .models import JellyfinData async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: JellyfinConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up Jellyfin media_player from a config entry.""" - jellyfin_data: JellyfinData = hass.data[DOMAIN][entry.entry_id] + jellyfin_data = entry.runtime_data coordinator = jellyfin_data.coordinators["sessions"] @callback diff --git a/homeassistant/components/jellyfin/media_source.py b/homeassistant/components/jellyfin/media_source.py index 8901e9e32c0..4b3e8b0146a 100644 --- a/homeassistant/components/jellyfin/media_source.py +++ b/homeassistant/components/jellyfin/media_source.py @@ -17,9 +17,9 @@ from homeassistant.components.media_source.models import ( MediaSourceItem, PlayMedia, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from . import JellyfinConfigEntry from .const import ( COLLECTION_TYPE_MOVIES, COLLECTION_TYPE_MUSIC, @@ -48,7 +48,6 @@ from .const import ( PLAYABLE_ITEM_TYPES, SUPPORTED_COLLECTION_TYPES, ) -from .models import JellyfinData _LOGGER = logging.getLogger(__name__) @@ -56,8 +55,8 @@ _LOGGER = logging.getLogger(__name__) async def async_get_media_source(hass: HomeAssistant) -> MediaSource: """Set up Jellyfin media source.""" # Currently only a single Jellyfin server is supported - entry = hass.config_entries.async_entries(DOMAIN)[0] - jellyfin_data: JellyfinData = hass.data[DOMAIN][entry.entry_id] + entry: JellyfinConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] + jellyfin_data = entry.runtime_data return JellyfinSource(hass, jellyfin_data.jellyfin_client, entry) @@ -68,7 +67,7 @@ class JellyfinSource(MediaSource): name: str = "Jellyfin" def __init__( - self, hass: HomeAssistant, client: JellyfinClient, entry: ConfigEntry + self, hass: HomeAssistant, client: JellyfinClient, entry: JellyfinConfigEntry ) -> None: """Initialize the Jellyfin media source.""" super().__init__(DOMAIN) diff --git a/homeassistant/components/jellyfin/sensor.py b/homeassistant/components/jellyfin/sensor.py index 85c7e9e9ee1..3be4ccf2559 100644 --- a/homeassistant/components/jellyfin/sensor.py +++ b/homeassistant/components/jellyfin/sensor.py @@ -6,15 +6,13 @@ from collections.abc import Callable from dataclasses import dataclass from homeassistant.components.sensor import SensorEntity, SensorEntityDescription -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType -from .const import DOMAIN +from . import JellyfinConfigEntry from .coordinator import JellyfinDataT from .entity import JellyfinEntity -from .models import JellyfinData @dataclass(frozen=True, kw_only=True) @@ -46,14 +44,14 @@ SENSOR_TYPES: dict[str, JellyfinSensorEntityDescription] = { async def async_setup_entry( hass: HomeAssistant, - entry: ConfigEntry, + entry: JellyfinConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up Jellyfin sensor based on a config entry.""" - jellyfin_data: JellyfinData = hass.data[DOMAIN][entry.entry_id] + data = entry.runtime_data async_add_entities( - JellyfinSensor(jellyfin_data.coordinators[coordinator_type], description) + JellyfinSensor(data.coordinators[coordinator_type], description) for coordinator_type, description in SENSOR_TYPES.items() ) diff --git a/tests/components/jellyfin/test_init.py b/tests/components/jellyfin/test_init.py index 51d7af2ae94..1af59737296 100644 --- a/tests/components/jellyfin/test_init.py +++ b/tests/components/jellyfin/test_init.py @@ -68,12 +68,10 @@ async def test_load_unload_config_entry( await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.async_block_till_done() - assert mock_config_entry.entry_id in hass.data[DOMAIN] assert mock_config_entry.state is ConfigEntryState.LOADED await hass.config_entries.async_unload(mock_config_entry.entry_id) await hass.async_block_till_done() - assert mock_config_entry.entry_id not in hass.data[DOMAIN] assert mock_config_entry.state is ConfigEntryState.NOT_LOADED