Merge branch 'feat-weather' of https://github.com/jpirnay/crosspoint-reader into mybuild
This commit is contained in:
@@ -338,6 +338,63 @@ STR_LINK: "[link]"
|
||||
STR_SCREENSHOT_BUTTON: "Take screenshot"
|
||||
STR_AUTO_TURN_ENABLED: "Auto Turn Enabled: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)"
|
||||
STR_WEATHER: "Weather"
|
||||
STR_WEATHER_LOCATION: "Location"
|
||||
STR_WEATHER_NO_LOCATION: "No location set for weather"
|
||||
STR_WEATHER_FETCH_FAILED: "Failed to fetch weather"
|
||||
STR_WEATHER_SETTINGS: "Weather Settings"
|
||||
STR_WEATHER_SETTINGS_SHORT: "Settings"
|
||||
STR_WEATHER_REFRESH: "Refresh"
|
||||
STR_WEATHER_FEELS_LIKE: "Feels like"
|
||||
STR_WEATHER_HUMIDITY: "Humidity"
|
||||
STR_WEATHER_WIND: "Wind"
|
||||
STR_WEATHER_PRESSURE: "Pressure"
|
||||
STR_WEATHER_LAST_UPDATED: "Last updated"
|
||||
STR_WEATHER_PRECIP: "Precip."
|
||||
STR_WEATHER_PRECIP_UNIT: "Precipitation Unit"
|
||||
STR_WEATHER_WIND_UNIT: "Wind Speed Unit"
|
||||
STR_WEATHER_TEMP_UNIT: "Temperature Unit"
|
||||
STR_WEATHER_48H_FORECAST: "48-Hour Forecast"
|
||||
STR_WEATHER_SEARCH_CITY: "Search City"
|
||||
STR_WEATHER_LONGITUDE: "Longitude"
|
||||
STR_WEATHER_LATITUDE: "Latitude"
|
||||
STR_WEATHER_SEARCH_RESULTS: "Search Results"
|
||||
STR_WEATHER_DAY_MON: "Mon"
|
||||
STR_WEATHER_DAY_TUE: "Tue"
|
||||
STR_WEATHER_DAY_WED: "Wed"
|
||||
STR_WEATHER_DAY_THU: "Thu"
|
||||
STR_WEATHER_DAY_FRI: "Fri"
|
||||
STR_WEATHER_DAY_SAT: "Sat"
|
||||
STR_WEATHER_DAY_SUN: "Sun"
|
||||
STR_WEATHER_DESC_CLEAR_SKY: "Clear sky"
|
||||
STR_WEATHER_DESC_MAINLY_CLEAR: "Mainly clear"
|
||||
STR_WEATHER_DESC_PARTLY_CLOUDY: "Partly cloudy"
|
||||
STR_WEATHER_DESC_OVERCAST: "Overcast"
|
||||
STR_WEATHER_DESC_FOG: "Fog"
|
||||
STR_WEATHER_DESC_RIME_FOG: "Rime fog"
|
||||
STR_WEATHER_DESC_LIGHT_DRIZZLE: "Light drizzle"
|
||||
STR_WEATHER_DESC_DRIZZLE: "Drizzle"
|
||||
STR_WEATHER_DESC_DENSE_DRIZZLE: "Dense drizzle"
|
||||
STR_WEATHER_DESC_FREEZING_DRIZZLE: "Freezing drizzle"
|
||||
STR_WEATHER_DESC_DENSE_FREEZING_DRIZZLE: "Dense freezing drizzle"
|
||||
STR_WEATHER_DESC_SLIGHT_RAIN: "Slight rain"
|
||||
STR_WEATHER_DESC_MODERATE_RAIN: "Moderate rain"
|
||||
STR_WEATHER_DESC_HEAVY_RAIN: "Heavy rain"
|
||||
STR_WEATHER_DESC_FREEZING_RAIN: "Freezing rain"
|
||||
STR_WEATHER_DESC_HEAVY_FREEZING_RAIN: "Heavy freezing rain"
|
||||
STR_WEATHER_DESC_SLIGHT_SNOW: "Slight snow"
|
||||
STR_WEATHER_DESC_MODERATE_SNOW: "Moderate snow"
|
||||
STR_WEATHER_DESC_HEAVY_SNOW: "Heavy snow"
|
||||
STR_WEATHER_DESC_SNOW_GRAINS: "Snow grains"
|
||||
STR_WEATHER_DESC_SLIGHT_SHOWERS: "Slight showers"
|
||||
STR_WEATHER_DESC_MODERATE_SHOWERS: "Moderate showers"
|
||||
STR_WEATHER_DESC_VIOLENT_SHOWERS: "Violent showers"
|
||||
STR_WEATHER_DESC_SNOW_SHOWERS: "Snow showers"
|
||||
STR_WEATHER_DESC_HEAVY_SNOW_SHOWERS: "Heavy snow showers"
|
||||
STR_WEATHER_DESC_THUNDERSTORM: "Thunderstorm"
|
||||
STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Thunderstorm, hail"
|
||||
STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Thunderstorm, heavy hail"
|
||||
STR_WEATHER_DESC_UNKNOWN: "Unknown"
|
||||
STR_INFO: "Info"
|
||||
STR_AUTHOR: "Author"
|
||||
STR_SERIES: "Series"
|
||||
|
||||
@@ -320,6 +320,66 @@ STR_UPTIME: "Laufzeit"
|
||||
STR_CHARGING: "Lädt"
|
||||
STR_GATHERING_DATA: "Daten werden gesammelt..."
|
||||
STR_READING: "Lese..."
|
||||
|
||||
STR_WEATHER: "Wetter"
|
||||
STR_WEATHER_LOCATION: "Ort"
|
||||
STR_WEATHER_NO_LOCATION: "Kein Ort für Wetter festgelegt"
|
||||
STR_WEATHER_FETCH_FAILED: "Wetterdaten konnten nicht geladen werden"
|
||||
STR_WEATHER_SETTINGS: "Wettereinstellungen"
|
||||
STR_WEATHER_SETTINGS_SHORT: "Einstell."
|
||||
STR_WEATHER_REFRESH: "Aktual."
|
||||
STR_WEATHER_FEELS_LIKE: "Gefühlt"
|
||||
STR_WEATHER_HUMIDITY: "Luftfeuchte"
|
||||
STR_WEATHER_WIND: "Wind"
|
||||
STR_WEATHER_PRESSURE: "Luftdruck"
|
||||
STR_WEATHER_LAST_UPDATED: "Zuletzt aktualisiert"
|
||||
STR_WEATHER_PRECIP: "Niederschl."
|
||||
STR_WEATHER_PRECIP_UNIT: "Niederschlagseinheit"
|
||||
STR_WEATHER_WIND_UNIT: "Windgeschwindigkeitseinheit"
|
||||
STR_WEATHER_TEMP_UNIT: "Temperatureinheit"
|
||||
STR_WEATHER_48H_FORECAST: "48-Stunden-Vorhersage"
|
||||
STR_WEATHER_SEARCH_CITY: "Stadt suchen"
|
||||
STR_WEATHER_LONGITUDE: "Längengrad"
|
||||
STR_WEATHER_LATITUDE: "Breitengrad"
|
||||
STR_WEATHER_SEARCH_RESULTS: "Suchergebnisse"
|
||||
|
||||
STR_WEATHER_DAY_MON: "Mo"
|
||||
STR_WEATHER_DAY_TUE: "Di"
|
||||
STR_WEATHER_DAY_WED: "Mi"
|
||||
STR_WEATHER_DAY_THU: "Do"
|
||||
STR_WEATHER_DAY_FRI: "Fr"
|
||||
STR_WEATHER_DAY_SAT: "Sa"
|
||||
STR_WEATHER_DAY_SUN: "So"
|
||||
|
||||
STR_WEATHER_DESC_CLEAR_SKY: "Klarer Himmel"
|
||||
STR_WEATHER_DESC_MAINLY_CLEAR: "Überwiegend klar"
|
||||
STR_WEATHER_DESC_PARTLY_CLOUDY: "Teilweise bewölkt"
|
||||
STR_WEATHER_DESC_OVERCAST: "Bedeckt"
|
||||
STR_WEATHER_DESC_FOG: "Nebel"
|
||||
STR_WEATHER_DESC_RIME_FOG: "Raureifnebel"
|
||||
STR_WEATHER_DESC_LIGHT_DRIZZLE: "Leichter Nieselregen"
|
||||
STR_WEATHER_DESC_DRIZZLE: "Nieselregen"
|
||||
STR_WEATHER_DESC_DENSE_DRIZZLE: "Dichter Nieselregen"
|
||||
STR_WEATHER_DESC_FREEZING_DRIZZLE: "Gefrierender Nieselregen"
|
||||
STR_WEATHER_DESC_DENSE_FREEZING_DRIZZLE: "Dichter gefrierender Nieselregen"
|
||||
STR_WEATHER_DESC_SLIGHT_RAIN: "Leichter Regen"
|
||||
STR_WEATHER_DESC_MODERATE_RAIN: "Mäßiger Regen"
|
||||
STR_WEATHER_DESC_HEAVY_RAIN: "Starker Regen"
|
||||
STR_WEATHER_DESC_FREEZING_RAIN: "Gefrierender Regen"
|
||||
STR_WEATHER_DESC_HEAVY_FREEZING_RAIN: "Starker gefrierender Regen"
|
||||
STR_WEATHER_DESC_SLIGHT_SNOW: "Leichter Schneefall"
|
||||
STR_WEATHER_DESC_MODERATE_SNOW: "Mäßiger Schneefall"
|
||||
STR_WEATHER_DESC_HEAVY_SNOW: "Starker Schneefall"
|
||||
STR_WEATHER_DESC_SNOW_GRAINS: "Schneegriesel"
|
||||
STR_WEATHER_DESC_SLIGHT_SHOWERS: "Leichte Schauer"
|
||||
STR_WEATHER_DESC_MODERATE_SHOWERS: "Mäßige Schauer"
|
||||
STR_WEATHER_DESC_VIOLENT_SHOWERS: "Heftige Schauer"
|
||||
STR_WEATHER_DESC_SNOW_SHOWERS: "Schneeschauer"
|
||||
STR_WEATHER_DESC_HEAVY_SNOW_SHOWERS: "Starke Schneeschauer"
|
||||
STR_WEATHER_DESC_THUNDERSTORM: "Gewitter"
|
||||
STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Gewitter mit Hagel"
|
||||
STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Gewitter mit starkem Hagel"
|
||||
STR_WEATHER_DESC_UNKNOWN: "Unbekannt"
|
||||
STR_CREATE_FALLBACK_FOR_INVALID_TOC: "Ersatz für ungültiges Inhaltsverzeichnis erstellen"
|
||||
STR_USE_CLOCK: "Uhr anzeigen"
|
||||
STR_CLOCK_SETTINGS: "Uhr-Einstellungen"
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
#include "WeatherClient.h"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <HalStorage.h>
|
||||
#include <Logging.h>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include "../../src/network/HttpDownloader.h"
|
||||
|
||||
namespace {
|
||||
constexpr char WEATHER_CACHE_FILE[] = "/.crosspoint/weather_cache.json";
|
||||
|
||||
std::string buildForecastUrl(const WeatherSettingsStore& settings) {
|
||||
// Open-Meteo supports plain HTTP. Using HTTP here avoids TLS handshake
|
||||
// instability observed on some ESP32-C3 builds.
|
||||
std::string url = "http://api.open-meteo.com/v1/forecast?";
|
||||
url += "latitude=" + std::to_string(settings.getLatitude());
|
||||
url += "&longitude=" + std::to_string(settings.getLongitude());
|
||||
url +=
|
||||
"¤t=temperature_2m,relative_humidity_2m,apparent_temperature,"
|
||||
"weather_code,wind_speed_10m,wind_direction_10m,is_day,precipitation,uv_index";
|
||||
url += "&hourly=temperature_2m,precipitation,precipitation_probability,weather_code,is_day";
|
||||
url +=
|
||||
"&daily=temperature_2m_max,temperature_2m_min,weather_code,"
|
||||
"precipitation_sum,sunrise,sunset,uv_index_max";
|
||||
url += "&timezone=auto&timeformat=unixtime";
|
||||
url += std::string("&temperature_unit=") + settings.getTempUnitParam();
|
||||
url += std::string("&wind_speed_unit=") + settings.getWindUnitParam();
|
||||
url += std::string("&precipitation_unit=") + settings.getPrecipUnitParam();
|
||||
url += "&forecast_days=" + std::to_string(settings.getForecastDays());
|
||||
// Request 48 hours of hourly data
|
||||
url += "&forecast_hours=48";
|
||||
return url;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
WeatherData WeatherClient::getWeather(const WeatherSettingsStore& settings, bool forceRefresh) {
|
||||
if (!settings.hasLocation()) {
|
||||
WeatherData data;
|
||||
data.errorMessage = "No location configured";
|
||||
return data;
|
||||
}
|
||||
|
||||
if (!forceRefresh) {
|
||||
WeatherData cached;
|
||||
if (loadCache(cached) && cached.valid) {
|
||||
time_t now;
|
||||
time(&now);
|
||||
if (now - cached.fetchedAt < CACHE_TTL_SECONDS) {
|
||||
LOG_DBG("WEA", "Using cached weather data (age: %ld s)", (long)(now - cached.fetchedAt));
|
||||
return cached;
|
||||
}
|
||||
LOG_DBG("WEA", "Cache expired (age: %ld s)", (long)(now - cached.fetchedAt));
|
||||
// Cache-first behavior: return stale cache and let the caller decide
|
||||
// whether/when to perform a network refresh.
|
||||
return cached;
|
||||
}
|
||||
|
||||
LOG_DBG("WEA", "No cache available; caller should establish network and force refresh");
|
||||
WeatherData data;
|
||||
data.errorMessage = "No cache";
|
||||
return data;
|
||||
}
|
||||
|
||||
return fetchFromApi(settings);
|
||||
}
|
||||
|
||||
WeatherData WeatherClient::fetchFromApi(const WeatherSettingsStore& settings) {
|
||||
WeatherData data;
|
||||
std::string url = buildForecastUrl(settings);
|
||||
LOG_DBG("WEA", "fetchFromApi[1] start");
|
||||
LOG_DBG("WEA", "fetchFromApi[2] url length=%zu", url.size());
|
||||
LOG_DBG("WEA", "Fetching weather from API");
|
||||
|
||||
std::string response;
|
||||
LOG_DBG("WEA", "fetchFromApi[3] HttpDownloader::fetchUrl before");
|
||||
if (!HttpDownloader::fetchUrl(url, response)) {
|
||||
data.errorMessage = "Network error";
|
||||
LOG_ERR("WEA", "Failed to fetch weather data");
|
||||
return data;
|
||||
}
|
||||
LOG_DBG("WEA", "fetchFromApi[4] HttpDownloader::fetchUrl after; bytes=%zu", response.size());
|
||||
|
||||
LOG_DBG("WEA", "fetchFromApi[5] parseWeatherJson before");
|
||||
if (!parseWeatherJson(response, data)) {
|
||||
LOG_ERR("WEA", "Failed to parse weather JSON");
|
||||
return data;
|
||||
}
|
||||
LOG_DBG("WEA", "fetchFromApi[6] parseWeatherJson after");
|
||||
|
||||
data.valid = true;
|
||||
time(&data.fetchedAt);
|
||||
saveCache(data);
|
||||
LOG_DBG("WEA", "fetchFromApi[7] Weather data fetched and cached");
|
||||
return data;
|
||||
}
|
||||
|
||||
bool WeatherClient::parseWeatherJson(const std::string& json, WeatherData& data) {
|
||||
JsonDocument doc;
|
||||
auto error = deserializeJson(doc, json);
|
||||
if (error) {
|
||||
data.errorMessage = "JSON parse error";
|
||||
LOG_ERR("WEA", "JSON parse error: %s", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for API error
|
||||
if (doc["error"] | false) {
|
||||
data.errorMessage = doc["reason"] | "API error";
|
||||
LOG_ERR("WEA", "API error: %s", data.errorMessage.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
data.timezone = doc["timezone"] | std::string("");
|
||||
data.utcOffsetSeconds = doc["utc_offset_seconds"] | 0;
|
||||
|
||||
// Parse current weather
|
||||
JsonObject current = doc["current"];
|
||||
if (current) {
|
||||
data.current.temperature = current["temperature_2m"] | 0.0f;
|
||||
data.current.apparentTemperature = current["apparent_temperature"] | 0.0f;
|
||||
data.current.humidity = current["relative_humidity_2m"] | 0;
|
||||
data.current.weatherCode = current["weather_code"] | 0;
|
||||
data.current.windSpeed = current["wind_speed_10m"] | 0.0f;
|
||||
data.current.windDirection = current["wind_direction_10m"] | 0;
|
||||
data.current.precipitation = current["precipitation"] | 0.0f;
|
||||
data.current.uvIndex = current["uv_index"] | 0.0f;
|
||||
data.current.isDay = (current["is_day"] | 1) != 0;
|
||||
}
|
||||
|
||||
// Parse daily forecast
|
||||
JsonObject daily = doc["daily"];
|
||||
if (daily) {
|
||||
JsonArray times = daily["time"];
|
||||
JsonArray tempMax = daily["temperature_2m_max"];
|
||||
JsonArray tempMin = daily["temperature_2m_min"];
|
||||
JsonArray codes = daily["weather_code"];
|
||||
JsonArray precip = daily["precipitation_sum"];
|
||||
JsonArray sunrise = daily["sunrise"];
|
||||
JsonArray sunset = daily["sunset"];
|
||||
JsonArray uvMax = daily["uv_index_max"];
|
||||
|
||||
size_t count = times.size();
|
||||
data.daily.reserve(count);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
DailyForecast day;
|
||||
day.date = times[i] | (time_t)0;
|
||||
day.tempMax = tempMax[i] | 0.0f;
|
||||
day.tempMin = tempMin[i] | 0.0f;
|
||||
day.weatherCode = codes[i] | 0;
|
||||
day.precipSum = precip[i] | 0.0f;
|
||||
day.sunrise = sunrise[i] | (time_t)0;
|
||||
day.sunset = sunset[i] | (time_t)0;
|
||||
day.uvIndexMax = uvMax[i] | 0.0f;
|
||||
data.daily.push_back(day);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse hourly forecast
|
||||
JsonObject hourly = doc["hourly"];
|
||||
if (hourly) {
|
||||
JsonArray times = hourly["time"];
|
||||
JsonArray temp = hourly["temperature_2m"];
|
||||
JsonArray precip = hourly["precipitation"];
|
||||
JsonArray precipProb = hourly["precipitation_probability"];
|
||||
JsonArray codes = hourly["weather_code"];
|
||||
JsonArray isDay = hourly["is_day"];
|
||||
|
||||
size_t count = times.size();
|
||||
data.hourly.reserve(count);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
HourlyForecast hour;
|
||||
hour.time = times[i] | (time_t)0;
|
||||
hour.temperature = temp[i] | 0.0f;
|
||||
hour.precipitation = precip[i] | 0.0f;
|
||||
hour.precipitationProbability = precipProb[i] | 0;
|
||||
hour.weatherCode = codes[i] | 0;
|
||||
hour.isDay = (isDay[i] | 1) != 0;
|
||||
data.hourly.push_back(hour);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WeatherClient::saveCache(const WeatherData& data) {
|
||||
Storage.mkdir("/.crosspoint");
|
||||
|
||||
JsonDocument doc;
|
||||
doc["fetchedAt"] = data.fetchedAt;
|
||||
doc["timezone"] = data.timezone;
|
||||
doc["utcOffsetSeconds"] = data.utcOffsetSeconds;
|
||||
|
||||
// Current
|
||||
JsonObject cur = doc["current"].to<JsonObject>();
|
||||
cur["temperature"] = data.current.temperature;
|
||||
cur["apparentTemperature"] = data.current.apparentTemperature;
|
||||
cur["humidity"] = data.current.humidity;
|
||||
cur["weatherCode"] = data.current.weatherCode;
|
||||
cur["windSpeed"] = data.current.windSpeed;
|
||||
cur["windDirection"] = data.current.windDirection;
|
||||
cur["precipitation"] = data.current.precipitation;
|
||||
cur["uvIndex"] = data.current.uvIndex;
|
||||
cur["isDay"] = data.current.isDay;
|
||||
|
||||
// Daily
|
||||
JsonArray dailyArr = doc["daily"].to<JsonArray>();
|
||||
for (const auto& day : data.daily) {
|
||||
JsonObject d = dailyArr.add<JsonObject>();
|
||||
d["date"] = day.date;
|
||||
d["tempMax"] = day.tempMax;
|
||||
d["tempMin"] = day.tempMin;
|
||||
d["weatherCode"] = day.weatherCode;
|
||||
d["precipSum"] = day.precipSum;
|
||||
d["uvIndexMax"] = day.uvIndexMax;
|
||||
d["sunrise"] = day.sunrise;
|
||||
d["sunset"] = day.sunset;
|
||||
}
|
||||
|
||||
// Hourly
|
||||
JsonArray hourlyArr = doc["hourly"].to<JsonArray>();
|
||||
for (const auto& hour : data.hourly) {
|
||||
JsonObject h = hourlyArr.add<JsonObject>();
|
||||
h["time"] = hour.time;
|
||||
h["temperature"] = hour.temperature;
|
||||
h["precipitation"] = hour.precipitation;
|
||||
h["precipProb"] = hour.precipitationProbability;
|
||||
h["weatherCode"] = hour.weatherCode;
|
||||
h["isDay"] = hour.isDay;
|
||||
}
|
||||
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
return Storage.writeFile(WEATHER_CACHE_FILE, json);
|
||||
}
|
||||
|
||||
bool WeatherClient::loadCache(WeatherData& data) {
|
||||
if (!Storage.exists(WEATHER_CACHE_FILE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String json = Storage.readFile(WEATHER_CACHE_FILE);
|
||||
if (json.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
auto error = deserializeJson(doc, json);
|
||||
if (error) {
|
||||
LOG_ERR("WEA", "Cache parse error: %s", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
data.fetchedAt = doc["fetchedAt"] | (time_t)0;
|
||||
data.timezone = doc["timezone"] | std::string("");
|
||||
data.utcOffsetSeconds = doc["utcOffsetSeconds"] | 0;
|
||||
|
||||
// Current
|
||||
JsonObject cur = doc["current"];
|
||||
if (cur) {
|
||||
data.current.temperature = cur["temperature"] | 0.0f;
|
||||
data.current.apparentTemperature = cur["apparentTemperature"] | 0.0f;
|
||||
data.current.humidity = cur["humidity"] | 0;
|
||||
data.current.weatherCode = cur["weatherCode"] | 0;
|
||||
data.current.windSpeed = cur["windSpeed"] | 0.0f;
|
||||
data.current.windDirection = cur["windDirection"] | 0;
|
||||
data.current.precipitation = cur["precipitation"] | 0.0f;
|
||||
data.current.uvIndex = cur["uvIndex"] | 0.0f;
|
||||
data.current.isDay = cur["isDay"] | true;
|
||||
}
|
||||
|
||||
// Daily
|
||||
JsonArray dailyArr = doc["daily"].as<JsonArray>();
|
||||
for (JsonObject d : dailyArr) {
|
||||
DailyForecast day;
|
||||
day.date = d["date"] | (time_t)0;
|
||||
day.tempMax = d["tempMax"] | 0.0f;
|
||||
day.tempMin = d["tempMin"] | 0.0f;
|
||||
day.weatherCode = d["weatherCode"] | 0;
|
||||
day.precipSum = d["precipSum"] | 0.0f;
|
||||
day.uvIndexMax = d["uvIndexMax"] | 0.0f;
|
||||
day.sunrise = d["sunrise"] | (time_t)0;
|
||||
day.sunset = d["sunset"] | (time_t)0;
|
||||
data.daily.push_back(day);
|
||||
}
|
||||
|
||||
// Hourly
|
||||
JsonArray hourlyArr = doc["hourly"].as<JsonArray>();
|
||||
for (JsonObject h : hourlyArr) {
|
||||
HourlyForecast hour;
|
||||
hour.time = h["time"] | (time_t)0;
|
||||
hour.temperature = h["temperature"] | 0.0f;
|
||||
hour.precipitation = h["precipitation"] | 0.0f;
|
||||
hour.precipitationProbability = h["precipProb"] | 0;
|
||||
hour.weatherCode = h["weatherCode"] | 0;
|
||||
hour.isDay = h["isDay"] | true;
|
||||
data.hourly.push_back(hour);
|
||||
}
|
||||
|
||||
data.valid = true;
|
||||
LOG_DBG("WEA", "Cache loaded, fetchedAt=%ld", (long)data.fetchedAt);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<GeocodingResult> WeatherClient::searchCity(const std::string& query) {
|
||||
std::vector<GeocodingResult> results;
|
||||
|
||||
std::string url = "https://geocoding-api.open-meteo.com/v1/search?name=";
|
||||
// Simple URL encoding for the query
|
||||
for (char c : query) {
|
||||
if (c == ' ') {
|
||||
url += "%20";
|
||||
} else if (isalnum(c) || c == '-' || c == '_' || c == '.') {
|
||||
url += c;
|
||||
} else {
|
||||
char buf[4];
|
||||
snprintf(buf, sizeof(buf), "%%%02X", (unsigned char)c);
|
||||
url += buf;
|
||||
}
|
||||
}
|
||||
url += "&count=5&language=en";
|
||||
|
||||
std::string response;
|
||||
if (!HttpDownloader::fetchUrl(url, response)) {
|
||||
LOG_ERR("WEA", "Geocoding request failed");
|
||||
return results;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
auto error = deserializeJson(doc, response);
|
||||
if (error) {
|
||||
LOG_ERR("WEA", "Geocoding parse error: %s", error.c_str());
|
||||
return results;
|
||||
}
|
||||
|
||||
JsonArray arr = doc["results"].as<JsonArray>();
|
||||
for (JsonObject obj : arr) {
|
||||
GeocodingResult r;
|
||||
r.name = obj["name"] | std::string("");
|
||||
r.country = obj["country"] | std::string("");
|
||||
r.admin1 = obj["admin1"] | std::string("");
|
||||
r.latitude = obj["latitude"] | 0.0f;
|
||||
r.longitude = obj["longitude"] | 0.0f;
|
||||
results.push_back(r);
|
||||
}
|
||||
|
||||
LOG_DBG("WEA", "Geocoding found %zu results for '%s'", results.size(), query.c_str());
|
||||
return results;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
#include "WeatherData.h"
|
||||
#include "WeatherSettingsStore.h"
|
||||
|
||||
class WeatherClient {
|
||||
public:
|
||||
/// Fetch weather data, using cache if valid (< 30 min old).
|
||||
/// If forceRefresh is true, always fetches from the API.
|
||||
static WeatherData getWeather(const WeatherSettingsStore& settings, bool forceRefresh = false);
|
||||
|
||||
/// Search for cities by name via Open-Meteo geocoding API.
|
||||
/// Returns up to 5 results.
|
||||
static std::vector<GeocodingResult> searchCity(const std::string& query);
|
||||
|
||||
private:
|
||||
static WeatherData fetchFromApi(const WeatherSettingsStore& settings);
|
||||
static bool parseWeatherJson(const std::string& json, WeatherData& data);
|
||||
static bool saveCache(const WeatherData& data);
|
||||
static bool loadCache(WeatherData& data);
|
||||
|
||||
static constexpr int CACHE_TTL_SECONDS = 30 * 60; // 30 minutes
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct CurrentWeather {
|
||||
float temperature = 0;
|
||||
float apparentTemperature = 0;
|
||||
int humidity = 0;
|
||||
int weatherCode = 0;
|
||||
float windSpeed = 0;
|
||||
int windDirection = 0;
|
||||
float precipitation = 0;
|
||||
float uvIndex = 0;
|
||||
bool isDay = true;
|
||||
};
|
||||
|
||||
struct DailyForecast {
|
||||
time_t date = 0;
|
||||
float tempMax = 0;
|
||||
float tempMin = 0;
|
||||
int weatherCode = 0;
|
||||
float precipSum = 0;
|
||||
float uvIndexMax = 0;
|
||||
time_t sunrise = 0;
|
||||
time_t sunset = 0;
|
||||
};
|
||||
|
||||
struct HourlyForecast {
|
||||
time_t time = 0;
|
||||
float temperature = 0;
|
||||
float precipitation = 0;
|
||||
int precipitationProbability = 0;
|
||||
int weatherCode = 0;
|
||||
bool isDay = true;
|
||||
};
|
||||
|
||||
struct WeatherData {
|
||||
CurrentWeather current;
|
||||
std::vector<DailyForecast> daily;
|
||||
std::vector<HourlyForecast> hourly;
|
||||
std::string timezone;
|
||||
int utcOffsetSeconds = 0;
|
||||
time_t fetchedAt = 0;
|
||||
bool valid = false;
|
||||
std::string errorMessage;
|
||||
};
|
||||
|
||||
struct GeocodingResult {
|
||||
std::string name;
|
||||
std::string country;
|
||||
std::string admin1; // State/region
|
||||
float latitude = 0;
|
||||
float longitude = 0;
|
||||
};
|
||||
@@ -0,0 +1,409 @@
|
||||
#include "WeatherIcons.h"
|
||||
|
||||
#include "WeatherIcons48.h"
|
||||
|
||||
// Weather icon glyph source attribution:
|
||||
// https://github.com/erikflowers/weather-icons
|
||||
// 64x64 monochrome icon bitmaps in this file are adapted from that icon set.
|
||||
|
||||
// ============================================================================
|
||||
// 64x64 Large Weather Icons (1-bit, MSB-first, row-major)
|
||||
// Each row = 64 pixels = 8 bytes. Total = 64 * 8 = 512 bytes per icon.
|
||||
// Pixel ON (1) = white/clear, Pixel OFF (0) = black/drawn.
|
||||
// These are "inverted" bitmaps - 0 bits are drawn as black pixels.
|
||||
// ============================================================================
|
||||
|
||||
// Clear Day - Sun with rays
|
||||
// clang-format off
|
||||
static const uint8_t ICON_CLEAR_DAY_48[] = {
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF,
|
||||
0xFF,0x8F,0xFE,0x7F,0xF1,0xFF, 0xFF,0x1F,0xFE,0x7F,0xF8,0xFF, 0xFF,0x3F,0x80,0x01,0xFC,0xFF,
|
||||
0xFF,0x7F,0x00,0x00,0xFE,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF,
|
||||
0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF,
|
||||
0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xE0,0xC0,0x00,0x00,0x03,0x07,
|
||||
0xE0,0x80,0x00,0x00,0x01,0x07, 0xF0,0x80,0x00,0x00,0x01,0x0F, 0xF8,0x80,0x00,0x00,0x01,0x1F,
|
||||
0xFC,0x80,0x00,0x00,0x01,0x3F, 0xFC,0x80,0x00,0x00,0x01,0x3F, 0xF8,0x80,0x00,0x00,0x01,0x1F,
|
||||
0xF0,0x80,0x00,0x00,0x01,0x0F, 0xE0,0x80,0x00,0x00,0x01,0x07, 0xE0,0xC0,0x00,0x00,0x03,0x07,
|
||||
0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF,
|
||||
0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF,
|
||||
0xFF,0xFC,0x00,0x00,0x3F,0xFF, 0xFF,0x7F,0x00,0x00,0xFE,0xFF, 0xFF,0x3F,0x80,0x01,0xFC,0xFF,
|
||||
0xFF,0x1F,0xFE,0x7F,0xF8,0xFF, 0xFF,0x8F,0xFE,0x7F,0xF1,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Clear Night - Moon crescent
|
||||
static const uint8_t ICON_CLEAR_NIGHT_48[] = {
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFC,0x0F,0xFF,0xFF, 0xFF,0xFF,0xF0,0x03,0xFF,0xFF,
|
||||
0xFF,0xFF,0xE0,0x01,0xFF,0xFF, 0xFF,0xFF,0xC0,0x00,0xFF,0xFF, 0xFF,0xFF,0x80,0x00,0x7F,0xFF,
|
||||
0xFF,0xFF,0x00,0x00,0x3F,0xFF, 0xFF,0xFF,0x00,0x00,0x3F,0xFF, 0xFF,0xFE,0x00,0x00,0x1F,0xFF,
|
||||
0xFF,0xFE,0x00,0x00,0x3F,0xFF, 0xFF,0xFC,0x00,0x00,0x7F,0xFF, 0xFF,0xFC,0x00,0x00,0xFF,0xFF,
|
||||
0xFF,0xFC,0x00,0x01,0xFF,0xFF, 0xFF,0xF8,0x00,0x03,0xFF,0xFF, 0xFF,0xF8,0x00,0x07,0xFF,0xFF,
|
||||
0xFF,0xF8,0x00,0x07,0xFF,0xFF, 0xFF,0xF8,0x00,0x0F,0xFF,0xFF, 0xFF,0xF8,0x00,0x0F,0xFF,0xFF,
|
||||
0xFF,0xF8,0x00,0x0F,0xFF,0xFF, 0xFF,0xF8,0x00,0x0F,0xFF,0xFF, 0xFF,0xF8,0x00,0x0F,0xFF,0xFF,
|
||||
0xFF,0xF8,0x00,0x0F,0xFF,0xFF, 0xFF,0xF8,0x00,0x07,0xFF,0xFF, 0xFF,0xF8,0x00,0x07,0xFF,0xFF,
|
||||
0xFF,0xF8,0x00,0x03,0xFF,0xFF, 0xFF,0xFC,0x00,0x01,0xFF,0xFF, 0xFF,0xFC,0x00,0x00,0xFF,0xFF,
|
||||
0xFF,0xFC,0x00,0x00,0x7F,0xFF, 0xFF,0xFE,0x00,0x00,0x3F,0xFF, 0xFF,0xFE,0x00,0x00,0x1F,0xFF,
|
||||
0xFF,0xFF,0x00,0x00,0x1F,0xFF, 0xFF,0xFF,0x00,0x00,0x3F,0xFF, 0xFF,0xFF,0x80,0x00,0x7F,0xFF,
|
||||
0xFF,0xFF,0xC0,0x00,0xFF,0xFF, 0xFF,0xFF,0xE0,0x01,0xFF,0xFF, 0xFF,0xFF,0xF0,0x03,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFC,0x0F,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Partly Cloudy Day - Sun behind cloud
|
||||
static const uint8_t ICON_PARTLY_CLOUDY_DAY_48[] = {
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0x9F,0xFE,0x7F,0xF9,0xFF,
|
||||
0xFF,0x3F,0xFE,0x7F,0xFC,0xFF, 0xFF,0x7F,0x80,0x01,0xFE,0xFF, 0xFF,0xFF,0x00,0x00,0xFF,0xFF,
|
||||
0xFF,0xFC,0x00,0x00,0x3F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF,
|
||||
0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xE0,0xE0,0x00,0x00,0x07,0x07, 0xF0,0xE0,0x00,0x00,0x07,0x0F,
|
||||
0xF8,0xE0,0x00,0x00,0x07,0x1F, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF,
|
||||
0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF,
|
||||
0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF,
|
||||
0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFE,0x00,0x00,0x00,0x00,0x7F,
|
||||
0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F,
|
||||
0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F,
|
||||
0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFF,0x00,0x00,0x00,0x00,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF,
|
||||
0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Partly Cloudy Night - Moon behind cloud (reuse partly cloudy day for simplicity)
|
||||
static const uint8_t* ICON_PARTLY_CLOUDY_NIGHT_48 = ICON_PARTLY_CLOUDY_DAY_48;
|
||||
|
||||
// Overcast - Full cloud
|
||||
static const uint8_t ICON_OVERCAST_48[] = {
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFC,0x3F,0xFF,0xFF,
|
||||
0xFF,0xFF,0xF0,0x0F,0xFF,0xFF, 0xFF,0xFF,0xE0,0x07,0xFF,0xFF, 0xFF,0xFF,0xC0,0x03,0xFF,0xFF,
|
||||
0xFF,0xFF,0x80,0x01,0xFF,0xFF, 0xFF,0xFF,0x00,0x00,0xFF,0xFF, 0xFF,0xFF,0x00,0x00,0xFF,0xFF,
|
||||
0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF,
|
||||
0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFE,0x00,0x00,0x00,0x00,0x7F,
|
||||
0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F,
|
||||
0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F,
|
||||
0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F,
|
||||
0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFF,0x00,0x00,0x00,0x00,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF,
|
||||
0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Rain - Cloud with rain drops
|
||||
static const uint8_t ICON_RAIN_48[] = {
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFC,0x3F,0xFF,0xFF, 0xFF,0xFF,0xF0,0x0F,0xFF,0xFF,
|
||||
0xFF,0xFF,0xE0,0x07,0xFF,0xFF, 0xFF,0xFF,0xC0,0x03,0xFF,0xFF, 0xFF,0xFF,0x80,0x01,0xFF,0xFF,
|
||||
0xFF,0xFF,0x00,0x00,0xFF,0xFF, 0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF,
|
||||
0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFE,0x00,0x00,0x00,0x00,0x7F,
|
||||
0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F,
|
||||
0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFF,0x00,0x00,0x00,0x00,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFB,0xDF,0xBF,0x7F,0xFF,
|
||||
0xFF,0xF3,0xCF,0x9F,0x3F,0xFF, 0xFF,0xF3,0xCF,0x9F,0x3F,0xFF, 0xFF,0xE7,0xE7,0xCF,0x9F,0xFF,
|
||||
0xFF,0xE7,0xE7,0xCF,0x9F,0xFF, 0xFF,0xCF,0xF3,0xE7,0xCF,0xFF, 0xFF,0xCF,0xF3,0xE7,0xCF,0xFF,
|
||||
0xFF,0xCF,0xF3,0xE7,0xCF,0xFF, 0xFF,0xDF,0xFB,0xF7,0xEF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Snow - Cloud with snowflakes
|
||||
static const uint8_t ICON_SNOW_48[] = {
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFC,0x3F,0xFF,0xFF, 0xFF,0xFF,0xF0,0x0F,0xFF,0xFF,
|
||||
0xFF,0xFF,0xE0,0x07,0xFF,0xFF, 0xFF,0xFF,0xC0,0x03,0xFF,0xFF, 0xFF,0xFF,0x80,0x01,0xFF,0xFF,
|
||||
0xFF,0xFF,0x00,0x00,0xFF,0xFF, 0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF,
|
||||
0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFE,0x00,0x00,0x00,0x00,0x7F,
|
||||
0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F,
|
||||
0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFF,0x00,0x00,0x00,0x00,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF9,0xE7,0xCF,0x3F,0xFF,
|
||||
0xFF,0xFC,0xF3,0xE7,0x9F,0xFF, 0xFF,0xFE,0x79,0xF3,0xCF,0xFF, 0xFF,0xFF,0x3C,0xF9,0xE7,0xFF,
|
||||
0xFF,0xFE,0x79,0xF3,0xCF,0xFF, 0xFF,0xFC,0xF3,0xE7,0x9F,0xFF, 0xFF,0xF9,0xE7,0xCF,0x3F,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Thunderstorm - Cloud with lightning bolt
|
||||
static const uint8_t ICON_THUNDERSTORM_48[] = {
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFC,0x3F,0xFF,0xFF, 0xFF,0xFF,0xF0,0x0F,0xFF,0xFF,
|
||||
0xFF,0xFF,0xE0,0x07,0xFF,0xFF, 0xFF,0xFF,0xC0,0x03,0xFF,0xFF, 0xFF,0xFF,0x80,0x01,0xFF,0xFF,
|
||||
0xFF,0xFF,0x00,0x00,0xFF,0xFF, 0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF,
|
||||
0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFE,0x00,0x00,0x00,0x00,0x7F,
|
||||
0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F,
|
||||
0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFF,0x00,0x00,0x00,0x00,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xF8,0xFF,0xFF,0xFF, 0xFF,0xFF,0xF1,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xE3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xC7,0xFF,0xFF,0xFF, 0xFF,0xFF,0x80,0x3F,0xFF,0xFF,
|
||||
0xFF,0xFF,0x00,0x7F,0xFF,0xFF, 0xFF,0xFF,0xF0,0xFF,0xFF,0xFF, 0xFF,0xFF,0xE1,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xC3,0xFF,0xFF,0xFF, 0xFF,0xFF,0x87,0xFF,0xFF,0xFF, 0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0x1F,0xFF,0xFF,0xFF, 0xFF,0xFF,0x3F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Fog - Horizontal lines
|
||||
static const uint8_t ICON_FOG_48[] = {
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF,
|
||||
0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF,
|
||||
0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
|
||||
// ============================================================================
|
||||
// 24x24 Small Weather Icons (1-bit, MSB-first)
|
||||
// Each row = 24 pixels = 3 bytes. Total = 24 * 3 = 72 bytes per icon.
|
||||
// ============================================================================
|
||||
|
||||
#if WEATHER_ENABLE_SMALL_ICONS
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Small Clear Day - Sun
|
||||
static const uint8_t ICON_CLEAR_DAY_24[] = {
|
||||
0xFF,0xFF,0xFF, 0xFF,0xE7,0xFF, 0xFF,0xE7,0xFF, 0xFF,0xE7,0xFF,
|
||||
0xF3,0xE7,0xCF, 0xF9,0x00,0x9F, 0xFF,0x00,0xFF, 0xFE,0x00,0x7F,
|
||||
0xFC,0x00,0x3F, 0xFC,0x00,0x3F, 0xE0,0x00,0x07, 0xE0,0x00,0x07,
|
||||
0xE0,0x00,0x07, 0xE0,0x00,0x07, 0xFC,0x00,0x3F, 0xFC,0x00,0x3F,
|
||||
0xFE,0x00,0x7F, 0xFF,0x00,0xFF, 0xF9,0x00,0x9F, 0xF3,0xE7,0xCF,
|
||||
0xFF,0xE7,0xFF, 0xFF,0xE7,0xFF, 0xFF,0xE7,0xFF, 0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Small Clear Night - Moon
|
||||
static const uint8_t ICON_CLEAR_NIGHT_24[] = {
|
||||
0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xF0,0xFF, 0xFF,0xE0,0x7F,
|
||||
0xFF,0xC0,0x3F, 0xFF,0x80,0x3F, 0xFF,0x80,0x7F, 0xFF,0x00,0xFF,
|
||||
0xFF,0x01,0xFF, 0xFF,0x03,0xFF, 0xFF,0x03,0xFF, 0xFF,0x03,0xFF,
|
||||
0xFF,0x03,0xFF, 0xFF,0x03,0xFF, 0xFF,0x01,0xFF, 0xFF,0x00,0xFF,
|
||||
0xFF,0x80,0x7F, 0xFF,0x80,0x3F, 0xFF,0xC0,0x3F, 0xFF,0xE0,0x7F,
|
||||
0xFF,0xF0,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Small Partly Cloudy
|
||||
static const uint8_t ICON_PARTLY_CLOUDY_24[] = {
|
||||
0xFF,0xFF,0xFF, 0xFF,0xE7,0xFF, 0xFB,0xE7,0xDF, 0xFD,0x00,0xBF,
|
||||
0xFF,0x00,0xFF, 0xFE,0x00,0x7F, 0xFE,0x00,0x7F, 0xFC,0x00,0x3F,
|
||||
0xFF,0x00,0x7F, 0xFE,0x00,0x3F, 0xFC,0x00,0x1F, 0xF8,0x00,0x0F,
|
||||
0xF0,0x00,0x0F, 0xF0,0x00,0x07, 0xE0,0x00,0x07, 0xE0,0x00,0x07,
|
||||
0xE0,0x00,0x07, 0xE0,0x00,0x07, 0xF0,0x00,0x0F, 0xF8,0x00,0x1F,
|
||||
0xFF,0x00,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Small Overcast - Cloud
|
||||
static const uint8_t ICON_OVERCAST_24[] = {
|
||||
0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
|
||||
0xFF,0xF0,0xFF, 0xFF,0xE0,0x7F, 0xFF,0xC0,0x3F, 0xFF,0x80,0x1F,
|
||||
0xFF,0x00,0x1F, 0xFE,0x00,0x0F, 0xFC,0x00,0x07, 0xF8,0x00,0x07,
|
||||
0xF0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03,
|
||||
0xE0,0x00,0x03, 0xF0,0x00,0x07, 0xF8,0x00,0x0F, 0xFE,0x00,0x7F,
|
||||
0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Small Rain
|
||||
static const uint8_t ICON_RAIN_24[] = {
|
||||
0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xF0,0xFF, 0xFF,0xE0,0x7F,
|
||||
0xFF,0x80,0x1F, 0xFF,0x00,0x0F, 0xFE,0x00,0x07, 0xFC,0x00,0x07,
|
||||
0xF0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03,
|
||||
0xF0,0x00,0x07, 0xF8,0x00,0x0F, 0xFF,0x00,0xFF, 0xFF,0xFF,0xFF,
|
||||
0xFE,0xF7,0xBF, 0xFD,0xEF,0x7F, 0xFD,0xEF,0x7F, 0xFB,0xDF,0x7F,
|
||||
0xFB,0xDF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Small Snow
|
||||
static const uint8_t ICON_SNOW_24[] = {
|
||||
0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xF0,0xFF, 0xFF,0xE0,0x7F,
|
||||
0xFF,0x80,0x1F, 0xFF,0x00,0x0F, 0xFE,0x00,0x07, 0xFC,0x00,0x07,
|
||||
0xF0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03,
|
||||
0xF0,0x00,0x07, 0xF8,0x00,0x0F, 0xFF,0x00,0xFF, 0xFF,0xFF,0xFF,
|
||||
0xFE,0xF7,0xBF, 0xFF,0x7B,0xDF, 0xFE,0xF7,0xBF, 0xFF,0x7B,0xDF,
|
||||
0xFE,0xF7,0xBF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Small Thunderstorm
|
||||
static const uint8_t ICON_THUNDERSTORM_24[] = {
|
||||
0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xF0,0xFF, 0xFF,0xE0,0x7F,
|
||||
0xFF,0x80,0x1F, 0xFF,0x00,0x0F, 0xFE,0x00,0x07, 0xFC,0x00,0x07,
|
||||
0xF0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03,
|
||||
0xF0,0x00,0x07, 0xF8,0x00,0x0F, 0xFF,0x00,0xFF, 0xFF,0xFF,0xFF,
|
||||
0xFF,0xF1,0xFF, 0xFF,0xE3,0xFF, 0xFF,0xC0,0xFF, 0xFF,0xF1,0xFF,
|
||||
0xFF,0xE3,0xFF, 0xFF,0xC7,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// Small Fog
|
||||
static const uint8_t ICON_FOG_24[] = {
|
||||
0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xF0,0x00,0x0F, 0xE0,0x00,0x07,
|
||||
0xF0,0x00,0x0F, 0xFF,0xFF,0xFF, 0xF8,0x00,0x1F, 0xF0,0x00,0x0F,
|
||||
0xF8,0x00,0x1F, 0xFF,0xFF,0xFF, 0xF0,0x00,0x0F, 0xE0,0x00,0x07,
|
||||
0xF0,0x00,0x0F, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
|
||||
0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
|
||||
#endif // WEATHER_ENABLE_SMALL_ICONS
|
||||
|
||||
// ============================================================================
|
||||
// Lookup functions
|
||||
// ============================================================================
|
||||
|
||||
const uint8_t* getWeatherIconLarge(WeatherIconType type) {
|
||||
switch (type) {
|
||||
case WeatherIconType::CLEAR_DAY:
|
||||
return WI48_CLEAR_DAY;
|
||||
case WeatherIconType::CLEAR_NIGHT:
|
||||
return WI48_CLEAR_NIGHT;
|
||||
case WeatherIconType::PARTLY_CLOUDY_DAY:
|
||||
return WI48_PARTLY_CLOUDY_DAY;
|
||||
case WeatherIconType::PARTLY_CLOUDY_NIGHT:
|
||||
return WI48_PARTLY_CLOUDY_NIGHT;
|
||||
case WeatherIconType::OVERCAST:
|
||||
return WI48_OVERCAST;
|
||||
case WeatherIconType::FOG:
|
||||
return WI48_FOG;
|
||||
case WeatherIconType::DRIZZLE:
|
||||
return WI48_DRIZZLE;
|
||||
case WeatherIconType::RAIN:
|
||||
case WeatherIconType::SHOWERS:
|
||||
return WI48_RAIN;
|
||||
case WeatherIconType::SNOW:
|
||||
return WI48_SNOW;
|
||||
case WeatherIconType::THUNDERSTORM:
|
||||
return WI48_THUNDERSTORM;
|
||||
default:
|
||||
return WI48_OVERCAST;
|
||||
}
|
||||
}
|
||||
|
||||
#if WEATHER_ENABLE_SMALL_ICONS
|
||||
const uint8_t* getWeatherIconSmall(WeatherIconType type) {
|
||||
switch (type) {
|
||||
case WeatherIconType::CLEAR_DAY:
|
||||
return ICON_CLEAR_DAY_24;
|
||||
case WeatherIconType::CLEAR_NIGHT:
|
||||
return ICON_CLEAR_NIGHT_24;
|
||||
case WeatherIconType::PARTLY_CLOUDY_DAY:
|
||||
case WeatherIconType::PARTLY_CLOUDY_NIGHT:
|
||||
return ICON_PARTLY_CLOUDY_24;
|
||||
case WeatherIconType::OVERCAST:
|
||||
return ICON_OVERCAST_24;
|
||||
case WeatherIconType::FOG:
|
||||
return ICON_FOG_24;
|
||||
case WeatherIconType::DRIZZLE:
|
||||
case WeatherIconType::RAIN:
|
||||
case WeatherIconType::SHOWERS:
|
||||
return ICON_RAIN_24;
|
||||
case WeatherIconType::SNOW:
|
||||
return ICON_SNOW_24;
|
||||
case WeatherIconType::THUNDERSTORM:
|
||||
return ICON_THUNDERSTORM_24;
|
||||
default:
|
||||
return ICON_OVERCAST_24;
|
||||
}
|
||||
}
|
||||
#endif // WEATHER_ENABLE_SMALL_ICONS
|
||||
|
||||
const char* getWeatherDescription(int wmoCode) {
|
||||
switch (wmoCode) {
|
||||
case 0:
|
||||
return "Clear sky";
|
||||
case 1:
|
||||
return "Mainly clear";
|
||||
case 2:
|
||||
return "Partly cloudy";
|
||||
case 3:
|
||||
return "Overcast";
|
||||
case 45:
|
||||
return "Fog";
|
||||
case 48:
|
||||
return "Rime fog";
|
||||
case 51:
|
||||
return "Light drizzle";
|
||||
case 53:
|
||||
return "Drizzle";
|
||||
case 55:
|
||||
return "Dense drizzle";
|
||||
case 56:
|
||||
return "Freezing drizzle";
|
||||
case 57:
|
||||
return "Dense freezing drizzle";
|
||||
case 61:
|
||||
return "Slight rain";
|
||||
case 63:
|
||||
return "Moderate rain";
|
||||
case 65:
|
||||
return "Heavy rain";
|
||||
case 66:
|
||||
return "Freezing rain";
|
||||
case 67:
|
||||
return "Heavy freezing rain";
|
||||
case 71:
|
||||
return "Slight snow";
|
||||
case 73:
|
||||
return "Moderate snow";
|
||||
case 75:
|
||||
return "Heavy snow";
|
||||
case 77:
|
||||
return "Snow grains";
|
||||
case 80:
|
||||
return "Slight showers";
|
||||
case 81:
|
||||
return "Moderate showers";
|
||||
case 82:
|
||||
return "Violent showers";
|
||||
case 85:
|
||||
return "Snow showers";
|
||||
case 86:
|
||||
return "Heavy snow showers";
|
||||
case 95:
|
||||
return "Thunderstorm";
|
||||
case 96:
|
||||
return "Thunderstorm, hail";
|
||||
case 99:
|
||||
return "Thunderstorm, heavy hail";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
const char* getWindDirectionText(int degrees) {
|
||||
// Normalize to 0-360
|
||||
degrees = ((degrees % 360) + 360) % 360;
|
||||
if (degrees >= 338 || degrees < 23) return "N";
|
||||
if (degrees < 68) return "NE";
|
||||
if (degrees < 113) return "E";
|
||||
if (degrees < 158) return "SE";
|
||||
if (degrees < 203) return "S";
|
||||
if (degrees < 248) return "SW";
|
||||
if (degrees < 293) return "W";
|
||||
return "NW";
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
// Set to 1 (for example via build flag -DWEATHER_ENABLE_SMALL_ICONS=1)
|
||||
// to include 24x24 weather icon assets and API.
|
||||
#ifndef WEATHER_ENABLE_SMALL_ICONS
|
||||
#define WEATHER_ENABLE_SMALL_ICONS 0
|
||||
#endif
|
||||
|
||||
// Weather icon size constants
|
||||
constexpr int WEATHER_ICON_LARGE = 64; // Large icon for current weather
|
||||
constexpr int WEATHER_ICON_SMALL = 24; // Small icon for daily forecast
|
||||
|
||||
// WMO weather code to icon category mapping
|
||||
enum class WeatherIconType {
|
||||
CLEAR_DAY,
|
||||
CLEAR_NIGHT,
|
||||
PARTLY_CLOUDY_DAY,
|
||||
PARTLY_CLOUDY_NIGHT,
|
||||
OVERCAST,
|
||||
FOG,
|
||||
DRIZZLE,
|
||||
RAIN,
|
||||
SNOW,
|
||||
SHOWERS,
|
||||
THUNDERSTORM,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
// Map WMO weather code + day/night to icon type
|
||||
inline WeatherIconType getWeatherIconType(int wmoCode, bool isDay) {
|
||||
switch (wmoCode) {
|
||||
case 0:
|
||||
return isDay ? WeatherIconType::CLEAR_DAY : WeatherIconType::CLEAR_NIGHT;
|
||||
case 1:
|
||||
case 2:
|
||||
return isDay ? WeatherIconType::PARTLY_CLOUDY_DAY : WeatherIconType::PARTLY_CLOUDY_NIGHT;
|
||||
case 3:
|
||||
return WeatherIconType::OVERCAST;
|
||||
case 45:
|
||||
case 48:
|
||||
return WeatherIconType::FOG;
|
||||
case 51:
|
||||
case 53:
|
||||
case 55:
|
||||
case 56:
|
||||
case 57:
|
||||
return WeatherIconType::DRIZZLE;
|
||||
case 61:
|
||||
case 63:
|
||||
case 65:
|
||||
case 66:
|
||||
case 67:
|
||||
return WeatherIconType::RAIN;
|
||||
case 71:
|
||||
case 73:
|
||||
case 75:
|
||||
case 77:
|
||||
return WeatherIconType::SNOW;
|
||||
case 80:
|
||||
case 81:
|
||||
case 82:
|
||||
case 85:
|
||||
case 86:
|
||||
return WeatherIconType::SHOWERS;
|
||||
case 95:
|
||||
case 96:
|
||||
case 99:
|
||||
return WeatherIconType::THUNDERSTORM;
|
||||
default:
|
||||
return WeatherIconType::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// Get human-readable description for WMO weather code
|
||||
const char* getWeatherDescription(int wmoCode);
|
||||
|
||||
// Get the appropriate large icon bitmap (64x64, 1-bit, MSB first)
|
||||
const uint8_t* getWeatherIconLarge(WeatherIconType type);
|
||||
|
||||
// Get the appropriate small icon bitmap (24x24, 1-bit, MSB first)
|
||||
#if WEATHER_ENABLE_SMALL_ICONS
|
||||
const uint8_t* getWeatherIconSmall(WeatherIconType type);
|
||||
#endif
|
||||
|
||||
// Wind direction arrow text (N, NE, E, SE, S, SW, W, NW)
|
||||
const char* getWindDirectionText(int degrees);
|
||||
@@ -0,0 +1,307 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
// Generated from erikflowers/weather-icons SVGs.
|
||||
// 48x48, 1-bit, MSB-first, row-major.
|
||||
// Regenerate with: python scripts/generate_weather_icons.py --fetch
|
||||
// clang-format off
|
||||
|
||||
static const uint8_t WI48_CLEAR_DAY[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xE1, 0xFF, 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x81,
|
||||
0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xE0,
|
||||
0x3F, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xF8, 0x3F, 0xF8, 0x3F,
|
||||
0xFC, 0x1F, 0xFF, 0xFF, 0xFC, 0x7F, 0xC0, 0x03, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
|
||||
0x01, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFC, 0x07,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF,
|
||||
0xFF, 0xC1, 0xFF, 0xFF, 0xC0, 0x3F, 0x83, 0xFF, 0xFF, 0xC1, 0xFC, 0x03, 0x80, 0x1F, 0x03, 0xFF, 0xFF, 0xE1, 0xF8,
|
||||
0x01, 0x00, 0x1F, 0x07, 0xFF, 0xFF, 0xE0, 0xF8, 0x00, 0x00, 0x1F, 0x07, 0xFF, 0xFF, 0xE0, 0xF8, 0x00, 0x80, 0x1F,
|
||||
0x03, 0xFF, 0xFF, 0xE1, 0xF8, 0x01, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF,
|
||||
0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
|
||||
0x3F, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0x80, 0x1F,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x7F, 0xC0, 0x03, 0xFE, 0x3F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFE,
|
||||
0x7F, 0xFC, 0x1F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xFC, 0x07,
|
||||
0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0x81,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0xC7, 0xFF, 0xFC, 0x3F,
|
||||
0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static const uint8_t WI48_CLEAR_NIGHT[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
|
||||
0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x03, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xE0, 0x00, 0x06, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x7E, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFE,
|
||||
0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0x01, 0xFF, 0xFF,
|
||||
0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xF8, 0x00,
|
||||
0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0x80,
|
||||
0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF,
|
||||
0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF,
|
||||
0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFC, 0x00,
|
||||
0x7F, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0x80,
|
||||
0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x01, 0x00, 0x7F, 0xFF, 0xFF,
|
||||
0xFF, 0xF0, 0x00, 0x01, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00,
|
||||
0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0x00, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x7F, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x01, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01,
|
||||
0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0x80, 0x3F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xF8, 0x07, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0xE0,
|
||||
0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xF0, 0x03, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFE, 0x00,
|
||||
0x3F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0x00,
|
||||
0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFE,
|
||||
0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x07, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
|
||||
0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00,
|
||||
0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x1F,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static const uint8_t WI48_PARTLY_CLOUDY_DAY[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xF9, 0xFF, 0xF8, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF,
|
||||
0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xC0, 0xFF,
|
||||
0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0x01,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x7F, 0xFF, 0xFF,
|
||||
0xF8, 0x00, 0x00, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x1F, 0xE0, 0x3F, 0xF8, 0x3F, 0xFF, 0xFF, 0xE0, 0x7F, 0xF8,
|
||||
0x3F, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFC, 0x1F, 0xFC, 0x1F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFE, 0x0F, 0xFE, 0x1F,
|
||||
0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0x0F, 0xFE, 0x1E, 0x00, 0xFF, 0x83, 0xFF, 0xFF, 0x0F, 0xFE, 0x1E, 0x00, 0xFF, 0x87,
|
||||
0xFF, 0xFF, 0x87, 0xFE, 0x1E, 0x00, 0xFE, 0x07, 0xFF, 0xFF, 0x80, 0x3E, 0x1F, 0xEF, 0xF8, 0x07, 0xFF, 0xFF, 0x80,
|
||||
0x0C, 0x1F, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0x80, 0x04, 0x3F, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xDC, 0x00, 0x3F, 0xFF,
|
||||
0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0x83, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
|
||||
0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x71, 0xFF, 0x07, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xF8, 0x70, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x70, 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8,
|
||||
0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFC, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xE0, 0x7F,
|
||||
0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00,
|
||||
0x07, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static const uint8_t WI48_PARTLY_CLOUDY_NIGHT[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x0F, 0x83, 0xFF, 0xFF, 0xFF,
|
||||
0xFC, 0x00, 0x00, 0x3F, 0x83, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00,
|
||||
0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x00, 0x1C, 0x00, 0x7F, 0xC0, 0xFF,
|
||||
0xFF, 0xFE, 0x01, 0xFF, 0x80, 0x3F, 0xE0, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, 0xE0, 0x7F, 0xFF, 0xFC, 0x0F,
|
||||
0xFF, 0xF8, 0x1F, 0xF0, 0x3F, 0xFF, 0xF8, 0x1F, 0xFF, 0xFC, 0x0F, 0xF0, 0x0F, 0xFF, 0xF8, 0x3F, 0xFF, 0xFC, 0x0F,
|
||||
0xF8, 0x01, 0xFF, 0xF0, 0x3F, 0xFF, 0xFE, 0x07, 0xFC, 0x00, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0x07, 0xFE, 0x00, 0xFF,
|
||||
0xE0, 0x7F, 0xFF, 0xFF, 0x03, 0xFF, 0x81, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xC1, 0xFF, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0x80, 0x07, 0x81, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0x03, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
|
||||
0x03, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x07, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xC0, 0x7F,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFC, 0x0F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F,
|
||||
0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0x83,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xC1, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFC, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
|
||||
0x3F, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static const uint8_t WI48_OVERCAST[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F,
|
||||
0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0x3F, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFE, 0x0F, 0xFF,
|
||||
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFC, 0x01,
|
||||
0x80, 0x3F, 0xFF, 0x00, 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0x00, 0x1F, 0xFF, 0xF0, 0x3F, 0xFC, 0x0F, 0xFF,
|
||||
0x80, 0x0F, 0xFF, 0xE0, 0xFF, 0xFE, 0x07, 0xFF, 0x80, 0x07, 0xFF, 0xE0, 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x03, 0xFF,
|
||||
0xC1, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xC3, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xC3, 0xFF, 0xFF,
|
||||
0xC0, 0xFF, 0xFF, 0xE1, 0xFE, 0x03, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xE0, 0xF8, 0x07, 0xFF, 0xFF, 0xC0, 0x03, 0xFF,
|
||||
0xF0, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xF0, 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xF0, 0xC0, 0x7F,
|
||||
0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xF0, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, 0x83, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xF8, 0x3F, 0xE1, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xC1, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0x83,
|
||||
0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1C, 0x03, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x07, 0x0F, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFE, 0x18, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||
0x18, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0x83,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xE0, 0x7F, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
|
||||
0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xC0,
|
||||
0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static const uint8_t WI48_FOG[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
|
||||
0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xF0, 0x1F, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFC, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFE,
|
||||
0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFE, 0x07,
|
||||
0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xC0,
|
||||
0x01, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF,
|
||||
0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0x07, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static const uint8_t WI48_DRIZZLE[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00,
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x00, 0x1F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xFF, 0xFC,
|
||||
0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF,
|
||||
0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF,
|
||||
0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0x80, 0x7F,
|
||||
0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0,
|
||||
0x00, 0x3F, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x0F, 0xFF, 0xFF, 0xCF, 0xFF, 0xE0, 0x0F, 0xE0,
|
||||
0x3F, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, 0x87, 0xFF, 0xFE, 0x03, 0xC0, 0xFF, 0xFF, 0xFF,
|
||||
0x03, 0xFF, 0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0x81, 0x83, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF,
|
||||
0xC1, 0x83, 0xFF, 0xFC, 0xFF, 0x87, 0xFF, 0xFF, 0xC1, 0x03, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF,
|
||||
0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xE0, 0x1F, 0xFF,
|
||||
0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xC0,
|
||||
0x83, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xC1, 0x81, 0xFF, 0xE0, 0x1F, 0xCF, 0xFF, 0xFF, 0xC1, 0x81, 0xFF, 0xF0,
|
||||
0x3F, 0x87, 0xFF, 0xFF, 0x81, 0xC0, 0xFF, 0xF8, 0x7F, 0x03, 0xFF, 0xFF, 0x03, 0xC0, 0x7F, 0xFF, 0xFE, 0x03, 0xFF,
|
||||
0xFE, 0x03, 0xE0, 0x3F, 0xFF, 0xFC, 0x01, 0xFF, 0xFC, 0x07, 0xF0, 0x0F, 0xFF, 0xFC, 0x00, 0xFF, 0xF0, 0x0F, 0xF8,
|
||||
0x01, 0xFF, 0xF8, 0x00, 0xFF, 0x80, 0x0F, 0xFC, 0x01, 0xFF, 0xF0, 0x00, 0x7F, 0x80, 0x3F, 0xFE, 0x01, 0xFF, 0xF0,
|
||||
0x00, 0x7F, 0x80, 0x7F, 0xFF, 0x81, 0xFF, 0xF0, 0x00, 0x3F, 0x80, 0xFF, 0xFF, 0xE1, 0xFF, 0xF0, 0x00, 0x3F, 0x87,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static const uint8_t WI48_RAIN[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0x00, 0x1F, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF,
|
||||
0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF,
|
||||
0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0,
|
||||
0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF,
|
||||
0xE0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F,
|
||||
0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xE0, 0x1F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x83,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xE0, 0x07, 0xFF, 0xFC, 0x3F, 0x8F, 0xE1, 0xFF, 0xE0, 0x03, 0xFF, 0xFC, 0x1F, 0x07, 0xC1, 0xFF, 0xC0, 0x03, 0xFF,
|
||||
0xF8, 0x1F, 0x07, 0xC1, 0xFF, 0xC0, 0x83, 0xFF, 0xF8, 0x1F, 0x07, 0xC1, 0xFF, 0xC1, 0x81, 0xFF, 0xF8, 0x3E, 0x07,
|
||||
0xC1, 0xFF, 0x81, 0x80, 0xFF, 0xF8, 0x3E, 0x0F, 0x81, 0xFF, 0x01, 0xC0, 0xFF, 0xF0, 0x3E, 0x0F, 0x83, 0xFF, 0x03,
|
||||
0xE0, 0x3F, 0xF0, 0x3E, 0x0F, 0x83, 0xFC, 0x07, 0xE0, 0x1F, 0xF0, 0x7C, 0x0F, 0x03, 0xF8, 0x07, 0xF0, 0x01, 0xE0,
|
||||
0x7C, 0x1F, 0x07, 0x80, 0x0F, 0xF8, 0x01, 0xE0, 0x7C, 0x1F, 0x07, 0x80, 0x1F, 0xFC, 0x01, 0xE0, 0xF8, 0x1F, 0x07,
|
||||
0x80, 0x3F, 0xFF, 0x01, 0xE0, 0xF8, 0x1E, 0x07, 0x80, 0xFF, 0xFF, 0xC1, 0xC0, 0xF8, 0x3E, 0x0F, 0x83, 0xFF, 0xFF,
|
||||
0xFD, 0xC0, 0xF8, 0x3E, 0x0F, 0xBF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0,
|
||||
0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xF0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xE0, 0x7C, 0x1F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0x83, 0xE0, 0x78, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xE0, 0xF8, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x87, 0xE0, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static const uint8_t WI48_SNOW[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x00, 0x1F, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF,
|
||||
0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF,
|
||||
0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0,
|
||||
0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF,
|
||||
0xC0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F,
|
||||
0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xE0, 0x1F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x83,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x07, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x81, 0xFF, 0xFF, 0xFE, 0x7F,
|
||||
0xFF, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0x03,
|
||||
0xC0, 0x7F, 0xFF, 0xFC, 0x1F, 0xFF, 0xFC, 0x07, 0xE0, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xF8, 0x07, 0xF0, 0x01, 0xFD,
|
||||
0xFF, 0xFF, 0x9F, 0x80, 0x0F, 0xF8, 0x01, 0xF0, 0xFF, 0xFF, 0x0F, 0x80, 0x1F, 0xFC, 0x01, 0xF0, 0x7F, 0xFE, 0x07,
|
||||
0x80, 0x3F, 0xFE, 0x01, 0xF0, 0x7F, 0xFE, 0x0F, 0x80, 0xFF, 0xFF, 0x81, 0xF0, 0xFF, 0xFF, 0x0F, 0x83, 0xFF, 0xFF,
|
||||
0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xF0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFE,
|
||||
0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static const uint8_t WI48_THUNDERSTORM[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
|
||||
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x80, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF,
|
||||
0xFE, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF,
|
||||
0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF,
|
||||
0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xE0,
|
||||
0x7F, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF,
|
||||
0xE0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F,
|
||||
0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xC0, 0x7F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x03,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0x00, 0x3F, 0x87, 0xE1, 0xFF,
|
||||
0xE0, 0x03, 0xFF, 0x00, 0x3F, 0x07, 0xE1, 0xFF, 0xC0, 0x03, 0xFE, 0x00, 0x7F, 0x07, 0xC0, 0xFF, 0xC0, 0x83, 0xFE,
|
||||
0x00, 0x7F, 0x07, 0xC1, 0xFF, 0xC1, 0x81, 0xFE, 0x00, 0xFE, 0x07, 0xC1, 0xFF, 0x81, 0xC0, 0xFC, 0x00, 0xFE, 0x0F,
|
||||
0x81, 0xFF, 0x01, 0xC0, 0x7C, 0x01, 0xFE, 0x0F, 0x81, 0xFE, 0x03, 0xE0, 0x3C, 0x01, 0xFE, 0x0F, 0x83, 0xFC, 0x07,
|
||||
0xE0, 0x08, 0x03, 0xFC, 0x0F, 0x83, 0xF0, 0x07, 0xF0, 0x00, 0x03, 0xFC, 0x1F, 0x03, 0x80, 0x0F, 0xF8, 0x00, 0x07,
|
||||
0xFC, 0x1F, 0x07, 0x80, 0x1F, 0xFC, 0x00, 0x07, 0xFC, 0x1F, 0x07, 0x80, 0x3F, 0xFF, 0x00, 0x0F, 0xF8, 0x1F, 0x07,
|
||||
0x80, 0xFF, 0xFF, 0xC0, 0x1F, 0xF8, 0x3E, 0x07, 0x83, 0xFF, 0xFF, 0xE0, 0x1F, 0xF8, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF,
|
||||
0xC0, 0x00, 0x38, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x70, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0xF0,
|
||||
0x7C, 0x0F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xF0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xE0, 0x7C, 0x1F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||
0x07, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xC0, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFC, 0x3F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF,
|
||||
0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xEF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "WeatherSettingsStore.h"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <HalStorage.h>
|
||||
#include <Logging.h>
|
||||
|
||||
WeatherSettingsStore WeatherSettingsStore::instance;
|
||||
|
||||
namespace {
|
||||
constexpr char WEATHER_SETTINGS_FILE[] = "/.crosspoint/weather_settings.json";
|
||||
}
|
||||
|
||||
bool WeatherSettingsStore::saveToFile() const {
|
||||
Storage.mkdir("/.crosspoint");
|
||||
|
||||
JsonDocument doc;
|
||||
doc["latitude"] = latitude;
|
||||
doc["longitude"] = longitude;
|
||||
doc["locationName"] = locationName;
|
||||
doc["tempUnit"] = static_cast<uint8_t>(tempUnit);
|
||||
doc["windUnit"] = static_cast<uint8_t>(windUnit);
|
||||
doc["precipUnit"] = static_cast<uint8_t>(precipUnit);
|
||||
doc["forecastDays"] = forecastDays;
|
||||
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
return Storage.writeFile(WEATHER_SETTINGS_FILE, json);
|
||||
}
|
||||
|
||||
bool WeatherSettingsStore::loadFromFile() {
|
||||
if (!Storage.exists(WEATHER_SETTINGS_FILE)) {
|
||||
LOG_DBG("WEA", "No weather settings file found");
|
||||
return false;
|
||||
}
|
||||
|
||||
String json = Storage.readFile(WEATHER_SETTINGS_FILE);
|
||||
if (json.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
auto error = deserializeJson(doc, json);
|
||||
if (error) {
|
||||
LOG_ERR("WEA", "JSON parse error: %s", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
latitude = doc["latitude"] | 0.0f;
|
||||
longitude = doc["longitude"] | 0.0f;
|
||||
locationName = doc["locationName"] | std::string("");
|
||||
tempUnit = static_cast<WeatherTempUnit>(doc["tempUnit"] | (uint8_t)0);
|
||||
windUnit = static_cast<WeatherWindUnit>(doc["windUnit"] | (uint8_t)0);
|
||||
precipUnit = static_cast<WeatherPrecipUnit>(doc["precipUnit"] | (uint8_t)0);
|
||||
forecastDays = doc["forecastDays"] | (uint8_t)3;
|
||||
|
||||
if (forecastDays < 1) forecastDays = 1;
|
||||
if (forecastDays > 5) forecastDays = 5;
|
||||
|
||||
LOG_DBG("WEA", "Loaded weather settings: %s (%.2f, %.2f)", locationName.c_str(), latitude, longitude);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WeatherSettingsStore::setLocation(float lat, float lon, const std::string& name) {
|
||||
latitude = lat;
|
||||
longitude = lon;
|
||||
locationName = name;
|
||||
LOG_DBG("WEA", "Set location: %s (%.4f, %.4f)", name.c_str(), lat, lon);
|
||||
}
|
||||
|
||||
void WeatherSettingsStore::setForecastDays(uint8_t days) {
|
||||
if (days < 1) days = 1;
|
||||
if (days > 5) days = 5;
|
||||
forecastDays = days;
|
||||
}
|
||||
|
||||
const char* WeatherSettingsStore::getTempUnitParam() const {
|
||||
return tempUnit == WeatherTempUnit::FAHRENHEIT ? "fahrenheit" : "celsius";
|
||||
}
|
||||
|
||||
const char* WeatherSettingsStore::getWindUnitParam() const {
|
||||
switch (windUnit) {
|
||||
case WeatherWindUnit::MS:
|
||||
return "ms";
|
||||
case WeatherWindUnit::MPH:
|
||||
return "mph";
|
||||
case WeatherWindUnit::KNOTS:
|
||||
return "kn";
|
||||
case WeatherWindUnit::KMH:
|
||||
default:
|
||||
return "kmh";
|
||||
}
|
||||
}
|
||||
|
||||
const char* WeatherSettingsStore::getPrecipUnitParam() const {
|
||||
return precipUnit == WeatherPrecipUnit::INCH ? "inch" : "mm";
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
enum class WeatherTempUnit : uint8_t { CELSIUS = 0, FAHRENHEIT = 1 };
|
||||
enum class WeatherWindUnit : uint8_t { KMH = 0, MS = 1, MPH = 2, KNOTS = 3 };
|
||||
enum class WeatherPrecipUnit : uint8_t { MM = 0, INCH = 1 };
|
||||
|
||||
class WeatherSettingsStore {
|
||||
private:
|
||||
static WeatherSettingsStore instance;
|
||||
|
||||
float latitude = 0;
|
||||
float longitude = 0;
|
||||
std::string locationName;
|
||||
WeatherTempUnit tempUnit = WeatherTempUnit::CELSIUS;
|
||||
WeatherWindUnit windUnit = WeatherWindUnit::KMH;
|
||||
WeatherPrecipUnit precipUnit = WeatherPrecipUnit::MM;
|
||||
uint8_t forecastDays = 3;
|
||||
|
||||
WeatherSettingsStore() = default;
|
||||
|
||||
public:
|
||||
WeatherSettingsStore(const WeatherSettingsStore&) = delete;
|
||||
WeatherSettingsStore& operator=(const WeatherSettingsStore&) = delete;
|
||||
|
||||
static WeatherSettingsStore& getInstance() { return instance; }
|
||||
|
||||
bool saveToFile() const;
|
||||
bool loadFromFile();
|
||||
|
||||
// Location
|
||||
void setLocation(float lat, float lon, const std::string& name);
|
||||
float getLatitude() const { return latitude; }
|
||||
float getLongitude() const { return longitude; }
|
||||
const std::string& getLocationName() const { return locationName; }
|
||||
bool hasLocation() const { return latitude != 0 || longitude != 0; }
|
||||
|
||||
// Units
|
||||
void setTempUnit(WeatherTempUnit unit) { tempUnit = unit; }
|
||||
WeatherTempUnit getTempUnit() const { return tempUnit; }
|
||||
void setWindUnit(WeatherWindUnit unit) { windUnit = unit; }
|
||||
WeatherWindUnit getWindUnit() const { return windUnit; }
|
||||
void setPrecipUnit(WeatherPrecipUnit unit) { precipUnit = unit; }
|
||||
WeatherPrecipUnit getPrecipUnit() const { return precipUnit; }
|
||||
|
||||
// Forecast
|
||||
void setForecastDays(uint8_t days);
|
||||
uint8_t getForecastDays() const { return forecastDays; }
|
||||
|
||||
// API parameter strings
|
||||
const char* getTempUnitParam() const;
|
||||
const char* getWindUnitParam() const;
|
||||
const char* getPrecipUnitParam() const;
|
||||
};
|
||||
|
||||
#define WEATHER_SETTINGS WeatherSettingsStore::getInstance()
|
||||
Reference in New Issue
Block a user