Make type checking pass for deCONZ init, gateway and services (#66054)

* Type and enable type checking for init, config_flow, diagnostics, gateway and services

* Fix import

* Fix review comment
This commit is contained in:
Robert Svensson 2022-02-23 13:10:35 +01:00 committed by GitHub
parent 3afadf8adb
commit dd88a05cb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 180 additions and 150 deletions

View file

@ -58,6 +58,11 @@ homeassistant.components.canary.*
homeassistant.components.cover.* homeassistant.components.cover.*
homeassistant.components.crownstone.* homeassistant.components.crownstone.*
homeassistant.components.cpuspeed.* homeassistant.components.cpuspeed.*
homeassistant.components.deconz
homeassistant.components.deconz.config_flow
homeassistant.components.deconz.diagnostics
homeassistant.components.deconz.gateway
homeassistant.components.deconz.services
homeassistant.components.device_automation.* homeassistant.components.device_automation.*
homeassistant.components.device_tracker.* homeassistant.components.device_tracker.*
homeassistant.components.devolo_home_control.* homeassistant.components.devolo_home_control.*

View file

@ -12,11 +12,14 @@ from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_STOP,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
import homeassistant.helpers.entity_registry as er import homeassistant.helpers.entity_registry as er
from .config_flow import get_master_gateway from .config_flow import get_master_gateway
from .const import CONF_GROUP_ID_BASE, CONF_MASTER_GATEWAY, DOMAIN from .const import CONF_GROUP_ID_BASE, CONF_MASTER_GATEWAY, DOMAIN, PLATFORMS
from .gateway import DeconzGateway from .deconz_event import async_setup_events, async_unload_events
from .errors import AuthenticationRequired, CannotConnect
from .gateway import DeconzGateway, get_deconz_session
from .services import async_setup_services, async_unload_services from .services import async_setup_services, async_unload_services
@ -33,16 +36,27 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
if not config_entry.options: if not config_entry.options:
await async_update_master_gateway(hass, config_entry) await async_update_master_gateway(hass, config_entry)
gateway = DeconzGateway(hass, config_entry) try:
if not await gateway.async_setup(): api = await get_deconz_session(hass, config_entry.data)
return False except CannotConnect as err:
raise ConfigEntryNotReady from err
except AuthenticationRequired as err:
raise ConfigEntryAuthFailed from err
if not hass.data[DOMAIN]: gateway = hass.data[DOMAIN][config_entry.entry_id] = DeconzGateway(
hass, config_entry, api
)
config_entry.add_update_listener(gateway.async_config_entry_updated)
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
await async_setup_events(gateway)
await gateway.async_update_device_registry()
if len(hass.data[DOMAIN]) == 1:
async_setup_services(hass) async_setup_services(hass)
hass.data[DOMAIN][config_entry.entry_id] = gateway api.start()
await gateway.async_update_device_registry()
config_entry.async_on_unload( config_entry.async_on_unload(
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, gateway.shutdown) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, gateway.shutdown)
@ -53,7 +67,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Unload deCONZ config entry.""" """Unload deCONZ config entry."""
gateway = hass.data[DOMAIN].pop(config_entry.entry_id) gateway: DeconzGateway = hass.data[DOMAIN].pop(config_entry.entry_id)
async_unload_events(gateway)
if not hass.data[DOMAIN]: if not hass.data[DOMAIN]:
async_unload_services(hass) async_unload_services(hass)
@ -89,9 +104,10 @@ async def async_update_group_unique_id(
hass: HomeAssistant, config_entry: ConfigEntry hass: HomeAssistant, config_entry: ConfigEntry
) -> None: ) -> None:
"""Update unique ID entities based on deCONZ groups.""" """Update unique ID entities based on deCONZ groups."""
if not isinstance(old_unique_id := config_entry.data.get(CONF_GROUP_ID_BASE), str): if not (group_id_base := config_entry.data.get(CONF_GROUP_ID_BASE)):
return return
old_unique_id = cast(str, group_id_base)
new_unique_id = cast(str, config_entry.unique_id) new_unique_id = cast(str, config_entry.unique_id)
@callback @callback

View file

@ -1,13 +1,21 @@
"""Representation of a deCONZ gateway.""" """Representation of a deCONZ gateway."""
from __future__ import annotations
import asyncio import asyncio
from types import MappingProxyType
from typing import Any, cast
import async_timeout import async_timeout
from pydeconz import DeconzSession, errors, group, light, sensor from pydeconz import DeconzSession, errors, group, light, sensor
from pydeconz.alarm_system import AlarmSystem as DeconzAlarmSystem
from pydeconz.group import Group as DeconzGroup
from pydeconz.light import DeconzLight
from pydeconz.sensor import DeconzSensor
from homeassistant.config_entries import SOURCE_HASSIO, ConfigEntry from homeassistant.config_entries import SOURCE_HASSIO, ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant, callback from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import ( from homeassistant.helpers import (
aiohttp_client, aiohttp_client,
device_registry as dr, device_registry as dr,
@ -29,25 +37,23 @@ from .const import (
LOGGER, LOGGER,
PLATFORMS, PLATFORMS,
) )
from .deconz_event import async_setup_events, async_unload_events from .deconz_event import DeconzAlarmEvent, DeconzEvent
from .errors import AuthenticationRequired, CannotConnect from .errors import AuthenticationRequired, CannotConnect
@callback
def get_gateway_from_config_entry(hass, config_entry):
"""Return gateway with a matching config entry ID."""
return hass.data[DECONZ_DOMAIN][config_entry.entry_id]
class DeconzGateway: class DeconzGateway:
"""Manages a single deCONZ gateway.""" """Manages a single deCONZ gateway."""
def __init__(self, hass, config_entry) -> None: def __init__(
self, hass: HomeAssistant, config_entry: ConfigEntry, api: DeconzSession
) -> None:
"""Initialize the system.""" """Initialize the system."""
self.hass = hass self.hass = hass
self.config_entry = config_entry self.config_entry = config_entry
self.api = api
self.api = None api.add_device_callback = self.async_add_device_callback
api.connection_status_callback = self.async_connection_status_callback
self.available = True self.available = True
self.ignore_state_updates = False self.ignore_state_updates = False
@ -66,24 +72,24 @@ class DeconzGateway:
sensor.RESOURCE_TYPE: self.signal_new_sensor, sensor.RESOURCE_TYPE: self.signal_new_sensor,
} }
self.deconz_ids = {} self.deconz_ids: dict[str, str] = {}
self.entities = {} self.entities: dict[str, set[str]] = {}
self.events = [] self.events: list[DeconzAlarmEvent | DeconzEvent] = []
@property @property
def bridgeid(self) -> str: def bridgeid(self) -> str:
"""Return the unique identifier of the gateway.""" """Return the unique identifier of the gateway."""
return self.config_entry.unique_id return cast(str, self.config_entry.unique_id)
@property @property
def host(self) -> str: def host(self) -> str:
"""Return the host of the gateway.""" """Return the host of the gateway."""
return self.config_entry.data[CONF_HOST] return cast(str, self.config_entry.data[CONF_HOST])
@property @property
def master(self) -> bool: def master(self) -> bool:
"""Gateway which is used with deCONZ services without defining id.""" """Gateway which is used with deCONZ services without defining id."""
return self.config_entry.options[CONF_MASTER_GATEWAY] return cast(bool, self.config_entry.options[CONF_MASTER_GATEWAY])
# Options # Options
@ -111,7 +117,7 @@ class DeconzGateway:
# Callbacks # Callbacks
@callback @callback
def async_connection_status_callback(self, available) -> None: def async_connection_status_callback(self, available: bool) -> None:
"""Handle signals of gateway connection status.""" """Handle signals of gateway connection status."""
self.available = available self.available = available
self.ignore_state_updates = False self.ignore_state_updates = False
@ -119,7 +125,15 @@ class DeconzGateway:
@callback @callback
def async_add_device_callback( def async_add_device_callback(
self, resource_type, device=None, force: bool = False self,
resource_type: str,
device: DeconzAlarmSystem
| DeconzGroup
| DeconzLight
| DeconzSensor
| list[DeconzAlarmSystem | DeconzGroup | DeconzLight | DeconzSensor]
| None = None,
force: bool = False,
) -> None: ) -> None:
"""Handle event of new device creation in deCONZ.""" """Handle event of new device creation in deCONZ."""
if ( if (
@ -166,32 +180,6 @@ class DeconzGateway:
via_device=(CONNECTION_NETWORK_MAC, self.api.config.mac), via_device=(CONNECTION_NETWORK_MAC, self.api.config.mac),
) )
async def async_setup(self) -> bool:
"""Set up a deCONZ gateway."""
try:
self.api = await get_gateway(
self.hass,
self.config_entry.data,
self.async_add_device_callback,
self.async_connection_status_callback,
)
except CannotConnect as err:
raise ConfigEntryNotReady from err
except AuthenticationRequired as err:
raise ConfigEntryAuthFailed from err
self.hass.config_entries.async_setup_platforms(self.config_entry, PLATFORMS)
await async_setup_events(self)
self.api.start()
self.config_entry.add_update_listener(self.async_config_entry_updated)
return True
@staticmethod @staticmethod
async def async_config_entry_updated( async def async_config_entry_updated(
hass: HomeAssistant, entry: ConfigEntry hass: HomeAssistant, entry: ConfigEntry
@ -211,7 +199,7 @@ class DeconzGateway:
await gateway.options_updated() await gateway.options_updated()
async def options_updated(self): async def options_updated(self) -> None:
"""Manage entities affected by config entry options.""" """Manage entities affected by config entry options."""
deconz_ids = [] deconz_ids = []
@ -242,14 +230,14 @@ class DeconzGateway:
entity_registry.async_remove(entity_id) entity_registry.async_remove(entity_id)
@callback @callback
def shutdown(self, event) -> None: def shutdown(self, event: Event) -> None:
"""Wrap the call to deconz.close. """Wrap the call to deconz.close.
Used as an argument to EventBus.async_listen_once. Used as an argument to EventBus.async_listen_once.
""" """
self.api.close() self.api.close()
async def async_reset(self): async def async_reset(self) -> bool:
"""Reset this gateway to default state.""" """Reset this gateway to default state."""
self.api.async_connection_status_callback = None self.api.async_connection_status_callback = None
self.api.close() self.api.close()
@ -258,30 +246,35 @@ class DeconzGateway:
self.config_entry, PLATFORMS self.config_entry, PLATFORMS
) )
async_unload_events(self)
self.deconz_ids = {} self.deconz_ids = {}
return True return True
async def get_gateway( @callback
hass, config, async_add_device_callback, async_connection_status_callback def get_gateway_from_config_entry(
hass: HomeAssistant, config_entry: ConfigEntry
) -> DeconzGateway:
"""Return gateway with a matching config entry ID."""
return cast(DeconzGateway, hass.data[DECONZ_DOMAIN][config_entry.entry_id])
async def get_deconz_session(
hass: HomeAssistant,
config: MappingProxyType[str, Any],
) -> DeconzSession: ) -> DeconzSession:
"""Create a gateway object and verify configuration.""" """Create a gateway object and verify configuration."""
session = aiohttp_client.async_get_clientsession(hass) session = aiohttp_client.async_get_clientsession(hass)
deconz = DeconzSession( deconz_session = DeconzSession(
session, session,
config[CONF_HOST], config[CONF_HOST],
config[CONF_PORT], config[CONF_PORT],
config[CONF_API_KEY], config[CONF_API_KEY],
add_device=async_add_device_callback,
connection_status=async_connection_status_callback,
) )
try: try:
async with async_timeout.timeout(10): async with async_timeout.timeout(10):
await deconz.refresh_state() await deconz_session.refresh_state()
return deconz return deconz_session
except errors.Unauthorized as err: except errors.Unauthorized as err:
LOGGER.warning("Invalid key for deCONZ at %s", config[CONF_HOST]) LOGGER.warning("Invalid key for deCONZ at %s", config[CONF_HOST])

View file

@ -1,7 +1,5 @@
"""deCONZ services.""" """deCONZ services."""
from types import MappingProxyType
from pydeconz.utils import normalize_bridge_id from pydeconz.utils import normalize_bridge_id
import voluptuous as vol import voluptuous as vol
@ -16,6 +14,7 @@ from homeassistant.helpers.entity_registry import (
async_entries_for_config_entry, async_entries_for_config_entry,
async_entries_for_device, async_entries_for_device,
) )
from homeassistant.util.read_only_dict import ReadOnlyDict
from .config_flow import get_master_gateway from .config_flow import get_master_gateway
from .const import CONF_BRIDGE_ID, DOMAIN, LOGGER from .const import CONF_BRIDGE_ID, DOMAIN, LOGGER
@ -111,9 +110,7 @@ def async_unload_services(hass: HomeAssistant) -> None:
hass.services.async_remove(DOMAIN, service) hass.services.async_remove(DOMAIN, service)
async def async_configure_service( async def async_configure_service(gateway: DeconzGateway, data: ReadOnlyDict) -> None:
gateway: DeconzGateway, data: MappingProxyType
) -> None:
"""Set attribute of device in deCONZ. """Set attribute of device in deCONZ.
Entity is used to resolve to a device path (e.g. '/lights/1'). Entity is used to resolve to a device path (e.g. '/lights/1').

View file

@ -447,6 +447,61 @@ no_implicit_optional = true
warn_return_any = true warn_return_any = true
warn_unreachable = true warn_unreachable = true
[mypy-homeassistant.components.deconz]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.deconz.config_flow]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.deconz.diagnostics]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.deconz.gateway]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.deconz.services]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.device_automation.*] [mypy-homeassistant.components.device_automation.*]
check_untyped_defs = true check_untyped_defs = true
disallow_incomplete_defs = true disallow_incomplete_defs = true
@ -2209,9 +2264,6 @@ ignore_errors = true
[mypy-homeassistant.components.conversation.default_agent] [mypy-homeassistant.components.conversation.default_agent]
ignore_errors = true ignore_errors = true
[mypy-homeassistant.components.deconz]
ignore_errors = true
[mypy-homeassistant.components.deconz.alarm_control_panel] [mypy-homeassistant.components.deconz.alarm_control_panel]
ignore_errors = true ignore_errors = true
@ -2227,9 +2279,6 @@ ignore_errors = true
[mypy-homeassistant.components.deconz.fan] [mypy-homeassistant.components.deconz.fan]
ignore_errors = true ignore_errors = true
[mypy-homeassistant.components.deconz.gateway]
ignore_errors = true
[mypy-homeassistant.components.deconz.light] [mypy-homeassistant.components.deconz.light]
ignore_errors = true ignore_errors = true
@ -2245,9 +2294,6 @@ ignore_errors = true
[mypy-homeassistant.components.deconz.sensor] [mypy-homeassistant.components.deconz.sensor]
ignore_errors = true ignore_errors = true
[mypy-homeassistant.components.deconz.services]
ignore_errors = true
[mypy-homeassistant.components.deconz.siren] [mypy-homeassistant.components.deconz.siren]
ignore_errors = true ignore_errors = true

View file

@ -23,19 +23,16 @@ IGNORED_MODULES: Final[list[str]] = [
"homeassistant.components.cloud.http_api", "homeassistant.components.cloud.http_api",
"homeassistant.components.conversation", "homeassistant.components.conversation",
"homeassistant.components.conversation.default_agent", "homeassistant.components.conversation.default_agent",
"homeassistant.components.deconz",
"homeassistant.components.deconz.alarm_control_panel", "homeassistant.components.deconz.alarm_control_panel",
"homeassistant.components.deconz.binary_sensor", "homeassistant.components.deconz.binary_sensor",
"homeassistant.components.deconz.climate", "homeassistant.components.deconz.climate",
"homeassistant.components.deconz.cover", "homeassistant.components.deconz.cover",
"homeassistant.components.deconz.fan", "homeassistant.components.deconz.fan",
"homeassistant.components.deconz.gateway",
"homeassistant.components.deconz.light", "homeassistant.components.deconz.light",
"homeassistant.components.deconz.lock", "homeassistant.components.deconz.lock",
"homeassistant.components.deconz.logbook", "homeassistant.components.deconz.logbook",
"homeassistant.components.deconz.number", "homeassistant.components.deconz.number",
"homeassistant.components.deconz.sensor", "homeassistant.components.deconz.sensor",
"homeassistant.components.deconz.services",
"homeassistant.components.deconz.siren", "homeassistant.components.deconz.siren",
"homeassistant.components.deconz.switch", "homeassistant.components.deconz.switch",
"homeassistant.components.denonavr.config_flow", "homeassistant.components.denonavr.config_flow",

View file

@ -1,7 +1,8 @@
"""Test deCONZ gateway.""" """Test deCONZ gateway."""
import asyncio
from copy import deepcopy from copy import deepcopy
from unittest.mock import Mock, patch from unittest.mock import patch
import pydeconz import pydeconz
from pydeconz.websocket import STATE_RETRYING, STATE_RUNNING from pydeconz.websocket import STATE_RETRYING, STATE_RUNNING
@ -19,7 +20,7 @@ from homeassistant.components.deconz.config_flow import DECONZ_MANUFACTURERURL
from homeassistant.components.deconz.const import DOMAIN as DECONZ_DOMAIN from homeassistant.components.deconz.const import DOMAIN as DECONZ_DOMAIN
from homeassistant.components.deconz.errors import AuthenticationRequired, CannotConnect from homeassistant.components.deconz.errors import AuthenticationRequired, CannotConnect
from homeassistant.components.deconz.gateway import ( from homeassistant.components.deconz.gateway import (
get_gateway, get_deconz_session,
get_gateway_from_config_entry, get_gateway_from_config_entry,
) )
from homeassistant.components.fan import DOMAIN as FAN_DOMAIN from homeassistant.components.fan import DOMAIN as FAN_DOMAIN
@ -202,25 +203,6 @@ async def test_gateway_device_configuration_url_when_addon(hass, aioclient_mock)
) )
async def test_gateway_retry(hass):
"""Retry setup."""
with patch(
"homeassistant.components.deconz.gateway.get_gateway",
side_effect=CannotConnect,
):
await setup_deconz_integration(hass)
assert not hass.data[DECONZ_DOMAIN]
async def test_gateway_setup_fails(hass):
"""Retry setup."""
with patch(
"homeassistant.components.deconz.gateway.get_gateway", side_effect=Exception
):
await setup_deconz_integration(hass)
assert not hass.data[DECONZ_DOMAIN]
async def test_connection_status_signalling( async def test_connection_status_signalling(
hass, aioclient_mock, mock_deconz_websocket hass, aioclient_mock, mock_deconz_websocket
): ):
@ -282,18 +264,6 @@ async def test_update_address(hass, aioclient_mock):
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
async def test_gateway_trigger_reauth_flow(hass):
"""Failed authentication trigger a reauthentication flow."""
with patch(
"homeassistant.components.deconz.gateway.get_gateway",
side_effect=AuthenticationRequired,
), patch.object(hass.config_entries.flow, "async_init") as mock_flow_init:
await setup_deconz_integration(hass)
mock_flow_init.assert_called_once()
assert hass.data[DECONZ_DOMAIN] == {}
async def test_reset_after_successful_setup(hass, aioclient_mock): async def test_reset_after_successful_setup(hass, aioclient_mock):
"""Make sure that connection status triggers a dispatcher send.""" """Make sure that connection status triggers a dispatcher send."""
config_entry = await setup_deconz_integration(hass, aioclient_mock) config_entry = await setup_deconz_integration(hass, aioclient_mock)
@ -305,25 +275,24 @@ async def test_reset_after_successful_setup(hass, aioclient_mock):
assert result is True assert result is True
async def test_get_gateway(hass): async def test_get_deconz_session(hass):
"""Successful call.""" """Successful call."""
with patch("pydeconz.DeconzSession.refresh_state", return_value=True): with patch("pydeconz.DeconzSession.refresh_state", return_value=True):
assert await get_gateway(hass, ENTRY_CONFIG, Mock(), Mock()) assert await get_deconz_session(hass, ENTRY_CONFIG)
async def test_get_gateway_fails_unauthorized(hass): @pytest.mark.parametrize(
"side_effect, raised_exception",
[
(asyncio.TimeoutError, CannotConnect),
(pydeconz.RequestError, CannotConnect),
(pydeconz.Unauthorized, AuthenticationRequired),
],
)
async def test_get_deconz_session_fails(hass, side_effect, raised_exception):
"""Failed call.""" """Failed call."""
with patch( with patch(
"pydeconz.DeconzSession.refresh_state", "pydeconz.DeconzSession.refresh_state",
side_effect=pydeconz.errors.Unauthorized, side_effect=side_effect,
), pytest.raises(AuthenticationRequired): ), pytest.raises(raised_exception):
assert await get_gateway(hass, ENTRY_CONFIG, Mock(), Mock()) is False assert await get_deconz_session(hass, ENTRY_CONFIG)
async def test_get_gateway_fails_cannot_connect(hass):
"""Failed call."""
with patch(
"pydeconz.DeconzSession.refresh_state",
side_effect=pydeconz.errors.RequestError,
), pytest.raises(CannotConnect):
assert await get_gateway(hass, ENTRY_CONFIG, Mock(), Mock()) is False

View file

@ -1,6 +1,5 @@
"""Test deCONZ component setup process.""" """Test deCONZ component setup process."""
import asyncio
from unittest.mock import patch from unittest.mock import patch
from homeassistant.components.deconz import ( from homeassistant.components.deconz import (
@ -13,6 +12,7 @@ from homeassistant.components.deconz.const import (
CONF_GROUP_ID_BASE, CONF_GROUP_ID_BASE,
DOMAIN as DECONZ_DOMAIN, DOMAIN as DECONZ_DOMAIN,
) )
from homeassistant.components.deconz.errors import AuthenticationRequired, CannotConnect
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
@ -42,22 +42,6 @@ async def setup_entry(hass, entry):
assert await async_setup_entry(hass, entry) is True assert await async_setup_entry(hass, entry) is True
async def test_setup_entry_fails(hass):
"""Test setup entry fails if deCONZ is not available."""
with patch("pydeconz.DeconzSession.refresh_state", side_effect=Exception):
await setup_deconz_integration(hass)
assert not hass.data[DECONZ_DOMAIN]
async def test_setup_entry_no_available_bridge(hass):
"""Test setup entry fails if deCONZ is not available."""
with patch(
"pydeconz.DeconzSession.refresh_state", side_effect=asyncio.TimeoutError
):
await setup_deconz_integration(hass)
assert not hass.data[DECONZ_DOMAIN]
async def test_setup_entry_successful(hass, aioclient_mock): async def test_setup_entry_successful(hass, aioclient_mock):
"""Test setup entry is successful.""" """Test setup entry is successful."""
config_entry = await setup_deconz_integration(hass, aioclient_mock) config_entry = await setup_deconz_integration(hass, aioclient_mock)
@ -67,6 +51,29 @@ async def test_setup_entry_successful(hass, aioclient_mock):
assert hass.data[DECONZ_DOMAIN][config_entry.entry_id].master assert hass.data[DECONZ_DOMAIN][config_entry.entry_id].master
async def test_setup_entry_fails_config_entry_not_ready(hass):
"""Failed authentication trigger a reauthentication flow."""
with patch(
"homeassistant.components.deconz.get_deconz_session",
side_effect=CannotConnect,
):
await setup_deconz_integration(hass)
assert hass.data[DECONZ_DOMAIN] == {}
async def test_setup_entry_fails_trigger_reauth_flow(hass):
"""Failed authentication trigger a reauthentication flow."""
with patch(
"homeassistant.components.deconz.get_deconz_session",
side_effect=AuthenticationRequired,
), patch.object(hass.config_entries.flow, "async_init") as mock_flow_init:
await setup_deconz_integration(hass)
mock_flow_init.assert_called_once()
assert hass.data[DECONZ_DOMAIN] == {}
async def test_setup_entry_multiple_gateways(hass, aioclient_mock): async def test_setup_entry_multiple_gateways(hass, aioclient_mock):
"""Test setup entry is successful with multiple gateways.""" """Test setup entry is successful with multiple gateways."""
config_entry = await setup_deconz_integration(hass, aioclient_mock) config_entry = await setup_deconz_integration(hass, aioclient_mock)