74 lines
2 KiB
Go
74 lines
2 KiB
Go
// Package displaycontroller steuert das physische Display per xset DPMS.
|
|
package displaycontroller
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"os/exec"
|
|
"sync"
|
|
)
|
|
|
|
// Controller führt xset-Befehle aus und verfolgt den letzten Display-Zustand.
|
|
// onStateChange wird nach jeder erfolgreichen Ausführung mit dem neuen Zustand
|
|
// aufgerufen (z. B. um ihn per MQTT zu publizieren). Darf nil sein.
|
|
type Controller struct {
|
|
display string
|
|
screenSlug string
|
|
onStateChange func(screenSlug, state string)
|
|
mu sync.Mutex
|
|
currentState string
|
|
}
|
|
|
|
// New erstellt einen Controller. display ist der Wert der DISPLAY-Env-Var (z. B. ":0").
|
|
func New(display, screenSlug string, onStateChange func(screenSlug, state string)) *Controller {
|
|
return &Controller{
|
|
display: display,
|
|
screenSlug: screenSlug,
|
|
onStateChange: onStateChange,
|
|
currentState: "unknown",
|
|
}
|
|
}
|
|
|
|
// Execute führt eine Display-Aktion aus. Bekannte Aktionen: "display_on", "display_off".
|
|
func (c *Controller) Execute(action string) {
|
|
switch action {
|
|
case "display_on":
|
|
if err := c.runXset("on"); err != nil {
|
|
slog.Error("display_on failed", "err", err)
|
|
return
|
|
}
|
|
c.setState("on")
|
|
case "display_off":
|
|
if err := c.runXset("off"); err != nil {
|
|
slog.Error("display_off failed", "err", err)
|
|
return
|
|
}
|
|
c.setState("off")
|
|
default:
|
|
slog.Warn("unknown display action", "action", action)
|
|
}
|
|
}
|
|
|
|
// State gibt den zuletzt gesetzten Display-Zustand zurück: "on", "off" oder "unknown".
|
|
func (c *Controller) State() string {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
return c.currentState
|
|
}
|
|
|
|
func (c *Controller) runXset(arg string) error {
|
|
out, err := exec.Command("xset", "-display", c.display, "dpms", "force", arg).CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("xset dpms force %s: %w (output: %s)", arg, err, out)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Controller) setState(state string) {
|
|
c.mu.Lock()
|
|
c.currentState = state
|
|
c.mu.Unlock()
|
|
if c.onStateChange != nil {
|
|
go c.onStateChange(c.screenSlug, state)
|
|
}
|
|
}
|