Fix a series of bugs due to Notion API changes (#93039)
* Fix a series of bugs due to Notion API changes * Simplify * Reduce blast radius * Reduce blast radius * Fix tests
This commit is contained in:
parent
e593ceaaf2
commit
637941df4d
13 changed files with 283 additions and 221 deletions
|
@ -2,7 +2,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from dataclasses import dataclass, field, fields
|
from dataclasses import dataclass, field
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -10,9 +10,16 @@ from typing import Any
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from aionotion import async_get_client
|
from aionotion import async_get_client
|
||||||
from aionotion.bridge.models import Bridge
|
from aionotion.bridge.models import Bridge, BridgeAllResponse
|
||||||
from aionotion.errors import InvalidCredentialsError, NotionError
|
from aionotion.errors import InvalidCredentialsError, NotionError
|
||||||
from aionotion.sensor.models import Listener, ListenerKind, Sensor
|
from aionotion.sensor.models import (
|
||||||
|
Listener,
|
||||||
|
ListenerAllResponse,
|
||||||
|
ListenerKind,
|
||||||
|
Sensor,
|
||||||
|
SensorAllResponse,
|
||||||
|
)
|
||||||
|
from aionotion.user.models import UserPreferences, UserPreferencesResponse
|
||||||
|
|
||||||
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
|
||||||
|
@ -51,6 +58,11 @@ PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||||
ATTR_SYSTEM_MODE = "system_mode"
|
ATTR_SYSTEM_MODE = "system_mode"
|
||||||
ATTR_SYSTEM_NAME = "system_name"
|
ATTR_SYSTEM_NAME = "system_name"
|
||||||
|
|
||||||
|
DATA_BRIDGES = "bridges"
|
||||||
|
DATA_LISTENERS = "listeners"
|
||||||
|
DATA_SENSORS = "sensors"
|
||||||
|
DATA_USER_PREFERENCES = "user_preferences"
|
||||||
|
|
||||||
DEFAULT_SCAN_INTERVAL = timedelta(minutes=1)
|
DEFAULT_SCAN_INTERVAL = timedelta(minutes=1)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
||||||
|
@ -84,6 +96,9 @@ def is_uuid(value: str) -> bool:
|
||||||
class NotionData:
|
class NotionData:
|
||||||
"""Define a manager class for Notion data."""
|
"""Define a manager class for Notion data."""
|
||||||
|
|
||||||
|
hass: HomeAssistant
|
||||||
|
entry: ConfigEntry
|
||||||
|
|
||||||
# Define a dict of bridges, indexed by bridge ID (an integer):
|
# Define a dict of bridges, indexed by bridge ID (an integer):
|
||||||
bridges: dict[int, Bridge] = field(default_factory=dict)
|
bridges: dict[int, Bridge] = field(default_factory=dict)
|
||||||
|
|
||||||
|
@ -93,12 +108,40 @@ class NotionData:
|
||||||
# Define a dict of sensors, indexed by sensor UUID (a string):
|
# Define a dict of sensors, indexed by sensor UUID (a string):
|
||||||
sensors: dict[str, Sensor] = field(default_factory=dict)
|
sensors: dict[str, Sensor] = field(default_factory=dict)
|
||||||
|
|
||||||
|
# Define a user preferences response object:
|
||||||
|
user_preferences: UserPreferences | None = field(default=None)
|
||||||
|
|
||||||
|
def update_data_from_response(
|
||||||
|
self,
|
||||||
|
response: BridgeAllResponse
|
||||||
|
| ListenerAllResponse
|
||||||
|
| SensorAllResponse
|
||||||
|
| UserPreferencesResponse,
|
||||||
|
) -> None:
|
||||||
|
"""Update data from an aionotion response."""
|
||||||
|
if isinstance(response, BridgeAllResponse):
|
||||||
|
for bridge in response.bridges:
|
||||||
|
# If a new bridge is discovered, register it:
|
||||||
|
if bridge.id not in self.bridges:
|
||||||
|
_async_register_new_bridge(self.hass, self.entry, bridge)
|
||||||
|
self.bridges[bridge.id] = bridge
|
||||||
|
elif isinstance(response, ListenerAllResponse):
|
||||||
|
self.listeners = {listener.id: listener for listener in response.listeners}
|
||||||
|
elif isinstance(response, SensorAllResponse):
|
||||||
|
self.sensors = {sensor.uuid: sensor for sensor in response.sensors}
|
||||||
|
elif isinstance(response, UserPreferencesResponse):
|
||||||
|
self.user_preferences = response.user_preferences
|
||||||
|
|
||||||
def asdict(self) -> dict[str, Any]:
|
def asdict(self) -> dict[str, Any]:
|
||||||
"""Represent this dataclass (and its Pydantic contents) as a dict."""
|
"""Represent this dataclass (and its Pydantic contents) as a dict."""
|
||||||
return {
|
data: dict[str, Any] = {
|
||||||
field.name: [obj.dict() for obj in getattr(self, field.name).values()]
|
DATA_BRIDGES: [bridge.dict() for bridge in self.bridges.values()],
|
||||||
for field in fields(self)
|
DATA_LISTENERS: [listener.dict() for listener in self.listeners.values()],
|
||||||
|
DATA_SENSORS: [sensor.dict() for sensor in self.sensors.values()],
|
||||||
}
|
}
|
||||||
|
if self.user_preferences:
|
||||||
|
data[DATA_USER_PREFERENCES] = self.user_preferences.dict()
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
@ -121,11 +164,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
|
||||||
async def async_update() -> NotionData:
|
async def async_update() -> NotionData:
|
||||||
"""Get the latest data from the Notion API."""
|
"""Get the latest data from the Notion API."""
|
||||||
data = NotionData()
|
data = NotionData(hass=hass, entry=entry)
|
||||||
tasks = {
|
tasks = {
|
||||||
"bridges": client.bridge.async_all(),
|
DATA_BRIDGES: client.bridge.async_all(),
|
||||||
"listeners": client.sensor.async_listeners(),
|
DATA_LISTENERS: client.sensor.async_listeners(),
|
||||||
"sensors": client.sensor.async_all(),
|
DATA_SENSORS: client.sensor.async_all(),
|
||||||
|
DATA_USER_PREFERENCES: client.user.async_preferences(),
|
||||||
}
|
}
|
||||||
|
|
||||||
results = await asyncio.gather(*tasks.values(), return_exceptions=True)
|
results = await asyncio.gather(*tasks.values(), return_exceptions=True)
|
||||||
|
@ -145,16 +189,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
f"There was an unknown error while updating {attr}: {result}"
|
f"There was an unknown error while updating {attr}: {result}"
|
||||||
) from result
|
) from result
|
||||||
|
|
||||||
for item in result:
|
data.update_data_from_response(result)
|
||||||
if attr == "bridges":
|
|
||||||
# If a new bridge is discovered, register it:
|
|
||||||
if item.id not in data.bridges:
|
|
||||||
_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
|
||||||
|
|
||||||
|
@ -216,7 +251,7 @@ 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: Bridge, entry: ConfigEntry
|
hass: HomeAssistant, entry: ConfigEntry, bridge: Bridge
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Register a new bridge."""
|
"""Register a new bridge."""
|
||||||
if name := bridge.name:
|
if name := bridge.name:
|
||||||
|
@ -279,6 +314,11 @@ class NotionEntity(CoordinatorEntity[DataUpdateCoordinator[NotionData]]):
|
||||||
and self._listener_id in self.coordinator.data.listeners
|
and self._listener_id in self.coordinator.data.listeners
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def listener(self) -> Listener:
|
||||||
|
"""Return the listener related to this entity."""
|
||||||
|
return self.coordinator.data.listeners[self._listener_id]
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_bridge_id(self) -> None:
|
def _async_update_bridge_id(self) -> None:
|
||||||
"""Update the entity's bridge ID if it has changed.
|
"""Update the entity's bridge ID if it has changed.
|
||||||
|
@ -310,21 +350,9 @@ class NotionEntity(CoordinatorEntity[DataUpdateCoordinator[NotionData]]):
|
||||||
this_device.id, via_device_id=bridge_device.id
|
this_device.id, via_device_id=bridge_device.id
|
||||||
)
|
)
|
||||||
|
|
||||||
@callback
|
|
||||||
def _async_update_from_latest_data(self) -> None:
|
|
||||||
"""Update the entity from the latest data."""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@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._listener_id in self.coordinator.data.listeners:
|
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()
|
super()._handle_coordinator_update()
|
||||||
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
|
||||||
"""Handle entity which will be added."""
|
|
||||||
await super().async_added_to_hass()
|
|
||||||
self._async_update_from_latest_data()
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ from homeassistant.components.binary_sensor import (
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import EntityCategory
|
from homeassistant.const import EntityCategory
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import NotionEntity
|
from . import NotionEntity
|
||||||
|
@ -37,7 +37,7 @@ from .model import NotionEntityDescriptionMixin
|
||||||
class NotionBinarySensorDescriptionMixin:
|
class NotionBinarySensorDescriptionMixin:
|
||||||
"""Define an entity description mixin for binary and regular sensors."""
|
"""Define an entity description mixin for binary and regular sensors."""
|
||||||
|
|
||||||
on_state: Literal["alarm", "critical", "leak", "not_missing", "open"]
|
on_state: Literal["alarm", "leak", "low", "not_missing", "open"]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -56,7 +56,7 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
||||||
device_class=BinarySensorDeviceClass.BATTERY,
|
device_class=BinarySensorDeviceClass.BATTERY,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
listener_kind=ListenerKind.BATTERY,
|
listener_kind=ListenerKind.BATTERY,
|
||||||
on_state="critical",
|
on_state="low",
|
||||||
),
|
),
|
||||||
NotionBinarySensorDescription(
|
NotionBinarySensorDescription(
|
||||||
key=SENSOR_DOOR,
|
key=SENSOR_DOOR,
|
||||||
|
@ -146,17 +146,10 @@ class NotionBinarySensor(NotionEntity, BinarySensorEntity):
|
||||||
|
|
||||||
entity_description: NotionBinarySensorDescription
|
entity_description: NotionBinarySensorDescription
|
||||||
|
|
||||||
@callback
|
@property
|
||||||
def _async_update_from_latest_data(self) -> None:
|
def is_on(self) -> bool | None:
|
||||||
"""Fetch new state data for the sensor."""
|
"""Return true if the binary sensor is on."""
|
||||||
listener = self.coordinator.data.listeners[self._listener_id]
|
if not self.listener.insights.primary.value:
|
||||||
|
LOGGER.warning("Unknown listener structure: %s", self.listener.dict())
|
||||||
if listener.status.trigger_value:
|
return False
|
||||||
state = listener.status.trigger_value
|
return self.listener.insights.primary.value == self.entity_description.on_state
|
||||||
elif listener.insights.primary.value:
|
|
||||||
state = listener.insights.primary.value
|
|
||||||
else:
|
|
||||||
LOGGER.warning("Unknown listener structure: %s", listener)
|
|
||||||
state = None
|
|
||||||
|
|
||||||
self._attr_is_on = self.entity_description.on_state == state
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ CONF_DEVICE_KEY = "device_key"
|
||||||
CONF_HARDWARE_ID = "hardware_id"
|
CONF_HARDWARE_ID = "hardware_id"
|
||||||
CONF_LAST_BRIDGE_HARDWARE_ID = "last_bridge_hardware_id"
|
CONF_LAST_BRIDGE_HARDWARE_ID = "last_bridge_hardware_id"
|
||||||
CONF_TITLE = "title"
|
CONF_TITLE = "title"
|
||||||
|
CONF_USER_ID = "user_id"
|
||||||
|
|
||||||
TO_REDACT = {
|
TO_REDACT = {
|
||||||
CONF_DEVICE_KEY,
|
CONF_DEVICE_KEY,
|
||||||
|
@ -27,6 +28,7 @@ TO_REDACT = {
|
||||||
CONF_TITLE,
|
CONF_TITLE,
|
||||||
CONF_UNIQUE_ID,
|
CONF_UNIQUE_ID,
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
|
CONF_USER_ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,5 +7,5 @@
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"loggers": ["aionotion"],
|
"loggers": ["aionotion"],
|
||||||
"requirements": ["aionotion==2023.05.1"]
|
"requirements": ["aionotion==2023.05.4"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,11 @@ from homeassistant.components.sensor import (
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import UnitOfTemperature
|
from homeassistant.const import UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import NotionEntity
|
from . import NotionEntity
|
||||||
from .const import DOMAIN, LOGGER, SENSOR_TEMPERATURE
|
from .const import DOMAIN, SENSOR_TEMPERATURE
|
||||||
from .model import NotionEntityDescriptionMixin
|
from .model import NotionEntityDescriptionMixin
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,15 +63,24 @@ async def async_setup_entry(
|
||||||
class NotionSensor(NotionEntity, SensorEntity):
|
class NotionSensor(NotionEntity, SensorEntity):
|
||||||
"""Define a Notion sensor."""
|
"""Define a Notion sensor."""
|
||||||
|
|
||||||
@callback
|
@property
|
||||||
def _async_update_from_latest_data(self) -> None:
|
def native_unit_of_measurement(self) -> str | None:
|
||||||
"""Fetch new state data for the sensor."""
|
"""Return the unit of measurement of the sensor."""
|
||||||
listener = self.coordinator.data.listeners[self._listener_id]
|
if self.listener.listener_kind == ListenerKind.TEMPERATURE:
|
||||||
|
if not self.coordinator.data.user_preferences:
|
||||||
|
return None
|
||||||
|
if self.coordinator.data.user_preferences.celsius_enabled:
|
||||||
|
return UnitOfTemperature.CELSIUS
|
||||||
|
return UnitOfTemperature.FAHRENHEIT
|
||||||
|
return None
|
||||||
|
|
||||||
if listener.listener_kind == ListenerKind.TEMPERATURE:
|
@property
|
||||||
self._attr_native_value = round(listener.status.temperature, 1) # type: ignore[attr-defined]
|
def native_value(self) -> str | None:
|
||||||
else:
|
"""Return the value reported by the sensor.
|
||||||
LOGGER.error(
|
|
||||||
"Unknown listener type for sensor %s",
|
The Notion API only returns a localized string for temperature (e.g. "70°"); we
|
||||||
self.coordinator.data.sensors[self._sensor_id],
|
simply remove the degree symbol:
|
||||||
)
|
"""
|
||||||
|
if not self.listener.status_localized:
|
||||||
|
return None
|
||||||
|
return self.listener.status_localized.state[:-1]
|
||||||
|
|
|
@ -223,7 +223,7 @@ aionanoleaf==0.2.1
|
||||||
aionotify==0.2.0
|
aionotify==0.2.0
|
||||||
|
|
||||||
# homeassistant.components.notion
|
# homeassistant.components.notion
|
||||||
aionotion==2023.05.1
|
aionotion==2023.05.4
|
||||||
|
|
||||||
# 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==2023.05.1
|
aionotion==2023.05.4
|
||||||
|
|
||||||
# homeassistant.components.oncue
|
# homeassistant.components.oncue
|
||||||
aiooncue==0.3.4
|
aiooncue==0.3.4
|
||||||
|
|
|
@ -3,8 +3,9 @@ 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.bridge.models import BridgeAllResponse
|
||||||
from aionotion.sensor.models import Listener, Sensor
|
from aionotion.sensor.models import ListenerAllResponse, SensorAllResponse
|
||||||
|
from aionotion.user.models import UserPreferencesResponse
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.notion import DOMAIN
|
from homeassistant.components.notion import DOMAIN
|
||||||
|
@ -27,24 +28,23 @@ def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="client")
|
@pytest.fixture(name="client")
|
||||||
def client_fixture(data_bridge, data_listener, data_sensor):
|
def client_fixture(data_bridge, data_listener, data_sensor, data_user_preferences):
|
||||||
"""Define a fixture for an aionotion client."""
|
"""Define a fixture for an aionotion client."""
|
||||||
return Mock(
|
return Mock(
|
||||||
bridge=Mock(
|
bridge=Mock(
|
||||||
async_all=AsyncMock(
|
async_all=AsyncMock(return_value=BridgeAllResponse.parse_obj(data_bridge))
|
||||||
return_value=[Bridge.parse_obj(bridge) for bridge in data_bridge]
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
sensor=Mock(
|
sensor=Mock(
|
||||||
async_all=AsyncMock(
|
async_all=AsyncMock(return_value=SensorAllResponse.parse_obj(data_sensor)),
|
||||||
return_value=[Sensor.parse_obj(sensor) for sensor in data_sensor]
|
|
||||||
),
|
|
||||||
async_listeners=AsyncMock(
|
async_listeners=AsyncMock(
|
||||||
return_value=[
|
return_value=ListenerAllResponse.parse_obj(data_listener)
|
||||||
Listener.parse_obj(listener) for listener in data_listener
|
|
||||||
]
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
user=Mock(
|
||||||
|
async_preferences=AsyncMock(
|
||||||
|
return_value=UserPreferencesResponse.parse_obj(data_user_preferences)
|
||||||
|
)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,6 +83,12 @@ def data_sensor_fixture():
|
||||||
return json.loads(load_fixture("sensor_data.json", "notion"))
|
return json.loads(load_fixture("sensor_data.json", "notion"))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="data_user_preferences", scope="package")
|
||||||
|
def data_user_preferences_fixture():
|
||||||
|
"""Define user preferences data."""
|
||||||
|
return json.loads(load_fixture("user_preferences_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."""
|
||||||
|
|
|
@ -1,50 +1,52 @@
|
||||||
[
|
{
|
||||||
{
|
"base_stations": [
|
||||||
"id": 12345,
|
{
|
||||||
"name": "Bridge 1",
|
"id": 12345,
|
||||||
"mode": "home",
|
"name": "Bridge 1",
|
||||||
"hardware_id": "0x0000000000000000",
|
"mode": "home",
|
||||||
"hardware_revision": 4,
|
"hardware_id": "0x0000000000000000",
|
||||||
"firmware_version": {
|
"hardware_revision": 4,
|
||||||
"silabs": "1.1.2",
|
"firmware_version": {
|
||||||
"wifi": "0.121.0",
|
"silabs": "1.1.2",
|
||||||
"wifi_app": "3.3.0"
|
"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
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"missing_at": null,
|
{
|
||||||
"created_at": "2019-06-27T00:18:44.337Z",
|
"id": 67890,
|
||||||
"updated_at": "2023-03-19T03:20:16.061Z",
|
"name": "Bridge 2",
|
||||||
"system_id": 11111,
|
"mode": "home",
|
||||||
"firmware": {
|
"hardware_id": "0x0000000000000000",
|
||||||
"silabs": "1.1.2",
|
"hardware_revision": 4,
|
||||||
"wifi": "0.121.0",
|
"firmware_version": {
|
||||||
"wifi_app": "3.3.0"
|
"wifi": "0.121.0",
|
||||||
},
|
"wifi_app": "3.3.0",
|
||||||
"links": {
|
"silabs": "1.1.2"
|
||||||
"system": 11111
|
},
|
||||||
|
"missing_at": null,
|
||||||
|
"created_at": "2019-04-30T01:43:50.497Z",
|
||||||
|
"updated_at": "2023-01-02T19:09:58.251Z",
|
||||||
|
"system_id": 11111,
|
||||||
|
"firmware": {
|
||||||
|
"wifi": "0.121.0",
|
||||||
|
"wifi_app": "3.3.0",
|
||||||
|
"silabs": "1.1.2"
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"system": 11111
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
{
|
}
|
||||||
"id": 67890,
|
|
||||||
"name": "Bridge 2",
|
|
||||||
"mode": "home",
|
|
||||||
"hardware_id": "0x0000000000000000",
|
|
||||||
"hardware_revision": 4,
|
|
||||||
"firmware_version": {
|
|
||||||
"wifi": "0.121.0",
|
|
||||||
"wifi_app": "3.3.0",
|
|
||||||
"silabs": "1.1.2"
|
|
||||||
},
|
|
||||||
"missing_at": null,
|
|
||||||
"created_at": "2019-04-30T01:43:50.497Z",
|
|
||||||
"updated_at": "2023-01-02T19:09:58.251Z",
|
|
||||||
"system_id": 11111,
|
|
||||||
"firmware": {
|
|
||||||
"wifi": "0.121.0",
|
|
||||||
"wifi_app": "3.3.0",
|
|
||||||
"silabs": "1.1.2"
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"system": 11111
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
|
@ -1,55 +1,57 @@
|
||||||
[
|
{
|
||||||
{
|
"listeners": [
|
||||||
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
{
|
||||||
"definition_id": 4,
|
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
"created_at": "2019-06-28T22:12:49.651Z",
|
"definition_id": 4,
|
||||||
"type": "sensor",
|
"created_at": "2019-06-28T22:12:49.651Z",
|
||||||
"model_version": "2.1",
|
"type": "sensor",
|
||||||
"sensor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
"model_version": "2.1",
|
||||||
"status": {
|
"sensor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
"trigger_value": "no_leak",
|
"status": {
|
||||||
"data_received_at": "2022-03-20T08:00:29.763Z"
|
"trigger_value": "no_leak",
|
||||||
},
|
|
||||||
"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"
|
"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"
|
||||||
},
|
},
|
||||||
"configuration": {},
|
{
|
||||||
"pro_monitoring_status": "eligible"
|
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
},
|
"definition_id": 7,
|
||||||
{
|
"created_at": "2019-07-10T22:40:48.847Z",
|
||||||
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
"type": "sensor",
|
||||||
"definition_id": 7,
|
"model_version": "3.1",
|
||||||
"created_at": "2019-07-10T22:40:48.847Z",
|
"sensor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
"type": "sensor",
|
"status": {
|
||||||
"model_version": "3.1",
|
"trigger_value": "no_alarm",
|
||||||
"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"
|
"data_received_at": "2019-06-28T22:12:49.516Z"
|
||||||
}
|
},
|
||||||
},
|
"status_localized": {
|
||||||
"configuration": {},
|
"state": "No Sound",
|
||||||
"pro_monitoring_status": "eligible"
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -1,34 +1,36 @@
|
||||||
[
|
{
|
||||||
{
|
"sensors": [
|
||||||
"id": 123456,
|
{
|
||||||
"uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
"id": 123456,
|
||||||
"user": {
|
"uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
"id": 12345,
|
"user": {
|
||||||
"email": "user@email.com"
|
"id": 12345,
|
||||||
},
|
"email": "user@email.com"
|
||||||
"bridge": {
|
},
|
||||||
"id": 67890,
|
"bridge": {
|
||||||
"hardware_id": "0x0000000000000000"
|
"id": 67890,
|
||||||
},
|
"hardware_id": "0x0000000000000000"
|
||||||
"last_bridge_hardware_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
},
|
||||||
"name": "Sensor 1",
|
"last_bridge_hardware_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||||
"location_id": 123456,
|
"name": "Sensor 1",
|
||||||
"system_id": 12345,
|
"location_id": 123456,
|
||||||
"hardware_id": "0x0000000000000000",
|
"system_id": 12345,
|
||||||
"hardware_revision": 5,
|
"hardware_id": "0x0000000000000000",
|
||||||
"firmware_version": "1.1.2",
|
"hardware_revision": 5,
|
||||||
"device_key": "0x0000000000000000",
|
"firmware_version": "1.1.2",
|
||||||
"encryption_key": true,
|
"device_key": "0x0000000000000000",
|
||||||
"installed_at": "2019-06-28T22:12:51.209Z",
|
"encryption_key": true,
|
||||||
"calibrated_at": "2023-03-07T19:51:56.838Z",
|
"installed_at": "2019-06-28T22:12:51.209Z",
|
||||||
"last_reported_at": "2023-04-19T18:09:40.479Z",
|
"calibrated_at": "2023-03-07T19:51:56.838Z",
|
||||||
"missing_at": null,
|
"last_reported_at": "2023-04-19T18:09:40.479Z",
|
||||||
"updated_at": "2023-03-28T13:33:33.801Z",
|
"missing_at": null,
|
||||||
"created_at": "2019-06-28T22:12:20.256Z",
|
"updated_at": "2023-03-28T13:33:33.801Z",
|
||||||
"signal_strength": 4,
|
"created_at": "2019-06-28T22:12:20.256Z",
|
||||||
"firmware": {
|
"signal_strength": 4,
|
||||||
"status": "valid"
|
"firmware": {
|
||||||
},
|
"status": "valid"
|
||||||
"surface_type": null
|
},
|
||||||
}
|
"surface_type": null
|
||||||
]
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
10
tests/components/notion/fixtures/user_preferences_data.json
Normal file
10
tests/components/notion/fixtures/user_preferences_data.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"user_preferences": {
|
||||||
|
"user_id": 12345,
|
||||||
|
"military_time_enabled": false,
|
||||||
|
"celsius_enabled": false,
|
||||||
|
"disconnect_alerts_enabled": true,
|
||||||
|
"home_away_alerts_enabled": false,
|
||||||
|
"battery_alerts_enabled": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,14 +86,6 @@ async def test_entry_diagnostics(
|
||||||
"device_type": "sensor",
|
"device_type": "sensor",
|
||||||
"model_version": "3.1",
|
"model_version": "3.1",
|
||||||
"sensor_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
"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": {
|
"insights": {
|
||||||
"primary": {
|
"primary": {
|
||||||
"origin": {"type": None, "id": None},
|
"origin": {"type": None, "id": None},
|
||||||
|
@ -103,6 +95,14 @@ async def test_entry_diagnostics(
|
||||||
},
|
},
|
||||||
"configuration": {},
|
"configuration": {},
|
||||||
"pro_monitoring_status": "eligible",
|
"pro_monitoring_status": "eligible",
|
||||||
|
"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",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sensors": [
|
"sensors": [
|
||||||
|
@ -131,5 +131,13 @@ async def test_entry_diagnostics(
|
||||||
"surface_type": None,
|
"surface_type": None,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"user_preferences": {
|
||||||
|
"user_id": REDACTED,
|
||||||
|
"military_time_enabled": False,
|
||||||
|
"celsius_enabled": False,
|
||||||
|
"disconnect_alerts_enabled": True,
|
||||||
|
"home_away_alerts_enabled": False,
|
||||||
|
"battery_alerts_enabled": True,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue