From b0b8b9a475c760320bbaf4aadca5e9d5b5ec909a Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Sat, 15 Aug 2026 13:10:39 +0200 Subject: [PATCH] feat(health): offer a sourcesUpdated pull alongside the presets push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a second QuickFix to the speaker_presets_count warning, reusing the existing postSourcesUpdated fix (checks_refresh_sources.go). It nudges the speaker to re-fetch /full, which is confirmed (both from marge.AccountFullToXML and a genuine captured Bose-cloud response) to carry presets alongside sources. Whether firmware actually re-applies /full's preset section back onto its own local table is unconfirmed — issue253_regression_test.go already flags that exact link as untested. So this is offered as a free, non-destructive thing to try first, with the guaranteed restore_presets_to_speaker push as the fallback. Gives both directions (pull-style nudge, direct push) rather than only the one. Refs #614 --- pkg/service/health/checks_presets_count.go | 28 +++++++++++++++---- .../health/checks_presets_count_test.go | 13 +++++++-- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/pkg/service/health/checks_presets_count.go b/pkg/service/health/checks_presets_count.go index e609149..1b4b661 100644 --- a/pkg/service/health/checks_presets_count.go +++ b/pkg/service/health/checks_presets_count.go @@ -50,6 +50,17 @@ func RegisterPresetsCountCheck(r *Registry, ds *datastore.DataStore) { r.RegisterFix(CheckIDPresetsCount, FixIDRestorePresetsToSpeaker, func(target Target) (string, error) { return restorePresetsToSpeaker(ds, target) }) + + // Same underlying nudge as the refresh_sources check (FixIDPostSourcesUpdated, + // checks_refresh_sources.go): POSTs sourcesUpdated so the speaker re-fetches + // /full. Confirmed that /full carries presets alongside sources + // (marge.AccountFullToXML); NOT confirmed that firmware re-applies the + // presets section locally (see issue253_regression_test.go — that exact + // link is documented as untested). Offered as a cheap, non-destructive + // thing to try before the guaranteed-but-heavier restore-to-speaker push. + r.RegisterFix(CheckIDPresetsCount, FixIDPostSourcesUpdated, func(target Target) (string, error) { + return postSourcesUpdated(ds, target) + }) } func runPresetsCountCheck(ds *datastore.DataStore) []Finding { @@ -149,11 +160,18 @@ func comparePresetsForDeviceWithURL(ds *datastore.DataStore, account, deviceID, // (reboot and/or Sync leaving the speaker's own preset // slots empty while the service's Presets.xml is untouched). severity = SeverityWarning - quickFixes = []QuickFix{{ - ID: FixIDRestorePresetsToSpeaker, - Label: "Restore presets to speaker", - Confirm: "This pushes AfterTouch's stored presets onto the speaker's own preset slots, one at a time. Doesn't require a reboot.", - }} + quickFixes = []QuickFix{ + { + ID: FixIDPostSourcesUpdated, + Label: "Try a sourcesUpdated nudge first", + Confirm: "Asks the speaker to re-fetch /full (the same nudge used to refresh sources). /full does include presets, but whether the speaker applies them back to its own preset table isn't confirmed — this is free and non-destructive, worth trying before the push below.", + }, + { + ID: FixIDRestorePresetsToSpeaker, + Label: "Restore presets to speaker", + Confirm: "This pushes AfterTouch's stored presets onto the speaker's own preset slots, one at a time. Doesn't require a reboot.", + }, + } } return []Finding{{ diff --git a/pkg/service/health/checks_presets_count_test.go b/pkg/service/health/checks_presets_count_test.go index e0addd3..5780725 100644 --- a/pkg/service/health/checks_presets_count_test.go +++ b/pkg/service/health/checks_presets_count_test.go @@ -188,8 +188,17 @@ func TestPresetsCount_SpeakerEmptyOffersRestoreQuickFix(t *testing.T) { t.Fatalf("expected one finding, got %+v", got) } - if len(got[0].QuickFixes) != 1 || got[0].QuickFixes[0].ID != FixIDRestorePresetsToSpeaker { - t.Errorf("expected the restore-presets QuickFix, got %+v", got[0].QuickFixes) + // Offers both the cheap, unconfirmed pull-style nudge (sourcesUpdated, + // which makes the speaker re-fetch /full — /full does carry presets, + // but firmware re-applying them locally is unconfirmed) and the + // guaranteed push (restore_presets_to_speaker) — see #614 discussion. + fixIDs := map[string]bool{} + for _, qf := range got[0].QuickFixes { + fixIDs[qf.ID] = true + } + + if len(got[0].QuickFixes) != 2 || !fixIDs[FixIDRestorePresetsToSpeaker] || !fixIDs[FixIDPostSourcesUpdated] { + t.Errorf("expected both the sourcesUpdated nudge and the restore-presets QuickFix, got %+v", got[0].QuickFixes) } }