| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- """The Solar of Things integration."""
- from __future__ import annotations
- import logging
- from datetime import timedelta
- from homeassistant.config_entries import ConfigEntry
- from homeassistant.const import Platform
- from homeassistant.core import HomeAssistant
- from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
- from .api import SolarOfThingsAPI
- from .const import DOMAIN, CONF_DEVICE_ID
- _LOGGER = logging.getLogger(__name__)
- PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.NUMBER, Platform.SELECT, Platform.SWITCH]
- DEVICE_UPDATE_INTERVAL = timedelta(minutes=5)
- STATION_UPDATE_INTERVAL = timedelta(minutes=30)
- async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
- """Set up Solar of Things from a config entry."""
- hass.data.setdefault(DOMAIN, {})
- api = SolarOfThingsAPI(
- iot_token=entry.data["iot_token"],
- time_zone=entry.data.get("time_zone") or entry.options.get("time_zone"),
- )
- station_id = entry.data["station_id"]
- configured_device_id = (entry.data.get(CONF_DEVICE_ID) or "").strip()
- # Station coordinator (device list + monthly)
- station_coordinator = SolarOfThingsStationCoordinator(
- hass=hass,
- api=api,
- station_id=station_id,
- )
- await station_coordinator.async_config_entry_first_refresh()
- # Create one coordinator per device
- devices = station_coordinator.data.get("devices", []) if station_coordinator.data else []
- # If the user configured a specific deviceId, only create entities for that device.
- # (We still keep station-level data like monthly summary.)
- if configured_device_id:
- filtered = [d for d in devices if str(d.get("id")) == configured_device_id]
- if filtered:
- devices = filtered
- else:
- # Fallback: allow the integration to work even if the device isn't returned
- # by the station device list (permissions / pagination / API quirks).
- devices = [{"id": configured_device_id, "name": configured_device_id}]
- device_coordinators: dict[str, SolarOfThingsDeviceCoordinator] = {}
- for dev in devices:
- device_id = str(dev.get("id") or "")
- if not device_id:
- continue
- c = SolarOfThingsDeviceCoordinator(
- hass=hass,
- api=api,
- station_id=station_id,
- device=device_id,
- device_meta=dev,
- )
- await c.async_config_entry_first_refresh()
- device_coordinators[device_id] = c
- hass.data[DOMAIN][entry.entry_id] = {
- "api": api,
- "station_id": station_id,
- "station_coordinator": station_coordinator,
- "device_coordinators": device_coordinators,
- "devices": devices,
- }
- await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
- return True
- async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
- """Unload a config entry."""
- if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
- hass.data[DOMAIN].pop(entry.entry_id, None)
- return unload_ok
- class SolarOfThingsStationCoordinator(DataUpdateCoordinator):
- """Fetch station-level data (device list + monthly stats)."""
- def __init__(self, hass: HomeAssistant, api: SolarOfThingsAPI, station_id: str) -> None:
- self.api = api
- self.station_id = station_id
- super().__init__(
- hass,
- _LOGGER,
- name=f"{DOMAIN}_station_{station_id}",
- update_interval=STATION_UPDATE_INTERVAL,
- )
- async def _async_update_data(self):
- try:
- devices = await self.hass.async_add_executor_job(self.api.list_devices, self.station_id)
- monthly = await self.hass.async_add_executor_job(self.api.fetch_monthly_summary, self.station_id)
- return {"devices": devices, "monthly": monthly}
- except Exception as err:
- raise UpdateFailed(f"Station update failed: {err}")
- class SolarOfThingsDeviceCoordinator(DataUpdateCoordinator):
- """Fetch device-level telemetry + settings."""
- def __init__(
- self,
- hass: HomeAssistant,
- api: SolarOfThingsAPI,
- station_id: str,
- device: str,
- device_meta: dict,
- ) -> None:
- self.api = api
- self.station_id = station_id
- self.device_id = device
- self.device_meta = device_meta
- super().__init__(
- hass,
- _LOGGER,
- name=f"{DOMAIN}_device_{device}",
- update_interval=DEVICE_UPDATE_INTERVAL,
- )
- async def _async_update_data(self):
- try:
- time_series = await self.hass.async_add_executor_job(self.api.fetch_latest_data, self.device_id)
- settings = await self.hass.async_add_executor_job(self.api.fetch_settings, self.device_id)
- return {
- "time_series": time_series,
- "settings": settings,
- "device": self.device_id,
- "station_id": self.station_id,
- "device_meta": self.device_meta,
- }
- except Exception as err:
- raise UpdateFailed(f"Device update failed for {self.device_id}: {err}")
|