Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix SolarEdge statistics; missing device_class #53720

Merged
merged 1 commit into from
Jul 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 34 additions & 23 deletions homeassistant/components/solaredge/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
import logging

from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT
from homeassistant.const import ENERGY_WATT_HOUR, PERCENTAGE, POWER_WATT
from homeassistant.const import (
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
ENERGY_WATT_HOUR,
PERCENTAGE,
POWER_WATT,
)
from homeassistant.util import dt as dt_util

from .models import SolarEdgeSensor
from .models import SolarEdgeSensorEntityDescription

DOMAIN = "solaredge"

Expand All @@ -29,146 +35,151 @@

# Supported overview sensors
SENSOR_TYPES = [
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
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,
device_class=DEVICE_CLASS_ENERGY,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
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,
device_class=DEVICE_CLASS_ENERGY,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
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,
device_class=DEVICE_CLASS_ENERGY,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="energy_today",
json_key="lastDayData",
name="Energy today",
entity_registry_enabled_default=False,
icon="mdi:solar-power",
unit_of_measurement=ENERGY_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="current_power",
json_key="currentPower",
name="Current Power",
icon="mdi:solar-power",
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=POWER_WATT,
device_class=DEVICE_CLASS_POWER,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="site_details",
name="Site details",
entity_registry_enabled_default=False,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="meters",
json_key="meters",
name="Meters",
entity_registry_enabled_default=False,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="sensors",
json_key="sensors",
name="Sensors",
entity_registry_enabled_default=False,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="gateways",
json_key="gateways",
name="Gateways",
entity_registry_enabled_default=False,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="batteries",
json_key="batteries",
name="Batteries",
entity_registry_enabled_default=False,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="inverters",
json_key="inverters",
name="Inverters",
entity_registry_enabled_default=False,
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="power_consumption",
json_key="LOAD",
name="Power Consumption",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="solar_power",
json_key="PV",
name="Solar Power",
entity_registry_enabled_default=False,
icon="mdi:solar-power",
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="grid_power",
json_key="GRID",
name="Grid Power",
entity_registry_enabled_default=False,
icon="mdi:power-plug",
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="storage_power",
json_key="STORAGE",
name="Storage Power",
entity_registry_enabled_default=False,
icon="mdi:car-battery",
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="purchased_power",
json_key="Purchased",
name="Imported Power",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="production_power",
json_key="Production",
name="Production Power",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="consumption_power",
json_key="Consumption",
name="Consumption Power",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="selfconsumption_power",
json_key="SelfConsumption",
name="SelfConsumption Power",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="feedin_power",
json_key="FeedIn",
name="Exported Power",
entity_registry_enabled_default=False,
icon="mdi:flash",
),
SolarEdgeSensor(
SolarEdgeSensorEntityDescription(
key="storage_level",
json_key="STORAGE",
name="Storage Level",
Expand Down
16 changes: 4 additions & 12 deletions homeassistant/components/solaredge/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@
from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime

from homeassistant.components.sensor import SensorEntityDescription
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the future, I'd try to keep the sensor integration imports in the sensor platform.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? We do these things all the time, e.g., in constants files.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constant files are only for constants that need to be shared between more than one module in the integration. Platform specific names are better to keep within each platform. It's nice to keep the import branching as clean as possible.

Copy link
Member Author

@frenck frenck Jul 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree on that. I'd like to move as much out of the platforms as possible.

And honestly, we do this all the time, for example, look at all the coordinator files.

Putting this in the constant file is ugly too as it is not a constant, but it is defined there by most integrations, which is just as bad. Leaving it in sensor, means sensor is important const, and const is importing sensor; which is circular.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The coordinator module here has clean import branches. It's only the const.py and models.py that have unclean imports since they import from the sensor integration.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry. I'm afraid I'm not able to get on the same page with this.


@dataclass
class SolarEdgeSensor:
"""Represents an SolarEdge Sensor."""

key: str
name: str
@dataclass
class SolarEdgeSensorEntityDescription(SensorEntityDescription):
"""Sensor entity description for SolarEdge."""

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
45 changes: 21 additions & 24 deletions homeassistant/components/solaredge/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
SolarEdgeOverviewDataService,
SolarEdgePowerFlowDataService,
)
from .models import SolarEdgeSensor
from .models import SolarEdgeSensorEntityDescription


async def async_setup_entry(
Expand Down Expand Up @@ -68,7 +68,8 @@ def __init__(
self.services: dict[
str,
tuple[
type[SolarEdgeSensor | SolarEdgeOverviewSensor], SolarEdgeDataService
type[SolarEdgeSensorEntity | SolarEdgeOverviewSensor],
SolarEdgeDataService,
],
] = {"site_details": (SolarEdgeDetailsSensor, details)}

Expand Down Expand Up @@ -99,7 +100,9 @@ def __init__(
):
self.services[key] = (SolarEdgeEnergyDetailsSensor, energy)

def create_sensor(self, sensor_type: SolarEdgeSensor) -> SolarEdgeSensor:
def create_sensor(
self, sensor_type: SolarEdgeSensorEntityDescription
) -> SolarEdgeSensorEntityDescription:
"""Create and return a sensor based on the sensor_key."""
sensor_class, service = self.services[sensor_type.key]

Expand All @@ -109,27 +112,21 @@ def create_sensor(self, sensor_type: SolarEdgeSensor) -> SolarEdgeSensor:
class SolarEdgeSensorEntity(CoordinatorEntity, SensorEntity):
"""Abstract class for a solaredge sensor."""

entity_description: SolarEdgeSensorEntityDescription

def __init__(
self,
platform_name: str,
sensor_type: SolarEdgeSensor,
description: SolarEdgeSensorEntityDescription,
data_service: SolarEdgeDataService,
) -> None:
"""Initialize the sensor."""
super().__init__(data_service.coordinator)
self.platform_name = platform_name
self.sensor_type = sensor_type
self.entity_description = description
self.data_service = data_service

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
self._attr_name = f"{platform_name} ({description.name})"


class SolarEdgeOverviewSensor(SolarEdgeSensorEntity):
Expand All @@ -138,7 +135,7 @@ class SolarEdgeOverviewSensor(SolarEdgeSensorEntity):
@property
def state(self) -> str | None:
"""Return the state of the sensor."""
return self.data_service.data.get(self.sensor_type.json_key)
return self.data_service.data.get(self.entity_description.json_key)


class SolarEdgeDetailsSensor(SolarEdgeSensorEntity):
Expand All @@ -161,12 +158,12 @@ class SolarEdgeInventorySensor(SolarEdgeSensorEntity):
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
return self.data_service.attributes.get(self.sensor_type.json_key)
return self.data_service.attributes.get(self.entity_description.json_key)

@property
def state(self) -> str | None:
"""Return the state of the sensor."""
return self.data_service.data.get(self.sensor_type.json_key)
return self.data_service.data.get(self.entity_description.json_key)


class SolarEdgeEnergyDetailsSensor(SolarEdgeSensorEntity):
Expand All @@ -181,12 +178,12 @@ def __init__(self, platform_name, sensor_type, data_service):
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
return self.data_service.attributes.get(self.sensor_type.json_key)
return self.data_service.attributes.get(self.entity_description.json_key)

@property
def state(self) -> str | None:
"""Return the state of the sensor."""
return self.data_service.data.get(self.sensor_type.json_key)
return self.data_service.data.get(self.entity_description.json_key)


class SolarEdgePowerFlowSensor(SolarEdgeSensorEntity):
Expand All @@ -197,23 +194,23 @@ class SolarEdgePowerFlowSensor(SolarEdgeSensorEntity):
def __init__(
self,
platform_name: str,
sensor_type: SolarEdgeSensor,
description: SolarEdgeSensorEntityDescription,
data_service: SolarEdgeDataService,
) -> None:
"""Initialize the power flow sensor."""
super().__init__(platform_name, sensor_type, data_service)
super().__init__(platform_name, description, data_service)

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.sensor_type.json_key)
return self.data_service.attributes.get(self.entity_description.json_key)

@property
def state(self) -> str | None:
"""Return the state of the sensor."""
return self.data_service.data.get(self.sensor_type.json_key)
return self.data_service.data.get(self.entity_description.json_key)


class SolarEdgeStorageLevelSensor(SolarEdgeSensorEntity):
Expand All @@ -224,7 +221,7 @@ class SolarEdgeStorageLevelSensor(SolarEdgeSensorEntity):
@property
def state(self) -> str | None:
"""Return the state of the sensor."""
attr = self.data_service.attributes.get(self.sensor_type.json_key)
attr = self.data_service.attributes.get(self.entity_description.json_key)
if attr and "soc" in attr:
return attr["soc"]
return None