Skip to main content

Python library for controlling SunEnergyXT (BK215, B215, EV3600) battery storage devices

Project description

SunEnergyXT Python API

A Python library for controlling and monitoring SunEnergyXT battery storage devices (BK215, BK215 Plus, B215, B215 Plus, EV3600, etc.).

Python Version License


About Sonnenladen GmbH

This library is proudly published, maintained, and developed by Sonnenladen GmbH. Our collaboration with the SunEnergyXT R&D Team has been instrumental in making this API a reality. At Sonnenladen GmbH, we are committed to providing top-notch solar energy solutions and are excited to offer this library to enhance the experience of using SunEnergyXT storage systems.

Purchase SunEnergyXT BK215 (Plus), B215 (Plus), EV3600 etc.

For those interested in purchasing SunEnergyXT products, please visit our German online shop at Sonnenladen. We offer all SunEnergyXT products, backed by our expertise in solar energy solutions. Your purchase helps to maintain projects like this!


Features

  • Easy to Use: Simple, intuitive API for controlling SunEnergyXT devices
  • Async Support: Both synchronous and asynchronous client implementations
  • Device Discovery: Automatic device discovery using mDNS/Zeroconf
  • Type Safe: Full type hints for better IDE support
  • Comprehensive: All device endpoints exposed with descriptive method names
  • Monitoring: Real-time device status monitoring with callbacks
  • Validation: Built-in parameter validation with helpful error messages

Installation

Install from PyPI:

pip install sunenergyxt

Or install from source:

git clone https://github.com/SonnenladenGmbH/sunenergyxt-api.git
cd sunenergyxt-api
pip install -e .

Quick Start

Get All Data as JSON (Simplest)

from sunenergyxt import SunEnergyXTClient

with SunEnergyXTClient("192.168.1.100") as client:
    if client.wait_for_data():
        # Get ALL device data as formatted JSON with one line!
        print(client.get_status_json())

Output:

{
  "overall_soc": 53,
  "total_batteries": 2,
  "system_discharge_limit": 10,
  "system_charge_limit": 85,
  "system_charging_power": 300,
  "home_discharge_cutoff": 5,
  "car_discharge_cutoff": 5,
  "battery_charge_cutoff": 85,
  "idle_shutdown_time": 15,
  "low_battery_shutdown_time": 5,
  "local_mode": true,
  "battery_charging_mode": true,
  "main_battery": {
    "soc": 64,
    "bms_min_limit": 10,
    "bms_max_limit": 90
  },
  "expansion_batteries": [...],
  ...
}

Control Device

from sunenergyxt import SunEnergyXTClient

# Connect to device
with SunEnergyXTClient("192.168.1.100") as client:
    # Enable home appliance mode
    client.enable_home_appliance_mode()

    # Set charging limits
    client.set_max_charge_soc(90)  # Charge to 90%
    client.set_min_discharge_soc(10)  # Discharge to 10%

    # Set charging power
    client.set_charging_power(2000)  # 2000W

    # Get battery status
    status = client.get_battery_status()
    print(f"Battery: {status.overall_soc}%")
    print(f"Available modules: {status.total_batteries}")

Asynchronous Client

import asyncio
from sunenergyxt import AsyncSunEnergyXTClient

async def main():
    async with AsyncSunEnergyXTClient("192.168.1.100") as client:
        # Enable charging mode
        await client.enable_charging_mode()

        # Get battery status
        status = client.get_battery_status()
        if status:
            print(f"Battery: {status.overall_soc}%")

asyncio.run(main())

Device Discovery

from sunenergyxt import discover_devices

# Discover devices on network
devices = discover_devices(timeout=5.0)

for device in devices:
    print(f"Found: {device.serial_number} at {device.host}:{device.port}")

# Connect to first discovered device
if devices:
    with SunEnergyXTClient(devices[0].host, devices[0].port) as client:
        print("Connected!")

Usage Examples

Control Operating Modes

from sunenergyxt import SunEnergyXTClient

with SunEnergyXTClient("192.168.1.100") as client:
    # Enable different modes
    client.enable_charging_mode()
    client.enable_home_appliance_mode()
    client.enable_car_charging_mode()
    client.enable_ev_ac_mixed_power()

    # Disable modes
    client.disable_charging_mode()

Configure Battery Limits

from sunenergyxt import SunEnergyXTClient

with SunEnergyXTClient("192.168.1.100") as client:
    # Set overall SOC limits
    client.set_min_discharge_soc(10)  # Min 10%
    client.set_max_charge_soc(90)     # Max 90%

    # Set mode-specific limits
    client.set_home_appliance_min_soc(15)  # 15% for home appliances
    client.set_ev_charging_min_soc(20)     # 20% for EV charging
    client.set_charging_max_soc(95)        # 95% max when charging

Apply Complete Charging Profile

from sunenergyxt import SunEnergyXTClient, ChargingProfile

# Create profile
profile = ChargingProfile(
    min_discharge_soc=10,
    max_charge_soc=90,
    charging_power=2500,
    home_appliance_min_soc=15,
    ev_charging_min_soc=20,
    charging_max_soc=95,
    no_io_shutdown_timeout=60,
    dod_shutdown_timeout=30
)

# Apply to device
with SunEnergyXTClient("192.168.1.100") as client:
    client.apply_charging_profile(profile)
    print("Profile applied successfully!")

Monitor Battery Status

from sunenergyxt import SunEnergyXTClient
import time

def on_update(data):
    print(f"SOC: {data.get('t211')}%")

with SunEnergyXTClient("192.168.1.100") as client:
    # Start monitoring with callback
    client.start_monitoring(callback=on_update)

    # Keep running
    time.sleep(60)

    # Get latest status
    status = client.get_battery_status()
    if status:
        print(f"\nFinal status: {status}")
        for battery in status.all_batteries:
            if battery.is_available:
                print(f"  {battery.name}: {battery.soc}%")

Async Monitoring

import asyncio
from sunenergyxt import AsyncSunEnergyXTClient

async def monitor_device():
    async def on_data(data):
        print(f"Update: SOC={data.get('t211')}%")

    async with AsyncSunEnergyXTClient("192.168.1.100") as client:
        await client.start_monitoring(callback=on_data)

        # Monitor for 60 seconds
        await asyncio.sleep(60)

        # Get status
        status = client.get_battery_status()
        print(f"Status: {status}")

asyncio.run(monitor_device())

Handle Errors

from sunenergyxt import SunEnergyXTClient
from sunenergyxt.exceptions import (
    SunEnergyXTConnectionError,
    SunEnergyXTCommandError,
    SunEnergyXTValidationError,
    SunEnergyXTTimeoutError
)

try:
    with SunEnergyXTClient("192.168.1.100", timeout=5.0) as client:
        # Try to set invalid value
        client.set_max_charge_soc(150)  # Out of range!

except SunEnergyXTValidationError as e:
    print(f"Validation error: {e}")
except SunEnergyXTConnectionError as e:
    print(f"Connection error: {e}")
except SunEnergyXTCommandError as e:
    print(f"Command error: {e} (code: {e.error_code})")
except SunEnergyXTTimeoutError as e:
    print(f"Timeout: {e}")

API Reference

Client Methods

Mode Control

  • enable_local_communication() / disable_local_communication()
  • enable_charging_mode() / disable_charging_mode()
  • enable_car_charging_mode() / disable_car_charging_mode()
  • enable_home_appliance_mode() / disable_home_appliance_mode()
  • enable_ev_ac_mixed_power() / disable_ev_ac_mixed_power()

SOC Configuration

  • set_min_discharge_soc(soc: int) - Set minimum discharge level (1-20%)
  • set_max_charge_soc(soc: int) - Set maximum charge level (70-100%)
  • set_home_appliance_min_soc(soc: int) - Set home appliance minimum (5-20%)
  • set_ev_charging_min_soc(soc: int) - Set EV charging minimum (5-40%)
  • set_charging_max_soc(soc: int) - Set charging maximum (80-100%)

Power & Timeouts

  • set_charging_power(watts: int) - Set charging power (0-3600W)
  • set_no_io_shutdown_timeout(minutes: int) - Set idle shutdown (15-1440 min)
  • set_dod_shutdown_timeout(minutes: int) - Set DOD shutdown (5-1440 min)

Profile Management

  • apply_charging_profile(profile: ChargingProfile) - Apply complete profile

Status & Monitoring

  • get_battery_status() -> BatteryStatus - Get complete battery status
  • get_status_json(indent: int = 2) -> str - Get all data as formatted JSON string
  • get_mode_status() -> ModeStatus - Get operating mode status
  • wait_for_data(timeout: float = 5.0) -> bool - Wait for initial data from device
  • start_monitoring(callback) - Start monitoring updates
  • stop_monitoring() - Stop monitoring

Connection

  • connect() - Establish connection
  • disconnect() - Close connection
  • is_connected() -> bool - Check connection status

Data Models

BatteryStatus

@dataclass
class BatteryStatus:
    # Battery Levels (Read-Only)
    overall_soc: Optional[int]                    # Overall state of charge %
    main_battery: Optional[BatteryModule]         # Main battery module
    slave_batteries: List[BatteryModule]          # Expansion battery modules

    # System Configuration (Read/Write)
    system_discharge_limit: Optional[int]         # Min discharge SOC (1-20%)
    system_charge_limit: Optional[int]            # Max charge SOC (70-100%)
    system_charging_power: Optional[int]          # Charging power (0-3600W)

    # Mode-Specific Configuration (Read/Write)
    home_discharge_cutoff: Optional[int]          # Home appliance mode (5-20%)
    car_discharge_cutoff: Optional[int]           # Car charging mode (5-40%)
    battery_charge_cutoff: Optional[int]          # Battery charging mode (80-100%)

    # Timeout Configuration (Read/Write)
    idle_shutdown_time: Optional[int]             # Idle auto-shutdown (15-1440 min)
    low_battery_shutdown_time: Optional[int]      # Low battery shutdown (5-1440 min)

    # Operating Modes (Read/Write)
    local_mode: Optional[bool]                    # Local mode enabled
    battery_charging_mode: Optional[bool]         # Battery charging mode
    car_charging_mode: Optional[bool]             # Car charging mode
    home_appliance_mode: Optional[bool]           # Home appliance mode
    ac_active_mode: Optional[bool]                # AC active mode

    timestamp: datetime                           # Last update timestamp

    @property
    def total_batteries(self) -> int              # Count of available batteries

    @property
    def all_batteries(self) -> List[BatteryModule]  # All battery modules

    def to_dict(self) -> Dict                     # Convert to dictionary

BatteryModule

@dataclass
class BatteryModule:
    name: str
    soc: Optional[int]              # State of charge %
    bms_min_limit: Optional[int]    # BMS minimum limit %
    bms_max_limit: Optional[int]    # BMS maximum limit %
    available: bool

    @property
    def is_available(self) -> bool

ModeStatus

@dataclass
class ModeStatus:
    local_communication: bool
    charging_mode: bool
    car_charging_mode: bool
    home_appliance_mode: bool
    ev_ac_mixed_power: bool

    def active_modes(self) -> List[str]

ChargingProfile

@dataclass
class ChargingProfile:
    min_discharge_soc: int = 10
    max_charge_soc: int = 90
    charging_power: int = 3600
    home_appliance_min_soc: int = 10
    ev_charging_min_soc: int = 20
    charging_max_soc: int = 95
    no_io_shutdown_timeout: int = 60
    dod_shutdown_timeout: int = 30

    def validate(self) -> None

DeviceInfo

@dataclass
class DeviceInfo:
    host: str
    port: int
    serial_number: Optional[str]
    firmware_version: Optional[str]
    hardware_model: Optional[str]
    hostname: Optional[str]

Exceptions

All exceptions inherit from SunEnergyXTError:

  • SunEnergyXTConnectionError - Connection failures
  • SunEnergyXTTimeoutError - Operation timeouts
  • SunEnergyXTCommandError - Command execution failures (has error_code attribute)
  • SunEnergyXTValidationError - Parameter validation failures
  • SunEnergyXTDiscoveryError - Device discovery failures

Advanced Usage

Custom Timeout

from sunenergyxt import SunEnergyXTClient

# 10 second timeout for slow networks
client = SunEnergyXTClient("192.168.1.100", timeout=10.0)
client.connect()

Manual Device Discovery

from sunenergyxt import SunEnergyXTDiscovery
import time

def on_device_found(device):
    print(f"Discovered: {device}")

discovery = SunEnergyXTDiscovery(callback=on_device_found)
discovery.start()

time.sleep(10)

devices = discovery.get_devices()
print(f"Found {len(devices)} devices")

discovery.stop()

Context Manager for Discovery

from sunenergyxt import SunEnergyXTDiscovery

with SunEnergyXTDiscovery() as discovery:
    import time
    time.sleep(5)
    devices = discovery.get_devices()
    print(f"Found {len(devices)} devices")

Protocol Details

The SunEnergyXT device uses a TCP-based JSON protocol:

  • Protocol: TCP/IP with JSON payloads
  • Default Port: 8000
  • Encoding: ASCII
  • Message Format: {"code": <int>, "data": {<fields>}}
  • Discovery: mDNS service type _http._tcp.local.

Message Codes

  • 0x6056 (24662) - Command to set values
  • 0x6057 (24663) - Command acknowledgment
  • 0x6052 (24658) - Device status report
  • 0x6055 (24661) - Alternate status report

Supported Devices

This library supports all SunEnergyXT battery storage systems:

  • BK215 / BK215 Plus
  • B215 / B215 Plus
  • EV3600
  • And other compatible SunEnergyXT models

Troubleshooting

Device Not Discovered

  1. Ensure device is on same network
  2. Check mDNS/Bonjour is enabled on your system
  3. Try manual connection with IP address

Connection Timeout

  1. Verify IP address is correct
  2. Check firewall settings
  3. Increase timeout parameter
  4. Ensure device is powered on

Command Fails

  1. Check parameter ranges
  2. Verify device is in correct mode
  3. Check BMS hardware limits
  4. Review error code in exception

No Data Reports

  1. Enable local communication: client.enable_local_communication()
  2. Start monitoring: client.start_monitoring()
  3. Keep connection alive
  4. Check device configuration

License

This project is licensed under the MIT License - see the LICENSE file for details.

Copyright (c) 2026 Sonnenladen GmbH - www.sonnenladen.de

Acknowledgments

  • Developed in collaboration with SunEnergyXT R&D Team
  • Protocol documentation based on device analysis & docs
  • Maintained by Sonnenladen GmbH

Links


Proudly developed and maintained by Sonnenladen GmbH ☀️🔋

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sunenergyxt-1.0.0.tar.gz (30.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sunenergyxt-1.0.0-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

File details

Details for the file sunenergyxt-1.0.0.tar.gz.

File metadata

  • Download URL: sunenergyxt-1.0.0.tar.gz
  • Upload date:
  • Size: 30.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.25

File hashes

Hashes for sunenergyxt-1.0.0.tar.gz
Algorithm Hash digest
SHA256 07d45adad893835f8b910e94bcb19b42bf36ffc416f92a7e51996b388b715b92
MD5 b764fc8ca7a11f674c91082207d1519d
BLAKE2b-256 1dec5fd9c371b4e246f6c9d2749ae8ad4a4328cddbd41ab893939898bab0da97

See more details on using hashes here.

File details

Details for the file sunenergyxt-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: sunenergyxt-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 25.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.25

File hashes

Hashes for sunenergyxt-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 79e4ec84b983ba242c6b8d73177a2c9dfecbca44bab09b4fb6306ba08ec7ea93
MD5 65f64075c60b02d4cd5a1cf859a42909
BLAKE2b-256 a34edf6a6e0d94c6476315a181668baf11085d65c37117b84fe257fd7c487664

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page