Implement settings (via Clay)

This commit is contained in:
2026-04-09 20:57:46 -04:00
parent 11f2dc0c94
commit fd9ec48511
5 changed files with 72 additions and 27 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
/.clangd /.clangd
/build /build
/.lock* /.lock*
/node_modules
/package-lock.json

View File

@@ -1,24 +1,29 @@
{ {
"author": "RandyTheSilly", "author": "RandyTheSilly",
"dependencies": {}, "dependencies": {
"@rebble/clay": "^1.0.8"
},
"keywords": [ "keywords": [
"jellyfin", "jellyfin",
"plex", "plex",
"sleep" "sleep",
"timeline",
"health"
], ],
"capabilities": [ "capabilities": [
"health" "health",
"configurable"
], ],
"name": "1-more-episode", "name": "1-more-episode",
"pebble": { "pebble": {
"displayName": "1 More Episode", "displayName": "1 More Episode",
"enableMultiJS": true, "enableMultiJS": true,
"messageKeys": [ "messageKeys": [
"JS_READY", "CLAY_API_HOST",
"SLEEP_TIMESTAMP", "CLAY_API_KEY",
"API_IS_PLEX", "CLAY_API_IS_JELLYFIN",
"API_KEY", "PKJS_READY",
"API_HOST" "PKJS_SLEEP_TIMESTAMP"
], ],
"projectType": "native", "projectType": "native",
"resources": { "resources": {

View File

@@ -16,32 +16,28 @@ static TextLayer *s_pin_notice_layer;
// declare time tracking statics // declare time tracking statics
static time_t s_sleep_timestamp; static time_t s_sleep_timestamp;
static void send_to_pkjs() { static void send_sleep_time_to_pkjs() {
DictionaryIterator *out; DictionaryIterator *out;
AppMessageResult result = app_message_outbox_begin(&out); AppMessageResult result = app_message_outbox_begin(&out);
if (result != APP_MSG_OK) { if (result != APP_MSG_OK) {
text_layer_set_text(s_last_episode_layer, "outbox_begin failure"); text_layer_set_text(s_last_episode_layer, "outbox_begin failure");
return; return;
} }
dict_write_uint32(out, MESSAGE_KEY_SLEEP_TIMESTAMP, s_sleep_timestamp); dict_write_uint32(out, MESSAGE_KEY_PKJS_SLEEP_TIMESTAMP, s_sleep_timestamp);
dict_write_uint8(out, MESSAGE_KEY_API_IS_PLEX, 0);
dict_write_cstring(out, MESSAGE_KEY_API_KEY, "");
dict_write_cstring(out, MESSAGE_KEY_API_HOST, "");
result = app_message_outbox_send(); result = app_message_outbox_send();
if (result != APP_MSG_OK) { if (result != APP_MSG_OK) {
text_layer_set_text(s_last_episode_layer, "outbox_send failure "); text_layer_set_text(s_last_episode_layer, "outbox_send failure");
return; return;
} }
} }
static void inbox_received_handler(DictionaryIterator *iter, void *context) { static void inbox_received_handler(DictionaryIterator *iter, void *context) {
Tuple *ready_tuple = dict_find(iter, MESSAGE_KEY_JS_READY); Tuple *ready_tuple = dict_find(iter, MESSAGE_KEY_PKJS_READY);
if (ready_tuple) { if (ready_tuple) {
s_js_ready = true; s_js_ready = true;
} }
APP_LOG(APP_LOG_LEVEL_INFO, "Received JS_READY, calling PKJS..."); APP_LOG(APP_LOG_LEVEL_INFO, "Received JS_READY, calling PKJS...");
send_to_pkjs(); send_sleep_time_to_pkjs();
} }
static void inbox_dropped_handler(AppMessageResult reason, void *context) { static void inbox_dropped_handler(AppMessageResult reason, void *context) {

37
src/pkjs/config.js Normal file
View File

@@ -0,0 +1,37 @@
module.exports = [
{
"type": "heading",
"defaultValue": "1 More Episode | Settings"
},
{
"type": "section",
"items": [
{
"type": "heading",
"defaultValue": "API Credentials"
},
{
"type": "input",
"messageKey": "CLAY_API_HOST",
"label": "API Host",
"description": "Example: https://jellyfin.example.com"
},
{
"type": "input",
"messageKey": "CLAY_API_KEY",
"label": "API Key"
},
{
"type": "toggle",
"messageKey": "CLAY_API_IS_JELLYFIN",
"defaultValue": true,
"label": "Jellyfin Support",
"description": "Toggle off if using Plex. If using Jellyfin, leave on :D"
}
]
},
{
"type": "submit",
"defaultValue": "Save Settings"
}
];

View File

@@ -1,24 +1,29 @@
var Clay = require('@rebble/clay');
var clayConfig = require('./config');
var clay = new Clay(clayConfig);
Pebble.addEventListener("ready", function () { Pebble.addEventListener("ready", function () {
Pebble.sendAppMessage({ JS_READY: 1 }); Pebble.sendAppMessage({ PKJS_READY: 1 });
}); });
Pebble.addEventListener("appmessage", function (dict) { Pebble.addEventListener("appmessage", function (dict) {
if (dict.payload["API_HOST"]) { if (dict.payload["PKJS_SLEEP_TIMESTAMP"]) {
if (dict.payload["API_IS_PLEX"] == 0) { var sleepTimestamp = dict.payload["PKJS_SLEEP_TIMESTAMP"];
callAPI(dict.payload["API_HOST"] + "/System/ActivityLog/Entries", false, dict.payload["API_KEY"]); var cfg = JSON.parse(localStorage.getItem('clay-settings'));
// TODO report last episode as "Configure app settings!" if CLAY_API_HOST or CLAY_API_KEY is empty
if (cfg.CLAY_API_IS_JELLYFIN == true) {
callAPI(cfg.CLAY_API_HOST + "/System/ActivityLog/Entries", cfg.CLAY_API_KEY, true, sleepTimestamp);
} else { } else {
// The official history endpoint doesn't seem to return history w/o Plex Pass. // The official history endpoint doesn't seem to return history w/o Plex Pass.
// If someone with Plex Pass wants to give it a shot, I'd be happy to accept a PR adding this as an option. // If someone with Plex Pass wants to give it a shot, I'd be happy to accept a PR adding this as an option.
// Until then, I'm using Tautulli because it's free. // Until then, I'm using Tautulli because it's free.
//callAPI(dict.payload["API_HOST"] + "/status/sessions/history/all", true, dict.payload["API_KEY"]); //callAPI(cfg.CLAY_API_HOST + "/status/sessions/history/all", cfg.CLAY_API_KEY, false, dict.payload["API_KEY"], sleepTimestamp);
callAPI(dict.payload["API_HOST"] + "/api/v2?apikey=" + dict.payload["API_KEY"] + "&cmd=get_history", true, NULL); callAPI(cfg.CLAY_API_HOST + "/api/v2?apikey=" + cfg.CLAY_API_KEY + "&cmd=get_history", null, false, sleepTimestamp);
} }
} }
}); });
function callAPI(fullURL, isPlex, apiKey) { function callAPI(fullURL, apiKey, isJellyfin, sleepTimestamp) {
console.log("callAPI URL:", fullURL, "isPlex:", isPlex);
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () { xhr.onreadystatechange = function () {
if (xhr.readyState === 4) { if (xhr.readyState === 4) {
@@ -28,7 +33,7 @@ function callAPI(fullURL, isPlex, apiKey) {
xhr.open("GET", fullURL, true); xhr.open("GET", fullURL, true);
xhr.setRequestHeader("Accept", "application/json"); xhr.setRequestHeader("Accept", "application/json");
if (!isPlex) { if (isJellyfin) {
xhr.setRequestHeader("Authorization", 'MediaBrowser Token="' + apiKey + '"'); xhr.setRequestHeader("Authorization", 'MediaBrowser Token="' + apiKey + '"');
} }