diff --git a/custom_components/power_group_monitor/manifest.json b/custom_components/power_group_monitor/manifest.json index 155376b..34f71bb 100644 --- a/custom_components/power_group_monitor/manifest.json +++ b/custom_components/power_group_monitor/manifest.json @@ -1,7 +1,7 @@ { "domain": "power_group_monitor", "name": "PowerGroupMonitor", - "version": "0.3.0", + "version": "0.4.0", "documentation": "https://github.com/mephdrac/PowerGroupMonitor", "issue_tracker": "https://github.com/mephdrac/PowerGroupMonitor/issues", "requirements": [], diff --git a/custom_components/power_group_monitor/sensor.py b/custom_components/power_group_monitor/sensor.py index 763f76d..020550a 100644 --- a/custom_components/power_group_monitor/sensor.py +++ b/custom_components/power_group_monitor/sensor.py @@ -24,12 +24,15 @@ from .sensors.power_total_sensor import PowerTotalSensor from .sensors.power_peak_total_sensor import PowerPeakTotalSensor from .sensors.power_standby_total_sensor import PowerStandbyTotalSensor + from .sensors.energy_today_sensor import EnergyTodaySensor from .sensors.energy_total_sensor import EnergyTotalSensor - from .sensors.energy_total_all_sensor import EnergyTotalAllSensor from .sensors.energy_today_all_sensor import EnergyTodayAllSensor +from .sensors.average_power_sensor import AveragePowerSensor +from .sensors.average_power_all_sensor import AveragePowerAllSensor + from .const import ( CONF_GROUP_NAME, CONF_GROUP_ENTITIES, @@ -73,6 +76,8 @@ async def async_setup_entry( # pylint: disable=too-many-locals, too-many-statem entity_list = [] energy_total_list = [] energy_today_list = [] + power_average_list = [] + total_standby_threshold = float(0) for group in groups: @@ -93,6 +98,9 @@ async def async_setup_entry( # pylint: disable=too-many-locals, too-many-statem standby_threshold=float(standby_threshold), # konfigurierbarer Wert? ) + # Durchschnittswert + average_power = AveragePowerSensor(entry, group_name, power_sensor) + # Energie pro Gruppe heute energie_heute_gruppe = EnergyTodaySensor( hass, entry, group_id, group_name, power_sensor @@ -109,11 +117,13 @@ async def async_setup_entry( # pylint: disable=too-many-locals, too-many-statem standby_sensor, energie_heute_gruppe, energie_gesamt_gruppe, + average_power, ] ) energy_total_list.extend([energie_gesamt_gruppe]) energy_today_list.extend([energie_heute_gruppe]) + power_average_list.extend([average_power]) async_add_entities(entity_list, update_before_add=True) @@ -128,6 +138,7 @@ async def async_setup_entry( # pylint: disable=too-many-locals, too-many-statem all_energy_total = EnergyTotalAllSensor(entry, energy_total_list) all_energy_today = EnergyTodayAllSensor(entry, energy_today_list) + all_power_average = AveragePowerAllSensor(entry, power_average_list) async_add_entities( [ @@ -136,5 +147,6 @@ async def async_setup_entry( # pylint: disable=too-many-locals, too-many-statem power_standby_total_sensor, all_energy_total, all_energy_today, + all_power_average ] ) diff --git a/custom_components/power_group_monitor/sensors/average_power_all_sensor.py b/custom_components/power_group_monitor/sensors/average_power_all_sensor.py new file mode 100644 index 0000000..b453432 --- /dev/null +++ b/custom_components/power_group_monitor/sensors/average_power_all_sensor.py @@ -0,0 +1,98 @@ +"""Sensor für Gesamtenergie pro Gruppe. + +Dieses Modul definiert einen Sensor für Home Assistant, +der die gesamte Energie gruppenübergreifent berechnet. +Dazu werden einfach die Gruppensensoren addiert. +""" +import logging + +from homeassistant.components.sensor import SensorEntity, SensorStateClass +from homeassistant.const import UnitOfPower +from homeassistant.config_entries import ConfigEntry +from homeassistant.helpers.event import async_track_state_change_event + +from ..const import DEVICE_INFO, DOMAIN + +_LOGGER = logging.getLogger(__name__) + + +class AveragePowerAllSensor(SensorEntity): + """Addiert alle Gruppen-Gesamtsummen""" + _attr_translation_key = "AveragePowerAllSensor" + _attr_has_entity_name = True + + def __init__(self, entry: ConfigEntry, obj_entities) -> None: + """Initialisiert den Sensor. + + Args: + entry (ConfigEntry): Die Konfigurationseintrag-Instanz für diese Integration. + + """ + self._attr_suggested_display_precision = 3 + self._entry = entry + self._obj_entities = obj_entities + self._entities = [entity.entity_id for entity in self._obj_entities] + self._unsub = None + + self._attr_unique_id = f"{entry.entry_id}_avg_all_power" + # self._attr_device_class = SensorDeviceClass.M + self._attr_state_class = SensorStateClass.MEASUREMENT + self._attr_native_unit_of_measurement = UnitOfPower.WATT + + self.my_icon = "mdi:counter" + self._attr_native_value = None + + async def async_added_to_hass(self): + """Wird beim Hinzufügen zur Home Assistant-Instanz aufgerufen.""" + + for entity in self._obj_entities: + + if entity.entity_id is None: + _LOGGER.warning("Sensor entity_id is None during standby setup!") + return + + self._entities = [entity.entity_id for entity in self._obj_entities] + self._unsub = async_track_state_change_event(self.hass, self._entities, + self._async_state_changed) + + await self._async_update_value() + + # pylint: disable=unused-argument + async def _async_state_changed(self, event): + # Wird aufgerufen, wenn sich eine Entity im Set ändert + await self._async_update_value() + + async def _async_update_value(self): + total = 0.0 + for entity_id in self._entities: + state = self.hass.states.get(entity_id) + if state is None: + continue + try: + value = float(state.state) + + total += value + except ValueError: + continue + self._attr_native_value = round(total, 3) + self.async_write_ha_state() + + @property + def device_info(self): + """Liefert die Geräteinformationen für diese Sensor-Entity. + + Returns: + dict: Ein Dictionary mit Informationen zur Identifikation + des Geräts in Home Assistant, einschließlich: + - identifiers: Eindeutige Identifikatoren (Domain und Entry ID) + - name: Anzeigename des Geräts + - manufacturer: Herstellername + - model: Modellbezeichnung + + """ + + return { + "identifiers": {(DOMAIN, self._entry.entry_id)}, + "name": self._entry.title, + **DEVICE_INFO, + } diff --git a/custom_components/power_group_monitor/sensors/average_power_sensor.py b/custom_components/power_group_monitor/sensors/average_power_sensor.py new file mode 100644 index 0000000..525eaa8 --- /dev/null +++ b/custom_components/power_group_monitor/sensors/average_power_sensor.py @@ -0,0 +1,84 @@ +"""Modul definiert einen 15 Minuten Durchschnittsleistungssensor für Home Assistant. +Dazu wird die History der letzten 15 Minuten ausgewertet.""" +import logging +from datetime import timedelta + +from homeassistant.components import recorder +from homeassistant.components.sensor import SensorEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import UnitOfPower +from homeassistant.util import dt as dt_util + +from ..const import DEVICE_INFO, DOMAIN # noqa: TID252 +from .power_sensor import PowerSensor + +_LOGGER = logging.getLogger(__name__) + + +class AveragePowerSensor(SensorEntity): + """Durchschnittliche Leistung über 15 Minuten.""" + + _attr_translation_key = "AveragePowerSensor" + _attr_has_entity_name = True + + def __init__(self, entry: ConfigEntry, group_name: str, source: PowerSensor): + self._entry = entry + self._group_name = group_name + self._attr_translation_placeholders = {"index": self._group_name} + self._source = source + self._attr_unique_id = f"{entry.entry_id}_{self._group_name}_avg_power" + self._attr_native_unit_of_measurement = UnitOfPower.WATT + self._attr_suggested_display_precision = 3 + self._source_entity_id = source.entity_id + self._statistics_sensor = None + + async def async_added_to_hass(self): + """Wird aufgerufen, wenn die Entity zu HA hinzugefügt wird.""" + + source_entity_id = self._source.entity_id + if source_entity_id is None: + _LOGGER.warning("Sensor entity_id is None during avg setup!") + return + + await super().async_added_to_hass() + + async def async_update(self): + """Wird aufgerufen, wenn die Entity aktualisiert wird.""" + self._attr_native_value = await self.async_calculate_average() + + async def async_calculate_average(self): + """Berechne den Durchschnitt der letzten 15 Minuten aus der History.""" + now = dt_util.utcnow() + start = now - timedelta(minutes=15) + + def _fetch(): + return recorder.history.state_changes_during_period( + self.hass, + start_time=start, + end_time=now, + entity_id=self._source.entity_id, + no_attributes=True, + ) + + instance = recorder.get_instance(self.hass) + states = await instance.async_add_executor_job(_fetch) + + values = [] + for state in states.get(self._source.entity_id, []): + try: + values.append(float(state.state)) + except (ValueError, TypeError): + continue + + if values: + return sum(values) / len(values) + return None + + @property + def device_info(self): + """Liefert die Geräteinformationen für diese Sensor-Entity.""" + return { + "identifiers": {(DOMAIN, self._entry.entry_id)}, + "name": self._entry.title, + **DEVICE_INFO, + } diff --git a/custom_components/power_group_monitor/translations/de.json b/custom_components/power_group_monitor/translations/de.json index 3f81453..2038068 100644 --- a/custom_components/power_group_monitor/translations/de.json +++ b/custom_components/power_group_monitor/translations/de.json @@ -65,6 +65,9 @@ }, "EnergyTodayAllSensor":{ "name": "Gesamt - Energie heute" + }, + "AveragePowerSensor":{ + "name": "{index} - 15 Min. Durchschnitt" } } } diff --git a/custom_components/power_group_monitor/translations/en.json b/custom_components/power_group_monitor/translations/en.json index 89b4a89..43ecb46 100644 --- a/custom_components/power_group_monitor/translations/en.json +++ b/custom_components/power_group_monitor/translations/en.json @@ -65,6 +65,9 @@ }, "EnergyTodayAllSensor":{ "name": "Total - Energy today" + }, + "AveragePowerSensor":{ + "name": "{index} - 15 Min. Average" } } }