diff --git a/apps/beeptest/ChangeLog b/apps/beeptest/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/beeptest/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/beeptest/app-icon.js b/apps/beeptest/app-icon.js new file mode 100644 index 000000000..907c45577 --- /dev/null +++ b/apps/beeptest/app-icon.js @@ -0,0 +1,5 @@ +require("heatshrink").decompress( + atob( + "MDDEAP//8/wsMtP8DDIrMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiAAAAAAAAAAAAAAAAABEQAAAAAAAiAAAiAAAAAAAAAAAAAAAREREREAAAAAIgAAACIAAAAAAAAAAAAREREREREQAAAAIgAAACIAAAAAAAAAAAEREREREREREAAAIgAAACIAAAAAAAAAAAEREREQAREREQAAIgAAACIAAAAAAAAAAAAREAAAABEREREAAiAAAiAAAAAAAAAAAAAAAAAAAAEREREQAiIiIiAAAAAAAAAAAAAAAAAAAAARERERAAIiIAAAAAAAAAAAAAAAAAAAAAAREREREAAAAAAAAAAAAAAAAAAAAAAAAAAREREREQAAAAAAAAAAAAAAAAAAAAAAAAMREREREQAAAAAAAAAAIiIiIiIiIiAAAAERERERERAAAAAAAAAAIiIiIiIiIiAAABERERERERAAAAEQAAAAAAAAAAAAAAAAABEREREREREAABERAAAAAAAAAAAAAAAAAREREREREREAARERAAAAAAAAAAAAAAAAEREREREREREQEREQAAAAAAAAAAAAAAAAEREREREQEREREREAAAAAIiIiIiIgAAABEREREREAARERERAAAAAAIiIiIiIgAAARERERERAAABEREQAAAAAAAAAAAAAAAAARERERERAAAAEREAAAAAAAAAAAAAAAAAEREREREQAAAAARAAAAAAAAAAAAAAAAAAEREREREAAAAAAAAAAAAAAAAAAAAAAAABEREREREAAAAAAAAAAAAAAAIiIiIAAAAREREREREQAAAAAAAAAAAAAAIiIiIAAAARERERERERAAAAAAAAAAAAAAAAAAAAAAERERERERERAAAAAAAAAAAAAAAAAAAAAAEREREREREREAAAAAAAAAAAAAAAAAAAABEREREREREREAAAAAAAAAAAAAAAAAAAABEREREREREREQAAAAAAAAAAAAAAAAAAAREREREREREREQAAAAAAAAAAAAAAAAAAEREREREREREREAAAAAAAAAAAAAAAAAAAERERAAAAAAAAAAAAAAAAAAAAAAAAAAABEREQAAAAAAAAAAAAAAAAAAAAAAAAAAAREREAAAAAAAAAAAAAAAAAAAAAAAAAAAARERAAAAAAAAAAAAAAAAAAAAAAAAAAAAERERAAAAAAAAAAAAAAAAAAAAAAAAAAAAEREwAAAAAAAAAAAAAAAAAAAAAAAAAAABEREAAAAAAAAAAAAAAAAAAAAAAAAAAAABERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + ), +); diff --git a/apps/beeptest/beeptest.app.js b/apps/beeptest/beeptest.app.js new file mode 100644 index 000000000..5f6438c24 --- /dev/null +++ b/apps/beeptest/beeptest.app.js @@ -0,0 +1,274 @@ +var Layout = require("Layout"); + +// Beep Test Data +const BEET_TEST_DATA = [ + { shuttles: 7, timePerShuttle: 9.0, totalTime: 63.0, distancePerLevel: 140 }, + { shuttles: 8, timePerShuttle: 8.0, totalTime: 64.0, distancePerLevel: 160 }, + { shuttles: 8, timePerShuttle: 7.58, totalTime: 60.6, distancePerLevel: 160 }, + { shuttles: 9, timePerShuttle: 7.2, totalTime: 64.8, distancePerLevel: 180 }, + { shuttles: 9, timePerShuttle: 6.86, totalTime: 61.7, distancePerLevel: 180 }, + { + shuttles: 10, + timePerShuttle: 6.55, + totalTime: 65.5, + distancePerLevel: 200, + }, + { + shuttles: 10, + timePerShuttle: 6.26, + totalTime: 62.6, + distancePerLevel: 200, + }, + { shuttles: 11, timePerShuttle: 6.0, totalTime: 66.0, distancePerLevel: 220 }, + { + shuttles: 11, + timePerShuttle: 5.76, + totalTime: 63.4, + distancePerLevel: 220, + }, + { + shuttles: 11, + timePerShuttle: 5.54, + totalTime: 60.9, + distancePerLevel: 220, + }, + { + shuttles: 12, + timePerShuttle: 5.33, + totalTime: 64.0, + distancePerLevel: 240, + }, + { + shuttles: 12, + timePerShuttle: 5.14, + totalTime: 61.7, + distancePerLevel: 240, + }, + { + shuttles: 13, + timePerShuttle: 4.97, + totalTime: 64.6, + distancePerLevel: 260, + }, + { shuttles: 13, timePerShuttle: 4.8, totalTime: 62.4, distancePerLevel: 260 }, + { + shuttles: 13, + timePerShuttle: 4.65, + totalTime: 60.4, + distancePerLevel: 260, + }, + { shuttles: 14, timePerShuttle: 4.5, totalTime: 63.0, distancePerLevel: 280 }, + { + shuttles: 14, + timePerShuttle: 4.36, + totalTime: 61.1, + distancePerLevel: 280, + }, + { + shuttles: 15, + timePerShuttle: 4.24, + totalTime: 63.5, + distancePerLevel: 300, + }, + { + shuttles: 15, + timePerShuttle: 4.11, + totalTime: 61.7, + distancePerLevel: 300, + }, + { shuttles: 16, timePerShuttle: 4.0, totalTime: 64.0, distancePerLevel: 320 }, + { + shuttles: 16, + timePerShuttle: 3.89, + totalTime: 62.3, + distancePerLevel: 320, + }, +]; + +// VO2max Data +const VO2MAX_DATA = [ + { level: 1, vo2max: 16.7 }, + { level: 2, vo2max: 23.0 }, + { level: 3, vo2max: 26.2 }, + { level: 4, vo2max: 29.3 }, + { level: 5, vo2max: 32.5 }, + { level: 6, vo2max: 35.7 }, + { level: 7, vo2max: 38.8 }, + { level: 8, vo2max: 42.0 }, + { level: 9, vo2max: 45.1 }, + { level: 10, vo2max: 48.3 }, + { level: 11, vo2max: 51.5 }, + { level: 12, vo2max: 54.6 }, + { level: 13, vo2max: 57.8 }, + { level: 14, vo2max: 60.9 }, + { level: 15, vo2max: 64.1 }, + { level: 16, vo2max: 67.3 }, + { level: 17, vo2max: 70.4 }, + { level: 18, vo2max: 73.6 }, + { level: 19, vo2max: 76.7 }, + { level: 20, vo2max: 79.9 }, + { level: 21, vo2max: 83.0 }, +]; + +let currentLevel = 0; +let currentShuttle = 0; +let timeRemaining = 0; +let intervalId; +let beepTestLayout; +let testState = "start"; // 'start' | 'running' | 'result' + +function initBeepTestLayout() { + beepTestLayout = new Layout( + { + type: "v", + c: [ + { type: "txt", font: "30%", pad: 0, label: "Start Test", id: "status" }, + { type: "txt", font: "15%", pad: 0, label: "", id: "level" }, + { type: "txt", font: "10%", pad: 0, label: "", id: "vo2max" }, // Smaller font for VO2max + { type: "txt", font: "10%", pad: 0, label: "", id: "distance" }, // Smaller font for Distance + ], + }, + { + btns: [ + { + label: "Start/Stop", + cb: (l) => { + if (testState === "start") { + startTest(); + } else if (testState === "running") { + stopTest(); + } else { + showStartScreen(); + } + }, + }, + ], + }, + ); +} + +function showStartScreen() { + testState = "start"; + g.clear(); + beepTestLayout.clear(beepTestLayout.status); + beepTestLayout.status.label = "Start\nTest"; + beepTestLayout.clear(beepTestLayout.level); + beepTestLayout.level.label = ""; + beepTestLayout.clear(beepTestLayout.vo2max); // Clear VO2max text + beepTestLayout.vo2max.label = ""; + beepTestLayout.clear(beepTestLayout.distance); // Clear Distance text + beepTestLayout.distance.label = ""; + beepTestLayout.render(); +} + +function startTest() { + testState = "running"; + currentLevel = 0; + currentShuttle = 0; + Bangle.buzz(2000); // Buzz for 2 seconds at the start of the test + runLevel(); +} + +function runLevel() { + if (currentLevel >= BEET_TEST_DATA.length) { + stopTest(); + return; + } + + const levelData = BEET_TEST_DATA[currentLevel]; + timeRemaining = levelData.timePerShuttle * 1000; // Convert to milliseconds + updateDisplay(); + + if (intervalId) clearInterval(intervalId); + intervalId = setInterval(() => { + if (timeRemaining <= 0) { + currentShuttle++; + Bangle.buzz(100); // Short buzz after each shuttle + + if (currentShuttle >= levelData.shuttles) { + // Buzz longer or twice at the end of each level + Bangle.buzz(1000); // Buzz for 1 second at level end + setTimeout(() => Bangle.buzz(1000), 500); // Buzz again after 0.5 seconds + currentLevel++; + currentShuttle = 0; + runLevel(); + return; + } + + timeRemaining = levelData.timePerShuttle * 1000; // Reset to original time for the next shuttle + } + + updateDisplay(); + timeRemaining -= 100; // Decrement time by 100 milliseconds + }, 100); // Update every 100 milliseconds +} + +function updateDisplay() { + g.clear(); // Clear the entire screen + beepTestLayout.status.label = formatTime(timeRemaining); + beepTestLayout.level.label = `Level: ${currentLevel + 1}.${currentShuttle + 1}`; + beepTestLayout.render(); +} + +function stopTest() { + g.clear(); // Clear the entire screen + testState = "result"; + clearInterval(intervalId); + + // Determine previous level and shuttle + let prevLevel = currentLevel; + let prevShuttle = currentShuttle; + + if (prevShuttle === 0) { + if (prevLevel > 0) { + prevLevel--; + prevShuttle = BEET_TEST_DATA[prevLevel].shuttles - 1; + } else { + prevShuttle = 0; + } + } else { + prevShuttle--; + } + + // Determine VO2max and total distance + const vo2max = getVO2max(prevLevel + 1); + const totalDistance = calculateTotalDistance(prevLevel + 1); + + beepTestLayout.clear(beepTestLayout.status); + beepTestLayout.status.label = "Result"; + beepTestLayout.clear(beepTestLayout.level); + beepTestLayout.level.label = `Level: ${prevLevel + 1}.${prevShuttle + 1}`; + beepTestLayout.clear(beepTestLayout.vo2max); + beepTestLayout.vo2max.label = `VO2max: ${vo2max}`; + beepTestLayout.clear(beepTestLayout.distance); + beepTestLayout.distance.label = `Distance: ${totalDistance} m`; + beepTestLayout.render(); +} + +function getVO2max(level) { + const result = VO2MAX_DATA.find((item) => item.level === level); + return result ? result.vo2max : "N/A"; +} + +function calculateTotalDistance(level) { + // Calculate the total number of shuttles completed + let totalShuttles = 0; + for (let i = 0; i < level - 1; i++) { + totalShuttles += BEET_TEST_DATA[i].shuttles; + } + const levelData = BEET_TEST_DATA[level - 1]; + totalShuttles += levelData.shuttles; // Add the shuttles completed in the current level + const distancePerShuttle = 20; // Distance per shuttle in meters + return totalShuttles * distancePerShuttle; // Total distance +} + +function formatTime(milliseconds) { + let seconds = Math.floor(milliseconds / 1000); + let tenths = Math.floor((milliseconds % 1000) / 100); // Get tenths of a second + return (seconds < 10 ? "" : "") + seconds + "." + tenths; // Display only the tenths digit +} + +// Initialize the app +Bangle.setLCDPower(1); // Keep the watch LCD lit up +initBeepTestLayout(); +showStartScreen(); diff --git a/apps/beeptest/beeptest.img b/apps/beeptest/beeptest.img new file mode 100644 index 000000000..9d0fbe59d Binary files /dev/null and b/apps/beeptest/beeptest.img differ diff --git a/apps/beeptest/metadata.json b/apps/beeptest/metadata.json new file mode 100644 index 000000000..7dbbb0759 --- /dev/null +++ b/apps/beeptest/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "beeptest", + "name": "Beep Test", + "shortName": "Beep Test", + "version": "0.01", + "description": "Aerobic fitness test created by Léger & Lambert", + "icon": "app.png", + "tags": "", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + { "name": "beeptest.app.js", "url": "app.js" }, + { "name": "beeptest.img", "url": "app-icon.js", "evaluate": true } + ] +} diff --git a/apps/beeptest/readme.md b/apps/beeptest/readme.md new file mode 100644 index 000000000..eff3919c6 --- /dev/null +++ b/apps/beeptest/readme.md @@ -0,0 +1,30 @@ +# App Name + +More information on making apps: + +* http://www.espruino.com/Bangle.js+First+App +* http://www.espruino.com/Bangle.js+App+Loader + +Describe the app... + +Add screen shots (if possible) to the app folder and link then into this file with ![](.png) + +## Usage + +Describe how to use it + +## Features + +Name the function + +## Controls + +Name the buttons and what they are used for + +## Requests + +Name who should be contacted for support/update requests + +## Creator + +Your name