@ -3,12 +3,17 @@
# include "displayapp/screens/BatteryIcon.h"
# include "displayapp/screens/BatteryIcon.h"
# include "displayapp/screens/NotificationIcon.h"
# include "displayapp/screens/NotificationIcon.h"
# include "displayapp/screens/Symbols.h"
# include "displayapp/screens/Symbols.h"
# include "displayapp/InfiniTimeTheme.h"
# include "components/battery/BatteryController.h"
# include "components/battery/BatteryController.h"
# include "components/ble/BleController.h"
# include "components/ble/BleController.h"
# include "components/ble/NotificationManager.h"
# include "components/ble/NotificationManager.h"
# include "components/heartrate/HeartRateController.h"
# include "components/heartrate/HeartRateController.h"
# include "components/motion/MotionController.h"
# include "components/motion/MotionController.h"
# include "components/settings/Settings.h"
# include "components/settings/Settings.h"
# include <nrfx_log.h>
# include <tuple>
# include <vector>
# include <cmath>
using namespace Pinetime : : Applications : : Screens ;
using namespace Pinetime : : Applications : : Screens ;
@ -18,7 +23,9 @@ WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController,
Controllers : : NotificationManager & notificationManager ,
Controllers : : NotificationManager & notificationManager ,
Controllers : : Settings & settingsController ,
Controllers : : Settings & settingsController ,
Controllers : : HeartRateController & heartRateController ,
Controllers : : HeartRateController & heartRateController ,
Controllers : : MotionController & motionController )
Controllers : : MotionController & motionController ,
Controllers : : SimpleWeatherService & weatherController
)
: currentDateTime { { } } ,
: currentDateTime { { } } ,
dateTimeController { dateTimeController } ,
dateTimeController { dateTimeController } ,
batteryController { batteryController } ,
batteryController { batteryController } ,
@ -26,14 +33,15 @@ WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController,
notificationManager { notificationManager } ,
notificationManager { notificationManager } ,
settingsController { settingsController } ,
settingsController { settingsController } ,
heartRateController { heartRateController } ,
heartRateController { heartRateController } ,
motionController { motionController } {
motionController { motionController } ,
weatherController { weatherController } {
batteryValue = lv_label_create ( lv_scr_act ( ) , nullptr ) ;
batteryValue = lv_label_create ( lv_scr_act ( ) , nullptr ) ;
lv_label_set_recolor ( batteryValue , true ) ;
lv_label_set_recolor ( batteryValue , true ) ;
lv_obj_align ( batteryValue , lv_scr_act ( ) , LV_ALIGN_IN_LEFT_MID , 0 , - 20 ) ;
lv_obj_align ( batteryValue , lv_scr_act ( ) , LV_ALIGN_IN_LEFT_MID , 0 , - 20 ) ;
connectState = lv_label_create ( lv_scr_act ( ) , nullptr ) ;
connectState = lv_label_create ( lv_scr_act ( ) , nullptr ) ;
lv_label_set_recolor ( connectState , true ) ;
lv_label_set_recolor ( connectState , true ) ;
lv_obj_align ( connectState , lv_scr_act ( ) , LV_ALIGN_IN_LEFT_MID , 0 , 4 0) ;
lv_obj_align ( connectState , lv_scr_act ( ) , LV_ALIGN_IN_LEFT_MID , 0 , 6 0) ;
notificationIcon = lv_label_create ( lv_scr_act ( ) , nullptr ) ;
notificationIcon = lv_label_create ( lv_scr_act ( ) , nullptr ) ;
lv_obj_align ( notificationIcon , nullptr , LV_ALIGN_IN_LEFT_MID , 0 , - 100 ) ;
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 " ) ;
lv_label_set_text_static ( label_prompt_1 , " user@watch:~ $ now " ) ;
label_prompt_2 = lv_label_create ( lv_scr_act ( ) , nullptr ) ;
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 , 6 0) ;
lv_obj_align ( label_prompt_2 , lv_scr_act ( ) , LV_ALIGN_IN_LEFT_MID , 0 , 8 0) ;
lv_label_set_text_static ( label_prompt_2 , " user@watch:~ $ " ) ;
lv_label_set_text_static ( label_prompt_2 , " user@watch:~ $ " ) ;
label_time = lv_label_create ( lv_scr_act ( ) , nullptr ) ;
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 ) ;
stepValue = lv_label_create ( lv_scr_act ( ) , nullptr ) ;
lv_label_set_recolor ( stepValue , true ) ;
lv_label_set_recolor ( stepValue , true ) ;
lv_obj_align ( stepValue , lv_scr_act ( ) , LV_ALIGN_IN_LEFT_MID , 0 , 0 ) ;
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 ) ;
taskRefresh = lv_task_create ( RefreshTaskCallback , LV_DISP_DEF_REFR_PERIOD , LV_TASK_PRIO_MID , this ) ;
Refresh ( ) ;
Refresh ( ) ;
@ -70,6 +82,94 @@ WatchFaceTerminal::~WatchFaceTerminal() {
lv_task_del ( taskRefresh ) ;
lv_task_del ( taskRefresh ) ;
lv_obj_clean ( lv_scr_act ( ) ) ;
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 < float , float , float > rgb ) {
char * rgbHex = new char [ 7 ] ;
snprintf ( rgbHex , 7 , " %02X%02X%02X " , static_cast < int > ( std : : get < 0 > ( rgb ) ) , static_cast < int > ( std : : get < 1 > ( rgb ) ) , static_cast < int > ( std : : get < 2 > ( rgb ) ) ) ;
return rgbHex ;
}
std : : tuple < float , float , float > hexToFloat ( int rgb ) {
float r = ( ( rgb > > 16 ) & 0xFF ) / 255 ;
float g = ( ( rgb > > 8 ) & 0xFF ) / 255 ;
float b = ( rgb & 0xFF ) / 255 ;
return std : : tuple < float , float , float > ( r , g , b ) ;
}
// reference: https://dev.to/ndesmic/linear-color-gradients-from-scratch-1a0e
std : : tuple < float , float , float > lerp ( std : : tuple < float , float , float > pointA , std : : tuple < float , float , float > pointB , float normalValue ) {
return std : : tuple < float , float , float > (
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 < int > colors = { 0x0000ff , 0xff9b00 , 0xff0000 } ;
std : : vector < std : : tuple < float , float , float > > 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 ( ) {
void WatchFaceTerminal : : Refresh ( ) {
powerPresent = batteryController . IsPowerPresent ( ) ;
powerPresent = batteryController . IsPowerPresent ( ) ;
@ -103,6 +203,34 @@ void WatchFaceTerminal::Refresh() {
lv_label_set_text_static ( notificationIcon , " " ) ;
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 < int > ( 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 < std : : chrono : : seconds > ( dateTimeController . CurrentDateTime ( ) ) ;
currentDateTime = std : : chrono : : time_point_cast < std : : chrono : : seconds > ( dateTimeController . CurrentDateTime ( ) ) ;
if ( currentDateTime . IsUpdated ( ) ) {
if ( currentDateTime . IsUpdated ( ) ) {
@ -148,4 +276,5 @@ void WatchFaceTerminal::Refresh() {
if ( stepCount . IsUpdated ( ) ) {
if ( stepCount . IsUpdated ( ) ) {
lv_label_set_text_fmt ( stepValue , " [STEP]#ee3377 %lu steps# " , stepCount . Get ( ) ) ;
lv_label_set_text_fmt ( stepValue , " [STEP]#ee3377 %lu steps# " , stepCount . Get ( ) ) ;
}
}
}
}