Skip to content

feat: add SunEnergyXT 500 Series battery storage module#3387

Draft
ChristophCaina wants to merge 1 commit into
openWB:masterfrom
ChristophCaina:master
Draft

feat: add SunEnergyXT 500 Series battery storage module#3387
ChristophCaina wants to merge 1 commit into
openWB:masterfrom
ChristophCaina:master

Conversation

@ChristophCaina
Copy link
Copy Markdown

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)
  • Grid power (GP)
  • PV power (PP)
  • 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

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.

Die device.vue hat in dem Modul nichts zu suchen, die wird nur übers UI Repo im entsprechenden UI Pfad erzeugt.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Danke für den Hinweis.
Das ist vermutlich noch von meinem lokalen testen übrig geblieben.
Werde ich anpassen und entfernen & Sobald ich mit realer Hardware weitere Tests gemacht habe, wird der pr finalisiert.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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.

Comment on lines +3 to +6
import logging
from typing import Any, Optional
import requests
from modules.common.abstract_device import AbstractBat
Comment on lines +33 to +44
def _read(self) -> dict:
url = f"{self._base_url}/read"
resp = requests.get(url, timeout=self._timeout)
resp.raise_for_status()
return resp.json()

def _write(self, **kwargs) -> None:
url = f"{self._base_url}/write"
payload = {"state": kwargs}
resp = requests.post(url, json=payload, timeout=self._timeout)
resp.raise_for_status()
log.debug("SunEnergyXT write %s → %s", kwargs, resp.text)
Comment on lines +56 to +64
bat_state = BatState(
power=power,
soc=soc,
imported=imported,
exported=exported,
)
bat_state.max_charge_power = max_power
bat_state.max_discharge_power = max_power
self.store.set(bat_state)
Comment on lines +6 to +10
class SunEnergyXTConfiguration:
def __init__(self,
ip_address: Optional[str] = "192.168.1.100",
port: int = 80,
timeout: int = 5):
Comment on lines +67 to +81
def set_power_limit(self, power_limit: Optional[int]) -> None:
if power_limit is None:
log.debug("SunEnergyXT: Automatik (MM=1, GS=0)")
self._write(MM=1, GS=0)
elif power_limit == 0:
log.debug("SunEnergyXT: Entladung gesperrt (MM=0, GS=0)")
self._write(MM=0, GS=0)
elif power_limit > 0:
p = int(min(power_limit, 9999))
log.debug("SunEnergyXT: Entladen mit %dW", p)
self._write(MM=0, GS=p)
else:
p = int(min(abs(power_limit), 9999))
log.debug("SunEnergyXT: Laden mit %dW", p)
self._write(MM=0, GS=-p)
Comment on lines +51 to +55
import DeviceConfigMixin from "../../DeviceConfigMixin.vue";
export default {
name: "DeviceSunEnergyXT",
mixins: [DeviceConfigMixin],
};
Comment on lines +22 to +26
return ConfigurableDevice(
device_config=device_config,
component_factory=ComponentFactoryByType(
bat=create_bat_component,
),
Comment on lines +46 to +50
def update(self) -> None:
data = self._read()
reported = data.get("state", {}).get("reported", data)

soc = int(float(reported.get("SC", 0)))
Comment on lines +2 to +10
import logging
from typing import Iterable
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_context import SingleComponentUpdateContext
from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater
from modules.devices.sunenergyxt.sunenergyxt.bat import SunEnergyXTBat
from modules.devices.sunenergyxt.sunenergyxt.config import SunEnergyXT, SunEnergyXTBatSetup

log = logging.getLogger(__name__)
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.

3 participants