Skip to main content

franklinwh-modbus

Modbus TCP SunSpec Python

A Python library for controlling FranklinWH battery storage systems via Modbus TCP, optimized for the aGate gateway with SunSpec model support and FranklinWH extension registers.

⚠️ Important Disclaimer

This library is unofficial and not endorsed, supported, or affiliated with FranklinWH in any way.

It is provided "AS IS", for educational and informational purposes only, without warranty of any kind, express or implied, including but not limited to warranties of merchantability or fitness for any particular purpose. The author(s) and contributor(s) of this library and its documentation accept no responsibility or liability for any consequences of its use, and make no warranty that it is fit for any purpose.

By using this library, you acknowledge that:

  • This software writes to energy hardware and can charge, discharge and stop your battery
  • You assume all risk associated with its use
  • It may break without notice due to firmware changes by FranklinWH
  • Behaviour observed on one firmware or model may not hold on yours
  • You will use it responsibly

Do NOT contact FranklinWH support about defects, issues or feature requests for this software. They did not write it and cannot help with it. Raise them at github.com/david2069/franklinwh-modbus/issues instead.

📖 New to Modbus TCP? See the documentation site for an introduction to Modbus TCP, SunSpec, and how this library compares to the Cloud API.

Note: This is the Modbus TCP library (pip install franklinwh-modbus).

Python import: from franklinwh_modbus import FranklinWHController

For the non-Modbus TCP FranklinWH david2069 franklinwh-cloud Cloud API, see franklinwh-cloud.

For the non-Modbus TCP FranklinWH richo franklinwh-python Cloud API, see franklinwh-python.

Status: Published on PyPI (pip install franklinwh-modbus) and actively maintained.

⚠️ Important — Before You Start

Network Requirements

  • Fixed IP address required — the aGate must have a static/reserved IP on your LAN.
  • LAN Ethernet strongly preferred — wired connection for reliable Modbus TCP control.
  • ⚠️ WiFi is highly undesirable — latency and packet loss can cause missed keep-alive cycles, leaving the aGate stuck in VPP Mode.

Extension Register Access

Read operations always work — battery status, grid power, solar production (proximal and remote), system alarms, and all SunSpec model data are readable by any Modbus TCP client without provisioning. The CLI --status, --healthcheck, and TUI monitor all work out of the box.

Writes to the extension registers (15507–15509: OnGridMode, SelfReserve, TOUReserve) are accepted at the protocol level but are not applied on the firmware tested here (aGate X, V10R01B04D00). The register echoes the write and then reads back its previous value, which is why every write in this library is verified by read-back.

Standard SunSpec M704 power commands (charge/discharge) are unaffected and work regardless — that is the supported way to dispatch the battery from this library.

To check your own unit, --check-span reports whether extension writes are applied on your system. Its result is the only reliable answer; nothing about your panel hardware predicts it.

Avoiding Control Conflicts

!!! caution Do not use the FranklinWH mobile app to send charge/discharge commands or schedule events while this library is actively controlling the aGate. Conflicting commands will cause unpredictable behavior.

  • Recommended: Set your aGate to Emergency Backup or Self-Consumption mode via the mobile app before starting library control — this reduces the likelihood of conflicting Cloud API activity.
  • VPP Mode indicator: While any remote client API is actively controlling the aGate — Modbus TCP (this library) or the FranklinWH Cloud API (VPP providers) — the mobile app displays "VPP Mode". This is normal and confirms direct control is active. See VPP Mode Visual Reference for mobile app screenshots.
  • On failure or loss of connectivity: Always release control using --stop:
# Via CLI
python3 franklinwh_cli.py -i YOUR_AGATE_IP --stop

# Via library
ctrl.reset_control_state()
ctrl.disconnect()

!!! warning If control is not released, the aGate persists the last command indefinitely. Hardware heartbeat (ControllerHb) and reversion timer (WSetRvrtTms) do not work on FranklinWH — use --revert N or send_command(cmd, duration_s=N) for software-side auto-revert.

Installation

# From PyPI (recommended)
pip install franklinwh-modbus

For development (editable, with test deps):

git clone git@github.com:david2069/franklinwh-modbus.git
cd franklinwh-modbus
python3 -m venv venv && source venv/bin/activate
pip install -e ".[dev]"

Library Usage

from franklinwh_modbus import FranklinWHController, BatteryCommand

# Connect to aGate
ctrl = FranklinWHController('YOUR_AGATE_IP')
ctrl.connect()

# Read battery status (battery_state derived from DC power, not unreliable M713.Sta)
status = ctrl.read_battery_status()
print(f"SoC: {status['soc']:.1f}%  State: {status['battery_state']}")

# Charge at 3000W with 1-hour software timeout (auto-reverts to cloud control)
cmd = BatteryCommand(power_watts=3000, mode='charge')
ctrl.send_command(cmd, duration_s=3600)

# Release control (or let timeout handle it)
ctrl.reset_control_state()
ctrl.disconnect()

CLI Quick Start

# System status
python3 franklinwh_cli.py -i YOUR_AGATE_IP --status

# Health check with conflict detection
python3 franklinwh_cli.py -i YOUR_AGATE_IP --healthcheck

# Charge at 3000W with auto-revert after 2 hours
python3 franklinwh_cli.py -i YOUR_AGATE_IP --charge 3000 --revert 7200

# Native hardware mode (Register 15507)
python3 franklinwh_cli.py -i YOUR_AGATE_IP --mode self-consumption

# Virtual software mode (Orchestrated control loop)
python3 franklinwh_cli.py -i YOUR_AGATE_IP --vmode self_consumption --target-soc 90

# Terminal UI monitor (requires `rich`)
python3 franklinwh_cli.py -i YOUR_AGATE_IP --monitor

# Release control
python3 franklinwh_cli.py -i YOUR_AGATE_IP --stop

Key Features

Feature Description
Modbus TCP Direct register read/write via pymodbus
SunSpec Models Models 1, 502, 701–715
FranklinWH Extensions Registers 15506–15512, 16000 (OnGridMode, reserves, PV energy, hi-res load)
InfoPoint Sequencer Scripted read/write sequences with auto scale-factor, uint32 widths, enum-symbol resolution, and inline {type, sf} / addr·point·address overrides
Native + Virtual Modes --mode switches the native aGate mode via 15507; --vmode runs Self-Consumption, Emergency Backup, TOU, Peak Shave, Manual orchestration
Multi-battery M714 parallel-stack summing + repeating-block suffix (714.DCW_1)
Enum Resolution PICS-certified enum descriptions (get_pics_enum_desc / get_enum_desc)
Conflict Detection Detects aGate Cloud API activity before taking control
SoC Safety Reserve validation, target checking, safety margins
Alarm Monitoring System, DC port, battery, solar alarms
Safe Release Standby handshake (ramps to 0 W before WSetEna=0) + software auto-revert (hardware reversion non-functional on FranklinWH)

Project Structure

franklinwh-modbus/
├── src/franklinwh_modbus/          # Core library (the package)
│   ├── controller.py        # FranklinWHController — Modbus interface
│   ├── modes.py             # VirtualModeController — control modes
│   ├── types.py             # BatteryCommand, VirtualMode, enums
│   ├── schedule.py          # TOUSchedule — time-of-use
│   ├── monitor.py           # CLIMonitor — TUI (optional, needs rich)
│   └── constants.py         # Register addresses, limits
├── tools/franklinwh_cli.py   # CLI tool (consumes the library)
├── tests/                   # Unit + integration + hardware tests
├── docs/                    # Documentation
├── tools/                   # Utility scripts
└── schedules/               # TOU schedule definitions

Supported Hardware

Model Status Notes
FranklinWH aPower ✅ Full Support Battery storage
FranklinWH aGate ✅ Full Support Communication gateway

SunSpec Model Support

Note: aGate uses base address 1 (not standard 40000). Unit ID 1 or 2 both work (DA=1).

Model Description Read Write Notes
1 Common ✅ ❌
502 Solar Module ✅ ❌ PV production (proximal + remote)
701 DER AC Measurement ✅ ❌ Per-phase W/VA/Var/PF/A/V + lifetime energy
702 DER Capacity ✅ ❌ Nameplate ratings (WChaRteMax/WDisChaRteMax)
703 Enter Service ✅ ❌
704 DER AC Controls ✅ ✅ WSetPct/WSetEna confirmed working
705 DER Volt-Var ✅ ⚠️ Untested
706 DER Volt-Watt ✅ ⚠️ Untested
707–712 DER Trip / Freq-Droop / Watt-Var ✅ ❌ Present on the aGate catalog
713 DER Storage Capacity ✅ ❌ ⚠️ Sta always 0 (unreliable)
714 DER DC Measurement ✅ ❌ DCW used for battery-state derivation; multi-stack aware (repeating blocks)
715 DERCtl ✅ ❌ LocRemCtl read-only, heartbeat non-functional

FranklinWH Extension Registers

Register Address Access Description
OnGridMode 15507 R (RW with SPAN) 1=Emergency Backup, 2=Self-Consumption, 3=TOU, 4=Manual
Self Reserve SOC 15508 R (RW with SPAN) Self-consumption reserve percentage (0-100)
TOU Reserve SOC 15509 R (RW with SPAN) ⚠️ Known defect: always mirrors 15508

See FRANKLINWH_SUNSPEC_QUIRKS.md for all documented hardware quirks.

Contributing

See CONTRIBUTING.md for development setup and guidelines.

License

MIT License — see LICENSE for details.

Support

Metadata

Release files for franklinwh-modbus 0.9.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for franklinwh-modbus 0.9.4
File Size Uploaded
franklinwh_modbus-0.9.4.tar.gz 72.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for franklinwh-modbus 0.9.4
File Interpreter ABI Platform
franklinwh_modbus-0.9.4-py3-none-any.whl Python 3 none any Details

Total release size: 138.6 kB

Release files / franklinwh_modbus-0.9.4.tar.gz

Download URL franklinwh_modbus-0.9.4.tar.gz
Size 72.6 kB
Tags Source
SHA-256 checksum
How to use checksums
3934912e17aace71cb9c6df1e86e61dae38b0df6a364434003bba820df1b9251
BLAKE2b-256 checksum
How to use checksums
b95f2614a5e09caa987dfef490e77a1cc6b11f43148f658aad8a6a078678f05b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

Release files / franklinwh_modbus-0.9.4-py3-none-any.whl

Download URL franklinwh_modbus-0.9.4-py3-none-any.whl
Size 66.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bc1b1ba5f678a2c05df1fc00a2bbf65cbe1322623197793e4b5302639ac0ed17
BLAKE2b-256 checksum
How to use checksums
643136956b761913204933b2212a4f2dce15394f02317c619bb586057aef5080
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.5

2 release files

This release

0.9.4 This release

2 release files

0.9.3

2 release files

0.9.2

2 release files

0.9.1

2 release files

0.9.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page