mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 10:17:09 +00:00
Convert doorbird to use asyncio (#121569)
This commit is contained in:
parent
020961d2d8
commit
0e0a339517
@ -4,10 +4,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
from aiohttp import ClientResponseError
|
||||||
from doorbirdpy import DoorBird
|
from doorbirdpy import DoorBird
|
||||||
import requests
|
|
||||||
|
|
||||||
from homeassistant.components import persistent_notification
|
from homeassistant.components import persistent_notification
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@ -19,6 +18,7 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
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
|
||||||
|
|
||||||
@ -48,12 +48,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: DoorBirdConfigEntry) ->
|
|||||||
device_ip = door_station_config[CONF_HOST]
|
device_ip = door_station_config[CONF_HOST]
|
||||||
username = door_station_config[CONF_USERNAME]
|
username = door_station_config[CONF_USERNAME]
|
||||||
password = door_station_config[CONF_PASSWORD]
|
password = door_station_config[CONF_PASSWORD]
|
||||||
|
session = async_get_clientsession(hass)
|
||||||
|
|
||||||
device = DoorBird(device_ip, username, password)
|
device = DoorBird(device_ip, username, password, http_session=session)
|
||||||
try:
|
try:
|
||||||
status, info = await hass.async_add_executor_job(_init_door_bird_device, device)
|
status = await device.ready()
|
||||||
except requests.exceptions.HTTPError as err:
|
info = await device.info()
|
||||||
if err.response.status_code == HTTPStatus.UNAUTHORIZED:
|
except ClientResponseError as err:
|
||||||
|
if err.status == HTTPStatus.UNAUTHORIZED:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Authorization rejected by DoorBird for %s@%s", username, device_ip
|
"Authorization rejected by DoorBird for %s@%s", username, device_ip
|
||||||
)
|
)
|
||||||
@ -91,11 +93,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: DoorBirdConfigEntry) ->
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _init_door_bird_device(device: DoorBird) -> tuple[tuple[bool, int], dict[str, Any]]:
|
|
||||||
"""Verify we can connect to the device and return the status."""
|
|
||||||
return device.ready(), device.info()
|
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: DoorBirdConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: DoorBirdConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
@ -106,8 +103,8 @@ async def _async_register_events(
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
"""Register events on device."""
|
"""Register events on device."""
|
||||||
try:
|
try:
|
||||||
await hass.async_add_executor_job(door_station.register_events, hass)
|
await door_station.async_register_events(hass)
|
||||||
except requests.exceptions.HTTPError:
|
except ClientResponseError:
|
||||||
persistent_notification.async_create(
|
persistent_notification.async_create(
|
||||||
hass,
|
hass,
|
||||||
(
|
(
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"""Support for powering relays in a DoorBird video doorbell."""
|
"""Support for powering relays in a DoorBird video doorbell."""
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable, Coroutine
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from doorbirdpy import DoorBird
|
from doorbirdpy import DoorBird
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ IR_RELAY = "__ir_light__"
|
|||||||
class DoorbirdButtonEntityDescription(ButtonEntityDescription):
|
class DoorbirdButtonEntityDescription(ButtonEntityDescription):
|
||||||
"""Class to describe a Doorbird Button entity."""
|
"""Class to describe a Doorbird Button entity."""
|
||||||
|
|
||||||
press_action: Callable[[DoorBird, str], None]
|
press_action: Callable[[DoorBird, str], Coroutine[Any, Any, bool]]
|
||||||
|
|
||||||
|
|
||||||
RELAY_ENTITY_DESCRIPTION = DoorbirdButtonEntityDescription(
|
RELAY_ENTITY_DESCRIPTION = DoorbirdButtonEntityDescription(
|
||||||
@ -73,6 +74,8 @@ class DoorBirdButton(DoorBirdEntity, ButtonEntity):
|
|||||||
self._attr_name = f"Relay {self._relay}"
|
self._attr_name = f"Relay {self._relay}"
|
||||||
self._attr_unique_id = f"{self._mac_addr}_{self._relay}"
|
self._attr_unique_id = f"{self._mac_addr}_{self._relay}"
|
||||||
|
|
||||||
def press(self) -> None:
|
async def async_press(self) -> None:
|
||||||
"""Power the relay."""
|
"""Power the relay."""
|
||||||
self.entity_description.press_action(self._door_station.device, self._relay)
|
await self.entity_description.press_action(
|
||||||
|
self._door_station.device, self._relay
|
||||||
|
)
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -10,7 +9,6 @@ import aiohttp
|
|||||||
|
|
||||||
from homeassistant.components.camera import Camera, CameraEntityFeature
|
from homeassistant.components.camera import Camera, CameraEntityFeature
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
@ -95,11 +93,9 @@ class DoorBirdCamera(DoorBirdEntity, Camera):
|
|||||||
return self._last_image
|
return self._last_image
|
||||||
|
|
||||||
try:
|
try:
|
||||||
websession = async_get_clientsession(self.hass)
|
self._last_image = await self._door_station.device.get_image(
|
||||||
async with asyncio.timeout(_TIMEOUT):
|
self._url, timeout=_TIMEOUT
|
||||||
response = await websession.get(self._url)
|
)
|
||||||
|
|
||||||
self._last_image = await response.read()
|
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
_LOGGER.error("DoorBird %s: Camera image timed out", self.name)
|
_LOGGER.error("DoorBird %s: Camera image timed out", self.name)
|
||||||
return self._last_image
|
return self._last_image
|
||||||
|
@ -6,8 +6,8 @@ from http import HTTPStatus
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from aiohttp import ClientResponseError
|
||||||
from doorbirdpy import DoorBird
|
from doorbirdpy import DoorBird
|
||||||
import requests
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import zeroconf
|
from homeassistant.components import zeroconf
|
||||||
@ -20,6 +20,7 @@ from homeassistant.config_entries import (
|
|||||||
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
|
||||||
from .const import CONF_EVENTS, DOMAIN, DOORBIRD_OUI
|
from .const import CONF_EVENTS, DOMAIN, DOORBIRD_OUI
|
||||||
from .util import get_mac_address_from_door_station_info
|
from .util import get_mac_address_from_door_station_info
|
||||||
@ -40,18 +41,17 @@ def _schema_with_defaults(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _check_device(device: DoorBird) -> tuple[tuple[bool, int], dict[str, Any]]:
|
|
||||||
"""Verify we can connect to the device and return the status."""
|
|
||||||
return device.ready(), device.info()
|
|
||||||
|
|
||||||
|
|
||||||
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, str]:
|
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, str]:
|
||||||
"""Validate the user input allows us to connect."""
|
"""Validate the user input allows us to connect."""
|
||||||
device = DoorBird(data[CONF_HOST], data[CONF_USERNAME], data[CONF_PASSWORD])
|
session = async_get_clientsession(hass)
|
||||||
|
device = DoorBird(
|
||||||
|
data[CONF_HOST], data[CONF_USERNAME], data[CONF_PASSWORD], http_session=session
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
status, info = await hass.async_add_executor_job(_check_device, device)
|
status = await device.ready()
|
||||||
except requests.exceptions.HTTPError as err:
|
info = await device.info()
|
||||||
if err.response.status_code == HTTPStatus.UNAUTHORIZED:
|
except ClientResponseError as err:
|
||||||
|
if err.status == HTTPStatus.UNAUTHORIZED:
|
||||||
raise InvalidAuth from err
|
raise InvalidAuth from err
|
||||||
raise CannotConnect from err
|
raise CannotConnect from err
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
@ -68,11 +68,12 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str,
|
|||||||
|
|
||||||
async def async_verify_supported_device(hass: HomeAssistant, host: str) -> bool:
|
async def async_verify_supported_device(hass: HomeAssistant, host: str) -> bool:
|
||||||
"""Verify the doorbell state endpoint returns a 401."""
|
"""Verify the doorbell state endpoint returns a 401."""
|
||||||
device = DoorBird(host, "", "")
|
session = async_get_clientsession(hass)
|
||||||
|
device = DoorBird(host, "", "", http_session=session)
|
||||||
try:
|
try:
|
||||||
await hass.async_add_executor_job(device.doorbell_state)
|
await device.doorbell_state()
|
||||||
except requests.exceptions.HTTPError as err:
|
except ClientResponseError as err:
|
||||||
if err.response.status_code == HTTPStatus.UNAUTHORIZED:
|
if err.status == HTTPStatus.UNAUTHORIZED:
|
||||||
return True
|
return True
|
||||||
except OSError:
|
except OSError:
|
||||||
return False
|
return False
|
||||||
|
@ -75,7 +75,7 @@ class ConfiguredDoorBird:
|
|||||||
"""Get token for device."""
|
"""Get token for device."""
|
||||||
return self._token
|
return self._token
|
||||||
|
|
||||||
def register_events(self, hass: HomeAssistant) -> None:
|
async def async_register_events(self, hass: HomeAssistant) -> None:
|
||||||
"""Register events on device."""
|
"""Register events on device."""
|
||||||
# Override url if another is specified in the configuration
|
# Override url if another is specified in the configuration
|
||||||
if custom_url := self.custom_url:
|
if custom_url := self.custom_url:
|
||||||
@ -88,14 +88,14 @@ class ConfiguredDoorBird:
|
|||||||
# User may not have permission to get the favorites
|
# User may not have permission to get the favorites
|
||||||
return
|
return
|
||||||
|
|
||||||
favorites = self.device.favorites()
|
favorites = await self.device.favorites()
|
||||||
for event in self.door_station_events:
|
for event in self.door_station_events:
|
||||||
if self._register_event(hass_url, event, favs=favorites):
|
if await self._async_register_event(hass_url, event, favs=favorites):
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"Successfully registered URL for %s on %s", event, self.name
|
"Successfully registered URL for %s on %s", event, self.name
|
||||||
)
|
)
|
||||||
|
|
||||||
schedule: list[DoorBirdScheduleEntry] = self.device.schedule()
|
schedule: list[DoorBirdScheduleEntry] = await self.device.schedule()
|
||||||
http_fav: dict[str, dict[str, Any]] = favorites.get("http") or {}
|
http_fav: dict[str, dict[str, Any]] = favorites.get("http") or {}
|
||||||
favorite_input_type: dict[str, str] = {
|
favorite_input_type: dict[str, str] = {
|
||||||
output.param: entry.input
|
output.param: entry.input
|
||||||
@ -122,18 +122,18 @@ class ConfiguredDoorBird:
|
|||||||
def _get_event_name(self, event: str) -> str:
|
def _get_event_name(self, event: str) -> str:
|
||||||
return f"{self.slug}_{event}"
|
return f"{self.slug}_{event}"
|
||||||
|
|
||||||
def _register_event(
|
async def _async_register_event(
|
||||||
self, hass_url: str, event: str, favs: dict[str, Any] | None = None
|
self, hass_url: str, event: str, favs: dict[str, Any] | None = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Add a schedule entry in the device for a sensor."""
|
"""Add a schedule entry in the device for a sensor."""
|
||||||
url = f"{hass_url}{API_URL}/{event}?token={self._token}"
|
url = f"{hass_url}{API_URL}/{event}?token={self._token}"
|
||||||
|
|
||||||
# Register HA URL as webhook if not already, then get the ID
|
# Register HA URL as webhook if not already, then get the ID
|
||||||
if self.webhook_is_registered(url, favs=favs):
|
if await self.async_webhook_is_registered(url, favs=favs):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
self.device.change_favorite("http", f"Home Assistant ({event})", url)
|
await self.device.change_favorite("http", f"Home Assistant ({event})", url)
|
||||||
if not self.webhook_is_registered(url):
|
if not await self.async_webhook_is_registered(url):
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
'Unable to set favorite URL "%s". Event "%s" will not fire',
|
'Unable to set favorite URL "%s". Event "%s" will not fire',
|
||||||
url,
|
url,
|
||||||
@ -142,20 +142,20 @@ class ConfiguredDoorBird:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def webhook_is_registered(
|
async def async_webhook_is_registered(
|
||||||
self, url: str, favs: dict[str, Any] | None = None
|
self, url: str, favs: dict[str, Any] | None = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Return whether the given URL is registered as a device favorite."""
|
"""Return whether the given URL is registered as a device favorite."""
|
||||||
return self.get_webhook_id(url, favs) is not None
|
return await self.async_get_webhook_id(url, favs) is not None
|
||||||
|
|
||||||
def get_webhook_id(
|
async def async_get_webhook_id(
|
||||||
self, url: str, favs: dict[str, Any] | None = None
|
self, url: str, favs: dict[str, Any] | None = None
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Return the device favorite ID for the given URL.
|
"""Return the device favorite ID for the given URL.
|
||||||
|
|
||||||
The favorite must exist or there will be problems.
|
The favorite must exist or there will be problems.
|
||||||
"""
|
"""
|
||||||
favs = favs if favs else self.device.favorites()
|
favs = favs if favs else await self.device.favorites()
|
||||||
http_fav: dict[str, dict[str, Any]] = favs.get("http") or {}
|
http_fav: dict[str, dict[str, Any]] = favs.get("http") or {}
|
||||||
for fav_id, data in http_fav.items():
|
for fav_id, data in http_fav.items():
|
||||||
if data["value"] == url:
|
if data["value"] == url:
|
||||||
@ -178,14 +178,8 @@ async def async_reset_device_favorites(
|
|||||||
hass: HomeAssistant, door_station: ConfiguredDoorBird
|
hass: HomeAssistant, door_station: ConfiguredDoorBird
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Handle clearing favorites on device."""
|
"""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
|
door_bird = door_station.device
|
||||||
favorites: dict[str, list[str]] = door_bird.favorites()
|
favorites: dict[str, dict[str, Any]] = await door_bird.favorites()
|
||||||
for favorite_type, favorite_ids in favorites.items():
|
for favorite_type, favorite_ids in favorites.items():
|
||||||
for favorite_id in favorite_ids:
|
for favorite_id in favorite_ids:
|
||||||
door_bird.delete_favorite(favorite_type, favorite_id)
|
await door_bird.delete_favorite(favorite_type, favorite_id)
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/doorbird",
|
"documentation": "https://www.home-assistant.io/integrations/doorbird",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["doorbirdpy"],
|
"loggers": ["doorbirdpy"],
|
||||||
"requirements": ["DoorBirdPy==2.1.0"],
|
"requirements": ["DoorBirdPy==3.0.0"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_axis-video._tcp.local.",
|
"type": "_axis-video._tcp.local.",
|
||||||
|
@ -16,7 +16,7 @@ Adax-local==0.1.5
|
|||||||
BlinkStick==1.2.0
|
BlinkStick==1.2.0
|
||||||
|
|
||||||
# homeassistant.components.doorbird
|
# homeassistant.components.doorbird
|
||||||
DoorBirdPy==2.1.0
|
DoorBirdPy==3.0.0
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
# homeassistant.components.homekit
|
||||||
HAP-python==4.9.1
|
HAP-python==4.9.1
|
||||||
|
@ -13,7 +13,7 @@ AIOSomecomfort==0.0.25
|
|||||||
Adax-local==0.1.5
|
Adax-local==0.1.5
|
||||||
|
|
||||||
# homeassistant.components.doorbird
|
# homeassistant.components.doorbird
|
||||||
DoorBirdPy==2.1.0
|
DoorBirdPy==3.0.0
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
# homeassistant.components.homekit
|
||||||
HAP-python==4.9.1
|
HAP-python==4.9.1
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
"""Test the DoorBird config flow."""
|
"""Test the DoorBird config flow."""
|
||||||
|
|
||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
from unittest.mock import MagicMock, Mock, patch
|
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components import zeroconf
|
from homeassistant.components import zeroconf
|
||||||
@ -25,18 +25,20 @@ VALID_CONFIG = {
|
|||||||
|
|
||||||
def _get_mock_doorbirdapi_return_values(ready=None, info=None):
|
def _get_mock_doorbirdapi_return_values(ready=None, info=None):
|
||||||
doorbirdapi_mock = MagicMock()
|
doorbirdapi_mock = MagicMock()
|
||||||
type(doorbirdapi_mock).ready = MagicMock(return_value=ready)
|
type(doorbirdapi_mock).ready = AsyncMock(return_value=ready)
|
||||||
type(doorbirdapi_mock).info = MagicMock(return_value=info)
|
type(doorbirdapi_mock).info = AsyncMock(return_value=info)
|
||||||
type(doorbirdapi_mock).doorbell_state = MagicMock(
|
type(doorbirdapi_mock).doorbell_state = AsyncMock(
|
||||||
side_effect=requests.exceptions.HTTPError(response=Mock(status_code=401))
|
side_effect=aiohttp.ClientResponseError(
|
||||||
|
request_info=Mock(), history=Mock(), status=401
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return doorbirdapi_mock
|
return doorbirdapi_mock
|
||||||
|
|
||||||
|
|
||||||
def _get_mock_doorbirdapi_side_effects(ready=None, info=None):
|
def _get_mock_doorbirdapi_side_effects(ready=None, info=None):
|
||||||
doorbirdapi_mock = MagicMock()
|
doorbirdapi_mock = MagicMock()
|
||||||
type(doorbirdapi_mock).ready = MagicMock(side_effect=ready)
|
type(doorbirdapi_mock).ready = AsyncMock(side_effect=ready)
|
||||||
type(doorbirdapi_mock).info = MagicMock(side_effect=info)
|
type(doorbirdapi_mock).info = AsyncMock(side_effect=info)
|
||||||
|
|
||||||
return doorbirdapi_mock
|
return doorbirdapi_mock
|
||||||
|
|
||||||
@ -234,7 +236,7 @@ async def test_form_zeroconf_correct_oui(hass: HomeAssistant) -> None:
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"doorbell_state_side_effect",
|
"doorbell_state_side_effect",
|
||||||
[
|
[
|
||||||
requests.exceptions.HTTPError(response=Mock(status_code=404)),
|
aiohttp.ClientResponseError(request_info=Mock(), history=Mock(), status=404),
|
||||||
OSError,
|
OSError,
|
||||||
None,
|
None,
|
||||||
],
|
],
|
||||||
@ -246,7 +248,7 @@ async def test_form_zeroconf_correct_oui_wrong_device(
|
|||||||
doorbirdapi = _get_mock_doorbirdapi_return_values(
|
doorbirdapi = _get_mock_doorbirdapi_return_values(
|
||||||
ready=[True], info={"WIFI_MAC_ADDR": "macaddr"}
|
ready=[True], info={"WIFI_MAC_ADDR": "macaddr"}
|
||||||
)
|
)
|
||||||
type(doorbirdapi).doorbell_state = MagicMock(side_effect=doorbell_state_side_effect)
|
type(doorbirdapi).doorbell_state = AsyncMock(side_effect=doorbell_state_side_effect)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.doorbird.config_flow.DoorBird",
|
"homeassistant.components.doorbird.config_flow.DoorBird",
|
||||||
@ -296,7 +298,9 @@ async def test_form_user_invalid_auth(hass: HomeAssistant) -> None:
|
|||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
|
|
||||||
mock_error = requests.exceptions.HTTPError(response=Mock(status_code=401))
|
mock_error = aiohttp.ClientResponseError(
|
||||||
|
request_info=Mock(), history=Mock(), status=401
|
||||||
|
)
|
||||||
doorbirdapi = _get_mock_doorbirdapi_side_effects(ready=mock_error)
|
doorbirdapi = _get_mock_doorbirdapi_side_effects(ready=mock_error)
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.doorbird.config_flow.DoorBird",
|
"homeassistant.components.doorbird.config_flow.DoorBird",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user