Skip to content

Latest commit

 

History

425 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FranklinWH Cloud Client

Tests

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 fetch for 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
Loading

Terminology Disambiguation: sendMqtt vs True MQTT
Throughout this project, you will see references to sendMqtt payload 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 term sendMqtt is 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 = 0 and LOGIN_TYPE_INSTALLER = 1 constants
  • login_type parameter on TokenFetcher, login(), and _login()
  • Configurable url_base on Client for future DNS changes

Planned scope:

  • CLI --installer flag to authenticate as an installer
  • discover command to list all customer gateways in the installer's fleet
  • status command with --gateway SN to 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.

About

FranklinWH Cloud API client — Python library for battery monitoring, mode control, TOU scheduling, and power management

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages