Add energy attributes to Fronius (#53741)
* Add energy attributes to Fronius * Add solar * Add power * Only add last reset for total meter entities * Only add last reset for total solar entities * Create different entity descriptions per key * only return the entity description for energy total * Use correct key * Meter devices keep it real * keys start with energy_real * Also device key starts with * Lint
This commit is contained in:
parent
87dab02ce6
commit
05a7853720
1 changed files with 88 additions and 29 deletions
|
@ -8,17 +8,26 @@ import logging
|
||||||
from pyfronius import Fronius
|
from pyfronius import Fronius
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
from homeassistant.components.sensor import (
|
||||||
|
PLATFORM_SCHEMA,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICE,
|
CONF_DEVICE,
|
||||||
CONF_MONITORED_CONDITIONS,
|
CONF_MONITORED_CONDITIONS,
|
||||||
CONF_RESOURCE,
|
CONF_RESOURCE,
|
||||||
CONF_SCAN_INTERVAL,
|
CONF_SCAN_INTERVAL,
|
||||||
CONF_SENSOR_TYPE,
|
CONF_SENSOR_TYPE,
|
||||||
|
DEVICE_CLASS_ENERGY,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
)
|
)
|
||||||
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
|
from homeassistant.util import dt
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -152,6 +161,12 @@ class FroniusAdapter:
|
||||||
"""Whether the fronius device is active."""
|
"""Whether the fronius device is active."""
|
||||||
return self._available
|
return self._available
|
||||||
|
|
||||||
|
def entity_description( # pylint: disable=no-self-use
|
||||||
|
self, key
|
||||||
|
) -> SensorEntityDescription | None:
|
||||||
|
"""Create entity description for a key."""
|
||||||
|
return None
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Retrieve and update latest state."""
|
"""Retrieve and update latest state."""
|
||||||
try:
|
try:
|
||||||
|
@ -198,14 +213,28 @@ class FroniusAdapter:
|
||||||
async def _update(self) -> dict:
|
async def _update(self) -> dict:
|
||||||
"""Return values of interest."""
|
"""Return values of interest."""
|
||||||
|
|
||||||
async def register(self, sensor):
|
@callback
|
||||||
|
def register(self, sensor):
|
||||||
"""Register child sensor for update subscriptions."""
|
"""Register child sensor for update subscriptions."""
|
||||||
self._registered_sensors.add(sensor)
|
self._registered_sensors.add(sensor)
|
||||||
|
return lambda: self._registered_sensors.remove(sensor)
|
||||||
|
|
||||||
|
|
||||||
class FroniusInverterSystem(FroniusAdapter):
|
class FroniusInverterSystem(FroniusAdapter):
|
||||||
"""Adapter for the fronius inverter with system scope."""
|
"""Adapter for the fronius inverter with system scope."""
|
||||||
|
|
||||||
|
def entity_description(self, key):
|
||||||
|
"""Return the entity descriptor."""
|
||||||
|
if key != "energy_total":
|
||||||
|
return None
|
||||||
|
|
||||||
|
return SensorEntityDescription(
|
||||||
|
key=key,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
last_reset=dt.utc_from_timestamp(0),
|
||||||
|
)
|
||||||
|
|
||||||
async def _update(self):
|
async def _update(self):
|
||||||
"""Get the values for the current state."""
|
"""Get the values for the current state."""
|
||||||
return await self.bridge.current_system_inverter_data()
|
return await self.bridge.current_system_inverter_data()
|
||||||
|
@ -214,6 +243,18 @@ class FroniusInverterSystem(FroniusAdapter):
|
||||||
class FroniusInverterDevice(FroniusAdapter):
|
class FroniusInverterDevice(FroniusAdapter):
|
||||||
"""Adapter for the fronius inverter with device scope."""
|
"""Adapter for the fronius inverter with device scope."""
|
||||||
|
|
||||||
|
def entity_description(self, key):
|
||||||
|
"""Return the entity descriptor."""
|
||||||
|
if key != "energy_total":
|
||||||
|
return None
|
||||||
|
|
||||||
|
return SensorEntityDescription(
|
||||||
|
key=key,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
last_reset=dt.utc_from_timestamp(0),
|
||||||
|
)
|
||||||
|
|
||||||
async def _update(self):
|
async def _update(self):
|
||||||
"""Get the values for the current state."""
|
"""Get the values for the current state."""
|
||||||
return await self.bridge.current_inverter_data(self._device)
|
return await self.bridge.current_inverter_data(self._device)
|
||||||
|
@ -230,6 +271,18 @@ class FroniusStorage(FroniusAdapter):
|
||||||
class FroniusMeterSystem(FroniusAdapter):
|
class FroniusMeterSystem(FroniusAdapter):
|
||||||
"""Adapter for the fronius meter with system scope."""
|
"""Adapter for the fronius meter with system scope."""
|
||||||
|
|
||||||
|
def entity_description(self, key):
|
||||||
|
"""Return the entity descriptor."""
|
||||||
|
if not key.startswith("energy_real_"):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return SensorEntityDescription(
|
||||||
|
key=key,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
last_reset=dt.utc_from_timestamp(0),
|
||||||
|
)
|
||||||
|
|
||||||
async def _update(self):
|
async def _update(self):
|
||||||
"""Get the values for the current state."""
|
"""Get the values for the current state."""
|
||||||
return await self.bridge.current_system_meter_data()
|
return await self.bridge.current_system_meter_data()
|
||||||
|
@ -238,6 +291,18 @@ class FroniusMeterSystem(FroniusAdapter):
|
||||||
class FroniusMeterDevice(FroniusAdapter):
|
class FroniusMeterDevice(FroniusAdapter):
|
||||||
"""Adapter for the fronius meter with device scope."""
|
"""Adapter for the fronius meter with device scope."""
|
||||||
|
|
||||||
|
def entity_description(self, key):
|
||||||
|
"""Return the entity descriptor."""
|
||||||
|
if not key.startswith("energy_real_"):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return SensorEntityDescription(
|
||||||
|
key=key,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
last_reset=dt.utc_from_timestamp(0),
|
||||||
|
)
|
||||||
|
|
||||||
async def _update(self):
|
async def _update(self):
|
||||||
"""Get the values for the current state."""
|
"""Get the values for the current state."""
|
||||||
return await self.bridge.current_meter_data(self._device)
|
return await self.bridge.current_meter_data(self._device)
|
||||||
|
@ -246,6 +311,14 @@ class FroniusMeterDevice(FroniusAdapter):
|
||||||
class FroniusPowerFlow(FroniusAdapter):
|
class FroniusPowerFlow(FroniusAdapter):
|
||||||
"""Adapter for the fronius power flow."""
|
"""Adapter for the fronius power flow."""
|
||||||
|
|
||||||
|
def entity_description(self, key):
|
||||||
|
"""Return the entity descriptor."""
|
||||||
|
return SensorEntityDescription(
|
||||||
|
key=key,
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
)
|
||||||
|
|
||||||
async def _update(self):
|
async def _update(self):
|
||||||
"""Get the values for the current state."""
|
"""Get the values for the current state."""
|
||||||
return await self.bridge.current_power_flow()
|
return await self.bridge.current_power_flow()
|
||||||
|
@ -254,27 +327,13 @@ class FroniusPowerFlow(FroniusAdapter):
|
||||||
class FroniusTemplateSensor(SensorEntity):
|
class FroniusTemplateSensor(SensorEntity):
|
||||||
"""Sensor for the single values (e.g. pv power, ac power)."""
|
"""Sensor for the single values (e.g. pv power, ac power)."""
|
||||||
|
|
||||||
def __init__(self, parent: FroniusAdapter, name):
|
def __init__(self, parent: FroniusAdapter, key):
|
||||||
"""Initialize a singular value sensor."""
|
"""Initialize a singular value sensor."""
|
||||||
self._name = name
|
self._key = key
|
||||||
self.parent = parent
|
self._attr_name = f"{key.replace('_', ' ').capitalize()} {parent.name}"
|
||||||
self._state = None
|
self._parent = parent
|
||||||
self._unit = None
|
if entity_description := parent.entity_description(key):
|
||||||
|
self.entity_description = entity_description
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return the name of the sensor."""
|
|
||||||
return f"{self._name.replace('_', ' ').capitalize()} {self.parent.name}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self):
|
|
||||||
"""Return the current state."""
|
|
||||||
return self._state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unit_of_measurement(self):
|
|
||||||
"""Return the unit of measurement."""
|
|
||||||
return self._unit
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
|
@ -284,19 +343,19 @@ class FroniusTemplateSensor(SensorEntity):
|
||||||
@property
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
"""Whether the fronius device is active."""
|
"""Whether the fronius device is active."""
|
||||||
return self.parent.available
|
return self._parent.available
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Update the internal state."""
|
"""Update the internal state."""
|
||||||
state = self.parent.data.get(self._name)
|
state = self._parent.data.get(self._key)
|
||||||
self._state = state.get("value")
|
self._attr_state = state.get("value")
|
||||||
if isinstance(self._state, float):
|
if isinstance(self._attr_state, float):
|
||||||
self._state = round(self._state, 2)
|
self._attr_state = round(self._attr_state, 2)
|
||||||
self._unit = state.get("unit")
|
self._attr_unit_of_measurement = state.get("unit")
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Register at parent component for updates."""
|
"""Register at parent component for updates."""
|
||||||
await self.parent.register(self)
|
self.async_on_remove(self._parent.register(self))
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
"""Hash sensor by hashing its name."""
|
"""Hash sensor by hashing its name."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue