mirror of
https://git.wetfence.xyz/RandyTheSilly/Trigalarm.git
synced 2026-10-27 03:09:58 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afa6976f1a | ||
|
|
482f4d21ba | ||
|
|
ce67fc3081 | ||
|
|
98f13a941d | ||
|
|
020442555d |
No files matched your search
@@ -6,28 +6,27 @@
|
|||||||
### Simple
|
### Simple
|
||||||
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).
|
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
|
### Persistent
|
||||||
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.
|
The alarm cannot be disabled until the user solves their configured number of triangles. Each triangle comes with a time limit. Failure to solve a triangle within the time limit will demote the user to the previous triangle.
|
||||||
|
|
||||||
|
Guards are in place to prevent the user from force-quitting/resetting the alarm when an alarm is meant to be active.
|
||||||
### Adaptable
|
### 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.
|
Trigalarm is meant to be adapted to your needs with as little confusion as possible. There are three powerful settings:
|
||||||
|
1. Number of triangles to solve in order to disarm the alarm
|
||||||
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.
|
2. The time limit for the final (fastest) triangle
|
||||||
|
- Time limits for prior triangles are calculated automatically based on this one
|
||||||
## Learn the Lingo
|
3. Pavlok integration (API key + zap intensity)
|
||||||
- Main alarm
|
- Punishes the user via mild electrocution if they...
|
||||||
- The first alarm that goes off; comes with continuous vibration and does not disarm Trigalarm when solved. If Pavlok integration is enabled, this alarm can zap the user after a configurable amount of time if they do not respond. Solving this alarm will schedule an awareness alarm.
|
- ...take too long to respond to the first triangle
|
||||||
- Awareness alarm
|
- ...get demoted from a higher level triangle
|
||||||
- Any subsequent alarm after the main alarm has been solved; comes with only one brief vibration to indicate to the user it has opened. It has a configurable time limit it must be solved within to disarm Trigalarm. If the time limit expires, it will begin vibrations (and optionally Pavlok zaps), and once solved, it will schedule another awareness alarm.
|
- ...force quit the app by holding the back button (the app re-opens and sends a zap)
|
||||||
|
|
||||||
## Tips & Tricks
|
## 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!
|
- 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!
|
||||||
- If you are stuck waiting for an awareness alarm or there is a bug that schedules an alarm incorrectly, there is a "Reset Alarm" button in the phone-side settings that will set your alarm to 00:00.
|
- There is a "Reset Alarm" button in the phone-side settings to help if you make an unsolvable alarm configuration by mistake.
|
||||||
|
|
||||||
## Some Neat Stuff
|
## Some Neat Stuff
|
||||||
- The vibration pattern is randomized. It derives the length of the three pulses from the angles in the generated triangle. Neat!
|
- 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!
|
- 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!
|
- 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!
|
||||||
|
|
||||||
## Future Goals (1.1 and beyond)
|
## Future Goals (2.1 and beyond)
|
||||||
- Full touch navigation
|
|
||||||
- Draw measurements based on triangle side locations, rather than in fixed positions
|
|
||||||
- Custom, self-hosted Pavlok API support (going to try some Bluetooth reverse engineering)
|
- Custom, self-hosted Pavlok API support (going to try some Bluetooth reverse engineering)
|
||||||
+6
-10
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "trigalarm",
|
"name": "trigalarm",
|
||||||
"author": "RandyTheSilly",
|
"author": "RandyTheSilly",
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"alarm",
|
"alarm",
|
||||||
"math",
|
"math",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
],
|
],
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rebble/clay": "^1.0.10"
|
"@rebble/clay": "^1.1.0"
|
||||||
},
|
},
|
||||||
"pebble": {
|
"pebble": {
|
||||||
"displayName": "Trigalarm",
|
"displayName": "Trigalarm",
|
||||||
@@ -34,15 +34,11 @@
|
|||||||
"messageKeys": [
|
"messageKeys": [
|
||||||
"PKJS_READY",
|
"PKJS_READY",
|
||||||
"PKJS_ALARM_RESET",
|
"PKJS_ALARM_RESET",
|
||||||
|
"PKJS_TRIANGLE_COUNT",
|
||||||
|
"PKJS_TRIANGLE_MIN_SECS",
|
||||||
"PKJS_PAVLOK_ENABLE",
|
"PKJS_PAVLOK_ENABLE",
|
||||||
"PKJS_PAVLOK_ZAP_MIN",
|
"PKJS_PAVLOK_ZAP_INTENSITY",
|
||||||
"PKJS_PAVLOK_ZAP_MAX",
|
"PKJS_PAVLOK_ZAP"
|
||||||
"PKJS_PAVLOK_ZAP_STEP",
|
|
||||||
"PKJS_PAVLOK_ZAP_MAIN_DELAY",
|
|
||||||
"PKJS_PAVLOK_ZAP_INTERVAL",
|
|
||||||
"PKJS_PAVLOK_ZAP",
|
|
||||||
"PKJS_AWARENESS_TIME_GAP",
|
|
||||||
"PKJS_AWARENESS_TIME_LIMIT"
|
|
||||||
],
|
],
|
||||||
"resources": {
|
"resources": {
|
||||||
"media": [
|
"media": [
|
||||||
|
|||||||
+174
-267
@@ -54,19 +54,16 @@ static GBitmap *s_clock_bmp;
|
|||||||
static GBitmap *s_check_bmp;
|
static GBitmap *s_check_bmp;
|
||||||
|
|
||||||
// global vars
|
// global vars
|
||||||
|
static WakeupReason s_alarm_reason;
|
||||||
static GColor s_setter_highlight_color;
|
static GColor s_setter_highlight_color;
|
||||||
static uint8_t s_highlight_pos = 0;
|
static uint8_t s_highlight_pos = 0;
|
||||||
static bool s_skip_current_day = false;
|
static bool s_skip_current_day = false;
|
||||||
//// alarm
|
//// alarm
|
||||||
static WakeupReason s_alarm_reason;
|
static uint8_t s_current_triangle_num;
|
||||||
static uint8_t s_solution_indices[3];
|
static uint8_t s_solution_indices[3];
|
||||||
static uint8_t s_selected_indices[3] = {9, 9, 9};
|
static uint8_t s_selected_indices[3] = {9, 9, 9};
|
||||||
static uint8_t s_main_countdown_till_zap;
|
|
||||||
static uint8_t s_awareness_countdown_int = 0;
|
static uint8_t s_awareness_countdown_int = 0;
|
||||||
static char s_awareness_countdown_buf[4];
|
static char s_awareness_countdown_buf[4];
|
||||||
static uint8_t s_alarm_zap_repeat_countdown = 0;
|
|
||||||
static uint8_t s_awareness_zap_intensity = 0;
|
|
||||||
static bool s_hide_countdown = true;
|
|
||||||
static uint32_t s_vibe_segments[5];
|
static uint32_t s_vibe_segments[5];
|
||||||
static VibePattern s_vibe_pattern;
|
static VibePattern s_vibe_pattern;
|
||||||
|
|
||||||
@@ -94,16 +91,9 @@ static char *s_options_under[2];
|
|||||||
|
|
||||||
////////////////////// STATUS //////////////////////
|
////////////////////// STATUS //////////////////////
|
||||||
|
|
||||||
static void click_handler_disable_tutorial(void *recognizer, void *ctx) {
|
|
||||||
settings.TutorialEnabled = false;
|
|
||||||
persist_write_data(STORAGE_KEY_SETTINGS, &settings, sizeof(settings));
|
|
||||||
window_stack_pop_all(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void click_handler_push_setter(void *recognizer, void *ctx) {
|
static void click_handler_push_setter(void *recognizer, void *ctx) {
|
||||||
s_highlight_pos = 0;
|
s_highlight_pos = 0;
|
||||||
s_skip_current_day = true; // setting the next alarm should assume the user meant tomorrow
|
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);
|
window_stack_pop_all(true);
|
||||||
push_setter_window();
|
push_setter_window();
|
||||||
}
|
}
|
||||||
@@ -116,11 +106,6 @@ static void click_handler_retry(void *recognizer, void *ctx) {
|
|||||||
window_stack_pop(true);
|
window_stack_pop(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void status_main_tutorial_click_config_provider(void *ctx) {
|
|
||||||
window_single_click_subscribe(BUTTON_ID_SELECT, click_handler_exit);
|
|
||||||
window_single_click_subscribe(BUTTON_ID_BACK, click_handler_disable_tutorial);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void status_confirmation_click_config_provider(void *ctx) {
|
static void status_confirmation_click_config_provider(void *ctx) {
|
||||||
window_single_click_subscribe(BUTTON_ID_BACK, click_handler_push_setter);
|
window_single_click_subscribe(BUTTON_ID_BACK, click_handler_push_setter);
|
||||||
window_single_click_subscribe(BUTTON_ID_SELECT, click_handler_exit);
|
window_single_click_subscribe(BUTTON_ID_SELECT, click_handler_exit);
|
||||||
@@ -143,11 +128,6 @@ static void status_window_load(StatusWindowPurpose purpose) {
|
|||||||
text_layer_set_font(s_status_txt_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24));
|
text_layer_set_font(s_status_txt_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24));
|
||||||
|
|
||||||
switch (purpose) {
|
switch (purpose) {
|
||||||
case STATUS_MAIN_TUTORIAL:
|
|
||||||
// don't disable ticks to ensure vibrations remain till the tutorial is closed
|
|
||||||
text_layer_set_text(s_status_txt_layer, "Correct! An awareness alarm is now being set. Solve it within the time limit to disarm Trigalarm.\n\nPress SELECT to exit or BACK to disable this tutorial in the future (and exit).");
|
|
||||||
window_set_click_config_provider(s_status_window, status_main_tutorial_click_config_provider);
|
|
||||||
break;
|
|
||||||
case STATUS_CONFIRMATION: {
|
case STATUS_CONFIRMATION: {
|
||||||
static char s_status_confirmed_buffer[106];
|
static char s_status_confirmed_buffer[106];
|
||||||
snprintf(s_status_confirmed_buffer, sizeof(s_status_confirmed_buffer), "Alarm disarmed and re-set (%02u:%02u) successfully.\n\nPress SELECT to exit or BACK to change the alarm time.", the_time.HourSet, the_time.MinuteSet);
|
snprintf(s_status_confirmed_buffer, sizeof(s_status_confirmed_buffer), "Alarm disarmed and re-set (%02u:%02u) successfully.\n\nPress SELECT to exit or BACK to change the alarm time.", the_time.HourSet, the_time.MinuteSet);
|
||||||
@@ -209,40 +189,26 @@ static bool schedule_alarm(WakeupReason schedule_reason) {
|
|||||||
WakeupId status;
|
WakeupId status;
|
||||||
|
|
||||||
switch (schedule_reason) {
|
switch (schedule_reason) {
|
||||||
case WAKE_MAIN_ALARM: {
|
case WAKE_ALARM: {
|
||||||
target = compute_alarm_time(the_time.HourWIP, the_time.MinuteWIP);
|
target = compute_alarm_time(the_time.HourWIP, the_time.MinuteWIP);
|
||||||
wakeup_cancel_all();
|
wakeup_cancel_all();
|
||||||
status = wakeup_schedule_simple_robust(target, SECONDS_PER_MINUTE, schedule_reason);
|
status = wakeup_schedule_simple_robust(target, SECONDS_PER_MINUTE, schedule_reason);
|
||||||
if (settings.PavlokEnabled) {
|
|
||||||
persist_write_int(STORAGE_KEY_PAVLOK_INTENSITY, settings.PavlokMin);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WAKE_AWARENESS_ALARM:
|
case WAKE_RECOVERY_ALARM:
|
||||||
target = time(NULL) + settings.AwarenessTimeGapMinutes * SECONDS_PER_MINUTE;
|
|
||||||
wakeup_cancel_all();
|
|
||||||
status = wakeup_schedule_simple_robust(target, SECONDS_PER_MINUTE, schedule_reason);
|
|
||||||
if (settings.PavlokEnabled && (s_alarm_reason == WAKE_AWARENESS_ALARM || s_alarm_reason == WAKE_RECOVERY_AWARENESS_ALARM)) {
|
|
||||||
uint8_t next_intensity = s_awareness_zap_intensity + settings.PavlokStep;
|
|
||||||
if (next_intensity <= settings.PavlokMax && next_intensity <= 100) {
|
|
||||||
// only boost next intensity if it is within the allowed bounds
|
|
||||||
persist_write_int(STORAGE_KEY_PAVLOK_INTENSITY, s_awareness_zap_intensity + settings.PavlokStep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WAKE_RECOVERY_MAIN_ALARM:
|
|
||||||
case WAKE_RECOVERY_AWARENESS_ALARM:
|
|
||||||
target = time(NULL) + 15;
|
target = time(NULL) + 15;
|
||||||
// no need to cancel alarms, as recovery alarms short-lived and is expired by the time another is being scheduled
|
// no need to cancel alarms, as recovery alarms are short-lived and are expired by the time another is being scheduled
|
||||||
status = wakeup_schedule_simple_robust(target, 15, schedule_reason);
|
status = wakeup_schedule_simple_robust(target, 15, schedule_reason);
|
||||||
}
|
}
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
push_status_window(STATUS_SET_FAILURE);
|
push_status_window(STATUS_SET_FAILURE);
|
||||||
persist_write_int(STORAGE_KEY_ALARM_REASON, WAKE_MAIN_ALARM);
|
persist_write_int(STORAGE_KEY_ALARM_REASON, WAKE_ALARM);
|
||||||
|
s_alarm_reason = WAKE_ALARM;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (persist_read_int(STORAGE_KEY_ALARM_REASON) != schedule_reason) {
|
if (persist_read_int(STORAGE_KEY_ALARM_REASON) != schedule_reason) {
|
||||||
persist_write_int(STORAGE_KEY_ALARM_REASON, schedule_reason);
|
persist_write_int(STORAGE_KEY_ALARM_REASON, schedule_reason);
|
||||||
|
s_alarm_reason = schedule_reason;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -289,25 +255,15 @@ static void inbox_received_handler(DictionaryIterator *iter, void *context) {
|
|||||||
if (dict_find(iter, MESSAGE_KEY_PKJS_ALARM_RESET)) {
|
if (dict_find(iter, MESSAGE_KEY_PKJS_ALARM_RESET)) {
|
||||||
the_time.HourWIP = 0;
|
the_time.HourWIP = 0;
|
||||||
the_time.MinuteWIP = 0;
|
the_time.MinuteWIP = 0;
|
||||||
s_alarm_reason = WAKE_MAIN_ALARM;
|
s_alarm_reason = WAKE_ALARM;
|
||||||
schedule_alarm(WAKE_MAIN_ALARM);
|
schedule_alarm(WAKE_ALARM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ENABLE, &settings.PavlokEnabled);
|
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ENABLE, &settings.PavlokEnabled);
|
||||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ZAP_INTERVAL, &settings.PavlokIntervalSeconds);
|
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ZAP_INTENSITY, &settings.PavlokIntensity);
|
||||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ZAP_MIN, &settings.PavlokMin);
|
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_TRIANGLE_COUNT, &settings.TriangleCount);
|
||||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ZAP_MAX, &settings.PavlokMax);
|
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_TRIANGLE_MIN_SECS, &settings.TriangeMinSecs);
|
||||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ZAP_STEP, &settings.PavlokStep);
|
|
||||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ZAP_MAIN_DELAY, &settings.PavlokMainDelay);
|
|
||||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_AWARENESS_TIME_GAP, &settings.AwarenessTimeGapMinutes);
|
|
||||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_AWARENESS_TIME_LIMIT, &settings.AwarenessTimeLimitSeconds);
|
|
||||||
if (settings.PavlokMax <= settings.PavlokMin) {
|
|
||||||
// disable step mode if max is not higher than min
|
|
||||||
settings.PavlokStep = 0;
|
|
||||||
// ensure force quit/overwrite punishments aren't weaker than base
|
|
||||||
settings.PavlokMax = settings.PavlokMin;
|
|
||||||
}
|
|
||||||
persist_write_data(STORAGE_KEY_SETTINGS, &settings, sizeof(settings));
|
persist_write_data(STORAGE_KEY_SETTINGS, &settings, sizeof(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,43 +286,13 @@ static void send_zap(uint8_t intensity) {
|
|||||||
|
|
||||||
////////////////////// ALARM //////////////////////
|
////////////////////// ALARM //////////////////////
|
||||||
|
|
||||||
static void main_alarm_seconds_handler(struct tm *tick_time, TimeUnits units_changed) {
|
|
||||||
if (s_main_countdown_till_zap > 0) {
|
|
||||||
s_main_countdown_till_zap--;
|
|
||||||
if (s_main_countdown_till_zap == 0) {
|
|
||||||
send_zap(settings.PavlokMin);
|
|
||||||
s_alarm_zap_repeat_countdown = settings.PavlokIntervalSeconds;
|
|
||||||
}
|
|
||||||
} else if (s_alarm_zap_repeat_countdown > 0) {
|
|
||||||
s_alarm_zap_repeat_countdown--;
|
|
||||||
if (s_alarm_zap_repeat_countdown == 0) {
|
|
||||||
send_zap(settings.PavlokMin);
|
|
||||||
s_alarm_zap_repeat_countdown = settings.PavlokIntervalSeconds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vibes_enqueue_custom_pattern(s_vibe_pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void awareness_alarm_seconds_handler(struct tm *tick_time, TimeUnits units_changed) {
|
static void awareness_alarm_seconds_handler(struct tm *tick_time, TimeUnits units_changed) {
|
||||||
if (s_awareness_countdown_int > 0) {
|
|
||||||
s_awareness_countdown_int--;
|
s_awareness_countdown_int--;
|
||||||
snprintf(s_awareness_countdown_buf, sizeof(s_awareness_countdown_buf), "%03u", s_awareness_countdown_int);
|
snprintf(s_awareness_countdown_buf, sizeof(s_awareness_countdown_buf), "%03u", s_awareness_countdown_int);
|
||||||
text_layer_set_text(s_countdown_txt_layer, s_awareness_countdown_buf);
|
text_layer_set_text(s_countdown_txt_layer, s_awareness_countdown_buf);
|
||||||
if (s_awareness_countdown_int == 0) {
|
if (s_awareness_countdown_int == 0) {
|
||||||
send_zap(s_awareness_zap_intensity);
|
send_zap(settings.PavlokIntensity);
|
||||||
s_alarm_zap_repeat_countdown = settings.PavlokIntervalSeconds;
|
// TODO demote and reset countdown int
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (s_alarm_zap_repeat_countdown > 0) {
|
|
||||||
s_alarm_zap_repeat_countdown--;
|
|
||||||
if (s_alarm_zap_repeat_countdown == 0) {
|
|
||||||
send_zap(s_awareness_zap_intensity);
|
|
||||||
s_alarm_zap_repeat_countdown = settings.PavlokIntervalSeconds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
layer_set_hidden(text_layer_get_layer(s_countdown_txt_layer), s_hide_countdown);
|
|
||||||
s_hide_countdown = !s_hide_countdown;
|
|
||||||
vibes_enqueue_custom_pattern(s_vibe_pattern);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,20 +373,15 @@ static void alarm_select_hold_handler(void *recognizer, void *ctx) {
|
|||||||
wakeup_service_subscribe(NULL);
|
wakeup_service_subscribe(NULL);
|
||||||
|
|
||||||
if (s_awareness_countdown_int > 0) {
|
if (s_awareness_countdown_int > 0) {
|
||||||
// set main alarm if the awareness alarm was solved within time limit
|
// TODO promote OR disarm
|
||||||
|
// if (s_current_triangle == settings.TriangleCount) {
|
||||||
tick_timer_service_unsubscribe(); // alarm is fully disarmed - prevent stimulus in confirmation window
|
tick_timer_service_unsubscribe(); // alarm is fully disarmed - prevent stimulus in confirmation window
|
||||||
if (schedule_alarm(WAKE_MAIN_ALARM)) {
|
if (schedule_alarm(WAKE_ALARM)) {
|
||||||
push_status_window(STATUS_CONFIRMATION);
|
push_status_window(STATUS_CONFIRMATION);
|
||||||
}
|
}
|
||||||
} else {
|
// } else {
|
||||||
// otherwise, set awareness alarm and exit
|
// GENERATE NEW TRIANLGE AND SET TIME LIMIT
|
||||||
if (schedule_alarm(WAKE_AWARENESS_ALARM)) {
|
// }
|
||||||
if (settings.TutorialEnabled && (s_alarm_reason == WAKE_MAIN_ALARM || s_alarm_reason == WAKE_RECOVERY_MAIN_ALARM)) {
|
|
||||||
push_status_window(STATUS_MAIN_TUTORIAL);
|
|
||||||
} else {
|
|
||||||
window_stack_pop_all(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,12 +450,143 @@ static void drop_menu_window_unload(Window *window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void live_wakeup_handler_for_alarm(WakeupId id, int32_t wakeup_reason) {
|
static void live_wakeup_handler_for_alarm(WakeupId id, int32_t wakeup_reason) {
|
||||||
if (wakeup_reason == WAKE_RECOVERY_MAIN_ALARM || wakeup_reason == WAKE_RECOVERY_AWARENESS_ALARM) {
|
if (wakeup_reason == WAKE_RECOVERY_ALARM) {
|
||||||
// if recovery alarm, re-schedule it
|
// if recovery alarm, re-schedule it
|
||||||
schedule_alarm(wakeup_reason);
|
schedule_alarm(wakeup_reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void update_for_new_triangle() {
|
||||||
|
// pick & display 2 side lengths to the user
|
||||||
|
static char side_a_buffer[8], side_b_buffer[8], side_c_buffer[8];
|
||||||
|
uint8_t ignored_side = rand() % 3;
|
||||||
|
uint8_t sin_index = rand() % 2;
|
||||||
|
uint8_t cos_index = rand() % 2;
|
||||||
|
uint8_t tan_index = rand() % 2;
|
||||||
|
switch (ignored_side) {
|
||||||
|
case 0:
|
||||||
|
snprintf(side_b_buffer, sizeof(side_b_buffer), "%u", s_triangle_layer->side_b);
|
||||||
|
snprintf(side_c_buffer, sizeof(side_c_buffer), "%u", s_triangle_layer->side_c);
|
||||||
|
s_options_over[0] = side_b_buffer;
|
||||||
|
s_options_over[1] = side_c_buffer;
|
||||||
|
s_options_under[0] = side_b_buffer;
|
||||||
|
s_options_under[1] = side_c_buffer;
|
||||||
|
|
||||||
|
// store solution
|
||||||
|
s_solution_indices[0] = 2; // tan/cot
|
||||||
|
if (s_triangle_layer->theta_angle_is_left) {
|
||||||
|
if (tan_index == 0) { // tan
|
||||||
|
s_solution_indices[1] = 0; // b (opposite)
|
||||||
|
s_solution_indices[2] = 1; // c (adjacent)
|
||||||
|
} else { // cot
|
||||||
|
s_solution_indices[1] = 1; // c (adjacent)
|
||||||
|
s_solution_indices[2] = 0; // b (opposite)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tan_index == 0) { // tan
|
||||||
|
s_solution_indices[1] = 1; // c (opposite)
|
||||||
|
s_solution_indices[2] = 0; // b (adjacent)
|
||||||
|
} else { // cot
|
||||||
|
s_solution_indices[1] = 0; // b (adjacent)
|
||||||
|
s_solution_indices[2] = 1; // c (opposite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
snprintf(side_a_buffer, sizeof(side_a_buffer), "%u", s_triangle_layer->side_a);
|
||||||
|
snprintf(side_c_buffer, sizeof(side_c_buffer), "%u", s_triangle_layer->side_c);
|
||||||
|
s_options_over[0] = side_a_buffer;
|
||||||
|
s_options_over[1] = side_c_buffer;
|
||||||
|
s_options_under[0] = side_a_buffer;
|
||||||
|
s_options_under[1] = side_c_buffer;
|
||||||
|
|
||||||
|
// store solution
|
||||||
|
if (s_triangle_layer->theta_angle_is_left) {
|
||||||
|
s_solution_indices[0] = 1; // cos/sec
|
||||||
|
if (cos_index == 0) { // cos
|
||||||
|
s_solution_indices[1] = 1; // c (adjacent)
|
||||||
|
s_solution_indices[2] = 0; // a (hypotenuse)
|
||||||
|
} else { // sec
|
||||||
|
s_solution_indices[1] = 0; // a (hypotenuse)
|
||||||
|
s_solution_indices[2] = 1; // c (adjacent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_solution_indices[0] = 0; // sin/csc
|
||||||
|
if (sin_index == 0) { // sin
|
||||||
|
s_solution_indices[1] = 1; // c (opposite)
|
||||||
|
s_solution_indices[2] = 0; // a (hypotenuse)
|
||||||
|
} else { // csc
|
||||||
|
s_solution_indices[1] = 0; // a (hypotenuse)
|
||||||
|
s_solution_indices[2] = 1; // c (opposite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
snprintf(side_a_buffer, sizeof(side_a_buffer), "%u", s_triangle_layer->side_a);
|
||||||
|
snprintf(side_b_buffer, sizeof(side_b_buffer), "%u", s_triangle_layer->side_b);
|
||||||
|
s_options_over[0] = side_a_buffer;
|
||||||
|
s_options_over[1] = side_b_buffer;
|
||||||
|
s_options_under[0] = side_a_buffer;
|
||||||
|
s_options_under[1] = side_b_buffer;
|
||||||
|
|
||||||
|
// store solution
|
||||||
|
if (s_triangle_layer->theta_angle_is_left) {
|
||||||
|
s_solution_indices[0] = 0; // sin/csc
|
||||||
|
if (sin_index == 0) { // sin
|
||||||
|
s_solution_indices[1] = 1; // b (opposite)
|
||||||
|
s_solution_indices[2] = 0; // a (hypotenuse)
|
||||||
|
} else { // csc
|
||||||
|
s_solution_indices[1] = 0; // a (hypotenuse)
|
||||||
|
s_solution_indices[2] = 1; // b (opposite)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_solution_indices[0] = 1;
|
||||||
|
if (cos_index == 0) { // cos
|
||||||
|
s_solution_indices[1] = 1; // b (adjacent)
|
||||||
|
s_solution_indices[2] = 0; // a (hypotenuse)
|
||||||
|
} else { // sec
|
||||||
|
s_solution_indices[1] = 0; // a (hypotenuse)
|
||||||
|
s_solution_indices[2] = 1; // b (adjacent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
text_layer_set_text(s_side_a_txt_layer, side_a_buffer);
|
||||||
|
text_layer_set_text(s_side_b_txt_layer, side_b_buffer);
|
||||||
|
text_layer_set_text(s_side_c_txt_layer, side_c_buffer);
|
||||||
|
|
||||||
|
// set trigonometric function options
|
||||||
|
s_sin = s_sin_arr[sin_index];
|
||||||
|
s_cos = s_cos_arr[cos_index];
|
||||||
|
s_tan = s_tan_arr[tan_index];
|
||||||
|
s_options_function[0] = s_sin;
|
||||||
|
s_options_function[1] = s_cos;
|
||||||
|
s_options_function[2] = s_tan;
|
||||||
|
|
||||||
|
// setup vibes
|
||||||
|
static const uint32_t VIBE_ON_FLOOR = 120; // ensure motor is on long enough to be felt
|
||||||
|
static const uint32_t VIBE_ON_SCALE = 4;
|
||||||
|
static const uint32_t VIBE_GAP = 40;
|
||||||
|
s_vibe_segments[0] = VIBE_ON_FLOOR + (uint32_t)s_triangle_layer->angle_A * VIBE_ON_SCALE;
|
||||||
|
s_vibe_segments[1] = VIBE_GAP;
|
||||||
|
s_vibe_segments[2] = VIBE_ON_FLOOR + (uint32_t)s_triangle_layer->angle_B * VIBE_ON_SCALE;
|
||||||
|
s_vibe_segments[3] = VIBE_GAP;
|
||||||
|
s_vibe_segments[4] = VIBE_ON_FLOOR + (uint32_t)s_triangle_layer->angle_C * VIBE_ON_SCALE;
|
||||||
|
s_vibe_pattern = (VibePattern){
|
||||||
|
.durations = s_vibe_segments,
|
||||||
|
.num_segments = ARRAY_LENGTH(s_vibe_segments),
|
||||||
|
};
|
||||||
|
|
||||||
|
// setup countdown
|
||||||
|
// TODO vary time based on progress:
|
||||||
|
// Store time step per triangle
|
||||||
|
// Determine time remaining from min+(s_total_count-s_current_triangle)*step
|
||||||
|
s_awareness_countdown_int = settings.TriangeMinSecs;
|
||||||
|
snprintf(s_awareness_countdown_buf, sizeof(s_awareness_countdown_buf), "%03u", s_awareness_countdown_int);
|
||||||
|
text_layer_set_text(s_countdown_txt_layer, s_awareness_countdown_buf);
|
||||||
|
}
|
||||||
|
|
||||||
static void alarm_window_load() {
|
static void alarm_window_load() {
|
||||||
wakeup_service_subscribe(live_wakeup_handler_for_alarm);
|
wakeup_service_subscribe(live_wakeup_handler_for_alarm);
|
||||||
|
|
||||||
@@ -638,159 +690,31 @@ static void alarm_window_load() {
|
|||||||
text_layer_set_background_color(s_side_c_txt_layer, GColorClear);
|
text_layer_set_background_color(s_side_c_txt_layer, GColorClear);
|
||||||
text_layer_set_font(s_side_c_txt_layer, s_font_mansalva_small);
|
text_layer_set_font(s_side_c_txt_layer, s_font_mansalva_small);
|
||||||
layer_add_child(window_layer, text_layer_get_layer(s_side_c_txt_layer));
|
layer_add_child(window_layer, text_layer_get_layer(s_side_c_txt_layer));
|
||||||
//// display 2 side lengths to the user
|
|
||||||
static char side_a_buffer[8], side_b_buffer[8], side_c_buffer[8];
|
|
||||||
uint8_t ignored_side = rand() % 3;
|
|
||||||
uint8_t sin_index = rand() % 2;
|
|
||||||
uint8_t cos_index = rand() % 2;
|
|
||||||
uint8_t tan_index = rand() % 2;
|
|
||||||
switch (ignored_side) {
|
|
||||||
case 0:
|
|
||||||
snprintf(side_b_buffer, sizeof(side_b_buffer), "%u", s_triangle_layer->side_b);
|
|
||||||
snprintf(side_c_buffer, sizeof(side_c_buffer), "%u", s_triangle_layer->side_c);
|
|
||||||
s_options_over[0] = side_b_buffer;
|
|
||||||
s_options_over[1] = side_c_buffer;
|
|
||||||
s_options_under[0] = side_b_buffer;
|
|
||||||
s_options_under[1] = side_c_buffer;
|
|
||||||
|
|
||||||
// store solution
|
// drop window
|
||||||
s_solution_indices[0] = 2; // tan/cot
|
|
||||||
if (s_triangle_layer->theta_angle_is_left) {
|
|
||||||
if (tan_index == 0) { // tan
|
|
||||||
s_solution_indices[1] = 0; // b (opposite)
|
|
||||||
s_solution_indices[2] = 1; // c (adjacent)
|
|
||||||
} else { // cot
|
|
||||||
s_solution_indices[1] = 1; // c (adjacent)
|
|
||||||
s_solution_indices[2] = 0; // b (opposite)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (tan_index == 0) { // tan
|
|
||||||
s_solution_indices[1] = 1; // c (opposite)
|
|
||||||
s_solution_indices[2] = 0; // b (adjacent)
|
|
||||||
} else { // cot
|
|
||||||
s_solution_indices[1] = 0; // b (adjacent)
|
|
||||||
s_solution_indices[2] = 1; // c (opposite)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
snprintf(side_a_buffer, sizeof(side_a_buffer), "%u", s_triangle_layer->side_a);
|
|
||||||
snprintf(side_c_buffer, sizeof(side_c_buffer), "%u", s_triangle_layer->side_c);
|
|
||||||
s_options_over[0] = side_a_buffer;
|
|
||||||
s_options_over[1] = side_c_buffer;
|
|
||||||
s_options_under[0] = side_a_buffer;
|
|
||||||
s_options_under[1] = side_c_buffer;
|
|
||||||
|
|
||||||
// store solution
|
|
||||||
if (s_triangle_layer->theta_angle_is_left) {
|
|
||||||
s_solution_indices[0] = 1; // cos/sec
|
|
||||||
if (cos_index == 0) { // cos
|
|
||||||
s_solution_indices[1] = 1; // c (adjacent)
|
|
||||||
s_solution_indices[2] = 0; // a (hypotenuse)
|
|
||||||
} else { // sec
|
|
||||||
s_solution_indices[1] = 0; // a (hypotenuse)
|
|
||||||
s_solution_indices[2] = 1; // c (adjacent)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s_solution_indices[0] = 0; // sin/csc
|
|
||||||
if (sin_index == 0) { // sin
|
|
||||||
s_solution_indices[1] = 1; // c (opposite)
|
|
||||||
s_solution_indices[2] = 0; // a (hypotenuse)
|
|
||||||
} else { // csc
|
|
||||||
s_solution_indices[1] = 0; // a (hypotenuse)
|
|
||||||
s_solution_indices[2] = 1; // c (opposite)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
snprintf(side_a_buffer, sizeof(side_a_buffer), "%u", s_triangle_layer->side_a);
|
|
||||||
snprintf(side_b_buffer, sizeof(side_b_buffer), "%u", s_triangle_layer->side_b);
|
|
||||||
s_options_over[0] = side_a_buffer;
|
|
||||||
s_options_over[1] = side_b_buffer;
|
|
||||||
s_options_under[0] = side_a_buffer;
|
|
||||||
s_options_under[1] = side_b_buffer;
|
|
||||||
|
|
||||||
// store solution
|
|
||||||
if (s_triangle_layer->theta_angle_is_left) {
|
|
||||||
s_solution_indices[0] = 0; // sin/csc
|
|
||||||
if (sin_index == 0) { // sin
|
|
||||||
s_solution_indices[1] = 1; // b (opposite)
|
|
||||||
s_solution_indices[2] = 0; // a (hypotenuse)
|
|
||||||
} else { // csc
|
|
||||||
s_solution_indices[1] = 0; // a (hypotenuse)
|
|
||||||
s_solution_indices[2] = 1; // b (opposite)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s_solution_indices[0] = 1;
|
|
||||||
if (cos_index == 0) { // cos
|
|
||||||
s_solution_indices[1] = 1; // b (adjacent)
|
|
||||||
s_solution_indices[2] = 0; // a (hypotenuse)
|
|
||||||
} else { // sec
|
|
||||||
s_solution_indices[1] = 0; // a (hypotenuse)
|
|
||||||
s_solution_indices[2] = 1; // b (adjacent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text_layer_set_text(s_side_a_txt_layer, side_a_buffer);
|
|
||||||
text_layer_set_text(s_side_b_txt_layer, side_b_buffer);
|
|
||||||
text_layer_set_text(s_side_c_txt_layer, side_c_buffer);
|
|
||||||
|
|
||||||
// drop menus
|
|
||||||
//// determine trigonometric function options
|
|
||||||
s_sin = s_sin_arr[sin_index];
|
|
||||||
s_cos = s_cos_arr[cos_index];
|
|
||||||
s_tan = s_tan_arr[tan_index];
|
|
||||||
//// set up menu options
|
|
||||||
s_options_function[0] = s_sin;
|
|
||||||
s_options_function[1] = s_cos;
|
|
||||||
s_options_function[2] = s_tan;
|
|
||||||
//// create drop window
|
|
||||||
s_drop_window = window_create();
|
s_drop_window = window_create();
|
||||||
window_set_window_handlers(
|
window_set_window_handlers(
|
||||||
s_drop_window,
|
s_drop_window,
|
||||||
(WindowHandlers){.load = drop_menu_window_load, .unload = drop_menu_window_unload});
|
(WindowHandlers){.load = drop_menu_window_load, .unload = drop_menu_window_unload});
|
||||||
|
|
||||||
// setup vibes
|
|
||||||
s_vibe_segments[0] = (uint32_t)(s_triangle_layer->angle_A * 2);
|
|
||||||
s_vibe_segments[1] = 200;
|
|
||||||
s_vibe_segments[2] = (uint32_t)(s_triangle_layer->angle_B * 2);
|
|
||||||
s_vibe_segments[3] = 200;
|
|
||||||
s_vibe_segments[4] = (uint32_t)(s_triangle_layer->angle_C * 2);
|
|
||||||
s_vibe_pattern = (VibePattern){
|
|
||||||
.durations = s_vibe_segments,
|
|
||||||
.num_segments = ARRAY_LENGTH(s_vibe_segments),
|
|
||||||
};
|
|
||||||
|
|
||||||
// countdown
|
// countdown
|
||||||
s_countdown_txt_layer = text_layer_create(GRect(0, (s_small_top_bar_height / 2) - PBL_IF_ROUND_ELSE(6, 10), PBL_DISPLAY_WIDTH, s_small_top_bar_height));
|
s_countdown_txt_layer = text_layer_create(GRect(0, (s_small_top_bar_height / 2) - PBL_IF_ROUND_ELSE(6, 10), PBL_DISPLAY_WIDTH, s_small_top_bar_height));
|
||||||
text_layer_set_background_color(s_countdown_txt_layer, GColorClear);
|
text_layer_set_background_color(s_countdown_txt_layer, GColorClear);
|
||||||
text_layer_set_text_color(s_countdown_txt_layer, GColorWhite);
|
text_layer_set_text_color(s_countdown_txt_layer, GColorWhite);
|
||||||
text_layer_set_text_alignment(s_countdown_txt_layer, GTextAlignmentCenter);
|
text_layer_set_text_alignment(s_countdown_txt_layer, GTextAlignmentCenter);
|
||||||
s_alarm_zap_repeat_countdown = 0;
|
|
||||||
if (s_alarm_reason == WAKE_AWARENESS_ALARM || s_alarm_reason == WAKE_RECOVERY_AWARENESS_ALARM) {
|
|
||||||
s_awareness_countdown_int = settings.AwarenessTimeLimitSeconds;
|
|
||||||
text_layer_set_font(s_countdown_txt_layer, s_font_lecoish_small);
|
text_layer_set_font(s_countdown_txt_layer, s_font_lecoish_small);
|
||||||
snprintf(s_awareness_countdown_buf, sizeof(s_awareness_countdown_buf), "%03u", s_awareness_countdown_int);
|
|
||||||
text_layer_set_text(s_countdown_txt_layer, s_awareness_countdown_buf);
|
|
||||||
layer_add_child(window_layer, text_layer_get_layer(s_countdown_txt_layer));
|
layer_add_child(window_layer, text_layer_get_layer(s_countdown_txt_layer));
|
||||||
|
|
||||||
schedule_alarm(WAKE_RECOVERY_AWARENESS_ALARM);
|
// configure UI around current triangle in s_triangle_layer
|
||||||
|
update_for_new_triangle();
|
||||||
|
|
||||||
|
// schedule recovery alarm and begin countdown :)
|
||||||
|
schedule_alarm(WAKE_RECOVERY_ALARM);
|
||||||
tick_timer_service_subscribe(SECOND_UNIT, awareness_alarm_seconds_handler);
|
tick_timer_service_subscribe(SECOND_UNIT, awareness_alarm_seconds_handler);
|
||||||
} else {
|
|
||||||
s_awareness_countdown_int = 0;
|
|
||||||
s_main_countdown_till_zap = settings.PavlokMainDelay;
|
|
||||||
text_layer_set_font(s_countdown_txt_layer, s_font_mansalva_small);
|
|
||||||
text_layer_set_text(s_countdown_txt_layer, "🏁 hold select 🏁");
|
|
||||||
layer_add_child(window_layer, text_layer_get_layer(s_countdown_txt_layer));
|
|
||||||
|
|
||||||
schedule_alarm(WAKE_RECOVERY_MAIN_ALARM);
|
|
||||||
tick_timer_service_subscribe(SECOND_UNIT, main_alarm_seconds_handler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void alarm_window_unload() {
|
static void alarm_window_unload() {
|
||||||
|
vibes_cancel();
|
||||||
tick_timer_service_unsubscribe();
|
tick_timer_service_unsubscribe();
|
||||||
text_layer_destroy(s_countdown_txt_layer);
|
text_layer_destroy(s_countdown_txt_layer);
|
||||||
window_destroy(s_drop_window);
|
window_destroy(s_drop_window);
|
||||||
@@ -816,21 +740,8 @@ static void alarm_window_unload() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void push_alarm_window() {
|
static void push_alarm_window() {
|
||||||
s_alarm_reason = persist_read_int(STORAGE_KEY_ALARM_REASON);
|
if (s_alarm_reason == WAKE_RECOVERY_ALARM) {
|
||||||
|
send_zap(settings.PavlokIntensity);
|
||||||
switch (s_alarm_reason) {
|
|
||||||
case WAKE_RECOVERY_MAIN_ALARM:
|
|
||||||
send_zap(settings.PavlokMax);
|
|
||||||
break;
|
|
||||||
case WAKE_RECOVERY_AWARENESS_ALARM:
|
|
||||||
send_zap(settings.PavlokMax);
|
|
||||||
[[fallthrough]];
|
|
||||||
case WAKE_AWARENESS_ALARM:
|
|
||||||
s_awareness_zap_intensity = persist_read_int(STORAGE_KEY_PAVLOK_INTENSITY);
|
|
||||||
vibes_short_pulse();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window_set_window_handlers(
|
window_set_window_handlers(
|
||||||
@@ -931,12 +842,7 @@ static void setter_down_click_handler(void *recognizer, void *ctx) {
|
|||||||
|
|
||||||
static void setter_select_click_handler(void *recognizer, void *ctx) {
|
static void setter_select_click_handler(void *recognizer, void *ctx) {
|
||||||
if (s_highlight_pos == 2) {
|
if (s_highlight_pos == 2) {
|
||||||
if (s_alarm_reason != WAKE_MAIN_ALARM) {
|
bool success = schedule_alarm(WAKE_ALARM);
|
||||||
send_zap(settings.PavlokMax);
|
|
||||||
vibes_double_pulse();
|
|
||||||
return; // early return to avoid overwriting recovery/awareness alarm
|
|
||||||
}
|
|
||||||
bool success = schedule_alarm(WAKE_MAIN_ALARM);
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return; // early return to avoid indicating alarm was set when it was not
|
return; // early return to avoid indicating alarm was set when it was not
|
||||||
}
|
}
|
||||||
@@ -1053,8 +959,6 @@ static void setter_window_unload() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void push_setter_window() {
|
static void push_setter_window() {
|
||||||
s_alarm_reason = persist_read_int(STORAGE_KEY_ALARM_REASON);
|
|
||||||
|
|
||||||
window_set_window_handlers(
|
window_set_window_handlers(
|
||||||
s_window,
|
s_window,
|
||||||
(WindowHandlers){.load = setter_window_load, .unload = setter_window_unload});
|
(WindowHandlers){.load = setter_window_load, .unload = setter_window_unload});
|
||||||
@@ -1072,7 +976,7 @@ static void init() {
|
|||||||
|
|
||||||
// initialize pkjs
|
// initialize pkjs
|
||||||
app_message_register_inbox_received(inbox_received_handler);
|
app_message_register_inbox_received(inbox_received_handler);
|
||||||
app_message_open(128, 128); // TODO tighten sizes
|
app_message_open(128, 64);
|
||||||
|
|
||||||
// load settings from persist (or set defaults if not present)
|
// load settings from persist (or set defaults if not present)
|
||||||
if (persist_exists(STORAGE_KEY_SETTINGS)) {
|
if (persist_exists(STORAGE_KEY_SETTINGS)) {
|
||||||
@@ -1080,21 +984,24 @@ static void init() {
|
|||||||
} else {
|
} else {
|
||||||
settings.TutorialEnabled = true;
|
settings.TutorialEnabled = true;
|
||||||
settings.PavlokEnabled = false;
|
settings.PavlokEnabled = false;
|
||||||
settings.PavlokIntervalSeconds = 10;
|
settings.PavlokIntensity = 50;
|
||||||
settings.PavlokMin = 30;
|
settings.TriangleCount = 5;
|
||||||
settings.PavlokMax = 60;
|
settings.TriangeMinSecs = 25;
|
||||||
settings.PavlokStep = 10;
|
|
||||||
settings.PavlokMainDelay = 60;
|
|
||||||
settings.AwarenessTimeGapMinutes = 9;
|
|
||||||
settings.AwarenessTimeLimitSeconds = 30;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read even when opening setter to ensure a recovery alarm is not able to be overwritten
|
||||||
|
s_alarm_reason = persist_read_int(STORAGE_KEY_ALARM_REASON);
|
||||||
|
|
||||||
switch (launch_reason()) {
|
switch (launch_reason()) {
|
||||||
case APP_LAUNCH_WAKEUP:
|
case APP_LAUNCH_WAKEUP:
|
||||||
push_alarm_window();
|
push_alarm_window();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (s_alarm_reason == WAKE_RECOVERY_ALARM) {
|
||||||
|
push_alarm_window();
|
||||||
|
} else {
|
||||||
push_setter_window();
|
push_setter_window();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
STATUS_MAIN_TUTORIAL = 0,
|
|
||||||
STATUS_CONFIRMATION = 1,
|
STATUS_CONFIRMATION = 1,
|
||||||
STATUS_SET_FAILURE = 2,
|
STATUS_SET_FAILURE = 2,
|
||||||
STATUS_MATH_FAILURE = 3
|
STATUS_MATH_FAILURE = 3
|
||||||
|
|||||||
+2
-4
@@ -3,10 +3,8 @@
|
|||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
WAKE_MAIN_ALARM = 0,
|
WAKE_ALARM = 0,
|
||||||
WAKE_AWARENESS_ALARM = 1,
|
WAKE_RECOVERY_ALARM = 1,
|
||||||
WAKE_RECOVERY_MAIN_ALARM = 2,
|
|
||||||
WAKE_RECOVERY_AWARENESS_ALARM = 3
|
|
||||||
} WakeupReason;
|
} WakeupReason;
|
||||||
|
|
||||||
typedef struct TheTime {
|
typedef struct TheTime {
|
||||||
|
|||||||
+5
-12
@@ -2,24 +2,17 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// BUMP ON UPDATES
|
#define STORAGE_KEY_SETTINGS 51 // BUMP ON MAJOR UPDATES
|
||||||
#define STORAGE_KEY_SETTINGS 50
|
|
||||||
#define STORAGE_KEY_ALARM_REASON 100
|
|
||||||
|
|
||||||
#define STORAGE_KEY_ALARM_HOUR 0
|
#define STORAGE_KEY_ALARM_HOUR 0
|
||||||
#define STORAGE_KEY_ALARM_MINUTE 1
|
#define STORAGE_KEY_ALARM_MINUTE 1
|
||||||
#define STORAGE_KEY_PAVLOK_INTENSITY 2
|
#define STORAGE_KEY_ALARM_REASON 2
|
||||||
|
|
||||||
typedef struct AppSettings {
|
typedef struct AppSettings {
|
||||||
bool TutorialEnabled;
|
bool TutorialEnabled;
|
||||||
bool PavlokEnabled;
|
bool PavlokEnabled;
|
||||||
uint8_t PavlokMin;
|
uint8_t PavlokIntensity;
|
||||||
uint8_t PavlokMax;
|
uint8_t TriangleCount;
|
||||||
uint8_t PavlokStep;
|
uint8_t TriangeMinSecs;
|
||||||
uint8_t PavlokMainDelay;
|
|
||||||
uint8_t PavlokIntervalSeconds;
|
|
||||||
uint8_t AwarenessTimeGapMinutes;
|
|
||||||
uint8_t AwarenessTimeLimitSeconds;
|
|
||||||
} __attribute__((__packed__)) AppSettings;
|
} __attribute__((__packed__)) AppSettings;
|
||||||
|
|
||||||
_Static_assert(sizeof(AppSettings) <= 256, "AppSettings exceeds Pebble 256-byte persist limit!");
|
_Static_assert(sizeof(AppSettings) <= 256, "AppSettings exceeds Pebble 256-byte persist limit!");
|
||||||
+23
-9
@@ -33,17 +33,11 @@ static void right_triangle_layer_update_proc(Layer *layer, GContext *ctx) {
|
|||||||
|
|
||||||
// circle theta angle
|
// circle theta angle
|
||||||
graphics_context_set_stroke_color(ctx, GColorRed);
|
graphics_context_set_stroke_color(ctx, GColorRed);
|
||||||
graphics_draw_circle(ctx, GPoint(tri->theta_angle_is_left ? start_x : end_x, base_y), 4);
|
graphics_context_set_stroke_width(ctx, 2);
|
||||||
|
graphics_draw_circle(ctx, GPoint(tri->theta_angle_is_left ? start_x : end_x, base_y), 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color) {
|
static void right_triangle_layer_generate_properties(RightTriangleLayer *tri, GRect frame) {
|
||||||
RightTriangleLayer *tri = malloc(sizeof(RightTriangleLayer));
|
|
||||||
if (!tri)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// set color
|
|
||||||
tri->color = color;
|
|
||||||
|
|
||||||
// determine angles
|
// determine angles
|
||||||
tri->angle_A = 90; // right angle at apex
|
tri->angle_A = 90; // right angle at apex
|
||||||
tri->theta_angle_is_left = rand() % 2;
|
tri->theta_angle_is_left = rand() % 2;
|
||||||
@@ -65,6 +59,18 @@ RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color) {
|
|||||||
tri->side_b = (uint16_t)(((uint32_t)sin_B * tri->side_a) / TRIG_MAX_RATIO);
|
tri->side_b = (uint16_t)(((uint32_t)sin_B * tri->side_a) / TRIG_MAX_RATIO);
|
||||||
tri->side_c = (uint16_t)(((uint32_t)sin_C * tri->side_a) / TRIG_MAX_RATIO);
|
tri->side_c = (uint16_t)(((uint32_t)sin_C * tri->side_a) / TRIG_MAX_RATIO);
|
||||||
} while (tri->side_a == tri->side_b || tri->side_a == tri->side_c || tri->side_b == tri->side_c);
|
} while (tri->side_a == tri->side_b || tri->side_a == tri->side_c || tri->side_b == tri->side_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color) {
|
||||||
|
RightTriangleLayer *tri = malloc(sizeof(RightTriangleLayer));
|
||||||
|
if (!tri)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// set color
|
||||||
|
tri->color = color;
|
||||||
|
|
||||||
|
// generate this triangle's angles and side lengths
|
||||||
|
right_triangle_layer_generate_properties(tri, frame);
|
||||||
|
|
||||||
// create layer with room for a pointer-sized data slot & store the RightTriangleLayer* there
|
// create layer with room for a pointer-sized data slot & store the RightTriangleLayer* there
|
||||||
tri->layer = layer_create_with_data(frame, sizeof(RightTriangleLayer *));
|
tri->layer = layer_create_with_data(frame, sizeof(RightTriangleLayer *));
|
||||||
@@ -74,6 +80,14 @@ RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color) {
|
|||||||
return tri;
|
return tri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void right_triangle_layer_regen(RightTriangleLayer *tri) {
|
||||||
|
if (!tri || !tri->layer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
right_triangle_layer_generate_properties(tri, layer_get_bounds(tri->layer));
|
||||||
|
layer_mark_dirty(tri->layer);
|
||||||
|
}
|
||||||
|
|
||||||
void right_triangle_layer_destroy(RightTriangleLayer *tri) {
|
void right_triangle_layer_destroy(RightTriangleLayer *tri) {
|
||||||
if (!tri)
|
if (!tri)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -20,4 +20,5 @@ typedef struct {
|
|||||||
} __attribute__((__packed__)) RightTriangleLayer;
|
} __attribute__((__packed__)) RightTriangleLayer;
|
||||||
|
|
||||||
RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color);
|
RightTriangleLayer *right_triangle_layer_create(GRect frame, GColor color);
|
||||||
|
void right_triangle_layer_regen(RightTriangleLayer *tri);
|
||||||
void right_triangle_layer_destroy(RightTriangleLayer *tri);
|
void right_triangle_layer_destroy(RightTriangleLayer *tri);
|
||||||
+18
-63
@@ -32,59 +32,14 @@ module.exports = [
|
|||||||
"defaultValue": "Test API Key w/Beep"
|
"defaultValue": "Test API Key w/Beep"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "pavlok_zap_min",
|
"id": "pavlok_zap_intensity",
|
||||||
"type": "slider",
|
"type": "slider",
|
||||||
"messageKey": "PKJS_PAVLOK_ZAP_MIN",
|
"messageKey": "PKJS_PAVLOK_ZAP_INTENSITY",
|
||||||
"defaultValue": 30,
|
"defaultValue": 50,
|
||||||
"min": 5,
|
"min": 5,
|
||||||
"max": 100,
|
"max": 100,
|
||||||
"step": 5,
|
"step": 5,
|
||||||
"label": "Zap Intensity (Base)",
|
"label": "Zap Intensity %"
|
||||||
"description": "% intensity of base zap during alarms."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pavlok_zap_max",
|
|
||||||
"type": "slider",
|
|
||||||
"messageKey": "PKJS_PAVLOK_ZAP_MAX",
|
|
||||||
"defaultValue": 60,
|
|
||||||
"min": 10,
|
|
||||||
"max": 100,
|
|
||||||
"step": 5,
|
|
||||||
"label": "Zap Intensity (Max)",
|
|
||||||
"description": "% intensity of max zap; only takes effect if greater than base. Used for force exit/alarm overwrite penalties, as well as max value to step to in awareness alarms if zap step is enabled."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pavlok_zap_step",
|
|
||||||
"type": "slider",
|
|
||||||
"messageKey": "PKJS_PAVLOK_ZAP_STEP",
|
|
||||||
"defaultValue": 10,
|
|
||||||
"min": 0,
|
|
||||||
"max": 100,
|
|
||||||
"step": 5,
|
|
||||||
"label": "Zap Step",
|
|
||||||
"description": "% to increment base zap intensity after each failed awareness alarm; set to 0 to disable."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pavlok_zap_main_delay",
|
|
||||||
"type": "slider",
|
|
||||||
"messageKey": "PKJS_PAVLOK_ZAP_MAIN_DELAY",
|
|
||||||
"defaultValue": 60,
|
|
||||||
"min": 15,
|
|
||||||
"max": 120,
|
|
||||||
"step": 1,
|
|
||||||
"label": "Zap Main Alarm Delay",
|
|
||||||
"description": "Seconds into main alarm to start zaps."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pavlok_zap_interval",
|
|
||||||
"type": "slider",
|
|
||||||
"messageKey": "PKJS_PAVLOK_ZAP_INTERVAL",
|
|
||||||
"defaultValue": 10,
|
|
||||||
"min": 1,
|
|
||||||
"max": 60,
|
|
||||||
"step": 1,
|
|
||||||
"label": "Zap Repeat Interval",
|
|
||||||
"description": "Seconds between zaps during alarms."
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -94,27 +49,27 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
"type": "heading",
|
"type": "heading",
|
||||||
"size": 3,
|
"size": 3,
|
||||||
"defaultValue": "Awareness Alarm"
|
"defaultValue": "Alarm Options"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "awareness_time_gap",
|
"id": "triangle_count",
|
||||||
"type": "slider",
|
"type": "slider",
|
||||||
"messageKey": "PKJS_AWARENESS_TIME_GAP",
|
"messageKey": "PKJS_TRIANGLE_COUNT",
|
||||||
"defaultValue": 9,
|
"defaultValue": 5,
|
||||||
"min": 1,
|
"min": 1,
|
||||||
"max": 30,
|
"max": 20,
|
||||||
"label": "Gap (Minutes)",
|
"label": "Triangle Count",
|
||||||
"description": "Time gap between main alarm and subsequent awareness alarms."
|
"description": "Number of triangles to solve before disarming the alarm."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "awareness_time_limit",
|
"id": "triangle_min_secs",
|
||||||
"type": "slider",
|
"type": "slider",
|
||||||
"messageKey": "PKJS_AWARENESS_TIME_LIMIT",
|
"messageKey": "PKJS_TRIANGLE_MIN_SECS",
|
||||||
"defaultValue": 30,
|
"defaultValue": 25,
|
||||||
"min": 3,
|
"min": 5,
|
||||||
"max": 120,
|
"max": 60,
|
||||||
"label": "Time Limit (Seconds)",
|
"label": "Minimum Time Limit (Seconds)",
|
||||||
"description": "Time limit the awareness alarm must be solved within to disarm Trigalarm."
|
"description": "Time limit the last/fastest triangle must be solved within. All other time limits are calculated based on this one."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-10
@@ -8,19 +8,11 @@ module.exports = function (minified) {
|
|||||||
if (this.get()) {
|
if (this.get()) {
|
||||||
clayConfig.getItemById('pavlok_api_key').show();
|
clayConfig.getItemById('pavlok_api_key').show();
|
||||||
clayConfig.getItemById('pavlok_test_beep').show();
|
clayConfig.getItemById('pavlok_test_beep').show();
|
||||||
clayConfig.getItemById('pavlok_zap_min').show();
|
clayConfig.getItemById('pavlok_zap_intensity').show();
|
||||||
clayConfig.getItemById('pavlok_zap_max').show();
|
|
||||||
clayConfig.getItemById('pavlok_zap_step').show();
|
|
||||||
clayConfig.getItemById('pavlok_zap_main_delay').show();
|
|
||||||
clayConfig.getItemById('pavlok_zap_interval').show();
|
|
||||||
} else {
|
} else {
|
||||||
clayConfig.getItemById('pavlok_api_key').hide();
|
clayConfig.getItemById('pavlok_api_key').hide();
|
||||||
clayConfig.getItemById('pavlok_test_beep').hide();
|
clayConfig.getItemById('pavlok_test_beep').hide();
|
||||||
clayConfig.getItemById('pavlok_zap_min').hide();
|
clayConfig.getItemById('pavlok_zap_intensity').hide();
|
||||||
clayConfig.getItemById('pavlok_zap_max').hide();
|
|
||||||
clayConfig.getItemById('pavlok_zap_step').hide();
|
|
||||||
clayConfig.getItemById('pavlok_zap_main_delay').hide();
|
|
||||||
clayConfig.getItemById('pavlok_zap_interval').hide();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -10,9 +10,7 @@ Pebble.addEventListener('showConfiguration', function () {
|
|||||||
|
|
||||||
// custom listener so messages can be sent from customClay.js
|
// custom listener so messages can be sent from customClay.js
|
||||||
Pebble.addEventListener('webviewclosed', function(e) {
|
Pebble.addEventListener('webviewclosed', function(e) {
|
||||||
if (e && !e.response) {
|
if (!e || !e.response) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var data;
|
var data;
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in new issue
Block a user