diff --git a/homeassistant/components/tedee/__init__.py b/homeassistant/components/tedee/__init__.py index 7940d594d71..1eb6b7a0333 100644 --- a/homeassistant/components/tedee/__init__.py +++ b/homeassistant/components/tedee/__init__.py @@ -9,6 +9,7 @@ from .const import DOMAIN from .coordinator import TedeeApiCoordinator PLATFORMS = [ + Platform.BINARY_SENSOR, Platform.LOCK, Platform.SENSOR, ] diff --git a/homeassistant/components/tedee/binary_sensor.py b/homeassistant/components/tedee/binary_sensor.py new file mode 100644 index 00000000000..9bb2cd0410c --- /dev/null +++ b/homeassistant/components/tedee/binary_sensor.py @@ -0,0 +1,78 @@ +"""Tedee sensor entities.""" +from collections.abc import Callable +from dataclasses import dataclass + +from pytedee_async import TedeeLock +from pytedee_async.lock import TedeeLockState + +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, + BinarySensorEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import EntityCategory +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .entity import TedeeDescriptionEntity + + +@dataclass(frozen=True, kw_only=True) +class TedeeBinarySensorEntityDescription( + BinarySensorEntityDescription, +): + """Describes Tedee binary sensor entity.""" + + is_on_fn: Callable[[TedeeLock], bool | None] + + +ENTITIES: tuple[TedeeBinarySensorEntityDescription, ...] = ( + TedeeBinarySensorEntityDescription( + key="charging", + device_class=BinarySensorDeviceClass.BATTERY_CHARGING, + is_on_fn=lambda lock: lock.is_charging, + entity_category=EntityCategory.DIAGNOSTIC, + ), + TedeeBinarySensorEntityDescription( + key="semi_locked", + translation_key="semi_locked", + is_on_fn=lambda lock: lock.state == TedeeLockState.HALF_OPEN, + entity_category=EntityCategory.DIAGNOSTIC, + ), + TedeeBinarySensorEntityDescription( + key="pullspring_enabled", + translation_key="pullspring_enabled", + is_on_fn=lambda lock: lock.is_enabled_pullspring, + entity_category=EntityCategory.DIAGNOSTIC, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Tedee sensor entity.""" + coordinator = hass.data[DOMAIN][entry.entry_id] + + for entity_description in ENTITIES: + async_add_entities( + [ + TedeeBinarySensorEntity(lock, coordinator, entity_description) + for lock in coordinator.data.values() + ] + ) + + +class TedeeBinarySensorEntity(TedeeDescriptionEntity, BinarySensorEntity): + """Tedee sensor entity.""" + + entity_description: TedeeBinarySensorEntityDescription + + @property + def is_on(self) -> bool | None: + """Return true if the binary sensor is on.""" + return self.entity_description.is_on_fn(self._lock) diff --git a/homeassistant/components/tedee/strings.json b/homeassistant/components/tedee/strings.json index 7a1df7d3875..db6a450c1f3 100644 --- a/homeassistant/components/tedee/strings.json +++ b/homeassistant/components/tedee/strings.json @@ -23,6 +23,14 @@ } }, "entity": { + "binary_sensor": { + "pullspring_enabled": { + "name": "Pullspring enabled" + }, + "semi_locked": { + "name": "Semi locked" + } + }, "sensor": { "pullspring_duration": { "name": "Pullspring duration" diff --git a/tests/components/tedee/snapshots/test_binary_sensor.ambr b/tests/components/tedee/snapshots/test_binary_sensor.ambr new file mode 100644 index 00000000000..16be8aafd0e --- /dev/null +++ b/tests/components/tedee/snapshots/test_binary_sensor.ambr @@ -0,0 +1,131 @@ +# serializer version: 1 +# name: test_binary_sensors[entry-charging] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.lock_1a2b_charging', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Charging', + 'platform': 'tedee', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '12345-charging', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensors[entry-pullspring_enabled] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.lock_1a2b_pullspring_enabled', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Pullspring enabled', + 'platform': 'tedee', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'pullspring_enabled', + 'unique_id': '12345-pullspring_enabled', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensors[entry-semi_locked] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.lock_1a2b_semi_locked', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Semi locked', + 'platform': 'tedee', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'semi_locked', + 'unique_id': '12345-semi_locked', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensors[state-charging] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'battery_charging', + 'friendly_name': 'Lock-1A2B Charging', + }), + 'context': , + 'entity_id': 'binary_sensor.lock_1a2b_charging', + 'last_changed': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_binary_sensors[state-pullspring_enabled] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Lock-1A2B Pullspring enabled', + }), + 'context': , + 'entity_id': 'binary_sensor.lock_1a2b_pullspring_enabled', + 'last_changed': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_binary_sensors[state-semi_locked] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Lock-1A2B Semi locked', + }), + 'context': , + 'entity_id': 'binary_sensor.lock_1a2b_semi_locked', + 'last_changed': , + 'last_updated': , + 'state': 'off', + }) +# --- diff --git a/tests/components/tedee/test_binary_sensor.py b/tests/components/tedee/test_binary_sensor.py new file mode 100644 index 00000000000..bdb66c9c0a9 --- /dev/null +++ b/tests/components/tedee/test_binary_sensor.py @@ -0,0 +1,34 @@ +"""Tests for the Tedee Binary Sensors.""" + +from unittest.mock import MagicMock + +import pytest +from syrupy import SnapshotAssertion + +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +pytestmark = pytest.mark.usefixtures("init_integration") + +BINARY_SENSORS = ( + "charging", + "semi_locked", + "pullspring_enabled", +) + + +async def test_binary_sensors( + hass: HomeAssistant, + mock_tedee: MagicMock, + entity_registry: er.EntityRegistry, + snapshot: SnapshotAssertion, +) -> None: + """Test tedee battery charging sensor.""" + for key in BINARY_SENSORS: + state = hass.states.get(f"binary_sensor.lock_1a2b_{key}") + assert state + assert state == snapshot(name=f"state-{key}") + + entry = entity_registry.async_get(state.entity_id) + assert entry + assert entry == snapshot(name=f"entry-{key}")