Add diagnostics to august (#68157)

This commit is contained in:
J. Nick Koston 2022-03-15 08:05:56 -10:00 committed by GitHub
parent ed24638201
commit 65c670c2c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 217 additions and 32 deletions

View file

@ -1,10 +1,15 @@
"""Support for August devices.""" """Support for August devices."""
from __future__ import annotations
import asyncio import asyncio
from collections.abc import ValuesView
from itertools import chain from itertools import chain
import logging import logging
from aiohttp import ClientError, ClientResponseError from aiohttp import ClientError, ClientResponseError
from yalexs.doorbell import Doorbell, DoorbellDetail
from yalexs.exceptions import AugustApiAIOHTTPError from yalexs.exceptions import AugustApiAIOHTTPError
from yalexs.lock import Lock, LockDetail
from yalexs.pubnub_activity import activities_from_pubnub_message from yalexs.pubnub_activity import activities_from_pubnub_message
from yalexs.pubnub_async import AugustPubNub, async_create_pubnub from yalexs.pubnub_async import AugustPubNub, async_create_pubnub
@ -18,19 +23,19 @@ from homeassistant.exceptions import (
) )
from .activity import ActivityStream from .activity import ActivityStream
from .const import DATA_AUGUST, DOMAIN, MIN_TIME_BETWEEN_DETAIL_UPDATES, PLATFORMS from .const import DOMAIN, MIN_TIME_BETWEEN_DETAIL_UPDATES, PLATFORMS
from .exceptions import CannotConnect, InvalidAuth, RequireValidation from .exceptions import CannotConnect, InvalidAuth, RequireValidation
from .gateway import AugustGateway from .gateway import AugustGateway
from .subscriber import AugustSubscriberMixin from .subscriber import AugustSubscriberMixin
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
API_CACHED_ATTRS = ( API_CACHED_ATTRS = {
"door_state", "door_state",
"door_state_datetime", "door_state_datetime",
"lock_status", "lock_status",
"lock_status_datetime", "lock_status_datetime",
) }
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@ -52,7 +57,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
hass.data[DOMAIN][entry.entry_id][DATA_AUGUST].async_stop() data: AugustData = hass.data[DOMAIN][entry.entry_id]
data.async_stop()
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
@ -78,10 +84,8 @@ async def async_setup_august(
await august_gateway.async_refresh_access_token_if_needed() await august_gateway.async_refresh_access_token_if_needed()
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
data = hass.data[DOMAIN][config_entry.entry_id] = { data = hass.data[DOMAIN][config_entry.entry_id] = AugustData(hass, august_gateway)
DATA_AUGUST: AugustData(hass, august_gateway) await data.async_setup()
}
await data[DATA_AUGUST].async_setup()
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS) hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
@ -189,16 +193,16 @@ class AugustData(AugustSubscriberMixin):
self.activity_stream.async_stop() self.activity_stream.async_stop()
@property @property
def doorbells(self): def doorbells(self) -> ValuesView[Doorbell]:
"""Return a list of py-august Doorbell objects.""" """Return a list of py-august Doorbell objects."""
return self._doorbells_by_id.values() return self._doorbells_by_id.values()
@property @property
def locks(self): def locks(self) -> ValuesView[Lock]:
"""Return a list of py-august Lock objects.""" """Return a list of py-august Lock objects."""
return self._locks_by_id.values() return self._locks_by_id.values()
def get_device_detail(self, device_id): def get_device_detail(self, device_id: str) -> DoorbellDetail | LockDetail:
"""Return the py-august LockDetail or DoorbellDetail object for a device.""" """Return the py-august LockDetail or DoorbellDetail object for a device."""
return self._device_detail_by_id[device_id] return self._device_detail_by_id[device_id]

View file

@ -29,7 +29,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_call_later from homeassistant.helpers.event import async_call_later
from . import AugustData from . import AugustData
from .const import ACTIVITY_UPDATE_INTERVAL, DATA_AUGUST, DOMAIN from .const import ACTIVITY_UPDATE_INTERVAL, DOMAIN
from .entity import AugustEntityMixin from .entity import AugustEntityMixin
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -160,7 +160,7 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the August binary sensors.""" """Set up the August binary sensors."""
data: AugustData = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
entities: list[BinarySensorEntity] = [] entities: list[BinarySensorEntity] = []
for door in data.locks: for door in data.locks:

View file

@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AugustData from . import AugustData
from .const import DATA_AUGUST, DOMAIN from .const import DOMAIN
from .entity import AugustEntityMixin from .entity import AugustEntityMixin
@ -17,8 +17,8 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up August lock wake buttons.""" """Set up August lock wake buttons."""
data: AugustData = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
async_add_entities([AugustWakeLockButton(data, lock) for lock in data.locks]) async_add_entities(AugustWakeLockButton(data, lock) for lock in data.locks)
class AugustWakeLockButton(AugustEntityMixin, ButtonEntity): class AugustWakeLockButton(AugustEntityMixin, ButtonEntity):

View file

@ -11,7 +11,7 @@ from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AugustData from . import AugustData
from .const import DATA_AUGUST, DEFAULT_NAME, DEFAULT_TIMEOUT, DOMAIN from .const import DEFAULT_NAME, DEFAULT_TIMEOUT, DOMAIN
from .entity import AugustEntityMixin from .entity import AugustEntityMixin
@ -21,13 +21,11 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up August cameras.""" """Set up August cameras."""
data: AugustData = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
session = aiohttp_client.async_get_clientsession(hass) session = aiohttp_client.async_get_clientsession(hass)
async_add_entities( async_add_entities(
[ AugustCamera(data, doorbell, session, DEFAULT_TIMEOUT)
AugustCamera(data, doorbell, session, DEFAULT_TIMEOUT) for doorbell in data.doorbells
for doorbell in data.doorbells
]
) )

View file

@ -19,8 +19,6 @@ MANUFACTURER = "August Home Inc."
DEFAULT_AUGUST_CONFIG_FILE = ".august.conf" DEFAULT_AUGUST_CONFIG_FILE = ".august.conf"
DATA_AUGUST = "data_august"
DEFAULT_NAME = "August" DEFAULT_NAME = "August"
DOMAIN = "august" DOMAIN = "august"

View file

@ -0,0 +1,47 @@
"""Diagnostics support for august."""
from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from . import AugustData
from .const import DOMAIN
TO_REDACT = {
"HouseID",
"OfflineKeys",
"installUserID",
"invitations",
"key",
"pins",
"pubsubChannel",
"recentImage",
"remoteOperateSecret",
"users",
"zWaveDSK",
}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
data: AugustData = hass.data[DOMAIN][entry.entry_id]
return {
"locks": {
lock.device_id: async_redact_data(
data.get_device_detail(lock.device_id).raw, TO_REDACT
)
for lock in data.locks
},
"doorbells": {
doorbell.device_id: async_redact_data(
data.get_device_detail(doorbell.device_id).raw, TO_REDACT
)
for doorbell in data.doorbells
},
}

View file

@ -15,7 +15,7 @@ from homeassistant.helpers.restore_state import RestoreEntity
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from . import AugustData from . import AugustData
from .const import DATA_AUGUST, DOMAIN from .const import DOMAIN
from .entity import AugustEntityMixin from .entity import AugustEntityMixin
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -29,8 +29,8 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up August locks.""" """Set up August locks."""
data: AugustData = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
async_add_entities([AugustLock(data, lock) for lock in data.locks]) async_add_entities(AugustLock(data, lock) for lock in data.locks)
class AugustLock(AugustEntityMixin, RestoreEntity, LockEntity): class AugustLock(AugustEntityMixin, RestoreEntity, LockEntity):

View file

@ -2,7 +2,7 @@
"domain": "august", "domain": "august",
"name": "August", "name": "August",
"documentation": "https://www.home-assistant.io/integrations/august", "documentation": "https://www.home-assistant.io/integrations/august",
"requirements": ["yalexs==1.1.22"], "requirements": ["yalexs==1.1.23"],
"codeowners": ["@bdraco"], "codeowners": ["@bdraco"],
"dhcp": [ "dhcp": [
{ {

View file

@ -31,7 +31,6 @@ from .const import (
ATTR_OPERATION_KEYPAD, ATTR_OPERATION_KEYPAD,
ATTR_OPERATION_METHOD, ATTR_OPERATION_METHOD,
ATTR_OPERATION_REMOTE, ATTR_OPERATION_REMOTE,
DATA_AUGUST,
DOMAIN, DOMAIN,
OPERATION_METHOD_AUTORELOCK, OPERATION_METHOD_AUTORELOCK,
OPERATION_METHOD_KEYPAD, OPERATION_METHOD_KEYPAD,
@ -93,7 +92,7 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the August sensors.""" """Set up the August sensors."""
data: AugustData = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] data: AugustData = hass.data[DOMAIN][config_entry.entry_id]
entities: list[SensorEntity] = [] entities: list[SensorEntity] = []
migrate_unique_id_devices = [] migrate_unique_id_devices = []
operation_sensors = [] operation_sensors = []

View file

@ -2443,7 +2443,7 @@ xs1-api-client==3.0.0
yalesmartalarmclient==0.3.8 yalesmartalarmclient==0.3.8
# homeassistant.components.august # homeassistant.components.august
yalexs==1.1.22 yalexs==1.1.23
# homeassistant.components.yeelight # homeassistant.components.yeelight
yeelight==0.7.9 yeelight==0.7.9

View file

@ -1563,7 +1563,7 @@ xmltodict==0.12.0
yalesmartalarmclient==0.3.8 yalesmartalarmclient==0.3.8
# homeassistant.components.august # homeassistant.components.august
yalexs==1.1.22 yalexs==1.1.23
# homeassistant.components.yeelight # homeassistant.components.yeelight
yeelight==0.7.9 yeelight==0.7.9

View file

@ -0,0 +1,139 @@
"""Test august diagnostics."""
from tests.components.august.mocks import (
_create_august_api_with_devices,
_mock_doorbell_from_fixture,
_mock_lock_from_fixture,
)
from tests.components.diagnostics import get_diagnostics_for_config_entry
async def test_diagnostics(hass, hass_client):
"""Test generating diagnostics for a config entry."""
lock_one = await _mock_lock_from_fixture(
hass, "get_lock.online_with_doorsense.json"
)
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
entry, _ = await _create_august_api_with_devices(hass, [lock_one, doorbell_one])
diag = await get_diagnostics_for_config_entry(hass, hass_client, entry)
assert diag == {
"doorbells": {
"K98GiDT45GUL": {
"HouseID": "**REDACTED**",
"LockID": "BBBB1F5F11114C24CCCC97571DD6AAAA",
"appID": "august-iphone",
"caps": ["reconnect"],
"createdAt": "2016-11-26T22:27:11.176Z",
"doorbellID": "K98GiDT45GUL",
"doorbellServerURL": "https://doorbells.august.com",
"dvrSubscriptionSetupDone": True,
"firmwareVersion": "2.3.0-RC153+201711151527",
"installDate": "2016-11-26T22:27:11.176Z",
"installUserID": "**REDACTED**",
"name": "Front Door",
"pubsubChannel": "**REDACTED**",
"recentImage": "**REDACTED**",
"serialNumber": "tBXZR0Z35E",
"settings": {
"ABREnabled": True,
"IREnabled": True,
"IVAEnabled": False,
"JPGQuality": 70,
"batteryLowThreshold": 3.1,
"batteryRun": False,
"batteryUseThreshold": 3.4,
"bitrateCeiling": 512000,
"buttonpush_notifications": True,
"debug": False,
"directLink": True,
"initialBitrate": 384000,
"irConfiguration": 8448272,
"keepEncoderRunning": True,
"micVolume": 100,
"minACNoScaling": 40,
"motion_notifications": True,
"notify_when_offline": True,
"overlayEnabled": True,
"ringSoundEnabled": True,
"speakerVolume": 92,
"turnOffCamera": False,
"videoResolution": "640x480",
},
"status": "doorbell_call_status_online",
"status_timestamp": 1512811834532,
"telemetry": {
"BSSID": "88:ee:00:dd:aa:11",
"SSID": "foo_ssid",
"ac_in": 23.856874,
"battery": 4.061763,
"battery_soc": 96,
"battery_soh": 95,
"date": "2017-12-10 08:05:12",
"doorbell_low_battery": False,
"ip_addr": "10.0.1.11",
"link_quality": 54,
"load_average": "0.50 0.47 0.35 " "1/154 9345",
"signal_level": -56,
"steady_ac_in": 22.196405,
"temperature": 28.25,
"updated_at": "2017-12-10T08:05:13.650Z",
"uptime": "16168.75 13830.49",
"wifi_freq": 5745,
},
"updatedAt": "2017-12-10T08:05:13.650Z",
}
},
"locks": {
"online_with_doorsense": {
"Bridge": {
"_id": "bridgeid",
"deviceModel": "august-connect",
"firmwareVersion": "2.2.1",
"hyperBridge": True,
"mfgBridgeID": "C5WY200WSH",
"operative": True,
"status": {
"current": "online",
"lastOffline": "2000-00-00T00:00:00.447Z",
"lastOnline": "2000-00-00T00:00:00.447Z",
"updated": "2000-00-00T00:00:00.447Z",
},
},
"Calibrated": False,
"Created": "2000-00-00T00:00:00.447Z",
"HouseID": "**REDACTED**",
"HouseName": "Test",
"LockID": "online_with_doorsense",
"LockName": "Online door with doorsense",
"LockStatus": {
"dateTime": "2017-12-10T04:48:30.272Z",
"doorState": "open",
"isLockStatusChanged": False,
"status": "locked",
"valid": True,
},
"SerialNumber": "XY",
"Type": 1001,
"Updated": "2000-00-00T00:00:00.447Z",
"battery": 0.922,
"currentFirmwareVersion": "undefined-4.3.0-1.8.14",
"homeKitEnabled": True,
"hostLockInfo": {
"manufacturer": "yale",
"productID": 1536,
"productTypeID": 32770,
"serialNumber": "ABC",
},
"isGalileo": False,
"macAddress": "12:22",
"pins": "**REDACTED**",
"pubsubChannel": "**REDACTED**",
"skuNumber": "AUG-MD01",
"supportsEntryCodes": True,
"timeZone": "Pacific/Hawaii",
"zWaveEnabled": False,
}
},
}