From 15dfaa31f988ec5f3d6e42f19201209b64dceb88 Mon Sep 17 00:00:00 2001 From: KemoNine Date: Fri, 4 Sep 2020 23:28:01 -0400 Subject: [PATCH] Wire up more of the config ; apply hdmi on/off schedules on save --- cmd/gui/gui.go | 25 +++++----- config/README.md | 3 ++ {ui => config}/constants.go | 2 +- ui/config.go | 81 +++++++++++++++++++++---------- ui/slideshow.go | 6 ++- utils/files.go | 20 ++++++++ utils/hdmi.go | 28 +++++++++++ utils/systemd.go | 23 +++++++++ utils/{temp.go => temperature.go} | 0 9 files changed, 148 insertions(+), 40 deletions(-) create mode 100644 config/README.md rename {ui => config}/constants.go (98%) create mode 100644 utils/files.go create mode 100644 utils/hdmi.go create mode 100644 utils/systemd.go rename utils/{temp.go => temperature.go} (100%) diff --git a/cmd/gui/gui.go b/cmd/gui/gui.go index 546827b..2ff3a68 100644 --- a/cmd/gui/gui.go +++ b/cmd/gui/gui.go @@ -12,6 +12,7 @@ import ( "github.com/knadh/koanf/providers/posflag" flag "github.com/spf13/pflag" + pfconfig "git.kemonine.info/PiFrame/config" "git.kemonine.info/PiFrame/ui" ) @@ -23,8 +24,8 @@ func main() { os.Exit(0) } // Command line flags - f.Bool(ui.CLI_FLAG_CONFIG_ONLY, false, "Only show the config UI, NOT the slideshow") - cliFlag := f.Lookup(ui.CLI_FLAG_CONFIG_ONLY) + f.Bool(pfconfig.CLI_FLAG_CONFIG_ONLY, false, "Only show the config UI, NOT the slideshow") + cliFlag := f.Lookup(pfconfig.CLI_FLAG_CONFIG_ONLY) if cliFlag != nil { cliFlag.NoOptDefVal = "true" } @@ -36,17 +37,17 @@ func main() { // Setup some defaults pfConfig.Load(confmap.Provider(map[string]interface{}{ - ui.CONFIG_KEY_SLIDESHOW_INTERVAL: ui.DEFAULT_SLIDESHOW_INTERVAL, - ui.CONFIG_KEY_SLIDESHOW_RESTART_INTERVAL: ui.DEFAULT_SLIDESHOW_RESTART_INTERVAL, - ui.CONFIG_KEY_HDMI_OFF: ui.DEFAULT_HDMI_OFF, - ui.CONFIG_KEY_HDMI_ON: ui.DEFAULT_HDMI_ON, - ui.CONFIG_KEY_ALBUMS_ROOT: ui.DEFAULT_ALBUMS_ROOT, - ui.CONFIG_KEY_ALBUMS_SELECTED: []string{ui.DEFAULT_ALBUM_SELECTED}, + pfconfig.CONFIG_KEY_SLIDESHOW_INTERVAL: pfconfig.DEFAULT_SLIDESHOW_INTERVAL, + pfconfig.CONFIG_KEY_SLIDESHOW_RESTART_INTERVAL: pfconfig.DEFAULT_SLIDESHOW_RESTART_INTERVAL, + pfconfig.CONFIG_KEY_HDMI_OFF: pfconfig.DEFAULT_HDMI_OFF, + pfconfig.CONFIG_KEY_HDMI_ON: pfconfig.DEFAULT_HDMI_ON, + pfconfig.CONFIG_KEY_ALBUMS_ROOT: pfconfig.DEFAULT_ALBUMS_ROOT, + pfconfig.CONFIG_KEY_ALBUMS_SELECTED: []string{pfconfig.DEFAULT_ALBUM_SELECTED}, }, "."), nil) // Bring in /etc/defaults/pf.toml if it exists - configFileProvider := file.Provider(ui.CONFIG_FILE_PATH) - _, err := os.Stat(ui.CONFIG_FILE_PATH) + configFileProvider := file.Provider(pfconfig.CONFIG_FILE_PATH) + _, err := os.Stat(pfconfig.CONFIG_FILE_PATH) if os.IsNotExist(err) { //log.Printf("%s does not exist, USING DEFAULTS", ui.CONFIG_FILE_PATH) } else { @@ -70,13 +71,13 @@ func main() { log.Fatalf("Error loading command line flags : %s", err) } - if !pfConfig.Bool(ui.CLI_FLAG_CONFIG_ONLY) { + if !pfConfig.Bool(pfconfig.CLI_FLAG_CONFIG_ONLY) { ui.Slideshow(pfConfig) } // Reset the CLI flag so it's never writted to the config as 'true' pfConfig.Load(confmap.Provider(map[string]interface{}{ - ui.CLI_FLAG_CONFIG_ONLY: false, + pfconfig.CLI_FLAG_CONFIG_ONLY: false, }, "."), nil) ui.ConfigGui(pfConfig) diff --git a/config/README.md b/config/README.md new file mode 100644 index 0000000..a09c7c0 --- /dev/null +++ b/config/README.md @@ -0,0 +1,3 @@ +# config + +Mini module for working with PiFrame configuration file. diff --git a/ui/constants.go b/config/constants.go similarity index 98% rename from ui/constants.go rename to config/constants.go index 50f9e88..4c31789 100644 --- a/ui/constants.go +++ b/config/constants.go @@ -1,4 +1,4 @@ -package ui +package config const ( CLI_FLAG_CONFIG_ONLY = "config-ui-only" diff --git a/ui/config.go b/ui/config.go index 2d5a087..cb70a9e 100644 --- a/ui/config.go +++ b/ui/config.go @@ -1,22 +1,25 @@ package ui import ( + "bytes" "fmt" "log" "math" "net" "os" "os/exec" - "io/ioutil" "path/filepath" "strings" + "text/template" "github.com/gdamore/tcell" "github.com/guillermo/go.procmeminfo" "github.com/knadh/koanf" "github.com/knadh/koanf/parsers/toml" + "github.com/knadh/koanf/providers/confmap" "github.com/rivo/tview" + "git.kemonine.info/PiFrame/config" "git.kemonine.info/PiFrame/utils" "git.kemonine.info/PiFrame/wifi" ) @@ -28,14 +31,14 @@ const ( ) const ( - PAGE_MAIN_UI = "PAGE_MAIN_UI" + PAGE_MAIN_UI = "PAGE_MAIN_UI" PAGE_SAVE_EXIT = "PAGE_SAVE_EXIT" - PAGE_EXIT = "PAGE_EXIT" - PAGE_REBOOT = "PAGE_REBOOT" - PAGE_POWEROFF = "PAGE_POWEROFF" + PAGE_EXIT = "PAGE_EXIT" + PAGE_REBOOT = "PAGE_REBOOT" + PAGE_POWEROFF = "PAGE_POWEROFF" ) -func ConfigGui(config *koanf.Koanf) { +func ConfigGui(pfconfig *koanf.Koanf) { // Memory info for status panel meminfo := &procmeminfo.MemInfo{} err := meminfo.Update() @@ -67,7 +70,7 @@ func ConfigGui(config *koanf.Koanf) { // Get list of all folders that can be used as albums var albums []string - err = filepath.Walk(config.String(CONFIG_KEY_ALBUMS_ROOT), func(path string, fi os.FileInfo, err error) error { + err = filepath.Walk(pfconfig.String(config.CONFIG_KEY_ALBUMS_ROOT), func(path string, fi os.FileInfo, err error) error { if err != nil { return err } @@ -75,7 +78,7 @@ func ConfigGui(config *koanf.Koanf) { if strings.Contains(path, SYNCTHING_FOLDER_SKIP) { return nil } - albumName := strings.TrimPrefix(path, config.String(CONFIG_KEY_ALBUMS_ROOT)) + albumName := strings.TrimPrefix(path, pfconfig.String(config.CONFIG_KEY_ALBUMS_ROOT)) if albumName == "" { albumName = "Main Folder" } @@ -166,7 +169,7 @@ func ConfigGui(config *koanf.Koanf) { // Select Albums Form selectAlbumsForm := tview.NewForm() - configSelectedAlbums := config.Strings(CONFIG_KEY_ALBUMS_SELECTED) + configSelectedAlbums := pfconfig.Strings(config.CONFIG_KEY_ALBUMS_SELECTED) for _, album := range albums { albumSelected := false for _, configSelectedAlbum := range configSelectedAlbums { @@ -187,8 +190,8 @@ func ConfigGui(config *koanf.Koanf) { // Slide Interval Form intervalsForm := tview.NewForm() - configSlideInterval := config.String(CONFIG_KEY_SLIDESHOW_INTERVAL) - configRestartInterval := config.String(CONFIG_KEY_SLIDESHOW_RESTART_INTERVAL) + configSlideInterval := pfconfig.String(config.CONFIG_KEY_SLIDESHOW_INTERVAL) + configRestartInterval := pfconfig.String(config.CONFIG_KEY_SLIDESHOW_RESTART_INTERVAL) intervalsForm.AddInputField("Slide", configSlideInterval, 0, nil, func(value string) { configSlideInterval = value }) @@ -223,8 +226,8 @@ func ConfigGui(config *koanf.Koanf) { // HDMI On/Off Form hdmiForm := tview.NewForm() - configHDMIOff := config.String(CONFIG_KEY_HDMI_OFF) - configHDMIOn := config.String(CONFIG_KEY_HDMI_ON) + configHDMIOff := pfconfig.String(config.CONFIG_KEY_HDMI_OFF) + configHDMIOn := pfconfig.String(config.CONFIG_KEY_HDMI_ON) hdmiForm.AddInputField("HDMI Off Schedule", configHDMIOff, 0, nil, func(value string) { configHDMIOff = value }) @@ -377,21 +380,49 @@ func ConfigGui(config *koanf.Koanf) { AddButtons([]string{"Yes", "Cancel"}). SetDoneFunc(func(buttonIndex int, buttonLabel string) { if buttonLabel == "Yes" { + // Housekeeping var for data to be written to disk + var hdmiForDisk bytes.Buffer + + // Apply HDMI configuration (on/off) + valueForTemplate := utils.SystemdTimer{OnCalendar: configHDMIOn} + screenOn, err := template.New("screenon.systemd.timer").Parse(utils.SCREEN_ON_DOT_TIMER) + if err != nil { + log.Fatalf("Error setting up screen on systemd timer : %s", err) + } + err = screenOn.Execute(&hdmiForDisk, valueForTemplate) + if err != nil { + log.Fatalf("Error setting up screen on systemd timer : %s", err) + } + utils.WriteFile(utils.SCREEN_ON_TIMER_PATH, utils.SCREEN_ON_TIMER_PATH+".tmp", hdmiForDisk.Bytes()) + + valueForTemplate.OnCalendar = configHDMIOff + hdmiForDisk.Truncate(0) + screenOff, err := template.New("screenoff.systemd.timer").Parse(utils.SCREEN_OFF_DOT_TIMER) + if err != nil { + log.Fatalf("Error setting up screen off systemd timer : %s", err) + } + err = screenOff.Execute(&hdmiForDisk, valueForTemplate) + if err != nil { + log.Fatalf("Error setting up screen off systemd timer : %s", err) + } + utils.WriteFile(utils.SCREEN_OFF_TIMER_PATH, utils.SCREEN_OFF_TIMER_PATH+".tmp", hdmiForDisk.Bytes()) + + // Reload systemd units after applying HDMI config + utils.SystemdDaemonReload() + + // Apply configuration updates to main config manager prior to saving + pfconfig.Load(confmap.Provider(map[string]interface{}{ + config.DEFAULT_HDMI_ON: configHDMIOn, + config.DEFAULT_HDMI_OFF: configHDMIOff, + }, "."), nil) + + // Save configuration parser := toml.Parser() - dataForDisk, err := config.Marshal(parser) + configForDisk, err := pfconfig.Marshal(parser) if err != nil { log.Fatalf("Error preparing config for disk write : %s", err) } - existingStat, err := os.Lstat(CONFIG_FILE_PATH) - if err != nil { - log.Fatalf("Error checking if config exists : %s", err) - } - if err := ioutil.WriteFile(CONFIG_FILE_PATH_TMP, dataForDisk, existingStat.Mode()); err != nil { - log.Fatalf("Error writing temp config file : %s", err) - } - if err := os.Rename(CONFIG_FILE_PATH_TMP, CONFIG_FILE_PATH); err != nil { - log.Fatalf("Error moving new config in place : %s", err) - } + utils.WriteFile(config.CONFIG_FILE_PATH, config.CONFIG_FILE_PATH_TMP, configForDisk) app.Stop() os.Exit(0) } @@ -472,7 +503,7 @@ func ConfigGui(config *koanf.Koanf) { albumField != -1 || albumButton != -1 || intervalField != -1 || intervalButton != -1 || hdmiField != -1 || hdmiButton != -1 || - advancedField != -1 || advancedButton != -1{ + advancedField != -1 || advancedButton != -1 { switch event.Key() { case tcell.KeyUp: return tcell.NewEventKey(tcell.KeyBacktab, 0, event.Modifiers()) diff --git a/ui/slideshow.go b/ui/slideshow.go index 8647451..58494e9 100644 --- a/ui/slideshow.go +++ b/ui/slideshow.go @@ -10,6 +10,8 @@ import ( "github.com/eiannone/keyboard" "github.com/knadh/koanf" + + pfconfig "git.kemonine.info/PiFrame/config" ) const ( @@ -32,7 +34,7 @@ func Slideshow(config *koanf.Koanf) { } // Advance slideshow every interval as defined in const() - ticker := time.NewTicker(config.Duration(CONFIG_KEY_SLIDESHOW_INTERVAL)) + ticker := time.NewTicker(config.Duration(pfconfig.CONFIG_KEY_SLIDESHOW_INTERVAL)) stop_ticker := make(chan struct{}) go func() { for { @@ -99,7 +101,7 @@ func Slideshow(config *koanf.Koanf) { if err != nil { log.Fatalf("Error controlling fim : %s", err) } - ticker.Reset(config.Duration(CONFIG_KEY_SLIDESHOW_INTERVAL)) + ticker.Reset(config.Duration(pfconfig.CONFIG_KEY_SLIDESHOW_INTERVAL)) } } } diff --git a/utils/files.go b/utils/files.go new file mode 100644 index 0000000..decd66c --- /dev/null +++ b/utils/files.go @@ -0,0 +1,20 @@ +package utils + +import ( + "io/ioutil" + "log" + "os" +) + +func WriteFile(destinationPath string, tempPath string, dataForDisk []byte) { + existingStat, err := os.Lstat(destinationPath) + if err != nil { + log.Fatalf("Error checking if file exists : %s", err) + } + if err := ioutil.WriteFile(tempPath, dataForDisk, existingStat.Mode()); err != nil { + log.Fatalf("Error writing temp file : %s", err) + } + if err := os.Rename(tempPath, destinationPath); err != nil { + log.Fatalf("Error moving new in place : %s", err) + } +} diff --git a/utils/hdmi.go b/utils/hdmi.go new file mode 100644 index 0000000..2b45f0a --- /dev/null +++ b/utils/hdmi.go @@ -0,0 +1,28 @@ +package utils + +type SystemdTimer struct { + OnCalendar string +} + +const SCREEN_ON_DOT_TIMER = ` +[Unit] +Description=Turn on display + +[Timer] +OnCalendar={{.OnCalendar}} +Persistent=true + +[Install] +WantedBy=timers.target +` +const SCREEN_OFF_DOT_TIMER = ` +[Unit] +Description=Turn off display + +[Timer] +OnCalendar={{.OnCalendar}} +Persistent=true + +[Install] +WantedBy=timers.target +` diff --git a/utils/systemd.go b/utils/systemd.go new file mode 100644 index 0000000..1dd40b9 --- /dev/null +++ b/utils/systemd.go @@ -0,0 +1,23 @@ +package utils + +import ( + "log" + "os/exec" +) + +const ( + CMD_SYSTEMCTL = "/usr/bin/systemctl" +) + +const ( + SCREEN_ON_TIMER_PATH = "/etc/systemd/system/screen-on.timer" + SCREEN_OFF_TIMER_PATH = "/etc/systemd/system/screen-off.timer" +) + +func SystemdDaemonReload() { + // Reload systemd units + err := exec.Command(CMD_SYSTEMCTL, "daemon-reload").Run() + if err != nil { + log.Fatalf("Error running %s : %s", CMD_SYSTEMCTL, err) + } +} diff --git a/utils/temp.go b/utils/temperature.go similarity index 100% rename from utils/temp.go rename to utils/temperature.go