Merge branch 'espruino:master' into master
|
@ -5,3 +5,4 @@
|
||||||
0.05: Battery optimisation, add the pause option, bug fixes
|
0.05: Battery optimisation, add the pause option, bug fixes
|
||||||
0.06: Add a temperature threshold to detect (and not alert) if the BJS isn't worn. Better support for the peoples using the app at night
|
0.06: Add a temperature threshold to detect (and not alert) if the BJS isn't worn. Better support for the peoples using the app at night
|
||||||
0.07: Fix bug on the cutting edge firmware
|
0.07: Fix bug on the cutting edge firmware
|
||||||
|
0.08: Use default Bangle formatter for booleans
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Activity Reminder",
|
"name": "Activity Reminder",
|
||||||
"shortName":"Activity Reminder",
|
"shortName":"Activity Reminder",
|
||||||
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
"description": "A reminder to take short walks for the ones with a sedentary lifestyle",
|
||||||
"version":"0.07",
|
"version":"0.08",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
"tags": "tool,activity",
|
"tags": "tool,activity",
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
"< Back": () => back(),
|
"< Back": () => back(),
|
||||||
'Enable': {
|
'Enable': {
|
||||||
value: settings.enabled,
|
value: settings.enabled,
|
||||||
format: v => v ? "Yes" : "No",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.enabled = v;
|
settings.enabled = v;
|
||||||
activityreminder.writeSettings(settings);
|
activityreminder.writeSettings(settings);
|
||||||
|
@ -38,9 +37,7 @@
|
||||||
settings.maxInnactivityMin = v;
|
settings.maxInnactivityMin = v;
|
||||||
activityreminder.writeSettings(settings);
|
activityreminder.writeSettings(settings);
|
||||||
},
|
},
|
||||||
format: x => {
|
format: x => x + "m"
|
||||||
return x + " min";
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'Dismiss delay': {
|
'Dismiss delay': {
|
||||||
value: settings.dismissDelayMin,
|
value: settings.dismissDelayMin,
|
||||||
|
@ -49,9 +46,7 @@
|
||||||
settings.dismissDelayMin = v;
|
settings.dismissDelayMin = v;
|
||||||
activityreminder.writeSettings(settings);
|
activityreminder.writeSettings(settings);
|
||||||
},
|
},
|
||||||
format: x => {
|
format: x => x + "m"
|
||||||
return x + " min";
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'Pause delay': {
|
'Pause delay': {
|
||||||
value: settings.pauseDelayMin,
|
value: settings.pauseDelayMin,
|
||||||
|
@ -61,7 +56,7 @@
|
||||||
activityreminder.writeSettings(settings);
|
activityreminder.writeSettings(settings);
|
||||||
},
|
},
|
||||||
format: x => {
|
format: x => {
|
||||||
return x + " min";
|
return x + "m";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Min steps': {
|
'Min steps': {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: AdvCasio first version
|
|
@ -0,0 +1,62 @@
|
||||||
|
# Adv Casio Clock
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/2981891/175355586-1dfc0d66-6555-4385-b124-1605fdb71a11.jpg" width="250" />
|
||||||
|
|
||||||
|
An over-engineered clock inspired by Casio watches.<br/>
|
||||||
|
It has a dedicated timer, a scratchpad and can display the weather condition 4 days ahead.<br/>
|
||||||
|
It uses a <a target="_blank" href="https://dotgreg.github.io/advCasioBangleClock/">custom web app</a> to update its content.<br/>
|
||||||
|
Forked from the awesome Cassio Watch.<br/>
|
||||||
|
|
||||||
|
## Todo
|
||||||
|
|
||||||
|
- Improving quality of the background images, right now it is quite blurry.
|
||||||
|
- Improving screenshots quality.
|
||||||
|
- Improving web app look.
|
||||||
|
- Improving bangle app performances (using functions for images and specialized array).
|
||||||
|
|
||||||
|
## Functionalities
|
||||||
|
|
||||||
|
- Current time
|
||||||
|
- Current day and month
|
||||||
|
- Footsteps
|
||||||
|
- Battery
|
||||||
|
- Simple Timer embedded
|
||||||
|
- Weather forecast (7 days)
|
||||||
|
- Scratchpad
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
Clock:<br/>
|
||||||
|
<img src="https://user-images.githubusercontent.com/2981891/175519126-049caf93-73d0-4472-9650-33b28f80843c.jpg" width="250" />
|
||||||
|
<img src="https://user-images.githubusercontent.com/2981891/175519128-96926e32-2165-4c61-9364-843440304bb9.jpg" width="250" />
|
||||||
|
<img src="https://user-images.githubusercontent.com/2981891/175519130-4921073c-48fc-4c29-932d-d42acc3b395c.jpg" width="250" />
|
||||||
|
|
||||||
|
Web interface to update weather & scratchpad <br/>
|
||||||
|
<a target="_blank" ref="https://dotgreg.github.io/advCasioBangleClock/">https://dotgreg.github.io/advCasioBangleClock</a>
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/2981891/175519121-851bb209-7192-40db-a014-490c344f7597.jpg" width="250" />
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
### How to update the tasks list / weather
|
||||||
|
- you will need a <a target="_blank" href="https://openweathermap.org/price#weather">free openweathermap.org api key</a>.
|
||||||
|
- go to https://dotgreg.github.io/advCasioBangleClock/
|
||||||
|
- Alternatively you can install it on your own server/heroku/service/github pages, the web-app code is <a target="_blank" href="https://github.com/dotgreg/advCasioBangleClock/tree/master/web-app">here</a>
|
||||||
|
- fill the location and the api key (it will be saved on your browser, no need to do it each time)
|
||||||
|
- edit the scratchpad with what you want
|
||||||
|
- click on sync
|
||||||
|
- reload your clock!
|
||||||
|
|
||||||
|
### How to start/stop the timer
|
||||||
|
- swipe up : add time (+5min)
|
||||||
|
- swipe down : remove time (-5min)
|
||||||
|
- swipe right : start timer
|
||||||
|
- swipe left : stop timer
|
||||||
|
|
||||||
|
## Links
|
||||||
|
### Issues, suggestions and bugtracker
|
||||||
|
<a target="_blank" href="https://github.com/dotgreg/advCasioBangleClock/issues">https://github.com/dotgreg/advCasioBangleClock/issues</a>
|
||||||
|
|
||||||
|
### Code repository (bangle app and web app)
|
||||||
|
<a target="_blank" href="https://github.com/dotgreg/advCasioBangleClock">https://github.com/dotgreg/advCasioBangleClock</a>
|
||||||
|
|
||||||
|
### Creator
|
||||||
|
<a target="_blank" href="https://github.com/dotgreg">https://github.com/dotgreg</a>
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwghC/AH4A/AGsCmUQC6kf/8wC6k///wgEv//zD4PxAQIJBABP//4QBC4IcBh/yEQIXKgP/l4rBl/yGAMP/4iBKJUC/5gBIAQVBBAMR/8gC5IQBAAMQC4IVBFoMjAYIXNmAXBgYXCPgQAJl/xHwPwj/yn5kC/55BUxSlC+JiBVgQ5BUxiDBUIIXBIQQXBcCoA/AH4ADXAUgUAUQBAkPeoTDFgIHBAALQEA4XwC4IOEAAQRBbAQBBCAIgBEYMQC4TnEC4XyeQgBDAAMwC4pIDC4kDAgJLD//xC5QIBNQISCFYIZCC4aEBAQRCDAAPyl4hBOIh3Cn53GNgMRiKxGBAR5BAoYA/AH4A/AH4A5A"))
|
|
@ -0,0 +1,306 @@
|
||||||
|
const storage = require('Storage');
|
||||||
|
|
||||||
|
require("Font6x12").add(Graphics);
|
||||||
|
require("Font6x8").add(Graphics);
|
||||||
|
require("Font8x12").add(Graphics);
|
||||||
|
require("Font7x11Numeric7Seg").add(Graphics);
|
||||||
|
|
||||||
|
function bigThenSmall(big, small, x, y) {
|
||||||
|
g.setFont("7x11Numeric7Seg", 2);
|
||||||
|
g.drawString(big, x, y);
|
||||||
|
x += g.stringWidth(big);
|
||||||
|
g.setFont("8x12");
|
||||||
|
g.drawString(small, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClockBg() {
|
||||||
|
return require("heatshrink").decompress(atob("icVgf/ABv8v4DBx4CB+PH8F+nAGB48fwEHBwXjxwqBuPH//+nAGBBwIjCAwI2D/wGBgIyDI4QGDwAGBHYX/4AGBn4UFEYQpCEYYpCAAMfMhP4FIgABwJ8OEBIA=="));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// sun, cloud, rain, thunder
|
||||||
|
var iconsWeather = [
|
||||||
|
require("heatshrink").decompress(atob("i8Ugf/ACcfA434BA/AAwsAv0/8F/BAcDwEHHIpECFI3wn4GC/gOC+PAGoXggEH/+ODQgXBGQv/wAbBBAnguEACIn4gfxI4JXFwJmG/kPBA3jSynw")), require("heatshrink").decompress(atob("i0Ugf/AEXggIGE/0A/kPBAmBCIN/A4Y8CgAICwEHBYoUE/ACCj4sDn4CBC4YyDwBrDCgYA3A")), require("heatshrink").decompress(atob("h8Rgf/AAuBAgf8h4FDCwM/AgPA/gFC/0HgEBBQPwnEfDoWAg4jC/gOCAoQmBAQXjFIV//8f//4IQP4j/+gAIB4EcHII4CAoI+DLQJXF/AA==")), require("heatshrink").decompress(atob("h0Pgf/AA8fAYX+g4EC8EBAgXADAeAgAECgAOC/wrCDQIOBBYfwgAaC/kAn4EB/EAv4aDHAeBIg38"))
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
function getBackgroundImage() {
|
||||||
|
return require("heatshrink").decompress(atob("2GwghC/AH4A/AH4AMl////wAwURiQECgUzmcxBQQCBiYUBBARW+LAcCAgcPBYgFBkAIFG7kQiAKIiIKBgISOAAJBD//zKQfxK4vyAoMQCgn/ERBhBBYR5BAwR1DB4Y2DgYPCGIQRCCQcP+EfGJI0FEgRSCGAQCCX4JXCkAhDn4lI+HyK4YWBFIPzJYJXHAIMSK4cwJ4I3CAYMzA4cfcRMBdwytBK4i6FK4IUCMgYAEGIITBK4cCaAPwgJXB+fzK4sAgYtCK5EfA4pXR+AmBaIZYCK6KcCAwSjDEYXx/8vK5QRCK4kPK6cDkJREBIMBfgIrDK5svUAIQBAwIaCK4w+DK4YGBK7IaBboIuCK4gFCJwYBBiBCCCgQhHHYgGDgArBK5IGDAYMgJ4Xwn53BGgLVDmBXKAAinDLpJXCAAYhHR4YODn/wJIPyTYZXDE4RXD+ECNILIDAIPwj4xIAAYNCR4fyVIYLFA4KEBBAglKAGUCmcykEAiMQBIURBYM/BgIUEgcz+bTKAH4A/AH4A/AHP/AGY1d+BWCh5X/LCpW1K74fgG/5X/AH5X/K9Bg/K63wK/5XWgBX/K6pWBK/5XU+BWBh5J/K6auCK/5XTVwRfFAH5XOKwRX/K6auDh5I/K6SuDWP5XSVwYADWX6vXK/5XQWQpW/K6auDJP5XWV35XT+Cu/K7Ku/K65H/K6hW/K7EPI35XWIv5XWAH5X/K/4A/K/5X/K/4A/K9cAAH4A/AFzz/AHRX/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/5X/AH5X/K/4A/K/5X/K/4A/K/5X/K/4A/K/5X/AH5X/K/5X/AH5X/K/40VAH4A/AFzLb+EPDm4AdK/5X/K+PwgEAHy5X9HgMAK/5XXH6xX/H65X/K/5X/K98AK7sAgBX3DjBWFO644DSTHwGzJXED4RXaDoLqcK7weWDIQcXK8I6YK77KXK4o8DPbY6ZK7qvDDy6vdR7JXDh60EDyw5BAIRXYSwjMbAgIhUDwJZCHwJX0GwjRWNwIAEHSwBCDSpXFH4pXzDS5XIEARXVSYbQEDaYzCK+6vcKaxXNDypX9HwQkbHS40COSpXKK2A6CHgRXcPIhX0SwpXYVuQ6EgBX/K644YODBXkSDJX/K/5X/DtRX6gA3YOkRWbLDZX4KwYA/AG8F5vdABncKH4AGhpRJAYXNAgPAKP4AF5vMJwoDBAQIKE6BR/AAvc5vO9wAB7oCB9veAoPcAoPcK+kwh8AgcA98An//gH/+sD//wCISgBJ4IABAYpaC9vdK4UP/9AAQNQr/zgHwEYNQFYQAh+EP+FegH+A4QBCMQIKBAAPNK4yxBA4RXCV4YZBE4IjChwCDmApCK8VdmHggHgFYf0SQJXE5nMK4anCAoYHC5pXCaQJXBop+BqAGEK7f/AAQeEKwQrBqCtDAILjBCQfNK4JTCAYZXF7qvD//gV4S2DgEFFIYAECgIACMC8PKoIBB8n1K4ivF5vc5xOCWYZbBAYavHU4RXCr4pEAEMDfoNQGoMEgEwYQPwAoIBBAAPM5ipC7oDCVIIAE7hXCD4SdBiEP+gGBgihCFYIAz5pXBAAnN7oIB7nc5gOBK4QA/K4pNCWgSpCBInNK/4AGhncKIStC7gCBA4QAC4BR/AAysCABZW/AHwA="));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// schedule a draw for the next minute
|
||||||
|
let rocketInterval;
|
||||||
|
var drawTimeout;
|
||||||
|
function queueDraw() {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(function() {
|
||||||
|
drawTimeout = undefined;
|
||||||
|
draw();
|
||||||
|
}, 60000 - (Date.now() % 60000));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clearIntervals() {
|
||||||
|
if (rocketInterval) clearInterval(rocketInterval);
|
||||||
|
rocketInterval = undefined;
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// TIMER FUNC
|
||||||
|
//
|
||||||
|
var timer_time = 0;
|
||||||
|
var alreadyListenTouch = false;
|
||||||
|
function initTouchTimer () {
|
||||||
|
if (alreadyListenTouch) return;
|
||||||
|
alreadyListenTouch = true;
|
||||||
|
|
||||||
|
Bangle.on('swipe', function(dirX,dirY) {
|
||||||
|
if (canTouch === false) return;
|
||||||
|
var njson = getDataJson();
|
||||||
|
if (!njson) return;
|
||||||
|
|
||||||
|
if (dirX === -1) {
|
||||||
|
timer_time = 0;
|
||||||
|
delete njson.timer;
|
||||||
|
setDataJson(njson);
|
||||||
|
}
|
||||||
|
else if (dirX === 1) {
|
||||||
|
var now = new Date().getTime();
|
||||||
|
njson.timer = now + (timer_time * 1000 * 60);
|
||||||
|
Bangle.setLocked(true);
|
||||||
|
setDataJson(njson);
|
||||||
|
Bangle.buzz(200, 0);
|
||||||
|
timer_time = 0;
|
||||||
|
}
|
||||||
|
else if (dirY === -1) {
|
||||||
|
if (canTouch === false || njson.timer) return;
|
||||||
|
timer_time = timer_time + 5;
|
||||||
|
}
|
||||||
|
else if (dirY === 1) {
|
||||||
|
if (canTouch === false || njson.timer) return;
|
||||||
|
timer_time = timer_time - 5;
|
||||||
|
}
|
||||||
|
draw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
initTouchTimer ();
|
||||||
|
});
|
||||||
|
|
||||||
|
function getTimerTime() {
|
||||||
|
// if timer_time !== -1, take it
|
||||||
|
if (timer_time !== 0) {
|
||||||
|
return timer_time + "m";
|
||||||
|
} else {
|
||||||
|
// else, show diff between njsontime and now
|
||||||
|
var njson = getDataJson();
|
||||||
|
if (!njson) return false;
|
||||||
|
var now = new Date().getTime();
|
||||||
|
var diff = Math.round((njson.timer - now) / (1000 * 60));
|
||||||
|
//console.log(123, njson, diff, now, njson.timer - now);
|
||||||
|
if (diff > 0) return diff + "m";
|
||||||
|
else if (njson.timer) {
|
||||||
|
Bangle.buzz(1000, 1);
|
||||||
|
console.log("END OF TIMER");
|
||||||
|
delete njson.timer;
|
||||||
|
setDataJson(njson);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// if diff is <0, delete timer from json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function drawTimer() {
|
||||||
|
//g.drawString(getTimerTime(), 100, 100);
|
||||||
|
g.setFont("8x12", 2);
|
||||||
|
var t = 97;
|
||||||
|
var l = 105;
|
||||||
|
var time = getTimerTime();
|
||||||
|
if (time || timer_time !== 0) g.drawString(time, l+5, t+0);
|
||||||
|
if (time && timer_time === 0) g.drawImage(getClockBg(), l-20, t+2, { scale: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// DATA READING
|
||||||
|
//
|
||||||
|
function getDataJson(){
|
||||||
|
var res = {"tasks":"", "weather":[]};
|
||||||
|
try {
|
||||||
|
res = storage.readJSON('advcasio.data.json');
|
||||||
|
} catch(ex) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
function setDataJson(resJson){
|
||||||
|
try {
|
||||||
|
res = storage.writeJSON('advcasio.data.json', resJson);
|
||||||
|
} catch(ex) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
var dataJson = getDataJson();
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// WEATHER!
|
||||||
|
//
|
||||||
|
function drawWeather(arr) {
|
||||||
|
g.setFont("6x8", 1);
|
||||||
|
var p = {l: 8, tText: 40, tIcon:20, decal:25};
|
||||||
|
var today = new Date().getTime();
|
||||||
|
var yesterday = today - (1000 * 60 * 60 * 24);
|
||||||
|
var testday = today + (1000 * 60 * 60 * 24 * 2);
|
||||||
|
//12h auj > 12h hier qui est sup a 0h auj
|
||||||
|
//23h59 hier est sup a 0h auj
|
||||||
|
var j = 0;
|
||||||
|
for(var i = 0; i<arr.length;i++) {
|
||||||
|
if (arr[i][2] > yesterday && j < 4) {
|
||||||
|
g.drawString(arr[i][0], p.l + p.decal*j + 4, p.tText);
|
||||||
|
g.drawImage(iconsWeather[arr[i][1]], p.l + p.decal*j, p.tIcon, { scale: 1 });
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// DRAWING FUNCS
|
||||||
|
//
|
||||||
|
function drawTasks(str) {
|
||||||
|
g.setFont("6x8", 1);
|
||||||
|
var t = 57;
|
||||||
|
var l = 0;
|
||||||
|
g.drawString(str, l+5, t+0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSteps() {
|
||||||
|
g.setFont("8x12", 2);
|
||||||
|
var t = 132;
|
||||||
|
var l = 150;
|
||||||
|
g.drawString(getSteps(), l+5, t+0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drawClock() {
|
||||||
|
g.setFont("7x11Numeric7Seg", 3);
|
||||||
|
g.clearRect(80, 57, 170, 96);
|
||||||
|
g.setColor(255, 255, 255);
|
||||||
|
var l = 77;
|
||||||
|
var t = 57;
|
||||||
|
var w = 170;
|
||||||
|
var h = 116;
|
||||||
|
g.drawRect(l, t, w, h);
|
||||||
|
g.fillRect(l, t, w, h);
|
||||||
|
g.setColor(0, 0, 0);
|
||||||
|
g.drawString(require("locale").time(new Date(), 1), 76, 60);
|
||||||
|
|
||||||
|
// day
|
||||||
|
//g.setFont("8x12", 1);
|
||||||
|
//g.setFont("9x18", 1);
|
||||||
|
//g.drawString(require("locale").dow(new Date(), 2).toUpperCase(), 25, 136);
|
||||||
|
g.setFont("8x12", 2);
|
||||||
|
g.drawString(require("locale").dow(new Date(), 2), 18, 130);
|
||||||
|
|
||||||
|
// month
|
||||||
|
g.setFont("8x12");
|
||||||
|
g.drawString(require("locale").month(new Date(), 2).toUpperCase(), 80, 127);
|
||||||
|
|
||||||
|
// day nb
|
||||||
|
g.setFont("8x12", 2);
|
||||||
|
const time = new Date().getDate();
|
||||||
|
g.drawString(time < 10 ? "0" + time : time, 78, 137);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawBattery() {
|
||||||
|
bigThenSmall(E.getBattery(), "%", 140, 23);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getSteps() {
|
||||||
|
var steps = 0;
|
||||||
|
try{
|
||||||
|
if (WIDGETS.wpedom !== undefined) {
|
||||||
|
steps = WIDGETS.wpedom.getSteps();
|
||||||
|
} else if (WIDGETS.activepedom !== undefined) {
|
||||||
|
steps = WIDGETS.activepedom.getSteps();
|
||||||
|
} else {
|
||||||
|
steps = Bangle.getHealthStatus("day").steps;
|
||||||
|
}
|
||||||
|
} catch(ex) {
|
||||||
|
// In case we failed, we can only show 0 steps.
|
||||||
|
return "? k";
|
||||||
|
}
|
||||||
|
|
||||||
|
steps = Math.round(steps/1000);
|
||||||
|
return steps + "k";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
|
||||||
|
queueDraw();
|
||||||
|
|
||||||
|
g.reset();
|
||||||
|
g.clear();
|
||||||
|
g.setColor(255, 255, 255);
|
||||||
|
g.fillRect(0, 0, g.getWidth(), g.getHeight());
|
||||||
|
let background = getBackgroundImage();
|
||||||
|
g.drawImage(background, 0, 0, { scale: 1 });
|
||||||
|
|
||||||
|
|
||||||
|
g.setColor(0, 0, 0);
|
||||||
|
g.setFont("6x12");
|
||||||
|
if(dataJson && dataJson.weather) drawWeather(dataJson.weather);
|
||||||
|
if(dataJson && dataJson.tasks) drawTasks(dataJson.tasks);
|
||||||
|
|
||||||
|
|
||||||
|
g.setFontAlign(0,-1);
|
||||||
|
g.setFont("8x12", 2);
|
||||||
|
|
||||||
|
drawSteps();
|
||||||
|
g.setFontAlign(-1,-1);
|
||||||
|
drawClock();
|
||||||
|
drawBattery();
|
||||||
|
drawTimer();
|
||||||
|
// Hide widgets
|
||||||
|
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save batt power, does not seem to work although...
|
||||||
|
var canTouch = true;
|
||||||
|
Bangle.on("lcdPower", (on) => {
|
||||||
|
if (on) {
|
||||||
|
draw();
|
||||||
|
} else {
|
||||||
|
canTouch = false;
|
||||||
|
clearIntervals();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Bangle.on("lock", (locked) => {
|
||||||
|
clearIntervals();
|
||||||
|
draw();
|
||||||
|
if (!locked) {
|
||||||
|
canTouch = true;
|
||||||
|
} else {
|
||||||
|
canTouch = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Load widgets, but don't show them
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.setUI("clock");
|
||||||
|
|
||||||
|
g.reset();
|
||||||
|
g.clear();
|
||||||
|
draw();
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1 @@
|
||||||
|
{"tasks":"", "weather":[]};
|
|
@ -0,0 +1,25 @@
|
||||||
|
{ "id": "advcasio",
|
||||||
|
"name": "Advanced Casio Clock",
|
||||||
|
"shortName":"advcasio",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "An over-engineered clock inspired by Casio watches. It has a 4 days weather, a timer using swipe and a scratchpad. Can be updated using a dedicated webapp.",
|
||||||
|
"icon": "app.png",
|
||||||
|
"tags": "clock",
|
||||||
|
"type": "clock",
|
||||||
|
"screenshots": [
|
||||||
|
{ "url": "screenshot-clock-1.jpg" },
|
||||||
|
{ "url": "screenshot-clock-2.jpg" },
|
||||||
|
{ "url": "screenshot-clock-3.jpg" },
|
||||||
|
{ "url": "screenshot-webapp.jpg" }
|
||||||
|
],
|
||||||
|
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"allow_emulator":true,
|
||||||
|
"storage": [
|
||||||
|
{"name":"advcasio.app.js","url":"app.js"},
|
||||||
|
{"name":"advcasio.img","url":"app-icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{ "name": "advcasio.data.json", "url": "data.json", "storageFile": true }
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 230 KiB |
|
@ -1 +1,2 @@
|
||||||
0.01: Basic agenda with events from GB
|
0.01: Basic agenda with events from GB
|
||||||
|
0.02: Added settings page to force calendar sync
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
0.01: First, proof of concept
|
||||||
|
0.02: Load AGPS data on app start and automatically in background
|
|
@ -0,0 +1,19 @@
|
||||||
|
# A-GPS Data
|
||||||
|
|
||||||
|
Load assisted GPS (A-GPS) data directly to your Bangle.js using the new http requests on Android GadgetBridge.
|
||||||
|
|
||||||
|
Will download A-GPS data in background (if enabled in settings).
|
||||||
|
|
||||||
|
The GNSS type can be configured in the settings.
|
||||||
|
|
||||||
|
Make sure:
|
||||||
|
* your GadgetBridge version supports http requests
|
||||||
|
* turn on internet access in GadgetBridge settings
|
||||||
|
|
||||||
|
Currently proof of concept on Bangle.js 2 only.
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
[@pidajo](https://github.com/pidajo)
|
||||||
|
|
||||||
|
## Contributor
|
||||||
|
[@myxor](https://github.com/myxor)
|
|
@ -0,0 +1 @@
|
||||||
|
atob("MDCEAAAAAAAAAAAAAAAAiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAIiIOIiIiAAAAAAAAAAAAAAAAAAAAAAAAIiDOIiIiAAAAAAAAAAAAAAAAAAAAAAAAIiDOIiIiIAAAAAAAAAAAAAAAAAAAAAACIiPOIiIiIAAAAAAAAAAAAAAAAAAAAAAiIj/OIiIiIgAAAAAAAAAAAAAAAAAAAAAiI//OIiIiIgAAAAAAAAAAAAAAAAAAAAAiI//OIiIiIiAAAAAAAAAAAAAAAAAAAAAiD//OIiIiIiAAAAAAAAAAAAAAAAAAAAIiP//OIiIiIiAAAAAAAAAAAAAAAAAAAAIg///OIiIiIiIAAAAAAAAAAAAAAAAAACIj///OIiIiIiIAAAAAAAAAAAAAAAAAACIP///OIiIiIiIgAAAAAAAAAAAAAAAAACI////OIiIiIiIgAAAAAAAAAAAAAAAAAiD////OIiIiIiIiAAAAAAAAAAAAAAAAAiP////OIiIiIiIiAAAAAAAAAAAAAAAAIiP////OIiIiIiIiIAAAAAAAAAAAAAAAIj/////OIiIiIiIiIAAAAAAAAAAAAAACIj/////OIiIiIiIiIgAAAAAAAAAAAAACI//////OIiIiIiIiIgAAAAAAAAAAAAAiI//////OIiIiIiIiIgAAAAAAAAAAAAAiIiIiIiIgzMzMzMziIiAAAAAAAAAAAAAiIiIiIiIj///////+IiAAAAAAAAAAAAIiIiIiIiIj////////4iIAAAAAAAAAAAIiIiIiIiIgzMzMzMzM4iIAAAAAAAAAACIP///////OIiIiIiIiIiIgAAAAAAAAACI////////OIiIiIiIiIiIgAAAAAAAAAiI////////OIiIiIiIiIiIiAAAAAAAAAiP////////OIiIiIiIiIiIiAAAAAAAAIiP////////OIiIiIiIiIiIiIAAAAAAAIj/////////OIiIiIiIiIiIiIAAAAAACIj////////ziIiIiIiIiIiIiIgAAAAACIP///////+IiIiIiIiIiIiIiIgAAAAACI//////84gYiIiIiIiIiIiIiIgAAAAAiD////84iIiAAAiIiIiIiIiIiIiAAAAAiP///ziIiIAAAAAIiIiIiIiIiIiAAAAIg/8ziIiIAAAAAAAAAIiIiIiIiIiIAAAIj/iIiIAAAAAAAAAAAAAIiIiIiIiIAACIiIiBgAAAAAAAAAAAAAAACIiIiIiIgACIiIgAAAAAAAAAAAAAAAAAAACIiIiIgACIgAAAAAAAAAAAAAAAAAAAAAAAiIiIgA==")
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,54 @@
|
||||||
|
function display(text1, text2) {
|
||||||
|
g.reset();
|
||||||
|
g.clear();
|
||||||
|
var img = require("Storage").read("agpsdata.img");
|
||||||
|
if (img) {
|
||||||
|
g.drawImage(img, g.getWidth() - 48, g.getHeight() - 48 - 24);
|
||||||
|
}
|
||||||
|
g.setFont("Vector", 18);
|
||||||
|
g.setFontAlign(0, 1);
|
||||||
|
g.drawString(text1, g.getWidth() / 2, g.getHeight() / 3 + 24);
|
||||||
|
if (text2 != undefined) {
|
||||||
|
g.setFont("Vector", 12);
|
||||||
|
g.setFontAlign(-1, -1);
|
||||||
|
g.drawString(text2, 5, g.getHeight() / 3 + 29);
|
||||||
|
}
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show launcher when middle button pressed
|
||||||
|
// Load widgets
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
let waiting = false;
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
g.reset();
|
||||||
|
g.clear();
|
||||||
|
waiting = false;
|
||||||
|
display("Retry?", "touch to retry");
|
||||||
|
Bangle.on("touch", () => { updateAgps(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAgps() {
|
||||||
|
g.reset();
|
||||||
|
g.clear();
|
||||||
|
if (!waiting) {
|
||||||
|
waiting = true;
|
||||||
|
display("Updating A-GPS...");
|
||||||
|
require("agpsdata").pull(function() {
|
||||||
|
waiting = false;
|
||||||
|
display("A-GPS updated.", "touch to close");
|
||||||
|
Bangle.on("touch", () => { load(); });
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
waiting = false;
|
||||||
|
E.showAlert(error, "Error")
|
||||||
|
.then(() => { start(); });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
display("Waiting...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateAgps();
|
|
@ -0,0 +1,33 @@
|
||||||
|
(function() {
|
||||||
|
let waiting = false;
|
||||||
|
let settings = require("Storage").readJSON("agpsdata.settings.json", 1) || {
|
||||||
|
enabled: true,
|
||||||
|
refresh: 1440
|
||||||
|
};
|
||||||
|
|
||||||
|
if (settings.refresh == undefined) settings.refresh = 1440;
|
||||||
|
|
||||||
|
function successCallback(){
|
||||||
|
waiting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorCallback(){
|
||||||
|
waiting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.enabled) {
|
||||||
|
let lastUpdate = settings.lastUpdate;
|
||||||
|
if (!lastUpdate || lastUpdate + settings.refresh * 1000 * 60 < Date.now()){
|
||||||
|
if (!waiting){
|
||||||
|
waiting = true;
|
||||||
|
require("agpsdata").pull(successCallback, errorCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInterval(() => {
|
||||||
|
if (!waiting && NRF.getSecurityStatus().connected){
|
||||||
|
waiting = true;
|
||||||
|
require("agpsdata").pull(successCallback, errorCallback);
|
||||||
|
}
|
||||||
|
}, settings.refresh * 1000 * 60);
|
||||||
|
}
|
||||||
|
})();
|
|
@ -0,0 +1 @@
|
||||||
|
{"enabled":true,"refresh":1440,"gnsstype":1}
|
|
@ -0,0 +1,75 @@
|
||||||
|
function readSettings() {
|
||||||
|
settings = Object.assign(
|
||||||
|
require('Storage').readJSON("agpsdata.default.json", true) || {},
|
||||||
|
require('Storage').readJSON(FILE, true) || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
var FILE = "agpsdata.settings.json";
|
||||||
|
var settings;
|
||||||
|
readSettings();
|
||||||
|
|
||||||
|
function setAGPS(data) {
|
||||||
|
var js = jsFromBase64(data);
|
||||||
|
try {
|
||||||
|
eval(js);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
console.log("error:", e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function jsFromBase64(b64) {
|
||||||
|
var bin = atob(b64);
|
||||||
|
var chunkSize = 128;
|
||||||
|
var js = "Bangle.setGPSPower(1);\n"; // turn GPS on
|
||||||
|
var gnsstype = settings.gnsstype || 1; // default GPS
|
||||||
|
js += `Serial1.println("${CASIC_CHECKSUM("$PCAS04,"+gnsstype)}")\n`; // set GNSS mode
|
||||||
|
// What about:
|
||||||
|
// NAV-TIMEUTC (0x01 0x10)
|
||||||
|
// NAV-PV (0x01 0x03)
|
||||||
|
// or AGPS.zip uses AID-INI (0x0B 0x01)
|
||||||
|
|
||||||
|
for (var i=0;i<bin.length;i+=chunkSize) {
|
||||||
|
var chunk = bin.substr(i,chunkSize);
|
||||||
|
js += `Serial1.write(atob("${btoa(chunk)}"))\n`;
|
||||||
|
}
|
||||||
|
return js;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CASIC_CHECKSUM(cmd) {
|
||||||
|
var cs = 0;
|
||||||
|
for (var i=1;i<cmd.length;i++)
|
||||||
|
cs = cs ^ cmd.charCodeAt(i);
|
||||||
|
return cmd+"*"+cs.toString(16).toUpperCase().padStart(2, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLastUpdate() {
|
||||||
|
const file = "agpsdata.json";
|
||||||
|
let data = require("Storage").readJSON(file, 1) || {};
|
||||||
|
data.lastUpdate = Math.round(Date.now());
|
||||||
|
require("Storage").writeJSON(file, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.pull = function(successCallback, failureCallback) {
|
||||||
|
let uri = "https://www.espruino.com/agps/casic.base64";
|
||||||
|
if (Bangle.http){
|
||||||
|
Bangle.http(uri, {timeout:10000}).then(event => {
|
||||||
|
let result = setAGPS(event.resp);
|
||||||
|
if (result) {
|
||||||
|
updateLastUpdate();
|
||||||
|
if (successCallback) successCallback();
|
||||||
|
} else {
|
||||||
|
console.log("error applying AGPS data");
|
||||||
|
if (failureCallback) failureCallback("Error applying AGPS data");
|
||||||
|
}
|
||||||
|
}).catch((e)=>{
|
||||||
|
console.log("error", e);
|
||||||
|
if (failureCallback) failureCallback(e);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("error: No http method found");
|
||||||
|
if (failureCallback) failureCallback(/*LANG*/"No http method");
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
{ "id": "agpsdata",
|
||||||
|
"name": "A-GPS Data",
|
||||||
|
"shortName":"A-GPS Data",
|
||||||
|
"icon": "agpsdata.png",
|
||||||
|
"version":"0.02",
|
||||||
|
"description": "Download assisted GPS (A-GPS) data directly to your Bangle.js",
|
||||||
|
"tags": "boot,tool,assisted,gps,agps,http",
|
||||||
|
"allow_emulator":true,
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"readme":"README.md",
|
||||||
|
"screenshots" : [ { "url":"screenshot.png" }, { "url":"screenshot2.png" } ],
|
||||||
|
"storage": [
|
||||||
|
{"name":"agpsdata.app.js","url":"app.js"},
|
||||||
|
{"name":"agpsdata.img","url":"agpsdata-icon.js","evaluate":true},
|
||||||
|
{"name":"agpsdata.default.json","url":"default.json"},
|
||||||
|
{"name":"agpsdata.boot.js","url":"boot.js"},
|
||||||
|
{"name":"agpsdata","url":"lib.js"},
|
||||||
|
{"name":"agpsdata.settings.js","url":"settings.js"}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{"name": "agpsdata.json"},
|
||||||
|
{"name": "agpsdata.settings.json"}
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,71 @@
|
||||||
|
(function(back) {
|
||||||
|
function writeSettings(key, value) {
|
||||||
|
var s = Object.assign(
|
||||||
|
require('Storage').readJSON(settingsDefaultFile, true) || {},
|
||||||
|
require('Storage').readJSON(settingsFile, true) || {});
|
||||||
|
s[key] = value;
|
||||||
|
require('Storage').writeJSON(settingsFile, s);
|
||||||
|
readSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function readSettings() {
|
||||||
|
settings = Object.assign(
|
||||||
|
require('Storage').readJSON(settingsDefaultFile, true) || {},
|
||||||
|
require('Storage').readJSON(settingsFile, true) || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
var settingsFile = "agpsdata.settings.json";
|
||||||
|
var settingsDefaultFile = "agpsdata.default.json";
|
||||||
|
|
||||||
|
var settings;
|
||||||
|
readSettings();
|
||||||
|
|
||||||
|
const gnsstypes = [
|
||||||
|
"", "GPS", "BDS", "GPS+BDS", "GLONASS", "GPS+GLONASS", "BDS+GLONASS",
|
||||||
|
"GPS+BDS+GLON."
|
||||||
|
];
|
||||||
|
|
||||||
|
function buildMainMenu() {
|
||||||
|
var mainmenu = {
|
||||||
|
'' : {'title' : 'AGPS download'},
|
||||||
|
'< Back' : back,
|
||||||
|
"Enabled" : {
|
||||||
|
value : !!settings.enabled,
|
||||||
|
onchange : v => { writeSettings("enabled", v); }
|
||||||
|
},
|
||||||
|
"Refresh every" : {
|
||||||
|
value : settings.refresh / 60,
|
||||||
|
min : 1,
|
||||||
|
max : 168,
|
||||||
|
step : 1,
|
||||||
|
format : v => v + "h",
|
||||||
|
onchange : v => { writeSettings("refresh", Math.round(v * 60)); }
|
||||||
|
},
|
||||||
|
"GNSS type" : {
|
||||||
|
value : settings.gnsstype,
|
||||||
|
min : 1,
|
||||||
|
max : 7,
|
||||||
|
step : 1,
|
||||||
|
format : v => gnsstypes[v],
|
||||||
|
onchange : x => writeSettings('gnsstype', x)
|
||||||
|
},
|
||||||
|
"Force refresh" : () => {
|
||||||
|
E.showMessage("Loading A-GPS data");
|
||||||
|
require("agpsdata")
|
||||||
|
.pull(
|
||||||
|
function() {
|
||||||
|
E.showAlert("Success").then(
|
||||||
|
() => { E.showMenu(buildMainMenu()); });
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
E.showAlert(error, "Error")
|
||||||
|
.then(() => { E.showMenu(buildMainMenu()); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return mainmenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
E.showMenu(buildMainMenu());
|
||||||
|
});
|
|
@ -31,3 +31,7 @@
|
||||||
0.29: Fix wrong 'dow' handling in new timer if first day of week is Monday
|
0.29: Fix wrong 'dow' handling in new timer if first day of week is Monday
|
||||||
0.30: Fix "Enable All"
|
0.30: Fix "Enable All"
|
||||||
0.31: Add seconds to timers
|
0.31: Add seconds to timers
|
||||||
|
0.32: Fix wrong hidden filter
|
||||||
|
Add option for auto-delete a timer after it expires
|
||||||
|
0.33: Allow hiding timers&alarms
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,10 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||||
value: alarm.as,
|
value: alarm.as,
|
||||||
onchange: v => alarm.as = v
|
onchange: v => alarm.as = v
|
||||||
},
|
},
|
||||||
|
/*LANG*/"Hidden": {
|
||||||
|
value: alarm.hidden || false,
|
||||||
|
onchange: v => alarm.hidden = v
|
||||||
|
},
|
||||||
/*LANG*/"Cancel": () => showMainMenu()
|
/*LANG*/"Cancel": () => showMainMenu()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -280,6 +284,14 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
||||||
value: timer.on,
|
value: timer.on,
|
||||||
onchange: v => timer.on = v
|
onchange: v => timer.on = v
|
||||||
},
|
},
|
||||||
|
/*LANG*/"Delete After Expiration": {
|
||||||
|
value: timer.del,
|
||||||
|
onchange: v => timer.del = v
|
||||||
|
},
|
||||||
|
/*LANG*/"Hidden": {
|
||||||
|
value: timer.hidden || false,
|
||||||
|
onchange: v => timer.hidden = v
|
||||||
|
},
|
||||||
/*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v),
|
/*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v),
|
||||||
/*LANG*/"Cancel": () => showMainMenu()
|
/*LANG*/"Cancel": () => showMainMenu()
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Alarms & Timers",
|
"name": "Alarms & Timers",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.31",
|
"version": "0.33",
|
||||||
"description": "Set alarms and timers on your Bangle",
|
"description": "Set alarms and timers on your Bangle",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm,widget",
|
"tags": "tool,alarm,widget",
|
||||||
|
|
|
@ -2,7 +2,7 @@ WIDGETS["alarm"]={area:"tl",width:0,draw:function() {
|
||||||
if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
|
if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
|
||||||
},reload:function() {
|
},reload:function() {
|
||||||
// don't include library here as we're trying to use as little RAM as possible
|
// 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"].width = (require('Storage').readJSON('sched.json',1)||[]).some(alarm=>alarm.on&&(alarm.hidden!==true)) ? 24 : 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
WIDGETS["alarm"].reload();
|
WIDGETS["alarm"].reload();
|
||||||
|
|
|
@ -9,3 +9,6 @@
|
||||||
0.08: Handling of alarms
|
0.08: Handling of alarms
|
||||||
0.09: Alarm vibration, repeat, and auto-snooze now handled by sched
|
0.09: Alarm vibration, repeat, and auto-snooze now handled by sched
|
||||||
0.10: Fix SMS bug
|
0.10: Fix SMS bug
|
||||||
|
0.12: Use default Bangle formatter for booleans
|
||||||
|
0.13: Added Bangle.http function (see Readme file for more info)
|
||||||
|
0.14: Fix timeout of http function not beeing cleaned up
|
||||||
|
|
|
@ -32,6 +32,25 @@ Responses are sent back to Gadgetbridge simply as one line of JSON.
|
||||||
|
|
||||||
More info on message formats on http://www.espruino.com/Gadgetbridge
|
More info on message formats on http://www.espruino.com/Gadgetbridge
|
||||||
|
|
||||||
|
## Functions provided
|
||||||
|
|
||||||
|
The boot code also provides some useful functions:
|
||||||
|
|
||||||
|
* `Bangle.messageResponse = function(msg,response)` - send a yes/no response to a message. `msg` is a message object, and `response` is a boolean.
|
||||||
|
* `Bangle.musicControl = function(cmd)` - control music, cmd = `play/pause/next/previous/volumeup/volumedown`
|
||||||
|
* `Bangle.http = function(url,options)` - make an HTTPS request to a URL and return a promise with the data. Requires the [internet enabled `Bangle.js Gadgetbridge` app](http://www.espruino.com/Gadgetbridge#http-requests). `options` can contain:
|
||||||
|
* `id` - a custom (string) ID
|
||||||
|
* `timeout` - a timeout for the request in milliseconds (default 30000ms)
|
||||||
|
* `xpath` an xPath query to run on the request (but right now the URL requested must be XML - HTML is rarely XML compliant)
|
||||||
|
|
||||||
|
eg:
|
||||||
|
|
||||||
|
```
|
||||||
|
Bangle.http("https://pur3.co.uk/hello.txt").then(data=>{
|
||||||
|
console.log("Got ",data);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Bangle.js can only hold one connection open at a time, so it's hard to see
|
Bangle.js can only hold one connection open at a time, so it's hard to see
|
||||||
|
|
|
@ -118,11 +118,50 @@
|
||||||
var cal = require("Storage").readJSON("android.calendar.json",true);
|
var cal = require("Storage").readJSON("android.calendar.json",true);
|
||||||
if (!cal || !Array.isArray(cal)) cal = [];
|
if (!cal || !Array.isArray(cal)) cal = [];
|
||||||
gbSend({t:"force_calendar_sync", ids: cal.map(e=>e.id)});
|
gbSend({t:"force_calendar_sync", ids: cal.map(e=>e.id)});
|
||||||
|
},
|
||||||
|
"http":function() {
|
||||||
|
//get the promise and call the promise resolve
|
||||||
|
if (Bangle.httpRequest === undefined) return;
|
||||||
|
var request=Bangle.httpRequest[event.id];
|
||||||
|
if (request === undefined) return; //already timedout or wrong id
|
||||||
|
delete Bangle.httpRequest[event.id];
|
||||||
|
clearTimeout(request.t); //t = timeout variable
|
||||||
|
if(event.err!==undefined) //if is error
|
||||||
|
request.j(event.err); //r = reJect function
|
||||||
|
else
|
||||||
|
request.r(event); //r = resolve function
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var h = HANDLERS[event.t];
|
var h = HANDLERS[event.t];
|
||||||
if (h) h(); else console.log("GB Unknown",event);
|
if (h) h(); else console.log("GB Unknown",event);
|
||||||
};
|
};
|
||||||
|
// HTTP request handling - see the readme
|
||||||
|
// options = {id,timeout,xpath}
|
||||||
|
Bangle.http = (url,options)=>{
|
||||||
|
options = options||{};
|
||||||
|
if (Bangle.httpRequest === undefined)
|
||||||
|
Bangle.httpRequest={};
|
||||||
|
if (options.id === undefined) {
|
||||||
|
// try and create a unique ID
|
||||||
|
do {
|
||||||
|
options.id = Math.random().toString().substr(2);
|
||||||
|
} while( Bangle.httpRequest[options.id]!==undefined);
|
||||||
|
}
|
||||||
|
//send the request
|
||||||
|
var req = {t: "http", url:url, id:options.id};
|
||||||
|
if (options.xpath) req.xpath = options.xpath;
|
||||||
|
gbSend(req);
|
||||||
|
//create the promise
|
||||||
|
var promise = new Promise(function(resolve,reject) {
|
||||||
|
//save the resolve function in the dictionary and create a timeout (30 seconds default)
|
||||||
|
Bangle.httpRequest[options.id]={r:resolve,j:reject,t:setTimeout(()=>{
|
||||||
|
//if after "timeoutMillisec" it still hasn't answered -> reject
|
||||||
|
delete Bangle.httpRequest[options.id];
|
||||||
|
reject("Timeout");
|
||||||
|
},options.timeout||30000)};
|
||||||
|
});
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
// Battery monitor
|
// Battery monitor
|
||||||
function sendBattery() { gbSend({ t: "status", bat: E.getBattery(), chg: Bangle.isCharging()?1:0 }); }
|
function sendBattery() { gbSend({ t: "status", bat: E.getBattery(), chg: Bangle.isCharging()?1:0 }); }
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "android",
|
"id": "android",
|
||||||
"name": "Android Integration",
|
"name": "Android Integration",
|
||||||
"shortName": "Android",
|
"shortName": "Android",
|
||||||
"version": "0.11",
|
"version": "0.14",
|
||||||
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,system,messages,notifications,gadgetbridge",
|
"tags": "tool,system,messages,notifications,gadgetbridge",
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
}),
|
}),
|
||||||
/*LANG*/"Keep Msgs" : {
|
/*LANG*/"Keep Msgs" : {
|
||||||
value : !!settings.keep,
|
value : !!settings.keep,
|
||||||
format : v=>v?/*LANG*/"Yes":/*LANG*/"No",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.keep = v;
|
settings.keep = v;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
|
|
|
@ -10,3 +10,4 @@
|
||||||
week is buffered until date or timezone changes
|
week is buffered until date or timezone changes
|
||||||
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
|
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
|
||||||
0.08: fixed calendar weeknumber not shortened to two digits
|
0.08: fixed calendar weeknumber not shortened to two digits
|
||||||
|
0.09: Use default Bangle formatter for booleans
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "antonclk",
|
"id": "antonclk",
|
||||||
"name": "Anton Clock",
|
"name": "Anton Clock",
|
||||||
"version": "0.08",
|
"version": "0.09",
|
||||||
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
|
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
|
||||||
"readme":"README.md",
|
"readme":"README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
(function(back) {
|
(function(back) {
|
||||||
var FILE = "antonclk.json";
|
var FILE = "antonclk.json";
|
||||||
// Load settings
|
|
||||||
var settings = Object.assign({
|
var settings = Object.assign({
|
||||||
secondsOnUnlock: false,
|
secondsOnUnlock: false,
|
||||||
}, require('Storage').readJSON(FILE, true) || {});
|
}, require('Storage').readJSON(FILE, true) || {});
|
||||||
|
@ -41,7 +40,6 @@
|
||||||
"Date": stringInSettings("dateOnMain", ["Long", "Short", "ISO8601"]),
|
"Date": stringInSettings("dateOnMain", ["Long", "Short", "ISO8601"]),
|
||||||
"Show Weekday": {
|
"Show Weekday": {
|
||||||
value: (settings.weekDay !== undefined ? settings.weekDay : true),
|
value: (settings.weekDay !== undefined ? settings.weekDay : true),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.weekDay = v;
|
settings.weekDay = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -49,7 +47,6 @@
|
||||||
},
|
},
|
||||||
"Show CalWeek": {
|
"Show CalWeek": {
|
||||||
value: (settings.calWeek !== undefined ? settings.calWeek : false),
|
value: (settings.calWeek !== undefined ? settings.calWeek : false),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.calWeek = v;
|
settings.calWeek = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -57,7 +54,6 @@
|
||||||
},
|
},
|
||||||
"Uppercase": {
|
"Uppercase": {
|
||||||
value: (settings.upperCase !== undefined ? settings.upperCase : true),
|
value: (settings.upperCase !== undefined ? settings.upperCase : true),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.upperCase = v;
|
settings.upperCase = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -65,7 +61,6 @@
|
||||||
},
|
},
|
||||||
"Vector font": {
|
"Vector font": {
|
||||||
value: (settings.vectorFont !== undefined ? settings.vectorFont : false),
|
value: (settings.vectorFont !== undefined ? settings.vectorFont : false),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.vectorFont = v;
|
settings.vectorFont = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -82,7 +77,6 @@
|
||||||
"Show": stringInSettings("secondsMode", ["Never", "Unlocked", "Always"]),
|
"Show": stringInSettings("secondsMode", ["Never", "Unlocked", "Always"]),
|
||||||
"With \":\"": {
|
"With \":\"": {
|
||||||
value: (settings.secondsWithColon !== undefined ? settings.secondsWithColon : true),
|
value: (settings.secondsWithColon !== undefined ? settings.secondsWithColon : true),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.secondsWithColon = v;
|
settings.secondsWithColon = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -90,7 +84,6 @@
|
||||||
},
|
},
|
||||||
"Color": {
|
"Color": {
|
||||||
value: (settings.secondsColoured !== undefined ? settings.secondsColoured : true),
|
value: (settings.secondsColoured !== undefined ? settings.secondsColoured : true),
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.secondsColoured = v;
|
settings.secondsColoured = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -99,9 +92,6 @@
|
||||||
"Date": stringInSettings("dateOnSecs", ["Year", "Weekday", "No"])
|
"Date": stringInSettings("dateOnSecs", ["Year", "Weekday", "No"])
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actually display the menu
|
|
||||||
E.showMenu(mainmenu);
|
E.showMenu(mainmenu);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// end of file
|
|
||||||
|
|
|
@ -12,3 +12,4 @@
|
||||||
0.12: Add settings to hide date,widgets
|
0.12: Add settings to hide date,widgets
|
||||||
0.13: Add font setting
|
0.13: Add font setting
|
||||||
0.14: Use ClockFace_menu.addItems
|
0.14: Use ClockFace_menu.addItems
|
||||||
|
0.15: Add Power saving option
|
|
@ -8,3 +8,4 @@ A simple digital clock showing seconds as a horizontal bar.
|
||||||
## Settings
|
## Settings
|
||||||
* `Show date`: display date at the bottom of screen
|
* `Show date`: display date at the bottom of screen
|
||||||
* `Font`: choose between bitmap or vector fonts
|
* `Font`: choose between bitmap or vector fonts
|
||||||
|
* `Power saving`: (Bangle.js 2 only) don't draw the seconds bar while the watch is locked
|
|
@ -13,15 +13,19 @@ let locale = require("locale");
|
||||||
locale.hasMeridian = (locale.meridian(date)!=="");
|
locale.hasMeridian = (locale.meridian(date)!=="");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let barW = 0, prevX = 0;
|
||||||
function renderBar(l) {
|
function renderBar(l) {
|
||||||
if (!this.fraction) {
|
"ram";
|
||||||
// zero-size fillRect stills draws one line of pixels, we don't want that
|
if (l) prevX = 0; // called from Layout: drawing area was cleared
|
||||||
return;
|
else l = clock.layout.bar;
|
||||||
|
let x2 = l.x+barW;
|
||||||
|
if (clock.powerSave && Bangle.isLocked()) x2 = 0; // hide bar
|
||||||
|
if (x2===prevX) return; // nothing to do
|
||||||
|
if (x2===0) x2--; // don't leave 1px line
|
||||||
|
if (x2<Math.max(0, prevX)) g.setBgColor(l.bgCol || g.theme.bg).clearRect(x2+1, l.y, prevX, l.y2);
|
||||||
|
else g.setColor(l.col || g.theme.fg).fillRect(prevX+1, l.y, x2, l.y2);
|
||||||
|
prevX = x2;
|
||||||
}
|
}
|
||||||
const width = this.fraction*l.w;
|
|
||||||
g.fillRect(l.x, l.y, l.x+width-1, l.y+l.height-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function timeText(date) {
|
function timeText(date) {
|
||||||
if (!clock.is12Hour) {
|
if (!clock.is12Hour) {
|
||||||
|
@ -47,11 +51,10 @@ function dateText(date) {
|
||||||
return `${dayName} ${dayMonth}`;
|
return `${dayName} ${dayMonth}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const ClockFace = require("ClockFace"),
|
const ClockFace = require("ClockFace"),
|
||||||
clock = new ClockFace({
|
clock = new ClockFace({
|
||||||
precision: 1,
|
precision: 1,
|
||||||
settingsFile:'barclock.settings.json',
|
settingsFile: "barclock.settings.json",
|
||||||
init: function() {
|
init: function() {
|
||||||
const Layout = require("Layout");
|
const Layout = require("Layout");
|
||||||
this.layout = new Layout({
|
this.layout = new Layout({
|
||||||
|
@ -62,7 +65,7 @@ const ClockFace = require("ClockFace"),
|
||||||
{id: "ampm", label: " ", type: "txt", font: "6x8:2", col: g.theme.fg, bgCol: g.theme.bg},
|
{id: "ampm", label: " ", type: "txt", font: "6x8:2", col: g.theme.fg, bgCol: g.theme.bg},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
|
{id: "bar", type: "custom", fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
|
||||||
this.showDate ? {height: 40} : {},
|
this.showDate ? {height: 40} : {},
|
||||||
this.showDate ? {id: "date", type: "txt", font: "10%", valign: 1} : {},
|
this.showDate ? {id: "date", type: "txt", font: "10%", valign: 1} : {},
|
||||||
],
|
],
|
||||||
|
@ -76,7 +79,8 @@ const ClockFace = require("ClockFace"),
|
||||||
this.layout.ampm.label = "";
|
this.layout.ampm.label = "";
|
||||||
thickness = Math.floor(Bangle.appRect.w/(5*6));
|
thickness = Math.floor(Bangle.appRect.w/(5*6));
|
||||||
}
|
}
|
||||||
this.layout.bar.height = thickness+1;
|
let bar = this.layout.bar;
|
||||||
|
bar.height = thickness+1;
|
||||||
if (this.font===1) { // vector
|
if (this.font===1) { // vector
|
||||||
const B2 = process.env.HWVERSION>1;
|
const B2 = process.env.HWVERSION>1;
|
||||||
if (this.is12Hour && locale.hasMeridian) {
|
if (this.is12Hour && locale.hasMeridian) {
|
||||||
|
@ -89,17 +93,32 @@ const ClockFace = require("ClockFace"),
|
||||||
this.layout.time.font = "6x8:"+thickness;
|
this.layout.time.font = "6x8:"+thickness;
|
||||||
}
|
}
|
||||||
this.layout.update();
|
this.layout.update();
|
||||||
|
bar.y2 = bar.y+bar.height-1;
|
||||||
},
|
},
|
||||||
update: function(date, c) {
|
update: function(date, c) {
|
||||||
|
"ram";
|
||||||
if (c.m) this.layout.time.label = timeText(date);
|
if (c.m) this.layout.time.label = timeText(date);
|
||||||
if (c.h) this.layout.ampm.label = ampmText(date);
|
if (c.h) this.layout.ampm.label = ampmText(date);
|
||||||
if (c.d && this.showDate) this.layout.date.label = dateText(date);
|
if (c.d && this.showDate) this.layout.date.label = dateText(date);
|
||||||
const SECONDS_PER_MINUTE = 60;
|
if (c.m) this.layout.render();
|
||||||
if (c.s) this.layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE;
|
if (c.s) {
|
||||||
this.layout.render();
|
barW = Math.round(date.getSeconds()/60*this.layout.bar.w);
|
||||||
|
renderBar();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
resume: function() {
|
resume: function() {
|
||||||
|
prevX = 0; // force redraw of bar
|
||||||
this.layout.forgetLazyState();
|
this.layout.forgetLazyState();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// power saving: only update once a minute while locked, hide bar
|
||||||
|
if (clock.powerSave) {
|
||||||
|
Bangle.on("lock", lock => {
|
||||||
|
clock.precision = lock ? 60 : 1;
|
||||||
|
clock.tick();
|
||||||
|
renderBar(); // hide/redraw bar right away
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
clock.start();
|
clock.start();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "barclock",
|
"id": "barclock",
|
||||||
"name": "Bar Clock",
|
"name": "Bar Clock",
|
||||||
"version": "0.14",
|
"version": "0.15",
|
||||||
"description": "A simple digital clock showing seconds as a bar",
|
"description": "A simple digital clock showing seconds as a bar",
|
||||||
"icon": "clock-bar.png",
|
"icon": "clock-bar.png",
|
||||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
|
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
|
||||||
|
|
|
@ -17,10 +17,14 @@
|
||||||
onchange: v => save("font", v),
|
onchange: v => save("font", v),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
require("ClockFace_menu").addItems(menu, save, {
|
let items = {
|
||||||
showDate: s.showDate,
|
showDate: s.showDate,
|
||||||
loadWidgets: s.loadWidgets,
|
loadWidgets: s.loadWidgets,
|
||||||
});
|
};
|
||||||
|
// Power saving for Bangle.js 1 doesn't make sense (no updates while screen is off anyway)
|
||||||
|
if (process.env.HWVERSION>1) {
|
||||||
|
items.powerSave = s.powerSave;
|
||||||
|
}
|
||||||
|
require("ClockFace_menu").addItems(menu, save, items);
|
||||||
E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Barometer altitude adjustment setting
|
0.02: Barometer altitude adjustment setting
|
||||||
|
0.03: Use default Bangle formatter for booleans
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "bikespeedo",
|
"id": "bikespeedo",
|
||||||
"name": "Bike Speedometer (beta)",
|
"name": "Bike Speedometer (beta)",
|
||||||
"shortName": "Bike Speedometer",
|
"shortName": "Bike Speedometer",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Shows GPS speed, GPS heading, Compass heading, GPS altitude and Barometer altitude from internal sources",
|
"description": "Shows GPS speed, GPS heading, Compass heading, GPS altitude and Barometer altitude from internal sources",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"Screenshot.png"}],
|
"screenshots": [{"url":"Screenshot.png"}],
|
||||||
|
|
|
@ -33,12 +33,10 @@
|
||||||
'< Back': function() { E.showMenu(appMenu); },
|
'< Back': function() { E.showMenu(appMenu); },
|
||||||
'Speed' : {
|
'Speed' : {
|
||||||
value : settings.spdFilt,
|
value : settings.spdFilt,
|
||||||
format : v => v?"On":"Off",
|
|
||||||
onchange : () => { settings.spdFilt = !settings.spdFilt; writeSettings(); }
|
onchange : () => { settings.spdFilt = !settings.spdFilt; writeSettings(); }
|
||||||
},
|
},
|
||||||
'Altitude' : {
|
'Altitude' : {
|
||||||
value : settings.altFilt,
|
value : settings.altFilt,
|
||||||
format : v => v?"On":"Off",
|
|
||||||
onchange : () => { settings.altFilt = !settings.altFilt; writeSettings(); }
|
onchange : () => { settings.altFilt = !settings.altFilt; writeSettings(); }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
...
|
||||||
|
0.02: First update with ChangeLog Added
|
||||||
|
0.03: updated watch face to use the ClockFace library
|
|
@ -1,55 +1,179 @@
|
||||||
var sprite = {
|
var sprite = {
|
||||||
width : 47, height : 47, bpp : 3,
|
width: 47,
|
||||||
|
height: 47,
|
||||||
|
bpp: 3,
|
||||||
transparent: 1,
|
transparent: 1,
|
||||||
buffer : require("heatshrink").decompress(atob("kmSpICFn/+BAwCImV//VICJuT//SogRMpmT/2SCJtSyQDB/4RMymRkmX/gRLygDC3/piVhCJElAYf/pNIkgRIlIDCl/6pVBkIRIGwWJEYPypMJCI9KGwQRBLANIPRI2CGoPkyVCBwmeyVLTYNJom8yImBz4gEqV/6Vf+g2BPwf/IIq8C/+kyVRkgDBp/5CIX/+mkz/+y/9BIOf0v6///5LdCz+kCIOk34RBYQMSp5XBGQVk/pNBAQP/9IyBxGSv4yCk/1OIK8EC4QgEpM/JgJ+EGoIRBTApQCEYvplLOFXIIdBO4SqBeQJABGoeTDQMlk5WCAAPSYQLgEz4aBlM/9IgB/7CCcAvP/QsBiVfUwOJBgUiCIcmpAVCy/+pMAKwMkRgIRCp6VBAwW6qVOgmSgPkwgRDv53E6WSuEkyEPRgmf2VJv5HBl2SgAKBwEJRgnJiVKp/Sr/0y/yBQOQv56DKwVSv2STwO/DgWD/BADmaDByRoBYoQRCgFCCIf/+jgDNwOUAwMg/kSPQbODX4IJBAwUH8B6DsmRl5oBl7OBklMyV+gBoDycSxMpiVLZwS8EAQeYyjaByR6BBIJBDAQnEIgbFCogOFRgQDBr//I4L0EAQsxAYP//5WCGQ6MCAAKbCpKYEAQiMB//kIQOUyf+CJF/CIIEBTYOfcgQRHBQv/CJKnBpP8GRTCDJIPkGRQCB5I3C/n/EZUgA"))
|
buffer: require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"kmSpICFn/+BAwCImV//VICJuT//SogRMpmT/2SCJtSyQDB/4RMymRkmX/gRLygDC3/piVhCJElAYf/pNIkgRIlIDCl/6pVBkIRIGwWJEYPypMJCI9KGwQRBLANIPRI2CGoPkyVCBwmeyVLTYNJom8yImBz4gEqV/6Vf+g2BPwf/IIq8C/+kyVRkgDBp/5CIX/+mkz/+y/9BIOf0v6///5LdCz+kCIOk34RBYQMSp5XBGQVk/pNBAQP/9IyBxGSv4yCk/1OIK8EC4QgEpM/JgJ+EGoIRBTApQCEYvplLOFXIIdBO4SqBeQJABGoeTDQMlk5WCAAPSYQLgEz4aBlM/9IgB/7CCcAvP/QsBiVfUwOJBgUiCIcmpAVCy/+pMAKwMkRgIRCp6VBAwW6qVOgmSgPkwgRDv53E6WSuEkyEPRgmf2VJv5HBl2SgAKBwEJRgnJiVKp/Sr/0y/yBQOQv56DKwVSv2STwO/DgWD/BADmaDByRoBYoQRCgFCCIf/+jgDNwOUAwMg/kSPQbODX4IJBAwUH8B6DsmRl5oBl7OBklMyV+gBoDycSxMpiVLZwS8EAQeYyjaByR6BBIJBDAQnEIgbFCogOFRgQDBr//I4L0EAQsxAYP//5WCGQ6MCAAKbCpKYEAQiMB//kIQOUyf+CJF/CIIEBTYOfcgQRHBQv/CJKnBpP8GRTCDJIPkGRQCB5I3C/n/EZUgA"
|
||||||
|
)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const boxes = {
|
const boxes = {
|
||||||
width : 122, height : 56, bpp : 3,
|
width: 122,
|
||||||
|
height: 56,
|
||||||
|
bpp: 3,
|
||||||
transparent: 1,
|
transparent: 1,
|
||||||
buffer : require("heatshrink").decompress(atob("kmZkmSpICPwgDBmQUQAQMJAYNkFiOSiQDB5JESAYQsSpADByYsSyBZBydt23bAR+wgFJkwUQAQNggGSposR23AgMkzZESwECpM2IiUAgmSFiW2gDlBFiVsgDlBFiXYgDNBL4MDWZy2FgEGWZy2FgENWZy2EL4MbWZpTBWwZfBXJpTCWwZiCWZpTBWwZiCWZsbWwhiCWZpWCWwTORWwgXRWwgXRWwZESWwZESWwZESWwYXRWwgXRW362/W362/W362/W362/W362/W362/W362/W362/W362/W362/WwuAgazOWwsAgyzOWwsAhqzOWwhfBjazNKYK2DL4K5NKYS2DMQSzNKYK2DMQSzNja2EMQSzNKwS2CZyK2EC6K2EC6K2DIiS2DIiS2DIiUAFoMAAFTkBFtckyAtrLgWSpICnLIIsqyVAgAsqpIA="))
|
buffer: require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"kmZkmSpICPwgDBmQUQAQMJAYNkFiOSiQDB5JESAYQsSpADByYsSyBZBydt23bAR+wgFJkwUQAQNggGSposR23AgMkzZESwECpM2IiUAgmSFiW2gDlBFiVsgDlBFiXYgDNBL4MDWZy2FgEGWZy2FgENWZy2EL4MbWZpTBWwZfBXJpTCWwZiCWZpTBWwZiCWZsbWwhiCWZpWCWwTORWwgXRWwgXRWwZESWwZESWwZESWwYXRWwgXRW362/W362/W362/W362/W362/W362/W362/W362/W362/W362/WwuAgazOWwsAgyzOWwsAhqzOWwhfBjazNKYK2DL4K5NKYS2DMQSzNKYK2DMQSzNja2EMQSzNKwS2CZyK2EC6K2EC6K2DIiS2DIiS2DIiUAFoMAAFTkBFtckyAtrLgWSpICnLIIsqyVAgAsqpIA="
|
||||||
|
)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const background = {
|
const background = {
|
||||||
width : 176, height : 176, bpp : 3,
|
width: 176,
|
||||||
|
height: 176,
|
||||||
|
bpp: 3,
|
||||||
transparent: 5,
|
transparent: 5,
|
||||||
buffer : require("heatshrink").decompress(atob("kmSpIC/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/ATWAgEAIP1///8iRB8gf/AAOCIPdIIARBBoJB/+E4IP4ABghB9v4CB8BB5g/92//9pB7wP/97FEIO9IgDACAAn8iVBIOlHH4xBDnA+wyY9IAAmB/BB//5B/IOQ/OAARBup5B/yV/IP5B/IP5BRt5B7/wDC7aD8/w+B+3bBgP7IP5B7HYNt23/AQPfIPX/9oCC24IDINwCBIRAAHIOACBHI3+g4EC/l/4BByAQkA//wpED//4gGAhJB3pMAgQFBgEBH3AC/AX4C/AX4C/AX4C/AX4C/AUOAgBB/v//ghB9gf///gH3UgiVIIAJBBwRB5j+CIIf8uBB5//wIIXb//+hJB6o/92/7v5B7/0/97GCIPYAG4MgIP/BjkSIP34/hB//5B/AAQ+0IP5B/IP5BN7ZB97///wCBIPX93yAB2wCB+5B5tv//dt24CB35B5v/+n/t+P/I4PH8ESIO38gFA/+CgH/+EIgiD3gACCPoMAgQ+2AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/ASVIgAACgRB/IPY8GkAHBiRB/IPBLKgJB/IP5B/AQUAkmQghB/IP2AgEAyVAiRB/IP5BBpMAIP5B/IIUkgBB/IP5BpoAsBgJBOgEEIIoIBIP5BlyE27dt2EEIJ4CBBAlIgRBgpEAhu2IIO24ESQwxB/IJQhGkEJIL8GHwQCDgOweQpB/IKMkwAKJILVgAofYeQhBzsEAIKICLoESILmBQARBBtuwgZB3kA4B4ENIgJBcpMAIMYCDIOcAgEbHYgCGsEJkhEBE6cBIP5BZfYQ+JIIkDsEBIP5BVyEAIKtAHxgCDwBEBINk2IKCGCIKmSpECIP5BUkEBHyACD2BBUFoMJIP5BSpEbHyQCDIP5BXkmAIP5B/AQcAbKJB/ILH/AAP8hM/AgWSv4KCAAP+gmfAoXJk4ME//gpIEC8mTBgvwkgEC+QRDAAX4gVPAgP5kgsCLwWQh/kMIUf5LuFg4jBAoMBKAJ5EwF/AoUA/yFFoE/CI6RDgY+BCIQsDIP5B/IP5B/IP5B/IJ/AIJfghJBKv0EIJcAIJfwIP5BMhMAAAMEz5BGgmABoVJII9IBgUkII8kBgUSII8CoAMBhJB/IIsQoMAYoP/AAP4YpAMC/+BII9/BgXAYpAMC8DFIBgXwIIcCIP6DCgkQh/kCIRBIbQcBIJAFCgBBICI5BE/IRDFgQA="))
|
buffer: require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"kmSpIC/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/ATWAgEAIP1///8iRB8gf/AAOCIPdIIARBBoJB/+E4IP4ABghB9v4CB8BB5g/92//9pB7wP/97FEIO9IgDACAAn8iVBIOlHH4xBDnA+wyY9IAAmB/BB//5B/IOQ/OAARBup5B/yV/IP5B/IP5BRt5B7/wDC7aD8/w+B+3bBgP7IP5B7HYNt23/AQPfIPX/9oCC24IDINwCBIRAAHIOACBHI3+g4EC/l/4BByAQkA//wpED//4gGAhJB3pMAgQFBgEBH3AC/AX4C/AX4C/AX4C/AX4C/AUOAgBB/v//ghB9gf///gH3UgiVIIAJBBwRB5j+CIIf8uBB5//wIIXb//+hJB6o/92/7v5B7/0/97GCIPYAG4MgIP/BjkSIP34/hB//5B/AAQ+0IP5B/IP5BN7ZB97///wCBIPX93yAB2wCB+5B5tv//dt24CB35B5v/+n/t+P/I4PH8ESIO38gFA/+CgH/+EIgiD3gACCPoMAgQ+2AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/AX4C/ASVIgAACgRB/IPY8GkAHBiRB/IPBLKgJB/IP5B/AQUAkmQghB/IP2AgEAyVAiRB/IP5BBpMAIP5B/IIUkgBB/IP5BpoAsBgJBOgEEIIoIBIP5BlyE27dt2EEIJ4CBBAlIgRBgpEAhu2IIO24ESQwxB/IJQhGkEJIL8GHwQCDgOweQpB/IKMkwAKJILVgAofYeQhBzsEAIKICLoESILmBQARBBtuwgZB3kA4B4ENIgJBcpMAIMYCDIOcAgEbHYgCGsEJkhEBE6cBIP5BZfYQ+JIIkDsEBIP5BVyEAIKtAHxgCDwBEBINk2IKCGCIKmSpECIP5BUkEBHyACD2BBUFoMJIP5BSpEbHyQCDIP5BXkmAIP5B/AQcAbKJB/ILH/AAP8hM/AgWSv4KCAAP+gmfAoXJk4ME//gpIEC8mTBgvwkgEC+QRDAAX4gVPAgP5kgsCLwWQh/kMIUf5LuFg4jBAoMBKAJ5EwF/AoUA/yFFoE/CI6RDgY+BCIQsDIP5B/IP5B/IP5B/IJ/AIJfghJBKv0EIJcAIJfwIP5BMhMAAAMEz5BGgmABoVJII9IBgUkII8kBgUSII8CoAMBhJB/IIsQoMAYoP/AAP4YpAMC/+BII9/BgXAYpAMC8DFIBgXwIIcCIP6DCgkQh/kCIRBIbQcBIJAFCgBBICI5BE/IRDFgQA="
|
||||||
|
)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
numbersDims = {
|
numbersDims = {
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 44
|
height: 44,
|
||||||
};
|
};
|
||||||
const numbers = [
|
const numbers = [
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARGQKYQIDAwUEBxMAAQNAgECpMgAQMkB4IOIAQQLCgEQBwQaBgEBB1oCBBwYCCiRWDCIRWEO5wOHAX4CnA=")),
|
require("heatshrink").decompress(
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARNIKYIIEwEAggOKNIQODyAHCBxQsWB3TUFgMgA4sSBwzU/AVA=")),
|
atob(
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKggIBAwkCBw+QCIQLCgIRCDQcQBwwyDDwUSCgVAAwIOBEwI7EpI7FBw4FDghZGHwgOEF4Y+CEYQ+DBxQADNAIAFNAIOFa/4CoA=")),
|
"ikswcBkmSpIC/ARGQKYQIDAwUEBxMAAQNAgECpMgAQMkB4IOIAQQLCgEQBwQaBgEBB1oCBBwYCCiRWDCIRWEO5wOHAX4CnA="
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKosSAwsBBw4aCoEAgQjEBoIpEBwtIBoIUEwEAggUDBwwyDDoWQA4ZWHhIIEJQoOCgI+EBwMQEAYOJO4oLBO4oRDJQrX/AU4")),
|
)
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARNIKgQIDwAGBgQOJNQYOCyAHDBxEggB6BBwYDBiVABxIjBCIIODF4YOEAAkBV40QBwxiDNAosEB0IC/AUg")),
|
),
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ5UFkmQAwkCBxIdGCIIIDBxAsTgAaEkEASooOBiQOVJQgOBiBKDBxMSJQwRBLIgRCBwjX/AVA=")),
|
require("heatshrink").decompress(
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/ARGQKgYICAwcCBxADBiQdDkEANYoOGEAYyEHYoOIHYqfFBxIdDBAMQFgZHCBysSFgwRBO46GFa/4CnA")),
|
atob("ikswcBkmSpIC/ARNIKYIIEwEAggOKNIQODyAHCBxQsWB3TUFgMgA4sSBwzU/AVA=")
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ5VGiAGFgIOIDQUgBwUCEYQOJGQYNBHAlADQgOHwEAggUDpANBCgYpBBwmQAwJiGhIjDB1gC/AU4A=")),
|
),
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKYYICAwcEBxGQgAaDgVJgACBDQQOJgB6CBwcAiQODHa4AEhIRBpAHDiARBwAGCgIgCFIYOCFIYOHiQrEJQxlCBwzX/AVAA=")),
|
require("heatshrink").decompress(
|
||||||
require("heatshrink").decompress(atob("ikswcBkmSpIC/AQ8gKggIBAwkCBw+QCIQLCgIRCDQcQBzkSTAsBHYoOIL4gOCMooOENAYOCoA4EBwoqDgiGGF4gOEa/4CoA=")),
|
atob(
|
||||||
|
"ikswcBkmSpIC/AQ8gKggIBAwkCBw+QCIQLCgIRCDQcQBwwyDDwUSCgVAAwIOBEwI7EpI7FBw4FDghZGHwgOEF4Y+CEYQ+DBxQADNAIAFNAIOFa/4CoA="
|
||||||
|
)
|
||||||
|
),
|
||||||
|
require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"ikswcBkmSpIC/AQ8gKosSAwsBBw4aCoEAgQjEBoIpEBwtIBoIUEwEAggUDBwwyDDoWQA4ZWHhIIEJQoOCgI+EBwMQEAYOJO4oLBO4oRDJQrX/AU4"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"ikswcBkmSpIC/ARNIKgQIDwAGBgQOJNQYOCyAHDBxEggB6BBwYDBiVABxIjBCIIODF4YOEAAkBV40QBwxiDNAosEB0IC/AUg"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"ikswcBkmSpIC/AQ5UFkmQAwkCBxIdGCIIIDBxAsTgAaEkEASooOBiQOVJQgOBiBKDBxMSJQwRBLIgRCBwjX/AVA="
|
||||||
|
)
|
||||||
|
),
|
||||||
|
require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"ikswcBkmSpIC/ARGQKgYICAwcCBxADBiQdDkEANYoOGEAYyEHYoOIHYqfFBxIdDBAMQFgZHCBysSFgwRBO46GFa/4CnA"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"ikswcBkmSpIC/AQ5VGiAGFgIOIDQUgBwUCEYQOJGQYNBHAlADQgOHwEAggUDpANBCgYpBBwmQAwJiGhIjDB1gC/AU4A="
|
||||||
|
)
|
||||||
|
),
|
||||||
|
require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"ikswcBkmSpIC/AQ8gKYYICAwcEBxGQgAaDgVJgACBDQQOJgB6CBwcAiQODHa4AEhIRBpAHDiARBwAGCgIgCFIYOCFIYOHiQrEJQxlCBwzX/AVAA="
|
||||||
|
)
|
||||||
|
),
|
||||||
|
require("heatshrink").decompress(
|
||||||
|
atob(
|
||||||
|
"ikswcBkmSpIC/AQ8gKggIBAwkCBw+QCIQLCgIRCDQcQBzkSTAsBHYoOIL4gOCMooOENAYOCoA4EBwoqDgiGGF4gOEa/4CoA="
|
||||||
|
)
|
||||||
|
),
|
||||||
];
|
];
|
||||||
digitPositions = [ // relative to the box
|
digitPositions = [
|
||||||
{x:13, y:6}, {x:32, y:6},
|
// relative to the box
|
||||||
{x:74, y:6}, {x:93, y:6},
|
{ x: 13, y: 6 },
|
||||||
|
{ x: 32, y: 6 },
|
||||||
|
{ x: 74, y: 6 },
|
||||||
|
{ x: 93, y: 6 },
|
||||||
];
|
];
|
||||||
|
|
||||||
var drawTimeout;
|
|
||||||
const animation_duration = 1; // seconds
|
const animation_duration = 1; // seconds
|
||||||
const animation_steps = 20;
|
const animation_steps = 20;
|
||||||
const jump_height = 45; // top coordinate of the jump
|
const jump_height = 45; // top coordinate of the jump
|
||||||
const seconds_per_minute = 60;
|
const seconds_per_minute = 60;
|
||||||
|
|
||||||
function draw() {
|
const ClockFace = require("ClockFace");
|
||||||
const now = new Date();
|
const clock = new ClockFace({
|
||||||
|
precision: 60, // just once a minute
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
// Clear the screen once, at startup
|
||||||
|
g.setTheme({ bg: "#00f", fg: "#fff", dark: true }).clear();
|
||||||
|
|
||||||
|
this.drawing = true;
|
||||||
|
|
||||||
|
this.simpleDraw = function(now) {
|
||||||
|
var boxTL_x = 27;
|
||||||
|
var boxTL_y = 29;
|
||||||
|
var sprite_TL_x = 72;
|
||||||
|
var sprite_TL_y = 161 - sprite.height;
|
||||||
|
const seconds =
|
||||||
|
(now.getSeconds() % seconds_per_minute) + now.getMilliseconds() / 1000;
|
||||||
|
const hours =
|
||||||
|
this.is12Hour && now.getHours() > 12
|
||||||
|
? now.getHours() - 12
|
||||||
|
: now.getHours();
|
||||||
|
|
||||||
|
const minutes = now.getMinutes();
|
||||||
|
|
||||||
|
g.drawImage(boxes, boxTL_x, boxTL_y);
|
||||||
|
g.drawImage(
|
||||||
|
numbers[(hours / 10) >> 0],
|
||||||
|
boxTL_x + digitPositions[0].x,
|
||||||
|
boxTL_y + digitPositions[0].y
|
||||||
|
);
|
||||||
|
g.drawImage(
|
||||||
|
numbers[hours % 10 >> 0],
|
||||||
|
boxTL_x + digitPositions[1].x,
|
||||||
|
boxTL_y + digitPositions[1].y
|
||||||
|
);
|
||||||
|
g.drawImage(
|
||||||
|
numbers[(minutes / 10) >> 0],
|
||||||
|
boxTL_x + digitPositions[2].x,
|
||||||
|
boxTL_y + digitPositions[2].y
|
||||||
|
);
|
||||||
|
g.drawImage(
|
||||||
|
numbers[minutes % 10 >> 0],
|
||||||
|
boxTL_x + digitPositions[3].x,
|
||||||
|
boxTL_y + digitPositions[3].y
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
pause: function() {
|
||||||
|
this.drawing = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
resume: function() {
|
||||||
|
this.drawing = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
draw: function(now) {
|
||||||
|
if (!this.drawing) {
|
||||||
|
this.simpleDraw(now);
|
||||||
|
return;
|
||||||
|
}
|
||||||
g.drawImage(background, 0, 0);
|
g.drawImage(background, 0, 0);
|
||||||
var boxTL_x = 27; var boxTL_y = 29;
|
var boxTL_x = 27;
|
||||||
var sprite_TL_x = 72; var sprite_TL_y = 161 - sprite.height;
|
var boxTL_y = 29;
|
||||||
const seconds = now.getSeconds()%seconds_per_minute + now.getMilliseconds()/1000;
|
var sprite_TL_x = 72;
|
||||||
const hours = now.getHours();
|
var sprite_TL_y = 161 - sprite.height;
|
||||||
|
const seconds =
|
||||||
|
(now.getSeconds() % seconds_per_minute) + now.getMilliseconds() / 1000;
|
||||||
|
const hours =
|
||||||
|
this.is12Hour && now.getHours() > 12
|
||||||
|
? now.getHours() - 12
|
||||||
|
: now.getHours();
|
||||||
|
|
||||||
const minutes = now.getMinutes();
|
const minutes = now.getMinutes();
|
||||||
|
|
||||||
var time_advance = seconds / animation_duration;
|
var time_advance = seconds / animation_duration;
|
||||||
|
@ -57,46 +181,53 @@ function draw() {
|
||||||
if (time_advance < 0.5) {
|
if (time_advance < 0.5) {
|
||||||
sprite_TL_y += (jump_height - sprite_TL_y) * time_advance * 2;
|
sprite_TL_y += (jump_height - sprite_TL_y) * time_advance * 2;
|
||||||
} else if (time_advance < 1) {
|
} else if (time_advance < 1) {
|
||||||
sprite_TL_y = jump_height + (sprite_TL_y-jump_height) * (time_advance-0.5) * 2;
|
sprite_TL_y =
|
||||||
|
jump_height + (sprite_TL_y - jump_height) * (time_advance - 0.5) * 2;
|
||||||
}
|
}
|
||||||
const box_penetration = boxTL_y + boxes.height - sprite_TL_y;
|
const box_penetration = boxTL_y + boxes.height - sprite_TL_y;
|
||||||
if (box_penetration > 0) {
|
if (box_penetration > 0) {
|
||||||
boxTL_y -= box_penetration;
|
boxTL_y -= box_penetration;
|
||||||
}
|
}
|
||||||
g.drawImage(boxes, boxTL_x, boxTL_y);
|
g.drawImage(boxes, boxTL_x, boxTL_y);
|
||||||
g.drawImage(numbers[(hours / 10) >> 0], boxTL_x+digitPositions[0].x, boxTL_y+digitPositions[0].y);
|
g.drawImage(
|
||||||
g.drawImage(numbers[(hours % 10) >> 0], boxTL_x+digitPositions[1].x, boxTL_y+digitPositions[1].y);
|
numbers[(hours / 10) >> 0],
|
||||||
g.drawImage(numbers[(minutes / 10) >> 0], boxTL_x+digitPositions[2].x, boxTL_y+digitPositions[2].y);
|
boxTL_x + digitPositions[0].x,
|
||||||
g.drawImage(numbers[(minutes % 10) >> 0], boxTL_x+digitPositions[3].x, boxTL_y+digitPositions[3].y);
|
boxTL_y + digitPositions[0].y
|
||||||
|
);
|
||||||
|
g.drawImage(
|
||||||
|
numbers[hours % 10 >> 0],
|
||||||
|
boxTL_x + digitPositions[1].x,
|
||||||
|
boxTL_y + digitPositions[1].y
|
||||||
|
);
|
||||||
|
g.drawImage(
|
||||||
|
numbers[(minutes / 10) >> 0],
|
||||||
|
boxTL_x + digitPositions[2].x,
|
||||||
|
boxTL_y + digitPositions[2].y
|
||||||
|
);
|
||||||
|
g.drawImage(
|
||||||
|
numbers[minutes % 10 >> 0],
|
||||||
|
boxTL_x + digitPositions[3].x,
|
||||||
|
boxTL_y + digitPositions[3].y
|
||||||
|
);
|
||||||
g.drawImage(sprite, sprite_TL_x, sprite_TL_y);
|
g.drawImage(sprite, sprite_TL_x, sprite_TL_y);
|
||||||
Bangle.drawWidgets();
|
// Bangle.drawWidgets();
|
||||||
|
|
||||||
const timeout = time_advance <= 1?
|
if (this.drawing) {
|
||||||
animation_duration / animation_steps
|
const timeout =
|
||||||
: (seconds_per_minute - seconds);
|
time_advance <= 1 ? animation_duration / animation_steps : -999;
|
||||||
setTimeout( _=>{
|
if (timeout > 0) {
|
||||||
drawTimeout = undefined;
|
setTimeout((_) => {
|
||||||
draw();
|
this.draw(new Date());
|
||||||
}, timeout * 1000);
|
}, timeout * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the screen once, at startup
|
|
||||||
g.setTheme({bg:"#00f",fg:"#fff",dark:true}).clear();
|
|
||||||
|
|
||||||
Bangle.on('lcdPower',on=>{
|
|
||||||
if (on) {
|
|
||||||
draw(); // draw immediately, queue redraw
|
|
||||||
} else { // stop draw timer
|
|
||||||
if (drawTimeout) {
|
|
||||||
clearTimeout(drawTimeout);
|
|
||||||
}
|
}
|
||||||
drawTimeout = undefined;
|
},
|
||||||
|
|
||||||
|
update: function(date, changed) {
|
||||||
|
if (this.drawing && changed.m) {
|
||||||
|
this.draw(date);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show launcher when middle button pressed
|
clock.start();
|
||||||
Bangle.setUI("clock");
|
|
||||||
// Load widgets
|
|
||||||
Bangle.loadWidgets();
|
|
||||||
|
|
||||||
draw();
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "bowserWF",
|
"id": "bowserWF",
|
||||||
"name": "Bowser Watchface",
|
"name": "Bowser Watchface",
|
||||||
"shortName": "Bowser Watchface",
|
"shortName": "Bowser Watchface",
|
||||||
"version":"0.02",
|
"version": "0.03",
|
||||||
"description": "Let bowser show you the time",
|
"description": "Let bowser show you the time",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -22,3 +22,10 @@
|
||||||
Restructure the settings menu
|
Restructure the settings menu
|
||||||
0.08: Allow scanning for devices in settings
|
0.08: Allow scanning for devices in settings
|
||||||
0.09: Misc Fixes and improvements (https://github.com/espruino/BangleApps/pull/1655)
|
0.09: Misc Fixes and improvements (https://github.com/espruino/BangleApps/pull/1655)
|
||||||
|
0.10: Use default Bangle formatter for booleans
|
||||||
|
0.11: App now shows status info while connecting
|
||||||
|
Fixes to allow cached BluetoothRemoteGATTCharacteristic to work with 2v14.14 onwards (>1 central)
|
||||||
|
0.12: Fix HRM fallback handling
|
||||||
|
Use default boolean formatter in custom menu and directly apply config if useful
|
||||||
|
Allow recording unmodified internal HR
|
||||||
|
Better connection retry handling
|
||||||
|
|
|
@ -19,7 +19,14 @@ Just install the app, then install an app that uses the heart rate monitor.
|
||||||
Once installed you will have to go into this app's settings while your heart rate monitor
|
Once installed you will have to go into this app's settings while your heart rate monitor
|
||||||
is available for bluetooth pairing and scan for devices.
|
is available for bluetooth pairing and scan for devices.
|
||||||
|
|
||||||
**To disable this and return to normal HRM, uninstall the app**
|
**To disable this and return to normal HRM, uninstall the app or change the settings**
|
||||||
|
|
||||||
|
### Modes
|
||||||
|
|
||||||
|
* Off - Internal HRM is used, no attempt on connecting to BT HRM.
|
||||||
|
* Default - Replaces internal HRM with BT HRM and falls back to internal HRM if no valid measurements received.
|
||||||
|
* Both - The BT HRM needs to be started explicitly by an app that wants to use it. BT HRM has its own event and is completely separated from the internal HRM. Apps not supporting the BT HRM will not see the BT HRM measurements.
|
||||||
|
* Custom - Combine low level settings as you see fit.
|
||||||
|
|
||||||
## Compatible Heart Rate Monitors
|
## Compatible Heart Rate Monitors
|
||||||
|
|
||||||
|
@ -35,6 +42,10 @@ So far it has been tested on:
|
||||||
* Polar OH1
|
* Polar OH1
|
||||||
* Wahoo TICKR X 2
|
* Wahoo TICKR X 2
|
||||||
|
|
||||||
|
## Recorder plugin
|
||||||
|
|
||||||
|
The recorder plugin can record the BT HRM event (blue) and the original unchanged HRM event (green). This is mainly useful for debugging purposes or comparing the BT with the internal HRM, as the resulting "merged" HRM can be recordet using the default HRM recorder.
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
This replaces `Bangle.setHRMPower` with its own implementation.
|
This replaces `Bangle.setHRMPower` with its own implementation.
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
var log = function(text, param){
|
var log = function(text, param){
|
||||||
|
if (global.showStatusInfo)
|
||||||
|
showStatusInfo(text);
|
||||||
if (settings.debuglog){
|
if (settings.debuglog){
|
||||||
var logline = new Date().toISOString() + " - " + text;
|
var logline = new Date().toISOString() + " - " + text;
|
||||||
if (param){
|
if (param) logline += ": " + JSON.stringify(param);
|
||||||
logline += " " + JSON.stringify(param);
|
|
||||||
}
|
|
||||||
print(logline);
|
print(logline);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var addNotificationHandler = function(characteristic) {
|
var addNotificationHandler = function(characteristic) {
|
||||||
log("Setting notification handler: " + supportedCharacteristics[characteristic.uuid].handler);
|
log("Setting notification handler"/*supportedCharacteristics[characteristic.uuid].handler*/);
|
||||||
characteristic.on('characteristicvaluechanged', (ev) => supportedCharacteristics[characteristic.uuid].handler(ev.target.value));
|
characteristic.on('characteristicvaluechanged', (ev) => supportedCharacteristics[characteristic.uuid].handler(ev.target.value));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,7 +61,8 @@
|
||||||
writeCache(cache);
|
writeCache(cache);
|
||||||
};
|
};
|
||||||
|
|
||||||
var characteristicsFromCache = function() {
|
var characteristicsFromCache = function(device) {
|
||||||
|
var service = { device : device }; // fake a BluetoothRemoteGATTService
|
||||||
log("Read cached characteristics");
|
log("Read cached characteristics");
|
||||||
var cache = getCache();
|
var cache = getCache();
|
||||||
if (!cache.characteristics) return [];
|
if (!cache.characteristics) return [];
|
||||||
|
@ -75,6 +76,7 @@
|
||||||
r.properties = {};
|
r.properties = {};
|
||||||
r.properties.notify = cached.notify;
|
r.properties.notify = cached.notify;
|
||||||
r.properties.read = cached.read;
|
r.properties.read = cached.read;
|
||||||
|
r.service = service;
|
||||||
addNotificationHandler(r);
|
addNotificationHandler(r);
|
||||||
log("Restored characteristic: ", r);
|
log("Restored characteristic: ", r);
|
||||||
restored.push(r);
|
restored.push(r);
|
||||||
|
@ -92,13 +94,24 @@
|
||||||
"0x180f", // Battery
|
"0x180f", // Battery
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var bpmTimeout;
|
||||||
|
|
||||||
var supportedCharacteristics = {
|
var supportedCharacteristics = {
|
||||||
"0x2a37": {
|
"0x2a37": {
|
||||||
//Heart rate measurement
|
//Heart rate measurement
|
||||||
|
active: false,
|
||||||
handler: function (dv){
|
handler: function (dv){
|
||||||
var flags = dv.getUint8(0);
|
var flags = dv.getUint8(0);
|
||||||
|
|
||||||
var bpm = (flags & 1) ? (dv.getUint16(1) / 100 /* ? */ ) : dv.getUint8(1); // 8 or 16 bit
|
var bpm = (flags & 1) ? (dv.getUint16(1) / 100 /* ? */ ) : dv.getUint8(1); // 8 or 16 bit
|
||||||
|
supportedCharacteristics["0x2a37"].active = bpm > 0;
|
||||||
|
log("BTHRM BPM " + supportedCharacteristics["0x2a37"].active);
|
||||||
|
if (supportedCharacteristics["0x2a37"].active) stopFallback();
|
||||||
|
if (bpmTimeout) clearTimeout(bpmTimeout);
|
||||||
|
bpmTimeout = setTimeout(()=>{
|
||||||
|
supportedCharacteristics["0x2a37"].active = false;
|
||||||
|
startFallback();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
var sensorContact;
|
var sensorContact;
|
||||||
|
|
||||||
|
@ -141,8 +154,8 @@
|
||||||
src: "bthrm"
|
src: "bthrm"
|
||||||
};
|
};
|
||||||
|
|
||||||
log("Emitting HRM: ", repEvent);
|
log("Emitting HRM", repEvent);
|
||||||
Bangle.emit("HRM", repEvent);
|
Bangle.emit("HRM_int", repEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newEvent = {
|
var newEvent = {
|
||||||
|
@ -155,7 +168,7 @@
|
||||||
if (battery) newEvent.battery = battery;
|
if (battery) newEvent.battery = battery;
|
||||||
if (sensorContact) newEvent.contact = sensorContact;
|
if (sensorContact) newEvent.contact = sensorContact;
|
||||||
|
|
||||||
log("Emitting BTHRM: ", newEvent);
|
log("Emitting BTHRM", newEvent);
|
||||||
Bangle.emit("BTHRM", newEvent);
|
Bangle.emit("BTHRM", newEvent);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -200,6 +213,10 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
if (settings.enabled){
|
if (settings.enabled){
|
||||||
|
Bangle.isBTHRMActive = function (){
|
||||||
|
return supportedCharacteristics["0x2a37"].active;
|
||||||
|
};
|
||||||
|
|
||||||
Bangle.isBTHRMOn = function(){
|
Bangle.isBTHRMOn = function(){
|
||||||
return (Bangle._PWR && Bangle._PWR.BTHRM && Bangle._PWR.BTHRM.length > 0);
|
return (Bangle._PWR && Bangle._PWR.BTHRM && Bangle._PWR.BTHRM.length > 0);
|
||||||
};
|
};
|
||||||
|
@ -210,24 +227,28 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.replace){
|
if (settings.replace){
|
||||||
var origIsHRMOn = Bangle.isHRMOn;
|
Bangle.origIsHRMOn = Bangle.isHRMOn;
|
||||||
|
|
||||||
Bangle.isHRMOn = function() {
|
Bangle.isHRMOn = function() {
|
||||||
if (settings.enabled && !settings.replace){
|
if (settings.enabled && !settings.replace){
|
||||||
return origIsHRMOn();
|
return Bangle.origIsHRMOn();
|
||||||
} else if (settings.enabled && settings.replace){
|
} else if (settings.enabled && settings.replace){
|
||||||
return Bangle.isBTHRMOn();
|
return Bangle.isBTHRMOn();
|
||||||
}
|
}
|
||||||
return origIsHRMOn() || Bangle.isBTHRMOn();
|
return Bangle.origIsHRMOn() || Bangle.isBTHRMOn();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var clearRetryTimeout = function() {
|
var clearRetryTimeout = function(resetTime) {
|
||||||
if (currentRetryTimeout){
|
if (currentRetryTimeout){
|
||||||
log("Clearing timeout " + currentRetryTimeout);
|
log("Clearing timeout " + currentRetryTimeout);
|
||||||
clearTimeout(currentRetryTimeout);
|
clearTimeout(currentRetryTimeout);
|
||||||
currentRetryTimeout = undefined;
|
currentRetryTimeout = undefined;
|
||||||
}
|
}
|
||||||
|
if (resetTime) {
|
||||||
|
log("Resetting retry time");
|
||||||
|
retryTime = initialRetryTime;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var retry = function() {
|
var retry = function() {
|
||||||
|
@ -257,11 +278,11 @@
|
||||||
var buzzing = false;
|
var buzzing = false;
|
||||||
var onDisconnect = function(reason) {
|
var onDisconnect = function(reason) {
|
||||||
log("Disconnect: " + reason);
|
log("Disconnect: " + reason);
|
||||||
log("GATT: ", gatt);
|
log("GATT", gatt);
|
||||||
log("Characteristics: ", characteristics);
|
log("Characteristics", characteristics);
|
||||||
retryTime = initialRetryTime;
|
clearRetryTimeout(reason != "Connection Timeout");
|
||||||
clearRetryTimeout();
|
supportedCharacteristics["0x2a37"].active = false;
|
||||||
switchInternalHrm();
|
startFallback();
|
||||||
blockInit = false;
|
blockInit = false;
|
||||||
if (settings.warnDisconnect && !buzzing){
|
if (settings.warnDisconnect && !buzzing){
|
||||||
buzzing = true;
|
buzzing = true;
|
||||||
|
@ -273,13 +294,13 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var createCharacteristicPromise = function(newCharacteristic) {
|
var createCharacteristicPromise = function(newCharacteristic) {
|
||||||
log("Create characteristic promise: ", newCharacteristic);
|
log("Create characteristic promise", newCharacteristic);
|
||||||
var result = Promise.resolve();
|
var result = Promise.resolve();
|
||||||
// For values that can be read, go ahead and read them, even if we might be notified in the future
|
// For values that can be read, go ahead and read them, even if we might be notified in the future
|
||||||
// Allows for getting initial state of infrequently updating characteristics, like battery
|
// Allows for getting initial state of infrequently updating characteristics, like battery
|
||||||
if (newCharacteristic.readValue){
|
if (newCharacteristic.readValue){
|
||||||
result = result.then(()=>{
|
result = result.then(()=>{
|
||||||
log("Reading data for " + JSON.stringify(newCharacteristic));
|
log("Reading data", newCharacteristic);
|
||||||
return newCharacteristic.readValue().then((data)=>{
|
return newCharacteristic.readValue().then((data)=>{
|
||||||
if (supportedCharacteristics[newCharacteristic.uuid] && supportedCharacteristics[newCharacteristic.uuid].handler) {
|
if (supportedCharacteristics[newCharacteristic.uuid] && supportedCharacteristics[newCharacteristic.uuid].handler) {
|
||||||
supportedCharacteristics[newCharacteristic.uuid].handler(data);
|
supportedCharacteristics[newCharacteristic.uuid].handler(data);
|
||||||
|
@ -289,8 +310,8 @@
|
||||||
}
|
}
|
||||||
if (newCharacteristic.properties.notify){
|
if (newCharacteristic.properties.notify){
|
||||||
result = result.then(()=>{
|
result = result.then(()=>{
|
||||||
log("Starting notifications for: ", newCharacteristic);
|
log("Starting notifications", newCharacteristic);
|
||||||
var startPromise = newCharacteristic.startNotifications().then(()=>log("Notifications started for ", newCharacteristic));
|
var startPromise = newCharacteristic.startNotifications().then(()=>log("Notifications started", newCharacteristic));
|
||||||
if (settings.gracePeriodNotification > 0){
|
if (settings.gracePeriodNotification > 0){
|
||||||
log("Add " + settings.gracePeriodNotification + "ms grace period after starting notifications");
|
log("Add " + settings.gracePeriodNotification + "ms grace period after starting notifications");
|
||||||
startPromise = startPromise.then(()=>{
|
startPromise = startPromise.then(()=>{
|
||||||
|
@ -301,7 +322,7 @@
|
||||||
return startPromise;
|
return startPromise;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result.then(()=>log("Handled characteristic: ", newCharacteristic));
|
return result.then(()=>log("Handled characteristic", newCharacteristic));
|
||||||
};
|
};
|
||||||
|
|
||||||
var attachCharacteristicPromise = function(promise, characteristic) {
|
var attachCharacteristicPromise = function(promise, characteristic) {
|
||||||
|
@ -312,11 +333,11 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var createCharacteristicsPromise = function(newCharacteristics) {
|
var createCharacteristicsPromise = function(newCharacteristics) {
|
||||||
log("Create characteristics promise: ", newCharacteristics);
|
log("Create characteristics promis ", newCharacteristics);
|
||||||
var result = Promise.resolve();
|
var result = Promise.resolve();
|
||||||
for (var c of newCharacteristics){
|
for (var c of newCharacteristics){
|
||||||
if (!supportedCharacteristics[c.uuid]) continue;
|
if (!supportedCharacteristics[c.uuid]) continue;
|
||||||
log("Supporting characteristic: ", c);
|
log("Supporting characteristic", c);
|
||||||
characteristics.push(c);
|
characteristics.push(c);
|
||||||
if (c.properties.notify){
|
if (c.properties.notify){
|
||||||
addNotificationHandler(c);
|
addNotificationHandler(c);
|
||||||
|
@ -328,10 +349,10 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var createServicePromise = function(service) {
|
var createServicePromise = function(service) {
|
||||||
log("Create service promise: ", service);
|
log("Create service promise", service);
|
||||||
var result = Promise.resolve();
|
var result = Promise.resolve();
|
||||||
result = result.then(()=>{
|
result = result.then(()=>{
|
||||||
log("Handling service: " + service.uuid);
|
log("Handling service" + service.uuid);
|
||||||
return service.getCharacteristics().then((c)=>createCharacteristicsPromise(c));
|
return service.getCharacteristics().then((c)=>createCharacteristicsPromise(c));
|
||||||
});
|
});
|
||||||
return result.then(()=>log("Handled service" + service.uuid));
|
return result.then(()=>log("Handled service" + service.uuid));
|
||||||
|
@ -368,7 +389,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
promise = promise.then((d)=>{
|
promise = promise.then((d)=>{
|
||||||
log("Got device: ", d);
|
log("Got device", d);
|
||||||
d.on('gattserverdisconnected', onDisconnect);
|
d.on('gattserverdisconnected', onDisconnect);
|
||||||
device = d;
|
device = d;
|
||||||
});
|
});
|
||||||
|
@ -379,14 +400,14 @@
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
promise = Promise.resolve();
|
promise = Promise.resolve();
|
||||||
log("Reuse device: ", device);
|
log("Reuse device", device);
|
||||||
}
|
}
|
||||||
|
|
||||||
promise = promise.then(()=>{
|
promise = promise.then(()=>{
|
||||||
if (gatt){
|
if (gatt){
|
||||||
log("Reuse GATT: ", gatt);
|
log("Reuse GATT", gatt);
|
||||||
} else {
|
} else {
|
||||||
log("GATT is new: ", gatt);
|
log("GATT is new", gatt);
|
||||||
characteristics = [];
|
characteristics = [];
|
||||||
var cachedId = getCache().id;
|
var cachedId = getCache().id;
|
||||||
if (device.id !== cachedId){
|
if (device.id !== cachedId){
|
||||||
|
@ -404,7 +425,10 @@
|
||||||
|
|
||||||
promise = promise.then((gatt)=>{
|
promise = promise.then((gatt)=>{
|
||||||
if (!gatt.connected){
|
if (!gatt.connected){
|
||||||
var connectPromise = gatt.connect(connectSettings);
|
log("Connecting...");
|
||||||
|
var connectPromise = gatt.connect(connectSettings).then(function() {
|
||||||
|
log("Connected.");
|
||||||
|
});
|
||||||
if (settings.gracePeriodConnect > 0){
|
if (settings.gracePeriodConnect > 0){
|
||||||
log("Add " + settings.gracePeriodConnect + "ms grace period after connecting");
|
log("Add " + settings.gracePeriodConnect + "ms grace period after connecting");
|
||||||
connectPromise = connectPromise.then(()=>{
|
connectPromise = connectPromise.then(()=>{
|
||||||
|
@ -432,7 +456,7 @@
|
||||||
|
|
||||||
promise = promise.then(()=>{
|
promise = promise.then(()=>{
|
||||||
if (!characteristics || characteristics.length === 0){
|
if (!characteristics || characteristics.length === 0){
|
||||||
characteristics = characteristicsFromCache();
|
characteristics = characteristicsFromCache(device);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -445,11 +469,11 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
characteristicsPromise = characteristicsPromise.then((services)=>{
|
characteristicsPromise = characteristicsPromise.then((services)=>{
|
||||||
log("Got services:", services);
|
log("Got services", services);
|
||||||
var result = Promise.resolve();
|
var result = Promise.resolve();
|
||||||
for (var service of services){
|
for (var service of services){
|
||||||
if (!(supportedServices.includes(service.uuid))) continue;
|
if (!(supportedServices.includes(service.uuid))) continue;
|
||||||
log("Supporting service: ", service.uuid);
|
log("Supporting service", service.uuid);
|
||||||
result = attachServicePromise(result, service);
|
result = attachServicePromise(result, service);
|
||||||
}
|
}
|
||||||
if (settings.gracePeriodService > 0) {
|
if (settings.gracePeriodService > 0) {
|
||||||
|
@ -473,7 +497,7 @@
|
||||||
return promise.then(()=>{
|
return promise.then(()=>{
|
||||||
log("Connection established, waiting for notifications");
|
log("Connection established, waiting for notifications");
|
||||||
characteristicsToCache(characteristics);
|
characteristicsToCache(characteristics);
|
||||||
clearRetryTimeout();
|
clearRetryTimeout(true);
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
characteristics = [];
|
characteristics = [];
|
||||||
log("Error:", e);
|
log("Error:", e);
|
||||||
|
@ -491,12 +515,14 @@
|
||||||
isOn = Bangle._PWR.BTHRM.length;
|
isOn = Bangle._PWR.BTHRM.length;
|
||||||
// so now we know if we're really on
|
// so now we know if we're really on
|
||||||
if (isOn) {
|
if (isOn) {
|
||||||
|
switchFallback();
|
||||||
if (!Bangle.isBTHRMConnected()) initBt();
|
if (!Bangle.isBTHRMConnected()) initBt();
|
||||||
} else { // not on
|
} else { // not on
|
||||||
log("Power off for " + app);
|
log("Power off for " + app);
|
||||||
|
clearRetryTimeout(true);
|
||||||
if (gatt) {
|
if (gatt) {
|
||||||
if (gatt.connected){
|
if (gatt.connected){
|
||||||
log("Disconnect with gatt: ", gatt);
|
log("Disconnect with gatt", gatt);
|
||||||
try{
|
try{
|
||||||
gatt.disconnect().then(()=>{
|
gatt.disconnect().then(()=>{
|
||||||
log("Successful disconnect");
|
log("Successful disconnect");
|
||||||
|
@ -511,7 +537,33 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var origSetHRMPower = Bangle.setHRMPower;
|
if (settings.replace){
|
||||||
|
Bangle.on("HRM", (e) => {
|
||||||
|
e.modified = true;
|
||||||
|
Bangle.emit("HRM_int", e);
|
||||||
|
});
|
||||||
|
|
||||||
|
Bangle.origOn = Bangle.on;
|
||||||
|
Bangle.on = function(name, callback) {
|
||||||
|
if (name == "HRM") {
|
||||||
|
Bangle.origOn("HRM_int", callback);
|
||||||
|
} else {
|
||||||
|
Bangle.origOn(name, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Bangle.origRemoveListener = Bangle.removeListener;
|
||||||
|
Bangle.removeListener = function(name, callback) {
|
||||||
|
if (name == "HRM") {
|
||||||
|
Bangle.origRemoveListener("HRM_int", callback);
|
||||||
|
} else {
|
||||||
|
Bangle.origRemoveListener(name, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Bangle.origSetHRMPower = Bangle.setHRMPower;
|
||||||
|
|
||||||
if (settings.startWithHrm){
|
if (settings.startWithHrm){
|
||||||
|
|
||||||
|
@ -521,40 +573,54 @@
|
||||||
Bangle.setBTHRMPower(isOn, app);
|
Bangle.setBTHRMPower(isOn, app);
|
||||||
}
|
}
|
||||||
if ((settings.enabled && !settings.replace) || !settings.enabled){
|
if ((settings.enabled && !settings.replace) || !settings.enabled){
|
||||||
origSetHRMPower(isOn, app);
|
Bangle.origSetHRMPower(isOn, app);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var fallbackInterval;
|
var fallbackActive = false;
|
||||||
|
var inSwitch = false;
|
||||||
|
|
||||||
var switchInternalHrm = function() {
|
var stopFallback = function(){
|
||||||
if (settings.allowFallback && !fallbackInterval){
|
if (fallbackActive){
|
||||||
log("Fallback to HRM enabled");
|
Bangle.origSetHRMPower(0, "bthrm_fallback");
|
||||||
origSetHRMPower(1, "bthrm_fallback");
|
fallbackActive = false;
|
||||||
fallbackInterval = setInterval(()=>{
|
|
||||||
if (Bangle.isBTHRMConnected()){
|
|
||||||
origSetHRMPower(0, "bthrm_fallback");
|
|
||||||
clearInterval(fallbackInterval);
|
|
||||||
fallbackInterval = undefined;
|
|
||||||
log("Fallback to HRM disabled");
|
log("Fallback to HRM disabled");
|
||||||
}
|
}
|
||||||
}, settings.fallbackTimeout);
|
};
|
||||||
|
|
||||||
|
var startFallback = function(){
|
||||||
|
if (!fallbackActive && settings.allowFallback) {
|
||||||
|
fallbackActive = true;
|
||||||
|
Bangle.origSetHRMPower(1, "bthrm_fallback");
|
||||||
|
log("Fallback to HRM enabled");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var switchFallback = function() {
|
||||||
|
log("Check falling back to HRM");
|
||||||
|
if (!inSwitch){
|
||||||
|
inSwitch = true;
|
||||||
|
if (Bangle.isBTHRMActive()){
|
||||||
|
stopFallback();
|
||||||
|
} else {
|
||||||
|
startFallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inSwitch = false;
|
||||||
|
};
|
||||||
|
|
||||||
if (settings.replace){
|
if (settings.replace){
|
||||||
log("Replace HRM event");
|
log("Replace HRM event");
|
||||||
if (Bangle._PWR && Bangle._PWR.HRM){
|
if (Bangle._PWR && Bangle._PWR.HRM){
|
||||||
for (var i = 0; i < Bangle._PWR.HRM.length; i++){
|
for (var i = 0; i < Bangle._PWR.HRM.length; i++){
|
||||||
var app = Bangle._PWR.HRM[i];
|
var app = Bangle._PWR.HRM[i];
|
||||||
log("Moving app " + app);
|
log("Moving app " + app);
|
||||||
origSetHRMPower(0, app);
|
Bangle.origSetHRMPower(0, app);
|
||||||
Bangle.setBTHRMPower(1, app);
|
Bangle.setBTHRMPower(1, app);
|
||||||
if (Bangle._PWR.HRM===undefined) break;
|
if (Bangle._PWR.HRM===undefined) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switchInternalHrm();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
E.on("kill", ()=>{
|
E.on("kill", ()=>{
|
||||||
|
|
|
@ -42,12 +42,20 @@ function draw(y, type, event) {
|
||||||
if (event.energy) str += " kJoule: " + event.energy.toFixed(0);
|
if (event.energy) str += " kJoule: " + event.energy.toFixed(0);
|
||||||
g.setFontVector(12).drawString(str,px,y+60);
|
g.setFontVector(12).drawString(str,px,y+60);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstEventBt = true;
|
var firstEventBt = true;
|
||||||
var firstEventInt = true;
|
var firstEventInt = true;
|
||||||
|
|
||||||
|
|
||||||
|
// This can get called for the boot code to show what's happening
|
||||||
|
function showStatusInfo(txt) {
|
||||||
|
var R = Bangle.appRect;
|
||||||
|
g.reset().clearRect(R.x,R.y2-8,R.x2,R.y2).setFont("6x8");
|
||||||
|
txt = g.wrapString(txt, R.w)[0];
|
||||||
|
g.setFontAlign(0,1).drawString(txt, (R.x+R.x2)/2, R.y2);
|
||||||
|
}
|
||||||
|
|
||||||
function onBtHrm(e) {
|
function onBtHrm(e) {
|
||||||
if (firstEventBt){
|
if (firstEventBt){
|
||||||
clear(24);
|
clear(24);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "bthrm",
|
"id": "bthrm",
|
||||||
"name": "Bluetooth Heart Rate Monitor",
|
"name": "Bluetooth Heart Rate Monitor",
|
||||||
"shortName": "BT HRM",
|
"shortName": "BT HRM",
|
||||||
"version": "0.09",
|
"version": "0.12",
|
||||||
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
|
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
|
|
@ -32,8 +32,45 @@
|
||||||
Bangle.removeListener('BTHRM', onHRM);
|
Bangle.removeListener('BTHRM', onHRM);
|
||||||
if (Bangle.setBTRHMPower) Bangle.setBTHRMPower(0,"recorder");
|
if (Bangle.setBTRHMPower) Bangle.setBTHRMPower(0,"recorder");
|
||||||
},
|
},
|
||||||
draw : (x,y) => g.setColor((bpm != "")?"#00f":"#88f").drawImage(atob("DAwBAAAAMMeef+f+f+P8H4DwBgAA"),x,y)
|
draw : (x,y) => g.setColor((Bangle.isBTHRMActive && Bangle.isBTHRMActive())?"#00f":"#88f").drawImage(atob("DAwBAAAAMMeef+f+f+P8H4DwBgAA"),x,y)
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
recorders.hrmint = function() {
|
||||||
|
var active = false;
|
||||||
|
var bpmTimeout;
|
||||||
|
var bpm = "", bpmConfidence = "", src="";
|
||||||
|
function onHRM(h) {
|
||||||
|
bpmConfidence = h.confidence;
|
||||||
|
bpm = h.bpm;
|
||||||
|
srv = h.src;
|
||||||
|
if (h.bpm > 0){
|
||||||
|
active = true;
|
||||||
|
print("active" + h.bpm);
|
||||||
|
if (bpmTimeout) clearTimeout(bpmTimeout);
|
||||||
|
bpmTimeout = setTimeout(()=>{
|
||||||
|
print("inactive");
|
||||||
|
active = false;
|
||||||
|
},3000);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name : "HR int",
|
||||||
|
fields : ["Heartrate", "Confidence"],
|
||||||
|
getValues : () => {
|
||||||
|
var r = [bpm,bpmConfidence,src];
|
||||||
|
bpm = ""; bpmConfidence = ""; src="";
|
||||||
|
return r;
|
||||||
|
},
|
||||||
|
start : () => {
|
||||||
|
Bangle.origOn('HRM', onHRM);
|
||||||
|
if (Bangle.origSetHRMPower) Bangle.origSetHRMPower(1,"recorder");
|
||||||
|
},
|
||||||
|
stop : () => {
|
||||||
|
Bangle.removeListener('HRM', onHRM);
|
||||||
|
if (Bangle.origSetHRMPower) Bangle.origSetHRMPower(0,"recorder");
|
||||||
|
},
|
||||||
|
draw : (x,y) => g.setColor(( Bangle.origIsHRMOn && Bangle.origIsHRMOn() && active)?"#0f0":"#8f8").drawImage(atob("DAwBAAAAMMeef+f+f+P8H4DwBgAA"),x,y)
|
||||||
|
};
|
||||||
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,14 @@
|
||||||
var settings;
|
var settings;
|
||||||
readSettings();
|
readSettings();
|
||||||
|
|
||||||
|
function applyCustomSettings(){
|
||||||
|
writeSettings("enabled",true);
|
||||||
|
writeSettings("replace",settings.custom_replace);
|
||||||
|
writeSettings("startWithHrm",settings.custom_startWithHrm);
|
||||||
|
writeSettings("allowFallback",settings.custom_allowFallback);
|
||||||
|
writeSettings("fallbackTimeout",settings.custom_fallbackTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
function buildMainMenu(){
|
function buildMainMenu(){
|
||||||
var mainmenu = {
|
var mainmenu = {
|
||||||
'': { 'title': 'Bluetooth HRM' },
|
'': { 'title': 'Bluetooth HRM' },
|
||||||
|
@ -35,7 +43,6 @@
|
||||||
case 1:
|
case 1:
|
||||||
writeSettings("enabled",true);
|
writeSettings("enabled",true);
|
||||||
writeSettings("replace",true);
|
writeSettings("replace",true);
|
||||||
writeSettings("debuglog",false);
|
|
||||||
writeSettings("startWithHrm",true);
|
writeSettings("startWithHrm",true);
|
||||||
writeSettings("allowFallback",true);
|
writeSettings("allowFallback",true);
|
||||||
writeSettings("fallbackTimeout",10);
|
writeSettings("fallbackTimeout",10);
|
||||||
|
@ -43,17 +50,11 @@
|
||||||
case 2:
|
case 2:
|
||||||
writeSettings("enabled",true);
|
writeSettings("enabled",true);
|
||||||
writeSettings("replace",false);
|
writeSettings("replace",false);
|
||||||
writeSettings("debuglog",false);
|
|
||||||
writeSettings("startWithHrm",false);
|
writeSettings("startWithHrm",false);
|
||||||
writeSettings("allowFallback",false);
|
writeSettings("allowFallback",false);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
writeSettings("enabled",true);
|
applyCustomSettings();
|
||||||
writeSettings("replace",settings.custom_replace);
|
|
||||||
writeSettings("debuglog",settings.custom_debuglog);
|
|
||||||
writeSettings("startWithHrm",settings.custom_startWithHrm);
|
|
||||||
writeSettings("allowFallback",settings.custom_allowFallback);
|
|
||||||
writeSettings("fallbackTimeout",settings.custom_fallbackTimeout);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
writeSettings("mode",v);
|
writeSettings("mode",v);
|
||||||
|
@ -85,14 +86,12 @@
|
||||||
'< Back': function() { E.showMenu(buildMainMenu()); },
|
'< Back': function() { E.showMenu(buildMainMenu()); },
|
||||||
'Alert on disconnect': {
|
'Alert on disconnect': {
|
||||||
value: !!settings.warnDisconnect,
|
value: !!settings.warnDisconnect,
|
||||||
format: v => settings.warnDisconnect ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
writeSettings("warnDisconnect",v);
|
writeSettings("warnDisconnect",v);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Debug log': {
|
'Debug log': {
|
||||||
value: !!settings.debuglog,
|
value: !!settings.debuglog,
|
||||||
format: v => settings.debuglog ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
writeSettings("debuglog",v);
|
writeSettings("debuglog",v);
|
||||||
}
|
}
|
||||||
|
@ -140,23 +139,23 @@
|
||||||
'< Back': function() { E.showMenu(buildMainMenu()); },
|
'< Back': function() { E.showMenu(buildMainMenu()); },
|
||||||
'Replace HRM': {
|
'Replace HRM': {
|
||||||
value: !!settings.custom_replace,
|
value: !!settings.custom_replace,
|
||||||
format: v => settings.custom_replace ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
writeSettings("custom_replace",v);
|
writeSettings("custom_replace",v);
|
||||||
|
if (settings.mode == 3) applyCustomSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Start w. HRM': {
|
'Start w. HRM': {
|
||||||
value: !!settings.custom_startWithHrm,
|
value: !!settings.custom_startWithHrm,
|
||||||
format: v => settings.custom_startWithHrm ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
writeSettings("custom_startWithHrm",v);
|
writeSettings("custom_startWithHrm",v);
|
||||||
|
if (settings.mode == 3) applyCustomSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'HRM Fallback': {
|
'HRM Fallback': {
|
||||||
value: !!settings.custom_allowFallback,
|
value: !!settings.custom_allowFallback,
|
||||||
format: v => settings.custom_allowFallback ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
writeSettings("custom_allowFallback",v);
|
writeSettings("custom_allowFallback",v);
|
||||||
|
if (settings.mode == 3) applyCustomSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Fallback Timeout': {
|
'Fallback Timeout': {
|
||||||
|
@ -167,6 +166,7 @@
|
||||||
format: v=>v+"s",
|
format: v=>v+"s",
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
writeSettings("custom_fallbackTimout",v*1000);
|
writeSettings("custom_fallbackTimout",v*1000);
|
||||||
|
if (settings.mode == 3) applyCustomSettings();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,3 +7,7 @@
|
||||||
0.07: Improved positioning.
|
0.07: Improved positioning.
|
||||||
0.08: Select the color of widgets correctly. Additional settings to hide colon.
|
0.08: Select the color of widgets correctly. Additional settings to hide colon.
|
||||||
0.09: Larger font size if colon is hidden to improve readability further.
|
0.09: Larger font size if colon is hidden to improve readability further.
|
||||||
|
0.10: HomeAssistant integration if HomeAssistant is installed.
|
||||||
|
0.11: Performance improvements.
|
||||||
|
0.12: Implements a 2D menu.
|
||||||
|
0.13: Clicks < 24px are for widgets, if fullscreen mode is disabled.
|
|
@ -1,17 +1,46 @@
|
||||||
# BW Clock
|
# BW Clock
|
||||||
|
A very minimalistic clock with date and time in focus.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Fullscreen on/off
|
The BW clock provides many features as well as 3rd party integrations:
|
||||||
- Tab left/right of screen to show steps, temperature etc.
|
- Bangle data such as steps, heart rate, battery or charging state.
|
||||||
- Enable / disable lock icon in the settings.
|
- A timer can be set directly. *Requirement: Scheduler library*
|
||||||
- If the "sched" app is installed tab top / bottom of the screen to set the timer.
|
- Weather temperature as well as the wind speed can be shown. *Requirement: Weather app*
|
||||||
- The design is adapted to the theme of your bangle.
|
- HomeAssistant triggers can be executed directly. *Requirement: HomeAssistant app*
|
||||||
- The colon (e.g. 7:35 = 735) can be hidden now in the settings.
|
|
||||||
|
Note: If some apps are not installed (e.gt. weather app), then this menu item is hidden.
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
2D menu allows you to display lots of different data including data from 3rd party apps and it's also possible to control things e.g. to set a timer or send a HomeAssistant trigger.
|
||||||
|
|
||||||
|
Simply click left / right to go through the menu entries such as Bangle, Timer etc.
|
||||||
|
and click up/down to move into this sub-menu. You can then click in the middle of the screen
|
||||||
|
to e.g. send a trigger via HomeAssistant once you selected it.
|
||||||
|
|
||||||
|
```
|
||||||
|
+5min
|
||||||
|
|
|
||||||
|
Bangle -- Timer[Optional] -- Weather[Optional] -- HomeAssistant [Optional]
|
||||||
|
| | | |
|
||||||
|
Bpm -5min Temperature Trigger1
|
||||||
|
| | |
|
||||||
|
Steps ... ...
|
||||||
|
|
|
||||||
|
Battery
|
||||||
|
```
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
- Fullscreen on/off (widgets are still loaded).
|
||||||
|
- Enable/disable lock icon in the settings. Useful if fullscreen is on.
|
||||||
|
- The colon (e.g. 7:35 = 735) can be hidden in the settings for an even larger time font to improve readability further.
|
||||||
|
- There are no design settings, as your bangle sys settings are used.
|
||||||
|
|
||||||
|
|
||||||
## Thanks to
|
## Thanks to
|
||||||
<a href="https://www.flaticon.com/free-icons/" title="Icons">Icons created by Flaticon</a>
|
<a href="https://www.flaticon.com/free-icons/" title="Icons">Icons created by Flaticon</a>
|
||||||
|
|
||||||
|
|
||||||
## Creator
|
## Creator
|
||||||
- [David Peer](https://github.com/peerdavid)
|
[David Peer](https://github.com/peerdavid)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"id": "bwclk",
|
"id": "bwclk",
|
||||||
"name": "BW Clock",
|
"name": "BW Clock",
|
||||||
"version": "0.09",
|
"version": "0.13",
|
||||||
"description": "BW Clock.",
|
"description": "A very minimalistic clock with date and time in focus.",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot_2.png"}, {"url":"screenshot_3.png"}],
|
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot_2.png"}, {"url":"screenshot_3.png"}, {"url":"screenshot_4.png"}],
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
"tags": "clock",
|
"tags": "clock",
|
||||||
"supports": ["BANGLEJS2"],
|
"supports": ["BANGLEJS2"],
|
||||||
|
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
|
@ -8,3 +8,4 @@
|
||||||
0.08: Do not register as watch, manually start clock on button
|
0.08: Do not register as watch, manually start clock on button
|
||||||
read start of week from system settings
|
read start of week from system settings
|
||||||
0.09: Fix scope of let variables
|
0.09: Fix scope of let variables
|
||||||
|
0.10: Use default Bangle formatter for booleans
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "calendar",
|
"id": "calendar",
|
||||||
"name": "Calendar",
|
"name": "Calendar",
|
||||||
"version": "0.09",
|
"version": "0.10",
|
||||||
"description": "Simple calendar",
|
"description": "Simple calendar",
|
||||||
"icon": "calendar.png",
|
"icon": "calendar.png",
|
||||||
"screenshots": [{"url":"screenshot_calendar.png"}],
|
"screenshots": [{"url":"screenshot_calendar.png"}],
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
"< Back": () => back(),
|
"< Back": () => back(),
|
||||||
'B2 Colors': {
|
'B2 Colors': {
|
||||||
value: settings.ndColors,
|
value: settings.ndColors,
|
||||||
format: v => v ? "Yes" : "No",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.ndColors = v;
|
settings.ndColors = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
1.00: New App!
|
0.01: New App!
|
||||||
1.01: Use fractional numbers and scale the points to keep working consistently on whole screen
|
0.02: Use fractional numbers and scale the points to keep working consistently on whole screen
|
||||||
|
0.03: Use default Bangle formatter for booleans
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "Touchscreen Calibration",
|
"name": "Touchscreen Calibration",
|
||||||
"shortName":"Calibration",
|
"shortName":"Calibration",
|
||||||
"icon": "calibration.png",
|
"icon": "calibration.png",
|
||||||
"version":"1.01",
|
"version":"0.03",
|
||||||
"description": "A simple calibration app for the touchscreen",
|
"description": "A simple calibration app for the touchscreen",
|
||||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
"< Back" : () => back(),
|
"< Back" : () => back(),
|
||||||
'Active': {
|
'Active': {
|
||||||
value: !!settings.active,
|
value: !!settings.active,
|
||||||
format: v => v? "On":"Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.active = v;
|
settings.active = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
|
|
@ -4,3 +4,4 @@
|
||||||
0.04: Change to 7 segment font, move to top widget bar
|
0.04: Change to 7 segment font, move to top widget bar
|
||||||
Better auto-update behaviour, less RAM used
|
Better auto-update behaviour, less RAM used
|
||||||
0.05: Fix error running app on new firmwares (fix #1140)
|
0.05: Fix error running app on new firmwares (fix #1140)
|
||||||
|
0.06: Use default Bangle formatter for booleans
|
||||||
|
|
|
@ -79,7 +79,6 @@ function showMenu() {
|
||||||
},
|
},
|
||||||
'Timer on': {
|
'Timer on': {
|
||||||
value: settingsChronowid.started,
|
value: settingsChronowid.started,
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settingsChronowid.started = v;
|
settingsChronowid.started = v;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "chronowid",
|
"id": "chronowid",
|
||||||
"name": "Chrono Widget",
|
"name": "Chrono Widget",
|
||||||
"shortName": "Chrono Widget",
|
"shortName": "Chrono Widget",
|
||||||
"version": "0.05",
|
"version": "0.06",
|
||||||
"description": "Chronometer (timer) which runs as widget.",
|
"description": "Chronometer (timer) which runs as widget.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,widget",
|
"tags": "tool,widget",
|
||||||
|
|
|
@ -24,3 +24,5 @@
|
||||||
Improve performance, reduce memory usage
|
Improve performance, reduce memory usage
|
||||||
Small optical adjustments
|
Small optical adjustments
|
||||||
0.12: Allow configuration of update interval
|
0.12: Allow configuration of update interval
|
||||||
|
0.13: Load step goal from Bangle health app as fallback
|
||||||
|
Memory optimizations
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
const locale = require("locale");
|
const locale = require("locale");
|
||||||
const storage = require("Storage");
|
const storage = require("Storage");
|
||||||
const SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
|
|
||||||
|
|
||||||
const shoesIcon = atob("EBCBAAAACAAcAB4AHgAeABwwADgGeAZ4AHgAMAAAAHAAIAAA");
|
|
||||||
const temperatureIcon = atob("EBCBAAAAAYADwAJAAkADwAPAA8ADwAfgB+AH4AfgA8ABgAAA");
|
|
||||||
|
|
||||||
Graphics.prototype.setFontRobotoRegular50NumericOnly = function(scale) {
|
Graphics.prototype.setFontRobotoRegular50NumericOnly = function(scale) {
|
||||||
// Actual height 39 (40 - 2)
|
// Actual height 39 (40 - 2)
|
||||||
this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAB8AAAAAAAfAAAAAAAPwAAAAAAB8AAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAA4AAAAAAB+AAAAAAD/gAAAAAD/4AAAAAH/4AAAAAP/wAAAAAP/gAAAAAf/gAAAAAf/AAAAAA/+AAAAAB/+AAAAAB/8AAAAAD/4AAAAAH/4AAAAAD/wAAAAAA/wAAAAAAPgAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///wAAAB////gAAA////8AAA/////gAAP////8AAH8AAA/gAB8AAAD4AA+AAAAfAAPAAAADwADwAAAA8AA8AAAAPAAPAAAADwADwAAAA8AA8AAAAPAAPgAAAHwAB8AAAD4AAfwAAD+AAD/////AAA/////wAAH////4AAAf///4AAAB///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAPgAAAAAADwAAAAAAB8AAAAAAAfAAAAAAAHgAAAAAAD4AAAAAAA+AAAAAAAPAAAAAAAH/////wAB/////8AA//////AAP/////wAD/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAAAfgAADwAAP4AAB8AAH+AAA/AAD/gAAfwAB/AAAf8AAfAAAP/AAPgAAH7wAD4AAD88AA8AAB+PAAPAAA/DwADwAAfg8AA8AAPwPAAPAAH4DwADwAH8A8AA+AD+APAAPwB/ADwAB/D/gA8AAf//gAPAAD//wADwAAf/wAA8AAD/4AAPAAAHwAADwAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAADgAAAHwAA+AAAD8AAP4AAB/AAD/AAA/wAA/wAAf4AAD+AAHwAAAPgAD4APAB8AA+ADwAPAAPAA8ADwADwAPAA8AA8ADwAPAAPAA8ADwADwAfAA8AA8AH4APAAPgD+AHwAB8B/wD4AAf7/+B+AAD//v//AAA//x//wAAD/4P/4AAAf8B/4AAAAYAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAHwAAAAAAH8AAAAAAD/AAAAAAD/wAAAAAD/8AAAAAB/vAAAAAB/jwAAAAA/g8AAAAA/wPAAAAAfwDwAAAAf4A8AAAAf4APAAAAP8ADwAAAP8AA8AAAH8AAPAAAD/////8AA//////AAP/////wAD/////8AA//////AAAAAAPAAAAAAADwAAAAAAA8AAAAAAAPAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAB/APwAAH//wD+AAD//8A/wAA///AH+AAP//wAPgAD/B4AB8AA8A+AAfAAPAPAADwADwDwAA8AA8A8AAPAAPAPAADwADwD4AA8AA8A+AAPAAPAPwAHwADwD8AD4AA8AfwD+AAPAH///AADwA///wAA8AH//4AAPAAf/4AAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAD//+AAAAD///4AAAD////AAAB////4AAA/78D/AAAfw8AH4AAPweAA+AAD4PgAHwAB8DwAA8AAfA8AAPAAHgPAADwAD4DwAA8AA+A8AAPAAPAPgAHwADwD4AB8AA8AfgA+AAPAH+B/gAAAA///wAAAAH//4AAAAA//8AAAAAH/8AAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAAAA8AAAAAAAPAAAAAAADwAAAAAAA8AAAABAAPAAAABwADwAAAB8AA8AAAB/AAPAAAB/wADwAAD/8AA8AAD/8AAPAAD/4AADwAD/4AAA8AD/4AAAPAH/wAAADwH/wAAAA8H/wAAAAPH/wAAAAD3/gAAAAA//gAAAAAP/gAAAAAD/gAAAAAA/AAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwA/4AAAH/Af/AAAH/8P/4AAD//n//AAA//7//4AAfx/+A+AAHwD+AHwAD4AfgB8AA8AHwAPAAPAA8ADwADwAPAA8AA8ADwAPAAPAA8ADwADwAfAA8AA+AH4AfAAHwD+AHwAB/D/4D4AAP/+/n+AAD//n//AAAf/w//gAAB/wH/wAAAHwA/4AAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAD/8AAAAAD//wAAAAB//+AAAAA///wAAAAf4H+APAAH4AfgDwAD8AB8A8AA+AAfAPAAPAADwDwADwAA8B8AA8AAPAfAAPAADwHgADwAA8D4AA+AAeB+AAHwAHg/AAB+ADwfgAAP8D4/4AAD////8AAAf///8AAAB///+AAAAP//+AAAAAP/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAOAAAB8AAHwAAAfgAD8AAAH4AA/AAAB8AAHwAAAOAAA4AAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("DRUcHBwcHBwcHBwcDA=="), 50+(scale<<8)+(1<<16));
|
this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAB8AAAAAAAfAAAAAAAPwAAAAAAB8AAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAA4AAAAAAB+AAAAAAD/gAAAAAD/4AAAAAH/4AAAAAP/wAAAAAP/gAAAAAf/gAAAAAf/AAAAAA/+AAAAAB/+AAAAAB/8AAAAAD/4AAAAAH/4AAAAAD/wAAAAAA/wAAAAAAPgAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///wAAAB////gAAA////8AAA/////gAAP////8AAH8AAA/gAB8AAAD4AA+AAAAfAAPAAAADwADwAAAA8AA8AAAAPAAPAAAADwADwAAAA8AA8AAAAPAAPgAAAHwAB8AAAD4AAfwAAD+AAD/////AAA/////wAAH////4AAAf///4AAAB///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAPgAAAAAADwAAAAAAB8AAAAAAAfAAAAAAAHgAAAAAAD4AAAAAAA+AAAAAAAPAAAAAAAH/////wAB/////8AA//////AAP/////wAD/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAAAfgAADwAAP4AAB8AAH+AAA/AAD/gAAfwAB/AAAf8AAfAAAP/AAPgAAH7wAD4AAD88AA8AAB+PAAPAAA/DwADwAAfg8AA8AAPwPAAPAAH4DwADwAH8A8AA+AD+APAAPwB/ADwAB/D/gA8AAf//gAPAAD//wADwAAf/wAA8AAD/4AAPAAAHwAADwAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAADgAAAHwAA+AAAD8AAP4AAB/AAD/AAA/wAA/wAAf4AAD+AAHwAAAPgAD4APAB8AA+ADwAPAAPAA8ADwADwAPAA8AA8ADwAPAAPAA8ADwADwAfAA8AA8AH4APAAPgD+AHwAB8B/wD4AAf7/+B+AAD//v//AAA//x//wAAD/4P/4AAAf8B/4AAAAYAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAHwAAAAAAH8AAAAAAD/AAAAAAD/wAAAAAD/8AAAAAB/vAAAAAB/jwAAAAA/g8AAAAA/wPAAAAAfwDwAAAAf4A8AAAAf4APAAAAP8ADwAAAP8AA8AAAH8AAPAAAD/////8AA//////AAP/////wAD/////8AA//////AAAAAAPAAAAAAADwAAAAAAA8AAAAAAAPAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAB/APwAAH//wD+AAD//8A/wAA///AH+AAP//wAPgAD/B4AB8AA8A+AAfAAPAPAADwADwDwAA8AA8A8AAPAAPAPAADwADwD4AA8AA8A+AAPAAPAPwAHwADwD8AD4AA8AfwD+AAPAH///AADwA///wAA8AH//4AAPAAf/4AAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAD//+AAAAD///4AAAD////AAAB////4AAA/78D/AAAfw8AH4AAPweAA+AAD4PgAHwAB8DwAA8AAfA8AAPAAHgPAADwAD4DwAA8AA+A8AAPAAPAPgAHwADwD4AB8AA8AfgA+AAPAH+B/gAAAA///wAAAAH//4AAAAA//8AAAAAH/8AAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAAAA8AAAAAAAPAAAAAAADwAAAAAAA8AAAABAAPAAAABwADwAAAB8AA8AAAB/AAPAAAB/wADwAAD/8AA8AAD/8AAPAAD/4AADwAD/4AAA8AD/4AAAPAH/wAAADwH/wAAAA8H/wAAAAPH/wAAAAD3/gAAAAA//gAAAAAP/gAAAAAD/gAAAAAA/AAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwA/4AAAH/Af/AAAH/8P/4AAD//n//AAA//7//4AAfx/+A+AAHwD+AHwAD4AfgB8AA8AHwAPAAPAA8ADwADwAPAA8AA8ADwAPAAPAA8ADwADwAfAA8AA+AH4AfAAHwD+AHwAB/D/4D4AAP/+/n+AAD//n//AAAf/w//gAAB/wH/wAAAHwA/4AAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAD/8AAAAAD//wAAAAB//+AAAAA///wAAAAf4H+APAAH4AfgDwAD8AB8A8AA+AAfAPAAPAADwDwADwAA8B8AA8AAPAfAAPAADwHgADwAA8D4AA+AAeB+AAHwAHg/AAB+ADwfgAAP8D4/4AAD////8AAAf///8AAAB///+AAAAP//+AAAAAP/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAOAAAB8AAHwAAAfgAD8AAAH4AA/AAAB8AAHwAAAOAAA4AAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("DRUcHBwcHBwcHBwcDA=="), 50+(scale<<8)+(1<<16));
|
||||||
|
@ -22,11 +17,17 @@ let settings = Object.assign(
|
||||||
storage.readJSON("circlesclock.default.json", true) || {},
|
storage.readJSON("circlesclock.default.json", true) || {},
|
||||||
storage.readJSON(SETTINGS_FILE, true) || {}
|
storage.readJSON(SETTINGS_FILE, true) || {}
|
||||||
);
|
);
|
||||||
// Load step goal from pedometer widget as fallback
|
|
||||||
|
// Load step goal from health app and pedometer widget as fallback
|
||||||
if (settings.stepGoal == undefined) {
|
if (settings.stepGoal == undefined) {
|
||||||
const d = storage.readJSON("wpedom.json", true) || {};
|
let d = storage.readJSON("health.json", true) || {};
|
||||||
|
settings.stepGoal = d != undefined && d.settings != undefined ? d.settings.stepGoal : undefined;
|
||||||
|
|
||||||
|
if (settings.stepGoal == undefined) {
|
||||||
|
d = storage.readJSON("wpedom.json", true) || {};
|
||||||
settings.stepGoal = d != undefined && d.settings != undefined ? d.settings.goal : 10000;
|
settings.stepGoal = d != undefined && d.settings != undefined ? d.settings.goal : 10000;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read location from myLocation app
|
* Read location from myLocation app
|
||||||
|
@ -126,19 +127,10 @@ function draw() {
|
||||||
g.drawString(locale.date(new Date()), w / 2, h2);
|
g.drawString(locale.date(new Date()), w / 2, h2);
|
||||||
g.drawString(locale.dow(new Date()), w / 2, h2 + dowOffset);
|
g.drawString(locale.dow(new Date()), w / 2, h2 + dowOffset);
|
||||||
|
|
||||||
// draw the circles a little bit delayed so we decrease the blocking time
|
|
||||||
setTimeout(function() {
|
|
||||||
drawCircle(1);
|
drawCircle(1);
|
||||||
}, 1);
|
|
||||||
setTimeout(function() {
|
|
||||||
drawCircle(2);
|
drawCircle(2);
|
||||||
}, 1);
|
|
||||||
setTimeout(function() {
|
|
||||||
drawCircle(3);
|
drawCircle(3);
|
||||||
}, 1);
|
|
||||||
setTimeout(function() {
|
|
||||||
if (circleCount >= 4) drawCircle(4);
|
if (circleCount >= 4) drawCircle(4);
|
||||||
}, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawCircle(index) {
|
function drawCircle(index) {
|
||||||
|
@ -294,7 +286,7 @@ function drawSteps(w) {
|
||||||
|
|
||||||
writeCircleText(w, shortValue(steps));
|
writeCircleText(w, shortValue(steps));
|
||||||
|
|
||||||
g.drawImage(getImage(shoesIcon, getCircleIconColor("steps", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
g.drawImage(getImage(atob("EBCBAAAACAAcAB4AHgAeABwwADgGeAZ4AHgAMAAAAHAAIAAA"), getCircleIconColor("steps", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawStepsDistance(w) {
|
function drawStepsDistance(w) {
|
||||||
|
@ -319,7 +311,7 @@ function drawStepsDistance(w) {
|
||||||
|
|
||||||
writeCircleText(w, shortValue(stepsDistance));
|
writeCircleText(w, shortValue(stepsDistance));
|
||||||
|
|
||||||
g.drawImage(getImage(shoesIcon, getCircleIconColor("stepsDistance", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
g.drawImage(getImage(atob("EBCBAAAACAAcAB4AHgAeABwwADgGeAZ4AHgAMAAAAHAAIAAA"), getCircleIconColor("stepsDistance", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawHeartRate(w) {
|
function drawHeartRate(w) {
|
||||||
|
@ -491,7 +483,7 @@ function drawTemperature(w) {
|
||||||
if (temperature)
|
if (temperature)
|
||||||
writeCircleText(w, locale.temp(temperature));
|
writeCircleText(w, locale.temp(temperature));
|
||||||
|
|
||||||
g.drawImage(getImage(temperatureIcon, getCircleIconColor("temperature", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
g.drawImage(getImage(atob("EBCBAAAAAYADwAJAAkADwAPAA8ADwAfgB+AH4AfgA8ABgAAA"), getCircleIconColor("temperature", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -517,7 +509,7 @@ function drawPressure(w) {
|
||||||
if (pressure)
|
if (pressure)
|
||||||
writeCircleText(w, Math.round(pressure));
|
writeCircleText(w, Math.round(pressure));
|
||||||
|
|
||||||
g.drawImage(getImage(temperatureIcon, getCircleIconColor("pressure", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
g.drawImage(getImage(atob("EBCBAAAAAYADwAJAAkADwAPAA8ADwAfgB+AH4AfgA8ABgAAA"), getCircleIconColor("pressure", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -543,7 +535,7 @@ function drawAltitude(w) {
|
||||||
if (altitude)
|
if (altitude)
|
||||||
writeCircleText(w, locale.distance(Math.round(altitude)));
|
writeCircleText(w, locale.distance(Math.round(altitude)));
|
||||||
|
|
||||||
g.drawImage(getImage(temperatureIcon, getCircleIconColor("altitude", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
g.drawImage(getImage(atob("EBCBAAAAAYADwAJAAkADwAPAA8ADwAfgB+AH4AfgA8ABgAAA"), getCircleIconColor("altitude", color, percent)), w - iconOffset, h3 + radiusOuter - iconOffset);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -641,6 +633,7 @@ function formatSeconds(s) {
|
||||||
|
|
||||||
function getSunData() {
|
function getSunData() {
|
||||||
if (location != undefined && location.lat != undefined) {
|
if (location != undefined && location.lat != undefined) {
|
||||||
|
const SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
|
||||||
// get today's sunlight times for lat/lon
|
// get today's sunlight times for lat/lon
|
||||||
return SunCalc ? SunCalc.getTimes(new Date(), location.lat, location.lon) : undefined;
|
return SunCalc ? SunCalc.getTimes(new Date(), location.lat, location.lon) : undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "circlesclock",
|
{ "id": "circlesclock",
|
||||||
"name": "Circles clock",
|
"name": "Circles clock",
|
||||||
"shortName":"Circles clock",
|
"shortName":"Circles clock",
|
||||||
"version":"0.12",
|
"version":"0.13",
|
||||||
"description": "A clock with three or four circles for different data at the bottom in a probably familiar style",
|
"description": "A clock with three or four circles for different data at the bottom in a probably familiar style",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}],
|
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}],
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: Initial upload
|
0.01: Initial upload
|
||||||
0.02: Added scrollable calendar and swipe gestures
|
0.02: Added scrollable calendar and swipe gestures
|
||||||
0.03: Configurable drag gestures
|
0.03: Configurable drag gestures
|
||||||
|
0.04: Use default Bangle formatter for booleans
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "clockcal",
|
"id": "clockcal",
|
||||||
"name": "Clock & Calendar",
|
"name": "Clock & Calendar",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "Clock with Calendar",
|
"description": "Clock with Calendar",
|
||||||
"readme":"README.md",
|
"readme":"README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
"< Back": () => back(),
|
"< Back": () => back(),
|
||||||
'Buzz(dis)conn.?': {
|
'Buzz(dis)conn.?': {
|
||||||
value: settings.BUZZ_ON_BT,
|
value: settings.BUZZ_ON_BT,
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.BUZZ_ON_BT = v;
|
settings.BUZZ_ON_BT = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -59,7 +58,6 @@
|
||||||
},
|
},
|
||||||
'Red Saturday?': {
|
'Red Saturday?': {
|
||||||
value: settings.REDSAT,
|
value: settings.REDSAT,
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.REDSAT = v;
|
settings.REDSAT = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
@ -67,7 +65,6 @@
|
||||||
},
|
},
|
||||||
'Red Sunday?': {
|
'Red Sunday?': {
|
||||||
value: settings.REDSUN,
|
value: settings.REDSUN,
|
||||||
format: v => v ? "On" : "Off",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.REDSUN = v;
|
settings.REDSUN = v;
|
||||||
writeSettings();
|
writeSettings();
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
...
|
||||||
|
0.03: First update with ChangeLog Added
|
|
@ -0,0 +1,2 @@
|
||||||
|
...
|
||||||
|
0.02: First update with ChangeLog Added
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: New app
|
0.01: New app
|
||||||
0.02: Cleanup interface and add settings, widget, add skin temp reporting.
|
0.02: Cleanup interface and add settings, widget, add skin temp reporting.
|
||||||
0.03: Move code for recording to this app
|
0.03: Move code for recording to this app
|
||||||
|
0.04: Use default Bangle formatter for booleans
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "coretemp",
|
"id": "coretemp",
|
||||||
"name": "CoreTemp",
|
"name": "CoreTemp",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "Display CoreTemp device sensor data",
|
"description": "Display CoreTemp device sensor data",
|
||||||
"icon": "coretemp.png",
|
"icon": "coretemp.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
|
|
@ -35,7 +35,6 @@ const menu = {
|
||||||
'< Back' : back,
|
'< Back' : back,
|
||||||
'Enabled' : {
|
'Enabled' : {
|
||||||
value : !!s.enabled,
|
value : !!s.enabled,
|
||||||
format : v => v ? "Yes" : "No",
|
|
||||||
onchange : v => {
|
onchange : v => {
|
||||||
s.enabled = v;
|
s.enabled = v;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Added decrement and touch functions
|
0.02: Added decrement and touch functions
|
||||||
0.03: Set color - ensures widgets don't end up coloring the counter's text
|
0.03: Set color - ensures widgets don't end up coloring the counter's text
|
||||||
|
0.04: Adopted for BangleJS 2
|
||||||
|
|
|
@ -1,15 +1,67 @@
|
||||||
var counter = 0;
|
var counter = 0;
|
||||||
|
const BANGLEJS2 = process.env.HWVERSION == 2;
|
||||||
|
|
||||||
|
if (BANGLEJS2) {
|
||||||
|
var drag;
|
||||||
|
var y = 45;
|
||||||
|
var x = 5;
|
||||||
|
} else {
|
||||||
|
var y = 100;
|
||||||
|
var x = 25;
|
||||||
|
}
|
||||||
|
|
||||||
function updateScreen() {
|
function updateScreen() {
|
||||||
|
if (BANGLEJS2) {
|
||||||
|
g.clearRect(0, 50, 250, 130);
|
||||||
|
} else {
|
||||||
g.clearRect(0, 50, 250, 150);
|
g.clearRect(0, 50, 250, 150);
|
||||||
g.setColor(0xFFFF);
|
}
|
||||||
|
g.setBgColor(g.theme.bg).setColor(g.theme.fg);
|
||||||
g.setFont("Vector",40).setFontAlign(0,0);
|
g.setFont("Vector",40).setFontAlign(0,0);
|
||||||
g.drawString(Math.floor(counter), g.getWidth()/2, 100);
|
g.drawString(Math.floor(counter), g.getWidth()/2, 100);
|
||||||
|
if (!BANGLEJS2) {
|
||||||
g.drawString('-', 45, 100);
|
g.drawString('-', 45, 100);
|
||||||
g.drawString('+', 185, 100);
|
g.drawString('+', 185, 100);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (BANGLEJS2) {
|
||||||
|
setWatch(() => {
|
||||||
|
counter = 0;
|
||||||
|
updateScreen();
|
||||||
|
}, BTN1, {repeat:true});
|
||||||
|
Bangle.on("drag", e => {
|
||||||
|
if (!drag) { // start dragging
|
||||||
|
drag = {x: e.x, y: e.y};
|
||||||
|
} else if (!e.b) { // released
|
||||||
|
const dx = e.x-drag.x, dy = e.y-drag.y;
|
||||||
|
drag = null;
|
||||||
|
if (Math.abs(dx)>Math.abs(dy)+10) {
|
||||||
|
// horizontal
|
||||||
|
if (dx < dy) {
|
||||||
|
//console.log("left " + dx + " " + dy);
|
||||||
|
} else {
|
||||||
|
//console.log("right " + dx + " " + dy);
|
||||||
|
}
|
||||||
|
} else if (Math.abs(dy)>Math.abs(dx)+10) {
|
||||||
|
// vertical
|
||||||
|
if (dx < dy) {
|
||||||
|
//console.log("down " + dx + " " + dy);
|
||||||
|
if (counter > 0) counter -= 1;
|
||||||
|
updateScreen();
|
||||||
|
} else {
|
||||||
|
//console.log("up " + dx + " " + dy);
|
||||||
|
counter += 1;
|
||||||
|
updateScreen();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//console.log("tap " + e.x + " " + e.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
|
||||||
// add a count by using BTN1 or BTN5
|
// add a count by using BTN1 or BTN5
|
||||||
setWatch(() => {
|
setWatch(() => {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
|
@ -23,12 +75,12 @@ setWatch(() => {
|
||||||
|
|
||||||
// subtract a count by using BTN3 or BTN4
|
// subtract a count by using BTN3 or BTN4
|
||||||
setWatch(() => {
|
setWatch(() => {
|
||||||
counter -= 1;
|
if (counter > 0) counter -= 1;
|
||||||
updateScreen();
|
updateScreen();
|
||||||
}, BTN4, {repeat:true});
|
}, BTN4, {repeat:true});
|
||||||
|
|
||||||
setWatch(() => {
|
setWatch(() => {
|
||||||
counter -= 1;
|
if (counter > 0) counter -= 1;
|
||||||
updateScreen();
|
updateScreen();
|
||||||
}, BTN3, {repeat:true});
|
}, BTN3, {repeat:true});
|
||||||
|
|
||||||
|
@ -37,9 +89,16 @@ setWatch(() => {
|
||||||
counter = 0;
|
counter = 0;
|
||||||
updateScreen();
|
updateScreen();
|
||||||
}, BTN2, {repeat:true});
|
}, BTN2, {repeat:true});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
g.clear(1).setFont("6x8");
|
g.clear(1).setFont("6x8");
|
||||||
g.drawString('Tap right or BTN1 to increase\nTap left or BTN3 to decrease\nPress BTN2 to reset.', 25, 200);
|
g.setBgColor(g.theme.bg).setColor(g.theme.fg);
|
||||||
|
if (BANGLEJS2) {
|
||||||
|
g.drawString('Swipe up to increase\nSwipe down to decrease\nPress button to reset.', x, 100 + y);
|
||||||
|
} else {
|
||||||
|
g.drawString('Tap right or BTN1 to increase\nTap left or BTN3 to decrease\nPress BTN2 to reset.', x, 100 + y);
|
||||||
|
}
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"id": "counter",
|
"id": "counter",
|
||||||
"name": "Counter",
|
"name": "Counter",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "Simple counter",
|
"description": "Simple counter",
|
||||||
"icon": "counter_icon.png",
|
"icon": "counter_icon.png",
|
||||||
"tags": "tool",
|
"tags": "tool",
|
||||||
"supports": ["BANGLEJS"],
|
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||||
"screenshots": [{"url":"bangle1-counter-screenshot.png"}],
|
"screenshots": [{"url":"bangle1-counter-screenshot.png"}],
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
"storage": [
|
"storage": [
|
||||||
|
|
|
@ -4,3 +4,4 @@
|
||||||
0.04: added heart rate which is switched on when cycled to it through up/down touch on rhs
|
0.04: added heart rate which is switched on when cycled to it through up/down touch on rhs
|
||||||
0.05: changed text to uppercase, just looks better, removed colons on text
|
0.05: changed text to uppercase, just looks better, removed colons on text
|
||||||
0.06: better contrast for light theme, use fg color instead of dithered for ring
|
0.06: better contrast for light theme, use fg color instead of dithered for ring
|
||||||
|
0.07: Use default Bangle formatter for booleans
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "daisy",
|
{ "id": "daisy",
|
||||||
"name": "Daisy",
|
"name": "Daisy",
|
||||||
"version":"0.06",
|
"version":"0.07",
|
||||||
"dependencies": {"mylocation":"app"},
|
"dependencies": {"mylocation":"app"},
|
||||||
"description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times",
|
"description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
},
|
},
|
||||||
'Idle Warning': {
|
'Idle Warning': {
|
||||||
value: !!s.idle_check,
|
value: !!s.idle_check,
|
||||||
format: v => v ? /*LANG*/"Yes":/*LANG*/"No",
|
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
s.idle_check = v;
|
s.idle_check = v;
|
||||||
save();
|
save();
|
||||||
|
|
|
@ -5,3 +5,4 @@
|
||||||
0.05: Add icon
|
0.05: Add icon
|
||||||
0.06: remove app image as it is unused
|
0.06: remove app image as it is unused
|
||||||
0.07: Bump version number for change to apps.json causing 404 on upload
|
0.07: Bump version number for change to apps.json causing 404 on upload
|
||||||
|
0.08: Use default Bangle formatter for booleans
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "dane_tcr",
|
"id": "dane_tcr",
|
||||||
"name": "DANE Touch Launcher",
|
"name": "DANE Touch Launcher",
|
||||||
"shortName": "DANE Toucher",
|
"shortName": "DANE Toucher",
|
||||||
"version": "0.07",
|
"version": "0.08",
|
||||||
"description": "Touch enable left to right launcher in the style of the DANE Watchface",
|
"description": "Touch enable left to right launcher in the style of the DANE Watchface",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "launch",
|
"type": "launch",
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
},
|
},
|
||||||
"Animation" : {
|
"Animation" : {
|
||||||
value : settings.animation,
|
value : settings.animation,
|
||||||
format : v => v?"On":"Off",
|
|
||||||
onchange : saveChange('animation')
|
onchange : saveChange('animation')
|
||||||
},
|
},
|
||||||
"Frame rate" : {
|
"Frame rate" : {
|
||||||
|
@ -51,7 +50,6 @@
|
||||||
},
|
},
|
||||||
"Debug" : {
|
"Debug" : {
|
||||||
value : settings.debug,
|
value : settings.debug,
|
||||||
format : v => v?"On":"Off",
|
|
||||||
onchange : saveChange('debug')
|
onchange : saveChange('debug')
|
||||||
},
|
},
|
||||||
'< Back': back
|
'< Back': back
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
0.10: Initial release - still work in progress
|
||||||
|
0.15: Added settings and calculations
|
||||||
|
0.20: Added status saving
|
||||||
|
0.25: Adopted for Bangle.js 1 - kind of
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Drink Counter
|
||||||
|
|
||||||
|
Counts drinks you had for science. Calculates BAC.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Swipe left/right to select drink. Swipe up/down to add/remove drinks.
|
||||||
|
|
||||||
|
## Important notes
|
||||||
|
|
||||||
|
No warranty whatsoever. Use at your own risk. Calculations might be wrong. Do not drink and drive - even if BAC is low.
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
|
||||||
|
Hank - contact at http://forum.espruino.com
|
|
@ -0,0 +1,291 @@
|
||||||
|
g.reset().clear();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
require("Font8x16").add(Graphics);
|
||||||
|
|
||||||
|
const BANGLEJS2 = process.env.HWVERSION == 2;
|
||||||
|
const SETTINGSFILE = "drinkcounter.json";
|
||||||
|
setting = require("Storage").readJSON("setting.json",1);
|
||||||
|
E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ
|
||||||
|
var _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false;
|
||||||
|
var ampm = "AM";
|
||||||
|
let drag;
|
||||||
|
|
||||||
|
var icoBeer = require("heatshrink").decompress(atob("lEoxH+AG2BAAoecEpAoWC4fXAAIGGAAowTDxAmJE4YGGE5QeJE5QHHE7owJE0pQKE7pQJE86fnE5QJSE5YUHBAIJQYxIpFAAvGBBAJIExYoGDgIACBBApFExonCDYoAOFSAnbFJYnE6vVDYYFHAwakQE4YaFAoQGJEIYoME7QoEE7ogFE/4neTBgntY84n/E+7HUE64mDE8IAFEw4nDTBifIE9gmId7gALE5IGCAooGDE6gASE8yaME7gmOFIgAREqIAhA=="));
|
||||||
|
var icoCocktail = require("heatshrink").decompress(atob("lEoxH+AH4AJtgABEkgmiEiXGAAIllAAiXeEAPXAQQDCFBYmTEgYqDFBZNWAIZRME6IfBEAYuEE5J2UwIAaJ5QncFBB3DB4YGCACQnKTQgoXE5bIEE6qfKPAZRFA4MUABgmNPAonBCgQnPExgpFPIgoNEyBSF4wGBFBgmSABCjJTZwoXEzwoHE0AoFE0QnCFAQmhKAonjFAInCE0Qn/E/4n/E/4n/wInDFEAhBEwQoDFLYdCEwooEFTAjHAAwoYIYgAMPDglT"));
|
||||||
|
var icoShot = require("heatshrink").decompress(atob("lEoxH+AH4A/AH4A/AH4AqwIAgE+HXADRPME8ZQM5AnSZBQkGAAYngEYonfJA5QQE8zGJFAYfKFBwmKE4iYIE7rpIeYgAJE5woEEpQKHTxhQIIpJaHJxgn/E8zGQZBAnQYxxQRFQYnlFgon5FCYmDE6LjHZRQmPE5AAOE/4njFCTGQKCwmRKAgATE54oWEyAqTDZY"));
|
||||||
|
var icoReset = require("heatshrink").decompress(atob("j0egILI8ACBh4DC/4DBh4DCv8f4ED8EPwEPEQMAvEAnkB4EA+AKBCAM8DYOA8EB//HwED/wXBg/wnAOC+EAjkDDoMgg+AJoRFCEIIAB/kHgEB/l8FwP/DYIDBC4MD/ASBgYeCAAw"));
|
||||||
|
var drawTimeout;
|
||||||
|
var activeDrink = 0;
|
||||||
|
var drinks = [0,0,0];
|
||||||
|
const maxDrinks = 2; // 3 drinks
|
||||||
|
var firstDrinkTime = null;
|
||||||
|
var firstDrinkTimeTime = null;
|
||||||
|
|
||||||
|
var confBeerSize;
|
||||||
|
var confSex;
|
||||||
|
var confWeight;
|
||||||
|
var confWeightUnit;
|
||||||
|
|
||||||
|
|
||||||
|
// Load Status ===============
|
||||||
|
var drinkStatus = require("Storage").open("drinkcounter.status.json", "r");
|
||||||
|
var test = drinkStatus.read(drinkStatus.getLength());
|
||||||
|
if(test!== undefined) {
|
||||||
|
drinkStatus = JSON.parse(test);
|
||||||
|
//console.log("read status: " + test);
|
||||||
|
for (let i = 0; i <= maxDrinks; i++) {
|
||||||
|
drinks[i] = drinkStatus.drinks[i];
|
||||||
|
}
|
||||||
|
firstDrinkTime = Date.parse(drinkStatus.firstDrinkTime);
|
||||||
|
//console.log("read firstDrinkTime: " + firstDrinkTime);
|
||||||
|
if (firstDrinkTime) firstDrinkTimeTime = require("locale").time(new Date(firstDrinkTime), 1);
|
||||||
|
//console.log("read firstDrinkTimeTime: " + firstDrinkTimeTime);
|
||||||
|
} else {
|
||||||
|
drinkStatus = {
|
||||||
|
drinks: [0,0,0]
|
||||||
|
};
|
||||||
|
//console.log("no status file - applying default");
|
||||||
|
}
|
||||||
|
// Load Status ===============
|
||||||
|
|
||||||
|
|
||||||
|
var drinksAlcohol = [12,16,5.6]; // in gramm
|
||||||
|
// Beer: 0.3L 12g - 0.5L 20g
|
||||||
|
// Radler: 0.3L 6g - 0.5L 10g
|
||||||
|
// Wine: 0.2L 16g
|
||||||
|
// Jäger Shot: 0.02L 5.6g
|
||||||
|
|
||||||
|
// sex: Women 60 - Men 70 (Percent)
|
||||||
|
// Formula: Alcohol in g /(Body weight in kg x sex) – (0,15 x Hours) = bac per mille
|
||||||
|
// Example: 5 Beer (0.3L=12g), 80KG, Male (70%), 5 hours
|
||||||
|
// (5 * 12) / (80 / 100 * 70) - (0.15 * 5)
|
||||||
|
|
||||||
|
function drawBac(){
|
||||||
|
if (firstDrinkTime) {
|
||||||
|
var sum_drinks = (drinks[0] * drinksAlcohol[0]) + (drinks[1] * drinksAlcohol[1]) + (drinks[2] * drinksAlcohol[2]);
|
||||||
|
|
||||||
|
if (confSex == "male") {
|
||||||
|
sex = 70;
|
||||||
|
} else {
|
||||||
|
sex = 60;
|
||||||
|
}
|
||||||
|
var weight = confWeight;
|
||||||
|
|
||||||
|
if (confWeightUnit == "US Pounds") {
|
||||||
|
weight = weight * 0.45359237;
|
||||||
|
}
|
||||||
|
var currentTime = new Date();
|
||||||
|
var time_diff = Math.floor(((currentTime - firstDrinkTime) % 86400000) / 3600000); // in hours!
|
||||||
|
//console.log("currentTime: " + currentTime)
|
||||||
|
//console.log("firstDrinkTime: " + firstDrinkTime)
|
||||||
|
|
||||||
|
//console.log("timediff: " + time_diff);
|
||||||
|
ebac = Math.round( ((sum_drinks) / (weight / 100 * sex) - (0.15 * time_diff) ) * 100) / 100;
|
||||||
|
|
||||||
|
//console.log("BAC: " + ebac + " weight: " + confWeight + " weightInKilo: " + weight + " Unit: " + confWeightUnit);
|
||||||
|
//console.log("sum_drinks: " + sum_drinks);
|
||||||
|
g.clearRect(0,34 + 20 + 8,176,34 + 20 + 20 + 8); //Clear
|
||||||
|
g.setFontAlign(0,0).setFont("8x16").setColor(g.theme.fg).drawString("BAC: " + ebac, 90, 74);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Load settings
|
||||||
|
function loadMySettings() {
|
||||||
|
// Helper function default setting
|
||||||
|
function def (value, def) {return value !== undefined ? value : def;}
|
||||||
|
|
||||||
|
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
|
||||||
|
confBeerSize = def(settings.beerSize, "0.3L");
|
||||||
|
confSex = def(settings.sex, "male");
|
||||||
|
confWeight = def(settings.weight, 80);
|
||||||
|
confWeightUnit = def(settings.weightUnit, "Kilo");
|
||||||
|
//console.log("Read config - weight: " + confWeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateTime(){
|
||||||
|
var d = require("locale").time(new Date(), 1);
|
||||||
|
|
||||||
|
//console.log(d);
|
||||||
|
var time = d.split(":");
|
||||||
|
var hours = time[0];
|
||||||
|
var minutes = time[1];
|
||||||
|
if (_12hour){
|
||||||
|
//do 12 hour stuff
|
||||||
|
if (hours > 12) {
|
||||||
|
ampm = "PM";
|
||||||
|
hours = hours - 12;
|
||||||
|
if (hours < 10) hours = doublenum(hours);
|
||||||
|
} else {
|
||||||
|
ampm = "AM";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ampm = "";
|
||||||
|
}
|
||||||
|
g.setBgColor(g.theme.bg).clearRect(0,24,176,44); //Clear
|
||||||
|
g.setFontAlign(0,0); // center font
|
||||||
|
g.setBgColor(g.theme.bg).setColor(g.theme.fg);
|
||||||
|
g.setFont("8x16").drawString("Time: " + hours + ":" + minutes + " " + ampm,90,34);
|
||||||
|
queueDrawTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueDrawTime() {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(function() {
|
||||||
|
drawTimeout = undefined;
|
||||||
|
updateTime();
|
||||||
|
}, 20000 - (Date.now() % 20000));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateDrinks(){
|
||||||
|
g.setBgColor(g.theme.bg).clearRect(0,145,176,176); //Clear
|
||||||
|
for (let i = 0; i <= maxDrinks; i++) {
|
||||||
|
if (i == activeDrink) {
|
||||||
|
g.setColor(g.theme.fg).fillRect((40 * (i + 1)) - 40 ,145,(40 * (i + 1)),176);
|
||||||
|
g.setColor(g.theme.bg);
|
||||||
|
} else {
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
}
|
||||||
|
g.setFont("Vector",20).drawString(drinks[i], (40 * (i + 1)) - 20, 160);
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
drinkStatus.drinks[i] = drinks[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setBgColor(g.theme.bg).setColor(g.theme.fg);
|
||||||
|
if (BANGLEJS2) {
|
||||||
|
g.drawImage(icoReset,145,145);
|
||||||
|
}
|
||||||
|
|
||||||
|
drinkStatus.firstDrinkTime = firstDrinkTime;
|
||||||
|
settings_file = require("Storage").open("drinkcounter.status.json", "w");
|
||||||
|
settings_file.write(JSON.stringify(drinkStatus));
|
||||||
|
|
||||||
|
drawBac();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFirstDrinkTime(){
|
||||||
|
if (firstDrinkTime){
|
||||||
|
g.setFont("8x16");
|
||||||
|
g.setFontAlign(0,0).drawString("1st drink @ " + firstDrinkTimeTime, 90, 34 + 20 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDrink(){
|
||||||
|
if (!firstDrinkTime){
|
||||||
|
firstDrinkTime = new Date();
|
||||||
|
firstDrinkTimeTime = require("locale").time(new Date(), 1);
|
||||||
|
//console.log("init drinking! " + firstDrinkTime);
|
||||||
|
}
|
||||||
|
drinks[activeDrink] = drinks[activeDrink] + 1;
|
||||||
|
updateFirstDrinkTime();
|
||||||
|
updateDrinks();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeDrink(){
|
||||||
|
if (drinks[activeDrink] > 0) drinks[activeDrink] = drinks[activeDrink] - 1;
|
||||||
|
updateDrinks();
|
||||||
|
|
||||||
|
if ((!BANGLEJS2) && (drinks[0] == 0) && (drinks[1] == 0) && (drinks[2] == 0)) {
|
||||||
|
resetDrinksFn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function previousDrink(){
|
||||||
|
if (activeDrink > 0) activeDrink = activeDrink - 1;
|
||||||
|
updateDrinks();
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextDrink(){
|
||||||
|
if (activeDrink < maxDrinks) activeDrink = activeDrink + 1;
|
||||||
|
updateDrinks();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDrinks() {
|
||||||
|
g.setBgColor(g.theme.bg);
|
||||||
|
g.drawImage(icoBeer,0,100);
|
||||||
|
g.drawImage(icoCocktail,40,100);
|
||||||
|
g.drawImage(icoShot,80,100);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetDrinksFn() {
|
||||||
|
g.clearRect(0,34,176,176); //Clear
|
||||||
|
resetDrinks = E.showPrompt("Reset drinks?", {
|
||||||
|
title: "Confirm",
|
||||||
|
buttons: { Yes: true, No: false },
|
||||||
|
});
|
||||||
|
resetDrinks.then((confirm) => {
|
||||||
|
if (confirm) {
|
||||||
|
for (let i = 0; i <= maxDrinks; i++) {
|
||||||
|
drinks[i] = 0;
|
||||||
|
}
|
||||||
|
//console.log("reset to default");
|
||||||
|
}
|
||||||
|
//console.log("reset " + confirm);
|
||||||
|
firstDrinkTime = null;
|
||||||
|
showDrinks();
|
||||||
|
updateDrinks();
|
||||||
|
updateTime();
|
||||||
|
updateFirstDrinkTime();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initDragEvents() {
|
||||||
|
|
||||||
|
if (BANGLEJS2) {
|
||||||
|
Bangle.on("drag", e => {
|
||||||
|
if (!drag) { // start dragging
|
||||||
|
drag = {x: e.x, y: e.y};
|
||||||
|
} else if (!e.b) { // released
|
||||||
|
const dx = e.x-drag.x, dy = e.y-drag.y;
|
||||||
|
drag = null;
|
||||||
|
if (Math.abs(dx)>Math.abs(dy)+10) {
|
||||||
|
// horizontal
|
||||||
|
if (dx < dy) {
|
||||||
|
//console.log("left " + dx + " " + dy);
|
||||||
|
previousDrink();
|
||||||
|
} else {
|
||||||
|
//console.log("right " + dx + " " + dy);
|
||||||
|
nextDrink();
|
||||||
|
}
|
||||||
|
} else if (Math.abs(dy)>Math.abs(dx)+10) {
|
||||||
|
// vertical
|
||||||
|
if (dx < dy) {
|
||||||
|
//console.log("down " + dx + " " + dy);
|
||||||
|
removeDrink();
|
||||||
|
} else {
|
||||||
|
//console.log("up " + dx + " " + dy);
|
||||||
|
addDrink();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//console.log("tap " + e.x + " " + e.y);
|
||||||
|
if (e.x > 145 && e.y > 145) {
|
||||||
|
resetDrinksFn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setWatch(addDrink, BTN1, { repeat: true, debounce:50 });
|
||||||
|
setWatch(removeDrink, BTN3, { repeat: true, debounce:50 });
|
||||||
|
setWatch(previousDrink, BTN4, { repeat: true, debounce:50 });
|
||||||
|
setWatch(nextDrink, BTN5, { repeat: true, debounce:50 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
loadMySettings();
|
||||||
|
showDrinks();
|
||||||
|
|
||||||
|
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = undefined;
|
||||||
|
updateTime();
|
||||||
|
queueDrawTime();
|
||||||
|
initDragEvents();
|
||||||
|
updateDrinks();
|
||||||
|
updateFirstDrinkTime();
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AAWBAAomkFpAweD4fXAAIGGAAo4bExAuJF4YGGF6QmJF5QHHF8o4JF1pgSF7pgRF96/vF5QJSF6YcHBAIJQdyIxFAAvGBBAJIFyYwGEgIACBBAxFFyovCEYoAOGTAvbGKYvE6vVEYYFHAwbEYF4YiFAoQGJFIYwUF7QwEF8ooFF/4v2XBgv1d94v/F/7vsF64uDF9IAFFx4vDXBi/IF+guQR6wvCFSIvOAwQFFAwYvcACQvuXSgvcFywxEACItZAH4A/AH4AlA=="))
|