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/device.py
homeassistant/components/doorbird/entity.py homeassistant/components/doorbird/entity.py
homeassistant/components/doorbird/util.py homeassistant/components/doorbird/util.py
homeassistant/components/doorbird/view.py
homeassistant/components/dormakaba_dkey/__init__.py homeassistant/components/dormakaba_dkey/__init__.py
homeassistant/components/dormakaba_dkey/binary_sensor.py homeassistant/components/dormakaba_dkey/binary_sensor.py
homeassistant/components/dormakaba_dkey/entity.py homeassistant/components/dormakaba_dkey/entity.py

View File

@ -5,13 +5,11 @@ from http import HTTPStatus
import logging import logging
from typing import Any from typing import Any
from aiohttp import web
from doorbirdpy import DoorBird from doorbirdpy import DoorBird
import requests import requests
import voluptuous as vol import voluptuous as vol
from homeassistant.components import persistent_notification from homeassistant.components import persistent_notification
from homeassistant.components.http import HomeAssistantView
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_HOST,
@ -20,21 +18,20 @@ from homeassistant.const import (
CONF_TOKEN, CONF_TOKEN,
CONF_USERNAME, CONF_USERNAME,
) )
from homeassistant.core import Event, HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType 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 .device import ConfiguredDoorBird
from .models import DoorBirdData from .models import DoorBirdData
from .util import get_door_station_by_token from .view import DoorBirdRequestView
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONF_CUSTOM_URL = "hass_url_override" CONF_CUSTOM_URL = "hass_url_override"
RESET_DEVICE_FAVORITES = "doorbird_reset_favorites"
DEVICE_SCHEMA = vol.Schema( 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: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the DoorBird component.""" """Set up the DoorBird component."""
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
# Provide an endpoint for the door stations to call to trigger events # Provide an endpoint for the door stations to call to trigger events
hass.http.register_view(DoorBirdRequestView) 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 return True
@ -150,6 +126,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def _async_register_events( async def _async_register_events(
hass: HomeAssistant, door_station: ConfiguredDoorBird hass: HomeAssistant, door_station: ConfiguredDoorBird
) -> bool: ) -> bool:
"""Register events on device."""
try: try:
await hass.async_add_executor_job(door_station.register_events, hass) await hass.async_add_executor_job(door_station.register_events, hass)
except requests.exceptions.HTTPError: except requests.exceptions.HTTPError:
@ -190,36 +167,3 @@ def _async_import_options_from_data_if_missing(hass: HomeAssistant, entry: Confi
if modified: if modified:
hass.config_entries.async_update_entry(entry, options=options) 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.""" """Unsubscribe from events."""
event_to_entity_id = self._door_bird_data.event_entity_ids event_to_entity_id = self._door_bird_data.event_entity_ids
for event in self._door_station.events: 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() await super().async_will_remove_from_hass()

View File

@ -145,3 +145,20 @@ class ConfiguredDoorBird:
"html5_viewer_url": self._device.html5_viewer_url, "html5_viewer_url": self._device.html5_viewer_url,
ATTR_ENTITY_ID: self._event_entity_ids.get(event), 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.""" """DoorBird integration utils."""
from typing import Any
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import DOMAIN from .const import DOMAIN
@ -7,7 +9,7 @@ from .device import ConfiguredDoorBird
from .models import DoorBirdData 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.""" """Get the mac address depending on the device type."""
return door_station_info.get("PRIMARY_MAC_ADDR", door_station_info["WIFI_MAC_ADDR"]) 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")