Switch Reolink from hass.data to runtime_data (#126002)

Switch from hass.data to runtime_data
This commit is contained in:
starkillerOG 2024-09-15 21:05:59 +02:00 committed by GitHub
parent d9812f0d48
commit e768bea298
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 75 additions and 104 deletions

View file

@ -9,7 +9,6 @@ import logging
from reolink_aio.api import RETRY_ATTEMPTS from reolink_aio.api import RETRY_ATTEMPTS
from reolink_aio.exceptions import CredentialsInvalidError, ReolinkError from reolink_aio.exceptions import CredentialsInvalidError, ReolinkError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
@ -26,7 +25,7 @@ from .const import DOMAIN
from .exceptions import PasswordIncompatible, ReolinkException, UserNotAdmin from .exceptions import PasswordIncompatible, ReolinkException, UserNotAdmin
from .host import ReolinkHost from .host import ReolinkHost
from .services import async_setup_services from .services import async_setup_services
from .util import ReolinkData, get_device_uid_and_ch from .util import ReolinkConfigEntry, ReolinkData, get_device_uid_and_ch
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -56,7 +55,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return True return True
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: async def async_setup_entry(
hass: HomeAssistant, config_entry: ReolinkConfigEntry
) -> bool:
"""Set up Reolink from a config entry.""" """Set up Reolink from a config entry."""
host = ReolinkHost(hass, config_entry.data, config_entry.options) host = ReolinkHost(hass, config_entry.data, config_entry.options)
@ -151,7 +152,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
await host.stop() await host.stop()
raise raise
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = ReolinkData( config_entry.runtime_data = ReolinkData(
host=host, host=host,
device_coordinator=device_coordinator, device_coordinator=device_coordinator,
firmware_coordinator=firmware_coordinator, firmware_coordinator=firmware_coordinator,
@ -168,30 +169,29 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
return True return True
async def entry_update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None: async def entry_update_listener(
hass: HomeAssistant, config_entry: ReolinkConfigEntry
) -> None:
"""Update the configuration of the host entity.""" """Update the configuration of the host entity."""
await hass.config_entries.async_reload(config_entry.entry_id) await hass.config_entries.async_reload(config_entry.entry_id)
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: async def async_unload_entry(
hass: HomeAssistant, config_entry: ReolinkConfigEntry
) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
host: ReolinkHost = hass.data[DOMAIN][config_entry.entry_id].host host: ReolinkHost = config_entry.runtime_data.host
await host.stop() await host.stop()
if unload_ok := await hass.config_entries.async_unload_platforms( return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
config_entry, PLATFORMS
):
hass.data[DOMAIN].pop(config_entry.entry_id)
return unload_ok
async def async_remove_config_entry_device( async def async_remove_config_entry_device(
hass: HomeAssistant, config_entry: ConfigEntry, device: dr.DeviceEntry hass: HomeAssistant, config_entry: ReolinkConfigEntry, device: dr.DeviceEntry
) -> bool: ) -> bool:
"""Remove a device from a config entry.""" """Remove a device from a config entry."""
host: ReolinkHost = hass.data[DOMAIN][config_entry.entry_id].host host: ReolinkHost = config_entry.runtime_data.host
(device_uid, ch, is_chime) = get_device_uid_and_ch(device, host) (device_uid, ch, is_chime) = get_device_uid_and_ch(device, host)
if is_chime: if is_chime:

View file

@ -20,15 +20,13 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity, BinarySensorEntity,
BinarySensorEntityDescription, BinarySensorEntityDescription,
) )
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.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription
from .util import ReolinkConfigEntry, ReolinkData
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -108,11 +106,11 @@ BINARY_SENSORS = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up a Reolink IP Camera.""" """Set up a Reolink IP Camera."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
entities: list[ReolinkBinarySensorEntity] = [] entities: list[ReolinkBinarySensorEntity] = []
for channel in reolink_data.host.api.channels: for channel in reolink_data.host.api.channels:

View file

@ -16,7 +16,6 @@ from homeassistant.components.button import (
ButtonEntityDescription, ButtonEntityDescription,
) )
from homeassistant.components.camera import CameraEntityFeature from homeassistant.components.camera import CameraEntityFeature
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.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
@ -26,14 +25,13 @@ from homeassistant.helpers.entity_platform import (
async_get_current_platform, async_get_current_platform,
) )
from . import ReolinkData
from .const import DOMAIN
from .entity import ( from .entity import (
ReolinkChannelCoordinatorEntity, ReolinkChannelCoordinatorEntity,
ReolinkChannelEntityDescription, ReolinkChannelEntityDescription,
ReolinkHostCoordinatorEntity, ReolinkHostCoordinatorEntity,
ReolinkHostEntityDescription, ReolinkHostEntityDescription,
) )
from .util import ReolinkConfigEntry, ReolinkData
ATTR_SPEED = "speed" ATTR_SPEED = "speed"
SUPPORT_PTZ_SPEED = CameraEntityFeature.STREAM SUPPORT_PTZ_SPEED = CameraEntityFeature.STREAM
@ -152,11 +150,11 @@ HOST_BUTTON_ENTITIES = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up a Reolink button entities.""" """Set up a Reolink button entities."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
entities: list[ReolinkButtonEntity | ReolinkHostButtonEntity] = [ entities: list[ReolinkButtonEntity | ReolinkHostButtonEntity] = [
ReolinkButtonEntity(reolink_data, channel, entity_description) ReolinkButtonEntity(reolink_data, channel, entity_description)

View file

@ -13,14 +13,12 @@ from homeassistant.components.camera import (
CameraEntityDescription, CameraEntityDescription,
CameraEntityFeature, CameraEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription
from .util import ReolinkConfigEntry, ReolinkData
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -91,11 +89,11 @@ CAMERA_ENTITIES = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up a Reolink IP Camera.""" """Set up a Reolink IP Camera."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
entities: list[ReolinkCamera] = [] entities: list[ReolinkCamera] = []
for entity_description in CAMERA_ENTITIES: for entity_description in CAMERA_ENTITIES:

View file

@ -11,12 +11,7 @@ from reolink_aio.exceptions import ApiError, CredentialsInvalidError, ReolinkErr
import voluptuous as vol import voluptuous as vol
from homeassistant.components import dhcp from homeassistant.components import dhcp
from homeassistant.config_entries import ( from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_HOST,
CONF_PASSWORD, CONF_PASSWORD,
@ -37,7 +32,7 @@ from .exceptions import (
UserNotAdmin, UserNotAdmin,
) )
from .host import ReolinkHost from .host import ReolinkHost
from .util import is_connected from .util import ReolinkConfigEntry, is_connected
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -48,7 +43,7 @@ DEFAULT_OPTIONS = {CONF_PROTOCOL: DEFAULT_PROTOCOL}
class ReolinkOptionsFlowHandler(OptionsFlow): class ReolinkOptionsFlowHandler(OptionsFlow):
"""Handle Reolink options.""" """Handle Reolink options."""
def __init__(self, config_entry: ConfigEntry) -> None: def __init__(self, config_entry: ReolinkConfigEntry) -> None:
"""Initialize ReolinkOptionsFlowHandler.""" """Initialize ReolinkOptionsFlowHandler."""
self.config_entry = config_entry self.config_entry = config_entry
@ -104,7 +99,7 @@ class ReolinkFlowHandler(ConfigFlow, domain=DOMAIN):
@staticmethod @staticmethod
@callback @callback
def async_get_options_flow( def async_get_options_flow(
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
) -> ReolinkOptionsFlowHandler: ) -> ReolinkOptionsFlowHandler:
"""Options callback for Reolink.""" """Options callback for Reolink."""
return ReolinkOptionsFlowHandler(config_entry) return ReolinkOptionsFlowHandler(config_entry)

View file

@ -4,18 +4,16 @@ from __future__ import annotations
from typing import Any from typing import Any
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import ReolinkData from .util import ReolinkConfigEntry, ReolinkData
from .const import DOMAIN
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry hass: HomeAssistant, config_entry: ReolinkConfigEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a config entry.""" """Return diagnostics for a config entry."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
host = reolink_data.host host = reolink_data.host
api = host.api api = host.api

View file

@ -15,15 +15,13 @@ from homeassistant.components.light import (
LightEntity, LightEntity,
LightEntityDescription, LightEntityDescription,
) )
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.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription
from .util import ReolinkConfigEntry, ReolinkData
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -64,11 +62,11 @@ LIGHT_ENTITIES = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up a Reolink light entities.""" """Set up a Reolink light entities."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
async_add_entities( async_add_entities(
ReolinkLightEntity(reolink_data, channel, entity_description) ReolinkLightEntity(reolink_data, channel, entity_description)

View file

@ -22,8 +22,8 @@ from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant 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 . import ReolinkData
from .const import DOMAIN from .const import DOMAIN
from .host import ReolinkHost
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -46,6 +46,13 @@ def res_name(stream: str) -> str:
return "Low res." return "Low res."
def get_host(hass: HomeAssistant, config_entry_id: str) -> ReolinkHost:
"""Return the Reolink host from the config entry id."""
config_entry = hass.config_entries.async_get_entry(config_entry_id)
assert config_entry is not None
return config_entry.runtime_data.host
class ReolinkVODMediaSource(MediaSource): class ReolinkVODMediaSource(MediaSource):
"""Provide Reolink camera VODs as media sources.""" """Provide Reolink camera VODs as media sources."""
@ -65,8 +72,7 @@ class ReolinkVODMediaSource(MediaSource):
_, config_entry_id, channel_str, stream_res, filename = identifier _, config_entry_id, channel_str, stream_res, filename = identifier
channel = int(channel_str) channel = int(channel_str)
data: dict[str, ReolinkData] = self.hass.data[DOMAIN] host = get_host(self.hass, config_entry_id)
host = data[config_entry_id].host
def get_vod_type() -> VodRequestType: def get_vod_type() -> VodRequestType:
if filename.endswith(".mp4"): if filename.endswith(".mp4"):
@ -151,8 +157,7 @@ class ReolinkVODMediaSource(MediaSource):
if config_entry.state != ConfigEntryState.LOADED: if config_entry.state != ConfigEntryState.LOADED:
continue continue
channels: list[str] = [] channels: list[str] = []
data: dict[str, ReolinkData] = self.hass.data[DOMAIN] host = config_entry.runtime_data.host
host = data[config_entry.entry_id].host
entities = er.async_entries_for_config_entry( entities = er.async_entries_for_config_entry(
entity_reg, config_entry.entry_id entity_reg, config_entry.entry_id
) )
@ -213,8 +218,7 @@ class ReolinkVODMediaSource(MediaSource):
self, config_entry_id: str, channel: int self, config_entry_id: str, channel: int
) -> BrowseMediaSource: ) -> BrowseMediaSource:
"""Allow the user to select the high or low playback resolution, (low loads faster).""" """Allow the user to select the high or low playback resolution, (low loads faster)."""
data: dict[str, ReolinkData] = self.hass.data[DOMAIN] host = get_host(self.hass, config_entry_id)
host = data[config_entry_id].host
main_enc = await host.api.get_encoding(channel, "main") main_enc = await host.api.get_encoding(channel, "main")
if main_enc == "h265": if main_enc == "h265":
@ -297,8 +301,7 @@ class ReolinkVODMediaSource(MediaSource):
self, config_entry_id: str, channel: int, stream: str self, config_entry_id: str, channel: int, stream: str
) -> BrowseMediaSource: ) -> BrowseMediaSource:
"""Return all days on which recordings are available for a reolink camera.""" """Return all days on which recordings are available for a reolink camera."""
data: dict[str, ReolinkData] = self.hass.data[DOMAIN] host = get_host(self.hass, config_entry_id)
host = data[config_entry_id].host
# We want today of the camera, not necessarily today of the server # We want today of the camera, not necessarily today of the server
now = host.api.time() or await host.api.async_get_time() now = host.api.time() or await host.api.async_get_time()
@ -354,8 +357,7 @@ class ReolinkVODMediaSource(MediaSource):
day: int, day: int,
) -> BrowseMediaSource: ) -> BrowseMediaSource:
"""Return all recording files on a specific day of a Reolink camera.""" """Return all recording files on a specific day of a Reolink camera."""
data: dict[str, ReolinkData] = self.hass.data[DOMAIN] host = get_host(self.hass, config_entry_id)
host = data[config_entry_id].host
start = dt.datetime(year, month, day, hour=0, minute=0, second=0) start = dt.datetime(year, month, day, hour=0, minute=0, second=0)
end = dt.datetime(year, month, day, hour=23, minute=59, second=59) end = dt.datetime(year, month, day, hour=23, minute=59, second=59)

View file

@ -14,19 +14,17 @@ from homeassistant.components.number import (
NumberEntityDescription, NumberEntityDescription,
NumberMode, NumberMode,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, UnitOfTime from homeassistant.const import EntityCategory, UnitOfTime
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ReolinkData
from .const import DOMAIN
from .entity import ( from .entity import (
ReolinkChannelCoordinatorEntity, ReolinkChannelCoordinatorEntity,
ReolinkChannelEntityDescription, ReolinkChannelEntityDescription,
ReolinkChimeCoordinatorEntity, ReolinkChimeCoordinatorEntity,
) )
from .util import ReolinkConfigEntry, ReolinkData
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -492,11 +490,11 @@ CHIME_NUMBER_ENTITIES = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up a Reolink number entities.""" """Set up a Reolink number entities."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
entities: list[ReolinkNumberEntity | ReolinkChimeNumberEntity] = [ entities: list[ReolinkNumberEntity | ReolinkChimeNumberEntity] = [
ReolinkNumberEntity(reolink_data, channel, entity_description) ReolinkNumberEntity(reolink_data, channel, entity_description)

View file

@ -20,19 +20,17 @@ from reolink_aio.api import (
from reolink_aio.exceptions import InvalidParameterError, ReolinkError from reolink_aio.exceptions import InvalidParameterError, ReolinkError
from homeassistant.components.select import SelectEntity, SelectEntityDescription from homeassistant.components.select import SelectEntity, SelectEntityDescription
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.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ReolinkData
from .const import DOMAIN
from .entity import ( from .entity import (
ReolinkChannelCoordinatorEntity, ReolinkChannelCoordinatorEntity,
ReolinkChannelEntityDescription, ReolinkChannelEntityDescription,
ReolinkChimeCoordinatorEntity, ReolinkChimeCoordinatorEntity,
) )
from .util import ReolinkConfigEntry, ReolinkData
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -183,11 +181,11 @@ CHIME_SELECT_ENTITIES = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up a Reolink select entities.""" """Set up a Reolink select entities."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
entities: list[ReolinkSelectEntity | ReolinkChimeSelectEntity] = [ entities: list[ReolinkSelectEntity | ReolinkChimeSelectEntity] = [
ReolinkSelectEntity(reolink_data, channel, entity_description) ReolinkSelectEntity(reolink_data, channel, entity_description)

View file

@ -16,20 +16,18 @@ from homeassistant.components.sensor import (
SensorEntityDescription, SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfTemperature from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfTemperature
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 . import ReolinkData
from .const import DOMAIN
from .entity import ( from .entity import (
ReolinkChannelCoordinatorEntity, ReolinkChannelCoordinatorEntity,
ReolinkChannelEntityDescription, ReolinkChannelEntityDescription,
ReolinkHostCoordinatorEntity, ReolinkHostCoordinatorEntity,
ReolinkHostEntityDescription, ReolinkHostEntityDescription,
) )
from .util import ReolinkConfigEntry, ReolinkData
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -126,11 +124,11 @@ HDD_SENSORS = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up a Reolink IP Camera.""" """Set up a Reolink IP Camera."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
entities: list[ entities: list[
ReolinkSensorEntity | ReolinkHostSensorEntity | ReolinkHddSensorEntity ReolinkSensorEntity | ReolinkHostSensorEntity | ReolinkHddSensorEntity

View file

@ -47,7 +47,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
translation_key="service_entry_ex", translation_key="service_entry_ex",
translation_placeholders={"service_name": "play_chime"}, translation_placeholders={"service_name": "play_chime"},
) )
host: ReolinkHost = hass.data[DOMAIN][config_entry.entry_id].host host: ReolinkHost = config_entry.runtime_data.host
(device_uid, chime_id, is_chime) = get_device_uid_and_ch(device, host) (device_uid, chime_id, is_chime) = get_device_uid_and_ch(device, host)
chime: Chime | None = host.api.chime(chime_id) chime: Chime | None = host.api.chime(chime_id)
if not is_chime or chime is None: if not is_chime or chime is None:

View file

@ -14,14 +14,12 @@ from homeassistant.components.siren import (
SirenEntityDescription, SirenEntityDescription,
SirenEntityFeature, SirenEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ReolinkData
from .const import DOMAIN
from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription
from .util import ReolinkConfigEntry, ReolinkData
@dataclass(frozen=True) @dataclass(frozen=True)
@ -42,11 +40,11 @@ SIREN_ENTITIES = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up a Reolink siren entities.""" """Set up a Reolink siren entities."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
async_add_entities( async_add_entities(
ReolinkSirenEntity(reolink_data, channel, entity_description) ReolinkSirenEntity(reolink_data, channel, entity_description)

View file

@ -10,14 +10,12 @@ from reolink_aio.api import Chime, Host
from reolink_aio.exceptions import ReolinkError from reolink_aio.exceptions import ReolinkError
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
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.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er, issue_registry as ir from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ReolinkData
from .const import DOMAIN from .const import DOMAIN
from .entity import ( from .entity import (
ReolinkChannelCoordinatorEntity, ReolinkChannelCoordinatorEntity,
@ -26,6 +24,7 @@ from .entity import (
ReolinkHostCoordinatorEntity, ReolinkHostCoordinatorEntity,
ReolinkHostEntityDescription, ReolinkHostEntityDescription,
) )
from .util import ReolinkConfigEntry, ReolinkData
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -283,11 +282,11 @@ DEPRECATED_HDR = ReolinkSwitchEntityDescription(
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up a Reolink switch entities.""" """Set up a Reolink switch entities."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
entities: list[ entities: list[
ReolinkSwitchEntity | ReolinkNVRSwitchEntity | ReolinkChimeSwitchEntity ReolinkSwitchEntity | ReolinkNVRSwitchEntity | ReolinkChimeSwitchEntity

View file

@ -15,20 +15,18 @@ from homeassistant.components.update import (
UpdateEntityDescription, UpdateEntityDescription,
UpdateEntityFeature, UpdateEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_call_later from homeassistant.helpers.event import async_call_later
from . import ReolinkData
from .const import DOMAIN
from .entity import ( from .entity import (
ReolinkChannelCoordinatorEntity, ReolinkChannelCoordinatorEntity,
ReolinkChannelEntityDescription, ReolinkChannelEntityDescription,
ReolinkHostCoordinatorEntity, ReolinkHostCoordinatorEntity,
ReolinkHostEntityDescription, ReolinkHostEntityDescription,
) )
from .util import ReolinkConfigEntry, ReolinkData
POLL_AFTER_INSTALL = 120 POLL_AFTER_INSTALL = 120
@ -68,11 +66,11 @@ HOST_UPDATE_ENTITIES = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ReolinkConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up update entities for Reolink component.""" """Set up update entities for Reolink component."""
reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] reolink_data: ReolinkData = config_entry.runtime_data
entities: list[ReolinkUpdateEntity | ReolinkHostUpdateEntity] = [ entities: list[ReolinkUpdateEntity | ReolinkHostUpdateEntity] = [
ReolinkUpdateEntity(reolink_data, channel, entity_description) ReolinkUpdateEntity(reolink_data, channel, entity_description)

View file

@ -12,6 +12,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN from .const import DOMAIN
from .host import ReolinkHost from .host import ReolinkHost
type ReolinkConfigEntry = config_entries.ConfigEntry[ReolinkData]
@dataclass @dataclass
class ReolinkData: class ReolinkData:
@ -24,13 +26,10 @@ class ReolinkData:
def is_connected(hass: HomeAssistant, config_entry: config_entries.ConfigEntry) -> bool: def is_connected(hass: HomeAssistant, config_entry: config_entries.ConfigEntry) -> bool:
"""Check if an existing entry has a proper connection.""" """Check if an existing entry has a proper connection."""
reolink_data: ReolinkData | None = hass.data.get(DOMAIN, {}).get(
config_entry.entry_id
)
return ( return (
reolink_data is not None hasattr(config_entry, "runtime_data")
and config_entry.state == config_entries.ConfigEntryState.LOADED and config_entry.state == config_entries.ConfigEntryState.LOADED
and reolink_data.device_coordinator.last_update_success and config_entry.runtime_data.device_coordinator.last_update_success
) )

View file

@ -5,13 +5,12 @@ from unittest.mock import MagicMock, patch
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL
from homeassistant.components.reolink.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import STATE_OFF, STATE_ON, Platform from homeassistant.const import STATE_OFF, STATE_ON, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from .conftest import TEST_DUO_MODEL, TEST_NVR_NAME, TEST_UID from .conftest import TEST_DUO_MODEL, TEST_NVR_NAME
from tests.common import MockConfigEntry, async_fire_time_changed from tests.common import MockConfigEntry, async_fire_time_changed
from tests.typing import ClientSessionGenerator from tests.typing import ClientSessionGenerator
@ -46,7 +45,7 @@ async def test_motion_sensor(
# test webhook callback # test webhook callback
reolink_connect.motion_detected.return_value = True reolink_connect.motion_detected.return_value = True
reolink_connect.ONVIF_event_callback.return_value = [0] reolink_connect.ONVIF_event_callback.return_value = [0]
webhook_id = f"{DOMAIN}_{TEST_UID.replace(':', '')}_ONVIF" webhook_id = config_entry.runtime_data.host.webhook_id
client = await hass_client_no_auth() client = await hass_client_no_auth()
await client.post(f"/api/webhook/{webhook_id}", data="test_data") await client.post(f"/api/webhook/{webhook_id}", data="test_data")

View file

@ -11,7 +11,6 @@ from reolink_aio.enums import SubType
from reolink_aio.exceptions import NotSupportedError, ReolinkError, SubscriptionError from reolink_aio.exceptions import NotSupportedError, ReolinkError, SubscriptionError
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL
from homeassistant.components.reolink.const import DOMAIN
from homeassistant.components.reolink.host import ( from homeassistant.components.reolink.host import (
FIRST_ONVIF_LONG_POLL_TIMEOUT, FIRST_ONVIF_LONG_POLL_TIMEOUT,
FIRST_ONVIF_TIMEOUT, FIRST_ONVIF_TIMEOUT,
@ -27,8 +26,6 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.network import NoURLAvailableError from homeassistant.helpers.network import NoURLAvailableError
from homeassistant.util.aiohttp import MockRequest from homeassistant.util.aiohttp import MockRequest
from .conftest import TEST_UID
from tests.common import MockConfigEntry, async_fire_time_changed from tests.common import MockConfigEntry, async_fire_time_changed
from tests.components.diagnostics import get_diagnostics_for_config_entry from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator from tests.typing import ClientSessionGenerator
@ -47,7 +44,7 @@ async def test_webhook_callback(
await hass.async_block_till_done() await hass.async_block_till_done()
assert config_entry.state is ConfigEntryState.LOADED assert config_entry.state is ConfigEntryState.LOADED
webhook_id = f"{DOMAIN}_{TEST_UID.replace(':', '')}_ONVIF" webhook_id = config_entry.runtime_data.host.webhook_id
signal_all = MagicMock() signal_all = MagicMock()
signal_ch = MagicMock() signal_ch = MagicMock()
@ -276,7 +273,7 @@ async def test_long_poll_stop_when_push(
# simulate ONVIF push callback # simulate ONVIF push callback
client = await hass_client_no_auth() client = await hass_client_no_auth()
reolink_connect.ONVIF_event_callback.return_value = None reolink_connect.ONVIF_event_callback.return_value = None
webhook_id = f"{DOMAIN}_{TEST_UID.replace(':', '')}_ONVIF" webhook_id = config_entry.runtime_data.host.webhook_id
await client.post(f"/api/webhook/{webhook_id}") await client.post(f"/api/webhook/{webhook_id}")
freezer.tick(DEVICE_UPDATE_INTERVAL) freezer.tick(DEVICE_UPDATE_INTERVAL)
@ -379,7 +376,7 @@ async def test_diagnostics_event_connection(
# simulate ONVIF push callback # simulate ONVIF push callback
client = await hass_client_no_auth() client = await hass_client_no_auth()
reolink_connect.ONVIF_event_callback.return_value = None reolink_connect.ONVIF_event_callback.return_value = None
webhook_id = f"{DOMAIN}_{TEST_UID.replace(':', '')}_ONVIF" webhook_id = config_entry.runtime_data.host.webhook_id
await client.post(f"/api/webhook/{webhook_id}") await client.post(f"/api/webhook/{webhook_id}")
diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)