mirror of https://github.com/home-assistant/core
194 lines
6.5 KiB
Python
194 lines
6.5 KiB
Python
"""The Nibe Heat Pump sensors."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
|
|
from nibe.coil import Coil
|
|
from nibe.coil_groups import WATER_HEATER_COILGROUPS, WaterHeaterCoilGroup
|
|
from nibe.exceptions import CoilNotFoundException
|
|
|
|
from homeassistant.components.water_heater import (
|
|
STATE_HEAT_PUMP,
|
|
STATE_HIGH_DEMAND,
|
|
WaterHeaterEntity,
|
|
WaterHeaterEntityFeature,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
|
|
from .const import (
|
|
DOMAIN,
|
|
LOGGER,
|
|
VALUES_TEMPORARY_LUX_INACTIVE,
|
|
VALUES_TEMPORARY_LUX_ONE_TIME_INCREASE,
|
|
)
|
|
from .coordinator import CoilCoordinator
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
config_entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up platform."""
|
|
|
|
coordinator: CoilCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
|
|
|
def water_heaters():
|
|
for key, group in WATER_HEATER_COILGROUPS.get(coordinator.series, ()).items():
|
|
try:
|
|
yield WaterHeater(coordinator, key, group)
|
|
except CoilNotFoundException as exception:
|
|
LOGGER.debug("Skipping water heater: %r", exception)
|
|
|
|
async_add_entities(water_heaters())
|
|
|
|
|
|
class WaterHeater(CoordinatorEntity[CoilCoordinator], WaterHeaterEntity):
|
|
"""Sensor entity."""
|
|
|
|
_attr_entity_category = None
|
|
_attr_has_entity_name = True
|
|
_attr_supported_features = WaterHeaterEntityFeature.OPERATION_MODE
|
|
_attr_max_temp = 35.0
|
|
_attr_min_temp = 5.0
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: CoilCoordinator,
|
|
key: str,
|
|
desc: WaterHeaterCoilGroup,
|
|
) -> None:
|
|
"""Initialize entity."""
|
|
|
|
super().__init__(
|
|
coordinator,
|
|
{
|
|
desc.hot_water_load,
|
|
desc.hot_water_comfort_mode,
|
|
*set(desc.start_temperature.values()),
|
|
*set(desc.stop_temperature.values()),
|
|
desc.active_accessory,
|
|
desc.temporary_lux,
|
|
},
|
|
)
|
|
self._attr_entity_registry_enabled_default = desc.active_accessory is None
|
|
self._attr_available = False
|
|
self._attr_name = desc.name
|
|
self._attr_unique_id = f"{coordinator.unique_id}-{key}"
|
|
self._attr_device_info = coordinator.device_info
|
|
|
|
self._attr_current_operation = None
|
|
self._attr_target_temperature_high = None
|
|
self._attr_target_temperature_low = None
|
|
self._attr_operation_list = []
|
|
self._operation_mode_to_lux: dict[str, str] = {}
|
|
|
|
def _get(address: int) -> Coil:
|
|
return coordinator.heatpump.get_coil_by_address(address)
|
|
|
|
def _map(data: dict[str, int]) -> dict[str, Coil]:
|
|
return {key: _get(address) for key, address in data.items()}
|
|
|
|
self._coil_current = _get(desc.hot_water_load)
|
|
self._coil_start_temperature = _map(desc.start_temperature)
|
|
self._coil_stop_temperature = _map(desc.stop_temperature)
|
|
self._coil_temporary_lux: Coil | None = None
|
|
if desc.temporary_lux:
|
|
self._coil_temporary_lux = _get(desc.temporary_lux)
|
|
self._coil_active_accessory: Coil | None = None
|
|
if address := desc.active_accessory:
|
|
self._coil_active_accessory = _get(address)
|
|
|
|
self._coil_hot_water_comfort_mode = _get(desc.hot_water_comfort_mode)
|
|
|
|
def _add_lux_mode(temporary_lux: str, operation_mode: str) -> None:
|
|
assert self._attr_operation_list is not None
|
|
if (
|
|
not self._coil_temporary_lux
|
|
or not self._coil_temporary_lux.reverse_mappings
|
|
):
|
|
return
|
|
|
|
if temporary_lux not in self._coil_temporary_lux.reverse_mappings:
|
|
return
|
|
|
|
self._attr_operation_list.append(operation_mode)
|
|
self._operation_mode_to_lux[operation_mode] = temporary_lux
|
|
|
|
_add_lux_mode(VALUES_TEMPORARY_LUX_ONE_TIME_INCREASE, STATE_HIGH_DEMAND)
|
|
_add_lux_mode(VALUES_TEMPORARY_LUX_INACTIVE, STATE_HEAT_PUMP)
|
|
|
|
self._attr_temperature_unit = self._coil_current.unit
|
|
|
|
@callback
|
|
def _handle_coordinator_update(self) -> None:
|
|
if not self.coordinator.data:
|
|
return
|
|
|
|
def _get_float(coil: Coil | None) -> float | None:
|
|
if coil is None:
|
|
return None
|
|
return self.coordinator.get_coil_float(coil)
|
|
|
|
def _get_value(coil: Coil | None) -> int | str | float | date | None:
|
|
if coil is None:
|
|
return None
|
|
return self.coordinator.get_coil_value(coil)
|
|
|
|
self._attr_current_temperature = _get_float(self._coil_current)
|
|
|
|
if (mode := _get_value(self._coil_hot_water_comfort_mode)) and isinstance(
|
|
mode, str
|
|
):
|
|
self._attr_target_temperature_low = _get_float(
|
|
self._coil_start_temperature.get(mode)
|
|
)
|
|
self._attr_target_temperature_high = _get_float(
|
|
self._coil_stop_temperature.get(mode)
|
|
)
|
|
else:
|
|
self._attr_target_temperature_low = None
|
|
self._attr_target_temperature_high = None
|
|
|
|
if (
|
|
_get_value(self._coil_temporary_lux)
|
|
== VALUES_TEMPORARY_LUX_ONE_TIME_INCREASE
|
|
):
|
|
self._attr_current_operation = STATE_HIGH_DEMAND
|
|
else:
|
|
self._attr_current_operation = STATE_HEAT_PUMP
|
|
|
|
super()._handle_coordinator_update()
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
"""Return if entity is available."""
|
|
if not self.coordinator.last_update_success:
|
|
return False
|
|
|
|
if not self._coil_active_accessory:
|
|
return True
|
|
|
|
if active_accessory := self.coordinator.get_coil_value(
|
|
self._coil_active_accessory
|
|
):
|
|
return active_accessory == "ON"
|
|
|
|
return False
|
|
|
|
async def async_set_operation_mode(self, operation_mode: str) -> None:
|
|
"""Set new target operation mode."""
|
|
if not self._coil_temporary_lux:
|
|
raise HomeAssistantError("Not supported")
|
|
|
|
lux = self._operation_mode_to_lux.get(operation_mode)
|
|
if not lux:
|
|
raise ValueError(f"Unsupported operation mode {operation_mode}")
|
|
|
|
await self.coordinator.async_write_coil(self._coil_temporary_lux, lux)
|