diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h index 1a995eb96f..700bcd4966 100644 --- a/src/components/ble/weather/WeatherData.h +++ b/src/components/ble/weather/WeatherData.h @@ -126,6 +126,24 @@ namespace Pinetime { Length }; + /** + * List of weather condition codes used to determine display + * (range of thunderstorm, drizzle, rain, snow, clouds, atmosphere including extreme conditions like tornado, hurricane etc.) + * https://openweathermap.org/weather-conditions + */ + enum class conditiontype { + ClearSky = 0, + FewClouds = 1, + ScatteredClouds = 2, + BrokenClouds = 3, + ShowerRain = 4, + Rain = 5, + Thunderstorm = 6, + Snow = 7, + Mist = 8, + Length + }; + /** * These are used for weather timeline manipulation * that isn't just adding to the stack of weather events @@ -165,6 +183,8 @@ namespace Pinetime { Clouds = 8, /** @see humidity */ Humidity = 9, + /** @see condition */ + Condition = 10, Length }; @@ -326,6 +346,20 @@ namespace Pinetime { specialtype type; }; + /** How weather condition is stored */ + class Condition : public TimelineHeader { + public: + /** + * Type of condition + */ + conditiontype type; + + /** + * Condition code + */ + uint16_t code; + }; + /** * How air quality is stored * diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp index b9a6af556c..bb01fb0b13 100644 --- a/src/components/ble/weather/WeatherService.cpp +++ b/src/components/ble/weather/WeatherService.cpp @@ -357,6 +357,34 @@ namespace Pinetime { } break; } + case WeatherData::eventtype::Condition: { + std::unique_ptr condition = std::make_unique(); + condition->timestamp = tmpTimestamp; + condition->eventType = static_cast(tmpEventType); + condition->expires = tmpExpires; + + int64_t tmpType = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); + if (tmpType < 0 || tmpType >= static_cast(WeatherData::conditiontype::Length)) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + condition->type = static_cast(tmpType); + + int64_t tmpCode = 0; + QCBORDecode_GetInt64InMapSZ(&decodeContext, "Code", &tmpCode); + if (tmpCode < 0 || tmpCode >= 65535) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + condition->code = static_cast(tmpCode); + + if (!AddEventToTimeline(std::move(condition))) { + CleanUpQcbor(&decodeContext); + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + break; + } default: { CleanUpQcbor(&decodeContext); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; @@ -509,6 +537,30 @@ namespace Pinetime { return reinterpret_cast&>(*this->nullHeader); } + std::unique_ptr& WeatherService::GetCurrentSpecial() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Special && currentTimestamp >= header->timestamp && + IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); + } + } + + return reinterpret_cast&>(*this->nullHeader); + } + + std::unique_ptr& WeatherService::GetCurrentCondition() { + uint64_t currentTimestamp = GetCurrentUnixTimestamp(); + for (auto&& header : this->timeline) { + if (header->eventType == WeatherData::eventtype::Condition && currentTimestamp >= header->timestamp && + IsEventStillValid(header, currentTimestamp)) { + return reinterpret_cast&>(header); + } + } + + return reinterpret_cast&>(*this->nullHeader); + } + size_t WeatherService::GetTimelineLength() const { return timeline.size(); } diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h index 609e8760d6..19f209b26d 100644 --- a/src/components/ble/weather/WeatherService.h +++ b/src/components/ble/weather/WeatherService.h @@ -58,6 +58,8 @@ namespace Pinetime { std::unique_ptr& GetCurrentHumidity(); std::unique_ptr& GetCurrentPressure(); std::unique_ptr& GetCurrentQuality(); + std::unique_ptr& GetCurrentSpecial(); + std::unique_ptr& GetCurrentCondition(); /** * Searches for the current day's maximum temperature diff --git a/src/displayapp/fonts/fonts.json b/src/displayapp/fonts/fonts.json index 8416fc5ef3..c8193695df 100644 --- a/src/displayapp/fonts/fonts.json +++ b/src/displayapp/fonts/fonts.json @@ -7,7 +7,7 @@ }, { "file": "FontAwesome5-Solid+Brands+Regular.woff", - "range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c" + "range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf5d7, 0xf6d3, 0xf6be, 0xf4ba, 0xf6c4" } ], "bpp": 1, @@ -68,7 +68,7 @@ "sources": [ { "file": "FontAwesome5-Solid+Brands+Regular.woff", - "range": "0xf185, 0xf6c4, 0xf743, 0xf740, 0xf75f, 0xf0c2, 0xf05e" + "range": "0xf185, 0xf6c4, 0xf743, 0xf740, 0xf75f, 0xf0c2, 0xf05e, 0xf0e7, 0xf2dc" } ], "bpp": 1, diff --git a/src/displayapp/screens/Symbols.h b/src/displayapp/screens/Symbols.h index 7154ff44c1..025f1bede4 100644 --- a/src/displayapp/screens/Symbols.h +++ b/src/displayapp/screens/Symbols.h @@ -45,6 +45,8 @@ namespace Pinetime { static constexpr const char* cloudShowersHeavy = "\xEF\x9D\x80"; static constexpr const char* smog = "\xEF\x9D\x9F"; static constexpr const char* cloud = "\xEF\x83\x82"; + static constexpr const char* bolt = "\xEF\x83\xA7"; + static constexpr const char* snowflake = "\xEF\x8B\x9C"; static constexpr const char* ban = "\xEF\x81\x9E"; // lv_font_sys_48.c diff --git a/src/displayapp/screens/WatchFacePineTimeStyle.cpp b/src/displayapp/screens/WatchFacePineTimeStyle.cpp index 250a745c15..a8e84471f6 100644 --- a/src/displayapp/screens/WatchFacePineTimeStyle.cpp +++ b/src/displayapp/screens/WatchFacePineTimeStyle.cpp @@ -30,10 +30,10 @@ #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" +#include "components/ble/weather/WeatherService.h" #include "components/motion/MotionController.h" #include "components/settings/Settings.h" #include "displayapp/DisplayApp.h" -#include "components/ble/weather/WeatherService.h" using namespace Pinetime::Applications::Screens; @@ -537,24 +537,39 @@ void WatchFacePineTimeStyle::Refresh() { } } - if (weatherService.GetCurrentTemperature()->timestamp != 0 && weatherService.GetCurrentClouds()->timestamp != 0 && - weatherService.GetCurrentPrecipitation()->timestamp != 0) { + if (weatherService.GetCurrentTemperature()->timestamp != 0 && weatherService.GetCurrentCondition()->timestamp != 0) { nowTemp = (weatherService.GetCurrentTemperature()->temperature / 100); - clouds = (weatherService.GetCurrentClouds()->amount); - precip = (weatherService.GetCurrentPrecipitation()->amount); if (nowTemp.IsUpdated()) { lv_label_set_text_fmt(temperature, "%d°", nowTemp.Get()); - if ((clouds <= 30) && (precip == 0)) { - lv_label_set_text(weatherIcon, Symbols::sun); - } else if ((clouds >= 70) && (clouds <= 90) && (precip == 1)) { - lv_label_set_text(weatherIcon, Symbols::cloudSunRain); - } else if ((clouds > 90) && (precip == 0)) { - lv_label_set_text(weatherIcon, Symbols::cloud); - } else if ((clouds > 70) && (precip >= 2)) { - lv_label_set_text(weatherIcon, Symbols::cloudShowersHeavy); - } else { - lv_label_set_text(weatherIcon, Symbols::cloudSun); - }; + switch (weatherService.GetCurrentCondition()->type) { + case Controllers::WeatherData::conditiontype::ClearSky: + lv_label_set_text(weatherIcon, Symbols::sun); + break; + case Controllers::WeatherData::conditiontype::FewClouds: + case Controllers::WeatherData::conditiontype::ScatteredClouds: + lv_label_set_text(weatherIcon, Symbols::cloudSun); + break; + case Controllers::WeatherData::conditiontype::BrokenClouds: + lv_label_set_text(weatherIcon, Symbols::cloud); + break; + case Controllers::WeatherData::conditiontype::ShowerRain: + lv_label_set_text(weatherIcon, Symbols::cloudShowersHeavy); + break; + case Controllers::WeatherData::conditiontype::Rain: + lv_label_set_text(weatherIcon, Symbols::cloudSunRain); + break; + case Controllers::WeatherData::conditiontype::Thunderstorm: + lv_label_set_text(weatherIcon, Symbols::bolt); + break; + case Controllers::WeatherData::conditiontype::Snow: + lv_label_set_text(weatherIcon, Symbols::snowflake); + break; + case Controllers::WeatherData::conditiontype::Mist: + lv_label_set_text(weatherIcon, Symbols::smog); + break; + default: + break; + } lv_obj_realign(temperature); lv_obj_realign(weatherIcon); } diff --git a/src/displayapp/screens/WatchFacePineTimeStyle.h b/src/displayapp/screens/WatchFacePineTimeStyle.h index e157bb2cd2..42433fe16b 100644 --- a/src/displayapp/screens/WatchFacePineTimeStyle.h +++ b/src/displayapp/screens/WatchFacePineTimeStyle.h @@ -32,7 +32,7 @@ namespace Pinetime { Controllers::NotificationManager& notificationManager, Controllers::Settings& settingsController, Controllers::MotionController& motionController, - Controllers::WeatherService& weather); + Controllers::WeatherService& weatherService); ~WatchFacePineTimeStyle() override; bool OnTouchEvent(TouchEvents event) override; @@ -61,8 +61,6 @@ namespace Pinetime { Utility::DirtyValue stepCount {}; Utility::DirtyValue notificationState {}; Utility::DirtyValue nowTemp {}; - int16_t clouds = 0; - int16_t precip = 0; static Pinetime::Controllers::Settings::Colors GetNext(Controllers::Settings::Colors color); static Pinetime::Controllers::Settings::Colors GetPrevious(Controllers::Settings::Colors color); diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index 4921174c7a..3ce6fce0be 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -33,6 +33,9 @@ Weather::Weather(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers: {[this]() -> std::unique_ptr { return CreateScreenTemperature(); }, + [this]() -> std::unique_ptr { + return CreateScreenCondition(); + }, [this]() -> std::unique_ptr { return CreateScreenAir(); }, @@ -60,7 +63,7 @@ void Weather::Refresh() { bool Weather::OnButtonPushed() { running = false; - return true; + return false; } bool Weather::OnTouchEvent(Pinetime::Applications::TouchEvents event) { @@ -100,6 +103,40 @@ std::unique_ptr Weather::CreateScreenTemperature() { return std::unique_ptr(new Screens::Label(0, 5, label)); } +std::unique_ptr Weather::CreateScreenCondition() { + lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(label, true); + std::unique_ptr& current = weatherService.GetCurrentCondition(); + if (current->timestamp == 0) { + // Do not use the data, it's invalid + lv_label_set_text_fmt(label, + "#FFFF00 Condition#\n\n" + "#444444 %d# \n\n" + "#444444 %d# \n\n" + "%d\n" + "%d\n", + 0, + 0, + 0, + 0, + 0); + } else { + lv_label_set_text_fmt(label, + "#FFFF00 Condition#\n\n" + "#444444 %d# \n\n" + "#444444 %d# \n\n" + "%llu\n" + "%lu\n", + current->type, + current->code, + current->timestamp, + current->expires); + } + lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); + lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + return std::unique_ptr(new Screens::Label(0, 5, label)); +} + std::unique_ptr Weather::CreateScreenAir() { lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(label, true); diff --git a/src/displayapp/screens/Weather.h b/src/displayapp/screens/Weather.h index 84177ea6fd..1761d4ad55 100644 --- a/src/displayapp/screens/Weather.h +++ b/src/displayapp/screens/Weather.h @@ -31,10 +31,12 @@ namespace Pinetime { Controllers::WeatherService& weatherService; - ScreenList<5> screens; + ScreenList<6> screens; std::unique_ptr CreateScreenTemperature(); + std::unique_ptr CreateScreenCondition(); + std::unique_ptr CreateScreenAir(); std::unique_ptr CreateScreenClouds();