Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .changeset/driver-control-holds-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"ftw": patch
---

Internal: operator driver-control holds moved from the API into their own
package so a later planner can share the same lock and expiry, and the unused
thermal policy draft (`DecideIntent`) is removed. No behaviour change.
7 changes: 7 additions & 0 deletions .changeset/thermal-control-declaration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"ftw": patch
---

A driver can now say what a control does to heat. A control may declare `asset` (space_heat, dhw, cooling or buffer), `strategy` (curve_offset, setpoint, on_off or discrete_mode), `zone`, `min_interval_s`, `device_hold_max_s` (how long the device keeps a value before it returns to its own regulation), and for on_off `min_on_s` and `min_off_s`. A number input may declare its `default`, the value the device runs at by itself. A driver may also list `sensors`: the metrics that are a heat store's temperature. `/api/drivers/{name}` shows both. Nothing plans for or sends a heat command because of them yet.

A string control may declare a closed set of `options`. The control API then refuses any other value instead of passing it to the driver. A control that declares these fields wrongly is left out, as an unrenderable control is today. Drivers that declare none of them are unchanged.
23 changes: 11 additions & 12 deletions go/internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/srcfl/ftw/go/internal/configreload"
"github.com/srcfl/ftw/go/internal/control"
"github.com/srcfl/ftw/go/internal/coverage"
"github.com/srcfl/ftw/go/internal/drivercontrol"
"github.com/srcfl/ftw/go/internal/driverrepo"
"github.com/srcfl/ftw/go/internal/drivers"
"github.com/srcfl/ftw/go/internal/energyreport"
Expand Down Expand Up @@ -296,11 +297,9 @@ type Server struct {
savingsCacheMu sync.Mutex
savingsCache map[string]daySavings

// controlStates serializes command dispatch, default dispatch, and hold
// transitions per driver. Process-lifetime only, deliberately: a restart
// should leave no device held by a setting nobody remembers making.
controlStateMu sync.Mutex
controlStates map[string]*controlDriverState
// controlHolds serializes command dispatch, default dispatch, and hold
// transitions per driver. Process-lifetime only; see drivercontrol.
controlHolds *drivercontrol.Holds

// beforeDriverControlSend is a package-test seam for reproducing a
// lifecycle change between request validation and registry dispatch. It is
Expand Down Expand Up @@ -340,13 +339,13 @@ func New(deps *Deps) *Server {
deps.WebDir = "web"
}
s := &Server{
deps: deps,
mux: http.NewServeMux(),
dailyCache: make(map[string]state.DayEnergy),
controlStates: make(map[string]*controlDriverState),
drafts: newDriverDrafts(),
marks: make(map[string]routeMark),
deps: deps,
mux: http.NewServeMux(),
dailyCache: make(map[string]state.DayEnergy),
drafts: newDriverDrafts(),
marks: make(map[string]routeMark),
}
s.controlHolds = drivercontrol.New(s.controlRegistry)
s.setup = onboarding.NewWithSecrets(deps.State, s.preserveSetupSecrets, filepath.Dir(deps.ConfigPath))
if deps.SaveConfigWithValues == nil && deps.State != nil {
deps.SaveConfigWithValues = func(path string, cfg *config.Config, values map[string]string, reviewedJSON []byte, beforeSave func() error) error {
Expand All @@ -356,7 +355,7 @@ func New(deps *Deps) *Server {
if deps.Registry != nil {
// Registry removal is the lifecycle boundary for a driver generation.
// Clear the API hold before a replacement instance can be added.
deps.Registry.SetLifecycleHook(s.clearDriverControl)
deps.Registry.SetLifecycleHook(s.controlHolds.Clear)
}
s.routes()
// A draft's timer died with the previous process, so anything left behind
Expand Down
Loading
Loading