Complete typing on AdGuard Home integration (#47477)

pull/47542/head
Franck Nijhof 2021-03-06 23:19:03 +01:00 committed by GitHub
parent d944bbbc52
commit b01a6367cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 57 deletions

View File

@ -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": {

View File

@ -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()

View File

@ -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,

View File

@ -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,

View File

@ -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