diff --git a/apps/bthrm/ChangeLog b/apps/bthrm/ChangeLog index 58d002f22..41eec666a 100644 --- a/apps/bthrm/ChangeLog +++ b/apps/bthrm/ChangeLog @@ -20,3 +20,4 @@ 0.07: Recorder icon only blue if values actually arive Adds some preset modes and a custom one Restructure the settings menu +0.08: Allow scanning for devices in settings diff --git a/apps/bthrm/boot.js b/apps/bthrm/boot.js index 339f6f8c6..3a1f1cc4c 100644 --- a/apps/bthrm/boot.js +++ b/apps/bthrm/boot.js @@ -23,7 +23,10 @@ } 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){ @@ -361,7 +364,13 @@ var promise; 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){ log("Add " + settings.gracePeriodRequest + "ms grace period after request"); @@ -488,11 +497,15 @@ if (gatt) { if (gatt.connected){ log("Disconnect with gatt: ", gatt); - gatt.disconnect().then(()=>{ - log("Successful disconnect"); - }).catch((e)=>{ - log("Error during disconnect", e); - }); + try{ + gatt.disconnect().then(()=>{ + log("Successful disconnect"); + }).catch((e)=>{ + log("Error during disconnect promise", e); + }); + } catch (e){ + log("Error during disconnect attempt", e); + } } } } diff --git a/apps/bthrm/metadata.json b/apps/bthrm/metadata.json index 1c21269e2..b35ebd6af 100644 --- a/apps/bthrm/metadata.json +++ b/apps/bthrm/metadata.json @@ -2,7 +2,7 @@ "id": "bthrm", "name": "Bluetooth Heart Rate Monitor", "shortName": "BT HRM", - "version": "0.07", + "version": "0.08", "description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.", "icon": "app.png", "type": "app", diff --git a/apps/bthrm/settings.js b/apps/bthrm/settings.js index beefb00e9..4b564d670 100644 --- a/apps/bthrm/settings.js +++ b/apps/bthrm/settings.js @@ -17,54 +17,73 @@ var settings; readSettings(); - var mainmenu = { - '': { 'title': 'Bluetooth HRM' }, - '< Back': back, - 'Mode': { - value: 0 | settings.mode, - min: 0, - max: 3, - format: v => ["Off", "Default", "Both", "Custom"][v], - onchange: v => { - settings.mode = v; - switch (v){ - case 0: - writeSettings("enabled",false); - break; - case 1: - writeSettings("enabled",true); - writeSettings("replace",true); - writeSettings("debuglog",false); - writeSettings("startWithHrm",true); - writeSettings("allowFallback",true); - writeSettings("fallbackTimeout",10); - break; - case 2: - writeSettings("enabled",true); - writeSettings("replace",false); - writeSettings("debuglog",false); - writeSettings("startWithHrm",false); - writeSettings("allowFallback",false); - break; - case 3: - writeSettings("enabled",true); - writeSettings("replace",settings.custom_replace); - writeSettings("debuglog",settings.custom_debuglog); - writeSettings("startWithHrm",settings.custom_startWithHrm); - writeSettings("allowFallback",settings.custom_allowFallback); - writeSettings("fallbackTimeout",settings.custom_fallbackTimeout); - break; + function buildMainMenu(){ + var mainmenu = { + '': { 'title': 'Bluetooth HRM' }, + '< Back': back, + 'Mode': { + value: 0 | settings.mode, + min: 0, + max: 3, + format: v => ["Off", "Default", "Both", "Custom"][v], + onchange: v => { + settings.mode = v; + switch (v){ + case 0: + writeSettings("enabled",false); + break; + case 1: + writeSettings("enabled",true); + writeSettings("replace",true); + writeSettings("debuglog",false); + writeSettings("startWithHrm",true); + writeSettings("allowFallback",true); + writeSettings("fallbackTimeout",10); + break; + case 2: + writeSettings("enabled",true); + writeSettings("replace",false); + writeSettings("debuglog",false); + writeSettings("startWithHrm",false); + writeSettings("allowFallback",false); + break; + case 3: + writeSettings("enabled",true); + writeSettings("replace",settings.custom_replace); + writeSettings("debuglog",settings.custom_debuglog); + writeSettings("startWithHrm",settings.custom_startWithHrm); + writeSettings("allowFallback",settings.custom_allowFallback); + writeSettings("fallbackTimeout",settings.custom_fallbackTimeout); + break; + } + 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 = { '' : { title: "Debug"}, - '< Back': function() { E.showMenu(mainmenu); }, + '< Back': function() { E.showMenu(buildMainMenu()); }, 'Alert on disconnect': { value: !!settings.warnDisconnect, format: v => settings.warnDisconnect ? "On" : "Off", @@ -81,10 +100,41 @@ }, '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 = { '' : { title: "Custom mode"}, - '< Back': function() { E.showMenu(mainmenu); }, + '< Back': function() { E.showMenu(buildMainMenu()); }, 'Replace HRM': { value: !!settings.custom_replace, format: v => settings.custom_replace ? "On" : "Off", @@ -165,7 +215,7 @@ var submenu = { '' : { title: "Grace periods"}, - '< Back': function() { E.showMenu(mainmenu); }, + '< Back': function() { E.showMenu(buildMainMenu()); }, 'Request': { value: settings.gracePeriodRequest, min: 0, @@ -208,5 +258,5 @@ } }; - E.showMenu(mainmenu); + E.showMenu(buildMainMenu()); }) diff --git a/apps/widbaroalarm/ChangeLog b/apps/widbaroalarm/ChangeLog index ec66c5568..c12cc0d65 100644 --- a/apps/widbaroalarm/ChangeLog +++ b/apps/widbaroalarm/ChangeLog @@ -1 +1,2 @@ 0.01: Initial version +0.02: Do not warn multiple times for the same exceedance diff --git a/apps/widbaroalarm/README.md b/apps/widbaroalarm/README.md index b92a51050..fdc239170 100644 --- a/apps/widbaroalarm/README.md +++ b/apps/widbaroalarm/README.md @@ -2,10 +2,8 @@ Get a notification when the pressure reaches defined thresholds. -![Screenshot](screenshot.png) ## Settings - * Interval: check interval of sensor data in minutes. 0 to disable automatic check. * Low alarm: Toggle low alarm * Low threshold: Warn when pressure drops below this value diff --git a/apps/widbaroalarm/metadata.json b/apps/widbaroalarm/metadata.json index 0976df531..9c58a41ab 100644 --- a/apps/widbaroalarm/metadata.json +++ b/apps/widbaroalarm/metadata.json @@ -1,8 +1,8 @@ { "id": "widbaroalarm", - "name": "Barometer alarm widget", - "shortName": "Barometer alarm", - "version": "0.01", + "name": "Barometer Alarm Widget", + "shortName": "Barometer Alarm", + "version": "0.02", "description": "A widget that can alarm on when the pressure reaches defined thresholds.", "icon": "widget.png", "type": "widget", diff --git a/apps/widbaroalarm/settings.js b/apps/widbaroalarm/settings.js index 1455072e4..bea6319d1 100644 --- a/apps/widbaroalarm/settings.js +++ b/apps/widbaroalarm/settings.js @@ -36,7 +36,7 @@ value: settings.min, min: 600, max: 1000, - step: 10, + step: 5, onchange: x => save("min", x), }, "High alarm": { @@ -48,9 +48,9 @@ }, "High threshold": { value: settings.max, - min: 1000, + min: 700, max: 1100, - step: 10, + step: 5, onchange: x => save("max", x), }, "Drop alarm": { diff --git a/apps/widbaroalarm/widget.js b/apps/widbaroalarm/widget.js index 7279a963a..5d62156eb 100644 --- a/apps/widbaroalarm/widget.js +++ b/apps/widbaroalarm/widget.js @@ -1,185 +1,239 @@ (function() { - let medianPressure; - let threeHourAvrPressure; - let currentPressures = []; + let medianPressure; + let threeHourAvrPressure; + let currentPressures = []; - const LOG_FILE = "widbaroalarm.log.json"; - const SETTINGS_FILE = "widbaroalarm.json"; - const storage = require('Storage'); - let settings = Object.assign( + const LOG_FILE = "widbaroalarm.log.json"; + const SETTINGS_FILE = "widbaroalarm.json"; + const storage = require('Storage'); + + let settings; + + function loadSettings() { + settings = Object.assign( storage.readJSON("widbaroalarm.default.json", true) || {}, storage.readJSON(SETTINGS_FILE, true) || {} ); + } - function setting(key) { - return settings[key]; + loadSettings(); + + + 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) { - if (body == undefined) return; + function didWeAlreadyWarn(key) { + return setting(key) == undefined || setting(key) > 0; + } - 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(); - } - } + function checkForAlarms(pressure) { + if (pressure == undefined || pressure <= 0) return; let alreadyWarned = false; - function checkForAlarms(pressure) { - if (pressure == undefined || pressure <= 0) return; + const ts = Math.round(Date.now() / 1000); // seconds + const d = { + "ts": ts, + "p": pressure + }; - const ts = Math.round(Date.now() / 1000); // seconds - const d = { - "ts": ts, - "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) { + // 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.push(d); - // write data to storage - storage.writeJSON(LOG_FILE, history3); - - if (setting("lowalarm") && pressure <= setting("min")) { - showAlarm("Pressure low: " + Math.round(pressure) + " hPa"); - alreadyWarned = true; + if (setting("lowalarm")) { + // Is below the alarm threshold? + if (pressure <= setting("min")) { + if (!didWeAlreadyWarn("lastLowWarningTs")) { + showAlarm("Pressure low: " + Math.round(pressure) + " hPa"); + saveSetting("lastLowWarningTs", ts); + alreadyWarned = true; + } + } else { + saveSetting("lastLowWarningTs", 0); } - if (setting("highalarm") && pressure >= setting("max")) { - showAlarm("Pressure high: " + Math.round(pressure) + " hPa"); - alreadyWarned = true; + } else { + saveSetting("lastLowWarningTs", 0); + } + + 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) { - // 3h change detection - const drop3halarm = setting("drop3halarm"); - const raise3halarm = setting("raise3halarm"); - if (drop3halarm > 0 || raise3halarm > 0) { - // we need at least 30min of data for reliable detection - if (history3[0]["ts"] > ts - (30 * 60)) { - return; - } + if (!alreadyWarned) { + // 3h change detection + const drop3halarm = setting("drop3halarm"); + const raise3halarm = setting("raise3halarm"); + if (drop3halarm > 0 || raise3halarm > 0) { + // we need at least 30min of data for reliable detection + if (history3[0]["ts"] > ts - (30 * 60)) { + return; + } - // Get oldest entry: - const oldestPressure = history3[0]["p"]; - if (oldestPressure != undefined && oldestPressure > 0) { - const diff = oldestPressure - pressure; + // Get oldest entry: + const oldestPressure = history3[0]["p"]; + if (oldestPressure != undefined && oldestPressure > 0) { + const diff = oldestPressure - pressure; - // drop alarm - if (drop3halarm > 0 && oldestPressure > pressure) { - if (Math.abs(diff) > drop3halarm) { + // drop alarm + if (drop3halarm > 0 && oldestPressure > pressure) { + if (Math.abs(diff) > drop3halarm) { + if (!didWeAlreadyWarn("lastDropWarningTs")) { showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " + Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure drop"); + saveSetting("lastDropWarningTs", ts); + } + } else { + saveSetting("lastDropWarningTs", 0); } + } else { + saveSetting("lastDropWarningTs", 0); } // raise alarm if (raise3halarm > 0 && oldestPressure < pressure) { if (Math.abs(diff) > raise3halarm) { - showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " + - Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure raise"); + if (!didWeAlreadyWarn("lastRaiseWarningTs")) { + 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) { - if (data) { - const pressure = Math.round(data.pressure); - if (pressure == undefined || pressure <= 0) return; - 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); + function baroHandler(data) { + if (data) { + const pressure = Math.round(data.pressure); + if (pressure == undefined || pressure <= 0) return; + currentPressures.push(pressure); } } -} -if (global.WIDGETS != undefined && typeof WIDGETS === "object") { - WIDGETS["baroalarm"] = { - width: setting("show") ? 24 : 0, - reload: reload, - area: "tr", - draw: draw - }; -} + /* + 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 = []; -// Let's delay the first check a bit -setTimeout(function() { -check(); -if (interval > 0) { - setInterval(check, interval * 60000); -} -}, 5000); + 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() { + 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); })(); diff --git a/core b/core index a7a80a13f..affb0b15b 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit a7a80a13fa187a4ff5f89669992babca2d95812c +Subproject commit affb0b15b41eb35a1548373831af7001bad64435