FranklinWH Modbus¶
Unofficial Python library & CLI for controlling FranklinWH battery storage systems via Modbus TCP, optimized for the aGate gateway.
Related Project
For the FranklinWH Cloud API (non-Modbus), see franklinwh-cloud.
Overview¶
FranklinWH Modbus provides direct, vendor-agnostic local network control over your aGate via the Modbus TCP protocol. Because it communicates directly with the hardware on your LAN, it operates entirely offline, bypasses cloud limits, and offers near-instantaneous response times (≤50ms).
👉 Read the complete guide on What Modbus TCP is, its challenges, and how it compares to the Cloud API
FranklinWH Implementation Coverage¶
What the aGate X / aPower S system implements via Modbus TCP:
✅ Working¶
| Function | Model | Details |
|---|---|---|
| Battery charge / discharge / standby | M704 (WSetPct) | Primary control mechanism — percentage of rated max |
| Battery SoC, SoH, DC power | M713 / M714 | M713.Sta unreliable (always 0) — state derived from M714.DCW |
| Battery lifetime energy | M714 (DCWhInj / DCWhAbs) | Cumulative charge and discharge (Wh) |
| AC grid power, voltage, frequency | M701 | Full implementation including per-phase data |
| Grid lifetime energy | M701 (TotWhInj / TotWhAbs) | Cumulative grid export and import (Wh) |
| Solar AC output power | M502 (OutPw / OutWh) | AC-coupled solar production and lifetime total |
| Grid protection trip curves | M707–M710 | Under/over voltage and frequency (read + write) |
| Volt-Var / Volt-Watt curves | M705 / M706 | Readable; writes untested |
| Frequency droop response | M711 | Readable; writes untested |
| Temperatures | M701 (TmpAmb / TmpCab) | Ambient and cabinet temperature |
❌ Not Functional¶
| Function | Model | Issue |
|---|---|---|
| DER heartbeat | M715 (ControllerHb) | Ignored by aGate — use software timeout instead |
| Hardware reversion timer | M704 (WSetRvrtTms) | Non-functional — software auto-revert used |
| DC battery voltage | M714 (DCV) | Register not populated |
| DC battery current | M714 (DCA) | Always returns 0 — calculate from P/V |
| Solar voltage / current | M502 (OutV / InV) | Not populated |
⚠️ Requires SPAN Unlock¶
| Function | Register | Without SPAN |
|---|---|---|
| Work mode switching | Ext 15507 (OnGridMode) | Read-only |
| Self-consumption reserve | Ext 15508 | Read-only |
| TOU reserve | Ext 15509 | Read-only (also mirrors 15508 — known defect) |
Features¶
- Modbus TCP — Direct register read/write via pymodbus + SunSpec 2.0
- SunSpec Models — Models 1, 701–706, 713–715
- FranklinWH Extensions — Registers 15507–15509 (OnGridMode, reserves)
- CLI Tool —
franklinwh_cli.pywith charge, discharge, standby, healthcheck, and a live TUI monitor - Virtual Modes — Self-Consumption, Emergency Backup, TOU, Peak Shave, Manual
- Safety Controls — SoC validation, alarm monitoring, conflict detection, auto-revert
- Target SoC — Charge/discharge to specific SoC with auto-stop
Quick Start¶
git clone git@github.com:david2069/franklinwh-modbus.git
cd franklinwh-modbus
python3 -m venv venv && source venv/bin/activate
pip install -e ".[dev]"
CLI¶
CLI="python3 tools/franklinwh_cli.py -i YOUR_AGATE_IP"
$CLI --status # Compact status
$CLI --healthcheck # System health check
$CLI --charge 3000 # Charge at 3000W
$CLI --discharge 2000 # Discharge at 2000W
$CLI --standby # Force battery idle
$CLI --charge 3000 --target-soc-auto 80 --loop # Charge to 80%
$CLI --stop # Release control
$CLI --monitor # Interactive TUI dashboard
Library¶
from franklinwh_modbus import FranklinWHController, BatteryCommand
ctrl = FranklinWHController('YOUR_AGATE_IP')
ctrl.connect()
# Read battery status
status = ctrl.read_battery_status()
print(f"SoC: {status['soc']:.1f}% State: {status['battery_state']}")
# Charge at 3000W with 1-hour auto-revert
cmd = BatteryCommand(power_watts=3000, mode='charge')
ctrl.send_command(cmd, duration_s=3600)
# Release control
ctrl.reset_control_state()
ctrl.disconnect()
SunSpec Model Support¶
| Model | Description | Read | Write | Notes |
|---|---|---|---|---|
| 1 | Common | ✅ | ❌ | |
| 502 | Solar Module | ✅ | ❌ | PV production (proximal + remote) |
| 701 | DER AC Measurement | ✅ | ❌ | DERMode: Grid Following, Grid Forming, PV Clipped |
| 702 | DER Capacity | ✅ | ❌ | Nameplate ratings |
| 703 | Enter Service | ✅ | ❌ | |
| 704 | DER AC Controls | ✅ | ✅ | WSetPct/WSetEna confirmed working |
| 705 | DER Volt-Var | ✅ | ⚠️ | Untested |
| 706 | DER Volt-Watt | ✅ | ⚠️ | Untested |
| 713 | DER Storage Capacity | ✅ | ❌ | ⚠️ Sta always 0 (unreliable) |
| 714 | DER DC Measurement | ✅ | ❌ | DCW used for battery-state derivation; multi-stack aware |
| 715 | DERCtl | ✅ | ❌ | LocRemCtl read-only, heartbeat non-functional |
Documentation¶
Explore the sidebar for detailed guides on:
- Modbus Guide — Start here: definitive implementation guide
- Model 704 Control Examples — Handbook and JSON sequence examples for power control
- SunSpec 700 Series Guide 2 — Core reference for 700 series Modbus standard rules
- SunSpec 700 Series Verification Plan — Non-destructive verification strategy
- Conformance & Telemetry Report — Telemetry and findings from the live aGate hardware tests
- CLI Command Reference — All switches tested with live output
- SunSpec Quirks — Hardware-specific quirks and workarounds
- DER Control Reference — Complete M704/M715 register map
- Safety Controls — 10 safety rules for development