Compare commits

...

26 commits

Author SHA1 Message Date
CCF100 a68d0a596e
Merge 1cbc664dc3 into a2356f2f4a 2024-10-09 16:09:56 +02:00
NeroBurner a2356f2f4a
MusicService: add missing includes for TickType_t and xTaskGetTickCount (#2130)
Some checks failed
CI / build-firmware (push) Successful in 10m55s
CI / build-simulator (push) Failing after 3s
CI / get-base-ref-size (push) Has been skipped
CI / Compare build size (push) Has been skipped
Add `FreeRTOS.h` include for the directly used data type `TickType_t` in the header
and the function `xTaskGetTickCount` from FreeRTOS's `task.h`
2024-09-29 21:10:32 +02:00
NeroBurner 3db4e012ce
Remove unused pointer to DisplayApp member variables (#2125)
Some checks failed
CI / build-firmware (push) Successful in 10m10s
CI / build-simulator (push) Failing after 3s
CI / get-base-ref-size (push) Has been skipped
CI / Compare build size (push) Has been skipped
In the screens that use `DisplayApp *app` and pass it to a child item,
or use the reference just in the constructor. Afterwards the `app`
member is not used. So remove it from the private member variables.

Completely remove `app` parameter from `SettingDisplay` constructor as
it is unused.
2024-09-29 19:39:14 +02:00
NeroBurner a0cd439efc
Alarm persist to flash (#1367)
Some checks failed
CI / build-firmware (push) Successful in 13m37s
CI / build-simulator (push) Failing after 4s
CI / get-base-ref-size (push) Has been skipped
CI / Compare build size (push) Has been skipped
* AlarmController: Add saving alarm time to file

Save the set alarm time to the SPI NOR flash, so it does not reset to
the default value when the watch resets, e.g. due to watchdog timeout
or reflashing of a new version of InfiniTime.

Just like the `Settings.h` `LoadSettingsFromFile()` the previous alarm
at boot (if available) and `SaveSettingsToFile()` the current alarm when
the `Alarm.h` screen is closed (only if the settings have changed).

The alarm-settings file is stored in `.system/alarm.dat`. The `.system`
folder is created if it doesn't yet exist.

Fixes: https://github.com/InfiniTimeOrg/InfiniTime/issues/1330

* alarmController: close .system dir after usage

Close the `lfs_dir` object for the `.system` dir after usage. Otherwise
on the second changed alarm the system will lockup because the `.system`
dir is already open and was never closed.

---------

Co-authored-by: Galdor Takacs <g@ldor.de>
2024-09-28 08:14:08 +02:00
CCF100 1cbc664dc3
Merge branch 'InfiniTimeOrg:main' into LinearColorGradientForWeather 2024-05-18 12:11:45 -05:00
Caleb Fontenot 81b6ca78c1 whoops 2024-05-09 15:20:26 -05:00
Caleb Fontenot 73d670e1db Clean up code, revert WatchFaceTerminal changes, those are going in another PR 2024-05-09 15:07:07 -05:00
Caleb Fontenot 28ef439ba2 Remove node_modules 2024-05-09 14:30:34 -05:00
Caleb Fontenot d91fb50ba2 add node_modules to .gitignore 2024-05-09 14:26:46 -05:00
Caleb Fontenot 751937066b More progress, implement testing application, polish color gradient calculation code as well 2024-05-09 14:26:46 -05:00
Caleb Fontenot 7064843d60 More refactoring to make the code a bit more presentable :) 2024-05-09 14:26:46 -05:00
Caleb Fontenot cb16b03281 More refactoring to make the code a bit more presentable :) 2024-05-09 14:26:46 -05:00
Caleb Fontenot d2b0e78b11 Get WeatherHelper working again 2024-05-09 14:26:46 -05:00
Caleb Fontenot b146f55135 It builds again! 2024-05-09 14:26:46 -05:00
Caleb Fontenot 9b65b54617 still broken, getting there though 2024-05-09 14:26:46 -05:00
Caleb Fontenot 2482bdde51 More breakage 2024-05-09 14:26:46 -05:00
Caleb Fontenot d9e164cb4a More breakage 2024-05-09 14:26:46 -05:00
Caleb Fontenot 314b000308 WARNING, BROKEN: sync changes 2024-05-09 14:26:46 -05:00
Caleb Fontenot 41f9fbfa59 WARNING, BROKEN: sync changes 2024-05-09 14:26:46 -05:00
Caleb Fontenot aae2010385 More Code Refactoring 2024-05-09 14:26:46 -05:00
Caleb Fontenot f00be73a10 Code Refactor 2024-05-09 14:26:46 -05:00
Caleb Fontenot 0f1e2c04e0 Add additional transitional color 2024-05-09 14:26:46 -05:00
Caleb Fontenot 3c8f56a595 Add mem free printing (I need it to verify a suspicion relating to a bug) 2024-05-09 14:26:46 -05:00
Caleb Fontenot b1b3495af2 Get rid of the memory leak 2024-05-09 14:26:46 -05:00
Caleb Fontenot 67a2ae1125 Linear Gradients 2024-05-09 14:26:46 -05:00
Caleb Fontenot 4c1abe9c9b Add temperature readout to terminal watchface 2024-05-09 14:26:46 -05:00
28 changed files with 446 additions and 150 deletions

2
.gitignore vendored
View file

@ -11,7 +11,7 @@ cmake_install.cmake
Makefile
build
tools
node_modules/
# Resulting binary files
*.a
*.so

View file

@ -1,54 +1,7 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C>
<option name="INDENT_NAMESPACE_MEMBERS" value="2" />
<option name="INDENT_C_STRUCT_MEMBERS" value="2" />
<option name="INDENT_CLASS_MEMBERS" value="2" />
<option name="INDENT_INSIDE_CODE_BLOCK" value="2" />
<option name="INDENT_DIRECTIVE_AS_CODE" value="true" />
<option name="SPACE_BEFORE_TEMPLATE_DECLARATION_LT" value="true" />
<option name="SPACE_BEFORE_POINTER_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_POINTER_IN_DECLARATION" value="true" />
<option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" />
</Objective-C>
<Objective-C-extensions>
<rules>
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
<rule entity="CLASS" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="ENUM" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="LOCAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
</rules>
</Objective-C-extensions>
<clangFormatSettings>
<option name="ENABLED" value="true" />
</clangFormatSettings>
<codeStyleSettings language="ObjectiveC">
<option name="RIGHT_MARGIN" value="140" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="WRAP_ON_TYPING" value="1" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
<option name="LABEL_INDENT_ABSOLUTE" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

118
package-lock.json generated Normal file
View file

@ -0,0 +1,118 @@
{
"name": "InfiniTime",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"lv_font_conv": "^1.5.2"
}
},
"node_modules/lv_font_conv": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/lv_font_conv/-/lv_font_conv-1.5.2.tgz",
"integrity": "sha512-0UapRSTkVP/pnB8Z4r2HDHx5p2dJx/xUG1+14u/WXo59mwuC7BahR+Bnx/66jKoDrG1wFQwn9ZzoyMxRHOD9bg==",
"bundleDependencies": [
"argparse",
"bit-buffer",
"debug",
"make-error",
"mkdirp",
"opentype.js",
"pngjs"
],
"dependencies": {
"argparse": "^2.0.0",
"bit-buffer": "^0.2.5",
"debug": "^4.1.1",
"make-error": "^1.3.5",
"mkdirp": "^1.0.4",
"opentype.js": "^1.1.0",
"pngjs": "^6.0.0"
},
"bin": {
"lv_font_conv": "lv_font_conv.js"
}
},
"node_modules/lv_font_conv/node_modules/argparse": {
"version": "2.0.1",
"inBundle": true,
"license": "Python-2.0"
},
"node_modules/lv_font_conv/node_modules/bit-buffer": {
"version": "0.2.5",
"inBundle": true,
"license": "MIT"
},
"node_modules/lv_font_conv/node_modules/debug": {
"version": "4.3.1",
"inBundle": true,
"license": "MIT",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/lv_font_conv/node_modules/make-error": {
"version": "1.3.6",
"inBundle": true,
"license": "ISC"
},
"node_modules/lv_font_conv/node_modules/mkdirp": {
"version": "1.0.4",
"inBundle": true,
"license": "MIT",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/lv_font_conv/node_modules/ms": {
"version": "2.1.2",
"inBundle": true,
"license": "MIT"
},
"node_modules/lv_font_conv/node_modules/opentype.js": {
"version": "1.3.3",
"inBundle": true,
"license": "MIT",
"dependencies": {
"string.prototype.codepointat": "^0.2.1",
"tiny-inflate": "^1.0.3"
},
"bin": {
"ot": "bin/ot"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/lv_font_conv/node_modules/pngjs": {
"version": "6.0.0",
"inBundle": true,
"license": "MIT",
"engines": {
"node": ">=12.13.0"
}
},
"node_modules/lv_font_conv/node_modules/string.prototype.codepointat": {
"version": "0.2.1",
"inBundle": true,
"license": "MIT"
},
"node_modules/lv_font_conv/node_modules/tiny-inflate": {
"version": "1.0.3",
"inBundle": true,
"license": "MIT"
}
}
}

5
package.json Normal file
View file

@ -0,0 +1,5 @@
{
"dependencies": {
"lv_font_conv": "^1.5.2"
}
}

View file

@ -364,6 +364,7 @@ list(APPEND SOURCE_FILES
BootloaderVersion.cpp
logging/NrfLogger.cpp
displayapp/DisplayApp.cpp
displayapp/WeatherHelper.cpp
displayapp/screens/Screen.cpp
displayapp/screens/Tile.cpp
displayapp/screens/InfiniPaint.cpp
@ -588,6 +589,7 @@ set(INCLUDE_FILES
logging/Logger.h
logging/NrfLogger.h
displayapp/DisplayApp.h
displayapp/WeatherHelper.h
displayapp/Messages.h
displayapp/TouchEvents.h
displayapp/screens/Screen.h

View file

@ -19,11 +19,13 @@
#include "systemtask/SystemTask.h"
#include "task.h"
#include <chrono>
#include <libraries/log/nrf_log.h>
using namespace Pinetime::Controllers;
using namespace std::chrono_literals;
AlarmController::AlarmController(Controllers::DateTime& dateTimeController) : dateTimeController {dateTimeController} {
AlarmController::AlarmController(Controllers::DateTime& dateTimeController, Controllers::FS& fs)
: dateTimeController {dateTimeController}, fs {fs} {
}
namespace {
@ -36,11 +38,28 @@ namespace {
void AlarmController::Init(System::SystemTask* systemTask) {
this->systemTask = systemTask;
alarmTimer = xTimerCreate("Alarm", 1, pdFALSE, this, SetOffAlarm);
LoadSettingsFromFile();
if (alarm.isEnabled) {
NRF_LOG_INFO("[AlarmController] Loaded alarm was enabled, scheduling");
ScheduleAlarm();
}
}
void AlarmController::SaveAlarm() {
// verify if it is necessary to save
if (alarmChanged) {
SaveSettingsToFile();
}
alarmChanged = false;
}
void AlarmController::SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin) {
hours = alarmHr;
minutes = alarmMin;
if (alarm.hours == alarmHr && alarm.minutes == alarmMin) {
return;
}
alarm.hours = alarmHr;
alarm.minutes = alarmMin;
alarmChanged = true;
}
void AlarmController::ScheduleAlarm() {
@ -53,18 +72,19 @@ void AlarmController::ScheduleAlarm() {
tm* tmAlarmTime = std::localtime(&ttAlarmTime);
// If the time being set has already passed today,the alarm should be set for tomorrow
if (hours < dateTimeController.Hours() || (hours == dateTimeController.Hours() && minutes <= dateTimeController.Minutes())) {
if (alarm.hours < dateTimeController.Hours() ||
(alarm.hours == dateTimeController.Hours() && alarm.minutes <= dateTimeController.Minutes())) {
tmAlarmTime->tm_mday += 1;
// tm_wday doesn't update automatically
tmAlarmTime->tm_wday = (tmAlarmTime->tm_wday + 1) % 7;
}
tmAlarmTime->tm_hour = hours;
tmAlarmTime->tm_min = minutes;
tmAlarmTime->tm_hour = alarm.hours;
tmAlarmTime->tm_min = alarm.minutes;
tmAlarmTime->tm_sec = 0;
// if alarm is in weekday-only mode, make sure it shifts to the next weekday
if (recurrence == RecurType::Weekdays) {
if (alarm.recurrence == RecurType::Weekdays) {
if (tmAlarmTime->tm_wday == 0) { // Sunday, shift 1 day
tmAlarmTime->tm_mday += 1;
} else if (tmAlarmTime->tm_wday == 6) { // Saturday, shift 2 days
@ -79,7 +99,10 @@ void AlarmController::ScheduleAlarm() {
xTimerChangePeriod(alarmTimer, secondsToAlarm * configTICK_RATE_HZ, 0);
xTimerStart(alarmTimer, 0);
state = AlarmState::Set;
if (!alarm.isEnabled) {
alarm.isEnabled = true;
alarmChanged = true;
}
}
uint32_t AlarmController::SecondsToAlarm() const {
@ -88,20 +111,72 @@ uint32_t AlarmController::SecondsToAlarm() const {
void AlarmController::DisableAlarm() {
xTimerStop(alarmTimer, 0);
state = AlarmState::Not_Set;
isAlerting = false;
if (alarm.isEnabled) {
alarm.isEnabled = false;
alarmChanged = true;
}
}
void AlarmController::SetOffAlarmNow() {
state = AlarmState::Alerting;
isAlerting = true;
systemTask->PushMessage(System::Messages::SetOffAlarm);
}
void AlarmController::StopAlerting() {
// Alarm state is off unless this is a recurring alarm
if (recurrence == RecurType::None) {
state = AlarmState::Not_Set;
isAlerting = false;
// Disable alarm unless it is recurring
if (alarm.recurrence == RecurType::None) {
alarm.isEnabled = false;
alarmChanged = true;
} else {
// set next instance
ScheduleAlarm();
}
}
void AlarmController::SetRecurrence(RecurType recurrence) {
if (alarm.recurrence != recurrence) {
alarm.recurrence = recurrence;
alarmChanged = true;
}
}
void AlarmController::LoadSettingsFromFile() {
lfs_file_t alarmFile;
AlarmSettings alarmBuffer;
if (fs.FileOpen(&alarmFile, "/.system/alarm.dat", LFS_O_RDONLY) != LFS_ERR_OK) {
NRF_LOG_WARNING("[AlarmController] Failed to open alarm data file");
return;
}
fs.FileRead(&alarmFile, reinterpret_cast<uint8_t*>(&alarmBuffer), sizeof(alarmBuffer));
fs.FileClose(&alarmFile);
if (alarmBuffer.version != alarmFormatVersion) {
NRF_LOG_WARNING("[AlarmController] Loaded alarm settings has version %u instead of %u, discarding",
alarmBuffer.version,
alarmFormatVersion);
return;
}
alarm = alarmBuffer;
NRF_LOG_INFO("[AlarmController] Loaded alarm settings from file");
}
void AlarmController::SaveSettingsToFile() const {
lfs_dir systemDir;
if (fs.DirOpen("/.system", &systemDir) != LFS_ERR_OK) {
fs.DirCreate("/.system");
}
fs.DirClose(&systemDir);
lfs_file_t alarmFile;
if (fs.FileOpen(&alarmFile, "/.system/alarm.dat", LFS_O_WRONLY | LFS_O_CREAT) != LFS_ERR_OK) {
NRF_LOG_WARNING("[AlarmController] Failed to open alarm data file for saving");
return;
}
fs.FileWrite(&alarmFile, reinterpret_cast<const uint8_t*>(&alarm), sizeof(alarm));
fs.FileClose(&alarmFile);
NRF_LOG_INFO("[AlarmController] Saved alarm settings with format version %u to file", alarm.version);
}

View file

@ -30,47 +30,65 @@ namespace Pinetime {
namespace Controllers {
class AlarmController {
public:
AlarmController(Controllers::DateTime& dateTimeController);
AlarmController(Controllers::DateTime& dateTimeController, Controllers::FS& fs);
void Init(System::SystemTask* systemTask);
void SaveAlarm();
void SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin);
void ScheduleAlarm();
void DisableAlarm();
void SetOffAlarmNow();
uint32_t SecondsToAlarm() const;
void StopAlerting();
enum class AlarmState { Not_Set, Set, Alerting };
enum class RecurType { None, Daily, Weekdays };
uint8_t Hours() const {
return hours;
return alarm.hours;
}
uint8_t Minutes() const {
return minutes;
return alarm.minutes;
}
AlarmState State() const {
return state;
bool IsAlerting() const {
return isAlerting;
}
bool IsEnabled() const {
return alarm.isEnabled;
}
RecurType Recurrence() const {
return recurrence;
return alarm.recurrence;
}
void SetRecurrence(RecurType recurType) {
recurrence = recurType;
}
void SetRecurrence(RecurType recurrence);
private:
// Versions 255 is reserved for now, so the version field can be made
// bigger, should it ever be needed.
static constexpr uint8_t alarmFormatVersion = 1;
struct AlarmSettings {
uint8_t version = alarmFormatVersion;
uint8_t hours = 7;
uint8_t minutes = 0;
RecurType recurrence = RecurType::None;
bool isEnabled = false;
};
bool isAlerting = false;
bool alarmChanged = false;
Controllers::DateTime& dateTimeController;
Controllers::FS& fs;
System::SystemTask* systemTask = nullptr;
TimerHandle_t alarmTimer;
uint8_t hours = 7;
uint8_t minutes = 0;
AlarmSettings alarm;
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> alarmTime;
AlarmState state = AlarmState::Not_Set;
RecurType recurrence = RecurType::None;
void LoadSettingsFromFile();
void SaveSettingsToFile() const;
};
}
}

View file

@ -18,6 +18,8 @@
#include "components/ble/MusicService.h"
#include "components/ble/NimbleController.h"
#include <cstring>
#include <FreeRTOS.h>
#include <task.h>
namespace {
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0

View file

@ -25,6 +25,7 @@
#include <host/ble_uuid.h>
#undef max
#undef min
#include <FreeRTOS.h>
namespace Pinetime {
namespace Controllers {

View file

@ -60,7 +60,7 @@ namespace Pinetime {
Smog = 8, // Mist
Unknown = 255
};
using Location = std::array<char, 33>; // 32 char + \0 (end of string)
struct CurrentWeather {
@ -111,6 +111,10 @@ namespace Pinetime {
static int16_t CelsiusToFahrenheit(int16_t celsius) {
return celsius * 9 / 5 + 3200;
}
static const char* TemperatureColor(int16_t temperature);
static int16_t RoundTemperature(int16_t temp);
private:
// 00050000-78fc-48fe-8e23-433b3a1942d0
@ -125,7 +129,7 @@ namespace Pinetime {
}
ble_uuid128_t weatherUuid {BaseUuid()};
ble_uuid128_t weatherDataCharUuid {CharUuid(0x00, 0x01)};
const struct ble_gatt_chr_def characteristicDefinition[2] = {{.uuid = &weatherDataCharUuid.u,

View file

@ -582,7 +582,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
currentScreen = std::make_unique<Screens::SettingWakeUp>(settingsController);
break;
case Apps::SettingDisplay:
currentScreen = std::make_unique<Screens::SettingDisplay>(this, settingsController);
currentScreen = std::make_unique<Screens::SettingDisplay>(settingsController);
break;
case Apps::SettingSteps:
currentScreen = std::make_unique<Screens::SettingSteps>(settingsController);

View file

@ -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 {

View file

@ -0,0 +1,110 @@
/* Copyright (C) 2024 Caleb Fontenot
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "WeatherHelper.h"
#include <FreeRTOS.h>
#include <lvgl/src/lv_misc/lv_color.h>
#include <tuple>
#include <vector>
#include <array>
#include <cstdint>
#include <cstdio>
#include <algorithm>
#include <nrfx_log.h>
using namespace Pinetime::Applications;
//Linear gradient temperature color calculator :)
int16_t WeatherHelper::RoundTemperature(int16_t temp) {
return temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0);
}
std::tuple<int, int, int> rgb565to888(int r, int g, int b) {
return {
( r * 527 + 23 ) >> 6,
( g * 259 + 33 ) >> 6,
( b * 527 + 23 ) >> 6
};
}
const char* WeatherHelper::floatToRgbHex(lv_color_t rgb) {
std::tuple<int, int, int> tuple = rgb565to888(LV_COLOR_GET_R(rgb), LV_COLOR_GET_G(rgb), LV_COLOR_GET_B(rgb));
char *rgbHex = new char[7];
snprintf(rgbHex, 7, "%02X%02X%02X", std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple));
return rgbHex;
}
lv_color_t hexToFloat(int rgb) {
return lv_color_hex(rgb);
}
float normalize(float value) {
if (value < 0.0f) {
return 0.0f;
} else if (value > 1.0f) {
return 1.0f;
} else {
return value;
}
}
// reference: https://dev.to/ndesmic/linear-color-gradients-from-scratch-1a0e
const lv_color_t lerp(lv_color_t pointA, lv_color_t pointB, float normalValue) {
auto [redA, greenA, blueA] = rgb565to888(LV_COLOR_GET_R(pointA), LV_COLOR_GET_G(pointA), LV_COLOR_GET_B(pointA));
auto [redB, greenB, blueB] = rgb565to888(LV_COLOR_GET_R(pointB), LV_COLOR_GET_G(pointB), LV_COLOR_GET_B(pointB));
NRF_LOG_INFO("Normal value: %f", normalValue);
int outputRed = (redA + (redB - redA) * normalValue);
int outputGreen = (greenA + (greenB - greenA) * normalValue);
int outputBlue = (blueA + (blueB - blueA) * normalValue);
//increase brightness
float incAmount = 1.2;
outputRed = std::min(255, static_cast<int>(outputRed*incAmount));
outputGreen = std::min(255, static_cast<int>(outputGreen*incAmount));
outputBlue = std::min(255, static_cast<int>(outputBlue*incAmount));
auto lerpOutput = LV_COLOR_MAKE(outputRed, outputGreen, outputBlue);
NRF_LOG_INFO("pointA: %i, %i, %i", redA, greenA, blueA);
NRF_LOG_INFO("pointB: %i, %i, %i", redB, greenB, blueB);
NRF_LOG_INFO("lerpOutput: %i, %i, %i", LV_COLOR_GET_R(lerpOutput), LV_COLOR_GET_G(lerpOutput), LV_COLOR_GET_B(lerpOutput));
return lerpOutput;
}
constexpr std::array<lv_color_t, 4> getColors() {
const std::array<int, 4> colors = {0x5555ff, 0x00c9ff, 0xff9b00, 0xff0000};
std::array<lv_color_t, 4> stops;
int8_t i = 0;
for (auto colorVal: colors) {
stops[i++] = (hexToFloat(colorVal));
}
return stops;
}
const lv_color_t WeatherHelper::TemperatureColor(int16_t temperature) {
std::array<lv_color_t, 4> stops = getColors();
int tempRounded = RoundTemperature(temperature);
if (tempRounded < 0) {
tempRounded = 1;
}
// convert temperature to range between newMin and newMax
float oldRange = (oldMax - oldMin);
float newRange = (newMax - newMin);
float newValue = (((tempRounded - oldMin) * newRange) / oldRange) + newMin;
newValue = normalize(newValue);
return lerp(stops[0], stops[3], newValue);
}

View file

@ -0,0 +1,35 @@
/* Copyright (C) 2024 Caleb Fontenot
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <FreeRTOS.h>
#include <lvgl/src/lv_misc/lv_color.h>
namespace Pinetime {
namespace Applications {
class WeatherHelper {
public:
static int16_t RoundTemperature(int16_t temp);
static const lv_color_t TemperatureColor(int16_t temperature);
static const char* floatToRgbHex(lv_color_t rgb);
constexpr static float oldMax = 50;
constexpr static float oldMin = 0;
constexpr static float newMax = 1;
constexpr static float newMin = 0;
};
}
}

View file

@ -117,7 +117,7 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
UpdateAlarmTime();
if (alarmController.State() == Controllers::AlarmController::AlarmState::Alerting) {
if (alarmController.IsAlerting()) {
SetAlerting();
} else {
SetSwitchState(LV_ANIM_OFF);
@ -125,14 +125,15 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
}
Alarm::~Alarm() {
if (alarmController.State() == AlarmController::AlarmState::Alerting) {
if (alarmController.IsAlerting()) {
StopAlerting();
}
lv_obj_clean(lv_scr_act());
alarmController.SaveAlarm();
}
void Alarm::DisableAlarm() {
if (alarmController.State() == AlarmController::AlarmState::Set) {
if (alarmController.IsEnabled()) {
alarmController.DisableAlarm();
lv_switch_off(enableSwitch, LV_ANIM_ON);
}
@ -172,7 +173,7 @@ bool Alarm::OnButtonPushed() {
HideInfo();
return true;
}
if (alarmController.State() == AlarmController::AlarmState::Alerting) {
if (alarmController.IsAlerting()) {
StopAlerting();
return true;
}
@ -181,7 +182,7 @@ bool Alarm::OnButtonPushed() {
bool Alarm::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
// Don't allow closing the screen by swiping while the alarm is alerting
return alarmController.State() == AlarmController::AlarmState::Alerting && event == TouchEvents::SwipeDown;
return alarmController.IsAlerting() && event == TouchEvents::SwipeDown;
}
void Alarm::OnValueChanged() {
@ -222,15 +223,10 @@ void Alarm::StopAlerting() {
}
void Alarm::SetSwitchState(lv_anim_enable_t anim) {
switch (alarmController.State()) {
case AlarmController::AlarmState::Set:
lv_switch_on(enableSwitch, anim);
break;
case AlarmController::AlarmState::Not_Set:
lv_switch_off(enableSwitch, anim);
break;
default:
break;
if (alarmController.IsEnabled()) {
lv_switch_on(enableSwitch, anim);
} else {
lv_switch_off(enableSwitch, anim);
}
}
@ -247,7 +243,7 @@ void Alarm::ShowInfo() {
txtMessage = lv_label_create(btnMessage, nullptr);
lv_obj_set_style_local_bg_color(btnMessage, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_NAVY);
if (alarmController.State() == AlarmController::AlarmState::Set) {
if (alarmController.IsEnabled()) {
auto timeToAlarm = alarmController.SecondsToAlarm();
auto daysToAlarm = timeToAlarm / 86400;

View file

@ -8,6 +8,7 @@
#include "displayapp/apps/Apps.h"
#include "components/settings/Settings.h"
#define MAXLISTITEMS 4
namespace Pinetime {

View file

@ -40,8 +40,7 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp* app,
Pinetime::Controllers::MotionController& motionController,
const Pinetime::Drivers::Cst816S& touchPanel,
const Pinetime::Drivers::SpiNorFlash& spiNorFlash)
: app {app},
dateTimeController {dateTimeController},
: dateTimeController {dateTimeController},
batteryController {batteryController},
brightnessController {brightnessController},
bleController {bleController},
@ -81,7 +80,7 @@ std::unique_ptr<Screen> 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"
"#808080 Version# %ld.%ld.%ld\n"
"#808080 Short Ref# %s\n"
"#808080 Build date#\n"

View file

@ -35,7 +35,6 @@ namespace Pinetime {
bool OnTouchEvent(TouchEvents event) override;
private:
DisplayApp* app;
Pinetime::Controllers::DateTime& dateTimeController;
const Pinetime::Controllers::Battery& batteryController;
Pinetime::Controllers::BrightnessController& brightnessController;

View file

@ -1,4 +1,5 @@
#include "displayapp/screens/Weather.h"
#include "displayapp/WeatherHelper.h"
#include <lvgl/lvgl.h>
#include "components/ble/SimpleWeatherService.h"
#include "components/datetime/DateTimeController.h"
@ -9,34 +10,6 @@
using namespace Pinetime::Applications::Screens;
namespace {
lv_color_t TemperatureColor(int16_t temperature) {
if (temperature <= 0) { // freezing
return Colors::blue;
} else if (temperature <= 400) { // ice
return LV_COLOR_CYAN;
} else if (temperature >= 2700) { // hot
return Colors::deepOrange;
}
return Colors::orange; // normal
}
uint8_t TemperatureStyle(int16_t temperature) {
if (temperature <= 0) { // freezing
return LV_TABLE_PART_CELL3;
} else if (temperature <= 400) { // ice
return LV_TABLE_PART_CELL4;
} else if (temperature >= 2700) { // hot
return LV_TABLE_PART_CELL6;
}
return LV_TABLE_PART_CELL5; // normal
}
int16_t RoundTemperature(int16_t temp) {
return temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0);
}
}
Weather::Weather(Controllers::Settings& settingsController, Controllers::SimpleWeatherService& weatherService)
: settingsController {settingsController}, weatherService {weatherService} {
@ -123,7 +96,7 @@ void Weather::Refresh() {
int16_t temp = optCurrentWeather->temperature;
int16_t minTemp = optCurrentWeather->minTemperature;
int16_t maxTemp = optCurrentWeather->maxTemperature;
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, TemperatureColor(temp));
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, WeatherHelper::TemperatureColor(temp));
char tempUnit = 'C';
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
temp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(temp);
@ -133,9 +106,11 @@ void Weather::Refresh() {
}
lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId));
lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId));
lv_label_set_text_fmt(temperature, "%d°%c", RoundTemperature(temp), tempUnit);
lv_label_set_text_fmt(minTemperature, "%d°", RoundTemperature(minTemp));
lv_label_set_text_fmt(maxTemperature, "%d°", RoundTemperature(maxTemp));
lv_label_set_text_fmt(temperature, "%d°%c", WeatherHelper::RoundTemperature(temp), tempUnit);
lv_label_set_text_fmt(minTemperature, "%d°", WeatherHelper::RoundTemperature(minTemp));
lv_label_set_text_fmt(maxTemperature, "%d°", WeatherHelper::RoundTemperature(maxTemp));
} else {
lv_label_set_text(icon, "");
lv_label_set_text(condition, "");
@ -155,8 +130,14 @@ void Weather::Refresh() {
for (int i = 0; i < Controllers::SimpleWeatherService::MaxNbForecastDays; i++) {
int16_t maxTemp = optCurrentForecast->days[i].maxTemperature;
int16_t minTemp = optCurrentForecast->days[i].minTemperature;
lv_table_set_cell_type(forecast, 2, i, TemperatureStyle(maxTemp));
lv_table_set_cell_type(forecast, 3, i, TemperatureStyle(minTemp));
auto color = WeatherHelper::TemperatureColor(maxTemp);
lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL5, LV_STATE_DEFAULT, color);
lv_table_set_cell_type(forecast, 2, i, LV_TABLE_PART_CELL5);
color = WeatherHelper::TemperatureColor(minTemp);
lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL6, LV_STATE_DEFAULT, color);
lv_table_set_cell_type(forecast, 3, i, LV_TABLE_PART_CELL6);
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
maxTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(maxTemp);
minTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(minTemp);
@ -165,8 +146,8 @@ void Weather::Refresh() {
if (wday > 7) {
wday -= 7;
}
maxTemp = RoundTemperature(maxTemp);
minTemp = RoundTemperature(minTemp);
maxTemp = WeatherHelper::RoundTemperature(maxTemp);
minTemp = WeatherHelper::RoundTemperature(minTemp);
const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast<Controllers::DateTime::Days>(wday));
lv_table_set_cell_value(forecast, 0, i, dayOfWeek);
lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i].iconId));
@ -181,7 +162,11 @@ void Weather::Refresh() {
maxPadding[0] = '\0';
minPadding[diff] = '\0';
}
//auto color = WeatherHelper::TemperatureColor(maxTemp);
//lv_obj_set_style_local_text_color(forecast, 2, i, color);
lv_table_set_cell_value_fmt(forecast, 2, i, "%s%d", maxPadding, maxTemp);
//color = WeatherHelper::TemperatureColor(minTemp);
//lv_obj_set_style_local_text_color(forecast, 3, i, color);
lv_table_set_cell_value_fmt(forecast, 3, i, "%s%d", minPadding, minTemp);
}
} else {

View file

@ -24,7 +24,7 @@ namespace Pinetime {
~Weather() override;
void Refresh() override;
private:
Controllers::Settings& settingsController;
Controllers::SimpleWeatherService& weatherService;

View file

@ -24,8 +24,7 @@ namespace {
constexpr std::array<uint16_t, 6> SettingDisplay::options;
SettingDisplay::SettingDisplay(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController)
: app {app}, settingsController {settingsController} {
SettingDisplay::SettingDisplay(Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} {
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);

View file

@ -14,14 +14,13 @@ namespace Pinetime {
class SettingDisplay : public Screen {
public:
SettingDisplay(DisplayApp* app, Pinetime::Controllers::Settings& settingsController);
SettingDisplay(Pinetime::Controllers::Settings& settingsController);
~SettingDisplay() override;
void UpdateSelected(lv_obj_t* object, lv_event_t event);
void ToggleAlwaysOn();
private:
DisplayApp* app;
static constexpr std::array<uint16_t, 6> options = {5000, 7000, 10000, 15000, 20000, 30000};
Controllers::Settings& settingsController;

View file

@ -15,8 +15,7 @@ bool SettingSetDateTime::OnTouchEvent(Pinetime::Applications::TouchEvents event)
SettingSetDateTime::SettingSetDateTime(Pinetime::Applications::DisplayApp* app,
Pinetime::Controllers::DateTime& dateTimeController,
Pinetime::Controllers::Settings& settingsController)
: app {app},
dateTimeController {dateTimeController},
: dateTimeController {dateTimeController},
settingsController {settingsController},
screens {app,
0,

View file

@ -20,7 +20,6 @@ namespace Pinetime {
void Quit();
private:
DisplayApp* app;
Controllers::DateTime& dateTimeController;
Controllers::Settings& settingsController;

View file

@ -54,8 +54,7 @@ SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app,
std::array<Screens::SettingWatchFace::Item, UserWatchFaceTypes::Count>&& watchfaceItems,
Pinetime::Controllers::Settings& settingsController,
Pinetime::Controllers::FS& filesystem)
: app {app},
watchfaceItems {std::move(watchfaceItems)},
: watchfaceItems {std::move(watchfaceItems)},
settingsController {settingsController},
filesystem {filesystem},
screens {app, 0, CreateScreenList(), Screens::ScreenListModes::UpDown} {

View file

@ -34,7 +34,6 @@ namespace Pinetime {
bool OnTouchEvent(TouchEvents event) override;
private:
DisplayApp* app;
auto CreateScreenList() const;
std::unique_ptr<Screen> CreateScreen(unsigned int screenNum) const;

View file

@ -104,7 +104,7 @@ Pinetime::Controllers::DateTime dateTimeController {settingsController};
Pinetime::Drivers::Watchdog watchdog;
Pinetime::Controllers::NotificationManager notificationManager;
Pinetime::Controllers::MotionController motionController;
Pinetime::Controllers::AlarmController alarmController {dateTimeController};
Pinetime::Controllers::AlarmController alarmController {dateTimeController, fs};
Pinetime::Controllers::TouchHandler touchHandler;
Pinetime::Controllers::ButtonHandler buttonHandler;
Pinetime::Controllers::BrightnessController brightnessController {};

View file

@ -216,7 +216,7 @@ void SystemTask::Work() {
GoToSleep();
break;
case Messages::OnNewTime:
if (alarmController.State() == Controllers::AlarmController::AlarmState::Set) {
if (alarmController.IsEnabled()) {
alarmController.ScheduleAlarm();
}
break;
@ -317,8 +317,7 @@ void SystemTask::Work() {
case Messages::OnNewHour:
using Pinetime::Controllers::AlarmController;
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours &&
alarmController.State() != AlarmController::AlarmState::Alerting) {
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours && !alarmController.IsAlerting()) {
GoToRunning();
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
}
@ -326,8 +325,7 @@ void SystemTask::Work() {
case Messages::OnNewHalfHour:
using Pinetime::Controllers::AlarmController;
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours &&
alarmController.State() != AlarmController::AlarmState::Alerting) {
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours && !alarmController.IsAlerting()) {
GoToRunning();
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
}