mirror of
https://github.com/home-assistant/core.git
synced 2025-04-27 18:57:57 +00:00

0.3.2 was succeeded by 0.3.4. 0.3.3 was yanked 0.3.4 was succeeded by 3.0.5. 3.0.5 is succeeded by 3.1.0.
198 lines
6.1 KiB
Python
198 lines
6.1 KiB
Python
"""Support for Solax inverter via local API."""
|
|
|
|
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 homeassistant.components.sensor import (
|
|
SensorDeviceClass,
|
|
SensorEntity,
|
|
SensorEntityDescription,
|
|
SensorStateClass,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.const import (
|
|
PERCENTAGE,
|
|
UnitOfElectricCurrent,
|
|
UnitOfElectricPotential,
|
|
UnitOfEnergy,
|
|
UnitOfFrequency,
|
|
UnitOfPower,
|
|
UnitOfTemperature,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import PlatformNotReady
|
|
from homeassistant.helpers.device_registry import DeviceInfo
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.helpers.event import async_track_time_interval
|
|
|
|
from .const import DOMAIN, MANUFACTURER
|
|
|
|
DEFAULT_PORT = 80
|
|
SCAN_INTERVAL = timedelta(seconds=30)
|
|
|
|
|
|
SENSOR_DESCRIPTIONS: dict[tuple[Units, bool], SensorEntityDescription] = {
|
|
(Units.C, False): SensorEntityDescription(
|
|
key=f"{Units.C}_{False}",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
),
|
|
(Units.KWH, False): SensorEntityDescription(
|
|
key=f"{Units.KWH}_{False}",
|
|
device_class=SensorDeviceClass.ENERGY,
|
|
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
),
|
|
(Units.KWH, True): SensorEntityDescription(
|
|
key=f"{Units.KWH}_{True}",
|
|
device_class=SensorDeviceClass.ENERGY,
|
|
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
|
),
|
|
(Units.V, False): SensorEntityDescription(
|
|
key=f"{Units.V}_{False}",
|
|
device_class=SensorDeviceClass.VOLTAGE,
|
|
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
),
|
|
(Units.A, False): SensorEntityDescription(
|
|
key=f"{Units.A}_{False}",
|
|
device_class=SensorDeviceClass.CURRENT,
|
|
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
),
|
|
(Units.W, False): SensorEntityDescription(
|
|
key=f"{Units.W}_{False}",
|
|
device_class=SensorDeviceClass.POWER,
|
|
native_unit_of_measurement=UnitOfPower.WATT,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
),
|
|
(Units.PERCENT, False): SensorEntityDescription(
|
|
key=f"{Units.PERCENT}_{False}",
|
|
device_class=SensorDeviceClass.BATTERY,
|
|
native_unit_of_measurement=PERCENTAGE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
),
|
|
(Units.HZ, False): SensorEntityDescription(
|
|
key=f"{Units.HZ}_{False}",
|
|
device_class=SensorDeviceClass.FREQUENCY,
|
|
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
),
|
|
(Units.NONE, False): SensorEntityDescription(
|
|
key=f"{Units.NONE}_{False}",
|
|
),
|
|
}
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Entry setup."""
|
|
api: RealTimeAPI = hass.data[DOMAIN][entry.entry_id]
|
|
resp = await api.get_data()
|
|
serial = resp.serial_number
|
|
version = resp.version
|
|
endpoint = RealTimeDataEndpoint(hass, api)
|
|
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():
|
|
description = SENSOR_DESCRIPTIONS[(measurement.unit, measurement.is_monotonic)]
|
|
|
|
uid = f"{serial}-{idx}"
|
|
devices.append(
|
|
Inverter(
|
|
api.inverter.manufacturer,
|
|
uid,
|
|
serial,
|
|
version,
|
|
sensor,
|
|
description.native_unit_of_measurement,
|
|
description.state_class,
|
|
description.device_class,
|
|
)
|
|
)
|
|
endpoint.sensors = devices
|
|
async_add_entities(devices)
|
|
|
|
|
|
class RealTimeDataEndpoint:
|
|
"""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."""
|
|
|
|
_attr_should_poll = False
|
|
|
|
def __init__(
|
|
self,
|
|
manufacturer,
|
|
uid,
|
|
serial,
|
|
version,
|
|
key,
|
|
unit,
|
|
state_class=None,
|
|
device_class=None,
|
|
):
|
|
"""Initialize an inverter sensor."""
|
|
self._attr_unique_id = uid
|
|
self._attr_name = f"{manufacturer} {serial} {key}"
|
|
self._attr_native_unit_of_measurement = unit
|
|
self._attr_state_class = state_class
|
|
self._attr_device_class = device_class
|
|
self._attr_device_info = DeviceInfo(
|
|
identifiers={(DOMAIN, serial)},
|
|
manufacturer=MANUFACTURER,
|
|
name=f"{manufacturer} {serial}",
|
|
sw_version=version,
|
|
)
|
|
self.key = key
|
|
self.value = None
|
|
|
|
@property
|
|
def native_value(self):
|
|
"""State of this inverter attribute."""
|
|
return self.value
|