Adding system status for ventilation modes

This commit is contained in:
Constantin Pascal
2026-02-02 14:30:51 +02:00
parent c6fde73e7a
commit cb57dc1e92
4 changed files with 103 additions and 0 deletions
@@ -24,6 +24,10 @@ from .const import (
DATA_INTEGRATION_REQUEST, DATA_INTEGRATION_REQUEST,
DATA_PUMP_ACTIVE, DATA_PUMP_ACTIVE,
DATA_RENEWAL_REQUEST, DATA_RENEWAL_REQUEST,
DATA_VENTILATION_MODE_DEHUMIDIFICATION,
DATA_VENTILATION_MODE_INTEGRATION,
DATA_VENTILATION_MODE_RENEWAL,
DATA_VENTILATION_MODE_VENTILATION,
DATA_VENTILATION_REQUEST, DATA_VENTILATION_REQUEST,
DATA_ZONE_ACTIVITY, DATA_ZONE_ACTIVITY,
DOMAIN, DOMAIN,
@@ -42,6 +46,14 @@ ZONE_REQUEST_TYPES: dict[str, tuple[str, str]] = {
"dehumidification_pump": (DATA_DEHUMIDIFICATION_PUMP, "Dehumidification pump"), "dehumidification_pump": (DATA_DEHUMIDIFICATION_PUMP, "Dehumidification pump"),
} }
# Ventilation mode types with their data keys and display names
VENTILATION_MODE_TYPES: dict[str, tuple[str, str]] = {
"dehumidification": (DATA_VENTILATION_MODE_DEHUMIDIFICATION, "Dehumidification"),
"ventilation": (DATA_VENTILATION_MODE_VENTILATION, "Ventilation"),
"renewal": (DATA_VENTILATION_MODE_RENEWAL, "Renewal"),
"integration": (DATA_VENTILATION_MODE_INTEGRATION, "Integration"),
}
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
@@ -58,6 +70,10 @@ async def async_setup_entry(
for pump_id in range(1, 9): for pump_id in range(1, 9):
entities.append(RDZPumpActiveBinarySensor(coordinator, pump_id)) entities.append(RDZPumpActiveBinarySensor(coordinator, pump_id))
# Add ventilation mode binary sensors
for mode_type in VENTILATION_MODE_TYPES:
entities.append(RDZVentilationModeBinarySensor(coordinator, mode_type))
# Add zone pump binary sensors for each configured zone # Add zone pump binary sensors for each configured zone
for zone_id_str, zone_data in zones_config.items(): for zone_id_str, zone_data in zones_config.items():
zone_id = int(zone_id_str) if isinstance(zone_id_str, str) else zone_id_str zone_id = int(zone_id_str) if isinstance(zone_id_str, str) else zone_id_str
@@ -119,6 +135,55 @@ class RDZPumpActiveBinarySensor(CoordinatorEntity[RDZDataUpdateCoordinator], Bin
self.async_write_ha_state() self.async_write_ha_state()
class RDZVentilationModeBinarySensor(CoordinatorEntity[RDZDataUpdateCoordinator], BinarySensorEntity):
"""Representation of an RDZ HMI ventilation mode binary sensor.
Indicates whether a ventilation unit work mode is active.
"""
_attr_has_entity_name = True
_attr_device_class = BinarySensorDeviceClass.RUNNING
def __init__(
self,
coordinator: RDZDataUpdateCoordinator,
mode_type: str,
) -> None:
"""Initialize the ventilation mode binary sensor entity.
Args:
coordinator: The data coordinator.
mode_type: The type of ventilation mode (key in VENTILATION_MODE_TYPES).
"""
super().__init__(coordinator)
self._mode_type = mode_type
self._data_key, self._display_name = VENTILATION_MODE_TYPES[mode_type]
self._attr_unique_id = f"{DOMAIN}_{coordinator.client.host}_ventilation_mode_{mode_type}"
self._attr_name = self._display_name
# Place on the System device
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{coordinator.client.host}_system")},
name="RDZ HMI System",
manufacturer="RDZ",
model="HMI Control System",
sw_version="1.0",
)
@property
def is_on(self) -> bool | None:
"""Return True if the ventilation mode is active."""
if self.coordinator.data is None:
return None
return self.coordinator.data.get(self._data_key)
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self.async_write_ha_state()
class RDZZonePumpBinarySensor(CoordinatorEntity[RDZDataUpdateCoordinator], BinarySensorEntity): class RDZZonePumpBinarySensor(CoordinatorEntity[RDZDataUpdateCoordinator], BinarySensorEntity):
"""Representation of an RDZ HMI zone pump binary sensor. """Representation of an RDZ HMI zone pump binary sensor.
@@ -140,6 +140,12 @@ REGISTER_CALCULATED_WATER_TEMP_COUNT = 8
# Pump active bitmask register # Pump active bitmask register
REGISTER_PUMP_ACTIVE = 7615 REGISTER_PUMP_ACTIVE = 7615
# Ventilation mode registers (LSB = on/off)
REGISTER_VENTILATION_MODE_DEHUMIDIFICATION = 8201
REGISTER_VENTILATION_MODE_VENTILATION = 8202
REGISTER_VENTILATION_MODE_RENEWAL = 8203
REGISTER_VENTILATION_MODE_INTEGRATION = 8204
# Data keys for new features # Data keys for new features
DATA_OUTSIDE_TEMP = "outside_temperature" DATA_OUTSIDE_TEMP = "outside_temperature"
DATA_TIME_SETTINGS = "time_settings" DATA_TIME_SETTINGS = "time_settings"
@@ -148,3 +154,7 @@ DATA_DELIVERY_WATER_TEMPS = "delivery_water_temps"
DATA_CALCULATED_WATER_TEMPS = "calculated_water_temps" DATA_CALCULATED_WATER_TEMPS = "calculated_water_temps"
DATA_PUMP_ACTIVE = "pump_active" DATA_PUMP_ACTIVE = "pump_active"
DATA_CALCULATED_SETPOINTS = "calculated_setpoints" DATA_CALCULATED_SETPOINTS = "calculated_setpoints"
DATA_VENTILATION_MODE_DEHUMIDIFICATION = "ventilation_mode_dehumidification"
DATA_VENTILATION_MODE_VENTILATION = "ventilation_mode_ventilation"
DATA_VENTILATION_MODE_RENEWAL = "ventilation_mode_renewal"
DATA_VENTILATION_MODE_INTEGRATION = "ventilation_mode_integration"
@@ -34,6 +34,10 @@ from .const import (
DATA_SYSTEM_ACTIVATION, DATA_SYSTEM_ACTIVATION,
DATA_TEMPERATURES, DATA_TEMPERATURES,
DATA_TIME_SETTINGS, DATA_TIME_SETTINGS,
DATA_VENTILATION_MODE_DEHUMIDIFICATION,
DATA_VENTILATION_MODE_INTEGRATION,
DATA_VENTILATION_MODE_RENEWAL,
DATA_VENTILATION_MODE_VENTILATION,
DATA_VENTILATION_REQUEST, DATA_VENTILATION_REQUEST,
DATA_WINTER_SETPOINTS, DATA_WINTER_SETPOINTS,
DATA_ZONE_ACTIVITY, DATA_ZONE_ACTIVITY,
@@ -107,6 +111,9 @@ class RDZDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
integration_request = await self.client.read_integration_request() integration_request = await self.client.read_integration_request()
dehumidification_pump = await self.client.read_dehumidification_pump() dehumidification_pump = await self.client.read_dehumidification_pump()
# Read ventilation mode registers
ventilation_modes = await self.client.read_ventilation_modes()
if temperatures is None or winter_setpoints is None or summer_setpoints is None: if temperatures is None or winter_setpoints is None or summer_setpoints is None:
raise UpdateFailed("Failed to read data from Modbus device") raise UpdateFailed("Failed to read data from Modbus device")
@@ -140,6 +147,11 @@ class RDZDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
DATA_RENEWAL_REQUEST: renewal_request, DATA_RENEWAL_REQUEST: renewal_request,
DATA_INTEGRATION_REQUEST: integration_request, DATA_INTEGRATION_REQUEST: integration_request,
DATA_DEHUMIDIFICATION_PUMP: dehumidification_pump, DATA_DEHUMIDIFICATION_PUMP: dehumidification_pump,
# Ventilation mode states
DATA_VENTILATION_MODE_DEHUMIDIFICATION: ventilation_modes.get("dehumidification") if ventilation_modes else None,
DATA_VENTILATION_MODE_VENTILATION: ventilation_modes.get("ventilation") if ventilation_modes else None,
DATA_VENTILATION_MODE_RENEWAL: ventilation_modes.get("renewal") if ventilation_modes else None,
DATA_VENTILATION_MODE_INTEGRATION: ventilation_modes.get("integration") if ventilation_modes else None,
} }
except Exception as ex: except Exception as ex:
@@ -558,3 +558,19 @@ class RDZModbusClient:
return await self._read_zone_bitmask( return await self._read_zone_bitmask(
REGISTER_DEHUMIDIFICATION_PUMP_START, REGISTER_DEHUMIDIFICATION_PUMP_COUNT REGISTER_DEHUMIDIFICATION_PUMP_START, REGISTER_DEHUMIDIFICATION_PUMP_COUNT
) )
async def read_ventilation_modes(self) -> dict[str, bool] | None:
"""Read ventilation mode registers 8201-8204.
Returns dict with keys: dehumidification, ventilation, renewal, integration.
LSB (bit 0) of each register indicates on/off state.
"""
registers = await self.read_registers(8201, 4)
if registers is None:
return None
return {
"dehumidification": bool(registers[0] & 1),
"ventilation": bool(registers[1] & 1),
"renewal": bool(registers[2] & 1),
"integration": bool(registers[3] & 1),
}