diff --git a/README.md b/README.md index 1ea88f0..ded22d5 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,7 @@ data: ### Energy Sensors - **Energy data not updating** — Ensure "Enable daily energy sensors" is checked in the integration options. Note that energy data resets daily. - **Current power seems inaccurate** — Current power is extrapolated from the daily energy reading and may not reflect instantaneous power accurately. +- **Random power spikes** — Current/cooling/heating extrapolated power are derived from daily energy readings and may spike on rare boundary cases; implausible readings are filtered in the integration, but a definitive fix for the calculation upstream is tracked in aio-panasonic-comfort-cloud. --- diff --git a/custom_components/panasonic_cc/panasonic/sensor.py b/custom_components/panasonic_cc/panasonic/sensor.py index 4c19e6b..5d06538 100644 --- a/custom_components/panasonic_cc/panasonic/sensor.py +++ b/custom_components/panasonic_cc/panasonic/sensor.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from datetime import datetime import logging +import math from typing import Any from homeassistant.const import UnitOfEnergy, UnitOfTemperature, EntityCategory @@ -258,9 +259,12 @@ class PanasonicEnergySensorEntity(PanasonicEnergyEntity, SensorEntity): entity_description: PanasonicEnergySensorEntityDescription # type: ignore[reportIncompatibleVariableOverride] + _POWER_KEYS = frozenset({"current_power", "cooling_power", "heating_power"}) + def __init__(self, coordinator: PanasonicDeviceEnergyCoordinator, description: PanasonicEnergySensorEntityDescription): self.entity_description = description # type: ignore[reportIncompatibleVariableOverride] super().__init__(coordinator, description.key) + self._prev_power: dict[str, float | None] = {} @property # type: ignore[reportIncompatibleOverride] def available(self) -> bool: @@ -275,7 +279,36 @@ def _async_update_attrs(self) -> None: if self.entity_description.get_state is None: return value = self.entity_description.get_state(energy) - self._attr_available = value is not None + key = self.entity_description.key + if value is None: + self._attr_available = False + return + try: + value = float(value) # type: ignore[arg-type] + except (TypeError, ValueError): + self._attr_available = self._prev_power.get(key) is not None + self._attr_native_value = self._prev_power.get(key) # type: ignore[assignment] + _LOGGER.debug("Discarding implausible power reading %r for %s", value, key) + return + prev = self._prev_power.get(key) + if math.isnan(value) or value < 0: + self._attr_available = prev is not None + self._attr_native_value = prev # type: ignore[assignment] + _LOGGER.debug("Discarding implausible power reading %r for %s", value, key) + return + if ( + key in self._POWER_KEYS + and prev is not None + and prev > 0 + and value > prev * 10 + and value > 5000 + ): + self._attr_available = True + self._attr_native_value = prev # type: ignore[assignment] + _LOGGER.debug("Discarding implausible power reading %r for %s", value, key) + return + self._prev_power[key] = value + self._attr_available = True self._attr_native_value = value # type: ignore[assignment]