forked from FOSS/BangleApps
Merge branch 'new_alarm'
commit
90bf384355
|
@ -14,3 +14,5 @@
|
||||||
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.17: Moving alarm internals to 'sched' library
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
Default Alarm & Timer
|
||||||
|
======================
|
||||||
|
|
||||||
|
This allows you to add/modify any running timers.
|
||||||
|
|
||||||
|
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps.
|
|
@ -1,72 +0,0 @@
|
||||||
// Chances are boot0.js got run already and scheduled *another*
|
|
||||||
// 'load(alarm.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 showAlarm(alarm) {
|
|
||||||
var msg = formatTime(alarm.hr);
|
|
||||||
var buzzCount = 10;
|
|
||||||
if (alarm.msg)
|
|
||||||
msg += "\n"+alarm.msg;
|
|
||||||
Bangle.loadWidgets();
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
E.showPrompt(msg,{
|
|
||||||
title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!",
|
|
||||||
buttons : {/*LANG*/"Sleep":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins
|
|
||||||
}).then(function(sleep) {
|
|
||||||
buzzCount = 0;
|
|
||||||
if (sleep) {
|
|
||||||
if(alarm.ohr===undefined) alarm.ohr = alarm.hr;
|
|
||||||
alarm.hr += 10/60; // 10 minutes
|
|
||||||
} else {
|
|
||||||
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("alarm.json",JSON.stringify(alarms));
|
|
||||||
load();
|
|
||||||
});
|
|
||||||
function buzz() {
|
|
||||||
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence
|
|
||||||
Bangle.buzz(100).then(()=>{
|
|
||||||
setTimeout(()=>{
|
|
||||||
Bangle.buzz(100).then(function() {
|
|
||||||
if (buzzCount--)
|
|
||||||
setTimeout(buzz, 3000);
|
|
||||||
else if(alarm.as) { // auto-snooze
|
|
||||||
buzzCount = 10;
|
|
||||||
setTimeout(buzz, 600000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},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("alarm.json",1)||[];
|
|
||||||
var active = alarms.filter(a=>a.on&&(a.hr<hr)&&(a.last!=day));
|
|
||||||
if (active.length) {
|
|
||||||
// if there's an alarm, show it
|
|
||||||
active = active.sort((a,b)=>a.hr-b.hr);
|
|
||||||
showAlarm(active[0]);
|
|
||||||
} else {
|
|
||||||
// otherwise just go back to default app
|
|
||||||
setTimeout(load, 100);
|
|
||||||
}
|
|
|
@ -1,36 +1,43 @@
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
var alarms = require("Storage").readJSON("alarm.json",1)||[];
|
var alarms = require("sched").getAlarms();
|
||||||
/*alarms = [
|
// An array of alarm objects (see sched/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("sched").setAlarms(alarms);
|
||||||
|
require("sched").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 +0,0 @@
|
||||||
// check for alarms
|
|
||||||
(function() {
|
|
||||||
var alarms = require('Storage').readJSON('alarm.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("alarm.js")) {
|
|
||||||
console.log("No alarm app!");
|
|
||||||
require('Storage').write('alarm.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 alarm.js is loaded. alarm.js
|
|
||||||
will then clearInterval() to get rid of this call so it can proceed
|
|
||||||
normally. */
|
|
||||||
setTimeout(function() {
|
|
||||||
load("alarm.js");
|
|
||||||
},t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
|
@ -1,18 +1,17 @@
|
||||||
{
|
{
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Default Alarm & Timer",
|
"name": "Alarm & Timer",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.15",
|
"version": "0.17",
|
||||||
"description": "Set and respond to alarms and timers",
|
"description": "Set alarms and timers on your Bangle",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm,widget",
|
"tags": "tool,alarm,widget",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"dependencies": {"scheduler":"type"},
|
||||||
"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.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"}
|
||||||
],
|
]
|
||||||
"data": [{"name":"alarm.json"}]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
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() {
|
||||||
WIDGETS["alarm"].width = (require('Storage').readJSON('alarm.json',1)||[]).some(alarm=>alarm.on) ? 24 : 0;
|
// don't include library here as we're trying to use as little RAM as possible
|
||||||
|
WIDGETS["alarm"].width = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on&&(alarm.hidden!==false)) ? 24 : 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
WIDGETS["alarm"].reload();
|
WIDGETS["alarm"].reload();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"shortName": "Q Alarm",
|
"shortName": "Q Alarm",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"version": "0.04",
|
"version": "0.04",
|
||||||
"description": "Alarm and timer app with days of week and 'hard' option.",
|
"description": "[Not recommended - use 'Alarm & Timer' app] Alarm and timer app with days of week and 'hard' option.",
|
||||||
"tags": "tool,alarm,widget",
|
"tags": "tool,alarm,widget",
|
||||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
|
@ -0,0 +1,89 @@
|
||||||
|
Sched: Scheduling library for alarms and timers
|
||||||
|
====================================
|
||||||
|
|
||||||
|
This provides boot code, a 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 `sched.json`, and take the form:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
id : "mytimer", // optional ID for this alarm/timer, so apps can easily find *their* timers
|
||||||
|
appid : "myappid", // optional app ID for alarms that you set/use for your app
|
||||||
|
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
|
||||||
|
|
||||||
|
date : "2022-04-04", // OPTIONAL date for the alarm, in YYYY-MM-DD format
|
||||||
|
// eg (new Date()).toISOString().substr(0,10)
|
||||||
|
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!
|
||||||
|
rp : true, // repeat the alarm every day?
|
||||||
|
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
|
||||||
|
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 'sched.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 (don't store a lot of data here)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched/lib.js) contains
|
||||||
|
a few helpful functions for getting/setting alarms and timers, but is intentionally sparse so as not to
|
||||||
|
use too much RAM.
|
||||||
|
|
||||||
|
It can be used as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
// add/update an existing alarm
|
||||||
|
require("sched").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("sched").reload();
|
||||||
|
|
||||||
|
// Get the time to the next alarm for us
|
||||||
|
var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm("mytimer"));
|
||||||
|
// timeToNext===undefined if no alarm or alarm disabled
|
||||||
|
|
||||||
|
// delete an alarm
|
||||||
|
require("sched").setAlarm("mytimer", undefined);
|
||||||
|
// reload after deleting...
|
||||||
|
require("sched").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("sched").setAlarm("customrunner", {
|
||||||
|
appid : "myapp",
|
||||||
|
js : "load('setting.app.js')",
|
||||||
|
timer : 1*60*1000, // 1 Minute
|
||||||
|
});
|
||||||
|
|
||||||
|
// If you have been specifying `appid` you can also find any alarms that
|
||||||
|
// your app has created with the following:
|
||||||
|
require("sched").getAlarms().filter(a=>a.appid=="myapp");
|
||||||
|
```
|
||||||
|
|
||||||
|
If your app requires alarms, you can specify that the alarms app needs to
|
||||||
|
be installed by adding `"dependencies": {"scheduler":"type"},` to your app's
|
||||||
|
metadata.
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwkGswAhiMRCCAREAo4eHBIQLEAgwYHsIJDiwHB5gACBpIhHCoYZEGA4gFCw4ABGA4HEjgXJ4IXGAwcUB4VEmf//8zogICoJIFAodMBoNDCoIADmgJB4gXIFwXDCwoABngwFC4guB4k/CQXwh4EC+YMCC44iBp4qDC4n/+gNBC41sEIJCEC4v/GAPGC4dhXYRdFC4xhCCYIXCdQRdDC5HzegQXCsxGHC45IDCwQXCUgwXHJAIXGRogXJSIIXcOw4XIPAYXcBwv/mEDBAwXOgtQC65QGC5vzoEAJAx3Nmk/mEABIiPN+dDAQIwFC4zXGFwKRCGAjvMFwQECGAgXI4YuGGAUvAgU8C4/EFwwGCAgdMC4p4EFwobFOwoXDJAIoEAApGBC4xIEABJGHGAapEAAqNBFwwXD4heI+YuBC5BIBVQhdHIw4wD5inFS4IKCCxFmigNCokzCoMzogICoIWIsMRjgPCAA3BiMWC48RBQIXJEgMRFxAJCCw4lEC44IECooOIBAaBJKwhgIAH4ACA=="))
|
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,25 @@
|
||||||
|
// check for alarms
|
||||||
|
(function() { // run in closure to ensure allocated vars get removed
|
||||||
|
if (Bangle.SCHED) {
|
||||||
|
clearTimeout(Bangle.SCHED);
|
||||||
|
delete Bangle.SCHED;
|
||||||
|
}
|
||||||
|
var alarms = require('Storage').readJSON('sched.json',1)||[];
|
||||||
|
var time = new Date();
|
||||||
|
var active = alarms.filter(a=>a.on && (a.dow>>time.getDay())&1 && (!a.date || a.date==time.toISOString().substr(0,10)));
|
||||||
|
if (active.length) {
|
||||||
|
active = active.sort((a,b)=>(a.t-b.t)+(a.last-b.last)*86400000);
|
||||||
|
var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000);
|
||||||
|
var t = active[0].t-currentTime;
|
||||||
|
if (active[0].last == time.getDate() || t < -60000) t += 86400000;
|
||||||
|
if (t<1000) t=1000; // start alarm min 1 sec from now
|
||||||
|
/* execute alarm at the correct time. We avoid execing immediately
|
||||||
|
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
|
||||||
|
normally.
|
||||||
|
If active[0].js is defined, just run that code as-is and not alarm.js */
|
||||||
|
Bangle.SCHED = setTimeout(active[0].js||'load("sched.js")',t);
|
||||||
|
} else { // check for new alarms at midnight (so day of week works)
|
||||||
|
Bangle.SCHED = setTimeout('eval(require("Storage").read("sched.boot.js"))', 86400000 - (Date.now()%86400000));
|
||||||
|
}
|
||||||
|
})();
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Return an array of all alarms
|
||||||
|
exports.getAlarms = function() {
|
||||||
|
return require("Storage").readJSON("sched.json",1)||[];
|
||||||
|
};
|
||||||
|
// Write a list of alarms back to storage
|
||||||
|
exports.setAlarms = function(alarms) {
|
||||||
|
return require("Storage").writeJSON("sched.json",alarms);
|
||||||
|
};
|
||||||
|
// Return an alarm object based on ID
|
||||||
|
exports.getAlarm = function(id) {
|
||||||
|
return exports.getAlarms().find(a=>a.id==id);
|
||||||
|
};
|
||||||
|
// Given a list of alarms from getAlarms, return a list of active alarms for the given time (or current time if time not specified)
|
||||||
|
exports.getActiveAlarms = function(alarms, time) {
|
||||||
|
if (!time) time = new Date();
|
||||||
|
var currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000)
|
||||||
|
+10000;// get current time - 10s in future to ensure we alarm if we've started the app a tad early
|
||||||
|
return alarms.filter(a=>a.on&&(a.t<currentTime)&&(a.last!=time.getDate()) && (!a.date || a.date==time.toISOString().substr(0,10))).sort((a,b)=>a.t-b.t);
|
||||||
|
}
|
||||||
|
// Set an alarm object based on ID. Leave 'alarm' undefined to remove it
|
||||||
|
exports.setAlarm = function(id, alarm) {
|
||||||
|
var alarms = exports.getAlarms().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);
|
||||||
|
}
|
||||||
|
exports.setAlarms(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 && (!alarm.date || alarm.date==time.toISOString().substr(0,10));
|
||||||
|
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("sched.boot.js"));
|
||||||
|
if (WIDGETS["alarm"]) {
|
||||||
|
WIDGETS["alarm"].reload();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"id": "sched",
|
||||||
|
"name": "Scheduler",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "Scheduling library for alarms and timers",
|
||||||
|
"icon": "app.png",
|
||||||
|
"type": "scheduler",
|
||||||
|
"tags": "tool,system,alarm",
|
||||||
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"sched.boot.js","url":"boot.js"},
|
||||||
|
{"name":"sched.js","url":"sched.js"},
|
||||||
|
{"name":"sched.img","url":"app-icon.js","evaluate":true},
|
||||||
|
{"name":"sched","url":"lib.js"}
|
||||||
|
],
|
||||||
|
"data": [{"name":"sched.json"}]
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Chances are boot0.js got run already and scheduled *another*
|
||||||
|
// 'load(sched.js)' - so let's remove it first!
|
||||||
|
if (Bangle.SCHED) {
|
||||||
|
clearInterval(Bangle.SCHED);
|
||||||
|
delete Bangle.SCHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(t) {
|
||||||
|
var o = decodeTime(t);
|
||||||
|
return o.hrs+":"+("0"+o.mins).substr(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAlarm(alarm) {
|
||||||
|
var msg = "";
|
||||||
|
msg += alarm.timer ? formatTime(alarm.timer) : formatTime(alarm.t);
|
||||||
|
if (alarm.msg) {
|
||||||
|
msg += "\n"+alarm.msg;
|
||||||
|
} else {
|
||||||
|
if (alarm.timer)
|
||||||
|
msg = atob("ACQswgD//33vRcGHIQAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAAAP/wAAAAAAAAAP/wAAAAAAAAAqqoAPAAAAAAqqqqoP8AAAAKqqqqqv/AAACqqqqqqq/wAAKqqqlWqqvwAAqqqqlVaqrAACqqqqlVVqqAAKqqqqlVVaqgAKqaqqlVVWqgAqpWqqlVVVqoAqlWqqlVVVaoCqlV6qlVVVaqCqVVfqlVVVWqCqVVf6lVVVWqKpVVX/lVVVVqqpVVV/+VVVVqqpVVV//lVVVqqpVVVfr1VVVqqpVVVfr1VVVqqpVVVb/lVVVqqpVVVW+VVVVqqpVVVVVVVVVqiqVVVVVVVVWqCqVVVVVVVVWqCqlVVVVVVVaqAqlVVVVVVVaoAqpVVVVVVVqoAKqVVVVVVWqgAKqlVVVVVaqgACqpVVVVVqqAAAqqlVVVaqoAAAKqqVVWqqgAAACqqqqqqqAAAAAKqqqqqgAAAAAAqqqqoAAAAAAAAqqoAAAAA==")+" "+msg;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
var buzzCount = 10;
|
||||||
|
E.showPrompt(msg,{
|
||||||
|
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
|
||||||
|
}).then(function(sleep) {
|
||||||
|
buzzCount = 0;
|
||||||
|
if (sleep) {
|
||||||
|
if(alarm.ot===undefined) alarm.ot = alarm.t;
|
||||||
|
alarm.t += 10*60*1000; // 10 minutes
|
||||||
|
} else {
|
||||||
|
alarm.last = (new Date()).getDate();
|
||||||
|
if (alarm.ot!==undefined) {
|
||||||
|
alarm.t = alarm.ot;
|
||||||
|
delete alarm.ot;
|
||||||
|
}
|
||||||
|
if (!alarm.rp) alarm.on = false;
|
||||||
|
}
|
||||||
|
// alarm is still a member of 'alarms', so writing to array writes changes back directly
|
||||||
|
require("sched").setAlarms(alarms);
|
||||||
|
load();
|
||||||
|
});
|
||||||
|
function buzz() {
|
||||||
|
require("buzz").pattern(alarm.vibrate===undefined?"..":alarm.vibrate).then(function() {
|
||||||
|
if (buzzCount--)
|
||||||
|
setTimeout(buzz, 3000);
|
||||||
|
else if(alarm.as) { // auto-snooze
|
||||||
|
buzzCount = 10;
|
||||||
|
setTimeout(buzz, 600000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return;
|
||||||
|
buzz();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for alarms
|
||||||
|
var alarms = require("sched").getAlarms();
|
||||||
|
var active = require("sched").getActiveAlarms(alarms);
|
||||||
|
if (active.length) // if there's an alarm, show it
|
||||||
|
showAlarm(active[0]);
|
||||||
|
else // otherwise just go back to default app
|
||||||
|
setTimeout(load, 100);
|
|
@ -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