feat: HTTP server with REST API for current/history/settings

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 21:15:16 +07:00
co-authored by Claude Opus 4.8
parent ea0256a5b9
commit 396688cd98
4 changed files with 96 additions and 0 deletions
+6
View File
@@ -8,6 +8,7 @@
#include "history.h"
#include "app_settings.h"
#include "net.h"
#include "web_server.h"
#include "state.h"
AppState g_state;
@@ -67,10 +68,15 @@ void setup() {
sampleNow();
historyAdd(rtcEpoch(), g_state.mslHpa, g_state.tempC);
displayRender(g_state);
webBegin();
if (netConnected()) Serial.printf("Web UI: http://%s/\n", netIP().c_str());
tSample = tHistory = tFlush = millis();
}
void loop() {
webLoop();
unsigned long now = millis();
if (now - tSample >= SAMPLE_INTERVAL_MS) {
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include <Arduino.h>
static const char INDEX_HTML[] PROGMEM = "<!doctype html><meta charset=utf-8><title>Weather Predictor</title><h1>Weather Predictor</h1><p>See <a href=/api/current>/api/current</a></p>";
+84
View File
@@ -0,0 +1,84 @@
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>
#include "state.h"
#include "app_settings.h"
#include "history.h"
#include "forecast.h"
#include "web_page.h"
static ESP8266WebServer server(80);
static void handleRoot() {
server.send_P(200, "text/html", INDEX_HTML);
}
static void handleCurrent() {
JsonDocument doc;
char t[24];
snprintf(t, sizeof(t), "%04u-%02u-%02u %02u:%02u:%02u",
g_state.now.year, g_state.now.month, g_state.now.day,
g_state.now.hour, g_state.now.minute, g_state.now.second);
doc["time"] = t;
doc["abs"] = g_state.absHpa;
doc["msl"] = g_state.mslHpa;
doc["temp"] = g_state.tempC;
doc["trend"] = (int)g_state.trend;
doc["haveTrend"] = g_state.haveTrend;
doc["forecast"] = g_state.haveTrend ? g_state.forecast.text : "Collecting data...";
doc["category"] = g_state.haveTrend ? categoryShort(g_state.forecast.category) : "...";
String out;
serializeJson(doc, out);
server.send(200, "application/json", out);
}
static void handleHistory() {
JsonDocument doc;
JsonArray arr = doc.to<JsonArray>();
int n = historyCount();
for (int i = 0; i < n; i++) {
Sample s = historyGet(i);
JsonObject o = arr.add<JsonObject>();
o["t"] = s.epoch;
o["msl"] = s.mslHpa;
o["temp"] = s.tempC;
}
String out;
serializeJson(doc, out);
server.send(200, "application/json", out);
}
static void handleGetSettings() {
JsonDocument doc;
doc["altitude"] = settings.altitudeM;
doc["tz"] = settings.tzOffsetMin;
doc["lat"] = settings.lat;
doc["lon"] = settings.lon;
String out;
serializeJson(doc, out);
server.send(200, "application/json", out);
}
static void handlePostSettings() {
JsonDocument doc;
if (deserializeJson(doc, server.arg("plain"))) {
server.send(400, "application/json", "{\"ok\":false,\"err\":\"bad json\"}");
return;
}
settings.altitudeM = doc["altitude"] | settings.altitudeM;
settings.tzOffsetMin = doc["tz"] | settings.tzOffsetMin;
settings.lat = doc["lat"] | settings.lat;
settings.lon = doc["lon"] | settings.lon;
settingsSave();
server.send(200, "application/json", "{\"ok\":true}");
}
void webBegin() {
server.on("/", handleRoot);
server.on("/api/current", handleCurrent);
server.on("/api/history", handleHistory);
server.on("/api/settings", HTTP_GET, handleGetSettings);
server.on("/api/settings", HTTP_POST, handlePostSettings);
server.begin();
}
void webLoop() { server.handleClient(); }
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void webBegin();
void webLoop();