Ensure Guardian is strictly typed (#53253)

This commit is contained in:
Aaron Bach 2021-07-22 00:01:05 -06:00 committed by GitHub
parent 560bde94ef
commit 1bde914075
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 67 additions and 44 deletions

View File

@ -43,6 +43,7 @@ homeassistant.components.fritz.*
homeassistant.components.geo_location.*
homeassistant.components.gios.*
homeassistant.components.group.*
homeassistant.components.guardian.*
homeassistant.components.history.*
homeassistant.components.homeassistant.triggers.event
homeassistant.components.http.*

View File

@ -2,6 +2,8 @@
from __future__ import annotations
import asyncio
from collections.abc import Awaitable, MutableMapping
from typing import Any, cast
from aioguardian import Client
@ -89,7 +91,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await paired_sensor_manager.async_process_latest_paired_sensor_uids()
@callback
def async_process_paired_sensor_uids():
def async_process_paired_sensor_uids() -> None:
"""Define a callback for when new paired sensor data is received."""
hass.async_create_task(
paired_sensor_manager.async_process_latest_paired_sensor_uids()
@ -133,8 +135,7 @@ class PairedSensorManager:
self._client = client
self._entry = entry
self._hass = hass
self._listeners = []
self._paired_uids = set()
self._paired_uids: set[str] = set()
async def async_pair_sensor(self, uid: str) -> None:
"""Add a new paired sensor coordinator."""
@ -148,7 +149,9 @@ class PairedSensorManager:
self._hass,
client=self._client,
api_name=f"{API_SENSOR_PAIRED_SENSOR_STATUS}_{uid}",
api_coro=lambda: self._client.sensor.paired_sensor_status(uid),
api_coro=lambda: cast(
Awaitable, self._client.sensor.paired_sensor_status(uid)
),
api_lock=self._api_lock,
valve_controller_uid=self._entry.data[CONF_UID],
)
@ -208,12 +211,19 @@ class GuardianEntity(CoordinatorEntity):
"""Define a base Guardian entity."""
def __init__( # pylint: disable=super-init-not-called
self, entry: ConfigEntry, kind: str, name: str, device_class: str, icon: str
self,
entry: ConfigEntry,
kind: str,
name: str,
device_class: str | None,
icon: str | None,
) -> None:
"""Initialize."""
self._attr_device_class = device_class
self._attr_device_info = {"manufacturer": "Elexa"}
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: "Data provided by Elexa"}
self._attr_extra_state_attributes: MutableMapping[str, Any] = {
ATTR_ATTRIBUTION: "Data provided by Elexa"
}
self._attr_icon = icon
self._attr_name = name
self._entry = entry
@ -236,16 +246,18 @@ class PairedSensorEntity(GuardianEntity):
coordinator: DataUpdateCoordinator,
kind: str,
name: str,
device_class: str,
icon: str,
device_class: str | None,
icon: str | None,
) -> None:
"""Initialize."""
super().__init__(entry, kind, name, device_class, icon)
paired_sensor_uid = coordinator.data["uid"]
self._attr_device_info["identifiers"] = {(DOMAIN, paired_sensor_uid)}
self._attr_device_info["name"] = f"Guardian Paired Sensor {paired_sensor_uid}"
self._attr_device_info["via_device"] = (DOMAIN, entry.data[CONF_UID])
self._attr_device_info = {
"identifiers": {(DOMAIN, paired_sensor_uid)},
"name": f"Guardian Paired Sensor {paired_sensor_uid}",
"via_device": (DOMAIN, entry.data[CONF_UID]),
}
self._attr_name = f"Guardian Paired Sensor {paired_sensor_uid}: {name}"
self._attr_unique_id = f"{paired_sensor_uid}_{kind}"
self._kind = kind
@ -271,13 +283,11 @@ class ValveControllerEntity(GuardianEntity):
"""Initialize."""
super().__init__(entry, kind, name, device_class, icon)
self._attr_device_info["identifiers"] = {(DOMAIN, entry.data[CONF_UID])}
self._attr_device_info[
"name"
] = f"Guardian Valve Controller {entry.data[CONF_UID]}"
self._attr_device_info["model"] = coordinators[API_SYSTEM_DIAGNOSTICS].data[
"firmware"
]
self._attr_device_info = {
"identifiers": {(DOMAIN, entry.data[CONF_UID])},
"name": f"Guardian Valve Controller {entry.data[CONF_UID]}",
"model": coordinators[API_SYSTEM_DIAGNOSTICS].data["firmware"],
}
self._attr_name = f"Guardian {entry.data[CONF_UID]}: {name}"
self._attr_unique_id = f"{entry.data[CONF_UID]}_{kind}"
self._kind = kind
@ -304,7 +314,7 @@ class ValveControllerEntity(GuardianEntity):
"""Add a listener to a DataUpdateCoordinator based on the API referenced."""
@callback
def update():
def update() -> None:
"""Update the entity's state."""
self._async_update_from_latest_data()
self.async_write_ha_state()
@ -327,6 +337,7 @@ class ValveControllerEntity(GuardianEntity):
return
refresh_tasks = [
coordinator.async_request_refresh() for coordinator in self.coordinators
coordinator.async_request_refresh()
for coordinator in self.coordinators.values()
]
await asyncio.gather(*refresh_tasks)

View File

@ -78,7 +78,7 @@ async def async_setup_entry(
)
)
sensors = []
sensors: list[PairedSensorBinarySensor | ValveControllerBinarySensor] = []
# Add all valve controller-specific binary sensors:
for kind in VALVE_CONTROLLER_SENSORS:

View File

@ -40,7 +40,7 @@ def async_get_pin_from_uid(uid: str) -> str:
return uid[-4:]
async def validate_input(hass: HomeAssistant, data: dict[str, Any]):
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]:
"""Validate the user input allows us to connect.
Data has the keys from DATA_SCHEMA with values provided by the user.
@ -60,7 +60,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
def __init__(self) -> None:
"""Initialize."""
self.discovery_info = {}
self.discovery_info: dict[str, Any] = {}
async def _async_set_unique_id(self, pin: str) -> None:
"""Set the config entry's unique ID (based on the device's 4-digit PIN)."""

View File

@ -3,7 +3,7 @@
"name": "Elexa Guardian",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/guardian",
"requirements": ["aioguardian==1.0.4"],
"requirements": ["aioguardian==1.0.8"],
"zeroconf": ["_api._udp.local."],
"codeowners": ["@bachya"],
"iot_class": "local_polling",

View File

@ -78,7 +78,7 @@ async def async_setup_entry(
)
)
sensors = []
sensors: list[PairedSensorSensor | ValveControllerSensor] = []
# Add all valve controller-specific binary sensors:
for kind in VALVE_CONTROLLER_SENSORS:

View File

@ -1,6 +1,8 @@
"""Switches for the Elexa Guardian integration."""
from __future__ import annotations
from typing import Any
from aioguardian import Client
from aioguardian.errors import GuardianError
import voluptuous as vol
@ -95,7 +97,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
self._attr_is_on = True
self._client = client
async def _async_continue_entity_setup(self):
async def _async_continue_entity_setup(self) -> None:
"""Register API interest (and related tasks) when the entity is added."""
self.async_add_coordinator_update_listener(API_VALVE_STATUS)
@ -127,7 +129,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
}
)
async def async_disable_ap(self):
async def async_disable_ap(self) -> None:
"""Disable the device's onboard access point."""
try:
async with self._client:
@ -135,7 +137,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
except GuardianError as err:
LOGGER.error("Error while disabling valve controller AP: %s", err)
async def async_enable_ap(self):
async def async_enable_ap(self) -> None:
"""Enable the device's onboard access point."""
try:
async with self._client:
@ -143,7 +145,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
except GuardianError as err:
LOGGER.error("Error while enabling valve controller AP: %s", err)
async def async_pair_sensor(self, *, uid):
async def async_pair_sensor(self, *, uid: str) -> None:
"""Add a new paired sensor."""
try:
async with self._client:
@ -156,7 +158,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
self._entry.entry_id
].async_pair_sensor(uid)
async def async_reboot(self):
async def async_reboot(self) -> None:
"""Reboot the device."""
try:
async with self._client:
@ -164,7 +166,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
except GuardianError as err:
LOGGER.error("Error while rebooting valve controller: %s", err)
async def async_reset_valve_diagnostics(self):
async def async_reset_valve_diagnostics(self) -> None:
"""Fully reset system motor diagnostics."""
try:
async with self._client:
@ -172,7 +174,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
except GuardianError as err:
LOGGER.error("Error while resetting valve diagnostics: %s", err)
async def async_unpair_sensor(self, *, uid):
async def async_unpair_sensor(self, *, uid: str) -> None:
"""Add a new paired sensor."""
try:
async with self._client:
@ -185,7 +187,9 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
self._entry.entry_id
].async_unpair_sensor(uid)
async def async_upgrade_firmware(self, *, url, port, filename):
async def async_upgrade_firmware(
self, *, url: str, port: int, filename: str
) -> None:
"""Upgrade the device firmware."""
try:
async with self._client:
@ -197,7 +201,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
except GuardianError as err:
LOGGER.error("Error while upgrading firmware: %s", err)
async def async_turn_off(self, **kwargs) -> None:
async def async_turn_off(self, **kwargs: dict[str, Any]) -> None:
"""Turn the valve off (closed)."""
try:
async with self._client:
@ -209,7 +213,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
self._attr_is_on = False
self.async_write_ha_state()
async def async_turn_on(self, **kwargs) -> None:
async def async_turn_on(self, **kwargs: dict[str, Any]) -> None:
"""Turn the valve on (open)."""
try:
async with self._client:

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import asyncio
from collections.abc import Awaitable
from datetime import timedelta
from typing import Callable
from typing import Any, Callable, Dict, cast
from aioguardian import Client
from aioguardian.errors import GuardianError
@ -42,11 +42,11 @@ class GuardianDataUpdateCoordinator(DataUpdateCoordinator[dict]):
self._api_lock = api_lock
self._client = client
async def _async_update_data(self) -> dict:
async def _async_update_data(self) -> dict[str, Any]:
"""Execute a "locked" API request against the valve controller."""
async with self._api_lock, self._client:
try:
resp = await self._api_coro()
except GuardianError as err:
raise UpdateFailed(err) from err
return resp["data"]
return cast(Dict[str, Any], resp["data"])

View File

@ -484,6 +484,17 @@ no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.guardian.*]
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.history.*]
check_untyped_defs = true
disallow_incomplete_defs = true
@ -1252,9 +1263,6 @@ ignore_errors = true
[mypy-homeassistant.components.gtfs.*]
ignore_errors = true
[mypy-homeassistant.components.guardian.*]
ignore_errors = true
[mypy-homeassistant.components.habitica.*]
ignore_errors = true

View File

@ -172,7 +172,7 @@ aioflo==0.4.1
aioftp==0.12.0
# homeassistant.components.guardian
aioguardian==1.0.4
aioguardian==1.0.8
# homeassistant.components.harmony
aioharmony==0.2.7

View File

@ -109,7 +109,7 @@ aioesphomeapi==5.0.1
aioflo==0.4.1
# homeassistant.components.guardian
aioguardian==1.0.4
aioguardian==1.0.8
# homeassistant.components.harmony
aioharmony==0.2.7

View File

@ -61,7 +61,6 @@ IGNORED_MODULES: Final[list[str]] = [
"homeassistant.components.gree.*",
"homeassistant.components.growatt_server.*",
"homeassistant.components.gtfs.*",
"homeassistant.components.guardian.*",
"homeassistant.components.habitica.*",
"homeassistant.components.harmony.*",
"homeassistant.components.hassio.*",