Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down
84 changes: 46 additions & 38 deletions homeassistant/components/alarm_control_panel/__init__.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -16,14 +19,12 @@
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,
Expand All @@ -33,21 +34,26 @@
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: Final = make_entity_service_schema(
{vol.Optional(ATTR_CODE): cv.string}
)

ALARM_SERVICE_SCHEMA = 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
Expand Down Expand Up @@ -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)

Expand All @@ -175,7 +183,7 @@ def supported_features(self) -> int:

@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,
Expand All @@ -187,9 +195,9 @@ def state_attributes(self):
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]
Copy link
Copy Markdown
Member Author

@mib1185 mib1185 May 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

python/mypy#4660 ... just if someone wants to deserve a badge 😄

_LOGGER.warning(
"AlarmControlPanel is deprecated, modify %s to extend AlarmControlPanelEntity",
cls.__name__,
Expand Down
24 changes: 13 additions & 11 deletions homeassistant/components/alarm_control_panel/const.py
Original file line number Diff line number Diff line change
@@ -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"
23 changes: 18 additions & 5 deletions homeassistant/components/alarm_control_panel/device_action.py
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -21,6 +23,7 @@
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 (
Expand All @@ -30,9 +33,15 @@
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),
Expand All @@ -41,7 +50,9 @@
)


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 = []
Expand Down Expand Up @@ -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]}
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -39,7 +41,7 @@
CONDITION_TRIGGERED,
)

CONDITION_TYPES = {
CONDITION_TYPES: Final[set[str]] = {
CONDITION_TRIGGERED,
CONDITION_DISARMED,
CONDITION_ARMED_HOME,
Expand All @@ -48,7 +50,7 @@
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),
Expand Down
22 changes: 16 additions & 6 deletions homeassistant/components/alarm_control_panel/device_trigger.py
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -32,10 +34,14 @@

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),
Expand All @@ -44,10 +50,12 @@
)


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):
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
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,
Expand All @@ -25,9 +25,9 @@

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,
Expand Down
Loading