1
0

daemon: pause work on device disconnect to save battery

This commit is contained in:
Maksim Totmin 2026-06-22 10:25:10 +07:00
parent cf190c57e9
commit 83fd596ba1
2 changed files with 55 additions and 4 deletions

View File

@ -9,6 +9,7 @@ import (
"regexp"
"strings"
"sync"
"sync/atomic"
"time"
"streamdeck-lets-go/internal/config"
@ -357,6 +358,7 @@ type AutoSwitchManager struct {
lastManualPage string
autoPage string
autoStay bool
paused atomic.Bool // disables auto-switch when device is disconnected
}
type compiledRule struct {
@ -410,6 +412,25 @@ func (m *AutoSwitchManager) NotifyManualPage(name string) {
slog.Debug("auto-switch: manual page set", "page", name)
}
// Pause disables auto-switch evaluation — use when the device is disconnected
// to prevent unnecessary page switches on a closed deck.
func (m *AutoSwitchManager) Pause() {
m.paused.Store(true)
slog.Debug("auto-switch: paused")
}
// Resume re-enables auto-switch evaluation after reconnect.
// Stale window events should be drained from the channel before resuming.
func (m *AutoSwitchManager) Resume() {
m.paused.Store(false)
slog.Debug("auto-switch: resumed")
}
// IsPaused returns true when auto-switch evaluation is paused.
func (m *AutoSwitchManager) IsPaused() bool {
return m.paused.Load()
}
func (m *AutoSwitchManager) Evaluate(win Window, currentPage string) (page string, shouldSwitch bool) {
m.mu.Lock()
defer m.mu.Unlock()

View File

@ -143,15 +143,27 @@ func Run(ctx context.Context, cfg *config.Config, opts RunOptions) error {
case evt, ok := <-primaryDeck.Events():
if !ok {
slog.Warn("deck event channel closed, attempting reconnect...")
// Stop all periodic scripts to avoid wasting cycles on a closed device.
primaryPM.stopPeriodicKeys()
asm.Pause()
primaryDeck.Close()
newDeck := reconnectDeck(ctx, cfg, &primaryPM, asm)
newDeck := reconnectDeck(ctx, cfg, &primaryPM)
if newDeck == nil {
return ctx.Err()
}
// Update all references to the new device and page manager.
pageMgrs[0] = primaryPM
decks[0] = newDeck
primaryDeck = newDeck
web.SetDecks(decks)
web.SetPageManager(primaryPM)
newDeck.SetBrightness(deviceBrightness(cfg, newDeck.Serial()))
asm.NotifyManualPage(cfg.DefaultPage)
asm.Resume()
continue
}
@ -194,6 +206,13 @@ func Run(ctx context.Context, cfg *config.Config, opts RunOptions) error {
}
case win := <-windowCh:
if asm.IsPaused() {
// Drain buffered window events accumulated while disconnected.
for len(windowCh) > 0 {
<-windowCh
}
continue
}
if page, ok := asm.Evaluate(win, primaryPM.ActivePageName()); ok {
for _, pm := range pageMgrs {
if err := pm.ActivatePage(page); err != nil {
@ -263,15 +282,27 @@ func Run(ctx context.Context, cfg *config.Config, opts RunOptions) error {
case <-reconnectTicker.C:
if err := primaryDeck.SetBrightness(primaryDeck.Brightness()); err != nil {
slog.Warn("deck connection lost, reconnecting...", "error", err)
// Stop all periodic scripts to avoid wasting cycles on a closed device.
primaryPM.stopPeriodicKeys()
asm.Pause()
primaryDeck.Close()
newDeck := reconnectDeck(ctx, cfg, &primaryPM, asm)
newDeck := reconnectDeck(ctx, cfg, &primaryPM)
if newDeck == nil {
return ctx.Err()
}
// Update all references to the new device and page manager.
pageMgrs[0] = primaryPM
decks[0] = newDeck
primaryDeck = newDeck
web.SetDecks(decks)
web.SetPageManager(primaryPM)
newDeck.SetBrightness(deviceBrightness(cfg, newDeck.Serial()))
asm.NotifyManualPage(cfg.DefaultPage)
asm.Resume()
}
case <-ssTicker.C:
@ -322,7 +353,7 @@ func deviceBrightness(cfg *config.Config, serial string) int {
return 75
}
func reconnectDeck(ctx context.Context, cfg *config.Config, pm **PageManager, asm *AutoSwitchManager) *Deck {
func reconnectDeck(ctx context.Context, cfg *config.Config, pm **PageManager) *Deck {
for {
newDeck, err := OpenDeck("")
if err == nil {
@ -332,7 +363,6 @@ func reconnectDeck(ctx context.Context, cfg *config.Config, pm **PageManager, as
(*pm).LoadPages(cfg.Pages)
(*pm).ActivatePage(cfg.DefaultPage)
(*pm).startPeriodicKeys()
asm.NotifyManualPage(cfg.DefaultPage)
return newDeck
}
select {