mirror of https://github.com/home-assistant/core
307 lines
9.3 KiB
Python
307 lines
9.3 KiB
Python
"""Component to interface with binary sensors."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import timedelta
|
|
from enum import StrEnum
|
|
from functools import cached_property, partial
|
|
import logging
|
|
from typing import Literal, final
|
|
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.const import STATE_OFF, STATE_ON, EntityCategory
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers.config_validation import ( # noqa: F401
|
|
PLATFORM_SCHEMA,
|
|
PLATFORM_SCHEMA_BASE,
|
|
)
|
|
from homeassistant.helpers.deprecation import (
|
|
DeprecatedConstantEnum,
|
|
all_with_deprecated_constants,
|
|
check_if_deprecated_constant,
|
|
dir_with_deprecated_constants,
|
|
)
|
|
from homeassistant.helpers.entity import Entity, EntityDescription
|
|
from homeassistant.helpers.entity_component import EntityComponent
|
|
from homeassistant.helpers.typing import ConfigType
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
DOMAIN = "binary_sensor"
|
|
SCAN_INTERVAL = timedelta(seconds=30)
|
|
|
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
|
|
|
|
|
class BinarySensorDeviceClass(StrEnum):
|
|
"""Device class for binary sensors."""
|
|
|
|
# On means low, Off means normal
|
|
BATTERY = "battery"
|
|
|
|
# On means charging, Off means not charging
|
|
BATTERY_CHARGING = "battery_charging"
|
|
|
|
# On means carbon monoxide detected, Off means no carbon monoxide (clear)
|
|
CO = "carbon_monoxide"
|
|
|
|
# On means cold, Off means normal
|
|
COLD = "cold"
|
|
|
|
# On means connected, Off means disconnected
|
|
CONNECTIVITY = "connectivity"
|
|
|
|
# On means open, Off means closed
|
|
DOOR = "door"
|
|
|
|
# On means open, Off means closed
|
|
GARAGE_DOOR = "garage_door"
|
|
|
|
# On means gas detected, Off means no gas (clear)
|
|
GAS = "gas"
|
|
|
|
# On means hot, Off means normal
|
|
HEAT = "heat"
|
|
|
|
# On means light detected, Off means no light
|
|
LIGHT = "light"
|
|
|
|
# On means open (unlocked), Off means closed (locked)
|
|
LOCK = "lock"
|
|
|
|
# On means wet, Off means dry
|
|
MOISTURE = "moisture"
|
|
|
|
# On means motion detected, Off means no motion (clear)
|
|
MOTION = "motion"
|
|
|
|
# On means moving, Off means not moving (stopped)
|
|
MOVING = "moving"
|
|
|
|
# On means occupied, Off means not occupied (clear)
|
|
OCCUPANCY = "occupancy"
|
|
|
|
# On means open, Off means closed
|
|
OPENING = "opening"
|
|
|
|
# On means plugged in, Off means unplugged
|
|
PLUG = "plug"
|
|
|
|
# On means power detected, Off means no power
|
|
POWER = "power"
|
|
|
|
# On means home, Off means away
|
|
PRESENCE = "presence"
|
|
|
|
# On means problem detected, Off means no problem (OK)
|
|
PROBLEM = "problem"
|
|
|
|
# On means running, Off means not running
|
|
RUNNING = "running"
|
|
|
|
# On means unsafe, Off means safe
|
|
SAFETY = "safety"
|
|
|
|
# On means smoke detected, Off means no smoke (clear)
|
|
SMOKE = "smoke"
|
|
|
|
# On means sound detected, Off means no sound (clear)
|
|
SOUND = "sound"
|
|
|
|
# On means tampering detected, Off means no tampering (clear)
|
|
TAMPER = "tamper"
|
|
|
|
# On means update available, Off means up-to-date
|
|
UPDATE = "update"
|
|
|
|
# On means vibration detected, Off means no vibration
|
|
VIBRATION = "vibration"
|
|
|
|
# On means open, Off means closed
|
|
WINDOW = "window"
|
|
|
|
|
|
DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.Coerce(BinarySensorDeviceClass))
|
|
|
|
# DEVICE_CLASS* below are deprecated as of 2021.12
|
|
# use the BinarySensorDeviceClass enum instead.
|
|
DEVICE_CLASSES = [cls.value for cls in BinarySensorDeviceClass]
|
|
_DEPRECATED_DEVICE_CLASS_BATTERY = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.BATTERY, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_BATTERY_CHARGING = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.BATTERY_CHARGING, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_CO = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.CO, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_COLD = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.COLD, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_CONNECTIVITY = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.CONNECTIVITY, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_DOOR = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.DOOR, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_GARAGE_DOOR = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.GARAGE_DOOR, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_GAS = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.GAS, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_HEAT = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.HEAT, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_LIGHT = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.LIGHT, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_LOCK = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.LOCK, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_MOISTURE = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.MOISTURE, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_MOTION = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.MOTION, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_MOVING = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.MOVING, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_OCCUPANCY = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.OCCUPANCY, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_OPENING = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.OPENING, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_PLUG = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.PLUG, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_POWER = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.POWER, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_PRESENCE = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.PRESENCE, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_PROBLEM = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.PROBLEM, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_RUNNING = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.RUNNING, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_SAFETY = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.SAFETY, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_SMOKE = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.SMOKE, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_SOUND = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.SOUND, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_TAMPER = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.TAMPER, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_UPDATE = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.UPDATE, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_VIBRATION = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.VIBRATION, "2025.1"
|
|
)
|
|
_DEPRECATED_DEVICE_CLASS_WINDOW = DeprecatedConstantEnum(
|
|
BinarySensorDeviceClass.WINDOW, "2025.1"
|
|
)
|
|
|
|
# mypy: disallow-any-generics
|
|
|
|
|
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|
"""Track states and offer events for binary sensors."""
|
|
component = hass.data[DOMAIN] = EntityComponent[BinarySensorEntity](
|
|
logging.getLogger(__name__), 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[BinarySensorEntity] = 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[BinarySensorEntity] = hass.data[DOMAIN]
|
|
return await component.async_unload_entry(entry)
|
|
|
|
|
|
class BinarySensorEntityDescription(EntityDescription, frozen_or_thawed=True):
|
|
"""A class that describes binary sensor entities."""
|
|
|
|
device_class: BinarySensorDeviceClass | None = None
|
|
|
|
|
|
CACHED_PROPERTIES_WITH_ATTR_ = {
|
|
"device_class",
|
|
"is_on",
|
|
}
|
|
|
|
|
|
class BinarySensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|
"""Represent a binary sensor."""
|
|
|
|
entity_description: BinarySensorEntityDescription
|
|
_attr_device_class: BinarySensorDeviceClass | None
|
|
_attr_is_on: bool | None = None
|
|
_attr_state: None = None
|
|
|
|
async def async_internal_added_to_hass(self) -> None:
|
|
"""Call when the binary sensor entity is added to hass."""
|
|
await super().async_internal_added_to_hass()
|
|
if self.entity_category == EntityCategory.CONFIG:
|
|
raise HomeAssistantError(
|
|
f"Entity {self.entity_id} cannot be added as the entity category is set to config"
|
|
)
|
|
|
|
def _default_to_device_class_name(self) -> bool:
|
|
"""Return True if an unnamed entity should be named by its device class.
|
|
|
|
For binary sensors this is True if the entity has a device class.
|
|
"""
|
|
return self.device_class is not None
|
|
|
|
@cached_property
|
|
def device_class(self) -> BinarySensorDeviceClass | 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
|
|
|
|
@cached_property
|
|
def is_on(self) -> bool | None:
|
|
"""Return true if the binary sensor is on."""
|
|
return self._attr_is_on
|
|
|
|
@final
|
|
@property
|
|
def state(self) -> Literal["on", "off"] | None:
|
|
"""Return the state of the binary sensor."""
|
|
if (is_on := self.is_on) is None:
|
|
return None
|
|
return STATE_ON if is_on else STATE_OFF
|
|
|
|
|
|
# These can be removed if no deprecated constant are in this module anymore
|
|
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
|
|
__dir__ = partial(
|
|
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
|
|
)
|
|
__all__ = all_with_deprecated_constants(globals())
|