// Package config provides configuration management for hdmistat package config import ( "context" "log/slog" "os" "time" "git.eeqj.de/sneak/smartconfig" ) const ( defaultWidth = 1920 defaultHeight = 1080 ) // Config holds the application configuration type Config struct { FramebufferDevice string RotationInterval string UpdateInterval string Screens []string LogLevel string Width int Height int // Parsed durations (not from config) rotationDuration time.Duration updateDuration time.Duration } // Load loads configuration from all available sources func Load(_ context.Context) (*Config, error) { // Start with defaults cfg := &Config{ FramebufferDevice: "/dev/fb0", RotationInterval: "10s", UpdateInterval: "1s", Screens: []string{"overview", "top_cpu", "top_memory"}, LogLevel: "info", Width: defaultWidth, Height: defaultHeight, } // Try to load from the default location for hdmistat sc, err := smartconfig.NewFromAppName("hdmistat") if err == nil { // Override defaults with config file values if they exist if val, err := sc.GetString("framebuffer_device"); err == nil && val != "" { cfg.FramebufferDevice = val } if val, err := sc.GetString("rotation_interval"); err == nil && val != "" { cfg.RotationInterval = val } if val, err := sc.GetString("update_interval"); err == nil && val != "" { cfg.UpdateInterval = val } if val, err := sc.GetString("log_level"); err == nil && val != "" { cfg.LogLevel = val } if val, err := sc.GetInt("width"); err == nil && val > 0 { cfg.Width = val } if val, err := sc.GetInt("height"); err == nil && val > 0 { cfg.Height = val } // Load screens array if screensRaw, exists := sc.Get("screens"); exists { if screens, ok := screensRaw.([]interface{}); ok && len(screens) > 0 { cfg.Screens = make([]string, 0, len(screens)) for _, s := range screens { if str, ok := s.(string); ok { cfg.Screens = append(cfg.Screens, str) } } } } } // Override with environment variables if set if envLogLevel := os.Getenv("HDMISTAT_LOG_LEVEL"); envLogLevel != "" { cfg.LogLevel = envLogLevel } // Parse durations cfg.rotationDuration, err = time.ParseDuration(cfg.RotationInterval) if err != nil { return nil, err } cfg.updateDuration, err = time.ParseDuration(cfg.UpdateInterval) if err != nil { return nil, err } return cfg, nil } // GetRotationDuration returns the parsed rotation interval func (c *Config) GetRotationDuration() time.Duration { return c.rotationDuration } // GetUpdateDuration returns the parsed update interval func (c *Config) GetUpdateDuration() time.Duration { return c.updateDuration } // GetLogLevel returns the slog level func (c *Config) GetLogLevel() slog.Level { switch c.LogLevel { case "debug": return slog.LevelDebug case "info": return slog.LevelInfo case "warn": return slog.LevelWarn case "error": return slog.LevelError default: return slog.LevelInfo } }