mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 23:27:37 +00:00
Add binary_sensor platform to Rehlko (#145391)
* feat: add binary_sensor platform to Rehlko * feat: add binary sensor platform * fix: simplify availability logic * fix: simplify availability logic * fix: simplify * fix: rename sensor * fix: rename sensor * fix: rename sensor * fix: remove unneeded type * fix: rename sensor to 'Auto run' * fix: use device_class name
This commit is contained in:
parent
613aa9b2cf
commit
1db5c514e6
@ -22,7 +22,7 @@ from .const import (
|
||||
)
|
||||
from .coordinator import RehlkoConfigEntry, RehlkoRuntimeData, RehlkoUpdateCoordinator
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
108
homeassistant/components/rehlko/binary_sensor.py
Normal file
108
homeassistant/components/rehlko/binary_sensor.py
Normal file
@ -0,0 +1,108 @@
|
||||
"""Binary sensor platform for Rehlko integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
DEVICE_DATA_DEVICES,
|
||||
DEVICE_DATA_ID,
|
||||
DEVICE_DATA_IS_CONNECTED,
|
||||
GENERATOR_DATA_DEVICE,
|
||||
)
|
||||
from .coordinator import RehlkoConfigEntry
|
||||
from .entity import RehlkoEntity
|
||||
|
||||
# Coordinator is used to centralize the data updates
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class RehlkoBinarySensorEntityDescription(BinarySensorEntityDescription):
|
||||
"""Class describing Rehlko binary sensor entities."""
|
||||
|
||||
on_value: str | bool = True
|
||||
off_value: str | bool = False
|
||||
document_key: str | None = None
|
||||
connectivity_key: str | None = DEVICE_DATA_IS_CONNECTED
|
||||
|
||||
|
||||
BINARY_SENSORS: tuple[RehlkoBinarySensorEntityDescription, ...] = (
|
||||
RehlkoBinarySensorEntityDescription(
|
||||
key=DEVICE_DATA_IS_CONNECTED,
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
document_key=GENERATOR_DATA_DEVICE,
|
||||
# Entity is available when the device is disconnected
|
||||
connectivity_key=None,
|
||||
),
|
||||
RehlkoBinarySensorEntityDescription(
|
||||
key="switchState",
|
||||
translation_key="auto_run",
|
||||
on_value="Auto",
|
||||
off_value="Off",
|
||||
),
|
||||
RehlkoBinarySensorEntityDescription(
|
||||
key="engineOilPressureOk",
|
||||
translation_key="oil_pressure",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
on_value=False,
|
||||
off_value=True,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: RehlkoConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the binary sensor platform."""
|
||||
homes = config_entry.runtime_data.homes
|
||||
coordinators = config_entry.runtime_data.coordinators
|
||||
async_add_entities(
|
||||
RehlkoBinarySensorEntity(
|
||||
coordinators[device_data[DEVICE_DATA_ID]],
|
||||
device_data[DEVICE_DATA_ID],
|
||||
device_data,
|
||||
sensor_description,
|
||||
document_key=sensor_description.document_key,
|
||||
connectivity_key=sensor_description.connectivity_key,
|
||||
)
|
||||
for home_data in homes
|
||||
for device_data in home_data[DEVICE_DATA_DEVICES]
|
||||
for sensor_description in BINARY_SENSORS
|
||||
)
|
||||
|
||||
|
||||
class RehlkoBinarySensorEntity(RehlkoEntity, BinarySensorEntity):
|
||||
"""Representation of a Binary Sensor."""
|
||||
|
||||
entity_description: RehlkoBinarySensorEntityDescription
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool | None:
|
||||
"""Return the state of the binary sensor."""
|
||||
if self._rehlko_value == self.entity_description.on_value:
|
||||
return True
|
||||
if self._rehlko_value == self.entity_description.off_value:
|
||||
return False
|
||||
_LOGGER.warning(
|
||||
"Unexpected value for %s: %s",
|
||||
self.entity_description.key,
|
||||
self._rehlko_value,
|
||||
)
|
||||
return None
|
@ -44,6 +44,7 @@ class RehlkoEntity(CoordinatorEntity[RehlkoUpdateCoordinator]):
|
||||
device_data: dict,
|
||||
description: EntityDescription,
|
||||
document_key: str | None = None,
|
||||
connectivity_key: str | None = DEVICE_DATA_IS_CONNECTED,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator)
|
||||
@ -62,6 +63,7 @@ class RehlkoEntity(CoordinatorEntity[RehlkoUpdateCoordinator]):
|
||||
connections=_get_device_connections(device_data[DEVICE_DATA_MAC_ADDRESS]),
|
||||
)
|
||||
self._document_key = document_key
|
||||
self._connectivity_key = connectivity_key
|
||||
|
||||
@property
|
||||
def _device_data(self) -> dict[str, Any]:
|
||||
@ -80,4 +82,6 @@ class RehlkoEntity(CoordinatorEntity[RehlkoUpdateCoordinator]):
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if entity is available."""
|
||||
return super().available and self._device_data[DEVICE_DATA_IS_CONNECTED]
|
||||
return super().available and (
|
||||
not self._connectivity_key or self._device_data[self._connectivity_key]
|
||||
)
|
||||
|
@ -31,6 +31,14 @@
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"auto_run": {
|
||||
"name": "Auto run"
|
||||
},
|
||||
"oil_pressure": {
|
||||
"name": "Oil pressure"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"engine_speed": {
|
||||
"name": "Engine speed"
|
||||
|
144
tests/components/rehlko/snapshots/test_binary_sensor.ambr
Normal file
144
tests/components/rehlko/snapshots/test_binary_sensor.ambr
Normal file
@ -0,0 +1,144 @@
|
||||
# serializer version: 1
|
||||
# name: test_sensors[binary_sensor.generator_1_auto_run-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'binary_sensor.generator_1_auto_run',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Auto run',
|
||||
'platform': 'rehlko',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'auto_run',
|
||||
'unique_id': 'myemail@email.com_12345_switchState',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.generator_1_auto_run-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Generator 1 Auto run',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.generator_1_auto_run',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.generator_1_connectivity-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.generator_1_connectivity',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.CONNECTIVITY: 'connectivity'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Connectivity',
|
||||
'platform': 'rehlko',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'myemail@email.com_12345_isConnected',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.generator_1_connectivity-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'connectivity',
|
||||
'friendly_name': 'Generator 1 Connectivity',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.generator_1_connectivity',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.generator_1_oil_pressure-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.generator_1_oil_pressure',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.PROBLEM: 'problem'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Oil pressure',
|
||||
'platform': 'rehlko',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'oil_pressure',
|
||||
'unique_id': 'myemail@email.com_12345_engineOilPressureOk',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[binary_sensor.generator_1_oil_pressure-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'problem',
|
||||
'friendly_name': 'Generator 1 Oil pressure',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.generator_1_oil_pressure',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
93
tests/components/rehlko/test_binary_sensor.py
Normal file
93
tests/components/rehlko/test_binary_sensor.py
Normal file
@ -0,0 +1,93 @@
|
||||
"""Tests for the Rehlko binary sensors."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.rehlko.const import GENERATOR_DATA_DEVICE
|
||||
from homeassistant.components.rehlko.coordinator import SCAN_INTERVAL_MINUTES
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNKNOWN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
|
||||
|
||||
@pytest.fixture(name="platform_binary_sensor", autouse=True)
|
||||
async def platform_binary_sensor_fixture():
|
||||
"""Patch Rehlko to only load binary_sensor platform."""
|
||||
with patch("homeassistant.components.rehlko.PLATFORMS", [Platform.BINARY_SENSOR]):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_sensors(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
rehlko_config_entry: MockConfigEntry,
|
||||
load_rehlko_config_entry: None,
|
||||
) -> None:
|
||||
"""Test the Rehlko binary sensors."""
|
||||
await snapshot_platform(
|
||||
hass, entity_registry, snapshot, rehlko_config_entry.entry_id
|
||||
)
|
||||
|
||||
|
||||
async def test_binary_sensor_states(
|
||||
hass: HomeAssistant,
|
||||
generator: dict[str, Any],
|
||||
mock_rehlko: AsyncMock,
|
||||
load_rehlko_config_entry: None,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test the Rehlko binary sensor state logic."""
|
||||
assert generator["engineOilPressureOk"] is True
|
||||
state = hass.states.get("binary_sensor.generator_1_oil_pressure")
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
generator["engineOilPressureOk"] = False
|
||||
freezer.tick(SCAN_INTERVAL_MINUTES)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("binary_sensor.generator_1_oil_pressure")
|
||||
assert state.state == STATE_ON
|
||||
|
||||
generator["engineOilPressureOk"] = "Unknown State"
|
||||
with caplog.at_level(logging.WARNING):
|
||||
caplog.clear()
|
||||
freezer.tick(SCAN_INTERVAL_MINUTES)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("binary_sensor.generator_1_oil_pressure")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert "Unknown State" in caplog.text
|
||||
assert "engineOilPressureOk" in caplog.text
|
||||
|
||||
|
||||
async def test_binary_sensor_connectivity_availability(
|
||||
hass: HomeAssistant,
|
||||
generator: dict[str, Any],
|
||||
mock_rehlko: AsyncMock,
|
||||
load_rehlko_config_entry: None,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test the connectivity entity availability when device is disconnected."""
|
||||
state = hass.states.get("binary_sensor.generator_1_connectivity")
|
||||
assert state.state == STATE_ON
|
||||
|
||||
# Entity should be available when device is disconnected
|
||||
generator[GENERATOR_DATA_DEVICE]["isConnected"] = False
|
||||
freezer.tick(SCAN_INTERVAL_MINUTES)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("binary_sensor.generator_1_connectivity")
|
||||
assert state.state == STATE_OFF
|
Loading…
x
Reference in New Issue
Block a user