From fe9d48ea6995d9230f53dc34d6e80cd4bb463b98 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 18 Sep 2026 14:27:51 -0700 Subject: [PATCH] Hide superseded observer profiles in picker --- docs/_javascript/firmware_picker.js | 32 +++++++++++++++++++++++++---- docs/firmware_picker.md | 6 ++++++ test/test_firmware_picker.js | 26 +++++++++++++++++++++++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/docs/_javascript/firmware_picker.js b/docs/_javascript/firmware_picker.js index 2b2e1c9b..d467f9e9 100644 --- a/docs/_javascript/firmware_picker.js +++ b/docs/_javascript/firmware_picker.js @@ -502,6 +502,26 @@ }); } + function omitLegacyObserverCompatibilityProfiles(profiles) { + const targets = new Set((profiles || []).map(function (profile) { + return String(profile && profile.target || "").toLowerCase(); + })); + + return (profiles || []).filter(function (profile) { + const target = String(profile && profile.target || ""); + // Same-partition ESP32 migrations publish the ordinary target's Full + // image with its established mOTA identity. The former observer-named + // Full image remains in the release for deployed devices that still use + // that identity, but it is not a second picker choice when the exact + // successor is present. Keep an unmatched observer image visible: it + // may still be the only compatible recipe for that hardware/role. + const successor = target.replace( + /_observer_mqtt_?(?=-full-usb-wifi$)/i, "" + ); + return successor === target || !targets.has(successor.toLowerCase()); + }); + } + function applyFullCompanionCapabilities(profiles) { return (profiles || []).map(function (profile) { if (!isFullCompanion(profile)) return profile; @@ -681,10 +701,12 @@ }).filter(function (profile) { return !isHiddenLegacyProfile(profile); }); - const profiles = applyMergedStandardUsbLoggingCapabilities( - applyMergedRak4631RepeaterCapabilities( - omitTransportsReplacedByFull( - applyFullCompanionCapabilities(visibleProfiles) + const profiles = omitLegacyObserverCompatibilityProfiles( + applyMergedStandardUsbLoggingCapabilities( + applyMergedRak4631RepeaterCapabilities( + omitTransportsReplacedByFull( + applyFullCompanionCapabilities(visibleProfiles) + ) ) ) ).sort(function (a, b) { @@ -1773,6 +1795,8 @@ applyFullCompanionCapabilities, canonicalHardware: canonicalHardware, omitTransportsReplacedByFull: omitTransportsReplacedByFull, + omitLegacyObserverCompatibilityProfiles: + omitLegacyObserverCompatibilityProfiles, omitTransportsReplacedByDualCdcFull: omitTransportsReplacedByFull, omitNrf52TransportsReplacedByFull: diff --git a/docs/firmware_picker.md b/docs/firmware_picker.md index eeaadab0..85e38769 100644 --- a/docs/firmware_picker.md +++ b/docs/firmware_picker.md @@ -414,6 +414,12 @@ off and the normal ASCII-to-binary mode switch occurs. nRF52 Full Companion retains its optional dedicated interface `02`. Exact filename search still finds old aliases from earlier releases. +For same-partition ESP32 infrastructure migrations, the picker similarly +recommends the ordinary target's exact-identity Full image when it is present. +The former observer-named Full image remains available through exact filename +search for deployed devices that still use that mOTA identity; it is not shown +as a second recommended configuration. + ## Maintaining the runtime directions The online picker and downloadable HTML use the same command renderer. diff --git a/test/test_firmware_picker.js b/test/test_firmware_picker.js index 698330ee..b996fb83 100644 --- a/test/test_firmware_picker.js +++ b/test/test_firmware_picker.js @@ -545,6 +545,32 @@ assert(picker.profileMatchesFacets(mqtt, { feature: "full", })); +// Phase one of the same-partition ESP32 migration keeps the legacy observer +// artifact downloadable for its distinct deployed mOTA identity, but offers +// the ordinary target's exact-identity Full successor in the picker. An +// unmatched observer profile remains visible. +const observerMigrationCatalog = picker.buildCatalog([ + release(family, "2026-09-18T12:00:00Z", [ + asset("Heltec_v3_repeater-full-usb-wifi-ota-" + family + ".bin"), + asset( + "Heltec_v3_repeater_observer_mqtt-full-usb-wifi-ota-" + + family + ".bin" + ), + asset( + "Heltec_v3_room_server_observer_mqtt-full-usb-wifi-ota-" + + family + ".bin" + ), + ]), +]); +assert.strictEqual(observerMigrationCatalog.rows.length, 3); +assert.deepStrictEqual( + observerMigrationCatalog.profiles.map(function (item) { return item.target; }), + [ + "Heltec_v3_repeater-full-usb-wifi", + "Heltec_v3_room_server_observer_mqtt-full-usb-wifi", + ] +); + const fullCompanionSteps = picker.installSteps(v4Full, "merged-bin"); assert(fullCompanionSteps.some(function (step) { return step.includes("usb.logging");