Skip to content

feat: add SunEnergyXT 500 Series battery storage module - #3387

Merged
LKuemmel merged 12 commits into
openWB:masterfrom
ChristophCaina:master
Sep 16, 2026
Merged

LKuemmel merged 12 commits into
openWB:masterfrom
ChristophCaina:master

Conversation

@ChristophCaina

@ChristophCaina ChristophCaina commented May 22, 2026 •

Copy link
Copy Markdown
Contributor

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:

Endpoint Method Description
http://<IP>/read GET Read all device state fields as JSON
http://<IP>/write POST Write control parameters

Features

Read (passive monitoring)

  • Battery SoC (SC)
  • Battery power — positive = charging, negative = discharging (PB)
  • Max inverter power (IS)

Active control (power_limit_controllable = True)

The module supports full active battery control by openWB:

openWB request Device command Effect
None — automatic MM=1, GS=0 Device self-regulates (self-consumption mode)
0 — stop discharge MM=0, GS=0 Battery inactive
> 0 — discharge N W MM=0, GS=+N Targeted discharge
< 0 — charge N W MM=0, GS=-N Forced charging

Configuration

  • IP address — local IP of the SunEnergyXT device
  • Port — default 80
  • Timeout — HTTP timeout in seconds, default 5

UI

A Vue configuration page is provided via a separate PR to openwb-ui-settings: ChristophCaina/openwb-ui-settings

Testing

  • ✅ Tested with SunEnergyXT device simulator
  • ✅ SoC, charge/discharge power verified against Home Assistant integration
  • ✅ Active control (power_limit_controllable) recognized by openWB
  • ⏳ Test with real hardware pending (EVSE issue on physical openWB being resolved and storage is available)
  • ⏳ Active power limit control (set_power_limit) not yet verified —
    requires a real charging session with physical hardware

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 sunenergyxt vendor/device module with configuration schema and device descriptor wiring.
  • Implements a battery component that reads SoC/power from /read and 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.

@LKuemmel
LKuemmel requested a review from ndrsnhs June 10, 2026 13:04
@ChristophCaina
ChristophCaina marked this pull request as ready for review August 20, 2026 06:10
@LKuemmel

LKuemmel commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Bitte noch die Flake8-Anmerkungen umsetzen, dann kann der PR gemergt werden.

ChristophCaina and others added 10 commits September 16, 2026 11:45
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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 at packages/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-charge GS convention.
        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 -2400 device 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))
Comment on lines +137 to +155
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}}
Comment on lines +7 to +9
def __init__(self,
ip_address: Optional[str] = None):
self.ip_address = ip_address
@LKuemmel
LKuemmel merged commit 24b536e into openWB:master Sep 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants