The TIME_ZONES table predates several tz releases, so 32 zones give the
wrong UTC offset today or within the next year when the device applies
the string with setenv("TZ"): Mexico (no DST since 2022), Egypt (DST
again since 2023), Kazakhstan, Morocco, Paraguay, Iran, Jordan, Syria,
Greenland, and from 1 November 2026 British Columbia, Alberta, the
Northwest Territories and Manitoba (permanent UTC-7, -6, -6 and -5).
Each value is now the POSIX TZ footer of the zone's TZif file in IANA
tz 2026e (PyPI tzdata 2026.5). The keys and their order are unchanged.
"UNK" abbreviations become the numeric <+01> form, which newlib accepts.
America/Godthab and America/Scoresbysund still show summer time from
1 January to the last Sunday in March on newlib: their rule time is
negative (M3.5.0/-1) and newlib's tzset reads it as unsigned. That is
a newlib bug, not a table error.
uploadComplete() leaves _is_firmware / _is_filesystem set after a
successful upload. When the client then closes the connection,
handleDisconnect() sees the flags, treats it as an aborted upload and
sets the status back to NORMAL, cancelling the pending restart. The WebUI
gets away with it because its status poll turns the pending restart into
a requested one first; an upload from curl or a script is accepted
(md5_ok) but the device keeps running the old firmware until something
else restarts it.
Clear the flags once the upload has completed.
A restart requested from the WebUI (Restart button, firmware upload,
firmware update from URL, settings or customization upload) is set to
PENDING_RESTART and was only escalated to RESTART_REQUESTED when the
WebUI's SystemMonitor polled /rest/systemStatus. If the tab was closed,
nothing ever escalated it and the generic 5 minute status timeout reset
it back to NORMAL, so the restart was dropped and an uploaded firmware
was never booted.
- System::loop() now escalates a pending restart itself if it hasn't been
picked up within 10 seconds (the SystemMonitor polls every second)
- the generic status reset timeout skips PENDING_RESTART, and its timer is
cleared whenever the status is back to normal, so a start time left over
from an earlier status can't reset a later one (e.g. a pending restart
right after a firmware download) immediately