Make Axis utilise forward_entry_setups (#75178)
parent
15f87ca0a1
commit
3b8650d053
|
@ -4,28 +4,36 @@ import logging
|
|||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_DEVICE, CONF_MAC, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
from homeassistant.helpers.entity_registry import async_migrate_entries
|
||||
|
||||
from .const import DOMAIN as AXIS_DOMAIN
|
||||
from .device import AxisNetworkDevice
|
||||
from .const import DOMAIN as AXIS_DOMAIN, PLATFORMS
|
||||
from .device import AxisNetworkDevice, get_axis_device
|
||||
from .errors import AuthenticationRequired, CannotConnect
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Set up the Axis component."""
|
||||
"""Set up the Axis integration."""
|
||||
hass.data.setdefault(AXIS_DOMAIN, {})
|
||||
|
||||
device = AxisNetworkDevice(hass, config_entry)
|
||||
|
||||
if not await device.async_setup():
|
||||
return False
|
||||
|
||||
hass.data[AXIS_DOMAIN][config_entry.unique_id] = device
|
||||
try:
|
||||
api = await get_axis_device(hass, config_entry.data)
|
||||
except CannotConnect as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
except AuthenticationRequired as err:
|
||||
raise ConfigEntryAuthFailed from err
|
||||
|
||||
device = hass.data[AXIS_DOMAIN][config_entry.unique_id] = AxisNetworkDevice(
|
||||
hass, config_entry, api
|
||||
)
|
||||
await device.async_update_device_registry()
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
device.async_setup_events()
|
||||
|
||||
config_entry.add_update_listener(device.async_new_address_callback)
|
||||
config_entry.async_on_unload(
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, device.shutdown)
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
|
||||
from collections.abc import Mapping
|
||||
from ipaddress import ip_address
|
||||
from types import MappingProxyType
|
||||
from typing import Any
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
|
@ -32,7 +33,7 @@ from .const import (
|
|||
DEFAULT_VIDEO_SOURCE,
|
||||
DOMAIN as AXIS_DOMAIN,
|
||||
)
|
||||
from .device import AxisNetworkDevice, get_device
|
||||
from .device import AxisNetworkDevice, get_axis_device
|
||||
from .errors import AuthenticationRequired, CannotConnect
|
||||
|
||||
AXIS_OUI = {"00:40:8c", "ac:cc:8e", "b8:a4:4f"}
|
||||
|
@ -66,13 +67,7 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN):
|
|||
|
||||
if user_input is not None:
|
||||
try:
|
||||
device = await get_device(
|
||||
self.hass,
|
||||
host=user_input[CONF_HOST],
|
||||
port=user_input[CONF_PORT],
|
||||
username=user_input[CONF_USERNAME],
|
||||
password=user_input[CONF_PASSWORD],
|
||||
)
|
||||
device = await get_axis_device(self.hass, MappingProxyType(user_input))
|
||||
|
||||
serial = device.vapix.serial_number
|
||||
await self.async_set_unique_id(format_mac(serial))
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""Axis network device abstraction."""
|
||||
|
||||
import asyncio
|
||||
from types import MappingProxyType
|
||||
from typing import Any
|
||||
|
||||
import async_timeout
|
||||
import axis
|
||||
|
@ -24,7 +26,6 @@ from homeassistant.const import (
|
|||
CONF_USERNAME,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
|
@ -50,15 +51,15 @@ from .errors import AuthenticationRequired, CannotConnect
|
|||
class AxisNetworkDevice:
|
||||
"""Manages a Axis device."""
|
||||
|
||||
def __init__(self, hass, config_entry):
|
||||
def __init__(self, hass, config_entry, api):
|
||||
"""Initialize the device."""
|
||||
self.hass = hass
|
||||
self.config_entry = config_entry
|
||||
self.available = True
|
||||
self.api = api
|
||||
|
||||
self.api = None
|
||||
self.fw_version = None
|
||||
self.product_type = None
|
||||
self.available = True
|
||||
self.fw_version = api.vapix.firmware_version
|
||||
self.product_type = api.vapix.product_type
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
|
@ -184,7 +185,7 @@ class AxisNetworkDevice:
|
|||
sw_version=self.fw_version,
|
||||
)
|
||||
|
||||
async def use_mqtt(self, hass: HomeAssistant, component: str) -> None:
|
||||
async def async_use_mqtt(self, hass: HomeAssistant, component: str) -> None:
|
||||
"""Set up to use MQTT."""
|
||||
try:
|
||||
status = await self.api.vapix.mqtt.get_client_status()
|
||||
|
@ -209,50 +210,18 @@ class AxisNetworkDevice:
|
|||
|
||||
# Setup and teardown methods
|
||||
|
||||
async def async_setup(self):
|
||||
"""Set up the device."""
|
||||
try:
|
||||
self.api = await get_device(
|
||||
self.hass,
|
||||
host=self.host,
|
||||
port=self.port,
|
||||
username=self.username,
|
||||
password=self.password,
|
||||
def async_setup_events(self):
|
||||
"""Set up the device events."""
|
||||
|
||||
if self.option_events:
|
||||
self.api.stream.connection_status_callback.append(
|
||||
self.async_connection_status_callback
|
||||
)
|
||||
self.api.enable_events(event_callback=self.async_event_callback)
|
||||
self.api.stream.start()
|
||||
|
||||
except CannotConnect as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
except AuthenticationRequired as err:
|
||||
raise ConfigEntryAuthFailed from err
|
||||
|
||||
self.fw_version = self.api.vapix.firmware_version
|
||||
self.product_type = self.api.vapix.product_type
|
||||
|
||||
async def start_platforms():
|
||||
await asyncio.gather(
|
||||
*(
|
||||
self.hass.config_entries.async_forward_entry_setup(
|
||||
self.config_entry, platform
|
||||
)
|
||||
for platform in PLATFORMS
|
||||
)
|
||||
)
|
||||
if self.option_events:
|
||||
self.api.stream.connection_status_callback.append(
|
||||
self.async_connection_status_callback
|
||||
)
|
||||
self.api.enable_events(event_callback=self.async_event_callback)
|
||||
self.api.stream.start()
|
||||
|
||||
if self.api.vapix.mqtt:
|
||||
async_when_setup(self.hass, MQTT_DOMAIN, self.use_mqtt)
|
||||
|
||||
self.hass.async_create_task(start_platforms())
|
||||
|
||||
self.config_entry.add_update_listener(self.async_new_address_callback)
|
||||
|
||||
return True
|
||||
if self.api.vapix.mqtt:
|
||||
async_when_setup(self.hass, MQTT_DOMAIN, self.async_use_mqtt)
|
||||
|
||||
@callback
|
||||
def disconnect_from_stream(self):
|
||||
|
@ -274,14 +243,21 @@ class AxisNetworkDevice:
|
|||
)
|
||||
|
||||
|
||||
async def get_device(
|
||||
hass: HomeAssistant, host: str, port: int, username: str, password: str
|
||||
async def get_axis_device(
|
||||
hass: HomeAssistant,
|
||||
config: MappingProxyType[str, Any],
|
||||
) -> axis.AxisDevice:
|
||||
"""Create a Axis device."""
|
||||
session = get_async_client(hass, verify_ssl=False)
|
||||
|
||||
device = axis.AxisDevice(
|
||||
Configuration(session, host, port=port, username=username, password=password)
|
||||
Configuration(
|
||||
session,
|
||||
config[CONF_HOST],
|
||||
port=config[CONF_PORT],
|
||||
username=config[CONF_USERNAME],
|
||||
password=config[CONF_PASSWORD],
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
|
@ -291,11 +267,13 @@ async def get_device(
|
|||
return device
|
||||
|
||||
except axis.Unauthorized as err:
|
||||
LOGGER.warning("Connected to device at %s but not registered", host)
|
||||
LOGGER.warning(
|
||||
"Connected to device at %s but not registered", config[CONF_HOST]
|
||||
)
|
||||
raise AuthenticationRequired from err
|
||||
|
||||
except (asyncio.TimeoutError, axis.RequestError) as err:
|
||||
LOGGER.error("Error connecting to the Axis device at %s", host)
|
||||
LOGGER.error("Error connecting to the Axis device at %s", config[CONF_HOST])
|
||||
raise CannotConnect from err
|
||||
|
||||
except axis.AxisException as err:
|
||||
|
|
|
@ -123,7 +123,7 @@ async def test_flow_fails_faulty_credentials(hass):
|
|||
assert result["step_id"] == SOURCE_USER
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.axis.config_flow.get_device",
|
||||
"homeassistant.components.axis.config_flow.get_axis_device",
|
||||
side_effect=config_flow.AuthenticationRequired,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
|
@ -149,7 +149,7 @@ async def test_flow_fails_cannot_connect(hass):
|
|||
assert result["step_id"] == SOURCE_USER
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.axis.config_flow.get_device",
|
||||
"homeassistant.components.axis.config_flow.get_axis_device",
|
||||
side_effect=config_flow.CannotConnect,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
|
|
|
@ -441,7 +441,7 @@ async def test_device_reset(hass):
|
|||
|
||||
async def test_device_not_accessible(hass):
|
||||
"""Failed setup schedules a retry of setup."""
|
||||
with patch.object(axis.device, "get_device", side_effect=axis.errors.CannotConnect):
|
||||
with patch.object(axis, "get_axis_device", side_effect=axis.errors.CannotConnect):
|
||||
await setup_axis_integration(hass)
|
||||
assert hass.data[AXIS_DOMAIN] == {}
|
||||
|
||||
|
@ -449,7 +449,7 @@ async def test_device_not_accessible(hass):
|
|||
async def test_device_trigger_reauth_flow(hass):
|
||||
"""Failed authentication trigger a reauthentication flow."""
|
||||
with patch.object(
|
||||
axis.device, "get_device", side_effect=axis.errors.AuthenticationRequired
|
||||
axis, "get_axis_device", side_effect=axis.errors.AuthenticationRequired
|
||||
), patch.object(hass.config_entries.flow, "async_init") as mock_flow_init:
|
||||
await setup_axis_integration(hass)
|
||||
mock_flow_init.assert_called_once()
|
||||
|
@ -458,7 +458,7 @@ async def test_device_trigger_reauth_flow(hass):
|
|||
|
||||
async def test_device_unknown_error(hass):
|
||||
"""Unknown errors are handled."""
|
||||
with patch.object(axis.device, "get_device", side_effect=Exception):
|
||||
with patch.object(axis, "get_axis_device", side_effect=Exception):
|
||||
await setup_axis_integration(hass)
|
||||
assert hass.data[AXIS_DOMAIN] == {}
|
||||
|
||||
|
@ -468,7 +468,7 @@ async def test_new_event_sends_signal(hass):
|
|||
entry = Mock()
|
||||
entry.data = ENTRY_CONFIG
|
||||
|
||||
axis_device = axis.device.AxisNetworkDevice(hass, entry)
|
||||
axis_device = axis.device.AxisNetworkDevice(hass, entry, Mock())
|
||||
|
||||
with patch.object(axis.device, "async_dispatcher_send") as mock_dispatch_send:
|
||||
axis_device.async_event_callback(action=OPERATION_INITIALIZED, event_id="event")
|
||||
|
@ -484,8 +484,7 @@ async def test_shutdown():
|
|||
entry = Mock()
|
||||
entry.data = ENTRY_CONFIG
|
||||
|
||||
axis_device = axis.device.AxisNetworkDevice(hass, entry)
|
||||
axis_device.api = Mock()
|
||||
axis_device = axis.device.AxisNetworkDevice(hass, entry, Mock())
|
||||
|
||||
await axis_device.shutdown(None)
|
||||
|
||||
|
@ -497,7 +496,7 @@ async def test_get_device_fails(hass):
|
|||
with patch(
|
||||
"axis.vapix.Vapix.request", side_effect=axislib.Unauthorized
|
||||
), pytest.raises(axis.errors.AuthenticationRequired):
|
||||
await axis.device.get_device(hass, host="", port="", username="", password="")
|
||||
await axis.device.get_axis_device(hass, ENTRY_CONFIG)
|
||||
|
||||
|
||||
async def test_get_device_device_unavailable(hass):
|
||||
|
@ -505,7 +504,7 @@ async def test_get_device_device_unavailable(hass):
|
|||
with patch(
|
||||
"axis.vapix.Vapix.request", side_effect=axislib.RequestError
|
||||
), pytest.raises(axis.errors.CannotConnect):
|
||||
await axis.device.get_device(hass, host="", port="", username="", password="")
|
||||
await axis.device.get_axis_device(hass, ENTRY_CONFIG)
|
||||
|
||||
|
||||
async def test_get_device_unknown_error(hass):
|
||||
|
@ -513,4 +512,4 @@ async def test_get_device_unknown_error(hass):
|
|||
with patch(
|
||||
"axis.vapix.Vapix.request", side_effect=axislib.AxisException
|
||||
), pytest.raises(axis.errors.AuthenticationRequired):
|
||||
await axis.device.get_device(hass, host="", port="", username="", password="")
|
||||
await axis.device.get_axis_device(hass, ENTRY_CONFIG)
|
||||
|
|
Loading…
Reference in New Issue