One schedule for every charger, and a clock on the server to keep it
The charger's own cloud schedule is one window inside one box: charge between these hours, every day, and that is the whole vocabulary. A third tab on Charging holds a list instead — each line an action, a time, the days it repeats on and the chargers it acts on — and one list covers the whole account rather than each charger hiding its own. The clock is the server's. A schedule that only fires while a tab is open is a reminder, so a ticker sweeps every enabled task and fires whichever minute has come. It sends by handing a synthesised request to the same control endpoint the page's buttons use, so a scheduled command goes through the same cascade, ownership gate, rate limit and audit trail — what the owner cannot press by hand, the scheduler cannot send for them. A task names its chargers, or names none, which means all of them and keeps meaning that for a charger imported next year. Times are stored as a wall clock plus the zone they were written in, so 23:00 stays 23:00 wherever the server sits. One action per task: a charging window is the two tasks that open and close it, which is how it is read back, edited and switched off. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
8e4c22cfcf
commit
5a4515978f
@@ -159,6 +159,7 @@ const (
|
||||
colDocuments = "car_documents"
|
||||
colReminders = "reminders"
|
||||
colHomeChargers = "home_chargers"
|
||||
colChargingTasks = "charging_tasks"
|
||||
colControlAudit = "control_audit"
|
||||
colAppSettings = "app_settings"
|
||||
)
|
||||
@@ -182,6 +183,10 @@ type Server struct {
|
||||
ocpp *ocpp.CSMS
|
||||
control *controlIndex // token -> owning user/charger for the /ocpp endpoint
|
||||
ctlRL *rateLimiter // per user+charger control-command rate limit
|
||||
|
||||
// schedulerStop ends the ticker that fires the home-charger scheduler's
|
||||
// tasks, started by StartScheduler (see chargingtasks_run.go).
|
||||
schedulerStop context.CancelFunc
|
||||
}
|
||||
|
||||
// New constructs a Server around an already-built PocketBase client.
|
||||
@@ -285,6 +290,9 @@ func (s *Server) Stop(ctx context.Context) {
|
||||
if s.pluginsStop != nil {
|
||||
s.pluginsStop()
|
||||
}
|
||||
if s.schedulerStop != nil {
|
||||
s.schedulerStop()
|
||||
}
|
||||
s.ocpp.Shutdown(ctx)
|
||||
s.plugins.Shutdown(ctx)
|
||||
}
|
||||
@@ -488,6 +496,14 @@ func (s *Server) Handler() http.Handler {
|
||||
mux.HandleFunc("PATCH /api/home-chargers/{id}", s.updateHomeCharger)
|
||||
mux.HandleFunc("DELETE /api/home-chargers/{id}", s.deleteHomeCharger)
|
||||
|
||||
// The home-charger scheduler: one list of charging tasks per user, covering
|
||||
// every charger they own. See chargingtasks.go.
|
||||
mux.HandleFunc("GET /api/charging-tasks", s.listChargingTasks)
|
||||
mux.HandleFunc("POST /api/charging-tasks", s.createChargingTask)
|
||||
mux.HandleFunc("PATCH /api/charging-tasks/{id}", s.updateChargingTask)
|
||||
mux.HandleFunc("DELETE /api/charging-tasks/{id}", s.deleteChargingTask)
|
||||
mux.HandleFunc("POST /api/charging-tasks/{id}/run", s.runChargingTaskNow)
|
||||
|
||||
// Cars + sharing.
|
||||
mux.HandleFunc("GET /api/cars", s.listCars)
|
||||
mux.HandleFunc("POST /api/cars", s.createCar)
|
||||
|
||||
Reference in New Issue
Block a user