FranklinWH Local (Direct Connect) API — the two Python implementations¶
There are two independent, open Python libraries for the FranklinWH aGate's local protocol — the cloudless TCP service the official app uses for installation and its "Direct Connect" menu. This document compares them by what matters functionally: what you can actually do with the aGate through each, how far each covers the device's capabilities, and how their documentation differs. They are not forks of each other.
franklinwh-direct-connect (this repo) |
voidstarr/franklinwh_local |
|
|---|---|---|
| Repo | david2069/franklinwh-direct-connect-api |
github.com/voidstarr/franklinwh_local |
| PyPI | franklinwh-direct-connect-api |
— (source only) |
| License | MIT | MIT |
| Focus | full data layer + control + tooling + emulator | transport + command table |
| Client methods | 52 | ~10 |
| Device capabilities covered | 70 cmdTypes, with hardware-verified semantics | 44-command table (names/opcodes) |
| Reads (battery cells, PE, energy, TOU, generator…) | ✅ deep | ⚠️ a few high-level queries |
| Writes / control (mode, circuits, generator, off-grid, DER) | ✅ with read-back verification | ⚠️ SunSpec-Modbus enable only |
| Emulator / mock aGate | ✅ dynamic, multi-aPower | ❌ |
| LAN discovery / scan | ✅ | ❌ |
| CLI (~20 subcommands) | ✅ | ❌ |
| Docstrings & worked examples | ✅ rich (79 docstrings in the client alone) | ✅ README example |
| Reference application (real-world) | ✅ full-stack bridge (~16k LOC) | ❌ |
| Home-Assistant integration | ✅ MQTT discovery + REST | ❌ |
| Documentation | layered (usage/API/mapping) + verified catalog | one excellent protocol README |
TL;DR — this library is a complete functional stack for operating the aGate; voidstarr's is a compact, well-documented transport core. The protocol itself is solved by both and is not a differentiator (see the footnote).
1. What you can do with the aGate¶
This is the part that matters — the protocol is just the pipe; the functionality is what reaches through it.
Reads¶
| Capability | This library | voidstarr |
|---|---|---|
| Live power flow / SoC / mode | ✅ | ✅ (query_mode, device info) |
| Login/firmware manifest | ✅ | ✅ |
| Solar / EMS | ✅ | ✅ |
| Per-cell BMS (voltages, temps, pack health) | ✅ | ❌ |
| Power electronics / DC bus, device states | ✅ | ❌ |
| Multi-aPower rosters (per-unit) | ✅ | ❌ |
| Energy history (96 quarter-hour points + kWh totals + TOU tiers) | ✅ | ❌ |
| Energy rollups (week/month/year/total, computed locally) | ✅ | ❌ |
| TOU tariff schedule as readable blocks | ✅ | ❌ |
| Generator status/config | ✅ | ❌ |
| Grid-compliance profile (fans out ~25 cmdTypes) | ✅ | ❌ |
| SunSpec-Modbus / IEEE-2030.5 settings | ✅ | ✅ (read) |
Writes / control (this is where the gap is widest)¶
| Control | This library | voidstarr |
|---|---|---|
| Set operating mode (Self-Consumption / TOU / Backup) | ✅ | ❌ |
| Smart-circuit on/off | ✅ (RMW + read-back verify) | ❌ |
| Off-grid on/off + hold SoC | ✅ | ❌ |
| Generator settings (windows / exercise / SoC thresholds) | ✅ | ❌ |
| DER comms (enable SunSpec Modbus / 2030.5) | ✅ | ✅ (SunSpec enable) |
| Reboot | ✅ | ❌ |
Every write here is a full-block read-modify-write with read-back verification
(the aGate rejects partial frames and a result:0 ack does not prove the change
applied) — so set_smart_circuit, set_mode, etc. only report success when a
re-read confirms it.
Tooling around the API¶
- Emulator — a deterministic per-seed synthetic aGate + N aPowers, wire-faithful for any serial, serving live telemetry, energy history, smart circuits, and persisting writes (toggle a mock circuit and it sticks). No hardware needed to develop against.
- Discovery — LAN scan for gateways on TCP 9000 with a real login handshake.
- Proxy / pcap decode / analyze — a transparent decoding relay and offline capture tooling.
- CLI — ~20 subcommands (
battery,mode,tou,energy_rollup,grid_profile,der_comms,scan,health,emulate, …). - Home-Assistant bridge — the companion
franklinwh-local-bridge.
voidstarr provides LocalClient with ~10 query helpers plus set_sunspec_modbus, and
raw request() for anything else — a solid base to build on, but the functional
surface stops at the transport plus a handful of reads.
2. Command coverage¶
This library documents 70 cmdTypes with prose semantics — often the date and method each was hardware-verified, and explicit "what is NOT established" caveats. Its value is knowing what each command means and whether it is safe to write.
voidstarr documents the 44-entry CommunicationCmd enum exactly as the app names
it (index / name / req_code / resp_id), lifted from the app binary. Its value is
canonical naming and opcodes — a useful cross-check against this library's
capture-derived catalog. (Tracked as a cross-reference in BACKLOG.md.)
One correction this comparison settled: voidstarr's table carries an optType field
per command, but that is an internal app field, not a wire key — captures never
send it; the real requests use plain opt. This library correctly omits it.
3. Documentation¶
This repo ships layered docs — README.md, docs/USAGE.md (how-to),
docs/API.md (reference), docs/CLOUD_MAPPING.md (local↔cloud command mapping) — plus
a large BACKLOG.md of open questions and hardware-verification results, and the
catalog itself, which doubles as per-command documentation.
voidstarr ships a single, unusually good README.md: a precise protocol spec with
an envelope field table and a per-item "verified against the capture" ledger. For its
scope it is excellent; it is a protocol document more than an API one.
4. Developer experience & the reference application¶
For a library, the code you read is as important as the code that runs. This is where the difference is largest, and it is what most developers actually feel.
Docstrings. Every public method here carries a real docstring — what it reads or
writes, the cmdType, the payload shape, the sign conventions, and the caveats (client.py
alone has 79 docstring blocks). Writes document their read-modify-write recipe and how
success is verified. voidstarr's code is clean and typed with a solid README example, but
the per-call documentation is lighter.
Examples. A quick-start in the README, a how-to in docs/USAGE.md, and — uniquely —
a CLI of ~20 subcommands that is itself a set of worked examples: every command is a
call into the library you can read, run offline against the emulator, or point at real
hardware.
The reference application — the Local Bridge, real-world example writ large.
The companion franklinwh-local-bridge is not a toy demo; it is a complete
application built on this library, and the best documentation of what the library can do:
- ~8,000 lines of Python — a FastAPI service exposing 103 REST endpoints, a per-gateway polling supervisor, an in-process mock-aGate manager, and a metrics store.
- Publishes to Home Assistant over MQTT (discovery + state), so the aGate shows up as native HA entities — with the synthetic/mock path gated off by default.
- ~9,000 lines of HTML + JavaScript — a rich optional web UI (Alpine.js + Chart.js, 13 tabs: dashboard, battery/per-cell, solar, smart circuits, generator, scheduler, logs, settings, Home-Assistant, …) with live charts, a Sankey energy flow, a real-time watch-live mode, and a full Gateways manager.
- Runs as a Docker container or a Home-Assistant add-on.
So a developer evaluating this library doesn't have to imagine what it enables — they can run a production-grade app that exercises nearly the entire surface (reads, verified writes, MQTT, REST, and a web UI) against real hardware or the built-in emulator.
voidstarr's library ships no application, UI, MQTT, or REST layer — it is a transport core to build those things on, not an example of them.
5. When to use which¶
- Building an integration, dashboard, automation, or HA setup → this library: it has the reads, the verified control surface, the emulator, discovery, and the bridge.
- You want a tiny, dependency-free base to build your own client on → voidstarr's.
- Both → this library already took voidstarr's seed formula; its canonical command enum remains a handy naming cross-check.
Footnote — the protocol is not a differentiator¶
Both libraries implement the same obfuscated framing (a per-byte additive cipher over
the payload, a CRC-32 envelope, response = request + 1, one frame per segment). It is
technically interesting, but functionally it is solved, fixed plumbing — FranklinWH
is not going to change it, and once decoded it never needs thinking about again. The
one wire-level detail with any functional consequence was the seed, which is derived
from the serial: this library previously hardcoded it (correct only for its own gateway)
and adopted voidstarr's per-serial formula so it can talk to any gateway. Beyond that,
the framing is invisible to everything above it.
Credit: the per-serial seed formula and the CommunicationCmd enum are the work of
voidstarr/franklinwh_local (MIT). Written 2026-09-19; both libraries evolve, so
re-check the repos for current coverage.