Add coordinator to emoncms (#120008)

This commit is contained in:
Alexandre CUER 2024-06-22 12:41:54 +02:00 committed by GitHub
parent 1a962b415e
commit cb045a794d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 61 additions and 22 deletions

View File

@ -0,0 +1,31 @@
"""DataUpdateCoordinator for the emoncms integration."""
from datetime import timedelta
import logging
from typing import Any
from pyemoncms import EmoncmsClient
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
class EmoncmsCoordinator(DataUpdateCoordinator[list[dict[str, Any]] | None]):
"""Emoncms Data Update Coordinator."""
def __init__(
self,
hass: HomeAssistant,
emoncms_client: EmoncmsClient,
scan_interval: timedelta,
) -> None:
"""Initialize the emoncms data coordinator."""
super().__init__(
hass,
_LOGGER,
name="emoncms_coordinator",
update_method=emoncms_client.async_list_feeds,
update_interval=scan_interval,
)

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
from datetime import timedelta
import logging import logging
from typing import Any from typing import Any
@ -17,18 +18,22 @@ from homeassistant.components.sensor import (
from homeassistant.const import ( from homeassistant.const import (
CONF_API_KEY, CONF_API_KEY,
CONF_ID, CONF_ID,
CONF_SCAN_INTERVAL,
CONF_UNIT_OF_MEASUREMENT, CONF_UNIT_OF_MEASUREMENT,
CONF_URL, CONF_URL,
CONF_VALUE_TEMPLATE, CONF_VALUE_TEMPLATE,
STATE_UNKNOWN, STATE_UNKNOWN,
UnitOfPower, UnitOfPower,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import template from homeassistant.helpers import template
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.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .coordinator import EmoncmsCoordinator
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -84,19 +89,21 @@ async def async_setup_platform(
exclude_feeds = config.get(CONF_EXCLUDE_FEEDID) exclude_feeds = config.get(CONF_EXCLUDE_FEEDID)
include_only_feeds = config.get(CONF_ONLY_INCLUDE_FEEDID) include_only_feeds = config.get(CONF_ONLY_INCLUDE_FEEDID)
sensor_names = config.get(CONF_SENSOR_NAMES) sensor_names = config.get(CONF_SENSOR_NAMES)
scan_interval = config.get(CONF_SCAN_INTERVAL, timedelta(seconds=30))
if value_template is not None: if value_template is not None:
value_template.hass = hass value_template.hass = hass
emoncms_client = EmoncmsClient(url, apikey, session=async_get_clientsession(hass)) emoncms_client = EmoncmsClient(url, apikey, session=async_get_clientsession(hass))
elems = await emoncms_client.async_list_feeds() coordinator = EmoncmsCoordinator(hass, emoncms_client, scan_interval)
await coordinator.async_refresh()
elems = coordinator.data
if elems is None: if elems is None:
return return
sensors = [] sensors: list[EmonCmsSensor] = []
for elem in elems: for idx, elem in enumerate(elems):
if exclude_feeds is not None and int(elem["id"]) in exclude_feeds: if exclude_feeds is not None and int(elem["id"]) in exclude_feeds:
continue continue
@ -114,48 +121,48 @@ async def async_setup_platform(
sensors.append( sensors.append(
EmonCmsSensor( EmonCmsSensor(
hass, coordinator,
emoncms_client,
name, name,
value_template, value_template,
unit_of_measurement, unit_of_measurement,
str(sensorid), str(sensorid),
elem, idx,
) )
) )
async_add_entities(sensors) async_add_entities(sensors)
class EmonCmsSensor(SensorEntity): class EmonCmsSensor(CoordinatorEntity[EmoncmsCoordinator], SensorEntity):
"""Implementation of an Emoncms sensor.""" """Implementation of an Emoncms sensor."""
def __init__( def __init__(
self, self,
hass: HomeAssistant, coordinator: EmoncmsCoordinator,
emoncms_client: EmoncmsClient,
name: str | None, name: str | None,
value_template: template.Template | None, value_template: template.Template | None,
unit_of_measurement: str | None, unit_of_measurement: str | None,
sensorid: str, sensorid: str,
elem: dict[str, Any], idx: int,
) -> None: ) -> None:
"""Initialize the sensor.""" """Initialize the sensor."""
super().__init__(coordinator)
self.idx = idx
elem = {}
if self.coordinator.data:
elem = self.coordinator.data[self.idx]
if name is None: if name is None:
# Suppress ID in sensor name if it's 1, since most people won't # Suppress ID in sensor name if it's 1, since most people won't
# have more than one EmonCMS source and it's redundant to show the # have more than one EmonCMS source and it's redundant to show the
# ID if there's only one. # ID if there's only one.
id_for_name = "" if str(sensorid) == "1" else sensorid id_for_name = "" if str(sensorid) == "1" else sensorid
# Use the feed name assigned in EmonCMS or fall back to the feed ID # Use the feed name assigned in EmonCMS or fall back to the feed ID
feed_name = elem.get("name") or f"Feed {elem['id']}" feed_name = elem.get("name", f"Feed {elem.get('id')}")
self._attr_name = f"EmonCMS{id_for_name} {feed_name}" self._attr_name = f"EmonCMS{id_for_name} {feed_name}"
else: else:
self._attr_name = name self._attr_name = name
self._hass = hass
self._emoncms_client = emoncms_client
self._value_template = value_template self._value_template = value_template
self._attr_native_unit_of_measurement = unit_of_measurement self._attr_native_unit_of_measurement = unit_of_measurement
self._sensorid = sensorid self._sensorid = sensorid
self._feed_id = elem["id"]
if unit_of_measurement in ("kWh", "Wh"): if unit_of_measurement in ("kWh", "Wh"):
self._attr_device_class = SensorDeviceClass.ENERGY self._attr_device_class = SensorDeviceClass.ENERGY
@ -208,9 +215,10 @@ class EmonCmsSensor(SensorEntity):
elif elem["value"] is not None: elif elem["value"] is not None:
self._attr_native_value = round(float(elem["value"]), DECIMALS) self._attr_native_value = round(float(elem["value"]), DECIMALS)
async def async_update(self) -> None: @callback
"""Get the latest data and updates the state.""" def _handle_coordinator_update(self) -> None:
elem = await self._emoncms_client.async_get_feed_fields(self._feed_id) """Handle updated data from the coordinator."""
if elem is None: data = self.coordinator.data
return if data:
self._update_attributes(elem) self._update_attributes(data[self.idx])
super()._handle_coordinator_update()