From 499f522867d69d0845605f5852f3fe4214948b92 Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Sun, 21 Sep 2025 15:01:57 -0500 Subject: [PATCH 01/11] schedclock: initial app --- apps/schedclock/ChangeLog | 1 + apps/schedclock/README.md | 38 +++++++ apps/schedclock/app-icon.js | 1 + apps/schedclock/app.js | 6 + apps/schedclock/app.png | Bin 0 -> 312 bytes apps/schedclock/lib.js | 51 +++++++++ apps/schedclock/metadata.json | 19 ++++ apps/schedclock/settings.js | 202 ++++++++++++++++++++++++++++++++++ 8 files changed, 318 insertions(+) create mode 100644 apps/schedclock/ChangeLog create mode 100644 apps/schedclock/README.md create mode 100644 apps/schedclock/app-icon.js create mode 100644 apps/schedclock/app.js create mode 100644 apps/schedclock/app.png create mode 100644 apps/schedclock/lib.js create mode 100644 apps/schedclock/metadata.json create mode 100644 apps/schedclock/settings.js diff --git a/apps/schedclock/ChangeLog b/apps/schedclock/ChangeLog new file mode 100644 index 0000000000..5560f00bce --- /dev/null +++ b/apps/schedclock/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/schedclock/README.md b/apps/schedclock/README.md new file mode 100644 index 0000000000..1f0de3e7a8 --- /dev/null +++ b/apps/schedclock/README.md @@ -0,0 +1,38 @@ +# Schedule Clock Faces + +Change clock faces on a schedule. + +For example: a fun clock face for weekends and after work; a detailed clock face for work days. + +TODO: add screenshots + +## Usage + +* Open the `Schedule Clock` app or find it in the `Settings` > `Apps` menu. +* Set `Enable` to `on` +* Select `Add New` to add a new scheduled face change +* Select the `Day`, `Hour`, `Minute`, and what `Clock` to change to +* Select `Save` to save the new (or changed) schedule + +An entry in `Scheduler` will be created to set the clockface based on that schedule. + +If the clockface you selected has been uninstalled, the schedule will still exist but won't do anything. + +## To Uninstall +Before uninstalling this app, clean up any scheduled alarms by setting the `Enable` toggle to `off`. + +If you skip this step, orphaned alarms may cause error logs but won't affect functionality. + +You can also remove the extra `schedclock` alarms manually with the [Scheduler](https://banglejs.com/apps/?id=sched) app. + +## Requests + +Send requests to [thinkpoop](https://github.com/thinkpoop) on GitHub + +## Creator + +[thinkpoop](https://github.com/thinkpoop) + +## Attribution + +App icon: [Schedule](https://icons8.com/icon/E7VlDozxin8k/schedule) by [Icons8](https://icons8.com/) \ No newline at end of file diff --git a/apps/schedclock/app-icon.js b/apps/schedclock/app-icon.js new file mode 100644 index 0000000000..2d4920a690 --- /dev/null +++ b/apps/schedclock/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AAcnACQYEACwv/F5scAAQv5DYceACQzJF/4vZjAAEF4YJFF/4TDF/4v7AAYv/GY4v/F9IANF/YALF/4vjdIYAPF/4v/F/4vvGY4APDpMYAAQv/F/4vdAH4A/AH7XNA64v/F/4vfAH4A/AH7vvF/4v/AAv+AAThnFYYv/F/4vPA")) diff --git a/apps/schedclock/app.js b/apps/schedclock/app.js new file mode 100644 index 0000000000..f5ae19852c --- /dev/null +++ b/apps/schedclock/app.js @@ -0,0 +1,6 @@ +(function () { + + // Load the settings page + eval(require("Storage").read("schedclock.settings.js"))(()=>load()); + +})(); diff --git a/apps/schedclock/app.png b/apps/schedclock/app.png new file mode 100644 index 0000000000000000000000000000000000000000..597732990bb68661fd96ae1257d68862863a1385 GIT binary patch literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9GG!XV7ZFl&wkQ1F$f zi(^Q|oVPa|`3@-vxFz~s(k@uD&S%G+Go350OKeYDq{+oK<@==i)WuJuO?njt1O){} zr)|&B6;IP<7TC?OqQh4Ii`II!33qw#_b-0w<;XC>mElruy(RyLcUBBl%qr#13^EKi z>=|b@r!XXm9+2cfH4?ialE z;xGD^#nZr$z;HsIp=1+dz-+z(4u%w_hA$7}Bi7opJ1`h9EKzRYvtr1)%*e;s;KMK> zte!)Sp^V{@C}Yi1`#aeWufJqyXK`TIkjLQS;v%x{67$?f?ZjU8Q=p(=@O1TaS?83{ F1OT9+X a.appid && a.appid === "schedclock") + .forEach(a => require("sched").setAlarm(a.id, undefined)); + + // If the app is disabled, we're done. + if (!settings.enabled) return; + + // Add a new alarm for each setting item + settings.sched.forEach((item, index) => { + + if (item.hour === undefined) return; + + // Create the new alarm object and save it using a unique ID. + require("sched").setAlarm(`schedclock.${index}`, { + on: true, + appid: "schedclock", + t: (item.hour * 3600000) + (item.minute * 60000), // time in milliseconds since midnight + dow: item.dow, + hidden: true, + rp: {interval: "week"}, + js: `require('schedclock.lib.js').loadFace('${item.face}')`, + }); + }); +}; + diff --git a/apps/schedclock/metadata.json b/apps/schedclock/metadata.json new file mode 100644 index 0000000000..b8d876896b --- /dev/null +++ b/apps/schedclock/metadata.json @@ -0,0 +1,19 @@ +{ "id": "schedclock", + "name": "Schedule Clock Faces", + "shortName":"Schedule Clock", + "version":"0.01", + "author": "thinkpoop", + "description": "Change clock faces on a schedule.", + "icon": "app.png", + "tags": "tool", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "dependencies": {"scheduler":"type"}, + "storage": [ + {"name":"schedclock.app.js","url":"app.js"}, + {"name":"schedclock.settings.js","url":"settings.js"}, + {"name":"schedclock.lib.js","url":"lib.js"}, + {"name":"schedclock.img","url":"app-icon.js","evaluate":true} + ], + "data": [{"name":"schedclock.settings.json"}] +} diff --git a/apps/schedclock/settings.js b/apps/schedclock/settings.js new file mode 100644 index 0000000000..7859a16f75 --- /dev/null +++ b/apps/schedclock/settings.js @@ -0,0 +1,202 @@ +(function(back) { + const SETTINGS_FILE = "schedclock.settings.json"; + const daysOfWeek = require("date_utils").dows(1).concat([/*LANG*/"Every Day", /*LANG*/"Weekdays", /*LANG*/"Weekends"]); + const WORKDAYS = 0b0111110; // 62 - MTWTF + const WEEKEND = 0b1000001; // 65 - SuSa + const EVERY_DAY = 0b1111111; // 127 - SuMTWTFSa + + // Function to load settings + const loadSettings = function() { + let settings = require("Storage").readJSON(SETTINGS_FILE, 1) || {}; + settings.enabled = !!settings.enabled; + if (!Array.isArray(settings.sched)) settings.sched = []; + + // Sort by time + settings.sched.sort((a, b) => { + return (a.hour * 60 + a.minute) - (b.hour * 60 + b.minute); + }); + return settings; + }; + + // Function to save settings + const saveSettings = function(settings) { + require("Storage").writeJSON(SETTINGS_FILE, settings); + // After saving, tell the library to sync the alarms + if (require("Storage").read("schedclock.lib.js")) { + require("schedclock.lib.js").syncAlarms(); + } + }; + + // Get a list of all installed clock faces + const getClockFaces = function() { + return require("Storage").list(/\.info$/).map(file => { + const info = require("Storage").readJSON(file, 1) || {}; + if (info && info.type === "clock" && info.src) { + return { + name: info.name || info.src.replace(".js",""), + sortorder: info.sortorder || 0, + src: info.src + }; + } + }) + .filter(f => f) // Remove any invalid entries + .sort((a, b) => { // Sort by sortorder, then name (from clkshortcuts) + var n = (0 | a.sortorder) - (0 | b.sortorder); + if (n) return n; + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + }); + }; + + // Show the main menu + const showMainMenu = function() { + const settings = loadSettings(); + const clockFaces = getClockFaces(); + const menu = { + "": { "title": /*LANG*/"Scheduled Clock" }, + "< Back": () => back(), + /*LANG*/"Enable": { + value: !!settings.enabled, + format: v => v ? /*LANG*/"On" : /*LANG*/"Off", + onchange: v => { + settings.enabled = v; + saveSettings(settings); + } + }, + }; + + // Add existing schedule items to the menu + settings.sched.forEach((item, index) => { + const faceName = (clockFaces.find(f => f.src === item.face) || {name: /*LANG*/"Unknown"}).name; + const dow = binaryToDow(item.dow); + const dayName = daysOfWeek[dow === undefined ? 7 : dow]; + const timeStr = ("0"+item.hour).slice(-2) + ":" + ("0"+item.minute).slice(-2); + menu[`${dayName} ${timeStr} - ${faceName}`] = () => editScheduleItem(index); + }); + + menu[/*LANG*/"Add New"] = () => editScheduleItem(-1); + + E.showMenu(menu); + }; + + const dowToBinary = function(v) { + // v is the index in daysOfWeek + switch(v) { + case 7: return EVERY_DAY; // Every Day + case 8: return WORKDAYS; // Weekdays + case 9: return WEEKEND; // Weekends + default: + return 1 << v; // Single day (0=Sun, 1=Mon, ..., 6=Sat) + } + } + + const binaryToDow = function(b) { + // b is the bitmask + switch(b) { + case EVERY_DAY: return 7; // Every Day + case WORKDAYS: return 8; // Weekdays + case WEEKEND: return 9; // Weekends + } + for (let i = 0; i < 7; i++) { + if (b === (1 << i)) return i; // Single day (0=Sun, 1=Mon, ..., 6=Sat) + } + return 7; + } + + // Function to edit a schedule item (or add a new one if index is -1) + const editScheduleItem = function(index) { + const settings = loadSettings(); + const clockFaces = getClockFaces(); + const isNew = index === -1; + const defaultFaceSrc = clockFaces.length > 0 ? clockFaces[0].src : ""; + + const currentItem = isNew ? + { hour: 8, minute: 0, face: defaultFaceSrc, dow: EVERY_DAY } : + Object.assign({}, settings.sched[index]); + + // Default odd items to "Every Day" + if (currentItem.dow === undefined) currentItem.dow = EVERY_DAY; + + let dow = binaryToDow(currentItem.dow); + + const menu = { + "": { "title": isNew ? /*LANG*/"Add Schedule" : /*LANG*/"Edit Schedule" }, + "< Back": () => showMainMenu(), + /*LANG*/"Day": { + value: dow, + min: 0, + max: daysOfWeek.length - 1, + format: v => daysOfWeek[v], + onchange: v => { + currentItem.dow = dowToBinary(v); + }, + }, + /*LANG*/"Hour": { + value: currentItem.hour, + min: 0, + max: 23, + onchange: v => { currentItem.hour = v; } + }, + /*LANG*/"Minute": { + value: currentItem.minute, + min: 0, + max: 59, + onchange: v => { currentItem.minute = v; } + }, + /*LANG*/"Clock Face": { + value: Math.max(0, clockFaces.findIndex(f => f.src === currentItem.face)), + min: 0, + max: clockFaces.length - 1, + format: v => (clockFaces[v] && clockFaces[v].name) || /*LANG*/"None", + onchange: v => { + if (clockFaces[v]) currentItem.face = clockFaces[v].src; + } + }, + /*LANG*/"Save": () => { + const validationError = settings.sched.some((item, i) => { + if (!isNew && i === index) return false; // Don't check against self when editing + + const timesMatch = item.hour === currentItem.hour + && item.minute === currentItem.minute; + if (!timesMatch) return false; + + // If times match, check for a day conflict. + return item.dow & currentItem.dow !== 0; + }); + + if (validationError) { + E.showAlert(/*LANG*/"Time conflict", /*LANG*/"An entry for this time already exists.") + .then(()=>E.showMenu(menu)); + return; // Prevent saving + } + + if (isNew) { + settings.sched.push(currentItem); + } else { + settings.sched[index] = currentItem; + } + saveSettings(settings); + showMainMenu(); + } + }; + + if (!isNew) { + menu[/*LANG*/"Delete"] = () => { + E.showPrompt(/*LANG*/"Delete this item?").then(confirm => { + if (confirm) { + settings.sched.splice(index, 1); + saveSettings(settings); + } + showMainMenu(); + }); + }; + } + + E.showMenu(menu); + }; + + Bangle.loadWidgets(); + Bangle.drawWidgets(); + showMainMenu(); +}) From c4b71089dc385cd3cec64626d97ad66b0a144e2a Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Sun, 21 Sep 2025 15:23:15 -0500 Subject: [PATCH 02/11] schedclock: tweak comment; add screenshots --- apps/schedclock/README.md | 8 +++++--- apps/schedclock/lib.js | 2 +- apps/schedclock/screenshot-1.png | Bin 0 -> 7770 bytes apps/schedclock/screenshot-2.png | Bin 0 -> 6888 bytes 4 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 apps/schedclock/screenshot-1.png create mode 100644 apps/schedclock/screenshot-2.png diff --git a/apps/schedclock/README.md b/apps/schedclock/README.md index 1f0de3e7a8..a6d96c1352 100644 --- a/apps/schedclock/README.md +++ b/apps/schedclock/README.md @@ -4,7 +4,7 @@ Change clock faces on a schedule. For example: a fun clock face for weekends and after work; a detailed clock face for work days. -TODO: add screenshots +![Screenshot](screenshot-1.png) ## Usage @@ -14,7 +14,9 @@ TODO: add screenshots * Select the `Day`, `Hour`, `Minute`, and what `Clock` to change to * Select `Save` to save the new (or changed) schedule -An entry in `Scheduler` will be created to set the clockface based on that schedule. +![SaveButton](screenshot-2.png) + +An entry in `Scheduler` will be created for each scheduled clock change. If the clockface you selected has been uninstalled, the schedule will still exist but won't do anything. @@ -23,7 +25,7 @@ Before uninstalling this app, clean up any scheduled alarms by setting the `Enab If you skip this step, orphaned alarms may cause error logs but won't affect functionality. -You can also remove the extra `schedclock` alarms manually with the [Scheduler](https://banglejs.com/apps/?id=sched) app. +You can also remove the extra `schedclock` alarms manually with the [Scheduler](/?id=sched) app. ## Requests diff --git a/apps/schedclock/lib.js b/apps/schedclock/lib.js index b271b20fba..4d854b8dcc 100644 --- a/apps/schedclock/lib.js +++ b/apps/schedclock/lib.js @@ -1,6 +1,6 @@ const SETTINGS_FILE = "schedclock.settings.json"; -// Simple function to be called directly by an alarm to load a specific clock face +// Called directly by an alarm to load a specific clock face exports.loadFace = function(faceSrc) { const settings = require("Storage").readJSON("setting.json", 1) || {}; // Only change the clock if it's different diff --git a/apps/schedclock/screenshot-1.png b/apps/schedclock/screenshot-1.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1111c8f7738eea69495a0b05638acd20149dd9 GIT binary patch literal 7770 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 D*rn{8 literal 0 HcmV?d00001 diff --git a/apps/schedclock/screenshot-2.png b/apps/schedclock/screenshot-2.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0943a978eaaf7350b8302438d23b661f735535 GIT binary patch literal 6888 zcmZ{pcT`hZyT${dC@^9{${+y;C4fj55CXxmAQrIEqy(gibfpE-5JyI;ii9SFQ4l3a zkQ%8`DN>^#gwR6A&;kTP2_g3|^WFP>_pWvSNV3+>S?9dxJ$pa<_dMsxH7ko>#C{b6 zfk3}lUOaaL_$}G}>=6OJNmY|BAkco0<+(GrA_V50qaO~E#XLs%{09*29HZtNeu33B zi-)t1Nl09TxSyiC4DvJR6Z1i%%FPa_iLM8OPUu9Us_RU^P@TP@!ylRSsX)5KwiNh_ zplx){aHGvOy*ANTBuTdaaFEo*^=}>g-u_suwp=3Jm@r`B`^Q30tpcAd;U~616yB&_ zfIpi!g+LgBS+Wt*O9YKt;zy8diDIyNb3{0pg*qUAHl3U#Fi#UyvTwgBF$7-K7>)8P z2>&kO%^brD7jw zvuE#E^m|S26+uUsd0nwG8b1@j!A8Qb-}=?6aqv(-Z#XL&;XUd$hYi)^pRLe{l-j(% zDdm=x2>m&A2hSCLo(5XU6Rs@HNwA@g8umy?`q2d5QkMepcWGLBOB|u3JLq9Wm(=~{$vaIEl=T-tA#x_yXbuTev}uY%1_6wZw`8wp(fn(=b) zHu25K{IKj-!TP~Bk?>B6gGi3>Hq^R<_;Gci;Sxv@JuTe49IC*i$YSvw%{&;GDwvdi zpeJ_1dU7L3TwzC2r$lX(%(50+B7XS0N?i_H0o-23Em z=1d*Ty*if}Q}D9Jg)i`%nIe8Xs@8A-zPm$2xQM{9W!`0dNEc`I!f7;FNxiXgVm6|-ez`Z>^HC1`RjxEfi$zq(t5Xj_Qt^=JgmRJ zv)-{PAH8mzO*lu5$fdoZ4&pgxm}``^tN8lKsGJTP8LQGQqUMlwd(Her0gD?jr(hcU zVCRfL11Z9BL>6b1U0xbH**b6c{4(8MiKa3o(YC@KWek=-9SyRBsPC%YNRiID%;-V(@rA zm=~~T-M$`{@PUgi3QoAoJ0f{55o3H*aw@?(QdAhVlL-2A>TK_+L*O6pYjrG)cbimO zS3EJ>t@A9N{!`Qy)mxlUZ1c}UBY*cd1(^2Ii2WtsapZ=nIZ-i{EjsPuM zKiq(IMlc&^`T|e|e49ph``Vo^x2M^>P`k^m1RQM;dHJoYwuk zP)?G$%GBfM;uv<@J3`VU>!rXo+K|BYR#aV#TI1lQe6@y^^2tN#%jT?L)4F6usiieT z!D|U}xpV6NKBZcPogwt&@FwcYS4{`nGv}4BT27aBPfbL-M49p9%JOY$LaIfDx9i8@ zX!pK|0M}v~8Wm?(pJ@Tq!fWe!ex)lSHypilI2@Q6t~L>=m+TjQ2*`p&n}XbI9-jny zuHgQrT~n@KcDH&;^HWW^O4)9r1fgdZ)$ARKu1w1_oc@&-S#qal#%!Vq&&j}EqwGxT zhNk+gz8j#U4LAzrVadheudp*j0gT6e)7C_L*L~3P%3&lFsi$3qGILuAMhwa zFzQg~Z?bSL`dcFiQ^3w0e-AafuAxYn+yAtv~_O8x5kDvtnSDV zD3ahx!-;jhpUF*?mt3X7C%rRIg*5f_Y}r6?-ygW*drwWIlFta7VA=}Wi-{zZ`yRvz zEcMIcY35EX{c{02mFpM~)rqn*Akkcz3X-q~nve(_!gUjI&-%(q$w`5Mv|ZsHu6}x! zEMN^CW`y17gAK)qF5bCDDT@mvI7P;zKE7suxqmakJS zxQLnP@Y6u)NTBqkLogIC9Q6?>pHE{9MKK6Fe(BEJSYs)}6mPcL&K|9yXD_EV?+s;A zOLy-o$76cQx)&p3?Zj(qmBI?+UwPecI*ERk~vHg2B(f`PyzQcNh7P)?_k_r!V z;-R=xBmHJm$qw_!2*J-Y40os8Sv=NQAeiZ{MSHbOvj}yMdMOGw0fDELaUJpGRnxo( zdGH?jtkDXGRQy8s-XrpIi5=xH?N>~M^|d(GtEas460(f(aT%@FoI^w`0#-(#Vx_T% z!ymy{=1$u<_5MLz*<%5$e&76?w5+a@g(K(hPz@XL%|=lJ@Lqo(Z??wLJEN?bg5ZHA zdMI^lcV)H&xjDX@5vPEJDAw(&>MmI}K9`A{Lah>SRPTu#K=ejOWi^Z1O6Wh8&-od@ zBzirk=R98RdhZ+SOOVv^Tf zM3qxxS9%p`uWQ|~3a??W$tRNrbdtCSw_q;8!~|0dh*DfUr#9p@io3noVYNM?74;DH zd`DZUZ~4!n@mTIFMCt+e*U9^HTMC^zOiUz3_dh(Sc0U#XlxSS zMK-nCG0liy6?2@bWJ`PnqK#%lj|2k90xiO3*xA>g7KtPha4iOy2}Wn6fl)o zon0^4&JneCtvi!JD_CfVkmCxMTh^h8t3ZpDi;e6ra%+gx<=&@Mkj5Z0M(RSVM;<@1jb7f%4EZ#!U|rS$`{}4+9Z>8o2y3 zK7zEYH~iO#sBlM~m(4(TVco3wyj0Xh>7K%ql?6#LJKR2Ezo^8{r$(w%> zj4=1hslN8ia8F9k(_4>HuDD}hduuN=YC+;w98haoiv`zQ+U#XyS;>y67C<8$J4+Wkigq@b3S_-h6f_U4BeuVbjaOrxy|WnGHeu zn7iJ2^jBL+;)SO7gnWm|*Xbn-6QA#?)SSFpJ$VN+?m=i}HPb?q`?5BDL|rMEZ}NI~ zR+e1^S4q^gYWkJim1hK~Be+JcKD&MK>AYUuz@hn+dA)arKmHm?x}~1xEQ>#yH3aCxTawGn0MIm z^i3j99g~Qy!(!L*$&N0%#Szxd^s_S+{n;O>$h=#LOt->tn-rW-4u5M`Ngd4RuR%Rf=99J%-m3Mm(EZbop&Vw*lhr{pr zm67j3w@^bi+kU1~r=Q2kSVg8dpwAMt%DGHT9g#h4CW&G4JO= z<``_FLt{7b*htu1PLqSrsp8p(c3d0Yy&U33wW1yYQWI5*Tdx5qI-=Hgqk z=HSJznzbIXsfNYwpbv}6nh24#kUIN137N#5^T9{aTG3xw`&ps>hMw5y$+S#L{uijy zxZ+vNP`2q&9&I1{xsZDQ$*->QB}j7@>XEY&UX2l8?@?kk2!Z=rRQIM^Bu}`-}`m(8jE6U z7$ZLN6}pF)IlxUl}tmW5*;UG7}Mg0z-Jq0 zjyj;iJ9IP8WL04NUkqZVI_l5_m%vG~KLC5wv$C_@tnvcL!SsnQisrE%FZ1S)3N(vq zhFH4#NjB8zGESJyD=WA{K>Ls$PA=6dsGp!AZ!_0Yz;P|=DZT(sz&?@mI3s&x0+}cp zvfU`Yll z8#FYOHMoififsfi%%b2$1=wuS(%j9f0_#(qa$LCs&1$3Q@9g&v<3GY3DbHCj;KG1H*T1cCF zgajOHhm9^J%|)}YA@pDmtac`PBpq(@L^&p#FCn3%yz0=eS%3O~jC^tF^s|P6eCl?o z-8*v6$~ZdIrLPGIDJmx}f7a{#M%r*Ssts4X#dfY6@5XMhJS1a~_a%o=X~*~M8`%T+R-7IT1a|e(>2OR9Xw!^RHv=@+NhuIH}7=_$Au zIBdDR69oemDKZ)_!)p{DONiN9QY~@h*M_Rw*MHjFm)B8#llH0B0LiwkIk}*LdF*TQ zWZMTqo;4;o6Ar`(nsG&njBxQ6qLVPjXG~;XQrn=9R@I3Ovlbh(f!Pu}Fv*E(qZ-M@ z$ZHgh3xnAx)&htZ{P}j}`+Y#$F5a~oolhsfMRxRIO~8BnB%&%QqDO7a1Rzx3ri$&W zX5*)W(Os0@nNNTLA)vutMk+4WS zwBUmS?8dVE@7~rmA&!|LvbgfP>F@I5-@H|P=pb2bMKA42d_yOga3cJG-hS$_3-@$3 zjk*=afr!G6@hEP+=zqG(eP(*bOkoU|U(imjX+*yaDexq>m*@Fyh5!3fWJXJ>B2-C6 zk-LEWOz(c=_@e+3*7`v)7wb%u*yAX{R$&6cNZ{134Ha1x0d!1etkbIJ*6h}m;4vXD z$`(D0(|pUR*LTQ)FJV~bo2}dsbh7nFIGbW~N90vRL$!-X2^*@7OZfK0AVok>Q2vWV zE-JRluQB8O!EY6g1X}5J!56F*aP$+mk=w?5N3ijthyjx&=2lK4u&_4VaMEjEEfGhP zg_n&E8^Y;n`hgfKB%S3OYKlL1(z21Yw4Vz8b7qaXRU3asz+xpR={Qc~qN_%ytDoau z3T{(7=7ayP1{TX|`e_INdEjo(_$MI|hJgKD?fe;WgB!S{%}-_5um~EGfR|DKw?kv_ zrb93{7%)^b__e6;5LEI%gi!839NvEjVR{mS8%BpJ5-0~G`Gth}yO^64wBWP6=`j~r zkRf2`N|a7!MRRq(38NU^Jtiif%w?ALKL7PTLZlkU;`&8;LuDgwQuwn~LlayTQX*FY zLN1&(6dq#KTmH9X#t1H^{4uF7xjr3TBfsnA^4|Qnm*Y+zJ=OW84mr9A_yRMe7k0X6 zVBrf~;4f5oE1PL%jB&Ciu$;zg=+JBCDGoMNnn-x`yc@e-PL^1QRVF&eEck?VEaEBL$?(fsBHjxA9KDYlZGebCs~Ieps5nS z!5J;avyN}y5sQXVVA-We-R>L9<;CPe1ilzB`B z8pw7sTnTxXtJVG#N`09l8`c0>JK%eJ-`6i~ z^lT^nIgiX%bAe~dkLApf(t*@wBisrKwhZ!2)O)RvTZCaw|FY+bgdN_<*T)Yr<@v-a zU}CEbhA@hD|GpWqI5))z1(&y?hi1FwRT5TX1?EYe)5<}_Hd@u;a=Vy}A&4Bk+ eBD-)32Bk%tUyy6O>j8{_EYDk=EB@W};eP=@yY~?Q literal 0 HcmV?d00001 From bbf19eddb4f25bf7029bb02050ebc849017022b9 Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Sun, 21 Sep 2025 15:41:03 -0500 Subject: [PATCH 03/11] schedclock: aim for translatable texts; fix enabled toggle --- apps/schedclock/README.md | 4 ++-- apps/schedclock/screenshot-1.png | Bin 7770 -> 7859 bytes apps/schedclock/settings.js | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/schedclock/README.md b/apps/schedclock/README.md index a6d96c1352..171d81e674 100644 --- a/apps/schedclock/README.md +++ b/apps/schedclock/README.md @@ -9,7 +9,7 @@ For example: a fun clock face for weekends and after work; a detailed clock face ## Usage * Open the `Schedule Clock` app or find it in the `Settings` > `Apps` menu. -* Set `Enable` to `on` +* Set `Enabled` to checked * Select `Add New` to add a new scheduled face change * Select the `Day`, `Hour`, `Minute`, and what `Clock` to change to * Select `Save` to save the new (or changed) schedule @@ -21,7 +21,7 @@ An entry in `Scheduler` will be created for each scheduled clock change. If the clockface you selected has been uninstalled, the schedule will still exist but won't do anything. ## To Uninstall -Before uninstalling this app, clean up any scheduled alarms by setting the `Enable` toggle to `off`. +Before uninstalling this app, clean up any scheduled alarms by setting the `Enabled` toggle to unchecked. If you skip this step, orphaned alarms may cause error logs but won't affect functionality. diff --git a/apps/schedclock/screenshot-1.png b/apps/schedclock/screenshot-1.png index 1b1111c8f7738eea69495a0b05638acd20149dd9..6a36817b213716b1d3d096e786f75663156166a3 100644 GIT binary patch delta 7500 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 M&K|Ek7Wmiy0F$?H3jhEB delta 7411 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 diff --git a/apps/schedclock/settings.js b/apps/schedclock/settings.js index 7859a16f75..d53c9a573c 100644 --- a/apps/schedclock/settings.js +++ b/apps/schedclock/settings.js @@ -54,11 +54,10 @@ const settings = loadSettings(); const clockFaces = getClockFaces(); const menu = { - "": { "title": /*LANG*/"Scheduled Clock" }, + "": { "title": /*LANG*/"Schedule Clock" }, "< Back": () => back(), - /*LANG*/"Enable": { - value: !!settings.enabled, - format: v => v ? /*LANG*/"On" : /*LANG*/"Off", + /*LANG*/"Enabled": { + value: settings.enabled, onchange: v => { settings.enabled = v; saveSettings(settings); From e9ebe194609334ce1c11cc6238c0af9c214903c7 Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Sun, 21 Sep 2025 15:44:04 -0500 Subject: [PATCH 04/11] schedclock: remove unused variable in lib.js --- apps/schedclock/lib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/schedclock/lib.js b/apps/schedclock/lib.js index 4d854b8dcc..b77cc8fa2b 100644 --- a/apps/schedclock/lib.js +++ b/apps/schedclock/lib.js @@ -23,7 +23,7 @@ exports.syncAlarms = function() { const settings = require("Storage").readJSON(SETTINGS_FILE, 1) || []; // Remove all existing alarms from the scheduler library - const alarms = require("sched") + require("sched") .getAlarms() .filter(a => a.appid && a.appid === "schedclock") .forEach(a => require("sched").setAlarm(a.id, undefined)); From 152fa67586f3ad70a4dcc36882d96952e79b8a0f Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Sun, 21 Sep 2025 17:45:21 -0500 Subject: [PATCH 05/11] schedclock: some code clean-up and comments --- apps/schedclock/lib.js | 26 +++++---- apps/schedclock/settings.js | 105 ++++++++++++++++++++++++++---------- 2 files changed, 93 insertions(+), 38 deletions(-) diff --git a/apps/schedclock/lib.js b/apps/schedclock/lib.js index b77cc8fa2b..f4fbfdce7b 100644 --- a/apps/schedclock/lib.js +++ b/apps/schedclock/lib.js @@ -1,11 +1,16 @@ const SETTINGS_FILE = "schedclock.settings.json"; +const APP_ID = "schedclock"; -// Called directly by an alarm to load a specific clock face +/** + * Called directly by an alarm to load a specific clock face + * @param {string} faceSrc - Source file of the clock face to load (e.g. "myclock.js") + **/ exports.loadFace = function(faceSrc) { const settings = require("Storage").readJSON("setting.json", 1) || {}; // Only change the clock if it's different if (faceSrc && settings.clock !== faceSrc) { const face = require("Storage").read(faceSrc); + // If the face doesn't exist, do nothing (but log it) if (!face) { console.log("schedclock: Invalid clock face", faceSrc); return; @@ -17,16 +22,19 @@ exports.loadFace = function(faceSrc) { } }; -// Function to sync all alarms in the scheduler with the settings file. -// This is the new core logic for the app. +/** + * Function to sync all alarms in the scheduler with the settings file. + * Called every time settings are changed; maybe a bit excessive, but keeps things simple. + **/ exports.syncAlarms = function() { + const Sched = require("sched"); const settings = require("Storage").readJSON(SETTINGS_FILE, 1) || []; // Remove all existing alarms from the scheduler library - require("sched") + Sched .getAlarms() - .filter(a => a.appid && a.appid === "schedclock") - .forEach(a => require("sched").setAlarm(a.id, undefined)); + .filter(a => a.appid && a.appid === APP_ID) + .forEach(a => Sched.setAlarm(a.id, undefined)); // If the app is disabled, we're done. if (!settings.enabled) return; @@ -37,14 +45,14 @@ exports.syncAlarms = function() { if (item.hour === undefined) return; // Create the new alarm object and save it using a unique ID. - require("sched").setAlarm(`schedclock.${index}`, { + Sched.setAlarm(`${APP_ID}.${index}`, { on: true, - appid: "schedclock", + appid: APP_ID, t: (item.hour * 3600000) + (item.minute * 60000), // time in milliseconds since midnight dow: item.dow, hidden: true, rp: {interval: "week"}, - js: `require('schedclock.lib.js').loadFace('${item.face}')`, + js: `require('${APP_ID}.lib.js').loadFace('${item.face}')`, }); }); }; diff --git a/apps/schedclock/settings.js b/apps/schedclock/settings.js index d53c9a573c..50c3212789 100644 --- a/apps/schedclock/settings.js +++ b/apps/schedclock/settings.js @@ -1,11 +1,32 @@ (function(back) { + /** + * @typedef ScheduleItemType - Individual Schedule Item + * @member {number} hour - Hour (0-23) + * @member {number} minute - Minute (0-59) + * @member {string} face - Clock face source file (e.g. "myclock.js") + * @member {number} dow - Bitmask for days of week [see Sched documentation] + * + * @typedef SettingsType - Overall Settings File/Object + * @member {boolean} enabled - Whether this app is enabled + * @member {Array} sched - Array of schedule items + * + */ + const SETTINGS_FILE = "schedclock.settings.json"; const daysOfWeek = require("date_utils").dows(1).concat([/*LANG*/"Every Day", /*LANG*/"Weekdays", /*LANG*/"Weekends"]); - const WORKDAYS = 0b0111110; // 62 - MTWTF - const WEEKEND = 0b1000001; // 65 - SuSa - const EVERY_DAY = 0b1111111; // 127 - SuMTWTFSa - - // Function to load settings + // Bitmasks for special day selection for sched.json + const BIN_WORKDAYS = 0b0111110; // 62 - MTWTF + const BIN_WEEKEND = 0b1000001; // 65 - SuSa + const BIN_EVERY_DAY = 0b1111111; // 127 - SuMTWTFSa + // Indexes in daysOfWeek for special day selection + const IND_EVERY_DAY = 7; + const IND_WORKDAYS = 8; + const IND_WEEKEND = 9; + + /** + * Function to load settings + * @returns {SettingsType} settings object + */ const loadSettings = function() { let settings = require("Storage").readJSON(SETTINGS_FILE, 1) || {}; settings.enabled = !!settings.enabled; @@ -18,16 +39,22 @@ return settings; }; - // Function to save settings + /** + * Function to save settings + * @param {SettingsType} settings + */ const saveSettings = function(settings) { require("Storage").writeJSON(SETTINGS_FILE, settings); // After saving, tell the library to sync the alarms if (require("Storage").read("schedclock.lib.js")) { require("schedclock.lib.js").syncAlarms(); - } + }; }; - // Get a list of all installed clock faces + /** + * Get a list of all installed clock faces + * @returns {Array<{name:string,sortorder:number,src:string}>} array of clock face info + **/ const getClockFaces = function() { return require("Storage").list(/\.info$/).map(file => { const info = require("Storage").readJSON(file, 1) || {}; @@ -49,7 +76,9 @@ }); }; - // Show the main menu + /** + * Show the main menu + */ const showMainMenu = function() { const settings = loadSettings(); const clockFaces = getClockFaces(); @@ -69,7 +98,7 @@ settings.sched.forEach((item, index) => { const faceName = (clockFaces.find(f => f.src === item.face) || {name: /*LANG*/"Unknown"}).name; const dow = binaryToDow(item.dow); - const dayName = daysOfWeek[dow === undefined ? 7 : dow]; + const dayName = daysOfWeek[dow === undefined ? IND_EVERY_DAY : dow]; const timeStr = ("0"+item.hour).slice(-2) + ":" + ("0"+item.minute).slice(-2); menu[`${dayName} ${timeStr} - ${faceName}`] = () => editScheduleItem(index); }); @@ -79,31 +108,44 @@ E.showMenu(menu); }; - const dowToBinary = function(v) { - // v is the index in daysOfWeek - switch(v) { - case 7: return EVERY_DAY; // Every Day - case 8: return WORKDAYS; // Weekdays - case 9: return WEEKEND; // Weekends + /** + * Get the bitmask for a day of week selection from an index in daysOfWeek + * @param {number} index index in daysOfWeek + * @returns bitmask for day of week + */ + const dowToBinary = function(index) { + switch(index) { + case IND_EVERY_DAY: return BIN_EVERY_DAY; + case IND_WORKDAYS: return BIN_WORKDAYS; + case IND_WEEKEND: return BIN_WEEKEND; default: - return 1 << v; // Single day (0=Sun, 1=Mon, ..., 6=Sat) + return 1 << index; // Single day (0=Sun, 1=Mon, ..., 6=Sat) } } + /** + * Get the index in daysOfWeek from a binary day-of-week bitmask + * @param {number} b binary number for day of week + * @returns index in daysOfWeek + */ const binaryToDow = function(b) { - // b is the bitmask switch(b) { - case EVERY_DAY: return 7; // Every Day - case WORKDAYS: return 8; // Weekdays - case WEEKEND: return 9; // Weekends + case BIN_EVERY_DAY: return IND_EVERY_DAY; + case BIN_WORKDAYS: return IND_WORKDAYS; + case BIN_WEEKEND: return IND_WEEKEND; } + // Check each single day (0=Sun, 1=Mon, ..., 6=Sat) for (let i = 0; i < 7; i++) { - if (b === (1 << i)) return i; // Single day (0=Sun, 1=Mon, ..., 6=Sat) + if (b === (1 << i)) return i; } - return 7; + // Bitmask was something we don't handle yet, default to everyday for now + return IND_EVERY_DAY; } - // Function to edit a schedule item (or add a new one if index is -1) + /** + * Function to edit a schedule item (or add a new one if index is -1) + * @param {number} index index of item to edit, or -1 to add a new item + */ const editScheduleItem = function(index) { const settings = loadSettings(); const clockFaces = getClockFaces(); @@ -111,11 +153,11 @@ const defaultFaceSrc = clockFaces.length > 0 ? clockFaces[0].src : ""; const currentItem = isNew ? - { hour: 8, minute: 0, face: defaultFaceSrc, dow: EVERY_DAY } : + { hour: 8, minute: 0, face: defaultFaceSrc, dow: BIN_EVERY_DAY } : Object.assign({}, settings.sched[index]); // Default odd items to "Every Day" - if (currentItem.dow === undefined) currentItem.dow = EVERY_DAY; + if (currentItem.dow === undefined) currentItem.dow = BIN_EVERY_DAY; let dow = binaryToDow(currentItem.dow); @@ -161,12 +203,17 @@ if (!timesMatch) return false; // If times match, check for a day conflict. - return item.dow & currentItem.dow !== 0; + return (item.dow & currentItem.dow) !== 0; }); if (validationError) { - E.showAlert(/*LANG*/"Time conflict", /*LANG*/"An entry for this time already exists.") - .then(()=>E.showMenu(menu)); + E.showAlert( + /*LANG*/"Time conflict", + /*LANG*/"An entry for this time already exists." + ).then( + // Note: not sure if this is the best way to return to the menu + ()=>E.showMenu(menu) + ); return; // Prevent saving } From cc091118880487d33f1689005f99e4e129220e3b Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Sun, 21 Sep 2025 17:55:20 -0500 Subject: [PATCH 06/11] schedclock: remove extra semicolon --- apps/schedclock/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/schedclock/settings.js b/apps/schedclock/settings.js index 50c3212789..95fc86f577 100644 --- a/apps/schedclock/settings.js +++ b/apps/schedclock/settings.js @@ -48,7 +48,7 @@ // After saving, tell the library to sync the alarms if (require("Storage").read("schedclock.lib.js")) { require("schedclock.lib.js").syncAlarms(); - }; + } }; /** From 6e4ce3947e854caebabe7ad17a942cad83e87a5b Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:00:16 -0500 Subject: [PATCH 07/11] schedclock: handle firstDayOfWeek; set last day on fired alarms; display 12h format --- apps/schedclock/lib.js | 36 ++++++++++++++++++++++++++------ apps/schedclock/settings.js | 41 +++++++++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/apps/schedclock/lib.js b/apps/schedclock/lib.js index f4fbfdce7b..54e4dde37d 100644 --- a/apps/schedclock/lib.js +++ b/apps/schedclock/lib.js @@ -5,7 +5,7 @@ const APP_ID = "schedclock"; * Called directly by an alarm to load a specific clock face * @param {string} faceSrc - Source file of the clock face to load (e.g. "myclock.js") **/ -exports.loadFace = function(faceSrc) { +const setClock = function(faceSrc) { const settings = require("Storage").readJSON("setting.json", 1) || {}; // Only change the clock if it's different if (faceSrc && settings.clock !== faceSrc) { @@ -22,6 +22,23 @@ exports.loadFace = function(faceSrc) { } }; +/** + * Handle alarms and resetting them + * @param {number} index Index of the alarm that went off + * @param {string} clock Clockface + */ +exports.onAlarm = function(index, clock) { + const date = new Date(); + const Sched = require("sched"); + const alarm = Sched.getAlarm(`${APP_ID}.${index}`); + alarm.last = date.getDate(); // prevent second run on the same day + // -- Don't think alarm.date is required, but just in case + //date.setDate(alarm.last + 1); + //alarm.date = date.toLocalISOString().slice(0,10); + Sched.setAlarm(alarm.id, alarm); + setClock(clock); +}; + /** * Function to sync all alarms in the scheduler with the settings file. * Called every time settings are changed; maybe a bit excessive, but keeps things simple. @@ -39,21 +56,28 @@ exports.syncAlarms = function() { // If the app is disabled, we're done. if (!settings.enabled) return; + const time = new Date(); + const currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); + // Add a new alarm for each setting item settings.sched.forEach((item, index) => { if (item.hour === undefined) return; + const schedTime = (item.hour * 3600000) + (item.minute * 60000); + const today = time.getDate(); + const tomorrow = today + 1; + // Create the new alarm object and save it using a unique ID. Sched.setAlarm(`${APP_ID}.${index}`, { + t: schedTime, // time in milliseconds since midnight on: true, - appid: APP_ID, - t: (item.hour * 3600000) + (item.minute * 60000), // time in milliseconds since midnight + rp: true, + last: (schedTime > currentTime) ? today : tomorrow, dow: item.dow, hidden: true, - rp: {interval: "week"}, - js: `require('${APP_ID}.lib.js').loadFace('${item.face}')`, + appid: APP_ID, + js: `require('${APP_ID}.lib.js').onAlarm(${index},'${item.face}')`, }); }); }; - diff --git a/apps/schedclock/settings.js b/apps/schedclock/settings.js index 95fc86f577..00a3e472a2 100644 --- a/apps/schedclock/settings.js +++ b/apps/schedclock/settings.js @@ -13,7 +13,6 @@ */ const SETTINGS_FILE = "schedclock.settings.json"; - const daysOfWeek = require("date_utils").dows(1).concat([/*LANG*/"Every Day", /*LANG*/"Weekdays", /*LANG*/"Weekends"]); // Bitmasks for special day selection for sched.json const BIN_WORKDAYS = 0b0111110; // 62 - MTWTF const BIN_WEEKEND = 0b1000001; // 65 - SuSa @@ -23,6 +22,16 @@ const IND_WORKDAYS = 8; const IND_WEEKEND = 9; + const daysOfWeek = (function() { + const firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0; + const daysOfWeek = require("date_utils").dows(1); + if (!firstDayOfWeek) { + // Move Sunday from end to start of week + daysOfWeek.unshift(daysOfWeek.splice(-1, 1)[0]); + } + return daysOfWeek.concat([/*LANG*/"Every Day", /*LANG*/"Weekdays", /*LANG*/"Weekends"]); + })(); + /** * Function to load settings * @returns {SettingsType} settings object @@ -99,7 +108,7 @@ const faceName = (clockFaces.find(f => f.src === item.face) || {name: /*LANG*/"Unknown"}).name; const dow = binaryToDow(item.dow); const dayName = daysOfWeek[dow === undefined ? IND_EVERY_DAY : dow]; - const timeStr = ("0"+item.hour).slice(-2) + ":" + ("0"+item.minute).slice(-2); + const timeStr = require("locale").time(new Date(1999, 1, 1, item.hour, item.minute, 0),1) menu[`${dayName} ${timeStr} - ${faceName}`] = () => editScheduleItem(index); }); @@ -119,9 +128,9 @@ case IND_WORKDAYS: return BIN_WORKDAYS; case IND_WEEKEND: return BIN_WEEKEND; default: - return 1 << index; // Single day (0=Sun, 1=Mon, ..., 6=Sat) + return 1 << (index + 1); // Single day (0=Sun, 1=Mon, ..., 6=Sat) } - } + }; /** * Get the index in daysOfWeek from a binary day-of-week bitmask @@ -133,14 +142,17 @@ case BIN_EVERY_DAY: return IND_EVERY_DAY; case BIN_WORKDAYS: return IND_WORKDAYS; case BIN_WEEKEND: return IND_WEEKEND; - } - // Check each single day (0=Sun, 1=Mon, ..., 6=Sat) - for (let i = 0; i < 7; i++) { - if (b === (1 << i)) return i; + case 1: return 0; + case 2: return 1; + case 4: return 2; + case 8: return 3; + case 16: return 4; + case 32: return 5; + case 64: return 6; } // Bitmask was something we don't handle yet, default to everyday for now return IND_EVERY_DAY; - } + }; /** * Function to edit a schedule item (or add a new one if index is -1) @@ -177,6 +189,17 @@ value: currentItem.hour, min: 0, max: 23, + format: v => { + // Format as 12h time if user has that set + const meridean = require("locale").meridian(new Date(1999, 1, 1, v, 0, 0),1); + if (meridean) { + return (v>12) + ? v-12 + meridean + : ((v===0)?12:v) + meridean; + } else { + return v; + } + }, onchange: v => { currentItem.hour = v; } }, /*LANG*/"Minute": { From 399aa87fd9005f4e19c83c9e6cc5314f6445c840 Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Wed, 24 Sep 2025 01:40:55 -0500 Subject: [PATCH 08/11] schedclock: use dow correctly; fix dowToBitmask; various cleanup --- apps/schedclock/settings.js | 64 +++++++++++++------------------------ 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/apps/schedclock/settings.js b/apps/schedclock/settings.js index 00a3e472a2..021380bd4e 100644 --- a/apps/schedclock/settings.js +++ b/apps/schedclock/settings.js @@ -1,15 +1,14 @@ (function(back) { /** - * @typedef ScheduleItemType - Individual Schedule Item - * @member {number} hour - Hour (0-23) - * @member {number} minute - Minute (0-59) - * @member {string} face - Clock face source file (e.g. "myclock.js") - * @member {number} dow - Bitmask for days of week [see Sched documentation] + * @typedef {Object} ScheduleItemType - Individual Schedule Item + * @property {number} hour - Hour (0-23) + * @property {number} minute - Minute (0-59) + * @property {string} face - Clock face source file (e.g. "myclock.js") + * @property {number} dow - Bitmask for days of week [see Sched documentation] * - * @typedef SettingsType - Overall Settings File/Object - * @member {boolean} enabled - Whether this app is enabled - * @member {Array} sched - Array of schedule items - * + * @typedef {Object} SettingsType - Overall Settings File/Object + * @property {boolean} enabled - Whether this app is enabled + * @property {Array} sched - Array of schedule items */ const SETTINGS_FILE = "schedclock.settings.json"; @@ -22,22 +21,15 @@ const IND_WORKDAYS = 8; const IND_WEEKEND = 9; - const daysOfWeek = (function() { - const firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0; - const daysOfWeek = require("date_utils").dows(1); - if (!firstDayOfWeek) { - // Move Sunday from end to start of week - daysOfWeek.unshift(daysOfWeek.splice(-1, 1)[0]); - } - return daysOfWeek.concat([/*LANG*/"Every Day", /*LANG*/"Weekdays", /*LANG*/"Weekends"]); - })(); + // dows(0) = days of week starting at Sunday + const daysOfWeek = require("date_utils").dows(0).concat([/*LANG*/"Every Day", /*LANG*/"Weekdays", /*LANG*/"Weekends"]); /** * Function to load settings * @returns {SettingsType} settings object */ const loadSettings = function() { - let settings = require("Storage").readJSON(SETTINGS_FILE, 1) || {}; + const settings = require("Storage").readJSON(SETTINGS_FILE, 1) || {}; settings.enabled = !!settings.enabled; if (!Array.isArray(settings.sched)) settings.sched = []; @@ -54,10 +46,7 @@ */ const saveSettings = function(settings) { require("Storage").writeJSON(SETTINGS_FILE, settings); - // After saving, tell the library to sync the alarms - if (require("Storage").read("schedclock.lib.js")) { - require("schedclock.lib.js").syncAlarms(); - } + require("schedclock.lib.js").syncAlarms(); }; /** @@ -106,7 +95,7 @@ // Add existing schedule items to the menu settings.sched.forEach((item, index) => { const faceName = (clockFaces.find(f => f.src === item.face) || {name: /*LANG*/"Unknown"}).name; - const dow = binaryToDow(item.dow); + const dow = bitmaskToDowIndex(item.dow); const dayName = daysOfWeek[dow === undefined ? IND_EVERY_DAY : dow]; const timeStr = require("locale").time(new Date(1999, 1, 1, item.hour, item.minute, 0),1) menu[`${dayName} ${timeStr} - ${faceName}`] = () => editScheduleItem(index); @@ -122,13 +111,13 @@ * @param {number} index index in daysOfWeek * @returns bitmask for day of week */ - const dowToBinary = function(index) { + const dowIndexToBitmask = function(index) { switch(index) { case IND_EVERY_DAY: return BIN_EVERY_DAY; case IND_WORKDAYS: return BIN_WORKDAYS; case IND_WEEKEND: return BIN_WEEKEND; default: - return 1 << (index + 1); // Single day (0=Sun, 1=Mon, ..., 6=Sat) + return 1 << index; // Bitmask: Sun=1, Mon=2, Tue=4, Wed=8, Thu=16, Fri=32, Sat=64 } }; @@ -137,11 +126,8 @@ * @param {number} b binary number for day of week * @returns index in daysOfWeek */ - const binaryToDow = function(b) { + const bitmaskToDowIndex = function(b) { switch(b) { - case BIN_EVERY_DAY: return IND_EVERY_DAY; - case BIN_WORKDAYS: return IND_WORKDAYS; - case BIN_WEEKEND: return IND_WEEKEND; case 1: return 0; case 2: return 1; case 4: return 2; @@ -149,9 +135,11 @@ case 16: return 4; case 32: return 5; case 64: return 6; + case BIN_WORKDAYS: return IND_WORKDAYS; + case BIN_WEEKEND: return IND_WEEKEND; + case BIN_EVERY_DAY: + default: return IND_EVERY_DAY; } - // Bitmask was something we don't handle yet, default to everyday for now - return IND_EVERY_DAY; }; /** @@ -171,7 +159,7 @@ // Default odd items to "Every Day" if (currentItem.dow === undefined) currentItem.dow = BIN_EVERY_DAY; - let dow = binaryToDow(currentItem.dow); + let dow = bitmaskToDowIndex(currentItem.dow); const menu = { "": { "title": isNew ? /*LANG*/"Add Schedule" : /*LANG*/"Edit Schedule" }, @@ -182,7 +170,7 @@ max: daysOfWeek.length - 1, format: v => daysOfWeek[v], onchange: v => { - currentItem.dow = dowToBinary(v); + currentItem.dow = dowIndexToBitmask(v); }, }, /*LANG*/"Hour": { @@ -192,13 +180,7 @@ format: v => { // Format as 12h time if user has that set const meridean = require("locale").meridian(new Date(1999, 1, 1, v, 0, 0),1); - if (meridean) { - return (v>12) - ? v-12 + meridean - : ((v===0)?12:v) + meridean; - } else { - return v; - } + return (!meridean) ? v : (v%12||12) + meridean; }, onchange: v => { currentItem.hour = v; } }, From bbf50fa5228e376654a93a91497a789bcbeb7976 Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Wed, 24 Sep 2025 10:41:01 -0500 Subject: [PATCH 09/11] schedclock: fix last to be yesterday/today; not tomorrow --- apps/schedclock/lib.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/schedclock/lib.js b/apps/schedclock/lib.js index 54e4dde37d..ee92b3a589 100644 --- a/apps/schedclock/lib.js +++ b/apps/schedclock/lib.js @@ -66,14 +66,14 @@ exports.syncAlarms = function() { const schedTime = (item.hour * 3600000) + (item.minute * 60000); const today = time.getDate(); - const tomorrow = today + 1; + const yesterday = today - 1; // Create the new alarm object and save it using a unique ID. Sched.setAlarm(`${APP_ID}.${index}`, { t: schedTime, // time in milliseconds since midnight on: true, rp: true, - last: (schedTime > currentTime) ? today : tomorrow, + last: (schedTime > currentTime) ? yesterday : today, dow: item.dow, hidden: true, appid: APP_ID, From 092dd0c99dfd3effccd1ebc564cb2fc1c80ed192 Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Fri, 26 Sep 2025 22:39:05 -0500 Subject: [PATCH 10/11] schedclock: update app icon size; update github username; fix dup time alert --- apps/schedclock/README.md | 6 ++++-- apps/schedclock/app-icon.js | 2 +- apps/schedclock/lib.js | 25 +++++++++++++------------ apps/schedclock/metadata.json | 2 +- apps/schedclock/settings.js | 5 ++--- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/apps/schedclock/README.md b/apps/schedclock/README.md index 171d81e674..b68cf658f4 100644 --- a/apps/schedclock/README.md +++ b/apps/schedclock/README.md @@ -29,11 +29,13 @@ You can also remove the extra `schedclock` alarms manually with the [Scheduler]( ## Requests -Send requests to [thinkpoop](https://github.com/thinkpoop) on GitHub +Send requests to [kidneyhex](https://github.com/kidneyhex) on GitHub. + +Make whatever changes you'd like. ## Creator -[thinkpoop](https://github.com/thinkpoop) +[kidneyhex](https://github.com/kidneyhex) ## Attribution diff --git a/apps/schedclock/app-icon.js b/apps/schedclock/app-icon.js index 2d4920a690..eaa3de0ce8 100644 --- a/apps/schedclock/app-icon.js +++ b/apps/schedclock/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwxH+AAcnACQYEACwv/F5scAAQv5DYceACQzJF/4vZjAAEF4YJFF/4TDF/4v7AAYv/GY4v/F9IANF/YALF/4vjdIYAPF/4v/F/4vvGY4APDpMYAAQv/F/4vdAH4A/AH7XNA64v/F/4vfAH4A/AH7vvF/4v/AAv+AAThnFYYv/F/4vPA")) +require("heatshrink").decompress(atob("mEw4kA///221nnnplr6+9jnn5Hhtlz6Wc2Wcy218vgllrimls/h0vgMf4A/ACcL3YAFC7dEC5UGswAFBQUEolACxG7sslqoADktr2AvM3dikQAFt+3g8NonQu4vJCwslt/nu84omDu9wC5Unu4XhDA4XFqQXJMIwXFu9yC7d5xGXC6l3mYWCC5SnGmYAFC5/pxAAFC5/kogAFC5/hC5m/sQXFkQXO91lktVAAclt1holBiMUC5FE01mAAukCYNIu+EC5NO/4AE/wRCpEzC5UW9wAE8xCCF5hUBAAoRCL5gAQC5AmCARAXLKwQCIC5iGBARAvjL66P/R+kAghfNoAWGC4QAMC8AA/AH4ALA==")) diff --git a/apps/schedclock/lib.js b/apps/schedclock/lib.js index ee92b3a589..34e967aeb3 100644 --- a/apps/schedclock/lib.js +++ b/apps/schedclock/lib.js @@ -32,9 +32,6 @@ exports.onAlarm = function(index, clock) { const Sched = require("sched"); const alarm = Sched.getAlarm(`${APP_ID}.${index}`); alarm.last = date.getDate(); // prevent second run on the same day - // -- Don't think alarm.date is required, but just in case - //date.setDate(alarm.last + 1); - //alarm.date = date.toLocalISOString().slice(0,10); Sched.setAlarm(alarm.id, alarm); setClock(clock); }; @@ -56,24 +53,28 @@ exports.syncAlarms = function() { // If the app is disabled, we're done. if (!settings.enabled) return; - const time = new Date(); - const currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); - + // Alarms need "last" set to let sched know they've already ran for the day + // So if an alarm is for before "now", set last to yesterday so it still triggers today + // else set last to today. + const currentDate = new Date(); + const currentTime = (currentDate.getHours()*3600000)+(currentDate.getMinutes()*60000)+(currentDate.getSeconds()*1000); + const dayOfMonthToday = currentDate.getDate(); + const dayOfMonthYesterday = dayOfMonthToday - 1; + // Add a new alarm for each setting item settings.sched.forEach((item, index) => { - if (item.hour === undefined) return; + // Skip invalid records + if (item.hour === undefined || item.minute === undefined) return; - const schedTime = (item.hour * 3600000) + (item.minute * 60000); - const today = time.getDate(); - const yesterday = today - 1; + const scheduledTime = (item.hour * 3600000) + (item.minute * 60000); // Create the new alarm object and save it using a unique ID. Sched.setAlarm(`${APP_ID}.${index}`, { - t: schedTime, // time in milliseconds since midnight + t: scheduledTime, // time in milliseconds since midnight on: true, rp: true, - last: (schedTime > currentTime) ? yesterday : today, + last: (scheduledTime > currentTime) ? dayOfMonthYesterday : dayOfMonthToday, dow: item.dow, hidden: true, appid: APP_ID, diff --git a/apps/schedclock/metadata.json b/apps/schedclock/metadata.json index b8d876896b..5b364541da 100644 --- a/apps/schedclock/metadata.json +++ b/apps/schedclock/metadata.json @@ -2,7 +2,7 @@ "name": "Schedule Clock Faces", "shortName":"Schedule Clock", "version":"0.01", - "author": "thinkpoop", + "author": "kidneyhex", "description": "Change clock faces on a schedule.", "icon": "app.png", "tags": "tool", diff --git a/apps/schedclock/settings.js b/apps/schedclock/settings.js index 021380bd4e..7ec6c2e6fb 100644 --- a/apps/schedclock/settings.js +++ b/apps/schedclock/settings.js @@ -213,10 +213,9 @@ if (validationError) { E.showAlert( - /*LANG*/"Time conflict", - /*LANG*/"An entry for this time already exists." + /*LANG*/"An entry for this time already exists.", + /*LANG*/"Time conflict" ).then( - // Note: not sure if this is the best way to return to the menu ()=>E.showMenu(menu) ); return; // Prevent saving From 2f868f07d3dbb6f9e73c40abd56a707a06ca235e Mon Sep 17 00:00:00 2001 From: Logan B <3870583+thinkpoop@users.noreply.github.com> Date: Sat, 27 Sep 2025 11:07:25 -0500 Subject: [PATCH 11/11] schedclock: shorten short name; rename screenshots; remove widgets from screenshots --- apps/schedclock/README.md | 10 ++-------- apps/schedclock/metadata.json | 6 +++++- apps/schedclock/screenshot-1.png | Bin 7859 -> 0 bytes apps/schedclock/screenshot-2.png | Bin 6888 -> 0 bytes apps/schedclock/screenshot1.png | Bin 0 -> 1781 bytes apps/schedclock/screenshot2.png | Bin 0 -> 1476 bytes 6 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 apps/schedclock/screenshot-1.png delete mode 100644 apps/schedclock/screenshot-2.png create mode 100644 apps/schedclock/screenshot1.png create mode 100644 apps/schedclock/screenshot2.png diff --git a/apps/schedclock/README.md b/apps/schedclock/README.md index b68cf658f4..aef023ec55 100644 --- a/apps/schedclock/README.md +++ b/apps/schedclock/README.md @@ -4,7 +4,7 @@ Change clock faces on a schedule. For example: a fun clock face for weekends and after work; a detailed clock face for work days. -![Screenshot](screenshot-1.png) +![Screenshot](screenshot1.png) ## Usage @@ -14,7 +14,7 @@ For example: a fun clock face for weekends and after work; a detailed clock face * Select the `Day`, `Hour`, `Minute`, and what `Clock` to change to * Select `Save` to save the new (or changed) schedule -![SaveButton](screenshot-2.png) +![SaveButton](screenshot2.png) An entry in `Scheduler` will be created for each scheduled clock change. @@ -27,12 +27,6 @@ If you skip this step, orphaned alarms may cause error logs but won't affect fun You can also remove the extra `schedclock` alarms manually with the [Scheduler](/?id=sched) app. -## Requests - -Send requests to [kidneyhex](https://github.com/kidneyhex) on GitHub. - -Make whatever changes you'd like. - ## Creator [kidneyhex](https://github.com/kidneyhex) diff --git a/apps/schedclock/metadata.json b/apps/schedclock/metadata.json index 5b364541da..284483bf7c 100644 --- a/apps/schedclock/metadata.json +++ b/apps/schedclock/metadata.json @@ -1,10 +1,14 @@ { "id": "schedclock", "name": "Schedule Clock Faces", - "shortName":"Schedule Clock", + "shortName":"Sched Clock", "version":"0.01", "author": "kidneyhex", "description": "Change clock faces on a schedule.", "icon": "app.png", + "screenshots": [ + {"url":"screenshot1.png"}, + {"url":"screenshot2.png"} + ], "tags": "tool", "supports" : ["BANGLEJS2"], "readme": "README.md", diff --git a/apps/schedclock/screenshot-1.png b/apps/schedclock/screenshot-1.png deleted file mode 100644 index 6a36817b213716b1d3d096e786f75663156166a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7859 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 diff --git a/apps/schedclock/screenshot-2.png b/apps/schedclock/screenshot-2.png deleted file mode 100644 index cc0943a978eaaf7350b8302438d23b661f735535..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6888 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 diff --git a/apps/schedclock/screenshot1.png b/apps/schedclock/screenshot1.png new file mode 100644 index 0000000000000000000000000000000000000000..e7a267151e502d6006d87b3281b15f4eaca165d4 GIT binary patch literal 1781 zcmZ8hdpuNW7(T-ca%Yf5$X!%3sllwfT!tBq8MzY^TQp;g>(H)LVn(i$LQ6`DR8!M# zSh<8zla{m;%B?{t(=xV<-C_1dXv@`?) zfkL4&GBR>}la?chZPpJ(tU{X89BRGXf{VpXv zIw=;wN5>_k?D6vmFg&maxyPQI@Wu)BG{pvEzQbU40dimf19TuT00Jr>5W4^Bt*u)? zq`&5+K#hb*0jeN}f{4q5e$B^&dTT|2*y{g}|MyY=JbZ~P00kY0eKrWl)qkdLb-=Fv zGpP*JTT2R@BlXouu(t!%zga13}_@Q zy`M1j;`t}#=WVvXQb13LVpA%0KexyC0;;DN_&EvCLEQQ8>*6r~9 zr#O6SoT%DaLYEQIX7AOB{_1#8ZsB?H2%PMy6PACf{jTV(fGPPtH`u}0cJlN7yceIs z48%g<1d+q)G^>!BYwrAg7u#SGp0t#pVsS;?u&`wF*d@L!1$m=havADCJ$~7?qBX8Z z;uXq=EC2zt?1?ovv z*8CW~D|fTV7pr#bY>e(wZ?-Xd(Y{ZZGjMj?e!23%3gxqAU%I6t(fUnwt%taPzPMns zvMRJ#iFD$wGN<-lm+%y31#Ki>vNXkHSi*j!mt2T3|J+aOOiRKOa73>pV{b}m49n9j zNOA1RH^{mn3Vxd^DN<{V9&h@Et09Z7Di>z-VfqIfnRW^^aaUzy%kDqvV+*7X)l(}4 zYp>jot3S1GwDau^bGl?;ro8{hSTi$alft(?PlYv69p@RP8&%pqq&z)lg?hdVJk>## z!I;&o91XJcdRfBgWtSb0VP6?E9(^*a$!tk`UzQVN4l761ELPu<({8U_PWXdRhg}R) zJqhn_Y;%K;ur_7Qii%}t%< zn4Z3QWT@>ncC_hWbIljRd$-KBbCJs_<+ym`w~P`0>4%1%<$ttg%}xKB8!_E&Ria?R zn_jw=jti%sD}RVGN;A)T^MhIRAuLD3y>ECbE&DdIILK%EgL)VH9`C5W+Dt`SKld80 zplzu{n--e{4k656`e2l$-;)f(D2&RC|uNT%GI6tSE`|7ud3-P|y^Fw!{*`fs1 zdUUZGaviH#Q!4s-V8(TD`!DUMTf*j%>#u8*{&J#q4jgq$M7WU~RvC!RxiWg&z|6?J zTgc&{;h9wZ?E$RWXX8m84&n227aNGW#UmM_`tZXBW1!7<@5(|t70o-}KA@m5mp1-7 z>HIsiiU`&zzKJ6`rAE$abw9MJOc`t>qReU@p{Yk+VPx-S7cQk%uPLthS2wfH_hEIL zUBrwqgVw|I(Jubs)1#g{kZV5Tyq)m+(9CBM`)-J6Z{)*Tnr*C?1Rou|@0s$f&afBi z7JH)ReyBFL>9$fz7Vn*nhB@ap9{$YDz^zx%TZF__&0ouPuV&_H-MSEz*!p;n@2b1qo#m5Fp3akS>eM^l_28Lz6}c)p0&;1y@fB0) kPwW4ESd!Fo7<)qkjZeQJowzr`Wb1c~@$fsyc8famH<$7Wga7~l literal 0 HcmV?d00001 diff --git a/apps/schedclock/screenshot2.png b/apps/schedclock/screenshot2.png new file mode 100644 index 0000000000000000000000000000000000000000..d27e9441203e525f915f3cbab2664260b08943b9 GIT binary patch literal 1476 zcmZ8hdpOf;9RF?1EyeOMabzqyX9`neF41Pp9Mh5>P8cRT$ZgqNk_tJD+Bk|vN-86l z<60_FnUG70GV>7fAkDQ>4Uh9%b^bZ;^L*aV=X-s>@B7Egp}Lc{s_3Wy0I-$pLi7N+ zMh1B~aQEYXivS4{<3S<-)qTIb2Mzf@@f17&)bh5h2f{#Go9gE21o9@B2Z2DLP&qld z&CSi>;bG9%pH9;TW6IGkf5ZU5wl*0c9Z_Y+WrZv!Z`P401}iS`WH3Mt42@)&x;uFy zv8EVP>+ndiJ(zPh*qh8ClhJ?@Xutp+2n>LL$`yp}Pc57HSBTtqKNQp$2oz8U`51`M zbouZ8Fi^`@00>$AfBe5g0r1Y*ihw+rVQ#U{98&zFtE>W6{G-z)P|Fq+>;wIt>0yln z)z2!7xw$Or_TsUN5pSp%e62%dBHlA(Y`(zf?^{T9r*i(%*)USQ{F#NyHvLtHmFI5q z5eY{d^cXB3!f?C|+1#w`Qsa<5wvD#fYrKD6T=a6q*8BZK|Bc7*a^5cPn~DB;;bMvDHHy7enXn48W}ZoO1Rg$San?I=7^9-qh~nO1uU43j6UGN&uF|g zRFoKRiz6r(^WtBwc34D>k(y(09qgqGqL8&9{)7~}u2^`ObPt}Wh+4MP9=AB@EYZ6l z90@qr-O|TF%vuYdNtp_m9ImnPs|n#;wzg)nFOsIl@!S6b+2ZO$IeqzVT0EDR?%Y1< z7`PTKkfLjwn_QazqMmc8imq#IDs8K%DjhHzaXcL}xuY{CS%2-^s405EYNcf>Gyb5* zne*N$l@Z8QwS%cwW*=Id&Iudgz|~A^xlT(ku8251(Hq~z%&t%7c{|eevOIMSvNK*e z321h^7;fYqdrQlNe(PmE?}?Kn!y3J3q9kV0Ea@uT%*N==DgvL5G~lwIj}3bZk@j{PSN5Q^y2{r}T*7%aO+()hl5R)(9r_d$(;2K~^k(A1m5OMB zDn=vP?>BVbxbKYvT1k^ZI-4ccxE0kYsRD20_>{7~S6%e6^_%FB zLf~as*VixWOl9Mqo%oHk5Op3GR^C@pXWsb~V^k{O7KAnm^Cbo?*2^0f(hmE8ds&%l z7n{q4*eo$txJRs@vHSgF7rMKtZG@oU*%QN~6vu!A%sU0RLsM1Fcl_*(^Uk)lU<_YZ ziQ7>qjJ7V%)oj#$o;ba5Nmu2yEwVhfob4*I`PF%^2rGt~i=14h4<5aq`$kBH&0hN(u6Q#tojzbk8{D&&@5*6?J3?@v}OKrRs}Fujh|a%f=4~noN$E zeBs_CNXD&{1dsYmBG_1E!I7dN4NZZ~KfBSZHfoh08%nIO75?HL_D=7acE9_xwa)jw nhGs6mfqqioWKFIGe~OVfWH#qbBer}VkbN^`CwF2sAt2>n;)`cG literal 0 HcmV?d00001