Files
2026-07-20 22:52:35 -04:00

645 lines
26 KiB
Python

"""Spook - Your homie."""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any
from homeassistant.components import (
automation,
input_boolean,
input_button,
input_datetime,
input_number,
input_select,
input_text,
persistent_notification,
person,
script,
sun,
zone,
)
from homeassistant.components.sensor import (
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
EVENT_COMPONENT_LOADED,
EVENT_HOMEASSISTANT_STARTED,
EntityCategory,
Platform,
)
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.helpers import (
area_registry as ar,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.event import async_call_later
from ...entity import SpookEntityDescription
from ...listeners import async_listen_once_tracked
from .entity import HomeAssistantSpookEntity
if TYPE_CHECKING:
from collections.abc import Callable
from datetime import datetime # Moved datetime here
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util.event_type import EventType
@dataclass(frozen=True, kw_only=True)
class HomeAssistantSpookSensorEntityDescription(
SpookEntityDescription,
SensorEntityDescription,
):
"""Class describing Spook Home Assistant sensor entities."""
value_fn: Callable[[HomeAssistant], int | None]
update_events: set[EventType[Any] | str] = field(default_factory=set)
@callback
def _count_active_domain_entities(hass: HomeAssistant, domain: str) -> int:
"""Count domain entities that are not restored placeholders."""
return sum(
(state := hass.states.get(entity_id)) is not None
and not state.attributes.get("restored", False)
for entity_id in hass.states.async_entity_ids(domain)
)
SENSORS: tuple[HomeAssistantSpookSensorEntityDescription, ...] = (
HomeAssistantSpookSensorEntityDescription(
key=Platform.AIR_QUALITY,
translation_key="homeassistant_air_quality",
entity_id="sensor.air_quality",
icon="mdi:air-filter",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.AIR_QUALITY)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.ALARM_CONTROL_PANEL,
translation_key="homeassistant_alarm_control_panel",
entity_id="sensor.alarm_control_panels",
icon="mdi:alarm-panel",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(
hass.states.async_entity_ids(Platform.ALARM_CONTROL_PANEL),
),
),
HomeAssistantSpookSensorEntityDescription(
key="area",
translation_key="homeassistant_area",
entity_id="sensor.areas",
icon="mdi:texture-box",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, ar.EVENT_AREA_REGISTRY_UPDATED},
value_fn=lambda hass: len(list(ar.async_get(hass).async_list_areas())),
),
HomeAssistantSpookSensorEntityDescription(
key=automation.DOMAIN,
translation_key="homeassistant_automation",
entity_id="sensor.automations",
icon="mdi:robot",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={automation.EVENT_AUTOMATION_RELOADED},
value_fn=lambda hass: _count_active_domain_entities(hass, automation.DOMAIN),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.BINARY_SENSOR,
translation_key="homeassistant_binary_sensor",
entity_id="sensor.binary_sensors",
icon="mdi:numeric-10",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.BINARY_SENSOR)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.BUTTON,
translation_key="homeassistant_button",
entity_id="sensor.buttons",
icon="mdi:gesture-tap",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.BUTTON)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.CALENDAR,
translation_key="homeassistant_calendar",
entity_id="sensor.calendars",
icon="mdi:calendar",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.CALENDAR)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.CAMERA,
translation_key="homeassistant_camera",
entity_id="sensor.cameras",
icon="mdi:cctv",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.CAMERA)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.CLIMATE,
translation_key="homeassistant_climate",
entity_id="sensor.climate",
icon="mdi:thermostat",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.CLIMATE)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.COVER,
translation_key="homeassistant_cover",
entity_id="sensor.covers",
icon="mdi:blinds",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.COVER)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.DATE,
translation_key="homeassistant_date",
entity_id="sensor.dates",
icon="mdi:calendar-month-outline",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.DATE)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.DATETIME,
translation_key="homeassistant_datetime",
entity_id="sensor.datetimes",
icon="mdi:calendar-clock",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.DATETIME)),
),
HomeAssistantSpookSensorEntityDescription(
key="device",
translation_key="homeassistant_device",
entity_id="sensor.devices",
icon="mdi:cellphone",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, dr.EVENT_DEVICE_REGISTRY_UPDATED},
value_fn=lambda hass: len(dr.async_get(hass).devices),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.DEVICE_TRACKER,
translation_key="homeassistant_device_tracker",
entity_id="sensor.device_trackers",
icon="mdi:cellphone-marker",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(
hass.states.async_entity_ids(Platform.DEVICE_TRACKER),
),
),
HomeAssistantSpookSensorEntityDescription(
key="entities",
translation_key="homeassistant_entities",
entity_id="sensor.entities",
icon="mdi:counter",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids()),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.FAN,
translation_key="homeassistant_fan",
entity_id="sensor.fans",
icon="mdi:fan",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.FAN)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.HUMIDIFIER,
translation_key="homeassistant_humidifier",
entity_id="sensor.humidifiers",
icon="mdi:air-humidifier",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.HUMIDIFIER)),
),
HomeAssistantSpookSensorEntityDescription(
key="integration",
translation_key="homeassistant_integration",
entity_id="sensor.integrations",
icon="mdi:package-variant-closed",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED},
value_fn=lambda hass: len(hass.data["entity_platform"]),
),
HomeAssistantSpookSensorEntityDescription(
key="custom_component",
translation_key="homeassistant_custom_component",
entity_id="sensor.custom_integrations",
icon="mdi:package-variant-closed",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED},
value_fn=lambda hass: len(hass.data["custom_components"]),
),
HomeAssistantSpookSensorEntityDescription(
key=input_boolean.DOMAIN,
translation_key="homeassistant_input_boolean",
entity_id="sensor.input_booleans",
icon="mdi:toggle-switch-outline",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(input_boolean.DOMAIN)),
),
HomeAssistantSpookSensorEntityDescription(
key=input_button.DOMAIN,
translation_key="homeassistant_input_button",
entity_id="sensor.input_buttons",
icon="mdi:gesture-tap-button",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(input_button.DOMAIN)),
),
HomeAssistantSpookSensorEntityDescription(
key=input_datetime.DOMAIN,
translation_key="homeassistant_input_datetime",
entity_id="sensor.input_datetimes",
icon="mdi:clock",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(input_datetime.DOMAIN)),
),
HomeAssistantSpookSensorEntityDescription(
key=input_number.DOMAIN,
translation_key="homeassistant_input_number",
entity_id="sensor.input_numbers",
icon="mdi:ray-vertex",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(input_number.DOMAIN)),
),
HomeAssistantSpookSensorEntityDescription(
key=input_select.DOMAIN,
translation_key="homeassistant_input_select",
entity_id="sensor.input_selects",
icon="mdi:form-dropdown",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(input_select.DOMAIN)),
),
HomeAssistantSpookSensorEntityDescription(
key=input_text.DOMAIN,
translation_key="homeassistant_input_text",
entity_id="sensor.input_texts",
icon="mdi:form-textbox",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(input_text.DOMAIN)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.IMAGE,
translation_key="homeassistant_image",
entity_id="sensor.images",
icon="mdi:image",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.IMAGE)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.LIGHT,
translation_key="homeassistant_light",
entity_id="sensor.lights",
icon="mdi:lightbulb",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.LIGHT)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.LOCK,
translation_key="homeassistant_lock",
entity_id="sensor.locks",
icon="mdi:lock",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.LOCK)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.MEDIA_PLAYER,
translation_key="homeassistant_media_player",
entity_id="sensor.media_players",
icon="mdi:record-player",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.MEDIA_PLAYER)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.NUMBER,
translation_key="homeassistant_number",
entity_id="sensor.numbers",
icon="mdi:ray-vertex",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.NUMBER)),
),
HomeAssistantSpookSensorEntityDescription(
key="persistent_notification",
translation_key="homeassistant_persistent_notification",
entity_id="sensor.persistent_notifications",
icon="mdi:bell-ring-outline",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={
"persistent_notifications_updated",
},
value_fn=lambda hass: len(
# pylint: disable-next=protected-access
persistent_notification._async_get_or_create_notifications( # noqa: SLF001
hass,
),
),
),
HomeAssistantSpookSensorEntityDescription(
key=person.DOMAIN,
translation_key="homeassistant_person",
entity_id="sensor.persons",
icon="mdi:account-group",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(person.DOMAIN)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.REMOTE,
translation_key="homeassistant_remote",
entity_id="sensor.remotes",
icon="mdi:remote",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.REMOTE)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.SCENE,
translation_key="homeassistant_scene",
entity_id="sensor.scenes",
icon="mdi:palette",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.SCENE)),
),
HomeAssistantSpookSensorEntityDescription(
key=script.DOMAIN,
translation_key="homeassistant_script",
entity_id="sensor.scripts",
icon="mdi:script-text",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: _count_active_domain_entities(hass, script.DOMAIN),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.SELECT,
translation_key="homeassistant_select",
entity_id="sensor.selects",
icon="mdi:format-list-bulleted",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.SELECT)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.SENSOR,
translation_key="homeassistant_sensor",
entity_id="sensor.sensors",
icon="mdi:eye",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.SENSOR)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.SIREN,
translation_key="homeassistant_siren",
entity_id="sensor.sirens",
icon="mdi:bullhorn",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.SIREN)),
),
HomeAssistantSpookSensorEntityDescription(
key=sun.DOMAIN,
translation_key="homeassistant_sun",
entity_id="sensor.suns",
icon="mdi:emoticon-cool",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED},
value_fn=lambda hass: len(hass.states.async_entity_ids(sun.DOMAIN)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.STT,
translation_key="homeassistant_stt",
entity_id="sensor.stt",
icon="mdi:microphone-message",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.STT)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.SWITCH,
translation_key="homeassistant_switch",
entity_id="sensor.switches",
icon="mdi:toggle-switch",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.SWITCH)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.TEXT,
translation_key="homeassistant_text",
entity_id="sensor.texts",
icon="mdi:form-textbox",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.TEXT)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.TIME,
translation_key="homeassistant_time",
entity_id="sensor.times",
icon="mdi:clock-time-eight-outline",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.TIME)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.TODO,
translation_key="homeassistant_todo",
entity_id="sensor.todos",
icon="mdi:clipboard-list",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.TODO)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.TTS,
translation_key="homeassistant_tts",
entity_id="sensor.tts",
icon="mdi:speaker-message",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.TTS)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.VACUUM,
translation_key="homeassistant_vacuum",
entity_id="sensor.vacuums",
icon="mdi:vacuum",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.VACUUM)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.UPDATE,
translation_key="homeassistant_update",
entity_id="sensor.update",
icon="mdi:cellphone-arrow-down",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.UPDATE)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.WATER_HEATER,
translation_key="homeassistant_water_heater",
entity_id="sensor.water_heaters",
icon="mdi:water-boiler",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.WATER_HEATER)),
),
HomeAssistantSpookSensorEntityDescription(
key=Platform.WEATHER,
translation_key="homeassistant_weather",
entity_id="sensor.weather",
icon="mdi:weather-cloudy",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(Platform.WEATHER)),
),
HomeAssistantSpookSensorEntityDescription(
key=zone.DOMAIN,
translation_key="homeassistant_zone",
entity_id="sensor.zones",
icon="mdi:selection-marker",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
update_events={EVENT_COMPONENT_LOADED, er.EVENT_ENTITY_REGISTRY_UPDATED},
value_fn=lambda hass: len(hass.states.async_entity_ids(zone.DOMAIN)),
),
)
async def async_setup_entry(
_hass: HomeAssistant,
_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Spook sensor."""
async_add_entities(
HomeAssistantSpookSensorEntity(description) for description in SENSORS
)
class HomeAssistantSpookSensorEntity(HomeAssistantSpookEntity, SensorEntity):
"""Spook sensor providig Home Asistant information."""
entity_description: HomeAssistantSpookSensorEntityDescription
_unsub_debouncer: Callable[[], None] | None = None
async def async_added_to_hass(self) -> None:
"""Register for sensor updates."""
@callback
def _debounced_update(
_now: datetime | None = None,
) -> None:
"""Update state after debounce."""
self._unsub_debouncer = None
self.async_schedule_update_ha_state()
@callback
def _update_state(_: Event) -> None:
"""Update state."""
if self._unsub_debouncer:
self._unsub_debouncer()
self._unsub_debouncer = async_call_later(self.hass, 5, _debounced_update)
for event in self.entity_description.update_events:
self.async_on_remove(self.hass.bus.async_listen(event, _update_state))
self.async_on_remove(
async_listen_once_tracked(
self.hass, EVENT_HOMEASSISTANT_STARTED, _update_state
),
)
async def async_will_remove_from_hass(self) -> None:
"""Clean up debounce timer."""
if self._unsub_debouncer:
self._unsub_debouncer()
self._unsub_debouncer = None
await super().async_will_remove_from_hass()
@property
def native_value(self) -> int | None:
"""Return the sensor value."""
return self.entity_description.value_fn(self.hass)