From 5c84ec9e2cdace86854666d1cef23be61491e7d8 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 4 Apr 2022 15:49:45 +0100 Subject: [PATCH] Refactored alarm into separate 'sched' library/app --- apps/alarm/ChangeLog | 1 + apps/alarm/README.md | 80 +---------------------- apps/alarm/app.js | 6 +- apps/alarm/boot.js | 32 --------- apps/alarm/metadata.json | 13 ++-- apps/alarm/widget.js | 2 +- apps/sched/ChangeLog | 1 + apps/sched/README.md | 82 ++++++++++++++++++++++++ apps/sched/app-icon.js | 1 + apps/sched/app.png | Bin 0 -> 1223 bytes apps/sched/boot.js | 27 ++++++++ apps/{alarm => sched}/lib.js | 12 ++-- apps/sched/metadata.json | 18 ++++++ apps/{alarm/alarm.js => sched/sched.js} | 8 +-- 14 files changed, 151 insertions(+), 132 deletions(-) delete mode 100644 apps/alarm/boot.js create mode 100644 apps/sched/ChangeLog create mode 100644 apps/sched/README.md create mode 100644 apps/sched/app-icon.js create mode 100644 apps/sched/app.png create mode 100644 apps/sched/boot.js rename apps/{alarm => sched}/lib.js (77%) create mode 100644 apps/sched/metadata.json rename apps/{alarm/alarm.js => sched/sched.js} (90%) diff --git a/apps/alarm/ChangeLog b/apps/alarm/ChangeLog index e07e748d4..0811f2166 100644 --- a/apps/alarm/ChangeLog +++ b/apps/alarm/ChangeLog @@ -15,3 +15,4 @@ 0.14: Order of 'back' menu item 0.15: Fix hour/minute wrapping code for new menu system 0.16: Adding alarm library +0.17: Moving alarm internals to 'sched' library diff --git a/apps/alarm/README.md b/apps/alarm/README.md index 7bc4b7155..42131a5a6 100644 --- a/apps/alarm/README.md +++ b/apps/alarm/README.md @@ -1,82 +1,6 @@ Default Alarm & Timer ====================== -This provides an app, widget, library and tools for alarms and timers. +This allows you to add/modify any running timers. -Other apps can use this to provide alarm functionality. - -App ---- - -The Alarm app allows you to add/modify any running timers. - - -Internals / Library -------------------- - -Alarms are stored in an array in `alarm.json`, and take the form: - -``` -{ - id : "mytimer", // optional ID for this alarm/timer, so apps can easily find *their* timers - on : true, // is the alarm enabled? - t : 23400000, // Time of day since midnight in ms (if a timer, this is set automatically when timer starts) - dow : 0b1111111, // Binary encoding for days of the week to run alarm on - // SUN = 1 - // MON = 2 - // TUE = 4 - // WED = 8 - // THU = 16 - // FRI = 32 - // SAT = 64 - msg : "Eat chocolate", // message to display - last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day! - rp : true, // repeat - vibrate : "...", // pattern of '.', '-' and ' ' to use for when buzzing out this alarm (defaults to '..' if not set) - as : false, // auto snooze - timer : 5*60*1000, // OPTIONAL - if set, this is a timer and it's the time in ms - js : "load('myapp.js')" // OPTIONAL - a JS command to execute when the alarm activates (*instead* of loading 'alarm.js') - // when this code is run, you're responsible for setting alarm.on=false (or removing the alarm) - data : { ... } // OPTIONAL - your app can store custom data in here if needed -} -``` - -You app - -The [`alarm` library](https://github.com/espruino/BangleApps/blob/master/apps/alarm/lib.js) contains -a few helpful functions for getting/setting alarms, but is intentionally sparse so as not to -use too much RAM. - -It can be used as follows: - -``` -// add/update an existing alarm -require("alarm").setAlarm("mytimer", { - msg : "Wake up", - timer : 10*60*1000, // 10 Minutes -}); -// Ensure the widget and alarm timer updates to schedule the new alarm properly -require("alarm").reload(); - -// Get the time to the next alarm for us -var timeToNext = require("alarm").getTimeToAlarm(require("alarm").getAlarm("mytimer")); -// timeToNext===undefined if no alarm or alarm disabled - -// delete an alarm -require("alarm").setAlarm("mytimer", undefined); -// reload after deleting... -require("alarm").reload(); - -// Or add an alarm that runs your own code - in this case -// loading the settings app. The alarm will not be removed/stopped -// automatically. -require("alarm").setAlarm("customrunner", { - js : "load('setting.app.js')", - timer : 1*60*1000, // 1 Minute -}); -``` - - - -If your app requires alarms, you can specify that the alarms app needs to -be installed by adding `"dependencies": {"alarm":"app"},` to your metadata. +It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps. diff --git a/apps/alarm/app.js b/apps/alarm/app.js index e83e1c3d5..d5379f469 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -1,7 +1,7 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); -var alarms = require("Storage").readJSON("alarm.json",1)||[]; +var alarms = require("Storage").readJSON("sched.json",1)||[]; // An array of alarm objects (see README.md) // time in ms -> { hrs, mins } @@ -31,8 +31,8 @@ function getCurrentTime() { } function saveAndReload() { - require("Storage").write("alarm.json",JSON.stringify(alarms)); - require("alarm").reload(); + require("Storage").write("sched.json",JSON.stringify(alarms)); + require("sched").reload(); } function showMainMenu() { diff --git a/apps/alarm/boot.js b/apps/alarm/boot.js deleted file mode 100644 index cb3a82a7e..000000000 --- a/apps/alarm/boot.js +++ /dev/null @@ -1,32 +0,0 @@ -// check for alarms -(function() { - if (Bangle.ALARM) { - clearTimeout(Bangle.ALARM); - delete Bangle.ALARM; - } - var alarms = require('Storage').readJSON('alarm.json',1)||[]; - var time = new Date(); - var active = alarms.filter(a=>a.on && (a.dow>>time.getDay())&1); - if (active.length) { - active = active.sort((a,b)=>(a.t-b.t)+(a.last-b.last)*86400000); - var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); - if (!require('Storage').read("alarm.js")) { - console.log("No alarm app!"); - require('Storage').write('alarm.json',"[]"); - } else { - var t = active[0].t-currentTime; - if (active[0].last == time.getDate() || t < -60000) t += 86400000; - if (t<1000) t=1000; // start alarm min 1 sec from now - /* execute alarm at the correct time. We avoid execing immediately - since this code will get called AGAIN when alarm.js is loaded. alarm.js - will then clearInterval() to get rid of this call so it can proceed - normally. - If active[0].js is defined, just run that code as-is and not alarm.js */ - Bangle.ALARM = setTimeout(active[0].js||'load("alarm.js")',t); - } - } else { // check for new alarms at midnight (so day of week works) - Bangle.ALARM = setTimeout(() => { - eval(require("Storage").read("alarm.boot.js")); - }, 86400000 - (Date.now()%86400000)); - } -})(); diff --git a/apps/alarm/metadata.json b/apps/alarm/metadata.json index c10f64a5c..726e2461c 100644 --- a/apps/alarm/metadata.json +++ b/apps/alarm/metadata.json @@ -1,20 +1,17 @@ { "id": "alarm", - "name": "Default Alarm & Timer", + "name": "Alarm & Timer", "shortName": "Alarms", - "version": "0.16", + "version": "0.17", "description": "Set and respond to alarms and timers", "icon": "app.png", "tags": "tool,alarm,widget", "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", + "dependencies": {"scheduler":"type"}, "storage": [ {"name":"alarm.app.js","url":"app.js"}, - {"name":"alarm.boot.js","url":"boot.js"}, - {"name":"alarm.js","url":"alarm.js"}, {"name":"alarm.img","url":"app-icon.js","evaluate":true}, - {"name":"alarm.wid.js","url":"widget.js"}, - {"name":"alarm","url":"lib.js"} - ], - "data": [{"name":"alarm.json"}] + {"name":"alarm.wid.js","url":"widget.js"} + ] } diff --git a/apps/alarm/widget.js b/apps/alarm/widget.js index 54c17dc6d..052ac9ebd 100644 --- a/apps/alarm/widget.js +++ b/apps/alarm/widget.js @@ -2,7 +2,7 @@ WIDGETS["alarm"]={area:"tl",width:0,draw:function() { if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y); },reload:function() { // don't include library here as we're trying to use as little RAM as possible - WIDGETS["alarm"].width = (require('Storage').readJSON('alarm.json',1)||[]).some(alarm=>alarm.on) ? 24 : 0; + WIDGETS["alarm"].width = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on&&(alarm.hidden!==false)) ? 24 : 0; } }; WIDGETS["alarm"].reload(); diff --git a/apps/sched/ChangeLog b/apps/sched/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/sched/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/sched/README.md b/apps/sched/README.md new file mode 100644 index 000000000..03d93c688 --- /dev/null +++ b/apps/sched/README.md @@ -0,0 +1,82 @@ +Sched: Scheduling library for alarms and timers +==================================== + +This provides boot code, a library and tools for alarms and timers. + +Other apps can use this to provide alarm functionality. + +App +--- + +The Alarm app allows you to add/modify any running timers. + + +Internals / Library +------------------- + +Alarms are stored in an array in `sched.json`, and take the form: + +``` +{ + id : "mytimer", // optional ID for this alarm/timer, so apps can easily find *their* timers + on : true, // is the alarm enabled? + t : 23400000, // Time of day since midnight in ms (if a timer, this is set automatically when timer starts) + dow : 0b1111111, // Binary encoding for days of the week to run alarm on + // SUN = 1 + // MON = 2 + // TUE = 4 + // WED = 8 + // THU = 16 + // FRI = 32 + // SAT = 64 + + date : "2022-04-04", // OPTIONAL date for the alarm, in YYYY-MM-DD format + // eg (new Date()).toISOString().substr(0,10) + msg : "Eat food", // message to display + last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day! + rp : true, // repeat the alarm every day? + vibrate : "...", // OPTIONAL pattern of '.', '-' and ' ' to use for when buzzing out this alarm (defaults to '..' if not set) + hidden : false, // OPTIONAL if false, the widget should not show an icon for this alarm + as : false, // auto snooze + timer : 5*60*1000, // OPTIONAL - if set, this is a timer and it's the time in ms + js : "load('myapp.js')" // OPTIONAL - a JS command to execute when the alarm activates (*instead* of loading 'sched.js') + // when this code is run, you're responsible for setting alarm.on=false (or removing the alarm) + data : { ... } // OPTIONAL - your app can store custom data in here if needed +} +``` + +The [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched/lib.js) contains +a few helpful functions for getting/setting alarms and timers, but is intentionally sparse so as not to +use too much RAM. + +It can be used as follows: + +``` +// add/update an existing alarm +require("sched").setAlarm("mytimer", { + msg : "Wake up", + timer : 10*60*1000, // 10 Minutes +}); +// Ensure the widget and alarm timer updates to schedule the new alarm properly +require("sched").reload(); + +// Get the time to the next alarm for us +var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm("mytimer")); +// timeToNext===undefined if no alarm or alarm disabled + +// delete an alarm +require("sched").setAlarm("mytimer", undefined); +// reload after deleting... +require("sched").reload(); + +// Or add an alarm that runs your own code - in this case +// loading the settings app. The alarm will not be removed/stopped +// automatically. +require("sched").setAlarm("customrunner", { + js : "load('setting.app.js')", + timer : 1*60*1000, // 1 Minute +}); +``` + +If your app requires alarms, you can specify that the alarms app needs to +be installed by adding `"dependencies": {"scheduler":"type"},` to your metadata. diff --git a/apps/sched/app-icon.js b/apps/sched/app-icon.js new file mode 100644 index 000000000..05515e859 --- /dev/null +++ b/apps/sched/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkGswAhiMRCCAREAo4eHBIQLEAgwYHsIJDiwHB5gACBpIhHCoYZEGA4gFCw4ABGA4HEjgXJ4IXGAwcUB4VEmf//8zogICoJIFAodMBoNDCoIADmgJB4gXIFwXDCwoABngwFC4guB4k/CQXwh4EC+YMCC44iBp4qDC4n/+gNBC41sEIJCEC4v/GAPGC4dhXYRdFC4xhCCYIXCdQRdDC5HzegQXCsxGHC45IDCwQXCUgwXHJAIXGRogXJSIIXcOw4XIPAYXcBwv/mEDBAwXOgtQC65QGC5vzoEAJAx3Nmk/mEABIiPN+dDAQIwFC4zXGFwKRCGAjvMFwQECGAgXI4YuGGAUvAgU8C4/EFwwGCAgdMC4p4EFwobFOwoXDJAIoEAApGBC4xIEABJGHGAapEAAqNBFwwXD4heI+YuBC5BIBVQhdHIw4wD5inFS4IKCCxFmigNCokzCoMzogICoIWIsMRjgPCAA3BiMWC48RBQIXJEgMRFxAJCCw4lEC44IECooOIBAaBJKwhgIAH4ACA==")) diff --git a/apps/sched/app.png b/apps/sched/app.png new file mode 100644 index 0000000000000000000000000000000000000000..1f5784bde644cca9d42f2081e2a9d05872f7d82a GIT binary patch literal 1223 zcmV;&1UUPNP)yt~5>heLDBtbIv{Ickem(+#iGyMi?Ot;mye3`tA2XRuFvAAO+_-k8R4C zi)@4+UQnF}5R&Y<%K8f+q!11u*zif>(|s-(3`e|Cm=^8hn?Hp74uk^;HbOptsQCz& zAN(1BFDh5h82?vz>^urWeh0#3G}s8|K(G<=IWVsy6bSs%XQAm-4M@SuFYV*0wETJL z&ua%BB?q$7jJ*E*QuMk=BrYah=ZTFTVb8%`437;1U^d3F`^`N#R5Rah!3N>pR1{@t#t=A zfQCFQ$<5 zq=_4&XSwv-ZVvCgM1RjPSvk*e>buh@v5UDmewA_8D6UB-cPAXAm{t)JrKkB)J-tKc zJfBinS|>jeA=T_5e@%GEjm^g1JzISss0 z`YINSh1}d+PM$o8!C>&KJMJ2zzI{7R)!}h>TiurSucyldyb9eIJ*&~#D@rjN6KHE| zqoAMwtJO+TQ4wQfV}5lPM;lnby!bvtN}s9}uwLk+vXvy6ble`dYrKrassB}ay`I+A zRyJcxoVgf4T+iCN?Qkrk(07jTf(^YP6y# zoH%iU!ootVRx4Jk6;)L!FE5{ACqY>@eVB?D<`sZ<_c)hsQcO#;NHKFcMNz1(uBN19 z>SUPBW*>d8GV5~}1iTm0MaQ8!r;_lNW00lBv>Efo#l>`VbRdKvFE7tW|CVDwqXi6j z&j&mJ!=elv&c6Y$57~SI0LagOa06$*X47a1=e_3xUWMX~8#LPX$1x_<@j2@{RR^~I zrfD3pa2J^e@y2IatV;plwsV-~o_Zv+Uo{d*OHVy_oFf1T=p9NPY4Mf|cmNSZk(Ts^ zmduw2EF8VC@1Z;4R2>|>P{-wg4i8I8A>Z@YUeD>(^MBxj)))3msP>FwW6b2N*h)rX zwpLzm-uVankPTaZllJBBx%t3mZoyt;k==Pa)yyslvKkP#ox_~5H*&___%JoCorZS5 z0t>bS9@MlI6eGpQK>94Azar$j{yNaFrZ!U!Y8@h>Y%nWN0p7Wn`G002ovPDHLkV1nE7J?;Pi literal 0 HcmV?d00001 diff --git a/apps/sched/boot.js b/apps/sched/boot.js new file mode 100644 index 000000000..15c332ad7 --- /dev/null +++ b/apps/sched/boot.js @@ -0,0 +1,27 @@ +// check for alarms +(function() { + if (Bangle.ALARM) { + clearTimeout(Bangle.ALARM); + delete Bangle.ALARM; + } + var alarms = require('Storage').readJSON('sched.json',1)||[]; + var time = new Date(); + var active = alarms.filter(a=>a.on && (a.dow>>time.getDay())&1 && (!a.date || a.date==time.toISOString().substr(0,10))); + if (active.length) { + active = active.sort((a,b)=>(a.t-b.t)+(a.last-b.last)*86400000); + var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); + var t = active[0].t-currentTime; + if (active[0].last == time.getDate() || t < -60000) t += 86400000; + if (t<1000) t=1000; // start alarm min 1 sec from now + /* execute alarm at the correct time. We avoid execing immediately + since this code will get called AGAIN when alarm.js is loaded. alarm.js + will then clearInterval() to get rid of this call so it can proceed + normally. + If active[0].js is defined, just run that code as-is and not alarm.js */ + Bangle.ALARM = setTimeout(active[0].js||'load("sched.js")',t); + } else { // check for new alarms at midnight (so day of week works) + Bangle.ALARM = setTimeout(() => { + eval(require("Storage").read("sched.boot.js")); + }, 86400000 - (Date.now()%86400000)); + } +})(); diff --git a/apps/alarm/lib.js b/apps/sched/lib.js similarity index 77% rename from apps/alarm/lib.js rename to apps/sched/lib.js index a0d8b3938..9d9744b1f 100644 --- a/apps/alarm/lib.js +++ b/apps/sched/lib.js @@ -1,15 +1,15 @@ // Return an array of all alarms exports.getAlarms = function() { - return require("Storage").readJSON("alarm.json",1)||[]; + return require("Storage").readJSON("sched.json",1)||[]; }; // Return an alarm object based on ID exports.getAlarm = function(id) { - var alarms = require("Storage").readJSON("alarm.json",1)||[]; + var alarms = require("Storage").readJSON("sched.json",1)||[]; return alarms.find(a=>a.id==id); }; // Set an alarm object based on ID. Leave 'alarm' undefined to remove it exports.setAlarm = function(id, alarm) { - var alarms = require("Storage").readJSON("alarm.json",1)||[]; + var alarms = require("Storage").readJSON("sched.json",1)||[]; alarms = alarms.filter(a=>a.id!=id); if (alarm !== undefined) { alarm.id = id; @@ -22,13 +22,13 @@ exports.setAlarm = function(id, alarm) { } } alarms.push(alarm); - require("Storage").writeJSON("alarm.json", alarms); + require("Storage").writeJSON("sched.json", alarms); }; /// Get time until the given alarm (object). Return undefined if alarm not enabled, or if 86400000 or more, alarm could me *more* than a day in the future exports.getTimeToAlarm = function(alarm, time) { if (!alarm) return undefined; if (!time) time = new Date(); - var active = alarm.on && (alarm.dow>>time.getDay())&1; + var active = alarm.on && (alarm.dow>>time.getDay())&1 && (!alarm.date || alarm.date==time.toISOString().substr(0,10)); if (!active) return undefined; var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); var t = alarm.t-currentTime; @@ -37,7 +37,7 @@ exports.getTimeToAlarm = function(alarm, time) { }; /// Force a reload of the current alarms and widget exports.reload = function() { - eval(require("Storage").read("alarm.boot.js")); + eval(require("Storage").read("sched.boot.js")); if (WIDGETS["alarm"]) { WIDGETS["alarm"].reload(); Bangle.drawWidgets(); diff --git a/apps/sched/metadata.json b/apps/sched/metadata.json new file mode 100644 index 000000000..60aa71d41 --- /dev/null +++ b/apps/sched/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "sched", + "name": "Scheduler", + "version": "0.01", + "description": "Scheduling library for alarms and timers", + "icon": "app.png", + "type": "scheduler", + "tags": "tool,system,alarm", + "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"sched.boot.js","url":"boot.js"}, + {"name":"sched.js","url":"sched.js"}, + {"name":"sched.img","url":"app-icon.js","evaluate":true}, + {"name":"sched","url":"lib.js"} + ], + "data": [{"name":"sched.json"}] +} diff --git a/apps/alarm/alarm.js b/apps/sched/sched.js similarity index 90% rename from apps/alarm/alarm.js rename to apps/sched/sched.js index 7ec8d0b73..f42a15fc3 100644 --- a/apps/alarm/alarm.js +++ b/apps/sched/sched.js @@ -1,5 +1,5 @@ // Chances are boot0.js got run already and scheduled *another* -// 'load(alarm.js)' - so let's remove it first! +// 'load(sched.js)' - so let's remove it first! if (Bangle.ALARM) { clearInterval(Bangle.ALARM); delete Bangle.ALARM; @@ -54,7 +54,7 @@ function showAlarm(alarm) { } if (!alarm.rp) alarm.on = false; } - require("Storage").write("alarm.json",JSON.stringify(alarms)); + require("Storage").write("sched.json",JSON.stringify(alarms)); load(); }); function buzz() { @@ -74,8 +74,8 @@ function showAlarm(alarm) { // Check for alarms var day = (new Date()).getDate(); var currentTime = getCurrentTime()+10000; // get current time - 10s in future to ensure we alarm if we've started the app a tad early -var alarms = require("Storage").readJSON("alarm.json",1)||[]; -var active = alarms.filter(a=>a.on&&(a.ta.on&&(a.ta.t-b.t);