mirror of
https://github.com/home-assistant/core.git
synced 2026-04-13 21:26:19 +02:00
Compare commits
1 Commits
edenhaus-p
...
python-3.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
427faf4854 |
2
.github/workflows/builder.yml
vendored
2
.github/workflows/builder.yml
vendored
@@ -14,7 +14,7 @@ env:
|
||||
UV_HTTP_TIMEOUT: 60
|
||||
UV_SYSTEM_PYTHON: "true"
|
||||
# Base image version from https://github.com/home-assistant/docker
|
||||
BASE_IMAGE_VERSION: "2026.01.0"
|
||||
BASE_IMAGE_VERSION: "2026.02.0"
|
||||
ARCHITECTURES: '["amd64", "aarch64"]'
|
||||
|
||||
permissions: {}
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.14.2
|
||||
3.14.3
|
||||
|
||||
@@ -62,32 +62,14 @@ class ResourceStorageCollection(collection.DictStorageCollection):
|
||||
)
|
||||
self.ll_config = ll_config
|
||||
|
||||
async def _async_ensure_loaded(self) -> None:
|
||||
"""Ensure the collection has been loaded from storage."""
|
||||
async def async_get_info(self) -> dict[str, int]:
|
||||
"""Return the resources info for YAML mode."""
|
||||
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:
|
||||
@@ -136,6 +118,10 @@ 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,8 +71,6 @@ 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
|
||||
|
||||
|
||||
@@ -117,8 +115,6 @@ 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
|
||||
|
||||
|
||||
@@ -218,8 +214,6 @@ 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"),
|
||||
)
|
||||
|
||||
@@ -823,8 +817,7 @@ 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, creator_fabric_index,
|
||||
last_modified_fabric_index, and next_credential_index.
|
||||
Returns typed dict with credential_exists, user_index, next_credential_index.
|
||||
Raises HomeAssistantError on failure.
|
||||
"""
|
||||
lock_endpoint = _get_lock_endpoint_or_raise(node)
|
||||
@@ -846,7 +839,5 @@ 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,14 +1,10 @@
|
||||
"""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,
|
||||
@@ -43,28 +39,21 @@ 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"
|
||||
@@ -93,15 +82,15 @@ WEBHOOK_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: PlaatoConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> 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)]
|
||||
)
|
||||
@@ -110,26 +99,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: PlaatoConfigEntry) -> bo
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_webhook(hass: HomeAssistant, entry: PlaatoConfigEntry) -> None:
|
||||
def async_setup_webhook(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Init webhook based on config entry."""
|
||||
webhook_id = entry.data[CONF_WEBHOOK_ID]
|
||||
device_name = entry.data[CONF_DEVICE_NAME]
|
||||
|
||||
entry.runtime_data = PlaatoData(
|
||||
coordinator=None,
|
||||
device_name=entry.data[CONF_DEVICE_NAME],
|
||||
device_type=entry.data[CONF_DEVICE_TYPE],
|
||||
device_id=None,
|
||||
)
|
||||
_set_entry_data(entry, hass)
|
||||
|
||||
webhook.async_register(
|
||||
hass, DOMAIN, f"{DOMAIN}.{device_name}", webhook_id, handle_webhook
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_coordinator(
|
||||
hass: HomeAssistant, entry: PlaatoConfigEntry
|
||||
) -> None:
|
||||
async def async_setup_coordinator(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Init auth token based on config entry."""
|
||||
auth_token = entry.data[CONF_TOKEN]
|
||||
device_type = entry.data[CONF_DEVICE_TYPE]
|
||||
@@ -144,44 +126,62 @@ async def async_setup_coordinator(
|
||||
)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
entry.runtime_data = PlaatoData(
|
||||
coordinator=coordinator,
|
||||
device_name=entry.data[CONF_DEVICE_NAME],
|
||||
device_type=entry.data[CONF_DEVICE_TYPE],
|
||||
device_id=auth_token,
|
||||
)
|
||||
_set_entry_data(entry, hass, coordinator, auth_token)
|
||||
|
||||
for platform in PLATFORMS:
|
||||
if entry.options.get(platform, True):
|
||||
coordinator.platforms.append(platform)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: PlaatoConfigEntry) -> bool:
|
||||
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:
|
||||
"""Unload a config entry."""
|
||||
if entry.data[CONF_USE_WEBHOOK]:
|
||||
use_webhook = entry.data[CONF_USE_WEBHOOK]
|
||||
hass.data[DOMAIN][entry.entry_id][UNDO_UPDATE_LISTENER]()
|
||||
|
||||
if use_webhook:
|
||||
return await async_unload_webhook(hass, entry)
|
||||
|
||||
return await async_unload_coordinator(hass, entry)
|
||||
|
||||
|
||||
async def async_unload_webhook(hass: HomeAssistant, entry: PlaatoConfigEntry) -> bool:
|
||||
async def async_unload_webhook(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload webhook based entry."""
|
||||
if entry.data[CONF_WEBHOOK_ID] is not None:
|
||||
webhook.async_unregister(hass, entry.data[CONF_WEBHOOK_ID])
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
return await async_unload_platforms(hass, entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_unload_coordinator(
|
||||
hass: HomeAssistant, entry: PlaatoConfigEntry
|
||||
) -> bool:
|
||||
async def async_unload_coordinator(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload auth token based entry."""
|
||||
coordinator = entry.runtime_data.coordinator
|
||||
return await hass.config_entries.async_unload_platforms(
|
||||
entry, coordinator.platforms if coordinator else PLATFORMS
|
||||
)
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR]
|
||||
return await async_unload_platforms(hass, entry, coordinator.platforms)
|
||||
|
||||
|
||||
async def _async_update_listener(hass: HomeAssistant, entry: PlaatoConfigEntry) -> None:
|
||||
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:
|
||||
"""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 . import PlaatoConfigEntry
|
||||
from .const import CONF_USE_WEBHOOK
|
||||
from .const import CONF_USE_WEBHOOK, COORDINATOR, DOMAIN
|
||||
from .entity import PlaatoEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: PlaatoConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Plaato from a config entry."""
|
||||
@@ -26,12 +26,10 @@ async def async_setup_entry(
|
||||
if config_entry.data[CONF_USE_WEBHOOK]:
|
||||
return
|
||||
|
||||
entry_data = config_entry.runtime_data
|
||||
coordinator = entry_data.coordinator
|
||||
assert coordinator is not None
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
|
||||
async_add_entities(
|
||||
PlaatoBinarySensor(
|
||||
entry_data,
|
||||
hass.data[DOMAIN][config_entry.entry_id],
|
||||
sensor_type,
|
||||
coordinator,
|
||||
)
|
||||
|
||||
@@ -19,7 +19,13 @@ 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,36 +1,30 @@
|
||||
"""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[PlaatoDevice]):
|
||||
class PlaatoCoordinator(DataUpdateCoordinator):
|
||||
"""Class to manage fetching data from the API."""
|
||||
|
||||
config_entry: PlaatoConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: PlaatoConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
auth_token: str,
|
||||
device_type: PlaatoDeviceType,
|
||||
update_interval: timedelta,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"""PlaatoEntity class."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import Any
|
||||
|
||||
from pyplaato.models.device import PlaatoDevice
|
||||
|
||||
@@ -10,10 +8,16 @@ from homeassistant.helpers import entity
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import DOMAIN, EXTRA_STATE_ATTRIBUTES, SENSOR_SIGNAL
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import PlaatoData
|
||||
from .const import (
|
||||
DEVICE,
|
||||
DEVICE_ID,
|
||||
DEVICE_NAME,
|
||||
DEVICE_TYPE,
|
||||
DOMAIN,
|
||||
EXTRA_STATE_ATTRIBUTES,
|
||||
SENSOR_DATA,
|
||||
SENSOR_SIGNAL,
|
||||
)
|
||||
|
||||
|
||||
class PlaatoEntity(entity.Entity):
|
||||
@@ -21,21 +25,21 @@ class PlaatoEntity(entity.Entity):
|
||||
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, data: PlaatoData, sensor_type, coordinator=None) -> None:
|
||||
def __init__(self, data, sensor_type, coordinator=None):
|
||||
"""Initialize the sensor."""
|
||||
self._coordinator = coordinator
|
||||
self._entry_data = data
|
||||
self._sensor_type = sensor_type
|
||||
self._device_id = data.device_id
|
||||
self._device_type = data.device_type
|
||||
self._device_name = data.device_name
|
||||
self._device_id = data[DEVICE][DEVICE_ID]
|
||||
self._device_type = data[DEVICE][DEVICE_TYPE]
|
||||
self._device_name = data[DEVICE][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)}, # type: ignore[arg-type]
|
||||
identifiers={(DOMAIN, self._device_id)},
|
||||
manufacturer="Plaato",
|
||||
model=self._device_type,
|
||||
name=self._device_name,
|
||||
@@ -54,7 +58,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,6 +6,7 @@ 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,
|
||||
@@ -17,8 +18,16 @@ from homeassistant.helpers.entity_platform import (
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from . import ATTR_TEMP, SENSOR_UPDATE, PlaatoConfigEntry
|
||||
from .const import CONF_USE_WEBHOOK, SENSOR_SIGNAL
|
||||
from . import ATTR_TEMP, SENSOR_UPDATE
|
||||
from .const import (
|
||||
CONF_USE_WEBHOOK,
|
||||
COORDINATOR,
|
||||
DEVICE,
|
||||
DEVICE_ID,
|
||||
DOMAIN,
|
||||
SENSOR_DATA,
|
||||
SENSOR_SIGNAL,
|
||||
)
|
||||
from .entity import PlaatoEntity
|
||||
|
||||
|
||||
@@ -33,19 +42,19 @@ async def async_setup_platform(
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: PlaatoConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Plaato from a config entry."""
|
||||
entry_data = entry.runtime_data
|
||||
entry_data = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
@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_id:
|
||||
entry_data.device_id = device_id
|
||||
if device_id != entry_data[DEVICE][DEVICE_ID]:
|
||||
entry_data[DEVICE][DEVICE_ID] = device_id
|
||||
async_add_entities(
|
||||
[
|
||||
PlaatoSensor(entry_data, sensor_type)
|
||||
@@ -59,8 +68,7 @@ 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
|
||||
assert coordinator is not None
|
||||
coordinator = entry_data[COORDINATOR]
|
||||
async_add_entities(
|
||||
PlaatoSensor(entry_data, sensor_type, coordinator)
|
||||
for sensor_type in coordinator.data.sensors
|
||||
|
||||
@@ -7,7 +7,6 @@ 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,
|
||||
@@ -35,7 +34,7 @@ EVENT_DESCRIPTIONS: tuple[RingEventEntityDescription, ...] = (
|
||||
key=KIND_DING,
|
||||
translation_key=KIND_DING,
|
||||
device_class=EventDeviceClass.DOORBELL,
|
||||
event_types=[DoorbellEventType.RING],
|
||||
event_types=[KIND_DING],
|
||||
capability=RingCapability.DING,
|
||||
),
|
||||
RingEventEntityDescription(
|
||||
@@ -101,10 +100,7 @@ class RingEvent(RingBaseEntity[RingListenCoordinator, RingDeviceT], EventEntity)
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
if (alert := self._get_coordinator_alert()) and not alert.is_update:
|
||||
if alert.kind == KIND_DING:
|
||||
self._async_handle_event(DoorbellEventType.RING)
|
||||
else:
|
||||
self._async_handle_event(alert.kind)
|
||||
self._async_handle_event(alert.kind)
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
|
||||
@@ -73,14 +73,7 @@
|
||||
},
|
||||
"event": {
|
||||
"ding": {
|
||||
"name": "Ding",
|
||||
"state_attributes": {
|
||||
"event_type": {
|
||||
"state": {
|
||||
"ring": "[%key:component::event::entity_component::doorbell::state_attributes::event_type::state::ring%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
"name": "Ding"
|
||||
},
|
||||
"intercom_unlock": {
|
||||
"name": "Intercom unlock"
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
|
||||
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
|
||||
from .coordinator import SanixConfigEntry, SanixCoordinator
|
||||
from .const import CONF_SERIAL_NUMBER, DOMAIN
|
||||
from .coordinator import SanixCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SanixConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Sanix from a config entry."""
|
||||
|
||||
serial_no = entry.data[CONF_SERIAL_NUMBER]
|
||||
@@ -21,13 +22,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: SanixConfigEntry) -> boo
|
||||
coordinator = SanixCoordinator(hass, entry, sanix_api)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
entry.runtime_data = coordinator
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SanixConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
@@ -15,16 +15,14 @@ from .const import MANUFACTURER
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
type SanixConfigEntry = ConfigEntry[SanixCoordinator]
|
||||
|
||||
|
||||
class SanixCoordinator(DataUpdateCoordinator[Measurement]):
|
||||
"""Sanix coordinator."""
|
||||
|
||||
config_entry: SanixConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: SanixConfigEntry, sanix_api: Sanix
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, sanix_api: Sanix
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
super().__init__(
|
||||
|
||||
@@ -20,6 +20,7 @@ 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
|
||||
@@ -27,7 +28,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN, MANUFACTURER
|
||||
from .coordinator import SanixConfigEntry, SanixCoordinator
|
||||
from .coordinator import SanixCoordinator
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -82,11 +83,11 @@ SENSOR_TYPES: tuple[SanixSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SanixConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Sanix Sensor entities based on a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
SanixSensorEntity(coordinator, description) for description in SENSOR_TYPES
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
"""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 PLATFORMS
|
||||
from .hub import SIAConfigEntry, SIAHub
|
||||
from .const import DOMAIN, PLATFORMS
|
||||
from .hub import SIAHub
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SIAConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up sia from a config entry."""
|
||||
hub = SIAHub(hass, entry)
|
||||
hub: SIAHub = 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)
|
||||
@@ -20,15 +23,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: SIAConfigEntry) -> 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: SIAConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
await entry.runtime_data.async_shutdown()
|
||||
hub: SIAHub = hass.data[DOMAIN].pop(entry.entry_id)
|
||||
await hub.async_shutdown()
|
||||
return unload_ok
|
||||
|
||||
@@ -16,7 +16,12 @@ from pysiaalarm import (
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
)
|
||||
from homeassistant.const import CONF_PORT, CONF_PROTOCOL
|
||||
from homeassistant.core import callback
|
||||
|
||||
@@ -31,7 +36,7 @@ from .const import (
|
||||
DOMAIN,
|
||||
TITLE,
|
||||
)
|
||||
from .hub import SIAConfigEntry, SIAHub
|
||||
from .hub import SIAHub
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -95,7 +100,7 @@ class SIAConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: SIAConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
) -> SIAOptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return SIAOptionsFlowHandler(config_entry)
|
||||
@@ -174,9 +179,7 @@ class SIAConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
class SIAOptionsFlowHandler(OptionsFlow):
|
||||
"""Handle SIA options."""
|
||||
|
||||
config_entry: SIAConfigEntry
|
||||
|
||||
def __init__(self, config_entry: SIAConfigEntry) -> None:
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize SIA options flow."""
|
||||
self.options = deepcopy(dict(config_entry.options))
|
||||
self.hub: SIAHub | None = None
|
||||
@@ -186,7 +189,7 @@ class SIAOptionsFlowHandler(OptionsFlow):
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Manage the SIA options."""
|
||||
self.hub = self.config_entry.runtime_data
|
||||
self.hub = self.hass.data[DOMAIN][self.config_entry.entry_id]
|
||||
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, ConfigEntryState
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
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,8 +28,6 @@ from .utils import get_event_data_from_sia_event
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
type SIAConfigEntry = ConfigEntry[SIAHub]
|
||||
|
||||
DEFAULT_TIMEBAND = (80, 40)
|
||||
|
||||
|
||||
@@ -39,11 +37,11 @@ class SIAHub:
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
entry: SIAConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
) -> None:
|
||||
"""Create the SIAHub."""
|
||||
self._hass = hass
|
||||
self._entry = entry
|
||||
self._hass: HomeAssistant = hass
|
||||
self._entry: ConfigEntry = entry
|
||||
self._port: int = entry.data[CONF_PORT]
|
||||
self._title: str = entry.title
|
||||
self._accounts: list[dict[str, Any]] = deepcopy(entry.data[CONF_ACCOUNTS])
|
||||
@@ -133,7 +131,7 @@ class SIAHub:
|
||||
|
||||
@staticmethod
|
||||
async def async_config_entry_updated(
|
||||
hass: HomeAssistant, config_entry: SIAConfigEntry
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> None:
|
||||
"""Handle signals of config entry being updated.
|
||||
|
||||
@@ -141,8 +139,8 @@ class SIAHub:
|
||||
Second, unload underlying platforms, and then setup platforms, this reflects any changes in number of zones.
|
||||
|
||||
"""
|
||||
if config_entry.state != ConfigEntryState.LOADED:
|
||||
if not (hub := hass.data[DOMAIN].get(config_entry.entry_id)):
|
||||
return
|
||||
config_entry.runtime_data.update_accounts()
|
||||
hub.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
|
||||
from typing import Any, cast
|
||||
|
||||
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, ConfigEntryState
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE,
|
||||
ATTR_DEVICE_ID,
|
||||
@@ -88,8 +88,6 @@ 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"
|
||||
@@ -225,15 +223,10 @@ 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 (
|
||||
(entry := hass.config_entries.async_get_entry(entry_id)) is None
|
||||
or entry.domain != DOMAIN
|
||||
or entry.state != ConfigEntryState.LOADED
|
||||
):
|
||||
if (simplisafe := hass.data[DOMAIN].get(entry_id)) is None:
|
||||
continue
|
||||
return entry.runtime_data.systems[system_id]
|
||||
return cast(SystemType, simplisafe.systems[system_id])
|
||||
|
||||
raise ValueError(f"No system for device ID: {device_id}")
|
||||
|
||||
@@ -293,7 +286,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: SimpliSafeConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up SimpliSafe as config entry."""
|
||||
_async_standardize_config_entry(hass, entry)
|
||||
|
||||
@@ -317,7 +310,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: SimpliSafeConfigEntry) -
|
||||
except SimplipyError as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
entry.runtime_data = simplisafe
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = simplisafe
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -402,9 +396,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: SimpliSafeConfigEntry) -
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SimpliSafeConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> 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,11 +28,12 @@ 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, SimpliSafeConfigEntry
|
||||
from . import SimpliSafe
|
||||
from .const import (
|
||||
ATTR_ALARM_DURATION,
|
||||
ATTR_ALARM_VOLUME,
|
||||
@@ -43,6 +44,7 @@ from .const import (
|
||||
ATTR_EXIT_DELAY_HOME,
|
||||
ATTR_LIGHT,
|
||||
ATTR_VOICE_PROMPT_VOLUME,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
)
|
||||
from .entity import SimpliSafeEntity
|
||||
@@ -102,11 +104,11 @@ WEBSOCKET_EVENTS_TO_LISTEN_FOR = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SimpliSafeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up a SimpliSafe alarm control panel based on a config entry."""
|
||||
simplisafe = entry.runtime_data
|
||||
simplisafe = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
[SimpliSafeAlarm(simplisafe, system) for system in simplisafe.systems.values()],
|
||||
True,
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
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
|
||||
@@ -13,12 +11,13 @@ 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, SimpliSafeConfigEntry
|
||||
from .const import LOGGER
|
||||
from . import SimpliSafe
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .entity import SimpliSafeEntity
|
||||
|
||||
SUPPORTED_BATTERY_SENSOR_TYPES = [
|
||||
@@ -60,11 +59,11 @@ TRIGGERED_SENSOR_TYPES = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SimpliSafeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SimpliSafe binary sensors based on a config entry."""
|
||||
simplisafe = entry.runtime_data
|
||||
simplisafe = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
sensors: list[BatteryBinarySensor | TriggeredBinarySensor] = []
|
||||
|
||||
@@ -73,22 +72,18 @@ 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,
|
||||
cast(SensorV3, sensor),
|
||||
sensor,
|
||||
TRIGGERED_SENSOR_TYPES[sensor.type],
|
||||
)
|
||||
)
|
||||
if sensor.type in SUPPORTED_BATTERY_SENSOR_TYPES:
|
||||
sensors.append(
|
||||
BatteryBinarySensor(simplisafe, system, cast(DeviceV3, sensor))
|
||||
)
|
||||
sensors.append(BatteryBinarySensor(simplisafe, system, sensor))
|
||||
|
||||
sensors.extend(
|
||||
BatteryBinarySensor(simplisafe, system, lock)
|
||||
|
||||
@@ -9,12 +9,14 @@ 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, SimpliSafeConfigEntry
|
||||
from . import SimpliSafe
|
||||
from .const import DOMAIN
|
||||
from .entity import SimpliSafeEntity
|
||||
from .typing import SystemType
|
||||
|
||||
@@ -45,11 +47,11 @@ BUTTON_DESCRIPTIONS = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SimpliSafeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SimpliSafe buttons based on a config entry."""
|
||||
simplisafe = entry.runtime_data
|
||||
simplisafe = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
|
||||
@@ -14,12 +14,16 @@ from simplipy.util.auth import (
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
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"
|
||||
@@ -64,7 +68,7 @@ class SimpliSafeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: SimpliSafeConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
) -> SimpliSafeOptionsFlowHandler:
|
||||
"""Define the config flow to handle options."""
|
||||
return SimpliSafeOptionsFlowHandler()
|
||||
|
||||
@@ -5,6 +5,7 @@ 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,
|
||||
@@ -15,7 +16,8 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import SimpliSafeConfigEntry
|
||||
from . import SimpliSafe
|
||||
from .const import DOMAIN
|
||||
|
||||
CONF_CREDIT_CARD = "creditCard"
|
||||
CONF_EXPIRES = "expires"
|
||||
@@ -51,10 +53,10 @@ TO_REDACT = {
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: SimpliSafeConfigEntry
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
simplisafe = entry.runtime_data
|
||||
simplisafe: SimpliSafe = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
return async_redact_data(
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import Any
|
||||
|
||||
from simplipy.device.lock import Lock, LockStates
|
||||
from simplipy.errors import SimplipyError
|
||||
@@ -10,12 +10,13 @@ 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, SimpliSafeConfigEntry
|
||||
from .const import LOGGER
|
||||
from . import SimpliSafe
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .entity import SimpliSafeEntity
|
||||
|
||||
ATTR_LOCK_LOW_BATTERY = "lock_low_battery"
|
||||
@@ -31,11 +32,11 @@ WEBSOCKET_EVENTS_TO_LISTEN_FOR = (EVENT_LOCK_LOCKED, EVENT_LOCK_UNLOCKED)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SimpliSafeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SimpliSafe locks based on a config entry."""
|
||||
simplisafe = entry.runtime_data
|
||||
simplisafe = hass.data[DOMAIN][entry.entry_id]
|
||||
locks: list[SimpliSafeLock] = []
|
||||
|
||||
for system in simplisafe.systems.values():
|
||||
@@ -43,8 +44,6 @@ 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,8 +2,6 @@
|
||||
|
||||
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
|
||||
@@ -13,22 +11,23 @@ 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, SimpliSafeConfigEntry
|
||||
from .const import LOGGER
|
||||
from . import SimpliSafe
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .entity import SimpliSafeEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SimpliSafeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SimpliSafe freeze sensors based on a config entry."""
|
||||
simplisafe = entry.runtime_data
|
||||
simplisafe = hass.data[DOMAIN][entry.entry_id]
|
||||
sensors: list[SimplisafeFreezeSensor] = []
|
||||
|
||||
for system in simplisafe.systems.values():
|
||||
@@ -36,10 +35,8 @@ 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, cast(SensorV3, sensor))
|
||||
SimplisafeFreezeSensor(simplisafe, system, sensor)
|
||||
for sensor in system.sensors.values()
|
||||
if sensor.type == DeviceTypes.TEMPERATURE
|
||||
)
|
||||
|
||||
@@ -7,12 +7,14 @@ 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 .coordinator import SkybellConfigEntry, SkybellDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SkybellDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
@@ -23,7 +25,7 @@ PLATFORMS = [
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SkybellConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Skybell from a config entry."""
|
||||
email = entry.data[CONF_EMAIL]
|
||||
password = entry.data[CONF_PASSWORD]
|
||||
@@ -51,12 +53,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: SkybellConfigEntry) -> b
|
||||
for coordinator in device_coordinators
|
||||
]
|
||||
)
|
||||
entry.runtime_data = device_coordinators
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = device_coordinators
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SkybellConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return unload_ok
|
||||
|
||||
@@ -9,10 +9,12 @@ 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 .coordinator import SkybellConfigEntry, SkybellDataUpdateCoordinator
|
||||
from . import DOMAIN
|
||||
from .coordinator import SkybellDataUpdateCoordinator
|
||||
from .entity import SkybellEntity
|
||||
|
||||
BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = (
|
||||
@@ -30,14 +32,14 @@ BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SkybellConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
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 entry.runtime_data
|
||||
for coordinator in hass.data[DOMAIN][entry.entry_id]
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -7,12 +7,14 @@ 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 .coordinator import SkybellConfigEntry, SkybellDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SkybellDataUpdateCoordinator
|
||||
from .entity import SkybellEntity
|
||||
|
||||
CAMERA_TYPES: tuple[CameraEntityDescription, ...] = (
|
||||
@@ -29,13 +31,13 @@ CAMERA_TYPES: tuple[CameraEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SkybellConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Skybell camera."""
|
||||
entities = []
|
||||
for description in CAMERA_TYPES:
|
||||
for coordinator in entry.runtime_data:
|
||||
for coordinator in hass.data[DOMAIN][entry.entry_id]:
|
||||
if description.key == "avatar":
|
||||
entities.append(SkybellCamera(coordinator, description))
|
||||
else:
|
||||
|
||||
@@ -10,19 +10,14 @@ 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: SkybellConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: SkybellConfigEntry,
|
||||
device: SkybellDevice,
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, device: SkybellDevice
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
super().__init__(
|
||||
|
||||
@@ -13,22 +13,23 @@ 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 .coordinator import SkybellConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .entity import SkybellEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SkybellConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Skybell switch."""
|
||||
async_add_entities(
|
||||
SkybellLight(coordinator, LightEntityDescription(key="light"))
|
||||
for coordinator in entry.runtime_data
|
||||
for coordinator in hass.data[DOMAIN][entry.entry_id]
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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 .coordinator import SkybellConfigEntry
|
||||
from .entity import SkybellEntity
|
||||
from .entity import DOMAIN, SkybellEntity
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -89,13 +89,13 @@ SENSOR_TYPES: tuple[SkybellSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SkybellConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Skybell sensor."""
|
||||
async_add_entities(
|
||||
SkybellSensor(coordinator, description)
|
||||
for coordinator in entry.runtime_data
|
||||
for coordinator in hass.data[DOMAIN][entry.entry_id]
|
||||
for description in SENSOR_TYPES
|
||||
if coordinator.device.owner or description.key not in CONST.ATTR_OWNER_STATS
|
||||
)
|
||||
|
||||
@@ -5,10 +5,11 @@ 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 .coordinator import SkybellConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .entity import SkybellEntity
|
||||
|
||||
SWITCH_TYPES: tuple[SwitchEntityDescription, ...] = (
|
||||
@@ -29,13 +30,13 @@ SWITCH_TYPES: tuple[SwitchEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SkybellConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SkyBell switch."""
|
||||
async_add_entities(
|
||||
SkybellSwitch(coordinator, description)
|
||||
for coordinator in entry.runtime_data
|
||||
for coordinator in hass.data[DOMAIN][entry.entry_id]
|
||||
for description in SWITCH_TYPES
|
||||
)
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
from aiohttp.client_exceptions import ClientError
|
||||
@@ -31,17 +30,6 @@ 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."""
|
||||
@@ -49,7 +37,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SlackConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Slack from a config entry."""
|
||||
session = aiohttp_client.async_get_clientsession(hass)
|
||||
slack = AsyncWebClient(
|
||||
@@ -64,25 +52,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: SlackConfigEntry) -> boo
|
||||
return False
|
||||
raise ConfigEntryNotReady("Error while setting up integration") from ex
|
||||
|
||||
entry.runtime_data = SlackData(
|
||||
client=slack,
|
||||
url=res[ATTR_URL],
|
||||
user_id=res[ATTR_USER_ID],
|
||||
)
|
||||
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}
|
||||
|
||||
hass.async_create_task(
|
||||
discovery.async_load_platform(
|
||||
hass,
|
||||
Platform.NOTIFY,
|
||||
DOMAIN,
|
||||
entry.data
|
||||
| {
|
||||
SLACK_DATA: {
|
||||
DATA_CLIENT: slack,
|
||||
ATTR_URL: res[ATTR_URL],
|
||||
ATTR_USER_ID: res[ATTR_USER_ID],
|
||||
}
|
||||
},
|
||||
hass.data[DOMAIN][entry.entry_id],
|
||||
hass.data[DATA_HASS_CONFIG],
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
"""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 . import SlackConfigEntry, SlackData
|
||||
from .const import DEFAULT_NAME, DOMAIN
|
||||
from .const import ATTR_URL, ATTR_USER_ID, DATA_CLIENT, DEFAULT_NAME, DOMAIN
|
||||
|
||||
|
||||
class SlackEntity(Entity):
|
||||
@@ -12,16 +16,16 @@ class SlackEntity(Entity):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: SlackData,
|
||||
data: dict[str, AsyncWebClient],
|
||||
description: EntityDescription,
|
||||
entry: SlackConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize a Slack entity."""
|
||||
self._client = data.client
|
||||
self._client: AsyncWebClient = data[DATA_CLIENT]
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{data.user_id}_{description.key}"
|
||||
self._attr_unique_id = f"{data[ATTR_USER_ID]}_{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
configuration_url=data.url,
|
||||
configuration_url=str(data[ATTR_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 . import SlackConfigEntry
|
||||
from .const import ATTR_SNOOZE
|
||||
from .const import ATTR_SNOOZE, DOMAIN, SLACK_DATA
|
||||
from .entity import SlackEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SlackConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Slack sensor."""
|
||||
"""Set up the Slack select."""
|
||||
async_add_entities(
|
||||
[
|
||||
SlackSensorEntity(
|
||||
entry.runtime_data,
|
||||
hass.data[DOMAIN][entry.entry_id][SLACK_DATA],
|
||||
SensorEntityDescription(
|
||||
key="do_not_disturb_until",
|
||||
translation_key="do_not_disturb_until",
|
||||
|
||||
@@ -23,7 +23,6 @@ from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DOMAIN, IS_IN_BED, SLEEP_NUMBER
|
||||
from .coordinator import (
|
||||
SleepIQConfigEntry,
|
||||
SleepIQData,
|
||||
SleepIQDataUpdateCoordinator,
|
||||
SleepIQPauseUpdateCoordinator,
|
||||
@@ -65,7 +64,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SleepIQConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up the SleepIQ config entry."""
|
||||
conf = entry.data
|
||||
email = conf[CONF_USERNAME]
|
||||
@@ -105,7 +104,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: SleepIQConfigEntry) -> b
|
||||
await pause_coordinator.async_config_entry_first_refresh()
|
||||
await sleep_data_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
entry.runtime_data = SleepIQData(
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = SleepIQData(
|
||||
data_coordinator=coordinator,
|
||||
pause_coordinator=pause_coordinator,
|
||||
sleep_data_coordinator=sleep_data_coordinator,
|
||||
@@ -117,9 +116,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: SleepIQConfigEntry) -> b
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SleepIQConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload the config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def _async_migrate_unique_ids(
|
||||
|
||||
@@ -6,21 +6,22 @@ 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 ICON_EMPTY, ICON_OCCUPIED, IS_IN_BED
|
||||
from .coordinator import SleepIQConfigEntry, SleepIQDataUpdateCoordinator
|
||||
from .const import DOMAIN, ICON_EMPTY, ICON_OCCUPIED, IS_IN_BED
|
||||
from .coordinator import SleepIQData, SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQSleeperEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SleepIQConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed binary sensors."""
|
||||
data = entry.runtime_data
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
IsInBedBinarySensor(data.data_coordinator, bed, sleeper)
|
||||
for bed in data.client.beds.values()
|
||||
|
||||
@@ -9,10 +9,12 @@ 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 .coordinator import SleepIQConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SleepIQData
|
||||
from .entity import SleepIQEntity
|
||||
|
||||
|
||||
@@ -41,11 +43,11 @@ ENTITY_DESCRIPTIONS = [
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SleepIQConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the sleep number buttons."""
|
||||
data = entry.runtime_data
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
SleepNumberButton(bed, ed)
|
||||
|
||||
@@ -18,18 +18,16 @@ 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: SleepIQConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: SleepIQConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
client: AsyncSleepIQ,
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
@@ -53,12 +51,12 @@ class SleepIQDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
class SleepIQPauseUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
"""SleepIQ data update coordinator."""
|
||||
|
||||
config_entry: SleepIQConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: SleepIQConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
client: AsyncSleepIQ,
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
@@ -80,12 +78,12 @@ class SleepIQPauseUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
class SleepIQSleepDataCoordinator(DataUpdateCoordinator[None]):
|
||||
"""SleepIQ sleep health data coordinator."""
|
||||
|
||||
config_entry: SleepIQConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: SleepIQConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
client: AsyncSleepIQ,
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
|
||||
@@ -6,10 +6,12 @@ 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 .coordinator import SleepIQConfigEntry, SleepIQDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SleepIQData, SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQBedEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -17,11 +19,11 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SleepIQConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed lights."""
|
||||
data = entry.runtime_data
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
SleepIQLightEntity(data.data_coordinator, bed, light)
|
||||
for bed in data.client.beds.values()
|
||||
|
||||
@@ -21,6 +21,7 @@ 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
|
||||
@@ -28,12 +29,13 @@ 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 SleepIQConfigEntry, SleepIQDataUpdateCoordinator
|
||||
from .coordinator import SleepIQData, SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQBedEntity, sleeper_for_side
|
||||
|
||||
|
||||
@@ -178,11 +180,11 @@ NUMBER_DESCRIPTIONS: dict[str, SleepIQNumberEntityDescription] = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SleepIQConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed sensors."""
|
||||
data = entry.runtime_data
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
entities: list[SleepIQNumberEntity] = []
|
||||
for bed in data.client.beds.values():
|
||||
|
||||
@@ -13,21 +13,22 @@ 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, FOOT_WARMER
|
||||
from .coordinator import SleepIQConfigEntry, SleepIQDataUpdateCoordinator
|
||||
from .const import CORE_CLIMATE, DOMAIN, FOOT_WARMER
|
||||
from .coordinator import SleepIQData, SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQBedEntity, SleepIQSleeperEntity, sleeper_for_side
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SleepIQConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ foundation preset select entities."""
|
||||
data = entry.runtime_data
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
entities: list[SleepIQBedEntity] = []
|
||||
for bed in data.client.beds.values():
|
||||
entities.extend(
|
||||
|
||||
@@ -13,11 +13,13 @@ 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,
|
||||
@@ -27,7 +29,7 @@ from .const import (
|
||||
SLEEP_SCORE,
|
||||
)
|
||||
from .coordinator import (
|
||||
SleepIQConfigEntry,
|
||||
SleepIQData,
|
||||
SleepIQDataUpdateCoordinator,
|
||||
SleepIQSleepDataCoordinator,
|
||||
)
|
||||
@@ -110,11 +112,11 @@ SLEEP_HEALTH_SENSORS: tuple[SleepIQSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SleepIQConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed sensors."""
|
||||
data = entry.runtime_data
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
entities: list[SensorEntity] = []
|
||||
|
||||
|
||||
@@ -7,20 +7,22 @@ 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 .coordinator import SleepIQConfigEntry, SleepIQPauseUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SleepIQData, SleepIQPauseUpdateCoordinator
|
||||
from .entity import SleepIQBedEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SleepIQConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the sleep number switches."""
|
||||
data = entry.runtime_data
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
SleepNumberPrivateSwitch(data.pause_coordinator, bed)
|
||||
for bed in data.client.beds.values()
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
"""The soundtouch component."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from libsoundtouch import soundtouch_device
|
||||
from libsoundtouch.device import SoundTouchDevice
|
||||
@@ -25,11 +22,6 @@ 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})
|
||||
@@ -58,12 +50,12 @@ CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||
|
||||
|
||||
class SoundTouchData:
|
||||
"""SoundTouch data stored in the config entry runtime data."""
|
||||
"""SoundTouch data stored in the Home Assistant data object."""
|
||||
|
||||
def __init__(self, device: SoundTouchDevice) -> None:
|
||||
"""Initialize the SoundTouch data object for a device."""
|
||||
self.device = device
|
||||
self.media_player: SoundTouchMediaPlayer | None = None
|
||||
self.media_player = None
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
@@ -73,25 +65,20 @@ 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 = [
|
||||
media_player
|
||||
for media_player in all_media_players
|
||||
if media_player.entity_id in slaves_ids
|
||||
data.media_player
|
||||
for data in hass.data[DOMAIN].values()
|
||||
if data.media_player.entity_id in slaves_ids
|
||||
]
|
||||
|
||||
master = next(
|
||||
iter(
|
||||
[
|
||||
media_player
|
||||
for media_player in all_media_players
|
||||
if media_player.entity_id == master_id
|
||||
data.media_player
|
||||
for data in hass.data[DOMAIN].values()
|
||||
if data.media_player.entity_id == master_id
|
||||
]
|
||||
),
|
||||
None,
|
||||
@@ -103,9 +90,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
|
||||
if service.service == SERVICE_PLAY_EVERYWHERE:
|
||||
slaves = [
|
||||
media_player
|
||||
for media_player in all_media_players
|
||||
if media_player.entity_id != master_id
|
||||
data.media_player
|
||||
for data in hass.data[DOMAIN].values()
|
||||
if data.media_player.entity_id != master_id
|
||||
]
|
||||
await hass.async_add_executor_job(master.create_zone, slaves)
|
||||
elif service.service == SERVICE_CREATE_ZONE:
|
||||
@@ -143,7 +130,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SoundTouchConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Bose SoundTouch from a config entry."""
|
||||
try:
|
||||
device = await hass.async_add_executor_job(
|
||||
@@ -154,12 +141,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: SoundTouchConfigEntry) -
|
||||
f"Unable to connect to SoundTouch device at {entry.data[CONF_HOST]}"
|
||||
) from err
|
||||
|
||||
entry.runtime_data = SoundTouchData(device)
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = SoundTouchData(device)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SoundTouchConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
del hass.data[DOMAIN][entry.entry_id]
|
||||
return unload_ok
|
||||
|
||||
@@ -19,6 +19,7 @@ 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 (
|
||||
@@ -28,7 +29,6 @@ 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: SoundTouchConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Bose SoundTouch media player based on a config entry."""
|
||||
device = entry.runtime_data.device
|
||||
device = hass.data[DOMAIN][entry.entry_id].device
|
||||
media_player = SoundTouchMediaPlayer(device)
|
||||
|
||||
async_add_entities([media_player], True)
|
||||
|
||||
entry.runtime_data.media_player = media_player
|
||||
hass.data[DOMAIN][entry.entry_id].media_player = media_player
|
||||
|
||||
|
||||
class SoundTouchMediaPlayer(MediaPlayerEntity):
|
||||
@@ -388,16 +388,14 @@ class SoundTouchMediaPlayer(MediaPlayerEntity):
|
||||
|
||||
def _get_instance_by_ip(self, ip_address):
|
||||
"""Search and return a SoundTouchDevice instance by it's IP address."""
|
||||
for entry in self.hass.config_entries.async_loaded_entries(DOMAIN):
|
||||
data = entry.runtime_data
|
||||
for data in self.hass.data[DOMAIN].values():
|
||||
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 entry in self.hass.config_entries.async_loaded_entries(DOMAIN):
|
||||
data = entry.runtime_data
|
||||
for data in self.hass.data[DOMAIN].values():
|
||||
if data.device.config.device_id == instance_id:
|
||||
return data.media_player
|
||||
return None
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
|
||||
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 LOGGER
|
||||
from .coordinator import SRPEnergyConfigEntry, SRPEnergyDataUpdateCoordinator
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .coordinator import SRPEnergyDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SRPEnergyConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> 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]
|
||||
@@ -29,13 +30,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: SRPEnergyConfigEntry) ->
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
entry.runtime_data = coordinator
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SRPEnergyConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
@@ -23,19 +23,14 @@ 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: SRPEnergyConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: SRPEnergyConfigEntry,
|
||||
client: SrpEnergyClient,
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, client: SrpEnergyClient
|
||||
) -> None:
|
||||
"""Initialize the srp_energy data coordinator."""
|
||||
self._client = client
|
||||
|
||||
@@ -7,6 +7,7 @@ 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
|
||||
@@ -14,17 +15,19 @@ 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: SRPEnergyConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SRP Energy Usage sensor."""
|
||||
async_add_entities([SrpEntity(entry.runtime_data, entry)])
|
||||
coordinator: SRPEnergyDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities([SrpEntity(coordinator, entry)])
|
||||
|
||||
|
||||
class SrpEntity(CoordinatorEntity[SRPEnergyDataUpdateCoordinator], SensorEntity):
|
||||
@@ -40,7 +43,7 @@ class SrpEntity(CoordinatorEntity[SRPEnergyDataUpdateCoordinator], SensorEntity)
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: SRPEnergyDataUpdateCoordinator,
|
||||
config_entry: SRPEnergyConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the SrpEntity class."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
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 StreamlabsConfigEntry, StreamlabsCoordinator
|
||||
from .coordinator import StreamlabsCoordinator
|
||||
|
||||
ATTR_AWAY_MODE = "away_mode"
|
||||
SERVICE_SET_AWAY_MODE = "set_away_mode"
|
||||
@@ -29,7 +30,7 @@ SET_AWAY_MODE_SCHEMA = vol.Schema(
|
||||
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: StreamlabsConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up StreamLabs from a config entry."""
|
||||
|
||||
api_key = entry.data[CONF_API_KEY]
|
||||
@@ -38,7 +39,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: StreamlabsConfigEntry) -
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
entry.runtime_data = coordinator
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
def set_away_mode(service: ServiceCall) -> None:
|
||||
@@ -54,6 +55,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: StreamlabsConfigEntry) -
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: StreamlabsConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
@@ -3,20 +3,22 @@
|
||||
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 .coordinator import StreamlabsConfigEntry, StreamlabsCoordinator
|
||||
from . import StreamlabsCoordinator
|
||||
from .const import DOMAIN
|
||||
from .entity import StreamlabsWaterEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: StreamlabsConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Streamlabs water binary sensor from a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
StreamlabsAwayMode(coordinator, location_id) for location_id in coordinator.data
|
||||
|
||||
@@ -23,18 +23,15 @@ class StreamlabsData:
|
||||
yearly_usage: float
|
||||
|
||||
|
||||
type StreamlabsConfigEntry = ConfigEntry[StreamlabsCoordinator]
|
||||
|
||||
|
||||
class StreamlabsCoordinator(DataUpdateCoordinator[dict[str, StreamlabsData]]):
|
||||
"""Coordinator for Streamlabs."""
|
||||
|
||||
config_entry: StreamlabsConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: StreamlabsConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
client: StreamlabsClient,
|
||||
) -> None:
|
||||
"""Coordinator for Streamlabs."""
|
||||
|
||||
@@ -10,12 +10,15 @@ 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 .coordinator import StreamlabsConfigEntry, StreamlabsCoordinator, StreamlabsData
|
||||
from . import StreamlabsCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import StreamlabsData
|
||||
from .entity import StreamlabsWaterEntity
|
||||
|
||||
|
||||
@@ -56,11 +59,11 @@ SENSORS: tuple[StreamlabsWaterSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: StreamlabsConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Streamlabs water sensor from a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
StreamLabsSensor(coordinator, location_id, entity_description)
|
||||
|
||||
@@ -9,6 +9,7 @@ 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
|
||||
@@ -23,7 +24,7 @@ from .const import (
|
||||
SERVICE_SET_LOCK_STATE,
|
||||
SERVICE_SET_PET_LOCATION,
|
||||
)
|
||||
from .coordinator import SurePetcareConfigEntry, SurePetcareDataCoordinator
|
||||
from .coordinator import SurePetcareDataCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -31,10 +32,15 @@ PLATFORMS = [Platform.BINARY_SENSOR, Platform.LOCK, Platform.SENSOR]
|
||||
SCAN_INTERVAL = timedelta(minutes=3)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SurePetcareConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Sure Petcare from a config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
try:
|
||||
coordinator = SurePetcareDataCoordinator(hass, entry)
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator = SurePetcareDataCoordinator(
|
||||
hass,
|
||||
entry,
|
||||
)
|
||||
except SurePetcareAuthenticationError as error:
|
||||
_LOGGER.error("Unable to connect to surepetcare.io: Wrong credentials!")
|
||||
raise ConfigEntryAuthFailed from error
|
||||
@@ -43,7 +49,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: SurePetcareConfigEntry)
|
||||
|
||||
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(
|
||||
@@ -86,8 +91,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: SurePetcareConfigEntry)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: SurePetcareConfigEntry
|
||||
) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
@@ -12,24 +12,26 @@ 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 .coordinator import SurePetcareConfigEntry, SurePetcareDataCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SurePetcareDataCoordinator
|
||||
from .entity import SurePetcareEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SurePetcareConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Sure PetCare Flaps binary sensors based on a config entry."""
|
||||
|
||||
entities: list[SurePetcareBinarySensor] = []
|
||||
|
||||
coordinator = entry.runtime_data
|
||||
coordinator: SurePetcareDataCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
for surepy_entity in coordinator.data.values():
|
||||
# connectivity
|
||||
|
||||
@@ -29,15 +29,13 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL = timedelta(minutes=3)
|
||||
|
||||
type SurePetcareConfigEntry = ConfigEntry[SurePetcareDataCoordinator]
|
||||
|
||||
|
||||
class SurePetcareDataCoordinator(DataUpdateCoordinator[dict[int, SurepyEntity]]):
|
||||
"""Handle Surepetcare data."""
|
||||
|
||||
config_entry: SurePetcareConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, entry: SurePetcareConfigEntry) -> None:
|
||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Initialize the data handler."""
|
||||
self.surepy = Surepy(
|
||||
entry.data[CONF_USERNAME],
|
||||
|
||||
@@ -8,21 +8,23 @@ 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 .coordinator import SurePetcareConfigEntry, SurePetcareDataCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SurePetcareDataCoordinator
|
||||
from .entity import SurePetcareEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SurePetcareConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Sure PetCare locks on a config entry."""
|
||||
|
||||
coordinator = entry.runtime_data
|
||||
coordinator: SurePetcareDataCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
SurePetcareLock(surepy_entity.id, coordinator, lock_state)
|
||||
|
||||
@@ -10,25 +10,26 @@ 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 SURE_BATT_VOLTAGE_DIFF, SURE_BATT_VOLTAGE_LOW
|
||||
from .coordinator import SurePetcareConfigEntry, SurePetcareDataCoordinator
|
||||
from .const import DOMAIN, SURE_BATT_VOLTAGE_DIFF, SURE_BATT_VOLTAGE_LOW
|
||||
from .coordinator import SurePetcareDataCoordinator
|
||||
from .entity import SurePetcareEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SurePetcareConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Sure PetCare Flaps sensors."""
|
||||
|
||||
entities: list[SurePetcareEntity] = []
|
||||
|
||||
coordinator = entry.runtime_data
|
||||
coordinator: SurePetcareDataCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
for surepy_entity in coordinator.data.values():
|
||||
if surepy_entity.type in [
|
||||
|
||||
@@ -9,6 +9,7 @@ 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
|
||||
@@ -16,7 +17,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 SwitchBeeConfigEntry, SwitchBeeCoordinator
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -52,9 +53,10 @@ async def get_api_object(
|
||||
return api
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: SwitchBeeConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> 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]
|
||||
@@ -65,28 +67,27 @@ async def async_setup_entry(hass: HomeAssistant, entry: SwitchBeeConfigEntry) ->
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
entry.runtime_data = coordinator
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: SwitchBeeConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def update_listener(
|
||||
hass: HomeAssistant, config_entry: SwitchBeeConfigEntry
|
||||
) -> None:
|
||||
async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
"""Update listener."""
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
|
||||
|
||||
async def async_migrate_entry(
|
||||
hass: HomeAssistant, config_entry: SwitchBeeConfigEntry
|
||||
) -> bool:
|
||||
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Migrate old entry."""
|
||||
_LOGGER.debug("Migrating from version %s", config_entry.version)
|
||||
|
||||
|
||||
@@ -4,21 +4,23 @@ 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 .coordinator import SwitchBeeConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .entity import SwitchBeeEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchBeeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Switchbee button."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
SwitchBeeButton(switchbee_device, coordinator)
|
||||
for switchbee_device in coordinator.data.values()
|
||||
|
||||
@@ -23,12 +23,14 @@ 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 .coordinator import SwitchBeeConfigEntry, SwitchBeeCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .entity import SwitchBeeDeviceEntity
|
||||
|
||||
FAN_SB_TO_HASS = {
|
||||
@@ -73,11 +75,11 @@ SUPPORTED_FAN_MODES = [FAN_AUTO, FAN_HIGH, FAN_MEDIUM, FAN_LOW]
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchBeeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBee climate."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
SwitchBeeClimateEntity(switchbee_device, coordinator)
|
||||
for switchbee_device in coordinator.data.values()
|
||||
|
||||
@@ -19,18 +19,16 @@ 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: SwitchBeeConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: SwitchBeeConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
swb_api: CentralUnitPolling | CentralUnitWsRPC,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
|
||||
@@ -14,21 +14,23 @@ 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 .coordinator import SwitchBeeConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .entity import SwitchBeeDeviceEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchBeeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBee covers."""
|
||||
coordinator = entry.runtime_data
|
||||
"""Set up SwitchBee switch."""
|
||||
coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
entities: list[CoverEntity] = []
|
||||
|
||||
for device in coordinator.data.values():
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, cast
|
||||
from typing import Any
|
||||
|
||||
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 .coordinator import SwitchBeeConfigEntry, SwitchBeeCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .entity import SwitchBeeDeviceEntity
|
||||
|
||||
MAX_BRIGHTNESS = 255
|
||||
@@ -34,13 +36,13 @@ def _switchbee_brightness_to_hass(value: int) -> int:
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchBeeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBee light."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
SwitchBeeLightEntity(cast(SwitchBeeDimmer, switchbee_device), coordinator)
|
||||
SwitchBeeLightEntity(switchbee_device, coordinator)
|
||||
for switchbee_device in coordinator.data.values()
|
||||
if switchbee_device.type == DeviceType.Dimmer
|
||||
)
|
||||
|
||||
@@ -14,21 +14,23 @@ 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 .coordinator import SwitchBeeConfigEntry, SwitchBeeCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBeeCoordinator
|
||||
from .entity import SwitchBeeDeviceEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchBeeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Switchbee switch."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
SwitchBeeSwitchEntity(device, coordinator)
|
||||
|
||||
@@ -114,25 +114,21 @@ 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,20 +145,6 @@
|
||||
"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,12 +326,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"child_lock": {
|
||||
"name": "Child lock"
|
||||
},
|
||||
"wireless_charging": {
|
||||
"name": "Wireless charging"
|
||||
}
|
||||
},
|
||||
"vacuum": {
|
||||
|
||||
@@ -2,61 +2,22 @@
|
||||
|
||||
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,
|
||||
SwitchEntityDescription,
|
||||
)
|
||||
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
|
||||
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 AIRPURIFIER_BASIC_MODELS, AIRPURIFIER_TABLE_MODELS, DOMAIN
|
||||
from .const import 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__)
|
||||
|
||||
@@ -75,64 +36,10 @@ 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,12 +75,9 @@ class SwitchbotCloudData:
|
||||
devices: SwitchbotDevices
|
||||
|
||||
|
||||
type SwitchbotCloudConfigEntry = ConfigEntry[SwitchbotCloudData]
|
||||
|
||||
|
||||
async def coordinator_for_device(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchbotCloudConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
api: SwitchBotAPI,
|
||||
device: Device | Remote,
|
||||
coordinators_by_id: dict[str, SwitchBotCoordinator],
|
||||
@@ -100,7 +97,7 @@ async def coordinator_for_device(
|
||||
|
||||
async def make_switchbot_devices(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchbotCloudConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
api: SwitchBotAPI,
|
||||
devices: list[Device | Remote],
|
||||
coordinators_by_id: dict[str, SwitchBotCoordinator],
|
||||
@@ -118,7 +115,7 @@ async def make_switchbot_devices(
|
||||
|
||||
async def make_device_data(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchbotCloudConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
api: SwitchBotAPI,
|
||||
device: Device | Remote,
|
||||
devices_data: SwitchbotDevices,
|
||||
@@ -333,9 +330,7 @@ async def make_device_data(
|
||||
devices_data.sensors.append((device, coordinator))
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: SwitchbotCloudConfigEntry
|
||||
) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up SwitchBot via API from a config entry."""
|
||||
token = entry.data[CONF_API_TOKEN]
|
||||
secret = entry.data[CONF_API_KEY]
|
||||
@@ -358,7 +353,10 @@ async def async_setup_entry(
|
||||
switchbot_devices = await make_switchbot_devices(
|
||||
hass, entry, api, devices, coordinators_by_id
|
||||
)
|
||||
entry.runtime_data = SwitchbotCloudData(api=api, devices=switchbot_devices)
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = SwitchbotCloudData(
|
||||
api=api, devices=switchbot_devices
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
@@ -367,16 +365,17 @@ async def async_setup_entry(
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: SwitchbotCloudConfigEntry
|
||||
) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def _initialize_webhook(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchbotCloudConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
api: SwitchBotAPI,
|
||||
coordinators_by_id: dict[str, SwitchBotCoordinator],
|
||||
) -> None:
|
||||
|
||||
@@ -11,11 +11,13 @@ 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 SwitchbotCloudConfigEntry
|
||||
from . import SwitchbotCloudData
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBotCoordinator
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
@@ -135,11 +137,11 @@ BINARY_SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
SwitchBotCloudBinarySensor(data.api, device, coordinator, description)
|
||||
|
||||
@@ -12,10 +12,12 @@ 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 SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import DOMAIN
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
|
||||
@@ -56,11 +58,11 @@ BUTTON_DESCRIPTIONS_BY_DEVICE_TYPES = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
entities: list[SwitchBotCloudBot] = []
|
||||
for device, coordinator in data.devices.buttons:
|
||||
description_set = BUTTON_DESCRIPTIONS_BY_DEVICE_TYPES[device.device_type]
|
||||
|
||||
@@ -26,6 +26,7 @@ from homeassistant.components.climate import (
|
||||
ClimateEntityFeature,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
PRECISION_TENTHS,
|
||||
STATE_UNAVAILABLE,
|
||||
@@ -36,9 +37,10 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from . import SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import (
|
||||
CLIMATE_PRESET_SCHEDULE,
|
||||
DOMAIN,
|
||||
SMART_RADIATOR_THERMOSTAT_AFTER_COMMAND_REFRESH,
|
||||
)
|
||||
from .entity import SwitchBotCloudEntity
|
||||
@@ -67,11 +69,11 @@ _DEFAULT_SWITCHBOT_FAN_MODE = _SWITCHBOT_FAN_MODES[FanState.FAN_AUTO]
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
async_add_entities(
|
||||
_async_make_entity(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.climates
|
||||
|
||||
@@ -18,21 +18,22 @@ 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 SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from .const import COVER_ENTITY_AFTER_COMMAND_REFRESH
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import COVER_ENTITY_AFTER_COMMAND_REFRESH, DOMAIN
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
async_add_entities(
|
||||
_async_make_entity(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.covers
|
||||
|
||||
@@ -13,12 +13,13 @@ 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 SwitchbotCloudConfigEntry
|
||||
from .const import AFTER_COMMAND_REFRESH, AirPurifierMode
|
||||
from . import SwitchbotCloudData
|
||||
from .const import AFTER_COMMAND_REFRESH, DOMAIN, AirPurifierMode
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -27,11 +28,11 @@ PARALLEL_UPDATES = 0
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
for device, coordinator in data.devices.fans:
|
||||
if device.device_type.startswith("Air Purifier"):
|
||||
async_add_entities(
|
||||
|
||||
@@ -12,12 +12,13 @@ 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 SwitchbotCloudConfigEntry
|
||||
from .const import AFTER_COMMAND_REFRESH, HUMIDITY_LEVELS, Humidifier2Mode
|
||||
from . import SwitchbotCloudData
|
||||
from .const import AFTER_COMMAND_REFRESH, DOMAIN, HUMIDITY_LEVELS, Humidifier2Mode
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
@@ -25,11 +26,11 @@ PARALLEL_UPDATES = 0
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SwitchbotCloudConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Switchbot based on a config entry."""
|
||||
data = entry.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
SwitchBotHumidifier(data.api, device, coordinator)
|
||||
if device.device_type == "Humidifier"
|
||||
|
||||
@@ -6,20 +6,22 @@ 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 SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import DOMAIN
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
async_add_entities(
|
||||
_async_make_entity(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.images
|
||||
|
||||
@@ -14,11 +14,12 @@ 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 SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from .const import AFTER_COMMAND_REFRESH
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import AFTER_COMMAND_REFRESH, DOMAIN
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
|
||||
@@ -34,11 +35,11 @@ def brightness_map_value(value: int) -> int:
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
async_add_entities(
|
||||
_async_make_entity(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.lights
|
||||
|
||||
@@ -5,20 +5,22 @@ 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 SwitchbotCloudConfigEntry, SwitchBotCoordinator
|
||||
from . import SwitchbotCloudData, SwitchBotCoordinator
|
||||
from .const import DOMAIN
|
||||
from .entity import SwitchBotCloudEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
async_add_entities(
|
||||
SwitchBotCloudLock(data.api, device, coordinator)
|
||||
for device, coordinator in data.devices.locks
|
||||
|
||||
@@ -12,6 +12,7 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
PERCENTAGE,
|
||||
@@ -25,7 +26,7 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import SwitchbotCloudConfigEntry
|
||||
from . import SwitchbotCloudData
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SwitchBotCoordinator
|
||||
from .entity import SwitchBotCloudEntity
|
||||
@@ -266,11 +267,11 @@ SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
entities: list[SwitchBotCloudSensor] = []
|
||||
for device, coordinator in data.devices.sensors:
|
||||
for description in SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES[device.device_type]:
|
||||
|
||||
@@ -6,11 +6,12 @@ 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 SwitchbotCloudConfigEntry
|
||||
from . import SwitchbotCloudData
|
||||
from .const import AFTER_COMMAND_REFRESH, DOMAIN
|
||||
from .coordinator import SwitchBotCoordinator
|
||||
from .entity import SwitchBotCloudEntity
|
||||
@@ -18,11 +19,11 @@ from .entity import SwitchBotCloudEntity
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
entities: list[SwitchBotCloudSwitch] = []
|
||||
for device, coordinator in data.devices.switches:
|
||||
if device.device_type == "Relay Switch 2PM":
|
||||
|
||||
@@ -17,11 +17,13 @@ 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 SwitchbotCloudConfigEntry
|
||||
from . import SwitchbotCloudData
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
VACUUM_FAN_SPEED_MAX,
|
||||
VACUUM_FAN_SPEED_QUIET,
|
||||
VACUUM_FAN_SPEED_STANDARD,
|
||||
@@ -33,11 +35,11 @@ from .entity import SwitchBotCloudEntity
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config: SwitchbotCloudConfigEntry,
|
||||
config: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up SwitchBot Cloud entry."""
|
||||
data = config.runtime_data
|
||||
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]
|
||||
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, ConfigEntryState
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_COMMAND,
|
||||
@@ -57,24 +57,7 @@ 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 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
|
||||
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -110,7 +93,7 @@ POWER_COMMAND_MAP = {
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
) -> bool:
|
||||
"""Set up System Bridge from a config entry."""
|
||||
|
||||
@@ -215,7 +198,8 @@ async def async_setup_entry(
|
||||
# Fetch initial data so we have data when entities subscribe
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
entry.runtime_data = coordinator
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
|
||||
# Set up all platforms except notify
|
||||
await hass.config_entries.async_forward_entry_setups(
|
||||
@@ -232,7 +216,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],
|
||||
)
|
||||
)
|
||||
|
||||
@@ -265,7 +249,9 @@ 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 = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
processes: list[Process] = coordinator.data.processes
|
||||
|
||||
# Find process.id from list, raise ServiceValidationError if not found
|
||||
@@ -289,7 +275,9 @@ 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 = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
|
||||
# Find processes from list
|
||||
items: list[dict[str, Any]] = [
|
||||
@@ -307,7 +295,9 @@ 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 = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
response = await coordinator.websocket_client.open_path(
|
||||
OpenPath(path=service_call.data[CONF_PATH])
|
||||
)
|
||||
@@ -316,7 +306,9 @@ 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 = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
response = await getattr(
|
||||
coordinator.websocket_client,
|
||||
POWER_COMMAND_MAP[service_call.data[CONF_COMMAND]],
|
||||
@@ -326,7 +318,9 @@ 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 = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
response = await coordinator.websocket_client.open_url(
|
||||
OpenUrl(url=service_call.data[CONF_URL])
|
||||
)
|
||||
@@ -334,7 +328,9 @@ async def async_setup_entry(
|
||||
|
||||
async def handle_send_keypress(service_call: ServiceCall) -> ServiceResponse:
|
||||
"""Handle the send_keypress service call."""
|
||||
coordinator = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
response = await coordinator.websocket_client.keyboard_keypress(
|
||||
KeyboardKey(key=service_call.data[CONF_KEY])
|
||||
)
|
||||
@@ -342,7 +338,9 @@ async def async_setup_entry(
|
||||
|
||||
async def handle_send_text(service_call: ServiceCall) -> ServiceResponse:
|
||||
"""Handle the send_keypress service call."""
|
||||
coordinator = _get_coordinator(hass, service_call.data[CONF_BRIDGE])
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
service_call.data[CONF_BRIDGE]
|
||||
]
|
||||
response = await coordinator.websocket_client.keyboard_text(
|
||||
KeyboardText(text=service_call.data[CONF_TEXT])
|
||||
)
|
||||
@@ -448,27 +446,33 @@ async def async_setup_entry(
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(
|
||||
hass: HomeAssistant, entry: SystemBridgeConfigEntry
|
||||
) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> 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 = entry.runtime_data
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
entry.entry_id
|
||||
]
|
||||
|
||||
# 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: SystemBridgeConfigEntry
|
||||
) -> None:
|
||||
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Reload the config entry when it changed."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
@@ -10,11 +10,13 @@ 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 .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .data import SystemBridgeData
|
||||
from .entity import SystemBridgeEntity
|
||||
|
||||
@@ -62,11 +64,11 @@ BATTERY_BINARY_SENSOR_TYPES: tuple[SystemBridgeBinarySensorEntityDescription, ..
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up System Bridge binary sensor based on a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
entities = [
|
||||
SystemBridgeBinarySensor(coordinator, description, entry.data[CONF_PORT])
|
||||
|
||||
@@ -36,20 +36,18 @@ 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: SystemBridgeConfigEntry
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
LOGGER: logging.Logger,
|
||||
*,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize global System Bridge data updater."""
|
||||
self.title = entry.title
|
||||
|
||||
@@ -15,11 +15,13 @@ 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 .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .data import SystemBridgeData
|
||||
from .entity import SystemBridgeEntity
|
||||
|
||||
@@ -62,11 +64,11 @@ MEDIA_PLAYER_DESCRIPTION: Final[MediaPlayerEntityDescription] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up System Bridge media players based on a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
data = coordinator.data
|
||||
|
||||
if data.media is not None:
|
||||
|
||||
@@ -15,22 +15,12 @@ from homeassistant.components.media_source import (
|
||||
MediaSourceItem,
|
||||
PlayMedia,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
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
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
|
||||
|
||||
async def async_get_media_source(hass: HomeAssistant) -> MediaSource:
|
||||
@@ -56,7 +46,9 @@ class SystemBridgeSource(MediaSource):
|
||||
) -> PlayMedia:
|
||||
"""Resolve media to a url."""
|
||||
entry_id, path, mime_type = item.identifier.split("~~", 2)
|
||||
entry = _get_loaded_entry(self.hass, entry_id)
|
||||
entry = self.hass.config_entries.async_get_entry(entry_id)
|
||||
if entry is None:
|
||||
raise ValueError("Invalid entry")
|
||||
path_split = path.split("/", 1)
|
||||
return PlayMedia(
|
||||
f"{_build_base_url(entry)}&base={path_split[0]}&path={path_split[1]}",
|
||||
@@ -72,14 +64,21 @@ class SystemBridgeSource(MediaSource):
|
||||
return self._build_bridges()
|
||||
|
||||
if "~~" not in item.identifier:
|
||||
entry = _get_loaded_entry(self.hass, item.identifier)
|
||||
coordinator = entry.runtime_data
|
||||
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
|
||||
)
|
||||
directories = await coordinator.websocket_client.get_directories()
|
||||
return _build_root_paths(entry, directories)
|
||||
|
||||
entry_id, path = item.identifier.split("~~", 1)
|
||||
entry = _get_loaded_entry(self.hass, entry_id)
|
||||
coordinator = entry.runtime_data
|
||||
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)
|
||||
|
||||
path_split = path.split("/", 1)
|
||||
|
||||
@@ -124,7 +123,7 @@ class SystemBridgeSource(MediaSource):
|
||||
|
||||
|
||||
def _build_base_url(
|
||||
entry: SystemBridgeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
) -> str:
|
||||
"""Build base url for System Bridge media."""
|
||||
return (
|
||||
@@ -134,7 +133,7 @@ def _build_base_url(
|
||||
|
||||
|
||||
def _build_root_paths(
|
||||
entry: SystemBridgeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
media_directories: list[MediaDirectory],
|
||||
) -> BrowseMediaSource:
|
||||
"""Build base categories for System Bridge media."""
|
||||
@@ -165,7 +164,7 @@ def _build_root_paths(
|
||||
|
||||
|
||||
def _build_media_items(
|
||||
entry: SystemBridgeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
media_files: MediaFiles,
|
||||
path: str,
|
||||
identifier: str,
|
||||
|
||||
@@ -17,7 +17,8 @@ from homeassistant.const import ATTR_ICON, CONF_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -36,13 +37,11 @@ async def async_get_service(
|
||||
if discovery_info is None:
|
||||
return None
|
||||
|
||||
entry: SystemBridgeConfigEntry | None = hass.config_entries.async_get_entry(
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
discovery_info[CONF_ENTITY_ID]
|
||||
)
|
||||
if entry is None:
|
||||
return None
|
||||
]
|
||||
|
||||
return SystemBridgeNotificationService(entry.runtime_data)
|
||||
return SystemBridgeNotificationService(coordinator)
|
||||
|
||||
|
||||
class SystemBridgeNotificationService(BaseNotificationService):
|
||||
|
||||
@@ -17,6 +17,7 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_PORT,
|
||||
PERCENTAGE,
|
||||
@@ -32,7 +33,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import UNDEFINED, StateType
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .data import SystemBridgeData
|
||||
from .entity import SystemBridgeEntity
|
||||
|
||||
@@ -362,11 +364,11 @@ BATTERY_SENSOR_TYPES: tuple[SystemBridgeSensorEntityDescription, ...] = (
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up System Bridge sensor based on a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
entities = [
|
||||
SystemBridgeSensor(coordinator, description, entry.data[CONF_PORT])
|
||||
|
||||
@@ -3,21 +3,23 @@
|
||||
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 .coordinator import SystemBridgeConfigEntry, SystemBridgeDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SystemBridgeDataUpdateCoordinator
|
||||
from .entity import SystemBridgeEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SystemBridgeConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up System Bridge update based on a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
|
||||
@@ -12,9 +12,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
from .const import ATTR_BOOT_TIME, ATTR_LOAD, ROUTER_DEFAULT_HOST
|
||||
|
||||
type VilfoConfigEntry = ConfigEntry[VilfoRouterData]
|
||||
from .const import ATTR_BOOT_TIME, ATTR_LOAD, DOMAIN, ROUTER_DEFAULT_HOST
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
@@ -23,7 +21,7 @@ DEFAULT_SCAN_INTERVAL = timedelta(seconds=30)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: VilfoConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Vilfo Router from a config entry."""
|
||||
host = entry.data[CONF_HOST]
|
||||
access_token = entry.data[CONF_ACCESS_TOKEN]
|
||||
@@ -35,16 +33,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: VilfoConfigEntry) -> boo
|
||||
if not vilfo_router.available:
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
entry.runtime_data = vilfo_router
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = vilfo_router
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: VilfoConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
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: VilfoConfigEntry,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Add Vilfo Router entities from a config_entry."""
|
||||
vilfo = config_entry.runtime_data
|
||||
vilfo = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
entities = [VilfoRouterSensor(vilfo, description) for description in SENSOR_TYPES]
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ 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
|
||||
|
||||
@@ -279,41 +278,6 @@ 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,8 +587,6 @@ 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,
|
||||
}
|
||||
],
|
||||
@@ -747,8 +745,6 @@ 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,
|
||||
},
|
||||
{
|
||||
@@ -759,8 +755,6 @@ 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,
|
||||
},
|
||||
],
|
||||
@@ -895,8 +889,6 @@ 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,
|
||||
}
|
||||
],
|
||||
@@ -950,59 +942,6 @@ 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(
|
||||
@@ -1585,8 +1524,6 @@ 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,
|
||||
}
|
||||
|
||||
@@ -1634,62 +1571,10 @@ 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([
|
||||
<DoorbellEventType.RING: 'ring'>,
|
||||
'ding',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
@@ -47,7 +47,7 @@
|
||||
'device_class': 'doorbell',
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
<DoorbellEventType.RING: 'ring'>,
|
||||
'ding',
|
||||
]),
|
||||
'friendly_name': 'Front Door Ding',
|
||||
}),
|
||||
@@ -187,7 +187,7 @@
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'event_types': list([
|
||||
<DoorbellEventType.RING: 'ring'>,
|
||||
'ding',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
@@ -227,7 +227,7 @@
|
||||
'device_class': 'doorbell',
|
||||
'event_type': None,
|
||||
'event_types': list([
|
||||
<DoorbellEventType.RING: 'ring'>,
|
||||
'ding',
|
||||
]),
|
||||
'friendly_name': 'Ingress Ding',
|
||||
}),
|
||||
|
||||
@@ -9,7 +9,6 @@ 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
|
||||
@@ -36,38 +35,26 @@ async def test_states(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("device_id", "device_name", "alert_kind", "device_class", "event_type"),
|
||||
("device_id", "device_name", "alert_kind", "device_class"),
|
||||
[
|
||||
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",
|
||||
DoorbellEventType.RING,
|
||||
id="front_door_ding",
|
||||
FRONT_DOOR_DEVICE_ID, "front_door", "ding", "doorbell", id="front_door_ding"
|
||||
),
|
||||
pytest.param(
|
||||
INGRESS_DEVICE_ID,
|
||||
"ingress",
|
||||
"ding",
|
||||
"doorbell",
|
||||
DoorbellEventType.RING,
|
||||
id="ingress_ding",
|
||||
INGRESS_DEVICE_ID, "ingress", "ding", "doorbell", id="ingress_ding"
|
||||
),
|
||||
pytest.param(
|
||||
INGRESS_DEVICE_ID,
|
||||
"ingress",
|
||||
"intercom_unlock",
|
||||
"button",
|
||||
"intercom_unlock",
|
||||
id="ingress_unlock",
|
||||
),
|
||||
],
|
||||
@@ -81,7 +68,6 @@ async def test_event(
|
||||
device_name: str,
|
||||
alert_kind: str,
|
||||
device_class: str,
|
||||
event_type: str,
|
||||
) -> None:
|
||||
"""Test the Ring event platforms."""
|
||||
|
||||
@@ -110,4 +96,3 @@ 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,10 +18,6 @@ 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,
|
||||
@@ -298,101 +294,3 @@ 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,6 +4,7 @@ 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
|
||||
@@ -62,7 +63,7 @@ async def test_async_image(
|
||||
entry = await configure_integration(hass)
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
cloud_data = entry.runtime_data
|
||||
cloud_data = hass.data[DOMAIN][entry.entry_id]
|
||||
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.const import DOMAIN
|
||||
from homeassistant.components.vilfo import DOMAIN
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
Reference in New Issue
Block a user