From d983215ae6e34950d576bab37f18db0d92628b2d Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Sun, 21 Jun 2026 02:05:49 +0500 Subject: [PATCH] goodwe: add battery charge/discharge current limit number entities battery_charge_current (reg 45353) and battery_discharge_current (reg 45355) are writable settings already exposed by the goodwe library but were never wired up as HA number entities. This adds both as EntityCategory.CONFIG controls with NumberDeviceClass.CURRENT and 0.1 A step. The slider ceiling is set dynamically from the BMS-reported battery_charge_limit / battery_discharge_limit sensors (registers 37004/37005) so it reflects what the hardware actually supports (e.g. 105 A), with a 100 A fallback when the coordinator has no data yet. --- homeassistant/components/goodwe/number.py | 63 ++++++++++++++++++-- homeassistant/components/goodwe/strings.json | 6 ++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/goodwe/number.py b/homeassistant/components/goodwe/number.py index cde4977e370f93..c91ff01cad6676 100644 --- a/homeassistant/components/goodwe/number.py +++ b/homeassistant/components/goodwe/number.py @@ -1,7 +1,7 @@ """GoodWe PV inverter numeric settings entities.""" from collections.abc import Awaitable, Callable -from dataclasses import dataclass +from dataclasses import dataclass, field import logging from goodwe import Inverter, InverterError @@ -11,13 +11,18 @@ NumberEntity, NumberEntityDescription, ) -from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfPower +from homeassistant.const import ( + PERCENTAGE, + EntityCategory, + UnitOfElectricCurrent, + UnitOfPower, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import DOMAIN -from .coordinator import GoodweConfigEntry +from .coordinator import GoodweConfigEntry, GoodweUpdateCoordinator _LOGGER = logging.getLogger(__name__) @@ -29,6 +34,9 @@ class GoodweNumberEntityDescription(NumberEntityDescription): getter: Callable[[Inverter], Awaitable[int]] setter: Callable[[Inverter, int], Awaitable[None]] filter: Callable[[Inverter], bool] + # Optional sensor id whose runtime value becomes native_max_value. + # Falls back to native_max_value from the description when not set or unavailable. + max_sensor_id: str | None = field(default=None) def _get_setting_unit(inverter: Inverter, setting: str) -> str: @@ -79,6 +87,38 @@ def _get_setting_unit(inverter: Inverter, setting: str) -> str: setter=lambda inv, val: inv.set_ongrid_battery_dod(val), filter=lambda inv: True, ), + GoodweNumberEntityDescription( + key="battery_charge_current", + translation_key="battery_charge_current", + icon="mdi:battery-arrow-up", + entity_category=EntityCategory.CONFIG, + device_class=NumberDeviceClass.CURRENT, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + native_step=0.1, + native_min_value=0, + native_max_value=100, + max_sensor_id="battery_charge_limit", + getter=lambda inv: inv.read_setting("battery_charge_current"), + setter=lambda inv, val: inv.write_setting("battery_charge_current", val), + filter=lambda inv: "battery_charge_current" in {s.id_ for s in inv.settings()}, + ), + GoodweNumberEntityDescription( + key="battery_discharge_current", + translation_key="battery_discharge_current", + icon="mdi:battery-arrow-down", + entity_category=EntityCategory.CONFIG, + device_class=NumberDeviceClass.CURRENT, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + native_step=0.1, + native_min_value=0, + native_max_value=100, + max_sensor_id="battery_discharge_limit", + getter=lambda inv: inv.read_setting("battery_discharge_current"), + setter=lambda inv, val: inv.write_setting("battery_discharge_current", val), + filter=lambda inv: ( + "battery_discharge_current" in {s.id_ for s in inv.settings()} + ), + ), ) @@ -89,6 +129,7 @@ async def async_setup_entry( ) -> None: """Set up the inverter select entities from a config entry.""" inverter = config_entry.runtime_data.inverter + coordinator = config_entry.runtime_data.coordinator device_info = config_entry.runtime_data.device_info entities = [] @@ -102,7 +143,9 @@ async def async_setup_entry( continue entities.append( - InverterNumberEntity(device_info, description, inverter, current_value) + InverterNumberEntity( + coordinator, device_info, description, inverter, current_value + ) ) async_add_entities(entities) @@ -117,6 +160,7 @@ class InverterNumberEntity(NumberEntity): def __init__( self, + coordinator: GoodweUpdateCoordinator, device_info: DeviceInfo, description: GoodweNumberEntityDescription, inverter: Inverter, @@ -128,6 +172,17 @@ def __init__( self._attr_device_info = device_info self._attr_native_value = float(current_value) self._inverter: Inverter = inverter + self._coordinator = coordinator + + @property + def native_max_value(self) -> float: + """Return max value, using the BMS-reported limit when available.""" + max_sensor_id = self.entity_description.max_sensor_id + if max_sensor_id is not None: + bms_limit = self._coordinator.sensor_value(max_sensor_id) + if bms_limit is not None and bms_limit > 0: + return float(bms_limit) + return self.entity_description.native_max_value or 100.0 async def async_update(self) -> None: """Get the current value from inverter.""" diff --git a/homeassistant/components/goodwe/strings.json b/homeassistant/components/goodwe/strings.json index fc8c3bccb2341d..eb9bf822a6d2ed 100644 --- a/homeassistant/components/goodwe/strings.json +++ b/homeassistant/components/goodwe/strings.json @@ -24,6 +24,12 @@ } }, "number": { + "battery_charge_current": { + "name": "Battery charge current limit" + }, + "battery_discharge_current": { + "name": "Battery discharge current limit" + }, "battery_discharge_depth": { "name": "Depth of discharge (on-grid)" },