mirror of
https://github.com/home-assistant/core.git
synced 2025-11-09 10:59:40 +00:00
Implement new state property for alarm_control_panel which is using an enum (#126283)
* Alarm state from enum * Fixes * Set final * Fix rebase * Test const * Fix breaking version * Fix other for alarm_control_panel * Fix integrations * More * More * More * More * Fix zha * Replace _attr_state * Fix alarm_control_panel * Fix tests * Fixes * Mods * Change some * More * More * More * Tests * Last tests * Return enum * Fix zha * Remove not needed check * Fix wording * Fix homekit * Mod prometheus * Fix mypy * Fix homekit * Fix ifttt
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
from functools import partial
|
||||
import logging
|
||||
@@ -33,6 +34,7 @@ from homeassistant.helpers.deprecation import (
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity_platform import EntityPlatform
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
@@ -49,6 +51,7 @@ from .const import ( # noqa: F401
|
||||
ATTR_CODE_ARM_REQUIRED,
|
||||
DOMAIN,
|
||||
AlarmControlPanelEntityFeature,
|
||||
AlarmControlPanelState,
|
||||
CodeFormat,
|
||||
)
|
||||
|
||||
@@ -142,6 +145,7 @@ CACHED_PROPERTIES_WITH_ATTR_ = {
|
||||
"changed_by",
|
||||
"code_arm_required",
|
||||
"supported_features",
|
||||
"alarm_state",
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +153,7 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
||||
"""An abstract class for alarm control entities."""
|
||||
|
||||
entity_description: AlarmControlPanelEntityDescription
|
||||
_attr_alarm_state: AlarmControlPanelState | None = None
|
||||
_attr_changed_by: str | None = None
|
||||
_attr_code_arm_required: bool = True
|
||||
_attr_code_format: CodeFormat | None = None
|
||||
@@ -157,6 +162,78 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
||||
)
|
||||
_alarm_control_panel_option_default_code: str | None = None
|
||||
|
||||
__alarm_legacy_state: bool = False
|
||||
__alarm_legacy_state_reported: bool = False
|
||||
|
||||
def __init_subclass__(cls, **kwargs: Any) -> None:
|
||||
"""Post initialisation processing."""
|
||||
super().__init_subclass__(**kwargs)
|
||||
if any(method in cls.__dict__ for method in ("_attr_state", "state")):
|
||||
# Integrations should use the 'alarm_state' property instead of
|
||||
# setting the state directly.
|
||||
cls.__alarm_legacy_state = True
|
||||
|
||||
def __setattr__(self, __name: str, __value: Any) -> None:
|
||||
"""Set attribute.
|
||||
|
||||
Deprecation warning if setting '_attr_state' directly
|
||||
unless already reported.
|
||||
"""
|
||||
if __name == "_attr_state":
|
||||
if self.__alarm_legacy_state_reported is not True:
|
||||
self._report_deprecated_alarm_state_handling()
|
||||
self.__alarm_legacy_state_reported = True
|
||||
return super().__setattr__(__name, __value)
|
||||
|
||||
@callback
|
||||
def add_to_platform_start(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
platform: EntityPlatform,
|
||||
parallel_updates: asyncio.Semaphore | None,
|
||||
) -> None:
|
||||
"""Start adding an entity to a platform."""
|
||||
super().add_to_platform_start(hass, platform, parallel_updates)
|
||||
if self.__alarm_legacy_state and not self.__alarm_legacy_state_reported:
|
||||
self._report_deprecated_alarm_state_handling()
|
||||
|
||||
@callback
|
||||
def _report_deprecated_alarm_state_handling(self) -> None:
|
||||
"""Report on deprecated handling of alarm state.
|
||||
|
||||
Integrations should implement alarm_state instead of using state directly.
|
||||
"""
|
||||
self.__alarm_legacy_state_reported = True
|
||||
if "custom_components" in type(self).__module__:
|
||||
# Do not report on core integrations as they have been fixed.
|
||||
report_issue = "report it to the custom integration author."
|
||||
_LOGGER.warning(
|
||||
"Entity %s (%s) is setting state directly"
|
||||
" which will stop working in HA Core 2025.11."
|
||||
" Entities should implement the 'alarm_state' property and"
|
||||
" return its state using the AlarmControlPanelState enum, please %s",
|
||||
self.entity_id,
|
||||
type(self),
|
||||
report_issue,
|
||||
)
|
||||
|
||||
@final
|
||||
@property
|
||||
def state(self) -> str | None:
|
||||
"""Return the current state."""
|
||||
if (alarm_state := self.alarm_state) is None:
|
||||
return None
|
||||
return alarm_state
|
||||
|
||||
@cached_property
|
||||
def alarm_state(self) -> AlarmControlPanelState | None:
|
||||
"""Return the current alarm control panel entity state.
|
||||
|
||||
Integrations should overwrite this or use the '_attr_alarm_state'
|
||||
attribute to set the alarm status using the 'AlarmControlPanelState' enum.
|
||||
"""
|
||||
return self._attr_alarm_state
|
||||
|
||||
@final
|
||||
@callback
|
||||
def code_or_default_code(self, code: str | None) -> str | None:
|
||||
|
||||
@@ -17,6 +17,21 @@ ATTR_CHANGED_BY: Final = "changed_by"
|
||||
ATTR_CODE_ARM_REQUIRED: Final = "code_arm_required"
|
||||
|
||||
|
||||
class AlarmControlPanelState(StrEnum):
|
||||
"""Alarm control panel entity states."""
|
||||
|
||||
DISARMED = "disarmed"
|
||||
ARMED_HOME = "armed_home"
|
||||
ARMED_AWAY = "armed_away"
|
||||
ARMED_NIGHT = "armed_night"
|
||||
ARMED_VACATION = "armed_vacation"
|
||||
ARMED_CUSTOM_BYPASS = "armed_custom_bypass"
|
||||
PENDING = "pending"
|
||||
ARMING = "arming"
|
||||
DISARMING = "disarming"
|
||||
TRIGGERED = "triggered"
|
||||
|
||||
|
||||
class CodeFormat(StrEnum):
|
||||
"""Code formats for the Alarm Control Panel."""
|
||||
|
||||
|
||||
@@ -13,13 +13,6 @@ from homeassistant.const import (
|
||||
CONF_DOMAIN,
|
||||
CONF_ENTITY_ID,
|
||||
CONF_TYPE,
|
||||
STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_ARMED_VACATION,
|
||||
STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_TRIGGERED,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import (
|
||||
@@ -31,7 +24,7 @@ from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
|
||||
from homeassistant.helpers.entity import get_supported_features
|
||||
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
|
||||
|
||||
from . import DOMAIN
|
||||
from . import DOMAIN, AlarmControlPanelState
|
||||
from .const import (
|
||||
CONDITION_ARMED_AWAY,
|
||||
CONDITION_ARMED_CUSTOM_BYPASS,
|
||||
@@ -109,19 +102,19 @@ def async_condition_from_config(
|
||||
) -> condition.ConditionCheckerType:
|
||||
"""Create a function to test a device condition."""
|
||||
if config[CONF_TYPE] == CONDITION_TRIGGERED:
|
||||
state = STATE_ALARM_TRIGGERED
|
||||
state = AlarmControlPanelState.TRIGGERED
|
||||
elif config[CONF_TYPE] == CONDITION_DISARMED:
|
||||
state = STATE_ALARM_DISARMED
|
||||
state = AlarmControlPanelState.DISARMED
|
||||
elif config[CONF_TYPE] == CONDITION_ARMED_HOME:
|
||||
state = STATE_ALARM_ARMED_HOME
|
||||
state = AlarmControlPanelState.ARMED_HOME
|
||||
elif config[CONF_TYPE] == CONDITION_ARMED_AWAY:
|
||||
state = STATE_ALARM_ARMED_AWAY
|
||||
state = AlarmControlPanelState.ARMED_AWAY
|
||||
elif config[CONF_TYPE] == CONDITION_ARMED_NIGHT:
|
||||
state = STATE_ALARM_ARMED_NIGHT
|
||||
state = AlarmControlPanelState.ARMED_NIGHT
|
||||
elif config[CONF_TYPE] == CONDITION_ARMED_VACATION:
|
||||
state = STATE_ALARM_ARMED_VACATION
|
||||
state = AlarmControlPanelState.ARMED_VACATION
|
||||
elif config[CONF_TYPE] == CONDITION_ARMED_CUSTOM_BYPASS:
|
||||
state = STATE_ALARM_ARMED_CUSTOM_BYPASS
|
||||
state = AlarmControlPanelState.ARMED_CUSTOM_BYPASS
|
||||
|
||||
registry = er.async_get(hass)
|
||||
entity_id = er.async_resolve_entity_id(registry, config[ATTR_ENTITY_ID])
|
||||
|
||||
@@ -15,13 +15,6 @@ from homeassistant.const import (
|
||||
CONF_FOR,
|
||||
CONF_PLATFORM,
|
||||
CONF_TYPE,
|
||||
STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_ARMED_VACATION,
|
||||
STATE_ALARM_ARMING,
|
||||
STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_TRIGGERED,
|
||||
)
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv, entity_registry as er
|
||||
@@ -29,7 +22,7 @@ from homeassistant.helpers.entity import get_supported_features
|
||||
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import DOMAIN
|
||||
from . import DOMAIN, AlarmControlPanelState
|
||||
from .const import AlarmControlPanelEntityFeature
|
||||
|
||||
BASIC_TRIGGER_TYPES: Final[set[str]] = {"triggered", "disarmed", "arming"}
|
||||
@@ -129,19 +122,19 @@ async def async_attach_trigger(
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Attach a trigger."""
|
||||
if config[CONF_TYPE] == "triggered":
|
||||
to_state = STATE_ALARM_TRIGGERED
|
||||
to_state = AlarmControlPanelState.TRIGGERED
|
||||
elif config[CONF_TYPE] == "disarmed":
|
||||
to_state = STATE_ALARM_DISARMED
|
||||
to_state = AlarmControlPanelState.DISARMED
|
||||
elif config[CONF_TYPE] == "arming":
|
||||
to_state = STATE_ALARM_ARMING
|
||||
to_state = AlarmControlPanelState.ARMING
|
||||
elif config[CONF_TYPE] == "armed_home":
|
||||
to_state = STATE_ALARM_ARMED_HOME
|
||||
to_state = AlarmControlPanelState.ARMED_HOME
|
||||
elif config[CONF_TYPE] == "armed_away":
|
||||
to_state = STATE_ALARM_ARMED_AWAY
|
||||
to_state = AlarmControlPanelState.ARMED_AWAY
|
||||
elif config[CONF_TYPE] == "armed_night":
|
||||
to_state = STATE_ALARM_ARMED_NIGHT
|
||||
to_state = AlarmControlPanelState.ARMED_NIGHT
|
||||
elif config[CONF_TYPE] == "armed_vacation":
|
||||
to_state = STATE_ALARM_ARMED_VACATION
|
||||
to_state = AlarmControlPanelState.ARMED_VACATION
|
||||
|
||||
state_config = {
|
||||
state_trigger.CONF_PLATFORM: "state",
|
||||
|
||||
@@ -16,28 +16,21 @@ from homeassistant.const import (
|
||||
SERVICE_ALARM_ARM_VACATION,
|
||||
SERVICE_ALARM_DISARM,
|
||||
SERVICE_ALARM_TRIGGER,
|
||||
STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_ARMED_VACATION,
|
||||
STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_TRIGGERED,
|
||||
)
|
||||
from homeassistant.core import Context, HomeAssistant, State
|
||||
|
||||
from . import DOMAIN
|
||||
from . import DOMAIN, AlarmControlPanelState
|
||||
|
||||
_LOGGER: Final = logging.getLogger(__name__)
|
||||
|
||||
VALID_STATES: Final[set[str]] = {
|
||||
STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_ARMED_VACATION,
|
||||
STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_TRIGGERED,
|
||||
AlarmControlPanelState.ARMED_AWAY,
|
||||
AlarmControlPanelState.ARMED_CUSTOM_BYPASS,
|
||||
AlarmControlPanelState.ARMED_HOME,
|
||||
AlarmControlPanelState.ARMED_NIGHT,
|
||||
AlarmControlPanelState.ARMED_VACATION,
|
||||
AlarmControlPanelState.DISARMED,
|
||||
AlarmControlPanelState.TRIGGERED,
|
||||
}
|
||||
|
||||
|
||||
@@ -65,19 +58,19 @@ async def _async_reproduce_state(
|
||||
|
||||
service_data = {ATTR_ENTITY_ID: state.entity_id}
|
||||
|
||||
if state.state == STATE_ALARM_ARMED_AWAY:
|
||||
if state.state == AlarmControlPanelState.ARMED_AWAY:
|
||||
service = SERVICE_ALARM_ARM_AWAY
|
||||
elif state.state == STATE_ALARM_ARMED_CUSTOM_BYPASS:
|
||||
elif state.state == AlarmControlPanelState.ARMED_CUSTOM_BYPASS:
|
||||
service = SERVICE_ALARM_ARM_CUSTOM_BYPASS
|
||||
elif state.state == STATE_ALARM_ARMED_HOME:
|
||||
elif state.state == AlarmControlPanelState.ARMED_HOME:
|
||||
service = SERVICE_ALARM_ARM_HOME
|
||||
elif state.state == STATE_ALARM_ARMED_NIGHT:
|
||||
elif state.state == AlarmControlPanelState.ARMED_NIGHT:
|
||||
service = SERVICE_ALARM_ARM_NIGHT
|
||||
elif state.state == STATE_ALARM_ARMED_VACATION:
|
||||
elif state.state == AlarmControlPanelState.ARMED_VACATION:
|
||||
service = SERVICE_ALARM_ARM_VACATION
|
||||
elif state.state == STATE_ALARM_DISARMED:
|
||||
elif state.state == AlarmControlPanelState.DISARMED:
|
||||
service = SERVICE_ALARM_DISARM
|
||||
elif state.state == STATE_ALARM_TRIGGERED:
|
||||
elif state.state == AlarmControlPanelState.TRIGGERED:
|
||||
service = SERVICE_ALARM_TRIGGER
|
||||
|
||||
await hass.services.async_call(
|
||||
|
||||
Reference in New Issue
Block a user