-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathconfig.example.yaml
More file actions
277 lines (259 loc) · 11.9 KB
/
Copy pathconfig.example.yaml
File metadata and controls
277 lines (259 loc) · 11.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# First boot imports this file into SQLite. After import, use FTW Settings.
# Core records config_database here; later edits to these values do not reload.
# FTW config example
# Copy to config.yaml and edit for your site.
site:
name: "Home"
control_interval_s: 2 # 2 s matches Ferroamp ehub ~1 Hz; drop to 1 for snappier response
grid_target_w: 0 # 0 = self-consumption
grid_tolerance_w: 42 # deadband (The Answer)
watchdog_timeout_s: 60
smoothing_alpha: 0.3
slew_rate_w: 3000 # W per cycle. Both Ferroamp and Sungrow ramp internally — this is a soft ceiling, not the primary protection
# slew_enabled: false # disable the external slew limiter entirely and trust the inverter's internal ramp control (experimental)
min_dispatch_interval_s: 2 # debounce; keep == control_interval_s
fuse:
max_amps: 16
phases: 3
voltage: 230
# ----------------------------------------------------------------------
# Drivers — Lua scripts with per-driver capabilities.
# Capabilities are the ONLY way the sandbox talks to the outside world.
# A driver with only mqtt capability can't touch modbus, and vice versa.
# ----------------------------------------------------------------------
drivers:
- name: ferroamp
lua: drivers/ferroamp.lua
is_site_meter: true # exactly one driver must have this
battery_capacity_wh: 15200
capabilities:
mqtt:
host: 192.168.1.153
port: 1883
username: extapi
password: ferroampExtApi
- name: sungrow
lua: drivers/sungrow.lua
battery_capacity_wh: 9600
capabilities:
modbus:
host: 192.168.1.10
port: 502
unit_id: 1
# proxy_listen: ":1502" # only needed when modbus_proxy is on and this is not the only Modbus device
# Pixii PowerShaper. When troubleshooting mode is enabled in Settings,
# Pixii also exposes calibration/control status and setpoint readback metrics.
# - name: pixii
# lua: drivers/pixii.lua
# is_site_meter: true
# battery_capacity_wh: 20000
# observe_only: true # retailer VPP controls actuation — FTW reads only
# capabilities:
# modbus:
# host: 192.168.1.50
# port: 502
# unit_id: 1
# V2X chargers are modeled separately from stationary batteries.
# They emit signed v2x_charger.w telemetry:
# +W = vehicle charging, -W = vehicle discharging into the site/grid.
# Manual test setpoints can be sent with POST /api/v2x/command.
#
# - name: ferroamp_dc2
# lua: drivers/ferroamp_dc2_v2x.lua
# capabilities:
# mqtt:
# host: 192.168.1.70
# port: 1883
# username: dc2
# password: dc2mqtt!
#
# - name: ambibox
# lua: drivers/ambibox_v2x.lua
# capabilities:
# # Lets FTW resolve the name itself over mDNS. Without it the name is
# # left to the system resolver, which answers on some platforms only.
# allow_unverified_local: true
# mqtt:
# host: sid-os.local
# port: 1883
# Sourceful Zap — P1/HAN site meter via the official local API.
# Default is the meter only. If Zap also lists an inverter or battery,
# add that device in FTW with its own driver when you can. Set read_pv
# / read_battery only when Zap is the only reader (closed Modbus, or
# an RS-485 bus Zap already owns). See docs/sourceful-zap.md.
#
# - name: sourceful-zap
# lua: drivers/zap.lua
# is_site_meter: true
# capabilities:
# # Lets FTW resolve zap.local itself. Without it the name is left to
# # the system resolver — which answers under Home Assistant, but not on
# # a plain Compose or Pi install.
# allow_unverified_local: true
# http:
# allowed_hosts: ["zap.local"] # .local is resolved by FTW over mDNS
# config:
# host: zap.local
# # meter_serial: p1m-... # optional; P1/HAN is auto-selected
# # read_pv: true # opt-in; default is P1/HAN only
# # read_battery: true # opt-in; telemetry only
# NIBE S-series heat pump — read-only telemetry over the on-prem Local
# REST API (HTTPS + Basic auth). The pump presents a SELF-SIGNED cert, so
# pin it: tls_pin_sha256 is the cert SHA-256 fingerprint (the
# "fingeravtryck" shown in the myUplink app, or from `openssl s_client
# -connect <ip>:8443 | openssl x509 -fingerprint -sha256`). The host then
# trusts exactly that one cert — never a blanket insecure-skip-verify.
# Read-only by default: the driver issues no writes at all. (The pump-side
# write gate is the installer's read-only/read-write choice for the Local
# REST API, menu 7.5.15 — NOT "aid mode", which is a compressor-off fault
# state unrelated to API permissions.) One opt-in write path exists — the
# pump's native Solar PV surplus feed (srcfl/ftw#537, developed in
# srcfl/device-drivers). It needs BOTH the host capabilities.http.allow_write
# grant below AND write.solar_pv: true in the driver config; leave either off
# and the driver stays read-only.
# - name: nibe
# lua: drivers/nibe_local.lua
# config:
# host: 192.168.1.180
# port: 8443
# username: <local-api-username>
# password: <local-api-password>
# # write: # opt-in Solar PV feed (off unless present)
# # solar_pv: true
# # max_w: 9000 # REQUIRED clamp ceiling (site PV nameplate, W)
# capabilities:
# http:
# allowed_hosts: ["192.168.1.180:8443"]
# tls_pin_sha256: "<64-hex-char certificate fingerprint>"
# # allow_write: true # host gate for host.http_patch; default off
api:
port: 8080
# lan_auth: false # when true, protected LAN routes need the house password (Bearer/cookie) or FTW_API_TOKEN
# Home Assistant MQTT bridge (optional)
homeassistant:
enabled: false
broker: 192.168.1.1
# allow_unverified_local: true # let FTW resolve broker.local over mDNS itself
port: 1883
username: homeems
password: homeems
publish_interval_s: 5
# Modbus TCP proxy. FTW already holds the inverter's single socket; other
# integrations (Home Assistant's native Modbus / Sungrow / SolarEdge
# integrations, Node-RED, …) share it by talking to this box instead of the
# device. Off by default: the listener has no Modbus authentication, and
# writes would bypass FTW's control loop.
# modbus_proxy:
# enabled: true
# listen: ":1502" # used when the site has one unique Modbus TCP endpoint
# allow_write: false # keep off unless you trust every host on the LAN
#
# Two inverters (different host:port) each need their own listen address:
# capabilities.modbus.proxy_listen: ":1502" / ":1503"
# Docker host-networking already publishes 1502; other compose files need
# the port mapped.
# Built-in OCPP Central System, speaking 1.6J and 2.0.1. EV chargers that speak
# OCPP connect to FTW directly — there is no driver to write and nothing to add
# under `drivers:`.
# A charger appears as a device on its first BootNotification, keyed by the last
# segment of the URL it dialled: ws://<ftw-host>:8887/garage-left
#
# Credentials are required when enabled, and FTW refuses to start without them:
# the OCPP library builds its listen address from the port alone, so the socket
# is open on every interface whatever bind says — bind refuses the handshake for
# a connection that arrived elsewhere, which controls access without closing the
# port. Keep it closed at your router. See docs/ocpp.md.
# Each version needs its own port: a charger picks its dialect in the WebSocket
# handshake, so one listener cannot serve both. 2.1 is not supported — no
# production-grade Go implementation of it exists yet.
ocpp:
enabled: false
# bind: 192.168.1.10 # refuse chargers reaching the box any other
# way; omit for every interface
port: 8887 # OCPP 1.6J
# port_v201: 8888 # OCPP 2.0.1; omit to disable
path: /
username: ftw
password: "" # required when enabled; use a long random string
heartbeat_interval_s: 60
# Serve wss:// instead of ws://. client_ca_file additionally requires each
# charger to present a certificate signed by that CA whose CN or DNS SAN
# matches the identity in the URL — OCPP 2.0.1 security profile 3.
# tls:
# cert_file: /etc/ftw/ocpp/server.crt
# key_file: /etc/ftw/ocpp/server.key
# client_ca_file: /etc/ftw/ocpp/charger-ca.crt
# Give a charger a credential of its own, so the shared password above stops
# being enough to connect under its name. The id is what the charger dials
# with, and the same string a loadpoint's driver_name adopts.
# chargers:
# - id: garage
# password: "" # a long random string, different per charger
# Persistent state (SQLite)
state:
path: state.db
# backup_dir: backups # use an absolute mounted path for off-device backups
# The FTW app. On by default: the box holds an outbound connection to a relay
# that carries encrypted frames and holds no keys. The relay still sees the
# source IP, timing and connection pattern. Set enabled to false to opt out.
# An empty app_link object is also off. Pair a phone by scanning the box's code;
# the pairing code is single-use and expires in ten minutes.
app_link:
enabled: true
# Anonymous fleet ping. Once a day, at a time that moves, the box tells the
# FTW relay six things: its FTW version, its release channel, which driver
# types it runs, roughly how much battery it has, its price zone and roughly
# how old the install is. No id, no key, no serial, no site name, no counter
# and no timestamp — nothing in the message says which box sent it. Two
# limits: those six fields still describe a household, so a rare mix may be
# the only one of its kind, and the relay sees the address during the request.
# It saves only daily totals, so the result counts reports, not unique boxes.
# Settings → FTW app → Fleet statistics shows the exact message this box would send. On by
# default; `enabled: false` turns it off without a restart.
fleet_ping:
enabled: true
# endpoint: https://relay.ftw.energy/fleet
# Independently distributed Lua drivers. The signed stable catalog is refreshed
# by default, but refresh never downloads, activates, or restarts a driver.
# Add `device_repository: { enabled: false }` to opt out. The expanded form is:
# device_repository:
# enabled: true
# refresh_interval_h: 24
# repositories:
# - id: ftw-official
# name: FTW device drivers
# format: ftw.manifest/v1
# manifest_url: https://github.com/srcfl/device-drivers/releases/download/drivers-stable/manifest.json
# enabled: true
# trusted_keys:
# ftw-drivers-2026-01: MX+j27UBkyM099hTyJlmMLK9qlTTDUJsaK/vH12fFKc=
# Test the public beta channel on one site before stable promotion:
# device_repository:
# enabled: true
# refresh_interval_h: 24
# repositories:
# - id: ftw-device-drivers-beta
# name: FTW device drivers beta
# format: ftw.manifest/v1
# manifest_url: https://github.com/srcfl/device-drivers/releases/download/drivers-beta/manifest.json
# enabled: true
# trusted_keys:
# ftw-drivers-2026-01: MX+j27UBkyM099hTyJlmMLK9qlTTDUJsaK/vH12fFKc=
# Refresh only updates the signed catalog. Install and activation stay explicit.
# Energyplan ships with Core and runs first in supported beta builds.
# Core DP provides shadow comparisons and validated fallback plans.
# planner:
# enabled: true
# # engine: energyplan # beta default; core selects the Go planner
# mode: passive_arbitrage
# forecast_trust: balanced # cautious | balanced | bold (first boot; live value is SQLite)
# battery_export: unknown # unknown | not_allowed | allowed (unknown = no battery sale)
# horizon_hours: 48
# interval_min: 15
# soc_min: 0.10
# soc_max: 0.95
# Peak-power tariff (weekday 06–20 local time). Same minor units as
# grid_tariff_ore_kwh, per kW, excluding VAT. 0 disables it.
# price:
# demand_price_per_kw: 7000
# demand_top_n: 3