1- """Sensor platform for Systemair0 ."""
1+ """Sensor platform for Systemair ."""
22
33from __future__ import annotations
44
55from dataclasses import dataclass
66from typing import TYPE_CHECKING
77
88from homeassistant .components .sensor import (
9+ SensorDeviceClass ,
910 SensorEntity ,
1011 SensorEntityDescription ,
12+ SensorStateClass ,
13+ )
14+ from homeassistant .const import (
15+ PERCENTAGE ,
16+ REVOLUTIONS_PER_MINUTE ,
17+ EntityCategory ,
18+ UnitOfPower ,
19+ UnitOfTemperature ,
20+ UnitOfTime ,
1121)
12- from homeassistant .components .sensor .const import SensorDeviceClass , SensorStateClass
13- from homeassistant .const import PERCENTAGE , REVOLUTIONS_PER_MINUTE , EntityCategory , UnitOfTemperature , UnitOfTime
1422
23+ from .const import MODEL_SPECS
1524from .entity import SystemairEntity
1625from .modbus import ModbusParameter , alarm_parameters , parameter_map
1726
2837 "Waiting" : 2 ,
2938 "Cleared Error Active" : 3 ,
3039}
31-
3240VALUE_MAP_TO_ALARM_STATE = {value : key for key , value in ALARM_STATE_TO_VALUE_MAP .items ()}
33-
3441IAQ_LEVEL_MAP = {0 : "Perfect" , 1 : "Good" , 2 : "Improving" }
3542DEMAND_CONTROLLER_MAP = {0 : "CO2" , 1 : "RH" }
3643DEFROSTING_STATE_MAP = {
4552@dataclass (kw_only = True , frozen = True )
4653class SystemairSensorEntityDescription (SensorEntityDescription ):
4754 """Describes a Systemair sensor entity."""
48-
4955 registry : ModbusParameter
5056
5157
58+ @dataclass (kw_only = True , frozen = True )
59+ class SystemairPowerSensorEntityDescription (SensorEntityDescription ):
60+ """Describes a Systemair power sensor entity."""
61+
62+
63+ POWER_SENSORS : tuple [SystemairPowerSensorEntityDescription , ...] = (
64+ SystemairPowerSensorEntityDescription (
65+ key = "supply_fan_power" ,
66+ translation_key = "supply_fan_power" ,
67+ device_class = SensorDeviceClass .POWER ,
68+ native_unit_of_measurement = UnitOfPower .WATT ,
69+ state_class = SensorStateClass .MEASUREMENT ,
70+ ),
71+ SystemairPowerSensorEntityDescription (
72+ key = "extract_fan_power" ,
73+ translation_key = "extract_fan_power" ,
74+ device_class = SensorDeviceClass .POWER ,
75+ native_unit_of_measurement = UnitOfPower .WATT ,
76+ state_class = SensorStateClass .MEASUREMENT ,
77+ ),
78+ SystemairPowerSensorEntityDescription (
79+ key = "total_power" ,
80+ translation_key = "total_power" ,
81+ device_class = SensorDeviceClass .POWER ,
82+ native_unit_of_measurement = UnitOfPower .WATT ,
83+ state_class = SensorStateClass .MEASUREMENT ,
84+ ),
85+ )
86+
5287ENTITY_DESCRIPTIONS = (
5388 SystemairSensorEntityDescription (
5489 key = "outside_air_temperature" ,
@@ -170,23 +205,27 @@ class SystemairSensorEntityDescription(SensorEntityDescription):
170205
171206
172207async def async_setup_entry (
173- hass : HomeAssistant , # noqa: ARG001 Unused function argument: `hass`
208+ hass : HomeAssistant , # noqa: ARG001
174209 entry : SystemairConfigEntry ,
175210 async_add_entities : AddEntitiesCallback ,
176211) -> None :
177212 """Set up the sensor platform."""
178- async_add_entities (
179- SystemairSensor (
180- coordinator = entry .runtime_data .coordinator ,
181- entity_description = entity_description ,
182- )
183- for entity_description in ENTITY_DESCRIPTIONS
184- )
213+ coordinator = entry .runtime_data .coordinator
214+
215+ sensors = [
216+ SystemairSensor (coordinator = coordinator , entity_description = desc )
217+ for desc in ENTITY_DESCRIPTIONS
218+ ]
219+ power_sensors = [
220+ SystemairPowerSensor (coordinator = coordinator , entity_description = desc )
221+ for desc in POWER_SENSORS
222+ ]
223+
224+ async_add_entities (sensors + power_sensors )
185225
186226
187227class SystemairSensor (SystemairEntity , SensorEntity ):
188228 """Systemair Sensor class."""
189-
190229 _attr_has_entity_name = True
191230
192231 entity_description : SystemairSensorEntityDescription
@@ -218,4 +257,54 @@ def native_value(self) -> str | None:
218257 if self .device_class == SensorDeviceClass .ENUM :
219258 return VALUE_MAP_TO_ALARM_STATE .get (value , "Inactive" )
220259
221- return str (value )
260+ return str (value )
261+
262+
263+ class SystemairPowerSensor (SystemairEntity , SensorEntity ):
264+ """Systemair Power Sensor class for calculated values."""
265+
266+ _attr_has_entity_name = True
267+ entity_description : SystemairPowerSensorEntityDescription
268+
269+ def __init__ (
270+ self ,
271+ coordinator : SystemairDataUpdateCoordinator ,
272+ entity_description : SystemairPowerSensorEntityDescription ,
273+ ) -> None :
274+ """Initialize the power sensor class."""
275+ super ().__init__ (coordinator )
276+ self .entity_description = entity_description
277+ self ._attr_unique_id = f"{ coordinator .config_entry .entry_id } -{ entity_description .key } "
278+
279+ @property
280+ def native_value (self ) -> float | None :
281+ """Return the calculated power consumption."""
282+ model = self .coordinator .config_entry .runtime_data .model
283+ specs = MODEL_SPECS .get (model )
284+ if not specs :
285+ return None
286+
287+ # Get current fan speeds and heater status
288+ supply_fan_pct = self .coordinator .get_modbus_data (parameter_map ["REG_OUTPUT_SAF" ])
289+ extract_fan_pct = self .coordinator .get_modbus_data (parameter_map ["REG_OUTPUT_EAF" ])
290+ heater_on = self .coordinator .get_modbus_data (parameter_map ["REG_OUTPUT_TRIAC" ])
291+
292+ if supply_fan_pct is None or extract_fan_pct is None or heater_on is None :
293+ return None
294+
295+ # Assume max fan power is split 50/50 between supply and extract fans
296+ max_fan_power_per_fan = specs ["fan_power" ] / 2
297+
298+ supply_power = (supply_fan_pct / 100 ) * max_fan_power_per_fan
299+ extract_power = (extract_fan_pct / 100 ) * max_fan_power_per_fan
300+ heater_power = specs ["heater_power" ] if heater_on else 0
301+
302+ key = self .entity_description .key
303+ if key == "supply_fan_power" :
304+ return round (supply_power , 1 )
305+ if key == "extract_fan_power" :
306+ return round (extract_power , 1 )
307+ if key == "total_power" :
308+ return round (supply_power + extract_power + heater_power , 1 )
309+
310+ return None
0 commit comments