From baa9516f5e6105d986c8e3d324e2783d2d908bda Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 23 Feb 2024 16:46:48 +0100 Subject: [PATCH 01/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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/35] 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 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 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 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 IM6N<$g1szb*Z=?k literal 2859 zcmb7Gc|6mP8{cLND;jM?_&Sm+N|(tM(LyoD980#6mX_m3u9f&y>YK0otlTYGW}Cts zIhGt1Idg=~=#Z-sHYxmk>)+o$zdxSm{aml-`Qv%Np4W2^>wuAhE5bn_kd*C7Yv)ZY z`&T5kY-(23C+|%Fhd5)bK+I01DG&($+1A?9HNtx?cRcm6>bBeAV;hBnx49vA7h)U` zsHX5}=4DIOT!$UhTVn(QA-W-cLzp<$x_(s*pvw$Zd+aXD_<&fwY-D&t64(ncn$4u) zDQd%9iC-AL+xKr2|O2%7t0E^$4xE;+*gfZ4gVeZaL8d0C6`*3ik@oS9Yg+n+6< zY?>f%1i$*INv+y2R%Q?6ufUGd-Cynmz@<;spS0cV6hJ%1e_CVMGC_x$ANQ6pjom0F zd=lk1rJ-4r%wD(v-KP-;u2Kg9fw9?dC>qD$J-eJ*U!cGN69K;is**V`6sYk7BU15$ zaWVHeI`|MN6U^6z3;KvHby_#;CO3bIJ z|Eaj~-uQP3pbI34EtFHXS1i?wf#m^#2O=aUMjR|LYxz&ZD>V>sH$-ylch&iwz{cN% ziKytLpL-S>!YWUYEs|dKHo#xi2iA7d2j5?|p9xLcfyUhIv1Bv5hFSfQ8{7*UJ7V!f zZv7K)c1Ej0Dre+8X3KEx5ptuq1G|GX)kii9kN>!*EOg|pKcQxh^;k|r%>Qh5C1D?C z%#6Sa%~v4lLb7faXWl_p8pm+e0;FLCNq3WejTk!R0NX3@Pq&HS~O?1$hR$f zFc(rkoF)pOMeBC@cCCLYXYXp8IBAniZ=;RV%KTa?(jWGh3HdY1#o}3aHg{czIXrao zX%1^x-!CzA8`*;OkY~%+n94ZqckT4@wPX<~d_Jd}NA5);D>csqd<`yGe$BA&bav=~ zWy+h(C}qaDM5PleCbDHc<|4eW3)cf6gTbw%>d2HE^3G3x>XO)y((g6B!|y4kP`3z* zQlMv+rY1TK9&x?LZg{D-O>*{wFym$3v#|3OpK9eEMT93({RfQGYw@lAPJERHtdGYq z7s}ptd5U^$@n~xaMJ2ZSKr9xDn=_8rAM|2y7&pp;_Plsykt=paB?PY|2KaA}Rt+N4 zl$eXOT~Ia<@X{|5LeEarct5?TbJgIsmOJj{Ge(Ni%G&dEyGhq;I{szQbp1o^^`pk~ z%1{F?sQ-(B8DIOsjg~a+nH?|7L$ap#qOTR8+eW2CF(NmT+m-U##t#ym_?56Ll$nFj6MooXg|LV2vxS^Ka`*6- z0?sFP+83er{BSR-nS$I2I|1B+9=v$!?_=eVEZz#GK7K>dO$q!|RYqN>tnn6z~8S86(8wogx z88!JS+mt+$j$j2w-qx00)(^%4qB^X*eGWeR)XD>e@>OY^4jNr3g}QY~1vv4YdGugn zQsenA*8qqd|71v;ehA&3!>*r zJ7EDXPhYz$qj0&-(>0EEKc%T0yK^`bxG58|VSd=8Zlr*h5m}^=JHr_X8VHxGi=>B+ zzJ7~pIg-&g9QggH#l4XXW*sNIxx#h(-j8^?{L(ch<(wuBK4g#dIBjPlv7|9?13%Si z?AB@g&|+0)<)+fkC@^>{gk)Qq*HrZ0Vc^#DWD|=llacULd0(zT?2LMnxoVI+{rtT+tLK)zj|RtYUFGMJ#_JTUt) z)A34Vzk9L#Y!*td7tJiR;~K3nK-#?Pn8#Pt zoSe=5!lb6-m7=m|gGw%XL91#HsJ-5y&cASRS0}5zVa(4RHGy8U##g6msbh&I1^6*~ zbb%7O{#i#RZ%UJx~&q!3_t9+uJOp9n^P6>=dZZ!l>Or+|Ln>tXt){}{8&R!80EEte5(W8}9rp>EjtJUbLv4umet0}YTq>aBAqf2Y3~5QY zX6{6S9WG%2Ef;>*$xpi!EHnI2<*M_#dD54)pwiPrxQ?3~dc;v$E&@&SaX0)&7mAZ8 zLC~#D!V>R-94D~+H^}>f<^(N`Qkue_rrEFVZnfm^{xdCqo4SEGa1lBRzhYeIyU|ZS z3cg6FoQs)Zh{!e5r3bryurpceEh{L&nwGTZ8sU84+O<#?ebjNHbn=4#EfHEz&nQuG zkkY_VEcTSEo*xvE3bB)0jP&M}i%}(khUw}R%U5mE*<-dQ(_ZI7AEh;{Lty45MVN6{ zLYH3}BdV(eHrObGU3c~HWD&f94B2j8rU@CwNTRdyPXY|%IhB4Dn48_PYr7zBF#Uvp z`WtAN!B`3#r*?IBmCm-e*WE0;yrZd*!S>y(fxLdHB2JBSLG6ftUtuWgx$Y%2?3)v} z0N%rT*j?L~!~L@t7UMPMcd^IoLF+wRlRdV6PDxfn+a 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/35] 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 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 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/35] 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/35] 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 b2ad6d509d835ac217943afa91a8d1ad5cf7ff91 Mon Sep 17 00:00:00 2001 From: naden Date: Mon, 4 Mar 2024 10:44:01 +0100 Subject: [PATCH 31/35] 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 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 Date: Mon, 4 Mar 2024 12:23:15 +0100 Subject: [PATCH 32/35] 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 33/35] 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 34/35] 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 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; 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 35/35] 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