diff --git a/homeassistant/components/abode/alarm_control_panel.py b/homeassistant/components/abode/alarm_control_panel.py index f1ff08f3a0a..88a072bd79c 100644 --- a/homeassistant/components/abode/alarm_control_panel.py +++ b/homeassistant/components/abode/alarm_control_panel.py @@ -2,6 +2,10 @@ import logging import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, @@ -51,6 +55,11 @@ class AbodeAlarm(AbodeDevice, alarm.AlarmControlPanel): state = None return state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + def alarm_disarm(self, code=None): """Send disarm command.""" self._device.set_standby() diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index 6faad5dd51f..36cb8650ace 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -1,4 +1,5 @@ """Component to interface with an alarm control panel.""" +from abc import abstractmethod from datetime import timedelta import logging @@ -7,22 +8,30 @@ import voluptuous as vol from homeassistant.const import ( ATTR_CODE, ATTR_CODE_FORMAT, - SERVICE_ALARM_TRIGGER, - SERVICE_ALARM_DISARM, - SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY, - SERVICE_ALARM_ARM_NIGHT, SERVICE_ALARM_ARM_CUSTOM_BYPASS, + SERVICE_ALARM_ARM_HOME, + SERVICE_ALARM_ARM_NIGHT, + SERVICE_ALARM_DISARM, + SERVICE_ALARM_TRIGGER, ) +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.config_validation import ( # noqa: F401 ENTITY_SERVICE_SCHEMA, PLATFORM_SCHEMA, PLATFORM_SCHEMA_BASE, ) -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent +from .const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_CUSTOM_BYPASS, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_TRIGGER, +) + DOMAIN = "alarm_control_panel" SCAN_INTERVAL = timedelta(seconds=30) ATTR_CHANGED_BY = "changed_by" @@ -49,21 +58,34 @@ async def async_setup(hass, config): SERVICE_ALARM_DISARM, ALARM_SERVICE_SCHEMA, "async_alarm_disarm" ) component.async_register_entity_service( - SERVICE_ALARM_ARM_HOME, ALARM_SERVICE_SCHEMA, "async_alarm_arm_home" + SERVICE_ALARM_ARM_HOME, + ALARM_SERVICE_SCHEMA, + "async_alarm_arm_home", + [SUPPORT_ALARM_ARM_HOME], ) component.async_register_entity_service( - SERVICE_ALARM_ARM_AWAY, ALARM_SERVICE_SCHEMA, "async_alarm_arm_away" + SERVICE_ALARM_ARM_AWAY, + ALARM_SERVICE_SCHEMA, + "async_alarm_arm_away", + [SUPPORT_ALARM_ARM_AWAY], ) component.async_register_entity_service( - SERVICE_ALARM_ARM_NIGHT, ALARM_SERVICE_SCHEMA, "async_alarm_arm_night" + SERVICE_ALARM_ARM_NIGHT, + ALARM_SERVICE_SCHEMA, + "async_alarm_arm_night", + [SUPPORT_ALARM_ARM_NIGHT], ) component.async_register_entity_service( SERVICE_ALARM_ARM_CUSTOM_BYPASS, ALARM_SERVICE_SCHEMA, "async_alarm_arm_custom_bypass", + [SUPPORT_ALARM_ARM_CUSTOM_BYPASS], ) component.async_register_entity_service( - SERVICE_ALARM_TRIGGER, ALARM_SERVICE_SCHEMA, "async_alarm_trigger" + SERVICE_ALARM_TRIGGER, + ALARM_SERVICE_SCHEMA, + "async_alarm_trigger", + [SUPPORT_ALARM_TRIGGER], ) return True @@ -164,6 +186,11 @@ class AlarmControlPanel(Entity): """ return self.hass.async_add_executor_job(self.alarm_arm_custom_bypass, code) + @property + @abstractmethod + def supported_features(self) -> int: + """Return the list of supported features.""" + @property def state_attributes(self): """Return the state attributes.""" diff --git a/homeassistant/components/alarm_control_panel/const.py b/homeassistant/components/alarm_control_panel/const.py new file mode 100644 index 00000000000..77f7846fc34 --- /dev/null +++ b/homeassistant/components/alarm_control_panel/const.py @@ -0,0 +1,7 @@ +"""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 diff --git a/homeassistant/components/alarm_control_panel/device_action.py b/homeassistant/components/alarm_control_panel/device_action.py index a3c2b482261..81e444ae16f 100644 --- a/homeassistant/components/alarm_control_panel/device_action.py +++ b/homeassistant/components/alarm_control_panel/device_action.py @@ -1,5 +1,6 @@ """Provides device automations for Alarm control panel.""" -from typing import Optional, List +from typing import List, Optional + import voluptuous as vol from homeassistant.const import ( @@ -16,10 +17,17 @@ from homeassistant.const import ( SERVICE_ALARM_DISARM, SERVICE_ALARM_TRIGGER, ) -from homeassistant.core import HomeAssistant, Context +from homeassistant.core import Context, HomeAssistant from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv + from . import ATTR_CODE_ARM_REQUIRED, DOMAIN +from .const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_TRIGGER, +) ACTION_TYPES = {"arm_away", "arm_home", "arm_night", "disarm", "trigger"} @@ -42,31 +50,42 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> List[dict]: if entry.domain != DOMAIN: continue + state = hass.states.get(entry.entity_id) + + # We need a state or else we can't populate the HVAC and preset modes. + if state is None: + continue + + supported_features = state.attributes["supported_features"] + # Add actions for each entity that belongs to this integration - actions.append( - { - CONF_DEVICE_ID: device_id, - CONF_DOMAIN: DOMAIN, - CONF_ENTITY_ID: entry.entity_id, - CONF_TYPE: "arm_away", - } - ) - actions.append( - { - CONF_DEVICE_ID: device_id, - CONF_DOMAIN: DOMAIN, - CONF_ENTITY_ID: entry.entity_id, - CONF_TYPE: "arm_home", - } - ) - actions.append( - { - CONF_DEVICE_ID: device_id, - CONF_DOMAIN: DOMAIN, - CONF_ENTITY_ID: entry.entity_id, - CONF_TYPE: "arm_night", - } - ) + if supported_features & SUPPORT_ALARM_ARM_AWAY: + actions.append( + { + CONF_DEVICE_ID: device_id, + CONF_DOMAIN: DOMAIN, + CONF_ENTITY_ID: entry.entity_id, + CONF_TYPE: "arm_away", + } + ) + if supported_features & SUPPORT_ALARM_ARM_HOME: + actions.append( + { + CONF_DEVICE_ID: device_id, + CONF_DOMAIN: DOMAIN, + CONF_ENTITY_ID: entry.entity_id, + CONF_TYPE: "arm_home", + } + ) + if supported_features & SUPPORT_ALARM_ARM_NIGHT: + actions.append( + { + CONF_DEVICE_ID: device_id, + CONF_DOMAIN: DOMAIN, + CONF_ENTITY_ID: entry.entity_id, + CONF_TYPE: "arm_night", + } + ) actions.append( { CONF_DEVICE_ID: device_id, @@ -75,14 +94,15 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> List[dict]: CONF_TYPE: "disarm", } ) - actions.append( - { - CONF_DEVICE_ID: device_id, - CONF_DOMAIN: DOMAIN, - CONF_ENTITY_ID: entry.entity_id, - CONF_TYPE: "trigger", - } - ) + if supported_features & SUPPORT_ALARM_TRIGGER: + actions.append( + { + CONF_DEVICE_ID: device_id, + CONF_DOMAIN: DOMAIN, + CONF_ENTITY_ID: entry.entity_id, + CONF_TYPE: "trigger", + } + ) return actions diff --git a/homeassistant/components/alarmdecoder/alarm_control_panel.py b/homeassistant/components/alarmdecoder/alarm_control_panel.py index 288c1dfd1c7..f9bff3bfda7 100644 --- a/homeassistant/components/alarmdecoder/alarm_control_panel.py +++ b/homeassistant/components/alarmdecoder/alarm_control_panel.py @@ -4,6 +4,11 @@ import logging import voluptuous as vol import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, +) from homeassistant.const import ( ATTR_CODE, STATE_ALARM_ARMED_AWAY, @@ -122,6 +127,11 @@ class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT + @property def device_state_attributes(self): """Return the state attributes.""" diff --git a/homeassistant/components/alarmdotcom/alarm_control_panel.py b/homeassistant/components/alarmdotcom/alarm_control_panel.py index 07d69960e0b..dd6b1272223 100644 --- a/homeassistant/components/alarmdotcom/alarm_control_panel.py +++ b/homeassistant/components/alarmdotcom/alarm_control_panel.py @@ -7,6 +7,10 @@ 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.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( CONF_CODE, CONF_NAME, @@ -95,6 +99,11 @@ class AlarmDotCom(alarm.AlarmControlPanel): return STATE_ALARM_ARMED_AWAY return None + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + @property def device_state_attributes(self): """Return the state attributes.""" diff --git a/homeassistant/components/arlo/alarm_control_panel.py b/homeassistant/components/arlo/alarm_control_panel.py index a56b2a63372..838f319abc1 100644 --- a/homeassistant/components/arlo/alarm_control_panel.py +++ b/homeassistant/components/arlo/alarm_control_panel.py @@ -7,6 +7,11 @@ from homeassistant.components.alarm_control_panel import ( PLATFORM_SCHEMA, AlarmControlPanel, ) +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, +) from homeassistant.const import ( ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, @@ -91,6 +96,11 @@ class ArloBaseStation(AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT + def update(self): """Update the state of the device.""" _LOGGER.debug("Updating Arlo Alarm Control Panel %s", self.name) diff --git a/homeassistant/components/blink/alarm_control_panel.py b/homeassistant/components/blink/alarm_control_panel.py index b1c9f6a7ec0..9b23c1606d4 100644 --- a/homeassistant/components/blink/alarm_control_panel.py +++ b/homeassistant/components/blink/alarm_control_panel.py @@ -2,6 +2,7 @@ import logging from homeassistant.components.alarm_control_panel import AlarmControlPanel +from homeassistant.components.alarm_control_panel.const import SUPPORT_ALARM_ARM_AWAY from homeassistant.const import ( ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, @@ -52,6 +53,11 @@ class BlinkSyncModule(AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_AWAY + @property def name(self): """Return the name of the panel.""" diff --git a/homeassistant/components/canary/alarm_control_panel.py b/homeassistant/components/canary/alarm_control_panel.py index 856ecb9f3a2..cceb78743d3 100644 --- a/homeassistant/components/canary/alarm_control_panel.py +++ b/homeassistant/components/canary/alarm_control_panel.py @@ -4,6 +4,11 @@ import logging from canary.api import LOCATION_MODE_AWAY, LOCATION_MODE_HOME, LOCATION_MODE_NIGHT from homeassistant.components.alarm_control_panel import AlarmControlPanel +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, +) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, @@ -59,6 +64,11 @@ class CanaryAlarm(AlarmControlPanel): return STATE_ALARM_ARMED_NIGHT return None + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT + @property def device_state_attributes(self): """Return the state attributes.""" diff --git a/homeassistant/components/concord232/alarm_control_panel.py b/homeassistant/components/concord232/alarm_control_panel.py index 37bbf052838..81a54a182d4 100644 --- a/homeassistant/components/concord232/alarm_control_panel.py +++ b/homeassistant/components/concord232/alarm_control_panel.py @@ -8,6 +8,10 @@ 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.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( CONF_CODE, CONF_HOST, @@ -85,6 +89,11 @@ class Concord232Alarm(alarm.AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + def update(self): """Update values from API.""" try: diff --git a/homeassistant/components/demo/alarm_control_panel.py b/homeassistant/components/demo/alarm_control_panel.py index d82edadf161..0323b68b1b0 100644 --- a/homeassistant/components/demo/alarm_control_panel.py +++ b/homeassistant/components/demo/alarm_control_panel.py @@ -1,16 +1,17 @@ """Demo platform that has two fake alarm control panels.""" import datetime + from homeassistant.components.manual.alarm_control_panel import ManualAlarm from homeassistant.const import ( + CONF_DELAY_TIME, + CONF_PENDING_TIME, + CONF_TRIGGER_TIME, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, - CONF_DELAY_TIME, - CONF_PENDING_TIME, - CONF_TRIGGER_TIME, ) diff --git a/homeassistant/components/egardia/alarm_control_panel.py b/homeassistant/components/egardia/alarm_control_panel.py index 22a458ae9aa..2c18be47a1f 100644 --- a/homeassistant/components/egardia/alarm_control_panel.py +++ b/homeassistant/components/egardia/alarm_control_panel.py @@ -4,6 +4,10 @@ import logging import requests import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, @@ -79,6 +83,11 @@ class EgardiaAlarm(alarm.AlarmControlPanel): """Return the state of the device.""" return self._status + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + @property def should_poll(self): """Poll if no report server is enabled.""" diff --git a/homeassistant/components/elkm1/alarm_control_panel.py b/homeassistant/components/elkm1/alarm_control_panel.py index 38519ab5b3f..8c4db6e06a5 100644 --- a/homeassistant/components/elkm1/alarm_control_panel.py +++ b/homeassistant/components/elkm1/alarm_control_panel.py @@ -3,6 +3,11 @@ from elkm1_lib.const import AlarmState, ArmedStatus, ArmLevel, ArmUpState import voluptuous as vol import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, +) from homeassistant.const import ( ATTR_CODE, ATTR_ENTITY_ID, @@ -143,6 +148,11 @@ class ElkArea(ElkEntity, alarm.AlarmControlPanel): """Return the state of the element.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT + @property def device_state_attributes(self): """Attributes of the area.""" diff --git a/homeassistant/components/envisalink/alarm_control_panel.py b/homeassistant/components/envisalink/alarm_control_panel.py index 663f19c8ed5..19703297ccd 100644 --- a/homeassistant/components/envisalink/alarm_control_panel.py +++ b/homeassistant/components/envisalink/alarm_control_panel.py @@ -4,6 +4,12 @@ import logging import voluptuous as vol import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_TRIGGER, +) from homeassistant.const import ( ATTR_ENTITY_ID, STATE_ALARM_ARMED_AWAY, @@ -141,6 +147,16 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel): state = STATE_ALARM_DISARMED return state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return ( + SUPPORT_ALARM_ARM_HOME + | SUPPORT_ALARM_ARM_AWAY + | SUPPORT_ALARM_ARM_NIGHT + | SUPPORT_ALARM_TRIGGER + ) + async def async_alarm_disarm(self, code=None): """Send disarm command.""" if code: diff --git a/homeassistant/components/homekit_controller/alarm_control_panel.py b/homeassistant/components/homekit_controller/alarm_control_panel.py index bb45a6c33d9..8cdbe9b2f36 100644 --- a/homeassistant/components/homekit_controller/alarm_control_panel.py +++ b/homeassistant/components/homekit_controller/alarm_control_panel.py @@ -4,6 +4,11 @@ import logging from homekit.model.characteristics import CharacteristicsTypes from homeassistant.components.alarm_control_panel import AlarmControlPanel +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, +) from homeassistant.const import ( ATTR_BATTERY_LEVEL, STATE_ALARM_ARMED_AWAY, @@ -88,6 +93,11 @@ class HomeKitAlarmControlPanel(HomeKitEntity, AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT + async def async_alarm_disarm(self, code=None): """Send disarm command.""" await self.set_alarm_state(STATE_ALARM_DISARMED, code) diff --git a/homeassistant/components/homematicip_cloud/alarm_control_panel.py b/homeassistant/components/homematicip_cloud/alarm_control_panel.py index f362133034f..f9a91203426 100644 --- a/homeassistant/components/homematicip_cloud/alarm_control_panel.py +++ b/homeassistant/components/homematicip_cloud/alarm_control_panel.py @@ -5,6 +5,10 @@ from typing import Any, Dict from homematicip.functionalHomes import SecurityAndAlarmHome from homeassistant.components.alarm_control_panel import AlarmControlPanel +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, @@ -77,6 +81,11 @@ class HomematicipAlarmControlPanel(AlarmControlPanel): def _security_and_alarm(self) -> SecurityAndAlarmHome: return self._home.get_functionalHome(SecurityAndAlarmHome) + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + async def async_alarm_disarm(self, code=None) -> None: """Send disarm command.""" await self._home.set_security_zones_activation(False, False) diff --git a/homeassistant/components/ialarm/alarm_control_panel.py b/homeassistant/components/ialarm/alarm_control_panel.py index 845c6b9021f..c3bcf626f0a 100644 --- a/homeassistant/components/ialarm/alarm_control_panel.py +++ b/homeassistant/components/ialarm/alarm_control_panel.py @@ -6,6 +6,10 @@ 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.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( CONF_CODE, CONF_HOST, @@ -91,6 +95,11 @@ class IAlarmPanel(alarm.AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + def update(self): """Return the state of the device.""" status = self._client.get_status() diff --git a/homeassistant/components/ifttt/alarm_control_panel.py b/homeassistant/components/ifttt/alarm_control_panel.py index e4d8b6ce654..f740cc8ccc9 100644 --- a/homeassistant/components/ifttt/alarm_control_panel.py +++ b/homeassistant/components/ifttt/alarm_control_panel.py @@ -6,6 +6,11 @@ import voluptuous as vol import homeassistant.components.alarm_control_panel as alarm from homeassistant.components.alarm_control_panel import DOMAIN, PLATFORM_SCHEMA +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, +) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_STATE, @@ -127,6 +132,11 @@ class IFTTTAlarmPanel(alarm.AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT + @property def assumed_state(self): """Notify that this platform return an assumed state.""" diff --git a/homeassistant/components/lupusec/alarm_control_panel.py b/homeassistant/components/lupusec/alarm_control_panel.py index 245743d0f65..c6ad817bfbf 100644 --- a/homeassistant/components/lupusec/alarm_control_panel.py +++ b/homeassistant/components/lupusec/alarm_control_panel.py @@ -2,6 +2,10 @@ from datetime import timedelta from homeassistant.components.alarm_control_panel import AlarmControlPanel +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, @@ -51,6 +55,11 @@ class LupusecAlarm(LupusecDevice, AlarmControlPanel): state = None return state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + def alarm_arm_away(self, code=None): """Send arm away command.""" self._device.set_away() diff --git a/homeassistant/components/manual/alarm_control_panel.py b/homeassistant/components/manual/alarm_control_panel.py index ac234dc0ac9..b41da2d51bd 100644 --- a/homeassistant/components/manual/alarm_control_panel.py +++ b/homeassistant/components/manual/alarm_control_panel.py @@ -7,6 +7,13 @@ import re import voluptuous as vol import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_CUSTOM_BYPASS, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_TRIGGER, +) from homeassistant.const import ( CONF_CODE, CONF_DELAY_TIME, @@ -25,8 +32,8 @@ from homeassistant.const import ( ) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import track_point_in_time -import homeassistant.util.dt as dt_util from homeassistant.helpers.restore_state import RestoreEntity +import homeassistant.util.dt as dt_util _LOGGER = logging.getLogger(__name__) @@ -234,6 +241,17 @@ class ManualAlarm(alarm.AlarmControlPanel, RestoreEntity): return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return ( + SUPPORT_ALARM_ARM_HOME + | SUPPORT_ALARM_ARM_AWAY + | SUPPORT_ALARM_ARM_NIGHT + | SUPPORT_ALARM_TRIGGER + | SUPPORT_ALARM_ARM_CUSTOM_BYPASS + ) + @property def _active_state(self): """Get the current state.""" diff --git a/homeassistant/components/manual_mqtt/alarm_control_panel.py b/homeassistant/components/manual_mqtt/alarm_control_panel.py index c57fa275516..f11dac357e6 100644 --- a/homeassistant/components/manual_mqtt/alarm_control_panel.py +++ b/homeassistant/components/manual_mqtt/alarm_control_panel.py @@ -6,30 +6,33 @@ import re import voluptuous as vol +from homeassistant.components import mqtt import homeassistant.components.alarm_control_panel as alarm -import homeassistant.util.dt as dt_util +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_TRIGGER, +) from homeassistant.const import ( + CONF_CODE, + CONF_DELAY_TIME, + CONF_DISARM_AFTER_TRIGGER, + CONF_NAME, + CONF_PENDING_TIME, + CONF_PLATFORM, + CONF_TRIGGER_TIME, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, - CONF_PLATFORM, - CONF_NAME, - CONF_CODE, - CONF_DELAY_TIME, - CONF_PENDING_TIME, - CONF_TRIGGER_TIME, - CONF_DISARM_AFTER_TRIGGER, ) -from homeassistant.components import mqtt - -from homeassistant.helpers.event import async_track_state_change from homeassistant.core import callback - import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.event import track_point_in_time +from homeassistant.helpers.event import async_track_state_change, track_point_in_time +import homeassistant.util.dt as dt_util _LOGGER = logging.getLogger(__name__) @@ -278,6 +281,16 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel): return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return ( + SUPPORT_ALARM_ARM_HOME + | SUPPORT_ALARM_ARM_AWAY + | SUPPORT_ALARM_ARM_NIGHT + | SUPPORT_ALARM_TRIGGER + ) + @property def _active_state(self): """Get the current state.""" diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index 7171a55b270..43d0bb570a8 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -6,6 +6,11 @@ import voluptuous as vol from homeassistant.components import mqtt import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, +) from homeassistant.const import ( CONF_CODE, CONF_DEVICE, @@ -223,6 +228,11 @@ class MqttAlarm( """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT + @property def code_format(self): """Return one or more digits/characters.""" diff --git a/homeassistant/components/ness_alarm/alarm_control_panel.py b/homeassistant/components/ness_alarm/alarm_control_panel.py index 1b45c52ab71..d2feebfb64f 100644 --- a/homeassistant/components/ness_alarm/alarm_control_panel.py +++ b/homeassistant/components/ness_alarm/alarm_control_panel.py @@ -3,6 +3,11 @@ import logging import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_TRIGGER, +) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMING, @@ -62,6 +67,11 @@ class NessAlarmPanel(alarm.AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_TRIGGER + async def async_alarm_disarm(self, code=None): """Send disarm command.""" await self._client.disarm(code) diff --git a/homeassistant/components/nx584/alarm_control_panel.py b/homeassistant/components/nx584/alarm_control_panel.py index d3d867ff378..62bc7ae32bb 100644 --- a/homeassistant/components/nx584/alarm_control_panel.py +++ b/homeassistant/components/nx584/alarm_control_panel.py @@ -6,6 +6,10 @@ 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.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( CONF_HOST, CONF_NAME, @@ -79,6 +83,11 @@ class NX584Alarm(alarm.AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + def update(self): """Process new events from panel.""" try: diff --git a/homeassistant/components/point/alarm_control_panel.py b/homeassistant/components/point/alarm_control_panel.py index f9e725f6c8e..e86b3dd42e8 100644 --- a/homeassistant/components/point/alarm_control_panel.py +++ b/homeassistant/components/point/alarm_control_panel.py @@ -2,6 +2,7 @@ import logging from homeassistant.components.alarm_control_panel import DOMAIN, AlarmControlPanel +from homeassistant.components.alarm_control_panel.const import SUPPORT_ALARM_ARM_AWAY from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_DISARMED, @@ -88,6 +89,11 @@ class MinutPointAlarmControl(AlarmControlPanel): """Return state of the device.""" return EVENT_MAP.get(self._home["alarm_status"], STATE_ALARM_ARMED_AWAY) + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_AWAY + @property def changed_by(self): """Return the user the last change was triggered by.""" diff --git a/homeassistant/components/satel_integra/alarm_control_panel.py b/homeassistant/components/satel_integra/alarm_control_panel.py index 2f0e165f21f..c4321673061 100644 --- a/homeassistant/components/satel_integra/alarm_control_panel.py +++ b/homeassistant/components/satel_integra/alarm_control_panel.py @@ -1,9 +1,13 @@ """Support for Satel Integra alarm, using ETHM module.""" import asyncio -import logging from collections import OrderedDict +import logging import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, @@ -17,8 +21,8 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import ( CONF_ARM_HOME_MODE, CONF_DEVICE_PARTITIONS, - DATA_SATEL, CONF_ZONE_NAME, + DATA_SATEL, SIGNAL_PANEL_MESSAGE, ) @@ -131,6 +135,11 @@ class SatelIntegraAlarmPanel(alarm.AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + async def async_alarm_disarm(self, code=None): """Send disarm command.""" if not code: diff --git a/homeassistant/components/simplisafe/alarm_control_panel.py b/homeassistant/components/simplisafe/alarm_control_panel.py index a63a077ed15..96f3fa05f6b 100644 --- a/homeassistant/components/simplisafe/alarm_control_panel.py +++ b/homeassistant/components/simplisafe/alarm_control_panel.py @@ -10,6 +10,10 @@ from homeassistant.components.alarm_control_panel import ( FORMAT_TEXT, AlarmControlPanel, ) +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( CONF_CODE, STATE_ALARM_ARMED_AWAY, @@ -94,6 +98,11 @@ class SimpliSafeAlarm(SimpliSafeEntity, AlarmControlPanel): """Return the state of the entity.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + def _validate_code(self, code, state): """Validate given code.""" check = self._code is None or code == self._code diff --git a/homeassistant/components/spc/alarm_control_panel.py b/homeassistant/components/spc/alarm_control_panel.py index 8eeccc06515..fa9a9681fff 100644 --- a/homeassistant/components/spc/alarm_control_panel.py +++ b/homeassistant/components/spc/alarm_control_panel.py @@ -2,6 +2,11 @@ import logging import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, +) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, @@ -80,6 +85,11 @@ class SpcAlarm(alarm.AlarmControlPanel): """Return the state of the device.""" return _get_alarm_state(self._area) + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT + async def async_alarm_disarm(self, code=None): """Send disarm command.""" from pyspcwebgw.const import AreaMode diff --git a/homeassistant/components/totalconnect/alarm_control_panel.py b/homeassistant/components/totalconnect/alarm_control_panel.py index e5f0b0c8279..b8b4236806f 100644 --- a/homeassistant/components/totalconnect/alarm_control_panel.py +++ b/homeassistant/components/totalconnect/alarm_control_panel.py @@ -2,15 +2,20 @@ import logging import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, +) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, + STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, - STATE_ALARM_DISARMED, STATE_ALARM_ARMING, + STATE_ALARM_DISARMED, STATE_ALARM_DISARMING, STATE_ALARM_TRIGGERED, - STATE_ALARM_ARMED_CUSTOM_BYPASS, ) from . import DOMAIN as TOTALCONNECT_DOMAIN @@ -55,6 +60,11 @@ class TotalConnectAlarm(alarm.AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT + @property def device_state_attributes(self): """Return the state attributes of the device.""" diff --git a/homeassistant/components/verisure/alarm_control_panel.py b/homeassistant/components/verisure/alarm_control_panel.py index 02f64b6fa9c..b7cc822bd43 100644 --- a/homeassistant/components/verisure/alarm_control_panel.py +++ b/homeassistant/components/verisure/alarm_control_panel.py @@ -3,6 +3,10 @@ import logging from time import sleep import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, @@ -64,6 +68,11 @@ class VerisureAlarm(alarm.AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + @property def code_format(self): """Return one or more digits/characters.""" diff --git a/homeassistant/components/wink/alarm_control_panel.py b/homeassistant/components/wink/alarm_control_panel.py index 654252f5ffe..733022e91b1 100644 --- a/homeassistant/components/wink/alarm_control_panel.py +++ b/homeassistant/components/wink/alarm_control_panel.py @@ -4,6 +4,10 @@ import logging import pywink import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, +) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, @@ -52,6 +56,11 @@ class WinkCameraDevice(WinkDevice, alarm.AlarmControlPanel): state = None return state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + def alarm_disarm(self, code=None): """Send disarm command.""" self.wink.set_mode("home") diff --git a/homeassistant/components/yale_smart_alarm/alarm_control_panel.py b/homeassistant/components/yale_smart_alarm/alarm_control_panel.py index c2d0ab01247..7f2cbc2a33d 100644 --- a/homeassistant/components/yale_smart_alarm/alarm_control_panel.py +++ b/homeassistant/components/yale_smart_alarm/alarm_control_panel.py @@ -11,13 +11,17 @@ from yalesmartalarmclient.client import ( ) from homeassistant.components.alarm_control_panel import ( - AlarmControlPanel, PLATFORM_SCHEMA, + AlarmControlPanel, +) +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, ) from homeassistant.const import ( + CONF_NAME, CONF_PASSWORD, CONF_USERNAME, - CONF_NAME, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, @@ -83,6 +87,11 @@ class YaleAlarmDevice(AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY + def update(self): """Return the state of the device.""" armed_status = self._client.get_armed_status() diff --git a/tests/components/alarm_control_panel/test_device_action.py b/tests/components/alarm_control_panel/test_device_action.py index c2dfcbd78b9..bc489dbe251 100644 --- a/tests/components/alarm_control_panel/test_device_action.py +++ b/tests/components/alarm_control_panel/test_device_action.py @@ -46,6 +46,9 @@ async def test_get_actions(hass, device_reg, entity_reg): connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, ) entity_reg.async_get_or_create(DOMAIN, "test", "5678", device_id=device_entry.id) + hass.states.async_set( + "alarm_control_panel.test_5678", "attributes", {"supported_features": 15} + ) expected_actions = [ { "domain": DOMAIN, @@ -82,6 +85,36 @@ async def test_get_actions(hass, device_reg, entity_reg): assert_lists_same(actions, expected_actions) +async def test_get_actions_arm_night_only(hass, device_reg, entity_reg): + """Test we get the expected actions from a alarm_control_panel.""" + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + device_entry = device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + entity_reg.async_get_or_create(DOMAIN, "test", "5678", device_id=device_entry.id) + hass.states.async_set( + "alarm_control_panel.test_5678", "attributes", {"supported_features": 4} + ) + expected_actions = [ + { + "domain": DOMAIN, + "type": "arm_night", + "device_id": device_entry.id, + "entity_id": "alarm_control_panel.test_5678", + }, + { + "domain": DOMAIN, + "type": "disarm", + "device_id": device_entry.id, + "entity_id": "alarm_control_panel.test_5678", + }, + ] + actions = await async_get_device_automations(hass, "action", device_entry.id) + assert_lists_same(actions, expected_actions) + + async def test_get_action_capabilities(hass, device_reg, entity_reg): """Test we get the expected capabilities from a sensor trigger.""" platform = getattr(hass.components, f"test.{DOMAIN}") diff --git a/tests/components/device_automation/test_init.py b/tests/components/device_automation/test_init.py index 3c0e3b1eca7..bddef3286ac 100644 --- a/tests/components/device_automation/test_init.py +++ b/tests/components/device_automation/test_init.py @@ -184,6 +184,9 @@ async def test_websocket_get_action_capabilities( entity_reg.async_get_or_create( "alarm_control_panel", "test", "5678", device_id=device_entry.id ) + hass.states.async_set( + "alarm_control_panel.test_5678", "attributes", {"supported_features": 15} + ) expected_capabilities = { "arm_away": {"extra_fields": []}, "arm_home": {"extra_fields": []}, diff --git a/tests/testing_config/custom_components/test/alarm_control_panel.py b/tests/testing_config/custom_components/test/alarm_control_panel.py index 0e2842f8695..1ffa52086e7 100644 --- a/tests/testing_config/custom_components/test/alarm_control_panel.py +++ b/tests/testing_config/custom_components/test/alarm_control_panel.py @@ -11,6 +11,12 @@ from homeassistant.const import ( STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, ) +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_TRIGGER, +) from tests.common import MockEntity ENTITIES = {} @@ -64,6 +70,16 @@ class MockAlarm(MockEntity, AlarmControlPanel): """Return the state of the device.""" return self._state + @property + def supported_features(self) -> int: + """Return the list of supported features.""" + return ( + SUPPORT_ALARM_ARM_HOME + | SUPPORT_ALARM_ARM_AWAY + | SUPPORT_ALARM_ARM_NIGHT + | SUPPORT_ALARM_TRIGGER + ) + def alarm_arm_away(self, code=None): """Send arm away command.""" self._state = STATE_ALARM_ARMED_AWAY