Repository navigation
v3.4 — 2026-07-12
Downloads
| File | Use it when |
|---|---|
firmware.bin |
Updating a board that already runs this firmware — upload it through the gateway's web updater (Settings ▸ Open Firmware Updater →, or http://<gateway-ip>/ota). This is the OTA app image. |
firmware.factory.bin |
First flash of a blank board, over USB — bundles bootloader + partition table + app. Write it at flash address 0x0 (e.g. with the ESP web flasher). |
Gives the dashboard the companion's look, folds backup & restore into the
Settings page, teaches the gateway and the companion to tell each other which tabs
they have, and fixes a group of real robustness bugs — most importantly a web
server that froze during a paced batch and a watchdog reboot on large walls.
Drop-in upgrade: no module, MQTT or wiring changes, and backups themselves are
untouched.
Changed
-
A new look — the Home Assistant design language. The dashboard is restyled to
match the companion (which ships the same look), so the two feel like one product.
It follows the browser/OS light or dark preference; there is no theme setting.
Purely cosmetic — no control moved or changed behaviour. -
Backup & restore moved to Settings. The Backup Calibration and Restore
Calibration cards now sit at the end of the Settings page; the Backup tab is
gone. Nothing about the backup format or the restore-by-serial behaviour changed —
only where the controls live. (The backup file is assembled in the browser from the
existing module/EEPROM endpoints; restore posts to/api/flap/restorebysn.) An old
#backupdeep link now lands on Settings rather than breaking. -
Batch pacing no longer blocks the web server.
POST /api/rs485/batchused to
produce itsstep_msstagger by sleeping between frames, on the web task — and
the HTTP server handles one connection at a time, so a paced batch froze the whole
web UI for the length of the cascade (up to the old 8 s cap) while further
connections piled up in the TCP accept queue holding window buffers. The handler now
stamps each frame with a due time, hands it to the RS-485 task (which already wakes
every 5 ms) and returns immediately. The cascade on the wall is unchanged.Two contract notes:
200now means accepted, not transmitted (sentcounts
frames accepted), and the 8 s total-pacing cap is replaced by a queue depth of 127
paced frames in flight. A frame goes out immediately instead of paced when
step_msis 0, when it is longer than 48 bytes, or when the queue is full — so a
cascade beyond 127 paced frames loses its stagger past that point. A whole-page
redraw is one frame per module, so that only bites above 127 modules. -
The companion URL is persisted on a debounce. Two companions pointed at one
gateway each re-register their own URL on their ~30 s heartbeat, so the stored URL
flipped back and forth — and saving on every change meant an NVS write every ~30 s,
forever (observed in the wild). The URL now applies to RAM instantly (the tab is
live at once) but only reaches flash once it has held still for two minutes. A
contested URL therefore never gets written at all, which is the right answer; a
companion re-registers within a heartbeat of any reboot, so nothing is lost.
New
-
Tab advertisement (
POST /api/companion). The companion may now sendtabs—
the deep links its own UI offers — and the response always carriesgwTabs, this
firmware's. Each side then renders the other's real tabs instead of a list hard-coded
on the far side, which is what made the Backup tab above a two-release problem.Both halves are optional and independent, so every old/new pairing works: an older
companion advertises nothing and the dashboard falls back to its built-in companion
tabs; an older gateway returns nogwTabsand the companion falls back to its own list
(which still includes Backup, because a pre-3.4 gateway really does have that tab).An advertised list is taken whole or not at all — any malformed entry, a bad id
(not[A-Za-z0-9_-]{1,24}), a label over 24 printable ASCII characters, more than 10
tabs, or a list over 384 bytes drops the list, and the peer then shows its built-in one.
The list is runtime-only (no flash writes: the companion re-sends it on every heartbeat)
and is cleared when the companion deregisters.
Fixes
-
Watchdog reboot on a large wall.
GET /api/flap/modulessent one chunk per
module. Each chunk could block on a slow client for up to the 3 s socket timeout, so
~41 modules × 3 s exceeded the 120 s web-stall threshold — the supervisor logged
STALL: Web=0and rebooted the gateway. It never fired below ~40 modules, which
is why it hid for so long. The response is now coalesced into ~1400-byte chunks, feeds
the watchdog on each flush, and aborts early if the client has gone away. -
A changed MQTT broker now takes effect.
POST /api/config/mqttsaved the new
host/port but never told PubSubClient about it, so the client kept dialling the
boot-time broker and failed forever withrc=-2until a reboot. It now re-points
the client and resets the failure counter. -
TCP connect timeouts were never actually set. Several call sites used
setTimeout(), which onNetworkClientsets the read timeout and leaves the
connect timeout at its 3 s default — the MQTT client and three web handlers all
meant the latter. They now callsetConnectionTimeout(). -
The dashboard re-downloaded its whole page on every navigation.
GET /now sends
anETagand answers a matchingIf-None-Matchwith304 Not Modified(~53 KB
saved per navigation); the favicon and logo are cached for a week. The dashboard also
gates its periodic polls so they can't stack up on the one-connection web server.
New
-
Tab advertisement (
POST /api/companion). The companion may now sendtabs—
the deep links its own UI offers — and the response always carriesgwTabs, this
firmware's. Each side then renders the other's real tabs instead of a list hard-coded
on the far side, which is what made the Backup tab above a two-release problem.Both halves are optional and independent, so every old/new pairing works: an older
companion advertises nothing and the dashboard falls back to its built-in companion
tabs; an older gateway returns nogwTabsand the companion falls back to its own list
(which still includes Backup, because a pre-3.4 gateway really does have that tab).An advertised list is taken whole or not at all — any malformed entry, a bad id
(not[A-Za-z0-9_-]{1,24}), a label over 24 printable ASCII characters, more than 10
tabs, or a list over 384 bytes drops the list, and the peer then shows its built-in one.
The list is runtime-only (no flash writes: the companion re-sends it on every heartbeat)
and is cleared when the companion deregisters.