Add state class support to SolarEdge (#52271)

This commit is contained in:
Franck Nijhof 2021-06-29 08:37:33 +02:00 committed by GitHub
parent f1303e02ff
commit a93487f389
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 217 additions and 114 deletions

View File

@ -2,7 +2,11 @@
from datetime import timedelta from datetime import timedelta
import logging import logging
from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT
from homeassistant.const import ENERGY_WATT_HOUR, PERCENTAGE, POWER_WATT from homeassistant.const import ENERGY_WATT_HOUR, PERCENTAGE, POWER_WATT
from homeassistant.util import dt as dt_util
from .models import SolarEdgeSensor
DOMAIN = "solaredge" DOMAIN = "solaredge"
@ -23,64 +27,153 @@ ENERGY_DETAILS_DELAY = timedelta(minutes=15)
SCAN_INTERVAL = timedelta(minutes=15) SCAN_INTERVAL = timedelta(minutes=15)
# Supported overview sensor types: # Supported overview sensors
# Key: ['json_key', 'name', unit, icon, default] SENSOR_TYPES = [
SENSOR_TYPES = { SolarEdgeSensor(
"lifetime_energy": [ key="lifetime_energy",
"lifeTimeData", json_key="lifeTimeData",
"Lifetime energy", name="Lifetime energy",
ENERGY_WATT_HOUR, icon="mdi:solar-power",
"mdi:solar-power", last_reset=dt_util.utc_from_timestamp(0),
False, state_class=STATE_CLASS_MEASUREMENT,
], unit_of_measurement=ENERGY_WATT_HOUR,
"energy_this_year": [ ),
"lastYearData", SolarEdgeSensor(
"Energy this year", key="energy_this_year",
ENERGY_WATT_HOUR, json_key="lastYearData",
"mdi:solar-power", name="Energy this year",
False, entity_registry_enabled_default=False,
], icon="mdi:solar-power",
"energy_this_month": [ unit_of_measurement=ENERGY_WATT_HOUR,
"lastMonthData", ),
"Energy this month", SolarEdgeSensor(
ENERGY_WATT_HOUR, key="energy_this_month",
"mdi:solar-power", json_key="lastMonthData",
False, name="Energy this month",
], entity_registry_enabled_default=False,
"energy_today": [ icon="mdi:solar-power",
"lastDayData", unit_of_measurement=ENERGY_WATT_HOUR,
"Energy today", ),
ENERGY_WATT_HOUR, SolarEdgeSensor(
"mdi:solar-power", key="energy_today",
False, json_key="lastDayData",
], name="Energy today",
"current_power": [ entity_registry_enabled_default=False,
"currentPower", icon="mdi:solar-power",
"Current Power", unit_of_measurement=ENERGY_WATT_HOUR,
POWER_WATT, ),
"mdi:solar-power", SolarEdgeSensor(
True, key="current_power",
], json_key="currentPower",
"site_details": [None, "Site details", None, None, False], name="Current Power",
"meters": ["meters", "Meters", None, None, False], icon="mdi:solar-power",
"sensors": ["sensors", "Sensors", None, None, False], state_class=STATE_CLASS_MEASUREMENT,
"gateways": ["gateways", "Gateways", None, None, False], unit_of_measurement=POWER_WATT,
"batteries": ["batteries", "Batteries", None, None, False], ),
"inverters": ["inverters", "Inverters", None, None, False], SolarEdgeSensor(
"power_consumption": ["LOAD", "Power Consumption", None, "mdi:flash", False], key="site_details",
"solar_power": ["PV", "Solar Power", None, "mdi:solar-power", False], name="Site details",
"grid_power": ["GRID", "Grid Power", None, "mdi:power-plug", False], entity_registry_enabled_default=False,
"storage_power": ["STORAGE", "Storage Power", None, "mdi:car-battery", False], ),
"purchased_power": ["Purchased", "Imported Power", None, "mdi:flash", False], SolarEdgeSensor(
"production_power": ["Production", "Production Power", None, "mdi:flash", False], key="meters",
"consumption_power": ["Consumption", "Consumption Power", None, "mdi:flash", False], json_key="meters",
"selfconsumption_power": [ name="Meters",
"SelfConsumption", entity_registry_enabled_default=False,
"SelfConsumption Power", ),
None, SolarEdgeSensor(
"mdi:flash", key="sensors",
False, json_key="sensors",
], name="Sensors",
"feedin_power": ["FeedIn", "Exported Power", None, "mdi:flash", False], entity_registry_enabled_default=False,
"storage_level": ["STORAGE", "Storage Level", PERCENTAGE, None, False], ),
} SolarEdgeSensor(
key="gateways",
json_key="gateways",
name="Gateways",
entity_registry_enabled_default=False,
),
SolarEdgeSensor(
key="batteries",
json_key="batteries",
name="Batteries",
entity_registry_enabled_default=False,
),
SolarEdgeSensor(
key="inverters",
json_key="inverters",
name="Inverters",
entity_registry_enabled_default=False,
),
SolarEdgeSensor(
key="power_consumption",
json_key="LOAD",
name="Power Consumption",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
key="solar_power",
json_key="PV",
name="Solar Power",
entity_registry_enabled_default=False,
icon="mdi:solar-power",
),
SolarEdgeSensor(
key="grid_power",
json_key="GRID",
name="Grid Power",
entity_registry_enabled_default=False,
icon="mdi:power-plug",
),
SolarEdgeSensor(
key="storage_power",
json_key="STORAGE",
name="Storage Power",
entity_registry_enabled_default=False,
icon="mdi:car-battery",
),
SolarEdgeSensor(
key="purchased_power",
json_key="Purchased",
name="Imported Power",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
key="production_power",
json_key="Production",
name="Production Power",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
key="consumption_power",
json_key="Consumption",
name="Consumption Power",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
key="selfconsumption_power",
json_key="SelfConsumption",
name="SelfConsumption Power",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
key="feedin_power",
json_key="FeedIn",
name="Exported Power",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
key="storage_level",
json_key="STORAGE",
name="Storage Level",
entity_registry_enabled_default=False,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=PERCENTAGE,
),
]

View File

@ -0,0 +1,21 @@
"""Models for the SolarEdge integration."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
@dataclass
class SolarEdgeSensor:
"""Represents an SolarEdge Sensor."""
key: str
name: str
json_key: str | None = None
device_class: str | None = None
entity_registry_enabled_default: bool = True
icon: str | None = None
last_reset: datetime | None = None
state_class: str | None = None
unit_of_measurement: str | None = None

View File

@ -21,6 +21,7 @@ from .coordinator import (
SolarEdgeOverviewDataService, SolarEdgeOverviewDataService,
SolarEdgePowerFlowDataService, SolarEdgePowerFlowDataService,
) )
from .models import SolarEdgeSensor
async def async_setup_entry( async def async_setup_entry(
@ -40,8 +41,8 @@ async def async_setup_entry(
await service.coordinator.async_refresh() await service.coordinator.async_refresh()
entities = [] entities = []
for sensor_key in SENSOR_TYPES: for sensor_type in SENSOR_TYPES:
sensor = sensor_factory.create_sensor(sensor_key) sensor = sensor_factory.create_sensor(sensor_type)
if sensor is not None: if sensor is not None:
entities.append(sensor) entities.append(sensor)
async_add_entities(entities) async_add_entities(entities)
@ -98,48 +99,49 @@ class SolarEdgeSensorFactory:
]: ]:
self.services[key] = (SolarEdgeEnergyDetailsSensor, energy) self.services[key] = (SolarEdgeEnergyDetailsSensor, energy)
def create_sensor(self, sensor_key: str) -> SolarEdgeSensor: def create_sensor(self, sensor_type: SolarEdgeSensor) -> SolarEdgeSensor:
"""Create and return a sensor based on the sensor_key.""" """Create and return a sensor based on the sensor_key."""
sensor_class, service = self.services[sensor_key] sensor_class, service = self.services[sensor_type.key]
return sensor_class(self.platform_name, sensor_key, service) return sensor_class(self.platform_name, sensor_type, service)
class SolarEdgeSensor(CoordinatorEntity, SensorEntity): class SolarEdgeSensorEntity(CoordinatorEntity, SensorEntity):
"""Abstract class for a solaredge sensor.""" """Abstract class for a solaredge sensor."""
def __init__( def __init__(
self, platform_name: str, sensor_key: str, data_service: SolarEdgeDataService self,
platform_name: str,
sensor_type: SolarEdgeSensor,
data_service: SolarEdgeDataService,
) -> None: ) -> None:
"""Initialize the sensor.""" """Initialize the sensor."""
super().__init__(data_service.coordinator) super().__init__(data_service.coordinator)
self.platform_name = platform_name self.platform_name = platform_name
self.sensor_key = sensor_key self.sensor_type = sensor_type
self.data_service = data_service self.data_service = data_service
self._attr_unit_of_measurement = SENSOR_TYPES[sensor_key][2] self._attr_device_class = sensor_type.device_class
self._attr_name = f"{platform_name} ({SENSOR_TYPES[sensor_key][1]})" self._attr_entity_registry_enabled_default = (
self._attr_icon = SENSOR_TYPES[sensor_key][3] sensor_type.entity_registry_enabled_default
)
self._attr_icon = sensor_type.icon
self._attr_last_reset = sensor_type.last_reset
self._attr_name = f"{platform_name} ({sensor_type.name})"
self._attr_state_class = sensor_type.state_class
self._attr_unit_of_measurement = sensor_type.unit_of_measurement
class SolarEdgeOverviewSensor(SolarEdgeSensor): class SolarEdgeOverviewSensor(SolarEdgeSensorEntity):
"""Representation of an SolarEdge Monitoring API overview sensor.""" """Representation of an SolarEdge Monitoring API overview sensor."""
def __init__(
self, platform_name: str, sensor_key: str, data_service: SolarEdgeDataService
) -> None:
"""Initialize the overview sensor."""
super().__init__(platform_name, sensor_key, data_service)
self._json_key = SENSOR_TYPES[self.sensor_key][0]
@property @property
def state(self) -> str | None: def state(self) -> str | None:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self.data_service.data.get(self._json_key) return self.data_service.data.get(self.sensor_type.json_key)
class SolarEdgeDetailsSensor(SolarEdgeSensor): class SolarEdgeDetailsSensor(SolarEdgeSensorEntity):
"""Representation of an SolarEdge Monitoring API details sensor.""" """Representation of an SolarEdge Monitoring API details sensor."""
@property @property
@ -153,89 +155,76 @@ class SolarEdgeDetailsSensor(SolarEdgeSensor):
return self.data_service.data return self.data_service.data
class SolarEdgeInventorySensor(SolarEdgeSensor): class SolarEdgeInventorySensor(SolarEdgeSensorEntity):
"""Representation of an SolarEdge Monitoring API inventory sensor.""" """Representation of an SolarEdge Monitoring API inventory sensor."""
def __init__(self, platform_name, sensor_key, data_service):
"""Initialize the inventory sensor."""
super().__init__(platform_name, sensor_key, data_service)
self._json_key = SENSOR_TYPES[self.sensor_key][0]
@property @property
def extra_state_attributes(self) -> dict[str, Any]: def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes.""" """Return the state attributes."""
return self.data_service.attributes.get(self._json_key) return self.data_service.attributes.get(self.sensor_type.json_key)
@property @property
def state(self) -> str | None: def state(self) -> str | None:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self.data_service.data.get(self._json_key) return self.data_service.data.get(self.sensor_type.json_key)
class SolarEdgeEnergyDetailsSensor(SolarEdgeSensor): class SolarEdgeEnergyDetailsSensor(SolarEdgeSensorEntity):
"""Representation of an SolarEdge Monitoring API power flow sensor.""" """Representation of an SolarEdge Monitoring API power flow sensor."""
def __init__(self, platform_name, sensor_key, data_service): def __init__(self, platform_name, sensor_type, data_service):
"""Initialize the power flow sensor.""" """Initialize the power flow sensor."""
super().__init__(platform_name, sensor_key, data_service) super().__init__(platform_name, sensor_type, data_service)
self._json_key = SENSOR_TYPES[self.sensor_key][0]
self._attr_unit_of_measurement = data_service.unit self._attr_unit_of_measurement = data_service.unit
@property @property
def extra_state_attributes(self) -> dict[str, Any]: def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes.""" """Return the state attributes."""
return self.data_service.attributes.get(self._json_key) return self.data_service.attributes.get(self.sensor_type.json_key)
@property @property
def state(self) -> str | None: def state(self) -> str | None:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self.data_service.data.get(self._json_key) return self.data_service.data.get(self.sensor_type.json_key)
class SolarEdgePowerFlowSensor(SolarEdgeSensor): class SolarEdgePowerFlowSensor(SolarEdgeSensorEntity):
"""Representation of an SolarEdge Monitoring API power flow sensor.""" """Representation of an SolarEdge Monitoring API power flow sensor."""
_attr_device_class = DEVICE_CLASS_POWER _attr_device_class = DEVICE_CLASS_POWER
def __init__( def __init__(
self, platform_name: str, sensor_key: str, data_service: SolarEdgeDataService self,
platform_name: str,
sensor_type: SolarEdgeSensor,
data_service: SolarEdgeDataService,
) -> None: ) -> None:
"""Initialize the power flow sensor.""" """Initialize the power flow sensor."""
super().__init__(platform_name, sensor_key, data_service) super().__init__(platform_name, sensor_type, data_service)
self._json_key = SENSOR_TYPES[self.sensor_key][0]
self._attr_unit_of_measurement = data_service.unit self._attr_unit_of_measurement = data_service.unit
@property @property
def extra_state_attributes(self) -> dict[str, Any]: def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes.""" """Return the state attributes."""
return self.data_service.attributes.get(self._json_key) return self.data_service.attributes.get(self.sensor_type.json_key)
@property @property
def state(self) -> str | None: def state(self) -> str | None:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self.data_service.data.get(self._json_key) return self.data_service.data.get(self.sensor_type.json_key)
class SolarEdgeStorageLevelSensor(SolarEdgeSensor): class SolarEdgeStorageLevelSensor(SolarEdgeSensorEntity):
"""Representation of an SolarEdge Monitoring API storage level sensor.""" """Representation of an SolarEdge Monitoring API storage level sensor."""
_attr_device_class = DEVICE_CLASS_BATTERY _attr_device_class = DEVICE_CLASS_BATTERY
def __init__(
self, platform_name: str, sensor_key: str, data_service: SolarEdgeDataService
) -> None:
"""Initialize the storage level sensor."""
super().__init__(platform_name, sensor_key, data_service)
self._json_key = SENSOR_TYPES[self.sensor_key][0]
@property @property
def state(self) -> str | None: def state(self) -> str | None:
"""Return the state of the sensor.""" """Return the state of the sensor."""
attr = self.data_service.attributes.get(self._json_key) attr = self.data_service.attributes.get(self.sensor_type.json_key)
if attr and "soc" in attr: if attr and "soc" in attr:
return attr["soc"] return attr["soc"]
return None return None