Wire up more of the config ; apply hdmi on/off schedules on save

This commit is contained in:
KemoNine 2020-09-04 23:28:01 -04:00
parent 769029b9c2
commit 15dfaa31f9
9 changed files with 148 additions and 40 deletions

View file

@ -12,6 +12,7 @@ import (
"github.com/knadh/koanf/providers/posflag" "github.com/knadh/koanf/providers/posflag"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
pfconfig "git.kemonine.info/PiFrame/config"
"git.kemonine.info/PiFrame/ui" "git.kemonine.info/PiFrame/ui"
) )
@ -23,8 +24,8 @@ func main() {
os.Exit(0) os.Exit(0)
} }
// Command line flags // Command line flags
f.Bool(ui.CLI_FLAG_CONFIG_ONLY, false, "Only show the config UI, NOT the slideshow") f.Bool(pfconfig.CLI_FLAG_CONFIG_ONLY, false, "Only show the config UI, NOT the slideshow")
cliFlag := f.Lookup(ui.CLI_FLAG_CONFIG_ONLY) cliFlag := f.Lookup(pfconfig.CLI_FLAG_CONFIG_ONLY)
if cliFlag != nil { if cliFlag != nil {
cliFlag.NoOptDefVal = "true" cliFlag.NoOptDefVal = "true"
} }
@ -36,17 +37,17 @@ func main() {
// Setup some defaults // Setup some defaults
pfConfig.Load(confmap.Provider(map[string]interface{}{ pfConfig.Load(confmap.Provider(map[string]interface{}{
ui.CONFIG_KEY_SLIDESHOW_INTERVAL: ui.DEFAULT_SLIDESHOW_INTERVAL, pfconfig.CONFIG_KEY_SLIDESHOW_INTERVAL: pfconfig.DEFAULT_SLIDESHOW_INTERVAL,
ui.CONFIG_KEY_SLIDESHOW_RESTART_INTERVAL: ui.DEFAULT_SLIDESHOW_RESTART_INTERVAL, pfconfig.CONFIG_KEY_SLIDESHOW_RESTART_INTERVAL: pfconfig.DEFAULT_SLIDESHOW_RESTART_INTERVAL,
ui.CONFIG_KEY_HDMI_OFF: ui.DEFAULT_HDMI_OFF, pfconfig.CONFIG_KEY_HDMI_OFF: pfconfig.DEFAULT_HDMI_OFF,
ui.CONFIG_KEY_HDMI_ON: ui.DEFAULT_HDMI_ON, pfconfig.CONFIG_KEY_HDMI_ON: pfconfig.DEFAULT_HDMI_ON,
ui.CONFIG_KEY_ALBUMS_ROOT: ui.DEFAULT_ALBUMS_ROOT, pfconfig.CONFIG_KEY_ALBUMS_ROOT: pfconfig.DEFAULT_ALBUMS_ROOT,
ui.CONFIG_KEY_ALBUMS_SELECTED: []string{ui.DEFAULT_ALBUM_SELECTED}, pfconfig.CONFIG_KEY_ALBUMS_SELECTED: []string{pfconfig.DEFAULT_ALBUM_SELECTED},
}, "."), nil) }, "."), nil)
// Bring in /etc/defaults/pf.toml if it exists // Bring in /etc/defaults/pf.toml if it exists
configFileProvider := file.Provider(ui.CONFIG_FILE_PATH) configFileProvider := file.Provider(pfconfig.CONFIG_FILE_PATH)
_, err := os.Stat(ui.CONFIG_FILE_PATH) _, err := os.Stat(pfconfig.CONFIG_FILE_PATH)
if os.IsNotExist(err) { if os.IsNotExist(err) {
//log.Printf("%s does not exist, USING DEFAULTS", ui.CONFIG_FILE_PATH) //log.Printf("%s does not exist, USING DEFAULTS", ui.CONFIG_FILE_PATH)
} else { } else {
@ -70,13 +71,13 @@ func main() {
log.Fatalf("Error loading command line flags : %s", err) 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) ui.Slideshow(pfConfig)
} }
// Reset the CLI flag so it's never writted to the config as 'true' // Reset the CLI flag so it's never writted to the config as 'true'
pfConfig.Load(confmap.Provider(map[string]interface{}{ pfConfig.Load(confmap.Provider(map[string]interface{}{
ui.CLI_FLAG_CONFIG_ONLY: false, pfconfig.CLI_FLAG_CONFIG_ONLY: false,
}, "."), nil) }, "."), nil)
ui.ConfigGui(pfConfig) ui.ConfigGui(pfConfig)

3
config/README.md Normal file
View file

@ -0,0 +1,3 @@
# config
Mini module for working with PiFrame configuration file.

View file

@ -1,4 +1,4 @@
package ui package config
const ( const (
CLI_FLAG_CONFIG_ONLY = "config-ui-only" CLI_FLAG_CONFIG_ONLY = "config-ui-only"

View file

@ -1,22 +1,25 @@
package ui package ui
import ( import (
"bytes"
"fmt" "fmt"
"log" "log"
"math" "math"
"net" "net"
"os" "os"
"os/exec" "os/exec"
"io/ioutil"
"path/filepath" "path/filepath"
"strings" "strings"
"text/template"
"github.com/gdamore/tcell" "github.com/gdamore/tcell"
"github.com/guillermo/go.procmeminfo" "github.com/guillermo/go.procmeminfo"
"github.com/knadh/koanf" "github.com/knadh/koanf"
"github.com/knadh/koanf/parsers/toml" "github.com/knadh/koanf/parsers/toml"
"github.com/knadh/koanf/providers/confmap"
"github.com/rivo/tview" "github.com/rivo/tview"
"git.kemonine.info/PiFrame/config"
"git.kemonine.info/PiFrame/utils" "git.kemonine.info/PiFrame/utils"
"git.kemonine.info/PiFrame/wifi" "git.kemonine.info/PiFrame/wifi"
) )
@ -28,14 +31,14 @@ const (
) )
const ( const (
PAGE_MAIN_UI = "PAGE_MAIN_UI" PAGE_MAIN_UI = "PAGE_MAIN_UI"
PAGE_SAVE_EXIT = "PAGE_SAVE_EXIT" PAGE_SAVE_EXIT = "PAGE_SAVE_EXIT"
PAGE_EXIT = "PAGE_EXIT" PAGE_EXIT = "PAGE_EXIT"
PAGE_REBOOT = "PAGE_REBOOT" PAGE_REBOOT = "PAGE_REBOOT"
PAGE_POWEROFF = "PAGE_POWEROFF" PAGE_POWEROFF = "PAGE_POWEROFF"
) )
func ConfigGui(config *koanf.Koanf) { func ConfigGui(pfconfig *koanf.Koanf) {
// Memory info for status panel // Memory info for status panel
meminfo := &procmeminfo.MemInfo{} meminfo := &procmeminfo.MemInfo{}
err := meminfo.Update() err := meminfo.Update()
@ -67,7 +70,7 @@ func ConfigGui(config *koanf.Koanf) {
// Get list of all folders that can be used as albums // Get list of all folders that can be used as albums
var albums []string 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 { if err != nil {
return err return err
} }
@ -75,7 +78,7 @@ func ConfigGui(config *koanf.Koanf) {
if strings.Contains(path, SYNCTHING_FOLDER_SKIP) { if strings.Contains(path, SYNCTHING_FOLDER_SKIP) {
return nil 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 == "" { if albumName == "" {
albumName = "Main Folder" albumName = "Main Folder"
} }
@ -166,7 +169,7 @@ func ConfigGui(config *koanf.Koanf) {
// Select Albums Form // Select Albums Form
selectAlbumsForm := tview.NewForm() selectAlbumsForm := tview.NewForm()
configSelectedAlbums := config.Strings(CONFIG_KEY_ALBUMS_SELECTED) configSelectedAlbums := pfconfig.Strings(config.CONFIG_KEY_ALBUMS_SELECTED)
for _, album := range albums { for _, album := range albums {
albumSelected := false albumSelected := false
for _, configSelectedAlbum := range configSelectedAlbums { for _, configSelectedAlbum := range configSelectedAlbums {
@ -187,8 +190,8 @@ func ConfigGui(config *koanf.Koanf) {
// Slide Interval Form // Slide Interval Form
intervalsForm := tview.NewForm() intervalsForm := tview.NewForm()
configSlideInterval := config.String(CONFIG_KEY_SLIDESHOW_INTERVAL) configSlideInterval := pfconfig.String(config.CONFIG_KEY_SLIDESHOW_INTERVAL)
configRestartInterval := config.String(CONFIG_KEY_SLIDESHOW_RESTART_INTERVAL) configRestartInterval := pfconfig.String(config.CONFIG_KEY_SLIDESHOW_RESTART_INTERVAL)
intervalsForm.AddInputField("Slide", configSlideInterval, 0, nil, func(value string) { intervalsForm.AddInputField("Slide", configSlideInterval, 0, nil, func(value string) {
configSlideInterval = value configSlideInterval = value
}) })
@ -223,8 +226,8 @@ func ConfigGui(config *koanf.Koanf) {
// HDMI On/Off Form // HDMI On/Off Form
hdmiForm := tview.NewForm() hdmiForm := tview.NewForm()
configHDMIOff := config.String(CONFIG_KEY_HDMI_OFF) configHDMIOff := pfconfig.String(config.CONFIG_KEY_HDMI_OFF)
configHDMIOn := config.String(CONFIG_KEY_HDMI_ON) configHDMIOn := pfconfig.String(config.CONFIG_KEY_HDMI_ON)
hdmiForm.AddInputField("HDMI Off Schedule", configHDMIOff, 0, nil, func(value string) { hdmiForm.AddInputField("HDMI Off Schedule", configHDMIOff, 0, nil, func(value string) {
configHDMIOff = value configHDMIOff = value
}) })
@ -377,21 +380,49 @@ func ConfigGui(config *koanf.Koanf) {
AddButtons([]string{"Yes", "Cancel"}). AddButtons([]string{"Yes", "Cancel"}).
SetDoneFunc(func(buttonIndex int, buttonLabel string) { SetDoneFunc(func(buttonIndex int, buttonLabel string) {
if buttonLabel == "Yes" { 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() parser := toml.Parser()
dataForDisk, err := config.Marshal(parser) configForDisk, err := pfconfig.Marshal(parser)
if err != nil { if err != nil {
log.Fatalf("Error preparing config for disk write : %s", err) log.Fatalf("Error preparing config for disk write : %s", err)
} }
existingStat, err := os.Lstat(CONFIG_FILE_PATH) utils.WriteFile(config.CONFIG_FILE_PATH, config.CONFIG_FILE_PATH_TMP, configForDisk)
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)
}
app.Stop() app.Stop()
os.Exit(0) os.Exit(0)
} }
@ -472,7 +503,7 @@ func ConfigGui(config *koanf.Koanf) {
albumField != -1 || albumButton != -1 || albumField != -1 || albumButton != -1 ||
intervalField != -1 || intervalButton != -1 || intervalField != -1 || intervalButton != -1 ||
hdmiField != -1 || hdmiButton != -1 || hdmiField != -1 || hdmiButton != -1 ||
advancedField != -1 || advancedButton != -1{ advancedField != -1 || advancedButton != -1 {
switch event.Key() { switch event.Key() {
case tcell.KeyUp: case tcell.KeyUp:
return tcell.NewEventKey(tcell.KeyBacktab, 0, event.Modifiers()) return tcell.NewEventKey(tcell.KeyBacktab, 0, event.Modifiers())

View file

@ -10,6 +10,8 @@ import (
"github.com/eiannone/keyboard" "github.com/eiannone/keyboard"
"github.com/knadh/koanf" "github.com/knadh/koanf"
pfconfig "git.kemonine.info/PiFrame/config"
) )
const ( const (
@ -32,7 +34,7 @@ func Slideshow(config *koanf.Koanf) {
} }
// Advance slideshow every interval as defined in const() // 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{}) stop_ticker := make(chan struct{})
go func() { go func() {
for { for {
@ -99,7 +101,7 @@ func Slideshow(config *koanf.Koanf) {
if err != nil { if err != nil {
log.Fatalf("Error controlling fim : %s", err) log.Fatalf("Error controlling fim : %s", err)
} }
ticker.Reset(config.Duration(CONFIG_KEY_SLIDESHOW_INTERVAL)) ticker.Reset(config.Duration(pfconfig.CONFIG_KEY_SLIDESHOW_INTERVAL))
} }
} }
} }

20
utils/files.go Normal file
View file

@ -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)
}
}

28
utils/hdmi.go Normal file
View file

@ -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
`

23
utils/systemd.go Normal file
View file

@ -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)
}
}