Merge remote-tracking branch 'upstream/new_alarm'
commit
64262c6410
|
|
@ -14,3 +14,4 @@
|
||||||
0.13: Alarm widget state now updates when setting/resetting an alarm
|
0.13: Alarm widget state now updates when setting/resetting an alarm
|
||||||
0.14: Order of 'back' menu item
|
0.14: Order of 'back' menu item
|
||||||
0.15: Fix hour/minute wrapping code for new menu system
|
0.15: Fix hour/minute wrapping code for new menu system
|
||||||
|
0.16: Adding alarm library
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
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 (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.
|
||||||
|
|
@ -1,20 +1,38 @@
|
||||||
// Chances are boot0.js got run already and scheduled *another*
|
// Chances are boot0.js got run already and scheduled *another*
|
||||||
// 'load(alarm.js)' - so let's remove it first!
|
// 'load(alarm.js)' - so let's remove it first!
|
||||||
clearInterval();
|
if (Bangle.ALARM) {
|
||||||
|
clearInterval(Bangle.ALARM);
|
||||||
function formatTime(t) {
|
delete Bangle.ALARM;
|
||||||
var hrs = 0|t;
|
|
||||||
var mins = Math.round((t-hrs)*60);
|
|
||||||
return hrs+":"+("0"+mins).substr(-2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
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) {
|
function showAlarm(alarm) {
|
||||||
var msg = formatTime(alarm.hr);
|
var msg = alarm.timer ? formatTime(alarm.timer) : formatTime(alarm.t);
|
||||||
var buzzCount = 10;
|
var buzzCount = 10;
|
||||||
if (alarm.msg)
|
if (alarm.msg)
|
||||||
msg += "\n"+alarm.msg;
|
msg += "\n"+alarm.msg;
|
||||||
|
|
@ -26,13 +44,13 @@ function showAlarm(alarm) {
|
||||||
}).then(function(sleep) {
|
}).then(function(sleep) {
|
||||||
buzzCount = 0;
|
buzzCount = 0;
|
||||||
if (sleep) {
|
if (sleep) {
|
||||||
if(alarm.ohr===undefined) alarm.ohr = alarm.hr;
|
if(alarm.ot===undefined) alarm.ot = alarm.t;
|
||||||
alarm.hr += 10/60; // 10 minutes
|
alarm.t += 10*60*1000; // 10 minutes
|
||||||
} else {
|
} else {
|
||||||
alarm.last = (new Date()).getDate();
|
alarm.last = (new Date()).getDate();
|
||||||
if (alarm.ohr!==undefined) {
|
if (alarm.ot!==undefined) {
|
||||||
alarm.hr = alarm.ohr;
|
alarm.t = alarm.ot;
|
||||||
delete alarm.ohr;
|
delete alarm.ot;
|
||||||
}
|
}
|
||||||
if (!alarm.rp) alarm.on = false;
|
if (!alarm.rp) alarm.on = false;
|
||||||
}
|
}
|
||||||
|
|
@ -40,10 +58,7 @@ function showAlarm(alarm) {
|
||||||
load();
|
load();
|
||||||
});
|
});
|
||||||
function buzz() {
|
function buzz() {
|
||||||
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence
|
require("buzz").pattern(alarm.vibrate===undefined?"..":alarm.vibrate).then(function() {
|
||||||
Bangle.buzz(100).then(()=>{
|
|
||||||
setTimeout(()=>{
|
|
||||||
Bangle.buzz(100).then(function() {
|
|
||||||
if (buzzCount--)
|
if (buzzCount--)
|
||||||
setTimeout(buzz, 3000);
|
setTimeout(buzz, 3000);
|
||||||
else if(alarm.as) { // auto-snooze
|
else if(alarm.as) { // auto-snooze
|
||||||
|
|
@ -51,20 +66,19 @@ function showAlarm(alarm) {
|
||||||
setTimeout(buzz, 600000);
|
setTimeout(buzz, 600000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},100);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return;
|
||||||
buzz();
|
buzz();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for alarms
|
// Check for alarms
|
||||||
var day = (new Date()).getDate();
|
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 alarms = require("Storage").readJSON("alarm.json",1)||[];
|
||||||
var active = alarms.filter(a=>a.on&&(a.hr<hr)&&(a.last!=day));
|
var active = alarms.filter(a=>a.on&&(a.t<currentTime)&&(a.last!=day));
|
||||||
if (active.length) {
|
if (active.length) {
|
||||||
// if there's an alarm, show it
|
// if there's an alarm, show it
|
||||||
active = active.sort((a,b)=>a.hr-b.hr);
|
active = active.sort((a,b)=>a.t-b.t);
|
||||||
showAlarm(active[0]);
|
showAlarm(active[0]);
|
||||||
} else {
|
} else {
|
||||||
// otherwise just go back to default app
|
// otherwise just go back to default app
|
||||||
|
|
|
||||||
|
|
@ -2,35 +2,42 @@ Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
var alarms = require("Storage").readJSON("alarm.json",1)||[];
|
var alarms = require("Storage").readJSON("alarm.json",1)||[];
|
||||||
/*alarms = [
|
// An array of alarm objects (see README.md)
|
||||||
{ on : true,
|
|
||||||
hr : 6.5, // hours + minutes/60
|
// time in ms -> { hrs, mins }
|
||||||
msg : "Eat chocolate",
|
function decodeTime(t) {
|
||||||
last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day!
|
t = 0|t; // sanitise
|
||||||
rp : true, // repeat
|
var hrs = 0|(t/3600000);
|
||||||
as : false, // auto snooze
|
return { hrs : hrs, mins : Math.round((t-hrs*3600000)/60000) };
|
||||||
timer : 5, // OPTIONAL - if set, this is a timer and it's the time in minutes
|
}
|
||||||
|
|
||||||
|
// time in { hrs, mins } -> ms
|
||||||
|
function encodeTime(o) {
|
||||||
|
return o.hrs*3600000 + o.mins*60000;
|
||||||
}
|
}
|
||||||
];*/
|
|
||||||
|
|
||||||
function formatTime(t) {
|
function formatTime(t) {
|
||||||
var hrs = 0|t;
|
var o = decodeTime(t);
|
||||||
var mins = Math.round((t-hrs)*60);
|
return o.hrs+":"+("0"+o.mins).substr(-2);
|
||||||
return hrs+":"+("0"+mins).substr(-2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatMins(t) {
|
function getCurrentTime() {
|
||||||
mins = (0|t)%60;
|
|
||||||
hrs = 0|(t/60);
|
|
||||||
return hrs+":"+("0"+mins).substr(-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentHr() {
|
|
||||||
var time = new Date();
|
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));
|
||||||
|
require("alarm").reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMainMenu() {
|
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 = {
|
const menu = {
|
||||||
'': { 'title': 'Alarm/Timer' },
|
'': { 'title': 'Alarm/Timer' },
|
||||||
/*LANG*/'< Back' : ()=>{load();},
|
/*LANG*/'< Back' : ()=>{load();},
|
||||||
|
|
@ -38,140 +45,151 @@ function showMainMenu() {
|
||||||
/*LANG*/'New Timer': ()=>editTimer(-1)
|
/*LANG*/'New Timer': ()=>editTimer(-1)
|
||||||
};
|
};
|
||||||
alarms.forEach((alarm,idx)=>{
|
alarms.forEach((alarm,idx)=>{
|
||||||
if (alarm.timer) {
|
var txt; // a leading space is currently required (JS error in Espruino 2v12)
|
||||||
txt = /*LANG*/"TIMER "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatMins(alarm.timer);
|
if (alarm.timer)
|
||||||
} else {
|
txt = /*LANG*/"Timer"+" "+formatTime(alarm.timer);
|
||||||
txt = /*LANG*/"ALARM "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatTime(alarm.hr);
|
else
|
||||||
if (alarm.rp) txt += /*LANG*/" (repeat)";
|
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);
|
||||||
}
|
}
|
||||||
menu[txt] = function() {
|
|
||||||
if (alarm.timer) editTimer(idx);
|
|
||||||
else editAlarm(idx);
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (WIDGETS["alarm"]) WIDGETS["alarm"].reload();
|
if (WIDGETS["alarm"]) WIDGETS["alarm"].reload();
|
||||||
return E.showMenu(menu);
|
return E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
function editAlarm(alarmIndex) {
|
function editDOW(dow, onchange) {
|
||||||
var newAlarm = alarmIndex<0;
|
const menu = {
|
||||||
var hrs = 12;
|
'': { 'title': /*LANG*/'Days of Week' },
|
||||||
var mins = 0;
|
'< Back' : () => onchange(dow)
|
||||||
var en = true;
|
};
|
||||||
var repeat = true;
|
for (var i = 0; i < 7; i++) (i => {
|
||||||
var as = false;
|
var dayOfWeek = require("locale").dow({ getDay: () => i });
|
||||||
if (!newAlarm) {
|
menu[dayOfWeek] = {
|
||||||
var a = alarms[alarmIndex];
|
value: !!(dow&(1<<i)),
|
||||||
hrs = 0|a.hr;
|
format: v => v ? "Yes" : "No",
|
||||||
mins = Math.round((a.hr-hrs)*60);
|
onchange: v => v ? dow |= 1<<i : dow &= ~(1<<i),
|
||||||
en = a.on;
|
};
|
||||||
repeat = a.rp;
|
})(i);
|
||||||
as = a.as;
|
E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function editAlarm(alarmIndex, alarm) {
|
||||||
|
var newAlarm = alarmIndex<0;
|
||||||
|
var a = {
|
||||||
|
t : 12*3600000, // 12 o clock default
|
||||||
|
on : true,
|
||||||
|
rp : true,
|
||||||
|
as : false,
|
||||||
|
dow : 0b1111111,
|
||||||
|
last : 0,
|
||||||
|
vibrate : ".."
|
||||||
|
}
|
||||||
|
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
|
||||||
|
if (alarm) Object.assign(a,alarm);
|
||||||
|
var t = decodeTime(a.t);
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
'': { 'title': /*LANG*/'Alarm' },
|
'': { 'title': /*LANG*/'Alarm' },
|
||||||
/*LANG*/'< Back' : showMainMenu,
|
'< Back' : () => showMainMenu(),
|
||||||
/*LANG*/'Hours': {
|
/*LANG*/'Hours': {
|
||||||
value: hrs, min : 0, max : 23, wrap : true,
|
value: t.hrs, min : 0, max : 23, wrap : true,
|
||||||
onchange: v => hrs=v
|
onchange: v => t.hrs=v
|
||||||
},
|
},
|
||||||
/*LANG*/'Minutes': {
|
/*LANG*/'Minutes': {
|
||||||
value: mins, min : 0, max : 59, wrap : true,
|
value: t.mins, min : 0, max : 59, wrap : true,
|
||||||
onchange: v => mins=v
|
onchange: v => t.mins=v
|
||||||
},
|
},
|
||||||
/*LANG*/'Enabled': {
|
/*LANG*/'Enabled': {
|
||||||
value: en,
|
value: a.on,
|
||||||
format: v=>v?"On":"Off",
|
format: v=>v?"On":"Off",
|
||||||
onchange: v=>en=v
|
onchange: v=>a.on=v
|
||||||
},
|
},
|
||||||
/*LANG*/'Repeat': {
|
/*LANG*/'Repeat': {
|
||||||
value: en,
|
value: a.rp,
|
||||||
format: v=>v?"Yes":"No",
|
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<<n)?d:".").join(""),
|
||||||
|
onchange: () => 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': {
|
/*LANG*/'Auto snooze': {
|
||||||
value: as,
|
value: a.as,
|
||||||
format: v=>v?"Yes":"No",
|
format: v=>v?"Yes":"No",
|
||||||
onchange: v=>as=v
|
onchange: v=>a.as=v
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
function getAlarm() {
|
menu[/*LANG*/"Save"] = function() {
|
||||||
var hr = hrs+(mins/60);
|
a.t = encodeTime(t);
|
||||||
var day = 0;
|
if (a.t < getCurrentTime())
|
||||||
// If alarm is for tomorrow not today (eg, in the past), set day
|
a.day = (new Date()).getDate();
|
||||||
if (hr < getCurrentHr())
|
if (newAlarm) alarms.push(a);
|
||||||
day = (new Date()).getDate();
|
else alarms[alarmIndex] = a;
|
||||||
// Save alarm
|
saveAndReload();
|
||||||
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));
|
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
};
|
};
|
||||||
if (!newAlarm) {
|
if (!newAlarm) {
|
||||||
menu[/*LANG*/"> Delete"] = function() {
|
menu[/*LANG*/"Delete"] = function() {
|
||||||
alarms.splice(alarmIndex,1);
|
alarms.splice(alarmIndex,1);
|
||||||
require("Storage").write("alarm.json",JSON.stringify(alarms));
|
saveAndReload();
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return E.showMenu(menu);
|
return E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
function editTimer(alarmIndex) {
|
function editTimer(alarmIndex, alarm) {
|
||||||
var newAlarm = alarmIndex<0;
|
var newAlarm = alarmIndex<0;
|
||||||
var hrs = 0;
|
var a = {
|
||||||
var mins = 5;
|
timer : 5*60*1000, // 5 minutes
|
||||||
var en = true;
|
on : true,
|
||||||
if (!newAlarm) {
|
rp : false,
|
||||||
var a = alarms[alarmIndex];
|
as : false,
|
||||||
mins = (0|a.timer)%60;
|
dow : 0b1111111,
|
||||||
hrs = 0|(a.timer/60);
|
last : 0,
|
||||||
en = a.on;
|
vibrate : ".."
|
||||||
}
|
}
|
||||||
|
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
|
||||||
|
if (alarm) Object.assign(a,alarm);
|
||||||
|
var t = decodeTime(a.timer);
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
'': { 'title': /*LANG*/'Timer' },
|
'': { 'title': /*LANG*/'Timer' },
|
||||||
|
'< Back' : () => showMainMenu(),
|
||||||
/*LANG*/'Hours': {
|
/*LANG*/'Hours': {
|
||||||
value: hrs, min : 0, max : 23, wrap : true,
|
value: t.hrs, min : 0, max : 23, wrap : true,
|
||||||
onchange: v => hrs=v
|
onchange: v => t.hrs=v
|
||||||
},
|
},
|
||||||
/*LANG*/'Minutes': {
|
/*LANG*/'Minutes': {
|
||||||
value: mins, min : 0, max : 59, wrap : true,
|
value: t.mins, min : 0, max : 59, wrap : true,
|
||||||
onchange: v => mins=v
|
onchange: v => t.mins=v
|
||||||
},
|
},
|
||||||
/*LANG*/'Enabled': {
|
/*LANG*/'Enabled': {
|
||||||
value: en,
|
value: a.on,
|
||||||
format: v=>v?/*LANG*/"On":/*LANG*/"Off",
|
format: v=>v?"On":"Off",
|
||||||
onchange: v=>en=v
|
onchange: v=>a.on=v
|
||||||
}
|
},
|
||||||
|
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
|
||||||
};
|
};
|
||||||
function getTimer() {
|
menu[/*LANG*/"Save"] = function() {
|
||||||
var d = new Date(Date.now() + ((hrs*60)+mins)*60000);
|
a.timer = encodeTime(t);
|
||||||
var hr = d.getHours() + (d.getMinutes()/60) + (d.getSeconds()/3600);
|
a.t = getCurrentTime() + a.timer;
|
||||||
// Save alarm
|
if (newAlarm) alarms.push(a);
|
||||||
return {
|
else alarms[alarmIndex] = a;
|
||||||
on : en,
|
saveAndReload();
|
||||||
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));
|
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
};
|
};
|
||||||
if (!newAlarm) {
|
if (!newAlarm) {
|
||||||
menu["> Delete"] = function() {
|
menu[/*LANG*/"Delete"] = function() {
|
||||||
alarms.splice(alarmIndex,1);
|
alarms.splice(alarmIndex,1);
|
||||||
require("Storage").write("alarm.json",JSON.stringify(alarms));
|
saveAndReload();
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,32 @@
|
||||||
// check for alarms
|
// check for alarms
|
||||||
(function() {
|
(function() {
|
||||||
|
if (Bangle.ALARM) {
|
||||||
|
clearTimeout(Bangle.ALARM);
|
||||||
|
delete Bangle.ALARM;
|
||||||
|
}
|
||||||
var alarms = require('Storage').readJSON('alarm.json',1)||[];
|
var alarms = require('Storage').readJSON('alarm.json',1)||[];
|
||||||
var time = new Date();
|
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) {
|
if (active.length) {
|
||||||
active = active.sort((a,b)=>(a.hr-b.hr)+(a.last-b.last)*24);
|
active = active.sort((a,b)=>(a.t-b.t)+(a.last-b.last)*86400000);
|
||||||
var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
|
var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000);
|
||||||
if (!require('Storage').read("alarm.js")) {
|
if (!require('Storage').read("alarm.js")) {
|
||||||
console.log("No alarm app!");
|
console.log("No alarm app!");
|
||||||
require('Storage').write('alarm.json',"[]");
|
require('Storage').write('alarm.json',"[]");
|
||||||
} else {
|
} else {
|
||||||
var t = 3600000*(active[0].hr-hr);
|
var t = active[0].t-currentTime;
|
||||||
if (active[0].last == time.getDate() || t < 0) t += 86400000;
|
if (active[0].last == time.getDate() || t < -60000) t += 86400000;
|
||||||
if (t<1000) t=1000;
|
if (t<1000) t=1000; // start alarm min 1 sec from now
|
||||||
/* execute alarm at the correct time. We avoid execing immediately
|
/* execute alarm at the correct time. We avoid execing immediately
|
||||||
since this code will get called AGAIN when alarm.js is loaded. alarm.js
|
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
|
will then clearInterval() to get rid of this call so it can proceed
|
||||||
normally. */
|
normally.
|
||||||
setTimeout(function() {
|
If active[0].js is defined, just run that code as-is and not alarm.js */
|
||||||
load("alarm.js");
|
Bangle.ALARM = setTimeout(active[0].js||'load("alarm.js")',t);
|
||||||
},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));
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -2,17 +2,19 @@
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Default Alarm & Timer",
|
"name": "Default Alarm & Timer",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.15",
|
"version": "0.16",
|
||||||
"description": "Set and respond to alarms and timers",
|
"description": "Set and respond to alarms and timers",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm,widget",
|
"tags": "tool,alarm,widget",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"alarm.app.js","url":"app.js"},
|
{"name":"alarm.app.js","url":"app.js"},
|
||||||
{"name":"alarm.boot.js","url":"boot.js"},
|
{"name":"alarm.boot.js","url":"boot.js"},
|
||||||
{"name":"alarm.js","url":"alarm.js"},
|
{"name":"alarm.js","url":"alarm.js"},
|
||||||
{"name":"alarm.img","url":"app-icon.js","evaluate":true},
|
{"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"}]
|
"data": [{"name":"alarm.json"}]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
WIDGETS["alarm"]={area:"tl",width:0,draw:function() {
|
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);
|
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() {
|
},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('alarm.json',1)||[]).some(alarm=>alarm.on) ? 24 : 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,18 +15,10 @@
|
||||||
require('Storage').writeJSON("messages.settings.json", settings);
|
require('Storage').writeJSON("messages.settings.json", settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
var vibPatterns = [/*LANG*/"Off", ".", "-", "--", "-.-", "---"];
|
|
||||||
var mainmenu = {
|
var mainmenu = {
|
||||||
"" : { "title" : /*LANG*/"Messages" },
|
"" : { "title" : /*LANG*/"Messages" },
|
||||||
"< Back" : back,
|
"< Back" : back,
|
||||||
/*LANG*/'Vibrate': {
|
/*LANG*/'Vibrate': require("buzz_menu").pattern(settings().vibrate, v => updateSetting("vibrate", v)),
|
||||||
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*/'Repeat': {
|
/*LANG*/'Repeat': {
|
||||||
value: settings().repeat,
|
value: settings().repeat,
|
||||||
min: 0, max: 10,
|
min: 0, max: 10,
|
||||||
|
|
|
||||||
|
|
@ -32,14 +32,7 @@ draw:function() {
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
},buzz:function() {
|
},buzz:function() {
|
||||||
if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return; // never buzz during Quiet Mode
|
if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return; // never buzz during Quiet Mode
|
||||||
let v = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || ".";
|
require("buzz").pattern((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();
|
|
||||||
},touch:function(b,c) {
|
},touch:function(b,c) {
|
||||||
var w=WIDGETS["messages"];
|
var w=WIDGETS["messages"];
|
||||||
if (!w||!w.width||c.x<w.x||c.x>w.x+w.width||c.y<w.y||c.y>w.y+w.iconwidth) return;
|
if (!w||!w.width||c.x<w.x||c.x>w.x+w.width||c.y<w.y||c.y>w.y+w.iconwidth) return;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
});
|
||||||
|
|
@ -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]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue