From 04bf4b2c45cdf48ab3fc1344625598182469675f Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Tue, 18 Aug 2026 00:11:00 -0400 Subject: [PATCH] Initial Pavlok integration --- package.json | 23 ++++++++++++++++--- src/c/main.c | 37 +++++++++++++++++++++++++++++- src/c/main.h | 1 + src/pkjs/config.js | 52 ++++++++++++++++++++++++++++++++++++++++++ src/pkjs/customClay.js | 24 +++++++++++++++++++ src/pkjs/index.js | 35 ++++++++++++++++++++++++++++ 6 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 src/pkjs/config.js create mode 100644 src/pkjs/customClay.js create mode 100644 src/pkjs/index.js diff --git a/package.json b/package.json index 49386ec..846f712 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,28 @@ "name": "trigalarm", "author": "RandyTheSilly", "version": "1.0.0", - "keywords": ["alarm", "math", "trigonometry", "trig", "triangle"], + "keywords": [ + "alarm", + "math", + "trigonometry", + "trig", + "triangle", + "pavlok", + "shock", + "zap" + ], + "capabilities": [ + "configurable" + ], "private": true, - "dependencies": {}, + "dependencies": { + "@rebble/clay": "^1.0.10" + }, "pebble": { "displayName": "Trigalarm", "uuid": "293cd38c-4e8c-46ae-a3dc-c789d7ce93ab", "sdkVersion": "3", - "enableMultiJS": false, + "enableMultiJS": true, "targetPlatforms": [ "emery", "gabbro" @@ -18,6 +32,9 @@ "watchface": false }, "messageKeys": [ + "PKJS_READY", + "PKJS_PAVLOK_ENABLE", + "PKJS_PAVLOK_ENGAGE" ], "resources": { "media": [ diff --git a/src/c/main.c b/src/c/main.c index 32a7601..7ee831b 100644 --- a/src/c/main.c +++ b/src/c/main.c @@ -170,6 +170,34 @@ static void push_status_window(StatusWindowPurpose purpose) { window_stack_push(s_status_window, true); } +////////////////////// PKJS ////////////////////// + +static void inbox_received_handler(DictionaryIterator *iter, void *context) { + Tuple *ready_tuple = dict_find(iter, MESSAGE_KEY_PKJS_READY); + if (ready_tuple) { + return; // TODO use ready signal better + } + + Tuple *clay_tuple = dict_find(iter, MESSAGE_KEY_PKJS_PAVLOK_ENABLE); + if (clay_tuple) { + persist_write_bool(STORAGE_KEY_PAVLOK_ENABLED, clay_tuple->value->uint8); + return; + } +} + +static void send_stimulus() { + DictionaryIterator *out; + AppMessageResult result = app_message_outbox_begin(&out); + if (result != APP_MSG_OK) { + return; + } + dict_write_uint8(out, MESSAGE_KEY_PKJS_PAVLOK_ENGAGE, 1); + result = app_message_outbox_send(); + if (result != APP_MSG_OK) { + return; + } +} + ////////////////////// MISC ////////////////////// static bool schedule_alarm(WakeupReason schedule_reason) { @@ -247,8 +275,11 @@ static void alarm_seconds_handler(struct tm *tick_time, TimeUnits units_changed) if (s_stopwatch_time[1] == 59) { s_stopwatch_time[1] = 0; s_stopwatch_time[0]++; + // zap Pavlok user as punishment for being a total failure + if (persist_read_bool(STORAGE_KEY_PAVLOK_ENABLED) == true) { + send_stimulus(); + } // fall back to main alarm and resume vibes - // if the user fails to solve within a minute if (s_alarming_for_awareness) { s_alarming_for_awareness = false; } @@ -928,6 +959,10 @@ static void init() { the_time.HourSet = the_time.HourWIP; the_time.MinuteSet = the_time.MinuteWIP; + // initialize pkjs TODO maybe only do in alarm mode? + app_message_register_inbox_received(inbox_received_handler); + app_message_open(64, 64); // TODO tighten sizes + switch (launch_reason()) { case APP_LAUNCH_PHONE: // DEBUG, REMOVE PRIOR TO RELEASE! case APP_LAUNCH_WAKEUP: diff --git a/src/c/main.h b/src/c/main.h index 86646a8..7fd69f3 100644 --- a/src/c/main.h +++ b/src/c/main.h @@ -4,6 +4,7 @@ #define STORAGE_KEY_ALARM_MINUTE 1 #define STORAGE_KEY_ALARMING_FOR_AWARENESS 2 #define STORAGE_KEY_DISABLE_TUTORIAL 3 +#define STORAGE_KEY_PAVLOK_ENABLED 4 typedef enum { diff --git a/src/pkjs/config.js b/src/pkjs/config.js new file mode 100644 index 0000000..e818e7f --- /dev/null +++ b/src/pkjs/config.js @@ -0,0 +1,52 @@ +module.exports = [ + { + "type": "heading", + "defaultValue": "Trigalarm | Settings" + }, + { + "type": "section", + "items": [ + { + "type": "heading", + "size": 3, + "defaultValue": "Pavlok Integration" + }, + { + "id": "pavlok_enable", + "type": "toggle", + "messageKey": "PKJS_PAVLOK_ENABLE", + "defaultValue": false, + "label": "Enabled" + }, + { + "id": "pavlok_api_key", + "type": "input", + "messageKey": "PKJS_PAVLOK_API_KEY", + "label": "API Key" + }, + { + "id": "pavlok_zap_intensity", + "type": "slider", + "messageKey": "PKJS_PAVLOK_ZAP_INTENSITY", + defaultValue: 30, + min: 5, + max: 100, + step: 5, + "label": "Zap Intensity" + }, + { + "id": "pavlok_interval", + "type": "slider", + "messageKey": "PKJS_PAVLOK_INTERVAL", + defaultValue: 5, + min: 1, + max: 60, + "label": "Zap Repeat Interval (Seconds - NOT IMPLEMENTED)" + } + ] + }, + { + "type": "submit", + "defaultValue": "Save Settings" + } +]; diff --git a/src/pkjs/customClay.js b/src/pkjs/customClay.js new file mode 100644 index 0000000..e62f424 --- /dev/null +++ b/src/pkjs/customClay.js @@ -0,0 +1,24 @@ +module.exports = function (minified) { + var clayConfig = this; + var _ = minified._; + var $ = minified.$; + var HTML = minified.HTML; + + function togglePavlokIntegration() { + if (this.get()) { + clayConfig.getItemById('pavlok_api_key').show(); + clayConfig.getItemById('pavlok_zap_intensity').show(); + clayConfig.getItemById('pavlok_interval').show(); + } else { + clayConfig.getItemById('pavlok_api_key').hide(); + clayConfig.getItemById('pavlok_zap_intensity').hide(); + clayConfig.getItemById('pavlok_interval').hide(); + } + } + + clayConfig.on(clayConfig.EVENTS.AFTER_BUILD, function () { + var pavlokIntegrationToggle = clayConfig.getItemById('pavlok_enable'); + togglePavlokIntegration.call(pavlokIntegrationToggle); + pavlokIntegrationToggle.on('change', togglePavlokIntegration); + }); +}; diff --git a/src/pkjs/index.js b/src/pkjs/index.js new file mode 100644 index 0000000..68161a4 --- /dev/null +++ b/src/pkjs/index.js @@ -0,0 +1,35 @@ +var Clay = require('@rebble/clay'); +var clayConfig = require('./config'); +var customClay = require('./customClay'); + +new Clay(clayConfig, customClay); + +Pebble.addEventListener("ready", function () { + Pebble.sendAppMessage({ PKJS_READY: 1 }); +}); + +Pebble.addEventListener("appmessage", function (dict) { + if (dict.payload["PKJS_PAVLOK_ENGAGE"]) { + const cfg = JSON.parse(localStorage.getItem('clay-settings')); + callAPI(cfg.PKJS_PAVLOK_API_KEY, cfg.PKJS_PAVLOK_ZAP_INTENSITY); + } +}); + +function callAPI(apiKey, zap_intensity) { + const data = JSON.stringify({ + stimulus: { + stimulusType: 'zap', + stimulusValue: zap_intensity, + reason: 'Trigalarm' + } + }); + + const xhr = new XMLHttpRequest(); + xhr.withCredentials = true; + + xhr.open('POST', 'https://api.pavlok.com/api/v5/stimulus/send'); + xhr.setRequestHeader('accept', 'application/json'); + xhr.setRequestHeader('content-type', 'application/json'); + xhr.setRequestHeader('Authorization', 'Bearer ' + apiKey); + xhr.send(data); +}