Skip to content

Commit

Permalink
Use open-meteo instead of openweathermap
Browse files Browse the repository at this point in the history
Originally, the openweathermap application was used, but version 2.5
of that API will no longer work after June 2024, so now we use the
open-meteo API instead.

This parallels a change that was made in the asteroid-weatherfetch
source code and is largely a cut-and-paste copy with a few small changes
to use the existing interfaces.

Note that eventually, this code could go away, and the watch would
parse the JSON data itself, but that would require a change to the
bluetooth implementation on the watch.

Signed-off-by: Ed Beroset <[email protected]>
  • Loading branch information
beroset authored and FlorentRevest committed Nov 20, 2024
1 parent 6891d9c commit d50844b
Showing 1 changed file with 77 additions and 22 deletions.
99 changes: 77 additions & 22 deletions asteroidsyncserviced/dbusinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,40 +34,97 @@
#include <utility>

/*!
* \brief Convert JSON weather string to QList<WeatherDay>
* look up weather icon code, given WMO weather code
*
* \param weatherJson String containing weather JSON. An example of the
* minimum acceptable string:
* '{"daily":[
* {"temp":{"min":289.19,"max":298.9}},{"weather":[{"id":800}]},
* {"temp":{"min":290.25,"max":300.2}},{"weather":[{"id":800}]}
* ]}'
* see https://openweathermap.org/api/one-call-api for full spec
* sources:
* WMO weather codes: https://www.nodc.noaa.gov/archive/arc0021/0002199/1.1/data/0-data/HTML/WMO-CODE/WMO4677.HTM
* weather icon codes: https://openweathermap.org/weather-conditions
*/
static QList<WeatherDay> parseWeatherJson(const QString &weatherJson)
[[nodiscard]] static int iconlookup(int wxcode) {
int iconcode{800}; // every day is sunny!
switch(wxcode) {
case 0: iconcode = 800; break; // sunny
case 1: iconcode = 801; break; // mainly sunny
case 2: iconcode = 802; break; // partly cloudy
case 3: iconcode = 803; break; // mostly cloudy
case 45: iconcode = 741; break; // foggy
case 48: iconcode = 741; break; // rime fog
case 51: iconcode = 300; break; // light drizzle
case 53: iconcode = 301; break; // drizzle
case 55: iconcode = 302; break; // heavy drizzle
case 56: iconcode = 612; break; // light freezing drizzle
case 57: iconcode = 613; break; // freezing drizzle
case 61: iconcode = 500; break; // light rain
case 63: iconcode = 501; break; // rain
case 65: iconcode = 502; break; // heavy rain
case 66: iconcode = 511; break; // light freezing rain
case 67: iconcode = 511; break; // freezing rain
case 71: iconcode = 600; break; // light snow
case 73: iconcode = 601; break; // snow
case 75: iconcode = 602; break; // heavy snow
case 77: iconcode = 601; break; // snow grains
case 80: iconcode = 500; break; // light showers
case 81: iconcode = 501; break; // showers
case 82: iconcode = 502; break; // heavy showers
case 85: iconcode = 600; break; // light snow showers
case 86: iconcode = 601; break; // snow showers
case 95: iconcode = 211; break; // thunderstorm
case 96: iconcode = 200; break; // light thunderstorms with hail (no hail designation in codes, so just use t'storm)
case 99: iconcode = 211; break; // thunderstorm with hail (no hail designation in codes, so just use t'storm)

default: {
iconcode = 800;
qDebug() << "unknown weather code passed to iconlookup:" << wxcode;
break;
}
}
return iconcode;
}
/*!
* \brief Convert JSON weather string to settings for asteroid-weather
*
* \param weatherJson String containing weather JSON. As and example,
* if we request the weather data for Cape Town, South Africa, the request
* URL would be
* "https://api.open-meteo.com/v1/forecast?latitude=35.858&longitude=-79.1032&timezone=auto&daily=weather_code,temperature_2m_max,temperature_2m_min"
* and the response might be:
* "latitude":35.850216,"longitude":-79.097015,"generationtime_ms":0.102996826171875,"utc_offset_seconds":-14400,"timezone":"America/New_York","timezone_abbreviation":"EDT","elevation":188.0,"daily_units":{"time":"iso8601","weather_code":"wmo code","temperature_2m_max":"°C","temperature_2m_min":"°C"},"daily":{"time":["2024-04-25","2024-04-26","2024-04-27","2024-04-28","2024-04-29","2024-04-30","2024-05-01"],"weather_code":[3,3,3,3,2,51,2],"temperature_2m_max":[23.2,21.8,20.2,25.9,27.7,28.8,29.6],"temperature_2m_min":[8.8,8.6,11.5,11.2,13.3,14.9,14.2]}}
*
* Note that by default, the temperatures are in degrees C which we must convert to Kelvin for the weather app
* see https://open-meteo.com/
*/
[[nodiscard]] static QList<WeatherDay> weatherJsonToVector(const QString &weatherJson)
{
/* This looks complex, but it's really just a way to compensate for the fact
* that with Qt5, size() returned an int, and with Qt6, it returns a qsizetype.
* This automatically determines the type so the comparison within std::min()
* below does not trigger a compiler warning.
*/
static constexpr decltype(std::declval<QJsonArray>().size()) maxWeatherDays{5};
QList<WeatherDay> weatherDays;
QList<WeatherDay> days;
constexpr double CtoKwithRounding{272.15 + 0.5};
QJsonParseError parseError;
qDebug() << "weather string: " << weatherJson.toUtf8();
auto json = QJsonDocument::fromJson(weatherJson.toUtf8(), &parseError);
if (json.isNull()) {
qWarning() << "JSON parse error: " << parseError.errorString();
return days;
}
auto daily = json["daily"].toArray();
int count = std::min(maxWeatherDays, daily.count());
for (int i = 0; i < count; ++i) {
auto day = daily.at(i).toObject();
short low = day["temp"].toObject()["min"].toDouble();
short high = day["temp"].toObject()["max"].toDouble();
short icon = day["weather"].toArray()[0].toObject()["id"].toInt();
weatherDays.push_back({icon, low, high});
auto min = json["daily"]["temperature_2m_min"].toArray();
auto max = json["daily"]["temperature_2m_max"].toArray();
auto icon = json["daily"]["weather_code"].toArray();
int count = std::min(maxWeatherDays, min.count());
for (int i{0}; i < count; ++i) {
days.push_back({ static_cast<short int>(iconlookup(icon[i].toInt())),
static_cast<short int>(min[i].toDouble() + CtoKwithRounding),
static_cast<short int>(max[i].toDouble() + CtoKwithRounding)
});
qDebug() << "Day " << i << " of " << count << " = [ " << days[i].m_wxIcon
<< ", " << days[i].m_loTemp
<< ", " << days[i].m_hiTemp
<< " ]";
}
return weatherDays;
return days;
}

/* Watch Interface */
Expand Down Expand Up @@ -138,9 +195,7 @@ void DBusWatch::WeatherSetCityName(QString cityName)

void DBusWatch::WeatherSetWeather(QString weatherJson)
{
QList<WeatherDay> weatherDays = parseWeatherJson(weatherJson);
auto wds = QVariant::fromValue<QList<WeatherDay>>(weatherDays);

QList<WeatherDay> weatherDays = weatherJsonToVector(weatherJson);
m_weatherService->setWeatherDays(weatherDays);
}

Expand Down

0 comments on commit d50844b

Please sign in to comment.