Fully type homeassistant integration (#107380)

This commit is contained in:
Marc Mueller 2024-01-08 10:07:30 +01:00 committed by GitHub
parent 5ae419367e
commit 78752264b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 65 additions and 49 deletions

View File

@ -196,8 +196,7 @@ homeassistant.components.here_travel_time.*
homeassistant.components.history.* homeassistant.components.history.*
homeassistant.components.history_stats.* homeassistant.components.history_stats.*
homeassistant.components.holiday.* homeassistant.components.holiday.*
homeassistant.components.homeassistant.exposed_entities homeassistant.components.homeassistant.*
homeassistant.components.homeassistant.triggers.event
homeassistant.components.homeassistant_alerts.* homeassistant.components.homeassistant_alerts.*
homeassistant.components.homeassistant_green.* homeassistant.components.homeassistant_green.*
homeassistant.components.homeassistant_hardware.* homeassistant.components.homeassistant_hardware.*

View File

@ -94,8 +94,8 @@ async def async_setup(hass: ha.HomeAssistant, config: ConfigType) -> bool: # no
sorted(all_referenced), lambda item: ha.split_entity_id(item)[0] sorted(all_referenced), lambda item: ha.split_entity_id(item)[0]
) )
tasks = [] tasks: list[Coroutine[Any, Any, ha.ServiceResponse]] = []
unsupported_entities = set() unsupported_entities: set[str] = set()
for domain, ent_ids in by_domain: for domain, ent_ids in by_domain:
# This leads to endless loop. # This leads to endless loop.
@ -298,7 +298,7 @@ async def async_setup(hass: ha.HomeAssistant, config: ConfigType) -> bool: # no
async def async_handle_reload_config_entry(call: ha.ServiceCall) -> None: async def async_handle_reload_config_entry(call: ha.ServiceCall) -> None:
"""Service handler for reloading a config entry.""" """Service handler for reloading a config entry."""
reload_entries = set() reload_entries: set[str] = set()
if ATTR_ENTRY_ID in call.data: if ATTR_ENTRY_ID in call.data:
reload_entries.add(call.data[ATTR_ENTRY_ID]) reload_entries.add(call.data[ATTR_ENTRY_ID])
reload_entries.update(await async_extract_config_entry_ids(hass, call)) reload_entries.update(await async_extract_config_entry_ids(hass, call))
@ -376,7 +376,7 @@ async def async_setup(hass: ha.HomeAssistant, config: ConfigType) -> bool: # no
return True return True
async def _async_stop(hass: ha.HomeAssistant, restart: bool): async def _async_stop(hass: ha.HomeAssistant, restart: bool) -> None:
"""Stop home assistant.""" """Stop home assistant."""
exit_code = RESTART_EXIT_CODE if restart else 0 exit_code = RESTART_EXIT_CODE if restart else 0
# Track trask in hass.data. No need to cleanup, we're stopping. # Track trask in hass.data. No need to cleanup, we're stopping.

View File

@ -475,7 +475,7 @@ def ws_expose_new_entities_get(
def ws_expose_new_entities_set( def ws_expose_new_entities_set(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any] hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
) -> None: ) -> None:
"""Expose new entities to an assistatant.""" """Expose new entities to an assistant."""
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES] exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES]
exposed_entities.async_set_expose_new_entities(msg["assistant"], msg["expose_new"]) exposed_entities.async_set_expose_new_entities(msg["assistant"], msg["expose_new"])
connection.send_result(msg["id"]) connection.send_result(msg["id"])

View File

@ -28,7 +28,7 @@ def async_describe_events(
@callback @callback
def async_describe_hass_event(event: Event) -> dict[str, str]: def async_describe_hass_event(event: Event) -> dict[str, str]:
"""Describe homeassisant logbook event.""" """Describe homeassistant logbook event."""
return { return {
LOGBOOK_ENTRY_NAME: "Home Assistant", LOGBOOK_ENTRY_NAME: "Home Assistant",
LOGBOOK_ENTRY_MESSAGE: EVENT_TO_NAME[event.event_type], LOGBOOK_ENTRY_MESSAGE: EVENT_TO_NAME[event.event_type],

View File

@ -135,7 +135,7 @@ class SceneConfig(NamedTuple):
id: str | None id: str | None
name: str name: str
icon: str | None icon: str | None
states: dict states: dict[str, State]
@callback @callback

View File

@ -1,4 +1,8 @@
"""Provide info to system health.""" """Provide info to system health."""
from __future__ import annotations
from typing import Any
from homeassistant.components import system_health from homeassistant.components import system_health
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import system_info from homeassistant.helpers import system_info
@ -12,7 +16,7 @@ def async_register(
register.async_register_info(system_health_info) register.async_register_info(system_health_info)
async def system_health_info(hass): async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
"""Get info for the info page.""" """Get info for the info page."""
info = await system_info.async_get_system_info(hass) info = await system_info.async_get_system_info(hass)

View File

@ -23,7 +23,7 @@ async def async_validate_trigger_config(
if hasattr(platform, "async_validate_trigger_config"): if hasattr(platform, "async_validate_trigger_config"):
return await platform.async_validate_trigger_config(hass, config) return await platform.async_validate_trigger_config(hass, config)
return platform.TRIGGER_SCHEMA(config) return platform.TRIGGER_SCHEMA(config) # type: ignore[no-any-return]
async def async_attach_trigger( async def async_attach_trigger(

View File

@ -1,5 +1,10 @@
"""Offer numeric state listening automation rules.""" """Offer numeric state listening automation rules."""
from __future__ import annotations
from collections.abc import Callable
from datetime import timedelta
import logging import logging
from typing import Any, TypeVar
import voluptuous as vol import voluptuous as vol
@ -13,7 +18,7 @@ from homeassistant.const import (
CONF_PLATFORM, CONF_PLATFORM,
CONF_VALUE_TEMPLATE, CONF_VALUE_TEMPLATE,
) )
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, State, callback
from homeassistant.helpers import ( from homeassistant.helpers import (
condition, condition,
config_validation as cv, config_validation as cv,
@ -21,14 +26,17 @@ from homeassistant.helpers import (
template, template,
) )
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
EventStateChangedData,
async_track_same_state, async_track_same_state,
async_track_state_change_event, async_track_state_change_event,
) )
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType, EventType
_T = TypeVar("_T", bound=dict[str, Any])
def validate_above_below(value): def validate_above_below(value: _T) -> _T:
"""Validate that above and below can co-exist.""" """Validate that above and below can co-exist."""
above = value.get(CONF_ABOVE) above = value.get(CONF_ABOVE)
below = value.get(CONF_BELOW) below = value.get(CONF_BELOW)
@ -96,9 +104,9 @@ async def async_attach_trigger(
time_delta = config.get(CONF_FOR) time_delta = config.get(CONF_FOR)
template.attach(hass, time_delta) template.attach(hass, time_delta)
value_template = config.get(CONF_VALUE_TEMPLATE) value_template = config.get(CONF_VALUE_TEMPLATE)
unsub_track_same = {} unsub_track_same: dict[str, Callable[[], None]] = {}
armed_entities = set() armed_entities: set[str] = set()
period: dict = {} period: dict[str, timedelta] = {}
attribute = config.get(CONF_ATTRIBUTE) attribute = config.get(CONF_ATTRIBUTE)
job = HassJob(action, f"numeric state trigger {trigger_info}") job = HassJob(action, f"numeric state trigger {trigger_info}")
@ -108,7 +116,7 @@ async def async_attach_trigger(
if value_template is not None: if value_template is not None:
value_template.hass = hass value_template.hass = hass
def variables(entity_id): def variables(entity_id: str) -> dict[str, Any]:
"""Return a dict with trigger variables.""" """Return a dict with trigger variables."""
trigger_info = { trigger_info = {
"trigger": { "trigger": {
@ -122,7 +130,9 @@ async def async_attach_trigger(
return {**_variables, **trigger_info} return {**_variables, **trigger_info}
@callback @callback
def check_numeric_state(entity_id, from_s, to_s): def check_numeric_state(
entity_id: str, from_s: State | None, to_s: str | State | None
) -> bool:
"""Return whether the criteria are met, raise ConditionError if unknown.""" """Return whether the criteria are met, raise ConditionError if unknown."""
return condition.async_numeric_state( return condition.async_numeric_state(
hass, to_s, below, above, value_template, variables(entity_id), attribute hass, to_s, below, above, value_template, variables(entity_id), attribute
@ -141,14 +151,17 @@ async def async_attach_trigger(
) )
@callback @callback
def state_automation_listener(event): def state_automation_listener(event: EventType[EventStateChangedData]) -> None:
"""Listen for state changes and calls action.""" """Listen for state changes and calls action."""
entity_id = event.data.get("entity_id") entity_id = event.data["entity_id"]
from_s = event.data.get("old_state") from_s = event.data["old_state"]
to_s = event.data.get("new_state") to_s = event.data["new_state"]
if to_s is None:
return
@callback @callback
def call_action(): def call_action() -> None:
"""Call action with right context.""" """Call action with right context."""
hass.async_run_hass_job( hass.async_run_hass_job(
job, job,
@ -169,7 +182,9 @@ async def async_attach_trigger(
) )
@callback @callback
def check_numeric_state_no_raise(entity_id, from_s, to_s): def check_numeric_state_no_raise(
entity_id: str, from_s: State | None, to_s: State | None
) -> bool:
"""Return True if the criteria are now met, False otherwise.""" """Return True if the criteria are now met, False otherwise."""
try: try:
return check_numeric_state(entity_id, from_s, to_s) return check_numeric_state(entity_id, from_s, to_s)
@ -216,7 +231,7 @@ async def async_attach_trigger(
unsub = async_track_state_change_event(hass, entity_ids, state_automation_listener) unsub = async_track_state_change_event(hass, entity_ids, state_automation_listener)
@callback @callback
def async_remove(): def async_remove() -> None:
"""Remove state listeners async.""" """Remove state listeners async."""
unsub() unsub()
for async_remove in unsub_track_same.values(): for async_remove in unsub_track_same.values():

View File

@ -1,6 +1,7 @@
"""Offer state listening automation rules.""" """Offer state listening automation rules."""
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable
from datetime import timedelta from datetime import timedelta
import logging import logging
@ -114,7 +115,7 @@ async def async_attach_trigger(
match_all = all( match_all = all(
item not in config for item in (CONF_FROM, CONF_NOT_FROM, CONF_NOT_TO, CONF_TO) item not in config for item in (CONF_FROM, CONF_NOT_FROM, CONF_NOT_TO, CONF_TO)
) )
unsub_track_same = {} unsub_track_same: dict[str, Callable[[], None]] = {}
period: dict[str, timedelta] = {} period: dict[str, timedelta] = {}
attribute = config.get(CONF_ATTRIBUTE) attribute = config.get(CONF_ATTRIBUTE)
job = HassJob(action, f"state trigger {trigger_info}") job = HassJob(action, f"state trigger {trigger_info}")
@ -158,7 +159,7 @@ async def async_attach_trigger(
return return
@callback @callback
def call_action(): def call_action() -> None:
"""Call action with right context.""" """Call action with right context."""
hass.async_run_hass_job( hass.async_run_hass_job(
job, job,
@ -201,7 +202,7 @@ async def async_attach_trigger(
) )
return return
def _check_same_state(_, _2, new_st: State | None) -> bool: def _check_same_state(_: str, _2: State | None, new_st: State | None) -> bool:
if new_st is None: if new_st is None:
return False return False
@ -227,7 +228,7 @@ async def async_attach_trigger(
unsub = async_track_state_change_event(hass, entity_ids, state_automation_listener) unsub = async_track_state_change_event(hass, entity_ids, state_automation_listener)
@callback @callback
def async_remove(): def async_remove() -> None:
"""Remove state listeners async.""" """Remove state listeners async."""
unsub() unsub()
for async_remove in unsub_track_same.values(): for async_remove in unsub_track_same.values():

View File

@ -53,7 +53,9 @@ async def async_attach_trigger(
job = HassJob(action, f"time trigger {trigger_info}") job = HassJob(action, f"time trigger {trigger_info}")
@callback @callback
def time_automation_listener(description, now, *, entity_id=None): def time_automation_listener(
description: str, now: datetime, *, entity_id: str | None = None
) -> None:
"""Listen for time changes and calls action.""" """Listen for time changes and calls action."""
hass.async_run_hass_job( hass.async_run_hass_job(
job, job,
@ -183,7 +185,7 @@ async def async_attach_trigger(
) )
@callback @callback
def remove_track_time_changes(): def remove_track_time_changes() -> None:
"""Remove tracked time changes.""" """Remove tracked time changes."""
for remove in entities.values(): for remove in entities.values():
remove() remove()

View File

@ -1,4 +1,9 @@
"""Offer time listening automation rules.""" """Offer time listening automation rules."""
from __future__ import annotations
from datetime import datetime
from typing import Any
import voluptuous as vol import voluptuous as vol
from homeassistant.const import CONF_PLATFORM from homeassistant.const import CONF_PLATFORM
@ -19,15 +24,15 @@ class TimePattern:
:raises Invalid: If the value has a wrong format or is outside the range. :raises Invalid: If the value has a wrong format or is outside the range.
""" """
def __init__(self, maximum): def __init__(self, maximum: int) -> None:
"""Initialize time pattern.""" """Initialize time pattern."""
self.maximum = maximum self.maximum = maximum
def __call__(self, value): def __call__(self, value: Any) -> str | int:
"""Validate input.""" """Validate input."""
try: try:
if value == "*": if value == "*":
return value return value # type: ignore[no-any-return]
if isinstance(value, str) and value.startswith("/"): if isinstance(value, str) and value.startswith("/"):
number = int(value[1:]) number = int(value[1:])
@ -39,7 +44,7 @@ class TimePattern:
except ValueError as err: except ValueError as err:
raise vol.Invalid("invalid time_pattern value") from err raise vol.Invalid("invalid time_pattern value") from err
return value return value # type: ignore[no-any-return]
TRIGGER_SCHEMA = vol.All( TRIGGER_SCHEMA = vol.All(
@ -75,7 +80,7 @@ async def async_attach_trigger(
seconds = 0 seconds = 0
@callback @callback
def time_automation_listener(now): def time_automation_listener(now: datetime) -> None:
"""Listen for time changes and calls action.""" """Listen for time changes and calls action."""
hass.async_run_hass_job( hass.async_run_hass_job(
job, job,

View File

@ -520,7 +520,7 @@ def async_extract_referenced_entity_ids(
@bind_hass @bind_hass
async def async_extract_config_entry_ids( async def async_extract_config_entry_ids(
hass: HomeAssistant, service_call: ServiceCall, expand_group: bool = True hass: HomeAssistant, service_call: ServiceCall, expand_group: bool = True
) -> set: ) -> set[str]:
"""Extract referenced config entry ids from a service call.""" """Extract referenced config entry ids from a service call."""
referenced = async_extract_referenced_entity_ids(hass, service_call, expand_group) referenced = async_extract_referenced_entity_ids(hass, service_call, expand_group)
ent_reg = entity_registry.async_get(hass) ent_reg = entity_registry.async_get(hass)

View File

@ -1721,17 +1721,7 @@ disallow_untyped_defs = true
warn_return_any = true warn_return_any = true
warn_unreachable = true warn_unreachable = true
[mypy-homeassistant.components.homeassistant.exposed_entities] [mypy-homeassistant.components.homeassistant.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.homeassistant.triggers.event]
check_untyped_defs = true check_untyped_defs = true
disallow_incomplete_defs = true disallow_incomplete_defs = true
disallow_subclassing_any = true disallow_subclassing_any = true