From 47205bfe61b294318c5936a79bbdc9742cc84f91 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 12 Feb 2020 10:48:14 +0000 Subject: [PATCH] Add alarms app (fix #83) --- apps.json | 18 ++++- apps/alarm/ChangeLog | 1 + apps/alarm/app-icon.js | 1 + apps/alarm/app.js | 151 +++++++++++++++++++++++++++++++++++++++ apps/alarm/app.json | 5 ++ apps/alarm/app.png | Bin 0 -> 2274 bytes apps/alarm/widget.js | 17 +++++ apps/boot/ChangeLog | 1 + apps/boot/boot0.js | 29 +++++++- apps/setting/settings.js | 3 + defaultapps.json | 2 +- 11 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 apps/alarm/ChangeLog create mode 100644 apps/alarm/app-icon.js create mode 100644 apps/alarm/app.js create mode 100644 apps/alarm/app.json create mode 100644 apps/alarm/app.png create mode 100644 apps/alarm/widget.js diff --git a/apps.json b/apps.json index 48053e64b..bfea97d09 100644 --- a/apps.json +++ b/apps.json @@ -2,7 +2,7 @@ { "id": "boot", "name": "Bootloader", "icon": "bootloader.png", - "version":"0.03", + "version":"0.04", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "tags": "tool,system", "storage": [ @@ -13,7 +13,7 @@ "sortorder" : -10 }, { "id": "launch", - "name": "Launcher", + "name": "Default Launcher", "icon": "app.png", "version":"0.01", "description": "This is needed by Bangle.js to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.", @@ -66,6 +66,20 @@ ], "sortorder" : -2 }, + { "id": "alarm", + "name": "Default Alarm", + "icon": "app.png", + "version":"0.01", + "description": "Set and respond to alarms", + "tags": "tool,alarm", + "storage": [ + {"name":"+alarm","url":"app.json"}, + {"name":"-alarm","url":"app.js"}, + {"name":"@alarm","content":"[]"}, + {"name":"*alarm","url":"app-icon.js","evaluate":true}, + {"name":"=alarm","url":"widget.js"} + ] + }, { "id": "wclock", "name": "Word Clock", "icon": "clock-word.png", diff --git a/apps/alarm/ChangeLog b/apps/alarm/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/alarm/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/alarm/app-icon.js b/apps/alarm/app-icon.js new file mode 100644 index 000000000..05515e859 --- /dev/null +++ b/apps/alarm/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/alarm/app.js b/apps/alarm/app.js new file mode 100644 index 000000000..153a6ef3b --- /dev/null +++ b/apps/alarm/app.js @@ -0,0 +1,151 @@ +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +var alarms = require("Storage").readJSON("@alarm")||[]; +/*alarms = [ + { on : true, + hr : 6.5, // hours + minutes/60 + 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 + } +];*/ + +function formatTime(t) { + var hrs = 0|t; + var mins = 0|((t-hrs)*60); + return hrs+":"+("0"+mins).substr(-2); +} + +function getCurrentHr() { + var time = new Date(); + return time.getHours()+(time.getMinutes()/60); +} + +function showMainMenu() { + const menu = { + '': { 'title': 'Alarms' }, + 'New Alarm': ()=>editAlarm(-1) + }; + alarms.forEach((alarm,idx)=>{ + txt = (alarm.on?"on ":"off ")+formatTime(alarm.hr); + if (alarm.rp) txt += " (repeat)"; + menu[txt] = function() { + editAlarm(idx); + }; + }); + menu['< Back'] = ()=>{load();}; + return E.showMenu(menu); +} + +function editAlarm(alarmIndex) { + var newAlarm = alarmIndex<0; + var hrs = 12; + var mins = 0; + var en = true; + var repeat = true; + if (!newAlarm) { + var a = alarms[alarmIndex]; + hrs = 0|a.hr; + mins = 0|((a.hr-hrs)*60); + en = a.on; + repeat = a.rp; + } + const menu = { + '': { 'title': 'Alarms' }, + 'Hours': { + value: hrs, + min: 0, + max: 23, + onchange: v=>hrs=v + }, + 'Minutes': { + value: mins, + min: 0, + max: 60, + onchange: v=>mins=v + }, + 'Enabled': { + value: en, + format: v=>v?"On":"Off", + onchange: v=>en=v + }, + 'Repeat': { + value: en, + format: v=>v?"Yes":"No", + onchange: v=>repeat=v + } + }; + function getAlarm() { + var hr = hrs+(mins/60); + var day = 0; + // If alarm is for tomorrow not today, set day + if (hr > getCurrentHr()) + day = (new Date()).getDate(); + // Save alarm + return { + on : en, hr : hr, + last : day, rp : repeat + }; + } + if (newAlarm) { + menu["> New Alarm"] = function() { + alarms.push(getAlarm()); + require("Storage").write("@alarm",JSON.stringify(alarms)); + showMainMenu(); + }; + } else { + menu["> Save"] = function() { + alarms[alarmIndex] = getAlarm(); + require("Storage").write("@alarm",JSON.stringify(alarms)); + showMainMenu(); + }; + } + menu['< Back'] = showMainMenu; + return E.showMenu(menu); +} + +function showAlarm(alarm) { + var msg = formatTime(alarm.hr); + var buzzCount = 10; + if (alarm.msg) + msg += "\n"+alarm.msg; + E.showPrompt(msg,{ + title:"ALARM!", + buttons : {"Sleep":true,"Ok":false} // default is sleep so it'll come back in 10 mins + }).then(function(sleep) { + buzzCount = 0; + if (sleep) { + alarm.hr += 10/60; // 10 minutes + } else { + alarm.last = (new Date()).getDate(); + if (!alarm.rp) alarm.on = false; + } + require("Storage").write("@alarm",JSON.stringify(alarms)); + load(); + }); + function buzz() { + Bangle.buzz(100).then(()=>{ + setTimeout(()=>{ + Bangle.buzz(100).then(function() { + if (buzzCount--) + setTimeout(buzz, 3000); + }); + },100); + }); + } + buzz(); +} + +// Check for alarms +var day = (new Date()).getDate(); +var hr = getCurrentHr(); +var active = alarms.filter(a=>a.on&&(a.hra.hr-b.hr); + showAlarm(active[0]); +} else { + // otherwise show the main menu + showMainMenu(); +} diff --git a/apps/alarm/app.json b/apps/alarm/app.json new file mode 100644 index 000000000..25b8c6a00 --- /dev/null +++ b/apps/alarm/app.json @@ -0,0 +1,5 @@ +{ + "name":"Alarms", + "icon":"*alarm", + "src":"-alarm" +} diff --git a/apps/alarm/app.png b/apps/alarm/app.png new file mode 100644 index 0000000000000000000000000000000000000000..72f364144eeada1ad6dc777ca4bfe9adf8f45794 GIT binary patch literal 2274 zcmV<82p#u{P)Gp-uv5 z{H)R%8l#SM8Xb*ko$1h-IMZnzbvnVsp>)!Kt;VqkX(q5ZN@gms1iun3vOu)~qVln@ z>;k*&-h283cfEV}?guE&blT_Fp67kfd7tN=yXV|{F7RItnyrB|atoNaZLF`Em0HSx zB7XE*CU|Zu2$=wuGB5^ECxJDC@UgM3_N%8MB0YEA3IbmQke-1h5r_bEgTY9^Us?<` zMt^-}ugZ(7Uth{8TqgnK2B^ybR2j2Y?m%g&APt5z1Er;0M)UQL0BrZCwO@c`f?Vq043YX9~LpjeokoX!EO7?27e8NdPnk&kk10KNs# zLBLg>=u&ojdn*A#W?ue!#^?-yIYH_TGUD=E>#Cdm8pz0bbs0EC3jj?h;{VY5uP=G0 zb1x&>4q#!BK2r1osN!6%vbU3-OApb+5C@joTwmMeCOEG!r>F=;e5}i{^VmI8uFK`> zVnl~#ARYh~0eDwXD!Y&UyEA9L)A3UVID+)i@(h0z6P_7J&nw6!hPq(lUm7)+tsfrv z34$A)E{4v>#C`maXiW)%%>-j>jE33xz_w5Q`TfFKXny#`%hr*%<$|1AjYz#7 z>a;Y3B_%-_9}h)T6aav?TH$)|0QSyKjJLJH*3<-Hd|Wz&_VwX`eX+dDa3N;?sz4@) zkXTj*rB>?|%d}ZuC>ZX)1if-I% z8S8TFTsPFeUEE8Rkbp(Uk3$&~^R%>kpqxJ+2}h4YA(^p!bN|loIvu+qnh64Z>Hh68 zzzC<^;)&Rn!+rPkB-)KhrNZ~#dv8|6OHD`?1I@4c?!HLGwzy3&@NTV-4YX#itO4N) zBWym=_0LuSJH(^J?b?O#Wy@wwyc^-k$>Zk75<6Ee- zREV%!R=3vGR5OL51c1sNnv;-#m`$5zUA!Bbty`tHl8WQ`J+1Y%pG7zI7YNh%*PfL&3LJt3Al&|#1$7y>7q{nMPp;5-78m`)s&o^fvM+|sRXqe(K$J@ zA~X=@tz8Rl?p!goaFI@zD$VmTzyYho?MS^o0<= zv@~hTGlNX+;4=U~CvHeLmuCr?+TWKFat0QN8*>&ckVy#+gTdhWeevSO8I2*W%_Q0Y z`Iq`gaf4HX2ss#UI>DK!oosazkYpM;|Mk{DPVwAnj>f6 zzPRDMe_tlWKUiNMCVzVbdB;znqq7T_FJFd6qk+X@!TR;^YK!`|5$ zazVFv)xkrc#Ki!Bl#~=yS69!-gu}OXhm`O!KtP+gJ>J$PO$k}N8#6UACaKMce8l(| z;E67Y+qNrL!0dKun!NY}@A!xpn>c^|JYr&EV6j-RVZ#Ot4GsAp({9IDbF(+e^OvM~ zJ_fSe+gll9o;Mz0VgjQ9)^RWKil=5)ebSt+GY6X0_OE4vSH zE>{@<&oRMdPY;IAo|PAW@ZFHbyAkNh1AzI=8BF%|i75vJp-fh*9GThG)eE9OiMfID za!lO5{g{Z3hU(BSL5YbW$_@<^w{BsmqC!fS>rJLUxqSaSF~=Qdz@WRyCMVH*=n!1} z{Qv-piUKW4cyh%exCRH&cla;_?=zwyl_31auS|ad{q5Uk5dB=t@n$o64jk~9c$#qa z_oHXuKDY)3yaSBwNi&%S<$DEr7RqjKKLg;Dm~(V>;oci>VEo37Cy?uniCedDx40ON z?rv$QOyALQE>Pc~FS@ydgP%uhwJ(4u+06-dJFE>2V1j^%<;y|JfM2Tof;pWqpFWKT zM~?VB_W=0J5g)(f&-d>6tUO|b^x>^7HFd7jW;@4-)=FavjRtYW#hABtEhs!ZG~sqT zEcNv;S5-+@8#n&0QmZ$oUA^jage^s=Uk)idC;w#ONdN9^>xgUu7ZCxqUJrF@D#CR- z`Q!DD4vby9hOwq5=_cjDmB{a~>X^8XnzB>hl>7EJ69bg7rr-k*b^*Z3aVz`rz_+7H zU|tA)Lk3UWNR>8z-VJrslqQB#MnlaWOr6{MMX(nd%2-qIzFRy1T$>wJVT*`ZMvR?A z@J~XazyaWW2NRNRsH09ku8duoYx2ucS{hJez!}KMEj$6RUw8<~RcX@c7J}nS7-QRr zXj+>BW2}dWsvrnuqD{(cwjX7lFxJ=Zo3??C8#gMx8J%>%y;233QU-9%I$heMbDLC5 zogOTN_h;5#047G%2A;p9H<_-=J=@5ba3w~o!q1&M$4|=u0Ho*UpCr&05GssyHSfrY wpCwR6Ucv9c@G~&{)o7^M`;6-TAOCy!5BoM;$iCIvNdN!<07*qoM6N<$f`#WsoB#j- literal 0 HcmV?d00001 diff --git a/apps/alarm/widget.js b/apps/alarm/widget.js new file mode 100644 index 000000000..9a8bb01e7 --- /dev/null +++ b/apps/alarm/widget.js @@ -0,0 +1,17 @@ +(() => { + var alarms = require('Storage').readJSON('@alarm')||[]; + alarms = alarms.filter(alarm=>alarm.on); + if (!alarms.length) return; + delete alarms; + // add the width + var xpos = WIDGETPOS.tl; + WIDGETPOS.tl += 24;/* the widget width plus some extra pixel to keep distance to others */; + + // draw your widget at xpos + + // add the widget + WIDGETS["alarm"]={draw:function() { + g.setColor(-1); + g.drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),xpos,0); + }}; +})() diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index e1bb12a32..bfbbfd5cc 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -1,2 +1,3 @@ 0.02: Attempt to reset state of the interpreter better before loading an app 0.03: Fix issue switching clockfaces via menu +0.04: Add alarm functionality diff --git a/apps/boot/boot0.js b/apps/boot/boot0.js index fb6fa128a..ecd8dc550 100644 --- a/apps/boot/boot0.js +++ b/apps/boot/boot0.js @@ -19,6 +19,30 @@ Bangle.setLCDTimeout(s.timeout); if (!s.timeout) Bangle.setLCDPower(1); E.setTimeZone(s.timezone); delete s; +// check for alarms +function checkAlarm() { + var alarms = require('Storage').readJSON('@alarm')||[]; + var time = new Date(); + var active = alarms.filter(a=>a.on&&(a.last!=time.getDate())); + if (active.length) { + active = active.sort((a,b)=>a.hr-b.hr); + var hr = time.getHours()+(time.getMinutes()/60); + if (!require('Storage').read("-alarm")) { + console.log("No alarm app!"); + require('Storage').write('@alarm',"[]") + } else { + if (active[0].hr < hr) { + // fire alarm now + load("-alarm"); + } else { + // execute alarm at the correct time + setTimeout(function() { + load("-alarm"); + },3600000*(active[0].hr-hr)); + } + } + } +} // check to see if our clock is wrong - if it is use GPS time if ((new Date()).getFullYear()==1970) { console.log("Searching for GPS time"); @@ -32,9 +56,12 @@ if ((new Date()).getFullYear()==1970) { } setTime(g.time.getTime()/1000); console.log("GPS time",g.time.toString()); + checkAlarm(); }); Bangle.setGPSPower(1); -} +} else checkAlarm(); +delete checkAlarm; +// Check for // All of this is just shim for older Bangles if (!Bangle.loadWidgets) { Bangle.loadWidgets = function(){ diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 86d2fec79..9c7a490fb 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -1,3 +1,6 @@ +Bangle.loadWidgets(); +Bangle.drawWidgets(); + const storage = require('Storage'); let settings; diff --git a/defaultapps.json b/defaultapps.json index 11db89641..6f6c4aa00 100644 --- a/defaultapps.json +++ b/defaultapps.json @@ -1 +1 @@ -["boot","launch","mclock","setting","sbat","sbt"] +["boot","launch","mclock","setting","alarm","sbat","sbt"]