Skip to main content

FranklinWH aGate Modbus TCP library โ€” SunSpec + extension registers

Project description

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.

๐Ÿ“– 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: Core library under active development, targeting PyPi publication.

Quick Links

โš ๏ธ 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 N or send_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

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

franklinwh_modbus-0.9.2.tar.gz (57.8 kB view details)

Uploaded Source

Built Distribution

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

franklinwh_modbus-0.9.2-py3-none-any.whl (51.6 kB view details)

Uploaded Python 3

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

Hashes for franklinwh_modbus-0.9.2.tar.gz
Algorithm Hash digest
SHA256 b6aca4375a124e447f8447c155938010f5290dcfa447881f3097cae141bdfed1
MD5 8302bcd5398a6a872bcd8667574709ac
BLAKE2b-256 8999c0866bc3529c2f737eaa739402d16bb27e9f6ae74da1086d07a252a2b8bc

See more details on using hashes here.

Provenance

The following attestation bundles were made for franklinwh_modbus-0.9.2.tar.gz:

Publisher: publish.yml on david2069/franklinwh-modbus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file franklinwh_modbus-0.9.2-py3-none-any.whl.

File metadata

File hashes

Hashes for franklinwh_modbus-0.9.2-py3-none-any.whl
Algorithm Hash digest
SHA256 8924735c810a167eef97b7f030cb0d7be559a28f70d5fd2153a62d045fe8a8a1
MD5 be3246c943d5e545b93edf3381ed99b8
BLAKE2b-256 64bc2c78e1643827d72df6ea1ff37c41a665f2dfb5679987ad7b29d8f6c185cf

See more details on using hashes here.

Provenance

The following attestation bundles were made for franklinwh_modbus-0.9.2-py3-none-any.whl:

Publisher: publish.yml on david2069/franklinwh-modbus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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