mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 15:47:12 +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
|
from .coordinator import RehlkoConfigEntry, RehlkoRuntimeData, RehlkoUpdateCoordinator
|
||||||
|
|
||||||
PLATFORMS = [Platform.SENSOR]
|
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_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,
|
device_data: dict,
|
||||||
description: EntityDescription,
|
description: EntityDescription,
|
||||||
document_key: str | None = None,
|
document_key: str | None = None,
|
||||||
|
connectivity_key: str | None = DEVICE_DATA_IS_CONNECTED,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
@ -62,6 +63,7 @@ class RehlkoEntity(CoordinatorEntity[RehlkoUpdateCoordinator]):
|
|||||||
connections=_get_device_connections(device_data[DEVICE_DATA_MAC_ADDRESS]),
|
connections=_get_device_connections(device_data[DEVICE_DATA_MAC_ADDRESS]),
|
||||||
)
|
)
|
||||||
self._document_key = document_key
|
self._document_key = document_key
|
||||||
|
self._connectivity_key = connectivity_key
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _device_data(self) -> dict[str, Any]:
|
def _device_data(self) -> dict[str, Any]:
|
||||||
@ -80,4 +82,6 @@ class RehlkoEntity(CoordinatorEntity[RehlkoUpdateCoordinator]):
|
|||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Return if entity is available."""
|
"""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": {
|
"entity": {
|
||||||
|
"binary_sensor": {
|
||||||
|
"auto_run": {
|
||||||
|
"name": "Auto run"
|
||||||
|
},
|
||||||
|
"oil_pressure": {
|
||||||
|
"name": "Oil pressure"
|
||||||
|
}
|
||||||
|
},
|
||||||
"sensor": {
|
"sensor": {
|
||||||
"engine_speed": {
|
"engine_speed": {
|
||||||
"name": "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