From 35eaf82a78db867b56816ffd2d1df9b26d3b9bf2 Mon Sep 17 00:00:00 2001 From: Timo Kiefer Date: Tue, 7 Apr 2020 23:03:30 +0200 Subject: [PATCH] Added tabata app to control high-intensity interval training --- apps.json | 12 ++++ apps/tabata/tabata-icon.js | 1 + apps/tabata/tabata.js | 130 +++++++++++++++++++++++++++++++++++++ apps/tabata/tabata.png | Bin 0 -> 1946 bytes 4 files changed, 143 insertions(+) create mode 100644 apps/tabata/tabata-icon.js create mode 100644 apps/tabata/tabata.js create mode 100644 apps/tabata/tabata.png diff --git a/apps.json b/apps.json index 4939e7c74..3dbdbe3e2 100644 --- a/apps.json +++ b/apps.json @@ -1107,5 +1107,17 @@ {"name":"openstmap.app.js","url":"app.js"}, {"name":"openstmap.img","url":"app-icon.js","evaluate":true} ] + }, + { "id": "tabata", + "name": "Tabata", + "shortName": "Tabata - Control High-Intensity Interval Training", + "icon": "tabata.png", + "version":"0.01", + "description": "Control high-intensity interval training (according to tabata: https://en.wikipedia.org/wiki/Tabata_method).", + "tags": "workout,health", + "storage": [ + {"name":"tabata.app.js","url":"tabata.js"}, + {"name":"tabata.img","url":"tabata-icon.js","evaluate":true} + ] } ] diff --git a/apps/tabata/tabata-icon.js b/apps/tabata/tabata-icon.js new file mode 100644 index 000000000..93783b607 --- /dev/null +++ b/apps/tabata/tabata-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwg96iOZzORgMZAYMQCxsBCwIAGDBsZyIDCCYQyBBAQuLEwWZCYYJBFx8BL4ITDjJILBgcJDYIGEyBCHFYQoGC4Y2BRBAJBC4wrDC4w9BiC6BC6Q6DAYIXpCIcJC/4X/C/4X/C50BjITCjOZD4IXOhOZCYMBAYIwEC52QC7QMDC/4XCzBH/C/4X/C5OQa6INBC4WRyIvQC5IOBC5YTBAYILByAECBAIXLA4IwBAYITBgMZDYIXGHgZICEAWRAYQfEAgIXIFAIIBiERCwYKCCQQOBBIQAvA")); \ No newline at end of file diff --git a/apps/tabata/tabata.js b/apps/tabata/tabata.js new file mode 100644 index 000000000..603cf96ee --- /dev/null +++ b/apps/tabata/tabata.js @@ -0,0 +1,130 @@ +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +var settings = require("Storage").readJSON("tabata.json",1)||{}; +settings.pause = settings.pause || 10; +settings.training = settings.training || 20; +settings.rounds = settings.rounds || 8; + +const MAX_SECONDS = 100; + +function debounce(callback, ms) { + var timer; + return () => { + if (timer) clearTimeout(timer); + timer = setTimeout(callback, ms); + }; +} + +function saveSettings() { + require("Storage").write("tabata.json",JSON.stringify(settings)); +} + +var saveSettingsDebounce = debounce(saveSettings, 250); + +function showMainMenu() { + const menu = { + '': { 'title': 'Tabata Training' }, + '>> Start >>': ()=> { + startTabata(); + }, + 'Pause sec.': { + value: settings.pause, + onchange: function(v){ + if (v<0)v=MAX_SECONDS; + if (v>MAX_SECONDS)v=0; + settings.pause=v; + this.value=v; + saveSettingsDebounce(); + } + }, + 'Trainig sec.': { + value: settings.training, + onchange: function(v){if (v<0)v=MAX_SECONDS;if (v>MAX_SECONDS)v=0;settings.training=v; + this.value=v; + saveSettingsDebounce(); + } + }, + 'Rounds': { + value: settings.rounds, + onchange: function(v){if (v<0)v=MAX_SECONDS;if (v>MAX_SECONDS)v=0;settings.rounds=v;this.value=v; + saveSettingsDebounce(); + } + }, + '< Back': () => load() + }; + menu['< Back'] = ()=>{load();}; + return E.showMenu(menu); +} + +function startTabata() { + g.clear(); + Bangle.setLCDMode("doublebuffered"); + g.flip(); + var pause = settings.pause, + training = settings.training, + round = 1, + active = true, + clearBtn1, clearBtn2, clearBtn3, timer; + Bangle.buzz(1000, 1); + + function exitTraining() { + clearTimeout(timer); + clearWatch(clearBtn1); + clearWatch(clearBtn2); + clearWatch(clearBtn2); + showMainMenu(); + } + + clearBtn1 = setWatch(exitTraining, BTN1); + clearBtn2 = setWatch(exitTraining, BTN2); + clearBtn3 = setWatch(exitTraining, BTN3); + + + timer = setInterval(function() { + if (round > settings.rounds) { + exitTraining(); + return; + } + + if (active) { + drawCountDown(round, training, active); + training--; + } else { + drawCountDown(round, pause, active); + pause--; + if (pause !== 0) { + Bangle.buzz(50, 0.2); + } + } + + if (training === 0) { + training = settings.training; + active = false; + Bangle.buzz(500, 1); + } + if (pause === 0) { + round++; + pause = settings.pause; + active = true; + Bangle.buzz(1000, 1); + } + }, 1000); +} + +function drawCountDown(round, count, active) { + g.clear(); + + g.setFontAlign(0,0); + g.setFont("6x8", 2); + g.drawString("Round " + round + "/" + settings.rounds,120,6); + + g.setFont("6x8", 6); + g.drawString("" + count,120,80); + + g.setFont("6x8",2); + g.drawString(active ? "Training" : "Pause", 120,45); + g.flip(); +} + +showMainMenu(); diff --git a/apps/tabata/tabata.png b/apps/tabata/tabata.png new file mode 100644 index 0000000000000000000000000000000000000000..f0aaadedee90e22b64f7beaecaddb73e293596b1 GIT binary patch literal 1946 zcmV;L2W9w)P)2C~ zDZ6){4g^5!>mN4qB_S&mI1aO8Ap2o93*>_WR4Q-+KowL8k-q}yf|&uRgxMO16ad&R zIXTa8=g#Y27=Si3e8*H@26;$Co@Pr+=g4tuZVqcYeMEre0AOa1NizA?D9yTc`Bqr? zLo?e7vXhSUsJ#AqYNB=Ju9%O@|zU=8e>F z-@a>-Og>MMS|Pt;sx6iNsMe`?dgUH6H)gy#**O-5@|EJ@3bCYx8_Hz)zw2FB6Mfb_hCg+kj*buEBD`sEVXNnn@7Vqaog+a+I6-$gfXp5iq$Bx!aCWE0G) zp!x*N-t+3~|7^9jBf+a-b}Z{%2#3oBC6aY-5I-A57K!=BS z09bEknvklD9Q@pd3^u? z$C-M^0M^*JOn^-!pOR#9Hvt0xZr!SqNc@SwvjCpf`ue#50CK`tF+-t=U)2-jGaNPm zfNIvgIvzmv8>*UOclS=;jJ&+<00@ysCp7j2#drX+6y$lfx4XUxue_25&?D9YXAheiM!M33x$qT zN;0qe`t=JC8F#Qbn@U}BL>{<0JbV^FKg`By0t1LfA~me8K8Mw-({l<6YWy_wX3bjQ zZQOWaR&MSzkaPVsfdPDyPIq=4J-XcK=%}XI2YwnUDmv%9`DXM$Pfye}yWy+3w>Gm! zStPO?svStD+m&4IE4qAnwz}@ACDW#zbI2)vlE46RR1e)(R5aHUnNQ%5pT_37M>j!%)L%+E7tu7N~G71=a_J%Op= z@b#*^y#JV)Yh=bxW57&Lwc~*qGp5q4*H07NYxLT+R6#f#0m<%6#q6h*?y?5ML66nB4GjtvPywQ1wNEyvS;LVhkUoHuyBEz zRe=15Z!Y;S2UtEmC+8AH&fgh8OG~E$`_=6CT31)%mqgdy4!|Msf|~7PBGKh*ps_I~ zji+q)vNoZxcI?iSFiSq$aI=r#hA$2T+sUZ`wZACB1_clFwAcDz(npB;C=;; z(`=WSJqM8x%=Uvk4PXXT*FaQdw-NhDq#1zA^DVxiD)K&}GtZ+hM@ gB}*f5GJ=f%0+Ylc2Q#LaGXMYp07*qoM6N<$f{d$?yZ`_I literal 0 HcmV?d00001