Merge pull request #1229 from halemmerich/bthrm

BLE Heart Rate Monitor improvements
pull/1242/head
Gordon Williams 2022-01-07 10:14:32 +00:00 committed by GitHub
commit 53d7103272
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 180 additions and 18 deletions

View File

@ -1040,16 +1040,19 @@
"id": "bthrm",
"name": "Bluetooth Heart Rate Monitor",
"shortName": "BT HRM",
"version": "0.01",
"version": "0.02",
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
"icon": "app.png",
"type": "boot",
"type": "app",
"tags": "health,bluetooth",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"bthrm.app.js","url":"bthrm.js"},
{"name":"bthrm.recorder.js","url":"recorder.js"},
{"name":"bthrm.boot.js","url":"boot.js"},
{"name":"bthrm.img","url":"app-icon.js","evaluate":true}
{"name":"bthrm.img","url":"app-icon.js","evaluate":true},
{"name":"bthrm.settings.js","url":"settings.js"}
]
},
{

View File

@ -1 +1,4 @@
0.01: New App!
0.02: Make overriding the HRM event optional
Emit BTHRM event for external sensor
Add recorder app plugin

View File

@ -3,23 +3,42 @@
var gatt;
var status;
Bangle.isHRMOn = function() {
var origIsHRMOn = Bangle.isHRMOn;
Bangle.isBTHRMOn = function(){
return (status=="searching" || status=="connecting") || (gatt!==undefined);
}
Bangle.setHRMPower = function(isOn, app) {
Bangle.isHRMOn = function() {
var settings = require('Storage').readJSON("bthrm.json", true) || {};
print(settings);
if (settings.enabled && !settings.replace){
return origIsHRMOn();
} else if (settings.enabled && settings.replace){
return Bangle.isBTHRMOn();
}
return origIsHRMOn() || Bangle.isBTHRMOn();
}
Bangle.setBTHRMPower = function(isOn, app) {
var settings = require('Storage').readJSON("bthrm.json", true) || {};
// Do app power handling
if (!app) app="?";
log("setHRMPower ->", isOn, app);
log("setBTHRMPower ->", 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;
if (Bangle._PWR.BTHRM===undefined) Bangle._PWR.BTHRM=[];
if (isOn && !Bangle._PWR.BTHRM.includes(app)) Bangle._PWR.BTHRM.push(app);
if (!isOn && Bangle._PWR.BTHRM.includes(app)) Bangle._PWR.BTHRM = Bangle._PWR.BTHRM.filter(a=>a!=app);
isOn = Bangle._PWR.BTHRM.length;
// so now we know if we're really on
if (isOn) {
log("setHRMPower on", app);
if (!Bangle.isHRMOn()) {
log("HRM not already on");
log("setBTHRMPower on", app);
if (!Bangle.isBTHRMOn()) {
log("BTHRM not already on");
status = "searching";
NRF.requestDevice({ filters: [{ services: ['180D'] }] }).then(function(device) {
log("Found device "+device.id);
@ -49,7 +68,11 @@
if (flags&16) {
var interval = dv.getUint16(idx,1); // in milliseconds
}*/
Bangle.emit('HRM',{
var eventName = settings.replace ? "HRM" : "BTHRM";
Bangle.emit(eventName, {
bpm:bpm,
confidence:100
});
@ -65,15 +88,27 @@
});
}
} else { // not on
log("setHRMPower off", app);
log("setBTHRMPower off", app);
if (gatt) {
log("HRM connected - disconnecting");
log("BTHRM connected - disconnecting");
status = undefined;
try {gatt.disconnect();}catch(e) {
log("HRM disconnect error", e);
log("BTHRM disconnect error", e);
}
gatt = undefined;
}
}
};
var origSetHRMPower = Bangle.setHRMPower;
Bangle.setHRMPower = function(isOn, app) {
var settings = require('Storage').readJSON("bthrm.json", true) || {};
if (settings.enabled || !isOn){
Bangle.setBTHRMPower(isOn, app);
}
if (settings.enabled && !settings.replace || !isOn){
origSetHRMPower(isOn, app);
}
}
})();

61
apps/bthrm/bthrm.js Normal file
View File

@ -0,0 +1,61 @@
var btm = g.getHeight()-1;
var eventInt = null;
var eventBt = null;
var counterInt = 0;
var counterBt = 0;
function draw(y, event, type, counter) {
var px = g.getWidth()/2;
g.reset();
g.setFontAlign(0,0);
g.clearRect(0,y,g.getWidth(),y+80);
if (type == null || event == null || counter == 0) return;
var str = event.bpm + "";
g.setFontVector(40).drawString(str,px,y+20);
str = "Confidence: " + event.confidence;
g.setFontVector(12).drawString(str,px,y+50);
str = "Event: " + type;
g.setFontVector(12).drawString(str,px,y+60);
}
function onBtHrm(e) {
print("Event for BT " + JSON.stringify(e));
counterBt += 5;
eventBt = e;
}
function onHrm(e) {
print("Event for Int " + JSON.stringify(e));
counterInt += 5;
eventInt = e;
}
Bangle.on('BTHRM', onBtHrm);
Bangle.on('HRM', onHrm);
Bangle.setHRMPower(1,'bthrm')
Bangle.setBTHRMPower(1,'bthrm')
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);
function drawInt(){
counterInt--;
if (counterInt < 0) counterInt = 0;
if (counterInt > 5) counterInt = 5;
draw(24, eventInt, "HRM", counterInt);
}
function drawBt(){
counterBt--;
if (counterBt < 0) counterBt = 0;
if (counterBt > 5) counterBt = 5;
draw(100, eventBt, "BTHRM", counterBt);
}
var interval = setInterval(drawInt, 1000);
var interval = setInterval(drawBt, 1000);

27
apps/bthrm/recorder.js Normal file
View File

@ -0,0 +1,27 @@
(function(recorders) {
recorders.bthrm = function() {
var bpm = 0;
function onHRM(h) {
bpm = h.bpm;
}
return {
name : "BTHR",
fields : ["BT Heartrate"],
getValues : () => {
result = [bpm];
bpm = 0;
return result;
},
start : () => {
Bangle.on('BTHRM', onHRM);
Bangle.setBTHRMPower(1,"recorder");
},
stop : () => {
Bangle.removeListener('BTHRM', onHRM);
Bangle.setBTHRMPower(0,"recorder");
},
draw : (x,y) => g.setColor(Bangle.isBTHRMOn()?"#00f":"#88f").drawImage(atob("DAwBAAAAMMeef+f+f+P8H4DwBgAA"),x,y)
};
}
})

33
apps/bthrm/settings.js Normal file
View File

@ -0,0 +1,33 @@
(function(back) {
var FILE = "bthrm.json";
var settings = Object.assign({
enabled: true,
replace: true,
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
E.showMenu({
'': { 'title': 'Bluetooth HRM' },
'< Back': back,
'Use BT HRM': {
value: !!settings.enabled,
format: v => settings.enabled ? "On" : "Off",
onchange: v => {
settings.enabled = v;
writeSettings();
}
},
'Use HRM event': {
value: !!settings.replace,
format: v => settings.replace ? "On" : "Off",
onchange: v => {
settings.replace = v;
writeSettings();
}
}
});
})