From 79de27544c7acf0a2b4bfeb01f57cebc09a646d7 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Fri, 4 Oct 2024 15:59:11 +0200 Subject: [PATCH] Simplify Jellyfin (#127353) * Simplify Jellyfin * Fix comment --- homeassistant/components/jellyfin/__init__.py | 30 +++++----------- .../components/jellyfin/coordinator.py | 36 +++++-------------- .../components/jellyfin/diagnostics.py | 11 +++--- homeassistant/components/jellyfin/entity.py | 6 ++-- .../components/jellyfin/media_player.py | 3 +- .../components/jellyfin/media_source.py | 4 +-- homeassistant/components/jellyfin/models.py | 18 ---------- homeassistant/components/jellyfin/sensor.py | 20 +++++------ 8 files changed, 37 insertions(+), 91 deletions(-) delete mode 100644 homeassistant/components/jellyfin/models.py diff --git a/homeassistant/components/jellyfin/__init__.py b/homeassistant/components/jellyfin/__init__.py index 0dc51ebd9b3..4f0886dfa22 100644 --- a/homeassistant/components/jellyfin/__init__.py +++ b/homeassistant/components/jellyfin/__init__.py @@ -9,10 +9,9 @@ from homeassistant.helpers import device_registry as dr from .client_wrapper import CannotConnect, InvalidAuth, create_client, validate_input from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, PLATFORMS -from .coordinator import JellyfinDataUpdateCoordinator, SessionsDataUpdateCoordinator -from .models import JellyfinData +from .coordinator import JellyfinDataUpdateCoordinator -type JellyfinConfigEntry = ConfigEntry[JellyfinData] +type JellyfinConfigEntry = ConfigEntry[JellyfinDataUpdateCoordinator] async def async_setup_entry(hass: HomeAssistant, entry: JellyfinConfigEntry) -> bool: @@ -36,20 +35,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: JellyfinConfigEntry) -> server_info: dict[str, Any] = connect_result["Servers"][0] - coordinators: dict[str, JellyfinDataUpdateCoordinator[Any]] = { - "sessions": SessionsDataUpdateCoordinator( - hass, client, server_info, entry.data[CONF_CLIENT_DEVICE_ID], user_id - ), - } + coordinator = JellyfinDataUpdateCoordinator(hass, client, server_info, user_id) - for coordinator in coordinators.values(): - await coordinator.async_config_entry_first_refresh() + await coordinator.async_config_entry_first_refresh() - entry.runtime_data = JellyfinData( - client_device_id=entry.data[CONF_CLIENT_DEVICE_ID], - jellyfin_client=client, - coordinators=coordinators, - ) + entry.runtime_data = coordinator + entry.async_on_unload(client.stop) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -58,19 +49,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: JellyfinConfigEntry) -> 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: - entry.runtime_data.jellyfin_client.stop() - - return unloaded + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) async def async_remove_config_entry_device( hass: HomeAssistant, config_entry: JellyfinConfigEntry, device_entry: dr.DeviceEntry ) -> bool: """Remove device from a config entry.""" - data = config_entry.runtime_data - coordinator = data.coordinators["sessions"] + coordinator = config_entry.runtime_data return not device_entry.identifiers.intersection( ( diff --git a/homeassistant/components/jellyfin/coordinator.py b/homeassistant/components/jellyfin/coordinator.py index bbd0dfe7496..a9b0a8b7031 100644 --- a/homeassistant/components/jellyfin/coordinator.py +++ b/homeassistant/components/jellyfin/coordinator.py @@ -2,32 +2,28 @@ from __future__ import annotations -from abc import ABC, abstractmethod from datetime import timedelta -from typing import Any, TypeVar +from typing import Any from jellyfin_apiclient_python import JellyfinClient +from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from .const import DOMAIN, LOGGER, USER_APP_NAME - -JellyfinDataT = TypeVar( - "JellyfinDataT", - bound=dict[str, dict[str, Any]] | dict[str, Any], -) +from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, LOGGER, USER_APP_NAME -class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[JellyfinDataT], ABC): +class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[dict[str, dict[str, Any]]]): """Data update coordinator for the Jellyfin integration.""" + config_entry: ConfigEntry + def __init__( self, hass: HomeAssistant, api_client: JellyfinClient, system_info: dict[str, Any], - client_device_id: str, user_id: str, ) -> None: """Initialize the coordinator.""" @@ -37,32 +33,18 @@ class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[JellyfinDataT], ABC): name=DOMAIN, update_interval=timedelta(seconds=10), ) - self.api_client: JellyfinClient = api_client + self.api_client = api_client self.server_id: str = system_info["Id"] self.server_name: str = system_info["Name"] self.server_version: str | None = system_info.get("Version") - self.client_device_id: str = client_device_id + self.client_device_id: str = self.config_entry.data[CONF_CLIENT_DEVICE_ID] self.user_id: str = user_id self.session_ids: set[str] = set() self.device_ids: set[str] = set() - async def _async_update_data(self) -> JellyfinDataT: + async def _async_update_data(self) -> dict[str, dict[str, Any]]: """Get the latest data from Jellyfin.""" - return await self._fetch_data() - - @abstractmethod - async def _fetch_data(self) -> JellyfinDataT: - """Fetch the actual data.""" - - -class SessionsDataUpdateCoordinator( - JellyfinDataUpdateCoordinator[dict[str, dict[str, Any]]] -): - """Sessions update coordinator for Jellyfin.""" - - async def _fetch_data(self) -> dict[str, dict[str, Any]]: - """Fetch the data.""" sessions = await self.hass.async_add_executor_job( self.api_client.jellyfin.sessions ) diff --git a/homeassistant/components/jellyfin/diagnostics.py b/homeassistant/components/jellyfin/diagnostics.py index 80bbd78c9ad..8042d588d1b 100644 --- a/homeassistant/components/jellyfin/diagnostics.py +++ b/homeassistant/components/jellyfin/diagnostics.py @@ -17,8 +17,7 @@ async def async_get_config_entry_diagnostics( hass: HomeAssistant, entry: JellyfinConfigEntry ) -> dict[str, Any]: """Return diagnostics for a config entry.""" - data = entry.runtime_data - sessions = data.coordinators["sessions"] + coordinator = entry.runtime_data return { "entry": { @@ -26,9 +25,9 @@ async def async_get_config_entry_diagnostics( "data": async_redact_data(entry.data, TO_REDACT), }, "server": { - "id": sessions.server_id, - "name": sessions.server_name, - "version": sessions.server_version, + "id": coordinator.server_id, + "name": coordinator.server_name, + "version": coordinator.server_version, }, "sessions": [ { @@ -42,6 +41,6 @@ async def async_get_config_entry_diagnostics( "now_playing": session_data.get("NowPlayingItem"), "play_state": session_data.get("PlayState"), } - for session_id, session_data in sessions.data.items() + for session_id, session_data in coordinator.data.items() ], } diff --git a/homeassistant/components/jellyfin/entity.py b/homeassistant/components/jellyfin/entity.py index 2204a36dc61..b166645f4b0 100644 --- a/homeassistant/components/jellyfin/entity.py +++ b/homeassistant/components/jellyfin/entity.py @@ -7,17 +7,17 @@ from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DEFAULT_NAME, DOMAIN -from .coordinator import JellyfinDataT, JellyfinDataUpdateCoordinator +from .coordinator import JellyfinDataUpdateCoordinator -class JellyfinEntity(CoordinatorEntity[JellyfinDataUpdateCoordinator[JellyfinDataT]]): +class JellyfinEntity(CoordinatorEntity[JellyfinDataUpdateCoordinator]): """Defines a base Jellyfin entity.""" _attr_has_entity_name = True def __init__( self, - coordinator: JellyfinDataUpdateCoordinator[JellyfinDataT], + coordinator: JellyfinDataUpdateCoordinator, description: EntityDescription, ) -> None: """Initialize the Jellyfin entity.""" diff --git a/homeassistant/components/jellyfin/media_player.py b/homeassistant/components/jellyfin/media_player.py index 96a058c726e..c9c3c8c90c9 100644 --- a/homeassistant/components/jellyfin/media_player.py +++ b/homeassistant/components/jellyfin/media_player.py @@ -31,8 +31,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up Jellyfin media_player from a config entry.""" - jellyfin_data = entry.runtime_data - coordinator = jellyfin_data.coordinators["sessions"] + coordinator = entry.runtime_data @callback def handle_coordinator_update() -> None: diff --git a/homeassistant/components/jellyfin/media_source.py b/homeassistant/components/jellyfin/media_source.py index 0a462be5d61..a061118dd0a 100644 --- a/homeassistant/components/jellyfin/media_source.py +++ b/homeassistant/components/jellyfin/media_source.py @@ -56,9 +56,9 @@ async def async_get_media_source(hass: HomeAssistant) -> MediaSource: """Set up Jellyfin media source.""" # Currently only a single Jellyfin server is supported entry: JellyfinConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] - jellyfin_data = entry.runtime_data + coordinator = entry.runtime_data - return JellyfinSource(hass, jellyfin_data.jellyfin_client, entry) + return JellyfinSource(hass, coordinator.api_client, entry) class JellyfinSource(MediaSource): diff --git a/homeassistant/components/jellyfin/models.py b/homeassistant/components/jellyfin/models.py deleted file mode 100644 index bfa639a7567..00000000000 --- a/homeassistant/components/jellyfin/models.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Models for the Jellyfin integration.""" - -from __future__ import annotations - -from dataclasses import dataclass - -from jellyfin_apiclient_python import JellyfinClient - -from .coordinator import JellyfinDataUpdateCoordinator - - -@dataclass -class JellyfinData: - """Data for the Jellyfin integration.""" - - client_device_id: str - jellyfin_client: JellyfinClient - coordinators: dict[str, JellyfinDataUpdateCoordinator] diff --git a/homeassistant/components/jellyfin/sensor.py b/homeassistant/components/jellyfin/sensor.py index 37926567b4e..abf30a6b537 100644 --- a/homeassistant/components/jellyfin/sensor.py +++ b/homeassistant/components/jellyfin/sensor.py @@ -4,6 +4,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass +from typing import Any from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.core import HomeAssistant @@ -11,7 +12,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from . import JellyfinConfigEntry -from .coordinator import JellyfinDataT from .entity import JellyfinEntity @@ -19,10 +19,10 @@ from .entity import JellyfinEntity class JellyfinSensorEntityDescription(SensorEntityDescription): """Describes Jellyfin sensor entity.""" - value_fn: Callable[[JellyfinDataT], StateType] + value_fn: Callable[[dict[str, dict[str, Any]]], StateType] -def _count_now_playing(data: JellyfinDataT) -> int: +def _count_now_playing(data: dict[str, dict[str, Any]]) -> int: """Count the number of now playing.""" session_ids = [ sid for (sid, session) in data.items() if "NowPlayingItem" in session @@ -31,14 +31,14 @@ def _count_now_playing(data: JellyfinDataT) -> int: return len(session_ids) -SENSOR_TYPES: dict[str, JellyfinSensorEntityDescription] = { - "sessions": JellyfinSensorEntityDescription( +SENSOR_TYPES: tuple[JellyfinSensorEntityDescription, ...] = ( + JellyfinSensorEntityDescription( key="watching", translation_key="watching", value_fn=_count_now_playing, native_unit_of_measurement="clients", - ) -} + ), +) async def async_setup_entry( @@ -47,18 +47,16 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up Jellyfin sensor based on a config entry.""" - data = entry.runtime_data + coordinator = entry.runtime_data async_add_entities( - JellyfinSensor(data.coordinators[coordinator_type], description) - for coordinator_type, description in SENSOR_TYPES.items() + JellyfinSensor(coordinator, description) for description in SENSOR_TYPES ) class JellyfinSensor(JellyfinEntity, SensorEntity): """Defines a Jellyfin sensor entity.""" - _attr_has_entity_name = True entity_description: JellyfinSensorEntityDescription @property