Detect newly connected clients in jellyfin (#89168)

This commit is contained in:
Chris Talkington 2023-03-05 08:00:16 -06:00 committed by GitHub
parent 680f3c27a5
commit 2e1f6cad96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 4899 additions and 12 deletions

View File

@ -36,7 +36,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
server_info: dict[str, Any] = connect_result["Servers"][0] server_info: dict[str, Any] = connect_result["Servers"][0]
coordinators: dict[str, JellyfinDataUpdateCoordinator[Any]] = { coordinators: dict[str, JellyfinDataUpdateCoordinator[Any]] = {
"sessions": SessionsDataUpdateCoordinator(hass, client, server_info, user_id), "sessions": SessionsDataUpdateCoordinator(
hass, client, server_info, entry.data[CONF_CLIENT_DEVICE_ID], user_id
),
} }
for coordinator in coordinators.values(): for coordinator in coordinators.values():

View File

@ -11,7 +11,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN, LOGGER from .const import DOMAIN, LOGGER, USER_APP_NAME
JellyfinDataT = TypeVar( JellyfinDataT = TypeVar(
"JellyfinDataT", "JellyfinDataT",
@ -29,6 +29,7 @@ class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[JellyfinDataT], ABC):
hass: HomeAssistant, hass: HomeAssistant,
api_client: JellyfinClient, api_client: JellyfinClient,
system_info: dict[str, Any], system_info: dict[str, Any],
client_device_id: str,
user_id: str, user_id: str,
) -> None: ) -> None:
"""Initialize the coordinator.""" """Initialize the coordinator."""
@ -42,8 +43,11 @@ class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[JellyfinDataT], ABC):
self.server_id: str = system_info["Id"] self.server_id: str = system_info["Id"]
self.server_name: str = system_info["Name"] self.server_name: str = system_info["Name"]
self.server_version: str | None = system_info.get("Version") self.server_version: str | None = system_info.get("Version")
self.client_device_id: str = client_device_id
self.user_id: str = user_id self.user_id: str = user_id
self.session_ids: set[str] = set()
async def _async_update_data(self) -> JellyfinDataT: async def _async_update_data(self) -> JellyfinDataT:
"""Get the latest data from Jellyfin.""" """Get the latest data from Jellyfin."""
return await self._fetch_data() return await self._fetch_data()
@ -65,7 +69,10 @@ class SessionsDataUpdateCoordinator(
) )
sessions_by_id: dict[str, dict[str, Any]] = { sessions_by_id: dict[str, dict[str, Any]] = {
session["Id"]: session for session in sessions session["Id"]: session
for session in sessions
if session["DeviceId"] != self.client_device_id
and session["Client"] != USER_APP_NAME
} }
return sessions_by_id return sessions_by_id

View File

@ -19,7 +19,7 @@ from homeassistant.util.dt import parse_datetime
from .browse_media import build_item_response, build_root_response from .browse_media import build_item_response, build_root_response
from .client_wrapper import get_artwork_url from .client_wrapper import get_artwork_url
from .const import CONTENT_TYPE_MAP, DOMAIN, USER_APP_NAME from .const import CONTENT_TYPE_MAP, DOMAIN, LOGGER
from .coordinator import JellyfinDataUpdateCoordinator from .coordinator import JellyfinDataUpdateCoordinator
from .entity import JellyfinEntity from .entity import JellyfinEntity
from .models import JellyfinData from .models import JellyfinData
@ -34,14 +34,23 @@ async def async_setup_entry(
jellyfin_data: JellyfinData = hass.data[DOMAIN][entry.entry_id] jellyfin_data: JellyfinData = hass.data[DOMAIN][entry.entry_id]
coordinator = jellyfin_data.coordinators["sessions"] coordinator = jellyfin_data.coordinators["sessions"]
async_add_entities( @callback
( def handle_coordinator_update() -> None:
JellyfinMediaPlayer(coordinator, session_id, session_data) """Add media player per session."""
for session_id, session_data in coordinator.data.items() entities: list[MediaPlayerEntity] = []
if session_data["DeviceId"] != jellyfin_data.client_device_id for session_id, session_data in coordinator.data.items():
and session_data["Client"] != USER_APP_NAME if session_id not in coordinator.session_ids:
), entity: MediaPlayerEntity = JellyfinMediaPlayer(
coordinator, session_id, session_data
) )
LOGGER.debug("Creating media player for session: %s", session_id)
coordinator.session_ids.add(session_id)
entities.append(entity)
async_add_entities(entities)
handle_coordinator_update()
entry.async_on_unload(coordinator.async_add_listener(handle_coordinator_update))
class JellyfinMediaPlayer(JellyfinEntity, MediaPlayerEntity): class JellyfinMediaPlayer(JellyfinEntity, MediaPlayerEntity):

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,8 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from . import async_load_json_fixture
from tests.common import MockConfigEntry, async_fire_time_changed from tests.common import MockConfigEntry, async_fire_time_changed
from tests.typing import WebSocketGenerator from tests.typing import WebSocketGenerator
@ -353,3 +355,24 @@ async def test_browse_media(
response["error"]["message"] response["error"]["message"]
== "Media not found: collection / COLLECTION-UUID-404" == "Media not found: collection / COLLECTION-UUID-404"
) )
async def test_new_client_connected(
hass: HomeAssistant,
init_integration: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_api: MagicMock,
) -> None:
"""Test Jellyfin media player reacts to new clients connecting."""
mock_api.sessions.return_value = await async_load_json_fixture(
hass,
"sessions-new-client.json",
)
assert len(mock_api.sessions.mock_calls) == 1
async_fire_time_changed(hass, utcnow() + timedelta(seconds=10))
await hass.async_block_till_done()
assert len(mock_api.sessions.mock_calls) == 2
state = hass.states.get("media_player.jellyfin_device_five")
assert state