diff --git a/homeassistant/components/august/__init__.py b/homeassistant/components/august/__init__.py index 031f513843f..570ec5983fe 100644 --- a/homeassistant/components/august/__init__.py +++ b/homeassistant/components/august/__init__.py @@ -1,10 +1,15 @@ """Support for August devices.""" +from __future__ import annotations + import asyncio +from collections.abc import ValuesView from itertools import chain import logging from aiohttp import ClientError, ClientResponseError +from yalexs.doorbell import Doorbell, DoorbellDetail from yalexs.exceptions import AugustApiAIOHTTPError +from yalexs.lock import Lock, LockDetail from yalexs.pubnub_activity import activities_from_pubnub_message from yalexs.pubnub_async import AugustPubNub, async_create_pubnub @@ -18,19 +23,19 @@ from homeassistant.exceptions import ( ) 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 .gateway import AugustGateway from .subscriber import AugustSubscriberMixin _LOGGER = logging.getLogger(__name__) -API_CACHED_ATTRS = ( +API_CACHED_ATTRS = { "door_state", "door_state_datetime", "lock_status", "lock_status_datetime", -) +} 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: """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) @@ -78,10 +84,8 @@ async def async_setup_august( await august_gateway.async_refresh_access_token_if_needed() hass.data.setdefault(DOMAIN, {}) - data = hass.data[DOMAIN][config_entry.entry_id] = { - DATA_AUGUST: AugustData(hass, august_gateway) - } - await data[DATA_AUGUST].async_setup() + data = hass.data[DOMAIN][config_entry.entry_id] = AugustData(hass, august_gateway) + await data.async_setup() hass.config_entries.async_setup_platforms(config_entry, PLATFORMS) @@ -189,16 +193,16 @@ class AugustData(AugustSubscriberMixin): self.activity_stream.async_stop() @property - def doorbells(self): + def doorbells(self) -> ValuesView[Doorbell]: """Return a list of py-august Doorbell objects.""" return self._doorbells_by_id.values() @property - def locks(self): + def locks(self) -> ValuesView[Lock]: """Return a list of py-august Lock objects.""" 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 self._device_detail_by_id[device_id] diff --git a/homeassistant/components/august/binary_sensor.py b/homeassistant/components/august/binary_sensor.py index 703a1e58d87..a33d1cb96dc 100644 --- a/homeassistant/components/august/binary_sensor.py +++ b/homeassistant/components/august/binary_sensor.py @@ -29,7 +29,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_call_later from . import AugustData -from .const import ACTIVITY_UPDATE_INTERVAL, DATA_AUGUST, DOMAIN +from .const import ACTIVITY_UPDATE_INTERVAL, DOMAIN from .entity import AugustEntityMixin _LOGGER = logging.getLogger(__name__) @@ -160,7 +160,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """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] = [] for door in data.locks: diff --git a/homeassistant/components/august/button.py b/homeassistant/components/august/button.py index d7f2a5ba4ae..5f4032153a2 100644 --- a/homeassistant/components/august/button.py +++ b/homeassistant/components/august/button.py @@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import AugustData -from .const import DATA_AUGUST, DOMAIN +from .const import DOMAIN from .entity import AugustEntityMixin @@ -17,8 +17,8 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up August lock wake buttons.""" - data: AugustData = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] - async_add_entities([AugustWakeLockButton(data, lock) for lock in data.locks]) + data: AugustData = hass.data[DOMAIN][config_entry.entry_id] + async_add_entities(AugustWakeLockButton(data, lock) for lock in data.locks) class AugustWakeLockButton(AugustEntityMixin, ButtonEntity): diff --git a/homeassistant/components/august/camera.py b/homeassistant/components/august/camera.py index 26889427555..c5ab5fc3cfa 100644 --- a/homeassistant/components/august/camera.py +++ b/homeassistant/components/august/camera.py @@ -11,7 +11,7 @@ from homeassistant.helpers import aiohttp_client from homeassistant.helpers.entity_platform import AddEntitiesCallback 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 @@ -21,13 +21,11 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """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) async_add_entities( - [ - AugustCamera(data, doorbell, session, DEFAULT_TIMEOUT) - for doorbell in data.doorbells - ] + AugustCamera(data, doorbell, session, DEFAULT_TIMEOUT) + for doorbell in data.doorbells ) diff --git a/homeassistant/components/august/const.py b/homeassistant/components/august/const.py index ac6f463467f..9a724d4a87b 100644 --- a/homeassistant/components/august/const.py +++ b/homeassistant/components/august/const.py @@ -19,8 +19,6 @@ MANUFACTURER = "August Home Inc." DEFAULT_AUGUST_CONFIG_FILE = ".august.conf" -DATA_AUGUST = "data_august" - DEFAULT_NAME = "August" DOMAIN = "august" diff --git a/homeassistant/components/august/diagnostics.py b/homeassistant/components/august/diagnostics.py new file mode 100644 index 00000000000..ffd62cd8fb7 --- /dev/null +++ b/homeassistant/components/august/diagnostics.py @@ -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 + }, + } diff --git a/homeassistant/components/august/lock.py b/homeassistant/components/august/lock.py index e30f8301a8f..c993cf03b89 100644 --- a/homeassistant/components/august/lock.py +++ b/homeassistant/components/august/lock.py @@ -15,7 +15,7 @@ from homeassistant.helpers.restore_state import RestoreEntity import homeassistant.util.dt as dt_util from . import AugustData -from .const import DATA_AUGUST, DOMAIN +from .const import DOMAIN from .entity import AugustEntityMixin _LOGGER = logging.getLogger(__name__) @@ -29,8 +29,8 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up August locks.""" - data: AugustData = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] - async_add_entities([AugustLock(data, lock) for lock in data.locks]) + data: AugustData = hass.data[DOMAIN][config_entry.entry_id] + async_add_entities(AugustLock(data, lock) for lock in data.locks) class AugustLock(AugustEntityMixin, RestoreEntity, LockEntity): diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index 1eb923b91a8..329522bef28 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,7 +2,7 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["yalexs==1.1.22"], + "requirements": ["yalexs==1.1.23"], "codeowners": ["@bdraco"], "dhcp": [ { diff --git a/homeassistant/components/august/sensor.py b/homeassistant/components/august/sensor.py index cd95e1c9926..6116e7d7601 100644 --- a/homeassistant/components/august/sensor.py +++ b/homeassistant/components/august/sensor.py @@ -31,7 +31,6 @@ from .const import ( ATTR_OPERATION_KEYPAD, ATTR_OPERATION_METHOD, ATTR_OPERATION_REMOTE, - DATA_AUGUST, DOMAIN, OPERATION_METHOD_AUTORELOCK, OPERATION_METHOD_KEYPAD, @@ -93,7 +92,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """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] = [] migrate_unique_id_devices = [] operation_sensors = [] diff --git a/requirements_all.txt b/requirements_all.txt index db676ad9ed1..9c71107e86a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2443,7 +2443,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.8 # homeassistant.components.august -yalexs==1.1.22 +yalexs==1.1.23 # homeassistant.components.yeelight yeelight==0.7.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5e7d0c25929..0f59c87c706 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1563,7 +1563,7 @@ xmltodict==0.12.0 yalesmartalarmclient==0.3.8 # homeassistant.components.august -yalexs==1.1.22 +yalexs==1.1.23 # homeassistant.components.yeelight yeelight==0.7.9 diff --git a/tests/components/august/test_diagnostics.py b/tests/components/august/test_diagnostics.py new file mode 100644 index 00000000000..520daa91f91 --- /dev/null +++ b/tests/components/august/test_diagnostics.py @@ -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, + } + }, + }