mirror of
https://github.com/home-assistant/core.git
synced 2026-07-13 01:27:57 +01:00
Migrate legacy data stores in Netatmo (#175620)
This commit is contained in:
@@ -29,16 +29,7 @@ from homeassistant.helpers.start import async_at_started
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import api
|
||||
from .const import (
|
||||
DATA_CAMERAS,
|
||||
DATA_DEVICE_IDS,
|
||||
DATA_EVENTS,
|
||||
DATA_HOMES,
|
||||
DATA_PERSONS,
|
||||
DATA_SCHEDULES,
|
||||
DOMAIN,
|
||||
PLATFORMS,
|
||||
)
|
||||
from .const import DOMAIN, PLATFORMS
|
||||
from .data_handler import NetatmoConfigEntry, NetatmoDataHandler
|
||||
from .services import async_setup_services
|
||||
from .webhook import async_register_webhook, async_unregister_webhook
|
||||
@@ -52,17 +43,6 @@ MAX_WEBHOOK_RETRIES = 3
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the Netatmo component."""
|
||||
# Uses legacy hass.data[DOMAIN] pattern
|
||||
# pylint: disable-next=home-assistant-use-runtime-data
|
||||
hass.data[DOMAIN] = {
|
||||
DATA_PERSONS: {},
|
||||
DATA_DEVICE_IDS: {},
|
||||
DATA_SCHEDULES: {},
|
||||
DATA_HOMES: {},
|
||||
DATA_EVENTS: {},
|
||||
DATA_CAMERAS: {},
|
||||
}
|
||||
|
||||
async_setup_services(hass)
|
||||
|
||||
return True
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""Support for the Netatmo cameras."""
|
||||
# pylint: disable=home-assistant-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern
|
||||
|
||||
import logging
|
||||
from typing import Any, cast, override
|
||||
@@ -24,8 +23,6 @@ from .const import (
|
||||
CAMERA_LIGHT_MODES,
|
||||
CAMERA_TRIGGERS,
|
||||
CONF_URL_SECURITY,
|
||||
DATA_CAMERAS,
|
||||
DATA_EVENTS,
|
||||
DOMAIN,
|
||||
EVENT_TYPE_CONNECTION,
|
||||
EVENT_TYPE_DISCONNECTION,
|
||||
@@ -137,7 +134,7 @@ class NetatmoCamera(NetatmoModuleEntity, Camera):
|
||||
)
|
||||
)
|
||||
|
||||
self.hass.data[DOMAIN][DATA_CAMERAS][self.device.entity_id] = self.device.name
|
||||
self.data_handler.cameras[self.device.entity_id] = self.device.name
|
||||
|
||||
@callback
|
||||
def handle_event(self, event: dict) -> None:
|
||||
@@ -281,8 +278,8 @@ class NetatmoCamera(NetatmoModuleEntity, Camera):
|
||||
self._attr_is_streaming = self.device.monitoring
|
||||
self._attr_motion_detection_enabled = self.device.monitoring
|
||||
|
||||
self.hass.data[DOMAIN][DATA_EVENTS][self.device.entity_id] = (
|
||||
self.process_events(self.device.events)
|
||||
self.data_handler.events[self.device.entity_id] = self.process_events(
|
||||
self.device.events
|
||||
)
|
||||
|
||||
def process_events(self, event_list: list[NaEvent]) -> dict:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""Support for Netatmo Smart thermostats."""
|
||||
# pylint: disable=home-assistant-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern
|
||||
|
||||
import logging
|
||||
from typing import Any, cast, override
|
||||
@@ -39,7 +38,6 @@ from .const import (
|
||||
ATTR_SELECTED_SCHEDULE_ID,
|
||||
ATTR_TARGET_TEMPERATURE,
|
||||
ATTR_TIME_PERIOD,
|
||||
DATA_SCHEDULES,
|
||||
DOMAIN,
|
||||
EVENT_TYPE_CANCEL_SET_POINT,
|
||||
EVENT_TYPE_SCHEDULE,
|
||||
@@ -254,7 +252,7 @@ class NetatmoThermostat(NetatmoRoomEntity, ClimateEntity):
|
||||
if data["event_type"] == EVENT_TYPE_SCHEDULE:
|
||||
# handle schedule change
|
||||
if "schedule_id" in data:
|
||||
selected_schedule = self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
selected_schedule = self.data_handler.schedules[
|
||||
self.home.entity_id
|
||||
].get(data["schedule_id"])
|
||||
self._selected_schedule = getattr(
|
||||
@@ -461,9 +459,7 @@ class NetatmoThermostat(NetatmoRoomEntity, ClimateEntity):
|
||||
async def _async_service_set_schedule(self, **kwargs: Any) -> None:
|
||||
schedule_name = kwargs.get(ATTR_SCHEDULE_NAME)
|
||||
schedule_id = None
|
||||
for sid, schedule in self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
self.home.entity_id
|
||||
].items():
|
||||
for sid, schedule in self.data_handler.schedules[self.home.entity_id].items():
|
||||
if schedule.name == schedule_name:
|
||||
schedule_id = sid
|
||||
break
|
||||
|
||||
@@ -71,13 +71,6 @@ CONF_WEATHER_AREAS = "weather_areas"
|
||||
OAUTH2_AUTHORIZE = "https://api.netatmo.com/oauth2/authorize"
|
||||
OAUTH2_TOKEN = "https://api.netatmo.com/oauth2/token"
|
||||
|
||||
DATA_CAMERAS = "cameras"
|
||||
DATA_DEVICE_IDS = "netatmo_device_ids"
|
||||
DATA_EVENTS = "netatmo_events"
|
||||
DATA_HOMES = "netatmo_homes"
|
||||
DATA_PERSONS = "netatmo_persons"
|
||||
DATA_SCHEDULES = "netatmo_schedules"
|
||||
|
||||
NETATMO_EVENT = "netatmo_event"
|
||||
|
||||
DEFAULT_DISCOVERY = True
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""The Netatmo data handler."""
|
||||
# pylint: disable=home-assistant-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern
|
||||
|
||||
from collections import deque
|
||||
from dataclasses import dataclass
|
||||
@@ -15,6 +14,7 @@ from pyatmo.modules.device_types import (
|
||||
DeviceCategory as NetatmoDeviceCategory,
|
||||
DeviceType as NetatmoDeviceType,
|
||||
)
|
||||
from pyatmo.schedule import Schedule
|
||||
|
||||
from homeassistant.components import cloud
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -27,8 +27,6 @@ from homeassistant.helpers.event import async_track_time_interval
|
||||
|
||||
from .const import (
|
||||
CAMERA_CONNECTION_WEBHOOKS,
|
||||
DATA_PERSONS,
|
||||
DATA_SCHEDULES,
|
||||
DOMAIN,
|
||||
MANUFACTURER,
|
||||
NETATMO_CREATE_BUTTON,
|
||||
@@ -91,6 +89,12 @@ SCAN_INTERVAL = 60
|
||||
type NetatmoConfigEntry = ConfigEntry[NetatmoDataHandler]
|
||||
|
||||
|
||||
def async_get_loaded_entry(hass: HomeAssistant) -> NetatmoConfigEntry | None:
|
||||
"""Return the single loaded Netatmo config entry, if any."""
|
||||
entries = hass.config_entries.async_loaded_entries(DOMAIN)
|
||||
return entries[0] if entries else None
|
||||
|
||||
|
||||
@dataclass
|
||||
class NetatmoDevice:
|
||||
"""Netatmo device class."""
|
||||
@@ -160,6 +164,11 @@ class NetatmoDataHandler:
|
||||
self._rate_limit = DEV_LIMIT
|
||||
self.poll_start = time()
|
||||
self.poll_count = 0
|
||||
self.persons: dict[str, dict[str, str | None]] = {}
|
||||
self.schedules: dict[str, dict[str, Schedule]] = {}
|
||||
self.device_ids: dict[str, str] = {}
|
||||
self.cameras: dict[str, str] = {}
|
||||
self.events: dict[str, dict] = {}
|
||||
|
||||
async def async_setup(self) -> None:
|
||||
"""Set up the Netatmo data handler."""
|
||||
@@ -330,7 +339,7 @@ class NetatmoDataHandler:
|
||||
self.setup_rooms(home, signal_home)
|
||||
self.setup_modules(home, signal_home)
|
||||
|
||||
self.hass.data[DOMAIN][DATA_PERSONS][home.entity_id] = {
|
||||
self.persons[home.entity_id] = {
|
||||
person.entity_id: person.pseudo for person in home.persons.values()
|
||||
}
|
||||
|
||||
@@ -459,7 +468,7 @@ class NetatmoDataHandler:
|
||||
if NetatmoDeviceCategory.climate in [
|
||||
next(iter(x)) for x in [room.features for room in home.rooms.values()] if x
|
||||
]:
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][home.entity_id] = self.account.homes[
|
||||
self.schedules[home.entity_id] = self.account.homes[
|
||||
home.entity_id
|
||||
].schedules
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ from homeassistant.helpers.entity import Entity
|
||||
from .const import (
|
||||
CONF_URL_ENERGY,
|
||||
CONF_URL_WEATHER,
|
||||
DATA_DEVICE_IDS,
|
||||
DEFAULT_ATTRIBUTION,
|
||||
DOMAIN,
|
||||
SIGNAL_NAME,
|
||||
@@ -141,9 +140,7 @@ class NetatmoRoomEntity(NetatmoDeviceEntity):
|
||||
if device := registry.async_get_device(
|
||||
identifiers={(DOMAIN, self.device.entity_id)}
|
||||
):
|
||||
# Uses legacy hass.data[DOMAIN] pattern
|
||||
# pylint: disable-next=home-assistant-use-runtime-data
|
||||
self.hass.data[DOMAIN][DATA_DEVICE_IDS][self.device.entity_id] = device.id
|
||||
self.data_handler.device_ids[self.device.entity_id] = device.id
|
||||
|
||||
@property
|
||||
@override
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""Netatmo Media Source Implementation."""
|
||||
# pylint: disable=home-assistant-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern
|
||||
|
||||
import datetime as dt
|
||||
import logging
|
||||
@@ -15,9 +14,11 @@ from homeassistant.components.media_source import (
|
||||
PlayMedia,
|
||||
Unresolvable,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
|
||||
from .const import DATA_CAMERAS, DATA_EVENTS, DOMAIN, MANUFACTURER
|
||||
from .const import DOMAIN, MANUFACTURER
|
||||
from .data_handler import NetatmoConfigEntry, NetatmoDataHandler, async_get_loaded_entry
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
MIME_TYPE = "application/x-mpegURL"
|
||||
@@ -29,7 +30,7 @@ class IncompatibleMediaSource(MediaSourceError):
|
||||
|
||||
async def async_get_media_source(hass: HomeAssistant) -> NetatmoSource:
|
||||
"""Set up Netatmo media source."""
|
||||
return NetatmoSource(hass)
|
||||
return NetatmoSource(hass, async_get_loaded_entry(hass))
|
||||
|
||||
|
||||
class NetatmoSource(MediaSource):
|
||||
@@ -37,11 +38,24 @@ class NetatmoSource(MediaSource):
|
||||
|
||||
name: str = MANUFACTURER
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
def __init__(self, hass: HomeAssistant, entry: NetatmoConfigEntry | None) -> None:
|
||||
"""Initialize Netatmo source."""
|
||||
super().__init__(DOMAIN)
|
||||
self.hass = hass
|
||||
self.events = self.hass.data[DOMAIN][DATA_EVENTS]
|
||||
self.entry = entry
|
||||
|
||||
@property
|
||||
def _data_handler(self) -> NetatmoDataHandler | None:
|
||||
"""Return the data handler of the config entry, if it is loaded."""
|
||||
if self.entry is None or self.entry.state is not ConfigEntryState.LOADED:
|
||||
return None
|
||||
return self.entry.runtime_data
|
||||
|
||||
@property
|
||||
def events(self) -> dict[str, dict]:
|
||||
"""Return the camera events."""
|
||||
data_handler = self._data_handler
|
||||
return data_handler.events if data_handler else {}
|
||||
|
||||
@override
|
||||
async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
|
||||
@@ -85,7 +99,12 @@ class NetatmoSource(MediaSource):
|
||||
)
|
||||
title = f"{created} - {message}"
|
||||
else:
|
||||
title = self.hass.data[DOMAIN][DATA_CAMERAS].get(camera_id, MANUFACTURER)
|
||||
data_handler = self._data_handler
|
||||
title = (
|
||||
data_handler.cameras.get(camera_id, MANUFACTURER)
|
||||
if data_handler
|
||||
else MANUFACTURER
|
||||
)
|
||||
thumbnail = None
|
||||
|
||||
if event_id:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""Support for the Netatmo climate schedule selector."""
|
||||
# pylint: disable=home-assistant-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern
|
||||
|
||||
import logging
|
||||
from typing import override
|
||||
@@ -12,7 +11,6 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
CONF_URL_ENERGY,
|
||||
DATA_SCHEDULES,
|
||||
DOMAIN,
|
||||
EVENT_TYPE_SCHEDULE,
|
||||
MANUFACTURER,
|
||||
@@ -100,18 +98,16 @@ class NetatmoScheduleSelect(NetatmoBaseEntity, SelectEntity):
|
||||
return
|
||||
|
||||
if data["event_type"] == EVENT_TYPE_SCHEDULE and "schedule_id" in data:
|
||||
if schedule := self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
self.home.entity_id
|
||||
].get(data["schedule_id"]):
|
||||
if schedule := self.data_handler.schedules[self.home.entity_id].get(
|
||||
data["schedule_id"]
|
||||
):
|
||||
self._attr_current_option = schedule.name
|
||||
self.async_write_ha_state()
|
||||
|
||||
@override
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
for sid, schedule in self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
self.home.entity_id
|
||||
].items():
|
||||
for sid, schedule in self.data_handler.schedules[self.home.entity_id].items():
|
||||
if schedule.name != option:
|
||||
continue
|
||||
_LOGGER.debug(
|
||||
@@ -130,9 +126,7 @@ class NetatmoScheduleSelect(NetatmoBaseEntity, SelectEntity):
|
||||
schedule = self.home.get_selected_schedule()
|
||||
assert schedule
|
||||
self._attr_current_option = schedule.name
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self.home.entity_id] = (
|
||||
self.home.schedules
|
||||
)
|
||||
self.data_handler.schedules[self.home.entity_id] = self.home.schedules
|
||||
self._attr_options = [
|
||||
schedule.name for schedule in self.home.schedules.values() if schedule.name
|
||||
]
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
"""Services for the Netatmo integration."""
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
|
||||
from .const import DOMAIN
|
||||
from .data_handler import NetatmoConfigEntry
|
||||
from .data_handler import NetatmoConfigEntry, async_get_loaded_entry
|
||||
from .webhook import async_register_webhook, async_unregister_webhook
|
||||
|
||||
SERVICE_REGISTER_WEBHOOK = "register_webhook"
|
||||
@@ -15,10 +14,7 @@ SERVICE_UNREGISTER_WEBHOOK = "unregister_webhook"
|
||||
|
||||
def _get_loaded_entry(hass: HomeAssistant) -> NetatmoConfigEntry:
|
||||
"""Return the loaded config entry or raise if unavailable."""
|
||||
entry: NetatmoConfigEntry | None = (
|
||||
hass.config_entries.async_entry_for_domain_unique_id(DOMAIN, DOMAIN)
|
||||
)
|
||||
if entry is None or entry.state is not ConfigEntryState.LOADED:
|
||||
if (entry := async_get_loaded_entry(hass)) is None:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="entry_not_loaded",
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"""The Netatmo integration."""
|
||||
|
||||
# pylint: disable=home-assistant-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern
|
||||
|
||||
import logging
|
||||
import secrets
|
||||
from typing import Any
|
||||
|
||||
from aiohttp.web import Request
|
||||
import pyatmo
|
||||
@@ -31,8 +30,6 @@ from .const import (
|
||||
ATTR_HOME_ID,
|
||||
ATTR_IS_KNOWN,
|
||||
CONF_CLOUDHOOK_URL,
|
||||
DATA_DEVICE_IDS,
|
||||
DATA_PERSONS,
|
||||
DEFAULT_PERSON,
|
||||
DOMAIN,
|
||||
EVENT_ID_MAP,
|
||||
@@ -40,7 +37,7 @@ from .const import (
|
||||
WEBHOOK_DEACTIVATION,
|
||||
WEBHOOK_PUSH_TYPE,
|
||||
)
|
||||
from .data_handler import NetatmoConfigEntry
|
||||
from .data_handler import NetatmoConfigEntry, NetatmoDataHandler
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -62,19 +59,31 @@ async def async_handle_webhook(
|
||||
|
||||
_LOGGER.debug("Got webhook data: %s", data)
|
||||
|
||||
entry = next(
|
||||
(
|
||||
entry
|
||||
for entry in hass.config_entries.async_loaded_entries(DOMAIN)
|
||||
if entry.data.get(CONF_WEBHOOK_ID) == webhook_id
|
||||
),
|
||||
None,
|
||||
)
|
||||
if entry is None:
|
||||
return
|
||||
data_handler = entry.runtime_data
|
||||
|
||||
event_type = data.get(ATTR_EVENT_TYPE)
|
||||
|
||||
if event_type in SUBEVENT_TYPE_MAP:
|
||||
async_send_event(hass, event_type, data)
|
||||
async_send_event(data_handler, event_type, data)
|
||||
|
||||
for event_data in data.get(SUBEVENT_TYPE_MAP[event_type], []):
|
||||
async_evaluate_event(hass, event_data)
|
||||
async_evaluate_event(data_handler, event_data)
|
||||
|
||||
else:
|
||||
async_evaluate_event(hass, data)
|
||||
async_evaluate_event(data_handler, data)
|
||||
|
||||
|
||||
def async_evaluate_event(hass: HomeAssistant, event_data: dict) -> None:
|
||||
def async_evaluate_event(data_handler: NetatmoDataHandler, event_data: dict) -> None:
|
||||
"""Evaluate events from webhook."""
|
||||
event_type = event_data.get(ATTR_EVENT_TYPE, "None")
|
||||
|
||||
@@ -82,20 +91,23 @@ def async_evaluate_event(hass: HomeAssistant, event_data: dict) -> None:
|
||||
for person in event_data.get(ATTR_PERSONS, {}):
|
||||
person_event_data = dict(event_data)
|
||||
person_event_data[ATTR_ID] = person.get(ATTR_ID)
|
||||
person_event_data[ATTR_NAME] = hass.data[DOMAIN][DATA_PERSONS][
|
||||
person_event_data[ATTR_NAME] = data_handler.persons[
|
||||
event_data[ATTR_HOME_ID]
|
||||
].get(person_event_data[ATTR_ID], DEFAULT_PERSON)
|
||||
person_event_data[ATTR_IS_KNOWN] = person.get(ATTR_IS_KNOWN)
|
||||
person_event_data[ATTR_FACE_URL] = person.get(ATTR_FACE_URL)
|
||||
|
||||
async_send_event(hass, event_type, person_event_data)
|
||||
async_send_event(data_handler, event_type, person_event_data)
|
||||
|
||||
else:
|
||||
async_send_event(hass, event_type, event_data)
|
||||
async_send_event(data_handler, event_type, event_data)
|
||||
|
||||
|
||||
def async_send_event(hass: HomeAssistant, event_type: str, data: dict) -> None:
|
||||
def async_send_event(
|
||||
data_handler: NetatmoDataHandler, event_type: str, data: dict
|
||||
) -> None:
|
||||
"""Send events."""
|
||||
hass = data_handler.hass
|
||||
_LOGGER.debug("%s: %s", event_type, data)
|
||||
async_dispatcher_send(
|
||||
hass,
|
||||
@@ -103,16 +115,14 @@ def async_send_event(hass: HomeAssistant, event_type: str, data: dict) -> None:
|
||||
{"type": event_type, "data": data},
|
||||
)
|
||||
|
||||
event_data = {
|
||||
event_data: dict[str, Any] = {
|
||||
"type": event_type,
|
||||
"data": data,
|
||||
}
|
||||
|
||||
if event_type in EVENT_ID_MAP:
|
||||
data_device_id = data[EVENT_ID_MAP[event_type]]
|
||||
event_data[ATTR_DEVICE_ID] = hass.data[DOMAIN][DATA_DEVICE_IDS].get(
|
||||
data_device_id
|
||||
)
|
||||
event_data[ATTR_DEVICE_ID] = data_handler.device_ids.get(data_device_id)
|
||||
|
||||
hass.bus.async_fire(
|
||||
event_type=NETATMO_EVENT,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Test Local Media Source."""
|
||||
|
||||
import ast
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -12,24 +13,30 @@ from homeassistant.components.media_source import (
|
||||
async_browse_media,
|
||||
async_resolve_media,
|
||||
)
|
||||
from homeassistant.components.netatmo import DATA_CAMERAS, DATA_EVENTS, DOMAIN
|
||||
from homeassistant.components.netatmo.const import DOMAIN
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import async_load_fixture
|
||||
from .common import selected_platforms
|
||||
|
||||
from tests.common import MockConfigEntry, async_load_fixture
|
||||
|
||||
|
||||
async def test_async_browse_media(hass: HomeAssistant) -> None:
|
||||
async def test_async_browse_media(
|
||||
hass: HomeAssistant, config_entry: MockConfigEntry, netatmo_auth: AsyncMock
|
||||
) -> None:
|
||||
"""Test browse media."""
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
with selected_platforms([Platform.CAMERA]):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Prepare cached Netatmo event date
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN][DATA_EVENTS] = ast.literal_eval(
|
||||
# Prepare cached Netatmo event data
|
||||
data_handler = config_entry.runtime_data
|
||||
data_handler.events = ast.literal_eval(
|
||||
await async_load_fixture(hass, "events.txt", DOMAIN)
|
||||
)
|
||||
|
||||
hass.data[DOMAIN][DATA_CAMERAS] = {
|
||||
data_handler.cameras = {
|
||||
"12:34:56:78:90:ab": "MyCamera",
|
||||
"12:34:56:78:90:ac": "MyOutdoorCamera",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user