Unofficial Python client library and CLI for FranklinWH energy storage systems via the Cloud API
Project description
FranklinWH Cloud Client
A Python client library for interacting with FranklinWH energy storage systems via the cloud API.
๐ฆ Package:
franklinwh-cloudโpip install franklinwh-cloud| import:from franklinwh_cloud import Client
๐ Fork of richo/franklinwh-python โ see FORK_ANALYSIS.md for a detailed comparison of additions (60+ API methods, 45+ sensor fields, TOU scheduling, power control, and more).
๐ API Citizenship: See API_CLIENT_GUIDE.md for rate limiting strategies, client identity headers, and how to prepare for authentication changes.
โ ๏ธ Disclaimer: This library is unofficial and not endorsed by FranklinWH. It interfaces with a proprietary Cloud API designed exclusively for the official FranklinWH mobile app and authorised installer portal โ not for third-party use. It is provided "AS IS" for educational and informational purposes only, with no warranty of fitness for any purpose. Use at your own risk. See the full disclaimer for details.
โจ Features
- Authentication: Automatic token management and refresh, homeowner + installer account types
- Real-time Data: Battery status, solar production, grid usage, home loads
- Mode Control: Switch between operating modes (Time-of-Use, Self-Consumption, Emergency Backup)
- TOU Schedules: Manage Time-of-Use scheduling with multi-season, weekday/weekend support
- Device Info: Gateway details, network status, device inventory, BMS cell-level data
- Performance Monitoring: API call metrics, response timing (min/avg/max), error rates, and CloudFront edge tracking
- CloudFront Edge Tracking: Automatic PoP location monitoring, failover detection, cache hit rates, and distribution ID tracking
- Client Identity: Honest identification headers for responsible API citizenship
- Rate Limiting: Opt-in client-side throttling with per-minute/hour/daily budgets
- Stale Data Cache: Per-endpoint TTL caching for graceful degradation when the cloud is slow or unavailable
- Modular Architecture: Domain-specific mixins (stats, modes, TOU, storm, power, devices, account)
- CLI Tool: Subcommand-based CLI with
fetchfor arbitrary endpoint access, debug tracing, JSON output
๐ง Technical Highlights
| Capability | Implementation |
|---|---|
| HTTP/2 | All API calls use httpx with http2=True โ multiplexed requests, header compression, persistent connections |
| CloudFront Monitoring | Tracks PoP edge location, cache hit rate, distribution IDs, and failover transitions in real-time |
| Configurable Base URL | Client(fetcher, gw, url_base=...) โ ready for FranklinWH DNS migration (energy. โ api.) |
| Dual Account Types | LOGIN_TYPE_USER (0) and LOGIN_TYPE_INSTALLER (1) constants for homeowner and installer login |
| Stale-While-Revalidate | Per-endpoint TTL cache returns last-known-good data during API outages |
| Modbus Mode Constants | Bidirectional CLOUD_TO_MODBUS_MODE / MODBUS_TO_CLOUD_MODE mapping for Modbus TCP integration |
| 70+ API Endpoints | Comprehensive coverage across TOU scheduling, power control, BMS, storm, smart circuits, compliance |
๐ฆ Installation
From wheel (recommended for downstream projects like FEM)
pip install dist/franklinwh_cloud_client-0.3.0-py3-none-any.whl
From source (editable, for development)
git clone https://github.com/david2069/franklinwh-cloud.git
cd franklinwh-cloud
python3 -m venv venv
source venv/bin/activate
pip install -e .
With test dependencies
pip install -e ".[test]"
From GitHub (direct)
# Latest (tracks main branch)
pip install git+https://github.com/david2069/franklinwh-cloud.git@main
# Pinned release (recommended for production / Docker)
pip install git+https://github.com/david2069/franklinwh-cloud.git@v0.3.0
โ๏ธ Configuration
Create franklinwh.ini with your credentials:
[energy.franklinwh.com]
email = your.email@example.com
password = your_password
[gateways.enabled]
serialno = YOUR_GATEWAY_SERIAL
Security Note: The .ini file is in .gitignore to protect your credentials.
Alternatively, set environment variables:
export FRANKLIN_USERNAME="your.email@example.com"
export FRANKLIN_PASSWORD="your_password"
export FRANKLIN_GATEWAY="YOUR_GATEWAY_SERIAL"
๐ Quick Start
Authentication Methods
The franklinwh-cloud library supports two distinct initialization architectures.
1. Preferred Method (Legacy Facade)
The easiest way to jump in and begin querying your battery. This wrapper handles standard username/password authentication, naturally locates your gateway serial, and binds the connection for you. Recommended for all standard scripts and basic automations.
import asyncio
from franklinwh_cloud import FranklinWHCloud
async def main():
client = FranklinWHCloud("user@email.com", "my_password")
await client.login()
await client.select_gateway()
stats = await client.get_stats()
print(f"Battery: {stats.current.battery_soc}%")
print(f"Solar: {stats.current.solar_production} kW")
print(f"Mode: {stats.current.work_mode_desc}")
asyncio.run(main())
2. Advanced / Future-Proof Method (Decoupled Client)
The Decoupled Client architecture separates the Authentication Engine from the Core API Dispatcher.
Future-Proofing: This method should be utilized when the existing legacy authentication method is no longer supported by FranklinWH Cloud API, and new token-based mechanisms (such as API Tokens, OAuth2, or JWT) become mandatory.
This method is also recommended for long-running services (like Home Assistant) that need to securely persist JWT tokens across reboots without storing plaintext passwords on disk.
import asyncio
from franklinwh_cloud.client import Client
from franklinwh_cloud.auth import TokenAuth
async def main():
# Inject a pre-authenticated OAuth/JWT token directly
fetcher = TokenAuth("jwt_token_string")
client = Client(fetcher, "10060006A02F241XXXX")
stats = await client.get_stats()
print(f"Battery: {stats.current.battery_soc}%")
asyncio.run(main())
CLI Tool
# System overview โ power, SOC, batteries, weather, grid, metrics
franklinwh-cli status
# Device discovery โ 3 verbosity tiers
franklinwh-cli discover # High-level: site, aGate, flags, state
franklinwh-cli discover -v # Verbose: + firmware, warranty, relays, accessories
franklinwh-cli discover -vv # Pedantic: + full firmware, NEM, PTO date
franklinwh-cli discover --json # Full JSON export
# Operating mode
franklinwh-cli mode
franklinwh-cli mode --set tou --soc 20
# TOU schedule inspection
franklinwh-cli tou --dispatch
# Direct API passthrough (33 methods available)
franklinwh-cli raw help
franklinwh-cli raw get_power_info
franklinwh-cli raw get_bms_info AP_SERIAL_NUMBER
# API metrics
franklinwh-cli metrics
# Real-time battery monitor (auto-refresh dashboard)
franklinwh-cli monitor # full dashboard, 30s refresh, Ctrl+C to exit
franklinwh-cli monitor -i 10 # refresh every 10 seconds
franklinwh-cli monitor -d 5 # run for 5 minutes then stop
franklinwh-cli monitor --compact # single-line mode (no screen clearing)
franklinwh-cli monitor --json # JSON stream per interval
Output modes:
franklinwh-cli status --json # JSON output
franklinwh-cli status --no-color # disable ANSI colours
Debug & tracing:
franklinwh-cli status -v # API call summaries
franklinwh-cli status -vv # + request/response headers
franklinwh-cli status -vvv # + full raw JSON payloads
franklinwh-cli tou --trace tou # only TOU mixin debug (46 log points!)
franklinwh-cli status --trace all # everything
franklinwh-cli status --api-trace # per-call timing
franklinwh-cli status -vv --log-file debug.log
๐๏ธ Architecture
graph TB
CLI["franklinwh-cli"] --> Client
FEM["FEM / HA Addon"] --> Client
Scripts["Python Scripts"] --> Client
subgraph "franklinwh_cloud"
Client["Client<br/>(composes all mixins)"]
Client --> API["api.py<br/>HTTP transport, auth"]
API --> RL["RateLimiter"]
API --> ET["EdgeTracker<br/>CloudFront PoP"]
API --> SC["StaleDataCache"]
Client --> Discover["mixins/discover.py"]
Client --> Stats["mixins/stats.py"]
Client --> Modes["mixins/modes.py"]
Client --> TOU["mixins/tou.py"]
Client --> Power["mixins/power.py"]
Client --> Devices["mixins/devices.py"]
Client --> Storm["mixins/storm.py"]
Client --> Account["mixins/account.py"]
end
API --> CF["CloudFront CDN"]
CF --> FW["FranklinWH Cloud"]
FW -->|"FranklinWH Official Client<br/>(sendMqtt format)"| aGate[aGate]
MB["franklinwh_modbus"] -->|"Modbus TCP<br/>(SunSpec/Raw)"| Net["Network<br/>(LAN / WiFi / Remote)"]
Net -->|port 502| aGate
aGate --> aPower[aPower Batteries]
aGate --> PV[Solar PV]
aGate --> SmC[Smart Circuits]
style CLI fill:#22c55e,color:#fff
style Client fill:#3b82f6,color:#fff
style ET fill:#eab308,color:#000
style FW fill:#7c3aed,color:#fff
style MB fill:#d97706,color:#fff
style Net fill:#6b7280,color:#fff
style aGate fill:#059669,color:#fff
Terminology Disambiguation:
sendMqttvs True MQTT
Throughout this project, you will see references tosendMqttpayload wrappers. Please note that the FranklinWH Cloud API executes these commands exclusively over standard HTTPS REST endpoints (e.g.,POST /hes-gateway/manage/sendMqtt). This library does not establish a true, continuous MQTT TCP/WebSocket local connection to the aGate broker. The termsendMqttis merely FranklinWH's internal naming convention for the HTTP-encapsulated JSON wrapper they use to tunnel polling requests from the Cloud down to the physical hardware.
franklinwh_cloud/
โโโ client.py # Client class (inherits all mixins)
โโโ models.py # Stats, Current, Totals, GridStatus dataclasses
โโโ api.py # HTTP transport, auth, session management
โโโ exceptions.py # Custom exception hierarchy
โโโ metrics.py # ClientMetrics โ API call instrumentation
โโโ discovery.py # DeviceSnapshot dataclass
โโโ const/ # Operating modes, TOU, device constants
โ โโโ modes.py, tou.py, devices.py, device_catalog.json
โโโ mixins/ # Domain-specific API method groups (8 modules)
โ โโโ discover.py # client.discover() โ 3-tier device survey
โ โโโ stats.py # get_stats, get_runtime_data, get_power_by_day
โ โโโ modes.py # get_mode, set_mode, get_mode_info
โ โโโ tou.py # TOU schedule CRUD + dispatch details
โ โโโ storm.py # weather, storm hedge settings
โ โโโ power.py # grid status, PCS settings, power control
โ โโโ devices.py # device info, BMS, composite info
โ โโโ account.py # site info, notifications, alarms, warranty
โโโ cli.py # CLI entry point
โโโ cli_output.py # Terminal rendering + colour utilities
โโโ cli_commands/ # CLI subcommand modules
โโโ status.py # Power flow, SOC, mode, weather, grid
โโโ discover.py # Device discovery โ 3 tiers, system readiness
โโโ monitor.py # Real-time dashboard (full/compact/JSON)
โโโ metrics.py # API probe + CloudFront edge data
โโโ bms.py # Battery Management System inspection
โโโ diag.py # System diagnostics report
โโโ tou.py # TOU schedule with dispatch details
โโโ mode.py # Operating mode get/set
โโโ raw.py # Direct API method calls
๐ Documentation
| Document | Description |
|---|---|
| API_CLIENT_GUIDE.md | Rate limiting, CloudFront edge tracking, metrics, monitor usage |
| franklinwh_openapi.json | Unofficial Swagger v3 schema mapped from 123+ raw HTTP .har intercepts |
| FORK_ANALYSIS.md | Detailed comparison with upstream richo/franklinwh-python |
| HISTORY.md | Project timeline from fork to independence |
| UPSTREAM_STRATEGY.md | Contributing back to upstream โ the trust ladder |
| CHANGELOG.md | Version history and release notes |
| CONTRIBUTING.md | Development setup, code standards, PR process |
| ISSUES.md | How to report bugs and request features |
๐งช Testing
# Unit tests only (no API credentials needed)
pytest -m "not live" -q
# Live API tests (requires franklinwh.ini or env vars)
pytest -m live -v
# All tests
pytest -v
# Record results for traceability (AP-11)
./tests/run_and_record.sh CLI-refactor
cat tests/results/test_history.log
Current coverage: 286 tests across all 8 domains
๐ API Reference
Client Methods
| Domain | Key Methods |
|---|---|
| Discovery | discover(tier=1) โ DeviceSnapshot โ site, aGate, batteries, flags, accessories, warranty |
| Stats | get_stats(), get_runtime_data(), get_power_by_day(date), get_power_details(type, date) |
| Modes | get_mode(), set_mode(mode, soc), get_mode_info() |
| TOU | get_tou_info(type), set_tou(schedule), get_gateway_tou_list(), get_tou_dispatch_detail() |
| Tariff | get_utility_companies(), get_tariff_list(), get_tariff_detail(), apply_tariff_template() |
| Storm | get_weather(), get_storm_settings(), get_storm_list() |
| Power | get_grid_status(), get_power_control_settings(), get_power_info() |
| Devices | get_device_info(), get_bms_info(serial), get_device_composite_info() |
| Account | siteinfo(), get_warranty_info(), get_alarm_codes_list(), get_notification_settings() |
| Metrics | get_metrics() โ {total_api_calls, avg_response_time_s, calls_by_method, errors, ...} |
Data Structures
stats.current.battery_soc # Battery State of Charge (%)
stats.current.solar_production # Solar production (kW)
stats.current.grid_use # Grid usage (kW, negative = export)
stats.current.home_load # Home consumption (kW)
stats.current.work_mode_desc # Operating mode name
stats.current.grid_status # GridStatus enum (NORMAL/DOWN/OFF)
stats.totals.solar # Daily solar production (kWh)
stats.totals.grid_import # Daily grid import (kWh)
stats.totals.home_use # Daily home consumption (kWh)
๐บ๏ธ Roadmap
Installer Account Support (CLI-only, read-only)
The FranklinWH Cloud API supports installer accounts โ these are privileged accounts used by solar installers to manage fleets of customer aGate gateways. The login endpoint (appUserOrInstallerLogin) supports both account types via a type parameter: 0 = app user, 1 = installer.
Already implemented:
LOGIN_TYPE_USER = 0andLOGIN_TYPE_INSTALLER = 1constantslogin_typeparameter onTokenFetcher,login(), and_login()- Configurable
url_baseonClientfor future DNS changes
Planned scope:
- CLI
--installerflag to authenticate as an installer discovercommand to list all customer gateways in the installer's fleetstatuscommand with--gateway SNto view any customer's system- Read-only only โ no write operations (mode changes, TOU updates) via installer CLI
- Per-gateway selection required (no fleet-wide operations)
Usage:
from franklinwh_cloud.client import TokenFetcher, LOGIN_TYPE_INSTALLER
# Installer login
fetcher = TokenFetcher("installer@company.com", "password", login_type=LOGIN_TYPE_INSTALLER)
client = Client(fetcher, "CUSTOMER_GATEWAY_SN")
info = await client.siteinfo() # returns installerId, userTypes, roles, etc.
โ ๏ธ Installer accounts can access and modify multiple customer sites. This library intentionally limits installer support to read-only CLI operations as a matter of responsible API citizenship. Developers who fork this library assume their own responsibility for write operations.
๐ค Contributing
See CONTRIBUTING.md for development setup, code standards, API citizenship requirements, and pull request process.
See ISSUES.md for how to report bugs and request features.
๐ License
๐ Telemetry & Privacy
This project strictly enforces opt-in telemetry to understand community usage patterns through Scarf and PostHog when actively configured. Because the gateway holds sensitive credentials and hardware mapping, we mathematically never log PII, serial numbers, IP addresses, or gateway configurations.
See the explicitly audited Telemetry & Privacy Policy for full compliance details on PyPI and Home Assistant integrations.
MIT License with Additional Terms โ see LICENSE for details.
The Additional Terms address the specific risks of interacting with undocumented energy equipment APIs. You must read and understand the LICENSE before using this software.
๐ Acknowledgments
- FranklinWH - For innovative energy storage systems
- richo - Original library foundation
- This project was developed with AI assistance (Claude, Gemini)
โ๏ธ Disclaimer
UNOFFICIAL SOFTWARE โ NOT AFFILIATED WITH FRANKLINWH
By using this software, you confirm that you have read and understood the LICENSE and its Additional Terms.
This software is provided AS-IS, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement. Use entirely at your own risk.
This library interacts with FranklinWH's undocumented cloud API, which may change, break, or become unavailable without notice. The authors accept no liability for service interruptions, data loss, equipment damage, or any other consequences arising from the use of this software.
MIT License โ see LICENSE for details.
This disclaimer is also logged once at startup by the library for audit trail purposes.
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_cloud-0.4.7.tar.gz.
File metadata
- Download URL: franklinwh_cloud-0.4.7.tar.gz
- Upload date:
- Size: 197.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 |
fdab6abf900ee58aef7a0af7354b82f220ba69edfc3b4e256535dc690110cd3a
|
|
| MD5 |
edc999c0a816d5226d7503647c5995fc
|
|
| BLAKE2b-256 |
53ce3f499130f6858dcd2301e246b408d800acba08b4fc60d36ec477179b82db
|
Provenance
The following attestation bundles were made for franklinwh_cloud-0.4.7.tar.gz:
Publisher:
publish.yml on david2069/franklinwh-cloud
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
franklinwh_cloud-0.4.7.tar.gz -
Subject digest:
fdab6abf900ee58aef7a0af7354b82f220ba69edfc3b4e256535dc690110cd3a - Sigstore transparency entry: 1239342019
- Sigstore integration time:
-
Permalink:
david2069/franklinwh-cloud@516ede8a54756a8633ad0b496867832adbbe96b0 -
Branch / Tag:
refs/tags/v0.4.7 - Owner: https://github.com/david2069
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@516ede8a54756a8633ad0b496867832adbbe96b0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file franklinwh_cloud-0.4.7-py3-none-any.whl.
File metadata
- Download URL: franklinwh_cloud-0.4.7-py3-none-any.whl
- Upload date:
- Size: 175.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 |
b56a43ca6aa4836b66e63de5f9dd68b2336198c8551d938d7ddd46d8784779af
|
|
| MD5 |
0bb67dad50ece6d5169602f677ffce9a
|
|
| BLAKE2b-256 |
f1e9bf62b6838f496e92b92cdcaa889ed3728aa949f93ff001e9e0c70fbc7c95
|
Provenance
The following attestation bundles were made for franklinwh_cloud-0.4.7-py3-none-any.whl:
Publisher:
publish.yml on david2069/franklinwh-cloud
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
franklinwh_cloud-0.4.7-py3-none-any.whl -
Subject digest:
b56a43ca6aa4836b66e63de5f9dd68b2336198c8551d938d7ddd46d8784779af - Sigstore transparency entry: 1239342021
- Sigstore integration time:
-
Permalink:
david2069/franklinwh-cloud@516ede8a54756a8633ad0b496867832adbbe96b0 -
Branch / Tag:
refs/tags/v0.4.7 - Owner: https://github.com/david2069
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@516ede8a54756a8633ad0b496867832adbbe96b0 -
Trigger Event:
push
-
Statement type: