diff --git a/README.md b/README.md index 0fb4d73..4b117d6 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,9 @@ yay -S fan2go-git Download the latest release from GitHub: ```shell +# Install dependencies +sudo pacman -S libnotify + curl -L -o fan2go https://github.com/markusressel/fan2go/releases/latest/download/fan2go-linux-amd64 chmod +x fan2go sudo cp ./fan2go /usr/bin/fan2go diff --git a/cmd/root.go b/cmd/root.go index de2b82b..eb5a69d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -38,7 +38,8 @@ on your computer based on temperature sensors.`, configuration.LoadConfig() err := configuration.Validate(configPath) if err != nil { - ui.Fatal(err.Error()) + ui.ErrorAndNotify("Config Validation Error", err.Error()) + return } internal.RunDaemon() diff --git a/internal/backend.go b/internal/backend.go index beb08c6..29667fc 100644 --- a/internal/backend.go +++ b/internal/backend.go @@ -64,7 +64,7 @@ func RunDaemon() { go func() { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { - ui.Error("Cannot start prometheus metrics endpoint (%s)", err.Error()) + ui.ErrorAndNotify("Statistics Error", "Cannot start prometheus metrics endpoint (%s)", err.Error()) } }() @@ -114,12 +114,13 @@ func RunDaemon() { err := fanController.Run(ctx) ui.Info("Fan controller for fan %s stopped.", fan.GetId()) if err != nil { + ui.NotifyError(fmt.Sprintf("Fan Controller: %s", fan.GetId()), err.Error()) panic(err) } return err }, func(err error) { if err != nil { - ui.Warning("Something went wrong: %v", err) + ui.WarningAndNotify(fmt.Sprintf("Fan Controller: %s", fan.GetId()), "Something went wrong: %v", err) } }) } diff --git a/internal/configuration/config.go b/internal/configuration/config.go index 62b12fc..7bce3c6 100644 --- a/internal/configuration/config.go +++ b/internal/configuration/config.go @@ -42,7 +42,7 @@ func InitConfig(cfgFile string) { // Find home directory. home, err := homedir.Dir() if err != nil { - ui.Error("Couldn't detect home directory: %v", err) + ui.ErrorAndNotify("Path Error", "Couldn't detect home directory: %v", err) os.Exit(1) } diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 39b4309..3b3224d 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -146,7 +146,7 @@ func (f *fanController) Run(ctx context.Context) error { case <-tick: err = f.UpdateFanSpeed() if err != nil { - ui.Error("Error in FanController for fan %s: %v", fan.GetId(), err) + ui.ErrorAndNotify("Fan Control Error", "Fan %s: %v", fan.GetId(), err) f.restorePwmEnabled() return nil } diff --git a/internal/persistence/persistence.go b/internal/persistence/persistence.go index 70274b9..ddb291b 100644 --- a/internal/persistence/persistence.go +++ b/internal/persistence/persistence.go @@ -34,7 +34,7 @@ func NewPersistence(dbPath string) Persistence { func (p persistence) openPersistence() *bolt.DB { db, err := bolt.Open(p.dbPath, 0600, &bolt.Options{Timeout: 1 * time.Minute}) if err != nil { - ui.Error("Could not open database file: %v", err) + ui.ErrorAndNotify("Persistence Error", "Could not open database file: %v", err) os.Exit(1) } return db diff --git a/internal/ui/logging.go b/internal/ui/logging.go index a65bd96..a7e3562 100644 --- a/internal/ui/logging.go +++ b/internal/ui/logging.go @@ -1,6 +1,7 @@ package ui import ( + "fmt" "github.com/pterm/pterm" ) @@ -32,10 +33,21 @@ func Warning(format string, a ...interface{}) { pterm.Warning.Printfln(format, a...) } +func WarningAndNotify(title string, format string, a ...interface{}) { + Error(format, a...) + NotifyError(title, fmt.Sprintf(format, a...)) +} + func Error(format string, a ...interface{}) { pterm.Error.Printfln(format, a...) } +func ErrorAndNotify(title string, format string, a ...interface{}) { + Error(format, a...) + NotifyError(title, fmt.Sprintf(format, a...)) +} + func Fatal(format string, a ...interface{}) { + NotifyError("Fatal Error", fmt.Sprintf(format, a...)) pterm.Fatal.Printfln(format, a...) } diff --git a/internal/ui/notification.go b/internal/ui/notification.go new file mode 100644 index 0000000..d0206c6 --- /dev/null +++ b/internal/ui/notification.go @@ -0,0 +1,80 @@ +package ui + +import ( + "os" + "os/exec" + "strings" +) + +// For a list of possible icons, see: https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html +const ( + IconDialogError = "dialog-error" + IconDialogInfo = "dialog-information" + IconDialogWarn = "dialog-warning" + + UrgencyLow = "low" + UrgencyNormal = "normal" + UrgencyCritical = "critical" +) + +func NotifyInfo(title, text string) { + NotifySend(UrgencyLow, title, text, IconDialogInfo) +} + +func NotifyWarn(title, text string) { + NotifySend(UrgencyNormal, title, text, IconDialogWarn) +} + +func NotifyError(title, text string) { + NotifySend(UrgencyCritical, title, text, IconDialogError) +} + +func NotifySend(urgency, title, text, icon string) { + display, exists := os.LookupEnv("DISPLAY") + if !exists { + Warning("Cannot send notification, missing env variable 'DISPLAY'!") + return + } + + cmd := exec.Command("who") + output, err := cmd.Output() + if err != nil { + Warning("Cannot send notification, unable to find user of display session: %v", err) + return + } + lines := strings.Split(string(output), "\n") + var user string + for _, line := range lines { + if strings.Contains(line, display) { + user = strings.TrimSpace(strings.Fields(line)[0]) + break + } + } + + if len(user) <= 0 { + Warning("Cannot send notification, unable to detect user of current display session") + return + } + + cmd = exec.Command("id", "-u", user) + output, err = cmd.Output() + userIdString := strings.TrimSpace(string(output)) + if len(userIdString) <= 0 { + Warning("Cannot send notification, unable to detect user id") + return + } + + cmd = exec.Command("sudo", "-u", user, + "DISPLAY="+display, + "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/"+userIdString+"/bus", + "notify-send", + "-a", "fan2go", + "-u", urgency, + "-i", icon, + title, text, + ) + err = cmd.Run() + if err != nil { + Error("Error sending notification: %v", err) + } +}