mirror of
https://github.com/home-assistant/core.git
synced 2026-04-13 13:16:15 +02:00
Compare commits
19 Commits
python-3.1
...
edenhaus-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46f8374063 | ||
|
|
109ec0705c | ||
|
|
6f7fa85d18 | ||
|
|
8d2564f00f | ||
|
|
f7096e3744 | ||
|
|
d7f28a09bb | ||
|
|
a54ea071f8 | ||
|
|
1597b740da | ||
|
|
3758d606c9 | ||
|
|
a79988aca7 | ||
|
|
837cd7d89d | ||
|
|
038bb6c15d | ||
|
|
6ccede7f30 | ||
|
|
fb541d8835 | ||
|
|
39a2c08d4e | ||
|
|
ea642980f2 | ||
|
|
4c8ea3669c | ||
|
|
14f24226ae | ||
|
|
3a9f805f10 |
@@ -62,14 +62,32 @@ class ResourceStorageCollection(collection.DictStorageCollection):
|
||||
)
|
||||
self.ll_config = ll_config
|
||||
|
||||
async def async_get_info(self) -> dict[str, int]:
|
||||
"""Return the resources info for YAML mode."""
|
||||
async def _async_ensure_loaded(self) -> None:
|
||||
"""Ensure the collection has been loaded from storage."""
|
||||
if not self.loaded:
|
||||
await self.async_load()
|
||||
self.loaded = True
|
||||
|
||||
async def async_get_info(self) -> dict[str, int]:
|
||||
"""Return the resources info for YAML mode."""
|
||||
await self._async_ensure_loaded()
|
||||
return {"resources": len(self.async_items() or [])}
|
||||
|
||||
async def async_create_item(self, data: dict) -> dict:
|
||||
"""Create a new item."""
|
||||
await self._async_ensure_loaded()
|
||||
return await super().async_create_item(data)
|
||||
|
||||
async def async_update_item(self, item_id: str, updates: dict) -> dict:
|
||||
"""Update item."""
|
||||
await self._async_ensure_loaded()
|
||||
return await super().async_update_item(item_id, updates)
|
||||
|
||||
async def async_delete_item(self, item_id: str) -> None:
|
||||
"""Delete item."""
|
||||
await self._async_ensure_loaded()
|
||||
await super().async_delete_item(item_id)
|
||||
|
||||
async def _async_load_data(self) -> collection.SerializedStorageCollection | None:
|
||||
"""Load the data."""
|
||||
if (store_data := await self.store.async_load()) is not None:
|
||||
@@ -118,10 +136,6 @@ class ResourceStorageCollection(collection.DictStorageCollection):
|
||||
|
||||
async def _update_data(self, item: dict, update_data: dict) -> dict:
|
||||
"""Return a new updated data object."""
|
||||
if not self.loaded:
|
||||
await self.async_load()
|
||||
self.loaded = True
|
||||
|
||||
update_data = self.UPDATE_SCHEMA(update_data)
|
||||
if CONF_RESOURCE_TYPE_WS in update_data:
|
||||
update_data[CONF_TYPE] = update_data.pop(CONF_RESOURCE_TYPE_WS)
|
||||
|
||||
@@ -71,6 +71,8 @@ class LockUserData(TypedDict):
|
||||
user_type: str
|
||||
credential_rule: str
|
||||
credentials: list[LockUserCredentialData]
|
||||
creator_fabric_index: int | None
|
||||
last_modified_fabric_index: int | None
|
||||
next_user_index: int | None
|
||||
|
||||
|
||||
@@ -115,6 +117,8 @@ class GetLockCredentialStatusResult(TypedDict):
|
||||
|
||||
credential_exists: bool
|
||||
user_index: int | None
|
||||
creator_fabric_index: int | None
|
||||
last_modified_fabric_index: int | None
|
||||
next_credential_index: int | None
|
||||
|
||||
|
||||
@@ -214,6 +218,8 @@ def _format_user_response(user_data: Any) -> LockUserData | None:
|
||||
_get_attr(user_data, "credentialRule"), "unknown"
|
||||
),
|
||||
credentials=credentials,
|
||||
creator_fabric_index=_get_attr(user_data, "creatorFabricIndex"),
|
||||
last_modified_fabric_index=_get_attr(user_data, "lastModifiedFabricIndex"),
|
||||
next_user_index=_get_attr(user_data, "nextUserIndex"),
|
||||
)
|
||||
|
||||
@@ -817,7 +823,8 @@ async def get_lock_credential_status(
|
||||
) -> GetLockCredentialStatusResult:
|
||||
"""Get the status of a credential slot on the lock.
|
||||
|
||||
Returns typed dict with credential_exists, user_index, next_credential_index.
|
||||
Returns typed dict with credential_exists, user_index, creator_fabric_index,
|
||||
last_modified_fabric_index, and next_credential_index.
|
||||
Raises HomeAssistantError on failure.
|
||||
"""
|
||||
lock_endpoint = _get_lock_endpoint_or_raise(node)
|
||||
@@ -839,5 +846,7 @@ async def get_lock_credential_status(
|
||||
return GetLockCredentialStatusResult(
|
||||
credential_exists=bool(_get_attr(response, "credentialExists")),
|
||||
user_index=_get_attr(response, "userIndex"),
|
||||
creator_fabric_index=_get_attr(response, "creatorFabricIndex"),
|
||||
last_modified_fabric_index=_get_attr(response, "lastModifiedFabricIndex"),
|
||||
next_credential_index=_get_attr(response, "nextCredentialIndex"),
|
||||
)
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
"""Support for Plaato devices."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from aiohttp import web
|
||||
from pyplaato.models.airlock import PlaatoAirlock
|
||||
from pyplaato.models.device import PlaatoDevice
|
||||
from pyplaato.plaato import (
|
||||
ATTR_ABV,
|
||||
ATTR_BATCH_VOLUME,
|
||||
@@ -39,21 +43,28 @@ from .const import (
|
||||
CONF_DEVICE_NAME,
|
||||
CONF_DEVICE_TYPE,
|
||||
CONF_USE_WEBHOOK,
|
||||
COORDINATOR,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DEVICE,
|
||||
DEVICE_ID,
|
||||
DEVICE_NAME,
|
||||
DEVICE_TYPE,
|
||||
DOMAIN,
|
||||
PLATFORMS,
|
||||
SENSOR_DATA,
|
||||
UNDO_UPDATE_LISTENER,
|
||||
)
|
||||
from .coordinator import PlaatoCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlaatoData:
|
||||
"""Runtime data for the Plaato integration."""
|
||||
|
||||
coordinator: PlaatoCoordinator | None
|
||||
device_name: str
|
||||
device_type: str
|
||||
device_id: str | None
|
||||
sensor_data: PlaatoDevice | None = field(default=None)
|
||||
|
||||
|
||||
type PlaatoConfigEntry = ConfigEntry[PlaatoData]
|
||||
|
||||
DEPENDENCIES = ["webhook"]
|
||||
|
||||
SENSOR_UPDATE = f"{DOMAIN}_sensor_update"
|
||||
@@ -82,15 +93,15 @@ WEBHOOK_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: PlaatoConfigEntry) -> bool:
|
||||
"""Configure based on config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
if entry.data[CONF_USE_WEBHOOK]:
|
||||
async_setup_webhook(hass, entry)
|
||||
else:
|
||||
await async_setup_coordinator(hass, entry)
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(_async_update_listener))
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(
|
||||
entry, [platform for platform in PLATFORMS if entry.options.get(platform, True)]
|
||||
)
|
||||
@@ -99,19 +110,26 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_webhook(hass: HomeAssistant, entry: ConfigEntry):
|
||||
def async_setup_webhook(hass: HomeAssistant, entry: PlaatoConfigEntry) -> None:
|
||||
"""Init webhook based on config entry."""
|
||||
webhook_id = entry.data[CONF_WEBHOOK_ID]
|
||||
device_name = entry.data[CONF_DEVICE_NAME]
|
||||
|
||||
_set_entry_data(entry, hass)
|
||||
entry.runtime_data = PlaatoData(
|
||||
coordinator=None,
|
||||
device_name=entry.data[CONF_DEVICE_NAME],
|
||||
device_type=entry.data[CONF_DEVICE_TYPE],
|
||||
device_id=None,
|
||||
)
|
||||
|
||||
webhook.async_register(
|
||||
hass, DOMAIN, f"{DOMAIN}.{device_name}", webhook_id, handle_webhook
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_coordinator(hass: HomeAssistant, entry: ConfigEntry):
|
||||
async def async_setup_coordinator(
|
||||
hass: HomeAssistant, entry: PlaatoConfigEntry
|
||||
) -> None:
|
||||
"""Init auth token based on config entry."""
|
||||
auth_token = entry.data[CONF_TOKEN]
|
||||
device_type = entry.data[CONF_DEVICE_TYPE]
|
||||
@@ -126,62 +144,44 @@ async def async_setup_coordinator(hass: HomeAssistant, entry: ConfigEntry):
|
||||
)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
_set_entry_data(entry, hass, coordinator, auth_token)
|
||||
entry.runtime_data = PlaatoData(
|
||||
coordinator=coordinator,
|
||||
device_name=entry.data[CONF_DEVICE_NAME],
|
||||
device_type=entry.data[CONF_DEVICE_TYPE],
|
||||
device_id=auth_token,
|
||||
)
|
||||
|
||||
for platform in PLATFORMS:
|
||||
if entry.options.get(platform, True):
|
||||
coordinator.platforms.append(platform)
|
||||
|
||||
|
||||
def _set_entry_data(entry, hass, coordinator=None, device_id=None):
|
||||
device = {
|
||||
DEVICE_NAME: entry.data[CONF_DEVICE_NAME],
|
||||
DEVICE_TYPE: entry.data[CONF_DEVICE_TYPE],
|
||||
DEVICE_ID: device_id,
|
||||
}
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
COORDINATOR: coordinator,
|
||||
DEVICE: device,
|
||||
SENSOR_DATA: None,
|
||||
UNDO_UPDATE_LISTENER: entry.add_update_listener(_async_update_listener),
|
||||
}
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: PlaatoConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
use_webhook = entry.data[CONF_USE_WEBHOOK]
|
||||
hass.data[DOMAIN][entry.entry_id][UNDO_UPDATE_LISTENER]()
|
||||
|
||||
if use_webhook:
|
||||
if entry.data[CONF_USE_WEBHOOK]:
|
||||
return await async_unload_webhook(hass, entry)
|
||||
|
||||
return await async_unload_coordinator(hass, entry)
|
||||
|
||||
|
||||
async def async_unload_webhook(hass: HomeAssistant, entry: ConfigEntry):
|
||||
async def async_unload_webhook(hass: HomeAssistant, entry: PlaatoConfigEntry) -> bool:
|
||||
"""Unload webhook based entry."""
|
||||
if entry.data[CONF_WEBHOOK_ID] is not None:
|
||||
webhook.async_unregister(hass, entry.data[CONF_WEBHOOK_ID])
|
||||
return await async_unload_platforms(hass, entry, PLATFORMS)
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_unload_coordinator(hass: HomeAssistant, entry: ConfigEntry):
|
||||
async def async_unload_coordinator(
|
||||
hass: HomeAssistant, entry: PlaatoConfigEntry
|
||||
) -> bool:
|
||||
"""Unload auth token based entry."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR]
|
||||
return await async_unload_platforms(hass, entry, coordinator.platforms)
|
||||
coordinator = entry.runtime_data.coordinator
|
||||
return await hass.config_entries.async_unload_platforms(
|
||||
entry, coordinator.platforms if coordinator else PLATFORMS
|
||||
)
|
||||
|
||||
|
||||
async def async_unload_platforms(hass: HomeAssistant, entry: ConfigEntry, platforms):
|
||||
"""Unload platforms."""
|
||||
unloaded = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unloaded:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unloaded
|
||||
|
||||
|
||||
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
async def _async_update_listener(hass: HomeAssistant, entry: PlaatoConfigEntry) -> None:
|
||||
"""Handle options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
@@ -8,17 +8,17 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import CONF_USE_WEBHOOK, COORDINATOR, DOMAIN
|
||||
from . import PlaatoConfigEntry
|
||||
from .const import CONF_USE_WEBHOOK
|
||||
from .entity import PlaatoEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PlaatoConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Plaato from a config entry."""
|
||||
@@ -26,10 +26,12 @@ async def async_setup_entry(
|
||||
if config_entry.data[CONF_USE_WEBHOOK]:
|
||||
return
|
||||
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
|
||||
entry_data = config_entry.runtime_data
|
||||
coordinator = entry_data.coordinator
|
||||
assert coordinator is not None
|
||||
async_add_entities(
|
||||
PlaatoBinarySensor(
|
||||
hass.data[DOMAIN][config_entry.entry_id],
|
||||
entry_data,
|
||||
sensor_type,
|
||||
coordinator,
|
||||
)
|
||||
|
||||
@@ -19,13 +19,7 @@ PLACEHOLDER_DEVICE_TYPE = "device_type"
|
||||
PLACEHOLDER_DEVICE_NAME = "device_name"
|
||||
DOCS_URL = "https://www.home-assistant.io/integrations/plaato/"
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
SENSOR_DATA = "sensor_data"
|
||||
COORDINATOR = "coordinator"
|
||||
DEVICE = "device"
|
||||
DEVICE_NAME = "device_name"
|
||||
DEVICE_TYPE = "device_type"
|
||||
DEVICE_ID = "device_id"
|
||||
UNDO_UPDATE_LISTENER = "undo_update_listener"
|
||||
|
||||
DEFAULT_SCAN_INTERVAL = 5
|
||||
MIN_UPDATE_INTERVAL = timedelta(minutes=1)
|
||||
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
"""Coordinator for Plaato devices."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pyplaato.models.device import PlaatoDevice
|
||||
from pyplaato.plaato import Plaato, PlaatoDeviceType
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import PlaatoConfigEntry
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PlaatoCoordinator(DataUpdateCoordinator):
|
||||
class PlaatoCoordinator(DataUpdateCoordinator[PlaatoDevice]):
|
||||
"""Class to manage fetching data from the API."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: PlaatoConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: PlaatoConfigEntry,
|
||||
auth_token: str,
|
||||
device_type: PlaatoDeviceType,
|
||||
update_interval: timedelta,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""PlaatoEntity class."""
|
||||
|
||||
from typing import Any
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from pyplaato.models.device import PlaatoDevice
|
||||
|
||||
@@ -8,16 +10,10 @@ from homeassistant.helpers import entity
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import (
|
||||
DEVICE,
|
||||
DEVICE_ID,
|
||||
DEVICE_NAME,
|
||||
DEVICE_TYPE,
|
||||
DOMAIN,
|
||||
EXTRA_STATE_ATTRIBUTES,
|
||||
SENSOR_DATA,
|
||||
SENSOR_SIGNAL,
|
||||
)
|
||||
from .const import DOMAIN, EXTRA_STATE_ATTRIBUTES, SENSOR_SIGNAL
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import PlaatoData
|
||||
|
||||
|
||||
class PlaatoEntity(entity.Entity):
|
||||
@@ -25,21 +21,21 @@ class PlaatoEntity(entity.Entity):
|
||||
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, data, sensor_type, coordinator=None):
|
||||
def __init__(self, data: PlaatoData, sensor_type, coordinator=None) -> None:
|
||||
"""Initialize the sensor."""
|
||||
self._coordinator = coordinator
|
||||
self._entry_data = data
|
||||
self._sensor_type = sensor_type
|
||||
self._device_id = data[DEVICE][DEVICE_ID]
|
||||
self._device_type = data[DEVICE][DEVICE_TYPE]
|
||||
self._device_name = data[DEVICE][DEVICE_NAME]
|
||||
self._device_id = data.device_id
|
||||
self._device_type = data.device_type
|
||||
self._device_name = data.device_name
|
||||
self._attr_unique_id = f"{self._device_id}_{self._sensor_type}"
|
||||
self._attr_name = f"{DOMAIN} {self._device_type} {self._device_name} {self._sensor_name}".title()
|
||||
sw_version = None
|
||||
if firmware := self._sensor_data.firmware_version:
|
||||
sw_version = firmware
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._device_id)},
|
||||
identifiers={(DOMAIN, self._device_id)}, # type: ignore[arg-type]
|
||||
manufacturer="Plaato",
|
||||
model=self._device_type,
|
||||
name=self._device_name,
|
||||
@@ -58,7 +54,7 @@ class PlaatoEntity(entity.Entity):
|
||||
def _sensor_data(self) -> PlaatoDevice:
|
||||
if self._coordinator:
|
||||
return self._coordinator.data
|
||||
return self._entry_data[SENSOR_DATA]
|
||||
return self._entry_data.sensor_data
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any] | None:
|
||||
|
||||
@@ -6,7 +6,6 @@ from pyplaato.models.device import PlaatoDevice
|
||||
from pyplaato.plaato import PlaatoKeg
|
||||
|
||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
@@ -18,16 +17,8 @@ from homeassistant.helpers.entity_platform import (
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import ATTR_TEMP, SENSOR_UPDATE
|
||||
from .const import (
|
||||
CONF_USE_WEBHOOK,
|
||||
COORDINATOR,
|
||||
DEVICE,
|
||||
DEVICE_ID,
|
||||
DOMAIN,
|
||||
SENSOR_DATA,
|
||||
SENSOR_SIGNAL,
|
||||
)
|
||||
from . import ATTR_TEMP, SENSOR_UPDATE, PlaatoConfigEntry
|
||||
from .const import CONF_USE_WEBHOOK, SENSOR_SIGNAL
|
||||
from .entity import PlaatoEntity
|
||||
|
||||
|
||||
@@ -42,19 +33,19 @@ async def async_setup_platform(
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: PlaatoConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Plaato from a config entry."""
|
||||
entry_data = hass.data[DOMAIN][entry.entry_id]
|
||||
entry_data = entry.runtime_data
|
||||
|
||||
@callback
|
||||
def _async_update_from_webhook(device_id, sensor_data: PlaatoDevice):
|
||||
"""Update/Create the sensors."""
|
||||
entry_data[SENSOR_DATA] = sensor_data
|
||||
entry_data.sensor_data = sensor_data
|
||||
|
||||
if device_id != entry_data[DEVICE][DEVICE_ID]:
|
||||
entry_data[DEVICE][DEVICE_ID] = device_id
|
||||
if device_id != entry_data.device_id:
|
||||
entry_data.device_id = device_id
|
||||
async_add_entities(
|
||||
[
|
||||
PlaatoSensor(entry_data, sensor_type)
|
||||
@@ -68,7 +59,8 @@ async def async_setup_entry(
|
||||
if entry.data[CONF_USE_WEBHOOK]:
|
||||
async_dispatcher_connect(hass, SENSOR_UPDATE, _async_update_from_webhook)
|
||||
else:
|
||||
coordinator = entry_data[COORDINATOR]
|
||||
coordinator = entry_data.coordinator
|
||||
assert coordinator is not None
|
||||
async_add_entities(
|
||||
PlaatoSensor(entry_data, sensor_type, coordinator)
|
||||
for sensor_type in coordinator.data.sensors
|
||||
|
||||
@@ -7,6 +7,7 @@ from ring_doorbell import RingCapability, RingEvent as RingAlert
|
||||
from ring_doorbell.const import KIND_DING, KIND_INTERCOM_UNLOCK, KIND_MOTION
|
||||
|
||||
from homeassistant.components.event import (
|
||||
DoorbellEventType,
|
||||
EventDeviceClass,
|
||||
EventEntity,
|
||||
EventEntityDescription,
|
||||
@@ -34,7 +35,7 @@ EVENT_DESCRIPTIONS: tuple[RingEventEntityDescription, ...] = (
|
||||
key=KIND_DING,
|
||||
translation_key=KIND_DING,
|
||||
device_class=EventDeviceClass.DOORBELL,
|
||||
event_types=[KIND_DING],
|
||||
event_types=[DoorbellEventType.RING],
|
||||
capability=RingCapability.DING,
|
||||
),
|
||||
RingEventEntityDescription(
|
||||
@@ -100,7 +101,10 @@ class RingEvent(RingBaseEntity[RingListenCoordinator, RingDeviceT], EventEntity)
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
if (alert := self._get_coordinator_alert()) and not alert.is_update:
|
||||
self._async_handle_event(alert.kind)
|
||||
if alert.kind == KIND_DING:
|
||||
self._async_handle_event(DoorbellEventType.RING)
|
||||
else:
|
||||
self._async_handle_event(alert.kind)
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
|
||||
@@ -73,7 +73,14 @@
|
||||
},
|
||||
"event": {
|
||||
"ding": {
|
||||
"name": "Ding"
|
||||
"name": "Ding",
|
||||
"state_attributes": {
|
||||
"event_type": {
|
||||
"state": {
|
||||
"ring": "[%key:component::event::entity_component::doorbell::state_attributes::event_type::state::ring%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"intercom_unlock": {
|
||||
"name": "Intercom unlock"
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
|
||||
from sanix import Sanix
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_TOKEN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_SERIAL_NUMBER, DOMAIN
|
||||
from .coordinator import SanixCoordinator
|
||||
from .const import CONF_SERIAL_NUMBER
|
||||
from .coordinator import SanixConfigEntry, SanixCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SanixConfigEntry) -> bool:
|
||||
"""Set up Sanix from a config entry."""
|
||||
|
||||
serial_no = entry.data[CONF_SERIAL_NUMBER]
|
||||
@@ -22,16 +21,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
coordinator = SanixCoordinator(hass, entry, sanix_api)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SanixConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -15,14 +15,16 @@ from .const import MANUFACTURER
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
type SanixConfigEntry = ConfigEntry[SanixCoordinator]
|
||||
|
||||
|
||||
class SanixCoordinator(DataUpdateCoordinator[Measurement]):
|
||||
"""Sanix coordinator."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: SanixConfigEntry
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, sanix_api: Sanix
|
||||
self, hass: HomeAssistant, config_entry: SanixConfigEntry, sanix_api: Sanix
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
super().__init__(
|
||||
|
||||
@@ -20,7 +20,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import PERCENTAGE, UnitOfLength
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
@@ -28,7 +27,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN, MANUFACTURER
|
||||
from .coordinator import SanixCoordinator
|
||||
from .coordinator import SanixConfigEntry, SanixCoordinator
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -83,11 +82,11 @@ SENSOR_TYPES: tuple[SanixSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SanixConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Sanix Sensor entities based on a config entry."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
SanixSensorEntity(coordinator, description) for description in SENSOR_TYPES
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
"""The sia integration."""
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PORT
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
|
||||
from .const import DOMAIN, PLATFORMS
|
||||
from .hub import SIAHub
|
||||
from .const import PLATFORMS
|
||||
from .hub import SIAConfigEntry, SIAHub
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SIAConfigEntry) -> bool:
|
||||
"""Set up sia from a config entry."""
|
||||
hub: SIAHub = SIAHub(hass, entry)
|
||||
hub = SIAHub(hass, entry)
|
||||
hub.async_setup_hub()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = hub
|
||||
try:
|
||||
if hub.sia_client:
|
||||
await hub.sia_client.async_start(reuse_port=True)
|
||||
@@ -23,14 +20,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
raise ConfigEntryNotReady(
|
||||
f"SIA Server at port {entry.data[CONF_PORT]} could not start."
|
||||
) from exc
|
||||
|
||||
entry.runtime_data = hub
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SIAConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hub: SIAHub = hass.data[DOMAIN].pop(entry.entry_id)
|
||||
await hub.async_shutdown()
|
||||
await entry.runtime_data.async_shutdown()
|
||||
return unload_ok
|
||||
|
||||
@@ -16,12 +16,7 @@ from pysiaalarm import (
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
|
||||
from homeassistant.const import CONF_PORT, CONF_PROTOCOL
|
||||
from homeassistant.core import callback
|
||||
|
||||
@@ -36,7 +31,7 @@ from .const import (
|
||||
DOMAIN,
|
||||
TITLE,
|
||||
)
|
||||
from .hub import SIAHub
|
||||
from .hub import SIAConfigEntry, SIAHub
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -100,7 +95,7 @@ class SIAConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SIAConfigEntry,
|
||||
) -> SIAOptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return SIAOptionsFlowHandler(config_entry)
|
||||
@@ -179,7 +174,9 @@ class SIAConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
class SIAOptionsFlowHandler(OptionsFlow):
|
||||
"""Handle SIA options."""
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
config_entry: SIAConfigEntry
|
||||
|
||||
def __init__(self, config_entry: SIAConfigEntry) -> None:
|
||||
"""Initialize SIA options flow."""
|
||||
self.options = deepcopy(dict(config_entry.options))
|
||||
self.hub: SIAHub | None = None
|
||||
@@ -189,7 +186,7 @@ class SIAOptionsFlowHandler(OptionsFlow):
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Manage the SIA options."""
|
||||
self.hub = self.hass.data[DOMAIN][self.config_entry.entry_id]
|
||||
self.hub = self.config_entry.runtime_data
|
||||
assert self.hub is not None
|
||||
assert self.hub.sia_accounts is not None
|
||||
self.accounts_todo = [a.account_id for a in self.hub.sia_accounts]
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Any
|
||||
|
||||
from pysiaalarm.aio import CommunicationsProtocol, SIAAccount, SIAClient, SIAEvent
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import CONF_PORT, CONF_PROTOCOL, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
@@ -28,6 +28,8 @@ from .utils import get_event_data_from_sia_event
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
type SIAConfigEntry = ConfigEntry[SIAHub]
|
||||
|
||||
DEFAULT_TIMEBAND = (80, 40)
|
||||
|
||||
|
||||
@@ -37,11 +39,11 @@ class SIAHub:
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SIAConfigEntry,
|
||||
) -> None:
|
||||
"""Create the SIAHub."""
|
||||
self._hass: HomeAssistant = hass
|
||||
self._entry: ConfigEntry = entry
|
||||
self._hass = hass
|
||||
self._entry = entry
|
||||
self._port: int = entry.data[CONF_PORT]
|
||||
self._title: str = entry.title
|
||||
self._accounts: list[dict[str, Any]] = deepcopy(entry.data[CONF_ACCOUNTS])
|
||||
@@ -131,7 +133,7 @@ class SIAHub:
|
||||
|
||||
@staticmethod
|
||||
async def async_config_entry_updated(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
hass: HomeAssistant, config_entry: SIAConfigEntry
|
||||
) -> None:
|
||||
"""Handle signals of config entry being updated.
|
||||
|
||||
@@ -139,8 +141,8 @@ class SIAHub:
|
||||
Second, unload underlying platforms, and then setup platforms, this reflects any changes in number of zones.
|
||||
|
||||
"""
|
||||
if not (hub := hass.data[DOMAIN].get(config_entry.entry_id)):
|
||||
if config_entry.state != ConfigEntryState.LOADED:
|
||||
return
|
||||
hub.update_accounts()
|
||||
config_entry.runtime_data.update_accounts()
|
||||
await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable, Coroutine
|
||||
from typing import Any, cast
|
||||
from typing import Any
|
||||
|
||||
from simplipy import API
|
||||
from simplipy.errors import (
|
||||
@@ -39,7 +39,7 @@ from simplipy.websocket import (
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE,
|
||||
ATTR_DEVICE_ID,
|
||||
@@ -88,6 +88,8 @@ from .const import (
|
||||
from .coordinator import SimpliSafeDataUpdateCoordinator
|
||||
from .typing import SystemType
|
||||
|
||||
type SimpliSafeConfigEntry = ConfigEntry[SimpliSafe]
|
||||
|
||||
ATTR_CATEGORY = "category"
|
||||
ATTR_LAST_EVENT_CHANGED_BY = "last_event_changed_by"
|
||||
ATTR_LAST_EVENT_SENSOR_SERIAL = "last_event_sensor_serial"
|
||||
@@ -223,10 +225,15 @@ def _async_get_system_for_service_call(
|
||||
]
|
||||
system_id = int(system_id_str)
|
||||
|
||||
entry: SimpliSafeConfigEntry | None
|
||||
for entry_id in base_station_device_entry.config_entries:
|
||||
if (simplisafe := hass.data[DOMAIN].get(entry_id)) is None:
|
||||
if (
|
||||
(entry := hass.config_entries.async_get_entry(entry_id)) is None
|
||||
or entry.domain != DOMAIN
|
||||
or entry.state != ConfigEntryState.LOADED
|
||||
):
|
||||
continue
|
||||
return cast(SystemType, simplisafe.systems[system_id])
|
||||
return entry.runtime_data.systems[system_id]
|
||||
|
||||
raise ValueError(f"No system for device ID: {device_id}")
|
||||
|
||||
@@ -286,7 +293,7 @@ def _async_standardize_config_entry(hass: HomeAssistant, entry: ConfigEntry) ->
|
||||
hass.config_entries.async_update_entry(entry, **entry_updates)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SimpliSafeConfigEntry) -> bool:
|
||||
"""Set up SimpliSafe as config entry."""
|
||||
_async_standardize_config_entry(hass, entry)
|
||||
|
||||
@@ -310,8 +317,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
except SimplipyError as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = simplisafe
|
||||
entry.runtime_data = simplisafe
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -396,11 +402,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SimpliSafeConfigEntry) -> bool:
|
||||
"""Unload a SimpliSafe config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
if not hass.config_entries.async_loaded_entries(DOMAIN):
|
||||
# If this is the last loaded instance of SimpliSafe, deregister any services
|
||||
|
||||
@@ -28,12 +28,11 @@ from homeassistant.components.alarm_control_panel import (
|
||||
AlarmControlPanelEntityFeature,
|
||||
AlarmControlPanelState,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SimpliSafe
|
||||
from . import SimpliSafe, SimpliSafeConfigEntry
|
||||
from .const import (
|
||||
ATTR_ALARM_DURATION,
|
||||
ATTR_ALARM_VOLUME,
|
||||
@@ -44,7 +43,6 @@ from .const import (
|
||||
ATTR_EXIT_DELAY_HOME,
|
||||
ATTR_LIGHT,
|
||||
ATTR_VOICE_PROMPT_VOLUME,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
)
|
||||
from .entity import SimpliSafeEntity
|
||||
@@ -104,11 +102,11 @@ WEBSOCKET_EVENTS_TO_LISTEN_FOR = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SimpliSafeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up a SimpliSafe alarm control panel based on a config entry."""
|
||||
simplisafe = hass.data[DOMAIN][entry.entry_id]
|
||||
simplisafe = entry.runtime_data
|
||||
async_add_entities(
|
||||
[SimpliSafeAlarm(simplisafe, system) for system in simplisafe.systems.values()],
|
||||
True,
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from simplipy.device import DeviceTypes, DeviceV3
|
||||
from simplipy.device.sensor.v3 import SensorV3
|
||||
from simplipy.system.v3 import SystemV3
|
||||
@@ -11,13 +13,12 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SimpliSafe
|
||||
from .const import DOMAIN, LOGGER
|
||||
from . import SimpliSafe, SimpliSafeConfigEntry
|
||||
from .const import LOGGER
|
||||
from .entity import SimpliSafeEntity
|
||||
|
||||
SUPPORTED_BATTERY_SENSOR_TYPES = [
|
||||
@@ -59,11 +60,11 @@ TRIGGERED_SENSOR_TYPES = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SimpliSafeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SimpliSafe binary sensors based on a config entry."""
|
||||
simplisafe = hass.data[DOMAIN][entry.entry_id]
|
||||
simplisafe = entry.runtime_data
|
||||
|
||||
sensors: list[BatteryBinarySensor | TriggeredBinarySensor] = []
|
||||
|
||||
@@ -72,18 +73,22 @@ async def async_setup_entry(
|
||||
LOGGER.warning("Skipping sensor setup for V2 system: %s", system.system_id)
|
||||
continue
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(system, SystemV3)
|
||||
for sensor in system.sensors.values():
|
||||
if sensor.type in TRIGGERED_SENSOR_TYPES:
|
||||
sensors.append(
|
||||
TriggeredBinarySensor(
|
||||
simplisafe,
|
||||
system,
|
||||
sensor,
|
||||
cast(SensorV3, sensor),
|
||||
TRIGGERED_SENSOR_TYPES[sensor.type],
|
||||
)
|
||||
)
|
||||
if sensor.type in SUPPORTED_BATTERY_SENSOR_TYPES:
|
||||
sensors.append(BatteryBinarySensor(simplisafe, system, sensor))
|
||||
sensors.append(
|
||||
BatteryBinarySensor(simplisafe, system, cast(DeviceV3, sensor))
|
||||
)
|
||||
|
||||
sensors.extend(
|
||||
BatteryBinarySensor(simplisafe, system, lock)
|
||||
|
||||
@@ -9,14 +9,12 @@ from simplipy.errors import SimplipyError
|
||||
from simplipy.system import System
|
||||
|
||||
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SimpliSafe
|
||||
from .const import DOMAIN
|
||||
from . import SimpliSafe, SimpliSafeConfigEntry
|
||||
from .entity import SimpliSafeEntity
|
||||
from .typing import SystemType
|
||||
|
||||
@@ -47,11 +45,11 @@ BUTTON_DESCRIPTIONS = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SimpliSafeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SimpliSafe buttons based on a config entry."""
|
||||
simplisafe = hass.data[DOMAIN][entry.entry_id]
|
||||
simplisafe = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
|
||||
@@ -14,16 +14,12 @@ from simplipy.util.auth import (
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
|
||||
from homeassistant.const import CONF_CODE, CONF_TOKEN, CONF_URL, CONF_USERNAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import aiohttp_client, config_validation as cv
|
||||
|
||||
from . import SimpliSafeConfigEntry
|
||||
from .const import DOMAIN, LOGGER
|
||||
|
||||
CONF_AUTH_CODE = "auth_code"
|
||||
@@ -68,7 +64,7 @@ class SimpliSafeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SimpliSafeConfigEntry,
|
||||
) -> SimpliSafeOptionsFlowHandler:
|
||||
"""Define the config flow to handle options."""
|
||||
return SimpliSafeOptionsFlowHandler()
|
||||
|
||||
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_CODE,
|
||||
@@ -16,8 +15,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import SimpliSafe
|
||||
from .const import DOMAIN
|
||||
from . import SimpliSafeConfigEntry
|
||||
|
||||
CONF_CREDIT_CARD = "creditCard"
|
||||
CONF_EXPIRES = "expires"
|
||||
@@ -53,10 +51,10 @@ TO_REDACT = {
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: SimpliSafeConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
simplisafe: SimpliSafe = hass.data[DOMAIN][entry.entry_id]
|
||||
simplisafe = entry.runtime_data
|
||||
|
||||
return async_redact_data(
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from simplipy.device.lock import Lock, LockStates
|
||||
from simplipy.errors import SimplipyError
|
||||
@@ -10,13 +10,12 @@ from simplipy.system.v3 import SystemV3
|
||||
from simplipy.websocket import EVENT_LOCK_LOCKED, EVENT_LOCK_UNLOCKED, WebsocketEvent
|
||||
|
||||
from homeassistant.components.lock import LockEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SimpliSafe
|
||||
from .const import DOMAIN, LOGGER
|
||||
from . import SimpliSafe, SimpliSafeConfigEntry
|
||||
from .const import LOGGER
|
||||
from .entity import SimpliSafeEntity
|
||||
|
||||
ATTR_LOCK_LOW_BATTERY = "lock_low_battery"
|
||||
@@ -32,11 +31,11 @@ WEBSOCKET_EVENTS_TO_LISTEN_FOR = (EVENT_LOCK_LOCKED, EVENT_LOCK_UNLOCKED)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SimpliSafeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SimpliSafe locks based on a config entry."""
|
||||
simplisafe = hass.data[DOMAIN][entry.entry_id]
|
||||
simplisafe = entry.runtime_data
|
||||
locks: list[SimpliSafeLock] = []
|
||||
|
||||
for system in simplisafe.systems.values():
|
||||
@@ -44,6 +43,8 @@ async def async_setup_entry(
|
||||
LOGGER.warning("Skipping lock setup for V2 system: %s", system.system_id)
|
||||
continue
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(system, SystemV3)
|
||||
locks.extend(
|
||||
SimpliSafeLock(simplisafe, system, lock) for lock in system.locks.values()
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from simplipy.device import DeviceTypes
|
||||
from simplipy.device.sensor.v3 import SensorV3
|
||||
from simplipy.system.v3 import SystemV3
|
||||
@@ -11,23 +13,22 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SimpliSafe
|
||||
from .const import DOMAIN, LOGGER
|
||||
from . import SimpliSafe, SimpliSafeConfigEntry
|
||||
from .const import LOGGER
|
||||
from .entity import SimpliSafeEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SimpliSafeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SimpliSafe freeze sensors based on a config entry."""
|
||||
simplisafe = hass.data[DOMAIN][entry.entry_id]
|
||||
simplisafe = entry.runtime_data
|
||||
sensors: list[SimplisafeFreezeSensor] = []
|
||||
|
||||
for system in simplisafe.systems.values():
|
||||
@@ -35,8 +36,10 @@ async def async_setup_entry(
|
||||
LOGGER.warning("Skipping sensor setup for V2 system: %s", system.system_id)
|
||||
continue
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(system, SystemV3)
|
||||
sensors.extend(
|
||||
SimplisafeFreezeSensor(simplisafe, system, sensor)
|
||||
SimplisafeFreezeSensor(simplisafe, system, cast(SensorV3, sensor))
|
||||
for sensor in system.sensors.values()
|
||||
if sensor.type == DeviceTypes.TEMPERATURE
|
||||
)
|
||||
|
||||
@@ -7,14 +7,12 @@ import asyncio
|
||||
from aioskybell import Skybell
|
||||
from aioskybell.exceptions import SkybellAuthenticationException, SkybellException
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SkybellDataUpdateCoordinator
|
||||
from .coordinator import SkybellConfigEntry, SkybellDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
@@ -25,7 +23,7 @@ PLATFORMS = [
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SkybellConfigEntry) -> bool:
|
||||
"""Set up Skybell from a config entry."""
|
||||
email = entry.data[CONF_EMAIL]
|
||||
password = entry.data[CONF_PASSWORD]
|
||||
@@ -53,14 +51,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
for coordinator in device_coordinators
|
||||
]
|
||||
)
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = device_coordinators
|
||||
entry.runtime_data = device_coordinators
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SkybellConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -9,12 +9,10 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import DOMAIN
|
||||
from .coordinator import SkybellDataUpdateCoordinator
|
||||
from .coordinator import SkybellConfigEntry, SkybellDataUpdateCoordinator
|
||||
from .entity import SkybellEntity
|
||||
|
||||
BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = (
|
||||
@@ -32,14 +30,14 @@ BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SkybellConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Skybell binary sensor."""
|
||||
async_add_entities(
|
||||
SkybellBinarySensor(coordinator, sensor)
|
||||
for sensor in BINARY_SENSOR_TYPES
|
||||
for coordinator in hass.data[DOMAIN][entry.entry_id]
|
||||
for coordinator in entry.runtime_data
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -7,14 +7,12 @@ from haffmpeg.camera import CameraMjpeg
|
||||
|
||||
from homeassistant.components.camera import Camera, CameraEntityDescription
|
||||
from homeassistant.components.ffmpeg import get_ffmpeg_manager
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
|
||||
from homeassistant.helpers.entity import EntityDescription
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SkybellDataUpdateCoordinator
|
||||
from .coordinator import SkybellConfigEntry, SkybellDataUpdateCoordinator
|
||||
from .entity import SkybellEntity
|
||||
|
||||
CAMERA_TYPES: tuple[CameraEntityDescription, ...] = (
|
||||
@@ -31,13 +29,13 @@ CAMERA_TYPES: tuple[CameraEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SkybellConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Skybell camera."""
|
||||
entities = []
|
||||
for description in CAMERA_TYPES:
|
||||
for coordinator in hass.data[DOMAIN][entry.entry_id]:
|
||||
for coordinator in entry.runtime_data:
|
||||
if description.key == "avatar":
|
||||
entities.append(SkybellCamera(coordinator, description))
|
||||
else:
|
||||
|
||||
@@ -10,14 +10,19 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
|
||||
from .const import LOGGER
|
||||
|
||||
type SkybellConfigEntry = ConfigEntry[list[SkybellDataUpdateCoordinator]]
|
||||
|
||||
|
||||
class SkybellDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
"""Data update coordinator for the Skybell integration."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: SkybellConfigEntry
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, device: SkybellDevice
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: SkybellConfigEntry,
|
||||
device: SkybellDevice,
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
super().__init__(
|
||||
|
||||
@@ -13,23 +13,22 @@ from homeassistant.components.light import (
|
||||
LightEntity,
|
||||
LightEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SkybellConfigEntry
|
||||
from .entity import SkybellEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SkybellConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Skybell switch."""
|
||||
async_add_entities(
|
||||
SkybellLight(coordinator, LightEntityDescription(key="light"))
|
||||
for coordinator in hass.data[DOMAIN][entry.entry_id]
|
||||
for coordinator in entry.runtime_data
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -14,13 +14,13 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from .entity import DOMAIN, SkybellEntity
|
||||
from .coordinator import SkybellConfigEntry
|
||||
from .entity import SkybellEntity
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -89,13 +89,13 @@ SENSOR_TYPES: tuple[SkybellSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SkybellConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Skybell sensor."""
|
||||
async_add_entities(
|
||||
SkybellSensor(coordinator, description)
|
||||
for coordinator in hass.data[DOMAIN][entry.entry_id]
|
||||
for coordinator in entry.runtime_data
|
||||
for description in SENSOR_TYPES
|
||||
if coordinator.device.owner or description.key not in CONST.ATTR_OWNER_STATS
|
||||
)
|
||||
|
||||
@@ -5,11 +5,10 @@ from __future__ import annotations
|
||||
from typing import Any, cast
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SkybellConfigEntry
|
||||
from .entity import SkybellEntity
|
||||
|
||||
SWITCH_TYPES: tuple[SwitchEntityDescription, ...] = (
|
||||
@@ -30,13 +29,13 @@ SWITCH_TYPES: tuple[SwitchEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SkybellConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SkyBell switch."""
|
||||
async_add_entities(
|
||||
SkybellSwitch(coordinator, description)
|
||||
for coordinator in hass.data[DOMAIN][entry.entry_id]
|
||||
for coordinator in entry.runtime_data
|
||||
for description in SWITCH_TYPES
|
||||
)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
from aiohttp.client_exceptions import ClientError
|
||||
@@ -30,6 +31,17 @@ PLATFORMS = [Platform.NOTIFY, Platform.SENSOR]
|
||||
|
||||
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
|
||||
type SlackConfigEntry = ConfigEntry[SlackData]
|
||||
|
||||
|
||||
@dataclass
|
||||
class SlackData:
|
||||
"""Runtime data for the Slack integration."""
|
||||
|
||||
client: AsyncWebClient
|
||||
url: str
|
||||
user_id: str
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the Slack component."""
|
||||
@@ -37,7 +49,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SlackConfigEntry) -> bool:
|
||||
"""Set up Slack from a config entry."""
|
||||
session = aiohttp_client.async_get_clientsession(hass)
|
||||
slack = AsyncWebClient(
|
||||
@@ -52,19 +64,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return False
|
||||
raise ConfigEntryNotReady("Error while setting up integration") from ex
|
||||
|
||||
data = {
|
||||
DATA_CLIENT: slack,
|
||||
ATTR_URL: res[ATTR_URL],
|
||||
ATTR_USER_ID: res[ATTR_USER_ID],
|
||||
}
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = entry.data | {SLACK_DATA: data}
|
||||
entry.runtime_data = SlackData(
|
||||
client=slack,
|
||||
url=res[ATTR_URL],
|
||||
user_id=res[ATTR_USER_ID],
|
||||
)
|
||||
|
||||
hass.async_create_task(
|
||||
discovery.async_load_platform(
|
||||
hass,
|
||||
Platform.NOTIFY,
|
||||
DOMAIN,
|
||||
hass.data[DOMAIN][entry.entry_id],
|
||||
entry.data
|
||||
| {
|
||||
SLACK_DATA: {
|
||||
DATA_CLIENT: slack,
|
||||
ATTR_URL: res[ATTR_URL],
|
||||
ATTR_USER_ID: res[ATTR_USER_ID],
|
||||
}
|
||||
},
|
||||
hass.data[DATA_HASS_CONFIG],
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
"""The slack integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from slack_sdk.web.async_client import AsyncWebClient
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
|
||||
from .const import ATTR_URL, ATTR_USER_ID, DATA_CLIENT, DEFAULT_NAME, DOMAIN
|
||||
from . import SlackConfigEntry, SlackData
|
||||
from .const import DEFAULT_NAME, DOMAIN
|
||||
|
||||
|
||||
class SlackEntity(Entity):
|
||||
@@ -16,16 +12,16 @@ class SlackEntity(Entity):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: dict[str, AsyncWebClient],
|
||||
data: SlackData,
|
||||
description: EntityDescription,
|
||||
entry: ConfigEntry,
|
||||
entry: SlackConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize a Slack entity."""
|
||||
self._client: AsyncWebClient = data[DATA_CLIENT]
|
||||
self._client = data.client
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{data[ATTR_USER_ID]}_{description.key}"
|
||||
self._attr_unique_id = f"{data.user_id}_{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
configuration_url=str(data[ATTR_URL]),
|
||||
configuration_url=data.url,
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={(DOMAIN, entry.entry_id)},
|
||||
manufacturer=DEFAULT_NAME,
|
||||
|
||||
@@ -9,25 +9,25 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import ATTR_SNOOZE, DOMAIN, SLACK_DATA
|
||||
from . import SlackConfigEntry
|
||||
from .const import ATTR_SNOOZE
|
||||
from .entity import SlackEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SlackConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Slack select."""
|
||||
"""Set up the Slack sensor."""
|
||||
async_add_entities(
|
||||
[
|
||||
SlackSensorEntity(
|
||||
hass.data[DOMAIN][entry.entry_id][SLACK_DATA],
|
||||
entry.runtime_data,
|
||||
SensorEntityDescription(
|
||||
key="do_not_disturb_until",
|
||||
translation_key="do_not_disturb_until",
|
||||
|
||||
@@ -23,6 +23,7 @@ from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DOMAIN, IS_IN_BED, SLEEP_NUMBER
|
||||
from .coordinator import (
|
||||
SleepIQConfigEntry,
|
||||
SleepIQData,
|
||||
SleepIQDataUpdateCoordinator,
|
||||
SleepIQPauseUpdateCoordinator,
|
||||
@@ -64,7 +65,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SleepIQConfigEntry) -> bool:
|
||||
"""Set up the SleepIQ config entry."""
|
||||
conf = entry.data
|
||||
email = conf[CONF_USERNAME]
|
||||
@@ -104,7 +105,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
await pause_coordinator.async_config_entry_first_refresh()
|
||||
await sleep_data_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = SleepIQData(
|
||||
entry.runtime_data = SleepIQData(
|
||||
data_coordinator=coordinator,
|
||||
pause_coordinator=pause_coordinator,
|
||||
sleep_data_coordinator=sleep_data_coordinator,
|
||||
@@ -116,11 +117,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SleepIQConfigEntry) -> bool:
|
||||
"""Unload the config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def _async_migrate_unique_ids(
|
||||
|
||||
@@ -6,22 +6,21 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN, ICON_EMPTY, ICON_OCCUPIED, IS_IN_BED
|
||||
from .coordinator import SleepIQData, SleepIQDataUpdateCoordinator
|
||||
from .const import ICON_EMPTY, ICON_OCCUPIED, IS_IN_BED
|
||||
from .coordinator import SleepIQConfigEntry, SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQSleeperEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SleepIQConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed binary sensors."""
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
async_add_entities(
|
||||
IsInBedBinarySensor(data.data_coordinator, bed, sleeper)
|
||||
for bed in data.client.beds.values()
|
||||
|
||||
@@ -9,12 +9,10 @@ from typing import Any
|
||||
from asyncsleepiq import SleepIQBed
|
||||
|
||||
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SleepIQData
|
||||
from .coordinator import SleepIQConfigEntry
|
||||
from .entity import SleepIQEntity
|
||||
|
||||
|
||||
@@ -43,11 +41,11 @@ ENTITY_DESCRIPTIONS = [
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SleepIQConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the sleep number buttons."""
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
SleepNumberButton(bed, ed)
|
||||
|
||||
@@ -18,16 +18,18 @@ UPDATE_INTERVAL = timedelta(seconds=60)
|
||||
LONGER_UPDATE_INTERVAL = timedelta(minutes=5)
|
||||
SLEEP_DATA_UPDATE_INTERVAL = timedelta(hours=1) # Sleep data doesn't change frequently
|
||||
|
||||
type SleepIQConfigEntry = ConfigEntry[SleepIQData]
|
||||
|
||||
|
||||
class SleepIQDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
"""SleepIQ data update coordinator."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: SleepIQConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SleepIQConfigEntry,
|
||||
client: AsyncSleepIQ,
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
@@ -51,12 +53,12 @@ class SleepIQDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
class SleepIQPauseUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
"""SleepIQ data update coordinator."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: SleepIQConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SleepIQConfigEntry,
|
||||
client: AsyncSleepIQ,
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
@@ -78,12 +80,12 @@ class SleepIQPauseUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
class SleepIQSleepDataCoordinator(DataUpdateCoordinator[None]):
|
||||
"""SleepIQ sleep health data coordinator."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: SleepIQConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SleepIQConfigEntry,
|
||||
client: AsyncSleepIQ,
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
|
||||
@@ -6,12 +6,10 @@ from typing import Any
|
||||
from asyncsleepiq import SleepIQBed, SleepIQLight
|
||||
|
||||
from homeassistant.components.light import ColorMode, LightEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SleepIQData, SleepIQDataUpdateCoordinator
|
||||
from .coordinator import SleepIQConfigEntry, SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQBedEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -19,11 +17,11 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SleepIQConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed lights."""
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SleepIQLightEntity(data.data_coordinator, bed, light)
|
||||
for bed in data.client.beds.values()
|
||||
|
||||
@@ -21,7 +21,6 @@ from homeassistant.components.number import (
|
||||
NumberEntity,
|
||||
NumberEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import UnitOfTime
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
@@ -29,13 +28,12 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from .const import (
|
||||
ACTUATOR,
|
||||
CORE_CLIMATE_TIMER,
|
||||
DOMAIN,
|
||||
ENTITY_TYPES,
|
||||
FIRMNESS,
|
||||
FOOT_WARMING_TIMER,
|
||||
ICON_OCCUPIED,
|
||||
)
|
||||
from .coordinator import SleepIQData, SleepIQDataUpdateCoordinator
|
||||
from .coordinator import SleepIQConfigEntry, SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQBedEntity, sleeper_for_side
|
||||
|
||||
|
||||
@@ -180,11 +178,11 @@ NUMBER_DESCRIPTIONS: dict[str, SleepIQNumberEntityDescription] = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SleepIQConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed sensors."""
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
|
||||
entities: list[SleepIQNumberEntity] = []
|
||||
for bed in data.client.beds.values():
|
||||
|
||||
@@ -13,22 +13,21 @@ from asyncsleepiq import (
|
||||
)
|
||||
|
||||
from homeassistant.components.select import SelectEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import CORE_CLIMATE, DOMAIN, FOOT_WARMER
|
||||
from .coordinator import SleepIQData, SleepIQDataUpdateCoordinator
|
||||
from .const import CORE_CLIMATE, FOOT_WARMER
|
||||
from .coordinator import SleepIQConfigEntry, SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQBedEntity, SleepIQSleeperEntity, sleeper_for_side
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SleepIQConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ foundation preset select entities."""
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
entities: list[SleepIQBedEntity] = []
|
||||
for bed in data.client.beds.values():
|
||||
entities.extend(
|
||||
|
||||
@@ -13,13 +13,11 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import UnitOfTime
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
HEART_RATE,
|
||||
HRV,
|
||||
PRESSURE,
|
||||
@@ -29,7 +27,7 @@ from .const import (
|
||||
SLEEP_SCORE,
|
||||
)
|
||||
from .coordinator import (
|
||||
SleepIQData,
|
||||
SleepIQConfigEntry,
|
||||
SleepIQDataUpdateCoordinator,
|
||||
SleepIQSleepDataCoordinator,
|
||||
)
|
||||
@@ -112,11 +110,11 @@ SLEEP_HEALTH_SENSORS: tuple[SleepIQSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SleepIQConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed sensors."""
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
|
||||
entities: list[SensorEntity] = []
|
||||
|
||||
|
||||
@@ -7,22 +7,20 @@ from typing import Any
|
||||
from asyncsleepiq import SleepIQBed
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SleepIQData, SleepIQPauseUpdateCoordinator
|
||||
from .coordinator import SleepIQConfigEntry, SleepIQPauseUpdateCoordinator
|
||||
from .entity import SleepIQBedEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SleepIQConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the sleep number switches."""
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SleepNumberPrivateSwitch(data.pause_coordinator, bed)
|
||||
for bed in data.client.beds.values()
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
"""The soundtouch component."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from libsoundtouch import soundtouch_device
|
||||
from libsoundtouch.device import SoundTouchDevice
|
||||
@@ -22,6 +25,11 @@ from .const import (
|
||||
SERVICE_REMOVE_ZONE_SLAVE,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .media_player import SoundTouchMediaPlayer
|
||||
|
||||
type SoundTouchConfigEntry = ConfigEntry[SoundTouchData]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVICE_PLAY_EVERYWHERE_SCHEMA = vol.Schema({vol.Required("master"): cv.entity_id})
|
||||
@@ -50,12 +58,12 @@ CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
|
||||
|
||||
class SoundTouchData:
|
||||
"""SoundTouch data stored in the Home Assistant data object."""
|
||||
"""SoundTouch data stored in the config entry runtime data."""
|
||||
|
||||
def __init__(self, device: SoundTouchDevice) -> None:
|
||||
"""Initialize the SoundTouch data object for a device."""
|
||||
self.device = device
|
||||
self.media_player = None
|
||||
self.media_player: SoundTouchMediaPlayer | None = None
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
@@ -65,20 +73,25 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Handle the applying of a service."""
|
||||
master_id = service.data.get("master")
|
||||
slaves_ids = service.data.get("slaves")
|
||||
all_media_players = [
|
||||
entry.runtime_data.media_player
|
||||
for entry in hass.config_entries.async_loaded_entries(DOMAIN)
|
||||
if entry.runtime_data.media_player is not None
|
||||
]
|
||||
slaves = []
|
||||
if slaves_ids:
|
||||
slaves = [
|
||||
data.media_player
|
||||
for data in hass.data[DOMAIN].values()
|
||||
if data.media_player.entity_id in slaves_ids
|
||||
media_player
|
||||
for media_player in all_media_players
|
||||
if media_player.entity_id in slaves_ids
|
||||
]
|
||||
|
||||
master = next(
|
||||
iter(
|
||||
[
|
||||
data.media_player
|
||||
for data in hass.data[DOMAIN].values()
|
||||
if data.media_player.entity_id == master_id
|
||||
media_player
|
||||
for media_player in all_media_players
|
||||
if media_player.entity_id == master_id
|
||||
]
|
||||
),
|
||||
None,
|
||||
@@ -90,9 +103,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
|
||||
if service.service == SERVICE_PLAY_EVERYWHERE:
|
||||
slaves = [
|
||||
data.media_player
|
||||
for data in hass.data[DOMAIN].values()
|
||||
if data.media_player.entity_id != master_id
|
||||
media_player
|
||||
for media_player in all_media_players
|
||||
if media_player.entity_id != master_id
|
||||
]
|
||||
await hass.async_add_executor_job(master.create_zone, slaves)
|
||||
elif service.service == SERVICE_CREATE_ZONE:
|
||||
@@ -130,7 +143,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SoundTouchConfigEntry) -> bool:
|
||||
"""Set up Bose SoundTouch from a config entry."""
|
||||
try:
|
||||
device = await hass.async_add_executor_job(
|
||||
@@ -141,14 +154,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
f"Unable to connect to SoundTouch device at {entry.data[CONF_HOST]}"
|
||||
) from err
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = SoundTouchData(device)
|
||||
entry.runtime_data = SoundTouchData(device)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SoundTouchConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
del hass.data[DOMAIN][entry.entry_id]
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -19,7 +19,6 @@ from homeassistant.components.media_player import (
|
||||
MediaType,
|
||||
async_process_play_media_url,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import (
|
||||
@@ -29,6 +28,7 @@ from homeassistant.helpers.device_registry import (
|
||||
)
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SoundTouchConfigEntry
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -46,16 +46,16 @@ ATTR_SOUNDTOUCH_ZONE = "soundtouch_zone"
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SoundTouchConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Bose SoundTouch media player based on a config entry."""
|
||||
device = hass.data[DOMAIN][entry.entry_id].device
|
||||
device = entry.runtime_data.device
|
||||
media_player = SoundTouchMediaPlayer(device)
|
||||
|
||||
async_add_entities([media_player], True)
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id].media_player = media_player
|
||||
entry.runtime_data.media_player = media_player
|
||||
|
||||
|
||||
class SoundTouchMediaPlayer(MediaPlayerEntity):
|
||||
@@ -388,14 +388,16 @@ class SoundTouchMediaPlayer(MediaPlayerEntity):
|
||||
|
||||
def _get_instance_by_ip(self, ip_address):
|
||||
"""Search and return a SoundTouchDevice instance by it's IP address."""
|
||||
for data in self.hass.data[DOMAIN].values():
|
||||
for entry in self.hass.config_entries.async_loaded_entries(DOMAIN):
|
||||
data = entry.runtime_data
|
||||
if data.device.config.device_ip == ip_address:
|
||||
return data.media_player
|
||||
return None
|
||||
|
||||
def _get_instance_by_id(self, instance_id):
|
||||
"""Search and return a SoundTouchDevice instance by it's ID (aka MAC address)."""
|
||||
for data in self.hass.data[DOMAIN].values():
|
||||
for entry in self.hass.config_entries.async_loaded_entries(DOMAIN):
|
||||
data = entry.runtime_data
|
||||
if data.device.config.device_id == instance_id:
|
||||
return data.media_player
|
||||
return None
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
|
||||
from srpenergy.client import SrpEnergyClient
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_ID, CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .coordinator import SRPEnergyDataUpdateCoordinator
|
||||
from .const import LOGGER
|
||||
from .coordinator import SRPEnergyConfigEntry, SRPEnergyDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SRPEnergyConfigEntry) -> bool:
|
||||
"""Set up the SRP Energy component from a config entry."""
|
||||
api_account_id: str = entry.data[CONF_ID]
|
||||
api_username: str = entry.data[CONF_USERNAME]
|
||||
@@ -30,17 +29,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SRPEnergyConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -23,14 +23,19 @@ from .const import (
|
||||
TIMEOUT = 10
|
||||
PHOENIX_ZONE_INFO = dt_util.get_time_zone(PHOENIX_TIME_ZONE)
|
||||
|
||||
type SRPEnergyConfigEntry = ConfigEntry[SRPEnergyDataUpdateCoordinator]
|
||||
|
||||
|
||||
class SRPEnergyDataUpdateCoordinator(DataUpdateCoordinator[float]):
|
||||
"""A srp_energy Data Update Coordinator."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: SRPEnergyConfigEntry
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, client: SrpEnergyClient
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: SRPEnergyConfigEntry,
|
||||
client: SrpEnergyClient,
|
||||
) -> None:
|
||||
"""Initialize the srp_energy data coordinator."""
|
||||
self._client = client
|
||||
|
||||
@@ -7,7 +7,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import UnitOfEnergy
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
@@ -15,19 +14,17 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import SRPEnergyDataUpdateCoordinator
|
||||
from .const import DEVICE_CONFIG_URL, DEVICE_MANUFACTURER, DEVICE_MODEL, DOMAIN
|
||||
from .coordinator import SRPEnergyConfigEntry, SRPEnergyDataUpdateCoordinator
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SRPEnergyConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SRP Energy Usage sensor."""
|
||||
coordinator: SRPEnergyDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities([SrpEntity(coordinator, entry)])
|
||||
async_add_entities([SrpEntity(entry.runtime_data, entry)])
|
||||
|
||||
|
||||
class SrpEntity(CoordinatorEntity[SRPEnergyDataUpdateCoordinator], SensorEntity):
|
||||
@@ -43,7 +40,7 @@ class SrpEntity(CoordinatorEntity[SRPEnergyDataUpdateCoordinator], SensorEntity)
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: SRPEnergyDataUpdateCoordinator,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SRPEnergyConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the SrpEntity class."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
from streamlabswater.streamlabswater import StreamlabsClient
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY, Platform
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import StreamlabsCoordinator
|
||||
from .coordinator import StreamlabsConfigEntry, StreamlabsCoordinator
|
||||
|
||||
ATTR_AWAY_MODE = "away_mode"
|
||||
SERVICE_SET_AWAY_MODE = "set_away_mode"
|
||||
@@ -30,7 +29,7 @@ SET_AWAY_MODE_SCHEMA = vol.Schema(
|
||||
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: StreamlabsConfigEntry) -> bool:
|
||||
"""Set up StreamLabs from a config entry."""
|
||||
|
||||
api_key = entry.data[CONF_API_KEY]
|
||||
@@ -39,7 +38,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
def set_away_mode(service: ServiceCall) -> None:
|
||||
@@ -55,9 +54,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: StreamlabsConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -3,22 +3,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import StreamlabsCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import StreamlabsConfigEntry, StreamlabsCoordinator
|
||||
from .entity import StreamlabsWaterEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: StreamlabsConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Streamlabs water binary sensor from a config entry."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
StreamlabsAwayMode(coordinator, location_id) for location_id in coordinator.data
|
||||
|
||||
@@ -23,15 +23,18 @@ class StreamlabsData:
|
||||
yearly_usage: float
|
||||
|
||||
|
||||
type StreamlabsConfigEntry = ConfigEntry[StreamlabsCoordinator]
|
||||
|
||||
|
||||
class StreamlabsCoordinator(DataUpdateCoordinator[dict[str, StreamlabsData]]):
|
||||
"""Coordinator for Streamlabs."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: StreamlabsConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: StreamlabsConfigEntry,
|
||||
client: StreamlabsClient,
|
||||
) -> None:
|
||||
"""Coordinator for Streamlabs."""
|
||||
|
||||
@@ -10,15 +10,12 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import UnitOfVolume
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
|
||||
from . import StreamlabsCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import StreamlabsData
|
||||
from .coordinator import StreamlabsConfigEntry, StreamlabsCoordinator, StreamlabsData
|
||||
from .entity import StreamlabsWaterEntity
|
||||
|
||||
|
||||
@@ -59,11 +56,11 @@ SENSORS: tuple[StreamlabsWaterSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: StreamlabsConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Streamlabs water sensor from a config entry."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
StreamLabsSensor(coordinator, location_id, entity_description)
|
||||
|
||||
@@ -9,7 +9,6 @@ from surepy.enums import Location
|
||||
from surepy.exceptions import SurePetcareAuthenticationError, SurePetcareError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
@@ -24,7 +23,7 @@ from .const import (
|
||||
SERVICE_SET_LOCK_STATE,
|
||||
SERVICE_SET_PET_LOCATION,
|
||||
)
|
||||
from .coordinator import SurePetcareDataCoordinator
|
||||
from .coordinator import SurePetcareConfigEntry, SurePetcareDataCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -32,15 +31,10 @@ PLATFORMS = [Platform.BINARY_SENSOR, Platform.LOCK, Platform.SENSOR]
|
||||
SCAN_INTERVAL = timedelta(minutes=3)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SurePetcareConfigEntry) -> bool:
|
||||
"""Set up Sure Petcare from a config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
try:
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator = SurePetcareDataCoordinator(
|
||||
hass,
|
||||
entry,
|
||||
)
|
||||
coordinator = SurePetcareDataCoordinator(hass, entry)
|
||||
except SurePetcareAuthenticationError as error:
|
||||
_LOGGER.error("Unable to connect to surepetcare.io: Wrong credentials!")
|
||||
raise ConfigEntryAuthFailed from error
|
||||
@@ -49,6 +43,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
entry.runtime_data = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
lock_state_service_schema = vol.Schema(
|
||||
@@ -91,10 +86,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: SurePetcareConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
@@ -12,26 +12,24 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SurePetcareDataCoordinator
|
||||
from .coordinator import SurePetcareConfigEntry, SurePetcareDataCoordinator
|
||||
from .entity import SurePetcareEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SurePetcareConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Sure PetCare Flaps binary sensors based on a config entry."""
|
||||
|
||||
entities: list[SurePetcareBinarySensor] = []
|
||||
|
||||
coordinator: SurePetcareDataCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
for surepy_entity in coordinator.data.values():
|
||||
# connectivity
|
||||
|
||||
@@ -29,13 +29,15 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL = timedelta(minutes=3)
|
||||
|
||||
type SurePetcareConfigEntry = ConfigEntry[SurePetcareDataCoordinator]
|
||||
|
||||
|
||||
class SurePetcareDataCoordinator(DataUpdateCoordinator[dict[int, SurepyEntity]]):
|
||||
"""Handle Surepetcare data."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: SurePetcareConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
def __init__(self, hass: HomeAssistant, entry: SurePetcareConfigEntry) -> None:
|
||||
"""Initialize the data handler."""
|
||||
self.surepy = Surepy(
|
||||
entry.data[CONF_USERNAME],
|
||||
|
||||
@@ -8,23 +8,21 @@ from surepy.entities import SurepyEntity
|
||||
from surepy.enums import EntityType, LockState as SurepyLockState
|
||||
|
||||
from homeassistant.components.lock import LockEntity, LockState
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SurePetcareDataCoordinator
|
||||
from .coordinator import SurePetcareConfigEntry, SurePetcareDataCoordinator
|
||||
from .entity import SurePetcareEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SurePetcareConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Sure PetCare locks on a config entry."""
|
||||
|
||||
coordinator: SurePetcareDataCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
SurePetcareLock(surepy_entity.id, coordinator, lock_state)
|
||||
|
||||
@@ -10,26 +10,25 @@ from surepy.entities.pet import Pet as SurepyPet
|
||||
from surepy.enums import EntityType
|
||||
|
||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_VOLTAGE, PERCENTAGE, EntityCategory, UnitOfVolume
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN, SURE_BATT_VOLTAGE_DIFF, SURE_BATT_VOLTAGE_LOW
|
||||
from .coordinator import SurePetcareDataCoordinator
|
||||
from .const import SURE_BATT_VOLTAGE_DIFF, SURE_BATT_VOLTAGE_LOW
|
||||
from .coordinator import SurePetcareConfigEntry, SurePetcareDataCoordinator
|
||||
from .entity import SurePetcareEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SurePetcareConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Sure PetCare Flaps sensors."""
|
||||
|
||||
entities: list[SurePetcareEntity] = []
|
||||
|
||||
coordinator: SurePetcareDataCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
for surepy_entity in coordinator.data.values():
|
||||
if surepy_entity.type in [
|
||||
|
||||
@@ -9,7 +9,6 @@ from aiohttp import ClientSession
|
||||
from switchbee.api import CentralUnitPolling, CentralUnitWsRPC, is_wsrpc_api
|
||||
from switchbee.api.central_unit import SwitchBeeError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
@@ -17,7 +16,7 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .coordinator import SwitchBeeConfigEntry, SwitchBeeCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -53,10 +52,9 @@ async def get_api_object(
|
||||
return api
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SwitchBeeConfigEntry) -> bool:
|
||||
"""Set up SwitchBee Smart Home from a config entry."""
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
central_unit = entry.data[CONF_HOST]
|
||||
user = entry.data[CONF_USERNAME]
|
||||
password = entry.data[CONF_PASSWORD]
|
||||
@@ -67,27 +65,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SwitchBeeConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
async def update_listener(
|
||||
hass: HomeAssistant, config_entry: SwitchBeeConfigEntry
|
||||
) -> None:
|
||||
"""Update listener."""
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_migrate_entry(
|
||||
hass: HomeAssistant, config_entry: SwitchBeeConfigEntry
|
||||
) -> bool:
|
||||
"""Migrate old entry."""
|
||||
_LOGGER.debug("Migrating from version %s", config_entry.version)
|
||||
|
||||
|
||||
@@ -4,23 +4,21 @@ from switchbee.api.central_unit import SwitchBeeError
|
||||
from switchbee.device import ApiStateCommand, DeviceType
|
||||
|
||||
from homeassistant.components.button import ButtonEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .coordinator import SwitchBeeConfigEntry
|
||||
from .entity import SwitchBeeEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SwitchBeeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Switchbee button."""
|
||||
coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
async_add_entities(
|
||||
SwitchBeeButton(switchbee_device, coordinator)
|
||||
for switchbee_device in coordinator.data.values()
|
||||
|
||||
@@ -23,14 +23,12 @@ from homeassistant.components.climate import (
|
||||
HVACAction,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .coordinator import SwitchBeeConfigEntry, SwitchBeeCoordinator
|
||||
from .entity import SwitchBeeDeviceEntity
|
||||
|
||||
FAN_SB_TO_HASS = {
|
||||
@@ -75,11 +73,11 @@ SUPPORTED_FAN_MODES = [FAN_AUTO, FAN_HIGH, FAN_MEDIUM, FAN_LOW]
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SwitchBeeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBee climate."""
|
||||
coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
async_add_entities(
|
||||
SwitchBeeClimateEntity(switchbee_device, coordinator)
|
||||
for switchbee_device in coordinator.data.values()
|
||||
|
||||
@@ -19,16 +19,18 @@ from .const import DOMAIN, SCAN_INTERVAL_SEC
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
type SwitchBeeConfigEntry = ConfigEntry[SwitchBeeCoordinator]
|
||||
|
||||
|
||||
class SwitchBeeCoordinator(DataUpdateCoordinator[Mapping[int, SwitchBeeBaseDevice]]):
|
||||
"""Class to manage fetching SwitchBee data API."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: SwitchBeeConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: SwitchBeeConfigEntry,
|
||||
swb_api: CentralUnitPolling | CentralUnitWsRPC,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
|
||||
@@ -14,23 +14,21 @@ from homeassistant.components.cover import (
|
||||
CoverEntity,
|
||||
CoverEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .coordinator import SwitchBeeConfigEntry
|
||||
from .entity import SwitchBeeDeviceEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SwitchBeeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBee switch."""
|
||||
coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
"""Set up SwitchBee covers."""
|
||||
coordinator = entry.runtime_data
|
||||
entities: list[CoverEntity] = []
|
||||
|
||||
for device in coordinator.data.values():
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
|
||||
from switchbee.api.central_unit import SwitchBeeDeviceOfflineError, SwitchBeeError
|
||||
from switchbee.device import ApiStateCommand, DeviceType, SwitchBeeDimmer
|
||||
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .coordinator import SwitchBeeConfigEntry, SwitchBeeCoordinator
|
||||
from .entity import SwitchBeeDeviceEntity
|
||||
|
||||
MAX_BRIGHTNESS = 255
|
||||
@@ -36,13 +34,13 @@ def _switchbee_brightness_to_hass(value: int) -> int:
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SwitchBeeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBee light."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
async_add_entities(
|
||||
SwitchBeeLightEntity(switchbee_device, coordinator)
|
||||
SwitchBeeLightEntity(cast(SwitchBeeDimmer, switchbee_device), coordinator)
|
||||
for switchbee_device in coordinator.data.values()
|
||||
if switchbee_device.type == DeviceType.Dimmer
|
||||
)
|
||||
|
||||
@@ -14,23 +14,21 @@ from switchbee.device import (
|
||||
)
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .coordinator import SwitchBeeConfigEntry, SwitchBeeCoordinator
|
||||
from .entity import SwitchBeeDeviceEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SwitchBeeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Switchbee switch."""
|
||||
coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
SwitchBeeSwitchEntity(device, coordinator)
|
||||
|
||||
@@ -114,21 +114,25 @@ PLATFORMS_BY_TYPE = {
|
||||
Platform.FAN,
|
||||
Platform.SENSOR,
|
||||
Platform.BUTTON,
|
||||
Platform.SWITCH,
|
||||
],
|
||||
SupportedModels.AIR_PURIFIER_US.value: [
|
||||
Platform.FAN,
|
||||
Platform.SENSOR,
|
||||
Platform.BUTTON,
|
||||
Platform.SWITCH,
|
||||
],
|
||||
SupportedModels.AIR_PURIFIER_TABLE_JP.value: [
|
||||
Platform.FAN,
|
||||
Platform.SENSOR,
|
||||
Platform.BUTTON,
|
||||
Platform.SWITCH,
|
||||
],
|
||||
SupportedModels.AIR_PURIFIER_TABLE_US.value: [
|
||||
Platform.FAN,
|
||||
Platform.SENSOR,
|
||||
Platform.BUTTON,
|
||||
Platform.SWITCH,
|
||||
],
|
||||
SupportedModels.EVAPORATIVE_HUMIDIFIER.value: [
|
||||
Platform.HUMIDIFIER,
|
||||
|
||||
@@ -145,6 +145,20 @@
|
||||
"medium": "mdi:water"
|
||||
}
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"child_lock": {
|
||||
"state": {
|
||||
"off": "mdi:lock-open",
|
||||
"on": "mdi:lock"
|
||||
}
|
||||
},
|
||||
"wireless_charging": {
|
||||
"state": {
|
||||
"off": "mdi:battery-charging-wireless-outline",
|
||||
"on": "mdi:battery-charging-wireless"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
|
||||
@@ -326,6 +326,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"child_lock": {
|
||||
"name": "Child lock"
|
||||
},
|
||||
"wireless_charging": {
|
||||
"name": "Wireless charging"
|
||||
}
|
||||
},
|
||||
"vacuum": {
|
||||
|
||||
@@ -2,22 +2,61 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import switchbot
|
||||
|
||||
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
|
||||
from homeassistant.components.switch import (
|
||||
SwitchDeviceClass,
|
||||
SwitchEntity,
|
||||
SwitchEntityDescription,
|
||||
)
|
||||
from homeassistant.const import STATE_ON
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import AIRPURIFIER_BASIC_MODELS, AIRPURIFIER_TABLE_MODELS, DOMAIN
|
||||
from .coordinator import SwitchbotConfigEntry, SwitchbotDataUpdateCoordinator
|
||||
from .entity import SwitchbotSwitchedEntity, exception_handler
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class SwitchbotSwitchEntityDescription(SwitchEntityDescription):
|
||||
"""Describes a Switchbot switch entity."""
|
||||
|
||||
is_on_fn: Callable[[switchbot.SwitchbotDevice], bool | None]
|
||||
turn_on_fn: Callable[[switchbot.SwitchbotDevice], Awaitable[Any]]
|
||||
turn_off_fn: Callable[[switchbot.SwitchbotDevice], Awaitable[Any]]
|
||||
|
||||
|
||||
AIRPURIFIER_BASIC_SWITCHES: tuple[SwitchbotSwitchEntityDescription, ...] = (
|
||||
SwitchbotSwitchEntityDescription(
|
||||
key="child_lock",
|
||||
translation_key="child_lock",
|
||||
device_class=SwitchDeviceClass.SWITCH,
|
||||
is_on_fn=lambda device: device.is_child_lock_on(),
|
||||
turn_on_fn=lambda device: device.open_child_lock(),
|
||||
turn_off_fn=lambda device: device.close_child_lock(),
|
||||
),
|
||||
)
|
||||
|
||||
AIRPURIFIER_TABLE_SWITCHES: tuple[SwitchbotSwitchEntityDescription, ...] = (
|
||||
*AIRPURIFIER_BASIC_SWITCHES,
|
||||
SwitchbotSwitchEntityDescription(
|
||||
key="wireless_charging",
|
||||
translation_key="wireless_charging",
|
||||
device_class=SwitchDeviceClass.SWITCH,
|
||||
is_on_fn=lambda device: device.is_wireless_charging_on(),
|
||||
turn_on_fn=lambda device: device.open_wireless_charging(),
|
||||
turn_off_fn=lambda device: device.close_wireless_charging(),
|
||||
),
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -36,10 +75,64 @@ async def async_setup_entry(
|
||||
for channel in range(1, coordinator.device.channel + 1)
|
||||
]
|
||||
async_add_entities(entries)
|
||||
elif coordinator.model in AIRPURIFIER_BASIC_MODELS:
|
||||
async_add_entities(
|
||||
[
|
||||
SwitchbotGenericSwitch(coordinator, desc)
|
||||
for desc in AIRPURIFIER_BASIC_SWITCHES
|
||||
]
|
||||
)
|
||||
elif coordinator.model in AIRPURIFIER_TABLE_MODELS:
|
||||
async_add_entities(
|
||||
[
|
||||
SwitchbotGenericSwitch(coordinator, desc)
|
||||
for desc in AIRPURIFIER_TABLE_SWITCHES
|
||||
]
|
||||
)
|
||||
else:
|
||||
async_add_entities([SwitchBotSwitch(coordinator)])
|
||||
|
||||
|
||||
class SwitchbotGenericSwitch(SwitchbotSwitchedEntity, SwitchEntity):
|
||||
"""Representation of a Switchbot switch controlled via entity description."""
|
||||
|
||||
entity_description: SwitchbotSwitchEntityDescription
|
||||
_device: switchbot.SwitchbotDevice
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: SwitchbotDataUpdateCoordinator,
|
||||
description: SwitchbotSwitchEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the Switchbot generic switch."""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{coordinator.base_unique_id}-{description.key}"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool | None:
|
||||
"""Return true if device is on."""
|
||||
return self.entity_description.is_on_fn(self._device)
|
||||
|
||||
@exception_handler
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on."""
|
||||
_LOGGER.debug(
|
||||
"Turning on %s for %s", self.entity_description.key, self._address
|
||||
)
|
||||
await self.entity_description.turn_on_fn(self._device)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@exception_handler
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off."""
|
||||
_LOGGER.debug(
|
||||
"Turning off %s for %s", self.entity_description.key, self._address
|
||||
)
|
||||
await self.entity_description.turn_off_fn(self._device)
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
class SwitchBotSwitch(SwitchbotSwitchedEntity, SwitchEntity, RestoreEntity):
|
||||
"""Representation of a Switchbot switch."""
|
||||
|
||||
|
||||
@@ -75,9 +75,12 @@ class SwitchbotCloudData:
|
||||
devices: SwitchbotDevices
|
||||
|
||||
|
||||
type SwitchbotCloudConfigEntry = ConfigEntry[SwitchbotCloudData]
|
||||
|
||||
|
||||
async def coordinator_for_device(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SwitchbotCloudConfigEntry,
|
||||
api: SwitchBotAPI,
|
||||
device: Device | Remote,
|
||||
coordinators_by_id: dict[str, SwitchBotCoordinator],
|
||||
@@ -97,7 +100,7 @@ async def coordinator_for_device(
|
||||
|
||||
async def make_switchbot_devices(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SwitchbotCloudConfigEntry,
|
||||
api: SwitchBotAPI,
|
||||
devices: list[Device | Remote],
|
||||
coordinators_by_id: dict[str, SwitchBotCoordinator],
|
||||
@@ -115,7 +118,7 @@ async def make_switchbot_devices(
|
||||
|
||||
async def make_device_data(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SwitchbotCloudConfigEntry,
|
||||
api: SwitchBotAPI,
|
||||
device: Device | Remote,
|
||||
devices_data: SwitchbotDevices,
|
||||
@@ -330,7 +333,9 @@ async def make_device_data(
|
||||
devices_data.sensors.append((device, coordinator))
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: SwitchbotCloudConfigEntry
|
||||
) -> bool:
|
||||
"""Set up SwitchBot via API from a config entry."""
|
||||
token = entry.data[CONF_API_TOKEN]
|
||||
secret = entry.data[CONF_API_KEY]
|
||||
@@ -353,10 +358,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
switchbot_devices = await make_switchbot_devices(
|
||||
hass, entry, api, devices, coordinators_by_id
|
||||
)
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = SwitchbotCloudData(
|
||||
api=api, devices=switchbot_devices
|
||||
)
|
||||
entry.runtime_data = SwitchbotCloudData(api=api, devices=switchbot_devices)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -365,17 +367,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: SwitchbotCloudConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
async def _initialize_webhook(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SwitchbotCloudConfigEntry,
|
||||
api: SwitchBotAPI,
|
||||
coordinators_by_id: dict[str, SwitchBotCoordinator],
|
||||
) -> None:
|
||||
|
||||
@@ -11,13 +11,11 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData
|
||||
from .const import DOMAIN
|
||||
from . import SwitchbotCloudConfigEntry
|
||||
from .coordinator import SwitchBotCoordinator
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
@@ -137,11 +135,11 @@ BINARY_SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
SwitchBotCloudBinarySensor(data.api, device, coordinator, description)
|
||||
|
||||
@@ -12,12 +12,10 @@ from switchbot_api import (
|
||||
from switchbot_api.commands import ArtFrameCommands, BotCommands, CommonCommands
|
||||
|
||||
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import DOMAIN
|
||||
from . import SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
|
||||
@@ -58,11 +56,11 @@ BUTTON_DESCRIPTIONS_BY_DEVICE_TYPES = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
entities: list[SwitchBotCloudBot] = []
|
||||
for device, coordinator in data.devices.buttons:
|
||||
description_set = BUTTON_DESCRIPTIONS_BY_DEVICE_TYPES[device.device_type]
|
||||
|
||||
@@ -26,7 +26,6 @@ from homeassistant.components.climate import (
|
||||
ClimateEntityFeature,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
PRECISION_TENTHS,
|
||||
STATE_UNAVAILABLE,
|
||||
@@ -37,10 +36,9 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from . import SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from .const import (
|
||||
CLIMATE_PRESET_SCHEDULE,
|
||||
DOMAIN,
|
||||
SMART_RADIATOR_THERMOSTAT_AFTER_COMMAND_REFRESH,
|
||||
)
|
||||
from .entity import SwitchBotCloudEntity
|
||||
@@ -69,11 +67,11 @@ _DEFAULT_SWITCHBOT_FAN_MODE = _SWITCHBOT_FAN_MODES[FanState.FAN_AUTO]
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
async_add_entities(
|
||||
_async_make_entity(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.climates
|
||||
|
||||
@@ -18,22 +18,21 @@ from homeassistant.components.cover import (
|
||||
CoverEntity,
|
||||
CoverEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import COVER_ENTITY_AFTER_COMMAND_REFRESH, DOMAIN
|
||||
from . import SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from .const import COVER_ENTITY_AFTER_COMMAND_REFRESH
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
async_add_entities(
|
||||
_async_make_entity(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.covers
|
||||
|
||||
@@ -13,13 +13,12 @@ from switchbot_api import (
|
||||
)
|
||||
|
||||
from homeassistant.components.fan import FanEntity, FanEntityFeature
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import STATE_ON
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData
|
||||
from .const import AFTER_COMMAND_REFRESH, DOMAIN, AirPurifierMode
|
||||
from . import SwitchbotCloudConfigEntry
|
||||
from .const import AFTER_COMMAND_REFRESH, AirPurifierMode
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -28,11 +27,11 @@ PARALLEL_UPDATES = 0
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
for device, coordinator in data.devices.fans:
|
||||
if device.device_type.startswith("Air Purifier"):
|
||||
async_add_entities(
|
||||
|
||||
@@ -12,13 +12,12 @@ from homeassistant.components.humidifier import (
|
||||
HumidifierEntity,
|
||||
HumidifierEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import STATE_ON
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData
|
||||
from .const import AFTER_COMMAND_REFRESH, DOMAIN, HUMIDITY_LEVELS, Humidifier2Mode
|
||||
from . import SwitchbotCloudConfigEntry
|
||||
from .const import AFTER_COMMAND_REFRESH, HUMIDITY_LEVELS, Humidifier2Mode
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
@@ -26,11 +25,11 @@ PARALLEL_UPDATES = 0
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Switchbot based on a config entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][entry.entry_id]
|
||||
data = entry.runtime_data
|
||||
async_add_entities(
|
||||
SwitchBotHumidifier(data.api, device, coordinator)
|
||||
if device.device_type == "Humidifier"
|
||||
|
||||
@@ -6,22 +6,20 @@ from switchbot_api import Device, Remote, SwitchBotAPI
|
||||
from switchbot_api.utils import get_file_stream_from_cloud
|
||||
|
||||
from homeassistant.components.image import ImageEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import DOMAIN
|
||||
from . import SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
async_add_entities(
|
||||
_async_make_entity(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.images
|
||||
|
||||
@@ -14,12 +14,11 @@ from switchbot_api import (
|
||||
)
|
||||
|
||||
from homeassistant.components.light import ColorMode, LightEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import AFTER_COMMAND_REFRESH, DOMAIN
|
||||
from . import SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from .const import AFTER_COMMAND_REFRESH
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
|
||||
@@ -35,11 +34,11 @@ def brightness_map_value(value: int) -> int:
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
async_add_entities(
|
||||
_async_make_entity(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.lights
|
||||
|
||||
@@ -5,22 +5,20 @@ from typing import Any
|
||||
from switchbot_api import Device, LockCommands, LockV2Commands, Remote, SwitchBotAPI
|
||||
|
||||
from homeassistant.components.lock import LockEntity, LockEntityFeature
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import DOMAIN
|
||||
from . import SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
async_add_entities(
|
||||
SwitchBotCloudLock(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.locks
|
||||
|
||||
@@ -12,7 +12,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
PERCENTAGE,
|
||||
@@ -26,7 +25,7 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData
|
||||
from . import SwitchbotCloudConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBotCoordinator
|
||||
from .entity import SwitchBotCloudEntity
|
||||
@@ -267,11 +266,11 @@ SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
entities: list[SwitchBotCloudSensor] = []
|
||||
for device, coordinator in data.devices.sensors:
|
||||
for description in SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES[device.device_type]:
|
||||
|
||||
@@ -6,12 +6,11 @@ from typing import Any
|
||||
from switchbot_api import CommonCommands, Device, PowerState, Remote, SwitchBotAPI
|
||||
|
||||
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData
|
||||
from . import SwitchbotCloudConfigEntry
|
||||
from .const import AFTER_COMMAND_REFRESH, DOMAIN
|
||||
from .coordinator import SwitchBotCoordinator
|
||||
from .entity import SwitchBotCloudEntity
|
||||
@@ -19,11 +18,11 @@ from .entity import SwitchBotCloudEntity
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
entities: list[SwitchBotCloudSwitch] = []
|
||||
for device, coordinator in data.devices.switches:
|
||||
if device.device_type == "Relay Switch 2PM":
|
||||
|
||||
@@ -17,13 +17,11 @@ from homeassistant.components.vacuum import (
|
||||
VacuumActivity,
|
||||
VacuumEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudData
|
||||
from . import SwitchbotCloudConfigEntry
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
VACUUM_FAN_SPEED_MAX,
|
||||
VACUUM_FAN_SPEED_QUIET,
|
||||
VACUUM_FAN_SPEED_STANDARD,
|
||||
@@ -35,11 +33,11 @@ from .entity import SwitchBotCloudEntity
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigEntry,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
data = config.runtime_data
|
||||
async_add_entities(
|
||||
_async_make_entity(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.vacuums
|
||||
|
||||
@@ -21,7 +21,7 @@ from systembridgeconnector.models.open_url import OpenUrl
|
||||
from systembridgeconnector.version import Version
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_COMMAND,
|
||||
@@ -57,7 +57,24 @@ from homeassistant.helpers.issue_registry import IssueSeverity, async_create_iss
|
||||
|
||||
from .config_flow import SystemBridgeConfigFlow
|
||||
from .const import DATA_WAIT_TIMEOUT, DOMAIN, MODULES
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
|
||||
|
||||
def _get_coordinator(
|
||||
hass: HomeAssistant, entry_id: str
|
||||
) -> SystemBridgeDataUpdateCoordinator:
|
||||
"""Return the coordinator for a config entry id."""
|
||||
entry: SystemBridgeConfigEntry | None = hass.config_entries.async_get_entry(
|
||||
entry_id
|
||||
)
|
||||
if entry is None or entry.state is not ConfigEntryState.LOADED:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="device_not_found",
|
||||
translation_placeholders={"device": entry_id},
|
||||
)
|
||||
return entry.runtime_data
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -93,7 +110,7 @@ POWER_COMMAND_MAP = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
) -> bool:
|
||||
"""Set up System Bridge from a config entry."""
|
||||
|
||||
@@ -198,8 +215,7 @@ async def async_setup_entry(
|
||||
# Fetch initial data so we have data when entities subscribe
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
entry.runtime_data = coordinator
|
||||
|
||||
# Set up all platforms except notify
|
||||
await hass.config_entries.async_forward_entry_setups(
|
||||
@@ -216,7 +232,7 @@ async def async_setup_entry(
|
||||
CONF_NAME: f"{DOMAIN}_{coordinator.data.system.hostname}",
|
||||
CONF_ENTITY_ID: entry.entry_id,
|
||||
},
|
||||
hass.data[DOMAIN][entry.entry_id],
|
||||
{},
|
||||
)
|
||||
)
|
||||
|
||||
@@ -249,9 +265,7 @@ async def async_setup_entry(
|
||||
async def handle_get_process_by_id(service_call: ServiceCall) -> ServiceResponse:
|
||||
"""Handle the get process by id service call."""
|
||||
_LOGGER.debug("Get process by id: %s", service_call.data)
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
coordinator = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
processes: list[Process] = coordinator.data.processes
|
||||
|
||||
# Find process.id from list, raise ServiceValidationError if not found
|
||||
@@ -275,9 +289,7 @@ async def async_setup_entry(
|
||||
) -> ServiceResponse:
|
||||
"""Handle the get process by name service call."""
|
||||
_LOGGER.debug("Get process by name: %s", service_call.data)
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
coordinator = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
|
||||
# Find processes from list
|
||||
items: list[dict[str, Any]] = [
|
||||
@@ -295,9 +307,7 @@ async def async_setup_entry(
|
||||
async def handle_open_path(service_call: ServiceCall) -> ServiceResponse:
|
||||
"""Handle the open path service call."""
|
||||
_LOGGER.debug("Open path: %s", service_call.data)
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
coordinator = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
response = await coordinator.websocket_client.open_path(
|
||||
OpenPath(path=service_call.data[CONF_PATH])
|
||||
)
|
||||
@@ -306,9 +316,7 @@ async def async_setup_entry(
|
||||
async def handle_power_command(service_call: ServiceCall) -> ServiceResponse:
|
||||
"""Handle the power command service call."""
|
||||
_LOGGER.debug("Power command: %s", service_call.data)
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
coordinator = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
response = await getattr(
|
||||
coordinator.websocket_client,
|
||||
POWER_COMMAND_MAP[service_call.data[CONF_COMMAND]],
|
||||
@@ -318,9 +326,7 @@ async def async_setup_entry(
|
||||
async def handle_open_url(service_call: ServiceCall) -> ServiceResponse:
|
||||
"""Handle the open url service call."""
|
||||
_LOGGER.debug("Open URL: %s", service_call.data)
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
coordinator = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
response = await coordinator.websocket_client.open_url(
|
||||
OpenUrl(url=service_call.data[CONF_URL])
|
||||
)
|
||||
@@ -328,9 +334,7 @@ async def async_setup_entry(
|
||||
|
||||
async def handle_send_keypress(service_call: ServiceCall) -> ServiceResponse:
|
||||
"""Handle the send_keypress service call."""
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
coordinator = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
response = await coordinator.websocket_client.keyboard_keypress(
|
||||
KeyboardKey(key=service_call.data[CONF_KEY])
|
||||
)
|
||||
@@ -338,9 +342,7 @@ async def async_setup_entry(
|
||||
|
||||
async def handle_send_text(service_call: ServiceCall) -> ServiceResponse:
|
||||
"""Handle the send_keypress service call."""
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
coordinator = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
response = await coordinator.websocket_client.keyboard_text(
|
||||
KeyboardText(text=service_call.data[CONF_TEXT])
|
||||
)
|
||||
@@ -446,33 +448,27 @@ async def async_setup_entry(
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: SystemBridgeConfigEntry
|
||||
) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY]
|
||||
)
|
||||
if unload_ok:
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
entry.entry_id
|
||||
]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
# Ensure disconnected and cleanup stop sub
|
||||
await coordinator.websocket_client.close()
|
||||
if coordinator.unsub:
|
||||
coordinator.unsub()
|
||||
|
||||
del hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
if not hass.data[DOMAIN]:
|
||||
hass.services.async_remove(DOMAIN, SERVICE_OPEN_PATH)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_OPEN_URL)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_SEND_KEYPRESS)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_SEND_TEXT)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
async def async_reload_entry(
|
||||
hass: HomeAssistant, entry: SystemBridgeConfigEntry
|
||||
) -> None:
|
||||
"""Reload the config entry when it changed."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
@@ -10,13 +10,11 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PORT
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
from .data import SystemBridgeData
|
||||
from .entity import SystemBridgeEntity
|
||||
|
||||
@@ -64,11 +62,11 @@ BATTERY_BINARY_SENSOR_TYPES: tuple[SystemBridgeBinarySensorEntityDescription, ..
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up System Bridge binary sensor based on a config entry."""
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
entities = [
|
||||
SystemBridgeBinarySensor(coordinator, description, entry.data[CONF_PORT])
|
||||
|
||||
@@ -36,18 +36,20 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
from .const import DOMAIN, GET_DATA_WAIT_TIMEOUT, MODULES
|
||||
from .data import SystemBridgeData
|
||||
|
||||
type SystemBridgeConfigEntry = ConfigEntry[SystemBridgeDataUpdateCoordinator]
|
||||
|
||||
|
||||
class SystemBridgeDataUpdateCoordinator(DataUpdateCoordinator[SystemBridgeData]):
|
||||
"""Class to manage fetching System Bridge data from single endpoint."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
config_entry: SystemBridgeConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
LOGGER: logging.Logger,
|
||||
*,
|
||||
entry: ConfigEntry,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize global System Bridge data updater."""
|
||||
self.title = entry.title
|
||||
|
||||
@@ -15,13 +15,11 @@ from homeassistant.components.media_player import (
|
||||
MediaPlayerState,
|
||||
RepeatMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PORT
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
from .data import SystemBridgeData
|
||||
from .entity import SystemBridgeEntity
|
||||
|
||||
@@ -64,11 +62,11 @@ MEDIA_PLAYER_DESCRIPTION: Final[MediaPlayerEntityDescription] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up System Bridge media players based on a config entry."""
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
data = coordinator.data
|
||||
|
||||
if data.media is not None:
|
||||
|
||||
@@ -15,12 +15,22 @@ from homeassistant.components.media_source import (
|
||||
MediaSourceItem,
|
||||
PlayMedia,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .coordinator import SystemBridgeConfigEntry
|
||||
|
||||
|
||||
def _get_loaded_entry(hass: HomeAssistant, entry_id: str) -> SystemBridgeConfigEntry:
|
||||
"""Return a loaded System Bridge config entry by id."""
|
||||
entry: SystemBridgeConfigEntry | None = hass.config_entries.async_get_entry(
|
||||
entry_id
|
||||
)
|
||||
if entry is None or entry.state is not ConfigEntryState.LOADED:
|
||||
raise ValueError("Invalid entry")
|
||||
return entry
|
||||
|
||||
|
||||
async def async_get_media_source(hass: HomeAssistant) -> MediaSource:
|
||||
@@ -46,9 +56,7 @@ class SystemBridgeSource(MediaSource):
|
||||
) -> PlayMedia:
|
||||
"""Resolve media to a url."""
|
||||
entry_id, path, mime_type = item.identifier.split("~~", 2)
|
||||
entry = self.hass.config_entries.async_get_entry(entry_id)
|
||||
if entry is None:
|
||||
raise ValueError("Invalid entry")
|
||||
entry = _get_loaded_entry(self.hass, entry_id)
|
||||
path_split = path.split("/", 1)
|
||||
return PlayMedia(
|
||||
f"{_build_base_url(entry)}&base={path_split[0]}&path={path_split[1]}",
|
||||
@@ -64,21 +72,14 @@ class SystemBridgeSource(MediaSource):
|
||||
return self._build_bridges()
|
||||
|
||||
if "~~" not in item.identifier:
|
||||
entry = self.hass.config_entries.async_get_entry(item.identifier)
|
||||
if entry is None:
|
||||
raise ValueError("Invalid entry")
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = self.hass.data[DOMAIN].get(
|
||||
entry.entry_id
|
||||
)
|
||||
entry = _get_loaded_entry(self.hass, item.identifier)
|
||||
coordinator = entry.runtime_data
|
||||
directories = await coordinator.websocket_client.get_directories()
|
||||
return _build_root_paths(entry, directories)
|
||||
|
||||
entry_id, path = item.identifier.split("~~", 1)
|
||||
entry = self.hass.config_entries.async_get_entry(entry_id)
|
||||
if entry is None:
|
||||
raise ValueError("Invalid entry")
|
||||
|
||||
coordinator = self.hass.data[DOMAIN].get(entry.entry_id)
|
||||
entry = _get_loaded_entry(self.hass, entry_id)
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
path_split = path.split("/", 1)
|
||||
|
||||
@@ -123,7 +124,7 @@ class SystemBridgeSource(MediaSource):
|
||||
|
||||
|
||||
def _build_base_url(
|
||||
entry: ConfigEntry,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
) -> str:
|
||||
"""Build base url for System Bridge media."""
|
||||
return (
|
||||
@@ -133,7 +134,7 @@ def _build_base_url(
|
||||
|
||||
|
||||
def _build_root_paths(
|
||||
entry: ConfigEntry,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
media_directories: list[MediaDirectory],
|
||||
) -> BrowseMediaSource:
|
||||
"""Build base categories for System Bridge media."""
|
||||
@@ -164,7 +165,7 @@ def _build_root_paths(
|
||||
|
||||
|
||||
def _build_media_items(
|
||||
entry: ConfigEntry,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
media_files: MediaFiles,
|
||||
path: str,
|
||||
identifier: str,
|
||||
|
||||
@@ -17,8 +17,7 @@ from homeassistant.const import ATTR_ICON, CONF_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -37,11 +36,13 @@ async def async_get_service(
|
||||
if discovery_info is None:
|
||||
return None
|
||||
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
entry: SystemBridgeConfigEntry | None = hass.config_entries.async_get_entry(
|
||||
discovery_info[CONF_ENTITY_ID]
|
||||
]
|
||||
)
|
||||
if entry is None:
|
||||
return None
|
||||
|
||||
return SystemBridgeNotificationService(coordinator)
|
||||
return SystemBridgeNotificationService(entry.runtime_data)
|
||||
|
||||
|
||||
class SystemBridgeNotificationService(BaseNotificationService):
|
||||
|
||||
@@ -17,7 +17,6 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_PORT,
|
||||
PERCENTAGE,
|
||||
@@ -33,8 +32,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import UNDEFINED, StateType
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
from .data import SystemBridgeData
|
||||
from .entity import SystemBridgeEntity
|
||||
|
||||
@@ -364,11 +362,11 @@ BATTERY_SENSOR_TYPES: tuple[SystemBridgeSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up System Bridge sensor based on a config entry."""
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
entities = [
|
||||
SystemBridgeSensor(coordinator, description, entry.data[CONF_PORT])
|
||||
|
||||
@@ -3,23 +3,21 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.update import UpdateEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PORT
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
from .entity import SystemBridgeEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up System Bridge update based on a config entry."""
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
|
||||
@@ -12,7 +12,9 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
from .const import ATTR_BOOT_TIME, ATTR_LOAD, DOMAIN, ROUTER_DEFAULT_HOST
|
||||
from .const import ATTR_BOOT_TIME, ATTR_LOAD, ROUTER_DEFAULT_HOST
|
||||
|
||||
type VilfoConfigEntry = ConfigEntry[VilfoRouterData]
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
@@ -21,7 +23,7 @@ DEFAULT_SCAN_INTERVAL = timedelta(seconds=30)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: VilfoConfigEntry) -> bool:
|
||||
"""Set up Vilfo Router from a config entry."""
|
||||
host = entry.data[CONF_HOST]
|
||||
access_token = entry.data[CONF_ACCESS_TOKEN]
|
||||
@@ -33,21 +35,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
if not vilfo_router.available:
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = vilfo_router
|
||||
entry.runtime_data = vilfo_router
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: VilfoConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
class VilfoRouterData:
|
||||
|
||||
@@ -7,12 +7,12 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import VilfoConfigEntry
|
||||
from .const import (
|
||||
ATTR_API_DATA_FIELD_BOOT_TIME,
|
||||
ATTR_API_DATA_FIELD_LOAD,
|
||||
@@ -50,11 +50,11 @@ SENSOR_TYPES: tuple[VilfoSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
config_entry: VilfoConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Add Vilfo Router entities from a config_entry."""
|
||||
vilfo = hass.data[DOMAIN][config_entry.entry_id]
|
||||
vilfo = config_entry.runtime_data
|
||||
|
||||
entities = [VilfoRouterSensor(vilfo, description) for description in SENSOR_TYPES]
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import uuid
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.lovelace import dashboard, resources
|
||||
from homeassistant.components.lovelace.const import LOVELACE_DATA
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
@@ -278,6 +279,41 @@ async def test_storage_resources_import_invalid(
|
||||
)
|
||||
|
||||
|
||||
async def test_storage_resources_create_preserves_existing(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test async_create_item lazy-loads before writing.
|
||||
|
||||
Custom integrations may call async_create_item() during startup before the
|
||||
frontend triggers a resource listing. Without a lazy-load guard, the
|
||||
collection is empty and async_create_item() overwrites all existing
|
||||
resources on disk.
|
||||
"""
|
||||
resource_config = [{**item, "id": uuid.uuid4().hex} for item in RESOURCE_EXAMPLES]
|
||||
hass_storage[resources.RESOURCE_STORAGE_KEY] = {
|
||||
"key": resources.RESOURCE_STORAGE_KEY,
|
||||
"version": 1,
|
||||
"data": {"items": resource_config},
|
||||
}
|
||||
assert await async_setup_component(hass, "lovelace", {})
|
||||
|
||||
resource_collection = hass.data[LOVELACE_DATA].resources
|
||||
|
||||
# Directly call async_create_item before any websocket listing
|
||||
await resource_collection.async_create_item(
|
||||
{"res_type": "module", "url": "/local/new.js"}
|
||||
)
|
||||
|
||||
# Existing resources must still be present
|
||||
items = resource_collection.async_items()
|
||||
assert len(items) == len(resource_config) + 1
|
||||
urls = [item["url"] for item in items]
|
||||
for original in resource_config:
|
||||
assert original["url"] in urls
|
||||
assert "/local/new.js" in urls
|
||||
|
||||
|
||||
@pytest.mark.parametrize("list_cmd", ["lovelace/resources", "lovelace/resources/list"])
|
||||
async def test_storage_resources_safe_mode(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -587,6 +587,8 @@ async def test_get_lock_users_service(
|
||||
"user_type": "unrestricted_user",
|
||||
"credential_rule": "single",
|
||||
"credentials": [],
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_user_index": None,
|
||||
}
|
||||
],
|
||||
@@ -745,6 +747,8 @@ async def test_get_lock_users_iterates_with_next_index(
|
||||
"user_type": "unrestricted_user",
|
||||
"credential_rule": "single",
|
||||
"credentials": [],
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_user_index": 5,
|
||||
},
|
||||
{
|
||||
@@ -755,6 +759,8 @@ async def test_get_lock_users_iterates_with_next_index(
|
||||
"user_type": "unrestricted_user",
|
||||
"credential_rule": "single",
|
||||
"credentials": [],
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_user_index": None,
|
||||
},
|
||||
],
|
||||
@@ -889,6 +895,8 @@ async def test_get_lock_users_with_credentials(
|
||||
{"type": "pin", "index": 1},
|
||||
{"type": "pin", "index": 2},
|
||||
],
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_user_index": None,
|
||||
}
|
||||
],
|
||||
@@ -942,6 +950,59 @@ async def test_get_lock_users_with_nullvalue_credentials(
|
||||
assert user["credentials"] == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["mock_door_lock"])
|
||||
@pytest.mark.parametrize("attributes", [{"1/257/65532": _FEATURE_USR_PIN}])
|
||||
async def test_get_lock_users_with_fabric_indices(
|
||||
hass: HomeAssistant,
|
||||
matter_client: MagicMock,
|
||||
matter_node: MatterNode,
|
||||
) -> None:
|
||||
"""Test get_lock_users returns fabric indices and normalizes NullValue."""
|
||||
matter_client.send_device_command = AsyncMock(
|
||||
side_effect=[
|
||||
{
|
||||
"userIndex": 1,
|
||||
"userName": "HA User",
|
||||
"userUniqueID": None,
|
||||
"userStatus": 1,
|
||||
"userType": 0,
|
||||
"credentialRule": 0,
|
||||
"credentials": None,
|
||||
"creatorFabricIndex": 3,
|
||||
"lastModifiedFabricIndex": NullValue,
|
||||
"nextUserIndex": 2,
|
||||
},
|
||||
{
|
||||
"userIndex": 2,
|
||||
"userName": "External User",
|
||||
"userUniqueID": None,
|
||||
"userStatus": 1,
|
||||
"userType": 0,
|
||||
"credentialRule": 0,
|
||||
"credentials": None,
|
||||
"creatorFabricIndex": NullValue,
|
||||
"lastModifiedFabricIndex": 5,
|
||||
"nextUserIndex": None,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
result = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"get_lock_users",
|
||||
{ATTR_ENTITY_ID: "lock.mock_door_lock"},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
|
||||
users = result["lock.mock_door_lock"]["users"]
|
||||
assert len(users) == 2
|
||||
assert users[0]["creator_fabric_index"] == 3
|
||||
assert users[0]["last_modified_fabric_index"] is None
|
||||
assert users[1]["creator_fabric_index"] is None
|
||||
assert users[1]["last_modified_fabric_index"] == 5
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["mock_door_lock"])
|
||||
@pytest.mark.parametrize("attributes", [{"1/257/65532": _FEATURE_USR_PIN}])
|
||||
@pytest.mark.parametrize(
|
||||
@@ -1524,6 +1585,8 @@ async def test_get_lock_credential_status(
|
||||
assert result["lock.mock_door_lock"] == {
|
||||
"credential_exists": True,
|
||||
"user_index": 2,
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_credential_index": 3,
|
||||
}
|
||||
|
||||
@@ -1571,10 +1634,62 @@ async def test_get_lock_credential_status_empty_slot(
|
||||
assert result["lock.mock_door_lock"] == {
|
||||
"credential_exists": False,
|
||||
"user_index": None,
|
||||
"creator_fabric_index": None,
|
||||
"last_modified_fabric_index": None,
|
||||
"next_credential_index": None,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["mock_door_lock"])
|
||||
@pytest.mark.parametrize("attributes", [{"1/257/65532": _FEATURE_USR_PIN}])
|
||||
@pytest.mark.parametrize(
|
||||
("creator", "last_modified", "expected_creator", "expected_last_modified"),
|
||||
[
|
||||
(3, NullValue, 3, None),
|
||||
(NullValue, 2, None, 2),
|
||||
],
|
||||
)
|
||||
async def test_get_lock_credential_status_with_fabric_indices(
|
||||
hass: HomeAssistant,
|
||||
matter_client: MagicMock,
|
||||
matter_node: MatterNode,
|
||||
creator: int,
|
||||
last_modified: int,
|
||||
expected_creator: int | None,
|
||||
expected_last_modified: int | None,
|
||||
) -> None:
|
||||
"""Test get_lock_credential_status returns fabric indices and normalizes NullValue."""
|
||||
matter_client.send_device_command = AsyncMock(
|
||||
return_value={
|
||||
"credentialExists": True,
|
||||
"userIndex": 2,
|
||||
"creatorFabricIndex": creator,
|
||||
"lastModifiedFabricIndex": last_modified,
|
||||
"nextCredentialIndex": 5,
|
||||
}
|
||||
)
|
||||
|
||||
result = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"get_lock_credential_status",
|
||||
{
|
||||
ATTR_ENTITY_ID: "lock.mock_door_lock",
|
||||
ATTR_CREDENTIAL_TYPE: "pin",
|
||||
ATTR_CREDENTIAL_INDEX: 1,
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
|
||||
assert result["lock.mock_door_lock"] == {
|
||||
"credential_exists": True,
|
||||
"user_index": 2,
|
||||
"creator_fabric_index": expected_creator,
|
||||
"last_modified_fabric_index": expected_last_modified,
|
||||
"next_credential_index": 5,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("node_fixture", ["mock_door_lock"])
|
||||
async def test_credential_services_without_usr_feature(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
'ding',
|
||||
<DoorbellEventType.RING: 'ring'>,
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
@@ -47,7 +47,7 @@
|
||||
'device_class': 'doorbell',
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
'ding',
|
||||
<DoorbellEventType.RING: 'ring'>,
|
||||
]),
|
||||
'friendly_name': 'Front Door Ding',
|
||||
}),
|
||||
@@ -187,7 +187,7 @@
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
'ding',
|
||||
<DoorbellEventType.RING: 'ring'>,
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
@@ -227,7 +227,7 @@
|
||||
'device_class': 'doorbell',
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
'ding',
|
||||
<DoorbellEventType.RING: 'ring'>,
|
||||
]),
|
||||
'friendly_name': 'Ingress Ding',
|
||||
}),
|
||||
|
||||
@@ -9,6 +9,7 @@ import pytest
|
||||
from ring_doorbell import Ring
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.event import ATTR_EVENT_TYPE, DoorbellEventType
|
||||
from homeassistant.components.ring.binary_sensor import RingEvent
|
||||
from homeassistant.components.ring.coordinator import RingEventListener
|
||||
from homeassistant.const import Platform
|
||||
@@ -35,26 +36,38 @@ async def test_states(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("device_id", "device_name", "alert_kind", "device_class"),
|
||||
("device_id", "device_name", "alert_kind", "device_class", "event_type"),
|
||||
[
|
||||
pytest.param(
|
||||
FRONT_DOOR_DEVICE_ID,
|
||||
"front_door",
|
||||
"motion",
|
||||
"motion",
|
||||
"motion",
|
||||
id="front_door_motion",
|
||||
),
|
||||
pytest.param(
|
||||
FRONT_DOOR_DEVICE_ID, "front_door", "ding", "doorbell", id="front_door_ding"
|
||||
FRONT_DOOR_DEVICE_ID,
|
||||
"front_door",
|
||||
"ding",
|
||||
"doorbell",
|
||||
DoorbellEventType.RING,
|
||||
id="front_door_ding",
|
||||
),
|
||||
pytest.param(
|
||||
INGRESS_DEVICE_ID, "ingress", "ding", "doorbell", id="ingress_ding"
|
||||
INGRESS_DEVICE_ID,
|
||||
"ingress",
|
||||
"ding",
|
||||
"doorbell",
|
||||
DoorbellEventType.RING,
|
||||
id="ingress_ding",
|
||||
),
|
||||
pytest.param(
|
||||
INGRESS_DEVICE_ID,
|
||||
"ingress",
|
||||
"intercom_unlock",
|
||||
"button",
|
||||
"intercom_unlock",
|
||||
id="ingress_unlock",
|
||||
),
|
||||
],
|
||||
@@ -68,6 +81,7 @@ async def test_event(
|
||||
device_name: str,
|
||||
alert_kind: str,
|
||||
device_class: str,
|
||||
event_type: str,
|
||||
) -> None:
|
||||
"""Test the Ring event platforms."""
|
||||
|
||||
@@ -96,3 +110,4 @@ async def test_event(
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == start_time_str
|
||||
assert state.attributes[ATTR_EVENT_TYPE] == event_type
|
||||
|
||||
@@ -18,6 +18,10 @@ from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from . import (
|
||||
AIR_PURIFIER_JP_SERVICE_INFO,
|
||||
AIR_PURIFIER_TABLE_JP_SERVICE_INFO,
|
||||
AIR_PURIFIER_TABLE_US_SERVICE_INFO,
|
||||
AIR_PURIFIER_US_SERVICE_INFO,
|
||||
PLUG_MINI_EU_SERVICE_INFO,
|
||||
RELAY_SWITCH_1_SERVICE_INFO,
|
||||
RELAY_SWITCH_2PM_SERVICE_INFO,
|
||||
@@ -294,3 +298,101 @@ async def test_relay_switch_control_with_exception(
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"service_info",
|
||||
"sensor_type",
|
||||
"entity_id",
|
||||
"turn_on_method",
|
||||
"turn_off_method",
|
||||
),
|
||||
[
|
||||
(
|
||||
AIR_PURIFIER_JP_SERVICE_INFO,
|
||||
"air_purifier_jp",
|
||||
"switch.test_name_child_lock",
|
||||
"open_child_lock",
|
||||
"close_child_lock",
|
||||
),
|
||||
(
|
||||
AIR_PURIFIER_TABLE_JP_SERVICE_INFO,
|
||||
"air_purifier_table_jp",
|
||||
"switch.test_name_child_lock",
|
||||
"open_child_lock",
|
||||
"close_child_lock",
|
||||
),
|
||||
(
|
||||
AIR_PURIFIER_US_SERVICE_INFO,
|
||||
"air_purifier_us",
|
||||
"switch.test_name_child_lock",
|
||||
"open_child_lock",
|
||||
"close_child_lock",
|
||||
),
|
||||
(
|
||||
AIR_PURIFIER_TABLE_US_SERVICE_INFO,
|
||||
"air_purifier_table_us",
|
||||
"switch.test_name_child_lock",
|
||||
"open_child_lock",
|
||||
"close_child_lock",
|
||||
),
|
||||
(
|
||||
AIR_PURIFIER_TABLE_JP_SERVICE_INFO,
|
||||
"air_purifier_table_jp",
|
||||
"switch.test_name_wireless_charging",
|
||||
"open_wireless_charging",
|
||||
"close_wireless_charging",
|
||||
),
|
||||
(
|
||||
AIR_PURIFIER_TABLE_US_SERVICE_INFO,
|
||||
"air_purifier_table_us",
|
||||
"switch.test_name_wireless_charging",
|
||||
"open_wireless_charging",
|
||||
"close_wireless_charging",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_air_purifier_switch_control(
|
||||
hass: HomeAssistant,
|
||||
mock_entry_encrypted_factory: Callable[[str], MockConfigEntry],
|
||||
service_info: BluetoothServiceInfoBleak,
|
||||
sensor_type: str,
|
||||
entity_id: str,
|
||||
turn_on_method: str,
|
||||
turn_off_method: str,
|
||||
) -> None:
|
||||
"""Test air purifier switch control."""
|
||||
inject_bluetooth_service_info(hass, service_info)
|
||||
|
||||
entry = mock_entry_encrypted_factory(sensor_type=sensor_type)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
mocked_turn_on = AsyncMock(return_value=True)
|
||||
mocked_turn_off = AsyncMock(return_value=True)
|
||||
|
||||
with patch.multiple(
|
||||
"homeassistant.components.switchbot.switch.switchbot.SwitchbotAirPurifier",
|
||||
update=AsyncMock(return_value=None),
|
||||
**{turn_on_method: mocked_turn_on, turn_off_method: mocked_turn_off},
|
||||
):
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mocked_turn_on.assert_awaited_once()
|
||||
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mocked_turn_off.assert_awaited_once()
|
||||
|
||||
@@ -4,7 +4,6 @@ from unittest.mock import AsyncMock, patch
|
||||
|
||||
from switchbot_api import Device
|
||||
|
||||
from homeassistant.components.switchbot_cloud import DOMAIN
|
||||
from homeassistant.components.switchbot_cloud.image import SwitchBotCloudImage
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import STATE_UNKNOWN
|
||||
@@ -63,7 +62,7 @@ async def test_async_image(
|
||||
entry = await configure_integration(hass)
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
cloud_data = hass.data[DOMAIN][entry.entry_id]
|
||||
cloud_data = entry.runtime_data
|
||||
device, coordinator = cloud_data.devices.images[0]
|
||||
image_entity = SwitchBotCloudImage(cloud_data.api, device, coordinator)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.vilfo import DOMAIN
|
||||
from homeassistant.components.vilfo.const import DOMAIN
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
Reference in New Issue
Block a user