Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 103 additions & 84 deletions homeassistant/components/binary_sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType, StateType
from homeassistant.util.enum import StrEnum

_LOGGER = logging.getLogger(__name__)

Expand All @@ -26,118 +27,124 @@

ENTITY_ID_FORMAT = DOMAIN + ".{}"

# On means low, Off means normal
DEVICE_CLASS_BATTERY = "battery"

# On means charging, Off means not charging
DEVICE_CLASS_BATTERY_CHARGING = "battery_charging"
class BinarySensorDeviceClass(StrEnum):
"""Device class for binary sensors."""

# On means cold, Off means normal
DEVICE_CLASS_COLD = "cold"
# On means low, Off means normal
BATTERY = "battery"

# On means connected, Off means disconnected
DEVICE_CLASS_CONNECTIVITY = "connectivity"
# On means charging, Off means not charging
BATTERY_CHARGING = "battery_charging"

# On means open, Off means closed
DEVICE_CLASS_DOOR = "door"
# On means cold, Off means normal
COLD = "cold"

# On means open, Off means closed
DEVICE_CLASS_GARAGE_DOOR = "garage_door"
# On means connected, Off means disconnected
CONNECTIVITY = "connectivity"

# On means gas detected, Off means no gas (clear)
DEVICE_CLASS_GAS = "gas"
# On means open, Off means closed
DOOR = "door"

# On means hot, Off means normal
DEVICE_CLASS_HEAT = "heat"
# On means open, Off means closed
GARAGE_DOOR = "garage_door"

# On means light detected, Off means no light
DEVICE_CLASS_LIGHT = "light"
# On means gas detected, Off means no gas (clear)
GAS = "gas"

# On means open (unlocked), Off means closed (locked)
DEVICE_CLASS_LOCK = "lock"
# On means hot, Off means normal
HEAT = "heat"

# On means wet, Off means dry
DEVICE_CLASS_MOISTURE = "moisture"
# On means light detected, Off means no light
LIGHT = "light"

# On means motion detected, Off means no motion (clear)
DEVICE_CLASS_MOTION = "motion"
# On means open (unlocked), Off means closed (locked)
LOCK = "lock"

# On means moving, Off means not moving (stopped)
DEVICE_CLASS_MOVING = "moving"
# On means wet, Off means dry
MOISTURE = "moisture"

# On means occupied, Off means not occupied (clear)
DEVICE_CLASS_OCCUPANCY = "occupancy"
# On means motion detected, Off means no motion (clear)
MOTION = "motion"

# On means open, Off means closed
DEVICE_CLASS_OPENING = "opening"
# On means moving, Off means not moving (stopped)
MOVING = "moving"

# On means plugged in, Off means unplugged
DEVICE_CLASS_PLUG = "plug"
# On means occupied, Off means not occupied (clear)
OCCUPANCY = "occupancy"

# On means power detected, Off means no power
DEVICE_CLASS_POWER = "power"
# On means open, Off means closed
OPENING = "opening"

# On means home, Off means away
DEVICE_CLASS_PRESENCE = "presence"
# On means plugged in, Off means unplugged
PLUG = "plug"

# On means problem detected, Off means no problem (OK)
DEVICE_CLASS_PROBLEM = "problem"
# On means power detected, Off means no power
POWER = "power"

# On means running, Off means not running
DEVICE_CLASS_RUNNING = "running"
# On means home, Off means away
PRESENCE = "presence"

# On means unsafe, Off means safe
DEVICE_CLASS_SAFETY = "safety"
# On means problem detected, Off means no problem (OK)
PROBLEM = "problem"

# On means smoke detected, Off means no smoke (clear)
DEVICE_CLASS_SMOKE = "smoke"
# On means running, Off means not running
RUNNING = "running"

# On means sound detected, Off means no sound (clear)
DEVICE_CLASS_SOUND = "sound"
# On means unsafe, Off means safe
SAFETY = "safety"

# On means tampering detected, Off means no tampering (clear)
DEVICE_CLASS_TAMPER = "tamper"
# On means smoke detected, Off means no smoke (clear)
SMOKE = "smoke"

# On means update available, Off means up-to-date
DEVICE_CLASS_UPDATE = "update"
# On means sound detected, Off means no sound (clear)
SOUND = "sound"

# On means vibration detected, Off means no vibration
DEVICE_CLASS_VIBRATION = "vibration"
# On means tampering detected, Off means no tampering (clear)
TAMPER = "tamper"

# On means open, Off means closed
DEVICE_CLASS_WINDOW = "window"
# On means update available, Off means up-to-date
UPDATE = "update"

DEVICE_CLASSES = [
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_BATTERY_CHARGING,
DEVICE_CLASS_COLD,
DEVICE_CLASS_CONNECTIVITY,
DEVICE_CLASS_DOOR,
DEVICE_CLASS_GARAGE_DOOR,
DEVICE_CLASS_GAS,
DEVICE_CLASS_HEAT,
DEVICE_CLASS_LIGHT,
DEVICE_CLASS_LOCK,
DEVICE_CLASS_MOISTURE,
DEVICE_CLASS_MOTION,
DEVICE_CLASS_MOVING,
DEVICE_CLASS_OCCUPANCY,
DEVICE_CLASS_OPENING,
DEVICE_CLASS_PLUG,
DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESENCE,
DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_RUNNING,
DEVICE_CLASS_SAFETY,
DEVICE_CLASS_SMOKE,
DEVICE_CLASS_SOUND,
DEVICE_CLASS_TAMPER,
DEVICE_CLASS_UPDATE,
DEVICE_CLASS_VIBRATION,
DEVICE_CLASS_WINDOW,
]
# On means vibration detected, Off means no vibration
VIBRATION = "vibration"

DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES))
# 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]
DEVICE_CLASS_BATTERY = BinarySensorDeviceClass.BATTERY.value
DEVICE_CLASS_BATTERY_CHARGING = BinarySensorDeviceClass.BATTERY_CHARGING.value
DEVICE_CLASS_COLD = BinarySensorDeviceClass.COLD.value
DEVICE_CLASS_CONNECTIVITY = BinarySensorDeviceClass.CONNECTIVITY.value
DEVICE_CLASS_DOOR = BinarySensorDeviceClass.DOOR.value
DEVICE_CLASS_GARAGE_DOOR = BinarySensorDeviceClass.GARAGE_DOOR.value
DEVICE_CLASS_GAS = BinarySensorDeviceClass.GAS.value
DEVICE_CLASS_HEAT = BinarySensorDeviceClass.HEAT.value
DEVICE_CLASS_LIGHT = BinarySensorDeviceClass.LIGHT.value
DEVICE_CLASS_LOCK = BinarySensorDeviceClass.LOCK.value
DEVICE_CLASS_MOISTURE = BinarySensorDeviceClass.MOISTURE.value
DEVICE_CLASS_MOTION = BinarySensorDeviceClass.MOTION.value
DEVICE_CLASS_MOVING = BinarySensorDeviceClass.MOVING.value
DEVICE_CLASS_OCCUPANCY = BinarySensorDeviceClass.OCCUPANCY.value
DEVICE_CLASS_OPENING = BinarySensorDeviceClass.OPENING.value
DEVICE_CLASS_PLUG = BinarySensorDeviceClass.PLUG.value
DEVICE_CLASS_POWER = BinarySensorDeviceClass.POWER.value
DEVICE_CLASS_PRESENCE = BinarySensorDeviceClass.PRESENCE.value
DEVICE_CLASS_PROBLEM = BinarySensorDeviceClass.PROBLEM.value
DEVICE_CLASS_RUNNING = BinarySensorDeviceClass.RUNNING.value
DEVICE_CLASS_SAFETY = BinarySensorDeviceClass.SAFETY.value
DEVICE_CLASS_SMOKE = BinarySensorDeviceClass.SMOKE.value
DEVICE_CLASS_SOUND = BinarySensorDeviceClass.SOUND.value
DEVICE_CLASS_TAMPER = BinarySensorDeviceClass.TAMPER.value
DEVICE_CLASS_UPDATE = BinarySensorDeviceClass.UPDATE.value
DEVICE_CLASS_VIBRATION = BinarySensorDeviceClass.VIBRATION.value
DEVICE_CLASS_WINDOW = BinarySensorDeviceClass.WINDOW.value


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
Expand Down Expand Up @@ -166,14 +173,26 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
class BinarySensorEntityDescription(EntityDescription):
"""A class that describes binary sensor entities."""

device_class: BinarySensorDeviceClass | str | None = None


class BinarySensorEntity(Entity):
"""Represent a binary sensor."""

entity_description: BinarySensorEntityDescription
_attr_device_class: BinarySensorDeviceClass | str | None
_attr_is_on: bool | None = None
_attr_state: None = None

@property
def device_class(self) -> BinarySensorDeviceClass | str | 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 is_on(self) -> bool | None:
"""Return true if the binary sensor is on."""
Expand Down
20 changes: 14 additions & 6 deletions homeassistant/components/demo/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Demo platform that has two fake binary sensors."""
from homeassistant.components.binary_sensor import (
DEVICE_CLASS_MOISTURE,
DEVICE_CLASS_MOTION,
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.helpers.entity import DeviceInfo
Expand All @@ -14,10 +13,13 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
async_add_entities(
[
DemoBinarySensor(
"binary_1", "Basement Floor Wet", False, DEVICE_CLASS_MOISTURE
"binary_1",
"Basement Floor Wet",
False,
BinarySensorDeviceClass.MOISTURE,
),
DemoBinarySensor(
"binary_2", "Movement Backyard", True, DEVICE_CLASS_MOTION
"binary_2", "Movement Backyard", True, BinarySensorDeviceClass.MOTION
),
]
)
Expand All @@ -31,7 +33,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class DemoBinarySensor(BinarySensorEntity):
"""representation of a Demo binary sensor."""

def __init__(self, unique_id, name, state, device_class):
def __init__(
self,
unique_id: str,
name: str,
state: bool,
device_class: BinarySensorDeviceClass,
) -> None:
"""Initialize the demo sensor."""
self._unique_id = unique_id
self._name = name
Expand All @@ -55,7 +63,7 @@ def unique_id(self):
return self._unique_id

@property
def device_class(self):
def device_class(self) -> BinarySensorDeviceClass:
"""Return the class of this sensor."""
return self._sensor_type

Expand Down
18 changes: 9 additions & 9 deletions homeassistant/components/sia/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
from pysiaalarm import SIAEvent

from homeassistant.components.binary_sensor import (
DEVICE_CLASS_MOISTURE,
DEVICE_CLASS_POWER,
DEVICE_CLASS_SMOKE,
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
Expand Down Expand Up @@ -81,11 +79,11 @@ def __init__(
entry: ConfigEntry,
account_data: dict[str, Any],
zone: int,
device_class: str,
device_class: BinarySensorDeviceClass,
) -> None:
"""Initialize a base binary sensor."""
super().__init__(entry, account_data, zone, device_class)

super().__init__(entry, account_data, zone)
self._attr_device_class = device_class
self._attr_unique_id = SIA_UNIQUE_ID_FORMAT_BINARY.format(
self._entry.entry_id, self._account, self._zone, self._attr_device_class
)
Expand All @@ -111,7 +109,7 @@ def __init__(
zone: int,
) -> None:
"""Initialize a Moisture binary sensor."""
super().__init__(entry, account_data, zone, DEVICE_CLASS_MOISTURE)
super().__init__(entry, account_data, zone, BinarySensorDeviceClass.MOISTURE)
self._attr_entity_registry_enabled_default = False

def update_state(self, sia_event: SIAEvent) -> None:
Expand All @@ -132,7 +130,7 @@ def __init__(
zone: int,
) -> None:
"""Initialize a Smoke binary sensor."""
super().__init__(entry, account_data, zone, DEVICE_CLASS_SMOKE)
super().__init__(entry, account_data, zone, BinarySensorDeviceClass.SMOKE)
self._attr_entity_registry_enabled_default = False

def update_state(self, sia_event: SIAEvent) -> None:
Expand All @@ -152,7 +150,9 @@ def __init__(
account_data: dict[str, Any],
) -> None:
"""Initialize a Power binary sensor."""
super().__init__(entry, account_data, SIA_HUB_ZONE, DEVICE_CLASS_POWER)
super().__init__(
entry, account_data, SIA_HUB_ZONE, BinarySensorDeviceClass.POWER
)
self._attr_entity_registry_enabled_default = True

def update_state(self, sia_event: SIAEvent) -> None:
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/sia/sia_entity_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ def __init__(
entry: ConfigEntry,
account_data: dict[str, Any],
zone: int,
device_class: str,
device_class: str | None = None,
) -> None:
"""Create SIABaseEntity object."""
self._entry: ConfigEntry = entry
self._account_data: dict[str, Any] = account_data
self._zone: int = zone
self._attr_device_class: str = device_class
self._attr_device_class = device_class

self._port: int = self._entry.data[CONF_PORT]
self._account: str = self._account_data[CONF_ACCOUNT]
Expand Down
8 changes: 7 additions & 1 deletion homeassistant/components/upcloud/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

import voluptuous as vol

from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity
from homeassistant.components.binary_sensor import (
PLATFORM_SCHEMA,
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_USERNAME
from homeassistant.core import HomeAssistant
Expand All @@ -29,3 +33,5 @@ async def async_setup_entry(

class UpCloudBinarySensor(UpCloudServerEntity, BinarySensorEntity):
"""Representation of an UpCloud server sensor."""

_attr_device_class = BinarySensorDeviceClass.POWER
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know this integration, is this correct?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously it used a hard coded string in its parent class to "power" for all iets platforms...

So this makes it specific on this platform to fix that (and make mypy happy as it now has catches this bug)