Convert solax to use DataUpdateCoordinator (#117767)

This commit is contained in:
J. Nick Koston 2024-05-20 21:44:10 -10:00 committed by GitHub
parent bb758bcb26
commit c1b4c977e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 76 additions and 76 deletions

View File

@ -1,18 +1,39 @@
"""The solax component.""" """The solax component."""
from solax import real_time_api from dataclasses import dataclass
from datetime import timedelta
import logging
from solax import InverterResponse, RealTimeAPI, real_time_api
from solax.inverter import InverterError
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, Platform from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import UpdateFailed
from .const import DOMAIN from .coordinator import SolaxDataUpdateCoordinator
PLATFORMS = [Platform.SENSOR] PLATFORMS = [Platform.SENSOR]
SCAN_INTERVAL = timedelta(seconds=30)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@dataclass(slots=True)
class SolaxData:
"""Class for storing solax data."""
api: RealTimeAPI
coordinator: SolaxDataUpdateCoordinator
type SolaxConfigEntry = ConfigEntry[SolaxData]
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: SolaxConfigEntry) -> bool:
"""Set up the sensors from a ConfigEntry.""" """Set up the sensors from a ConfigEntry."""
try: try:
@ -21,19 +42,30 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
entry.data[CONF_PORT], entry.data[CONF_PORT],
entry.data[CONF_PASSWORD], entry.data[CONF_PASSWORD],
) )
await api.get_data()
except Exception as err: except Exception as err:
raise ConfigEntryNotReady from err raise ConfigEntryNotReady from err
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = api async def _async_update() -> InverterResponse:
try:
return await api.get_data()
except InverterError as err:
raise UpdateFailed from err
coordinator = SolaxDataUpdateCoordinator(
hass,
logger=_LOGGER,
name=f"solax {entry.title}",
update_interval=SCAN_INTERVAL,
update_method=_async_update,
)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = SolaxData(api=api, coordinator=coordinator)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: SolaxConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@ -0,0 +1,9 @@
"""Constants for the solax integration."""
from solax import InverterResponse
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
class SolaxDataUpdateCoordinator(DataUpdateCoordinator[InverterResponse]):
"""DataUpdateCoordinator for solax."""

View File

@ -2,11 +2,6 @@
from __future__ import annotations from __future__ import annotations
import asyncio
from datetime import timedelta
from solax import RealTimeAPI
from solax.inverter import InverterError
from solax.units import Units from solax.units import Units
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
@ -15,7 +10,6 @@ from homeassistant.components.sensor import (
SensorEntityDescription, SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
PERCENTAGE, PERCENTAGE,
UnitOfElectricCurrent, UnitOfElectricCurrent,
@ -26,15 +20,15 @@ from homeassistant.const import (
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import SolaxConfigEntry
from .const import DOMAIN, MANUFACTURER from .const import DOMAIN, MANUFACTURER
from .coordinator import SolaxDataUpdateCoordinator
DEFAULT_PORT = 80 DEFAULT_PORT = 80
SCAN_INTERVAL = timedelta(seconds=30)
SENSOR_DESCRIPTIONS: dict[tuple[Units, bool], SensorEntityDescription] = { SENSOR_DESCRIPTIONS: dict[tuple[Units, bool], SensorEntityDescription] = {
@ -94,28 +88,23 @@ SENSOR_DESCRIPTIONS: dict[tuple[Units, bool], SensorEntityDescription] = {
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: SolaxConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Entry setup.""" """Entry setup."""
api: RealTimeAPI = hass.data[DOMAIN][entry.entry_id] api = entry.runtime_data.api
resp = await api.get_data() coordinator = entry.runtime_data.coordinator
resp = coordinator.data
serial = resp.serial_number serial = resp.serial_number
version = resp.version version = resp.version
endpoint = RealTimeDataEndpoint(hass, api) entities: list[InverterSensorEntity] = []
entry.async_create_background_task(
hass, endpoint.async_refresh(), f"solax {entry.title} initial refresh"
)
entry.async_on_unload(
async_track_time_interval(hass, endpoint.async_refresh, SCAN_INTERVAL)
)
devices = []
for sensor, (idx, measurement) in api.inverter.sensor_map().items(): for sensor, (idx, measurement) in api.inverter.sensor_map().items():
description = SENSOR_DESCRIPTIONS[(measurement.unit, measurement.is_monotonic)] description = SENSOR_DESCRIPTIONS[(measurement.unit, measurement.is_monotonic)]
uid = f"{serial}-{idx}" uid = f"{serial}-{idx}"
devices.append( entities.append(
Inverter( InverterSensorEntity(
coordinator,
api.inverter.manufacturer, api.inverter.manufacturer,
uid, uid,
serial, serial,
@ -126,57 +115,28 @@ async def async_setup_entry(
description.device_class, description.device_class,
) )
) )
endpoint.sensors = devices async_add_entities(entities)
async_add_entities(devices)
class RealTimeDataEndpoint: class InverterSensorEntity(CoordinatorEntity, SensorEntity):
"""Representation of a Sensor."""
def __init__(self, hass: HomeAssistant, api: RealTimeAPI) -> None:
"""Initialize the sensor."""
self.hass = hass
self.api = api
self.ready = asyncio.Event()
self.sensors: list[Inverter] = []
async def async_refresh(self, now=None):
"""Fetch new state data for the sensor.
This is the only method that should fetch new data for Home Assistant.
"""
try:
api_response = await self.api.get_data()
self.ready.set()
except InverterError as err:
if now is not None:
self.ready.clear()
return
raise PlatformNotReady from err
data = api_response.data
for sensor in self.sensors:
if sensor.key in data:
sensor.value = data[sensor.key]
sensor.async_schedule_update_ha_state()
class Inverter(SensorEntity):
"""Class for a sensor.""" """Class for a sensor."""
_attr_should_poll = False _attr_should_poll = False
def __init__( def __init__(
self, self,
manufacturer, coordinator: SolaxDataUpdateCoordinator,
uid, manufacturer: str,
serial, uid: str,
version, serial: str,
key, version: str,
unit, key: str,
state_class=None, unit: str | None,
device_class=None, state_class: SensorStateClass | str | None,
): device_class: SensorDeviceClass | None,
) -> None:
"""Initialize an inverter sensor.""" """Initialize an inverter sensor."""
super().__init__(coordinator)
self._attr_unique_id = uid self._attr_unique_id = uid
self._attr_name = f"{manufacturer} {serial} {key}" self._attr_name = f"{manufacturer} {serial} {key}"
self._attr_native_unit_of_measurement = unit self._attr_native_unit_of_measurement = unit
@ -189,9 +149,8 @@ class Inverter(SensorEntity):
sw_version=version, sw_version=version,
) )
self.key = key self.key = key
self.value = None
@property @property
def native_value(self): def native_value(self):
"""State of this inverter attribute.""" """State of this inverter attribute."""
return self.value return self.coordinator.data.data[self.key]