From a93487f389e6377fdbaace890ffc22697c39045e Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Jun 2021 08:37:33 +0200 Subject: [PATCH] Add state class support to SolarEdge (#52271) --- homeassistant/components/solaredge/const.py | 215 +++++++++++++------ homeassistant/components/solaredge/models.py | 21 ++ homeassistant/components/solaredge/sensor.py | 95 ++++---- 3 files changed, 217 insertions(+), 114 deletions(-) create mode 100644 homeassistant/components/solaredge/models.py diff --git a/homeassistant/components/solaredge/const.py b/homeassistant/components/solaredge/const.py index 258eafff304..81d9bc5aebe 100644 --- a/homeassistant/components/solaredge/const.py +++ b/homeassistant/components/solaredge/const.py @@ -2,7 +2,11 @@ from datetime import timedelta import logging +from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT from homeassistant.const import ENERGY_WATT_HOUR, PERCENTAGE, POWER_WATT +from homeassistant.util import dt as dt_util + +from .models import SolarEdgeSensor DOMAIN = "solaredge" @@ -23,64 +27,153 @@ ENERGY_DETAILS_DELAY = timedelta(minutes=15) SCAN_INTERVAL = timedelta(minutes=15) -# Supported overview sensor types: -# Key: ['json_key', 'name', unit, icon, default] -SENSOR_TYPES = { - "lifetime_energy": [ - "lifeTimeData", - "Lifetime energy", - ENERGY_WATT_HOUR, - "mdi:solar-power", - False, - ], - "energy_this_year": [ - "lastYearData", - "Energy this year", - ENERGY_WATT_HOUR, - "mdi:solar-power", - False, - ], - "energy_this_month": [ - "lastMonthData", - "Energy this month", - ENERGY_WATT_HOUR, - "mdi:solar-power", - False, - ], - "energy_today": [ - "lastDayData", - "Energy today", - ENERGY_WATT_HOUR, - "mdi:solar-power", - False, - ], - "current_power": [ - "currentPower", - "Current Power", - POWER_WATT, - "mdi:solar-power", - True, - ], - "site_details": [None, "Site details", None, None, False], - "meters": ["meters", "Meters", None, None, False], - "sensors": ["sensors", "Sensors", None, None, False], - "gateways": ["gateways", "Gateways", None, None, False], - "batteries": ["batteries", "Batteries", None, None, False], - "inverters": ["inverters", "Inverters", None, None, False], - "power_consumption": ["LOAD", "Power Consumption", None, "mdi:flash", False], - "solar_power": ["PV", "Solar Power", None, "mdi:solar-power", False], - "grid_power": ["GRID", "Grid Power", None, "mdi:power-plug", False], - "storage_power": ["STORAGE", "Storage Power", None, "mdi:car-battery", False], - "purchased_power": ["Purchased", "Imported Power", None, "mdi:flash", False], - "production_power": ["Production", "Production Power", None, "mdi:flash", False], - "consumption_power": ["Consumption", "Consumption Power", None, "mdi:flash", False], - "selfconsumption_power": [ - "SelfConsumption", - "SelfConsumption Power", - None, - "mdi:flash", - False, - ], - "feedin_power": ["FeedIn", "Exported Power", None, "mdi:flash", False], - "storage_level": ["STORAGE", "Storage Level", PERCENTAGE, None, False], -} +# Supported overview sensors +SENSOR_TYPES = [ + SolarEdgeSensor( + key="lifetime_energy", + json_key="lifeTimeData", + name="Lifetime energy", + icon="mdi:solar-power", + last_reset=dt_util.utc_from_timestamp(0), + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=ENERGY_WATT_HOUR, + ), + SolarEdgeSensor( + key="energy_this_year", + json_key="lastYearData", + name="Energy this year", + entity_registry_enabled_default=False, + icon="mdi:solar-power", + unit_of_measurement=ENERGY_WATT_HOUR, + ), + SolarEdgeSensor( + key="energy_this_month", + json_key="lastMonthData", + name="Energy this month", + entity_registry_enabled_default=False, + icon="mdi:solar-power", + unit_of_measurement=ENERGY_WATT_HOUR, + ), + SolarEdgeSensor( + key="energy_today", + json_key="lastDayData", + name="Energy today", + entity_registry_enabled_default=False, + icon="mdi:solar-power", + unit_of_measurement=ENERGY_WATT_HOUR, + ), + SolarEdgeSensor( + key="current_power", + json_key="currentPower", + name="Current Power", + icon="mdi:solar-power", + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=POWER_WATT, + ), + SolarEdgeSensor( + key="site_details", + name="Site details", + entity_registry_enabled_default=False, + ), + SolarEdgeSensor( + key="meters", + json_key="meters", + name="Meters", + entity_registry_enabled_default=False, + ), + SolarEdgeSensor( + key="sensors", + json_key="sensors", + name="Sensors", + entity_registry_enabled_default=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, + ), +] diff --git a/homeassistant/components/solaredge/models.py b/homeassistant/components/solaredge/models.py new file mode 100644 index 00000000000..f91db9ee9ff --- /dev/null +++ b/homeassistant/components/solaredge/models.py @@ -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 diff --git a/homeassistant/components/solaredge/sensor.py b/homeassistant/components/solaredge/sensor.py index 5cd644f5006..340b6e0c2c9 100644 --- a/homeassistant/components/solaredge/sensor.py +++ b/homeassistant/components/solaredge/sensor.py @@ -21,6 +21,7 @@ from .coordinator import ( SolarEdgeOverviewDataService, SolarEdgePowerFlowDataService, ) +from .models import SolarEdgeSensor async def async_setup_entry( @@ -40,8 +41,8 @@ async def async_setup_entry( await service.coordinator.async_refresh() entities = [] - for sensor_key in SENSOR_TYPES: - sensor = sensor_factory.create_sensor(sensor_key) + for sensor_type in SENSOR_TYPES: + sensor = sensor_factory.create_sensor(sensor_type) if sensor is not None: entities.append(sensor) async_add_entities(entities) @@ -98,48 +99,49 @@ class SolarEdgeSensorFactory: ]: 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.""" - 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.""" def __init__( - self, platform_name: str, sensor_key: str, data_service: SolarEdgeDataService + self, + platform_name: str, + sensor_type: SolarEdgeSensor, + data_service: SolarEdgeDataService, ) -> None: """Initialize the sensor.""" super().__init__(data_service.coordinator) self.platform_name = platform_name - self.sensor_key = sensor_key + self.sensor_type = sensor_type self.data_service = data_service - self._attr_unit_of_measurement = SENSOR_TYPES[sensor_key][2] - self._attr_name = f"{platform_name} ({SENSOR_TYPES[sensor_key][1]})" - self._attr_icon = SENSOR_TYPES[sensor_key][3] + self._attr_device_class = sensor_type.device_class + self._attr_entity_registry_enabled_default = ( + 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.""" - 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 def state(self) -> str | None: """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.""" @property @@ -153,89 +155,76 @@ class SolarEdgeDetailsSensor(SolarEdgeSensor): return self.data_service.data -class SolarEdgeInventorySensor(SolarEdgeSensor): +class SolarEdgeInventorySensor(SolarEdgeSensorEntity): """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 def extra_state_attributes(self) -> dict[str, Any]: """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 def state(self) -> str | None: """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.""" - def __init__(self, platform_name, sensor_key, data_service): + def __init__(self, platform_name, sensor_type, data_service): """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 @property def extra_state_attributes(self) -> dict[str, Any]: """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 def state(self) -> str | None: """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.""" _attr_device_class = DEVICE_CLASS_POWER def __init__( - self, platform_name: str, sensor_key: str, data_service: SolarEdgeDataService + self, + platform_name: str, + sensor_type: SolarEdgeSensor, + data_service: SolarEdgeDataService, ) -> None: """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 @property def extra_state_attributes(self) -> dict[str, Any]: """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 def state(self) -> str | None: """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.""" _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 def state(self) -> str | None: """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: return attr["soc"] return None