From 70204b6c273dda08015ec87f9245b194bda5053e Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Thu, 27 Oct 2022 20:22:06 +0200 Subject: [PATCH 1/8] sensortools - Fix scoping --- apps/sensortools/boot.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/sensortools/boot.js b/apps/sensortools/boot.js index 82c2036a9..219878766 100644 --- a/apps/sensortools/boot.js +++ b/apps/sensortools/boot.js @@ -1,11 +1,11 @@ -(function() { - var settings = Object.assign( +{ + let settings = Object.assign( require('Storage').readJSON("sensortools.default.json", true) || {}, require('Storage').readJSON("sensortools.json", true) || {} ); - var log = function(text, param) { - var logline = new Date().toISOString() + " - " + "Sensortools - " + text; + let log = function(text, param) { + let logline = new Date().toISOString() + " - " + "Sensortools - " + text; if (param) logline += ": " + JSON.stringify(param); print(logline); }; @@ -15,7 +15,7 @@ log("Enabled"); const POWER_DELAY = 10000; - var onEvents = []; + let onEvents = []; Bangle.sensortoolsOrigOn = Bangle.on; Bangle.sensortoolsOrigEmit = Bangle.emit; @@ -55,7 +55,7 @@ } }; - var createPowerFunction = function(type, name, origPower) { + let createPowerFunction = function(type, name, origPower) { return function(isOn, app) { if (type == "nop") { return true; @@ -123,17 +123,17 @@ } function degrees(a) { - var d = a*180/Math.PI; + let d = a*180/Math.PI; return (d+360)%360; } function bearing(a,b){ if (!a || !b || !a.lon || !a.lat || !b.lon || !b.lat) return Infinity; - var delta = radians(b.lon-a.lon); - var alat = radians(a.lat); - var blat = radians(b.lat); - var y = Math.sin(delta) * Math.cos(blat); - var x = Math.cos(alat)*Math.sin(blat) - + let delta = radians(b.lon-a.lon); + let alat = radians(a.lat); + let blat = radians(b.lat); + let y = Math.sin(delta) * Math.cos(blat); + let x = Math.cos(alat)*Math.sin(blat) - Math.sin(alat)*Math.cos(blat)*Math.cos(delta); return Math.round(degrees(Math.atan2(y, x))); } @@ -348,4 +348,4 @@ } } } -})(); +} From 54101956bf531bc9a87ae8ccbaca0e22199180ac Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 30 Oct 2022 14:12:38 +0100 Subject: [PATCH 2/8] sensortools - Move code into lib for faster boot if disabled --- apps/sensortools/boot.js | 352 +-------------------------------- apps/sensortools/lib.js | 348 ++++++++++++++++++++++++++++++++ apps/sensortools/metadata.json | 1 + 3 files changed, 350 insertions(+), 351 deletions(-) create mode 100644 apps/sensortools/lib.js diff --git a/apps/sensortools/boot.js b/apps/sensortools/boot.js index 219878766..fa210f1a0 100644 --- a/apps/sensortools/boot.js +++ b/apps/sensortools/boot.js @@ -1,351 +1 @@ -{ - let settings = Object.assign( - require('Storage').readJSON("sensortools.default.json", true) || {}, - require('Storage').readJSON("sensortools.json", true) || {} - ); - - let log = function(text, param) { - let logline = new Date().toISOString() + " - " + "Sensortools - " + text; - if (param) logline += ": " + JSON.stringify(param); - print(logline); - }; - - if (settings.enabled) { - - log("Enabled"); - const POWER_DELAY = 10000; - - let onEvents = []; - - Bangle.sensortoolsOrigOn = Bangle.on; - Bangle.sensortoolsOrigEmit = Bangle.emit; - Bangle.sensortoolsOrigRemoveListener = Bangle.removeListener; - - Bangle.on = function(name, callback) { - if (onEvents[name]) { - log("Redirecting listener for", name, "to", name + "_mod"); - Bangle.sensortoolsOrigOn(name + "_mod", callback); - Bangle.sensortoolsOrigOn(name, (e) => { - log("Redirected event for", name, "to", name + "_mod"); - Bangle.sensortoolsOrigEmit(name + "_mod", onEvents[name](e)); - }); - } else { - log("Pass through on call for", name, callback); - Bangle.sensortoolsOrigOn(name, callback); - } - }; - - Bangle.removeListener = function(name, callback) { - if (onEvents[name]) { - log("Removing augmented listener for", name, onEvents[name]); - Bangle.sensortoolsOrigRemoveListener(name + "_mod", callback); - } else { - log("Pass through remove listener for", name); - Bangle.sensortoolsOrigRemoveListener(name, callback); - } - }; - - Bangle.emit = function(name, event) { - if (onEvents[name]) { - log("Augmenting emit call for", name, onEvents[name]); - Bangle.sensortoolsOrigEmit(name + "_mod", event); - } else { - log("Pass through emit call for", name); - Bangle.sensortoolsOrigEmit(name, event); - } - }; - - let createPowerFunction = function(type, name, origPower) { - return function(isOn, app) { - if (type == "nop") { - return true; - }else if (type == "delay") { - setTimeout(() => { - origPower(isOn, app); - }, POWER_DELAY); - } else if (type == "on") { - origPower(1, "sensortools_force_on"); - } else if (type == "passthrough"){ - origPower(isOn, "app"); - } else if (type == "emulate"){ - if (!Bangle._PWR) Bangle._PWR={}; - if (!Bangle._PWR[name]) Bangle._PWR[name] = []; - if (!app) app="?"; - if (isOn) { - Bangle._PWR[name].push(app); - return true; - } else { - Bangle._PWR[name] = Bangle._PWR[name].filter((v)=>{return v == app;}); - return false; - } - } - }; - }; - - if (settings.hrm && settings.hrm.enabled) { - log("HRM", settings.hrm); - if (settings.hrm.power) { - log("HRM power"); - Bangle.sensortoolsOrigSetHRMPower = Bangle.setHRMPower; - Bangle.setHRMPower = createPowerFunction(settings.hrm.power, "HRM", Bangle.sensortoolsOrigSetHRMPower); - } - if (settings.hrm.mode == "modify") { - if (settings.hrm.name == "bpmtrippled") { - onEvents.HRM = (e) => { - return { - bpm: e.bpm * 3 - }; - }; - } - } else if (settings.hrm.mode == "emulate") { - if (settings.hrm.name == "sin") { - setInterval(() => { - Bangle.sensortoolsOrigEmit(60 + 3 * Math.sin(Date.now() / 10000)); - }, 1000); - } - } - } - if (settings.gps && settings.gps.enabled) { - log("GPS", settings.gps); - let modGps = function(dataProvider) { - Bangle.getGPSFix = dataProvider; - setInterval(() => { - Bangle.sensortoolsOrigEmit("GPS", dataProvider()); - }, 1000); - }; - if (settings.gps.power) { - Bangle.sensortoolsOrigSetGPSPower = Bangle.setGPSPower; - Bangle.setGPSPower = createPowerFunction(settings.gps.power, "GPS", Bangle.sensortoolsOrigSetGPSPower); - } - if (settings.gps.mode == "emulate") { - function radians(a) { - return a*Math.PI/180; - } - - function degrees(a) { - let d = a*180/Math.PI; - return (d+360)%360; - } - - function bearing(a,b){ - if (!a || !b || !a.lon || !a.lat || !b.lon || !b.lat) return Infinity; - let delta = radians(b.lon-a.lon); - let alat = radians(a.lat); - let blat = radians(b.lat); - let y = Math.sin(delta) * Math.cos(blat); - let x = Math.cos(alat)*Math.sin(blat) - - Math.sin(alat)*Math.cos(blat)*Math.cos(delta); - return Math.round(degrees(Math.atan2(y, x))); - } - - function interpolate(a,b,progress){ - return { - lat: a.lat * progress + b.lat * (1-progress), - lon: a.lon * progress + b.lon * (1-progress), - ele: a.ele * progress + b.ele * (1-progress) - } - } - - function getSquareRoute(){ - return [ - {lat:"47.2577411",lon:"11.9927442",ele:2273}, - {lat:"47.266761",lon:"11.9926673",ele:2166}, - {lat:"47.2667605",lon:"12.0059511",ele:2245}, - {lat:"47.2577516",lon:"12.0059925",ele:1994} - ]; - } - function getSquareRouteFuzzy(){ - return [ - {lat:"47.2578455",lon:"11.9929891",ele:2265}, - {lat:"47.258592",lon:"11.9923341",ele:2256}, - {lat:"47.2594506",lon:"11.9927412",ele:2230}, - {lat:"47.2603323",lon:"11.9924949",ele:2219}, - {lat:"47.2612056",lon:"11.9928175",ele:2199}, - {lat:"47.2621002",lon:"11.9929817",ele:2182}, - {lat:"47.2629025",lon:"11.9923915",ele:2189}, - {lat:"47.2637828",lon:"11.9926486",ele:2180}, - {lat:"47.2646733",lon:"11.9928167",ele:2191}, - {lat:"47.2655617",lon:"11.9930357",ele:2185}, - {lat:"47.2662862",lon:"11.992252",ele:2186}, - {lat:"47.2669305",lon:"11.993173",ele:2166}, - {lat:"47.266666",lon:"11.9944419",ele:2171}, - {lat:"47.2667579",lon:"11.99576",ele:2194}, - {lat:"47.2669409",lon:"11.9970579",ele:2207}, - {lat:"47.2666562",lon:"11.9983128",ele:2212}, - {lat:"47.2666027",lon:"11.9996335",ele:2262}, - {lat:"47.2667245",lon:"12.0009395",ele:2278}, - {lat:"47.2668457",lon:"12.002256",ele:2297}, - {lat:"47.2666126",lon:"12.0035373",ele:2303}, - {lat:"47.2664554",lon:"12.004841",ele:2251}, - {lat:"47.2669461",lon:"12.005948",ele:2245}, - {lat:"47.2660877",lon:"12.006323",ele:2195}, - {lat:"47.2652729",lon:"12.0057552",ele:2163}, - {lat:"47.2643926",lon:"12.0060123",ele:2131}, - {lat:"47.2634978",lon:"12.0058302",ele:2095}, - {lat:"47.2626129",lon:"12.0060759",ele:2066}, - {lat:"47.2617325",lon:"12.0058188",ele:2037}, - {lat:"47.2608668",lon:"12.0061784",ele:1993}, - {lat:"47.2600155",lon:"12.0057392",ele:1967}, - {lat:"47.2591203",lon:"12.0058233",ele:1949}, - {lat:"47.2582307",lon:"12.0059718",ele:1972}, - {lat:"47.2578014",lon:"12.004804",ele:2011}, - {lat:"47.2577232",lon:"12.0034834",ele:2044}, - {lat:"47.257745",lon:"12.0021656",ele:2061}, - {lat:"47.2578682",lon:"12.0008597",ele:2065}, - {lat:"47.2577082",lon:"11.9995526",ele:2071}, - {lat:"47.2575917",lon:"11.9982348",ele:2102}, - {lat:"47.2577401",lon:"11.996924",ele:2147}, - {lat:"47.257715",lon:"11.9956061",ele:2197}, - {lat:"47.2578996",lon:"11.9943081",ele:2228} - ]; - } - - if (settings.gps.name == "staticfix") { - modGps(() => { return { - "lat": 52, - "lon": 8, - "alt": 100, - "speed": 10, - "course": 12, - "time": Date.now(), - "satellites": 7, - "fix": 1, - "hdop": 1 - };}); - } else if (settings.gps.name.includes("route")) { - let route; - let interpSteps; - if (settings.gps.name == "routeFuzzy"){ - route = getSquareRouteFuzzy(); - interpSteps = 5; - } else { - route = getSquareRoute(); - interpSteps = 50; - } - - let step = 0; - let routeIndex = 0; - modGps(() => { - let newIndex = (routeIndex + 1)%route.length; - - let result = { - "speed": Math.random() * 3 + 2, - "time": Date.now(), - "satellites": Math.floor(Math.random()*5)+3, - "fix": 1, - "hdop": Math.floor(Math.random(30)+1) - }; - - let oldPos = route[routeIndex]; - if (step != 0){ - oldPos = interpolate(route[routeIndex], route[newIndex], E.clip(0,1,step/interpSteps)); - } - let newPos = route[newIndex]; - if (step < interpSteps - 1){ - newPos = interpolate(route[routeIndex], route[newIndex], E.clip(0,1,(step+1)%interpSteps/interpSteps)); - } - - if (step == interpSteps - 1){ - let followingIndex = (routeIndex + 2)%route.length; - newPos = interpolate(route[newIndex], route[followingIndex], E.clip(0,1,1/interpSteps)); - } - - result.lat = oldPos.lat; - result.lon = oldPos.lon; - result.alt = oldPos.ele; - - result.course = bearing(oldPos,newPos); - - step++; - if (step == interpSteps){ - routeIndex = (routeIndex + 1) % route.length; - step = 0; - } - return result; - }); - } else if (settings.gps.name == "nofix") { - modGps(() => { return { - "lat": NaN, - "lon": NaN, - "alt": NaN, - "speed": NaN, - "course": NaN, - "time": Date.now(), - "satellites": 2, - "fix": 0, - "hdop": NaN - };}); - } else if (settings.gps.name == "changingfix") { - let currentSpeed=1; - let currentLat=20; - let currentLon=10; - let currentCourse=10; - let currentAlt=-100; - let currentSats=5; - modGps(() => { - currentLat += 0.1; - if (currentLat > 50) currentLat = 20; - currentLon += 0.1; - if (currentLon > 20) currentLon = 10; - currentSpeed *= 10; - if (currentSpeed > 1000) currentSpeed = 1; - currentCourse += 12; - if (currentCourse > 360) currentCourse -= 360; - currentSats += 1; - if (currentSats > 10) currentSats = 5; - currentAlt *= 10; - if (currentAlt > 1000) currentAlt = -100; - return { - "lat": currentLat, - "lon": currentLon, - "alt": currentAlt, - "speed": currentSpeed, - "course": currentCourse, - "time": Date.now(), - "satellites": currentSats, - "fix": 1, - "hdop": 1 - };}); - } - } - } - - if (settings.mag && settings.mag.enabled) { - log("MAG", settings.mag); - let modMag = function(data) { - setInterval(() => { - Bangle.getCompass = data; - Bangle.sensortoolsOrigEmit("mag", data()); - }, 100); - }; - if (settings.mag.power) { - Bangle.sensortoolsOrigSetCompassPower = Bangle.setCompassPower; - Bangle.setCompassPower = createPowerFunction(settings.mag.power, "Compass", Bangle.sensortoolsOrigSetCompassPower); - } - if (settings.mag.mode == "emulate") { - if (settings.mag.name == "static") { - modMag(()=>{return { - x: 1, - y: 1, - z: 1, - dx: 1, - dy: 1, - dz: 1, - heading: 0 - };}); - } else if (settings.mag.name == "rotate"){ - let last = 0; - modMag(()=>{return { - x: 1, - y: 1, - z: 1, - dx: 1, - dy: 1, - dz: 1, - heading: last = (last+1)%360, - };}); - } - } - } - } -} +if ((require('Storage').readJSON("sensortools.json", true) || {}).enabled) require("sensortools").enable(); diff --git a/apps/sensortools/lib.js b/apps/sensortools/lib.js new file mode 100644 index 000000000..460245c2f --- /dev/null +++ b/apps/sensortools/lib.js @@ -0,0 +1,348 @@ +exports.enable = () => { + let settings = Object.assign( + require('Storage').readJSON("sensortools.default.json", true) || {}, + require('Storage').readJSON("sensortools.json", true) || {} + ); + + let log = function(text, param) { + let logline = new Date().toISOString() + " - " + "Sensortools - " + text; + if (param) logline += ": " + JSON.stringify(param); + print(logline); + }; + + log("Enabled"); + const POWER_DELAY = 10000; + + let onEvents = []; + + Bangle.sensortoolsOrigOn = Bangle.on; + Bangle.sensortoolsOrigEmit = Bangle.emit; + Bangle.sensortoolsOrigRemoveListener = Bangle.removeListener; + + Bangle.on = function(name, callback) { + if (onEvents[name]) { + log("Redirecting listener for", name, "to", name + "_mod"); + Bangle.sensortoolsOrigOn(name + "_mod", callback); + Bangle.sensortoolsOrigOn(name, (e) => { + log("Redirected event for", name, "to", name + "_mod"); + Bangle.sensortoolsOrigEmit(name + "_mod", onEvents[name](e)); + }); + } else { + log("Pass through on call for", name, callback); + Bangle.sensortoolsOrigOn(name, callback); + } + }; + + Bangle.removeListener = function(name, callback) { + if (onEvents[name]) { + log("Removing augmented listener for", name, onEvents[name]); + Bangle.sensortoolsOrigRemoveListener(name + "_mod", callback); + } else { + log("Pass through remove listener for", name); + Bangle.sensortoolsOrigRemoveListener(name, callback); + } + }; + + Bangle.emit = function(name, event) { + if (onEvents[name]) { + log("Augmenting emit call for", name, onEvents[name]); + Bangle.sensortoolsOrigEmit(name + "_mod", event); + } else { + log("Pass through emit call for", name); + Bangle.sensortoolsOrigEmit(name, event); + } + }; + + let createPowerFunction = function(type, name, origPower) { + return function(isOn, app) { + if (type == "nop") { + return true; + }else if (type == "delay") { + setTimeout(() => { + origPower(isOn, app); + }, POWER_DELAY); + } else if (type == "on") { + origPower(1, "sensortools_force_on"); + } else if (type == "passthrough"){ + origPower(isOn, "app"); + } else if (type == "emulate"){ + if (!Bangle._PWR) Bangle._PWR={}; + if (!Bangle._PWR[name]) Bangle._PWR[name] = []; + if (!app) app="?"; + if (isOn) { + Bangle._PWR[name].push(app); + return true; + } else { + Bangle._PWR[name] = Bangle._PWR[name].filter((v)=>{return v == app;}); + return false; + } + } + }; + }; + + if (settings.hrm && settings.hrm.enabled) { + log("HRM", settings.hrm); + if (settings.hrm.power) { + log("HRM power"); + Bangle.sensortoolsOrigSetHRMPower = Bangle.setHRMPower; + Bangle.setHRMPower = createPowerFunction(settings.hrm.power, "HRM", Bangle.sensortoolsOrigSetHRMPower); + } + if (settings.hrm.mode == "modify") { + if (settings.hrm.name == "bpmtrippled") { + onEvents.HRM = (e) => { + return { + bpm: e.bpm * 3 + }; + }; + } + } else if (settings.hrm.mode == "emulate") { + if (settings.hrm.name == "sin") { + setInterval(() => { + Bangle.sensortoolsOrigEmit(60 + 3 * Math.sin(Date.now() / 10000)); + }, 1000); + } + } + } + if (settings.gps && settings.gps.enabled) { + log("GPS", settings.gps); + let modGps = function(dataProvider) { + Bangle.getGPSFix = dataProvider; + setInterval(() => { + Bangle.sensortoolsOrigEmit("GPS", dataProvider()); + }, 1000); + }; + if (settings.gps.power) { + Bangle.sensortoolsOrigSetGPSPower = Bangle.setGPSPower; + Bangle.setGPSPower = createPowerFunction(settings.gps.power, "GPS", Bangle.sensortoolsOrigSetGPSPower); + } + if (settings.gps.mode == "emulate") { + function radians(a) { + return a*Math.PI/180; + } + + function degrees(a) { + let d = a*180/Math.PI; + return (d+360)%360; + } + + function bearing(a,b){ + if (!a || !b || !a.lon || !a.lat || !b.lon || !b.lat) return Infinity; + let delta = radians(b.lon-a.lon); + let alat = radians(a.lat); + let blat = radians(b.lat); + let y = Math.sin(delta) * Math.cos(blat); + let x = Math.cos(alat)*Math.sin(blat) - + Math.sin(alat)*Math.cos(blat)*Math.cos(delta); + return Math.round(degrees(Math.atan2(y, x))); + } + + function interpolate(a,b,progress){ + return { + lat: a.lat * progress + b.lat * (1-progress), + lon: a.lon * progress + b.lon * (1-progress), + ele: a.ele * progress + b.ele * (1-progress) + } + } + + function getSquareRoute(){ + return [ + {lat:"47.2577411",lon:"11.9927442",ele:2273}, + {lat:"47.266761",lon:"11.9926673",ele:2166}, + {lat:"47.2667605",lon:"12.0059511",ele:2245}, + {lat:"47.2577516",lon:"12.0059925",ele:1994} + ]; + } + function getSquareRouteFuzzy(){ + return [ + {lat:"47.2578455",lon:"11.9929891",ele:2265}, + {lat:"47.258592",lon:"11.9923341",ele:2256}, + {lat:"47.2594506",lon:"11.9927412",ele:2230}, + {lat:"47.2603323",lon:"11.9924949",ele:2219}, + {lat:"47.2612056",lon:"11.9928175",ele:2199}, + {lat:"47.2621002",lon:"11.9929817",ele:2182}, + {lat:"47.2629025",lon:"11.9923915",ele:2189}, + {lat:"47.2637828",lon:"11.9926486",ele:2180}, + {lat:"47.2646733",lon:"11.9928167",ele:2191}, + {lat:"47.2655617",lon:"11.9930357",ele:2185}, + {lat:"47.2662862",lon:"11.992252",ele:2186}, + {lat:"47.2669305",lon:"11.993173",ele:2166}, + {lat:"47.266666",lon:"11.9944419",ele:2171}, + {lat:"47.2667579",lon:"11.99576",ele:2194}, + {lat:"47.2669409",lon:"11.9970579",ele:2207}, + {lat:"47.2666562",lon:"11.9983128",ele:2212}, + {lat:"47.2666027",lon:"11.9996335",ele:2262}, + {lat:"47.2667245",lon:"12.0009395",ele:2278}, + {lat:"47.2668457",lon:"12.002256",ele:2297}, + {lat:"47.2666126",lon:"12.0035373",ele:2303}, + {lat:"47.2664554",lon:"12.004841",ele:2251}, + {lat:"47.2669461",lon:"12.005948",ele:2245}, + {lat:"47.2660877",lon:"12.006323",ele:2195}, + {lat:"47.2652729",lon:"12.0057552",ele:2163}, + {lat:"47.2643926",lon:"12.0060123",ele:2131}, + {lat:"47.2634978",lon:"12.0058302",ele:2095}, + {lat:"47.2626129",lon:"12.0060759",ele:2066}, + {lat:"47.2617325",lon:"12.0058188",ele:2037}, + {lat:"47.2608668",lon:"12.0061784",ele:1993}, + {lat:"47.2600155",lon:"12.0057392",ele:1967}, + {lat:"47.2591203",lon:"12.0058233",ele:1949}, + {lat:"47.2582307",lon:"12.0059718",ele:1972}, + {lat:"47.2578014",lon:"12.004804",ele:2011}, + {lat:"47.2577232",lon:"12.0034834",ele:2044}, + {lat:"47.257745",lon:"12.0021656",ele:2061}, + {lat:"47.2578682",lon:"12.0008597",ele:2065}, + {lat:"47.2577082",lon:"11.9995526",ele:2071}, + {lat:"47.2575917",lon:"11.9982348",ele:2102}, + {lat:"47.2577401",lon:"11.996924",ele:2147}, + {lat:"47.257715",lon:"11.9956061",ele:2197}, + {lat:"47.2578996",lon:"11.9943081",ele:2228} + ]; + } + + if (settings.gps.name == "staticfix") { + modGps(() => { return { + "lat": 52, + "lon": 8, + "alt": 100, + "speed": 10, + "course": 12, + "time": Date.now(), + "satellites": 7, + "fix": 1, + "hdop": 1 + };}); + } else if (settings.gps.name.includes("route")) { + let route; + let interpSteps; + if (settings.gps.name == "routeFuzzy"){ + route = getSquareRouteFuzzy(); + interpSteps = 5; + } else { + route = getSquareRoute(); + interpSteps = 50; + } + + let step = 0; + let routeIndex = 0; + modGps(() => { + let newIndex = (routeIndex + 1)%route.length; + + let result = { + "speed": Math.random() * 3 + 2, + "time": Date.now(), + "satellites": Math.floor(Math.random()*5)+3, + "fix": 1, + "hdop": Math.floor(Math.random(30)+1) + }; + + let oldPos = route[routeIndex]; + if (step != 0){ + oldPos = interpolate(route[routeIndex], route[newIndex], E.clip(0,1,step/interpSteps)); + } + let newPos = route[newIndex]; + if (step < interpSteps - 1){ + newPos = interpolate(route[routeIndex], route[newIndex], E.clip(0,1,(step+1)%interpSteps/interpSteps)); + } + + if (step == interpSteps - 1){ + let followingIndex = (routeIndex + 2)%route.length; + newPos = interpolate(route[newIndex], route[followingIndex], E.clip(0,1,1/interpSteps)); + } + + result.lat = oldPos.lat; + result.lon = oldPos.lon; + result.alt = oldPos.ele; + + result.course = bearing(oldPos,newPos); + + step++; + if (step == interpSteps){ + routeIndex = (routeIndex + 1) % route.length; + step = 0; + } + return result; + }); + } else if (settings.gps.name == "nofix") { + modGps(() => { return { + "lat": NaN, + "lon": NaN, + "alt": NaN, + "speed": NaN, + "course": NaN, + "time": Date.now(), + "satellites": 2, + "fix": 0, + "hdop": NaN + };}); + } else if (settings.gps.name == "changingfix") { + let currentSpeed=1; + let currentLat=20; + let currentLon=10; + let currentCourse=10; + let currentAlt=-100; + let currentSats=5; + modGps(() => { + currentLat += 0.1; + if (currentLat > 50) currentLat = 20; + currentLon += 0.1; + if (currentLon > 20) currentLon = 10; + currentSpeed *= 10; + if (currentSpeed > 1000) currentSpeed = 1; + currentCourse += 12; + if (currentCourse > 360) currentCourse -= 360; + currentSats += 1; + if (currentSats > 10) currentSats = 5; + currentAlt *= 10; + if (currentAlt > 1000) currentAlt = -100; + return { + "lat": currentLat, + "lon": currentLon, + "alt": currentAlt, + "speed": currentSpeed, + "course": currentCourse, + "time": Date.now(), + "satellites": currentSats, + "fix": 1, + "hdop": 1 + };}); + } + } + } + + if (settings.mag && settings.mag.enabled) { + log("MAG", settings.mag); + let modMag = function(data) { + setInterval(() => { + Bangle.getCompass = data; + Bangle.sensortoolsOrigEmit("mag", data()); + }, 100); + }; + if (settings.mag.power) { + Bangle.sensortoolsOrigSetCompassPower = Bangle.setCompassPower; + Bangle.setCompassPower = createPowerFunction(settings.mag.power, "Compass", Bangle.sensortoolsOrigSetCompassPower); + } + if (settings.mag.mode == "emulate") { + if (settings.mag.name == "static") { + modMag(()=>{return { + x: 1, + y: 1, + z: 1, + dx: 1, + dy: 1, + dz: 1, + heading: 0 + };}); + } else if (settings.mag.name == "rotate"){ + let last = 0; + modMag(()=>{return { + x: 1, + y: 1, + z: 1, + dx: 1, + dy: 1, + dz: 1, + heading: last = (last+1)%360, + };}); + } + } + } +}; diff --git a/apps/sensortools/metadata.json b/apps/sensortools/metadata.json index 550fa5edc..8e1f5bd29 100644 --- a/apps/sensortools/metadata.json +++ b/apps/sensortools/metadata.json @@ -12,6 +12,7 @@ "storage": [ {"name":"sensortools.0.boot.js","url":"boot.js"}, {"name":"sensortools.settings.js","url":"settings.js"}, + {"name":"sensortools","url":"lib.js"}, {"name":"sensortools.default.json","url":"default.json"} ] } From 6b03375a9759a970f92322cd92a1633d1f2e47e8 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 30 Oct 2022 15:20:41 +0100 Subject: [PATCH 3/8] quicklaunch - Only check configured apps on use --- apps/quicklaunch/boot.js | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/apps/quicklaunch/boot.js b/apps/quicklaunch/boot.js index bc2408e78..4a2381d49 100644 --- a/apps/quicklaunch/boot.js +++ b/apps/quicklaunch/boot.js @@ -1,29 +1,24 @@ { - let settings = Object.assign(require("Storage").readJSON("quicklaunch.json", true) || {}); + let settings = require("Storage").readJSON("quicklaunch.json", true) || {}; + const storage = require("Storage"); - let hash = require("Storage").hash(/\.info/); - if (settings.hash!=hash) { - //apps changed, rescan and remove no longer existing apps - let apps = require("Storage").list(/\.info$/).map(app=>{var a=require("Storage").readJSON(app,1);return a&&{src:a.src};}); - - for (let c of ["leftapp","rightapp","upapp","downapp","tapapp"]){ - if (!settings[c]) settings[c] = {"name":"(none)"}; - if (!require("Storage").read(settings[c].src)) settings[c] = {"name":"(none)"}; - } - settings.hash = hash; - require("Storage").write("quicklaunch.json",settings); - } + let reset = function(name){ + if (!settings[name]) settings[name] = {"name":"(none)"}; + if (!require("Storage").read(settings[name].src)) settings[name] = {"name":"(none)"}; + storage.write("quicklaunch.json", settings); + }; Bangle.on("touch", () => { if (!Bangle.CLOCK) return; - if (settings.tapapp.src) load(settings.tapapp.src); + if (settings.tapapp.src){ if (!storage.read(settings.tapapp.src)) reset("tapapp"); else load(settings.tapapp.src); } }); + Bangle.on("swipe", (lr,ud) => { if (!Bangle.CLOCK) return; - if (lr == -1 && settings.leftapp.src) load(settings.leftapp.src); - if (lr == 1 && settings.rightapp.src) load(settings.rightapp.src); - if (ud == 1 && settings.upapp.src) load(settings.upapp.src); - if (ud == -1 && settings.downapp.src) load(settings.downapp.src); + if (lr == -1 && settings.leftapp && settings.leftapp.src){ if (!storage.read(settings.leftapp.src)) reset("leftapp"); else load(settings.leftapp.src); } + if (lr == 1 && settings.rightapp && settings.rightapp.src){ if (!storage.read(settings.rightapp.src)) reset("rightapp"); else load(settings.rightapp.src); } + if (ud == 1 && settings.upapp && settings.upapp.src){ if (!storage.read(settings.upapp.src)) reset("upapp"); else load(settings.upapp.src); } + if (ud == -1 && settings.downapp && settings.downapp.src){ if (!storage.read(settings.downapp.src)) reset("downapp"); else load(settings.downapp.src); } }); } From 6b5176c086d1eadb1688f9380a6736ed51e42406 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 30 Oct 2022 15:30:10 +0100 Subject: [PATCH 4/8] owmweather - Do not wrap in function --- apps/owmweather/boot.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/owmweather/boot.js b/apps/owmweather/boot.js index 64d2df3e9..51cb2835f 100644 --- a/apps/owmweather/boot.js +++ b/apps/owmweather/boot.js @@ -1,10 +1,10 @@ -(function() { +{ let waiting = false; let settings = require("Storage").readJSON("owmweather.json", 1) || { enabled: false }; - function completion(){ + let completion = function(){ waiting = false; } @@ -25,4 +25,4 @@ } }, settings.refresh * 1000 * 60); } -})(); +} From 0d145328a01b3703ee5fd007bd2d349fdfe2af29 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 30 Oct 2022 15:52:06 +0100 Subject: [PATCH 5/8] owmweather - Schedule the first update for 5s after boot --- apps/owmweather/boot.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/owmweather/boot.js b/apps/owmweather/boot.js index 51cb2835f..737413940 100644 --- a/apps/owmweather/boot.js +++ b/apps/owmweather/boot.js @@ -7,16 +7,19 @@ let completion = function(){ waiting = false; } - + if (settings.enabled) { let weather = require("Storage").readJSON('weather.json') || {}; let lastUpdate; if (weather && weather.weather && weather.weather.time) lastUpdate = weather.weather.time; + if (!lastUpdate || lastUpdate + settings.refresh * 1000 * 60 < Date.now()){ - if (!waiting){ - waiting = true; - require("owmweather").pull(completion); - } + setTimeout(() => { + if (!waiting){ + waiting = true; + require("owmweather").pull(completion); + } + }, 5000); } setInterval(() => { if (!waiting && NRF.getSecurityStatus().connected){ From f37c8b316708a0f54b37a48693527ef2a478fb0f Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 30 Oct 2022 15:46:16 +0100 Subject: [PATCH 6/8] bthrm - Move code into lib to boot faster if disabled --- apps/bthrm/boot.js | 634 +-------------------------------------- apps/bthrm/lib.js | 633 ++++++++++++++++++++++++++++++++++++++ apps/bthrm/metadata.json | 1 + 3 files changed, 635 insertions(+), 633 deletions(-) create mode 100644 apps/bthrm/lib.js diff --git a/apps/bthrm/boot.js b/apps/bthrm/boot.js index aa97d83b7..3e3d35737 100644 --- a/apps/bthrm/boot.js +++ b/apps/bthrm/boot.js @@ -1,633 +1 @@ -(function() { - var settings = Object.assign( - require('Storage').readJSON("bthrm.default.json", true) || {}, - require('Storage').readJSON("bthrm.json", true) || {} - ); - - var log = function(text, param){ - if (global.showStatusInfo) - showStatusInfo(text); - if (settings.debuglog){ - var logline = new Date().toISOString() + " - " + text; - if (param) logline += ": " + JSON.stringify(param); - print(logline); - } - }; - - log("Settings: ", settings); - - if (settings.enabled){ - - var clearCache = function() { - return require('Storage').erase("bthrm.cache.json"); - }; - - var getCache = function() { - var cache = require('Storage').readJSON("bthrm.cache.json", true) || {}; - if (settings.btid && settings.btid === cache.id) return cache; - clearCache(); - return {}; - }; - - var addNotificationHandler = function(characteristic) { - log("Setting notification handler"/*supportedCharacteristics[characteristic.uuid].handler*/); - characteristic.on('characteristicvaluechanged', (ev) => supportedCharacteristics[characteristic.uuid].handler(ev.target.value)); - }; - - var writeCache = function(cache) { - var oldCache = getCache(); - if (oldCache !== cache) { - log("Writing cache"); - require('Storage').writeJSON("bthrm.cache.json", cache); - } else { - log("No changes, don't write cache"); - } - }; - - var characteristicsToCache = function(characteristics) { - log("Cache characteristics"); - var cache = getCache(); - if (!cache.characteristics) cache.characteristics = {}; - for (var c of characteristics){ - //"handle_value":16,"handle_decl":15 - log("Saving handle " + c.handle_value + " for characteristic: ", c); - cache.characteristics[c.uuid] = { - "handle": c.handle_value, - "uuid": c.uuid, - "notify": c.properties.notify, - "read": c.properties.read - }; - } - writeCache(cache); - }; - - var characteristicsFromCache = function(device) { - var service = { device : device }; // fake a BluetoothRemoteGATTService - log("Read cached characteristics"); - var cache = getCache(); - if (!cache.characteristics) return []; - var restored = []; - for (var c in cache.characteristics){ - var cached = cache.characteristics[c]; - var r = new BluetoothRemoteGATTCharacteristic(); - log("Restoring characteristic ", cached); - r.handle_value = cached.handle; - r.uuid = cached.uuid; - r.properties = {}; - r.properties.notify = cached.notify; - r.properties.read = cached.read; - r.service = service; - addNotificationHandler(r); - log("Restored characteristic: ", r); - restored.push(r); - } - return restored; - }; - - log("Start"); - - var lastReceivedData={ - }; - - var supportedServices = [ - "0x180d", // Heart Rate - "0x180f", // Battery - ]; - - var bpmTimeout; - - var supportedCharacteristics = { - "0x2a37": { - //Heart rate measurement - active: false, - handler: function (dv){ - var flags = dv.getUint8(0); - - var bpm = (flags & 1) ? (dv.getUint16(1) / 100 /* ? */ ) : dv.getUint8(1); // 8 or 16 bit - supportedCharacteristics["0x2a37"].active = bpm > 0; - log("BTHRM BPM " + supportedCharacteristics["0x2a37"].active); - if (supportedCharacteristics["0x2a37"].active) stopFallback(); - if (bpmTimeout) clearTimeout(bpmTimeout); - bpmTimeout = setTimeout(()=>{ - supportedCharacteristics["0x2a37"].active = false; - startFallback(); - }, 3000); - - var sensorContact; - - if (flags & 2){ - sensorContact = !!(flags & 4); - } - - var idx = 2 + (flags&1); - - var energyExpended; - if (flags & 8){ - energyExpended = dv.getUint16(idx,1); - idx += 2; - } - var interval; - if (flags & 16) { - interval = []; - var maxIntervalBytes = (dv.byteLength - idx); - log("Found " + (maxIntervalBytes / 2) + " rr data fields"); - for(var i = 0 ; i < maxIntervalBytes / 2; i++){ - interval[i] = dv.getUint16(idx,1); // in milliseconds - idx += 2; - } - } - - var location; - if (lastReceivedData && lastReceivedData["0x180d"] && lastReceivedData["0x180d"]["0x2a38"]){ - location = lastReceivedData["0x180d"]["0x2a38"]; - } - - var battery; - if (lastReceivedData && lastReceivedData["0x180f"] && lastReceivedData["0x180f"]["0x2a19"]){ - battery = lastReceivedData["0x180f"]["0x2a19"]; - } - - if (settings.replace){ - var repEvent = { - bpm: bpm, - confidence: (sensorContact || sensorContact === undefined)? 100 : 0, - src: "bthrm" - }; - - log("Emitting HRM", repEvent); - Bangle.emit("HRM_int", repEvent); - } - - var newEvent = { - bpm: bpm - }; - - if (location) newEvent.location = location; - if (interval) newEvent.rr = interval; - if (energyExpended) newEvent.energy = energyExpended; - if (battery) newEvent.battery = battery; - if (sensorContact) newEvent.contact = sensorContact; - - log("Emitting BTHRM", newEvent); - Bangle.emit("BTHRM", newEvent); - } - }, - "0x2a38": { - //Body sensor location - handler: function(dv){ - if (!lastReceivedData["0x180d"]) lastReceivedData["0x180d"] = {}; - lastReceivedData["0x180d"]["0x2a38"] = parseInt(dv.buffer, 10); - } - }, - "0x2a19": { - //Battery - handler: function (dv){ - if (!lastReceivedData["0x180f"]) lastReceivedData["0x180f"] = {}; - lastReceivedData["0x180f"]["0x2a19"] = dv.getUint8(0); - } - } - }; - - var device; - var gatt; - var characteristics = []; - var blockInit = false; - var currentRetryTimeout; - var initialRetryTime = 40; - var maxRetryTime = 60000; - var retryTime = initialRetryTime; - - var connectSettings = { - minInterval: 7.5, - maxInterval: 1500 - }; - - var waitingPromise = function(timeout) { - return new Promise(function(resolve){ - log("Start waiting for " + timeout); - setTimeout(()=>{ - log("Done waiting for " + timeout); - resolve(); - }, timeout); - }); - }; - - if (settings.enabled){ - Bangle.isBTHRMActive = function (){ - return supportedCharacteristics["0x2a37"].active; - }; - - Bangle.isBTHRMOn = function(){ - return (Bangle._PWR && Bangle._PWR.BTHRM && Bangle._PWR.BTHRM.length > 0); - }; - - Bangle.isBTHRMConnected = function(){ - return gatt && gatt.connected; - }; - } - - if (settings.replace){ - Bangle.origIsHRMOn = Bangle.isHRMOn; - - Bangle.isHRMOn = function() { - if (settings.enabled && !settings.replace){ - return Bangle.origIsHRMOn(); - } else if (settings.enabled && settings.replace){ - return Bangle.isBTHRMOn(); - } - return Bangle.origIsHRMOn() || Bangle.isBTHRMOn(); - }; - } - - var clearRetryTimeout = function(resetTime) { - if (currentRetryTimeout){ - log("Clearing timeout " + currentRetryTimeout); - clearTimeout(currentRetryTimeout); - currentRetryTimeout = undefined; - } - if (resetTime) { - log("Resetting retry time"); - retryTime = initialRetryTime; - } - }; - - var retry = function() { - log("Retry"); - - if (!currentRetryTimeout){ - - var clampedTime = retryTime < 100 ? 100 : retryTime; - - log("Set timeout for retry as " + clampedTime); - clearRetryTimeout(); - currentRetryTimeout = setTimeout(() => { - log("Retrying"); - currentRetryTimeout = undefined; - initBt(); - }, clampedTime); - - retryTime = Math.pow(clampedTime, 1.1); - if (retryTime > maxRetryTime){ - retryTime = maxRetryTime; - } - } else { - log("Already in retry..."); - } - }; - - var buzzing = false; - var onDisconnect = function(reason) { - log("Disconnect: " + reason); - log("GATT", gatt); - log("Characteristics", characteristics); - clearRetryTimeout(reason != "Connection Timeout"); - supportedCharacteristics["0x2a37"].active = false; - startFallback(); - blockInit = false; - if (settings.warnDisconnect && !buzzing){ - buzzing = true; - Bangle.buzz(500,0.3).then(()=>waitingPromise(4500)).then(()=>{buzzing = false;}); - } - if (Bangle.isBTHRMOn()){ - retry(); - } - }; - - var createCharacteristicPromise = function(newCharacteristic) { - log("Create characteristic promise", newCharacteristic); - var result = Promise.resolve(); - // For values that can be read, go ahead and read them, even if we might be notified in the future - // Allows for getting initial state of infrequently updating characteristics, like battery - if (newCharacteristic.readValue){ - result = result.then(()=>{ - log("Reading data", newCharacteristic); - return newCharacteristic.readValue().then((data)=>{ - if (supportedCharacteristics[newCharacteristic.uuid] && supportedCharacteristics[newCharacteristic.uuid].handler) { - supportedCharacteristics[newCharacteristic.uuid].handler(data); - } - }); - }); - } - if (newCharacteristic.properties.notify){ - result = result.then(()=>{ - log("Starting notifications", newCharacteristic); - var startPromise = newCharacteristic.startNotifications().then(()=>log("Notifications started", newCharacteristic)); - if (settings.gracePeriodNotification > 0){ - log("Add " + settings.gracePeriodNotification + "ms grace period after starting notifications"); - startPromise = startPromise.then(()=>{ - log("Wait after connect"); - return waitingPromise(settings.gracePeriodNotification); - }); - } - return startPromise; - }); - } - return result.then(()=>log("Handled characteristic", newCharacteristic)); - }; - - var attachCharacteristicPromise = function(promise, characteristic) { - return promise.then(()=>{ - log("Handling characteristic:", characteristic); - return createCharacteristicPromise(characteristic); - }); - }; - - var createCharacteristicsPromise = function(newCharacteristics) { - log("Create characteristics promis ", newCharacteristics); - var result = Promise.resolve(); - for (var c of newCharacteristics){ - if (!supportedCharacteristics[c.uuid]) continue; - log("Supporting characteristic", c); - characteristics.push(c); - if (c.properties.notify){ - addNotificationHandler(c); - } - - result = attachCharacteristicPromise(result, c); - } - return result.then(()=>log("Handled characteristics")); - }; - - var createServicePromise = function(service) { - log("Create service promise", service); - var result = Promise.resolve(); - result = result.then(()=>{ - log("Handling service" + service.uuid); - return service.getCharacteristics().then((c)=>createCharacteristicsPromise(c)); - }); - return result.then(()=>log("Handled service" + service.uuid)); - }; - - var attachServicePromise = function(promise, service) { - return promise.then(()=>createServicePromise(service)); - }; - - var initBt = function () { - log("initBt with blockInit: " + blockInit); - if (blockInit){ - retry(); - return; - } - - blockInit = true; - - var promise; - var filters; - - if (!device){ - if (settings.btid){ - log("Configured device id", settings.btid); - filters = [{ id: settings.btid }]; - } else { - return; - } - log("Requesting device with filters", filters); - promise = NRF.requestDevice({ filters: filters, active: true }); - - if (settings.gracePeriodRequest){ - log("Add " + settings.gracePeriodRequest + "ms grace period after request"); - } - - promise = promise.then((d)=>{ - log("Got device", d); - d.on('gattserverdisconnected', onDisconnect); - device = d; - }); - - promise = promise.then(()=>{ - log("Wait after request"); - return waitingPromise(settings.gracePeriodRequest); - }); - } else { - promise = Promise.resolve(); - log("Reuse device", device); - } - - promise = promise.then(()=>{ - if (gatt){ - log("Reuse GATT", gatt); - } else { - log("GATT is new", gatt); - characteristics = []; - var cachedId = getCache().id; - if (device.id !== cachedId){ - log("Device ID changed from " + cachedId + " to " + device.id + ", clearing cache"); - clearCache(); - } - var newCache = getCache(); - newCache.id = device.id; - writeCache(newCache); - gatt = device.gatt; - } - - return Promise.resolve(gatt); - }); - - promise = promise.then((gatt)=>{ - if (!gatt.connected){ - log("Connecting..."); - var connectPromise = gatt.connect(connectSettings).then(function() { - log("Connected."); - }); - if (settings.gracePeriodConnect > 0){ - log("Add " + settings.gracePeriodConnect + "ms grace period after connecting"); - connectPromise = connectPromise.then(()=>{ - log("Wait after connect"); - return waitingPromise(settings.gracePeriodConnect); - }); - } - return connectPromise; - } else { - return Promise.resolve(); - } - }); - -/* promise = promise.then(() => { - log(JSON.stringify(gatt.getSecurityStatus())); - if (gatt.getSecurityStatus()['bonded']) { - log("Already bonded"); - return Promise.resolve(); - } else { - log("Start bonding"); - return gatt.startBonding() - .then(() => console.log(gatt.getSecurityStatus())); - } - });*/ - - promise = promise.then(()=>{ - if (!characteristics || characteristics.length === 0){ - characteristics = characteristicsFromCache(device); - } - }); - - promise = promise.then(()=>{ - var characteristicsPromise = Promise.resolve(); - if (characteristics.length === 0){ - characteristicsPromise = characteristicsPromise.then(()=>{ - log("Getting services"); - return gatt.getPrimaryServices(); - }); - - characteristicsPromise = characteristicsPromise.then((services)=>{ - log("Got services", services); - var result = Promise.resolve(); - for (var service of services){ - if (!(supportedServices.includes(service.uuid))) continue; - log("Supporting service", service.uuid); - result = attachServicePromise(result, service); - } - if (settings.gracePeriodService > 0) { - log("Add " + settings.gracePeriodService + "ms grace period after services"); - result = result.then(()=>{ - log("Wait after services"); - return waitingPromise(settings.gracePeriodService); - }); - } - return result; - }); - } else { - for (var characteristic of characteristics){ - characteristicsPromise = attachCharacteristicPromise(characteristicsPromise, characteristic, true); - } - } - - return characteristicsPromise; - }); - - return promise.then(()=>{ - log("Connection established, waiting for notifications"); - characteristicsToCache(characteristics); - clearRetryTimeout(true); - }).catch((e) => { - characteristics = []; - log("Error:", e); - onDisconnect(e); - }); - }; - - Bangle.setBTHRMPower = function(isOn, app) { - // Do app power handling - if (!app) app="?"; - if (Bangle._PWR===undefined) Bangle._PWR={}; - 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) { - switchFallback(); - if (!Bangle.isBTHRMConnected()) initBt(); - } else { // not on - log("Power off for " + app); - clearRetryTimeout(true); - if (gatt) { - if (gatt.connected){ - log("Disconnect with gatt", gatt); - try{ - gatt.disconnect().then(()=>{ - log("Successful disconnect"); - }).catch((e)=>{ - log("Error during disconnect promise", e); - }); - } catch (e){ - log("Error during disconnect attempt", e); - } - } - } - } - }; - - if (settings.replace){ - Bangle.on("HRM", (e) => { - e.modified = true; - Bangle.emit("HRM_int", e); - }); - - Bangle.origOn = Bangle.on; - Bangle.on = function(name, callback) { - if (name == "HRM") { - Bangle.origOn("HRM_int", callback); - } else { - Bangle.origOn(name, callback); - } - }; - - Bangle.origRemoveListener = Bangle.removeListener; - Bangle.removeListener = function(name, callback) { - if (name == "HRM") { - Bangle.origRemoveListener("HRM_int", callback); - } else { - Bangle.origRemoveListener(name, callback); - } - }; - - } - - Bangle.origSetHRMPower = Bangle.setHRMPower; - - if (settings.startWithHrm){ - - Bangle.setHRMPower = function(isOn, app) { - log("setHRMPower for " + app + ": " + (isOn?"on":"off")); - if (settings.enabled){ - Bangle.setBTHRMPower(isOn, app); - } - if ((settings.enabled && !settings.replace) || !settings.enabled){ - Bangle.origSetHRMPower(isOn, app); - } - }; - } - - var fallbackActive = false; - var inSwitch = false; - - var stopFallback = function(){ - if (fallbackActive){ - Bangle.origSetHRMPower(0, "bthrm_fallback"); - fallbackActive = false; - log("Fallback to HRM disabled"); - } - }; - - var startFallback = function(){ - if (!fallbackActive && settings.allowFallback) { - fallbackActive = true; - Bangle.origSetHRMPower(1, "bthrm_fallback"); - log("Fallback to HRM enabled"); - } - }; - - var switchFallback = function() { - log("Check falling back to HRM"); - if (!inSwitch){ - inSwitch = true; - if (Bangle.isBTHRMActive()){ - stopFallback(); - } else { - startFallback(); - } - } - inSwitch = false; - }; - - if (settings.replace){ - log("Replace HRM event"); - if (Bangle._PWR && Bangle._PWR.HRM){ - for (var i = 0; i < Bangle._PWR.HRM.length; i++){ - var app = Bangle._PWR.HRM[i]; - log("Moving app " + app); - Bangle.origSetHRMPower(0, app); - Bangle.setBTHRMPower(1, app); - if (Bangle._PWR.HRM===undefined) break; - } - } - } - - E.on("kill", ()=>{ - if (gatt && gatt.connected){ - log("Got killed, trying to disconnect"); - gatt.disconnect().then(()=>log("Disconnected on kill")).catch((e)=>log("Error during disconnnect on kill", e)); - } - }); - } -})(); +if ((require('Storage').readJSON("bthrm.json", true) || {}).enabled != false) require("bthrm").enable(); diff --git a/apps/bthrm/lib.js b/apps/bthrm/lib.js new file mode 100644 index 000000000..9e2f0fe63 --- /dev/null +++ b/apps/bthrm/lib.js @@ -0,0 +1,633 @@ +exports.enable = () => { + var settings = Object.assign( + require('Storage').readJSON("bthrm.default.json", true) || {}, + require('Storage').readJSON("bthrm.json", true) || {} + ); + + var log = function(text, param){ + if (global.showStatusInfo) + showStatusInfo(text); + if (settings.debuglog){ + var logline = new Date().toISOString() + " - " + text; + if (param) logline += ": " + JSON.stringify(param); + print(logline); + } + }; + + log("Settings: ", settings); + + if (settings.enabled){ + + var clearCache = function() { + return require('Storage').erase("bthrm.cache.json"); + }; + + var getCache = function() { + var cache = require('Storage').readJSON("bthrm.cache.json", true) || {}; + if (settings.btid && settings.btid === cache.id) return cache; + clearCache(); + return {}; + }; + + var addNotificationHandler = function(characteristic) { + log("Setting notification handler"/*supportedCharacteristics[characteristic.uuid].handler*/); + characteristic.on('characteristicvaluechanged', (ev) => supportedCharacteristics[characteristic.uuid].handler(ev.target.value)); + }; + + var writeCache = function(cache) { + var oldCache = getCache(); + if (oldCache !== cache) { + log("Writing cache"); + require('Storage').writeJSON("bthrm.cache.json", cache); + } else { + log("No changes, don't write cache"); + } + }; + + var characteristicsToCache = function(characteristics) { + log("Cache characteristics"); + var cache = getCache(); + if (!cache.characteristics) cache.characteristics = {}; + for (var c of characteristics){ + //"handle_value":16,"handle_decl":15 + log("Saving handle " + c.handle_value + " for characteristic: ", c); + cache.characteristics[c.uuid] = { + "handle": c.handle_value, + "uuid": c.uuid, + "notify": c.properties.notify, + "read": c.properties.read + }; + } + writeCache(cache); + }; + + var characteristicsFromCache = function(device) { + var service = { device : device }; // fake a BluetoothRemoteGATTService + log("Read cached characteristics"); + var cache = getCache(); + if (!cache.characteristics) return []; + var restored = []; + for (var c in cache.characteristics){ + var cached = cache.characteristics[c]; + var r = new BluetoothRemoteGATTCharacteristic(); + log("Restoring characteristic ", cached); + r.handle_value = cached.handle; + r.uuid = cached.uuid; + r.properties = {}; + r.properties.notify = cached.notify; + r.properties.read = cached.read; + r.service = service; + addNotificationHandler(r); + log("Restored characteristic: ", r); + restored.push(r); + } + return restored; + }; + + log("Start"); + + var lastReceivedData={ + }; + + var supportedServices = [ + "0x180d", // Heart Rate + "0x180f", // Battery + ]; + + var bpmTimeout; + + var supportedCharacteristics = { + "0x2a37": { + //Heart rate measurement + active: false, + handler: function (dv){ + var flags = dv.getUint8(0); + + var bpm = (flags & 1) ? (dv.getUint16(1) / 100 /* ? */ ) : dv.getUint8(1); // 8 or 16 bit + supportedCharacteristics["0x2a37"].active = bpm > 0; + log("BTHRM BPM " + supportedCharacteristics["0x2a37"].active); + if (supportedCharacteristics["0x2a37"].active) stopFallback(); + if (bpmTimeout) clearTimeout(bpmTimeout); + bpmTimeout = setTimeout(()=>{ + supportedCharacteristics["0x2a37"].active = false; + startFallback(); + }, 3000); + + var sensorContact; + + if (flags & 2){ + sensorContact = !!(flags & 4); + } + + var idx = 2 + (flags&1); + + var energyExpended; + if (flags & 8){ + energyExpended = dv.getUint16(idx,1); + idx += 2; + } + var interval; + if (flags & 16) { + interval = []; + var maxIntervalBytes = (dv.byteLength - idx); + log("Found " + (maxIntervalBytes / 2) + " rr data fields"); + for(var i = 0 ; i < maxIntervalBytes / 2; i++){ + interval[i] = dv.getUint16(idx,1); // in milliseconds + idx += 2; + } + } + + var location; + if (lastReceivedData && lastReceivedData["0x180d"] && lastReceivedData["0x180d"]["0x2a38"]){ + location = lastReceivedData["0x180d"]["0x2a38"]; + } + + var battery; + if (lastReceivedData && lastReceivedData["0x180f"] && lastReceivedData["0x180f"]["0x2a19"]){ + battery = lastReceivedData["0x180f"]["0x2a19"]; + } + + if (settings.replace){ + var repEvent = { + bpm: bpm, + confidence: (sensorContact || sensorContact === undefined)? 100 : 0, + src: "bthrm" + }; + + log("Emitting HRM", repEvent); + Bangle.emit("HRM_int", repEvent); + } + + var newEvent = { + bpm: bpm + }; + + if (location) newEvent.location = location; + if (interval) newEvent.rr = interval; + if (energyExpended) newEvent.energy = energyExpended; + if (battery) newEvent.battery = battery; + if (sensorContact) newEvent.contact = sensorContact; + + log("Emitting BTHRM", newEvent); + Bangle.emit("BTHRM", newEvent); + } + }, + "0x2a38": { + //Body sensor location + handler: function(dv){ + if (!lastReceivedData["0x180d"]) lastReceivedData["0x180d"] = {}; + lastReceivedData["0x180d"]["0x2a38"] = parseInt(dv.buffer, 10); + } + }, + "0x2a19": { + //Battery + handler: function (dv){ + if (!lastReceivedData["0x180f"]) lastReceivedData["0x180f"] = {}; + lastReceivedData["0x180f"]["0x2a19"] = dv.getUint8(0); + } + } + }; + + var device; + var gatt; + var characteristics = []; + var blockInit = false; + var currentRetryTimeout; + var initialRetryTime = 40; + var maxRetryTime = 60000; + var retryTime = initialRetryTime; + + var connectSettings = { + minInterval: 7.5, + maxInterval: 1500 + }; + + var waitingPromise = function(timeout) { + return new Promise(function(resolve){ + log("Start waiting for " + timeout); + setTimeout(()=>{ + log("Done waiting for " + timeout); + resolve(); + }, timeout); + }); + }; + + if (settings.enabled){ + Bangle.isBTHRMActive = function (){ + return supportedCharacteristics["0x2a37"].active; + }; + + Bangle.isBTHRMOn = function(){ + return (Bangle._PWR && Bangle._PWR.BTHRM && Bangle._PWR.BTHRM.length > 0); + }; + + Bangle.isBTHRMConnected = function(){ + return gatt && gatt.connected; + }; + } + + if (settings.replace){ + Bangle.origIsHRMOn = Bangle.isHRMOn; + + Bangle.isHRMOn = function() { + if (settings.enabled && !settings.replace){ + return Bangle.origIsHRMOn(); + } else if (settings.enabled && settings.replace){ + return Bangle.isBTHRMOn(); + } + return Bangle.origIsHRMOn() || Bangle.isBTHRMOn(); + }; + } + + var clearRetryTimeout = function(resetTime) { + if (currentRetryTimeout){ + log("Clearing timeout " + currentRetryTimeout); + clearTimeout(currentRetryTimeout); + currentRetryTimeout = undefined; + } + if (resetTime) { + log("Resetting retry time"); + retryTime = initialRetryTime; + } + }; + + var retry = function() { + log("Retry"); + + if (!currentRetryTimeout){ + + var clampedTime = retryTime < 100 ? 100 : retryTime; + + log("Set timeout for retry as " + clampedTime); + clearRetryTimeout(); + currentRetryTimeout = setTimeout(() => { + log("Retrying"); + currentRetryTimeout = undefined; + initBt(); + }, clampedTime); + + retryTime = Math.pow(clampedTime, 1.1); + if (retryTime > maxRetryTime){ + retryTime = maxRetryTime; + } + } else { + log("Already in retry..."); + } + }; + + var buzzing = false; + var onDisconnect = function(reason) { + log("Disconnect: " + reason); + log("GATT", gatt); + log("Characteristics", characteristics); + clearRetryTimeout(reason != "Connection Timeout"); + supportedCharacteristics["0x2a37"].active = false; + startFallback(); + blockInit = false; + if (settings.warnDisconnect && !buzzing){ + buzzing = true; + Bangle.buzz(500,0.3).then(()=>waitingPromise(4500)).then(()=>{buzzing = false;}); + } + if (Bangle.isBTHRMOn()){ + retry(); + } + }; + + var createCharacteristicPromise = function(newCharacteristic) { + log("Create characteristic promise", newCharacteristic); + var result = Promise.resolve(); + // For values that can be read, go ahead and read them, even if we might be notified in the future + // Allows for getting initial state of infrequently updating characteristics, like battery + if (newCharacteristic.readValue){ + result = result.then(()=>{ + log("Reading data", newCharacteristic); + return newCharacteristic.readValue().then((data)=>{ + if (supportedCharacteristics[newCharacteristic.uuid] && supportedCharacteristics[newCharacteristic.uuid].handler) { + supportedCharacteristics[newCharacteristic.uuid].handler(data); + } + }); + }); + } + if (newCharacteristic.properties.notify){ + result = result.then(()=>{ + log("Starting notifications", newCharacteristic); + var startPromise = newCharacteristic.startNotifications().then(()=>log("Notifications started", newCharacteristic)); + if (settings.gracePeriodNotification > 0){ + log("Add " + settings.gracePeriodNotification + "ms grace period after starting notifications"); + startPromise = startPromise.then(()=>{ + log("Wait after connect"); + return waitingPromise(settings.gracePeriodNotification); + }); + } + return startPromise; + }); + } + return result.then(()=>log("Handled characteristic", newCharacteristic)); + }; + + var attachCharacteristicPromise = function(promise, characteristic) { + return promise.then(()=>{ + log("Handling characteristic:", characteristic); + return createCharacteristicPromise(characteristic); + }); + }; + + var createCharacteristicsPromise = function(newCharacteristics) { + log("Create characteristics promis ", newCharacteristics); + var result = Promise.resolve(); + for (var c of newCharacteristics){ + if (!supportedCharacteristics[c.uuid]) continue; + log("Supporting characteristic", c); + characteristics.push(c); + if (c.properties.notify){ + addNotificationHandler(c); + } + + result = attachCharacteristicPromise(result, c); + } + return result.then(()=>log("Handled characteristics")); + }; + + var createServicePromise = function(service) { + log("Create service promise", service); + var result = Promise.resolve(); + result = result.then(()=>{ + log("Handling service" + service.uuid); + return service.getCharacteristics().then((c)=>createCharacteristicsPromise(c)); + }); + return result.then(()=>log("Handled service" + service.uuid)); + }; + + var attachServicePromise = function(promise, service) { + return promise.then(()=>createServicePromise(service)); + }; + + var initBt = function () { + log("initBt with blockInit: " + blockInit); + if (blockInit){ + retry(); + return; + } + + blockInit = true; + + var promise; + var filters; + + if (!device){ + if (settings.btid){ + log("Configured device id", settings.btid); + filters = [{ id: settings.btid }]; + } else { + return; + } + log("Requesting device with filters", filters); + promise = NRF.requestDevice({ filters: filters, active: true }); + + if (settings.gracePeriodRequest){ + log("Add " + settings.gracePeriodRequest + "ms grace period after request"); + } + + promise = promise.then((d)=>{ + log("Got device", d); + d.on('gattserverdisconnected', onDisconnect); + device = d; + }); + + promise = promise.then(()=>{ + log("Wait after request"); + return waitingPromise(settings.gracePeriodRequest); + }); + } else { + promise = Promise.resolve(); + log("Reuse device", device); + } + + promise = promise.then(()=>{ + if (gatt){ + log("Reuse GATT", gatt); + } else { + log("GATT is new", gatt); + characteristics = []; + var cachedId = getCache().id; + if (device.id !== cachedId){ + log("Device ID changed from " + cachedId + " to " + device.id + ", clearing cache"); + clearCache(); + } + var newCache = getCache(); + newCache.id = device.id; + writeCache(newCache); + gatt = device.gatt; + } + + return Promise.resolve(gatt); + }); + + promise = promise.then((gatt)=>{ + if (!gatt.connected){ + log("Connecting..."); + var connectPromise = gatt.connect(connectSettings).then(function() { + log("Connected."); + }); + if (settings.gracePeriodConnect > 0){ + log("Add " + settings.gracePeriodConnect + "ms grace period after connecting"); + connectPromise = connectPromise.then(()=>{ + log("Wait after connect"); + return waitingPromise(settings.gracePeriodConnect); + }); + } + return connectPromise; + } else { + return Promise.resolve(); + } + }); + +/* promise = promise.then(() => { + log(JSON.stringify(gatt.getSecurityStatus())); + if (gatt.getSecurityStatus()['bonded']) { + log("Already bonded"); + return Promise.resolve(); + } else { + log("Start bonding"); + return gatt.startBonding() + .then(() => console.log(gatt.getSecurityStatus())); + } + });*/ + + promise = promise.then(()=>{ + if (!characteristics || characteristics.length === 0){ + characteristics = characteristicsFromCache(device); + } + }); + + promise = promise.then(()=>{ + var characteristicsPromise = Promise.resolve(); + if (characteristics.length === 0){ + characteristicsPromise = characteristicsPromise.then(()=>{ + log("Getting services"); + return gatt.getPrimaryServices(); + }); + + characteristicsPromise = characteristicsPromise.then((services)=>{ + log("Got services", services); + var result = Promise.resolve(); + for (var service of services){ + if (!(supportedServices.includes(service.uuid))) continue; + log("Supporting service", service.uuid); + result = attachServicePromise(result, service); + } + if (settings.gracePeriodService > 0) { + log("Add " + settings.gracePeriodService + "ms grace period after services"); + result = result.then(()=>{ + log("Wait after services"); + return waitingPromise(settings.gracePeriodService); + }); + } + return result; + }); + } else { + for (var characteristic of characteristics){ + characteristicsPromise = attachCharacteristicPromise(characteristicsPromise, characteristic, true); + } + } + + return characteristicsPromise; + }); + + return promise.then(()=>{ + log("Connection established, waiting for notifications"); + characteristicsToCache(characteristics); + clearRetryTimeout(true); + }).catch((e) => { + characteristics = []; + log("Error:", e); + onDisconnect(e); + }); + }; + + Bangle.setBTHRMPower = function(isOn, app) { + // Do app power handling + if (!app) app="?"; + if (Bangle._PWR===undefined) Bangle._PWR={}; + 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) { + switchFallback(); + if (!Bangle.isBTHRMConnected()) initBt(); + } else { // not on + log("Power off for " + app); + clearRetryTimeout(true); + if (gatt) { + if (gatt.connected){ + log("Disconnect with gatt", gatt); + try{ + gatt.disconnect().then(()=>{ + log("Successful disconnect"); + }).catch((e)=>{ + log("Error during disconnect promise", e); + }); + } catch (e){ + log("Error during disconnect attempt", e); + } + } + } + } + }; + + if (settings.replace){ + Bangle.on("HRM", (e) => { + e.modified = true; + Bangle.emit("HRM_int", e); + }); + + Bangle.origOn = Bangle.on; + Bangle.on = function(name, callback) { + if (name == "HRM") { + Bangle.origOn("HRM_int", callback); + } else { + Bangle.origOn(name, callback); + } + }; + + Bangle.origRemoveListener = Bangle.removeListener; + Bangle.removeListener = function(name, callback) { + if (name == "HRM") { + Bangle.origRemoveListener("HRM_int", callback); + } else { + Bangle.origRemoveListener(name, callback); + } + }; + + } + + Bangle.origSetHRMPower = Bangle.setHRMPower; + + if (settings.startWithHrm){ + + Bangle.setHRMPower = function(isOn, app) { + log("setHRMPower for " + app + ": " + (isOn?"on":"off")); + if (settings.enabled){ + Bangle.setBTHRMPower(isOn, app); + } + if ((settings.enabled && !settings.replace) || !settings.enabled){ + Bangle.origSetHRMPower(isOn, app); + } + }; + } + + var fallbackActive = false; + var inSwitch = false; + + var stopFallback = function(){ + if (fallbackActive){ + Bangle.origSetHRMPower(0, "bthrm_fallback"); + fallbackActive = false; + log("Fallback to HRM disabled"); + } + }; + + var startFallback = function(){ + if (!fallbackActive && settings.allowFallback) { + fallbackActive = true; + Bangle.origSetHRMPower(1, "bthrm_fallback"); + log("Fallback to HRM enabled"); + } + }; + + var switchFallback = function() { + log("Check falling back to HRM"); + if (!inSwitch){ + inSwitch = true; + if (Bangle.isBTHRMActive()){ + stopFallback(); + } else { + startFallback(); + } + } + inSwitch = false; + }; + + if (settings.replace){ + log("Replace HRM event"); + if (Bangle._PWR && Bangle._PWR.HRM){ + for (var i = 0; i < Bangle._PWR.HRM.length; i++){ + var app = Bangle._PWR.HRM[i]; + log("Moving app " + app); + Bangle.origSetHRMPower(0, app); + Bangle.setBTHRMPower(1, app); + if (Bangle._PWR.HRM===undefined) break; + } + } + } + + E.on("kill", ()=>{ + if (gatt && gatt.connected){ + log("Got killed, trying to disconnect"); + gatt.disconnect().then(()=>log("Disconnected on kill")).catch((e)=>log("Error during disconnnect on kill", e)); + } + }); + } +}; diff --git a/apps/bthrm/metadata.json b/apps/bthrm/metadata.json index 4d2cb811b..ee2ca1554 100644 --- a/apps/bthrm/metadata.json +++ b/apps/bthrm/metadata.json @@ -15,6 +15,7 @@ {"name":"bthrm.0.boot.js","url":"boot.js"}, {"name":"bthrm.img","url":"app-icon.js","evaluate":true}, {"name":"bthrm.settings.js","url":"settings.js"}, + {"name":"bthrm","url":"lib.js"}, {"name":"bthrm.default.json","url":"default.json"} ] } From c195c85b2c1f0e823e606ef088c685b4aa7915d5 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 30 Oct 2022 16:47:56 +0100 Subject: [PATCH 7/8] Bump versions --- apps/bthrm/ChangeLog | 1 + apps/bthrm/metadata.json | 2 +- apps/owmweather/ChangeLog | 1 + apps/owmweather/metadata.json | 2 +- apps/quicklaunch/ChangeLog | 1 + apps/quicklaunch/metadata.json | 2 +- apps/sensortools/ChangeLog | 1 + apps/sensortools/metadata.json | 2 +- 8 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/bthrm/ChangeLog b/apps/bthrm/ChangeLog index 57f0ecf3d..a70ae3f8a 100644 --- a/apps/bthrm/ChangeLog +++ b/apps/bthrm/ChangeLog @@ -29,3 +29,4 @@ Use default boolean formatter in custom menu and directly apply config if useful Allow recording unmodified internal HR Better connection retry handling +0.13: Less time used during boot if disabled diff --git a/apps/bthrm/metadata.json b/apps/bthrm/metadata.json index ee2ca1554..7eedd223c 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.12", + "version": "0.13", "description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.", "icon": "app.png", "type": "app", diff --git a/apps/owmweather/ChangeLog b/apps/owmweather/ChangeLog index 5560f00bc..dba6a755a 100644 --- a/apps/owmweather/ChangeLog +++ b/apps/owmweather/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Do first update request 5s after boot to boot up faster diff --git a/apps/owmweather/metadata.json b/apps/owmweather/metadata.json index 013d345a5..56f9afca7 100644 --- a/apps/owmweather/metadata.json +++ b/apps/owmweather/metadata.json @@ -1,7 +1,7 @@ { "id": "owmweather", "name": "OpenWeatherMap weather provider", "shortName":"OWM Weather", - "version":"0.01", + "version":"0.02", "description": "Pulls weather from OpenWeatherMap (OWM) API", "icon": "app.png", "type": "bootloader", diff --git a/apps/quicklaunch/ChangeLog b/apps/quicklaunch/ChangeLog index 80334b57d..0ab99632a 100644 --- a/apps/quicklaunch/ChangeLog +++ b/apps/quicklaunch/ChangeLog @@ -1,3 +1,4 @@ 0.01: Initial version 0.02: Moved settings from launcher to settings->apps menu 0.03: Better performance by not scanning on every boot +0.04: Better performace by not scanning on boot at all diff --git a/apps/quicklaunch/metadata.json b/apps/quicklaunch/metadata.json index dd0362324..ccad02c96 100644 --- a/apps/quicklaunch/metadata.json +++ b/apps/quicklaunch/metadata.json @@ -2,7 +2,7 @@ "id": "quicklaunch", "name": "Quick Launch", "icon": "app.png", - "version":"0.03", + "version":"0.04", "description": "Tap or swipe left/right/up/down on your clock face to launch up to five apps of your choice. Configurations can be accessed through Settings->Apps.", "type": "bootloader", "tags": "tools, system", diff --git a/apps/sensortools/ChangeLog b/apps/sensortools/ChangeLog index 5560f00bc..eb1ab5cbc 100644 --- a/apps/sensortools/ChangeLog +++ b/apps/sensortools/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Less time used during boot if disabled diff --git a/apps/sensortools/metadata.json b/apps/sensortools/metadata.json index 8e1f5bd29..59a129ec1 100644 --- a/apps/sensortools/metadata.json +++ b/apps/sensortools/metadata.json @@ -2,7 +2,7 @@ "id": "sensortools", "name": "Sensor tools", "shortName": "Sensor tools", - "version": "0.01", + "version": "0.02", "description": "Tools for testing and debugging apps that use sensor input", "icon": "icon.png", "type": "bootloader", From 7ebdc92717a532298e5e981fa212949c3f814cfb Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 30 Oct 2022 18:18:38 +0100 Subject: [PATCH 8/8] quicklaunch - Fix inverted up/down swipes --- apps/quicklaunch/boot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/quicklaunch/boot.js b/apps/quicklaunch/boot.js index 4a2381d49..4ab238293 100644 --- a/apps/quicklaunch/boot.js +++ b/apps/quicklaunch/boot.js @@ -18,7 +18,7 @@ if (lr == -1 && settings.leftapp && settings.leftapp.src){ if (!storage.read(settings.leftapp.src)) reset("leftapp"); else load(settings.leftapp.src); } if (lr == 1 && settings.rightapp && settings.rightapp.src){ if (!storage.read(settings.rightapp.src)) reset("rightapp"); else load(settings.rightapp.src); } - if (ud == 1 && settings.upapp && settings.upapp.src){ if (!storage.read(settings.upapp.src)) reset("upapp"); else load(settings.upapp.src); } - if (ud == -1 && settings.downapp && settings.downapp.src){ if (!storage.read(settings.downapp.src)) reset("downapp"); else load(settings.downapp.src); } + if (ud == -1 && settings.upapp && settings.upapp.src){ if (!storage.read(settings.upapp.src)) reset("upapp"); else load(settings.upapp.src); } + if (ud == 1 && settings.downapp && settings.downapp.src){ if (!storage.read(settings.downapp.src)) reset("downapp"); else load(settings.downapp.src); } }); }