From bb554eac0776e2c64154f98ee67530fd75227463 Mon Sep 17 00:00:00 2001 From: Bas Meerman <30606346+basmeerman@users.noreply.github.com> Date: Fri, 26 Jun 2026 21:53:12 +0200 Subject: [PATCH] docs: document the OTA / reflash procedure Add a consistent "updating the device" section to README.md, CLAUDE.md, and PROJECT_PLAN.md (F5): OTA-over-WiFi as the normal path (esphome run --device fancontrol.local), web-UI and USB fallbacks, and a warning never to flash the dummy-secret GitHub Release binaries. Notes that OTA is push-based (no GitHub auto-pull) and that secrets.yaml is local / backed up in 1Password. Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 26 ++++++++++++++++++++++++++ PROJECT_PLAN.md | 23 +++++++++++++++++++++++ README.md | 30 ++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 10090f3..2f7d71c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,6 +52,32 @@ their SHA-256 checksums. password + checksum. Pre-v0.2.0 releases (v0.1.0, v0.1.1) are still RSA-signed and verifiable against `docs/signing_public_key.pem`; see [`SECURITY.md`](SECURITY.md). +## Updating the device + +After first boot the device is on WiFi, so updates go **OTA over the network** +— no USB needed. + +```bash +esphome config fancontrol.yaml # lint +esphome compile fancontrol.yaml # build (lambdas fail here, not at config) +esphome run fancontrol.yaml --device fancontrol.local # compile + OTA push (mDNS) +esphome logs fancontrol.yaml --device fancontrol.local # confirm it came back up +``` + +`esphome run` compiles and uploads in one step; the device authenticates the +upload with `ota_password` from `secrets.yaml`, reboots, and `restart_counter` +increments. The web UI (`http://fancontrol.local`) also accepts an OTA upload +of the built `.ota.bin`. + +- **USB fallback** (OTA broken, or device won't join WiFi / is in safe mode): + `esphome run fancontrol.yaml --device /dev/cu.wchusbserial10`. +- **Never flash the GitHub Release `.bin`.** Release artifacts are built in CI + with **dummy secrets** (placeholder WiFi/MQTT), so they will not join the + network. Always build locally against the real `secrets.yaml`. Releases are + reference/reproducibility artifacts only. +- `secrets.yaml` is gitignored and lives locally; the backup is in the + maintainer's 1Password. Restore it there before building on a fresh machine. + ## Architecture constraints (enforce these) Because the project lives in one YAML file, "architecture" is mostly about diff --git a/PROJECT_PLAN.md b/PROJECT_PLAN.md index 5495ca5..9ff0f2a 100644 --- a/PROJECT_PLAN.md +++ b/PROJECT_PLAN.md @@ -63,6 +63,29 @@ native API and leave MQTT off. ### F5 — OTA updates - `ota:` platform esphome, password-gated (`secrets.yaml` → `ota_password`). - SHA-256 handshake; no RSA signing (see *History* below and `SECURITY.md`). +- **Push-based, not auto-pull.** The device never polls GitHub; there is no + `dashboard_import` / `http_request` update platform. You push firmware to it + from the LAN. + +**Update procedure (after first boot — device is on WiFi):** + +```bash +esphome config fancontrol.yaml # lint +esphome compile fancontrol.yaml # build +esphome run fancontrol.yaml --device fancontrol.local # compile + OTA over WiFi +esphome logs fancontrol.yaml --device fancontrol.local # verify boot +``` + +`esphome run` compiles and uploads in one step; the device authenticates with +`ota_password`, reboots, and `restart_counter` increments. Alternatives: the +web UI at `http://fancontrol.local` (OTA upload button, feed it the built +`.ota.bin`), or USB fallback `--device /dev/cu.wchusbserial10` if OTA is broken +or the device won't join WiFi. + +> **Do not flash the GitHub Release `.bin`.** Release artifacts are CI-built +> with dummy secrets and will not join the network — they are reference / +> reproducibility artifacts only. Always build locally against the real +> `secrets.yaml` (gitignored; backed up in 1Password). ### F6 — Persistence `globals` with `restore_value: true` for the boot counter. All `number` diff --git a/README.md b/README.md index 3eb812e..265b675 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,36 @@ esphome run fancontrol.yaml If WiFi credentials are wrong or missing, the device falls back to an open AP named **`FanControl-Setup`** at `192.168.4.1` — connect, configure, save. +## Updating the device + +After the first USB flash the device is on WiFi, so every later update goes +**over the air** — no cable needed: + +```bash +# Build and push the new firmware over WiFi (password-gated OTA) +esphome compile fancontrol.yaml +esphome run fancontrol.yaml --device fancontrol.local + +# Confirm it rebooted and reconnected +esphome logs fancontrol.yaml --device fancontrol.local +``` + +The device authenticates the upload with `ota_password`, reboots into the new +build, and bumps its restart counter. You can also upload a locally-built +`.ota.bin` through the web dashboard at `http://fancontrol.local`. + +**If OTA fails** (or the device can't join WiFi / is in safe mode), reflash over +USB: + +```bash +esphome run fancontrol.yaml --device /dev/cu.wchusbserial10 # adjust the serial port +``` + +> ⚠️ **Don't flash the binaries attached to a GitHub Release.** They are built +> in CI with placeholder secrets and will not connect to your WiFi. Always +> build locally against your own `secrets.yaml`. GitHub Release artifacts exist +> for reference and reproducibility only. + ## Home Assistant integration Once on the network, HA auto-discovers the device via the ESPHome integration.