diff --git a/apps.json b/apps.json index 1bb88f882..e57b605c4 100644 --- a/apps.json +++ b/apps.json @@ -2490,5 +2490,23 @@ {"name":"HRV.app.js","url":"app.js"}, {"name":"HRV.img","url":"app-icon.js","evaluate":true} ] +}, +{ "id": "hardalarm", + "name": "Hard Alarm", + "shortName":"HardAlarm", + "icon": "app.png", + "version":"0.01", + "description": "Count to the right number to turn off the alarm", + "tags": "tool,alarm,widget", + "storage": [ + {"name":"hardalarm.app.js","url":"app.js"}, + {"name":"hardalarm.boot.js","url":"boot.js"}, + {"name":"hardalarm.js","url":"hardalarm.js"}, + {"name":"hardalarm.img","url":"app-icon.js","evaluate":true}, + {"name":"hardalarm.wid.js","url":"widget.js"} + ], + "data": [ + {"name":"alarm.json"} + ] } ] diff --git a/apps/hardalarm/ChangeLog b/apps/hardalarm/ChangeLog new file mode 100644 index 000000000..b8b4561b8 --- /dev/null +++ b/apps/hardalarm/ChangeLog @@ -0,0 +1 @@ +0.01: Add a number to match to turn off alarm diff --git a/apps/hardalarm/app-icon.js b/apps/hardalarm/app-icon.js new file mode 100644 index 000000000..6def7b58f --- /dev/null +++ b/apps/hardalarm/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkE5gA/AF9cBZXFQYIOGBIUMC5PATgQJFqAIBgovMBwISDAYQ5HGBAAGFxQ/FgMzmcgJ5BIKgAXFIxYuBgMxCIMjmcQgECiBHLFwITBFYIvBiBLBmQwLqECCYMziICBmIeBD4IwKFwQAIGAJGJRQUhgIbDAocQJBHAgYlCOQIrDAoUwhhGIiZZBX4gFEAgIXICIMwX4gFFC45eDF5RgI4pZHAo0gC43AXoaPJC4J4GRwQAMSA4XfmKuBC6kQRYQXMO4YACXYYADO46nDIwcCiBIFU47XDBwcTkBQFa4/MH4sAkYxBBAoWGC4I/DmQ1BdwJPEC5CQEmAECGQKOKMA0gCYRgELxBICCYUyFQZgCJgIWI5lQYIxjCGYMFC5PFLAiKDFwRGJGATCFLoYuKGAYYGiAuMDAkiCoMiCx6SCAAq7IF48F4tQCoMFqAXP4AQF4B1MSAZXFMwIXPA41VC5wA/ADAA==")) diff --git a/apps/hardalarm/app.js b/apps/hardalarm/app.js new file mode 100644 index 000000000..61467b421 --- /dev/null +++ b/apps/hardalarm/app.js @@ -0,0 +1,112 @@ +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +var alarms = require("Storage").readJSON("hardalarm.json",1)||[]; +/*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 + as : false, // auto snooze + } +];*/ + +function formatTime(t) { + var hrs = 0|t; + var mins = Math.round((t-hrs)*60); + return hrs+":"+("0"+mins).substr(-2); +} + +function getCurrentHr() { + var time = new Date(); + return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); +} + +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; + var as = false; + if (!newAlarm) { + var a = alarms[alarmIndex]; + hrs = 0|a.hr; + mins = Math.round((a.hr-hrs)*60); + en = a.on; + repeat = a.rp; + as = a.as; + } + const menu = { + '': { 'title': 'Alarms' }, + 'Hours': { + value: hrs, + onchange: function(v){if (v<0)v=23;if (v>23)v=0;hrs=v;this.value=v;} // no arrow fn -> preserve 'this' + }, + 'Minutes': { + value: mins, + onchange: function(v){if (v<0)v=59;if (v>59)v=0;mins=v;this.value=v;} // no arrow fn -> preserve 'this' + }, + 'Enabled': { + value: en, + format: v=>v?"On":"Off", + onchange: v=>en=v + }, + 'Repeat': { + value: en, + format: v=>v?"Yes":"No", + onchange: v=>repeat=v + }, + 'Auto snooze': { + value: as, + format: v=>v?"Yes":"No", + onchange: v=>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["> Save"] = function() { + if (newAlarm) alarms.push(getAlarm()); + else alarms[alarmIndex] = getAlarm(); + require("Storage").write("hardalarm.json",JSON.stringify(alarms)); + showMainMenu(); + }; + if (!newAlarm) { + menu["> Delete"] = function() { + alarms.splice(alarmIndex,1); + require("Storage").write("hardalarm.json",JSON.stringify(alarms)); + showMainMenu(); + }; + } + menu['< Back'] = showMainMenu; + return E.showMenu(menu); +} + +showMainMenu(); diff --git a/apps/hardalarm/app.png b/apps/hardalarm/app.png new file mode 100644 index 000000000..ed830c5eb Binary files /dev/null and b/apps/hardalarm/app.png differ diff --git a/apps/hardalarm/boot.js b/apps/hardalarm/boot.js new file mode 100644 index 000000000..e327317e3 --- /dev/null +++ b/apps/hardalarm/boot.js @@ -0,0 +1,25 @@ +// check for alarms +(function() { + var alarms = require('Storage').readJSON('hardalarm.json',1)||[]; + var time = new Date(); + var active = alarms.filter(a=>a.on); + 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); + if (!require('Storage').read("hardalarm.js")) { + console.log("No alarm app!"); + require('Storage').write('hardalarm.json',"[]"); + } else { + var t = 3600000*(active[0].hr-hr); + if (active[0].last == time.getDate() || t < 0) t += 86400000; + if (t<1000) t=1000; + /* execute alarm at the correct time. We avoid execing immediately + since this code will get called AGAIN when hardalarm.js is loaded. alarm.js + will then clearInterval() to get rid of this call so it can proceed + normally. */ + setTimeout(function() { + load("hardalarm.js"); + },t); + } + } +})(); diff --git a/apps/hardalarm/hardalarm.js b/apps/hardalarm/hardalarm.js new file mode 100644 index 000000000..c3623a193 --- /dev/null +++ b/apps/hardalarm/hardalarm.js @@ -0,0 +1,127 @@ +// Chances are boot0.js got run already and scheduled *another* +// 'load(hardalarm.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); +} + +function getCurrentHr() { + var time = new Date(); + return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); +} + +function getRandomInt(max) { + return Math.floor(Math.random() * Math.floor(max)); +} + +function getRandomFromRange(lowerRangeMin, lowerRangeMax, higherRangeMin, higherRangeMax) { + var lowerRange = lowerRangeMax - lowerRangeMin; + var higherRange = higherRangeMax - higherRangeMin; + var fullRange = lowerRange + higherRange; + var randomNum = getRandomInt(fullRange); + if(randomNum <= (lowerRangeMax - lowerRangeMin)) { + return randomNum + lowerRangeMin; + } else { + return randomNum + (higherRangeMin - lowerRangeMax); + } +} + +function showNumberPicker(currentGuess, randomNum) { + if(currentGuess == randomNum) { + E.showMessage("" + currentGuess + "\n PRESS ENTER", "Get to " + randomNum); + } else { + E.showMessage("" + currentGuess, "Get to " + randomNum); + } +} + +function showPrompt(msg, buzzCount, alarm) { + E.showPrompt(msg,{ + title:"STAY AWAKE!", + buttons : {"Sleep":0,"Stop":1} // default is sleep so it'll come back in 10 mins + }).then(function(choice) { + buzzCount = 0; + if (choice==0) { + if(alarm.ohr===undefined) alarm.ohr = alarm.hr; + alarm.hr += 10/60; // 10 minutes + require("Storage").write("hardalarm.json",JSON.stringify(alarms)); + load(); + } else if(choice==1) { + alarm.last = (new Date()).getDate(); + if (alarm.ohr!==undefined) { + alarm.hr = alarm.ohr; + delete alarm.ohr; + } + if (!alarm.rp) alarm.on = false; + require("Storage").write("hardalarm.json",JSON.stringify(alarms)); + load(); + } + }); +} + +function showAlarm(alarm) { + var msg = formatTime(alarm.hr); + var buzzCount = 20; + if (alarm.msg) + msg += "\n"+alarm.msg; + var okClicked = false; + var currentGuess = 10; + var randomNum = getRandomFromRange(0, 7, 13, 20); + showNumberPicker(currentGuess, randomNum) + setWatch(o => { + if(!okClicked && currentGuess < 20) { + currentGuess = currentGuess + 1; + showNumberPicker(currentGuess, randomNum); + } + }, BTN1, {repeat: true, edge: 'rising'}); + + setWatch(o => { + if(currentGuess == randomNum) { + okClicked = true; + showPrompt(msg, buzzCount, alarm); + } + }, BTN2, {repeat: true, edge: 'rising'}); + + setWatch(o => { + if(!okClicked && currentGuess > 0) { + currentGuess = currentGuess - 1; + showNumberPicker(currentGuess, randomNum); + } + }, BTN3, {repeat: true, edge: 'rising'}); + + function buzz() { + Bangle.buzz(500).then(()=>{ + setTimeout(()=>{ + Bangle.buzz(500).then(function() { + setTimeout(()=>{ + Bangle.buzz(2000).then(function() { + if (buzzCount--) + setTimeout(buzz, 2000); + else if(alarm.as) { // auto-snooze + buzzCount = 20; + setTimeout(buzz, 600000); // 10 minutes + } + }); + },100); + }); + },100); + }); + } + buzz(); +} + +// 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 alarms = require("Storage").readJSON("hardalarm.json",1)||[]; +var active = alarms.filter(a=>a.on&&(a.hra.hr-b.hr); + showAlarm(active[0]); +} else { + // otherwise just go back to default app + setTimeout(load, 100); +} diff --git a/apps/hardalarm/widget.js b/apps/hardalarm/widget.js new file mode 100644 index 000000000..677266195 --- /dev/null +++ b/apps/hardalarm/widget.js @@ -0,0 +1,11 @@ +(() => { + var alarms = require('Storage').readJSON('hardalarm.json',1)||[]; + alarms = alarms.filter(alarm=>alarm.on); + if (!alarms.length) return; // no alarms, no widget! + delete alarms; + // add the widget + WIDGETS["alarm"]={area:"tl",width:24,draw:function() { + g.setColor(-1); + g.drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y); + }}; +})()