Merge pull request #1707 from alessandrococco/sched-unlock-display-at-buzz

[Scheduler] New functions & Settings page
master
Gordon Williams 2022-04-20 13:46:13 +01:00 committed by GitHub
commit c214f47313
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 221 additions and 73 deletions

View File

@ -18,3 +18,4 @@
0.17: Moving alarm internals to 'sched' library 0.17: Moving alarm internals to 'sched' library
0.18: Cope with >1 identical alarm at once (#1667) 0.18: Cope with >1 identical alarm at once (#1667)
0.19: Ensure rescheduled alarms that already fired have 'last' reset 0.19: Ensure rescheduled alarms that already fired have 'last' reset
0.20: Use the new 'sched' factories to initialize new alarms/timers

View File

@ -1,28 +1,28 @@
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
var alarms = require("sched").getAlarms();
// An array of alarm objects (see sched/README.md) // An array of alarm objects (see sched/README.md)
let alarms = require("sched").getAlarms();
// time in ms -> { hrs, mins } // time in ms -> { hrs, mins }
function decodeTime(t) { function decodeTime(t) {
t = 0|t; // sanitise t = 0 | t; // sanitise
var hrs = 0|(t/3600000); let hrs = 0 | (t / 3600000);
return { hrs : hrs, mins : Math.round((t-hrs*3600000)/60000) }; return { hrs: hrs, mins: Math.round((t - hrs * 3600000) / 60000) };
} }
// time in { hrs, mins } -> ms // time in { hrs, mins } -> ms
function encodeTime(o) { function encodeTime(o) {
return o.hrs*3600000 + o.mins*60000; return o.hrs * 3600000 + o.mins * 60000;
} }
function formatTime(t) { function formatTime(t) {
var o = decodeTime(t); let o = decodeTime(t);
return o.hrs+":"+("0"+o.mins).substr(-2); return o.hrs + ":" + ("0" + o.mins).substr(-2);
} }
function getCurrentTime() { function getCurrentTime() {
var time = new Date(); let time = new Date();
return ( return (
time.getHours() * 3600000 + time.getHours() * 3600000 +
time.getMinutes() * 60000 + time.getMinutes() * 60000 +
@ -39,7 +39,7 @@ function showMainMenu() {
// Timer img "\0"+atob("DhKBAP////MDDAwwMGGBzgPwB4AeAPwHOBhgwMMzDez////w") // 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") // 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': /*LANG*/'Alarms&Timers' },
/*LANG*/'< Back' : ()=>{load();}, /*LANG*/'< Back' : ()=>{load();},
/*LANG*/'New Alarm': ()=>editAlarm(-1), /*LANG*/'New Alarm': ()=>editAlarm(-1),
/*LANG*/'New Timer': ()=>editTimer(-1) /*LANG*/'New Timer': ()=>editTimer(-1)
@ -76,13 +76,13 @@ function showMainMenu() {
function editDOW(dow, onchange) { function editDOW(dow, onchange) {
const menu = { const menu = {
'': { 'title': /*LANG*/'Days of Week' }, '': { 'title': /*LANG*/'Days of Week' },
'< Back' : () => onchange(dow) /*LANG*/'< Back' : () => onchange(dow)
}; };
for (var i = 0; i < 7; i++) (i => { for (let i = 0; i < 7; i++) (i => {
var dayOfWeek = require("locale").dow({ getDay: () => i }); let dayOfWeek = require("locale").dow({ getDay: () => i });
menu[dayOfWeek] = { menu[dayOfWeek] = {
value: !!(dow&(1<<i)), value: !!(dow&(1<<i)),
format: v => v ? "Yes" : "No", format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v => v ? dow |= 1<<i : dow &= ~(1<<i), onchange: v => v ? dow |= 1<<i : dow &= ~(1<<i),
}; };
})(i); })(i);
@ -90,23 +90,15 @@ function editDOW(dow, onchange) {
} }
function editAlarm(alarmIndex, alarm) { function editAlarm(alarmIndex, alarm) {
var newAlarm = alarmIndex<0; let newAlarm = alarmIndex < 0;
var a = { let a = require("sched").newDefaultAlarm();
t : 12*3600000, // 12 o clock default
on : true,
rp : false, // repeat not the default
as : false,
dow : 0b1111111,
last : 0,
vibrate : ".."
}
if (!newAlarm) Object.assign(a, alarms[alarmIndex]); if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
if (alarm) Object.assign(a,alarm); if (alarm) Object.assign(a,alarm);
var t = decodeTime(a.t); let t = decodeTime(a.t);
const menu = { const menu = {
'': { 'title': /*LANG*/'Alarm' }, '': { 'title': /*LANG*/'Alarm' },
'< Back' : () => showMainMenu(), /*LANG*/'< Back' : () => showMainMenu(),
/*LANG*/'Hours': { /*LANG*/'Hours': {
value: t.hrs, min : 0, max : 23, wrap : true, value: t.hrs, min : 0, max : 23, wrap : true,
onchange: v => t.hrs=v onchange: v => t.hrs=v
@ -117,23 +109,23 @@ function editAlarm(alarmIndex, alarm) {
}, },
/*LANG*/'Enabled': { /*LANG*/'Enabled': {
value: a.on, value: a.on,
format: v=>v?"On":"Off", format: v => v ? /*LANG*/"On" : /*LANG*/"Off",
onchange: v=>a.on=v onchange: v=>a.on=v
}, },
/*LANG*/'Repeat': { /*LANG*/'Repeat': {
value: a.rp, value: a.rp,
format: v=>v?"Yes":"No", format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v=>a.rp=v onchange: v => a.rp = v
}, },
/*LANG*/'Days': { /*LANG*/'Days': {
value: "SMTWTFS".split("").map((d,n)=>a.dow&(1<<n)?d:".").join(""), value: "SMTWTFS".split("").map((d,n)=>a.dow&(1<<n)?d:".").join(""),
onchange: () => editDOW(a.dow, d=>{a.dow=d;editAlarm(alarmIndex,a)}) onchange: () => editDOW(a.dow, d=>{a.dow=d;editAlarm(alarmIndex,a)})
}, },
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ), /*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
/*LANG*/'Auto snooze': { /*LANG*/'Auto Snooze': {
value: a.as, value: a.as,
format: v=>v?"Yes":"No", format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v=>a.as=v onchange: v => a.as = v
} }
}; };
menu[/*LANG*/"Save"] = function() { menu[/*LANG*/"Save"] = function() {
@ -155,23 +147,15 @@ function editAlarm(alarmIndex, alarm) {
} }
function editTimer(alarmIndex, alarm) { function editTimer(alarmIndex, alarm) {
var newAlarm = alarmIndex<0; let newAlarm = alarmIndex < 0;
var a = { let a = require("sched").newDefaultTimer();
timer : 5*60*1000, // 5 minutes
on : true,
rp : false,
as : false,
dow : 0b1111111,
last : 0,
vibrate : ".."
}
if (!newAlarm) Object.assign(a, alarms[alarmIndex]); if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
if (alarm) Object.assign(a,alarm); if (alarm) Object.assign(a,alarm);
var t = decodeTime(a.timer); let t = decodeTime(a.timer);
const menu = { const menu = {
'': { 'title': /*LANG*/'Timer' }, '': { 'title': /*LANG*/'Timer' },
'< Back' : () => showMainMenu(), /*LANG*/'< Back' : () => showMainMenu(),
/*LANG*/'Hours': { /*LANG*/'Hours': {
value: t.hrs, min : 0, max : 23, wrap : true, value: t.hrs, min : 0, max : 23, wrap : true,
onchange: v => t.hrs=v onchange: v => t.hrs=v
@ -182,8 +166,8 @@ function editTimer(alarmIndex, alarm) {
}, },
/*LANG*/'Enabled': { /*LANG*/'Enabled': {
value: a.on, value: a.on,
format: v=>v?"On":"Off", format: v => v ? /*LANG*/"On" : /*LANG*/"Off",
onchange: v=>a.on=v onchange: v => a.on = v
}, },
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ), /*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
}; };

View File

@ -1,8 +1,8 @@
{ {
"id": "alarm", "id": "alarm",
"name": "Alarm & Timer", "name": "Alarms & Timers",
"shortName": "Alarms", "shortName": "Alarms",
"version": "0.19", "version": "0.20",
"description": "Set alarms and timers on your Bangle", "description": "Set alarms and timers on your Bangle",
"icon": "app.png", "icon": "app.png",
"tags": "tool,alarm,widget", "tags": "tool,alarm,widget",

View File

@ -2,3 +2,4 @@
0.02: Fix scheduling of other alarms if there is a pending alarm from the past (fix #1667) 0.02: Fix scheduling of other alarms if there is a pending alarm from the past (fix #1667)
0.03: Fix `getTimeToAlarm` for a timer already used at same day, don't set `last` for timers. 0.03: Fix `getTimeToAlarm` for a timer already used at same day, don't set `last` for timers.
0.04: Fix `getTimeToAlarm` to check for next dow if alarm.t lower currentTime. 0.04: Fix `getTimeToAlarm` to check for next dow if alarm.t lower currentTime.
0.05: Export new functions (`newDefaultAlarm/Timer`), add Settings page

View File

@ -8,8 +8,17 @@ Other apps can use this to provide alarm functionality.
App App
--- ---
The Alarm app allows you to add/modify any running timers. The **Alarms & Timers** app allows you to add/modify any running alarms and timers.
Global Settings
---------------
- `Unlock at Buzz` - If `Yes` the alarm/timer will unlock the watch
- `Default Auto Snooze` - Default _Auto Snooze_ value for newly created alarms (_Alarms_ only)
- `Default Snooze` - Default _Snooze_ value for newly created alarms/timers
- `Buzz Count` - The number of buzzes before the watch goes silent
- `Buzz Interval` - The interval between one buzz and the next
- `Default Alarm/Timer Pattern` - Default vibration pattern for newly created alarms/timers
Internals / Library Internals / Library
------------------- -------------------
@ -53,21 +62,27 @@ use too much RAM.
It can be used as follows: It can be used as follows:
``` ```
// add/update an existing alarm // Get a new alarm with default values
let alarm = require("sched").newDefaultAlarm();
// Get a new timer with default values
let timer = require("sched").newDefaultTimer();
// Add/update an existing alarm
require("sched").setAlarm("mytimer", { require("sched").setAlarm("mytimer", {
msg : "Wake up", msg : "Wake up",
timer : 10*60*1000, // 10 Minutes timer : 10 * 60 * 1000 // 10 minutes
}); });
// Ensure the widget and alarm timer updates to schedule the new alarm properly // Ensure the widget and alarm timer updates to schedule the new alarm properly
require("sched").reload(); require("sched").reload();
// Get the time to the next alarm for us // Get the time to the next alarm for us
var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm("mytimer")); let timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm("mytimer"));
// timeToNext===undefined if no alarm or alarm disabled // timeToNext === undefined if no alarm or alarm disabled
// delete an alarm // Delete an alarm
require("sched").setAlarm("mytimer", undefined); require("sched").setAlarm("mytimer", undefined);
// reload after deleting... // Reload after deleting
require("sched").reload(); require("sched").reload();
// Or add an alarm that runs your own code - in this case // Or add an alarm that runs your own code - in this case
@ -76,12 +91,15 @@ require("sched").reload();
require("sched").setAlarm("customrunner", { require("sched").setAlarm("customrunner", {
appid : "myapp", appid : "myapp",
js : "load('setting.app.js')", js : "load('setting.app.js')",
timer : 1*60*1000, // 1 Minute timer : 1 * 60 * 1000 // 1 minute
}); });
// If you have been specifying `appid` you can also find any alarms that // If you have been specifying `appid` you can also find any alarms that
// your app has created with the following: // your app has created with the following:
require("sched").getAlarms().filter(a=>a.appid=="myapp"); require("sched").getAlarms().filter(a => a.appid == "myapp");
// Get the scheduler settings
let settings = require("sched").getSettings();
``` ```
If your app requires alarms, you can specify that the alarms app needs to If your app requires alarms, you can specify that the alarms app needs to

View File

@ -52,3 +52,58 @@ exports.reload = function() {
Bangle.drawWidgets(); Bangle.drawWidgets();
} }
}; };
// Factory that creates a new alarm with default values
exports.newDefaultAlarm = function () {
const settings = exports.getSettings();
let alarm = {
t: 12 * 3600000, // Default to 12:00
on: true,
rp: false, // repeat not the default
as: settings.defaultAutoSnooze || false,
dow: 0b1111111,
last: 0,
vibrate: settings.defaultAlarmPattern,
};
delete settings;
return alarm;
}
// Factory that creates a new timer with default values
exports.newDefaultTimer = function () {
const settings = exports.getSettings();
let timer = {
timer: 5 * 60 * 1000, // 5 minutes
on: true,
rp: false,
as: false,
dow: 0b1111111,
last: 0,
vibrate: settings.defaultTimerPattern
}
delete settings;
return timer;
};
// Return the scheduler settings
exports.getSettings = function () {
return Object.assign(
{
unlockAtBuzz: false,
defaultSnoozeMillis: 600000, // 10 minutes
defaultAutoSnooze: false,
buzzCount: 10,
buzzIntervalMillis: 3000, // 3 seconds
defaultAlarmPattern: "..",
defaultTimerPattern: ".."
},
require("Storage").readJSON("sched.settings.json", true) || {}
);
}
// Write the updated settings back to storage
exports.setSettings = function(settings) {
require("Storage").writeJSON("sched.settings.json", settings);
};

View File

@ -1,7 +1,7 @@
{ {
"id": "sched", "id": "sched",
"name": "Scheduler", "name": "Scheduler",
"version": "0.04", "version": "0.05",
"description": "Scheduling library for alarms and timers", "description": "Scheduling library for alarms and timers",
"icon": "app.png", "icon": "app.png",
"type": "scheduler", "type": "scheduler",
@ -12,7 +12,8 @@
{"name":"sched.boot.js","url":"boot.js"}, {"name":"sched.boot.js","url":"boot.js"},
{"name":"sched.js","url":"sched.js"}, {"name":"sched.js","url":"sched.js"},
{"name":"sched.img","url":"app-icon.js","evaluate":true}, {"name":"sched.img","url":"app-icon.js","evaluate":true},
{"name":"sched","url":"lib.js"} {"name":"sched","url":"lib.js"},
{"name":"sched.settings.js","url":"settings.js"}
], ],
"data": [{"name":"sched.json"}] "data": [{"name":"sched.json"}, {"name":"sched.settings.json"}]
} }

View File

@ -18,7 +18,9 @@ function formatTime(t) {
} }
function showAlarm(alarm) { function showAlarm(alarm) {
var msg = ""; const settings = require("sched").getSettings();
let msg = "";
msg += alarm.timer ? formatTime(alarm.timer) : formatTime(alarm.t); msg += alarm.timer ? formatTime(alarm.timer) : formatTime(alarm.t);
if (alarm.msg) { if (alarm.msg) {
msg += "\n"+alarm.msg; msg += "\n"+alarm.msg;
@ -28,9 +30,12 @@ function showAlarm(alarm) {
else 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; 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.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
var buzzCount = 10;
let buzzCount = settings.buzzCount;
E.showPrompt(msg,{ E.showPrompt(msg,{
title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!", title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!",
buttons : {/*LANG*/"Snooze":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
@ -38,7 +43,7 @@ function showAlarm(alarm) {
buzzCount = 0; buzzCount = 0;
if (sleep) { if (sleep) {
if(alarm.ot===undefined) alarm.ot = alarm.t; if(alarm.ot===undefined) alarm.ot = alarm.t;
alarm.t += 10*60*1000; // 10 minutes alarm.t += settings.defaultSnoozeMillis;
} else { } else {
if (!alarm.timer) alarm.last = (new Date()).getDate(); if (!alarm.timer) alarm.last = (new Date()).getDate();
if (alarm.ot!==undefined) { if (alarm.ot!==undefined) {
@ -51,24 +56,35 @@ function showAlarm(alarm) {
require("sched").setAlarms(alarms); require("sched").setAlarms(alarms);
load(); load();
}); });
function buzz() { function buzz() {
require("buzz").pattern(alarm.vibrate===undefined?"..":alarm.vibrate).then(function() { if (settings.unlockAtBuzz) {
if (buzzCount--) Bangle.setLocked(false);
setTimeout(buzz, 3000); }
else if(alarm.as) { // auto-snooze
buzzCount = 10; require("buzz").pattern(alarm.vibrate === undefined ? ".." : alarm.vibrate).then(() => {
setTimeout(buzz, 600000); if (buzzCount--) {
setTimeout(buzz, settings.buzzIntervalMillis);
} else if (alarm.as) { // auto-snooze
buzzCount = settings.buzzCount;
setTimeout(buzz, settings.defaultSnoozeMillis);
} }
}); });
} }
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return;
if ((require("Storage").readJSON("setting.json", 1) || {}).quiet > 1)
return;
buzz(); buzz();
} }
// Check for alarms // Check for alarms
var alarms = require("sched").getAlarms(); let alarms = require("sched").getAlarms();
var active = require("sched").getActiveAlarms(alarms); let active = require("sched").getActiveAlarms(alarms);
if (active.length) // if there's an alarm, show it if (active.length) {
// if there's an alarm, show it
showAlarm(active[0]); showAlarm(active[0]);
else // otherwise just go back to default app } else {
// otherwise just go back to default app
setTimeout(load, 100); setTimeout(load, 100);
}

72
apps/sched/settings.js Normal file
View File

@ -0,0 +1,72 @@
(function (back) {
let settings = require("sched").getSettings();
E.showMenu({
"": { "title": /*LANG*/"Scheduler" },
/*LANG*/"< Back": () => back(),
/*LANG*/"Unlock at Buzz": {
value: settings.unlockAtBuzz,
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v => {
settings.unlockAtBuzz = v;
require("sched").setSettings(settings);
}
},
/*LANG*/"Default Auto Snooze": {
value: settings.defaultAutoSnooze,
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: v => {
settings.defaultAutoSnooze = v;
require("sched").setSettings(settings);
}
},
/*LANG*/"Default Snooze": {
value: settings.defaultSnoozeMillis / 60000,
min: 5,
max: 30,
step: 5,
format: v => v + /*LANG*/" min",
onchange: v => {
settings.defaultSnoozeMillis = v * 60000;
require("sched").setSettings(settings);
}
},
/*LANG*/"Buzz Count": {
value: settings.buzzCount,
min: 5,
max: 15,
step: 1,
onchange: v => {
settings.buzzCount = v;
require("sched").setSettings(settings);
}
},
/*LANG*/"Buzz Interval": {
value: settings.buzzIntervalMillis / 1000,
min: 1,
max: 5,
step: 1,
format: v => v + /*LANG*/"s",
onchange: v => {
settings.buzzIntervalMillis = v * 1000;
require("sched").setSettings(settings);
}
},
/*LANG*/"Default Alarm Pattern": require("buzz_menu").pattern(settings.defaultAlarmPattern, v => {
settings.defaultAlarmPattern = v;
require("sched").setSettings(settings);
}),
/*LANG*/"Default Timer Pattern": require("buzz_menu").pattern(settings.defaultTimerPattern, v => {
settings.defaultTimerPattern = v;
require("sched").setSettings(settings);
})
});
});