Repository navigation
feat: add SunEnergyXT 500 Series battery storage module - #3387
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new openWB device integration for the SunEnergyXT 500 Series battery storage via the device’s local HTTP API, including active power-limit control support.
Changes:
- Introduces a new
sunenergyxtvendor/device module with configuration schema and device descriptor wiring. - Implements a battery component that reads SoC/power from
/readand writes control parameters to/write. - Adds a device configuration Vue component (settings UI) for IP/port/timeout.
Reviewed changes
Copilot reviewed 5 out of 7 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/modules/devices/sunenergyxt/vendor.py | Registers the new vendor metadata and grouping. |
| packages/modules/devices/sunenergyxt/sunenergyxt/device.py | Wires the configurable device and battery component updater. |
| packages/modules/devices/sunenergyxt/sunenergyxt/config.py | Adds device + component configuration classes (IP/port/timeout). |
| packages/modules/devices/sunenergyxt/sunenergyxt/bat.py | Implements HTTP read/write logic, state parsing, and set_power_limit() control. |
| packages/modules/devices/sunenergyxt/sunenergyxt/device.vue | Adds a settings UI component for configuring the device connection. |
| packages/modules/devices/sunenergyxt/init.py | Package marker. |
| packages/modules/devices/sunenergyxt/sunenergyxt/init.py | Package marker. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Bitte noch die Flake8-Anmerkungen umsetzen, dann kann der PR gemergt werden. |
Removed default values for ip_address, port, and timeout in SunEnergyXTConfiguration.
refactor(sunenergyxt): apply Copilot review suggestions to bat.py - Replace raw `requests` with `req.get_http_session()` to align with openWB HTTP session conventions (timeout, error handling) - Remove `max_charge_power` / `max_discharge_power` assignments – BatState does not expose these fields - Replace magic constant `9999` with dynamic `self._gs_max`, derived from device field IS (max. inverter power) on each update(). IS reflects both model (500 / 500 Pro) and number of modules (BN), so no manual calculation needed. Falls back to 800W until first update. - Remove `self._timeout` – timeout is now a fixed inline value (5s)
Change power key from 'PB' to 'BP' in battery state.
There was a problem hiding this comment.
🟡 Changes recommended
Battery power parsing and charge/discharge control are incorrect, and advertised connection settings are missing.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (4)
packages/modules/devices/sunenergyxt/sunenergyxt/bat.py:83
- These branches invert openWB's battery-limit convention: the controller supplies negative values for discharge and positive values for charge (
packages/control/bat_all.py:255-297, also implemented atpackages/modules/devices/sungrow/sungrow_sh/bat.py:137-155). As written, active control will command the opposite battery action. Swap the input-sign branches while preserving the device's positive-discharge/negative-chargeGSconvention.
elif power_limit > 0:
p = int(min(power_limit, self._gs_max))
log.debug("SunEnergyXT: Entladen mit %dW (gs_max=%dW)", p, self._gs_max)
self._write(MM=0, GS=p)
packages/modules/devices/sunenergyxt/sunenergyxt/SunEnergyXT500_bat_test.py:163
- This discharge-cap case also uses the reversed openWB sign. Pass a negative limit so it continues testing capped discharge after the production branches are corrected.
bat.set_power_limit(9999) # Weit über Limit
packages/modules/devices/sunenergyxt/sunenergyxt/SunEnergyXT500_bat_test.py:174
- This charge-cap case also uses the reversed openWB sign. Charging is requested with a positive limit; the device payload should remain negative.
bat.set_power_limit(-9999) # Weit über Limit
packages/modules/devices/sunenergyxt/sunenergyxt/SunEnergyXT500_bat_test.py:188
- The PRO-limit test likewise sends a discharge request while expecting a charge payload. Use a positive openWB limit to verify the
-2400device command.
bat.set_power_limit(-2400)
payload = requests_mock.last_request.json()
assert payload["state"]["GS"] == -2400
- Files reviewed: 5/7 changed files
- Comments generated: 3
- Review effort level: Balanced
| reported = data.get("state", {}).get("reported", data) | ||
|
|
||
| soc = int(float(reported.get("SC", 0))) | ||
| power = float(reported.get("BP", 0)) |
| def test_positive_discharges(self, requests_mock): | ||
| """power_limit>0 → MM=0, GS=+p (Entladen).""" | ||
| bat = _make_bat() | ||
| bat._gs_max = 800 | ||
| requests_mock.post(f"{BASE_URL}/write", json={}) | ||
|
|
||
| bat.set_power_limit(500) | ||
|
|
||
| assert requests_mock.last_request.json() == {"state": {"MM": 0, "GS": 500}} | ||
|
|
||
| def test_negative_charges(self, requests_mock): | ||
| """power_limit<0 → MM=0, GS=-p (Laden).""" | ||
| bat = _make_bat() | ||
| bat._gs_max = 800 | ||
| requests_mock.post(f"{BASE_URL}/write", json={}) | ||
|
|
||
| bat.set_power_limit(-600) | ||
|
|
||
| assert requests_mock.last_request.json() == {"state": {"MM": 0, "GS": -600}} |
| def __init__(self, | ||
| ip_address: Optional[str] = None): | ||
| self.ip_address = ip_address |
Summary
This PR adds support for the SunEnergyXT 500 Series (including the 500 PRO) battery storage system to openWB.
Communication
The device exposes a local HTTP API — no cloud dependency, no Modbus:
http://<IP>/readhttp://<IP>/writeFeatures
Read (passive monitoring)
SC)PB)IS)Active control (
power_limit_controllable = True)The module supports full active battery control by openWB:
None— automaticMM=1, GS=00— stop dischargeMM=0, GS=0> 0— discharge N WMM=0, GS=+N< 0— charge N WMM=0, GS=-NConfiguration
805UI
A Vue configuration page is provided via a separate PR to
openwb-ui-settings: ChristophCaina/openwb-ui-settingsTesting
power_limit_controllable) recognized by openWBrequires a real charging session with physical hardware