Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/notifications #120

Merged
merged 8 commits into from
May 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
5 changes: 3 additions & 2 deletions internal/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
}()

Expand Down Expand Up @@ -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)
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion internal/configuration/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion internal/persistence/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions internal/ui/logging.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ui

import (
"fmt"
"github.com/pterm/pterm"
)

Expand Down Expand Up @@ -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...)
}
80 changes: 80 additions & 0 deletions internal/ui/notification.go
Original file line number Diff line number Diff line change
@@ -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)
}
}