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.

[!IMPORTANT] FranklinWH Modbus extensions are not writeable by default and will not function unless FranklinWH Support unlocks them or a future firmware release allows them. Even though these setters are implemented in software/the library, they will be non-functional for the vast majority of users (excluding those with SPAN or Lumin smart panels who have obtained installer unlock).

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

# 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, 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) 1=Backup, 2=Self-Consumption, 3=TOU
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.3.tar.gz (69.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.3-py3-none-any.whl (64.1 kB view details)

Uploaded Python 3

File details

Details for the file franklinwh_modbus-0.9.3.tar.gz.

File metadata

  • Download URL: franklinwh_modbus-0.9.3.tar.gz
  • Upload date:
  • Size: 69.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for franklinwh_modbus-0.9.3.tar.gz
Algorithm Hash digest
SHA256 fe17238119e1c32c93fceaaa441a2de6ad32c4637f381f16f72ec67254d3e41e
MD5 9864360a20ac955c94e6b24308d029a3
BLAKE2b-256 b29292d3029635cbc91459218e76edc1054a83bd9cfb3ec76e3f0d92fe6d3d18

See more details on using hashes here.

Provenance

The following attestation bundles were made for franklinwh_modbus-0.9.3.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.3-py3-none-any.whl.

File metadata

File hashes

Hashes for franklinwh_modbus-0.9.3-py3-none-any.whl
Algorithm Hash digest
SHA256 306018330e06a32714045c2c7e6a804a31b29fce5ae4035e7e78849bf0a5e30b
MD5 63506230756b8cc4961e1a70ae0a5927
BLAKE2b-256 a5863864cef754d7d86ed781ec09050e6beb4c2f8045949cd882ba012b7d9cc2

See more details on using hashes here.

Provenance

The following attestation bundles were made for franklinwh_modbus-0.9.3-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