From b940af9a34c71798c8cfb19d91840776af0816ba Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Sun, 20 Apr 2025 17:25:58 +0200 Subject: [PATCH 01/57] edgeclk: Reset graphics before initial clearing of the screen. Helps in some situations if using fastload utils where the graphics from the previous app would not be cleared. --- apps/edgeclk/ChangeLog | 2 ++ apps/edgeclk/app.js | 2 +- apps/edgeclk/metadata.json | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/edgeclk/ChangeLog b/apps/edgeclk/ChangeLog index 72bb39ab1..34caa2d6a 100644 --- a/apps/edgeclk/ChangeLog +++ b/apps/edgeclk/ChangeLog @@ -2,3 +2,5 @@ 0.02: Fix reset of progress bars on midnight. Fix display of 100k+ steps. 0.03: Added option to display weather. 0.04: Added option to display live updates of step count. +0.05: Reset graphics before initial clearing of the screen. Helps in some + situations if using fastload utils. diff --git a/apps/edgeclk/app.js b/apps/edgeclk/app.js index 79310c3da..9bc6335ac 100644 --- a/apps/edgeclk/app.js +++ b/apps/edgeclk/app.js @@ -336,7 +336,7 @@ /* Startup Process ------------------------------------------------------------------------------*/ - g.clear(); + g.reset().clear(); drawAll(); startTimers(); registerEvents(); diff --git a/apps/edgeclk/metadata.json b/apps/edgeclk/metadata.json index ef97b314b..f7af147da 100644 --- a/apps/edgeclk/metadata.json +++ b/apps/edgeclk/metadata.json @@ -2,7 +2,7 @@ "id": "edgeclk", "name": "Edge Clock", "shortName": "Edge Clock", - "version": "0.04", + "version": "0.05", "description": "Crisp clock with perfect readability.", "readme": "README.md", "icon": "app.png", From 6ae4ad9d85ee2851a2cf3216b52611c6fbf1ffd4 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Thu, 24 Apr 2025 00:17:17 +0200 Subject: [PATCH 02/57] edgeclk: reset inside the `g.clear` call --- apps/edgeclk/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/edgeclk/app.js b/apps/edgeclk/app.js index 9bc6335ac..b67018917 100644 --- a/apps/edgeclk/app.js +++ b/apps/edgeclk/app.js @@ -336,7 +336,7 @@ /* Startup Process ------------------------------------------------------------------------------*/ - g.reset().clear(); + g.clear(1); drawAll(); startTimers(); registerEvents(); From f85e4a33fa3c9e9e116dc5aa1b63b22e452e4570 Mon Sep 17 00:00:00 2001 From: thyttan <6uuxstm66@mozmail.com⁩> Date: Thu, 24 Apr 2025 23:07:01 +0200 Subject: [PATCH 03/57] widmsggrid: reset graphics state after setClipRect as per https://github.com/espruino/BangleApps/pull/3813#issuecomment-2826952155. --- apps/widmsggrid/ChangeLog | 1 + apps/widmsggrid/metadata.json | 2 +- apps/widmsggrid/widget.js | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/widmsggrid/ChangeLog b/apps/widmsggrid/ChangeLog index 0573b9677..fd64d3f5a 100644 --- a/apps/widmsggrid/ChangeLog +++ b/apps/widmsggrid/ChangeLog @@ -4,3 +4,4 @@ 0.04: Remove library stub 0.05: Don't turn on LCD 0.06: Don't draw outside of widget field +0.07: Don't leave clipRect modified, as per [this comment](https://github.com/espruino/BangleApps/pull/3813#issuecomment-2826952155). diff --git a/apps/widmsggrid/metadata.json b/apps/widmsggrid/metadata.json index 75b859c3b..ebfa4a208 100644 --- a/apps/widmsggrid/metadata.json +++ b/apps/widmsggrid/metadata.json @@ -1,7 +1,7 @@ { "id": "widmsggrid", "name": "Messages Grid Widget", - "version": "0.06", + "version": "0.07", "description": "Widget that displays notification icons in a grid", "icon": "widget.png", "type": "widget", diff --git a/apps/widmsggrid/widget.js b/apps/widmsggrid/widget.js index dc444f782..080702e9d 100644 --- a/apps/widmsggrid/widget.js +++ b/apps/widmsggrid/widget.js @@ -49,6 +49,7 @@ r++; } }); + g.reset(); // Make sure we don't leave clipRect set to some smaller rectangle. if (w.total > 1) { // show total number of messages in bottom-right corner g.reset(); From 8bc2d769fd3327d18c1bf46e8ac33af794775045 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 00:09:43 +0000 Subject: [PATCH 04/57] build(deps): bump core from `76b887d` to `993d993` Bumps [core](https://github.com/espruino/EspruinoAppLoaderCore) from `76b887d` to `993d993`. - [Commits](https://github.com/espruino/EspruinoAppLoaderCore/compare/76b887dd6fc5693786fc1e63b97f3e4ab4306ed7...993d9939c744775fe612c954ca696c734fc0ac4e) --- updated-dependencies: - dependency-name: core dependency-version: 993d9939c744775fe612c954ca696c734fc0ac4e dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index 76b887dd6..993d9939c 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 76b887dd6fc5693786fc1e63b97f3e4ab4306ed7 +Subproject commit 993d9939c744775fe612c954ca696c734fc0ac4e From 9051d92e7bd0d73247fc25104656d08db7b0508e Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Thu, 24 Apr 2025 20:55:10 -0400 Subject: [PATCH 05/57] Fixed comma drawing --- apps/jsonclock/ChangeLog | 1 + apps/jsonclock/app.js | 3 ++- apps/jsonclock/metadata.json | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/jsonclock/ChangeLog b/apps/jsonclock/ChangeLog index f8cb62040..a5b6cdc1e 100644 --- a/apps/jsonclock/ChangeLog +++ b/apps/jsonclock/ChangeLog @@ -1,2 +1,3 @@ 0.01: first release 0.02: memory leak fix; color changes to better align with VSCode color scheme; logo is transparent +0.03: Fixed redrawing of commas diff --git a/apps/jsonclock/app.js b/apps/jsonclock/app.js index 759b51793..3cdc72eb8 100644 --- a/apps/jsonclock/app.js +++ b/apps/jsonclock/app.js @@ -229,6 +229,7 @@ let redraw = function() { if (!(key in valsArrs)) continue; let valsArr = valsArrs[key]; if (value === valsArr.text) continue; // No need to update + if (valsArr.endComma) value = value.slice(0, -1); valsArrs[key].text = value; // Clear prev values @@ -239,7 +240,7 @@ let redraw = function() { g.drawString(value, valsArr.x, valsArr.y); if (valsArr.endComma){ g.setColor(clrs.brackets); - g.drawString(',', valsArr.Banglex + g.stringWidth(value), valsArr.y); + g.drawString(',', valsArr.x + g.stringWidth(value), valsArr.y); } } }; diff --git a/apps/jsonclock/metadata.json b/apps/jsonclock/metadata.json index d097d5b08..da1522207 100644 --- a/apps/jsonclock/metadata.json +++ b/apps/jsonclock/metadata.json @@ -1,6 +1,6 @@ { "id": "jsonclock", "name": "JsonClock", - "version": "0.02", + "version": "0.03", "description": "JSON view of the time, date, steps, battery, and sunrise and sunset times", "icon": "app.png", "screenshots": [{"url":"dark.png"}], From 80cc3a609f883954017d5104b8cdbeaf2424262e Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Fri, 25 Apr 2025 08:45:00 +0100 Subject: [PATCH 06/57] settings: handle a `undefined` scroller (BJS1) Fixes #3824 --- apps/setting/ChangeLog | 1 + apps/setting/metadata.json | 2 +- apps/setting/settings.js | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index 0f88be9dd..17098b069 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -86,3 +86,4 @@ of 'Select Clock' 0.75: Restore previous menu's scroll positions 0.76: Add altitude calibration menu (and update README after menu changed) 0.77: Save altitude calibration when user exits via reset +0.78: Fix menu scroll restore on BangleJS1 diff --git a/apps/setting/metadata.json b/apps/setting/metadata.json index b37534577..4940b5f70 100644 --- a/apps/setting/metadata.json +++ b/apps/setting/metadata.json @@ -1,7 +1,7 @@ { "id": "setting", "name": "Settings", - "version": "0.77", + "version": "0.78", "description": "A menu for setting up Bangle.js", "icon": "settings.png", "tags": "tool,system", diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 8303c8c25..3d7f35cd8 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -27,7 +27,8 @@ function pushMenu(menu) { function restoreMenu(menu) { // equivalent to pushMenu(null); popMenu(menu); if(!menu[""]) menu[""] = {}; - menu[""].scroll = menuScroller.scroll; + if(menuScroller) // may be undefined on BangleJS1 + menu[""].scroll = menuScroller.scroll; menuScroller = E.showMenu(menu).scroller; } From 4b9fae38030919672870ec234285493ec6e8dd30 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 25 Apr 2025 09:33:06 +0100 Subject: [PATCH 07/57] warpdrive 0.04: Remove buffer finding code (which fails if screen rotated) and make dependent on fw 2v21's Bangle.getOptions().lcdBufferPtr (fix #3818) Also adds check for non-contiguous memory --- apps/warpdrive/ChangeLog | 1 + apps/warpdrive/app.js | 90 ++++++++++-------------------------- apps/warpdrive/metadata.json | 2 +- 3 files changed, 26 insertions(+), 67 deletions(-) diff --git a/apps/warpdrive/ChangeLog b/apps/warpdrive/ChangeLog index 03c748ea5..c12c017e6 100644 --- a/apps/warpdrive/ChangeLog +++ b/apps/warpdrive/ChangeLog @@ -1,3 +1,4 @@ 0.01: New app! 0.02: Minor code improvements 0.03: Minor code improvements +0.04: Remove buffer finding code (which fails if screen rotated) and make dependent on fw 2v21's Bangle.getOptions().lcdBufferPtr (fix #3818) \ No newline at end of file diff --git a/apps/warpdrive/app.js b/apps/warpdrive/app.js index b16766f47..0dc7ef3b7 100644 --- a/apps/warpdrive/app.js +++ b/apps/warpdrive/app.js @@ -480,7 +480,6 @@ void render(int* n, const unsigned char* m){ ); } } - `); const nodeCount = 4; @@ -490,9 +489,7 @@ const translation = new Uint32Array(10); let bgColor = 0; const BLACK = g.setColor.bind(g, 0); const WHITE = g.setColor.bind(g, 0xFFFF); -let lcdBuffer = 0, - start = 0; - +let lcdBuffer = 0; let locked = false; let charging = false; let stopped = true; @@ -522,65 +519,6 @@ function test(addr, y) { return !peek8(addr); } -function probe() { - if (!start) { - start = 0x20000000; - if (test(Bangle.getOptions().lcdBufferPtr, 0)) - start = Bangle.getOptions().lcdBufferPtr; // FW=2v21 - else if (test(0x2002d3fe, 0)) // try to skip loading if possible - start = 0x2002d3fe; // FW=2v20 - } - const end = Math.min(start + 0x800, 0x20038000); - - if (start >= end) { - print("Could not find framebuffer"); - return; - } - - BLACK().fillRect(0, 0, 176, 0); - // sampling every 64 bytes since a 176-pixel row is 66 bytes at 3bpp - for (; start < end; start += 64) { - if (peek8(start)) continue; - WHITE().fillRect(0, 0, 176, 0); - let b = peek8(start); - BLACK().fillRect(0, 0, 176, 0); - if (!b) continue; - if (!peek8(start)) break; - } - - if (start >= end) { - setTimeout(probe, 1); - return; - } - - // find the beginning of the row - while (test(start - 1, 0)) - start--; - - /* - let stride = (176 * 3 + 7) >> 3, - padding = 0; - for (let i = 0; i < 20; ++i, ++padding) { - if (test(start + stride + padding, 1)) { - break; - } - } - - stride += padding; - if (padding == 20) { - print("Warning: Could not calculate padding"); - stride = 68; - } - */ - let stride = 68; - - lcdBuffer = start; - print('Found lcdBuffer at ' + lcdBuffer.toString(16) + ' stride=' + stride); - gfx.init(start, stride, E.getAddressOf(sintable, true)); - gfx.setCamera(0, 0, -300 << 8); - setupInterval(true); -} - function init() { bgColor = g.theme.bg & 0x8410; bgColor = ((bgColor >> 15) | (bgColor >> 9) | (bgColor >> 2)); @@ -612,7 +550,22 @@ function init() { c: i }; } - setTimeout(probe, 1); + lcdBuffer = Bangle.getOptions().lcdBufferPtr; + if (!lcdBuffer) { + E.showMessage("Needs firmwave 2v21 or newer"); + return; + } + let stride = 68; + //print('Found lcdBuffer at ' + lcdBuffer.toString(16) + ' stride=' + stride); + var sintablePtr = E.getAddressOf(sintable, true); + if (!sintablePtr) { + lcdBuffer = 0; + E.showMessage("Not enough memory"); + return; + } + gfx.init(lcdBuffer, stride, sintablePtr); + gfx.setCamera(0, 0, -300 << 8); + setupInterval(true); } function updateNode(index) { @@ -657,12 +610,17 @@ function drawNode(index) { translation[i++] = o.y * 256; translation[i++] = o.z * 256; translation[i++] = o.c; - gfx.render(E.getAddressOf(translation, true)); + let translationPtr = E.getAddressOf(translation, true); + if (!translationPtr) { + lcdBuffer = 0; + E.showMessage("Not enough memory"); + return; + } + gfx.render(translationPtr); } function tick(locked) { g.reset(); - if (lcdBuffer && !locked) { BLACK().drawRect(-1, -1, 0, 177); // dirty all the rows gfx.clear(bgColor); diff --git a/apps/warpdrive/metadata.json b/apps/warpdrive/metadata.json index 330166bc8..fc9fc46c6 100644 --- a/apps/warpdrive/metadata.json +++ b/apps/warpdrive/metadata.json @@ -1,7 +1,7 @@ { "id": "warpdrive", "name": "warpdrive clock", - "version": "0.03", + "version": "0.04", "description": "A watchface with an animated 3D scene.", "readme": "README.md", "icon": "app.png", From 053d17dd565a96bb9dcbbac362bb38ab1796cca2 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 25 Apr 2025 09:50:04 +0100 Subject: [PATCH 08/57] fix lint errors --- apps/warpdrive/app.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/apps/warpdrive/app.js b/apps/warpdrive/app.js index 0dc7ef3b7..59a610003 100644 --- a/apps/warpdrive/app.js +++ b/apps/warpdrive/app.js @@ -488,7 +488,6 @@ const sintable = new Uint8Array(256); const translation = new Uint32Array(10); let bgColor = 0; const BLACK = g.setColor.bind(g, 0); -const WHITE = g.setColor.bind(g, 0xFFFF); let lcdBuffer = 0; let locked = false; let charging = false; @@ -509,16 +508,6 @@ function setupInterval(force) { } } -function test(addr, y) { - BLACK().fillRect(0, y, 176, y); - if (peek8(addr)) return false; - WHITE().fillRect(0, y, 176, y); - let b = peek8(addr); - BLACK().fillRect(0, y, 176, y); - if (!b) return false; - return !peek8(addr); -} - function init() { bgColor = g.theme.bg & 0x8410; bgColor = ((bgColor >> 15) | (bgColor >> 9) | (bgColor >> 2)); From e867ff5c007a7cea89ae0880d54be35eeeb6d47b Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 25 Apr 2025 10:07:41 +0100 Subject: [PATCH 09/57] synthwave 0.04: Remove buffer finding code (which fails if screen rotated) and make dependent on fw 2v21's Bangle.getOptions().lcdBufferPtr (fix #3818) --- apps/lint_exemptions.js | 6 --- apps/synthwave/ChangeLog | 1 + apps/synthwave/app.js | 92 +++++++----------------------------- apps/synthwave/metadata.json | 2 +- 4 files changed, 20 insertions(+), 81 deletions(-) diff --git a/apps/lint_exemptions.js b/apps/lint_exemptions.js index e4b489333..753e0a62a 100644 --- a/apps/lint_exemptions.js +++ b/apps/lint_exemptions.js @@ -475,12 +475,6 @@ module.exports = { "no-undef" ] }, - "apps/warpdrive/app.js": { - "hash": "c2f9113c4d298a3021ec4bc0bc5f5d1bcd88267b4fa2acc03ae17d6447ed7d00", - "rules": [ - "no-undef" - ] - }, "apps/usgs/settings.js": { "hash": "00ee672a6920f5667bfbd2988fd2853cfd579895a843ae036a00028dcb13878d", "rules": [ diff --git a/apps/synthwave/ChangeLog b/apps/synthwave/ChangeLog index 03c748ea5..c12c017e6 100644 --- a/apps/synthwave/ChangeLog +++ b/apps/synthwave/ChangeLog @@ -1,3 +1,4 @@ 0.01: New app! 0.02: Minor code improvements 0.03: Minor code improvements +0.04: Remove buffer finding code (which fails if screen rotated) and make dependent on fw 2v21's Bangle.getOptions().lcdBufferPtr (fix #3818) \ No newline at end of file diff --git a/apps/synthwave/app.js b/apps/synthwave/app.js index 41711d950..2f81d877f 100644 --- a/apps/synthwave/app.js +++ b/apps/synthwave/app.js @@ -618,10 +618,7 @@ var fontNum = atob("AAAAAAAAAAAAAA//8D//g//8P/+I//8//44//w//j4//A/+P4/8A/4/4AAAA const sintable = new Uint8Array(256); let bgColor = 0; const BLACK = g.setColor.bind(g, 0); -const WHITE = g.setColor.bind(g, 0xFFFF); -let lcdBuffer = 0, - start = 0; - +let lcdBuffer = 0; let locked = false; let charging = false; let stopped = true; @@ -641,75 +638,6 @@ function setupInterval(force) { } } -function test(addr, y) { - BLACK().fillRect(0, y, 176, y); - if (peek8(addr)) return false; - WHITE().fillRect(0, y, 176, y); - let b = peek8(addr); - BLACK().fillRect(0, y, 176, y); - if (!b) return false; - return !peek8(addr); -} - -function probe() { - if (!start) { - start = 0x20000000; - if (test(Bangle.getOptions().lcdBufferPtr, 0)) - start = Bangle.getOptions().lcdBufferPtr; // FW=2v21 - else if (test(0x2002d3fe, 0)) // try to skip loading if possible - start = 0x2002d3fe; // FW=2v20 - } - const end = Math.min(start + 0x800, 0x20038000); - - if (start >= end) { - print("Could not find framebuffer"); - return; - } - - BLACK().fillRect(0, 0, 176, 0); - // sampling every 64 bytes since a 176-pixel row is 66 bytes at 3bpp - for (; start < end; start += 64) { - if (peek8(start)) continue; - WHITE().fillRect(0, 0, 176, 0); - let b = peek8(start); - BLACK().fillRect(0, 0, 176, 0); - if (!b) continue; - if (!peek8(start)) break; - } - - if (start >= end) { - setTimeout(probe, 1); - return; - } - - // find the beginning of the row - while (test(start - 1, 0)) - start--; - - /* - let stride = (176 * 3 + 7) >> 3, - padding = 0; - for (let i = 0; i < 20; ++i, ++padding) { - if (test(start + stride + padding, 1)) { - break; - } - } - - stride += padding; - if (padding == 20) { - print("Warning: Could not calculate padding"); - stride = 68; - } - */ - let stride = 68; - - lcdBuffer = start; - print('Found lcdBuffer at ' + lcdBuffer.toString(16) + ' stride=' + stride); - gfx.init(start, stride, E.getAddressOf(sintable, true)); - gfx.setCamera(0, 0, 0); - setupInterval(true); -} - function init() { require("Font5x9Numeric7Seg").add(Graphics); g.setFont("5x9Numeric7Seg"); @@ -723,7 +651,23 @@ function init() { // setup sin/cos table for (let i = 0; i < sintable.length; ++i) sintable[i] = Math.sin((i * Math.PI * 0.5) / sintable.length) * ((1 << 8) - 1); - setTimeout(probe, 1); + + lcdBuffer = Bangle.getOptions().lcdBufferPtr; + if (!lcdBuffer) { + E.showMessage("Needs firmwave 2v21 or newer"); + return; + } + let stride = 68; + //print('Found lcdBuffer at ' + lcdBuffer.toString(16) + ' stride=' + stride); + var sintablePtr = E.getAddressOf(sintable, true); + if (!sintablePtr) { + lcdBuffer = 0; + E.showMessage("Not enough memory"); + return; + } + gfx.init(lcdBuffer, stride, sintablePtr); + gfx.setCamera(0, 0, 0); + setupInterval(true); } function tick(locked) { diff --git a/apps/synthwave/metadata.json b/apps/synthwave/metadata.json index 8f9864441..274484ea0 100644 --- a/apps/synthwave/metadata.json +++ b/apps/synthwave/metadata.json @@ -1,7 +1,7 @@ { "id": "synthwave", "name": "synthwave clock", - "version": "0.03", + "version": "0.04", "description": "A watchface with an animated 3D scene.", "readme": "README.md", "icon": "app.png", From befff7e77cb75dfd8746d2b32042b8e8af5f7f5c Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Fri, 25 Apr 2025 12:06:58 +0100 Subject: [PATCH 10/57] pace: fix menu rendering --- apps/pace/ChangeLog | 2 ++ apps/pace/README.md | 3 ++- apps/pace/app.js | 20 ++++++++++++++------ apps/pace/app.ts | 21 +++++++++++++++------ apps/pace/metadata.json | 2 +- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/apps/pace/ChangeLog b/apps/pace/ChangeLog index 66e01974e..35b746350 100644 --- a/apps/pace/ChangeLog +++ b/apps/pace/ChangeLog @@ -2,3 +2,5 @@ 0.02: Show elapsed time on pause screen 0.03: Avoid initial GPS skew and allow reset of time/splits 0.04: Bump exstats module - show active time, not elapsed +0.05: Fix menu display - don't draw over the menu and vice-versa. Require + double-tap for menu diff --git a/apps/pace/README.md b/apps/pace/README.md index 7a1ea7c06..f9096b289 100644 --- a/apps/pace/README.md +++ b/apps/pace/README.md @@ -5,7 +5,8 @@ A running pace app, useful for races. Will also record your splits and display t Drag up/down on the pause menu to scroll through your splits. Press the button to pause/resume - when resumed, pressing the button will pause instantly, regardless of whether the screen is locked. +Double tap the pause screen to access a menu for finer control + # Todo - Load splits on app start, button to reset (exs is always reset) -- Show total time on pause screen diff --git a/apps/pace/app.js b/apps/pace/app.js index 5954ca541..0ee6556e8 100644 --- a/apps/pace/app.js +++ b/apps/pace/app.js @@ -10,6 +10,7 @@ }); var S_1 = require("Storage"); var drawTimeout_1; + var menuShown_1 = false; var splits_1 = []; var splitOffset_1 = 0, splitOffsetPx_1 = 0; var GPS_TIMEOUT_MS_1 = 30000; @@ -129,6 +130,12 @@ else resumeRun_1(); }; + var hideMenu_1 = function () { + if (!menuShown_1) + return; + Bangle.setUI(); + menuShown_1 = false; + }; exs_1.start(); exs_1.stats.dist.on("notify", function (dist) { var prev = { time: 0, dist: 0 }; @@ -156,7 +163,7 @@ }); setWatch(function () { return onButton_1(); }, BTN1, { repeat: true }); Bangle.on('drag', function (e) { - if (exs_1.state.active || e.b === 0) + if (exs_1.state.active || e.b === 0 || menuShown_1) return; splitOffsetPx_1 -= e.dy; if (splitOffsetPx_1 > 20) { @@ -174,9 +181,10 @@ Bangle.on('twist', function () { Bangle.setBacklight(1); }); - Bangle.on('tap', function (_e) { - if (exs_1.state.active) + Bangle.on('tap', function (e) { + if (exs_1.state.active || menuShown_1 || !e.double) return; + menuShown_1 = true; var menu = { "": { remove: function () { @@ -184,16 +192,16 @@ }, }, "< Back": function () { - Bangle.setUI(); + hideMenu_1(); }, "Zero time": function () { exs_1.start(); exs_1.stop(); - Bangle.setUI(); + hideMenu_1(); }, "Clear splits": function () { splits_1.splice(0, splits_1.length); - Bangle.setUI(); + hideMenu_1(); }, }; E.showMenu(menu); diff --git a/apps/pace/app.ts b/apps/pace/app.ts index 6d4fd5112..7ebb276b2 100644 --- a/apps/pace/app.ts +++ b/apps/pace/app.ts @@ -14,6 +14,7 @@ const exs = require("exstats").getStats( const S = require("Storage"); let drawTimeout: TimeoutId | undefined; +let menuShown = false; type Dist = number & { brand: 'dist' }; type Time = number & { brand: 'time' }; @@ -171,6 +172,12 @@ const onButton = () => { resumeRun(); }; +const hideMenu = () => { + if (!menuShown) return; + Bangle.setUI(); // calls `remove`, which handles redrawing + menuShown = false; +} + exs.start(); // aka reset exs.stats.dist.on("notify", (dist) => { @@ -209,7 +216,7 @@ Bangle.on('lock', locked => { setWatch(() => onButton(), BTN1, { repeat: true }); Bangle.on('drag', e => { - if (exs.state.active || e.b === 0) return; + if (exs.state.active || e.b === 0 || menuShown) return; splitOffsetPx -= e.dy; if (splitOffsetPx > 20) { @@ -226,9 +233,11 @@ Bangle.on('twist', () => { Bangle.setBacklight(1); }); -Bangle.on('tap', _e => { - if(exs.state.active) return; +Bangle.on('tap', e => { + // require a double tap, to avoid picking up menu "< Back" taps + if(exs.state.active || menuShown || !e.double) return; + menuShown = true; const menu: Menu = { "": { remove: () => { @@ -236,16 +245,16 @@ Bangle.on('tap', _e => { }, }, "< Back": () => { - Bangle.setUI(); // calls `remove`, which handles redrawing + hideMenu(); }, "Zero time": () => { exs.start(); // calls reset exs.stop(); // re-pauses - Bangle.setUI(); + hideMenu(); }, "Clear splits": () => { splits.splice(0, splits.length); - Bangle.setUI(); + hideMenu(); }, }; diff --git a/apps/pace/metadata.json b/apps/pace/metadata.json index 4a31a6802..0ae402b82 100644 --- a/apps/pace/metadata.json +++ b/apps/pace/metadata.json @@ -1,7 +1,7 @@ { "id": "pace", "name": "Pace", - "version": "0.04", + "version": "0.05", "description": "Show pace and time running splits", "icon": "app.png", "tags": "run,running,fitness,outdoors", From 53eb2d91735965fb1cfb3745dc0c437a11b38ed5 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Fri, 25 Apr 2025 12:12:12 +0100 Subject: [PATCH 11/57] pace: set GPS power once doesn't really matter - exstats sets it too --- apps/pace/app.js | 3 +-- apps/pace/app.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/pace/app.js b/apps/pace/app.js index 0ee6556e8..62001ac0c 100644 --- a/apps/pace/app.js +++ b/apps/pace/app.js @@ -114,12 +114,10 @@ }; var pauseRun_1 = function () { exs_1.stop(); - Bangle.setGPSPower(0, "pace"); draw_1(); }; var resumeRun_1 = function () { exs_1.resume(); - Bangle.setGPSPower(1, "pace"); g.clearRect(Bangle.appRect); layout_1.forgetLazyState(); draw_1(); @@ -208,6 +206,7 @@ }); Bangle.loadWidgets(); Bangle.drawWidgets(); + Bangle.setGPSPower(1, "pace"); g.clearRect(Bangle.appRect); draw_1(); } diff --git a/apps/pace/app.ts b/apps/pace/app.ts index 7ebb276b2..be9ac572b 100644 --- a/apps/pace/app.ts +++ b/apps/pace/app.ts @@ -152,13 +152,11 @@ const drawSplit = (i: number, y: number, pace: number | string) => const pauseRun = () => { exs.stop(); - Bangle.setGPSPower(0, "pace") draw(); }; const resumeRun = () => { exs.resume(); - Bangle.setGPSPower(1, "pace"); g.clearRect(Bangle.appRect); // splits -> layout, clear. layout -> splits, fine layout.forgetLazyState(); @@ -263,6 +261,7 @@ Bangle.on('tap', e => { Bangle.loadWidgets(); Bangle.drawWidgets(); +Bangle.setGPSPower(1, "pace"); g.clearRect(Bangle.appRect); draw(); From 0e06ccffdaae0d6f438a9670c1a388b9f2fe6e39 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 25 Apr 2025 15:33:54 +0100 Subject: [PATCH 12/57] Add chip for fonts, otherwise they're really hard to find --- android.html | 1 + index.html | 1 + 2 files changed, 2 insertions(+) diff --git a/android.html b/android.html index 38e168372..1dc015468 100644 --- a/android.html +++ b/android.html @@ -85,6 +85,7 @@ + -

Click

+ From 0a4cc641c0bc5362fae65f48ce9da1c196123d93 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 28 Apr 2025 22:06:12 +0100 Subject: [PATCH 43/57] qrcode: return on error (instead of generating more) --- apps/qrcode/custom.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/qrcode/custom.html b/apps/qrcode/custom.html index ec6221a59..9106fdbb5 100644 --- a/apps/qrcode/custom.html +++ b/apps/qrcode/custom.html @@ -270,6 +270,7 @@ } catch (error) { document.getElementById("errors").innerText="Error: QR could not be created."; console.error(error); + return; } targetSize = Math.min(deviceWidth - border, deviceHeight - border); @@ -300,6 +301,7 @@ } catch (error) { document.getElementById("errors").innerText="Error: QR could not be created."; console.error(error); + return; } } From 2401063c7d604aed0e062a6757a4e0bfef069737 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 28 Apr 2025 22:06:37 +0100 Subject: [PATCH 44/57] qrcode: simplify `if` --- apps/qrcode/custom.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/qrcode/custom.html b/apps/qrcode/custom.html index 9106fdbb5..a51190153 100644 --- a/apps/qrcode/custom.html +++ b/apps/qrcode/custom.html @@ -341,7 +341,7 @@ if(document.getElementById("useWIFI").checked){ content = document.getElementById("ssid").value } - if(!(document.getElementById("description").value === "")){ + if(document.getElementById("description").value !== ""){ content = document.getElementById("description").value; } var img = imageconverter.canvastoString(document.getElementsByTagName("canvas")[0],{mode:"1bit",output:"string",compression:true}); From e146686b070db37d397770a614fa032e92c05688 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 28 Apr 2025 22:07:53 +0100 Subject: [PATCH 45/57] qrcode: allow custom app name (for multiple QR codes) --- apps/qrcode/custom.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/qrcode/custom.html b/apps/qrcode/custom.html index a51190153..2ff1cd9db 100644 --- a/apps/qrcode/custom.html +++ b/apps/qrcode/custom.html @@ -101,6 +101,8 @@ + + @@ -365,8 +367,10 @@ g.drawString(content,g.getWidth()/2,g.getHeight()-(g.getHeight()-img[1])/4); `} g.setColor(1,1,1); `; + + var appname = document.getElementById("appname").value.trim() || "qrcode"; sendCustomizedApp({ - storage:[{name:"qrcode.app.js", url:"app.js", content:app}] + storage:[{name:`${appname}.app.js`, url:"app.js", content:app}] }); }); From 0bf0a3fb972117aa44d6f142a87fd0624c23282e Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 28 Apr 2025 22:08:39 +0100 Subject: [PATCH 46/57] qrcode: bump version --- apps/qrcode/ChangeLog | 1 + apps/qrcode/metadata.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/qrcode/ChangeLog b/apps/qrcode/ChangeLog index 52eadbcf9..9b4526c10 100644 --- a/apps/qrcode/ChangeLog +++ b/apps/qrcode/ChangeLog @@ -4,3 +4,4 @@ 0.04: Allow scanning of QR codes from camera or file 0.05: Change brightness on touch 0.06: Add ability to generate contact info (MeCard format) QR code +0.07: Add custom appname (for storing multiple QR codes) diff --git a/apps/qrcode/metadata.json b/apps/qrcode/metadata.json index 89c859a0c..fc70ab27d 100644 --- a/apps/qrcode/metadata.json +++ b/apps/qrcode/metadata.json @@ -2,7 +2,7 @@ "id": "qrcode", "name": "Custom QR Code", "shortName": "QR Code", - "version": "0.06", + "version": "0.07", "description": "Use this to upload a customised QR code to Bangle.js", "icon": "app.png", "tags": "qrcode", From 04023e859bfcc00a06dd0fd6a0b05f68e7b28535 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Fri, 2 May 2025 13:59:00 +0100 Subject: [PATCH 47/57] Updated core tools with support for int pretokenisation (2v26) --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index 993d9939c..c357d5103 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 993d9939c744775fe612c954ca696c734fc0ac4e +Subproject commit c357d51033c2bd09dbfc243736e16ced6c69ec62 From 963fc4970e9874c8d75933671b2b14b709044807 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sun, 4 May 2025 09:50:27 +0200 Subject: [PATCH 48/57] Zambretti Weather Forecaster Zambretti Forecaster, uses the Barometer for empirical weather forecast in the Northern Hemisphere (see https://web.archive.org/web/20110610213848/http://www.meteormetrics.com/zambretti.htm), similar to weather stations. Watch must be stationary and its height above sea level set. Uses code from widbaroalarm for measurements (boot.js) --- apps/zambretti/app-icon.js | 1 + apps/zambretti/app.js | 247 ++++++++++++++++++++++++++++++++++ apps/zambretti/app.png | Bin 0 -> 716 bytes apps/zambretti/boot.js | 106 +++++++++++++++ apps/zambretti/metadata.json | 20 +++ apps/zambretti/screenshot.png | Bin 0 -> 3934 bytes apps/zambretti/settings.js | 25 ++++ lang/de_DE.json | 31 ++++- 8 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 apps/zambretti/app-icon.js create mode 100644 apps/zambretti/app.js create mode 100644 apps/zambretti/app.png create mode 100644 apps/zambretti/boot.js create mode 100644 apps/zambretti/metadata.json create mode 100644 apps/zambretti/screenshot.png create mode 100644 apps/zambretti/settings.js diff --git a/apps/zambretti/app-icon.js b/apps/zambretti/app-icon.js new file mode 100644 index 000000000..c76c5bca1 --- /dev/null +++ b/apps/zambretti/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4UA/4AB31H//A/hL/ABfABRMD+ALJh5kKn//BRCBCP4ILT/WrBZOq1W/BY/+BYOvBY0DBYZhGh/6BYOrMI0/BYf5qpGGBYWVqtQC4+qqtVqgvF1NVAIIABI4ogBAAdAL4gKEBZg8E/oLiBQpUEgILHJAUFBY4kCioLHQoQKHGAYL4I4RTLNZaDLGA78EAH4AR")) diff --git a/apps/zambretti/app.js b/apps/zambretti/app.js new file mode 100644 index 000000000..ef714e9d9 --- /dev/null +++ b/apps/zambretti/app.js @@ -0,0 +1,247 @@ +/** + * https://web.archive.org/web/20110610213848/http://www.meteormetrics.com/zambretti.htm + */ + +const storage = require('Storage'); +const Layout = require("Layout"); + +let height; +let mainScreen = false; + +const ZAMBRETTI_FORECAST = { +A: /*LANG*/'Settled Fine', +B: /*LANG*/'Fine Weather', +C: /*LANG*/'Becoming Fine', +D: /*LANG*/'Fine Becoming Less Settled', +E: /*LANG*/'Fine, Possibly showers', +F: /*LANG*/'Fairly Fine, Improving', +G: /*LANG*/'Fairly Fine, Possibly showers, early', +H: /*LANG*/'Fairly Fine Showery Later', +I: /*LANG*/'Showery Early, Improving', +J: /*LANG*/'Changeable Mending', +K: /*LANG*/'Fairly Fine, Showers likely', +L: /*LANG*/'Rather Unsettled Clearing Later', +M: /*LANG*/'Unsettled, Probably Improving', +N: /*LANG*/'Showery Bright Intervals', +O: /*LANG*/'Showery Becoming more unsettled', +P: /*LANG*/'Changeable some rain', +Q: /*LANG*/'Unsettled, short fine Intervals', +R: /*LANG*/'Unsettled, Rain later', +S: /*LANG*/'Unsettled, rain at times', +T: /*LANG*/'Very Unsettled, Finer at times', +U: /*LANG*/'Rain at times, worse later.', +V: /*LANG*/'Rain at times, becoming very unsettled', +W: /*LANG*/'Rain at Frequent Intervals', +X: /*LANG*/'Very Unsettled, Rain', +Y: /*LANG*/'Stormy, possibly improving', +Z: /*LANG*/'Stormy, much rain', +}; + +const ZAMBRETTI_FALLING = { + 1050: 'A', + 1040: 'B', + 1024: 'C', + 1018: 'H', + 1010: 'O', + 1004: 'R', + 998: 'U', + 991: 'V', + 985: 'X', +}; + +const ZAMBRETTI_STEADY = { + 1033: 'A', + 1023: 'B', + 1014: 'E', + 1008: 'K', + 1000: 'N', + 994: 'P', + 989: 'S', + 981: 'W', + 974: 'X', + 960: 'Z', +}; + +const ZAMBRETTI_RISING = { + 1030: 'A', + 1022: 'B', + 1012: 'C', + 1007: 'F', + 1000: 'G', + 995: 'I', + 990: 'J', + 984: 'L', + 978: 'M', + 970: 'Q', + 965: 'T', + 959: 'Y', + 947: 'Z', +}; + +function correct_season(letter, dir) { + const month = new Date().getMonth() + 1; + const location = require("Storage").readJSON("mylocation.json",1)||{"lat":51.5072,"lon":0.1276,"location":"London"}; + const northern_hemisphere = location.lat > 0; + const summer = northern_hemisphere ? (month >= 4 && month <= 9) : (month >= 10 || month <= 3); + + let corr = 0; + if (dir < 0 && !summer) { // Winter falling + corr = -1; + } else if (dir > 0 && summer) { // Summer rising + corr = 1; + } + return String.fromCharCode(letter.charCodeAt(0)+corr); +} + +function get_zambretti_letter(pressure, dir) { + let table = () => { + if (dir < 0) { // Barometer Falling + return ZAMBRETTI_FALLING; + } else if (dir == 0) { // Barometer Steady + return ZAMBRETTI_STEADY; + } else { // Barometer Rising + return ZAMBRETTI_RISING; + } + }(); + + const closest = Object.keys(table).reduce(function(prev, curr) { + return (Math.abs(curr - pressure) < Math.abs(prev - pressure) ? curr : prev); + }); + + return correct_season(table[closest], dir); +} + +function loadSettings() { + const settings = require('Storage').readJSON("zambretti.json", true) || {}; + height = settings.height; +} + +function showMenu() { + const menu = { + "" : { + title : "Zambretti Forecast", + }, + "< Back": () => { + E.showMenu(); + layout.forgetLazyState(); + show(); + }, + /*LANG*/"Exit": () => load(), + /*LANG*/"Settings": () => + eval(require('Storage').read('zambretti.settings.js'))(() => { + loadSettings(); + showMenu(); + }), + 'Plot history': () => {E.showMenu(); history();}, + }; + E.showMenu(menu); +} + +const layout = new Layout({ + type:"v", c: [ + {type:"txt", font:"9%", label:/*LANG*/"Zambretti Forecast", bgCol:g.theme.bgH, fillx: true, pad: 1}, + {type:"txt", font:"12%", id:"forecast", filly: 1, wrap: 1, width: Bangle.appRect.w, pad: 1}, + {type:"h", c:[ + {type: 'v', c:[ + {type:"txt", font:"9%", label:/*LANG*/"Pressure ", pad: 3, halign: -1}, + {type:"txt", font:"9%", label:/*LANG*/"Difference", pad: 3, halign: -1}, + {type:"txt", font:"9%", label:/*LANG*/"Temperature", pad: 3, halign: -1}, + ]}, + {type: 'v', c:[ + {type:"txt", font:"9%", id:"pressure", pad: 3, halign: -1}, + {type:"txt", font:"9%", id:"diff", pad: 3, halign: -1}, + {type:"txt", font:"9%", id:"temp", pad: 3, halign: -1}, + ]} + ]}, + ] +}, {lazy:true}); + +function draw(temperature) { + const history3 = storage.readJSON("zambretti.log.json", true) || []; // history of recent 3 hours + const pressure_cur = history3[history3.length-1].p; + const pressure_last = history3[0].p; + const diff = pressure_cur - pressure_last; + const pressure_sea = Math.round(pressure_cur * Math.pow(1 - (0.0065 * height) / (temperature + (0.0065 * height) + 273.15),-5.257)); + + layout.forecast.label = ZAMBRETTI_FORECAST[get_zambretti_letter(pressure_sea, diff)]; + layout.pressure.label = pressure_sea; + layout.diff.label = diff; + layout.temp.label = require("locale").number(temperature,1); + layout.render(); + //layout.debug(); + + mainScreen = true; + Bangle.setUI({ + mode: "custom", + btn: (n) => {mainScreen = false; showMenu();}, + }); +} + +function show() { + Bangle.getPressure().then(p =>{if (p) draw(p.temperature);}); +} + +function history() { + const interval = 15; // minutes + const history3 = require('Storage').readJSON("zambretti.log.json", true) || []; // history of recent 3 hours + + const now = new Date()/(1000); + let curtime = now-3*60*60; // 3h ago + const data = []; + while (curtime <= now) { + // find closest value in history for this timestamp + const closest = history3.reduce((prev, curr) => { + return (Math.abs(curr.ts - curtime) < Math.abs(prev.ts - curtime) ? curr : prev); + }); + data.push(closest.p); + curtime += interval*60; + } + + Bangle.setUI({ + mode: "custom", + back: () => showMenu(), + }); + + g.reset().setFont("6x8",1); + require("graph").drawLine(g, data, { + axes: true, + x: 4, + y: Bangle.appRect.y+8, + height: Bangle.appRect.h-20, + gridx: 1, + gridy: 1, + miny: Math.min.apply(null, data)-1, + maxy: Math.max.apply(null, data)+1, + title: /*LANG*/"Barometer history (mBar)", + ylabel: y => y, + xlabel: i => { + const t = -3*60 + interval*i; + if (t % 60 === 0) { + return "-" + t/60 + "h"; + } + return ""; + }, + }); +} + +g.reset().clear(); +loadSettings(); +Bangle.loadWidgets(); + +if (height === undefined) { + // setting of height required + eval(require('Storage').read('zambretti.settings.js'))(() => { + loadSettings(); + show(); + }); +} else { + show(); +} +Bangle.drawWidgets(); + +// Update every 15 minutes +setInterval(() => { + if (mainScreen) { + show(); + } +}, 15*60*1000); diff --git a/apps/zambretti/app.png b/apps/zambretti/app.png new file mode 100644 index 0000000000000000000000000000000000000000..8db4fb8f80025d946d1532929de170ad46422a65 GIT binary patch literal 716 zcmV;-0yF)IP)UmR65Re-IO6P#eh30Q|1V{`8Jb}l@fVZcCtx@+{j5n_f0sxu=dtj!Wc{_yt z+#m)UhqsN@HeSrP%HB?XZznjdN~`BP0mz;i%!-BEp0@_I69DV_KWA%CVkk{d;9YjE zno`w@$s(j=gILnUa$|N0Cwd29z1ui#9&6il0#G^MI9Gmk74sk0L9AWNWGS0myArqr zIC&eya-?}`K`5z{k~{r_u=c#o;7)lQ`BD;@!Y0y%pU4z8kT1<(r+mHffBC~r&*sSn z(Z3`Tv4*kwmocoYy*Sdy^4d$xe>%Et+)FC2R}kwZ#6b#+@wVoBQKpiKo&Yo;YyjJd zvA&z9=1@~#0P=61`2s*n*82jGSuXkmz_QmDfVBGIQv$j-2_#o>idf$xrE{cY*Hrs; ze-hBv9BdKmr!$4Uvt5}txpvuP`R12latG1Y>qh71ttaDv&C@?^CF7pkCJ1+)4kw|c zN(#em0UA9Y0K?>%g1+A#KVh~;aFYeX#9W(zppmH9}KB1MX1c+hY3>;8rh58J!|0000HbA literal 0 HcmV?d00001 diff --git a/apps/zambretti/boot.js b/apps/zambretti/boot.js new file mode 100644 index 000000000..3d23e2a44 --- /dev/null +++ b/apps/zambretti/boot.js @@ -0,0 +1,106 @@ +{ + // Copied from widbaroalarm + const LOG_FILE = "zambretti.log.json"; + const history3 = require('Storage').readJSON(LOG_FILE, true) || []; // history of recent 3 hours + let currentPressures = []; + + isValidPressureValue = (pressure) => { + return !(pressure == undefined || pressure <= 0); + }; + + calculcate3hAveragePressure = () => { + if (history3 != undefined && 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; + } + }; + + handlePressureValue = (pressure) => { + if (pressure == undefined || pressure <= 0) { + return; + } + + const ts = Math.round(Date.now() / 1000); // seconds + const d = {"ts" : ts, "p" : pressure}; + + history3.push(d); + + // delete oldest entries until we have max 50 + while (history3.length > 50) { + history3.shift(); + } + + // delete entries older than 3h + for (let i = 0; i < history3.length; i++) { + if (history3[i].ts < ts - (3 * 60 * 60)) { + history3.shift(); + } else { + break; + } + } + + // write data to storage + require('Storage').writeJSON(LOG_FILE, history3); + + calculcate3hAveragePressure(); + }; + + barometerPressureHandler = (e) => { + const MEDIANLENGTH = 20; + while (currentPressures.length > MEDIANLENGTH) + currentPressures.pop(); + + const pressure = e.pressure; + if (isValidPressureValue(pressure)) { + currentPressures.unshift(pressure); + let median = currentPressures.slice().sort(); + + 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(); + handlePressureValue(medianPressure); + } + } + } + }; + + /* + turn on barometer power + take multiple measurements + sort the results + take the middle one (median) + turn off barometer power + */ + getPressureValue = () => { + Bangle.setBarometerPower(true, "zambretti"); + Bangle.on('pressure', barometerPressureHandler); + setTimeout(turnOff, 30000); + }; + + turnOff = () => { + Bangle.removeListener('pressure', barometerPressureHandler); + Bangle.setBarometerPower(false, "zambretti"); + }; + + // delay pressure measurement by interval-lastrun + const interval = 15; // minutes + const lastRun = history3.length > 0 ? history3[history3.length-1].ts : 0; + const lastRunAgo = Math.round(Date.now() / 1000) - lastRun; + let diffNextRun = interval*60-lastRunAgo; + if (diffNextRun < 0) { + diffNextRun = 0; // run asap + } + setTimeout(() => { + if (interval > 0) { + setInterval(getPressureValue, interval * 60000); + } + getPressureValue(); + }, diffNextRun*1000); +} diff --git a/apps/zambretti/metadata.json b/apps/zambretti/metadata.json new file mode 100644 index 000000000..7feb5e3f1 --- /dev/null +++ b/apps/zambretti/metadata.json @@ -0,0 +1,20 @@ +{ + "id": "zambretti", + "name": "Zambretti Weather Forecaster", + "shortName": "Zb. Weather", + "version": "0.01", + "description": "Zambretti Forecaster, uses the Barometer for empirical weather forecast in the Northern Hemisphere (see https://web.archive.org/web/20110610213848/http://www.meteormetrics.com/zambretti.htm), similar to weather stations. Watch must be stationary and its height above sea level set.", + "icon": "app.png", + "tags": "outdoors,weather", + "supports": ["BANGLEJS2"], + "dependencies": {"mylocation":"app"}, + "screenshots": [ {"url":"screenshot.png"} ], + "storage": [ + {"name":"zambretti.app.js","url":"app.js"}, + {"name":"zambretti.settings.js","url":"settings.js"}, + {"name":"zambretti.boot.js","url":"boot.js"}, + {"name":"zambretti.img","url":"app-icon.js","evaluate":true} + ], + "data": [{"name":"zambretti.json"}, {"name":"zambretti.log.json"}] +} + diff --git a/apps/zambretti/screenshot.png b/apps/zambretti/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..1f01fd3d4d5f65190faef5fdf68b6b15ed390a29 GIT binary patch literal 3934 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 back(), + 'Height above sea level (m)': { + value: settings.height, + min: 0, max: 1000, + onchange: v => { + settings.height = v; + writeSettings(); + } + }, + }); +}) diff --git a/lang/de_DE.json b/lang/de_DE.json index 9f9af6b18..5c21cb995 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -228,5 +228,34 @@ "quarter to *$2": "viertel vor *$2", "ten to *$2": "zehn vor *$2", "five to *$2": "fünf vor *$2" + }, + "zambretti": { + "//": "App-specific overrides", + "Settled Fine": "Beständig sonnig", + "Fine Weather": "Sonniges Wetter", + "Becoming Fine": "Es wird schöner", + "Fine Becoming Less Settled": "Sonnig, Tendenz unbeständiger", + "Fine, Possibly showers": "Sonnig, eventuell Schauer", + "Fairly Fine, Improving": "Heiter bis wolkig, Besserung zu erwarten", + "Fairly Fine, Possibly showers, early": "Heiter bis wolkig, anfangs evtl. Schauer", + "Fairly Fine Showery Later": "Heiter bis wolkig, später Regen", + "Showery Early, Improving": "Anfangs noch Schauer, dann Besserung", + "Changeable Mending": "Wechselhaft mit Schauern", + "Fairly Fine, Showers likely": "Heiter bis wolkig, vereinzelt Regen", + "Rather Unsettled Clearing Later": "Unbeständig, spaeter Aufklarung", + "Unsettled, Probably Improving": "Unbeständig, evtl. Besserung.", + "Showery Bright Intervals": "Regnerisch mit heiteren Phasen", + "Showery Becoming more unsettled": "Regnerisch, wird unbeständiger", + "Changeable some rain": "Wechselhaft mit etwas Regen", + "Unsettled, short fine Intervals": "Unbeständig mit heiteren Phasen", + "Unsettled, Rain later": "Unbeständig, später Regen", + "Unsettled, rain at times": "Unbeständig mit etwas Regen", + "Very Unsettled, Finer at times": "Wechselhaft und regnerisch", + "Rain at times, worse later.": "Gelegentlich Regen, Verschlechterung", + "Rain at times, becoming very unsettled": "Zuweilen Regen, sehr unbeständig", + "Rain at Frequent Intervals": "Häufiger Regen", + "Very Unsettled, Rain": "Regen, sehr unbeständig", + "Stormy, possibly improving": "Stürmisch, evtl. Besserung", + "Stormy, much rain": "Stürmisch mit viel Regen" } -} \ No newline at end of file +} From dd0d5b2cd4bd9f26b731e233697b6e6c948829ff Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sun, 4 May 2025 10:10:04 +0200 Subject: [PATCH 49/57] zambretti: attempt to fix parser error --- apps/zambretti/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/zambretti/app.js b/apps/zambretti/app.js index ef714e9d9..69e226ac0 100644 --- a/apps/zambretti/app.js +++ b/apps/zambretti/app.js @@ -94,7 +94,7 @@ function correct_season(letter, dir) { } function get_zambretti_letter(pressure, dir) { - let table = () => { + let table = (() => { if (dir < 0) { // Barometer Falling return ZAMBRETTI_FALLING; } else if (dir == 0) { // Barometer Steady @@ -102,7 +102,7 @@ function get_zambretti_letter(pressure, dir) { } else { // Barometer Rising return ZAMBRETTI_RISING; } - }(); + })(); const closest = Object.keys(table).reduce(function(prev, curr) { return (Math.abs(curr - pressure) < Math.abs(prev - pressure) ? curr : prev); From 359a75f5c2945c0e74b39037d81c70bca69fe2f6 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sun, 4 May 2025 10:15:34 +0200 Subject: [PATCH 50/57] lint.. --- apps/zambretti/app.js | 68 +++++++++++++++++++++--------------------- apps/zambretti/boot.js | 14 ++++----- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/apps/zambretti/app.js b/apps/zambretti/app.js index 69e226ac0..6ba88c591 100644 --- a/apps/zambretti/app.js +++ b/apps/zambretti/app.js @@ -38,44 +38,44 @@ Z: /*LANG*/'Stormy, much rain', }; const ZAMBRETTI_FALLING = { - 1050: 'A', - 1040: 'B', - 1024: 'C', - 1018: 'H', - 1010: 'O', - 1004: 'R', - 998: 'U', - 991: 'V', - 985: 'X', +1050: 'A', +1040: 'B', +1024: 'C', +1018: 'H', +1010: 'O', +1004: 'R', + 998: 'U', + 991: 'V', + 985: 'X', }; const ZAMBRETTI_STEADY = { - 1033: 'A', - 1023: 'B', - 1014: 'E', - 1008: 'K', - 1000: 'N', - 994: 'P', - 989: 'S', - 981: 'W', - 974: 'X', - 960: 'Z', +1033: 'A', +1023: 'B', +1014: 'E', +1008: 'K', +1000: 'N', + 994: 'P', + 989: 'S', + 981: 'W', + 974: 'X', + 960: 'Z', }; const ZAMBRETTI_RISING = { - 1030: 'A', - 1022: 'B', - 1012: 'C', - 1007: 'F', - 1000: 'G', - 995: 'I', - 990: 'J', - 984: 'L', - 978: 'M', - 970: 'Q', - 965: 'T', - 959: 'Y', - 947: 'Z', +1030: 'A', +1022: 'B', +1012: 'C', +1007: 'F', +1000: 'G', + 995: 'I', + 990: 'J', + 984: 'L', + 978: 'M', + 970: 'Q', + 965: 'T', + 959: 'Y', + 947: 'Z', }; function correct_season(letter, dir) { @@ -132,7 +132,7 @@ function showMenu() { loadSettings(); showMenu(); }), - 'Plot history': () => {E.showMenu(); history();}, + 'Plot history': () => {E.showMenu(); plot();}, }; E.showMenu(menu); } @@ -181,7 +181,7 @@ function show() { Bangle.getPressure().then(p =>{if (p) draw(p.temperature);}); } -function history() { +function plot() { const interval = 15; // minutes const history3 = require('Storage').readJSON("zambretti.log.json", true) || []; // history of recent 3 hours diff --git a/apps/zambretti/boot.js b/apps/zambretti/boot.js index 3d23e2a44..438ac7cfc 100644 --- a/apps/zambretti/boot.js +++ b/apps/zambretti/boot.js @@ -4,11 +4,11 @@ const history3 = require('Storage').readJSON(LOG_FILE, true) || []; // history of recent 3 hours let currentPressures = []; - isValidPressureValue = (pressure) => { + const isValidPressureValue = (pressure) => { return !(pressure == undefined || pressure <= 0); }; - calculcate3hAveragePressure = () => { + const calculcate3hAveragePressure = () => { if (history3 != undefined && history3.length > 0) { let sum = 0; for (let i = 0; i < history3.length; i++) { @@ -20,7 +20,7 @@ } }; - handlePressureValue = (pressure) => { + const handlePressureValue = (pressure) => { if (pressure == undefined || pressure <= 0) { return; } @@ -50,7 +50,7 @@ calculcate3hAveragePressure(); }; - barometerPressureHandler = (e) => { + const barometerPressureHandler = (e) => { const MEDIANLENGTH = 20; while (currentPressures.length > MEDIANLENGTH) currentPressures.pop(); @@ -62,7 +62,7 @@ if (median.length > 10) { var mid = median.length >> 1; - medianPressure = Math.round(E.sum(median.slice(mid - 4, mid + 5)) / 9); + let medianPressure = Math.round(E.sum(median.slice(mid - 4, mid + 5)) / 9); if (medianPressure > 0) { turnOff(); handlePressureValue(medianPressure); @@ -78,13 +78,13 @@ take the middle one (median) turn off barometer power */ - getPressureValue = () => { + const getPressureValue = () => { Bangle.setBarometerPower(true, "zambretti"); Bangle.on('pressure', barometerPressureHandler); setTimeout(turnOff, 30000); }; - turnOff = () => { + const turnOff = () => { Bangle.removeListener('pressure', barometerPressureHandler); Bangle.setBarometerPower(false, "zambretti"); }; From 176508ac34a505a0cb6f918c23ab187dc80183fe Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sun, 4 May 2025 10:19:48 +0200 Subject: [PATCH 51/57] lint.. --- apps/zambretti/boot.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/apps/zambretti/boot.js b/apps/zambretti/boot.js index 438ac7cfc..1ff4f512d 100644 --- a/apps/zambretti/boot.js +++ b/apps/zambretti/boot.js @@ -8,18 +8,6 @@ return !(pressure == undefined || pressure <= 0); }; - const calculcate3hAveragePressure = () => { - if (history3 != undefined && 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; - } - }; - const handlePressureValue = (pressure) => { if (pressure == undefined || pressure <= 0) { return; @@ -46,8 +34,6 @@ // write data to storage require('Storage').writeJSON(LOG_FILE, history3); - - calculcate3hAveragePressure(); }; const barometerPressureHandler = (e) => { From af1148c67fbae1c031a0ef2a2a6c8b4823d633d0 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sun, 4 May 2025 12:03:19 +0200 Subject: [PATCH 52/57] zambretti: remove extra space --- apps/zambretti/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/zambretti/app.js b/apps/zambretti/app.js index 6ba88c591..1a043e2f5 100644 --- a/apps/zambretti/app.js +++ b/apps/zambretti/app.js @@ -143,7 +143,7 @@ const layout = new Layout({ {type:"txt", font:"12%", id:"forecast", filly: 1, wrap: 1, width: Bangle.appRect.w, pad: 1}, {type:"h", c:[ {type: 'v', c:[ - {type:"txt", font:"9%", label:/*LANG*/"Pressure ", pad: 3, halign: -1}, + {type:"txt", font:"9%", label:/*LANG*/"Pressure", pad: 3, halign: -1}, {type:"txt", font:"9%", label:/*LANG*/"Difference", pad: 3, halign: -1}, {type:"txt", font:"9%", label:/*LANG*/"Temperature", pad: 3, halign: -1}, ]}, From 64ee5b561679e921785a96a903c1c71f068bf55d Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sun, 4 May 2025 16:24:47 +0200 Subject: [PATCH 53/57] zambretti: rename zb -> zn --- apps/zambretti/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/zambretti/metadata.json b/apps/zambretti/metadata.json index 7feb5e3f1..a1fc0f263 100644 --- a/apps/zambretti/metadata.json +++ b/apps/zambretti/metadata.json @@ -1,9 +1,9 @@ { "id": "zambretti", "name": "Zambretti Weather Forecaster", - "shortName": "Zb. Weather", + "shortName": "Zn. Weather", "version": "0.01", - "description": "Zambretti Forecaster, uses the Barometer for empirical weather forecast in the Northern Hemisphere (see https://web.archive.org/web/20110610213848/http://www.meteormetrics.com/zambretti.htm), similar to weather stations. Watch must be stationary and its height above sea level set.", + "description": "Zambretti Forecaster by Negretti and Zambra, uses the Barometer for empirical weather forecast in the Northern Hemisphere (see https://web.archive.org/web/20110610213848/http://www.meteormetrics.com/zambretti.htm), similar to weather stations. Watch must be stationary and its height above sea level set.", "icon": "app.png", "tags": "outdoors,weather", "supports": ["BANGLEJS2"], From 9f8b367bb30561263d4ee6f69344dca540822705 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 00:42:03 +0000 Subject: [PATCH 54/57] build(deps): bump webtools from `b03155d` to `8b02a59` Bumps [webtools](https://github.com/espruino/EspruinoWebTools) from `b03155d` to `8b02a59`. - [Commits](https://github.com/espruino/EspruinoWebTools/compare/b03155d1835a9904c335b65dc22326cb606e7cdd...8b02a5932f88c652a9d59a947b7a2e14afe5d8f2) --- updated-dependencies: - dependency-name: webtools dependency-version: 8b02a5932f88c652a9d59a947b7a2e14afe5d8f2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- webtools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webtools b/webtools index b03155d18..8b02a5932 160000 --- a/webtools +++ b/webtools @@ -1 +1 @@ -Subproject commit b03155d1835a9904c335b65dc22326cb606e7cdd +Subproject commit 8b02a5932f88c652a9d59a947b7a2e14afe5d8f2 From 20577d9a820739649e7cfa82a5ffaee501621d0f Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Mon, 5 May 2025 07:50:29 +0200 Subject: [PATCH 55/57] zambretti: fix summer/winter --- apps/zambretti/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/zambretti/app.js b/apps/zambretti/app.js index 1a043e2f5..7501a1911 100644 --- a/apps/zambretti/app.js +++ b/apps/zambretti/app.js @@ -86,11 +86,11 @@ function correct_season(letter, dir) { let corr = 0; if (dir < 0 && !summer) { // Winter falling - corr = -1; + corr = +1; } else if (dir > 0 && summer) { // Summer rising - corr = 1; + corr = -1; } - return String.fromCharCode(letter.charCodeAt(0)+corr); + return letter == 'A' || letter == 'Z' ? letter : String.fromCharCode(letter.charCodeAt(0)+corr); } function get_zambretti_letter(pressure, dir) { From 55eaa319dfccf894ba750333c78c8a12f3cd69da Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Mon, 5 May 2025 12:42:31 +0200 Subject: [PATCH 56/57] zambretti round only for display --- apps/zambretti/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/zambretti/app.js b/apps/zambretti/app.js index 7501a1911..475810302 100644 --- a/apps/zambretti/app.js +++ b/apps/zambretti/app.js @@ -161,10 +161,10 @@ function draw(temperature) { const pressure_cur = history3[history3.length-1].p; const pressure_last = history3[0].p; const diff = pressure_cur - pressure_last; - const pressure_sea = Math.round(pressure_cur * Math.pow(1 - (0.0065 * height) / (temperature + (0.0065 * height) + 273.15),-5.257)); + const pressure_sea = pressure_cur * Math.pow(1 - (0.0065 * height) / (temperature + (0.0065 * height) + 273.15),-5.257); layout.forecast.label = ZAMBRETTI_FORECAST[get_zambretti_letter(pressure_sea, diff)]; - layout.pressure.label = pressure_sea; + layout.pressure.label = Math.round(pressure_sea); layout.diff.label = diff; layout.temp.label = require("locale").number(temperature,1); layout.render(); From e9527899a68107141263c92305bd15e64ad88b99 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 6 May 2025 12:15:24 +0100 Subject: [PATCH 57/57] updated espruinotools after minifying error fixed --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index c357d5103..ef99424a9 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit c357d51033c2bd09dbfc243736e16ced6c69ec62 +Subproject commit ef99424a9fbd01be504841a6c759ba4292a542f7