diff --git a/.coveragerc b/.coveragerc index d160efb776c..6cf3f66d8af 100644 --- a/.coveragerc +++ b/.coveragerc @@ -407,6 +407,7 @@ omit = homeassistant/components/garages_amsterdam/binary_sensor.py homeassistant/components/garages_amsterdam/sensor.py homeassistant/components/gardena_bluetooth/__init__.py + homeassistant/components/gardena_bluetooth/binary_sensor.py homeassistant/components/gardena_bluetooth/const.py homeassistant/components/gardena_bluetooth/coordinator.py homeassistant/components/gardena_bluetooth/number.py diff --git a/homeassistant/components/gardena_bluetooth/__init__.py b/homeassistant/components/gardena_bluetooth/__init__.py index 98869019d29..c779d30b0fc 100644 --- a/homeassistant/components/gardena_bluetooth/__init__.py +++ b/homeassistant/components/gardena_bluetooth/__init__.py @@ -20,7 +20,12 @@ import homeassistant.util.dt as dt_util from .const import DOMAIN from .coordinator import Coordinator, DeviceUnavailable -PLATFORMS: list[Platform] = [Platform.NUMBER, Platform.SENSOR, Platform.SWITCH] +PLATFORMS: list[Platform] = [ + Platform.BINARY_SENSOR, + Platform.NUMBER, + Platform.SENSOR, + Platform.SWITCH, +] LOGGER = logging.getLogger(__name__) TIMEOUT = 20.0 DISCONNECT_DELAY = 5 diff --git a/homeassistant/components/gardena_bluetooth/binary_sensor.py b/homeassistant/components/gardena_bluetooth/binary_sensor.py new file mode 100644 index 00000000000..0285f7bdf82 --- /dev/null +++ b/homeassistant/components/gardena_bluetooth/binary_sensor.py @@ -0,0 +1,64 @@ +"""Support for binary_sensor entities.""" +from __future__ import annotations + +from dataclasses import dataclass, field + +from gardena_bluetooth.const import Valve +from gardena_bluetooth.parse import CharacteristicBool + +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 .coordinator import Coordinator, GardenaBluetoothDescriptorEntity + + +@dataclass +class GardenaBluetoothBinarySensorEntityDescription(BinarySensorEntityDescription): + """Description of entity.""" + + char: CharacteristicBool = field(default_factory=lambda: CharacteristicBool("")) + + +DESCRIPTIONS = ( + GardenaBluetoothBinarySensorEntityDescription( + key=Valve.connected_state.uuid, + translation_key="valve_connected_state", + device_class=BinarySensorDeviceClass.CONNECTIVITY, + entity_category=EntityCategory.DIAGNOSTIC, + char=Valve.connected_state, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up binary sensor based on a config entry.""" + coordinator: Coordinator = hass.data[DOMAIN][entry.entry_id] + entities = [ + GardenaBluetoothBinarySensor(coordinator, description) + for description in DESCRIPTIONS + if description.key in coordinator.characteristics + ] + async_add_entities(entities) + + +class GardenaBluetoothBinarySensor( + GardenaBluetoothDescriptorEntity, BinarySensorEntity +): + """Representation of a binary sensor.""" + + entity_description: GardenaBluetoothBinarySensorEntityDescription + + def _handle_coordinator_update(self) -> None: + char = self.entity_description.char + self._attr_is_on = self.coordinator.get_cached(char) + super()._handle_coordinator_update() diff --git a/homeassistant/components/gardena_bluetooth/strings.json b/homeassistant/components/gardena_bluetooth/strings.json index 3548412e04f..5a3f77eafa4 100644 --- a/homeassistant/components/gardena_bluetooth/strings.json +++ b/homeassistant/components/gardena_bluetooth/strings.json @@ -19,6 +19,11 @@ } }, "entity": { + "binary_sensor": { + "valve_connected_state": { + "name": "Valve connection" + } + }, "number": { "remaining_open_time": { "name": "Remaining open time" diff --git a/tests/components/gardena_bluetooth/snapshots/test_binary_sensor.ambr b/tests/components/gardena_bluetooth/snapshots/test_binary_sensor.ambr new file mode 100644 index 00000000000..8a2600dcbb1 --- /dev/null +++ b/tests/components/gardena_bluetooth/snapshots/test_binary_sensor.ambr @@ -0,0 +1,27 @@ +# serializer version: 1 +# name: test_setup[98bd0f12-0b0e-421a-84e5-ddbf75dc6de4-raw0-binary_sensor.mock_title_valve_connection] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'connectivity', + 'friendly_name': 'Mock Title Valve connection', + }), + 'context': , + 'entity_id': 'binary_sensor.mock_title_valve_connection', + 'last_changed': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_setup[98bd0f12-0b0e-421a-84e5-ddbf75dc6de4-raw0-binary_sensor.mock_title_valve_connection].1 + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'connectivity', + 'friendly_name': 'Mock Title Valve connection', + }), + 'context': , + 'entity_id': 'binary_sensor.mock_title_valve_connection', + 'last_changed': , + 'last_updated': , + 'state': 'off', + }) +# --- diff --git a/tests/components/gardena_bluetooth/test_binary_sensor.py b/tests/components/gardena_bluetooth/test_binary_sensor.py new file mode 100644 index 00000000000..cda24f871e8 --- /dev/null +++ b/tests/components/gardena_bluetooth/test_binary_sensor.py @@ -0,0 +1,44 @@ +"""Test Gardena Bluetooth binary sensor.""" + + +from gardena_bluetooth.const import Valve +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant + +from . import setup_entry + +from tests.common import MockConfigEntry + + +@pytest.mark.parametrize( + ("uuid", "raw", "entity_id"), + [ + ( + Valve.connected_state.uuid, + [b"\x01", b"\x00"], + "binary_sensor.mock_title_valve_connection", + ), + ], +) +async def test_setup( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_entry: MockConfigEntry, + mock_read_char_raw: dict[str, bytes], + uuid: str, + raw: list[bytes], + entity_id: str, +) -> None: + """Test setup creates expected entities.""" + + mock_read_char_raw[uuid] = raw[0] + coordinator = await setup_entry(hass, mock_entry, [Platform.BINARY_SENSOR]) + assert hass.states.get(entity_id) == snapshot + + for char_raw in raw[1:]: + mock_read_char_raw[uuid] = char_raw + await coordinator.async_refresh() + assert hass.states.get(entity_id) == snapshot