Add alarm reset button to phone-side settings

This commit is contained in:
2026-08-23 22:38:31 -04:00
parent 0fe121f9ca
commit 86c250a052
6 changed files with 103 additions and 51 deletions
+1
View File
@@ -13,6 +13,7 @@ Perhaps the most unique, Trigalarm features optional [Pavlok](https://pavlok.com
## 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!
- 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.
## Some Neat Stuff
- The vibration pattern is randomized. It derives the length of the three pulses from the angles in the generated triangle. Neat!
+1
View File
@@ -33,6 +33,7 @@
},
"messageKeys": [
"PKJS_READY",
"PKJS_ALARM_RESET",
"PKJS_PAVLOK_ENABLE",
"PKJS_PAVLOK_ZAP_MIN",
"PKJS_PAVLOK_ZAP_STEP",
+59 -49
View File
@@ -182,54 +182,6 @@ static void push_status_window(StatusWindowPurpose purpose) {
window_stack_push(s_status_window, true);
}
////////////////////// PKJS //////////////////////
static void apply_uint8_from_tuple(DictionaryIterator *iter, uint32_t key, void *dest) {
Tuple *t = dict_find(iter, key);
if (!t)
return;
*(uint8_t *)dest = t->value->uint8;
}
static void inbox_received_handler(DictionaryIterator *iter, void *context) {
Tuple *ready_tuple = dict_find(iter, MESSAGE_KEY_PKJS_READY);
if (ready_tuple) {
return;
}
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);
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(uint8_t intensity) {
if (!settings.PavlokEnabled) {
return;
}
DictionaryIterator *out;
AppMessageResult result = app_message_outbox_begin(&out);
if (result != APP_MSG_OK) {
return;
}
dict_write_uint8(out, MESSAGE_KEY_PKJS_PAVLOK_ZAP, intensity);
result = app_message_outbox_send();
if (result != APP_MSG_OK) {
return;
}
}
////////////////////// MISC //////////////////////
static time_t compute_alarm_time(uint8_t hour, uint8_t minute) {
@@ -317,6 +269,64 @@ static void add_bar_layers_to_window(Layer *window_layer, bool small_top) {
layer_add_child(window_layer, s_bottom_bar_layer);
}
////////////////////// PKJS //////////////////////
static void apply_uint8_from_tuple(DictionaryIterator *iter, uint32_t key, void *dest) {
Tuple *t = dict_find(iter, key);
if (!t)
return;
*(uint8_t *)dest = t->value->uint8;
}
static void inbox_received_handler(DictionaryIterator *iter, void *context) {
Tuple *ready_tuple = dict_find(iter, MESSAGE_KEY_PKJS_READY);
if (ready_tuple) {
return;
}
// handle alarm reset requests
if (dict_find(iter, MESSAGE_KEY_PKJS_ALARM_RESET)) {
the_time.HourWIP = 0;
the_time.MinuteWIP = 0;
s_alarm_reason = WAKE_MAIN_ALARM;
schedule_alarm(WAKE_MAIN_ALARM);
return;
}
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);
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(uint8_t intensity) {
if (!settings.PavlokEnabled) {
return;
}
DictionaryIterator *out;
AppMessageResult result = app_message_outbox_begin(&out);
if (result != APP_MSG_OK) {
return;
}
dict_write_uint8(out, MESSAGE_KEY_PKJS_PAVLOK_ZAP, intensity);
result = app_message_outbox_send();
if (result != APP_MSG_OK) {
return;
}
}
////////////////////// ALARM //////////////////////
static void main_alarm_seconds_handler(struct tm *tick_time, TimeUnits units_changed) {
@@ -1047,7 +1057,7 @@ static void init() {
// initialize pkjs
app_message_register_inbox_received(inbox_received_handler);
app_message_open(64, 64); // TODO tighten sizes
app_message_open(128, 128); // TODO tighten sizes
// load settings from persist (or set defaults if not present)
if (persist_exists(STORAGE_KEY_SETTINGS)) {
+5 -1
View File
@@ -29,7 +29,6 @@ module.exports = [
{
"id": "pavlok_test_beep",
"type": "button",
"messageKey": "PKJS_PAVLOK_TEST_BEEP",
"defaultValue": "Test API Key w/Beep"
},
{
@@ -108,6 +107,11 @@ module.exports = [
}
]
},
{
"id": "alarm_reset",
"type": "button",
"defaultValue": "Reset Alarm"
},
{
"type": "submit",
"defaultValue": "Save Settings"
+6
View File
@@ -39,11 +39,17 @@ module.exports = function (minified) {
xhr.send(data);
}
function sendResetSignal() {
window.location = 'pebblejs://close#' +
encodeURIComponent(JSON.stringify({ resetAlarm: 1 }));
}
clayConfig.on(clayConfig.EVENTS.AFTER_BUILD, function () {
var pavlokIntegrationToggle = clayConfig.getItemById('pavlok_enable');
togglePavlokIntegration.call(pavlokIntegrationToggle);
pavlokIntegrationToggle.on('change', togglePavlokIntegration);
clayConfig.getItemById('pavlok_test_beep').on('click', sendTestBeep);
clayConfig.getItemById('alarm_reset').on('click', sendResetSignal);
});
};
+31 -1
View File
@@ -2,7 +2,37 @@ var Clay = require('@rebble/clay');
var clayConfig = require('./config');
var customClay = require('./customClay');
new Clay(clayConfig, customClay);
var clay = new Clay(clayConfig, customClay, { autoHandleEvents: false });
Pebble.addEventListener('showConfiguration', function () {
Pebble.openURL(clay.generateUrl());
});
// custom listener so messages can be sent from customClay.js
Pebble.addEventListener('webviewclosed', function(e) {
if (e && !e.response) {
return;
}
var data;
try {
data = JSON.parse(e.response.match(/^\{/) ? e.response : decodeURIComponent(e.response));
} catch (err) {
return;
}
if (data.resetAlarm) {
Pebble.sendAppMessage({ PKJS_ALARM_RESET: 1 });
return;
}
var dict = clay.getSettings(e.response);
Pebble.sendAppMessage(dict, function(e) {
console.log('Sent config data to Pebble');
}, function(e) {
console.log('Failed to send config data!');
console.log(JSON.stringify(e));
});
});
Pebble.addEventListener("ready", function () {
Pebble.sendAppMessage({ PKJS_READY: 1 });