Compare commits
3 Commits
e676b74c32
...
f144789210
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f144789210 | ||
|
|
20c46e18b3 | ||
|
|
3eaf9dc0c4 |
@ -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()
|
||||
|
||||
42
daemon.go
42
daemon.go
@ -125,7 +125,7 @@ func Run(ctx context.Context, cfg *config.Config, opts RunOptions) error {
|
||||
}
|
||||
})
|
||||
|
||||
reconnectTicker := time.NewTicker(5 * time.Second)
|
||||
reconnectTicker := time.NewTicker(2 * time.Second)
|
||||
defer reconnectTicker.Stop()
|
||||
|
||||
ssTicker := time.NewTicker(5 * time.Second)
|
||||
@ -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,13 +363,12 @@ 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 {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-time.After(3 * time.Second):
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
page.go
2
page.go
@ -313,7 +313,7 @@ func (pm *PageManager) renderKey(idx int, k *config.KeyConfig) {
|
||||
}
|
||||
slog.Warn("invalid background color", "value", k.Background, "error", err)
|
||||
}
|
||||
if err := pm.deck.WriteText(idx, k.Label, image.Black, fontName, fontSize); err != nil {
|
||||
if err := pm.deck.WriteText(idx, k.Label, color.RGBA{0x21, 0x25, 0x2b, 0xff}, fontName, fontSize); err != nil {
|
||||
slog.Warn("write text", "error", err)
|
||||
}
|
||||
} else if k.Background != "" {
|
||||
|
||||
@ -123,7 +123,7 @@ func composeImageWithLabel(src image.Image, text string, keySize int, fontSize f
|
||||
}
|
||||
|
||||
func renderTextImage(text string, keySize int, fontSize float64) image.Image {
|
||||
rgba := blankImage(keySize, color.RGBA{0, 0, 0, 255}).(*image.RGBA)
|
||||
rgba := blankImage(keySize, color.RGBA{0, 0, 0, 0}).(*image.RGBA)
|
||||
|
||||
if text != "" {
|
||||
face, err := parseDisplayFace(fontSize)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user