-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
779 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
"""Demo platform that offers a fake event entity.""" | ||
from __future__ import annotations | ||
|
||
from homeassistant.components.event import EventDeviceClass, EventEntity | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.core import Event, HomeAssistant, callback | ||
from homeassistant.helpers.entity import DeviceInfo | ||
from homeassistant.helpers.entity_platform import AddEntitiesCallback | ||
|
||
from . import DOMAIN | ||
|
||
|
||
async def async_setup_entry( | ||
hass: HomeAssistant, | ||
config_entry: ConfigEntry, | ||
async_add_entities: AddEntitiesCallback, | ||
) -> None: | ||
"""Set up the demo event platform.""" | ||
async_add_entities([DemoEvent()]) | ||
|
||
|
||
class DemoEvent(EventEntity): | ||
"""Representation of a demo event entity.""" | ||
|
||
_attr_device_class = EventDeviceClass.BUTTON | ||
_attr_event_types = ["pressed"] | ||
_attr_has_entity_name = True | ||
_attr_name = "Button press" | ||
_attr_should_poll = False | ||
_attr_translation_key = "push" | ||
_attr_unique_id = "push" | ||
|
||
def __init__(self) -> None: | ||
"""Initialize the Demo event entity.""" | ||
self._attr_device_info = DeviceInfo( | ||
identifiers={(DOMAIN, "push")}, | ||
) | ||
|
||
async def async_added_to_hass(self) -> None: | ||
"""Register callbacks.""" | ||
self.hass.bus.async_listen("demo_button_pressed", self._async_handle_event) | ||
|
||
@callback | ||
def _async_handle_event(self, _: Event) -> None: | ||
"""Handle the demo button event.""" | ||
self._trigger_event("pressed") | ||
self.async_write_ha_state() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
"""Component for handling incoming events as a platform.""" | ||
from __future__ import annotations | ||
|
||
from dataclasses import asdict, dataclass | ||
from datetime import datetime, timedelta | ||
import logging | ||
from typing import Any, final | ||
|
||
from typing_extensions import Self | ||
|
||
from homeassistant.backports.enum import StrEnum | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers.config_validation import ( # noqa: F401 | ||
PLATFORM_SCHEMA, | ||
PLATFORM_SCHEMA_BASE, | ||
) | ||
from homeassistant.helpers.entity import EntityDescription | ||
from homeassistant.helpers.entity_component import EntityComponent | ||
from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity | ||
from homeassistant.helpers.typing import ConfigType | ||
from homeassistant.util import dt as dt_util | ||
|
||
from .const import ATTR_EVENT_TYPE, ATTR_EVENT_TYPES, DOMAIN | ||
|
||
SCAN_INTERVAL = timedelta(seconds=30) | ||
|
||
ENTITY_ID_FORMAT = DOMAIN + ".{}" | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
class EventDeviceClass(StrEnum): | ||
"""Device class for events.""" | ||
|
||
DOORBELL = "doorbell" | ||
BUTTON = "button" | ||
MOTION = "motion" | ||
|
||
|
||
__all__ = [ | ||
"ATTR_EVENT_TYPE", | ||
"ATTR_EVENT_TYPES", | ||
"DOMAIN", | ||
"PLATFORM_SCHEMA_BASE", | ||
"PLATFORM_SCHEMA", | ||
"EventDeviceClass", | ||
"EventEntity", | ||
"EventEntityDescription", | ||
"EventEntityFeature", | ||
] | ||
|
||
# mypy: disallow-any-generics | ||
|
||
|
||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: | ||
"""Set up Event entities.""" | ||
component = hass.data[DOMAIN] = EntityComponent[EventEntity]( | ||
_LOGGER, DOMAIN, hass, SCAN_INTERVAL | ||
) | ||
await component.async_setup(config) | ||
return True | ||
|
||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Set up a config entry.""" | ||
component: EntityComponent[EventEntity] = hass.data[DOMAIN] | ||
return await component.async_setup_entry(entry) | ||
|
||
|
||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Unload a config entry.""" | ||
component: EntityComponent[EventEntity] = hass.data[DOMAIN] | ||
return await component.async_unload_entry(entry) | ||
|
||
|
||
@dataclass | ||
class EventEntityDescription(EntityDescription): | ||
"""A class that describes event entities.""" | ||
|
||
device_class: EventDeviceClass | None = None | ||
event_types: list[str] | None = None | ||
|
||
|
||
@dataclass | ||
class EventExtraStoredData(ExtraStoredData): | ||
"""Object to hold extra stored data.""" | ||
|
||
last_event_type: str | None | ||
last_event_attributes: dict[str, Any] | None | ||
|
||
def as_dict(self) -> dict[str, Any]: | ||
"""Return a dict representation of the event data.""" | ||
return asdict(self) | ||
|
||
@classmethod | ||
def from_dict(cls, restored: dict[str, Any]) -> Self | None: | ||
"""Initialize a stored event state from a dict.""" | ||
try: | ||
return cls( | ||
restored["last_event_type"], | ||
restored["last_event_attributes"], | ||
) | ||
except KeyError: | ||
return None | ||
|
||
|
||
class EventEntity(RestoreEntity): | ||
"""Representation of a Event entity.""" | ||
|
||
entity_description: EventEntityDescription | ||
_attr_device_class: EventDeviceClass | None | ||
_attr_event_types: list[str] | ||
_attr_state: None | ||
|
||
__last_event_triggered: datetime | None = None | ||
__last_event_type: str | None = None | ||
__last_event_attributes: dict[str, Any] | None = None | ||
|
||
@property | ||
def device_class(self) -> EventDeviceClass | None: | ||
"""Return the class of this entity.""" | ||
if hasattr(self, "_attr_device_class"): | ||
return self._attr_device_class | ||
if hasattr(self, "entity_description"): | ||
return self.entity_description.device_class | ||
return None | ||
|
||
@property | ||
def event_types(self) -> list[str]: | ||
"""Return a list of possible events.""" | ||
if hasattr(self, "_attr_event_types"): | ||
return self._attr_event_types | ||
if ( | ||
hasattr(self, "entity_description") | ||
and self.entity_description.event_types is not None | ||
): | ||
return self.entity_description.event_types | ||
raise AttributeError() | ||
|
||
@final | ||
def _trigger_event( | ||
self, event_type: str, event_attributes: dict[str, Any] | None = None | ||
) -> None: | ||
"""Process a new event.""" | ||
if event_type not in self.event_types: | ||
raise ValueError(f"Invalid event type {event_type} for {self.entity_id}") | ||
self.__last_event_triggered = dt_util.utcnow() | ||
self.__last_event_type = event_type | ||
self.__last_event_attributes = event_attributes | ||
|
||
def _default_to_device_class_name(self) -> bool: | ||
"""Return True if an unnamed entity should be named by its device class. | ||
For events this is True if the entity has a device class. | ||
""" | ||
return self.device_class is not None | ||
|
||
@property | ||
@final | ||
def capability_attributes(self) -> dict[str, list[str]]: | ||
"""Return capability attributes.""" | ||
return { | ||
ATTR_EVENT_TYPES: self.event_types, | ||
} | ||
|
||
@property | ||
@final | ||
def state(self) -> str | None: | ||
"""Return the entity state.""" | ||
if (last_event := self.__last_event_triggered) is None: | ||
return None | ||
return last_event.isoformat(timespec="milliseconds") | ||
|
||
@final | ||
@property | ||
def state_attributes(self) -> dict[str, Any]: | ||
"""Return the state attributes.""" | ||
attributes = {ATTR_EVENT_TYPE: self.__last_event_type} | ||
if self.__last_event_attributes: | ||
attributes |= self.__last_event_attributes | ||
return attributes | ||
|
||
@final | ||
async def async_internal_added_to_hass(self) -> None: | ||
"""Call when the event entity is added to hass.""" | ||
await super().async_internal_added_to_hass() | ||
if ( | ||
(state := await self.async_get_last_state()) | ||
and state.state is not None | ||
and (event_data := await self.async_get_last_event_data()) | ||
): | ||
self.__last_event_triggered = dt_util.parse_datetime(state.state) | ||
self.__last_event_type = event_data.last_event_type | ||
self.__last_event_attributes = event_data.last_event_attributes | ||
|
||
@property | ||
def extra_restore_state_data(self) -> EventExtraStoredData: | ||
"""Return event specific state data to be restored.""" | ||
return EventExtraStoredData( | ||
self.__last_event_type, | ||
self.__last_event_attributes, | ||
) | ||
|
||
async def async_get_last_event_data(self) -> EventExtraStoredData | None: | ||
"""Restore event specific state date.""" | ||
if (restored_last_extra_data := await self.async_get_last_extra_data()) is None: | ||
return None | ||
return EventExtraStoredData.from_dict(restored_last_extra_data.as_dict()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"""Provides the constants needed for the component.""" | ||
|
||
DOMAIN = "event" | ||
ATTR_EVENT_TYPE = "event_type" | ||
ATTR_EVENT_TYPES = "event_types" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"domain": "event", | ||
"name": "Event", | ||
"codeowners": ["@home-assistant/core"], | ||
"documentation": "https://www.home-assistant.io/integrations/event", | ||
"integration_type": "entity", | ||
"quality_scale": "internal" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"""Integration platform for recorder.""" | ||
from __future__ import annotations | ||
|
||
from homeassistant.core import HomeAssistant, callback | ||
|
||
from . import ATTR_EVENT_TYPES | ||
|
||
|
||
@callback | ||
def exclude_attributes(hass: HomeAssistant) -> set[str]: | ||
"""Exclude static attributes from being recorded in the database.""" | ||
return {ATTR_EVENT_TYPES} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"title": "Event", | ||
"entity_component": { | ||
"_": { | ||
"name": "[%key:component::button::title%]", | ||
"state_attributes": { | ||
"event_type": { | ||
"name": "Event type" | ||
}, | ||
"event_types": { | ||
"name": "Event types" | ||
} | ||
} | ||
}, | ||
"doorbell": { | ||
"name": "Doorbell" | ||
}, | ||
"button": { | ||
"name": "Button" | ||
}, | ||
"motion": { | ||
"name": "Motion" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""The tests for the event integration.""" |
Oops, something went wrong.