mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Add each fronius sensor as own template (#25608)
* Fix formatting in history test * Add each sensor as own template * Make adapters non-entities * Externalize and organize data fetching, improve system view * Small fixes Rename fetching adapters to adapters throw away non-working system overviews slightly change naming remove scan_interval from schema formatting * Scan interval is already timedelta and unnecessary return * Formatting * Ensure better codestyle by storing cell variables explicitely in different places
This commit is contained in:
parent
35400b0db1
commit
87bc2134ad
@ -1,5 +1,6 @@
|
||||
"""Support for Fronius devices."""
|
||||
import copy
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
import voluptuous as vol
|
||||
|
||||
@ -11,10 +12,13 @@ from homeassistant.const import (
|
||||
CONF_SENSOR_TYPE,
|
||||
CONF_DEVICE,
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
CONF_SCAN_INTERVAL,
|
||||
)
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import async_track_time_interval
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -30,6 +34,7 @@ SCOPE_SYSTEM = "system"
|
||||
DEFAULT_SCOPE = SCOPE_DEVICE
|
||||
DEFAULT_DEVICE = 0
|
||||
DEFAULT_INVERTER = 1
|
||||
DEFAULT_SCAN_INTERVAL = timedelta(seconds=60)
|
||||
|
||||
SENSOR_TYPES = [TYPE_INVERTER, TYPE_STORAGE, TYPE_METER, TYPE_POWER_FLOW]
|
||||
SCOPE_TYPES = [SCOPE_DEVICE, SCOPE_SYSTEM]
|
||||
@ -78,47 +83,64 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||
session = async_get_clientsession(hass)
|
||||
fronius = Fronius(session, config[CONF_RESOURCE])
|
||||
|
||||
sensors = []
|
||||
scan_interval = config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
|
||||
adapters = []
|
||||
# Creates all adapters for monitored conditions
|
||||
for condition in config[CONF_MONITORED_CONDITIONS]:
|
||||
|
||||
device = condition[CONF_DEVICE]
|
||||
name = "Fronius {} {} {}".format(
|
||||
condition[CONF_SENSOR_TYPE].replace("_", " ").capitalize(),
|
||||
device,
|
||||
config[CONF_RESOURCE],
|
||||
)
|
||||
sensor_type = condition[CONF_SENSOR_TYPE]
|
||||
scope = condition[CONF_SCOPE]
|
||||
name = "Fronius {} {} {}".format(
|
||||
condition[CONF_SENSOR_TYPE].replace("_", " ").capitalize(),
|
||||
device if scope == SCOPE_DEVICE else SCOPE_SYSTEM,
|
||||
config[CONF_RESOURCE],
|
||||
)
|
||||
if sensor_type == TYPE_INVERTER:
|
||||
if scope == SCOPE_SYSTEM:
|
||||
sensor_cls = FroniusInverterSystem
|
||||
adapter_cls = FroniusInverterSystem
|
||||
else:
|
||||
sensor_cls = FroniusInverterDevice
|
||||
adapter_cls = FroniusInverterDevice
|
||||
elif sensor_type == TYPE_METER:
|
||||
if scope == SCOPE_SYSTEM:
|
||||
sensor_cls = FroniusMeterSystem
|
||||
adapter_cls = FroniusMeterSystem
|
||||
else:
|
||||
sensor_cls = FroniusMeterDevice
|
||||
adapter_cls = FroniusMeterDevice
|
||||
elif sensor_type == TYPE_POWER_FLOW:
|
||||
sensor_cls = FroniusPowerFlow
|
||||
adapter_cls = FroniusPowerFlow
|
||||
else:
|
||||
sensor_cls = FroniusStorage
|
||||
adapter_cls = FroniusStorage
|
||||
|
||||
sensors.append(sensor_cls(fronius, name, device))
|
||||
adapters.append(adapter_cls(fronius, name, device, async_add_entities))
|
||||
|
||||
async_add_entities(sensors, True)
|
||||
# Creates a lamdba that fetches an update when called
|
||||
def adapter_data_fetcher(data_adapter):
|
||||
async def fetch_data(*_):
|
||||
await data_adapter.async_update()
|
||||
|
||||
return fetch_data
|
||||
|
||||
# Set up the fetching in a fixed interval for each adapter
|
||||
for adapter in adapters:
|
||||
fetch = adapter_data_fetcher(adapter)
|
||||
# fetch data once at set-up
|
||||
await fetch()
|
||||
async_track_time_interval(hass, fetch, scan_interval)
|
||||
|
||||
|
||||
class FroniusSensor(Entity):
|
||||
"""The Fronius sensor implementation."""
|
||||
class FroniusAdapter:
|
||||
"""The Fronius sensor fetching component."""
|
||||
|
||||
def __init__(self, data, name, device):
|
||||
def __init__(self, bridge, name, device, add_entities):
|
||||
"""Initialize the sensor."""
|
||||
self.data = data
|
||||
self.bridge = bridge
|
||||
self._name = name
|
||||
self._device = device
|
||||
self._state = None
|
||||
self._attributes = {}
|
||||
self._fetched = {}
|
||||
|
||||
self.sensors = set()
|
||||
self._registered_sensors = set()
|
||||
self._add_entities = add_entities
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -126,14 +148,9 @@ class FroniusSensor(Entity):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the current state."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
def data(self):
|
||||
"""Return the state attributes."""
|
||||
return self._attributes
|
||||
return self._fetched
|
||||
|
||||
async def async_update(self):
|
||||
"""Retrieve and update latest state."""
|
||||
@ -148,62 +165,129 @@ class FroniusSensor(Entity):
|
||||
"Maybe the configured device is not supported"
|
||||
)
|
||||
|
||||
if values:
|
||||
self._state = values["status"]["Code"]
|
||||
attributes = {}
|
||||
for key in values:
|
||||
if "value" in values[key]:
|
||||
attributes[key] = values[key].get("value", 0)
|
||||
self._attributes = attributes
|
||||
if not values:
|
||||
return
|
||||
attributes = self._fetched
|
||||
# Copy data of current fronius device
|
||||
for key, entry in values.items():
|
||||
# If the data is directly a sensor
|
||||
if "value" in entry:
|
||||
attributes[key] = entry
|
||||
self._fetched = attributes
|
||||
|
||||
# Add discovered value fields as sensors
|
||||
# because some fields are only sent temporarily
|
||||
new_sensors = []
|
||||
for key in attributes:
|
||||
if key not in self.sensors:
|
||||
self.sensors.add(key)
|
||||
_LOGGER.info("Discovered %s, adding as sensor", key)
|
||||
new_sensors.append(FroniusTemplateSensor(self, key))
|
||||
self._add_entities(new_sensors, True)
|
||||
|
||||
# Schedule an update for all included sensors
|
||||
for sensor in self._registered_sensors:
|
||||
sensor.async_schedule_update_ha_state(True)
|
||||
|
||||
async def _update(self):
|
||||
"""Return values of interest."""
|
||||
pass
|
||||
|
||||
async def register(self, sensor):
|
||||
"""Register child sensor for update subscriptions."""
|
||||
self._registered_sensors.add(sensor)
|
||||
|
||||
class FroniusInverterSystem(FroniusSensor):
|
||||
"""Sensor for the fronius inverter with system scope."""
|
||||
|
||||
class FroniusInverterSystem(FroniusAdapter):
|
||||
"""Adapter for the fronius inverter with system scope."""
|
||||
|
||||
async def _update(self):
|
||||
"""Get the values for the current state."""
|
||||
return await self.data.current_system_inverter_data()
|
||||
return await self.bridge.current_system_inverter_data()
|
||||
|
||||
|
||||
class FroniusInverterDevice(FroniusSensor):
|
||||
"""Sensor for the fronius inverter with device scope."""
|
||||
class FroniusInverterDevice(FroniusAdapter):
|
||||
"""Adapter for the fronius inverter with device scope."""
|
||||
|
||||
async def _update(self):
|
||||
"""Get the values for the current state."""
|
||||
return await self.data.current_inverter_data(self._device)
|
||||
return await self.bridge.current_inverter_data(self._device)
|
||||
|
||||
|
||||
class FroniusStorage(FroniusSensor):
|
||||
"""Sensor for the fronius battery storage."""
|
||||
class FroniusStorage(FroniusAdapter):
|
||||
"""Adapter for the fronius battery storage."""
|
||||
|
||||
async def _update(self):
|
||||
"""Get the values for the current state."""
|
||||
return await self.data.current_storage_data(self._device)
|
||||
return await self.bridge.current_storage_data(self._device)
|
||||
|
||||
|
||||
class FroniusMeterSystem(FroniusSensor):
|
||||
"""Sensor for the fronius meter with system scope."""
|
||||
class FroniusMeterSystem(FroniusAdapter):
|
||||
"""Adapter for the fronius meter with system scope."""
|
||||
|
||||
async def _update(self):
|
||||
"""Get the values for the current state."""
|
||||
return await self.data.current_system_meter_data()
|
||||
return await self.bridge.current_system_meter_data()
|
||||
|
||||
|
||||
class FroniusMeterDevice(FroniusSensor):
|
||||
"""Sensor for the fronius meter with device scope."""
|
||||
class FroniusMeterDevice(FroniusAdapter):
|
||||
"""Adapter for the fronius meter with device scope."""
|
||||
|
||||
async def _update(self):
|
||||
"""Get the values for the current state."""
|
||||
return await self.data.current_meter_data(self._device)
|
||||
return await self.bridge.current_meter_data(self._device)
|
||||
|
||||
|
||||
class FroniusPowerFlow(FroniusSensor):
|
||||
"""Sensor for the fronius power flow."""
|
||||
class FroniusPowerFlow(FroniusAdapter):
|
||||
"""Adapter for the fronius power flow."""
|
||||
|
||||
async def _update(self):
|
||||
"""Get the values for the current state."""
|
||||
return await self.data.current_power_flow()
|
||||
return await self.bridge.current_power_flow()
|
||||
|
||||
|
||||
class FroniusTemplateSensor(Entity):
|
||||
"""Sensor for the single values (e.g. pv power, ac power)."""
|
||||
|
||||
def __init__(self, parent: FroniusAdapter, name):
|
||||
"""Initialize a singular value sensor."""
|
||||
self._name = name
|
||||
self.parent = parent
|
||||
self._state = None
|
||||
self._unit = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return "{} {}".format(
|
||||
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
|
||||
def should_poll(self):
|
||||
"""Device should not be polled, returns False."""
|
||||
return False
|
||||
|
||||
async def async_update(self):
|
||||
"""Update the internal state."""
|
||||
state = self.parent.data.get(self._name)
|
||||
self._state = state.get("value")
|
||||
self._unit = state.get("unit")
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register at parent component for updates."""
|
||||
await self.parent.register(self)
|
||||
|
||||
def __hash__(self):
|
||||
"""Hash sensor by hashing its name."""
|
||||
return hash(self.name)
|
||||
|
Loading…
x
Reference in New Issue
Block a user