From 1813ef408baa51737b5cd04702e56bf262003077 Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Mon, 27 Jul 2026 12:49:53 -0500 Subject: [PATCH] Default the M-1 to WLED 16.0.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The M-1 page opened on WLED-MM 14.5.1 (Rev6). Make WLED 16.0.1 (Rev6 only) the default by moving it to the first key of firmware.stable, which is what device.js picks on load. Rev4 hardware is not covered by 16.0.1, so Rev4 owners now have to switch to the "WLED-MM 14.5.1 (Rev4)" variant instead of landing on a working default. Side effect of the reorder, both already-built behavior, no code change: the page now loads with the "Classic installer" link hidden (WLED-M1 has no installer page, installers.stable maps that variant to null) and the header GitHub link pointing at WLED-M1. Both come back on a 14.5.1 variant. Two tests guarded themselves with test.skip(... 'override is on the default variant') and would have gone silently dormant once the override became the default. Rewrote both to compare the default variant against the first variant that differs, so they assert the toggle in either direction. Verified: scripts/validate_registry.py OK, 35 python tests, 26 playwright tests, 0 skipped. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- devices.json | 4 +-- docs/m1-wled-migration.md | 16 ++++++----- tests/installer.spec.js | 57 ++++++++++++++++++++++----------------- 3 files changed, 45 insertions(+), 32 deletions(-) diff --git a/devices.json b/devices.json index fb8a782..16397d5 100644 --- a/devices.json +++ b/devices.json @@ -230,9 +230,9 @@ "githubPagesInstaller": "https://apolloautomation.github.io/WLED-MM-M1/", "firmware": { "stable": { + "WLED 16.0.1 (Rev6 only)": "https://apolloautomation.github.io/WLED-M1/manifest.json", "WLED-MM 14.5.1 (Rev6)": "https://apolloautomation.github.io/WLED-MM-M1/Rev6_14.5.1/manifest.json", - "WLED-MM 14.5.1 (Rev4)": "https://apolloautomation.github.io/WLED-MM-M1/14.5.1/manifest.json", - "WLED 16.0.1 (Rev6 only)": "https://apolloautomation.github.io/WLED-M1/manifest.json" + "WLED-MM 14.5.1 (Rev4)": "https://apolloautomation.github.io/WLED-MM-M1/14.5.1/manifest.json" } }, "repos": { diff --git a/docs/m1-wled-migration.md b/docs/m1-wled-migration.md index 0dde407..4ed391e 100644 --- a/docs/m1-wled-migration.md +++ b/docs/m1-wled-migration.md @@ -2,17 +2,21 @@ The M-1 offers two WLED firmwares in the installer, both served from GitHub Pages: -- WLED-MM 14.5.1 (default) from ApolloAutomation/WLED-MM-M1. Two hardware revisions: +- WLED 16.0.1 (Rev6 only, default) from ApolloAutomation/WLED-M1: + - https://apolloautomation.github.io/WLED-M1/manifest.json +- WLED-MM 14.5.1 from ApolloAutomation/WLED-MM-M1. Two hardware revisions: - Rev6: https://apolloautomation.github.io/WLED-MM-M1/Rev6_14.5.1/manifest.json - Rev4: https://apolloautomation.github.io/WLED-MM-M1/14.5.1/manifest.json -- WLED 16.0.1 (Rev6 only) from ApolloAutomation/WLED-M1: - - https://apolloautomation.github.io/WLED-M1/manifest.json -Rev6 with WLED-MM 14.5.1 is the default because that is the hardware being sold and the firmware -shipped on it today. WLED 16.0.1 is a single merged full-install image (bootloader, partition +WLED 16.0.1 is the default. It is a single merged full-install image (bootloader, partition table, otadata, application, and LittleFS factory config) built from ApolloAutomation/WLED-M1. Its manifest sets new_install_prompt_erase, so esp-web-tools prompts a full erase, which is what -guarantees a factory-fresh 64x64 boot. It is offered for Rev6 only. +guarantees a factory-fresh 64x64 boot. It is offered for Rev6 only, so Rev4 owners must switch +to the WLED-MM 14.5.1 (Rev4) variant. + +Because the default variant has no classic installer page of its own, the M-1 page now loads with +the "Classic installer" link hidden and the header GitHub link pointing at WLED-M1; both come +back when a 14.5.1 variant is selected. Customers already on WLED-MM 14.5.1 can OTA to 16.0.1 with the M-1_ota.bin app-only image (same partition table preserves settings); the installer only serves the first-flash full image. diff --git a/tests/installer.spec.js b/tests/installer.spec.js index 9ac5871..da77463 100644 --- a/tests/installer.spec.js +++ b/tests/installer.spec.js @@ -214,24 +214,28 @@ test('release notes ignore an off-allowlist API url and use the safe releases hr test('release notes follow the selected variant repo (per-variant repos override)', async ({ page }) => { const d = registry.devices.find((x) => x.repos && x.repos.stable); test.skip(!d, 'no device with a per-variant repos override'); - const overrideVariant = Object.keys(d.repos.stable)[0]; - const overrideRepo = d.repos.stable[overrideVariant]; - const defaultVariant = Object.keys(d.firmware.stable)[0]; - test.skip(overrideVariant === defaultVariant, 'override is on the default variant'); + // Works whichever way round the override sits: the default variant may itself be + // the overridden one, so compare the first variant against the first one that + // resolves to a different repo. + const variants = Object.keys(d.firmware.stable); + const repoOf = (v) => d.repos.stable[v] || d.repo; + const startVariant = variants[0]; + const otherVariant = variants.find((v) => repoOf(v) !== repoOf(startVariant)); + test.skip(!otherVariant, 'all variants resolve to the same repo'); // Force the API-failure path so the deterministic .fail-link (built from the // resolved repo) is what we assert on. await page.route('https://api.github.com/**', (route) => route.fulfill({ status: 403 })); await page.goto(`/#/${d.id}`); - // Default variant resolves to the device-level repo. + // Default variant resolves to its own repo (device-level or override). await expect(page.locator('.release-notes .fail-link')) - .toHaveAttribute('href', `https://github.com/${d.repo}/releases`); + .toHaveAttribute('href', `https://github.com/${repoOf(startVariant)}/releases`); - // Selecting the override variant must re-render release notes against the override repo. - await page.locator(`#variant-seg button[data-variant="${overrideVariant}"]`).click(); + // Selecting a variant with a different repo must re-render release notes against it. + await page.locator(`#variant-seg button[data-variant="${otherVariant}"]`).click(); await expect(page.locator('.release-notes .fail-link')) - .toHaveAttribute('href', `https://github.com/${overrideRepo}/releases`); + .toHaveAttribute('href', `https://github.com/${repoOf(otherVariant)}/releases`); }); test('clicking the already-selected variant makes no extra release-notes fetch', async ({ page }) => { @@ -275,28 +279,33 @@ test('header GitHub link and classic-installer links follow the selected variant const d = registry.devices.find((x) => x.installers && x.installers.stable && Object.values(x.installers.stable).some((v) => v === null)); test.skip(!d, 'no device that hides a classic installer for a variant'); - const hiddenVariant = Object.keys(d.installers.stable).find((k) => d.installers.stable[k] === null); - const defaultVariant = Object.keys(d.firmware.stable)[0]; - test.skip(hiddenVariant === defaultVariant, 'hidden installer is on the default variant'); - const overrideRepo = d.repos && d.repos.stable && d.repos.stable[hiddenVariant]; + // The variant that hides its classic installer may be the default one, so assert + // the link toggling in whichever direction the registry happens to order them. + const variants = Object.keys(d.firmware.stable); + const installerOf = (v) => (v in d.installers.stable + ? d.installers.stable[v] : d.githubPagesInstaller); + const repoOf = (v) => (d.repos && d.repos.stable && d.repos.stable[v]) || d.repo; + const startVariant = variants[0]; + const otherVariant = variants.find((v) => !installerOf(v) !== !installerOf(startVariant)); + test.skip(!otherVariant, 'every variant has the same classic-installer visibility'); await page.route('https://api.github.com/**', (route) => route.fulfill({ status: 403 })); await page.goto(`/#/${d.id}`); - // Default variant: header GitHub = device repo, Classic installer link present. + // Default variant: header GitHub and Classic installer reflect that variant. await expect(page.locator('.links a', { hasText: 'GitHub' })) - .toHaveAttribute('href', `https://github.com/${d.repo}`); - await expect(page.locator('.links a', { hasText: 'Classic installer' })).toHaveCount(1); + .toHaveAttribute('href', `https://github.com/${repoOf(startVariant)}`); + await expect(page.locator('.links a', { hasText: 'Classic installer' })) + .toHaveCount(installerOf(startVariant) ? 1 : 0); - // Select the variant whose installer is null. - await page.locator(`#variant-seg button[data-variant="${hiddenVariant}"]`).click(); + // Select the variant on the other side of the null/non-null installer split. + await page.locator(`#variant-seg button[data-variant="${otherVariant}"]`).click(); - // Header GitHub link now points at the override repo (if set); Classic installer link is gone. - if (overrideRepo) { - await expect(page.locator('.links a', { hasText: 'GitHub' })) - .toHaveAttribute('href', `https://github.com/${overrideRepo}`); - } - await expect(page.locator('.links a', { hasText: 'Classic installer' })).toHaveCount(0); + // Header GitHub link follows that variant's repo; installer link flips visibility. + await expect(page.locator('.links a', { hasText: 'GitHub' })) + .toHaveAttribute('href', `https://github.com/${repoOf(otherVariant)}`); + await expect(page.locator('.links a', { hasText: 'Classic installer' })) + .toHaveCount(installerOf(otherVariant) ? 1 : 0); }); test('step 3 shows the Home Assistant hand-off', async ({ page }) => {