v2.8.0: Weather icon in header
Adds a small weather icon in the header, right where the old camera button used to be. Shows current conditions (sun, cloud, sun-behind-cloud, rain, snow, thunderstorm) for whichever location is currently active, including active location presets, so switching to e.g. Milan shows Milan's weather. Powered by the free Open-Meteo API (no API key needed), refreshed automatically every 10 minutes in the background, or immediately after switching to a different location preset. Icons are drawn entirely with TFT_eSPI basic shapes (circles, lines), no image files or extra fonts needed. The initial implementation failed to parse Open-Meteo's response (ArduinoJson "InvalidInput") because the API replies with Transfer-Encoding: chunked and streaming directly from http.getStream() didn't reassemble the chunks correctly. Fixed by collecting the full body via http.getString() first, then parsing that. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,7 @@ You can flash the firmware directly from your browser to your CYD display withou
|
|||||||
- **Night dimming** – automatically dims the backlight, plus the radar's aircraft markers and sweep line, to a softer (but still readable) look between 22:00 and 06:00 local time (see [Night Dimming](#-night-dimming) below)
|
- **Night dimming** – automatically dims the backlight, plus the radar's aircraft markers and sweep line, to a softer (but still readable) look between 22:00 and 06:00 local time (see [Night Dimming](#-night-dimming) below)
|
||||||
- **History chart** – a simple 7-day bar chart of logged aircraft counts, right next to the plain logbook file list (see [History Chart](#-history-chart) below)
|
- **History chart** – a simple 7-day bar chart of logged aircraft counts, right next to the plain logbook file list (see [History Chart](#-history-chart) below)
|
||||||
- **Web export** – a small built-in web page (reachable via the device's IP while on the same WiFi, with its own "Logbook / WebUI" entry in Menu → System) to view, download and individually delete the flight logbook, in addition to a merged CSV of all days (see [Web Export](#-web-export) below)
|
- **Web export** – a small built-in web page (reachable via the device's IP while on the same WiFi, with its own "Logbook / WebUI" entry in Menu → System) to view, download and individually delete the flight logbook, in addition to a merged CSV of all days (see [Web Export](#-web-export) below)
|
||||||
|
- **Weather icon** – a small icon in the header shows the current conditions (sun, cloud, sun-behind-cloud, rain, snow, thunderstorm) for whichever location is currently active, including active location presets (see [Weather Icon](#-weather-icon) below)
|
||||||
- **IP-based geolocation** (no GPS module needed) to center the radar on your location
|
- **IP-based geolocation** (no GPS module needed) to center the radar on your location
|
||||||
- **Proximity LED alert** – the onboard RGB LED blinks green when an aircraft is within 3 km (no speaker on this board, so this replaces an audible alert)
|
- **Proximity LED alert** – the onboard RGB LED blinks green when an aircraft is within 3 km (no speaker on this board, so this replaces an audible alert)
|
||||||
- **Dual-core design** – all networking (WiFi, ADS-B polling, aircraft detail lookups) runs on Core 0, while the display and touch input run on Core 1, so the UI never freezes during a network request
|
- **Dual-core design** – all networking (WiFi, ADS-B polling, aircraft detail lookups) runs on Core 0, while the display and touch input run on Core 1, so the UI never freezes during a network request
|
||||||
@@ -138,6 +139,13 @@ In addition to viewing the logbook on the device itself, a small built-in web pa
|
|||||||
|
|
||||||
Don't know the device's current IP? **Menu > System > "Logbook / WebUI"** shows it live on the device, along with a short explanation of the page (the "?" info button on the **Logbook files** screen also still shows it).
|
Don't know the device's current IP? **Menu > System > "Logbook / WebUI"** shows it live on the device, along with a short explanation of the page (the "?" info button on the **Logbook files** screen also still shows it).
|
||||||
|
|
||||||
|
### ☀️ Weather Icon
|
||||||
|
A small icon in the header (where the camera button used to be) shows the current weather conditions – sun, cloud, sun-behind-cloud, rain, snow, or thunderstorm – drawn entirely with simple shapes, no image files or extra fonts needed.
|
||||||
|
|
||||||
|
It always follows whichever location is currently active, including active [location presets](#-location-presets) – switch the active preset to e.g. Milan or Tokyo and the icon updates to show that location's weather.
|
||||||
|
|
||||||
|
Powered by the free [Open-Meteo](https://open-meteo.com) API (no API key needed). Refreshed automatically in the background every 10 minutes, or immediately after switching to a different location.
|
||||||
|
|
||||||
### 📖 Flight logbook (ON/OFF)
|
### 📖 Flight logbook (ON/OFF)
|
||||||
When enabled, the device logs **every newly sighted aircraft** (timestamp, hex code, callsign, registration, type, distance, altitude) into a CSV file on the SD card. An aircraft is only logged once per activation, even if it crosses the radar multiple times.
|
When enabled, the device logs **every newly sighted aircraft** (timestamp, hex code, callsign, registration, type, distance, altitude) into a CSV file on the SD card. An aircraft is only logged once per activation, even if it crosses the radar multiple times.
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Config {
|
|||||||
// CLAUDE.md-Workflow "Standard-Workflow: Push & Release") - erscheint
|
// CLAUDE.md-Workflow "Standard-Workflow: Push & Release") - erscheint
|
||||||
// im Info-Screen (Menue > System > Info) und muss zum jeweiligen
|
// im Info-Screen (Menue > System > Info) und muss zum jeweiligen
|
||||||
// Git-Tag passen.
|
// Git-Tag passen.
|
||||||
constexpr const char* APP_VERSION = "2.7.5";
|
constexpr const char* APP_VERSION = "2.8.0";
|
||||||
|
|
||||||
// Display-Helligkeit (Menue > System > Helligkeit), in Prozent.
|
// Display-Helligkeit (Menue > System > Helligkeit), in Prozent.
|
||||||
// MIN bewusst nicht 0 - ein komplett dunkles Display koennte sonst wie
|
// MIN bewusst nicht 0 - ein komplett dunkles Display koennte sonst wie
|
||||||
@@ -40,6 +40,12 @@ namespace Config {
|
|||||||
constexpr uint32_t FETCH_INTERVAL_MS = 8000;
|
constexpr uint32_t FETCH_INTERVAL_MS = 8000;
|
||||||
constexpr uint32_t HTTP_TIMEOUT_MS = 6000;
|
constexpr uint32_t HTTP_TIMEOUT_MS = 6000;
|
||||||
|
|
||||||
|
// Wetter-Icon im Header (siehe weather.cpp) - deutlich seltener
|
||||||
|
// abgefragt als die ADS-B-Daten, das Wetter aendert sich nicht
|
||||||
|
// minuetlich und die kostenlose Open-Meteo-API soll nicht unnoetig oft
|
||||||
|
// belastet werden.
|
||||||
|
constexpr uint32_t WEATHER_FETCH_INTERVAL_MS = 600000; // 10 Minuten
|
||||||
|
|
||||||
constexpr float DEFAULT_PROXIMITY_ALERT_KM = 8.0f;
|
constexpr float DEFAULT_PROXIMITY_ALERT_KM = 8.0f;
|
||||||
|
|
||||||
constexpr float LED_ALERT_RADIUS_KM = 3.0f;
|
constexpr float LED_ALERT_RADIUS_KM = 3.0f;
|
||||||
|
|||||||
86
src/main.cpp
86
src/main.cpp
@@ -27,10 +27,12 @@
|
|||||||
#include "splash_screen.h"
|
#include "splash_screen.h"
|
||||||
#include "led_alert.h"
|
#include "led_alert.h"
|
||||||
#include "flight_logbook.h"
|
#include "flight_logbook.h"
|
||||||
|
#include "weather.h"
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
#include "first_run_language_screen.h"
|
#include "first_run_language_screen.h"
|
||||||
#include "menu_stars.h"
|
#include "menu_stars.h"
|
||||||
#include "ui_font.h"
|
#include "ui_font.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
TFT_eSPI tft = TFT_eSPI();
|
TFT_eSPI tft = TFT_eSPI();
|
||||||
|
|
||||||
@@ -44,6 +46,7 @@ uint32_t lastPollMs = 0;
|
|||||||
uint32_t lastSweepMs = 0;
|
uint32_t lastSweepMs = 0;
|
||||||
uint32_t lastStatusLineMs = 0;
|
uint32_t lastStatusLineMs = 0;
|
||||||
uint32_t lastRenderedVersion = 0xFFFFFFFF;
|
uint32_t lastRenderedVersion = 0xFFFFFFFF;
|
||||||
|
Weather::Condition lastRenderedWeather = Weather::Condition::Unknown;
|
||||||
bool forceRedraw = false;
|
bool forceRedraw = false;
|
||||||
bool wasEmergency = false;
|
bool wasEmergency = false;
|
||||||
bool bannerBlinkOn = false;
|
bool bannerBlinkOn = false;
|
||||||
@@ -80,6 +83,11 @@ struct Rect {
|
|||||||
|
|
||||||
Rect menuBtn = {Config::SCREEN_WIDTH - 90, 3, 54, 22};
|
Rect menuBtn = {Config::SCREEN_WIDTH - 90, 3, 54, 22};
|
||||||
|
|
||||||
|
// Platz, an dem frueher der Cam-Button war (siehe entfernte Screenshot-
|
||||||
|
// Funktion) - zeigt jetzt stattdessen ein kleines Wetter-Icon fuer den
|
||||||
|
// aktuell aktiven Standort (siehe weather.cpp/Weather::update()).
|
||||||
|
Rect weatherIconRect = {(int16_t)(menuBtn.x - 46), 3, 42, 22};
|
||||||
|
|
||||||
void drawMenuButton() {
|
void drawMenuButton() {
|
||||||
tft.fillRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_BLACK);
|
tft.fillRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_BLACK);
|
||||||
tft.drawRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_GREEN);
|
tft.drawRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_GREEN);
|
||||||
@@ -89,6 +97,73 @@ void drawMenuButton() {
|
|||||||
tft.setTextDatum(TL_DATUM);
|
tft.setTextDatum(TL_DATUM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kleine, mit einfachen TFT_eSPI-Grundformen gezeichnete Wolke (kein
|
||||||
|
// Bild/Font noetig) - Basis fuer die Regen/Schnee/Gewitter-Varianten.
|
||||||
|
void drawCloudShape(int16_t cx, int16_t cy, uint16_t color) {
|
||||||
|
tft.fillCircle(cx - 5, cy + 1, 3, color);
|
||||||
|
tft.fillCircle(cx - 1, cy - 2, 4, color);
|
||||||
|
tft.fillCircle(cx + 4, cy, 4, color);
|
||||||
|
tft.fillRect(cx - 8, cy, 13, 3, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sonne mit ein paar Strahlen ringsum, ebenfalls nur aus Grundformen.
|
||||||
|
void drawSunShape(int16_t cx, int16_t cy, int16_t r, uint16_t color) {
|
||||||
|
tft.fillCircle(cx, cy, r, color);
|
||||||
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
|
float angle = i * (PI / 4.0f);
|
||||||
|
int16_t x1 = cx + (int16_t)((r + 2) * cosf(angle));
|
||||||
|
int16_t y1 = cy + (int16_t)((r + 2) * sinf(angle));
|
||||||
|
int16_t x2 = cx + (int16_t)((r + 4) * cosf(angle));
|
||||||
|
int16_t y2 = cy + (int16_t)((r + 4) * sinf(angle));
|
||||||
|
tft.drawLine(x1, y1, x2, y2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zeichnet das Wetter-Icon passend zur zuletzt abgefragten Wetterlage
|
||||||
|
// (Weather::current()) - bei Weather::Condition::Unknown (noch keine
|
||||||
|
// erfolgreiche Abfrage, z.B. kurz nach dem Booten) bleibt die Flaeche
|
||||||
|
// einfach leer, statt einen Platzhalter anzuzeigen.
|
||||||
|
void drawWeatherIcon() {
|
||||||
|
int16_t cx = weatherIconRect.x + weatherIconRect.w / 2;
|
||||||
|
int16_t cy = weatherIconRect.y + weatherIconRect.h / 2;
|
||||||
|
|
||||||
|
tft.fillRect(weatherIconRect.x, weatherIconRect.y, weatherIconRect.w, weatherIconRect.h, TFT_BLACK);
|
||||||
|
|
||||||
|
switch (Weather::current()) {
|
||||||
|
case Weather::Condition::Clear:
|
||||||
|
drawSunShape(cx, cy, 6, TFT_YELLOW);
|
||||||
|
break;
|
||||||
|
case Weather::Condition::PartlyCloudy:
|
||||||
|
drawSunShape((int16_t)(cx - 4), (int16_t)(cy - 3), 4, TFT_YELLOW);
|
||||||
|
drawCloudShape((int16_t)(cx + 3), (int16_t)(cy + 2), TFT_LIGHTGREY);
|
||||||
|
break;
|
||||||
|
case Weather::Condition::Cloudy:
|
||||||
|
drawCloudShape(cx, cy, TFT_LIGHTGREY);
|
||||||
|
break;
|
||||||
|
case Weather::Condition::Rain:
|
||||||
|
drawCloudShape(cx, (int16_t)(cy - 3), TFT_LIGHTGREY);
|
||||||
|
tft.drawLine(cx - 4, cy + 4, cx - 6, cy + 8, TFT_SKYBLUE);
|
||||||
|
tft.drawLine(cx, cy + 4, cx - 2, cy + 8, TFT_SKYBLUE);
|
||||||
|
tft.drawLine(cx + 4, cy + 4, cx + 2, cy + 8, TFT_SKYBLUE);
|
||||||
|
break;
|
||||||
|
case Weather::Condition::Snow:
|
||||||
|
drawCloudShape(cx, (int16_t)(cy - 3), TFT_LIGHTGREY);
|
||||||
|
tft.drawPixel(cx - 4, cy + 6, TFT_WHITE);
|
||||||
|
tft.drawPixel(cx, cy + 7, TFT_WHITE);
|
||||||
|
tft.drawPixel(cx + 4, cy + 6, TFT_WHITE);
|
||||||
|
break;
|
||||||
|
case Weather::Condition::Thunderstorm:
|
||||||
|
drawCloudShape(cx, (int16_t)(cy - 3), TFT_LIGHTGREY);
|
||||||
|
tft.drawLine(cx, cy + 3, cx - 3, cy + 7, TFT_YELLOW);
|
||||||
|
tft.drawLine(cx - 3, cy + 7, cx + 1, cy + 7, TFT_YELLOW);
|
||||||
|
tft.drawLine(cx + 1, cy + 7, cx - 2, cy + 11, TFT_YELLOW);
|
||||||
|
break;
|
||||||
|
case Weather::Condition::Unknown:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void drawWifiIcon(int16_t rightX, int16_t rowTop, int16_t rowH, int8_t rssi) {
|
void drawWifiIcon(int16_t rightX, int16_t rowTop, int16_t rowH, int8_t rssi) {
|
||||||
uint8_t level;
|
uint8_t level;
|
||||||
if (rssi >= -55) level = 4;
|
if (rssi >= -55) level = 4;
|
||||||
@@ -131,6 +206,8 @@ void drawHeader() {
|
|||||||
tft.println("Eiswolfs FR");
|
tft.println("Eiswolfs FR");
|
||||||
drawMenuButton();
|
drawMenuButton();
|
||||||
updateWifiIcon();
|
updateWifiIcon();
|
||||||
|
drawWeatherIcon();
|
||||||
|
lastRenderedWeather = Weather::current();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateStatusLine() {
|
void updateStatusLine() {
|
||||||
@@ -165,6 +242,15 @@ void updateStatusLine() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateWifiIcon();
|
updateWifiIcon();
|
||||||
|
|
||||||
|
// Icon nur bei tatsaechlicher Aenderung neu zeichnen (Weather::update()
|
||||||
|
// laeuft im Hintergrund auf Core 0 und aktualisiert typischerweise nur
|
||||||
|
// alle paar Minuten) - vermeidet unnoetiges Neuzeichnen jede Sekunde.
|
||||||
|
Weather::Condition weatherNow = Weather::current();
|
||||||
|
if (weatherNow != lastRenderedWeather) {
|
||||||
|
lastRenderedWeather = weatherNow;
|
||||||
|
drawWeatherIcon();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prueft, ob die aktuelle Lokalzeit im Nachtdimm-Fenster (22:00-06:00) liegt.
|
// Prueft, ob die aktuelle Lokalzeit im Nachtdimm-Fenster (22:00-06:00) liegt.
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "flight_logbook.h"
|
#include "flight_logbook.h"
|
||||||
#include "led_alert.h"
|
#include "led_alert.h"
|
||||||
#include "web_export_server.h"
|
#include "web_export_server.h"
|
||||||
|
#include "weather.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
@@ -39,6 +40,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AircraftDetails::update();
|
AircraftDetails::update();
|
||||||
|
Weather::update();
|
||||||
|
|
||||||
if (millis() - lastFetchMs >= Config::FETCH_INTERVAL_MS) {
|
if (millis() - lastFetchMs >= Config::FETCH_INTERVAL_MS) {
|
||||||
lastFetchMs = millis();
|
lastFetchMs = millis();
|
||||||
|
|||||||
103
src/weather.cpp
Normal file
103
src/weather.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#include "weather.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "location_manager.h"
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <WiFiClientSecure.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
namespace Weather {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
WiFiClientSecure client;
|
||||||
|
bool clientConfigured = false;
|
||||||
|
|
||||||
|
Condition currentCondition = Condition::Unknown;
|
||||||
|
uint32_t lastFetchMs = 0;
|
||||||
|
double lastLat = 0;
|
||||||
|
double lastLon = 0;
|
||||||
|
bool hasLastLocation = false;
|
||||||
|
|
||||||
|
// Ordnet den WMO-Wettercode von Open-Meteo (Feld "weathercode", siehe
|
||||||
|
// https://open-meteo.com/en/docs) einer der wenigen Icon-Kategorien zu,
|
||||||
|
// die main.cpp zeichnen kann.
|
||||||
|
Condition conditionFromWmoCode(int code) {
|
||||||
|
if (code == 0) return Condition::Clear;
|
||||||
|
if (code == 1 || code == 2) return Condition::PartlyCloudy;
|
||||||
|
if (code == 3 || code == 45 || code == 48) return Condition::Cloudy;
|
||||||
|
if ((code >= 51 && code <= 67) || (code >= 80 && code <= 82)) return Condition::Rain;
|
||||||
|
if (code >= 71 && code <= 77) return Condition::Snow;
|
||||||
|
if (code >= 95) return Condition::Thunderstorm;
|
||||||
|
return Condition::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fetchNow(double lat, double lon) {
|
||||||
|
if (WiFi.status() != WL_CONNECTED) return;
|
||||||
|
|
||||||
|
if (!clientConfigured) {
|
||||||
|
client.setInsecure();
|
||||||
|
client.setTimeout(Config::HTTP_TIMEOUT_MS);
|
||||||
|
clientConfigured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPClient http;
|
||||||
|
char url[160];
|
||||||
|
snprintf(url, sizeof(url),
|
||||||
|
"https://api.open-meteo.com/v1/forecast?latitude=%.4f&longitude=%.4f¤t_weather=true",
|
||||||
|
lat, lon);
|
||||||
|
|
||||||
|
http.setTimeout(Config::HTTP_TIMEOUT_MS);
|
||||||
|
if (!http.begin(client, url)) return;
|
||||||
|
|
||||||
|
int code = http.GET();
|
||||||
|
if (code != HTTP_CODE_OK) {
|
||||||
|
http.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body erst komplett als String einsammeln (getString() kuemmert
|
||||||
|
// sich zuverlaessig um Chunked-Transfer-Encoding), statt direkt aus
|
||||||
|
// http.getStream() zu parsen - Letzteres scheiterte bei Open-Meteo
|
||||||
|
// zuverlaessig mit einem ArduinoJson-"InvalidInput"-Fehler.
|
||||||
|
String body = http.getString();
|
||||||
|
http.end();
|
||||||
|
|
||||||
|
JsonDocument doc;
|
||||||
|
DeserializationError err = deserializeJson(doc, body);
|
||||||
|
if (err) return;
|
||||||
|
|
||||||
|
int wmoCode = doc["current_weather"]["weathercode"] | -1;
|
||||||
|
if (wmoCode < 0) return;
|
||||||
|
|
||||||
|
currentCondition = conditionFromWmoCode(wmoCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
double lat = 0, lon = 0;
|
||||||
|
LocationManager::getHomeLocation(lat, lon);
|
||||||
|
if (lat == 0 && lon == 0) return;
|
||||||
|
|
||||||
|
// Deutliche Standort-Aenderung (z.B. anderes Standort-Preset aktiviert)
|
||||||
|
// - sofort neu abfragen statt bis zum naechsten regulaeren Intervall zu
|
||||||
|
// warten, damit das Icon nicht minutenlang das Wetter des alten
|
||||||
|
// Standorts zeigt.
|
||||||
|
bool locationChanged = !hasLastLocation ||
|
||||||
|
fabs(lat - lastLat) > 0.01 || fabs(lon - lastLon) > 0.01;
|
||||||
|
|
||||||
|
uint32_t now = millis();
|
||||||
|
if (!locationChanged && (now - lastFetchMs < Config::WEATHER_FETCH_INTERVAL_MS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFetchMs = now;
|
||||||
|
lastLat = lat;
|
||||||
|
lastLon = lon;
|
||||||
|
hasLastLocation = true;
|
||||||
|
fetchNow(lat, lon);
|
||||||
|
}
|
||||||
|
|
||||||
|
Condition current() { return currentCondition; }
|
||||||
|
|
||||||
|
}
|
||||||
33
src/weather.h
Normal file
33
src/weather.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
// Sehr einfache Wetteranzeige (Icon im Header, dort wo frueher der
|
||||||
|
// Cam-Button war) - fragt periodisch die aktuelle Wetterlage fuer den
|
||||||
|
// gerade aktiven Standort ab (also inkl. aktivem Standort-Preset, siehe
|
||||||
|
// LocationManager::getHomeLocation() - wenn dort z.B. Mailand ausgewaehlt
|
||||||
|
// ist, zeigt das Icon das Wetter in Mailand). Nutzt die kostenlose
|
||||||
|
// Open-Meteo-API (kein API-Key noetig).
|
||||||
|
namespace Weather {
|
||||||
|
enum class Condition {
|
||||||
|
Unknown, // noch keine erfolgreiche Abfrage - Icon zeigt nichts an
|
||||||
|
Clear,
|
||||||
|
PartlyCloudy,
|
||||||
|
Cloudy,
|
||||||
|
Rain,
|
||||||
|
Snow,
|
||||||
|
Thunderstorm
|
||||||
|
};
|
||||||
|
|
||||||
|
// Muss regelmaessig aus dem NetTask (Core 0) aufgerufen werden - kuemmert
|
||||||
|
// sich intern um das Abfrage-Intervall (Config::WEATHER_FETCH_INTERVAL_MS)
|
||||||
|
// und erkennt eine Standort-Aenderung (z.B. anderes aktives Preset), um
|
||||||
|
// dann sofort neu abzufragen statt bis zum naechsten reguleren Intervall
|
||||||
|
// zu warten.
|
||||||
|
void update();
|
||||||
|
|
||||||
|
// Aktuell bekannte Wetterlage, threadsicher genug fuer diesen Zweck (ein
|
||||||
|
// einzelnes uint8-artiges Enum, das nur vom NetTask geschrieben und vom
|
||||||
|
// UI-Thread gelesen wird - kein Lock noetig, ein kurzfristig veraltetes
|
||||||
|
// Lesen ist unkritisch).
|
||||||
|
Condition current();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user