diff --git a/homeassistant/components/solax/config_flow.py b/homeassistant/components/solax/config_flow.py index e3255a8e377..2334fd0def2 100644 --- a/homeassistant/components/solax/config_flow.py +++ b/homeassistant/components/solax/config_flow.py @@ -5,7 +5,7 @@ import logging from typing import Any from solax import real_time_api -from solax.inverter import DiscoveryError +from solax.discovery import DiscoveryError import voluptuous as vol from homeassistant import config_entries diff --git a/homeassistant/components/solax/manifest.json b/homeassistant/components/solax/manifest.json index 17ae6db0232..a41285277da 100644 --- a/homeassistant/components/solax/manifest.json +++ b/homeassistant/components/solax/manifest.json @@ -2,7 +2,7 @@ "domain": "solax", "name": "SolaX Power", "documentation": "https://www.home-assistant.io/integrations/solax", - "requirements": ["solax==0.2.9"], + "requirements": ["solax==0.3.0"], "codeowners": ["@squishykid"], "iot_class": "local_polling", "config_flow": true, diff --git a/homeassistant/components/solax/sensor.py b/homeassistant/components/solax/sensor.py index 7f9d81ac9b0..307d3c4c373 100644 --- a/homeassistant/components/solax/sensor.py +++ b/homeassistant/components/solax/sensor.py @@ -4,15 +4,26 @@ from __future__ import annotations import asyncio from datetime import timedelta -from solax.inverter import InverterError +from solax import RealTimeAPI +from solax.discovery 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 TEMP_CELSIUS +from homeassistant.const import ( + ELECTRIC_CURRENT_AMPERE, + ELECTRIC_POTENTIAL_VOLT, + ENERGY_KILO_WATT_HOUR, + FREQUENCY_HERTZ, + PERCENTAGE, + POWER_WATT, + TEMP_CELSIUS, +) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers.entity import DeviceInfo @@ -25,13 +36,69 @@ 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=TEMP_CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + (Units.KWH, False): SensorEntityDescription( + key=f"{Units.KWH}_{False}", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + state_class=SensorStateClass.MEASUREMENT, + ), + (Units.KWH, True): SensorEntityDescription( + key=f"{Units.KWH}_{True}", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=ENERGY_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=ELECTRIC_POTENTIAL_VOLT, + state_class=SensorStateClass.MEASUREMENT, + ), + (Units.A, False): SensorEntityDescription( + key=f"{Units.A}_{False}", + device_class=SensorDeviceClass.CURRENT, + native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + state_class=SensorStateClass.MEASUREMENT, + ), + (Units.W, False): SensorEntityDescription( + key=f"{Units.W}_{False}", + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=POWER_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=FREQUENCY_HERTZ, + state_class=SensorStateClass.MEASUREMENT, + ), + (Units.NONE, False): SensorEntityDescription( + key=f"{Units.NONE}_{False}", + state_class=SensorStateClass.MEASUREMENT, + ), +} + + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Entry setup.""" - api = hass.data[DOMAIN][entry.entry_id] + api: RealTimeAPI = hass.data[DOMAIN][entry.entry_id] resp = await api.get_data() serial = resp.serial_number version = resp.version @@ -39,30 +106,21 @@ async def async_setup_entry( hass.async_add_job(endpoint.async_refresh) async_track_time_interval(hass, endpoint.async_refresh, SCAN_INTERVAL) devices = [] - for sensor, (idx, unit) in api.inverter.sensor_map().items(): - device_class = state_class = None - if unit == "C": - device_class = SensorDeviceClass.TEMPERATURE - state_class = SensorStateClass.MEASUREMENT - unit = TEMP_CELSIUS - elif unit == "kWh": - device_class = SensorDeviceClass.ENERGY - state_class = SensorStateClass.TOTAL_INCREASING - elif unit == "V": - device_class = SensorDeviceClass.VOLTAGE - state_class = SensorStateClass.MEASUREMENT - elif unit == "A": - device_class = SensorDeviceClass.CURRENT - state_class = SensorStateClass.MEASUREMENT - elif unit == "W": - device_class = SensorDeviceClass.POWER - state_class = SensorStateClass.MEASUREMENT - elif unit == "%": - device_class = SensorDeviceClass.BATTERY - state_class = SensorStateClass.MEASUREMENT + 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(uid, serial, version, sensor, unit, state_class, device_class) + 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) @@ -71,12 +129,12 @@ async def async_setup_entry( class RealTimeDataEndpoint: """Representation of a Sensor.""" - def __init__(self, hass, api): + def __init__(self, hass: HomeAssistant, api: RealTimeAPI) -> None: """Initialize the sensor.""" self.hass = hass self.api = api self.ready = asyncio.Event() - self.sensors = [] + self.sensors: list[Inverter] = [] async def async_refresh(self, now=None): """Fetch new state data for the sensor. @@ -105,6 +163,7 @@ class Inverter(SensorEntity): def __init__( self, + manufacturer, uid, serial, version, @@ -115,14 +174,14 @@ class Inverter(SensorEntity): ): """Initialize an inverter sensor.""" self._attr_unique_id = uid - self._attr_name = f"Solax {serial} {key}" + 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"Solax {serial}", + name=f"{manufacturer} {serial}", sw_version=version, ) self.key = key diff --git a/requirements_all.txt b/requirements_all.txt index 1c7129153d7..f5d3058bb27 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2266,7 +2266,7 @@ solaredge-local==0.2.0 solaredge==0.0.2 # homeassistant.components.solax -solax==0.2.9 +solax==0.3.0 # homeassistant.components.honeywell somecomfort==0.8.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ddac1964828..3d49e63ff71 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1548,7 +1548,7 @@ soco==0.28.0 solaredge==0.0.2 # homeassistant.components.solax -solax==0.2.9 +solax==0.3.0 # homeassistant.components.honeywell somecomfort==0.8.0 diff --git a/tests/components/solax/test_config_flow.py b/tests/components/solax/test_config_flow.py index cb658405860..68a14e9133f 100644 --- a/tests/components/solax/test_config_flow.py +++ b/tests/components/solax/test_config_flow.py @@ -1,8 +1,9 @@ """Tests for the solax config flow.""" from unittest.mock import patch -from solax import RealTimeAPI, inverter +from solax import RealTimeAPI from solax.inverter import InverterResponse +from solax.inverters import X1MiniV34 from homeassistant import config_entries from homeassistant.components.solax.const import DOMAIN @@ -10,7 +11,7 @@ from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT def __mock_real_time_api_success(): - return RealTimeAPI(inverter.X1MiniV34) + return RealTimeAPI(X1MiniV34) def __mock_get_data():