Phase 0/2: API layer kept 1:1, only secrets.h include path fixed

http_client/weather_api/calendar_api/departures_api are hardware-
independent (HTTP GET + ArduinoJson parsing, same endpoints, same
JSON). Kept verbatim per PLAN.md migration strategy; only changed
#include "../../include/secrets.h" -> #include <secrets.h> to match
the PlatformIO include/ layout (no functional change).
This commit is contained in:
2026-08-02 10:28:56 +02:00
parent abb5c62f3b
commit 59198284fd
8 changed files with 204 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
#include "calendar_api.h"
#include "http_client.h"
#include <secrets.h>
#include <ArduinoJson.h>
CalendarData fetchCalendar() {
CalendarData data;
ApiResult res = httpGet(CALENDAR_API_URL);
if (!res.success) { data.valid = false; return data; }
DynamicJsonDocument doc(8192);
if (deserializeJson(doc, res.body)) { data.valid = false; return data; }
JsonArray events = doc["events"];
for (JsonObject e : events) {
CalendarEvent ev;
ev.id = e["id"] | 0;
ev.summary = e["summary"] | "";
ev.startAt = e["start_at"] | "";
ev.endAt = e["end_at"] | "";
ev.allDay = e["all_day"] | false;
ev.status = e["status"] | "";
data.events.push_back(ev);
}
data.valid = true;
return data;
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <Arduino.h>
#include <vector>
struct CalendarEvent {
long id = 0;
String summary;
String startAt;
String endAt;
bool allDay = false;
String status;
};
struct CalendarData {
bool valid = false;
std::vector<CalendarEvent> events;
};
CalendarData fetchCalendar();
+30
View File
@@ -0,0 +1,30 @@
#include "departures_api.h"
#include "http_client.h"
#include <secrets.h>
#include <ArduinoJson.h>
DeparturesData fetchDepartures() {
DeparturesData data;
ApiResult res = httpGet(DEPARTURES_API_URL);
if (!res.success) { data.valid = false; return data; }
DynamicJsonDocument doc(16384);
if (deserializeJson(doc, res.body)) { data.valid = false; return data; }
JsonArray departures = doc["departures"];
for (JsonObject d : departures) {
Departure dep;
dep.station = d["station"] | "";
dep.type = d["type"] | "";
dep.icon = d["icon"] | "";
dep.line = d["line"] | "";
dep.destination = d["destination"] | "";
dep.timeEpoch = d["time_epoch"] | 0;
dep.timeStr = d["time_str"] | "";
dep.delayMin = d["delay_min"] | 0;
dep.cancelled = d["cancelled"] | false;
data.departures.push_back(dep);
}
data.valid = true;
return data;
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <Arduino.h>
#include <vector>
struct Departure {
String station;
String type;
String icon;
String line;
String destination;
long timeEpoch = 0;
String timeStr;
int delayMin = 0;
bool cancelled = false;
};
struct DeparturesData {
bool valid = false;
std::vector<Departure> departures;
};
DeparturesData fetchDepartures();
+25
View File
@@ -0,0 +1,25 @@
#include "http_client.h"
#include <HTTPClient.h>
#include <WiFi.h>
ApiResult httpGet(const String &url, uint32_t timeoutMs) {
ApiResult result;
if (WiFi.status() != WL_CONNECTED) {
result.success = false;
result.httpCode = -1;
return result;
}
HTTPClient http;
http.setTimeout(timeoutMs);
http.begin(url);
int code = http.GET();
result.httpCode = code;
if (code == HTTP_CODE_OK) {
result.body = http.getString();
result.success = true;
} else {
result.success = false;
}
http.end();
return result;
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <Arduino.h>
struct ApiResult {
bool success = false;
String body;
int httpCode = -1;
};
ApiResult httpGet(const String &url, uint32_t timeoutMs = 5000);
+44
View File
@@ -0,0 +1,44 @@
#include "weather_api.h"
#include "http_client.h"
#include <secrets.h>
#include <ArduinoJson.h>
WeatherData fetchWeather() {
WeatherData data;
ApiResult res = httpGet(WEATHER_API_URL);
if (!res.success) { data.valid = false; return data; }
DynamicJsonDocument doc(8192);
if (deserializeJson(doc, res.body)) { data.valid = false; return data; }
JsonObject current = doc["current"];
data.current.temperature = current["temperature"] | 0;
data.current.symbol = current["symbol"] | "";
data.current.description = current["description"] | "";
JsonArray forecast = doc["forecast"];
for (JsonObject entry : forecast) {
ForecastEntry fe;
fe.time = entry["time"] | "";
fe.temperature = entry["temperature"] | 0;
fe.symbol = entry["symbol"] | "";
fe.description = entry["description"] | "";
JsonObject precip = entry["precipitation"];
fe.precipitationProbability = precip["probability"] | 0.0f;
fe.precipitationType = precip["type"] | "";
data.forecast.push_back(fe);
}
data.valid = true;
return data;
}
bool willRainSoon(const WeatherData &data, int hours) {
if (!data.valid) return false;
int checked = 0;
for (const auto &entry : data.forecast) {
if (checked >= hours) break;
if (entry.precipitationType == "rain" && entry.precipitationProbability > 0.0f) return true;
checked++;
}
return false;
}
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <Arduino.h>
#include <vector>
struct WeatherCurrent {
int temperature = 0;
String symbol;
String description;
};
struct ForecastEntry {
String time;
int temperature = 0;
String symbol;
String description;
float precipitationProbability = 0.0f;
String precipitationType;
};
struct WeatherData {
bool valid = false;
WeatherCurrent current;
std::vector<ForecastEntry> forecast;
};
WeatherData fetchWeather();
bool willRainSoon(const WeatherData &data, int hours = 8);