From b01a6367cc631c22b6a71994df00006bd5ef301e Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 6 Mar 2021 23:19:03 +0100 Subject: [PATCH] Complete typing on AdGuard Home integration (#47477) --- homeassistant/components/adguard/__init__.py | 8 ++-- .../components/adguard/config_flow.py | 28 ++++++++---- homeassistant/components/adguard/sensor.py | 43 ++++++++++--------- homeassistant/components/adguard/switch.py | 42 ++++++++++-------- tests/components/adguard/test_config_flow.py | 30 +++++++++---- 5 files changed, 94 insertions(+), 57 deletions(-) diff --git a/homeassistant/components/adguard/__init__.py b/homeassistant/components/adguard/__init__.py index 0a316784f8b..6ad7d9579a8 100644 --- a/homeassistant/components/adguard/__init__.py +++ b/homeassistant/components/adguard/__init__.py @@ -1,6 +1,8 @@ """Support for AdGuard Home.""" +from __future__ import annotations + import logging -from typing import Any, Dict +from typing import Any from adguardhome import AdGuardHome, AdGuardHomeConnectionError, AdGuardHomeError import voluptuous as vol @@ -117,7 +119,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool return True -async def async_unload_entry(hass: HomeAssistantType, entry: ConfigType) -> bool: +async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool: """Unload AdGuard Home config entry.""" hass.services.async_remove(DOMAIN, SERVICE_ADD_URL) hass.services.async_remove(DOMAIN, SERVICE_REMOVE_URL) @@ -191,7 +193,7 @@ class AdGuardHomeDeviceEntity(AdGuardHomeEntity): """Defines a AdGuard Home device entity.""" @property - def device_info(self) -> Dict[str, Any]: + def device_info(self) -> dict[str, Any]: """Return device information about this AdGuard Home instance.""" return { "identifiers": { diff --git a/homeassistant/components/adguard/config_flow.py b/homeassistant/components/adguard/config_flow.py index d728eed3003..308670272b5 100644 --- a/homeassistant/components/adguard/config_flow.py +++ b/homeassistant/components/adguard/config_flow.py @@ -1,9 +1,12 @@ """Config flow to configure the AdGuard Home integration.""" +from __future__ import annotations + +from typing import Any + from adguardhome import AdGuardHome, AdGuardHomeConnectionError import voluptuous as vol from homeassistant import config_entries -from homeassistant.components.adguard.const import DOMAIN from homeassistant.config_entries import ConfigFlow from homeassistant.const import ( CONF_HOST, @@ -15,9 +18,10 @@ from homeassistant.const import ( ) from homeassistant.helpers.aiohttp_client import async_get_clientsession +from .const import DOMAIN # pylint: disable=unused-import -@config_entries.HANDLERS.register(DOMAIN) -class AdGuardHomeFlowHandler(ConfigFlow): + +class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN): """Handle a AdGuard Home config flow.""" VERSION = 1 @@ -25,7 +29,9 @@ class AdGuardHomeFlowHandler(ConfigFlow): _hassio_discovery = None - async def _show_setup_form(self, errors=None): + async def _show_setup_form( + self, errors: dict[str, str] | None = None + ) -> dict[str, Any]: """Show the setup form to the user.""" return self.async_show_form( step_id="user", @@ -42,7 +48,9 @@ class AdGuardHomeFlowHandler(ConfigFlow): errors=errors or {}, ) - async def _show_hassio_form(self, errors=None): + async def _show_hassio_form( + self, errors: dict[str, str] | None = None + ) -> dict[str, Any]: """Show the Hass.io confirmation form to the user.""" return self.async_show_form( step_id="hassio_confirm", @@ -51,7 +59,9 @@ class AdGuardHomeFlowHandler(ConfigFlow): errors=errors or {}, ) - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> dict[str, Any]: """Handle a flow initiated by the user.""" if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") @@ -91,7 +101,7 @@ class AdGuardHomeFlowHandler(ConfigFlow): }, ) - async def async_step_hassio(self, discovery_info): + async def async_step_hassio(self, discovery_info: dict[str, Any]) -> dict[str, Any]: """Prepare configuration for a Hass.io AdGuard Home add-on. This flow is triggered by the discovery component. @@ -129,7 +139,9 @@ class AdGuardHomeFlowHandler(ConfigFlow): return self.async_abort(reason="existing_instance_updated") - async def async_step_hassio_confirm(self, user_input=None): + async def async_step_hassio_confirm( + self, user_input: dict[str, Any] | None = None + ) -> dict[str, Any]: """Confirm Hass.io discovery.""" if user_input is None: return await self._show_hassio_form() diff --git a/homeassistant/components/adguard/sensor.py b/homeassistant/components/adguard/sensor.py index 05e23ba8b80..edd9fe22ba9 100644 --- a/homeassistant/components/adguard/sensor.py +++ b/homeassistant/components/adguard/sensor.py @@ -1,25 +1,28 @@ """Support for AdGuard Home sensors.""" +from __future__ import annotations + from datetime import timedelta +from typing import Callable -from adguardhome import AdGuardHomeConnectionError +from adguardhome import AdGuardHome, AdGuardHomeConnectionError -from homeassistant.components.adguard import AdGuardHomeDeviceEntity -from homeassistant.components.adguard.const import ( - DATA_ADGUARD_CLIENT, - DATA_ADGUARD_VERION, - DOMAIN, -) from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE, TIME_MILLISECONDS +from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady -from homeassistant.helpers.typing import HomeAssistantType +from homeassistant.helpers.entity import Entity + +from . import AdGuardHomeDeviceEntity +from .const import DATA_ADGUARD_CLIENT, DATA_ADGUARD_VERION, DOMAIN SCAN_INTERVAL = timedelta(seconds=300) PARALLEL_UPDATES = 4 async def async_setup_entry( - hass: HomeAssistantType, entry: ConfigEntry, async_add_entities + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: Callable[[list[Entity], bool], None], ) -> None: """Set up AdGuard Home sensor based on a config entry.""" adguard = hass.data[DOMAIN][DATA_ADGUARD_CLIENT] @@ -50,7 +53,7 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity): def __init__( self, - adguard, + adguard: AdGuardHome, name: str, icon: str, measurement: str, @@ -78,12 +81,12 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity): ) @property - def state(self): + def state(self) -> str | None: """Return the state of the sensor.""" return self._state @property - def unit_of_measurement(self) -> str: + def unit_of_measurement(self) -> str | None: """Return the unit this state is expressed in.""" return self._unit_of_measurement @@ -91,7 +94,7 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity): class AdGuardHomeDNSQueriesSensor(AdGuardHomeSensor): """Defines a AdGuard Home DNS Queries sensor.""" - def __init__(self, adguard): + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home sensor.""" super().__init__( adguard, "AdGuard DNS Queries", "mdi:magnify", "dns_queries", "queries" @@ -105,7 +108,7 @@ class AdGuardHomeDNSQueriesSensor(AdGuardHomeSensor): class AdGuardHomeBlockedFilteringSensor(AdGuardHomeSensor): """Defines a AdGuard Home blocked by filtering sensor.""" - def __init__(self, adguard): + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home sensor.""" super().__init__( adguard, @@ -124,7 +127,7 @@ class AdGuardHomeBlockedFilteringSensor(AdGuardHomeSensor): class AdGuardHomePercentageBlockedSensor(AdGuardHomeSensor): """Defines a AdGuard Home blocked percentage sensor.""" - def __init__(self, adguard): + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home sensor.""" super().__init__( adguard, @@ -143,7 +146,7 @@ class AdGuardHomePercentageBlockedSensor(AdGuardHomeSensor): class AdGuardHomeReplacedParentalSensor(AdGuardHomeSensor): """Defines a AdGuard Home replaced by parental control sensor.""" - def __init__(self, adguard): + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home sensor.""" super().__init__( adguard, @@ -161,7 +164,7 @@ class AdGuardHomeReplacedParentalSensor(AdGuardHomeSensor): class AdGuardHomeReplacedSafeBrowsingSensor(AdGuardHomeSensor): """Defines a AdGuard Home replaced by safe browsing sensor.""" - def __init__(self, adguard): + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home sensor.""" super().__init__( adguard, @@ -179,7 +182,7 @@ class AdGuardHomeReplacedSafeBrowsingSensor(AdGuardHomeSensor): class AdGuardHomeReplacedSafeSearchSensor(AdGuardHomeSensor): """Defines a AdGuard Home replaced by safe search sensor.""" - def __init__(self, adguard): + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home sensor.""" super().__init__( adguard, @@ -197,7 +200,7 @@ class AdGuardHomeReplacedSafeSearchSensor(AdGuardHomeSensor): class AdGuardHomeAverageProcessingTimeSensor(AdGuardHomeSensor): """Defines a AdGuard Home average processing time sensor.""" - def __init__(self, adguard): + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home sensor.""" super().__init__( adguard, @@ -216,7 +219,7 @@ class AdGuardHomeAverageProcessingTimeSensor(AdGuardHomeSensor): class AdGuardHomeRulesCountSensor(AdGuardHomeSensor): """Defines a AdGuard Home rules count sensor.""" - def __init__(self, adguard): + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home sensor.""" super().__init__( adguard, diff --git a/homeassistant/components/adguard/switch.py b/homeassistant/components/adguard/switch.py index 44aab11573d..0b127a280cf 100644 --- a/homeassistant/components/adguard/switch.py +++ b/homeassistant/components/adguard/switch.py @@ -1,19 +1,20 @@ """Support for AdGuard Home switches.""" +from __future__ import annotations + from datetime import timedelta import logging +from typing import Callable -from adguardhome import AdGuardHomeConnectionError, AdGuardHomeError +from adguardhome import AdGuardHome, AdGuardHomeConnectionError, AdGuardHomeError -from homeassistant.components.adguard import AdGuardHomeDeviceEntity -from homeassistant.components.adguard.const import ( - DATA_ADGUARD_CLIENT, - DATA_ADGUARD_VERION, - DOMAIN, -) from homeassistant.components.switch import SwitchEntity from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady -from homeassistant.helpers.typing import HomeAssistantType +from homeassistant.helpers.entity import Entity + +from . import AdGuardHomeDeviceEntity +from .const import DATA_ADGUARD_CLIENT, DATA_ADGUARD_VERION, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -22,7 +23,9 @@ PARALLEL_UPDATES = 1 async def async_setup_entry( - hass: HomeAssistantType, entry: ConfigEntry, async_add_entities + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: Callable[[list[Entity], bool], None], ) -> None: """Set up AdGuard Home switch based on a config entry.""" adguard = hass.data[DOMAIN][DATA_ADGUARD_CLIENT] @@ -49,8 +52,13 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity): """Defines a AdGuard Home switch.""" def __init__( - self, adguard, name: str, icon: str, key: str, enabled_default: bool = True - ): + self, + adguard: AdGuardHome, + name: str, + icon: str, + key: str, + enabled_default: bool = True, + ) -> None: """Initialize AdGuard Home switch.""" self._state = False self._key = key @@ -96,7 +104,7 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity): class AdGuardHomeProtectionSwitch(AdGuardHomeSwitch): """Defines a AdGuard Home protection switch.""" - def __init__(self, adguard) -> None: + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home switch.""" super().__init__( adguard, "AdGuard Protection", "mdi:shield-check", "protection" @@ -118,7 +126,7 @@ class AdGuardHomeProtectionSwitch(AdGuardHomeSwitch): class AdGuardHomeParentalSwitch(AdGuardHomeSwitch): """Defines a AdGuard Home parental control switch.""" - def __init__(self, adguard) -> None: + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home switch.""" super().__init__( adguard, "AdGuard Parental Control", "mdi:shield-check", "parental" @@ -140,7 +148,7 @@ class AdGuardHomeParentalSwitch(AdGuardHomeSwitch): class AdGuardHomeSafeSearchSwitch(AdGuardHomeSwitch): """Defines a AdGuard Home safe search switch.""" - def __init__(self, adguard) -> None: + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home switch.""" super().__init__( adguard, "AdGuard Safe Search", "mdi:shield-check", "safesearch" @@ -162,7 +170,7 @@ class AdGuardHomeSafeSearchSwitch(AdGuardHomeSwitch): class AdGuardHomeSafeBrowsingSwitch(AdGuardHomeSwitch): """Defines a AdGuard Home safe search switch.""" - def __init__(self, adguard) -> None: + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home switch.""" super().__init__( adguard, "AdGuard Safe Browsing", "mdi:shield-check", "safebrowsing" @@ -184,7 +192,7 @@ class AdGuardHomeSafeBrowsingSwitch(AdGuardHomeSwitch): class AdGuardHomeFilteringSwitch(AdGuardHomeSwitch): """Defines a AdGuard Home filtering switch.""" - def __init__(self, adguard) -> None: + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home switch.""" super().__init__(adguard, "AdGuard Filtering", "mdi:shield-check", "filtering") @@ -204,7 +212,7 @@ class AdGuardHomeFilteringSwitch(AdGuardHomeSwitch): class AdGuardHomeQueryLogSwitch(AdGuardHomeSwitch): """Defines a AdGuard Home query log switch.""" - def __init__(self, adguard) -> None: + def __init__(self, adguard: AdGuardHome) -> None: """Initialize AdGuard Home switch.""" super().__init__( adguard, diff --git a/tests/components/adguard/test_config_flow.py b/tests/components/adguard/test_config_flow.py index 06fe235741f..94760cade9f 100644 --- a/tests/components/adguard/test_config_flow.py +++ b/tests/components/adguard/test_config_flow.py @@ -16,8 +16,10 @@ from homeassistant.const import ( CONF_VERIFY_SSL, CONTENT_TYPE_JSON, ) +from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry +from tests.test_util.aiohttp import AiohttpClientMocker FIXTURE_USER_INPUT = { CONF_HOST: "127.0.0.1", @@ -29,7 +31,7 @@ FIXTURE_USER_INPUT = { } -async def test_show_authenticate_form(hass): +async def test_show_authenticate_form(hass: HomeAssistant) -> None: """Test that the setup form is served.""" flow = config_flow.AdGuardHomeFlowHandler() flow.hass = hass @@ -39,7 +41,9 @@ async def test_show_authenticate_form(hass): assert result["step_id"] == "user" -async def test_connection_error(hass, aioclient_mock): +async def test_connection_error( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test we show user form on AdGuard Home connection error.""" aioclient_mock.get( f"{'https' if FIXTURE_USER_INPUT[CONF_SSL] else 'http'}" @@ -57,7 +61,9 @@ async def test_connection_error(hass, aioclient_mock): assert result["errors"] == {"base": "cannot_connect"} -async def test_full_flow_implementation(hass, aioclient_mock): +async def test_full_flow_implementation( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test registering an integration and finishing flow works.""" aioclient_mock.get( f"{'https' if FIXTURE_USER_INPUT[CONF_SSL] else 'http'}" @@ -84,7 +90,7 @@ async def test_full_flow_implementation(hass, aioclient_mock): assert result["data"][CONF_VERIFY_SSL] == FIXTURE_USER_INPUT[CONF_VERIFY_SSL] -async def test_integration_already_exists(hass): +async def test_integration_already_exists(hass: HomeAssistant) -> None: """Test we only allow a single config flow.""" MockConfigEntry(domain=DOMAIN).add_to_hass(hass) @@ -95,7 +101,7 @@ async def test_integration_already_exists(hass): assert result["reason"] == "single_instance_allowed" -async def test_hassio_single_instance(hass): +async def test_hassio_single_instance(hass: HomeAssistant) -> None: """Test we only allow a single config flow.""" MockConfigEntry( domain="adguard", data={"host": "mock-adguard", "port": "3000"} @@ -110,7 +116,7 @@ async def test_hassio_single_instance(hass): assert result["reason"] == "single_instance_allowed" -async def test_hassio_update_instance_not_running(hass): +async def test_hassio_update_instance_not_running(hass: HomeAssistant) -> None: """Test we only allow a single config flow.""" entry = MockConfigEntry( domain="adguard", data={"host": "mock-adguard", "port": "3000"} @@ -131,7 +137,9 @@ async def test_hassio_update_instance_not_running(hass): assert result["reason"] == "existing_instance_updated" -async def test_hassio_update_instance_running(hass, aioclient_mock): +async def test_hassio_update_instance_running( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test we only allow a single config flow.""" aioclient_mock.get( "http://mock-adguard-updated:3000/control/status", @@ -192,7 +200,9 @@ async def test_hassio_update_instance_running(hass, aioclient_mock): assert entry.data["host"] == "mock-adguard-updated" -async def test_hassio_confirm(hass, aioclient_mock): +async def test_hassio_confirm( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test we can finish a config flow.""" aioclient_mock.get( "http://mock-adguard:3000/control/status", @@ -220,7 +230,9 @@ async def test_hassio_confirm(hass, aioclient_mock): assert result["data"][CONF_VERIFY_SSL] -async def test_hassio_connection_error(hass, aioclient_mock): +async def test_hassio_connection_error( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: """Test we show Hass.io confirm form on AdGuard Home connection error.""" aioclient_mock.get( "http://mock-adguard:3000/control/status", exc=aiohttp.ClientError