From 79de27544c7acf0a2b4bfeb01f57cebc09a646d7 Mon Sep 17 00:00:00 2001
From: Joost Lekkerkerker <joostlek@outlook.com>
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
+from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, LOGGER, USER_APP_NAME
 
-JellyfinDataT = TypeVar(
-    "JellyfinDataT",
-    bound=dict[str, dict[str, Any]] | dict[str, Any],
-)
 
-
-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
-- 
GitLab