Compare commits
No commits in common. "main" and "tests" have entirely different histories.
2
.gitignore
vendored
2
.gitignore
vendored
@ -23,5 +23,3 @@ go.work
|
|||||||
/cmd/git_version.go
|
/cmd/git_version.go
|
||||||
/dist
|
/dist
|
||||||
.env
|
.env
|
||||||
screen_off
|
|
||||||
screen_on
|
|
||||||
|
|||||||
3
Makefile
3
Makefile
@ -10,9 +10,6 @@ init:
|
|||||||
go mod download
|
go mod download
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
||||||
install-tools:
|
|
||||||
grep _ tools.go | awk -F'"' '{print $$2}' | xargs -tI % go install %
|
|
||||||
|
|
||||||
generate: init
|
generate: init
|
||||||
go generate
|
go generate
|
||||||
|
|
||||||
|
|||||||
87
cmd/root.go
87
cmd/root.go
@ -1,18 +1,15 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"log/slog"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"strings"
|
||||||
"syscall"
|
"time"
|
||||||
|
|
||||||
// "git.bloy.org/mike/hasshelper/kiosk"
|
|
||||||
"git.bloy.org/mike/hasshelper/web"
|
"git.bloy.org/mike/hasshelper/web"
|
||||||
_ "github.com/joho/godotenv/autoload"
|
"github.com/rs/zerolog"
|
||||||
"github.com/lmittmann/tint"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
@ -26,37 +23,26 @@ var rootCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func rootCmdRun(cmd *cobra.Command, args []string) {
|
func rootCmdRun(cmd *cobra.Command, args []string) {
|
||||||
var logLevel slog.Level
|
logLevel, err := zerolog.ParseLevel(viper.GetString("loglevel"))
|
||||||
logLevel.UnmarshalText([]byte(viper.GetString("loglevel")))
|
zerolog.SetGlobalLevel(logLevel)
|
||||||
logger := slog.New(
|
if err != nil {
|
||||||
tint.NewHandler(os.Stdout, &tint.Options{
|
fmt.Fprintln(os.Stderr, err)
|
||||||
Level: logLevel,
|
os.Exit(1)
|
||||||
TimeFormat: "2006-01-02T15:04:05.999",
|
}
|
||||||
NoColor: viper.GetString("deployment") == "prod",
|
var logger zerolog.Logger
|
||||||
}))
|
if viper.GetString("deployment") == "prod" {
|
||||||
logger.Info("HASSHelper startup", "version", viper.GetString("version"))
|
logger = zerolog.New(os.Stderr)
|
||||||
|
} else {
|
||||||
|
logger = zerolog.New(zerolog.ConsoleWriter{
|
||||||
|
Out: os.Stdout,
|
||||||
|
FormatLevel: func(i interface{}) string { return strings.ToUpper(fmt.Sprintf("%-6s|", i)) },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
logger = logger.With().Timestamp().Caller().Logger()
|
||||||
|
logger.Info().Str("version", viper.GetString("version")).Msg("HASSHelper startup")
|
||||||
exitchan := make(chan bool)
|
exitchan := make(chan bool)
|
||||||
signalchan := make(chan os.Signal, 1)
|
web.Run(logger, exitchan)
|
||||||
done := make(chan bool, 2)
|
<-exitchan // run the main command until one of the goroutines is done
|
||||||
signal.Notify(signalchan, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-signalchan:
|
|
||||||
logger.Warn("received interrupt. Exiting")
|
|
||||||
cancel()
|
|
||||||
done <- true
|
|
||||||
case <-exitchan:
|
|
||||||
logger.Error("unexpected exit of component")
|
|
||||||
cancel()
|
|
||||||
done <- true
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
go web.Run(logger, exitchan, ctx)
|
|
||||||
// go kiosk.Run(logger, exitchan, ctx)
|
|
||||||
logger.Debug("Waiting for exit")
|
|
||||||
<-done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute will kick off cobra's processing of the root command
|
// Execute will kick off cobra's processing of the root command
|
||||||
@ -68,27 +54,26 @@ func Execute() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var userConfigDir, err = os.UserConfigDir()
|
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "",
|
||||||
|
"config file (default is $HOME/.config/hasshelper/config.toml)")
|
||||||
|
cobra.OnInitialize(initConfig)
|
||||||
const dirName = "hasshelper"
|
const dirName = "hasshelper"
|
||||||
var defDir = fmt.Sprintf("%s%c%s", "/etc", os.PathSeparator, dirName)
|
var userConfigDir, err = os.UserConfigDir()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
defDir = fmt.Sprintf("%s%c%s", userConfigDir, os.PathSeparator, dirName)
|
viper.AddConfigPath(
|
||||||
viper.AddConfigPath(defDir)
|
fmt.Sprintf("%s%c%s", userConfigDir, os.PathSeparator, dirName))
|
||||||
} else {
|
} else {
|
||||||
log.Println("could not locate user config dir:", err)
|
log.Println("could not locate user config dir:", err)
|
||||||
}
|
}
|
||||||
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "",
|
|
||||||
fmt.Sprintf("config file (default is %s%c%s)", defDir, os.PathSeparator, "hasshelper.toml"))
|
|
||||||
cobra.OnInitialize(initConfig)
|
|
||||||
viper.AddConfigPath(fmt.Sprintf("/etc%c%s", os.PathSeparator, dirName))
|
viper.AddConfigPath(fmt.Sprintf("/etc%c%s", os.PathSeparator, dirName))
|
||||||
|
viper.SetConfigName("config.toml")
|
||||||
|
viper.SetEnvPrefix("hasshelper")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initConfig() {
|
func initConfig() {
|
||||||
viper.Set("version", gitVersion)
|
viper.Set("version", gitVersion)
|
||||||
viper.SetDefault("deployment", "prod")
|
viper.SetDefault("deployment", "prod")
|
||||||
viper.SetDefault("loglevel", "info")
|
viper.SetDefault("loglevel", "info")
|
||||||
viper.SetDefault("kiosk_cmd_shell", "bash")
|
|
||||||
viper.SetEnvPrefix("HASS")
|
|
||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
if cfgFile != "" {
|
if cfgFile != "" {
|
||||||
viper.SetConfigFile(cfgFile)
|
viper.SetConfigFile(cfgFile)
|
||||||
@ -105,18 +90,12 @@ func initConfig() {
|
|||||||
"image_dir",
|
"image_dir",
|
||||||
"version",
|
"version",
|
||||||
"webserver_port",
|
"webserver_port",
|
||||||
// "mqtt_broker_url",
|
|
||||||
// "mqtt_broker_user",
|
|
||||||
// "mqtt_broker_password",
|
|
||||||
// "mqtt_presence_topic",
|
|
||||||
// "kiosk_cmd_shell",
|
|
||||||
// "kiosk_cmd_screen_on",
|
|
||||||
// "kiosk_cmd_screen_off",
|
|
||||||
}
|
}
|
||||||
for _, key := range expected_config {
|
for _, key := range expected_config {
|
||||||
if !viper.IsSet(key) {
|
if !viper.IsSet(key) {
|
||||||
log.Fatalf("Missing configuration value: %s\n", key)
|
fmt.Fprintf(os.Stderr, "Missing configuration value: %s\n", key)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,12 +14,6 @@ func TestInitConfig(t *testing.T) {
|
|||||||
viper.Set("deployment", "testing")
|
viper.Set("deployment", "testing")
|
||||||
viper.Set("image_dir", "/tmp")
|
viper.Set("image_dir", "/tmp")
|
||||||
viper.Set("webserver_port", "8080")
|
viper.Set("webserver_port", "8080")
|
||||||
viper.Set("mqtt_broker_url", "mqtt://example.com:1883")
|
|
||||||
viper.Set("mqtt_broker_user", "testuser")
|
|
||||||
viper.Set("mqtt_broker_password", "testpass")
|
|
||||||
viper.Set("mqtt_presence_topic", "home/foo/test")
|
|
||||||
viper.Set("kiosk_cmd_screen_on", "echo screen on")
|
|
||||||
viper.Set("kiosk_cmd_screen_off", "echo screen off")
|
|
||||||
|
|
||||||
initConfig()
|
initConfig()
|
||||||
assert.Equal(t, gitVersion, viper.GetString("version"), "config version mismatch")
|
assert.Equal(t, gitVersion, viper.GetString("version"), "config version mismatch")
|
||||||
@ -31,12 +25,6 @@ func TestInitConfigMissingImageDir(t *testing.T) {
|
|||||||
viper.Set("deployment", "testing")
|
viper.Set("deployment", "testing")
|
||||||
viper.Set("version", "vTest")
|
viper.Set("version", "vTest")
|
||||||
viper.Set("webserver_port", "8080")
|
viper.Set("webserver_port", "8080")
|
||||||
viper.Set("mqtt_broker_url", "mqtt://example.com:1883")
|
|
||||||
viper.Set("mqtt_broker_user", "testuser")
|
|
||||||
viper.Set("mqtt_broker_password", "testpass")
|
|
||||||
viper.Set("mqtt_presence_topic", "home/foo/test")
|
|
||||||
viper.Set("kiosk_cmd_screen_on", "echo screen on")
|
|
||||||
viper.Set("kiosk_cmd_screen_off", "echo screen off")
|
|
||||||
|
|
||||||
initConfig()
|
initConfig()
|
||||||
return
|
return
|
||||||
|
|||||||
4
go.mod
4
go.mod
@ -7,10 +7,8 @@ toolchain go1.23.1
|
|||||||
require (
|
require (
|
||||||
github.com/cortesi/devd v0.0.0-20200427000907-c1a3bfba27d8
|
github.com/cortesi/devd v0.0.0-20200427000907-c1a3bfba27d8
|
||||||
github.com/cortesi/modd v0.8.1
|
github.com/cortesi/modd v0.8.1
|
||||||
github.com/eclipse/paho.mqtt.golang v1.5.0
|
|
||||||
github.com/joho/godotenv v1.5.1
|
|
||||||
github.com/lmittmann/tint v1.0.5
|
|
||||||
github.com/mdomke/git-semver/v6 v6.9.0
|
github.com/mdomke/git-semver/v6 v6.9.0
|
||||||
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
|
|||||||
13
go.sum
13
go.sum
@ -28,6 +28,7 @@ github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
|
|||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||||
github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY=
|
github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY=
|
||||||
github.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
github.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cortesi/devd v0.0.0-20200427000907-c1a3bfba27d8 h1:1vi6SfP/dbmV8Yg4OuuJCSRAZhinuhVnA/YLISA91X0=
|
github.com/cortesi/devd v0.0.0-20200427000907-c1a3bfba27d8 h1:1vi6SfP/dbmV8Yg4OuuJCSRAZhinuhVnA/YLISA91X0=
|
||||||
github.com/cortesi/devd v0.0.0-20200427000907-c1a3bfba27d8/go.mod h1:KNI5SyGy0Z2vqxTcYNeuNEWZY1cOSMO6q/W0DgqVcs0=
|
github.com/cortesi/devd v0.0.0-20200427000907-c1a3bfba27d8/go.mod h1:KNI5SyGy0Z2vqxTcYNeuNEWZY1cOSMO6q/W0DgqVcs0=
|
||||||
github.com/cortesi/modd v0.8.1 h1:0s8e10CJ6pxc6NQHYFrmUZOLP0X6v63ry+3na6Gq2Ow=
|
github.com/cortesi/modd v0.8.1 h1:0s8e10CJ6pxc6NQHYFrmUZOLP0X6v63ry+3na6Gq2Ow=
|
||||||
@ -56,8 +57,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
|
|||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
|
|
||||||
github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
|
|
||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
@ -85,6 +84,7 @@ github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZt
|
|||||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d h1:lBXNCxVENCipq4D1Is42JVOP4eQjlB8TQ6H69Yx5J9Q=
|
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d h1:lBXNCxVENCipq4D1Is42JVOP4eQjlB8TQ6H69Yx5J9Q=
|
||||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
@ -106,8 +106,6 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf
|
|||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
|
||||||
github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
||||||
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
||||||
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
||||||
@ -122,8 +120,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lmittmann/tint v1.0.5 h1:NQclAutOfYsqs2F1Lenue6OoWCajs5wJcP3DfWVpePw=
|
|
||||||
github.com/lmittmann/tint v1.0.5/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
|
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
@ -139,6 +135,7 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
|||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mdomke/git-semver/v6 v6.9.0 h1:pVqzo3LTT0iuiVeicOJwElH1l2CBlbaVHXNncWi08U0=
|
github.com/mdomke/git-semver/v6 v6.9.0 h1:pVqzo3LTT0iuiVeicOJwElH1l2CBlbaVHXNncWi08U0=
|
||||||
@ -170,6 +167,9 @@ github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4Ug
|
|||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
|
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
||||||
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
||||||
@ -276,6 +276,7 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
|||||||
@ -1,59 +0,0 @@
|
|||||||
package kiosk
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log/slog"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
type handler func(area string, present bool)
|
|
||||||
|
|
||||||
type configObj struct {
|
|
||||||
logger *slog.Logger
|
|
||||||
broker string
|
|
||||||
username string
|
|
||||||
password string
|
|
||||||
presenceTopic string
|
|
||||||
shell string
|
|
||||||
screen_on_cmd string
|
|
||||||
screen_off_cmd string
|
|
||||||
}
|
|
||||||
|
|
||||||
var config = configObj{}
|
|
||||||
|
|
||||||
func Run(rootLogger *slog.Logger, exitch chan bool, ctx context.Context) {
|
|
||||||
config.logger = rootLogger.With("component", "kiosk")
|
|
||||||
config.broker = viper.GetString("mqtt_broker_url")
|
|
||||||
config.username = viper.GetString("mqtt_broker_user")
|
|
||||||
config.password = viper.GetString("mqtt_broker_password")
|
|
||||||
config.presenceTopic = viper.GetString("mqtt_presence_topic")
|
|
||||||
config.shell = viper.GetString("kiosk_cmd_shell")
|
|
||||||
config.screen_on_cmd = viper.GetString("kiosk_cmd_screen_on")
|
|
||||||
config.screen_off_cmd = viper.GetString("kiosk_cmd_screen_off")
|
|
||||||
config.logger.Info("starting MQTT broker client")
|
|
||||||
brokerConsume(ctx)
|
|
||||||
exitch <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlePresenceMessage(msg [2]string) {
|
|
||||||
logger := config.logger
|
|
||||||
logger.Debug("received presence message", "msg", msg)
|
|
||||||
cmdStr := ""
|
|
||||||
switch msg[1] {
|
|
||||||
case "on":
|
|
||||||
cmdStr = config.screen_on_cmd
|
|
||||||
case "off":
|
|
||||||
cmdStr = config.screen_off_cmd
|
|
||||||
default:
|
|
||||||
logger.Warn("recieved unexpected presence message", "msg", msg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.Info("Presence change event", "presence", msg[1], "command", cmdStr)
|
|
||||||
cmd := exec.Command(config.shell, "-c", cmdStr)
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error while executing command", "msg", msg, "command", cmdStr, "err", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
package kiosk
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
|
||||||
)
|
|
||||||
|
|
||||||
func brokerConsume(ctx context.Context) {
|
|
||||||
logger := config.logger
|
|
||||||
opts := MQTT.NewClientOptions()
|
|
||||||
opts.AddBroker(config.broker)
|
|
||||||
hostname, err := os.Hostname()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
messages := make(chan [2]string)
|
|
||||||
opts.SetClientID(fmt.Sprintf("hasskiosk-%s-%d", hostname, os.Getpid()))
|
|
||||||
opts.SetUsername(config.username)
|
|
||||||
opts.SetPassword(config.password)
|
|
||||||
opts.SetDefaultPublishHandler(func(client MQTT.Client, msg MQTT.Message) {
|
|
||||||
messages <- [2]string{msg.Topic(), string(msg.Payload())}
|
|
||||||
})
|
|
||||||
done := make(chan bool)
|
|
||||||
|
|
||||||
logger.Debug("creating client")
|
|
||||||
client := MQTT.NewClient(opts)
|
|
||||||
logger.Debug("starting client")
|
|
||||||
if token := client.Connect(); token.Wait() && token.Error() != nil {
|
|
||||||
logger.Error("MQTT client startup problem", "err", token.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer client.Disconnect(250)
|
|
||||||
if token := client.Subscribe(config.presenceTopic, 1, nil); token.Wait() && token.Error() != nil {
|
|
||||||
logger.Error("MQTT subscribe error", "err", token.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
for true {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
logger.Info("Context shutdown. Exiting MQTT loop")
|
|
||||||
done <- true
|
|
||||||
return
|
|
||||||
case msg := <-messages:
|
|
||||||
go handlePresenceMessage(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
@ -9,5 +9,6 @@
|
|||||||
**/*.go .env {
|
**/*.go .env {
|
||||||
prep: go mod tidy
|
prep: go mod tidy
|
||||||
prep: go test @dirmods
|
prep: go test @dirmods
|
||||||
daemon +sigterm: go run ./main.go
|
prep: go build
|
||||||
|
daemon +sigterm: ./hasshelper -c ../hasshelper.toml
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,20 +18,20 @@ func imageHandler() http.Handler {
|
|||||||
|
|
||||||
func chooseRandomFile(ctx context.Context, imageDir string) (filepath string, err error) {
|
func chooseRandomFile(ctx context.Context, imageDir string) (filepath string, err error) {
|
||||||
validEntries := make([]string, 0)
|
validEntries := make([]string, 0)
|
||||||
log := config.logger.With("imageDir", imageDir)
|
log := config.logger.With().Str("imageDir", imageDir).Logger()
|
||||||
err = fs.WalkDir(
|
err = fs.WalkDir(
|
||||||
os.DirFS(imageDir), ".", func(path string, d fs.DirEntry, err error) error {
|
os.DirFS(imageDir), ".", func(path string, d fs.DirEntry, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error walking directory", "err", err)
|
log.Error().Err(err).Msg("Error walking directory")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
if ctxErr := ctx.Err(); ctxErr != nil {
|
||||||
log.Error("Context closed while walking directory", "err", ctxErr)
|
log.Error().Err(ctxErr).Msg("Context closed while walking directory")
|
||||||
return ctxErr
|
return ctxErr
|
||||||
}
|
}
|
||||||
if d.Type().IsRegular() {
|
if d.Type().IsRegular() {
|
||||||
filename := fmt.Sprintf("%s%c%s", imageDir, os.PathSeparator, d.Name())
|
filename := fmt.Sprintf("%s%c%s", imageDir, os.PathSeparator, d.Name())
|
||||||
log.Debug("file scan", filename, "filename")
|
log.Debug().Str("filename", filename).Msg("file scan")
|
||||||
validEntries = append(validEntries, filename)
|
validEntries = append(validEntries, filename)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -52,10 +52,10 @@ func randomImageFunc(w http.ResponseWriter, r *http.Request) {
|
|||||||
log := config.logger
|
log := config.logger
|
||||||
path, err := chooseRandomFile(r.Context(), config.imageDir)
|
path, err := chooseRandomFile(r.Context(), config.imageDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Couldn't choose a random file", "err", err)
|
log.Error().Err(err).Msg("Couldn't choose a random file")
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Info("random image served", "imagefile", path)
|
log.Info().Str("imageFile", path).Msg("random image served")
|
||||||
http.ServeFile(w, r, path)
|
http.ServeFile(w, r, path)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,17 +2,14 @@ package web
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type configObj struct {
|
type configObj struct {
|
||||||
logger *slog.Logger
|
logger zerolog.Logger
|
||||||
port int
|
port int
|
||||||
imageDir string
|
imageDir string
|
||||||
}
|
}
|
||||||
@ -21,9 +18,8 @@ var config = configObj{}
|
|||||||
|
|
||||||
// Run starts up the webserver part of hasshelper. It writes to exitch if
|
// Run starts up the webserver part of hasshelper. It writes to exitch if
|
||||||
// the webserver ends unexpectedly. Config values will be read from viper.
|
// the webserver ends unexpectedly. Config values will be read from viper.
|
||||||
func Run(rootLogger *slog.Logger, exitch chan bool, ctx context.Context) {
|
func Run(rootLogger zerolog.Logger, exitch chan bool) {
|
||||||
rootLogger = rootLogger.With("component", "web")
|
config.logger = rootLogger.With().Str("name", "web").Logger()
|
||||||
config.logger = rootLogger
|
|
||||||
config.port = viper.GetInt("webserver_port")
|
config.port = viper.GetInt("webserver_port")
|
||||||
config.imageDir = viper.GetString("image_dir")
|
config.imageDir = viper.GetString("image_dir")
|
||||||
|
|
||||||
@ -31,34 +27,17 @@ func Run(rootLogger *slog.Logger, exitch chan bool, ctx context.Context) {
|
|||||||
http.HandleFunc("/randimg/", randomImageFunc)
|
http.HandleFunc("/randimg/", randomImageFunc)
|
||||||
var logger = config.logger
|
var logger = config.logger
|
||||||
|
|
||||||
logger.Info("Webserver startup", "port", config.port, "imageDir", config.imageDir)
|
logger.Info().
|
||||||
go func() {
|
Int("port", config.port).
|
||||||
server := http.Server{
|
Str("imageDir", config.imageDir).
|
||||||
Addr: fmt.Sprintf(":%d", config.port),
|
Msg("Webserver startup")
|
||||||
Handler: middleware(http.DefaultServeMux),
|
|
||||||
BaseContext: func(net.Listener) context.Context {
|
|
||||||
return ctx
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := server.ListenAndServe(); err != nil {
|
|
||||||
logger.Error("Webserver fatal error", "err", err)
|
|
||||||
} else {
|
|
||||||
logger.Info("Webserver shutting down")
|
|
||||||
}
|
|
||||||
exitch <- true
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func middleware(next http.Handler) http.Handler {
|
addr := fmt.Sprintf(":%d", config.port)
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
logger := config.logger.
|
if err := http.ListenAndServe(addr, nil); err != nil {
|
||||||
WithGroup("request").
|
logger.Error().Err(err).Msg("Webserver fatal error")
|
||||||
With("method", r.Method,
|
} else {
|
||||||
"url", r.URL)
|
logger.Info().Msg("webserver shutting down")
|
||||||
logger.Info("Starting web request")
|
}
|
||||||
start := time.Now()
|
exitch <- true
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
elapsed := time.Now().Sub(start)
|
|
||||||
logger.Info("Web request", "durationMS", elapsed.Milliseconds())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user