Assume user means to set an alarm for tomorrow, not later today, when returning to the setter from a disarmed alarm

This commit is contained in:
2026-08-21 18:31:21 -04:00
parent f4812ab775
commit 3031d7509d
2 changed files with 34 additions and 28 deletions
+10 -8
View File
@@ -1,23 +1,25 @@
# Trigalarm
A habit-correcting alarm for heavy sleepers. Cannot be shut off until you solve for theta in a right triangle.
A habit-correcting alarm for heavy sleepers that forces you to solve for theta in right triangles. It's the perfect amount of thought to activate your brain in the morning!
## The Pitch
### Simple
The app only has one page. You can only set one alarm. There is no snooze, there is no weekly schedule, and there is no way to disable the alarm (you'll always have one set - every day - like it or not).
The alarm setter is only one page. You can only set one alarm. There is no snooze, there is no weekly schedule, and there is no way to disable the alarm (you'll always have one set - every day - like it or not).
### Persistent
The alarm cannot be stopped until you solve for theta in a randomized right triangle. Even if you force-close the app, it will re-open itself until you complete the equation. After you successfully disarm the alarm, it will shortly open again with only a small vibration to verify you are still awake by having you solve another triangle in a short time limit. Fail to notice and solve it in time? Vibrations will resume and another alarm will be scheduled after this one is solved. The alarm is not fully disarmed until the second, "awareness" alarm is solved before the countdown expires. Integrate with [Pavlok](https://pavlok.com/pages/how-it-works) to introduce mild electrocution into the mix!
The alarm goes off at least twice. The first time, you must solve a triangle to silence the alarm. The second time, you must do it again, but within a strict time limit (and no constant vibration). Fail to solve it within the time limit? The alarm is scheduled for a third time. This process continues until you solve within the time limit - this is to prove you managed to stay awake between alarms before allowing it to be fully disarmed. Even if you force-quit Trigalarm, it will shortly re-open to continue nagging you.
### Adaptable
Trigalarm is meant to be adapted to your needs, and you should adjust its settings as you progress on your sleep journey. For example, as your trig skills improve, you may want to lower the time limit for the disarming alarm. You can also adjust the gap between alarms to account for how quickly you find yourself getting out of bed.
## Why Trigonometry?
Because it requires the perfect amount of thought to activate your brain in the morning. If the app gave you a more typical math problem, you could just mindlessly punch it into a calculator. Trigonometry still requires a base amount of thought ("Do I need sine, cosine, or tangent?") before a calculator is of any use.
Perhaps the most unique, Trigalarm features optional [Pavlok](https://pavlok.com/pages/how-it-works) API integration to punish you via minor electrocution if you fall back asleep, force-quit the app, or attempt to overwrite your active alarm cycle.
## Tips & Tricks
- When you successfully disarm the alarm, you are prompted with the options to either exit and keep the same alarm time for tomorrow, or to select a new time. If choosing the latter, whatever time you select will assume you mean tomorrow. This avoids situations where you wake up at 6 on a Friday, set an alarm intended for 9 on Saturday, and end up with your wrist buzzing in three hours!
## Some Neat Stuff
- The vibration pattern is randomized. It derives the length of the three pulses from the angles in the generated triangle. Neat!
- Some of the alarm scheduling code was taken from [Gentle Wake](https://github.com/SeaPea/GentleWake). It seems trusted by the community, so... I stole it! The beauty of open source, no AI needed!
- A custom font was made to satisfy the need for something that looks like LECO but is monospaced. The version I made contains only numbers, period, and colon, but if you want to build off of it, [it's public](https://git.wetfence.xyz/RandyTheSilly/pebble-misc/src/branch/main/fonts) and easy to modify!
- Trigalarm can optionally integrate with [Pavlok](https://pavlok.com/pages/how-it-works) to shock you for failing to respond to, force-quitting, or attempting to overwrite alarms!
## Future Goals (1.1 and beyond)
- Timeline integration
- Speaker support
- Full touch navigation
- Draw measurements based on triangle side locations, rather than in fixed positions
- Speaker support
+24 -20
View File
@@ -56,6 +56,7 @@ static GBitmap *s_check_bmp;
// global vars
static GColor s_setter_highlight_color;
static uint8_t s_highlight_pos = 0;
static bool s_skip_current_day = false;
//// alarm
static WakeupReason s_alarm_reason;
static uint8_t s_solution_indices[3] = {8, 8, 8};
@@ -100,6 +101,7 @@ static void click_handler_disable_tutorial(void *recognizer, void *ctx) {
static void click_handler_push_setter(void *recognizer, void *ctx) {
s_highlight_pos = 0;
s_skip_current_day = true; // setting the next alarm should assume the user meant tomorrow
s_alarm_reason = WAKE_MAIN_ALARM; // going back to setter means alarm was disarmed or failed to set; do not punish user
window_stack_pop_all(true);
push_setter_window();
@@ -230,21 +232,32 @@ static void send_zap(uint8_t intensity) {
////////////////////// MISC //////////////////////
static time_t compute_alarm_time(uint8_t hour, uint8_t minute) {
time_t now = time(NULL);
struct tm target_tm = *localtime(&now);
bool already_passed_today =
(hour < target_tm.tm_hour) ||
(hour == target_tm.tm_hour && minute <= target_tm.tm_min);
target_tm.tm_hour = hour;
target_tm.tm_min = minute;
target_tm.tm_sec = 0;
if (s_skip_current_day || already_passed_today) {
target_tm.tm_mday += 1;
}
return mktime(&target_tm);
}
static bool schedule_alarm(WakeupReason schedule_reason) {
time_t target;
WakeupId status;
switch (schedule_reason) {
case WAKE_MAIN_ALARM: {
time_t now = time(NULL);
struct tm target_tm = *localtime(&now);
target_tm.tm_hour = the_time.HourWIP;
target_tm.tm_min = the_time.MinuteWIP;
target_tm.tm_sec = 0;
target = mktime(&target_tm);
if (target <= now) {
target += SECONDS_PER_DAY;
}
target = compute_alarm_time(the_time.HourWIP, the_time.MinuteWIP);
wakeup_cancel_all();
status = wakeup_schedule_simple_robust(target, SECONDS_PER_MINUTE, schedule_reason);
if (settings.PavlokEnabled) {
@@ -807,18 +820,8 @@ static void push_alarm_window() {
static void setter_seconds_handler(struct tm *tick_time, TimeUnits units_changed) {
struct tm now_tm = *tick_time;
// build the target time today
struct tm target_tm = now_tm;
target_tm.tm_hour = the_time.HourSet;
target_tm.tm_min = the_time.MinuteSet;
target_tm.tm_sec = 0;
time_t now = mktime(&now_tm);
time_t target = mktime(&target_tm);
if (target <= now) {
target += SECONDS_PER_DAY;
}
time_t target = compute_alarm_time(the_time.HourSet, the_time.MinuteSet);
int32_t diff = (int32_t)(target - now);
@@ -948,6 +951,7 @@ static void setter_click_config_provider(void *ctx) {
static void live_wakeup_handler_for_setter(WakeupId id, int32_t reason) {
s_highlight_pos = 0;
s_skip_current_day = false; // reset
window_stack_pop_all(false);
push_alarm_window();
}