FranklinWH aGate Modbus TCP library โ SunSpec + extension registers
Project description
franklinwh-modbus
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.
๐ 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 FranklinWHControllerFor 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: Core library under active development, targeting PyPi publication.
Quick Links
- ๐ Getting Started โ setup, install, CLI, network scanner & SunSpec reader
- ๐ Library & CLI Usage Guide
- ๐ Documentation
- ๐งช Hardware Test Guide
- ๐ Changelog
โ ๏ธ 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.
Write access to extension registers (15507โ15509: OnGridMode, SelfReserve, TOUReserve) requires "SPAN Modbus" unlock in FranklinWH installer settings.
Already qualified: Owners with SPAN Panels or Lumin Panels connected to the aGate via Modbus TCP โ these systems already have full write access enabled.
Not yet provisioned? Contact FranklinWH Support to request Modbus write access for your aGate. Without provisioning, extension registers are read-only (writes fail silently). Note: Standard SunSpec M704 power commands (charge/discharge) work regardless of SPAN unlock status.
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 Norsend_command(cmd, duration_s=N)for software-side auto-revert.
Installation
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
# Self-consumption mode
python3 franklinwh_cli.py -i YOUR_AGATE_IP --mode 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, 701-706, 713-715 |
| FranklinWH Extensions | Registers 15507-15509 (OnGridMode, reserves) |
| Virtual Modes | Self-Consumption, Emergency Backup, TOU, Peak Shave, Manual |
| 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 |
| Auto-Revert | Software timeout (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 | โ | โ | |
| 701 | DER AC Measurements | โ | โ | |
| 702 | DER DC Measurements | โ | โ | |
| 703 | DER Capacity | โ | โ | |
| 704 | DER AC Battery Control | โ | โ | WSetPct/WSetEna confirmed working |
| 705 | DER AC Controls | โ | โ ๏ธ | Untested |
| 706 | DER Volt/Var/Watt | โ | โ ๏ธ | Untested |
| 713 | DER Storage Capacity | โ | โ | โ ๏ธ Sta always 0 (unreliable) |
| 714 | DER Storage Status | โ | โ | DCW used for battery state derivation |
| 715 | DER Storage Controls | โ | โ | LocRemCtl read-only, heartbeat non-functional |
FranklinWH Extension Registers
| Register | Address | Access | Description |
|---|---|---|---|
| OnGridMode | 15507 | R (RW with SPAN) | 0=Backup, 1=TOU, 2=Self-Consumption, 3=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
- ๐ Report a Bug
- ๐ก Request a Feature
- ๐ Documentation
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file franklinwh_modbus-0.9.2.tar.gz.
File metadata
- Download URL: franklinwh_modbus-0.9.2.tar.gz
- Upload date:
- Size: 57.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6aca4375a124e447f8447c155938010f5290dcfa447881f3097cae141bdfed1
|
|
| MD5 |
8302bcd5398a6a872bcd8667574709ac
|
|
| BLAKE2b-256 |
8999c0866bc3529c2f737eaa739402d16bb27e9f6ae74da1086d07a252a2b8bc
|
Provenance
The following attestation bundles were made for franklinwh_modbus-0.9.2.tar.gz:
Publisher:
publish.yml on david2069/franklinwh-modbus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
franklinwh_modbus-0.9.2.tar.gz -
Subject digest:
b6aca4375a124e447f8447c155938010f5290dcfa447881f3097cae141bdfed1 - Sigstore transparency entry: 1176015217
- Sigstore integration time:
-
Permalink:
david2069/franklinwh-modbus@5ecc00c71495e8fc6758c322a51135c8969193aa -
Branch / Tag:
refs/tags/v0.9.2 - Owner: https://github.com/david2069
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5ecc00c71495e8fc6758c322a51135c8969193aa -
Trigger Event:
push
-
Statement type:
File details
Details for the file franklinwh_modbus-0.9.2-py3-none-any.whl.
File metadata
- Download URL: franklinwh_modbus-0.9.2-py3-none-any.whl
- Upload date:
- Size: 51.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8924735c810a167eef97b7f030cb0d7be559a28f70d5fd2153a62d045fe8a8a1
|
|
| MD5 |
be3246c943d5e545b93edf3381ed99b8
|
|
| BLAKE2b-256 |
64bc2c78e1643827d72df6ea1ff37c41a665f2dfb5679987ad7b29d8f6c185cf
|
Provenance
The following attestation bundles were made for franklinwh_modbus-0.9.2-py3-none-any.whl:
Publisher:
publish.yml on david2069/franklinwh-modbus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
franklinwh_modbus-0.9.2-py3-none-any.whl -
Subject digest:
8924735c810a167eef97b7f030cb0d7be559a28f70d5fd2153a62d045fe8a8a1 - Sigstore transparency entry: 1176015267
- Sigstore integration time:
-
Permalink:
david2069/franklinwh-modbus@5ecc00c71495e8fc6758c322a51135c8969193aa -
Branch / Tag:
refs/tags/v0.9.2 - Owner: https://github.com/david2069
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5ecc00c71495e8fc6758c322a51135c8969193aa -
Trigger Event:
push
-
Statement type: