mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Ensure Guardian is strictly typed (#53253)
This commit is contained in:
parent
560bde94ef
commit
1bde914075
@ -43,6 +43,7 @@ homeassistant.components.fritz.*
|
|||||||
homeassistant.components.geo_location.*
|
homeassistant.components.geo_location.*
|
||||||
homeassistant.components.gios.*
|
homeassistant.components.gios.*
|
||||||
homeassistant.components.group.*
|
homeassistant.components.group.*
|
||||||
|
homeassistant.components.guardian.*
|
||||||
homeassistant.components.history.*
|
homeassistant.components.history.*
|
||||||
homeassistant.components.homeassistant.triggers.event
|
homeassistant.components.homeassistant.triggers.event
|
||||||
homeassistant.components.http.*
|
homeassistant.components.http.*
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from collections.abc import Awaitable, MutableMapping
|
||||||
|
from typing import Any, cast
|
||||||
|
|
||||||
from aioguardian import Client
|
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()
|
await paired_sensor_manager.async_process_latest_paired_sensor_uids()
|
||||||
|
|
||||||
@callback
|
@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."""
|
"""Define a callback for when new paired sensor data is received."""
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
paired_sensor_manager.async_process_latest_paired_sensor_uids()
|
paired_sensor_manager.async_process_latest_paired_sensor_uids()
|
||||||
@ -133,8 +135,7 @@ class PairedSensorManager:
|
|||||||
self._client = client
|
self._client = client
|
||||||
self._entry = entry
|
self._entry = entry
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._listeners = []
|
self._paired_uids: set[str] = set()
|
||||||
self._paired_uids = set()
|
|
||||||
|
|
||||||
async def async_pair_sensor(self, uid: str) -> None:
|
async def async_pair_sensor(self, uid: str) -> None:
|
||||||
"""Add a new paired sensor coordinator."""
|
"""Add a new paired sensor coordinator."""
|
||||||
@ -148,7 +149,9 @@ class PairedSensorManager:
|
|||||||
self._hass,
|
self._hass,
|
||||||
client=self._client,
|
client=self._client,
|
||||||
api_name=f"{API_SENSOR_PAIRED_SENSOR_STATUS}_{uid}",
|
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,
|
api_lock=self._api_lock,
|
||||||
valve_controller_uid=self._entry.data[CONF_UID],
|
valve_controller_uid=self._entry.data[CONF_UID],
|
||||||
)
|
)
|
||||||
@ -208,12 +211,19 @@ class GuardianEntity(CoordinatorEntity):
|
|||||||
"""Define a base Guardian entity."""
|
"""Define a base Guardian entity."""
|
||||||
|
|
||||||
def __init__( # pylint: disable=super-init-not-called
|
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:
|
) -> None:
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
self._attr_device_class = device_class
|
self._attr_device_class = device_class
|
||||||
self._attr_device_info = {"manufacturer": "Elexa"}
|
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_icon = icon
|
||||||
self._attr_name = name
|
self._attr_name = name
|
||||||
self._entry = entry
|
self._entry = entry
|
||||||
@ -236,16 +246,18 @@ class PairedSensorEntity(GuardianEntity):
|
|||||||
coordinator: DataUpdateCoordinator,
|
coordinator: DataUpdateCoordinator,
|
||||||
kind: str,
|
kind: str,
|
||||||
name: str,
|
name: str,
|
||||||
device_class: str,
|
device_class: str | None,
|
||||||
icon: str,
|
icon: str | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
super().__init__(entry, kind, name, device_class, icon)
|
super().__init__(entry, kind, name, device_class, icon)
|
||||||
|
|
||||||
paired_sensor_uid = coordinator.data["uid"]
|
paired_sensor_uid = coordinator.data["uid"]
|
||||||
self._attr_device_info["identifiers"] = {(DOMAIN, paired_sensor_uid)}
|
self._attr_device_info = {
|
||||||
self._attr_device_info["name"] = f"Guardian Paired Sensor {paired_sensor_uid}"
|
"identifiers": {(DOMAIN, paired_sensor_uid)},
|
||||||
self._attr_device_info["via_device"] = (DOMAIN, entry.data[CONF_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_name = f"Guardian Paired Sensor {paired_sensor_uid}: {name}"
|
||||||
self._attr_unique_id = f"{paired_sensor_uid}_{kind}"
|
self._attr_unique_id = f"{paired_sensor_uid}_{kind}"
|
||||||
self._kind = kind
|
self._kind = kind
|
||||||
@ -271,13 +283,11 @@ class ValveControllerEntity(GuardianEntity):
|
|||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
super().__init__(entry, kind, name, device_class, icon)
|
super().__init__(entry, kind, name, device_class, icon)
|
||||||
|
|
||||||
self._attr_device_info["identifiers"] = {(DOMAIN, entry.data[CONF_UID])}
|
self._attr_device_info = {
|
||||||
self._attr_device_info[
|
"identifiers": {(DOMAIN, entry.data[CONF_UID])},
|
||||||
"name"
|
"name": f"Guardian Valve Controller {entry.data[CONF_UID]}",
|
||||||
] = f"Guardian Valve Controller {entry.data[CONF_UID]}"
|
"model": coordinators[API_SYSTEM_DIAGNOSTICS].data["firmware"],
|
||||||
self._attr_device_info["model"] = coordinators[API_SYSTEM_DIAGNOSTICS].data[
|
}
|
||||||
"firmware"
|
|
||||||
]
|
|
||||||
self._attr_name = f"Guardian {entry.data[CONF_UID]}: {name}"
|
self._attr_name = f"Guardian {entry.data[CONF_UID]}: {name}"
|
||||||
self._attr_unique_id = f"{entry.data[CONF_UID]}_{kind}"
|
self._attr_unique_id = f"{entry.data[CONF_UID]}_{kind}"
|
||||||
self._kind = kind
|
self._kind = kind
|
||||||
@ -304,7 +314,7 @@ class ValveControllerEntity(GuardianEntity):
|
|||||||
"""Add a listener to a DataUpdateCoordinator based on the API referenced."""
|
"""Add a listener to a DataUpdateCoordinator based on the API referenced."""
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def update():
|
def update() -> None:
|
||||||
"""Update the entity's state."""
|
"""Update the entity's state."""
|
||||||
self._async_update_from_latest_data()
|
self._async_update_from_latest_data()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
@ -327,6 +337,7 @@ class ValveControllerEntity(GuardianEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
refresh_tasks = [
|
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)
|
await asyncio.gather(*refresh_tasks)
|
||||||
|
@ -78,7 +78,7 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
sensors = []
|
sensors: list[PairedSensorBinarySensor | ValveControllerBinarySensor] = []
|
||||||
|
|
||||||
# Add all valve controller-specific binary sensors:
|
# Add all valve controller-specific binary sensors:
|
||||||
for kind in VALVE_CONTROLLER_SENSORS:
|
for kind in VALVE_CONTROLLER_SENSORS:
|
||||||
|
@ -40,7 +40,7 @@ def async_get_pin_from_uid(uid: str) -> str:
|
|||||||
return uid[-4:]
|
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.
|
"""Validate the user input allows us to connect.
|
||||||
|
|
||||||
Data has the keys from DATA_SCHEMA with values provided by the user.
|
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:
|
def __init__(self) -> None:
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
self.discovery_info = {}
|
self.discovery_info: dict[str, Any] = {}
|
||||||
|
|
||||||
async def _async_set_unique_id(self, pin: str) -> None:
|
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)."""
|
"""Set the config entry's unique ID (based on the device's 4-digit PIN)."""
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Elexa Guardian",
|
"name": "Elexa Guardian",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/guardian",
|
"documentation": "https://www.home-assistant.io/integrations/guardian",
|
||||||
"requirements": ["aioguardian==1.0.4"],
|
"requirements": ["aioguardian==1.0.8"],
|
||||||
"zeroconf": ["_api._udp.local."],
|
"zeroconf": ["_api._udp.local."],
|
||||||
"codeowners": ["@bachya"],
|
"codeowners": ["@bachya"],
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
|
@ -78,7 +78,7 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
sensors = []
|
sensors: list[PairedSensorSensor | ValveControllerSensor] = []
|
||||||
|
|
||||||
# Add all valve controller-specific binary sensors:
|
# Add all valve controller-specific binary sensors:
|
||||||
for kind in VALVE_CONTROLLER_SENSORS:
|
for kind in VALVE_CONTROLLER_SENSORS:
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Switches for the Elexa Guardian integration."""
|
"""Switches for the Elexa Guardian integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from aioguardian import Client
|
from aioguardian import Client
|
||||||
from aioguardian.errors import GuardianError
|
from aioguardian.errors import GuardianError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -95,7 +97,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
|||||||
self._attr_is_on = True
|
self._attr_is_on = True
|
||||||
self._client = client
|
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."""
|
"""Register API interest (and related tasks) when the entity is added."""
|
||||||
self.async_add_coordinator_update_listener(API_VALVE_STATUS)
|
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."""
|
"""Disable the device's onboard access point."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
@ -135,7 +137,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
|||||||
except GuardianError as err:
|
except GuardianError as err:
|
||||||
LOGGER.error("Error while disabling valve controller AP: %s", 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."""
|
"""Enable the device's onboard access point."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
@ -143,7 +145,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
|||||||
except GuardianError as err:
|
except GuardianError as err:
|
||||||
LOGGER.error("Error while enabling valve controller AP: %s", 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."""
|
"""Add a new paired sensor."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
@ -156,7 +158,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
|||||||
self._entry.entry_id
|
self._entry.entry_id
|
||||||
].async_pair_sensor(uid)
|
].async_pair_sensor(uid)
|
||||||
|
|
||||||
async def async_reboot(self):
|
async def async_reboot(self) -> None:
|
||||||
"""Reboot the device."""
|
"""Reboot the device."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
@ -164,7 +166,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
|||||||
except GuardianError as err:
|
except GuardianError as err:
|
||||||
LOGGER.error("Error while rebooting valve controller: %s", 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."""
|
"""Fully reset system motor diagnostics."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
@ -172,7 +174,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
|||||||
except GuardianError as err:
|
except GuardianError as err:
|
||||||
LOGGER.error("Error while resetting valve diagnostics: %s", 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."""
|
"""Add a new paired sensor."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
@ -185,7 +187,9 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
|||||||
self._entry.entry_id
|
self._entry.entry_id
|
||||||
].async_unpair_sensor(uid)
|
].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."""
|
"""Upgrade the device firmware."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
@ -197,7 +201,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
|||||||
except GuardianError as err:
|
except GuardianError as err:
|
||||||
LOGGER.error("Error while upgrading firmware: %s", 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)."""
|
"""Turn the valve off (closed)."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
@ -209,7 +213,7 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
|
|||||||
self._attr_is_on = False
|
self._attr_is_on = False
|
||||||
self.async_write_ha_state()
|
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)."""
|
"""Turn the valve on (open)."""
|
||||||
try:
|
try:
|
||||||
async with self._client:
|
async with self._client:
|
||||||
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Awaitable
|
from collections.abc import Awaitable
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Callable
|
from typing import Any, Callable, Dict, cast
|
||||||
|
|
||||||
from aioguardian import Client
|
from aioguardian import Client
|
||||||
from aioguardian.errors import GuardianError
|
from aioguardian.errors import GuardianError
|
||||||
@ -42,11 +42,11 @@ class GuardianDataUpdateCoordinator(DataUpdateCoordinator[dict]):
|
|||||||
self._api_lock = api_lock
|
self._api_lock = api_lock
|
||||||
self._client = client
|
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."""
|
"""Execute a "locked" API request against the valve controller."""
|
||||||
async with self._api_lock, self._client:
|
async with self._api_lock, self._client:
|
||||||
try:
|
try:
|
||||||
resp = await self._api_coro()
|
resp = await self._api_coro()
|
||||||
except GuardianError as err:
|
except GuardianError as err:
|
||||||
raise UpdateFailed(err) from err
|
raise UpdateFailed(err) from err
|
||||||
return resp["data"]
|
return cast(Dict[str, Any], resp["data"])
|
||||||
|
14
mypy.ini
14
mypy.ini
@ -484,6 +484,17 @@ no_implicit_optional = true
|
|||||||
warn_return_any = true
|
warn_return_any = true
|
||||||
warn_unreachable = 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.*]
|
[mypy-homeassistant.components.history.*]
|
||||||
check_untyped_defs = true
|
check_untyped_defs = true
|
||||||
disallow_incomplete_defs = true
|
disallow_incomplete_defs = true
|
||||||
@ -1252,9 +1263,6 @@ ignore_errors = true
|
|||||||
[mypy-homeassistant.components.gtfs.*]
|
[mypy-homeassistant.components.gtfs.*]
|
||||||
ignore_errors = true
|
ignore_errors = true
|
||||||
|
|
||||||
[mypy-homeassistant.components.guardian.*]
|
|
||||||
ignore_errors = true
|
|
||||||
|
|
||||||
[mypy-homeassistant.components.habitica.*]
|
[mypy-homeassistant.components.habitica.*]
|
||||||
ignore_errors = true
|
ignore_errors = true
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ aioflo==0.4.1
|
|||||||
aioftp==0.12.0
|
aioftp==0.12.0
|
||||||
|
|
||||||
# homeassistant.components.guardian
|
# homeassistant.components.guardian
|
||||||
aioguardian==1.0.4
|
aioguardian==1.0.8
|
||||||
|
|
||||||
# homeassistant.components.harmony
|
# homeassistant.components.harmony
|
||||||
aioharmony==0.2.7
|
aioharmony==0.2.7
|
||||||
|
@ -109,7 +109,7 @@ aioesphomeapi==5.0.1
|
|||||||
aioflo==0.4.1
|
aioflo==0.4.1
|
||||||
|
|
||||||
# homeassistant.components.guardian
|
# homeassistant.components.guardian
|
||||||
aioguardian==1.0.4
|
aioguardian==1.0.8
|
||||||
|
|
||||||
# homeassistant.components.harmony
|
# homeassistant.components.harmony
|
||||||
aioharmony==0.2.7
|
aioharmony==0.2.7
|
||||||
|
@ -61,7 +61,6 @@ IGNORED_MODULES: Final[list[str]] = [
|
|||||||
"homeassistant.components.gree.*",
|
"homeassistant.components.gree.*",
|
||||||
"homeassistant.components.growatt_server.*",
|
"homeassistant.components.growatt_server.*",
|
||||||
"homeassistant.components.gtfs.*",
|
"homeassistant.components.gtfs.*",
|
||||||
"homeassistant.components.guardian.*",
|
|
||||||
"homeassistant.components.habitica.*",
|
"homeassistant.components.habitica.*",
|
||||||
"homeassistant.components.harmony.*",
|
"homeassistant.components.harmony.*",
|
||||||
"homeassistant.components.hassio.*",
|
"homeassistant.components.hassio.*",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user