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:
@@ -8,6 +8,7 @@
|
|||||||
#include "history.h"
|
#include "history.h"
|
||||||
#include "app_settings.h"
|
#include "app_settings.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
#include "web_server.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
|
||||||
AppState g_state;
|
AppState g_state;
|
||||||
@@ -67,10 +68,15 @@ void setup() {
|
|||||||
sampleNow();
|
sampleNow();
|
||||||
historyAdd(rtcEpoch(), g_state.mslHpa, g_state.tempC);
|
historyAdd(rtcEpoch(), g_state.mslHpa, g_state.tempC);
|
||||||
displayRender(g_state);
|
displayRender(g_state);
|
||||||
|
|
||||||
|
webBegin();
|
||||||
|
if (netConnected()) Serial.printf("Web UI: http://%s/\n", netIP().c_str());
|
||||||
|
|
||||||
tSample = tHistory = tFlush = millis();
|
tSample = tHistory = tFlush = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
webLoop();
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
|
|
||||||
if (now - tSample >= SAMPLE_INTERVAL_MS) {
|
if (now - tSample >= SAMPLE_INTERVAL_MS) {
|
||||||
|
|||||||
@@ -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>";
|
||||||
@@ -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(); }
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
void webBegin();
|
||||||
|
void webLoop();
|
||||||
Reference in New Issue
Block a user