Browser installer on GitHub Pages
installer/apollo-mtr-1.factory.yaml is the example config without Wi-Fi credentials, with Improv serial and the access point. A version tag on the GitHub mirror builds it with esphome/build-action and publishes the page, firmware and manifest to GitHub Pages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6831bbf9a8
commit
f13dfd350b
5 files changed
+299
-3
No files matched your search
@@ -0,0 +1,80 @@
|
|||||||
|
# Builds installer/apollo-mtr-1.factory.yaml and publishes the browser
|
||||||
|
# installer (installer/index.html, the firmware and its ESP Web Tools
|
||||||
|
# manifest) to GitHub Pages. Runs on a version tag (v2026.10.0) or by hand.
|
||||||
|
# The site holds one firmware: the latest build.
|
||||||
|
|
||||||
|
name: Installer
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: installer
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
|
- name: Version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [[ "$GITHUB_REF_TYPE" == "tag" ]]; then v="${GITHUB_REF_NAME#v}"; else v="dev-${GITHUB_SHA::7}"; fi
|
||||||
|
echo "version=$v" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Build firmware
|
||||||
|
id: build
|
||||||
|
uses: esphome/build-action@ae5408cdbc4f70692bf8e345c4982bca05a93667 # v8.1.0
|
||||||
|
with:
|
||||||
|
yaml-file: installer/apollo-mtr-1.factory.yaml
|
||||||
|
version: 2026.8.2
|
||||||
|
complete-manifest: true
|
||||||
|
substitutions: |
|
||||||
|
follow_me_version=${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Assemble site
|
||||||
|
env:
|
||||||
|
BUILD_DIR: ${{ steps.build.outputs.name }}
|
||||||
|
VERSION: ${{ steps.version.outputs.version }}
|
||||||
|
run: |
|
||||||
|
mkdir -p site/fonts
|
||||||
|
cp -r "$BUILD_DIR" site/firmware
|
||||||
|
rm -f site/firmware/*.elf
|
||||||
|
cp wizard/fonts/*.woff2 wizard/fonts/OFL.txt site/fonts/
|
||||||
|
sed "s/__VERSION__/$VERSION/g" installer/index.html > site/index.html
|
||||||
|
ls -R site
|
||||||
|
|
||||||
|
- name: Upload site
|
||||||
|
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
||||||
|
with:
|
||||||
|
path: site
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
publish:
|
||||||
|
name: Publish
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
steps:
|
||||||
|
- name: Configure Pages
|
||||||
|
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
|
||||||
@@ -41,7 +41,15 @@ All values for your stairs come from the setup wizard: the shape, the number of
|
|||||||
- The sensor and the WLED controller on the same network.
|
- The sensor and the WLED controller on the same network.
|
||||||
- Straight, L-shaped or U-shaped stairs, with at most one landing.
|
- Straight, L-shaped or U-shaped stairs, with at most one landing.
|
||||||
|
|
||||||
## Installing on an Apollo MTR-1
|
## Installing from the browser
|
||||||
|
|
||||||
|
Open the [follow-me installer](https://randompedestrian07.github.io/follow-me-stairs/) in Chrome or Edge on a computer, and connect the MTR-1 with a USB-C data cable. The installer flashes the firmware and then asks for your Wi-Fi. Installing erases the sensor. A sensor that already has a follow-me setup must go through the setup wizard again.
|
||||||
|
|
||||||
|
The installer firmware has no Wi-Fi credentials. If the Wi-Fi step does not appear, connect a phone to the `Apollo MTR1 Hotspot` network and enter your Wi-Fi there.
|
||||||
|
|
||||||
|
To change the firmware yourself, install with ESPHome instead.
|
||||||
|
|
||||||
|
## Installing on an Apollo MTR-1 with ESPHome
|
||||||
|
|
||||||
1. Copy `example/apollo-mtr-1.yaml` into your ESPHome configuration folder.
|
1. Copy `example/apollo-mtr-1.yaml` into your ESPHome configuration folder.
|
||||||
2. Put this repository in the same folder, as a folder named `follow-me-stairs`.
|
2. Put this repository in the same folder, as a folder named `follow-me-stairs`.
|
||||||
|
|||||||
+10
-2
@@ -89,6 +89,14 @@ These addresses have no authentication. Any device on the network can use them.
|
|||||||
- The LD2450 reports target x mirrored. The stored setup keeps the radar's own frame. Code that draws radar readings or decides left and right must negate x.
|
- The LD2450 reports target x mirrored. The stored setup keeps the radar's own frame. Code that draws radar readings or decides left and right must negate x.
|
||||||
- The LD2450 sees about 60° to each side and about 6 m out. The wizard warns when part of the stairs is outside that area.
|
- The LD2450 sees about 60° to each side and about 6 m out. The wizard warns when part of the stairs is outside that area.
|
||||||
|
|
||||||
## Not built
|
## Browser installer
|
||||||
|
|
||||||
- Installing a prebuilt firmware from a web browser.
|
| Path | Contents |
|
||||||
|
|---|---|
|
||||||
|
| `installer/apollo-mtr-1.factory.yaml` | The installer firmware: `example/apollo-mtr-1.yaml` without Wi-Fi credentials, with Improv over USB serial and the access point |
|
||||||
|
| `installer/index.html` | The installer page. `__VERSION__` is replaced with the release version. |
|
||||||
|
| `.github/workflows/installer.yml` | Builds the firmware with ESPHome and publishes the page, the firmware and its manifest to GitHub Pages |
|
||||||
|
|
||||||
|
The workflow runs on the GitHub mirror when a tag that starts with `v` arrives, for example `v2026.10.0`. The tag without the `v` becomes the firmware's project version. The site holds one firmware: the latest build.
|
||||||
|
|
||||||
|
The installer firmware leaves out Improv over Bluetooth. With it, flash use goes from 69% to 91%.
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# Apollo MTR-1 with the follow-me stair light: the firmware the browser
|
||||||
|
# installer flashes (installer/index.html, published to GitHub Pages by
|
||||||
|
# .github/workflows/installer.yml on every version tag).
|
||||||
|
#
|
||||||
|
# Same as example/apollo-mtr-1.yaml without Wi-Fi credentials. After flashing,
|
||||||
|
# the installer page asks for Wi-Fi over USB (Improv serial). Without it, the
|
||||||
|
# sensor opens the "Apollo MTR1 Hotspot" access point with a captive portal.
|
||||||
|
# Improv over Bluetooth is left out: it raises flash use from 69% to 91%.
|
||||||
|
|
||||||
|
substitutions:
|
||||||
|
name: apollo-mtr-1
|
||||||
|
version: "26.3.2.1" # Apollo's MTR-1 release, shown as its firmware version
|
||||||
|
follow_me_version: "dev" # set by the installer workflow from the tag
|
||||||
|
follow_me_dir: ../firmware
|
||||||
|
|
||||||
|
esphome:
|
||||||
|
name: "${name}"
|
||||||
|
friendly_name: Apollo MTR-1
|
||||||
|
comment: Apollo MTR-1 with follow-me-stairs
|
||||||
|
name_add_mac_suffix: true
|
||||||
|
project:
|
||||||
|
name: "follow-me-stairs.apollo-mtr-1"
|
||||||
|
version: "${follow_me_version}"
|
||||||
|
min_version: 2026.8.2
|
||||||
|
on_boot:
|
||||||
|
- priority: 500
|
||||||
|
then:
|
||||||
|
- text_sensor.template.publish:
|
||||||
|
id: apollo_firmware_version
|
||||||
|
state: "${version}"
|
||||||
|
- priority: -10
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- lambda: "return id(runTest);"
|
||||||
|
then:
|
||||||
|
- lambda: "id(testScript).execute();"
|
||||||
|
|
||||||
|
logger:
|
||||||
|
|
||||||
|
ota:
|
||||||
|
- platform: esphome
|
||||||
|
id: ota_default
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
power_save_mode: none
|
||||||
|
ap:
|
||||||
|
ssid: "Apollo MTR1 Hotspot"
|
||||||
|
|
||||||
|
improv_serial:
|
||||||
|
|
||||||
|
captive_portal:
|
||||||
|
|
||||||
|
packages:
|
||||||
|
core: github://ApolloAutomation/MTR-1/Integrations/ESPHome/Core.yaml@26.3.2.1
|
||||||
|
follow_me: !include ../firmware/follow-me.yaml
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Follow-Me Installer</title>
|
||||||
|
<meta name="description" content="Install the follow-me stair light on an Apollo MTR-1 from your browser.">
|
||||||
|
<script type="module" src="https://unpkg.com/esp-web-tools@10.4.0/dist/web/install-button.js?module"></script>
|
||||||
|
<style>
|
||||||
|
@font-face{font-family:"IBM Plex Sans";font-weight:100 700;font-display:swap;src:url(fonts/plex-sans.woff2) format("woff2")}
|
||||||
|
@font-face{font-family:"IBM Plex Mono";font-weight:400;font-display:swap;src:url(fonts/plex-mono-400.woff2) format("woff2")}
|
||||||
|
@font-face{font-family:"IBM Plex Mono";font-weight:500 600;font-display:swap;src:url(fonts/plex-mono-500.woff2) format("woff2")}
|
||||||
|
:root{
|
||||||
|
--bg:#0a0a0b; --panel:#0e0e10;
|
||||||
|
--ink:#ece8df; --ink-2:rgba(236,232,223,.62); --ink-3:rgba(236,232,223,.36); --ink-4:rgba(236,232,223,.14);
|
||||||
|
--line:rgba(236,232,223,.1); --line-2:rgba(236,232,223,.2);
|
||||||
|
--warm:#ffb35a; --warm-2:#ffd6a0;
|
||||||
|
--sans:"IBM Plex Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||||
|
--mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||||
|
color-scheme:dark;
|
||||||
|
/* ESP Web Tools dialogs */
|
||||||
|
--improv-primary-color:var(--warm);
|
||||||
|
--improv-text-color:var(--ink);
|
||||||
|
--mdc-theme-primary:var(--warm);
|
||||||
|
--mdc-theme-on-primary:#1b1006;
|
||||||
|
--mdc-theme-surface:#141416;
|
||||||
|
--mdc-dialog-heading-ink-color:var(--ink);
|
||||||
|
--mdc-dialog-content-ink-color:var(--ink-2);
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box}
|
||||||
|
html{background:var(--bg)}
|
||||||
|
body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 var(--sans);-webkit-font-smoothing:antialiased;
|
||||||
|
background-image:linear-gradient(var(--line) 1px,transparent 1px),linear-gradient(90deg,var(--line) 1px,transparent 1px);
|
||||||
|
background-size:48px 48px;background-position:-1px -1px}
|
||||||
|
a{color:var(--warm-2);text-decoration:none;border-bottom:1px solid rgba(255,214,160,.3)}
|
||||||
|
a:hover{border-bottom-color:var(--warm-2)}
|
||||||
|
.wrap{max-width:720px;margin:0 auto;padding:0 16px}
|
||||||
|
header.bar{display:flex;align-items:center;justify-content:space-between;padding:22px 0;border-bottom:1px solid var(--line)}
|
||||||
|
.brand{font:500 13px/1 var(--mono);letter-spacing:.32em}
|
||||||
|
.ver{font:11px/1 var(--mono);letter-spacing:.12em;color:var(--ink-3)}
|
||||||
|
|
||||||
|
.hero{padding:56px 0 8px}
|
||||||
|
.kicker{font:11px/1 var(--mono);letter-spacing:.16em;color:var(--ink-3);margin-bottom:16px}
|
||||||
|
h1{font:600 clamp(30px,6vw,44px)/1.08 var(--sans);letter-spacing:-.02em;margin:0 0 16px}
|
||||||
|
.lede{color:var(--ink-2);font-size:16.5px;max-width:560px;margin:0}
|
||||||
|
|
||||||
|
/* the strip: a lit window gliding along a row of LEDs */
|
||||||
|
.strip{display:grid;grid-template-columns:repeat(36,1fr);gap:clamp(3px,1vw,7px);margin:40px 0 8px;position:relative}
|
||||||
|
.strip i{aspect-ratio:1;border-radius:50%;background:var(--ink-4)}
|
||||||
|
.strip .glow{position:absolute;inset:-6px auto -6px 0;width:22%;border-radius:999px;
|
||||||
|
background:radial-gradient(closest-side,rgba(255,214,160,.95),rgba(255,179,90,.55) 55%,transparent);
|
||||||
|
filter:blur(3px);mix-blend-mode:screen;animation:walk 9s cubic-bezier(.45,0,.55,1) infinite alternate}
|
||||||
|
@keyframes walk{0%,8%{left:0}92%,100%{left:78%}}
|
||||||
|
@media (prefers-reduced-motion:reduce){.strip .glow{animation:none;left:39%}}
|
||||||
|
|
||||||
|
.card{background:var(--panel);border:1px solid var(--line);margin:40px 0 0;padding:28px 24px}
|
||||||
|
.card h2{font:600 20px/1.2 var(--sans);margin:0 0 6px}
|
||||||
|
.card .sub{color:var(--ink-3);font-size:13.5px;margin:0 0 20px}
|
||||||
|
ol.steps{list-style:none;counter-reset:s;margin:0 0 24px;padding:0;border-top:1px solid var(--line)}
|
||||||
|
ol.steps li{counter-increment:s;display:grid;grid-template-columns:36px 1fr;padding:12px 0;border-bottom:1px solid var(--line);color:var(--ink-2)}
|
||||||
|
ol.steps li::before{content:counter(s,decimal-leading-zero);font:12px/1.9 var(--mono);color:var(--ink-3)}
|
||||||
|
ol.steps b{color:var(--ink);font-weight:500}
|
||||||
|
|
||||||
|
.go{display:inline-flex;align-items:center;gap:10px;border:0;background:var(--ink);color:#111;padding:14px 22px;
|
||||||
|
font:500 15px/1 var(--sans);letter-spacing:.01em;cursor:pointer;border-radius:3px}
|
||||||
|
.go:hover{background:#fff}
|
||||||
|
.go:focus-visible{outline:2px solid var(--warm);outline-offset:3px}
|
||||||
|
.warn{border-left:2px solid var(--warm);padding:8px 0 8px 14px;margin:22px 0 0;font-size:13.5px;color:var(--warm-2)}
|
||||||
|
.note{border-left:2px solid var(--line-2);padding:8px 0 8px 14px;margin:0;font-size:14px;color:var(--ink-2)}
|
||||||
|
esp-web-install-button[install-unsupported] .go,esp-web-install-button[install-supported] .note{display:none}
|
||||||
|
|
||||||
|
section.next{margin:48px 0 0}
|
||||||
|
section.next h2,.links h2{font:11px/1 var(--mono);letter-spacing:.16em;color:var(--ink-3);margin:0 0 14px;font-weight:400}
|
||||||
|
section.next p{color:var(--ink-2);margin:0 0 10px}
|
||||||
|
code{font:13px var(--mono);color:var(--ink);background:rgba(255,255,255,.05);padding:1px 5px;border-radius:2px}
|
||||||
|
|
||||||
|
.links{margin:48px 0 0;padding-top:24px;border-top:1px solid var(--line)}
|
||||||
|
.links ul{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:4px 24px}
|
||||||
|
.links li{padding:6px 0;font-size:14px}
|
||||||
|
footer{margin:48px 0 0;padding:20px 0 40px;border-top:1px solid var(--line);font:11.5px/1.7 var(--mono);color:var(--ink-3)}
|
||||||
|
footer a{color:var(--ink-2);border-bottom-color:var(--line-2)}
|
||||||
|
@media (max-width:520px){.card{padding:22px 16px}.hero{padding-top:40px}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<header class="bar">
|
||||||
|
<span class="brand">FOLLOW-ME</span>
|
||||||
|
<span class="ver">VERSION __VERSION__</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="hero">
|
||||||
|
<div class="kicker">STAIR LIGHT · APOLLO MTR-1 · WLED</div>
|
||||||
|
<h1>A stair light that follows you.</h1>
|
||||||
|
<p class="lede">A radar sensor at the stairs tracks where each person is. An LED strip along the stairs lights the part next to that person, and the lit part moves with them, up or down.</p>
|
||||||
|
<div class="strip" aria-hidden="true">
|
||||||
|
<i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i>
|
||||||
|
<i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i>
|
||||||
|
<i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i>
|
||||||
|
<span class="glow"></span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card">
|
||||||
|
<h2>Install on an Apollo MTR-1</h2>
|
||||||
|
<p class="sub">Use Chrome or Edge on a computer. Phones and other browsers cannot connect to the sensor over USB.</p>
|
||||||
|
<ol class="steps">
|
||||||
|
<li><span>Connect the MTR-1 to this computer with a <b>USB-C data cable</b>. A charge-only cable does not work.</span></li>
|
||||||
|
<li><span>Click <b>Install Follow-Me</b> and pick the sensor from the list.</span></li>
|
||||||
|
<li><span>When the install is complete, enter your <b>Wi-Fi</b> name and password.</span></li>
|
||||||
|
<li><span>Click <b>Visit device</b> to open the sensor's web page.</span></li>
|
||||||
|
</ol>
|
||||||
|
<esp-web-install-button manifest="firmware/manifest.json">
|
||||||
|
<button slot="activate" class="go">Install Follow-Me →</button>
|
||||||
|
<p slot="unsupported" class="note">This browser cannot install firmware. Open this page in Chrome or Edge on a computer.</p>
|
||||||
|
<p slot="not-allowed" class="note">The installer works only over HTTPS. Open this page from its https:// address.</p>
|
||||||
|
</esp-web-install-button>
|
||||||
|
<div class="warn">Installing erases the sensor. A sensor that already has a follow-me setup must go through the setup wizard again. To keep the setup, update the sensor from ESPHome instead.</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="next">
|
||||||
|
<h2>NEXT: SET UP YOUR STAIRS</h2>
|
||||||
|
<p>On the sensor's web page, click <b>Set up Follow-Me</b>. The setup wizard asks for the shape of your stairs, has you walk them once, and lines up the LED strip. It takes about ten minutes.</p>
|
||||||
|
<p>If the Wi-Fi step did not appear, connect your phone to the <code>Apollo MTR1 Hotspot</code> network and enter your Wi-Fi there.</p>
|
||||||
|
<p>WLED needs some settings changed first. The wizard checks them and can change them for you.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="links">
|
||||||
|
<h2>MORE</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://git.outer-haven.io/JacksoTheGreat/follow-me-stairs">Source and documentation</a></li>
|
||||||
|
<li><a href="https://git.outer-haven.io/JacksoTheGreat/follow-me-stairs/src/branch/main/docs/settings.md">Every setting explained</a></li>
|
||||||
|
<li><a href="https://github.com/RandomPedestrian07/follow-me-stairs">GitHub mirror</a></li>
|
||||||
|
<li><a href="https://apolloautomation.com/products/mtr-1">Apollo MTR-1</a></li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
MIT license. IBM Plex fonts: <a href="fonts/OFL.txt">SIL Open Font License 1.1</a>.<br>
|
||||||
|
Installer by <a href="https://esphome.github.io/esp-web-tools/">ESP Web Tools</a>. Firmware built with <a href="https://esphome.io">ESPHome</a>.
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in new issue
Block a user