From dab3c045db660ae4f7837312e713fcc206dd113a Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 26 May 2022 19:13:40 +0200 Subject: [PATCH 01/41] Use prompt with dimiss and pause --- apps/widbaroalarm/ChangeLog | 1 + apps/widbaroalarm/metadata.json | 2 +- apps/widbaroalarm/settings.js | 20 +++++++++++++++ apps/widbaroalarm/widget.js | 44 ++++++++++++++++++++------------- 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/apps/widbaroalarm/ChangeLog b/apps/widbaroalarm/ChangeLog index 5786741c7..b61e1691b 100644 --- a/apps/widbaroalarm/ChangeLog +++ b/apps/widbaroalarm/ChangeLog @@ -1,3 +1,4 @@ 0.01: Initial version 0.02: Do not warn multiple times for the same exceedance 0.03: Fix crash +0.04: Use Prompt with dismiss and pause diff --git a/apps/widbaroalarm/metadata.json b/apps/widbaroalarm/metadata.json index 134f03623..9f5cffcfe 100644 --- a/apps/widbaroalarm/metadata.json +++ b/apps/widbaroalarm/metadata.json @@ -2,7 +2,7 @@ "id": "widbaroalarm", "name": "Barometer Alarm Widget", "shortName": "Barometer Alarm", - "version": "0.03", + "version": "0.04", "description": "A widget that can alarm on when the pressure reaches defined thresholds.", "icon": "widget.png", "type": "widget", diff --git a/apps/widbaroalarm/settings.js b/apps/widbaroalarm/settings.js index bea6319d1..faece90a2 100644 --- a/apps/widbaroalarm/settings.js +++ b/apps/widbaroalarm/settings.js @@ -87,6 +87,26 @@ }, onchange: x => save('buzz', x) }, + 'Dismiss delay': { + value: settings.dismissDelayMin, + min: 5, max: 60, + onchange: v => { + save('dismissDelayMin', x) + }, + format: x => { + return x + " min"; + } + }, + 'Pause delay': { + value: settings.pauseDelayMin, + min: 30, max: 240, + onchange: v => { + save('pauseDelayMin', x) + }, + format: x => { + return x + " min"; + } + }, }; E.showMenu(menu); } diff --git a/apps/widbaroalarm/widget.js b/apps/widbaroalarm/widget.js index 2745db8ad..c2a9e1c54 100644 --- a/apps/widbaroalarm/widget.js +++ b/apps/widbaroalarm/widget.js @@ -32,13 +32,21 @@ let history3 = storage.readJSON(LOG_FILE, true) || []; // history of recent 3 hours - function showAlarm(body, title) { + function showAlarm(body, title, key) { if (body == undefined) return; - require("notify").show({ + E.showPrompt(body, { title: title || "Pressure", - body: body, - icon: require("heatshrink").decompress(atob("jEY4cA///gH4/++mkK30kiWC4H8x3BGDmSGgYDCgmSoEAg3bsAIDpAIFkmSpMAm3btgIFDQwIGNQpTYkAIJwAHEgMoCA0JgMEyBnBCAW3KoQQDhu3oAIH5JnDBAW24IIBEYm2EYwACBCIACA")) + buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 } + }).then(function (v) { + const tsNow = Math.round(Date.now() / 1000); // seconds + + if (v == 2) { + saveSetting(key, tsNow + 60 * settings('dismissDelayMin')); + } + if (v == 3) { + saveSetting(key, tsNow + 60 * settings('pauseDelayMin')); + } }); if (setting("buzz") && @@ -48,8 +56,10 @@ } - function didWeAlreadyWarn(key) { - return setting(key) == undefined || setting(key) > 0; + function doWeNeedToWarn(key) { + const tsNow = Math.round(Date.now() / 1000); // seconds + + return setting(key) == 0 || setting(key) < tsNow; } function checkForAlarms(pressure) { @@ -77,7 +87,7 @@ if (setting("lowalarm")) { // Is below the alarm threshold? if (pressure <= setting("min")) { - if (!didWeAlreadyWarn("lastLowWarningTs")) { + if (!doWeNeedToWarn("lastLowWarningTs")) { showAlarm("Pressure low: " + Math.round(pressure) + " hPa"); saveSetting("lastLowWarningTs", ts); alreadyWarned = true; @@ -92,7 +102,7 @@ if (setting("highalarm")) { // Is above the alarm threshold? if (pressure >= setting("max")) { - if (!didWeAlreadyWarn("lastHighWarningTs")) { + if (!doWeNeedToWarn("lastHighWarningTs")) { showAlarm("Pressure high: " + Math.round(pressure) + " hPa"); saveSetting("lastHighWarningTs", ts); alreadyWarned = true; @@ -122,7 +132,7 @@ // drop alarm if (drop3halarm > 0 && oldestPressure > pressure) { if (Math.abs(diff) > drop3halarm) { - if (!didWeAlreadyWarn("lastDropWarningTs")) { + if (!doWeNeedToWarn("lastDropWarningTs")) { showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " + Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure drop"); saveSetting("lastDropWarningTs", ts); @@ -137,7 +147,7 @@ // raise alarm if (raise3halarm > 0 && oldestPressure < pressure) { if (Math.abs(diff) > raise3halarm) { - if (!didWeAlreadyWarn("lastRaiseWarningTs")) { + if (!doWeNeedToWarn("lastRaiseWarningTs")) { showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " + Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure raise"); saveSetting("lastRaiseWarningTs", ts); @@ -176,7 +186,7 @@ /* turn on barometer power - take 5 measurements + take `numberOfMeasurements` measurements sort the results take the middle one (median) turn off barometer power @@ -186,11 +196,11 @@ setTimeout(function() { currentPressures = []; - Bangle.getPressure().then(baroHandler); - Bangle.getPressure().then(baroHandler); - Bangle.getPressure().then(baroHandler); - Bangle.getPressure().then(baroHandler); - Bangle.getPressure().then(baroHandler); + const numberOfMeasurements = 5; + + for (let i = 0; i < numberOfMeasurements; i++) { + Bangle.getPressure().then(baroHandler); + } setTimeout(function() { Bangle.setBarometerPower(false, "widbaroalarm"); @@ -198,7 +208,7 @@ currentPressures.sort(); // take median value - medianPressure = currentPressures[3]; + medianPressure = currentPressures[Math.round(numberOfMeasurements / 2) + 1]; checkForAlarms(medianPressure); }, 1000); }, 500); From 71d9907657c820da3ad94bd5219b18777215d6b3 Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 26 May 2022 19:20:15 +0200 Subject: [PATCH 02/41] Fix settings --- apps/widbaroalarm/default.json | 4 +++- apps/widbaroalarm/settings.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/widbaroalarm/default.json b/apps/widbaroalarm/default.json index 3d81baa81..696c70819 100644 --- a/apps/widbaroalarm/default.json +++ b/apps/widbaroalarm/default.json @@ -7,5 +7,7 @@ "drop3halarm": 2, "raise3halarm": 0, "show": true, - "interval": 15 + "interval": 15, + "dismissDelayMin": 15, + "pauseDelayMin": 60 } diff --git a/apps/widbaroalarm/settings.js b/apps/widbaroalarm/settings.js index faece90a2..ee8ce82c2 100644 --- a/apps/widbaroalarm/settings.js +++ b/apps/widbaroalarm/settings.js @@ -91,7 +91,7 @@ value: settings.dismissDelayMin, min: 5, max: 60, onchange: v => { - save('dismissDelayMin', x) + save('dismissDelayMin', v) }, format: x => { return x + " min"; @@ -101,7 +101,7 @@ value: settings.pauseDelayMin, min: 30, max: 240, onchange: v => { - save('pauseDelayMin', x) + save('pauseDelayMin', v) }, format: x => { return x + " min"; From a29eeaca510a3386fd9794316a051d4fd0b6977e Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 26 May 2022 19:25:29 +0200 Subject: [PATCH 03/41] Remove dependency --- apps/widbaroalarm/metadata.json | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/widbaroalarm/metadata.json b/apps/widbaroalarm/metadata.json index 9f5cffcfe..41b8d3e17 100644 --- a/apps/widbaroalarm/metadata.json +++ b/apps/widbaroalarm/metadata.json @@ -8,7 +8,6 @@ "type": "widget", "tags": "tool,barometer", "supports": ["BANGLEJS2"], - "dependencies": {"notify":"type"}, "readme": "README.md", "storage": [ {"name":"widbaroalarm.wid.js","url":"widget.js"}, From 55a346272aaeb317a32f9d33f010f4912bbf861b Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 26 May 2022 19:31:48 +0200 Subject: [PATCH 04/41] Update README --- apps/widbaroalarm/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/widbaroalarm/README.md b/apps/widbaroalarm/README.md index fdc239170..a74d89546 100644 --- a/apps/widbaroalarm/README.md +++ b/apps/widbaroalarm/README.md @@ -15,7 +15,8 @@ Get a notification when the pressure reaches defined thresholds. 0 to disable this alarm. * Show widget: Enable/disable widget visibility * Buzz on alarm: Enable/disable buzzer on alarm - +* Dismiss delay: Delay added before the next alert if the alert is dismissed. From 5 to 60 min +* Pause delay: Same as Dismiss delay but longer (usefull for meetings and such). From 30 to 240 min ## Widget The widget shows two rows: pressure value of last measurement and pressure average of the the last three hours. From 02fc7af75085581c6de5cc67c515b445331398f2 Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 26 May 2022 20:07:15 +0200 Subject: [PATCH 05/41] Fix warning --- apps/widbaroalarm/widget.js | 43 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/apps/widbaroalarm/widget.js b/apps/widbaroalarm/widget.js index c2a9e1c54..721d0bbb8 100644 --- a/apps/widbaroalarm/widget.js +++ b/apps/widbaroalarm/widget.js @@ -32,33 +32,38 @@ let history3 = storage.readJSON(LOG_FILE, true) || []; // history of recent 3 hours - function showAlarm(body, title, key) { + function showAlarm(body, key) { if (body == undefined) return; E.showPrompt(body, { - title: title || "Pressure", + title: "Pressure alarm", buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 } }).then(function (v) { const tsNow = Math.round(Date.now() / 1000); // seconds + if (v == 1) { + saveSetting(key, tsNow); + } if (v == 2) { - saveSetting(key, tsNow + 60 * settings('dismissDelayMin')); + saveSetting(key, tsNow + 60 * setting('dismissDelayMin')); } if (v == 3) { - saveSetting(key, tsNow + 60 * settings('pauseDelayMin')); + saveSetting(key, tsNow + 60 * setting('pauseDelayMin')); } + load(); }); if (setting("buzz") && !(storage.readJSON('setting.json', 1) || {}).quiet) { Bangle.buzz(); } + + setTimeout(load, 20000); } function doWeNeedToWarn(key) { const tsNow = Math.round(Date.now() / 1000); // seconds - return setting(key) == 0 || setting(key) < tsNow; } @@ -88,8 +93,7 @@ // Is below the alarm threshold? if (pressure <= setting("min")) { if (!doWeNeedToWarn("lastLowWarningTs")) { - showAlarm("Pressure low: " + Math.round(pressure) + " hPa"); - saveSetting("lastLowWarningTs", ts); + showAlarm("Pressure low: " + Math.round(pressure) + " hPa", "lastLowWarningTs"); alreadyWarned = true; } } else { @@ -102,9 +106,8 @@ if (setting("highalarm")) { // Is above the alarm threshold? if (pressure >= setting("max")) { - if (!doWeNeedToWarn("lastHighWarningTs")) { - showAlarm("Pressure high: " + Math.round(pressure) + " hPa"); - saveSetting("lastHighWarningTs", ts); + if (doWeNeedToWarn("lastHighWarningTs")) { + showAlarm("Pressure high: " + Math.round(pressure) + " hPa", "lastHighWarningTs"); alreadyWarned = true; } } else { @@ -132,10 +135,9 @@ // drop alarm if (drop3halarm > 0 && oldestPressure > pressure) { if (Math.abs(diff) > drop3halarm) { - if (!doWeNeedToWarn("lastDropWarningTs")) { + if (doWeNeedToWarn("lastDropWarningTs")) { showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " + - Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure drop"); - saveSetting("lastDropWarningTs", ts); + Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure drop", "lastDropWarningTs"); } } else { saveSetting("lastDropWarningTs", 0); @@ -147,10 +149,9 @@ // raise alarm if (raise3halarm > 0 && oldestPressure < pressure) { if (Math.abs(diff) > raise3halarm) { - if (!doWeNeedToWarn("lastRaiseWarningTs")) { + if (doWeNeedToWarn("lastRaiseWarningTs")) { showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " + - Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure raise"); - saveSetting("lastRaiseWarningTs", ts); + Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure raise", "lastRaiseWarningTs"); } } else { saveSetting("lastRaiseWarningTs", 0); @@ -186,7 +187,7 @@ /* turn on barometer power - take `numberOfMeasurements` measurements + take `numberOfMeasurements` measurements with a delay of 1000ms each sort the results take the middle one (median) turn off barometer power @@ -196,10 +197,12 @@ setTimeout(function() { currentPressures = []; - const numberOfMeasurements = 5; + const numberOfMeasurements = 7; for (let i = 0; i < numberOfMeasurements; i++) { - Bangle.getPressure().then(baroHandler); + setTimeout(function() { + Bangle.getPressure().then(baroHandler); + }, i * 1000); } setTimeout(function() { @@ -210,7 +213,7 @@ // take median value medianPressure = currentPressures[Math.round(numberOfMeasurements / 2) + 1]; checkForAlarms(medianPressure); - }, 1000); + }, numberOfMeasurements * 1000 + 500); }, 500); } From 5968df327cf8001cf8e70445131a0689ad0f2035 Mon Sep 17 00:00:00 2001 From: Marco H Date: Thu, 26 May 2022 20:15:05 +0200 Subject: [PATCH 06/41] Improve changelog --- apps/widbaroalarm/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/widbaroalarm/ChangeLog b/apps/widbaroalarm/ChangeLog index b61e1691b..0afe4bc8b 100644 --- a/apps/widbaroalarm/ChangeLog +++ b/apps/widbaroalarm/ChangeLog @@ -2,3 +2,4 @@ 0.02: Do not warn multiple times for the same exceedance 0.03: Fix crash 0.04: Use Prompt with dismiss and pause + Improve barometer value median calculation From 53cbb2bb328ce766a94af11438a8393ddb2750b4 Mon Sep 17 00:00:00 2001 From: deirdreobyrne Date: Fri, 10 Jun 2022 05:14:22 +0100 Subject: [PATCH 07/41] Proper fix for the race condition --- apps/bigdclock/ChangeLog | 1 + apps/bigdclock/bigdclock.app.js | 7 +++---- apps/bigdclock/metadata.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/bigdclock/ChangeLog b/apps/bigdclock/ChangeLog index 5407fee95..09cc978fb 100644 --- a/apps/bigdclock/ChangeLog +++ b/apps/bigdclock/ChangeLog @@ -2,3 +2,4 @@ 0.02: setTimeout bug fix; no leading zero on date; lightmode; 12 hour format; cleanup 0.03: Internationalisation; bug fix - battery icon responds promptly to charging state 0.04: bug fix +0.05: proper fix for the race condition in queueDraw() diff --git a/apps/bigdclock/bigdclock.app.js b/apps/bigdclock/bigdclock.app.js index 014aa92af..c013c6188 100644 --- a/apps/bigdclock/bigdclock.app.js +++ b/apps/bigdclock/bigdclock.app.js @@ -12,13 +12,12 @@ Graphics.prototype.setFontOpenSans = function(scale) { var drawTimeout; -// schedule a draw for the next minute -function queueDraw() { +function queueDraw(millis_now) { if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = setTimeout(function () { drawTimeout = undefined; draw(); - }, 60300 - (Date.now() % 60000)); // We aim for 300ms into the next minute to ensure we make it! + }, 60000 - (millis_now % 60000)); } function draw() { @@ -70,7 +69,7 @@ function draw() { // widget redraw Bangle.drawWidgets(); - queueDraw(); + queueDraw(date.getTime()); } Bangle.on('lcdPower', on => { diff --git a/apps/bigdclock/metadata.json b/apps/bigdclock/metadata.json index 769b05fcf..7359bcf20 100644 --- a/apps/bigdclock/metadata.json +++ b/apps/bigdclock/metadata.json @@ -1,7 +1,7 @@ { "id": "bigdclock", "name": "Big digit clock containing just the essentials", "shortName":"Big digit clk", - "version":"0.04", + "version":"0.05", "description": "A clock containing just the essentials, made as easy to read as possible for those of us that need glasses. It contains the time, the day-of-week, the day-of-month, and the current battery state-of-charge.", "icon": "bigdclock.png", "type": "clock", From 87387d546dc06dd9659895e5b546eee3473ff584 Mon Sep 17 00:00:00 2001 From: Dennis Kueper Date: Sat, 11 Jun 2022 15:16:18 +0200 Subject: [PATCH 08/41] Initial Release --- apps/hworldclock/ChangeLog | 1 + apps/hworldclock/README.md | 30 +++ apps/hworldclock/app.js | 266 ++++++++++++++++++++++ apps/hworldclock/app.png | Bin 0 -> 2867 bytes apps/hworldclock/custom.html | 76 +++++++ apps/hworldclock/hsuncalc.js | 298 +++++++++++++++++++++++++ apps/hworldclock/hworldclock-icon.js | 1 + apps/hworldclock/hworldclock.png | Bin 0 -> 3457 bytes apps/hworldclock/metadata.json | 21 ++ apps/hworldclock/screenshot_hworld.png | Bin 0 -> 3457 bytes 10 files changed, 693 insertions(+) create mode 100644 apps/hworldclock/ChangeLog create mode 100644 apps/hworldclock/README.md create mode 100644 apps/hworldclock/app.js create mode 100644 apps/hworldclock/app.png create mode 100644 apps/hworldclock/custom.html create mode 100644 apps/hworldclock/hsuncalc.js create mode 100644 apps/hworldclock/hworldclock-icon.js create mode 100644 apps/hworldclock/hworldclock.png create mode 100644 apps/hworldclock/metadata.json create mode 100644 apps/hworldclock/screenshot_hworld.png diff --git a/apps/hworldclock/ChangeLog b/apps/hworldclock/ChangeLog new file mode 100644 index 000000000..5b2a1edce --- /dev/null +++ b/apps/hworldclock/ChangeLog @@ -0,0 +1 @@ +0.15: Initial release - be patient as this is the first try :) diff --git a/apps/hworldclock/README.md b/apps/hworldclock/README.md new file mode 100644 index 000000000..40af71ee3 --- /dev/null +++ b/apps/hworldclock/README.md @@ -0,0 +1,30 @@ +# Hanks World Clock - See the time in four locations + +In addition to the main clock and date in your current location, you can add up to three other locations. Great for travel or remote working. +Additionally we show the sunset/sunrise and seconds for the current location and the day name is shown in your locale. + +![](hworldclock.png) + +## Usage + +Provide names and the UTC offsets for up to three other timezones in the app store. These are stored in a json file on your watch. UTC offsets can be decimal (e.g., 5.5 for India). + +The clock does not handle summer time / daylight saving time changes automatically. If one of your three locations changes its UTC offset, you can simply change the setting in the app store and update. Currently the clock only supports 24 hour time format for the additional time zones. + + +## Requests + +Please use [the Espruino Forum](http://forum.espruino.com/microcosms/1424/) if you have feature requests or notice bugs. + +## Creator + +Created by Hank. + +Based on the great work of +================= +World Clock - 4 time zones +Made by [Scott Hale](https://www.github.com/computermacgyver), based upon the [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock). +===== a n d ===== +Sun Clock +[Sun Clock](https://github.com/espruino/BangleApps/tree/master/apps/sunclock) +================= diff --git a/apps/hworldclock/app.js b/apps/hworldclock/app.js new file mode 100644 index 000000000..8122371a6 --- /dev/null +++ b/apps/hworldclock/app.js @@ -0,0 +1,266 @@ +const big = g.getWidth()>200; +// Font for primary time and date +const primaryTimeFontSize = big?6:5; +const primaryDateFontSize = big?3:2; +require("Font5x9Numeric7Seg").add(Graphics); +require("FontTeletext10x18Ascii").add(Graphics); + +// Font for single secondary time +const secondaryTimeFontSize = 4; +const secondaryTimeZoneFontSize = 2; + +// Font / columns for multiple secondary times +const secondaryRowColFontSize = 2; +const xcol1 = 10; +const xcol2 = g.getWidth() - xcol1; + +const font = "6x8"; + +/* TODO: we could totally use 'Layout' here and +avoid a whole bunch of hard-coded offsets */ + + +const xyCenter = g.getWidth() / 2; +const xyCenterSeconds = xyCenter + (big ? 85 : 68); +const yAmPm = xyCenter - (big ? 70 : 48); +const yposTime = big ? 70 : 55; +const yposTime2 = yposTime + (big ? 100 : 60); +const yposDate = big ? 135 : 95; +const yposWorld = big ? 170 : 120; + +const OFFSET_TIME_ZONE = 0; +const OFFSET_HOURS = 1; + +var offsets = require("Storage").readJSON("hworldclock.settings.json") || []; + +//=======Sun +setting = require("Storage").readJSON("setting.json",1); +E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ +SunCalc = require("hsuncalc.js"); +const LOCATION_FILE = "mylocation.json"; +var rise = "07:00"; +var set = "20:00"; +var pos = {altitude: 20, azimuth: 135}; +var noonpos = {altitude: 37, azimuth: 180}; +//=======Sun + +var ampm = "AM"; + +// TESTING CODE +// Used to test offset array values during development. +// Uncomment to override secondary offsets value +/* +const mockOffsets = { + zeroOffsets: [], + oneOffset: [["UTC", 0]], + twoOffsets: [ + ["Tokyo", 9], + ["UTC", 0], + ], + fourOffsets: [ + ["Tokyo", 9], + ["UTC", 0], + ["Denver", -7], + ["Miami", -5], + ], +};*/ + +// Uncomment one at a time to test various offsets array scenarios +//offsets = mockOffsets.zeroOffsets; // should render nothing below primary time +//offsets = mockOffsets.oneOffset; // should render larger in two rows +//offsets = mockOffsets.twoOffsets; // should render two in columns +//offsets = mockOffsets.fourOffsets; // should render in columns + +// END TESTING CODE + +// Check settings for what type our clock should be +var _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false; + +// timeout used to update every minute +var drawTimeout; +var drawTimeoutSeconds; + +g.setBgColor(0, 0, 0); + +// schedule a draw for the next minute +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} + +function doublenum(x) { + return x < 10 ? "0" + x : "" + x; +} + +function getCurrentTimeFromOffset(dt, offset) { + return new Date(dt.getTime() + offset * 60 * 60 * 1000); +} + +function updatePos() { + coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":53.3,"lon":10.1,"location":"Pattensen"}; + pos = SunCalc.getPosition(Date.now(), coord.lat, coord.lon); + times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon); + rise = times.sunrise.toString().split(" ")[4].substr(0,5); + set = times.sunset.toString().split(" ")[4].substr(0,5); + noonpos = SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon); +} + + +function drawSeconds() { + // get date + var d = new Date(); + var da = d.toString().split(" "); + + // default draw styles + g.reset(); + g.setBgColor(0, 0, 0); + + // drawSting centered + g.setFontAlign(0, 0); + + // draw time + var time = da[4].split(":"); + var seconds = time[2]; + + g.setFont("5x9Numeric7Seg",primaryTimeFontSize - 3); + g.setColor("#22ff05"); + //g.setFont(font, primaryTimeFontSize-3); + g.drawString(`${seconds}`, xyCenterSeconds, yposTime+14, true); +} + +function draw() { + // get date + var d = new Date(); + var da = d.toString().split(" "); + + // default draw styles + g.reset(); + g.setBgColor(0, 0, 0); + + // drawSting centered + g.setFontAlign(0, 0); + + // draw time + var time = da[4].split(":"); + var hours = time[0], + minutes = time[1]; + + + if (_12hour){ + //do 12 hour stuff + if (hours > 12) { + ampm = "PM"; + hours = hours - 12; + } else { + ampm = "AM"; + } + } + + //g.setFont(font, primaryTimeFontSize); + g.setFont("5x9Numeric7Seg",primaryTimeFontSize); + g.setColor("#22ff05"); + g.drawString(`${doublenum(hours)}:${minutes}`, xyCenter-10, yposTime, true); + + // am / PM ? + if (_12hour){ + //do 12 hour stuff + //var ampm = require("locale").medidian(new Date()); Not working + g.setFont("Vector", 17); + g.setColor("#22ff05"); + g.drawString(ampm, xyCenterSeconds, yAmPm, true); + } + + drawSeconds(); // To make sure... + + // draw Day, name of month, Date + //DATE + var localDate = require("locale").date(new Date(), 1); + localDate = localDate.substring(0, localDate.length - 5) + g.setFont("Vector", 17); + g.drawString(require("locale").dow(new Date(), 1).toUpperCase() + ", " + localDate, xyCenter, yposDate, true); + + + + g.setFont(font, primaryDateFontSize); + // set gmt to UTC+0 + var gmt = new Date(d.getTime() + d.getTimezoneOffset() * 60 * 1000); + + // Loop through offset(s) and render + offsets.forEach((offset, index) => { + dx = getCurrentTimeFromOffset(gmt, offset[OFFSET_HOURS]); + hours = doublenum(dx.getHours()); + minutes = doublenum(dx.getMinutes()); + + + if (offsets.length === 1) { + var date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)]; + // For a single secondary timezone, draw it bigger and drop time zone to second line + const xOffset = 30; + g.setFont(font, secondaryTimeFontSize); + g.drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true); + g.setFont(font, secondaryTimeZoneFontSize); + g.drawString(offset[OFFSET_TIME_ZONE], xyCenter, yposTime2 + 30, true); + + // draw Day, name of month, Date + g.setFont(font, secondaryTimeZoneFontSize); + g.drawString(date, xyCenter, yposDate, true); + } else if (index < 3) { + // For > 1 extra timezones, render as columns / rows + g.setFont(font, secondaryRowColFontSize); + g.setFontAlign(-1, 0); + g.drawString( + offset[OFFSET_TIME_ZONE], + xcol1, + yposWorld + index * 15, + true + ); + g.setFontAlign(1, 0); + g.drawString(`${hours}:${minutes}`, xcol2, yposWorld + index * 15, true); + } + }); + + g.setFontAlign(-1, 0); + g.setFont("Vector",12); + g.drawString(`^${rise}`, 10, 3 + yposWorld + 3 * 15, true); // draw riseset + g.setFontAlign(1, 0); + g.drawString(`v${set}`, xcol2, 3 + yposWorld + 3 * 15, true); // draw riseset + + queueDraw(); +} + +// clean app screen +g.clear(); +// Show launcher when button pressed +Bangle.setUI("clock"); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +updatePos(); +setInterval(drawSeconds, 1E3); + + + +// Stop updates when LCD is off, restart when on +Bangle.on('lcdPower',on=>{ + if (on) { + draw(); // draw immediately, queue redraw + drawSeconds(); // draw immediately, queue redraw + setInterval(updatePos, 60*5E3); // refesh every 5 mins + setInterval(drawSeconds, 1E3); + updatePos(); + } else { // stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); + drawTimeout = undefined; + drawTimeoutSeconds = undefined; + setInterval(updatePos, 60*50E3); // refesh every 50 mins + setInterval(drawSeconds, 10E3); + updatePos(); + } +}); + +// draw now +drawSeconds(); +draw(); diff --git a/apps/hworldclock/app.png b/apps/hworldclock/app.png new file mode 100644 index 0000000000000000000000000000000000000000..506fa45b917be1f40fc2e691581ff199e8144f58 GIT binary patch literal 2867 zcmV-33(WM1P)Dr$W1mWa^_`k0JNLm4i2H*_fM*v3! zLEwGx1OW&FpahUD2*R!2a`Y+yk^+F|01O21A3+eBd+V?pUIzfr1Ly#dAqaxHFL;fe zNJu7;^z3u;UwyccOe7iD*TgeO&h1lD^ZuSC8Piu}uaFGr6SCg<^(GllvfqP{EFt+= zAL{C@e^-*p_Z0-mMI?P6g09}`7(_C$w?@yB9QBYi^%}bFWO7X0^=e2C1>htI!s&-C ztfvqJp$>qYU33i;a!VYj1DYPzSkO-kFq?G#|PI850w;MkEr=)bY3i@OO50 zj??|Nj*5y(`|`^#v$C_Zvm6~Ap90`!Z*M=)&CM+WCLXSCP|v&LY3hFj1qF#JD=Xvb z>+2O#sZ`P4-hSQM+FIDRZ{LK#z(D`3tgJn*uC7BI9UXnb!opk&3JOX!8ckmR{{5d( ztJNqgE2}OnEPQ{%h7JGLb6&rGJ@?yhzkRu@t1GgosHo)YufLXrhljg(d3jA$DwU(& ze*0~SNF)+eDisC%cA~z%{z_F<)u6z@K$ToBFKKOUEtAP);vqwZ$d@l)UXqfM z;!M&`5Cr8fZ)e)HX{+n&>u<_rvM!}kNkgbqDt2{s@tbeH$%u%E_3=IMEG%5MY}pI|4xXN#uK=(GfOp@0cXDxY@s&-RHdQn=HEFhQ-+sHXv5_R1 zo14q4SFftrL)6vPbyZhaD{^vjWK*V02_-q-@GXR`U%x)>%$YMEIXgRh*3{G}jS0@4 zJ=+o*8ag61HFddMF6ZvuyN>~|l1L;Detv%ThGfHs5BHipdGd=(mo9y4<;sCCYNlTCvlYCxx(Qn6&9hXS5 zrl#iN-o1ObtXZ>WrMtU(P*0prpFVx$wQJXY$qSvGovf^^Y^$oOYEDm2zjpKHO^sTu z=2u^RReAmTb#+g=&zd!B=1C+H4`W{>|3Fd#l24L+N`GV2s8R2=wY6zdQ&SfJu_*_JI^Qi_X0VzKy< z{QUfDB$=3)m;*oS=G6@xfcLnz<>eq4h{~H z-30M6NJg3pKnDwvNHqKC(W5s=vZbY^Ln@Vi?&jtuHr4pM@4nloP$*blUf$Hw(qb~x z%H?v^>eZ{CH?*5SfBwku@bFi4f4w3iB9^`Z3t-888FOpM0(w1f_0I$9FT3};iqshK4l}dM;6OxgU@t$G(ii(P602To3 z?iGYFJUl#n0s;bJ!otG-EEbDLe(}W@ugc|eZO5&zzkREH`i7oF$XVx}u_@KHR-~_d7bxFME1= zKA{g_(Y;u5a&k5pim9%yRs!g6&L%G}FUufchJ}UsA31X5a8y)Ow_Vl{adB}^78Mnp zRjai*Ppww-`0?Xs0rb;pw$%p^OVZ@bqQ|G7ewv^cbMD-^HeX*~?|TV|iHUi-t*wnD zdHVF}Y5=y27cX9dUyHluU|CX9a@n}sc6D_zAt51xlpdKmGL6Y?6L3 z>;WW~m}5&xNtvP1Xn648!Kz7dCl-s{#A2~qFU13Zl}IGgul%gHZQJ&b{QUeM zUwY}K0Fti*Kzw}szYGI-hUD<>*a89qp1N}7irUW3?x7WT%^^BEdc42Ce;`S>*4EYq z78Vu@TwPu7EVGbYYKFtMxVX3_K0babfctr(eFBn8h7TX^1MTk5U4^Z38mvr>aC`gq z?NdocIXOAmK2(+6fZ7$q)ym4s8ff7=1#ph!#NR|&51{MTZMqt1kBy?tjECP0)K;cnykFzk-!lkm_vK!HP?FidM|UC_ zOmabAk}dhIW+#$!NSgc>eh|_@B$ttVx=)Gk^zk?!@>VWLhEuzH=--E&LrK0ta%yjF z@B6HTZd~{PXa%re5QLk(w6_H?6~H3^F6lUWeO#e04tqEQm;&wg5urWzZ~;IQfE&7N zj?f-Y@Yb~x0Ne(UF9^bw2l4;F0T_d%4YVgx1_H2$Hd(eq`=-tt?yU3${s(KJ-urwb RzLEd{002ovPDHLkV1oI8cc%aV literal 0 HcmV?d00001 diff --git a/apps/hworldclock/custom.html b/apps/hworldclock/custom.html new file mode 100644 index 000000000..896d999f5 --- /dev/null +++ b/apps/hworldclock/custom.html @@ -0,0 +1,76 @@ + + + + + + +

You can add up to 3 timezones. Please give a name and UTC offset in hours. + If you want less than 3, clear the checkbox to the left.

+ + + + + + + +
Enabled?NameUTC Offset
+ +

Click

+ + + + + + diff --git a/apps/hworldclock/hsuncalc.js b/apps/hworldclock/hsuncalc.js new file mode 100644 index 000000000..b1af0a0d9 --- /dev/null +++ b/apps/hworldclock/hsuncalc.js @@ -0,0 +1,298 @@ +/* Module suncalc.js + (c) 2011-2015, Vladimir Agafonkin + SunCalc is a JavaScript library for calculating sun/moon position and light phases. + https://github.com/mourner/suncalc + +PB: Usage: +E.setTimeZone(2); // 1 = MEZ, 2 = MESZ +SunCalc = require("suncalc.js"); +pos = SunCalc.getPosition(Date.now(), 53.3, 10.1); +times = SunCalc.getTimes(Date.now(), 53.3, 10.1); +rise = times.sunrise; // Date object +rise_str = rise.getHours() + ':' + rise.getMinutes(); //hh:mm +*/ +var exports={}; + +// shortcuts for easier to read formulas + +var PI = Math.PI, + sin = Math.sin, + cos = Math.cos, + tan = Math.tan, + asin = Math.asin, + atan = Math.atan2, + acos = Math.acos, + rad = PI / 180; + +// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas + +// date/time constants and conversions + +var dayMs = 1000 * 60 * 60 * 24, + J1970 = 2440588, + J2000 = 2451545; + +function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; } +function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } // PB: onece removed + 0.5; included it again 4 Jan 2021 +function toDays(date) { return toJulian(date) - J2000; } + + +// general calculations for position + +var e = rad * 23.4397; // obliquity of the Earth + +function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); } +function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); } + +function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); } +function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); } + +function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; } + +function astroRefraction(h) { + if (h < 0) // the following formula works for positive altitudes only. + h = 0; // if h = -0.08901179 a div/0 would occur. + + // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad: + return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179)); +} + +// general sun calculations + +function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); } + +function eclipticLongitude(M) { + + var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center + P = rad * 102.9372; // perihelion of the Earth + + return M + C + P + PI; +} + +function sunCoords(d) { + + var M = solarMeanAnomaly(d), + L = eclipticLongitude(M); + + return { + dec: declination(L, 0), + ra: rightAscension(L, 0) + }; +} + +// calculates sun position for a given date and latitude/longitude + +exports.getPosition = function (date, lat, lng) { + + var lw = rad * -lng, + phi = rad * lat, + d = toDays(date), + + c = sunCoords(d), + H = siderealTime(d, lw) - c.ra; + + return { + azimuth: Math.round((azimuth(H, phi, c.dec) / rad + 180) % 360), // PB: converted to deg + altitude: Math.round( altitude(H, phi, c.dec) / rad) // PB: converted to deg + }; +}; + + +// sun times configuration (angle, morning name, evening name) + +var times = [ + [-0.833, 'sunrise', 'sunset' ] +]; + +// calculations for sun times +var J0 = 0.0009; + +function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); } + +function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; } +function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); } + +function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); } +function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; } + +// returns set time for the given sun altitude +function getSetJ(h, lw, phi, dec, n, M, L) { + + var w = hourAngle(h, phi, dec), + a = approxTransit(w, lw, n); + return solarTransitJ(a, M, L); +} + + +// calculates sun times for a given date, latitude/longitude, and, optionally, +// the observer height (in meters) relative to the horizon + +exports.getTimes = function (date, lat, lng, height) { + + height = height || 0; + + var lw = rad * -lng, + phi = rad * lat, + + dh = observerAngle(height), + + d = toDays(date), + n = julianCycle(d, lw), + ds = approxTransit(0, lw, n), + + M = solarMeanAnomaly(ds), + L = eclipticLongitude(M), + dec = declination(L, 0), + + Jnoon = solarTransitJ(ds, M, L), + + i, len, time, h0, Jset, Jrise; + + + var result = { + solarNoon: fromJulian(Jnoon), + nadir: fromJulian(Jnoon - 0.5) + }; + + for (i = 0, len = times.length; i < len; i += 1) { + time = times[i]; + h0 = (time[0] + dh) * rad; + + Jset = getSetJ(h0, lw, phi, dec, n, M, L); + Jrise = Jnoon - (Jset - Jnoon); + + result[time[1]] = fromJulian(Jrise); + result[time[2]] = fromJulian(Jset); + } + + return result; +}; + + +// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas + +function moonCoords(d) { // geocentric ecliptic coordinates of the moon + + var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude + M = rad * (134.963 + 13.064993 * d), // mean anomaly + F = rad * (93.272 + 13.229350 * d), // mean distance + + l = L + rad * 6.289 * sin(M), // longitude + b = rad * 5.128 * sin(F), // latitude + dt = 385001 - 20905 * cos(M); // distance to the moon in km + + return { + ra: rightAscension(l, b), + dec: declination(l, b), + dist: dt + }; +} + +getMoonPosition = function (date, lat, lng) { + + var lw = rad * -lng, + phi = rad * lat, + d = toDays(date), + + c = moonCoords(d), + H = siderealTime(d, lw) - c.ra, + h = altitude(H, phi, c.dec), + // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H)); + + h = h + astroRefraction(h); // altitude correction for refraction + + return { + azimuth: azimuth(H, phi, c.dec), + altitude: h, + distance: c.dist, + parallacticAngle: pa + }; +}; + + +// calculations for illumination parameters of the moon, +// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and +// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + +getMoonIllumination = function (date) { + + var d = toDays(date || new Date()), + s = sunCoords(d), + m = moonCoords(d), + + sdist = 149598000, // distance from Earth to Sun in km + + phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)), + inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)), + angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) - + cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra)); + + return { + fraction: (1 + cos(inc)) / 2, + phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI, + angle: angle + }; +}; + + +function hoursLater(date, h) { + return new Date(date.valueOf() + h * dayMs / 24); +} + +// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article + +getMoonTimes = function (date, lat, lng, inUTC) { + var t = new Date(date); + if (inUTC) t.setUTCHours(0, 0, 0, 0); + else t.setHours(0, 0, 0, 0); + + var hc = 0.133 * rad, + h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc, + h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx; + + // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) + for (var i = 1; i <= 24; i += 2) { + h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc; + h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc; + + a = (h0 + h2) / 2 - h1; + b = (h2 - h0) / 2; + xe = -b / (2 * a); + ye = (a * xe + b) * xe + h1; + d = b * b - 4 * a * h1; + roots = 0; + + if (d >= 0) { + dx = Math.sqrt(d) / (Math.abs(a) * 2); + x1 = xe - dx; + x2 = xe + dx; + if (Math.abs(x1) <= 1) roots++; + if (Math.abs(x2) <= 1) roots++; + if (x1 < -1) x1 = x2; + } + + if (roots === 1) { + if (h0 < 0) rise = i + x1; + else set = i + x1; + + } else if (roots === 2) { + rise = i + (ye < 0 ? x2 : x1); + set = i + (ye < 0 ? x1 : x2); + } + + if (rise && set) break; + + h0 = h2; + } + + var result = {}; + + if (rise) result.rise = hoursLater(t, rise); + if (set) result.set = hoursLater(t, set); + + if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true; + + return result; +}; \ No newline at end of file diff --git a/apps/hworldclock/hworldclock-icon.js b/apps/hworldclock/hworldclock-icon.js new file mode 100644 index 000000000..6e05d254c --- /dev/null +++ b/apps/hworldclock/hworldclock-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgJC/ABEE+EA4EAj9E8HF//gn/gwP///wt/MgF//8gh/8gYLBwEP+EHAofghgFD4EOj//gEPA4ILBGgIxB/wFBgwFB/lsgCKBj/4oxHBvAFBJoV8gP4TQX+gJUBAAN/Aok+AoVgAoXogAfBjkA8AfBAoXAAoUYY4cAiCDEAooA/ABg")) diff --git a/apps/hworldclock/hworldclock.png b/apps/hworldclock/hworldclock.png new file mode 100644 index 0000000000000000000000000000000000000000..565e0dc6b8e08edffc313b889bc2e062ffc1edf7 GIT binary patch literal 3457 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 Date: Sat, 11 Jun 2022 15:23:47 +0200 Subject: [PATCH 09/41] Initial Release --- .../widget.png | Bin 0 -> 2385 bytes apps/hwid_a_battery_widget/ChangeLog | 5 ++ apps/hwid_a_battery_widget/README.md | 15 +++++ .../h_battery_widget-pic.jpg | Bin 0 -> 66429 bytes apps/hwid_a_battery_widget/metadata.json | 15 +++++ apps/hwid_a_battery_widget/widget.js | 56 ++++++++++++++++++ apps/hwid_a_battery_widget/widget.png | Bin 0 -> 877 bytes 7 files changed, 91 insertions(+) create mode 100644 apps/hwid_a_battery_widget/Automatisch beibehalten von Corel/widget.png create mode 100644 apps/hwid_a_battery_widget/ChangeLog create mode 100644 apps/hwid_a_battery_widget/README.md create mode 100644 apps/hwid_a_battery_widget/h_battery_widget-pic.jpg create mode 100644 apps/hwid_a_battery_widget/metadata.json create mode 100644 apps/hwid_a_battery_widget/widget.js create mode 100644 apps/hwid_a_battery_widget/widget.png diff --git a/apps/hwid_a_battery_widget/Automatisch beibehalten von Corel/widget.png b/apps/hwid_a_battery_widget/Automatisch beibehalten von Corel/widget.png new file mode 100644 index 0000000000000000000000000000000000000000..868628b6085cb0da28098648ae713dc1f08e44d0 GIT binary patch literal 2385 zcmaJ@3s_S}7QRV%6-6sAF<4ClyK7v@i-;0K3Ni8!q9Gtc4b~8DAdq)1aB(YAt>u%d z1QAzxOM?njO9Tu>6tPi@^y8Cq0o>m5~KlokLMAQ3HazyNs#E&zZ#FAa}`B6%T9u~JSJ#VTV!GAvi2N&pLHBBmUK zLS3?Dm5U%i%ca`iELW}fxyzlN|*w!{|p^@ z^C8AVkcvs6q@<*fQ#{E^O&o>h?d?sW(kXPh2ZHd>rYay2?4i)QO^f`Y!v(csjZ_6m zl?no?D~eGjK|Vwxy3y-49VaA}%- zo53R0G|7Az(=oEp2$)dxGIwnb9z78#m+Sq0cd}KFI#A;yHf?IRDcwX=oQiR>aFy?ugdQ6YyDM9=!Ox)6 z-QwI1%ye`nVGTAnt{o#ero6HML)Uy^Dj6ygJslhQ-l*K`UtG1cwe@N1cVDLrwdRMG z*64k76TUsT2uh&{4?j3|Ne+tME|dQYKSCDEpg8kolSq#+^^!R)ie+U z+s11v=x1NukMZ}niWUm_R2O|qOK`|UPMgE#(5}Jk;~k)HT0%l*MP+4DZAyM`Pfuxc za}bloswueJ3BW4`U0q#Co}QjDK$KIEn`0ez=clGG-0CZEQ^M%z@-?u@J`5Kt(5+iU z#uXJ8=iGmKrnyp3Bv0G-oedIQndCY+#&iwI4%dH@kxX^BbGx#&>l$)y9T~ zkjlzR27|%n@pupf>3gb(iHV6Hepx3Z=bb!xs=mG+w&RbTc;zlnU-sJ6JCs%aX z>{!HYi_>azcJKZQPoS#rbKUi=N-VU=E%-tkypQL*|~ zOUnW`T+CgsyMm664#o!`eARY=_wK;w_qw~!V2!1v9Jf`=Tt3OM-BDRvyKQ7|=fk0) z3UTLfFhy+I?6Dod#QiOvLL!Bx7oB7eSJ~g*e--e3wmRopRvE^nd$={{#|FZYOIi0` z{iDsmz26SNiEa)73wZvvR8kw&<<{1Q|u{R*kDbNFBDQ31}%3AJl^-n#&A7i zxIK5{orx`M+GVZ3ZuIbI-e`Wmh~?5m3NID^c4ADVE=l|sd~femU2l7U^9Td|Ul6ou L3->%{*S`M&n6+>< literal 0 HcmV?d00001 diff --git a/apps/hwid_a_battery_widget/ChangeLog b/apps/hwid_a_battery_widget/ChangeLog new file mode 100644 index 000000000..84cbad8ad --- /dev/null +++ b/apps/hwid_a_battery_widget/ChangeLog @@ -0,0 +1,5 @@ +0.01: Release for Bangle 2 (2021/11/18) +0.02: Internal id update to wid_* as per Gordon's request (2021/11/21) +0.03: Support dark themes +0.04: Increase screen update rate when charging +0.05: Deleting Background - making Font larger \ No newline at end of file diff --git a/apps/hwid_a_battery_widget/README.md b/apps/hwid_a_battery_widget/README.md new file mode 100644 index 000000000..638272b3f --- /dev/null +++ b/apps/hwid_a_battery_widget/README.md @@ -0,0 +1,15 @@ +# A Battery Widget (with percentage) + +Show the current battery level and charging status in the top right of the clock, with charge percentage + +* Works with Bangle 2 +* Simple design, no settings + * Red when the batterly level is below 30% + * Blue when charging +* 40 pixels wide + +![](a_battery_widget-pic.jpg) + +## Creator +[@alainsaas](https://github.com/alainsaas) +Mod by Hank diff --git a/apps/hwid_a_battery_widget/h_battery_widget-pic.jpg b/apps/hwid_a_battery_widget/h_battery_widget-pic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d9a4deccf352be3086e7d8d66200a436839c8da GIT binary patch literal 66429 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�y4n1O6P<0tvFKDNAjl02*?a;Bxc?7T-^SzHp2 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 { + if (Bangle.isCharging()) return COLORS.charging; + if (l >= 30) return COLORS.high; + return COLORS.low; + }; + + function draw() { + var s = 29; + var x = this.x, y = this.y; + + const l = E.getBattery(); + + let xl = x+4+l*(s-12)/100; + + g.setColor(COLORS.white); + // g.fillRect(x+2,y+5,x+s-6,y+18); + + g.fillRect(x,y,xl+4,y+16+3); //Clear + + g.setColor(levelColor(l)); + // g.fillRect(x+1,y+3,x+s-5,y+4); + // g.fillRect(x+1,y+19,x+s-5,y+20); + // g.fillRect(x,y+4,x+1,y+19); + // g.fillRect(x+s-5,y+4,x+s-4,y+19); + //g.fillRect(x+s-3,y+8,x+s-2,y+16); // tip of the battery + g.fillRect(x+4,y+14+3,xl,y+16+3); // charging bar + + g.fillRect((x+4+100*(s-12)/100)-1,y+14+3,x+4+100*(s-12)/100,y+16+3); // charging bar full + + g.setColor(COLORS.black); + g.setFontAlign(0,0); + g.setFont('Vector',16); + g.drawString(l, x + 14, y + 10); + + if (Bangle.isCharging()) changeInterval(id, intervalHigh); + else changeInterval(id, intervalLow); + } + + + Bangle.on('charging',function(charging) { draw(); }); + var id = setInterval(()=>WIDGETS["wid_a_battery_widget"].draw(), intervalLow); + + WIDGETS["wid_a_battery_widget"]={area:"tr",width:30,draw:draw}; +})(); diff --git a/apps/hwid_a_battery_widget/widget.png b/apps/hwid_a_battery_widget/widget.png new file mode 100644 index 0000000000000000000000000000000000000000..b9c9594b15c9832c0c5632044ae68989ff5f8cba GIT binary patch literal 877 zcmV-z1CsoSP)kEXCn{4Izi|(P5>F#v86JekZe56yURMlVS)agELvk4O>OqeiXb7r^O zZBD0i!{_sDc)i|vZ0m;R#r%97rVNWuCX>$#g~DIr5AlomNo0uc#4+)S_&~fT-Vlex zYXbj^5>JSS#6EGKxJTR}ZV@+#Ys47aH3bttea}Z(5u=6y?@9}udrqk&uCX)#V zziY8fpZW3e8$`eprVOD%1N^{XYKHc~V8Cj%8Y>oym}~~Lbvhl^@Ant>QJVv8)aMey z$*cey4UpCoP_NfnC=_BYm+J(#+s(msEEZ$sa+w8#!I^d?i9~{XKtsRjUhl5||ayh-Bmk@xLwsU-bmcPY4n42o|5oWLT@!;<1iUfk%`fgs`est4vN3 z8Uij_74X@J0QA9Ol*0+1_>SFex7*wkYT;ubgf^`T__9cVym;(5b{>m|C**X9EL5pf zShLyWAtr}fKA-2FfCzX&L%?OL0zNJhfXU^sNvG2@X;GP}(8q*;ZL0!~j0nIYuo%R< zG92_WmBA3YVpYI9qaMI69u)yQRs|ejBJ3I#Pt7YqD|N2J1JSeYf#{h%1E_>(fTwcS(5tGJMJrpWRAg68 zS7fL~sU|&7gFKdM+I^|UeTKs=)xI$zA%X8NB@Q5-ctkvqYT>)YZQ`a>4_}vR;ytM@ z-ocJ5*rWM Date: Sat, 11 Jun 2022 15:55:03 +0200 Subject: [PATCH 10/41] Improve median calculation Do no longer show 3h average pressure in widget --- apps/widbaroalarm/README.md | 2 +- apps/widbaroalarm/widget.js | 74 +++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/apps/widbaroalarm/README.md b/apps/widbaroalarm/README.md index a74d89546..80ba35abc 100644 --- a/apps/widbaroalarm/README.md +++ b/apps/widbaroalarm/README.md @@ -19,7 +19,7 @@ Get a notification when the pressure reaches defined thresholds. * Pause delay: Same as Dismiss delay but longer (usefull for meetings and such). From 30 to 240 min ## Widget -The widget shows two rows: pressure value of last measurement and pressure average of the the last three hours. +The widget shows the last median pressure value ## Creator Marco ([myxor](https://github.com/myxor)) diff --git a/apps/widbaroalarm/widget.js b/apps/widbaroalarm/widget.js index 721d0bbb8..00456f42f 100644 --- a/apps/widbaroalarm/widget.js +++ b/apps/widbaroalarm/widget.js @@ -37,8 +37,12 @@ E.showPrompt(body, { title: "Pressure alarm", - buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 } - }).then(function (v) { + buttons: { + "Ok": 1, + "Dismiss": 2, + "Pause": 3 + } + }).then(function(v) { const tsNow = Math.round(Date.now() / 1000); // seconds if (v == 1) { @@ -176,45 +180,38 @@ } - - function baroHandler(data) { - if (data) { - const pressure = Math.round(data.pressure); - if (pressure == undefined || pressure <= 0) return; - currentPressures.push(pressure); - } - } - /* turn on barometer power - take `numberOfMeasurements` measurements with a delay of 1000ms each + take multiple measurements sort the results take the middle one (median) turn off barometer power */ function check() { + const MEDIANLENGTH = 20; Bangle.setBarometerPower(true, "widbaroalarm"); - setTimeout(function() { - currentPressures = []; + Bangle.on('pressure', function(e) { + while (currentPressures.length > MEDIANLENGTH) currentPressures.pop(); + currentPressures.unshift(e.pressure); + median = currentPressures.slice().sort(); - const numberOfMeasurements = 7; - - for (let i = 0; i < numberOfMeasurements; i++) { - setTimeout(function() { - Bangle.getPressure().then(baroHandler); - }, i * 1000); + if (median.length > 10) { + var mid = median.length >> 1; + medianPressure = Math.round(E.sum(median.slice(mid - 4, mid + 5)) / 9); + if (medianPressure > 0) { + turnOff(); + checkForAlarms(medianPressure); + } } + }); - setTimeout(function() { - Bangle.setBarometerPower(false, "widbaroalarm"); + setTimeout(function() { + turnOff(); + }, 10000); + } - currentPressures.sort(); - - // take median value - medianPressure = currentPressures[Math.round(numberOfMeasurements / 2) + 1]; - checkForAlarms(medianPressure); - }, numberOfMeasurements * 1000 + 500); - }, 500); + function turnOff() { + Bangle.setBarometerPower(false, "widbaroalarm"); } function reload() { @@ -222,8 +219,8 @@ } function draw() { - if (global.WIDGETS != undefined && typeof WIDGETS === "object") { - WIDGETS["baroalarm"] = { + if (global.WIDGETS != undefined && typeof global.WIDGETS === "object") { + global.WIDGETS["baroalarm"] = { width: setting("show") ? 24 : 0, reload: reload, area: "tr", @@ -235,18 +232,13 @@ if (setting("show") && medianPressure != undefined) { g.setFont("6x8", 1).setFontAlign(1, 0); g.drawString(Math.round(medianPressure), this.x + 24, this.y + 6); - if (threeHourAvrPressure != undefined && threeHourAvrPressure > 0) { - g.drawString(Math.round(threeHourAvrPressure), this.x + 24, this.y + 6 + 10); - } } } - // Let's delay the first check a bit - setTimeout(function() { - check(); - if (interval > 0) { - setInterval(check, interval * 60000); - } - }, 1000); + check(); + if (interval > 0) { + setInterval(check, interval * 60000); + } + draw(); })(); From e6515080e85e4ef2d1ba081ec453abdcbec51263 Mon Sep 17 00:00:00 2001 From: Dennis Kueper Date: Sat, 11 Jun 2022 17:46:36 +0200 Subject: [PATCH 11/41] Fixing refresh issues --- apps/hwid_a_battery_widget/ChangeLog | 3 +- apps/hwid_a_battery_widget/metadata.json | 2 +- apps/hwid_a_battery_widget/widget.js | 92 ++++++++++++------------ 3 files changed, 48 insertions(+), 49 deletions(-) diff --git a/apps/hwid_a_battery_widget/ChangeLog b/apps/hwid_a_battery_widget/ChangeLog index 84cbad8ad..3d7b39737 100644 --- a/apps/hwid_a_battery_widget/ChangeLog +++ b/apps/hwid_a_battery_widget/ChangeLog @@ -2,4 +2,5 @@ 0.02: Internal id update to wid_* as per Gordon's request (2021/11/21) 0.03: Support dark themes 0.04: Increase screen update rate when charging -0.05: Deleting Background - making Font larger \ No newline at end of file +0.05: Deleting Background - making Font larger +0.06: Fixing refresh issues \ No newline at end of file diff --git a/apps/hwid_a_battery_widget/metadata.json b/apps/hwid_a_battery_widget/metadata.json index 7fdd2907a..d0cb5233a 100644 --- a/apps/hwid_a_battery_widget/metadata.json +++ b/apps/hwid_a_battery_widget/metadata.json @@ -3,7 +3,7 @@ "name": "A Battery Widget (with percentage) - Hanks Mod", "shortName":"H Battery Widget", "icon": "widget.png", - "version":"0.04", + "version":"0.06", "type": "widget", "supports": ["BANGLEJS", "BANGLEJS2"], "readme": "README.md", diff --git a/apps/hwid_a_battery_widget/widget.js b/apps/hwid_a_battery_widget/widget.js index 0929dea41..708e8cbbf 100644 --- a/apps/hwid_a_battery_widget/widget.js +++ b/apps/hwid_a_battery_widget/widget.js @@ -1,56 +1,54 @@ (function(){ - const intervalLow = 60000; // update time when not charging - const intervalHigh = 2000; // update time when charging + const intervalLow = 60000; // update time when not charging + const intervalHigh = 2000; // update time when charging + var old_l; - let COLORS = { - 'white': g.theme.dark ? "#000" : "#fff", - 'black': g.theme.dark ? "#fff" : "#000", - 'charging': "#08f", - 'high': g.theme.dark ? "#fff" : "#000", - 'low': "#f00", - }; + let COLORS = { + 'white': g.theme.dark ? "#000" : "#fff", + 'black': g.theme.dark ? "#fff" : "#000", + 'charging': "#08f", + 'high': g.theme.dark ? "#fff" : "#000", + 'low': "#f00", + }; - const levelColor = (l) => { - if (Bangle.isCharging()) return COLORS.charging; - if (l >= 30) return COLORS.high; - return COLORS.low; - }; + const levelColor = (l) => { + if (Bangle.isCharging()) return COLORS.charging; + if (l >= 30) return COLORS.high; + return COLORS.low; + }; - function draw() { - var s = 29; - var x = this.x, y = this.y; + function draw() { + var s = 29; + var x = this.x, y = this.y; + const l = E.getBattery(); + let xl = x+4+l*(s-12)/100; + if (l != old_l){ // Delete the old value from screen + old_l = l; + let xl_old = x+4+old_l*(s-12)/100; + g.setColor(COLORS.white); + // g.fillRect(x+2,y+5,x+s-6,y+18); + g.fillRect(x,y,xl+4,y+16+3); //Clear + g.setFontAlign(0,0); + g.setFont('Vector',16); + g.drawString(old_l, x + 14, y + 10); + g.fillRect(x+4,y+14+3,xl_old,y+16+3); // charging bar + } - const l = E.getBattery(); + g.setColor(levelColor(l)); + g.fillRect(x+4,y+14+3,xl,y+16+3); // charging bar + g.fillRect((x+4+100*(s-12)/100)-1,y+14+3,x+4+100*(s-12)/100,y+16+3); // charging bar "full mark" + // Show percentage + g.setColor(COLORS.black); + g.setFontAlign(0,0); + g.setFont('Vector',16); + g.drawString(l, x + 14, y + 10); - let xl = x+4+l*(s-12)/100; + if (Bangle.isCharging()) changeInterval(id, intervalHigh); + else changeInterval(id, intervalLow); + } - g.setColor(COLORS.white); - // g.fillRect(x+2,y+5,x+s-6,y+18); + Bangle.on('charging',function(charging) { draw(); }); + var id = setInterval(()=>WIDGETS["wid_a_battery_widget"].draw(), intervalLow); - g.fillRect(x,y,xl+4,y+16+3); //Clear - - g.setColor(levelColor(l)); - // g.fillRect(x+1,y+3,x+s-5,y+4); - // g.fillRect(x+1,y+19,x+s-5,y+20); - // g.fillRect(x,y+4,x+1,y+19); - // g.fillRect(x+s-5,y+4,x+s-4,y+19); - //g.fillRect(x+s-3,y+8,x+s-2,y+16); // tip of the battery - g.fillRect(x+4,y+14+3,xl,y+16+3); // charging bar - - g.fillRect((x+4+100*(s-12)/100)-1,y+14+3,x+4+100*(s-12)/100,y+16+3); // charging bar full - - g.setColor(COLORS.black); - g.setFontAlign(0,0); - g.setFont('Vector',16); - g.drawString(l, x + 14, y + 10); - - if (Bangle.isCharging()) changeInterval(id, intervalHigh); - else changeInterval(id, intervalLow); - } - - - Bangle.on('charging',function(charging) { draw(); }); - var id = setInterval(()=>WIDGETS["wid_a_battery_widget"].draw(), intervalLow); - - WIDGETS["wid_a_battery_widget"]={area:"tr",width:30,draw:draw}; + WIDGETS["wid_a_battery_widget"]={area:"tr",width:30,draw:draw}; })(); From 5c8ba46ea96159da3e8fc23daf5f7845f4698435 Mon Sep 17 00:00:00 2001 From: David Peer Date: Sat, 11 Jun 2022 17:49:47 +0200 Subject: [PATCH 12/41] Version 0.10 - Show daily step count, temperature as well as heartrate. --- apps/cassioWatch/ChangeLog | 3 +- apps/cassioWatch/README.md | 3 +- apps/cassioWatch/app.js | 61 ++++++++++++++++++++++++++++++++++---- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/apps/cassioWatch/ChangeLog b/apps/cassioWatch/ChangeLog index f00b3fa0a..419810021 100644 --- a/apps/cassioWatch/ChangeLog +++ b/apps/cassioWatch/ChangeLog @@ -7,4 +7,5 @@ 0.6: Add Settings Page 0.7: Update Rocket Sequences Scope to not use memory all time 0.8: Update Some Variable Scopes to not use memory until need -0.9: Remove ESLint spaces \ No newline at end of file +0.9: Remove ESLint spaces +0.10: Show daily steps, heartrate and the temperature if weather information is available. \ No newline at end of file diff --git a/apps/cassioWatch/README.md b/apps/cassioWatch/README.md index 1342af8e6..aaeb3f122 100644 --- a/apps/cassioWatch/README.md +++ b/apps/cassioWatch/README.md @@ -6,5 +6,6 @@ Clock with Space Cassio Watch Style. It displays current temperature,day,steps,battery.heartbeat and weather. + **To-do**: -Integrate heartbeat and Weather, Align and change size of some elements. +Align and change size of some elements. diff --git a/apps/cassioWatch/app.js b/apps/cassioWatch/app.js index 93538ec50..49e23c2eb 100644 --- a/apps/cassioWatch/app.js +++ b/apps/cassioWatch/app.js @@ -1,7 +1,11 @@ +const storage = require('Storage'); +const locale = require('locale'); + require("Font6x12").add(Graphics); require("Font8x12").add(Graphics); require("Font7x11Numeric7Seg").add(Graphics); + let ClockInterval; let RocketInterval; let BatteryInterval; @@ -43,7 +47,7 @@ function getRocketSequences() { let rocket_sequence = 1; -let settings = require('Storage').readJSON("cassioWatch.settings.json", true) || {}; +let settings = storage.readJSON("cassioWatch.settings.json", true) || {}; let rocketSpeed = settings.rocketSpeed || 700; delete settings; @@ -82,6 +86,43 @@ function DrawRocket() { if (rocket_sequence > 8) rocket_sequence = 1; } +function getTemperature(){ + try { + var weatherJson = storage.readJSON('weather.json'); + var weather = weatherJson.weather; + return Math.round(weather.temp-273.15); + + } catch(ex) { + print(ex) + return "?" + } +} + +function getSteps() { + var steps = 0; + try{ + if (WIDGETS.wpedom !== undefined) { + steps = WIDGETS.wpedom.getSteps(); + } else if (WIDGETS.activepedom !== undefined) { + steps = WIDGETS.activepedom.getSteps(); + } else { + steps = Bangle.getHealthStatus("day").steps; + } + } catch(ex) { + // In case we failed, we can only show 0 steps. + return "? k"; + } + + // Show always 2 digits. E.g. 1.5k if < 10000 otherwise 12k + if(steps > 10000){ + steps = Math.round(steps/1000); + } else { + steps = Math.round(steps/100) / 10; + } + return steps + "k"; +} + + function DrawScene() { g.reset(); g.clear(); @@ -94,17 +135,22 @@ function DrawScene() { g.drawString("Launching Process", 30, 20); g.setFont("8x12"); g.drawString("ACTIVATE", 40, 35); + + g.setFontAlign(0,-1); g.setFont("8x12", 2); - g.drawString("30", 142, 132); - g.drawString("55", 95, 98); - g.setFont("8x12", 1); - g.drawString(Bangle.getStepCount(), 143, 104); + g.drawString(getTemperature(), 155, 132); + g.drawString(Math.round(Bangle.getHealthStatus("last").bpm), 109, 98); + g.drawString(getSteps(), 158, 98); + + g.setFontAlign(-1,-1); ClockInterval = setInterval(DrawClock, 30000); DrawClock(); RocketInterval = setInterval(DrawRocket, rocketSpeed); DrawRocket(); BatteryInterval = setInterval(DrawBattery, 5 * 60000); DrawBattery(); + + for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} } Bangle.on("lcdPower", (on) => { @@ -123,6 +169,11 @@ Bangle.on("lock", (locked) => { } }); + +// Load widgets, but don't show them +Bangle.loadWidgets(); + + g.reset(); g.clear(); Bangle.setUI("clock"); From 8295cc794e6e074735a6cbbd47c9ec316318516d Mon Sep 17 00:00:00 2001 From: David Peer Date: Sat, 11 Jun 2022 17:58:15 +0200 Subject: [PATCH 13/41] Update version to 0.10 --- apps/cassioWatch/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/cassioWatch/metadata.json b/apps/cassioWatch/metadata.json index 70cd9c242..dabdc2c93 100644 --- a/apps/cassioWatch/metadata.json +++ b/apps/cassioWatch/metadata.json @@ -4,7 +4,7 @@ "description": "Animated Clock with Space Cassio Watch Style", "screenshots": [{ "url": "screens/screen_night.png" },{ "url": "screens/screen_day.png" }], "icon": "app.png", - "version": "0.9", + "version": "0.10", "type": "clock", "tags": "clock, weather, cassio, retro", "supports": ["BANGLEJS2"], From 3ec548c922de3d9f60b6f7db55d2c15d372e2ee2 Mon Sep 17 00:00:00 2001 From: Dennis Kueper Date: Sat, 11 Jun 2022 17:58:18 +0200 Subject: [PATCH 14/41] Prepare new version --- apps/hwid_a_battery_widget/ChangeLog | 3 ++- apps/hwid_a_battery_widget/metadata.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/hwid_a_battery_widget/ChangeLog b/apps/hwid_a_battery_widget/ChangeLog index 3d7b39737..9d5ee1264 100644 --- a/apps/hwid_a_battery_widget/ChangeLog +++ b/apps/hwid_a_battery_widget/ChangeLog @@ -3,4 +3,5 @@ 0.03: Support dark themes 0.04: Increase screen update rate when charging 0.05: Deleting Background - making Font larger -0.06: Fixing refresh issues \ No newline at end of file +0.06: Fixing refresh issues +0.07 \ No newline at end of file diff --git a/apps/hwid_a_battery_widget/metadata.json b/apps/hwid_a_battery_widget/metadata.json index d0cb5233a..38fd503a2 100644 --- a/apps/hwid_a_battery_widget/metadata.json +++ b/apps/hwid_a_battery_widget/metadata.json @@ -3,7 +3,7 @@ "name": "A Battery Widget (with percentage) - Hanks Mod", "shortName":"H Battery Widget", "icon": "widget.png", - "version":"0.06", + "version":"0.07", "type": "widget", "supports": ["BANGLEJS", "BANGLEJS2"], "readme": "README.md", From 99cdfdb8d48603baae38cf9c347b79a42653933d Mon Sep 17 00:00:00 2001 From: Dennis Kueper Date: Sat, 11 Jun 2022 18:15:27 +0200 Subject: [PATCH 15/41] Removing obsolete stuff --- apps/hworldclock/app.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/hworldclock/app.js b/apps/hworldclock/app.js index 8122371a6..1358b81d2 100644 --- a/apps/hworldclock/app.js +++ b/apps/hworldclock/app.js @@ -246,7 +246,6 @@ setInterval(drawSeconds, 1E3); Bangle.on('lcdPower',on=>{ if (on) { draw(); // draw immediately, queue redraw - drawSeconds(); // draw immediately, queue redraw setInterval(updatePos, 60*5E3); // refesh every 5 mins setInterval(drawSeconds, 1E3); updatePos(); @@ -262,5 +261,4 @@ Bangle.on('lcdPower',on=>{ }); // draw now -drawSeconds(); draw(); From b5c3607ca192bf3d9a37dd8b9fdf69572148e8ae Mon Sep 17 00:00:00 2001 From: Dennis Kueper Date: Sat, 11 Jun 2022 18:22:37 +0200 Subject: [PATCH 16/41] Version Info --- apps/hworldclock/app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/hworldclock/app.js b/apps/hworldclock/app.js index 1358b81d2..f724c35d9 100644 --- a/apps/hworldclock/app.js +++ b/apps/hworldclock/app.js @@ -129,6 +129,7 @@ function drawSeconds() { g.setColor("#22ff05"); //g.setFont(font, primaryTimeFontSize-3); g.drawString(`${seconds}`, xyCenterSeconds, yposTime+14, true); + queueDraw(); } function draw() { @@ -238,7 +239,7 @@ Bangle.setUI("clock"); Bangle.loadWidgets(); Bangle.drawWidgets(); updatePos(); -setInterval(drawSeconds, 1E3); +setInterval(drawSeconds, 1E3/2); From 3e0a5af414e57e39b55808e2d4a394e44c320fef Mon Sep 17 00:00:00 2001 From: Dennis Kueper Date: Sat, 11 Jun 2022 18:22:56 +0200 Subject: [PATCH 17/41] Version Info --- apps/hworldclock/ChangeLog | 1 + apps/hworldclock/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/hworldclock/ChangeLog b/apps/hworldclock/ChangeLog index 5b2a1edce..e67af806a 100644 --- a/apps/hworldclock/ChangeLog +++ b/apps/hworldclock/ChangeLog @@ -1 +1,2 @@ 0.15: Initial release - be patient as this is the first try :) +0.16: Fix timing \ No newline at end of file diff --git a/apps/hworldclock/metadata.json b/apps/hworldclock/metadata.json index e9e0175f5..4f3cd80cf 100644 --- a/apps/hworldclock/metadata.json +++ b/apps/hworldclock/metadata.json @@ -2,7 +2,7 @@ "id": "hworldclock", "name": "Hanks World Clock", "shortName": "Hanks World Clock", - "version": "0.15", + "version": "0.16", "description": "Current time zone plus up to three others", "allow_emulator":true, "icon": "app.png", From e1180c88d23cc21c48f99e0a9c1f8b9b25d14e78 Mon Sep 17 00:00:00 2001 From: David Peer Date: Sat, 11 Jun 2022 19:08:37 +0200 Subject: [PATCH 18/41] Use interval for animation and timeout for time to ensure that the time changes precisely. --- apps/cassioWatch/app.js | 90 ++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/apps/cassioWatch/app.js b/apps/cassioWatch/app.js index 49e23c2eb..75ec85503 100644 --- a/apps/cassioWatch/app.js +++ b/apps/cassioWatch/app.js @@ -1,15 +1,9 @@ const storage = require('Storage'); -const locale = require('locale'); require("Font6x12").add(Graphics); require("Font8x12").add(Graphics); require("Font7x11Numeric7Seg").add(Graphics); - -let ClockInterval; -let RocketInterval; -let BatteryInterval; - function bigThenSmall(big, small, x, y) { g.setFont("7x11Numeric7Seg", 2); g.drawString(big, x, y); @@ -18,16 +12,6 @@ function bigThenSmall(big, small, x, y) { g.drawString(small, x, y); } -function ClearIntervals(inoreclock) { - if (RocketInterval) clearInterval(RocketInterval); - if (BatteryInterval) clearInterval(BatteryInterval); - RocketInterval = undefined; - BatteryInterval = undefined; - if (inoreclock) return; - if (ClockInterval) clearInterval(ClockInterval); - ClockInterval = undefined; -} - function getBackgroundImage() { return require("heatshrink").decompress(atob("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")); } @@ -45,15 +29,31 @@ function getRocketSequences() { }; } -let rocket_sequence = 1; - +let rocketSequence = 1; let settings = storage.readJSON("cassioWatch.settings.json", true) || {}; let rocketSpeed = settings.rocketSpeed || 700; delete settings; -g.clear(); +// schedule a draw for the next minute +let rocketInterval; +var drawTimeout; +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} -function DrawClock() { + +function clearIntervals() { + if (rocketInterval) clearInterval(rocketInterval); + rocketInterval = undefined; + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; +} + +function drawClock() { g.setFont("7x11Numeric7Seg", 3); g.clearRect(80, 57, 170, 96); g.setColor(0, 255, 255); @@ -70,20 +70,20 @@ function DrawClock() { g.drawString(time < 10 ? "0" + time : time, 78, 137); } -function DrawBattery() { +function drawBattery() { bigThenSmall(E.getBattery(), "%", 135, 21); } -function DrawRocket() { +function drawRocket() { let Rocket = getRocketSequences(); g.clearRect(5, 62, 63, 115); g.setColor(0, 255, 255); g.drawRect(5, 62, 63, 115); g.fillRect(5, 62, 63, 115); - g.drawImage(Rocket[rocket_sequence], 5, 65, { scale: 0.7 }); + g.drawImage(Rocket[rocketSequence], 5, 65, { scale: 0.7 }); g.setColor(0, 0, 0); - rocket_sequence = rocket_sequence + 1; - if (rocket_sequence > 8) rocket_sequence = 1; + rocketSequence = rocketSequence + 1; + if(rocketSequence > 8) rocketSequence = 1; } function getTemperature(){ @@ -123,7 +123,9 @@ function getSteps() { } -function DrawScene() { +function draw() { + queueDraw(); + g.reset(); g.clear(); g.setColor(0, 255, 255); @@ -143,42 +145,36 @@ function DrawScene() { g.drawString(getSteps(), 158, 98); g.setFontAlign(-1,-1); - ClockInterval = setInterval(DrawClock, 30000); - DrawClock(); - RocketInterval = setInterval(DrawRocket, rocketSpeed); - DrawRocket(); - BatteryInterval = setInterval(DrawBattery, 5 * 60000); - DrawBattery(); + drawClock(); + drawRocket(); + rocketSequence -= 1; // This avoids a "jump" in the animation + drawBattery(); for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} } Bangle.on("lcdPower", (on) => { - if (!on) { - g.clear(); - ClearIntervals(true); + if (on) { + draw(); + } else { + clearIntervals(); } }); + Bangle.on("lock", (locked) => { - if (locked) { - ClearIntervals(true); - } else { - ClearIntervals(); - DrawScene(); + clearIntervals(); + draw(); + if (!locked) { + rocketInterval = setInterval(drawRocket, rocketSpeed); } }); // Load widgets, but don't show them Bangle.loadWidgets(); - +Bangle.setUI("clock"); g.reset(); g.clear(); -Bangle.setUI("clock"); -DrawScene(); - -if (Bangle.isLocked()) { - ClearIntervals(true); -} \ No newline at end of file +draw(); \ No newline at end of file From 1a3b8e5446a26a7a73973d4148c7fb920dcb81ce Mon Sep 17 00:00:00 2001 From: pebl-hank Date: Sat, 11 Jun 2022 23:08:03 +0200 Subject: [PATCH 19/41] Fix hours --- apps/hworldclock/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/hworldclock/app.js b/apps/hworldclock/app.js index f724c35d9..c37ec5714 100644 --- a/apps/hworldclock/app.js +++ b/apps/hworldclock/app.js @@ -163,7 +163,7 @@ function draw() { //g.setFont(font, primaryTimeFontSize); g.setFont("5x9Numeric7Seg",primaryTimeFontSize); g.setColor("#22ff05"); - g.drawString(`${doublenum(hours)}:${minutes}`, xyCenter-10, yposTime, true); + g.drawString(`${hours}:${minutes}`, xyCenter-10, yposTime, true); // am / PM ? if (_12hour){ From 223ee5043535dd61f2684dbf91048865f64a6f53 Mon Sep 17 00:00:00 2001 From: pancake Date: Sun, 12 Jun 2022 02:36:39 +0200 Subject: [PATCH 20/41] football: Upgrade to 1.01. Works with BTN1 --- apps/football/ChangeLog | 1 + apps/football/app.js | 76 +++++++++++++++++++++++++++++------------ 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/apps/football/ChangeLog b/apps/football/ChangeLog index 9b91672a5..66b9882cc 100644 --- a/apps/football/ChangeLog +++ b/apps/football/ChangeLog @@ -1 +1,2 @@ 1.00: Initial implementation +1.01: Bug fixes and performance and visual improvements diff --git a/apps/football/app.js b/apps/football/app.js index 8350bea88..d12f07e2b 100644 --- a/apps/football/app.js +++ b/apps/football/app.js @@ -1,4 +1,19 @@ +// globals. TODO: move into an object const digit = []; +let part = 0; +let endInc = 0; +var endGame = false; +let goalFrame = 0; +var stopped = true; +let score0 = 0; +let score1 = 0; +let inc = 0; +let msinc = 0; +let seq0 = 0; +let seq1 = 0; +let goaler = -1; +const w = g.getWidth(); +let owner = -1; const dash = { width: 75, @@ -6,6 +21,7 @@ const dash = { bpp: 1, buffer: require('heatshrink').decompress(atob('AH4A/AH4A/AH4A/AH4A/AB0D/4AB+AJEBAX/BAk/CQ8PCQ4kDCQoIDCQgkDCQgkECQgIE4ASHFxH8JRgSEEgYSEPJAkEAH4A/AH4A/AH4A/AH4A/ACg=')) }; + function loadDigits () { digit[0] = { width: 80, @@ -58,8 +74,8 @@ function loadDigits () { digit[7] = { width: 80, height: 128, - bpp: 1, - buffer: require('heatshrink').decompress(atob('AGUH/4AE/wJBgYJF/gJBgIJF+AeCBJN/BIngsAJBn4JE4HgBIMfBImBBIUPBIkDBIRQE/0HBIRQE/kPBIRQE/EfBIRQE+E/BIZQD8AJEKAfABYIJCKAYsBBIYADIAIJHKgIJHNAIJ/BP4J/BP4Jzg//4AJGgf/wAJGgP/BAwAB/wJIvgJInAJIiAJIAH5PPMZJ3JRZCfJWZLHJfM4J/BP4J/BP4JNg4JIgYJIgIJIgAJJv4JIn4JIj4JIh4JIeg4JIgYJIgIJIgAJJsAJIkAJIAH4AQA=')) + bpp: 4, + buffer : require("heatshrink").decompress(atob("AH4A/AEtVADdQE5Nf/4AayAnJgoma/J4LKDR2KKDZODUMadChKhiJwefE5RQXJwbxLKCxOEE5hQVJwgnNKCZOFE5pQTJwonOKCJOGE5xQRD4Q8EE5xQPJw4nPgFZzIAMqCdFE6IARJwgnhJwonhJwonhe5In/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4nlr4mE/NQE78FE4n1Ez5QGE0JQEJ0RQETsBQFJ0gABrJOkAH4A/AH4A/ADNZqAmkgv/yAnkr///JQjJwIABypOkAAP5J0oABUMJODKAShgEwhQiE/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/E/4n/AA+fE80JE8xQGE8JQFE8JQFE8RQEE8RQEE8ZQDE8ZQDE8hQCE8hQCE8pQBE8pQBE80JE80AE84A/AH4A/AH4A/AAQA==")) }; digit[8] = { @@ -170,9 +186,6 @@ const gol11 = { loadDigits(); -let goalFrame = 0; -let score0 = 0; -let score1 = 0; function printNumber (n, x, y, options) { if (n > 9 || n < -1) { @@ -197,13 +210,7 @@ function printNumber (n, x, y, options) { g.drawImage(img, x, y, options); } } -let inc = 0; -let msinc = 0; -let seq0 = 0; -let seq1 = 0; -let goaler = -1; -const w = g.getWidth(); -let owner = -1; + g.setBgColor(0, 0, 0); g.clear(); g.setColor(1, 1, 1); @@ -247,43 +254,63 @@ function onStop () { refresh(); refresh_ms(); } -var stopped = true; -Bangle.on('tap', function (pos) { - console.log('touch', pos); + +function onButtonPress() { + console.log('on.tap'); + setWatch(() => { + onButtonPress(); +}, BTN1); + Bangle.beep(); if (endGame) { - Bangle.beep(); score0 = 0; score1 = 0; seq0 = 0; seq1 = 0; + part = 0; inc = 0; msinc = 0; stopped = true; endGame = false; } else { if (inc == 0) { - autogame(); + // autogame(); + stopped = false; } else { onStop(); } } +} + +setWatch(() => { + onButtonPress(); +}, BTN1); +/*Bangle.on('tap', function () { + onButtonPress(); }); +*/ g.setFont12x20(3); -let part = 0; -let endInc = 0; -var endGame = false; + function refresh () { g.clear(); if (inc > 59) { inc = 0; part++; } + if (part >= 2 && inc > 30) { + part = 2; + Bangle.buzz(); + endGame = true; + endInc = inc; + inc = 0; + } if (inc > 44) { + inc = 0; if (part < 2) { part++; } if (part >= 2) { if (score0 != score1) { + Bangle.buzz(); endGame = true; endInc = inc; inc = 0; @@ -342,6 +369,12 @@ function refresh_pixels () { let bx = (owner == 0) ? w / 3 : w / 2; bx += 2; g.drawImage(frame4 ? ball0 : ball1, bx, 10, { scale: 5 }); + const liney = 60; + if (owner) { + g.drawLine(w-8, liney, 2*(w/3), liney); + } else { + g.drawLine(8, liney, w/3, liney); + } } let dots = 0; function refresh_dots () { @@ -437,4 +470,5 @@ function autogame () { } Bangle.setOptions({ lockTimeout: 0, backlightTimeout: 0 }); -autogame(); +// autogame(); + From b5f3930c41cc7f20a14f1f5ee19ce8d3bf969abd Mon Sep 17 00:00:00 2001 From: Marco H Date: Sun, 12 Jun 2022 11:24:29 +0200 Subject: [PATCH 21/41] Improve widget drawing code & fix some typos --- apps/widbaroalarm/ChangeLog | 2 +- apps/widbaroalarm/README.md | 4 ++-- apps/widbaroalarm/widget.js | 19 ++++++++++++++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/apps/widbaroalarm/ChangeLog b/apps/widbaroalarm/ChangeLog index 0afe4bc8b..86a902605 100644 --- a/apps/widbaroalarm/ChangeLog +++ b/apps/widbaroalarm/ChangeLog @@ -1,5 +1,5 @@ 0.01: Initial version -0.02: Do not warn multiple times for the same exceedance +0.02: Do not warn multiple times for the same exceed 0.03: Fix crash 0.04: Use Prompt with dismiss and pause Improve barometer value median calculation diff --git a/apps/widbaroalarm/README.md b/apps/widbaroalarm/README.md index 80ba35abc..59d91ff66 100644 --- a/apps/widbaroalarm/README.md +++ b/apps/widbaroalarm/README.md @@ -16,10 +16,10 @@ Get a notification when the pressure reaches defined thresholds. * Show widget: Enable/disable widget visibility * Buzz on alarm: Enable/disable buzzer on alarm * Dismiss delay: Delay added before the next alert if the alert is dismissed. From 5 to 60 min -* Pause delay: Same as Dismiss delay but longer (usefull for meetings and such). From 30 to 240 min +* Pause delay: Same as Dismiss delay but longer (useful for meetings and such). From 30 to 240 min ## Widget -The widget shows the last median pressure value +The widget shows two rows: pressure value of last measurement and pressure average of the the last three hours. ## Creator Marco ([myxor](https://github.com/myxor)) diff --git a/apps/widbaroalarm/widget.js b/apps/widbaroalarm/widget.js index 00456f42f..444b63ce6 100644 --- a/apps/widbaroalarm/widget.js +++ b/apps/widbaroalarm/widget.js @@ -227,11 +227,24 @@ draw: draw }; } - g.reset(); - if (setting("show") && medianPressure != undefined) { + if (setting("show")) { g.setFont("6x8", 1).setFontAlign(1, 0); - g.drawString(Math.round(medianPressure), this.x + 24, this.y + 6); + if (medianPressure == undefined) { + check(); + const x = this.x, y = this.y; + g.drawString("...", x + 24, y + 6); + setTimeout(function() { + g.setFont("6x8", 1).setFontAlign(1, 0); + g.drawString(Math.round(medianPressure), x + 24, y + 6); + }, 10000); + } else { + g.drawString(Math.round(medianPressure), this.x + 24, this.y + 6); + } + + if (threeHourAvrPressure != undefined && threeHourAvrPressure > 0) { + g.drawString(Math.round(threeHourAvrPressure), this.x + 24, this.y + 6 + 10); + } } } From 4dba48f0ace282a1b5ec8d21a7864db251be53db Mon Sep 17 00:00:00 2001 From: Marco H Date: Sun, 12 Jun 2022 17:12:06 +0200 Subject: [PATCH 22/41] Improve rais/drop calculation and handling --- apps/widbaroalarm/widget.js | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/apps/widbaroalarm/widget.js b/apps/widbaroalarm/widget.js index 444b63ce6..590c2f316 100644 --- a/apps/widbaroalarm/widget.js +++ b/apps/widbaroalarm/widget.js @@ -103,8 +103,6 @@ } else { saveSetting("lastLowWarningTs", 0); } - } else { - saveSetting("lastLowWarningTs", 0); } if (setting("highalarm")) { @@ -117,8 +115,6 @@ } else { saveSetting("lastHighWarningTs", 0); } - } else { - saveSetting("lastHighWarningTs", 0); } if (history3.length > 0 && !alreadyWarned) { @@ -127,21 +123,22 @@ const raise3halarm = setting("raise3halarm"); if (drop3halarm > 0 || raise3halarm > 0) { // we need at least 30min of data for reliable detection - if (history3[0]["ts"] > ts - (30 * 60)) { + const diffDateAge = Math.abs(history3[0]["ts"] - ts); + if (diffDateAge < 10 * 60) { // todo change to 1800 return; } // Get oldest entry: const oldestPressure = history3[0]["p"]; if (oldestPressure != undefined && oldestPressure > 0) { - const diff = oldestPressure - pressure; + const diffPressure = Math.abs(oldestPressure - pressure); // drop alarm if (drop3halarm > 0 && oldestPressure > pressure) { - if (Math.abs(diff) > drop3halarm) { + if (diffPressure > drop3halarm) { if (doWeNeedToWarn("lastDropWarningTs")) { - showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " + - Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure drop", "lastDropWarningTs"); + showAlarm((Math.round(diffPressure * 10) / 10) + " hPa/3h from " + + Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "lastDropWarningTs"); } } else { saveSetting("lastDropWarningTs", 0); @@ -152,10 +149,10 @@ // raise alarm if (raise3halarm > 0 && oldestPressure < pressure) { - if (Math.abs(diff) > raise3halarm) { + if (diffPressure > raise3halarm) { if (doWeNeedToWarn("lastRaiseWarningTs")) { - showAlarm((Math.round(Math.abs(diff) * 10) / 10) + " hPa/3h from " + - Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "Pressure raise", "lastRaiseWarningTs"); + showAlarm((Math.round(diffPressure * 10) / 10) + " hPa/3h from " + + Math.round(oldestPressure) + " to " + Math.round(pressure) + " hPa", "lastRaiseWarningTs"); } } else { saveSetting("lastRaiseWarningTs", 0); From da9261446fa3da51f25be7fd738a3e4cec4fcf60 Mon Sep 17 00:00:00 2001 From: Marco H Date: Sun, 12 Jun 2022 17:34:30 +0200 Subject: [PATCH 23/41] Further improvements --- apps/widbaroalarm/widget.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/apps/widbaroalarm/widget.js b/apps/widbaroalarm/widget.js index 590c2f316..46ac9d480 100644 --- a/apps/widbaroalarm/widget.js +++ b/apps/widbaroalarm/widget.js @@ -49,9 +49,11 @@ saveSetting(key, tsNow); } if (v == 2) { + // save timestamp of the future so that we do not warn again for the same event until then saveSetting(key, tsNow + 60 * setting('dismissDelayMin')); } if (v == 3) { + // save timestamp of the future so that we do not warn again for the same event until then saveSetting(key, tsNow + 60 * setting('pauseDelayMin')); } load(); @@ -196,8 +198,8 @@ var mid = median.length >> 1; medianPressure = Math.round(E.sum(median.slice(mid - 4, mid + 5)) / 9); if (medianPressure > 0) { - turnOff(); - checkForAlarms(medianPressure); + turnOff(); + checkForAlarms(medianPressure); } } }); @@ -208,7 +210,8 @@ } function turnOff() { - Bangle.setBarometerPower(false, "widbaroalarm"); + if (Bangle.isBarometerOn()) + Bangle.setBarometerPower(false, "widbaroalarm"); } function reload() { @@ -228,15 +231,16 @@ if (setting("show")) { g.setFont("6x8", 1).setFontAlign(1, 0); if (medianPressure == undefined) { - check(); - const x = this.x, y = this.y; - g.drawString("...", x + 24, y + 6); - setTimeout(function() { - g.setFont("6x8", 1).setFontAlign(1, 0); - g.drawString(Math.round(medianPressure), x + 24, y + 6); - }, 10000); + check(); + const x = this.x, + y = this.y; + g.drawString("...", x + 24, y + 6); + setTimeout(function() { + g.setFont("6x8", 1).setFontAlign(1, 0); + g.drawString(Math.round(medianPressure), x + 24, y + 6); + }, 10000); } else { - g.drawString(Math.round(medianPressure), this.x + 24, this.y + 6); + g.drawString(Math.round(medianPressure), this.x + 24, this.y + 6); } if (threeHourAvrPressure != undefined && threeHourAvrPressure > 0) { @@ -245,7 +249,6 @@ } } - check(); if (interval > 0) { setInterval(check, interval * 60000); } From 02bb0ab481cdcf26699c901ecdd140d088b7922a Mon Sep 17 00:00:00 2001 From: Marco H Date: Sun, 12 Jun 2022 17:46:25 +0200 Subject: [PATCH 24/41] Calculate 3h average only if data is available --- apps/widbaroalarm/widget.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/widbaroalarm/widget.js b/apps/widbaroalarm/widget.js index 46ac9d480..d8eb43995 100644 --- a/apps/widbaroalarm/widget.js +++ b/apps/widbaroalarm/widget.js @@ -171,11 +171,15 @@ storage.writeJSON(LOG_FILE, history3); // calculate 3h average for widget - let sum = 0; - for (let i = 0; i < history3.length; i++) { - sum += history3[i]["p"]; + if (history3.length > 0) { + let sum = 0; + for (let i = 0; i < history3.length; i++) { + sum += history3[i]["p"]; + } + threeHourAvrPressure = sum / history3.length; + } else { + threeHourAvrPressure = undefined; } - threeHourAvrPressure = sum / history3.length; } From d7e1cf704decf53bf17f4517fff87bbc09604c32 Mon Sep 17 00:00:00 2001 From: Hank Date: Sun, 12 Jun 2022 17:48:07 +0200 Subject: [PATCH 25/41] Fix hours --- apps/hworldclock/ChangeLog | 3 ++- apps/hworldclock/metadata.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/hworldclock/ChangeLog b/apps/hworldclock/ChangeLog index e67af806a..64b2be430 100644 --- a/apps/hworldclock/ChangeLog +++ b/apps/hworldclock/ChangeLog @@ -1,2 +1,3 @@ 0.15: Initial release - be patient as this is the first try :) -0.16: Fix timing \ No newline at end of file +0.16: Fix timing +0.17: Fix hours \ No newline at end of file diff --git a/apps/hworldclock/metadata.json b/apps/hworldclock/metadata.json index 4f3cd80cf..794992d2f 100644 --- a/apps/hworldclock/metadata.json +++ b/apps/hworldclock/metadata.json @@ -2,7 +2,7 @@ "id": "hworldclock", "name": "Hanks World Clock", "shortName": "Hanks World Clock", - "version": "0.16", + "version": "0.17", "description": "Current time zone plus up to three others", "allow_emulator":true, "icon": "app.png", From 85a52afbf55725bd999bb94e2e8c1b929a3cc4ed Mon Sep 17 00:00:00 2001 From: Stiralbios Date: Sun, 12 Jun 2022 17:50:43 +0200 Subject: [PATCH 26/41] Fix the const reassign bug (cutting edge firmware) and better indentation and scoping --- apps/activityreminder/ChangeLog | 1 + apps/activityreminder/app.js | 81 +++++++------- apps/activityreminder/boot.js | 122 +++++++++++---------- apps/activityreminder/lib.js | 10 +- apps/activityreminder/metadata.json | 2 +- apps/activityreminder/settings.js | 164 ++++++++++++++-------------- 6 files changed, 191 insertions(+), 189 deletions(-) diff --git a/apps/activityreminder/ChangeLog b/apps/activityreminder/ChangeLog index 4edb72aa5..da897b899 100644 --- a/apps/activityreminder/ChangeLog +++ b/apps/activityreminder/ChangeLog @@ -4,3 +4,4 @@ 0.04: Obey system quiet mode 0.05: Battery optimisation, add the pause option, bug fixes 0.06: Add a temperature threshold to detect (and not alert) if the BJS isn't worn. Better support for the peoples using the app at night +0.07: Fix bug on the cutting edge firmware \ No newline at end of file diff --git a/apps/activityreminder/app.js b/apps/activityreminder/app.js index f3d72976e..52dec2928 100644 --- a/apps/activityreminder/app.js +++ b/apps/activityreminder/app.js @@ -1,42 +1,43 @@ -function drawAlert() { - E.showPrompt("Inactivity detected", { - title: "Activity reminder", - buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 } - }).then(function (v) { - if (v == 1) { - activityreminder_data.okDate = new Date(); +(function () { + function drawAlert() { + E.showPrompt("Inactivity detected", { + title: "Activity reminder", + buttons: { "Ok": 1, "Dismiss": 2, "Pause": 3 } + }).then(function (v) { + if (v == 1) { + activityreminder_data.okDate = new Date(); + } + if (v == 2) { + activityreminder_data.dismissDate = new Date(); + } + if (v == 3) { + activityreminder_data.pauseDate = new Date(); + } + activityreminder.saveData(activityreminder_data); + load(); + }); + + // Obey system quiet mode: + if (!(storage.readJSON('setting.json', 1) || {}).quiet) { + Bangle.buzz(400); + } + setTimeout(load, 20000); } - if (v == 2) { - activityreminder_data.dismissDate = new Date(); + + function run() { + if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) { + drawAlert(); + } else { + eval(storage.read("activityreminder.settings.js"))(() => load()); + } } - if (v == 3) { - activityreminder_data.pauseDate = new Date(); - } - activityreminder.saveData(activityreminder_data); - load(); - }); - - // Obey system quiet mode: - if (!(storage.readJSON('setting.json', 1) || {}).quiet) { - Bangle.buzz(400); - } - setTimeout(load, 20000); -} - -function run() { - if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) { - drawAlert(); - } else { - eval(storage.read("activityreminder.settings.js"))(() => load()); - } -} - - -const activityreminder = require("activityreminder"); -const storage = require("Storage"); -g.clear(); -Bangle.loadWidgets(); -Bangle.drawWidgets(); -const activityreminder_settings = activityreminder.loadSettings(); -const activityreminder_data = activityreminder.loadData(); -run(); + + const activityreminder = require("activityreminder"); + const storage = require("Storage"); + g.clear(); + Bangle.loadWidgets(); + Bangle.drawWidgets(); + const activityreminder_settings = activityreminder.loadSettings(); + const activityreminder_data = activityreminder.loadData(); + run(); +})(); \ No newline at end of file diff --git a/apps/activityreminder/boot.js b/apps/activityreminder/boot.js index 4ae9548c2..86de0e901 100644 --- a/apps/activityreminder/boot.js +++ b/apps/activityreminder/boot.js @@ -1,65 +1,67 @@ -function run() { - if (isNotWorn()) return; - let now = new Date(); - let h = now.getHours(); +(function () { + function run() { + if (isNotWorn()) return; + let now = new Date(); + let h = now.getHours(); - if (isDuringAlertHours(h)) { - let health = Bangle.getHealthStatus("day"); - if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed - || health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch - activityreminder_data.stepsOnDate = health.steps; - activityreminder_data.stepsDate = now; + if (isDuringAlertHours(h)) { + let health = Bangle.getHealthStatus("day"); + if (health.steps - activityreminder_data.stepsOnDate >= activityreminder_settings.minSteps // more steps made than needed + || health.steps < activityreminder_data.stepsOnDate) { // new day or reboot of the watch + activityreminder_data.stepsOnDate = health.steps; + activityreminder_data.stepsDate = now; + activityreminder.saveData(activityreminder_data); + /* todo in a futur release + Add settimer to trigger like 30 secs after going in this part cause the person have been walking + (pass some argument to run() to handle long walks and not triggering so often) + */ + } + + if (activityreminder.mustAlert(activityreminder_data, activityreminder_settings)) { + load('activityreminder.app.js'); + } + } + + } + + function isNotWorn() { + return (Bangle.isCharging() || activityreminder_settings.tempThreshold >= E.getTemperature()); + } + + function isDuringAlertHours(h) { + if (activityreminder_settings.startHour < activityreminder_settings.endHour) { // not passing through midnight + return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour) + } else { // passing through midnight + return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour) + } + } + + Bangle.on('midnight', function () { + /* + Usefull trick to have the app working smothly for people using it at night + */ + let now = new Date(); + let h = now.getHours(); + if (activityreminder_settings.enabled && isDuringAlertHours(h)) { + // updating only the steps and keeping the original stepsDate on purpose + activityreminder_data.stepsOnDate = 0; activityreminder.saveData(activityreminder_data); - /* todo in a futur release - Add settimer to trigger like 30 secs after going in this part cause the person have been walking - (pass some argument to run() to handle long walks and not triggering so often) - */ } - - if(activityreminder.mustAlert(activityreminder_data, activityreminder_settings)){ - load('activityreminder.app.js'); + }); + + + const activityreminder = require("activityreminder"); + const activityreminder_settings = activityreminder.loadSettings(); + if (activityreminder_settings.enabled) { + const activityreminder_data = activityreminder.loadData(); + if (activityreminder_data.firstLoad) { + activityreminder_data.firstLoad = false; + activityreminder.saveData(activityreminder_data); } + setInterval(run, 60000); + /* todo in a futur release + increase setInterval time to something that is still sensible (5 mins ?) + when we added a settimer + */ } - -} - -function isNotWorn() { - return (Bangle.isCharging() || activityreminder_settings.tempThreshold >= E.getTemperature()); -} - -function isDuringAlertHours(h) { - if(activityreminder_settings.startHour < activityreminder_settings.endHour){ // not passing through midnight - return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour) - } else{ // passing through midnight - return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour) - } -} - -Bangle.on('midnight', function() { - /* - Usefull trick to have the app working smothly for people using it at night - */ - let now = new Date(); - let h = now.getHours(); - if (activityreminder_settings.enabled && isDuringAlertHours(h)){ - // updating only the steps and keeping the original stepsDate on purpose - activityreminder_data.stepsOnDate = 0; - activityreminder.saveData(activityreminder_data); - } -}); - -const activityreminder = require("activityreminder"); -const activityreminder_settings = activityreminder.loadSettings(); -if (activityreminder_settings.enabled) { - const activityreminder_data = activityreminder.loadData(); - if(activityreminder_data.firstLoad){ - activityreminder_data.firstLoad = false; - activityreminder.saveData(activityreminder_data); - } - setInterval(run, 60000); - /* todo in a futur release - increase setInterval time to something that is still sensible (5 mins ?) - when we added a settimer - */ -} - +})(); diff --git a/apps/activityreminder/lib.js b/apps/activityreminder/lib.js index 08fffd5f4..e0f7caca3 100644 --- a/apps/activityreminder/lib.js +++ b/apps/activityreminder/lib.js @@ -1,5 +1,3 @@ -const storage = require("Storage"); - exports.loadSettings = function () { return Object.assign({ enabled: true, @@ -10,15 +8,15 @@ exports.loadSettings = function () { pauseDelayMin: 120, minSteps: 50, tempThreshold: 27 - }, storage.readJSON("activityreminder.s.json", true) || {}); + }, require("Storage").readJSON("activityreminder.s.json", true) || {}); }; exports.writeSettings = function (settings) { - storage.writeJSON("activityreminder.s.json", settings); + require("Storage").writeJSON("activityreminder.s.json", settings); }; exports.saveData = function (data) { - storage.writeJSON("activityreminder.data.json", data); + require("Storage").writeJSON("activityreminder.data.json", data); }; exports.loadData = function () { @@ -31,7 +29,7 @@ exports.loadData = function () { dismissDate: new Date(1970), pauseDate: new Date(1970), }, - storage.readJSON("activityreminder.data.json") || {}); + require("Storage").readJSON("activityreminder.data.json") || {}); if(typeof(data.stepsDate) == "string") data.stepsDate = new Date(data.stepsDate); diff --git a/apps/activityreminder/metadata.json b/apps/activityreminder/metadata.json index 752c6c101..fb9423e7c 100644 --- a/apps/activityreminder/metadata.json +++ b/apps/activityreminder/metadata.json @@ -3,7 +3,7 @@ "name": "Activity Reminder", "shortName":"Activity Reminder", "description": "A reminder to take short walks for the ones with a sedentary lifestyle", - "version":"0.06", + "version":"0.07", "icon": "app.png", "type": "app", "tags": "tool,activity", diff --git a/apps/activityreminder/settings.js b/apps/activityreminder/settings.js index f25697de0..b4d288500 100644 --- a/apps/activityreminder/settings.js +++ b/apps/activityreminder/settings.js @@ -1,85 +1,85 @@ (function (back) { - // Load settings - const activityreminder = require("activityreminder"); - const settings = activityreminder.loadSettings(); + // Load settings + const activityreminder = require("activityreminder"); + const settings = activityreminder.loadSettings(); - // Show the menu - E.showMenu({ - "": { "title": "Activity Reminder" }, - "< Back": () => back(), - 'Enable': { - value: settings.enabled, - format: v => v ? "Yes" : "No", - onchange: v => { - settings.enabled = v; - activityreminder.writeSettings(settings); - } - }, - 'Start hour': { - value: settings.startHour, - min: 0, max: 24, - onchange: v => { - settings.startHour = v; - activityreminder.writeSettings(settings); - } - }, - 'End hour': { - value: settings.endHour, - min: 0, max: 24, - onchange: v => { - settings.endHour = v; - activityreminder.writeSettings(settings); - } - }, - 'Max inactivity': { - value: settings.maxInnactivityMin, - min: 15, max: 120, - onchange: v => { - settings.maxInnactivityMin = v; - activityreminder.writeSettings(settings); - }, - format: x => { - return x + " min"; - } - }, - 'Dismiss delay': { - value: settings.dismissDelayMin, - min: 5, max: 60, - onchange: v => { - settings.dismissDelayMin = v; - activityreminder.writeSettings(settings); - }, - format: x => { - return x + " min"; - } - }, - 'Pause delay': { - value: settings.pauseDelayMin, - min: 30, max: 240, step: 5, - onchange: v => { - settings.pauseDelayMin = v; - activityreminder.writeSettings(settings); - }, - format: x => { - return x + " min"; - } - }, - 'Min steps': { - value: settings.minSteps, - min: 10, max: 500, step: 10, - onchange: v => { - settings.minSteps = v; - activityreminder.writeSettings(settings); - } - }, - 'Temp Threshold': { - value: settings.tempThreshold, - min: 20, max: 40, step: 0.5, - format: v => v + "°C", - onchange: v => { - settings.tempThreshold = v; - activityreminder.writeSettings(settings); - } - } - }); + // Show the menu + E.showMenu({ + "": { "title": "Activity Reminder" }, + "< Back": () => back(), + 'Enable': { + value: settings.enabled, + format: v => v ? "Yes" : "No", + onchange: v => { + settings.enabled = v; + activityreminder.writeSettings(settings); + } + }, + 'Start hour': { + value: settings.startHour, + min: 0, max: 24, + onchange: v => { + settings.startHour = v; + activityreminder.writeSettings(settings); + } + }, + 'End hour': { + value: settings.endHour, + min: 0, max: 24, + onchange: v => { + settings.endHour = v; + activityreminder.writeSettings(settings); + } + }, + 'Max inactivity': { + value: settings.maxInnactivityMin, + min: 15, max: 120, + onchange: v => { + settings.maxInnactivityMin = v; + activityreminder.writeSettings(settings); + }, + format: x => { + return x + " min"; + } + }, + 'Dismiss delay': { + value: settings.dismissDelayMin, + min: 5, max: 60, + onchange: v => { + settings.dismissDelayMin = v; + activityreminder.writeSettings(settings); + }, + format: x => { + return x + " min"; + } + }, + 'Pause delay': { + value: settings.pauseDelayMin, + min: 30, max: 240, step: 5, + onchange: v => { + settings.pauseDelayMin = v; + activityreminder.writeSettings(settings); + }, + format: x => { + return x + " min"; + } + }, + 'Min steps': { + value: settings.minSteps, + min: 10, max: 500, step: 10, + onchange: v => { + settings.minSteps = v; + activityreminder.writeSettings(settings); + } + }, + 'Temp Threshold': { + value: settings.tempThreshold, + min: 20, max: 40, step: 0.5, + format: v => v + "°C", + onchange: v => { + settings.tempThreshold = v; + activityreminder.writeSettings(settings); + } + } + }); }) From 6ef45a4258e1326d561ba8f2512157cd954d111a Mon Sep 17 00:00:00 2001 From: David Peer Date: Sun, 12 Jun 2022 18:00:41 +0200 Subject: [PATCH 27/41] Minor fix --- apps/cassioWatch/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/cassioWatch/app.js b/apps/cassioWatch/app.js index 75ec85503..41515b48c 100644 --- a/apps/cassioWatch/app.js +++ b/apps/cassioWatch/app.js @@ -147,9 +147,9 @@ function draw() { g.setFontAlign(-1,-1); drawClock(); drawRocket(); - rocketSequence -= 1; // This avoids a "jump" in the animation drawBattery(); + // Hide widgets for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} } From d734f921180d2d1fd094dfb17e8b99d7238a339e Mon Sep 17 00:00:00 2001 From: David Peer Date: Sun, 12 Jun 2022 18:01:47 +0200 Subject: [PATCH 28/41] Show steps in k. --- apps/cassioWatch/app.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/cassioWatch/app.js b/apps/cassioWatch/app.js index 41515b48c..6bbb9e823 100644 --- a/apps/cassioWatch/app.js +++ b/apps/cassioWatch/app.js @@ -113,12 +113,7 @@ function getSteps() { return "? k"; } - // Show always 2 digits. E.g. 1.5k if < 10000 otherwise 12k - if(steps > 10000){ - steps = Math.round(steps/1000); - } else { - steps = Math.round(steps/100) / 10; - } + steps = Math.round(steps/1000); return steps + "k"; } From 749cb2fc2a9698b7f7e0c11988f065af65f8bc0a Mon Sep 17 00:00:00 2001 From: Hank Date: Sun, 12 Jun 2022 19:38:40 +0200 Subject: [PATCH 29/41] 0.18: Code cleanup and major changes with seconds timing. New feature: if watch is locked, seconds get refreshed every 10 seconds. --- apps/hworldclock/ChangeLog | 3 +- apps/hworldclock/README.md | 1 + apps/hworldclock/app.js | 126 ++++++++++++++++++++++++++++----- apps/hworldclock/metadata.json | 2 +- 4 files changed, 113 insertions(+), 19 deletions(-) diff --git a/apps/hworldclock/ChangeLog b/apps/hworldclock/ChangeLog index 64b2be430..30d729cbf 100644 --- a/apps/hworldclock/ChangeLog +++ b/apps/hworldclock/ChangeLog @@ -1,3 +1,4 @@ 0.15: Initial release - be patient as this is the first try :) 0.16: Fix timing -0.17: Fix hours \ No newline at end of file +0.17: Fix hours +0.18: Code cleanup and major changes with seconds timing. New feature: if watch is locked, seconds get refreshed every 10 seconds. \ No newline at end of file diff --git a/apps/hworldclock/README.md b/apps/hworldclock/README.md index 40af71ee3..0f4f9296c 100644 --- a/apps/hworldclock/README.md +++ b/apps/hworldclock/README.md @@ -2,6 +2,7 @@ In addition to the main clock and date in your current location, you can add up to three other locations. Great for travel or remote working. Additionally we show the sunset/sunrise and seconds for the current location and the day name is shown in your locale. +If watch is locked, seconds get refreshed every 10 seconds. ![](hworldclock.png) diff --git a/apps/hworldclock/app.js b/apps/hworldclock/app.js index c37ec5714..6d16e2295 100644 --- a/apps/hworldclock/app.js +++ b/apps/hworldclock/app.js @@ -31,6 +31,8 @@ const yposWorld = big ? 170 : 120; const OFFSET_TIME_ZONE = 0; const OFFSET_HOURS = 1; +var PosInterval = 0; + var offsets = require("Storage").readJSON("hworldclock.settings.json") || []; //=======Sun @@ -79,6 +81,7 @@ var _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||fal // timeout used to update every minute var drawTimeout; var drawTimeoutSeconds; +var secondsTimeout; g.setBgColor(0, 0, 0); @@ -91,6 +94,16 @@ function queueDraw() { }, 60000 - (Date.now() % 60000)); } +// schedule a draw for the next second +function queueDrawSeconds() { + if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); + drawTimeoutSeconds = setTimeout(function() { + drawTimeoutSeconds = undefined; + drawSeconds(); + //console.log("TO: " + secondsTimeout); + }, secondsTimeout - (Date.now() % secondsTimeout)); +} + function doublenum(x) { return x < 10 ? "0" + x : "" + x; } @@ -127,9 +140,13 @@ function drawSeconds() { g.setFont("5x9Numeric7Seg",primaryTimeFontSize - 3); g.setColor("#22ff05"); - //g.setFont(font, primaryTimeFontSize-3); + //console.log("---"); + //console.log(seconds); + if (Bangle.isLocked()) seconds = seconds.slice(0, -1) + ':::'; // we use :: as the font does not have an x + //console.log(seconds); g.drawString(`${seconds}`, xyCenterSeconds, yposTime+14, true); - queueDraw(); + queueDrawSeconds(); + } function draw() { @@ -179,7 +196,7 @@ function draw() { // draw Day, name of month, Date //DATE var localDate = require("locale").date(new Date(), 1); - localDate = localDate.substring(0, localDate.length - 5) + localDate = localDate.substring(0, localDate.length - 5); g.setFont("Vector", 17); g.drawString(require("locale").dow(new Date(), 1).toUpperCase() + ", " + localDate, xyCenter, yposDate, true); @@ -230,36 +247,111 @@ function draw() { g.drawString(`v${set}`, xcol2, 3 + yposWorld + 3 * 15, true); // draw riseset queueDraw(); + queueDrawSeconds(); } // clean app screen g.clear(); + // Show launcher when button pressed Bangle.setUI("clock"); Bangle.loadWidgets(); Bangle.drawWidgets(); -updatePos(); -setInterval(drawSeconds, 1E3/2); +// draw immediately at first, queue update +draw(); -// Stop updates when LCD is off, restart when on -Bangle.on('lcdPower',on=>{ - if (on) { - draw(); // draw immediately, queue redraw - setInterval(updatePos, 60*5E3); // refesh every 5 mins - setInterval(drawSeconds, 1E3); - updatePos(); - } else { // stop draw timer + +if (!Bangle.isLocked()) { // Initial state + if (PosInterval != 0) clearInterval(PosInterval); + PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins + + secondsTimeout = 1000; if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); drawTimeout = undefined; drawTimeoutSeconds = undefined; - setInterval(updatePos, 60*50E3); // refesh every 50 mins - setInterval(drawSeconds, 10E3); + + draw(); // draw immediately, queue redraw + updatePos(); + }else{ + secondsTimeout = 10 * 1000; + if (drawTimeout) clearTimeout(drawTimeout); + if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); + drawTimeout = undefined; + drawTimeoutSeconds = undefined; + + if (PosInterval != 0) clearInterval(PosInterval); + PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins + draw(); // draw immediately, queue redraw + updatePos(); + } + + +// Stop updates when LCD is off, restart when on +Bangle.on('lcdPower',on=>{ + if (on) { + if (PosInterval != 0) clearInterval(PosInterval); + + PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins + secondsTimeout = 1000; + if (drawTimeout) clearTimeout(drawTimeout); + if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); + drawTimeout = undefined; + drawTimeoutSeconds = undefined; + draw(); // draw immediately, queue redraw + updatePos(); + } else { // stop draw timer + + secondsTimeout = 1000 * 60; + if (drawTimeout) clearTimeout(drawTimeout); + if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); + drawTimeout = undefined; + drawTimeoutSeconds = undefined; + + if (PosInterval != 0) clearInterval(PosInterval); + PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins + draw(); // draw immediately, queue redraw updatePos(); } }); -// draw now -draw(); + +Bangle.on('lock',on=>{ + if (!on) { // UNlocked + + if (PosInterval != 0) clearInterval(PosInterval); + PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins + + secondsTimeout = 1000; + if (drawTimeout) clearTimeout(drawTimeout); + if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); + drawTimeout = undefined; + drawTimeoutSeconds = undefined; + + draw(); // draw immediately, queue redraw + updatePos(); + }else{ // locked + + secondsTimeout = 10 * 1000; + if (drawTimeout) clearTimeout(drawTimeout); + if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); + drawTimeout = undefined; + drawTimeoutSeconds = undefined; + + + if (PosInterval != 0) clearInterval(PosInterval); + PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins + draw(); // draw immediately, queue redraw + updatePos(); + } + }); + + + + + + + + diff --git a/apps/hworldclock/metadata.json b/apps/hworldclock/metadata.json index 794992d2f..9eb95a0ed 100644 --- a/apps/hworldclock/metadata.json +++ b/apps/hworldclock/metadata.json @@ -2,7 +2,7 @@ "id": "hworldclock", "name": "Hanks World Clock", "shortName": "Hanks World Clock", - "version": "0.17", + "version": "0.18", "description": "Current time zone plus up to three others", "allow_emulator":true, "icon": "app.png", From 516814cdf80940d0c253d7ee4d4a7e6c11657157 Mon Sep 17 00:00:00 2001 From: Hank Date: Sun, 12 Jun 2022 19:42:48 +0200 Subject: [PATCH 30/41] Fix spaces --- apps/hworldclock/app.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/hworldclock/app.js b/apps/hworldclock/app.js index 6d16e2295..6101d4427 100644 --- a/apps/hworldclock/app.js +++ b/apps/hworldclock/app.js @@ -277,11 +277,11 @@ if (!Bangle.isLocked()) { // Initial state updatePos(); }else{ secondsTimeout = 10 * 1000; - if (drawTimeout) clearTimeout(drawTimeout); + if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); drawTimeout = undefined; drawTimeoutSeconds = undefined; - + if (PosInterval != 0) clearInterval(PosInterval); PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins draw(); // draw immediately, queue redraw @@ -333,13 +333,12 @@ Bangle.on('lock',on=>{ draw(); // draw immediately, queue redraw updatePos(); }else{ // locked - + secondsTimeout = 10 * 1000; - if (drawTimeout) clearTimeout(drawTimeout); + if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); drawTimeout = undefined; drawTimeoutSeconds = undefined; - if (PosInterval != 0) clearInterval(PosInterval); PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins From 59ee5b1a782d84f4e11a4d1bc94618820e728e8b Mon Sep 17 00:00:00 2001 From: Stiralbios Date: Sun, 12 Jun 2022 19:51:46 +0200 Subject: [PATCH 31/41] Moving stuff around to be sure to instanciate the variable before even referencing them in functions --- apps/activityreminder/app.js | 13 ++++++++----- apps/activityreminder/boot.js | 20 ++++++++++++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/apps/activityreminder/app.js b/apps/activityreminder/app.js index 52dec2928..95bdfe6f0 100644 --- a/apps/activityreminder/app.js +++ b/apps/activityreminder/app.js @@ -1,4 +1,10 @@ (function () { + + const activityreminder = require("activityreminder"); + const storage = require("Storage"); + const activityreminder_settings = activityreminder.loadSettings(); + const activityreminder_data = activityreminder.loadData(); + function drawAlert() { E.showPrompt("Inactivity detected", { title: "Activity reminder", @@ -31,13 +37,10 @@ eval(storage.read("activityreminder.settings.js"))(() => load()); } } - - const activityreminder = require("activityreminder"); - const storage = require("Storage"); + g.clear(); Bangle.loadWidgets(); Bangle.drawWidgets(); - const activityreminder_settings = activityreminder.loadSettings(); - const activityreminder_data = activityreminder.loadData(); run(); + })(); \ No newline at end of file diff --git a/apps/activityreminder/boot.js b/apps/activityreminder/boot.js index 86de0e901..c87dde3b2 100644 --- a/apps/activityreminder/boot.js +++ b/apps/activityreminder/boot.js @@ -1,4 +1,15 @@ (function () { + + const activityreminder = require("activityreminder"); + const activityreminder_settings = activityreminder.loadSettings(); + if (activityreminder_settings.enabled) { + const activityreminder_data = activityreminder.loadData(); + if (activityreminder_data.firstLoad) { + activityreminder_data.firstLoad = false; + activityreminder.saveData(activityreminder_data); + } + } + function run() { if (isNotWorn()) return; let now = new Date(); @@ -49,15 +60,8 @@ } }); - - const activityreminder = require("activityreminder"); - const activityreminder_settings = activityreminder.loadSettings(); + if (activityreminder_settings.enabled) { - const activityreminder_data = activityreminder.loadData(); - if (activityreminder_data.firstLoad) { - activityreminder_data.firstLoad = false; - activityreminder.saveData(activityreminder_data); - } setInterval(run, 60000); /* todo in a futur release increase setInterval time to something that is still sensible (5 mins ?) From 9b71b107cfb9ab05aeedbbe98a396933d223d209 Mon Sep 17 00:00:00 2001 From: Stiralbios Date: Sun, 12 Jun 2022 20:05:32 +0200 Subject: [PATCH 32/41] Use let instead of const on object that get updated --- apps/activityreminder/app.js | 4 ++-- apps/activityreminder/boot.js | 4 ++-- apps/activityreminder/lib.js | 2 +- apps/activityreminder/settings.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/activityreminder/app.js b/apps/activityreminder/app.js index 95bdfe6f0..c2b626fb3 100644 --- a/apps/activityreminder/app.js +++ b/apps/activityreminder/app.js @@ -1,9 +1,9 @@ (function () { - + // load variable before defining functions cause it can trigger a ReferenceError const activityreminder = require("activityreminder"); const storage = require("Storage"); const activityreminder_settings = activityreminder.loadSettings(); - const activityreminder_data = activityreminder.loadData(); + let activityreminder_data = activityreminder.loadData(); function drawAlert() { E.showPrompt("Inactivity detected", { diff --git a/apps/activityreminder/boot.js b/apps/activityreminder/boot.js index c87dde3b2..4d6847a20 100644 --- a/apps/activityreminder/boot.js +++ b/apps/activityreminder/boot.js @@ -1,9 +1,9 @@ (function () { - + // load variable before defining functions cause it can trigger a ReferenceError const activityreminder = require("activityreminder"); const activityreminder_settings = activityreminder.loadSettings(); if (activityreminder_settings.enabled) { - const activityreminder_data = activityreminder.loadData(); + let activityreminder_data = activityreminder.loadData(); if (activityreminder_data.firstLoad) { activityreminder_data.firstLoad = false; activityreminder.saveData(activityreminder_data); diff --git a/apps/activityreminder/lib.js b/apps/activityreminder/lib.js index e0f7caca3..704d35641 100644 --- a/apps/activityreminder/lib.js +++ b/apps/activityreminder/lib.js @@ -21,7 +21,7 @@ exports.saveData = function (data) { exports.loadData = function () { let health = Bangle.getHealthStatus("day"); - const data = Object.assign({ + let data = Object.assign({ firstLoad: true, stepsDate: new Date(), stepsOnDate: health.steps, diff --git a/apps/activityreminder/settings.js b/apps/activityreminder/settings.js index b4d288500..ce7cdc913 100644 --- a/apps/activityreminder/settings.js +++ b/apps/activityreminder/settings.js @@ -1,7 +1,7 @@ (function (back) { // Load settings const activityreminder = require("activityreminder"); - const settings = activityreminder.loadSettings(); + let settings = activityreminder.loadSettings(); // Show the menu E.showMenu({ From ea82ae3f255319e23704e041627655c7570e34f9 Mon Sep 17 00:00:00 2001 From: Stiralbios Date: Sun, 12 Jun 2022 20:16:14 +0200 Subject: [PATCH 33/41] Fix activityreminder_data scoping issue in boot.js --- apps/activityreminder/boot.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/activityreminder/boot.js b/apps/activityreminder/boot.js index 4d6847a20..16c209737 100644 --- a/apps/activityreminder/boot.js +++ b/apps/activityreminder/boot.js @@ -2,8 +2,9 @@ // load variable before defining functions cause it can trigger a ReferenceError const activityreminder = require("activityreminder"); const activityreminder_settings = activityreminder.loadSettings(); + let activityreminder_data = null; if (activityreminder_settings.enabled) { - let activityreminder_data = activityreminder.loadData(); + activityreminder_data = activityreminder.loadData(); if (activityreminder_data.firstLoad) { activityreminder_data.firstLoad = false; activityreminder.saveData(activityreminder_data); From 7ba81f1ba5b26b69a761ca07a9615c90a0b365ed Mon Sep 17 00:00:00 2001 From: Stiralbios Date: Sun, 12 Jun 2022 20:29:43 +0200 Subject: [PATCH 34/41] add missing semicolons --- apps/activityreminder/boot.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/apps/activityreminder/boot.js b/apps/activityreminder/boot.js index 16c209737..f97cf274d 100644 --- a/apps/activityreminder/boot.js +++ b/apps/activityreminder/boot.js @@ -2,13 +2,11 @@ // load variable before defining functions cause it can trigger a ReferenceError const activityreminder = require("activityreminder"); const activityreminder_settings = activityreminder.loadSettings(); - let activityreminder_data = null; - if (activityreminder_settings.enabled) { - activityreminder_data = activityreminder.loadData(); - if (activityreminder_data.firstLoad) { - activityreminder_data.firstLoad = false; - activityreminder.saveData(activityreminder_data); - } + let activityreminder_data = activityreminder.loadData(); + + if (activityreminder_data.firstLoad) { + activityreminder_data.firstLoad = false; + activityreminder.saveData(activityreminder_data); } function run() { @@ -42,9 +40,9 @@ function isDuringAlertHours(h) { if (activityreminder_settings.startHour < activityreminder_settings.endHour) { // not passing through midnight - return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour) + return (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour); } else { // passing through midnight - return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour) + return (h >= activityreminder_settings.startHour || h < activityreminder_settings.endHour); } } From 38f23be5b4b9da4726eb53bcffd716766a5aa4d4 Mon Sep 17 00:00:00 2001 From: Hank Date: Sun, 12 Jun 2022 21:13:28 +0200 Subject: [PATCH 35/41] Fix PM hours --- apps/hworldclock/ChangeLog | 3 ++- apps/hworldclock/app.js | 16 +++------------- apps/hworldclock/metadata.json | 2 +- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/apps/hworldclock/ChangeLog b/apps/hworldclock/ChangeLog index 30d729cbf..fdc717c92 100644 --- a/apps/hworldclock/ChangeLog +++ b/apps/hworldclock/ChangeLog @@ -1,4 +1,5 @@ 0.15: Initial release - be patient as this is the first try :) 0.16: Fix timing 0.17: Fix hours -0.18: Code cleanup and major changes with seconds timing. New feature: if watch is locked, seconds get refreshed every 10 seconds. \ No newline at end of file +0.18: Code cleanup and major changes with seconds timing. New feature: if watch is locked, seconds get refreshed every 10 seconds. +0.19: Fix PM Hours \ No newline at end of file diff --git a/apps/hworldclock/app.js b/apps/hworldclock/app.js index 6101d4427..e907dd703 100644 --- a/apps/hworldclock/app.js +++ b/apps/hworldclock/app.js @@ -19,7 +19,6 @@ const font = "6x8"; /* TODO: we could totally use 'Layout' here and avoid a whole bunch of hard-coded offsets */ - const xyCenter = g.getWidth() / 2; const xyCenterSeconds = xyCenter + (big ? 85 : 68); const yAmPm = xyCenter - (big ? 70 : 48); @@ -171,7 +170,8 @@ function draw() { //do 12 hour stuff if (hours > 12) { ampm = "PM"; - hours = hours - 12; + hours = hours - 12; + if (hours < 10) hours = doublenum(hours); } else { ampm = "AM"; } @@ -200,8 +200,6 @@ function draw() { g.setFont("Vector", 17); g.drawString(require("locale").dow(new Date(), 1).toUpperCase() + ", " + localDate, xyCenter, yposDate, true); - - g.setFont(font, primaryDateFontSize); // set gmt to UTC+0 var gmt = new Date(d.getTime() + d.getTimezoneOffset() * 60 * 1000); @@ -345,12 +343,4 @@ Bangle.on('lock',on=>{ draw(); // draw immediately, queue redraw updatePos(); } - }); - - - - - - - - + }); \ No newline at end of file diff --git a/apps/hworldclock/metadata.json b/apps/hworldclock/metadata.json index 9eb95a0ed..1ee04b333 100644 --- a/apps/hworldclock/metadata.json +++ b/apps/hworldclock/metadata.json @@ -2,7 +2,7 @@ "id": "hworldclock", "name": "Hanks World Clock", "shortName": "Hanks World Clock", - "version": "0.18", + "version": "0.19", "description": "Current time zone plus up to three others", "allow_emulator":true, "icon": "app.png", From 1d6887a4dff0f11475cc1bf21d62c59e07b75a94 Mon Sep 17 00:00:00 2001 From: Marco H Date: Sun, 12 Jun 2022 21:17:52 +0200 Subject: [PATCH 36/41] Do not check new values if alarm is shown --- apps/widbaroalarm/widget.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/widbaroalarm/widget.js b/apps/widbaroalarm/widget.js index d8eb43995..e1516b6f1 100644 --- a/apps/widbaroalarm/widget.js +++ b/apps/widbaroalarm/widget.js @@ -2,6 +2,7 @@ let medianPressure; let threeHourAvrPressure; let currentPressures = []; + let stop = false; // semaphore const LOG_FILE = "widbaroalarm.log.json"; const SETTINGS_FILE = "widbaroalarm.json"; @@ -34,6 +35,7 @@ function showAlarm(body, key) { if (body == undefined) return; + stop = true; E.showPrompt(body, { title: "Pressure alarm", @@ -56,6 +58,7 @@ // save timestamp of the future so that we do not warn again for the same event until then saveSetting(key, tsNow + 60 * setting('pauseDelayMin')); } + stop = false; load(); }); @@ -64,7 +67,10 @@ Bangle.buzz(); } - setTimeout(load, 20000); + setTimeout(function() { + stop = false; + load(); + }, 20000); } @@ -191,6 +197,7 @@ turn off barometer power */ function check() { + if (stop) return; const MEDIANLENGTH = 20; Bangle.setBarometerPower(true, "widbaroalarm"); Bangle.on('pressure', function(e) { From c3b1a973c0bcef1ac7130e1b0df89dd3b1003703 Mon Sep 17 00:00:00 2001 From: Hank Date: Sun, 12 Jun 2022 22:44:32 +0200 Subject: [PATCH 37/41] Add theme support --- apps/hworldclock/ChangeLog | 3 ++- apps/hworldclock/app.js | 45 ++++++++++------------------------ apps/hworldclock/metadata.json | 2 +- 3 files changed, 16 insertions(+), 34 deletions(-) diff --git a/apps/hworldclock/ChangeLog b/apps/hworldclock/ChangeLog index fdc717c92..c7b1731a2 100644 --- a/apps/hworldclock/ChangeLog +++ b/apps/hworldclock/ChangeLog @@ -2,4 +2,5 @@ 0.16: Fix timing 0.17: Fix hours 0.18: Code cleanup and major changes with seconds timing. New feature: if watch is locked, seconds get refreshed every 10 seconds. -0.19: Fix PM Hours \ No newline at end of file +0.19: Fix PM Hours +0.20: Add theme support \ No newline at end of file diff --git a/apps/hworldclock/app.js b/apps/hworldclock/app.js index e907dd703..faf24e974 100644 --- a/apps/hworldclock/app.js +++ b/apps/hworldclock/app.js @@ -82,7 +82,7 @@ var drawTimeout; var drawTimeoutSeconds; var secondsTimeout; -g.setBgColor(0, 0, 0); +g.setBgColor(g.theme.bg); // schedule a draw for the next minute function queueDraw() { @@ -128,7 +128,7 @@ function drawSeconds() { // default draw styles g.reset(); - g.setBgColor(0, 0, 0); + g.setBgColor(g.theme.bg); // drawSting centered g.setFontAlign(0, 0); @@ -138,7 +138,11 @@ function drawSeconds() { var seconds = time[2]; g.setFont("5x9Numeric7Seg",primaryTimeFontSize - 3); - g.setColor("#22ff05"); + if (g.theme.dark) { + g.setColor("#22ff05"); + } else { + g.setColor(g.theme.fg); + } //console.log("---"); //console.log(seconds); if (Bangle.isLocked()) seconds = seconds.slice(0, -1) + ':::'; // we use :: as the font does not have an x @@ -155,7 +159,7 @@ function draw() { // default draw styles g.reset(); - g.setBgColor(0, 0, 0); + g.setBgColor(g.theme.bg); // drawSting centered g.setFontAlign(0, 0); @@ -179,7 +183,11 @@ function draw() { //g.setFont(font, primaryTimeFontSize); g.setFont("5x9Numeric7Seg",primaryTimeFontSize); - g.setColor("#22ff05"); + if (g.theme.dark) { + g.setColor("#22ff05"); + } else { + g.setColor(g.theme.fg); + } g.drawString(`${hours}:${minutes}`, xyCenter-10, yposTime, true); // am / PM ? @@ -187,7 +195,6 @@ function draw() { //do 12 hour stuff //var ampm = require("locale").medidian(new Date()); Not working g.setFont("Vector", 17); - g.setColor("#22ff05"); g.drawString(ampm, xyCenterSeconds, yAmPm, true); } @@ -287,33 +294,7 @@ if (!Bangle.isLocked()) { // Initial state } -// Stop updates when LCD is off, restart when on -Bangle.on('lcdPower',on=>{ - if (on) { - if (PosInterval != 0) clearInterval(PosInterval); - - PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins - secondsTimeout = 1000; - if (drawTimeout) clearTimeout(drawTimeout); - if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); - drawTimeout = undefined; - drawTimeoutSeconds = undefined; - draw(); // draw immediately, queue redraw - updatePos(); - } else { // stop draw timer - secondsTimeout = 1000 * 60; - if (drawTimeout) clearTimeout(drawTimeout); - if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); - drawTimeout = undefined; - drawTimeoutSeconds = undefined; - - if (PosInterval != 0) clearInterval(PosInterval); - PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins - draw(); // draw immediately, queue redraw - updatePos(); - } -}); Bangle.on('lock',on=>{ diff --git a/apps/hworldclock/metadata.json b/apps/hworldclock/metadata.json index 1ee04b333..5cc2a49a8 100644 --- a/apps/hworldclock/metadata.json +++ b/apps/hworldclock/metadata.json @@ -2,7 +2,7 @@ "id": "hworldclock", "name": "Hanks World Clock", "shortName": "Hanks World Clock", - "version": "0.19", + "version": "0.20", "description": "Current time zone plus up to three others", "allow_emulator":true, "icon": "app.png", From d89ba34bb7e72f94a85780cc7a19d185137557b1 Mon Sep 17 00:00:00 2001 From: Hank Date: Sun, 12 Jun 2022 22:49:55 +0200 Subject: [PATCH 38/41] Remove --- .../Automatisch beibehalten von Corel/widget.png | Bin 2385 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 apps/hwid_a_battery_widget/Automatisch beibehalten von Corel/widget.png diff --git a/apps/hwid_a_battery_widget/Automatisch beibehalten von Corel/widget.png b/apps/hwid_a_battery_widget/Automatisch beibehalten von Corel/widget.png deleted file mode 100644 index 868628b6085cb0da28098648ae713dc1f08e44d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2385 zcmaJ@3s_S}7QRV%6-6sAF<4ClyK7v@i-;0K3Ni8!q9Gtc4b~8DAdq)1aB(YAt>u%d z1QAzxOM?njO9Tu>6tPi@^y8Cq0o>m5~KlokLMAQ3HazyNs#E&zZ#FAa}`B6%T9u~JSJ#VTV!GAvi2N&pLHBBmUK zLS3?Dm5U%i%ca`iELW}fxyzlN|*w!{|p^@ z^C8AVkcvs6q@<*fQ#{E^O&o>h?d?sW(kXPh2ZHd>rYay2?4i)QO^f`Y!v(csjZ_6m zl?no?D~eGjK|Vwxy3y-49VaA}%- zo53R0G|7Az(=oEp2$)dxGIwnb9z78#m+Sq0cd}KFI#A;yHf?IRDcwX=oQiR>aFy?ugdQ6YyDM9=!Ox)6 z-QwI1%ye`nVGTAnt{o#ero6HML)Uy^Dj6ygJslhQ-l*K`UtG1cwe@N1cVDLrwdRMG z*64k76TUsT2uh&{4?j3|Ne+tME|dQYKSCDEpg8kolSq#+^^!R)ie+U z+s11v=x1NukMZ}niWUm_R2O|qOK`|UPMgE#(5}Jk;~k)HT0%l*MP+4DZAyM`Pfuxc za}bloswueJ3BW4`U0q#Co}QjDK$KIEn`0ez=clGG-0CZEQ^M%z@-?u@J`5Kt(5+iU z#uXJ8=iGmKrnyp3Bv0G-oedIQndCY+#&iwI4%dH@kxX^BbGx#&>l$)y9T~ zkjlzR27|%n@pupf>3gb(iHV6Hepx3Z=bb!xs=mG+w&RbTc;zlnU-sJ6JCs%aX z>{!HYi_>azcJKZQPoS#rbKUi=N-VU=E%-tkypQL*|~ zOUnW`T+CgsyMm664#o!`eARY=_wK;w_qw~!V2!1v9Jf`=Tt3OM-BDRvyKQ7|=fk0) z3UTLfFhy+I?6Dod#QiOvLL!Bx7oB7eSJ~g*e--e3wmRopRvE^nd$={{#|FZYOIi0` z{iDsmz26SNiEa)73wZvvR8kw&<<{1Q|u{R*kDbNFBDQ31}%3AJl^-n#&A7i zxIK5{orx`M+GVZ3ZuIbI-e`Wmh~?5m3NID^c4ADVE=l|sd~femU2l7U^9Td|Ul6ou L3->%{*S`M&n6+>< From e68d04da5bb5ee2be61fa15df743d8f37fd0f2d1 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 13 Jun 2022 08:23:16 +0100 Subject: [PATCH 39/41] works on v2 --- apps/espruinoctrl/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/espruinoctrl/metadata.json b/apps/espruinoctrl/metadata.json index 5798c7842..253307fa0 100644 --- a/apps/espruinoctrl/metadata.json +++ b/apps/espruinoctrl/metadata.json @@ -6,7 +6,7 @@ "description": "Send commands to other Espruino devices via the Bluetooth UART interface. Customisable commands!", "icon": "app.png", "tags": "", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "custom": "custom.html", "storage": [ From dac6e6ec916d37908eb5cfb49bd9f90b5ef4d8bb Mon Sep 17 00:00:00 2001 From: Rarder44 Date: Mon, 13 Jun 2022 14:18:26 +0200 Subject: [PATCH 40/41] Rebble: fix battery size --- apps/rebble/rebble.app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/rebble/rebble.app.js b/apps/rebble/rebble.app.js index 8ba61f818..fc91fe0ac 100644 --- a/apps/rebble/rebble.app.js +++ b/apps/rebble/rebble.app.js @@ -236,7 +236,7 @@ function drawBattery(x,y,wi,hi) { g.clearRect(x+2,y+2+2,x+wi-4-2,y+2+hi-2); // centre g.setColor(g.theme.fg); g.fillRect(x+wi-3,y+2+(((hi - 1)/2)-1),x+wi-2,y+2+(((hi - 1)/2)-1)+4); // contact - g.fillRect(x+3, y+5, x +4 + E.getBattery()*(wi-12)/100, y+hi-1); // the level + g.fillRect(x+3, y+5, x +3 + E.getBattery()*(wi-10)/100, y+hi-1); // the level if( Bangle.isCharging() ) { From 988b4d478f6b58ec40b72d09f3122b2466ebd3d8 Mon Sep 17 00:00:00 2001 From: Rarder44 Date: Mon, 13 Jun 2022 14:20:40 +0200 Subject: [PATCH 41/41] changelog and metatada update --- apps/rebble/ChangeLog | 1 + apps/rebble/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/rebble/ChangeLog b/apps/rebble/ChangeLog index 4b415c1c5..4e2e76484 100644 --- a/apps/rebble/ChangeLog +++ b/apps/rebble/ChangeLog @@ -6,3 +6,4 @@ 0.06: Add 12h support and autocycle control 0.07: added localization, removed deprecated code 0.08: removed unused font, fix autocycle, imported suncalc and trimmed, removed pedometer dependency, "tap to cycle" setting +0.09: fix battery icon size \ No newline at end of file diff --git a/apps/rebble/metadata.json b/apps/rebble/metadata.json index e28c67784..ec7650f53 100644 --- a/apps/rebble/metadata.json +++ b/apps/rebble/metadata.json @@ -2,7 +2,7 @@ "id": "rebble", "name": "Rebble Clock", "shortName": "Rebble", - "version": "0.08", + "version": "0.09", "description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion", "readme": "README.md", "icon": "rebble.png",