ha-core/pylint/plugins/hass_enforce_type_hints.py

3303 lines
109 KiB
Python

"""Plugin to enforce type hints on specific functions."""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
import re
from typing import TYPE_CHECKING
from astroid import nodes
from astroid.exceptions import NameInferenceError
from pylint.checkers import BaseChecker
from pylint.lint import PyLinter
from homeassistant.const import Platform
if TYPE_CHECKING:
# InferenceResult is available only from astroid >= 2.12.0
# pre-commit should still work on out of date environments
from astroid.typing import InferenceResult
_COMMON_ARGUMENTS: dict[str, list[str]] = {
"hass": ["HomeAssistant", "HomeAssistant | None"]
}
_PLATFORMS: set[str] = {platform.value for platform in Platform}
class _Special(Enum):
"""Sentinel values."""
UNDEFINED = 1
@dataclass
class TypeHintMatch:
"""Class for pattern matching."""
function_name: str
return_type: list[str | _Special | None] | str | _Special | None
arg_types: dict[int, str] | None = None
"""arg_types is for positional arguments"""
named_arg_types: dict[str, str] | None = None
"""named_arg_types is for named or keyword arguments"""
kwargs_type: str | None = None
"""kwargs_type is for the special case `**kwargs`"""
has_async_counterpart: bool = False
def need_to_check_function(self, node: nodes.FunctionDef) -> bool:
"""Confirm if function should be checked."""
return (
self.function_name == node.name
or self.has_async_counterpart
and node.name == f"async_{self.function_name}"
or self.function_name.endswith("*")
and node.name.startswith(self.function_name[:-1])
)
@dataclass(kw_only=True)
class ClassTypeHintMatch:
"""Class for pattern matching."""
base_class: str
exclude_base_classes: set[str] | None = None
matches: list[TypeHintMatch]
_INNER_MATCH = r"((?:[\w\| ]+)|(?:\.{3})|(?:\w+\[.+\]))"
_TYPE_HINT_MATCHERS: dict[str, re.Pattern[str]] = {
# a_or_b matches items such as "DiscoveryInfoType | None"
# or "dict | list | None"
"a_or_b": re.compile(rf"^(.+) \| {_INNER_MATCH}$"),
}
_INNER_MATCH_POSSIBILITIES = [i + 1 for i in range(5)]
_TYPE_HINT_MATCHERS.update(
{
f"x_of_y_{i}": re.compile(
rf"^(\w+)\[{_INNER_MATCH}" + f", {_INNER_MATCH}" * (i - 1) + r"\]$"
)
for i in _INNER_MATCH_POSSIBILITIES
}
)
_MODULE_REGEX: re.Pattern[str] = re.compile(r"^homeassistant\.components\.\w+(\.\w+)?$")
_METHOD_MATCH: list[TypeHintMatch] = [
TypeHintMatch(
function_name="__init__",
return_type=None,
),
]
_TEST_FIXTURES: dict[str, list[str] | str] = {
"aioclient_mock": "AiohttpClientMocker",
"aiohttp_client": "ClientSessionGenerator",
"area_registry": "AreaRegistry",
"async_setup_recorder_instance": "RecorderInstanceGenerator",
"caplog": "pytest.LogCaptureFixture",
"current_request_with_host": "None",
"device_registry": "DeviceRegistry",
"enable_bluetooth": "None",
"enable_custom_integrations": "None",
"enable_nightly_purge": "bool",
"enable_statistics": "bool",
"enable_schema_validation": "bool",
"entity_registry": "EntityRegistry",
"entity_registry_enabled_by_default": "None",
"freezer": "FrozenDateTimeFactory",
"hass_access_token": "str",
"hass_admin_credential": "Credentials",
"hass_admin_user": "MockUser",
"hass_client": "ClientSessionGenerator",
"hass_client_no_auth": "ClientSessionGenerator",
"hass_config": "ConfigType",
"hass_config_yaml": "str",
"hass_config_yaml_files": "dict[str, str]",
"hass_owner_user": "MockUser",
"hass_read_only_access_token": "str",
"hass_read_only_user": "MockUser",
"hass_recorder": "Callable[..., HomeAssistant]",
"hass_storage": "dict[str, Any]",
"hass_supervisor_access_token": "str",
"hass_supervisor_user": "MockUser",
"hass_ws_client": "WebSocketGenerator",
"issue_registry": "IssueRegistry",
"legacy_auth": "LegacyApiPasswordAuthProvider",
"local_auth": "HassAuthProvider",
"mock_async_zeroconf": "None",
"mock_bleak_scanner_start": "MagicMock",
"mock_bluetooth": "None",
"mock_bluetooth_adapters": "None",
"mock_device_tracker_conf": "list[Device]",
"mock_get_source_ip": "None",
"mock_hass_config": "None",
"mock_hass_config_yaml": "None",
"mock_zeroconf": "None",
"mqtt_client_mock": "MqttMockPahoClient",
"mqtt_mock": "MqttMockHAClient",
"mqtt_mock_entry": "MqttMockHAClientGenerator",
"recorder_db_url": "str",
"recorder_mock": "Recorder",
"requests_mock": "requests_mock.Mocker",
"snapshot": "SnapshotAssertion",
"stub_blueprint_populate": "None",
"tmp_path": "Path",
"tmpdir": "py.path.local",
}
_TEST_FUNCTION_MATCH = TypeHintMatch(
function_name="test_*",
return_type=None,
)
_FUNCTION_MATCH: dict[str, list[TypeHintMatch]] = {
"__init__": [
TypeHintMatch(
function_name="setup",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="bool",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="async_setup_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type="bool",
),
TypeHintMatch(
function_name="async_remove_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type=None,
),
TypeHintMatch(
function_name="async_unload_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type="bool",
),
TypeHintMatch(
function_name="async_migrate_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type="bool",
),
TypeHintMatch(
function_name="async_remove_config_entry_device",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
2: "DeviceEntry",
},
return_type="bool",
),
TypeHintMatch(
function_name="async_reset_platform",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type=None,
),
],
"__any_platform__": [
TypeHintMatch(
function_name="setup_platform",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "AddEntitiesCallback",
3: "DiscoveryInfoType | None",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="async_setup_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
2: "AddEntitiesCallback",
},
return_type=None,
),
],
"application_credentials": [
TypeHintMatch(
function_name="async_get_auth_implementation",
arg_types={
0: "HomeAssistant",
1: "str",
2: "ClientCredential",
},
return_type="AbstractOAuth2Implementation",
),
TypeHintMatch(
function_name="async_get_authorization_server",
arg_types={
0: "HomeAssistant",
},
return_type="AuthorizationServer",
),
],
"backup": [
TypeHintMatch(
function_name="async_pre_backup",
arg_types={
0: "HomeAssistant",
},
return_type=None,
),
TypeHintMatch(
function_name="async_post_backup",
arg_types={
0: "HomeAssistant",
},
return_type=None,
),
],
"cast": [
TypeHintMatch(
function_name="async_get_media_browser_root_object",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type="list[BrowseMedia]",
),
TypeHintMatch(
function_name="async_browse_media",
arg_types={
0: "HomeAssistant",
1: "MediaType | str",
2: "str",
3: "str",
},
return_type=["BrowseMedia", "BrowseMedia | None"],
),
TypeHintMatch(
function_name="async_play_media",
arg_types={
0: "HomeAssistant",
1: "str",
2: "Chromecast",
3: "MediaType | str",
4: "str",
},
return_type="bool",
),
],
"config_flow": [
TypeHintMatch(
function_name="_async_has_devices",
arg_types={
0: "HomeAssistant",
},
return_type="bool",
),
],
"device_action": [
TypeHintMatch(
function_name="async_validate_action_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConfigType",
),
TypeHintMatch(
function_name="async_call_action_from_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "TemplateVarsType",
3: "Context | None",
},
return_type=None,
),
TypeHintMatch(
function_name="async_get_action_capabilities",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="dict[str, Schema]",
),
TypeHintMatch(
function_name="async_get_actions",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type=["list[dict[str, str]]", "list[dict[str, Any]]"],
),
],
"device_condition": [
TypeHintMatch(
function_name="async_validate_condition_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConfigType",
),
TypeHintMatch(
function_name="async_condition_from_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConditionCheckerType",
),
TypeHintMatch(
function_name="async_get_condition_capabilities",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="dict[str, Schema]",
),
TypeHintMatch(
function_name="async_get_conditions",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type=["list[dict[str, str]]", "list[dict[str, Any]]"],
),
],
"device_tracker": [
TypeHintMatch(
function_name="setup_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "SeeCallback",
3: "DiscoveryInfoType | None",
},
return_type="bool",
),
TypeHintMatch(
function_name="async_setup_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "AsyncSeeCallback",
3: "DiscoveryInfoType | None",
},
return_type="bool",
),
TypeHintMatch(
function_name="get_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type=["DeviceScanner", None],
has_async_counterpart=True,
),
],
"device_trigger": [
TypeHintMatch(
function_name="async_validate_condition_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConfigType",
),
TypeHintMatch(
function_name="async_attach_trigger",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "TriggerActionType",
3: "TriggerInfo",
},
return_type="CALLBACK_TYPE",
),
TypeHintMatch(
function_name="async_get_trigger_capabilities",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="dict[str, Schema]",
),
TypeHintMatch(
function_name="async_get_triggers",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type=["list[dict[str, str]]", "list[dict[str, Any]]"],
),
],
"diagnostics": [
TypeHintMatch(
function_name="async_get_config_entry_diagnostics",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type="Mapping[str, Any]",
),
TypeHintMatch(
function_name="async_get_device_diagnostics",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
2: "DeviceEntry",
},
return_type="Mapping[str, Any]",
),
],
"notify": [
TypeHintMatch(
function_name="get_service",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "DiscoveryInfoType | None",
},
return_type=["BaseNotificationService", None],
has_async_counterpart=True,
),
],
}
_CLASS_MATCH: dict[str, list[ClassTypeHintMatch]] = {
"config_flow": [
ClassTypeHintMatch(
base_class="FlowHandler",
exclude_base_classes={"ConfigEntryBaseFlow"},
matches=[
TypeHintMatch(
function_name="async_step_*",
arg_types={},
return_type="FlowResult",
),
],
),
ClassTypeHintMatch(
base_class="ConfigFlow",
matches=[
TypeHintMatch(
function_name="async_get_options_flow",
arg_types={
0: "ConfigEntry",
},
return_type="OptionsFlow",
),
TypeHintMatch(
function_name="async_step_dhcp",
arg_types={
1: "DhcpServiceInfo",
},
return_type="ConfigFlowResult",
),
TypeHintMatch(
function_name="async_step_hassio",
arg_types={
1: "HassioServiceInfo",
},
return_type="ConfigFlowResult",
),
TypeHintMatch(
function_name="async_step_homekit",
arg_types={
1: "ZeroconfServiceInfo",
},
return_type="ConfigFlowResult",
),
TypeHintMatch(
function_name="async_step_mqtt",
arg_types={
1: "MqttServiceInfo",
},
return_type="ConfigFlowResult",
),
TypeHintMatch(
function_name="async_step_reauth",
arg_types={
1: "Mapping[str, Any]",
},
return_type="ConfigFlowResult",
),
TypeHintMatch(
function_name="async_step_ssdp",
arg_types={
1: "SsdpServiceInfo",
},
return_type="ConfigFlowResult",
),
TypeHintMatch(
function_name="async_step_usb",
arg_types={
1: "UsbServiceInfo",
},
return_type="ConfigFlowResult",
),
TypeHintMatch(
function_name="async_step_zeroconf",
arg_types={
1: "ZeroconfServiceInfo",
},
return_type="ConfigFlowResult",
),
TypeHintMatch(
function_name="async_step_*",
arg_types={},
return_type="ConfigFlowResult",
),
],
),
ClassTypeHintMatch(
base_class="OptionsFlow",
matches=[
TypeHintMatch(
function_name="async_step_*",
arg_types={},
return_type="ConfigFlowResult",
),
],
),
],
}
# Overriding properties and functions are normally checked by mypy, and will only
# be checked by pylint when --ignore-missing-annotations is False
_ENTITY_MATCH: list[TypeHintMatch] = [
TypeHintMatch(
function_name="should_poll",
return_type="bool",
),
TypeHintMatch(
function_name="unique_id",
return_type=["str", None],
),
TypeHintMatch(
function_name="name",
return_type=["str", "UndefinedType", None],
),
TypeHintMatch(
function_name="state",
return_type=["StateType", None, "str", "int", "float"],
),
TypeHintMatch(
function_name="capability_attributes",
return_type=["Mapping[str, Any]", None],
),
TypeHintMatch(
function_name="state_attributes",
return_type=["dict[str, Any]", None],
),
TypeHintMatch(
function_name="extra_state_attributes",
return_type=["Mapping[str, Any]", None],
),
TypeHintMatch(
function_name="device_info",
return_type=["DeviceInfo", None],
),
TypeHintMatch(
function_name="device_class",
return_type=["str", None],
),
TypeHintMatch(
function_name="unit_of_measurement",
return_type=["str", None],
),
TypeHintMatch(
function_name="icon",
return_type=["str", None],
),
TypeHintMatch(
function_name="entity_picture",
return_type=["str", None],
),
TypeHintMatch(
function_name="available",
return_type="bool",
),
TypeHintMatch(
function_name="assumed_state",
return_type="bool",
),
TypeHintMatch(
function_name="force_update",
return_type="bool",
),
TypeHintMatch(
function_name="supported_features",
return_type=["int", None],
),
TypeHintMatch(
function_name="entity_registry_enabled_default",
return_type="bool",
),
TypeHintMatch(
function_name="entity_registry_visible_default",
return_type="bool",
),
TypeHintMatch(
function_name="attribution",
return_type=["str", None],
),
TypeHintMatch(
function_name="entity_category",
return_type=["EntityCategory", None],
),
TypeHintMatch(
function_name="async_removed_from_registry",
return_type=None,
),
TypeHintMatch(
function_name="async_added_to_hass",
return_type=None,
),
TypeHintMatch(
function_name="async_will_remove_from_hass",
return_type=None,
),
TypeHintMatch(
function_name="async_registry_entry_updated",
return_type=None,
),
TypeHintMatch(
function_name="update",
return_type=None,
has_async_counterpart=True,
),
]
_RESTORE_ENTITY_MATCH: list[TypeHintMatch] = [
TypeHintMatch(
function_name="async_get_last_state",
return_type=["State", None],
),
TypeHintMatch(
function_name="async_get_last_extra_data",
return_type=["ExtraStoredData", None],
),
TypeHintMatch(
function_name="extra_restore_state_data",
return_type=["ExtraStoredData", None],
),
]
_TOGGLE_ENTITY_MATCH: list[TypeHintMatch] = [
TypeHintMatch(
function_name="is_on",
return_type=["bool", None],
),
TypeHintMatch(
function_name="turn_on",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="turn_off",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="toggle",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
]
_INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = {
# "air_quality": [], # ignored as deprecated
"alarm_control_panel": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="AlarmControlPanelEntity",
matches=[
TypeHintMatch(
function_name="code_format",
return_type=["CodeFormat", None],
),
TypeHintMatch(
function_name="changed_by",
return_type=["str", None],
),
TypeHintMatch(
function_name="code_arm_required",
return_type="bool",
),
TypeHintMatch(
function_name="supported_features",
return_type="AlarmControlPanelEntityFeature",
),
TypeHintMatch(
function_name="alarm_disarm",
named_arg_types={
"code": "str | None",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="alarm_arm_home",
named_arg_types={
"code": "str | None",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="alarm_arm_away",
named_arg_types={
"code": "str | None",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="alarm_arm_night",
named_arg_types={
"code": "str | None",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="alarm_arm_vacation",
named_arg_types={
"code": "str | None",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="alarm_trigger",
named_arg_types={
"code": "str | None",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="alarm_arm_custom_bypass",
named_arg_types={
"code": "str | None",
},
return_type=None,
has_async_counterpart=True,
),
],
),
],
"binary_sensor": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="BinarySensorEntity",
matches=[
TypeHintMatch(
function_name="device_class",
return_type=["BinarySensorDeviceClass", None],
),
TypeHintMatch(
function_name="is_on",
return_type=["bool", None],
),
],
),
],
"button": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="ButtonEntity",
matches=[
TypeHintMatch(
function_name="device_class",
return_type=["ButtonDeviceClass", "str", None],
),
TypeHintMatch(
function_name="press",
return_type=None,
has_async_counterpart=True,
),
],
),
],
"calendar": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="CalendarEntity",
matches=[
TypeHintMatch(
function_name="event",
return_type=["CalendarEvent", None],
),
TypeHintMatch(
function_name="async_get_events",
arg_types={
1: "HomeAssistant",
2: "datetime",
3: "datetime",
},
return_type="list[CalendarEvent]",
),
],
),
],
"camera": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="Camera",
matches=[
TypeHintMatch(
function_name="entity_picture",
return_type="str",
),
TypeHintMatch(
function_name="supported_features",
return_type="CameraEntityFeature",
),
TypeHintMatch(
function_name="is_recording",
return_type="bool",
),
TypeHintMatch(
function_name="is_streaming",
return_type="bool",
),
TypeHintMatch(
function_name="brand",
return_type=["str", None],
),
TypeHintMatch(
function_name="motion_detection_enabled",
return_type="bool",
),
TypeHintMatch(
function_name="model",
return_type=["str", None],
),
TypeHintMatch(
function_name="frame_interval",
return_type="float",
),
TypeHintMatch(
function_name="frontend_stream_type",
return_type=["StreamType", None],
),
TypeHintMatch(
function_name="available",
return_type="bool",
),
TypeHintMatch(
function_name="async_create_stream",
return_type=["Stream", None],
),
TypeHintMatch(
function_name="stream_source",
return_type=["str", None],
),
TypeHintMatch(
function_name="async_handle_web_rtc_offer",
arg_types={
1: "str",
},
return_type=["str", None],
),
TypeHintMatch(
function_name="camera_image",
named_arg_types={
"width": "int | None",
"height": "int | None",
},
return_type=["bytes", None],
has_async_counterpart=True,
),
TypeHintMatch(
function_name="handle_async_still_stream",
arg_types={
1: "Request",
2: "float",
},
return_type="StreamResponse",
),
TypeHintMatch(
function_name="handle_async_mjpeg_stream",
arg_types={
1: "Request",
},
return_type=["StreamResponse", None],
),
TypeHintMatch(
function_name="is_on",
return_type="bool",
),
TypeHintMatch(
function_name="turn_off",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="turn_on",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="enable_motion_detection",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="disable_motion_detection",
return_type=None,
has_async_counterpart=True,
),
],
),
],
"climate": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="ClimateEntity",
matches=[
TypeHintMatch(
function_name="precision",
return_type="float",
),
TypeHintMatch(
function_name="temperature_unit",
return_type="str",
),
TypeHintMatch(
function_name="current_humidity",
return_type=["float", None],
),
TypeHintMatch(
function_name="target_humidity",
return_type=["float", None],
),
TypeHintMatch(
function_name="hvac_mode",
return_type=["HVACMode", None],
),
TypeHintMatch(
function_name="hvac_modes",
return_type="list[HVACMode]",
),
TypeHintMatch(
function_name="hvac_action",
return_type=["HVACAction", None],
),
TypeHintMatch(
function_name="current_temperature",
return_type=["float", None],
),
TypeHintMatch(
function_name="target_temperature",
return_type=["float", None],
),
TypeHintMatch(
function_name="target_temperature_step",
return_type=["float", None],
),
TypeHintMatch(
function_name="target_temperature_high",
return_type=["float", None],
),
TypeHintMatch(
function_name="target_temperature_low",
return_type=["float", None],
),
TypeHintMatch(
function_name="preset_mode",
return_type=["str", None],
),
TypeHintMatch(
function_name="preset_modes",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="is_aux_heat",
return_type=["bool", None],
),
TypeHintMatch(
function_name="fan_mode",
return_type=["str", None],
),
TypeHintMatch(
function_name="fan_modes",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="swing_mode",
return_type=["str", None],
),
TypeHintMatch(
function_name="swing_modes",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="set_temperature",
kwargs_type="Any",
return_type="None",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_humidity",
arg_types={
1: "int",
},
return_type="None",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_fan_mode",
arg_types={
1: "str",
},
return_type="None",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_hvac_mode",
arg_types={
1: "HVACMode",
},
return_type="None",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_swing_mode",
arg_types={
1: "str",
},
return_type="None",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_preset_mode",
arg_types={
1: "str",
},
return_type="None",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="turn_aux_heat_on",
return_type="None",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="turn_aux_heat_off",
return_type="None",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="turn_on",
return_type="None",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="turn_off",
return_type="None",
has_async_counterpart=True,
),
TypeHintMatch(
function_name="supported_features",
return_type="ClimateEntityFeature",
),
TypeHintMatch(
function_name="min_temp",
return_type="float",
),
TypeHintMatch(
function_name="max_temp",
return_type="float",
),
TypeHintMatch(
function_name="min_humidity",
return_type="float",
),
TypeHintMatch(
function_name="max_humidity",
return_type="float",
),
],
),
],
"cover": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="CoverEntity",
matches=[
TypeHintMatch(
function_name="device_class",
return_type=["CoverDeviceClass", None],
),
TypeHintMatch(
function_name="current_cover_position",
return_type=["int", None],
),
TypeHintMatch(
function_name="current_cover_tilt_position",
return_type=["int", None],
),
TypeHintMatch(
function_name="is_opening",
return_type=["bool", None],
),
TypeHintMatch(
function_name="is_closing",
return_type=["bool", None],
),
TypeHintMatch(
function_name="is_closed",
return_type=["bool", None],
),
TypeHintMatch(
function_name="supported_features",
return_type="CoverEntityFeature",
),
TypeHintMatch(
function_name="open_cover",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="close_cover",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="toggle",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_cover_position",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="stop_cover",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="open_cover_tilt",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="close_cover_tilt",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_cover_tilt_position",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="stop_cover_tilt",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="toggle_tilt",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
],
),
],
"device_tracker": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="BaseTrackerEntity",
matches=[
TypeHintMatch(
function_name="battery_level",
return_type=["int", None],
),
TypeHintMatch(
function_name="source_type",
return_type=["SourceType", "str"],
),
],
),
ClassTypeHintMatch(
base_class="TrackerEntity",
matches=[
TypeHintMatch(
function_name="force_update",
return_type="bool",
),
TypeHintMatch(
function_name="location_accuracy",
return_type="int",
),
TypeHintMatch(
function_name="location_name",
return_type=["str", None],
),
TypeHintMatch(
function_name="latitude",
return_type=["float", None],
),
TypeHintMatch(
function_name="longitude",
return_type=["float", None],
),
TypeHintMatch(
function_name="state",
return_type=["str", None],
),
],
),
ClassTypeHintMatch(
base_class="ScannerEntity",
matches=[
TypeHintMatch(
function_name="ip_address",
return_type=["str", None],
),
TypeHintMatch(
function_name="mac_address",
return_type=["str", None],
),
TypeHintMatch(
function_name="hostname",
return_type=["str", None],
),
TypeHintMatch(
function_name="state",
return_type="str",
),
TypeHintMatch(
function_name="is_connected",
return_type="bool",
),
],
),
],
"fan": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="ToggleEntity",
matches=_TOGGLE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="FanEntity",
matches=[
TypeHintMatch(
function_name="percentage",
return_type=["int", None],
),
TypeHintMatch(
function_name="speed_count",
return_type="int",
),
TypeHintMatch(
function_name="percentage_step",
return_type="float",
),
TypeHintMatch(
function_name="current_direction",
return_type=["str", None],
),
TypeHintMatch(
function_name="oscillating",
return_type=["bool", None],
),
TypeHintMatch(
function_name="preset_mode",
return_type=["str", None],
),
TypeHintMatch(
function_name="preset_modes",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="supported_features",
return_type="FanEntityFeature",
),
TypeHintMatch(
function_name="set_percentage",
arg_types={1: "int"},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_preset_mode",
arg_types={1: "str"},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_direction",
arg_types={1: "str"},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="turn_on",
named_arg_types={
"percentage": "int | None",
"preset_mode": "str | None",
},
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="oscillate",
arg_types={1: "bool"},
return_type=None,
has_async_counterpart=True,
),
],
),
],
"geo_location": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="GeolocationEvent",
matches=[
TypeHintMatch(
function_name="source",
return_type="str",
),
TypeHintMatch(
function_name="distance",
return_type=["float", None],
),
TypeHintMatch(
function_name="latitude",
return_type=["float", None],
),
TypeHintMatch(
function_name="longitude",
return_type=["float", None],
),
],
),
],
"image_processing": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="ImageProcessingEntity",
matches=[
TypeHintMatch(
function_name="camera_entity",
return_type=["str", None],
),
TypeHintMatch(
function_name="confidence",
return_type=["float", None],
),
TypeHintMatch(
function_name="device_class",
return_type=["ImageProcessingDeviceClass", None],
),
TypeHintMatch(
function_name="process_image",
arg_types={1: "bytes"},
return_type=None,
has_async_counterpart=True,
),
],
),
ClassTypeHintMatch(
base_class="ImageProcessingFaceEntity",
matches=[
TypeHintMatch(
function_name="process_faces",
arg_types={
1: "list[FaceInformation]",
2: "int",
},
return_type=None,
has_async_counterpart=True,
),
],
),
],
"humidifier": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="ToggleEntity",
matches=_TOGGLE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="HumidifierEntity",
matches=[
TypeHintMatch(
function_name="available_modes",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="device_class",
return_type=["HumidifierDeviceClass", None],
),
TypeHintMatch(
function_name="min_humidity",
return_type=["float"],
),
TypeHintMatch(
function_name="max_humidity",
return_type=["float"],
),
TypeHintMatch(
function_name="mode",
return_type=["str", None],
),
TypeHintMatch(
function_name="supported_features",
return_type="HumidifierEntityFeature",
),
TypeHintMatch(
function_name="target_humidity",
return_type=["float", None],
),
TypeHintMatch(
function_name="set_humidity",
arg_types={1: "int"},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_mode",
arg_types={1: "str"},
return_type=None,
has_async_counterpart=True,
),
],
),
],
"light": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="ToggleEntity",
matches=_TOGGLE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="LightEntity",
matches=[
TypeHintMatch(
function_name="brightness",
return_type=["int", None],
),
TypeHintMatch(
function_name="color_mode",
return_type=["ColorMode", "str", None],
),
TypeHintMatch(
function_name="hs_color",
return_type=["tuple[float, float]", None],
),
TypeHintMatch(
function_name="xy_color",
return_type=["tuple[float, float]", None],
),
TypeHintMatch(
function_name="rgb_color",
return_type=["tuple[int, int, int]", None],
),
TypeHintMatch(
function_name="rgbw_color",
return_type=["tuple[int, int, int, int]", None],
),
TypeHintMatch(
function_name="rgbww_color",
return_type=["tuple[int, int, int, int, int]", None],
),
TypeHintMatch(
function_name="color_temp",
return_type=["int", None],
),
TypeHintMatch(
function_name="min_mireds",
return_type="int",
),
TypeHintMatch(
function_name="max_mireds",
return_type="int",
),
TypeHintMatch(
function_name="effect_list",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="effect",
return_type=["str", None],
),
TypeHintMatch(
function_name="capability_attributes",
return_type=["dict[str, Any]", None],
),
TypeHintMatch(
function_name="supported_color_modes",
return_type=["set[ColorMode]", "set[str]", None],
),
TypeHintMatch(
function_name="supported_features",
return_type="LightEntityFeature",
),
TypeHintMatch(
function_name="turn_on",
named_arg_types={
"brightness": "int | None",
"brightness_pct": "float | None",
"brightness_step": "int | None",
"brightness_step_pct": "float | None",
"color_name": "str | None",
"color_temp": "int | None",
"effect": "str | None",
"flash": "str | None",
"kelvin": "int | None",
"hs_color": "tuple[float, float] | None",
"rgb_color": "tuple[int, int, int] | None",
"rgbw_color": "tuple[int, int, int, int] | None",
"rgbww_color": "tuple[int, int, int, int, int] | None",
"transition": "float | None",
"xy_color": "tuple[float, float] | None",
"white": "int | None",
},
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
],
),
],
"lock": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="LockEntity",
matches=[
TypeHintMatch(
function_name="changed_by",
return_type=["str", None],
),
TypeHintMatch(
function_name="code_format",
return_type=["str", None],
),
TypeHintMatch(
function_name="is_locked",
return_type=["bool", None],
),
TypeHintMatch(
function_name="is_locking",
return_type=["bool", None],
),
TypeHintMatch(
function_name="is_unlocking",
return_type=["bool", None],
),
TypeHintMatch(
function_name="is_jammed",
return_type=["bool", None],
),
TypeHintMatch(
function_name="supported_features",
return_type="LockEntityFeature",
),
TypeHintMatch(
function_name="lock",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="unlock",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="open",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
],
),
],
"mailbox": [
ClassTypeHintMatch(
base_class="Mailbox",
matches=[
TypeHintMatch(
function_name="media_type",
return_type="str",
),
TypeHintMatch(
function_name="can_delete",
return_type="bool",
),
TypeHintMatch(
function_name="has_media",
return_type="bool",
),
TypeHintMatch(
function_name="async_get_media",
arg_types={1: "str"},
return_type="bytes",
),
TypeHintMatch(
function_name="async_get_messages",
return_type="list[dict[str, Any]]",
),
TypeHintMatch(
function_name="async_delete",
arg_types={1: "str"},
return_type="bool",
),
],
),
],
"media_player": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="MediaPlayerEntity",
matches=[
TypeHintMatch(
function_name="device_class",
return_type=["MediaPlayerDeviceClass", None],
),
TypeHintMatch(
function_name="state",
return_type=["MediaPlayerState", None],
),
TypeHintMatch(
function_name="access_token",
return_type="str",
),
TypeHintMatch(
function_name="volume_level",
return_type=["float", None],
),
TypeHintMatch(
function_name="is_volume_muted",
return_type=["bool", None],
),
TypeHintMatch(
function_name="media_content_id",
return_type=["str", None],
),
TypeHintMatch(
function_name="media_content_type",
return_type=["MediaType", "str", None],
),
TypeHintMatch(
function_name="media_duration",
return_type=["int", None],
),
TypeHintMatch(
function_name="media_position",
return_type=["int", None],
),
TypeHintMatch(
function_name="media_position_updated_at",
return_type=["datetime", None],
),
TypeHintMatch(
function_name="media_image_url",
return_type=["str", None],
),
TypeHintMatch(
function_name="media_image_remotely_accessible",
return_type="bool",
),
TypeHintMatch(
function_name="media_image_hash",
return_type=["str", None],
),
TypeHintMatch(
function_name="async_get_media_image",
return_type="tuple[bytes | None, str | None]",
),
TypeHintMatch(
function_name="async_get_browse_image",
arg_types={
1: "MediaType | str",
2: "str",
3: "str | None",
},
return_type="tuple[bytes | None, str | None]",
),
TypeHintMatch(
function_name="media_title",
return_type=["str", None],
),
TypeHintMatch(
function_name="media_artist",
return_type=["str", None],
),
TypeHintMatch(
function_name="media_album_name",
return_type=["str", None],
),
TypeHintMatch(
function_name="media_album_artist",
return_type=["str", None],
),
TypeHintMatch(
function_name="media_track",
return_type=["int", None],
),
TypeHintMatch(
function_name="media_series_title",
return_type=["str", None],
),
TypeHintMatch(
function_name="media_season",
return_type=["str", None],
),
TypeHintMatch(
function_name="media_episode",
return_type=["str", None],
),
TypeHintMatch(
function_name="media_channel",
return_type=["str", None],
),
TypeHintMatch(
function_name="media_playlist",
return_type=["str", None],
),
TypeHintMatch(
function_name="app_id",
return_type=["str", None],
),
TypeHintMatch(
function_name="app_name",
return_type=["str", None],
),
TypeHintMatch(
function_name="source",
return_type=["str", None],
),
TypeHintMatch(
function_name="source_list",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="sound_mode",
return_type=["str", None],
),
TypeHintMatch(
function_name="sound_mode_list",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="shuffle",
return_type=["bool", None],
),
TypeHintMatch(
function_name="repeat",
return_type=["RepeatMode", None],
),
TypeHintMatch(
function_name="group_members",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="supported_features",
return_type="MediaPlayerEntityFeature",
),
TypeHintMatch(
function_name="turn_on",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="turn_off",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="mute_volume",
arg_types={
1: "bool",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_volume_level",
arg_types={
1: "float",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="media_play",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="media_pause",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="media_stop",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="media_previous_track",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="media_next_track",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="media_seek",
arg_types={
1: "float",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="play_media",
arg_types={
1: "MediaType | str",
2: "str",
},
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="select_source",
arg_types={
1: "str",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="select_sound_mode",
arg_types={
1: "str",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="clear_playlist",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_shuffle",
arg_types={
1: "bool",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_repeat",
arg_types={
1: "RepeatMode",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="toggle",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="volume_up",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="volume_down",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="media_play_pause",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="media_image_local",
return_type=["str", None],
),
TypeHintMatch(
function_name="capability_attributes",
return_type="dict[str, Any]",
),
TypeHintMatch(
function_name="async_browse_media",
arg_types={
1: "MediaType | str | None",
2: "str | None",
},
return_type="BrowseMedia",
),
TypeHintMatch(
function_name="join_players",
arg_types={
1: "list[str]",
},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="unjoin_player",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="get_browse_image_url",
arg_types={
1: "str",
2: "str",
3: "str | None",
},
return_type="str",
),
],
),
],
"notify": [
ClassTypeHintMatch(
base_class="BaseNotificationService",
matches=[
TypeHintMatch(
function_name="targets",
return_type=["Mapping[str, Any]", None],
),
TypeHintMatch(
function_name="send_message",
arg_types={1: "str"},
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
],
),
],
"number": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="NumberEntity",
matches=[
TypeHintMatch(
function_name="device_class",
return_type=["NumberDeviceClass", None],
),
TypeHintMatch(
function_name="capability_attributes",
return_type="dict[str, Any]",
),
TypeHintMatch(
function_name="native_min_value",
return_type="float",
),
TypeHintMatch(
function_name="native_max_value",
return_type="float",
),
TypeHintMatch(
function_name="native_step",
return_type=["float", None],
),
TypeHintMatch(
function_name="mode",
return_type="NumberMode",
),
TypeHintMatch(
function_name="native_unit_of_measurement",
return_type=["str", None],
),
TypeHintMatch(
function_name="native_value",
return_type=["float", None],
),
TypeHintMatch(
function_name="set_native_value",
arg_types={1: "float"},
return_type=None,
has_async_counterpart=True,
),
],
),
ClassTypeHintMatch(
base_class="RestoreNumber",
matches=[
TypeHintMatch(
function_name="extra_restore_state_data",
return_type="NumberExtraStoredData",
),
TypeHintMatch(
function_name="async_get_last_number_data",
return_type=["NumberExtraStoredData", None],
),
],
),
],
"remote": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="ToggleEntity",
matches=_TOGGLE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RemoteEntity",
matches=[
TypeHintMatch(
function_name="activity_list",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="current_activity",
return_type=["str", None],
),
TypeHintMatch(
function_name="supported_features",
return_type="RemoteEntityFeature",
),
TypeHintMatch(
function_name="send_command",
arg_types={1: "Iterable[str]"},
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="learn_command",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="delete_command",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
],
),
],
"scene": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="Scene",
matches=[
TypeHintMatch(
function_name="activate",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
],
),
],
"select": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="SelectEntity",
matches=[
TypeHintMatch(
function_name="capability_attributes",
return_type="dict[str, Any]",
),
TypeHintMatch(
function_name="options",
return_type="list[str]",
),
TypeHintMatch(
function_name="current_option",
return_type=["str", None],
),
TypeHintMatch(
function_name="select_option",
return_type=None,
),
TypeHintMatch(
function_name="select_option",
arg_types={1: "str"},
return_type=None,
has_async_counterpart=True,
),
],
),
],
"sensor": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="SensorEntity",
matches=[
TypeHintMatch(
function_name="device_class",
return_type=["SensorDeviceClass", None],
),
TypeHintMatch(
function_name="state_class",
return_type=["SensorStateClass", "str", None],
),
TypeHintMatch(
function_name="last_reset",
return_type=["datetime", None],
),
TypeHintMatch(
function_name="native_value",
return_type=[
"StateType",
"str",
"int",
"float",
None,
"date",
"datetime",
"Decimal",
],
),
TypeHintMatch(
function_name="native_unit_of_measurement",
return_type=["str", None],
),
],
),
ClassTypeHintMatch(
base_class="RestoreSensor",
matches=[
TypeHintMatch(
function_name="extra_restore_state_data",
return_type="SensorExtraStoredData",
),
TypeHintMatch(
function_name="async_get_last_sensor_data",
return_type=["SensorExtraStoredData", None],
),
],
),
],
"siren": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="ToggleEntity",
matches=_TOGGLE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="SirenEntity",
matches=[
TypeHintMatch(
function_name="available_tones",
return_type=["dict[int, str]", "list[int | str]", None],
),
TypeHintMatch(
function_name="supported_features",
return_type="SirenEntityFeature",
),
],
),
],
"stt": [
ClassTypeHintMatch(
base_class="Provider",
matches=[
TypeHintMatch(
function_name="supported_languages",
return_type="list[str]",
),
TypeHintMatch(
function_name="supported_formats",
return_type="list[AudioFormats]",
),
TypeHintMatch(
function_name="supported_codecs",
return_type="list[AudioCodecs]",
),
TypeHintMatch(
function_name="supported_bit_rates",
return_type="list[AudioBitRates]",
),
TypeHintMatch(
function_name="supported_sample_rates",
return_type="list[AudioSampleRates]",
),
TypeHintMatch(
function_name="supported_channels",
return_type="list[AudioChannels]",
),
TypeHintMatch(
function_name="async_process_audio_stream",
arg_types={1: "SpeechMetadata", 2: "AsyncIterable[bytes]"},
return_type="SpeechResult",
),
],
),
],
"switch": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="ToggleEntity",
matches=_TOGGLE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="SwitchEntity",
matches=[
TypeHintMatch(
function_name="device_class",
return_type=["SwitchDeviceClass", None],
),
],
),
],
"todo": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="TodoListEntity",
matches=[
TypeHintMatch(
function_name="todo_items",
return_type=["list[TodoItem]", None],
),
TypeHintMatch(
function_name="async_create_todo_item",
arg_types={
1: "TodoItem",
},
return_type="None",
),
TypeHintMatch(
function_name="async_update_todo_item",
arg_types={
1: "TodoItem",
},
return_type="None",
),
TypeHintMatch(
function_name="async_delete_todo_items",
arg_types={
1: "list[str]",
},
return_type="None",
),
TypeHintMatch(
function_name="async_move_todo_item",
arg_types={
1: "str",
2: "str | None",
},
return_type="None",
),
],
),
],
"tts": [
ClassTypeHintMatch(
base_class="Provider",
matches=[
TypeHintMatch(
function_name="default_language",
return_type=["str", None],
),
TypeHintMatch(
function_name="supported_languages",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="supported_options",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="default_options",
return_type=["Mapping[str, Any]", None],
),
TypeHintMatch(
function_name="get_tts_audio",
arg_types={1: "str", 2: "str", 3: "dict[str, Any]"},
return_type="TtsAudioType",
has_async_counterpart=True,
),
],
),
],
"update": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="UpdateEntity",
matches=[
TypeHintMatch(
function_name="auto_update",
return_type="bool",
),
TypeHintMatch(
function_name="installed_version",
return_type=["str", None],
),
TypeHintMatch(
function_name="device_class",
return_type=["UpdateDeviceClass", None],
),
TypeHintMatch(
function_name="in_progress",
return_type=["bool", "int", None],
),
TypeHintMatch(
function_name="latest_version",
return_type=["str", None],
),
TypeHintMatch(
function_name="release_summary",
return_type=["str", None],
),
TypeHintMatch(
function_name="release_url",
return_type=["str", None],
),
TypeHintMatch(
function_name="supported_features",
return_type="UpdateEntityFeature",
),
TypeHintMatch(
function_name="title",
return_type=["str", None],
),
TypeHintMatch(
function_name="install",
arg_types={1: "str | None", 2: "bool"},
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="release_notes",
return_type=["str", None],
has_async_counterpart=True,
),
],
),
],
"vacuum": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="ToggleEntity",
matches=_TOGGLE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="_BaseVacuum",
matches=[
TypeHintMatch(
function_name="battery_level",
return_type=["int", None],
),
TypeHintMatch(
function_name="battery_icon",
return_type="str",
),
TypeHintMatch(
function_name="fan_speed",
return_type=["str", None],
),
TypeHintMatch(
function_name="fan_speed_list",
return_type="list[str]",
),
TypeHintMatch(
function_name="supported_features",
return_type="VacuumEntityFeature",
),
TypeHintMatch(
function_name="stop",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="return_to_base",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="clean_spot",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="locate",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_fan_speed",
named_arg_types={
"fan_speed": "str",
},
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="send_command",
named_arg_types={
"command": "str",
"params": "dict[str, Any] | list[Any] | None",
},
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
],
),
ClassTypeHintMatch(
base_class="VacuumEntity",
matches=[
TypeHintMatch(
function_name="status",
return_type=["str", None],
),
TypeHintMatch(
function_name="start_pause",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="async_pause",
return_type=None,
),
TypeHintMatch(
function_name="async_start",
return_type=None,
),
],
),
ClassTypeHintMatch(
base_class="StateVacuumEntity",
matches=[
TypeHintMatch(
function_name="state",
return_type=["str", None],
),
TypeHintMatch(
function_name="start",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="pause",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="async_turn_on",
kwargs_type="Any",
return_type=None,
),
TypeHintMatch(
function_name="async_turn_off",
kwargs_type="Any",
return_type=None,
),
TypeHintMatch(
function_name="async_toggle",
kwargs_type="Any",
return_type=None,
),
],
),
],
"water_heater": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="WaterHeaterEntity",
matches=[
TypeHintMatch(
function_name="current_operation",
return_type=["str", None],
),
TypeHintMatch(
function_name="current_temperature",
return_type=["float", None],
),
TypeHintMatch(
function_name="is_away_mode_on",
return_type=["bool", None],
),
TypeHintMatch(
function_name="max_temp",
return_type="float",
),
TypeHintMatch(
function_name="min_temp",
return_type="float",
),
TypeHintMatch(
function_name="operation_list",
return_type=["list[str]", None],
),
TypeHintMatch(
function_name="precision",
return_type="float",
),
TypeHintMatch(
function_name="supported_features",
return_type="WaterHeaterEntityFeature",
),
TypeHintMatch(
function_name="target_temperature",
return_type=["float", None],
),
TypeHintMatch(
function_name="target_temperature_high",
return_type=["float", None],
),
TypeHintMatch(
function_name="target_temperature_low",
return_type=["float", None],
),
TypeHintMatch(
function_name="temperature_unit",
return_type="str",
),
TypeHintMatch(
function_name="set_temperature",
kwargs_type="Any",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="set_operation_mode",
arg_types={1: "str"},
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="turn_away_mode_on",
return_type=None,
has_async_counterpart=True,
),
TypeHintMatch(
function_name="turn_away_mode_off",
return_type=None,
has_async_counterpart=True,
),
],
),
],
"weather": [
ClassTypeHintMatch(
base_class="Entity",
matches=_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="RestoreEntity",
matches=_RESTORE_ENTITY_MATCH,
),
ClassTypeHintMatch(
base_class="WeatherEntity",
matches=[
TypeHintMatch(
function_name="native_temperature",
return_type=["float", None],
),
TypeHintMatch(
function_name="native_temperature_unit",
return_type=["str", None],
),
TypeHintMatch(
function_name="native_pressure",
return_type=["float", None],
),
TypeHintMatch(
function_name="native_pressure_unit",
return_type=["str", None],
),
TypeHintMatch(
function_name="humidity",
return_type=["float", None],
),
TypeHintMatch(
function_name="native_wind_speed",
return_type=["float", None],
),
TypeHintMatch(
function_name="native_wind_speed_unit",
return_type=["str", None],
),
TypeHintMatch(
function_name="wind_bearing",
return_type=["float", "str", None],
),
TypeHintMatch(
function_name="ozone",
return_type=["float", None],
),
TypeHintMatch(
function_name="native_visibility",
return_type=["float", None],
),
TypeHintMatch(
function_name="native_visibility_unit",
return_type=["str", None],
),
TypeHintMatch(
function_name="forecast",
return_type=["list[Forecast]", None],
),
TypeHintMatch(
function_name="native_precipitation_unit",
return_type=["str", None],
),
TypeHintMatch(
function_name="precision",
return_type="float",
),
TypeHintMatch(
function_name="condition",
return_type=["str", None],
),
],
),
],
}
def _is_valid_type(
expected_type: list[str] | str | None | object,
node: nodes.NodeNG,
in_return: bool = False,
) -> bool:
"""Check the argument node against the expected type."""
if expected_type is _Special.UNDEFINED:
return True
if isinstance(expected_type, list):
for expected_type_item in expected_type:
if _is_valid_type(expected_type_item, node, in_return):
return True
return False
# Const occurs when the type is None
if expected_type is None or expected_type == "None":
return isinstance(node, nodes.Const) and node.value is None
assert isinstance(expected_type, str)
# Const occurs when the type is an Ellipsis
if expected_type == "...":
return isinstance(node, nodes.Const) and node.value == Ellipsis
# Special case for `xxx | yyy`
if match := _TYPE_HINT_MATCHERS["a_or_b"].match(expected_type):
return (
isinstance(node, nodes.BinOp)
and _is_valid_type(match.group(1), node.left)
and _is_valid_type(match.group(2), node.right)
)
# Special case for `xxx[aaa, bbb, ccc, ...]
if (
isinstance(node, nodes.Subscript)
and isinstance(node.slice, nodes.Tuple)
and (
match := _TYPE_HINT_MATCHERS[f"x_of_y_{len(node.slice.elts)}"].match(
expected_type
)
)
):
# This special case is separate because we want Mapping[str, Any]
# to also match dict[str, int] and similar
if (
len(node.slice.elts) == 2
and in_return
and match.group(1) == "Mapping"
and match.group(3) == "Any"
):
return (
isinstance(node.value, nodes.Name)
# We accept dict when Mapping is needed
and node.value.name in ("Mapping", "dict")
and isinstance(node.slice, nodes.Tuple)
and _is_valid_type(match.group(2), node.slice.elts[0])
# Ignore second item
# and _is_valid_type(match.group(3), node.slice.elts[1])
)
# This is the default case
return (
_is_valid_type(match.group(1), node.value)
and isinstance(node.slice, nodes.Tuple)
and all(
_is_valid_type(match.group(n + 2), node.slice.elts[n], in_return)
for n in range(len(node.slice.elts))
)
)
# Special case for xxx[yyy]
if match := _TYPE_HINT_MATCHERS["x_of_y_1"].match(expected_type):
return (
isinstance(node, nodes.Subscript)
and _is_valid_type(match.group(1), node.value)
and _is_valid_type(match.group(2), node.slice)
)
# Special case for float in return type
if (
expected_type == "float"
and in_return
and isinstance(node, nodes.Name)
and node.name in ("float", "int")
):
return True
# Special case for int in argument type
if (
expected_type == "int"
and not in_return
and isinstance(node, nodes.Name)
and node.name in ("float", "int")
):
return True
# Name occurs when a namespace is not used, eg. "HomeAssistant"
if isinstance(node, nodes.Name) and node.name == expected_type:
return True
# Attribute occurs when a namespace is used, eg. "core.HomeAssistant"
return isinstance(node, nodes.Attribute) and (
node.attrname == expected_type or node.as_string() == expected_type
)
def _is_valid_return_type(match: TypeHintMatch, node: nodes.NodeNG) -> bool:
if _is_valid_type(match.return_type, node, True):
return True
if isinstance(node, nodes.BinOp):
return _is_valid_return_type(match, node.left) and _is_valid_return_type(
match, node.right
)
if isinstance(match.return_type, (str, list)) and isinstance(node, nodes.Name):
if isinstance(match.return_type, str):
valid_types = {match.return_type}
else:
valid_types = {el for el in match.return_type if isinstance(el, str)}
if "Mapping[str, Any]" in valid_types:
valid_types.add("TypedDict")
try:
for infer_node in node.infer():
if _check_ancestry(infer_node, valid_types):
return True
except NameInferenceError:
for class_node in node.root().nodes_of_class(nodes.ClassDef):
if class_node.name != node.name:
continue
for infer_node in class_node.infer():
if _check_ancestry(infer_node, valid_types):
return True
return False
def _check_ancestry(infer_node: InferenceResult, valid_types: set[str]) -> bool:
if isinstance(infer_node, nodes.ClassDef):
if infer_node.name in valid_types:
return True
for ancestor in infer_node.ancestors():
if ancestor.name in valid_types:
return True
return False
def _get_all_annotations(node: nodes.FunctionDef) -> list[nodes.NodeNG | None]:
args = node.args
annotations: list[nodes.NodeNG | None] = (
args.posonlyargs_annotations + args.annotations + args.kwonlyargs_annotations
)
if args.vararg is not None:
annotations.append(args.varargannotation)
if args.kwarg is not None:
annotations.append(args.kwargannotation)
return annotations
def _get_named_annotation(
node: nodes.FunctionDef, key: str
) -> tuple[nodes.NodeNG, nodes.NodeNG] | tuple[None, None]:
args = node.args
for index, arg_node in enumerate(args.args):
if key == arg_node.name:
return arg_node, args.annotations[index]
for index, arg_node in enumerate(args.kwonlyargs):
if key == arg_node.name:
return arg_node, args.kwonlyargs_annotations[index]
return None, None
def _has_valid_annotations(
annotations: list[nodes.NodeNG | None],
) -> bool:
return any(annotation is not None for annotation in annotations)
def _get_module_platform(module_name: str) -> str | None:
"""Return the platform for the module name."""
if not (module_match := _MODULE_REGEX.match(module_name)):
# Ensure `homeassistant.components.<component>`
# Or `homeassistant.components.<component>.<platform>`
return None
platform = module_match.groups()[0]
return platform.lstrip(".") if platform else "__init__"
def _is_test_function(module_name: str, node: nodes.FunctionDef) -> bool:
"""Return True if function is a pytest function."""
return module_name.startswith("tests.") and node.name.startswith("test_")
class HassTypeHintChecker(BaseChecker):
"""Checker for setup type hints."""
name = "hass_enforce_type_hints"
priority = -1
msgs = {
"W7431": (
"Argument %s should be of type %s in %s",
"hass-argument-type",
"Used when method argument type is incorrect",
),
"W7432": (
"Return type should be %s in %s",
"hass-return-type",
"Used when method return type is incorrect",
),
}
options = (
(
"ignore-missing-annotations",
{
"default": False,
"type": "yn",
"metavar": "<y or n>",
"help": "Set to ``no`` if you wish to check functions that do not "
"have any type hints.",
},
),
)
_class_matchers: list[ClassTypeHintMatch]
_function_matchers: list[TypeHintMatch]
_module_name: str
def visit_module(self, node: nodes.Module) -> None:
"""Populate matchers for a Module node."""
self._class_matchers = []
self._function_matchers = []
self._module_name = node.name
if (module_platform := _get_module_platform(node.name)) is None:
return
if module_platform in _PLATFORMS:
self._function_matchers.extend(_FUNCTION_MATCH["__any_platform__"])
if function_matches := _FUNCTION_MATCH.get(module_platform):
self._function_matchers.extend(function_matches)
if class_matches := _CLASS_MATCH.get(module_platform):
self._class_matchers.extend(class_matches)
if property_matches := _INHERITANCE_MATCH.get(module_platform):
self._class_matchers.extend(property_matches)
self._class_matchers.reverse()
def _ignore_function(
self, node: nodes.FunctionDef, annotations: list[nodes.NodeNG | None]
) -> bool:
"""Check if we can skip the function validation."""
return (
self.linter.config.ignore_missing_annotations
and node.returns is None
and not _has_valid_annotations(annotations)
)
def visit_classdef(self, node: nodes.ClassDef) -> None:
"""Apply relevant type hint checks on a ClassDef node."""
ancestor: nodes.ClassDef
checked_class_methods: set[str] = set()
ancestors = list(node.ancestors()) # cache result for inside loop
for class_matcher in self._class_matchers:
skip_matcher = False
if exclude_base_classes := class_matcher.exclude_base_classes:
for ancestor in ancestors:
if ancestor.name in exclude_base_classes:
skip_matcher = True
break
if skip_matcher:
continue
for ancestor in ancestors:
if ancestor.name == class_matcher.base_class:
self._visit_class_functions(
node, class_matcher.matches, checked_class_methods
)
def _visit_class_functions(
self,
node: nodes.ClassDef,
matches: list[TypeHintMatch],
checked_class_methods: set[str],
) -> None:
cached_methods: list[nodes.FunctionDef] = list(node.mymethods())
for match in matches:
for function_node in cached_methods:
if (
function_node.name in checked_class_methods
or not match.need_to_check_function(function_node)
):
continue
annotations = _get_all_annotations(function_node)
if self._ignore_function(function_node, annotations):
continue
self._check_function(function_node, match, annotations)
checked_class_methods.add(function_node.name)
def visit_functiondef(self, node: nodes.FunctionDef) -> None:
"""Apply relevant type hint checks on a FunctionDef node."""
annotations = _get_all_annotations(node)
if self._ignore_function(node, annotations):
return
# Check that common arguments are correctly typed.
for arg_name, expected_type in _COMMON_ARGUMENTS.items():
arg_node, annotation = _get_named_annotation(node, arg_name)
if arg_node and not _is_valid_type(expected_type, annotation):
self.add_message(
"hass-argument-type",
node=arg_node,
args=(arg_name, expected_type, node.name),
)
# Check method or function matchers.
if node.is_method():
matchers = _METHOD_MATCH
else:
matchers = self._function_matchers
if _is_test_function(self._module_name, node):
self._check_test_function(node, annotations)
for match in matchers:
if not match.need_to_check_function(node):
continue
self._check_function(node, match, annotations)
visit_asyncfunctiondef = visit_functiondef
def _check_function(
self,
node: nodes.FunctionDef,
match: TypeHintMatch,
annotations: list[nodes.NodeNG | None],
) -> None:
# Check that all positional arguments are correctly annotated.
if match.arg_types:
for key, expected_type in match.arg_types.items():
if (
node.args.args[key].name in _COMMON_ARGUMENTS
or _is_test_function(self._module_name, node)
and node.args.args[key].name in _TEST_FIXTURES
):
# It has already been checked, avoid double-message
continue
if not _is_valid_type(expected_type, annotations[key]):
self.add_message(
"hass-argument-type",
node=node.args.args[key],
args=(key + 1, expected_type, node.name),
)
# Check that all keyword arguments are correctly annotated.
if match.named_arg_types is not None:
for arg_name, expected_type in match.named_arg_types.items():
if (
arg_name in _COMMON_ARGUMENTS
or _is_test_function(self._module_name, node)
and arg_name in _TEST_FIXTURES
):
# It has already been checked, avoid double-message
continue
arg_node, annotation = _get_named_annotation(node, arg_name)
if arg_node and not _is_valid_type(expected_type, annotation):
self.add_message(
"hass-argument-type",
node=arg_node,
args=(arg_name, expected_type, node.name),
)
# Check that kwargs is correctly annotated.
if match.kwargs_type and not _is_valid_type(
match.kwargs_type, node.args.kwargannotation
):
self.add_message(
"hass-argument-type",
node=node,
args=(node.args.kwarg, match.kwargs_type, node.name),
)
# Check the return type.
if not _is_valid_return_type(match, node.returns):
self.add_message(
"hass-return-type",
node=node,
args=(match.return_type or "None", node.name),
)
def _check_test_function(
self, node: nodes.FunctionDef, annotations: list[nodes.NodeNG | None]
) -> None:
# Check the return type.
if not _is_valid_return_type(_TEST_FUNCTION_MATCH, node.returns):
self.add_message(
"hass-return-type",
node=node,
args=(_TEST_FUNCTION_MATCH.return_type or "None", node.name),
)
# Check that all positional arguments are correctly annotated.
for arg_name, expected_type in _TEST_FIXTURES.items():
arg_node, annotation = _get_named_annotation(node, arg_name)
if arg_node and not _is_valid_type(expected_type, annotation):
self.add_message(
"hass-argument-type",
node=arg_node,
args=(arg_name, expected_type, node.name),
)
def register(linter: PyLinter) -> None:
"""Register the checker."""
linter.register_checker(HassTypeHintChecker(linter))