2cdb7b3702
The launcher clock read UTC and sat next to the title because the toolkit could only stack children from the start of an axis. justify adds the CSS main-axis modes that had a caller -- start, end, center, between -- so a header keeps its title left and its clock right without any app doing arithmetic. Timezones are stored as POSIX TZ rules rather than offsets, so newlib applies DST changeovers and os.date() in Lua reports local time with no binding of its own. /lib/timezones.lua is only the picker list: a zone missing from it still works if its rule is written into settings, the same split themes already use. settings_calibration.lua now finds rows by label. Adding the timezone row shifted every hardcoded y coordinate in it, which is the failure that had been predicted and would have silently retargeted taps at the wrong control.
77 lines
2.3 KiB
C++
77 lines
2.3 KiB
C++
#include "net.h"
|
|
|
|
#include <WiFi.h>
|
|
#include <esp_sntp.h>
|
|
#include <sys/time.h>
|
|
|
|
#include "settings.h"
|
|
|
|
namespace {
|
|
|
|
bool synced = false;
|
|
bool wasConnected = false;
|
|
|
|
// The device cannot be running before its own firmware was compiled, so the build
|
|
// timestamp is a safe floor. Certificate validity checks fail against 1970, and this
|
|
// makes them pass before SNTP has answered. mktime reads it as UTC while the build
|
|
// machine wrote local time; a few hours of error is irrelevant for a lower bound.
|
|
time_t buildTime() {
|
|
static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
|
|
char month[4] = {__DATE__[0], __DATE__[1], __DATE__[2], '\0'};
|
|
const char* found = strstr(months, month);
|
|
struct tm parts = {};
|
|
parts.tm_mon = found ? (found - months) / 3 : 0;
|
|
parts.tm_mday = atoi(__DATE__ + 4);
|
|
parts.tm_year = atoi(__DATE__ + 7) - 1900;
|
|
parts.tm_hour = atoi(__TIME__);
|
|
parts.tm_min = atoi(__TIME__ + 3);
|
|
parts.tm_sec = atoi(__TIME__ + 6);
|
|
return mktime(&parts);
|
|
}
|
|
|
|
// Latched, because sntp_get_sync_status() reports COMPLETED only briefly before
|
|
// resetting to wait for the next cycle; polling it would flap.
|
|
void onTimeSync(struct timeval*) {
|
|
synced = true;
|
|
Serial.printf("[net] clock synced: %lu\n", (unsigned long)time(nullptr));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void net::applyTimezone() {
|
|
setenv("TZ", settings.timezone.c_str(), 1);
|
|
tzset();
|
|
}
|
|
|
|
void net::begin() {
|
|
applyTimezone();
|
|
time_t floor = buildTime();
|
|
if (time(nullptr) < floor) {
|
|
struct timeval seed = {.tv_sec = floor, .tv_usec = 0};
|
|
settimeofday(&seed, nullptr);
|
|
}
|
|
|
|
sntp_set_time_sync_notification_cb(onTimeSync);
|
|
|
|
WiFi.persistent(false);
|
|
if (settings.wifiSsid.length()) {
|
|
WiFi.mode(WIFI_STA);
|
|
WiFi.begin(settings.wifiSsid.c_str(), settings.wifiPassword.c_str());
|
|
}
|
|
}
|
|
|
|
void net::loop() {
|
|
bool connected = WiFi.status() == WL_CONNECTED;
|
|
if (connected == wasConnected) return;
|
|
wasConnected = connected;
|
|
if (!connected) return;
|
|
|
|
// Started per join rather than once at boot: a fresh lease may hand us a different
|
|
// NTP server, and the daemon re-syncs on its own hourly from here.
|
|
sntp_servermode_dhcp(1);
|
|
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC; the UI decides how to show it
|
|
Serial.println("[net] wifi up, sntp started");
|
|
}
|
|
|
|
bool net::clockSynced() { return synced; }
|