Refactor doorbird to avoid using events internally (#98585)

pull/98714/head
J. Nick Koston 2023-08-20 07:49:33 -05:00 committed by GitHub
parent f07724ff52
commit 38af44225e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 62 deletions

View File

@ -221,6 +221,7 @@ omit =
homeassistant/components/doorbird/device.py
homeassistant/components/doorbird/entity.py
homeassistant/components/doorbird/util.py
homeassistant/components/doorbird/view.py
homeassistant/components/dormakaba_dkey/__init__.py
homeassistant/components/dormakaba_dkey/binary_sensor.py
homeassistant/components/dormakaba_dkey/entity.py

View File

@ -5,13 +5,11 @@ from http import HTTPStatus
import logging
from typing import Any
from aiohttp import web
from doorbirdpy import DoorBird
import requests
import voluptuous as vol
from homeassistant.components import persistent_notification
from homeassistant.components.http import HomeAssistantView
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_HOST,
@ -20,21 +18,20 @@ from homeassistant.const import (
CONF_TOKEN,
CONF_USERNAME,
)
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType
from .const import API_URL, CONF_EVENTS, DOMAIN, PLATFORMS
from .const import CONF_EVENTS, DOMAIN, PLATFORMS
from .device import ConfiguredDoorBird
from .models import DoorBirdData
from .util import get_door_station_by_token
from .view import DoorBirdRequestView
_LOGGER = logging.getLogger(__name__)
CONF_CUSTOM_URL = "hass_url_override"
RESET_DEVICE_FAVORITES = "doorbird_reset_favorites"
DEVICE_SCHEMA = vol.Schema(
{
@ -54,29 +51,8 @@ CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the DoorBird component."""
hass.data.setdefault(DOMAIN, {})
# Provide an endpoint for the door stations to call to trigger events
hass.http.register_view(DoorBirdRequestView)
def _reset_device_favorites_handler(event: Event) -> None:
"""Handle clearing favorites on device."""
if (token := event.data.get("token")) is None:
return
door_station = get_door_station_by_token(hass, token)
if door_station is None:
_LOGGER.error("Device not found for provided token")
return
# Clear webhooks
favorites: dict[str, list[str]] = door_station.device.favorites()
for favorite_type, favorite_ids in favorites.items():
for favorite_id in favorite_ids:
door_station.device.delete_favorite(favorite_type, favorite_id)
hass.bus.async_listen(RESET_DEVICE_FAVORITES, _reset_device_favorites_handler)
return True
@ -150,6 +126,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def _async_register_events(
hass: HomeAssistant, door_station: ConfiguredDoorBird
) -> bool:
"""Register events on device."""
try:
await hass.async_add_executor_job(door_station.register_events, hass)
except requests.exceptions.HTTPError:
@ -190,36 +167,3 @@ def _async_import_options_from_data_if_missing(hass: HomeAssistant, entry: Confi
if modified:
hass.config_entries.async_update_entry(entry, options=options)
class DoorBirdRequestView(HomeAssistantView):
"""Provide a page for the device to call."""
requires_auth = False
url = API_URL
name = API_URL[1:].replace("/", ":")
extra_urls = [API_URL + "/{event}"]
async def get(self, request: web.Request, event: str) -> web.Response:
"""Respond to requests from the device."""
hass: HomeAssistant = request.app["hass"]
token: str | None = request.query.get("token")
if token is None or (device := get_door_station_by_token(hass, token)) is None:
return web.Response(
status=HTTPStatus.UNAUTHORIZED, text="Invalid token provided."
)
if device:
event_data = device.get_event_data(event)
else:
event_data = {}
if event == "clear":
hass.bus.async_fire(RESET_DEVICE_FAVORITES, {"token": token})
message = f"HTTP Favorites cleared for {device.slug}"
return web.Response(text=message)
hass.bus.async_fire(f"{DOMAIN}_{event}", event_data)
return web.Response(text="OK")

View File

@ -128,5 +128,6 @@ class DoorBirdCamera(DoorBirdEntity, Camera):
"""Unsubscribe from events."""
event_to_entity_id = self._door_bird_data.event_entity_ids
for event in self._door_station.events:
del event_to_entity_id[event]
# If the clear api was called, the events may not be in the dict
event_to_entity_id.pop(event, None)
await super().async_will_remove_from_hass()

View File

@ -145,3 +145,20 @@ class ConfiguredDoorBird:
"html5_viewer_url": self._device.html5_viewer_url,
ATTR_ENTITY_ID: self._event_entity_ids.get(event),
}
async def async_reset_device_favorites(
hass: HomeAssistant, door_station: ConfiguredDoorBird
) -> None:
"""Handle clearing favorites on device."""
await hass.async_add_executor_job(_reset_device_favorites, door_station)
def _reset_device_favorites(door_station: ConfiguredDoorBird) -> None:
"""Handle clearing favorites on device."""
# Clear webhooks
door_bird = door_station.device
favorites: dict[str, list[str]] = door_bird.favorites()
for favorite_type, favorite_ids in favorites.items():
for favorite_id in favorite_ids:
door_bird.delete_favorite(favorite_type, favorite_id)

View File

@ -1,5 +1,7 @@
"""DoorBird integration utils."""
from typing import Any
from homeassistant.core import HomeAssistant
from .const import DOMAIN
@ -7,7 +9,7 @@ from .device import ConfiguredDoorBird
from .models import DoorBirdData
def get_mac_address_from_door_station_info(door_station_info):
def get_mac_address_from_door_station_info(door_station_info: dict[str, Any]) -> str:
"""Get the mac address depending on the device type."""
return door_station_info.get("PRIMARY_MAC_ADDR", door_station_info["WIFI_MAC_ADDR"])

View File

@ -0,0 +1,58 @@
"""Support for DoorBird devices."""
from __future__ import annotations
from http import HTTPStatus
import logging
from aiohttp import web
from homeassistant.components.http import HomeAssistantView
from homeassistant.core import HomeAssistant
from .const import API_URL, DOMAIN
from .device import async_reset_device_favorites
from .util import get_door_station_by_token
_LOGGER = logging.getLogger(__name__)
class DoorBirdRequestView(HomeAssistantView):
"""Provide a page for the device to call."""
requires_auth = False
url = API_URL
name = API_URL[1:].replace("/", ":")
extra_urls = [API_URL + "/{event}"]
async def get(self, request: web.Request, event: str) -> web.Response:
"""Respond to requests from the device."""
hass: HomeAssistant = request.app["hass"]
token: str | None = request.query.get("token")
if (
token is None
or (door_station := get_door_station_by_token(hass, token)) is None
):
return web.Response(
status=HTTPStatus.UNAUTHORIZED, text="Invalid token provided."
)
if door_station:
event_data = door_station.get_event_data(event)
else:
event_data = {}
if event == "clear":
await async_reset_device_favorites(hass, door_station)
message = f"HTTP Favorites cleared for {door_station.slug}"
return web.Response(text=message)
#
# This integration uses a multiple different events.
# It would be a major breaking change to change this to
# a single event at this point.
#
# Do not copy this pattern in the future
# for any new integrations.
#
hass.bus.async_fire(f"{DOMAIN}_{event}", event_data)
return web.Response(text="OK")