Skip to content

Commit b7dcb28

Browse files
committed
+ Implemented SystemairEnergySensor for energy usage display
1 parent a1beb85 commit b7dcb28

File tree

3 files changed

+131
-3
lines changed

3 files changed

+131
-3
lines changed

custom_components/systemair/sensor.py

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import TYPE_CHECKING, Any, ClassVar
77

88
from homeassistant.components.sensor import (
9+
RestoreSensor,
910
SensorDeviceClass,
1011
SensorEntity,
1112
SensorEntityDescription,
@@ -15,10 +16,12 @@
1516
PERCENTAGE,
1617
REVOLUTIONS_PER_MINUTE,
1718
EntityCategory,
19+
UnitOfEnergy,
1820
UnitOfPower,
1921
UnitOfTemperature,
2022
UnitOfTime,
2123
)
24+
from homeassistant.util import dt as dt_util
2225

2326
from .const import MODEL_SPECS
2427
from .entity import SystemairEntity
@@ -30,12 +33,15 @@
3033
)
3134

3235
if TYPE_CHECKING:
36+
from datetime import datetime, timedelta
37+
3338
from homeassistant.core import HomeAssistant
3439
from homeassistant.helpers.entity_platform import AddEntitiesCallback
3540

3641
from .coordinator import SystemairDataUpdateCoordinator
3742
from .data import SystemairConfigEntry
3843

44+
3945
YEAR_2000_THRESHOLD = 100
4046

4147
ALARM_ID_TO_NAME_MAP = {
@@ -110,6 +116,40 @@ class SystemairPowerSensorEntityDescription(SensorEntityDescription):
110116
"""Describes a Systemair power sensor entity."""
111117

112118

119+
@dataclass(kw_only=True, frozen=True)
120+
class SystemairEnergySensorEntityDescription(SensorEntityDescription):
121+
"""Describes a Systemair energy sensor entity."""
122+
123+
power_sensor_key: str
124+
125+
126+
ENERGY_SENSORS: tuple[SystemairEnergySensorEntityDescription, ...] = (
127+
SystemairEnergySensorEntityDescription(
128+
key="supply_fan_energy",
129+
translation_key="supply_fan_energy",
130+
device_class=SensorDeviceClass.ENERGY,
131+
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
132+
state_class=SensorStateClass.TOTAL_INCREASING,
133+
power_sensor_key="supply_fan_power",
134+
),
135+
SystemairEnergySensorEntityDescription(
136+
key="extract_fan_energy",
137+
translation_key="extract_fan_energy",
138+
device_class=SensorDeviceClass.ENERGY,
139+
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
140+
state_class=SensorStateClass.TOTAL_INCREASING,
141+
power_sensor_key="extract_fan_power",
142+
),
143+
SystemairEnergySensorEntityDescription(
144+
key="total_energy",
145+
translation_key="total_energy",
146+
device_class=SensorDeviceClass.ENERGY,
147+
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
148+
state_class=SensorStateClass.TOTAL_INCREASING,
149+
power_sensor_key="total_power",
150+
),
151+
)
152+
113153
POWER_SENSORS: tuple[SystemairPowerSensorEntityDescription, ...] = (
114154
SystemairPowerSensorEntityDescription(
115155
key="supply_fan_power",
@@ -268,14 +308,25 @@ async def async_setup_entry(
268308
"""Set up the sensor platform."""
269309
coordinator = entry.runtime_data.coordinator
270310

311+
power_sensors_map = {desc.key: SystemairPowerSensor(coordinator=coordinator, entity_description=desc) for desc in POWER_SENSORS}
312+
271313
sensors = [SystemairSensor(coordinator=coordinator, entity_description=desc) for desc in ENTITY_DESCRIPTIONS]
272-
power_sensors = [SystemairPowerSensor(coordinator=coordinator, entity_description=desc) for desc in POWER_SENSORS]
314+
power_sensors = list(power_sensors_map.values())
273315

274-
async_add_entities(sensors + power_sensors)
316+
energy_sensors = [
317+
SystemairEnergySensor(
318+
coordinator=coordinator,
319+
entity_description=desc,
320+
power_sensor=power_sensors_map[desc.power_sensor_key],
321+
)
322+
for desc in ENERGY_SENSORS
323+
]
324+
325+
async_add_entities(sensors + power_sensors + energy_sensors)
275326

276327

277328
class SystemairSensor(SystemairEntity, SensorEntity):
278-
"""Systemair Sensor class."""
329+
"""Systemair Sensor class for all sensors."""
279330

280331
_attr_has_entity_name = True
281332
entity_description: SystemairSensorEntityDescription
@@ -426,3 +477,62 @@ def native_value(self) -> float | None:
426477
}
427478

428479
return power_map.get(self.entity_description.key)
480+
481+
482+
class SystemairEnergySensor(SystemairEntity, RestoreSensor):
483+
"""Systemair Energy Sensor class."""
484+
485+
_attr_has_entity_name = True
486+
entity_description: SystemairEnergySensorEntityDescription
487+
488+
def __init__(
489+
self,
490+
coordinator: SystemairDataUpdateCoordinator,
491+
entity_description: SystemairEnergySensorEntityDescription,
492+
power_sensor: SystemairPowerSensor,
493+
) -> None:
494+
"""Initialize the energy sensor."""
495+
super().__init__(coordinator)
496+
self.entity_description = entity_description
497+
self._power_sensor = power_sensor
498+
self._attr_unique_id = f"{coordinator.config_entry.entry_id}-{entity_description.key}"
499+
self._last_update: datetime | None = None
500+
501+
async def async_added_to_hass(self) -> None:
502+
"""Handle entity which provides long-term statistics."""
503+
await super().async_added_to_hass()
504+
505+
last_sensor_data = await self.async_get_last_sensor_data()
506+
if last_sensor_data:
507+
self._attr_native_value = last_sensor_data.native_value
508+
509+
if (
510+
(last_state := await self.async_get_last_state())
511+
and "last_update" in last_state.attributes
512+
and last_state.attributes["last_update"] is not None
513+
):
514+
self._last_update = dt_util.parse_datetime(last_state.attributes["last_update"])
515+
516+
@property
517+
def extra_state_attributes(self) -> dict[str, Any]:
518+
"""Return the state attributes."""
519+
return {"last_update": self._last_update.isoformat() if self._last_update else None}
520+
521+
def _handle_coordinator_update(self) -> None:
522+
"""Handle updated data from the coordinator."""
523+
now = dt_util.utcnow()
524+
power_w = self._power_sensor.native_value
525+
526+
if power_w is None or self._last_update is None:
527+
self._last_update = now
528+
self.async_write_ha_state()
529+
return
530+
531+
time_delta: timedelta = now - self._last_update
532+
energy_increment_kwh = (power_w / 1000) * (time_delta.total_seconds() / 3600)
533+
534+
current_value = self.native_value or 0
535+
self._attr_native_value = round(current_value + energy_increment_kwh, 4)
536+
537+
self._last_update = now
538+
self.async_write_ha_state()

custom_components/systemair/translations/en.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@
135135
"total_power": {
136136
"name": "Total Power Consumption"
137137
},
138+
"supply_fan_energy": {
139+
"name": "Supply Fan Energy"
140+
},
141+
"extract_fan_energy": {
142+
"name": "Extract Fan Energy"
143+
},
144+
"total_energy": {
145+
"name": "Total Energy Consumption"
146+
},
138147
"alarm_history": {
139148
"name": "Alarm History"
140149
},

custom_components/systemair/translations/ru.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@
135135
"total_power": {
136136
"name": "Общее энергопотребление"
137137
},
138+
"supply_fan_energy": {
139+
"name": "Энергопотребление приточного вентилятора"
140+
},
141+
"extract_fan_energy": {
142+
"name": "Энергопотребление вытяжного вентилятора"
143+
},
144+
"total_energy": {
145+
"name": "Общее потребление энергии"
146+
},
138147
"alarm_history": {
139148
"name": "История аварий"
140149
},

0 commit comments

Comments
 (0)