From e4866595255020ee823893da47190c61ad9de021 Mon Sep 17 00:00:00 2001 From: Erovia Date: Wed, 21 Feb 2024 20:45:50 +0000 Subject: [PATCH 1/4] First version of the C25K app --- apps/c25k/ChangeLog | 1 + apps/c25k/README.md | 70 +++++++++++++ apps/c25k/app-icon.js | 1 + apps/c25k/app.js | 216 +++++++++++++++++++++++++++++++++++++++ apps/c25k/app.png | Bin 0 -> 376 bytes apps/c25k/c25k-scrn1.png | Bin 0 -> 2792 bytes apps/c25k/c25k-scrn2.png | Bin 0 -> 2766 bytes apps/c25k/c25k-scrn3.png | Bin 0 -> 2404 bytes apps/c25k/c25k-scrn4.png | Bin 0 -> 3055 bytes apps/c25k/c25k-scrn5.png | Bin 0 -> 3042 bytes apps/c25k/c25k-scrn6.png | Bin 0 -> 5865 bytes apps/c25k/c25k-scrn7.png | Bin 0 -> 3397 bytes apps/c25k/metadata.json | 28 +++++ 13 files changed, 316 insertions(+) create mode 100644 apps/c25k/ChangeLog create mode 100644 apps/c25k/README.md create mode 100644 apps/c25k/app-icon.js create mode 100644 apps/c25k/app.js create mode 100644 apps/c25k/app.png create mode 100644 apps/c25k/c25k-scrn1.png create mode 100644 apps/c25k/c25k-scrn2.png create mode 100644 apps/c25k/c25k-scrn3.png create mode 100644 apps/c25k/c25k-scrn4.png create mode 100644 apps/c25k/c25k-scrn5.png create mode 100644 apps/c25k/c25k-scrn6.png create mode 100644 apps/c25k/c25k-scrn7.png create mode 100644 apps/c25k/metadata.json diff --git a/apps/c25k/ChangeLog b/apps/c25k/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/c25k/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/c25k/README.md b/apps/c25k/README.md new file mode 100644 index 000000000..8be8c2e15 --- /dev/null +++ b/apps/c25k/README.md @@ -0,0 +1,70 @@ +# C25K + +Unofficial app for the Couch to 5k training plan. +From being a couch-potato to running 5k in 8 weeks! + +Each week has 3 training days, ideally with rest days between them. + +Each day's programme consists of running for a certain time with occasional walking/resting phases. +When walking is part of the programme, the (run+walk) stages are repeated a number of times. + +![](c25k-scrn1.png) +![](c25k-scrn2.png) +![](c25k-scrn3.png) + +## Features + +- Show remaining time in seconds for each phase +- Vibrates on phase changes +- Keeps screen on to allow quickly glancing at the time while running +- Shows time on button press + +## Usage + +If you know the week and day of the programme you'd like to start, set `Week` and `Day` to the appropriate values in the main menu and press `Start`. + +**Example**: +To start the programme of the **second day** of **week 4**: +![](c25k-scrn4.png) + +--- + +Alternatively, you can go to the `View plan` menu to look at all the programmes and select the one you'd like to start. + +**Example**: +Go to the `View plan` menu: +![](c25k-scrn5.png) + +Select the programme to start it: +![](c25k-scrn6.png) + +--- + +The format of the `View menu` is `w{week}d{day}(r:{run mins}|w:{walk mins}|x{number of reps})`. + +For example `w6d1(r:6|w:3|x2)` means: +`it's the programme of day 1 on week 6`, +`it consists of running for 6 minutes`, +`followed by walking for 3`, +`done 2 times back to back`. + +--- + +### Show the time: + +If you ever need to peek at the time, just press the middle (or only) physical button on the watch: +![](c25k-scrn7.png) + +--- + +## Disclaimer + +This app was hacked together in a day with no JS knowledge. +It's probably inefficient and buggy, but it does what I needed it to do: allow me to follow the C25K programme without a phone. + +The app was designed with a Bangle.js 1 in mind, as that's the one I have. +It *should* work fine on the Bangle.js 2, but I couldn't test it on real hardware. + +--- + +Made with <3 by [Erovia](https://github.com/Erovia) diff --git a/apps/c25k/app-icon.js b/apps/c25k/app-icon.js new file mode 100644 index 000000000..6b85dbf29 --- /dev/null +++ b/apps/c25k/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4X/AoPk9G9gsj14lZhWq0AEBgtVqALmhQJBAQMFBIICCBc4ADBQYLnAQQKEBcibETQIABHggLiAEQqEh/wgACCBcpXDBAIKDBcqJDh//BQYLkHwg7GBcY7FU5ALgAEQA=")) diff --git a/apps/c25k/app.js b/apps/c25k/app.js new file mode 100644 index 000000000..bbc0c606e --- /dev/null +++ b/apps/c25k/app.js @@ -0,0 +1,216 @@ +var week = 1; +var day = 1; +var time; + +var loop; // To store how many times we will have to do a countdown +var counter = 0; // To keep count of how many iterations we have in the current countdown +var currentMode; // Either "run" or "walk" +var mainInterval; // Ticks every second, checking if a new countdown is needed +var activityInterval; // Ticks every second, doing the countdown +var buttonWatch; // Watch for button presses + +function outOfTime() { + // Buzz 3 times on state transitions + Bangle.buzz(500) + .then(() => new Promise(resolve => setTimeout(resolve, 200))) + .then(() => Bangle.buzz(500)) + .then(() => new Promise(resolve => setTimeout(resolve, 200))) + .then(() => Bangle.buzz(500)); + + // Once we're done + if (loop == 0) { + clearWatch(buttonWatch); // Don't watch for button presses anymore + g.setBgColor("#75C0E0"); // Blue background for the "Done" text + g.clear(); + g.drawString("Done", g.getWidth()/2, g.getHeight()/2); // Write "Done" to screen + g.reset(); + setTimeout(E.showMenu, 5000, mainmenu); // Show the main menu again after 5secs + clearInterval(mainInterval); // Stop the main interval from starting a new activity + mainInterval = undefined; + } +} + +function countDown() { + text = (currentMode === "run") ? "Run\n" + counter : "Walk\n" + counter; // Switches output text + if (time) text += "\n" + time; + g.clear(); + g.setFontAlign(0,0); // center font + if (process.env.HWVERSION == 2) { // To accomodate the Bangle.js 2 screen + if (time) { + g.setFont("6x8",5); // when time is shown + } + else { + g.setFont("6x8",7); // when time is not shown + } + } + else { + g.setFont("6x8",8); // bitmap font, 8x magnified + } + g.drawString(text, g.getWidth() / 2, g.getHeight() / 2); // draw the current mode and seconds + Bangle.setLCDPower(1); // keep the watch LCD lit up + + counter--; // Reduce the seconds + + // If the current activity is done + if (counter < 0) { + clearInterval(activityInterval); + activityInterval = undefined; + outOfTime(); + return; + } +} + +function startTimer(w, d) { + // If something is already running, do nothing + if (activityInterval) { + return; + } + + // Switches between the two modes + if (!currentMode || currentMode === "walk") { + currentMode = "run"; + counter = PLAN[w][d].run * 60; + g.setBgColor("#ff5733"); + } + else { + currentMode = "walk"; + counter = PLAN[w][d].walk * 60; + g.setBgColor("#4da80a"); + } + + countDown(); + if (!activityInterval) { + loop--; // Reduce the number of iterations + activityInterval = setInterval(countDown, 1000); // Start a new activity + } +} + +function showTime() { + if (time) return; // If clock is already shown, don't do anything even if the button was pressed again + // Get the time and format it with a leading 0 if necessary + var d = new Date(); + var h = d.getHours(); + var m = d.getMinutes(); + time = h + ":" + m.toString().padStart(2, 0); + setTimeout(function() { time = undefined; }, 5000); // Hide clock after 5secs +} + +// Populate the PLAN menu +function populatePlan() { + for (var i = 0; i < PLAN.length; i++) { + for (var j = 0; j < PLAN[i].length; j++) { + // Ever line will have the following format: + // w{week}d{day}(r:{run mins}|w:{walk mins}|x{number of reps}) + var name = "w" + (i + 1) + "d" + (j + 1); + if (process.env.HWVERSION == 2) { + name += "\n"; // Print in 2 lines to accomodate the Bangle.js 2 screen + } + name += "(r:" + PLAN[i][j].run; + if ("walk" in PLAN[i][j]) name += "|w:" + PLAN[i][j].walk; + if ("repetition" in PLAN[i][j]) name += "|x" + PLAN[i][j].repetition; + name += ")"; + // Each menu item will have a function that start the program at the selected day + planmenu[name] = getFunc(i, j); + } + } +} + +// Helper function to generate functions for the PLAN menu +function getFunc(i, j) { + return function() { + week = i + 1; + day = j + 1; + startActivity(); + }; +} + +function startActivity() { + var w = week - 1; + var d = day - 1; + + if ("walk" in PLAN[w][d]) { + loop = PLAN[w][d].repetition * 2; + } + else { + loop = 1; + } + E.showMenu(); // Hide the main menu + buttonWatch = setWatch(showTime, (process.env.HWVERSION == 2) ? BTN1 : BTN2, {repeat: true}); // Show the clock on button press + mainInterval = setInterval(function() {startTimer(w, d);}, 1000); // Check every second if we need to do something +} + +const PLAN = [ + [ + {"run": 1, "walk": 1.5, "repetition": 8}, + {"run": 1, "walk": 1.5, "repetition": 8}, + {"run": 1, "walk": 1.5, "repetition": 8}, + ], + [ + {"run": 1.5, "walk": 2, "repetition": 6}, + {"run": 1.5, "walk": 2, "repetition": 6}, + {"run": 1.5, "walk": 2, "repetition": 6}, + ], + [ + {"run": 2, "walk": 2, "repetition": 5}, + {"run": 2.5, "walk": 2.5, "repetition": 4}, + {"run": 2.5, "walk": 2.5, "repetition": 4}, + ], + [ + {"run": 3, "walk": 2, "repetition": 5}, + {"run": 3, "walk": 2, "repetition": 5}, + {"run": 4, "walk": 2.5, "repetition": 3}, + ], + [ + {"run": 5, "walk": 2, "repetition": 3}, + {"run": 8, "walk": 5, "repetition": 2}, + {"run": 20}, + ], + [ + {"run": 6, "walk": 3, "repetition": 2}, + {"run": 10, "walk": 3, "repetition": 2}, + {"run": 25}, + ], + [ + {"run": 25}, + {"run": 25}, + {"run": 25}, + ], + [ + {"run": 30}, + {"run": 30}, + {"run": 30}, + ], +]; + +// Main menu +var mainmenu = { + "" : { + "title" : "-- C25K --" + }, + "Week" : { + value : week, + min:1,max:PLAN.length,step:1, + onchange : v => { week = v; } + }, + "Day" : { + value : day, + min:1,max:3,step:1, + onchange : v => { day = v; } + }, + "View plan" : function() { E.showMenu(planmenu); }, + "Start" : function() { startActivity(); }, + "Exit" : function() { load(); }, +}; + +// Plan view +var planmenu = { + "" : { + title : "-- Plan --", + back : function() { E.showMenu(mainmenu);}, + } +}; + +// Populate the PLAN menu view +populatePlan(); +// Actually display the menu +E.showMenu(mainmenu); diff --git a/apps/c25k/app.png b/apps/c25k/app.png new file mode 100644 index 0000000000000000000000000000000000000000..6b3a9ba95ad9dd9e8d87d1df39cc4bf2d18b58bc GIT binary patch literal 376 zcmV-;0f+vHP)Px$GD$>1RA@u(Sj!E`AScVN4kquae8;Bd~2<1X6)hKz-B$Sjst~Q4E zvE!r(A#`EJpJq7#&TU$!!^)-OW4ll0uWuKs9oLiQIk z*J-qUqG{$o$OOQ*|4RYHK&^59PFyL1{{>Kb?49nrk%q_3HK{3^A5}mA%)=DRXfF$! zzZ?b7GYA?|h*1=e)u$B@05FB50NU4)$C1S>!?qqe7qWXm089@^0eB}yo-uLs%7CT} zio1Kj`Tv6eIJecF;9~cI>J;#<)82Cc&3(2f=v@f)ga8H8|H|QM?3*lv-L7>d-XOaLEN&a&t-h;)^pr*coT~uzTj5 zJ>S`HzVrY8=Bvxg-OFM`F#rHyWoGQor^cm?hwe|kxi6SMpa!rqfA21!=a;Q!0ARFb z?tZ$Uy3}&!n}gAXoBTTK#p2l1r*_x2HHc4UGtjeLPu90Q>s&uNa>2jInR(n}{bObt zi}A#Pfd!#!ZN#a{C;I*5VR^#Hr=bzQ@UNy|1l6w zd_Xnvu$ovv{f-B+4=gX$CxY;yfNgm>eC3JSed0iN-_BU&D-feNE$wVJUwI7-3=hjQ zUZ1_c1Y+C@%1}3>P-L*~W@w(Uihu7{|E_>;$ZL~T9anh2m@zEifox8+77`q}M1vTc zjPAr2gjwSfK3@r@!5vGV;(p=Hp>|eIU{Nrg%|T6DnlFB?qN<)Xp2B&!JUv!T04kA? zp_618-4LREP_JthD2k92bOmuFmvtdhfsFhb(t{mvE_2U3rowJyJL*JTNxVW~Dt||8 zw?ZO6oivrOG>z3woYff;3Hy7Pc8t566aqGSJZ!+GOz}xNesmTgK8r?$e(5gs}I<7A)>9KdKYGGAp{i--c7^ zMl=VZL&|au>5lqatUKy*uX5IZ0`72dxE}gd6=r?8XZ4}vXrM$`B$D%X>7!rjE(DHY z*0YXBG%>AiIxFSvL{4|@vYY-KxNyn2`Lq?gkCTqwKpbY%&3H;IZasO(+bCKqR>m96 zaFz}y1+q}iT(+ZAk!o1LtSl^n1E*>DW-?jPg+LwBPObwn&A0_avz2fMibt}2r)3h2 zNmIDTEIqFnOOf^v;Y)VtoJsHMQxe`{D0y{z?&=p)-F2CZPb1>4u(E)jd!6`8N*cv z?*O`SA6J5V3yrnG8A^<+z9wZ{_>otIi#SDCnA{V$ft}l*zfW1x2lRi0D62@*f}OQz zzH*m=Xx#j9p%7S9#GivEC6XU3o@DRve8k!;&H9l2_UglOYC3@vEj$lcNV98f13`Ty z=!#1$r|vK(;Bq3rMxLs*N+@l=9;r^}wUiy!Kuy8pYXgDoB@0>Oe3NP%xcom1RM#6=Qp5KSgp!b6RF9jpC-;IaYZxSCIqgq!W N^V!_pJ-dWQ{sN33V2%I) literal 0 HcmV?d00001 diff --git a/apps/c25k/c25k-scrn2.png b/apps/c25k/c25k-scrn2.png new file mode 100644 index 0000000000000000000000000000000000000000..ba064200e28f4350e0c001fff4b4f3911d63b464 GIT binary patch literal 2766 zcmeHJZBQH66_u7%twn@2{Uo0G(bmgMG;7=5CTFL2n&6)+OGJ~PW!X{ z*YwBkn>X*gd*_^c-L0&1ndSV~@YxH0EJ?AZYVb+%B=Q8@H z_Bk1h__qu5a>`Vd?)h)u>dsF(>-ZJ_cGZO+3>2h3^;~gwVp3x3>iyQesiEJxCXTF5 z|Ebnn_xyK~u`SXdrGgF-o2R^m3qWC8drwkOzyw|g!JS;5$Ke^g!9d&$;3||DRI1MB zYw>ypG6AyHS!W)kiDlVZ`~m<9_jUd?i8rucOdMqb2X{(b?8MofJY_2b*>zQa5oEu) zBQdK;i(j2Ep9a98IQG7DF)`t&-p>TA%&%S=;$Z_}r?$opd0Jrd!3dhEkM_9q(V=N* z^e+B$^67#%6z97h+3U#Tl>ZcB;C{ak1qUIEO!{u~ddI4fQrPS> zZ&{Z?TZ!!Vn&hWHK6&Kas$xX*9vtbwtm@BdsyEki!Bnb0_Cc*k)?3q%}2ID zaWCc!S4Z8ofPz0zE`n~&weR_s?_`DOGptFN>8WQ}eTKLim%?l9vp;D!NeBm=c;Phe zTvu=gE8zDOktTo}oZLUR=O;!@#_T4&YsJlX5;Q0drhB?%W;FdM0-cfnjR-(RGGJHy z39|a1kZ#;5DYQNwqH>y#+w0(dizM=*3}{3!-T)bQ1$R(CyVsOciXYb2+59>aI%QUy z$+1e&(jv=GqsDN!gfLQIv9}xTa;n44FZ=!-P)Tgwe)8jKy>$4I#U&v8TZaz{22y4@ zk6E3Kh!xek$xW^@tTs~Xm2jh}9u`JfHB3X`b=*z5qgW&94$YA~WhkR=uXqF-liA0j ze)VChu0#fiNDP7YY4QfOL+g4}YhHG5$>HsDxv)}3Ckb)@EBuZQd+X1}W1iP47s%`D>Y318W#{dsmCBN$yEfodK zZ$&JG+Dk{N?3LE0Az?npXm zj&iq)$rrlS*VY^Qu@|JDqf=uzP1TbE_Q2xw9P_CdQ9zG!VNIu|ITR95>pzNckNYed z(Pw&-s5$yZ{%GvNKuXqFnur4QDQjlwhbZ4`sYw^Y?hFws32Ur&{jzmR7wq=7zzDZX zvMY-<(@AFyaP6Dq5LSkfnpN1SS#80uoeO2uheE2RM B$YCoWw@B1b2Rmu_P#zI4 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 F`ybNL9sU3S literal 0 HcmV?d00001 diff --git a/apps/c25k/c25k-scrn5.png b/apps/c25k/c25k-scrn5.png new file mode 100644 index 0000000000000000000000000000000000000000..ca32abdfa9f9e3911d3a1bd48752f491af1aa2d8 GIT binary patch literal 3042 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 literal 0 HcmV?d00001 diff --git a/apps/c25k/c25k-scrn6.png b/apps/c25k/c25k-scrn6.png new file mode 100644 index 0000000000000000000000000000000000000000..53f5221d70b4c7ebd9b7346923008b1e05328f5d GIT binary patch literal 5865 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 literal 0 HcmV?d00001 diff --git a/apps/c25k/c25k-scrn7.png b/apps/c25k/c25k-scrn7.png new file mode 100644 index 0000000000000000000000000000000000000000..9fc8ebd3c157555d1bb3f83dcedd0a32191b1b0f GIT binary patch literal 3397 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 fIX{h17}yc;*5#MqzfjKp7Xcf7k(S)F4lVyH(h Date: Wed, 21 Feb 2024 21:38:48 +0000 Subject: [PATCH 2/4] Add rep info to time screen --- apps/c25k/ChangeLog | 1 + apps/c25k/README.md | 2 ++ apps/c25k/app.js | 29 +++++++++++++++-------------- apps/c25k/c25k-scrn7.png | Bin 3397 -> 3117 bytes apps/c25k/metadata.json | 2 +- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/apps/c25k/ChangeLog b/apps/c25k/ChangeLog index 5560f00bc..4ef344621 100644 --- a/apps/c25k/ChangeLog +++ b/apps/c25k/ChangeLog @@ -1 +1,2 @@ 0.01: New App! +0.02: Add rep info to time screen diff --git a/apps/c25k/README.md b/apps/c25k/README.md index 8be8c2e15..8d523700f 100644 --- a/apps/c25k/README.md +++ b/apps/c25k/README.md @@ -55,6 +55,8 @@ For example `w6d1(r:6|w:3|x2)` means: If you ever need to peek at the time, just press the middle (or only) physical button on the watch: ![](c25k-scrn7.png) +This view also shows `current rep / total reps` at the top. + --- ## Disclaimer diff --git a/apps/c25k/app.js b/apps/c25k/app.js index bbc0c606e..2f80626f3 100644 --- a/apps/c25k/app.js +++ b/apps/c25k/app.js @@ -3,7 +3,8 @@ var day = 1; var time; var loop; // To store how many times we will have to do a countdown -var counter = 0; // To keep count of how many iterations we have in the current countdown +var rep = 0; // The current rep counter +var counter = 0; // The seconds counter var currentMode; // Either "run" or "walk" var mainInterval; // Ticks every second, checking if a new countdown is needed var activityInterval; // Ticks every second, doing the countdown @@ -31,21 +32,20 @@ function outOfTime() { } function countDown() { - text = (currentMode === "run") ? "Run\n" + counter : "Walk\n" + counter; // Switches output text + var text = ""; + var textsize = (process.env.HWVERSION == 2) ? 7 : 8; // Default font size, Banglejs 2 has smaller screen + if (time) { + var w = week -1; + var d = day - 1; + var total = ("walk" in PLAN[w][d]) ? PLAN[w][d].repetition : 1; + text += rep + "/" + total + "\n"; // Show the current/total rep count when time is shown + textsize -= (process.env.HWVERSION == 2) ? 2 : 1; // Use smaller font size to fit everything nicely on the screen + } + text += (currentMode === "run") ? "Run\n" + counter : "Walk\n" + counter; // Switches output text if (time) text += "\n" + time; g.clear(); - g.setFontAlign(0,0); // center font - if (process.env.HWVERSION == 2) { // To accomodate the Bangle.js 2 screen - if (time) { - g.setFont("6x8",5); // when time is shown - } - else { - g.setFont("6x8",7); // when time is not shown - } - } - else { - g.setFont("6x8",8); // bitmap font, 8x magnified - } + g.setFontAlign(0, 0); // center font + g.setFont("6x8", textsize); g.drawString(text, g.getWidth() / 2, g.getHeight() / 2); // draw the current mode and seconds Bangle.setLCDPower(1); // keep the watch LCD lit up @@ -69,6 +69,7 @@ function startTimer(w, d) { // Switches between the two modes if (!currentMode || currentMode === "walk") { currentMode = "run"; + rep++; // Increase the rep counter every time a "run" activity starts counter = PLAN[w][d].run * 60; g.setBgColor("#ff5733"); } diff --git a/apps/c25k/c25k-scrn7.png b/apps/c25k/c25k-scrn7.png index 9fc8ebd3c157555d1bb3f83dcedd0a32191b1b0f..407afd48bd5dd49cbc469a1086c355c619951b5e 100644 GIT binary patch literal 3117 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 V#X9x2O3?q~ygdTk8{MLF{sq7yjphIV literal 3397 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 fIX{h17}yc;*5#MqzfjKp7Xcf7k(S)F4lVyH(h Date: Thu, 22 Feb 2024 15:39:13 +0000 Subject: [PATCH 3/4] Add option to pause/resume workout (Bangle.js 1 only) --- apps/c25k/ChangeLog | 1 + apps/c25k/README.md | 13 ++++- apps/c25k/app.js | 120 +++++++++++++++++++++++---------------- apps/c25k/c25k-scrn8.png | Bin 0 -> 2334 bytes apps/c25k/metadata.json | 5 +- 5 files changed, 87 insertions(+), 52 deletions(-) create mode 100644 apps/c25k/c25k-scrn8.png diff --git a/apps/c25k/ChangeLog b/apps/c25k/ChangeLog index 4ef344621..3fa68b916 100644 --- a/apps/c25k/ChangeLog +++ b/apps/c25k/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Add rep info to time screen +0.03: Add option to pause/resume workout (Bangle.js 1 only) diff --git a/apps/c25k/README.md b/apps/c25k/README.md index 8d523700f..5d56fe17c 100644 --- a/apps/c25k/README.md +++ b/apps/c25k/README.md @@ -50,7 +50,7 @@ For example `w6d1(r:6|w:3|x2)` means: --- -### Show the time: +### Show extra info: If you ever need to peek at the time, just press the middle (or only) physical button on the watch: ![](c25k-scrn7.png) @@ -59,6 +59,15 @@ This view also shows `current rep / total reps` at the top. --- +### Pause/resume workout: + +**This is currently only available on Bangle.js 1.** + +Press the top button to pause or to resume the active programme: +![](c25k-scrn8.png) + +--- + ## Disclaimer This app was hacked together in a day with no JS knowledge. @@ -69,4 +78,4 @@ It *should* work fine on the Bangle.js 2, but I couldn't test it on real hardwar --- -Made with <3 by [Erovia](https://github.com/Erovia) +Made with <3 by [Erovia](https://github.com/Erovia/BangleApps/tree/c25k) diff --git a/apps/c25k/app.js b/apps/c25k/app.js index 2f80626f3..e7cd2276f 100644 --- a/apps/c25k/app.js +++ b/apps/c25k/app.js @@ -3,27 +3,26 @@ var day = 1; var time; var loop; // To store how many times we will have to do a countdown -var rep = 0; // The current rep counter -var counter = 0; // The seconds counter +var rep; // The current rep counter +var counter; // The seconds counter var currentMode; // Either "run" or "walk" var mainInterval; // Ticks every second, checking if a new countdown is needed var activityInterval; // Ticks every second, doing the countdown -var buttonWatch; // Watch for button presses +var extraInfoWatch; // Watch for button presses to show additional info +var paused = false; // Track pause state +var pauseOrResumeWatch; // Watch for button presses to pause/resume countdown +var defaultFontSize = (process.env.HWVERSION == 2) ? 7 : 8; // Default font size, Banglejs 2 has smaller +var activityBgColour; // Background colour of current activity function outOfTime() { - // Buzz 3 times on state transitions - Bangle.buzz(500) - .then(() => new Promise(resolve => setTimeout(resolve, 200))) - .then(() => Bangle.buzz(500)) - .then(() => new Promise(resolve => setTimeout(resolve, 200))) - .then(() => Bangle.buzz(500)); + buzz(); // Once we're done if (loop == 0) { - clearWatch(buttonWatch); // Don't watch for button presses anymore + clearWatch(extraInfoWatch); // Don't watch for button presses anymore + if (pauseOrResumeWatch) clearWatch(pauseOrResumeWatch); // Don't watch for button presses anymore g.setBgColor("#75C0E0"); // Blue background for the "Done" text - g.clear(); - g.drawString("Done", g.getWidth()/2, g.getHeight()/2); // Write "Done" to screen + drawText("Done", defaultFontSize); // Write "Done" to screen g.reset(); setTimeout(E.showMenu, 5000, mainmenu); // Show the main menu again after 5secs clearInterval(mainInterval); // Stop the main interval from starting a new activity @@ -31,54 +30,69 @@ function outOfTime() { } } -function countDown() { - var text = ""; - var textsize = (process.env.HWVERSION == 2) ? 7 : 8; // Default font size, Banglejs 2 has smaller screen - if (time) { - var w = week -1; - var d = day - 1; - var total = ("walk" in PLAN[w][d]) ? PLAN[w][d].repetition : 1; - text += rep + "/" + total + "\n"; // Show the current/total rep count when time is shown - textsize -= (process.env.HWVERSION == 2) ? 2 : 1; // Use smaller font size to fit everything nicely on the screen - } - text += (currentMode === "run") ? "Run\n" + counter : "Walk\n" + counter; // Switches output text - if (time) text += "\n" + time; +// Buzz 3 times on state transitions +function buzz() { + Bangle.buzz(500) + .then(() => new Promise(resolve => setTimeout(resolve, 200))) + .then(() => Bangle.buzz(500)) + .then(() => new Promise(resolve => setTimeout(resolve, 200))) + .then(() => Bangle.buzz(500)); +} + +function drawText(text, size){ g.clear(); g.setFontAlign(0, 0); // center font - g.setFont("6x8", textsize); - g.drawString(text, g.getWidth() / 2, g.getHeight() / 2); // draw the current mode and seconds - Bangle.setLCDPower(1); // keep the watch LCD lit up + g.setFont("6x8", size); + g.drawString(text, g.getWidth() / 2, g.getHeight() / 2); +} - counter--; // Reduce the seconds +function countDown() { + if (!paused) { + var text = ""; + var size = defaultFontSize; + if (time) { + var w = week -1; + var d = day - 1; + var total = ("walk" in PLAN[w][d]) ? PLAN[w][d].repetition : 1; + text += rep + "/" + total + "\n"; // Show the current/total rep count when time is shown + size -= (process.env.HWVERSION == 2) ? 2 : 1; // Use smaller font size to fit everything nicely on the screen + } + text += (currentMode === "run") ? "Run\n" + counter : "Walk\n" + counter; // Switches output text + if (time) text += "\n" + time; + drawText(text, size); // draw the current mode and seconds + Bangle.setLCDPower(1); // keep the watch LCD lit up - // If the current activity is done - if (counter < 0) { - clearInterval(activityInterval); - activityInterval = undefined; - outOfTime(); - return; + counter--; // Reduce the seconds + + // If the current activity is done + if (counter < 0) { + clearInterval(activityInterval); + activityInterval = undefined; + outOfTime(); + return; + } } } function startTimer(w, d) { // If something is already running, do nothing - if (activityInterval) { - return; - } + if (activityInterval) return; // Switches between the two modes if (!currentMode || currentMode === "walk") { currentMode = "run"; rep++; // Increase the rep counter every time a "run" activity starts counter = PLAN[w][d].run * 60; - g.setBgColor("#ff5733"); + activityBgColour = "#ff5733"; // Red background for running } else { currentMode = "walk"; counter = PLAN[w][d].walk * 60; - g.setBgColor("#4da80a"); + activityBgColour = "#4da80a"; // Green background for walking + } + g.setBgColor(activityBgColour); countDown(); if (!activityInterval) { loop--; // Reduce the number of iterations @@ -103,9 +117,7 @@ function populatePlan() { // Ever line will have the following format: // w{week}d{day}(r:{run mins}|w:{walk mins}|x{number of reps}) var name = "w" + (i + 1) + "d" + (j + 1); - if (process.env.HWVERSION == 2) { - name += "\n"; // Print in 2 lines to accomodate the Bangle.js 2 screen - } + if (process.env.HWVERSION == 2) name += "\n"; // Print in 2 lines to accomodate the Bangle.js 2 screen name += "(r:" + PLAN[i][j].run; if ("walk" in PLAN[i][j]) name += "|w:" + PLAN[i][j].walk; if ("repetition" in PLAN[i][j]) name += "|x" + PLAN[i][j].repetition; @@ -129,15 +141,27 @@ function startActivity() { var w = week - 1; var d = day - 1; - if ("walk" in PLAN[w][d]) { - loop = PLAN[w][d].repetition * 2; + loop = ("walk" in PLAN[w][d]) ? PLAN[w][d].repetition * 2 : 1; + rep = 0; + + E.showMenu(); // Hide the main menu + extraInfoWatch = setWatch(showTime, (process.env.HWVERSION == 2) ? BTN1 : BTN2, {repeat: true}); // Show the clock on button press + if (process.env.HWVERSION == 1) pauseOrResumeWatch = setWatch(pauseOrResumeActivity, BTN1, {repeat: true}); // Pause or resume on button press (Bangle.js 1 only) + buzz(); + mainInterval = setInterval(function() {startTimer(w, d);}, 1000); // Check every second if we need to do something +} + +// Pause or resume current activity +function pauseOrResumeActivity() { + paused = !paused; + buzz(); + if (paused) { + g.setBgColor("#fdd835"); // Yellow background for pause screen + drawText("Paused", (process.env.HWVERSION == 2) ? defaultFontSize - 3 : defaultFontSize - 2); // Although the font size is configured here, this feature does not work on Bangle.js 2 as the only physical button is tied to the extra info screen already } else { - loop = 1; + g.setBgColor(activityBgColour); } - E.showMenu(); // Hide the main menu - buttonWatch = setWatch(showTime, (process.env.HWVERSION == 2) ? BTN1 : BTN2, {repeat: true}); // Show the clock on button press - mainInterval = setInterval(function() {startTimer(w, d);}, 1000); // Check every second if we need to do something } const PLAN = [ diff --git a/apps/c25k/c25k-scrn8.png b/apps/c25k/c25k-scrn8.png new file mode 100644 index 0000000000000000000000000000000000000000..1cd92d876d8583a7d75bb022e71a920ef767e10f GIT binary patch literal 2334 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic4M^IBzMKT47>k44ofy`glX=O&z;VOV#WAEJ z?(H4NNw--HeN1E%jnGX#IE$?reE>%r!Iy8i25uldt|?cJKk_m8UYT^F){%lg%yZLI!z zT7KT&YM0#@RPd7Z{?-LQmYk}cw}18e68`wzyYlU)zWdz#S?I@#2_hzq{T+3GikbEw z={?|ngX2%Flji)@^DA;I_c;A`R?vO6&U%;nhl{!wg@5QXy|k-tt;=1Xbb+Og_e-q# zoz?d_Y#QThnD%dTyI-dzYBblsc-e9P=a+3>{<&It>GATu1|0`?3I`>|u{9dN5 zx<{^72AvCk?%6;8-1J!e#(pkOZolXK=l*{^e;KGcb6&~6S2iy{UjDi1wd#jJ#ms{* z|6KgZIY-dz=km{yvyT6-yFEQZ^Q8Fm%T1SmnuJyTDX}{{`CR;5b=x|t__Dq84!E~2 zVO{>&{rPv6a~whzZ_JXj*-VN@b)%Qlt`L?_=^W4UiLn`U&&Oj=9s`#@2C*lv3kD2 z%h`vHmfvaLFSX>3O{2Z}?n~3dUxdqV<+ppz`G)h)8L!xy<_}zdeK_UM3d}fg{>{0b z1247j7>oZubLMEnqwqD`Y|b0hq^s?{SMqOe&iR^euWZES|L;*ZX#6j}KiGEj=lC;A zxM!U^-gt;VX72MxkA;7%c)z8g zr4NC3Vc~^c;FkE0z}WP^3lA-RygdHa-Ko#leNK~_`+fh^*!S8`+#U$rsDI4zuSegTe~DWM4fTPKtn literal 0 HcmV?d00001 diff --git a/apps/c25k/metadata.json b/apps/c25k/metadata.json index da3d061c6..e7c616d94 100644 --- a/apps/c25k/metadata.json +++ b/apps/c25k/metadata.json @@ -2,7 +2,7 @@ "id": "c25k", "name": "C25K", "icon": "app.png", - "version":"0.02", + "version":"0.03", "description": "Unofficial app for the Couch to 5k training plan", "readme": "README.md", "type": "app", @@ -23,6 +23,7 @@ {"url": "c25k-scrn4.png"}, {"url": "c25k-scrn5.png"}, {"url": "c25k-scrn6.png"}, - {"url": "c25k-scrn7.png"} + {"url": "c25k-scrn7.png"}, + {"url": "c25k-scrn8.png"} ] } From 551a5db61158a3f6bdec51b54f663ca27c702538 Mon Sep 17 00:00:00 2001 From: Erovia Date: Fri, 23 Feb 2024 17:06:16 +0000 Subject: [PATCH 4/4] Add possibility of creating a custom exercise --- apps/c25k/ChangeLog | 1 + apps/c25k/README.md | 15 +++++ apps/c25k/app.js | 123 +++++++++++++++++++++++++++------------ apps/c25k/c25k-scrn9.png | Bin 0 -> 3472 bytes apps/c25k/metadata.json | 5 +- 5 files changed, 105 insertions(+), 39 deletions(-) create mode 100644 apps/c25k/c25k-scrn9.png diff --git a/apps/c25k/ChangeLog b/apps/c25k/ChangeLog index 3fa68b916..0e7594334 100644 --- a/apps/c25k/ChangeLog +++ b/apps/c25k/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Add rep info to time screen 0.03: Add option to pause/resume workout (Bangle.js 1 only) +0.04: Add possibility of creating a custom exercise diff --git a/apps/c25k/README.md b/apps/c25k/README.md index 5d56fe17c..8237199d5 100644 --- a/apps/c25k/README.md +++ b/apps/c25k/README.md @@ -50,6 +50,21 @@ For example `w6d1(r:6|w:3|x2)` means: --- +### Create a custom excercise + +Under the `Custom run` menu, it's possible to create a custom excercise. +![](c25k-scrn9.png) + +Some important details/limitations: + +- To disable walking: set `walk` to `0` +- When walking is set to `0`, the repetition count is set to `1`. +- When repetition is set to `2` or higher, `walk` is set to `1`. + +**Unfortunately, the value in the menu do not update to reflect the changes, so I recommend setting the values with the rules above in mind.** + +--- + ### Show extra info: If you ever need to peek at the time, just press the middle (or only) physical button on the watch: diff --git a/apps/c25k/app.js b/apps/c25k/app.js index e7cd2276f..eed918e46 100644 --- a/apps/c25k/app.js +++ b/apps/c25k/app.js @@ -1,6 +1,10 @@ -var week = 1; -var day = 1; -var time; +var week = 1; // Stock plan: programme week +var day = 1; // Stock plan: programe day +var run = 1; // Custom plan: running time +var walk = 0; // Custom plan: walking time +var reps = 1; // Custom plan: repetition count + +var time; // To store the date var loop; // To store how many times we will have to do a countdown var rep; // The current rep counter @@ -11,8 +15,9 @@ var activityInterval; // Ticks every second, doing the countdown var extraInfoWatch; // Watch for button presses to show additional info var paused = false; // Track pause state var pauseOrResumeWatch; // Watch for button presses to pause/resume countdown -var defaultFontSize = (process.env.HWVERSION == 2) ? 7 : 8; // Default font size, Banglejs 2 has smaller +var defaultFontSize = (process.env.HWVERSION == 2) ? 7 : 9; // Default font size, Banglejs 2 has smaller var activityBgColour; // Background colour of current activity +var currentActivity; // To store the current activity function outOfTime() { buzz(); @@ -27,6 +32,7 @@ function outOfTime() { setTimeout(E.showMenu, 5000, mainmenu); // Show the main menu again after 5secs clearInterval(mainInterval); // Stop the main interval from starting a new activity mainInterval = undefined; + currentMode = undefined; } } @@ -51,11 +57,9 @@ function countDown() { var text = ""; var size = defaultFontSize; if (time) { - var w = week -1; - var d = day - 1; - var total = ("walk" in PLAN[w][d]) ? PLAN[w][d].repetition : 1; + var total = ("walk" in currentActivity) ? currentActivity.repetition : 1; text += rep + "/" + total + "\n"; // Show the current/total rep count when time is shown - size -= (process.env.HWVERSION == 2) ? 2 : 1; // Use smaller font size to fit everything nicely on the screen + size -= 2; // Use smaller font size to fit everything nicely on the screen } text += (currentMode === "run") ? "Run\n" + counter : "Walk\n" + counter; // Switches output text if (time) text += "\n" + time; @@ -74,7 +78,7 @@ function countDown() { } } -function startTimer(w, d) { +function startTimer() { // If something is already running, do nothing if (activityInterval) return; @@ -82,12 +86,12 @@ function startTimer(w, d) { if (!currentMode || currentMode === "walk") { currentMode = "run"; rep++; // Increase the rep counter every time a "run" activity starts - counter = PLAN[w][d].run * 60; + counter = currentActivity.run * 60; activityBgColour = "#ff5733"; // Red background for running } else { currentMode = "walk"; - counter = PLAN[w][d].walk * 60; + counter = currentActivity.walk * 60; activityBgColour = "#4da80a"; // Green background for walking } @@ -128,27 +132,23 @@ function populatePlan() { } } -// Helper function to generate functions for the PLAN menu +// Helper function to generate functions for the activePlan menu function getFunc(i, j) { return function() { - week = i + 1; - day = j + 1; + currentActivity = PLAN[i][j]; startActivity(); }; } function startActivity() { - var w = week - 1; - var d = day - 1; - - loop = ("walk" in PLAN[w][d]) ? PLAN[w][d].repetition * 2 : 1; + loop = ("walk" in currentActivity) ? currentActivity.repetition * 2 : 1; rep = 0; E.showMenu(); // Hide the main menu extraInfoWatch = setWatch(showTime, (process.env.HWVERSION == 2) ? BTN1 : BTN2, {repeat: true}); // Show the clock on button press if (process.env.HWVERSION == 1) pauseOrResumeWatch = setWatch(pauseOrResumeActivity, BTN1, {repeat: true}); // Pause or resume on button press (Bangle.js 1 only) buzz(); - mainInterval = setInterval(function() {startTimer(w, d);}, 1000); // Check every second if we need to do something + mainInterval = setInterval(function() {startTimer();}, 1000); // Check every second if we need to do something } // Pause or resume current activity @@ -207,35 +207,84 @@ const PLAN = [ ], ]; +var customRun = {"run": 1}; + // Main menu var mainmenu = { - "" : { - "title" : "-- C25K --" - }, - "Week" : { - value : week, - min:1,max:PLAN.length,step:1, + "": { "title": "-- C25K --" }, + "Week": { + value: week, + min: 1, max: PLAN.length, step: 1, onchange : v => { week = v; } }, - "Day" : { - value : day, - min:1,max:3,step:1, - onchange : v => { day = v; } + "Day": { + value: day, + min: 1, max: 3, step: 1, + onchange: v => { day = v; } }, - "View plan" : function() { E.showMenu(planmenu); }, - "Start" : function() { startActivity(); }, - "Exit" : function() { load(); }, + "View plan": function() { E.showMenu(planmenu); }, + "Custom run": function() { E.showMenu(custommenu); }, + "Start": function() { + currentActivity = PLAN[week - 1][day -1]; + startActivity(); + }, + "Exit": function() { load(); }, }; // Plan view var planmenu = { - "" : { - title : "-- Plan --", - back : function() { E.showMenu(mainmenu);}, - } + "": { title: "-- Plan --" }, + "< Back": function() { E.showMenu(mainmenu);}, }; -// Populate the PLAN menu view +// Custom view +var custommenu = { + "": { title : "-- Cust. run --" }, + "< Back": function() { E.showMenu(mainmenu);}, + "Run (mins)": { + value: run, + min: 1, max: 150, step: 1, + wrap: true, + onchange: v => { customRun.run = v; } + }, + "Walk (mins)": { + value: walk, + min: 0, max: 10, step: 1, + onchange: v => { + if (v > 0) { + if (reps == 1) { reps = 2; } // Walking only makes sense with multiple reps + customRun.repetition = reps; + customRun.walk = v; + } + else { + // If no walking, delete both the reps and walk data + delete customRun.repetition; + delete customRun.walk; + } + walk = v; + } + }, + "Reps": { + value: reps, + min: 1, max: 10, step: 1, + onchange: v => { + if (v > 1) { + if (walk == 0) { walk = 1; } // Multiple reps only make sense with walking phases + customRun.walk = walk; + customRun.repetition = v; + } + else { + // If no multiple reps, delete both the reps and walk data + delete customRun.repetition; + delete customRun.walk; + } + reps = v; + } + }, + "Start": function() { currentActivity = customRun; startActivity(); } +}; + +// Populate the activePlan menu view populatePlan(); // Actually display the menu E.showMenu(mainmenu); diff --git a/apps/c25k/c25k-scrn9.png b/apps/c25k/c25k-scrn9.png new file mode 100644 index 0000000000000000000000000000000000000000..53dbaad1f49b5cc49adb5a3fbc1838d86666cb6a GIT binary patch literal 3472 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 BJLUiY literal 0 HcmV?d00001 diff --git a/apps/c25k/metadata.json b/apps/c25k/metadata.json index e7c616d94..876926a0c 100644 --- a/apps/c25k/metadata.json +++ b/apps/c25k/metadata.json @@ -2,7 +2,7 @@ "id": "c25k", "name": "C25K", "icon": "app.png", - "version":"0.03", + "version":"0.04", "description": "Unofficial app for the Couch to 5k training plan", "readme": "README.md", "type": "app", @@ -24,6 +24,7 @@ {"url": "c25k-scrn5.png"}, {"url": "c25k-scrn6.png"}, {"url": "c25k-scrn7.png"}, - {"url": "c25k-scrn8.png"} + {"url": "c25k-scrn8.png"}, + {"url": "c25k-scrn9.png"} ] }