Local-first home energy coordination.
FTW is a local-first home energy management system (EMS). It coordinates solar, batteries, grid power, EV charging and thermal assets on a Raspberry Pi or Linux host. The safety-critical runtime is one Go binary, hardware integrations are sandboxed Lua drivers, and a compiled Energyplan worker handles long-horizon planning.
The control path stays on the local network. Cloud price, weather and device integrations degrade independently; they are not required for safe local operation.
FTW should make mixed equipment simple to live with: useful first-day planning, reliable daily charging, fast and honest live feedback, and structured access for agents. The default experience should need few choices while keeping expert controls and Lua drivers available.
VISION.md is the product direction set by Fredrik. docs/roadmap.md lists the outcomes and proof needed. These include goals that have not shipped; the capability list below is separate.
Sourceful Energy maintains FTW Community under AGPL-3.0-only with the Energyplan combination permission. Contributions are welcome, preferably starting with issues. Share a short Markdown proposal or a focused fix with relevant test evidence. See CONTRIBUTING.md. Community help is best effort; SUPPORT.md describes separate commercial services.
FTW has three explicit modules:
- Core owns configuration, telemetry, state, safety, dispatch, API and UI.
- Drivers translate vendor protocols and power signs in isolated Lua VMs.
- Optimizer proposes plans over a versioned contract; core validates every result and keeps a Go fallback.
Drivers and the optimizer can evolve without moving safety authority out of Core. A new module needs a concrete reason and must reduce the complexity of the whole product. See docs/architecture.md.
- self-consumption, peak shaving and explicit grid targets;
- multi-battery allocation with fuse, SoC, slew and stale-data protection;
- price-, weather-, PV- and load-aware planning;
- EV charging, V2X and thermal planning;
- local web UI, with history and configuration in SQLite;
- Home Assistant MQTT discovery;
- hot-reloadable, independently released Lua drivers;
- a built-in OCPP 1.6J + 2.0.1 server, so OCPP chargers connect with no driver.
The local catalog is generated from DRIVER metadata. The public
srcfl/device-drivers repo is the
editable source and FTW's default signed driver channel.
This repository holds no driver source. drivers/*.lua is gitignored and
fetched from that repository at the commit pinned in
drivers/BUNDLED_SOURCE.json:
make driversThe files still ship. FTW's offline recovery set exists because startup is
deliberately local — a gateway boots and runs without the network, so a remote
refresh must never block it — so the image, the release tarballs and the tests
all read drivers/. They are simply fetched rather than committed, which is
why a driver cannot be edited here at all. There is no file to open a pull
request against; fix it upstream and move the pin. CI fails if one is
committed.
Moving the pin is a driver release. make driver-versions-across-pin compares
the drivers at the old pin against the drivers at the new one and fails when a
file changed without its DRIVER version moving — the signed channel refuses to
publish changed bytes under a version it already published, and a gateway
offered the same version twice cannot tell them apart.
The pin needs watching in both directions. Every pull request runs
--check, which catches a driver edited here. A daily job runs --behind,
which catches the opposite: the pin left in place while a fix lands upstream.
It opens one issue and keeps it up to date, and stays quiet unless a bundled
driver actually changed.
You install FTW yourself and choose how it runs: natively with systemd, or in Docker. Both use the same checksummed release package. 0.x is in beta; docs/native-beta.md has both paths, the everyday commands and recovery. On a fresh 64-bit Raspberry Pi OS, Debian or Ubuntu host, the native install is:
tag=v0.136.3-beta.1 # the newest beta on the Releases page
curl -fsSLO "https://raw.githubusercontent.com/srcfl/ftw/${tag}/scripts/install.sh"
bash install.sh --fresh-host --tag "${tag}"--fresh-host confirms there is no
existing FTW site, even a stopped Docker site in a custom directory. The
installer checks the package and checksum, creates native release slots under
/opt/ftw, and starts the local
Core service. It does not install Docker. Open http://<host>:8080/setup on
the LAN, then check storage health and live device readings. If the first
install is interrupted, use --resume --tag with the same tag after checking
the service log; it keeps any data the first attempt created.
Give the FTW machine a DHCP reservation (a fixed IP) in your router. Devices that dial in to FTW — OCPP chargers store their backend URL at commissioning, and some hardware whitelists which addresses may talk to it — silently lose the connection if DHCP later hands the host a different address.
Existing 1.x, 2.x, 3.x and earlier native sites stay on their current version until the guided 0.x migration is ready. The fresh installer refuses them; do not use Update or old Docker migration scripts to cross release lines.
The on-box dashboard remains local. The optional FTW webapp connects through an encrypted session and blind relay; relay loss does not stop local control. Cloud MCP access is a product goal, not an endpoint provided by this installation guide.
The official Home Assistant app repository is
srcfl/home-assistant-addons.
It publishes beta builds of the older 3.x line; the 0.x line is not in the
app yet. There is no stable app — Home Assistant OS and Supervisor
qualification has to finish first — so treat it as a beta rather than a
production install.
Open Settings → Apps → App store → Repositories in Home Assistant and add:
https://github.com/srcfl/home-assistant-addons
Check the add-on repository's
compatibility record
before each install or update. Report Home Assistant install, update, backup,
restore, or container faults in its
issue tracker.
Report Core, API, UI, control, or state faults in this repository. Report driver
faults to srcfl/device-drivers.
Requirements are Go and Node.js. Python 3 verifies release artifacts during development; no Python interpreter or service runs the planner.
git clone https://github.com/srcfl/ftw.git
cd ftw
make devUseful checks:
make test # Go tests
npm test # web
make verify # fast test, compose, vet and build checks
make e2e # simulator-backed full stack
make ci # e2e, builds and browser smokeSee docs/development.md for the small set of development workflows.
config.example.yaml and the validation types in
go/internal/config are the configuration reference. Copy the example for a native development setup:
cp config.local.example.yaml config.local.yamlPower values above a driver use one convention: positive means into the site, negative means out. Drivers alone translate vendor conventions. Read docs/site-convention.md before editing power math or writing a driver.
Drivers are plain Lua files and need no compilation. A driver declares its catalog metadata, lifecycle and required capabilities in one file. The Go host provides capability-scoped Modbus, MQTT, serial, HTTP, WebSocket and TCP access.
Which devices are covered, and on what evidence, is published as a searchable
catalog: Device driver catalog.
It is generated from srcfl/device-drivers on every push to that repository's
main, so the versions, tested models and per-target status it shows are the
ones FTW installs. Listing is not an install claim: the page states which
drivers have been confirmed against physical hardware and which have not.
Start with docs/writing-a-driver.md. Send shared
driver changes to srcfl/device-drivers. That repo publishes one signed,
content-addressed asset per driver and version. FTW downloads only the chosen
driver, which can update or roll back without a new FTW core release. Device
Support may consume the same public source later for other products or a higher
support level.
EV chargers that speak OCPP are the exception: they need no driver. FTW runs an OCPP Central System (1.6J and 2.0.1), so the charger connects and registers itself. See docs/ocpp.md.
There are two channels:
- beta receives every merged change in the next beta, aiming for one a week;
- stable promotes the exact commit already published and tested as beta.
There is no edge channel. Beta is the shared playground: run it on a real
site and report what you find as an issue naming the beta version you saw it
on. An issue marked release-blocker stops that line from promoting. A beta
promotes to stable after a week on the home box and at least one other real
site with no open release-blocker.
Changesets produce versions and changelog entries; the native release
workflow builds the checksummed Linux packages that the installer, ftw update and the Docker files use. The repository owner cuts every release.
Details for operators are in docs/self-update.md; the
full maintainer rules, including the exceptional repair path for the old
Docker line, are in the Releases section of AGENTS.md.
The repository deliberately keeps prose small. Code, types, tests and driver metadata are the detailed reference.
- Architecture
- Product roadmap
- Power sign convention
- Safety invariants
- Operations and recovery
- Full backup and safe restore
- Writing a driver
- Device driver catalog — every supported device and the evidence behind it
- OCPP chargers (no driver needed)
- Self-update and release channels
- Status of old Docker upgrades
- Home Assistant
Other files under docs/ are focused installation or
external-integration guides.
Read CONTRIBUTING.md. User-visible changes need a Changeset.
AGPL-3.0-only with the Energyplan combination permission — see LICENSE and LICENSING.md. Energyplan binaries have separate household-use terms; commercial use of those binaries needs a Sourceful agreement. Earlier versions retain their earlier licenses.