Improve Plex device handling (#48369)
This commit is contained in:
parent
2f37a5727c
commit
be71d626c8
4 changed files with 178 additions and 0 deletions
|
@ -24,6 +24,7 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dev_reg, entity_registry as ent_reg
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.debounce import Debouncer
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
|
@ -221,6 +222,8 @@ async def async_setup_entry(hass, entry):
|
|||
)
|
||||
task.add_done_callback(partial(start_websocket_session, platform))
|
||||
|
||||
async_cleanup_plex_devices(hass, entry)
|
||||
|
||||
def get_plex_account(plex_server):
|
||||
try:
|
||||
return plex_server.account
|
||||
|
@ -261,3 +264,30 @@ async def async_options_updated(hass, entry):
|
|||
# Guard incomplete setup during reauth flows
|
||||
if server_id in hass.data[PLEX_DOMAIN][SERVERS]:
|
||||
hass.data[PLEX_DOMAIN][SERVERS][server_id].options = entry.options
|
||||
|
||||
|
||||
@callback
|
||||
def async_cleanup_plex_devices(hass, entry):
|
||||
"""Clean up old and invalid devices from the registry."""
|
||||
device_registry = dev_reg.async_get(hass)
|
||||
entity_registry = ent_reg.async_get(hass)
|
||||
|
||||
device_entries = hass.helpers.device_registry.async_entries_for_config_entry(
|
||||
device_registry, entry.entry_id
|
||||
)
|
||||
|
||||
for device_entry in device_entries:
|
||||
if (
|
||||
len(
|
||||
hass.helpers.entity_registry.async_entries_for_device(
|
||||
entity_registry, device_entry.id, include_disabled_entities=True
|
||||
)
|
||||
)
|
||||
== 0
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"Removing orphaned device: %s / %s",
|
||||
device_entry.name,
|
||||
device_entry.identifiers,
|
||||
)
|
||||
device_registry.async_remove_device(device_entry.id)
|
||||
|
|
|
@ -4,6 +4,7 @@ from homeassistant.const import __version__
|
|||
DOMAIN = "plex"
|
||||
NAME_FORMAT = "Plex ({})"
|
||||
COMMON_PLAYERS = ["Plex Web"]
|
||||
TRANSIENT_DEVICE_MODELS = ["Plex Web", "Plex for Sonos"]
|
||||
|
||||
DEFAULT_PORT = 32400
|
||||
DEFAULT_SSL = False
|
||||
|
|
|
@ -41,6 +41,7 @@ from .const import (
|
|||
PLEX_UPDATE_MEDIA_PLAYER_SIGNAL,
|
||||
PLEX_UPDATE_SENSOR_SIGNAL,
|
||||
SERVERS,
|
||||
TRANSIENT_DEVICE_MODELS,
|
||||
)
|
||||
from .media_browser import browse_media
|
||||
|
||||
|
@ -544,6 +545,15 @@ class PlexMediaPlayer(MediaPlayerEntity):
|
|||
if self.machine_identifier is None:
|
||||
return None
|
||||
|
||||
if self.device_product in TRANSIENT_DEVICE_MODELS:
|
||||
return {
|
||||
"identifiers": {(PLEX_DOMAIN, "plex.tv-clients")},
|
||||
"name": "Plex Client Service",
|
||||
"manufacturer": "Plex",
|
||||
"model": "Plex Clients",
|
||||
"entry_type": "service",
|
||||
}
|
||||
|
||||
return {
|
||||
"identifiers": {(PLEX_DOMAIN, self.machine_identifier)},
|
||||
"manufacturer": self.device_platform or "Plex",
|
||||
|
|
137
tests/components/plex/test_device_handling.py
Normal file
137
tests/components/plex/test_device_handling.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
"""Tests for handling the device registry."""
|
||||
|
||||
from homeassistant.components.media_player.const import DOMAIN as MP_DOMAIN
|
||||
from homeassistant.components.plex.const import DOMAIN
|
||||
|
||||
|
||||
async def test_cleanup_orphaned_devices(hass, entry, setup_plex_server):
|
||||
"""Test cleaning up orphaned devices on startup."""
|
||||
test_device_id = {(DOMAIN, "temporary_device_123")}
|
||||
|
||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
test_device = device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers=test_device_id,
|
||||
)
|
||||
assert test_device is not None
|
||||
|
||||
test_entity = entity_registry.async_get_or_create(
|
||||
MP_DOMAIN, DOMAIN, "entity_unique_id_123", device_id=test_device.id
|
||||
)
|
||||
assert test_entity is not None
|
||||
|
||||
# Ensure device is not removed with an entity
|
||||
await setup_plex_server()
|
||||
device = device_registry.async_get_device(identifiers=test_device_id)
|
||||
assert device is not None
|
||||
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
|
||||
# Ensure device is removed without an entity
|
||||
entity_registry.async_remove(test_entity.entity_id)
|
||||
await setup_plex_server()
|
||||
device = device_registry.async_get_device(identifiers=test_device_id)
|
||||
assert device is None
|
||||
|
||||
|
||||
async def test_migrate_transient_devices(
|
||||
hass, entry, setup_plex_server, requests_mock, player_plexweb_resources
|
||||
):
|
||||
"""Test cleaning up transient devices on startup."""
|
||||
plexweb_device_id = {(DOMAIN, "plexweb_id")}
|
||||
non_plexweb_device_id = {(DOMAIN, "1234567890123456-com-plexapp-android")}
|
||||
plex_client_service_device_id = {(DOMAIN, "plex.tv-clients")}
|
||||
|
||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
# Pre-create devices and entities to test device migration
|
||||
plexweb_device = device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers=plexweb_device_id,
|
||||
model="Plex Web",
|
||||
)
|
||||
# plexweb_entity = entity_registry.async_get_or_create(MP_DOMAIN, DOMAIN, "unique_id_123:plexweb_id", suggested_object_id="plex_plex_web_chrome", device_id=plexweb_device.id)
|
||||
entity_registry.async_get_or_create(
|
||||
MP_DOMAIN,
|
||||
DOMAIN,
|
||||
"unique_id_123:plexweb_id",
|
||||
suggested_object_id="plex_plex_web_chrome",
|
||||
device_id=plexweb_device.id,
|
||||
)
|
||||
|
||||
non_plexweb_device = device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers=non_plexweb_device_id,
|
||||
model="Plex for Android (TV)",
|
||||
)
|
||||
entity_registry.async_get_or_create(
|
||||
MP_DOMAIN,
|
||||
DOMAIN,
|
||||
"unique_id_123:1234567890123456-com-plexapp-android",
|
||||
suggested_object_id="plex_plex_for_android_tv_shield_android_tv",
|
||||
device_id=non_plexweb_device.id,
|
||||
)
|
||||
|
||||
# Ensure the Plex Web client is available
|
||||
requests_mock.get("/resources", text=player_plexweb_resources)
|
||||
|
||||
plexweb_device = device_registry.async_get_device(identifiers=plexweb_device_id)
|
||||
non_plexweb_device = device_registry.async_get_device(
|
||||
identifiers=non_plexweb_device_id
|
||||
)
|
||||
plex_service_device = device_registry.async_get_device(
|
||||
identifiers=plex_client_service_device_id
|
||||
)
|
||||
|
||||
assert (
|
||||
len(
|
||||
hass.helpers.entity_registry.async_entries_for_device(
|
||||
entity_registry, device_id=plexweb_device.id
|
||||
)
|
||||
)
|
||||
== 1
|
||||
)
|
||||
assert (
|
||||
len(
|
||||
hass.helpers.entity_registry.async_entries_for_device(
|
||||
entity_registry, device_id=non_plexweb_device.id
|
||||
)
|
||||
)
|
||||
== 1
|
||||
)
|
||||
assert plex_service_device is None
|
||||
|
||||
# Ensure Plex Web entity is migrated to a service
|
||||
await setup_plex_server()
|
||||
|
||||
plex_service_device = device_registry.async_get_device(
|
||||
identifiers=plex_client_service_device_id
|
||||
)
|
||||
|
||||
assert (
|
||||
len(
|
||||
hass.helpers.entity_registry.async_entries_for_device(
|
||||
entity_registry, device_id=plexweb_device.id
|
||||
)
|
||||
)
|
||||
== 0
|
||||
)
|
||||
assert (
|
||||
len(
|
||||
hass.helpers.entity_registry.async_entries_for_device(
|
||||
entity_registry, device_id=non_plexweb_device.id
|
||||
)
|
||||
)
|
||||
== 1
|
||||
)
|
||||
assert (
|
||||
len(
|
||||
hass.helpers.entity_registry.async_entries_for_device(
|
||||
entity_registry, device_id=plex_service_device.id
|
||||
)
|
||||
)
|
||||
== 1
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue