From 0feebdb318130e9ef0246b698b9ae941d6980bc3 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 31 Mar 2022 13:48:45 +0100 Subject: [PATCH 01/10] improved alarm app --- apps/alarm/alarm.js | 52 +++++++---- apps/alarm/app.js | 219 ++++++++++++++++++++++++-------------------- apps/alarm/boot.js | 22 +++-- 3 files changed, 171 insertions(+), 122 deletions(-) diff --git a/apps/alarm/alarm.js b/apps/alarm/alarm.js index a655dad1e..9a9e172cc 100644 --- a/apps/alarm/alarm.js +++ b/apps/alarm/alarm.js @@ -1,20 +1,38 @@ // Chances are boot0.js got run already and scheduled *another* // 'load(alarm.js)' - so let's remove it first! -clearInterval(); - -function formatTime(t) { - var hrs = 0|t; - var mins = Math.round((t-hrs)*60); - return hrs+":"+("0"+mins).substr(-2); +if (Bangle.ALARM) { + clearInterval(Bangle.ALARM); + delete Bangle.ALARM; } -function getCurrentHr() { +// time in ms -> { hrs, mins } +function decodeTime(t) { + t = 0|t; // sanitise + var hrs = 0|(t/3600000); + return { hrs : hrs, mins : Math.round((t-hrs*3600000)/60000) }; +} + +// time in { hrs, mins } -> ms +function encodeTime(o) { + return o.hrs*3600000 + o.mins*60000; +} + +function formatTime(t) { + var o = decodeTime(t); + return o.hrs+":"+("0"+o.mins).substr(-2); +} + +function getCurrentTime() { var time = new Date(); - return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); + return ( + time.getHours() * 3600000 + + time.getMinutes() * 60000 + + time.getSeconds() * 1000 + ); } function showAlarm(alarm) { - var msg = formatTime(alarm.hr); + var msg = alarm.timer ? formatTime(alarm.timer) : formatTime(alarm.t); var buzzCount = 10; if (alarm.msg) msg += "\n"+alarm.msg; @@ -26,13 +44,13 @@ function showAlarm(alarm) { }).then(function(sleep) { buzzCount = 0; if (sleep) { - if(alarm.ohr===undefined) alarm.ohr = alarm.hr; - alarm.hr += 10/60; // 10 minutes + if(alarm.ot===undefined) alarm.ot = alarm.t; + alarm.t += 10*60*1000; // 10 minutes } else { alarm.last = (new Date()).getDate(); - if (alarm.ohr!==undefined) { - alarm.hr = alarm.ohr; - delete alarm.ohr; + if (alarm.ot!==undefined) { + alarm.t = alarm.ot; + delete alarm.ot; } if (!alarm.rp) alarm.on = false; } @@ -59,12 +77,12 @@ function showAlarm(alarm) { // Check for alarms var day = (new Date()).getDate(); -var hr = getCurrentHr()+10000; // get current time - 10s in future to ensure we alarm if we've started the app a tad early +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.hra.on&&(a.ta.hr-b.hr); + active = active.sort((a,b)=>a.t-b.t); showAlarm(active[0]); } else { // otherwise just go back to default app diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 56184edf1..7419f0d5d 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -4,33 +4,49 @@ Bangle.drawWidgets(); var alarms = require("Storage").readJSON("alarm.json",1)||[]; /*alarms = [ { on : true, - hr : 6.5, // hours + minutes/60 + t : 23400000, // Time of day since midnight in ms msg : "Eat chocolate", last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day! rp : true, // repeat as : false, // auto snooze - timer : 5, // OPTIONAL - if set, this is a timer and it's the time in minutes + timer : 5*60*1000, // OPTIONAL - if set, this is a timer and it's the time in ms } ];*/ +// time in ms -> { hrs, mins } +function decodeTime(t) { + t = 0|t; // sanitise + var hrs = 0|(t/3600000); + return { hrs : hrs, mins : Math.round((t-hrs*3600000)/60000) }; +} + +// time in { hrs, mins } -> ms +function encodeTime(o) { + return o.hrs*3600000 + o.mins*60000; +} + function formatTime(t) { - var hrs = 0|t; - var mins = Math.round((t-hrs)*60); - return hrs+":"+("0"+mins).substr(-2); + var o = decodeTime(t); + return o.hrs+":"+("0"+o.mins).substr(-2); } -function formatMins(t) { - mins = (0|t)%60; - hrs = 0|(t/60); - return hrs+":"+("0"+mins).substr(-2); -} - -function getCurrentHr() { +function getCurrentTime() { var time = new Date(); - return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); + return ( + time.getHours() * 3600000 + + time.getMinutes() * 60000 + + time.getSeconds() * 1000 + ); +} + +function saveAndReload() { + require("Storage").write("alarm.json",JSON.stringify(alarms)); + eval(require("Storage").read("alarm.boot.js")); } function showMainMenu() { + // Timer img "\0"+atob("DhKBAP////MDDAwwMGGBzgPwB4AeAPwHOBhgwMMzDez////w") + // Alarm img "\0"+atob("FBSBAABgA4YcMPDGP8Zn/mx/48//PP/zD/8A//AP/wD/8A//AP/wH/+D//w//8AAAADwAAYA") const menu = { '': { 'title': 'Alarm/Timer' }, /*LANG*/'< Back' : ()=>{load();}, @@ -38,140 +54,147 @@ function showMainMenu() { /*LANG*/'New Timer': ()=>editTimer(-1) }; alarms.forEach((alarm,idx)=>{ - if (alarm.timer) { - txt = /*LANG*/"TIMER "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatMins(alarm.timer); - } else { - txt = /*LANG*/"ALARM "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatTime(alarm.hr); - if (alarm.rp) txt += /*LANG*/" (repeat)"; - } - menu[txt] = function() { - if (alarm.timer) editTimer(idx); - else editAlarm(idx); + var txt; // a leading space is currently required (JS error in Espruino 2v12) + if (alarm.timer) + txt = /*LANG*/"Timer"+" "+formatTime(alarm.timer); + else + txt = /*LANG*/"Alarm"+" "+formatTime(alarm.t); + if (alarm.rp) txt += "\0"+atob("FBaBAAABgAAcAAHn//////wAHsABzAAYwAAMAADAAAAAAwAAMAADGAAzgAN4AD//////54AAOAABgAA="); + menu[txt] = { + value : "\0"+atob(alarm.on?"EhKBAH//v/////////////5//x//j//H+eP+Mf/A//h//z//////////3//g":"EhKBAH//v//8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA///3//g"), + onchange : function() { + if (alarm.timer) editTimer(idx, alarm); + else editAlarm(idx, alarm); + } }; }); - if (WIDGETS["alarm"]) WIDGETS["alarm"].reload(); return E.showMenu(menu); } -function editAlarm(alarmIndex) { +function editDOW(dow, onchange) { + const menu = { + '': { 'title': /*LANG*/'Days of Week' }, + '< Back' : () => onchange(dow) + }; + for (var i = 0; i < 7; i++) (i => { + var dayOfWeek = require("locale").dow({ getDay: () => i }); + menu[dayOfWeek] = { + value: !!(dow&(1< v ? "Yes" : "No", + onchange: v => v ? dow |= 1< showMainMenu(), /*LANG*/'Hours': { - value: hrs, min : 0, max : 23, wrap : true, - onchange: v => hrs=v + value: t.hrs, min : 0, max : 23, wrap : true, + onchange: v => t.hrs=v }, /*LANG*/'Minutes': { - value: mins, min : 0, max : 59, wrap : true, - onchange: v => mins=v + value: t.mins, min : 0, max : 59, wrap : true, + onchange: v => t.mins=v }, /*LANG*/'Enabled': { - value: en, + value: a.on, format: v=>v?"On":"Off", - onchange: v=>en=v + onchange: v=>a.on=v }, /*LANG*/'Repeat': { - value: en, + value: a.rp, format: v=>v?"Yes":"No", - onchange: v=>repeat=v + onchange: v=>a.rp=v + }, + /*LANG*/'Days': { + value: "SMTWTFS".split("").map((d,n)=>a.dow&(1< editDOW(a.dow, d=>{a.dow=d;editAlarm(alarmIndex,a)}) }, /*LANG*/'Auto snooze': { - value: as, + value: a.as, format: v=>v?"Yes":"No", - onchange: v=>as=v + onchange: v=>a.as=v } }; - function getAlarm() { - var hr = hrs+(mins/60); - var day = 0; - // If alarm is for tomorrow not today (eg, in the past), set day - if (hr < getCurrentHr()) - day = (new Date()).getDate(); - // Save alarm - return { - on : en, hr : hr, - last : day, rp : repeat, as: as - }; - } - menu[/*LANG*/"> Save"] = function() { - if (newAlarm) alarms.push(getAlarm()); - else alarms[alarmIndex] = getAlarm(); - require("Storage").write("alarm.json",JSON.stringify(alarms)); + menu[/*LANG*/"Save"] = function() { + a.t = encodeTime(t); + if (a.t < getCurrentTime()) + a.day = (new Date()).getDate(); + if (newAlarm) alarms.push(a); + else alarms[alarmIndex] = a; + saveAndReload(); showMainMenu(); }; if (!newAlarm) { - menu[/*LANG*/"> Delete"] = function() { + menu[/*LANG*/"Delete"] = function() { alarms.splice(alarmIndex,1); - require("Storage").write("alarm.json",JSON.stringify(alarms)); + saveAndReload(); showMainMenu(); }; } return E.showMenu(menu); } -function editTimer(alarmIndex) { +function editTimer(alarmIndex, alarm) { var newAlarm = alarmIndex<0; - var hrs = 0; - var mins = 5; - var en = true; - if (!newAlarm) { - var a = alarms[alarmIndex]; - mins = (0|a.timer)%60; - hrs = 0|(a.timer/60); - en = a.on; + var a = { + timer : 5*60*1000, // 5 minutes + on : true, + rp : false, + as : false, + dow : 0b1111111, + last : 0 } + if (!newAlarm) Object.assign(a, alarms[alarmIndex]); + if (alarm) Object.assign(a,alarm); + var t = decodeTime(a.timer); + const menu = { '': { 'title': /*LANG*/'Timer' }, + '< Back' : () => showMainMenu(), /*LANG*/'Hours': { - value: hrs, min : 0, max : 23, wrap : true, - onchange: v => hrs=v + value: t.hrs, min : 0, max : 23, wrap : true, + onchange: v => t.hrs=v }, /*LANG*/'Minutes': { - value: mins, min : 0, max : 59, wrap : true, - onchange: v => mins=v + value: t.mins, min : 0, max : 59, wrap : true, + onchange: v => t.mins=v }, /*LANG*/'Enabled': { - value: en, - format: v=>v?/*LANG*/"On":/*LANG*/"Off", - onchange: v=>en=v + value: a.on, + format: v=>v?"On":"Off", + onchange: v=>a.on=v } }; - function getTimer() { - var d = new Date(Date.now() + ((hrs*60)+mins)*60000); - var hr = d.getHours() + (d.getMinutes()/60) + (d.getSeconds()/3600); - // Save alarm - return { - on : en, - timer : (hrs*60)+mins, - hr : hr, - rp : false, as: false - }; - } - menu["> Save"] = function() { - if (newAlarm) alarms.push(getTimer()); - else alarms[alarmIndex] = getTimer(); - require("Storage").write("alarm.json",JSON.stringify(alarms)); + menu[/*LANG*/"Save"] = function() { + a.timer = encodeTime(t); + a.hr = getCurrentTime() + a.timer; + if (newAlarm) alarms.push(a); + else alarms[alarmIndex] = a; + saveAndReload(); showMainMenu(); }; if (!newAlarm) { - menu["> Delete"] = function() { + menu[/*LANG*/"Delete"] = function() { alarms.splice(alarmIndex,1); - require("Storage").write("alarm.json",JSON.stringify(alarms)); + saveAndReload(); showMainMenu(); }; } diff --git a/apps/alarm/boot.js b/apps/alarm/boot.js index 47dae5361..a7fdd3658 100644 --- a/apps/alarm/boot.js +++ b/apps/alarm/boot.js @@ -1,25 +1,33 @@ // 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); + var active = alarms.filter(a=>a.on && (a.dow>>time.getDay())&1); if (active.length) { - active = active.sort((a,b)=>(a.hr-b.hr)+(a.last-b.last)*24); - var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); + 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 = 3600000*(active[0].hr-hr); - if (active[0].last == time.getDate() || t < 0) t += 86400000; - if (t<1000) t=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. */ - setTimeout(function() { + Bangle.ALARM = setTimeout(function() { 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)); } })(); From 9055cd79900da0886920766fbf2fd7d04cf66576 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 31 Mar 2022 15:32:26 +0100 Subject: [PATCH 02/10] Fixes, README and alarm lib --- apps/alarm/ChangeLog | 1 + apps/alarm/README.md | 70 ++++++++++++++++++++++++++++++++++++++++ apps/alarm/app.js | 19 ++++++----- apps/alarm/boot.js | 7 ++-- apps/alarm/lib.js | 45 ++++++++++++++++++++++++++ apps/alarm/metadata.json | 6 ++-- apps/alarm/widget.js | 1 + 7 files changed, 135 insertions(+), 14 deletions(-) create mode 100644 apps/alarm/README.md create mode 100644 apps/alarm/lib.js diff --git a/apps/alarm/ChangeLog b/apps/alarm/ChangeLog index 4576237a5..e07e748d4 100644 --- a/apps/alarm/ChangeLog +++ b/apps/alarm/ChangeLog @@ -14,3 +14,4 @@ 0.13: Alarm widget state now updates when setting/resetting an alarm 0.14: Order of 'back' menu item 0.15: Fix hour/minute wrapping code for new menu system +0.16: Adding alarm library diff --git a/apps/alarm/README.md b/apps/alarm/README.md new file mode 100644 index 000000000..16a39f8b9 --- /dev/null +++ b/apps/alarm/README.md @@ -0,0 +1,70 @@ +Default Alarm & Timer +====================== + +This provides an app, widget, 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 `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 + 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 + 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) +} +``` + +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. diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 7419f0d5d..b5efb3f17 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -3,13 +3,16 @@ Bangle.drawWidgets(); var alarms = require("Storage").readJSON("alarm.json",1)||[]; /*alarms = [ - { on : true, - t : 23400000, // Time of day since midnight in ms - msg : "Eat chocolate", - last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day! - rp : true, // repeat - as : false, // auto snooze + { + 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 + 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 + 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') } ];*/ @@ -41,7 +44,7 @@ function getCurrentTime() { function saveAndReload() { require("Storage").write("alarm.json",JSON.stringify(alarms)); - eval(require("Storage").read("alarm.boot.js")); + require("alarm").reload(); } function showMainMenu() { @@ -185,7 +188,7 @@ function editTimer(alarmIndex, alarm) { }; menu[/*LANG*/"Save"] = function() { a.timer = encodeTime(t); - a.hr = getCurrentTime() + a.timer; + a.t = getCurrentTime() + a.timer; if (newAlarm) alarms.push(a); else alarms[alarmIndex] = a; saveAndReload(); diff --git a/apps/alarm/boot.js b/apps/alarm/boot.js index a7fdd3658..cb3a82a7e 100644 --- a/apps/alarm/boot.js +++ b/apps/alarm/boot.js @@ -20,10 +20,9 @@ /* 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. */ - Bangle.ALARM = setTimeout(function() { - load("alarm.js"); - },t); + 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(() => { diff --git a/apps/alarm/lib.js b/apps/alarm/lib.js new file mode 100644 index 000000000..a0d8b3938 --- /dev/null +++ b/apps/alarm/lib.js @@ -0,0 +1,45 @@ +// Return an array of all alarms +exports.getAlarms = function() { + return require("Storage").readJSON("alarm.json",1)||[]; +}; +// Return an alarm object based on ID +exports.getAlarm = function(id) { + var alarms = require("Storage").readJSON("alarm.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)||[]; + alarms = alarms.filter(a=>a.id!=id); + if (alarm !== undefined) { + alarm.id = id; + if (alarm.dow===undefined) alarm.dow = 0b1111111; + if (alarm.on!==false) alarm.on=true; + if (alarm.timer) { // if it's a timer, set the start time as a time from *now* + var time = new Date(); + var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); + alarm.t = currentTime + alarm.timer; + } + } + alarms.push(alarm); + require("Storage").writeJSON("alarm.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; + if (!active) return undefined; + var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); + var t = alarm.t-currentTime; + if (alarm.last == time.getDate() || t < -60000) t += 86400000; + return t; +}; +/// Force a reload of the current alarms and widget +exports.reload = function() { + eval(require("Storage").read("alarm.boot.js")); + if (WIDGETS["alarm"]) { + WIDGETS["alarm"].reload(); + Bangle.drawWidgets(); + } +}; diff --git a/apps/alarm/metadata.json b/apps/alarm/metadata.json index d29298309..c10f64a5c 100644 --- a/apps/alarm/metadata.json +++ b/apps/alarm/metadata.json @@ -2,17 +2,19 @@ "id": "alarm", "name": "Default Alarm & Timer", "shortName": "Alarms", - "version": "0.15", + "version": "0.16", "description": "Set and respond to alarms and timers", "icon": "app.png", "tags": "tool,alarm,widget", "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", "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.wid.js","url":"widget.js"}, + {"name":"alarm","url":"lib.js"} ], "data": [{"name":"alarm.json"}] } diff --git a/apps/alarm/widget.js b/apps/alarm/widget.js index e8bb79fc7..54c17dc6d 100644 --- a/apps/alarm/widget.js +++ b/apps/alarm/widget.js @@ -1,6 +1,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; } }; From 771950f51b57f37a1190d886617da1d344792c5f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 1 Apr 2022 12:50:36 +0100 Subject: [PATCH 03/10] docs --- apps/alarm/README.md | 10 +++++++++- apps/alarm/app.js | 14 +------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/apps/alarm/README.md b/apps/alarm/README.md index 16a39f8b9..a38d6f6ed 100644 --- a/apps/alarm/README.md +++ b/apps/alarm/README.md @@ -20,7 +20,15 @@ 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 + 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 diff --git a/apps/alarm/app.js b/apps/alarm/app.js index b5efb3f17..4968fc95b 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -2,19 +2,7 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); var alarms = require("Storage").readJSON("alarm.json",1)||[]; -/*alarms = [ - { - 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 - 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 - 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') - } -];*/ +// An array of alarm objects (see README.md) // time in ms -> { hrs, mins } function decodeTime(t) { From 5c33cad685a4dfa52059e5512bd3f34c9e74518a Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 1 Apr 2022 13:27:37 +0100 Subject: [PATCH 04/10] Adding alarm.vibrate and menu --- apps/alarm/README.md | 4 ++++ apps/alarm/alarm.js | 20 ++++++++------------ apps/alarm/app.js | 10 +++++++--- apps/messages/settings.js | 10 +--------- apps/messages/widget.js | 9 +-------- modules/buzz.js | 14 ++++++++++++++ modules/buzz_menu.js | 13 +++++++++++++ 7 files changed, 48 insertions(+), 32 deletions(-) create mode 100644 modules/buzz.js create mode 100644 modules/buzz_menu.js diff --git a/apps/alarm/README.md b/apps/alarm/README.md index a38d6f6ed..7bc4b7155 100644 --- a/apps/alarm/README.md +++ b/apps/alarm/README.md @@ -32,13 +32,17 @@ Alarms are stored in an array in `alarm.json`, and take the form: 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. diff --git a/apps/alarm/alarm.js b/apps/alarm/alarm.js index 9a9e172cc..7ec8d0b73 100644 --- a/apps/alarm/alarm.js +++ b/apps/alarm/alarm.js @@ -58,20 +58,16 @@ function showAlarm(alarm) { load(); }); function buzz() { - if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence - Bangle.buzz(100).then(()=>{ - setTimeout(()=>{ - Bangle.buzz(100).then(function() { - if (buzzCount--) - setTimeout(buzz, 3000); - else if(alarm.as) { // auto-snooze - buzzCount = 10; - setTimeout(buzz, 600000); - } - }); - },100); + require("buzz").pattern(alarm.vibrate===undefined?"..":alarm.vibrate).then(function() { + if (buzzCount--) + setTimeout(buzz, 3000); + else if(alarm.as) { // auto-snooze + buzzCount = 10; + setTimeout(buzz, 600000); + } }); } + if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; buzz(); } diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 4968fc95b..e83e1c3d5 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -87,7 +87,8 @@ function editAlarm(alarmIndex, alarm) { rp : true, as : false, dow : 0b1111111, - last : 0 + last : 0, + vibrate : ".." } if (!newAlarm) Object.assign(a, alarms[alarmIndex]); if (alarm) Object.assign(a,alarm); @@ -118,6 +119,7 @@ function editAlarm(alarmIndex, alarm) { value: "SMTWTFS".split("").map((d,n)=>a.dow&(1< editDOW(a.dow, d=>{a.dow=d;editAlarm(alarmIndex,a)}) }, + /*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ), /*LANG*/'Auto snooze': { value: a.as, format: v=>v?"Yes":"No", @@ -151,7 +153,8 @@ function editTimer(alarmIndex, alarm) { rp : false, as : false, dow : 0b1111111, - last : 0 + last : 0, + vibrate : ".." } if (!newAlarm) Object.assign(a, alarms[alarmIndex]); if (alarm) Object.assign(a,alarm); @@ -172,7 +175,8 @@ function editTimer(alarmIndex, alarm) { value: a.on, format: v=>v?"On":"Off", onchange: v=>a.on=v - } + }, + /*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ), }; menu[/*LANG*/"Save"] = function() { a.timer = encodeTime(t); diff --git a/apps/messages/settings.js b/apps/messages/settings.js index 589d603da..7449b473b 100644 --- a/apps/messages/settings.js +++ b/apps/messages/settings.js @@ -15,18 +15,10 @@ require('Storage').writeJSON("messages.settings.json", settings); } - var vibPatterns = [/*LANG*/"Off", ".", "-", "--", "-.-", "---"]; var mainmenu = { "" : { "title" : /*LANG*/"Messages" }, "< Back" : back, - /*LANG*/'Vibrate': { - value: Math.max(0,vibPatterns.indexOf(settings().vibrate)), - min: 0, max: vibPatterns.length, - format: v => vibPatterns[v]||"Off", - onchange: v => { - updateSetting("vibrate", vibPatterns[v]); - } - }, + /*LANG*/'Vibrate': require("buzz_menu").pattern(settings().vibrate, v => updateSetting("vibrate", v) }), /*LANG*/'Repeat': { value: settings().repeat, min: 0, max: 10, diff --git a/apps/messages/widget.js b/apps/messages/widget.js index 7abb415c3..3ac726e77 100644 --- a/apps/messages/widget.js +++ b/apps/messages/widget.js @@ -32,14 +32,7 @@ draw:function() { Bangle.drawWidgets(); },buzz:function() { if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return; // never buzz during Quiet Mode - let v = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || "."; - function b() { - var c = v[0]; - v = v.substr(1); - if (c==".") Bangle.buzz().then(()=>setTimeout(b,100)); - if (c=="-") Bangle.buzz(500).then(()=>setTimeout(b,100)); - } - b(); + require("buzz").pattern((require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || "."); },touch:function(b,c) { var w=WIDGETS["messages"]; if (!w||!w.width||c.xw.x+w.width||c.yw.y+w.iconwidth) return; diff --git a/modules/buzz.js b/modules/buzz.js new file mode 100644 index 000000000..488d0228d --- /dev/null +++ b/modules/buzz.js @@ -0,0 +1,14 @@ +/* Call this with a pattern like '.-.', '.. .' or '..' to buzz that pattern +out on the internal vibration motor. use buzz_menu to display a menu +where the patterns can be chosen. */ +exports.pattern = pattern => new Promise(resolve => { + function b() { + if (pattern=="") resolve(); + var c = pattern[0]; + pattern = pattern.substr(1); + if (c==".") Bangle.buzz().then(()=>setTimeout(b,100)); + else if (c=="-") Bangle.buzz(500).then(()=>setTimeout(b,100)); + else setTimeout(b,100); + } + b(); +}); diff --git a/modules/buzz_menu.js b/modules/buzz_menu.js new file mode 100644 index 000000000..64b225343 --- /dev/null +++ b/modules/buzz_menu.js @@ -0,0 +1,13 @@ +/* Display a menu to select from various vibration patterns for use with buzz.js */ + +exports.pattern = function(value, callback) { + var vibPatterns = ["", ".", "..", "-", "--", "-.-", "---"]; + return { + value: Math.max(0,vibPatterns.indexOf(value)), + min: 0, max: vibPatterns.length, + format: v => vibPatterns[v]||/*LANG*/"Off", + onchange: v => { + callback(vibPatterns[v]); + } + }; +} From 74203945a291f54237dca6e8063825012be3abbd Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 1 Apr 2022 13:50:06 +0100 Subject: [PATCH 05/10] oops --- apps/messages/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/messages/settings.js b/apps/messages/settings.js index 7449b473b..754347f19 100644 --- a/apps/messages/settings.js +++ b/apps/messages/settings.js @@ -18,7 +18,7 @@ var mainmenu = { "" : { "title" : /*LANG*/"Messages" }, "< Back" : back, - /*LANG*/'Vibrate': require("buzz_menu").pattern(settings().vibrate, v => updateSetting("vibrate", v) }), + /*LANG*/'Vibrate': require("buzz_menu").pattern(settings().vibrate, v => updateSetting("vibrate", v)), /*LANG*/'Repeat': { value: settings().repeat, min: 0, max: 10, From 5c84ec9e2cdace86854666d1cef23be61491e7d8 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 4 Apr 2022 15:49:45 +0100 Subject: [PATCH 06/10] 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); From 25aafe1ccf3efebb81b6406ed3a8e2e2f2b1465c Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 4 Apr 2022 16:58:17 +0100 Subject: [PATCH 07/10] more refactoring to try and reduce code duplication --- apps/alarm/app.js | 6 +++--- apps/alarm/metadata.json | 2 +- apps/qalarm/metadata.json | 2 +- apps/sched/boot.js | 14 ++++++-------- apps/sched/lib.js | 19 ++++++++++++++----- apps/sched/sched.js | 37 +++++++++---------------------------- 6 files changed, 34 insertions(+), 46 deletions(-) diff --git a/apps/alarm/app.js b/apps/alarm/app.js index d5379f469..45edd83f5 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -1,8 +1,8 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); -var alarms = require("Storage").readJSON("sched.json",1)||[]; -// An array of alarm objects (see README.md) +var alarms = require("sched").getAlarms(); +// An array of alarm objects (see sched/README.md) // time in ms -> { hrs, mins } function decodeTime(t) { @@ -31,7 +31,7 @@ function getCurrentTime() { } function saveAndReload() { - require("Storage").write("sched.json",JSON.stringify(alarms)); + require("sched").setAlarms(alarms); require("sched").reload(); } diff --git a/apps/alarm/metadata.json b/apps/alarm/metadata.json index 726e2461c..fe82e04c9 100644 --- a/apps/alarm/metadata.json +++ b/apps/alarm/metadata.json @@ -3,7 +3,7 @@ "name": "Alarm & Timer", "shortName": "Alarms", "version": "0.17", - "description": "Set and respond to alarms and timers", + "description": "Set alarms and timers on your Bangle", "icon": "app.png", "tags": "tool,alarm,widget", "supports": ["BANGLEJS","BANGLEJS2"], diff --git a/apps/qalarm/metadata.json b/apps/qalarm/metadata.json index 326ba33a7..2039af4bf 100644 --- a/apps/qalarm/metadata.json +++ b/apps/qalarm/metadata.json @@ -4,7 +4,7 @@ "shortName": "Q Alarm", "icon": "app.png", "version": "0.04", - "description": "Alarm and timer app with days of week and 'hard' option.", + "description": "[Not recommended - use 'Alarm & Timer' app] Alarm and timer app with days of week and 'hard' option.", "tags": "tool,alarm,widget", "supports": ["BANGLEJS", "BANGLEJS2"], "storage": [ diff --git a/apps/sched/boot.js b/apps/sched/boot.js index 15c332ad7..c772a135e 100644 --- a/apps/sched/boot.js +++ b/apps/sched/boot.js @@ -1,8 +1,8 @@ // check for alarms -(function() { - if (Bangle.ALARM) { - clearTimeout(Bangle.ALARM); - delete Bangle.ALARM; +(function() { // run in closure to ensure allocated vars get removed + if (Bangle.SCHED) { + clearTimeout(Bangle.SCHED); + delete Bangle.SCHED; } var alarms = require('Storage').readJSON('sched.json',1)||[]; var time = new Date(); @@ -18,10 +18,8 @@ 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); + Bangle.SCHED = 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)); + Bangle.SCHED = setTimeout('eval(require("Storage").read("sched.boot.js"))', 86400000 - (Date.now()%86400000)); } })(); diff --git a/apps/sched/lib.js b/apps/sched/lib.js index 9d9744b1f..4b645aede 100644 --- a/apps/sched/lib.js +++ b/apps/sched/lib.js @@ -2,15 +2,24 @@ exports.getAlarms = function() { return require("Storage").readJSON("sched.json",1)||[]; }; +// Write a list of alarms back to storage +exports.setAlarms = function(alarms) { + return require("Storage").writeJSON("sched.json",alarms); +}; // Return an alarm object based on ID exports.getAlarm = function(id) { - var alarms = require("Storage").readJSON("sched.json",1)||[]; - return alarms.find(a=>a.id==id); + return exports.getAlarms().find(a=>a.id==id); }; +// Given a list of alarms from getAlarms, return a list of active alarms for the given time (or current time if time not specified) +exports.getActiveAlarms = function(alarms, time) { + if (!time) time = new Date(); + var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000) + +10000;// get current time - 10s in future to ensure we alarm if we've started the app a tad early + return alarms.filter(a=>a.on&&(a.ta.t-b.t); +} // Set an alarm object based on ID. Leave 'alarm' undefined to remove it exports.setAlarm = function(id, alarm) { - var alarms = require("Storage").readJSON("sched.json",1)||[]; - alarms = alarms.filter(a=>a.id!=id); + var alarms = exports.getAlarms().filter(a=>a.id!=id); if (alarm !== undefined) { alarm.id = id; if (alarm.dow===undefined) alarm.dow = 0b1111111; @@ -22,7 +31,7 @@ exports.setAlarm = function(id, alarm) { } } alarms.push(alarm); - require("Storage").writeJSON("sched.json", alarms); + exports.setAlarms(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) { diff --git a/apps/sched/sched.js b/apps/sched/sched.js index f42a15fc3..a19c85717 100644 --- a/apps/sched/sched.js +++ b/apps/sched/sched.js @@ -1,8 +1,8 @@ // Chances are boot0.js got run already and scheduled *another* // 'load(sched.js)' - so let's remove it first! -if (Bangle.ALARM) { - clearInterval(Bangle.ALARM); - delete Bangle.ALARM; +if (Bangle.SCHED) { + clearInterval(Bangle.SCHED); + delete Bangle.SCHED; } // time in ms -> { hrs, mins } @@ -12,25 +12,11 @@ function decodeTime(t) { return { hrs : hrs, mins : Math.round((t-hrs*3600000)/60000) }; } -// time in { hrs, mins } -> ms -function encodeTime(o) { - return o.hrs*3600000 + o.mins*60000; -} - function formatTime(t) { var o = decodeTime(t); return o.hrs+":"+("0"+o.mins).substr(-2); } -function getCurrentTime() { - var time = new Date(); - return ( - time.getHours() * 3600000 + - time.getMinutes() * 60000 + - time.getSeconds() * 1000 - ); -} - function showAlarm(alarm) { var msg = alarm.timer ? formatTime(alarm.timer) : formatTime(alarm.t); var buzzCount = 10; @@ -54,7 +40,8 @@ function showAlarm(alarm) { } if (!alarm.rp) alarm.on = false; } - require("Storage").write("sched.json",JSON.stringify(alarms)); + // alarm is still a member of 'alarms', so writing to array writes changes back directly + require("sched").setAlarms(alarms); load(); }); function buzz() { @@ -72,15 +59,9 @@ 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("sched.json",1)||[]; -var active = alarms.filter(a=>a.on&&(a.ta.t-b.t); +var alarms = require("sched").getAlarms(); +var active = require("sched").getActiveAlarms(alarms); +if (active.length) // if there's an alarm, show it showAlarm(active[0]); -} else { - // otherwise just go back to default app +else // otherwise just go back to default app setTimeout(load, 100); -} From 67facc692db64059d92bdc37b964396a267022e8 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 5 Apr 2022 10:59:13 +0100 Subject: [PATCH 08/10] fix https://github.com/espruino/BangleApps/issues/1555#issuecomment-1087784581 --- apps/sched/lib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sched/lib.js b/apps/sched/lib.js index 4b645aede..e517413c3 100644 --- a/apps/sched/lib.js +++ b/apps/sched/lib.js @@ -29,8 +29,8 @@ exports.setAlarm = function(id, alarm) { var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000); alarm.t = currentTime + alarm.timer; } + alarms.push(alarm); } - alarms.push(alarm); exports.setAlarms(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 From 333d4053424da271ec09edd5f64d0fc32c9c91b7 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 6 Apr 2022 09:13:35 +0100 Subject: [PATCH 09/10] sleep -> snooze, and add images for alarms/timers --- apps/sched/sched.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/sched/sched.js b/apps/sched/sched.js index a19c85717..4fdc82736 100644 --- a/apps/sched/sched.js +++ b/apps/sched/sched.js @@ -18,15 +18,22 @@ function formatTime(t) { } function showAlarm(alarm) { - var msg = alarm.timer ? formatTime(alarm.timer) : formatTime(alarm.t); - var buzzCount = 10; - if (alarm.msg) + var msg = ""; + msg += alarm.timer ? formatTime(alarm.timer) : formatTime(alarm.t); + if (alarm.msg) { msg += "\n"+alarm.msg; + } else { + if (alarm.timer) + msg = atob("ACQswgD//33vRcGHIQAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAAAP/wAAAAAAAAAP/wAAAAAAAAAqqoAPAAAAAAqqqqoP8AAAAKqqqqqv/AAACqqqqqqq/wAAKqqqlWqqvwAAqqqqlVaqrAACqqqqlVVqqAAKqqqqlVVaqgAKqaqqlVVWqgAqpWqqlVVVqoAqlWqqlVVVaoCqlV6qlVVVaqCqVVfqlVVVWqCqVVf6lVVVWqKpVVX/lVVVVqqpVVV/+VVVVqqpVVV//lVVVqqpVVVfr1VVVqqpVVVfr1VVVqqpVVVb/lVVVqqpVVVW+VVVVqqpVVVVVVVVVqiqVVVVVVVVWqCqVVVVVVVVWqCqlVVVVVVVaqAqlVVVVVVVaoAqpVVVVVVVqoAKqVVVVVVWqgAKqlVVVVVaqgACqpVVVVVqqAAAqqlVVVaqoAAAKqqVVWqqgAAACqqqqqqqAAAAAKqqqqqgAAAAAAqqqqoAAAAAAAAqqoAAAAA==")+" "+msg; + else + msg = atob("AC0swgF97///RcEpMlVVVVVVf9VVVVVVVVX/9VVf9VVf/1VVV///1Vf9VX///VVX///VWqqlV///1Vf//9aqqqqpf//9V///2qqqqqqn///V///6qqqqqqr///X//+qqoAAKqqv//3//6qoAAAAKqr//3//qqAAAAAAqq//3/+qoAADwAAKqv/3/+qgAADwAACqv/3/aqAAADwAAAqp/19qoAAADwAAAKqfV1qgAAADwAAACqXVWqgAAADwAAACqlVWqAAAADwAAAAqlVWqAAAADwAAAAqlVWqAAAADwAAAAqlVaoAAAADwAAAAKpVaoAAAADwAAAAKpVaoAAAADwAAAAKpVaoAAAAOsAAAAKpVaoAAAAOsAAAAKpVaoAAAAL/AAAAKpVaoAAAAgPwAAAKpVaoAAACAD8AAAKpVWqAAAIAA/AAAqlVWqAAAgAAPwAAqlVWqAACAAADwAAqlVWqgAIAAAAAACqlVVqgAgAAAAAACqVVVqoAAAAAAAAKqVVVaqAAAAAAAAqpVVVWqgAAAAAACqlVVVWqoAAAAAAKqlVVVVqqAAAAAAqqVVVVVaqoAAAAKqpVVVVVeqqoAAKqqtVVVVV/6qqqqqqr/VVVVX/2qqqqqqn/1VVVf/VaqqqqpV/9VVVf9VVWqqlVVf9VVVf1VVVVVVVVX9VQ==")+" "+msg; + } Bangle.loadWidgets(); Bangle.drawWidgets(); + var buzzCount = 10; E.showPrompt(msg,{ title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!", - buttons : {/*LANG*/"Sleep":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins + buttons : {/*LANG*/"Snooze":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins }).then(function(sleep) { buzzCount = 0; if (sleep) { From 486586022ecd38d391879472cce78284675a9295 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 6 Apr 2022 11:45:14 +0100 Subject: [PATCH 10/10] docs --- apps/sched/README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/sched/README.md b/apps/sched/README.md index 03d93c688..54c61679c 100644 --- a/apps/sched/README.md +++ b/apps/sched/README.md @@ -19,6 +19,7 @@ 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 + appid : "myappid", // optional app ID for alarms that you set/use for your app 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 @@ -32,7 +33,7 @@ Alarms are stored in an array in `sched.json`, and take the form: 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 + 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) @@ -41,7 +42,7 @@ Alarms are stored in an array in `sched.json`, and take the form: 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 + data : { ... } // OPTIONAL - your app can store custom data in here if needed (don't store a lot of data here) } ``` @@ -73,10 +74,16 @@ require("sched").reload(); // loading the settings app. The alarm will not be removed/stopped // automatically. require("sched").setAlarm("customrunner", { + appid : "myapp", js : "load('setting.app.js')", timer : 1*60*1000, // 1 Minute }); + +// If you have been specifying `appid` you can also find any alarms that +// your app has created with the following: +require("sched").getAlarms().filter(a=>a.appid=="myapp"); ``` If your app requires alarms, you can specify that the alarms app needs to -be installed by adding `"dependencies": {"scheduler":"type"},` to your metadata. +be installed by adding `"dependencies": {"scheduler":"type"},` to your app's +metadata.