package config import ( "encoding/json" "fmt" "os" ) type Config struct { // ScreenID is the unique slug for this screen — used for self-registration and playlist fetching. ScreenID string `json:"screen_id"` // ScreenName is the human-readable display name shown in the admin UI. Falls back to ScreenID. ScreenName string `json:"screen_name"` // ScreenOrientation is "landscape" (default) or "portrait". ScreenOrientation string `json:"screen_orientation"` ServerBaseURL string `json:"server_base_url"` MQTTBroker string `json:"mqtt_broker"` MQTTUsername string `json:"mqtt_username"` MQTTPassword string `json:"mqtt_password"` HeartbeatEvery int `json:"heartbeat_every_seconds"` StatusReportEvery int `json:"status_report_every_seconds"` PlayerListenAddr string `json:"player_listen_addr"` // PlayerContentURL is a fallback URL shown when no playlist is available from the server. PlayerContentURL string `json:"player_content_url"` // RegisterSecret ist das Pre-Shared-Secret für POST /api/v1/screens/register (K6). // Muss mit MORZ_INFOBOARD_REGISTER_SECRET auf dem Server übereinstimmen. // Wenn leer, wird kein Header gesendet (kompatibel mit Servern ohne Secret). RegisterSecret string `json:"register_secret"` // ScreenshotEvery gibt das Intervall in Sekunden für periodische Screenshots an (Phase 6). // 0 oder negativ = Screenshots deaktiviert. ScreenshotEvery int `json:"screenshot_every_seconds"` } const defaultConfigPath = "/etc/signage/config.json" func Load() (Config, error) { cfg := defaultConfig() configPath := getenv("MORZ_INFOBOARD_CONFIG", defaultConfigPath) if err := loadFromFile(configPath, &cfg); err != nil { if !os.IsNotExist(err) { return Config{}, fmt.Errorf("load config file: %w", err) } } overrideFromEnv(&cfg) if cfg.ScreenID == "" { return Config{}, fmt.Errorf("screen id is required") } if cfg.ScreenName == "" { cfg.ScreenName = cfg.ScreenID } if cfg.HeartbeatEvery <= 0 { cfg.HeartbeatEvery = defaultConfig().HeartbeatEvery } if cfg.StatusReportEvery <= 0 { cfg.StatusReportEvery = defaultConfig().StatusReportEvery } return cfg, nil } func defaultConfig() Config { return Config{ ScreenID: "unset-screen", ScreenOrientation: "landscape", ServerBaseURL: "http://127.0.0.1:8080", MQTTBroker: "", HeartbeatEvery: 30, StatusReportEvery: 60, PlayerListenAddr: "127.0.0.1:8090", PlayerContentURL: "", } } func loadFromFile(path string, cfg *Config) error { data, err := os.ReadFile(path) if err != nil { return err } return json.Unmarshal(data, cfg) } func overrideFromEnv(cfg *Config) { cfg.ScreenID = getenv("MORZ_INFOBOARD_SCREEN_ID", cfg.ScreenID) cfg.ServerBaseURL = getenv("MORZ_INFOBOARD_SERVER_URL", cfg.ServerBaseURL) cfg.MQTTBroker = getenv("MORZ_INFOBOARD_MQTT_BROKER", cfg.MQTTBroker) cfg.MQTTUsername = getenv("MORZ_INFOBOARD_MQTT_USERNAME", cfg.MQTTUsername) cfg.MQTTPassword = getenv("MORZ_INFOBOARD_MQTT_PASSWORD", cfg.MQTTPassword) cfg.PlayerListenAddr = getenv("MORZ_INFOBOARD_PLAYER_ADDR", cfg.PlayerListenAddr) cfg.ScreenName = getenv("MORZ_INFOBOARD_SCREEN_NAME", cfg.ScreenName) cfg.ScreenOrientation = getenv("MORZ_INFOBOARD_SCREEN_ORIENTATION", cfg.ScreenOrientation) cfg.PlayerContentURL = getenv("MORZ_INFOBOARD_PLAYER_CONTENT_URL", cfg.PlayerContentURL) cfg.RegisterSecret = getenv("MORZ_INFOBOARD_REGISTER_SECRET", cfg.RegisterSecret) if value := getenv("MORZ_INFOBOARD_SCREENSHOT_EVERY", ""); value != "" { var parsed int _, _ = fmt.Sscanf(value, "%d", &parsed) cfg.ScreenshotEvery = parsed } if value := getenv("MORZ_INFOBOARD_STATUS_REPORT_EVERY", ""); value != "" { var parsed int _, _ = fmt.Sscanf(value, "%d", &parsed) if parsed > 0 { cfg.StatusReportEvery = parsed } } } func getenv(key, fallback string) string { value := os.Getenv(key) if value == "" { return fallback } return value }