1
0
Fork 0

Merge branch 'espruino:master' into master

master
Peer David 2022-03-10 21:50:07 +01:00 committed by GitHub
commit 3a03b8cf7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 321 additions and 204 deletions

View File

@ -20,3 +20,4 @@
0.07: Recorder icon only blue if values actually arive 0.07: Recorder icon only blue if values actually arive
Adds some preset modes and a custom one Adds some preset modes and a custom one
Restructure the settings menu Restructure the settings menu
0.08: Allow scanning for devices in settings

View File

@ -23,7 +23,10 @@
} }
function getCache(){ function getCache(){
return require('Storage').readJSON("bthrm.cache.json", true) || {}; var cache = require('Storage').readJSON("bthrm.cache.json", true) || {};
if (settings.btname && settings.btname == cache.name) return cache;
clearCache();
return {};
} }
function addNotificationHandler(characteristic){ function addNotificationHandler(characteristic){
@ -361,7 +364,13 @@
var promise; var promise;
if (!device){ if (!device){
promise = NRF.requestDevice({ filters: serviceFilters }); var filters = serviceFilters;
if (settings.btname){
log("Configured device name", settings.btname);
filters = [{name: settings.btname}];
}
log("Requesting device with filters", filters);
promise = NRF.requestDevice({ filters: filters });
if (settings.gracePeriodRequest){ if (settings.gracePeriodRequest){
log("Add " + settings.gracePeriodRequest + "ms grace period after request"); log("Add " + settings.gracePeriodRequest + "ms grace period after request");
@ -488,11 +497,15 @@
if (gatt) { if (gatt) {
if (gatt.connected){ if (gatt.connected){
log("Disconnect with gatt: ", gatt); log("Disconnect with gatt: ", gatt);
gatt.disconnect().then(()=>{ try{
log("Successful disconnect"); gatt.disconnect().then(()=>{
}).catch((e)=>{ log("Successful disconnect");
log("Error during disconnect", e); }).catch((e)=>{
}); log("Error during disconnect promise", e);
});
} catch (e){
log("Error during disconnect attempt", e);
}
} }
} }
} }

View File

@ -2,7 +2,7 @@
"id": "bthrm", "id": "bthrm",
"name": "Bluetooth Heart Rate Monitor", "name": "Bluetooth Heart Rate Monitor",
"shortName": "BT HRM", "shortName": "BT HRM",
"version": "0.07", "version": "0.08",
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.", "description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",

View File

@ -17,54 +17,73 @@
var settings; var settings;
readSettings(); readSettings();
var mainmenu = { function buildMainMenu(){
'': { 'title': 'Bluetooth HRM' }, var mainmenu = {
'< Back': back, '': { 'title': 'Bluetooth HRM' },
'Mode': { '< Back': back,
value: 0 | settings.mode, 'Mode': {
min: 0, value: 0 | settings.mode,
max: 3, min: 0,
format: v => ["Off", "Default", "Both", "Custom"][v], max: 3,
onchange: v => { format: v => ["Off", "Default", "Both", "Custom"][v],
settings.mode = v; onchange: v => {
switch (v){ settings.mode = v;
case 0: switch (v){
writeSettings("enabled",false); case 0:
break; writeSettings("enabled",false);
case 1: break;
writeSettings("enabled",true); case 1:
writeSettings("replace",true); writeSettings("enabled",true);
writeSettings("debuglog",false); writeSettings("replace",true);
writeSettings("startWithHrm",true); writeSettings("debuglog",false);
writeSettings("allowFallback",true); writeSettings("startWithHrm",true);
writeSettings("fallbackTimeout",10); writeSettings("allowFallback",true);
break; writeSettings("fallbackTimeout",10);
case 2: break;
writeSettings("enabled",true); case 2:
writeSettings("replace",false); writeSettings("enabled",true);
writeSettings("debuglog",false); writeSettings("replace",false);
writeSettings("startWithHrm",false); writeSettings("debuglog",false);
writeSettings("allowFallback",false); writeSettings("startWithHrm",false);
break; writeSettings("allowFallback",false);
case 3: break;
writeSettings("enabled",true); case 3:
writeSettings("replace",settings.custom_replace); writeSettings("enabled",true);
writeSettings("debuglog",settings.custom_debuglog); writeSettings("replace",settings.custom_replace);
writeSettings("startWithHrm",settings.custom_startWithHrm); writeSettings("debuglog",settings.custom_debuglog);
writeSettings("allowFallback",settings.custom_allowFallback); writeSettings("startWithHrm",settings.custom_startWithHrm);
writeSettings("fallbackTimeout",settings.custom_fallbackTimeout); writeSettings("allowFallback",settings.custom_allowFallback);
break; writeSettings("fallbackTimeout",settings.custom_fallbackTimeout);
break;
}
writeSettings("mode",v);
} }
writeSettings("mode",v);
} }
}, };
'Custom Mode': function() { E.showMenu(submenu_custom); },
'Debug': function() { E.showMenu(submenu_debug); } if (settings.btname){
}; var name = "Clear " + settings.btname;
mainmenu[name] = function() {
E.showPrompt("Clear current device name?").then((r)=>{
if (r) {
writeSettings("btname",undefined);
}
E.showMenu(buildMainMenu());
});
};
}
mainmenu["BLE Scan"] = ()=> createMenuFromScan();
mainmenu["Custom Mode"] = function() { E.showMenu(submenu_custom); };
mainmenu.Debug = function() { E.showMenu(submenu_debug); };
return mainmenu;
}
var submenu_debug = { var submenu_debug = {
'' : { title: "Debug"}, '' : { title: "Debug"},
'< Back': function() { E.showMenu(mainmenu); }, '< Back': function() { E.showMenu(buildMainMenu()); },
'Alert on disconnect': { 'Alert on disconnect': {
value: !!settings.warnDisconnect, value: !!settings.warnDisconnect,
format: v => settings.warnDisconnect ? "On" : "Off", format: v => settings.warnDisconnect ? "On" : "Off",
@ -82,9 +101,40 @@
'Grace periods': function() { E.showMenu(submenu_grace); } 'Grace periods': function() { E.showMenu(submenu_grace); }
}; };
function createMenuFromScan(){
E.showMenu();
E.showMessage("Scanning");
var submenu_scan = {
'' : { title: "Scan"},
'< Back': function() { E.showMenu(buildMainMenu()); }
};
var packets=10;
var scanStart=Date.now();
NRF.setScan(function(d) {
packets--;
if (packets<=0 || Date.now() - scanStart > 5000){
NRF.setScan();
E.showMenu(submenu_scan);
} else if (d.name){
print("Found device", d);
submenu_scan[d.name] = function(){
E.showPrompt("Set "+d.name+"?").then((r)=>{
if (r) {
writeSettings("btname",d.name);
}
E.showMenu(buildMainMenu());
});
};
}
}, { filters: [{services: [ "180d" ]}]});
}
var submenu_custom = { var submenu_custom = {
'' : { title: "Custom mode"}, '' : { title: "Custom mode"},
'< Back': function() { E.showMenu(mainmenu); }, '< Back': function() { E.showMenu(buildMainMenu()); },
'Replace HRM': { 'Replace HRM': {
value: !!settings.custom_replace, value: !!settings.custom_replace,
format: v => settings.custom_replace ? "On" : "Off", format: v => settings.custom_replace ? "On" : "Off",
@ -165,7 +215,7 @@
var submenu = { var submenu = {
'' : { title: "Grace periods"}, '' : { title: "Grace periods"},
'< Back': function() { E.showMenu(mainmenu); }, '< Back': function() { E.showMenu(buildMainMenu()); },
'Request': { 'Request': {
value: settings.gracePeriodRequest, value: settings.gracePeriodRequest,
min: 0, min: 0,
@ -208,5 +258,5 @@
} }
}; };
E.showMenu(mainmenu); E.showMenu(buildMainMenu());
}) })

View File

@ -1 +1,2 @@
0.01: Initial version 0.01: Initial version
0.02: Do not warn multiple times for the same exceedance

View File

@ -2,10 +2,8 @@
Get a notification when the pressure reaches defined thresholds. Get a notification when the pressure reaches defined thresholds.
![Screenshot](screenshot.png)
## Settings ## Settings
* Interval: check interval of sensor data in minutes. 0 to disable automatic check. * Interval: check interval of sensor data in minutes. 0 to disable automatic check.
* Low alarm: Toggle low alarm * Low alarm: Toggle low alarm
* Low threshold: Warn when pressure drops below this value * Low threshold: Warn when pressure drops below this value

View File

@ -1,8 +1,8 @@
{ {
"id": "widbaroalarm", "id": "widbaroalarm",
"name": "Barometer alarm widget", "name": "Barometer Alarm Widget",
"shortName": "Barometer alarm", "shortName": "Barometer Alarm",
"version": "0.01", "version": "0.02",
"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",

View File

@ -36,7 +36,7 @@
value: settings.min, value: settings.min,
min: 600, min: 600,
max: 1000, max: 1000,
step: 10, step: 5,
onchange: x => save("min", x), onchange: x => save("min", x),
}, },
"High alarm": { "High alarm": {
@ -48,9 +48,9 @@
}, },
"High threshold": { "High threshold": {
value: settings.max, value: settings.max,
min: 1000, min: 700,
max: 1100, max: 1100,
step: 10, step: 5,
onchange: x => save("max", x), onchange: x => save("max", x),
}, },
"Drop alarm": { "Drop alarm": {

View File

@ -1,185 +1,239 @@
(function() { (function() {
let medianPressure; let medianPressure;
let threeHourAvrPressure; let threeHourAvrPressure;
let currentPressures = []; let currentPressures = [];
const LOG_FILE = "widbaroalarm.log.json"; const LOG_FILE = "widbaroalarm.log.json";
const SETTINGS_FILE = "widbaroalarm.json"; const SETTINGS_FILE = "widbaroalarm.json";
const storage = require('Storage'); const storage = require('Storage');
let settings = Object.assign(
let settings;
function loadSettings() {
settings = Object.assign(
storage.readJSON("widbaroalarm.default.json", true) || {}, storage.readJSON("widbaroalarm.default.json", true) || {},
storage.readJSON(SETTINGS_FILE, true) || {} storage.readJSON(SETTINGS_FILE, true) || {}
); );
}
function setting(key) { loadSettings();
return settings[key];
function setting(key) {
return settings[key];
}
function saveSetting(key, value) {
settings[key] = value;
storage.write(SETTINGS_FILE, settings);
}
const interval = setting("interval");
let history3 = storage.readJSON(LOG_FILE, true) || []; // history of recent 3 hours
function showAlarm(body, title) {
if (body == undefined) return;
require("notify").show({
title: title || "Pressure",
body: body,
icon: require("heatshrink").decompress(atob("jEY4cA///gH4/++mkK30kiWC4H8x3BGDmSGgYDCgmSoEAg3bsAIDpAIFkmSpMAm3btgIFDQwIGNQpTYkAIJwAHEgMoCA0JgMEyBnBCAW3KoQQDhu3oAIH5JnDBAW24IIBEYm2EYwACBCIACA"))
});
if (setting("buzz") &&
!(storage.readJSON('setting.json', 1) || {}).quiet) {
Bangle.buzz();
} }
const interval = setting("interval"); }
let history3 = storage.readJSON(LOG_FILE, true) || []; // history of recent 3 hours
function showAlarm(body, title) { function didWeAlreadyWarn(key) {
if (body == undefined) return; return setting(key) == undefined || setting(key) > 0;
}
require("notify").show({ function checkForAlarms(pressure) {
title: title || "Pressure", if (pressure == undefined || pressure <= 0) return;
body: body,
icon: require("heatshrink").decompress(atob("jEY4cA///gH4/++mkK30kiWC4H8x3BGDmSGgYDCgmSoEAg3bsAIDpAIFkmSpMAm3btgIFDQwIGNQpTYkAIJwAHEgMoCA0JgMEyBnBCAW3KoQQDhu3oAIH5JnDBAW24IIBEYm2EYwACBCIACA"))
});
if (setting("buzz") &&
!(storage.readJSON('setting.json', 1) || {}).quiet) {
Bangle.buzz();
}
}
let alreadyWarned = false; let alreadyWarned = false;
function checkForAlarms(pressure) { const ts = Math.round(Date.now() / 1000); // seconds
if (pressure == undefined || pressure <= 0) return; const d = {
"ts": ts,
"p": pressure
};
const ts = Math.round(Date.now() / 1000); // seconds // delete entries older than 3h
const d = { for (let i = 0; i < history3.length; i++) {
"ts": ts, if (history3[i]["ts"] < ts - (3 * 60 * 60)) {
"p": pressure
};
// delete entries older than 3h
for (let i = 0; i < history3.length; i++) {
if (history3[i]["ts"] < ts - (3 * 60 * 60)) {
history3.shift();
}
}
// delete oldest entries until we have max 50
while (history3.length > 50) {
history3.shift(); history3.shift();
} }
}
// delete oldest entries until we have max 50
while (history3.length > 50) {
history3.shift();
}
history3.push(d); if (setting("lowalarm")) {
// write data to storage // Is below the alarm threshold?
storage.writeJSON(LOG_FILE, history3); if (pressure <= setting("min")) {
if (!didWeAlreadyWarn("lastLowWarningTs")) {
if (setting("lowalarm") && pressure <= setting("min")) { showAlarm("Pressure low: " + Math.round(pressure) + " hPa");
showAlarm("Pressure low: " + Math.round(pressure) + " hPa"); saveSetting("lastLowWarningTs", ts);
alreadyWarned = true; alreadyWarned = true;
}
} else {
saveSetting("lastLowWarningTs", 0);
} }
if (setting("highalarm") && pressure >= setting("max")) { } else {
showAlarm("Pressure high: " + Math.round(pressure) + " hPa"); saveSetting("lastLowWarningTs", 0);
alreadyWarned = true; }
if (setting("highalarm")) {
// Is above the alarm threshold?
if (pressure >= setting("max")) {
if (!didWeAlreadyWarn("lastHighWarningTs")) {
showAlarm("Pressure high: " + Math.round(pressure) + " hPa");
saveSetting("lastHighWarningTs", ts);
alreadyWarned = true;
}
} else {
saveSetting("lastHighWarningTs", 0);
} }
} else {
saveSetting("lastHighWarningTs", 0);
}
if (!alreadyWarned) { if (!alreadyWarned) {
// 3h change detection // 3h change detection
const drop3halarm = setting("drop3halarm"); const drop3halarm = setting("drop3halarm");
const raise3halarm = setting("raise3halarm"); const raise3halarm = setting("raise3halarm");
if (drop3halarm > 0 || raise3halarm > 0) { if (drop3halarm > 0 || raise3halarm > 0) {
// we need at least 30min of data for reliable detection // we need at least 30min of data for reliable detection
if (history3[0]["ts"] > ts - (30 * 60)) { if (history3[0]["ts"] > ts - (30 * 60)) {
return; return;
} }
// Get oldest entry: // Get oldest entry:
const oldestPressure = history3[0]["p"]; const oldestPressure = history3[0]["p"];
if (oldestPressure != undefined && oldestPressure > 0) { if (oldestPressure != undefined && oldestPressure > 0) {
const diff = oldestPressure - pressure; const diff = oldestPressure - pressure;
// drop alarm // drop alarm
if (drop3halarm > 0 && oldestPressure > pressure) { if (drop3halarm > 0 && oldestPressure > pressure) {
if (Math.abs(diff) > drop3halarm) { if (Math.abs(diff) > drop3halarm) {
if (!didWeAlreadyWarn("lastDropWarningTs")) {
showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " + showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " +
Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure drop"); Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure drop");
saveSetting("lastDropWarningTs", ts);
}
} else {
saveSetting("lastDropWarningTs", 0);
} }
} else {
saveSetting("lastDropWarningTs", 0);
} }
// raise alarm // raise alarm
if (raise3halarm > 0 && oldestPressure < pressure) { if (raise3halarm > 0 && oldestPressure < pressure) {
if (Math.abs(diff) > raise3halarm) { if (Math.abs(diff) > raise3halarm) {
showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " + if (!didWeAlreadyWarn("lastRaiseWarningTs")) {
Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure raise"); showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " +
Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure raise");
saveSetting("lastRaiseWarningTs", ts);
}
} else {
saveSetting("lastRaiseWarningTs", 0);
}
} else {
saveSetting("lastRaiseWarningTs", 0);
} }
} }
} }
} }
history3.push(d);
// write data to storage
storage.writeJSON(LOG_FILE, history3);
// calculate 3h average for widget
let sum = 0;
for (let i = 0; i < history3.length; i++) {
sum += history3[i]["p"];
}
threeHourAvrPressure = sum / history3.length;
} }
// calculate 3h average for widget
let sum = 0;
for (let i = 0; i < history3.length; i++) {
sum += history3[i]["p"];
}
threeHourAvrPressure = sum / history3.length;
}
function baroHandler(data) {
function baroHandler(data) { if (data) {
if (data) { const pressure = Math.round(data.pressure);
const pressure = Math.round(data.pressure); if (pressure == undefined || pressure <= 0) return;
if (pressure == undefined || pressure <= 0) return; currentPressures.push(pressure);
currentPressures.push(pressure);
}
}
/*
turn on barometer power
take 5 measurements
sort the results
take the middle one (median)
turn off barometer power
*/
function check() {
Bangle.setBarometerPower(true, "widbaroalarm");
setTimeout(function() {
currentPressures = [];
Bangle.getPressure().then(baroHandler);
Bangle.getPressure().then(baroHandler);
Bangle.getPressure().then(baroHandler);
Bangle.getPressure().then(baroHandler);
Bangle.getPressure().then(baroHandler);
setTimeout(function() {
Bangle.setBarometerPower(false, "widbaroalarm");
currentPressures.sort();
// take median value
medianPressure = currentPressures[3];
checkForAlarms(medianPressure);
}, 1000);
}, 500);
}
function reload() {
check();
}
function draw() {
g.reset();
if (setting("show") && medianPressure != undefined) {
g.setFont("6x8", 1).setFontAlign(1, 0);
g.drawString(Math.round(medianPressure), this.x + 24, this.y + 6);
if (threeHourAvrPressure != undefined && threeHourAvrPressure > 0) {
g.drawString(Math.round(threeHourAvrPressure), this.x + 24, this.y + 6 + 10);
} }
} }
}
if (global.WIDGETS != undefined && typeof WIDGETS === "object") { /*
WIDGETS["baroalarm"] = { turn on barometer power
width: setting("show") ? 24 : 0, take 5 measurements
reload: reload, sort the results
area: "tr", take the middle one (median)
draw: draw turn off barometer power
}; */
} function check() {
Bangle.setBarometerPower(true, "widbaroalarm");
setTimeout(function() {
currentPressures = [];
// Let's delay the first check a bit Bangle.getPressure().then(baroHandler);
setTimeout(function() { Bangle.getPressure().then(baroHandler);
check(); Bangle.getPressure().then(baroHandler);
if (interval > 0) { Bangle.getPressure().then(baroHandler);
setInterval(check, interval * 60000); Bangle.getPressure().then(baroHandler);
}
}, 5000); setTimeout(function() {
Bangle.setBarometerPower(false, "widbaroalarm");
currentPressures.sort();
// take median value
medianPressure = currentPressures[3];
checkForAlarms(medianPressure);
}, 1000);
}, 500);
}
function reload() {
check();
}
function draw() {
if (global.WIDGETS != undefined && typeof WIDGETS === "object") {
WIDGETS["baroalarm"] = {
width: setting("show") ? 24 : 0,
reload: reload,
area: "tr",
draw: draw
};
}
g.reset();
if (setting("show") && medianPressure != undefined) {
g.setFont("6x8", 1).setFontAlign(1, 0);
g.drawString(Math.round(medianPressure), this.x + 24, this.y + 6);
if (threeHourAvrPressure != undefined && threeHourAvrPressure > 0) {
g.drawString(Math.round(threeHourAvrPressure), this.x + 24, this.y + 6 + 10);
}
}
}
// Let's delay the first check a bit
setTimeout(function() {
check();
if (interval > 0) {
setInterval(check, interval * 60000);
}
}, 1000);
})(); })();

2
core

@ -1 +1 @@
Subproject commit a7a80a13fa187a4ff5f89669992babca2d95812c Subproject commit affb0b15b41eb35a1548373831af7001bad64435