From 76ffb963c77ac23c4f1f21069c065cfb78c60003 Mon Sep 17 00:00:00 2001 From: lost-hope Date: Sat, 25 Jul 2026 13:00:33 +0200 Subject: [PATCH 01/11] Rebuild as a dependency-free static installer Replaces the static per-release bin/ manifest tree with live GitHub Releases API fetching (app.js), consolidating script.js/releases.js into one module. Adds: - Flash size selection (1/2/4/8/16MB) per chip, with unavailable sizes hidden automatically. - HUB75 Matrix variant with a board/layout picker (ESP32 default and forum pinouts, HD-WF2, Adafruit MatrixPortal S3, MOONHUB/LilyGO T7-S3, Waveshare RGB-Matrix), each showing its required chip and flash size. - New pre-patched + re-signed bootloaders and partition tables for ESP32-S3 at 4/16/32MB, generated with esptool's own header-patch algorithm and verified via `esptool image-info`. - A CORS-proxy health check that disables Install until real firmware bytes are confirmed reachable, instead of silently flashing an error page as firmware. - Dynamic i18n: languages load from lang/*.json + lang/languages.json at runtime, so adding a language needs no code changes. Added German, Spanish, Portuguese, Dutch and French alongside the existing English/Chinese. Removes installer/ (unused bundled esp-web-tools, superseded by the CDN build already in index.html), the legacy auto.htm/v2.htm pages, and the disabled nightly-download workflow. --- .github/workflows/nightly.yaml | 19 - CNAME | 1 - README.md | 59 +- app.js | 747 +++++++++++++++++ auto.htm | 163 ---- bin/Beta/beta_0_15_1-b1/boot_app0.bin | Bin 8192 -> 0 bytes bin/Beta/beta_0_15_1-b1/bootloader_s2.bin | Bin 13536 -> 0 bytes .../beta_0_15_1-b1/esp32-c3_bootloader_v2.bin | Bin 65536 -> 0 bytes .../beta_0_15_1-b1/esp32_bootloader_v4.bin | Bin 65535 -> 0 bytes bin/Beta/beta_0_15_1-b1/manifest.json | 69 -- bin/Beta/beta_0_15_1-b1/manifest_e.json | 19 - bin/Beta/beta_0_15_1-b1/manifest_t.json | 12 - bin/Beta/beta_0_15_1-b2/boot_app0.bin | Bin 8192 -> 0 bytes bin/Beta/beta_0_15_1-b2/bootloader_s2.bin | Bin 13536 -> 0 bytes .../beta_0_15_1-b2/esp32-c3_bootloader_v2.bin | Bin 65536 -> 0 bytes .../beta_0_15_1-b2/esp32_bootloader_v4.bin | Bin 65535 -> 0 bytes bin/Beta/beta_0_15_1-b2/manifest.json | 68 -- bin/Beta/beta_0_15_1-b2/manifest_e.json | 18 - bin/Beta/beta_0_15_1-b2/manifest_t.json | 11 - bin/Beta/beta_0_15_1-b2/partitions_s2_4m.bin | Bin 3072 -> 0 bytes bin/Beta/beta_0_15_1-b2/partitions_s3_8m.bin | Bin 3072 -> 0 bytes bin/Beta/beta_0_15_2-b1/boot_app0.bin | Bin 8192 -> 0 bytes bin/Beta/beta_0_15_2-b1/bootloader_s2.bin | Bin 13536 -> 0 bytes .../beta_0_15_2-b1/esp32-c3_bootloader_v2.bin | Bin 65536 -> 0 bytes .../beta_0_15_2-b1/esp32_bootloader_v4.bin | Bin 65535 -> 0 bytes bin/Beta/beta_0_15_2-b1/manifest.json | 76 -- bin/Beta/beta_0_15_2-b1/manifest_e.json | 19 - bin/Beta/beta_0_15_2-b1/manifest_t.json | 12 - bin/Beta/beta_0_15_2-b1/manifest_v4.json | 15 - bin/Beta/beta_0_15_2-b1/partitions_c3_4m.bin | Bin 3072 -> 0 bytes bin/Beta/beta_0_15_2-b1/partitions_s2_4m.bin | Bin 3072 -> 0 bytes bin/Beta/beta_0_15_2-b1/partitions_s3_8m.bin | Bin 3072 -> 0 bytes bin/Release/release_0_14_3/bootloader_s2.bin | Bin 13536 -> 0 bytes bin/Release/release_0_14_3/bootloader_s3.bin | Bin 14768 -> 0 bytes .../release_0_14_3/esp32-c3_bootloader_v2.bin | Bin 65536 -> 0 bytes .../release_0_14_3/esp32_bootloader_v4.bin | Bin 65535 -> 0 bytes bin/Release/release_0_14_3/manifest.json | 76 -- bin/Release/release_0_14_3/manifest_a.json | 14 - bin/Release/release_0_14_3/manifest_e.json | 18 - bin/Release/release_0_14_3/manifest_t.json | 11 - .../release_0_14_3/partitions_c3_4m.bin | Bin 3072 -> 0 bytes .../release_0_14_3/partitions_s2_4m.bin | Bin 3072 -> 0 bytes .../release_0_14_3/partitions_s3_8m.bin | Bin 3072 -> 0 bytes bin/Release/release_0_14_4/bootloader_s2.bin | Bin 13536 -> 0 bytes bin/Release/release_0_14_4/bootloader_s3.bin | Bin 14768 -> 0 bytes .../release_0_14_4/esp32-c3_bootloader_v2.bin | Bin 65536 -> 0 bytes .../release_0_14_4/esp32_bootloader_v4.bin | Bin 65535 -> 0 bytes bin/Release/release_0_14_4/manifest.json | 76 -- bin/Release/release_0_14_4/manifest_a.json | 14 - bin/Release/release_0_14_4/manifest_e.json | 18 - bin/Release/release_0_14_4/manifest_t.json | 11 - .../release_0_14_4/partitions_c3_4m.bin | Bin 3072 -> 0 bytes .../release_0_14_4/partitions_s2_4m.bin | Bin 3072 -> 0 bytes .../release_0_14_4/partitions_s3_8m.bin | Bin 3072 -> 0 bytes bin/Release/release_0_15_1/boot_app0.bin | Bin 8192 -> 0 bytes bin/Release/release_0_15_1/bootloader_s2.bin | Bin 13536 -> 0 bytes bin/Release/release_0_15_1/bootloader_s3.bin | Bin 14768 -> 0 bytes .../release_0_15_1/esp32-c3_bootloader_v2.bin | Bin 65536 -> 0 bytes .../release_0_15_1/esp32_bootloader_v4.bin | Bin 65535 -> 0 bytes bin/Release/release_0_15_1/manifest.json | 76 -- bin/Release/release_0_15_1/manifest_e.json | 19 - bin/Release/release_0_15_1/manifest_t.json | 12 - bin/Release/release_0_15_1/manifest_v4.json | 15 - .../release_0_15_1/partitions_c3_4m.bin | Bin 3072 -> 0 bytes .../release_0_15_1/partitions_s2_4m.bin | Bin 3072 -> 0 bytes .../release_0_15_1/partitions_s3_8m.bin | Bin 3072 -> 0 bytes bin/Release/release_0_15_2/boot_app0.bin | Bin 8192 -> 0 bytes bin/Release/release_0_15_2/bootloader_s2.bin | Bin 13536 -> 0 bytes bin/Release/release_0_15_2/bootloader_s3.bin | Bin 14768 -> 0 bytes .../release_0_15_2/esp32-c3_bootloader_v2.bin | Bin 65536 -> 0 bytes .../release_0_15_2/esp32_bootloader_v4.bin | Bin 65535 -> 0 bytes bin/Release/release_0_15_2/manifest.json | 76 -- bin/Release/release_0_15_2/manifest_e.json | 19 - bin/Release/release_0_15_2/manifest_t.json | 12 - bin/Release/release_0_15_2/manifest_v4.json | 15 - .../release_0_15_2/partitions_c3_4m.bin | Bin 3072 -> 0 bytes .../release_0_15_2/partitions_s2_4m.bin | Bin 3072 -> 0 bytes .../release_0_15_2/partitions_s3_8m.bin | Bin 3072 -> 0 bytes bin/Release/release_0_15_3/boot_app0.bin | Bin 8192 -> 0 bytes bin/Release/release_0_15_3/bootloader_s2.bin | Bin 13536 -> 0 bytes bin/Release/release_0_15_3/bootloader_s3.bin | Bin 14768 -> 0 bytes .../release_0_15_3/esp32-c3_bootloader_v2.bin | Bin 65536 -> 0 bytes .../release_0_15_3/esp32_bootloader_v4.bin | Bin 65535 -> 0 bytes bin/Release/release_0_15_3/manifest.json | 76 -- bin/Release/release_0_15_3/manifest_e.json | 19 - bin/Release/release_0_15_3/manifest_t.json | 12 - bin/Release/release_0_15_3/manifest_v4.json | 15 - .../release_0_15_3/partitions_c3_4m.bin | Bin 3072 -> 0 bytes .../release_0_15_3/partitions_s2_4m.bin | Bin 3072 -> 0 bytes .../release_0_15_3/partitions_s3_8m.bin | Bin 3072 -> 0 bytes .../bootloader_s3_16m.bin} | Bin 14768 -> 14768 bytes .../bootloader_s3_32m.bin} | Bin 14768 -> 14768 bytes .../bootloader_s3_4m.bin} | Bin 14768 -> 14768 bytes .../partitions_s3_16m.bin} | Bin 3072 -> 3072 bytes .../partitions_s3_32m.bin} | Bin 3072 -> 3072 bytes .../partitions_s3_4m.bin} | Bin .../partitions_s3_hdwf2.bin} | Bin 3072 -> 3072 bytes bin/nightly/boot_app0.bin | Bin 8192 -> 0 bytes bin/nightly/boot_app0_v2022.bin | Bin 8192 -> 0 bytes bin/nightly/bootloader_s2.bin | Bin 13536 -> 0 bytes bin/nightly/bootloader_s3.bin | Bin 14768 -> 0 bytes bin/nightly/esp32-c3_bootloader_v2.bin | Bin 12144 -> 0 bytes bin/nightly/esp32_bootloader_v4.bin | Bin 65535 -> 0 bytes bin/nightly/manifest.json | 77 -- bin/nightly/manifest_debug.json | 15 - bin/nightly/manifest_e.json | 19 - bin/nightly/manifest_t.json | 19 - bin/nightly/partitions_c3.bin | Bin 3072 -> 0 bytes bin/nightly/partitions_s2_4m.bin | Bin 3072 -> 0 bytes bin/nightly/partitions_s3_8m.bin | Bin 3072 -> 0 bytes checkhtml.sh | 136 --- i18n.js | 173 ++-- index.htm | 163 ---- index.html | 160 ++++ installer/esp32-76e55e75.js | 1 - installer/esp32c3-47b374e0.js | 1 - installer/esp32c6-6da0b175.js | 1 - installer/esp32s2-0d35c7ed.js | 1 - installer/esp32s3-69876819.js | 1 - installer/esp8266-00e25dbe.js | 1 - installer/index-54fb20b9.js | 126 --- installer/install-button.js | 1 - installer/install-dialog-19322187.js | 785 ------------------ installer/rom-7e8b4fbe.js | 1 - installer/styles-fa761598.js | 101 --- lang/de.json | 40 + lang/en.json | 40 + lang/es.json | 40 + lang/fr.json | 40 + lang/languages.json | 9 + lang/nl.json | 40 + lang/pt.json | 40 + lang/zh-CN.json | 40 + releases.js | 372 --------- script.js | 74 -- style.css | 64 +- v2.htm | 214 ----- 137 files changed, 1368 insertions(+), 3339 deletions(-) delete mode 100644 .github/workflows/nightly.yaml delete mode 100644 CNAME create mode 100644 app.js delete mode 100644 auto.htm delete mode 100644 bin/Beta/beta_0_15_1-b1/boot_app0.bin delete mode 100644 bin/Beta/beta_0_15_1-b1/bootloader_s2.bin delete mode 100644 bin/Beta/beta_0_15_1-b1/esp32-c3_bootloader_v2.bin delete mode 100644 bin/Beta/beta_0_15_1-b1/esp32_bootloader_v4.bin delete mode 100644 bin/Beta/beta_0_15_1-b1/manifest.json delete mode 100644 bin/Beta/beta_0_15_1-b1/manifest_e.json delete mode 100644 bin/Beta/beta_0_15_1-b1/manifest_t.json delete mode 100644 bin/Beta/beta_0_15_1-b2/boot_app0.bin delete mode 100644 bin/Beta/beta_0_15_1-b2/bootloader_s2.bin delete mode 100644 bin/Beta/beta_0_15_1-b2/esp32-c3_bootloader_v2.bin delete mode 100644 bin/Beta/beta_0_15_1-b2/esp32_bootloader_v4.bin delete mode 100644 bin/Beta/beta_0_15_1-b2/manifest.json delete mode 100644 bin/Beta/beta_0_15_1-b2/manifest_e.json delete mode 100644 bin/Beta/beta_0_15_1-b2/manifest_t.json delete mode 100644 bin/Beta/beta_0_15_1-b2/partitions_s2_4m.bin delete mode 100644 bin/Beta/beta_0_15_1-b2/partitions_s3_8m.bin delete mode 100644 bin/Beta/beta_0_15_2-b1/boot_app0.bin delete mode 100644 bin/Beta/beta_0_15_2-b1/bootloader_s2.bin delete mode 100644 bin/Beta/beta_0_15_2-b1/esp32-c3_bootloader_v2.bin delete mode 100644 bin/Beta/beta_0_15_2-b1/esp32_bootloader_v4.bin delete mode 100644 bin/Beta/beta_0_15_2-b1/manifest.json delete mode 100644 bin/Beta/beta_0_15_2-b1/manifest_e.json delete mode 100644 bin/Beta/beta_0_15_2-b1/manifest_t.json delete mode 100644 bin/Beta/beta_0_15_2-b1/manifest_v4.json delete mode 100644 bin/Beta/beta_0_15_2-b1/partitions_c3_4m.bin delete mode 100644 bin/Beta/beta_0_15_2-b1/partitions_s2_4m.bin delete mode 100644 bin/Beta/beta_0_15_2-b1/partitions_s3_8m.bin delete mode 100644 bin/Release/release_0_14_3/bootloader_s2.bin delete mode 100644 bin/Release/release_0_14_3/bootloader_s3.bin delete mode 100644 bin/Release/release_0_14_3/esp32-c3_bootloader_v2.bin delete mode 100644 bin/Release/release_0_14_3/esp32_bootloader_v4.bin delete mode 100644 bin/Release/release_0_14_3/manifest.json delete mode 100644 bin/Release/release_0_14_3/manifest_a.json delete mode 100644 bin/Release/release_0_14_3/manifest_e.json delete mode 100644 bin/Release/release_0_14_3/manifest_t.json delete mode 100644 bin/Release/release_0_14_3/partitions_c3_4m.bin delete mode 100644 bin/Release/release_0_14_3/partitions_s2_4m.bin delete mode 100644 bin/Release/release_0_14_3/partitions_s3_8m.bin delete mode 100644 bin/Release/release_0_14_4/bootloader_s2.bin delete mode 100644 bin/Release/release_0_14_4/bootloader_s3.bin delete mode 100644 bin/Release/release_0_14_4/esp32-c3_bootloader_v2.bin delete mode 100644 bin/Release/release_0_14_4/esp32_bootloader_v4.bin delete mode 100644 bin/Release/release_0_14_4/manifest.json delete mode 100644 bin/Release/release_0_14_4/manifest_a.json delete mode 100644 bin/Release/release_0_14_4/manifest_e.json delete mode 100644 bin/Release/release_0_14_4/manifest_t.json delete mode 100644 bin/Release/release_0_14_4/partitions_c3_4m.bin delete mode 100644 bin/Release/release_0_14_4/partitions_s2_4m.bin delete mode 100644 bin/Release/release_0_14_4/partitions_s3_8m.bin delete mode 100644 bin/Release/release_0_15_1/boot_app0.bin delete mode 100644 bin/Release/release_0_15_1/bootloader_s2.bin delete mode 100644 bin/Release/release_0_15_1/bootloader_s3.bin delete mode 100644 bin/Release/release_0_15_1/esp32-c3_bootloader_v2.bin delete mode 100644 bin/Release/release_0_15_1/esp32_bootloader_v4.bin delete mode 100644 bin/Release/release_0_15_1/manifest.json delete mode 100644 bin/Release/release_0_15_1/manifest_e.json delete mode 100644 bin/Release/release_0_15_1/manifest_t.json delete mode 100644 bin/Release/release_0_15_1/manifest_v4.json delete mode 100644 bin/Release/release_0_15_1/partitions_c3_4m.bin delete mode 100644 bin/Release/release_0_15_1/partitions_s2_4m.bin delete mode 100644 bin/Release/release_0_15_1/partitions_s3_8m.bin delete mode 100644 bin/Release/release_0_15_2/boot_app0.bin delete mode 100644 bin/Release/release_0_15_2/bootloader_s2.bin delete mode 100644 bin/Release/release_0_15_2/bootloader_s3.bin delete mode 100644 bin/Release/release_0_15_2/esp32-c3_bootloader_v2.bin delete mode 100644 bin/Release/release_0_15_2/esp32_bootloader_v4.bin delete mode 100644 bin/Release/release_0_15_2/manifest.json delete mode 100644 bin/Release/release_0_15_2/manifest_e.json delete mode 100644 bin/Release/release_0_15_2/manifest_t.json delete mode 100644 bin/Release/release_0_15_2/manifest_v4.json delete mode 100644 bin/Release/release_0_15_2/partitions_c3_4m.bin delete mode 100644 bin/Release/release_0_15_2/partitions_s2_4m.bin delete mode 100644 bin/Release/release_0_15_2/partitions_s3_8m.bin delete mode 100644 bin/Release/release_0_15_3/boot_app0.bin delete mode 100644 bin/Release/release_0_15_3/bootloader_s2.bin delete mode 100644 bin/Release/release_0_15_3/bootloader_s3.bin delete mode 100644 bin/Release/release_0_15_3/esp32-c3_bootloader_v2.bin delete mode 100644 bin/Release/release_0_15_3/esp32_bootloader_v4.bin delete mode 100644 bin/Release/release_0_15_3/manifest.json delete mode 100644 bin/Release/release_0_15_3/manifest_e.json delete mode 100644 bin/Release/release_0_15_3/manifest_t.json delete mode 100644 bin/Release/release_0_15_3/manifest_v4.json delete mode 100644 bin/Release/release_0_15_3/partitions_c3_4m.bin delete mode 100644 bin/Release/release_0_15_3/partitions_s2_4m.bin delete mode 100644 bin/Release/release_0_15_3/partitions_s3_8m.bin rename bin/{Beta/beta_0_15_2-b1/bootloader_s3.bin => boot/bootloader_s3_16m.bin} (99%) rename bin/{Beta/beta_0_15_1-b1/bootloader_s3.bin => boot/bootloader_s3_32m.bin} (99%) rename bin/{Beta/beta_0_15_1-b2/bootloader_s3.bin => boot/bootloader_s3_4m.bin} (99%) rename bin/{Beta/beta_0_15_1-b1/partitions_s3_8m.bin => boot/partitions_s3_16m.bin} (93%) rename bin/{Beta/beta_0_15_1-b2/partitions_c3_4m.bin => boot/partitions_s3_32m.bin} (93%) rename bin/{Beta/beta_0_15_1-b1/partitions_c3_4m.bin => boot/partitions_s3_4m.bin} (100%) rename bin/{Beta/beta_0_15_1-b1/partitions_s2_4m.bin => boot/partitions_s3_hdwf2.bin} (95%) delete mode 100644 bin/nightly/boot_app0.bin delete mode 100644 bin/nightly/boot_app0_v2022.bin delete mode 100644 bin/nightly/bootloader_s2.bin delete mode 100644 bin/nightly/bootloader_s3.bin delete mode 100644 bin/nightly/esp32-c3_bootloader_v2.bin delete mode 100644 bin/nightly/esp32_bootloader_v4.bin delete mode 100644 bin/nightly/manifest.json delete mode 100644 bin/nightly/manifest_debug.json delete mode 100644 bin/nightly/manifest_e.json delete mode 100644 bin/nightly/manifest_t.json delete mode 100644 bin/nightly/partitions_c3.bin delete mode 100644 bin/nightly/partitions_s2_4m.bin delete mode 100644 bin/nightly/partitions_s3_8m.bin delete mode 100755 checkhtml.sh delete mode 100644 index.htm create mode 100644 index.html delete mode 100644 installer/esp32-76e55e75.js delete mode 100644 installer/esp32c3-47b374e0.js delete mode 100644 installer/esp32c6-6da0b175.js delete mode 100644 installer/esp32s2-0d35c7ed.js delete mode 100644 installer/esp32s3-69876819.js delete mode 100644 installer/esp8266-00e25dbe.js delete mode 100644 installer/index-54fb20b9.js delete mode 100644 installer/install-button.js delete mode 100644 installer/install-dialog-19322187.js delete mode 100644 installer/rom-7e8b4fbe.js delete mode 100644 installer/styles-fa761598.js create mode 100644 lang/de.json create mode 100644 lang/en.json create mode 100644 lang/es.json create mode 100644 lang/fr.json create mode 100644 lang/languages.json create mode 100644 lang/nl.json create mode 100644 lang/pt.json create mode 100644 lang/zh-CN.json delete mode 100644 releases.js delete mode 100644 script.js delete mode 100644 v2.htm diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml deleted file mode 100644 index 121b4bf..0000000 --- a/.github/workflows/nightly.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: Nightly Update - disabled -on: - workflow_dispatch: - # repository_dispatch: - # types: [release-nightly] -jobs: - download-bins: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: | - echo "Download files listed in https://api.github.com/repos/wled/WLED/releases/200568682/assets" - mkdir -p bin/nightly - cd bin/nightly - find ./ -name \*.bin\* -exec git rm -f {} \; - curl https://api.github.com/repos/wled/WLED/releases/200568682/assets | jq '.[].browser_download_url' | xargs wget - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - file_pattern: '*.bin*' diff --git a/CNAME b/CNAME deleted file mode 100644 index d2cb76b..0000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -install.wled.me \ No newline at end of file diff --git a/README.md b/README.md index f0520ef..b74274f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,57 @@ -# WLED-WebFlasher -WLED web flasher using the Web Serial API, powered by ESPHome Web +# WLED Web Installer + +A static, GitHub-Pages-friendly web installer for [WLED](https://github.com/wled/WLED), built on +[ESP Web Tools](https://esphome.github.io/esp-web-tools/). + +No build step and no server component: `index.html` fetches the release list straight from the +GitHub Releases API at page-load time and generates an install manifest on the fly for whichever +version / variant / flash size combination the user picks. + +## Features + +- Always up to date - versions come live from the [wled/WLED releases](https://github.com/wled/WLED/releases) API, no manual manifest maintenance. +- Variant selection (Plain, Audioreactive, Ethernet, ESP8266 CPU frequency test, ESP32 V4, DEBUG, HUB75 Matrix) - unavailable variants for the selected version are hidden automatically. +- Flash size selection (1MB / 2MB / 4MB / 8MB / 16MB) for boards that ship multiple binaries (ESP32-S3, ESP8266) - unavailable sizes for the selected version/variant are hidden automatically. +- HUB75 layout selection (board/pinout picker) when the HUB75 Matrix variant is selected. +- Multi-language UI, loaded dynamically from `lang/` - see [Localization](#localization). + +## Local development + +Any static file server works, e.g.: + +``` +npx serve . +``` + +Then open the printed local URL in Chrome or Edge (Web Serial is required). + +## Deploying + +Push to a repository and enable GitHub Pages for it (Settings -> Pages -> Deploy from branch). +No build step is required - the repository root is the site. + +## Localization + +`lang/languages.json` maps each supported language code to its display name, and +`lang/.json` holds that language's strings. Both are fetched at runtime - `i18n.js` never +hardcodes a language list. + +To add a language: + +1. Copy `lang/en.json` to `lang/.json` (e.g. `lang/de.json`) and translate the values. +2. Add one line to `lang/languages.json`: `"": ""`. + +That's it - the language selector and every `data-i18n` element on the page pick it up on next +load, no code changes required. Missing keys in a translation silently fall back to English. + +## Files + +- `index.html`, `style.css` - page markup/styling. +- `app.js` - release fetching, manifest generation, UI wiring. +- `i18n.js` - loads `lang/` and applies translations. +- `lang/` - one JSON file per language, plus `languages.json` listing them. +- `bin/boot/` - chip bootloaders and partition tables needed to flash bare chips (WLED release assets only contain the application binary). + +## License + +MIT, see `LICENSE`. diff --git a/app.js b/app.js new file mode 100644 index 0000000..527cf52 --- /dev/null +++ b/app.js @@ -0,0 +1,747 @@ +// app.js - WLED Web Installer +// +// Fetches available WLED releases from the GitHub Releases API, builds +// esp-web-tools manifests on the fly (as blob URLs) for every combination of +// version x variant x flash size, and wires up the page's controls. +// +// No build step, no server component - everything below runs directly in +// the browser so the page can be hosted as-is on GitHub Pages. + +(function () { + 'use strict'; + + // --------------------------------------------------------------------- + // Configuration + // --------------------------------------------------------------------- + + const GITHUB_RELEASES_URL = 'https://api.github.com/repos/wled/WLED/releases'; + const CORS_PROXY = 'https://proxy.corsfix.com/?'; + const CACHE_KEY = 'wled_webinstaller_releases_cache'; + const CACHE_TTL = 5 * 60 * 1000; // 5 minutes + const MAX_STABLE_RELEASES = 8; + const MAX_BETA_RELEASES = 2; + + // Base URL for locally-hosted bootloader / partition-table files. These + // are chip-specific (and, for the ESP32-S3, flash-size specific) and are + // shared across all WLED versions. + const bootBase = new URL('bin/boot/', window.location.href).href; + + // --------------------------------------------------------------------- + // Chip boot configuration + // --------------------------------------------------------------------- + // Describes the boot-stage parts (bootloader / partition table) that must + // be flashed before the WLED firmware itself. The firmware part is always + // appended last, at firmwareOffset. + // + // The ESP32-S3 partition table depends on the amount of flash on the + // board, so its bootParts is a function of the selected flash size. + + const CHIP_CONFIG = { + 'ESP32': { + chipFamily: 'ESP32', + fixedFlashSize: '4MB', + bootParts: () => [ + { path: bootBase + 'esp32_bootloader_v4.bin', offset: 0 } + ], + firmwareOffset: 65536 + }, + 'ESP32-C3': { + chipFamily: 'ESP32-C3', + fixedFlashSize: '4MB', + bootParts: () => [ + { path: bootBase + 'esp32-c3_bootloader_v2.bin', offset: 0 } + ], + firmwareOffset: 65536 + }, + 'ESP32-S2': { + chipFamily: 'ESP32-S2', + fixedFlashSize: '4MB', + bootParts: () => [ + { path: bootBase + 'bootloader_s2.bin', offset: 4096 }, + { path: bootBase + 'partitions_s2_4m.bin', offset: 32768 } + ], + firmwareOffset: 65536 + }, + 'ESP32-S3': { + chipFamily: 'ESP32-S3', + defaultFlashSize: '8MB', + // esp-web-tools flashes bootloader images as-is (it never patches the + // flash-size field the way `esptool.py write_flash` does), so we need + // one pre-patched+re-signed bootloader per flash size rather than + // reusing a single 8MB-labeled one for every board. + bootParts: (flashSizeId) => { + const files = { + '4MB': { bootloader: 'bootloader_s3_4m.bin', partitions: 'partitions_s3_4m.bin' }, + '8MB': { bootloader: 'bootloader_s3.bin', partitions: 'partitions_s3_8m.bin' }, + '16MB': { bootloader: 'bootloader_s3_16m.bin', partitions: 'partitions_s3_16m.bin' } + }; + const f = files[flashSizeId] || files['8MB']; + return [ + { path: bootBase + f.bootloader, offset: 0 }, + { path: bootBase + f.partitions, offset: 32768 } + ]; + }, + firmwareOffset: 65536 + }, + 'ESP8266': { + chipFamily: 'ESP8266', + defaultFlashSize: '4MB', + bootParts: () => [], + firmwareOffset: 0 + } + }; + + // Flash sizes shown in the UI, in display order. Not every chip supports + // every size - see VARIANTS below. + const FLASH_SIZES = [ + { id: '1MB', label: '1MB' }, + { id: '2MB', label: '2MB' }, + { id: '4MB', label: '4MB' }, + { id: '8MB', label: '8MB' }, + { id: '16MB', label: '16MB' } + ]; + const DEFAULT_FLASH_SIZE = '4MB'; + + // --------------------------------------------------------------------- + // HUB75 layouts + // --------------------------------------------------------------------- + // Each HUB75 build targets one specific board/pinout combination rather + // than a chip family with a flash-size choice, so it doesn't fit the + // VARIANTS model below - a layout fully determines chip, flash size and + // boot parts all at once. flashSizeLabel is display-only. + + const HUB75_LAYOUTS = [ + { + id: 'esp32_default', + label: 'ESP32 (default pinout)', + chip: 'ESP32', + suffix: '_ESP32_HUB75.bin', + flashSizeLabel: '4MB', + bootParts: () => CHIP_CONFIG['ESP32'].bootParts(), + firmwareOffset: CHIP_CONFIG['ESP32'].firmwareOffset + }, + { + id: 'esp32_forum', + label: 'ESP32 (forum pinout)', + chip: 'ESP32', + suffix: '_ESP32_HUB75_forum_pinout.bin', + flashSizeLabel: '4MB', + bootParts: () => CHIP_CONFIG['ESP32'].bootParts(), + firmwareOffset: CHIP_CONFIG['ESP32'].firmwareOffset + }, + { + id: 'hdwf2', + label: 'Huidu HD-WF2', + chip: 'ESP32-S3', + suffix: '_ESP32-S3_HD-WF2.bin', + // Physically a 4MB board; WLED's own build config for it (uncommonly) + // inherits an 8MB-declared bootloader paired with a scaled-down 4MB + // partition table, so we match that exact pairing here rather than + // "correct" it - it's what upstream actually ships and tests. + flashSizeLabel: '4MB', + bootParts: () => [ + { path: bootBase + 'bootloader_s3.bin', offset: 0 }, + { path: bootBase + 'partitions_s3_hdwf2.bin', offset: 32768 } + ], + firmwareOffset: 65536 + }, + { + id: 'matrixportal', + label: 'Adafruit MatrixPortal S3', + chip: 'ESP32-S3', + suffix: '_ESP32-S3_Adafruit_Matrixportal.bin', + flashSizeLabel: '8MB', + bootParts: () => CHIP_CONFIG['ESP32-S3'].bootParts('8MB'), + firmwareOffset: 65536 + }, + { + id: 'moonhub', + label: 'MOONHUB / LilyGO T7-S3', + chip: 'ESP32-S3', + suffix: '_ESP32-S3_16MB_opi_HUB75.bin', + flashSizeLabel: '16MB', + bootParts: () => CHIP_CONFIG['ESP32-S3'].bootParts('16MB'), + firmwareOffset: 65536 + }, + { + id: 'waveshare', + label: 'Waveshare ESP32-S3-RGB-Matrix', + chip: 'ESP32-S3', + suffix: '_ESP32-S3_Waveshare_HUB75.bin', + flashSizeLabel: '32MB', + bootParts: () => [ + { path: bootBase + 'bootloader_s3_32m.bin', offset: 0 }, + { path: bootBase + 'partitions_s3_32m.bin', offset: 32768 } + ], + firmwareOffset: 65536 + } + ]; + const DEFAULT_HUB75_LAYOUT = HUB75_LAYOUTS[0].id; + + // --------------------------------------------------------------------- + // Variant definitions + // --------------------------------------------------------------------- + // Each variant maps chip families to the asset-name suffix used in GitHub + // release assets. A chip entry is either: + // - a plain string: one asset, flash size is irrelevant + // - a { flashSizeId: suffix } map: the chip ships multiple binaries + // for different flash sizes + // + // Chips that have no entry for a given variant are simply left out of the + // generated manifest for that variant (e.g. "audio" is ESP32-only). + + const VARIANTS = { + normal: { + 'ESP32': '_ESP32.bin', + 'ESP32-C3': '_ESP32-C3.bin', + 'ESP32-S2': '_ESP32-S2.bin', + 'ESP32-S3': { '4MB': '_ESP32-S3_4M_qspi.bin', '8MB': '_ESP32-S3_8MB_opi.bin', '16MB': '_ESP32-S3_16MB_opi.bin' }, + 'ESP8266': { '1MB': '_ESP01.bin', '2MB': '_ESP02.bin', '4MB': '_ESP8266.bin' } + }, + ethernet: { + 'ESP32': '_ESP32_Ethernet.bin' + }, + audio: { + 'ESP32': '_ESP32_audioreactive.bin' + }, + test: { + 'ESP8266': { '1MB': '_ESP01_160.bin', '2MB': '_ESP02_160.bin', '4MB': '_ESP8266_160.bin' } + }, + v4: { + 'ESP32': '_ESP32_V4.bin' + }, + debug: { + 'ESP32': '_ESP32_DEBUG.bin' + } + }; + + // Maps variant names to the DOM ids used for their radio inputs. "hub75" + // is handled outside the VARIANTS table (see HUB75_LAYOUTS above) but its + // radio button follows the same enable/disable convention as the rest. + const VARIANT_IDS = ['normal', 'ethernet', 'audio', 'test', 'v4', 'debug', 'hub75']; + + // --------------------------------------------------------------------- + // Helpers + // --------------------------------------------------------------------- + + function hasFlashAxis(entry) { + return entry !== null && typeof entry === 'object'; + } + + /** Resolve the asset suffix for a chip, given the globally selected flash size. */ + function resolveSuffix(entry, chip, flashSizeId) { + if (!hasFlashAxis(entry)) return entry; + return entry[flashSizeId] || entry[CHIP_CONFIG[chip].defaultFlashSize]; + } + + /** Find a release asset whose name ends with `suffix` (ignore .gz files). */ + function findAsset(assets, suffix) { + if (!suffix) return null; + return assets.find(function (a) { + return a.name.endsWith(suffix) && !a.name.endsWith('.gz'); + }) || null; + } + + /** Extract the WLED version string from asset filenames (for nightly). */ + function extractVersionFromAssets(assets) { + for (let i = 0; i < assets.length; i++) { + const m = assets[i].name.match(/^WLED_(.+?)_(ESP\d|ESP8)/); + if (m) return m[1]; + } + return 'unknown'; + } + + function getDisplayVersion(release) { + if (release.tag_name === 'nightly') { + return extractVersionFromAssets(release.assets) + ' Nightly'; + } + return release.tag_name.replace(/^v/, ''); + } + + function getManifestVersion(release, variantName) { + let ver = release.tag_name === 'nightly' + ? extractVersionFromAssets(release.assets) + : release.tag_name.replace(/^v/, ''); + if (variantName !== 'normal') ver += ' ' + variantName; + return ver; + } + + function categorize(release) { + if (release.tag_name === 'nightly') return 'nightly'; + if (release.prerelease) return 'beta'; + return 'release'; + } + + // --------------------------------------------------------------------- + // Manifest generation + // --------------------------------------------------------------------- + + /** + * Build an esp-web-tools manifest object for the given release + variant + + * flash size. Returns null if no matching assets are found at all. + */ + function generateManifest(release, variantName, flashSizeId) { + const chipEntries = VARIANTS[variantName]; + const version = getManifestVersion(release, variantName); + const builds = []; + + for (const chip in chipEntries) { + const suffix = resolveSuffix(chipEntries[chip], chip, flashSizeId); + const asset = findAsset(release.assets, suffix); + if (!asset) continue; + + const config = CHIP_CONFIG[chip]; + const parts = config.bootParts(flashSizeId); + parts.push({ + path: CORS_PROXY + asset.browser_download_url, + offset: config.firmwareOffset + }); + + builds.push({ chipFamily: config.chipFamily, parts: parts }); + } + + if (builds.length === 0) return null; + + return { + name: 'WLED', + version: version, + home_assistant_domain: 'wled', + new_install_prompt_erase: true, + builds: builds + }; + } + + /** + * Build a manifest for a single HUB75 layout (one specific board). Unlike + * generateManifest(), this always produces at most one build entry, since + * a layout already fully determines the chip. + */ + function generateHub75Manifest(release, layoutId) { + const layout = HUB75_LAYOUTS.find(function (l) { return l.id === layoutId; }); + if (!layout) return null; + + const asset = findAsset(release.assets, layout.suffix); + if (!asset) return null; + + const parts = layout.bootParts(); + parts.push({ + path: CORS_PROXY + asset.browser_download_url, + offset: layout.firmwareOffset + }); + + return { + name: 'WLED', + version: getManifestVersion(release, 'hub75'), + home_assistant_domain: 'wled', + new_install_prompt_erase: true, + builds: [{ chipFamily: layout.chip, parts: parts }] + }; + } + + function createManifestUrl(manifest) { + const blob = new Blob([JSON.stringify(manifest)], { type: 'application/json' }); + return URL.createObjectURL(blob); + } + + /** + * For a given release + variant, figure out which chips are actually + * available at each flash size (i.e. a matching release asset exists). + * Chips with a single fixed flash size (ESP32, ESP32-C3, ESP32-S2) are + * listed too, alongside the chips that actually offer a size choice + * (ESP32-S3, ESP8266) - the row itself is only shown when at least one + * chip in this variant has a real choice to make. + */ + function getFlashSizeAvailability(release, variantName) { + const chipEntries = VARIANTS[variantName]; + const axisChips = Object.keys(chipEntries).filter(function (chip) { + return hasFlashAxis(chipEntries[chip]); + }); + + const chipsBySize = {}; + FLASH_SIZES.forEach(function (fs) { chipsBySize[fs.id] = []; }); + + Object.keys(chipEntries).forEach(function (chip) { + const entry = chipEntries[chip]; + if (hasFlashAxis(entry)) { + Object.keys(entry).forEach(function (sizeId) { + if (findAsset(release.assets, entry[sizeId])) chipsBySize[sizeId].push(chip); + }); + } else { + const fixedSize = CHIP_CONFIG[chip] && CHIP_CONFIG[chip].fixedFlashSize; + if (fixedSize && findAsset(release.assets, entry)) chipsBySize[fixedSize].push(chip); + } + }); + + return { hasAxis: axisChips.length > 0, chipsBySize: chipsBySize }; + } + + // --------------------------------------------------------------------- + // Dropdown population + // --------------------------------------------------------------------- + + /** + * Create a single