Add binary states for Weheat indoor unit (#133811)

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
pull/133819/head
Barry vd. Heuvel 2024-12-22 19:07:01 +01:00 committed by GitHub
parent 0e9965150e
commit d994884726
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 380 additions and 1 deletions

View File

@ -17,7 +17,7 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
from .const import API_URL, LOGGER
from .coordinator import WeheatDataUpdateCoordinator
PLATFORMS: list[Platform] = [Platform.SENSOR]
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
type WeheatConfigEntry = ConfigEntry[list[WeheatDataUpdateCoordinator]]

View File

@ -0,0 +1,100 @@
"""Binary sensor platform for Weheat integration."""
from collections.abc import Callable
from dataclasses import dataclass
from weheat.abstractions.heat_pump import HeatPump
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from . import WeheatConfigEntry
from .coordinator import WeheatDataUpdateCoordinator
from .entity import WeheatEntity
@dataclass(frozen=True, kw_only=True)
class WeHeatBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Describes Weheat binary sensor entity."""
value_fn: Callable[[HeatPump], StateType]
BINARY_SENSORS = [
WeHeatBinarySensorEntityDescription(
translation_key="indoor_unit_water_pump_state",
key="indoor_unit_water_pump_state",
device_class=BinarySensorDeviceClass.RUNNING,
value_fn=lambda status: status.indoor_unit_water_pump_state,
),
WeHeatBinarySensorEntityDescription(
translation_key="indoor_unit_auxiliary_pump_state",
key="indoor_unit_auxiliary_pump_state",
device_class=BinarySensorDeviceClass.RUNNING,
value_fn=lambda status: status.indoor_unit_auxiliary_pump_state,
),
WeHeatBinarySensorEntityDescription(
translation_key="indoor_unit_dhw_valve_or_pump_state",
key="indoor_unit_dhw_valve_or_pump_state",
device_class=BinarySensorDeviceClass.RUNNING,
value_fn=lambda status: status.indoor_unit_dhw_valve_or_pump_state,
),
WeHeatBinarySensorEntityDescription(
translation_key="indoor_unit_gas_boiler_state",
key="indoor_unit_gas_boiler_state",
value_fn=lambda status: status.indoor_unit_gas_boiler_state,
),
WeHeatBinarySensorEntityDescription(
translation_key="indoor_unit_electric_heater_state",
key="indoor_unit_electric_heater_state",
device_class=BinarySensorDeviceClass.RUNNING,
value_fn=lambda status: status.indoor_unit_electric_heater_state,
),
]
async def async_setup_entry(
hass: HomeAssistant,
entry: WeheatConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the sensors for weheat heat pump."""
entities = [
WeheatHeatPumpBinarySensor(coordinator, entity_description)
for entity_description in BINARY_SENSORS
for coordinator in entry.runtime_data
if entity_description.value_fn(coordinator.data) is not None
]
async_add_entities(entities)
class WeheatHeatPumpBinarySensor(WeheatEntity, BinarySensorEntity):
"""Defines a Weheat heat pump binary sensor."""
coordinator: WeheatDataUpdateCoordinator
entity_description: WeHeatBinarySensorEntityDescription
def __init__(
self,
coordinator: WeheatDataUpdateCoordinator,
entity_description: WeHeatBinarySensorEntityDescription,
) -> None:
"""Pass coordinator to CoordinatorEntity."""
super().__init__(coordinator)
self.entity_description = entity_description
self._attr_unique_id = f"{coordinator.heatpump_id}_{entity_description.key}"
@property
def is_on(self) -> bool | None:
"""Return True if the binary sensor is on."""
value = self.entity_description.value_fn(self.coordinator.data)
return bool(value) if value is not None else None

View File

@ -1,5 +1,22 @@
{
"entity": {
"binary_sensor": {
"indoor_unit_water_pump_state": {
"default": "mdi:pump"
},
"indoor_unit_auxiliary_pump_state": {
"default": "mdi:pump"
},
"indoor_unit_dhw_valve_or_pump_state": {
"default": "mdi:pump"
},
"indoor_unit_gas_boiler_state": {
"default": "mdi:toggle-switch"
},
"indoor_unit_electric_heater_state": {
"default": "mdi:heating-coil"
}
},
"sensor": {
"power_output": {
"default": "mdi:heat-wave"

View File

@ -32,6 +32,23 @@
}
},
"entity": {
"binary_sensor": {
"indoor_unit_water_pump_state": {
"name": "Indoor unit water pump"
},
"indoor_unit_auxiliary_pump_state": {
"name": "Indoor unit auxilary water pump"
},
"indoor_unit_dhw_valve_or_pump_state": {
"name": "Indoor unit DHW valve or water pump"
},
"indoor_unit_gas_boiler_state": {
"name": "Indoor unit gas boiler heating allowed"
},
"indoor_unit_electric_heater_state": {
"name": "Indoor unit electric heater"
}
},
"sensor": {
"power_output": {
"name": "Output power"

View File

@ -124,6 +124,11 @@ def mock_weheat_heat_pump_instance() -> MagicMock:
mock_heat_pump_instance.energy_output = 56789
mock_heat_pump_instance.compressor_rpm = 4500
mock_heat_pump_instance.compressor_percentage = 100
mock_heat_pump_instance.indoor_unit_water_pump_state = False
mock_heat_pump_instance.indoor_unit_auxiliary_pump_state = False
mock_heat_pump_instance.indoor_unit_dhw_valve_or_pump_state = None
mock_heat_pump_instance.indoor_unit_gas_boiler_state = False
mock_heat_pump_instance.indoor_unit_electric_heater_state = True
return mock_heat_pump_instance

View File

@ -0,0 +1,188 @@
# serializer version: 1
# name: test_binary_entities[binary_sensor.test_model_indoor_unit_auxilary_water_pump-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.test_model_indoor_unit_auxilary_water_pump',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.RUNNING: 'running'>,
'original_icon': None,
'original_name': 'Indoor unit auxilary water pump',
'platform': 'weheat',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'indoor_unit_auxiliary_pump_state',
'unique_id': '0000-1111-2222-3333_indoor_unit_auxiliary_pump_state',
'unit_of_measurement': None,
})
# ---
# name: test_binary_entities[binary_sensor.test_model_indoor_unit_auxilary_water_pump-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'Test Model Indoor unit auxilary water pump',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.test_model_indoor_unit_auxilary_water_pump',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_binary_entities[binary_sensor.test_model_indoor_unit_electric_heater-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.test_model_indoor_unit_electric_heater',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.RUNNING: 'running'>,
'original_icon': None,
'original_name': 'Indoor unit electric heater',
'platform': 'weheat',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'indoor_unit_electric_heater_state',
'unique_id': '0000-1111-2222-3333_indoor_unit_electric_heater_state',
'unit_of_measurement': None,
})
# ---
# name: test_binary_entities[binary_sensor.test_model_indoor_unit_electric_heater-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'Test Model Indoor unit electric heater',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.test_model_indoor_unit_electric_heater',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_binary_entities[binary_sensor.test_model_indoor_unit_gas_boiler_heating_allowed-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.test_model_indoor_unit_gas_boiler_heating_allowed',
'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': 'Indoor unit gas boiler heating allowed',
'platform': 'weheat',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'indoor_unit_gas_boiler_state',
'unique_id': '0000-1111-2222-3333_indoor_unit_gas_boiler_state',
'unit_of_measurement': None,
})
# ---
# name: test_binary_entities[binary_sensor.test_model_indoor_unit_gas_boiler_heating_allowed-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Test Model Indoor unit gas boiler heating allowed',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.test_model_indoor_unit_gas_boiler_heating_allowed',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_binary_entities[binary_sensor.test_model_indoor_unit_water_pump-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.test_model_indoor_unit_water_pump',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.RUNNING: 'running'>,
'original_icon': None,
'original_name': 'Indoor unit water pump',
'platform': 'weheat',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'indoor_unit_water_pump_state',
'unique_id': '0000-1111-2222-3333_indoor_unit_water_pump_state',
'unit_of_measurement': None,
})
# ---
# name: test_binary_entities[binary_sensor.test_model_indoor_unit_water_pump-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'running',
'friendly_name': 'Test Model Indoor unit water pump',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.test_model_indoor_unit_water_pump',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@ -0,0 +1,52 @@
"""Tests for the weheat sensor platform."""
from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy import SnapshotAssertion
from weheat.abstractions.discovery import HeatPumpDiscovery
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import MockConfigEntry, snapshot_platform
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_binary_entities(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
mock_weheat_discover: AsyncMock,
mock_weheat_heat_pump: AsyncMock,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test all entities."""
with patch("homeassistant.components.weheat.PLATFORMS", [Platform.BINARY_SENSOR]):
await setup_integration(hass, mock_config_entry)
await hass.async_block_till_done()
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
async def test_create_binary_entities(
hass: HomeAssistant,
mock_weheat_discover: AsyncMock,
mock_weheat_heat_pump: AsyncMock,
mock_heat_pump_info: HeatPumpDiscovery.HeatPumpInfo,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test creating entities."""
mock_weheat_discover.return_value = [mock_heat_pump_info]
with patch("homeassistant.components.weheat.PLATFORMS", [Platform.BINARY_SENSOR]):
await setup_integration(hass, mock_config_entry)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 4