Use runtime_data instead of hass.data for Jellyfin (#122410)
* Use runtime_data instead of hass.data * Process reviewpull/122693/head
parent
6dd43be6ac
commit
99aa68c93f
|
@ -12,11 +12,11 @@ from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, PLATFORMS
|
||||||
from .coordinator import JellyfinDataUpdateCoordinator, SessionsDataUpdateCoordinator
|
from .coordinator import JellyfinDataUpdateCoordinator, SessionsDataUpdateCoordinator
|
||||||
from .models import JellyfinData
|
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."""
|
"""Set up Jellyfin from a config entry."""
|
||||||
hass.data.setdefault(DOMAIN, {})
|
|
||||||
|
|
||||||
if CONF_CLIENT_DEVICE_ID not in entry.data:
|
if CONF_CLIENT_DEVICE_ID not in entry.data:
|
||||||
entry_data = entry.data.copy()
|
entry_data = entry.data.copy()
|
||||||
entry_data[CONF_CLIENT_DEVICE_ID] = entry.entry_id
|
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():
|
for coordinator in coordinators.values():
|
||||||
await coordinator.async_config_entry_first_refresh()
|
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],
|
client_device_id=entry.data[CONF_CLIENT_DEVICE_ID],
|
||||||
jellyfin_client=client,
|
jellyfin_client=client,
|
||||||
coordinators=coordinators,
|
coordinators=coordinators,
|
||||||
|
@ -56,21 +56,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
return True
|
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."""
|
"""Unload a config entry."""
|
||||||
unloaded = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
unloaded = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
if unloaded:
|
if unloaded:
|
||||||
data = hass.data[DOMAIN].pop(entry.entry_id)
|
entry.runtime_data.jellyfin_client.stop()
|
||||||
data.jellyfin_client.stop()
|
|
||||||
|
|
||||||
return unloaded
|
return unloaded
|
||||||
|
|
||||||
|
|
||||||
async def async_remove_config_entry_device(
|
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:
|
) -> bool:
|
||||||
"""Remove device from a config entry."""
|
"""Remove device from a config entry."""
|
||||||
data = hass.data[DOMAIN][config_entry.entry_id]
|
data = config_entry.runtime_data
|
||||||
coordinator = data.coordinators["sessions"]
|
coordinator = data.coordinators["sessions"]
|
||||||
|
|
||||||
return not device_entry.identifiers.intersection(
|
return not device_entry.identifiers.intersection(
|
||||||
|
|
|
@ -9,15 +9,15 @@ from typing import Any
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import (
|
from homeassistant.config_entries import (
|
||||||
ConfigEntry,
|
|
||||||
ConfigFlow,
|
ConfigFlow,
|
||||||
ConfigFlowResult,
|
ConfigFlowResult,
|
||||||
OptionsFlow,
|
OptionsFlowWithConfigEntry,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
|
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.util.uuid import random_uuid_hex
|
from homeassistant.util.uuid import random_uuid_hex
|
||||||
|
|
||||||
|
from . import JellyfinConfigEntry
|
||||||
from .client_wrapper import CannotConnect, InvalidAuth, create_client, validate_input
|
from .client_wrapper import CannotConnect, InvalidAuth, create_client, validate_input
|
||||||
from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, SUPPORTED_AUDIO_CODECS
|
from .const import CONF_CLIENT_DEVICE_ID, DOMAIN, SUPPORTED_AUDIO_CODECS
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class JellyfinConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
"""Initialize the Jellyfin config flow."""
|
"""Initialize the Jellyfin config flow."""
|
||||||
self.client_device_id: str | None = None
|
self.client_device_id: str | None = None
|
||||||
self.entry: ConfigEntry | None = None
|
self.entry: JellyfinConfigEntry | None = None
|
||||||
|
|
||||||
async def async_step_user(
|
async def async_step_user(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
@ -146,18 +146,16 @@ class JellyfinConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@callback
|
@callback
|
||||||
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
|
def async_get_options_flow(
|
||||||
|
config_entry: JellyfinConfigEntry,
|
||||||
|
) -> OptionsFlowWithConfigEntry:
|
||||||
"""Create the options flow."""
|
"""Create the options flow."""
|
||||||
return OptionsFlowHandler(config_entry)
|
return OptionsFlowHandler(config_entry)
|
||||||
|
|
||||||
|
|
||||||
class OptionsFlowHandler(OptionsFlow):
|
class OptionsFlowHandler(OptionsFlowWithConfigEntry):
|
||||||
"""Handle an option flow for jellyfin."""
|
"""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(
|
async def async_step_init(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
|
|
|
@ -8,7 +8,6 @@ from typing import Any, TypeVar
|
||||||
|
|
||||||
from jellyfin_apiclient_python import JellyfinClient
|
from jellyfin_apiclient_python import JellyfinClient
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -23,8 +22,6 @@ JellyfinDataT = TypeVar(
|
||||||
class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[JellyfinDataT], ABC):
|
class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[JellyfinDataT], ABC):
|
||||||
"""Data update coordinator for the Jellyfin integration."""
|
"""Data update coordinator for the Jellyfin integration."""
|
||||||
|
|
||||||
config_entry: ConfigEntry
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
|
|
@ -5,21 +5,19 @@ from __future__ import annotations
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.components.diagnostics import async_redact_data
|
from homeassistant.components.diagnostics import async_redact_data
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import CONF_PASSWORD
|
from homeassistant.const import CONF_PASSWORD
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .const import DOMAIN
|
from . import JellyfinConfigEntry
|
||||||
from .models import JellyfinData
|
|
||||||
|
|
||||||
TO_REDACT = {CONF_PASSWORD}
|
TO_REDACT = {CONF_PASSWORD}
|
||||||
|
|
||||||
|
|
||||||
async def async_get_config_entry_diagnostics(
|
async def async_get_config_entry_diagnostics(
|
||||||
hass: HomeAssistant, entry: ConfigEntry
|
hass: HomeAssistant, entry: JellyfinConfigEntry
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return diagnostics for a config entry."""
|
"""Return diagnostics for a config entry."""
|
||||||
data: JellyfinData = hass.data[DOMAIN][entry.entry_id]
|
data = entry.runtime_data
|
||||||
sessions = data.coordinators["sessions"]
|
sessions = data.coordinators["sessions"]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -12,27 +12,26 @@ from homeassistant.components.media_player import (
|
||||||
MediaType,
|
MediaType,
|
||||||
)
|
)
|
||||||
from homeassistant.components.media_player.browse_media import BrowseMedia
|
from homeassistant.components.media_player.browse_media import BrowseMedia
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.util.dt import parse_datetime
|
from homeassistant.util.dt import parse_datetime
|
||||||
|
|
||||||
|
from . import JellyfinConfigEntry
|
||||||
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, LOGGER
|
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
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: JellyfinConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Jellyfin media_player from a config entry."""
|
"""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"]
|
coordinator = jellyfin_data.coordinators["sessions"]
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
|
|
@ -17,9 +17,9 @@ from homeassistant.components.media_source.models import (
|
||||||
MediaSourceItem,
|
MediaSourceItem,
|
||||||
PlayMedia,
|
PlayMedia,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from . import JellyfinConfigEntry
|
||||||
from .const import (
|
from .const import (
|
||||||
COLLECTION_TYPE_MOVIES,
|
COLLECTION_TYPE_MOVIES,
|
||||||
COLLECTION_TYPE_MUSIC,
|
COLLECTION_TYPE_MUSIC,
|
||||||
|
@ -48,7 +48,6 @@ from .const import (
|
||||||
PLAYABLE_ITEM_TYPES,
|
PLAYABLE_ITEM_TYPES,
|
||||||
SUPPORTED_COLLECTION_TYPES,
|
SUPPORTED_COLLECTION_TYPES,
|
||||||
)
|
)
|
||||||
from .models import JellyfinData
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -56,8 +55,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
async def async_get_media_source(hass: HomeAssistant) -> MediaSource:
|
async def async_get_media_source(hass: HomeAssistant) -> MediaSource:
|
||||||
"""Set up Jellyfin media source."""
|
"""Set up Jellyfin media source."""
|
||||||
# Currently only a single Jellyfin server is supported
|
# Currently only a single Jellyfin server is supported
|
||||||
entry = hass.config_entries.async_entries(DOMAIN)[0]
|
entry: JellyfinConfigEntry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
jellyfin_data: JellyfinData = hass.data[DOMAIN][entry.entry_id]
|
jellyfin_data = entry.runtime_data
|
||||||
|
|
||||||
return JellyfinSource(hass, jellyfin_data.jellyfin_client, entry)
|
return JellyfinSource(hass, jellyfin_data.jellyfin_client, entry)
|
||||||
|
|
||||||
|
@ -68,7 +67,7 @@ class JellyfinSource(MediaSource):
|
||||||
name: str = "Jellyfin"
|
name: str = "Jellyfin"
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, hass: HomeAssistant, client: JellyfinClient, entry: ConfigEntry
|
self, hass: HomeAssistant, client: JellyfinClient, entry: JellyfinConfigEntry
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Jellyfin media source."""
|
"""Initialize the Jellyfin media source."""
|
||||||
super().__init__(DOMAIN)
|
super().__init__(DOMAIN)
|
||||||
|
|
|
@ -6,15 +6,13 @@ from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
from .const import DOMAIN
|
from . import JellyfinConfigEntry
|
||||||
from .coordinator import JellyfinDataT
|
from .coordinator import JellyfinDataT
|
||||||
from .entity import JellyfinEntity
|
from .entity import JellyfinEntity
|
||||||
from .models import JellyfinData
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
@ -46,14 +44,14 @@ SENSOR_TYPES: dict[str, JellyfinSensorEntityDescription] = {
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: JellyfinConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Jellyfin sensor based on a config entry."""
|
"""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(
|
async_add_entities(
|
||||||
JellyfinSensor(jellyfin_data.coordinators[coordinator_type], description)
|
JellyfinSensor(data.coordinators[coordinator_type], description)
|
||||||
for coordinator_type, description in SENSOR_TYPES.items()
|
for coordinator_type, description in SENSOR_TYPES.items()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -68,12 +68,10 @@ async def test_load_unload_config_entry(
|
||||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert mock_config_entry.entry_id in hass.data[DOMAIN]
|
|
||||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
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
|
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue