diff --git a/src/displayapp/UserApps.h b/src/displayapp/UserApps.h index 67bbfa7d..531a447d 100644 --- a/src/displayapp/UserApps.h +++ b/src/displayapp/UserApps.h @@ -14,6 +14,7 @@ #include "displayapp/screens/WatchFaceInfineat.h" #include "displayapp/screens/WatchFacePineTimeStyle.h" #include "displayapp/screens/WatchFaceTerminal.h" +#include "displayapp/screens/Weather.h" namespace Pinetime { namespace Applications { diff --git a/src/displayapp/screens/List.h b/src/displayapp/screens/List.h index 17a25f82..b5e0e555 100644 --- a/src/displayapp/screens/List.h +++ b/src/displayapp/screens/List.h @@ -8,6 +8,7 @@ #include "displayapp/apps/Apps.h" #include "components/settings/Settings.h" + #define MAXLISTITEMS 4 namespace Pinetime { diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp index 18fb7ad2..30a92704 100644 --- a/src/displayapp/screens/SystemInfo.cpp +++ b/src/displayapp/screens/SystemInfo.cpp @@ -79,7 +79,8 @@ std::unique_ptr SystemInfo::CreateScreen1() { lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(label, true); lv_label_set_text_fmt(label, - "#FFFF00 InfiniTime#\n\n" + "#FFFF00 InfiniTime#\n" + "modified by\nCaleb Fontenot\n" "#808080 Version# %ld.%ld.%ld\n" "#808080 Short Ref# %s\n" "#808080 Build date#\n" diff --git a/src/displayapp/screens/WatchFaceTerminal.cpp b/src/displayapp/screens/WatchFaceTerminal.cpp index 96d77741..63496df2 100644 --- a/src/displayapp/screens/WatchFaceTerminal.cpp +++ b/src/displayapp/screens/WatchFaceTerminal.cpp @@ -3,12 +3,17 @@ #include "displayapp/screens/BatteryIcon.h" #include "displayapp/screens/NotificationIcon.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/InfiniTimeTheme.h" #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" #include "components/heartrate/HeartRateController.h" #include "components/motion/MotionController.h" #include "components/settings/Settings.h" +#include +#include +#include +#include using namespace Pinetime::Applications::Screens; @@ -18,7 +23,9 @@ WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController, Controllers::NotificationManager& notificationManager, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController) + Controllers::MotionController& motionController, + Controllers::SimpleWeatherService& weatherController + ) : currentDateTime {{}}, dateTimeController {dateTimeController}, batteryController {batteryController}, @@ -26,14 +33,15 @@ WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController, notificationManager {notificationManager}, settingsController {settingsController}, heartRateController {heartRateController}, - motionController {motionController} { + motionController {motionController}, + weatherController {weatherController} { batteryValue = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(batteryValue, true); lv_obj_align(batteryValue, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -20); connectState = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(connectState, true); - lv_obj_align(connectState, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 40); + lv_obj_align(connectState, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 60); notificationIcon = lv_label_create(lv_scr_act(), nullptr); lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_LEFT_MID, 0, -100); @@ -47,7 +55,7 @@ WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController, lv_label_set_text_static(label_prompt_1, "user@watch:~ $ now"); label_prompt_2 = lv_label_create(lv_scr_act(), nullptr); - lv_obj_align(label_prompt_2, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 60); + lv_obj_align(label_prompt_2, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 80); lv_label_set_text_static(label_prompt_2, "user@watch:~ $"); label_time = lv_label_create(lv_scr_act(), nullptr); @@ -61,6 +69,10 @@ WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController, stepValue = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(stepValue, true); lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 0); + + weatherStatus = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_recolor(weatherStatus, true); + lv_obj_align(weatherStatus, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 40); taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); Refresh(); @@ -70,6 +82,94 @@ WatchFaceTerminal::~WatchFaceTerminal() { lv_task_del(taskRefresh); lv_obj_clean(lv_scr_act()); } +//https://www.wpc.ncep.noaa.gov/html/contract.html + const char* WeatherString(uint8_t weatherID) { + switch (weatherID) { + case 0: { + return "CLR"; + } + case 1: { + return "SLGT CLD"; + } + case 2: { + return "CLD"; + } + case 3: { + return "EXTRM CLD"; + } + case 4: { + return "TSRA"; + } + case 5: { + return "RA"; + } + case 6: { + return "TSTM"; + } + case 7: { + return "SN"; + } + case 8: { + return "BR"; + } + default: { + return "UNKN"; + } + } + } + + // TODO: This code is duplicated from Weather.cpp. It would probably be better to put it in its own class, but I'm not really certain where it should go. + int16_t RoundTemperature(int16_t temp) { + return temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0); + } + // End of code duplication. + + //Linear gradient temperature color calculator :) + + const char* floatToRgbHex(std::tuple rgb) { + char *rgbHex = new char[7]; + snprintf(rgbHex, 7, "%02X%02X%02X", static_cast(std::get<0>(rgb)), static_cast(std::get<1>(rgb)), static_cast(std::get<2>(rgb))); + return rgbHex; + } + + std::tuple hexToFloat(int rgb) { + float r = ((rgb >> 16) & 0xFF) / 255; + float g = ((rgb >> 8) & 0xFF) / 255; + float b = (rgb & 0xFF) / 255; + return std::tuple(r, g, b); + } + // reference: https://dev.to/ndesmic/linear-color-gradients-from-scratch-1a0e + + std::tuple lerp(std::tuple pointA, std::tuple pointB, float normalValue) { + return std::tuple( + get<0>(pointA) + (get<0>(pointB) - get<0>(pointA)) * normalValue, + get<1>(pointA) + (get<1>(pointB) - get<1>(pointA)) * normalValue, + get<2>(pointA) + (get<2>(pointB) - get<2>(pointA)) * normalValue + ); + } + + const char* TemperatureColor(int16_t temperature) { + const std::vector colors = {0x0000ff, 0xff9b00, 0xff0000}; + std::vector> stops; + for (auto colorVal: colors) { + stops.emplace_back(hexToFloat(colorVal)); + } + int tempRounded = RoundTemperature(temperature); + if (tempRounded < 0) { + tempRounded = 1; + } + // convert temperature to range between 0 and 1 + float oldRange = (56 - 0); + float newRange = (1 - 0); + float newValue = (((tempRounded - 0) * newRange) / oldRange) + 1; + + if (newValue <= .5f) { + return floatToRgbHex(lerp(stops[0], stops[1], newValue)); + } else { + return floatToRgbHex(lerp(stops[1], stops[2], newValue)); + } + } + void WatchFaceTerminal::Refresh() { powerPresent = batteryController.IsPowerPresent(); @@ -103,6 +203,34 @@ void WatchFaceTerminal::Refresh() { lv_label_set_text_static(notificationIcon, ""); } } + + // Following along from the example given by Weather.cpp... + + // get an instance of the weather service? not exactly sure what this does... + currentWeather = weatherController.Current(); + // check to see if we have valid weather data + if (currentWeather.IsUpdated()) { + auto optCurrentWeather = currentWeather.Get(); // the actual weather data + if (optCurrentWeather) { + int16_t temp = optCurrentWeather->temperature; // current temperature + uint8_t weatherId = static_cast(optCurrentWeather->iconId); // weather type + NRF_LOG_INFO("Raw temp: %d", temp); + NRF_LOG_INFO("Rounded temp: %d", RoundTemperature(temp)); + // unit conversion + char tempUnit = 'C'; + if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) { + temp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(temp); + tempUnit = 'F'; + } + auto color = "ff9b00"; + //TemperatureColor(temp); + NRF_LOG_INFO("Color hex: %s", color); + lv_label_set_text_fmt(weatherStatus, "[WTHR]#%s %d#°%c %s", color, RoundTemperature(temp), tempUnit, WeatherString(weatherId)); + } else { + lv_label_set_text_static(weatherStatus, "[WTHR]No Data"); + } + } + currentDateTime = std::chrono::time_point_cast(dateTimeController.CurrentDateTime()); if (currentDateTime.IsUpdated()) { @@ -148,4 +276,5 @@ void WatchFaceTerminal::Refresh() { if (stepCount.IsUpdated()) { lv_label_set_text_fmt(stepValue, "[STEP]#ee3377 %lu steps#", stepCount.Get()); } + } diff --git a/src/displayapp/screens/WatchFaceTerminal.h b/src/displayapp/screens/WatchFaceTerminal.h index bf460866..f36fb99c 100644 --- a/src/displayapp/screens/WatchFaceTerminal.h +++ b/src/displayapp/screens/WatchFaceTerminal.h @@ -7,6 +7,7 @@ #include #include "displayapp/screens/Screen.h" #include "components/datetime/DateTimeController.h" +#include "components/ble/SimpleWeatherService.h" #include "utility/DirtyValue.h" namespace Pinetime { @@ -30,7 +31,9 @@ namespace Pinetime { Controllers::NotificationManager& notificationManager, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController); + Controllers::MotionController& motionController, + Controllers::SimpleWeatherService& weatherService + ); ~WatchFaceTerminal() override; void Refresh() override; @@ -45,6 +48,7 @@ namespace Pinetime { Utility::DirtyValue heartbeat {}; Utility::DirtyValue heartbeatRunning {}; Utility::DirtyValue notificationState {}; + Utility::DirtyValue> currentWeather {}; Utility::DirtyValue> currentDate; lv_obj_t* label_time; @@ -56,6 +60,7 @@ namespace Pinetime { lv_obj_t* stepValue; lv_obj_t* notificationIcon; lv_obj_t* connectState; + lv_obj_t* weatherStatus; Controllers::DateTime& dateTimeController; const Controllers::Battery& batteryController; @@ -64,7 +69,7 @@ namespace Pinetime { Controllers::Settings& settingsController; Controllers::HeartRateController& heartRateController; Controllers::MotionController& motionController; - + Controllers::SimpleWeatherService& weatherController; lv_task_t* taskRefresh; }; } @@ -81,7 +86,8 @@ namespace Pinetime { controllers.notificationManager, controllers.settingsController, controllers.heartRateController, - controllers.motionController); + controllers.motionController, + *controllers.weatherController); }; static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) { diff --git a/src/displayapp/screens/Weather.h b/src/displayapp/screens/Weather.h index 6975311e..e72cc86e 100644 --- a/src/displayapp/screens/Weather.h +++ b/src/displayapp/screens/Weather.h @@ -24,7 +24,7 @@ namespace Pinetime { ~Weather() override; void Refresh() override; - + private: Controllers::Settings& settingsController; Controllers::SimpleWeatherService& weatherService;