From 74bd784b091ad97964a7923fa888816fd8770412 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Mon, 20 Apr 2020 15:00:06 +0100 Subject: [PATCH 01/14] Initial commit of BuffGym 5x5 training program --- apps.json | 14 ++ apps/buffgym/.eslintrc.json | 33 ++++ apps/buffgym/buffgym-exercise.js | 187 +++++++++++++++++++ apps/buffgym/buffgym-icon.js | 1 + apps/buffgym/buffgym-program.js | 68 +++++++ apps/buffgym/buffgym-set.js | 46 +++++ apps/buffgym/buffgym.app.js | 311 +++++++++++++++++++++++++++++++ apps/buffgym/buffgym.png | Bin 0 -> 1800 bytes 8 files changed, 660 insertions(+) create mode 100644 apps/buffgym/.eslintrc.json create mode 100644 apps/buffgym/buffgym-exercise.js create mode 100644 apps/buffgym/buffgym-icon.js create mode 100644 apps/buffgym/buffgym-program.js create mode 100644 apps/buffgym/buffgym-set.js create mode 100755 apps/buffgym/buffgym.app.js create mode 100644 apps/buffgym/buffgym.png diff --git a/apps.json b/apps.json index 60b47dbd8..e993f0e18 100644 --- a/apps.json +++ b/apps.json @@ -1293,5 +1293,19 @@ "evaluate": true } ] + }, + { + "id": "buffgym", + "name": "BuffGym", + "icon": "buffgym.png", + "version":"0.01", + "description": "BuffGym is the famous 5x5 workout program for the BangleJS", + "tags": "tool,outdoors", + "type": "app", + "storage": [ + {"name":"buffgym"}, + {"name":"buffgym.app.js"}, + {"name":"buffgym.img","url":"buffgym-icon.js","evaluate":true} + ] } ] diff --git a/apps/buffgym/.eslintrc.json b/apps/buffgym/.eslintrc.json new file mode 100644 index 000000000..c91a72544 --- /dev/null +++ b/apps/buffgym/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "env": { + "browser": true, + "commonjs": true, + "es6": true + }, + "extends": "eslint:recommended", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 2018 + }, + "rules": { + "indent": [ + "error", + 2 + ], + "linebreak-style": [ + "error", + "windows" + ], + "quotes": [ + "error", + "double" + ], + "semi": [ + "error", + "always" + ] + } +} \ No newline at end of file diff --git a/apps/buffgym/buffgym-exercise.js b/apps/buffgym/buffgym-exercise.js new file mode 100644 index 000000000..b7a1e3e15 --- /dev/null +++ b/apps/buffgym/buffgym-exercise.js @@ -0,0 +1,187 @@ +const STARTED = 1; +const RESTING = 2; +const COMPLETED = 3; +const ONE_SECOND = 1000; +const INCREMENT = "increment"; +const DECREMENT = "decrement"; + +class Exercise { + constructor(params /*{title, weight, unit, restPeriod}*/) { + const DEFAULTS = { + title: "Unknown", + weight: 0, + unit: "Kg", + restPeriod: 90, + weightIncrement: 2.5, + }; + const p = Object.assign({}, DEFAULTS, params); + + this._title = p.title; + this._weight = p.weight; + this._unit = p.unit; + this._originalRestPeriod = p.restPeriod; // Used when reseting _restPeriod + this._restPeriod = p.restPeriod; + this._weightIncrement = p.weightIncrement; + this._started = new Date(); + this._completed = false; + this._sets = []; + this._restTimeout = null; + this._restInterval = null; + this._state = null; + } + + get title() { + return this._title; + } + + get humanTitle() { + return `${this._title} ${this._weight}${this._unit}`; + } + + get subTitle() { + const totalSets = this._sets.length; + const uncompletedSets = this._sets.filter((set) => !set.isCompleted()).length; + const currentSet = (totalSets - uncompletedSets) + 1; + return `Set ${currentSet} of ${totalSets}`; + } + + get restPeriod() { + return this._restPeriod; + } + + decRestPeriod() { + this._restPeriod--; + } + + get weight() { + return this._weight; + } + + get unit() { + return this._unit; + } + + get started() { + return this._started; + } + + addSet(set) { + this._sets.push(set); + } + + addSets(sets) { + sets.forEach(set => this.addSet(set)); + } + + get currentSet() { + return this._sets.filter(set => !set.isCompleted())[0]; + } + + isLastSet() { + return this._sets.filter(set => !set.isCompleted()).length === 1; + } + + isCompleted() { + return !!this._completed; + } + + canSetCompleted() { + return this._sets.filter(set => set.isCompleted()).length === this._sets.length; + } + + setCompleted() { + if (!this.canSetCompleted()) throw "All sets must be completed"; + if (this.canProgress) this._weight += this._weightIncrement; + this._completed = true; + } + + get canProgress() { + let completedRepsTotalSum = 0; + let targetRepsTotalSum = 0; + + const completedRepsTotal = this._sets.forEach(set => completedRepsTotalSum += set.reps); + const targetRepsTotal = this._sets.forEach(set => targetRepsTotalSum += set.maxReps); + + return (targetRepsTotalSum - completedRepsTotalSum) === 0; + } + + startRestTimer(program) { + this._restTimeout = setTimeout(() => { + this.next(); + }, ONE_SECOND * this._restPeriod); + + this._restInterval = setInterval(() => { + program.emit("redraw"); + }, ONE_SECOND); + } + + resetRestTimer() { + clearTimeout(this._restTimeout); + clearInterval(this._restInterval); + this._restTimeout = null; + this._restInterval = null; + this._restPeriod = this._originalRestPeriod; + } + + isRestTimerRunning() { + return this._restTimeout != null; + } + + setupStartedButtons(program) { + clearWatch(); + + setWatch(() => { + this.currentSet.incReps(); + program.emit("redraw"); + }, BTN1, {repeat: true}); + + setWatch(program.next.bind(program), BTN2, {repeat: false}); + + setWatch(() => { + this.currentSet.decReps(); + program.emit("redraw"); + }, BTN3, {repeat: true}); + } + + setupRestingButtons(program) { + clearWatch(); + setWatch(program.next.bind(program), BTN2, {repeat: true}); + } + + next(program) { + global.poo = this; + switch(this._state) { + case null: + console.log("XXX 1 moving null -> STARTED"); + this._state = STARTED; + this.setupStartedButtons(program); + break; + case STARTED: + console.log("XXX 2 moving STARTED -> RESTING"); + this._state = RESTING; + this.startRestTimer(program); + this.setupRestingButtons(program); + break; + case RESTING: + this.resetRestTimer(); + this.currentSet.setCompleted(); + + if (this.canSetCompleted()) { + console.log("XXX 3b moving RESTING -> COMPLETED"); + this._state = COMPLETED; + this.setCompleted(); + } else { + console.log("XXX 3a moving RESTING -> null"); + this._state = null; + } + // As we are changing state and require it to be reprocessed + // invoke the next step of program + program.next(program); + break; + default: + throw "Exercise: Attempting to move to an unknown state"; + } + + program.emit("redraw"); + } +} \ No newline at end of file diff --git a/apps/buffgym/buffgym-icon.js b/apps/buffgym/buffgym-icon.js new file mode 100644 index 000000000..949b0e45b --- /dev/null +++ b/apps/buffgym/buffgym-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwhC/AFEEolAC6lN7vdDCcECwPd6guVGCYuDC4cCBQMikQXQJAMjkECmcyIx4XDmUjmYvLC4XUDARHBIoIWLgATCGQdA7tEonQC5ouDDYg0BOxgSEAggwKRwgUCC6ZIDSwoXNogWDDgNCAgIWIkUEoUk6kiCgMkokipsiBIQXIki2CAgNCAoYADC5Eic4Mic4ICCAIIJCC5MzAAcykYGEAAIXOABAXTmUzGoIXVAIIXLB4SICDIovjO76PZbYR3PDI4XiI6530MIh3SC6R33C/oAOC48CCxsgC44A/ADY=")) diff --git a/apps/buffgym/buffgym-program.js b/apps/buffgym/buffgym-program.js new file mode 100644 index 000000000..22f39f10b --- /dev/null +++ b/apps/buffgym/buffgym-program.js @@ -0,0 +1,68 @@ +class Program { + constructor(params) { + const DEFAULTS = { + title: "Unknown", + trainDay: "", // Day of week + }; + const p = Object.assign({}, DEFAULTS, params); + + this._title = p.title; + this._trainDay = p.trainDay; + this._exercises = []; + + this.on("redraw", redraw.bind(null, this)); + } + + get title() { + return `${this._title} - ${this._trainDay}`; + } + + addExercise(exercise) { + this._exercises.push(exercise); + } + + addExercises(exercises) { + exercises.forEach(exercise => this.addExercise(exercise)); + } + + currentExercise() { + return ( + this._exercises + .filter(exercise => !exercise.isCompleted())[0] + ); + } + + canComplete() { + return ( + this._exercises + .filter(exercise => exercise.isCompleted()) + .length === this._exercises.length + ); + } + + setCompleted() { + if (!this.canComplete()) throw "All exercises must be completed"; + this._completed = true; + } + + isCompleted() { + return !!this._completed; + } + + // State machine + next() { + console.log("XXX Program.next"); + const exercise = this.currentExercise(); + + // All exercises are completed so mark the + // Program as comleted + if (this.canComplete()) { + this.setCompleted(); + this.emit("redraw"); + + return; + } + + exercise.next(this); + } +} \ No newline at end of file diff --git a/apps/buffgym/buffgym-set.js b/apps/buffgym/buffgym-set.js new file mode 100644 index 000000000..d7d610a78 --- /dev/null +++ b/apps/buffgym/buffgym-set.js @@ -0,0 +1,46 @@ +class Set { + constructor(maxReps) { + this._minReps = 0; + this._maxReps = maxReps; + this._reps = 0; + this._completed = false; + } + + get title() { + return this._title; + } + + get weight() { + return this._weight; + } + + isCompleted() { + return !!this._completed; + } + + setCompleted() { + this._completed = true; + } + + get reps() { + return this._reps; + } + + get maxReps() { + return this._maxReps; + } + + incReps() { + if (this._completed) return; + if (this._reps >= this._maxReps) return; + + this._reps++; + } + + decReps() { + if (this._completed) return; + if (this._reps <= this._minReps) return; + + this._reps--; + } +} \ No newline at end of file diff --git a/apps/buffgym/buffgym.app.js b/apps/buffgym/buffgym.app.js new file mode 100755 index 000000000..e74b7062a --- /dev/null +++ b/apps/buffgym/buffgym.app.js @@ -0,0 +1,311 @@ +/* global g, setWatch, clearWatch, reset, BTN1, BTN2, BTN3 */ + +(() => { + const W = g.getWidth(); + const H = g.getHeight(); + const RED = "#d32e29"; + const PINK = "#f05a56"; + const WHITE = "#ffffff"; + + const Set = require("set.js"); + const Exercise = require("exercise.js"); + const Program = require("program.js"); + + function centerStringX(str) { + return (W - g.stringWidth(str)) / 2; + } + + function iconIncrement() { + const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglHA4IpJBYwTHA4RMJCY5oDJo4THKIQKET5IMGCaY7TMaKLTWajbTFJIlICgoVBFYXJYQYSGCggAGCRAVIBgw")); + return img; + } + + function iconDecrement() { + const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglKFJADBCRYABCYQmOFAhNMKIw6FTw4LHCaY7TMaKLTWajbTFJglFCgoVBFYXJYQYSGCggAGCRAVIBgw=")); + return img; + } + + function iconOk() { + const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglKFJADBCJQxCCYQmMIwZoDJpQMCKIg6KBYwTGFQgeHHYouCCRI7EMYTXFRhILEK5SfFRgYSIborbSbpglFCgoVBFYXJYQYSGCggAGCRAVIBgwA==")); + return img; + } + + function drawMenu(params) { + const DEFAULT_PARAMS = { + showBTN1: false, + showBTN2: false, + showBTN3: false, + }; + const p = Object.assign({}, DEFAULT_PARAMS, params); + if (p.showBTN1) g.drawImage(iconIncrement(), W - 30, 10); + if (p.showBTN2) g.drawImage(iconOk(), W - 30, 110); + if (p.showBTN3) g.drawImage(iconDecrement(), W - 30, 210); + } + + function clearScreen() { + g.setColor(RED); + g.fillRect(0,0,W,H); + } + + function drawTitle(exercise) { + const title = exercise.humanTitle; + + g.setFont("Vector",20); + g.setColor(WHITE); + g.drawString(title, centerStringX(title), 5); + } + + function drawReps(exercise) { + const set = exercise.currentSet; + if (set.isCompleted()) return; + + g.setColor(PINK); + g.fillCircle(W / 2, H / 2, 50); + g.setColor(WHITE); + g.setFont("Vector", 40); + g.drawString(set.reps, centerStringX(set.reps), (H - 45) / 2); + g.setFont("Vector", 15); + const note = `of ${set.maxReps}`; + g.drawString(note, centerStringX(note), (H / 2) + 25); + } + + function drawSets(exercise) { + const sets = exercise.subTitle; + + g.setColor(WHITE); + g.setFont("Vector", 15); + g.drawString(sets, centerStringX(sets), H - 25); + } + + function drawSetProgress(exercise) { + drawTitle(exercise); + drawReps(exercise); + drawSets(exercise); + drawMenu({showBTN1: true, showBTN2: true, showBTN3: true}); + } + + function drawStartNextExercise() { + const title = "Good work"; + const msg = "No need to rest\nmove straight on\nto the next exercise"; + + g.setColor(WHITE); + g.setFont("Vector", 35); + g.drawString(title, centerStringX(title), 10); + g.setFont("Vector", 15); + g.drawString(msg, 30, 150); + drawMenu({showBTN1: false, showBTN2: true, showBTN3: false}); + } + + function drawProgramCompleted() { + const title1 = "You did"; + const title2 = "GREAT!"; + const msg = "That's the program\ncompleted. Now eat\nsome food and\nget plenty of rest."; + + clearWatch(); + setWatch(reset, BTN2, {repeat: false}); + + g.setColor(WHITE); + g.setFont("Vector", 35); + g.drawString(title1, centerStringX(title1), 10); + g.setFont("Vector", 40); + g.drawString(title2, centerStringX(title2), 50); + g.setFont("Vector", 15); + g.drawString(msg, 30, 150); + drawMenu({showBTN1: false, showBTN2: true, showBTN3: false}); + } + + /* + function drawExerciseCompleted(program) { + const exercise = program.currentExercise(); + const title = exercise.canProgress? + "WELL DONE!" : + "NOT BAD!"; + const msg = exercise.canProgress? + `You weight is automatically increased\nfor ${exercise.title} to ${exercise.weight}${exercise.unit}` : + "It looks like you struggled\non a few sets, your weight will\nstay the same"; + const action = "Move straight on to the next exercise"; + + clearScreen(); + g.setColor(WHITE); + g.setFont("Vector", 20); + g.drawString(title, centerStringX(title), 10); + g.setFont("Vector", 10); + g.drawString(msg, centerStringX(msg), 180); + g.drawString(action, centerStringX(action), 210); + drawMenu({showBTN2: true}); + + clearWatch(); + setWatch(() => { + init(program); + }, BTN2, {repeat: false}); + } + */ + + function drawRestTimer(program) { + const exercise = program.currentExercise(); + const motivation = "Take a breather.."; + clearScreen(); + drawMenu({showBTN2: true}); + + g.setColor(PINK); + g.fillCircle(W / 2, H / 2, 50); + g.setColor(WHITE); + g.setFont("Vector", 15); + g.drawString(motivation, centerStringX(motivation), 25); + g.setFont("Vector", 40); + g.drawString(exercise.restPeriod, centerStringX(exercise.restPeriod), (H - 45) / 2); + exercise.decRestPeriod(); + + if (exercise.restPeriod <= 0) { + exercise.resetRestTimer(); + redraw(program); + } + } + + function redraw(program) { + const exercise = program.currentExercise(); + + clearScreen(); + + if (program.isCompleted()) { + drawProgramCompleted(program); + return; + } + + if (exercise.isRestTimerRunning()) { + if (exercise.isLastSet()) { + drawStartNextExercise(program); + } else { + drawRestTimer(program); + } + + return; + } + + drawSetProgress(exercise); + } + + function init(program) { + clearWatch(); + program.next(); + } + + // Setup training program. This should come from file + + // Squats + function buildPrograms() { + const squats = new Exercise({ + title: "Squats", + weight: 40, + unit: "Kg", + }); + squats.addSets([ + new Set(5), + new Set(5), + new Set(5), + new Set(5), + new Set(5), + ]); + + const bench = new Exercise({ + title: "Bench press", + weight: 20, + unit: "Kg", + }); + bench.addSets([ + new Set(5), + new Set(5), + new Set(5), + new Set(5), + new Set(5), + ]); + + const row = new Exercise({ + title: "Row", + weight: 20, + unit: "Kg", + }); + row.addSets([ + new Set(5), + new Set(5), + new Set(5), + new Set(5), + new Set(5), + ]); + + const ohPress = new Exercise({ + title: "Overhead press", + weight: 20, + unit: "Kg", + }); + ohPress.addSets([ + new Set(5), + new Set(5), + new Set(5), + new Set(5), + new Set(5), + ]); + + const deadlift = new Exercise({ + title: "Deadlift", + weight: 20, + unit: "Kg", + }); + deadlift.addSets([ + new Set(5), + ]); + + const pullups = new Exercise({ + title: "Pullups", + weight: 0, + unit: "Kg", + }); + pullups.addSets([ + new Set(10), + new Set(10), + new Set(10), + ]); + + const triceps = new Exercise({ + title: "Tricep extension", + weight: 20, + unit: "Kg", + }); + triceps.addSets([ + new Set(10), + new Set(10), + new Set(10), + ]); + + const programA = new Program({ + title: "Program A", + }); + programA.addExercises([ + squats, + ohPress, + deadlift, + pullups, + ]); + + const programB = new Program({ + title: "Program B", + }); + programB.addExercises([ + squats, + bench, + row, + triceps, + ]); + + const programs = [ + programA, + programB, + ]; + return programs; + } + + // For this spike, just run the first program, what will + // really happen is the user picks a program to do from + // some menu on a start page. + init(buildPrograms()[0]); +})(); \ No newline at end of file diff --git a/apps/buffgym/buffgym.png b/apps/buffgym/buffgym.png new file mode 100644 index 0000000000000000000000000000000000000000..93a29a4a686f172e5e6dbd2ecb2a1d3edf4751c0 GIT binary patch literal 1800 zcmV+j2lx1iP)tlxqC9)f@Omu&Na^VuQ3o*OkLX&0x0b91a zC7LC3CR;|G&UwKyoC)@U)%oe$2-|YHkk(v4M!t35mBz$@a#wDU>?rxOl>4iDU0;K~&^NeJ~rZV1qG0 z2VEWRN{fW6B-AV=vyLG{LNQb2i6*D;)z1+>HV9oV)YY-AYv;CEAGyt1BxHeIB6`O1 zgNbCKvH5&(h}tjTY&oMqX-z=6c>c(BKect-eBUPt!g0uGg&+h40X>Fzw5_e#pVR#$ zXoP~+BFSrtk0nGN171%U%*9ZeH|HLjvY%#M?bNRbI9pq_zjAQwp^ph~HbFcpN~ZRz z(p@Mo-ho770lfp=4bOss8*F%I4msLmKr#xaD)J*)`yvYx`ky+nJwKX`iBeY-qKGqH z?QS=3P!M$53s7Hu5JniTm@SU>rp6YK0dui1iN|DU+@DO!eyGs{ttdh(`FAE0vla{o z?WidZt%?gW#AR7;<X-pbXX@H zNxpMYdq4RMO4a}WV;wc05JSkxHD~SsCa-dTj zNTxVqQheR_7dUJ%8VslVKYYW7jWB?j;7(;1x|h0^D=>+_|8G8A);PQcVdy~=1vqUM z=Bmmm)pMW?ZosIy0KK;IbR&uXN!$f_n-$fCGwJquLBgH9YRnj{JTt}EHBxNEeXlt^ z=25u`mrr_dyKd3t92OJZd{9ZwAii{~WsvTQjEnfS8xO2y_@!_U?;}HwP2R(Ijz)}m zit&g9Oe?63KH;Lj-FATAd+p`e9q7aT9cJ8VbS}; zQ^kGnzzKFl@TSGDoSS-YQ$q_XStXt~30!=tPYtT;z`;H5V0>y6M#+fsq8;frcBVm9 z*_*JM9oV^KK>zTcC~y_yt(v2JXX>jDabwn+OJjC>_p^QzY!*8o^Zl)_Rq0Y~0Idq0 zS2Ejbz57Z043707FgeP1%EK<&HQJ?2qjG*1bTS%v z%ubCHDDjk|*z-F39AIq&YmVQ|gsn7eCKBY&B;IDWBhQ|XP-F^P(QUxw%mhX!9y3?4 za|0-g(DPbUZ?Ai9XPitl89;gYdW9(B>6do0 z&{d=<|EJJx0R4iRi$%B)8xs*{tC-D)*IL!li9sItRi$t8xdUT=@j7sD@4L`~ZUg#< z@9|7Teoy(EeEDwwZ_kbU;PA;c-%(!VWd_vn&m{hDmP)mv+W`74ORG=SmA{Aypl2;4 z5^=uzMCz^;-3HXM(oa{4%%IK%Ex`EHFzC+|RLpt@yWwJmOe>of#Mu@=51VEH#S6s{ z9o#%D1JN_>#7hfe`U6y8YJ?wyDGkFo zERDt!OF!~i5Yltbc`JfdVhJF1ywFnv_&cBz%HNa*=^#c>zCWO1`&}!lb?WRyb7@D_ zoMJQ4ZGbZ)W6%m}S;fo+s9pL&qm@ky;%p0`2i^anc~wxo1C;V!MN}J*?VR%}kkbG~ qkv|qi@hCO{S(a~Li!HWTm;V6Sm{)4DoNDL*0000 Date: Mon, 20 Apr 2020 15:27:21 +0100 Subject: [PATCH 02/14] Update apps.json --- apps.json | 4 + apps/buffgym/buffgym-programs.json | 101 ++++++++++++++++++++++++ apps/buffgym/buffgym.app.js | 121 +++++------------------------ 3 files changed, 125 insertions(+), 101 deletions(-) create mode 100644 apps/buffgym/buffgym-programs.json diff --git a/apps.json b/apps.json index e993f0e18..bff5fe66b 100644 --- a/apps.json +++ b/apps.json @@ -1305,6 +1305,10 @@ "storage": [ {"name":"buffgym"}, {"name":"buffgym.app.js"}, + {"name":"buffgym-set.js","url":"buffgym-set.js"}, + {"name":"buffgym-exercise.js","url":"buffgym-exercise.js"}, + {"name":"buffgym-program.js","url":"buffgym-program.js"}, + {"name":"buffgym-programs.json","url":"buffgym-program.json"}, {"name":"buffgym.img","url":"buffgym-icon.js","evaluate":true} ] } diff --git a/apps/buffgym/buffgym-programs.json b/apps/buffgym/buffgym-programs.json new file mode 100644 index 000000000..bf5aa0e0d --- /dev/null +++ b/apps/buffgym/buffgym-programs.json @@ -0,0 +1,101 @@ +[ + { + title: "Program A", + exercises: [ + { + title: "Squats", + weight: 40, + unit: "Kg", + sets: [ + 5, + 5, + 5, + 5, + 5 + ] + }, + { + title: "Overhead press", + weight: 20, + unit: "Kg", + sets: [ + 5, + 5, + 5, + 5, + 5 + ] + }, + { + title: "Deadlift", + weight: 20, + unit: "Kg", + sets: [ + 5 + ] + }, + { + title: "Pullups", + weight: 0, + unit: "Kg", + sets: [ + 10, + 10, + 10 + ] + } + ] + }, + { + title: "Program B", + exercises: [ + { + title: "Squats", + weight: 40, + unit: "Kg", + sets: [ + 5, + 5, + 5, + 5, + 5 + ] + }, + { + title: "Bench press", + weight: 20, + unit: "Kg", + sets: [ + 5, + 5, + 5, + 5, + 5 + ] + }, + { + title: "Row", + weight: 20, + unit:"Kg", + sets: [ + 5, + 5, + 5, + 5, + 5 + ] + + }, + { + title: "Tricep extension", + weight: 20, + unit: "Kg", + sets: [ + 10, + 10, + 10 + ] + } + ] + } +] \ No newline at end of file diff --git a/apps/buffgym/buffgym.app.js b/apps/buffgym/buffgym.app.js index e74b7062a..d3a4e6834 100755 --- a/apps/buffgym/buffgym.app.js +++ b/apps/buffgym/buffgym.app.js @@ -194,113 +194,32 @@ // Squats function buildPrograms() { - const squats = new Exercise({ - title: "Squats", - weight: 40, - unit: "Kg", - }); - squats.addSets([ - new Set(5), - new Set(5), - new Set(5), - new Set(5), - new Set(5), - ]); + const programsJSON = require("Storage").readJSON("buffgym-programs.json", 1); - const bench = new Exercise({ - title: "Bench press", - weight: 20, - unit: "Kg", - }); - bench.addSets([ - new Set(5), - new Set(5), - new Set(5), - new Set(5), - new Set(5), - ]); + if (!programsJSON) throw "No programs JSON found"; - const row = new Exercise({ - title: "Row", - weight: 20, - unit: "Kg", - }); - row.addSets([ - new Set(5), - new Set(5), - new Set(5), - new Set(5), - new Set(5), - ]); + const programs = []; - const ohPress = new Exercise({ - title: "Overhead press", - weight: 20, - unit: "Kg", - }); - ohPress.addSets([ - new Set(5), - new Set(5), - new Set(5), - new Set(5), - new Set(5), - ]); + programsJSON.forEach(programJSON => { + const program = new Program({ + title: programJSON.title, + }); + const exercises = programJSON.exercises.map(exerciseJSON => { + const exercise = new Exercise({ + title: exerciseJSON.title, + weight: exerciseJSON.weight, + unit: exerciseJSON.unit, + }); + exerciseJSON.sets.forEach(setJSON => { + exercise.addSet(new Set(setJSON)); + }); - const deadlift = new Exercise({ - title: "Deadlift", - weight: 20, - unit: "Kg", + return exercise; + }); + program.addExercises(exercises); + programs.push(program); }); - deadlift.addSets([ - new Set(5), - ]); - const pullups = new Exercise({ - title: "Pullups", - weight: 0, - unit: "Kg", - }); - pullups.addSets([ - new Set(10), - new Set(10), - new Set(10), - ]); - - const triceps = new Exercise({ - title: "Tricep extension", - weight: 20, - unit: "Kg", - }); - triceps.addSets([ - new Set(10), - new Set(10), - new Set(10), - ]); - - const programA = new Program({ - title: "Program A", - }); - programA.addExercises([ - squats, - ohPress, - deadlift, - pullups, - ]); - - const programB = new Program({ - title: "Program B", - }); - programB.addExercises([ - squats, - bench, - row, - triceps, - ]); - - const programs = [ - programA, - programB, - ]; return programs; } From 3e9f581b9fb3adcf23421fff4956490f20fdc452 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Mon, 20 Apr 2020 17:09:27 +0100 Subject: [PATCH 03/14] Fix apps.json --- apps.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps.json b/apps.json index bff5fe66b..7a1a3f5fb 100644 --- a/apps.json +++ b/apps.json @@ -1308,7 +1308,7 @@ {"name":"buffgym-set.js","url":"buffgym-set.js"}, {"name":"buffgym-exercise.js","url":"buffgym-exercise.js"}, {"name":"buffgym-program.js","url":"buffgym-program.js"}, - {"name":"buffgym-programs.json","url":"buffgym-program.json"}, + {"name":"buffgym-programs.json","url":"buffgym-programs.json"}, {"name":"buffgym.img","url":"buffgym-icon.js","evaluate":true} ] } From 900805e71b2a670ef5098f87a71525c0b7672c36 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Mon, 20 Apr 2020 17:20:23 +0100 Subject: [PATCH 04/14] Fix apps.json --- apps.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps.json b/apps.json index 7a1a3f5fb..4eeee4e32 100644 --- a/apps.json +++ b/apps.json @@ -1304,7 +1304,7 @@ "type": "app", "storage": [ {"name":"buffgym"}, - {"name":"buffgym.app.js"}, + {"name":"buffgym.app.js", "url": "buffgym.app.js"}, {"name":"buffgym-set.js","url":"buffgym-set.js"}, {"name":"buffgym-exercise.js","url":"buffgym-exercise.js"}, {"name":"buffgym-program.js","url":"buffgym-program.js"}, From 707f4a1ccdd4e3f89095529060de429d6d1fa827 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Mon, 20 Apr 2020 21:39:30 +0100 Subject: [PATCH 05/14] More fixes --- apps/buffgym/buffgym-exercise.js | 4 +- apps/buffgym/buffgym-program.js | 2 +- apps/buffgym/buffgym-programs.json | 102 +---- apps/buffgym/buffgym-programs.json.unminified | 101 +++++ apps/buffgym/buffgym-set.js | 2 +- apps/buffgym/buffgym.app.js | 420 +++++++++--------- 6 files changed, 313 insertions(+), 318 deletions(-) create mode 100644 apps/buffgym/buffgym-programs.json.unminified diff --git a/apps/buffgym/buffgym-exercise.js b/apps/buffgym/buffgym-exercise.js index b7a1e3e15..99d658571 100644 --- a/apps/buffgym/buffgym-exercise.js +++ b/apps/buffgym/buffgym-exercise.js @@ -2,10 +2,8 @@ const STARTED = 1; const RESTING = 2; const COMPLETED = 3; const ONE_SECOND = 1000; -const INCREMENT = "increment"; -const DECREMENT = "decrement"; -class Exercise { +exports = class Exercise { constructor(params /*{title, weight, unit, restPeriod}*/) { const DEFAULTS = { title: "Unknown", diff --git a/apps/buffgym/buffgym-program.js b/apps/buffgym/buffgym-program.js index 22f39f10b..956827f56 100644 --- a/apps/buffgym/buffgym-program.js +++ b/apps/buffgym/buffgym-program.js @@ -1,4 +1,4 @@ -class Program { +exports = class Program { constructor(params) { const DEFAULTS = { title: "Unknown", diff --git a/apps/buffgym/buffgym-programs.json b/apps/buffgym/buffgym-programs.json index bf5aa0e0d..7551c7a47 100644 --- a/apps/buffgym/buffgym-programs.json +++ b/apps/buffgym/buffgym-programs.json @@ -1,101 +1 @@ -[ - { - title: "Program A", - exercises: [ - { - title: "Squats", - weight: 40, - unit: "Kg", - sets: [ - 5, - 5, - 5, - 5, - 5 - ] - }, - { - title: "Overhead press", - weight: 20, - unit: "Kg", - sets: [ - 5, - 5, - 5, - 5, - 5 - ] - }, - { - title: "Deadlift", - weight: 20, - unit: "Kg", - sets: [ - 5 - ] - }, - { - title: "Pullups", - weight: 0, - unit: "Kg", - sets: [ - 10, - 10, - 10 - ] - } - ] - }, - { - title: "Program B", - exercises: [ - { - title: "Squats", - weight: 40, - unit: "Kg", - sets: [ - 5, - 5, - 5, - 5, - 5 - ] - }, - { - title: "Bench press", - weight: 20, - unit: "Kg", - sets: [ - 5, - 5, - 5, - 5, - 5 - ] - }, - { - title: "Row", - weight: 20, - unit:"Kg", - sets: [ - 5, - 5, - 5, - 5, - 5 - ] - - }, - { - title: "Tricep extension", - weight: 20, - unit: "Kg", - sets: [ - 10, - 10, - 10 - ] - } - ] - } -] \ No newline at end of file +[{"title":"Program A","exercises":[{"title":"Squats","weight":40,"unit":"Kg","sets":[5,5,5,5,5]},{"title":"Overhead press","weight":20,"unit":"Kg","sets":[5,5,5,5,5]},{"title":"Deadlift","weight":20,"unit":"Kg","sets":[5]},{"title":"Pullups","weight":0,"unit":"Kg","sets":[10,10,10]}]},{"title":"Program B","exercises":[{"title":"Squats","weight":40,"unit":"Kg","sets":[5,5,5,5,5]},{"title":"Bench press","weight":20,"unit":"Kg","sets":[5,5,5,5,5]},{"title":"Row","weight":20,"unit":"Kg","sets":[5,5,5,5,5]},{"title":"Tricep extension","weight":20,"unit":"Kg","sets":[10,10,10]}]}] \ No newline at end of file diff --git a/apps/buffgym/buffgym-programs.json.unminified b/apps/buffgym/buffgym-programs.json.unminified new file mode 100644 index 000000000..cd005eeab --- /dev/null +++ b/apps/buffgym/buffgym-programs.json.unminified @@ -0,0 +1,101 @@ +[ + { + "title": "Program A", + "exercises": [ + { + "title": "Squats", + "weight": 40, + "unit": "Kg", + "sets": [ + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "title": "Overhead press", + "weight": 20, + "unit": "Kg", + "sets": [ + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "title": "Deadlift", + "weight": 20, + "unit": "Kg", + "sets": [ + 5 + ] + }, + { + "title": "Pullups", + "weight": 0, + "unit": "Kg", + "sets": [ + 10, + 10, + 10 + ] + } + ] + }, + { + "title": "Program B", + "exercises": [ + { + "title": "Squats", + "weight": 40, + "unit": "Kg", + "sets": [ + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "title": "Bench press", + "weight": 20, + "unit": "Kg", + "sets": [ + 5, + 5, + 5, + 5, + 5 + ] + }, + { + "title": "Row", + "weight": 20, + "unit":"Kg", + "sets": [ + 5, + 5, + 5, + 5, + 5 + ] + + }, + { + "title": "Tricep extension", + "weight": 20, + "unit": "Kg", + "sets": [ + 10, + 10, + 10 + ] + } + ] + } +] \ No newline at end of file diff --git a/apps/buffgym/buffgym-set.js b/apps/buffgym/buffgym-set.js index d7d610a78..aed6df260 100644 --- a/apps/buffgym/buffgym-set.js +++ b/apps/buffgym/buffgym-set.js @@ -1,4 +1,4 @@ -class Set { +exports = class Set { constructor(maxReps) { this._minReps = 0; this._maxReps = maxReps; diff --git a/apps/buffgym/buffgym.app.js b/apps/buffgym/buffgym.app.js index d3a4e6834..11005900a 100755 --- a/apps/buffgym/buffgym.app.js +++ b/apps/buffgym/buffgym.app.js @@ -1,230 +1,226 @@ -/* global g, setWatch, clearWatch, reset, BTN1, BTN2, BTN3 */ +const W = g.getWidth(); +const H = g.getHeight(); +const RED = "#d32e29"; +const PINK = "#f05a56"; +const WHITE = "#ffffff"; -(() => { - const W = g.getWidth(); - const H = g.getHeight(); - const RED = "#d32e29"; - const PINK = "#f05a56"; - const WHITE = "#ffffff"; +const Set = require("buffgym-set.js"); +const Exercise = require("buffgym-exercise.js"); +const Program = require("buffgym-program.js"); - const Set = require("set.js"); - const Exercise = require("exercise.js"); - const Program = require("program.js"); +function centerStringX(str) { + return (W - g.stringWidth(str)) / 2; +} - function centerStringX(str) { - return (W - g.stringWidth(str)) / 2; +function iconIncrement() { + const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglHA4IpJBYwTHA4RMJCY5oDJo4THKIQKET5IMGCaY7TMaKLTWajbTFJIlICgoVBFYXJYQYSGCggAGCRAVIBgw")); + return img; +} + +function iconDecrement() { + const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglKFJADBCRYABCYQmOFAhNMKIw6FTw4LHCaY7TMaKLTWajbTFJglFCgoVBFYXJYQYSGCggAGCRAVIBgw=")); + return img; +} + +function iconOk() { + const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglKFJADBCJQxCCYQmMIwZoDJpQMCKIg6KBYwTGFQgeHHYouCCRI7EMYTXFRhILEK5SfFRgYSIborbSbpglFCgoVBFYXJYQYSGCggAGCRAVIBgwA==")); + return img; +} + +function drawMenu(params) { + const DEFAULT_PARAMS = { + showBTN1: false, + showBTN2: false, + showBTN3: false, + }; + const p = Object.assign({}, DEFAULT_PARAMS, params); + if (p.showBTN1) g.drawImage(iconIncrement(), W - 30, 10); + if (p.showBTN2) g.drawImage(iconOk(), W - 30, 110); + if (p.showBTN3) g.drawImage(iconDecrement(), W - 30, 210); +} + +function clearScreen() { + g.setColor(RED); + g.fillRect(0,0,W,H); +} + +function drawTitle(exercise) { + const title = exercise.humanTitle; + + g.setFont("Vector",20); + g.setColor(WHITE); + g.drawString(title, centerStringX(title), 5); +} + +function drawReps(exercise) { + const set = exercise.currentSet; + if (set.isCompleted()) return; + + g.setColor(PINK); + g.fillCircle(W / 2, H / 2, 50); + g.setColor(WHITE); + g.setFont("Vector", 40); + g.drawString(set.reps, centerStringX(set.reps), (H - 45) / 2); + g.setFont("Vector", 15); + const note = `of ${set.maxReps}`; + g.drawString(note, centerStringX(note), (H / 2) + 25); +} + +function drawSets(exercise) { + const sets = exercise.subTitle; + + g.setColor(WHITE); + g.setFont("Vector", 15); + g.drawString(sets, centerStringX(sets), H - 25); +} + +function drawSetProgress(exercise) { + drawTitle(exercise); + drawReps(exercise); + drawSets(exercise); + drawMenu({showBTN1: true, showBTN2: true, showBTN3: true}); +} + +function drawStartNextExercise() { + const title = "Good work"; + const msg = "No need to rest\nmove straight on\nto the next exercise"; + + g.setColor(WHITE); + g.setFont("Vector", 35); + g.drawString(title, centerStringX(title), 10); + g.setFont("Vector", 15); + g.drawString(msg, 30, 150); + drawMenu({showBTN1: false, showBTN2: true, showBTN3: false}); +} + +function drawProgramCompleted() { + const title1 = "You did"; + const title2 = "GREAT!"; + const msg = "That's the program\ncompleted. Now eat\nsome food and\nget plenty of rest."; + + clearWatch(); + setWatch(reset, BTN2, {repeat: false}); + + g.setColor(WHITE); + g.setFont("Vector", 35); + g.drawString(title1, centerStringX(title1), 10); + g.setFont("Vector", 40); + g.drawString(title2, centerStringX(title2), 50); + g.setFont("Vector", 15); + g.drawString(msg, 30, 150); + drawMenu({showBTN1: false, showBTN2: true, showBTN3: false}); +} + +/* +function drawExerciseCompleted(program) { + const exercise = program.currentExercise(); + const title = exercise.canProgress? + "WELL DONE!" : + "NOT BAD!"; + const msg = exercise.canProgress? + `You weight is automatically increased\nfor ${exercise.title} to ${exercise.weight}${exercise.unit}` : + "It looks like you struggled\non a few sets, your weight will\nstay the same"; + const action = "Move straight on to the next exercise"; + + clearScreen(); + g.setColor(WHITE); + g.setFont("Vector", 20); + g.drawString(title, centerStringX(title), 10); + g.setFont("Vector", 10); + g.drawString(msg, centerStringX(msg), 180); + g.drawString(action, centerStringX(action), 210); + drawMenu({showBTN2: true}); + + clearWatch(); + setWatch(() => { + init(program); + }, BTN2, {repeat: false}); +} +*/ + +function drawRestTimer(program) { + const exercise = program.currentExercise(); + const motivation = "Take a breather.."; + clearScreen(); + drawMenu({showBTN2: true}); + + g.setColor(PINK); + g.fillCircle(W / 2, H / 2, 50); + g.setColor(WHITE); + g.setFont("Vector", 15); + g.drawString(motivation, centerStringX(motivation), 25); + g.setFont("Vector", 40); + g.drawString(exercise.restPeriod, centerStringX(exercise.restPeriod), (H - 45) / 2); + exercise.decRestPeriod(); + + if (exercise.restPeriod <= 0) { + exercise.resetRestTimer(); + redraw(program); + } +} + +function redraw(program) { + const exercise = program.currentExercise(); + + clearScreen(); + + if (program.isCompleted()) { + drawProgramCompleted(program); + return; } - function iconIncrement() { - const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglHA4IpJBYwTHA4RMJCY5oDJo4THKIQKET5IMGCaY7TMaKLTWajbTFJIlICgoVBFYXJYQYSGCggAGCRAVIBgw")); - return img; - } - - function iconDecrement() { - const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglKFJADBCRYABCYQmOFAhNMKIw6FTw4LHCaY7TMaKLTWajbTFJglFCgoVBFYXJYQYSGCggAGCRAVIBgw=")); - return img; - } - - function iconOk() { - const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglKFJADBCJQxCCYQmMIwZoDJpQMCKIg6KBYwTGFQgeHHYouCCRI7EMYTXFRhILEK5SfFRgYSIborbSbpglFCgoVBFYXJYQYSGCggAGCRAVIBgwA==")); - return img; - } - - function drawMenu(params) { - const DEFAULT_PARAMS = { - showBTN1: false, - showBTN2: false, - showBTN3: false, - }; - const p = Object.assign({}, DEFAULT_PARAMS, params); - if (p.showBTN1) g.drawImage(iconIncrement(), W - 30, 10); - if (p.showBTN2) g.drawImage(iconOk(), W - 30, 110); - if (p.showBTN3) g.drawImage(iconDecrement(), W - 30, 210); - } - - function clearScreen() { - g.setColor(RED); - g.fillRect(0,0,W,H); - } - - function drawTitle(exercise) { - const title = exercise.humanTitle; - - g.setFont("Vector",20); - g.setColor(WHITE); - g.drawString(title, centerStringX(title), 5); - } - - function drawReps(exercise) { - const set = exercise.currentSet; - if (set.isCompleted()) return; - - g.setColor(PINK); - g.fillCircle(W / 2, H / 2, 50); - g.setColor(WHITE); - g.setFont("Vector", 40); - g.drawString(set.reps, centerStringX(set.reps), (H - 45) / 2); - g.setFont("Vector", 15); - const note = `of ${set.maxReps}`; - g.drawString(note, centerStringX(note), (H / 2) + 25); - } - - function drawSets(exercise) { - const sets = exercise.subTitle; - - g.setColor(WHITE); - g.setFont("Vector", 15); - g.drawString(sets, centerStringX(sets), H - 25); - } - - function drawSetProgress(exercise) { - drawTitle(exercise); - drawReps(exercise); - drawSets(exercise); - drawMenu({showBTN1: true, showBTN2: true, showBTN3: true}); - } - - function drawStartNextExercise() { - const title = "Good work"; - const msg = "No need to rest\nmove straight on\nto the next exercise"; - - g.setColor(WHITE); - g.setFont("Vector", 35); - g.drawString(title, centerStringX(title), 10); - g.setFont("Vector", 15); - g.drawString(msg, 30, 150); - drawMenu({showBTN1: false, showBTN2: true, showBTN3: false}); - } - - function drawProgramCompleted() { - const title1 = "You did"; - const title2 = "GREAT!"; - const msg = "That's the program\ncompleted. Now eat\nsome food and\nget plenty of rest."; - - clearWatch(); - setWatch(reset, BTN2, {repeat: false}); - - g.setColor(WHITE); - g.setFont("Vector", 35); - g.drawString(title1, centerStringX(title1), 10); - g.setFont("Vector", 40); - g.drawString(title2, centerStringX(title2), 50); - g.setFont("Vector", 15); - g.drawString(msg, 30, 150); - drawMenu({showBTN1: false, showBTN2: true, showBTN3: false}); - } - - /* - function drawExerciseCompleted(program) { - const exercise = program.currentExercise(); - const title = exercise.canProgress? - "WELL DONE!" : - "NOT BAD!"; - const msg = exercise.canProgress? - `You weight is automatically increased\nfor ${exercise.title} to ${exercise.weight}${exercise.unit}` : - "It looks like you struggled\non a few sets, your weight will\nstay the same"; - const action = "Move straight on to the next exercise"; - - clearScreen(); - g.setColor(WHITE); - g.setFont("Vector", 20); - g.drawString(title, centerStringX(title), 10); - g.setFont("Vector", 10); - g.drawString(msg, centerStringX(msg), 180); - g.drawString(action, centerStringX(action), 210); - drawMenu({showBTN2: true}); - - clearWatch(); - setWatch(() => { - init(program); - }, BTN2, {repeat: false}); - } - */ - - function drawRestTimer(program) { - const exercise = program.currentExercise(); - const motivation = "Take a breather.."; - clearScreen(); - drawMenu({showBTN2: true}); - - g.setColor(PINK); - g.fillCircle(W / 2, H / 2, 50); - g.setColor(WHITE); - g.setFont("Vector", 15); - g.drawString(motivation, centerStringX(motivation), 25); - g.setFont("Vector", 40); - g.drawString(exercise.restPeriod, centerStringX(exercise.restPeriod), (H - 45) / 2); - exercise.decRestPeriod(); - - if (exercise.restPeriod <= 0) { - exercise.resetRestTimer(); - redraw(program); - } - } - - function redraw(program) { - const exercise = program.currentExercise(); - - clearScreen(); - - if (program.isCompleted()) { - drawProgramCompleted(program); - return; + if (exercise.isRestTimerRunning()) { + if (exercise.isLastSet()) { + drawStartNextExercise(program); + } else { + drawRestTimer(program); } - if (exercise.isRestTimerRunning()) { - if (exercise.isLastSet()) { - drawStartNextExercise(program); - } else { - drawRestTimer(program); - } - - return; - } - - drawSetProgress(exercise); + return; } - function init(program) { - clearWatch(); - program.next(); - } + drawSetProgress(exercise); +} - // Setup training program. This should come from file +function init(program) { + clearWatch(); + program.next(); +} - // Squats - function buildPrograms() { - const programsJSON = require("Storage").readJSON("buffgym-programs.json", 1); +// Setup training program. This should come from file - if (!programsJSON) throw "No programs JSON found"; +// Squats +function buildPrograms() { + const programsJSON = require("Storage").readJSON("buffgym-programs.json", 1); - const programs = []; + if (!programsJSON) throw "No programs JSON found"; - programsJSON.forEach(programJSON => { - const program = new Program({ - title: programJSON.title, - }); - const exercises = programJSON.exercises.map(exerciseJSON => { - const exercise = new Exercise({ - title: exerciseJSON.title, - weight: exerciseJSON.weight, - unit: exerciseJSON.unit, - }); - exerciseJSON.sets.forEach(setJSON => { - exercise.addSet(new Set(setJSON)); - }); + const programs = []; - return exercise; - }); - program.addExercises(exercises); - programs.push(program); + programsJSON.forEach(programJSON => { + const program = new Program({ + title: programJSON.title, }); + const exercises = programJSON.exercises.map(exerciseJSON => { + const exercise = new Exercise({ + title: exerciseJSON.title, + weight: exerciseJSON.weight, + unit: exerciseJSON.unit, + }); + exerciseJSON.sets.forEach(setJSON => { + exercise.addSet(new Set(setJSON)); + }); - return programs; - } + return exercise; + }); + program.addExercises(exercises); + programs.push(program); + }); - // For this spike, just run the first program, what will - // really happen is the user picks a program to do from - // some menu on a start page. - init(buildPrograms()[0]); -})(); \ No newline at end of file + return programs; +} + +// For this spike, just run the first program, what will +// really happen is the user picks a program to do from +// some menu on a start page. +init(buildPrograms()[0]); \ No newline at end of file From 6deb376d99b4dab29c9ec31bd78432e08b08510e Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Mon, 20 Apr 2020 22:02:55 +0100 Subject: [PATCH 06/14] Return to launcher on exit --- apps/buffgym/buffgym.app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/buffgym/buffgym.app.js b/apps/buffgym/buffgym.app.js index 11005900a..e1ab3e66b 100755 --- a/apps/buffgym/buffgym.app.js +++ b/apps/buffgym/buffgym.app.js @@ -99,7 +99,7 @@ function drawProgramCompleted() { const msg = "That's the program\ncompleted. Now eat\nsome food and\nget plenty of rest."; clearWatch(); - setWatch(reset, BTN2, {repeat: false}); + setWatch(Bangle.showLauncher, BTN2, {repeat: false}); g.setColor(WHITE); g.setFont("Vector", 35); From 8272b19bb6517b96950502ff3f422cab8e6fecea Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Tue, 21 Apr 2020 19:38:02 +0100 Subject: [PATCH 07/14] Finish BuffGym app --- apps/buffgym/buffgym-exercise.js | 115 ++---- apps/buffgym/buffgym-icon.js | 2 +- apps/buffgym/buffgym-program-a.json | 33 ++ apps/buffgym/buffgym-program-b.json | 33 ++ apps/buffgym/buffgym-program-index.json | 10 + apps/buffgym/buffgym-program.js | 62 ++- apps/buffgym/buffgym-programs.json | 1 - apps/buffgym/buffgym-programs.json.unminified | 101 ----- apps/buffgym/buffgym-set.js | 42 +- apps/buffgym/buffgym.app.js | 373 ++++++++++-------- apps/buffgym/buffgym.png | Bin 1800 -> 7576 bytes 11 files changed, 363 insertions(+), 409 deletions(-) create mode 100644 apps/buffgym/buffgym-program-a.json create mode 100644 apps/buffgym/buffgym-program-b.json create mode 100644 apps/buffgym/buffgym-program-index.json delete mode 100644 apps/buffgym/buffgym-programs.json delete mode 100644 apps/buffgym/buffgym-programs.json.unminified mode change 100644 => 100755 apps/buffgym/buffgym.png diff --git a/apps/buffgym/buffgym-exercise.js b/apps/buffgym/buffgym-exercise.js index 99d658571..68e49be84 100644 --- a/apps/buffgym/buffgym-exercise.js +++ b/apps/buffgym/buffgym-exercise.js @@ -1,116 +1,76 @@ -const STARTED = 1; -const RESTING = 2; -const COMPLETED = 3; -const ONE_SECOND = 1000; - exports = class Exercise { - constructor(params /*{title, weight, unit, restPeriod}*/) { - const DEFAULTS = { - title: "Unknown", - weight: 0, - unit: "Kg", - restPeriod: 90, - weightIncrement: 2.5, - }; - const p = Object.assign({}, DEFAULTS, params); - - this._title = p.title; - this._weight = p.weight; - this._unit = p.unit; - this._originalRestPeriod = p.restPeriod; // Used when reseting _restPeriod - this._restPeriod = p.restPeriod; - this._weightIncrement = p.weightIncrement; - this._started = new Date(); - this._completed = false; - this._sets = []; + constructor(params) { + this.title = params.title; + this.weight = params.weight; + this.unit = params.unit; + this.restPeriod = params.restPeriod; + this.completed = false; + this.sets = []; this._restTimeout = null; this._restInterval = null; this._state = null; - } - - get title() { - return this._title; + this._originalRestPeriod = params.restPeriod; + this._weightIncrement = params.weightIncrement || 2.5; } get humanTitle() { - return `${this._title} ${this._weight}${this._unit}`; + return `${this.title} ${this.weight}${this.unit}`; } get subTitle() { - const totalSets = this._sets.length; - const uncompletedSets = this._sets.filter((set) => !set.isCompleted()).length; + const totalSets = this.sets.length; + const uncompletedSets = this.sets.filter((set) => !set.isCompleted()).length; const currentSet = (totalSets - uncompletedSets) + 1; return `Set ${currentSet} of ${totalSets}`; } - get restPeriod() { - return this._restPeriod; - } - decRestPeriod() { - this._restPeriod--; - } - - get weight() { - return this._weight; - } - - get unit() { - return this._unit; - } - - get started() { - return this._started; + this.restPeriod--; } addSet(set) { - this._sets.push(set); + this.sets.push(set); } - addSets(sets) { - sets.forEach(set => this.addSet(set)); - } - - get currentSet() { - return this._sets.filter(set => !set.isCompleted())[0]; + currentSet() { + return this.sets.filter(set => !set.isCompleted())[0]; } isLastSet() { - return this._sets.filter(set => !set.isCompleted()).length === 1; + return this.sets.filter(set => !set.isCompleted()).length === 1; } isCompleted() { - return !!this._completed; + return !!this.completed; } canSetCompleted() { - return this._sets.filter(set => set.isCompleted()).length === this._sets.length; + return this.sets.filter(set => set.isCompleted()).length === this.sets.length; } setCompleted() { if (!this.canSetCompleted()) throw "All sets must be completed"; - if (this.canProgress) this._weight += this._weightIncrement; - this._completed = true; + if (this.canProgress()) this.weight += this._weightIncrement; + this.completed = true; } - get canProgress() { + canProgress() { let completedRepsTotalSum = 0; let targetRepsTotalSum = 0; - - const completedRepsTotal = this._sets.forEach(set => completedRepsTotalSum += set.reps); - const targetRepsTotal = this._sets.forEach(set => targetRepsTotalSum += set.maxReps); + this.sets.forEach(set => completedRepsTotalSum += set.reps); + this.sets.forEach(set => targetRepsTotalSum += set.maxReps); return (targetRepsTotalSum - completedRepsTotalSum) === 0; } startRestTimer(program) { this._restTimeout = setTimeout(() => { - this.next(); - }, ONE_SECOND * this._restPeriod); + this.next(program); + }, 1000 * this.restPeriod); this._restInterval = setInterval(() => { program.emit("redraw"); - }, ONE_SECOND); + }, 1000 ); } resetRestTimer() { @@ -118,7 +78,7 @@ exports = class Exercise { clearInterval(this._restInterval); this._restTimeout = null; this._restInterval = null; - this._restPeriod = this._originalRestPeriod; + this.restPeriod = this._originalRestPeriod; } isRestTimerRunning() { @@ -129,52 +89,51 @@ exports = class Exercise { clearWatch(); setWatch(() => { - this.currentSet.incReps(); + this.currentSet().incReps(); program.emit("redraw"); }, BTN1, {repeat: true}); setWatch(program.next.bind(program), BTN2, {repeat: false}); setWatch(() => { - this.currentSet.decReps(); + this.currentSet().decReps(); program.emit("redraw"); }, BTN3, {repeat: true}); } setupRestingButtons(program) { clearWatch(); - setWatch(program.next.bind(program), BTN2, {repeat: true}); + setWatch(program.next.bind(program), BTN2, {repeat: false}); } next(program) { - global.poo = this; + const STARTED = 1; + const RESTING = 2; + const COMPLETED = 3; + switch(this._state) { case null: - console.log("XXX 1 moving null -> STARTED"); this._state = STARTED; this.setupStartedButtons(program); break; case STARTED: - console.log("XXX 2 moving STARTED -> RESTING"); this._state = RESTING; this.startRestTimer(program); this.setupRestingButtons(program); break; case RESTING: this.resetRestTimer(); - this.currentSet.setCompleted(); + this.currentSet().setCompleted(); if (this.canSetCompleted()) { - console.log("XXX 3b moving RESTING -> COMPLETED"); this._state = COMPLETED; this.setCompleted(); } else { - console.log("XXX 3a moving RESTING -> null"); this._state = null; } // As we are changing state and require it to be reprocessed // invoke the next step of program - program.next(program); + program.next(); break; default: throw "Exercise: Attempting to move to an unknown state"; diff --git a/apps/buffgym/buffgym-icon.js b/apps/buffgym/buffgym-icon.js index 949b0e45b..31764acbb 100644 --- a/apps/buffgym/buffgym-icon.js +++ b/apps/buffgym/buffgym-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwwhC/AFEEolAC6lN7vdDCcECwPd6guVGCYuDC4cCBQMikQXQJAMjkECmcyIx4XDmUjmYvLC4XUDARHBIoIWLgATCGQdA7tEonQC5ouDDYg0BOxgSEAggwKRwgUCC6ZIDSwoXNogWDDgNCAgIWIkUEoUk6kiCgMkokipsiBIQXIki2CAgNCAoYADC5Eic4Mic4ICCAIIJCC5MzAAcykYGEAAIXOABAXTmUzGoIXVAIIXLB4SICDIovjO76PZbYR3PDI4XiI6530MIh3SC6R33C/oAOC48CCxsgC44A/ADY=")) +require("heatshrink").decompress(atob("mEwxH+ACPI5AUSADAtB5vNGFQtBAIfNF95hoF4wwoF5AwmF5BhmXYbAEF/6QbF1QwIF04qB54ADAwIwoF4oRKBoIvsB4gvZ58kkgCDFxoxaF5wuHGDQcMF5IwXDZwLDGDmlDIWlkgJDSwIABCRAwPDQohCFgIABDQIOCFwYABr4RCCQIvQDYguEAAwtFF5owJDZAvHFw4vFOYQvKFAowMBxIvFMQwvPAB4wFUQ4vJGDYvUGC4vNdgyuEGDIsNFwYwGNAgAPExAvMGIdfTIovfTpYvrfRCOkZ44ugF44NGF05gUFyQvKGIoueGKIufGJ4uhG5oupGItfr4vvAAgvlGAQvt/wrEF9oEGF841IF9QGHX0oGIAD8kAAYJOFzwEBBQoMFACA=")); \ No newline at end of file diff --git a/apps/buffgym/buffgym-program-a.json b/apps/buffgym/buffgym-program-a.json new file mode 100644 index 000000000..7ebaf3741 --- /dev/null +++ b/apps/buffgym/buffgym-program-a.json @@ -0,0 +1,33 @@ +{ + "title": "Program A", + "exercises": [ + { + "title": "Squats", + "weight": 40, + "unit": "Kg", + "sets": [5, 5, 5, 5, 5], + "restPeriod": 90 + }, + { + "title": "Overhead press", + "weight": 20, + "unit": "Kg", + "sets": [5, 5, 5, 5, 5], + "restPeriod": 90 + }, + { + "title": "Deadlift", + "weight": 20, + "unit": "Kg", + "sets": [5], + "restPeriod": 90 + }, + { + "title": "Pullups", + "weight": 0, + "unit": "Kg", + "sets": [10, 10, 10], + "restPeriod": 90 + } + ] +} \ No newline at end of file diff --git a/apps/buffgym/buffgym-program-b.json b/apps/buffgym/buffgym-program-b.json new file mode 100644 index 000000000..b93348621 --- /dev/null +++ b/apps/buffgym/buffgym-program-b.json @@ -0,0 +1,33 @@ +{ + "title": "Program B", + "exercises": [ + { + "title": "Squats", + "weight": 40, + "unit": "Kg", + "sets": [5, 5, 5, 5, 5], + "restPeriod": 90 + }, + { + "title": "Bench press", + "weight": 20, + "unit": "Kg", + "sets": [5, 5, 5, 5, 5], + "restPeriod": 90 + }, + { + "title": "Row", + "weight": 20, + "unit":"Kg", + "sets": [5, 5, 5, 5, 5], + "restPeriod": 90 + }, + { + "title": "Tricep extension", + "weight": 20, + "unit": "Kg", + "sets": [10, 10, 10], + "restPeriod": 90 + } + ] +} \ No newline at end of file diff --git a/apps/buffgym/buffgym-program-index.json b/apps/buffgym/buffgym-program-index.json new file mode 100644 index 000000000..3bb51f1b5 --- /dev/null +++ b/apps/buffgym/buffgym-program-index.json @@ -0,0 +1,10 @@ +[ + { + "title": "Program A", + "file": "buffgym-program-a.json" + }, + { + "title": "Program B", + "file": "buffgym-program-b.json" + } +] \ No newline at end of file diff --git a/apps/buffgym/buffgym-program.js b/apps/buffgym/buffgym-program.js index 956827f56..68a069da5 100644 --- a/apps/buffgym/buffgym-program.js +++ b/apps/buffgym/buffgym-program.js @@ -1,68 +1,56 @@ exports = class Program { constructor(params) { - const DEFAULTS = { - title: "Unknown", - trainDay: "", // Day of week - }; - const p = Object.assign({}, DEFAULTS, params); - - this._title = p.title; - this._trainDay = p.trainDay; - this._exercises = []; - + this.title = params.title; + this.exercises = []; + this.completed = false; this.on("redraw", redraw.bind(null, this)); } - get title() { - return `${this._title} - ${this._trainDay}`; - } - - addExercise(exercise) { - this._exercises.push(exercise); - } - addExercises(exercises) { - exercises.forEach(exercise => this.addExercise(exercise)); + exercises.forEach(exercise => this.exercises.push(exercise)); } currentExercise() { - return ( - this._exercises - .filter(exercise => !exercise.isCompleted())[0] - ); + return this.exercises.filter(exercise => !exercise.isCompleted())[0]; } canComplete() { - return ( - this._exercises - .filter(exercise => exercise.isCompleted()) - .length === this._exercises.length - ); + return this.exercises.filter(exercise => exercise.isCompleted()).length === this.exercises.length; } setCompleted() { if (!this.canComplete()) throw "All exercises must be completed"; - this._completed = true; + this.completed = true; } isCompleted() { - return !!this._completed; + return !!this.completed; + } + + toJSON() { + return { + title: this.title, + exercises: this.exercises.map(exercise => { + return { + title: exercise.title, + weight: exercise.weight, + unit: exercise.unit, + sets: exercise.sets.map(set => set.maxReps), + restPeriod: exercise.restPeriod, + }; + }), + }; } // State machine next() { - console.log("XXX Program.next"); - const exercise = this.currentExercise(); - - // All exercises are completed so mark the - // Program as comleted if (this.canComplete()) { this.setCompleted(); this.emit("redraw"); - return; } - exercise.next(this); + // Call current exercise state machine + this.currentExercise().next(this); } } \ No newline at end of file diff --git a/apps/buffgym/buffgym-programs.json b/apps/buffgym/buffgym-programs.json deleted file mode 100644 index 7551c7a47..000000000 --- a/apps/buffgym/buffgym-programs.json +++ /dev/null @@ -1 +0,0 @@ -[{"title":"Program A","exercises":[{"title":"Squats","weight":40,"unit":"Kg","sets":[5,5,5,5,5]},{"title":"Overhead press","weight":20,"unit":"Kg","sets":[5,5,5,5,5]},{"title":"Deadlift","weight":20,"unit":"Kg","sets":[5]},{"title":"Pullups","weight":0,"unit":"Kg","sets":[10,10,10]}]},{"title":"Program B","exercises":[{"title":"Squats","weight":40,"unit":"Kg","sets":[5,5,5,5,5]},{"title":"Bench press","weight":20,"unit":"Kg","sets":[5,5,5,5,5]},{"title":"Row","weight":20,"unit":"Kg","sets":[5,5,5,5,5]},{"title":"Tricep extension","weight":20,"unit":"Kg","sets":[10,10,10]}]}] \ No newline at end of file diff --git a/apps/buffgym/buffgym-programs.json.unminified b/apps/buffgym/buffgym-programs.json.unminified deleted file mode 100644 index cd005eeab..000000000 --- a/apps/buffgym/buffgym-programs.json.unminified +++ /dev/null @@ -1,101 +0,0 @@ -[ - { - "title": "Program A", - "exercises": [ - { - "title": "Squats", - "weight": 40, - "unit": "Kg", - "sets": [ - 5, - 5, - 5, - 5, - 5 - ] - }, - { - "title": "Overhead press", - "weight": 20, - "unit": "Kg", - "sets": [ - 5, - 5, - 5, - 5, - 5 - ] - }, - { - "title": "Deadlift", - "weight": 20, - "unit": "Kg", - "sets": [ - 5 - ] - }, - { - "title": "Pullups", - "weight": 0, - "unit": "Kg", - "sets": [ - 10, - 10, - 10 - ] - } - ] - }, - { - "title": "Program B", - "exercises": [ - { - "title": "Squats", - "weight": 40, - "unit": "Kg", - "sets": [ - 5, - 5, - 5, - 5, - 5 - ] - }, - { - "title": "Bench press", - "weight": 20, - "unit": "Kg", - "sets": [ - 5, - 5, - 5, - 5, - 5 - ] - }, - { - "title": "Row", - "weight": 20, - "unit":"Kg", - "sets": [ - 5, - 5, - 5, - 5, - 5 - ] - - }, - { - "title": "Tricep extension", - "weight": 20, - "unit": "Kg", - "sets": [ - 10, - 10, - 10 - ] - } - ] - } -] \ No newline at end of file diff --git a/apps/buffgym/buffgym-set.js b/apps/buffgym/buffgym-set.js index aed6df260..4bd12d7ec 100644 --- a/apps/buffgym/buffgym-set.js +++ b/apps/buffgym/buffgym-set.js @@ -1,46 +1,28 @@ exports = class Set { constructor(maxReps) { - this._minReps = 0; - this._maxReps = maxReps; - this._reps = 0; - this._completed = false; - } - - get title() { - return this._title; - } - - get weight() { - return this._weight; + this.minReps = 0; + this.maxReps = maxReps; + this.reps = 0; + this.completed = false; } isCompleted() { - return !!this._completed; + return !!this.completed; } setCompleted() { - this._completed = true; - } - - get reps() { - return this._reps; - } - - get maxReps() { - return this._maxReps; + this.completed = true; } incReps() { - if (this._completed) return; - if (this._reps >= this._maxReps) return; - - this._reps++; + if (this.completed) return; + if (this.reps >= this.maxReps) return; + this.reps++; } decReps() { - if (this._completed) return; - if (this._reps <= this._minReps) return; - - this._reps--; + if (this.completed) return; + if (this.reps <= this.minReps) return; + this.reps--; } } \ No newline at end of file diff --git a/apps/buffgym/buffgym.app.js b/apps/buffgym/buffgym.app.js index e1ab3e66b..eeabd5c29 100755 --- a/apps/buffgym/buffgym.app.js +++ b/apps/buffgym/buffgym.app.js @@ -1,177 +1,135 @@ +Bangle.setLCDMode("120x120"); + const W = g.getWidth(); const H = g.getHeight(); const RED = "#d32e29"; const PINK = "#f05a56"; const WHITE = "#ffffff"; -const Set = require("buffgym-set.js"); -const Exercise = require("buffgym-exercise.js"); -const Program = require("buffgym-program.js"); - -function centerStringX(str) { - return (W - g.stringWidth(str)) / 2; -} - -function iconIncrement() { - const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglHA4IpJBYwTHA4RMJCY5oDJo4THKIQKET5IMGCaY7TMaKLTWajbTFJIlICgoVBFYXJYQYSGCggAGCRAVIBgw")); - return img; -} - -function iconDecrement() { - const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglKFJADBCRYABCYQmOFAhNMKIw6FTw4LHCaY7TMaKLTWajbTFJglFCgoVBFYXJYQYSGCggAGCRAVIBgw=")); - return img; -} - -function iconOk() { - const img = require("heatshrink").decompress(atob("ikUxH+AA3XAAgNHCJIVMBYXQ5PC4XJ6AUJCIQQBAAoVCCQwjCAA/JCgglKFJADBCJQxCCYQmMIwZoDJpQMCKIg6KBYwTGFQgeHHYouCCRI7EMYTXFRhILEK5SfFRgYSIborbSbpglFCgoVBFYXJYQYSGCggAGCRAVIBgwA==")); - return img; -} - function drawMenu(params) { + const hs = require("heatshrink"); + const incImg = hs.decompress(atob("gsFwMAkM+oUA")); + const decImg = hs.decompress(atob("gsFwIEBnwCBA")); + const okImg = hs.decompress(atob("gsFwMAhGFo0A")); const DEFAULT_PARAMS = { showBTN1: false, showBTN2: false, showBTN3: false, }; const p = Object.assign({}, DEFAULT_PARAMS, params); - if (p.showBTN1) g.drawImage(iconIncrement(), W - 30, 10); - if (p.showBTN2) g.drawImage(iconOk(), W - 30, 110); - if (p.showBTN3) g.drawImage(iconDecrement(), W - 30, 210); + if (p.showBTN1) g.drawImage(incImg, W - 10, 10); + if (p.showBTN2) g.drawImage(okImg, W - 10, 60); + if (p.showBTN3) g.drawImage(decImg, W - 10, 110); } -function clearScreen() { - g.setColor(RED); - g.fillRect(0,0,W,H); -} - -function drawTitle(exercise) { - const title = exercise.humanTitle; - - g.setFont("Vector",20); - g.setColor(WHITE); - g.drawString(title, centerStringX(title), 5); -} - -function drawReps(exercise) { - const set = exercise.currentSet; +function drawSet(exercise) { + const set = exercise.currentSet(); if (set.isCompleted()) return; + g.clear(); + + // Draw exercise title g.setColor(PINK); - g.fillCircle(W / 2, H / 2, 50); + g.fillRect(15, 0, W - 15, 18); + g.setFontAlign(0, -1); + g.setFont("6x8", 1); g.setColor(WHITE); - g.setFont("Vector", 40); - g.drawString(set.reps, centerStringX(set.reps), (H - 45) / 2); - g.setFont("Vector", 15); - const note = `of ${set.maxReps}`; - g.drawString(note, centerStringX(note), (H / 2) + 25); -} - -function drawSets(exercise) { - const sets = exercise.subTitle; - + g.drawString(exercise.title, W / 2, 5); + g.setFont("6x8", 1); + g.drawString(exercise.weight + " " + exercise.unit, W / 2, 27); + // Draw completed reps counter + g.setFontAlign(0, 0); + g.setColor(PINK); + g.fillRect(15, 42, W - 15, 80); g.setColor(WHITE); - g.setFont("Vector", 15); - g.drawString(sets, centerStringX(sets), H - 25); -} + g.setFont("6x8", 5); + g.drawString(set.reps, (W / 2) + 2, (H / 2) + 1); + g.setFont("6x8", 1); + const note = `Target reps: ${set.maxReps}`; + g.drawString(note, W / 2, H - 24); + // Draw sets monitor + g.drawString(exercise.subTitle, W / 2, H - 12); -function drawSetProgress(exercise) { - drawTitle(exercise); - drawReps(exercise); - drawSets(exercise); drawMenu({showBTN1: true, showBTN2: true, showBTN3: true}); + + g.flip(); } -function drawStartNextExercise() { - const title = "Good work"; - const msg = "No need to rest\nmove straight on\nto the next exercise"; - - g.setColor(WHITE); - g.setFont("Vector", 35); - g.drawString(title, centerStringX(title), 10); - g.setFont("Vector", 15); - g.drawString(msg, 30, 150); - drawMenu({showBTN1: false, showBTN2: true, showBTN3: false}); -} - -function drawProgramCompleted() { +function drawProgDone() { const title1 = "You did"; const title2 = "GREAT!"; const msg = "That's the program\ncompleted. Now eat\nsome food and\nget plenty of rest."; clearWatch(); setWatch(Bangle.showLauncher, BTN2, {repeat: false}); - - g.setColor(WHITE); - g.setFont("Vector", 35); - g.drawString(title1, centerStringX(title1), 10); - g.setFont("Vector", 40); - g.drawString(title2, centerStringX(title2), 50); - g.setFont("Vector", 15); - g.drawString(msg, 30, 150); - drawMenu({showBTN1: false, showBTN2: true, showBTN3: false}); -} - -/* -function drawExerciseCompleted(program) { - const exercise = program.currentExercise(); - const title = exercise.canProgress? - "WELL DONE!" : - "NOT BAD!"; - const msg = exercise.canProgress? - `You weight is automatically increased\nfor ${exercise.title} to ${exercise.weight}${exercise.unit}` : - "It looks like you struggled\non a few sets, your weight will\nstay the same"; - const action = "Move straight on to the next exercise"; - - clearScreen(); - g.setColor(WHITE); - g.setFont("Vector", 20); - g.drawString(title, centerStringX(title), 10); - g.setFont("Vector", 10); - g.drawString(msg, centerStringX(msg), 180); - g.drawString(action, centerStringX(action), 210); drawMenu({showBTN2: true}); - clearWatch(); - setWatch(() => { - init(program); - }, BTN2, {repeat: false}); + g.setFontAlign(0, -1); + g.setColor(WHITE); + g.setFont("6x8", 2); + g.drawString(title1, W / 2, 10); + g.drawString(title2, W / 2, 30); + g.setFont("6x8", 1); + g.drawString(msg, (W / 2) + 3, 70); + g.flip(); +} + +function drawSetComp() { + const title = "Good work"; + const msg = "No need to rest\nmove straight on\nto the next\nexercise.Your\nweight has been\nincreased for\nnext time!"; + + g.clear(); + drawMenu({showBTN2: true}); + + g.setFontAlign(0, -1); + g.setColor(WHITE); + g.setFont("6x8", 2); + g.drawString(title, W / 2, 10); + g.setFont("6x8", 1); + g.drawString(msg, (W / 2) - 2, 45); + + g.flip(); } -*/ function drawRestTimer(program) { const exercise = program.currentExercise(); const motivation = "Take a breather.."; - clearScreen(); - drawMenu({showBTN2: true}); - - g.setColor(PINK); - g.fillCircle(W / 2, H / 2, 50); - g.setColor(WHITE); - g.setFont("Vector", 15); - g.drawString(motivation, centerStringX(motivation), 25); - g.setFont("Vector", 40); - g.drawString(exercise.restPeriod, centerStringX(exercise.restPeriod), (H - 45) / 2); - exercise.decRestPeriod(); if (exercise.restPeriod <= 0) { exercise.resetRestTimer(); - redraw(program); + program.next(); + + return; } + + g.clear(); + drawMenu({showBTN2: true}); + g.setFontAlign(0, -1); + g.setColor(PINK); + g.fillRect(15, 42, W - 15, 80); + g.setColor(WHITE); + g.setFont("6x8", 1); + g.drawString("Have a short\nrest period.", W / 2, 10); + g.setFont("6x8", 5); + g.drawString(exercise.restPeriod, (W / 2) + 2, (H / 2) - 19); + g.flip(); + + exercise.decRestPeriod(); } function redraw(program) { const exercise = program.currentExercise(); - - clearScreen(); + g.clear(); if (program.isCompleted()) { - drawProgramCompleted(program); + saveProg(program); + drawProgDone(program); return; } if (exercise.isRestTimerRunning()) { if (exercise.isLastSet()) { - drawStartNextExercise(program); + drawSetComp(program); } else { drawRestTimer(program); } @@ -179,48 +137,141 @@ function redraw(program) { return; } - drawSetProgress(exercise); + drawSet(exercise); } -function init(program) { - clearWatch(); - program.next(); -} +function drawProgMenu(programs, selProgIdx) { + g.clear(); + g.setFontAlign(0, -1); + g.setColor(WHITE); + g.setFont("6x8", 2); + g.drawString("BuffGym", W / 2, 10); -// Setup training program. This should come from file - -// Squats -function buildPrograms() { - const programsJSON = require("Storage").readJSON("buffgym-programs.json", 1); - - if (!programsJSON) throw "No programs JSON found"; - - const programs = []; - - programsJSON.forEach(programJSON => { - const program = new Program({ - title: programJSON.title, - }); - const exercises = programJSON.exercises.map(exerciseJSON => { - const exercise = new Exercise({ - title: exerciseJSON.title, - weight: exerciseJSON.weight, - unit: exerciseJSON.unit, - }); - exerciseJSON.sets.forEach(setJSON => { - exercise.addSet(new Set(setJSON)); - }); - - return exercise; - }); - program.addExercises(exercises); - programs.push(program); + g.setFont("6x8", 1); + g.setFontAlign(-1, -1); + let selectedProgram = programs[selProgIdx].title; + let yPos = 50; + programs.forEach(program => { + g.setColor("#f05a56"); + g.fillRect(0, yPos, W, yPos + 11); + g.setColor("#ffffff"); + if (selectedProgram === program.title) { + g.drawRect(0, yPos, W - 1, yPos + 11); + } + g.drawString(program.title, 10, yPos + 2); + yPos += 15; }); - - return programs; + g.flip(); } -// For this spike, just run the first program, what will -// really happen is the user picks a program to do from -// some menu on a start page. -init(buildPrograms()[0]); \ No newline at end of file +function setupMenu() { + clearWatch(); + const progs = getProgIndex(); + let selProgIdx = 0; + drawProgMenu(progs, selProgIdx); + + setWatch(()=>{ + selProgIdx--; + if (selProgIdx< 0) selProgIdx = 0; + drawProgMenu(progs, selProgIdx); + }, BTN1, {repeat: true}); + + setWatch(()=>{ + const prog = buildProg(progs[selProgIdx].file); + prog.next(); + }, BTN2, {repeat: false}); + + setWatch(()=>{ + selProgIdx++; + if (selProgIdx > progs.length - 1) selProgIdx = progs.length - 1; + drawProgMenu(progs, selProgIdx); + }, BTN3, {repeat: true}); +} + +function drawSplash() { + g.reset(); + g.setBgColor(RED); + g.clear(); + g.setColor(WHITE); + g.setFontAlign(0,-1); + g.setFont("6x8", 2); + g.drawString("BuffGym", W / 2, 10); + g.setFont("6x8", 1); + g.drawString("5x5", W / 2, 42); + g.drawString("training app", W / 2, 55); + g.drawRect(19, 38, 100, 99); + const img = require("heatshrink").decompress(atob("lkdxH+AB/I5ASQACwpB5vNFkwpBAIfNFdZZkFYwskFZAsiFZBZiVYawEFf6ETFUwsIFUYmB54ADAwIskFYoRKBoIroB4grV58kkgCDFRotWFZwqHFiwYMFZIsTC5wLDFjGlCoWlkgJDRQIABCRAsLCwodCFAIABCwIOCFQYABr4RCCQIrMC4gqEAAwpFFZosFC5ArHFQ4rFNYQrGEgosMBxIrFLQwrLAB4sFSw4rFFjYrQFi4rNbASeEFjIoJFQYsGMAgAPEQgAIGwosCRoorbA=")); + g.drawImage(img, 40, 70); + g.flip(); + + let flasher = false; + let bgCol, txtCol; + const i = setInterval(() => { + if (flasher) { + bgCol = WHITE; + txtCol = RED; + } else { + bgCol = RED; + txtCol = WHITE; + } + flasher = !flasher; + g.setColor(bgCol); + g.fillRect(0, 108, W, 120); + g.setColor(txtCol); + g.drawString("Press btn to begin", W / 2, 110); + g.flip(); + }, 250); + + setWatch(()=>{ + clearInterval(i); + setupMenu(); + }, BTN1, {repeat: false}); + + setWatch(()=>{ + clearInterval(i); + setupMenu(); + }, BTN2, {repeat: false}); + + setWatch(()=>{ + clearInterval(i); + setupMenu(); + }, BTN3, {repeat: false}); +} + +function getProgIndex() { + const progIdx = require("Storage").readJSON("buffgym-program-index.json"); + return progIdx; +} + +function buildProg(fName) { + const Set = require("buffgym-set.js"); + const Exercise = require("buffgym-exercise.js"); + const Program = require("buffgym-program.js"); + const progJSON = require("Storage").readJSON(fName); + const prog = new Program({ + title: progJSON.title, + }); + const exercises = progJSON.exercises.map(exerciseJSON => { + const exercise = new Exercise({ + title: exerciseJSON.title, + weight: exerciseJSON.weight, + unit: exerciseJSON.unit, + restPeriod: exerciseJSON.restPeriod, + }); + exerciseJSON.sets.forEach(setJSON => { + exercise.addSet(new Set(setJSON)); + }); + + return exercise; + }); + prog.addExercises(exercises); + + return prog; +} + +function saveProg(program) { + const fName = getProgIndex().find(prog => prog.title === program.title).file; + require("Storage").writeJSON(fName, program.toJSON()); +} + +drawSplash(); \ No newline at end of file diff --git a/apps/buffgym/buffgym.png b/apps/buffgym/buffgym.png old mode 100644 new mode 100755 index 93a29a4a686f172e5e6dbd2ecb2a1d3edf4751c0..9bde64cc4b4a6cbea919ae5d61fe2f01694da589 GIT binary patch literal 7576 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 Date: Tue, 21 Apr 2020 20:10:56 +0100 Subject: [PATCH 08/14] Add README --- apps/buffgym/README.md | 43 +++++++++++++++++++++++++++++++++ apps/buffgym/buffgym-scrn1.png | Bin 0 -> 4141 bytes apps/buffgym/buffgym-scrn2.png | Bin 0 -> 2801 bytes apps/buffgym/buffgym-scrn3.png | Bin 0 -> 3569 bytes apps/buffgym/buffgym-scrn4.png | Bin 0 -> 3299 bytes apps/buffgym/buffgym-scrn5.png | Bin 0 -> 4500 bytes apps/buffgym/buffgym-scrn6.png | Bin 0 -> 3912 bytes 7 files changed, 43 insertions(+) create mode 100644 apps/buffgym/README.md create mode 100755 apps/buffgym/buffgym-scrn1.png create mode 100755 apps/buffgym/buffgym-scrn2.png create mode 100755 apps/buffgym/buffgym-scrn3.png create mode 100755 apps/buffgym/buffgym-scrn4.png create mode 100755 apps/buffgym/buffgym-scrn5.png create mode 100755 apps/buffgym/buffgym-scrn6.png diff --git a/apps/buffgym/README.md b/apps/buffgym/README.md new file mode 100644 index 000000000..e9e217828 --- /dev/null +++ b/apps/buffgym/README.md @@ -0,0 +1,43 @@ +# BuffGym + +This gym training assistant trains you on the famous [Stronglifts 5x5 workout](https://stronglifts.com/5x5) program. + +## Usage + +When you start the app it will wait on a splash screen until you are ready to start the work out. Press any of the buttons to start + +![](buffgym-scrn1.png) + +You are then presented with the programs menu, use BTN1 to move up the list, and BTN3 to move down the list. Once you have made your selection, press BTN2 to select the program. + +![](buffgym-scrn2.png) + +You will now begin moving through the exercises in the program. You will see the exercise information on the display. +1. At the top is the exercise name, e.g 'Squats' +2. Next is the weight you must train +3. In the center is where you record the number of *reps* you completed (more on that shortly) +4. Below the *reps* value, is the target reps you must try to reach. +5. Below the target reps is the current set you are training, out of the total sets for the exercise. +6. The *reps* value is used to store what you achieved for the current set, you enter this after you have trained on your current set. To alter this value, use BTN1 to increase the value (it will stop at the maximum required reps) and BTN3 to decreas the value to a minimum of 0 (this is the default value). Pressing BTN2 will confirm your reps + +![](buffgym-scrn3.png) + +You will then be presented with a rest timer screen, it counts down and automatically moves to the next exercise when it reaches 0. You can cancel the timer early if you wish by pressing BTN2. If it is the last set of an exercise, you don't need to rest, so it lets you know you have completed all the sets in the exercise and can start the next exercise. + +![](buffgym-scrn4.png) +![](buffgym-scrn5.png) + +Once all exercises are done, you are presented with a pat-on-the-back screen to tell you how awesome you are. + +![](buffgym-scrn6.png) + +## Features + +* If you successfully complete all reps and sets for an exercise, it will automatically update your weights for next time +* Has a neat rest timer to make sure you are training optimally +* Doesn't require a mobile phone, most 'smart watches' are just a visual presentation of the mobile phone app, this runs purley on the watch. So why not leave your phone and its distractions out of the gym! +* Clear and simple user interface + +## Created by + +[Paul Cockrell](https://github.com/paulcockrell) April 2020. \ No newline at end of file diff --git a/apps/buffgym/buffgym-scrn1.png b/apps/buffgym/buffgym-scrn1.png new file mode 100755 index 0000000000000000000000000000000000000000..07b79386f1503317acee9ff894d8dcf4c0035bc4 GIT binary patch literal 4141 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 literal 0 HcmV?d00001 diff --git a/apps/buffgym/buffgym-scrn2.png b/apps/buffgym/buffgym-scrn2.png new file mode 100755 index 0000000000000000000000000000000000000000..ec70fb79110bb029f286b66194fea8be4fe50e28 GIT binary patch literal 2801 zcmeHJT~t%q6~6c81_G&qM!*3faZNQ;$Kps?APU64A}vE?AV8rImeMlH@MAS3CI33M zqYDUQDJ}(*ahQUVhJb>^0Lf)hfhARHF+@}#%M1uyNPv(Z5<+@yANtg{K6I_l!#U^S z?C(4KoW1wA&(%{YiJN?beE|U2lyvg=r`Wpmeh|E{Ii4|h9RPe)Nym?*bMh7^KED3l zz!vc;_?1rXtTOE$w@lwq zGKKDX&lXDA&&3fCZ7Sa+4ZI@|=0SMw5pGLAEXyFI`s&)}D#jzyc76FHFaLGp$ zBzp328kK@P1H>Uj!ctZA$O|7rTjYNM##{No;WRU|Lp>I|u*1ZR6BHdN$@?({8NTXy zlg~mjB*FtX%CCq38Si+8_vcmz^}<;yqL0PNaStoji_YG9IxR&ea1QmAiIGaV1VJmR zrBma^%)CUUa^Aj*LOl++A+)U#_kw>UPVITS_{hBk^>B!I&c{przYj7z_NIw~a0xue zu(!%p_h;W^R&T5ygKytgXpdYUH?rMn+78XiZ*%FLG99l;Ni1_j{4Cx3o9;eFehV(! zidHixm{xAD)T^mQ$!l@PR`lk|Om65SL>L>%psBr+MQQD1J+TcSE|KBG;{Kq9`F*FN zc%9%JsVQfwSDQWcoZg``hQAX-?i2RCZi*pKhx1gc!weZ$JVl1p*;Nj0(!%*%vz^%!Aa3~-}7^U>M(7v z(iM6lnx=DhTTq*F;9pcWxFk-DcPxDA96YJOWf#-!0cx&Lr*%~uXP05sUbRg?&!-#4 zhz)>pQvv%T7-+~4$#G-+!RP!VoJHYk;TzNXUWrbR&%%80gPiWMQKeHapJwH%+tB^KBMlz(>hx@?mh39_RmaohvYJt2rt7&? zIe@lyRe(uK_PcK9#<0-K1YPv20X$hMbku4#l?EdnnnP1MJF(0Xu`7M*Qn_sVoL8pz z%IvZnb9!s6O^v;@AH9}ZW@EBLL-g9}@<)I&uWebmtE@BV%c8j#54IRy4TTc%a7=%I z;jeU7I0gCOYoJijats8Y0r(h8+9&zxG*RMO6t%{OP&U(i3%M%-Br8QJ6p}v%h!UrQ zVA!<2fwr51+?1Vz8X@TL6Xij1GEEejpo4#khdH&(3(!r`ds3fDU&2zjb_PCxv9G6- zwg>ndQUy*xeL1)qxrr%N)5AP?b6{4W!Hg?S(HAaWX5Zg# zI+LK1p&E5PpbXRuIUwFwRWY34VO)>(ny2FHhZ~;33i?bA0_YI2j@`k($*))LDqj2>rsu%SaG+Tkeg$b4Fn) z^{(r0IvWf3A@aw&C>JgXG0g8nC#fDZhO6z=I13FX;uC5ATPf;8SgIFz1sc#R#g8lN z@SMj5lI^2i_Y=c|je8(yoljMlK2qKn&n$l~9>hDwUl&`=SRR`nHjY`s#ah^rD(}jI z%sUO4Kbks2R=m9f30}JIgp`h(cVbrUl8oh3ASjp}$gyz=geC4mBjYeuEM)a^ojVMp z4|9DM;1}RnS!8-v^*&_vS{+{qf*Ma)cHd<@?C7g6owEn|8(ii)0f)0NT+dpa;NJ$- zeoK=~A#cC)?|-r|pKv?-@Q!nbu0Kl@rS;|LFzvNwLL<&Ir^&C|?fIqd1#~b*r_Ss0 zEF_f7BrcW&AQ$gL>%qxj%1Et~gl{Do6x3f@kIgEx5+}6mc-^lVmV2$4#!`h){!HRv zorvIT0k%w~%XwkK!p)4OWXj&C0W%-^Ry^yqyH{A0J6E`{8gZ;gFs6)`bGI_%>bqyC zNMam_=jg>3M%y8*yVwH+R#J>RP`%PR!|bJ{>*X7|L)T2!k_5X1t9kB zL@e3M=Hv_#b0k literal 0 HcmV?d00001 diff --git a/apps/buffgym/buffgym-scrn3.png b/apps/buffgym/buffgym-scrn3.png new file mode 100755 index 0000000000000000000000000000000000000000..0888fc507c229f1eb1b757770531a56ffa63b63e GIT binary patch literal 3569 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 DipYXY literal 0 HcmV?d00001 diff --git a/apps/buffgym/buffgym-scrn4.png b/apps/buffgym/buffgym-scrn4.png new file mode 100755 index 0000000000000000000000000000000000000000..3078d25dce7b2b114c17804571e16c858aaf2d7f GIT binary patch literal 3299 zcmeH~X;4#H7RSTO6M|w4FcAR*aRC((5k#D@7(g}`pxJ^5Bx<)oY(bHL>^zsYU2#Ag zv;`S)5E3BTfRF@8zy)PT36Uibki||Q1n2+)N#@zse3+@44>M&=)lAKY_g=lKbN}bu zbL#)Qb+7n%xvyMqupEIvtn~EQ>kEHnpC6Pu{B{iZH3NarqI>Rj@;?_gk>ix1`$lK) zWkdP-W$r^BYp*O@5xMhNSKbbZ)m2$x%pI!Pz(6wNw)=3?IWw%z^T(4>Q7Ywg6&?YZ zXeK#}OxkV~tdn^mAbk*JegFaKIobzbMgZ1GOi(rg*q(H*9IXZ(SI0lGR0G52W#Kpg z)vQIN905?(vcV1lh{@JZ%1c9&p?l;a3sg)UKaX;pgH zx-?!h0$gpH##>=*RN{hw?yCKLLLI2mcwj)FSXy!oP8B>nw8%cGLM7?_`cl$t)>xb@ zr1dqSLU8^xg+(4Sm>O7HHcJz%#mhn;=`pPtn?L`Z;a2m~k&b|aV^hF|)3Ko~+=RTc z;Ro`m_k!-Sr#-4aO-|9MW3*SaXH+?3j9i0;Qgxk0>G?xC>d@9@4W-*RovsL#=gf`c zxa>^(QZlwyOs)yzGo#r|W*6sNuCRmQO%$_8A%UEmJT7;2{A?OqBVSxe7bGWF7(rWy z+E}&sd1Bl@0JKp(wx*ujwAaEPy8GZUitF>#zc5^Jv~*KN^fif_1%REr6qvgbO7?MeGbio^Q`lkR&Xm>MpOTA7Vg0 zFZ~4p&M{2#1oyBzHorGLv8LzoNfl$bx|m0L0JY_I(A5YWWihq0BmEGphC1L*xoanj84gDQ zx)`FU&ZW>X82!q7ahfxi99MIq&e*_3RF~O9dm`?kWj$F5;rY(Fe0{}> zGsBFO`ayJYZc-Euc(9aAfhT~c4Nx#un+n(qBglG>VV|b3QBTw z7)p3<>(h9b_gs>q(ST}wJN6fjvW}G?bU&xuikTwK0vCnQI7m zCs<}yT;gToP!q&F*e`bFK`Wp)D7@@j4QD&dM|dXaZC%b3D*$ER4Yi{|*o##z*D5gC z*AaksEWR8G!OoSoAKeC;Zh#g4_hc}-^Aj_@-);`q06u^z%K1MUz4ai9)Xjf#&V!qc zU03QtISMytYOvh^Gy_l}+Cv1;6y}evF}lR*pM>lSRn2bm`y>$@5DI5cKD^E|y)~OH zR8Oc>+*4PF4&odWn@>6Ghh}u{jc1)0=V-dEp-ujjW;h zP*twCd&m*oh)}txQ$dd}puy5x8VWDmF<29qDz(R!2a{U%=eTzm0}PFi!H>~_5tKd! zYo_z8kAxmyG(!}T!GcfeJw<<85U{(V^j;QW73fBxxeW-4E-=yMcHYA4u#DdvWAtv< zly(p3d-EHOtu~fYWna8m_?LJouMgtukD!zqAtxeJkxx&mf= z$Les|c25ZTeGm&tDyzo@)mT63pAy2w&@~f7NWYgOLO0`raym?;JbE_$w6uEl_|(g# zEx+x49#%h&{2-00tnS~b#dH%Lx!8i3Bic4)H3=0HKaqf03nFEE8==Lao%XVSd0Z-I zL>|8CKN7bFJ3rAdH}^wIigLZptL;zwklT(#nMco69u7lR9NPleZFbz?f?J|?23q-X zV|-bU7X04p3qr6}iDSv-@MazCX+C`WIreS5S(bllWUH$YFrXD;@fb~C8y=Revv(vz z+;*LH)kSpGN6aP^7Abwt7l!yMgh@uIBoNEZocHV$_ADj{q%B&}e4?Vp-6?I|A{4H;f1YvGMVi}Mg~3So(G&#HW_pibrn}>1h@G89CPi_>{uy7st0LT*=&_Ufis<^!u7w) zF1k{{$d;>#d3^f{&wp7?4;zFte}`W(&NS$R4Nb63d41$7nE2nJMaCM`Mil<^d4kP4 z3XCz+TQ7)sCj_M1S){3^s|JH+qdK4opzh~+9YkfLV4`f?f$@M31CSQs%-f5A8Fd9B zo<{(i3jK5eQ=k|1{gA5`_{?QdiF#eu|B09E2|~2v1k9 Ky`|2S^M3`WxUwt& literal 0 HcmV?d00001 diff --git a/apps/buffgym/buffgym-scrn5.png b/apps/buffgym/buffgym-scrn5.png new file mode 100755 index 0000000000000000000000000000000000000000..b34a5b124aba6acacdcd671c2401563958faa509 GIT binary patch literal 4500 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 HT+Y7%cOZEH literal 0 HcmV?d00001 From a49397ed3dac75b38e3fd3537fe080d2a661a5a4 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Tue, 21 Apr 2020 20:11:32 +0100 Subject: [PATCH 09/14] Disable emulator --- apps.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps.json b/apps.json index 4eeee4e32..139192339 100644 --- a/apps.json +++ b/apps.json @@ -1302,6 +1302,7 @@ "description": "BuffGym is the famous 5x5 workout program for the BangleJS", "tags": "tool,outdoors", "type": "app", + "allow_emulator": false, "storage": [ {"name":"buffgym"}, {"name":"buffgym.app.js", "url": "buffgym.app.js"}, From b5fd14b4fb39e313544758f35950d9068f738fe0 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Tue, 21 Apr 2020 20:45:02 +0100 Subject: [PATCH 10/14] Add training programs to apps file --- apps.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps.json b/apps.json index 139192339..4b51a4689 100644 --- a/apps.json +++ b/apps.json @@ -1309,7 +1309,9 @@ {"name":"buffgym-set.js","url":"buffgym-set.js"}, {"name":"buffgym-exercise.js","url":"buffgym-exercise.js"}, {"name":"buffgym-program.js","url":"buffgym-program.js"}, - {"name":"buffgym-programs.json","url":"buffgym-programs.json"}, + {"name":"buffgym-program-a.json","url":"buffgym-program-a.json"}, + {"name":"buffgym-program-b.json","url":"buffgym-program-b.json"}, + {"name":"buffgym-program-index.json","url":"buffgym-program-index.json"}, {"name":"buffgym.img","url":"buffgym-icon.js","evaluate":true} ] } From 7e305033814aa2946c68686d82e94f85e70cbf6f Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Tue, 21 Apr 2020 20:46:30 +0100 Subject: [PATCH 11/14] Add readme to apps.json --- apps.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps.json b/apps.json index 4b51a4689..d800050ee 100644 --- a/apps.json +++ b/apps.json @@ -1303,6 +1303,7 @@ "tags": "tool,outdoors", "type": "app", "allow_emulator": false, + "readme": "README.md", "storage": [ {"name":"buffgym"}, {"name":"buffgym.app.js", "url": "buffgym.app.js"}, From 14e209b020c0eca0e54fc591c378c8a52f1fab4b Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Tue, 21 Apr 2020 20:47:45 +0100 Subject: [PATCH 12/14] Add tags to buffgym app in apps.json --- apps.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps.json b/apps.json index d800050ee..96f758c07 100644 --- a/apps.json +++ b/apps.json @@ -1300,7 +1300,7 @@ "icon": "buffgym.png", "version":"0.01", "description": "BuffGym is the famous 5x5 workout program for the BangleJS", - "tags": "tool,outdoors", + "tags": "tools,outdoors,gym,exercise", "type": "app", "allow_emulator": false, "readme": "README.md", From bb9747da0924adbef19929b29c833bdc707fd70d Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Tue, 21 Apr 2020 20:51:15 +0100 Subject: [PATCH 13/14] Add app comments --- apps/buffgym/buffgym.app.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/buffgym/buffgym.app.js b/apps/buffgym/buffgym.app.js index eeabd5c29..4dc6ffd5a 100755 --- a/apps/buffgym/buffgym.app.js +++ b/apps/buffgym/buffgym.app.js @@ -1,3 +1,14 @@ +/** + * BangleJS Stronglifts 5x5 training aid + * + * Original Author: Paul Cockrell https://github.com/paulcockrell + * Created: April 2020 + * + * Inspired by: + * - Stronglifts 5x5 training program https://stronglifts.com/5x5/ + * - Stronglifts smart watch app + */ + Bangle.setLCDMode("120x120"); const W = g.getWidth(); From 94bb31889cba0b31be983a6dcfda7d506c367fa0 Mon Sep 17 00:00:00 2001 From: Paul Cockrell Date: Tue, 21 Apr 2020 21:14:18 +0100 Subject: [PATCH 14/14] Update apps.json --- apps.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps.json b/apps.json index 96f758c07..65f0090b3 100644 --- a/apps.json +++ b/apps.json @@ -1300,7 +1300,7 @@ "icon": "buffgym.png", "version":"0.01", "description": "BuffGym is the famous 5x5 workout program for the BangleJS", - "tags": "tools,outdoors,gym,exercise", + "tags": "tool,outdoors,gym,exercise", "type": "app", "allow_emulator": false, "readme": "README.md",