Refactor doorbird to avoid using events internally (#98585)
parent
f07724ff52
commit
38af44225e
|
@ -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
|
||||||
|
|
|
@ -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")
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"])
|
||||||
|
|
||||||
|
|
|
@ -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")
|
Loading…
Reference in New Issue