1
0
Fork 0

merge with upstream

master
hughbarney 2021-10-21 19:59:49 +01:00
commit 9f6602c596
42 changed files with 832 additions and 285 deletions

View File

@ -79,10 +79,10 @@
}, },
{ {
"id": "launch", "id": "launch",
"name": "Launcher (Default)", "name": "Launcher (Bangle.js 1 default)",
"shortName": "Launcher", "shortName": "Launcher",
"version": "0.07", "version": "0.07",
"description": "This is needed by Bangle.js to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.", "description": "This is needed by Bangle.js 1.0 to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.",
"icon": "app.png", "icon": "app.png",
"type": "launch", "type": "launch",
"tags": "tool,system,launcher", "tags": "tool,system,launcher",
@ -94,10 +94,10 @@
}, },
{ {
"id": "launchb2", "id": "launchb2",
"name": "Launcher (Bangle.js 2)", "name": "Launcher (Bangle.js 2 default)",
"shortName": "Launcher", "shortName": "Launcher",
"version": "0.03", "version": "0.03",
"description": "This is needed by Bangle.js 2.0 to display a menu allowing you to choose your own applications. It will not work on Bangle.js 1.0.", "description": "This is needed by Bangle.js 2.0 to display a menu allowing you to choose your own applications.",
"icon": "app.png", "icon": "app.png",
"type": "launch", "type": "launch",
"tags": "tool,system,launcher", "tags": "tool,system,launcher",
@ -320,7 +320,7 @@
"icon": "slidingtext.png", "icon": "slidingtext.png",
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"custom": "custom.html", "custom": "custom.html",
"allow_emulator": false, "allow_emulator": false,
@ -537,11 +537,11 @@
{ {
"id": "compass", "id": "compass",
"name": "Compass", "name": "Compass",
"version": "0.03", "version": "0.04",
"description": "Simple compass that points North", "description": "Simple compass that points North",
"icon": "compass.png", "icon": "compass.png",
"tags": "tool,outdoors", "tags": "tool,outdoors",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [
{"name":"compass.app.js","url":"compass.js"}, {"name":"compass.app.js","url":"compass.js"},
{"name":"compass.img","url":"compass-icon.js","evaluate":true} {"name":"compass.img","url":"compass-icon.js","evaluate":true}
@ -594,7 +594,7 @@
"description": "Application that allows you to record a GPS track. Can run in background", "description": "Application that allows you to record a GPS track. Can run in background",
"icon": "app.png", "icon": "app.png",
"tags": "tool,outdoors,gps,widget", "tags": "tool,outdoors,gps,widget",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"interface": "interface.html", "interface": "interface.html",
"storage": [ "storage": [
@ -624,11 +624,12 @@
{ {
"id": "heart", "id": "heart",
"name": "Heart Rate Recorder", "name": "Heart Rate Recorder",
"version": "0.06", "shortName": "HRM Record",
"version": "0.07",
"description": "Application that allows you to record your heart rate. Can run in background", "description": "Application that allows you to record your heart rate. Can run in background",
"icon": "app.png", "icon": "app.png",
"tags": "tool,health,widget", "tags": "tool,health,widget",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"interface": "interface.html", "interface": "interface.html",
"storage": [ "storage": [
{"name":"heart.app.js","url":"app.js"}, {"name":"heart.app.js","url":"app.js"},
@ -818,11 +819,11 @@
{ {
"id": "hrm", "id": "hrm",
"name": "Heart Rate Monitor", "name": "Heart Rate Monitor",
"version": "0.05", "version": "0.06",
"description": "Measure your heart rate and see live sensor data", "description": "Measure your heart rate and see live sensor data",
"icon": "heartrate.png", "icon": "heartrate.png",
"tags": "health", "tags": "health",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [
{"name":"hrm.app.js","url":"heartrate.js"}, {"name":"hrm.app.js","url":"heartrate.js"},
{"name":"hrm.img","url":"heartrate-icon.js","evaluate":true} {"name":"hrm.img","url":"heartrate-icon.js","evaluate":true}
@ -831,16 +832,31 @@
{ {
"id": "widhrm", "id": "widhrm",
"name": "Simple Heart Rate widget", "name": "Simple Heart Rate widget",
"version": "0.04", "version": "0.05",
"description": "When the screen is on, the widget turns on the heart rate monitor and displays the current heart rate (or last known in grey). For this to work well you'll need at least a 15 second LCD Timeout.", "description": "When the screen is on, the widget turns on the heart rate monitor and displays the current heart rate (or last known in grey). For this to work well you'll need at least a 15 second LCD Timeout.",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "health,widget", "tags": "health,widget",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [
{"name":"widhrm.wid.js","url":"widget.js"} {"name":"widhrm.wid.js","url":"widget.js"}
] ]
}, },
{ "id": "bthrm",
"name": "Bluetooth Heart Rate Monitor",
"shortName":"BT HRM",
"version":"0.01",
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
"icon": "app.png",
"tags": "health,bluetooth",
"type": "boot",
"supports" : ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"bthrm.boot.js","url":"boot.js"},
{"name":"bthrm.img","url":"app-icon.js","evaluate":true}
]
},
{ {
"id": "stetho", "id": "stetho",
"name": "Stethoscope", "name": "Stethoscope",
@ -1026,7 +1042,7 @@
{ {
"id": "sclock", "id": "sclock",
"name": "Simple Clock", "name": "Simple Clock",
"version": "0.06", "version": "0.07",
"description": "A Simple Digital Clock", "description": "A Simple Digital Clock",
"icon": "clock-simple.png", "icon": "clock-simple.png",
"type": "clock", "type": "clock",
@ -1071,12 +1087,12 @@
{ {
"id": "svclock", "id": "svclock",
"name": "Simple V-Clock", "name": "Simple V-Clock",
"version": "0.03", "version": "0.04",
"description": "Modification of Simple Clock 0.04 to use Vectorfont", "description": "Modification of Simple Clock 0.04 to use Vectorfont",
"icon": "vclock-simple.png", "icon": "vclock-simple.png",
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"allow_emulator": true, "allow_emulator": true,
"storage": [ "storage": [
{"name":"svclock.app.js","url":"vclock-simple.js"}, {"name":"svclock.app.js","url":"vclock-simple.js"},
@ -1376,12 +1392,12 @@
{ {
"id": "berlinc", "id": "berlinc",
"name": "Berlin Clock", "name": "Berlin Clock",
"version": "0.04", "version": "0.05",
"description": "Berlin Clock (see https://en.wikipedia.org/wiki/Mengenlehreuhr)", "description": "Berlin Clock (see https://en.wikipedia.org/wiki/Mengenlehreuhr)",
"icon": "berlin-clock.png", "icon": "berlin-clock.png",
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"allow_emulator": true, "allow_emulator": true,
"storage": [ "storage": [
{"name":"berlinc.app.js","url":"berlin-clock.js"}, {"name":"berlinc.app.js","url":"berlin-clock.js"},
@ -1502,7 +1518,7 @@
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,address,mac", "tags": "widget,address,mac",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"storage": [ "storage": [
{"name":"widid.wid.js","url":"widget.js"} {"name":"widid.wid.js","url":"widget.js"}
] ]
@ -2804,12 +2820,12 @@
"id": "worldclock", "id": "worldclock",
"name": "World Clock - 4 time zones", "name": "World Clock - 4 time zones",
"shortName": "World Clock", "shortName": "World Clock",
"version": "0.04", "version": "0.05",
"description": "Current time zone plus up to four others", "description": "Current time zone plus up to four others",
"icon": "app.png", "icon": "app.png",
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"custom": "custom.html", "custom": "custom.html",
"storage": [ "storage": [
@ -3334,12 +3350,12 @@
{ {
"id": "widhrt", "id": "widhrt",
"name": "HRM Widget", "name": "HRM Widget",
"version": "0.02", "version": "0.03",
"description": "Tiny widget to show the power on/off status of the Heart Rate Monitor. Requires firmware v2.08.167 or later", "description": "Tiny widget to show the power on/off status of the Heart Rate Monitor. Requires firmware v2.08.167 or later",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,hrm", "tags": "widget,hrm",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"widhrt.wid.js","url":"widget.js"} {"name":"widhrt.wid.js","url":"widget.js"}
@ -3382,7 +3398,7 @@
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"tags": "widget,compass", "tags": "widget,compass",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"widcom.wid.js","url":"widget.js"} {"name":"widcom.wid.js","url":"widget.js"}
@ -3391,7 +3407,7 @@
{ {
"id": "arrow", "id": "arrow",
"name": "Arrow Compass", "name": "Arrow Compass",
"version": "0.04", "version": "0.05",
"description": "Moving arrow compass that points North, shows heading, with tilt correction. Based on jeffmer's Navigation Compass", "description": "Moving arrow compass that points North, shows heading, with tilt correction. Based on jeffmer's Navigation Compass",
"icon": "arrow.png", "icon": "arrow.png",
"type": "app", "type": "app",
@ -3975,5 +3991,20 @@
{"name":"stopwatch.app.js","url":"stopwatch.app.js"}, {"name":"stopwatch.app.js","url":"stopwatch.app.js"},
{"name":"stopwatch.img","url":"stopwatch.icon.js","evaluate":true} {"name":"stopwatch.img","url":"stopwatch.icon.js","evaluate":true}
] ]
},
{ "id": "vernierrespirate",
"name": "Vernier Go Direct Respiration Belt",
"shortName":"Respiration Belt",
"version":"0.01",
"description": "Connects to a Go Direct Respiration Belt and shows respiration rate",
"icon": "app.png",
"tags": "health,bluetooth",
"supports" : ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"vernierrespirate.app.js","url":"app.js"},
{"name":"vernierrespirate.img","url":"app-icon.js","evaluate":true}
],
"data": [{"name":"vernierrespirate.json"}]
} }
] ]

View File

@ -1,4 +1,6 @@
0.01: First version 0.01: First version
0.02: Moved arrow image load to global scope 0.02: Moved arrow image load to global scope
0.03: faster drawCompass() function, does not cause buttons to become unresponsive 0.03: faster drawCompass() function, does not cause buttons to become unresponsive
0.04: removed LCD1.write() as it was keeping LCD on 0.04: removed LED1.write() as it was keeping LCD on
0.05: Turn compass off when screen off
Calibrate at start if no info

View File

@ -1,5 +1,5 @@
var pal1color = new Uint16Array([0x0000,0xFFC0],0,1); var pal1color = new Uint16Array([g.theme.bg,0xFFC0],0,1);
var pal2color = new Uint16Array([0x0000,0xffff],0,1); var pal2color = new Uint16Array([g.theme.bg,g.theme.fg],0,1);
var buf1 = Graphics.createArrayBuffer(128,128,1,{msb:true}); var buf1 = Graphics.createArrayBuffer(128,128,1,{msb:true});
var buf2 = Graphics.createArrayBuffer(80,40,1,{msb:true}); var buf2 = Graphics.createArrayBuffer(80,40,1,{msb:true});
var intervalRef; var intervalRef;
@ -7,6 +7,7 @@ var bearing=0; // always point north
var heading = 0; var heading = 0;
var oldHeading = 0; var oldHeading = 0;
var candraw = false; var candraw = false;
var isCalibrating = false;
var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null; var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null;
function flip1(x,y) { function flip1(x,y) {
@ -29,7 +30,7 @@ function drawCompass(hd) {
if (Math.abs(hd - oldHeading) < 2) return 0; if (Math.abs(hd - oldHeading) < 2) return 0;
hd=hd*Math.PI/180; hd=hd*Math.PI/180;
var p = [0, 1.1071, Math.PI/4, 2.8198, 3.4633, 7*Math.PI/4 , 5.1760]; var p = [0, 1.1071, Math.PI/4, 2.8198, 3.4633, 7*Math.PI/4 , 5.1760];
// using polar cordinates, 64,64 is the offset from the 0,0 origin // using polar cordinates, 64,64 is the offset from the 0,0 origin
var poly = [ var poly = [
64+60*Math.sin(hd+p[0]), 64-60*Math.cos(hd+p[0]), 64+60*Math.sin(hd+p[0]), 64-60*Math.cos(hd+p[0]),
@ -40,16 +41,16 @@ function drawCompass(hd) {
64+28.2843*Math.sin(hd+p[5]), 64-28.2843*Math.cos(hd+p[5]), 64+28.2843*Math.sin(hd+p[5]), 64-28.2843*Math.cos(hd+p[5]),
64+44.7214*Math.sin(hd+p[6]), 64-44.7214*Math.cos(hd+p[6]) 64+44.7214*Math.sin(hd+p[6]), 64-44.7214*Math.cos(hd+p[6])
]; ];
buf1.fillPoly(poly); buf1.fillPoly(poly);
flip1(56, 56); flip1(56, 56);
} }
// stops violent compass swings and wobbles, takes 3ms // stops violent compass swings and wobbles, takes 3ms
function newHeading(m,h){ function newHeading(m,h){
var s = Math.abs(m - h); var s = Math.abs(m - h);
var delta = (m>h)?1:-1; var delta = (m>h)?1:-1;
if (s>=180){s=360-s; delta = -delta;} if (s>=180){s=360-s; delta = -delta;}
if (s<2) return h; if (s<2) return h;
var hd = h + delta*(1 + Math.round(s/5)); var hd = h + delta*(1 + Math.round(s/5));
if (hd<0) hd+=360; if (hd<0) hd+=360;
@ -76,7 +77,7 @@ function tiltfixread(O,S){
return psi; return psi;
} }
function reading() { function reading(m) {
var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale); var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
heading = newHeading(d,heading); heading = newHeading(d,heading);
var dir = bearing - heading; var dir = bearing - heading;
@ -97,18 +98,19 @@ function reading() {
function calibrate(){ function calibrate(){
var max={x:-32000, y:-32000, z:-32000}, var max={x:-32000, y:-32000, z:-32000},
min={x:32000, y:32000, z:32000}; min={x:32000, y:32000, z:32000};
var ref = setInterval(()=>{ function onMag(m) {
var m = Bangle.getCompass();
max.x = m.x>max.x?m.x:max.x; max.x = m.x>max.x?m.x:max.x;
max.y = m.y>max.y?m.y:max.y; max.y = m.y>max.y?m.y:max.y;
max.z = m.z>max.z?m.z:max.z; max.z = m.z>max.z?m.z:max.z;
min.x = m.x<min.x?m.x:min.x; min.x = m.x<min.x?m.x:min.x;
min.y = m.y<min.y?m.y:min.y; min.y = m.y<min.y?m.y:min.y;
min.z = m.z<min.z?m.z:min.z; min.z = m.z<min.z?m.z:min.z;
}, 100); }
Bangle.on('mag', onMag);
Bangle.setCompassPower(1, "app");
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(()=>{ setTimeout(()=>{
if(ref) clearInterval(ref); Bangle.removeListener('mag', onMag);
var offset = {x:(max.x+min.x)/2,y:(max.y+min.y)/2,z:(max.z+min.z)/2}; var offset = {x:(max.x+min.x)/2,y:(max.y+min.y)/2,z:(max.z+min.z)/2};
var delta = {x:(max.x-min.x)/2,y:(max.y-min.y)/2,z:(max.z-min.z)/2}; var delta = {x:(max.x-min.x)/2,y:(max.y-min.y)/2,z:(max.z-min.z)/2};
var avg = (delta.x+delta.y+delta.z)/3; var avg = (delta.x+delta.y+delta.z)/3;
@ -132,6 +134,7 @@ function docalibrate(e,first){
flip1(56,56); flip1(56,56);
calibrate().then((r)=>{ calibrate().then((r)=>{
isCalibrating = false;
require("Storage").write("magnav.json",r); require("Storage").write("magnav.json",r);
Bangle.buzz(); Bangle.buzz();
CALIBDATA = r; CALIBDATA = r;
@ -142,27 +145,39 @@ function docalibrate(e,first){
startdraw(); startdraw();
setTimeout(setButtons,1000); setTimeout(setButtons,1000);
} }
} }
if (first===undefined) first=false;
if (first === undefined) first = false;
stopdraw(); stopdraw();
clearWatch(); clearWatch();
if (first) isCalibrating = true;
if (first)
E.showAlert(msg,title).then(action.bind(null,true)); E.showAlert(msg,title).then(action.bind(null,true));
else else
E.showPrompt(msg,{title:title,buttons:{"Start":true,"Cancel":false}}).then(action); E.showPrompt(msg,{title:title,buttons:{"Start":true,"Cancel":false}}).then(action);
} }
function startdraw(){ function startdraw(){
Bangle.setCompassPower(1, "app");
g.clear(); g.clear();
g.setColor(1,1,1); g.setColor(1,1,1);
Bangle.drawWidgets(); Bangle.drawWidgets();
candraw = true; candraw = true;
intervalRef = setInterval(reading,500); if (intervalRef) clearInterval(intervalRef);
intervalRef = setInterval(reading,200);
} }
function stopdraw() { function stopdraw() {
candraw=false; candraw=false;
if(intervalRef) {clearInterval(intervalRef);}
Bangle.setCompassPower(0, "app");
if (intervalRef) {
clearInterval(intervalRef);
intervalRef = undefined;
}
} }
function setButtons(){ function setButtons(){
@ -170,8 +185,9 @@ function setButtons(){
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
setWatch(docalibrate, BTN3, {repeat:false,edge:"falling"}); setWatch(docalibrate, BTN3, {repeat:false,edge:"falling"});
} }
Bangle.on('lcdPower',function(on) { Bangle.on('lcdPower',function(on) {
if (isCalibrating) return;
if (on) { if (on) {
startdraw(); startdraw();
} else { } else {
@ -179,9 +195,8 @@ Bangle.on('lcdPower',function(on) {
} }
}); });
Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.setCompassPower(1);
startdraw();
setButtons(); setButtons();
Bangle.setLCDPower(1);
if (CALIBDATA) startdraw(); else docalibrate({},true);

View File

@ -1,3 +1,6 @@
0.02: Modified for use with new bootloader and firmware 0.02: Modified for use with new bootloader and firmware
0.03: Shrinked size to avoid cut-off edges on the physical device. BTN3: show date. BTN1: show time in decimal. 0.03: Shrinked size to avoid cut-off edges on the physical device. BTN3: show date. BTN1: show time in decimal.
0.04: Update to use Bangle.setUI instead of setWatch 0.04: Update to use Bangle.setUI instead of setWatch
0.05: Update *on* the minute rather than every 15 secs
Now show widgets
Make compatible with themes, and Bangle.js 2

View File

@ -1,7 +1,7 @@
// Berlin Clock see https://en.wikipedia.org/wiki/Mengenlehreuhr // Berlin Clock see https://en.wikipedia.org/wiki/Mengenlehreuhr
// https://github.com/eska-muc/BangleApps // https://github.com/eska-muc/BangleApps
const fields = [4, 4, 11, 4]; const fields = [4, 4, 11, 4];
const offset = 20; const offset = 24;
const width = g.getWidth() - 2 * offset; const width = g.getWidth() - 2 * offset;
const height = g.getHeight() - 2 * offset; const height = g.getHeight() - 2 * offset;
const rowHeight = height / 4; const rowHeight = height / 4;
@ -10,11 +10,23 @@ var show_date = false;
var show_time = false; var show_time = false;
var yy = 0; var yy = 0;
rowlights = []; var rowlights = [];
time_digit = []; var time_digit = [];
function drawBerlinClock() { // timeout used to update every minute
g.clear(); var drawTimeout;
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
function draw() {
g.reset().clearRect(0,24,g.getWidth(),g.getHeight());
var now = new Date(); var now = new Date();
// show date below the clock // show date below the clock
@ -24,8 +36,7 @@ function drawBerlinClock() {
var day = now.getDate(); var day = now.getDate();
var dateString = `${yr}-${month < 10 ? '0' : ''}${month}-${day < 10 ? '0' : ''}${day}`; var dateString = `${yr}-${month < 10 ? '0' : ''}${month}-${day < 10 ? '0' : ''}${day}`;
var strWidth = g.stringWidth(dateString); var strWidth = g.stringWidth(dateString);
g.setColor(1, 1, 1); g.setColor(g.theme.fg).setFontAlign(-1,-1);
g.setFontAlign(-1,-1);
g.drawString(dateString, ( g.getWidth() - strWidth ) / 2, height + offset + 4); g.drawString(dateString, ( g.getWidth() - strWidth ) / 2, height + offset + 4);
} }
@ -50,8 +61,7 @@ function drawBerlinClock() {
x2 = (col + 1) * boxWidth + offset; x2 = (col + 1) * boxWidth + offset;
y2 = (row + 1) * rowHeight + offset; y2 = (row + 1) * rowHeight + offset;
g.setColor(1, 1, 1); g.setColor(g.theme.fg).drawRect(x1, y1, x2, y2);
g.drawRect(x1, y1, x2, y2);
if (col < rowlights[row]) { if (col < rowlights[row]) {
if (row === 2) { if (row === 2) {
if (((col + 1) % 3) === 0) { if (((col + 1) % 3) === 0) {
@ -65,46 +75,42 @@ function drawBerlinClock() {
g.fillRect(x1 + 2, y1 + 2, x2 - 2, y2 - 2); g.fillRect(x1 + 2, y1 + 2, x2 - 2, y2 - 2);
} }
if (row == 3 && show_time) { if (row == 3 && show_time) {
g.setColor(1,1,1); g.setColor(g.theme.fg).setFontAlign(0,0);
g.setFontAlign(0,0);
g.drawString(time_digit[col],(x1+x2)/2,(y1+y2)/2); g.drawString(time_digit[col],(x1+x2)/2,(y1+y2)/2);
} }
} }
} }
queueDraw();
} }
function toggleDate() { function toggleDate() {
show_date = ! show_date; show_date = ! show_date;
drawBerlinClock(); draw();
} }
function toggleTime() { function toggleTime() {
show_time = ! show_time; show_time = ! show_time;
drawBerlinClock(); draw();
} }
// special function to handle display switch on // Stop updates when LCD is off, restart when on
Bangle.on('lcdPower', (on) => { Bangle.on('lcdPower',on=>{
g.clear();
if (on) { if (on) {
Bangle.drawWidgets(); draw(); // draw immediately, queue redraw
// call your app function here } else { // stop draw timer
drawBerlinClock(); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
} }
}); });
// refesh every 15 sec // Show launcher when button pressed, handle up/down
setInterval(drawBerlinClock, 15E3); Bangle.setUI("clockupdown", dir=> {
if (dir<0) toggleTime();
if (dir>0) toggleDate();
});
g.clear(); g.clear();
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
drawBerlinClock(); draw();
if (BTN3) {
// Toggle date display, when BTN3 is pressed
setWatch(toggleTime,BTN1, { repeat : true, edge: "falling"});
// Toggle date display, when BTN3 is pressed
setWatch(toggleDate,BTN3, { repeat : true, edge: "falling"});
}
// Show launcher when button pressed
Bangle.setUI("clock");

1
apps/bthrm/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New App!

45
apps/bthrm/README.md Normal file
View File

@ -0,0 +1,45 @@
# Bluetooth Heart Rate Monitor
When this app is installed it overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.
HRM is requested it searches on Bluetooth for a heart rate monitor, connects, and sends data back using the `Bangle.on('HRM'` event as if it came from the on board monitor.
This means it's compatible with many Bangle.js apps including:
* [Heart Rate Widget](https://banglejs.com/apps/#widhrt)
* [Heart Rate Recorder](https://banglejs.com/apps/#heart)
It it NOT COMPATIBLE with [Heart Rate Monitor](https://banglejs.com/apps/#hrm)
as that requires live sensor data (rather than just BPM readings).
## Usage
Just install the app, then install an app that uses the heart rate monitor.
Once installed it'll automatically try and connect to the first bluetooth
heart rate monitor it finds.
**To disable this and return to normal HRM, uninstall the app**
## Compatible Heart Rate Monitors
This works with any heart rate monitor providing the standard Bluetooth
Heart Rate Service (`180D`) and characteristic (`2A37`).
So far it has been tested on:
* CooSpo Bluetooth Heart Rate Monitor
## Internals
This replaces `Bangle.setHRMPower` with its own implementation.
## TODO
* Maybe a `bthrm.settings.js` and app (that calls it) to enable it to be turned on and off
* A widget to show connection state?
* Specify a specific device by address?
## Creator
Gordon Williams

1
apps/bthrm/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4UA///g3yy06AoIZNitUAg8AgtVqtQAgoRCAwITBAggABAoIABAgsAgIGDoIEDoApDAAwwBFIV1BYo1E+oLTAgQLGJon9BZNXBatdBYRVFBYN/r9fHoxTBBYYlEL4QLFq/a1WUgE///fr4xBv/+1Wq1EAh/3/tX6/fv/6BYOqwCzBBYf9tWq9QLF79X+oLBDIOgKgILEEIIxBGAMVNAP/BYf/BYUFBYJSB6wLC9QLBeAQLBqwLCGAL9BBYmr9X+GAILBbIIlBBYP6/wwBBYMFBYZGB/4XDGAILD34vEcwYLB15HBBYYkBBYWrFwILDKoRTCVIQLCEgQXIEgVaF44YCoRHHAAMUgQuBNgILFgECO4W/BZCPFBYinGBY6/CAArXFBY7vDAAsq1QuB0ALIOwOABY0KEgJGGGAguHDAYDBA=="))

BIN
apps/bthrm/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

79
apps/bthrm/boot.js Normal file
View File

@ -0,0 +1,79 @@
(function() {
var log = function() {};//print
var gatt;
var status;
Bangle.isHRMOn = function() {
return (status=="searching" || status=="connecting") || (gatt!==undefined);
}
Bangle.setHRMPower = function(isOn, app) {
// Do app power handling
if (!app) app="?";
log("setHRMPower ->", isOn, app);
if (Bangle._PWR===undefined) Bangle._PWR={};
if (Bangle._PWR.HRM===undefined) Bangle._PWR.HRM=[];
if (isOn && !Bangle._PWR.HRM.includes(app)) Bangle._PWR.HRM.push(app);
if (!isOn && Bangle._PWR.HRM.includes(app)) Bangle._PWR.HRM = Bangle._PWR.HRM.filter(a=>a!=app);
isOn = Bangle._PWR.HRM.length;
// so now we know if we're really on
if (isOn) {
log("setHRMPower on", app);
if (!Bangle.isHRMOn()) {
log("HRM not already on");
status = "searching";
NRF.requestDevice({ filters: [{ services: ['180D'] }] }).then(function(device) {
log("Found device "+device.id);
status = "connecting";
device.on('gattserverdisconnected', function(reason) {
gatt = undefined;
});
return device.gatt.connect();
}).then(function(g) {
log("Connected");
gatt = g;
return gatt.getPrimaryService(0x180D);
}).then(function(service) {
return service.getCharacteristic(0x2A37);
}).then(function(characteristic) {
log("Got characteristic");
characteristic.on('characteristicvaluechanged', function(event) {
var dv = event.target.value;
var flags = dv.getUint8(0);
// 0 = 8 or 16 bit
// 1,2 = sensor contact
// 3 = energy expended shown
// 4 = RR interval
var bpm = (flags&1) ? (dv.getUint16(1)/100/* ? */) : dv.getUint8(1); // 8 or 16 bit
/* var idx = 2 + (flags&1); // index of next field
if (flags&8) idx += 2; // energy expended
if (flags&16) {
var interval = dv.getUint16(idx,1); // in milliseconds
}*/
Bangle.emit('HRM',{
bpm:bpm,
confidence:100
});
});
return characteristic.startNotifications();
}).then(function() {
log("Ready");
status = "ok";
}).catch(function(err) {
log("Error",err);
gatt = undefined;
status = "error";
});
}
} else { // not on
log("setHRMPower off", app);
if (gatt) {
log("HRM connected - disconnecting");
status = undefined;
try {gatt.disconnect();}catch(e) {
log("HRM disconnect error", e);
}
gatt = undefined;
}
}
};
})();

View File

@ -1,3 +1,4 @@
0.01: New App! 0.01: New App!
0.02: Show text if uncalibrated 0.02: Show text if uncalibrated
0.03: Eliminate flickering 0.03: Eliminate flickering
0.04: Fix for Bangle.js 2 and themes

View File

@ -1,60 +1,72 @@
var tg = Graphics.createArrayBuffer(120,20,1,{msb:true}); var W = g.getWidth();
var timg = { var M = W/2; // middle of screen
width:tg.getWidth(), // Angle buffer
height:tg.getHeight(), var AGS = W > 200 ? 160 : 120; // buffer size
bpp:1, var AGM = AGS/2; // midpoint/radius
buffer:tg.buffer var AGH = AGM-10; // hand size
}; var ag = Graphics.createArrayBuffer(AGS,AGS,2,{msb:true});
var ag = Graphics.createArrayBuffer(160,160,2,{msb:true});
var aimg = { var aimg = {
width:ag.getWidth(), width:ag.getWidth(),
height:ag.getHeight(), height:ag.getHeight(),
bpp:2, bpp:2,
buffer:ag.buffer, buffer:ag.buffer,
palette:new Uint16Array([0,0x03FF,0xF800,0x001F]) palette:new Uint16Array([
g.theme.bg,
g.toColor("#07f"),
g.toColor("#f00"),
g.toColor("#00f")])
}; };
ag.setColor(1); ag.setColor(1).fillCircle(AGM,AGM,AGM-1,AGM-1);
ag.fillCircle(80,80,79,79); ag.setColor(0).fillCircle(AGM,AGM,AGM-11,AGM-11);
ag.setColor(0);
ag.fillCircle(80,80,69,69);
function arrow(r,c) { function arrow(r,c) {
r=r*Math.PI/180; r=r*Math.PI/180;
var p = Math.PI/2; var p = Math.PI/2;
ag.setColor(c); ag.setColor(c).fillPoly([
ag.fillPoly([ AGM+AGH*Math.sin(r), AGM-AGH*Math.cos(r),
80+60*Math.sin(r), 80-60*Math.cos(r), AGM+10*Math.sin(r+p), AGM-10*Math.cos(r+p),
80+10*Math.sin(r+p), 80-10*Math.cos(r+p), AGM+10*Math.sin(r-p), AGM-10*Math.cos(r-p),
80+10*Math.sin(r-p), 80-10*Math.cos(r-p),
]); ]);
} }
var wasUncalibrated = false;
var oldHeading = 0; var oldHeading = 0;
Bangle.on('mag', function(m) { Bangle.on('mag', function(m) {
if (!Bangle.isLCDOn()) return; if (!Bangle.isLCDOn()) return;
tg.clear(); g.reset();
tg.setFont("6x8",1);
tg.setColor(1);
if (isNaN(m.heading)) { if (isNaN(m.heading)) {
tg.setFontAlign(0,-1); if (!wasUncalibrated) {
tg.setFont("6x8",1); g.clearRect(0,24,W,48);
tg.drawString("Uncalibrated",60,4); g.setFontAlign(0,-1).setFont("6x8");
tg.drawString("turn 360° around",60,12); g.drawString("Uncalibrated\nturn 360° around",M,24+4);
wasUncalibrated = true;
}
} else {
if (wasUncalibrated) {
g.clearRect(0,24,W,48);
wasUncalibrated = false;
}
g.setFontAlign(0,0).setFont("6x8",3);
var y = 36;
g.clearRect(M-40,y,M+40,y+24);
g.drawString(Math.round(m.heading),M,y,true);
} }
else {
tg.setFontAlign(0,0);
tg.setFont("6x8",2);
tg.drawString(Math.round(m.heading),60,12);
}
g.drawImage(timg,0,0,{scale:2});
ag.setColor(0); ag.setColor(0);
arrow(oldHeading,0); arrow(oldHeading,0);
arrow(oldHeading+180,0); arrow(oldHeading+180,0);
arrow(m.heading,2); arrow(m.heading,2);
arrow(m.heading+180,3); arrow(m.heading+180,3);
g.drawImage(aimg,40,50); g.drawImage(aimg,
(W-ag.getWidth())/2,
g.getHeight()-(ag.getHeight()+4));
oldHeading = m.heading; oldHeading = m.heading;
}); });
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.setCompassPower(1); Bangle.setCompassPower(1);
Bangle.setLCDPower(1);
Bangle.setLCDTimeout(0);

View File

@ -12,3 +12,4 @@
Generate scale based on defined minimum and maximum measurement Generate scale based on defined minimum and maximum measurement
Added background line on 50% to ease estimation of drawn values Added background line on 50% to ease estimation of drawn values
0.06: tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799) 0.06: tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799)
0.07: theme support

View File

@ -221,9 +221,9 @@ function graphRecord(n) {
if (tempCount == startLine) { if (tempCount == startLine) {
// generating rgaph in loop when reaching startLine to keep loading // generating rgaph in loop when reaching startLine to keep loading
// message on screen until graph can be drawn // message on screen until graph can be drawn
g.clear(). g.reset().clearRect(0,24,g.getWidth(),g.getHeight()).
// Home for Btn2 // Home for Btn2
setColor(1, 1, 1). setColor(g.theme.fg).
drawLine(220, 118, 227, 110). drawLine(220, 118, 227, 110).
drawLine(227, 110, 234, 118). drawLine(227, 110, 234, 118).
drawPoly([222,117,222,125,232,125,232,117], false). drawPoly([222,117,222,125,232,125,232,117], false).
@ -245,7 +245,7 @@ function graphRecord(n) {
// scale indicator line for 50% // scale indicator line for 50%
drawLine(GraphXZero - GraphMarkerOffset, GraphY100 + (GraphYZero - GraphY100)/2, GraphXZero, GraphY100 + (GraphYZero - GraphY100)/2). drawLine(GraphXZero - GraphMarkerOffset, GraphY100 + (GraphYZero - GraphY100)/2, GraphXZero, GraphY100 + (GraphYZero - GraphY100)/2).
// background line for 50% // background line for 50%
setColor(1, 1, 1). setColor(g.theme.fg).
drawLine(GraphXZero + 1, GraphY100 + (GraphYZero - GraphY100)/2, GraphXMax, GraphY100 + (GraphYZero - GraphY100)/2). drawLine(GraphXZero + 1, GraphY100 + (GraphYZero - GraphY100)/2, GraphXMax, GraphY100 + (GraphYZero - GraphY100)/2).
setFontAlign(1, -1, 0). setFontAlign(1, -1, 0).
setFont("Vector", 10); setFont("Vector", 10);
@ -303,7 +303,7 @@ function graphRecord(n) {
log("Finished rendering data"); log("Finished rendering data");
Bangle.buzz(200, 0.3); Bangle.buzz(200, 0.3);
g.flip(); g.flip();
setWatch(stop, BTN2, {edge:"falling", debounce:50, repeat:false}); setWatch(stop, (global.BTN2!==undefined)?BTN2:BTN1, {edge:"falling", debounce:50, repeat:false});
return; return;
} }

View File

@ -3,3 +3,4 @@
0.03: Fix timing issues, and use 1/2 scale to keep graph on screen 0.03: Fix timing issues, and use 1/2 scale to keep graph on screen
0.04: Update for new firmwares that have a 'HRM-raw' event 0.04: Update for new firmwares that have a 'HRM-raw' event
0.05: Tweaks for 'HRM-raw' handling 0.05: Tweaks for 'HRM-raw' handling
0.06: Add widgets

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("mEwghC/AH4AThnMAAXABJoMHBwgJJAAYMFAAIJLFxImCBJIuLABYuI4gXNNZFCC6AIFkZIQA4szC6vEmdMC60sC6nDmc8C6RDBC4irLC4gTBocymgGBoYXO4UyUwNEAYKrMC4ZEBUwNMVAR7LC4dDCoYBBSYJ7DoZQCC4kCmczkc0JIVM4UzmgaBAAQWD4AXBggJBJAIkBocs4c0BAQXJJARBD4c8oc8HAKZCI4gWCVAYXEJIJoCOovNC4cMUIQPB4RFBTAYAFIwapEC4JyCZAalHGAvCJYZYCVAYuIMIhjE5heGCwxhDMYTtIFw4wFoYsGFxIwF4YuRGAh7DFxxhGFyIYKCxqrGIpwwKFx4YGCyJJFCyQYDCygA/AH4AFA=")) require("heatshrink").decompress(atob("mEw4UA///g3yrv/7f+Jf4AJgNVoAEGAANVAAIEGCIQABoAEEBYMFAwVQAggLBioGCqgEEFIgAGFwdXBYw1Dr4LKrwLHIIVaBYxNDvXVBanVteVBZGVt+VKooLBq+19u1JItQgNW0vlBYIxEL4Ne1u18taGIN9BYUD1XvBYN62+q1a0D1d7ytttYLEWYV6BYNt93VEYKzCita6t59vqX4sFIgN70tqa4pUBTgO1vbvFgB0BKQNZawYACdYNeytdFwgwCBYJ2DFwQwCqoxBFwwABBYoKEGAKyDFwgwDFw4kDERBVDEQ4kEEQ4kDBRAYBERBuCNAoA/AA4="))

View File

@ -4,13 +4,14 @@ Bangle.setHRMPower(1);
var hrmInfo, hrmOffset = 0; var hrmInfo, hrmOffset = 0;
var hrmInterval; var hrmInterval;
var btm = g.getHeight()-1; var btm = g.getHeight()-1;
var lastHrmPt = []; // last xy coords we draw a line to
function onHRM(h) { function onHRM(h) {
if (counter!==undefined) { if (counter!==undefined) {
// the first time we're called remove // the first time we're called remove
// the countdown // the countdown
counter = undefined; counter = undefined;
g.clear(); g.clearRect(0,24,g.getWidth(),g.getHeight());
} }
hrmInfo = h; hrmInfo = h;
/* On 2v09 and earlier firmwares the only solution for realtime /* On 2v09 and earlier firmwares the only solution for realtime
@ -28,7 +29,7 @@ function onHRM(h) {
var px = g.getWidth()/2; var px = g.getWidth()/2;
g.setFontAlign(0,0); g.setFontAlign(0,0);
g.clearRect(0,24,239,80); g.clearRect(0,24,g.getWidth(),80);
g.setFont("6x8").drawString("Confidence "+hrmInfo.confidence+"%", px, 75); g.setFont("6x8").drawString("Confidence "+hrmInfo.confidence+"%", px, 75);
var str = hrmInfo.bpm; var str = hrmInfo.bpm;
g.setFontVector(40).drawString(str,px,45); g.setFontVector(40).drawString(str,px,45);
@ -43,17 +44,18 @@ Bangle.on('HRM-raw', function(v) {
hrmOffset++; hrmOffset++;
if (hrmOffset>g.getWidth()) { if (hrmOffset>g.getWidth()) {
hrmOffset=0; hrmOffset=0;
g.clearRect(0,80,239,239); g.clearRect(0,80,g.getWidth(),g.getHeight());
g.moveTo(-100,0); lastHrmPt = [-100,0];
} }
y = E.clip(btm-v.filt/4,btm-10,btm); y = E.clip(btm-v.filt/4,btm-10,btm);
g.setColor(1,0,0).fillRect(hrmOffset,btm, hrmOffset, y); g.setColor(1,0,0).fillRect(hrmOffset,btm, hrmOffset, y);
y = E.clip(170 - (v.raw/2),80,btm); y = E.clip(170 - (v.raw/2),80,btm);
g.setColor(g.theme.fg).lineTo(hrmOffset, y); g.setColor(g.theme.fg).drawLine(lastHrmPt[0],lastHrmPt[1],hrmOffset, y);
lastHrmPt = [hrmOffset, y];
if (counter !==undefined) { if (counter !==undefined) {
counter = undefined; counter = undefined;
g.clear(); g.clearRect(0,24,g.getWidth(),g.getHeight());
} }
}); });
@ -65,7 +67,10 @@ function countDown() {
setTimeout(countDown, 1000); setTimeout(countDown, 1000);
} }
} }
g.clear().setFont("6x8",2).setFontAlign(0,0); g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
g.reset().setFont("6x8",2).setFontAlign(0,0);
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16); g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
countDown(); countDown();
@ -79,13 +84,14 @@ function readHRM() {
if (!hrmInfo) return; if (!hrmInfo) return;
if (hrmOffset==0) { if (hrmOffset==0) {
g.clearRect(0,100,239,239); g.clearRect(0,100,g.getWidth(),g.getHeight());
g.moveTo(-100,0); lastHrmPt = [-100,0];
} }
for (var i=0;i<2;i++) { for (var i=0;i<2;i++) {
var a = hrmInfo.raw[hrmOffset]; var a = hrmInfo.raw[hrmOffset];
hrmOffset++; hrmOffset++;
y = E.clip(170 - (a*2),100,230); y = E.clip(170 - (a*2),100,230);
g.setColor(g.theme.fg).lineTo(hrmOffset, y); g.setColor(g.theme.fg).drawLine(lastHrmPt[0],lastHrmPt[1],hrmOffset, y);
lastHrmPt = [hrmOffset, y];
} }
} }

View File

@ -3,3 +3,4 @@
0.04: Make this clock do 12h and 24h 0.04: Make this clock do 12h and 24h
0.05: setUI, screen size changes 0.05: setUI, screen size changes
0.06: Use Bangle.setUI for button/launcher handling 0.06: Use Bangle.setUI for button/launcher handling
0.07: Update *on* the minute rather than every 15 secs

View File

@ -1,4 +1,3 @@
/* jshint esversion: 6 */
const big = g.getWidth()>200; const big = g.getWidth()>200;
const timeFontSize = big?6:5; const timeFontSize = big?6:5;
const dateFontSize = big?3:2; const dateFontSize = big?3:2;
@ -14,7 +13,19 @@ const yposGMT = xyCenter*1.9;
// Check settings for what type our clock should be // Check settings for what type our clock should be
var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]; var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"];
function drawSimpleClock() { // timeout used to update every minute
var drawTimeout;
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
function draw() {
// get date // get date
var d = new Date(); var d = new Date();
var da = d.toString().split(" "); var da = d.toString().split(" ");
@ -60,11 +71,18 @@ function drawSimpleClock() {
var gmt = da[5]; var gmt = da[5];
g.setFont(font, gmtFontSize); g.setFont(font, gmtFontSize);
g.drawString(gmt, xyCenter, yposGMT, true); g.drawString(gmt, xyCenter, yposGMT, true);
queueDraw();
} }
// handle switch display on by pressing BTN1 // Stop updates when LCD is off, restart when on
Bangle.on('lcdPower', function(on) { Bangle.on('lcdPower',on=>{
if (on) drawSimpleClock(); if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
}); });
// clean app screen // clean app screen
@ -74,8 +92,5 @@ Bangle.setUI("clock");
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
// refesh every 15 sec
setInterval(drawSimpleClock, 15E3);
// draw now // draw now
drawSimpleClock(); draw();

View File

@ -1,3 +1,4 @@
0.01: Modification of SimpleClock 0.04 to use Vectorfont 0.01: Modification of SimpleClock 0.04 to use Vectorfont
0.02: Use Bangle.setUI for button/launcher handling 0.02: Use Bangle.setUI for button/launcher handling
0.03: Scale to BangleJS 2 and add locale 0.03: Scale to BangleJS 2 and add locale
0.04: Fix rendering issue on real hardware, now update *on* the minute rather than every 15 secs

View File

@ -1,4 +1,3 @@
/* jshint esversion: 6 */
const locale = require("locale"); const locale = require("locale");
var timeFontSize; var timeFontSize;
@ -12,8 +11,7 @@ var yposDate;
var yposYear; var yposYear;
var yposGMT; var yposGMT;
switch (process.env.BOARD) { if (g.getWidth() > 200) {
case "EMSCRIPTEN":
timeFontSize = 65; timeFontSize = 65;
dateFontSize = 20; dateFontSize = 20;
gmtFontSize = 10; gmtFontSize = 10;
@ -22,8 +20,7 @@ switch (process.env.BOARD) {
yposDate = 130; yposDate = 130;
yposYear = 175; yposYear = 175;
yposGMT = 220; yposGMT = 220;
break; } else {
case "EMSCRIPTEN2":
timeFontSize = 48; timeFontSize = 48;
dateFontSize = 15; dateFontSize = 15;
gmtFontSize = 10; gmtFontSize = 10;
@ -32,12 +29,23 @@ switch (process.env.BOARD) {
yposDate = 95; yposDate = 95;
yposYear = 128; yposYear = 128;
yposGMT = 161; yposGMT = 161;
break;
} }
// Check settings for what type our clock should be // Check settings for what type our clock should be
var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]; var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"];
function drawSimpleClock() { // timeout used to update every minute
var drawTimeout;
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
function draw() {
g.clear(); g.clear();
Bangle.drawWidgets(); Bangle.drawWidgets();
@ -76,23 +84,26 @@ function drawSimpleClock() {
// draw gmt // draw gmt
g.setFont(font, gmtFontSize); g.setFont(font, gmtFontSize);
g.drawString(d.toString().match(/GMT[+-]\d+/), xyCenter, yposGMT, true); g.drawString(d.toString().match(/GMT[+-]\d+/), xyCenter, yposGMT, true);
queueDraw();
} }
// handle switch display on by pressing BTN1 // Stop updates when LCD is off, restart when on
Bangle.on('lcdPower', function(on) { Bangle.on('lcdPower',on=>{
if (on) drawSimpleClock(); if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
}); });
// Show launcher when button pressed
Bangle.setUI("clock");
// clean app screen // clean app screen
g.clear(); g.clear();
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
// refesh every 15 sec
setInterval(drawSimpleClock, 15E3);
// draw now // draw now
drawSimpleClock(); draw();
// Show launcher when button pressed
Bangle.setUI("clock");

View File

@ -0,0 +1 @@
0.01: New App!

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4UA///9nou30h/qJf8Ah/wBasK0ALhHcMBqALBgtABYsVqgLBAYILFqtUF4MVqoKEgoLFqALGEYQLFAwILEGAlV6tUlWoitXGAgLC1WqBYsBq+VqwLBAYPVMIUFBYN61Uq1oLBHgQLC1WohWqrwLDiteEIOggQDB2pICivqA4IFBlWq1YLCq/V9WkAoMa1YHBKQVf9XUAoMX1f1KgVVEYIpDEYILBLwIuBC4YFBMAMBLAILFLQILBrdV12UBYMW3VV8tAgt9q+2BYee6t9qEFuoLHroLBqte6wvDy+1ZoILBvdWSoeV9oLD+tfBYYFBBYTEBrq5CgN1aQNQgNVAALdEAAISBAYL1EfgISCAgIKDDAQSEAH4AQ"))

View File

@ -0,0 +1,256 @@
// get settings
var settings = require("Storage").readJSON("vernierrespirate.json",1)||{};
settings.vibrateBPM = settings.vibrateBPM||27;
// settings.vibrate; // undefined / "calculated" / "vernier"
function saveSettings() {
require("Storage").writeJSON("vernierrespirate.json", settings);
}
g.clear();
var graphHeight = g.getHeight()-100;
var last = {
time : Date.now(),
x : 0,
y : 24,
};
var avrValue;
var aboveAvr = false;
var lastBreath;
var lastBreaths = [];
var vibrateInterval;
function onMsg(txt) {
print(txt);
E.showMessage(txt);
}
function setVibrate(isOn) {
var wasOn = vibrateInterval!==undefined;
if (isOn == wasOn) return;
if (isOn) {
vibrateInterval = setInterval(function() {
Bangle.buzz();
}, 1000);
} else {
clearInterval(vibrateInterval);
vibrateInterval = undefined;
}
}
function onBreath() {
var t = Date.now();
if (lastBreath!==undefined) {
// time between breaths
var value = 60000 / (t-lastBreath);
// average of last 3
while (lastBreaths.length>=3) lastBreaths.shift(); // keep length small
lastBreaths.push(value);
value = E.sum(lastBreaths) / lastBreaths.length;
// draw value
g.reset();
g.clearRect(0,g.getHeight()-100,g.getWidth(),g.getHeight()-50);
g.setFont("6x8").setFontAlign(0,0);
g.drawString("Calculated measurement", g.getWidth()/2, g.getHeight()-95);
g.setFont("Vector",40).setFontAlign(0,0);
g.drawString(value.toFixed(2), g.getWidth()/2, g.getHeight()-70);
// set vibration IF we're doing it from our calculations
if (settings.vibrate == "calculated")
setVibrate(value > settings.vibrateBPM);
}
lastBreath = t;
}
function onData(n, value) {
g.reset();
if (n==2) {
function scale(v) {
return Math.max(graphHeight - (1+v*4),24);
}
if (avrValue==undefined) avrValue=value;
avrValue = avrValue*0.95 + value*0.05;
if (avrValue < 1) avrValue = 1;
if (value > avrValue) {
if (!aboveAvr) onBreath();
aboveAvr = true;
} else aboveAvr = false;
var t = Date.now();
var x = Math.round((t - last.time) / 100) // 10 per second
if (last.x>=g.getWidth()) {
x = 0;
last.x = 0;
last.time = t;
g.clearRect(0,24,g.getWidth(),graphHeight);
}
var y = scale(value);
g.setPixel(x, scale(avrValue), "#f00");
g.drawLine(last.x, last.y, x, y);
last.x = x;
last.y = y;
}
if (n==4) {
g.clearRect(0,g.getHeight()-50,g.getWidth(),g.getHeight());
g.setFont("6x8").setFontAlign(0,0);
g.drawString("GoDirect measurement", g.getWidth()/2, g.getHeight()-45);
g.setFont("Vector",40).setFontAlign(0,0);
g.drawString(value.toFixed(2), g.getWidth()/2, g.getHeight()-20);
// set vibration IF we're doing it from our calculations
if (settings.vibrate == "vernier")
setVibrate(value > settings.vibrateBPM);
}
Bangle.setLCDPower(1); // ensure LCD is on
}
function connect() {
var gatt, service, rx, tx;
var rollingCounter = 0xFF;
// any button to exit
Bangle.setUI("updown", function() {
setVibrate(false);
Bangle.buzz();
try {
if (gatt) gatt.disconnect();
} catch (e) {
}
setTimeout(mainMenu, 1000);
});
function sendCommand(subCommand) {
const command = new Uint8Array(4 + subCommand.length);
command.set(new Uint8Array(subCommand), 4);
// Populate the packet header bytes
command[0] = 0x58; // header
command[1] = command.length;
command[2] = --rollingCounter;
command[3] = E.sum(command) & 0xFF; // checksum
return tx.writeValue(command);
}
function firstSetBit(v) {
return v & -v;
}
function handleResponse(dv) {
//print(dv.buffer);
var resType = dv.getUint8(0);
if (resType==0x20) {
// [32, 25, 207, 216, 6, 6, 0, 2, 252, 128, 138, 7, 191, 0, 0, 192, 127, 128, 49, 8, 191, 0, 0, 192, 127])
// 6 = data type = real
// 6,0 = bit mask for sensors
// 2 = value count
if (dv.getUint8(4)!=6) return; //throw "Not float32 data";
var sensorIds = dv.getUint16(5, true);
// var count = dv.getUint8(7); doesn't seem right
var offs = 9;
while (sensorIds) {
var value = dv.getFloat32(offs, true);
var s = firstSetBit(sensorIds);
if (isFinite(value)) onData(s,value);
//else print(s,value);
sensorIds &= ~s;
offs += 4;
}
} else {
var cmd = dv.getUint8(4); // cmd
//print("CMD",dv.buffer);
}
}
onMsg("Searching...");
NRF.requestDevice({ filters: [{ namePrefix: 'GDX-RB' }] }).then(function(device) {
device.on("gattserverdisconnected", function() {
onMsg("Device disconnected");
});
onMsg("Found. Connecting...");
return device.gatt.connect({minInterval:20, maxInterval:20});
}).then(function(g) {
gatt = g;
return gatt.getPrimaryService("d91714ef-28b9-4f91-ba16-f0d9a604f112");
}).then(function(s) {
service = s;
return service.getCharacteristic("f4bf14a6-c7d5-4b6d-8aa8-df1a7c83adcb");
}).then(function(c) {
tx = c;
return service.getCharacteristic("b41e6675-a329-40e0-aa01-44d2f444babe");
}).then(function(c) {
rx = c;
rx.on('characteristicvaluechanged', function(event) {
//print("EVT",event.target.value.buffer);
handleResponse(event.target.value);
});
return rx.startNotifications();
}).then(function() {
onMsg("Init");
sendCommand([ // init
0x1a, 0xa5, 0x4a, 0x06,
0x49, 0x07, 0x48, 0x08,
0x47, 0x09, 0x46, 0x0a,
0x45, 0x0b, 0x44, 0x0c,
0x43, 0x0d, 0x42, 0x0e,
0x41,
]);
/*setTimeout(function() {
print("Set measurement period");
var us = 100000; // period in us
sendCommand([0x1b, 0xff, 0x00,
us & 255,
(us >> 8) & 255,
(us >> 16) & 255,
(us >> 24) & 255,
0x00,
0x00,
0x00,
0x00]);
}, 100);*/
/* setTimeout(function() {
print("Get sensor info");
sendCommand([0x51, 0]); // get sensor IDs
// returns [152, 10, 1, 39, 81, 253, 54, 0, 0, 0]
// 54 is the bit mask of available channels
//sendCommand([106, 16]); // get sensor info
}, 2000);*/
setTimeout(function() {
onMsg("Start measurements");
//https://github.com/VernierST/godirect-js/blob/main/src/Device.js#L588
var channels = 6; // data channels 4 and 2
sendCommand([ // start measurements
0x18, 0xff, 0x01, channels,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
]);
}, 500);
}).catch(function() {
onMsg("Connect Fail");
});
}
Bangle.loadWidgets();
Bangle.drawWidgets();
function mainMenu() {
var vibText = ["No","Calculated","Vernier"];
var vibValue = ["","calculated","vernier"];
E.showMenu({"":{title:"Respiration Belt"},
"< Back" : () => { saveSettings(); load(); },
"Connect" : () => { saveSettings(); E.showMenu(); connect(); },
"Vib" : {
value : Math.max(vibValue.indexOf(settings.vibrate),0),
format : v => vibText[v],
min:0,max:2,
onchange : v => { settings.vibrate=vibValue[v]; }
},
"BPM" : {
value : settings.vibrateBPM,
min:10,max:50,
onchange : v => { settings.vibrateBPM=v; }
}
});
}
mainMenu();

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

3
apps/widcom/ChangeLog Normal file
View File

@ -0,0 +1,3 @@
0.02: Works with light theme
Doesn't drain battery by updating every 2 secs
Fix alignment

View File

@ -1,30 +1,17 @@
(function(){ (function(){
//var img = E.toArrayBuffer(atob("FBSBAAAAAAAAA/wAf+AP/wH/2D/zw/w8PwfD9nw+b8Pg/Dw/w8/8G/+A//AH/gA/wAAAAAAA")); var cp = Bangle.setCompassPower;
//var img = E.toArrayBuffer(atob("GBiBAAB+AAP/wAeB4A4AcBgAGDAADHAADmABhmAHhsAfA8A/A8BmA8BmA8D8A8D4A2HgBmGABnAADjAADBgAGA4AcAeB4AP/wAB+AA==")); Bangle.setCompassPower = () => {
var img = E.toArrayBuffer(atob("FBSBAAH4AH/gHAODgBwwAMYABkAMLAPDwPg8CYPBkDwfA8PANDACYABjAAw4AcHAOAf+AB+A")); cp.apply(Bangle, arguments);
WIDGETS.compass.draw();
function draw() { };
WIDGETS.compass={area:"tr",width:24,draw:function() {
g.reset(); g.reset();
if (Bangle.isCompassOn()) { if (Bangle.isCompassOn()) {
g.setColor(1,0.8,0); // on = amber g.setColor(g.theme.dark ? "#FC0" : "#F00");
} else { } else {
g.setColor(0.3,0.3,0.3); // off = grey g.setColor(g.theme.dark ? "#333" : "#CCC");
} }
g.drawImage(img, 10+this.x, 2+this.var); g.drawImage(atob("FBSBAAH4AH/gHAODgBwwAMYABkAMLAPDwPg8CYPBkDwfA8PANDACYABjAAw4AcHAOAf+AB+A"), 2+this.x, 2+this.var);
} }};
var timerInterval;
Bangle.on('lcdPower', function(on) {
if (on) {
WIDGETS.compass.draw();
if (!timerInterval) timerInterval = setInterval(()=>WIDGETS.compass.draw(), 2000);
} else {
if (timerInterval) {
clearInterval(timerInterval);
timerInterval = undefined;
}
}
});
WIDGETS.compass={area:"tr",width:24,draw:draw};
})(); })();

View File

@ -2,3 +2,4 @@
0.02: Tweaks for variable size widget system 0.02: Tweaks for variable size widget system
0.03: Ensure redrawing works with variable size widget system 0.03: Ensure redrawing works with variable size widget system
0.04: tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799) 0.04: tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799)
0.05: Use new 'lock' event, not LCD (so it works on Bangle.js 2)

View File

@ -1,13 +1,38 @@
(() => { (() => {
var currentBPM = undefined; if (!Bangle.isLocked) return; // old firmware
var lastBPM = undefined; var currentBPM;
var firstBPM = true; // first reading since sensor turned on var lastBPM;
var isHRMOn = false;
function draw() { // turn on sensor when the LCD is unlocked
Bangle.on('lock', function(isLocked) {
if (!isLocked) {
Bangle.setHRMPower(1,"widhrm");
currentBPM = undefined;
WIDGETS["hrm"].draw();
} else {
Bangle.setHRMPower(0,"widhrm");
}
});
var hp = Bangle.setHRMPower;
Bangle.setHRMPower = () => {
hp.apply(Bangle, arguments);
isHRMOn = Bangle.isHRMOn();
WIDGETS["hrm"].draw();
};
Bangle.on('HRM',function(d) {
currentBPM = d.bpm;
lastBPM = currentBPM;
WIDGETS["hrm"].draw();
});
// add your widget
WIDGETS["hrm"]={area:"tl",width:24,draw:function() {
var width = 24; var width = 24;
g.reset(); g.reset();
g.setFont("6x8", 1); g.setFont("6x8", 1).setFontAlign(0, 0);
g.setFontAlign(0, 0);
g.clearRect(this.x,this.y+15,this.x+width,this.y+23); // erase background g.clearRect(this.x,this.y+15,this.x+width,this.y+23); // erase background
var bpm = currentBPM, isCurrent = true; var bpm = currentBPM, isCurrent = true;
if (bpm===undefined) { if (bpm===undefined) {
@ -16,36 +41,12 @@
} }
if (bpm===undefined) if (bpm===undefined)
bpm = "--"; bpm = "--";
g.setColor(isCurrent ? "#ffffff" : "#808080"); g.setColor(isCurrent ? g.theme.fg : "#808080");
g.drawString(bpm, this.x+width/2, this.y+19); g.drawString(bpm, this.x+width/2, this.y+19);
g.setColor(isCurrent ? "#ff0033" : "#808080"); g.setColor(isHRMOn ? "#ff0033" : "#808080");
g.drawImage(atob("CgoCAAABpaQ//9v//r//5//9L//A/+AC+AAFAA=="),this.x+(width-10)/2,this.y+1); g.drawImage(atob("CgoCAAABpaQ//9v//r//5//9L//A/+AC+AAFAA=="),this.x+(width-10)/2,this.y+1);
g.setColor(-1); g.setColor(-1);
} }};
// redraw when the LCD turns on Bangle.setHRMPower(!Bangle.isLocked(),"widhrm");
Bangle.on('lcdPower', function(on) {
if (on) {
Bangle.setHRMPower(1,"widhrm");
firstBPM = true;
currentBPM = undefined;
WIDGETS["hrm"].draw();
} else {
Bangle.setHRMPower(0,"widhrm");
}
});
Bangle.on('HRM',function(d) {
if (firstBPM)
firstBPM=false; // ignore the first one as it's usually rubbish
else {
currentBPM = d.bpm;
lastBPM = currentBPM;
}
WIDGETS["hrm"].draw();
});
Bangle.setHRMPower(Bangle.isLCDOn(),"widhrm");
// add your widget
WIDGETS["hrm"]={area:"tl",width:24,draw:draw};
})(); })();

View File

@ -1,3 +1,5 @@
0.01: First version 0.01: First version
0.02: Don't break if running on 2v08 firmware (just don't display anything) 0.02: Don't break if running on 2v08 firmware (just don't display anything)
0.03: Works with light theme
Doesn't drain battery by updating every 2 secs
fix alignment

View File

@ -1,28 +1,18 @@
(function(){ (function(){
if (!Bangle.isHRMOn) return; // old firmware if (!Bangle.isHRMOn) return; // old firmware
var hp = Bangle.setHRMPower;
Bangle.setHRMPower = () => {
hp.apply(Bangle, arguments);
WIDGETS.widhrt.draw();
};
function draw() { WIDGETS.widhrt={area:"tr",width:24,draw:function() {
g.reset(); g.reset();
if (Bangle.isHRMOn()) { if (Bangle.isHRMOn()) {
g.setColor(1,0,0); // on = red g.setColor("#f00"); // on = red
} else { } else {
g.setColor(0.3,0.3,0.3); // off = grey g.setColor(g.theme.dark ? "#333" : "#CCC"); // off = grey
} }
g.drawImage(atob("FhaBAAAAAAAAAAAAAcDgD8/AYeGDAwMMDAwwADDAAMOABwYAGAwAwBgGADAwAGGAAMwAAeAAAwAAAAAAAAAAAAA="), 10+this.x, 2+this.y); g.drawImage(atob("FhaBAAAAAAAAAAAAAcDgD8/AYeGDAwMMDAwwADDAAMOABwYAGAwAwBgGADAwAGGAAMwAAeAAAwAAAAAAAAAAAAA="), 1+this.x, 1+this.y);
} }};
var timerInterval;
Bangle.on('lcdPower', function(on) {
if (on) {
WIDGETS.widhrt.draw();
if (!timerInterval) timerInterval = setInterval(()=>WIDGETS["widhrt"].draw(), 2000);
} else {
if (timerInterval) {
clearInterval(timerInterval);
timerInterval = undefined;
}
}
});
WIDGETS.widhrt={area:"tr",width:24,draw:draw};
})(); })();

View File

@ -2,3 +2,5 @@
0.02: Update custom.html for refactor; add README 0.02: Update custom.html for refactor; add README
0.03: Update for larger secondary timezone display (#610) 0.03: Update for larger secondary timezone display (#610)
0.04: setUI, different screen sizes 0.04: setUI, different screen sizes
0.05: Now update *on* the minute rather than every 15 secs
Fix rendering of single extra timezone on Bangle.js 2

View File

@ -1,5 +1,3 @@
/* jshint esversion: 6 */
const big = g.getWidth()>200; const big = g.getWidth()>200;
// Font for primary time and date // Font for primary time and date
const primaryTimeFontSize = big?6:5; const primaryTimeFontSize = big?6:5;
@ -16,8 +14,13 @@ const xcol2 = g.getWidth() - xcol1;
const font = "6x8"; const font = "6x8";
/* TODO: we could totally use 'Layout' here and
avoid a whole bunch of hard-coded offsets */
const xyCenter = g.getWidth() / 2; const xyCenter = g.getWidth() / 2;
const yposTime = big ? 75 : 60; const yposTime = big ? 75 : 60;
const yposTime2 = yposTime + (big ? 100 : 60);
const yposDate = big ? 130 : 90; const yposDate = big ? 130 : 90;
const yposWorld = big ? 170 : 120; const yposWorld = big ? 170 : 120;
@ -29,41 +32,52 @@ var offsets = require("Storage").readJSON("worldclock.settings.json") || [];
// TESTING CODE // TESTING CODE
// Used to test offset array values during development. // Used to test offset array values during development.
// Uncomment to override secondary offsets value // Uncomment to override secondary offsets value
/*
// const mockOffsets = { const mockOffsets = {
// zeroOffsets: [], zeroOffsets: [],
// oneOffset: [["UTC", 0]], oneOffset: [["UTC", 0]],
// twoOffsets: [ twoOffsets: [
// ["Tokyo", 9], ["Tokyo", 9],
// ["UTC", 0], ["UTC", 0],
// ], ],
// fourOffsets: [ fourOffsets: [
// ["Tokyo", 9], ["Tokyo", 9],
// ["UTC", 0], ["UTC", 0],
// ["Denver", -7], ["Denver", -7],
// ["Miami", -5], ["Miami", -5],
// ], ],
// fiveOffsets: [ fiveOffsets: [
// ["Tokyo", 9], ["Tokyo", 9],
// ["UTC", 0], ["UTC", 0],
// ["Denver", -7], ["Denver", -7],
// ["Chicago", -6], ["Chicago", -6],
// ["Miami", -5], ["Miami", -5],
// ], ],
// }; };*/
// Uncomment one at a time to test various offsets array scenarios // Uncomment one at a time to test various offsets array scenarios
// offsets = mockOffsets.zeroOffsets; // should render nothing below primary time //offsets = mockOffsets.zeroOffsets; // should render nothing below primary time
// offsets = mockOffsets.oneOffset; // should render larger in two rows //offsets = mockOffsets.oneOffset; // should render larger in two rows
// offsets = mockOffsets.twoOffsets; // should render two in columns //offsets = mockOffsets.twoOffsets; // should render two in columns
// offsets = mockOffsets.fourOffsets; // should render in columns //offsets = mockOffsets.fourOffsets; // should render in columns
// offsets = mockOffsets.fiveOffsets; // should render first four in columns //offsets = mockOffsets.fiveOffsets; // should render first four in columns
// END TESTING CODE // END TESTING CODE
// Check settings for what type our clock should be // Check settings for what type our clock should be
//var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]; //var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"];
var secondInterval;
// timeout used to update every minute
var drawTimeout;
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
function doublenum(x) { function doublenum(x) {
return x < 10 ? "0" + x : "" + x; return x < 10 ? "0" + x : "" + x;
@ -73,7 +87,7 @@ function getCurrentTimeFromOffset(dt, offset) {
return new Date(dt.getTime() + offset * 60 * 60 * 1000); return new Date(dt.getTime() + offset * 60 * 60 * 1000);
} }
function drawSimpleClock() { function draw() {
// get date // get date
var d = new Date(); var d = new Date();
var da = d.toString().split(" "); var da = d.toString().split(" ");
@ -111,9 +125,9 @@ function drawSimpleClock() {
// For a single secondary timezone, draw it bigger and drop time zone to second line // For a single secondary timezone, draw it bigger and drop time zone to second line
const xOffset = 30; const xOffset = 30;
g.setFont(font, secondaryTimeFontSize); g.setFont(font, secondaryTimeFontSize);
g.drawString(`${hours}:${minutes}`, xyCenter, yposTime + 100, true); g.drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
g.setFont(font, secondaryTimeZoneFontSize); g.setFont(font, secondaryTimeZoneFontSize);
g.drawString(offset[OFFSET_TIME_ZONE], xyCenter, yposTime + 130, true); g.drawString(offset[OFFSET_TIME_ZONE], xyCenter, yposTime2 + 30, true);
// draw Day, name of month, Date // draw Day, name of month, Date
g.setFont(font, secondaryTimeZoneFontSize); g.setFont(font, secondaryTimeZoneFontSize);
@ -132,6 +146,8 @@ function drawSimpleClock() {
g.drawString(`${hours}:${minutes}`, xcol2, yposWorld + index * 15, true); g.drawString(`${hours}:${minutes}`, xcol2, yposWorld + index * 15, true);
} }
}); });
queueDraw();
} }
// clean app screen // clean app screen
@ -141,18 +157,15 @@ Bangle.setUI("clock");
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
// refesh every 15 sec when screen is on // Stop updates when LCD is off, restart when on
Bangle.on("lcdPower", (on) => { Bangle.on('lcdPower',on=>{
if (secondInterval) clearInterval(secondInterval);
secondInterval = undefined;
if (on) { if (on) {
secondInterval = setInterval(drawSimpleClock, 15e3); draw(); // draw immediately, queue redraw
drawSimpleClock(); // draw immediately } else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
} }
}); });
// draw now and every 15 sec until display goes off // draw now
drawSimpleClock(); draw();
if (Bangle.isLCDOn()) {
secondInterval = setInterval(drawSimpleClock, 15e3);
}

2
core

@ -1 +1 @@
Subproject commit bc5b1284f41b0fcfdd264e1e2f12872e0b18c479 Subproject commit 3a2c706b4cdf02e5365b191103c80d587b3ace5a

View File

@ -35,6 +35,21 @@
top: 36px; top: 36px;
left: -24px; left: -24px;
} }
.btn-favourite { .btn.btn-favourite { color: red; }
color: red; .btn.btn-favourite:hover { color: red; }
.icon.icon-emulator { text-indent: 0px; } /*override spectre*/
.icon.icon-emulator::before {
content: "\01F5B5";
}
.icon.icon-favourite { text-indent: 0px; } /*override spectre*/
.icon.icon-favourite::before {
content: "\02661"; /* 0x2661 = empty heart; 0x2606 = empty star */
}
.icon.icon-favourite-active::before {
content: "\02665"; /* 0x2665 = solid heart; 0x2605 = solid star */
}
.icon.icon-interface {text-indent: 0px;font-size: 130%;vertical-align: -30%;} /*override spectre*/
.icon.icon-interface::before {
content: "\01F5AB";
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
css/spectre.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
["boot","launch","s7clk","setting","about","widbat","widbt","widlock","widid"]

View File

@ -133,8 +133,8 @@ window.addEventListener('load', (event) => {
}); });
}); });
// Hook onto device chooser dropdown
window.addEventListener('load', (event) => { window.addEventListener('load', (event) => {
// Hook onto device chooser dropdown
htmlToArray(document.querySelectorAll(".devicetype-nav .menu-item")).forEach(button => { htmlToArray(document.querySelectorAll(".devicetype-nav .menu-item")).forEach(button => {
button.addEventListener("click", event => { button.addEventListener("click", event => {
var a = event.target; var a = event.target;
@ -144,4 +144,20 @@ window.addEventListener('load', (event) => {
document.querySelector(".devicetype-nav span").innerText = a.innerText; document.querySelector(".devicetype-nav span").innerText = a.innerText;
}); });
}); });
// Button to install all default apps in one go
document.getElementById("installdefault").addEventListener("click",event=>{
getInstalledApps().then(() => {
if (device.id == "BANGLEJS")
return httpGet("defaultapps_banglejs.json");
if (device.id == "BANGLEJS2")
return httpGet("defaultapps_banglejs2.json");
throw new Error("Unknown device "+device.id);
}).then(json=>{
return installMultipleApps(JSON.parse(json), "default");
}).catch(err=>{
Progress.hide({sticky:true});
showToast("App Install failed, "+err,"error");
});
});
}); });