-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsettings.cpp
More file actions
158 lines (147 loc) · 6.73 KB
/
Copy pathsettings.cpp
File metadata and controls
158 lines (147 loc) · 6.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/* settings.cpp — NVS-backed configuration + time helpers. */
#include "settings.h"
#include "config.h" // WEB_PASS_DEFAULT
#include <Preferences.h>
#include <time.h>
#include <sys/time.h>
#include <WiFi.h>
#include <esp_random.h> // hardware RNG for the first-boot web password
#include "rtc.h"
#include "display.h" // Display::lock() to fence flash writes
#include "lwip/priv/tcpip_priv.h" // SNTP (configTzTime) also uses raw lwIP udp
#if defined(LWIP_TCPIP_CORE_LOCKING) && LWIP_TCPIP_CORE_LOCKING
#define HM_LOCK_TCPIP() LOCK_TCPIP_CORE()
#define HM_UNLOCK_TCPIP() UNLOCK_TCPIP_CORE()
#else
#define HM_LOCK_TCPIP()
#define HM_UNLOCK_TCPIP()
#endif
namespace {
Preferences prefs;
WebhookCfg g_webhook;
WifiCfg g_wifi;
Defaults g_def;
WebAuthCfg g_auth;
// Webhook body format, in its OWN NVS key so adding it doesn't resize the WebhookCfg
// blob (which would reset a configured webhook on upgrade): 0 = native HostMonitor
// schema, 1 = M5Stack /api/alerts/inject schema {slug,key,value,severity}.
uint8_t g_whFmt = 0;
char g_whSlug[16] = ""; // m5inject slug; empty = fall back to DEVICE_NAME
char g_buf[24];
const char* g_src="runtime";
}
static void loadBlob(const char* k, void* p, size_t n){
// Only accept a stored blob whose size EXACTLY matches the current struct. If the
// layout changed across a firmware update — e.g. removing the SSL check shrank
// Defaults::interval[] from 6 to 5 — an old, larger blob would be copied in
// misaligned and corrupt every field after the change. On a size mismatch we keep
// the compiled-in defaults instead (the user re-saves Settings if they'd customised).
if(prefs.getBytesLength(k) != n) return;
prefs.getBytes(k, p, n);
}
static void genPassword(char* out, size_t n){
// Unambiguous alphabet (no 0/O/1/l/I) so it's easy to read off the LCD.
static const char cs[]="ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789";
size_t L=12; if(L>n-1) L=n-1;
for(size_t i=0;i<L;i++) out[i]=cs[esp_random() % (sizeof(cs)-1)];
out[L]=0;
}
void Settings::begin(){
prefs.begin("hostmon", false);
loadBlob("webhook", &g_webhook, sizeof(g_webhook));
loadBlob("wifi", &g_wifi, sizeof(g_wifi));
loadBlob("def", &g_def, sizeof(g_def));
loadBlob("auth", &g_auth, sizeof(g_auth));
loadBlob("whfmt", &g_whFmt, sizeof(g_whFmt)); // own key -> no WebhookCfg resize
prefs.getString("whslug", g_whSlug, sizeof(g_whSlug));
}
void Settings::ensureWebPassword(){
// First boot (or after a flash erase): the password is still the compile-time
// default. Replace it with a random per-device one so no two units ship with the
// same known credential. It's shown on the Alerts/Setup card until the user sets
// their own.
//
// IMPORTANT: this MUST run AFTER Wi-Fi is started — esp_random() only produces true
// hardware-random output once the RF subsystem is enabled; before that it's a weaker
// PRNG, which would make this credential more predictable. Still called before
// Display::begin(), so the NVS write stays panel-safe (flash write w/ cache enabled).
if(strcmp(g_auth.pass, WEB_PASS_DEFAULT)==0){
genPassword(g_auth.pass, sizeof(g_auth.pass));
g_auth.autoGen = true;
prefs.putBytes("auth", &g_auth, sizeof(g_auth));
}
}
void Settings::save(){
// Fence the NVS (flash) writes behind the LVGL lock so the RGB panel isn't
// reading PSRAM through a disabled cache during them (see csv.cpp saveHosts).
Display::lock();
prefs.putBytes("webhook", &g_webhook, sizeof(g_webhook));
prefs.putBytes("wifi", &g_wifi, sizeof(g_wifi));
prefs.putBytes("def", &g_def, sizeof(g_def));
prefs.putBytes("auth", &g_auth, sizeof(g_auth));
prefs.putBytes("whfmt", &g_whFmt, sizeof(g_whFmt));
prefs.putString("whslug", g_whSlug);
Display::unlock();
}
WebhookCfg& Settings::webhook(){ return g_webhook; }
uint8_t& Settings::webhookFormat(){ return g_whFmt; } // 0=native, 1=m5inject
char* Settings::webhookSlug(){ return g_whSlug; }
WifiCfg& Settings::wifi(){ return g_wifi; }
WebAuthCfg& Settings::auth(){ return g_auth; }
Defaults& Settings::defaults(){ return g_def; }
void Settings::startClock(){
// Boot-time: seed the system clock from the external RTC if it holds a
// valid time, so the LCD shows the right time before any network is up.
Rtc::begin();
struct tm tm;
if(Rtc::get(tm)){
time_t t=mktime(&tm); struct timeval tv={ .tv_sec=t, .tv_usec=0 };
settimeofday(&tv, nullptr); g_src="rtc";
Serial.println("[time] seeded from RTC");
} else {
g_src="runtime";
}
// Release the shared I2C pins to the display panel BEFORE it initialises.
// (Wire = new driver; panel = legacy driver — they can't share a port.)
Rtc::releaseBus();
}
void Settings::startTime(){
// Network is up: try NTP. On success, also persist the time to the RTC so it
// survives reboots/power loss. If NTP fails, keep whatever the RTC/runtime
// clock already provides.
if(WiFi.status()==WL_CONNECTED){
// configTzTime()/esp_sntp manage their own tcpip-thread locking; holding
// LOCK_TCPIP_CORE() here deadlocks SNTP init (same class of bug as AsyncUDP).
configTzTime(NTP_TZ, NTP_SERVER_1, NTP_SERVER_2);
struct tm tm;
if(getLocalTime(&tm, NTP_TIMEOUT_MS)){
g_src="ntp";
if(Rtc::present()){ Rtc::set(tm); Serial.println("[time] NTP ok -> RTC updated"); }
else Serial.println("[time] NTP ok (no RTC to persist)");
return;
}
Serial.println("[time] NTP unavailable");
}
if(Rtc::present()){ struct tm tm; if(Rtc::get(tm)) g_src="rtc"; }
}
const char* Settings::timeSource(){ return g_src; }
static struct tm nowTm(){
time_t t = time(nullptr);
struct tm tm; localtime_r(&t, &tm); return tm;
}
const char* Settings::clockHHMM(){ struct tm tm=nowTm(); snprintf(g_buf,sizeof(g_buf),"%02d:%02d",tm.tm_hour,tm.tm_min); return g_buf; }
const char* Settings::dateShort(){
static const char* W[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static const char* Mn[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
struct tm tm=nowTm(); snprintf(g_buf,sizeof(g_buf),"%s %d %s",W[tm.tm_wday],tm.tm_mday,Mn[tm.tm_mon]); return g_buf;
}
const char* Settings::dateLongUpper(){
static const char* W[]={"SUN","MON","TUE","WED","THU","FRI","SAT"};
static const char* Mn[]={"JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"};
struct tm tm=nowTm(); snprintf(g_buf,sizeof(g_buf),"%s %02d %s",W[tm.tm_wday],tm.tm_mday,Mn[tm.tm_mon]); return g_buf;
}
void Settings::isoNow(char* out, size_t n){
struct tm tm=nowTm();
snprintf(out,n,"%04d-%02d-%02dT%02d:%02d:%02dZ",
tm.tm_year+1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
}