Merge branch 'master' of https://github.com/jdekarske/BangleApps
|
@ -0,0 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Fullscreen settings.
|
|
@ -0,0 +1,13 @@
|
|||
# 90s Clock
|
||||
|
||||
A watch face in 90s style:
|
||||
|
||||

|
||||
|
||||
Fullscreen mode can be enabled in the settings:
|
||||
|
||||

|
||||
|
||||
|
||||
## Creator
|
||||
- [David Peer](https://github.com/peerdavid)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgc8+fAgEgwAMDvPnz99BYdl2weHtu27ft2AGBiEcuEAhAPDg4jGgECIRMN23fthUNgP374vBAB3gAgc/gAXNjlx4EDxwJEpAjG/6IBjkBL4UAjVgBAJuCgPHBQMFEIkkyQjFhwEClgXBEYNBwkQJoibCBwNFBAUCEAVAQZAjC/8euPHDon//hKB//xEYMP//jBYP/+ARDNYM///+EYIgBj1B/8fCIUhEYQRB//FUIM/EZU4EYMkEYP/8VhEYUH/gRBWAUfI4MD+AjBoAsBwEH8EB/EDwE4HwYjCuEHWAOHgExEYKbBCIZNB8fAEYQHByE/EwPABAY+BgRHDBANyJQXHNwIjD8CSBj/+BwMSTwOOBYK2D/4CCNYZQB/iJBQwYjCCIcAgeBSoOAWYQjEVoIRCNAIjKAQKJBgAFC8ZoCWwJbDABMHGQPAAoMQB5EDx/4A4gqBZwIGCWwIABuBWC4EBZwPgv/AcwS/EAAcIU4IRBVQIRKEwIjBv0ARIUDCJIjD//x/ARK/5HC/+BCJkcI45uDgECUgQjCWAM4WwUBWYanEAA8cTARWBEYUC5RAHw1YgEOFQXADQPHIIkAhgICuARBh0A23blhHBagIKBsOGjNswhHDEYUUAoTUBhkxEYMwKwU503bvuwXILmCEYMYsumWYYjB85lDEYovBEYXm7fs25EBI4kYtOWNwIjD4+8NYsw4YjGz9/2hrEoOGjVBwE4NYdzNYSwBuEDEYcxaIUA8+atugGogjBiVgWAI"))
|
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 36 KiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "90sclk",
|
||||
"name": "90s Clock",
|
||||
"version": "0.02",
|
||||
"description": "A 90s style watch-face",
|
||||
"readme": "README.md",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_2.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"90sclk.app.js","url":"app.js"},
|
||||
{"name":"90sclk.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"90sclk.settings.js","url":"settings.js"}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 5.3 KiB |
|
@ -0,0 +1,31 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "90sclk.setting.json";
|
||||
|
||||
// initialize with default settings...
|
||||
const storage = require('Storage')
|
||||
let settings = {
|
||||
fullscreen: false,
|
||||
};
|
||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||
for (const key in saved_settings) {
|
||||
settings[key] = saved_settings[key]
|
||||
}
|
||||
|
||||
function save() {
|
||||
storage.write(SETTINGS_FILE, settings)
|
||||
}
|
||||
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': '90s Clock' },
|
||||
'< Back': back,
|
||||
'Full Screen': {
|
||||
value: settings.fullscreen,
|
||||
format: () => (settings.fullscreen ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.fullscreen = !settings.fullscreen;
|
||||
save();
|
||||
},
|
||||
}
|
||||
});
|
||||
})
|
|
@ -14,3 +14,7 @@
|
|||
0.13: Alarm widget state now updates when setting/resetting an alarm
|
||||
0.14: Order of 'back' menu item
|
||||
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.18: Cope with >1 identical alarm at once (#1667)
|
||||
0.19: Ensure rescheduled alarms that already fired have 'last' reset
|
||||
|
|
|
@ -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.drawWidgets();
|
||||
|
||||
var alarms = require("Storage").readJSON("alarm.json",1)||[];
|
||||
/*alarms = [
|
||||
{ on : true,
|
||||
hr : 6.5, // hours + minutes/60
|
||||
msg : "Eat chocolate",
|
||||
last : 0, // last day of the month we alarmed on - so we don't alarm twice in one day!
|
||||
rp : true, // repeat
|
||||
as : false, // auto snooze
|
||||
timer : 5, // OPTIONAL - if set, this is a timer and it's the time in minutes
|
||||
}
|
||||
];*/
|
||||
var alarms = require("sched").getAlarms();
|
||||
// An array of alarm objects (see sched/README.md)
|
||||
|
||||
// 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 hrs = 0|t;
|
||||
var mins = Math.round((t-hrs)*60);
|
||||
return hrs+":"+("0"+mins).substr(-2);
|
||||
var o = decodeTime(t);
|
||||
return o.hrs+":"+("0"+o.mins).substr(-2);
|
||||
}
|
||||
|
||||
function formatMins(t) {
|
||||
mins = (0|t)%60;
|
||||
hrs = 0|(t/60);
|
||||
return hrs+":"+("0"+mins).substr(-2);
|
||||
}
|
||||
|
||||
function getCurrentHr() {
|
||||
function getCurrentTime() {
|
||||
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() {
|
||||
// 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 = {
|
||||
'': { 'title': 'Alarm/Timer' },
|
||||
/*LANG*/'< Back' : ()=>{load();},
|
||||
|
@ -38,140 +45,161 @@ function showMainMenu() {
|
|||
/*LANG*/'New Timer': ()=>editTimer(-1)
|
||||
};
|
||||
alarms.forEach((alarm,idx)=>{
|
||||
var type,txt; // a leading space is currently required (JS error in Espruino 2v12)
|
||||
if (alarm.timer) {
|
||||
txt = /*LANG*/"TIMER "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatMins(alarm.timer);
|
||||
type = /*LANG*/"Timer";
|
||||
txt = " "+formatTime(alarm.timer);
|
||||
} else {
|
||||
txt = /*LANG*/"ALARM "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatTime(alarm.hr);
|
||||
if (alarm.rp) txt += /*LANG*/" (repeat)";
|
||||
type = /*LANG*/"Alarm";
|
||||
txt = " "+formatTime(alarm.t);
|
||||
}
|
||||
menu[txt] = function() {
|
||||
if (alarm.timer) editTimer(idx);
|
||||
else editAlarm(idx);
|
||||
if (alarm.rp) txt += "\0"+atob("FBaBAAABgAAcAAHn//////wAHsABzAAYwAAMAADAAAAAAwAAMAADGAAzgAN4AD//////54AAOAABgAA=");
|
||||
// rename duplicate alarms
|
||||
if (menu[type+txt]) {
|
||||
var n = 2;
|
||||
while (menu[type+" "+n+txt]) n++;
|
||||
txt = type+" "+n+txt;
|
||||
} else txt = type+txt;
|
||||
// add to menu
|
||||
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);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if (WIDGETS["alarm"]) WIDGETS["alarm"].reload();
|
||||
return E.showMenu(menu);
|
||||
}
|
||||
|
||||
function editAlarm(alarmIndex) {
|
||||
function editDOW(dow, onchange) {
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Days of Week' },
|
||||
'< Back' : () => onchange(dow)
|
||||
};
|
||||
for (var i = 0; i < 7; i++) (i => {
|
||||
var dayOfWeek = require("locale").dow({ getDay: () => i });
|
||||
menu[dayOfWeek] = {
|
||||
value: !!(dow&(1<<i)),
|
||||
format: v => v ? "Yes" : "No",
|
||||
onchange: v => v ? dow |= 1<<i : dow &= ~(1<<i),
|
||||
};
|
||||
})(i);
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function editAlarm(alarmIndex, alarm) {
|
||||
var newAlarm = alarmIndex<0;
|
||||
var hrs = 12;
|
||||
var mins = 0;
|
||||
var en = true;
|
||||
var repeat = true;
|
||||
var as = false;
|
||||
if (!newAlarm) {
|
||||
var a = alarms[alarmIndex];
|
||||
hrs = 0|a.hr;
|
||||
mins = Math.round((a.hr-hrs)*60);
|
||||
en = a.on;
|
||||
repeat = a.rp;
|
||||
as = a.as;
|
||||
var a = {
|
||||
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 (alarm) Object.assign(a,alarm);
|
||||
var t = decodeTime(a.t);
|
||||
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Alarm' },
|
||||
/*LANG*/'< Back' : showMainMenu,
|
||||
'< Back' : () => showMainMenu(),
|
||||
/*LANG*/'Hours': {
|
||||
value: hrs, min : 0, max : 23, wrap : true,
|
||||
onchange: v => hrs=v
|
||||
value: t.hrs, min : 0, max : 23, wrap : true,
|
||||
onchange: v => t.hrs=v
|
||||
},
|
||||
/*LANG*/'Minutes': {
|
||||
value: mins, min : 0, max : 59, wrap : true,
|
||||
onchange: v => mins=v
|
||||
value: t.mins, min : 0, max : 59, wrap : true,
|
||||
onchange: v => t.mins=v
|
||||
},
|
||||
/*LANG*/'Enabled': {
|
||||
value: en,
|
||||
value: a.on,
|
||||
format: v=>v?"On":"Off",
|
||||
onchange: v=>en=v
|
||||
onchange: v=>a.on=v
|
||||
},
|
||||
/*LANG*/'Repeat': {
|
||||
value: en,
|
||||
value: a.rp,
|
||||
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': {
|
||||
value: as,
|
||||
value: a.as,
|
||||
format: v=>v?"Yes":"No",
|
||||
onchange: v=>as=v
|
||||
onchange: v=>a.as=v
|
||||
}
|
||||
};
|
||||
function getAlarm() {
|
||||
var hr = hrs+(mins/60);
|
||||
var day = 0;
|
||||
// If alarm is for tomorrow not today (eg, in the past), set day
|
||||
if (hr < getCurrentHr())
|
||||
day = (new Date()).getDate();
|
||||
// Save alarm
|
||||
return {
|
||||
on : en, hr : hr,
|
||||
last : day, rp : repeat, as: as
|
||||
};
|
||||
}
|
||||
menu[/*LANG*/"> Save"] = function() {
|
||||
if (newAlarm) alarms.push(getAlarm());
|
||||
else alarms[alarmIndex] = getAlarm();
|
||||
require("Storage").write("alarm.json",JSON.stringify(alarms));
|
||||
menu[/*LANG*/"Save"] = function() {
|
||||
a.t = encodeTime(t);
|
||||
a.last = (a.t < getCurrentTime()) ? (new Date()).getDate() : 0;
|
||||
if (newAlarm) alarms.push(a);
|
||||
else alarms[alarmIndex] = a;
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
};
|
||||
if (!newAlarm) {
|
||||
menu[/*LANG*/"> Delete"] = function() {
|
||||
menu[/*LANG*/"Delete"] = function() {
|
||||
alarms.splice(alarmIndex,1);
|
||||
require("Storage").write("alarm.json",JSON.stringify(alarms));
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
};
|
||||
}
|
||||
return E.showMenu(menu);
|
||||
}
|
||||
|
||||
function editTimer(alarmIndex) {
|
||||
function editTimer(alarmIndex, alarm) {
|
||||
var newAlarm = alarmIndex<0;
|
||||
var hrs = 0;
|
||||
var mins = 5;
|
||||
var en = true;
|
||||
if (!newAlarm) {
|
||||
var a = alarms[alarmIndex];
|
||||
mins = (0|a.timer)%60;
|
||||
hrs = 0|(a.timer/60);
|
||||
en = a.on;
|
||||
var a = {
|
||||
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 (alarm) Object.assign(a,alarm);
|
||||
var t = decodeTime(a.timer);
|
||||
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Timer' },
|
||||
'< Back' : () => showMainMenu(),
|
||||
/*LANG*/'Hours': {
|
||||
value: hrs, min : 0, max : 23, wrap : true,
|
||||
onchange: v => hrs=v
|
||||
value: t.hrs, min : 0, max : 23, wrap : true,
|
||||
onchange: v => t.hrs=v
|
||||
},
|
||||
/*LANG*/'Minutes': {
|
||||
value: mins, min : 0, max : 59, wrap : true,
|
||||
onchange: v => mins=v
|
||||
value: t.mins, min : 0, max : 59, wrap : true,
|
||||
onchange: v => t.mins=v
|
||||
},
|
||||
/*LANG*/'Enabled': {
|
||||
value: en,
|
||||
format: v=>v?/*LANG*/"On":/*LANG*/"Off",
|
||||
onchange: v=>en=v
|
||||
}
|
||||
value: a.on,
|
||||
format: v=>v?"On":"Off",
|
||||
onchange: v=>a.on=v
|
||||
},
|
||||
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
|
||||
};
|
||||
function getTimer() {
|
||||
var d = new Date(Date.now() + ((hrs*60)+mins)*60000);
|
||||
var hr = d.getHours() + (d.getMinutes()/60) + (d.getSeconds()/3600);
|
||||
// Save alarm
|
||||
return {
|
||||
on : en,
|
||||
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));
|
||||
menu[/*LANG*/"Save"] = function() {
|
||||
a.timer = encodeTime(t);
|
||||
a.t = getCurrentTime() + a.timer;
|
||||
a.last = 0;
|
||||
if (newAlarm) alarms.push(a);
|
||||
else alarms[alarmIndex] = a;
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
};
|
||||
if (!newAlarm) {
|
||||
menu["> Delete"] = function() {
|
||||
menu[/*LANG*/"Delete"] = function() {
|
||||
alarms.splice(alarmIndex,1);
|
||||
require("Storage").write("alarm.json",JSON.stringify(alarms));
|
||||
saveAndReload();
|
||||
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",
|
||||
"name": "Default Alarm & Timer",
|
||||
"name": "Alarm & Timer",
|
||||
"shortName": "Alarms",
|
||||
"version": "0.15",
|
||||
"description": "Set and respond to alarms and timers",
|
||||
"version": "0.19",
|
||||
"description": "Set alarms and timers on your Bangle",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,alarm,widget",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"dependencies": {"scheduler":"type"},
|
||||
"storage": [
|
||||
{"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.wid.js","url":"widget.js"}
|
||||
],
|
||||
"data": [{"name":"alarm.json"}]
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
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);
|
||||
},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();
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Actually upload correct code
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4UA///t9TmuV3+GJf4AN+ALVgf8BasP/4LVn//4ALUWgJUJBZUDBYJUIBZcP3/nKhEOt/WBZE5r+VKg0KgEVr9V3wLHqtaqt9sALElWAqoABt1QBZNeBYuq0ILCrVUBYulBYVWBYkCBYgABBZ8K1WVBYlABZegKQWqBQlVqALKqWoKQWpBYtWBZeqKRAAB1WABZZSHAANq0ALLKQ6qC1ALLKQ5UEAH4AG"))
|
|
@ -0,0 +1,30 @@
|
|||
Bangle.setBarometerPower(true, "app");
|
||||
|
||||
g.clear(1);
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
var zero = 0;
|
||||
var R = Bangle.appRect;
|
||||
var y = R.y + R.h/2;
|
||||
var MEDIANLENGTH = 20;
|
||||
var avr = [], median;
|
||||
var value = 0;
|
||||
|
||||
Bangle.on('pressure', function(e) {
|
||||
while (avr.length>MEDIANLENGTH) avr.pop();
|
||||
avr.unshift(e.altitude);
|
||||
median = avr.slice().sort();
|
||||
g.reset().clearRect(0,y-30,g.getWidth()-10,y+30);
|
||||
if (median.length>10) {
|
||||
var mid = median.length>>1;
|
||||
value = E.sum(median.slice(mid-4,mid+5)) / 9;
|
||||
g.setFont("Vector",50).setFontAlign(0,0).drawString((value-zero).toFixed(1), g.getWidth()/2, y);
|
||||
}
|
||||
});
|
||||
|
||||
g.reset();
|
||||
g.setFont("6x8").setFontAlign(0,0).drawString(/*LANG*/"ALTITUDE (m)", g.getWidth()/2, y-40);
|
||||
g.setFont("6x8").setFontAlign(0,0,3).drawString(/*LANG*/"ZERO", g.getWidth()-5, g.getHeight()/2);
|
||||
setWatch(function() {
|
||||
zero = value;
|
||||
}, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat:true});
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,12 @@
|
|||
{ "id": "altimeter",
|
||||
"name": "Altimeter",
|
||||
"version":"0.02",
|
||||
"description": "Simple altimeter that can display height changed using Bangle.js 2's built in pressure sensor.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,outdoors",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"altimeter.app.js","url":"app.js"},
|
||||
{"name":"altimeter.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
0.01: New app!
|
||||
0.02: Fix bug with regenerating index, fix bug in word lookups
|
||||
0.03: Improve word search performance
|
|
@ -27,10 +27,7 @@ least once and yields an additional 7 points. Each game contains at least one pa
|
|||
The game uses an internal dictionary consisting of a newline separated list of English words ('bee.words', using the '2of12inf' word list).
|
||||
The dictionary is fairly large (~700kB of flash space) and thus requires appropriate space on the watch and will make installing the app somewhat
|
||||
slow. Because of its size it cannot be compressed (heatshrink needs to hold the compressed/uncompressed data in memory).
|
||||
In order to make checking the validity of a guessed word faster an index file ('bee_lindex.json') is installed with
|
||||
the app that facilitates faster word lookups. This index file is specific to the dictionary file used. If one were to
|
||||
replace the dictionary file with a different version (e.g. a different language) the index file has to be regenerated. The easiest
|
||||
way to do so is to delete (via the Web IDE or the fileman app on the watch) the file 'bee_lindex.json' - it will be regenerated (and saved,
|
||||
i.e. it only happens once) on app startup automatically, a process that takes roughly 30 seconds.
|
||||
This file can be replaced with a custom dictionary, an ASCII file containing a newline-separated (single "\n", not DOS-style "\r\n") alphabetically
|
||||
sorted (sorting is important for the word lookup algorithm) list of words.
|
||||
|
||||

|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
const S = require("Storage");
|
||||
const words = S.read("bee.words");
|
||||
var letters = [];
|
||||
var letterIdx = [];
|
||||
|
||||
var centers = [];
|
||||
|
||||
|
@ -12,28 +12,17 @@ var score = 0;
|
|||
|
||||
var intervalID = -1;
|
||||
|
||||
function prepareLetterIdx () {
|
||||
function biSearch(w, ws, start, end, count) {
|
||||
"compile"
|
||||
var li = [0];
|
||||
if (S.read("bee_lindex.json")!==undefined) li = S.readJSON("bee_lindex.json"); // check for cached index
|
||||
else {
|
||||
for (var i=1; i<26; ++i) {
|
||||
var prefix = String.fromCharCode(97+i%26);
|
||||
console.log(prefix);
|
||||
li.push(S.read('bee.words').indexOf("\n"+prefix, li[i-1])+1);
|
||||
}
|
||||
li.push(S.read('bee.words').length);
|
||||
S.writeJSON("bee_lindex.json", li);
|
||||
}
|
||||
for (var i=0; i<26; ++i) letterIdx[i] = S.read("bee.words", li[i], li[i+1]-li[i]);
|
||||
}
|
||||
|
||||
function findWord (w) {
|
||||
"compile"
|
||||
var ci = w.charCodeAt(0)-97;
|
||||
var f = letterIdx[ci].indexOf(w);
|
||||
if (f>=0 && letterIdx[ci][f+w.length]=="\n") return true;
|
||||
return false;
|
||||
if (start>end-w.legnth || count--<=0) return ws.substr(start, end-start).indexOf("\n"+w+"\n");
|
||||
var mid = (end+start)>>1;
|
||||
if (ws[mid-1]==="\n") --mid;
|
||||
else while (mid<end && ws[mid]!=="\n") mid++;
|
||||
var i = 0;
|
||||
while (i<w.length && ws[mid+i+1]==w[i]) ++i;
|
||||
if (i==w.length && ws[mid+i+1]==="\n") return mid+1;
|
||||
if (i==w.length || w[i]<ws[mid+i+1]) return biSearch(w, ws, start, mid+1, count);
|
||||
if (w[i]>ws[mid+i+1]) return biSearch(w, ws, mid+1, end, count);
|
||||
}
|
||||
|
||||
function isPangram(w) {
|
||||
|
@ -45,8 +34,9 @@ function isPangram(w) {
|
|||
function checkWord (w) {
|
||||
if (w.indexOf(String.fromCharCode(97+letters[0]))==-1) return false; // does it contain central letter?
|
||||
if (foundWords.indexOf(w)>=0) return false; // already found
|
||||
if (findWord(w)) {
|
||||
if (biSearch(w, words, 0, words.length, 20)>-1) {
|
||||
foundWords.push(w);
|
||||
foundWords.sort();
|
||||
if (w.length==4) score++;
|
||||
else score += w.length;
|
||||
if (isPangram(w)) score += 7;
|
||||
|
@ -91,13 +81,12 @@ function pickLetters() {
|
|||
var ltrs = "";
|
||||
while (ltrs.length!==7) {
|
||||
ltrs = [];
|
||||
var j = Math.floor(26*Math.random());
|
||||
var i = Math.floor((letterIdx[j].length-10)*Math.random());
|
||||
while (letterIdx[j][i]!="\n" && i<letterIdx[j].length) ++i;
|
||||
if (i<letterIdx[j].length-1) {
|
||||
var i = Math.floor((words.length-10)*Math.random());
|
||||
while (words[i]!="\n" && i<words.length) ++i;
|
||||
if (i<words.length-1) {
|
||||
++i;
|
||||
while (letterIdx[j][i]!=="\n") {
|
||||
var c = letterIdx[j][i];
|
||||
while (words[i]!=="\n") {
|
||||
var c = words[i];
|
||||
if (ltrs.indexOf(c)===-1) ltrs += c;
|
||||
++i;
|
||||
}
|
||||
|
@ -185,7 +174,6 @@ function showWordList() {
|
|||
});
|
||||
}
|
||||
|
||||
prepareLetterIdx();
|
||||
pickLetters();
|
||||
drawHive();
|
||||
drawScore();
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
[0,41048,80445,152390,198606,228714,257919,279071,303726,337982,343582,348026,367246,404452,419780,438696,496250,499697,544600,624304,659085,680996,691270,708186,708341,709916,710883]
|
|
@ -2,7 +2,7 @@
|
|||
"name": "Bee",
|
||||
"shortName":"Bee",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"version":"0.03",
|
||||
"description": "Spelling bee",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
|
@ -10,7 +10,6 @@
|
|||
"storage": [
|
||||
{"name":"bee.app.js","url":"bee.app.js"},
|
||||
{"name":"bee.words","url":"bee_words_2of12"},
|
||||
{"name":"bee_lindex.json","url":"bee_lindex.json"},
|
||||
{"name":"bee.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
0.01: New App.
|
||||
0.02: Use build in function for steps and other improvements.
|
||||
0.03: Adapt colors based on the theme of the user.
|
||||
0.04: Steps can be hidden now such that the time is even larger.
|
||||
0.05: Included icons for information.
|
|
@ -0,0 +1,16 @@
|
|||
# Black & White clock
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
- Fullscreen on/off
|
||||
- The design is adapted to the theme of your bangle.
|
||||
- Tab left/right of screen to show steps, temperature etc.
|
||||
- Enable / disable lock icon in the settings.
|
||||
- If the "sched" app is installed tab top / bottom of the screen to set the timer.
|
||||
|
||||
## Thanks to
|
||||
<a href="https://www.flaticon.com/free-icons/" title="Icons">Icons created by Flaticon</a>
|
||||
|
||||
## Creator
|
||||
- [David Peer](https://github.com/peerdavid)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgIcah0EgEB/H8iFsAoOY4kMBYMDhmGgXkAoUGiWkAoQQBoAFCjgnCAoM4hgFDuEI+wpC8EKyg1C/0eAoMAsEAiQvBAAeAApQAB/4Ao+P4v/wn0P8Pgn/wnkH4Pjv/j/nn9PH//n/nj/IFF4F88AXBAoM88EcAoPHj//jlDAoOf/+Y+YFHjnnjAjBEIIjD+BHDO9IALA=="))
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "bwclk",
|
||||
"name": "BlackWhite Clock",
|
||||
"version": "0.05",
|
||||
"description": "Black and white clock.",
|
||||
"readme": "README.md",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot_2.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"bwclk.app.js","url":"app.js"},
|
||||
{"name":"bwclk.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"bwclk.settings.js","url":"settings.js"}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1,40 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "bwclk.setting.json";
|
||||
|
||||
// initialize with default settings...
|
||||
const storage = require('Storage')
|
||||
let settings = {
|
||||
fullscreen: false,
|
||||
showLock: true,
|
||||
};
|
||||
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
|
||||
for (const key in saved_settings) {
|
||||
settings[key] = saved_settings[key]
|
||||
}
|
||||
|
||||
function save() {
|
||||
storage.write(SETTINGS_FILE, settings)
|
||||
}
|
||||
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'BlackWhite Clock' },
|
||||
'< Back': back,
|
||||
'Fullscreen': {
|
||||
value: settings.fullscreen,
|
||||
format: () => (settings.fullscreen ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.fullscreen = !settings.fullscreen;
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Show Lock': {
|
||||
value: settings.showLock,
|
||||
format: () => (settings.showLock ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.showLock = !settings.showLock;
|
||||
save();
|
||||
},
|
||||
}
|
||||
});
|
||||
})
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Eliminate flickering
|
||||
0.04: Fix for Bangle.js 2 and themes
|
||||
0.05: Fix bearing not clearing correctly (visible in single or double digit bearings)
|
||||
0.06: Add button for force compass calibration
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Compass
|
||||
|
||||
This app uses Bangle.js's built-in magnetometer as a compass.
|
||||
|
||||
## Usage
|
||||
|
||||
Hold your Bangle.js **face up** (so the display is parallel to the ground),
|
||||
and the red arrow will point north, with the heading in degrees printed at
|
||||
the top of the screen.
|
||||
|
||||
This compass app does not include tilt compensation - so much like a real
|
||||
compass you should always keep it face up when taking a reading.
|
||||
|
||||
The first time you run the compass after your Bangle has booted (or if
|
||||
you move to an area with a substantially different magnetic field) you will
|
||||
need to recalibrate your compass (even if a heading is shown).
|
||||
|
||||
## Calibration
|
||||
|
||||
Press the button next to the `RESET` label on the screen. The North/South marker
|
||||
will disappear and a message will appear asking you to rotate the watch 360 degrees.
|
||||
|
||||
* Hold the watch face up, so the display is parallel to the ground
|
||||
* Rotate it around slowly, all 360 degrees (with the display still parallel to the ground)
|
||||
* The `Uncalibrated` message will disappear before you have finished rotating the full 360 degrees - but you should still complete the full rotation in order for the compass to work properly.
|
||||
|
||||
Once you've rotated the full 360 degrees your compass should now work fine,
|
||||
and calibration is stored between runs of the app. However if you go near
|
||||
to a strong magnet you may still need to recalibrate.
|
|
@ -38,7 +38,7 @@ Bangle.on('mag', function(m) {
|
|||
if (!wasUncalibrated) {
|
||||
g.clearRect(0,24,W,48);
|
||||
g.setFontAlign(0,-1).setFont("6x8");
|
||||
g.drawString("Uncalibrated\nturn 360° around",M,24+4);
|
||||
g.drawString(/*LANG*/"Uncalibrated\nturn 360° around",M,24+4);
|
||||
wasUncalibrated = true;
|
||||
}
|
||||
} else {
|
||||
|
@ -64,7 +64,12 @@ Bangle.on('mag', function(m) {
|
|||
oldHeading = m.heading;
|
||||
});
|
||||
|
||||
g.clear();
|
||||
g.clear(1);
|
||||
g.setFont("6x8").setFontAlign(0,0,3).drawString(/*LANG*/"RESET", g.getWidth()-5, g.getHeight()/2);
|
||||
setWatch(function() {
|
||||
Bangle.resetCompass();
|
||||
}, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat:true});
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
Bangle.setCompassPower(1);
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
{
|
||||
"id": "compass",
|
||||
"name": "Compass",
|
||||
"version": "0.05",
|
||||
"version": "0.06",
|
||||
"description": "Simple compass that points North",
|
||||
"icon": "compass.png",
|
||||
"screenshots": [{"url":"screenshot_compass.png"}],
|
||||
"tags": "tool,outdoors",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"compass.app.js","url":"compass.js"},
|
||||
{"name":"compass.img","url":"compass-icon.js","evaluate":true}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: First release
|
|
@ -0,0 +1,26 @@
|
|||
# Fuzzy Text Clock
|
||||
|
||||
An imprecise clock for when you're not in a rush.
|
||||
|
||||
This clock is a remake of one of my favourite Pebble watchfaces, Fuzzy Text International. I use this watch for weekends and holidays, when 'within 5 minutes of the actual time' is close enough!
|
||||
|
||||
By default it will use the language set on the watch, go to settings to pick:
|
||||
* en_GB - English
|
||||
* en_US - American
|
||||
* es_ES - Spanish
|
||||
* fr_FR - French
|
||||
* no_NO - Norwegian
|
||||
* sv_SE - Swedish
|
||||
* de_DE - German
|
||||
|
||||
Most translations are taken from the original Fuzzy Text International code.
|
||||
|
||||
## TODO
|
||||
* Bold hour word (as the pebble version has)
|
||||
* Animation when changing time?
|
||||
|
||||
## References
|
||||
Based on Pebble app Fuzzy Text International: https://github.com/hallettj/Fuzzy-Text-International
|
||||
|
||||

|
||||

|
|
@ -0,0 +1,186 @@
|
|||
{
|
||||
"en_GB":{
|
||||
"hours":[
|
||||
"midnight", "one", "two", "three", "four", "five",
|
||||
"six", "seven", "eight", "nine", "ten", "eleven",
|
||||
"twelve", "one", "two", "three", "four", "five",
|
||||
"six", "seven", "eight", "nine", "ten", "eleven"
|
||||
],
|
||||
"minutes":[
|
||||
"*$1 o'clock",
|
||||
"five past *$1",
|
||||
"ten past *$1",
|
||||
"quarter past *$1",
|
||||
"twenty past *$1",
|
||||
"twenty five past *$1",
|
||||
"half past *$1",
|
||||
"twenty five to *$2",
|
||||
"twenty to *$2",
|
||||
"quarter to *$2",
|
||||
"ten to *$2",
|
||||
"five to *$2"
|
||||
],
|
||||
"text_scale":3.5
|
||||
},
|
||||
"en_US":{
|
||||
"hours":[
|
||||
"midnight", "one", "two", "three", "four", "five",
|
||||
"six", "seven", "eight", "nine", "ten", "eleven",
|
||||
"twelve", "one", "two", "three", "four", "five",
|
||||
"six", "seven", "eight", "nine", "ten", "eleven"
|
||||
],
|
||||
"minutes":[
|
||||
"*$1 o'clock",
|
||||
"five after *$1",
|
||||
"ten after *$1",
|
||||
"quarter after *$1",
|
||||
"twenty after *$1",
|
||||
"twenty five after *$1",
|
||||
"half past *$1",
|
||||
"twenty five to *$2",
|
||||
"twenty to *$2",
|
||||
"quarter to *$2",
|
||||
"ten to *$2",
|
||||
"five to *$2"
|
||||
],
|
||||
"text_scale":3.5
|
||||
},
|
||||
"es_ES":{
|
||||
"hours":[
|
||||
"doce", "una", "dos", "tres", "cuatro", "cinco",
|
||||
"seis", "siete", "ocho", "nueve", "diez", "once",
|
||||
"doce", "una", "dos", "tres", "cuatro", "cinco",
|
||||
"seis", "siete", "ocho", "nueve", "diez", "once"
|
||||
],
|
||||
"minutes":[
|
||||
"*$1 en punto",
|
||||
"*$1 y cinco",
|
||||
"*$1 y diez",
|
||||
"*$1 y cuarto",
|
||||
"*$1 y veinte",
|
||||
"*$1 y veinti- cinco",
|
||||
"*$1 y media",
|
||||
"*$2 menos veinti- cinco",
|
||||
"*$2 menos veinte",
|
||||
"*$2 menos cuarto",
|
||||
"*$2 menos diez",
|
||||
"*$2 menos cinco"
|
||||
],
|
||||
"text_scale":3.5
|
||||
},
|
||||
"fr_FR":{
|
||||
"hours":[
|
||||
"douze", "une", "deux", "trois", "quatre", "cinq",
|
||||
"six", "sept", "huit", "neuf", "dix", "onze",
|
||||
"douze", "une", "deux", "trois", "quatre", "cinq",
|
||||
"six", "sept", "huit", "neuf", "dix", "onze"
|
||||
],
|
||||
"minutes":[
|
||||
"*$1 heures",
|
||||
"*$1 heures cinq",
|
||||
"*$1 heures dix",
|
||||
"*$1 heures et quart",
|
||||
"*$1 heures vingt",
|
||||
"*$1 heures vingt- cinq",
|
||||
"*$1 heures et demie",
|
||||
"*$2 moins vingt- cinq",
|
||||
"*$2 heures moins vingt",
|
||||
"*$2 moins le quart",
|
||||
"*$2 heures moins dix",
|
||||
"*$2 heures moins cinq"
|
||||
],
|
||||
"text_scale":3.5
|
||||
},
|
||||
"no_NB":{
|
||||
"hours":[
|
||||
"tolv", "ett", "to", "tre", "fire", "fem",
|
||||
"seks", "sju", "åtte", "ni", "ti", "elleve",
|
||||
"tolv", "ett", "to", "tre", "fire", "fem",
|
||||
"seks", "sju", "åtte", "ni", "ti", "elleve"
|
||||
],
|
||||
"minutes":[
|
||||
"klokka er *$1",
|
||||
"fem over *$1",
|
||||
"ti over *$1",
|
||||
"kvart over *$1",
|
||||
"ti på halv *$2",
|
||||
"fem på halv *$2",
|
||||
"halv *$2",
|
||||
"fem over halv *$2",
|
||||
"ti over halv *$2",
|
||||
"kvart på *$2",
|
||||
"ti på *$2",
|
||||
"fem på *$2"
|
||||
],
|
||||
"text_scale":3.5
|
||||
},
|
||||
"nn_NO":{
|
||||
"hours":[
|
||||
"tolv", "eitt", "to", "tre", "fire", "fem",
|
||||
"seks", "sju", "åtte", "ni", "ti", "elleve",
|
||||
"tolv", "eitt", "to", "tre", "fire", "fem",
|
||||
"seks", "sju", "åtte", "ni", "ti", "elleve"
|
||||
],
|
||||
"minutes":[
|
||||
"klokka er *$1",
|
||||
"fem over *$1",
|
||||
"ti over *$1",
|
||||
"kvart over *$1",
|
||||
"ti på halv *$2",
|
||||
"fem på halv *$2",
|
||||
"halv *$2",
|
||||
"fem over halv *$2",
|
||||
"ti over halv *$2",
|
||||
"kvart på *$2",
|
||||
"ti på *$2",
|
||||
"fem på *$2"
|
||||
],
|
||||
"text_scale":3.5
|
||||
},
|
||||
"sv_SE":{
|
||||
"hours":[
|
||||
"tolv", "ett", "två", "tre", "fyra", "fem",
|
||||
"sex", "sju", "åtta", "nio", "tio", "elva",
|
||||
"tolv", "ett", "två", "tre", "fyra", "fem",
|
||||
"sex", "sju", "åtta", "nio", "tio", "elva"
|
||||
],
|
||||
"minutes":[
|
||||
"*$1",
|
||||
"fem över *$1",
|
||||
"tio över *$1",
|
||||
"kvart över *$1",
|
||||
"tjugo över *$1",
|
||||
"fem i halv *$2",
|
||||
"halv *$2",
|
||||
"fem över halv *$2",
|
||||
"tjugo i *$2",
|
||||
"kvart i *$2",
|
||||
"tio i *$2",
|
||||
"fem i *$2"
|
||||
],
|
||||
"text_scale":3.5
|
||||
},
|
||||
"de_DE":{
|
||||
"hours":[
|
||||
"zwölf", "eins", "zwei", "drei", "vier", "fünf",
|
||||
"sechs", "sieben", "acht", "neun", "zehn", "elf",
|
||||
"zwölf", "eins", "zwei", "drei", "vier", "fünf",
|
||||
"sechs", "sieben", "acht", "neun", "zehn", "elf"
|
||||
],
|
||||
"minutes":[
|
||||
"*$1 uhr",
|
||||
"fünf nach *$1",
|
||||
"zehn nach *$1",
|
||||
"viertel nach *$1",
|
||||
"zwanzig nach *$1",
|
||||
"fünf for halb *$2",
|
||||
"halb *$2",
|
||||
"fünf nach halb *$2",
|
||||
"zwanzig vor *$2",
|
||||
"viertel vor *$2",
|
||||
"zehn vor *$2",
|
||||
"fünf vor *$2"
|
||||
],
|
||||
"text_scale":3.5
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,75 @@
|
|||
// adapted from https://github.com/hallettj/Fuzzy-Text-International/
|
||||
const fuzzy_strings = require("Storage").readJSON("fuzzy_strings.json");
|
||||
|
||||
const SETTINGS_FILE = "fuzzyw.settings.json";
|
||||
let settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'language': 'System', 'alignment':'Centre'};
|
||||
|
||||
if (settings.language == 'System') {
|
||||
settings.language = require('locale').name;
|
||||
}
|
||||
|
||||
let fuzzy_string = fuzzy_strings[settings.language];
|
||||
|
||||
let timeout = 2.5*60;
|
||||
let drawTimeout;
|
||||
|
||||
function queueDraw(seconds) {
|
||||
let millisecs = seconds * 1000;
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, millisecs - (Date.now() % millisecs));
|
||||
}
|
||||
|
||||
const h = g.getHeight();
|
||||
const w = g.getWidth();
|
||||
let align_mode = 0;
|
||||
let align_pos = w/2;
|
||||
if (settings.alignment =='Left') {
|
||||
align_mode = -1;
|
||||
align_pos = 0;
|
||||
} else if (settings.alignment == 'Right') {
|
||||
align_mode = 1;
|
||||
align_pos = w;
|
||||
}
|
||||
|
||||
function getTimeString(date) {
|
||||
let segment = Math.round((date.getMinutes()*60 + date.getSeconds() + 1)/300);
|
||||
let hour = date.getHours() + Math.floor(segment/12);
|
||||
f_string = fuzzy_string.minutes[segment % 12];
|
||||
if (f_string.includes('$1')) {
|
||||
f_string = f_string.replace('$1', fuzzy_string.hours[(hour) % 24]);
|
||||
} else {
|
||||
f_string = f_string.replace('$2', fuzzy_string.hours[(hour + 1) % 24]);
|
||||
}
|
||||
return f_string;
|
||||
}
|
||||
|
||||
function draw() {
|
||||
let time_string = getTimeString(new Date()).replace('*', '');
|
||||
// print(time_string);
|
||||
g.setFont('Vector', (h-24*2)/fuzzy_string.text_scale);
|
||||
g.setFontAlign(align_mode, 0);
|
||||
g.clearRect(0, 24, w, h-24);
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawString(g.wrapString(time_string, w).join("\n"), align_pos, h/2);
|
||||
queueDraw(timeout);
|
||||
}
|
||||
|
||||
g.clear();
|
||||
draw();
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.setUI('clock');
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgP/ABX8oYFD+AFE8AFE8IXE8YFKwFCj08h4FBocenEHCIPDjk4CoIFBhlwAoeMuIFEuBSBAoOI+AFD4HxGoQFB+AFD4P4uYFC8P4gYFD/w7BAFEfApfEj+B/Ecg/Ah8A+EMg/Dw0YseHj/Dw/8sfHAoPH/lhDoIFBwFwj4FB40AvkPAoU8v4dCAoIdDw04FIMP4EOgFwh47Bj8EvEfw/DJwgFXABY"))
|
After Width: | Height: | Size: 893 B |
|
@ -0,0 +1,46 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "fuzzyw.settings.json";
|
||||
|
||||
var align_options = ['Left','Centre','Right'];
|
||||
var language_options = ['System', 'en_GB', 'en_US', 'es_ES', 'fr_FR', 'no_NO', 'sv_SE', 'de_DE'];
|
||||
|
||||
// initialize with default settings...
|
||||
let s = {'language': 'System', 'alignment': 'Centre'};
|
||||
|
||||
// ...and overwrite them with any saved values
|
||||
// This way saved values are preserved if a new version adds more settings
|
||||
const storage = require('Storage')
|
||||
let settings = storage.readJSON(SETTINGS_FILE, 1) || s;
|
||||
const saved = settings || {}
|
||||
for (const key in saved) {
|
||||
s[key] = saved[key]
|
||||
}
|
||||
|
||||
function save() {
|
||||
settings = s
|
||||
storage.write(SETTINGS_FILE, settings)
|
||||
}
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'Fuzzy Text Clock' },
|
||||
'< Back': back,
|
||||
'Language': {
|
||||
value: 0 | language_options.indexOf(s.language),
|
||||
min: 0, max: language_options.length - 1,
|
||||
format: v => language_options[v],
|
||||
onchange: v => {
|
||||
s.language = language_options[v];
|
||||
save();
|
||||
}
|
||||
},
|
||||
'Alignment': {
|
||||
value: 0 | align_options.indexOf(s.alignment),
|
||||
min: 0, max: align_options.length - 1,
|
||||
format: v => align_options[v],
|
||||
onchange: v => {
|
||||
s.alignment = align_options[v];
|
||||
save();
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"id":"fuzzyw",
|
||||
"name":"Fuzzy Text Clock",
|
||||
"shortName": "Fuzzy Text",
|
||||
"version": "0.01",
|
||||
"description": "An imprecise clock for when you're not in a rush",
|
||||
"readme": "README.md",
|
||||
"icon":"fuzzyw.png",
|
||||
"screenshots": [{"url":"fuzzyw-light.png"},{"url":"fuzzyw-dark.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"fuzzyw.app.js","url":"fuzzyw.app.js"},
|
||||
{"name":"fuzzyw.settings.js","url":"fuzzyw.settings.js"},
|
||||
{"name":"fuzzyw.img","url":"fuzzyw.icon.js","evaluate":true},
|
||||
{"name":"fuzzy_strings.json","url":"fuzzy_strings.json"}
|
||||
]
|
||||
}
|
|
@ -17,3 +17,4 @@
|
|||
0.17: Settings for mph/kph and other minor improvements.
|
||||
0.18: Fullscreen mode can now be enabled or disabled in the settings.
|
||||
0.19: Alarms can not go bigger than 100.
|
||||
0.20: Use alarm for alarm functionality instead of own implementation.
|
|
@ -3,7 +3,8 @@
|
|||
A simple LCARS inspired clock.
|
||||
Note: To display the steps, the wpedom app is required. To show weather data
|
||||
such as temperature, humidity or window you BangleJS must be connected
|
||||
with Gadgetbride and the weather app must be installed.
|
||||
with Gadgetbride and the weather app must be installed. To use the timer
|
||||
the "sched" app must be installed on your device.
|
||||
|
||||
## Control
|
||||
* Tap left / right to change between screens.
|
||||
|
@ -15,7 +16,7 @@ with Gadgetbride and the weather app must be installed.
|
|||
* Tab on left/right to switch between different screens.
|
||||
* Cusomizable data that is shown on screen 1 (steps, weather etc.)
|
||||
* Shows random and real images of planets.
|
||||
* Tap on top/bottom of screen 1 to activate an alarm.
|
||||
* Tap on top/bottom of screen 1 to activate an alarm. Depends on widtmr.
|
||||
* The lower orange line indicates the battery level.
|
||||
* Display graphs (day or month) for steps + hrm on the second screen.
|
||||
|
||||
|
@ -36,8 +37,9 @@ Access different screens via tap on the left/ right side of the screen
|
|||

|
||||

|
||||
|
||||
## Creator
|
||||
- [David Peer](https://github.com/peerdavid)
|
||||
|
||||
## Contributors
|
||||
- [David Peer](https://github.com/peerdavid).
|
||||
- [Adam Schmalhofer](https://github.com/adamschmalhofer).
|
||||
- [Jon Warrington](https://github.com/BartokW).
|
||||
- [Adam Schmalhofer](https://github.com/adamschmalhofer)
|
||||
- [Jon Warrington](https://github.com/BartokW)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const TIMER_IDX = "lcars";
|
||||
const SETTINGS_FILE = "lcars.setting.json";
|
||||
const locale = require('locale');
|
||||
const storage = require('Storage')
|
||||
const storage = require('Storage');
|
||||
let settings = {
|
||||
alarm: -1,
|
||||
dataRow1: "Steps",
|
||||
|
@ -124,11 +125,16 @@ Graphics.prototype.setFontAntonioLarge = function(scale) {
|
|||
*/
|
||||
var drawTimeout;
|
||||
function queueDraw() {
|
||||
|
||||
// Faster updates during alarm to ensure that it is
|
||||
// shown correctly...
|
||||
var timeout = isAlarmEnabled() ? 10000 : 60000;
|
||||
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}, timeout - (Date.now() % timeout));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -238,6 +244,7 @@ function drawInfo(){
|
|||
return;
|
||||
}
|
||||
|
||||
g.setFontAlign(-1, -1, 0);
|
||||
g.setFontAntonioMedium();
|
||||
g.setColor(cOrange);
|
||||
g.clearRect(120, 10, g.getWidth(), 75);
|
||||
|
@ -480,9 +487,6 @@ function draw(){
|
|||
// Queue draw first to ensure that its called in one minute again.
|
||||
queueDraw();
|
||||
|
||||
// First handle alarm to show this correctly afterwards
|
||||
handleAlarm();
|
||||
|
||||
// Next draw the watch face
|
||||
g.reset();
|
||||
g.clearRect(0, 0, g.getWidth(), g.getHeight());
|
||||
|
@ -561,43 +565,57 @@ function getWeather(){
|
|||
/*
|
||||
* Handle alarm
|
||||
*/
|
||||
function getCurrentTimeInMinutes(){
|
||||
return Math.floor(Date.now() / (1000*60));
|
||||
}
|
||||
|
||||
function isAlarmEnabled(){
|
||||
return settings.alarm >= 0;
|
||||
try{
|
||||
var alarm = require('sched');
|
||||
var alarmObj = alarm.getAlarm(TIMER_IDX);
|
||||
if(alarmObj===undefined || !alarmObj.on){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch(ex){ }
|
||||
return false;
|
||||
}
|
||||
|
||||
function getAlarmMinutes(){
|
||||
var currentTime = getCurrentTimeInMinutes();
|
||||
return settings.alarm - currentTime;
|
||||
if(!isAlarmEnabled()){
|
||||
return -1;
|
||||
}
|
||||
|
||||
var alarm = require('sched');
|
||||
var alarmObj = alarm.getAlarm(TIMER_IDX);
|
||||
return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000));
|
||||
}
|
||||
|
||||
function handleAlarm(){
|
||||
if(!isAlarmEnabled()){
|
||||
return;
|
||||
}
|
||||
function increaseAlarm(){
|
||||
try{
|
||||
var minutes = isAlarmEnabled() ? getAlarmMinutes() : 0;
|
||||
var alarm = require('sched')
|
||||
alarm.setAlarm(TIMER_IDX, {
|
||||
timer : (minutes+5)*60*1000,
|
||||
});
|
||||
alarm.reload();
|
||||
} catch(ex){ }
|
||||
}
|
||||
|
||||
if(getAlarmMinutes() > 0){
|
||||
return;
|
||||
}
|
||||
function decreaseAlarm(){
|
||||
try{
|
||||
var minutes = getAlarmMinutes();
|
||||
minutes -= 5;
|
||||
|
||||
// Alarm
|
||||
var t = 300;
|
||||
Bangle.buzz(t, 1)
|
||||
.then(() => new Promise(resolve => setTimeout(resolve, t)))
|
||||
.then(() => Bangle.buzz(t, 1))
|
||||
.then(() => new Promise(resolve => setTimeout(resolve, t)))
|
||||
.then(() => Bangle.buzz(t, 1))
|
||||
.then(() => new Promise(resolve => setTimeout(resolve, t)))
|
||||
.then(() => Bangle.buzz(t, 1))
|
||||
.then(() => new Promise(resolve => setTimeout(resolve, 5E3)))
|
||||
.then(() => {
|
||||
// Update alarm state to disabled
|
||||
settings.alarm = -1;
|
||||
storage.writeJSON(SETTINGS_FILE, settings);
|
||||
});
|
||||
var alarm = require('sched')
|
||||
alarm.setAlarm(TIMER_IDX, undefined);
|
||||
|
||||
if(minutes > 0){
|
||||
alarm.setAlarm(TIMER_IDX, {
|
||||
timer : minutes*60*1000,
|
||||
});
|
||||
}
|
||||
|
||||
alarm.reload();
|
||||
} catch(ex){ }
|
||||
}
|
||||
|
||||
|
||||
|
@ -625,27 +643,6 @@ Bangle.on('charging',function(charging) {
|
|||
});
|
||||
|
||||
|
||||
function increaseAlarm(){
|
||||
if(isAlarmEnabled() && getAlarmMinutes() < 95){
|
||||
settings.alarm += 5;
|
||||
} else {
|
||||
settings.alarm = getCurrentTimeInMinutes() + 5;
|
||||
}
|
||||
|
||||
storage.writeJSON(SETTINGS_FILE, settings);
|
||||
}
|
||||
|
||||
|
||||
function decreaseAlarm(){
|
||||
if(isAlarmEnabled() && (settings.alarm-5 > getCurrentTimeInMinutes())){
|
||||
settings.alarm -= 5;
|
||||
} else {
|
||||
settings.alarm = -1;
|
||||
}
|
||||
|
||||
storage.writeJSON(SETTINGS_FILE, settings);
|
||||
}
|
||||
|
||||
function feedback(){
|
||||
Bangle.buzz(40, 0.3);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "LCARS Clock",
|
||||
"shortName":"LCARS",
|
||||
"icon": "lcars.png",
|
||||
"version":"0.19",
|
||||
"version":"0.20",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"description": "Library Computer Access Retrieval System (LCARS) clock.",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Add the option to enable touching the widget only on clock and settings.
|
||||
0.03: Settings page now uses built-in min/max/wrap (fix #1607)
|
||||
0.04: Add masking widget input to other apps (using espruino/Espruino#2151), add a oversize option to increase the touch area.
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
# Light Switch Widget
|
||||
|
||||
Whis this widget I wanted to create a solution to quickly en-/disable the LCD backlight and even change the brightness.
|
||||
With this widget I wanted to create a solution to quickly en-/disable the LCD backlight and even change the brightness.
|
||||
In addition it shows the lock status with the option to personalize the lock icon with a tiny image.
|
||||
|
||||
All touch and drag inputs related to this widget are cached/masked to prevent actions in the active app.
|
||||
(See [espruino/Espruino#2151](https://github.com/espruino/Espruino/issues/2151) for more information.)
|
||||
|
||||
---
|
||||
### Control
|
||||
---
|
||||
|
@ -39,6 +42,9 @@ In addition it shows the lock status with the option to personalize the lock ico
|
|||
* _clk+launch_ -> on all apps of the types _clock_ and _launch_
|
||||
* _except apps_ -> on all apps of the types _clock_ and _launch_ and in the settings
|
||||
* _always on_ -> always enabled when the widget is displayed
|
||||
* __Oversize__
|
||||
_0px_ / _1px_ / _..._ / __20px__ / _..._ / _50px_
|
||||
To make it easier to hit the widget, this value extends the touch area of the widget in all directions.
|
||||
* __Drag Delay__
|
||||
_off_ / _50ms_ / _100ms_ / _..._ / __500ms__ / _..._ / _1000ms_
|
||||
Change the maximum delay between first touch and re-touch/drag to change the brightness or disable changing the brightness completely.
|
||||
|
@ -85,8 +91,6 @@ This images are stored in a seperate file _(lightswitch.images.json)_.
|
|||
### Worth Mentioning
|
||||
---
|
||||
#### To do list
|
||||
* Catch the touch and draw input related to this widget to prevent actions in the active app.
|
||||
_(For now I have no idea how to achieve this, help is appreciated)_
|
||||
* Manage images for the lock icon through a _Customize and Upload App_ page.
|
||||
|
||||
#### Requests, Bugs and Feedback
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "lightswitch",
|
||||
"name": "Light Switch Widget",
|
||||
"shortName": "Light Switch",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "A fast way to switch LCD backlight on/off, change the brightness and show the lock status. All in one widget.",
|
||||
"icon": "images/app.png",
|
||||
"screenshots": [
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
var settings = Object.assign({
|
||||
colors: "011",
|
||||
image: "default",
|
||||
touchOn: "clock,launch",
|
||||
touchOn: "always",
|
||||
oversize: 20,
|
||||
dragDelay: 500,
|
||||
minValue: 0.1,
|
||||
unlockSide: "",
|
||||
|
@ -45,7 +46,7 @@
|
|||
return {
|
||||
value: entry.value.indexOf(settings[key]),
|
||||
min : 0,
|
||||
max : entry.value.length-1,
|
||||
max : entry.value.length - 1,
|
||||
wrap : true,
|
||||
format: v => entry.title ? entry.title[v] : entry.value[v],
|
||||
onchange: function(v) {
|
||||
|
@ -57,11 +58,11 @@
|
|||
// return entry for numerical value
|
||||
return {
|
||||
value: settings[key] * entry.factor,
|
||||
step: entry.step,
|
||||
format: v => v > 0 ? v + entry.unit : "off",
|
||||
min : entry.min,
|
||||
max : entry.max,
|
||||
step: entry.step,
|
||||
wrap : true,
|
||||
format: v => v > 0 ? v + entry.unit : "off",
|
||||
onchange: function(v) {
|
||||
writeSetting(key, v / entry.factor, entry.drawWidgets);
|
||||
},
|
||||
|
@ -96,6 +97,14 @@
|
|||
value: ["", "clock", "clock,setting.app.js", "clock,launch", "clock,setting.app.js,launch", "always"],
|
||||
drawWidgets: true
|
||||
},
|
||||
oversize: {
|
||||
factor: 1,
|
||||
unit: "px",
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 1,
|
||||
drawWidgets: true
|
||||
},
|
||||
dragDelay: {
|
||||
factor: 1,
|
||||
unit: "ms",
|
||||
|
@ -142,6 +151,7 @@
|
|||
"Image": getEntry("image"),
|
||||
"-- Control": 0,
|
||||
"Touch": getEntry("touchOn"),
|
||||
"Oversize": getEntry("oversize"),
|
||||
"Drag Delay": getEntry("dragDelay"),
|
||||
"Min Value": getEntry("minValue"),
|
||||
"-- Unlock": 0,
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
"101" -> magenta
|
||||
|
||||
* image: string //
|
||||
"default" ->
|
||||
"random" ->
|
||||
"default" -> image nearest to the default lock
|
||||
"random" -> a random image from all available
|
||||
|
||||
* touchOn: string // select when widget touch is active
|
||||
"" -> only on default clock
|
||||
|
@ -19,6 +19,9 @@
|
|||
"clock,launch" -> on all clocks and lanchers (default)
|
||||
"always" -> always
|
||||
|
||||
* oversize: int // extends the touch area of the widget in px in all directions
|
||||
0 to 50, 20 as default
|
||||
|
||||
* dragDelay: int // drag listener reset time in ms
|
||||
// time until a drag is needed to activate backlight changing mode
|
||||
0 -> disabled
|
||||
|
@ -59,6 +62,7 @@
|
|||
"colors": "011",
|
||||
"image": "default",
|
||||
"touchOn": "clock,launch",
|
||||
"oversize": 20,
|
||||
"dragDelay": 500,
|
||||
"minValue": 0.1,
|
||||
"unlockSide": "",
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
var settings = Object.assign({
|
||||
colors: "011",
|
||||
image: "default",
|
||||
touchOn: "clock,launch",
|
||||
touchOn: "always",
|
||||
oversize: 20,
|
||||
dragDelay: 500,
|
||||
minValue: 0.1,
|
||||
unlockSide: "",
|
||||
|
@ -162,6 +163,11 @@
|
|||
// change brigthness value, skip write to storage while still touching
|
||||
w.changeValue(value, event.b);
|
||||
|
||||
// masks this drag event by messing up the event handler
|
||||
// see https://github.com/espruino/Espruino/issues/2151
|
||||
Bangle.removeListener("drag", w.dragListener);
|
||||
Bangle["#ondrag"] = [w.dragListener].concat(Bangle["#ondrag"]);
|
||||
|
||||
// on touch release remove drag listener and reset drag status to indicate stopped drag action
|
||||
if (!event.b) {
|
||||
Bangle.removeListener("drag", w.dragListener);
|
||||
|
@ -184,14 +190,14 @@
|
|||
if (w.dragStatus === "off") {
|
||||
|
||||
// check if inside widget area
|
||||
if (!(!w || cursor.x < w.x || cursor.x > w.x + w.width ||
|
||||
cursor.y < w.y || cursor.y > w.y + 23)) {
|
||||
if (!(!w || cursor.x < w.x - w.oversize || cursor.x > w.x + w.width + w.oversize ||
|
||||
cursor.y < w.y - w.oversize || cursor.y > w.y + 23 + w.oversize)) {
|
||||
// first touch feedback
|
||||
Bangle.buzz(25);
|
||||
// check if drag is disabled
|
||||
if (w.dragDelay) {
|
||||
// add drag listener
|
||||
Bangle.on("drag", w.dragListener);
|
||||
// add drag listener at first position
|
||||
Bangle["#ondrag"] = [w.dragListener].concat(Bangle["#ondrag"]);
|
||||
// set drag timeout
|
||||
w.dragStatus = setTimeout((w) => {
|
||||
// remove drag listener
|
||||
|
@ -204,6 +210,10 @@
|
|||
}
|
||||
// switch backlight
|
||||
w.changeValue();
|
||||
// masks this touch event by messing up the event handler
|
||||
// see https://github.com/espruino/Espruino/issues/2151
|
||||
Bangle.removeListener("touch", w.touchListener);
|
||||
Bangle["#ontouch"] = [w.touchListener].concat(Bangle["#ontouch"]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -236,11 +246,11 @@
|
|||
// add lock listener
|
||||
Bangle.on("lock", w.draw);
|
||||
|
||||
// add touch listener to control the light depending on settings
|
||||
// add touch listener to control the light depending on settings at first position
|
||||
if (w.touchOn === "always" || !global.__FILE__ ||
|
||||
w.touchOn.includes(__FILE__) ||
|
||||
w.touchOn.includes(require("Storage").readJSON(__FILE__.replace("app.js", "info")).type))
|
||||
Bangle.on("touch", w.touchListener);
|
||||
Bangle["#ontouch"] = [w.touchListener].concat(Bangle["#ontouch"]);
|
||||
|
||||
// add tap listener to unlock and/or flash backlight
|
||||
if (w.unlockSide || w.tapSide) Bangle.on("tap", require("lightswitch.js").tapListener);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<p>Then click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||
|
||||
<script src="../../core/lib/customize.js"></script>
|
||||
<script src="../../core/js/utils.js"></script>
|
||||
<script src="locales.js" charset="utf-8"></script>
|
||||
|
||||
<script>
|
||||
|
@ -59,7 +60,7 @@ exports = { name : "en_GB", currencySym:"£",
|
|||
/*else if (chCode<256) // it's non-ascii, but <256 - just escape it
|
||||
n = chCode;*/
|
||||
else {
|
||||
if (charFallbacks[ch]) return charFallbacks[ch];
|
||||
if (CODEPAGE_CONVERSIONS[ch]) return CODEPAGE_CONVERSIONS[ch];
|
||||
console.error(`Locale ${lang}: Character ${ch} (${chCode}) is not in Code Page ${codePage.name}`);
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -34,33 +34,8 @@ const codePages = {
|
|||
`.replace(/[ \n]/g,"")
|
||||
}
|
||||
};
|
||||
/* When it's not in the codepage, try and use
|
||||
these conversions */
|
||||
const charFallbacks = {
|
||||
"ą":"a",
|
||||
"ā":"a",
|
||||
"č":"c",
|
||||
"ć":"c",
|
||||
"ě":"e",
|
||||
"ę":"e",
|
||||
"ē":"e",
|
||||
"ģ":"g",
|
||||
"i":"ī",
|
||||
"ķ":"k",
|
||||
"ļ":"l",
|
||||
"ł":"l",
|
||||
"ń":"n",
|
||||
"ņ":"n",
|
||||
"ő":"o",
|
||||
"ó":"o",
|
||||
"ř":"r",
|
||||
"ś":"s",
|
||||
"š":"s",
|
||||
"ū":"u",
|
||||
"ż":"z",
|
||||
"ź":"z",
|
||||
"ž":"z",
|
||||
};
|
||||
|
||||
// charFallbacks is now in core/js/utils.js as CODEPAGE_CONVERSIONS
|
||||
|
||||
/*
|
||||
timePattern / datePattern:
|
||||
|
@ -80,7 +55,7 @@ timePattern / datePattern:
|
|||
%S second (00..60)
|
||||
%p locale's equivalent of either AM or PM; blank if not known
|
||||
%P like %p, but lower case
|
||||
*/
|
||||
*/
|
||||
|
||||
var locales = {
|
||||
"en_GB": { // this is default
|
||||
|
@ -154,7 +129,7 @@ var locales = {
|
|||
month: "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
||||
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
||||
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
|
||||
"< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" }
|
||||
},
|
||||
"en_US": {
|
||||
|
@ -271,7 +246,7 @@ var locales = {
|
|||
thousands_sep: ".",
|
||||
currency_symbol: "kr",
|
||||
int_curr_symbol: "SKR",
|
||||
speed: "kmh",
|
||||
speed: "km/tim",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: { 0: "fm", 1: "em" },
|
||||
|
@ -289,7 +264,7 @@ var locales = {
|
|||
thousands_sep: ".",
|
||||
currency_symbol: "kr",
|
||||
int_curr_symbol: "SKR",
|
||||
speed: 'kmh',
|
||||
speed: 'km/h',
|
||||
distance: { "0": "m", "1": "km" },
|
||||
temperature: '°C',
|
||||
ampm: { 0: "", 1: "" },
|
||||
|
@ -353,7 +328,7 @@ var locales = {
|
|||
month: "Jänner,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
||||
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
||||
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
|
||||
"< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" }
|
||||
},
|
||||
"en_IL": {
|
||||
|
@ -475,7 +450,7 @@ var locales = {
|
|||
distance: { "0": "m", "1": "km" },
|
||||
temperature: '°C',
|
||||
ampm: { 0: "", 1: "" },
|
||||
timePattern: { 0: "%HH:%MM.%SS ", 1: "%HH:%MM" }, // 17:00.00 // 17:00
|
||||
timePattern: { 0: "%HH:%MM.%SS ", 1: "%HH:%MM" }, // 17:00.00 // 17:00
|
||||
datePattern: { 0: "%d %b %Y", "1": "%d/%m/%Y" }, // 1 marzo 2020 // 01/03/2020
|
||||
abmonth: "gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic",
|
||||
month: "gennaio,febbraio,marzo,aprile,maggio,giugno,luglio,agosto,settembre,ottobre,novembre,dicembre",
|
||||
|
@ -493,7 +468,7 @@ var locales = {
|
|||
distance: { "0": "m", "1": "km" },
|
||||
temperature: '°C',
|
||||
ampm: { 0: "", 1: "" },
|
||||
timePattern: { 0: "%HH:%MM.%SS ", 1: "%HH:%MM" }, // 17:00.00 // 17:00
|
||||
timePattern: { 0: "%HH:%MM.%SS ", 1: "%HH:%MM" }, // 17:00.00 // 17:00
|
||||
datePattern: { 0: "%d %b %Y", "1": "%d/%m/%Y" }, // 1 marzo 2020 // 01/03/2020
|
||||
abmonth: "gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic",
|
||||
month: "gennaio,febbraio,marzo,aprile,maggio,giugno,luglio,agosto,settembre,ottobre,novembre,dicembre",
|
||||
|
@ -572,7 +547,7 @@ var locales = {
|
|||
abday: "dg,dl,dm,dc,dj,dv,ds",
|
||||
day: "dimenge,diluns,dimars,dimècres,dijòus,divendres,dissabte",
|
||||
trans: { yes: "òc", Yes: "Òc", no: "non", No: "Non", ok: "ok", on: "on", off: "off" }
|
||||
},
|
||||
},
|
||||
"pt_BR": {
|
||||
lang: "pt_BR",
|
||||
decimal_point: ",",
|
||||
|
@ -591,7 +566,7 @@ var locales = {
|
|||
day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado",
|
||||
trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "certo", on: "ligado", off: "desligado" }
|
||||
},
|
||||
"cs_CZ": { // THIS NEVER WORKED PROPERLY - many chars are not in the ISO8859-1 codepage and we use charFallbacks
|
||||
"cs_CZ": { // THIS NEVER WORKED PROPERLY - many chars are not in the ISO8859-1 codepage and we use CODEPAGE_CONVERSIONS
|
||||
lang: "cs_CZ",
|
||||
decimal_point: ",",
|
||||
thousands_sep: " ",
|
||||
|
@ -681,6 +656,42 @@ var locales = {
|
|||
day: "Pirmdiena,Otrdiena,Trešdiena,Ceturtdiena,Piektdiena,Sestdiena,Svētdiena",
|
||||
trans: { yes: "jā", Yes: "Jā", no: "nē", No: "Nē", ok: "labi", on: "Ieslēgt", off: "Izslēgt", "< Back": "< Atpakaļ" }
|
||||
},
|
||||
"nn_NO": { // Using charfallbacks
|
||||
lang: "nn_NO",
|
||||
decimal_point: ",",
|
||||
thousands_sep: " ",
|
||||
currency_symbol: "kr",
|
||||
int_curr_symbol: "NOK",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: { 0: "", 1: "" },
|
||||
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20
|
||||
abmonth: "Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Des",
|
||||
month: "Januar,Februar,Mars,April,Mai,Juni,Juli,August,September,Oktober,November,Desember",
|
||||
abday: "Su,Må,Ty,On,To,Fr,La",
|
||||
day: "Sundag,Måndag,Tysdag,Onsdag,Torsdag,Fredag,Laurdag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nei", No: "Nei", ok: "ok", on: "på", off: "av", "< Back": "< Tilbake", "Delete": "Slett", "Mark Unread": "Merk som ulesen" }
|
||||
},
|
||||
"nb_NO": { // Using charfallbacks
|
||||
lang: "nb_NO",
|
||||
decimal_point: ",",
|
||||
thousands_sep: " ",
|
||||
currency_symbol: "kr",
|
||||
int_curr_symbol: "NOK",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: { 0: "", 1: "" },
|
||||
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20
|
||||
abmonth: "Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Des",
|
||||
month: "Januar,Februar,Mars,April,Mai,Juni,Juli,August,September,Oktober,November,Desember",
|
||||
abday: "Sø,Ma,Ti,On,To,Fr,Lø",
|
||||
day: "Søndag,Mandag,Tirsdag,Onsdag,Torsdag,Fredag,Lørdag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nei", No: "Nei", ok: "ok", on: "på", off: "av", "< Back": "< Tilbake", "Delete": "Slett", "Mark Unread": "Merk som ulest" }
|
||||
},
|
||||
/*,
|
||||
"he_IL": { // This won't work until we get a font - see https://github.com/espruino/BangleApps/issues/399
|
||||
codePage : "ISO8859-8",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Create New App !
|
|
@ -0,0 +1,2 @@
|
|||
Denti :
|
||||
This teeth washing assistan helps you to wash your teeth
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4kA///A4M79/6823gvb70/qvLrXrrXdqmyzl2gvTzn0glS4ttuZh9iMQCykBvsRC6vUC6sdppIUgMb2Ol+IYBGaBGB6Oll1xGKMRpsfi/FrQXRjtHutai/uC6EBunRj9SrB5DPppeBj/njVV0IICu1xUpscqvBvgSCC5qlCuN1kqnCI5xGB69VwP1qJ2QUoMR4tcuOBA4LXOUoOKi/lq8RgMd64YMUoXCr8XFgMXs1p6JeM7cRjVS0PZtN5s2Z3oXKiKlBivHjl4swACu4XJgIWB6mxv0l0MZC4eZ3YXHKgPdolLuMR+tXC4lmzq/GQQNEonduILBDwOWC4lmBgLRG3vREQcBFwoABtfRC4kXaIKyFjAHBjtmy4EBvoXFjtBNA0X13u0N5j/O5+BEwhGBDwZICgPykUil0RwUikfqC4kR3YGCgOGwIuBCwIAB+NSkXh/3xIwlIiEBC4MbDgMaCoMl8U+AQNajXhIwcUIwIXBI4ceC4UcmVXJoMfC4nUVooABjhGC0LEBJAM+C4alBew5HCGAPu8QEBC4bsB2IXLAAk+0KlHXwoXLUop1BgK/CmQXG9ynCjtHIwUB3ZLDgJaCC4qJBLwJGDC4zvCAAbvBBoUReYZHCMAcTCYWvGgMu95CDRo7AFl8RiqmCCZSQGqKtCRwYAMgLqB4JYBmXuwIXP4UiRIXj9xyDCpTDBuUuYoWDRwYAKWIUVl4SCjXnC5I5DjORCQMxCQUf8JdNZITMEAggAvA="))
|
|
@ -0,0 +1,99 @@
|
|||
var i = 0;
|
||||
var counter = 10;
|
||||
var counterInterval;
|
||||
|
||||
var img = Graphics.createImage(`
|
||||
|
||||
##### #####
|
||||
# ##### #
|
||||
# #
|
||||
# #
|
||||
## ##
|
||||
## ##
|
||||
## ##
|
||||
# #### #
|
||||
# # # #
|
||||
# # # #
|
||||
## ##
|
||||
## ##
|
||||
`);
|
||||
var img1 = Graphics.createImage(`
|
||||
|
||||
|
||||
### # ##### ## ####
|
||||
# # # # # # #
|
||||
# # ### # # ####
|
||||
# # # ###### # #
|
||||
### #### ##### # # # #
|
||||
|
||||
##### #####
|
||||
# ##### #
|
||||
# #
|
||||
# #
|
||||
## ##
|
||||
## ##
|
||||
## ##
|
||||
# #### #
|
||||
# # # #
|
||||
# # # #
|
||||
## ##
|
||||
## ##
|
||||
`);
|
||||
g.setColor('#012345');
|
||||
|
||||
function outOfTime() {
|
||||
if (counterInterval) return;
|
||||
E.showMessage("Out of Time", "My Timer");
|
||||
Bangle.beep(200, 4000)
|
||||
.then(() => new Promise(resolve => setTimeout(resolve,200)))
|
||||
.then(() => Bangle.beep(200, 3000));
|
||||
// again, 10 secs later
|
||||
setTimeout(outOfTime, 10000);
|
||||
g.setColor('#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0'));
|
||||
}
|
||||
|
||||
function immagine(){
|
||||
g.drawImage(img1, 90, 20, {scale:2});
|
||||
}
|
||||
|
||||
function countDown() {
|
||||
counter--;
|
||||
// Out of time
|
||||
if (counter<=0) {
|
||||
clearInterval(counterInterval);
|
||||
counterInterval = undefined;
|
||||
setWatch(startTimer, (process.env.HWVERSION==2) ? BTN1 : BTN2);
|
||||
g.clear(img);
|
||||
outOfTime();
|
||||
return;
|
||||
|
||||
}
|
||||
g.clear(1);
|
||||
g.setFontAlign(0,0); // center font
|
||||
g.setFont("Vector",80); // vector font, 80px
|
||||
// draw the current counter value
|
||||
g.drawImage(img, 90, 20, {scale:2});
|
||||
g.drawString(counter,120,120);
|
||||
g.drawLine(50,50,180,50);
|
||||
g.drawLine(50,51,180,51);
|
||||
g.drawLine(50,52,180,52);
|
||||
// optional - this keeps the watch LCD lit up
|
||||
Bangle.setLCDPower(1);
|
||||
if (counter<=5){
|
||||
immagine();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function startTimer() {
|
||||
counter = 10;
|
||||
countDown();
|
||||
if (!counterInterval)
|
||||
counterInterval = setInterval(countDown, 1000);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
startTimer();
|
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,15 @@
|
|||
{ "id": "megadenti",
|
||||
"name": "Denti",
|
||||
"shortName":"My Denti",
|
||||
"icon": "brush-teeth.png",
|
||||
"version":"0.01",
|
||||
"description": "This app allows you wash your teeth in an efficent way. A coloured timer guides you while your smile becomes bright!",
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS"],
|
||||
"allow_emulator": true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"megadenti.app.js","url":"app.js"},
|
||||
{"name":"megadenti.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -43,3 +43,4 @@
|
|||
0.28: Option to auto-unlock the watch when a new message arrives
|
||||
0.29: Fix message list overwrites on Bangle.js 1 (fix #1642)
|
||||
0.30: Add new Icons (Youtube, Twitch, MS TODO, Teams, Snapchat, Signal, Post & DHL, Nina, Lieferando, Kalender, Discord, Corona Warn, Bibel)
|
||||
0.31: Option to disable icon flashing
|
||||
|
|
|
@ -21,6 +21,7 @@ is chosen if there isn't much message text, but this specifies the smallest the
|
|||
it starts getting clipped.
|
||||
* `Auto-Open Music` - Should the app automatically open when the phone starts playing music?
|
||||
* `Unlock Watch` - Should the app unlock the watch when a new message arrives, so you can touch the buttons at the bottom of the app?
|
||||
* `Flash Icon` - Toggle flashing of the widget icon.
|
||||
|
||||
## New Messages
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "messages",
|
||||
"name": "Messages",
|
||||
"version": "0.30",
|
||||
"version": "0.31",
|
||||
"description": "App to display notifications from iOS and Gadgetbridge/Android",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
settings.unlockWatch=!!settings.unlockWatch;
|
||||
settings.openMusic=!!settings.openMusic;
|
||||
settings.maxUnreadTimeout=240;
|
||||
if (settings.flash===undefined) settings.flash=true;
|
||||
return settings;
|
||||
}
|
||||
function updateSetting(setting, value) {
|
||||
|
@ -15,18 +16,10 @@
|
|||
require('Storage').writeJSON("messages.settings.json", settings);
|
||||
}
|
||||
|
||||
var vibPatterns = [/*LANG*/"Off", ".", "-", "--", "-.-", "---"];
|
||||
var mainmenu = {
|
||||
"" : { "title" : /*LANG*/"Messages" },
|
||||
"< Back" : back,
|
||||
/*LANG*/'Vibrate': {
|
||||
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*/'Vibrate': require("buzz_menu").pattern(settings().vibrate, v => updateSetting("vibrate", v)),
|
||||
/*LANG*/'Repeat': {
|
||||
value: settings().repeat,
|
||||
min: 0, max: 10,
|
||||
|
@ -55,6 +48,11 @@
|
|||
format: v => v?/*LANG*/'Yes':/*LANG*/'No',
|
||||
onchange: v => updateSetting("unlockWatch", v)
|
||||
},
|
||||
/*LANG*/'Flash Icon': {
|
||||
value: !!settings().flash,
|
||||
format: v => v?/*LANG*/'Yes':/*LANG*/'No',
|
||||
onchange: v => updateSetting("flash", v)
|
||||
},
|
||||
};
|
||||
E.showMenu(mainmenu);
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
WIDGETS["messages"]={area:"tl", width:0, iconwidth:24,
|
||||
draw:function() {
|
||||
draw:function(recall) {
|
||||
// If we had a setTimeout queued from the last time we were called, remove it
|
||||
if (WIDGETS["messages"].i) {
|
||||
clearTimeout(WIDGETS["messages"].i);
|
||||
|
@ -8,15 +8,18 @@ draw:function() {
|
|||
Bangle.removeListener('touch', this.touch);
|
||||
if (!this.width) return;
|
||||
var c = (Date.now()-this.t)/1000;
|
||||
g.reset().clearRect(this.x, this.y, this.x+this.width, this.y+this.iconwidth);
|
||||
g.drawImage((c&1) ? atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+DAADDAADDAADDwAPD8A/DOBzDDn/DA//DAHvDAPvjAPvjAPvjAPvh///gf/vAAD+AAB8AAAAA==") : atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+D///D///A//8CP/xDj/HD48DD+B8D/D+D/3vD/vvj/vvj/vvj/vvh/v/gfnvAAD+AAB8AAAAA=="), this.x, this.y);
|
||||
let settings = require('Storage').readJSON("messages.settings.json", true) || {};
|
||||
if (settings.flash===undefined) settings.flash = true;
|
||||
if (recall !== true || settings.flash) {
|
||||
g.reset().clearRect(this.x, this.y, this.x+this.width, this.y+23);
|
||||
g.drawImage(settings.flash && (c&1) ? atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+DAADDAADDAADDwAPD8A/DOBzDDn/DA//DAHvDAPvjAPvjAPvjAPvh///gf/vAAD+AAB8AAAAA==") : atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+D///D///A//8CP/xDj/HD48DD+B8D/D+D/3vD/vvj/vvj/vvj/vvh/v/gfnvAAD+AAB8AAAAA=="), this.x, this.y-1);
|
||||
}
|
||||
if (settings.repeat===undefined) settings.repeat = 4;
|
||||
if (c<120 && (Date.now()-this.l)>settings.repeat*1000) {
|
||||
this.l = Date.now();
|
||||
WIDGETS["messages"].buzz(); // buzz every 4 seconds
|
||||
}
|
||||
WIDGETS["messages"].i=setTimeout(()=>WIDGETS["messages"].draw(), 1000);
|
||||
WIDGETS["messages"].i=setTimeout(()=>WIDGETS["messages"].draw(true), 1000);
|
||||
if (process.env.HWVERSION>1) Bangle.on('touch', this.touch);
|
||||
},show:function(quiet) {
|
||||
WIDGETS["messages"].t=Date.now(); // first time
|
||||
|
@ -32,14 +35,7 @@ draw:function() {
|
|||
Bangle.drawWidgets();
|
||||
},buzz:function() {
|
||||
if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return; // never buzz during Quiet Mode
|
||||
let v = (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();
|
||||
require("buzz").pattern((require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || ".");
|
||||
},touch:function(b,c) {
|
||||
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;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.01: First release
|
||||
0.02: Enhanced icon, make it bolder
|
||||
0.03: Fixed issue with defaulting back to London
|
||||
0.04: Fixed issue selecting Frankfurt not saved
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"icon": "mylocation.png",
|
||||
"type": "app",
|
||||
"screenshots": [{"url":"screenshot_1.png"}],
|
||||
"version":"0.03",
|
||||
"version":"0.04",
|
||||
"description": "Sets and stores the lat and long of your preferred City or it can be set from the GPS. mylocation.json can be used by other apps that need your main location lat and lon. See README",
|
||||
"readme": "README.md",
|
||||
"tags": "tool,utility",
|
||||
|
|
|
@ -61,7 +61,7 @@ function showMainMenu() {
|
|||
min: 0, max: locations.length - 1,
|
||||
format: v => locations[v],
|
||||
onchange: v => {
|
||||
if (v != 6) {
|
||||
if (locations[v] !== "???") {
|
||||
s.location = locations[v];
|
||||
s.lat = lats[v];
|
||||
s.lon = lons[v];
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.01: Launch app.
|
||||
0.02: 12k steps are 360 degrees - improves readability of steps.
|
||||
0.03: Battery improvements through sleep (no minute updates) and partial updates of drawing.
|
||||
0.03: Battery improvements through sleep (no minute updates) and partial updates of drawing.
|
||||
0.04: Use alarm for timer instead of own alarm implementation.
|
|
@ -8,8 +8,8 @@ black one the battery level (100% = 360 degrees).
|
|||
The selected theme is also respected. Note that this watch face is in fullscreen
|
||||
mode, but widgets are still loaded in background.
|
||||
|
||||
## Other features
|
||||
- Set a timer - simply touch top (+5min.) or bottom (-5 min.).
|
||||
## Other Features
|
||||
- Set a timer - simply touch top (+5min.) or bottom (-5 min.). This only works if "sched" is installed.
|
||||
- If the weather is available through the weather app, the outside temp. will be shown.
|
||||
- Sleep modus at midnight to save more battery (no minute updates).
|
||||
- Icons for charging and GPS.
|
||||
|
@ -29,5 +29,5 @@ which helped a lot for this development.
|
|||
|
||||
Icons from <a href="https://www.flaticon.com/free-icons" title="icons">by Freepik - Flaticon</a>
|
||||
|
||||
## Contributors
|
||||
## Creator
|
||||
- [David Peer](https://github.com/peerdavid).
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Not Analog",
|
||||
"shortName":"Not Analog",
|
||||
"icon": "notanalog.png",
|
||||
"version":"0.03",
|
||||
"version":"0.04",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"description": "An analog watch face for people that can not read analog watch faces.",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* NOT ANALOG CLOCK
|
||||
*/
|
||||
|
||||
const TIMER_IDX = "notanalog";
|
||||
const locale = require('locale');
|
||||
const storage = require('Storage')
|
||||
const SETTINGS_FILE = "notanalog.setting.json";
|
||||
|
@ -291,7 +291,6 @@ function drawSleep(){
|
|||
function draw(fastUpdate){
|
||||
// Execute handlers
|
||||
handleState(fastUpdate);
|
||||
handleAlarm();
|
||||
|
||||
if(state.sleep){
|
||||
drawSleep();
|
||||
|
@ -377,82 +376,80 @@ Bangle.on('touch', function(btn, e){
|
|||
* Some helpers
|
||||
*/
|
||||
function queueDraw() {
|
||||
|
||||
// Faster updates during alarm to ensure that it is
|
||||
// shown correctly...
|
||||
var timeout = isAlarmEnabled() ? 10000 : 60000;
|
||||
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw(true);
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
draw();
|
||||
}, timeout - (Date.now() % timeout));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle alarm
|
||||
*/
|
||||
function getCurrentTimeInMinutes(){
|
||||
return Math.floor(Date.now() / (1000*60));
|
||||
}
|
||||
|
||||
function isAlarmEnabled(){
|
||||
return settings.alarm >= 0;
|
||||
}
|
||||
try{
|
||||
var alarm = require('sched');
|
||||
var alarmObj = alarm.getAlarm(TIMER_IDX);
|
||||
if(alarmObj===undefined || !alarmObj.on){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch(ex){ }
|
||||
return false;
|
||||
}
|
||||
|
||||
function getAlarmMinutes(){
|
||||
var currentTime = getCurrentTimeInMinutes();
|
||||
return settings.alarm - currentTime;
|
||||
}
|
||||
|
||||
function handleAlarm(){
|
||||
if(!isAlarmEnabled()){
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(getAlarmMinutes() > 0){
|
||||
return;
|
||||
}
|
||||
|
||||
// Alarm
|
||||
var t = 300;
|
||||
Bangle.buzz(t, 1)
|
||||
.then(() => new Promise(resolve => setTimeout(resolve, t)))
|
||||
.then(() => Bangle.buzz(t, 1))
|
||||
.then(() => new Promise(resolve => setTimeout(resolve, t)))
|
||||
.then(() => Bangle.buzz(t, 1))
|
||||
.then(() => new Promise(resolve => setTimeout(resolve, t)))
|
||||
.then(() => Bangle.buzz(t, 1))
|
||||
.then(() => new Promise(resolve => setTimeout(resolve, 5E3)))
|
||||
.then(() => {
|
||||
// Update alarm state to disabled
|
||||
settings.alarm = -1;
|
||||
storage.writeJSON(SETTINGS_FILE, settings);
|
||||
});
|
||||
var alarm = require('sched');
|
||||
var alarmObj = alarm.getAlarm(TIMER_IDX);
|
||||
return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000));
|
||||
}
|
||||
|
||||
|
||||
function increaseAlarm(){
|
||||
if(isAlarmEnabled()){
|
||||
settings.alarm += 5;
|
||||
} else {
|
||||
settings.alarm = getCurrentTimeInMinutes() + 5;
|
||||
}
|
||||
|
||||
storage.writeJSON(SETTINGS_FILE, settings);
|
||||
try{
|
||||
var minutes = isAlarmEnabled() ? getAlarmMinutes() : 0;
|
||||
var alarm = require('sched')
|
||||
alarm.setAlarm(TIMER_IDX, {
|
||||
timer : (minutes+5)*60*1000,
|
||||
});
|
||||
alarm.reload();
|
||||
} catch(ex){ }
|
||||
}
|
||||
|
||||
|
||||
function decreaseAlarm(){
|
||||
if(isAlarmEnabled() && (settings.alarm-5 > getCurrentTimeInMinutes())){
|
||||
settings.alarm -= 5;
|
||||
} else {
|
||||
settings.alarm = -1;
|
||||
}
|
||||
try{
|
||||
var minutes = getAlarmMinutes();
|
||||
minutes -= 5;
|
||||
|
||||
storage.writeJSON(SETTINGS_FILE, settings);
|
||||
var alarm = require('sched')
|
||||
alarm.setAlarm(TIMER_IDX, undefined);
|
||||
|
||||
if(minutes > 0){
|
||||
alarm.setAlarm(TIMER_IDX, {
|
||||
timer : minutes*60*1000,
|
||||
});
|
||||
}
|
||||
|
||||
alarm.reload();
|
||||
} catch(ex){ }
|
||||
}
|
||||
|
||||
function feedback(){
|
||||
Bangle.buzz(40, 0.6);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Lets start widgets, listen for btn etc.
|
||||
*/
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"shortName": "Q Alarm",
|
||||
"icon": "app.png",
|
||||
"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",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"storage": [
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
0.01: New App!
|
||||
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,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! (No change from 0 on timers)
|
||||
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=="))
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,44 @@
|
|||
// 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 currentTime = (time.getHours()*3600000)+(time.getMinutes()*60000)+(time.getSeconds()*1000);
|
||||
var d = time.getDate();
|
||||
var active = alarms.filter(
|
||||
a=>a.on && // enabled
|
||||
a.last!=d && // not already fired today
|
||||
a.t+60000>currentTime && // is not in the past by >1 minute
|
||||
(a.dow>>time.getDay())&1 && // is allowed on this day of the week
|
||||
(!a.date || a.date==time.toISOString().substr(0,10)) // is allowed on this date
|
||||
);
|
||||
if (active.length) {
|
||||
active = active.sort((a,b)=>a.t-b.t); // sort by time
|
||||
var t = active[0].t-currentTime;
|
||||
if (t<1000) t=1000; // start alarm minimum 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));
|
||||
}
|
||||
})();
|
||||
/* DEBUGGING
|
||||
===============
|
||||
|
||||
// show the current timer for the next event
|
||||
global["\xff"].timers[Bangle.SCHED]
|
||||
|
||||
// time in hours of scheduled timer event
|
||||
global["\xff"].timers[Bangle.SCHED].time / (1024*1024*60*60)
|
||||
|
||||
// set time 1 hour in the past
|
||||
setTime(getTime() - 60*60)
|
||||
|
||||
*/
|
|
@ -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 be *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.03",
|
||||
"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 {
|
||||
if (!alarm.timer) 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 @@
|
|||
0.01: Release
|
|
@ -0,0 +1,21 @@
|
|||
# Simple Timer
|
||||
|
||||
A simple app to set a timer quickly. Simply tab on top/bottom/left/right
|
||||
to select the minutes and tab in the middle of the screen to start/stop
|
||||
the timer. Note that this timer depends on qalarm.
|
||||
|
||||
# Overview
|
||||
If you open the app, you can simply control the timer
|
||||
by clicking on top, bottom, left or right of the screen.
|
||||
If you tab at the middle of the screen, the timer is
|
||||
started / stopped.
|
||||
|
||||

|
||||
|
||||
|
||||
# Creator
|
||||
[David Peer](https://github.com/peerdavid)
|
||||
|
||||
|
||||
# Thanks to...
|
||||
Time icon created by <a href="https://www.flaticon.com/free-icons/time" title="time icons">CreativeCons - Flaticon</a>
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwMAg//AAXgApcAvAZBhwCBuAFuGoUeAQM4AQM8AQl8Bwn4AQMPgEB+AFBg+AgZYBgED4AHBAoIPBCYIAC/AfCGwQrCGAQ3CAAMcIYQFCJ4QABnoREvIdE/eeAgUB+fPAoUD8/nIIUHz/zJoUPn/5LIUev/8MoU8//+OIU5XwO8AoN7AoPeAoNzAoPOAsrFKg4QBAAPgApYA=="))
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* SIMPLE TIMER
|
||||
*
|
||||
* Creator: David Peer
|
||||
* Date: 02/2022
|
||||
*/
|
||||
|
||||
Bangle.loadWidgets();
|
||||
|
||||
|
||||
const alarm = require("sched");
|
||||
|
||||
const TIMER_IDX = "smpltmr";
|
||||
const screenWidth = g.getWidth();
|
||||
const screenHeight = g.getHeight();
|
||||
const cx = parseInt(screenWidth/2);
|
||||
const cy = parseInt(screenHeight/2)-12;
|
||||
var minutes = 5;
|
||||
var interval; //used for the 1 second interval timer
|
||||
|
||||
|
||||
function isTimerEnabled(){
|
||||
var alarmObj = alarm.getAlarm(TIMER_IDX);
|
||||
if(alarmObj===undefined || !alarmObj.on){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getTimerMin(){
|
||||
var alarmObj = alarm.getAlarm(TIMER_IDX);
|
||||
return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000));
|
||||
}
|
||||
|
||||
function setTimer(minutes){
|
||||
alarm.setAlarm(TIMER_IDX, {
|
||||
// msg : "Simple Timer",
|
||||
timer : minutes*60*1000,
|
||||
});
|
||||
alarm.reload();
|
||||
}
|
||||
|
||||
function deleteTimer(){
|
||||
alarm.setAlarm(TIMER_IDX, undefined);
|
||||
alarm.reload();
|
||||
}
|
||||
|
||||
setWatch(_=>load(), BTN1);
|
||||
function draw(){
|
||||
g.clear(1);
|
||||
Bangle.drawWidgets();
|
||||
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
interval = undefined;
|
||||
|
||||
// Write time
|
||||
g.setFontAlign(0, 0, 0);
|
||||
g.setFont("Vector", 32).setFontAlign(0,-1);
|
||||
|
||||
var started = isTimerEnabled();
|
||||
var text = minutes + " min.";
|
||||
if(started){
|
||||
var min = getTimerMin();
|
||||
text = min + " min.";
|
||||
}
|
||||
|
||||
var rectWidth = parseInt(g.stringWidth(text) / 2);
|
||||
|
||||
if(started){
|
||||
interval = setInterval(draw, 1000);
|
||||
g.setColor("#ff0000");
|
||||
} else {
|
||||
g.setColor(g.theme.fg);
|
||||
}
|
||||
g.fillRect(cx-rectWidth-5, cy-5, cx+rectWidth, cy+30);
|
||||
|
||||
g.setColor(g.theme.bg);
|
||||
g.drawString(text, cx, cy);
|
||||
}
|
||||
|
||||
|
||||
Bangle.on('touch', function(btn, e){
|
||||
var left = parseInt(g.getWidth() * 0.25);
|
||||
var right = g.getWidth() - left;
|
||||
var upper = parseInt(g.getHeight() * 0.25);
|
||||
var lower = g.getHeight() - upper;
|
||||
|
||||
var isLeft = e.x < left;
|
||||
var isRight = e.x > right;
|
||||
var isUpper = e.y < upper;
|
||||
var isLower = e.y > lower;
|
||||
var isMiddle = !isLeft && !isRight && !isUpper && !isLower;
|
||||
var started = isTimerEnabled();
|
||||
|
||||
if(isRight && !started){
|
||||
minutes += 1;
|
||||
Bangle.buzz(40, 0.3);
|
||||
} else if(isLeft && !started){
|
||||
minutes -= 1;
|
||||
Bangle.buzz(40, 0.3);
|
||||
} else if(isUpper && !started){
|
||||
minutes += 5;
|
||||
Bangle.buzz(40, 0.3);
|
||||
} else if(isLower && !started){
|
||||
minutes -= 5;
|
||||
Bangle.buzz(40, 0.3);
|
||||
} else if(isMiddle) {
|
||||
if(!started){
|
||||
setTimer(minutes);
|
||||
} else {
|
||||
deleteTimer();
|
||||
}
|
||||
Bangle.buzz(80, 0.6);
|
||||
}
|
||||
minutes = Math.max(0, minutes);
|
||||
|
||||
draw();
|
||||
});
|
||||
|
||||
g.reset();
|
||||
draw();
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 7.6 KiB |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "smpltmr",
|
||||
"name": "Simple Timer",
|
||||
"shortName": "Simple Timer",
|
||||
"version": "0.01",
|
||||
"description": "A very simple app to start a timer.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool",
|
||||
"dependencies": {"scheduler":"type"},
|
||||
"supports": ["BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot.png"}, {"url": "screenshot_2.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"smpltmr.app.js","url":"app.js"},
|
||||
{"name":"smpltmr.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
|
@ -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]);
|
||||
}
|
||||
};
|
||||
}
|