diff --git a/.gitignore b/.gitignore
index d6f917cfc4..13b939a3f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,10 @@ src/arm-none-eabi
node_modules
package.json
package-lock.json
+
+# Toolchain and External dependencies
+sdk-toolchain/
+
+# Developer files
+CMakeUserPresets.json
+compile_commands.json
diff --git a/doc/gettingStarted/Applications.md b/doc/gettingStarted/Applications.md
index 8ca2b25287..1f48f1fd7a 100644
--- a/doc/gettingStarted/Applications.md
+++ b/doc/gettingStarted/Applications.md
@@ -16,6 +16,7 @@ InfiniTime has 13 apps on the `main` branch at the time of writing.
- Metronome
- Maps
- Weather
+- Sleeptracking
### Stopwatch

@@ -97,3 +98,10 @@ InfiniTime has 13 apps on the `main` branch at the time of writing.

- This app shows weather info.
- Please note that this app is not very useful without a device connected.
+
+### SleepTracking
+
+- This app records your body movement and heartrate and wakes you up at the specified time.
+- Sleeptracking files can be accessed through the files API in the `/logs/sleep` directory.
+ - Session files are comma-separated data files of the format: `%YYYY-%MM-%ddT%hh:%mm:%ss,heartrate,motionx,motiony,motionz`.
+ - To save space session files are rotated, only max 10 sessions are saved at a time.
diff --git a/doc/gettingStarted/AppsScreenshots/Sleeptracking.png b/doc/gettingStarted/AppsScreenshots/Sleeptracking.png
new file mode 100644
index 0000000000..4d65bd0aa5
Binary files /dev/null and b/doc/gettingStarted/AppsScreenshots/Sleeptracking.png differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5cd2e656a4..e9f50aa227 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -396,6 +396,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/PassKey.cpp
displayapp/screens/Error.cpp
displayapp/screens/Alarm.cpp
+ displayapp/screens/Sleep.cpp
displayapp/screens/Styles.cpp
displayapp/screens/WeatherSymbols.cpp
displayapp/Colors.cpp
@@ -469,6 +470,7 @@ list(APPEND SOURCE_FILES
components/settings/Settings.cpp
components/timer/Timer.cpp
components/alarm/AlarmController.cpp
+ components/sleeptracking/SleepTrackingController.cpp
components/fs/FS.cpp
drivers/Cst816s.cpp
FreeRTOS/port.c
@@ -538,6 +540,7 @@ list(APPEND RECOVERY_SOURCE_FILES
components/settings/Settings.cpp
components/timer/Timer.cpp
components/alarm/AlarmController.cpp
+ components/sleeptracking/SleepTrackingController.cpp
drivers/Cst816s.cpp
FreeRTOS/port.c
FreeRTOS/port_cmsis_systick.c
@@ -615,6 +618,7 @@ set(INCLUDE_FILES
displayapp/screens/Timer.h
displayapp/screens/Dice.h
displayapp/screens/Alarm.h
+ displayapp/screens/Sleep.h
displayapp/Colors.h
displayapp/widgets/Counter.h
displayapp/widgets/PageIndicator.h
@@ -657,6 +661,7 @@ set(INCLUDE_FILES
components/settings/Settings.h
components/timer/Timer.h
components/alarm/AlarmController.h
+ components/sleeptracking/SleepTrackingController.h
drivers/Cst816s.h
FreeRTOS/portmacro.h
FreeRTOS/portmacro_cmsis.h
diff --git a/src/components/sleeptracking/SleepTrackingController.cpp b/src/components/sleeptracking/SleepTrackingController.cpp
new file mode 100644
index 0000000000..b7c6900ce0
--- /dev/null
+++ b/src/components/sleeptracking/SleepTrackingController.cpp
@@ -0,0 +1,303 @@
+/* Copyright (C) 2025 Asger Gitz-Johansen
+
+ 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 .
+*/
+#include "SleepTrackingController.h"
+
+#include
+#include
+#include
+#include
+
+#define pdSEC_TO_TICKS(n) pdMS_TO_TICKS(n * 1000)
+#define pdMIN_TO_TICKS(n) pdSEC_TO_TICKS(n * 60)
+#ifndef MIN // Wrap in an ifndef becuase the simulator doesn't autoinclude it.
+ #define MIN(a, b) ((a < b) ? (a) : (b))
+#endif
+
+namespace {
+ void MotionTrackingTimerTrigger(TimerHandle_t xTimer) {
+ static_cast(pvTimerGetTimerID(xTimer))->OnMotionTrackingTimerTrigger();
+ }
+
+ void HeartRateTrackingTimerTrigger(TimerHandle_t xTimer) {
+ static_cast(pvTimerGetTimerID(xTimer))->OnHeartRateTrackingTimerTrigger();
+ }
+
+ void StoreDataTimerTrigger(TimerHandle_t xTimer) {
+ static_cast(pvTimerGetTimerID(xTimer))->OnStoreDataTimerTrigger();
+ }
+
+ void GentleWakeupTimerTrigger(TimerHandle_t xTimer) {
+ static_cast(pvTimerGetTimerID(xTimer))->OnGentleWakeupTimerTrigger();
+ }
+
+ void WakeAlarmTimerTrigger(TimerHandle_t xTimer) {
+ static_cast(pvTimerGetTimerID(xTimer))->OnWakeAlarmTrigger();
+ }
+}
+
+namespace Pinetime::Controllers {
+ SleepTrackingController::SleepTrackingController(FS& filesystem,
+ DateTime& datetimeController,
+ Pinetime::Drivers::Bma421& motionSensor,
+ HeartRateController& heartRateController,
+ MotorController& motorController)
+ : settings {},
+ systemTask {nullptr},
+ filesystem {filesystem},
+ datetimeController {datetimeController},
+
+ // Sleep tracking related
+ motionSensor {motionSensor},
+ heartRateController {heartRateController},
+ motionTrackingTimer {},
+ heartRateTrackingTimer {},
+ storeDataTimer {},
+ currentDataPoint {},
+ previousValues {},
+ hasPreviousValues {false},
+
+ // Wakeup related
+ wakeupAlarmTimer {},
+ gentleWakeupTimer {},
+ motorController {motorController},
+ vibrationDurationMillis {wakeAlarmVibrationDurationStart},
+ isAlerting {false} {
+ }
+
+ void SleepTrackingController::Init(System::SystemTask* systemTask) {
+ this->systemTask = systemTask;
+ motionTrackingTimer = xTimerCreate("sampleMotion", pdSEC_TO_TICKS(2), pdFALSE, this, MotionTrackingTimerTrigger);
+ heartRateTrackingTimer = xTimerCreate("sampleHR", pdMIN_TO_TICKS(2), pdFALSE, this, HeartRateTrackingTimerTrigger);
+ storeDataTimer = xTimerCreate("storeData", pdMIN_TO_TICKS(3), pdFALSE, this, StoreDataTimerTrigger);
+ wakeupAlarmTimer = xTimerCreate("wakeupAlarm", 1, pdFALSE, this, WakeAlarmTimerTrigger);
+ gentleWakeupTimer = xTimerCreate("gentleWakeup", pdSEC_TO_TICKS(10), pdFALSE, this, GentleWakeupTimerTrigger);
+ LoadSettings();
+
+ if (settings.isTracking) {
+ StartTracking();
+ NRF_LOG_INFO("[SleepTrackingController] Sleep tracking resumed");
+ }
+ }
+
+ void SleepTrackingController::StartTracking() {
+ // Reset tracking data.
+ previousValues = {};
+ hasPreviousValues = false;
+ currentDataPoint = {};
+ settings.isTracking = true;
+ SaveSettings();
+ ScheduleWakeAlarm();
+
+ // Start tracking timers.
+ xTimerStart(motionTrackingTimer, 0);
+ xTimerStart(heartRateTrackingTimer, 0);
+ xTimerStart(storeDataTimer, 0);
+ NRF_LOG_INFO("[SleepTrackingController] Sleep tracking started");
+ }
+
+ void SleepTrackingController::StopTracking() {
+ // Stop tracking timers.
+ xTimerStop(motionTrackingTimer, 0);
+ xTimerStop(heartRateTrackingTimer, 0);
+ xTimerStop(storeDataTimer, 0);
+ settings.isTracking = false;
+ settings.currentSession = (settings.currentSession + 1) % 10;
+ SaveSettings();
+ DismissWakeAlarm();
+ ClearTrackingFile();
+ NRF_LOG_INFO("[SleepTrackingController] Sleep tracking stopped");
+ }
+
+ void SleepTrackingController::OnMotionTrackingTimerTrigger() {
+ auto data = motionSensor.Process();
+ if (hasPreviousValues) {
+ currentDataPoint.xDiffSum += ABS(previousValues.x) - ABS(data.x);
+ currentDataPoint.yDiffSum += ABS(previousValues.y) - ABS(data.y);
+ currentDataPoint.zDiffSum += ABS(previousValues.z) - ABS(data.z);
+ }
+ previousValues = data;
+ hasPreviousValues = true;
+ xTimerStart(motionTrackingTimer, 0);
+ }
+
+ void SleepTrackingController::OnHeartRateTrackingTimerTrigger() {
+ currentDataPoint.heartRate = heartRateController.HeartRate();
+ xTimerStart(heartRateTrackingTimer, 0);
+ }
+
+ void SleepTrackingController::OnStoreDataTimerTrigger() {
+ systemTask->PushMessage(Pinetime::System::Messages::OnSleepTrackingDataPoint);
+ }
+
+ void SleepTrackingController::SaveDatapoint() {
+ lfs_file_t sleepDataFile;
+ auto day = datetimeController.Day();
+ auto month = datetimeController.Month();
+ auto year = datetimeController.Year();
+ auto hours = datetimeController.Hours();
+ auto minutes = datetimeController.Minutes();
+ auto seconds = datetimeController.Seconds();
+
+ char filename[32] {};
+ snprintf(filename, 32, "logs/sleep/session-%d.csv", settings.currentSession);
+
+ // Ensure that the subdirectory exists.
+ lfs_dir logdir {};
+ if (filesystem.DirOpen("logs", &logdir) != LFS_ERR_OK) {
+ filesystem.DirCreate("logs");
+ }
+ filesystem.DirClose(&logdir);
+ if (filesystem.DirOpen("logs/sleep", &logdir) != LFS_ERR_OK) {
+ filesystem.DirCreate("logs/sleep");
+ }
+ filesystem.DirClose(&logdir);
+
+ if (filesystem.FileOpen(&sleepDataFile, filename, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) < 0) {
+ NRF_LOG_WARNING("[SleepTrackingController] Failed to open '%s' file", filename);
+ xTimerStart(storeDataTimer, 0);
+ return;
+ }
+
+ char buffer[64];
+ auto len = snprintf(buffer,
+ sizeof(buffer),
+ "%04d-%02d-%02dT%02d:%02d:%02d,%d,%d,%d,%d\n",
+ year,
+ static_cast(month),
+ day,
+ hours,
+ minutes,
+ seconds,
+ currentDataPoint.heartRate,
+ currentDataPoint.xDiffSum,
+ currentDataPoint.yDiffSum,
+ currentDataPoint.zDiffSum);
+ filesystem.FileWrite(&sleepDataFile, reinterpret_cast(buffer), len);
+ filesystem.FileClose(&sleepDataFile);
+ xTimerStart(storeDataTimer, 0);
+ }
+
+ void SleepTrackingController::DismissWakeAlarm() {
+ isAlerting = false;
+ xTimerStop(wakeupAlarmTimer, 0);
+ xTimerStop(gentleWakeupTimer, 0);
+ vibrationDurationMillis = wakeAlarmVibrationDurationStart;
+ if (!isAlerting) {
+ return;
+ }
+ motorController.StopRinging();
+ }
+
+ void SleepTrackingController::LoadSettings() {
+ lfs_file_t settingsFile;
+ Settings settingsBuffer;
+ if (filesystem.FileOpen(&settingsFile, settingsFileName, LFS_O_RDONLY) < 0) {
+ NRF_LOG_WARNING("[SleepTrackingController] Failed to open settings file");
+ return;
+ }
+
+ filesystem.FileRead(&settingsFile, reinterpret_cast(&settingsBuffer), sizeof(settingsBuffer));
+ filesystem.FileClose(&settingsFile);
+ if (settingsBuffer.version != sleeptrackingSettingsFormatVersion) {
+ NRF_LOG_WARNING("[SleepTrackingController] Loaded settings has version %u instead of %u, discarding",
+ settingsBuffer.version,
+ sleeptrackingSettingsFormatVersion);
+ return;
+ }
+
+ settings = settingsBuffer;
+ NRF_LOG_INFO("[SleepTrackingController] Loaded settings from file");
+ }
+
+ void SleepTrackingController::SaveSettings() {
+ lfs_file_t settingsFile;
+ if (filesystem.FileOpen(&settingsFile, settingsFileName, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) != LFS_ERR_OK) {
+ NRF_LOG_WARNING("[SleepTrackingController] Failed to open settings file");
+ return;
+ }
+ filesystem.FileWrite(&settingsFile, reinterpret_cast(&settings), sizeof(settings));
+ filesystem.FileClose(&settingsFile);
+ NRF_LOG_INFO("[SleepTrackingController] Saved settings to file");
+ }
+
+ void SleepTrackingController::OnWakeAlarmTrigger() {
+ isAlerting = true;
+ // Notify the system that the wake alarm is triggered, so we can show the alarm dismissal screen.
+ systemTask->PushMessage(System::Messages::SetOffWakeAlarm);
+ }
+
+ void SleepTrackingController::OnGentleWakeupTimerTrigger() {
+ // TODO: Also set intensity when motorcontroller supports it. (start low, end medium)
+ motorController.RunForDuration(vibrationDurationMillis);
+ vibrationDurationMillis = MIN(vibrationDurationMillis + 100, static_cast(1000));
+ xTimerStart(gentleWakeupTimer, 0);
+ }
+
+ void SleepTrackingController::ScheduleWakeAlarm() {
+ // Determine the next time the alarm needs to go off and set the timer
+ xTimerStop(wakeupAlarmTimer, 0);
+ auto now = datetimeController.CurrentDateTime();
+ auto ttAlarmTime = std::chrono::system_clock::to_time_t(std::chrono::time_point_cast(now));
+ auto* tmAlarmTime = std::localtime(&ttAlarmTime);
+ // If the time being set has already passed today,the alarm should be set for tomorrow
+ if (settings.alarm.hours < datetimeController.Hours() ||
+ (settings.alarm.hours == datetimeController.Hours() && settings.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 = settings.alarm.hours;
+ tmAlarmTime->tm_min = settings.alarm.minutes;
+ tmAlarmTime->tm_sec = 0;
+ tmAlarmTime->tm_isdst = -1; // use system timezone setting to determine DST
+ // now can convert back to a time_point
+ auto alarmTime = std::chrono::system_clock::from_time_t(std::mktime(tmAlarmTime));
+ auto secondsToAlarm = std::chrono::duration_cast(alarmTime - now).count();
+ xTimerChangePeriod(wakeupAlarmTimer, secondsToAlarm * configTICK_RATE_HZ, 0);
+ xTimerStart(wakeupAlarmTimer, 0);
+ NRF_LOG_INFO("[SleepTrackingController] New alarm scheduled in %d seconds", secondsToAlarm);
+ }
+
+ void SleepTrackingController::ClearTrackingFile() {
+ char filename[32] {};
+ snprintf(filename, 32, "logs/sleep/session-%d.csv", settings.currentSession);
+ lfs_info info;
+ filesystem.Stat(filename, &info);
+ if (info.size > 0) {
+ lfs_file_t file;
+ filesystem.FileOpen(&file, filename, LFS_O_CREAT | LFS_O_WRONLY | LFS_O_TRUNC); // NOTE: TRUNC = truncate
+ filesystem.FileClose(&file);
+ }
+ }
+
+ SleepTrackingController::Settings SleepTrackingController::GetSettings() {
+ return settings;
+ }
+
+ void SleepTrackingController::SetSettings(const Settings& newSettings) {
+ settings = newSettings;
+ }
+
+ bool SleepTrackingController::IsAlerting() const {
+ return isAlerting;
+ }
+
+ bool SleepTrackingController::IsTracking() const {
+ return settings.isTracking;
+ }
+}
diff --git a/src/components/sleeptracking/SleepTrackingController.h b/src/components/sleeptracking/SleepTrackingController.h
new file mode 100644
index 0000000000..3162155547
--- /dev/null
+++ b/src/components/sleeptracking/SleepTrackingController.h
@@ -0,0 +1,110 @@
+/* Copyright (C) 2025 Asger Gitz-Johansen
+
+ 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 .
+*/
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Pinetime::Controllers {
+ class SleepTrackingController {
+ private:
+ using timepoint = std::chrono::time_point;
+ static constexpr uint8_t sleeptrackingSettingsFormatVersion = 1;
+ static constexpr uint8_t maxSavedSessions = 14; // A night is ~5.5KiB (pessimistic estimate) = max(ish) 77KiB usage.
+ static constexpr const char* settingsFileName = "sleeptracksettings.dat";
+ static constexpr uint8_t wakeAlarmVibrationDurationStart = 50;
+ static constexpr uint8_t maxSessionNameLength = 36;
+
+ struct Settings {
+ uint8_t version = sleeptrackingSettingsFormatVersion;
+ uint8_t currentSession = 0;
+ bool isTracking = false;
+
+ struct Alarm {
+ uint8_t hours = 7;
+ uint8_t minutes = 0;
+ } alarm;
+ };
+
+ struct SleepDataPoint {
+ int16_t xDiffSum = 0;
+ int16_t yDiffSum = 0;
+ int16_t zDiffSum = 0;
+ uint8_t heartRate = 0;
+ };
+
+ public:
+ SleepTrackingController(FS& filesystem,
+ DateTime& datetimeController,
+ Pinetime::Drivers::Bma421& motionSensor,
+ HeartRateController& heartRateController,
+ MotorController& motorController);
+
+ void Init(System::SystemTask* systemTask);
+
+ void StartTracking();
+ void StopTracking();
+ void OnWakeAlarmTrigger();
+
+ void OnMotionTrackingTimerTrigger();
+ void OnHeartRateTrackingTimerTrigger();
+ void OnStoreDataTimerTrigger();
+ void OnGentleWakeupTimerTrigger();
+
+ void ScheduleWakeAlarm();
+ bool IsAlerting() const;
+ bool IsTracking() const;
+ void SaveDatapoint();
+
+ Settings GetSettings();
+ void SetSettings(const Settings& newSettings);
+ void SaveSettings();
+
+ private:
+ void DismissWakeAlarm();
+ void LoadSettings();
+ void ClearTrackingFile();
+
+ // Dependencies
+ Settings settings;
+ System::SystemTask* systemTask;
+ FS& filesystem;
+ DateTime& datetimeController;
+
+ // Sleep tracking
+ Pinetime::Drivers::Bma421& motionSensor;
+ HeartRateController& heartRateController;
+ TimerHandle_t motionTrackingTimer;
+ TimerHandle_t heartRateTrackingTimer;
+ TimerHandle_t storeDataTimer;
+ SleepDataPoint currentDataPoint;
+ Drivers::Bma421::Values previousValues;
+ bool hasPreviousValues;
+
+ // Wakeup
+ TimerHandle_t wakeupAlarmTimer;
+ TimerHandle_t gentleWakeupTimer;
+ MotorController& motorController;
+ uint16_t vibrationDurationMillis;
+ bool isAlerting;
+ };
+}
diff --git a/src/displayapp/Controllers.h b/src/displayapp/Controllers.h
index 9992426c5d..6aeb6ffbf3 100644
--- a/src/displayapp/Controllers.h
+++ b/src/displayapp/Controllers.h
@@ -20,6 +20,7 @@ namespace Pinetime {
class MotionController;
class AlarmController;
class BrightnessController;
+ class SleepTrackingController;
class SimpleWeatherService;
class FS;
class Timer;
@@ -43,6 +44,7 @@ namespace Pinetime {
Pinetime::Controllers::MotionController& motionController;
Pinetime::Controllers::AlarmController& alarmController;
Pinetime::Controllers::BrightnessController& brightnessController;
+ Pinetime::Controllers::SleepTrackingController& sleeptrackingController;
Pinetime::Controllers::SimpleWeatherService* weatherController;
Pinetime::Controllers::FS& filesystem;
Pinetime::Controllers::Timer& timer;
diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp
index bfd7dbed6d..c17113eec0 100644
--- a/src/displayapp/DisplayApp.cpp
+++ b/src/displayapp/DisplayApp.cpp
@@ -1,9 +1,11 @@
#include "displayapp/DisplayApp.h"
+#include
#include
#include "displayapp/screens/HeartRate.h"
#include "displayapp/screens/Motion.h"
#include "displayapp/screens/Timer.h"
#include "displayapp/screens/Alarm.h"
+#include "displayapp/screens/Sleep.h"
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/datetime/DateTimeController.h"
@@ -83,6 +85,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::BrightnessController& brightnessController,
+ Pinetime::Controllers::SleepTrackingController& sleeptrackingController,
Pinetime::Controllers::TouchHandler& touchHandler,
Pinetime::Controllers::FS& filesystem,
Pinetime::Drivers::SpiNorFlash& spiNorFlash)
@@ -99,6 +102,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
motionController {motionController},
alarmController {alarmController},
brightnessController {brightnessController},
+ sleeptrackingController {sleeptrackingController},
touchHandler {touchHandler},
filesystem {filesystem},
spiNorFlash {spiNorFlash},
@@ -114,6 +118,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
motionController,
alarmController,
brightnessController,
+ sleeptrackingController,
nullptr,
filesystem,
timer,
@@ -384,6 +389,18 @@ void DisplayApp::Refresh() {
LoadNewScreen(Apps::Alarm, DisplayApp::FullRefreshDirections::None);
}
break;
+ case Messages::WakeAlarmTriggered:
+ if (currentApp == Apps::Sleep) {
+ auto* sleep = static_cast(currentScreen.get());
+ sleep->StartAlerting();
+ } else {
+ LoadNewScreen(Apps::Sleep, DisplayApp::FullRefreshDirections::None);
+ }
+ break;
+ case Messages::SleepSaveDataPoint:
+ sleeptrackingController.SaveDatapoint();
+ PushMessage(Messages::GoToSleep);
+ break;
case Messages::ShowPairingKey:
LoadNewScreen(Apps::PassKey, DisplayApp::FullRefreshDirections::Up);
motorController.RunForDuration(35);
diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h
index dabed99ea7..0e4b1317fc 100644
--- a/src/displayapp/DisplayApp.h
+++ b/src/displayapp/DisplayApp.h
@@ -65,6 +65,7 @@ namespace Pinetime {
Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::BrightnessController& brightnessController,
+ Pinetime::Controllers::SleepTrackingController& sleeptrackingController,
Pinetime::Controllers::TouchHandler& touchHandler,
Pinetime::Controllers::FS& filesystem,
Pinetime::Drivers::SpiNorFlash& spiNorFlash);
@@ -95,6 +96,7 @@ namespace Pinetime {
Pinetime::Controllers::MotionController& motionController;
Pinetime::Controllers::AlarmController& alarmController;
Pinetime::Controllers::BrightnessController& brightnessController;
+ Pinetime::Controllers::SleepTrackingController& sleeptrackingController;
Pinetime::Controllers::TouchHandler& touchHandler;
Pinetime::Controllers::FS& filesystem;
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
diff --git a/src/displayapp/DisplayAppRecovery.cpp b/src/displayapp/DisplayAppRecovery.cpp
index bcb8db0e9d..03d0beca29 100644
--- a/src/displayapp/DisplayAppRecovery.cpp
+++ b/src/displayapp/DisplayAppRecovery.cpp
@@ -23,6 +23,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Pinetime::Controllers::MotionController& /*motionController*/,
Pinetime::Controllers::AlarmController& /*alarmController*/,
Pinetime::Controllers::BrightnessController& /*brightnessController*/,
+ Pinetime::Controllers::SleepTrackingController& /*sleeptrackingController*/,
Pinetime::Controllers::TouchHandler& /*touchHandler*/,
Pinetime::Controllers::FS& /*filesystem*/,
Pinetime::Drivers::SpiNorFlash& /*spiNorFlash*/)
diff --git a/src/displayapp/DisplayAppRecovery.h b/src/displayapp/DisplayAppRecovery.h
index 162ff2575e..f5c49505b9 100644
--- a/src/displayapp/DisplayAppRecovery.h
+++ b/src/displayapp/DisplayAppRecovery.h
@@ -33,6 +33,7 @@ namespace Pinetime {
class MotorController;
class AlarmController;
class BrightnessController;
+ class SleepTrackingController;
class FS;
class SimpleWeatherService;
class MusicService;
@@ -59,6 +60,7 @@ namespace Pinetime {
Pinetime::Controllers::MotionController& motionController,
Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::BrightnessController& brightnessController,
+ Pinetime::Controllers::SleepTrackingController& sleeptrackingController,
Pinetime::Controllers::TouchHandler& touchHandler,
Pinetime::Controllers::FS& filesystem,
Pinetime::Drivers::SpiNorFlash& spiNorFlash);
diff --git a/src/displayapp/Messages.h b/src/displayapp/Messages.h
index 1fcd72d278..d3206ca556 100644
--- a/src/displayapp/Messages.h
+++ b/src/displayapp/Messages.h
@@ -22,6 +22,8 @@ namespace Pinetime {
NotifyDeviceActivity,
ShowPairingKey,
AlarmTriggered,
+ WakeAlarmTriggered,
+ SleepSaveDataPoint,
Chime,
BleRadioEnableToggle,
};
diff --git a/src/displayapp/apps/Apps.h.in b/src/displayapp/apps/Apps.h.in
index f6feeb7b6d..57b3f1dc0c 100644
--- a/src/displayapp/apps/Apps.h.in
+++ b/src/displayapp/apps/Apps.h.in
@@ -14,6 +14,7 @@ namespace Pinetime {
NotificationsPreview,
Notifications,
Timer,
+ Sleep,
Alarm,
FlashLight,
BatteryInfo,
diff --git a/src/displayapp/apps/CMakeLists.txt b/src/displayapp/apps/CMakeLists.txt
index 93196ed6a0..477acdbf65 100644
--- a/src/displayapp/apps/CMakeLists.txt
+++ b/src/displayapp/apps/CMakeLists.txt
@@ -15,6 +15,7 @@ else ()
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Calculator")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather")
+ set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Sleep")
#set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Motion")
set(USERAPP_TYPES "${DEFAULT_USER_APP_TYPES}" CACHE STRING "List of user apps to build into the firmware")
endif ()
diff --git a/src/displayapp/fonts/fonts.json b/src/displayapp/fonts/fonts.json
index fea3160572..f742fbc8a5 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, 0xf0f3, 0xf522, 0xf743, 0xf1ec, 0xf55a"
+ "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, 0xf0f3, 0xf522, 0xf743, 0xf1ec, 0xf55a, 0xf236"
}
],
"bpp": 1,
diff --git a/src/displayapp/screens/Sleep.cpp b/src/displayapp/screens/Sleep.cpp
new file mode 100644
index 0000000000..ab4f4e8004
--- /dev/null
+++ b/src/displayapp/screens/Sleep.cpp
@@ -0,0 +1,237 @@
+/* Copyright (C) 2025 Asger Gitz-Johansen
+
+ 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 .
+*/
+#include "Sleep.h"
+
+#include
+#include
+
+namespace {
+ void OnValueChangedHandler(void* userData) {
+ static_cast(userData)->OnAlarmValueChanged();
+ }
+
+ void OnButtonPress(lv_obj_t* obj, lv_event_t event) {
+ static_cast(obj->user_data)->OnButtonEvent(obj, event);
+ }
+
+ void StopAlarmTaskCallback(lv_task_t* task) {
+ static_cast(task->user_data)->StopAlerting();
+ }
+}
+
+namespace Pinetime::Applications::Screens {
+ Sleep::Sleep(Controllers::SleepTrackingController& sleeptrackingController,
+ Controllers::Settings::ClockType clockType,
+ Controllers::MotorController& motorController,
+ System::SystemTask& systemTask,
+ DisplayApp& displayApp)
+ : sleeptrackingController {sleeptrackingController},
+ motorController {motorController},
+ systemTask {systemTask},
+ displayApp {displayApp},
+ wakeLock {systemTask},
+ state {},
+ hourCounter {0, 23, jetbrains_mono_76},
+ minuteCounter {0, 59, jetbrains_mono_76},
+ lblampm {nullptr},
+ startButton {nullptr},
+ startText {nullptr},
+ stopButton {nullptr},
+ stopText {nullptr},
+ dismissButton {nullptr},
+ dismissText {nullptr},
+ colonLabel {nullptr},
+ wakeUpLabel {nullptr},
+ sleepingLabel {nullptr},
+ taskStopAlarm {nullptr} {
+ auto settings = sleeptrackingController.GetSettings();
+ hourCounter.Create();
+ lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
+ if (clockType == Controllers::Settings::ClockType::H12) {
+ hourCounter.EnableTwelveHourMode();
+
+ lblampm = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_font(lblampm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
+ lv_label_set_text_static(lblampm, "AM");
+ lv_label_set_align(lblampm, LV_LABEL_ALIGN_CENTER);
+ lv_obj_align(lblampm, lv_scr_act(), LV_ALIGN_CENTER, 0, 30);
+ }
+ hourCounter.SetValue(settings.alarm.hours);
+ hourCounter.SetValueChangedEventCallback(this, OnValueChangedHandler);
+
+ minuteCounter.Create();
+ lv_obj_align(minuteCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
+ minuteCounter.SetValue(settings.alarm.minutes);
+ minuteCounter.SetValueChangedEventCallback(this, OnValueChangedHandler);
+
+ lv_obj_t* colonLabel = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_font(colonLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
+ lv_label_set_text_static(colonLabel, ":");
+ lv_obj_align(colonLabel, lv_scr_act(), LV_ALIGN_CENTER, 0, -29);
+
+ stopButton = lv_btn_create(lv_scr_act(), nullptr);
+ stopButton->user_data = this;
+ lv_obj_set_event_cb(stopButton, OnButtonPress);
+ lv_obj_set_size(stopButton, 240, 50);
+ lv_obj_align(stopButton, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+ lv_obj_set_style_local_bg_color(stopButton, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
+ stopText = lv_label_create(stopButton, nullptr);
+ lv_label_set_text_static(stopText, Symbols::pause);
+ lv_obj_set_hidden(stopButton, true);
+
+ dismissButton = lv_btn_create(lv_scr_act(), nullptr);
+ dismissButton->user_data = this;
+ lv_obj_set_event_cb(dismissButton, OnButtonPress);
+ lv_obj_set_size(dismissButton, 240, 50);
+ lv_obj_align(dismissButton, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+ lv_obj_set_style_local_bg_color(dismissButton, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
+ dismissText = lv_label_create(dismissButton, nullptr);
+ lv_label_set_text_static(dismissText, Symbols::stop);
+ lv_obj_set_hidden(dismissButton, true);
+
+ startButton = lv_btn_create(lv_scr_act(), nullptr);
+ startButton->user_data = this;
+ lv_obj_set_event_cb(startButton, OnButtonPress);
+ lv_obj_set_size(startButton, 240, 50);
+ lv_obj_align(startButton, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+ lv_obj_set_style_local_bg_color(startButton, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
+ startText = lv_label_create(startButton, nullptr);
+ lv_label_set_text_static(startText, Symbols::bed);
+ lv_obj_set_hidden(startButton, false);
+
+ UpdateWakeAlarmTime();
+
+ if (sleeptrackingController.IsAlerting()) {
+ StartAlerting();
+ } else if (sleeptrackingController.IsTracking()) {
+ SetState(State::Tracking);
+ } else {
+ SetState(State::NotTracking);
+ }
+ }
+
+ Sleep::~Sleep() {
+ if (sleeptrackingController.IsAlerting()) {
+ StopAlerting();
+ }
+ lv_obj_clean(lv_scr_act());
+ sleeptrackingController.SaveSettings();
+ }
+
+ void Sleep::StartAlerting() {
+ SetState(State::Alerting);
+ sleeptrackingController.OnGentleWakeupTimerTrigger();
+ }
+
+ void Sleep::OnButtonEvent(lv_obj_t* obj, lv_event_t event) {
+ if (event != LV_EVENT_CLICKED) {
+ return;
+ }
+ if (obj == stopButton) {
+ sleeptrackingController.StopTracking();
+ SetState(State::NotTracking);
+ return;
+ }
+ if (obj == dismissButton) {
+ sleeptrackingController.StopTracking();
+ SetState(State::NotTracking);
+ return;
+ }
+ if (obj == startButton) {
+ sleeptrackingController.StartTracking();
+ SetState(State::Tracking);
+ return;
+ }
+ }
+
+ void Sleep::StopAlerting() {
+ sleeptrackingController.StopTracking();
+ SetState(State::NotTracking);
+ }
+
+ void Sleep::OnAlarmValueChanged() {
+ UpdateWakeAlarmTime();
+ }
+
+ void Sleep::UpdateWakeAlarmTime() {
+ if (lblampm != nullptr) {
+ if (hourCounter.GetValue() >= 12) {
+ lv_label_set_text_static(lblampm, "PM");
+ } else {
+ lv_label_set_text_static(lblampm, "AM");
+ }
+ }
+ auto settings = sleeptrackingController.GetSettings();
+ settings.alarm.hours = hourCounter.GetValue();
+ settings.alarm.minutes = minuteCounter.GetValue();
+ sleeptrackingController.SetSettings(settings);
+ }
+
+ bool Sleep::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
+ // Don't allow closing the screen by swiping while the wake alarm is alerting
+ return sleeptrackingController.IsAlerting() && event == TouchEvents::SwipeDown;
+ }
+
+ void Sleep::SetState(const State& uistate) {
+ state = uistate;
+ switch (uistate) {
+ case State::NotTracking:
+ wakeLock.Release();
+ lv_obj_set_hidden(stopButton, true);
+ lv_obj_set_hidden(dismissButton, true);
+ hourCounter.ShowControls();
+ minuteCounter.ShowControls();
+ lv_obj_set_hidden(startButton, false);
+ if (taskStopAlarm != nullptr) {
+ lv_task_del(taskStopAlarm);
+ taskStopAlarm = nullptr;
+ }
+ break;
+ case State::Tracking:
+ wakeLock.Release();
+ lv_obj_set_hidden(startButton, true);
+ lv_obj_set_hidden(dismissButton, true);
+ hourCounter.HideControls();
+ minuteCounter.HideControls();
+ lv_obj_set_hidden(stopButton, false);
+ break;
+ case State::Alerting:
+ wakeLock.Lock();
+ lv_obj_set_hidden(startButton, true);
+ lv_obj_set_hidden(stopButton, true);
+ hourCounter.HideControls();
+ minuteCounter.HideControls();
+ lv_obj_set_hidden(dismissButton, false);
+ if (taskStopAlarm != nullptr) {
+ lv_task_del(taskStopAlarm);
+ taskStopAlarm = nullptr;
+ }
+ taskStopAlarm = lv_task_create(StopAlarmTaskCallback, pdMS_TO_TICKS(10 * 60 * 1000), LV_TASK_PRIO_MID, this);
+ break;
+ }
+ }
+
+ bool Sleep::OnButtonPushed() {
+ if (sleeptrackingController.IsAlerting()) {
+ sleeptrackingController.StopTracking();
+ SetState(State::NotTracking);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/displayapp/screens/Sleep.h b/src/displayapp/screens/Sleep.h
new file mode 100644
index 0000000000..a5eef332ce
--- /dev/null
+++ b/src/displayapp/screens/Sleep.h
@@ -0,0 +1,93 @@
+/* Copyright (C) 2025 Asger Gitz-Johansen
+
+ 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 .
+*/
+#pragma once
+
+#include "Apps.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Pinetime::Applications::Screens {
+ class Sleep : public Screen {
+ public:
+ enum class State : uint8_t { NotTracking, Tracking, Alerting };
+
+ Sleep(Controllers::SleepTrackingController& sleeptrackingController,
+ Controllers::Settings::ClockType clockType,
+ Controllers::MotorController& motorController,
+ System::SystemTask& systemTask,
+ DisplayApp& displayApp);
+ ~Sleep() override;
+
+ void StartAlerting();
+
+ void SetState(const State& uistate);
+
+ void OnButtonEvent(lv_obj_t* obj, lv_event_t event);
+ void OnAlarmValueChanged();
+
+ bool OnTouchEvent(TouchEvents event) override;
+ bool OnButtonPushed() override;
+ void StopAlerting();
+
+ private:
+ void UpdateWakeAlarmTime();
+
+ // Dependencies
+ Controllers::SleepTrackingController& sleeptrackingController;
+ Controllers::MotorController& motorController;
+ System::SystemTask& systemTask;
+ DisplayApp& displayApp;
+ System::WakeLock wakeLock;
+
+ // UI
+ State state;
+ Widgets::Counter hourCounter;
+ Widgets::Counter minuteCounter;
+ lv_obj_t* lblampm;
+ lv_obj_t *startButton, *startText;
+ lv_obj_t *stopButton, *stopText;
+ lv_obj_t *dismissButton, *dismissText;
+ lv_obj_t *colonLabel, *wakeUpLabel, *sleepingLabel;
+ lv_task_t* taskStopAlarm;
+ };
+}
+
+namespace Pinetime::Applications {
+ template <>
+ struct AppTraits {
+ static constexpr Apps app = Apps::Sleep;
+ static constexpr const char* icon = Screens::Symbols::bed;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Sleep(controllers.sleeptrackingController,
+ controllers.settingsController.GetClockType(),
+ controllers.motorController,
+ *controllers.systemTask,
+ *controllers.displayApp);
+ }
+
+ static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
+ return true;
+ };
+ };
+}
diff --git a/src/displayapp/screens/Symbols.h b/src/displayapp/screens/Symbols.h
index 40699b3d65..0395d63af2 100644
--- a/src/displayapp/screens/Symbols.h
+++ b/src/displayapp/screens/Symbols.h
@@ -41,6 +41,7 @@ namespace Pinetime {
static constexpr const char* sleep = "\xEE\xBD\x84";
static constexpr const char* calculator = "\xEF\x87\xAC";
static constexpr const char* backspace = "\xEF\x95\x9A";
+ static constexpr const char* bed = "\xEF\x88\xB6";
// fontawesome_weathericons.c
// static constexpr const char* sun = "\xEF\x86\x85";
diff --git a/src/main.cpp b/src/main.cpp
index 24f13caddd..7fa2a9c06c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -32,6 +32,7 @@
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/brightness/BrightnessController.h"
+#include "components/sleeptracking/SleepTrackingController.h"
#include "components/motor/MotorController.h"
#include "components/datetime/DateTimeController.h"
#include "components/heartrate/HeartRateController.h"
@@ -108,6 +109,11 @@ Pinetime::Controllers::AlarmController alarmController {dateTimeController, fs};
Pinetime::Controllers::TouchHandler touchHandler;
Pinetime::Controllers::ButtonHandler buttonHandler;
Pinetime::Controllers::BrightnessController brightnessController {};
+Pinetime::Controllers::SleepTrackingController sleeptrackingController {fs,
+ dateTimeController,
+ motionSensor,
+ heartRateController,
+ motorController};
Pinetime::Applications::DisplayApp displayApp(lcd,
touchPanel,
@@ -122,6 +128,7 @@ Pinetime::Applications::DisplayApp displayApp(lcd,
motionController,
alarmController,
brightnessController,
+ sleeptrackingController,
touchHandler,
fs,
spiNorFlash);
@@ -134,6 +141,7 @@ Pinetime::System::SystemTask systemTask(spi,
bleController,
dateTimeController,
alarmController,
+ sleeptrackingController,
watchdog,
notificationManager,
heartRateSensor,
diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h
index fee94bb747..d3cddd2bfa 100644
--- a/src/systemtask/Messages.h
+++ b/src/systemtask/Messages.h
@@ -24,7 +24,9 @@ namespace Pinetime {
OnNewHalfHour,
OnChargingEvent,
OnPairing,
+ OnSleepTrackingDataPoint,
SetOffAlarm,
+ SetOffWakeAlarm,
MeasureBatteryTimerExpired,
BatteryPercentageUpdated,
StartFileTransfer,
diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp
index 8e0435e372..97ff079793 100644
--- a/src/systemtask/SystemTask.cpp
+++ b/src/systemtask/SystemTask.cpp
@@ -40,6 +40,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi,
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Controllers::AlarmController& alarmController,
+ Controllers::SleepTrackingController& sleeptrackingController,
Drivers::Watchdog& watchdog,
Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Drivers::Hrs3300& heartRateSensor,
@@ -60,6 +61,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi,
bleController {bleController},
dateTimeController {dateTimeController},
alarmController {alarmController},
+ sleeptrackingController {sleeptrackingController},
watchdog {watchdog},
notificationManager {notificationManager},
heartRateSensor {heartRateSensor},
@@ -128,6 +130,7 @@ void SystemTask::Work() {
batteryController.Register(this);
motionSensor.SoftReset();
alarmController.Init(this);
+ sleeptrackingController.Init(this);
// Reset the TWI device because the motion sensor chip most probably crashed it...
twiMaster.Sleep();
@@ -218,6 +221,14 @@ void SystemTask::Work() {
GoToRunning();
displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered);
break;
+ case Messages::SetOffWakeAlarm:
+ GoToRunning();
+ displayApp.PushMessage(Pinetime::Applications::Display::Messages::WakeAlarmTriggered);
+ break;
+ case Messages::OnSleepTrackingDataPoint:
+ GoToRunning();
+ displayApp.PushMessage(Pinetime::Applications::Display::Messages::SleepSaveDataPoint);
+ break;
case Messages::BleConnected:
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity);
isBleDiscoveryTimerRunning = true;
diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h
index 0060e36096..5f69204947 100644
--- a/src/systemtask/SystemTask.h
+++ b/src/systemtask/SystemTask.h
@@ -16,6 +16,7 @@
#include "components/ble/NimbleController.h"
#include "components/ble/NotificationManager.h"
#include "components/alarm/AlarmController.h"
+#include "components/sleeptracking/SleepTrackingController.h"
#include "components/fs/FS.h"
#include "touchhandler/TouchHandler.h"
#include "buttonhandler/ButtonHandler.h"
@@ -61,6 +62,7 @@ namespace Pinetime {
Controllers::Ble& bleController,
Controllers::DateTime& dateTimeController,
Controllers::AlarmController& alarmController,
+ Controllers::SleepTrackingController& sleeptrackingController,
Drivers::Watchdog& watchdog,
Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Drivers::Hrs3300& heartRateSensor,
@@ -101,6 +103,7 @@ namespace Pinetime {
Pinetime::Controllers::Ble& bleController;
Pinetime::Controllers::DateTime& dateTimeController;
Pinetime::Controllers::AlarmController& alarmController;
+ Pinetime::Controllers::SleepTrackingController& sleeptrackingController;
QueueHandle_t systemTasksMsgQueue;
Pinetime::Drivers::Watchdog& watchdog;
Pinetime::Controllers::NotificationManager& notificationManager;