[sched, alarm] allow dated Event repeat
parent
20651a102c
commit
a1097a3eea
|
|
@ -38,3 +38,4 @@
|
||||||
0.35: Add automatic translation of more strings
|
0.35: Add automatic translation of more strings
|
||||||
0.36: alarm widget moved out of app
|
0.36: alarm widget moved out of app
|
||||||
0.37: add message input and dated Events
|
0.37: add message input and dated Events
|
||||||
|
0.38: Dated event repeat option
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master
|
||||||
- `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely)
|
- `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely)
|
||||||
- `New Timer` → Configure a new timer (triggered based on amount of time elapsed in hours/minutes/seconds)
|
- `New Timer` → Configure a new timer (triggered based on amount of time elapsed in hours/minutes/seconds)
|
||||||
- `New Event` → Configure a new event (triggered based on time and date)
|
- `New Event` → Configure a new event (triggered based on time and date)
|
||||||
|
- `Repeat` → Alarm can be be fired only once or repeated (every X number of _days_, _weeks_, _months_ or _years_)
|
||||||
- `Advanced`
|
- `Advanced`
|
||||||
- `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details
|
- `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details
|
||||||
- `Enable All` → Enable _all_ disabled alarms & timers
|
- `Enable All` → Enable _all_ disabled alarms & timers
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ const firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {})
|
||||||
const WORKDAYS = 62
|
const WORKDAYS = 62
|
||||||
const WEEKEND = firstDayOfWeek ? 192 : 65;
|
const WEEKEND = firstDayOfWeek ? 192 : 65;
|
||||||
const EVERY_DAY = firstDayOfWeek ? 254 : 127;
|
const EVERY_DAY = firstDayOfWeek ? 254 : 127;
|
||||||
|
const INTERVALS = ["day", "week", "month", "year"];
|
||||||
|
const INTERVAL_LABELS = [/*LANG*/"Day", /*LANG*/"Week", /*LANG*/"Month", /*LANG*/"Year"];
|
||||||
|
|
||||||
const iconAlarmOn = "\0" + atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/n+B/n+B/h+B/4+A/+8A//8Af/4AP/wAH/gAB+AAAAAAAAAA==");
|
const iconAlarmOn = "\0" + atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/n+B/n+B/h+B/4+A/+8A//8Af/4AP/wAH/gAB+AAAAAAAAAA==");
|
||||||
const iconAlarmOff = "\0" + (g.theme.dark
|
const iconAlarmOff = "\0" + (g.theme.dark
|
||||||
|
|
@ -50,8 +52,9 @@ function showMainMenu() {
|
||||||
alarms.forEach((e, index) => {
|
alarms.forEach((e, index) => {
|
||||||
var label = (e.timer
|
var label = (e.timer
|
||||||
? require("time_utils").formatDuration(e.timer)
|
? require("time_utils").formatDuration(e.timer)
|
||||||
: (e.date ? `${e.date.substring(5,10)} ${require("time_utils").formatTime(e.t)}` : require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeDOW(e)}` : ""))
|
: (e.date ? `${e.date.substring(5,10)}${e.rp?"*":""} ${require("time_utils").formatTime(e.t)}` : require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeRepeat(e)}` : ""))
|
||||||
) + (e.msg ? " " + e.msg : "");
|
) + (e.msg ? ` ${e.msg}` : "");
|
||||||
|
|
||||||
menu[label] = {
|
menu[label] = {
|
||||||
value: e.on ? (e.timer ? iconTimerOn : iconAlarmOn) : (e.timer ? iconTimerOff : iconAlarmOff),
|
value: e.on ? (e.timer ? iconTimerOn : iconAlarmOn) : (e.timer ? iconTimerOff : iconAlarmOff),
|
||||||
onchange: () => setTimeout(e.timer ? showEditTimerMenu : showEditAlarmMenu, 10, e, index)
|
onchange: () => setTimeout(e.timer ? showEditTimerMenu : showEditAlarmMenu, 10, e, index)
|
||||||
|
|
@ -148,8 +151,8 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
|
||||||
onchange: v => alarm.on = v
|
onchange: v => alarm.on = v
|
||||||
},
|
},
|
||||||
/*LANG*/"Repeat": {
|
/*LANG*/"Repeat": {
|
||||||
value: decodeDOW(alarm),
|
value: decodeRepeat(alarm),
|
||||||
onchange: () => setTimeout(showEditRepeatMenu, 100, alarm.rp, alarm.dow, (repeat, dow) => {
|
onchange: () => setTimeout(showEditRepeatMenu, 100, alarm.rp, date || alarm.dow, (repeat, dow) => {
|
||||||
alarm.rp = repeat;
|
alarm.rp = repeat;
|
||||||
alarm.dow = dow;
|
alarm.dow = dow;
|
||||||
prepareAlarmForSave(alarm, alarmIndex, time, date, true);
|
prepareAlarmForSave(alarm, alarmIndex, time, date, true);
|
||||||
|
|
@ -174,9 +177,7 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!keyboard) delete menu[/*LANG*/"Message"];
|
if (!keyboard) delete menu[/*LANG*/"Message"];
|
||||||
if (alarm.date || withDate) {
|
if (!alarm.date) {
|
||||||
delete menu[/*LANG*/"Repeat"];
|
|
||||||
} else {
|
|
||||||
delete menu[/*LANG*/"Day"];
|
delete menu[/*LANG*/"Day"];
|
||||||
delete menu[/*LANG*/"Month"];
|
delete menu[/*LANG*/"Month"];
|
||||||
delete menu[/*LANG*/"Year"];
|
delete menu[/*LANG*/"Year"];
|
||||||
|
|
@ -225,49 +226,77 @@ function saveAndReload() {
|
||||||
alarms.filter(e => e.timer === undefined).forEach(a => a.dow = handleFirstDayOfWeek(a.dow));
|
alarms.filter(e => e.timer === undefined).forEach(a => a.dow = handleFirstDayOfWeek(a.dow));
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeDOW(alarm) {
|
function decodeRepeat(alarm) {
|
||||||
return alarm.rp
|
return alarm.rp
|
||||||
? require("date_utils")
|
? (alarm.date
|
||||||
.dows(firstDayOfWeek, 2)
|
? `${alarm.rp.num}*${INTERVAL_LABELS[INTERVALS.indexOf(alarm.rp.interval)]}`
|
||||||
.map((day, index) => alarm.dow & (1 << (index + firstDayOfWeek)) ? day : "_")
|
: require("date_utils")
|
||||||
.join("")
|
.dows(firstDayOfWeek, 2)
|
||||||
.toLowerCase()
|
.map((day, index) => alarm.dow & (1 << (index + firstDayOfWeek)) ? day : "_")
|
||||||
|
.join("")
|
||||||
|
.toLowerCase())
|
||||||
: /*LANG*/"Once"
|
: /*LANG*/"Once"
|
||||||
}
|
}
|
||||||
|
|
||||||
function showEditRepeatMenu(repeat, dow, dowChangeCallback) {
|
function showEditRepeatMenu(repeat, day, dowChangeCallback) {
|
||||||
var originalRepeat = repeat;
|
var originalRepeat = repeat;
|
||||||
var originalDow = dow;
|
var dow;
|
||||||
var isCustom = repeat && dow != WORKDAYS && dow != WEEKEND && dow != EVERY_DAY;
|
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
"": { "title": /*LANG*/"Repeat Alarm" },
|
"": { "title": /*LANG*/"Repeat Alarm" },
|
||||||
"< Back": () => dowChangeCallback(repeat, dow),
|
"< Back": () => dowChangeCallback(repeat, dow),
|
||||||
/*LANG*/"Once": {
|
/*LANG*/"Only Once": () => dowChangeCallback(false, EVERY_DAY)
|
||||||
// The alarm will fire once. Internally it will be saved
|
// The alarm will fire once. Internally it will be saved
|
||||||
// as "fire every days" BUT the repeat flag is false so
|
// as "fire every days" BUT the repeat flag is false so
|
||||||
// we avoid messing up with the scheduler.
|
// we avoid messing up with the scheduler.
|
||||||
value: !repeat,
|
|
||||||
onchange: () => dowChangeCallback(false, EVERY_DAY)
|
|
||||||
},
|
|
||||||
/*LANG*/"Workdays": {
|
|
||||||
value: repeat && dow == WORKDAYS,
|
|
||||||
onchange: () => dowChangeCallback(true, WORKDAYS)
|
|
||||||
},
|
|
||||||
/*LANG*/"Weekends": {
|
|
||||||
value: repeat && dow == WEEKEND,
|
|
||||||
onchange: () => dowChangeCallback(true, WEEKEND)
|
|
||||||
},
|
|
||||||
/*LANG*/"Every Day": {
|
|
||||||
value: repeat && dow == EVERY_DAY,
|
|
||||||
onchange: () => dowChangeCallback(true, EVERY_DAY)
|
|
||||||
},
|
|
||||||
/*LANG*/"Custom": {
|
|
||||||
value: isCustom ? decodeDOW({ rp: true, dow: dow }) : false,
|
|
||||||
onchange: () => setTimeout(showCustomDaysMenu, 10, isCustom ? dow : EVERY_DAY, dowChangeCallback, originalRepeat, originalDow)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let restOfMenu;
|
||||||
|
if (typeof day === "number") {
|
||||||
|
dow = day;
|
||||||
|
var originalDow = dow;
|
||||||
|
var isCustom = repeat && dow != WORKDAYS && dow != WEEKEND && dow != EVERY_DAY;
|
||||||
|
|
||||||
|
restOfMenu = {
|
||||||
|
/*LANG*/"Workdays": {
|
||||||
|
value: repeat && dow == WORKDAYS,
|
||||||
|
onchange: () => dowChangeCallback(true, WORKDAYS)
|
||||||
|
},
|
||||||
|
/*LANG*/"Weekends": {
|
||||||
|
value: repeat && dow == WEEKEND,
|
||||||
|
onchange: () => dowChangeCallback(true, WEEKEND)
|
||||||
|
},
|
||||||
|
/*LANG*/"Every Day": {
|
||||||
|
value: repeat && dow == EVERY_DAY,
|
||||||
|
onchange: () => dowChangeCallback(true, EVERY_DAY)
|
||||||
|
},
|
||||||
|
/*LANG*/"Custom": {
|
||||||
|
value: isCustom ? decodeRepeat({ rp: true, dow: dow }) : false,
|
||||||
|
onchange: () => setTimeout(showCustomDaysMenu, 10, isCustom ? dow : EVERY_DAY, dowChangeCallback, originalRepeat, originalDow)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
var date = day; // eventually: detect day of date and configure a repeat e.g. 3rd Monday of Month
|
||||||
|
dow = EVERY_DAY;
|
||||||
|
repeat = repeat || {interval: "month", num: 1};
|
||||||
|
|
||||||
|
restOfMenu = {
|
||||||
|
/*LANG*/"Every": {
|
||||||
|
value: repeat.num,
|
||||||
|
min: 1,
|
||||||
|
onchange: v => repeat.num = v
|
||||||
|
},
|
||||||
|
/*LANG*/"Interval": {
|
||||||
|
value: INTERVALS.indexOf(repeat.interval),
|
||||||
|
format: v => INTERVAL_LABELS[v],
|
||||||
|
min: 0,
|
||||||
|
max: INTERVALS.length - 1,
|
||||||
|
onchange: v => repeat.interval = INTERVALS[v]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(menu, restOfMenu);
|
||||||
E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Alarms & Timers",
|
"name": "Alarms & Timers",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.37",
|
"version": "0.38",
|
||||||
"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",
|
"tags": "tool,alarm",
|
||||||
|
|
|
||||||
|
|
@ -22,3 +22,4 @@
|
||||||
0.19: Update clock_info to refresh periodically on active alarms/timers
|
0.19: Update clock_info to refresh periodically on active alarms/timers
|
||||||
0.20: Alarm dismiss and snooze events
|
0.20: Alarm dismiss and snooze events
|
||||||
0.21: Fix crash in clock_info
|
0.21: Fix crash in clock_info
|
||||||
|
0.22: Dated event repeat option
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,9 @@ Alarms are stored in an array in `sched.json`, and take the form:
|
||||||
// eg (new Date()).toISOString().substr(0,10)
|
// 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! (No change from 0 on timers)
|
last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day! (No change from 0 on timers)
|
||||||
rp : true, // repeat the alarm every day?
|
rp : true, // repeat the alarm every day? If date is given, pass an object instead of a boolean,
|
||||||
|
// e.g. repeat every 2 months: { interval: "month", num: 2 }.
|
||||||
|
// Supported intervals: day, week, month, year
|
||||||
vibrate : "...", // OPTIONAL pattern of '.', '-' and ' ' to use for when buzzing out this alarm (defaults to '..' if not set)
|
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
|
hidden : false, // OPTIONAL if false, the widget should not show an icon for this alarm
|
||||||
as : false, // auto snooze
|
as : false, // auto snooze
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "sched",
|
"id": "sched",
|
||||||
"name": "Scheduler",
|
"name": "Scheduler",
|
||||||
"version": "0.21",
|
"version": "0.22",
|
||||||
"description": "Scheduling library for alarms and timers",
|
"description": "Scheduling library for alarms and timers",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "scheduler",
|
"type": "scheduler",
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ function showAlarm(alarm) {
|
||||||
if (del) {
|
if (del) {
|
||||||
alarms.splice(alarmIndex, 1);
|
alarms.splice(alarmIndex, 1);
|
||||||
} else {
|
} else {
|
||||||
if (!alarm.timer) {
|
if (alarm.date && alarm.rp) {
|
||||||
|
setNextRepeatDate(alarm);
|
||||||
|
} else if (!alarm.timer) {
|
||||||
alarm.last = new Date().getDate();
|
alarm.last = new Date().getDate();
|
||||||
}
|
}
|
||||||
if (alarm.ot !== undefined) {
|
if (alarm.ot !== undefined) {
|
||||||
|
|
@ -78,6 +80,30 @@ function showAlarm(alarm) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setNextRepeatDate(alarm) {
|
||||||
|
let date = new Date(alarm.date);
|
||||||
|
let rp = alarm.rp;
|
||||||
|
switch(rp.interval) {
|
||||||
|
case "day":
|
||||||
|
date.setDate(date.getDate() + rp.num);
|
||||||
|
break;
|
||||||
|
case "week":
|
||||||
|
date.setDate(date.getDate() + (rp.num * 7));
|
||||||
|
break;
|
||||||
|
case "month":
|
||||||
|
if (!alarm.od) alarm.od = date.getDate();
|
||||||
|
date = new Date(date.getFullYear(), date.getMonth() + rp.num, alarm.od);
|
||||||
|
if (date.getDate() != alarm.od) date.setDate(0);
|
||||||
|
break;
|
||||||
|
case "year":
|
||||||
|
if (!alarm.od) alarm.od = date.getDate();
|
||||||
|
date = new Date(date.getFullYear() + rp.num, date.getMonth(), alarm.od);
|
||||||
|
if (date.getDate() != alarm.od) date.setDate(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
alarm.date = date.toLocalISOString().slice(0,10);
|
||||||
|
}
|
||||||
|
|
||||||
if ((require("Storage").readJSON("setting.json", 1) || {}).quiet > 1)
|
if ((require("Storage").readJSON("setting.json", 1) || {}).quiet > 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue