From 5d13118416b8266165113f606269f501d1b58d08 Mon Sep 17 00:00:00 2001 From: naden Date: Fri, 1 Mar 2024 15:31:42 +0100 Subject: [PATCH] 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 zcmds2`!|$%82`RA(;y78xr|Gzv9U{TLoOlPGE%vul0wZ=<`~0H7?%wD3X$7%v1>)t zmYjs4$)Hnuvka}vh}>`Qx~@Bo)JEC2-G5+rf7~CQ@8^4-=W{;KInQ}6mp$B^<+qZz z0s!P)4mf&ZSn;^tZIuzy3Pl==Siq@^g|Ly-=8x3*=COSKT#0 ztZ?_KKMaU$oWU6vpoh=e;E_gFGZUrQ+feDD53d{+Q!pm-A>>^xt|4v)iZ(@fldx5m z8X}7$n!@8UhFjGdDpelhjpfuDLK-k3pBMEMOg%Y9m_tMxq412FP(>IT8kthjhUmb! zRq)gt=s_c!rv4M|E&M~{JdLJI92m>M0qn((;$;nd&bkqJfTnc)(Tal@|7#X{9k=e|E80(T_hnjmojqEmA@k zU5)&uRF;sk;cdvk2@(vIZa-H}q=UQ{LB)6zxa#NDZXideS}#Ok<@3s}CBqPFSMf~} zLPPHGI!^JX9_ccVW5nRg!(}Q6R+1UQHc#wyak7?`%(B6ldQJZK{Fitur3&V= zptyQh3-?AFjObK78cZ~SAzzyW(HKL_7xc5asGRLNY2@fi(V14Ior)`dsr_+-Z4i6! z9eE_-N+=nJ(w9RU=Ibg&!0N}YUVT;nWl0 z>die62$tNvVGV&iJi{gg2YRMlEW&YMk5qfa00ES?U1v@a0E?7A^GF7G=jnB~%78vi ztF9nrP_f6oBo_l~_Ps8woBt09v$t?plLyj_JKIY%f(H9@lEfn?yoQ_Y&hfsKv3j6$ zlH=q0z*$D5C?5WwP30BjP5s1+&7%>8#NlNHgf*-P-jl zf?3)P_1sv(`A<@{O@<0J#p-r2=GEwLiJfL)0Kns_WwvE6qLaNppdia)-vN}pua zp5#zg9z~++XO}L$oog1nKbp#PPL=K~h_%^ZNM2TD6}GB6HJN!{!g+I(Jfecu9OV`W5X+9Nml(vOSX7}_Q@sg*}mytNWOF_`+;O<`>QJbRo%|F<}ZMF zDzdbiVR>-}VhI-G0(z)*3>KkzHUMz(Xn|#*-Cm}+ulrkCZ?!Gjqj9Y{lV)$GeqS;z z-|$@0_qzs?yPg(r=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 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); } }, });