consolidate sketches
This commit is contained in:
11
libraries/KlubhausCore/library.properties
Normal file
11
libraries/KlubhausCore/library.properties
Normal file
@@ -0,0 +1,11 @@
|
||||
name=KlubhausCore
|
||||
version=5.1.0
|
||||
author=David
|
||||
maintainer=David
|
||||
sentence=Core logic for Klubhaus doorbell alert system
|
||||
paragraph=Shared state machine, ntfy.sh polling, networking, and abstract display interface.
|
||||
category=Other
|
||||
url=https://git.notsosm.art/david/klubhaus-doorbell
|
||||
architectures=esp32
|
||||
includes=KlubhausCore.h
|
||||
depends=ArduinoJson,NTPClient
|
||||
26
libraries/KlubhausCore/src/Config.h
Normal file
26
libraries/KlubhausCore/src/Config.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#define FW_VERSION "5.1"
|
||||
|
||||
// ── ntfy.sh ──
|
||||
#define NTFY_SERVER "ntfy.sh"
|
||||
#define ALERT_TOPIC "ALERT_klubhaus_topic"
|
||||
#define SILENCE_TOPIC "SILENCE_klubhaus_topic"
|
||||
#define ADMIN_TOPIC "ADMIN_klubhaus_topic"
|
||||
#define STATUS_TOPIC "STATUS_klubhaus_topic"
|
||||
|
||||
// ── Timing ──
|
||||
#define POLL_INTERVAL_MS 15000
|
||||
#define HEARTBEAT_INTERVAL_MS 300000
|
||||
#define BOOT_GRACE_MS 5000
|
||||
#define HOLD_TO_SILENCE_MS 3000
|
||||
#define ALERT_TIMEOUT_MS 120000
|
||||
#define SILENCE_DISPLAY_MS 10000
|
||||
#define WIFI_CONNECT_TIMEOUT_MS 15000
|
||||
#define HTTP_TIMEOUT_MS 10000
|
||||
|
||||
// ── WiFi credential struct (populated in each board's secrets.h) ──
|
||||
struct WiFiCred {
|
||||
const char* ssid;
|
||||
const char* pass;
|
||||
};
|
||||
24
libraries/KlubhausCore/src/DisplayManager.h
Normal file
24
libraries/KlubhausCore/src/DisplayManager.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "IDisplayDriver.h"
|
||||
|
||||
/// Owns a pointer to the concrete driver; all calls delegate.
|
||||
/// Board sketch creates the concrete driver and passes it in.
|
||||
class DisplayManager {
|
||||
public:
|
||||
DisplayManager() : _drv(nullptr) {}
|
||||
explicit DisplayManager(IDisplayDriver* drv) : _drv(drv) {}
|
||||
void setDriver(IDisplayDriver* drv) { _drv = drv; }
|
||||
|
||||
void begin() { if (_drv) _drv->begin(); }
|
||||
void setBacklight(bool on) { if (_drv) _drv->setBacklight(on); }
|
||||
void render(const ScreenState& st) { if (_drv) _drv->render(st); }
|
||||
TouchEvent readTouch() { return _drv ? _drv->readTouch() : TouchEvent{}; }
|
||||
int dashboardTouch(int x, int y) { return _drv ? _drv->dashboardTouch(x, y) : -1; }
|
||||
HoldState updateHold(unsigned long ms) { return _drv ? _drv->updateHold(ms) : HoldState{}; }
|
||||
void updateHint() { if (_drv) _drv->updateHint(); }
|
||||
int width() { return _drv ? _drv->width() : 0; }
|
||||
int height() { return _drv ? _drv->height() : 0; }
|
||||
|
||||
private:
|
||||
IDisplayDriver* _drv;
|
||||
};
|
||||
260
libraries/KlubhausCore/src/DoorbellLogic.cpp
Normal file
260
libraries/KlubhausCore/src/DoorbellLogic.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
#include "DoorbellLogic.h"
|
||||
|
||||
DoorbellLogic::DoorbellLogic(DisplayManager* display)
|
||||
: _display(display) {}
|
||||
|
||||
// ── URL builder ─────────────────────────────────────────────
|
||||
|
||||
String DoorbellLogic::topicUrl(const char* base) {
|
||||
String suffix = _debug ? "_test" : "";
|
||||
return String("https://") + NTFY_SERVER + "/" + base + suffix
|
||||
+ "/json?since=20s&poll=1";
|
||||
}
|
||||
|
||||
// ── Lifecycle ───────────────────────────────────────────────
|
||||
|
||||
void DoorbellLogic::begin(const char* version, const char* boardName,
|
||||
const WiFiCred* creds, int credCount) {
|
||||
_version = version;
|
||||
_board = boardName;
|
||||
#ifdef DEBUG_MODE
|
||||
_debug = true;
|
||||
#endif
|
||||
|
||||
Serial.println(F("========================================"));
|
||||
Serial.printf( " KLUBHAUS ALERT v%s — %s\n", _version, _board);
|
||||
if (_debug) Serial.println(F(" *** DEBUG MODE — _test topics ***"));
|
||||
Serial.println(F("========================================\n"));
|
||||
|
||||
// Display
|
||||
_display->begin();
|
||||
|
||||
// Network
|
||||
_net.begin(creds, credCount);
|
||||
|
||||
if (_net.isConnected()) {
|
||||
_net.syncNTP();
|
||||
Serial.printf("[NET] WiFi:%s RSSI:%d IP:%s\n",
|
||||
_net.getSSID().c_str(), _net.getRSSI(),
|
||||
_net.getIP().c_str());
|
||||
_net.dnsCheck(NTFY_SERVER);
|
||||
_net.tlsCheck(NTFY_SERVER);
|
||||
}
|
||||
|
||||
// Topic URLs
|
||||
_alertUrl = topicUrl(ALERT_TOPIC);
|
||||
_silenceUrl = topicUrl(SILENCE_TOPIC);
|
||||
_adminUrl = topicUrl(ADMIN_TOPIC);
|
||||
String sfx = _debug ? "_test" : "";
|
||||
_statusUrl = String("https://") + NTFY_SERVER + "/" + STATUS_TOPIC + sfx;
|
||||
|
||||
Serial.printf("[CONFIG] ALERT_URL: %s\n", _alertUrl.c_str());
|
||||
Serial.printf("[CONFIG] SILENCE_URL: %s\n", _silenceUrl.c_str());
|
||||
Serial.printf("[CONFIG] ADMIN_URL: %s\n", _adminUrl.c_str());
|
||||
|
||||
// Boot status
|
||||
flushStatus(String("BOOTED — ") + _net.getSSID() + " "
|
||||
+ _net.getIP() + " RSSI:" + String(_net.getRSSI()));
|
||||
}
|
||||
|
||||
void DoorbellLogic::finishBoot() {
|
||||
transition(DeviceState::SILENT);
|
||||
_state.screen = ScreenID::OFF;
|
||||
_display->setBacklight(false);
|
||||
_state.backlightOn = false;
|
||||
_bootGraceEnd = millis() + BOOT_GRACE_MS;
|
||||
Serial.printf("[BOOT] Grace period: %d ms\n", BOOT_GRACE_MS);
|
||||
Serial.printf("[BOOT] Ready — monitoring %s\n", NTFY_SERVER);
|
||||
}
|
||||
|
||||
// ── Main loop tick ──────────────────────────────────────────
|
||||
|
||||
void DoorbellLogic::update() {
|
||||
uint32_t now = millis();
|
||||
_state.uptimeMs = now;
|
||||
|
||||
if (_net.isConnected()) {
|
||||
_state.wifiRssi = _net.getRSSI();
|
||||
_state.wifiSsid = _net.getSSID();
|
||||
_state.ipAddr = _net.getIP();
|
||||
}
|
||||
if (!_net.checkConnection()) return;
|
||||
|
||||
// Poll
|
||||
if (now - _lastPollMs >= POLL_INTERVAL_MS) {
|
||||
_lastPollMs = now;
|
||||
pollTopics();
|
||||
}
|
||||
|
||||
// Heartbeat
|
||||
if (now - _lastHeartbeatMs >= HEARTBEAT_INTERVAL_MS) {
|
||||
_lastHeartbeatMs = now;
|
||||
heartbeat();
|
||||
}
|
||||
|
||||
// Auto-transitions
|
||||
switch (_state.deviceState) {
|
||||
case DeviceState::ALERTING:
|
||||
if (now - _state.alertStartMs > ALERT_TIMEOUT_MS) {
|
||||
Serial.println("[STATE] Alert timed out → SILENT");
|
||||
transition(DeviceState::SILENT);
|
||||
_state.screen = ScreenID::OFF;
|
||||
_display->setBacklight(false);
|
||||
_state.backlightOn = false;
|
||||
}
|
||||
break;
|
||||
case DeviceState::SILENCED:
|
||||
if (now - _state.silenceStartMs > SILENCE_DISPLAY_MS) {
|
||||
Serial.println("[STATE] Silence display done → SILENT");
|
||||
transition(DeviceState::SILENT);
|
||||
_state.screen = ScreenID::OFF;
|
||||
_display->setBacklight(false);
|
||||
_state.backlightOn = false;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Polling ─────────────────────────────────────────────────
|
||||
|
||||
void DoorbellLogic::pollTopics() {
|
||||
pollTopic(_alertUrl, "ALERT");
|
||||
pollTopic(_silenceUrl, "SILENCE");
|
||||
pollTopic(_adminUrl, "ADMIN");
|
||||
_state.lastPollMs = millis();
|
||||
}
|
||||
|
||||
void DoorbellLogic::pollTopic(const String& url, const char* label) {
|
||||
String body;
|
||||
int code = _net.httpGet(url.c_str(), body);
|
||||
if (code != 200 || body.length() == 0) return;
|
||||
|
||||
// ntfy returns newline-delimited JSON
|
||||
int pos = 0;
|
||||
while (pos < (int)body.length()) {
|
||||
int nl = body.indexOf('\n', pos);
|
||||
if (nl < 0) nl = body.length();
|
||||
String line = body.substring(pos, nl);
|
||||
line.trim();
|
||||
pos = nl + 1;
|
||||
if (line.length() == 0) continue;
|
||||
|
||||
JsonDocument doc;
|
||||
if (deserializeJson(doc, line)) continue;
|
||||
|
||||
const char* evt = doc["event"] | "";
|
||||
if (strcmp(evt, "message") != 0) continue;
|
||||
|
||||
const char* title = doc["title"] | "";
|
||||
const char* message = doc["message"] | "";
|
||||
Serial.printf("[%s] title=\"%s\" message=\"%s\"\n",
|
||||
label, title, message);
|
||||
|
||||
if (strcmp(label, "ALERT") == 0) onAlert(String(title), String(message));
|
||||
else if (strcmp(label, "SILENCE") == 0) onSilence();
|
||||
else if (strcmp(label, "ADMIN") == 0) onAdmin(String(message));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Event handlers ──────────────────────────────────────────
|
||||
|
||||
void DoorbellLogic::onAlert(const String& title, const String& body) {
|
||||
if (millis() < _bootGraceEnd) {
|
||||
Serial.println("[ALERT] Ignored (boot grace)");
|
||||
return;
|
||||
}
|
||||
Serial.printf("[ALERT] %s: %s\n", title.c_str(), body.c_str());
|
||||
_state.alertTitle = title;
|
||||
_state.alertBody = body;
|
||||
_state.alertStartMs = millis();
|
||||
transition(DeviceState::ALERTING);
|
||||
_state.screen = ScreenID::ALERT;
|
||||
_display->setBacklight(true);
|
||||
_state.backlightOn = true;
|
||||
flushStatus("ALERT: " + title);
|
||||
}
|
||||
|
||||
void DoorbellLogic::onSilence() {
|
||||
if (_state.deviceState != DeviceState::ALERTING) return;
|
||||
Serial.println("[SILENCE] Alert silenced");
|
||||
_state.silenceStartMs = millis();
|
||||
transition(DeviceState::SILENCED);
|
||||
_state.screen = ScreenID::OFF;
|
||||
_display->setBacklight(false);
|
||||
_state.backlightOn = false;
|
||||
flushStatus("SILENCED");
|
||||
}
|
||||
|
||||
void DoorbellLogic::silenceAlert() { onSilence(); }
|
||||
|
||||
void DoorbellLogic::onAdmin(const String& cmd) {
|
||||
Serial.printf("[ADMIN] %s\n", cmd.c_str());
|
||||
if (cmd == "reboot") {
|
||||
flushStatus("REBOOTING (admin)");
|
||||
delay(500);
|
||||
ESP.restart();
|
||||
} else if (cmd == "dashboard") {
|
||||
_state.screen = ScreenID::DASHBOARD;
|
||||
_display->setBacklight(true);
|
||||
_state.backlightOn = true;
|
||||
} else if (cmd == "off") {
|
||||
_state.screen = ScreenID::OFF;
|
||||
_display->setBacklight(false);
|
||||
_state.backlightOn = false;
|
||||
} else if (cmd == "status") {
|
||||
heartbeat(); // re-uses heartbeat message format
|
||||
}
|
||||
}
|
||||
|
||||
// ── Status / heartbeat ─────────────────────────────────────
|
||||
|
||||
void DoorbellLogic::flushStatus(const String& message) {
|
||||
Serial.printf("[STATUS] Queued: %s\n", message.c_str());
|
||||
String full = String(_board) + " " + message;
|
||||
int code = _net.httpPost(_statusUrl.c_str(), full);
|
||||
Serial.printf("[STATUS] Sent (%d): %s\n", code, message.c_str());
|
||||
_state.lastHeartbeatMs = millis();
|
||||
}
|
||||
|
||||
void DoorbellLogic::heartbeat() {
|
||||
String m = String("HEARTBEAT ") + deviceStateStr(_state.deviceState)
|
||||
+ " up:" + String(millis() / 1000) + "s"
|
||||
+ " RSSI:" + String(_net.getRSSI())
|
||||
+ " heap:" + String(ESP.getFreeHeap());
|
||||
#ifdef BOARD_HAS_PSRAM
|
||||
m += " psram:" + String(ESP.getFreePsram());
|
||||
#endif
|
||||
flushStatus(m);
|
||||
}
|
||||
|
||||
void DoorbellLogic::transition(DeviceState s) {
|
||||
Serial.printf("-> %s\n", deviceStateStr(s));
|
||||
_state.deviceState = s;
|
||||
}
|
||||
|
||||
// ── Serial console ──────────────────────────────────────────
|
||||
|
||||
void DoorbellLogic::onSerialCommand(const String& cmd) {
|
||||
Serial.printf("[CMD] %s\n", cmd.c_str());
|
||||
if (cmd == "alert") onAlert("Test Alert", "Serial test");
|
||||
else if (cmd == "silence") onSilence();
|
||||
else if (cmd == "reboot") ESP.restart();
|
||||
else if (cmd == "dashboard") onAdmin("dashboard");
|
||||
else if (cmd == "off") onAdmin("off");
|
||||
else if (cmd == "status") {
|
||||
Serial.printf("[STATE] %s screen:%s bl:%s\n",
|
||||
deviceStateStr(_state.deviceState),
|
||||
screenIdStr(_state.screen),
|
||||
_state.backlightOn ? "ON" : "OFF");
|
||||
Serial.printf("[MEM] heap:%d", ESP.getFreeHeap());
|
||||
#ifdef BOARD_HAS_PSRAM
|
||||
Serial.printf(" psram:%d", ESP.getFreePsram());
|
||||
#endif
|
||||
Serial.println();
|
||||
Serial.printf("[NET] %s RSSI:%d IP:%s\n",
|
||||
_state.wifiSsid.c_str(), _state.wifiRssi,
|
||||
_state.ipAddr.c_str());
|
||||
}
|
||||
else Serial.println(F("[CMD] alert|silence|reboot|dashboard|off|status"));
|
||||
}
|
||||
55
libraries/KlubhausCore/src/DoorbellLogic.h
Normal file
55
libraries/KlubhausCore/src/DoorbellLogic.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "Config.h"
|
||||
#include "ScreenState.h"
|
||||
#include "DisplayManager.h"
|
||||
#include "NetManager.h"
|
||||
|
||||
class DoorbellLogic {
|
||||
public:
|
||||
explicit DoorbellLogic(DisplayManager* display);
|
||||
|
||||
/// Call from setup(). Pass board-specific WiFi creds.
|
||||
void begin(const char* version, const char* boardName,
|
||||
const WiFiCred* creds, int credCount);
|
||||
/// Call from loop() — polls topics, runs timers, transitions state.
|
||||
void update();
|
||||
/// Transition out of BOOTED → SILENT. Call at end of setup().
|
||||
void finishBoot();
|
||||
/// Serial debug console.
|
||||
void onSerialCommand(const String& cmd);
|
||||
|
||||
const ScreenState& getScreenState() const { return _state; }
|
||||
|
||||
/// Externally trigger silence (e.g. hold-to-silence gesture).
|
||||
void silenceAlert();
|
||||
|
||||
private:
|
||||
void pollTopics();
|
||||
void pollTopic(const String& url, const char* label);
|
||||
void onAlert(const String& title, const String& body);
|
||||
void onSilence();
|
||||
void onAdmin(const String& command);
|
||||
void flushStatus(const String& message);
|
||||
void heartbeat();
|
||||
void transition(DeviceState s);
|
||||
String topicUrl(const char* base);
|
||||
|
||||
DisplayManager* _display;
|
||||
NetManager _net;
|
||||
ScreenState _state;
|
||||
|
||||
const char* _version = "";
|
||||
const char* _board = "";
|
||||
bool _debug = false;
|
||||
|
||||
uint32_t _lastPollMs = 0;
|
||||
uint32_t _lastHeartbeatMs = 0;
|
||||
uint32_t _bootGraceEnd = 0;
|
||||
|
||||
String _alertUrl;
|
||||
String _silenceUrl;
|
||||
String _adminUrl;
|
||||
String _statusUrl;
|
||||
};
|
||||
38
libraries/KlubhausCore/src/IDisplayDriver.h
Normal file
38
libraries/KlubhausCore/src/IDisplayDriver.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "ScreenState.h"
|
||||
|
||||
struct TouchEvent {
|
||||
bool pressed = false;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
};
|
||||
|
||||
struct HoldState {
|
||||
bool active = false;
|
||||
bool completed = false;
|
||||
float progress = 0.0f; // 0.0 – 1.0
|
||||
};
|
||||
|
||||
/// Abstract display driver — implemented per-board.
|
||||
class IDisplayDriver {
|
||||
public:
|
||||
virtual ~IDisplayDriver() = default;
|
||||
|
||||
virtual void begin() = 0;
|
||||
virtual void setBacklight(bool on) = 0;
|
||||
|
||||
/// Called every loop() iteration; draw the screen described by `state`.
|
||||
virtual void render(const ScreenState& state) = 0;
|
||||
|
||||
// ── Touch ──
|
||||
virtual TouchEvent readTouch() = 0;
|
||||
/// Returns tile index at (x,y), or -1 if none.
|
||||
virtual int dashboardTouch(int x, int y) = 0;
|
||||
/// Track a long-press gesture; returns progress/completion.
|
||||
virtual HoldState updateHold(unsigned long holdMs) = 0;
|
||||
/// Idle hint animation (e.g. pulsing ring) while alert is showing.
|
||||
virtual void updateHint() = 0;
|
||||
|
||||
virtual int width() = 0;
|
||||
virtual int height() = 0;
|
||||
};
|
||||
9
libraries/KlubhausCore/src/KlubhausCore.h
Normal file
9
libraries/KlubhausCore/src/KlubhausCore.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
// Umbrella header — board sketches just #include <KlubhausCore.h>
|
||||
#include "Config.h"
|
||||
#include "ScreenState.h"
|
||||
#include "IDisplayDriver.h"
|
||||
#include "DisplayManager.h"
|
||||
#include "NetManager.h"
|
||||
#include "DoorbellLogic.h"
|
||||
102
libraries/KlubhausCore/src/NetManager.cpp
Normal file
102
libraries/KlubhausCore/src/NetManager.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "NetManager.h"
|
||||
|
||||
// ── WiFi ────────────────────────────────────────────────────
|
||||
|
||||
void NetManager::begin(const WiFiCred* creds, int count) {
|
||||
WiFi.mode(WIFI_STA);
|
||||
for (int i = 0; i < count; i++)
|
||||
_multi.addAP(creds[i].ssid, creds[i].pass);
|
||||
|
||||
Serial.println("[WIFI] Connecting...");
|
||||
unsigned long t0 = millis();
|
||||
while (_multi.run() != WL_CONNECTED) {
|
||||
if (millis() - t0 > WIFI_CONNECT_TIMEOUT_MS) {
|
||||
Serial.println("[WIFI] Timeout!");
|
||||
return;
|
||||
}
|
||||
delay(250);
|
||||
}
|
||||
Serial.printf("[WIFI] Connected: %s %s\n",
|
||||
getSSID().c_str(), getIP().c_str());
|
||||
}
|
||||
|
||||
bool NetManager::checkConnection() {
|
||||
if (WiFi.status() == WL_CONNECTED) return true;
|
||||
Serial.println("[WIFI] Reconnecting...");
|
||||
return _multi.run(WIFI_CONNECT_TIMEOUT_MS) == WL_CONNECTED;
|
||||
}
|
||||
|
||||
bool NetManager::isConnected() { return WiFi.status() == WL_CONNECTED; }
|
||||
String NetManager::getSSID() { return WiFi.SSID(); }
|
||||
String NetManager::getIP() { return WiFi.localIP().toString(); }
|
||||
int NetManager::getRSSI() { return WiFi.RSSI(); }
|
||||
|
||||
// ── NTP ─────────────────────────────────────────────────────
|
||||
|
||||
bool NetManager::syncNTP() {
|
||||
Serial.println("[NTP] Starting sync...");
|
||||
if (!_ntp) _ntp = new NTPClient(_udp, "pool.ntp.org", 0, 60000);
|
||||
_ntp->begin();
|
||||
_ntp->forceUpdate();
|
||||
_ntpReady = _ntp->isTimeSet();
|
||||
Serial.printf("[NTP] %s: %s UTC\n",
|
||||
_ntpReady ? "Synced" : "FAILED",
|
||||
_ntpReady ? _ntp->getFormattedTime().c_str() : "--");
|
||||
return _ntpReady;
|
||||
}
|
||||
|
||||
String NetManager::getTimeStr() {
|
||||
return (_ntp && _ntpReady) ? _ntp->getFormattedTime() : "??:??:??";
|
||||
}
|
||||
|
||||
// ── Diagnostics ─────────────────────────────────────────────
|
||||
|
||||
bool NetManager::dnsCheck(const char* host) {
|
||||
IPAddress ip;
|
||||
bool ok = WiFi.hostByName(host, ip);
|
||||
Serial.printf("[NET] DNS %s: %s\n", ok ? "OK" : "FAIL",
|
||||
ok ? ip.toString().c_str() : "");
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool NetManager::tlsCheck(const char* host) {
|
||||
WiFiClientSecure c;
|
||||
c.setInsecure();
|
||||
c.setTimeout(HTTP_TIMEOUT_MS);
|
||||
bool ok = c.connect(host, 443);
|
||||
if (ok) c.stop();
|
||||
Serial.printf("[NET] TLS %s\n", ok ? "OK" : "FAIL");
|
||||
return ok;
|
||||
}
|
||||
|
||||
// ── HTTP helpers ────────────────────────────────────────────
|
||||
|
||||
int NetManager::httpGet(const char* url, String& response) {
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
client.setTimeout(HTTP_TIMEOUT_MS);
|
||||
|
||||
HTTPClient http;
|
||||
http.setTimeout(HTTP_TIMEOUT_MS);
|
||||
http.begin(client, url);
|
||||
|
||||
int code = http.GET();
|
||||
if (code > 0) response = http.getString();
|
||||
http.end();
|
||||
return code;
|
||||
}
|
||||
|
||||
int NetManager::httpPost(const char* url, const String& body) {
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
client.setTimeout(HTTP_TIMEOUT_MS);
|
||||
|
||||
HTTPClient http;
|
||||
http.setTimeout(HTTP_TIMEOUT_MS);
|
||||
http.begin(client, url);
|
||||
http.addHeader("Content-Type", "text/plain");
|
||||
|
||||
int code = http.POST(body);
|
||||
http.end();
|
||||
return code;
|
||||
}
|
||||
37
libraries/KlubhausCore/src/NetManager.h
Normal file
37
libraries/KlubhausCore/src/NetManager.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <NTPClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include "Config.h"
|
||||
|
||||
class NetManager {
|
||||
public:
|
||||
void begin(const WiFiCred* creds, int count);
|
||||
bool checkConnection();
|
||||
bool isConnected();
|
||||
|
||||
String getSSID();
|
||||
String getIP();
|
||||
int getRSSI();
|
||||
|
||||
bool syncNTP();
|
||||
String getTimeStr();
|
||||
|
||||
bool dnsCheck(const char* host);
|
||||
bool tlsCheck(const char* host);
|
||||
|
||||
/// HTTPS GET; writes body into `response`. Returns HTTP status code.
|
||||
int httpGet(const char* url, String& response);
|
||||
/// HTTPS POST with text/plain body. Returns HTTP status code.
|
||||
int httpPost(const char* url, const String& body);
|
||||
|
||||
private:
|
||||
WiFiMulti _multi;
|
||||
WiFiUDP _udp;
|
||||
NTPClient* _ntp = nullptr;
|
||||
bool _ntpReady = false;
|
||||
};
|
||||
54
libraries/KlubhausCore/src/ScreenState.h
Normal file
54
libraries/KlubhausCore/src/ScreenState.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
enum class DeviceState {
|
||||
BOOTED,
|
||||
SILENT,
|
||||
ALERTING,
|
||||
SILENCED
|
||||
};
|
||||
|
||||
enum class ScreenID {
|
||||
BOOT,
|
||||
OFF,
|
||||
ALERT,
|
||||
DASHBOARD
|
||||
};
|
||||
|
||||
struct ScreenState {
|
||||
DeviceState deviceState = DeviceState::BOOTED;
|
||||
ScreenID screen = ScreenID::BOOT;
|
||||
|
||||
String alertTitle;
|
||||
String alertBody;
|
||||
uint32_t alertStartMs = 0;
|
||||
uint32_t silenceStartMs = 0;
|
||||
|
||||
bool backlightOn = false;
|
||||
int wifiRssi = 0;
|
||||
String wifiSsid;
|
||||
String ipAddr;
|
||||
uint32_t uptimeMs = 0;
|
||||
uint32_t lastPollMs = 0;
|
||||
uint32_t lastHeartbeatMs= 0;
|
||||
};
|
||||
|
||||
inline const char* deviceStateStr(DeviceState s) {
|
||||
switch (s) {
|
||||
case DeviceState::BOOTED: return "BOOTED";
|
||||
case DeviceState::SILENT: return "SILENT";
|
||||
case DeviceState::ALERTING: return "ALERTING";
|
||||
case DeviceState::SILENCED: return "SILENCED";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
inline const char* screenIdStr(ScreenID s) {
|
||||
switch (s) {
|
||||
case ScreenID::BOOT: return "BOOT";
|
||||
case ScreenID::OFF: return "OFF";
|
||||
case ScreenID::ALERT: return "ALERT";
|
||||
case ScreenID::DASHBOARD: return "DASHBOARD";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
Reference in New Issue
Block a user