mirror of https://github.com/espruino/BangleApps
Merge branch 'master' into gpsmagdir
commit
d2e0bc8da0
|
@ -10,6 +10,12 @@ Standard # of drag handlers: 0-10 (Default: 0, must be changed for backswipe to
|
||||||
|
|
||||||
Standard # of handlers settings are used to fine tune when backswipe should trigger the back function. E.g. when using a keyboard that works on drags, we don't want the backswipe to trigger when we just wanted to select a letter. This might not be able to cover all cases however.
|
Standard # of handlers settings are used to fine tune when backswipe should trigger the back function. E.g. when using a keyboard that works on drags, we don't want the backswipe to trigger when we just wanted to select a letter. This might not be able to cover all cases however.
|
||||||
|
|
||||||
|
To get an indication for standard # of handlers `Bangle["#onswipe"]` and `Bangle["#ondrag"]` can be entered in the [Espruino Web IDE](https://www.espruino.com/ide) console field. They return `undefined` if no handler is active, a function if one is active, or a list of functions if multiple are active. Calling this on the clock app is a good start.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- Possibly add option to tweak standard # of handlers on per app basis.
|
||||||
|
|
||||||
## Creator
|
## Creator
|
||||||
Kedlub
|
Kedlub
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
0.01: Initial fork from messages_light
|
||||||
|
0.02: Fix touch/drag/swipe handlers not being restored correctly if a message is removed
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Messages overlay app
|
||||||
|
|
||||||
|
This app handles the display of messages and message notifications as an overlay pop up.
|
||||||
|
|
||||||
|
It is a GUI replacement for the `messages` apps.
|
||||||
|
|
||||||
|
Messages are ephemeral and not stored on the Bangle.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Close app by tapping the X and scroll by swiping. The border of the pop up changes color if the Bangle is locked. The color depends on your currently active theme.
|
||||||
|
|
||||||
|
## Firmware hint
|
||||||
|
Current stable firmware draws incorrect colors for emojis. Nightly firmware builds correct this.
|
||||||
|
|
||||||
|
## Low memory mode
|
||||||
|
|
||||||
|
If free memory is below 2000 blocks, the overlay automatically only uses 1 bit depth. Default uses roundabout 1300 blocks, while low memory mode uses about 600.
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
|
||||||
|
[halemmerich](https://github.com/halemmerich)
|
||||||
|
Forked from messages_light by Rarder44
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,7 @@
|
||||||
|
//override require to filter require("message")
|
||||||
|
global.require_real=global.require;
|
||||||
|
global.require = (_require => file => {
|
||||||
|
if (file==="messages") file = "messagesoverlay";
|
||||||
|
return _require(file);
|
||||||
|
})(require);
|
||||||
|
|
|
@ -0,0 +1,486 @@
|
||||||
|
/* MESSAGES is a list of:
|
||||||
|
{id:int,
|
||||||
|
src,
|
||||||
|
title,
|
||||||
|
subject,
|
||||||
|
body,
|
||||||
|
sender,
|
||||||
|
tel:string,
|
||||||
|
new:true // not read yet
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ovrx = 10;
|
||||||
|
const ovry = 10;
|
||||||
|
const ovrw = g.getWidth()-2*ovrx;
|
||||||
|
const ovrh = g.getHeight()-2*ovry;
|
||||||
|
let _g = g;
|
||||||
|
|
||||||
|
let lockListener;
|
||||||
|
let quiet;
|
||||||
|
|
||||||
|
let LOG = function() {
|
||||||
|
//print.apply(null, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
let isQuiet = function(){
|
||||||
|
if (quiet == undefined) quiet = (require('Storage').readJSON('setting.json', 1) || {}).quiet;
|
||||||
|
return quiet;
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings = {
|
||||||
|
fontSmall:"6x8",
|
||||||
|
fontMedium:"Vector:14",
|
||||||
|
fontBig:"Vector:20",
|
||||||
|
fontLarge:"Vector:30",
|
||||||
|
};
|
||||||
|
|
||||||
|
let eventQueue = [];
|
||||||
|
let callInProgress = false;
|
||||||
|
|
||||||
|
let show = function(ovr){
|
||||||
|
let img = ovr;
|
||||||
|
if (ovr.getBPP() == 1) {
|
||||||
|
img = ovr.asImage();
|
||||||
|
img.palette = new Uint16Array([_g.theme.fg,_g.theme.bg]);
|
||||||
|
}
|
||||||
|
Bangle.setLCDOverlay(img, ovrx, ovry);
|
||||||
|
};
|
||||||
|
|
||||||
|
let manageEvent = function(ovr, event) {
|
||||||
|
event.new = true;
|
||||||
|
|
||||||
|
LOG("manageEvent");
|
||||||
|
if (event.id == "call") {
|
||||||
|
showCall(ovr, event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (event.t) {
|
||||||
|
case "add":
|
||||||
|
eventQueue.unshift(event);
|
||||||
|
|
||||||
|
if (!callInProgress)
|
||||||
|
showMessage(ovr, event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "modify":
|
||||||
|
let find = false;
|
||||||
|
eventQueue.forEach(element => {
|
||||||
|
if (element.id == event.id) {
|
||||||
|
find = true;
|
||||||
|
Object.assign(element, event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!find)
|
||||||
|
eventQueue.unshift(event);
|
||||||
|
|
||||||
|
if (!callInProgress)
|
||||||
|
showMessage(ovr, event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "remove":
|
||||||
|
if (eventQueue.length == 0 && !callInProgress)
|
||||||
|
next(ovr);
|
||||||
|
|
||||||
|
if (!callInProgress && eventQueue[0] !== undefined && eventQueue[0].id == event.id)
|
||||||
|
next(ovr);
|
||||||
|
|
||||||
|
else {
|
||||||
|
eventQueue.length = 0; // empty existing queue
|
||||||
|
eventQueue.forEach(element => {
|
||||||
|
if (element.id != event.id)
|
||||||
|
neweventQueue.push(element);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "musicstate":
|
||||||
|
case "musicinfo":
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let roundedRect = function(ovr, x,y,w,h,filled){
|
||||||
|
var poly = [
|
||||||
|
x,y+4,
|
||||||
|
x+4,y,
|
||||||
|
x+w-5,y,
|
||||||
|
x+w-1,y+4,
|
||||||
|
x+w-1,y+h-5,
|
||||||
|
x+w-5,y+h-1,
|
||||||
|
x+4,y+h-1,
|
||||||
|
x,y+h-5,
|
||||||
|
x,y+4
|
||||||
|
];
|
||||||
|
ovr.drawPoly(poly,true);
|
||||||
|
if (filled) ovr.fillPoly(poly,true);
|
||||||
|
};
|
||||||
|
|
||||||
|
let drawScreen = function(ovr, title, titleFont, src, iconcolor, icon){
|
||||||
|
ovr.setBgColor(ovr.theme.bg2);
|
||||||
|
ovr.clearRect(2,2,ovr.getWidth()-3,37);
|
||||||
|
|
||||||
|
ovr.setColor(ovr.theme.fg2);
|
||||||
|
ovr.setFont(settings.fontSmall);
|
||||||
|
ovr.setFontAlign(0,-1);
|
||||||
|
|
||||||
|
let textCenter = (ovr.getWidth()+35-26)/2;
|
||||||
|
|
||||||
|
if (src) {
|
||||||
|
let shortened = src;
|
||||||
|
while (ovr.stringWidth(shortened) > ovr.getWidth()-80) shortened = shortened.substring(0,shortened.length-2);
|
||||||
|
if (shortened.length != src.length) shortened += "...";
|
||||||
|
ovr.drawString(shortened, textCenter, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
ovr.setFontAlign(0,0);
|
||||||
|
ovr.setFont(titleFont);
|
||||||
|
if (title) ovr.drawString(title, textCenter, 38/2 + 5);
|
||||||
|
|
||||||
|
ovr.setColor(ovr.theme.fg2);
|
||||||
|
|
||||||
|
ovr.setFont(settings.fontMedium);
|
||||||
|
roundedRect(ovr, ovr.getWidth()-26,5,22,30,false);
|
||||||
|
ovr.setFont("Vector:16");
|
||||||
|
ovr.drawString("X",ovr.getWidth()-14,21);
|
||||||
|
|
||||||
|
ovr.setColor("#888");
|
||||||
|
roundedRect(ovr, 5,5,30,30,true);
|
||||||
|
ovr.setColor(ovr.getBPP() != 1 ? iconcolor : ovr.theme.bg2);
|
||||||
|
ovr.drawImage(icon,8,8);
|
||||||
|
};
|
||||||
|
|
||||||
|
let showMessage = function(ovr, msg) {
|
||||||
|
LOG("showMessage");
|
||||||
|
ovr.setBgColor(ovr.theme.bg);
|
||||||
|
|
||||||
|
if (typeof msg.CanscrollDown === "undefined")
|
||||||
|
msg.CanscrollDown = false;
|
||||||
|
if (typeof msg.CanscrollUp === "undefined")
|
||||||
|
msg.CanscrollUp = false;
|
||||||
|
|
||||||
|
// Normal text message display
|
||||||
|
let title = msg.title,
|
||||||
|
titleFont = settings.fontLarge,
|
||||||
|
lines;
|
||||||
|
if (title) {
|
||||||
|
let w = ovr.getWidth() - 35 - 26;
|
||||||
|
if (ovr.setFont(titleFont).stringWidth(title) > w)
|
||||||
|
titleFont = settings.fontMedium;
|
||||||
|
if (ovr.setFont(titleFont).stringWidth(title) > w) {
|
||||||
|
lines = ovr.wrapString(title, w);
|
||||||
|
title = (lines.length > 2) ? lines.slice(0, 2).join("\n") + "..." : lines.join("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawScreen(ovr, title, titleFont, msg.src || /*LANG*/ "Message", require("messageicons").getColor(msg), require("messageicons").getImage(msg));
|
||||||
|
|
||||||
|
if (!isQuiet() && msg.new) {
|
||||||
|
msg.new = false;
|
||||||
|
Bangle.buzz();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawMessage(ovr, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
let drawBorder = function(ovr) {
|
||||||
|
if (Bangle.isLocked())
|
||||||
|
ovr.setColor(ovr.theme.fgH);
|
||||||
|
else
|
||||||
|
ovr.setColor(ovr.theme.fg);
|
||||||
|
ovr.drawRect(0,0,ovr.getWidth()-1,ovr.getHeight()-1);
|
||||||
|
ovr.drawRect(1,1,ovr.getWidth()-2,ovr.getHeight()-2);
|
||||||
|
show(ovr);
|
||||||
|
if (!isQuiet()) Bangle.setLCDPower(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let showCall = function(ovr, msg) {
|
||||||
|
LOG("showCall");
|
||||||
|
LOG(msg);
|
||||||
|
|
||||||
|
if (msg.t == "remove") {
|
||||||
|
LOG("hide call screen");
|
||||||
|
next(ovr); //dont shift
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callInProgress = true;
|
||||||
|
|
||||||
|
let title = msg.title,
|
||||||
|
titleFont = settings.fontLarge,
|
||||||
|
lines;
|
||||||
|
if (title) {
|
||||||
|
let w = ovr.getWidth() - 35 -26;
|
||||||
|
if (ovr.setFont(titleFont).stringWidth(title) > w)
|
||||||
|
titleFont = settings.fontMedium;
|
||||||
|
if (ovr.setFont(titleFont).stringWidth(title) > w) {
|
||||||
|
lines = ovr.wrapString(title, w);
|
||||||
|
title = (lines.length > 2) ? lines.slice(0, 2).join("\n") + "..." : lines.join("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawScreen(ovr, title, titleFont, msg.src || /*LANG*/ "Message", require("messageicons").getColor(msg), require("messageicons").getImage(msg));
|
||||||
|
|
||||||
|
stopCallBuzz();
|
||||||
|
if (!isQuiet()) {
|
||||||
|
if (msg.new) {
|
||||||
|
msg.new = false;
|
||||||
|
if (callBuzzTimer) clearInterval(callBuzzTimer);
|
||||||
|
callBuzzTimer = setInterval(function() {
|
||||||
|
Bangle.buzz(500);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
Bangle.buzz(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawMessage(ovr, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
let next = function(ovr) {
|
||||||
|
LOG("next");
|
||||||
|
stopCallBuzz();
|
||||||
|
|
||||||
|
if (!callInProgress)
|
||||||
|
eventQueue.shift();
|
||||||
|
|
||||||
|
callInProgress = false;
|
||||||
|
if (eventQueue.length == 0) {
|
||||||
|
LOG("no element in queue - closing");
|
||||||
|
cleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showMessage(ovr, eventQueue[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let showMapMessage = function(ovr, msg) {
|
||||||
|
ovr.clearRect(2,2,ovr.getWidth()-3,ovr.getHeight()-3);
|
||||||
|
drawMessage(ovr, {
|
||||||
|
body: "Not implemented!"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let callBuzzTimer = null;
|
||||||
|
let stopCallBuzz = function() {
|
||||||
|
if (callBuzzTimer) {
|
||||||
|
clearInterval(callBuzzTimer);
|
||||||
|
callBuzzTimer = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let drawTriangleUp = function(ovr) {
|
||||||
|
ovr.reset();
|
||||||
|
ovr.fillPoly([ovr.getWidth()-9, 46,ovr.getWidth()-14, 56,ovr.getWidth()-4, 56]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let drawTriangleDown = function(ovr) {
|
||||||
|
ovr.reset();
|
||||||
|
ovr.fillPoly([ovr.getWidth()-9, ovr.getHeight()-6, ovr.getWidth()-14, ovr.getHeight()-16, ovr.getWidth()-4, ovr.getHeight()-16]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let scrollUp = function(ovr) {
|
||||||
|
msg = eventQueue[0];
|
||||||
|
LOG("up", msg);
|
||||||
|
if (typeof msg.FirstLine === "undefined")
|
||||||
|
msg.FirstLine = 0;
|
||||||
|
if (typeof msg.CanscrollUp === "undefined")
|
||||||
|
msg.CanscrollUp = false;
|
||||||
|
|
||||||
|
if (!msg.CanscrollUp) return;
|
||||||
|
|
||||||
|
msg.FirstLine = msg.FirstLine > 0 ? msg.FirstLine - 1 : 0;
|
||||||
|
|
||||||
|
drawMessage(ovr, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
let scrollDown = function(ovr) {
|
||||||
|
msg = eventQueue[0];
|
||||||
|
LOG("down", msg);
|
||||||
|
if (typeof msg.FirstLine === "undefined")
|
||||||
|
msg.FirstLine = 0;
|
||||||
|
if (typeof msg.CanscrollDown === "undefined")
|
||||||
|
msg.CanscrollDown = false;
|
||||||
|
|
||||||
|
if (!msg.CanscrollDown) return;
|
||||||
|
|
||||||
|
msg.FirstLine = msg.FirstLine + 1;
|
||||||
|
drawMessage(ovr, msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
let drawMessage = function(ovr, msg) {
|
||||||
|
let MyWrapString = function(str, maxWidth) {
|
||||||
|
str = str.replace("\r\n", "\n").replace("\r", "\n");
|
||||||
|
return ovr.wrapString(str, maxWidth);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof msg.FirstLine === "undefined") msg.FirstLine = 0;
|
||||||
|
|
||||||
|
let bodyFont = typeof msg.bodyFont === "undefined" ? settings.fontMedium : msg.bodyFont;
|
||||||
|
let Padding = 3;
|
||||||
|
if (typeof msg.lines === "undefined") {
|
||||||
|
ovr.setFont(bodyFont);
|
||||||
|
msg.lines = MyWrapString(msg.body, ovr.getWidth() - (Padding * 2));
|
||||||
|
if (msg.lines.length <= 2) {
|
||||||
|
bodyFont = ovr.getFonts().includes("Vector") ? "Vector:20" : "6x8:3";
|
||||||
|
ovr.setFont(bodyFont);
|
||||||
|
msg.lines = MyWrapString(msg.body, ovr.getWidth() - (Padding * 2));
|
||||||
|
msg.bodyFont = bodyFont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let NumLines = 7;
|
||||||
|
|
||||||
|
let linesToPrint = (msg.lines.length > NumLines) ? msg.lines.slice(msg.FirstLine, msg.FirstLine + NumLines) : msg.lines;
|
||||||
|
|
||||||
|
let yText = 40;
|
||||||
|
|
||||||
|
ovr.setBgColor(ovr.theme.bg);
|
||||||
|
ovr.setColor(ovr.theme.fg);
|
||||||
|
ovr.clearRect(2, yText, ovrw-3, ovrh-3);
|
||||||
|
let xText = Padding;
|
||||||
|
yText += Padding;
|
||||||
|
ovr.setFont(bodyFont);
|
||||||
|
let HText = ovr.getFontHeight();
|
||||||
|
|
||||||
|
yText = ((ovrh - yText) / 2) - (linesToPrint.length * HText / 2) + yText;
|
||||||
|
|
||||||
|
if (linesToPrint.length <= 3) {
|
||||||
|
ovr.setFontAlign(0, -1);
|
||||||
|
xText = ovr.getWidth() / 2;
|
||||||
|
} else
|
||||||
|
ovr.setFontAlign(-1, -1);
|
||||||
|
|
||||||
|
|
||||||
|
linesToPrint.forEach((line, i) => {
|
||||||
|
ovr.drawString(line, xText, yText + HText * i);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (msg.FirstLine != 0) {
|
||||||
|
msg.CanscrollUp = true;
|
||||||
|
drawTriangleUp(ovr);
|
||||||
|
} else
|
||||||
|
msg.CanscrollUp = false;
|
||||||
|
|
||||||
|
if (msg.FirstLine + linesToPrint.length < msg.lines.length) {
|
||||||
|
msg.CanscrollDown = true;
|
||||||
|
drawTriangleDown(ovr);
|
||||||
|
} else
|
||||||
|
msg.CanscrollDown = false;
|
||||||
|
show(ovr);
|
||||||
|
if (!isQuiet()) Bangle.setLCDPower(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let getSwipeHandler = function(ovr){
|
||||||
|
return (lr, ud) => {
|
||||||
|
if (ud == 1) {
|
||||||
|
scrollUp(ovr);
|
||||||
|
} else if (ud == -1){
|
||||||
|
scrollDown(ovr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let getTouchHandler = function(ovr){
|
||||||
|
return (_, xy) => {
|
||||||
|
if (xy.y < ovry + 40){
|
||||||
|
next(ovr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let restoreHandler = function(event){
|
||||||
|
LOG("Restore", event, backup[event]);
|
||||||
|
Bangle.removeAllListeners(event);
|
||||||
|
Bangle["#on" + event]=backup[event];
|
||||||
|
backup[event] = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
let backupHandler = function(event){
|
||||||
|
if (backupDone) return; // do not backup, overlay is already up
|
||||||
|
backup[event] = Bangle["#on" + event];
|
||||||
|
LOG("Backed up", backup[event]);
|
||||||
|
Bangle.removeAllListeners(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
let cleanup = function(){
|
||||||
|
if (lockListener) {
|
||||||
|
Bangle.removeListener("lock", lockListener);
|
||||||
|
lockListener = undefined;
|
||||||
|
}
|
||||||
|
restoreHandler("touch");
|
||||||
|
restoreHandler("swipe");
|
||||||
|
restoreHandler("drag");
|
||||||
|
|
||||||
|
Bangle.setLCDOverlay();
|
||||||
|
backupDone = false;
|
||||||
|
ovr = undefined;
|
||||||
|
quiet = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
let backup = {};
|
||||||
|
|
||||||
|
let backupDone = false;
|
||||||
|
|
||||||
|
let main = function(ovr, event) {
|
||||||
|
LOG("Main", event, settings);
|
||||||
|
|
||||||
|
if (!lockListener) {
|
||||||
|
lockListener = function (){
|
||||||
|
drawBorder(ovr);
|
||||||
|
};
|
||||||
|
Bangle.on('lock', lockListener);
|
||||||
|
}
|
||||||
|
backupHandler("touch");
|
||||||
|
backupHandler("swipe");
|
||||||
|
backupHandler("drag");
|
||||||
|
if (!backupDone){
|
||||||
|
Bangle.on('touch', getTouchHandler(ovr));
|
||||||
|
Bangle.on('swipe', getSwipeHandler(ovr));
|
||||||
|
}
|
||||||
|
backupDone=true;
|
||||||
|
|
||||||
|
if (event !== undefined){
|
||||||
|
drawBorder(ovr);
|
||||||
|
manageEvent(ovr, event);
|
||||||
|
} else {
|
||||||
|
LOG("No event given");
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ovr;
|
||||||
|
|
||||||
|
exports.pushMessage = function(event) {
|
||||||
|
if( event.id=="music") return require_real("messages").pushMessage(event);
|
||||||
|
|
||||||
|
bpp = 4;
|
||||||
|
if (process.memory().free < 2000) bpp = 1;
|
||||||
|
|
||||||
|
if (!ovr) {
|
||||||
|
ovr = Graphics.createArrayBuffer(ovrw, ovrh, bpp, {
|
||||||
|
msb: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ovr.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
g = ovr;
|
||||||
|
|
||||||
|
if (bpp == 4)
|
||||||
|
ovr.theme = g.theme;
|
||||||
|
else
|
||||||
|
ovr.theme = { fg:0, bg:1, fg2:1, bg2:0, fgH:1, bgH:0 };
|
||||||
|
|
||||||
|
main(ovr, event);
|
||||||
|
|
||||||
|
g = _g;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//Call original message library
|
||||||
|
exports.clearAll = function() { return require_real("messages").clearAll();};
|
||||||
|
exports.getMessages = function() { return require_real("messages").getMessages();};
|
||||||
|
exports.status = function() { return require_real("messages").status();};
|
||||||
|
exports.buzz = function() { return require_real("messages").buzz(msgSrc);};
|
||||||
|
exports.stopBuzz = function() { return require_real("messages").stopBuzz();};
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"id": "messagesoverlay",
|
||||||
|
"name": "Messages Overlay",
|
||||||
|
"version": "0.02",
|
||||||
|
"description": "An overlay based implementation of a messages UI (display notifications from iOS and Gadgetbridge/Android)",
|
||||||
|
"icon": "app.png",
|
||||||
|
"type": "bootloader",
|
||||||
|
"tags": "tool,system",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"dependencies" : { "messageicons":"module","messages":"app" },
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"messagesoverlay","url":"lib.js"},
|
||||||
|
{"name":"messagesoverlay.boot.js","url":"boot.js"}
|
||||||
|
],
|
||||||
|
"screenshots": [{"url":"screen_call.png"} ,{"url":"screen_message.png"} ]
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
|
@ -7,3 +7,5 @@
|
||||||
0.06: Allow logging of some things using power
|
0.06: Allow logging of some things using power
|
||||||
Add widget for live monitoring of power use
|
Add widget for live monitoring of power use
|
||||||
0.07: Convert Yes/No On/Off in settings to checkboxes
|
0.07: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
0.08: Fix the wrapping of intervals/timeouts with parameters
|
||||||
|
Fix the widget drawing if widgets are hidden and Bangle.setLCDBrightness is called
|
|
@ -19,6 +19,14 @@ You can switch on logging in the options to diagnose unexpected power use. Curre
|
||||||
|
|
||||||
Do not use trace logging for extended time, it uses a lot of storage and can fill up the flash quite quick.
|
Do not use trace logging for extended time, it uses a lot of storage and can fill up the flash quite quick.
|
||||||
|
|
||||||
|
### TODO
|
||||||
|
|
||||||
|
* Wrap functions given as strings to setTimeout/setInterval
|
||||||
|
* Handle eval in setTimeout/setInterval
|
||||||
|
* Track functions executed as event handlers
|
||||||
|
* Track buzzer
|
||||||
|
* Modify browser interface to estimate power use like widget does
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
Battery calibration offset is set by writing `batFullVoltage` in setting.json
|
Battery calibration offset is set by writing `batFullVoltage` in setting.json
|
||||||
|
|
|
@ -77,14 +77,14 @@
|
||||||
|
|
||||||
let functions = {};
|
let functions = {};
|
||||||
let wrapDeferred = ((o,t) => (a) => {
|
let wrapDeferred = ((o,t) => (a) => {
|
||||||
if (a == eval){
|
if (a == eval || typeof a == "string") {
|
||||||
return o.apply(this, arguments);
|
return o.apply(this, arguments);
|
||||||
} else {
|
} else {
|
||||||
let wrapped = a;
|
let wrapped = a;
|
||||||
if (!a.__wrapped){
|
if (!a.__wrapped){
|
||||||
wrapped = ()=>{
|
wrapped = ()=>{
|
||||||
let start = Date.now();
|
let start = Date.now();
|
||||||
let result = a.apply(undefined, arguments.slice(1));
|
let result = a.apply(undefined, arguments.slice(2)); // function arguments for deferred calls start at index 2, first is function, second is time
|
||||||
let end = Date.now()-start;
|
let end = Date.now()-start;
|
||||||
let f = a.toString().substring(0,100);
|
let f = a.toString().substring(0,100);
|
||||||
if (settings.logDetails) logDeferred(t, end, f);
|
if (settings.logDetails) logDeferred(t, end, f);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "powermanager",
|
"id": "powermanager",
|
||||||
"name": "Power Manager",
|
"name": "Power Manager",
|
||||||
"shortName": "Power Manager",
|
"shortName": "Power Manager",
|
||||||
"version": "0.07",
|
"version": "0.08",
|
||||||
"description": "Allow configuration of warnings and thresholds for battery charging and display.",
|
"description": "Allow configuration of warnings and thresholds for battery charging and display.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "bootloader",
|
"type": "bootloader",
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
"data": [
|
"data": [
|
||||||
{"name":"powermanager.hw.json"},
|
{"name":"powermanager.hw.json"},
|
||||||
{"name":"powermanager.def.json"},
|
{"name":"powermanager.def.json"},
|
||||||
|
{"name":"powermanager.json"},
|
||||||
{"name":"powermanager.log"}
|
{"name":"powermanager.log"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ currently-running apps */
|
||||||
let brightnessSetting = settings.brightness || 1;
|
let brightnessSetting = settings.brightness || 1;
|
||||||
Bangle.setLCDBrightness = ((o) => (a) => {
|
Bangle.setLCDBrightness = ((o) => (a) => {
|
||||||
brightnessSetting = a;
|
brightnessSetting = a;
|
||||||
draw(WIDGETS.powermanager);
|
WIDGETS.powermanager.draw(WIDGETS.powermanager);
|
||||||
return o(a);
|
return o(a);
|
||||||
})(Bangle.setLCDBrightness);
|
})(Bangle.setLCDBrightness);
|
||||||
|
|
||||||
|
|
|
@ -13,5 +13,7 @@
|
||||||
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
|
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
|
||||||
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
|
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
|
||||||
0.14: Fix Bangle.js 1 issue where after the 'overwrite track' menu, the start/stop button stopped working
|
0.14: Fix Bangle.js 1 issue where after the 'overwrite track' menu, the start/stop button stopped working
|
||||||
0.15: Diverge from the standard "Run" app. Swipe to intensity interface a la Karvonnen (curtesy of FTeacher at https://github.com/f-teacher)
|
0.15: Diverge from the standard "Run" app. Swipe to intensity interface a la Karvonen (curtesy of FTeacher at https://github.com/f-teacher)
|
||||||
Keep run state between runs (allowing you to exit and restart the app)
|
Keep run state between runs (allowing you to exit and restart the app)
|
||||||
|
0.16: Don't clear zone 2b indicator segment when updating HRM reading.
|
||||||
|
Write to correct settings file, fixing settings not working.
|
||||||
|
|
|
@ -67,3 +67,10 @@ app loader, the module is automatically included in the app's source. However
|
||||||
when developing via the IDE the module won't get pulled in by default.
|
when developing via the IDE the module won't get pulled in by default.
|
||||||
|
|
||||||
There are some options to fix this easily - please check out the [modules README.md file](https://github.com/espruino/BangleApps/blob/master/modules/README.md)
|
There are some options to fix this easily - please check out the [modules README.md file](https://github.com/espruino/BangleApps/blob/master/modules/README.md)
|
||||||
|
## Contributors (Run and Run+)
|
||||||
|
gfwilliams
|
||||||
|
hughbarney
|
||||||
|
GrandVizierOlaf
|
||||||
|
BartS23
|
||||||
|
f-teacher
|
||||||
|
thyttan
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
let wu = require("widget_utils");
|
let wu = require("widget_utils");
|
||||||
|
|
||||||
let runInterval;
|
let runInterval;
|
||||||
let karvonnenActive = false;
|
let karvonenActive = false;
|
||||||
// Run interface wrapped in a function
|
// Run interface wrapped in a function
|
||||||
let ExStats = require("exstats");
|
let ExStats = require("exstats");
|
||||||
let B2 = process.env.HWVERSION===2;
|
let B2 = process.env.HWVERSION===2;
|
||||||
|
@ -63,7 +63,6 @@ function setStatus(running) {
|
||||||
function onStartStop() {
|
function onStartStop() {
|
||||||
let running = !exs.state.active;
|
let running = !exs.state.active;
|
||||||
let prepPromises = [];
|
let prepPromises = [];
|
||||||
|
|
||||||
// start/stop recording
|
// start/stop recording
|
||||||
// Do this first in case recorder needs to prompt for
|
// Do this first in case recorder needs to prompt for
|
||||||
// an overwrite before we start tracking exstats
|
// an overwrite before we start tracking exstats
|
||||||
|
@ -155,7 +154,7 @@ Bangle.on("GPS", function(fix) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// run() function used to switch between traditional run UI and karvonnen UI
|
// run() function used to switch between traditional run UI and karvonen UI
|
||||||
function run() {
|
function run() {
|
||||||
wu.show();
|
wu.show();
|
||||||
layout.lazy = false;
|
layout.lazy = false;
|
||||||
|
@ -165,35 +164,35 @@ function run() {
|
||||||
if (!runInterval){
|
if (!runInterval){
|
||||||
runInterval = setInterval(function() {
|
runInterval = setInterval(function() {
|
||||||
layout.clock.label = locale.time(new Date(),1);
|
layout.clock.label = locale.time(new Date(),1);
|
||||||
if (!isMenuDisplayed && !karvonnenActive) layout.render();
|
if (!isMenuDisplayed && !karvonenActive) layout.render();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
run();
|
run();
|
||||||
|
|
||||||
///////////////////////////////////////////////
|
///////////////////////////////////////////////
|
||||||
// Karvonnen
|
// Karvonen
|
||||||
///////////////////////////////////////////////
|
///////////////////////////////////////////////
|
||||||
|
|
||||||
function stopRunUI() {
|
function stopRunUI() {
|
||||||
// stop updating and drawing the traditional run app UI
|
// stop updating and drawing the traditional run app UI
|
||||||
clearInterval(runInterval);
|
clearInterval(runInterval);
|
||||||
runInterval = undefined;
|
runInterval = undefined;
|
||||||
karvonnenActive = true;
|
karvonenActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopKarvonnenUI() {
|
function stopKarvonenUI() {
|
||||||
g.reset().clear();
|
g.reset().clear();
|
||||||
clearInterval(karvonnenInterval);
|
clearInterval(karvonenInterval);
|
||||||
karvonnenInterval = undefined;
|
karvonenInterval = undefined;
|
||||||
karvonnenActive = false;
|
karvonenActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let karvonnenInterval;
|
let karvonenInterval;
|
||||||
// Define the function to go back and forth between the different UI's
|
// Define the function to go back and forth between the different UI's
|
||||||
function swipeHandler(LR,_) {
|
function swipeHandler(LR,_) {
|
||||||
if (LR==-1 && karvonnenActive && !isMenuDisplayed) {stopKarvonnenUI(); run();}
|
if (LR==-1 && karvonenActive && !isMenuDisplayed) {stopKarvonenUI(); run();}
|
||||||
if (LR==1 && !karvonnenActive && !isMenuDisplayed) {stopRunUI(); karvonnenInterval = eval(require("Storage").read("runplus_karvonnen"))(settings.HRM, exs.stats.bpm);}
|
if (LR==1 && !karvonenActive && !isMenuDisplayed) {stopRunUI(); karvonenInterval = eval(require("Storage").read("runplus_karvonen"))(settings.HRM, exs.stats.bpm);}
|
||||||
}
|
}
|
||||||
// Listen for swipes with the swipeHandler
|
// Listen for swipes with the swipeHandler
|
||||||
Bangle.on("swipe", swipeHandler);
|
Bangle.on("swipe", swipeHandler);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
(function karvonnen(hrmSettings, exsHrmStats) {
|
(function karvonen(hrmSettings, exsHrmStats) {
|
||||||
//This app is an extra feature implementation for the Run.app of the bangle.js. It's called run+
|
//This app is an extra feature implementation for the Run.app of the bangle.js. It's called run+
|
||||||
//The calculation of the Heart Rate Zones is based on the Karvonnen method. It requires to know maximum and minimum heart rates. More precise calculation methods require a lab.
|
//The calculation of the Heart Rate Zones is based on the Karvonen method. It requires to know maximum and minimum heart rates. More precise calculation methods require a lab.
|
||||||
//Other methods are even more approximative.
|
//Other methods are even more approximative.
|
||||||
let wu = require("widget_utils");
|
let wu = require("widget_utils");
|
||||||
wu.hide();
|
wu.hide();
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
let hr = exsHrmStats.getValue();
|
let hr = exsHrmStats.getValue();
|
||||||
let hr1 = hr;
|
let hr1 = hr;
|
||||||
// These letiables display next and previous HR zone.
|
// These letiables display next and previous HR zone.
|
||||||
//get the hrzones right. The calculation of the Heart rate zones here is based on the Karvonnen method
|
//get the hrzones right. The calculation of the Heart rate zones here is based on the Karvonen method
|
||||||
//60-70% of HRR+minHR = zone2. //70-80% of HRR+minHR = zone3. //80-90% of HRR+minHR = zone4. //90-99% of HRR+minHR = zone5. //=>99% of HRR+minHR = serious risk of heart attack
|
//60-70% of HRR+minHR = zone2. //70-80% of HRR+minHR = zone3. //80-90% of HRR+minHR = zone4. //90-99% of HRR+minHR = zone5. //=>99% of HRR+minHR = serious risk of heart attack
|
||||||
let minzone2 = hrr * 0.6 + minhr;
|
let minzone2 = hrr * 0.6 + minhr;
|
||||||
let maxzone2 = hrr * 0.7 + minhr;
|
let maxzone2 = hrr * 0.7 + minhr;
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
// HR data: large, readable, in the middle of the screen
|
// HR data: large, readable, in the middle of the screen
|
||||||
function drawHR() {
|
function drawHR() {
|
||||||
g.setFontAlign(-1,0,0);
|
g.setFontAlign(-1,0,0);
|
||||||
g.clearRect(Rdiv(x,11/4),Rdiv(y,2)-25,Rdiv(x,11/4)+50*2,Rdiv(y,2)+25);
|
g.clearRect(Rdiv(x,11/4),Rdiv(y,2)-25,Rdiv(x,11/4)+50*2-14,Rdiv(y,2)+25);
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg);
|
||||||
g.setFont("Vector",50);
|
g.setFont("Vector",50);
|
||||||
g.drawString(hr, Rdiv(x,11/4), Rdiv(y,2)+4);
|
g.drawString(hr, Rdiv(x,11/4), Rdiv(y,2)+4);
|
||||||
|
@ -207,9 +207,9 @@
|
||||||
initDraw();
|
initDraw();
|
||||||
|
|
||||||
// check for updates every second.
|
// check for updates every second.
|
||||||
karvonnenInterval = setInterval(function() {
|
karvonenInterval = setInterval(function() {
|
||||||
if (!isMenuDisplayed && karvonnenActive) updateUI();
|
if (!isMenuDisplayed && karvonenActive) updateUI();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
return karvonnenInterval;
|
return karvonenInterval;
|
||||||
})
|
})
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"id": "runplus",
|
"id": "runplus",
|
||||||
"name": "Run+",
|
"name": "Run+",
|
||||||
"version": "0.15",
|
"version": "0.16",
|
||||||
"description": "Displays distance, time, steps, cadence, pace and more for runners. Based on the Run app, but extended with additional screen for heart rate interval training.",
|
"description": "Displays distance, time, steps, cadence, pace and more for runners. Based on the Run app, but extended with additional screen for heart rate interval training.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "run,running,fitness,outdoors,gps,karvonnen",
|
"tags": "run,running,fitness,outdoors,gps,karvonen,karvonnen",
|
||||||
"supports": [
|
"supports": [
|
||||||
"BANGLEJS2"
|
"BANGLEJS2"
|
||||||
],
|
],
|
||||||
|
@ -29,8 +29,8 @@
|
||||||
"url": "settings.js"
|
"url": "settings.js"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "runplus_karvonnen",
|
"name": "runplus_karvonen",
|
||||||
"url": "karvonnen.js"
|
"url": "karvonen.js"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
(function(back) {
|
(function(back) {
|
||||||
const SETTINGS_FILE = "run.json";
|
const SETTINGS_FILE = "runplus.json";
|
||||||
var ExStats = require("exstats");
|
var ExStats = require("exstats");
|
||||||
var statsList = ExStats.getList();
|
var statsList = ExStats.getList();
|
||||||
statsList.unshift({name:"-",id:""}); // add blank menu item
|
statsList.unshift({name:"-",id:""}); // add blank menu item
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
0.01: 1st version: saves values to csv
|
0.01: 1st version: saves values to csv
|
||||||
0.02: added HTML interface
|
0.02: added HTML interface
|
||||||
|
0.03: Added Stop/start recording, change BG color, filesize info
|
||||||
|
|
|
@ -9,6 +9,12 @@ Bangle JS1
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
UI for bangleJS1
|
||||||
|

|
||||||
|
|
||||||
|
UI for bangleJS2
|
||||||
|

|
||||||
|
|
||||||
Screenshot BJS2
|
Screenshot BJS2
|
||||||
|
|
||||||

|

|
||||||
|
@ -30,18 +36,35 @@ Screenshot data file content
|
||||||
|
|
||||||
Open and see a temperature in the screen
|
Open and see a temperature in the screen
|
||||||
Download the CSV file and process in your favourite spreadsheet software
|
Download the CSV file and process in your favourite spreadsheet software
|
||||||
|
if you have any problem enable the modedebug in code; v_mode_debug=1 or 2
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
Colours, all inputs , graph, widgets loaded
|
- Cross compatibility (JS1,JS2) and widgets compatibility
|
||||||
Counter for Times Display
|
- BG/FG Colour, Export to file and counter of saved records per session
|
||||||
|
- File operations: Info, delete (no yet)
|
||||||
|
|
||||||
|
## Pending/future Features
|
||||||
|
- Buttons layout: btn txt(BJS1) , on screen button (BJS2)
|
||||||
|
- Long press touch to delete file (BJS1,BJS2)
|
||||||
|
- File operations: Delete
|
||||||
|
|
||||||
## Controls
|
## Controls/UI
|
||||||
|
- Left area: Back/Exit/launcher
|
||||||
|
- BTN3 (long press)(BJS1): default Exit/kill app
|
||||||
|
|
||||||
|
- BTN1 (BJS2): "Launcher" / open "Messages"
|
||||||
|
- BTN2 (BJS1): "Launcher" / open "Messages"
|
||||||
|
- BTN1 (BJS1): Change FG Color
|
||||||
|
- BTN3 (BJS1): Change BG Color
|
||||||
|
- Right area: Change FG Color
|
||||||
|
- Swipe left: Change BG Color
|
||||||
|
- Swipe right: Increase/Decrease Hour circle/Points
|
||||||
|
|
||||||
exit: left side
|
|
||||||
|
|
||||||
|
|
||||||
## Creator
|
## Creator
|
||||||
|
|
||||||
Daniel Perez
|
Daniel Perez
|
||||||
|
For suggestions or feedback
|
||||||
|
https://github.com/dapgo/my_espruino_smartwatch_things
|
Binary file not shown.
After Width: | Height: | Size: 73 KiB |
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "tempmonitor",
|
"id": "tempmonitor",
|
||||||
"name": "Temperature monitor",
|
"name": "Temperature monitor",
|
||||||
"version": "0.02",
|
"version": "0.03",
|
||||||
"description": "Displays the current temperature and stores in a CSV file",
|
"description": "Displays the current temperature and stores in a CSV file",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool",
|
"tags": "tool",
|
||||||
|
|
|
@ -1,42 +1,69 @@
|
||||||
// Temperature monitor that saves a log of measures
|
// Temperature monitor that saves a log of measures
|
||||||
// standalone ver for developer, to remove testing lines
|
// standalone ver for developer, to remove testing lines
|
||||||
// delimiter ; (excel) or , (oldscool)
|
// delimiter ; (excel) or , (oldscool)
|
||||||
|
/* REFACTOR and remove commented code related to
|
||||||
|
SetUI, Layout, and setWatch( function(b) { }, BTN1, { repeat: true, edge:'falling' })
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
var v_mode_debug=0; //, 0=no, 1 min, 2 prone detail
|
var v_mode_debug=0; //, 0=no, 1 min, 2 prone detail
|
||||||
//var required for drawing with dynamic screen
|
//var required for drawing with dynamic screen
|
||||||
var rect = Bangle.appRect;
|
var rect = Bangle.appRect;
|
||||||
var history = [];
|
var history = [];
|
||||||
var readFreq=5000; //ms //PEND add to settings
|
var readFreq=4000; //ms //PEND add to settings
|
||||||
var saveFreq=60000; //ms 1min
|
if (v_mode_debug>0) var saveFreq=6000; //ms for testin 6sec
|
||||||
|
else var saveFreq=60000; //ms 1min
|
||||||
var v_saveToFile= new Boolean(true); //true save //false
|
var v_saveToFile= new Boolean(true); //true save //false
|
||||||
//with upload file º is not displayed properly
|
//with upload file º is not displayed properly
|
||||||
//with upload RAM º is displayed
|
//with upload RAM º is displayed
|
||||||
var v_t_symbol="";//ºC
|
var v_t_symbol="";//ºC
|
||||||
var v_saved_entries=0;
|
var v_saved_entries=0;
|
||||||
var filename ="temphistory.csv";
|
var v_filename ="temphistory.csv";
|
||||||
var lastMeasure = new String();
|
var lastMeasure = new String();
|
||||||
var v_model=process.env.BOARD;
|
var v_model=process.env.BOARD;
|
||||||
|
var v_color_erase=g.getBgColor(); //original BG color overwritten on SetVariables
|
||||||
|
var v_color=g.getColor();//original FG color
|
||||||
|
var id_rec_intv; //var for the recording interval
|
||||||
|
|
||||||
|
if (readFreq>saveFreq) console.log("Read refresh freq should be higher than saving");
|
||||||
|
if (v_mode_debug>0) console.log("original BG/FG color="+v_color_erase+" / "+v_color);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function SetVariables(){
|
||||||
//EMSCRIPTEN,EMSCRIPTEN2
|
//EMSCRIPTEN,EMSCRIPTEN2
|
||||||
if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') {
|
if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') {
|
||||||
v_font_size1=16;
|
v_font_size1=16;
|
||||||
v_font_size2=60;
|
v_font_size2=50;
|
||||||
//g.setColor("#0ff"); //light color
|
|
||||||
}else{
|
}else{
|
||||||
v_font_size1=11;
|
//Banglejs2 or others
|
||||||
|
v_font_size1=11; //too small?
|
||||||
v_font_size2=40;
|
v_font_size2=40;
|
||||||
//g.setColor("#000"); //black or dark
|
|
||||||
}
|
}
|
||||||
|
//overwriting default BG, is better detect?
|
||||||
|
if (g.theme.dark==1) v_color_erase=0x0000; //dynamic; //bg black
|
||||||
|
else if (g.theme.dark==0) v_color_erase=0xFFFF; //dynamic; //bg white
|
||||||
|
}
|
||||||
|
|
||||||
function onTemperature(v_temp) {
|
//print result
|
||||||
if (v_mode_debug>1) console.log("v_temp in "+v_temp);
|
function printTemperature(v_temp) {
|
||||||
|
if (v_mode_debug>1) console.log("v_temp in "+v_temp+" entries "+v_saved_entries);
|
||||||
ClearBox();
|
ClearBox();
|
||||||
//g.setFont("6x8",2).setFontAlign(0,0);
|
//g.setFont("6x8",2).setFontAlign(0,0);
|
||||||
g.setFontVector(v_font_size1).setFontAlign(0,0);
|
g.setFontVector(v_font_size1).setFontAlign(0,0);
|
||||||
var x = (rect.x+rect.x2)/2;
|
var x = (rect.x+(rect.x2-60))/2;//-60 space for graph and layout buttons
|
||||||
var y = (rect.y+rect.y2)/2 + 20;
|
var y = (rect.y+rect.y2)/2 + 20;
|
||||||
g.drawString("Records: "+v_saved_entries, x, rect.y+35);
|
|
||||||
g.drawString("Temperature:", x, rect.y+37+v_font_size1);
|
if (v_saveToFile==true) {
|
||||||
|
// if (v_mode_debug>0) console.log("prev color="+v_color);
|
||||||
|
printInfo("Recording : "+v_saved_entries, '#CC3333',x,rect.y+30);
|
||||||
|
//g.setColor('#CC3333'); //red
|
||||||
|
// g.drawString("Recording : "+v_saved_entries, x, rect.y+35);
|
||||||
|
//g.setColor(v_color);//restore default color
|
||||||
|
}
|
||||||
|
else printInfo("Rec paused : "+v_saved_entries, v_color,x,rect.y+30);
|
||||||
|
//else g.drawString("Rec paused : "+v_saved_entries, x, rect.y+35);
|
||||||
|
//space for printing info
|
||||||
|
g.drawString("Temperature:", x, rect.y+45+(v_font_size1*2));
|
||||||
//dynamic font (g.getWidth() > 200 ? 60 : 40)
|
//dynamic font (g.getWidth() > 200 ? 60 : 40)
|
||||||
g.setFontVector(v_font_size2).setFontAlign(0,0);
|
g.setFontVector(v_font_size2).setFontAlign(0,0);
|
||||||
// Avg of temperature readings
|
// Avg of temperature readings
|
||||||
|
@ -48,33 +75,68 @@ function onTemperature(v_temp) {
|
||||||
lastMeasure=avrTemp.toString();
|
lastMeasure=avrTemp.toString();
|
||||||
if (lastMeasure.length>4) lastMeasure=lastMeasure.substr(0,4);
|
if (lastMeasure.length>4) lastMeasure=lastMeasure.substr(0,4);
|
||||||
//DRAW temperature in the center
|
//DRAW temperature in the center
|
||||||
g.drawString(" ", x-20, y);
|
//remove g.drawString(" ", x-20, y);
|
||||||
g.drawString(v_temp+v_t_symbol, x-20, y);
|
g.drawString(v_temp+v_t_symbol, x, y);
|
||||||
g.flip();
|
g.flip();
|
||||||
}
|
}
|
||||||
// from: BJS2 pressure sensor, BJS1 inbuilt thermistor
|
// from: BJS2 pressure sensor, BJS1 inbuilt thermistor
|
||||||
function drawTemperature() {
|
function getTemperature() {
|
||||||
if(v_model.substr(0,10)!='EMSCRIPTEN'){
|
if(v_model.substr(0,10)!='EMSCRIPTEN'){
|
||||||
if (Bangle.getPressure) {
|
if (Bangle.getPressure) {
|
||||||
Bangle.getPressure().then(p =>{if (p) onTemperature(p);});
|
Bangle.getPressure().then(p =>{if (p) printTemperature(p);});
|
||||||
} else onTemperature(E.getTemperature());
|
} else printTemperature(E.getTemperature());
|
||||||
}
|
}
|
||||||
else onTemperature(11);//fake temp for emulators
|
else printTemperature(11.25);//fake temperature medition for emulators
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveToFile() {
|
/* Note that it changes BG and also FG to an opposite*/
|
||||||
|
function changeBGcolor(){
|
||||||
|
//pend to refactor
|
||||||
|
if (v_mode_debug>1) console.log("before BG/FG "+v_color_erase+" /"+v_color);
|
||||||
|
v_color_erase=0xFFFF-v_color_erase;
|
||||||
|
v_color=0xFFFF-v_color;
|
||||||
|
if (v_mode_debug>1) console.log("after result BG/FG "+v_color_erase+" /"+v_color);
|
||||||
|
//g.setColor(color_result);
|
||||||
|
g.setBgColor(v_color_erase);// 0 white, 1 black
|
||||||
|
g.setColor(v_color);
|
||||||
|
//move to event?
|
||||||
|
ClearScreen();
|
||||||
|
ClearBox();
|
||||||
|
drawGraph();
|
||||||
|
getTemperature();
|
||||||
|
//setDrawLayout(); //uncomment if layout can work with setUI
|
||||||
|
//g.clear();//impact on widgets
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveToFile(){
|
||||||
//input global vars: lastMeasure
|
//input global vars: lastMeasure
|
||||||
var a=new Date();
|
var a=new Date();
|
||||||
var strlastSaveTime=new String();
|
var strlastSaveTime=new String();
|
||||||
strlastSaveTime=a.toISOString();
|
strlastSaveTime=a.toISOString();
|
||||||
//strlastSaveTime=strlastSaveTime.concat(a.getFullYear(),a.getMonth()+1,a.getDate(),a.getHours(),a.getMinutes());;
|
//strlastSaveTime=strlastSaveTime.concat(a.getFullYear(),a.getMonth()+1,a.getDate(),a.getHours(),a.getMinutes());;
|
||||||
if (v_mode_debug==1) console.log("saving="+strlastSaveTime+";"+a.getHours()+":"+a.getMinutes()+";"+lastMeasure);
|
if (v_mode_debug>1) console.log("saving="+strlastSaveTime+";"+a.getHours()+":"+a.getMinutes()+";"+lastMeasure);
|
||||||
if (v_saveToFile==true){
|
if (v_saveToFile==true){
|
||||||
//write(strlastSaveTime+";"+
|
//write(strlastSaveTime+";"+
|
||||||
require("Storage").open(filename,"a").write((a.getMonth()+1)+";"+a.getDate()+";"+a.getHours()+":"+a.getMinutes()+";"+lastMeasure+"\n");
|
//var f = require("Storage").open(v_filename,"r");
|
||||||
|
// f=require("Storage").read(v_filename+"\1");//suffix required load completely!!
|
||||||
|
//note that .read uses Storage Class .open uses StorageFile Class , difference in file chunks
|
||||||
|
// if (v_mode_debug>0) console.log("f "+f);
|
||||||
|
var f = require("Storage").open(v_filename,"r");
|
||||||
|
if ((v_mode_debug>0) && (v_saved_entries==0)) console.log("file info:"+f);
|
||||||
|
if (f.len>0) {
|
||||||
|
if (!f) {
|
||||||
|
require("Storage").open(v_filename,"w").write("Month;Day;Time;Temp"+"\n");
|
||||||
|
if (v_mode_debug>0) console.log("not exist but created "+f);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
require("Storage").open(v_filename,"a").write((a.getMonth()+1)+";"+a.getDate()+";"+a.getHours()+":"+a.getMinutes()+";"+lastMeasure+"\n");
|
||||||
//(getTime()+",");
|
//(getTime()+",");
|
||||||
v_saved_entries=v_saved_entries+1;
|
v_saved_entries=v_saved_entries+1;
|
||||||
|
if (v_mode_debug>1) console.log("append to already exist "+f.name+" , "+v_saved_entries);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (v_mode_debug>0) console.log("recording mode stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawGraph(){
|
function drawGraph(){
|
||||||
|
@ -83,17 +145,19 @@ function drawGraph(){
|
||||||
transparent : 0,
|
transparent : 0,
|
||||||
buffer : require("heatshrink").decompress(atob("AEFt2AMKm3bsAMJjdt23ABhEB+/7tgaJ///DRUP//7tuADRP923YDRXbDRfymwaJhu/koaK7eyiwaK3cLDRlWDRY1NKBY1Ztu5kjmJg3cyVI7YMHgdu5Mkyu2fxHkyVJjdgDRFJkmRDRPsDQNbDQ5QBGoONKBJrBoxQIQwO2eRcbtu24AMIFIQLJAH4AMA=="))
|
buffer : require("heatshrink").decompress(atob("AEFt2AMKm3bsAMJjdt23ABhEB+/7tgaJ///DRUP//7tuADRP923YDRXbDRfymwaJhu/koaK7eyiwaK3cLDRlWDRY1NKBY1Ztu5kjmJg3cyVI7YMHgdu5Mkyu2fxHkyVJjdgDRFJkmRDRPsDQNbDQ5QBGoONKBJrBoxQIQwO2eRcbtu24AMIFIQLJAH4AMA=="))
|
||||||
};
|
};
|
||||||
g.drawImage(img_obj_thermo,rect.x2-50,rect.y2/2);
|
g.drawImage(img_obj_thermo,rect.x2-60,rect.y2/2);
|
||||||
g.flip();
|
g.flip();
|
||||||
}
|
}
|
||||||
function ClearScreen(){
|
function ClearScreen(){
|
||||||
//avoid widget areas
|
//avoid widget areas
|
||||||
g.reset(1).clearRect(rect.x, rect.y+24, rect.x2, rect.y2-24);
|
g.setBgColor(v_color_erase);
|
||||||
|
g.clearRect(rect.x, rect.y+24, rect.x2, rect.y2-24);
|
||||||
g.flip();
|
g.flip();
|
||||||
}
|
}
|
||||||
function ClearBox(){
|
function ClearBox(){
|
||||||
//custom boxarea , left space for static graph at right
|
//custom boxarea , left space for static graph at right
|
||||||
g.reset(1).clearRect(rect.x, rect.y+24, rect.x2-50, rect.y2-24);
|
g.setBgColor(v_color_erase);
|
||||||
|
g.clearRect(rect.x, rect.y+24, rect.x2-60, rect.y2-24);
|
||||||
g.flip();
|
g.flip();
|
||||||
}
|
}
|
||||||
function introPage(){
|
function introPage(){
|
||||||
|
@ -109,30 +173,140 @@ function introPage(){
|
||||||
g.drawString("Read freq(ms): "+readFreq, x, y );
|
g.drawString("Read freq(ms): "+readFreq, x, y );
|
||||||
g.drawString("Save to file: "+v_saveToFile, x, y+ ((v_font_size1*1)+2) );
|
g.drawString("Save to file: "+v_saveToFile, x, y+ ((v_font_size1*1)+2) );
|
||||||
g.drawString("Save freq(ms):"+saveFreq, x, y+((v_font_size1*2)+2) );
|
g.drawString("Save freq(ms):"+saveFreq, x, y+((v_font_size1*2)+2) );
|
||||||
fr=require("Storage").read(filename+"\1");//suffix required
|
fr=require("Storage").read(v_filename+"\1");//suffix required
|
||||||
if (fr) g.drawString("Current filesize:"+fr.length.toString()+"kb", x, y+((v_font_size1*3)+2) );
|
if (fr) g.drawString("Filesize:"+fr.length.toString()+"kb", x, y+((v_font_size1*3)+2) );
|
||||||
else g.drawString("File not exist", x, y+((v_font_size1*3)+2));
|
else g.drawString("File not exist", x, y+((v_font_size1*3)+2));
|
||||||
}
|
}
|
||||||
//MAIN
|
function printInfo(pmsg, pcolor,px,py){
|
||||||
Bangle.loadWidgets();
|
g.setColor(pcolor);
|
||||||
Bangle.setUI({
|
g.setFontVector(v_font_size1).setFontAlign(0,0);
|
||||||
|
g.drawString(pmsg, px,py+v_font_size1);
|
||||||
|
g.setColor(v_color);//restore default color
|
||||||
|
}
|
||||||
|
function toggleRecMode(duration, exectime){
|
||||||
|
//bydefault float, standard epoch requires *1000
|
||||||
|
if (v_mode_debug>0) console.log("duration"+duration);
|
||||||
|
if (duration>2) { //delete file
|
||||||
|
var x = (rect.x+(rect.x2-60))/2;
|
||||||
|
printInfo("Deleting file",'#CC3333',x, rect.y+32+v_font_size1);
|
||||||
|
// g.setColor('#CC3333'); //red
|
||||||
|
|
||||||
|
//too long "Deleting file: "+v_filename,
|
||||||
|
// for StorageFiles created with require("Storage").open(filename, ...)
|
||||||
|
//require("Storage").erase(v_filename);
|
||||||
|
//TODO refactor in a new function
|
||||||
|
//var mifile = require("Storage").open(v_filename,"w");
|
||||||
|
var mifile = require("Storage").open("temphistory.csv","w");
|
||||||
|
var v_output=mifile.erase();
|
||||||
|
//mifile.StorageFile.erase();
|
||||||
|
if (v_mode_debug>0) console.log("output"+v_output);
|
||||||
|
setTimeout(function() { if (v_mode_debug>0) console.log("pause for 1 sec");},1000);
|
||||||
|
return; //leave this function
|
||||||
|
}
|
||||||
|
if (v_saveToFile) v_saveToFile=false;
|
||||||
|
else v_saveToFile=true;
|
||||||
|
if (v_mode_debug>0) console.log("recording? "+v_saveToFile);
|
||||||
|
setRecordingFreq();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRecordingFreq(){
|
||||||
|
if (v_saveToFile==true) { //TODO now start on false btn will no enable
|
||||||
|
id_rec_intv=setInterval(function() {
|
||||||
|
saveToFile();
|
||||||
|
}, saveFreq); //ms
|
||||||
|
if (v_mode_debug>0) console.log("interval id / frq"+id_rec_intv+" / "+saveFreq);
|
||||||
|
}
|
||||||
|
else if (id_rec_intv){
|
||||||
|
clearInterval(id_rec_intv);
|
||||||
|
if (v_mode_debug>0) console.log("rec interval removed, id "+id_rec_intv);
|
||||||
|
id_rec_intv=0; // to reset var
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function UserInput(){
|
||||||
|
//theoretically incompatible with Layout
|
||||||
|
Bangle.setUI({
|
||||||
mode : "custom",
|
mode : "custom",
|
||||||
back : function() {load();}
|
//adds a back icon on top widget area
|
||||||
});
|
back : function() {load();},
|
||||||
|
//touch : function(n,e) {}, // optional - handler for 'touch' events
|
||||||
|
// righ/Left 1/-1 , updown
|
||||||
|
swipe : function(dir_rl,dir_ud) {
|
||||||
|
if(dir_rl == 1) {
|
||||||
|
if (v_mode_debug>0) console.log("swipe right: ");
|
||||||
|
getFileInfo(v_filename);
|
||||||
|
}
|
||||||
|
else if (dir_rl == -1){
|
||||||
|
if (v_mode_debug>0) console.log("swipe left: ");
|
||||||
|
changeBGcolor();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
touch : function(tzone,tobj){
|
||||||
|
if ((process.env.HWVERSION == 2)&&(v_mode_debug>0)){
|
||||||
|
console.log("tobj x,y,type : "+tobj.x+" "+tobj.y+" "+tobj.type);
|
||||||
|
}
|
||||||
|
switch(tzone){
|
||||||
|
//case 1: //left , back managed by setUI
|
||||||
|
case 2: // right disable/enable recording
|
||||||
|
toggleRecMode(0); //toggleRecMode(duration, exectime)
|
||||||
|
break;
|
||||||
|
// case 3: console.log("Touch 3 aka 1+2 not for BJS1 emul");//center 1+2
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//inferior to
|
||||||
|
btn : function(btn) {
|
||||||
|
if(btn == 1) {
|
||||||
|
if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') toggleRecMode(1); //console.log("btn1 BJS1");
|
||||||
|
else mainBtnShortcut(); //console.log("btn1 BJS2");
|
||||||
|
}
|
||||||
|
else if (btn == 2) mainBtnShortcut(); //console.log("btn2 BJS1");
|
||||||
|
else if (btn == 3) changeBGcolor(); //console.log("btn3 BJS1");
|
||||||
|
}
|
||||||
|
}); //endof setUI
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function mainBtnShortcut() {
|
||||||
|
//if messages app installed shortcut otherwise default access to launcher
|
||||||
|
if (require("Storage").read("messagegui.app.js")===undefined)
|
||||||
|
{
|
||||||
|
if (require("Storage").read("messagelist.app.js")===undefined) Bangle.showLauncher(); // implies btn2(js1) btn(js2)- launcher
|
||||||
|
else if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') load("messagelist.app.js");
|
||||||
|
else load("messagelist.app.js");
|
||||||
|
}
|
||||||
|
else if (v_model=='BANGLEJS'||v_model=='EMSCRIPTEN') load("messagegui.app.js");
|
||||||
|
else load("messagegui.app.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Show file size
|
||||||
|
function getFileInfo(v_filename) {
|
||||||
|
var f = require("Storage").open(v_filename,"r");
|
||||||
|
//todo refactor and reuse common code
|
||||||
|
g.setFontVector(v_font_size1).setFontAlign(0,0);
|
||||||
|
var x = (rect.x+(rect.x2-60))/2;
|
||||||
|
printInfo("file size:"+f.len,v_color,x, rect.y+32+v_font_size1);
|
||||||
|
// g.drawString("file size:"+f.len, x, rect.y+37+v_font_size1);
|
||||||
|
if (v_mode_debug>0) console.log("file "+v_filename+" size: "+f.len);
|
||||||
|
}// not used
|
||||||
|
|
||||||
|
|
||||||
|
//MAIN
|
||||||
|
SetVariables();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
|
||||||
ClearScreen();
|
ClearScreen();
|
||||||
introPage();
|
introPage();
|
||||||
|
|
||||||
|
//setDrawLayout(); //uncomment if layout can work with setUI
|
||||||
|
|
||||||
|
UserInput(); //inc SetUI and back icon
|
||||||
|
|
||||||
setInterval(function() {
|
setInterval(function() {
|
||||||
drawTemperature();
|
getTemperature();
|
||||||
}, readFreq); //ms
|
}, readFreq); //ms
|
||||||
|
|
||||||
if (v_saveToFile==true) {
|
setRecordingFreq();
|
||||||
setInterval(function() {
|
|
||||||
saveToFile();
|
|
||||||
}, saveFreq); //ms
|
|
||||||
}
|
|
||||||
setTimeout(ClearScreen, 3500);
|
|
||||||
setTimeout(drawGraph,4000);
|
|
||||||
setTimeout(drawTemperature,4500);
|
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
{"id":"tempmonitor","name":"tempmonitor","src":"tempmonitor.app.js","icon":"tempmonitor.img","version":"0.01","files":"tempmonitor.info,tempmonitor.app.js,tempmonitor.img"}
|
{"id":"tempmonitor","name":"tempmonitor","src":"tempmonitor.app.js","icon":"tempmonitor.img","version":"0.03","files":"tempmonitor.info,tempmonitor.app.js,tempmonitor.img"}
|
|
@ -6,11 +6,11 @@
|
||||||
);
|
);
|
||||||
const iconWidth = 18;
|
const iconWidth = 18;
|
||||||
|
|
||||||
function draw(this: { x: number; y: number }) {
|
function draw(this: { x?: number; y?: number }) {
|
||||||
g.reset();
|
g.reset();
|
||||||
if (Bangle.isCharging()) {
|
if (Bangle.isCharging()) {
|
||||||
g.setColor('#FD0');
|
g.setColor('#FD0');
|
||||||
g.drawImage(icon, this.x + 1, this.y + 1, {
|
g.drawImage(icon, this.x! + 1, this.y! + 1, {
|
||||||
scale: 0.6875,
|
scale: 0.6875,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,3 +6,6 @@
|
||||||
Update to match the default alarm widget, and not show itself when an alarm is hidden.
|
Update to match the default alarm widget, and not show itself when an alarm is hidden.
|
||||||
0.04: Fix check for active alarm
|
0.04: Fix check for active alarm
|
||||||
0.05: Convert Yes/No On/Off in settings to checkboxes
|
0.05: Convert Yes/No On/Off in settings to checkboxes
|
||||||
|
0.06: Remember next alarm to reduce calculation amount
|
||||||
|
Redraw only every hour when no alarm in next 24h
|
||||||
|
0.07: Fix when no alarms are present
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "widalarmeta",
|
"id": "widalarmeta",
|
||||||
"name": "Alarm & Timer ETA",
|
"name": "Alarm & Timer ETA",
|
||||||
"shortName": "Alarm ETA",
|
"shortName": "Alarm ETA",
|
||||||
"version": "0.05",
|
"version": "0.07",
|
||||||
"description": "A widget that displays the time to the next Alarm or Timer in hours and minutes, maximum 24h (configurable).",
|
"description": "A widget that displays the time to the next Alarm or Timer in hours and minutes, maximum 24h (configurable).",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
|
|
|
@ -1,24 +1,45 @@
|
||||||
(() => {
|
(() => {
|
||||||
require("Font5x9Numeric7Seg").add(Graphics);
|
require("Font5x9Numeric7Seg").add(Graphics);
|
||||||
const alarms = require("Storage").readJSON("sched.json",1) || [];
|
|
||||||
const config = Object.assign({
|
const config = Object.assign({
|
||||||
maxhours: 24,
|
maxhours: 24,
|
||||||
drawBell: false,
|
drawBell: false,
|
||||||
showSeconds: 0, // 0=never, 1=only when display is unlocked, 2=for less than a minute
|
showSeconds: 0, // 0=never, 1=only when display is unlocked, 2=for less than a minute
|
||||||
}, require("Storage").readJSON("widalarmeta.json",1) || {});
|
}, require("Storage").readJSON("widalarmeta.json",1) || {});
|
||||||
|
|
||||||
function draw() {
|
function getNextAlarm(date) {
|
||||||
const times = alarms
|
const alarms = (require("Storage").readJSON("sched.json",1) || []).filter(alarm => alarm.on && alarm.hidden !== true);
|
||||||
.map(alarm =>
|
WIDGETS["widalarmeta"].numActiveAlarms = alarms.length;
|
||||||
alarm.hidden !== true
|
if (alarms.length > 0) {
|
||||||
&& require("sched").getTimeToAlarm(alarm)
|
const times = alarms.map(alarm => require("sched").getTimeToAlarm(alarm, date) || Number.POSITIVE_INFINITY);
|
||||||
)
|
const eta = Math.min.apply(null, times);
|
||||||
.filter(a => a !== undefined);
|
if (eta !== Number.POSITIVE_INFINITY) {
|
||||||
const next = times.length > 0 ? Math.min.apply(null, times) : 0;
|
const idx = times.indexOf(eta);
|
||||||
|
const alarm = alarms[idx];
|
||||||
|
delete alarm.msg; delete alarm.id; delete alarm.data; // free some memory
|
||||||
|
return alarm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // getNextAlarm
|
||||||
|
|
||||||
|
function draw(fromInterval) {
|
||||||
|
if (this.nextAlarm === undefined) {
|
||||||
|
let alarm = getNextAlarm();
|
||||||
|
if (alarm === undefined) {
|
||||||
|
// try again with next hour
|
||||||
|
const nextHour = new Date();
|
||||||
|
nextHour.setHours(nextHour.getHours()+1);
|
||||||
|
alarm = getNextAlarm(nextHour);
|
||||||
|
}
|
||||||
|
if (alarm !== undefined) {
|
||||||
|
this.nextAlarm = alarm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const next = this.nextAlarm !== undefined ? require("sched").getTimeToAlarm(this.nextAlarm) : 0;
|
||||||
|
|
||||||
let calcWidth = 0;
|
let calcWidth = 0;
|
||||||
let drawSeconds = false;
|
let drawSeconds = false;
|
||||||
|
|
||||||
if (next > 0 && next < config.maxhours*60*60*1000) {
|
if (next > 0 && next <= config.maxhours*60*60*1000) {
|
||||||
const hours = Math.floor((next-1) / 3600000).toString();
|
const hours = Math.floor((next-1) / 3600000).toString();
|
||||||
const minutes = Math.floor(((next-1) % 3600000) / 60000).toString();
|
const minutes = Math.floor(((next-1) % 3600000) / 60000).toString();
|
||||||
const seconds = Math.floor(((next-1) % 60000) / 1000).toString();
|
const seconds = Math.floor(((next-1) % 60000) / 1000).toString();
|
||||||
|
@ -39,10 +60,14 @@
|
||||||
if (drawSeconds) {
|
if (drawSeconds) {
|
||||||
calcWidth += 3*5;
|
calcWidth += 3*5;
|
||||||
}
|
}
|
||||||
} else if (config.drawBell && alarms.some(alarm=>alarm.on&&(alarm.hidden!==true))) {
|
this.bellVisible = false;
|
||||||
// next alarm too far in future, draw only widalarm bell
|
} else if (config.drawBell && this.numActiveAlarms > 0) {
|
||||||
g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
|
|
||||||
calcWidth = 24;
|
calcWidth = 24;
|
||||||
|
// next alarm too far in future, draw only widalarm bell
|
||||||
|
if (this.bellVisible !== true || fromInterval !== true) {
|
||||||
|
g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
|
||||||
|
this.bellVisible = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.width !== calcWidth) {
|
if (this.width !== calcWidth) {
|
||||||
|
@ -51,8 +76,8 @@
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
// redraw next full minute or second
|
// redraw next hour when no alarm else full minute or second
|
||||||
const period = drawSeconds ? 1000 : 60000;
|
const period = next === 0 ? 3600000 : (drawSeconds ? 1000 : 60000);
|
||||||
let timeout = next > 0 ? next % period : period - (Date.now() % period);
|
let timeout = next > 0 ? next % period : period - (Date.now() % period);
|
||||||
if (timeout === 0) {
|
if (timeout === 0) {
|
||||||
timeout += period;
|
timeout += period;
|
||||||
|
@ -62,8 +87,8 @@
|
||||||
clearTimeout(this.timeoutId);
|
clearTimeout(this.timeoutId);
|
||||||
}
|
}
|
||||||
this.timeoutId = setTimeout(()=>{
|
this.timeoutId = setTimeout(()=>{
|
||||||
this.timeoutId = undefined;
|
WIDGETS["widalarmeta"].timeoutId = undefined;
|
||||||
this.draw();
|
WIDGETS["widalarmeta"].draw(true);
|
||||||
}, timeout);
|
}, timeout);
|
||||||
} /* draw */
|
} /* draw */
|
||||||
|
|
||||||
|
|
|
@ -7,3 +7,4 @@
|
||||||
Show difference of last measurement to pressure average of the the last three hours in the widget
|
Show difference of last measurement to pressure average of the the last three hours in the widget
|
||||||
Only use valid pressure values
|
Only use valid pressure values
|
||||||
0.06: Fix exception
|
0.06: Fix exception
|
||||||
|
0.07: Ensure barometer gets turned off after a few readings (isBarometerOn broken in 2v16)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "widbaroalarm",
|
"id": "widbaroalarm",
|
||||||
"name": "Barometer Alarm Widget",
|
"name": "Barometer Alarm Widget",
|
||||||
"shortName": "Barometer Alarm",
|
"shortName": "Barometer Alarm",
|
||||||
"version": "0.06",
|
"version": "0.07",
|
||||||
"description": "A widget that can alarm on when the pressure reaches defined thresholds.",
|
"description": "A widget that can alarm on when the pressure reaches defined thresholds.",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
|
|
|
@ -211,19 +211,8 @@ function calculcate3hAveragePressure() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
function barometerPressureHandler(e) {
|
||||||
turn on barometer power
|
|
||||||
take multiple measurements
|
|
||||||
sort the results
|
|
||||||
take the middle one (median)
|
|
||||||
turn off barometer power
|
|
||||||
*/
|
|
||||||
function getPressureValue() {
|
|
||||||
if (stop)
|
|
||||||
return;
|
|
||||||
const MEDIANLENGTH = 20;
|
const MEDIANLENGTH = 20;
|
||||||
Bangle.setBarometerPower(true, "widbaroalarm");
|
|
||||||
Bangle.on('pressure', function(e) {
|
|
||||||
while (currentPressures.length > MEDIANLENGTH)
|
while (currentPressures.length > MEDIANLENGTH)
|
||||||
currentPressures.pop();
|
currentPressures.pop();
|
||||||
|
|
||||||
|
@ -242,13 +231,24 @@ function getPressureValue() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
setTimeout(function() { turnOff(); }, 30000);
|
/*
|
||||||
|
turn on barometer power
|
||||||
|
take multiple measurements
|
||||||
|
sort the results
|
||||||
|
take the middle one (median)
|
||||||
|
turn off barometer power
|
||||||
|
*/
|
||||||
|
function getPressureValue() {
|
||||||
|
if (stop) return;
|
||||||
|
Bangle.setBarometerPower(true, "widbaroalarm");
|
||||||
|
Bangle.on('pressure', barometerPressureHandler);
|
||||||
|
setTimeout(turnOff, 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function turnOff() {
|
function turnOff() {
|
||||||
if (Bangle.isBarometerOn())
|
Bangle.removeListener('pressure', barometerPressureHandler);
|
||||||
Bangle.setBarometerPower(false, "widbaroalarm");
|
Bangle.setBarometerPower(false, "widbaroalarm");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,3 +7,4 @@
|
||||||
0.08: Ensure battery updates every 60s even if LCD was on at boot and stays on
|
0.08: Ensure battery updates every 60s even if LCD was on at boot and stays on
|
||||||
0.09: Misc speed/memory tweaks
|
0.09: Misc speed/memory tweaks
|
||||||
0.10: Color changes due to the battery level
|
0.10: Color changes due to the battery level
|
||||||
|
0.11: Change level for medium charge (50% -> 40%), and darken color on light themes as yellow was almost invisible
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "widbat",
|
"id": "widbat",
|
||||||
"name": "Battery Level Widget",
|
"name": "Battery Level Widget",
|
||||||
"version": "0.10",
|
"version": "0.11",
|
||||||
"description": "Show the current battery level and charging status in the top right of the clock",
|
"description": "Show the current battery level and charging status in the top right of the clock",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
g.setColor(g.theme.fg).fillRect(x,y+2,x+s-4,y+21).clearRect(x+2,y+4,x+s-6,y+19).fillRect(x+s-3,y+10,x+s,y+14);
|
g.setColor(g.theme.fg).fillRect(x,y+2,x+s-4,y+21).clearRect(x+2,y+4,x+s-6,y+19).fillRect(x+s-3,y+10,x+s,y+14);
|
||||||
var battery = E.getBattery();
|
var battery = E.getBattery();
|
||||||
if(battery < 20) {g.setColor("#f00");}
|
if(battery < 20) {g.setColor("#f00");}
|
||||||
else if (battery < 50) {g.setColor("#ff0");}
|
else if (battery < 40) {g.setColor(g.theme.dark ? "#ff0" : "#f80");}
|
||||||
else {g.setColor("#0f0");}
|
else {g.setColor("#0f0");}
|
||||||
g.fillRect(x+4,y+6,x+4+battery*(s-12)/100,y+17);
|
g.fillRect(x+4,y+6,x+4+battery*(s-12)/100,y+17);
|
||||||
}};
|
}};
|
||||||
|
|
|
@ -15,3 +15,4 @@
|
||||||
0.16: Increase screen update rate when charging
|
0.16: Increase screen update rate when charging
|
||||||
0.17: Add option 'Remove Jitter'='Drop only' to prevent percentage from getting up again when not charging
|
0.17: Add option 'Remove Jitter'='Drop only' to prevent percentage from getting up again when not charging
|
||||||
Add option to disable vibration when charger connects
|
Add option to disable vibration when charger connects
|
||||||
|
0.18: Only redraw when values change
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "widbatpc",
|
"id": "widbatpc",
|
||||||
"name": "Battery Level Widget (with percentage)",
|
"name": "Battery Level Widget (with percentage)",
|
||||||
"shortName": "Battery Widget",
|
"shortName": "Battery Widget",
|
||||||
"version": "0.17",
|
"version": "0.18",
|
||||||
"description": "Show the current battery level and charging status in the top right of the clock, with charge percentage",
|
"description": "Show the current battery level and charging status in the top right of the clock, with charge percentage",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
|
|
|
@ -86,7 +86,7 @@
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw() {
|
function draw(fromInterval) {
|
||||||
// if hidden, don't draw
|
// if hidden, don't draw
|
||||||
if (!WIDGETS["batpc"].width) return;
|
if (!WIDGETS["batpc"].width) return;
|
||||||
// else...
|
// else...
|
||||||
|
@ -103,6 +103,14 @@
|
||||||
l = prevMin;
|
l = prevMin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fromInterval === true && this.prevLevel === l && this.prevCharging === Bangle.isCharging()) {
|
||||||
|
return; // unchanged, do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prevLevel = l;
|
||||||
|
this.prevCharging = Bangle.isCharging();
|
||||||
|
|
||||||
const c = levelColor(l);
|
const c = levelColor(l);
|
||||||
|
|
||||||
if (Bangle.isCharging() && setting('charger')) {
|
if (Bangle.isCharging() && setting('charger')) {
|
||||||
|
@ -173,7 +181,7 @@
|
||||||
if (on) update();
|
if (on) update();
|
||||||
});
|
});
|
||||||
|
|
||||||
var id = setInterval(()=>WIDGETS["batpc"].draw(), intervalLow);
|
var id = setInterval(()=>WIDGETS["batpc"].draw(true), intervalLow);
|
||||||
|
|
||||||
WIDGETS["batpc"]={area:"tr",width:40,draw:draw,reload:reload};
|
WIDGETS["batpc"]={area:"tr",width:40,draw:draw,reload:reload};
|
||||||
setWidth();
|
setWidth();
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
0.01: First commit
|
0.01: First commit
|
||||||
|
0.02: Add tap-to-lock functionality
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"id": "widlockunlock",
|
"id": "widlockunlock",
|
||||||
"name": "Lock/Unlock Widget",
|
"name": "Lock/Unlock Widget",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked, or an unlock icon otherwise",
|
"description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked, or an unlock icon otherwise. Tap to lock the lcd",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
"tags": "widget,lock",
|
"tags": "widget,lock",
|
||||||
|
|
|
@ -1,5 +1,28 @@
|
||||||
Bangle.on("lockunlock", function() {
|
Bangle.on("lock", () => Bangle.drawWidgets());
|
||||||
Bangle.drawWidgets();
|
|
||||||
|
Bangle.on('touch', (_btn, xy) => {
|
||||||
|
const oversize = 5;
|
||||||
|
|
||||||
|
const w = WIDGETS.lockunlock;
|
||||||
|
|
||||||
|
const x = xy.x;
|
||||||
|
const y = xy.y;
|
||||||
|
|
||||||
|
if(w.x - oversize <= x && x < w.x + 14 + oversize
|
||||||
|
&& w.y - oversize <= y && y < w.y + 24 + oversize)
|
||||||
|
{
|
||||||
|
Bangle.setLocked(true);
|
||||||
|
|
||||||
|
const backlightTimeout = Bangle.getOptions().backlightTimeout; // ms
|
||||||
|
|
||||||
|
// seems to be a race/if we don't give the firmware enough time,
|
||||||
|
// it won't timeout the backlight and we'll restore it in our setTimeout below
|
||||||
|
Bangle.setOptions({ backlightTimeout: 100 });
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
Bangle.setOptions({ backlightTimeout });
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
WIDGETS["lockunlock"]={area:"tl",sortorder:10,width:14,draw:function(w) {
|
WIDGETS["lockunlock"]={area:"tl",sortorder:10,width:14,draw:function(w) {
|
||||||
g.reset().drawImage(atob(Bangle.isLocked() ? "DBGBAAAA8DnDDCBCBP////////n/n/n//////z/A" : "DBGBAAAA8BnDDCBABP///8A8A8Y8Y8Y8A8A//z/A"), w.x+1, w.y+3);
|
g.reset().drawImage(atob(Bangle.isLocked() ? "DBGBAAAA8DnDDCBCBP////////n/n/n//////z/A" : "DBGBAAAA8BnDDCBABP///8A8A8Y8Y8Y8A8A//z/A"), w.x+1, w.y+3);
|
||||||
|
|
|
@ -44,6 +44,7 @@ declare module ClockInfo {
|
||||||
w: number,
|
w: number,
|
||||||
h: number,
|
h: number,
|
||||||
draw(itm: MenuItem, info: Item, options: InteractiveOptions): void,
|
draw(itm: MenuItem, info: Item, options: InteractiveOptions): void,
|
||||||
|
app?: string, // used to remember clock_info locations, per app
|
||||||
};
|
};
|
||||||
|
|
||||||
type InteractiveOptions =
|
type InteractiveOptions =
|
||||||
|
|
|
@ -83,7 +83,10 @@ type WidgetArea = "tl" | "tr" | "bl" | "br";
|
||||||
type Widget = {
|
type Widget = {
|
||||||
area: WidgetArea;
|
area: WidgetArea;
|
||||||
width: number;
|
width: number;
|
||||||
draw: (this: { x: number; y: number }) => void;
|
sortorder?: number;
|
||||||
|
draw: (this: Widget, w: Widget) => void;
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
};
|
};
|
||||||
declare const WIDGETS: { [key: string]: Widget };
|
declare const WIDGETS: { [key: string]: Widget };
|
||||||
|
|
||||||
|
@ -8688,6 +8691,15 @@ interface ObjectConstructor {
|
||||||
*/
|
*/
|
||||||
entries(object: any): Array<[string, any]>;
|
entries(object: any): Array<[string, any]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms an array of key-value pairs into an object
|
||||||
|
*
|
||||||
|
* @param {any} entries - An array of `[key,value]` pairs to be used to create an object
|
||||||
|
* @returns {any} An object containing all the specified pairs
|
||||||
|
* @url http://www.espruino.com/Reference#l_Object_fromEntries
|
||||||
|
*/
|
||||||
|
fromEntries(entries: any): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new object with the specified prototype object and properties.
|
* Creates a new object with the specified prototype object and properties.
|
||||||
* properties are currently unsupported.
|
* properties are currently unsupported.
|
||||||
|
@ -8709,6 +8721,15 @@ interface ObjectConstructor {
|
||||||
*/
|
*/
|
||||||
getOwnPropertyDescriptor(obj: any, name: any): any;
|
getOwnPropertyDescriptor(obj: any, name: any): any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get information on all properties in the object (from `Object.getOwnPropertyDescriptor`), or just `{}` if no properties
|
||||||
|
*
|
||||||
|
* @param {any} obj - The object
|
||||||
|
* @returns {any} An object containing all the property descriptors of an object
|
||||||
|
* @url http://www.espruino.com/Reference#l_Object_getOwnPropertyDescriptors
|
||||||
|
*/
|
||||||
|
getOwnPropertyDescriptors(obj: any): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new property to the Object. 'Desc' is an object with the following fields:
|
* Add a new property to the Object. 'Desc' is an object with the following fields:
|
||||||
* * `configurable` (bool = false) - can this property be changed/deleted (not
|
* * `configurable` (bool = false) - can this property be changed/deleted (not
|
||||||
|
@ -8945,32 +8966,32 @@ interface Function {
|
||||||
/**
|
/**
|
||||||
* This executes the function with the supplied 'this' argument and parameters
|
* This executes the function with the supplied 'this' argument and parameters
|
||||||
*
|
*
|
||||||
* @param {any} this - The value to use as the 'this' argument when executing the function
|
* @param {any} thisArg - The value to use as the 'this' argument when executing the function
|
||||||
* @param {any} params - Optional Parameters
|
* @param {any} params - Optional Parameters
|
||||||
* @returns {any} The return value of executing this function
|
* @returns {any} The return value of executing this function
|
||||||
* @url http://www.espruino.com/Reference#l_Function_call
|
* @url http://www.espruino.com/Reference#l_Function_call
|
||||||
*/
|
*/
|
||||||
call(this: any, ...params: any[]): any;
|
call(thisArg: any, ...params: any[]): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This executes the function with the supplied 'this' argument and parameters
|
* This executes the function with the supplied 'this' argument and parameters
|
||||||
*
|
*
|
||||||
* @param {any} this - The value to use as the 'this' argument when executing the function
|
* @param {any} thisArg - The value to use as the 'this' argument when executing the function
|
||||||
* @param {any} args - Optional Array of Arguments
|
* @param {any} args - Optional Array of Arguments
|
||||||
* @returns {any} The return value of executing this function
|
* @returns {any} The return value of executing this function
|
||||||
* @url http://www.espruino.com/Reference#l_Function_apply
|
* @url http://www.espruino.com/Reference#l_Function_apply
|
||||||
*/
|
*/
|
||||||
apply(this: any, args: any): any;
|
apply(thisArg: any, args: ArrayLike<any>): any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This executes the function with the supplied 'this' argument and parameters
|
* This executes the function with the supplied 'this' argument and parameters
|
||||||
*
|
*
|
||||||
* @param {any} this - The value to use as the 'this' argument when executing the function
|
* @param {any} thisArg - The value to use as the 'this' argument when executing the function
|
||||||
* @param {any} params - Optional Default parameters that are prepended to the call
|
* @param {any} params - Optional Default parameters that are prepended to the call
|
||||||
* @returns {any} The 'bound' function
|
* @returns {any} The 'bound' function
|
||||||
* @url http://www.espruino.com/Reference#l_Function_bind
|
* @url http://www.espruino.com/Reference#l_Function_bind
|
||||||
*/
|
*/
|
||||||
bind(this: any, ...params: any[]): any;
|
bind(thisArg: any, ...params: any[]): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue