Files
core/homeassistant/components/solaredge_modbus/sensor.py
T

731 lines
30 KiB
Python

"""Support for SolarEdge Modbus sensor entities."""
from collections.abc import Callable
from dataclasses import dataclass
from typing import override
from solaredged import Inverter, InverterStatus, Meter, SunSpecDID
from homeassistant.components.sensor import (
RestoreSensor,
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
PERCENTAGE,
EntityCategory,
UnitOfApparentPower,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfFrequency,
UnitOfPower,
UnitOfReactivePower,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from .const import LOGGER
from .coordinator import SolarEdgeModbusConfigEntry
from .entity import SolarEdgeModbusInverterEntity, SolarEdgeModbusMeterEntity
PARALLEL_UPDATES = 0
# Per-phase points only carry data on split- and three-phase inverters.
_MULTI_PHASE = (SunSpecDID.SPLIT_PHASE_INVERTER, SunSpecDID.THREE_PHASE_INVERTER)
# Meters report per-phase points from two phases up; the third phase only on a
# three-phase meter, and line-to-neutral voltages on everything but a delta.
_MULTI_PHASE_METER = (
SunSpecDID.SPLIT_PHASE_METER,
SunSpecDID.THREE_PHASE_WYE_METER,
SunSpecDID.THREE_PHASE_DELTA_METER,
)
_THREE_PHASE_METER = (
SunSpecDID.THREE_PHASE_WYE_METER,
SunSpecDID.THREE_PHASE_DELTA_METER,
)
# A delta meter has no neutral, so it measures nothing against one.
_NEUTRAL_METER = (
SunSpecDID.SINGLE_PHASE_METER,
SunSpecDID.SPLIT_PHASE_METER,
SunSpecDID.THREE_PHASE_WYE_METER,
)
_PHASE_NEUTRAL_METER = (
SunSpecDID.SPLIT_PHASE_METER,
SunSpecDID.THREE_PHASE_WYE_METER,
)
@dataclass(frozen=True, kw_only=True)
class SolarEdgeModbusSensorEntityDescription[ComponentT](SensorEntityDescription):
"""Describes a SolarEdge Modbus sensor entity."""
exists_fn: Callable[[ComponentT], bool] = lambda _: True
value_fn: Callable[[ComponentT], StateType]
INVERTER_SENSORS: tuple[SolarEdgeModbusSensorEntityDescription[Inverter], ...] = (
SolarEdgeModbusSensorEntityDescription(
key="ac_power",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda inverter: inverter.ac_power,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_energy",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda inverter: inverter.ac_energy,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_current",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=2,
value_fn=lambda inverter: inverter.ac_current,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_current_phase_a",
translation_key="current_phase_a",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=2,
exists_fn=lambda inverter: inverter.did in _MULTI_PHASE,
value_fn=lambda inverter: inverter.ac_current_a,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_current_phase_b",
translation_key="current_phase_b",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=2,
exists_fn=lambda inverter: inverter.did in _MULTI_PHASE,
value_fn=lambda inverter: inverter.ac_current_b,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_current_phase_c",
translation_key="current_phase_c",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=2,
exists_fn=lambda inverter: inverter.did is SunSpecDID.THREE_PHASE_INVERTER,
value_fn=lambda inverter: inverter.ac_current_c,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda inverter: inverter.did is SunSpecDID.SINGLE_PHASE_INVERTER,
value_fn=lambda inverter: inverter.ac_voltage_an,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_ab",
translation_key="voltage_phase_ab",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda inverter: inverter.did in _MULTI_PHASE,
value_fn=lambda inverter: inverter.ac_voltage_ab,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_bc",
translation_key="voltage_phase_bc",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda inverter: inverter.did is SunSpecDID.THREE_PHASE_INVERTER,
value_fn=lambda inverter: inverter.ac_voltage_bc,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_ca",
translation_key="voltage_phase_ca",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda inverter: inverter.did is SunSpecDID.THREE_PHASE_INVERTER,
value_fn=lambda inverter: inverter.ac_voltage_ca,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_an",
translation_key="voltage_phase_an",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda inverter: inverter.did in _MULTI_PHASE,
value_fn=lambda inverter: inverter.ac_voltage_an,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_bn",
translation_key="voltage_phase_bn",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda inverter: inverter.did in _MULTI_PHASE,
value_fn=lambda inverter: inverter.ac_voltage_bn,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_cn",
translation_key="voltage_phase_cn",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda inverter: inverter.did is SunSpecDID.THREE_PHASE_INVERTER,
value_fn=lambda inverter: inverter.ac_voltage_cn,
),
SolarEdgeModbusSensorEntityDescription(
key="dc_power",
translation_key="dc_power",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda inverter: inverter.dc_power,
),
SolarEdgeModbusSensorEntityDescription(
key="dc_current",
translation_key="dc_current",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=2,
value_fn=lambda inverter: inverter.dc_current,
),
SolarEdgeModbusSensorEntityDescription(
key="dc_voltage",
translation_key="dc_voltage",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
value_fn=lambda inverter: inverter.dc_voltage,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_frequency",
device_class=SensorDeviceClass.FREQUENCY,
native_unit_of_measurement=UnitOfFrequency.HERTZ,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
suggested_display_precision=2,
value_fn=lambda inverter: inverter.ac_frequency,
),
SolarEdgeModbusSensorEntityDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
value_fn=lambda inverter: inverter.temperature_heatsink,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_apparent_power",
device_class=SensorDeviceClass.APPARENT_POWER,
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
value_fn=lambda inverter: inverter.ac_va,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_reactive_power",
device_class=SensorDeviceClass.REACTIVE_POWER,
native_unit_of_measurement=UnitOfReactivePower.VOLT_AMPERE_REACTIVE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
value_fn=lambda inverter: inverter.ac_var,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_power_factor",
device_class=SensorDeviceClass.POWER_FACTOR,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
suggested_display_precision=1,
value_fn=lambda inverter: inverter.ac_power_factor,
),
SolarEdgeModbusSensorEntityDescription(
key="status",
translation_key="inverter_status",
device_class=SensorDeviceClass.ENUM,
options=[status.name.lower() for status in InverterStatus],
value_fn=lambda inverter: (
inverter.status.name.lower() if inverter.status else None
),
),
)
METER_SENSORS: tuple[SolarEdgeModbusSensorEntityDescription[Meter], ...] = (
SolarEdgeModbusSensorEntityDescription(
key="ac_power",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda meter: meter.ac_power,
),
SolarEdgeModbusSensorEntityDescription(
key="energy_exported",
translation_key="energy_exported",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda meter: meter.energy_exported,
),
SolarEdgeModbusSensorEntityDescription(
key="energy_imported",
translation_key="energy_imported",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda meter: meter.energy_imported,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_power_phase_a",
translation_key="power_phase_a",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
exists_fn=lambda meter: meter.did in _MULTI_PHASE_METER,
value_fn=lambda meter: meter.ac_power_a,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_power_phase_b",
translation_key="power_phase_b",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
exists_fn=lambda meter: meter.did in _MULTI_PHASE_METER,
value_fn=lambda meter: meter.ac_power_b,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_power_phase_c",
translation_key="power_phase_c",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
exists_fn=lambda meter: meter.did in _THREE_PHASE_METER,
value_fn=lambda meter: meter.ac_power_c,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_current",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=2,
value_fn=lambda meter: meter.ac_current,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_current_phase_a",
translation_key="current_phase_a",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=2,
exists_fn=lambda meter: meter.did in _MULTI_PHASE_METER,
value_fn=lambda meter: meter.ac_current_a,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_current_phase_b",
translation_key="current_phase_b",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=2,
exists_fn=lambda meter: meter.did in _MULTI_PHASE_METER,
value_fn=lambda meter: meter.ac_current_b,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_current_phase_c",
translation_key="current_phase_c",
device_class=SensorDeviceClass.CURRENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=2,
exists_fn=lambda meter: meter.did in _THREE_PHASE_METER,
value_fn=lambda meter: meter.ac_current_c,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda meter: meter.did in _NEUTRAL_METER,
value_fn=lambda meter: meter.ac_voltage_ln,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_an",
translation_key="voltage_phase_an",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda meter: meter.did in _PHASE_NEUTRAL_METER,
value_fn=lambda meter: meter.ac_voltage_an,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_bn",
translation_key="voltage_phase_bn",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda meter: meter.did in _PHASE_NEUTRAL_METER,
value_fn=lambda meter: meter.ac_voltage_bn,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_cn",
translation_key="voltage_phase_cn",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda meter: meter.did is SunSpecDID.THREE_PHASE_WYE_METER,
value_fn=lambda meter: meter.ac_voltage_cn,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_ab",
translation_key="voltage_phase_ab",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda meter: meter.did in _MULTI_PHASE_METER,
value_fn=lambda meter: meter.ac_voltage_ab,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_bc",
translation_key="voltage_phase_bc",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda meter: meter.did in _THREE_PHASE_METER,
value_fn=lambda meter: meter.ac_voltage_bc,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_voltage_phase_ca",
translation_key="voltage_phase_ca",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=1,
exists_fn=lambda meter: meter.did in _THREE_PHASE_METER,
value_fn=lambda meter: meter.ac_voltage_ca,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_frequency",
device_class=SensorDeviceClass.FREQUENCY,
native_unit_of_measurement=UnitOfFrequency.HERTZ,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
suggested_display_precision=2,
value_fn=lambda meter: meter.ac_frequency,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_apparent_power",
device_class=SensorDeviceClass.APPARENT_POWER,
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
value_fn=lambda meter: meter.ac_va,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_reactive_power",
device_class=SensorDeviceClass.REACTIVE_POWER,
native_unit_of_measurement=UnitOfReactivePower.VOLT_AMPERE_REACTIVE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
value_fn=lambda meter: meter.ac_var,
),
SolarEdgeModbusSensorEntityDescription(
key="ac_power_factor",
device_class=SensorDeviceClass.POWER_FACTOR,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
suggested_display_precision=1,
value_fn=lambda meter: meter.ac_power_factor,
),
SolarEdgeModbusSensorEntityDescription(
key="energy_exported_phase_a",
translation_key="energy_exported_phase_a",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
exists_fn=lambda meter: meter.did in _MULTI_PHASE_METER,
value_fn=lambda meter: meter.energy_exported_a,
),
SolarEdgeModbusSensorEntityDescription(
key="energy_exported_phase_b",
translation_key="energy_exported_phase_b",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
exists_fn=lambda meter: meter.did in _MULTI_PHASE_METER,
value_fn=lambda meter: meter.energy_exported_b,
),
SolarEdgeModbusSensorEntityDescription(
key="energy_exported_phase_c",
translation_key="energy_exported_phase_c",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
exists_fn=lambda meter: meter.did in _THREE_PHASE_METER,
value_fn=lambda meter: meter.energy_exported_c,
),
SolarEdgeModbusSensorEntityDescription(
key="energy_imported_phase_a",
translation_key="energy_imported_phase_a",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
exists_fn=lambda meter: meter.did in _MULTI_PHASE_METER,
value_fn=lambda meter: meter.energy_imported_a,
),
SolarEdgeModbusSensorEntityDescription(
key="energy_imported_phase_b",
translation_key="energy_imported_phase_b",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
exists_fn=lambda meter: meter.did in _MULTI_PHASE_METER,
value_fn=lambda meter: meter.energy_imported_b,
),
SolarEdgeModbusSensorEntityDescription(
key="energy_imported_phase_c",
translation_key="energy_imported_phase_c",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
exists_fn=lambda meter: meter.did in _THREE_PHASE_METER,
value_fn=lambda meter: meter.energy_imported_c,
),
)
def _inverter_sensor(
entry: SolarEdgeModbusConfigEntry,
description: SolarEdgeModbusSensorEntityDescription[Inverter],
) -> SensorEntity:
"""Build an inverter sensor, monotonic where its state class asks for it."""
if description.state_class is SensorStateClass.TOTAL_INCREASING:
return SolarEdgeModbusInverterEnergySensorEntity(
entry=entry, description=description
)
return SolarEdgeModbusInverterSensorEntity(entry=entry, description=description)
def _meter_sensor(
entry: SolarEdgeModbusConfigEntry,
description: SolarEdgeModbusSensorEntityDescription[Meter],
index: int,
) -> SensorEntity:
"""Build a meter sensor, monotonic where its state class asks for it."""
if description.state_class is SensorStateClass.TOTAL_INCREASING:
return SolarEdgeModbusMeterEnergySensorEntity(
entry=entry, description=description, index=index
)
return SolarEdgeModbusMeterSensorEntity(
entry=entry, description=description, index=index
)
async def async_setup_entry(
hass: HomeAssistant,
entry: SolarEdgeModbusConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up SolarEdge Modbus sensor entities based on a config entry."""
solaredge = entry.runtime_data.solaredge
entities: list[SensorEntity] = [
_inverter_sensor(entry, description)
for description in INVERTER_SENSORS
if description.exists_fn(solaredge.inverter)
]
entities.extend(
_meter_sensor(entry, description, index)
for index, meter in enumerate(solaredge.meters, 1)
for description in METER_SENSORS
if description.exists_fn(meter)
)
async_add_entities(entities)
class SolarEdgeModbusInverterSensorEntity(SolarEdgeModbusInverterEntity, SensorEntity):
"""Defines a SolarEdge Modbus inverter sensor entity."""
entity_description: SolarEdgeModbusSensorEntityDescription[Inverter]
@property
@override
def native_value(self) -> StateType:
"""Return the sensor value."""
return self.entity_description.value_fn(self.coordinator.solaredge.inverter)
class SolarEdgeModbusMeterSensorEntity(SolarEdgeModbusMeterEntity, SensorEntity):
"""Defines a SolarEdge Modbus meter sensor entity."""
entity_description: SolarEdgeModbusSensorEntityDescription[Meter]
@property
@override
def native_value(self) -> StateType:
"""Return the sensor value."""
return self.entity_description.value_fn(
self.coordinator.solaredge.meters[self._index - 1]
)
class SolarEdgeModbusEnergySensorEntity(RestoreSensor):
"""Keeps a lifetime-energy sensor monotonic across glitches and restarts.
SolarEdge accumulators transiently report lower values (or zero) around
the inverter's sleep/wake transition; a single such sample fed to a
``total_increasing`` sensor registers as a meter reset and corrupts the
long-term statistics. The highest value seen wins, and it is restored
across restarts so an overnight restart does not lose that truth.
"""
_highest_value: float | None = None
_glitch_logged = False
@override
async def async_added_to_hass(self) -> None:
"""Restore the highest previously seen value."""
await super().async_added_to_hass()
data = await self.async_get_last_sensor_data()
if data is not None and isinstance(data.native_value, (int, float)):
self._highest_value = data.native_value
@property
@override
def native_value(self) -> StateType:
"""Return the sensor value, never lower than seen before."""
value = super().native_value
if not isinstance(value, (int, float)):
return self._highest_value
if self._highest_value is None or value >= self._highest_value:
self._highest_value = value
self._glitch_logged = False
return value
if not self._glitch_logged:
LOGGER.warning(
(
"%s reported a lifetime energy of %s Wh, lower than the"
" %s Wh seen before; ignoring the lower value (a known"
" SolarEdge glitch around its sleep/wake transition)"
),
self.entity_id,
value,
self._highest_value,
)
self._glitch_logged = True
return self._highest_value
class SolarEdgeModbusInverterEnergySensorEntity(
SolarEdgeModbusEnergySensorEntity, SolarEdgeModbusInverterSensorEntity
):
"""Defines a monotonic SolarEdge Modbus inverter energy sensor entity."""
class SolarEdgeModbusMeterEnergySensorEntity(
SolarEdgeModbusEnergySensorEntity, SolarEdgeModbusMeterSensorEntity
):
"""Defines a monotonic SolarEdge Modbus meter energy sensor entity."""