From baa9516f5e6105d986c8e3d324e2783d2d908bda Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 16:46:48 +0100 Subject: [PATCH 01/40] initial commit --- apps/regattatimer/app.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/regattatimer/app.js diff --git a/apps/regattatimer/app.js b/apps/regattatimer/app.js new file mode 100644 index 000000000..8337712ea --- /dev/null +++ b/apps/regattatimer/app.js @@ -0,0 +1 @@ +// From 39e524f6f45463575431d64b9b9208825bb21feb Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 16:53:24 +0100 Subject: [PATCH 02/40] Add files via upload --- apps/regattatimer/README.md | 7 + apps/regattatimer/app-icon.js | 1 + apps/regattatimer/metadata.json | 18 + apps/regattatimer/regattatimer.app.js | 368 ++++++++++++++++++ apps/regattatimer/regattatimer.json | 8 + apps/regattatimer/regattatimer.settings.js | 46 +++ .../regattatimer.translations.json | 10 + apps/regattatimer/screenshot (1).png | Bin 0 -> 1950 bytes apps/regattatimer/screenshot (2).png | Bin 0 -> 2134 bytes apps/regattatimer/screenshot.png | Bin 0 -> 1805 bytes 10 files changed, 458 insertions(+) create mode 100644 apps/regattatimer/README.md create mode 100644 apps/regattatimer/app-icon.js create mode 100644 apps/regattatimer/metadata.json create mode 100644 apps/regattatimer/regattatimer.app.js create mode 100644 apps/regattatimer/regattatimer.json create mode 100644 apps/regattatimer/regattatimer.settings.js create mode 100644 apps/regattatimer/regattatimer.translations.json create mode 100644 apps/regattatimer/screenshot (1).png create mode 100644 apps/regattatimer/screenshot (2).png create mode 100644 apps/regattatimer/screenshot.png diff --git a/apps/regattatimer/README.md b/apps/regattatimer/README.md new file mode 100644 index 000000000..f838ee9c0 --- /dev/null +++ b/apps/regattatimer/README.md @@ -0,0 +1,7 @@ +require("locale").name + +Go to: https://banglejs.com/apps/ +Search for app: Languages +Click The arrow Up or burger icon +Choose your language fro the dropdown +click "upload" diff --git a/apps/regattatimer/app-icon.js b/apps/regattatimer/app-icon.js new file mode 100644 index 000000000..0d5ffe739 --- /dev/null +++ b/apps/regattatimer/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mUvgP/AAM/AYQAB54FE8YFE/GfAof+kYMEhF/AofCh4FD8mBAof5mQyD/nM44kDmc4CQec54SD58zCQfj5n4Egfzn4SC/n85/nEgU/mf5CQUf5hrDx4YBNYXH/mPCQRuBk/+LwPx//JCoJ0B//yv/zAoN//kf/g2BE4JvBAYMD//D/4+B+Ef+BNBFYOA/kDFYX+g8wh6dC+EP4EnCILDDj/AAocHHIIAC+I5BXoZoBTwd/ZYZ5BA4IFCCAOHAonwIARYB/sH3/2hwgBgED+EOIwMggkAwEQgE+g/A/Efg/9/+/+/+DAMfwfwvwYMggA=")) diff --git a/apps/regattatimer/metadata.json b/apps/regattatimer/metadata.json new file mode 100644 index 000000000..00da0c0e3 --- /dev/null +++ b/apps/regattatimer/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "regattatimer", + "name": "Regatta Timer", + "shortName": "Regatta Timer", + "version": "0.1", + "description": "Regatta Timer with classic 5-4-1 Countdown", + "icon": "icon.png", + "screenshots": [{"url":"screenshot.png"}], + "tags": "tool,outdoors,sailing,race,regatta,boat,timer", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"regattatimer.app.js","url":"app.js"}, + {"name":"regattatimer.settings.js","url":"settings.js"}, + {"name":"regattatimer.img","url":"app-icon.js","evaluate":true} + ], + "data": [{"name": "regattatimer.json"}] +} diff --git a/apps/regattatimer/regattatimer.app.js b/apps/regattatimer/regattatimer.app.js new file mode 100644 index 000000000..90fb908b8 --- /dev/null +++ b/apps/regattatimer/regattatimer.app.js @@ -0,0 +1,368 @@ +/** + * Regatta Timer + * + */ +const DEBUG = true; +const Layout = require("Layout"); +const locale = require("locale").name.substring(0, 2); + +// "Anton" bold font +Graphics.prototype.setFontAnton = function(scale) { + // Actual height 69 (68 - 0) + g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAA/gAAAAAAAAAAP/gAAAAAAAAAH//gAAAAAAAAB///gAAAAAAAAf///gAAAAAAAP////gAAAAAAD/////gAAAAAA//////gAAAAAP//////gAAAAH///////gAAAB////////gAAAf////////gAAP/////////gAD//////////AA//////////gAA/////////4AAA////////+AAAA////////gAAAA///////wAAAAA//////8AAAAAA//////AAAAAAA/////gAAAAAAA////4AAAAAAAA///+AAAAAAAAA///gAAAAAAAAA//wAAAAAAAAAA/8AAAAAAAAAAA/AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////AAAAAB///////8AAAAH////////AAAAf////////wAAA/////////4AAB/////////8AAD/////////+AAH//////////AAP//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wA//8AAAAAB//4A//wAAAAAAf/4A//gAAAAAAP/4A//gAAAAAAP/4A//gAAAAAAP/4A//wAAAAAAf/4A///////////4Af//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH//////////AAD/////////+AAB/////////8AAA/////////4AAAP////////gAAAD///////+AAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAAAAAAAAAP/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/AAAAAAAAAAA//AAAAAAAAAAA/+AAAAAAAAAAB/8AAAAAAAAAAD//////////gAH//////////gAP//////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAAAB/gAAD//4AAAAf/gAAP//4AAAB//gAA///4AAAH//gAB///4AAAf//gAD///4AAA///gAH///4AAD///gAP///4AAH///gAP///4AAP///gAf///4AAf///gAf///4AB////gAf///4AD////gA////4AH////gA////4Af////gA////4A/////gA//wAAB/////gA//gAAH/////gA//gAAP/////gA//gAA///8//gA//gAD///w//gA//wA////g//gA////////A//gA///////8A//gA///////4A//gAf//////wA//gAf//////gA//gAf/////+AA//gAP/////8AA//gAP/////4AA//gAH/////gAA//gAD/////AAA//gAB////8AAA//gAA////wAAA//gAAP///AAAA//gAAD//8AAAA//gAAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAD/wAAB//8AAAAP/wAAB///AAAA//wAAB///wAAB//wAAB///4AAD//wAAB///8AAH//wAAB///+AAP//wAAB///+AAP//wAAB////AAf//wAAB////AAf//wAAB////gAf//wAAB////gA///wAAB////gA///wAAB////gA///w//AAf//wA//4A//AAA//wA//gA//AAAf/wA//gB//gAAf/wA//gB//gAAf/wA//gD//wAA//wA//wH//8AB//wA///////////gA///////////gA///////////gA///////////gAf//////////AAf//////////AAP//////////AAP/////////+AAH/////////8AAH///+/////4AAD///+f////wAAA///8P////gAAAf//4H///+AAAAH//gB///wAAAAAP4AAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAAAAAAAAAA//wAAAAAAAAAP//wAAAAAAAAB///wAAAAAAAAf///wAAAAAAAH////wAAAAAAA/////wAAAAAAP/////wAAAAAB//////wAAAAAf//////wAAAAH///////wAAAA////////wAAAP////////wAAA///////H/wAAA//////wH/wAAA/////8AH/wAAA/////AAH/wAAA////gAAH/wAAA///4AAAH/wAAA//+AAAAH/wAAA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAH/4AAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//8AAA/////+B///AAA/////+B///wAA/////+B///4AA/////+B///8AA/////+B///8AA/////+B///+AA/////+B////AA/////+B////AA/////+B////AA/////+B////gA/////+B////gA/////+B////gA/////+A////gA//gP/gAAB//wA//gf/AAAA//wA//gf/AAAAf/wA//g//AAAAf/wA//g//AAAA//wA//g//gAAA//wA//g//+AAP//wA//g////////gA//g////////gA//g////////gA//g////////gA//g////////AA//gf///////AA//gf//////+AA//gP//////+AA//gH//////8AA//gD//////4AA//gB//////wAA//gA//////AAAAAAAH////8AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////gAAAAB///////+AAAAH////////gAAAf////////4AAB/////////8AAD/////////+AAH//////////AAH//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wAf//////////4A//wAD/4AAf/4A//gAH/wAAP/4A//gAH/wAAP/4A//gAP/wAAP/4A//gAP/4AAf/4A//wAP/+AD//4A///wP//////4Af//4P//////wAf//4P//////wAf//4P//////wAf//4P//////wAP//4P//////gAP//4H//////gAH//4H//////AAH//4D/////+AAD//4D/////8AAB//4B/////4AAA//4A/////wAAAP/4AP////AAAAB/4AD///4AAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAADgA//gAAAAAAP/gA//gAAAAAH//gA//gAAAAB///gA//gAAAAP///gA//gAAAD////gA//gAAAf////gA//gAAB/////gA//gAAP/////gA//gAB//////gA//gAH//////gA//gA///////gA//gD///////gA//gf///////gA//h////////gA//n////////gA//////////gAA/////////AAAA////////wAAAA///////4AAAAA///////AAAAAA//////4AAAAAA//////AAAAAAA/////4AAAAAAA/////AAAAAAAA////8AAAAAAAA////gAAAAAAAA///+AAAAAAAAA///4AAAAAAAAA///AAAAAAAAAA//4AAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gB///wAAAAP//4H///+AAAA///8P////gAAB///+f////4AAD///+/////8AAH/////////+AAH//////////AAP//////////gAP//////////gAf//////////gAf//////////wAf//////////wAf//////////wA///////////wA//4D//wAB//4A//wB//gAA//4A//gA//gAAf/4A//gA//AAAf/4A//gA//gAAf/4A//wB//gAA//4A///P//8AH//4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////gAP//////////gAP//////////AAH//////////AAD/////////+AAD///+/////8AAB///8f////wAAAf//4P////AAAAH//wD///8AAAAA/+AAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAB///+AA/+AAAAP////gA//wAAAf////wA//4AAB/////4A//8AAD/////8A//+AAD/////+A///AAH/////+A///AAP//////A///gAP//////A///gAf//////A///wAf//////A///wAf//////A///wAf//////A///wA///////AB//4A//4AD//AAP/4A//gAB//AAP/4A//gAA//AAP/4A//gAA/+AAP/4A//gAB/8AAP/4A//wAB/8AAf/4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH/////////+AAD/////////8AAB/////////4AAAf////////wAAAP////////AAAAB///////4AAAAAD/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAB/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("EiAnGicnJycnJycnEw=="), 78 + (scale << 8) + (1 << 16)); +}; + +/* +function Dial(dial) { + return { + "numeric": { + splash: function() { + + }, + start: function() { + }, + race: function() { + } + }, + "circle": { + start: function() { + }, + race: function() { + } + } + }[dial]; +} +*/ +function Regattatimer() { + return { + layout: undefined, + mode: "idle", // idle, start, race" + countdown: DEBUG ? 1 : 300, // 5 minutes + counter: undefined, + interval: undefined, + raceTimeStart: undefined, + + // compass settings + calibrated: false, + directions: [ + "N", + // NNO: {22.5, 22.5}, + "NE", + // ONO: {67.5, 67.5}, + "E", + // OSO: {112.5, 112.5}, + "SE", + // SSO: {157.5, 157.5}, + "S", + // SSW: {202.5, 202.5}, + "SW", + // WSW: {247.5, 247.5}, + "W", + // WNW: {292.5, 292.5}, + "NW", + // NNW: {337.5, 337.5}, + ], + + settings: Object.assign({ + "dial": "numeric", + "gps": false, + "compass": false, + "language": "en", + "fgColor": "#FFFF00", + "bgColor": "#000000" + }, require('Storage').readJSON("regattatimer.json", true) || {}), + + translations: Object.assign({ + "de": { + "speed": "FüG", + "speed_unit": "kn" + }, + "en": { + "speed": "SOA", + "speed_unit": "kn" + } + }, require('Storage').readJSON("translations.json", true) || {}), + + translate: function(slug) { + return this.translations[this.settings.language][slug]; + }, + /** + * During the start phase, the the clock counts down 5 4 1 minutes. + * On button press, the countdown begins again. + */ + startCounter: function() { + + this.counter --; + + if(this.counter >= 0) { + var counterMinutes = parseInt(this.counter / 60); + + if(counterMinutes > 0) { + this.layout.minutes.label = counterMinutes; + this.layout.seconds.label = "0".concat(this.counter - counterMinutes * 60).toString().slice(-2); + this.layout.render(); + } + else { + this.setLayoutStartSec(); + //this.layout.seconds.label = "0".concat(this.counter.toString()).slice(-2); + this.layout.seconds.label = this.counter.toString(); + this.layout.render(); + } + + // this keeps the watch LCD lit up + g.flip(); + } + // time is up + else { + this.raceCounterStart(); + } + }, + padZeroLeft: function(s) { + return "0".concat(s).slice(-2); + }, + formatTime: function(time) { + var hours = parseInt(time / 3600); + var minutes = parseInt(time / 60); + var seconds = time - (minutes * 60); + + return this.padZeroLeft(hours.toString()) + .concat(":") + .concat(this.padZeroLeft(minutes.toString())) + .concat(":") + .concat(this.padZeroLeft(seconds.toString())); + }, + raceCounter: function() { + Bangle.setLCDPower(1); + + this.counter ++; + + this.layout.racetime.label = this.formatTime(this.counter); + this.layout.daytime.label = require("locale").time(new Date(), 1); + this.layout.render(); + + // keeps the watch screen lit up + g.flip(); + }, + raceCounterStop: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + this.mode = "stoped"; + }, + raceCounterStart: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + + Bangle.buzz(); + + this.counter = 0; + + // switch to race mode + this.mode = "race"; + this.setLayoutRace(); + this.raceCounter(); + this.interval = setInterval((() => { + this.raceCounter(); + }).bind(this), 1000); + }, + /** + * Show an initial splash screen + */ + /* + setLayoutSplash: function() { + + g.clear(); + + (new Layout({ + type: "v", + bgCol: this.settings.bgColor, + c: [ + {type: "txt", font: "17%", label: ""}, + {type: "txt", font: "17%", col: this.settings.fgColor, label: "REGATTA\nTIMER"}, + {type: "txt", font: "6x8", col: this.settings.fgColor, label: "v0.2"}, + {type: "txt", font: "6x8", col: this.settings.fgColor, fillx: true, filly: true, pad: 4, label: "BUTTON PUSH -> start"}, + {type: "txt", font: "6x8", col: this.settings.fgColor, fillx: true, pad: 4, label: "(c) 2021-2023\nNaden Badalgogtapeh"} + ]}, {lazy:true})).render(); + }, + */ + + setLayoutIdle: function() { + + g.clear(); + + this.mode = "idle"; + + (new Layout({ + type: "v", + bgCol: this.settings.bgColor, + c: [ + { + type: "h", + c: [ + {type: "txt", font: "Anton", label: "5", col: this.settings.fgColor, id: "minutes", fillx: 1, filly: 1}, + ] + } + ]}, {lazy:true})).render(); + }, + resetCounter: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + this.counter = this.countdown; + }, + setLayoutStartMinSec: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.settings.bgColor, + c: [ + { + type: "h", + c: [ + {type: "txt", font: "Anton", label: "4", col: this.settings.fgColor, id: "minutes", fillx: 1, filly: 1}, + {type: "txt", font: "Anton", label: "59", col: this.settings.fgColor, id: "seconds", fillx: 1, filly: 1}, + ] + } + ]}, {lazy:true}); + + //this.layout.render(); + }, + setLayoutStartSec: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.settings.bgColor, + c:[ + {type: "txt", font: "Anton", label: "", fillx: true, filly: true, col: this.settings.fgColor, id: "seconds"}, + ]}, {lazy:true}); + }, + setLayoutRace: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.settings.bgColor, + c: [ + {type: "txt", font: "20%", label: "00:00:00", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, id: "racetime"}, + {type: "txt", font: "20%", label: "-", col: this.settings.fgColor, pad: 4, filly:1, fillx:1, id: "compass"}, + // horizontal + {type: "h", c: [ + {type:"txt", font:"10%", label: this.translate("speed"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, + {type:"txt", font:"15%", label: "...", col: this.settings.fgColor, pad:4, fillx:1, filly:1, id: "speed"}, + {type:"txt", font:"10%", label: this.translate("speed_unit"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, + ]}, + {type: "h", c: [ + {type: "txt", font: "10%", label: "-", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, id: "satellites"}, + {type: "txt", font: "10%", label: "00:00", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, halign: 1, id: "daytime"}, + ]} + + ]},{lazy:true}); + }, + onGPS: function(fix) { + if(this.mode == "race") { + if(fix.fix && isFinite(fix.speed)) { + this.layout.speed.label = fix.speed.toFixed(2); //m[1]; + } + this. layout.satellites.label = "Satellites ".concat(fix.satellites); + } + }, + /* + onCompass: function(data) { + + if(this.mode == "race") { + if(this.calibrated) { + this.layout.compass.label = this.directions[data.heading.toFixed(0) % 8]; + } + else { + this.layout.compass.label = "turn 360°"; + } + + var start = data.heading.toFixed(0) - 90; + + if(start<0) { + start += 360; + } + + var frag = 15 - start%15; + if (frag<15) {}else frag = 0; + var res = start + frag; + + var label = '?'; + + if(res%90==0){ + label = this.directions[Math.floor(res/45)%8]; + } else if (res%45==0) { + label = this.directions[Math.floor(res/45)%8]; + } + + //this.layout.compass.label = this.directions[data.heading.toFixed(0) % 8].concat(" ").concat(data.heading.toFixed(0).toString()) + this.layout.compass.label = label.concat(" ").concat(data.heading.toFixed(0).toString().concat("°")) + } + }, + */ + init: function() { + + this.setLayoutIdle(); + + Bangle.setLCDPower(1); + + var onButtonClick = ((ev) => { + + // "idle" or "start" mode, a button click (re)starts the start counter + switch(this.mode) { + case "idle": + this.resetCounter(); + this.mode = "start"; + this.setLayoutStartMinSec(); + this.startCounter(); + this.interval = setInterval((() => { + this.startCounter(); + }).bind(this), 1000); + break; + case "stoped": + case "start": + this.resetCounter(); + this.setLayoutIdle(); + break; + // "race" mode, a button click triggers a time snapshot and stops + case "race": + this.raceCounterStop(); + break; + } + + }).bind(this); + + setWatch(onButtonClick, BTN1, true); + } + } +} + +var regattatimer = Regattatimer() + +regattatimer.init(); + +if(regattatimer.gps) { + Bangle.setGPSPower(1); + Bangle.on('GPS', regattatimer.onGPS.bind(regattatimer)); +} + +if(regattatimer.compass) { + Bangle.setCompassPower(1); + Bangle.on('mag', regattatimer.onCompass.bind(regattatimer)); +} + +Bangle.on('kill',function() { + if(regattatimer.gps) { + Bangle.setGPSPower(0); + } + + if(regattatimer.compass) { + Bangle.setCompassPower(0); + } +}); diff --git a/apps/regattatimer/regattatimer.json b/apps/regattatimer/regattatimer.json new file mode 100644 index 000000000..3f81dedb5 --- /dev/null +++ b/apps/regattatimer/regattatimer.json @@ -0,0 +1,8 @@ +{ + "dial": "numeric", + "gps": false, + "compass": false, + "language": "en", + "fgColor": "#FFFF00", + "bgColor": "#000000" +} diff --git a/apps/regattatimer/regattatimer.settings.js b/apps/regattatimer/regattatimer.settings.js new file mode 100644 index 000000000..8e177a1a4 --- /dev/null +++ b/apps/regattatimer/regattatimer.settings.js @@ -0,0 +1,46 @@ +(function(back) { + var file = "regattatimer.json"; + // Load settings + var settings = Object.assign({ + "dial": "numeric", + "gps": false, + "compass": false, + "language": "en", + "fgColor": "#FFFF00", + "bgColor": "#000000" + }, require('Storage').readJSON(file, true) || {}); + + function writeSettings() { + require('Storage').writeJSON(file, settings); + } + + // Show the menu + E.showMenu({ + "" : { "title" : "Regatta Timer" }, + "< Back" : () => back(), + 'GPS': { + value: !!settings.gps, // !! converts undefined to false + onchange: v => { + settings.gps = v; + writeSettings(); + } + }, + 'COMPASS': { + value: !!settings.gps, // 0| converts undefined to 0 + onchange: v => { + settings.compass = v; + writeSettings(); + } + } + "DIAL": { + value: settings.dial, + min: 0, + max: 1, + format: v => ["Numeric", "Disc"][v], + onchange: v => { + settings.dial = v; + writeSettings(); + } + } + }); +}) diff --git a/apps/regattatimer/regattatimer.translations.json b/apps/regattatimer/regattatimer.translations.json new file mode 100644 index 000000000..e89eeddf8 --- /dev/null +++ b/apps/regattatimer/regattatimer.translations.json @@ -0,0 +1,10 @@ +{ + "de": { + "speed": "FüG", + "speed_unit": "kn" + }, + "en": { + "speed": "SOA", + "speed_unit": "kn" + } +} diff --git a/apps/regattatimer/screenshot (1).png b/apps/regattatimer/screenshot (1).png new file mode 100644 index 0000000000000000000000000000000000000000..a24bfa88715623b1386413c57d99fca915c501c9 GIT binary patch literal 1950 zcmds&{X5j@9>=F|x6G)l`C5{b$C>e%v1(cJxWiyp6w-Pa)2z~*4K)&t#;R`}i%f%= zhFDZ=2w|O!hsf8S9vq6}gl3zV*~#P42&Gh({nqaJ56(~Lhx>kiuGf9tpZDjwulIG` z*?WWhHftGb;c&Rk41b>xOdCHe{03}qXc=c=vMwpakA|!7-!_B8X@)a=zTC%SsY?cr zKXcv$Bx`HaOqZzJOYPf=lw2HbuA|;-@56qBk$bDVgwNh9HK9O4ni=<4K)A9&Y06QF6F zf)tuW^ST2l+{#umSLPVd9SiF73W3ylYvj#NG)yHz%?9GPwCJ}+vS8Wd#Qe{88e?BF zVV=xkHbV`@7#RJ}f-9r$M! zO3gU(xU&T9>im(!z46!jxk7k3-k8oVq&3g7Xv+LALhwx6;#xi*@f+{#_WsDFdc1v4 zU0I-B@8NLgH8ePt!7lVznw7#lUnVy4jw$UY_`03|$W^Z<8oT&mkjU<*qb>S8Ne8(x zs-k_V+eRznv98RkO}DOTF)7&n8)2-jPZ8U@6y)Whn(0GZT2+;-rSSpl zlP=lK@4eXhQ`!wwmEBFMAA4b0CmdK6kQ%k-ee*8wfbiSPEfM;e7#FA($6)7Q*)K#5 zyDGe)hIB}`bL#8gD&kgBcH;%;H&Dgmg%y>cJ_A+!nq+9R_Y-z?Hg7P?KVSdFTxzA_ zmLt&sAqmS^vnyH+)ja|o)la@ij=Hj!125DOp*=?D_TPM`^WxTwP2)jDmqYsS>~lAB zJ_%`74g@zt`z~fnBuI3%@8e9Ie@>sd zI%LirzsO?dpr@}`!8XnKm0uy-MYrDf5Wp54RHuZFEpCD_eV=muMnJ;#0vpCfg zbL;x?C8BED-B*!EZ#bR&67P2Hj-q_chq+0^@XDkhu1uI#`O8Q^o5n4V(l*QDF z7K5$*8u-b&U)e!9k*4z|XU_i(D@pU#NN}5P0|~Lr2vr!ZjYz$rfnL2-v1IVv7m4D< z%aUXr-Xz7t{KfOXG!+>SaJunkUyVj6i=s}ZUwL`^GCmZPx!lbx#>f98Vb%+SNpFe= zpAi9OA=p+Ui-}16p1IXshgSr#USazofI3W~- zq1@Jdygd)K=*RkRC+vo%swIyyfK#FcX3k*4&VDyasAR4-r`?Nfz3#o`j)J*@yQcGQ zyNvEHY}Uh{pm;$i-CbUnzO>Np*WtpZNa=|Pc@Pd!XML+ApFxA#>k=%_pec7g=fYlW z%jglQt{2-i#?YBy+9;;R$uTi<*jxg1WQ@XQH73GK8-gJG^B?=3q(Jz1zuoIC*MXFO z$0oS015#2YBE~_kGfmP`93&?`LK(O}?3A8je}PHhp*|1n!^JV^K|b{~_SxS7D)VLU literal 0 HcmV?d00001 diff --git a/apps/regattatimer/screenshot (2).png b/apps/regattatimer/screenshot (2).png new file mode 100644 index 0000000000000000000000000000000000000000..932028ae09d44ca916be36d5bcf4c7ee901ed26d GIT binary patch literal 2134 zcmd6p`!kz~8pnw@q)A(e5-m~@TP+f2jUJalf{n&)>)Hq@O|46}i%{1bT79uDQN*q| zt)i-SP^m<86N{~sQfKSZ3zZzUf~ZqP+*7T~vH!*X@I0TH&&+q`ndkZCDfBx}IRZC; ztEs6S@$?`E98mCA!88v#U#ti}fO=8@#Z9ec*yw|r8Vu=4b`44oU#j-McG2!I@XHtD z|C*aqar`tW$LJ;?-P1Fks%_A(5C?EoVzKxVa|d+shbUneA3H>3>9{b+XTDVfy~io( zKtK2zK(;4e&!sSo{_Zjl*z4Or`u=;F(pVR<%9}Q@LX3SmaZ{!zuVGg z-bFrOrnNkbzm9#o@ruVnrgz|$a@aj-%y&w%?t12A%uuc*RJ(y0s2k69#>CIK_Bp|v z)NhQNvU^a{D||ymn2?ykv=gyeQpxEP|B_vEk-M##*IB^lg9~1Wa^0N9FtDAf`px?a zI*bbzsjy!|{$cT0l?cS+1a$+-GraEXt*c-5C-jo;oo$Y&37B$g)Vy&@;W@YV&&sSg z+V64u82ivkN)Q@J3SIJq=KmZWIGD7dNj+lU{K_*bYp_A*#oCQRX((GcS zYxU}w4@ySaULrDEiCzrLMf@NPRVTc0&l;z~_lAvxRF$Hn@D8{i;hW@leAd`+!4Ai?Mu`_LeCnZ|+gk3Wg{_rN85kWwtgtq2UVNDNnWSHx#H*MoYKqWMr2 zdJw$YznX*)&azibE$*Mpc~Tj+Nh~5j5TmVFM7T*O&YQWR*ygSSoipL<~wB>MVJvG7|)%67Qy!cSxc$>DCkvSgWO!;pVX}o^=S3GEIN7uq!g2se7*bGvc`B$t*SMb67e5Fp_98|5$>e(+G(GBc(q$n-UouKU0ut6I*&LJ^H&S;)8PR zdfJxHxi)3x<<74XM$t7;&g$f0h3uj~J>J%LBgrNfm0rs|A<-D$PuVa7V`Nr{%s1$o zA8$Rg1w%xz*tT6KYlM`w`{Fyh*l9hH4#<@DSaQlf#Xu|%4s&@N&Un80zHFGD&VA$~ zKY*U_`^H(9;KE=TPGB-(!N|YcVv2!M1zh;f7+GHmmsmM!9eujxOEZB4>u-}2jWK)+ zVP#ySWQZ^$Kij?>d?rVRyEeW^u~ zPWR#L&e+)=XeW6%y7PiOwybU1>eK0rv)+oZ*ixi@s3I&O3Jten>#*!59sty$F2fdy zhh^H@Ma!gh*&f;nvaDtlgoB_+EjKnpN<)X27*2f`JuaYLr qQSEWRrX9|cq-{*5di~8KyBhT-a$~KMDa65}pyug*p4{Tb$o@a%)!qOA literal 0 HcmV?d00001 diff --git a/apps/regattatimer/screenshot.png b/apps/regattatimer/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..32f1993b534e69630a0c0404656a1e8522229f61 GIT binary patch literal 1805 zcmbtVX;_n28ol`v0$~$~ECH%wAX)@W*;O_vi`p2#Q3-o20u`tP5eWj4XaPrur8PjJ z5Qh2?nW7;Dg#ao25L%E$Y-!69mK36tB_RZ2SSn6tp7}Gs`{Ug6yzhC>x%ba~-oJ;E zgAH^obpZedCr<>N(Xik@&_Qd?##U*ph7cKNg8c#irS$>;=-!h7$IsGZWm7+0+kZ?y zAWZpwZ}ns11v}!@Y4H>LPu_Ay^;)7QKGV0&@V(qBo-g*b4RvMi0_HO7gc&R#8l5@- zBtOd@(n0(`uR_3S%IA(Y5&f5&0+b|V_3-Ju+(&&iHW-ot#X$Wm-tVmy*F<3DRQ8Ux z_ZV4=?i#&s+a?6zX{F(PmFTJuoXS*XpFn)HdEe2#IuKr@C#Cna z??BYCVSuKs#AS3NZmFY}gr}B8!4~h{_W>ef*sjcoNEb);?%wfjtPR%zNTDM$fYBu$ zJ<3yLT<#_i8HmYBKM1}oTKt!rID0>}?KcZ&X0<{1T%K$4k1lOSvE#jl^fy7nxh;qK zSi{w3f)r(^dv5{#mra&M&TlPHY+YMJRkL>VdIk?!mEe5DDCtOwYk!3*l@Xr$$DO3h zUFNG}mq1c=P;yVi@CVGbK}$2Z!6RL@R>K_IW-A#DqN4Y2JQsc)+|5LGJY|X?4 z7v@|ynwHBWQR8{Qy>JLsvdc=7;>`R%b9>uX51R;zQYLIS^Ci~g)S z3DR3THy4w)H9hRRHKT2|g==@Ed#@rXWzEQyzJ-p=&M-9%Xd}6}6;5-T4bsy{!9fRq zKL5qf8BXDOz|iE^5$W`k7|+h7j_XdDPk{W~`h0fygUQI7mOso~=W|cT9AZSNL|!|| zx1w6ZZ+YIzYAAeY&2mEu&%J_QEbT~ZRU?JoJ)o08Qk_*xhf366Hd}p!6^3o-2un@M z?>+vgu)C<0EI*Wk6xw(zxO#f<{FNr3WZPJ^>i1*;=)6yrtOa*#t)#oO`CCixA=2z% zxd%<1x^e00=UtMi{Djtv4{yw2BwcYNQHQJ^Arj{=eUB6#aO=*9w2VJ^K-Q>RJ@jIA zHXeF^!b`kcdvdvEyX9hCkjP6?k;qWTL5>^*rJh%3{M?yaao!_3+t8;Vl=tAzp~$+@ zfszU7pC<7yN`G=DPemwvsK<;CXKKH^-Ks3VkY<9U(8@vMzF(^C5Z3I>AfYK=|2v(j zG%2ss|6)(nLjS8fW5rpE9_fr1ly*yH0pe?-3LXSs!_+8#l< zZmC+ROr)tBl2X0+B#g!>X=H={ zYS{|jPK1u)&CRc#-PgPbAI;Z~*d%OMW;@Tm?k_V>Yd;j=4oilLJxr;JH@#nd``k54 z;=tgv<2dqB7DS|{)V?_D$jEQ+k`DDKa~-=8rQA-~yVPzDUy0VA{)eqN*;1D-UDryp zVm&U6H6+rS&n!2jSV)XV%td)8oPASpn)E@z_boPzDNXC0Uz0Bu9zPb4SA-cuxa1k* zIDmm4x#*Y!org(rIr)!fjNp-0x*RzHT=M1%T^Fx{J>_28g|<}0;JU$r>D^RYK&7zA zeunIYsEOdM*+Bd`nF z;DRh_>ETT$5;d$x0|ALj5ENA;nG*yDlQf_Zh}f}@2b%S;8UvQH+X2hQEZ4psFTgnT zb1)9N6_`QP+gHlv=P0w+gD5kt0aPNC^I7(_3((ywiO}7f36KlZ`>9t4kr{Zzg&El8 z%;dm(BDpba`0FYT{56#Wlc>8Khc(xSyj=E&YB7EJc>L2yGdy#&VMKOq+t$>i{yxA8 zljrNjYlM=jlYSh4rEr{52CzWj^{IO(0ZXlKgm$X1{@xf#d+bKlSFn0q{XK;{X5v literal 0 HcmV?d00001 From a60620cf6496cf3be2693dc511af4e7621a44926 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 17:06:55 +0100 Subject: [PATCH 03/40] Update README.md --- apps/regattatimer/README.md | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/apps/regattatimer/README.md b/apps/regattatimer/README.md index f838ee9c0..8b5957b0b 100644 --- a/apps/regattatimer/README.md +++ b/apps/regattatimer/README.md @@ -1,7 +1,32 @@ -require("locale").name +Regatta Timer with 5-4-1 countdown -Go to: https://banglejs.com/apps/ -Search for app: Languages -Click The arrow Up or burger icon -Choose your language fro the dropdown -click "upload" +Modes + +- Idle mode + + On startup the application is in idle mode showing a large 5 in the centre of the screen. + Click the button to switch to start mode. + +- Start mode + + Countdown from 5 minutes to 0. During the countdown, the screen changes layout several + several times to use as much space as possible to display the numbers. + When 0 is reached, the buzzer sounds and the application switches to race mode. + A click on the button switches back to idle mode. + +- Race Mode + + Race time, local time, SOA and number of GPS satellites available are displayed. + A click on the button switches to "stopped mode". + +- Stoped mode + A button click switches to idle mode. + +Localization +- Go to: https://banglejs.com/apps/ +- Search for app: Languages +- Click the "arrow up" or "burger" icon +- Choose your language from the dropdown +- Click "upload" + +© by https://naden.de From 33fc9f2669e9ce4967d0a46a5d8a7a00274a9e52 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 17:07:56 +0100 Subject: [PATCH 04/40] Add files via upload --- apps/regattatimer/icon.png | Bin 0 -> 1825 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/regattatimer/icon.png diff --git a/apps/regattatimer/icon.png b/apps/regattatimer/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e919037ae93d1d76d76c8c169214b1c3d9c694ae GIT binary patch literal 1825 zcmV++2j2LJP)$g!1_=o?NJy|G^PZj_TDo*81qB7sfCh5+?p+cDfrq6@uq5-% zn>P=9AZ=}JG_udgr9r%s(pb#-;t2jt?#i>%;+1q(Qi$bpxo|Yat;a z^!xX3>i}}#zyVfj)v8th3y=YM`0ydmgM)*m`xO-xGO&-(Rz4&A$hN zqX6NywzgDRS*b5T2Bf*Undj4{O*6<#qtVdFkt2BxV$nYXf?($8=*WVzva$>dkO6u9 z`n4hu(2*lYc#WHz8~yn4gDegNPZv28gvQ3k8W$h~QeIvzMSNq_-rg?pO-)U;ED*$t zg$oz5u)x4TJ-aXlgmMmfBlO|1~moHzc+9wCHV#NxcBLJGB&dyFLeZi45DMyVmh}2@hgEx8g z>Xn*pT0rnrMGt4lO&g-b#6(`dapOkQa!f5iq6eb`!#Q*2Pv9q(|H>4=X*o~h*f6}g9yJ+apq0B5MCZ<<@T^C4FQWF0~7SJ2=@$un(_wV0V zmSgP3qD6}&B>(~iQ(s8rA*T0*g@t`ee^OFXl;s$^v3&V*t(x1##f7$S-%hu0-|ki2 zAm@QXs<#g$CntyZu3x{tZ$LngA3x?A!^_J{OQDGqC(@QJTlncBuBZh9hjHrEDb>af zdj9-5@5K=_i~B{r0sk-7+TyUcx2H{;Hqn(USJTv9X~&d-f;;0f_!qR8TQ+)D(e;Zvt-5 z+NiOyk?Va!?pP1VxN+lnp*gyL=A`=idMOORbAJB(SsBRMwQG4XzHL|wJ$UdyatLTp zeERfBZ;c+1r%#`9e+rxS@87RF!LtN)b#-xSBI<)O0_B8i_nV!aO~Z!|ms*)gMa0CF5l$0cW)q;DCi;FYa6EFz`St^K4lx547@qI*FtUZ7J zJV$cW+NKC=uof=9sHlh{BO@g{5CnlgX~V!l#l^+^f#}@1bBdX>fIchE=Xb{M+;oC??Mg~`f;1B;_R#wK>bW<7m@b~wpyu3Vm z^ym>CJ9dm^%J1Vl%HhL@Sx4xKw{`2*Eqe0g2?;nZ#u!5=-iEI7hQ`d9Gr6ILzOJdY zXuyH5nzb%2F1%Lk3mY07`%qWRnl+1)G;G3uDWJqHDJhX$FT4eMu{g7++u_pz4o^NI zA%W|9F*8MNeeK#c&T{dDkttwcj%&Cl*5BB3=gu8AZpd|Wb7S6UhC>#Ppy;Wf3;qEC zEhRJsp(}L8!$u~qXd`FNoT0F=Fg~YB1R<9$UFx-sz|EVAQ^n@C5dZ P00000NkvXXu0mjfl Date: Fri, 23 Feb 2024 17:12:21 +0100 Subject: [PATCH 05/40] Update metadata.json --- apps/regattatimer/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/regattatimer/metadata.json b/apps/regattatimer/metadata.json index 00da0c0e3..596f81d26 100644 --- a/apps/regattatimer/metadata.json +++ b/apps/regattatimer/metadata.json @@ -10,8 +10,8 @@ "supports": ["BANGLEJS2"], "readme": "README.md", "storage": [ - {"name":"regattatimer.app.js","url":"app.js"}, - {"name":"regattatimer.settings.js","url":"settings.js"}, + {"name":"regattatimer.app.js","url":"regattatimer.app.js"}, + {"name":"regattatimer.settings.js","url":"regattatimer.settings.js"}, {"name":"regattatimer.img","url":"app-icon.js","evaluate":true} ], "data": [{"name": "regattatimer.json"}] From dcb7078b55e2a2c72a858f608d403f6a25b17f2c Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 17:17:30 +0100 Subject: [PATCH 06/40] Delete apps/regattatimer/regattatimer.app.js --- apps/regattatimer/regattatimer.app.js | 368 -------------------------- 1 file changed, 368 deletions(-) delete mode 100644 apps/regattatimer/regattatimer.app.js diff --git a/apps/regattatimer/regattatimer.app.js b/apps/regattatimer/regattatimer.app.js deleted file mode 100644 index 90fb908b8..000000000 --- a/apps/regattatimer/regattatimer.app.js +++ /dev/null @@ -1,368 +0,0 @@ -/** - * Regatta Timer - * - */ -const DEBUG = true; -const Layout = require("Layout"); -const locale = require("locale").name.substring(0, 2); - -// "Anton" bold font -Graphics.prototype.setFontAnton = function(scale) { - // Actual height 69 (68 - 0) - g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAA/gAAAAAAAAAAP/gAAAAAAAAAH//gAAAAAAAAB///gAAAAAAAAf///gAAAAAAAP////gAAAAAAD/////gAAAAAA//////gAAAAAP//////gAAAAH///////gAAAB////////gAAAf////////gAAP/////////gAD//////////AA//////////gAA/////////4AAA////////+AAAA////////gAAAA///////wAAAAA//////8AAAAAA//////AAAAAAA/////gAAAAAAA////4AAAAAAAA///+AAAAAAAAA///gAAAAAAAAA//wAAAAAAAAAA/8AAAAAAAAAAA/AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////AAAAAB///////8AAAAH////////AAAAf////////wAAA/////////4AAB/////////8AAD/////////+AAH//////////AAP//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wA//8AAAAAB//4A//wAAAAAAf/4A//gAAAAAAP/4A//gAAAAAAP/4A//gAAAAAAP/4A//wAAAAAAf/4A///////////4Af//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH//////////AAD/////////+AAB/////////8AAA/////////4AAAP////////gAAAD///////+AAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAAAAAAAAAP/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/AAAAAAAAAAA//AAAAAAAAAAA/+AAAAAAAAAAB/8AAAAAAAAAAD//////////gAH//////////gAP//////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAAAB/gAAD//4AAAAf/gAAP//4AAAB//gAA///4AAAH//gAB///4AAAf//gAD///4AAA///gAH///4AAD///gAP///4AAH///gAP///4AAP///gAf///4AAf///gAf///4AB////gAf///4AD////gA////4AH////gA////4Af////gA////4A/////gA//wAAB/////gA//gAAH/////gA//gAAP/////gA//gAA///8//gA//gAD///w//gA//wA////g//gA////////A//gA///////8A//gA///////4A//gAf//////wA//gAf//////gA//gAf/////+AA//gAP/////8AA//gAP/////4AA//gAH/////gAA//gAD/////AAA//gAB////8AAA//gAA////wAAA//gAAP///AAAA//gAAD//8AAAA//gAAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAD/wAAB//8AAAAP/wAAB///AAAA//wAAB///wAAB//wAAB///4AAD//wAAB///8AAH//wAAB///+AAP//wAAB///+AAP//wAAB////AAf//wAAB////AAf//wAAB////gAf//wAAB////gA///wAAB////gA///wAAB////gA///w//AAf//wA//4A//AAA//wA//gA//AAAf/wA//gB//gAAf/wA//gB//gAAf/wA//gD//wAA//wA//wH//8AB//wA///////////gA///////////gA///////////gA///////////gAf//////////AAf//////////AAP//////////AAP/////////+AAH/////////8AAH///+/////4AAD///+f////wAAA///8P////gAAAf//4H///+AAAAH//gB///wAAAAAP4AAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAAAAAAAAAA//wAAAAAAAAAP//wAAAAAAAAB///wAAAAAAAAf///wAAAAAAAH////wAAAAAAA/////wAAAAAAP/////wAAAAAB//////wAAAAAf//////wAAAAH///////wAAAA////////wAAAP////////wAAA///////H/wAAA//////wH/wAAA/////8AH/wAAA/////AAH/wAAA////gAAH/wAAA///4AAAH/wAAA//+AAAAH/wAAA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAH/4AAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//8AAA/////+B///AAA/////+B///wAA/////+B///4AA/////+B///8AA/////+B///8AA/////+B///+AA/////+B////AA/////+B////AA/////+B////AA/////+B////gA/////+B////gA/////+B////gA/////+A////gA//gP/gAAB//wA//gf/AAAA//wA//gf/AAAAf/wA//g//AAAAf/wA//g//AAAA//wA//g//gAAA//wA//g//+AAP//wA//g////////gA//g////////gA//g////////gA//g////////gA//g////////AA//gf///////AA//gf//////+AA//gP//////+AA//gH//////8AA//gD//////4AA//gB//////wAA//gA//////AAAAAAAH////8AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////gAAAAB///////+AAAAH////////gAAAf////////4AAB/////////8AAD/////////+AAH//////////AAH//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wAf//////////4A//wAD/4AAf/4A//gAH/wAAP/4A//gAH/wAAP/4A//gAP/wAAP/4A//gAP/4AAf/4A//wAP/+AD//4A///wP//////4Af//4P//////wAf//4P//////wAf//4P//////wAf//4P//////wAP//4P//////gAP//4H//////gAH//4H//////AAH//4D/////+AAD//4D/////8AAB//4B/////4AAA//4A/////wAAAP/4AP////AAAAB/4AD///4AAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAADgA//gAAAAAAP/gA//gAAAAAH//gA//gAAAAB///gA//gAAAAP///gA//gAAAD////gA//gAAAf////gA//gAAB/////gA//gAAP/////gA//gAB//////gA//gAH//////gA//gA///////gA//gD///////gA//gf///////gA//h////////gA//n////////gA//////////gAA/////////AAAA////////wAAAA///////4AAAAA///////AAAAAA//////4AAAAAA//////AAAAAAA/////4AAAAAAA/////AAAAAAAA////8AAAAAAAA////gAAAAAAAA///+AAAAAAAAA///4AAAAAAAAA///AAAAAAAAAA//4AAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gB///wAAAAP//4H///+AAAA///8P////gAAB///+f////4AAD///+/////8AAH/////////+AAH//////////AAP//////////gAP//////////gAf//////////gAf//////////wAf//////////wAf//////////wA///////////wA//4D//wAB//4A//wB//gAA//4A//gA//gAAf/4A//gA//AAAf/4A//gA//gAAf/4A//wB//gAA//4A///P//8AH//4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////gAP//////////gAP//////////AAH//////////AAD/////////+AAD///+/////8AAB///8f////wAAAf//4P////AAAAH//wD///8AAAAA/+AAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAB///+AA/+AAAAP////gA//wAAAf////wA//4AAB/////4A//8AAD/////8A//+AAD/////+A///AAH/////+A///AAP//////A///gAP//////A///gAf//////A///wAf//////A///wAf//////A///wAf//////A///wA///////AB//4A//4AD//AAP/4A//gAB//AAP/4A//gAA//AAP/4A//gAA/+AAP/4A//gAB/8AAP/4A//wAB/8AAf/4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH/////////+AAD/////////8AAB/////////4AAAf////////wAAAP////////AAAAB///////4AAAAAD/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAB/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("EiAnGicnJycnJycnEw=="), 78 + (scale << 8) + (1 << 16)); -}; - -/* -function Dial(dial) { - return { - "numeric": { - splash: function() { - - }, - start: function() { - }, - race: function() { - } - }, - "circle": { - start: function() { - }, - race: function() { - } - } - }[dial]; -} -*/ -function Regattatimer() { - return { - layout: undefined, - mode: "idle", // idle, start, race" - countdown: DEBUG ? 1 : 300, // 5 minutes - counter: undefined, - interval: undefined, - raceTimeStart: undefined, - - // compass settings - calibrated: false, - directions: [ - "N", - // NNO: {22.5, 22.5}, - "NE", - // ONO: {67.5, 67.5}, - "E", - // OSO: {112.5, 112.5}, - "SE", - // SSO: {157.5, 157.5}, - "S", - // SSW: {202.5, 202.5}, - "SW", - // WSW: {247.5, 247.5}, - "W", - // WNW: {292.5, 292.5}, - "NW", - // NNW: {337.5, 337.5}, - ], - - settings: Object.assign({ - "dial": "numeric", - "gps": false, - "compass": false, - "language": "en", - "fgColor": "#FFFF00", - "bgColor": "#000000" - }, require('Storage').readJSON("regattatimer.json", true) || {}), - - translations: Object.assign({ - "de": { - "speed": "FüG", - "speed_unit": "kn" - }, - "en": { - "speed": "SOA", - "speed_unit": "kn" - } - }, require('Storage').readJSON("translations.json", true) || {}), - - translate: function(slug) { - return this.translations[this.settings.language][slug]; - }, - /** - * During the start phase, the the clock counts down 5 4 1 minutes. - * On button press, the countdown begins again. - */ - startCounter: function() { - - this.counter --; - - if(this.counter >= 0) { - var counterMinutes = parseInt(this.counter / 60); - - if(counterMinutes > 0) { - this.layout.minutes.label = counterMinutes; - this.layout.seconds.label = "0".concat(this.counter - counterMinutes * 60).toString().slice(-2); - this.layout.render(); - } - else { - this.setLayoutStartSec(); - //this.layout.seconds.label = "0".concat(this.counter.toString()).slice(-2); - this.layout.seconds.label = this.counter.toString(); - this.layout.render(); - } - - // this keeps the watch LCD lit up - g.flip(); - } - // time is up - else { - this.raceCounterStart(); - } - }, - padZeroLeft: function(s) { - return "0".concat(s).slice(-2); - }, - formatTime: function(time) { - var hours = parseInt(time / 3600); - var minutes = parseInt(time / 60); - var seconds = time - (minutes * 60); - - return this.padZeroLeft(hours.toString()) - .concat(":") - .concat(this.padZeroLeft(minutes.toString())) - .concat(":") - .concat(this.padZeroLeft(seconds.toString())); - }, - raceCounter: function() { - Bangle.setLCDPower(1); - - this.counter ++; - - this.layout.racetime.label = this.formatTime(this.counter); - this.layout.daytime.label = require("locale").time(new Date(), 1); - this.layout.render(); - - // keeps the watch screen lit up - g.flip(); - }, - raceCounterStop: function() { - if(this.interval) { - clearInterval(this.interval); - this.interval = undefined; - } - this.mode = "stoped"; - }, - raceCounterStart: function() { - if(this.interval) { - clearInterval(this.interval); - this.interval = undefined; - } - - Bangle.buzz(); - - this.counter = 0; - - // switch to race mode - this.mode = "race"; - this.setLayoutRace(); - this.raceCounter(); - this.interval = setInterval((() => { - this.raceCounter(); - }).bind(this), 1000); - }, - /** - * Show an initial splash screen - */ - /* - setLayoutSplash: function() { - - g.clear(); - - (new Layout({ - type: "v", - bgCol: this.settings.bgColor, - c: [ - {type: "txt", font: "17%", label: ""}, - {type: "txt", font: "17%", col: this.settings.fgColor, label: "REGATTA\nTIMER"}, - {type: "txt", font: "6x8", col: this.settings.fgColor, label: "v0.2"}, - {type: "txt", font: "6x8", col: this.settings.fgColor, fillx: true, filly: true, pad: 4, label: "BUTTON PUSH -> start"}, - {type: "txt", font: "6x8", col: this.settings.fgColor, fillx: true, pad: 4, label: "(c) 2021-2023\nNaden Badalgogtapeh"} - ]}, {lazy:true})).render(); - }, - */ - - setLayoutIdle: function() { - - g.clear(); - - this.mode = "idle"; - - (new Layout({ - type: "v", - bgCol: this.settings.bgColor, - c: [ - { - type: "h", - c: [ - {type: "txt", font: "Anton", label: "5", col: this.settings.fgColor, id: "minutes", fillx: 1, filly: 1}, - ] - } - ]}, {lazy:true})).render(); - }, - resetCounter: function() { - if(this.interval) { - clearInterval(this.interval); - this.interval = undefined; - } - this.counter = this.countdown; - }, - setLayoutStartMinSec: function() { - g.clear(); - - this.layout = new Layout({ - type: "v", - bgCol: this.settings.bgColor, - c: [ - { - type: "h", - c: [ - {type: "txt", font: "Anton", label: "4", col: this.settings.fgColor, id: "minutes", fillx: 1, filly: 1}, - {type: "txt", font: "Anton", label: "59", col: this.settings.fgColor, id: "seconds", fillx: 1, filly: 1}, - ] - } - ]}, {lazy:true}); - - //this.layout.render(); - }, - setLayoutStartSec: function() { - g.clear(); - - this.layout = new Layout({ - type: "v", - bgCol: this.settings.bgColor, - c:[ - {type: "txt", font: "Anton", label: "", fillx: true, filly: true, col: this.settings.fgColor, id: "seconds"}, - ]}, {lazy:true}); - }, - setLayoutRace: function() { - g.clear(); - - this.layout = new Layout({ - type: "v", - bgCol: this.settings.bgColor, - c: [ - {type: "txt", font: "20%", label: "00:00:00", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, id: "racetime"}, - {type: "txt", font: "20%", label: "-", col: this.settings.fgColor, pad: 4, filly:1, fillx:1, id: "compass"}, - // horizontal - {type: "h", c: [ - {type:"txt", font:"10%", label: this.translate("speed"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, - {type:"txt", font:"15%", label: "...", col: this.settings.fgColor, pad:4, fillx:1, filly:1, id: "speed"}, - {type:"txt", font:"10%", label: this.translate("speed_unit"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, - ]}, - {type: "h", c: [ - {type: "txt", font: "10%", label: "-", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, id: "satellites"}, - {type: "txt", font: "10%", label: "00:00", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, halign: 1, id: "daytime"}, - ]} - - ]},{lazy:true}); - }, - onGPS: function(fix) { - if(this.mode == "race") { - if(fix.fix && isFinite(fix.speed)) { - this.layout.speed.label = fix.speed.toFixed(2); //m[1]; - } - this. layout.satellites.label = "Satellites ".concat(fix.satellites); - } - }, - /* - onCompass: function(data) { - - if(this.mode == "race") { - if(this.calibrated) { - this.layout.compass.label = this.directions[data.heading.toFixed(0) % 8]; - } - else { - this.layout.compass.label = "turn 360°"; - } - - var start = data.heading.toFixed(0) - 90; - - if(start<0) { - start += 360; - } - - var frag = 15 - start%15; - if (frag<15) {}else frag = 0; - var res = start + frag; - - var label = '?'; - - if(res%90==0){ - label = this.directions[Math.floor(res/45)%8]; - } else if (res%45==0) { - label = this.directions[Math.floor(res/45)%8]; - } - - //this.layout.compass.label = this.directions[data.heading.toFixed(0) % 8].concat(" ").concat(data.heading.toFixed(0).toString()) - this.layout.compass.label = label.concat(" ").concat(data.heading.toFixed(0).toString().concat("°")) - } - }, - */ - init: function() { - - this.setLayoutIdle(); - - Bangle.setLCDPower(1); - - var onButtonClick = ((ev) => { - - // "idle" or "start" mode, a button click (re)starts the start counter - switch(this.mode) { - case "idle": - this.resetCounter(); - this.mode = "start"; - this.setLayoutStartMinSec(); - this.startCounter(); - this.interval = setInterval((() => { - this.startCounter(); - }).bind(this), 1000); - break; - case "stoped": - case "start": - this.resetCounter(); - this.setLayoutIdle(); - break; - // "race" mode, a button click triggers a time snapshot and stops - case "race": - this.raceCounterStop(); - break; - } - - }).bind(this); - - setWatch(onButtonClick, BTN1, true); - } - } -} - -var regattatimer = Regattatimer() - -regattatimer.init(); - -if(regattatimer.gps) { - Bangle.setGPSPower(1); - Bangle.on('GPS', regattatimer.onGPS.bind(regattatimer)); -} - -if(regattatimer.compass) { - Bangle.setCompassPower(1); - Bangle.on('mag', regattatimer.onCompass.bind(regattatimer)); -} - -Bangle.on('kill',function() { - if(regattatimer.gps) { - Bangle.setGPSPower(0); - } - - if(regattatimer.compass) { - Bangle.setCompassPower(0); - } -}); From 71a4e6dc8ccd4a6ddacda4b6b78a58e8c0f313a1 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 17:17:45 +0100 Subject: [PATCH 07/40] Update app.js --- apps/regattatimer/app.js | 369 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 368 insertions(+), 1 deletion(-) diff --git a/apps/regattatimer/app.js b/apps/regattatimer/app.js index 8337712ea..4287e9e2b 100644 --- a/apps/regattatimer/app.js +++ b/apps/regattatimer/app.js @@ -1 +1,368 @@ -// +/** + * Regatta Timer + * + */ +const DEBUG = true; +const Layout = require("Layout"); +const locale = require("locale").name.substring(0, 2); + +// "Anton" bold font +Graphics.prototype.setFontAnton = function(scale) { + // Actual height 69 (68 - 0) + g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAA/gAAAAAAAAAAP/gAAAAAAAAAH//gAAAAAAAAB///gAAAAAAAAf///gAAAAAAAP////gAAAAAAD/////gAAAAAA//////gAAAAAP//////gAAAAH///////gAAAB////////gAAAf////////gAAP/////////gAD//////////AA//////////gAA/////////4AAA////////+AAAA////////gAAAA///////wAAAAA//////8AAAAAA//////AAAAAAA/////gAAAAAAA////4AAAAAAAA///+AAAAAAAAA///gAAAAAAAAA//wAAAAAAAAAA/8AAAAAAAAAAA/AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////AAAAAB///////8AAAAH////////AAAAf////////wAAA/////////4AAB/////////8AAD/////////+AAH//////////AAP//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wA//8AAAAAB//4A//wAAAAAAf/4A//gAAAAAAP/4A//gAAAAAAP/4A//gAAAAAAP/4A//wAAAAAAf/4A///////////4Af//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH//////////AAD/////////+AAB/////////8AAA/////////4AAAP////////gAAAD///////+AAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAAAAAAAAAP/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/AAAAAAAAAAA//AAAAAAAAAAA/+AAAAAAAAAAB/8AAAAAAAAAAD//////////gAH//////////gAP//////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAAAB/gAAD//4AAAAf/gAAP//4AAAB//gAA///4AAAH//gAB///4AAAf//gAD///4AAA///gAH///4AAD///gAP///4AAH///gAP///4AAP///gAf///4AAf///gAf///4AB////gAf///4AD////gA////4AH////gA////4Af////gA////4A/////gA//wAAB/////gA//gAAH/////gA//gAAP/////gA//gAA///8//gA//gAD///w//gA//wA////g//gA////////A//gA///////8A//gA///////4A//gAf//////wA//gAf//////gA//gAf/////+AA//gAP/////8AA//gAP/////4AA//gAH/////gAA//gAD/////AAA//gAB////8AAA//gAA////wAAA//gAAP///AAAA//gAAD//8AAAA//gAAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAD/wAAB//8AAAAP/wAAB///AAAA//wAAB///wAAB//wAAB///4AAD//wAAB///8AAH//wAAB///+AAP//wAAB///+AAP//wAAB////AAf//wAAB////AAf//wAAB////gAf//wAAB////gA///wAAB////gA///wAAB////gA///w//AAf//wA//4A//AAA//wA//gA//AAAf/wA//gB//gAAf/wA//gB//gAAf/wA//gD//wAA//wA//wH//8AB//wA///////////gA///////////gA///////////gA///////////gAf//////////AAf//////////AAP//////////AAP/////////+AAH/////////8AAH///+/////4AAD///+f////wAAA///8P////gAAAf//4H///+AAAAH//gB///wAAAAAP4AAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAAAAAAAAAA//wAAAAAAAAAP//wAAAAAAAAB///wAAAAAAAAf///wAAAAAAAH////wAAAAAAA/////wAAAAAAP/////wAAAAAB//////wAAAAAf//////wAAAAH///////wAAAA////////wAAAP////////wAAA///////H/wAAA//////wH/wAAA/////8AH/wAAA/////AAH/wAAA////gAAH/wAAA///4AAAH/wAAA//+AAAAH/wAAA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAH/4AAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//8AAA/////+B///AAA/////+B///wAA/////+B///4AA/////+B///8AA/////+B///8AA/////+B///+AA/////+B////AA/////+B////AA/////+B////AA/////+B////gA/////+B////gA/////+B////gA/////+A////gA//gP/gAAB//wA//gf/AAAA//wA//gf/AAAAf/wA//g//AAAAf/wA//g//AAAA//wA//g//gAAA//wA//g//+AAP//wA//g////////gA//g////////gA//g////////gA//g////////gA//g////////AA//gf///////AA//gf//////+AA//gP//////+AA//gH//////8AA//gD//////4AA//gB//////wAA//gA//////AAAAAAAH////8AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////gAAAAB///////+AAAAH////////gAAAf////////4AAB/////////8AAD/////////+AAH//////////AAH//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wAf//////////4A//wAD/4AAf/4A//gAH/wAAP/4A//gAH/wAAP/4A//gAP/wAAP/4A//gAP/4AAf/4A//wAP/+AD//4A///wP//////4Af//4P//////wAf//4P//////wAf//4P//////wAf//4P//////wAP//4P//////gAP//4H//////gAH//4H//////AAH//4D/////+AAD//4D/////8AAB//4B/////4AAA//4A/////wAAAP/4AP////AAAAB/4AD///4AAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAADgA//gAAAAAAP/gA//gAAAAAH//gA//gAAAAB///gA//gAAAAP///gA//gAAAD////gA//gAAAf////gA//gAAB/////gA//gAAP/////gA//gAB//////gA//gAH//////gA//gA///////gA//gD///////gA//gf///////gA//h////////gA//n////////gA//////////gAA/////////AAAA////////wAAAA///////4AAAAA///////AAAAAA//////4AAAAAA//////AAAAAAA/////4AAAAAAA/////AAAAAAAA////8AAAAAAAA////gAAAAAAAA///+AAAAAAAAA///4AAAAAAAAA///AAAAAAAAAA//4AAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gB///wAAAAP//4H///+AAAA///8P////gAAB///+f////4AAD///+/////8AAH/////////+AAH//////////AAP//////////gAP//////////gAf//////////gAf//////////wAf//////////wAf//////////wA///////////wA//4D//wAB//4A//wB//gAA//4A//gA//gAAf/4A//gA//AAAf/4A//gA//gAAf/4A//wB//gAA//4A///P//8AH//4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////gAP//////////gAP//////////AAH//////////AAD/////////+AAD///+/////8AAB///8f////wAAAf//4P////AAAAH//wD///8AAAAA/+AAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAB///+AA/+AAAAP////gA//wAAAf////wA//4AAB/////4A//8AAD/////8A//+AAD/////+A///AAH/////+A///AAP//////A///gAP//////A///gAf//////A///wAf//////A///wAf//////A///wAf//////A///wA///////AB//4A//4AD//AAP/4A//gAB//AAP/4A//gAA//AAP/4A//gAA/+AAP/4A//gAB/8AAP/4A//wAB/8AAf/4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH/////////+AAD/////////8AAB/////////4AAAf////////wAAAP////////AAAAB///////4AAAAAD/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAB/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("EiAnGicnJycnJycnEw=="), 78 + (scale << 8) + (1 << 16)); +}; + +/* +function Dial(dial) { + return { + "numeric": { + splash: function() { + + }, + start: function() { + }, + race: function() { + } + }, + "circle": { + start: function() { + }, + race: function() { + } + } + }[dial]; +} +*/ +function Regattatimer() { + return { + layout: undefined, + mode: "idle", // idle, start, race" + countdown: DEBUG ? 1 : 300, // 5 minutes + counter: undefined, + interval: undefined, + raceTimeStart: undefined, + + // compass settings + calibrated: false, + directions: [ + "N", + // NNO: {22.5, 22.5}, + "NE", + // ONO: {67.5, 67.5}, + "E", + // OSO: {112.5, 112.5}, + "SE", + // SSO: {157.5, 157.5}, + "S", + // SSW: {202.5, 202.5}, + "SW", + // WSW: {247.5, 247.5}, + "W", + // WNW: {292.5, 292.5}, + "NW", + // NNW: {337.5, 337.5}, + ], + + settings: Object.assign({ + "dial": "numeric", + "gps": false, + "compass": false, + "language": "en", + "fgColor": "#FFFF00", + "bgColor": "#000000" + }, require('Storage').readJSON("regattatimer.json", true) || {}), + + translations: Object.assign({ + "de": { + "speed": "FüG", + "speed_unit": "kn" + }, + "en": { + "speed": "SOA", + "speed_unit": "kn" + } + }, require('Storage').readJSON("translations.json", true) || {}), + + translate: function(slug) { + return this.translations[this.settings.language][slug]; + }, + /** + * During the start phase, the the clock counts down 5 4 1 minutes. + * On button press, the countdown begins again. + */ + startCounter: function() { + + this.counter --; + + if(this.counter >= 0) { + var counterMinutes = parseInt(this.counter / 60); + + if(counterMinutes > 0) { + this.layout.minutes.label = counterMinutes; + this.layout.seconds.label = "0".concat(this.counter - counterMinutes * 60).toString().slice(-2); + this.layout.render(); + } + else { + this.setLayoutStartSec(); + //this.layout.seconds.label = "0".concat(this.counter.toString()).slice(-2); + this.layout.seconds.label = this.counter.toString(); + this.layout.render(); + } + + // this keeps the watch LCD lit up + g.flip(); + } + // time is up + else { + this.raceCounterStart(); + } + }, + padZeroLeft: function(s) { + return "0".concat(s).slice(-2); + }, + formatTime: function(time) { + var hours = parseInt(time / 3600); + var minutes = parseInt(time / 60); + var seconds = time - (minutes * 60); + + return this.padZeroLeft(hours.toString()) + .concat(":") + .concat(this.padZeroLeft(minutes.toString())) + .concat(":") + .concat(this.padZeroLeft(seconds.toString())); + }, + raceCounter: function() { + Bangle.setLCDPower(1); + + this.counter ++; + + this.layout.racetime.label = this.formatTime(this.counter); + this.layout.daytime.label = require("locale").time(new Date(), 1); + this.layout.render(); + + // keeps the watch screen lit up + g.flip(); + }, + raceCounterStop: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + this.mode = "stoped"; + }, + raceCounterStart: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + + Bangle.buzz(); + + this.counter = 0; + + // switch to race mode + this.mode = "race"; + this.setLayoutRace(); + this.raceCounter(); + this.interval = setInterval((() => { + this.raceCounter(); + }).bind(this), 1000); + }, + /** + * Show an initial splash screen + */ + /* + setLayoutSplash: function() { + + g.clear(); + + (new Layout({ + type: "v", + bgCol: this.settings.bgColor, + c: [ + {type: "txt", font: "17%", label: ""}, + {type: "txt", font: "17%", col: this.settings.fgColor, label: "REGATTA\nTIMER"}, + {type: "txt", font: "6x8", col: this.settings.fgColor, label: "v0.1"}, + {type: "txt", font: "6x8", col: this.settings.fgColor, fillx: true, filly: true, pad: 4, label: "BUTTON PUSH -> start"}, + {type: "txt", font: "6x8", col: this.settings.fgColor, fillx: true, pad: 4, label: "(c) 2021-2023\nNaden Badalgogtapeh"} + ]}, {lazy:true})).render(); + }, + */ + + setLayoutIdle: function() { + + g.clear(); + + this.mode = "idle"; + + (new Layout({ + type: "v", + bgCol: this.settings.bgColor, + c: [ + { + type: "h", + c: [ + {type: "txt", font: "Anton", label: "5", col: this.settings.fgColor, id: "minutes", fillx: 1, filly: 1}, + ] + } + ]}, {lazy:true})).render(); + }, + resetCounter: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + this.counter = this.countdown; + }, + setLayoutStartMinSec: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.settings.bgColor, + c: [ + { + type: "h", + c: [ + {type: "txt", font: "Anton", label: "4", col: this.settings.fgColor, id: "minutes", fillx: 1, filly: 1}, + {type: "txt", font: "Anton", label: "59", col: this.settings.fgColor, id: "seconds", fillx: 1, filly: 1}, + ] + } + ]}, {lazy:true}); + + //this.layout.render(); + }, + setLayoutStartSec: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.settings.bgColor, + c:[ + {type: "txt", font: "Anton", label: "", fillx: true, filly: true, col: this.settings.fgColor, id: "seconds"}, + ]}, {lazy:true}); + }, + setLayoutRace: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.settings.bgColor, + c: [ + {type: "txt", font: "20%", label: "00:00:00", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, id: "racetime"}, + {type: "txt", font: "20%", label: "-", col: this.settings.fgColor, pad: 4, filly:1, fillx:1, id: "compass"}, + // horizontal + {type: "h", c: [ + {type:"txt", font:"10%", label: this.translate("speed"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, + {type:"txt", font:"15%", label: "...", col: this.settings.fgColor, pad:4, fillx:1, filly:1, id: "speed"}, + {type:"txt", font:"10%", label: this.translate("speed_unit"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, + ]}, + {type: "h", c: [ + {type: "txt", font: "10%", label: "-", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, id: "satellites"}, + {type: "txt", font: "10%", label: "00:00", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, halign: 1, id: "daytime"}, + ]} + + ]},{lazy:true}); + }, + onGPS: function(fix) { + if(this.mode == "race") { + if(fix.fix && isFinite(fix.speed)) { + this.layout.speed.label = fix.speed.toFixed(2); //m[1]; + } + this. layout.satellites.label = "Satellites ".concat(fix.satellites); + } + }, + /* + onCompass: function(data) { + + if(this.mode == "race") { + if(this.calibrated) { + this.layout.compass.label = this.directions[data.heading.toFixed(0) % 8]; + } + else { + this.layout.compass.label = "turn 360°"; + } + + var start = data.heading.toFixed(0) - 90; + + if(start<0) { + start += 360; + } + + var frag = 15 - start%15; + if (frag<15) {}else frag = 0; + var res = start + frag; + + var label = '?'; + + if(res%90==0){ + label = this.directions[Math.floor(res/45)%8]; + } else if (res%45==0) { + label = this.directions[Math.floor(res/45)%8]; + } + + //this.layout.compass.label = this.directions[data.heading.toFixed(0) % 8].concat(" ").concat(data.heading.toFixed(0).toString()) + this.layout.compass.label = label.concat(" ").concat(data.heading.toFixed(0).toString().concat("°")) + } + }, + */ + init: function() { + + this.setLayoutIdle(); + + Bangle.setLCDPower(1); + + var onButtonClick = ((ev) => { + + // "idle" or "start" mode, a button click (re)starts the start counter + switch(this.mode) { + case "idle": + this.resetCounter(); + this.mode = "start"; + this.setLayoutStartMinSec(); + this.startCounter(); + this.interval = setInterval((() => { + this.startCounter(); + }).bind(this), 1000); + break; + case "stoped": + case "start": + this.resetCounter(); + this.setLayoutIdle(); + break; + // "race" mode, a button click triggers a time snapshot and stops + case "race": + this.raceCounterStop(); + break; + } + + }).bind(this); + + setWatch(onButtonClick, BTN1, true); + } + } +} + +var regattatimer = Regattatimer() + +regattatimer.init(); + +if(regattatimer.gps) { + Bangle.setGPSPower(1); + Bangle.on('GPS', regattatimer.onGPS.bind(regattatimer)); +} + +if(regattatimer.compass) { + Bangle.setCompassPower(1); + Bangle.on('mag', regattatimer.onCompass.bind(regattatimer)); +} + +Bangle.on('kill',function() { + if(regattatimer.gps) { + Bangle.setGPSPower(0); + } + + if(regattatimer.compass) { + Bangle.setCompassPower(0); + } +}); From 291bad8e35aa38dece4bef23f5b3aca98a07a245 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 17:18:34 +0100 Subject: [PATCH 08/40] Update metadata.json --- apps/regattatimer/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/regattatimer/metadata.json b/apps/regattatimer/metadata.json index 596f81d26..00da0c0e3 100644 --- a/apps/regattatimer/metadata.json +++ b/apps/regattatimer/metadata.json @@ -10,8 +10,8 @@ "supports": ["BANGLEJS2"], "readme": "README.md", "storage": [ - {"name":"regattatimer.app.js","url":"regattatimer.app.js"}, - {"name":"regattatimer.settings.js","url":"regattatimer.settings.js"}, + {"name":"regattatimer.app.js","url":"app.js"}, + {"name":"regattatimer.settings.js","url":"settings.js"}, {"name":"regattatimer.img","url":"app-icon.js","evaluate":true} ], "data": [{"name": "regattatimer.json"}] From a83af8166facc7a587558d806161d25920091a3e Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 17:19:59 +0100 Subject: [PATCH 09/40] Delete apps/regattatimer/regattatimer.settings.js --- apps/regattatimer/regattatimer.settings.js | 46 ---------------------- 1 file changed, 46 deletions(-) delete mode 100644 apps/regattatimer/regattatimer.settings.js diff --git a/apps/regattatimer/regattatimer.settings.js b/apps/regattatimer/regattatimer.settings.js deleted file mode 100644 index 8e177a1a4..000000000 --- a/apps/regattatimer/regattatimer.settings.js +++ /dev/null @@ -1,46 +0,0 @@ -(function(back) { - var file = "regattatimer.json"; - // Load settings - var settings = Object.assign({ - "dial": "numeric", - "gps": false, - "compass": false, - "language": "en", - "fgColor": "#FFFF00", - "bgColor": "#000000" - }, require('Storage').readJSON(file, true) || {}); - - function writeSettings() { - require('Storage').writeJSON(file, settings); - } - - // Show the menu - E.showMenu({ - "" : { "title" : "Regatta Timer" }, - "< Back" : () => back(), - 'GPS': { - value: !!settings.gps, // !! converts undefined to false - onchange: v => { - settings.gps = v; - writeSettings(); - } - }, - 'COMPASS': { - value: !!settings.gps, // 0| converts undefined to 0 - onchange: v => { - settings.compass = v; - writeSettings(); - } - } - "DIAL": { - value: settings.dial, - min: 0, - max: 1, - format: v => ["Numeric", "Disc"][v], - onchange: v => { - settings.dial = v; - writeSettings(); - } - } - }); -}) From e92165d357521ca430d0e0afc9d945a907aab314 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 17:20:32 +0100 Subject: [PATCH 10/40] Create settings.js --- apps/regattatimer/settings.js | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 apps/regattatimer/settings.js diff --git a/apps/regattatimer/settings.js b/apps/regattatimer/settings.js new file mode 100644 index 000000000..8e177a1a4 --- /dev/null +++ b/apps/regattatimer/settings.js @@ -0,0 +1,46 @@ +(function(back) { + var file = "regattatimer.json"; + // Load settings + var settings = Object.assign({ + "dial": "numeric", + "gps": false, + "compass": false, + "language": "en", + "fgColor": "#FFFF00", + "bgColor": "#000000" + }, require('Storage').readJSON(file, true) || {}); + + function writeSettings() { + require('Storage').writeJSON(file, settings); + } + + // Show the menu + E.showMenu({ + "" : { "title" : "Regatta Timer" }, + "< Back" : () => back(), + 'GPS': { + value: !!settings.gps, // !! converts undefined to false + onchange: v => { + settings.gps = v; + writeSettings(); + } + }, + 'COMPASS': { + value: !!settings.gps, // 0| converts undefined to 0 + onchange: v => { + settings.compass = v; + writeSettings(); + } + } + "DIAL": { + value: settings.dial, + min: 0, + max: 1, + format: v => ["Numeric", "Disc"][v], + onchange: v => { + settings.dial = v; + writeSettings(); + } + } + }); +}) From 972b05947088b3853b9b8127e003505cfa3cd14c Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 17:22:40 +0100 Subject: [PATCH 11/40] Update app.js --- apps/regattatimer/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regattatimer/app.js b/apps/regattatimer/app.js index 4287e9e2b..67992e333 100644 --- a/apps/regattatimer/app.js +++ b/apps/regattatimer/app.js @@ -2,7 +2,7 @@ * Regatta Timer * */ -const DEBUG = true; +const DEBUG = false; const Layout = require("Layout"); const locale = require("locale").name.substring(0, 2); From be897389287900f98f82d8db62b0a82a61ae391d Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 17:24:25 +0100 Subject: [PATCH 12/40] Update metadata.json --- apps/regattatimer/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regattatimer/metadata.json b/apps/regattatimer/metadata.json index 00da0c0e3..633b1685b 100644 --- a/apps/regattatimer/metadata.json +++ b/apps/regattatimer/metadata.json @@ -2,7 +2,7 @@ "id": "regattatimer", "name": "Regatta Timer", "shortName": "Regatta Timer", - "version": "0.1", + "version": "0.2", "description": "Regatta Timer with classic 5-4-1 Countdown", "icon": "icon.png", "screenshots": [{"url":"screenshot.png"}], From 2a76bc638b1b8d4f291de82e065ac87a30adcabe Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 20:21:37 +0100 Subject: [PATCH 13/40] Update settings.js --- apps/regattatimer/settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/regattatimer/settings.js b/apps/regattatimer/settings.js index 8e177a1a4..54ddb56bd 100644 --- a/apps/regattatimer/settings.js +++ b/apps/regattatimer/settings.js @@ -26,12 +26,12 @@ } }, 'COMPASS': { - value: !!settings.gps, // 0| converts undefined to 0 + value: !!settings.compass, // 0| converts undefined to 0 onchange: v => { settings.compass = v; writeSettings(); } - } + }, "DIAL": { value: settings.dial, min: 0, From 20d9f772653892a4fbf44222f2bb35f09f4294c9 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 20:41:30 +0100 Subject: [PATCH 14/40] Add files via upload --- apps/regattatimer/ChnageLog | 2 ++ apps/regattatimer/README.md | 56 ++++++++++++++++------------- apps/regattatimer/metadata.json | 5 +-- apps/regattatimer/screenshot-1.png | Bin 0 -> 1950 bytes apps/regattatimer/screenshot-2.png | Bin 0 -> 2134 bytes 5 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 apps/regattatimer/ChnageLog create mode 100644 apps/regattatimer/screenshot-1.png create mode 100644 apps/regattatimer/screenshot-2.png diff --git a/apps/regattatimer/ChnageLog b/apps/regattatimer/ChnageLog new file mode 100644 index 000000000..0789c830e --- /dev/null +++ b/apps/regattatimer/ChnageLog @@ -0,0 +1,2 @@ +0.01: (2023-02-23) initial alpha upload +0.02: (2023-02-23) fixing minor issues with settings diff --git a/apps/regattatimer/README.md b/apps/regattatimer/README.md index 8b5957b0b..156c2c3eb 100644 --- a/apps/regattatimer/README.md +++ b/apps/regattatimer/README.md @@ -1,32 +1,38 @@ -Regatta Timer with 5-4-1 countdown +# Regatta Timer with 5-4-1 countdown -Modes +## Modes -- Idle mode - +* **Idle** On startup the application is in idle mode showing a large 5 in the centre of the screen. - Click the button to switch to start mode. - -- Start mode - - Countdown from 5 minutes to 0. During the countdown, the screen changes layout several - several times to use as much space as possible to display the numbers. - When 0 is reached, the buzzer sounds and the application switches to race mode. - A click on the button switches back to idle mode. - -- Race Mode - + `Button` switches to start mode. +* **Start** + During the countdown, the screen changes the layout several times to use as much space as + possible to display the numbers. + When time is up the buzzer sounds and the application switches to race mode. + `Button` switches to idle mode. +* **Race** Race time, local time, SOA and number of GPS satellites available are displayed. - A click on the button switches to "stopped mode". + `Button` switches to "stopped mode". +* **Stoped** + `Button` switches to idle mode. -- Stoped mode - A button click switches to idle mode. +## Screenshots -Localization -- Go to: https://banglejs.com/apps/ -- Search for app: Languages -- Click the "arrow up" or "burger" icon -- Choose your language from the dropdown -- Click "upload" +![Start mode minutes and seconds](screenshot-1.png) +![Start mode seconds](screenshot-2.png) -© by https://naden.de +## Localization + +1 Go to: https://banglejs.com/apps/ +2 Search for app: Languages +3 Click the "arrow up" or "burger" icon +4 Choose your language from the dropdown +5 Click `upload` + +## Feedback + +Report bugs, request a feature at https://www.github.com/naden + +## Created by + +© 2021-2023 https://naden.de diff --git a/apps/regattatimer/metadata.json b/apps/regattatimer/metadata.json index 633b1685b..4f3b72e93 100644 --- a/apps/regattatimer/metadata.json +++ b/apps/regattatimer/metadata.json @@ -1,7 +1,7 @@ { "id": "regattatimer", "name": "Regatta Timer", - "shortName": "Regatta Timer", + "shortName": "Regatta T.", "version": "0.2", "description": "Regatta Timer with classic 5-4-1 Countdown", "icon": "icon.png", @@ -14,5 +14,6 @@ {"name":"regattatimer.settings.js","url":"settings.js"}, {"name":"regattatimer.img","url":"app-icon.js","evaluate":true} ], - "data": [{"name": "regattatimer.json"}] + "data": [{"name": "regattatimer.json"}], + "screenshots": [{"url":"screenshot.png"},{"url":"screenshot-1.png"},{"url":"screenshot-2.png"}] } diff --git a/apps/regattatimer/screenshot-1.png b/apps/regattatimer/screenshot-1.png new file mode 100644 index 0000000000000000000000000000000000000000..a24bfa88715623b1386413c57d99fca915c501c9 GIT binary patch literal 1950 zcmds&{X5j@9>=F|x6G)l`C5{b$C>e%v1(cJxWiyp6w-Pa)2z~*4K)&t#;R`}i%f%= zhFDZ=2w|O!hsf8S9vq6}gl3zV*~#P42&Gh({nqaJ56(~Lhx>kiuGf9tpZDjwulIG` z*?WWhHftGb;c&Rk41b>xOdCHe{03}qXc=c=vMwpakA|!7-!_B8X@)a=zTC%SsY?cr zKXcv$Bx`HaOqZzJOYPf=lw2HbuA|;-@56qBk$bDVgwNh9HK9O4ni=<4K)A9&Y06QF6F zf)tuW^ST2l+{#umSLPVd9SiF73W3ylYvj#NG)yHz%?9GPwCJ}+vS8Wd#Qe{88e?BF zVV=xkHbV`@7#RJ}f-9r$M! zO3gU(xU&T9>im(!z46!jxk7k3-k8oVq&3g7Xv+LALhwx6;#xi*@f+{#_WsDFdc1v4 zU0I-B@8NLgH8ePt!7lVznw7#lUnVy4jw$UY_`03|$W^Z<8oT&mkjU<*qb>S8Ne8(x zs-k_V+eRznv98RkO}DOTF)7&n8)2-jPZ8U@6y)Whn(0GZT2+;-rSSpl zlP=lK@4eXhQ`!wwmEBFMAA4b0CmdK6kQ%k-ee*8wfbiSPEfM;e7#FA($6)7Q*)K#5 zyDGe)hIB}`bL#8gD&kgBcH;%;H&Dgmg%y>cJ_A+!nq+9R_Y-z?Hg7P?KVSdFTxzA_ zmLt&sAqmS^vnyH+)ja|o)la@ij=Hj!125DOp*=?D_TPM`^WxTwP2)jDmqYsS>~lAB zJ_%`74g@zt`z~fnBuI3%@8e9Ie@>sd zI%LirzsO?dpr@}`!8XnKm0uy-MYrDf5Wp54RHuZFEpCD_eV=muMnJ;#0vpCfg zbL;x?C8BED-B*!EZ#bR&67P2Hj-q_chq+0^@XDkhu1uI#`O8Q^o5n4V(l*QDF z7K5$*8u-b&U)e!9k*4z|XU_i(D@pU#NN}5P0|~Lr2vr!ZjYz$rfnL2-v1IVv7m4D< z%aUXr-Xz7t{KfOXG!+>SaJunkUyVj6i=s}ZUwL`^GCmZPx!lbx#>f98Vb%+SNpFe= zpAi9OA=p+Ui-}16p1IXshgSr#USazofI3W~- zq1@Jdygd)K=*RkRC+vo%swIyyfK#FcX3k*4&VDyasAR4-r`?Nfz3#o`j)J*@yQcGQ zyNvEHY}Uh{pm;$i-CbUnzO>Np*WtpZNa=|Pc@Pd!XML+ApFxA#>k=%_pec7g=fYlW z%jglQt{2-i#?YBy+9;;R$uTi<*jxg1WQ@XQH73GK8-gJG^B?=3q(Jz1zuoIC*MXFO z$0oS015#2YBE~_kGfmP`93&?`LK(O}?3A8je}PHhp*|1n!^JV^K|b{~_SxS7D)VLU literal 0 HcmV?d00001 diff --git a/apps/regattatimer/screenshot-2.png b/apps/regattatimer/screenshot-2.png new file mode 100644 index 0000000000000000000000000000000000000000..932028ae09d44ca916be36d5bcf4c7ee901ed26d GIT binary patch literal 2134 zcmd6p`!kz~8pnw@q)A(e5-m~@TP+f2jUJalf{n&)>)Hq@O|46}i%{1bT79uDQN*q| zt)i-SP^m<86N{~sQfKSZ3zZzUf~ZqP+*7T~vH!*X@I0TH&&+q`ndkZCDfBx}IRZC; ztEs6S@$?`E98mCA!88v#U#ti}fO=8@#Z9ec*yw|r8Vu=4b`44oU#j-McG2!I@XHtD z|C*aqar`tW$LJ;?-P1Fks%_A(5C?EoVzKxVa|d+shbUneA3H>3>9{b+XTDVfy~io( zKtK2zK(;4e&!sSo{_Zjl*z4Or`u=;F(pVR<%9}Q@LX3SmaZ{!zuVGg z-bFrOrnNkbzm9#o@ruVnrgz|$a@aj-%y&w%?t12A%uuc*RJ(y0s2k69#>CIK_Bp|v z)NhQNvU^a{D||ymn2?ykv=gyeQpxEP|B_vEk-M##*IB^lg9~1Wa^0N9FtDAf`px?a zI*bbzsjy!|{$cT0l?cS+1a$+-GraEXt*c-5C-jo;oo$Y&37B$g)Vy&@;W@YV&&sSg z+V64u82ivkN)Q@J3SIJq=KmZWIGD7dNj+lU{K_*bYp_A*#oCQRX((GcS zYxU}w4@ySaULrDEiCzrLMf@NPRVTc0&l;z~_lAvxRF$Hn@D8{i;hW@leAd`+!4Ai?Mu`_LeCnZ|+gk3Wg{_rN85kWwtgtq2UVNDNnWSHx#H*MoYKqWMr2 zdJw$YznX*)&azibE$*Mpc~Tj+Nh~5j5TmVFM7T*O&YQWR*ygSSoipL<~wB>MVJvG7|)%67Qy!cSxc$>DCkvSgWO!;pVX}o^=S3GEIN7uq!g2se7*bGvc`B$t*SMb67e5Fp_98|5$>e(+G(GBc(q$n-UouKU0ut6I*&LJ^H&S;)8PR zdfJxHxi)3x<<74XM$t7;&g$f0h3uj~J>J%LBgrNfm0rs|A<-D$PuVa7V`Nr{%s1$o zA8$Rg1w%xz*tT6KYlM`w`{Fyh*l9hH4#<@DSaQlf#Xu|%4s&@N&Un80zHFGD&VA$~ zKY*U_`^H(9;KE=TPGB-(!N|YcVv2!M1zh;f7+GHmmsmM!9eujxOEZB4>u-}2jWK)+ zVP#ySWQZ^$Kij?>d?rVRyEeW^u~ zPWR#L&e+)=XeW6%y7PiOwybU1>eK0rv)+oZ*ixi@s3I&O3Jten>#*!59sty$F2fdy zhh^H@Ma!gh*&f;nvaDtlgoB_+EjKnpN<)X27*2f`JuaYLr qQSEWRrX9|cq-{*5di~8KyBhT-a$~KMDa65}pyug*p4{Tb$o@a%)!qOA literal 0 HcmV?d00001 From d9ff6c4ecaa8afd9d1390484b3ffc053b73050b4 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 20:51:04 +0100 Subject: [PATCH 15/40] Add files via upload --- apps/regattatimer/app-icon.js | 2 +- apps/regattatimer/icon.png | Bin 1825 -> 3359 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regattatimer/app-icon.js b/apps/regattatimer/app-icon.js index 0d5ffe739..aa8982414 100644 --- a/apps/regattatimer/app-icon.js +++ b/apps/regattatimer/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mUvgP/AAM/AYQAB54FE8YFE/GfAof+kYMEhF/AofCh4FD8mBAof5mQyD/nM44kDmc4CQec54SD58zCQfj5n4Egfzn4SC/n85/nEgU/mf5CQUf5hrDx4YBNYXH/mPCQRuBk/+LwPx//JCoJ0B//yv/zAoN//kf/g2BE4JvBAYMD//D/4+B+Ef+BNBFYOA/kDFYX+g8wh6dC+EP4EnCILDDj/AAocHHIIAC+I5BXoZoBTwd/ZYZ5BA4IFCCAOHAonwIARYB/sH3/2hwgBgED+EOIwMggkAwEQgE+g/A/Efg/9/+/+/+DAMfwfwvwYMggA=")) +require("heatshrink").decompress(atob("mEwxH+AH4A/ACaaCBRAIQ/2sF6IKSBBGsAAJfRBKQHGxAvCMKohEBBgACwQuDSLJfPwIuESCJAQAAouGMCQuVdgYvowIuJGEYuCF9YuNF53OAAS6PAAQvKGBguDGBouQF7iLDF9QuTGBgvNFwwvad5XB4OkCYwuOF5gAIFwPB4QvqFwRfYGBQmDAA6/CFyovVd4YvqBoYvXGBIqE3QOIGAgvbGAm6Lggvp3e7MBYuSF5RgRF6aQYGAQveeQoAEF7AtKF6GBFbYwTFbgAGsYAZF9xfvF/4wJrtdEIoHMFKNeAAQvDA4YmDA5ofHFpWCwQSDA7wvHBgOI1utxAFBA74vHBgIAlF/4v/F64A/AH4A7A")) diff --git a/apps/regattatimer/icon.png b/apps/regattatimer/icon.png index e919037ae93d1d76d76c8c169214b1c3d9c694ae..2f634774c39f7fe823bc6c0a1a4c0cf9dfb81233 100644 GIT binary patch literal 3359 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 KRm+s`Km8vzgyA~? delta 1824 zcmV+*2jBRg8les$iBL{Q4GJ0x0000DNk~Le0000o0000o2nGNE03JVxv5~$Pe+JV@ zL_t(|oZVV^NEKZapQ6RAOl{FJEnLgU$}G?psmLN!D%^q+2@0{I#i9`Vr*i2JL5dbD zh$011BcehxsI;`jsOY6Fnu=*zkd`ej>51R@nEB>y^G16wpB}h)-^{(|oZrkn>nPA5 zA%O-72{cGZuq5-Io*r7dbSVV|e+AKi26FfAT@nO=howodB=gOiHxGOuZEbBdcI;R! zAOQgZ(;H~`LJQb_^*?b6TNx!#yWv?cXvxq{_54Me_BBH?%m7t z-Me>_udgr9r%s(pb#-;t2jt?#i>%;+1q(Qi$bpxo|Yat;a^!xX3>i}}#zyVfj z)v8th3y=YM`0ydmgM)*mfBO{`6*PMEXuh79nArb7o;`a;BSws%QKLptO-+qKATM6L z;5qC=PPw_c(z?r+FZUmiU%!6Q?Afzfkwb?LX$z16sjjZ(xwp5s9*-S6cJRFk6DIU2 zV44L;e0)4BF?a4TW4w^rIKHp!nX3f6`e}ba`;kLH6R9RW6 zFF*#Qxw)C=)22-`$V;Qq(8!S^c@1LGKLdhb=IH3ig0r%+3=5C}dHwpeA`sA#BS(0R zo0}W``0;}*4g^mZITM7&#>N^KAOlifUM@v^W7OW>F7ZuGO|>i##EXRs7qYOxz(75_ zFb3rM_3J!expJkdf8#oS{CN8I?VDwQz$imQLs`)D>C@@MhY!jEWI)cIJFMd=bRTPv9zAM)AULzDSFe_`SGZG?0%SmtnqYqL;6ao73JMCOhovUss|M2A+Dc27 zEMex8CQYK3FJG$KCkL`(#R{Gy0GguC&Q2+P!I3m6M~yOwf7D{YgEx8g>Xn*pT0rnr zMGt4lO&g-b#6(`dapOkQa!f5iq6eb`!#Q*2Pv9q(| zH>4=X*o~h*f6}g9yJ+apq0B5MCZ<<@T^C4FQWF0~7SJ2=@$un(_wV0VmSgP3qD6}& zB>(~iQ(s8re<7y#g@uKEN`F#PQk3NwyRm%va;=)%#l?lTZ{JS0Z{O}!-5}?ILaMh9 zBqt|__pV>RzHdN4j~_qg8pF%WOG}}N6DQJ^EnE2MBCeNvvD_7XA^aRM(%7?SFe=}E#Cr_SK1lHEp#_Q2aG7Au0 zcbu7-$#yhk$Ph_66jg}&7cN|&uV24v0l^+z-@0`xyKr4#h)toPp_HDU&Q+u6*>H?; zA99qA^k1j(Z{NP9w6rwtBFYuBv9Y1>@NlhwD5t8bDn4d#BzQZzz?__%C@Lz7PMkO) zwU3kwQD+SP#g!apQQQIl6!5r26`LDGb1Ke*XMf8OYkTYk4ugZCDFEc*8svjlZ@b#ZDU>Vq-@f8~T~_nV!aO~Z!|ms*)gMa0CF5l$0cW)q;DCi;FYa6EFz`St^K4lx547@qI*FtUZ7JJV$cW zf7+%9Yp@nBzNn~(A|oRuI}ikcKWW3jLB++z{DJ7)xpRt{vw%J;&f~|AAE}|Cfzx#K zZ^eQ}9E%q(X5WpbgZw%=gJ=-Qui@KBMn(o#h2RhWUshJe*K|`E`SADmr@Xv8di3ZK z9Xoc6Wyv}OWMQwfU+BME{@r02nU|^1GxF^=%*mLL39X4*rb#rrL-e`tH7LK6ksh|t~b^!q` zB{T)0D|E)gMkcOkBWKQ>p|G$pKBr0qA(t*)>a~r)&6|o-#pbpV1S5PqN7ab4`TqTT zDlIMLPN3K->Whuw!zp;&yLXS*f%ky25v;|YL1QCDMMca5yuh=sHu4ADmY$a!mv^E7 O0000 Date: Fri, 23 Feb 2024 20:53:45 +0100 Subject: [PATCH 16/40] Add files via upload --- apps/regattatimer/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/regattatimer/README.md b/apps/regattatimer/README.md index 156c2c3eb..0523d5483 100644 --- a/apps/regattatimer/README.md +++ b/apps/regattatimer/README.md @@ -23,11 +23,12 @@ ## Localization -1 Go to: https://banglejs.com/apps/ -2 Search for app: Languages -3 Click the "arrow up" or "burger" icon -4 Choose your language from the dropdown -5 Click `upload` +Localization is done by the Bangle.js 2 app "Languages" +* Go to: https://banglejs.com/apps/ +* Search for app: Languages +* Click the "arrow up" or "burger" icon +* Choose your language from the dropdown +* Click `upload` ## Feedback @@ -35,4 +36,4 @@ Report bugs, request a feature at https://www.github.com/naden ## Created by -© 2021-2023 https://naden.de +© 2021 - 2023 https://naden.de From 2dbec19f1e9819a5179b0f0830601568398df514 Mon Sep 17 00:00:00 2001 From: naden Date: Sat, 24 Feb 2024 21:07:32 +0100 Subject: [PATCH 17/40] Add files via upload --- apps/regattatimer/README.md | 2 +- apps/regattatimer/app.js | 24 +++++++++++++----------- apps/regattatimer/metadata.json | 4 ++-- apps/regattatimer/regattatimer.json | 2 +- apps/regattatimer/settings.js | 11 +++++++++-- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/apps/regattatimer/README.md b/apps/regattatimer/README.md index 0523d5483..180c8cb5d 100644 --- a/apps/regattatimer/README.md +++ b/apps/regattatimer/README.md @@ -1,4 +1,4 @@ -# Regatta Timer with 5-4-1 countdown +# Regatta Timer 5-4-1 countdown ## Modes diff --git a/apps/regattatimer/app.js b/apps/regattatimer/app.js index 67992e333..6e7bb16b2 100644 --- a/apps/regattatimer/app.js +++ b/apps/regattatimer/app.js @@ -1,8 +1,7 @@ /** * Regatta Timer - * + * speed of advance */ -const DEBUG = false; const Layout = require("Layout"); const locale = require("locale").name.substring(0, 2); @@ -37,7 +36,7 @@ function Regattatimer() { return { layout: undefined, mode: "idle", // idle, start, race" - countdown: DEBUG ? 1 : 300, // 5 minutes + countdown: 300, // 5 minutes counter: undefined, interval: undefined, raceTimeStart: undefined, @@ -64,10 +63,10 @@ function Regattatimer() { ], settings: Object.assign({ + "debug": false, "dial": "numeric", "gps": false, "compass": false, - "language": "en", "fgColor": "#FFFF00", "bgColor": "#000000" }, require('Storage').readJSON("regattatimer.json", true) || {}), @@ -84,7 +83,7 @@ function Regattatimer() { }, require('Storage').readJSON("translations.json", true) || {}), translate: function(slug) { - return this.translations[this.settings.language][slug]; + return this.translations[locale][slug]; }, /** * During the start phase, the the clock counts down 5 4 1 minutes. @@ -261,7 +260,6 @@ function Regattatimer() { {type: "txt", font: "10%", label: "-", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, id: "satellites"}, {type: "txt", font: "10%", label: "00:00", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, halign: 1, id: "daytime"}, ]} - ]},{lazy:true}); }, onGPS: function(fix) { @@ -269,7 +267,7 @@ function Regattatimer() { if(fix.fix && isFinite(fix.speed)) { this.layout.speed.label = fix.speed.toFixed(2); //m[1]; } - this. layout.satellites.label = "Satellites ".concat(fix.satellites); + this. layout.satellites.label = "Sats: ".concat(fix.satellites); } }, /* @@ -308,6 +306,10 @@ function Regattatimer() { */ init: function() { + if(this.settings.debug) { + this.countdown = 1; + } + this.setLayoutIdle(); Bangle.setLCDPower(1); @@ -347,22 +349,22 @@ var regattatimer = Regattatimer() regattatimer.init(); -if(regattatimer.gps) { +if(regattatimer.settings.gps) { Bangle.setGPSPower(1); Bangle.on('GPS', regattatimer.onGPS.bind(regattatimer)); } -if(regattatimer.compass) { +if(regattatimer.settings.compass) { Bangle.setCompassPower(1); Bangle.on('mag', regattatimer.onCompass.bind(regattatimer)); } Bangle.on('kill',function() { - if(regattatimer.gps) { + if(regattatimer.settings.gps) { Bangle.setGPSPower(0); } - if(regattatimer.compass) { + if(regattatimer.settings.compass) { Bangle.setCompassPower(0); } }); diff --git a/apps/regattatimer/metadata.json b/apps/regattatimer/metadata.json index 4f3b72e93..d933af5d3 100644 --- a/apps/regattatimer/metadata.json +++ b/apps/regattatimer/metadata.json @@ -1,9 +1,9 @@ { "id": "regattatimer", "name": "Regatta Timer", - "shortName": "Regatta T.", + "shortName": "RegattaTimer", "version": "0.2", - "description": "Regatta Timer with classic 5-4-1 Countdown", + "description": "Regatta Timer with 5-4-1 Countdown", "icon": "icon.png", "screenshots": [{"url":"screenshot.png"}], "tags": "tool,outdoors,sailing,race,regatta,boat,timer", diff --git a/apps/regattatimer/regattatimer.json b/apps/regattatimer/regattatimer.json index 3f81dedb5..baad5af42 100644 --- a/apps/regattatimer/regattatimer.json +++ b/apps/regattatimer/regattatimer.json @@ -1,8 +1,8 @@ { + "debug": false, "dial": "numeric", "gps": false, "compass": false, - "language": "en", "fgColor": "#FFFF00", "bgColor": "#000000" } diff --git a/apps/regattatimer/settings.js b/apps/regattatimer/settings.js index 54ddb56bd..7466f0906 100644 --- a/apps/regattatimer/settings.js +++ b/apps/regattatimer/settings.js @@ -2,10 +2,10 @@ var file = "regattatimer.json"; // Load settings var settings = Object.assign({ + "debug": false, "dial": "numeric", "gps": false, "compass": false, - "language": "en", "fgColor": "#FFFF00", "bgColor": "#000000" }, require('Storage').readJSON(file, true) || {}); @@ -41,6 +41,13 @@ settings.dial = v; writeSettings(); } - } + }, + 'DEBUG': { + value: !!settings.debug, // 0| converts undefined to 0 + onchange: v => { + settings.debug = v; + writeSettings(); + } + }, }); }) From 2621e53765d6bb9722a1b3e5c854c44984bd32ed Mon Sep 17 00:00:00 2001 From: naden Date: Sat, 24 Feb 2024 21:17:21 +0100 Subject: [PATCH 18/40] Add files via upload --- apps/regattatimer/app.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/regattatimer/app.js b/apps/regattatimer/app.js index 6e7bb16b2..f687353c8 100644 --- a/apps/regattatimer/app.js +++ b/apps/regattatimer/app.js @@ -310,9 +310,10 @@ function Regattatimer() { this.countdown = 1; } - this.setLayoutIdle(); - Bangle.setLCDPower(1); + Bangle.setLCDTimeout(0); + + this.setLayoutIdle(); var onButtonClick = ((ev) => { From 5d13118416b8266165113f606269f501d1b58d08 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 1 Mar 2024 15:31:42 +0100 Subject: [PATCH 19/40] Add files via upload --- apps/regattatimer/ChnageLog | 5 +- apps/regattatimer/README.md | 42 ++- apps/regattatimer/app-icon.js | 2 +- apps/regattatimer/app.js | 385 +++++++++++++--------------- apps/regattatimer/icon.png | Bin 3359 -> 1391 bytes apps/regattatimer/metadata.json | 11 +- apps/regattatimer/regattatimer.json | 6 +- apps/regattatimer/screenshot-1.png | Bin 1950 -> 1800 bytes apps/regattatimer/screenshot-2.png | Bin 2134 -> 2145 bytes apps/regattatimer/screenshot-3.png | Bin 0 -> 1830 bytes apps/regattatimer/screenshot-4.png | Bin 0 -> 2865 bytes apps/regattatimer/screenshot-5.png | Bin 0 -> 2798 bytes apps/regattatimer/screenshot-6.png | Bin 0 -> 2859 bytes apps/regattatimer/screenshot-7.png | Bin 0 -> 2376 bytes apps/regattatimer/screenshot.png | Bin 1805 -> 2865 bytes apps/regattatimer/settings.js | 93 ++++--- 16 files changed, 289 insertions(+), 255 deletions(-) create mode 100644 apps/regattatimer/screenshot-3.png create mode 100644 apps/regattatimer/screenshot-4.png create mode 100644 apps/regattatimer/screenshot-5.png create mode 100644 apps/regattatimer/screenshot-6.png create mode 100644 apps/regattatimer/screenshot-7.png diff --git a/apps/regattatimer/ChnageLog b/apps/regattatimer/ChnageLog index 0789c830e..81eece2d3 100644 --- a/apps/regattatimer/ChnageLog +++ b/apps/regattatimer/ChnageLog @@ -1,2 +1,3 @@ -0.01: (2023-02-23) initial alpha upload -0.02: (2023-02-23) fixing minor issues with settings +0.01: (2024-02-23) initial alpha upload +0.02: (2024-02-23) fixed minor issues with settings +0.03: (2024-03-01) advanced settings, rearanged ui elements, fixed rendering problems diff --git a/apps/regattatimer/README.md b/apps/regattatimer/README.md index 180c8cb5d..dbf924775 100644 --- a/apps/regattatimer/README.md +++ b/apps/regattatimer/README.md @@ -3,7 +3,7 @@ ## Modes * **Idle** - On startup the application is in idle mode showing a large 5 in the centre of the screen. + On startup the application is in idle mode showing a large 5 in the centre of the screen and the time of day below. `Button` switches to start mode. * **Start** During the countdown, the screen changes the layout several times to use as much space as @@ -11,29 +11,55 @@ When time is up the buzzer sounds and the application switches to race mode. `Button` switches to idle mode. * **Race** - Race time, local time, SOA and number of GPS satellites available are displayed. + Race time, local time, SOA, number reachable GPS satellites and battery level are shown. `Button` switches to "stopped mode". * **Stoped** + The race counter stops. `Button` switches to idle mode. ## Screenshots -![Start mode minutes and seconds](screenshot-1.png) -![Start mode seconds](screenshot-2.png) +*Idle mode: showing a big 5 and time of day below* +![Idle mode: showing a big 5 and time of day below](screenshot-1.png) + +*Start mode: minutes and seconds* +![Start mode: minutes and seconds](screenshot-2.png) + +*Start mode: seconds* +![Start mode: seconds](screenshot-3.png) + +*Race mode: elapsed time, time of day, speed, satellites, battery* +![Race mode: elapsed time, time of day, speed, satellites, battery](screenshot-4.png) + +*Race mode: with german abbreviations* +![Race mode: with german abbreviations](screenshot-5.png) + +*Settings page: main* +![Settings page: main](screenshot-6.png) + +*Settings page: choose the theme* +![Settings page: choose the theme](screenshot-7.png) ## Localization Localization is done by the Bangle.js 2 app "Languages" -* Go to: https://banglejs.com/apps/ -* Search for app: Languages +* Go to [banglejs.com/apps](https://banglejs.com/apps/) +* Search for app "Languages" * Click the "arrow up" or "burger" icon * Choose your language from the dropdown * Click `upload` +**Some nautical abbreviations which are not part of the banglejs2 Languages packages are stored in `translations.json`.** + ## Feedback -Report bugs, request a feature at https://www.github.com/naden +Report bugs or request a feature at [github.com/naden](https://github.com/naden) + +## Roadmap +* adding a seconds coundown layout to mimic a classic regatta chronograph ## Created by -© 2021 - 2023 https://naden.de +© 2021 - 2024 [naden.de](https://naden.de) + +Icons by [Icons8](https://icons8.com/) diff --git a/apps/regattatimer/app-icon.js b/apps/regattatimer/app-icon.js index aa8982414..8f9c37a94 100644 --- a/apps/regattatimer/app-icon.js +++ b/apps/regattatimer/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwxH+AH4A/ACaaCBRAIQ/2sF6IKSBBGsAAJfRBKQHGxAvCMKohEBBgACwQuDSLJfPwIuESCJAQAAouGMCQuVdgYvowIuJGEYuCF9YuNF53OAAS6PAAQvKGBguDGBouQF7iLDF9QuTGBgvNFwwvad5XB4OkCYwuOF5gAIFwPB4QvqFwRfYGBQmDAA6/CFyovVd4YvqBoYvXGBIqE3QOIGAgvbGAm6Lggvp3e7MBYuSF5RgRF6aQYGAQveeQoAEF7AtKF6GBFbYwTFbgAGsYAZF9xfvF/4wJrtdEIoHMFKNeAAQvDA4YmDA5ofHFpWCwQSDA7wvHBgOI1utxAFBA74vHBgIAlF/4v/F64A/AH4A7A")) +require("heatshrink").decompress(atob("mEw4n/8H/BAWlsEI0ExkFj0H34GcgHnwGUsHO4H/kEhkGl4He8HeI8dgCyvTiM0ruABxNoxAACwwIC4MRAAMUkAWHgoNCAAMdBIVBBAccuAXGsIXIwtEmIxD0AXFhgXIAAMI6oZCnIXFh1VqtRC44AB2RkCqBiHC5UAhIxBip5KC5EA8gwIyMRiYXKgyWBjIIEhQgBpIXKgEEEwKSEA4MU3IXLhYwBoAHDuMRn8JC5cA8cRi4eDTIOQC5oNBjgFChoFChEx4wXKgApB6BeDiQEBxVgC5chMAeBiM7CZYAD2cRjAEB6L3JAA7oBNwVRJgYANOQMVAgKtBwAXPhDRBAgMxiYDBylEB4lk+C3D6gnBCYYbDrAnFrngIgeUDoKoBgEGC4dHyxWEqgXDXAIXBLYNgC4ldykAhWoIwNk8EL1TIBC4pHEBYOAgV+gFV6mbg13C4hHCMYcGqEMd4fXuhOEC4R3DU4eUpLPFL4kFwCnEa6/RiNQC58FiMdAgOBiM7C5+ziMYMwURiQXPkMRoAEBgURjgXPUwJyDAoOQCxsJFIOwAwVxiM+C5vjiMXY4itBaoYAIgzRBLwQABhUxA4oAHE4MT0AIEyIwMFwRGEYwcRpwXJ8gNBdA9RHIKSIhJVBir3JTIMrBQuyWoIuIAAOTBgMTruAA4MI6otBiM3KZMKNYIZC4lMCoSDBRgoAFg4+CAAscuDjMhoxDFofQfZpaCoYVBmhkDACEGbpQAY")) diff --git a/apps/regattatimer/app.js b/apps/regattatimer/app.js index f687353c8..87769ee0e 100644 --- a/apps/regattatimer/app.js +++ b/apps/regattatimer/app.js @@ -1,9 +1,9 @@ /** * Regatta Timer - * speed of advance */ const Layout = require("Layout"); -const locale = require("locale").name.substring(0, 2); +const locale = require("locale").name == "system" ? "en" : require("locale").name.substring(0, 2); +const hs = require("heatshrink"); // "Anton" bold font Graphics.prototype.setFontAnton = function(scale) { @@ -11,84 +11,156 @@ Graphics.prototype.setFontAnton = function(scale) { g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAA/gAAAAAAAAAAP/gAAAAAAAAAH//gAAAAAAAAB///gAAAAAAAAf///gAAAAAAAP////gAAAAAAD/////gAAAAAA//////gAAAAAP//////gAAAAH///////gAAAB////////gAAAf////////gAAP/////////gAD//////////AA//////////gAA/////////4AAA////////+AAAA////////gAAAA///////wAAAAA//////8AAAAAA//////AAAAAAA/////gAAAAAAA////4AAAAAAAA///+AAAAAAAAA///gAAAAAAAAA//wAAAAAAAAAA/8AAAAAAAAAAA/AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////AAAAAB///////8AAAAH////////AAAAf////////wAAA/////////4AAB/////////8AAD/////////+AAH//////////AAP//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wA//8AAAAAB//4A//wAAAAAAf/4A//gAAAAAAP/4A//gAAAAAAP/4A//gAAAAAAP/4A//wAAAAAAf/4A///////////4Af//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH//////////AAD/////////+AAB/////////8AAA/////////4AAAP////////gAAAD///////+AAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAAAAAAAAAP/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/AAAAAAAAAAA//AAAAAAAAAAA/+AAAAAAAAAAB/8AAAAAAAAAAD//////////gAH//////////gAP//////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAAAB/gAAD//4AAAAf/gAAP//4AAAB//gAA///4AAAH//gAB///4AAAf//gAD///4AAA///gAH///4AAD///gAP///4AAH///gAP///4AAP///gAf///4AAf///gAf///4AB////gAf///4AD////gA////4AH////gA////4Af////gA////4A/////gA//wAAB/////gA//gAAH/////gA//gAAP/////gA//gAA///8//gA//gAD///w//gA//wA////g//gA////////A//gA///////8A//gA///////4A//gAf//////wA//gAf//////gA//gAf/////+AA//gAP/////8AA//gAP/////4AA//gAH/////gAA//gAD/////AAA//gAB////8AAA//gAA////wAAA//gAAP///AAAA//gAAD//8AAAA//gAAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAD/wAAB//8AAAAP/wAAB///AAAA//wAAB///wAAB//wAAB///4AAD//wAAB///8AAH//wAAB///+AAP//wAAB///+AAP//wAAB////AAf//wAAB////AAf//wAAB////gAf//wAAB////gA///wAAB////gA///wAAB////gA///w//AAf//wA//4A//AAA//wA//gA//AAAf/wA//gB//gAAf/wA//gB//gAAf/wA//gD//wAA//wA//wH//8AB//wA///////////gA///////////gA///////////gA///////////gAf//////////AAf//////////AAP//////////AAP/////////+AAH/////////8AAH///+/////4AAD///+f////wAAA///8P////gAAAf//4H///+AAAAH//gB///wAAAAAP4AAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAAAAAAAAAA//wAAAAAAAAAP//wAAAAAAAAB///wAAAAAAAAf///wAAAAAAAH////wAAAAAAA/////wAAAAAAP/////wAAAAAB//////wAAAAAf//////wAAAAH///////wAAAA////////wAAAP////////wAAA///////H/wAAA//////wH/wAAA/////8AH/wAAA/////AAH/wAAA////gAAH/wAAA///4AAAH/wAAA//+AAAAH/wAAA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAH/4AAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//8AAA/////+B///AAA/////+B///wAA/////+B///4AA/////+B///8AA/////+B///8AA/////+B///+AA/////+B////AA/////+B////AA/////+B////AA/////+B////gA/////+B////gA/////+B////gA/////+A////gA//gP/gAAB//wA//gf/AAAA//wA//gf/AAAAf/wA//g//AAAAf/wA//g//AAAA//wA//g//gAAA//wA//g//+AAP//wA//g////////gA//g////////gA//g////////gA//g////////gA//g////////AA//gf///////AA//gf//////+AA//gP//////+AA//gH//////8AA//gD//////4AA//gB//////wAA//gA//////AAAAAAAH////8AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////gAAAAB///////+AAAAH////////gAAAf////////4AAB/////////8AAD/////////+AAH//////////AAH//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wAf//////////4A//wAD/4AAf/4A//gAH/wAAP/4A//gAH/wAAP/4A//gAP/wAAP/4A//gAP/4AAf/4A//wAP/+AD//4A///wP//////4Af//4P//////wAf//4P//////wAf//4P//////wAf//4P//////wAP//4P//////gAP//4H//////gAH//4H//////AAH//4D/////+AAD//4D/////8AAB//4B/////4AAA//4A/////wAAAP/4AP////AAAAB/4AD///4AAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAADgA//gAAAAAAP/gA//gAAAAAH//gA//gAAAAB///gA//gAAAAP///gA//gAAAD////gA//gAAAf////gA//gAAB/////gA//gAAP/////gA//gAB//////gA//gAH//////gA//gA///////gA//gD///////gA//gf///////gA//h////////gA//n////////gA//////////gAA/////////AAAA////////wAAAA///////4AAAAA///////AAAAAA//////4AAAAAA//////AAAAAAA/////4AAAAAAA/////AAAAAAAA////8AAAAAAAA////gAAAAAAAA///+AAAAAAAAA///4AAAAAAAAA///AAAAAAAAAA//4AAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gB///wAAAAP//4H///+AAAA///8P////gAAB///+f////4AAD///+/////8AAH/////////+AAH//////////AAP//////////gAP//////////gAf//////////gAf//////////wAf//////////wAf//////////wA///////////wA//4D//wAB//4A//wB//gAA//4A//gA//gAAf/4A//gA//AAAf/4A//gA//gAAf/4A//wB//gAA//4A///P//8AH//4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////gAP//////////gAP//////////AAH//////////AAD/////////+AAD///+/////8AAB///8f////wAAAf//4P////AAAAH//wD///8AAAAA/+AAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAB///+AA/+AAAAP////gA//wAAAf////wA//4AAB/////4A//8AAD/////8A//+AAD/////+A///AAH/////+A///AAP//////A///gAP//////A///gAf//////A///wAf//////A///wAf//////A///wAf//////A///wA///////AB//4A//4AD//AAP/4A//gAB//AAP/4A//gAA//AAP/4A//gAA/+AAP/4A//gAB/8AAP/4A//wAB/8AAf/4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH/////////+AAD/////////8AAB/////////4AAAf////////wAAAP////////AAAAB///////4AAAAAD/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAB/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("EiAnGicnJycnJycnEw=="), 78 + (scale << 8) + (1 << 16)); }; -/* -function Dial(dial) { - return { - "numeric": { - splash: function() { - - }, - start: function() { - }, - race: function() { - } - }, - "circle": { - start: function() { - }, - race: function() { - } - } - }[dial]; -} -*/ function Regattatimer() { return { + icons: { + "satellite": function() { + return hs.decompress(atob("jEYxH+AH4Ab6QIIBJAfNAAQtSC4gxSCwgYHHBYYMC6IYPC5AZOC8QYMC5YYLC5inSDH4waVbAYJCpgA/AAI=")); + }, + "battery": function() { + return hs.decompress(atob("jEYxH+AHHSAAgXmCgoaRC/4X/C/4X/C/4X/C64Ap")); + } + }, layout: undefined, + /* + layouts: { + idle: function() { + switch(settings.dial) { + case "Discs": + break; + case "Numeric": + default: + break; + } + }, + start: function(phase) { + switch(settings.dial) { + case "Discs": + break; + case "Numeric": + default: + break; + } + }, + race: function() { + + } + }, + */ mode: "idle", // idle, start, race" countdown: 300, // 5 minutes counter: undefined, interval: undefined, - raceTimeStart: undefined, - - // compass settings - calibrated: false, - directions: [ - "N", - // NNO: {22.5, 22.5}, - "NE", - // ONO: {67.5, 67.5}, - "E", - // OSO: {112.5, 112.5}, - "SE", - // SSO: {157.5, 157.5}, - "S", - // SSW: {202.5, 202.5}, - "SW", - // WSW: {247.5, 247.5}, - "W", - // WNW: {292.5, 292.5}, - "NW", - // NNW: {337.5, 337.5}, - ], - settings: Object.assign({ "debug": false, - "dial": "numeric", + "buzzer": true, + "dial": "Numeric", "gps": false, - "compass": false, + "record": false, + "theme": "Dark", "fgColor": "#FFFF00", "bgColor": "#000000" }, require('Storage').readJSON("regattatimer.json", true) || {}), translations: Object.assign({ "de": { - "speed": "FüG", + "speed": "FüG", // Fahrt über Grund "speed_unit": "kn" }, "en": { - "speed": "SOA", + "speed": "SOA", // SOA speed of advance "speed_unit": "kn" } }, require('Storage').readJSON("translations.json", true) || {}), + init: function() { + + if(this.settings.debug) { + this.countdown = 1; + } + + if(this.settings.theme == "Dark") { + this.settings.fgColor = "#FFFF00"; + this.settings.bgColor = "#000000"; + } + else { + this.settings.fgColor = "#000000"; + this.settings.bgColor = "#FFFF00"; + } + + Bangle.setLCDPower(1); + Bangle.setLCDTimeout(0); + /* + // in "idle", "start" or "stoped" mode, a button click (re)starts the countdown + // in "race" mode, a button click stops the counter + var onButtonClick = (function(ev) { + switch(this.mode) { + case "idle": + this.resetCounter(); + this.mode = "start"; + this.setLayoutStartMinSec(); + this.startCounter(); + this.interval = setInterval((function() { + this.startCounter(); + }).bind(this), 1000); + break; + case "stoped": + case "start": + this.resetCounter(); + this.setLayoutIdle(); + break; + case "race": + this.raceCounterStop(); + break; + } + }).bind(this); + + setWatch(onButtonClick, BTN1, true); + */ + + setWatch(this.onButtonClick.binf(this), BTN1, true); + + this.setLayoutIdle(); + }, + + onButtonClick: function(ev) { + switch(this.mode) { + case "idle": + this.resetCounter(); + this.mode = "start"; + this.setLayoutStartMinSec(); + this.startCounter(); + this.interval = setInterval((function() { + this.startCounter(); + }).bind(this), 1000); + break; + case "stoped": + case "start": + this.resetCounter(); + this.setLayoutIdle(); + break; + case "race": + this.raceCounterStop(); + break; + } + }, + + onGPS: function(fix) { + if(this.mode == "race") { + if(fix.fix && isFinite(fix.speed)) { + this.layout.clear(layout.speed); + this.layout.speed.label = fix.speed.toFixed(2); + this.layout.render(this.layout.speed); + } + this.layout.satellites.label = fix.satellites; + } + }, + translate: function(slug) { return this.translations[locale][slug]; }, - /** - * During the start phase, the the clock counts down 5 4 1 minutes. - * On button press, the countdown begins again. - */ + // during the start phase, the clock counts down 5 4 1 0 minutes + // a button click restarts the countdown startCounter: function() { this.counter --; @@ -98,16 +170,15 @@ function Regattatimer() { if(counterMinutes > 0) { this.layout.minutes.label = counterMinutes; - this.layout.seconds.label = "0".concat(this.counter - counterMinutes * 60).toString().slice(-2); + // this.layout.seconds.label = "0".concat(this.counter - counterMinutes * 60).toString().slice(-2); + this.layout.seconds.label = this.padZeroLeft(this.counter - counterMinutes * 60); this.layout.render(); } else { this.setLayoutStartSec(); - //this.layout.seconds.label = "0".concat(this.counter.toString()).slice(-2); this.layout.seconds.label = this.counter.toString(); this.layout.render(); } - // this keeps the watch LCD lit up g.flip(); } @@ -116,22 +187,23 @@ function Regattatimer() { this.raceCounterStart(); } }, - padZeroLeft: function(s) { - return "0".concat(s).slice(-2); + padZeroLeft: function(str) { + return str.toString().padStart(2, "0"); }, formatTime: function(time) { - var hours = parseInt(time / 3600); - var minutes = parseInt(time / 60); - var seconds = time - (minutes * 60); + var + minutes = parseInt(time / 60), + seconds = time - (minutes * 60); - return this.padZeroLeft(hours.toString()) - .concat(":") - .concat(this.padZeroLeft(minutes.toString())) - .concat(":") - .concat(this.padZeroLeft(seconds.toString())); + return this.padZeroLeft(parseInt(time / 3600)) + ":" + this.padZeroLeft(minutes) + ":" + this.padZeroLeft(seconds); }, raceCounter: function() { - Bangle.setLCDPower(1); + + if(this.counter % 60 == 0) { + this.layout.clear(this.layout.battery); + this.layout.battery.label = E.getBattery() + "%"; + this.layout.render(this.layout.battery); + } this.counter ++; @@ -155,38 +227,27 @@ function Regattatimer() { this.interval = undefined; } - Bangle.buzz(); + if(settings.buzzer) { + Bangle.buzz(); + } this.counter = 0; - // switch to race mode this.mode = "race"; this.setLayoutRace(); this.raceCounter(); - this.interval = setInterval((() => { + this.interval = setInterval((function() { this.raceCounter(); }).bind(this), 1000); }, - /** - * Show an initial splash screen - */ - /* - setLayoutSplash: function() { - g.clear(); - - (new Layout({ - type: "v", - bgCol: this.settings.bgColor, - c: [ - {type: "txt", font: "17%", label: ""}, - {type: "txt", font: "17%", col: this.settings.fgColor, label: "REGATTA\nTIMER"}, - {type: "txt", font: "6x8", col: this.settings.fgColor, label: "v0.1"}, - {type: "txt", font: "6x8", col: this.settings.fgColor, fillx: true, filly: true, pad: 4, label: "BUTTON PUSH -> start"}, - {type: "txt", font: "6x8", col: this.settings.fgColor, fillx: true, pad: 4, label: "(c) 2021-2023\nNaden Badalgogtapeh"} - ]}, {lazy:true})).render(); + resetCounter: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + this.counter = this.countdown; }, - */ setLayoutIdle: function() { @@ -194,24 +255,26 @@ function Regattatimer() { this.mode = "idle"; - (new Layout({ + this.layout = new Layout({ type: "v", bgCol: this.settings.bgColor, c: [ { - type: "h", + type: "v", c: [ {type: "txt", font: "Anton", label: "5", col: this.settings.fgColor, id: "minutes", fillx: 1, filly: 1}, + {type: "txt", font: "20%", label: "--:--", col: this.settings.fgColor, id: "daytime", fillx: 1, filly: 1} ] } - ]}, {lazy:true})).render(); - }, - resetCounter: function() { - if(this.interval) { - clearInterval(this.interval); - this.interval = undefined; - } - this.counter = this.countdown; + ]}, {lazy: true}); + + this.interval = setInterval((function() { + this.layout.daytime.label = require("locale").time(new Date(), 1); + this.layout.render(); + + // keeps the watch screen lit up + g.flip(); + }).bind(this), 1000); }, setLayoutStartMinSec: function() { g.clear(); @@ -227,9 +290,8 @@ function Regattatimer() { {type: "txt", font: "Anton", label: "59", col: this.settings.fgColor, id: "seconds", fillx: 1, filly: 1}, ] } - ]}, {lazy:true}); - - //this.layout.render(); + ]}, {lazy: true} + ); }, setLayoutStartSec: function() { g.clear(); @@ -239,7 +301,7 @@ function Regattatimer() { bgCol: this.settings.bgColor, c:[ {type: "txt", font: "Anton", label: "", fillx: true, filly: true, col: this.settings.fgColor, id: "seconds"}, - ]}, {lazy:true}); + ]}, {lazy: true}); }, setLayoutRace: function() { g.clear(); @@ -249,105 +311,27 @@ function Regattatimer() { bgCol: this.settings.bgColor, c: [ {type: "txt", font: "20%", label: "00:00:00", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, id: "racetime"}, - {type: "txt", font: "20%", label: "-", col: this.settings.fgColor, pad: 4, filly:1, fillx:1, id: "compass"}, + {type: "txt", font: "15%", label: "-", col: this.settings.fgColor, pad: 4, filly:1, fillx:1, id: "daytime"}, // horizontal {type: "h", c: [ - {type:"txt", font:"10%", label: this.translate("speed"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, - {type:"txt", font:"15%", label: "...", col: this.settings.fgColor, pad:4, fillx:1, filly:1, id: "speed"}, - {type:"txt", font:"10%", label: this.translate("speed_unit"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, + {type: "txt", font: "10%", label: this.translate("speed"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, + {type: "txt", font: "20%", label: "0", col: this.settings.fgColor, pad:4, fillx:1, filly:1, id: "speed"}, + {type: "txt", font: "10%", label: this.translate("speed_unit"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, ]}, {type: "h", c: [ - {type: "txt", font: "10%", label: "-", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, id: "satellites"}, - {type: "txt", font: "10%", label: "00:00", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, halign: 1, id: "daytime"}, + {type:"img", pad: 2, src: this.icons.satellite()}, + {type: "txt", font: "10%", label: "0", col: this.settings.fgColor, pad: 2, filly:1, id: "satellites"}, + // hacky, use empty element with fillx to push the other elments to the left an right side + {type: undefined, pad: 2, fillx: 1}, + {type:"img", pad: 2, src: this.icons.battery()}, + {type: "txt", font: "10%", label: "-", col: this.settings.fgColor, pad: 2, filly: 1, id: "battery"}, ]} - ]},{lazy:true}); - }, - onGPS: function(fix) { - if(this.mode == "race") { - if(fix.fix && isFinite(fix.speed)) { - this.layout.speed.label = fix.speed.toFixed(2); //m[1]; - } - this. layout.satellites.label = "Sats: ".concat(fix.satellites); - } - }, - /* - onCompass: function(data) { - - if(this.mode == "race") { - if(this.calibrated) { - this.layout.compass.label = this.directions[data.heading.toFixed(0) % 8]; - } - else { - this.layout.compass.label = "turn 360°"; - } - - var start = data.heading.toFixed(0) - 90; - - if(start<0) { - start += 360; - } - - var frag = 15 - start%15; - if (frag<15) {}else frag = 0; - var res = start + frag; - - var label = '?'; - - if(res%90==0){ - label = this.directions[Math.floor(res/45)%8]; - } else if (res%45==0) { - label = this.directions[Math.floor(res/45)%8]; - } - - //this.layout.compass.label = this.directions[data.heading.toFixed(0) % 8].concat(" ").concat(data.heading.toFixed(0).toString()) - this.layout.compass.label = label.concat(" ").concat(data.heading.toFixed(0).toString().concat("°")) - } - }, - */ - init: function() { - - if(this.settings.debug) { - this.countdown = 1; - } - - Bangle.setLCDPower(1); - Bangle.setLCDTimeout(0); - - this.setLayoutIdle(); - - var onButtonClick = ((ev) => { - - // "idle" or "start" mode, a button click (re)starts the start counter - switch(this.mode) { - case "idle": - this.resetCounter(); - this.mode = "start"; - this.setLayoutStartMinSec(); - this.startCounter(); - this.interval = setInterval((() => { - this.startCounter(); - }).bind(this), 1000); - break; - case "stoped": - case "start": - this.resetCounter(); - this.setLayoutIdle(); - break; - // "race" mode, a button click triggers a time snapshot and stops - case "race": - this.raceCounterStop(); - break; - } - - }).bind(this); - - setWatch(onButtonClick, BTN1, true); + ]}, {lazy: true}); } - } + }; } -var regattatimer = Regattatimer() - +var regattatimer = Regattatimer(); regattatimer.init(); if(regattatimer.settings.gps) { @@ -355,17 +339,12 @@ if(regattatimer.settings.gps) { Bangle.on('GPS', regattatimer.onGPS.bind(regattatimer)); } -if(regattatimer.settings.compass) { - Bangle.setCompassPower(1); - Bangle.on('mag', regattatimer.onCompass.bind(regattatimer)); -} +Bangle.on('kill', function() { + Bangle.setLCDPower(0); + Bangle.setLCDTimeout(10); -Bangle.on('kill',function() { if(regattatimer.settings.gps) { Bangle.setGPSPower(0); } - - if(regattatimer.settings.compass) { - Bangle.setCompassPower(0); - } }); + diff --git a/apps/regattatimer/icon.png b/apps/regattatimer/icon.png index 2f634774c39f7fe823bc6c0a1a4c0cf9dfb81233..eb6057305876b0d72d3a8e7f3d52e440a579aaf0 100644 GIT binary patch delta 1355 zcmV-R1+@B~8t)2_B$2@se+3~)L_t(|oYh)Mixf=|4Hfm^iVJwsLELfQcSIb}0bvC7 zrYLTp;6;xj2wog<7iDx*Tt;yLFCK)s_y^R32M70k!`*Qld(`*w$SymxGHdDTs_xIj z1MSMnjF*`a@giyj5d6!KEGPf7hJq3 zShGgZySIDJE?oo%e+~%#{7GSpo%A2&*s+4|-vv8& zW={Z6YmFaot~GhG+aD|f$Bx<8=Kg*4tXu?;_lfS^&Gl$oD1o*%l@;}u_U#Myi>p_& zBLE2Y>@nA?s9)s^y|Q~&hqr=ATSoW0U)Z@2RVZ44wZZ=N}riN-(a z+qZ)2*9D(He`jkv@;GtAv}wVD00OUHn?jDMzO}VHMj|3(_v%%!i>lzw8wUY8q|S8h z+R7V`JXWtZZEI|F5TL>OAd0R^8;(3KTrh20zurM$=1g-Cm1Ct1M;_0fnYPWEp=Oq*1ap*a~b%k`ym?3!ivhe@Yrv>%(O22r& zZQ3N*xKVKFk`jlMBwk+O;v8RJ@6P5f0wYF*T%jl@<}NK>tbU^+96sE=b2=_xE*Lp_ zv|z`M=ywo6!0*KU`}>^O+g1IDUc9h{75g}|e`kjm7%s3Z)lVl1wY7prkBSVVaS|Di z%{#hs;{5p-iOer0*!n29p@>=NrQ zf3ag$ObAR1&CPaEfBd*T&LY4>Ma8MV$V?hH38jIS7Qwi2rVZ?JyF$3LvBf45n@Rcv z)H9`gh=_|^POxyHxi)QKv#sRTB2&C4qO#ig8b@pK@ZsQh0trxmu(9f&jMpg(pJ;Bj zuMNR=f8y>m{edlR|D<#-MyE7FY%V9*f4$pwSc2atN@}lrBwQ!q<%yFglVxna)6ihY zd41Lc`e65f$}#M_8FdV`jV=$w=dfXSJ~08})-6?yxfb?cF%}>U zeFXpQx+3BO#|A~4R0t4^#q1KZP>(VwE~=_jl1d`gr4;9*E@5ab_VcW#DyyqiK_?=$ z58X+@UL_4-p}_{3*TL4oGLV%Nos)i`OJe+A|JZ{a?5w-klI7&(z<-M4p~-}(t@Qu^ N002ovPDHLkV1gEOo%H|! literal 3359 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 KRm+s`Km8vzgyA~? diff --git a/apps/regattatimer/metadata.json b/apps/regattatimer/metadata.json index d933af5d3..19c81f987 100644 --- a/apps/regattatimer/metadata.json +++ b/apps/regattatimer/metadata.json @@ -2,18 +2,17 @@ "id": "regattatimer", "name": "Regatta Timer", "shortName": "RegattaTimer", - "version": "0.2", + "version": "0.3", "description": "Regatta Timer with 5-4-1 Countdown", "icon": "icon.png", - "screenshots": [{"url":"screenshot.png"}], "tags": "tool,outdoors,sailing,race,regatta,boat,timer", "supports": ["BANGLEJS2"], "readme": "README.md", "storage": [ - {"name":"regattatimer.app.js","url":"app.js"}, - {"name":"regattatimer.settings.js","url":"settings.js"}, - {"name":"regattatimer.img","url":"app-icon.js","evaluate":true} + {"name": "regattatimer.app.js", "url": "app.js"}, + {"name": "regattatimer.settings.js", "url": "settings.js"}, + {"name": "regattatimer.img", "url": "app-icon.js", "evaluate": true}, ], "data": [{"name": "regattatimer.json"}], - "screenshots": [{"url":"screenshot.png"},{"url":"screenshot-1.png"},{"url":"screenshot-2.png"}] + "screenshots": [{"url": "screenshot.png"},{"url": "screenshot-1.png"},{"url": "screenshot-2.png"},{"url": "screenshot-3.png"},{"url": "screenshot-4.png"},{"url": "screenshot-5.png"},{"url": "screenshot-6.png"},{"url": "screenshot-7.png"}] } diff --git a/apps/regattatimer/regattatimer.json b/apps/regattatimer/regattatimer.json index baad5af42..c9ea85101 100644 --- a/apps/regattatimer/regattatimer.json +++ b/apps/regattatimer/regattatimer.json @@ -1,8 +1,10 @@ { "debug": false, - "dial": "numeric", + "buzzer": true, + "dial": "Numeric", "gps": false, - "compass": false, + "record": false, + "theme": "Dark", "fgColor": "#FFFF00", "bgColor": "#000000" } diff --git a/apps/regattatimer/screenshot-1.png b/apps/regattatimer/screenshot-1.png index a24bfa88715623b1386413c57d99fca915c501c9..6888ccd62957148b4f419cdd5c52c8cc07b6633a 100644 GIT binary patch literal 1800 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 diff --git a/apps/regattatimer/screenshot-2.png b/apps/regattatimer/screenshot-2.png index 932028ae09d44ca916be36d5bcf4c7ee901ed26d..bde59766ddfb7a3fda36727fecd35652c0273c42 100644 GIT binary patch literal 2145 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 diff --git a/apps/regattatimer/screenshot-3.png b/apps/regattatimer/screenshot-3.png new file mode 100644 index 0000000000000000000000000000000000000000..4416a0fc8bdf64955247bb0e6d371f8b32f08270 GIT binary patch literal 1830 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 literal 0 HcmV?d00001 diff --git a/apps/regattatimer/screenshot-7.png b/apps/regattatimer/screenshot-7.png new file mode 100644 index 0000000000000000000000000000000000000000..e1f96005f9d2562ace4191296a96901542ce4749 GIT binary patch literal 2376 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 diff --git a/apps/regattatimer/settings.js b/apps/regattatimer/settings.js index 7466f0906..21922242b 100644 --- a/apps/regattatimer/settings.js +++ b/apps/regattatimer/settings.js @@ -1,52 +1,79 @@ (function(back) { - var file = "regattatimer.json"; - // Load settings - var settings = Object.assign({ - "debug": false, - "dial": "numeric", - "gps": false, - "compass": false, - "fgColor": "#FFFF00", - "bgColor": "#000000" - }, require('Storage').readJSON(file, true) || {}); + var + file = "regattatimer.json", - function writeSettings() { - require('Storage').writeJSON(file, settings); + storage = require("Storage"), + + dials = ["Numeric", "Discs"], + + themes = ["Light", "Dark"], + + settings = Object.assign({ + "debug": false, + "buzzer": true, + "dial": "Numeric", + "gps": false, + "record": false, + "theme": "Dark", + "fgColor": "#FFFF00", + "bgColor": "#000000" + }, storage.readJSON(file, true) || {}); + + function save(key, value) { + settings[key] = value; + storage.writeJSON(file, settings); } - // Show the menu E.showMenu({ "" : { "title" : "Regatta Timer" }, "< Back" : () => back(), - 'GPS': { + "GPS": { value: !!settings.gps, // !! converts undefined to false onchange: v => { - settings.gps = v; - writeSettings(); + save("gps", v); } }, - 'COMPASS': { - value: !!settings.compass, // 0| converts undefined to 0 - onchange: v => { - settings.compass = v; - writeSettings(); - } - }, - "DIAL": { - value: settings.dial, + "THEME": { + value: themes.indexOf(settings.theme), min: 0, - max: 1, - format: v => ["Numeric", "Disc"][v], - onchange: v => { - settings.dial = v; - writeSettings(); + max: themes.length - 1, + step: 1, + wrap: true, + format: v => themes[v], + onchange: (d) => { + save("theme", themes[d]); } }, - 'DEBUG': { + "BUZZER": { + value: !!settings.buzzer, // !! converts undefined to false + onchange: v => { + save("buzzer", v); + } + }, + /* + "DIAL": { + value: dials.indexOf(settings.dial), + min: 0, + max: dials.length - 1, + step: 1, + wrap: true, + format: v => dials[v], + onchange: (d) => { + save("dial", dials[d]); + } + }, + "RECORD": { + value: !!settings.record, // 0| converts undefined to 0 + onchange: v => { + settings.record = v; + save("record", v); + } + }, + */ + "DEBUG": { value: !!settings.debug, // 0| converts undefined to 0 onchange: v => { - settings.debug = v; - writeSettings(); + save("debug", v); } }, }); From 017df90c58e65f6a3a9dbc11f2a6b1074570e761 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 1 Mar 2024 15:39:28 +0100 Subject: [PATCH 20/40] Add files via upload --- apps/regattatimer/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/regattatimer/README.md b/apps/regattatimer/README.md index dbf924775..e3729d281 100644 --- a/apps/regattatimer/README.md +++ b/apps/regattatimer/README.md @@ -20,24 +20,31 @@ ## Screenshots *Idle mode: showing a big 5 and time of day below* + ![Idle mode: showing a big 5 and time of day below](screenshot-1.png) *Start mode: minutes and seconds* + ![Start mode: minutes and seconds](screenshot-2.png) *Start mode: seconds* + ![Start mode: seconds](screenshot-3.png) *Race mode: elapsed time, time of day, speed, satellites, battery* + ![Race mode: elapsed time, time of day, speed, satellites, battery](screenshot-4.png) *Race mode: with german abbreviations* + ![Race mode: with german abbreviations](screenshot-5.png) *Settings page: main* + ![Settings page: main](screenshot-6.png) *Settings page: choose the theme* + ![Settings page: choose the theme](screenshot-7.png) ## Localization @@ -56,7 +63,8 @@ Localization is done by the Bangle.js 2 app "Languages" Report bugs or request a feature at [github.com/naden](https://github.com/naden) ## Roadmap -* adding a seconds coundown layout to mimic a classic regatta chronograph +* add a seconds coundown layout to mimic a classic regatta chronograph +* add recording of gps course and race time ## Created by From 5bcd589d9ffbb19309048c49d223f0b2e969393c Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 1 Mar 2024 15:40:13 +0100 Subject: [PATCH 21/40] Delete apps/regattatimer/screenshot (1).png --- apps/regattatimer/screenshot (1).png | Bin 1950 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 apps/regattatimer/screenshot (1).png diff --git a/apps/regattatimer/screenshot (1).png b/apps/regattatimer/screenshot (1).png deleted file mode 100644 index a24bfa88715623b1386413c57d99fca915c501c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1950 zcmds&{X5j@9>=F|x6G)l`C5{b$C>e%v1(cJxWiyp6w-Pa)2z~*4K)&t#;R`}i%f%= zhFDZ=2w|O!hsf8S9vq6}gl3zV*~#P42&Gh({nqaJ56(~Lhx>kiuGf9tpZDjwulIG` z*?WWhHftGb;c&Rk41b>xOdCHe{03}qXc=c=vMwpakA|!7-!_B8X@)a=zTC%SsY?cr zKXcv$Bx`HaOqZzJOYPf=lw2HbuA|;-@56qBk$bDVgwNh9HK9O4ni=<4K)A9&Y06QF6F zf)tuW^ST2l+{#umSLPVd9SiF73W3ylYvj#NG)yHz%?9GPwCJ}+vS8Wd#Qe{88e?BF zVV=xkHbV`@7#RJ}f-9r$M! zO3gU(xU&T9>im(!z46!jxk7k3-k8oVq&3g7Xv+LALhwx6;#xi*@f+{#_WsDFdc1v4 zU0I-B@8NLgH8ePt!7lVznw7#lUnVy4jw$UY_`03|$W^Z<8oT&mkjU<*qb>S8Ne8(x zs-k_V+eRznv98RkO}DOTF)7&n8)2-jPZ8U@6y)Whn(0GZT2+;-rSSpl zlP=lK@4eXhQ`!wwmEBFMAA4b0CmdK6kQ%k-ee*8wfbiSPEfM;e7#FA($6)7Q*)K#5 zyDGe)hIB}`bL#8gD&kgBcH;%;H&Dgmg%y>cJ_A+!nq+9R_Y-z?Hg7P?KVSdFTxzA_ zmLt&sAqmS^vnyH+)ja|o)la@ij=Hj!125DOp*=?D_TPM`^WxTwP2)jDmqYsS>~lAB zJ_%`74g@zt`z~fnBuI3%@8e9Ie@>sd zI%LirzsO?dpr@}`!8XnKm0uy-MYrDf5Wp54RHuZFEpCD_eV=muMnJ;#0vpCfg zbL;x?C8BED-B*!EZ#bR&67P2Hj-q_chq+0^@XDkhu1uI#`O8Q^o5n4V(l*QDF z7K5$*8u-b&U)e!9k*4z|XU_i(D@pU#NN}5P0|~Lr2vr!ZjYz$rfnL2-v1IVv7m4D< z%aUXr-Xz7t{KfOXG!+>SaJunkUyVj6i=s}ZUwL`^GCmZPx!lbx#>f98Vb%+SNpFe= zpAi9OA=p+Ui-}16p1IXshgSr#USazofI3W~- zq1@Jdygd)K=*RkRC+vo%swIyyfK#FcX3k*4&VDyasAR4-r`?Nfz3#o`j)J*@yQcGQ zyNvEHY}Uh{pm;$i-CbUnzO>Np*WtpZNa=|Pc@Pd!XML+ApFxA#>k=%_pec7g=fYlW z%jglQt{2-i#?YBy+9;;R$uTi<*jxg1WQ@XQH73GK8-gJG^B?=3q(Jz1zuoIC*MXFO z$0oS015#2YBE~_kGfmP`93&?`LK(O}?3A8je}PHhp*|1n!^JV^K|b{~_SxS7D)VLU From 10f8a606151b69f8c9cb226af10e5a167e3343d5 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 1 Mar 2024 15:40:25 +0100 Subject: [PATCH 22/40] Delete apps/regattatimer/screenshot (2).png --- apps/regattatimer/screenshot (2).png | Bin 2134 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 apps/regattatimer/screenshot (2).png diff --git a/apps/regattatimer/screenshot (2).png b/apps/regattatimer/screenshot (2).png deleted file mode 100644 index 932028ae09d44ca916be36d5bcf4c7ee901ed26d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2134 zcmd6p`!kz~8pnw@q)A(e5-m~@TP+f2jUJalf{n&)>)Hq@O|46}i%{1bT79uDQN*q| zt)i-SP^m<86N{~sQfKSZ3zZzUf~ZqP+*7T~vH!*X@I0TH&&+q`ndkZCDfBx}IRZC; ztEs6S@$?`E98mCA!88v#U#ti}fO=8@#Z9ec*yw|r8Vu=4b`44oU#j-McG2!I@XHtD z|C*aqar`tW$LJ;?-P1Fks%_A(5C?EoVzKxVa|d+shbUneA3H>3>9{b+XTDVfy~io( zKtK2zK(;4e&!sSo{_Zjl*z4Or`u=;F(pVR<%9}Q@LX3SmaZ{!zuVGg z-bFrOrnNkbzm9#o@ruVnrgz|$a@aj-%y&w%?t12A%uuc*RJ(y0s2k69#>CIK_Bp|v z)NhQNvU^a{D||ymn2?ykv=gyeQpxEP|B_vEk-M##*IB^lg9~1Wa^0N9FtDAf`px?a zI*bbzsjy!|{$cT0l?cS+1a$+-GraEXt*c-5C-jo;oo$Y&37B$g)Vy&@;W@YV&&sSg z+V64u82ivkN)Q@J3SIJq=KmZWIGD7dNj+lU{K_*bYp_A*#oCQRX((GcS zYxU}w4@ySaULrDEiCzrLMf@NPRVTc0&l;z~_lAvxRF$Hn@D8{i;hW@leAd`+!4Ai?Mu`_LeCnZ|+gk3Wg{_rN85kWwtgtq2UVNDNnWSHx#H*MoYKqWMr2 zdJw$YznX*)&azibE$*Mpc~Tj+Nh~5j5TmVFM7T*O&YQWR*ygSSoipL<~wB>MVJvG7|)%67Qy!cSxc$>DCkvSgWO!;pVX}o^=S3GEIN7uq!g2se7*bGvc`B$t*SMb67e5Fp_98|5$>e(+G(GBc(q$n-UouKU0ut6I*&LJ^H&S;)8PR zdfJxHxi)3x<<74XM$t7;&g$f0h3uj~J>J%LBgrNfm0rs|A<-D$PuVa7V`Nr{%s1$o zA8$Rg1w%xz*tT6KYlM`w`{Fyh*l9hH4#<@DSaQlf#Xu|%4s&@N&Un80zHFGD&VA$~ zKY*U_`^H(9;KE=TPGB-(!N|YcVv2!M1zh;f7+GHmmsmM!9eujxOEZB4>u-}2jWK)+ zVP#ySWQZ^$Kij?>d?rVRyEeW^u~ zPWR#L&e+)=XeW6%y7PiOwybU1>eK0rv)+oZ*ixi@s3I&O3Jten>#*!59sty$F2fdy zhh^H@Ma!gh*&f;nvaDtlgoB_+EjKnpN<)X27*2f`JuaYLr qQSEWRrX9|cq-{*5di~8KyBhT-a$~KMDa65}pyug*p4{Tb$o@a%)!qOA From 7ee0149b810a24a809adca36e298dab72a275267 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 1 Mar 2024 21:12:14 +0100 Subject: [PATCH 23/40] Update metadata.json --- apps/regattatimer/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regattatimer/metadata.json b/apps/regattatimer/metadata.json index 19c81f987..74e7e3bfb 100644 --- a/apps/regattatimer/metadata.json +++ b/apps/regattatimer/metadata.json @@ -11,7 +11,7 @@ "storage": [ {"name": "regattatimer.app.js", "url": "app.js"}, {"name": "regattatimer.settings.js", "url": "settings.js"}, - {"name": "regattatimer.img", "url": "app-icon.js", "evaluate": true}, + {"name": "regattatimer.img", "url": "app-icon.js", "evaluate": true} ], "data": [{"name": "regattatimer.json"}], "screenshots": [{"url": "screenshot.png"},{"url": "screenshot-1.png"},{"url": "screenshot-2.png"},{"url": "screenshot-3.png"},{"url": "screenshot-4.png"},{"url": "screenshot-5.png"},{"url": "screenshot-6.png"},{"url": "screenshot-7.png"}] From 867474fbb0ae80fdadebc7a9c69bd4a63de6c109 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 1 Mar 2024 21:20:58 +0100 Subject: [PATCH 24/40] Update README.md --- apps/regattatimer/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/regattatimer/README.md b/apps/regattatimer/README.md index e3729d281..2ca05837a 100644 --- a/apps/regattatimer/README.md +++ b/apps/regattatimer/README.md @@ -56,18 +56,17 @@ Localization is done by the Bangle.js 2 app "Languages" * Choose your language from the dropdown * Click `upload` -**Some nautical abbreviations which are not part of the banglejs2 Languages packages are stored in `translations.json`.** +**Some nautical abbreviations which are not part of the Bangle.js 2 app "Languages" app are stored in `translations.json`.** ## Feedback Report bugs or request a feature at [github.com/naden](https://github.com/naden) ## Roadmap -* add a seconds coundown layout to mimic a classic regatta chronograph +* add a seconds coundown layout; mimic a classic regatta chronograph * add recording of gps course and race time ## Created by - © 2021 - 2024 [naden.de](https://naden.de) Icons by [Icons8](https://icons8.com/) From 465673bd567cf43d793675196a932638818297e9 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 1 Mar 2024 21:32:08 +0100 Subject: [PATCH 25/40] Update app.js --- apps/regattatimer/app.js | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/apps/regattatimer/app.js b/apps/regattatimer/app.js index 87769ee0e..6ca0eaf68 100644 --- a/apps/regattatimer/app.js +++ b/apps/regattatimer/app.js @@ -90,7 +90,7 @@ function Regattatimer() { Bangle.setLCDPower(1); Bangle.setLCDTimeout(0); - /* + // in "idle", "start" or "stoped" mode, a button click (re)starts the countdown // in "race" mode, a button click stops the counter var onButtonClick = (function(ev) { @@ -116,35 +116,10 @@ function Regattatimer() { }).bind(this); setWatch(onButtonClick, BTN1, true); - */ - - setWatch(this.onButtonClick.binf(this), BTN1, true); this.setLayoutIdle(); }, - onButtonClick: function(ev) { - switch(this.mode) { - case "idle": - this.resetCounter(); - this.mode = "start"; - this.setLayoutStartMinSec(); - this.startCounter(); - this.interval = setInterval((function() { - this.startCounter(); - }).bind(this), 1000); - break; - case "stoped": - case "start": - this.resetCounter(); - this.setLayoutIdle(); - break; - case "race": - this.raceCounterStop(); - break; - } - }, - onGPS: function(fix) { if(this.mode == "race") { if(fix.fix && isFinite(fix.speed)) { From de5b976d4f9ebdd29c2af8fc28b8e158f8dc37ad Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 1 Mar 2024 21:38:19 +0100 Subject: [PATCH 26/40] Add files via upload --- apps/regattatimer/app.js | 2 +- apps/regattatimer/regattatimer.json | 2 +- apps/regattatimer/settings.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/regattatimer/app.js b/apps/regattatimer/app.js index 6ca0eaf68..9f7c4f1af 100644 --- a/apps/regattatimer/app.js +++ b/apps/regattatimer/app.js @@ -55,7 +55,7 @@ function Regattatimer() { "debug": false, "buzzer": true, "dial": "Numeric", - "gps": false, + "gps": true, "record": false, "theme": "Dark", "fgColor": "#FFFF00", diff --git a/apps/regattatimer/regattatimer.json b/apps/regattatimer/regattatimer.json index c9ea85101..d8ed2f2ac 100644 --- a/apps/regattatimer/regattatimer.json +++ b/apps/regattatimer/regattatimer.json @@ -2,7 +2,7 @@ "debug": false, "buzzer": true, "dial": "Numeric", - "gps": false, + "gps": true, "record": false, "theme": "Dark", "fgColor": "#FFFF00", diff --git a/apps/regattatimer/settings.js b/apps/regattatimer/settings.js index 21922242b..c96bc4771 100644 --- a/apps/regattatimer/settings.js +++ b/apps/regattatimer/settings.js @@ -12,7 +12,7 @@ "debug": false, "buzzer": true, "dial": "Numeric", - "gps": false, + "gps": true, "record": false, "theme": "Dark", "fgColor": "#FFFF00", From fedf2d3190cb873aa65b847dcd9b05312396e3f3 Mon Sep 17 00:00:00 2001 From: naden Date: Sat, 2 Mar 2024 19:22:33 +0100 Subject: [PATCH 27/40] Add files via upload --- apps/regattatimer/README.md | 2 + apps/regattatimer/app-icon.js | 2 +- apps/regattatimer/app.js | 84 ++++++++++++++++------------ apps/regattatimer/icon.png | Bin 1391 -> 2990 bytes apps/regattatimer/regattatimer.json | 4 +- apps/regattatimer/screenshot-6.png | Bin 2859 -> 2775 bytes apps/regattatimer/settings.js | 4 +- 7 files changed, 52 insertions(+), 44 deletions(-) diff --git a/apps/regattatimer/README.md b/apps/regattatimer/README.md index 2ca05837a..8d906e1d7 100644 --- a/apps/regattatimer/README.md +++ b/apps/regattatimer/README.md @@ -65,6 +65,8 @@ Report bugs or request a feature at [github.com/naden](https://github.com/naden) ## Roadmap * add a seconds coundown layout; mimic a classic regatta chronograph * add recording of gps course and race time +* add icons for light mode +* add flag icons ## Created by © 2021 - 2024 [naden.de](https://naden.de) diff --git a/apps/regattatimer/app-icon.js b/apps/regattatimer/app-icon.js index 8f9c37a94..3c6f27c44 100644 --- a/apps/regattatimer/app-icon.js +++ b/apps/regattatimer/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEw4n/8H/BAWlsEI0ExkFj0H34GcgHnwGUsHO4H/kEhkGl4He8HeI8dgCyvTiM0ruABxNoxAACwwIC4MRAAMUkAWHgoNCAAMdBIVBBAccuAXGsIXIwtEmIxD0AXFhgXIAAMI6oZCnIXFh1VqtRC44AB2RkCqBiHC5UAhIxBip5KC5EA8gwIyMRiYXKgyWBjIIEhQgBpIXKgEEEwKSEA4MU3IXLhYwBoAHDuMRn8JC5cA8cRi4eDTIOQC5oNBjgFChoFChEx4wXKgApB6BeDiQEBxVgC5chMAeBiM7CZYAD2cRjAEB6L3JAA7oBNwVRJgYANOQMVAgKtBwAXPhDRBAgMxiYDBylEB4lk+C3D6gnBCYYbDrAnFrngIgeUDoKoBgEGC4dHyxWEqgXDXAIXBLYNgC4ldykAhWoIwNk8EL1TIBC4pHEBYOAgV+gFV6mbg13C4hHCMYcGqEMd4fXuhOEC4R3DU4eUpLPFL4kFwCnEa6/RiNQC58FiMdAgOBiM7C5+ziMYMwURiQXPkMRoAEBgURjgXPUwJyDAoOQCxsJFIOwAwVxiM+C5vjiMXY4itBaoYAIgzRBLwQABhUxA4oAHE4MT0AIEyIwMFwRGEYwcRpwXJ8gNBdA9RHIKSIhJVBir3JTIMrBQuyWoIuIAAOTBgMTruAA4MI6otBiM3KZMKNYIZC4lMCoSDBRgoAFg4+CAAscuDjMhoxDFofQfZpaCoYVBmhkDACEGbpQAY")) +require("heatshrink").decompress(atob("qFQ4UB8H/AAIJBoGtqoACDZYPDCRwUGqATNgoTDoATNgISCqhtPio6QHgg6OHggJGn+q1X8PJAHFnwSBAAO8CYxiFgeq1/Agf61XAMgpiFnWvAof61hkFCYkD1YhEgfqAwkFOwk62EAhkCwEwgEOFAkBCYgfCnYTB9gvCCZECDwMshgGBmE4GAOACY8KHQIjBAAQTBh2gCYY6EgHwTIsPgA8EAAeogAeDGAcAlQSGgQRBE5EKKAYdDA4wfGYgo6HHgbKFgHrgB3BAA0OBgQAEDQw0Hcog5IHojyEgWwWAgAFncOOAkO4ATLgZbENYIAMJIk7CZo0ElcDCRfA9AFD9A8M0ErFgjlBABXwJQiyMWgyyMQwrGBmASLhjIDUgPs//wBoc8ZAwTEmGqKQiEDh2shATDdwMA16nE1ADCh4dBeAcCAYJlEgQTDYoQPCAYevFwQfBYAgTH0eqFAcKlATEhQTG2GsgeqgE4CYM6LQITHDoUD/8A9fq4E/SYI7HgfwO4fq1RVDCY2wgevT4krfgqLDWYXqI4IACE4cO1XMY4vsn41DgBSBBgX/bYugmD/HAAcMhQgDYogAJhRWFABk6XQkPCRfwnYFD9AtEAA+gSQgEEABPoAgYsEABLTDNAo8KAgakBDQgAFnbCBAwbwBCZbuDZAayMc4i0NWQgAB9cAIYhbEBgQaGHpCDBGg0KKwgAFgQeGA4XwBIr5BD5GoHg46BlQwH9QnJ1YGDoAeDO4MsBYc4O4IwDgITDgRsBlgiBFgITBnRODCYg8BXgM6gWADIMDHQgTFnQ8BhgTBmA6BTokBqDyE1blEgYvCAAUFCYgoB14FD/TEFgtUAwkD1Wv4ED/WqEwkAitVTIs+1QAC3gLFqoTGgE/CQP8BQwTBPAgALgITBMgoAKgoTBMgoAKMQI8QHQQ8QHQQACCZoSEChoPD")) diff --git a/apps/regattatimer/app.js b/apps/regattatimer/app.js index 9f7c4f1af..30d03daed 100644 --- a/apps/regattatimer/app.js +++ b/apps/regattatimer/app.js @@ -13,14 +13,6 @@ Graphics.prototype.setFontAnton = function(scale) { function Regattatimer() { return { - icons: { - "satellite": function() { - return hs.decompress(atob("jEYxH+AH4Ab6QIIBJAfNAAQtSC4gxSCwgYHHBYYMC6IYPC5AZOC8QYMC5YYLC5inSDH4waVbAYJCpgA/AAI=")); - }, - "battery": function() { - return hs.decompress(atob("jEYxH+AHHSAAgXmCgoaRC/4X/C/4X/C/4X/C64Ap")); - } - }, layout: undefined, /* layouts: { @@ -51,6 +43,33 @@ function Regattatimer() { countdown: 300, // 5 minutes counter: undefined, interval: undefined, + theme: null, + themes: { + "Light": { + "fgColor": "#000000", + "bgColor": "#FFFF00", + "icons": { + "satellites": function() { + return hs.decompress(atob("jEYxH+AH4Ab6QIIBJAfNAAQtSC4gxSCwgYHHBYYMC6IYPC5AZOC8QYMC5YYLC5inSDH4waVbAYJCpgA/AAI=")); + }, + "battery": function() { + return hs.decompress(atob("jEYxH+AHHSAAgXmCgoaRC/4X/C/4X/C/4X/C64Ap")); + } + } + }, + "Dark": { + "fgColor": "#FFFF00", + "bgColor": "#000000", + "icons": { + "satellites": function() { + return hs.decompress(atob("jEYxH+AH4Ab6QIIBJAfNAAQtSC4gxSCwgYHHBYYMC6IYPC5AZOC8QYMC5YYLC5inSDH4waVbAYJCpgA/AAI=")); + }, + "battery": function() { + return hs.decompress(atob("jEYxH+AHHSAAgXmCgoaRC/4X/C/4X/C/4X/C64Ap")); + } + } + } + }, settings: Object.assign({ "debug": false, "buzzer": true, @@ -58,8 +77,6 @@ function Regattatimer() { "gps": true, "record": false, "theme": "Dark", - "fgColor": "#FFFF00", - "bgColor": "#000000" }, require('Storage').readJSON("regattatimer.json", true) || {}), translations: Object.assign({ @@ -79,14 +96,7 @@ function Regattatimer() { this.countdown = 1; } - if(this.settings.theme == "Dark") { - this.settings.fgColor = "#FFFF00"; - this.settings.bgColor = "#000000"; - } - else { - this.settings.fgColor = "#000000"; - this.settings.bgColor = "#FFFF00"; - } + this.theme = this.themes[this.settings.theme]; Bangle.setLCDPower(1); Bangle.setLCDTimeout(0); @@ -202,7 +212,7 @@ function Regattatimer() { this.interval = undefined; } - if(settings.buzzer) { + if(this.settings.buzzer) { Bangle.buzz(); } @@ -232,13 +242,13 @@ function Regattatimer() { this.layout = new Layout({ type: "v", - bgCol: this.settings.bgColor, + bgCol: this.theme.bgColor, c: [ { type: "v", c: [ - {type: "txt", font: "Anton", label: "5", col: this.settings.fgColor, id: "minutes", fillx: 1, filly: 1}, - {type: "txt", font: "20%", label: "--:--", col: this.settings.fgColor, id: "daytime", fillx: 1, filly: 1} + {type: "txt", font: "Anton", label: "5", col: this.theme.fgColor, id: "minutes", fillx: 1, filly: 1}, + {type: "txt", font: "20%", label: "--:--", col: this.theme.fgColor, id: "daytime", fillx: 1, filly: 1} ] } ]}, {lazy: true}); @@ -256,13 +266,13 @@ function Regattatimer() { this.layout = new Layout({ type: "v", - bgCol: this.settings.bgColor, + bgCol: this.theme.bgColor, c: [ { type: "h", c: [ - {type: "txt", font: "Anton", label: "4", col: this.settings.fgColor, id: "minutes", fillx: 1, filly: 1}, - {type: "txt", font: "Anton", label: "59", col: this.settings.fgColor, id: "seconds", fillx: 1, filly: 1}, + {type: "txt", font: "Anton", label: "4", col: this.theme.fgColor, id: "minutes", fillx: 1, filly: 1}, + {type: "txt", font: "Anton", label: "59", col: this.theme.fgColor, id: "seconds", fillx: 1, filly: 1}, ] } ]}, {lazy: true} @@ -273,9 +283,9 @@ function Regattatimer() { this.layout = new Layout({ type: "v", - bgCol: this.settings.bgColor, + bgCol: this.theme.bgColor, c:[ - {type: "txt", font: "Anton", label: "", fillx: true, filly: true, col: this.settings.fgColor, id: "seconds"}, + {type: "txt", font: "Anton", label: "", fillx: true, filly: true, col: this.theme.fgColor, id: "seconds"}, ]}, {lazy: true}); }, setLayoutRace: function() { @@ -283,23 +293,23 @@ function Regattatimer() { this.layout = new Layout({ type: "v", - bgCol: this.settings.bgColor, + bgCol: this.theme.bgColor, c: [ - {type: "txt", font: "20%", label: "00:00:00", col: this.settings.fgColor, pad: 4, filly: 1, fillx: 1, id: "racetime"}, - {type: "txt", font: "15%", label: "-", col: this.settings.fgColor, pad: 4, filly:1, fillx:1, id: "daytime"}, + {type: "txt", font: "20%", label: "00:00:00", col: this.theme.fgColor, pad: 4, filly: 1, fillx: 1, id: "racetime"}, + {type: "txt", font: "15%", label: "-", col: this.theme.fgColor, pad: 4, filly:1, fillx:1, id: "daytime"}, // horizontal {type: "h", c: [ - {type: "txt", font: "10%", label: this.translate("speed"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, - {type: "txt", font: "20%", label: "0", col: this.settings.fgColor, pad:4, fillx:1, filly:1, id: "speed"}, - {type: "txt", font: "10%", label: this.translate("speed_unit"), col: this.settings.fgColor, pad:4, fillx:1, filly:1}, + {type: "txt", font: "10%", label: this.translate("speed"), col: this.theme.fgColor, pad:4, fillx:1, filly:1}, + {type: "txt", font: "20%", label: "0", col: this.theme.fgColor, pad:4, fillx:1, filly:1, id: "speed"}, + {type: "txt", font: "10%", label: this.translate("speed_unit"), col: this.theme.fgColor, pad:4, fillx:1, filly:1}, ]}, {type: "h", c: [ - {type:"img", pad: 2, src: this.icons.satellite()}, - {type: "txt", font: "10%", label: "0", col: this.settings.fgColor, pad: 2, filly:1, id: "satellites"}, + {type:"img", pad: 2, src: this.theme["icons"].satellites()}, + {type: "txt", font: "10%", label: "0", col: this.theme.fgColor, pad: 2, filly:1, id: "satellites"}, // hacky, use empty element with fillx to push the other elments to the left an right side {type: undefined, pad: 2, fillx: 1}, - {type:"img", pad: 2, src: this.icons.battery()}, - {type: "txt", font: "10%", label: "-", col: this.settings.fgColor, pad: 2, filly: 1, id: "battery"}, + {type:"img", pad: 2, src: this.theme["icons"].battery()}, + {type: "txt", font: "10%", label: "-", col: this.theme.fgColor, pad: 2, filly: 1, id: "battery"}, ]} ]}, {lazy: true}); } diff --git a/apps/regattatimer/icon.png b/apps/regattatimer/icon.png index eb6057305876b0d72d3a8e7f3d52e440a579aaf0..47712c7ed12a2f1f18797165d6b063069aa6f7b0 100644 GIT binary patch literal 2990 zcmV;f3sLlmP)G2jPhz_@@$zlaKgJDT{(B`yep zChl7_F42Iv#tn>LR6syX+!uU4p2&l!DBv!mvI(P6Fv5%`ir|9LLD2K^dv)t|-JZVP z_jXs`duN^`CtRw#PuKZ(EvNqHR3Vb$kE%4N%1Bk2q$>YZm9?s}MOFS#6|Hmmqfpm1 zg}P(I2vzBk28&988>lK%RHeNRfKboUrYe(Fr8y7a2B^v`RoPYthHB1&jWblGzc28{ zsmkU$Fifi~Kd8#sEWq_rmDzP*h}KwUf<>j^^;MOHbx?V&6IlQzm9)IS!P8D4E>e}= zHTdZ_4r`-$$kr)clqM)*zH(4nGZ#)#g2yLqOqt?Dgt zMiO}A^RC2>9ip$jCVJ_mq6Zux%4@U8;K8DoT_(C{k?5a)Rt3Z{rNC=cl}&lQc*6$K z2@^z{nu-CSb)9~?=&iSkKJ$#|M<0oP^_A$BEu!1D8R7T(_2wG)aPMiSnP**H&CTW= zwCWrI+#KoR6#P8zFZ|*Q(TgrJUC|nP;DMrd-!1ydC)rvJt@-1RMen*xbjXlEKkvHa z645We%niipVep!LJITNNB6|JxqCI;Wqxk#$^F=@SAivh}{`;cmofo{bS1-{UZxr3S z)%&}^YLE-y6rV4cJ2&X67<2jMrd!6fq}j7Y*R3lqKk6vaV~!~_(NrN93!dev1yS33G=(N(LGpIf%f+|MlRmIL0g#}=239&PSz zX-U?*a;5nQwm#QfBihm74M6yS1}U&AUM7j5Lrq0@*+uk`M@0YnD|$@CC3*DG;ubi4 zy6EcF$?xGFbn}cYLl-)u#ysjUZJMbQbvyiUukO!qDbP8Y6s=urx(O9zFz3scx|bG- zG56l<&4~X_NPYUme}EM$jLSnE2MrQ!ZT0f*i2yh^i;`u_MEmtK6&p25^yi<8ufO${ z=+dRdW!ty=1Fw`$-pRY&vY&nuJ>djX*T%;3hJgh6hZKm#)Ge>E(NyTHv!chBU3V4T zV-Np;CCP5PMZcGE`ph!}5Hq$!+I%{yiZQRbIRMWclU$iINA$%Ps}Eq@d#URgX9V3j z!;n?^>IdW`TYk|d~htUf0bG-ow%gdL6&#SeOnzM`_ zZ*0B!W={Yz?c15xB}3u&R+T|j)w^H=Ezkb@z4w9{xg54U5y0194`_I|eEGfRFpv(x#crv_@$(3CD<@?W^vh_cN&|oxWWT~v^114 z&DYvkt&&)VJlyLl29VhoMbJW}*cR=Eg00w$?MX~cEP&5FXOs}PwPg&fgd@B1*ry@E z7Q&dcf)cU;Y}sd@iH;i=#2cM z@WS!{q?>c!zWZhX&_>%fZ;rnH%{RexKY6n886SASPb$TePa55{>b`xe58$Rv=Dz;@ zGXQ84f*8o7(k)tAg6?IhE`A?!NVW_BDo@3E4>Mym01T&*P=D@&t@a9_n{Kguxfy(^ z6s_*N?@Zi*XC8mNvpzd8yQv>h@4esx^X@fk%)gTWgsyk>#*w0>01T&*cmXh}3u}a2 zeYJV!h8sFxgN$Gxw{rjeo$q<)9nqb3G9SX;XIu8!XPd=-_?c&)E!O+eOO*qdOBYU> zM3yc!1gqQHFo0+9qsj%{l~-RiGoLFxdKjGtA1wOHE9vy6okU!sp<)0JJ1pLfnK#c| z-)k?!T#&kJTkV@~W;4_cWWoBNGphAfBES7+S~0q=y*7TFwr&hD`6Sjqya!^~T0c0xyN=WFoWw;eI@9xCT zY~OBda>;~KPBFG8MH~O7voxO0YH!Z~AY$olD6Cqy6s?lj3FM%IlJ89f(7GjTB?wdc zQ>N73*sOvL;WQOH_T!*u29O;DxP@nR1EGO*f2N_U23 zR$E<0Qh2=!Hn_4AftCM<)Q)xQ%(@!_ zA!+ul2*9|+o5hQ}UUn9B4QFS?B~W<*kd_BC%ok$MML0Wa&YZ5DorRl552L5WZA(iH zAi~ByzR>XDRok1Z)evkMF~Zn3abji9RsZu9Tc{I3s_+_~uFIqWi93IO<$6RHDF-@ zJVLN82l(TUfzy25b?Nk`+Gs;1V2VY|-ZM6>*lWa3>XE6TPHt?6Iv;mjFs8fLD&1(4 zYO5E%;h;hQiM?l~K(oACwE^Rur91<7s~2I5v>c<|>Xmx4VQXu!*$@!jmS}}k3Z&Wq zmU=IBwBQjia??4|ZZ^!c-L<790Fk|Cj4SSSww#iHRPF7qyfeJrmAct(3wSW;KDN7N z-f-R88gys)!?6GnzHFD%3Cp%WW@DD3D6 zTpR@NzMRxxx{}>@oOEi3-7H;B_oLMvd8D}?KMz+W%@3t$9G){7Y+?VUYJ6pG!92J|I1_{9? z7J}n>Y*a)iWGH-|1rB0>wQZ%#vofSZRhq)57jf*Rcf-~f4#9Dceqy2_-byxn7N?tM z2>=npuoHwGgOd*X$tY8!&d%zW=iy^*ZD#uvJAmBG0DLtPx?zCH-U3uWC!xt6KE`j# zxJLKoUY>!Ha&^&FxmGS2n&D{B=<`)MGc?B+E5g`C-VBR-n{Jy$&_VPJVz){Z+vub- z1UWNQt0nF2W-9^N@MFgsR|uDcBqe(QBflx*8o4TTf1dH^j+86YV!?9`+5T6RE2dpQ z{;euK69IG%pcp)I03d=q(YiF%5!KV87hJq3ShGgZySIDJE?oo%e+~%#{7GSpo%A2&*s+4|-vv8&W={Z6YmFaot~GhG+aD|f$Bx<8 z=Kg*4tXu?;_lfS^&Gl$oD1o*%l@;}u_U#Myi>p_&BLE2Y>@nA?s9 z)s^y|Q~&hqr=ATSoW0U)Z@2RVZ44wZZ=N}riN-(a+qZ)2*9D(He`jkv@;GtAv}wVD z00OUHn?jDMzO}VHMj|3(_v%%!i>lzw8wUY8q|S8h+R7V`JXWtZZEI|F5TL>OAd0R^ z8;(3KTrh20zurM$=1g-Cm1Ct1M;_0fnYPWEp=Oq*1ap*a~b%k`ym?3!ivhe@Yrv>%(O22r&ZQ3N*xKVKFk`jlMBwk+O;v8RJ z@6P5f0wYF*T%jl@<}NK>tbU^+96sE=b2=_xE*Lp_v|z`M=ywo6!0*KU`}>^O+g1ID zUc9h{75g}|e`kjm7%s3Z)lVl1wY7prkBSVVaS|Di%{#hs;{5p-iOer0*!n29p@>=NrQf3ag$ObAR1&CPaEfBd*T&LY4> zMa8MV$V?hH38jIS7Qwi2rVZ?JyF$3LvBf45n@Rcv)H9`gh=_|^POxyHxi)QKv#sRT zB2&C4qO#ig8b@pK@ZsQh0trxmu(9f&jMpg(pJ;BjuMNR=f8y>m{edlR|D<#-MyE7F zY%V9*f4$pwSc2atN@}lrBwQ!q<%yFglVxna)6ihYd41Lc`e65f z$}#M_8FdV`jV=$w=dfXSJ~08})-6?yxfb?cF%}>UeFXpQx+3BO#|A~4R0t4^#q1KZ zP>(VwE~=_jl1d`gr4;9*E@5ab_VcW#DyyqiK_?=$58X+@UL_4-p}_{3*TL4oGLV%N tos)i`OJe+A|JZ{a?5w-klI7&(z<-M4p~-}(t@Qu^002ovPDHLkV1m1_q4fX& diff --git a/apps/regattatimer/regattatimer.json b/apps/regattatimer/regattatimer.json index d8ed2f2ac..e2ea78f76 100644 --- a/apps/regattatimer/regattatimer.json +++ b/apps/regattatimer/regattatimer.json @@ -4,7 +4,5 @@ "dial": "Numeric", "gps": true, "record": false, - "theme": "Dark", - "fgColor": "#FFFF00", - "bgColor": "#000000" + "theme": "Dark" } diff --git a/apps/regattatimer/screenshot-6.png b/apps/regattatimer/screenshot-6.png index eaa306ba4083fa2cf9e704ce68c2258ebfa1ee2d..391eaafd216c0bfbda0df0f576fa6517b8e6f66b 100644 GIT binary patch delta 2754 zcmV;z3O)6!7S|P!F@KRsL_t(|UhSRRlH4i`MKk~Z(K+Sm_%uxeTLNrzS$Rp-a4~G8 z)v|2E&!0bk{{AukJ1hPe>r)K{u2$f%#jF8aEgOTrdYR}yr$Ewxt%e(SiXmfYcPsE^ zz_rGE4jd!CdkOcx-iHCV8ZSn?w=q32`~MBNm+@l6Pb}lU*MG!-_g(uXX8voy7W$sC zt)(s$Ucxq}n1soI;~O)p5pzQ~g$+zSNNK>e#(SuO0Z(lkQw+Xnz%9m$0Z+01(+~3J zZ{|sym!7$>!rRaEiZX#)6~KURm79z8R)7$qx1#iSs{-7_-6}U1>#cw^;MC0+X|&$U zK!3Fgm<%{HVt=dgl7*;U654qb;8C(iE$R-Z00unVk}x2T0=!P?QH#36DZmTu!z~E| z@+iP3MIN=NJDdW1)H&ReFd&ZtMFWmI;wA8&r0ha;aF(9si0v-ht27GY#{4@W* zUe-g0@pO2ZIRy5&J9URspqjvcGhfpE{JsGE>Q;Sh_kZ(S`IB&#ej(xU96Co@=Juv4< zVY#PY2s}Q4)q1L!TDPwxaEYohS$o#?dM@>!RY0x18lAO$cLIl=)sw)=V<{giTcQbC z9&m5p_f@bPI)fnV<^zNp4Rq{5jf>xirB=yP|FuhnY{K@qzvrY_*NTO3++y*7n^A?8!n{i3*wLA@7s~&c(e}FS7V_&%P|zqfVOw zt?OlNKLvpgQn$ubWGU{=Qnap@Zhui0;;PJo)qf+~xIyb)3bd}5wf!&zRvxFyA?&?` zReHCsmu_E4;M{u*slQvj==AzreQ!U#C_+E=3bd}7wY{6b@m*#-#njtq<+qA>8_k-H znW>&8=tFtndY`1_P6}8FdV3yN#Xgje6^lN#9_k^M)tSRz^v_?Qh8Wm-aq8`@K&ug} zcYlEob*p3Gr#zeMLiq6X6~2kv8 z3n)MgObkp6ynrJ!g-I1Cq|s)Hg&5~d??|-YV|;oVdEiOO!?3xD!v+>m00J+th&xXK z1KxS{7nnN+yuc#vJO#24OD z^nB(#h5`^60z=?2e34P?sDPWm)=#C6@A=ifzNu4+)FPby=JyMDznymUAC5!!J>NdO zHE(yR^V>W-Db-xR&u*igvJj;mSk&$;wL4j=evhfu_N|P3h4Pj)>otGN8d|R9CVz15 zWX%*2YY1#5>p8j7b&oohUxK~fr+uHGYB|5JmawW_>)vVqum8WV8ZE}2tkdGnwUms* zm~aP|R9P=?uNG;rhDr}r-eWqwt}!)%!-HgE(uN+cehDD(XY3#ARf?HQyOHMF#p@R9 zZ(Fa0V&IUvCyi_A-h(cq_o3*g%74%K#q~Uz_u313dIE?06^Ah$1kQges0gRZLt>_M ze&3(uvpT65*mK=mV_-!j@0oHILZoPr8;4~VL^X$*%9VbqJe+GzXQgS0-X1NNA#f=> z;`8kFzj9W3q{`!)NkGYZmF-H9IaHY^hmv7840x8yANy5w^JVY zO7Eg?6i^C4;0^P@YY-U%E9a(s0tFy21m>&c2^^InE~)?ohQJVbQRik_6DR0Eq6)A>I|QDLz@MJT85xU`X_skXz`LyUqVvRn7hTw0riB6TvVYQx&JzP(bYXXy z7Fh_g5M&{C@pk4po&r3HgTRv!m~-OB!w4g)RsaG+UDRLBBKu@~p3|q+vs7Nz`_auiFJNa8CkzXlKccyYC+O7QCfM53-se6ginH~9imkVOx76N;S zcV}JCwIFdm2399kS?*C%b%r6Z=Putv#vc88uD2U$bMsAw1o1tEIxXq(EJP`f>&;k% z-shoWuezRoAo2S>m{^NkJbXKWEqUPht+e!HK4T-b_ndfnrV*n@+D1ZTD4pS?D0Z+Yr3?2i<2Bua31D<;M7(7`B zvJhk;-~b9dr2tRjAn;@a=A5{vV8L}SPyhl$UoFm=u7(5EufRT!l&~aDf7D z0;^A@)qbz3IUJu)*Yum)zc;mmPJa8&V?_1);D7RsVE!9H%id=tn;!n0xNz{6UDo4$ zwbYJ>sy_RjU4X=YT`C}1udMcls3vfabK*Sbz=gX^sy)g)=frt-uY}b0U#eDJb^DhK zxZj0P)C$X0T`Pf)dY*o3>sqNmJ*|Le?Nhy3+iy=`V%Tf;xzGe{JCE=*`Y8zP-AmX* zV1LaLYSbuTC1|d1Yy05{oSRVX`E#u#fY5b>Zq;44zcB{R8|C*IZK=D?-ULqFA*;LH z8Vc>)#!ArT2;3vPK^DTEEx76zMiX?S7?>=?d@P5+(K`y{f$L(?s|c(5g}@PXJeh^s zy7O^v+z~@y2t4&W8dDP3n=PyFE`xkffTfj#+JCB64;G|%~S@tMi- zSn_A_&J}a2nDp-QVF|2^k#ZD>vqm+FSPAZa5_h*JbGY7DviY4tUOpv|5N zv>LJH=_mx|o484{9hL7w?8y>lJ*ol_7y?7!QN5FKt*-zChQJVbeLrW0qbdM_Av-Vx z9@RS;*ZK;uLpubXjKH5x>HaEteV=BAGbw-p&$L>M^-F>O0F6z3mb2QZuK)l507*qo IM6N<$g1szb*Z=?k literal 2859 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 diff --git a/apps/regattatimer/settings.js b/apps/regattatimer/settings.js index c96bc4771..cdcdf53f5 100644 --- a/apps/regattatimer/settings.js +++ b/apps/regattatimer/settings.js @@ -15,8 +15,6 @@ "gps": true, "record": false, "theme": "Dark", - "fgColor": "#FFFF00", - "bgColor": "#000000" }, storage.readJSON(file, true) || {}); function save(key, value) { @@ -77,4 +75,4 @@ } }, }); -}) +})(load) From 258ad275e44d47512b98b7de85c75d23d683765b Mon Sep 17 00:00:00 2001 From: naden Date: Sat, 2 Mar 2024 19:23:00 +0100 Subject: [PATCH 28/40] Add files via upload --- README.md | 598 ++++------------------------------------------ app-icon.js | 1 + app.js | 335 ++++++++++++++++++++++++++ icon.png | Bin 0 -> 2990 bytes regattatimer.json | 8 + screenshot-6.png | Bin 0 -> 2775 bytes settings.js | 78 ++++++ 7 files changed, 471 insertions(+), 549 deletions(-) create mode 100644 app-icon.js create mode 100644 app.js create mode 100644 icon.png create mode 100644 regattatimer.json create mode 100644 screenshot-6.png create mode 100644 settings.js diff --git a/README.md b/README.md index ed6a501ef..8d906e1d7 100644 --- a/README.md +++ b/README.md @@ -1,574 +1,74 @@ -Bangle.js App Loader (and Apps) -================================ +# Regatta Timer 5-4-1 countdown -[![Build Status](https://github.com/espruino/BangleApps/actions/workflows/nodejs.yml/badge.svg)](https://github.com/espruino/BangleApps/actions/workflows/nodejs.yml) +## Modes -* Try the **release version** at [banglejs.com/apps](https://banglejs.com/apps) -* Try the **development version** at [espruino.github.io](https://espruino.github.io/BangleApps/) +* **Idle** + On startup the application is in idle mode showing a large 5 in the centre of the screen and the time of day below. + `Button` switches to start mode. +* **Start** + During the countdown, the screen changes the layout several times to use as much space as + possible to display the numbers. + When time is up the buzzer sounds and the application switches to race mode. + `Button` switches to idle mode. +* **Race** + Race time, local time, SOA, number reachable GPS satellites and battery level are shown. + `Button` switches to "stopped mode". +* **Stoped** + The race counter stops. + `Button` switches to idle mode. -**All software (including apps) in this repository is MIT Licensed - see [LICENSE](LICENSE)** By -submitting code to this repository you confirm that you are happy with it being MIT licensed, -and that it is not licensed in another way that would make this impossible. +## Screenshots -## How does it work? +*Idle mode: showing a big 5 and time of day below* -* A list of apps is in `apps.json` (this is auto-generated from all the `apps/yourapp/metadata.json` using Jekyll or `bin/create_apps_json.sh`) -* Each element references an app in `apps/` which is uploaded -* When it starts, BangleAppLoader checks the JSON and compares -it with the files it sees in the watch's storage. -* To upload an app, BangleAppLoader checks the files that are -listed in `apps.json`, loads them, and sends them over Web Bluetooth. +![Idle mode: showing a big 5 and time of day below](screenshot-1.png) -## Getting Started +*Start mode: minutes and seconds* -Check out: +![Start mode: minutes and seconds](screenshot-2.png) -* [Building your first Bangle.js Application](https://www.espruino.com/Bangle.js+First+App) -* [Adding an app to the Bangle.js App Loader](https://www.espruino.com/Bangle.js+App+Loader) -* [Customising the App Loader](https://www.espruino.com/Bangle.js+App+Loader+Custom) +*Start mode: seconds* -## What filenames are used +![Start mode: seconds](screenshot-3.png) -Filenames in storage are limited to 28 characters. To -easily distinguish between file types, we use the following: +*Race mode: elapsed time, time of day, speed, satellites, battery* -* `stuff.info` is JSON that describes an app - this is auto-generated by the App Loader -* `stuff.img` is an image -* `stuff.app.js` is JS code for applications -* `stuff.wid.js` is JS code for widgets -* `stuff.settings.js` is JS code for the settings menu -* `stuff.boot.js` is JS code that automatically gets run at boot time -* `stuff.json` is used for JSON settings for an app +![Race mode: elapsed time, time of day, speed, satellites, battery](screenshot-4.png) -## Developing your own app +*Race mode: with german abbreviations* -* Head over to [the Web IDE](https://www.espruino.com/ide/) and ensure `Save on Send` in settings set to the *default setting* of `To RAM` -* We'd recommend that you start off using code from 'Example Applications' (below) to get started... -* Load [`app.js`](apps/_example_app/app.js) or [`widget.js`](apps/_example_widget/widget.js) into the IDE and start developing. -* The `Upload` button will load your app to Bangle.js temporarily +![Race mode: with german abbreviations](screenshot-5.png) -## Adding your app to the menu +*Settings page: main* -* Come up with a unique (all lowercase, no spaces) name, we'll assume `myappid`. Bangle.js -is limited to 28 char filenames and appends a file extension (eg `.js`) so please -try and keep filenames short to avoid overflowing the buffer. -* Create a folder called `apps/`, lets assume `apps/myappid` -* We'd recommend that you copy files from one of the Examples in `apps/_example_*` (see below), or... -* `apps/myappid/app.png` should be a 48px icon -* Use http://www.espruino.com/Image+Converter to create `apps/myappid/app-icon.js`, using a 1 bit, 4 bit or 8 bit Web Palette "Image String" -* Create/modify `apps/myappid/metadata.json` as follows: +![Settings page: main](screenshot-6.png) -``` -{ "id": "myappid", - "name": "My app's human readable name", - "shortName" : "Short Name", - "icon": "app.png", - "description": "A detailed description of my great app", - "tags": "", - "storage": [ - {"name":"myappid.app.js","url":"app.js"}, - {"name":"myappid.img","url":"app-icon.js","evaluate":true} - ], -}, -``` +*Settings page: choose the theme* -### Screenshots +![Settings page: choose the theme](screenshot-7.png) -In the app `metadata.json` file you can add a list of screenshots with a line like: `"screenshots" : [ { "url":"screenshot.png" } ],` +## Localization -To get a screenshot you can: +Localization is done by the Bangle.js 2 app "Languages" +* Go to [banglejs.com/apps](https://banglejs.com/apps/) +* Search for app "Languages" +* Click the "arrow up" or "burger" icon +* Choose your language from the dropdown +* Click `upload` -* Type `g.dump()` in the left-hand side of the Web IDE when connected to a Bangle.js 2 - you can then -right-click and save the image shown in the terminal (this only works on Bangle.js 2 - Bangle.js 1 is -unable to read data back from the LCD controller). -* Run your code in the emulator and use the screenshot button in the bottom right of the window. +**Some nautical abbreviations which are not part of the Bangle.js 2 app "Languages" app are stored in `translations.json`.** +## Feedback -## Testing +Report bugs or request a feature at [github.com/naden](https://github.com/naden) -### Online +## Roadmap +* add a seconds coundown layout; mimic a classic regatta chronograph +* add recording of gps course and race time +* add icons for light mode +* add flag icons -This is the best way to test... +## Created by +© 2021 - 2024 [naden.de](https://naden.de) -* Fork the https://github.com/espruino/BangleApps git repository -* Add your files -* Go to GitHub Settings and activate GitHub Pages -* Run your personal `Bangle App Loader` at https://\.github.io/BangleApps/index.html to load apps onto your device -* Your apps should be inside it - if there are problems, check your web browser's 'developer console' for errors - -**Note:** It's a great idea to get a local copy of the repository on your PC, -then run `bin/sanitycheck.js` - it'll run through a bunch of common issues -that there might be. To get the project running locally, you have to initialize and update the git submodules first: `git submodule update --init`. - -Be aware of the delay between commits and updates on github.io - it can take a few minutes (and a 'hard refresh' of your browser) for changes to take effect. - -### Offline - -Using the 'Storage' icon in [the Web IDE](https://www.espruino.com/ide/) -(4 discs), upload your files into the places described in your JSON: - -* `app-icon.js` -> `myappid.img` - -Now load `app.js` up in the editor, and click the down-arrow to the bottom -right of the `Send to Espruino` icon. Click `Storage` and then either choose -`myappid.app.js` (if you'd uploaded your app previously), or `New File` -and then enter `myappid.app.js` as the name. - -Now, clicking the `Send to Espruino` icon will load the app directly into -Espruino **and** will automatically run it. - -When you upload code this way, your app will even be uploaded to Bangle.js's menu -without you having to use the `Bangle App Loader` - -**Note:** Widgets need to be run inside a clock or app, so if you're -developing a widget you need to go go `Settings` -> `Communications` -> `Load after saving` -and set it to `Load default application`. - -## Example Applications - -To make the process easier we've come up with some example applications that you can use as a base -when creating your own. Just come up with a unique name (ideally lowercase, under 20 chars), copy `apps/_example_app` -or `apps/_example_widget` to `apps/myappid`, and edit `apps/myappid/metadata.json` accordingly. - -**Note:** the max filename length is 28 chars, so we suggest an app ID of under -20 so that when `.app.js`/etc gets added to the end the filename isn't cropped. - -**If you're making a widget** please start the name with `wid` to make -it easy to find! - -### App Example - -The app example is available in [`apps/_example_app`](apps/_example_app) - -Apps are listed in the Bangle.js menu, accessible from a clock app via the middle button. - -* `metadata.json` - describes the app to bootloader and loader -* `app.png` - app icon - 48x48px -* `app-icon.js` - JS version of the icon (made with http://www.espruino.com/Image+Converter) for use in Bangle.js's menu -* `app.js` - app code -* `ChangeLog` - A file containing a list of changes to your app so users can see what's changed - -#### `app-icon.js` - -The icon image and short description is used in Bangle.js's launcher. - -Use the Espruino [image converter](https://www.espruino.com/Image+Converter) and upload your `app.png` file. - -Follow this steps to create a readable icon as image string. - -1. upload a 48x48 png file - THE IMAGE SHOULD BE 48x48 OR LESS -2. set _X_ Use Compression -3. set _X_ Transparency (optional) -4. set Diffusion: _flat_ -5. set Colours: _1 bit_, any of the Optimised options, or _8 bit Web Palette_ are best -6. set Output as: _Image String_ - -Replace this line with the image converter output: - -``` -require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA==")) -``` - -**Do not add a trailing semicolon** - -You can also use this converter for creating images you like to draw with `g.drawImage()` with your app. - -Apps that need widgets can call `Bangle.loadWidgets()` **once** at startup to load -them, and then `Bangle.drawWidgets()` to draw them onto the screen whenever the app -has call to completely clear the screen. Widgets themselves will update as and when needed. - -### Widget Example - -The widget example is available in [`apps/_example_widget`](apps/_example_widget) - -* `metadata.json` - describes the widget to bootloader and loader -* `widget.js` - widget code - -Widgets are just small bits of code that run whenever an app that supports them -calls `Bangle.loadWidgets()`. If they want to display something in the 24px high -widget bar at the top of the screen they can add themselves to the global -`WIDGETS` array with: - -``` -WIDGETS["mywidget"]={ - area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right) - sortorder:0, // (Optional) determines order of widgets in the same corner - width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout - draw:draw // called to draw the widget -}; -``` - -When the widget is to be drawn, `x` and `y` values are set up in `WIDGETS["mywidget"]` -and `draw` can then use `this.x` and `this.y` to figure out where it needs to draw to. - - -### ChangeLog - -This is a file containing a list of changes to your app so users can see what's changed, for example: - -``` -0.01: New App! -0.02: Changed the colors -0.03: Made the app run quicker -``` - -Entries should be newest last, with the version number of the last entry matching the version in `metadata.json` - -Please keep the same format at the example as the file needs to be parsed by the BangleApps tools. - -### `app.info` format - -This is the file that's **auto-generated** from `metadata.json` and loaded onto Bangle.js by the App Loader, -and which gives information about the app for the Launcher. - -``` -{ - "name":"Short Name", // for Bangle.js menu - "icon":"*myappid", // for Bangle.js menu - "src":"-myappid", // source file - "type":"widget/clock/app/bootloader/...", // optional, default "app" - // see 'type' in 'metadata.json format' below for more options/info - "version":"1.23", - // added by BangleApps loader on upload based on metadata.json - "files:"file1,file2,file3", - // added by BangleApps loader on upload - lists all files - // that belong to the app so it can be deleted - "data":"appid.data.json,appid.data?.json;appidStorageFile,appidStorageFile*" - // added by BangleApps loader on upload - lists files that - // the app might write, so they can be deleted on uninstall - // typically these files are not uploaded, but created by the app - // these can include '*' or '?' wildcards -} -``` - -### `metadata.json` format - -``` -{ "id": "appid", // 7 character app id - "name": "Readable name", // readable name - "shortName": "Short name", // short name for launcher - "version": "0v01", // the version of this app - "description": "...", // long description (can contain markdown) - "icon": "icon.png", // icon in apps/ - "screenshots" : [ { "url":"screenshot.png" } ], // optional screenshot for app - "type":"...", // optional(if app) - - // 'app' - an application - // 'clock' - a clock - required for clocks to automatically start - // 'widget' - a widget - // 'module' - this provides a module that can be used with 'require'. - // 'provides_modules' should be used if type:module is specified - // 'bootloader' - an app that at startup (app.boot.js) but doesn't have a launcher entry for 'app.js' - // 'settings' - apps that appear in Settings->Apps (with appname.settings.js) but that have no 'app.js' - // 'clkinfo' - Provides a 'myapp.clkinfo.js' file that can be used to display info in clocks - see modules/clock_info.js - // 'RAM' - code that runs and doesn't upload anything to storage - // 'launch' - replacement 'Launcher' - // 'textinput' - provides a 'textinput' library that allows text to be input on the Bangle - // 'scheduler' - provides 'sched' library and boot code for scheduling alarms/timers - // (currently only 'sched' app) - // 'notify' - provides 'notify' library for showing notifications - // 'locale' - provides 'locale' library for language-specific date/distance/etc - // (a version of 'locale' is included in the firmware) - "tags": "", // comma separated tag list for searching - // common types are: - // 'clock' - it's a clock - // 'widget' - it is (or provides) a widget - // 'outdoors' - useful for outdoor activities - // 'tool' - a useful utility (timer, calculator, etc) - // 'game' - a game - // 'bluetooth' - uses Bluetooth LE - // 'system' - used by the system - // 'clkinfo' - provides or uses clock_info module for data on your clock face or clocks that support it (see apps/clock_info/README.md) - // 'health' - e.g. heart rate monitors or step counting - "supports": ["BANGLEJS2"], // List of device IDs supported, either BANGLEJS or BANGLEJS2 - "dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above) - "dependencies" : { "messages":"app" } // optional, depend on a specific app ID - // for instance this will use notify/notifyfs is they exist, or will pull in 'notify' - "dependencies" : { "messageicons":"module" } // optional, depend on a specific library to be used with 'require' - see provides_modules - "dependencies" : { "message":"widget" } // optional, depend on a specific type of widget - see provides_widgets - "provides_modules" : ["messageicons"] // optional, this app provides a module that can be used with 'require' - "provides_widgets" : ["battery"] // optional, this app provides a type of widget - 'alarm/battery/bluetooth/pedometer/message' - "default" : true, // set if an app is the default implementer of something (a widget/module/etc) - "readme": "README.md", // if supplied, a link to a markdown-style text file - // that contains more information about this app (usage, etc) - // A 'Read more...' link will be added under the app - - "custom": "custom.html", // if supplied, apps/custom.html is loaded in an - // iframe, and it must post back an 'app' structure - // like this one with 'storage','name' and 'id' set up - // see below for more info - - "customConnect": true, // if supplied, ensure we are connected to a device - // before the "custom.html" iframe is loaded. An - // onInit function in "custom.html" is then called - // with info on the currently connected device. - - "interface": "interface.html", // if supplied, apps/interface.html is loaded in an - // iframe, and it may interact with the connected Bangle - // to retrieve information from it - // see below for more info - - "allow_emulator":true, // if 'app.js' will run in the emulator, set to true to - // add an icon to allow your app to be tested - - "storage": [ // list of files to add to storage - {"name":"appid.js", // filename to use in storage. - // If name=='RAM', the code is sent directly to Bangle.js and is not saved to a file - "url":"", // URL of file to load (currently relative to apps/) - "content":"...", // if supplied, this content is loaded directly - "evaluate":true, // if supplied, data isn't quoted into a String before upload - // (eg it's evaluated as JS) - "noOverwrite":true // if supplied, this file will not be overwritten if it - // already exists - "supports": ["BANGLEJS2"]// if supplied, this file will ONLY be uploaded to the device - // types named in the array. This allows different versions of - // the app to be uploaded for different platforms - }, - ] - "data": [ // list of files the app writes to - {"name":"appid.data.json", // filename used in storage - "storageFile":true // if supplied, file is treated as storageFile - "url":"", // if supplied URL of file to load (currently relative to apps/) - "content":"...", // if supplied, this content is loaded directly - "evaluate":true, // if supplied, data isn't quoted into a String before upload - // (eg it's evaluated as JS) - }, - {"wildcard":"appid.data.*" // wildcard of filenames used in storage - }, // this is mutually exclusive with using "name" - ], - "sortorder" : 0, // optional - choose where in the list this goes. - // this should only really be used to put system - // stuff at the top -} -``` - -* name, icon and description present the app in the app loader. -* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher`, `bluetooth` or empty. -* storage is used to identify the app files and how to handle them -* data is used to clean up files when the app is uninstalled - -### `metadata.json`: `custom` element - -Apps that can be customised need to define a `custom` element in `metadata.json`, -which names an HTML file in that app's folder. - -When `custom` is defined, the 'upload' button is replaced by a customize -button, and when clicked it opens the HTML page specified in an iframe. - -In that HTML file you're then responsible for handling a button -press and calling `sendCustomizedApp` with your own customised -version of what's in `metadata.json`: - -``` - - - - - -

- - - - -``` - -This'll then be loaded in to the watch. See [apps/qrcode/grcode.html](the QR Code app) -for a clean example. - -**Note:** we specify a `url` for JS files even though it doesn't have to exist -and will never be loaded. This is so the app loader can tell if it's a JavaScript -file based on the extension, and if so it can minify and pretokenise it. - -### `metadata.json`: `interface` element - -Apps that create data that can be read back can define a `interface` element in `metadata.json`, -which names an HTML file in that app's folder. - -When `interface` is defined, a `Download from App` button is added to -the app's description, and when clicked it opens the HTML page specified -in an iframe. - -``` - - - - - - -
Loading...
- - - -``` - -When the page is ready a function called `onInit` is called, -and in that you can call `Puck.write` and `Puck.eval` to get -the data you require from Bangle.js. - -See [apps/gpsrec/interface.html](the GPS Recorder) for a full example. - -### Adding configuration to the "Settings" menu - -Apps (or widgets) can add their own settings to the "Settings" menu under "App/widget settings". -To do so, the app needs to include a `settings.js` file, containing a single function -that handles configuring the app. -When the app settings are opened, this function is called with one -argument, `back`: a callback to return to the settings menu. - -Usually it will save any information in `myappid.json` where `myappid` is the name -of your app - so you should change the example accordingly. - -Example `settings.js` -```js -// make sure to enclose the function in parentheses -(function(back) { - let settings = require('Storage').readJSON('myappid.json',1)||{}; - if (typeof settings.monkeys !== "number") settings.monkeys = 12; // default value - function save(key, value) { - settings[key] = value; - require('Storage').write('myappid.json', settings); - } - const appMenu = { - '': {'title': 'App Settings'}, - '< Back': back, - 'Monkeys': { - value: settings.monkeys, - onchange: (m) => {save('monkeys', m)} - } - }; - E.showMenu(appMenu) -}) -``` -In this example the app needs to add `myappid.settings.js` to `storage` in `metadata.json`. -It should also add `myappid.json` to `data`, to make sure it is cleaned up when the app is uninstalled. -```json - { "id": "myappid", - ... - "storage": [ - ... - {"name":"myappid.settings.js","url":"settings.js"} - ], - "data": [ - {"name":"myappid.json"} - ] - }, -``` - -## Modules - -You can include any of [Espruino's modules](https://www.espruino.com/Modules) as -normal with `require("modulename")`. To include [Bangle's modules](modules) for use in the Web -IDE, [upload the modules to internal storage](modules#upload-the-module-to-the-bangles-internal-storage) -or [change the IDE's search path](modules#change-the-web-ide-search-path-to-include-banglejs-modules). -If you want to develop your own module for your -app(s) then you can do that too. Just add the module into the `modules` folder -then you can use it from your app as normal. - -You won't be able to develop apps using your own modules with the IDE, -so instead we'd recommend you write your module to a Storage File called -`modulename` on Bangle.js. You can then develop your app as normal on Bangle.js -from the IDE. - -## Coding hints - -- use `g.setFont(.., size)` to multiply the font size, eg ("6x8",3) : "18x24" - -- use `g.drawString(text,x,y,true)` to draw with background color to overwrite existing text - -- use `g.clearRect()` to clear parts of the screen, instead of using `g.clear()` - -- use `g.fillPoly()` or `g.drawImage()` for complex graphic elements - -- using `g.clear()` can cause screen flicker - -- using `g.setLCDBrightness()` can save you power during long periods with lcd on - -- chaining graphics methods, eg `g.setColor(0xFD20).setFontAlign(0,0).setfont("6x8",3)` - -### Misc Notes - -- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `myappid.json`, then load it at startup. - -- 'Alarm' apps define a file called `alarm.js` which handles the actual alarm window. - -- Locale is handled by `require("locale")`. An app may create a `locale` file in Storage which is -a module that overwrites Bangle.js's default locale. - - -### Graphic areas - -The screen is parted in a widget and app area for lcd mode `direct`(default). - -| areas | as rectangle or point | -| :-:| :-: | -| Widget | (0,0,239,23) | -| Apps | (0,24,239,239) | -| BTN1 | (230, 55) | -| BTN2 | (230, 140) | -| BTN3 | (230, 210) | -| BTN4 | (0,0,119, 239)| -| BTN5 | (120,0,239,239) | - -- Use `g.setFontAlign(0, 0, 3)` to draw rotated string to BTN1-BTN3 with `g.drawString()`. - -- For BTN4-5 the touch area is named - -## Available colors - -You can use `g.setColor(r,g,b)` OR `g.setColor(16bitnumber)` - some common 16 bit colors are below: - -| color-name | color-value| -| :-: | :-: | -| Black | 0x0000 | -| Navy | 0x000F | -| DarkGreen | 0x03E0 | -| DarkCyan | 0x03EF | -| Maroon | 0x7800 | -| Purple | 0x780F | -| Olive | 0x7BE0 -| LightGray | 0xC618 -| DarkGrey | 0x7BEF -| Blue | 0x001F -| Green | 0x07E0 | -| Cyan | 0x07FF | -| RED | 0xF800 | -| Magenta | 0xF81F | -| Yellow | 0xFFE0 | -| White | 0xFFFF | -| Orange | 0xFD20 | -| GreenYellow | 0xAFE5 | -| Pink | 0xF81F | - -## API Reference - -[Reference](http://www.espruino.com/Reference#software) - -[Bangle Class](https://banglejs.com/reference#Bangle) - -[Graphics Class](https://banglejs.com/reference#Graphics) - -## 'Testing' folder - -The [`testing`](testing) folder contains snippets of code that might be useful for your apps. - -* `testing/colors.js` - 16 bit colors as name value pairs -* `testing/gpstrack.js` - code to store a GPS track in Bangle.js storage and output it back to the console - -## Credits - -The majority of icons used for these apps are from [Icons8](https://icons8.com/) - we have a commercial license but icons are also free for Open Source projects. +Icons by [Icons8](https://icons8.com/) diff --git a/app-icon.js b/app-icon.js new file mode 100644 index 000000000..3c6f27c44 --- /dev/null +++ b/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("qFQ4UB8H/AAIJBoGtqoACDZYPDCRwUGqATNgoTDoATNgISCqhtPio6QHgg6OHggJGn+q1X8PJAHFnwSBAAO8CYxiFgeq1/Agf61XAMgpiFnWvAof61hkFCYkD1YhEgfqAwkFOwk62EAhkCwEwgEOFAkBCYgfCnYTB9gvCCZECDwMshgGBmE4GAOACY8KHQIjBAAQTBh2gCYY6EgHwTIsPgA8EAAeogAeDGAcAlQSGgQRBE5EKKAYdDA4wfGYgo6HHgbKFgHrgB3BAA0OBgQAEDQw0Hcog5IHojyEgWwWAgAFncOOAkO4ATLgZbENYIAMJIk7CZo0ElcDCRfA9AFD9A8M0ErFgjlBABXwJQiyMWgyyMQwrGBmASLhjIDUgPs//wBoc8ZAwTEmGqKQiEDh2shATDdwMA16nE1ADCh4dBeAcCAYJlEgQTDYoQPCAYevFwQfBYAgTH0eqFAcKlATEhQTG2GsgeqgE4CYM6LQITHDoUD/8A9fq4E/SYI7HgfwO4fq1RVDCY2wgevT4krfgqLDWYXqI4IACE4cO1XMY4vsn41DgBSBBgX/bYugmD/HAAcMhQgDYogAJhRWFABk6XQkPCRfwnYFD9AtEAA+gSQgEEABPoAgYsEABLTDNAo8KAgakBDQgAFnbCBAwbwBCZbuDZAayMc4i0NWQgAB9cAIYhbEBgQaGHpCDBGg0KKwgAFgQeGA4XwBIr5BD5GoHg46BlQwH9QnJ1YGDoAeDO4MsBYc4O4IwDgITDgRsBlgiBFgITBnRODCYg8BXgM6gWADIMDHQgTFnQ8BhgTBmA6BTokBqDyE1blEgYvCAAUFCYgoB14FD/TEFgtUAwkD1Wv4ED/WqEwkAitVTIs+1QAC3gLFqoTGgE/CQP8BQwTBPAgALgITBMgoAKgoTBMgoAKMQI8QHQQ8QHQQACCZoSEChoPD")) diff --git a/app.js b/app.js new file mode 100644 index 000000000..30d03daed --- /dev/null +++ b/app.js @@ -0,0 +1,335 @@ +/** + * Regatta Timer + */ +const Layout = require("Layout"); +const locale = require("locale").name == "system" ? "en" : require("locale").name.substring(0, 2); +const hs = require("heatshrink"); + +// "Anton" bold font +Graphics.prototype.setFontAnton = function(scale) { + // Actual height 69 (68 - 0) + g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAA/gAAAAAAAAAAP/gAAAAAAAAAH//gAAAAAAAAB///gAAAAAAAAf///gAAAAAAAP////gAAAAAAD/////gAAAAAA//////gAAAAAP//////gAAAAH///////gAAAB////////gAAAf////////gAAP/////////gAD//////////AA//////////gAA/////////4AAA////////+AAAA////////gAAAA///////wAAAAA//////8AAAAAA//////AAAAAAA/////gAAAAAAA////4AAAAAAAA///+AAAAAAAAA///gAAAAAAAAA//wAAAAAAAAAA/8AAAAAAAAAAA/AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////AAAAAB///////8AAAAH////////AAAAf////////wAAA/////////4AAB/////////8AAD/////////+AAH//////////AAP//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wA//8AAAAAB//4A//wAAAAAAf/4A//gAAAAAAP/4A//gAAAAAAP/4A//gAAAAAAP/4A//wAAAAAAf/4A///////////4Af//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH//////////AAD/////////+AAB/////////8AAA/////////4AAAP////////gAAAD///////+AAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAAAAAAAAAP/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/AAAAAAAAAAA//AAAAAAAAAAA/+AAAAAAAAAAB/8AAAAAAAAAAD//////////gAH//////////gAP//////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAAAB/gAAD//4AAAAf/gAAP//4AAAB//gAA///4AAAH//gAB///4AAAf//gAD///4AAA///gAH///4AAD///gAP///4AAH///gAP///4AAP///gAf///4AAf///gAf///4AB////gAf///4AD////gA////4AH////gA////4Af////gA////4A/////gA//wAAB/////gA//gAAH/////gA//gAAP/////gA//gAA///8//gA//gAD///w//gA//wA////g//gA////////A//gA///////8A//gA///////4A//gAf//////wA//gAf//////gA//gAf/////+AA//gAP/////8AA//gAP/////4AA//gAH/////gAA//gAD/////AAA//gAB////8AAA//gAA////wAAA//gAAP///AAAA//gAAD//8AAAA//gAAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAD/wAAB//8AAAAP/wAAB///AAAA//wAAB///wAAB//wAAB///4AAD//wAAB///8AAH//wAAB///+AAP//wAAB///+AAP//wAAB////AAf//wAAB////AAf//wAAB////gAf//wAAB////gA///wAAB////gA///wAAB////gA///w//AAf//wA//4A//AAA//wA//gA//AAAf/wA//gB//gAAf/wA//gB//gAAf/wA//gD//wAA//wA//wH//8AB//wA///////////gA///////////gA///////////gA///////////gAf//////////AAf//////////AAP//////////AAP/////////+AAH/////////8AAH///+/////4AAD///+f////wAAA///8P////gAAAf//4H///+AAAAH//gB///wAAAAAP4AAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAAAAAAAAAA//wAAAAAAAAAP//wAAAAAAAAB///wAAAAAAAAf///wAAAAAAAH////wAAAAAAA/////wAAAAAAP/////wAAAAAB//////wAAAAAf//////wAAAAH///////wAAAA////////wAAAP////////wAAA///////H/wAAA//////wH/wAAA/////8AH/wAAA/////AAH/wAAA////gAAH/wAAA///4AAAH/wAAA//+AAAAH/wAAA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAH/4AAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//8AAA/////+B///AAA/////+B///wAA/////+B///4AA/////+B///8AA/////+B///8AA/////+B///+AA/////+B////AA/////+B////AA/////+B////AA/////+B////gA/////+B////gA/////+B////gA/////+A////gA//gP/gAAB//wA//gf/AAAA//wA//gf/AAAAf/wA//g//AAAAf/wA//g//AAAA//wA//g//gAAA//wA//g//+AAP//wA//g////////gA//g////////gA//g////////gA//g////////gA//g////////AA//gf///////AA//gf//////+AA//gP//////+AA//gH//////8AA//gD//////4AA//gB//////wAA//gA//////AAAAAAAH////8AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////gAAAAB///////+AAAAH////////gAAAf////////4AAB/////////8AAD/////////+AAH//////////AAH//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wAf//////////4A//wAD/4AAf/4A//gAH/wAAP/4A//gAH/wAAP/4A//gAP/wAAP/4A//gAP/4AAf/4A//wAP/+AD//4A///wP//////4Af//4P//////wAf//4P//////wAf//4P//////wAf//4P//////wAP//4P//////gAP//4H//////gAH//4H//////AAH//4D/////+AAD//4D/////8AAB//4B/////4AAA//4A/////wAAAP/4AP////AAAAB/4AD///4AAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAADgA//gAAAAAAP/gA//gAAAAAH//gA//gAAAAB///gA//gAAAAP///gA//gAAAD////gA//gAAAf////gA//gAAB/////gA//gAAP/////gA//gAB//////gA//gAH//////gA//gA///////gA//gD///////gA//gf///////gA//h////////gA//n////////gA//////////gAA/////////AAAA////////wAAAA///////4AAAAA///////AAAAAA//////4AAAAAA//////AAAAAAA/////4AAAAAAA/////AAAAAAAA////8AAAAAAAA////gAAAAAAAA///+AAAAAAAAA///4AAAAAAAAA///AAAAAAAAAA//4AAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gB///wAAAAP//4H///+AAAA///8P////gAAB///+f////4AAD///+/////8AAH/////////+AAH//////////AAP//////////gAP//////////gAf//////////gAf//////////wAf//////////wAf//////////wA///////////wA//4D//wAB//4A//wB//gAA//4A//gA//gAAf/4A//gA//AAAf/4A//gA//gAAf/4A//wB//gAA//4A///P//8AH//4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////gAP//////////gAP//////////AAH//////////AAD/////////+AAD///+/////8AAB///8f////wAAAf//4P////AAAAH//wD///8AAAAA/+AAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAB///+AA/+AAAAP////gA//wAAAf////wA//4AAB/////4A//8AAD/////8A//+AAD/////+A///AAH/////+A///AAP//////A///gAP//////A///gAf//////A///wAf//////A///wAf//////A///wAf//////A///wA///////AB//4A//4AD//AAP/4A//gAB//AAP/4A//gAA//AAP/4A//gAA/+AAP/4A//gAB/8AAP/4A//wAB/8AAf/4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH/////////+AAD/////////8AAB/////////4AAAf////////wAAAP////////AAAAB///////4AAAAAD/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAB/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("EiAnGicnJycnJycnEw=="), 78 + (scale << 8) + (1 << 16)); +}; + +function Regattatimer() { + return { + layout: undefined, + /* + layouts: { + idle: function() { + switch(settings.dial) { + case "Discs": + break; + case "Numeric": + default: + break; + } + }, + start: function(phase) { + switch(settings.dial) { + case "Discs": + break; + case "Numeric": + default: + break; + } + }, + race: function() { + + } + }, + */ + mode: "idle", // idle, start, race" + countdown: 300, // 5 minutes + counter: undefined, + interval: undefined, + theme: null, + themes: { + "Light": { + "fgColor": "#000000", + "bgColor": "#FFFF00", + "icons": { + "satellites": function() { + return hs.decompress(atob("jEYxH+AH4Ab6QIIBJAfNAAQtSC4gxSCwgYHHBYYMC6IYPC5AZOC8QYMC5YYLC5inSDH4waVbAYJCpgA/AAI=")); + }, + "battery": function() { + return hs.decompress(atob("jEYxH+AHHSAAgXmCgoaRC/4X/C/4X/C/4X/C64Ap")); + } + } + }, + "Dark": { + "fgColor": "#FFFF00", + "bgColor": "#000000", + "icons": { + "satellites": function() { + return hs.decompress(atob("jEYxH+AH4Ab6QIIBJAfNAAQtSC4gxSCwgYHHBYYMC6IYPC5AZOC8QYMC5YYLC5inSDH4waVbAYJCpgA/AAI=")); + }, + "battery": function() { + return hs.decompress(atob("jEYxH+AHHSAAgXmCgoaRC/4X/C/4X/C/4X/C64Ap")); + } + } + } + }, + settings: Object.assign({ + "debug": false, + "buzzer": true, + "dial": "Numeric", + "gps": true, + "record": false, + "theme": "Dark", + }, require('Storage').readJSON("regattatimer.json", true) || {}), + + translations: Object.assign({ + "de": { + "speed": "FüG", // Fahrt über Grund + "speed_unit": "kn" + }, + "en": { + "speed": "SOA", // SOA speed of advance + "speed_unit": "kn" + } + }, require('Storage').readJSON("translations.json", true) || {}), + + init: function() { + + if(this.settings.debug) { + this.countdown = 1; + } + + this.theme = this.themes[this.settings.theme]; + + Bangle.setLCDPower(1); + Bangle.setLCDTimeout(0); + + // in "idle", "start" or "stoped" mode, a button click (re)starts the countdown + // in "race" mode, a button click stops the counter + var onButtonClick = (function(ev) { + switch(this.mode) { + case "idle": + this.resetCounter(); + this.mode = "start"; + this.setLayoutStartMinSec(); + this.startCounter(); + this.interval = setInterval((function() { + this.startCounter(); + }).bind(this), 1000); + break; + case "stoped": + case "start": + this.resetCounter(); + this.setLayoutIdle(); + break; + case "race": + this.raceCounterStop(); + break; + } + }).bind(this); + + setWatch(onButtonClick, BTN1, true); + + this.setLayoutIdle(); + }, + + onGPS: function(fix) { + if(this.mode == "race") { + if(fix.fix && isFinite(fix.speed)) { + this.layout.clear(layout.speed); + this.layout.speed.label = fix.speed.toFixed(2); + this.layout.render(this.layout.speed); + } + this.layout.satellites.label = fix.satellites; + } + }, + + translate: function(slug) { + return this.translations[locale][slug]; + }, + // during the start phase, the clock counts down 5 4 1 0 minutes + // a button click restarts the countdown + startCounter: function() { + + this.counter --; + + if(this.counter >= 0) { + var counterMinutes = parseInt(this.counter / 60); + + if(counterMinutes > 0) { + this.layout.minutes.label = counterMinutes; + // this.layout.seconds.label = "0".concat(this.counter - counterMinutes * 60).toString().slice(-2); + this.layout.seconds.label = this.padZeroLeft(this.counter - counterMinutes * 60); + this.layout.render(); + } + else { + this.setLayoutStartSec(); + this.layout.seconds.label = this.counter.toString(); + this.layout.render(); + } + // this keeps the watch LCD lit up + g.flip(); + } + // time is up + else { + this.raceCounterStart(); + } + }, + padZeroLeft: function(str) { + return str.toString().padStart(2, "0"); + }, + formatTime: function(time) { + var + minutes = parseInt(time / 60), + seconds = time - (minutes * 60); + + return this.padZeroLeft(parseInt(time / 3600)) + ":" + this.padZeroLeft(minutes) + ":" + this.padZeroLeft(seconds); + }, + raceCounter: function() { + + if(this.counter % 60 == 0) { + this.layout.clear(this.layout.battery); + this.layout.battery.label = E.getBattery() + "%"; + this.layout.render(this.layout.battery); + } + + this.counter ++; + + this.layout.racetime.label = this.formatTime(this.counter); + this.layout.daytime.label = require("locale").time(new Date(), 1); + this.layout.render(); + + // keeps the watch screen lit up + g.flip(); + }, + raceCounterStop: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + this.mode = "stoped"; + }, + raceCounterStart: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + + if(this.settings.buzzer) { + Bangle.buzz(); + } + + this.counter = 0; + // switch to race mode + this.mode = "race"; + this.setLayoutRace(); + this.raceCounter(); + this.interval = setInterval((function() { + this.raceCounter(); + }).bind(this), 1000); + }, + + resetCounter: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + this.counter = this.countdown; + }, + + setLayoutIdle: function() { + + g.clear(); + + this.mode = "idle"; + + this.layout = new Layout({ + type: "v", + bgCol: this.theme.bgColor, + c: [ + { + type: "v", + c: [ + {type: "txt", font: "Anton", label: "5", col: this.theme.fgColor, id: "minutes", fillx: 1, filly: 1}, + {type: "txt", font: "20%", label: "--:--", col: this.theme.fgColor, id: "daytime", fillx: 1, filly: 1} + ] + } + ]}, {lazy: true}); + + this.interval = setInterval((function() { + this.layout.daytime.label = require("locale").time(new Date(), 1); + this.layout.render(); + + // keeps the watch screen lit up + g.flip(); + }).bind(this), 1000); + }, + setLayoutStartMinSec: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.theme.bgColor, + c: [ + { + type: "h", + c: [ + {type: "txt", font: "Anton", label: "4", col: this.theme.fgColor, id: "minutes", fillx: 1, filly: 1}, + {type: "txt", font: "Anton", label: "59", col: this.theme.fgColor, id: "seconds", fillx: 1, filly: 1}, + ] + } + ]}, {lazy: true} + ); + }, + setLayoutStartSec: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.theme.bgColor, + c:[ + {type: "txt", font: "Anton", label: "", fillx: true, filly: true, col: this.theme.fgColor, id: "seconds"}, + ]}, {lazy: true}); + }, + setLayoutRace: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.theme.bgColor, + c: [ + {type: "txt", font: "20%", label: "00:00:00", col: this.theme.fgColor, pad: 4, filly: 1, fillx: 1, id: "racetime"}, + {type: "txt", font: "15%", label: "-", col: this.theme.fgColor, pad: 4, filly:1, fillx:1, id: "daytime"}, + // horizontal + {type: "h", c: [ + {type: "txt", font: "10%", label: this.translate("speed"), col: this.theme.fgColor, pad:4, fillx:1, filly:1}, + {type: "txt", font: "20%", label: "0", col: this.theme.fgColor, pad:4, fillx:1, filly:1, id: "speed"}, + {type: "txt", font: "10%", label: this.translate("speed_unit"), col: this.theme.fgColor, pad:4, fillx:1, filly:1}, + ]}, + {type: "h", c: [ + {type:"img", pad: 2, src: this.theme["icons"].satellites()}, + {type: "txt", font: "10%", label: "0", col: this.theme.fgColor, pad: 2, filly:1, id: "satellites"}, + // hacky, use empty element with fillx to push the other elments to the left an right side + {type: undefined, pad: 2, fillx: 1}, + {type:"img", pad: 2, src: this.theme["icons"].battery()}, + {type: "txt", font: "10%", label: "-", col: this.theme.fgColor, pad: 2, filly: 1, id: "battery"}, + ]} + ]}, {lazy: true}); + } + }; +} + +var regattatimer = Regattatimer(); +regattatimer.init(); + +if(regattatimer.settings.gps) { + Bangle.setGPSPower(1); + Bangle.on('GPS', regattatimer.onGPS.bind(regattatimer)); +} + +Bangle.on('kill', function() { + Bangle.setLCDPower(0); + Bangle.setLCDTimeout(10); + + if(regattatimer.settings.gps) { + Bangle.setGPSPower(0); + } +}); + diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..47712c7ed12a2f1f18797165d6b063069aa6f7b0 GIT binary patch literal 2990 zcmV;f3sLlmP)G2jPhz_@@$zlaKgJDT{(B`yep zChl7_F42Iv#tn>LR6syX+!uU4p2&l!DBv!mvI(P6Fv5%`ir|9LLD2K^dv)t|-JZVP z_jXs`duN^`CtRw#PuKZ(EvNqHR3Vb$kE%4N%1Bk2q$>YZm9?s}MOFS#6|Hmmqfpm1 zg}P(I2vzBk28&988>lK%RHeNRfKboUrYe(Fr8y7a2B^v`RoPYthHB1&jWblGzc28{ zsmkU$Fifi~Kd8#sEWq_rmDzP*h}KwUf<>j^^;MOHbx?V&6IlQzm9)IS!P8D4E>e}= zHTdZ_4r`-$$kr)clqM)*zH(4nGZ#)#g2yLqOqt?Dgt zMiO}A^RC2>9ip$jCVJ_mq6Zux%4@U8;K8DoT_(C{k?5a)Rt3Z{rNC=cl}&lQc*6$K z2@^z{nu-CSb)9~?=&iSkKJ$#|M<0oP^_A$BEu!1D8R7T(_2wG)aPMiSnP**H&CTW= zwCWrI+#KoR6#P8zFZ|*Q(TgrJUC|nP;DMrd-!1ydC)rvJt@-1RMen*xbjXlEKkvHa z645We%niipVep!LJITNNB6|JxqCI;Wqxk#$^F=@SAivh}{`;cmofo{bS1-{UZxr3S z)%&}^YLE-y6rV4cJ2&X67<2jMrd!6fq}j7Y*R3lqKk6vaV~!~_(NrN93!dev1yS33G=(N(LGpIf%f+|MlRmIL0g#}=239&PSz zX-U?*a;5nQwm#QfBihm74M6yS1}U&AUM7j5Lrq0@*+uk`M@0YnD|$@CC3*DG;ubi4 zy6EcF$?xGFbn}cYLl-)u#ysjUZJMbQbvyiUukO!qDbP8Y6s=urx(O9zFz3scx|bG- zG56l<&4~X_NPYUme}EM$jLSnE2MrQ!ZT0f*i2yh^i;`u_MEmtK6&p25^yi<8ufO${ z=+dRdW!ty=1Fw`$-pRY&vY&nuJ>djX*T%;3hJgh6hZKm#)Ge>E(NyTHv!chBU3V4T zV-Np;CCP5PMZcGE`ph!}5Hq$!+I%{yiZQRbIRMWclU$iINA$%Ps}Eq@d#URgX9V3j z!;n?^>IdW`TYk|d~htUf0bG-ow%gdL6&#SeOnzM`_ zZ*0B!W={Yz?c15xB}3u&R+T|j)w^H=Ezkb@z4w9{xg54U5y0194`_I|eEGfRFpv(x#crv_@$(3CD<@?W^vh_cN&|oxWWT~v^114 z&DYvkt&&)VJlyLl29VhoMbJW}*cR=Eg00w$?MX~cEP&5FXOs}PwPg&fgd@B1*ry@E z7Q&dcf)cU;Y}sd@iH;i=#2cM z@WS!{q?>c!zWZhX&_>%fZ;rnH%{RexKY6n886SASPb$TePa55{>b`xe58$Rv=Dz;@ zGXQ84f*8o7(k)tAg6?IhE`A?!NVW_BDo@3E4>Mym01T&*P=D@&t@a9_n{Kguxfy(^ z6s_*N?@Zi*XC8mNvpzd8yQv>h@4esx^X@fk%)gTWgsyk>#*w0>01T&*cmXh}3u}a2 zeYJV!h8sFxgN$Gxw{rjeo$q<)9nqb3G9SX;XIu8!XPd=-_?c&)E!O+eOO*qdOBYU> zM3yc!1gqQHFo0+9qsj%{l~-RiGoLFxdKjGtA1wOHE9vy6okU!sp<)0JJ1pLfnK#c| z-)k?!T#&kJTkV@~W;4_cWWoBNGphAfBES7+S~0q=y*7TFwr&hD`6Sjqya!^~T0c0xyN=WFoWw;eI@9xCT zY~OBda>;~KPBFG8MH~O7voxO0YH!Z~AY$olD6Cqy6s?lj3FM%IlJ89f(7GjTB?wdc zQ>N73*sOvL;WQOH_T!*u29O;DxP@nR1EGO*f2N_U23 zR$E<0Qh2=!Hn_4AftCM<)Q)xQ%(@!_ zA!+ul2*9|+o5hQ}UUn9B4QFS?B~W<*kd_BC%ok$MML0Wa&YZ5DorRl552L5WZA(iH zAi~ByzR>XDRok1Z)evkMF~Zn3abji9RsZu9Tc{I3s_+_~uFIqWi93IO<$6RHDF-@ zJVLN82l(TUfzy25b?Nk`+Gs;1V2VY|-ZM6>*lWa3>XE6TPHt?6Iv;mjFs8fLD&1(4 zYO5E%;h;hQiM?l~K(oACwE^Rur91<7s~2I5v>c<|>Xmx4VQXu!*$@!jmS}}k3Z&Wq zmU=IBwBQjia??4|ZZ^!c-L<790Fk|Cj4SSSww#iHRPF7qyfeJrmAct(3wSW;KDN7N z-f-R88gys)!?6GnzHFD%3Cp%WW@DD3D6 zTpR@NzMRxxx{}>@oOEi3-7H;B_oLMvd8D}?KMz+W%@3t$9G){7Y+?VUYJ6pG!92J|I1_{9? z7J}n>Y*a)iWGH-|1rB0>wQZ%#vofSZRhq)57jf*Rcf-~f4#9Dceqy2_-byxn7N?tM z2>=npuoHwGgOd*X$tY8!&d%zW=iy^*ZD#uvJAmBG0DLtPx?zCH-U3uWC!xt6KE`j# zxJLKoUY>!Ha&^&FxmGS2n&D{B=<`)MGc?B+E5g`C-VBR-n{Jy$&_VPJVz){Z+vub- z1UWNQt0nF2W-9^N@MFgsR|uDcBqe(QBflx*8o4TTf1dH^j+86YV!?9`+5T6RE2dpQ z{;euK69IG%pcp)I03d=q(YiF%5!KV8Px~k|O#@p3Y;#$8N!4&MY^2q)Y{So=KY#xI zG5$L%{ut|14F#@N;IPH40b4B_gT8v1=s%}G(txdo8+VE!V`z6P@MXZY#(NGNBffhH z_rBhT0k;}2M!dH%Ju&z!v(Rv8|;p6kftMrkI4u zfa4o8s}XZUH-!yMJxFQ5wZ?m>f&ou$8&eFvXuvJTivdru{?iZg=WpgooR^-tu)^EV z^olZpTNS{7Z-cdG*2#N8@47wfHnG~m?D7-_WL%Rqm%3YZKyG-9jq zl7*;U654qb;8C(iE$R-Z00unVk}x2T0=!P?QH#36DZmTu!z~E|@+iP3MIN=NJDdW1 z)H&ReFd&ZtMFWmI;wA8&r0ha;aF(9si0v-ht27GY#{4@W*Ue-g0@pO2ZIRy5& zJ9URspqjvcGhfpE{JsGE>Q;Sh_w!r%lW>-PA>tgr2x|S6H>B+2Z7&T3K8@^kf%m`o z@40$Mi&UAlUo~;cpI0CUBJapz8;je!3H&y)FamiKAuK;VFy~2Oxu;(UJU)Tdda9UO zx345{iK;PKd)D=OF7=;PK&`zRowa>;0*9W}lfcSjDIY6aq6u0auWo-4flD!Q>~*1+ zSv^j9F6Cn=(7K-1_Ky)b_;k;{EZ3t>n*y!tWo3n)MgObkp6ynrJ!g-I1C zq|s)Hg&5~d??|-YV|;oVdEiOO!?3xD!v+>m00J+th&xXK1KxS{7nnN+yuc#vJO#24 zNZlj&D z5TzYh)b1>`J6WoJkEzx6t&Dty@|HF0HGj(*TCU|LaPDNy6cK9(Y$fYCxzcryI+kC8 zz22vNpP*_vzps|Cs$J{eY5%YPzpolC#-6Ox;?1>`jKi342bWY?FK@3FX|RS$4^`e{ zI=!wjHG#u}WMa~W9oh`Nj1-n)liZdU^tf`xS>V9R$vQE2s#k%0ptNbbjBTNkGYZmF-H9IaHY^hmvv1fGn*pPtAW8H zmuX?ZyR7u0^TdD`UD#cwg#quf(u>X$1737tcbOJh2(l1lA$IY0<~g1MJc)zAlM$G6 z;>N=WBdS&a0z+U3TQ8sa}=mnz)fJw4lUk(X;ENXS}{Q;*fb)AmXF`bb?u~X|6Q}9G~&`Yr2J9_?rlv8+_^HM(;kbE#wW)k9!G`^W^Wl=G5JauiFJNa8CkzXlKccz$bY< z<55J~t^C`7U-ufRdx_GS9r=5g3u53F0(*#eXI;;=AaOnhRwq?i?om>8h9R)$F5g4O z9{qZ*w;O45^G$^W@jZn)E$Q(rL@AH!%~*uq=b>V+x}JU@@%ufPSc_add^>?HdEof1 zwDe>?VjEQ055Sdmth?~vcM{=!&wx2$(s*UA`JU667V2!DB7)##puuzDc% zu6f5`7F{85tLxJKaRpk9SkX6p#1Tw(Z*t5+IL1i~{5YW)K)V9&JTRWmFVq;>76{z# z{`By;0uY#eA=nqaTCJf#3n;vn#31m>K$r(nT#FHisi zLtqGefn(7n3YZjtzz`S$n_PuTC~$!SZUU=MrPY3~sW}{sy_RjU4X=YT`C}1udMcls3vfabK*Sb zz=gX^sy)g)=frt-uY}b0U#eDJb^DhKxZj0P)C$X0T`Pf)dY*o3>sqNmJ*|Le?Nhy3 z+iy=`V%Tf;xzGe{JCE=*`Y8zP-AmX*V9gS0)F@yjXs&N-`{4+jn^5ifbFCzR&~=1v z)m^v0F$T^X<@XtFsk_eJ1Ww%{tGnGA3hms+O3>v9+#|a|7Q&t_xat>16Lg~(m@LG6 zEQi3+I|}51>tfNX2&?*qz!7vjnT6WA^Kow65kp`IJoP&oQxe#lEwDa`OL=q7!&~c) z%e1*hHavkn`C28t_iZ%K`E&7^$?;h7XYtMzbE=s1?($&?tc;Oz6o|7%HHugX?tT(? zwH#NwXc5??UX!5@tQB0uUGiL*P-p zlX0!D00f4>5O{q*XNIFH0D&Pe1Rm8p8Q1y>utPfpo{YesPU-$Cd3~Q|hBGOE0nfBr djP*-_{{W3meU`J@sILG3002ovPDHLkV1f>kVUYj; literal 0 HcmV?d00001 diff --git a/settings.js b/settings.js new file mode 100644 index 000000000..cdcdf53f5 --- /dev/null +++ b/settings.js @@ -0,0 +1,78 @@ +(function(back) { + var + file = "regattatimer.json", + + storage = require("Storage"), + + dials = ["Numeric", "Discs"], + + themes = ["Light", "Dark"], + + settings = Object.assign({ + "debug": false, + "buzzer": true, + "dial": "Numeric", + "gps": true, + "record": false, + "theme": "Dark", + }, storage.readJSON(file, true) || {}); + + function save(key, value) { + settings[key] = value; + storage.writeJSON(file, settings); + } + + E.showMenu({ + "" : { "title" : "Regatta Timer" }, + "< Back" : () => back(), + "GPS": { + value: !!settings.gps, // !! converts undefined to false + onchange: v => { + save("gps", v); + } + }, + "THEME": { + value: themes.indexOf(settings.theme), + min: 0, + max: themes.length - 1, + step: 1, + wrap: true, + format: v => themes[v], + onchange: (d) => { + save("theme", themes[d]); + } + }, + "BUZZER": { + value: !!settings.buzzer, // !! converts undefined to false + onchange: v => { + save("buzzer", v); + } + }, + /* + "DIAL": { + value: dials.indexOf(settings.dial), + min: 0, + max: dials.length - 1, + step: 1, + wrap: true, + format: v => dials[v], + onchange: (d) => { + save("dial", dials[d]); + } + }, + "RECORD": { + value: !!settings.record, // 0| converts undefined to 0 + onchange: v => { + settings.record = v; + save("record", v); + } + }, + */ + "DEBUG": { + value: !!settings.debug, // 0| converts undefined to 0 + onchange: v => { + save("debug", v); + } + }, + }); +})(load) From a50a307258233976fc20880d82f256c5a6471f1f Mon Sep 17 00:00:00 2001 From: naden Date: Sat, 2 Mar 2024 19:28:51 +0100 Subject: [PATCH 29/40] Add files via upload --- apps/regattatimer/app-icon.js | 2 +- apps/regattatimer/icon.png | Bin 2990 -> 3073 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regattatimer/app-icon.js b/apps/regattatimer/app-icon.js index 3c6f27c44..d6201e3c9 100644 --- a/apps/regattatimer/app-icon.js +++ b/apps/regattatimer/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("qFQ4UB8H/AAIJBoGtqoACDZYPDCRwUGqATNgoTDoATNgISCqhtPio6QHgg6OHggJGn+q1X8PJAHFnwSBAAO8CYxiFgeq1/Agf61XAMgpiFnWvAof61hkFCYkD1YhEgfqAwkFOwk62EAhkCwEwgEOFAkBCYgfCnYTB9gvCCZECDwMshgGBmE4GAOACY8KHQIjBAAQTBh2gCYY6EgHwTIsPgA8EAAeogAeDGAcAlQSGgQRBE5EKKAYdDA4wfGYgo6HHgbKFgHrgB3BAA0OBgQAEDQw0Hcog5IHojyEgWwWAgAFncOOAkO4ATLgZbENYIAMJIk7CZo0ElcDCRfA9AFD9A8M0ErFgjlBABXwJQiyMWgyyMQwrGBmASLhjIDUgPs//wBoc8ZAwTEmGqKQiEDh2shATDdwMA16nE1ADCh4dBeAcCAYJlEgQTDYoQPCAYevFwQfBYAgTH0eqFAcKlATEhQTG2GsgeqgE4CYM6LQITHDoUD/8A9fq4E/SYI7HgfwO4fq1RVDCY2wgevT4krfgqLDWYXqI4IACE4cO1XMY4vsn41DgBSBBgX/bYugmD/HAAcMhQgDYogAJhRWFABk6XQkPCRfwnYFD9AtEAA+gSQgEEABPoAgYsEABLTDNAo8KAgakBDQgAFnbCBAwbwBCZbuDZAayMc4i0NWQgAB9cAIYhbEBgQaGHpCDBGg0KKwgAFgQeGA4XwBIr5BD5GoHg46BlQwH9QnJ1YGDoAeDO4MsBYc4O4IwDgITDgRsBlgiBFgITBnRODCYg8BXgM6gWADIMDHQgTFnQ8BhgTBmA6BTokBqDyE1blEgYvCAAUFCYgoB14FD/TEFgtUAwkD1Wv4ED/WqEwkAitVTIs+1QAC3gLFqoTGgE/CQP8BQwTBPAgALgITBMgoAKgoTBMgoAKMQI8QHQQ8QHQQACCZoSEChoPD")) +require("heatshrink").decompress(atob("mEw4cB8H/AAIJBsHvgHGkGl0H34F7kmSpMgEJMBBwQCBoA1MCIZHNiQRByARNgQRBwA/E5ZdB7HACQgRBAwmdl/sy/03gKEhJoEkVeE4c6qAuECIcH6tghHF0PAger2ARHovYiULgdiqMgh+oCIZeD1EGiwcE4FFTY0VoEtBAu0hAkDAAWKhkbBAtssMUA4kDqFuRIo3B1kisAHDg/ggywHmALBRgliOgYAEg9DSQkVjQRJuB/EiboLtQRD/D9KkkffAUG0ARLiMwAgMICoQAJgK1ChfE3VIR4dwAYUM4uD6AFBh/EyN1Zwf/AYV1olj+ARCoET2wjCgtfCIU1oECCIcFut/JQUfzgRCh9D0Q8CCINustAxewn+pwMXsARGoESAQMNwH/9UAjYLCGohZBuhrDCIIABqmkLIZ9B0PGRIeRJgVJjp9DUKLFRdKMBiY1LtQEDisa2APHg9w1AGDkViCJNDqAGE8EGCI8wBYIGDgdQsihDPQeskVgBAmKhi/BAAlssMUBAsVoEtBAu0hBYEAAMC1EGiQzDkHAoqxCgEgAYVF7EShcDsVRkEPEQYYBNofVsEI4uh4ED1aGDCIiSBrwKDnSMEgMkJQmdl/sy/03gKEhMkbQQYC5f///YSotJkj5KPAgRByQRNiQRCQYYAJyQRCpJuEbIwOCA==")) diff --git a/apps/regattatimer/icon.png b/apps/regattatimer/icon.png index 47712c7ed12a2f1f18797165d6b063069aa6f7b0..02dcc6875594330df34cb1e9d9c932b324ba6a2c 100644 GIT binary patch literal 3073 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 Date: Sat, 2 Mar 2024 20:17:02 +0100 Subject: [PATCH 30/40] Add files via upload --- apps/regattatimer/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regattatimer/settings.js b/apps/regattatimer/settings.js index cdcdf53f5..2f9133d9f 100644 --- a/apps/regattatimer/settings.js +++ b/apps/regattatimer/settings.js @@ -75,4 +75,4 @@ } }, }); -})(load) +}) From 04057c401813752b5f5e991e89690dd0e0244ac2 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Sun, 3 Mar 2024 22:38:04 +0000 Subject: [PATCH 31/40] widlockunlock: stop event propagation on tap --- apps/widlockunlock/widget.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/widlockunlock/widget.js b/apps/widlockunlock/widget.js index cfbbc87a3..b5af798dd 100644 --- a/apps/widlockunlock/widget.js +++ b/apps/widlockunlock/widget.js @@ -11,6 +11,8 @@ Bangle.on('touch', (_btn, xy) => { if(w.x - oversize <= x && x < w.x + 14 + oversize && w.y - oversize <= y && y < w.y + 24 + oversize) { + E.stopEventPropagation && E.stopEventPropagation(); + Bangle.setLocked(true); const backlightTimeout = Bangle.getOptions().backlightTimeout; // ms From fa11b76da26b28fe2b0759ceb4d60f103ccbd991 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Sun, 3 Mar 2024 22:40:31 +0000 Subject: [PATCH 32/40] widlockunlock: disable tap-to-lock if back button is present --- apps/widlockunlock/ChangeLog | 1 + apps/widlockunlock/metadata.json | 2 +- apps/widlockunlock/widget.js | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/widlockunlock/ChangeLog b/apps/widlockunlock/ChangeLog index b5efcaa86..40a842cc7 100644 --- a/apps/widlockunlock/ChangeLog +++ b/apps/widlockunlock/ChangeLog @@ -1,2 +1,3 @@ 0.01: First commit 0.02: Add tap-to-lock functionality +0.03: Disable tap-to-lock if back button is present diff --git a/apps/widlockunlock/metadata.json b/apps/widlockunlock/metadata.json index cc4fa76cd..f98f29d63 100644 --- a/apps/widlockunlock/metadata.json +++ b/apps/widlockunlock/metadata.json @@ -1,7 +1,7 @@ { "id": "widlockunlock", "name": "Lock/Unlock Widget", - "version": "0.02", + "version": "0.03", "description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked, or an unlock icon otherwise. Tap to lock the lcd", "icon": "widget.png", "type": "widget", diff --git a/apps/widlockunlock/widget.js b/apps/widlockunlock/widget.js index b5af798dd..4b78b440c 100644 --- a/apps/widlockunlock/widget.js +++ b/apps/widlockunlock/widget.js @@ -1,6 +1,8 @@ Bangle.on("lock", () => Bangle.drawWidgets()); Bangle.on('touch', (_btn, xy) => { + if (WIDGETS["back"]) return; + const oversize = 5; const w = WIDGETS.lockunlock; From b2ad6d509d835ac217943afa91a8d1ad5cf7ff91 Mon Sep 17 00:00:00 2001 From: naden Date: Mon, 4 Mar 2024 10:44:01 +0100 Subject: [PATCH 33/40] Add files via upload --- apps/regattatimer/app-icon.js | 2 +- apps/regattatimer/icon.png | Bin 3073 -> 980 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regattatimer/app-icon.js b/apps/regattatimer/app-icon.js index d6201e3c9..71417f0cd 100644 --- a/apps/regattatimer/app-icon.js +++ b/apps/regattatimer/app-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEw4cB8H/AAIJBsHvgHGkGl0H34F7kmSpMgEJMBBwQCBoA1MCIZHNiQRByARNgQRBwA/E5ZdB7HACQgRBAwmdl/sy/03gKEhJoEkVeE4c6qAuECIcH6tghHF0PAger2ARHovYiULgdiqMgh+oCIZeD1EGiwcE4FFTY0VoEtBAu0hAkDAAWKhkbBAtssMUA4kDqFuRIo3B1kisAHDg/ggywHmALBRgliOgYAEg9DSQkVjQRJuB/EiboLtQRD/D9KkkffAUG0ARLiMwAgMICoQAJgK1ChfE3VIR4dwAYUM4uD6AFBh/EyN1Zwf/AYV1olj+ARCoET2wjCgtfCIU1oECCIcFut/JQUfzgRCh9D0Q8CCINustAxewn+pwMXsARGoESAQMNwH/9UAjYLCGohZBuhrDCIIABqmkLIZ9B0PGRIeRJgVJjp9DUKLFRdKMBiY1LtQEDisa2APHg9w1AGDkViCJNDqAGE8EGCI8wBYIGDgdQsihDPQeskVgBAmKhi/BAAlssMUBAsVoEtBAu0hBYEAAMC1EGiQzDkHAoqxCgEgAYVF7EShcDsVRkEPEQYYBNofVsEI4uh4ED1aGDCIiSBrwKDnSMEgMkJQmdl/sy/03gKEhMkbQQYC5f///YSotJkj5KPAgRByQRNiQRCQYYAJyQRCpJuEbIwOCA==")) +require("heatshrink").decompress(atob("mEw4f/8H/A4NgtfgzkgoVg50g40Awg5lnmSpMk4ARMkMkCQNgCJkJCKM////0AQLgNJkBZP5OACB0B5OKjJqNgGJLINOEZprDCJ0rmVJoARMzfAg1JLh8AQCIRNGQPghg1NhQgBghZOhMmoR9ClmSqDXJR4NLAwMhknQEhEsCINwboQRKgG59RuDCJYADLgIRPqVnCJ9CuYROgOSwYjPyUDCJ0IzwRP4mQCIsRlARH8mZWYPJbgK/BCJOSR4OTCIMGCJ8MAoIRIi3btUk3UACJYABLIcapMJCJxZLCKbMFCP4R/COQAo")) diff --git a/apps/regattatimer/icon.png b/apps/regattatimer/icon.png index 02dcc6875594330df34cb1e9d9c932b324ba6a2c..5ae5f13819c07ce8c6ef22243142f725ce1f1eda 100644 GIT binary patch delta 946 zcmV;j15Nya7}N)lFnBK~lfp$Hy7B`mTnLIHT1d4L6x^u`#ShShp@L!sMW%!z2!6a64wv(u@4k0^ zuY2>oc(b~fGiPT0GjnF)%M6V1@Pem6c;N6xw$YpYL7A}q?iI|Zid=grS9%sNKOWc%Gw&Frox>& z@Z<@^#yVkAV1F`A0X%*TZ{PYCB&qf36XfRyrqFI~Qvd}8Ff?SXN+FPw553p^DF8$L^0G>&uU{cP zUZud*tG>(TuV0Xm0NdLk0wAz}LS-d%bf{etsh>Z!gcFHCAOI#NRPGB&hb}3B=g-3g zfRxzGOb{;%NxzUoEOV4WAsqo3=pH>%rry5~eSdw>))poJJpVx;G2SxUskh=g+7E5e8>Yh>ICru8uo835L%nsY<|1nW7w_C+FK zmnVaj6Nx~up0jITBm#DMGFUlbAA!k9$Scay@cq5N{`G|>STEbPF9N_WPX;UJ57xfJ Uu*vYf0000007*qoM6N<$f-wfWZU6uP literal 3073 zcmY*bdpy%^8{d`~9U zXCiXQaXGU>TOKCyPET*o``*9L^}D|Jb$_q#bzk=%_vcQs#+nKWNDBY}06}vzV;inl z-!U*R_wNAWByt^)Xk%&!c>GRwfjhbCxsE^B z%!vp9>=D~BARzayBv;LeVCzV7JZ*{c2)Y2rdj_5Nf|D-ecWE2-(@FL;m$rl0w zi72w3;!g&Oi+92ZMfsl;lE0p!<7sPoqo5Ek`D1W(xVj=nKwe%R9pdSYvN1OKmCl{% zDf*H~!6*bGEG!HjrU?%U@j)PUbaW8v8VC&y7?%MfQUXbMGAxj&^pD7Yb&S1;9wCHa z5+Nv1en%I7J}8u=r>MA7==b<%pCp3!zm)=szuMw9h}c;{AmQqW-`d<%^iC9I6+-ah zR^HLaAkjaW{}=mJ2aVV%{y&@fr_-NNZmSppG~)NMVFaXbpZK^3|ChP3fvxZEu3V>b z>XdZL_DegV_je8%4hbC%72D4iKag-$8qQPn($)MR-0bXGS0cV<*5r6au4vzp>tbv) z^tDbi^cXf?dZgDcY@~a8t%1oIcsW4(dpM;JHD^A)itXOSEU#B6gc{fp7JF_C^nGSs ztLmd-gYVVd>eDJc%|GS6mDZ#dmrgapp7kYR8VMPFex-{sckP|F zsMrV^DQ}Wxtv`d%dSg*)9XUF)ij_&Oo-bny%GmzM`jGB$Np@CvN^6_lZjktz zc26?puzgeuV+Y;W_-ZO zpNOJ8T7I=`yTO^Wi&`-Vd^rcTJ2>xKTv=k=4!P8SF3by8T>MBj{9%2j!z8V}NC}lL zvbUTa6XHDg;hM2h$@)Q)f(Yy|(TcD(c1ZOxvAYY;nlHvZ>$8A;q>F3JmN->~+}n$Z z0@U;bys9USQ)+`~V=P(9r}%2L=Nf=y>_E!DaBDS8Ma}2q;)+QAwd(K7-T6-$A50E! zu6w{Eglw1V`kLb(+=q31yZpV!NJBl*w%wRK zsRWk(_Fbu%?-hTDYB4UUy0I4FaWb2qUb_NR$cg)qJ|J3NI@4dhuo}~DK)>o`rW6pp zVcvhS<>AI+*|DymGw5|wT<;t9kjID#nq`&+5z{3J47bYg@|*?3YfkhZ9ej2>9&9i* zRSOG_`?%Z|$A>=ELT5fIv+bgeKr3*YDq69j(B&#ijW!82-$YAwG)w61%q-Zh zDt*Ty@j%A#tx#V}ni^cuUAln_Kf(-oVHF@d_Ob{ct1Sefc$!R!YD|`G zsuY+dLjQQZveI~H7987y6KStJr+O}&fP4SBiDyjfj8A{<5{i&_f%OA4T_1G{F9aAx z_EgA7^9K8k`V^#Iq62#`^I4?Qb>_?lajGz|HQPz#E7f7F_J#AoDJFpn_& zXtwL5OS{5N?dLo#vPxfQZg8T3AuZ5XGInqU#HfmVy1}e0iCFF$ubYt|Fe`}qukwek zBwU?5XrJ7TVdu4KQdZYD*?(8;i9aRXLIe$mjUwH&`42G~g{xfKM*>G0?%4?YN{T?H z)#Ih+!rv{>#*{hAxA*R;E6s1mp;m3%{4d=zFpx8N)&X~N3I;a}MJZboyw$EbT}STnV^p8y@xzixb6=rva{#~dACG*WStLlF=Jutzh@`mO0rp3x*F z**m5|_5J2?RS!;g=x$}Xc;`l~-8PF|7F-?Ep-oRD04+z;G40gl`s~ECiXZD!B`Juk zNcj5-+HADAu_O5Y?PECE#|@~KAFG2tqjyrjRlUiua2KtjAcL&`X2fagVMk)N%g?SH zUXWJMLf>^0KB0K9W{Vmb(jb%{RsC%yo3g&p;(u8D$Z#t#^GeoCdNAk(p>TJwXxa5; z(CQI@Y4gqY6khxwK+{Y&FhD3S!1J=qvpiIKn%bpVn`jRnG6^(!6T7wavNnxBtz~7h z5P(q?2OFKb6xVyEOlW6%+?c9&C5 zmb2ro8aq~j0*N!F}8PAzx|h?t4}<@aeXm4 z+2|a;dHVZLo9si!-gIdfxNkLC8$&hk7ERW6GbxbsjLnSf4b@89(Dx^Q6a0NU(OQ$Z-k(w0qz>Xm^MyYRz3M>A{&USwRoo06t_Nd{3*fY) zQmUs27Ykd?L0$E%RB@f|Z^CF`aJT>*uFr@%P4x?YGQZCHNM6X4R z{OU80J+GJw!#}yvyp){n+0d|5DWzkg1BPQ%+19sHTbIaPb3fCi0YyKvF*eV;#a zhTh$l0Dr6<_$u=PBPc3&d_6Ns+r#&!kIRL&yiX^Uk99|_%z5S1ijb!}bJixhFU;@> z4J$s_CFe}0r}XoVe^XGM-W`~cczqtNnKJ{Lz$-x4#l*U!Ff8Ry(X@x;4f%5CNafDg zzys3aYsz*MYUTdx`=1CG^dQqp`37D8_%hS!klLotlA?{b9*47SUKfjLAB|OCvB|@* znmAO!q+%p~`nVPSV}dn4(d!oI^J+6TFJjRx=wj~TvtB}$5qo0BXW)$XRuez?tt<1Z zi?pG(MOJ~+Ll1fS<78xGC7OfT<(g3_s2zlOUpU~_LZ2{BScSyD%;3f zCee8VyAhLApV}$Xv}mibUEDEGci`x+* Date: Mon, 4 Mar 2024 12:23:15 +0100 Subject: [PATCH 34/40] Add files via upload --- apps/regattatimer/ChangeLog | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 apps/regattatimer/ChangeLog diff --git a/apps/regattatimer/ChangeLog b/apps/regattatimer/ChangeLog new file mode 100644 index 000000000..81eece2d3 --- /dev/null +++ b/apps/regattatimer/ChangeLog @@ -0,0 +1,3 @@ +0.01: (2024-02-23) initial alpha upload +0.02: (2024-02-23) fixed minor issues with settings +0.03: (2024-03-01) advanced settings, rearanged ui elements, fixed rendering problems From 8734ba9deb4c4b3bfc204b792a1fa5cef9cd8e0d Mon Sep 17 00:00:00 2001 From: naden Date: Mon, 4 Mar 2024 12:23:46 +0100 Subject: [PATCH 35/40] Delete apps/regattatimer/ChnageLog --- apps/regattatimer/ChnageLog | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 apps/regattatimer/ChnageLog diff --git a/apps/regattatimer/ChnageLog b/apps/regattatimer/ChnageLog deleted file mode 100644 index 81eece2d3..000000000 --- a/apps/regattatimer/ChnageLog +++ /dev/null @@ -1,3 +0,0 @@ -0.01: (2024-02-23) initial alpha upload -0.02: (2024-02-23) fixed minor issues with settings -0.03: (2024-03-01) advanced settings, rearanged ui elements, fixed rendering problems From 8190b84ce666559c815619bb8f94fd64b3acea7e Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 4 Mar 2024 12:30:52 +0000 Subject: [PATCH 36/40] Undo changes to non-regattatime files --- README.md | 598 ++++++++++++++++++++++++++++++++++++++++++---- app-icon.js | 1 - app.js | 335 -------------------------- icon.png | Bin 2990 -> 0 bytes regattatimer.json | 8 - screenshot-6.png | Bin 2775 -> 0 bytes settings.js | 78 ------ 7 files changed, 549 insertions(+), 471 deletions(-) delete mode 100644 app-icon.js delete mode 100644 app.js delete mode 100644 icon.png delete mode 100644 regattatimer.json delete mode 100644 screenshot-6.png delete mode 100644 settings.js diff --git a/README.md b/README.md index 8d906e1d7..ed6a501ef 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,574 @@ -# Regatta Timer 5-4-1 countdown +Bangle.js App Loader (and Apps) +================================ -## Modes +[![Build Status](https://github.com/espruino/BangleApps/actions/workflows/nodejs.yml/badge.svg)](https://github.com/espruino/BangleApps/actions/workflows/nodejs.yml) -* **Idle** - On startup the application is in idle mode showing a large 5 in the centre of the screen and the time of day below. - `Button` switches to start mode. -* **Start** - During the countdown, the screen changes the layout several times to use as much space as - possible to display the numbers. - When time is up the buzzer sounds and the application switches to race mode. - `Button` switches to idle mode. -* **Race** - Race time, local time, SOA, number reachable GPS satellites and battery level are shown. - `Button` switches to "stopped mode". -* **Stoped** - The race counter stops. - `Button` switches to idle mode. +* Try the **release version** at [banglejs.com/apps](https://banglejs.com/apps) +* Try the **development version** at [espruino.github.io](https://espruino.github.io/BangleApps/) -## Screenshots +**All software (including apps) in this repository is MIT Licensed - see [LICENSE](LICENSE)** By +submitting code to this repository you confirm that you are happy with it being MIT licensed, +and that it is not licensed in another way that would make this impossible. -*Idle mode: showing a big 5 and time of day below* +## How does it work? -![Idle mode: showing a big 5 and time of day below](screenshot-1.png) +* A list of apps is in `apps.json` (this is auto-generated from all the `apps/yourapp/metadata.json` using Jekyll or `bin/create_apps_json.sh`) +* Each element references an app in `apps/` which is uploaded +* When it starts, BangleAppLoader checks the JSON and compares +it with the files it sees in the watch's storage. +* To upload an app, BangleAppLoader checks the files that are +listed in `apps.json`, loads them, and sends them over Web Bluetooth. -*Start mode: minutes and seconds* +## Getting Started -![Start mode: minutes and seconds](screenshot-2.png) +Check out: -*Start mode: seconds* +* [Building your first Bangle.js Application](https://www.espruino.com/Bangle.js+First+App) +* [Adding an app to the Bangle.js App Loader](https://www.espruino.com/Bangle.js+App+Loader) +* [Customising the App Loader](https://www.espruino.com/Bangle.js+App+Loader+Custom) -![Start mode: seconds](screenshot-3.png) +## What filenames are used -*Race mode: elapsed time, time of day, speed, satellites, battery* +Filenames in storage are limited to 28 characters. To +easily distinguish between file types, we use the following: -![Race mode: elapsed time, time of day, speed, satellites, battery](screenshot-4.png) +* `stuff.info` is JSON that describes an app - this is auto-generated by the App Loader +* `stuff.img` is an image +* `stuff.app.js` is JS code for applications +* `stuff.wid.js` is JS code for widgets +* `stuff.settings.js` is JS code for the settings menu +* `stuff.boot.js` is JS code that automatically gets run at boot time +* `stuff.json` is used for JSON settings for an app -*Race mode: with german abbreviations* +## Developing your own app -![Race mode: with german abbreviations](screenshot-5.png) +* Head over to [the Web IDE](https://www.espruino.com/ide/) and ensure `Save on Send` in settings set to the *default setting* of `To RAM` +* We'd recommend that you start off using code from 'Example Applications' (below) to get started... +* Load [`app.js`](apps/_example_app/app.js) or [`widget.js`](apps/_example_widget/widget.js) into the IDE and start developing. +* The `Upload` button will load your app to Bangle.js temporarily -*Settings page: main* +## Adding your app to the menu -![Settings page: main](screenshot-6.png) +* Come up with a unique (all lowercase, no spaces) name, we'll assume `myappid`. Bangle.js +is limited to 28 char filenames and appends a file extension (eg `.js`) so please +try and keep filenames short to avoid overflowing the buffer. +* Create a folder called `apps/`, lets assume `apps/myappid` +* We'd recommend that you copy files from one of the Examples in `apps/_example_*` (see below), or... +* `apps/myappid/app.png` should be a 48px icon +* Use http://www.espruino.com/Image+Converter to create `apps/myappid/app-icon.js`, using a 1 bit, 4 bit or 8 bit Web Palette "Image String" +* Create/modify `apps/myappid/metadata.json` as follows: -*Settings page: choose the theme* +``` +{ "id": "myappid", + "name": "My app's human readable name", + "shortName" : "Short Name", + "icon": "app.png", + "description": "A detailed description of my great app", + "tags": "", + "storage": [ + {"name":"myappid.app.js","url":"app.js"}, + {"name":"myappid.img","url":"app-icon.js","evaluate":true} + ], +}, +``` -![Settings page: choose the theme](screenshot-7.png) +### Screenshots -## Localization +In the app `metadata.json` file you can add a list of screenshots with a line like: `"screenshots" : [ { "url":"screenshot.png" } ],` -Localization is done by the Bangle.js 2 app "Languages" -* Go to [banglejs.com/apps](https://banglejs.com/apps/) -* Search for app "Languages" -* Click the "arrow up" or "burger" icon -* Choose your language from the dropdown -* Click `upload` +To get a screenshot you can: -**Some nautical abbreviations which are not part of the Bangle.js 2 app "Languages" app are stored in `translations.json`.** +* Type `g.dump()` in the left-hand side of the Web IDE when connected to a Bangle.js 2 - you can then +right-click and save the image shown in the terminal (this only works on Bangle.js 2 - Bangle.js 1 is +unable to read data back from the LCD controller). +* Run your code in the emulator and use the screenshot button in the bottom right of the window. -## Feedback -Report bugs or request a feature at [github.com/naden](https://github.com/naden) +## Testing -## Roadmap -* add a seconds coundown layout; mimic a classic regatta chronograph -* add recording of gps course and race time -* add icons for light mode -* add flag icons +### Online -## Created by -© 2021 - 2024 [naden.de](https://naden.de) +This is the best way to test... -Icons by [Icons8](https://icons8.com/) +* Fork the https://github.com/espruino/BangleApps git repository +* Add your files +* Go to GitHub Settings and activate GitHub Pages +* Run your personal `Bangle App Loader` at https://\.github.io/BangleApps/index.html to load apps onto your device +* Your apps should be inside it - if there are problems, check your web browser's 'developer console' for errors + +**Note:** It's a great idea to get a local copy of the repository on your PC, +then run `bin/sanitycheck.js` - it'll run through a bunch of common issues +that there might be. To get the project running locally, you have to initialize and update the git submodules first: `git submodule update --init`. + +Be aware of the delay between commits and updates on github.io - it can take a few minutes (and a 'hard refresh' of your browser) for changes to take effect. + +### Offline + +Using the 'Storage' icon in [the Web IDE](https://www.espruino.com/ide/) +(4 discs), upload your files into the places described in your JSON: + +* `app-icon.js` -> `myappid.img` + +Now load `app.js` up in the editor, and click the down-arrow to the bottom +right of the `Send to Espruino` icon. Click `Storage` and then either choose +`myappid.app.js` (if you'd uploaded your app previously), or `New File` +and then enter `myappid.app.js` as the name. + +Now, clicking the `Send to Espruino` icon will load the app directly into +Espruino **and** will automatically run it. + +When you upload code this way, your app will even be uploaded to Bangle.js's menu +without you having to use the `Bangle App Loader` + +**Note:** Widgets need to be run inside a clock or app, so if you're +developing a widget you need to go go `Settings` -> `Communications` -> `Load after saving` +and set it to `Load default application`. + +## Example Applications + +To make the process easier we've come up with some example applications that you can use as a base +when creating your own. Just come up with a unique name (ideally lowercase, under 20 chars), copy `apps/_example_app` +or `apps/_example_widget` to `apps/myappid`, and edit `apps/myappid/metadata.json` accordingly. + +**Note:** the max filename length is 28 chars, so we suggest an app ID of under +20 so that when `.app.js`/etc gets added to the end the filename isn't cropped. + +**If you're making a widget** please start the name with `wid` to make +it easy to find! + +### App Example + +The app example is available in [`apps/_example_app`](apps/_example_app) + +Apps are listed in the Bangle.js menu, accessible from a clock app via the middle button. + +* `metadata.json` - describes the app to bootloader and loader +* `app.png` - app icon - 48x48px +* `app-icon.js` - JS version of the icon (made with http://www.espruino.com/Image+Converter) for use in Bangle.js's menu +* `app.js` - app code +* `ChangeLog` - A file containing a list of changes to your app so users can see what's changed + +#### `app-icon.js` + +The icon image and short description is used in Bangle.js's launcher. + +Use the Espruino [image converter](https://www.espruino.com/Image+Converter) and upload your `app.png` file. + +Follow this steps to create a readable icon as image string. + +1. upload a 48x48 png file - THE IMAGE SHOULD BE 48x48 OR LESS +2. set _X_ Use Compression +3. set _X_ Transparency (optional) +4. set Diffusion: _flat_ +5. set Colours: _1 bit_, any of the Optimised options, or _8 bit Web Palette_ are best +6. set Output as: _Image String_ + +Replace this line with the image converter output: + +``` +require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA==")) +``` + +**Do not add a trailing semicolon** + +You can also use this converter for creating images you like to draw with `g.drawImage()` with your app. + +Apps that need widgets can call `Bangle.loadWidgets()` **once** at startup to load +them, and then `Bangle.drawWidgets()` to draw them onto the screen whenever the app +has call to completely clear the screen. Widgets themselves will update as and when needed. + +### Widget Example + +The widget example is available in [`apps/_example_widget`](apps/_example_widget) + +* `metadata.json` - describes the widget to bootloader and loader +* `widget.js` - widget code + +Widgets are just small bits of code that run whenever an app that supports them +calls `Bangle.loadWidgets()`. If they want to display something in the 24px high +widget bar at the top of the screen they can add themselves to the global +`WIDGETS` array with: + +``` +WIDGETS["mywidget"]={ + area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right) + sortorder:0, // (Optional) determines order of widgets in the same corner + width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout + draw:draw // called to draw the widget +}; +``` + +When the widget is to be drawn, `x` and `y` values are set up in `WIDGETS["mywidget"]` +and `draw` can then use `this.x` and `this.y` to figure out where it needs to draw to. + + +### ChangeLog + +This is a file containing a list of changes to your app so users can see what's changed, for example: + +``` +0.01: New App! +0.02: Changed the colors +0.03: Made the app run quicker +``` + +Entries should be newest last, with the version number of the last entry matching the version in `metadata.json` + +Please keep the same format at the example as the file needs to be parsed by the BangleApps tools. + +### `app.info` format + +This is the file that's **auto-generated** from `metadata.json` and loaded onto Bangle.js by the App Loader, +and which gives information about the app for the Launcher. + +``` +{ + "name":"Short Name", // for Bangle.js menu + "icon":"*myappid", // for Bangle.js menu + "src":"-myappid", // source file + "type":"widget/clock/app/bootloader/...", // optional, default "app" + // see 'type' in 'metadata.json format' below for more options/info + "version":"1.23", + // added by BangleApps loader on upload based on metadata.json + "files:"file1,file2,file3", + // added by BangleApps loader on upload - lists all files + // that belong to the app so it can be deleted + "data":"appid.data.json,appid.data?.json;appidStorageFile,appidStorageFile*" + // added by BangleApps loader on upload - lists files that + // the app might write, so they can be deleted on uninstall + // typically these files are not uploaded, but created by the app + // these can include '*' or '?' wildcards +} +``` + +### `metadata.json` format + +``` +{ "id": "appid", // 7 character app id + "name": "Readable name", // readable name + "shortName": "Short name", // short name for launcher + "version": "0v01", // the version of this app + "description": "...", // long description (can contain markdown) + "icon": "icon.png", // icon in apps/ + "screenshots" : [ { "url":"screenshot.png" } ], // optional screenshot for app + "type":"...", // optional(if app) - + // 'app' - an application + // 'clock' - a clock - required for clocks to automatically start + // 'widget' - a widget + // 'module' - this provides a module that can be used with 'require'. + // 'provides_modules' should be used if type:module is specified + // 'bootloader' - an app that at startup (app.boot.js) but doesn't have a launcher entry for 'app.js' + // 'settings' - apps that appear in Settings->Apps (with appname.settings.js) but that have no 'app.js' + // 'clkinfo' - Provides a 'myapp.clkinfo.js' file that can be used to display info in clocks - see modules/clock_info.js + // 'RAM' - code that runs and doesn't upload anything to storage + // 'launch' - replacement 'Launcher' + // 'textinput' - provides a 'textinput' library that allows text to be input on the Bangle + // 'scheduler' - provides 'sched' library and boot code for scheduling alarms/timers + // (currently only 'sched' app) + // 'notify' - provides 'notify' library for showing notifications + // 'locale' - provides 'locale' library for language-specific date/distance/etc + // (a version of 'locale' is included in the firmware) + "tags": "", // comma separated tag list for searching + // common types are: + // 'clock' - it's a clock + // 'widget' - it is (or provides) a widget + // 'outdoors' - useful for outdoor activities + // 'tool' - a useful utility (timer, calculator, etc) + // 'game' - a game + // 'bluetooth' - uses Bluetooth LE + // 'system' - used by the system + // 'clkinfo' - provides or uses clock_info module for data on your clock face or clocks that support it (see apps/clock_info/README.md) + // 'health' - e.g. heart rate monitors or step counting + "supports": ["BANGLEJS2"], // List of device IDs supported, either BANGLEJS or BANGLEJS2 + "dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above) + "dependencies" : { "messages":"app" } // optional, depend on a specific app ID + // for instance this will use notify/notifyfs is they exist, or will pull in 'notify' + "dependencies" : { "messageicons":"module" } // optional, depend on a specific library to be used with 'require' - see provides_modules + "dependencies" : { "message":"widget" } // optional, depend on a specific type of widget - see provides_widgets + "provides_modules" : ["messageicons"] // optional, this app provides a module that can be used with 'require' + "provides_widgets" : ["battery"] // optional, this app provides a type of widget - 'alarm/battery/bluetooth/pedometer/message' + "default" : true, // set if an app is the default implementer of something (a widget/module/etc) + "readme": "README.md", // if supplied, a link to a markdown-style text file + // that contains more information about this app (usage, etc) + // A 'Read more...' link will be added under the app + + "custom": "custom.html", // if supplied, apps/custom.html is loaded in an + // iframe, and it must post back an 'app' structure + // like this one with 'storage','name' and 'id' set up + // see below for more info + + "customConnect": true, // if supplied, ensure we are connected to a device + // before the "custom.html" iframe is loaded. An + // onInit function in "custom.html" is then called + // with info on the currently connected device. + + "interface": "interface.html", // if supplied, apps/interface.html is loaded in an + // iframe, and it may interact with the connected Bangle + // to retrieve information from it + // see below for more info + + "allow_emulator":true, // if 'app.js' will run in the emulator, set to true to + // add an icon to allow your app to be tested + + "storage": [ // list of files to add to storage + {"name":"appid.js", // filename to use in storage. + // If name=='RAM', the code is sent directly to Bangle.js and is not saved to a file + "url":"", // URL of file to load (currently relative to apps/) + "content":"...", // if supplied, this content is loaded directly + "evaluate":true, // if supplied, data isn't quoted into a String before upload + // (eg it's evaluated as JS) + "noOverwrite":true // if supplied, this file will not be overwritten if it + // already exists + "supports": ["BANGLEJS2"]// if supplied, this file will ONLY be uploaded to the device + // types named in the array. This allows different versions of + // the app to be uploaded for different platforms + }, + ] + "data": [ // list of files the app writes to + {"name":"appid.data.json", // filename used in storage + "storageFile":true // if supplied, file is treated as storageFile + "url":"", // if supplied URL of file to load (currently relative to apps/) + "content":"...", // if supplied, this content is loaded directly + "evaluate":true, // if supplied, data isn't quoted into a String before upload + // (eg it's evaluated as JS) + }, + {"wildcard":"appid.data.*" // wildcard of filenames used in storage + }, // this is mutually exclusive with using "name" + ], + "sortorder" : 0, // optional - choose where in the list this goes. + // this should only really be used to put system + // stuff at the top +} +``` + +* name, icon and description present the app in the app loader. +* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher`, `bluetooth` or empty. +* storage is used to identify the app files and how to handle them +* data is used to clean up files when the app is uninstalled + +### `metadata.json`: `custom` element + +Apps that can be customised need to define a `custom` element in `metadata.json`, +which names an HTML file in that app's folder. + +When `custom` is defined, the 'upload' button is replaced by a customize +button, and when clicked it opens the HTML page specified in an iframe. + +In that HTML file you're then responsible for handling a button +press and calling `sendCustomizedApp` with your own customised +version of what's in `metadata.json`: + +``` + + + + + +

+ + + + +``` + +This'll then be loaded in to the watch. See [apps/qrcode/grcode.html](the QR Code app) +for a clean example. + +**Note:** we specify a `url` for JS files even though it doesn't have to exist +and will never be loaded. This is so the app loader can tell if it's a JavaScript +file based on the extension, and if so it can minify and pretokenise it. + +### `metadata.json`: `interface` element + +Apps that create data that can be read back can define a `interface` element in `metadata.json`, +which names an HTML file in that app's folder. + +When `interface` is defined, a `Download from App` button is added to +the app's description, and when clicked it opens the HTML page specified +in an iframe. + +``` + + + + + + +
Loading...
+ + + +``` + +When the page is ready a function called `onInit` is called, +and in that you can call `Puck.write` and `Puck.eval` to get +the data you require from Bangle.js. + +See [apps/gpsrec/interface.html](the GPS Recorder) for a full example. + +### Adding configuration to the "Settings" menu + +Apps (or widgets) can add their own settings to the "Settings" menu under "App/widget settings". +To do so, the app needs to include a `settings.js` file, containing a single function +that handles configuring the app. +When the app settings are opened, this function is called with one +argument, `back`: a callback to return to the settings menu. + +Usually it will save any information in `myappid.json` where `myappid` is the name +of your app - so you should change the example accordingly. + +Example `settings.js` +```js +// make sure to enclose the function in parentheses +(function(back) { + let settings = require('Storage').readJSON('myappid.json',1)||{}; + if (typeof settings.monkeys !== "number") settings.monkeys = 12; // default value + function save(key, value) { + settings[key] = value; + require('Storage').write('myappid.json', settings); + } + const appMenu = { + '': {'title': 'App Settings'}, + '< Back': back, + 'Monkeys': { + value: settings.monkeys, + onchange: (m) => {save('monkeys', m)} + } + }; + E.showMenu(appMenu) +}) +``` +In this example the app needs to add `myappid.settings.js` to `storage` in `metadata.json`. +It should also add `myappid.json` to `data`, to make sure it is cleaned up when the app is uninstalled. +```json + { "id": "myappid", + ... + "storage": [ + ... + {"name":"myappid.settings.js","url":"settings.js"} + ], + "data": [ + {"name":"myappid.json"} + ] + }, +``` + +## Modules + +You can include any of [Espruino's modules](https://www.espruino.com/Modules) as +normal with `require("modulename")`. To include [Bangle's modules](modules) for use in the Web +IDE, [upload the modules to internal storage](modules#upload-the-module-to-the-bangles-internal-storage) +or [change the IDE's search path](modules#change-the-web-ide-search-path-to-include-banglejs-modules). +If you want to develop your own module for your +app(s) then you can do that too. Just add the module into the `modules` folder +then you can use it from your app as normal. + +You won't be able to develop apps using your own modules with the IDE, +so instead we'd recommend you write your module to a Storage File called +`modulename` on Bangle.js. You can then develop your app as normal on Bangle.js +from the IDE. + +## Coding hints + +- use `g.setFont(.., size)` to multiply the font size, eg ("6x8",3) : "18x24" + +- use `g.drawString(text,x,y,true)` to draw with background color to overwrite existing text + +- use `g.clearRect()` to clear parts of the screen, instead of using `g.clear()` + +- use `g.fillPoly()` or `g.drawImage()` for complex graphic elements + +- using `g.clear()` can cause screen flicker + +- using `g.setLCDBrightness()` can save you power during long periods with lcd on + +- chaining graphics methods, eg `g.setColor(0xFD20).setFontAlign(0,0).setfont("6x8",3)` + +### Misc Notes + +- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `myappid.json`, then load it at startup. + +- 'Alarm' apps define a file called `alarm.js` which handles the actual alarm window. + +- Locale is handled by `require("locale")`. An app may create a `locale` file in Storage which is +a module that overwrites Bangle.js's default locale. + + +### Graphic areas + +The screen is parted in a widget and app area for lcd mode `direct`(default). + +| areas | as rectangle or point | +| :-:| :-: | +| Widget | (0,0,239,23) | +| Apps | (0,24,239,239) | +| BTN1 | (230, 55) | +| BTN2 | (230, 140) | +| BTN3 | (230, 210) | +| BTN4 | (0,0,119, 239)| +| BTN5 | (120,0,239,239) | + +- Use `g.setFontAlign(0, 0, 3)` to draw rotated string to BTN1-BTN3 with `g.drawString()`. + +- For BTN4-5 the touch area is named + +## Available colors + +You can use `g.setColor(r,g,b)` OR `g.setColor(16bitnumber)` - some common 16 bit colors are below: + +| color-name | color-value| +| :-: | :-: | +| Black | 0x0000 | +| Navy | 0x000F | +| DarkGreen | 0x03E0 | +| DarkCyan | 0x03EF | +| Maroon | 0x7800 | +| Purple | 0x780F | +| Olive | 0x7BE0 +| LightGray | 0xC618 +| DarkGrey | 0x7BEF +| Blue | 0x001F +| Green | 0x07E0 | +| Cyan | 0x07FF | +| RED | 0xF800 | +| Magenta | 0xF81F | +| Yellow | 0xFFE0 | +| White | 0xFFFF | +| Orange | 0xFD20 | +| GreenYellow | 0xAFE5 | +| Pink | 0xF81F | + +## API Reference + +[Reference](http://www.espruino.com/Reference#software) + +[Bangle Class](https://banglejs.com/reference#Bangle) + +[Graphics Class](https://banglejs.com/reference#Graphics) + +## 'Testing' folder + +The [`testing`](testing) folder contains snippets of code that might be useful for your apps. + +* `testing/colors.js` - 16 bit colors as name value pairs +* `testing/gpstrack.js` - code to store a GPS track in Bangle.js storage and output it back to the console + +## Credits + +The majority of icons used for these apps are from [Icons8](https://icons8.com/) - we have a commercial license but icons are also free for Open Source projects. diff --git a/app-icon.js b/app-icon.js deleted file mode 100644 index 3c6f27c44..000000000 --- a/app-icon.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("qFQ4UB8H/AAIJBoGtqoACDZYPDCRwUGqATNgoTDoATNgISCqhtPio6QHgg6OHggJGn+q1X8PJAHFnwSBAAO8CYxiFgeq1/Agf61XAMgpiFnWvAof61hkFCYkD1YhEgfqAwkFOwk62EAhkCwEwgEOFAkBCYgfCnYTB9gvCCZECDwMshgGBmE4GAOACY8KHQIjBAAQTBh2gCYY6EgHwTIsPgA8EAAeogAeDGAcAlQSGgQRBE5EKKAYdDA4wfGYgo6HHgbKFgHrgB3BAA0OBgQAEDQw0Hcog5IHojyEgWwWAgAFncOOAkO4ATLgZbENYIAMJIk7CZo0ElcDCRfA9AFD9A8M0ErFgjlBABXwJQiyMWgyyMQwrGBmASLhjIDUgPs//wBoc8ZAwTEmGqKQiEDh2shATDdwMA16nE1ADCh4dBeAcCAYJlEgQTDYoQPCAYevFwQfBYAgTH0eqFAcKlATEhQTG2GsgeqgE4CYM6LQITHDoUD/8A9fq4E/SYI7HgfwO4fq1RVDCY2wgevT4krfgqLDWYXqI4IACE4cO1XMY4vsn41DgBSBBgX/bYugmD/HAAcMhQgDYogAJhRWFABk6XQkPCRfwnYFD9AtEAA+gSQgEEABPoAgYsEABLTDNAo8KAgakBDQgAFnbCBAwbwBCZbuDZAayMc4i0NWQgAB9cAIYhbEBgQaGHpCDBGg0KKwgAFgQeGA4XwBIr5BD5GoHg46BlQwH9QnJ1YGDoAeDO4MsBYc4O4IwDgITDgRsBlgiBFgITBnRODCYg8BXgM6gWADIMDHQgTFnQ8BhgTBmA6BTokBqDyE1blEgYvCAAUFCYgoB14FD/TEFgtUAwkD1Wv4ED/WqEwkAitVTIs+1QAC3gLFqoTGgE/CQP8BQwTBPAgALgITBMgoAKgoTBMgoAKMQI8QHQQ8QHQQACCZoSEChoPD")) diff --git a/app.js b/app.js deleted file mode 100644 index 30d03daed..000000000 --- a/app.js +++ /dev/null @@ -1,335 +0,0 @@ -/** - * Regatta Timer - */ -const Layout = require("Layout"); -const locale = require("locale").name == "system" ? "en" : require("locale").name.substring(0, 2); -const hs = require("heatshrink"); - -// "Anton" bold font -Graphics.prototype.setFontAnton = function(scale) { - // Actual height 69 (68 - 0) - g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAA/gAAAAAAAAAAP/gAAAAAAAAAH//gAAAAAAAAB///gAAAAAAAAf///gAAAAAAAP////gAAAAAAD/////gAAAAAA//////gAAAAAP//////gAAAAH///////gAAAB////////gAAAf////////gAAP/////////gAD//////////AA//////////gAA/////////4AAA////////+AAAA////////gAAAA///////wAAAAA//////8AAAAAA//////AAAAAAA/////gAAAAAAA////4AAAAAAAA///+AAAAAAAAA///gAAAAAAAAA//wAAAAAAAAAA/8AAAAAAAAAAA/AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////AAAAAB///////8AAAAH////////AAAAf////////wAAA/////////4AAB/////////8AAD/////////+AAH//////////AAP//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wA//8AAAAAB//4A//wAAAAAAf/4A//gAAAAAAP/4A//gAAAAAAP/4A//gAAAAAAP/4A//wAAAAAAf/4A///////////4Af//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH//////////AAD/////////+AAB/////////8AAA/////////4AAAP////////gAAAD///////+AAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAAAAAAAAAP/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/AAAAAAAAAAA//AAAAAAAAAAA/+AAAAAAAAAAB/8AAAAAAAAAAD//////////gAH//////////gAP//////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAAAB/gAAD//4AAAAf/gAAP//4AAAB//gAA///4AAAH//gAB///4AAAf//gAD///4AAA///gAH///4AAD///gAP///4AAH///gAP///4AAP///gAf///4AAf///gAf///4AB////gAf///4AD////gA////4AH////gA////4Af////gA////4A/////gA//wAAB/////gA//gAAH/////gA//gAAP/////gA//gAA///8//gA//gAD///w//gA//wA////g//gA////////A//gA///////8A//gA///////4A//gAf//////wA//gAf//////gA//gAf/////+AA//gAP/////8AA//gAP/////4AA//gAH/////gAA//gAD/////AAA//gAB////8AAA//gAA////wAAA//gAAP///AAAA//gAAD//8AAAA//gAAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAD/wAAB//8AAAAP/wAAB///AAAA//wAAB///wAAB//wAAB///4AAD//wAAB///8AAH//wAAB///+AAP//wAAB///+AAP//wAAB////AAf//wAAB////AAf//wAAB////gAf//wAAB////gA///wAAB////gA///wAAB////gA///w//AAf//wA//4A//AAA//wA//gA//AAAf/wA//gB//gAAf/wA//gB//gAAf/wA//gD//wAA//wA//wH//8AB//wA///////////gA///////////gA///////////gA///////////gAf//////////AAf//////////AAP//////////AAP/////////+AAH/////////8AAH///+/////4AAD///+f////wAAA///8P////gAAAf//4H///+AAAAH//gB///wAAAAAP4AAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAAAAAAAAAA//wAAAAAAAAAP//wAAAAAAAAB///wAAAAAAAAf///wAAAAAAAH////wAAAAAAA/////wAAAAAAP/////wAAAAAB//////wAAAAAf//////wAAAAH///////wAAAA////////wAAAP////////wAAA///////H/wAAA//////wH/wAAA/////8AH/wAAA/////AAH/wAAA////gAAH/wAAA///4AAAH/wAAA//+AAAAH/wAAA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAH/4AAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//8AAA/////+B///AAA/////+B///wAA/////+B///4AA/////+B///8AA/////+B///8AA/////+B///+AA/////+B////AA/////+B////AA/////+B////AA/////+B////gA/////+B////gA/////+B////gA/////+A////gA//gP/gAAB//wA//gf/AAAA//wA//gf/AAAAf/wA//g//AAAAf/wA//g//AAAA//wA//g//gAAA//wA//g//+AAP//wA//g////////gA//g////////gA//g////////gA//g////////gA//g////////AA//gf///////AA//gf//////+AA//gP//////+AA//gH//////8AA//gD//////4AA//gB//////wAA//gA//////AAAAAAAH////8AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////gAAAAB///////+AAAAH////////gAAAf////////4AAB/////////8AAD/////////+AAH//////////AAH//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wAf//////////4A//wAD/4AAf/4A//gAH/wAAP/4A//gAH/wAAP/4A//gAP/wAAP/4A//gAP/4AAf/4A//wAP/+AD//4A///wP//////4Af//4P//////wAf//4P//////wAf//4P//////wAf//4P//////wAP//4P//////gAP//4H//////gAH//4H//////AAH//4D/////+AAD//4D/////8AAB//4B/////4AAA//4A/////wAAAP/4AP////AAAAB/4AD///4AAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAADgA//gAAAAAAP/gA//gAAAAAH//gA//gAAAAB///gA//gAAAAP///gA//gAAAD////gA//gAAAf////gA//gAAB/////gA//gAAP/////gA//gAB//////gA//gAH//////gA//gA///////gA//gD///////gA//gf///////gA//h////////gA//n////////gA//////////gAA/////////AAAA////////wAAAA///////4AAAAA///////AAAAAA//////4AAAAAA//////AAAAAAA/////4AAAAAAA/////AAAAAAAA////8AAAAAAAA////gAAAAAAAA///+AAAAAAAAA///4AAAAAAAAA///AAAAAAAAAA//4AAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gB///wAAAAP//4H///+AAAA///8P////gAAB///+f////4AAD///+/////8AAH/////////+AAH//////////AAP//////////gAP//////////gAf//////////gAf//////////wAf//////////wAf//////////wA///////////wA//4D//wAB//4A//wB//gAA//4A//gA//gAAf/4A//gA//AAAf/4A//gA//gAAf/4A//wB//gAA//4A///P//8AH//4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////gAP//////////gAP//////////AAH//////////AAD/////////+AAD///+/////8AAB///8f////wAAAf//4P////AAAAH//wD///8AAAAA/+AAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAB///+AA/+AAAAP////gA//wAAAf////wA//4AAB/////4A//8AAD/////8A//+AAD/////+A///AAH/////+A///AAP//////A///gAP//////A///gAf//////A///wAf//////A///wAf//////A///wAf//////A///wA///////AB//4A//4AD//AAP/4A//gAB//AAP/4A//gAA//AAP/4A//gAA/+AAP/4A//gAB/8AAP/4A//wAB/8AAf/4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH/////////+AAD/////////8AAB/////////4AAAf////////wAAAP////////AAAAB///////4AAAAAD/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAB/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("EiAnGicnJycnJycnEw=="), 78 + (scale << 8) + (1 << 16)); -}; - -function Regattatimer() { - return { - layout: undefined, - /* - layouts: { - idle: function() { - switch(settings.dial) { - case "Discs": - break; - case "Numeric": - default: - break; - } - }, - start: function(phase) { - switch(settings.dial) { - case "Discs": - break; - case "Numeric": - default: - break; - } - }, - race: function() { - - } - }, - */ - mode: "idle", // idle, start, race" - countdown: 300, // 5 minutes - counter: undefined, - interval: undefined, - theme: null, - themes: { - "Light": { - "fgColor": "#000000", - "bgColor": "#FFFF00", - "icons": { - "satellites": function() { - return hs.decompress(atob("jEYxH+AH4Ab6QIIBJAfNAAQtSC4gxSCwgYHHBYYMC6IYPC5AZOC8QYMC5YYLC5inSDH4waVbAYJCpgA/AAI=")); - }, - "battery": function() { - return hs.decompress(atob("jEYxH+AHHSAAgXmCgoaRC/4X/C/4X/C/4X/C64Ap")); - } - } - }, - "Dark": { - "fgColor": "#FFFF00", - "bgColor": "#000000", - "icons": { - "satellites": function() { - return hs.decompress(atob("jEYxH+AH4Ab6QIIBJAfNAAQtSC4gxSCwgYHHBYYMC6IYPC5AZOC8QYMC5YYLC5inSDH4waVbAYJCpgA/AAI=")); - }, - "battery": function() { - return hs.decompress(atob("jEYxH+AHHSAAgXmCgoaRC/4X/C/4X/C/4X/C64Ap")); - } - } - } - }, - settings: Object.assign({ - "debug": false, - "buzzer": true, - "dial": "Numeric", - "gps": true, - "record": false, - "theme": "Dark", - }, require('Storage').readJSON("regattatimer.json", true) || {}), - - translations: Object.assign({ - "de": { - "speed": "FüG", // Fahrt über Grund - "speed_unit": "kn" - }, - "en": { - "speed": "SOA", // SOA speed of advance - "speed_unit": "kn" - } - }, require('Storage').readJSON("translations.json", true) || {}), - - init: function() { - - if(this.settings.debug) { - this.countdown = 1; - } - - this.theme = this.themes[this.settings.theme]; - - Bangle.setLCDPower(1); - Bangle.setLCDTimeout(0); - - // in "idle", "start" or "stoped" mode, a button click (re)starts the countdown - // in "race" mode, a button click stops the counter - var onButtonClick = (function(ev) { - switch(this.mode) { - case "idle": - this.resetCounter(); - this.mode = "start"; - this.setLayoutStartMinSec(); - this.startCounter(); - this.interval = setInterval((function() { - this.startCounter(); - }).bind(this), 1000); - break; - case "stoped": - case "start": - this.resetCounter(); - this.setLayoutIdle(); - break; - case "race": - this.raceCounterStop(); - break; - } - }).bind(this); - - setWatch(onButtonClick, BTN1, true); - - this.setLayoutIdle(); - }, - - onGPS: function(fix) { - if(this.mode == "race") { - if(fix.fix && isFinite(fix.speed)) { - this.layout.clear(layout.speed); - this.layout.speed.label = fix.speed.toFixed(2); - this.layout.render(this.layout.speed); - } - this.layout.satellites.label = fix.satellites; - } - }, - - translate: function(slug) { - return this.translations[locale][slug]; - }, - // during the start phase, the clock counts down 5 4 1 0 minutes - // a button click restarts the countdown - startCounter: function() { - - this.counter --; - - if(this.counter >= 0) { - var counterMinutes = parseInt(this.counter / 60); - - if(counterMinutes > 0) { - this.layout.minutes.label = counterMinutes; - // this.layout.seconds.label = "0".concat(this.counter - counterMinutes * 60).toString().slice(-2); - this.layout.seconds.label = this.padZeroLeft(this.counter - counterMinutes * 60); - this.layout.render(); - } - else { - this.setLayoutStartSec(); - this.layout.seconds.label = this.counter.toString(); - this.layout.render(); - } - // this keeps the watch LCD lit up - g.flip(); - } - // time is up - else { - this.raceCounterStart(); - } - }, - padZeroLeft: function(str) { - return str.toString().padStart(2, "0"); - }, - formatTime: function(time) { - var - minutes = parseInt(time / 60), - seconds = time - (minutes * 60); - - return this.padZeroLeft(parseInt(time / 3600)) + ":" + this.padZeroLeft(minutes) + ":" + this.padZeroLeft(seconds); - }, - raceCounter: function() { - - if(this.counter % 60 == 0) { - this.layout.clear(this.layout.battery); - this.layout.battery.label = E.getBattery() + "%"; - this.layout.render(this.layout.battery); - } - - this.counter ++; - - this.layout.racetime.label = this.formatTime(this.counter); - this.layout.daytime.label = require("locale").time(new Date(), 1); - this.layout.render(); - - // keeps the watch screen lit up - g.flip(); - }, - raceCounterStop: function() { - if(this.interval) { - clearInterval(this.interval); - this.interval = undefined; - } - this.mode = "stoped"; - }, - raceCounterStart: function() { - if(this.interval) { - clearInterval(this.interval); - this.interval = undefined; - } - - if(this.settings.buzzer) { - Bangle.buzz(); - } - - this.counter = 0; - // switch to race mode - this.mode = "race"; - this.setLayoutRace(); - this.raceCounter(); - this.interval = setInterval((function() { - this.raceCounter(); - }).bind(this), 1000); - }, - - resetCounter: function() { - if(this.interval) { - clearInterval(this.interval); - this.interval = undefined; - } - this.counter = this.countdown; - }, - - setLayoutIdle: function() { - - g.clear(); - - this.mode = "idle"; - - this.layout = new Layout({ - type: "v", - bgCol: this.theme.bgColor, - c: [ - { - type: "v", - c: [ - {type: "txt", font: "Anton", label: "5", col: this.theme.fgColor, id: "minutes", fillx: 1, filly: 1}, - {type: "txt", font: "20%", label: "--:--", col: this.theme.fgColor, id: "daytime", fillx: 1, filly: 1} - ] - } - ]}, {lazy: true}); - - this.interval = setInterval((function() { - this.layout.daytime.label = require("locale").time(new Date(), 1); - this.layout.render(); - - // keeps the watch screen lit up - g.flip(); - }).bind(this), 1000); - }, - setLayoutStartMinSec: function() { - g.clear(); - - this.layout = new Layout({ - type: "v", - bgCol: this.theme.bgColor, - c: [ - { - type: "h", - c: [ - {type: "txt", font: "Anton", label: "4", col: this.theme.fgColor, id: "minutes", fillx: 1, filly: 1}, - {type: "txt", font: "Anton", label: "59", col: this.theme.fgColor, id: "seconds", fillx: 1, filly: 1}, - ] - } - ]}, {lazy: true} - ); - }, - setLayoutStartSec: function() { - g.clear(); - - this.layout = new Layout({ - type: "v", - bgCol: this.theme.bgColor, - c:[ - {type: "txt", font: "Anton", label: "", fillx: true, filly: true, col: this.theme.fgColor, id: "seconds"}, - ]}, {lazy: true}); - }, - setLayoutRace: function() { - g.clear(); - - this.layout = new Layout({ - type: "v", - bgCol: this.theme.bgColor, - c: [ - {type: "txt", font: "20%", label: "00:00:00", col: this.theme.fgColor, pad: 4, filly: 1, fillx: 1, id: "racetime"}, - {type: "txt", font: "15%", label: "-", col: this.theme.fgColor, pad: 4, filly:1, fillx:1, id: "daytime"}, - // horizontal - {type: "h", c: [ - {type: "txt", font: "10%", label: this.translate("speed"), col: this.theme.fgColor, pad:4, fillx:1, filly:1}, - {type: "txt", font: "20%", label: "0", col: this.theme.fgColor, pad:4, fillx:1, filly:1, id: "speed"}, - {type: "txt", font: "10%", label: this.translate("speed_unit"), col: this.theme.fgColor, pad:4, fillx:1, filly:1}, - ]}, - {type: "h", c: [ - {type:"img", pad: 2, src: this.theme["icons"].satellites()}, - {type: "txt", font: "10%", label: "0", col: this.theme.fgColor, pad: 2, filly:1, id: "satellites"}, - // hacky, use empty element with fillx to push the other elments to the left an right side - {type: undefined, pad: 2, fillx: 1}, - {type:"img", pad: 2, src: this.theme["icons"].battery()}, - {type: "txt", font: "10%", label: "-", col: this.theme.fgColor, pad: 2, filly: 1, id: "battery"}, - ]} - ]}, {lazy: true}); - } - }; -} - -var regattatimer = Regattatimer(); -regattatimer.init(); - -if(regattatimer.settings.gps) { - Bangle.setGPSPower(1); - Bangle.on('GPS', regattatimer.onGPS.bind(regattatimer)); -} - -Bangle.on('kill', function() { - Bangle.setLCDPower(0); - Bangle.setLCDTimeout(10); - - if(regattatimer.settings.gps) { - Bangle.setGPSPower(0); - } -}); - diff --git a/icon.png b/icon.png deleted file mode 100644 index 47712c7ed12a2f1f18797165d6b063069aa6f7b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2990 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 diff --git a/settings.js b/settings.js deleted file mode 100644 index cdcdf53f5..000000000 --- a/settings.js +++ /dev/null @@ -1,78 +0,0 @@ -(function(back) { - var - file = "regattatimer.json", - - storage = require("Storage"), - - dials = ["Numeric", "Discs"], - - themes = ["Light", "Dark"], - - settings = Object.assign({ - "debug": false, - "buzzer": true, - "dial": "Numeric", - "gps": true, - "record": false, - "theme": "Dark", - }, storage.readJSON(file, true) || {}); - - function save(key, value) { - settings[key] = value; - storage.writeJSON(file, settings); - } - - E.showMenu({ - "" : { "title" : "Regatta Timer" }, - "< Back" : () => back(), - "GPS": { - value: !!settings.gps, // !! converts undefined to false - onchange: v => { - save("gps", v); - } - }, - "THEME": { - value: themes.indexOf(settings.theme), - min: 0, - max: themes.length - 1, - step: 1, - wrap: true, - format: v => themes[v], - onchange: (d) => { - save("theme", themes[d]); - } - }, - "BUZZER": { - value: !!settings.buzzer, // !! converts undefined to false - onchange: v => { - save("buzzer", v); - } - }, - /* - "DIAL": { - value: dials.indexOf(settings.dial), - min: 0, - max: dials.length - 1, - step: 1, - wrap: true, - format: v => dials[v], - onchange: (d) => { - save("dial", dials[d]); - } - }, - "RECORD": { - value: !!settings.record, // 0| converts undefined to 0 - onchange: v => { - settings.record = v; - save("record", v); - } - }, - */ - "DEBUG": { - value: !!settings.debug, // 0| converts undefined to 0 - onchange: v => { - save("debug", v); - } - }, - }); -})(load) From 490ad702f62b0710dd7f5971321bae695fa4fac9 Mon Sep 17 00:00:00 2001 From: naden Date: Mon, 4 Mar 2024 14:08:42 +0100 Subject: [PATCH 37/40] Add files via upload --- apps/regattatimer/ChangeLog | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/regattatimer/ChangeLog b/apps/regattatimer/ChangeLog index 81eece2d3..aea920eea 100644 --- a/apps/regattatimer/ChangeLog +++ b/apps/regattatimer/ChangeLog @@ -1,3 +1,3 @@ -0.01: (2024-02-23) initial alpha upload -0.02: (2024-02-23) fixed minor issues with settings -0.03: (2024-03-01) advanced settings, rearanged ui elements, fixed rendering problems +0.1: (2024-02-23) initial alpha upload +0.2: (2024-02-23) fixed minor issues with settings +0.3: (2024-03-01) advanced settings, rearanged ui elements, fixed rendering problems From 000543edf30abe5d5f3ab77cac7712bd37c4f431 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Mon, 4 Mar 2024 22:14:50 +0000 Subject: [PATCH 38/40] widlockunlock: update description --- apps/widlockunlock/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/widlockunlock/metadata.json b/apps/widlockunlock/metadata.json index f98f29d63..c3f871c84 100644 --- a/apps/widlockunlock/metadata.json +++ b/apps/widlockunlock/metadata.json @@ -2,7 +2,7 @@ "id": "widlockunlock", "name": "Lock/Unlock Widget", "version": "0.03", - "description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked, or an unlock icon otherwise. Tap to lock the lcd", + "description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked, or an unlock icon otherwise. Tap to lock the lcd (unless the back button is shown, in which case it takes priority)", "icon": "widget.png", "type": "widget", "tags": "widget,lock", From 0b15dfa48f0a7c035efb7108f31a2c663a1dc41b Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 6 Mar 2024 09:17:15 +0000 Subject: [PATCH 39/40] 0.06: Add message icon for 'molly' and 'threema libre' --- apps/messageicons/ChangeLog | 1 + apps/messageicons/icons/generate.js | 15 ++++++++------- apps/messageicons/icons/icon_names.json | 2 ++ apps/messageicons/lib.js | 12 +++++------- apps/messageicons/metadata.json | 2 +- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/messageicons/ChangeLog b/apps/messageicons/ChangeLog index f420615cb..94f7cdd0e 100644 --- a/apps/messageicons/ChangeLog +++ b/apps/messageicons/ChangeLog @@ -4,3 +4,4 @@ Store all icons in a separate binary file (much faster lookup) 0.04: Add message icon for 'clock' 0.05: Add message icon for 'jira' +0.06: Add message icon for 'molly' and 'threema libre' diff --git a/apps/messageicons/icons/generate.js b/apps/messageicons/icons/generate.js index b77cfc26e..e1c26f6bc 100755 --- a/apps/messageicons/icons/generate.js +++ b/apps/messageicons/icons/generate.js @@ -72,7 +72,9 @@ Promise.all(promises).then(function() { require("fs").writeFileSync(__dirname+"/../icons.img", Buffer.from(iconData,"binary")); console.log("Saving library"); - require("fs").writeFileSync(__dirname+"/../lib.js", `exports.getImage = function(msg) { + require("fs").writeFileSync(__dirname+"/../lib.js", `// This file is auto-generated - DO NOT MODIFY +// If you want to add icons, change icons/icon_names.json and re-run icons/generate.js +exports.getImage = function(msg) { if (msg.img) return atob(msg.img); let s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase(); if (msg.id=="music") s="music"; @@ -87,16 +89,15 @@ exports.getColor = function(msg,options) { if (st.iconColorMode == 'mono') return options.default; const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase(); return { - // This file is generated by /icons/generate.js. If you need to modify its content, you should do it there instead. - // generic colors, using B2-safe colors - // DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used + /* generic colors, using B2-safe colors */ ${ /* DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used */"" } "airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/ "mail": "#ff0", "music": "#f0f", "phone": "#0f0", - "sms message": "#0ff", - // brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos) - // all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?) + "sms message": "#0ff", ${ /* + brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos) + all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?) +*/""} "bibel": "#54342c", "bring": "#455a64", "discord": "#5865f2", // https://discord.com/branding diff --git a/apps/messageicons/icons/icon_names.json b/apps/messageicons/icons/icon_names.json index 7c09cd397..de8be9e98 100644 --- a/apps/messageicons/icons/icon_names.json +++ b/apps/messageicons/icons/icon_names.json @@ -80,6 +80,7 @@ { "app":"infinity", "icon":"reddit.png" }, { "app":"slide", "icon":"reddit.png" }, { "app":"signal", "icon":"signal.png" }, + { "app":"molly", "icon":"signal.png" }, { "app":"skype", "icon":"skype.png" }, { "app":"slack", "icon":"slack.png" }, { "app":"snapchat", "icon":"snapchat.png" }, @@ -89,6 +90,7 @@ { "app":"telegram", "icon":"telegram.png" }, { "app":"telegram foss", "icon":"telegram.png" }, { "app":"threema", "icon":"threema.png" }, + { "app":"threema libre", "icon":"threema.png" }, { "app":"tiktok", "icon":"tiktok.png" }, { "app":"to do", "icon":"task.png" }, { "app":"opentasks", "icon":"task.png" }, diff --git a/apps/messageicons/lib.js b/apps/messageicons/lib.js index 306a4ae46..b3ff2d9ff 100644 --- a/apps/messageicons/lib.js +++ b/apps/messageicons/lib.js @@ -1,3 +1,5 @@ +// This file is auto-generated - DO NOT MODIFY +// If you want to add icons, change icons/icon_names.json and re-run icons/generate.js exports.getImage = function(msg) { if (msg.img) return atob(msg.img); let s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase(); @@ -13,16 +15,12 @@ exports.getColor = function(msg,options) { if (st.iconColorMode == 'mono') return options.default; const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase(); return { - // This file is generated by /icons/generate.js. If you need to modify its content, you should do it there instead. - // generic colors, using B2-safe colors - // DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used + /* generic colors, using B2-safe colors */ "airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/ "mail": "#ff0", "music": "#f0f", "phone": "#0f0", - "sms message": "#0ff", - // brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos) - // all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?) + "sms message": "#0ff", "bibel": "#54342c", "bring": "#455a64", "discord": "#5865f2", // https://discord.com/branding @@ -67,4 +65,4 @@ exports.getColor = function(msg,options) { "youtube": "#f00", // https://www.youtube.com/howyoutubeworks/resources/brand-resources/#logos-icons-and-colors }[s]||options.default; }; - + \ No newline at end of file diff --git a/apps/messageicons/metadata.json b/apps/messageicons/metadata.json index 13f218508..827d08a55 100644 --- a/apps/messageicons/metadata.json +++ b/apps/messageicons/metadata.json @@ -1,7 +1,7 @@ { "id": "messageicons", "name": "Message Icons", - "version": "0.05", + "version": "0.06", "description": "Library containing a list of icons and colors for apps", "icon": "app.png", "type": "module", From 932ecced7693a8174abf53464460a44a5c2669cb Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 6 Mar 2024 09:19:44 +0000 Subject: [PATCH 40/40] fix imageconverter on node.js --- webtools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webtools b/webtools index 2ab71a33d..af870d7b8 160000 --- a/webtools +++ b/webtools @@ -1 +1 @@ -Subproject commit 2ab71a33d69bfda40465174ffe57adb03c21fc42 +Subproject commit af870d7b8386bfa824b07b268bce414e4daf3fbb