Bump aionotion
to 2023.04.2 to address imminent API change (#91786)
* Bump `aionotion` to 2023.04.0 * Bump `aionotion` to 2023.04.2 to address imminent API change * Clean migration * Reduce blast area * Fix tests * Better naming
This commit is contained in:
parent
4de124cdd5
commit
c6d846453d
15 changed files with 410 additions and 314 deletions
|
@ -2,13 +2,17 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from dataclasses import dataclass, field, fields
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
from aionotion import async_get_client
|
from aionotion import async_get_client
|
||||||
|
from aionotion.bridge.models import Bridge
|
||||||
from aionotion.errors import InvalidCredentialsError, NotionError
|
from aionotion.errors import InvalidCredentialsError, NotionError
|
||||||
|
from aionotion.sensor.models import Listener, ListenerKind, Sensor
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||||
|
@ -18,6 +22,7 @@ from homeassistant.helpers import (
|
||||||
aiohttp_client,
|
aiohttp_client,
|
||||||
config_validation as cv,
|
config_validation as cv,
|
||||||
device_registry as dr,
|
device_registry as dr,
|
||||||
|
entity_registry as er,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.entity import DeviceInfo, EntityDescription
|
from homeassistant.helpers.entity import DeviceInfo, EntityDescription
|
||||||
from homeassistant.helpers.update_coordinator import (
|
from homeassistant.helpers.update_coordinator import (
|
||||||
|
@ -26,7 +31,20 @@ from homeassistant.helpers.update_coordinator import (
|
||||||
UpdateFailed,
|
UpdateFailed,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .const import DOMAIN, LOGGER
|
from .const import (
|
||||||
|
DOMAIN,
|
||||||
|
LOGGER,
|
||||||
|
SENSOR_BATTERY,
|
||||||
|
SENSOR_DOOR,
|
||||||
|
SENSOR_GARAGE_DOOR,
|
||||||
|
SENSOR_LEAK,
|
||||||
|
SENSOR_MISSING,
|
||||||
|
SENSOR_SAFE,
|
||||||
|
SENSOR_SLIDING,
|
||||||
|
SENSOR_SMOKE_CO,
|
||||||
|
SENSOR_TEMPERATURE,
|
||||||
|
SENSOR_WINDOW_HINGED,
|
||||||
|
)
|
||||||
|
|
||||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||||
|
|
||||||
|
@ -37,6 +55,51 @@ DEFAULT_SCAN_INTERVAL = timedelta(minutes=1)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
||||||
|
|
||||||
|
# Define a map of old-API task types to new-API listener types:
|
||||||
|
TASK_TYPE_TO_LISTENER_MAP: dict[str, ListenerKind] = {
|
||||||
|
SENSOR_BATTERY: ListenerKind.BATTERY,
|
||||||
|
SENSOR_DOOR: ListenerKind.DOOR,
|
||||||
|
SENSOR_GARAGE_DOOR: ListenerKind.GARAGE_DOOR,
|
||||||
|
SENSOR_LEAK: ListenerKind.LEAK_STATUS,
|
||||||
|
SENSOR_MISSING: ListenerKind.CONNECTED,
|
||||||
|
SENSOR_SAFE: ListenerKind.SAFE,
|
||||||
|
SENSOR_SLIDING: ListenerKind.SLIDING_DOOR_OR_WINDOW,
|
||||||
|
SENSOR_SMOKE_CO: ListenerKind.SMOKE,
|
||||||
|
SENSOR_TEMPERATURE: ListenerKind.TEMPERATURE,
|
||||||
|
SENSOR_WINDOW_HINGED: ListenerKind.HINGED_WINDOW,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def is_uuid(value: str) -> bool:
|
||||||
|
"""Return whether a string is a valid UUID."""
|
||||||
|
try:
|
||||||
|
UUID(value)
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NotionData:
|
||||||
|
"""Define a manager class for Notion data."""
|
||||||
|
|
||||||
|
# Define a dict of bridges, indexed by bridge ID (an integer):
|
||||||
|
bridges: dict[int, Bridge] = field(default_factory=dict)
|
||||||
|
|
||||||
|
# Define a dict of listeners, indexed by listener UUID (a string):
|
||||||
|
listeners: dict[str, Listener] = field(default_factory=dict)
|
||||||
|
|
||||||
|
# Define a dict of sensors, indexed by sensor UUID (a string):
|
||||||
|
sensors: dict[str, Sensor] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def asdict(self) -> dict[str, Any]:
|
||||||
|
"""Represent this dataclass (and its Pydantic contents) as a dict."""
|
||||||
|
return {
|
||||||
|
field.name: [obj.dict() for obj in getattr(self, field.name).values()]
|
||||||
|
for field in fields(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up Notion as a config entry."""
|
"""Set up Notion as a config entry."""
|
||||||
|
@ -56,13 +119,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
except NotionError as err:
|
except NotionError as err:
|
||||||
raise ConfigEntryNotReady("Config entry failed to load") from err
|
raise ConfigEntryNotReady("Config entry failed to load") from err
|
||||||
|
|
||||||
async def async_update() -> dict[str, dict[str, Any]]:
|
async def async_update() -> NotionData:
|
||||||
"""Get the latest data from the Notion API."""
|
"""Get the latest data from the Notion API."""
|
||||||
data: dict[str, dict[str, Any]] = {"bridges": {}, "sensors": {}, "tasks": {}}
|
data = NotionData()
|
||||||
tasks = {
|
tasks = {
|
||||||
"bridges": client.bridge.async_all(),
|
"bridges": client.bridge.async_all(),
|
||||||
|
"listeners": client.sensor.async_listeners(),
|
||||||
"sensors": client.sensor.async_all(),
|
"sensors": client.sensor.async_all(),
|
||||||
"tasks": client.task.async_all(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = await asyncio.gather(*tasks.values(), return_exceptions=True)
|
results = await asyncio.gather(*tasks.values(), return_exceptions=True)
|
||||||
|
@ -83,10 +146,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
) from result
|
) from result
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
if attr == "bridges" and item["id"] not in data["bridges"]:
|
if attr == "bridges":
|
||||||
# If a new bridge is discovered, register it:
|
# If a new bridge is discovered, register it:
|
||||||
_async_register_new_bridge(hass, item, entry)
|
if item.id not in data.bridges:
|
||||||
data[attr][item["id"]] = item
|
_async_register_new_bridge(hass, item, entry)
|
||||||
|
data.bridges[item.id] = item
|
||||||
|
elif attr == "listeners":
|
||||||
|
data.listeners[item.id] = item
|
||||||
|
else:
|
||||||
|
data.sensors[item.uuid] = item
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@ -102,6 +170,36 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_migrate_entity_entry(entry: er.RegistryEntry) -> dict[str, Any] | None:
|
||||||
|
"""Migrate Notion entity entries.
|
||||||
|
|
||||||
|
This migration focuses on unique IDs, which have changed because of a Notion API
|
||||||
|
change:
|
||||||
|
|
||||||
|
Old Format: <sensor_id>_<task_type>
|
||||||
|
New Format: <listener_uuid>
|
||||||
|
"""
|
||||||
|
if is_uuid(entry.unique_id):
|
||||||
|
# If the unique ID is already a UUID, we don't need to migrate it:
|
||||||
|
return None
|
||||||
|
|
||||||
|
sensor_id_str, task_type = entry.unique_id.split("_", 1)
|
||||||
|
sensor = next(
|
||||||
|
sensor
|
||||||
|
for sensor in coordinator.data.sensors.values()
|
||||||
|
if sensor.id == int(sensor_id_str)
|
||||||
|
)
|
||||||
|
listener = next(
|
||||||
|
listener
|
||||||
|
for listener in coordinator.data.listeners.values()
|
||||||
|
if listener.sensor_id == sensor.uuid
|
||||||
|
and listener.listener_kind == TASK_TYPE_TO_LISTENER_MAP[task_type]
|
||||||
|
)
|
||||||
|
|
||||||
|
return {"new_unique_id": listener.id}
|
||||||
|
|
||||||
|
await er.async_migrate_entries(hass, entry.entry_id, async_migrate_entity_entry)
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -118,22 +216,22 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_register_new_bridge(
|
def _async_register_new_bridge(
|
||||||
hass: HomeAssistant, bridge: dict, entry: ConfigEntry
|
hass: HomeAssistant, bridge: Bridge, entry: ConfigEntry
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Register a new bridge."""
|
"""Register a new bridge."""
|
||||||
if name := bridge["name"]:
|
if name := bridge.name:
|
||||||
bridge_name = name.capitalize()
|
bridge_name = name.capitalize()
|
||||||
else:
|
else:
|
||||||
bridge_name = bridge["id"]
|
bridge_name = str(bridge.id)
|
||||||
|
|
||||||
device_registry = dr.async_get(hass)
|
device_registry = dr.async_get(hass)
|
||||||
device_registry.async_get_or_create(
|
device_registry.async_get_or_create(
|
||||||
config_entry_id=entry.entry_id,
|
config_entry_id=entry.entry_id,
|
||||||
identifiers={(DOMAIN, bridge["hardware_id"])},
|
identifiers={(DOMAIN, bridge.hardware_id)},
|
||||||
manufacturer="Silicon Labs",
|
manufacturer="Silicon Labs",
|
||||||
model=bridge["hardware_revision"],
|
model=str(bridge.hardware_revision),
|
||||||
name=bridge_name,
|
name=bridge_name,
|
||||||
sw_version=bridge["firmware_version"]["wifi"],
|
sw_version=bridge.firmware_version.wifi,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,7 +243,7 @@ class NotionEntity(CoordinatorEntity):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: DataUpdateCoordinator,
|
coordinator: DataUpdateCoordinator,
|
||||||
task_id: str,
|
listener_id: str,
|
||||||
sensor_id: str,
|
sensor_id: str,
|
||||||
bridge_id: str,
|
bridge_id: str,
|
||||||
system_id: str,
|
system_id: str,
|
||||||
|
@ -154,25 +252,23 @@ class NotionEntity(CoordinatorEntity):
|
||||||
"""Initialize the entity."""
|
"""Initialize the entity."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
|
|
||||||
bridge = self.coordinator.data["bridges"].get(bridge_id, {})
|
bridge = self.coordinator.data.bridges.get(bridge_id, {})
|
||||||
sensor = self.coordinator.data["sensors"][sensor_id]
|
sensor = self.coordinator.data.sensors[sensor_id]
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, sensor["hardware_id"])},
|
identifiers={(DOMAIN, sensor.hardware_id)},
|
||||||
manufacturer="Silicon Labs",
|
manufacturer="Silicon Labs",
|
||||||
model=sensor["hardware_revision"],
|
model=sensor.hardware_revision,
|
||||||
name=str(sensor["name"]).capitalize(),
|
name=str(sensor.name).capitalize(),
|
||||||
sw_version=sensor["firmware_version"],
|
sw_version=sensor.firmware_version,
|
||||||
via_device=(DOMAIN, bridge.get("hardware_id")),
|
via_device=(DOMAIN, bridge.hardware_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
self._attr_extra_state_attributes = {}
|
self._attr_extra_state_attributes = {}
|
||||||
self._attr_unique_id = (
|
self._attr_unique_id = listener_id
|
||||||
f'{sensor_id}_{coordinator.data["tasks"][task_id]["task_type"]}'
|
|
||||||
)
|
|
||||||
self._bridge_id = bridge_id
|
self._bridge_id = bridge_id
|
||||||
|
self._listener_id = listener_id
|
||||||
self._sensor_id = sensor_id
|
self._sensor_id = sensor_id
|
||||||
self._system_id = system_id
|
self._system_id = system_id
|
||||||
self._task_id = task_id
|
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -180,7 +276,7 @@ class NotionEntity(CoordinatorEntity):
|
||||||
"""Return True if entity is available."""
|
"""Return True if entity is available."""
|
||||||
return (
|
return (
|
||||||
self.coordinator.last_update_success
|
self.coordinator.last_update_success
|
||||||
and self._task_id in self.coordinator.data["tasks"]
|
and self._listener_id in self.coordinator.data.listeners
|
||||||
)
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -189,27 +285,23 @@ class NotionEntity(CoordinatorEntity):
|
||||||
|
|
||||||
Sensors can move to other bridges based on signal strength, etc.
|
Sensors can move to other bridges based on signal strength, etc.
|
||||||
"""
|
"""
|
||||||
sensor = self.coordinator.data["sensors"][self._sensor_id]
|
sensor = self.coordinator.data.sensors[self._sensor_id]
|
||||||
|
|
||||||
# If the sensor's bridge ID is the same as what we had before or if it points
|
# If the sensor's bridge ID is the same as what we had before or if it points
|
||||||
# to a bridge that doesn't exist (which can happen due to a Notion API bug),
|
# to a bridge that doesn't exist (which can happen due to a Notion API bug),
|
||||||
# return immediately:
|
# return immediately:
|
||||||
if (
|
if (
|
||||||
self._bridge_id == sensor["bridge"]["id"]
|
self._bridge_id == sensor.bridge.id
|
||||||
or sensor["bridge"]["id"] not in self.coordinator.data["bridges"]
|
or sensor.bridge.id not in self.coordinator.data.bridges
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
self._bridge_id = sensor["bridge"]["id"]
|
self._bridge_id = sensor.bridge.id
|
||||||
|
|
||||||
device_registry = dr.async_get(self.hass)
|
device_registry = dr.async_get(self.hass)
|
||||||
this_device = device_registry.async_get_device(
|
this_device = device_registry.async_get_device({(DOMAIN, sensor.hardware_id)})
|
||||||
{(DOMAIN, sensor["hardware_id"])}
|
bridge = self.coordinator.data.bridges[self._bridge_id]
|
||||||
)
|
bridge_device = device_registry.async_get_device({(DOMAIN, bridge.hardware_id)})
|
||||||
bridge = self.coordinator.data["bridges"][self._bridge_id]
|
|
||||||
bridge_device = device_registry.async_get_device(
|
|
||||||
{(DOMAIN, bridge["hardware_id"])}
|
|
||||||
)
|
|
||||||
|
|
||||||
if not bridge_device or not this_device:
|
if not bridge_device or not this_device:
|
||||||
return
|
return
|
||||||
|
@ -226,7 +318,7 @@ class NotionEntity(CoordinatorEntity):
|
||||||
@callback
|
@callback
|
||||||
def _handle_coordinator_update(self) -> None:
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Respond to a DataUpdateCoordinator update."""
|
"""Respond to a DataUpdateCoordinator update."""
|
||||||
if self._task_id in self.coordinator.data["tasks"]:
|
if self._listener_id in self.coordinator.data.listeners:
|
||||||
self._async_update_bridge_id()
|
self._async_update_bridge_id()
|
||||||
self._async_update_from_latest_data()
|
self._async_update_from_latest_data()
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
|
from aionotion.sensor.models import ListenerKind
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorDeviceClass,
|
BinarySensorDeviceClass,
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
|
@ -26,9 +28,9 @@ from .const import (
|
||||||
SENSOR_SAFE,
|
SENSOR_SAFE,
|
||||||
SENSOR_SLIDING,
|
SENSOR_SLIDING,
|
||||||
SENSOR_SMOKE_CO,
|
SENSOR_SMOKE_CO,
|
||||||
SENSOR_WINDOW_HINGED_HORIZONTAL,
|
SENSOR_WINDOW_HINGED,
|
||||||
SENSOR_WINDOW_HINGED_VERTICAL,
|
|
||||||
)
|
)
|
||||||
|
from .model import NotionEntityDescriptionMixin
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -40,7 +42,9 @@ class NotionBinarySensorDescriptionMixin:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NotionBinarySensorDescription(
|
class NotionBinarySensorDescription(
|
||||||
BinarySensorEntityDescription, NotionBinarySensorDescriptionMixin
|
BinarySensorEntityDescription,
|
||||||
|
NotionBinarySensorDescriptionMixin,
|
||||||
|
NotionEntityDescriptionMixin,
|
||||||
):
|
):
|
||||||
"""Describe a Notion binary sensor."""
|
"""Describe a Notion binary sensor."""
|
||||||
|
|
||||||
|
@ -51,24 +55,28 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
||||||
name="Low battery",
|
name="Low battery",
|
||||||
device_class=BinarySensorDeviceClass.BATTERY,
|
device_class=BinarySensorDeviceClass.BATTERY,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
listener_kind=ListenerKind.BATTERY,
|
||||||
on_state="critical",
|
on_state="critical",
|
||||||
),
|
),
|
||||||
NotionBinarySensorDescription(
|
NotionBinarySensorDescription(
|
||||||
key=SENSOR_DOOR,
|
key=SENSOR_DOOR,
|
||||||
name="Door",
|
name="Door",
|
||||||
device_class=BinarySensorDeviceClass.DOOR,
|
device_class=BinarySensorDeviceClass.DOOR,
|
||||||
|
listener_kind=ListenerKind.DOOR,
|
||||||
on_state="open",
|
on_state="open",
|
||||||
),
|
),
|
||||||
NotionBinarySensorDescription(
|
NotionBinarySensorDescription(
|
||||||
key=SENSOR_GARAGE_DOOR,
|
key=SENSOR_GARAGE_DOOR,
|
||||||
name="Garage door",
|
name="Garage door",
|
||||||
device_class=BinarySensorDeviceClass.GARAGE_DOOR,
|
device_class=BinarySensorDeviceClass.GARAGE_DOOR,
|
||||||
|
listener_kind=ListenerKind.GARAGE_DOOR,
|
||||||
on_state="open",
|
on_state="open",
|
||||||
),
|
),
|
||||||
NotionBinarySensorDescription(
|
NotionBinarySensorDescription(
|
||||||
key=SENSOR_LEAK,
|
key=SENSOR_LEAK,
|
||||||
name="Leak detector",
|
name="Leak detector",
|
||||||
device_class=BinarySensorDeviceClass.MOISTURE,
|
device_class=BinarySensorDeviceClass.MOISTURE,
|
||||||
|
listener_kind=ListenerKind.LEAK_STATUS,
|
||||||
on_state="leak",
|
on_state="leak",
|
||||||
),
|
),
|
||||||
NotionBinarySensorDescription(
|
NotionBinarySensorDescription(
|
||||||
|
@ -76,36 +84,34 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
||||||
name="Missing",
|
name="Missing",
|
||||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
listener_kind=ListenerKind.CONNECTED,
|
||||||
on_state="not_missing",
|
on_state="not_missing",
|
||||||
),
|
),
|
||||||
NotionBinarySensorDescription(
|
NotionBinarySensorDescription(
|
||||||
key=SENSOR_SAFE,
|
key=SENSOR_SAFE,
|
||||||
name="Safe",
|
name="Safe",
|
||||||
device_class=BinarySensorDeviceClass.DOOR,
|
device_class=BinarySensorDeviceClass.DOOR,
|
||||||
|
listener_kind=ListenerKind.SAFE,
|
||||||
on_state="open",
|
on_state="open",
|
||||||
),
|
),
|
||||||
NotionBinarySensorDescription(
|
NotionBinarySensorDescription(
|
||||||
key=SENSOR_SLIDING,
|
key=SENSOR_SLIDING,
|
||||||
name="Sliding door/window",
|
name="Sliding door/window",
|
||||||
device_class=BinarySensorDeviceClass.DOOR,
|
device_class=BinarySensorDeviceClass.DOOR,
|
||||||
|
listener_kind=ListenerKind.SLIDING_DOOR_OR_WINDOW,
|
||||||
on_state="open",
|
on_state="open",
|
||||||
),
|
),
|
||||||
NotionBinarySensorDescription(
|
NotionBinarySensorDescription(
|
||||||
key=SENSOR_SMOKE_CO,
|
key=SENSOR_SMOKE_CO,
|
||||||
name="Smoke/Carbon monoxide detector",
|
name="Smoke/Carbon monoxide detector",
|
||||||
device_class=BinarySensorDeviceClass.SMOKE,
|
device_class=BinarySensorDeviceClass.SMOKE,
|
||||||
|
listener_kind=ListenerKind.SMOKE,
|
||||||
on_state="alarm",
|
on_state="alarm",
|
||||||
),
|
),
|
||||||
NotionBinarySensorDescription(
|
NotionBinarySensorDescription(
|
||||||
key=SENSOR_WINDOW_HINGED_HORIZONTAL,
|
key=SENSOR_WINDOW_HINGED,
|
||||||
name="Hinged window",
|
name="Hinged window",
|
||||||
device_class=BinarySensorDeviceClass.WINDOW,
|
listener_kind=ListenerKind.HINGED_WINDOW,
|
||||||
on_state="open",
|
|
||||||
),
|
|
||||||
NotionBinarySensorDescription(
|
|
||||||
key=SENSOR_WINDOW_HINGED_VERTICAL,
|
|
||||||
name="Hinged window",
|
|
||||||
device_class=BinarySensorDeviceClass.WINDOW,
|
|
||||||
on_state="open",
|
on_state="open",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -121,16 +127,16 @@ async def async_setup_entry(
|
||||||
[
|
[
|
||||||
NotionBinarySensor(
|
NotionBinarySensor(
|
||||||
coordinator,
|
coordinator,
|
||||||
task_id,
|
listener_id,
|
||||||
sensor["id"],
|
sensor.uuid,
|
||||||
sensor["bridge"]["id"],
|
sensor.bridge.id,
|
||||||
sensor["system_id"],
|
sensor.system_id,
|
||||||
description,
|
description,
|
||||||
)
|
)
|
||||||
for task_id, task in coordinator.data["tasks"].items()
|
for listener_id, listener in coordinator.data.listeners.items()
|
||||||
for description in BINARY_SENSOR_DESCRIPTIONS
|
for description in BINARY_SENSOR_DESCRIPTIONS
|
||||||
if description.key == task["task_type"]
|
if description.listener_kind == listener.listener_kind
|
||||||
and (sensor := coordinator.data["sensors"][task["sensor_id"]])
|
and (sensor := coordinator.data.sensors[listener.sensor_id])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -143,14 +149,14 @@ class NotionBinarySensor(NotionEntity, BinarySensorEntity):
|
||||||
@callback
|
@callback
|
||||||
def _async_update_from_latest_data(self) -> None:
|
def _async_update_from_latest_data(self) -> None:
|
||||||
"""Fetch new state data for the sensor."""
|
"""Fetch new state data for the sensor."""
|
||||||
task = self.coordinator.data["tasks"][self._task_id]
|
listener = self.coordinator.data.listeners[self._listener_id]
|
||||||
|
|
||||||
if "value" in task["status"]:
|
if listener.status.trigger_value:
|
||||||
state = task["status"]["value"]
|
state = listener.status.trigger_value
|
||||||
elif task["status"].get("insights", {}).get("primary"):
|
elif listener.insights.primary.value:
|
||||||
state = task["status"]["insights"]["primary"]["to_state"]
|
state = listener.insights.primary.value
|
||||||
else:
|
else:
|
||||||
LOGGER.warning("Unknown data payload: %s", task["status"])
|
LOGGER.warning("Unknown listener structure: %s", listener)
|
||||||
state = None
|
state = None
|
||||||
|
|
||||||
self._attr_is_on = self.entity_description.on_state == state
|
self._attr_is_on = self.entity_description.on_state == state
|
||||||
|
|
|
@ -13,5 +13,4 @@ SENSOR_SAFE = "safe"
|
||||||
SENSOR_SLIDING = "sliding"
|
SENSOR_SLIDING = "sliding"
|
||||||
SENSOR_SMOKE_CO = "alarm"
|
SENSOR_SMOKE_CO = "alarm"
|
||||||
SENSOR_TEMPERATURE = "temperature"
|
SENSOR_TEMPERATURE = "temperature"
|
||||||
SENSOR_WINDOW_HINGED_HORIZONTAL = "window_hinged_horizontal"
|
SENSOR_WINDOW_HINGED = "window_hinged"
|
||||||
SENSOR_WINDOW_HINGED_VERTICAL = "window_hinged_vertical"
|
|
||||||
|
|
|
@ -35,7 +35,10 @@ async def async_get_config_entry_diagnostics(
|
||||||
"""Return diagnostics for a config entry."""
|
"""Return diagnostics for a config entry."""
|
||||||
coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
return {
|
return async_redact_data(
|
||||||
"entry": async_redact_data(entry.as_dict(), TO_REDACT),
|
{
|
||||||
"data": async_redact_data(coordinator.data, TO_REDACT),
|
"entry": entry.as_dict(),
|
||||||
}
|
"data": coordinator.data.asdict(),
|
||||||
|
},
|
||||||
|
TO_REDACT,
|
||||||
|
)
|
||||||
|
|
|
@ -7,5 +7,5 @@
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["aionotion"],
|
"loggers": ["aionotion"],
|
||||||
"requirements": ["aionotion==3.0.2"]
|
"requirements": ["aionotion==2023.04.2"]
|
||||||
}
|
}
|
||||||
|
|
11
homeassistant/components/notion/model.py
Normal file
11
homeassistant/components/notion/model.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
"""Define Notion model mixins."""
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from aionotion.sensor.models import ListenerKind
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NotionEntityDescriptionMixin:
|
||||||
|
"""Define an description mixin Notion entities."""
|
||||||
|
|
||||||
|
listener_kind: ListenerKind
|
|
@ -1,4 +1,8 @@
|
||||||
"""Support for Notion sensors."""
|
"""Support for Notion sensors."""
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from aionotion.sensor.models import ListenerKind
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
|
@ -12,14 +16,22 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import NotionEntity
|
from . import NotionEntity
|
||||||
from .const import DOMAIN, LOGGER, SENSOR_TEMPERATURE
|
from .const import DOMAIN, LOGGER, SENSOR_TEMPERATURE
|
||||||
|
from .model import NotionEntityDescriptionMixin
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NotionSensorDescription(SensorEntityDescription, NotionEntityDescriptionMixin):
|
||||||
|
"""Describe a Notion sensor."""
|
||||||
|
|
||||||
|
|
||||||
SENSOR_DESCRIPTIONS = (
|
SENSOR_DESCRIPTIONS = (
|
||||||
SensorEntityDescription(
|
NotionSensorDescription(
|
||||||
key=SENSOR_TEMPERATURE,
|
key=SENSOR_TEMPERATURE,
|
||||||
name="Temperature",
|
name="Temperature",
|
||||||
device_class=SensorDeviceClass.TEMPERATURE,
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
listener_kind=ListenerKind.TEMPERATURE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,16 +46,16 @@ async def async_setup_entry(
|
||||||
[
|
[
|
||||||
NotionSensor(
|
NotionSensor(
|
||||||
coordinator,
|
coordinator,
|
||||||
task_id,
|
listener_id,
|
||||||
sensor["id"],
|
sensor.uuid,
|
||||||
sensor["bridge"]["id"],
|
sensor.bridge.id,
|
||||||
sensor["system_id"],
|
sensor.system_id,
|
||||||
description,
|
description,
|
||||||
)
|
)
|
||||||
for task_id, task in coordinator.data["tasks"].items()
|
for listener_id, listener in coordinator.data.listeners.items()
|
||||||
for description in SENSOR_DESCRIPTIONS
|
for description in SENSOR_DESCRIPTIONS
|
||||||
if description.key == task["task_type"]
|
if description.listener_kind == listener.listener_kind
|
||||||
and (sensor := coordinator.data["sensors"][task["sensor_id"]])
|
and (sensor := coordinator.data.sensors[listener.sensor_id])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,13 +66,12 @@ class NotionSensor(NotionEntity, SensorEntity):
|
||||||
@callback
|
@callback
|
||||||
def _async_update_from_latest_data(self) -> None:
|
def _async_update_from_latest_data(self) -> None:
|
||||||
"""Fetch new state data for the sensor."""
|
"""Fetch new state data for the sensor."""
|
||||||
task = self.coordinator.data["tasks"][self._task_id]
|
listener = self.coordinator.data.listeners[self._listener_id]
|
||||||
|
|
||||||
if task["task_type"] == SENSOR_TEMPERATURE:
|
if listener.listener_kind == ListenerKind.TEMPERATURE:
|
||||||
self._attr_native_value = round(float(task["status"]["value"]), 1)
|
self._attr_native_value = round(listener.status.temperature, 1)
|
||||||
else:
|
else:
|
||||||
LOGGER.error(
|
LOGGER.error(
|
||||||
"Unknown task type: %s: %s",
|
"Unknown listener type for sensor %s",
|
||||||
self.coordinator.data["sensors"][self._sensor_id],
|
self.coordinator.data.sensors[self._sensor_id],
|
||||||
task["task_type"],
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -223,7 +223,7 @@ aionanoleaf==0.2.1
|
||||||
aionotify==0.2.0
|
aionotify==0.2.0
|
||||||
|
|
||||||
# homeassistant.components.notion
|
# homeassistant.components.notion
|
||||||
aionotion==3.0.2
|
aionotion==2023.04.2
|
||||||
|
|
||||||
# homeassistant.components.oncue
|
# homeassistant.components.oncue
|
||||||
aiooncue==0.3.4
|
aiooncue==0.3.4
|
||||||
|
|
|
@ -204,7 +204,7 @@ aiomusiccast==0.14.8
|
||||||
aionanoleaf==0.2.1
|
aionanoleaf==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.notion
|
# homeassistant.components.notion
|
||||||
aionotion==3.0.2
|
aionotion==2023.04.2
|
||||||
|
|
||||||
# homeassistant.components.oncue
|
# homeassistant.components.oncue
|
||||||
aiooncue==0.3.4
|
aiooncue==0.3.4
|
||||||
|
|
|
@ -3,10 +3,13 @@ from collections.abc import Generator
|
||||||
import json
|
import json
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
|
from aionotion.bridge.models import Bridge
|
||||||
|
from aionotion.sensor.models import Listener, Sensor
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.notion import DOMAIN
|
from homeassistant.components.notion import DOMAIN
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, load_fixture
|
from tests.common import MockConfigEntry, load_fixture
|
||||||
|
|
||||||
|
@ -24,17 +27,29 @@ def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="client")
|
@pytest.fixture(name="client")
|
||||||
def client_fixture(data_bridge, data_sensor, data_task):
|
def client_fixture(data_bridge, data_listener, data_sensor):
|
||||||
"""Define a fixture for an aionotion client."""
|
"""Define a fixture for an aionotion client."""
|
||||||
return Mock(
|
return Mock(
|
||||||
bridge=Mock(async_all=AsyncMock(return_value=data_bridge)),
|
bridge=Mock(
|
||||||
sensor=Mock(async_all=AsyncMock(return_value=data_sensor)),
|
async_all=AsyncMock(
|
||||||
task=Mock(async_all=AsyncMock(return_value=data_task)),
|
return_value=[Bridge.parse_obj(bridge) for bridge in data_bridge]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
sensor=Mock(
|
||||||
|
async_all=AsyncMock(
|
||||||
|
return_value=[Sensor.parse_obj(sensor) for sensor in data_sensor]
|
||||||
|
),
|
||||||
|
async_listeners=AsyncMock(
|
||||||
|
return_value=[
|
||||||
|
Listener.parse_obj(listener) for listener in data_listener
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="config_entry")
|
@pytest.fixture(name="config_entry")
|
||||||
def config_entry_fixture(hass, config):
|
def config_entry_fixture(hass: HomeAssistant, config):
|
||||||
"""Define a config entry fixture."""
|
"""Define a config entry fixture."""
|
||||||
entry = MockConfigEntry(domain=DOMAIN, unique_id=TEST_USERNAME, data=config)
|
entry = MockConfigEntry(domain=DOMAIN, unique_id=TEST_USERNAME, data=config)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
@ -56,18 +71,18 @@ def data_bridge_fixture():
|
||||||
return json.loads(load_fixture("bridge_data.json", "notion"))
|
return json.loads(load_fixture("bridge_data.json", "notion"))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="data_listener", scope="package")
|
||||||
|
def data_listener_fixture():
|
||||||
|
"""Define listener data."""
|
||||||
|
return json.loads(load_fixture("listener_data.json", "notion"))
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="data_sensor", scope="package")
|
@pytest.fixture(name="data_sensor", scope="package")
|
||||||
def data_sensor_fixture():
|
def data_sensor_fixture():
|
||||||
"""Define sensor data."""
|
"""Define sensor data."""
|
||||||
return json.loads(load_fixture("sensor_data.json", "notion"))
|
return json.loads(load_fixture("sensor_data.json", "notion"))
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="data_task", scope="package")
|
|
||||||
def data_task_fixture():
|
|
||||||
"""Define task data."""
|
|
||||||
return json.loads(load_fixture("task_data.json", "notion"))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="get_client")
|
@pytest.fixture(name="get_client")
|
||||||
def get_client_fixture(client):
|
def get_client_fixture(client):
|
||||||
"""Define a fixture to mock the async_get_client method."""
|
"""Define a fixture to mock the async_get_client method."""
|
||||||
|
@ -88,7 +103,7 @@ async def mock_aionotion_fixture(client):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="setup_config_entry")
|
@pytest.fixture(name="setup_config_entry")
|
||||||
async def setup_config_entry_fixture(hass, config_entry, mock_aionotion):
|
async def setup_config_entry_fixture(hass: HomeAssistant, config_entry, mock_aionotion):
|
||||||
"""Define a fixture to set up notion."""
|
"""Define a fixture to set up notion."""
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
|
@ -1,26 +1,50 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": 12345,
|
"id": 12345,
|
||||||
"name": null,
|
"name": "Bridge 1",
|
||||||
"mode": "home",
|
"mode": "home",
|
||||||
"hardware_id": "0x1234567890abcdef",
|
"hardware_id": "0x0000000000000000",
|
||||||
|
"hardware_revision": 4,
|
||||||
|
"firmware_version": {
|
||||||
|
"silabs": "1.1.2",
|
||||||
|
"wifi": "0.121.0",
|
||||||
|
"wifi_app": "3.3.0"
|
||||||
|
},
|
||||||
|
"missing_at": null,
|
||||||
|
"created_at": "2019-06-27T00:18:44.337Z",
|
||||||
|
"updated_at": "2023-03-19T03:20:16.061Z",
|
||||||
|
"system_id": 11111,
|
||||||
|
"firmware": {
|
||||||
|
"silabs": "1.1.2",
|
||||||
|
"wifi": "0.121.0",
|
||||||
|
"wifi_app": "3.3.0"
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"system": 11111
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 67890,
|
||||||
|
"name": "Bridge 2",
|
||||||
|
"mode": "home",
|
||||||
|
"hardware_id": "0x0000000000000000",
|
||||||
"hardware_revision": 4,
|
"hardware_revision": 4,
|
||||||
"firmware_version": {
|
"firmware_version": {
|
||||||
"wifi": "0.121.0",
|
"wifi": "0.121.0",
|
||||||
"wifi_app": "3.3.0",
|
"wifi_app": "3.3.0",
|
||||||
"silabs": "1.0.1"
|
"silabs": "1.1.2"
|
||||||
},
|
},
|
||||||
"missing_at": null,
|
"missing_at": null,
|
||||||
"created_at": "2019-04-30T01:43:50.497Z",
|
"created_at": "2019-04-30T01:43:50.497Z",
|
||||||
"updated_at": "2019-04-30T01:44:43.749Z",
|
"updated_at": "2023-01-02T19:09:58.251Z",
|
||||||
"system_id": 12345,
|
"system_id": 11111,
|
||||||
"firmware": {
|
"firmware": {
|
||||||
"wifi": "0.121.0",
|
"wifi": "0.121.0",
|
||||||
"wifi_app": "3.3.0",
|
"wifi_app": "3.3.0",
|
||||||
"silabs": "1.0.1"
|
"silabs": "1.1.2"
|
||||||
},
|
},
|
||||||
"links": {
|
"links": {
|
||||||
"system": 12345
|
"system": 11111
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
55
tests/components/notion/fixtures/listener_data.json
Normal file
55
tests/components/notion/fixtures/listener_data.json
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
|
"definition_id": 4,
|
||||||
|
"created_at": "2019-06-28T22:12:49.651Z",
|
||||||
|
"type": "sensor",
|
||||||
|
"model_version": "2.1",
|
||||||
|
"sensor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
|
"status": {
|
||||||
|
"trigger_value": "no_leak",
|
||||||
|
"data_received_at": "2022-03-20T08:00:29.763Z"
|
||||||
|
},
|
||||||
|
"status_localized": {
|
||||||
|
"state": "No Leak",
|
||||||
|
"description": "Mar 20 at 2:00am"
|
||||||
|
},
|
||||||
|
"insights": {
|
||||||
|
"primary": {
|
||||||
|
"origin": {
|
||||||
|
"type": "Sensor",
|
||||||
|
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
|
},
|
||||||
|
"value": "no_leak",
|
||||||
|
"data_received_at": "2022-03-20T08:00:29.763Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configuration": {},
|
||||||
|
"pro_monitoring_status": "eligible"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
|
"definition_id": 7,
|
||||||
|
"created_at": "2019-07-10T22:40:48.847Z",
|
||||||
|
"type": "sensor",
|
||||||
|
"model_version": "3.1",
|
||||||
|
"sensor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
|
"status": {
|
||||||
|
"trigger_value": "no_alarm",
|
||||||
|
"data_received_at": "2019-06-28T22:12:49.516Z"
|
||||||
|
},
|
||||||
|
"status_localized": {
|
||||||
|
"state": "No Sound",
|
||||||
|
"description": "Jun 28 at 4:12pm"
|
||||||
|
},
|
||||||
|
"insights": {
|
||||||
|
"primary": {
|
||||||
|
"origin": {},
|
||||||
|
"value": "no_alarm",
|
||||||
|
"data_received_at": "2019-06-28T22:12:49.516Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configuration": {},
|
||||||
|
"pro_monitoring_status": "eligible"
|
||||||
|
}
|
||||||
|
]
|
|
@ -7,64 +7,28 @@
|
||||||
"email": "user@email.com"
|
"email": "user@email.com"
|
||||||
},
|
},
|
||||||
"bridge": {
|
"bridge": {
|
||||||
"id": 12345,
|
"id": 67890,
|
||||||
"hardware_id": "0x1234567890abcdef"
|
"hardware_id": "0x0000000000000000"
|
||||||
},
|
},
|
||||||
"last_bridge_hardware_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
"last_bridge_hardware_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
"name": "Bathroom Sensor",
|
"name": "Sensor 1",
|
||||||
"location_id": 123456,
|
"location_id": 123456,
|
||||||
"system_id": 12345,
|
"system_id": 12345,
|
||||||
"hardware_id": "0x1234567890abcdef",
|
"hardware_id": "0x0000000000000000",
|
||||||
"firmware_version": "1.1.2",
|
|
||||||
"hardware_revision": 5,
|
"hardware_revision": 5,
|
||||||
"device_key": "0x1234567890abcdef",
|
|
||||||
"encryption_key": true,
|
|
||||||
"installed_at": "2019-04-30T01:57:34.443Z",
|
|
||||||
"calibrated_at": "2019-04-30T01:57:35.651Z",
|
|
||||||
"last_reported_at": "2019-04-30T02:20:04.821Z",
|
|
||||||
"missing_at": null,
|
|
||||||
"updated_at": "2019-04-30T01:57:36.129Z",
|
|
||||||
"created_at": "2019-04-30T01:56:45.932Z",
|
|
||||||
"signal_strength": 5,
|
|
||||||
"links": {
|
|
||||||
"location": 123456
|
|
||||||
},
|
|
||||||
"lqi": 0,
|
|
||||||
"rssi": -46,
|
|
||||||
"surface_type": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 132462,
|
|
||||||
"uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"user": {
|
|
||||||
"id": 12345,
|
|
||||||
"email": "user@email.com"
|
|
||||||
},
|
|
||||||
"bridge": {
|
|
||||||
"id": 12345,
|
|
||||||
"hardware_id": "0x1234567890abcdef"
|
|
||||||
},
|
|
||||||
"last_bridge_hardware_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"name": "Living Room Sensor",
|
|
||||||
"location_id": 123456,
|
|
||||||
"system_id": 12345,
|
|
||||||
"hardware_id": "0x1234567890abcdef",
|
|
||||||
"firmware_version": "1.1.2",
|
"firmware_version": "1.1.2",
|
||||||
"hardware_revision": 5,
|
"device_key": "0x0000000000000000",
|
||||||
"device_key": "0x1234567890abcdef",
|
|
||||||
"encryption_key": true,
|
"encryption_key": true,
|
||||||
"installed_at": "2019-04-30T01:45:56.169Z",
|
"installed_at": "2019-06-28T22:12:51.209Z",
|
||||||
"calibrated_at": "2019-04-30T01:46:06.256Z",
|
"calibrated_at": "2023-03-07T19:51:56.838Z",
|
||||||
"last_reported_at": "2019-04-30T02:20:04.829Z",
|
"last_reported_at": "2023-04-19T18:09:40.479Z",
|
||||||
"missing_at": null,
|
"missing_at": null,
|
||||||
"updated_at": "2019-04-30T01:46:07.717Z",
|
"updated_at": "2023-03-28T13:33:33.801Z",
|
||||||
"created_at": "2019-04-30T01:45:14.148Z",
|
"created_at": "2019-06-28T22:12:20.256Z",
|
||||||
"signal_strength": 5,
|
"signal_strength": 4,
|
||||||
"links": {
|
"firmware": {
|
||||||
"location": 123456
|
"status": "valid"
|
||||||
},
|
},
|
||||||
"lqi": 0,
|
|
||||||
"rssi": -30,
|
|
||||||
"surface_type": null
|
"surface_type": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"task_type": "missing",
|
|
||||||
"sensor_data": [],
|
|
||||||
"status": {
|
|
||||||
"value": "not_missing",
|
|
||||||
"received_at": "2020-11-11T21:18:06.613Z"
|
|
||||||
},
|
|
||||||
"created_at": "2020-11-11T21:18:06.613Z",
|
|
||||||
"updated_at": "2020-11-11T21:18:06.617Z",
|
|
||||||
"sensor_id": 525993,
|
|
||||||
"model_version": "2.0",
|
|
||||||
"configuration": {},
|
|
||||||
"links": {
|
|
||||||
"sensor": 525993
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"task_type": "leak",
|
|
||||||
"sensor_data": [],
|
|
||||||
"status": {
|
|
||||||
"insights": {
|
|
||||||
"primary": {
|
|
||||||
"from_state": null,
|
|
||||||
"to_state": "no_leak",
|
|
||||||
"data_received_at": "2020-11-11T21:19:13.755Z",
|
|
||||||
"origin": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"created_at": "2020-11-11T21:19:13.755Z",
|
|
||||||
"updated_at": "2020-11-11T21:19:13.764Z",
|
|
||||||
"sensor_id": 525993,
|
|
||||||
"model_version": "2.1",
|
|
||||||
"configuration": {},
|
|
||||||
"links": {
|
|
||||||
"sensor": 525993
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"task_type": "temperature",
|
|
||||||
"sensor_data": [],
|
|
||||||
"status": {
|
|
||||||
"value": "20.991287231445312",
|
|
||||||
"received_at": "2021-01-27T15:18:49.996Z"
|
|
||||||
},
|
|
||||||
"created_at": "2020-11-11T21:19:13.856Z",
|
|
||||||
"updated_at": "2020-11-11T21:19:13.865Z",
|
|
||||||
"sensor_id": 525993,
|
|
||||||
"model_version": "2.1",
|
|
||||||
"configuration": {
|
|
||||||
"lower": 15.56,
|
|
||||||
"upper": 29.44,
|
|
||||||
"offset": 0
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"sensor": 525993
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"task_type": "low_battery",
|
|
||||||
"sensor_data": [],
|
|
||||||
"status": {
|
|
||||||
"insights": {
|
|
||||||
"primary": {
|
|
||||||
"from_state": null,
|
|
||||||
"to_state": "high",
|
|
||||||
"data_received_at": "2020-11-17T18:40:27.024Z",
|
|
||||||
"origin": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"created_at": "2020-11-17T18:40:27.024Z",
|
|
||||||
"updated_at": "2020-11-17T18:40:27.033Z",
|
|
||||||
"sensor_id": 525993,
|
|
||||||
"model_version": "4.1",
|
|
||||||
"configuration": {},
|
|
||||||
"links": {
|
|
||||||
"sensor": 525993
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""Test Notion diagnostics."""
|
"""Test Notion diagnostics."""
|
||||||
from homeassistant.components.diagnostics import REDACTED
|
from homeassistant.components.diagnostics import REDACTED
|
||||||
|
from homeassistant.components.notion import DOMAIN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||||
|
@ -17,7 +18,7 @@ async def test_entry_diagnostics(
|
||||||
"entry": {
|
"entry": {
|
||||||
"entry_id": config_entry.entry_id,
|
"entry_id": config_entry.entry_id,
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"domain": "notion",
|
"domain": DOMAIN,
|
||||||
"title": REDACTED,
|
"title": REDACTED,
|
||||||
"data": {"username": REDACTED, "password": REDACTED},
|
"data": {"username": REDACTED, "password": REDACTED},
|
||||||
"options": {},
|
"options": {},
|
||||||
|
@ -28,106 +29,107 @@ async def test_entry_diagnostics(
|
||||||
"disabled_by": None,
|
"disabled_by": None,
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"bridges": {
|
"bridges": [
|
||||||
"12345": {
|
{
|
||||||
"id": 12345,
|
"id": 12345,
|
||||||
"name": None,
|
"name": "Bridge 1",
|
||||||
"mode": "home",
|
"mode": "home",
|
||||||
"hardware_id": REDACTED,
|
"hardware_id": REDACTED,
|
||||||
"hardware_revision": 4,
|
"hardware_revision": 4,
|
||||||
"firmware_version": {
|
"firmware_version": {
|
||||||
|
"silabs": "1.1.2",
|
||||||
"wifi": "0.121.0",
|
"wifi": "0.121.0",
|
||||||
"wifi_app": "3.3.0",
|
"wifi_app": "3.3.0",
|
||||||
"silabs": "1.0.1",
|
|
||||||
},
|
},
|
||||||
"missing_at": None,
|
"missing_at": None,
|
||||||
"created_at": "2019-04-30T01:43:50.497Z",
|
"created_at": "2019-06-27T00:18:44.337000+00:00",
|
||||||
"updated_at": "2019-04-30T01:44:43.749Z",
|
"updated_at": "2023-03-19T03:20:16.061000+00:00",
|
||||||
"system_id": 12345,
|
"system_id": 11111,
|
||||||
"firmware": {
|
"firmware": {
|
||||||
|
"silabs": "1.1.2",
|
||||||
"wifi": "0.121.0",
|
"wifi": "0.121.0",
|
||||||
"wifi_app": "3.3.0",
|
"wifi_app": "3.3.0",
|
||||||
"silabs": "1.0.1",
|
|
||||||
},
|
},
|
||||||
"links": {"system": 12345},
|
"links": {"system": 11111},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 67890,
|
||||||
|
"name": "Bridge 2",
|
||||||
|
"mode": "home",
|
||||||
|
"hardware_id": REDACTED,
|
||||||
|
"hardware_revision": 4,
|
||||||
|
"firmware_version": {
|
||||||
|
"silabs": "1.1.2",
|
||||||
|
"wifi": "0.121.0",
|
||||||
|
"wifi_app": "3.3.0",
|
||||||
|
},
|
||||||
|
"missing_at": None,
|
||||||
|
"created_at": "2019-04-30T01:43:50.497000+00:00",
|
||||||
|
"updated_at": "2023-01-02T19:09:58.251000+00:00",
|
||||||
|
"system_id": 11111,
|
||||||
|
"firmware": {
|
||||||
|
"silabs": "1.1.2",
|
||||||
|
"wifi": "0.121.0",
|
||||||
|
"wifi_app": "3.3.0",
|
||||||
|
},
|
||||||
|
"links": {"system": 11111},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"listeners": [
|
||||||
|
{
|
||||||
|
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
|
"listener_kind": {
|
||||||
|
"__type": "<enum 'ListenerKind'>",
|
||||||
|
"repr": "<ListenerKind.SMOKE: 7>",
|
||||||
|
},
|
||||||
|
"created_at": "2019-07-10T22:40:48.847000+00:00",
|
||||||
|
"device_type": "sensor",
|
||||||
|
"model_version": "3.1",
|
||||||
|
"sensor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
|
"status": {
|
||||||
|
"trigger_value": "no_alarm",
|
||||||
|
"data_received_at": "2019-06-28T22:12:49.516000+00:00",
|
||||||
|
},
|
||||||
|
"status_localized": {
|
||||||
|
"state": "No Sound",
|
||||||
|
"description": "Jun 28 at 4:12pm",
|
||||||
|
},
|
||||||
|
"insights": {
|
||||||
|
"primary": {
|
||||||
|
"origin": {"type": None, "id": None},
|
||||||
|
"value": "no_alarm",
|
||||||
|
"data_received_at": "2019-06-28T22:12:49.516000+00:00",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configuration": {},
|
||||||
|
"pro_monitoring_status": "eligible",
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
"sensors": {
|
"sensors": [
|
||||||
"123456": {
|
{
|
||||||
"id": 123456,
|
"id": 123456,
|
||||||
"uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
"uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
"user": {"id": 12345, "email": REDACTED},
|
"user": {"id": 12345, "email": REDACTED},
|
||||||
"bridge": {"id": 12345, "hardware_id": REDACTED},
|
"bridge": {"id": 67890, "hardware_id": REDACTED},
|
||||||
"last_bridge_hardware_id": REDACTED,
|
"last_bridge_hardware_id": REDACTED,
|
||||||
"name": "Bathroom Sensor",
|
"name": "Sensor 1",
|
||||||
"location_id": 123456,
|
"location_id": 123456,
|
||||||
"system_id": 12345,
|
"system_id": 12345,
|
||||||
"hardware_id": REDACTED,
|
"hardware_id": REDACTED,
|
||||||
"firmware_version": "1.1.2",
|
|
||||||
"hardware_revision": 5,
|
"hardware_revision": 5,
|
||||||
|
"firmware_version": "1.1.2",
|
||||||
"device_key": REDACTED,
|
"device_key": REDACTED,
|
||||||
"encryption_key": True,
|
"encryption_key": True,
|
||||||
"installed_at": "2019-04-30T01:57:34.443Z",
|
"installed_at": "2019-06-28T22:12:51.209000+00:00",
|
||||||
"calibrated_at": "2019-04-30T01:57:35.651Z",
|
"calibrated_at": "2023-03-07T19:51:56.838000+00:00",
|
||||||
"last_reported_at": "2019-04-30T02:20:04.821Z",
|
"last_reported_at": "2023-04-19T18:09:40.479000+00:00",
|
||||||
"missing_at": None,
|
"missing_at": None,
|
||||||
"updated_at": "2019-04-30T01:57:36.129Z",
|
"updated_at": "2023-03-28T13:33:33.801000+00:00",
|
||||||
"created_at": "2019-04-30T01:56:45.932Z",
|
"created_at": "2019-06-28T22:12:20.256000+00:00",
|
||||||
"signal_strength": 5,
|
"signal_strength": 4,
|
||||||
"links": {"location": 123456},
|
"firmware": {"status": "valid"},
|
||||||
"lqi": 0,
|
|
||||||
"rssi": -46,
|
|
||||||
"surface_type": None,
|
"surface_type": None,
|
||||||
},
|
|
||||||
"132462": {
|
|
||||||
"id": 132462,
|
|
||||||
"uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"user": {"id": 12345, "email": REDACTED},
|
|
||||||
"bridge": {"id": 12345, "hardware_id": REDACTED},
|
|
||||||
"last_bridge_hardware_id": REDACTED,
|
|
||||||
"name": "Living Room Sensor",
|
|
||||||
"location_id": 123456,
|
|
||||||
"system_id": 12345,
|
|
||||||
"hardware_id": REDACTED,
|
|
||||||
"firmware_version": "1.1.2",
|
|
||||||
"hardware_revision": 5,
|
|
||||||
"device_key": REDACTED,
|
|
||||||
"encryption_key": True,
|
|
||||||
"installed_at": "2019-04-30T01:45:56.169Z",
|
|
||||||
"calibrated_at": "2019-04-30T01:46:06.256Z",
|
|
||||||
"last_reported_at": "2019-04-30T02:20:04.829Z",
|
|
||||||
"missing_at": None,
|
|
||||||
"updated_at": "2019-04-30T01:46:07.717Z",
|
|
||||||
"created_at": "2019-04-30T01:45:14.148Z",
|
|
||||||
"signal_strength": 5,
|
|
||||||
"links": {"location": 123456},
|
|
||||||
"lqi": 0,
|
|
||||||
"rssi": -30,
|
|
||||||
"surface_type": None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"tasks": {
|
|
||||||
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx": {
|
|
||||||
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"task_type": "low_battery",
|
|
||||||
"sensor_data": [],
|
|
||||||
"status": {
|
|
||||||
"insights": {
|
|
||||||
"primary": {
|
|
||||||
"from_state": None,
|
|
||||||
"to_state": "high",
|
|
||||||
"data_received_at": "2020-11-17T18:40:27.024Z",
|
|
||||||
"origin": {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"created_at": "2020-11-17T18:40:27.024Z",
|
|
||||||
"updated_at": "2020-11-17T18:40:27.033Z",
|
|
||||||
"sensor_id": 525993,
|
|
||||||
"model_version": "4.1",
|
|
||||||
"configuration": {},
|
|
||||||
"links": {"sensor": 525993},
|
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue