Add strict type annotations to alarm_control_panel (#50945)

* Add strict type annotations

* Apply suggestions

* Type code as optional string
pull/51005/head
Michael 2021-05-23 17:51:40 +02:00 committed by GitHub
parent f55213d8b1
commit dbefa8fac0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 128 additions and 77 deletions

View File

@ -10,6 +10,7 @@ homeassistant.components.aftership.*
homeassistant.components.air_quality.*
homeassistant.components.airly.*
homeassistant.components.aladdin_connect.*
homeassistant.components.alarm_control_panel.*
homeassistant.components.amazon_polly.*
homeassistant.components.ampio.*
homeassistant.components.automation.*

View File

@ -1,11 +1,14 @@
"""Component to interface with an alarm control panel."""
from __future__ import annotations
from abc import abstractmethod
from datetime import timedelta
import logging
from typing import final
from typing import Any, Final, final
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_CODE,
ATTR_CODE_FORMAT,
@ -16,14 +19,12 @@ from homeassistant.const import (
SERVICE_ALARM_DISARM,
SERVICE_ALARM_TRIGGER,
)
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import ( # noqa: F401
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
make_entity_service_schema,
)
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
from .const import (
SUPPORT_ALARM_ARM_AWAY,
@ -33,21 +34,26 @@ from .const import (
SUPPORT_ALARM_TRIGGER,
)
_LOGGER = logging.getLogger(__name__)
_LOGGER: Final = logging.getLogger(__name__)
DOMAIN = "alarm_control_panel"
SCAN_INTERVAL = timedelta(seconds=30)
ATTR_CHANGED_BY = "changed_by"
FORMAT_TEXT = "text"
FORMAT_NUMBER = "number"
ATTR_CODE_ARM_REQUIRED = "code_arm_required"
DOMAIN: Final = "alarm_control_panel"
SCAN_INTERVAL: Final = timedelta(seconds=30)
ATTR_CHANGED_BY: Final = "changed_by"
FORMAT_TEXT: Final = "text"
FORMAT_NUMBER: Final = "number"
ATTR_CODE_ARM_REQUIRED: Final = "code_arm_required"
ENTITY_ID_FORMAT = DOMAIN + ".{}"
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
ALARM_SERVICE_SCHEMA = make_entity_service_schema({vol.Optional(ATTR_CODE): cv.string})
ALARM_SERVICE_SCHEMA: Final = make_entity_service_schema(
{vol.Optional(ATTR_CODE): cv.string}
)
PLATFORM_SCHEMA: Final = cv.PLATFORM_SCHEMA
PLATFORM_SCHEMA_BASE: Final = cv.PLATFORM_SCHEMA_BASE
async def async_setup(hass, config):
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Track states and offer events for sensors."""
component = hass.data[DOMAIN] = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL
@ -92,79 +98,81 @@ async def async_setup(hass, config):
return True
async def async_setup_entry(hass, entry):
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry."""
return await hass.data[DOMAIN].async_setup_entry(entry)
component: EntityComponent = hass.data[DOMAIN]
return await component.async_setup_entry(entry)
async def async_unload_entry(hass, entry):
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.data[DOMAIN].async_unload_entry(entry)
component: EntityComponent = hass.data[DOMAIN]
return await component.async_unload_entry(entry)
class AlarmControlPanelEntity(Entity):
"""An abstract class for alarm control entities."""
@property
def code_format(self):
def code_format(self) -> str | None:
"""Regex for code format or None if no code is required."""
return None
@property
def changed_by(self):
def changed_by(self) -> str | None:
"""Last change triggered by."""
return None
@property
def code_arm_required(self):
def code_arm_required(self) -> bool:
"""Whether the code is required for arm actions."""
return True
def alarm_disarm(self, code=None):
def alarm_disarm(self, code: str | None = None) -> None:
"""Send disarm command."""
raise NotImplementedError()
async def async_alarm_disarm(self, code=None):
async def async_alarm_disarm(self, code: str | None = None) -> None:
"""Send disarm command."""
await self.hass.async_add_executor_job(self.alarm_disarm, code)
def alarm_arm_home(self, code=None):
def alarm_arm_home(self, code: str | None = None) -> None:
"""Send arm home command."""
raise NotImplementedError()
async def async_alarm_arm_home(self, code=None):
async def async_alarm_arm_home(self, code: str | None = None) -> None:
"""Send arm home command."""
await self.hass.async_add_executor_job(self.alarm_arm_home, code)
def alarm_arm_away(self, code=None):
def alarm_arm_away(self, code: str | None = None) -> None:
"""Send arm away command."""
raise NotImplementedError()
async def async_alarm_arm_away(self, code=None):
async def async_alarm_arm_away(self, code: str | None = None) -> None:
"""Send arm away command."""
await self.hass.async_add_executor_job(self.alarm_arm_away, code)
def alarm_arm_night(self, code=None):
def alarm_arm_night(self, code: str | None = None) -> None:
"""Send arm night command."""
raise NotImplementedError()
async def async_alarm_arm_night(self, code=None):
async def async_alarm_arm_night(self, code: str | None = None) -> None:
"""Send arm night command."""
await self.hass.async_add_executor_job(self.alarm_arm_night, code)
def alarm_trigger(self, code=None):
def alarm_trigger(self, code: str | None = None) -> None:
"""Send alarm trigger command."""
raise NotImplementedError()
async def async_alarm_trigger(self, code=None):
async def async_alarm_trigger(self, code: str | None = None) -> None:
"""Send alarm trigger command."""
await self.hass.async_add_executor_job(self.alarm_trigger, code)
def alarm_arm_custom_bypass(self, code=None):
def alarm_arm_custom_bypass(self, code: str | None = None) -> None:
"""Send arm custom bypass command."""
raise NotImplementedError()
async def async_alarm_arm_custom_bypass(self, code=None):
async def async_alarm_arm_custom_bypass(self, code: str | None = None) -> None:
"""Send arm custom bypass command."""
await self.hass.async_add_executor_job(self.alarm_arm_custom_bypass, code)
@ -175,7 +183,7 @@ class AlarmControlPanelEntity(Entity):
@final
@property
def state_attributes(self):
def state_attributes(self) -> dict[str, Any] | None:
"""Return the state attributes."""
return {
ATTR_CODE_FORMAT: self.code_format,
@ -187,9 +195,9 @@ class AlarmControlPanelEntity(Entity):
class AlarmControlPanel(AlarmControlPanelEntity):
"""An abstract class for alarm control entities (for backwards compatibility)."""
def __init_subclass__(cls, **kwargs):
def __init_subclass__(cls, **kwargs: Any) -> None:
"""Print deprecation warning."""
super().__init_subclass__(**kwargs)
super().__init_subclass__(**kwargs) # type: ignore[call-arg]
_LOGGER.warning(
"AlarmControlPanel is deprecated, modify %s to extend AlarmControlPanelEntity",
cls.__name__,

View File

@ -1,14 +1,16 @@
"""Provides the constants needed for component."""
SUPPORT_ALARM_ARM_HOME = 1
SUPPORT_ALARM_ARM_AWAY = 2
SUPPORT_ALARM_ARM_NIGHT = 4
SUPPORT_ALARM_TRIGGER = 8
SUPPORT_ALARM_ARM_CUSTOM_BYPASS = 16
from typing import Final
CONDITION_TRIGGERED = "is_triggered"
CONDITION_DISARMED = "is_disarmed"
CONDITION_ARMED_HOME = "is_armed_home"
CONDITION_ARMED_AWAY = "is_armed_away"
CONDITION_ARMED_NIGHT = "is_armed_night"
CONDITION_ARMED_CUSTOM_BYPASS = "is_armed_custom_bypass"
SUPPORT_ALARM_ARM_HOME: Final = 1
SUPPORT_ALARM_ARM_AWAY: Final = 2
SUPPORT_ALARM_ARM_NIGHT: Final = 4
SUPPORT_ALARM_TRIGGER: Final = 8
SUPPORT_ALARM_ARM_CUSTOM_BYPASS: Final = 16
CONDITION_TRIGGERED: Final = "is_triggered"
CONDITION_DISARMED: Final = "is_disarmed"
CONDITION_ARMED_HOME: Final = "is_armed_home"
CONDITION_ARMED_AWAY: Final = "is_armed_away"
CONDITION_ARMED_NIGHT: Final = "is_armed_night"
CONDITION_ARMED_CUSTOM_BYPASS: Final = "is_armed_custom_bypass"

View File

@ -1,6 +1,8 @@
"""Provides device automations for Alarm control panel."""
from __future__ import annotations
from typing import Final
import voluptuous as vol
from homeassistant.const import (
@ -21,6 +23,7 @@ from homeassistant.const import (
from homeassistant.core import Context, HomeAssistant
from homeassistant.helpers import entity_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType
from . import ATTR_CODE_ARM_REQUIRED, DOMAIN
from .const import (
@ -30,9 +33,15 @@ from .const import (
SUPPORT_ALARM_TRIGGER,
)
ACTION_TYPES = {"arm_away", "arm_home", "arm_night", "disarm", "trigger"}
ACTION_TYPES: Final[set[str]] = {
"arm_away",
"arm_home",
"arm_night",
"disarm",
"trigger",
}
ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
ACTION_SCHEMA: Final = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
{
vol.Required(CONF_TYPE): vol.In(ACTION_TYPES),
vol.Required(CONF_ENTITY_ID): cv.entity_domain(DOMAIN),
@ -41,7 +50,9 @@ ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
)
async def async_get_actions(hass: HomeAssistant, device_id: str) -> list[dict]:
async def async_get_actions(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device actions for Alarm control panel devices."""
registry = await entity_registry.async_get_registry(hass)
actions = []
@ -109,7 +120,7 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> list[dict]:
async def async_call_action_from_config(
hass: HomeAssistant, config: dict, variables: dict, context: Context | None
hass: HomeAssistant, config: ConfigType, variables: dict, context: Context | None
) -> None:
"""Execute a device action."""
service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]}
@ -132,7 +143,9 @@ async def async_call_action_from_config(
)
async def async_get_action_capabilities(hass, config):
async def async_get_action_capabilities(
hass: HomeAssistant, config: ConfigType
) -> dict[str, vol.Schema]:
"""List action capabilities."""
state = hass.states.get(config[CONF_ENTITY_ID])
code_required = state.attributes.get(ATTR_CODE_ARM_REQUIRED) if state else False

View File

@ -1,6 +1,8 @@
"""Provide the device automations for Alarm control panel."""
from __future__ import annotations
from typing import Final
import voluptuous as vol
from homeassistant.components.alarm_control_panel.const import (
@ -39,7 +41,7 @@ from .const import (
CONDITION_TRIGGERED,
)
CONDITION_TYPES = {
CONDITION_TYPES: Final[set[str]] = {
CONDITION_TRIGGERED,
CONDITION_DISARMED,
CONDITION_ARMED_HOME,
@ -48,7 +50,7 @@ CONDITION_TYPES = {
CONDITION_ARMED_CUSTOM_BYPASS,
}
CONDITION_SCHEMA = DEVICE_CONDITION_BASE_SCHEMA.extend(
CONDITION_SCHEMA: Final = DEVICE_CONDITION_BASE_SCHEMA.extend(
{
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_TYPE): vol.In(CONDITION_TYPES),

View File

@ -1,6 +1,8 @@
"""Provides device automations for Alarm control panel."""
from __future__ import annotations
from typing import Final
import voluptuous as vol
from homeassistant.components.alarm_control_panel.const import (
@ -32,10 +34,14 @@ from homeassistant.helpers.typing import ConfigType
from . import DOMAIN
BASIC_TRIGGER_TYPES = {"triggered", "disarmed", "arming"}
TRIGGER_TYPES = BASIC_TRIGGER_TYPES | {"armed_home", "armed_away", "armed_night"}
BASIC_TRIGGER_TYPES: Final[set[str]] = {"triggered", "disarmed", "arming"}
TRIGGER_TYPES: Final[set[str]] = BASIC_TRIGGER_TYPES | {
"armed_home",
"armed_away",
"armed_night",
}
TRIGGER_SCHEMA = TRIGGER_BASE_SCHEMA.extend(
TRIGGER_SCHEMA: Final = TRIGGER_BASE_SCHEMA.extend(
{
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_TYPE): vol.In(TRIGGER_TYPES),
@ -44,10 +50,12 @@ TRIGGER_SCHEMA = TRIGGER_BASE_SCHEMA.extend(
)
async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict]:
async def async_get_triggers(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device triggers for Alarm control panel devices."""
registry = await entity_registry.async_get_registry(hass)
triggers = []
triggers: list[dict[str, str]] = []
# Get all the integrations entities for this device
for entry in entity_registry.async_entries_for_device(registry, device_id):
@ -102,7 +110,9 @@ async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict]:
return triggers
async def async_get_trigger_capabilities(hass: HomeAssistant, config: dict) -> dict:
async def async_get_trigger_capabilities(
hass: HomeAssistant, config: ConfigType
) -> dict[str, vol.Schema]:
"""List trigger capabilities."""
return {
"extra_fields": vol.Schema(

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import asyncio
from collections.abc import Iterable
import logging
from typing import Any
from typing import Any, Final
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -25,9 +25,9 @@ from homeassistant.core import Context, HomeAssistant, State
from . import DOMAIN
_LOGGER = logging.getLogger(__name__)
_LOGGER: Final = logging.getLogger(__name__)
VALID_STATES = {
VALID_STATES: Final[set[str]] = {
STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME,

View File

@ -4,7 +4,7 @@ import logging
import voluptuous as vol
from homeassistant.components.alarm_control_panel import (
PLATFORM_SCHEMA,
PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
AlarmControlPanelEntity,
)
from homeassistant.components.alarm_control_panel.const import (
@ -37,7 +37,7 @@ DISARMED = "disarmed"
ICON = "mdi:security"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_HOME_MODE_NAME, default=ARMED): cv.string,
vol.Optional(CONF_AWAY_MODE_NAME, default=ARMED): cv.string,

View File

@ -7,7 +7,9 @@ import requests
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.components.alarm_control_panel import (
PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
)
from homeassistant.components.alarm_control_panel.const import (
SUPPORT_ALARM_ARM_AWAY,
SUPPORT_ALARM_ARM_HOME,
@ -33,7 +35,7 @@ DEFAULT_MODE = "audible"
SCAN_INTERVAL = datetime.timedelta(seconds=10)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,

View File

@ -7,7 +7,7 @@ import voluptuous as vol
from homeassistant.components.alarm_control_panel import (
FORMAT_NUMBER,
FORMAT_TEXT,
PLATFORM_SCHEMA,
PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
AlarmControlPanelEntity,
)
from homeassistant.components.alarm_control_panel.const import (
@ -54,7 +54,7 @@ DEFAULT_EVENT_DISARM = "alarm_disarm"
CONF_CODE_ARM_REQUIRED = "code_arm_required"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_CODE): cv.string,

View File

@ -7,7 +7,9 @@ import requests
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.components.alarm_control_panel import (
PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
)
from homeassistant.components.alarm_control_panel.const import (
SUPPORT_ALARM_ARM_AWAY,
SUPPORT_ALARM_ARM_HOME,
@ -35,7 +37,7 @@ SERVICE_BYPASS_ZONE = "bypass_zone"
SERVICE_UNBYPASS_ZONE = "unbypass_zone"
ATTR_ZONE = "zone"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,

View File

@ -6,7 +6,7 @@ import voluptuous as vol
from homeassistant.components.alarm_control_panel import (
ENTITY_ID_FORMAT,
FORMAT_NUMBER,
PLATFORM_SCHEMA,
PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
AlarmControlPanelEntity,
)
from homeassistant.components.alarm_control_panel.const import (
@ -68,7 +68,7 @@ ALARM_CONTROL_PANEL_SCHEMA = vol.Schema(
}
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_ALARM_CONTROL_PANELS): cv.schema_with_slug_keys(
ALARM_CONTROL_PANEL_SCHEMA

View File

@ -11,7 +11,7 @@ from yalesmartalarmclient.client import (
)
from homeassistant.components.alarm_control_panel import (
PLATFORM_SCHEMA,
PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
AlarmControlPanelEntity,
)
from homeassistant.components.alarm_control_panel.const import (
@ -36,7 +36,7 @@ DEFAULT_AREA_ID = "1"
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,

View File

@ -121,6 +121,17 @@ no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.alarm_control_panel.*]
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.amazon_polly.*]
check_untyped_defs = true
disallow_incomplete_defs = true