diff --git a/server/backend/internal/scheduler/scheduler.go b/server/backend/internal/scheduler/scheduler.go new file mode 100644 index 0000000..441c4fc --- /dev/null +++ b/server/backend/internal/scheduler/scheduler.go @@ -0,0 +1,72 @@ +// Package scheduler enthält den Display-Zeitplan-Scheduler. +// Er prüft jede Minute ob ein Screen ein- oder ausgeschaltet werden soll. +package scheduler + +import ( + "context" + "log/slog" + "time" + + "git.az-it.net/az/morz-infoboard/server/backend/internal/store" +) + +// DisplayCommander sendet einen Display-Befehl per MQTT. +type DisplayCommander interface { + SendDisplayCommand(screenSlug, action string) error +} + +// ScreenSlugGetter lädt den Slug für eine Screen-ID. +type ScreenSlugGetter interface { + GetByID(ctx context.Context, id string) (*store.Screen, error) +} + +// Run startet den Scheduler-Loop. Blockiert bis ctx abgebrochen wird. +func Run(ctx context.Context, schedules *store.ScreenScheduleStore, screens ScreenSlugGetter, notifier DisplayCommander) { + ticker := time.NewTicker(1 * time.Minute) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + check(ctx, schedules, screens, notifier) + case <-ctx.Done(): + return + } + } +} + +// check prüft alle aktiven Zeitpläne und sendet ggf. Befehle. +func check(ctx context.Context, schedules *store.ScreenScheduleStore, screens ScreenSlugGetter, notifier DisplayCommander) { + now := time.Now().Format("15:04") + + enabled, err := schedules.ListEnabled(ctx) + if err != nil { + slog.Error("scheduler: list enabled schedules failed", "err", err) + return + } + + for _, sc := range enabled { + screen, err := screens.GetByID(ctx, sc.ScreenID) + if err != nil { + slog.Warn("scheduler: screen not found", "screen_id", sc.ScreenID, "err", err) + continue + } + + var action string + if sc.PowerOnTime != "" && sc.PowerOnTime == now { + action = "display_on" + } else if sc.PowerOffTime != "" && sc.PowerOffTime == now { + action = "display_off" + } + + if action == "" { + continue + } + + if err := notifier.SendDisplayCommand(screen.Slug, action); err != nil { + slog.Error("scheduler: send command failed", "screen_id", sc.ScreenID, "action", action, "err", err) + } else { + slog.Info("scheduler: display command sent", "screen_id", sc.ScreenID, "slug", screen.Slug, "action", action) + } + } +}