Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7d5dc48
feat(control): show command limits and measured response tiers
frahlg Sep 29, 2026
646f55b
fix(control): require fresh source samples for measured confirmation
frahlg Sep 29, 2026
c875ebe
feat(control): compare site response curves with measured flow changes
frahlg Sep 29, 2026
b465646
fix(control): classify commanded devices and qualify solar response
frahlg Sep 29, 2026
3b015a2
Recognize relayed physical meters and show measured time gaps
frahlg Sep 29, 2026
5a78757
Verify control per device with unknown flows in the residual
frahlg Sep 29, 2026
a12ef32
Put control proof on device bubbles and open evidence on tap
frahlg Sep 29, 2026
278740d
Keep control evidence quiet on overview bubbles
frahlg Sep 30, 2026
c4dc4b8
Open device controls first with measured evidence below
frahlg Sep 30, 2026
9be358f
Keep control evidence below the device controls in the vision
frahlg Sep 30, 2026
7951863
Separate measured power evidence from target fulfilment
frahlg Sep 30, 2026
e51c2a6
Stop charging full batteries and explain the pause
frahlg Sep 30, 2026
aa8f7ee
Answer "Are we in control?" in every mode and name the evidence
frahlg Oct 1, 2026
35ba523
Show the control answer first in each device sheet
frahlg Oct 1, 2026
712e586
Describe control status and the evidence receipt
frahlg Oct 1, 2026
6716098
Describe measured changes in words instead of signs
frahlg Oct 1, 2026
fe2f9d2
Keep change-only sources current while they confirm the value
frahlg Oct 1, 2026
b490ea2
Pin drivers with confirmed Easee power and site-signed Pixii AC
frahlg Oct 1, 2026
857fc9a
Merge master into feat/control-feedback
frahlg Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/full-battery-stop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ftw": patch
---

Pause charging for each battery that reports 100% and allow it again at 99% or lower. Keep discharge available and apply the stop to planned and manual charging, including during ramp limits and dispatch waits. Show the full-battery stop separately from measurement confidence and retain warnings when the device ignores the stop.
9 changes: 9 additions & 0 deletions .changeset/measured-control-feedback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"ftw": minor
---

Show whether each device FTW controls does what FTW asked. A tap on the battery, charger or solar bubble answers "Are we in control?" first: Following FTW, Waiting, Limited, Not following, No contact or Not controlled, with one sentence and, when you can act, the next step. Manual override moves below it.

"How FTW knows" lists the evidence: sent, accepted, measured and confirmed by a separate grid meter, with numbers and response curves for experts. Support reports and the API carry the same status and evidence.

The overview stays quiet while FTW is in control and marks only devices that need a look (amber) or attention now (red). Proof follows a device through targets retuned every tick and slow cloud sources, a confirmed step survives later household loads, and a battery that takes less power as it fills is explained instead of flagged.
67 changes: 67 additions & 0 deletions VISION.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,54 @@ Core validates every plan and command, including plans supplied by another
system. Fuse, equipment, SoC, freshness and other quantified safety limits
always apply. Stale required site-meter data stops dispatch.

## Are we in control?

**Trust what you read, not what you sent.**

For each device function FTW commands, such as a battery, a car charger, a
solar cap or V2X, the normal view answers one question: is the device doing
what FTW asked? A successful driver call, an API reply or an echoed setpoint
does not prove a physical effect.

Core decides the answer and its urgency; clients only render it:

- **Following FTW:** fresh readings show the device doing what FTW asked.
- **Waiting:** a response is still due, or there is nothing to verify yet.
- **Limited:** the device follows within a known limit, such as a nearly full
battery, the main fuse or a charger setting.
- **Not following:** fresh readings disagree with the command after the
device's response time.
- **No contact:** readings or commands fail. Losing a device FTW had measured
is an alarm.
- **Not controlled:** FTW only reads the device or has handed control back.

Show the evidence as a receipt, not as the headline: sent, accepted, measured
and confirmed. Accepted means the driver took the command. Measured means
fresh, distinct device readings across the response window. Confirmed means a
separate physical meter saw the matching change. Keep the target separate: a
battery can be confirmed at 4.4 kW against a 5 kW command, and the shortfall
stays visible.

- Freshness follows the source. A slow cloud source stays current for as long
as it declares; a repeated or cached sample never extends proof.
- Compare each reading with the commands that could still be in force during
the response time. Retuning every tick is normal control, not a new test.
A material step starts a new comparison.
- Confirmation needs a separate physical sensor, aligned readings and a clear
step. Subtract other measured flows, and never let an unmeasured load
disappear into an average. A confirmed step stays confirmed while the device
keeps following with fresh readings.
- Explain a shortfall only with a fresh, relevant fact, such as state of
charge or a reported limit. Say when the cause is unknown.
- Keep the overview quiet while FTW is in control. Mark warnings amber and
alarms red, and clear them when fresh evidence shows recovery.
- Never change a power target just to create a test signal without the
owner's consent.
- Give authorized agents and support reports the same evidence.

This is the product direction. Each implementation states which devices,
paths and physical outcomes it has verified.

## Trust through visible behaviour

The live view is a core product feature. It must feel local and fast, and
Expand Down Expand Up @@ -68,6 +116,25 @@ verified device limits where available and learn the usable response within
safe bounds. Advanced users may set limits explicitly. Do not treat an
observed power level as proof of an absolute hardware or installation limit.

Develop the per-device battery and inverter model into a digital twin grounded
in normal operation. Learn charge and discharge response, delay, usable power
and losses under the observed state of charge, temperature and device mode.
Keep energy capacity in kWh separate from power in kW. A 10 kWh battery may
have a 5 kW inverter; reaching 5 kW says nothing by itself about its capacity.
If user input conflicts with repeated measurements or device ratings, explain
the conflict and propose a correction rather than silently changing that input.

Keep user input, reported limits and learned estimates distinct, with source,
age, tested conditions and uncertainty. Prefer independently confirmed samples
for learning, label device-only samples, and never train on an inferred site
effect as though it were another meter. A plateau at one state of charge is
evidence for those conditions, not a permanent nameplate limit. Learn capacity
only from suitable energy and state-of-charge observations. Detect changed
behaviour and rebuild confidence after equipment or mode changes. Use the model
to plan achievable work within verified safety limits; learned estimates must
never raise those limits or replace fresh measurements as proof of an effect.
This is the target for the model, not a claim that the full twin has shipped.

For solar, the target is that "I have solar" is enough to start learning.
Installed kWp is an optional starting estimate. Approximate user input must
not permanently constrain a model when measurements support a better fit.
Expand Down
10 changes: 9 additions & 1 deletion docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ clear result that the owner can review.
| 5 | Complete external authority and fair value as separate focused changes. | Temporary control expires to a defined local default; durable goals persist; schedule/plan access can be revoked; cloud MCP does not expose data to the relay. Separately, the validated self-consumption comparison reports FTW's incremental value and missing evidence honestly. |
| Later | Bounded thermal control and further expert extensions. | A named tank/hot-water use case meets comfort, hardware and failure requirements without making ordinary household setup harder. Native expansion still follows its existing Pair + Now verification gates. |

Battery and inverter learning should build on the existing per-battery response
model. The digital-twin target needs tests for separate kW and kWh inputs,
reported versus observed limits, partial delivery confirmed by the site meter,
charge/discharge and state-of-charge-dependent response, temperature and mode
changes, stale samples and model recovery. Show source and uncertainty before
using an estimate in planning. A model prediction must never verify itself or
raise a safety limit. These are acceptance requirements, not shipped support.

Necessary safety, security, recovery and support fixes continue throughout.
Correct misleading value labels when their scope is known; that need not wait
for the new counterfactual model. Reading and analysis access for agents can
Expand All @@ -69,7 +77,7 @@ or reopen closed issues.
|---|---|---|
| Simple setup and first-day value | Discover mixed equipment. Confirm the main fuse and site meter. Read battery capacity where possible and ask for kWh when needed. Power settings and solar kWp are optional where safe device information and learning allow. Provide useful initial load and PV forecasts. | A fresh install reaches useful automatic operation without panel drawings or expert settings. Missing data and wrong start estimates have tested behaviour. Record device identity, known limits and uncertainty. Validate on named hardware combinations as well as simulators. |
| A clear commissioning result | Check commands and measured response within known limits. Distinguish working telemetry from working control. Exclude failed control from both the plan and dispatch. | Show request, device response, measured effect and timing. Cover delayed response, refusal, disconnect, stale site data and recovery. A short commissioning test does not claim full hardware qualification. |
| Live control that earns trust | Keep the fast local feel. Make request, acceptance, command, response, physical effect and freshness visible in the normal Flow experience. | Browser review on desktop and mobile, timing measurements on a target box, and traces with different sampling rates. A pending command or old reading never appears as completed or fresh. Examine existing Live and Flow views before deciding their final layout. |
| Verified control: “Are we in control?” | Keep the fast local feel. Make request, acceptance, command, response, physical effect and freshness visible in the normal Flow experience. | Browser review on desktop and mobile, timing measurements on a target box, and traces with different sampling rates. A successful call, echoed setpoint or old reading never appears as a verified physical result. Test accepted, measured and confirmed evidence per device, and the status each one shows. Confirm one device while another is offline; keep unavailable background flows in the residual. Show overview status, alarm on lost measured proof after the response wait, and clear it on measured recovery. Include direction, tolerance, response delay, simultaneous load/solar changes, shared measurement sources, overridden setpoints, device limits and recovery. A reduced request remains visibly limited even when the device follows the reduced command. Examine existing Live and Flow views before deciding their final layout. |
| Good automatic planning | Use site physics and charge/discharge efficiency. Default wear cost is zero; users may opt in. Keep hard SoC limits separate from forecast-based caution and explicit backup needs. | Cold-start and learned forecasts, stale inputs, multiple assets and unavailable optimizer paths have tests. Backtests use held-out periods and report uncertainty. Defaults and persisted settings agree across UI, Core and worker. Site runs establish practical benefit. |
| Reliable daily charging | Persistent weekday target and deadline. Offline-car estimates. Direct SoC slider after connection, no extra save, prompt replanning, one-action Charge now and notifications when action is needed. | Test from app intent to charger/car response and delivered energy, including missed-goal risk, absent vehicle cloud, unknown SoC, reconnect and restart. Test notifications with the app closed. Confirm physical charging separately from simulation. |
| Useful analysis and fair savings | Keep enough provenance to explain plans and outcomes. Main savings target compares with ordinary self-consumption on the same installation. | Actual cost reconciles with measured import/export and prices. Specify EV behaviour, initial and final stored-energy accounting, efficiency and coverage. Show missing and negative results. Label the current no-PV/no-battery comparison as total site value until replacement is verified. |
Expand Down
2 changes: 1 addition & 1 deletion drivers/BUNDLED_SOURCE.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"from the signed channel. Run scripts/sync-bundled-drivers.sh to update."
],
"repository": "srcfl/device-drivers",
"commit": "7e572fb499dda7acf6f1212b8b0b78235b9d3ba9",
"commit": "d74ace6a6e5fc335177c40696ac3d449b1517057",
"source_dir": "drivers/lua",
"drivers": [
"ambibox_v2x", "ctek", "ctek_hybrid", "ctek_v2", "deye", "easee_cloud",
Expand Down
3 changes: 3 additions & 0 deletions go/cmd/ftw/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2457,6 +2457,9 @@ func main() {
SiteDispatchBlocked: func() string {
return siteDispatchNow(tel, cfg, cfgMu, ctrl, ctrlMu, time.Now()).Reason
},
SiteMeasurementSources: func() telemetry.ForecastOptions {
return forecastSettings.Snapshot().Options
},
HA: haOwner.Bridge,
Registry: reg,
DriverRepository: driverRepository,
Expand Down
6 changes: 5 additions & 1 deletion go/internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ const (
// One instance is shared across all handlers; mutations use the contained
// mutexes from each package.
type Deps struct {
// SiteMeasurementSources reuses the configured physical-flow inventory.
// These are source declarations, never forecast values or derived load.
SiteMeasurementSources func() telemetry.ForecastOptions

// MutationPolicy protects every state-changing route at the shared
// Handler boundary. Production requires tokens for non-local hostnames;
Expand Down Expand Up @@ -1220,6 +1223,7 @@ func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
// diagnostic — incremented when actual fleet delivery diverges
// from the plan's BatteryEnergyWh by > 50 % (over) or < 50 %
// (under). Idle slots (|planned| ≤ 50 Wh) are ignored.
"control_feedback": s.controlFeedback(time.Now()),
"slot_delivery_stats": ctrl.SlotDeliveryStats,
}
// A stale or missing site meter is not 0 W. Publishing zero made the
Expand Down Expand Up @@ -3677,7 +3681,7 @@ func (s *Server) handleLoadpoints(w http.ResponseWriter, r *http.Request) {
writeJSON(w, 200, map[string]any{
"enabled": true,
"vehicle_limit_goal_supported": true,
"loadpoints": states,
"loadpoints": s.loadpointsWithFeedback(states),
})
}

Expand Down
Loading
Loading