Rework Synology DSM to use config entry runtime_data (#141084)

rework to use config entry runtime_data
This commit is contained in:
Michael 2025-03-23 12:05:40 +01:00 committed by GitHub
parent d8a5881eaa
commit 489c486278
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 110 additions and 106 deletions

View File

@ -9,7 +9,6 @@ from synology_dsm.api.surveillance_station import SynoSurveillanceStation
from synology_dsm.api.surveillance_station.camera import SynoCamera from synology_dsm.api.surveillance_station.camera import SynoCamera
from synology_dsm.exceptions import SynologyDSMNotLoggedInException from synology_dsm.exceptions import SynologyDSMNotLoggedInException
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_MAC, CONF_SCAN_INTERVAL, CONF_VERIFY_SSL from homeassistant.const import CONF_MAC, CONF_SCAN_INTERVAL, CONF_VERIFY_SSL
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
@ -31,15 +30,16 @@ from .const import (
from .coordinator import ( from .coordinator import (
SynologyDSMCameraUpdateCoordinator, SynologyDSMCameraUpdateCoordinator,
SynologyDSMCentralUpdateCoordinator, SynologyDSMCentralUpdateCoordinator,
SynologyDSMConfigEntry,
SynologyDSMData,
SynologyDSMSwitchUpdateCoordinator, SynologyDSMSwitchUpdateCoordinator,
) )
from .models import SynologyDSMData
from .service import async_setup_services from .service import async_setup_services
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: SynologyDSMConfigEntry) -> bool:
"""Set up Synology DSM sensors.""" """Set up Synology DSM sensors."""
# Migrate device identifiers # Migrate device identifiers
@ -120,13 +120,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except SYNOLOGY_CONNECTION_EXCEPTIONS as ex: except SYNOLOGY_CONNECTION_EXCEPTIONS as ex:
raise ConfigEntryNotReady from ex raise ConfigEntryNotReady from ex
synology_data = SynologyDSMData( entry.runtime_data = SynologyDSMData(
api=api, api=api,
coordinator_central=coordinator_central, coordinator_central=coordinator_central,
coordinator_cameras=coordinator_cameras, coordinator_cameras=coordinator_cameras,
coordinator_switches=coordinator_switches, coordinator_switches=coordinator_switches,
) )
hass.data.setdefault(DOMAIN, {})[entry.unique_id] = synology_data
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(_async_update_listener)) entry.async_on_unload(entry.add_update_listener(_async_update_listener))
@ -143,25 +142,28 @@ 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: SynologyDSMConfigEntry
) -> bool:
"""Unload Synology DSM sensors.""" """Unload Synology DSM sensors."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
entry_data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id] entry_data = entry.runtime_data
await entry_data.api.async_unload() await entry_data.api.async_unload()
hass.data[DOMAIN].pop(entry.unique_id)
return unload_ok return unload_ok
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: async def _async_update_listener(
hass: HomeAssistant, entry: SynologyDSMConfigEntry
) -> None:
"""Handle options update.""" """Handle options update."""
await hass.config_entries.async_reload(entry.entry_id) await hass.config_entries.async_reload(entry.entry_id)
async def async_remove_config_entry_device( async def async_remove_config_entry_device(
hass: HomeAssistant, entry: ConfigEntry, device_entry: dr.DeviceEntry hass: HomeAssistant, entry: SynologyDSMConfigEntry, device_entry: dr.DeviceEntry
) -> bool: ) -> bool:
"""Remove synology_dsm config entry from a device.""" """Remove synology_dsm config entry from a device."""
data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id] data = entry.runtime_data
api = data.api api = data.api
assert api.information is not None assert api.information is not None
serial = api.information.serial serial = api.information.serial

View File

@ -17,7 +17,6 @@ from homeassistant.components.backup import (
BackupNotFound, BackupNotFound,
suggested_filename, suggested_filename,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.aiohttp_client import ChunkAsyncStreamIterator from homeassistant.helpers.aiohttp_client import ChunkAsyncStreamIterator
from homeassistant.helpers.json import json_dumps from homeassistant.helpers.json import json_dumps
@ -29,7 +28,7 @@ from .const import (
DATA_BACKUP_AGENT_LISTENERS, DATA_BACKUP_AGENT_LISTENERS,
DOMAIN, DOMAIN,
) )
from .models import SynologyDSMData from .coordinator import SynologyDSMConfigEntry
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -47,18 +46,17 @@ async def async_get_backup_agents(
hass: HomeAssistant, hass: HomeAssistant,
) -> list[BackupAgent]: ) -> list[BackupAgent]:
"""Return a list of backup agents.""" """Return a list of backup agents."""
if not ( entries: list[SynologyDSMConfigEntry] = hass.config_entries.async_loaded_entries(
entries := hass.config_entries.async_loaded_entries(DOMAIN) DOMAIN
) or not hass.data.get(DOMAIN): )
if not entries:
LOGGER.debug("No proper config entry found") LOGGER.debug("No proper config entry found")
return [] return []
syno_datas: dict[str, SynologyDSMData] = hass.data[DOMAIN]
return [ return [
SynologyDSMBackupAgent(hass, entry, entry.unique_id) SynologyDSMBackupAgent(hass, entry, entry.unique_id)
for entry in entries for entry in entries
if entry.unique_id is not None if entry.unique_id is not None
and (syno_data := syno_datas.get(entry.unique_id)) and entry.runtime_data.api.file_station
and syno_data.api.file_station
and entry.options.get(CONF_BACKUP_PATH) and entry.options.get(CONF_BACKUP_PATH)
] ]
@ -91,7 +89,9 @@ class SynologyDSMBackupAgent(BackupAgent):
domain = DOMAIN domain = DOMAIN
def __init__(self, hass: HomeAssistant, entry: ConfigEntry, unique_id: str) -> None: def __init__(
self, hass: HomeAssistant, entry: SynologyDSMConfigEntry, unique_id: str
) -> None:
"""Initialize the Synology DSM backup agent.""" """Initialize the Synology DSM backup agent."""
super().__init__() super().__init__()
LOGGER.debug("Initializing Synology DSM backup agent for %s", entry.unique_id) LOGGER.debug("Initializing Synology DSM backup agent for %s", entry.unique_id)
@ -100,7 +100,7 @@ class SynologyDSMBackupAgent(BackupAgent):
self.path = ( self.path = (
f"{entry.options[CONF_BACKUP_SHARE]}/{entry.options[CONF_BACKUP_PATH]}" f"{entry.options[CONF_BACKUP_SHARE]}/{entry.options[CONF_BACKUP_PATH]}"
) )
syno_data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id] syno_data = entry.runtime_data
self.api = syno_data.api self.api = syno_data.api
self.backup_base_names: dict[str, str] = {} self.backup_base_names: dict[str, str] = {}

View File

@ -12,20 +12,17 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity, BinarySensorEntity,
BinarySensorEntityDescription, BinarySensorEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DISKS, EntityCategory from homeassistant.const import CONF_DISKS, EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import SynoApi from . import SynoApi
from .const import DOMAIN from .coordinator import SynologyDSMCentralUpdateCoordinator, SynologyDSMConfigEntry
from .coordinator import SynologyDSMCentralUpdateCoordinator
from .entity import ( from .entity import (
SynologyDSMBaseEntity, SynologyDSMBaseEntity,
SynologyDSMDeviceEntity, SynologyDSMDeviceEntity,
SynologyDSMEntityDescription, SynologyDSMEntityDescription,
) )
from .models import SynologyDSMData
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -64,11 +61,11 @@ STORAGE_DISK_BINARY_SENSORS: tuple[SynologyDSMBinarySensorEntityDescription, ...
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SynologyDSMConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the Synology NAS binary sensor.""" """Set up the Synology NAS binary sensor."""
data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id] data = entry.runtime_data
api = data.api api = data.api
coordinator = data.coordinator_central coordinator = data.coordinator_central
assert api.storage is not None assert api.storage is not None

View File

@ -12,7 +12,6 @@ from homeassistant.components.button import (
ButtonEntity, ButtonEntity,
ButtonEntityDescription, ButtonEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
@ -20,7 +19,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import SynoApi from . import SynoApi
from .const import DOMAIN from .const import DOMAIN
from .models import SynologyDSMData from .coordinator import SynologyDSMConfigEntry
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -52,11 +51,11 @@ BUTTONS: Final = [
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SynologyDSMConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set buttons for device.""" """Set buttons for device."""
data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id] data = entry.runtime_data
async_add_entities(SynologyDSMButton(data.api, button) for button in BUTTONS) async_add_entities(SynologyDSMButton(data.api, button) for button in BUTTONS)

View File

@ -16,7 +16,6 @@ from homeassistant.components.camera import (
CameraEntityDescription, CameraEntityDescription,
CameraEntityFeature, CameraEntityFeature,
) )
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.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -29,9 +28,8 @@ from .const import (
DOMAIN, DOMAIN,
SIGNAL_CAMERA_SOURCE_CHANGED, SIGNAL_CAMERA_SOURCE_CHANGED,
) )
from .coordinator import SynologyDSMCameraUpdateCoordinator from .coordinator import SynologyDSMCameraUpdateCoordinator, SynologyDSMConfigEntry
from .entity import SynologyDSMBaseEntity, SynologyDSMEntityDescription from .entity import SynologyDSMBaseEntity, SynologyDSMEntityDescription
from .models import SynologyDSMData
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -47,11 +45,11 @@ class SynologyDSMCameraEntityDescription(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SynologyDSMConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the Synology NAS cameras.""" """Set up the Synology NAS cameras."""
data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id] data = entry.runtime_data
if coordinator := data.coordinator_cameras: if coordinator := data.coordinator_cameras:
async_add_entities( async_add_entities(
SynoDSMCamera(data.api, coordinator, camera_id) SynoDSMCamera(data.api, coordinator, camera_id)

View File

@ -72,7 +72,7 @@ from .const import (
DOMAIN, DOMAIN,
SYNOLOGY_CONNECTION_EXCEPTIONS, SYNOLOGY_CONNECTION_EXCEPTIONS,
) )
from .models import SynologyDSMData from .coordinator import SynologyDSMConfigEntry
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -131,7 +131,7 @@ class SynologyDSMFlowHandler(ConfigFlow, domain=DOMAIN):
@staticmethod @staticmethod
@callback @callback
def async_get_options_flow( def async_get_options_flow(
config_entry: ConfigEntry, config_entry: SynologyDSMConfigEntry,
) -> SynologyDSMOptionsFlowHandler: ) -> SynologyDSMOptionsFlowHandler:
"""Get the options flow for this handler.""" """Get the options flow for this handler."""
return SynologyDSMOptionsFlowHandler() return SynologyDSMOptionsFlowHandler()
@ -444,6 +444,8 @@ class SynologyDSMFlowHandler(ConfigFlow, domain=DOMAIN):
class SynologyDSMOptionsFlowHandler(OptionsFlow): class SynologyDSMOptionsFlowHandler(OptionsFlow):
"""Handle a option flow.""" """Handle a option flow."""
config_entry: SynologyDSMConfigEntry
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:
@ -451,7 +453,7 @@ class SynologyDSMOptionsFlowHandler(OptionsFlow):
if user_input is not None: if user_input is not None:
return self.async_create_entry(title="", data=user_input) return self.async_create_entry(title="", data=user_input)
syno_data: SynologyDSMData = self.hass.data[DOMAIN][self.config_entry.unique_id] syno_data = self.config_entry.runtime_data
data_schema = vol.Schema( data_schema = vol.Schema(
{ {

View File

@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Awaitable, Callable, Coroutine from collections.abc import Awaitable, Callable, Coroutine
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
import logging import logging
from typing import Any, Concatenate from typing import Any, Concatenate
@ -28,6 +29,19 @@ from .const import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@dataclass
class SynologyDSMData:
"""Data for the synology_dsm integration."""
api: SynoApi
coordinator_central: SynologyDSMCentralUpdateCoordinator
coordinator_cameras: SynologyDSMCameraUpdateCoordinator | None
coordinator_switches: SynologyDSMSwitchUpdateCoordinator | None
type SynologyDSMConfigEntry = ConfigEntry[SynologyDSMData]
def async_re_login_on_expired[_T: SynologyDSMUpdateCoordinator[Any], **_P, _R]( def async_re_login_on_expired[_T: SynologyDSMUpdateCoordinator[Any], **_P, _R](
func: Callable[Concatenate[_T, _P], Awaitable[_R]], func: Callable[Concatenate[_T, _P], Awaitable[_R]],
) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R]]: ) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R]]:
@ -57,12 +71,12 @@ def async_re_login_on_expired[_T: SynologyDSMUpdateCoordinator[Any], **_P, _R](
class SynologyDSMUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT]): class SynologyDSMUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
"""DataUpdateCoordinator base class for synology_dsm.""" """DataUpdateCoordinator base class for synology_dsm."""
config_entry: ConfigEntry config_entry: SynologyDSMConfigEntry
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SynologyDSMConfigEntry,
api: SynoApi, api: SynoApi,
update_interval: timedelta, update_interval: timedelta,
) -> None: ) -> None:
@ -85,7 +99,7 @@ class SynologyDSMSwitchUpdateCoordinator(
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SynologyDSMConfigEntry,
api: SynoApi, api: SynoApi,
) -> None: ) -> None:
"""Initialize DataUpdateCoordinator for switch devices.""" """Initialize DataUpdateCoordinator for switch devices."""
@ -116,7 +130,7 @@ class SynologyDSMCentralUpdateCoordinator(SynologyDSMUpdateCoordinator[None]):
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SynologyDSMConfigEntry,
api: SynoApi, api: SynoApi,
) -> None: ) -> None:
"""Initialize DataUpdateCoordinator for central device.""" """Initialize DataUpdateCoordinator for central device."""
@ -136,7 +150,7 @@ class SynologyDSMCameraUpdateCoordinator(
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SynologyDSMConfigEntry,
api: SynoApi, api: SynoApi,
) -> None: ) -> None:
"""Initialize DataUpdateCoordinator for cameras.""" """Initialize DataUpdateCoordinator for cameras."""

View File

@ -6,21 +6,20 @@ from typing import Any
from homeassistant.components.camera import diagnostics as camera_diagnostics from homeassistant.components.camera import diagnostics as camera_diagnostics
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, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import CONF_DEVICE_TOKEN, DOMAIN from .const import CONF_DEVICE_TOKEN
from .models import SynologyDSMData from .coordinator import SynologyDSMConfigEntry
TO_REDACT = {CONF_USERNAME, CONF_PASSWORD, CONF_DEVICE_TOKEN} TO_REDACT = {CONF_USERNAME, CONF_PASSWORD, CONF_DEVICE_TOKEN}
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry hass: HomeAssistant, entry: SynologyDSMConfigEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a config entry.""" """Return diagnostics for a config entry."""
data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id] data = entry.runtime_data
syno_api = data.api syno_api = data.api
dsm_info = syno_api.dsm.information dsm_info = syno_api.dsm.information

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
from logging import getLogger
import mimetypes import mimetypes
from aiohttp import web from aiohttp import web
@ -22,7 +23,9 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import DOMAIN, SHARED_SUFFIX from .const import DOMAIN, SHARED_SUFFIX
from .models import SynologyDSMData from .coordinator import SynologyDSMConfigEntry, SynologyDSMData
LOGGER = getLogger(__name__)
async def async_get_media_source(hass: HomeAssistant) -> MediaSource: async def async_get_media_source(hass: HomeAssistant) -> MediaSource:
@ -41,15 +44,13 @@ class SynologyPhotosMediaSourceIdentifier:
"""Split identifier into parts.""" """Split identifier into parts."""
parts = identifier.split("/") parts = identifier.split("/")
self.unique_id = None self.unique_id = parts[0]
self.album_id = None self.album_id = None
self.cache_key = None self.cache_key = None
self.file_name = None self.file_name = None
self.is_shared = False self.is_shared = False
self.passphrase = "" self.passphrase = ""
self.unique_id = parts[0]
if len(parts) > 1: if len(parts) > 1:
album_parts = parts[1].split("_") album_parts = parts[1].split("_")
self.album_id = album_parts[0] self.album_id = album_parts[0]
@ -82,7 +83,7 @@ class SynologyPhotosMediaSource(MediaSource):
item: MediaSourceItem, item: MediaSourceItem,
) -> BrowseMediaSource: ) -> BrowseMediaSource:
"""Return media.""" """Return media."""
if not self.hass.data.get(DOMAIN): if not self.hass.config_entries.async_loaded_entries(DOMAIN):
raise BrowseError("Diskstation not initialized") raise BrowseError("Diskstation not initialized")
return BrowseMediaSource( return BrowseMediaSource(
domain=DOMAIN, domain=DOMAIN,
@ -116,7 +117,13 @@ class SynologyPhotosMediaSource(MediaSource):
for entry in self.entries for entry in self.entries
] ]
identifier = SynologyPhotosMediaSourceIdentifier(item.identifier) identifier = SynologyPhotosMediaSourceIdentifier(item.identifier)
diskstation: SynologyDSMData = self.hass.data[DOMAIN][identifier.unique_id] entry: SynologyDSMConfigEntry | None = (
self.hass.config_entries.async_entry_for_domain_unique_id(
DOMAIN, identifier.unique_id
)
)
assert entry
diskstation = entry.runtime_data
assert diskstation.api.photos is not None assert diskstation.api.photos is not None
if identifier.album_id is None: if identifier.album_id is None:
@ -244,7 +251,7 @@ class SynologyDsmMediaView(http.HomeAssistantView):
self, request: web.Request, source_dir_id: str, location: str self, request: web.Request, source_dir_id: str, location: str
) -> web.Response: ) -> web.Response:
"""Start a GET request.""" """Start a GET request."""
if not self.hass.data.get(DOMAIN): if not self.hass.config_entries.async_loaded_entries(DOMAIN):
raise web.HTTPNotFound raise web.HTTPNotFound
# location: {cache_key}/{filename} # location: {cache_key}/{filename}
cache_key, file_name, passphrase = location.split("/") cache_key, file_name, passphrase = location.split("/")
@ -257,7 +264,13 @@ class SynologyDsmMediaView(http.HomeAssistantView):
if not isinstance(mime_type, str): if not isinstance(mime_type, str):
raise web.HTTPNotFound raise web.HTTPNotFound
diskstation: SynologyDSMData = self.hass.data[DOMAIN][source_dir_id] entry: SynologyDSMConfigEntry | None = (
self.hass.config_entries.async_entry_for_domain_unique_id(
DOMAIN, source_dir_id
)
)
assert entry
diskstation = entry.runtime_data
assert diskstation.api.photos is not None assert diskstation.api.photos is not None
item = SynoPhotosItem(image_id, "", "", "", cache_key, "xl", shared, passphrase) item = SynoPhotosItem(image_id, "", "", "", cache_key, "xl", shared, passphrase)
try: try:

View File

@ -1,22 +0,0 @@
"""The synology_dsm integration models."""
from __future__ import annotations
from dataclasses import dataclass
from .common import SynoApi
from .coordinator import (
SynologyDSMCameraUpdateCoordinator,
SynologyDSMCentralUpdateCoordinator,
SynologyDSMSwitchUpdateCoordinator,
)
@dataclass
class SynologyDSMData:
"""Data for the synology_dsm integration."""
api: SynoApi
coordinator_central: SynologyDSMCentralUpdateCoordinator
coordinator_cameras: SynologyDSMCameraUpdateCoordinator | None
coordinator_switches: SynologyDSMSwitchUpdateCoordinator | None

View File

@ -11,7 +11,6 @@ import voluptuous as vol
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.selector import ( from homeassistant.helpers.selector import (
@ -28,7 +27,7 @@ from .const import (
ISSUE_MISSING_BACKUP_SETUP, ISSUE_MISSING_BACKUP_SETUP,
SYNOLOGY_CONNECTION_EXCEPTIONS, SYNOLOGY_CONNECTION_EXCEPTIONS,
) )
from .models import SynologyDSMData from .coordinator import SynologyDSMConfigEntry
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -36,7 +35,7 @@ LOGGER = logging.getLogger(__name__)
class MissingBackupSetupRepairFlow(RepairsFlow): class MissingBackupSetupRepairFlow(RepairsFlow):
"""Handler for an issue fixing flow.""" """Handler for an issue fixing flow."""
def __init__(self, entry: ConfigEntry, issue_id: str) -> None: def __init__(self, entry: SynologyDSMConfigEntry, issue_id: str) -> None:
"""Create flow.""" """Create flow."""
self.entry = entry self.entry = entry
self.issue_id = issue_id self.issue_id = issue_id
@ -59,7 +58,7 @@ class MissingBackupSetupRepairFlow(RepairsFlow):
) -> data_entry_flow.FlowResult: ) -> data_entry_flow.FlowResult:
"""Handle the confirm step of a fix flow.""" """Handle the confirm step of a fix flow."""
syno_data: SynologyDSMData = self.hass.data[DOMAIN][self.entry.unique_id] syno_data = self.entry.runtime_data
if user_input is not None: if user_input is not None:
self.hass.config_entries.async_update_entry( self.hass.config_entries.async_update_entry(

View File

@ -16,7 +16,6 @@ from homeassistant.components.sensor import (
SensorEntityDescription, SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_DISKS, CONF_DISKS,
PERCENTAGE, PERCENTAGE,
@ -31,14 +30,13 @@ from homeassistant.helpers.typing import StateType
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from . import SynoApi from . import SynoApi
from .const import CONF_VOLUMES, DOMAIN, ENTITY_UNIT_LOAD from .const import CONF_VOLUMES, ENTITY_UNIT_LOAD
from .coordinator import SynologyDSMCentralUpdateCoordinator from .coordinator import SynologyDSMCentralUpdateCoordinator, SynologyDSMConfigEntry
from .entity import ( from .entity import (
SynologyDSMBaseEntity, SynologyDSMBaseEntity,
SynologyDSMDeviceEntity, SynologyDSMDeviceEntity,
SynologyDSMEntityDescription, SynologyDSMEntityDescription,
) )
from .models import SynologyDSMData
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -287,11 +285,11 @@ INFORMATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SynologyDSMConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the Synology NAS Sensor.""" """Set up the Synology NAS Sensor."""
data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id] data = entry.runtime_data
api = data.api api = data.api
coordinator = data.coordinator_central coordinator = data.coordinator_central
storage = api.storage storage = api.storage

View File

@ -3,13 +3,14 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import cast
from synology_dsm.exceptions import SynologyDSMException from synology_dsm.exceptions import SynologyDSMException
from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.core import HomeAssistant, ServiceCall
from .const import CONF_SERIAL, DOMAIN, SERVICE_REBOOT, SERVICE_SHUTDOWN, SERVICES from .const import CONF_SERIAL, DOMAIN, SERVICE_REBOOT, SERVICE_SHUTDOWN, SERVICES
from .models import SynologyDSMData from .coordinator import SynologyDSMConfigEntry
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -19,11 +20,20 @@ async def async_setup_services(hass: HomeAssistant) -> None:
async def service_handler(call: ServiceCall) -> None: async def service_handler(call: ServiceCall) -> None:
"""Handle service call.""" """Handle service call."""
serial = call.data.get(CONF_SERIAL) serial: str | None = call.data.get(CONF_SERIAL)
dsm_devices = hass.data[DOMAIN] entries: list[SynologyDSMConfigEntry] = (
hass.config_entries.async_loaded_entries(DOMAIN)
)
dsm_devices = {
cast(str, entry.unique_id): entry.runtime_data for entry in entries
}
if serial: if serial:
dsm_device: SynologyDSMData = hass.data[DOMAIN][serial] entry: SynologyDSMConfigEntry | None = (
hass.config_entries.async_entry_for_domain_unique_id(DOMAIN, serial)
)
assert entry
dsm_device = entry.runtime_data
elif len(dsm_devices) == 1: elif len(dsm_devices) == 1:
dsm_device = next(iter(dsm_devices.values())) dsm_device = next(iter(dsm_devices.values()))
serial = next(iter(dsm_devices)) serial = next(iter(dsm_devices))
@ -39,7 +49,7 @@ async def async_setup_services(hass: HomeAssistant) -> None:
return return
if call.service in [SERVICE_REBOOT, SERVICE_SHUTDOWN]: if call.service in [SERVICE_REBOOT, SERVICE_SHUTDOWN]:
if serial not in hass.data[DOMAIN]: if serial not in dsm_devices:
LOGGER.error("DSM with specified serial %s not found", serial) LOGGER.error("DSM with specified serial %s not found", serial)
return return
LOGGER.debug("%s DSM with serial %s", call.service, serial) LOGGER.debug("%s DSM with serial %s", call.service, serial)
@ -50,7 +60,7 @@ async def async_setup_services(hass: HomeAssistant) -> None:
), ),
call.service, call.service,
) )
dsm_device = hass.data[DOMAIN][serial] dsm_device = dsm_devices[serial]
dsm_api = dsm_device.api dsm_api = dsm_device.api
try: try:
await getattr(dsm_api, f"async_{call.service}")() await getattr(dsm_api, f"async_{call.service}")()

View File

@ -9,16 +9,14 @@ from typing import Any
from synology_dsm.api.surveillance_station import SynoSurveillanceStation from synology_dsm.api.surveillance_station import SynoSurveillanceStation
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import SynoApi from . import SynoApi
from .const import DOMAIN from .const import DOMAIN
from .coordinator import SynologyDSMSwitchUpdateCoordinator from .coordinator import SynologyDSMConfigEntry, SynologyDSMSwitchUpdateCoordinator
from .entity import SynologyDSMBaseEntity, SynologyDSMEntityDescription from .entity import SynologyDSMBaseEntity, SynologyDSMEntityDescription
from .models import SynologyDSMData
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -41,11 +39,11 @@ SURVEILLANCE_SWITCH: tuple[SynologyDSMSwitchEntityDescription, ...] = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SynologyDSMConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the Synology NAS switch.""" """Set up the Synology NAS switch."""
data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id] data = entry.runtime_data
if coordinator := data.coordinator_switches: if coordinator := data.coordinator_switches:
assert coordinator.version is not None assert coordinator.version is not None
async_add_entities( async_add_entities(

View File

@ -9,15 +9,12 @@ from synology_dsm.api.core.upgrade import SynoCoreUpgrade
from yarl import URL from yarl import URL
from homeassistant.components.update import UpdateEntity, UpdateEntityDescription from homeassistant.components.update import UpdateEntity, UpdateEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN from .coordinator import SynologyDSMCentralUpdateCoordinator, SynologyDSMConfigEntry
from .coordinator import SynologyDSMCentralUpdateCoordinator
from .entity import SynologyDSMBaseEntity, SynologyDSMEntityDescription from .entity import SynologyDSMBaseEntity, SynologyDSMEntityDescription
from .models import SynologyDSMData
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -39,11 +36,11 @@ UPDATE_ENTITIES: Final = [
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SynologyDSMConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up Synology DSM update entities.""" """Set up Synology DSM update entities."""
data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id] data = entry.runtime_data
async_add_entities( async_add_entities(
SynoDSMUpdateEntity(data.api, data.coordinator_central, description) SynoDSMUpdateEntity(data.api, data.coordinator_central, description)
for description in UPDATE_ENTITIES for description in UPDATE_ENTITIES