Add zap step mode (zaps get stronger as more awareness alarms are failed); remove nudge setting (nudge is always on now); fix nudge being missed if wakeup occurs while in setter; fix force-exiting an awareness alarm triggering a main alarm

This commit is contained in:
2026-08-19 22:31:16 -04:00
parent 50358d70ed
commit ce0a8ee4fe
7 changed files with 135 additions and 77 deletions
+73 -46
View File
@@ -62,7 +62,8 @@ static uint8_t s_solution_indices[3] = {8, 8, 8};
static uint8_t s_selected_indices[3] = {9, 9, 9};
static uint8_t s_awareness_countdown_int = 0;
static char s_awareness_countdown_buf[4];
static uint8_t s_zap_repeat_countdown = 0;
static uint8_t s_awareness_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 VibePattern s_vibe_pattern;
@@ -195,13 +196,22 @@ static void inbox_received_handler(DictionaryIterator *iter, void *context) {
}
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_MIN, &settings.PavlokMin);
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ZAP_STEP, &settings.PavlokStep);
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ZAP_MAX, &settings.PavlokMax);
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);
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_AWARENESS_NUDGE_ENABLE, &settings.AwarenessNudgeEnabled);
if (settings.PavlokStep == 0) {
// match min and max values if disabling step mode, as the max is used for force exit/overwrite punishments
settings.PavlokMax = settings.PavlokMin;
} else if (settings.PavlokMax <= settings.PavlokMin) {
// disable step mode if max is not higher than min
settings.PavlokStep = 0;
}
persist_write_data(STORAGE_KEY_SETTINGS, &settings, sizeof(settings));
}
static void send_zap() {
static void send_zap(uint8_t intensity) {
if (!settings.PavlokEnabled) {
return;
}
@@ -211,7 +221,7 @@ static void send_zap() {
if (result != APP_MSG_OK) {
return;
}
dict_write_uint8(out, MESSAGE_KEY_PKJS_PAVLOK_ZAP, 1);
dict_write_uint8(out, MESSAGE_KEY_PKJS_PAVLOK_ZAP, intensity);
result = app_message_outbox_send();
if (result != APP_MSG_OK) {
return;
@@ -225,16 +235,6 @@ static bool schedule_alarm(WakeupReason schedule_reason) {
WakeupId status;
switch (schedule_reason) {
case WAKE_RECOVERY_ALARM:
target = time(NULL) + 15;
// no need to cancel alarms, as RECOVERY_ALARM is short-lived and is expired by the time another is being scheduled
status = wakeup_schedule_simple_robust(target, 15, schedule_reason);
break;
case WAKE_AWARENESS_ALARM:
target = time(NULL) + settings.AwarenessTimeGapMinutes * SECONDS_PER_MINUTE;
wakeup_cancel_all();
status = wakeup_schedule_simple_robust(target, SECONDS_PER_MINUTE, schedule_reason);
break;
case WAKE_MAIN_ALARM: {
time_t now = time(NULL);
struct tm target_tm = *localtime(&now);
@@ -247,7 +247,28 @@ static bool schedule_alarm(WakeupReason schedule_reason) {
}
wakeup_cancel_all();
status = wakeup_schedule_simple_robust(target, SECONDS_PER_MINUTE, schedule_reason);
if (settings.PavlokEnabled) {
persist_write_int(STORAGE_KEY_PAVLOK_INTENSITY, settings.PavlokMin);
}
break;
}
case WAKE_AWARENESS_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;
// no need to cancel alarms, as recovery alarms short-lived and is expired by the time another is being scheduled
status = wakeup_schedule_simple_robust(target, 15, schedule_reason);
}
if (status < 0) {
push_status_window(STATUS_SET_FAILURE);
@@ -295,15 +316,15 @@ static void awareness_alarm_seconds_handler(struct tm *tick_time, TimeUnits unit
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);
if (s_awareness_countdown_int == 0) {
send_zap();
s_zap_repeat_countdown = settings.PavlokIntervalSeconds;
send_zap(s_awareness_zap_intensity);
s_awareness_zap_repeat_countdown = settings.PavlokIntervalSeconds;
}
} else {
if (s_zap_repeat_countdown > 0) {
s_zap_repeat_countdown--;
if (s_zap_repeat_countdown == 0) {
send_zap();
s_zap_repeat_countdown = settings.PavlokIntervalSeconds;
if (s_awareness_zap_repeat_countdown > 0) {
s_awareness_zap_repeat_countdown--;
if (s_awareness_zap_repeat_countdown == 0) {
send_zap(s_awareness_zap_intensity);
s_awareness_zap_repeat_countdown = settings.PavlokIntervalSeconds;
}
}
layer_set_hidden(text_layer_get_layer(s_countdown_txt_layer), s_hide_countdown);
@@ -396,8 +417,9 @@ static void drop_menu_window_unload(Window *window) {
}
static void live_wakeup_handler_for_alarm(WakeupId id, int32_t wakeup_reason) {
if (wakeup_reason == WAKE_RECOVERY_ALARM) { // if recovery alarm, re-schedule it
schedule_alarm(WAKE_RECOVERY_ALARM); // output is not captured, as it is not actionable
if (wakeup_reason == WAKE_RECOVERY_MAIN_ALARM || wakeup_reason == WAKE_RECOVERY_AWARENESS_ALARM) {
// if recovery alarm, re-schedule it
schedule_alarm(wakeup_reason);
}
}
@@ -623,13 +645,10 @@ static void alarm_window_load() {
text_layer_set_background_color(s_countdown_txt_layer, GColorClear);
text_layer_set_text_color(s_countdown_txt_layer, GColorWhite);
text_layer_set_text_alignment(s_countdown_txt_layer, GTextAlignmentCenter);
// this code forces recovery alarms to act as main alarms, even if the force quit was an awareness alarm;
// this is a "bug" I'm willing to accept for the sake of simplicity - all it does is punish the user more
// for force-quitting
if (s_alarm_reason == WAKE_AWARENESS_ALARM) {
if (s_alarm_reason == WAKE_AWARENESS_ALARM || s_alarm_reason == WAKE_RECOVERY_AWARENESS_ALARM) {
text_layer_set_font(s_countdown_txt_layer, s_font_lecoish_small);
s_awareness_countdown_int = settings.AwarenessTimeLimitSeconds;
s_zap_repeat_countdown = 0;
s_awareness_zap_repeat_countdown = 0;
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);
} else {
@@ -649,10 +668,11 @@ static void alarm_window_load() {
.num_segments = ARRAY_LENGTH(s_vibe_segments),
};
schedule_alarm(WAKE_RECOVERY_ALARM); // output is not captured, as it is not actionable
if (s_alarm_reason == WAKE_AWARENESS_ALARM) {
if (s_alarm_reason == WAKE_AWARENESS_ALARM || s_alarm_reason == WAKE_RECOVERY_AWARENESS_ALARM) {
schedule_alarm(WAKE_RECOVERY_AWARENESS_ALARM);
tick_timer_service_subscribe(SECOND_UNIT, awareness_alarm_seconds_handler);
} else {
schedule_alarm(WAKE_RECOVERY_MAIN_ALARM);
tick_timer_service_subscribe(SECOND_UNIT, main_alarm_seconds_handler);
}
}
@@ -758,6 +778,23 @@ static void alarm_click_config_provider(void *ctx) {
}
static void push_alarm_window() {
s_alarm_reason = persist_read_int(STORAGE_KEY_ALARM_REASON);
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(
s_window,
(WindowHandlers){.load = alarm_window_load, .unload = alarm_window_unload});
@@ -867,7 +904,7 @@ static void setter_down_click_handler(void *recognizer, void *ctx) {
static void setter_select_click_handler(void *recognizer, void *ctx) {
if (s_highlight_pos == 2) {
if (s_alarm_reason != WAKE_MAIN_ALARM) {
send_zap();
send_zap(settings.PavlokMax);
vibes_double_pulse();
return; // early return to avoid overwriting recovery/awareness alarm
}
@@ -910,7 +947,6 @@ static void setter_click_config_provider(void *ctx) {
}
static void live_wakeup_handler_for_setter(WakeupId id, int32_t reason) {
s_alarm_reason = persist_read_int(STORAGE_KEY_ALARM_REASON);
s_highlight_pos = 0;
window_stack_pop_all(false);
push_alarm_window();
@@ -981,6 +1017,8 @@ static void setter_window_unload() {
}
static void push_setter_window() {
s_alarm_reason = persist_read_int(STORAGE_KEY_ALARM_REASON);
window_set_window_handlers(
s_window,
(WindowHandlers){.load = setter_window_load, .unload = setter_window_unload});
@@ -1007,26 +1045,15 @@ static void init() {
settings.TutorialEnabled = true;
settings.PavlokEnabled = false;
settings.PavlokIntervalSeconds = 10;
settings.PavlokMin = 30;
settings.PavlokStep = 10;
settings.PavlokMax = 60;
settings.AwarenessTimeGapMinutes = 9;
settings.AwarenessTimeLimitSeconds = 30;
settings.AwarenessNudgeEnabled = true;
}
s_alarm_reason = persist_read_int(STORAGE_KEY_ALARM_REASON);
switch (launch_reason()) {
case APP_LAUNCH_WAKEUP:
switch (s_alarm_reason) {
case WAKE_RECOVERY_ALARM:
send_zap();
break;
case WAKE_AWARENESS_ALARM:
if (settings.AwarenessNudgeEnabled) {
vibes_short_pulse();
}
break;
default:
break;
}
push_alarm_window();
break;
default: