From 3e9df1a711c3e138ef6364e83a10186bf29082e7 Mon Sep 17 00:00:00 2001 From: nujw Date: Tue, 8 Feb 2022 08:17:31 +1300 Subject: [PATCH 01/60] Update app.js --- apps/speedalt2/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/speedalt2/app.js b/apps/speedalt2/app.js index cf414296e..3cc47e875 100644 --- a/apps/speedalt2/app.js +++ b/apps/speedalt2/app.js @@ -6,7 +6,7 @@ Mike Bennett mike[at]kereru.com 1.34 : Add bluetooth data stream for Droidscript 1.43 : Keep GPS in SuperE mode while using Droiscript screen mirroring */ -var v = '1.46'; +var v = '1.47'; var vDroid = '1.46'; // Required DroidScript program version /*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */ @@ -645,7 +645,7 @@ function btSend(dat) { var dur = getTime() - btLast; if ( dur < 1.0 ) return; // Don't need to transmit more than every 1.0 secs. btLast = getTime(); - console.log(JSON.stringify(dat)); // transmit the data + Bluetooth.println(JSON.stringify(dat)); // transmit the data } // == Events From cd5807338346fa4a18bffe0d06bc7f10654866c8 Mon Sep 17 00:00:00 2001 From: nujw Date: Tue, 8 Feb 2022 08:17:59 +1300 Subject: [PATCH 02/60] Update metadata.json --- apps/speedalt2/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/speedalt2/metadata.json b/apps/speedalt2/metadata.json index bb3fefb35..35298d42d 100644 --- a/apps/speedalt2/metadata.json +++ b/apps/speedalt2/metadata.json @@ -2,7 +2,7 @@ "id": "speedalt2", "name": "GPS Adventure Sports II", "shortName":"GPS Adv Sport II", - "version":"1.46", + "version":"1.47", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "icon": "app.png", "type": "app", From d4e9aff5c9d4c555a1582788a65456b8e0baf4a5 Mon Sep 17 00:00:00 2001 From: nujw Date: Tue, 8 Feb 2022 08:27:32 +1300 Subject: [PATCH 03/60] Update app.js --- apps/speedalt2/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/speedalt2/app.js b/apps/speedalt2/app.js index 3cc47e875..9c5245dcf 100644 --- a/apps/speedalt2/app.js +++ b/apps/speedalt2/app.js @@ -7,7 +7,7 @@ Mike Bennett mike[at]kereru.com 1.43 : Keep GPS in SuperE mode while using Droiscript screen mirroring */ var v = '1.47'; -var vDroid = '1.46'; // Required DroidScript program version +var vDroid = '1.50'; // Required DroidScript program version /*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */ var KalmanFilter = (function () { From 09d0f424da56a1fc163734a96c3f4ae0b4ffb391 Mon Sep 17 00:00:00 2001 From: nujw Date: Tue, 8 Feb 2022 08:29:01 +1300 Subject: [PATCH 04/60] Update GPS Adv Sports II.js --- apps/speedalt2/GPS Adv Sports II.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/speedalt2/GPS Adv Sports II.js b/apps/speedalt2/GPS Adv Sports II.js index 5eb9b64c8..bd8955328 100644 --- a/apps/speedalt2/GPS Adv Sports II.js +++ b/apps/speedalt2/GPS Adv Sports II.js @@ -7,8 +7,8 @@ app.LoadPlugin("PuckJS"); //Called when application is started. function OnStart() { - v = '1.49' // Version of this script - requiredBangleVer = '1.46'; // Minimum speedalt2 version required on Bangle + v = '1.50' // Version of this script + requiredBangleVer = '1.47'; // Minimum speedalt2 version required on Bangle curBangleVer = '-.--' isStopped = true; // Data receive turned off lastData = new Date().getTime() / 1000; // Time of last data received @@ -20,6 +20,8 @@ function OnStart() { col = new Array(['black'],['#64FF00'],['#FCFA00'],['#00E4FF']) // bg, main, units, wp - 0xFFFF,0x007F,0x0054,0x0054 // Connect to Bangle + if( !app.IsBluetoothEnabled() ) app.SetBluetoothEnabled( true ); + puck = app.CreatePuckJS(); puck.SetOnConnect(onConnect); // Callback. puck.SetOnReceive(readResponse); // Callback to capture console output from app. @@ -270,4 +272,3 @@ function btn_OnScan() { btnStop.SetBackColor(btnOff) puck.Scan("Bangle"); } - From 6daccc38133d6414329c53e54fe3057e4623400c Mon Sep 17 00:00:00 2001 From: Marco Heiming Date: Tue, 8 Feb 2022 11:41:50 +0100 Subject: [PATCH 05/60] Move default settings to external file --- apps/circlesclock/app.js | 91 +++++++++++++++------------------ apps/circlesclock/default.json | 25 +++++++++ apps/circlesclock/metadata.json | 5 +- apps/circlesclock/settings.js | 43 ++++++++-------- 4 files changed, 90 insertions(+), 74 deletions(-) create mode 100644 apps/circlesclock/default.json diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index 49af2a057..9c01cf3cd 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -20,25 +20,14 @@ const weatherStormy = atob("EBCBAAAAAYAH4AwwOBBgGEAOQMJAgjmOGcgAgACAAAAAAAAA"); const sunSetDown = atob("EBCBAAAAAAABgAAAAAATyAZoBCB//gAAAAAGYAPAAYAAAAAA"); const sunSetUp = atob("EBCBAAAAAAABgAAAAAATyAZoBCB//gAAAAABgAPABmAAAAAA"); -let settings = storage.readJSON("circlesclock.json", 1) || { - 'minHR': 40, - 'maxHR': 200, - 'confidence': 0, - 'stepGoal': 10000, - 'stepDistanceGoal': 8000, - 'stepLength': 0.8, - 'batteryWarn': 30, - 'showWidgets': false, - 'weatherCircleData': 'humidity', - 'circleCount': 3, - 'circle1': 'hr', - 'circle2': 'steps', - 'circle3': 'battery', - 'circle4': 'weather' -}; +const SETTINGS_FILE = "circlesclock.json"; +let settings = Object.assign( + storage.readJSON("circlesclock.default.json", true) || {}, + storage.readJSON(SETTINGS_FILE, true) || {} +); // Load step goal from pedometer widget as fallback if (settings.stepGoal == undefined) { - const d = storage.readJSON("wpedom.json", 1) || {}; + const d = storage.readJSON("wpedom.json", true) || {}; settings.stepGoal = d != undefined && d.settings != undefined ? d.settings.goal : 10000; } @@ -69,7 +58,7 @@ const widgetOffset = showWidgets ? 24 : 0; const dowOffset = circleCount == 3 ? 22 : 24; // dow offset relative to date const h = g.getHeight() - widgetOffset; const w = g.getWidth(); -const hOffset = 30 - widgetOffset; +const hOffset = (circleCount == 3 ? 34 : 30) - widgetOffset; const h1 = Math.round(1 * h / 5 - hOffset); const h2 = Math.round(3 * h / 5 - hOffset); const h3 = Math.round(8 * h / 8 - hOffset - 3); // circle y position @@ -279,10 +268,10 @@ function drawSteps(w) { drawCircleBackground(w); - const color = getCircleColor("steps") || colorBlue; + const color = getCircleColor("steps"); let percent; - const stepGoal = settings.stepGoal || 10000; + const stepGoal = settings.stepGoal; if (stepGoal > 0) { percent = steps / stepGoal; if (stepGoal < steps) percent = 1; @@ -299,15 +288,15 @@ function drawSteps(w) { function drawStepsDistance(w) { if (!w) w = getCircleXPosition("stepsDistance"); const steps = getSteps(); - const stepDistance = settings.stepLength || 0.8; + const stepDistance = settings.stepLength; const stepsDistance = Math.round(steps * stepDistance); drawCircleBackground(w); - const color = getCircleColor("stepsDistance") || colorGreen; + const color = getCircleColor("stepsDistance"); let percent; - const stepDistanceGoal = settings.stepDistanceGoal || 8000; + const stepDistanceGoal = settings.stepDistanceGoal; if (stepDistanceGoal > 0) { percent = stepsDistance / stepDistanceGoal; if (stepDistanceGoal < stepsDistance) percent = 1; @@ -326,12 +315,12 @@ function drawHeartRate(w) { drawCircleBackground(w); - const color = getCircleColor("hr") || colorRed; + const color = getCircleColor("hr"); let percent; if (hrtValue != undefined) { - const minHR = settings.minHR || 40; - const maxHR = settings.maxHR || 200; + const minHR = settings.minHR; + const maxHR = settings.maxHR; percent = (hrtValue - minHR) / (maxHR - minHR); if (isNaN(percent)) percent = 0; drawGauge(w, h3, percent, color); @@ -350,7 +339,7 @@ function drawBattery(w) { drawCircleBackground(w); - let color = getCircleColor("battery") || colorYellow; + let color = getCircleColor("battery"); let percent; if (battery > 0) { @@ -380,9 +369,9 @@ function drawWeather(w) { drawCircleBackground(w); - const color = getCircleColor("weather") || colorYellow; + const color = getCircleColor("weather"); let percent; - const data = settings.weatherCircleData || "humidity"; + const data = settings.weatherCircleData; switch (data) { case "humidity": const humidity = weather ? weather.hum : undefined; @@ -427,7 +416,7 @@ function drawSunProgress(w) { drawCircleBackground(w); - const color = getCircleColor("sunprogress") || colorYellow; + const color = getCircleColor("sunprogress"); drawGauge(w, h3, percent, color); @@ -467,7 +456,7 @@ function drawTemperature(w) { getPressureValue("temperature").then((temperature) => { drawCircleBackground(w); - const color = getCircleColor("temperature") || colorGreen; + const color = getCircleColor("temperature"); let percent; if (temperature) { @@ -493,7 +482,7 @@ function drawPressure(w) { getPressureValue("pressure").then((pressure) => { drawCircleBackground(w); - const color = getCircleColor("pressure") || colorGreen; + const color = getCircleColor("pressure"); let percent; if (pressure && pressure > 0) { @@ -519,7 +508,7 @@ function drawAltitude(w) { getPressureValue("altitude").then((altitude) => { drawCircleBackground(w); - const color = getCircleColor("altitude") || colorGreen; + const color = getCircleColor("altitude"); let percent; if (altitude) { @@ -578,23 +567,23 @@ function getWeatherIconByCode(code) { default: return weatherRainy; } - case 6: - return weatherSnowy; - case 7: - return weatherFoggy; - case 8: - switch (code) { - case 800: - return isDay() ? weatherSunny : weatherMoon; - case 801: - return weatherPartlyCloudy; - case 802: - return weatherPartlyCloudy; + case 6: + return weatherSnowy; + case 7: + return weatherFoggy; + case 8: + switch (code) { + case 800: + return isDay() ? weatherSunny : weatherMoon; + case 801: + return weatherPartlyCloudy; + case 802: + return weatherPartlyCloudy; + default: + return weatherCloudy; + } default: - return weatherCloudy; - } - default: - return undefined; + return undefined; } } @@ -797,7 +786,7 @@ Bangle.on('lock', function(isLocked) { let timerHrm; Bangle.on('HRM', function(hrm) { if (isCircleEnabled("hr")) { - if (hrm.confidence >= (settings.confidence || 0)) { + if (hrm.confidence >= (settings.confidence)) { hrtValue = hrm.bpm; if (Bangle.isLCDOn()) { drawHeartRate(); @@ -809,7 +798,7 @@ Bangle.on('HRM', function(hrm) { timerHrm = setTimeout(() => { hrtValue = '...'; drawHeartRate(); - }, settings.hrmValidity * 1000 || 30000); + }, settings.hrmValidity * 1000); } } }); diff --git a/apps/circlesclock/default.json b/apps/circlesclock/default.json new file mode 100644 index 000000000..cb6bfcff8 --- /dev/null +++ b/apps/circlesclock/default.json @@ -0,0 +1,25 @@ +{ + "minHR": 40, + "maxHR": 200, + "confidence": 0, + "stepGoal": 10000, + "stepDistanceGoal": 8000, + "stepLength": 0.8, + "batteryWarn": 30, + "showWidgets": false, + "weatherCircleData": "humidity", + "circleCount": 3, + "circle1": "hr", + "circle2": "steps", + "circle3": "battery", + "circle4": "weather", + "circle1color": "green-red", + "circle2color": "#0000ff", + "circle3color": "red-green", + "circle4color": "#ffff00", + "circle1colorizeIcon": true, + "circle2colorizeIcon": true, + "circle3colorizeIcon": true, + "circle4colorizeIcon": false, + "hrmValidity": 60 +} diff --git a/apps/circlesclock/metadata.json b/apps/circlesclock/metadata.json index f426a1681..3279ec2cf 100644 --- a/apps/circlesclock/metadata.json +++ b/apps/circlesclock/metadata.json @@ -1,7 +1,7 @@ { "id": "circlesclock", "name": "Circles clock", "shortName":"Circles clock", - "version":"0.09", + "version":"0.10", "description": "A clock with three or four circles for different data at the bottom in a probably familiar style", "icon": "app.png", "screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}], @@ -13,7 +13,8 @@ "storage": [ {"name":"circlesclock.app.js","url":"app.js"}, {"name":"circlesclock.img","url":"app-icon.js","evaluate":true}, - {"name":"circlesclock.settings.js","url":"settings.js"} + {"name":"circlesclock.settings.js","url":"settings.js"}, + {"name":"circlesclock.default.json","url":"default.json"} ], "data": [ {"name":"circlesclock.json"} diff --git a/apps/circlesclock/settings.js b/apps/circlesclock/settings.js index 348d187eb..bec539376 100644 --- a/apps/circlesclock/settings.js +++ b/apps/circlesclock/settings.js @@ -1,7 +1,11 @@ (function(back) { const SETTINGS_FILE = "circlesclock.json"; const storage = require('Storage'); - let settings = storage.readJSON(SETTINGS_FILE, 1) || {}; + let settings = Object.assign( + storage.readJSON("circlesclock.default.json", true) || {}, + storage.readJSON(SETTINGS_FILE, true) || {} + ); + function save(key, value) { settings[key] = value; storage.write(SETTINGS_FILE, settings); @@ -10,8 +14,8 @@ const valuesCircleTypes = ["empty", "steps", "stepsDist", "hr", "battery", "weather", "sunprogress", "temperature", "pressure", "altitude"]; const namesCircleTypes = ["empty", "steps", "distance", "heart", "battery", "weather", "sun", "temperature", "pressure", "altitude"]; - const valuesColors = ["", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff", "#fff", "#000", "green-red", "red-green"]; - const namesColors = ["default", "red", "green", "blue", "yellow", "magenta", "cyan", "white", "black", "green->red", "red->green"]; + const valuesColors = ["", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff", "#fff", "#000", "green-red", "red-green"]; + const namesColors = ["default", "red", "green", "blue", "yellow", "magenta", "cyan", "white", "black", "green->red", "red->green"]; const weatherData = ["empty", "humidity", "wind"]; @@ -20,7 +24,7 @@ '': { 'title': 'Circles clock' }, /*LANG*/'< Back': back, /*LANG*/'circle count': { - value: "circleCount" in settings ? settings.circleCount : 3, + value: settings.circleCount, min: 3, max : 4, step: 1, @@ -33,7 +37,7 @@ /*LANG*/'heartrate': ()=>showHRMenu(), /*LANG*/'steps': ()=>showStepMenu(), /*LANG*/'battery warn': { - value: "batteryWarn" in settings ? settings.batteryWarn : 30, + value: settings.batteryWarn, min: 10, max : 100, step: 10, @@ -43,12 +47,12 @@ onchange: x => save('batteryWarn', x), }, /*LANG*/'show widgets': { - value: "showWidgets" in settings ? settings.showWidgets : false, + value: !!settings.showWidgets, format: () => (settings.showWidgets ? 'Yes' : 'No'), onchange: x => save('showWidgets', x), }, - /*LANG*/'weather circle': { - value: settings.weatherCircleData ? weatherData.indexOf(settings.weatherCircleData) : 1, + /*LANG*/'weather data': { + value: weatherData.indexOf(settings.weatherCircleData), min: 0, max: 2, format: v => weatherData[v], onchange: x => save('weatherCircleData', weatherData[x]), @@ -62,7 +66,7 @@ '': { 'title': /*LANG*/'Heartrate' }, /*LANG*/'< Back': ()=>showMainMenu(), /*LANG*/'minimum': { - value: "minHR" in settings ? settings.minHR : 40, + value: settings.minHR, min: 0, max : 250, step: 5, @@ -72,7 +76,7 @@ onchange: x => save('minHR', x), }, /*LANG*/'maximum': { - value: "maxHR" in settings ? settings.maxHR : 200, + value: settings.maxHR, min: 20, max : 250, step: 5, @@ -82,7 +86,7 @@ onchange: x => save('maxHR', x), }, /*LANG*/'min. confidence': { - value: "confidence" in settings ? settings.confidence : 0, + value: settings.confidence, min: 0, max : 100, step: 10, @@ -92,7 +96,7 @@ onchange: x => save('confidence', x), }, /*LANG*/'valid period': { - value: "hrmValidity" in settings ? settings.hrmValidity : 30, + value: settings.hrmValidity, min: 10, max : 600, step: 10, @@ -110,7 +114,7 @@ '': { 'title': /*LANG*/'Steps' }, /*LANG*/'< Back': ()=>showMainMenu(), /*LANG*/'goal': { - value: "stepGoal" in settings ? settings.stepGoal : 10000, + value: settings.stepGoal, min: 2000, max : 50000, step: 2000, @@ -120,7 +124,7 @@ onchange: x => save('stepGoal', x), }, /*LANG*/'distance goal': { - value: "stepDistanceGoal" in settings ? settings.stepDistanceGoal : 8000, + value: settings.stepDistanceGoal, min: 2000, max : 30000, step: 1000, @@ -130,7 +134,7 @@ onchange: x => save('stepDistanceGoal', x), }, /*LANG*/'step length': { - value: "stepLength" in settings ? settings.stepLength : 0.8, + value: settings.stepLength, min: 0.1, max : 1.5, step: 0.01, @@ -142,9 +146,6 @@ }; E.showMenu(menu); } - - const defaultCircleTypes = ["steps", "hr", "battery", "weather"]; - function showCircleMenu(circleId) { const circleName = "circle" + circleId; const colorKey = circleName + "color"; @@ -154,19 +155,19 @@ '': { 'title': /*LANG*/'Circle ' + circleId }, /*LANG*/'< Back': ()=>showMainMenu(), /*LANG*/'data': { - value: settings[circleName]!=undefined ? valuesCircleTypes.indexOf(settings[circleName]) : valuesCircleTypes.indexOf(defaultCircleTypes[circleId -1]), + value: valuesCircleTypes.indexOf(settings[circleName]), min: 0, max: valuesCircleTypes.length - 1, format: v => namesCircleTypes[v], onchange: x => save(circleName, valuesCircleTypes[x]), }, /*LANG*/'color': { - value: settings[colorKey] ? valuesColors.indexOf(settings[colorKey]) : 0, + value: valuesColors.indexOf(settings[colorKey]) || 0, min: 0, max: valuesColors.length - 1, format: v => namesColors[v], onchange: x => save(colorKey, valuesColors[x]), }, /*LANG*/'colorize icon': { - value: colorizeIconKey in settings ? settings[colorizeIconKey] : false, + value: settings[colorizeIconKey] || false, format: () => (settings[colorizeIconKey] ? 'Yes' : 'No'), onchange: x => save(colorizeIconKey, x), }, From e27c43b1398be614f81561f2473a7ca4010b9a1a Mon Sep 17 00:00:00 2001 From: Marco Heiming Date: Tue, 8 Feb 2022 16:19:07 +0100 Subject: [PATCH 06/60] Use Google Roboto font for time, date and day of week and center them --- apps/circlesclock/app.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index 9c01cf3cd..157698865 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -20,6 +20,18 @@ const weatherStormy = atob("EBCBAAAAAYAH4AwwOBBgGEAOQMJAgjmOGcgAgACAAAAAAAAA"); const sunSetDown = atob("EBCBAAAAAAABgAAAAAATyAZoBCB//gAAAAAGYAPAAYAAAAAA"); const sunSetUp = atob("EBCBAAAAAAABgAAAAAATyAZoBCB//gAAAAABgAPABmAAAAAA"); +Graphics.prototype.setFontRobotoRegular50NumericOnly = function(scale) { + // Actual height 39 (40 - 2) + this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAB8AAAAAAAfAAAAAAAPwAAAAAAB8AAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAA4AAAAAAB+AAAAAAD/gAAAAAD/4AAAAAH/4AAAAAP/wAAAAAP/gAAAAAf/gAAAAAf/AAAAAA/+AAAAAB/+AAAAAB/8AAAAAD/4AAAAAH/4AAAAAD/wAAAAAA/wAAAAAAPgAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///wAAAB////gAAA////8AAA/////gAAP////8AAH8AAA/gAB8AAAD4AA+AAAAfAAPAAAADwADwAAAA8AA8AAAAPAAPAAAADwADwAAAA8AA8AAAAPAAPgAAAHwAB8AAAD4AAfwAAD+AAD/////AAA/////wAAH////4AAAf///4AAAB///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAPgAAAAAADwAAAAAAB8AAAAAAAfAAAAAAAHgAAAAAAD4AAAAAAA+AAAAAAAPAAAAAAAH/////wAB/////8AA//////AAP/////wAD/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAAAfgAADwAAP4AAB8AAH+AAA/AAD/gAAfwAB/AAAf8AAfAAAP/AAPgAAH7wAD4AAD88AA8AAB+PAAPAAA/DwADwAAfg8AA8AAPwPAAPAAH4DwADwAH8A8AA+AD+APAAPwB/ADwAB/D/gA8AAf//gAPAAD//wADwAAf/wAA8AAD/4AAPAAAHwAADwAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAADgAAAHwAA+AAAD8AAP4AAB/AAD/AAA/wAA/wAAf4AAD+AAHwAAAPgAD4APAB8AA+ADwAPAAPAA8ADwADwAPAA8AA8ADwAPAAPAA8ADwADwAfAA8AA8AH4APAAPgD+AHwAB8B/wD4AAf7/+B+AAD//v//AAA//x//wAAD/4P/4AAAf8B/4AAAAYAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAHwAAAAAAH8AAAAAAD/AAAAAAD/wAAAAAD/8AAAAAB/vAAAAAB/jwAAAAA/g8AAAAA/wPAAAAAfwDwAAAAf4A8AAAAf4APAAAAP8ADwAAAP8AA8AAAH8AAPAAAD/////8AA//////AAP/////wAD/////8AA//////AAAAAAPAAAAAAADwAAAAAAA8AAAAAAAPAAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAB/APwAAH//wD+AAD//8A/wAA///AH+AAP//wAPgAD/B4AB8AA8A+AAfAAPAPAADwADwDwAA8AA8A8AAPAAPAPAADwADwD4AA8AA8A+AAPAAPAPwAHwADwD8AD4AA8AfwD+AAPAH///AADwA///wAA8AH//4AAPAAf/4AAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAD//+AAAAD///4AAAD////AAAB////4AAA/78D/AAAfw8AH4AAPweAA+AAD4PgAHwAB8DwAA8AAfA8AAPAAHgPAADwAD4DwAA8AA+A8AAPAAPAPgAHwADwD4AB8AA8AfgA+AAPAH+B/gAAAA///wAAAAH//4AAAAA//8AAAAAH/8AAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAAAA8AAAAAAAPAAAAAAADwAAAAAAA8AAAABAAPAAAABwADwAAAB8AA8AAAB/AAPAAAB/wADwAAD/8AA8AAD/8AAPAAD/4AADwAD/4AAA8AD/4AAAPAH/wAAADwH/wAAAA8H/wAAAAPH/wAAAAD3/gAAAAA//gAAAAAP/gAAAAAD/gAAAAAA/AAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwA/4AAAH/Af/AAAH/8P/4AAD//n//AAA//7//4AAfx/+A+AAHwD+AHwAD4AfgB8AA8AHwAPAAPAA8ADwADwAPAA8AA8ADwAPAAPAA8ADwADwAfAA8AA+AH4AfAAHwD+AHwAB/D/4D4AAP/+/n+AAD//n//AAAf/w//gAAB/wH/wAAAHwA/4AAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAD/8AAAAAD//wAAAAB//+AAAAA///wAAAAf4H+APAAH4AfgDwAD8AB8A8AA+AAfAPAAPAADwDwADwAA8B8AA8AAPAfAAPAADwHgADwAA8D4AA+AAeB+AAHwAHg/AAB+ADwfgAAP8D4/4AAD////8AAAf///8AAAB///+AAAAP//+AAAAAP/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAOAAAB8AAHwAAAfgAD8AAAH4AA/AAAB8AAHwAAAOAAA4AAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("DRUcHBwcHBwcHBwcDA=="), 50+(scale<<8)+(1<<16)); + return this; +}; + +Graphics.prototype.setFontRobotoRegular21 = function(scale) { + // Actual height 22 (21 - 0) + this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAB/+YH/5gAAAAAAAAAAA+AAD4AAAAAA+AAD4AAAAAAAAAAAMAAYwABj+AH/4D/wAfjABGM4AZ/gD/4B/sAHYwABiAAEAAAAAAAAOAPw8A/h4HGBh4cHvgweGDhgcHOA8fwBw/AAAAAAAAPgAB/AAGMAAYwMBjDgD48AHHAABwAAOfADjuAcMYAAxgAD+AAHwAAAAAAAAAHgBx/AP3eB/wYGHBgY+GBjeYH4/gPA8AAHwAB/gAHGAAAAAAAA+AAD4AAAAAAAAAAA/gAf/wH//w8AHnAAHwAAEAAAMAAB4AAN4ABz8B+D//gD/4AAAAAAAAMAAAxAADMAAHwAH8AAf4AAPwAAzAADAAAAAAABgAAGAAAYAABgAD/8AP/wABgAAGAAAYAABgAAGAAAADAAB8AAPgAAYAAwAADAAAMAAAwAADAAAMAAAAAAAEAAA4AABgAAAAAAAAAAQAAPAAH4AB8AA/AAPgAH4AAcAAAAAAAAAAD/wA//wH4fgYAOBgAYGABgYAGB4B4D//AH/4AAAAAAAAAAAAYAADAAAMAABwAAH//gf/+AAAAAAAAAAAAAAAAAAAHAGA8A4HAHgYA+BgHYGB5gYPGB7wYD+BgHgGAAAYAAAAHA4A8DwHgDgYMGBgwYGDBgYcGB/44D9/AHj4AAAAAAMAABwAAfAAHsAA8wAPDADwMAf/+B//4H//gAAwAADAAAAAAAAAfjgH+HAf4OBjAYGMBgYwGBjg4GH/AYP4AAOAAAAAAfgAP/gB//AO4OBzAYGMBgYwGBjg4AH/AAP4AAEAAAAAYAABgAAGABgYAeBgHwGD8AY/ABvgAH4AAeAABAAAAAAADB4A/fwH/3gccGBgwYGDBgYcGB744D9/AHj4AAAAAAAAD8AA/4AHjxgYDGBgMYGAzgcDMA87wD/+AD/gAAAAAAAABgGAHA4AYBgAAAAAAAAYB8BwPgGAYAAAAAAAAAYAADwAAPAAB+AAGYAA5wADDAAcOABgYAAAAAAAAAZgABmAAGYAAZgABmAAGYAAZgABmAAGYAAZgAAAAAAAABgYAHDgAMMAA5wABmAAGYAAPAAA8AADwAAGAAAAAAAAAOAAA4AAHAAAYHmBg+YGHgAf4AA/AAAwAAAAAAAfwAH/4B4DwOADhwAGGD8Mw/4zHBjMYGMxgYzGPDMf+Mw8YhgBgHAGAOA4Af/AAPwAAAAAAAgAAeAAP4AD+AB/gA/mAHwYAeBgB/GAA/4AAfwAAfwAAPgAAGAAAAAAAAf/+B//4GDBgYMGBgwYGDBgYcGBz44D//AHz8AAHAAAAAAAAAH+AB/+AP/8A4A4HABgYAGBgAYGABgYAGB4A4DwPAHA4AAAAAAAAAAAB//4H//gYAGBgAYGABgYAGBgAYDADAPA8Af/gAf4AAAAAAAAAAAAf/+B//4GDBgYMGBgwYGDBgYMGBgwYGDBgYAGAAAAAAAAAAAB//4H//gYMABgwAGDAAYMABgwAGDAAYMABgAAAAAAB/AAf/AD//AeAcBgA4GABgYEGBgYYGBhgcGGA8fwBx/AAH4AAAAAAAAAAAB//4H//gAMAAAwAADAAAMAAAwAADAAAMAAAwAH//gf/+AAAAAAAAAAAAAAAH//gf/+AAAAAAAAAAQAADwAAPgAAOAAAYAABgAAGAAA4H//Af/8B//AAAAAAAAAAAAH//gf/+AA4AAHAAA+AAP8AB48APB4B4DwGADgQAGAAAIAAAAAAAB//4H//gAAGAAAYAABgAAGAAAYAABgAAGAAAIAAAAAAAB//4H//gfgAAfgAAPwAAPwAAH4AAHgAB+AAfgAPwAH8AB+AAH//gf/+B//4AAAAAAAAAAAH//gf/+A8AAB8AAB4AAD4AADwAAHwAAHgAAPgf/+B//4AAAAAAAAAAAAf4AH/4A//wDgHAcAOBgAYGABgYAGBwAYDADgPz8Af/gAf4AAAAAAAAAAAAf/+B//4GBgAYGABgYAGBgAYGABg4AHnAAP8AAfgAAAAAAAAAH+AB/+APz8AwAwHADgYAGBgAYGABgcAOA4B8D//4H/5gH+CAAAAAAAAAAAH//gf/+BgYAGBgAYGABgYAGD4AcP4A/z4D+HgDgGAAAAAAAADg4A/DwD+DAcYGBhwYGDBgYMGBg4YHBzgPH8AcPgAAAAYAABgAAGAAAYAABgAAH//gf/+B//4GAAAYAABgAAGAAAYAAAAAAH/4Af/4B//wAADgAAGAAAYAABgAAGAAA4AAfAf/8B//AAAAAQAAB4AAH8AAH+AAD+AAB/AAA+AAD4AB/AA/gAf4AH8AAeAABAAAAAAAfAAB/wAB/8AAP+AAD4AB/gA/wA/wAH4AAfgAAf4AAH+AAD+AAH4AH/gP/gB/gAHAAAAAAAAAAGABgcAOB8D4B4+AD/gAD8AAPwAD/wAeHgHwPgcAOBAAYAAAAQAABwAAHwAAPwAAPwAAP/gAP+AB/4AfAAHwAB8AAHAAAQAAAAAAGADgYAeBgH4GA9gYPmBh4YGfBgbwGB+AYHgBgcAGAAAIAAAA///7///v//+wAAbAABkAAAcAAB+AAB/AAA/AAAfgAAfgAAPAAAMMAAGwAAb///v//+AAAAAAAAAAAA4AAPgAH4AAeAAB+AAB+AAA4AAAgAAAAYAABgAAGAAAYAABgAAGAAAYAABgAAGAAAYAAABAAAGAAAcAAAwAAAAAAAAAAAAAAGOAA58AHu4AYxgBjGAGMYAYzgB/+AD/4AD/gAAAAAAAP//g//+D//4AcDgBgGAGAYAYBgBwOAD/wAH+AADAAAAAAD8AA/8AHh4AYBgBgGAGAYAYBgB4OADhwAGGAAAAAAAAAD8AA/8AHh4AYBgBgGAGAYAYBgAwMD//4P//gAAAAAAAAD8AA/8ADtwAYxgBjGAGMYAYxgB7OAD8wADyAAAAAEAAAYAAP/+B//4P//gxgADGAAMAAAAAAAA/AAP/MB4e4GAZgYBmBgGYGAZgMDeB//wH/+AAAAAAAD//4P//g//+AHAAAYAABgAAGAAAf/gA/+AB/4AAAAAAABn/4Gf/gZ/+AAAAAAAGZ//5n//mf/4AAAAAAAP//g//+D//4ABwAAPgAB/AAePABweAGA4AAAgAAAD//4P//g//+AAAAAAAAB/+AH/4Af/gBgAAGAAAYAABgAAH/4AP/gA/+AHAAAYAABgAAGAAAeAAA/+AB/4AAAAAAAAH/4Af/gB/+AHAAAYAABgAAGAAAf/gA/+AB/4AAAAAAAAA/AAP/AA4cAGAYAYBgBgGAGAYAcDgA/8AB/gAB4AAAAAAAAAf/+B//4H//gYBgBgGAGAYAYBgBwOAD/wAH+AADAAAAAAH8AA/8AHh4AYBgBgGAGAYAYBgAwOAH//gf/+AAAAAAAAAAAB/+AH/4Af/gBwAAGAAAYAAAAAABxgAPnAB+OAGcYAYxgBjGAHOYAefgA58AAAAAYAABgAA//wD//gBgOAGAYAQBgAAAAH/gAf/AB/+AAAYAABgAAGAAAYAf/gB/+AH/4AAAABAAAHgAAfwAAP4AAH4AAHgAD8AB/AAfgABwAAAAAAQAAB8A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"), 32, atob("BQYHDgwQDgQICAkMBAYGCQwMDAwMDAwMDAwFBQsMDAoUDg4ODg0MDxAGDA4MExAPDg8ODQ0ODhQODQ0GCQYJCgcMDAwMDAgMDAUFCwUTDA0MDQcLBwwLEQsKCwcFBw8ACw0ZEBgUGRoQGBQZGhIQDhMAAAAAEhEYExAUEBQQEA0FBQwNEAwFDgkRCgoMABEKCAwICAcMCwYFCAoKEBERCg4ODg4ODhUODQ0NDQYGBgYPEA8PDw8PDA8ODg4ODQ0NDAwMDAwMEwwMDAwMBQUFBQ0MDQ0NDQ0NDAwMDAwKDQo="), 22+(scale<<8)+(1<<16)); + return this; +}; + const SETTINGS_FILE = "circlesclock.json"; let settings = Object.assign( storage.readJSON("circlesclock.default.json", true) || {}, @@ -114,17 +126,17 @@ function draw() { g.fillRect(0, widgetOffset, w, h2 + 22); // time - g.setFont("Vector:50"); + g.setFontRobotoRegular50NumericOnly(); g.setFontAlign(0, -1); g.setColor(colorFg); g.drawString(locale.time(new Date(), 1), w / 2, h1 + 8); now = Math.round(new Date().getTime() / 1000); // date & dow - g.setFont("Vector:21"); - g.setFontAlign(-1, 0); - g.drawString(locale.date(new Date()), w > 180 ? 2 * w / 10 : w / 10, h2); - g.drawString(locale.dow(new Date()), w > 180 ? 2 * w / 10 : w / 10, h2 + dowOffset); + g.setFontRobotoRegular21(); + g.setFontAlign(0, 0); + g.drawString(locale.date(new Date()), w / 2, h2); + g.drawString(locale.dow(new Date()), w / 2, h2 + dowOffset); drawCircle(1); drawCircle(2); From 64bdddc8f1b7bb5d638f78ce6250f49e5f4e95d7 Mon Sep 17 00:00:00 2001 From: Marco Heiming Date: Tue, 8 Feb 2022 16:51:49 +0100 Subject: [PATCH 07/60] Let us call draw at beginning of minute --- apps/circlesclock/app.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index 157698865..903c7bdb2 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -827,5 +827,10 @@ if (isCircleEnabled("hr")) { Bangle.setUI("clock"); Bangle.loadWidgets(); +// schedule a draw for the next minute +setTimeout(function() { + // draw every 60 seconds + setInterval(draw,60000); +}, 60000 - (Date.now() % 60000)); + draw(); -setInterval(draw, 60000); From 5fbc33ef4cbaa588fa5433f951b64df89ba196a2 Mon Sep 17 00:00:00 2001 From: nujw Date: Wed, 9 Feb 2022 15:25:50 +1300 Subject: [PATCH 08/60] Update GPS Adv Sports II.js --- apps/speedalt2/GPS Adv Sports II.js | 67 +++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/apps/speedalt2/GPS Adv Sports II.js b/apps/speedalt2/GPS Adv Sports II.js index bd8955328..502a71001 100644 --- a/apps/speedalt2/GPS Adv Sports II.js +++ b/apps/speedalt2/GPS Adv Sports II.js @@ -3,21 +3,27 @@ app.LoadPlugin("PuckJS"); +app.SetDebugEnabled(false); + +conf = JSON.parse( app.LoadText( "settings", "{}" )) +if ( typeof(conf.btAddr) != 'string' ) conf.btAddr = '' // Address of last connection +if ( typeof(conf.btName) != 'string' ) conf.btName = '' // Name of last connection + +v = '1.51' // Version of this script +requiredBangleVer = '1.47'; // Minimum speedalt2 version required on Bangle +curBangleVer = '-.--' +isStopped = true; // Data receive turned off +lastData = new Date().getTime() / 1000; // Time of last data received + +// Mode = 0 // 0=SPD, 1=ALT, 2=DST, 3=VMG, 4=POSN, 5=TIME +btnOff = '#175A63' +btnOn = '#4285F4' +col = new Array(['black'],['#64FF00'],['#FCFA00'],['#00E4FF']) // bg, main, units, wp - 0xFFFF,0x007F,0x0054,0x0054 + //Called when application is started. function OnStart() { - v = '1.50' // Version of this script - requiredBangleVer = '1.47'; // Minimum speedalt2 version required on Bangle - curBangleVer = '-.--' - isStopped = true; // Data receive turned off - lastData = new Date().getTime() / 1000; // Time of last data received - addr = ''; // Address of last connection - - // Mode = 0 // 0=SPD, 1=ALT, 2=DST, 3=VMG, 4=POSN, 5=TIME - btnOff = '#175A63' - btnOn = '#4285F4' - col = new Array(['black'],['#64FF00'],['#FCFA00'],['#00E4FF']) // bg, main, units, wp - 0xFFFF,0x007F,0x0054,0x0054 // Connect to Bangle if( !app.IsBluetoothEnabled() ) app.SetBluetoothEnabled( true ); @@ -25,7 +31,21 @@ function OnStart() { puck = app.CreatePuckJS(); puck.SetOnConnect(onConnect); // Callback. puck.SetOnReceive(readResponse); // Callback to capture console output from app. - puck.Scan("Bangle"); + + + if ( conf.btAddr == '' ) { + console.log('Scanning') + puck.Scan("Bangle"); + } + else { + console.log('Reconnecting to : '+conf.btAddr) + puck.address = conf.btAddr + puck.name = conf.btName + puck.Connect(puck.address); + } + + app.SetDebugEnabled(false); + setInterval(checkConnection,5000) // Periodic check for data timeout and attempt a reconnect // Controls @@ -200,7 +220,7 @@ function readResponse(data) { if (d.m == 5) { // Time val.SetTextSize(90) - val2.SetTextSize(10) + val2.SetTextSize(0) dt = new Date(); @@ -224,10 +244,16 @@ function setLED(canvas,on,colour) { canvas.Update() } -function onConnect(name, address, bonded, rssi) { - addr = address - console.log( "Connected to " + address ); +function onConnect(name, address) { +// app.SetDebugEnabled(true) + + if ( typeof(address) == 'string' ) conf.btAddr = address + if ( typeof(name) == 'string' ) conf.btName = name + app.SaveText( "settings", JSON.stringify( conf )) // persist connection for future so no need to scan each time used. + + console.log( "Connected to : " + conf.btAddr ); btn_OnStart() // Once connect tell app to start sending updates + app.SetDebugEnabled(false) } // Periodic check for data timeout and attempt a reconnect @@ -235,17 +261,20 @@ function checkConnection() { if (isStopped) return if ( parseFloat(new Date().getTime() / 1000) - lastData > 30 ) { - console.log( "Reconnecting to : "+addr); + console.log( "Reconnecting to : "+conf.btAddr); // Flash orange 'led' indicator for connection attempts. setLED(led,true,"#FC8A00") setTimeout(function() {setLED(led,false,-1)}, 500) - puck.Connect(addr) + puck.Connect(conf.btAddr) } } function btn_OnAbout() { - app.ShowPopup("GPS Adv Sports II\nAndroid Mirror : "+v+"\nBangle.js : "+curBangleVer,"Long") + app.ShowPopup( + "GPS Adv Sports II\nAndroid Mirror : "+v+ + "\nBangle.js : "+curBangleVer+ + "\nConnected : "+conf.btName,"Long") } function btn_OnStart() { From 2cbdde59dee0417ef133797e4ac8dde4d862be18 Mon Sep 17 00:00:00 2001 From: nujw Date: Wed, 9 Feb 2022 15:32:48 +1300 Subject: [PATCH 09/60] Update app.js --- apps/speedalt2/app.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/speedalt2/app.js b/apps/speedalt2/app.js index 9c5245dcf..bf5116fba 100644 --- a/apps/speedalt2/app.js +++ b/apps/speedalt2/app.js @@ -6,7 +6,7 @@ Mike Bennett mike[at]kereru.com 1.34 : Add bluetooth data stream for Droidscript 1.43 : Keep GPS in SuperE mode while using Droiscript screen mirroring */ -var v = '1.47'; +var v = '1.48'; var vDroid = '1.50'; // Required DroidScript program version /*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */ @@ -175,6 +175,7 @@ var KalmanFilter = (function () { var buf = Graphics.createArrayBuffer(240,160,2,{msb:true}); +/* let LED = // LED as minimal and only definition (as instance / singleton) { isOn: false // status on / off, not needed if you don't need to ask for it , set: function(v) { // turn on w/ no arg or truey, else off @@ -183,6 +184,7 @@ let LED = // LED as minimal and only definition (as instance / singleton) , write: function(v) { this.set(v); } // turn on w/ no arg or truey, else off , toggle: function() { this.set( ! this.isOn); } // toggle the LED }, LED1 = LED; // LED1 as 'synonym' for LED +*/ var lf = {fix:0,satellites:0}; var showMax = 0; // 1 = display the max values. 0 = display the cur fix From e1ffb6c5c7b94105ca4ab5d61c614cba9315c9a7 Mon Sep 17 00:00:00 2001 From: nujw Date: Wed, 9 Feb 2022 15:33:15 +1300 Subject: [PATCH 10/60] Update metadata.json --- apps/speedalt2/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/speedalt2/metadata.json b/apps/speedalt2/metadata.json index 35298d42d..8ff29c624 100644 --- a/apps/speedalt2/metadata.json +++ b/apps/speedalt2/metadata.json @@ -2,7 +2,7 @@ "id": "speedalt2", "name": "GPS Adventure Sports II", "shortName":"GPS Adv Sport II", - "version":"1.47", + "version":"1.48", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "icon": "app.png", "type": "app", From af0a286dcdf79e2f295922578c35962cdd206382 Mon Sep 17 00:00:00 2001 From: nujw Date: Wed, 9 Feb 2022 16:03:18 +1300 Subject: [PATCH 11/60] Update app.js --- apps/speedalt2/app.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/speedalt2/app.js b/apps/speedalt2/app.js index bf5116fba..73fa3bacb 100644 --- a/apps/speedalt2/app.js +++ b/apps/speedalt2/app.js @@ -6,7 +6,7 @@ Mike Bennett mike[at]kereru.com 1.34 : Add bluetooth data stream for Droidscript 1.43 : Keep GPS in SuperE mode while using Droiscript screen mirroring */ -var v = '1.48'; +var v = '1.49'; var vDroid = '1.50'; // Required DroidScript program version /*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */ @@ -175,16 +175,16 @@ var KalmanFilter = (function () { var buf = Graphics.createArrayBuffer(240,160,2,{msb:true}); -/* + let LED = // LED as minimal and only definition (as instance / singleton) { isOn: false // status on / off, not needed if you don't need to ask for it , set: function(v) { // turn on w/ no arg or truey, else off - g.setColor((this.isOn=(v===undefined||!!v))?1:0,0,0).fillCircle(40,10,10); } + g.setColor((this.isOn=(v===undefined||!!v))?1:0,0,0).fillCircle(120,10,10); } , reset: function() { this.set(false); } // turn off , write: function(v) { this.set(v); } // turn on w/ no arg or truey, else off , toggle: function() { this.set( ! this.isOn); } // toggle the LED }, LED1 = LED; // LED1 as 'synonym' for LED -*/ + var lf = {fix:0,satellites:0}; var showMax = 0; // 1 = display the max values. 0 = display the cur fix From a33f848e261a3e1839a3456615882c44303e47c7 Mon Sep 17 00:00:00 2001 From: nujw Date: Wed, 9 Feb 2022 16:03:40 +1300 Subject: [PATCH 12/60] Update metadata.json --- apps/speedalt2/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/speedalt2/metadata.json b/apps/speedalt2/metadata.json index 8ff29c624..4ace46854 100644 --- a/apps/speedalt2/metadata.json +++ b/apps/speedalt2/metadata.json @@ -2,7 +2,7 @@ "id": "speedalt2", "name": "GPS Adventure Sports II", "shortName":"GPS Adv Sport II", - "version":"1.48", + "version":"1.49", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "icon": "app.png", "type": "app", From 0d5f62aac97946c773f86c2a9db4b46776f6ed44 Mon Sep 17 00:00:00 2001 From: nujw Date: Wed, 9 Feb 2022 16:28:04 +1300 Subject: [PATCH 13/60] Update GPS Adv Sports II.js --- apps/speedalt2/GPS Adv Sports II.js | 49 +++++++++++++++++++---------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/apps/speedalt2/GPS Adv Sports II.js b/apps/speedalt2/GPS Adv Sports II.js index 502a71001..176113248 100644 --- a/apps/speedalt2/GPS Adv Sports II.js +++ b/apps/speedalt2/GPS Adv Sports II.js @@ -3,23 +3,39 @@ app.LoadPlugin("PuckJS"); -app.SetDebugEnabled(false); +app.SetDebugEnabled(true); -conf = JSON.parse( app.LoadText( "settings", "{}" )) -if ( typeof(conf.btAddr) != 'string' ) conf.btAddr = '' // Address of last connection -if ( typeof(conf.btName) != 'string' ) conf.btName = '' // Name of last connection -v = '1.51' // Version of this script +v = '1.52' // Version of this script requiredBangleVer = '1.47'; // Minimum speedalt2 version required on Bangle curBangleVer = '-.--' isStopped = true; // Data receive turned off lastData = new Date().getTime() / 1000; // Time of last data received +//Colours // Mode = 0 // 0=SPD, 1=ALT, 2=DST, 3=VMG, 4=POSN, 5=TIME btnOff = '#175A63' btnOn = '#4285F4' col = new Array(['black'],['#64FF00'],['#FCFA00'],['#00E4FF']) // bg, main, units, wp - 0xFFFF,0x007F,0x0054,0x0054 +// Settings +conf = JSON.parse( app.LoadText( "settings", "{}" )) +if ( typeof(conf.btAddr) != 'string' ) conf.btAddr = '' // Address of last connection +if ( typeof(conf.btName) != 'string' ) conf.btName = '' // Name of last connection + +// Extend PuckJS +app.CreateBangleJS = function( options ) +{ + return new BangleJS( options ); +} + +class BangleJS extends PuckJS { + + constructor(options) { + super(options) + } + +} //Called when application is started. function OnStart() { @@ -28,20 +44,21 @@ function OnStart() { // Connect to Bangle if( !app.IsBluetoothEnabled() ) app.SetBluetoothEnabled( true ); - puck = app.CreatePuckJS(); - puck.SetOnConnect(onConnect); // Callback. - puck.SetOnReceive(readResponse); // Callback to capture console output from app. +// puck = app.CreatePuckJS(); + bngl = app.CreateBangleJS(); + bngl.SetOnConnect(onConnect); // Callback. + bngl.SetOnReceive(readResponse); // Callback to capture console output from app. if ( conf.btAddr == '' ) { console.log('Scanning') - puck.Scan("Bangle"); + bngl.Scan("Bangle"); } else { console.log('Reconnecting to : '+conf.btAddr) - puck.address = conf.btAddr - puck.name = conf.btName - puck.Connect(puck.address); + bngl.address = conf.btAddr + bngl.name = conf.btName + bngl.Connect(bngl.address); } app.SetDebugEnabled(false); @@ -266,7 +283,7 @@ function checkConnection() { // Flash orange 'led' indicator for connection attempts. setLED(led,true,"#FC8A00") setTimeout(function() {setLED(led,false,-1)}, 500) - puck.Connect(conf.btAddr) + bngl.Connect(conf.btAddr) } } @@ -280,14 +297,14 @@ function btn_OnAbout() { function btn_OnStart() { btnStart.SetBackColor(btnOn) btnStop.SetBackColor(btnOff) - puck.SendCode('btOn(1)\n') // Enable the data send + bngl.SendCode('btOn(1)\n') // Enable the data send isStopped = false } function btn_OnStop() { btnStart.SetBackColor(btnOff) btnStop.SetBackColor(btnOn) - puck.SendCode('btOn(0)\n') // Disable the data send + bngl.SendCode('btOn(0)\n') // Disable the data send isStopped = true val.SetText('') val2.SetText('') @@ -299,5 +316,5 @@ function btn_OnStop() { function btn_OnScan() { btnStart.SetBackColor(btnOff) btnStop.SetBackColor(btnOff) - puck.Scan("Bangle"); + bngl.Scan("Bangle"); } From 7c3d4c5908f8d43d39f21dad956d1c9f8eec1bbb Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Sun, 6 Feb 2022 17:51:07 +0100 Subject: [PATCH 14/60] bthrv - Write interval data even if values are missing --- apps/bthrv/ChangeLog | 11 +------- apps/bthrv/app.js | 60 +++++++++++++++++++++++----------------- apps/bthrv/metadata.json | 2 +- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/apps/bthrv/ChangeLog b/apps/bthrv/ChangeLog index 0e51186a4..e144fd8f9 100644 --- a/apps/bthrv/ChangeLog +++ b/apps/bthrv/ChangeLog @@ -1,11 +1,2 @@ 0.01: New App! -0.02: Make overriding the HRM event optional - Emit BTHRM event for external sensor - Add recorder app plugin -0.03: Prevent readings from internal sensor mixing into BT values - Mark events with src property - Show actual source of event in app -0.04: Allow reading additional data if available: HRM battery and position - Better caching of scanned BT device properties - New setting for not starting the BTHRM together with HRM - Save some RAM by not definining functions if disabled in settings +0.02: Write available data on reset or kill diff --git a/apps/bthrv/app.js b/apps/bthrv/app.js index 7f6ec2d35..067c84f56 100644 --- a/apps/bthrv/app.js +++ b/apps/bthrv/app.js @@ -11,34 +11,30 @@ var currentSlot = 0; var hrvSlots = [10,20,30,60,120,300]; var hrvValues = {}; var rrRmsProgress; -var saved = false; var rrNumberOfValues = 0; var rrSquared = 0; -var rrLastValue +var rrLastValue; var rrMax; var rrMin; function calcHrv(rr){ //Calculate HRV with RMSSD method: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5624990/ - for (currentRr of rr){ + for (var currentRr of rr){ if (!rrMax) rrMax = currentRr; if (!rrMin) rrMin = currentRr; rrMax = Math.max(rrMax, currentRr); rrMin = Math.min(rrMin, currentRr); - //print("Calc for: " + currentRr); rrNumberOfValues++; if (!rrLastValue){ rrLastValue = currentRr; continue; } rrSquared += (rrLastValue - currentRr)*(rrLastValue - currentRr); - - //print("rr²: " + rrSquared); + rrLastValue = currentRr; } var rms = Math.sqrt(rrSquared / rrNumberOfValues); - //print("rms: " + rms); return rms; } @@ -56,17 +52,36 @@ function draw(y, hrv) { if (hrvValues[hrvSlots[i]]) str += hrvValues[hrvSlots[i]].toFixed(1) + "ms"; g.setFontVector(16).drawString(str,px,y+44+(i*17)); } - + g.setRotation(3); g.setFontVector(12).drawString("Reset",g.getHeight()/2, g.getWidth()-10); g.setRotation(0); } +function write(){ + if (!hrvValues[hrvSlots[0]]){ + return; + } + + var file = require('Storage').open("bthrv.csv", "a"); + var data = new Date(startingTime).toISOString(); + for (var i = 0; i < hrvSlots.length; i++ ){ + data += ","; + if (hrvValues[hrvSlots[i]]){ + data += hrvValues[hrvSlots[i]]; + } + } + + data += "," + rrMax + "," + rrMin + ","+rrNumberOfValues; + data += "\n"; + file.write(data); + Bangle.buzz(500); +} + function onBtHrm(e) { if (e.rr && !startingTime) Bangle.buzz(500); if (e.rr && !startingTime) startingTime=Date.now(); - //print("Event:" + e.rr); - + var hrv = calcHrv(e.rr); if (hrv){ if (currentSlot <= hrvSlots.length && (Date.now() - startingTime) > (hrvSlots[currentSlot] * 1000) && !hrvValues[hrvSlots[currentSlot]]){ @@ -74,35 +89,25 @@ function onBtHrm(e) { currentSlot++; } } - if (!saved && currentSlot == hrvSlots.length){ - var file = require('Storage').open("bthrv.csv", "a"); - var data = new Date(startingTime).toISOString(); - for (var c of hrvSlots){ - data+=","+hrvValues[c]; - } - data+="," + rrMax + "," + rrMin + ","+rrNumberOfValues; - data+="\n"; - file.write(data); - saved = true; - Bangle.buzz(500); - } + if (hrv){ - if (!ui){ + if (!ui){ Bangle.setUI("leftright", ()=>{ resetHrv(); clear(30); }); ui = true; } + draw(30, hrv); } } function resetHrv(){ + write(); hrvValues={}; startingTime=undefined; currentSlot=0; - saved=false; rrNumberOfValues = 0; rrSquared = 0; rrLastValue = undefined; @@ -117,7 +122,6 @@ g.clear(); Bangle.loadWidgets(); Bangle.drawWidgets(); - if (Bangle.setBTHRMPower){ Bangle.on('BTHRM', onBtHrm); Bangle.setBTHRMPower(1,'bthrv'); @@ -133,6 +137,11 @@ if (Bangle.setBTHRMPower){ file.write(data); } + E.on('kill', ()=>{ + write(); + Bangle.setBTHRMPower(0,'bthrv'); + }); + g.reset().setFont("6x8",2).setFontAlign(0,0); g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16); } else { @@ -140,4 +149,3 @@ if (Bangle.setBTHRMPower){ g.drawString("Missing BT HRM",g.getWidth()/2,g.getHeight()/2 - 16); } -E.on('kill', ()=>Bangle.setBTHRMPower(0,'bthrv')); diff --git a/apps/bthrv/metadata.json b/apps/bthrv/metadata.json index 6a8e7e940..183008034 100644 --- a/apps/bthrv/metadata.json +++ b/apps/bthrv/metadata.json @@ -2,7 +2,7 @@ "id": "bthrv", "name": "Bluetooth Heart Rate variance calculator", "shortName": "BT HRV", - "version": "0.01", + "version": "0.02", "description": "Calculates HRV from a a BT HRM with interval data", "icon": "app.png", "type": "app", From 8438c90c1a255791700dc126561854690733bfed Mon Sep 17 00:00:00 2001 From: Marco H Date: Mon, 14 Feb 2022 21:04:40 +0100 Subject: [PATCH 15/60] Update changelog --- apps/circlesclock/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/circlesclock/ChangeLog b/apps/circlesclock/ChangeLog index 58ab4cd48..7165f8521 100644 --- a/apps/circlesclock/ChangeLog +++ b/apps/circlesclock/ChangeLog @@ -19,3 +19,4 @@ Colors of circles can be configured Color depending on value (green -> red, red -> green) option Good HRM value will not be overwritten so fast anymore +0.10: Use roboto font for time, date and day of week and center align them From 6be26f110ba9c399f0a319c2bf6c453276a5d2e1 Mon Sep 17 00:00:00 2001 From: nujw Date: Tue, 15 Feb 2022 09:47:51 +1300 Subject: [PATCH 16/60] Update ChangeLog --- apps/speedalt2/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/speedalt2/ChangeLog b/apps/speedalt2/ChangeLog index 4ff386666..602147856 100644 --- a/apps/speedalt2/ChangeLog +++ b/apps/speedalt2/ChangeLog @@ -12,3 +12,4 @@ 1.10: Adds Kalman filter. 1.14: Add VMG and coordinates screens 1.43: Adds mirroring of the watch face to an Android device. See README.md +1.48: Droidscript mirroring prog automatically uses last connection address. Auto connects when run. From 016cce5bf73ad55f7151da2e85f368e282f149a1 Mon Sep 17 00:00:00 2001 From: Daniel Cox Date: Tue, 15 Feb 2022 17:01:08 +1300 Subject: [PATCH 17/60] Initial Commit --- apps/README.md | 12 ++++ apps/app-icon.js | 1 + apps/app.js | 144 ++++++++++++++++++++++++++++++++++++++++++++ apps/metadata.json | 17 ++++++ apps/rolex.png | Bin 0 -> 2406 bytes apps/screenshot.png | Bin 0 -> 3683 bytes 6 files changed, 174 insertions(+) create mode 100644 apps/README.md create mode 100644 apps/app-icon.js create mode 100644 apps/app.js create mode 100644 apps/metadata.json create mode 100644 apps/rolex.png create mode 100644 apps/screenshot.png diff --git a/apps/README.md b/apps/README.md new file mode 100644 index 000000000..e35070c33 --- /dev/null +++ b/apps/README.md @@ -0,0 +1,12 @@ +# Rolex + +![](screenshot.png) + +Created with the aid of the Espruino documentation and looking through many of the wonderful exising watchfaces that have been made. +This has not been tested on a watch yet as I haven't aquired one but has been tested in the emulator. +The hands don't rotate dead on center but they're as close as I could get them to. + +Special thanks to: +* rozek (for his updated widget draw code for utilization with background images) +* Gordon Williams (Bangle.js, watchapps for reference code and documentation) +* The community (for helping drive such a wonderful project) \ No newline at end of file diff --git a/apps/app-icon.js b/apps/app-icon.js new file mode 100644 index 000000000..563a4a78b --- /dev/null +++ b/apps/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkEBxURiIlUgMxiUQC6cCiMhmAqPmQEDkUhF4cjGhUD/4FDn/zAof/GhYMEC4kCEQgAHl/xGoYcDj/yC5ZIFJgnwPBglIn6rNBpEBXp8QiQSBiMQFpMCS4sDXgMjgMhgYXFEgIDBh//kA/EiEhiURiMBBYs/FYSwB+YdBCQIBBkAYBiUQkACBCwTOEUYKaBkUhAAIXCDYMRkYxBIILNDAAMTHgZxBiBFBFQKOCgMvbRUBgUxIYJ3BSYUBmYJBU5QsCkIDBgQIBkcyYJwAFkczeBoAGiYWVgYWTbQMCmchfojgBDxc/+f/mUjC4abBkEf/4ABDY0C+cvmQKFgcxkTyBC47pBC4LgDAAUPmMyh4IEiUQiUyiJHBIwJ9GmMxC4kBmXyGYcBdQMykIPEcIIlBFgMikMzIAxiBkSPEIYqSKmX/mLWTgEimRiGAB0T+bwTVocCMBEAj51GAA4aGif/+AXNh//FAcC//ziEBgEhiCxBiADCXAIDBCIYdFgLCBaIMCkKNBkQIBkQTBgZBDgRdEiIsBGoMBAoLoDLQRxIkMhewMigMyiESiRrNWqpMB+QJHl4hMh/zBI//a5IlDYQcBFQcf+IWKgLJEj4cDgY5IBgf/AoYXEEQp2HHggXEKQIXKAAoXFACIXkA")) \ No newline at end of file diff --git a/apps/app.js b/apps/app.js new file mode 100644 index 000000000..adfe8a2c7 --- /dev/null +++ b/apps/app.js @@ -0,0 +1,144 @@ +/* Set background image */ + +var imgBg = { + width : 176, height : 176, bpp : 8, + transparent : 254, + buffer : require("heatshrink").decompress(atob("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")) +}; + +/* Set hour hand image */ + +var imgHour = { + width : 19, height : 62, bpp : 8, + transparent : 254, + buffer : require("heatshrink").decompress(atob("/wAF64AEBgwQJChYRJCY4RLCYoRNCQYROCYYS/CWKXSXqbjTCZYRHCZIRJCY4RLCQVWqwSQwOBCX4S/CXb1VCRYRGChIJC1hJDAYYTFA4QMBCQuB1gTFCIoSFCYokGCQyJEBQwSHA4ISSCYIKFqwSRSgiKCCQwhBM4QAFEpAMEDA1WCQgyHHwy9JChASRchYkJCYpNBAAQQICYxaHCZwRKCYTTCCR6ZCCR62BLoIRMawoSNJgQSRCKASmCYQSRCKASmABIA=")) +}; + +/* Set minute hand image */ + +var imgMin = { + width : 10, height : 80, bpp : 8, + transparent : 254, + buffer : require("heatshrink").decompress(atob("/wAL64ABA44ADBBAKCBKQAdHhJQILZBtIBLwKEHRoTKBMlWqwJIwIJ/BP4J/BKDbJBM2BBP4J/BKgADBJoKEBAgJoBQYJIBAwJoBQIJIABw=")) +}; + +/* Set second hand image */ + +var imgSec = { + width : 8, height : 116, bpp : 8, + transparent : 254, + buffer : require("heatshrink").decompress(atob("/3XAAf+AAIHEBAQHMC4QIEA4wGDBAYH/A/4HsayD0BBYj9DBowDLC44nIHAxHGR/4H/A/4H/A9IGBA4YFCAAYHHAAmsAomBqwABA4gICA4oIBC5YICGZRAGK5Cf/A/4H/A7YGFA4oA==")) +}; + +/* Set variables to get screen width, height and center points */ + +let W = g.getWidth(); +let H = g.getHeight(); +let cx = W/2; +let cy = H/2; +let Timeout; + +/* set font */ + +require("Font4x5Numeric").add(Graphics); + +Bangle.loadWidgets(); + +/* Custom version of Bangle.drawWidgets (does not clear the widget areas) Thanks to rozek */ + +Bangle.drawWidgets = function () { + var w = g.getWidth(), h = g.getHeight(); + + var pos = { + tl:{x:0, y:0, r:0, c:0}, // if r==1, we're right->left + tr:{x:w-1, y:0, r:1, c:0}, + bl:{x:0, y:h-24, r:0, c:0}, + br:{x:w-1, y:h-24, r:1, c:0} + }; + + if (global.WIDGETS) { + for (var wd of WIDGETS) { + var p = pos[wd.area]; + if (!p) continue; + + wd.x = p.x - p.r*wd.width; + wd.y = p.y; + + p.x += wd.width*(1-2*p.r); + p.c++; + } + + g.reset(); // also loads the current theme + + try { + for (var wd of WIDGETS) { + g.setClipRect(wd.x,wd.y, wd.x+wd.width-1,23); + wd.draw(wd); + } + } catch (e) { print(e); } + + g.reset(); // clears the clipping rectangle! + } + }; + +/* Draws the clock hands and date */ + +function drawHands() { + let d = new Date(); + + let hour = d.getHours() % 12; + let min = d.getMinutes(); + let sec = d.getSeconds(); + + let twoPi = 2*Math.PI; + let Pi = Math.PI; + let halfPi = Math.PI/2; + + let hourAngle = (hour+(min/60))/12 * twoPi - Pi; + let minAngle = (min/60) * twoPi - Pi; + let secAngle = (sec/60) * twoPi - Pi; + + let hourSin = Math.sin(hourAngle); + let hourCos = Math.cos(hourAngle); + let minSin = Math.sin(minAngle); + let minCos = Math.cos(minAngle); + let secSin = Math.sin(secAngle); + let secCos = Math.cos(secAngle); + + g.drawImage(imgHour,cx-22*hourSin,cy+22*hourCos,{rotate:hourAngle}); + g.drawImage(imgMin,cx-34*minSin,cy+34*minCos,{rotate:minAngle}); + g.drawImage(imgSec,cx-25*secSin,cy+25*secCos,{rotate:secAngle}); + g.setFont("4x5Numeric:3"); + g.drawString(d.getDate(),157,81); +} + +function drawBackground() { + g.setBgColor(0,0,0); + g.setColor(1,1,1); + g.clear(); + g.drawImage(imgBg,0,0); + g.reset(); +} + +/* Refresh the display every second */ + +function displayRefresh() { + g.clear(true); + drawBackground(); + drawHands(); + Bangle.drawWidgets(); + + let Pause = 1000 - (Date.now() % 1000); + Timeout = setTimeout(displayRefresh,Pause); +} +setTimeout(displayRefresh,500); + +Bangle.on('lcdPower', (on) => { + if (on) { + if (Timeout != null) { clearTimeout(Timeout); Timeout = undefined;} + displayRefresh(); + } +}); + +Bangle.loadWidgets(); +Bangle.setUI("clock"); \ No newline at end of file diff --git a/apps/metadata.json b/apps/metadata.json new file mode 100644 index 000000000..2c4437b35 --- /dev/null +++ b/apps/metadata.json @@ -0,0 +1,17 @@ +{ "id": "rolex", + "name": "rolex", + "shortName":"rolex", + "icon": "rolex.png", + "screenshots": [{"url":"screenshot.png"}], + "version":"0.01", + "description": "A rolex like watch face", + "tags": "clock", + "type": "clock", + "supports":["BANGLEJS2"], + "readme": "README.md", + "allow_emulator": true, + "storage": [ + {"name":"rolex.app.js","url":"app.js"}, + {"name":"rolex.img","url":"app-icon.js","evaluate":true} + ] + } \ No newline at end of file diff --git a/apps/rolex.png b/apps/rolex.png new file mode 100644 index 0000000000000000000000000000000000000000..2e204376341d56b7fc5bf294e3da2e9eb23be6cc GIT binary patch literal 2406 zcmV-s37PhZP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2>VGyK~!i%#aU&D zRBaSK>#n&5b|-cxc7qr{>{bj6LD8;3lqyw9(xge_vxuHVynsxZGD)gb?zdsW8#Zi^bm`Jb z*|KHj)TvW4W5x{0moJ~sB6w_2R_~S2qi#5oaNNmkby%fS>p6 z+b6mpeol_0PoLiRNu*!De)9SAXWzHT#JaL@s_D@>o*Zq~tQk+kcFluY2R*CidZQ`O zG;P|H?$oknOEY=$WV%D!wry!stXMJArcE18v__2@CVTem^!>k>v|oc-Sty(qd(W8fGdKF!OOrJho+O=y(o4R%DN~1=NeTS=1$@YuB!P@!h+3`BDgILi6U$rD)Nj+>H+(K1jK8<>bzt zJA7#2!i8nmu3eIlkRYc|pO&XjpK>9O9zBwR1q({vym@8Mo;{K?XHI$f@+Gfz?AVd- z#Crmza^=dhZrwT=HENXP$dQ95hD}BC`1I)$ZTv2Ti^v4Uix-#U$B%Oi4_816;xHLA zW)wwYJ`fM-+_^I!iiP7O)W1pM-oAaytb%)Ci)&a67KrOutj2@XsZ)m=f%k%TID+S# zJb99?hfD_=g3lUPd`_Sw2@EJe3P9urF1m}RRjXD6stz4G71KJnqrn>S|S#ED649F5aIR)G&A_}l^w?r`ne zHM&*BiWRxb*j;Gp(xrKN*wy#%-^=jf!)5vMUko;~I9&=cBsrGtKp2fN%Hi-K5%B0Q??2%Vrx!t%E71V5yo$NwF_nxM2G9>0=FKubN^- zJ-|$91^pc95Pm*>{CFbv&!0azzG>4Y898z!fyzyAFK{QZAdSbM8uEP)5(Jrpc9Du! zWUPaMBS-=V4jdrzVo?ang9i_a@+MBKOqnvwHf}Ov#0cr#yEhX7f*eQ;eA%&Mhi-oe z0rAp%lcj?P59TwrZrw^^r#o&mI~m<=)1W~Eqm!`S7fP>Rzs|x)vl^d)3hvOMLwrZX z^*|^A4xp6+!Ud`U!H$DIfLDPM%$_})X;zCDUiT{CQ;sOW5@+x(NL*l15h#h>2l*aQ z4oKg*b0<3viom1+pca@I$Q(#lo;-P&|G_vx7eMB)DA+5i27#9s!E*s}5Og>IfV%no z?kR}-_U$WU$Bre5>ej8BOqnu;{s#t%Sg{GX41yRf7AJtgMzL7L1Qw}oAxDoMb#1pm zfa>bfB#ww3=Rc4?XaS(c2t4dACIA|MD~3Py>KNA-yUh%c4b#d=`<3zo7ZA1aX7OBObss5u9*UxMrl;$&n<-;wT{gZ~8u= z6vaGn2AF_40l1+tk)&M-H}=4ftb;6QG+mi-sG6`C|apm1YN7x^yWE5Xi-g7n%2O-@eW7$i?s8 zy<;^6Yyb!X88HU31=Q6NkhUOg=s*DcFjlW#J+@$ws&OB>AMx?=EPT+(Kull^#Xmq4 zxgL!MjNv{2sg|iMngW{){w(AK67oNC|EyWFMC;+8iVP3a0)%-Slel{IDg*NT`SV;f z2plrY(4j*~BhdfRdYC_mRIgrLwrttL$zZbpfKDKT+yQ?O&?{D~@HZ78C_xu2Sm5t5 zVQzSq1Aaki6hMQtHfho%S9AoR)v8sCI}b2=`0ybofD#iE$HK4?*xg&VZV^c_Sxg%B zG5%MnQiYTnHb80so15UI@hguKME-XqSvY{Ad&rO>+$j|3csX1M{09^PTm_%8AW&cI zx+VjrN$fBd47Y(xXer6f0MQF4pbuy=1bPJo-y%{Vx01M^vA_Vs=fLks0=8dtoXlej z&Ir^x=&ESuI+-Dvbu1NmUP+$OfMv7SX5&GvgCP|aob!7Y3UZr`S5?>Y|73+a*=FNG zt%D(T$&w{}A;b+}(18QmLfzWv{rmUlvovM0HE**Cqt-#I;+P%5LTomUg55e2uIq5+ z$`#t-rz_1!RvQ(^b07*qoM6N<$f@eB;cK`qY literal 0 HcmV?d00001 diff --git a/apps/screenshot.png b/apps/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..288c82cf4d68e39b11a7ba4e19af7fd6d0f04c01 GIT binary patch literal 3683 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 Date: Tue, 15 Feb 2022 17:02:37 +1300 Subject: [PATCH 18/60] Delete app-icon.js --- apps/app-icon.js | 1 - 1 file changed, 1 deletion(-) delete mode 100644 apps/app-icon.js diff --git a/apps/app-icon.js b/apps/app-icon.js deleted file mode 100644 index 563a4a78b..000000000 --- a/apps/app-icon.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("mEwwkEBxURiIlUgMxiUQC6cCiMhmAqPmQEDkUhF4cjGhUD/4FDn/zAof/GhYMEC4kCEQgAHl/xGoYcDj/yC5ZIFJgnwPBglIn6rNBpEBXp8QiQSBiMQFpMCS4sDXgMjgMhgYXFEgIDBh//kA/EiEhiURiMBBYs/FYSwB+YdBCQIBBkAYBiUQkACBCwTOEUYKaBkUhAAIXCDYMRkYxBIILNDAAMTHgZxBiBFBFQKOCgMvbRUBgUxIYJ3BSYUBmYJBU5QsCkIDBgQIBkcyYJwAFkczeBoAGiYWVgYWTbQMCmchfojgBDxc/+f/mUjC4abBkEf/4ABDY0C+cvmQKFgcxkTyBC47pBC4LgDAAUPmMyh4IEiUQiUyiJHBIwJ9GmMxC4kBmXyGYcBdQMykIPEcIIlBFgMikMzIAxiBkSPEIYqSKmX/mLWTgEimRiGAB0T+bwTVocCMBEAj51GAA4aGif/+AXNh//FAcC//ziEBgEhiCxBiADCXAIDBCIYdFgLCBaIMCkKNBkQIBkQTBgZBDgRdEiIsBGoMBAoLoDLQRxIkMhewMigMyiESiRrNWqpMB+QJHl4hMh/zBI//a5IlDYQcBFQcf+IWKgLJEj4cDgY5IBgf/AoYXEEQp2HHggXEKQIXKAAoXFACIXkA")) \ No newline at end of file From be54dbadd08e99154a10f819ea7af1c2e8f8ba42 Mon Sep 17 00:00:00 2001 From: Daniel Cox Date: Tue, 15 Feb 2022 17:02:51 +1300 Subject: [PATCH 19/60] Delete README.md --- apps/README.md | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 apps/README.md diff --git a/apps/README.md b/apps/README.md deleted file mode 100644 index e35070c33..000000000 --- a/apps/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Rolex - -![](screenshot.png) - -Created with the aid of the Espruino documentation and looking through many of the wonderful exising watchfaces that have been made. -This has not been tested on a watch yet as I haven't aquired one but has been tested in the emulator. -The hands don't rotate dead on center but they're as close as I could get them to. - -Special thanks to: -* rozek (for his updated widget draw code for utilization with background images) -* Gordon Williams (Bangle.js, watchapps for reference code and documentation) -* The community (for helping drive such a wonderful project) \ No newline at end of file From 6e1e3be435c0a398afbde78a5f8b6d03be38bcd0 Mon Sep 17 00:00:00 2001 From: Daniel Cox Date: Tue, 15 Feb 2022 17:03:02 +1300 Subject: [PATCH 20/60] Delete app.js --- apps/app.js | 144 ---------------------------------------------------- 1 file changed, 144 deletions(-) delete mode 100644 apps/app.js diff --git a/apps/app.js b/apps/app.js deleted file mode 100644 index adfe8a2c7..000000000 --- a/apps/app.js +++ /dev/null @@ -1,144 +0,0 @@ -/* Set background image */ - -var imgBg = { - width : 176, height : 176, bpp : 8, - transparent : 254, - buffer : require("heatshrink").decompress(atob("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")) -}; - -/* Set hour hand image */ - -var imgHour = { - width : 19, height : 62, bpp : 8, - transparent : 254, - buffer : require("heatshrink").decompress(atob("/wAF64AEBgwQJChYRJCY4RLCYoRNCQYROCYYS/CWKXSXqbjTCZYRHCZIRJCY4RLCQVWqwSQwOBCX4S/CXb1VCRYRGChIJC1hJDAYYTFA4QMBCQuB1gTFCIoSFCYokGCQyJEBQwSHA4ISSCYIKFqwSRSgiKCCQwhBM4QAFEpAMEDA1WCQgyHHwy9JChASRchYkJCYpNBAAQQICYxaHCZwRKCYTTCCR6ZCCR62BLoIRMawoSNJgQSRCKASmCYQSRCKASmABIA=")) -}; - -/* Set minute hand image */ - -var imgMin = { - width : 10, height : 80, bpp : 8, - transparent : 254, - buffer : require("heatshrink").decompress(atob("/wAL64ABA44ADBBAKCBKQAdHhJQILZBtIBLwKEHRoTKBMlWqwJIwIJ/BP4J/BKDbJBM2BBP4J/BKgADBJoKEBAgJoBQYJIBAwJoBQIJIABw=")) -}; - -/* Set second hand image */ - -var imgSec = { - width : 8, height : 116, bpp : 8, - transparent : 254, - buffer : require("heatshrink").decompress(atob("/3XAAf+AAIHEBAQHMC4QIEA4wGDBAYH/A/4HsayD0BBYj9DBowDLC44nIHAxHGR/4H/A/4H/A9IGBA4YFCAAYHHAAmsAomBqwABA4gICA4oIBC5YICGZRAGK5Cf/A/4H/A7YGFA4oA==")) -}; - -/* Set variables to get screen width, height and center points */ - -let W = g.getWidth(); -let H = g.getHeight(); -let cx = W/2; -let cy = H/2; -let Timeout; - -/* set font */ - -require("Font4x5Numeric").add(Graphics); - -Bangle.loadWidgets(); - -/* Custom version of Bangle.drawWidgets (does not clear the widget areas) Thanks to rozek */ - -Bangle.drawWidgets = function () { - var w = g.getWidth(), h = g.getHeight(); - - var pos = { - tl:{x:0, y:0, r:0, c:0}, // if r==1, we're right->left - tr:{x:w-1, y:0, r:1, c:0}, - bl:{x:0, y:h-24, r:0, c:0}, - br:{x:w-1, y:h-24, r:1, c:0} - }; - - if (global.WIDGETS) { - for (var wd of WIDGETS) { - var p = pos[wd.area]; - if (!p) continue; - - wd.x = p.x - p.r*wd.width; - wd.y = p.y; - - p.x += wd.width*(1-2*p.r); - p.c++; - } - - g.reset(); // also loads the current theme - - try { - for (var wd of WIDGETS) { - g.setClipRect(wd.x,wd.y, wd.x+wd.width-1,23); - wd.draw(wd); - } - } catch (e) { print(e); } - - g.reset(); // clears the clipping rectangle! - } - }; - -/* Draws the clock hands and date */ - -function drawHands() { - let d = new Date(); - - let hour = d.getHours() % 12; - let min = d.getMinutes(); - let sec = d.getSeconds(); - - let twoPi = 2*Math.PI; - let Pi = Math.PI; - let halfPi = Math.PI/2; - - let hourAngle = (hour+(min/60))/12 * twoPi - Pi; - let minAngle = (min/60) * twoPi - Pi; - let secAngle = (sec/60) * twoPi - Pi; - - let hourSin = Math.sin(hourAngle); - let hourCos = Math.cos(hourAngle); - let minSin = Math.sin(minAngle); - let minCos = Math.cos(minAngle); - let secSin = Math.sin(secAngle); - let secCos = Math.cos(secAngle); - - g.drawImage(imgHour,cx-22*hourSin,cy+22*hourCos,{rotate:hourAngle}); - g.drawImage(imgMin,cx-34*minSin,cy+34*minCos,{rotate:minAngle}); - g.drawImage(imgSec,cx-25*secSin,cy+25*secCos,{rotate:secAngle}); - g.setFont("4x5Numeric:3"); - g.drawString(d.getDate(),157,81); -} - -function drawBackground() { - g.setBgColor(0,0,0); - g.setColor(1,1,1); - g.clear(); - g.drawImage(imgBg,0,0); - g.reset(); -} - -/* Refresh the display every second */ - -function displayRefresh() { - g.clear(true); - drawBackground(); - drawHands(); - Bangle.drawWidgets(); - - let Pause = 1000 - (Date.now() % 1000); - Timeout = setTimeout(displayRefresh,Pause); -} -setTimeout(displayRefresh,500); - -Bangle.on('lcdPower', (on) => { - if (on) { - if (Timeout != null) { clearTimeout(Timeout); Timeout = undefined;} - displayRefresh(); - } -}); - -Bangle.loadWidgets(); -Bangle.setUI("clock"); \ No newline at end of file From 7a7cc179dd75b0d0c1b5ebbbccce87c7c0300fb9 Mon Sep 17 00:00:00 2001 From: Daniel Cox Date: Tue, 15 Feb 2022 17:03:15 +1300 Subject: [PATCH 21/60] Delete metadata.json --- apps/metadata.json | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 apps/metadata.json diff --git a/apps/metadata.json b/apps/metadata.json deleted file mode 100644 index 2c4437b35..000000000 --- a/apps/metadata.json +++ /dev/null @@ -1,17 +0,0 @@ -{ "id": "rolex", - "name": "rolex", - "shortName":"rolex", - "icon": "rolex.png", - "screenshots": [{"url":"screenshot.png"}], - "version":"0.01", - "description": "A rolex like watch face", - "tags": "clock", - "type": "clock", - "supports":["BANGLEJS2"], - "readme": "README.md", - "allow_emulator": true, - "storage": [ - {"name":"rolex.app.js","url":"app.js"}, - {"name":"rolex.img","url":"app-icon.js","evaluate":true} - ] - } \ No newline at end of file From 7a6278b94cc52ac099c2ca49987f0b255dfd3dc6 Mon Sep 17 00:00:00 2001 From: Daniel Cox Date: Tue, 15 Feb 2022 17:03:23 +1300 Subject: [PATCH 22/60] Delete rolex.png --- apps/rolex.png | Bin 2406 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 apps/rolex.png diff --git a/apps/rolex.png b/apps/rolex.png deleted file mode 100644 index 2e204376341d56b7fc5bf294e3da2e9eb23be6cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2406 zcmV-s37PhZP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2>VGyK~!i%#aU&D zRBaSK>#n&5b|-cxc7qr{>{bj6LD8;3lqyw9(xge_vxuHVynsxZGD)gb?zdsW8#Zi^bm`Jb z*|KHj)TvW4W5x{0moJ~sB6w_2R_~S2qi#5oaNNmkby%fS>p6 z+b6mpeol_0PoLiRNu*!De)9SAXWzHT#JaL@s_D@>o*Zq~tQk+kcFluY2R*CidZQ`O zG;P|H?$oknOEY=$WV%D!wry!stXMJArcE18v__2@CVTem^!>k>v|oc-Sty(qd(W8fGdKF!OOrJho+O=y(o4R%DN~1=NeTS=1$@YuB!P@!h+3`BDgILi6U$rD)Nj+>H+(K1jK8<>bzt zJA7#2!i8nmu3eIlkRYc|pO&XjpK>9O9zBwR1q({vym@8Mo;{K?XHI$f@+Gfz?AVd- z#Crmza^=dhZrwT=HENXP$dQ95hD}BC`1I)$ZTv2Ti^v4Uix-#U$B%Oi4_816;xHLA zW)wwYJ`fM-+_^I!iiP7O)W1pM-oAaytb%)Ci)&a67KrOutj2@XsZ)m=f%k%TID+S# zJb99?hfD_=g3lUPd`_Sw2@EJe3P9urF1m}RRjXD6stz4G71KJnqrn>S|S#ED649F5aIR)G&A_}l^w?r`ne zHM&*BiWRxb*j;Gp(xrKN*wy#%-^=jf!)5vMUko;~I9&=cBsrGtKp2fN%Hi-K5%B0Q??2%Vrx!t%E71V5yo$NwF_nxM2G9>0=FKubN^- zJ-|$91^pc95Pm*>{CFbv&!0azzG>4Y898z!fyzyAFK{QZAdSbM8uEP)5(Jrpc9Du! zWUPaMBS-=V4jdrzVo?ang9i_a@+MBKOqnvwHf}Ov#0cr#yEhX7f*eQ;eA%&Mhi-oe z0rAp%lcj?P59TwrZrw^^r#o&mI~m<=)1W~Eqm!`S7fP>Rzs|x)vl^d)3hvOMLwrZX z^*|^A4xp6+!Ud`U!H$DIfLDPM%$_})X;zCDUiT{CQ;sOW5@+x(NL*l15h#h>2l*aQ z4oKg*b0<3viom1+pca@I$Q(#lo;-P&|G_vx7eMB)DA+5i27#9s!E*s}5Og>IfV%no z?kR}-_U$WU$Bre5>ej8BOqnu;{s#t%Sg{GX41yRf7AJtgMzL7L1Qw}oAxDoMb#1pm zfa>bfB#ww3=Rc4?XaS(c2t4dACIA|MD~3Py>KNA-yUh%c4b#d=`<3zo7ZA1aX7OBObss5u9*UxMrl;$&n<-;wT{gZ~8u= z6vaGn2AF_40l1+tk)&M-H}=4ftb;6QG+mi-sG6`C|apm1YN7x^yWE5Xi-g7n%2O-@eW7$i?s8 zy<;^6Yyb!X88HU31=Q6NkhUOg=s*DcFjlW#J+@$ws&OB>AMx?=EPT+(Kull^#Xmq4 zxgL!MjNv{2sg|iMngW{){w(AK67oNC|EyWFMC;+8iVP3a0)%-Slel{IDg*NT`SV;f z2plrY(4j*~BhdfRdYC_mRIgrLwrttL$zZbpfKDKT+yQ?O&?{D~@HZ78C_xu2Sm5t5 zVQzSq1Aaki6hMQtHfho%S9AoR)v8sCI}b2=`0ybofD#iE$HK4?*xg&VZV^c_Sxg%B zG5%MnQiYTnHb80so15UI@hguKME-XqSvY{Ad&rO>+$j|3csX1M{09^PTm_%8AW&cI zx+VjrN$fBd47Y(xXer6f0MQF4pbuy=1bPJo-y%{Vx01M^vA_Vs=fLks0=8dtoXlej z&Ir^x=&ESuI+-Dvbu1NmUP+$OfMv7SX5&GvgCP|aob!7Y3UZr`S5?>Y|73+a*=FNG zt%D(T$&w{}A;b+}(18QmLfzWv{rmUlvovM0HE**Cqt-#I;+P%5LTomUg55e2uIq5+ z$`#t-rz_1!RvQ(^b07*qoM6N<$f@eB;cK`qY From 2799e45248b418e8c8f1f78202064fbad7374d6c Mon Sep 17 00:00:00 2001 From: Daniel Cox Date: Tue, 15 Feb 2022 17:03:36 +1300 Subject: [PATCH 23/60] Delete screenshot.png --- apps/screenshot.png | Bin 3683 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 apps/screenshot.png diff --git a/apps/screenshot.png b/apps/screenshot.png deleted file mode 100644 index 288c82cf4d68e39b11a7ba4e19af7fd6d0f04c01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3683 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 Date: Tue, 15 Feb 2022 17:04:41 +1300 Subject: [PATCH 24/60] Rolex README.md --- apps/Rolex/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 apps/Rolex/README.md diff --git a/apps/Rolex/README.md b/apps/Rolex/README.md new file mode 100644 index 000000000..4c24aad0c --- /dev/null +++ b/apps/Rolex/README.md @@ -0,0 +1,12 @@ +# Rolex + +![](screenshot.png) + +Created with the aid of the Espruino documentation and looking through many of the wonderful exising watchfaces that have been made. +This has not been tested on a watch yet as I haven't aquired one but has been tested in the emulator. +The hands don't rotate dead on center but they're as close as I could get them to. + +Special thanks to: +* rozek (for his updated widget draw code for utilization with background images) +* Gordon Williams (Bangle.js, watchapps for reference code and documentation) +* The community (for helping drive such a wonderful project) From ced26c57e370bae5c17301f331e0fff4050b0592 Mon Sep 17 00:00:00 2001 From: Daniel Cox Date: Tue, 15 Feb 2022 17:05:08 +1300 Subject: [PATCH 25/60] Initial Upload --- apps/Rolex/app-icon.js | 1 + apps/Rolex/app.js | 144 ++++++++++++++++++++++++++++++++++++++ apps/Rolex/metadata.json | 17 +++++ apps/Rolex/rolex.png | Bin 0 -> 2406 bytes apps/Rolex/screenshot.png | Bin 0 -> 3683 bytes 5 files changed, 162 insertions(+) create mode 100644 apps/Rolex/app-icon.js create mode 100644 apps/Rolex/app.js create mode 100644 apps/Rolex/metadata.json create mode 100644 apps/Rolex/rolex.png create mode 100644 apps/Rolex/screenshot.png diff --git a/apps/Rolex/app-icon.js b/apps/Rolex/app-icon.js new file mode 100644 index 000000000..563a4a78b --- /dev/null +++ b/apps/Rolex/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkEBxURiIlUgMxiUQC6cCiMhmAqPmQEDkUhF4cjGhUD/4FDn/zAof/GhYMEC4kCEQgAHl/xGoYcDj/yC5ZIFJgnwPBglIn6rNBpEBXp8QiQSBiMQFpMCS4sDXgMjgMhgYXFEgIDBh//kA/EiEhiURiMBBYs/FYSwB+YdBCQIBBkAYBiUQkACBCwTOEUYKaBkUhAAIXCDYMRkYxBIILNDAAMTHgZxBiBFBFQKOCgMvbRUBgUxIYJ3BSYUBmYJBU5QsCkIDBgQIBkcyYJwAFkczeBoAGiYWVgYWTbQMCmchfojgBDxc/+f/mUjC4abBkEf/4ABDY0C+cvmQKFgcxkTyBC47pBC4LgDAAUPmMyh4IEiUQiUyiJHBIwJ9GmMxC4kBmXyGYcBdQMykIPEcIIlBFgMikMzIAxiBkSPEIYqSKmX/mLWTgEimRiGAB0T+bwTVocCMBEAj51GAA4aGif/+AXNh//FAcC//ziEBgEhiCxBiADCXAIDBCIYdFgLCBaIMCkKNBkQIBkQTBgZBDgRdEiIsBGoMBAoLoDLQRxIkMhewMigMyiESiRrNWqpMB+QJHl4hMh/zBI//a5IlDYQcBFQcf+IWKgLJEj4cDgY5IBgf/AoYXEEQp2HHggXEKQIXKAAoXFACIXkA")) \ No newline at end of file diff --git a/apps/Rolex/app.js b/apps/Rolex/app.js new file mode 100644 index 000000000..adfe8a2c7 --- /dev/null +++ b/apps/Rolex/app.js @@ -0,0 +1,144 @@ +/* Set background image */ + +var imgBg = { + width : 176, height : 176, bpp : 8, + transparent : 254, + buffer : require("heatshrink").decompress(atob("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")) +}; + +/* Set hour hand image */ + +var imgHour = { + width : 19, height : 62, bpp : 8, + transparent : 254, + buffer : require("heatshrink").decompress(atob("/wAF64AEBgwQJChYRJCY4RLCYoRNCQYROCYYS/CWKXSXqbjTCZYRHCZIRJCY4RLCQVWqwSQwOBCX4S/CXb1VCRYRGChIJC1hJDAYYTFA4QMBCQuB1gTFCIoSFCYokGCQyJEBQwSHA4ISSCYIKFqwSRSgiKCCQwhBM4QAFEpAMEDA1WCQgyHHwy9JChASRchYkJCYpNBAAQQICYxaHCZwRKCYTTCCR6ZCCR62BLoIRMawoSNJgQSRCKASmCYQSRCKASmABIA=")) +}; + +/* Set minute hand image */ + +var imgMin = { + width : 10, height : 80, bpp : 8, + transparent : 254, + buffer : require("heatshrink").decompress(atob("/wAL64ABA44ADBBAKCBKQAdHhJQILZBtIBLwKEHRoTKBMlWqwJIwIJ/BP4J/BKDbJBM2BBP4J/BKgADBJoKEBAgJoBQYJIBAwJoBQIJIABw=")) +}; + +/* Set second hand image */ + +var imgSec = { + width : 8, height : 116, bpp : 8, + transparent : 254, + buffer : require("heatshrink").decompress(atob("/3XAAf+AAIHEBAQHMC4QIEA4wGDBAYH/A/4HsayD0BBYj9DBowDLC44nIHAxHGR/4H/A/4H/A9IGBA4YFCAAYHHAAmsAomBqwABA4gICA4oIBC5YICGZRAGK5Cf/A/4H/A7YGFA4oA==")) +}; + +/* Set variables to get screen width, height and center points */ + +let W = g.getWidth(); +let H = g.getHeight(); +let cx = W/2; +let cy = H/2; +let Timeout; + +/* set font */ + +require("Font4x5Numeric").add(Graphics); + +Bangle.loadWidgets(); + +/* Custom version of Bangle.drawWidgets (does not clear the widget areas) Thanks to rozek */ + +Bangle.drawWidgets = function () { + var w = g.getWidth(), h = g.getHeight(); + + var pos = { + tl:{x:0, y:0, r:0, c:0}, // if r==1, we're right->left + tr:{x:w-1, y:0, r:1, c:0}, + bl:{x:0, y:h-24, r:0, c:0}, + br:{x:w-1, y:h-24, r:1, c:0} + }; + + if (global.WIDGETS) { + for (var wd of WIDGETS) { + var p = pos[wd.area]; + if (!p) continue; + + wd.x = p.x - p.r*wd.width; + wd.y = p.y; + + p.x += wd.width*(1-2*p.r); + p.c++; + } + + g.reset(); // also loads the current theme + + try { + for (var wd of WIDGETS) { + g.setClipRect(wd.x,wd.y, wd.x+wd.width-1,23); + wd.draw(wd); + } + } catch (e) { print(e); } + + g.reset(); // clears the clipping rectangle! + } + }; + +/* Draws the clock hands and date */ + +function drawHands() { + let d = new Date(); + + let hour = d.getHours() % 12; + let min = d.getMinutes(); + let sec = d.getSeconds(); + + let twoPi = 2*Math.PI; + let Pi = Math.PI; + let halfPi = Math.PI/2; + + let hourAngle = (hour+(min/60))/12 * twoPi - Pi; + let minAngle = (min/60) * twoPi - Pi; + let secAngle = (sec/60) * twoPi - Pi; + + let hourSin = Math.sin(hourAngle); + let hourCos = Math.cos(hourAngle); + let minSin = Math.sin(minAngle); + let minCos = Math.cos(minAngle); + let secSin = Math.sin(secAngle); + let secCos = Math.cos(secAngle); + + g.drawImage(imgHour,cx-22*hourSin,cy+22*hourCos,{rotate:hourAngle}); + g.drawImage(imgMin,cx-34*minSin,cy+34*minCos,{rotate:minAngle}); + g.drawImage(imgSec,cx-25*secSin,cy+25*secCos,{rotate:secAngle}); + g.setFont("4x5Numeric:3"); + g.drawString(d.getDate(),157,81); +} + +function drawBackground() { + g.setBgColor(0,0,0); + g.setColor(1,1,1); + g.clear(); + g.drawImage(imgBg,0,0); + g.reset(); +} + +/* Refresh the display every second */ + +function displayRefresh() { + g.clear(true); + drawBackground(); + drawHands(); + Bangle.drawWidgets(); + + let Pause = 1000 - (Date.now() % 1000); + Timeout = setTimeout(displayRefresh,Pause); +} +setTimeout(displayRefresh,500); + +Bangle.on('lcdPower', (on) => { + if (on) { + if (Timeout != null) { clearTimeout(Timeout); Timeout = undefined;} + displayRefresh(); + } +}); + +Bangle.loadWidgets(); +Bangle.setUI("clock"); \ No newline at end of file diff --git a/apps/Rolex/metadata.json b/apps/Rolex/metadata.json new file mode 100644 index 000000000..2c4437b35 --- /dev/null +++ b/apps/Rolex/metadata.json @@ -0,0 +1,17 @@ +{ "id": "rolex", + "name": "rolex", + "shortName":"rolex", + "icon": "rolex.png", + "screenshots": [{"url":"screenshot.png"}], + "version":"0.01", + "description": "A rolex like watch face", + "tags": "clock", + "type": "clock", + "supports":["BANGLEJS2"], + "readme": "README.md", + "allow_emulator": true, + "storage": [ + {"name":"rolex.app.js","url":"app.js"}, + {"name":"rolex.img","url":"app-icon.js","evaluate":true} + ] + } \ No newline at end of file diff --git a/apps/Rolex/rolex.png b/apps/Rolex/rolex.png new file mode 100644 index 0000000000000000000000000000000000000000..2e204376341d56b7fc5bf294e3da2e9eb23be6cc GIT binary patch literal 2406 zcmV-s37PhZP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2>VGyK~!i%#aU&D zRBaSK>#n&5b|-cxc7qr{>{bj6LD8;3lqyw9(xge_vxuHVynsxZGD)gb?zdsW8#Zi^bm`Jb z*|KHj)TvW4W5x{0moJ~sB6w_2R_~S2qi#5oaNNmkby%fS>p6 z+b6mpeol_0PoLiRNu*!De)9SAXWzHT#JaL@s_D@>o*Zq~tQk+kcFluY2R*CidZQ`O zG;P|H?$oknOEY=$WV%D!wry!stXMJArcE18v__2@CVTem^!>k>v|oc-Sty(qd(W8fGdKF!OOrJho+O=y(o4R%DN~1=NeTS=1$@YuB!P@!h+3`BDgILi6U$rD)Nj+>H+(K1jK8<>bzt zJA7#2!i8nmu3eIlkRYc|pO&XjpK>9O9zBwR1q({vym@8Mo;{K?XHI$f@+Gfz?AVd- z#Crmza^=dhZrwT=HENXP$dQ95hD}BC`1I)$ZTv2Ti^v4Uix-#U$B%Oi4_816;xHLA zW)wwYJ`fM-+_^I!iiP7O)W1pM-oAaytb%)Ci)&a67KrOutj2@XsZ)m=f%k%TID+S# zJb99?hfD_=g3lUPd`_Sw2@EJe3P9urF1m}RRjXD6stz4G71KJnqrn>S|S#ED649F5aIR)G&A_}l^w?r`ne zHM&*BiWRxb*j;Gp(xrKN*wy#%-^=jf!)5vMUko;~I9&=cBsrGtKp2fN%Hi-K5%B0Q??2%Vrx!t%E71V5yo$NwF_nxM2G9>0=FKubN^- zJ-|$91^pc95Pm*>{CFbv&!0azzG>4Y898z!fyzyAFK{QZAdSbM8uEP)5(Jrpc9Du! zWUPaMBS-=V4jdrzVo?ang9i_a@+MBKOqnvwHf}Ov#0cr#yEhX7f*eQ;eA%&Mhi-oe z0rAp%lcj?P59TwrZrw^^r#o&mI~m<=)1W~Eqm!`S7fP>Rzs|x)vl^d)3hvOMLwrZX z^*|^A4xp6+!Ud`U!H$DIfLDPM%$_})X;zCDUiT{CQ;sOW5@+x(NL*l15h#h>2l*aQ z4oKg*b0<3viom1+pca@I$Q(#lo;-P&|G_vx7eMB)DA+5i27#9s!E*s}5Og>IfV%no z?kR}-_U$WU$Bre5>ej8BOqnu;{s#t%Sg{GX41yRf7AJtgMzL7L1Qw}oAxDoMb#1pm zfa>bfB#ww3=Rc4?XaS(c2t4dACIA|MD~3Py>KNA-yUh%c4b#d=`<3zo7ZA1aX7OBObss5u9*UxMrl;$&n<-;wT{gZ~8u= z6vaGn2AF_40l1+tk)&M-H}=4ftb;6QG+mi-sG6`C|apm1YN7x^yWE5Xi-g7n%2O-@eW7$i?s8 zy<;^6Yyb!X88HU31=Q6NkhUOg=s*DcFjlW#J+@$ws&OB>AMx?=EPT+(Kull^#Xmq4 zxgL!MjNv{2sg|iMngW{){w(AK67oNC|EyWFMC;+8iVP3a0)%-Slel{IDg*NT`SV;f z2plrY(4j*~BhdfRdYC_mRIgrLwrttL$zZbpfKDKT+yQ?O&?{D~@HZ78C_xu2Sm5t5 zVQzSq1Aaki6hMQtHfho%S9AoR)v8sCI}b2=`0ybofD#iE$HK4?*xg&VZV^c_Sxg%B zG5%MnQiYTnHb80so15UI@hguKME-XqSvY{Ad&rO>+$j|3csX1M{09^PTm_%8AW&cI zx+VjrN$fBd47Y(xXer6f0MQF4pbuy=1bPJo-y%{Vx01M^vA_Vs=fLks0=8dtoXlej z&Ir^x=&ESuI+-Dvbu1NmUP+$OfMv7SX5&GvgCP|aob!7Y3UZr`S5?>Y|73+a*=FNG zt%D(T$&w{}A;b+}(18QmLfzWv{rmUlvovM0HE**Cqt-#I;+P%5LTomUg55e2uIq5+ z$`#t-rz_1!RvQ(^b07*qoM6N<$f@eB;cK`qY literal 0 HcmV?d00001 diff --git a/apps/Rolex/screenshot.png b/apps/Rolex/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..288c82cf4d68e39b11a7ba4e19af7fd6d0f04c01 GIT binary patch literal 3683 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 Date: Tue, 15 Feb 2022 17:07:00 +1300 Subject: [PATCH 26/60] Add Changelog --- apps/Rolex/Changelog | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/Rolex/Changelog diff --git a/apps/Rolex/Changelog b/apps/Rolex/Changelog new file mode 100644 index 000000000..a899bfcda --- /dev/null +++ b/apps/Rolex/Changelog @@ -0,0 +1 @@ +0.01 Initial Release From 9293eb9111ebc3cf1763065a3f1f723abcb10bfd Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 15 Feb 2022 09:17:28 +0000 Subject: [PATCH 27/60] misc rolex tweaks --- apps/{Rolex/Changelog => rolex/ChangeLog} | 0 apps/{Rolex => rolex}/README.md | 0 apps/{Rolex => rolex}/app-icon.js | 0 apps/{Rolex => rolex}/app.js | 0 apps/{Rolex => rolex}/metadata.json | 0 apps/{Rolex => rolex}/rolex.png | Bin apps/{Rolex => rolex}/screenshot.png | Bin 7 files changed, 0 insertions(+), 0 deletions(-) rename apps/{Rolex/Changelog => rolex/ChangeLog} (100%) rename apps/{Rolex => rolex}/README.md (100%) rename apps/{Rolex => rolex}/app-icon.js (100%) rename apps/{Rolex => rolex}/app.js (100%) rename apps/{Rolex => rolex}/metadata.json (100%) rename apps/{Rolex => rolex}/rolex.png (100%) rename apps/{Rolex => rolex}/screenshot.png (100%) diff --git a/apps/Rolex/Changelog b/apps/rolex/ChangeLog similarity index 100% rename from apps/Rolex/Changelog rename to apps/rolex/ChangeLog diff --git a/apps/Rolex/README.md b/apps/rolex/README.md similarity index 100% rename from apps/Rolex/README.md rename to apps/rolex/README.md diff --git a/apps/Rolex/app-icon.js b/apps/rolex/app-icon.js similarity index 100% rename from apps/Rolex/app-icon.js rename to apps/rolex/app-icon.js diff --git a/apps/Rolex/app.js b/apps/rolex/app.js similarity index 100% rename from apps/Rolex/app.js rename to apps/rolex/app.js diff --git a/apps/Rolex/metadata.json b/apps/rolex/metadata.json similarity index 100% rename from apps/Rolex/metadata.json rename to apps/rolex/metadata.json diff --git a/apps/Rolex/rolex.png b/apps/rolex/rolex.png similarity index 100% rename from apps/Rolex/rolex.png rename to apps/rolex/rolex.png diff --git a/apps/Rolex/screenshot.png b/apps/rolex/screenshot.png similarity index 100% rename from apps/Rolex/screenshot.png rename to apps/rolex/screenshot.png From a35d5afec08e4854723636b95d47198b7533e9e6 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 15 Feb 2022 09:20:07 +0000 Subject: [PATCH 28/60] oops - commit changed rolex files --- apps/rolex/ChangeLog | 3 ++- apps/rolex/app.js | 19 +++++++++++-------- apps/rolex/metadata.json | 32 ++++++++++++++++---------------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/apps/rolex/ChangeLog b/apps/rolex/ChangeLog index a899bfcda..283856aed 100644 --- a/apps/rolex/ChangeLog +++ b/apps/rolex/ChangeLog @@ -1 +1,2 @@ -0.01 Initial Release +0.01: Initial Release +0.02: Minor tweaks for light theme diff --git a/apps/rolex/app.js b/apps/rolex/app.js index adfe8a2c7..f8db71638 100644 --- a/apps/rolex/app.js +++ b/apps/rolex/app.js @@ -1,5 +1,3 @@ -/* Set background image */ - var imgBg = { width : 176, height : 176, bpp : 8, transparent : 254, @@ -113,9 +111,7 @@ function drawHands() { } function drawBackground() { - g.setBgColor(0,0,0); - g.setColor(1,1,1); - g.clear(); + g.clear(1); g.drawImage(imgBg,0,0); g.reset(); } @@ -131,7 +127,6 @@ function displayRefresh() { let Pause = 1000 - (Date.now() % 1000); Timeout = setTimeout(displayRefresh,Pause); } -setTimeout(displayRefresh,500); Bangle.on('lcdPower', (on) => { if (on) { @@ -140,5 +135,13 @@ Bangle.on('lcdPower', (on) => { } }); -Bangle.loadWidgets(); -Bangle.setUI("clock"); \ No newline at end of file +g.setTheme({ + bg : 0, fg : "#fff", dark:true, + bg2 : 0, fg2 : "#fff", + bgH : "#00f", fgH : "#fff", +}); +Bangle.setUI("clock"); +// load widgets after 'setUI' so they're aware there is a clock active +Bangle.loadWidgets(); +displayRefresh(); + diff --git a/apps/rolex/metadata.json b/apps/rolex/metadata.json index 2c4437b35..d7be25508 100644 --- a/apps/rolex/metadata.json +++ b/apps/rolex/metadata.json @@ -1,17 +1,17 @@ { "id": "rolex", - "name": "rolex", - "shortName":"rolex", - "icon": "rolex.png", - "screenshots": [{"url":"screenshot.png"}], - "version":"0.01", - "description": "A rolex like watch face", - "tags": "clock", - "type": "clock", - "supports":["BANGLEJS2"], - "readme": "README.md", - "allow_emulator": true, - "storage": [ - {"name":"rolex.app.js","url":"app.js"}, - {"name":"rolex.img","url":"app-icon.js","evaluate":true} - ] - } \ No newline at end of file + "name": "rolex", + "shortName":"rolex", + "icon": "rolex.png", + "screenshots": [{"url":"screenshot.png"}], + "version":"0.02", + "description": "A rolex like watch face", + "tags": "clock", + "type": "clock", + "supports":["BANGLEJS2"], + "readme": "README.md", + "allow_emulator": true, + "storage": [ + {"name":"rolex.app.js","url":"app.js"}, + {"name":"rolex.img","url":"app-icon.js","evaluate":true} + ] +} From fc58ef10894f7c3b219db0a777c0a785a7fbf279 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 15 Feb 2022 09:20:26 +0000 Subject: [PATCH 29/60] add updated core --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index 3093d78a5..8dcdaf141 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 3093d78a5d752cbf03ea8f9a1a7c0b50b9c8123b +Subproject commit 8dcdaf14153d9ecb42052a9de1317de293a5c495 From ef9f772a49b2d58035ec891845abfe60f8480435 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 15 Feb 2022 12:17:40 +0000 Subject: [PATCH 30/60] better search --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index 8dcdaf141..bd894bfdc 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 8dcdaf14153d9ecb42052a9de1317de293a5c495 +Subproject commit bd894bfdcee8293c97763de2b4d105a6b6e5415e From c1db20320550619d55a748271ae0f68a6af06b38 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 15 Feb 2022 15:07:11 +0000 Subject: [PATCH 31/60] settings 0.42: Fix theme customizer on new Bangle 2 firmware --- apps/setting/ChangeLog | 1 + apps/setting/metadata.json | 2 +- apps/setting/settings.js | 20 ++++++-------------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index 77c7b2040..39b4897b8 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -44,3 +44,4 @@ 0.39: Fix misbehaving debug info option 0.40: Moved off into Utils, put System after Apps 0.41: Stop users disabling all wake-up methods and locking themselves out (fix #1272) +0.42: Fix theme customizer on new Bangle 2 firmware diff --git a/apps/setting/metadata.json b/apps/setting/metadata.json index 1e82f97b4..4bb5ec129 100644 --- a/apps/setting/metadata.json +++ b/apps/setting/metadata.json @@ -1,7 +1,7 @@ { "id": "setting", "name": "Settings", - "version": "0.41", + "version": "0.42", "description": "A menu for setting up Bangle.js", "icon": "settings.png", "tags": "tool,system", diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 9bd63491f..09d95934f 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -243,12 +243,11 @@ function showThemeMenu() { }); function showCustomThemeMenu() { - function cv(x) { return g.setColor(x).getColor(); } function setT(t, v) { let th = g.theme; th[t] = v; if (t==="bg") { - th['dark'] = (v===cv("#000")); + th['dark'] = (v===cl("#000")); } upd(th); } @@ -260,11 +259,7 @@ function showThemeMenu() { let colors = [], names = []; for(const c in rgb) { names.push(c); - colors.push(cv(rgb[c])); - } - function cn(v) { - const i = colors.indexOf(v); - return i!== -1 ? names[i] : v; // another color: just show value + colors.push(cl(rgb[c])); } let menu = { '':{title:'Custom Theme'}, @@ -277,14 +272,11 @@ function showThemeMenu() { }; ["fg", "bg", "fg2", "bg2", "fgH", "bgH"].forEach(t => { menu[labels[t]] = { - value: colors.indexOf(g.theme[t]), - format: () => cn(g.theme[t]), + min : 0, max : colors.length-1, wrap : true, + value: Math.max(colors.indexOf(g.theme[t]),0), + format: v => names[v], onchange: function(v) { - // wrap around - if (v>=colors.length) {v = 0;} - if (v<0) {v = colors.length-1;} - this.value = v; - const c = colors[v]; + var c = colors[v]; // if we select the same fg and bg: set the other to the old color // e.g. bg=black;fg=white, user selects fg=black -> bg changes to white automatically // so users don't end up with a black-on-black menu From 029b7734e2a0e3aa245b5f3c626c951de8ea6292 Mon Sep 17 00:00:00 2001 From: Hilmar Strauch <56518493+HilmarSt@users.noreply.github.com> Date: Tue, 15 Feb 2022 17:43:09 +0100 Subject: [PATCH 32/60] Update README.md --- apps/speedalt/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/speedalt/README.md b/apps/speedalt/README.md index db3c7e673..c21828aff 100644 --- a/apps/speedalt/README.md +++ b/apps/speedalt/README.md @@ -1,6 +1,8 @@ # GPS Speed, Altimeter and Distance to Waypoint -You can switch between three display modes. One showing speed and altitude (A), one showing speed and distance to waypoint (D) and a large dispay of time and selected waypoint. +You can switch between three display modes. One showing speed and altitude (A), one showing speed and distance to waypoint (D) and a large dispay of time and selected waypoint. + +*Note for **Bangle.js 2:** Currently only the BTN3 functionality is working with the Bangle.js 2 button.* Within the [A]ltitude and [D]istance displays modes one figure is displayed on the watch face using the largest possible characters depending on the number of digits. The other is in a smaller characters below that. Both are always visible. You can display the current or maximum observed speed/altitude values. Current time is always displayed. @@ -10,6 +12,8 @@ The waypoints list is the same as that used with the [GPS Navigation](https://ba BTN3 : Cycles the modes between Speed+[A]ltitude, Speed+[D]istance and large Time/Waypoint +***Bangle.js 2:** Currently only this button function is working* + ### [A]ltitude mode BTN1 : Short press < 2 secs toggles the displays between showing the current speed/alt values or the maximum speed/alt values recorded. From 92e03e13e0477e526c7c709153564353257bd3b8 Mon Sep 17 00:00:00 2001 From: Hilmar Strauch <56518493+HilmarSt@users.noreply.github.com> Date: Tue, 15 Feb 2022 17:44:30 +0100 Subject: [PATCH 33/60] Update ChangeLog --- apps/speedalt/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/speedalt/ChangeLog b/apps/speedalt/ChangeLog index 09d33f615..0550f9b86 100644 --- a/apps/speedalt/ChangeLog +++ b/apps/speedalt/ChangeLog @@ -8,3 +8,4 @@ 0.08: New features. Added waypoints file and distance to selected waypoint display. Added integration with GPS Setup module to switch GPS to low power mode when screen off. Save display settings and restore when app restarted. 0.09: Add third screen mode with large clock and waypoint selection display to ease visibility in bright daylight. 0.10: Add Kalman filter to smooth the speed and altitude values. Can be disabled in settings. +0.11: Now also runs on Bangle.js 2 with basic functionality From 73c755f74ac8f3502cce1a6e760606b78b232ed1 Mon Sep 17 00:00:00 2001 From: Hilmar Strauch <56518493+HilmarSt@users.noreply.github.com> Date: Tue, 15 Feb 2022 17:54:06 +0100 Subject: [PATCH 34/60] Update metadata.json --- apps/speedalt/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/speedalt/metadata.json b/apps/speedalt/metadata.json index 458023278..4c0d26980 100644 --- a/apps/speedalt/metadata.json +++ b/apps/speedalt/metadata.json @@ -2,7 +2,7 @@ "id": "speedalt", "name": "GPS Adventure Sports", "shortName": "GPS Adv Sport", - "version": "0.10", + "version": "0.11", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "icon": "app.png", "type": "app", From 8c579f274cd773ade90e80569d3be892461d7a0b Mon Sep 17 00:00:00 2001 From: Hilmar Strauch <56518493+HilmarSt@users.noreply.github.com> Date: Tue, 15 Feb 2022 17:55:26 +0100 Subject: [PATCH 35/60] Update settings.js --- apps/speedalt/settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/speedalt/settings.js b/apps/speedalt/settings.js index 63d77971e..8906e2e2c 100644 --- a/apps/speedalt/settings.js +++ b/apps/speedalt/settings.js @@ -49,14 +49,14 @@ '': {'title': 'Units'}, '< Back': function() { E.showMenu(appMenu); }, 'default (spd)' : function() { setUnits(0,''); }, - 'Kph (spd)' : function() { setUnits(1,'kph'); }, + 'km/h (spd)' : function() { setUnits(1,'km/h'); }, 'Knots (spd)' : function() { setUnits(1.852,'kts'); }, 'Mph (spd)' : function() { setUnits(1.60934,'mph'); }, 'm/s (spd)' : function() { setUnits(3.6,'m/s'); }, 'Km (dist)' : function() { setUnitsDist(1000,'km'); }, 'Miles (dist)' : function() { setUnitsDist(1609.344,'mi'); }, 'Nm (dist)' : function() { setUnitsDist(1852.001,'nm'); }, - 'Meters (alt)' : function() { setUnitsAlt(1,'m'); }, + 'Meters (alt)' : function() { setUnitsAlt(1,'meter'); }, 'Feet (alt)' : function() { setUnitsAlt(0.3048,'ft'); } }; From b1f4548ee13a9ad7b6db1016e4a5ba9d65f9953d Mon Sep 17 00:00:00 2001 From: Hilmar Strauch <56518493+HilmarSt@users.noreply.github.com> Date: Tue, 15 Feb 2022 18:24:36 +0100 Subject: [PATCH 36/60] Update app.js --- apps/speedalt/app.js | 288 ++++++++++++++++++++++++++++--------------- 1 file changed, 188 insertions(+), 100 deletions(-) diff --git a/apps/speedalt/app.js b/apps/speedalt/app.js index c8735479d..5d97c4e3f 100644 --- a/apps/speedalt/app.js +++ b/apps/speedalt/app.js @@ -5,7 +5,16 @@ Mike Bennett mike[at]kereru.com 1.01 : Third mode large clock display 1.02 : add smoothing with kalman filter */ -var v = '1.02g'; +//var v = '1.02g'; + +const BANGLEJS2 = process.env.HWVERSION==2; +const screenH = g.getHeight(); +const screenH_Half = screenH / 2; +const screenH_Third = screenH / 3; +const screenH_TwoThirds = screenH * 2 / 3; +const screenW = g.getWidth(); +const screenW_Half = screenW / 2; +const fontFactorB2 = 2/3; /*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */ var KalmanFilter = (function () { @@ -171,10 +180,11 @@ var KalmanFilter = (function () { }()); -var buf = Graphics.createArrayBuffer(240,160,2,{msb:true}); -// Load fonts -require("Font7x11Numeric7Seg").add(Graphics); +var buf = Graphics.createArrayBuffer(screenW,screenH_TwoThirds,2,{msb:true}); + +if (!BANGLEJS2) +require("Font7x11Numeric7Seg").add(Graphics); // Load fonts var lf = {fix:0,satellites:0}; var showMax = 0; // 1 = display the max values. 0 = display the cur fix @@ -188,9 +198,10 @@ max.spd = 0; max.alt = 0; max.n = 0; // counter. Only start comparing for max after a certain number of fixes to allow kalman filter to have smoohed the data. -var emulator = (process.env.BOARD=="EMSCRIPTEN")?1:0; // 1 = running in emulator. Supplies test values; +var emulator = (process.env.BOARD=="EMSCRIPTEN" || process.env.BOARD=="EMSCRIPTEN2")?1:0; // 1 = running in emulator. Supplies test values; var wp = {}; // Waypoint to use for distance from cur position. +var SATinView = 0; function nxtWp(inc){ cfg.wp+=inc; @@ -212,7 +223,7 @@ function radians(a) { function distance(a,b){ var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2)); var y = radians(b.lat-a.lat); - + // Distance in selected units var d = Math.sqrt(x*x + y*y) * 6371000; d = (d/parseFloat(cfg.dist)).toFixed(2); @@ -228,41 +239,53 @@ function drawFix(dat) { buf.clear(); - var v = ''; + var v = ''; var u=''; - + // Primary Display v = (cfg.primSpd)?dat.speed.toString():dat.alt.toString(); - + // Primary Units u = (cfg.primSpd)?cfg.spd_unit:dat.alt_units; drawPrimary(v,u); - + // Secondary Display v = (cfg.primSpd)?dat.alt.toString():dat.speed.toString(); // Secondary Units u = (cfg.primSpd)?dat.alt_units:cfg.spd_unit; - + drawSecondary(v,u); - + // Time drawTime(); // Waypoint name drawWP(); - + //Sats if ( dat.age > 10 ) { if ( dat.age > 90 ) dat.age = '>90'; drawSats('Age:'+dat.age); } else drawSats('Sats:'+dat.sats); - + +/* else if (!BANGLEJS2) { + drawSats('Sats:'+dat.sats); + } else { + if (lf.fix) { + if(emulator)console.log("fix "+lf.fix); + drawSats('Sats:'+dat.sats); + } else { + if(emulator)console.log("inView: "+SATinView); + drawSats('View:' + SATinView); + } + } +*/ g.reset(); g.drawImage(img,0,40); - + } function drawClock() { @@ -275,125 +298,143 @@ function drawClock() { } function drawPrimary(n,u) { - + if(emulator)console.log("drawPrimary: " + n +" "+ u); // Primary Display - + var s=40; // Font size var l=n.length; - + if ( l <= 7 ) s=48; if ( l <= 6 ) s=55; if ( l <= 5 ) s=66; if ( l <= 4 ) s=85; if ( l <= 3 ) s=110; - - buf.setFontAlign(0,-1); //Centre - buf.setColor(1); + + buf.setFontAlign(0,-1); //Centre + buf.setColor(1); + if (BANGLEJS2) s *= fontFactorB2; buf.setFontVector(s); - buf.drawString(n,110,0); - - - - // Primary Units + buf.drawString(n,screenW_Half-10,0); + + // Primary Units + s = 35; // Font size buf.setFontAlign(1,-1,3); //right - buf.setColor(2); - buf.setFontVector(35); - buf.drawString(u,210,0); + buf.setColor(2); + if (BANGLEJS2) s = 20; + buf.setFontVector(s); + buf.drawString(u,screenW-30,0); } function drawSecondary(n,u) { - - var s=180; // units X position + if(emulator)console.log("drawSecondary: " + n +" "+ u); + var xu = 180; // units X position var l=n.length; - if ( l <= 5 ) s=155; - if ( l <= 4 ) s=125; - if ( l <= 3 ) s=100; - if ( l <= 2 ) s=65; - if ( l <= 1 ) s=35; - - buf.setFontAlign(-1,1); //left, bottom - buf.setColor(1); - buf.setFontVector(45); - buf.drawString(n,5,140); - + if ( l <= 5 ) xu = 155; + if ( l <= 4 ) xu = 125; + if ( l <= 3 ) xu = 100; + if ( l <= 2 ) xu = 65; + if ( l <= 1 ) xu = 35; + + buf.setFontAlign(-1,1); //left, bottom + buf.setColor(1); + var s = 45; // Font size + if (BANGLEJS2) s *= fontFactorB2; + buf.setFontVector(s); + buf.drawString(n,5,screenH_TwoThirds-20); + // Secondary Units buf.setFontAlign(-1,1); //left, bottom - buf.setColor(2); - buf.setFontVector(30); - buf.drawString(u,s,135); + + buf.setColor(2); + s = 30; // Font size + if (BANGLEJS2) s *= fontFactorB2; + buf.setFontVector(s); + buf.drawString(u,xu - (BANGLEJS2*20),screenH_TwoThirds-25); } function drawTime() { var x, y; - + if ( cfg.modeA == 2 ) { - x=120; - y=0; - buf.setFontAlign(0,-1); - buf.setFontVector(80); + x = screenW_Half; + y = 0; + buf.setFontAlign(0,-1); + buf.setFontVector(screenH_Third); } else { x = 0; - y = 160; + y = screenH_TwoThirds; buf.setFontAlign(-1,1); + if (!BANGLEJS2) buf.setFont("7x11Numeric7Seg", 2); + else + buf.setFont("6x8", 2); } buf.setColor(0); buf.drawString(time,x,y); time = require("locale").time(new Date(),1); - buf.setColor(3); + buf.setColor(3); buf.drawString(time,x,y); } -function drawWP() { +function drawWP() { // from waypoints.json - see README.md var nm = wp.name; if ( nm == undefined || nm == 'NONE' || cfg.modeA ==1 ) nm = ''; - buf.setColor(2); - + if (emulator) nm="waypoint"; + buf.setColor(2); + var s = 20; // Font size + if ( cfg.modeA == 0 ) { // dist mode + if(emulator)console.log("drawWP() 0: "+nm); buf.setFontAlign(-1,1); //left, bottom - buf.setFontVector(20); - buf.drawString(nm.substring(0,6),72,160); + if (BANGLEJS2) s *= fontFactorB2; + buf.setFontVector(s); + buf.drawString(nm.substring(0,6),72,screenH_TwoThirds); } if ( cfg.modeA == 2 ) { // clock/large mode + if(emulator)console.log("drawWP() 2: "+nm); + s = 55; // Font size buf.setFontAlign(0,1); //left, bottom - buf.setFontVector(55); - buf.drawString(nm.substring(0,6),120,160); + if (BANGLEJS2) s *= fontFactorB2; + buf.setFontVector(s); + buf.drawString(nm.substring(0,6),screenW_Half,screenH_TwoThirds); } - + } function drawSats(sats) { - buf.setColor(3); + buf.setColor(3); buf.setFont("6x8", 2); buf.setFontAlign(1,1); //right, bottom - buf.drawString(sats,240,160); + buf.drawString(sats,screenW,screenH_TwoThirds); + + s = 30; // Font size + if (BANGLEJS2) s = 18; + buf.setFontVector(s); + buf.setColor(2); - buf.setFontVector(30); - buf.setColor(2); - if ( cfg.modeA == 1 ) { - buf.drawString('A',240,140); + buf.drawString('A',screenW,140-(BANGLEJS2 * 40)); if ( showMax ) { buf.setFontAlign(0,1); //centre, bottom buf.drawString('MAX',120,164); } } - if ( cfg.modeA == 0 ) buf.drawString('D',240,140); + if ( cfg.modeA == 0 ) buf.drawString('D',screenW,140-(BANGLEJS2 * 40)); } function onGPS(fix) { - + if ( emulator ) { fix.fix = 1; fix.speed = 10 + (Math.random()*5); fix.alt = 354 + (Math.random()*50); fix.lat = -38.92; - fix.lon = 175.7613350; + fix.lon = 175.7613350; fix.course = 245; fix.satellites = 12; fix.time = new Date(); @@ -402,7 +443,7 @@ function onGPS(fix) { var m; - var sp = '---'; + var sp = '---'; var al = '---'; var di = '---'; var age = '---'; @@ -411,6 +452,8 @@ function onGPS(fix) { if (lf.fix) { +// if (BANGLEJS2 && !emulator) Bangle.removeListener('GPS-raw', onGPSraw); + // Smooth data if ( lf.smoothed !== 1 ) { if ( cfg.spdFilt ) lf.speed = spdFilter.filter(lf.speed); @@ -418,8 +461,8 @@ function onGPS(fix) { lf.smoothed = 1; if ( max.n <= 15 ) max.n++; } - - + + // Speed if ( cfg.spd == 0 ) { m = require("locale").speed(lf.speed).match(/([0-9,\.]+)(.*)/); // regex splits numbers from units @@ -427,7 +470,7 @@ function onGPS(fix) { cfg.spd_unit = m[2]; } else sp = parseFloat(lf.speed)/parseFloat(cfg.spd); // Calculate for selected units - + if ( sp < 10 ) sp = sp.toFixed(1); else sp = Math.round(sp); if (parseFloat(sp) > parseFloat(max.spd) && max.n > 15 ) max.spd = parseFloat(sp); @@ -444,9 +487,9 @@ function onGPS(fix) { // Age of last fix (secs) age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000)); } - + if ( cfg.modeA == 1 ) { - if ( showMax ) + if ( showMax ) drawFix({ speed:max.spd, sats:lf.satellites, @@ -455,7 +498,7 @@ function onGPS(fix) { age:age, fix:lf.fix }); // Speed and alt maximums - else + else drawFix({ speed:sp, sats:lf.satellites, @@ -467,7 +510,7 @@ function onGPS(fix) { } if ( cfg.modeA == 0 ) { // Show speed/distance - if ( di <= 0 ) + if ( di <= 0 ) drawFix({ speed:sp, sats:lf.satellites, @@ -476,7 +519,7 @@ function onGPS(fix) { age:age, fix:lf.fix }); // No WP selected - else + else drawFix({ speed:sp, sats:lf.satellites, @@ -494,7 +537,7 @@ function onGPS(fix) { } function setButtons(){ - +if (!BANGLEJS2) { // Buttons for Bangle.js // Spd+Dist : Select next waypoint setWatch(function(e) { var dur = e.time - e.lastTime; @@ -506,10 +549,10 @@ function setButtons(){ else nxtWp(1); // Spd+Dist or Clock mode - Select next waypoint onGPS(lf); }, BTN1, { edge:"falling",repeat:true}); - - // Power saving on/off + + // Power saving on/off setWatch(function(e){ - pwrSav=!pwrSav; + pwrSav=!pwrSav; if ( pwrSav ) { LED1.reset(); var s = require('Storage').readJSON('setting.json',1)||{}; @@ -522,15 +565,15 @@ function setButtons(){ LED1.set(); } }, BTN2, {repeat:true,edge:"falling"}); - + // Toggle between alt or dist setWatch(function(e){ cfg.modeA = cfg.modeA+1; if ( cfg.modeA > 2 ) cfg.modeA = 0; savSettings(); - onGPS(lf); + onGPS(lf); }, BTN3, {repeat:true,edge:"falling"}); - + // Touch left screen to toggle display setWatch(function(e){ cfg.primSpd = !cfg.primSpd; @@ -538,11 +581,42 @@ function setButtons(){ onGPS(lf); // Update display }, BTN4, {repeat:true,edge:"falling"}); +} else { // Buttons for Bangle.js 2 + setWatch(function(e){ // Bangle.js BTN3 + cfg.modeA = cfg.modeA+1; + if ( cfg.modeA > 2 ) cfg.modeA = 0; + if(emulator)console.log("cfg.modeA="+cfg.modeA); + savSettings(); + onGPS(lf); + }, BTN1, {repeat:true,edge:"falling"}); + +/* Bangle.on('tap', function(data) { // data - {dir, double, x, y, z} + cfg.primSpd = !cfg.primSpd; + if(emulator)console.log("!cfg.primSpd"); + }); */ + +/* Bangle.on('swipe', function(dir) { + if (dir < 0) { // left: Bangle.js BTN3 + cfg.modeA = cfg.modeA+1; + if ( cfg.modeA > 2 ) cfg.modeA = 0; + if(emulator)console.log("cfg.modeA="+cfg.modeA); + } + else + { // right: Bangle.js BTN4 + cfg.primSpd = !cfg.primSpd; + if(emulator)console.log("!cfg.primSpd"); + } + }); +*/ + savSettings(); + onGPS(lf); + } } + function updateClock() { if (!canDraw) return; - drawTime(); + drawTime(); g.reset(); g.drawImage(img,0,40); if ( emulator ) {max.spd++;max.alt++;} @@ -573,21 +647,21 @@ function setLpMode(m) { // =Main Prog -// Read settings. +// Read settings. let cfg = require('Storage').readJSON('speedalt.json',1)||{}; cfg.spd = cfg.spd||0; // Multiplier for speed unit conversions. 0 = use the locale values for speed -cfg.spd_unit = cfg.spd_unit||''; // Displayed speed unit -cfg.alt = cfg.alt||0.3048;// Multiplier for altitude unit conversions. -cfg.alt_unit = cfg.alt_unit||'feet'; // Displayed altitude units +cfg.spd_unit = cfg.spd_unit||'km/h'; // Displayed speed unit +cfg.alt = cfg.alt||1;// Multiplier for altitude unit conversions. (feet:'0.3048') +cfg.alt_unit = cfg.alt_unit||'meter'; // Displayed altitude units ('feet') cfg.dist = cfg.dist||1000;// Multiplier for distnce unit conversions. cfg.dist_unit = cfg.dist_unit||'km'; // Displayed altitude units cfg.colour = cfg.colour||0; // Colour scheme. cfg.wp = cfg.wp||0; // Last selected waypoint for dist -cfg.modeA = cfg.modeA||0; // 0 = [D]ist, 1 = [A]ltitude, 2 = [C]lock -cfg.primSpd = cfg.primSpd||0; // 1 = Spd in primary, 0 = Spd in secondary +cfg.modeA = cfg.modeA||1; // 0 = [D]ist, 1 = [A]ltitude, 2 = [C]lock +cfg.primSpd = cfg.primSpd||1; // 1 = Spd in primary, 0 = Spd in secondary -cfg.spdFilt = cfg.spdFilt==undefined?true:cfg.spdFilt; +cfg.spdFilt = cfg.spdFilt==undefined?true:cfg.spdFilt; cfg.altFilt = cfg.altFilt==undefined?true:cfg.altFilt; if ( cfg.spdFilt ) var spdFilter = new KalmanFilter({R: 0.1 , Q: 1 }); @@ -602,29 +676,43 @@ Colour Pallet Idx 2 : Units 3 : Sats */ +const background = 0; // g.theme.bg = 0xFFFF = gelb!? var img = { width:buf.getWidth(), height:buf.getHeight(), bpp:2, buffer:buf.buffer, - palette:new Uint16Array([0,0x4FE0,0xEFE0,0x07DB]) + palette:new Uint16Array([background,0x4FE0,0xEFE0,0x07DB]) // "Default" }; -if ( cfg.colour == 1 ) img.palette = new Uint16Array([0,0xFFFF,0xFFF6,0xDFFF]); -if ( cfg.colour == 2 ) img.palette = new Uint16Array([0,0xFF800,0xFAE0,0xF813]); +if ( cfg.colour == 1 ) img.palette = new Uint16Array([background,0xFFFF,0xFFF6,0xDFFF]); // "Hi contrast" +if ( cfg.colour == 2 ) img.palette = new Uint16Array([background,0xFF800,0xFAE0,0xF813]); // "Night" var SCREENACCESS = { withApp:true, request:function(){this.withApp=false;stopDraw();}, release:function(){this.withApp=true;startDraw();} -}; +}; Bangle.on('lcdPower',function(on) { if (!SCREENACCESS.withApp) return; - if (on) startDraw(); + if (on) startDraw(); else stopDraw(); }); +/* +function onGPSraw(nmea) { + var nofGP = 0, nofBD = 0, nofGL = 0; + if (nmea.slice(3,6) == "GSV") { + // console.log(nmea.slice(1,3) + " " + nmea.slice(11,13)); + if (nmea.slice(0,7) == "$GPGSV,") nofGP = Number(nmea.slice(11,13)); + if (nmea.slice(0,7) == "$BDGSV,") nofBD = Number(nmea.slice(11,13)); + if (nmea.slice(0,7) == "$GLGSV,") nofGL = Number(nmea.slice(11,13)); + SATinView = nofGP + nofBD + nofGL; + } } +if(BANGLEJS2) Bangle.on('GPS-raw', onGPSraw); +*/ + var gpssetup; try { gpssetup = require("gpssetup"); @@ -634,8 +722,6 @@ try { // All set up. Lets go. g.clear(); -Bangle.loadWidgets(); -Bangle.drawWidgets(); onGPS(lf); Bangle.setGPSPower(1); @@ -650,3 +736,5 @@ Bangle.on('GPS', onGPS); setButtons(); setInterval(updateClock, 10000); +Bangle.loadWidgets(); +Bangle.drawWidgets(); From e9c29e0a01a9826d9723044a9adcd2d7536e9b55 Mon Sep 17 00:00:00 2001 From: Hilmar Strauch <56518493+HilmarSt@users.noreply.github.com> Date: Tue, 15 Feb 2022 18:26:05 +0100 Subject: [PATCH 37/60] Update metadata.json --- apps/speedalt/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/speedalt/metadata.json b/apps/speedalt/metadata.json index 4c0d26980..617ac4b8e 100644 --- a/apps/speedalt/metadata.json +++ b/apps/speedalt/metadata.json @@ -7,7 +7,7 @@ "icon": "app.png", "type": "app", "tags": "tool,outdoors", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "allow_emulator": true, "storage": [ From 67ee66193bc1d9ee5fff71b5b56f66dde6720250 Mon Sep 17 00:00:00 2001 From: Hilmar Strauch <56518493+HilmarSt@users.noreply.github.com> Date: Tue, 15 Feb 2022 18:36:30 +0100 Subject: [PATCH 38/60] Update app.js --- apps/speedalt/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/speedalt/app.js b/apps/speedalt/app.js index 5d97c4e3f..ca543bef9 100644 --- a/apps/speedalt/app.js +++ b/apps/speedalt/app.js @@ -391,7 +391,7 @@ function drawWP() { // from waypoints.json - see README.md buf.setFontAlign(-1,1); //left, bottom if (BANGLEJS2) s *= fontFactorB2; buf.setFontVector(s); - buf.drawString(nm.substring(0,6),72,screenH_TwoThirds); + buf.drawString(nm.substring(0,6),72,screenH_TwoThirds-(BANGLEJS2 * 20)); } if ( cfg.modeA == 2 ) { // clock/large mode From 3316897c34fc2f22584c960fbea1f9929b216859 Mon Sep 17 00:00:00 2001 From: Hilmar Strauch <56518493+HilmarSt@users.noreply.github.com> Date: Tue, 15 Feb 2022 18:43:03 +0100 Subject: [PATCH 39/60] Update app.js --- apps/speedalt/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/speedalt/app.js b/apps/speedalt/app.js index ca543bef9..f979762f1 100644 --- a/apps/speedalt/app.js +++ b/apps/speedalt/app.js @@ -400,7 +400,7 @@ function drawWP() { // from waypoints.json - see README.md buf.setFontAlign(0,1); //left, bottom if (BANGLEJS2) s *= fontFactorB2; buf.setFontVector(s); - buf.drawString(nm.substring(0,6),screenW_Half,screenH_TwoThirds); + buf.drawString(nm.substring(0,6),screenW_Half,screenH_TwoThirds-(BANGLEJS2 * 20)); } } From 8c4f2f18c942a7a454e3ea872d9af50b5e51789e Mon Sep 17 00:00:00 2001 From: storm64 Date: Tue, 15 Feb 2022 19:47:15 +0100 Subject: [PATCH 40/60] sleeplog: Correct fix #1445, display loading info while calculating sleep data Update app.js - move complex data analysis into separate function - add displaying a loading info - faster feedback by calling the analysis through a timeout Update boot.js - correct fix #1445 as mentioned by splitting the `stop()` function to only remove the kill listener on a manual stop - optimize `start()` and `stop()` functions Update README.md - change beautified menu entries according to changes in settings.js Update settings.js - replace `circulate()` function with internal `wrap: true` - move delay for menu redraw as suggested in espruino/Espruino#2149 - beautify menu entries for new touchscreen E.showMenu system (> fw 2v.13.32) --- apps/sleeplog/ChangeLog | 5 ++- apps/sleeplog/README.md | 16 +++---- apps/sleeplog/app.js | 74 +++++++++++++++++++++---------- apps/sleeplog/boot.js | 41 ++++++++++------- apps/sleeplog/metadata.json | 2 +- apps/sleeplog/settings.js | 88 +++++++++++++++++-------------------- 6 files changed, 129 insertions(+), 97 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index b82555903..e37283f09 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! -0.02: Fix crash on start -0.03: Added power saving mode, move all read/write log actions into lib/module, fix #1445 +0.02: Fix crash on start #1423 +0.03: Added power saving mode, move all read/write log actions into lib/module +0.04: Fix #1445, display loading info while calculating sleep data diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 32a14839a..b120befb0 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -34,31 +34,31 @@ also provides a power saving mode using the built in movement calculation. The i --- ### Settings --- -* __BreakTod__ | break at time of day +* __Break Tod__ | break at time of day _0_ / _1_ / _..._ / __10__ / _..._ / _12_ Change time of day on wich the lower graph starts and the upper graph ends. -* __MaxAwake__ | maximal awake duration +* __Max Awake__ | maximal awake duration _15min_ / _20min_ / _..._ / __60min__ / _..._ / _120min_ Adjust the maximal awake duration upon the exceeding of which aborts the consecutive sleep period. -* __MinConsec__ | minimal consecutive sleep duration +* __Min Consec__ | minimal consecutive sleep duration _15min_ / _20min_ / _..._ / __30min__ / _..._ / _120min_ Adjust the minimal consecutive sleep duration that will be considered for the consecutive sleep value. -* __TempThresh__ | temperature threshold +* __Temp Thresh__ | temperature threshold _20°C_ / _20.5°C_ / _..._ / __25°C__ / _..._ / _40°C_ The internal temperature must be greater than this threshold to log _sleeping_, otherwise it is _not worn_. -* __PowerSaving__ +* __Power Saving__ _on_ / __off__ En-/Disable power saving mode. _Saves battery, but might decrease accurracy._ -* __MaxMove__ | maximal movement threshold +* __Max Move__ | maximal movement threshold (only available when on power saving mode) _50_ / _51_ / _..._ / __100__ / _..._ / _200_ On power saving mode the watch is considered resting if this threshold is lower or equal to the movement value of bangle's health event. -* __NoMoThresh__ | no movement threshold +* __NoMo Thresh__ | no movement threshold (only available when not on power saving mode) _0.006_ / _0.007_ / _..._ / __0.012__ / _..._ / _0.020_ The standard deviation over the measured values needs to be lower then this threshold to count as not moving. The defaut threshold value worked best for my watch. A threshold value below 0.008 may get triggert by noise. -* __MinDuration__ | minimal no movement duration +* __Min Duration__ | minimal no movement duration (only available when not on power saving mode) _5min_ / _6min_ / _..._ / __10min__ / _..._ / _15min_ If no movement is detected for this duration, the watch is considered as resting. diff --git a/apps/sleeplog/app.js b/apps/sleeplog/app.js index c89b37267..cf4ecc415 100644 --- a/apps/sleeplog/app.js +++ b/apps/sleeplog/app.js @@ -127,34 +127,24 @@ function drawLog(topY, viewUntil) { return output.map(value => value /= 6E4); } -// define draw night to function -function drawNightTo(prevDays) { - // calculate 10am of this or a previous day - var date = Date(); - date = Date(date.getFullYear(), date.getMonth(), date.getDate() - prevDays, breaktod); +// define function to draw the analysis +function drawAnalysis(toDate) { + //var t0 = Date.now(); // get width var width = g.getWidth(); - // clear app area - g.clearRect(0, 24, width, width); - // define variable for sleep calculation var outputs = [0, 0]; // [estimated, true] - // draw log graphs and read outputs - drawLog(110, date).forEach( - (value, index) => outputs[index] += value); - drawLog(145, Date(date.valueOf() - 432E5)).forEach( - (value, index) => outputs[index] += value); - // reduce date by 1s to ensure correct headline - date = Date(date.valueOf() - 1E3); - // draw headline, on red bg if service or loggging disabled or green bg if powersaving enabled - g.setColor(global.sleeplog && sleeplog.enabled && sleeplog.logfile ? sleeplog.powersaving ? 2016 : g.theme.bg : 63488); - g.fillRect(0, 30, width, 66).reset(); - g.setFont("12x20").setFontAlign(0, -1); - g.drawString("Night to " + require('locale').dow(date, 1) + "\n" + - require('locale').date(date, 1), width / 2, 30); + // clear analysis area + g.clearRect(0, 71, width, width); + + // draw log graphs and read outputs + drawLog(110, toDate).forEach( + (value, index) => outputs[index] += value); + drawLog(145, Date(toDate.valueOf() - 432E5)).forEach( + (value, index) => outputs[index] += value); // draw outputs g.reset(); // area: 0, 70, width, 105 @@ -166,8 +156,47 @@ function drawNightTo(prevDays) { Math.floor(outputs[0] % 60) + "min", width - 10, 70); g.drawString(Math.floor(outputs[1] / 60) + "h " + Math.floor(outputs[1] % 60) + "min", width - 10, 90); + + //print("analysis processing seconds:", Math.round(Date.now() - t0) / 1000); } +// define draw night to function +function drawNightTo(prevDays) { + // calculate 10am of this or a previous day + var toDate = Date(); + toDate = Date(toDate.getFullYear(), toDate.getMonth(), toDate.getDate() - prevDays, breaktod); + + // get width + var width = g.getWidth(); + var center = width / 2; + + // reduce date by 1s to ensure correct headline + toDate = Date(toDate.valueOf() - 1E3); + + // clear heading area + g.clearRect(0, 24, width, 70); + + // display service statuses: service, loggging and powersaving + g.setColor(global.sleeplog && sleeplog.enabled && sleeplog.logfile ? sleeplog.powersaving ? 2016 : g.theme.bg : 63488); + g.fillRect(0, 30, width, 66).reset(); + + // draw headline + g.setFont("12x20").setFontAlign(0, -1); + g.drawString("Night to " + require('locale').dow(toDate, 1) + "\n" + + require('locale').date(toDate, 1), center, 30); + + // show loading info + var info = "calculating data ...\nplease be patient :)"; + var y0 = center + 30; + var bounds = [center - 80, y0 - 20, center + 80, y0 + 20]; + g.clearRect.apply(g, bounds).drawRect.apply(g, bounds); + g.setFont("6x8").setFontAlign(0, 0); + g.drawString(info, center, y0); + + // calculate and draw analysis after timeout for faster feedback + if (ATID) ATID = clearTimeout(ATID); + ATID = setTimeout(drawAnalysis, 50, toDate); +} // define function to draw and setup UI function startApp() { @@ -182,8 +211,9 @@ function startApp() { }); } -// define day to display +// define day to display and analysis timeout id var prevDays = 0; +var ATID; // setup app g.clear(); diff --git a/apps/sleeplog/boot.js b/apps/sleeplog/boot.js index b7df51c2d..f989e2835 100644 --- a/apps/sleeplog/boot.js +++ b/apps/sleeplog/boot.js @@ -28,36 +28,43 @@ if (sleeplog.enabled) { resting: undefined, status: undefined, - // define stop function (logging will restart if enabled and boot file is executed) - stop: function() { + // define function to handle stopping the service, it will be restarted on reload if enabled + stopHandler: function() { // remove all listeners Bangle.removeListener('accel', sleeplog.accel); Bangle.removeListener('health', sleeplog.health); - E.removeListener('kill', () => sleeplog.stop()); - // exit on missing global object - if (!global.sleeplog) return; // write log with undefined sleeping status require("sleeplog").writeLog(0, [Math.floor(Date.now()), 0]); - // reset always used cached values - sleeplog.resting = undefined; - sleeplog.status = undefined; - sleeplog.ess_values = []; - sleeplog.nomocount = 0; - sleeplog.firstnomodate = undefined; + // reset cached values if sleeplog is defined + if (global.sleeplog) { + sleeplog.resting = undefined; + sleeplog.status = undefined; + // reset cached ESS calculation values + if (!sleeplog.powersaving) { + sleeplog.ess_values = []; + sleeplog.nomocount = 0; + sleeplog.firstnomodate = undefined; + } + } }, - // define restart function (also use for initial starting) + // define function to remove the kill listener and stop the service + // https://github.com/espruino/BangleApps/issues/1445 + stop: function() { + E.removeListener('kill', sleeplog.stopHandler); + sleeplog.stopHandler(); + }, + + // define function to initialy start or restart the service start: function() { - // exit on missing global object - if (!global.sleeplog) return; + // add kill listener + E.on('kill', sleeplog.stopHandler); // add health listener if defined and if (sleeplog.health) Bangle.on('health', sleeplog.health); // add acceleration listener if defined and set status to unknown if (sleeplog.accel) Bangle.on('accel', sleeplog.accel); - // add kill listener - E.on('kill', () => sleeplog.stop()); // read log since 5min ago and restore status to last known state or unknown - sleeplog.status = (require("sleeplog").readLog(0, Date.now() - 3E5)[1] || [0, 0])[1] + sleeplog.status = (require("sleeplog").readLog(0, Date.now() - 3E5)[1] || [0, 0])[1]; // update resting according to status sleeplog.resting = sleeplog.status % 2; // write restored status to log diff --git a/apps/sleeplog/metadata.json b/apps/sleeplog/metadata.json index 1d098cc72..8cf6979d6 100644 --- a/apps/sleeplog/metadata.json +++ b/apps/sleeplog/metadata.json @@ -2,7 +2,7 @@ "id":"sleeplog", "name":"Sleep Log", "shortName": "SleepLog", - "version": "0.03", + "version": "0.04", "description": "Log and view your sleeping habits. This app derived from SleepPhaseAlarm and uses also the principe of Estimation of Stationary Sleep-segments (ESS). It also provides a power saving mode using the built in movement calculation.", "icon": "app.png", "type": "app", diff --git a/apps/sleeplog/settings.js b/apps/sleeplog/settings.js index 24bd82f61..ffc014337 100644 --- a/apps/sleeplog/settings.js +++ b/apps/sleeplog/settings.js @@ -29,11 +29,6 @@ storage.writeJSON(filename, settings); } - // define circulate function - function circulate(min, max, value) { - return value > max ? min : value < min ? max : value; - } - // define function to change values that need a restart of the service function changeRestart() { require("sleeplog").setEnabled(settings.enabled, settings.logfile, settings.powersaving); @@ -49,77 +44,79 @@ title: "Sleep Log", selected: selected }, - "< Exit": () => load(), + "Exit": () => load(), "< Back": () => back(), - "BreakTod": { + "Break Tod": { value: settings.breaktod, step: 1, - onchange: function(v) { - this.value = v = circulate(0, 23, v); - writeSetting("breaktod", v); - } + min: 0, + max: 23, + wrap: true, + onchange: v => writeSetting("breaktod", v), }, - "MaxAwake": { + "Max Awake": { value: settings.maxawake / 6E4, step: 5, + min: 15, + max: 120, + wrap: true, format: v => v + "min", - onchange: function(v) { - this.value = v = circulate(15, 120, v); - writeSetting("maxawake", v * 6E4); - } + onchange: v => writeSetting("maxawake", v * 6E4), }, - "MinConsec": { + "Min Consec": { value: settings.minconsec / 6E4, step: 5, + min: 15, + max: 120, + wrap: true, format: v => v + "min", - onchange: function(v) { - this.value = v = circulate(15, 120, v); - writeSetting("minconsec", v * 6E4); - } + onchange: v => writeSetting("minconsec", v * 6E4), }, - "TempThresh": { + "Temp Thresh": { value: settings.tempthresh, step: 0.5, + min: 20, + max: 40, + wrap: true, format: v => v + "°C", - onchange: function(v) { - this.value = v = circulate(20, 40, v); - writeSetting("tempthresh", v); - } + onchange: v => writeSetting("tempthresh", v), }, - "PowerSaving": { + "Power Saving": { value: settings.powersaving, format: v => v ? "on" : "off", onchange: function(v) { settings.powersaving = v; changeRestart(); - showMain(7); + // redraw menu with changed entries subsequent to onchange + // https://github.com/espruino/Espruino/issues/2149 + setTimeout(showMain, 1, 6); } }, - "MaxMove": { + "Max Move": { value: settings.maxmove, step: 1, - onchange: function(v) { - this.value = v = circulate(50, 200, v); - writeSetting("maxmove", v); - } + min: 50, + max: 200, + wrap: true, + onchange: v => writeSetting("maxmove", v), }, - "NoMoThresh": { + "NoMo Thresh": { value: settings.nomothresh, step: 0.001, + min: 0.006, + max: 0.02, + wrap: true, format: v => ("" + v).padEnd(5, "0"), - onchange: function(v) { - this.value = v = circulate(0.006, 0.02, v); - writeSetting("nomothresh", v); - } + onchange: v => writeSetting("nomothresh", v), }, - "MinDuration": { + "Min Duration": { value: Math.floor(settings.sleepthresh * stFactor), step: 1, + min: 5, + max: 15, + wrap: true, format: v => v + "min", - onchange: function(v) { - this.value = v = circulate(5, 15, v); - writeSetting("sleepthresh", Math.ceil(v / stFactor)); - } + onchange: v => writeSetting("sleepthresh", Math.ceil(v / stFactor)), }, "Enabled": { value: settings.enabled, @@ -141,11 +138,8 @@ } }; // check power saving mode to delete unused entries - (settings.powersaving ? ["NoMoThresh", "MinDuration"] : ["MaxMove"]).forEach(property => delete mainMenu[property]); + (settings.powersaving ? ["NoMo Thresh", "Min Duration"] : ["Max Move"]).forEach(property => delete mainMenu[property]); var menu = E.showMenu(mainMenu); - // workaround to display changed entries correct - // https://github.com/espruino/Espruino/issues/2149 - if (selected) setTimeout(m => m.draw(), 1, menu); } // draw main menu From 8ea5f8e1de4297cf13695ef990d275ca0da1d597 Mon Sep 17 00:00:00 2001 From: Daniel Cox Date: Wed, 16 Feb 2022 08:46:48 +1300 Subject: [PATCH 41/60] Fixed theme honoring and made images 2 bit This update should fix theme honoring so the watch face now changes to match the theme set on the watch. The images have also been updated to be better compatible with 2 bit and also now set to 2 bit to save ram. Also fixed the date text as that stopped displaying in the last update. --- apps/rolex/Changelog | 3 +++ apps/rolex/app.js | 35 ++++++++++++++++------------------- apps/rolex/metadata.json | 32 ++++++++++++++++---------------- 3 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 apps/rolex/Changelog diff --git a/apps/rolex/Changelog b/apps/rolex/Changelog new file mode 100644 index 000000000..3bfed4a4e --- /dev/null +++ b/apps/rolex/Changelog @@ -0,0 +1,3 @@ +0.01: Initial Release +0.02: Minor tweaks for light theme +0.03: Made images 2 bit and fixed theme honoring \ No newline at end of file diff --git a/apps/rolex/app.js b/apps/rolex/app.js index f8db71638..e409704fc 100644 --- a/apps/rolex/app.js +++ b/apps/rolex/app.js @@ -1,31 +1,31 @@ var imgBg = { - width : 176, height : 176, bpp : 8, - transparent : 254, - buffer : require("heatshrink").decompress(atob("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")) + width : 176, height : 176, bpp : 1, + transparent : 0, + buffer : require("heatshrink").decompress(atob("ABMBwAVpmfMCqdxxwVpmOMCiUDmOIoAVRg8xzHwCqUZCqcDCoPAYBwDDzOZYx0DB4QVGF5UPComRAoZbKn4hD7uzGof4CpN/8AVI/wVO5+3T4YVKn5NDt/2Cqd/CAcP/gVJj5jDCogJECpc/EwYVLG4gVEJYgAGMYgVVLgqjDAA0D/5GDRAgVPg4VD/77DAA0B/+ABBwAEEQ4VVJQgAIMg4VVUQgAIn4VUj5kGgbfDABEeBA7fDABEMBA84CpYAIiAVUAHVAh4USgf4j+HVgPg4AVOxlw44DBuLMGcgQVFg1gs0DgE7JowVCGognB4AVB0DiFgPgAYMPAYRXD8AVB+EOHooVDfQkDjBBDj4VEh/wAYMf/wVEhkw40Tg0zNok/Cof/BQcDxyZB+f8sJjE/49Cn//Uh0B//8FYYwCwEgAYMQVQ4VDh4ECgfSufOvF548c8+CuQmGgf//4eBv3BxFMtuN23DmVzCAN//6rDCoPAg1suEMsAVBmgVBrYPDPwZuCCoMZhhBBx84CoPyNgSqETQUDpnZhFIsODmGDiX7KIRsCOYeAgOAuDsBgKhB8UMwPAFYJsCT4avNX41/Cpz2EgEeAYU+jl8vwHBnl8BQV+DgpSBAAMjmkQoEDsVmnAKCvA2JkcmCocweocwCpMhm0QvECsccCoYAKkd+FYNCscGCp0ysQVBs0iAgIVNn1DPYM8AAIVOhACBnkYhEYChoA/AH4A/AH4A/AC1///+CtH/AAIVVAAIuQCqkBCv4V/CozERCqrxZCtF/QCAA/AH4A/AH4A/AH4ArhgONkAGFnwVNuAGFvwVN/gFEgP/CpoOFg4VNEgOAAwcfAwoVJ8AGDn//4AVLgf//BHEFZoVB/wFIIJZnDh//RQMYhGICxN/KIZWB+EAmEfzsN4Ew8PMw3JnYMBPoIDBNgk6u8XjmA8OT2XJ/8zvg8ECoRsBh1/jscwdhj/ztO7iV4NAQVCj5sCh1Tzseydhy/jnO3icwSgSaCh4DCnOBwviwcw+Msm0BidINwRXCh4DCABsfCIUHbJgADg7yCg4IDhlmjEgAgPssOGsDHDCoQAEiXMs9yt8R5VrzmwBoY9HCoNjuVGiHOpOcyBKLiWMsd6s0R51LjgVMjkIjEPukM5UIjiQMhkAjEDmEMMoMGNoYA0jAVUjgIHuAVLn4IH/AVLv7OGgf8Cpj6Gg4VM/4rGg/+CsEB/+AK43/CpQMIDwIVVGgxONMA4VIj/wCp0PUwYVEXA4VZj7+DCok/AgYAGn4VID4gVHCAgVPJogVEMIgVRXJClGCojPJFYQVIgYVKj79DCoptKh4VIgKvKgYwECohLDABYVEACAV/Cv4V7")) }; /* Set hour hand image */ var imgHour = { - width : 19, height : 62, bpp : 8, - transparent : 254, - buffer : require("heatshrink").decompress(atob("/wAF64AEBgwQJChYRJCY4RLCYoRNCQYROCYYS/CWKXSXqbjTCZYRHCZIRJCY4RLCQVWqwSQwOBCX4S/CXb1VCRYRGChIJC1hJDAYYTFA4QMBCQuB1gTFCIoSFCYokGCQyJEBQwSHA4ISSCYIKFqwSRSgiKCCQwhBM4QAFEpAMEDA1WCQgyHHwy9JChASRchYkJCYpNBAAQQICYxaHCZwRKCYTTCCR6ZCCR62BLoIRMawoSNJgQSRCKASmCYQSRCKASmABIA=")) + width : 19, height : 62, bpp : 2, + transparent : 0, + buffer : E.toArrayBuffer(atob("AAP/wAAA///wAA////AA////8A/////A/////8P/////D/////w/////8P/////D/////w/////8D////8AP////AA////AAD///AAAP//AAAA//AAAAPXwAAAD18AAAA9fAAAAPXwAAAD18AAAA9fAAAAPXwAAAD18AAAA9fAAAAPXwAAAD18AAAA//AAAA//wAAA///wAD/X1/AD/V9X8A/VfVfw/VX1V8PVV9VXD1VfVV89VX1VfPVf/1Xz1f//V89/9f/fP/1Vf/A/VVVfwP1VVXwA/VVX8AD/VfwAAP//wAAA//wAAAD18AAAA9fAAAAPXAAAAD1wAAAAN8AAAAD8AAAAA/AAAAAPwAAAAA8AAAAAMAAAAADAAAAAAwAAAAAAAAAA==")) }; /* Set minute hand image */ var imgMin = { - width : 10, height : 80, bpp : 8, - transparent : 254, - buffer : require("heatshrink").decompress(atob("/wAL64ABA44ADBBAKCBKQAdHhJQILZBtIBLwKEHRoTKBMlWqwJIwIJ/BP4J/BKDbJBM2BBP4J/BKgADBJoKEBAgJoBQYJIBAwJoBQIJIABw=")) + width : 10, height : 80, bpp : 2, + transparent : 0, + buffer : E.toArrayBuffer(atob("AAAADwAP/wP//D//z/////////8//8P//A//AD/AA/wAP8AD/AA/8A/8AP/wD/8A//AP/wD/8A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A9fAPXwD18A//AP/wA/wAP8AD/AA/wAP8AA/AAPAADwAA8AAPAADwAAMAAAAAAAA=")) }; /* Set second hand image */ var imgSec = { - width : 8, height : 116, bpp : 8, - transparent : 254, - buffer : require("heatshrink").decompress(atob("/3XAAf+AAIHEBAQHMC4QIEA4wGDBAYH/A/4HsayD0BBYj9DBowDLC44nIHAxHGR/4H/A/4H/A9IGBA4YFCAAYHHAAmsAomBqwABA4gICA4oIBC5YICGZRAGK5Cf/A/4H/A7YGFA4oA==")) + width : 8, height : 116, bpp : 2, + transparent : 2, + buffer : E.toArrayBuffer(atob("v/q//r/+v/qv+q/qq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qv+v/6/D/wD8PDw8PwD/w///6v+qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqr+r//v///X/1X/Vf9V/1X/1///+//q/qq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq+qr6qvqq6qrqg==")) }; /* Set variables to get screen width, height and center points */ @@ -107,11 +107,14 @@ function drawHands() { g.drawImage(imgMin,cx-34*minSin,cy+34*minCos,{rotate:minAngle}); g.drawImage(imgSec,cx-25*secSin,cy+25*secCos,{rotate:secAngle}); g.setFont("4x5Numeric:3"); + g.setColor(g.theme.bg); g.drawString(d.getDate(),157,81); } function drawBackground() { g.clear(1); + g.setBgColor(g.theme.bg); + g.setColor(g.theme.fg); g.drawImage(imgBg,0,0); g.reset(); } @@ -135,13 +138,7 @@ Bangle.on('lcdPower', (on) => { } }); -g.setTheme({ - bg : 0, fg : "#fff", dark:true, - bg2 : 0, fg2 : "#fff", - bgH : "#00f", fgH : "#fff", -}); Bangle.setUI("clock"); // load widgets after 'setUI' so they're aware there is a clock active Bangle.loadWidgets(); -displayRefresh(); - +displayRefresh(); \ No newline at end of file diff --git a/apps/rolex/metadata.json b/apps/rolex/metadata.json index d7be25508..e24344dad 100644 --- a/apps/rolex/metadata.json +++ b/apps/rolex/metadata.json @@ -1,17 +1,17 @@ { "id": "rolex", - "name": "rolex", - "shortName":"rolex", - "icon": "rolex.png", - "screenshots": [{"url":"screenshot.png"}], - "version":"0.02", - "description": "A rolex like watch face", - "tags": "clock", - "type": "clock", - "supports":["BANGLEJS2"], - "readme": "README.md", - "allow_emulator": true, - "storage": [ - {"name":"rolex.app.js","url":"app.js"}, - {"name":"rolex.img","url":"app-icon.js","evaluate":true} - ] -} + "name": "rolex", + "shortName":"rolex", + "icon": "rolex.png", + "screenshots": [{"url":"screenshot.png"}], + "version":"0.03", + "description": "A rolex like watch face", + "tags": "clock", + "type": "clock", + "supports":["BANGLEJS2"], + "readme": "README.md", + "allow_emulator": true, + "storage": [ + {"name":"rolex.app.js","url":"app.js"}, + {"name":"rolex.img","url":"app-icon.js","evaluate":true} + ] + } \ No newline at end of file From 01450290c49c3836368df137ebebbf995f730ada Mon Sep 17 00:00:00 2001 From: storm64 Date: Tue, 15 Feb 2022 21:17:07 +0100 Subject: [PATCH 42/60] Update README.md Update README.md - change missed beautified menu entries according to changes in settings.js --- apps/sleeplog/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index b120befb0..4cb136c71 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -8,16 +8,16 @@ also provides a power saving mode using the built in movement calculation. The i #### Operating Principle * __ESS calculation__ The accelerometer polls values with 12.5Hz. On each poll the magnitude value is saved. When 13 values are collected, every 1.04 seconds, the standard deviation over this values is calculated. - Is the calculated standard deviation lower than the "no movement" threshold (__NoMoThresh__) a "no movement" counter is incremented. Each time the "no movement" threshold is reached the "no movement" counter will be reset. The first time no movement is detected the actual timestamp is cached (in _sleeplog.firstnomodate_) for logging. - When the "no movement" counter reaches the sleep threshold the watch is considered as resting. (The sleep threshold is calculated from the __MinDuration__ setting, Example: _sleep threshold = MinDuration * 60 / calculation interval => 10min * 60s/min / 1.04s ~= 576,9 rounded up to 577_) + Is the calculated standard deviation lower than the "no movement" threshold (__NoMo Thresh__) a "no movement" counter is incremented. Each time the "no movement" threshold is reached the "no movement" counter will be reset. The first time no movement is detected the actual timestamp is cached (in _sleeplog.firstnomodate_) for logging. + When the "no movement" counter reaches the sleep threshold the watch is considered as resting. (The sleep threshold is calculated from the __Min Duration__ setting, Example: _sleep threshold = Min Duration * 60 / calculation interval => 10min * 60s/min / 1.04s ~= 576,9 rounded up to 577_) * __Power Saving Mode__ - On power saving mode the movement value of bangle's build in health event is checked against the maximal movement threshold (__MaxMove__). The event is only triggered every 10 minutes which decreases the battery impact but also reduces accurracy. + On power saving mode the movement value of bangle's build in health event is checked against the maximal movement threshold (__Max Move__). The event is only triggered every 10 minutes which decreases the battery impact but also reduces accurracy. * ___Sleeping___ __or__ ___Not Worn___ - To check if a resting watch indicates a sleeping status, the internal temperature must be greater than the temperature threshold (__TempThresh__). Otherwise the watch is considered as not worn. + To check if a resting watch indicates a sleeping status, the internal temperature must be greater than the temperature threshold (__Temp Thresh__). Otherwise the watch is considered as not worn. * __True Sleep__ The true sleep value is a simple addition of all registert sleeping periods. * __Consecutive Sleep__ - In addition the consecutive sleep value tries to predict the complete time you were asleep, even the light sleeping phases with registered movements. All periods after a sleeping period will be summarized til the first following non sleeping period that is longer then the maximal awake duration (__MaxAwake__). If this sum is lower than the minimal consecutive sleep duration (__MinConsec__) it is not considered, otherwise it will be added to the consecutive sleep value. + In addition the consecutive sleep value tries to predict the complete time you were asleep, even the light sleeping phases with registered movements. All periods after a sleeping period will be summarized til the first following non sleeping period that is longer then the maximal awake duration (__Max Awake__). If this sum is lower than the minimal consecutive sleep duration (__Min Consec__) it is not considered, otherwise it will be added to the consecutive sleep value. * __Logging__ To minimize the log size only a changed state is logged. The logged timestamp is matching the beginning of its measurement period. When not on power saving mode a movement is detected nearly instantaneous and the detection of a no movement period is delayed by the minimal no movement duration. To match the beginning of the measurement period a cached timestamp (_sleeplog.firstnomodate_) is logged. From a110a30ad526a4a85e01d424e7ee308115dd7210 Mon Sep 17 00:00:00 2001 From: storm64 Date: Wed, 16 Feb 2022 12:52:00 +0100 Subject: [PATCH 43/60] sleeplog: Fix logfile disabling, add status icons Update app.js - adjust label position to improve readability on light themes - add icons to display service states Update ChangeLog - compact old and add new changes Update lib.js - fix logfile correction in `setEnabled(...)` to make disable logging possible - simplify logfile checks Update README.md - add icons of the service states Update settings.js - fix error on reading a non string logfile value Add icons: disabled.png, nolog.png and powersaving.png --- apps/sleeplog/ChangeLog | 16 +++++++++++++++- apps/sleeplog/README.md | 3 +++ apps/sleeplog/app.js | 24 +++++++++++++++++------- apps/sleeplog/disabled.png | Bin 0 -> 8670 bytes apps/sleeplog/lib.js | 15 +++++++++------ apps/sleeplog/nolog.png | Bin 0 -> 10229 bytes apps/sleeplog/powersaving.png | Bin 0 -> 9751 bytes apps/sleeplog/settings.js | 2 +- 8 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 apps/sleeplog/disabled.png create mode 100644 apps/sleeplog/nolog.png create mode 100644 apps/sleeplog/powersaving.png diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index e37283f09..0b7f686ee 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -1,4 +1,18 @@ 0.01: New App! 0.02: Fix crash on start #1423 0.03: Added power saving mode, move all read/write log actions into lib/module -0.04: Fix #1445, display loading info while calculating sleep data +0.04: Fix #1445, display loading info, add icons to display service states + +Update app.js + - adjust label position to improve readability on light themes + - add icons to display service states +Update ChangeLog + - compact old and add new changes +Update lib.js + - fix logfile correction in `setEnabled(...)` to make disable logging possible + - simplify logfile checks +Update README.md + - add icons of the service states +Update settings.js + - fix error on reading a non string logfile value +Add icons: disabled.png, nolog.png and powersaving.png diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 4cb136c71..10d9a6b0b 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -49,6 +49,7 @@ also provides a power saving mode using the built in movement calculation. The i * __Power Saving__ _on_ / __off__ En-/Disable power saving mode. _Saves battery, but might decrease accurracy._ + In the app the following icon on the right indicates that power saving mode is enabled: ![](powersaving.png) * __Max Move__ | maximal movement threshold (only available when on power saving mode) _50_ / _51_ / _..._ / __100__ / _..._ / _200_ @@ -65,10 +66,12 @@ also provides a power saving mode using the built in movement calculation. The i * __Enabled__ __on__ / _off_ En-/Disable the service (all background activities). _Saves the most battery, but might make this app useless._ + In the app the following icon on the left indicates that the service is disabled: ![](disabled.png) * __Logfile__ __default__ / _off_ En-/Disable logging by setting the logfile to _sleeplog.log_ / _undefined_. If the logfile has been customized it is displayed with _custom_. + In the app the following icon on the left indicates that logging is disabled: ![](nolog.png) --- ### Global Object and Module Functions diff --git a/apps/sleeplog/app.js b/apps/sleeplog/app.js index cf4ecc415..8077b4d62 100644 --- a/apps/sleeplog/app.js +++ b/apps/sleeplog/app.js @@ -64,7 +64,7 @@ function drawLog(topY, viewUntil) { for (var x = 0; x < hours; x++) { g.fillRect(x * stepwidth, y + 2, x * stepwidth, y + 4); g.setFontAlign(-1, -1).setFont("6x8") - .drawString((startHour + x) % 24, x * stepwidth, y + 6); + .drawString((startHour + x) % 24, x * stepwidth + 1, y + 6); } // define variables for sleep calculation @@ -143,7 +143,7 @@ function drawAnalysis(toDate) { // draw log graphs and read outputs drawLog(110, toDate).forEach( (value, index) => outputs[index] += value); - drawLog(145, Date(toDate.valueOf() - 432E5)).forEach( + drawLog(144, Date(toDate.valueOf() - 432E5)).forEach( (value, index) => outputs[index] += value); // draw outputs @@ -176,12 +176,22 @@ function drawNightTo(prevDays) { // clear heading area g.clearRect(0, 24, width, 70); - // display service statuses: service, loggging and powersaving - g.setColor(global.sleeplog && sleeplog.enabled && sleeplog.logfile ? sleeplog.powersaving ? 2016 : g.theme.bg : 63488); - g.fillRect(0, 30, width, 66).reset(); + // display service states: service, loggging and powersaving + if (!sleeplog.enabled) { + // draw disabled service icon + g.setColor(1, 0, 0) + .drawImage(atob("FBSBAAH4AH/gH/+D//w/n8f5/nud7znP85z/f+/3/v8/z/P895+efGPj4Hw//8H/+Af+AB+A"), 2, 36); + } else if (!sleeplog.logfile) { + // draw disabled log icon + g.reset().drawImage(atob("EA6BAM//z/8AAAAAz//P/wAAAADP/8//AAAAAM//z/8="), 4, 40) + .setColor(1, 0, 0).fillPoly([2, 38, 4, 36, 22, 54, 20, 56]); + } + // draw power saving icon + if (sleeplog.powersaving) g.setColor(0, 1, 0) + .drawImage(atob("FBSBAAAAcAD/AH/wP/4P/+H//h//4//+fv/nj/7x/88//Of/jH/4j/8I/+Af+AH+AD8AA4AA"), width - 22, 36); // draw headline - g.setFont("12x20").setFontAlign(0, -1); + g.reset().setFont("12x20").setFontAlign(0, -1); g.drawString("Night to " + require('locale').dow(toDate, 1) + "\n" + require('locale').date(toDate, 1), center, 30); @@ -195,7 +205,7 @@ function drawNightTo(prevDays) { // calculate and draw analysis after timeout for faster feedback if (ATID) ATID = clearTimeout(ATID); - ATID = setTimeout(drawAnalysis, 50, toDate); + ATID = setTimeout(drawAnalysis, 100, toDate); } // define function to draw and setup UI diff --git a/apps/sleeplog/disabled.png b/apps/sleeplog/disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..ea487b48c1277fd748fca990ed7b1b53ab9a8f63 GIT binary patch literal 8670 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 literal 0 HcmV?d00001 diff --git a/apps/sleeplog/lib.js b/apps/sleeplog/lib.js index 1fbd3d2cb..7b35d8a85 100644 --- a/apps/sleeplog/lib.js +++ b/apps/sleeplog/lib.js @@ -5,8 +5,8 @@ exports = { if (typeof global.sleeplog !== "object") return; // set default logfile - logfile = (typeof logfile === "string" && logfile.endsWith(".log")) ? logfile : - logfile === false ? undefined : "sleeplog.log"; + if ((typeof logfile !== "string" || !logfile.endsWith(".log")) && + logfile !== false) logfile = "sleeplog.log"; // stop if enabled if (global.sleeplog.enabled) global.sleeplog.stop(); @@ -40,8 +40,9 @@ exports = { // - string // additional information readLog: function(logfile, since, until) { // check/set logfile - logfile = typeof logfile === "string" && logfile.endsWith(".log") ? logfile : - (global.sleeplog || {}).logfile || "sleeplog.log"; + if (typeof logfile !== "string" || !logfile.endsWith(".log")) { + logfile = (global.sleeplog || {}).logfile || "sleeplog.log"; + } // check if since is in the future if (since > Date()) return []; @@ -73,8 +74,10 @@ exports = { // replace log with input if at least one entry like above is inside another array writeLog: function(logfile, input) { // check/set logfile - logfile = typeof logfile === "string" && logfile.endsWith(".log") ? logfile : - (global.sleeplog || {}).logfile || "sleeplog.log"; + if (typeof logfile !== "string" || !logfile.endsWith(".log")) { + if (!global.sleeplog || sleeplog.logfile === false) return; + logfile = sleeplog.logfile || "sleeplog.log"; + } // check if input is an array if (typeof input !== "object" || typeof input.length !== "number") return; diff --git a/apps/sleeplog/nolog.png b/apps/sleeplog/nolog.png new file mode 100644 index 0000000000000000000000000000000000000000..b153b57698f1d4d747e0112d1d7755db26ebf524 GIT binary patch literal 10229 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 literal 0 HcmV?d00001 diff --git a/apps/sleeplog/settings.js b/apps/sleeplog/settings.js index ffc014337..11c7c0adb 100644 --- a/apps/sleeplog/settings.js +++ b/apps/sleeplog/settings.js @@ -127,7 +127,7 @@ } }, "Logfile ": { - value: settings.logfile === "sleeplog.log" ? true : settings.logfile.endsWith(".log") ? "custom" : false, + value: settings.logfile === "sleeplog.log" ? true : (settings.logfile || "").endsWith(".log") ? "custom" : false, format: v => v === true ? "default" : v ? "custom" : "off", onchange: function(v) { if (v !== "custom") { From 1ffcc42a93c229ca08fc440b67c803e1b3fe912d Mon Sep 17 00:00:00 2001 From: storm64 Date: Wed, 16 Feb 2022 12:54:40 +0100 Subject: [PATCH 44/60] Update ChangeLog Remove commit description --- apps/sleeplog/ChangeLog | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/apps/sleeplog/ChangeLog b/apps/sleeplog/ChangeLog index 0b7f686ee..d12c565ac 100644 --- a/apps/sleeplog/ChangeLog +++ b/apps/sleeplog/ChangeLog @@ -2,17 +2,3 @@ 0.02: Fix crash on start #1423 0.03: Added power saving mode, move all read/write log actions into lib/module 0.04: Fix #1445, display loading info, add icons to display service states - -Update app.js - - adjust label position to improve readability on light themes - - add icons to display service states -Update ChangeLog - - compact old and add new changes -Update lib.js - - fix logfile correction in `setEnabled(...)` to make disable logging possible - - simplify logfile checks -Update README.md - - add icons of the service states -Update settings.js - - fix error on reading a non string logfile value -Add icons: disabled.png, nolog.png and powersaving.png From 9885caff71bbc4eab0947e1eceaa0f23972dc22d Mon Sep 17 00:00:00 2001 From: storm64 Date: Wed, 16 Feb 2022 13:00:39 +0100 Subject: [PATCH 45/60] Update README.md Change description to icons --- apps/sleeplog/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/sleeplog/README.md b/apps/sleeplog/README.md index 10d9a6b0b..4b10438ef 100644 --- a/apps/sleeplog/README.md +++ b/apps/sleeplog/README.md @@ -48,8 +48,8 @@ also provides a power saving mode using the built in movement calculation. The i The internal temperature must be greater than this threshold to log _sleeping_, otherwise it is _not worn_. * __Power Saving__ _on_ / __off__ - En-/Disable power saving mode. _Saves battery, but might decrease accurracy._ - In the app the following icon on the right indicates that power saving mode is enabled: ![](powersaving.png) + En-/Disable power saving mode. _Saves battery, but might decrease accurracy._ + In app icon showing that power saving mode is enabled: ![](powersaving.png) * __Max Move__ | maximal movement threshold (only available when on power saving mode) _50_ / _51_ / _..._ / __100__ / _..._ / _200_ @@ -65,13 +65,13 @@ also provides a power saving mode using the built in movement calculation. The i If no movement is detected for this duration, the watch is considered as resting. * __Enabled__ __on__ / _off_ - En-/Disable the service (all background activities). _Saves the most battery, but might make this app useless._ - In the app the following icon on the left indicates that the service is disabled: ![](disabled.png) + En-/Disable the service (all background activities). _Saves the most battery, but might make this app useless._ + In app icon showing that the service is disabled: ![](disabled.png) * __Logfile__ __default__ / _off_ En-/Disable logging by setting the logfile to _sleeplog.log_ / _undefined_. - If the logfile has been customized it is displayed with _custom_. - In the app the following icon on the left indicates that logging is disabled: ![](nolog.png) + If the logfile has been customized it is displayed with _custom_. + In app icon showing that logging is disabled: ![](nolog.png) --- ### Global Object and Module Functions From ef4c2d0e12811e123028de8defd1bdaff7e564c8 Mon Sep 17 00:00:00 2001 From: storm64 Date: Wed, 16 Feb 2022 13:04:09 +0100 Subject: [PATCH 46/60] sleeplog: Switch disabled.png and powersaving.png --- apps/sleeplog/disabled.png | Bin 8670 -> 9751 bytes apps/sleeplog/powersaving.png | Bin 9751 -> 8670 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/sleeplog/disabled.png b/apps/sleeplog/disabled.png index ea487b48c1277fd748fca990ed7b1b53ab9a8f63..8e7d9fb2c0786fc9415c47c25eefac864e509403 100644 GIT binary patch delta 6149 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 X<-RZ<=9j!q00000NkvXXu0mjfMDeh_ delta 5059 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 Z0KY6ADufT35W4^X002ovPDHLkV1jmPyTJee diff --git a/apps/sleeplog/powersaving.png b/apps/sleeplog/powersaving.png index 8e7d9fb2c0786fc9415c47c25eefac864e509403..ea487b48c1277fd748fca990ed7b1b53ab9a8f63 100644 GIT binary patch delta 5059 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 Z0KY6ADufT35W4^X002ovPDHLkV1jmPyTJee delta 6149 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 X<-RZ<=9j!q00000NkvXXu0mjfMDeh_ From b32cc829f540908ca0d05563545ed468155db25d Mon Sep 17 00:00:00 2001 From: storm64 Date: Wed, 16 Feb 2022 13:21:00 +0100 Subject: [PATCH 47/60] sleeplog: Update screenshots --- apps/sleeplog/screenshot1.png | Bin 4074 -> 25161 bytes apps/sleeplog/screenshot2.png | Bin 3497 -> 20979 bytes apps/sleeplog/screenshot3.png | Bin 4111 -> 26582 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/sleeplog/screenshot1.png b/apps/sleeplog/screenshot1.png index 9ab384ca93dedea2d7499bf8f9e7cd61b3ee3826..200a305c48a34db260de66737612dd28c6484c8a 100644 GIT binary patch literal 25161 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 A1^@s6 diff --git a/apps/sleeplog/screenshot2.png b/apps/sleeplog/screenshot2.png index 69e370042b9d83e90c9fc5c9930f54a884dfa77c..61f580336bdce8121365fac1b796ba2c44a34ec1 100644 GIT binary patch literal 20979 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⪚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 QI{*Lx07*qoM6N<$f-Wg0E&u=k literal 3497 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 diff --git a/apps/sleeplog/screenshot3.png b/apps/sleeplog/screenshot3.png index 79433949a4f1abe017d9dd469e489b75caaa9983..4a29b50089b5bd1e400c9bac9ea70c3b16fe972f 100644 GIT binary patch literal 26582 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 L-pIXf^Wc8~v`e`~ literal 4111 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 PA3ku+#LBqB5EcI)fkepR From 6d25243580c358e3c87708ebb9fb9d2c43bed992 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 16 Feb 2022 14:42:17 +0000 Subject: [PATCH 48/60] tweak --- apps/rolex/ChangeLog | 5 +++-- apps/rolex/Changelog | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-) delete mode 100644 apps/rolex/Changelog diff --git a/apps/rolex/ChangeLog b/apps/rolex/ChangeLog index 283856aed..3bfed4a4e 100644 --- a/apps/rolex/ChangeLog +++ b/apps/rolex/ChangeLog @@ -1,2 +1,3 @@ -0.01: Initial Release -0.02: Minor tweaks for light theme +0.01: Initial Release +0.02: Minor tweaks for light theme +0.03: Made images 2 bit and fixed theme honoring \ No newline at end of file diff --git a/apps/rolex/Changelog b/apps/rolex/Changelog deleted file mode 100644 index 3bfed4a4e..000000000 --- a/apps/rolex/Changelog +++ /dev/null @@ -1,3 +0,0 @@ -0.01: Initial Release -0.02: Minor tweaks for light theme -0.03: Made images 2 bit and fixed theme honoring \ No newline at end of file From 0398ffd855e3c75cb4ca6389a1886028401a5034 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 16 Feb 2022 16:30:40 +0000 Subject: [PATCH 49/60] new core --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index bd894bfdc..bf29f5697 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit bd894bfdcee8293c97763de2b4d105a6b6e5415e +Subproject commit bf29f5697445686255a785476e6b1ed6a13ff697 From d6da125df7927dec535c9c8c7c3dea5e145d88c8 Mon Sep 17 00:00:00 2001 From: David Peer Date: Wed, 16 Feb 2022 17:54:12 +0100 Subject: [PATCH 50/60] Added optinoal fullscreen mode. --- apps/neonx/ChangeLog | 1 + apps/neonx/README.md | 8 +++++-- apps/neonx/metadata.json | 2 +- apps/neonx/neonx.app.js | 45 ++++++++++++++++++++++++------------ apps/neonx/neonx.settings.js | 13 +++++++++-- 5 files changed, 49 insertions(+), 20 deletions(-) diff --git a/apps/neonx/ChangeLog b/apps/neonx/ChangeLog index af7f83942..7ac033fe8 100644 --- a/apps/neonx/ChangeLog +++ b/apps/neonx/ChangeLog @@ -1 +1,2 @@ 0.01: Initial release +0.02: Optional fullscreen mode \ No newline at end of file diff --git a/apps/neonx/README.md b/apps/neonx/README.md index d836dfab3..c3926c4b6 100644 --- a/apps/neonx/README.md +++ b/apps/neonx/README.md @@ -4,8 +4,8 @@ |---------------------------------|--------------------------------------| |

Neon X
|
Neon IO X
| -This is a clock based on Pebble's Neon X and Neon IO X watchfaces by Sam Jerichow. -Can be switched between in the Settings menu, which can be accessed through +This is a clock based on Pebble's Neon X and Neon IO X watchfaces by Sam Jerichow. +Can be switched between in the Settings menu, which can be accessed through the app/widget settings menu of the Bangle.js ## Settings @@ -18,3 +18,7 @@ The thickness of watch lines, from 1 to 5. ### Date on touch Shows the current date as DD MM on touch and reverts back to time after 5 seconds or with another touch. + +### Fullscreen +Shows the watchface in fullscreen mode. +Note: In fullscreen mode, widgets are hidden, but still loaded. \ No newline at end of file diff --git a/apps/neonx/metadata.json b/apps/neonx/metadata.json index 41b16d11b..ffa4d1b8e 100644 --- a/apps/neonx/metadata.json +++ b/apps/neonx/metadata.json @@ -2,7 +2,7 @@ "id": "neonx", "name": "Neon X & IO X Clock", "shortName": "Neon X Clock", - "version": "0.01", + "version": "0.02", "description": "Pebble Neon X & Neon IO X for Bangle.js", "icon": "neonx.png", "type": "clock", diff --git a/apps/neonx/neonx.app.js b/apps/neonx/neonx.app.js index 967fc8582..d3521b1db 100644 --- a/apps/neonx/neonx.app.js +++ b/apps/neonx/neonx.app.js @@ -34,6 +34,7 @@ const colors = { const is12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false; const screenWidth = g.getWidth(); +const screenHeight = g.getHeight(); const halfWidth = screenWidth / 2; const scale = screenWidth / 240; const REFRESH_RATE = 10E3; @@ -58,16 +59,19 @@ function drawLine(poly, thickness){ } } -let settings = require('Storage').readJSON('neonx.json', 1); - -if (!settings) { - settings = { - thickness: 4, - io: 0, - showDate: 1 - }; +let settings = { + thickness: 4, + io: 0, + showDate: 1, + fullscreen: false, +}; +let saved_settings = require('Storage').readJSON('neonx.json', 1) || settings; +for (const key in saved_settings) { + settings[key] = saved_settings[key] } + + function drawClock(num){ let tx, ty; @@ -79,13 +83,15 @@ function drawClock(num){ g.setColor(colors[settings.io ? 'io' : 'x'][y][x]); if (!settings.io) { - tx = (x * 100 + 18) * newScale; - ty = (y * 100 + 32) * newScale; + newScale *= settings.fullscreen ? 1.18 : 1.0; + let dx = settings.fullscreen ? 0 : 18 + tx = (x * 100 + dx) * newScale; + ty = (y * 100 + dx*2) * newScale; } else { - newScale = 0.33 + current * 0.4; + newScale = 0.33 + current * (settings.fullscreen ? 0.48 : 0.4); - tx = (halfWidth - 139) * newScale + halfWidth; - ty = (halfWidth - 139) * newScale + halfWidth + 12; + tx = (halfWidth - 139) * newScale + halfWidth + (settings.fullscreen ? 2 : 0); + ty = (halfWidth - 139) * newScale + halfWidth + (settings.fullscreen ? 1 : 12); } for (let i = 0; i < digits[num[y][x]].length; i++) { @@ -116,7 +122,11 @@ function draw(date){ l2 = ('0' + d.getMinutes()).substr(-2); } - g.clearRect(0,24,240,240); + if(settings.fullscreen){ + g.clearRect(0,0,screenWidth,screenHeight); + } else { + g.clearRect(0,24,240,240); + } drawClock([l1, l2]); } @@ -150,4 +160,9 @@ Bangle.on('lcdPower', function(on){ }); Bangle.loadWidgets(); -Bangle.drawWidgets(); + +if(settings.fullscreen){ + for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} +} else { + Bangle.drawWidgets(); +} \ No newline at end of file diff --git a/apps/neonx/neonx.settings.js b/apps/neonx/neonx.settings.js index 0e205e03b..b4b481baf 100644 --- a/apps/neonx/neonx.settings.js +++ b/apps/neonx/neonx.settings.js @@ -7,7 +7,8 @@ neonXSettings = { thickness: 4, io: 0, - showDate: 1 + showDate: 1, + fullscreen: false, }; updateSettings(); @@ -48,7 +49,15 @@ neonXSettings.showDate = v; updateSettings(); } - } + }, + 'Fullscreen': { + value: false | neonXSettings.fullscreen, + format: () => (neonXSettings.fullscreen ? 'Yes' : 'No'), + onchange: () => { + neonXSettings.fullscreen = !neonXSettings.fullscreen; + updateSettings(); + }, + }, }; E.showMenu(menu); }) From 08af40ff9aeed52acebbfe523659dd5a641f9bb2 Mon Sep 17 00:00:00 2001 From: David Peer Date: Wed, 16 Feb 2022 18:05:22 +0100 Subject: [PATCH 51/60] Allow a thickness of 6 - looks nice in fullscreen mode. --- apps/neonx/README.md | 2 +- apps/neonx/neonx.settings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/neonx/README.md b/apps/neonx/README.md index c3926c4b6..f205b702f 100644 --- a/apps/neonx/README.md +++ b/apps/neonx/README.md @@ -14,7 +14,7 @@ the app/widget settings menu of the Bangle.js Activate the Neon IO X clock look, a bit hard to read until one gets used to it. ### Thickness -The thickness of watch lines, from 1 to 5. +The thickness of watch lines, from 1 to 6. ### Date on touch Shows the current date as DD MM on touch and reverts back to time after 5 seconds or with another touch. diff --git a/apps/neonx/neonx.settings.js b/apps/neonx/neonx.settings.js index b4b481baf..3af2e0fa5 100644 --- a/apps/neonx/neonx.settings.js +++ b/apps/neonx/neonx.settings.js @@ -18,7 +18,7 @@ if (!neonXSettings) resetSettings(); - let thicknesses = [1, 2, 3, 4, 5]; + let thicknesses = [1, 2, 3, 4, 5, 6]; const menu = { "" : { "title":"Neon X & IO"}, From 307527386e7f75959514fec2ee99d1b6f790a6ab Mon Sep 17 00:00:00 2001 From: David Peer Date: Wed, 16 Feb 2022 18:07:15 +0100 Subject: [PATCH 52/60] Minor position fix --- apps/neonx/neonx.app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/neonx/neonx.app.js b/apps/neonx/neonx.app.js index d3521b1db..4ef0986fe 100644 --- a/apps/neonx/neonx.app.js +++ b/apps/neonx/neonx.app.js @@ -91,7 +91,7 @@ function drawClock(num){ newScale = 0.33 + current * (settings.fullscreen ? 0.48 : 0.4); tx = (halfWidth - 139) * newScale + halfWidth + (settings.fullscreen ? 2 : 0); - ty = (halfWidth - 139) * newScale + halfWidth + (settings.fullscreen ? 1 : 12); + ty = (halfWidth - 139) * newScale + halfWidth + (settings.fullscreen ? 2 : 12); } for (let i = 0; i < digits[num[y][x]].length; i++) { From 88e9fca8e46e265b3c855de9ae80b50de1c6e06d Mon Sep 17 00:00:00 2001 From: David Peer Date: Wed, 16 Feb 2022 19:57:22 +0100 Subject: [PATCH 53/60] Add simple info app --- apps/info/ChangeLog | 1 + apps/info/README.md | 17 +++++++ apps/info/info.app.js | 106 ++++++++++++++++++++++++++++++++++++++++ apps/info/info.icon.js | 1 + apps/info/info.png | Bin 0 -> 1548 bytes apps/info/metadata.json | 18 +++++++ 6 files changed, 143 insertions(+) create mode 100644 apps/info/ChangeLog create mode 100644 apps/info/README.md create mode 100644 apps/info/info.app.js create mode 100644 apps/info/info.icon.js create mode 100644 apps/info/info.png create mode 100644 apps/info/metadata.json diff --git a/apps/info/ChangeLog b/apps/info/ChangeLog new file mode 100644 index 000000000..07afedd21 --- /dev/null +++ b/apps/info/ChangeLog @@ -0,0 +1 @@ +0.01: Release \ No newline at end of file diff --git a/apps/info/README.md b/apps/info/README.md new file mode 100644 index 000000000..007a9794e --- /dev/null +++ b/apps/info/README.md @@ -0,0 +1,17 @@ +# Info + +A very simple app that shows information on 3 different screens. +Go to the next screen via tab right, go to the previous screen +via tab left and reload the data via tab in the middle of the +screen. Very useful if combined with pattern launcher ;) + +![](screenshot_1.png) +![](screenshot_2.png) +![](screenshot_2.png) + + +## Contributors +- [David Peer](https://github.com/peerdavid). + +## Thanks To +Info icons created by Freepik - Flaticon diff --git a/apps/info/info.app.js b/apps/info/info.app.js new file mode 100644 index 000000000..b241907f3 --- /dev/null +++ b/apps/info/info.app.js @@ -0,0 +1,106 @@ +var s = require("Storage"); +const locale = require('locale'); +var ENV = process.env; +var W = g.getWidth(), H = g.getHeight(); +var screen = 0; + +function getVersion(file) { + var j = s.readJSON(file,1); + var v = ("object"==typeof j)?j.version:false; + return v?((v?"v"+v:"Unknown")):"NO "; +} + + +function drawData(name, value, y){ + g.drawString(name, 5, y); + g.drawString(value, 100, y); +} + +function getSteps(){ + try{ + return Bangle.getHealthStatus("day").steps; + } catch(e) { + return ">= 2v12"; + } +} + +function getBpm(){ + try{ + return Math.round(Bangle.getHealthStatus("day").bpm) + " bpm"; + } catch(e) { + return ">= 2v12"; + } +} + +function drawInfo() { + g.reset().clearRect(Bangle.appRect); + var h=18, y = h;//-h; + + // Header + g.setFont("Vector", h+2).setFontAlign(0,-1); + g.drawString("--==|| INFO ||==--", W/2, 0); + g.setFont("Vector",h).setFontAlign(-1,-1); + + // Dynamic data + if(screen == 0){ + drawData("Steps", getSteps(), y+=h); + drawData("HRM", getBpm(), y+=h); + drawData("Battery", E.getBattery() + "%", y+=h); + drawData("Voltage", E.getAnalogVRef().toFixed(2) + "V", y+=h); + drawData("IntTemp.", locale.temp(parseInt(E.getTemperature())), y+=h); + } + + if(screen == 1){ + drawData("Charging?", Bangle.isCharging() ? "Yes" : "No", y+=h); + drawData("Bluetooth", NRF.getSecurityStatus().connected ? "Conn." : "Disconn.", y+=h); + drawData("GPS", Bangle.isGPSOn() ? "On" : "Off", y+=h); + drawData("Compass", Bangle.isCompassOn() ? "On" : "Off", y+=h); + drawData("HRM", Bangle.isHRMOn() ? "On" : "Off", y+=h); + } + + // Static data + if(screen == 2){ + drawData("Firmw.", ENV.VERSION, y+=h); + drawData("Boot.", getVersion("boot.info"), y+=h); + drawData("Settings", getVersion("setting.info"), y+=h); + drawData("Storage", "", y+=h); + drawData(" Total", ENV.STORAGE>>10, y+=h); + drawData(" Free", require("Storage").getFree()>>10, y+=h); + } + + if(Bangle.isLocked()){ + g.setFont("Vector",h-2).setFontAlign(-1,-1); + g.drawString("Locked", 0, H-h+2); + } + + g.setFont("Vector",h-2).setFontAlign(1,-1); + g.drawString((screen+1) + "/3", W, H-h+2); +} + +drawInfo(); +setWatch(_=>load(), BTN1); + +Bangle.on('touch', function(btn, e){ + var left = parseInt(g.getWidth() * 0.3); + var right = g.getWidth() - left; + var isLeft = e.x < left; + var isRight = e.x > right; + + if(isRight){ + screen = (screen + 1) % 3; + } + + if(isLeft){ + screen = Math.max(0, (screen - 1)); + } + + drawInfo(); +}); + +Bangle.on('lock', function(isLocked) { + drawInfo(); +}); + +Bangle.loadWidgets(); +for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} +// Bangle.drawWidgets(); \ No newline at end of file diff --git a/apps/info/info.icon.js b/apps/info/info.icon.js new file mode 100644 index 000000000..8dbab8357 --- /dev/null +++ b/apps/info/info.icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwcBkmSpICDBwcJBYwCDpAhFggRJGg8SCI+ABgU//gSDCI4JBj//AAX4JRAIBg4QDAAPgBIJWGgIQFAAI+BLglAgEPCI/wEgJoEgYQHAAPANwhWFAApcBCIWQgAQJAAMAgSMDCJiSCwB6GQA6eCn5TFL4q5BUgIRF/wuBv4RGkCeGO4IREUgMBCJCVGCISwIWw0BYRLIICLBHHCJRrGCIQIFR44I5LIoRaPpARcdIwRJfYMBCJuACKUkgE/a5f8gEJCJD7FCIeAg78FAAvggFJCIMACJZOBCIOQCJsCCIOSgEfCBP4gESCIZTFOIwRDoDIGaguSCIVIgCkFTwcAggRDpIYBQAx6BgAOCAQYIBLghWBTwQRFFgIABXIIFDBwgCDBYQAENAYCFLgIAEKwpKIIhA=")) diff --git a/apps/info/info.png b/apps/info/info.png new file mode 100644 index 0000000000000000000000000000000000000000..c73813025950aaccc31b7476b67ceb6326b5f915 GIT binary patch literal 1548 zcmV+n2J`ueP)&f>~B9rWa~T zO&bJa&{8T~N=wOnsPP9`P{G7MVAx;4pjMrUnq>u3O-u&aq>I5{OcTJi(H~mE+Gfk` zhf%z{UZL%^)V-5S?s@KcKA+d;p68x>?s-lCJcJM`j>qHsVzJmqkw`=t2n3XO?%er& zqdID}dak;<`dMXVI(xU#bHT_6xR>G%8PGcz;NZ0s~N zG%U$vvRR2l;wmpMzr`nzv}L#3NMqE?>U9sV}3^`177Udj^Y&ihNF|^Y3){HkHX_ z>Vgz^uDQ9Hx_I#-m8nKXMyO-Qj-_l7v)OE$oSZD;4S)Ld>7i6kawUyM!)d>>vvV|S zaD>nY27_UO%kb3HRL)j5ALy~a-T<{$o9eDw#%WO6$1)vYg-1N(D?XxQV}@xNctk%xpOD4x1`Z%WTd51>G=A70zybg5X4iUO_JPhH?K*TTrQtZ z*gACR5bxP2olT`u`I44a);1UZ7t1PreSKQaX432RZhAqp*<5ILiX=$}yjH9Aq+IBA zI2@m`qdkBAe8J$kmKLp6>o;^}##Cb4wr%rg&YbC|=bbxu4z6Fn&KYn%b^G>huvjdN zyzcJqA4*C}o^DtXA%v|hWiukba^*@v?|fTZ+fN&=OtW?3#EB`kkyEEmJs? z`0?YvWVFEn05C8xP@~i79fFvXm(BVxnoIvU-OZ#@ss601s~g(Ad-pw(B>zgcUe-T*L?RK@^73+r z-|zp@>-A=}prlkPU&~~&=TfP3yrQCFGMk@0c+C*ky4LTko@l5yPs}U y=jhR+zwg?$YqqMY>dE%)+keAx+{foHm45-pmUCXlaFiAR0000 Date: Wed, 16 Feb 2022 20:00:33 +0100 Subject: [PATCH 54/60] Added screenshots --- apps/info/screenshot_1.png | Bin 0 -> 3552 bytes apps/info/screenshot_2.png | Bin 0 -> 3334 bytes apps/info/screenshot_3.png | Bin 0 -> 3455 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/info/screenshot_1.png create mode 100644 apps/info/screenshot_2.png create mode 100644 apps/info/screenshot_3.png diff --git a/apps/info/screenshot_1.png b/apps/info/screenshot_1.png new file mode 100644 index 0000000000000000000000000000000000000000..97d42a89636e01437a6397075db1dc453ca22fe8 GIT binary patch literal 3552 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 literal 0 HcmV?d00001 diff --git a/apps/info/screenshot_2.png b/apps/info/screenshot_2.png new file mode 100644 index 0000000000000000000000000000000000000000..2d25dd4e6ce4166d5a6b48ad7f50231bf643cc95 GIT binary patch literal 3334 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 literal 0 HcmV?d00001 diff --git a/apps/info/screenshot_3.png b/apps/info/screenshot_3.png new file mode 100644 index 0000000000000000000000000000000000000000..782e4a1952f1dc3205f20852edf88e9493edd6d8 GIT binary patch literal 3455 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 Xk+%_!CsMW>72tTx)$W-M?(+Wt^h|>m literal 0 HcmV?d00001 From 88ea3bd0b11f3f946939647a7647cecca94663f4 Mon Sep 17 00:00:00 2001 From: David Peer Date: Wed, 16 Feb 2022 20:02:20 +0100 Subject: [PATCH 55/60] Added README to metadata --- apps/info/metadata.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/info/metadata.json b/apps/info/metadata.json index 9b82acb4e..f05f0e134 100644 --- a/apps/info/metadata.json +++ b/apps/info/metadata.json @@ -6,6 +6,7 @@ "icon": "info.png", "type": "app", "tags": "tool", + "readme": "README.md", "supports": ["BANGLEJS2"], "screenshots": [ {"url":"screenshot_1.png"}, From 50f973b3d3779ef883aab748bbbf3fca39ca2184 Mon Sep 17 00:00:00 2001 From: David Peer Date: Wed, 16 Feb 2022 20:44:35 +0100 Subject: [PATCH 56/60] Pagination bugfix. --- apps/info/info.app.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/info/info.app.js b/apps/info/info.app.js index b241907f3..bce9d4cff 100644 --- a/apps/info/info.app.js +++ b/apps/info/info.app.js @@ -3,6 +3,7 @@ const locale = require('locale'); var ENV = process.env; var W = g.getWidth(), H = g.getHeight(); var screen = 0; +const maxScreen = 2; function getVersion(file) { var j = s.readJSON(file,1); @@ -87,11 +88,12 @@ Bangle.on('touch', function(btn, e){ var isRight = e.x > right; if(isRight){ - screen = (screen + 1) % 3; + screen = (screen + 1) % (maxScreen+1); } if(isLeft){ - screen = Math.max(0, (screen - 1)); + screen -= 1; + screen = screen < 0 ? maxScreen : screen; } drawInfo(); From 9dd3e9b06925b848d89eadd9ec46d7ada4085a0a Mon Sep 17 00:00:00 2001 From: hughbarney Date: Wed, 16 Feb 2022 20:02:56 +0000 Subject: [PATCH 57/60] Pastel: make new boolean setting work --- apps/pastel/ChangeLog | 1 + apps/pastel/metadata.json | 2 +- apps/pastel/pastel.settings.js | 32 +++++++++++--------------------- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/apps/pastel/ChangeLog b/apps/pastel/ChangeLog index d133697b3..a77fa758f 100644 --- a/apps/pastel/ChangeLog +++ b/apps/pastel/ChangeLog @@ -16,3 +16,4 @@ 0.14: incorporated lazybones idle timer, configuration settings to come 0.15: fixed tendancy for mylocation to default to London added setting to enable/disable idle timer warning +0.16: make check_idle boolean setting work properly with new B2 menu diff --git a/apps/pastel/metadata.json b/apps/pastel/metadata.json index da3c18eae..f04a7ae54 100644 --- a/apps/pastel/metadata.json +++ b/apps/pastel/metadata.json @@ -2,7 +2,7 @@ "id": "pastel", "name": "Pastel Clock", "shortName": "Pastel", - "version": "0.15", + "version": "0.16", "description": "A Configurable clock with custom fonts, background and weather display. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times", "icon": "pastel.png", "dependencies": {"mylocation":"app","weather":"app"}, diff --git a/apps/pastel/pastel.settings.js b/apps/pastel/pastel.settings.js index 26dafd271..afe461f15 100644 --- a/apps/pastel/pastel.settings.js +++ b/apps/pastel/pastel.settings.js @@ -38,38 +38,28 @@ }, }, 'Show Grid': { - value: s.grid, - format: () => (s.grid ? 'Yes' : 'No'), - onchange: () => { - s.grid = !s.grid; + value: !!s.grid, + format: v => v ? /*LANG*/"Yes":/*LANG*/"No", + onchange: v => { + s.grid = v; save(); }, }, 'Show Weather': { - value: s.weather, - format: () => (s.weather ? 'Yes' : 'No'), - onchange: () => { - s.weather = !s.weather; + value: !!s.weather, + format: v => v ? /*LANG*/"Yes":/*LANG*/"No", + onchange: v => { + s.weather = v; save(); }, }, - // for use when the new menu system goes live - /* 'Idle Warning': { - value: s.idle_check, - onchange : v => { + value: !!s.idle_check, + format: v => v ? /*LANG*/"Yes":/*LANG*/"No", + onchange: v => { s.idle_check = v; save(); }, - }, - */ - 'Idle Warning': { - value: s.idle_check, - format: () => (s.idle_check ? 'Yes' : 'No'), - onchange: () => { - s.idle_check = !s.idle_check; - save(); - }, } }) }) From ece775bb8ea0e38d38123116fa49865923b6cc1f Mon Sep 17 00:00:00 2001 From: David Peer Date: Wed, 16 Feb 2022 21:31:46 +0100 Subject: [PATCH 58/60] Minor change --- apps/info/info.app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/info/info.app.js b/apps/info/info.app.js index bce9d4cff..c61a88045 100644 --- a/apps/info/info.app.js +++ b/apps/info/info.app.js @@ -27,7 +27,7 @@ function getSteps(){ function getBpm(){ try{ - return Math.round(Bangle.getHealthStatus("day").bpm) + " bpm"; + return Math.round(Bangle.getHealthStatus("day").bpm) + "bpm"; } catch(e) { return ">= 2v12"; } From 864faef9617c2e551093abff341aa8f3d2a62d57 Mon Sep 17 00:00:00 2001 From: Martin Boonk Date: Wed, 16 Feb 2022 21:59:28 +0100 Subject: [PATCH 59/60] widbars - Yellow battery bar on charge and prevent GC on every draw --- apps/widbars/ChangeLog | 2 ++ apps/widbars/metadata.json | 2 +- apps/widbars/widget.js | 10 ++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/widbars/ChangeLog b/apps/widbars/ChangeLog index 4c21f3ace..61e28e6e4 100644 --- a/apps/widbars/ChangeLog +++ b/apps/widbars/ChangeLog @@ -1 +1,3 @@ 0.01: New Widget! +0.02: Battery bar turns yellow on charge + Memory status bar does not trigger garbage collect diff --git a/apps/widbars/metadata.json b/apps/widbars/metadata.json index e8d52c90a..a9981305c 100644 --- a/apps/widbars/metadata.json +++ b/apps/widbars/metadata.json @@ -1,7 +1,7 @@ { "id": "widbars", "name": "Bars Widget", - "version": "0.01", + "version": "0.02", "description": "Display several measurements as vertical bars.", "icon": "icon.png", "screenshots": [{"url":"screenshot.png"}], diff --git a/apps/widbars/widget.js b/apps/widbars/widget.js index a1134f31f..cceeb0897 100644 --- a/apps/widbars/widget.js +++ b/apps/widbars/widget.js @@ -42,19 +42,25 @@ if (top) g .clearRect(x,y, x+w-1,y+top-1); // erase above bar if (f) g.setColor(col).fillRect(x,y+top, x+w-1,y+h-1); // even for f=0.001 this is still 1 pixel high } + let batColor='#0f0'; function draw() { g.reset(); const x = this.x, y = this.y, - m = process.memory(); + m = process.memory(false); let b=0; // ==HRM== bar(x+(w*b++),y,'#f00'/*red */,bpm/200); // >200 seems very unhealthy; if we have no valid bpm this will just be empty space // ==Temperature== bar(x+(w*b++),y,'#ff0'/*yellow */,E.getTemperature()/50); // you really don't want to wear a watch that's hotter than 50°C bar(x+(w*b++),y,g.theme.dark?'#0ff':'#00f'/*cyan/blue*/,1-(require('Storage').getFree() / process.env.STORAGE)); bar(x+(w*b++),y,'#f0f'/*magenta*/,m.usage/m.total); - bar(x+(w*b++),y,'#0f0'/*green */,E.getBattery()/100); + bar(x+(w*b++),y,batColor,E.getBattery()/100); } let redraw; + Bangle.on('charging', function(charging) { + batColor=charging?'#ff0':'#0f0'; + WIDGETS["bars"].draw(); + }); + Bangle.on('lcdPower', on => { if (redraw) clearInterval(redraw) redraw = undefined; From 63156bcca81875e7b61f6f64a69492e513f9e246 Mon Sep 17 00:00:00 2001 From: Danny <31635744+DDDanny@users.noreply.github.com> Date: Thu, 17 Feb 2022 02:42:21 +0100 Subject: [PATCH 60/60] v 0.04 fixes #1154 --- apps/timecal/ChangeLog | 9 + apps/timecal/README.md | 22 + apps/timecal/metadata.json | 11 +- apps/timecal/testing/timecal.app.test.js | 798 +++++++++++++++++++++++ apps/timecal/timecal.app.js | 352 +++++++--- apps/timecal/timecal.settings.js | 109 ++++ 6 files changed, 1211 insertions(+), 90 deletions(-) create mode 100644 apps/timecal/ChangeLog create mode 100644 apps/timecal/README.md create mode 100644 apps/timecal/testing/timecal.app.test.js create mode 100644 apps/timecal/timecal.settings.js diff --git a/apps/timecal/ChangeLog b/apps/timecal/ChangeLog new file mode 100644 index 000000000..43bff461d --- /dev/null +++ b/apps/timecal/ChangeLog @@ -0,0 +1,9 @@ +0.01: Initial creation of the clock face time and calendar +0.02: Feature Request #1154 and some findings... + -> get rendered time from optimisations + -> *BATT SAFE* only update once a minute instead of once a second + -> *RAM optimized* clean code, corrected minute update (timout, no intervall) + -> locale: weekday name (first two characters) from locale + -> added settings to render cal view begin day (-1: today, 0:sunday, 1:monday [default]) +0.03: a lot of more settings for outline, colors and highlights +0.04: finalized README, fixed settings cancel, fixed border-setting \ No newline at end of file diff --git a/apps/timecal/README.md b/apps/timecal/README.md new file mode 100644 index 000000000..d26f9ba4d --- /dev/null +++ b/apps/timecal/README.md @@ -0,0 +1,22 @@ +# Calendar Clock + +## Features +Shows the +* Date +* Time (hh:mm) - respecting 12/24 (uses locale string) +* 3 weeks calendar view (last,current and next week) + +### The settings menu +Calendar View can be customized +* < Save: Exist and save the current settings +* Show date: Choose if and how the date is displayed: none, locale (default), monthfull or monthshort.yearshort #weeknum with 0 prefixed +* Start wday: Set day of week start. Values: 0=Sunday, 1=Monday,...,6=Saturday or -1=Relative to today (default 0: Sunday) +* Su color: Set Sundays color. Values: none (default), red, green or blue +* Border: show or none (default) +* Submenu Today settings - choose how today is highlighted + * < Back: + * Color: none, red (default), green or blue + * Marker: Outline today graphically. Values: none (default), circle, rect(angle) + * Mrk.Color: Circle/rectangle color: red (default), green or blue + * Mrk.Size: Circle/rectangle thickness in pixel: min:1, max: 10, default:3 +* < Cancel: Exit and no change. Nevertheless missing default settings and superflous settings will be removed and saved. diff --git a/apps/timecal/metadata.json b/apps/timecal/metadata.json index 3237dd08a..4dd8a8ca0 100644 --- a/apps/timecal/metadata.json +++ b/apps/timecal/metadata.json @@ -1,13 +1,16 @@ { "id": "timecal", "name": "TimeCal", "shortName":"TimeCal", + "version":"0.04", + "description": "TimeCal shows the date/time along with a 3 week calendar", "icon": "icon.png", - "version":"0.01", - "description": "TimeCal shows the Time along with a 3 week calendar", - "tags": "clock", "type": "clock", + "tags": "clock,calendar", "supports":["BANGLEJS2"], + "readme": "README.md", + "allow_emulator":true, "storage": [ - {"name":"timecal.app.js","url":"timecal.app.js"} + {"name":"timecal.app.js","url":"timecal.app.js"}, + {"name":"timecal.settings.js","url":"timecal.settings.js"} ] } diff --git a/apps/timecal/testing/timecal.app.test.js b/apps/timecal/testing/timecal.app.test.js new file mode 100644 index 000000000..e41f3d848 --- /dev/null +++ b/apps/timecal/testing/timecal.app.test.js @@ -0,0 +1,798 @@ +//Clock renders date, time and pre,current,next week calender view +class TimeCalClock{ + DATE_FONT_SIZE(){ return 20; } + TIME_FONT_SIZE(){ return 40; } + + /** + * @param{Date} date optional the date (e.g. for testing) + * @param{Settings} settings optional settings to use e.g. for testing + */ + constructor(date, settings){ + if (date) + this.date=date; + + if (settings) + this._settings = settings; + else + this._settings = require("Storage").readJSON("timecal.settings.json", 1) || {}; + + const defaults = { + shwDate:1, //0:none, 1:locale, 2:month, 3:monthshort.year #week + + wdStrt:0, //identical to getDay() 0->Su, 1->Mo, ... //Issue #1154: weekstart So/Mo, -1 for use today + + tdyNumClr:3, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E + tdyMrkr:0, //0:none, 1:circle, 2:rectangle, 3:filled + tdyMrkClr:2, //1:red=#E00, 2:green=#0E0, 3:blue=#00E + tdyMrkPxl:3, //px + + suClr:1, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E + //phColor:"#E00", //public holiday + + calBrdr:false + }; + for (const k in this._settings) if (!defaults.hasOwnProperty(k)) delete this._settings[k]; //remove invalid settings + for (const k in defaults) if(!this._settings.hasOwnProperty(k)) this._settings[k] = defaults[k]; //assign missing defaults + + g.clear(); + Bangle.setUI("clock"); + Bangle.loadWidgets(); + Bangle.drawWidgets(); + + this.centerX = Bangle.appRect.w/2; + this.nrgb = [g.theme.fg, "#E00", "#0E0", "#00E"]; //fg, r ,g , b + + this.ABR_DAY=[]; + if (require("locale") && require("locale").dow) + for (let d=0; d<=6; d++) { + var refDay=new Date(); + refDay.setFullYear(1972); + refDay.setMonth(0); + refDay.setDate(2+d); + this.ABR_DAY.push(require("locale").dow(refDay)); + + } + else + this.ABR_DAY=["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + } + + /** + * @returns {Object} current settings object + */ + settings(){ + return this._settings; + } + + + /* + * Run forest run + **/ + draw(){ + this.drawTime(); + + if (this.TZOffset===undefined || this.TZOffset!==d.getTimezoneOffset()) + this.drawDateAndCal(); + } + + /** + * draw given or current time from date + * overwatch timezone changes + * schedules itself to update + */ + drawTime(){ + d=this.date ? this.date : new Date(); + const Y=Bangle.appRect.y+this.DATE_FONT_SIZE()+10; + + d=d?d :new Date(); + + g.setFontAlign(0, -1).setFont("Vector", this.TIME_FONT_SIZE()).setColor(g.theme.fg) + .clearRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+this.TIME_FONT_SIZE()-7) + .drawString(("0" + require("locale").time(d, 1)).slice(-5), this.centerX, Y, true); + //.drawRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+this.TIME_FONT_SIZE()-7); //DEV-Option + + setTimeout(this.draw.bind(this), 60000-(d.getSeconds()*1000)-d.getMilliseconds()); + } + + /** + * draws given date and cal + * @param{Date} d provide date or uses today + */ + drawDateAndCal(){ + d=this.date ? this.date : new Date(); + + this.TZOffset=d.getTimezoneOffset(); + this.drawDate(); + this.drawCal(); + + if (this.tOutD) //abort exisiting + clearTimeout(this.tOutD); + this.tOutD=setTimeout(this.drawDateAndCal.bind(this), 86400000-(d.getHours()*24*60*1000)-(d.getMinutes()*60*1000)-d.getSeconds()-d.getMilliseconds()); + } + + /** + * draws given date as defiend in settings + */ + drawDate(){ + d=this.date ? this.date : new Date(); + + const FONT_SIZE=20; + const Y=Bangle.appRect.y; + var render=false; + var dateStr = ""; + if (this.settings().shwDate>0) { //skip if exactly -none + const dateSttngs = ["","l","M","m.Y #W"]; + for (let c of dateSttngs[this.settings().shwDate]) { //add part as configured + switch (c){ + case "l":{ //locale + render=true; + dateStr+=require("locale").date(d,1); + break; + } + case "m":{ //month e.g. Jan. + render=true; + dateStr+=require("locale").month(d,1); + break; + } + case "M":{ //month e.g. January + render=true; + dateStr+=require("locale").month(d,0); + break; + } + case "y":{ //year e.g. 22 + render=true; + dateStr+=d.getFullYear().slice(-2); + break; + } + case "Y":{ //year e.g. 2022 + render=true; + dateStr+=d.getFullYear(); + break; + } + case "w":{ //week e.g. #2 + dateStr+=(this.ISO8601calWeek(d)); + break; + } + case "W":{ //week e.g. #02 + dateStr+=("0"+this.ISO8601calWeek(d)).slice(-2); + break; + } + default: //append c + dateStr+=c; + render=dateStr.length>0; + break; //noop + } + } + } + if (render){ + g.setFont("Vector", FONT_SIZE).setColor(g.theme.fg).setFontAlign(0, -1).clearRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+FONT_SIZE-3).drawString(dateStr,this.centerX,Y); + } + //g.drawRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+FONT_SIZE-3); //DEV-Option + } + + /** + * draws calender week view (-1,0,1) for given date + */ + drawCal(){ + d=this.date ? this.date : new Date(); + + const DAY_NAME_FONT_SIZE=10; + const CAL_Y=Bangle.appRect.y+this.DATE_FONT_SIZE()+10+this.TIME_FONT_SIZE()+3; + const CAL_AREA_H=Bangle.appRect.h-CAL_Y+24; //+24: top widegtes only + const CELL_W=Bangle.appRect.w/7; //cell width + const CELL_H=(CAL_AREA_H-DAY_NAME_FONT_SIZE)/3; //cell heigth + const DAY_NUM_FONT_SIZE=Math.min(CELL_H-1,15); //size down, max 15 + + g.setFont("Vector", DAY_NAME_FONT_SIZE).setColor(g.theme.fg).setFontAlign(-1, -1).clearRect(Bangle.appRect.x, CAL_Y, Bangle.appRect.x2, CAL_Y+CAL_AREA_H); + + //draw grid & Headline + const dNames = this.ABR_DAY.map((a) => a.length<=2 ? a : a.substr(0, 2)); //force shrt 2 + for(var dNo=0; dNo=0 ? (dNo+this.settings().wdStrt)%7 : (dNo+d.getDay()+4)%7; + const dName=dNames[dIdx]; + if(dNo>0) + g.drawLine(dNo*CELL_W, CAL_Y, dNo*CELL_W, CAL_Y+CAL_AREA_H-1); + + if (dIdx==0) g.setColor(this.nrgb[this.settings().suClr]); //sunday maybe colorize txt + g.drawString(dName, dNo*CELL_W+(CELL_W-g.stringWidth(dName))/2+2, CAL_Y+1).setColor(g.theme.fg); + } + + var nextY=CAL_Y+DAY_NAME_FONT_SIZE; + + for(i=0; i<3; i++){ + const y=nextY+i*CELL_H; + g.drawLine(Bangle.appRect.x, y, Bangle.appRect.x2, y); + } + + g.setFont("Vector", DAY_NUM_FONT_SIZE); + + //write days + const tdyDate=d.getDate(); + const days=this.settings().wdStrt>=0 ? 7+((7+d.getDay()-this.settings().wdStrt)%7) : 10;//start day (week before=7 days + days in this week realtive to week start) or fixed 7+3 days + var rD=new Date(d.getTime()); + rD.setDate(rD.getDate()-days); + var rDate=rD.getDate(); + for(var y=0; y<3; y++){ + for(var x=0; x", + cases: [ + { + value: "required,", + beforeTxt: "optional,", + beforeExpression: "optional,", + afterText: "optional,", + afterExpression: "optional," + } + ], + constructorParams: ["optional: ","|TEST_SETTINGS|","..."], //TEST_SETTINGS will be replcaed with each current {setting: case} + functionNames: ["required, ", "..."], + functionParams: ["optional: ","|TEST_SETTINGS|","..."] + }; + } + + constructor(data){ + + this._validate(data); + + this.setting = data.setting; + this.cases = data.cases.map((entry) => { + return { + value: entry.value, + beforeTxt: entry.beforeTxt||"", + beforeExpression: entry.beforeExpression||true, + afterTxt: entry.afterTxt||"", + afterExpression: entry.afterExpression||true + }; + }); + this.constructorParams = data.constructorParams; + this.functionNames = data.functionNames; + this.functionParams = data.functionParams; + } + + /** + * validates the given data config + */ + _validate(data){ + //validate given config + if (!data.setting) throw new EmptyMandatoryError("setting", data, this.TEST_SETTING_SAMPLE()); + if (!(data.cases instanceof Array) || data.cases.length==0) throw new EmptyMandatoryError("cases", data, this.TEST_SETTING_SAMPLE()); + if (!(data.functionNames instanceof Array) || data.functionNames==0) throw new EmptyMandatoryError("functionNames", data, this.TEST_SETTING_SAMPLE()); + + data.cases.forEach((entry,idx) => { + if (entry.value === undefined) throw new EmptyMandatoryError("cases["+idx+"].value", entry, this.TEST_SETTING_SAMPLE()); + }); + } +} + +/*************************************************************************/ + +/** + * Testing a Bangle object + */ +class BangleTestRunner{ + /** + * create for ObjClass + * @param {Class} objClass + * @param {LogSeverity} minSeverity to Log + */ + constructor(objClass, minSeverity){ + this.TESTCASE_MSG_BEFORE_TIMEOUT = 1000; //5s + this.TESTCASE_RUN_TIMEOUT = 1000; //5s + this.TESTCASE_MSG_AFTER_TIMEOUT = 1000; //5s + + this.oClass = objClass; + this.minSvrty = minSeverity; + this.tests = []; + + this.currentCaseNum = this.currentTestNum = this.currentTest = this.currentCase = undefined; + } + + /** + * add a Setting Test, return instance for chaining + * @param {TestSetting} + */ + addTestSettings(sttngs) { + this.tests.push(new TestSetting(sttngs)); + return this; + } + + /** + * Test execution of all tests + */ + execute() { + this._init(); + while (this._nextTest()) { + this._beforeTest(); + while (this._nextCase()) { + this._beforeCase(); + this._runCase(); + this._afterCase(); + } + this._afterTest(); + this._firstCase(); + } + this._exit(); + } + + /** + * global prepare - before all test + */ + _init() { + console.log(this._nowTime(), ">>init"); + this.currentTestNum=-1; + this.currentCaseNum=-1; + } + + /** + * before each test + */ + _beforeTest() { + console.log(this._nowTime(), ">>test #" + this.currentTestNum); + } + + /** + * befor each testcase + */ + _beforeCase() { + console.log(this.currentTest); + console.log(this._nowTime(), ">>case #" + this.currentTestNum + "." + this.currentCaseNum + "/" + (this.currentTest.cases.length-1)); + if (this.currentTest instanceof TestSetting) + console.log(this.currentTest.setting+"="+this.currentCase.value+"/n"+(this.currentCase.beforeTxt ? "#"+this.currentCase.beforeTxt : "")); + } + + /** + * testcase runner + */ + _runCase() { + console.log(this._nowTime(), ">>running..."); + var returns = []; + this.currentTest.functionNames.forEach((fName) => { + var settings={}; settings[this.currentTest.setting] = this.currentCase.value; + var cParams = this.currentTest.constructorParams||[]; + cParams = cParams.map((v) => (v && v instanceof String && v==="|TEST_SETTINGS|") ? settings : v);//replace settings in call params + var fParams = this.currentTest.functionParams||[]; + fParams = fParams.map((v) => (v && v instanceof String && v==="|TEST_SETTINGS|") ? settings : v);//replace settings in call params + + var creatorFunc = new Function("console.log('Constructor params:', arguments); return new " + this.oClass + "(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7],arguments[8],arguments[9])"); //prepare spwan arguments[0],arguments[1] + let instance = creatorFunc.call(this.oClass, cParams[0], cParams[1], cParams[2], cParams[3], cParams[4], cParams[5], cParams[6], cParams[7], cParams[8], cParams[9]); //spwan + + console.log(">>"+this.oClass+"["+fName+"]()"); + + console.log('Instance:', instance); + console.log('Function params:', fParams); + returns.push(instance[fName](fParams[0], fParams[1], fParams[2], fParams[3], fParams[4], fParams[5], fParams[6], fParams[7], fParams[8], fParams[9])); //run method and store result + g.dump(); + console.log("<<"+this.oClass+"["+fName+"]()"); + }); + console.log(this._nowTime(), "<<...running"); + } + + /** + * after each testcase + */ + _afterCase() { + if (this.currentTest instanceof TestSetting) + if (this.currentCase.afterTxt.length>0) + console.log("++EXPECTED:" + this.currentCase.afterTxt + "EXPECTED++"); + console.log(this._nowTime(), "< setTimeout(resolve, sec)); + } + + _waits(sec) { + this._delay(1).then(); + } + + _log() { + + } + + _nextTest() { + if (this.currentTestNum>=-1 && (this.currentTestNum+1)=-1 && (this.currentCaseNum+1)Su, 1->Mo, ... //Issue #1154: weekstart So/Mo, -1 for use today + + tdyNumClr:3, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E + tdyMrkr:0, //0:none, 1:circle, 2:rectangle, 3:filled + tdyMrkClr:2, //1:red=#E00, 2:green=#0E0, 3:blue=#00E + tdyMrkPxl:3, //px + + suClr:1, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E + //phColor:"#E00", //public holiday + + calBrdr:false + }; + for (const k in this._settings) if (!defaults.hasOwnProperty(k)) delete this._settings[k]; //remove invalid settings + for (const k in defaults) if(!this._settings.hasOwnProperty(k)) this._settings[k] = defaults[k]; //assign missing defaults + + g.clear(); + Bangle.setUI("clock"); + Bangle.loadWidgets(); + Bangle.drawWidgets(); + + this.centerX = Bangle.appRect.w/2; + this.nrgb = [g.theme.fg, "#E00", "#0E0", "#00E"]; //fg, r ,g , b + + this.ABR_DAY=[]; + if (require("locale") && require("locale").dow) + for (let d=0; d<=6; d++) { + var refDay=new Date(); + refDay.setFullYear(1972); + refDay.setMonth(0); + refDay.setDate(2+d); + this.ABR_DAY.push(require("locale").dow(refDay)); -function drawCal(d){ - var calStart = 101; - var cellSize = g.getWidth() / 7; - var halfSize = cellSize / 2; - g.clearRect(0,calStart,g.getWidth(),g.getHeight()); - g.drawLine(0,calStart,g.getWidth(),calStart); - var days = ["Mo","Tu","We","Th","Fr","Sa","Su"]; - g.setFont("Vector",10); - g.setColor(fontColor); - g.setFontAlign(-1,-1,0); - for(var i = 0; i < days.length;i++){ - g.drawString(days[i],i*cellSize+5,calStart -11); - if(i!=0){ - g.drawLine(i*cellSize,calStart,i*cellSize,g.getHeight()); - } - } - var cellHeight = (g.getHeight() -calStart ) / 3; - for(var i = 0;i < 3;i++){ - var starty = calStart + i * cellHeight; - g.drawLine(0,starty,g.getWidth(),starty); - } - - g.setFont("Vector",15); - - var dayOfWeek = d.getDay(); - var dayRem = d.getDay() - 1; - if(dayRem <0){ - dayRem = 0; - } - - var start = new Date(); - start.setDate(start.getDate()-(7+dayRem)); - g.setFontAlign(0,-1,0); - for (var y = 0;y < 3; y++){ - for(var x = 0;x < 7; x++){ - if(start.getDate() === d.getDate()){ - g.setColor(accentColor); - }else{ - g.setColor(fontColor); } - g.drawString(start.getDate(),x*cellSize +(cellSize / 2) + 2,calStart+(cellHeight*y) + 5); - start.setDate(start.getDate()+1); + else + this.ABR_DAY=["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + } + + /** + * @returns {Object} current settings object + */ + settings(){ + return this._settings; + } + + + /* + * Run forest run + **/ + draw(){ + this.drawTime(); + + if (this.TZOffset===undefined || this.TZOffset!==d.getTimezoneOffset()) + this.drawDateAndCal(); } + + /** + * draw given or current time from date + * overwatch timezone changes + * schedules itself to update + */ + drawTime(){ + d=this.date ? this.date : new Date(); + const Y=Bangle.appRect.y+this.DATE_FONT_SIZE()+10; + + d=d?d :new Date(); + + g.setFontAlign(0, -1).setFont("Vector", this.TIME_FONT_SIZE()).setColor(g.theme.fg) + .clearRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+this.TIME_FONT_SIZE()-7) + .drawString(("0" + require("locale").time(d, 1)).slice(-5), this.centerX, Y, true); + //.drawRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+this.TIME_FONT_SIZE()-7); //DEV-Option + + setTimeout(this.draw.bind(this), 60000-(d.getSeconds()*1000)-d.getMilliseconds()); + } + + /** + * draws given date and cal + * @param{Date} d provide date or uses today + */ + drawDateAndCal(){ + d=this.date ? this.date : new Date(); + + this.TZOffset=d.getTimezoneOffset(); + this.drawDate(); + this.drawCal(); + + if (this.tOutD) //abort exisiting + clearTimeout(this.tOutD); + this.tOutD=setTimeout(this.drawDateAndCal.bind(this), 86400000-(d.getHours()*24*60*1000)-(d.getMinutes()*60*1000)-d.getSeconds()-d.getMilliseconds()); + } + + /** + * draws given date as defiend in settings + */ + drawDate(){ + d=this.date ? this.date : new Date(); + + const FONT_SIZE=20; + const Y=Bangle.appRect.y; + var render=false; + var dateStr = ""; + if (this.settings().shwDate>0) { //skip if exactly -none + const dateSttngs = ["","l","M","m.Y #W"]; + for (let c of dateSttngs[this.settings().shwDate]) { //add part as configured + switch (c){ + case "l":{ //locale + render=true; + dateStr+=require("locale").date(d,1); + break; + } + case "m":{ //month e.g. Jan. + render=true; + dateStr+=require("locale").month(d,1); + break; + } + case "M":{ //month e.g. January + render=true; + dateStr+=require("locale").month(d,0); + break; + } + case "y":{ //year e.g. 22 + render=true; + dateStr+=d.getFullYear().slice(-2); + break; + } + case "Y":{ //year e.g. 2022 + render=true; + dateStr+=d.getFullYear(); + break; + } + case "w":{ //week e.g. #2 + dateStr+=(this.ISO8601calWeek(d)); + break; + } + case "W":{ //week e.g. #02 + dateStr+=("0"+this.ISO8601calWeek(d)).slice(-2); + break; + } + default: //append c + dateStr+=c; + render=dateStr.length>0; + break; //noop + } + } + } + if (render){ + g.setFont("Vector", FONT_SIZE).setColor(g.theme.fg).setFontAlign(0, -1).clearRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+FONT_SIZE-3).drawString(dateStr,this.centerX,Y); + } + //g.drawRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+FONT_SIZE-3); //DEV-Option + } + + /** + * draws calender week view (-1,0,1) for given date + */ + drawCal(){ + d=this.date ? this.date : new Date(); + + const DAY_NAME_FONT_SIZE=10; + const CAL_Y=Bangle.appRect.y+this.DATE_FONT_SIZE()+10+this.TIME_FONT_SIZE()+3; + const CAL_AREA_H=Bangle.appRect.h-CAL_Y+24; //+24: top widegtes only + const CELL_W=Bangle.appRect.w/7; //cell width + const CELL_H=(CAL_AREA_H-DAY_NAME_FONT_SIZE)/3; //cell heigth + const DAY_NUM_FONT_SIZE=Math.min(CELL_H-1,15); //size down, max 15 + + g.setFont("Vector", DAY_NAME_FONT_SIZE).setColor(g.theme.fg).setFontAlign(-1, -1).clearRect(Bangle.appRect.x, CAL_Y, Bangle.appRect.x2, CAL_Y+CAL_AREA_H); + + //draw grid & Headline + const dNames = this.ABR_DAY.map((a) => a.length<=2 ? a : a.substr(0, 2)); //force shrt 2 + for(var dNo=0; dNo=0 ? (dNo+this.settings().wdStrt)%7 : (dNo+d.getDay()+4)%7; + const dName=dNames[dIdx]; + if(dNo>0) + g.drawLine(dNo*CELL_W, CAL_Y, dNo*CELL_W, CAL_Y+CAL_AREA_H-1); + + if (dIdx==0) g.setColor(this.nrgb[this.settings().suClr]); //sunday maybe colorize txt + g.drawString(dName, dNo*CELL_W+(CELL_W-g.stringWidth(dName))/2+2, CAL_Y+1).setColor(g.theme.fg); + } + + var nextY=CAL_Y+DAY_NAME_FONT_SIZE; + + for(i=0; i<3; i++){ + const y=nextY+i*CELL_H; + g.drawLine(Bangle.appRect.x, y, Bangle.appRect.x2, y); + } + + g.setFont("Vector", DAY_NUM_FONT_SIZE); + + //write days + const tdyDate=d.getDate(); + const days=this.settings().wdStrt>=0 ? 7+((7+d.getDay()-this.settings().wdStrt)%7) : 10;//start day (week before=7 days + days in this week realtive to week start) or fixed 7+3 days + var rD=new Date(d.getTime()); + rD.setDate(rD.getDate()-days); + var rDate=rD.getDate(); + for(var y=0; y<3; y++){ + for(var x=0; xSu, 1->Mo, ... //Issue #1154: weekstart So/Mo, -1 for use today + + tdyNumClr:3, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E + tdyMrkr:0, //0:none, 1:circle, 2:rectangle, 3:filled + tdyMrkClr:2, //1:red=#E00, 2:green=#0E0, 3:blue=#00E + tdyMrkPxl:3, //px + + suClr:1, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E + //phColor:"#E00", //public holiday + + calBrdr:false + }; + validSttngs = require("Storage").readJSON("timecal.validSttngs.json", 1) || {}; + for (const k in validSttngs) if (!DEFAULTS.hasOwnProperty(k)) delete this.validSttngs[k]; //remove invalid settings + for (const k in DEFAULTS) if(!validSttngs.hasOwnProperty(k)) validSttngs[k] = validSttngs[k]; //assign missing defaults + + var changedSttngs = Object.assign({}, validSttngs); + + var saveExitSettings = () => { + require('Storage').writeJSON(FILE, changedSttngs); + exit(); + }; + + var cancelExitSettings = () => { + require('Storage').writeJSON(FILE, validSttngs); + exit(); + }; + + var showMainMenu = () => { + E.showMenu({ + "": { + "title": "TimeCal "+ /*LANG*/"settings" + }, + /*LANG*/"< Save": () => saveExitSettings(), + /*LANG*/"Show date": { + value: validSttngs.shwDate, + min: 0, max: 3, + format: v => [/*LANG*/"none", /*LANG*/"locale", /*LANG*/"M", /*LANG*/"m.Y #W"][v], + onchange: v => validSttngs.shwDate = v + }, + /*LANG*/"Start wday": { + value: validSttngs.wdStrt, + min: -1, max: 6, + format: v => v>=0 ? ABR_DAY[v] : /*LANG*/"today", + onchange: v => validSttngs.wdStrt = v + }, + /*LANG*/"Su color": { + value: validSttngs.suClr, + min: 0, max: 3, + format: v => [/*LANG*/"none", /*LANG*/"red", /*LANG*/"green", /*LANG*/"blue"][v], + onchange: v => validSttngs.suClr = v + }, + /*LANG*/"Border": { + value: validSttngs.calBrdr, + format: v => v ? /*LANG*/"show" : /*LANG*/"none", + onchange: v => validSttngs.calBrdr = v + }, + /*LANG*/"Today settings": () => { + showTodayMenu(); + }, + /*LANG*/"< Cancel": () => cancelExitSettings() + }); + }; + + var showTodayMenu = () => { + E.showMenu({ + "": { + "title": /*LANG*/"Today settings" + }, + "< Back": () => showMainMenu(), + /*LANG*/"Color": { + value: validSttngs.tdyNumClr, + min: 0, max: 3, + format: v => [/*LANG*/"none", /*LANG*/"red", /*LANG*/"green", /*LANG*/"blue"][v], + onchange: v => validSttngs.tdyNumClr = v + }, + /*LANG*/"Marker": { + value: validSttngs.tdyMrkr, + min: 0, max: 3, + format: v => [/*LANG*/"none", /*LANG*/"circle", /*LANG*/"rectangle", /*LANG*/"filled"][v], + onchange: v => validSttngs.tdyMrkr = v + }, + /*LANG*/"Mrk.Color": { + value: validSttngs.tdyMrkClr, + min: 0, max: 2, + format: v => [/*LANG*/"red", /*LANG*/"green", /*LANG*/"blue"][v], + onchange: v => validSttngs.tdyMrkClr = v + }, + /*LANG*/"Mrk.Size": { + value: validSttngs.tdyMrkPxl, + min: 1, max: 10, + format: v => v+"px", + onchange: v => validSttngs.tdyMrkPxl = v + }, + /*LANG*/"< Cancel": () => cancelExitSettings() + }); + }; + + showMainMenu(); +});