22
apps.json
|
@ -1321,6 +1321,28 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "buffgym",
|
||||
"name": "BuffGym",
|
||||
"icon": "buffgym.png",
|
||||
"version":"0.01",
|
||||
"description": "BuffGym is the famous 5x5 workout program for the BangleJS",
|
||||
"tags": "tool,outdoors,gym,exercise",
|
||||
"type": "app",
|
||||
"allow_emulator": false,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"buffgym"},
|
||||
{"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"},
|
||||
{"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}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "banglerun",
|
||||
"name": "BangleRun",
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||

|
||||
|
||||
Once all exercises are done, you are presented with a pat-on-the-back screen to tell you how awesome you are.
|
||||
|
||||

|
||||
|
||||
## 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.
|
|
@ -0,0 +1,144 @@
|
|||
exports = class Exercise {
|
||||
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;
|
||||
this._originalRestPeriod = params.restPeriod;
|
||||
this._weightIncrement = params.weightIncrement || 2.5;
|
||||
}
|
||||
|
||||
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}`;
|
||||
}
|
||||
|
||||
decRestPeriod() {
|
||||
this.restPeriod--;
|
||||
}
|
||||
|
||||
addSet(set) {
|
||||
this.sets.push(set);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
canProgress() {
|
||||
let completedRepsTotalSum = 0;
|
||||
let targetRepsTotalSum = 0;
|
||||
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(program);
|
||||
}, 1000 * this.restPeriod);
|
||||
|
||||
this._restInterval = setInterval(() => {
|
||||
program.emit("redraw");
|
||||
}, 1000 );
|
||||
}
|
||||
|
||||
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: false});
|
||||
}
|
||||
|
||||
next(program) {
|
||||
const STARTED = 1;
|
||||
const RESTING = 2;
|
||||
const COMPLETED = 3;
|
||||
|
||||
switch(this._state) {
|
||||
case null:
|
||||
this._state = STARTED;
|
||||
this.setupStartedButtons(program);
|
||||
break;
|
||||
case STARTED:
|
||||
this._state = RESTING;
|
||||
this.startRestTimer(program);
|
||||
this.setupRestingButtons(program);
|
||||
break;
|
||||
case RESTING:
|
||||
this.resetRestTimer();
|
||||
this.currentSet().setCompleted();
|
||||
|
||||
if (this.canSetCompleted()) {
|
||||
this._state = COMPLETED;
|
||||
this.setCompleted();
|
||||
} else {
|
||||
this._state = null;
|
||||
}
|
||||
// As we are changing state and require it to be reprocessed
|
||||
// invoke the next step of program
|
||||
program.next();
|
||||
break;
|
||||
default:
|
||||
throw "Exercise: Attempting to move to an unknown state";
|
||||
}
|
||||
|
||||
program.emit("redraw");
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+ACPI5AUSADAtB5vNGFQtBAIfNF95hoF4wwoF5AwmF5BhmXYbAEF/6QbF1QwIF04qB54ADAwIwoF4oRKBoIvsB4gvZ58kkgCDFxoxaF5wuHGDQcMF5IwXDZwLDGDmlDIWlkgJDSwIABCRAwPDQohCFgIABDQIOCFwYABr4RCCQIvQDYguEAAwtFF5owJDZAvHFw4vFOYQvKFAowMBxIvFMQwvPAB4wFUQ4vJGDYvUGC4vNdgyuEGDIsNFwYwGNAgAPExAvMGIdfTIovfTpYvrfRCOkZ44ugF44NGF05gUFyQvKGIoueGKIufGJ4uhG5oupGItfr4vvAAgvlGAQvt/wrEF9oEGF841IF9QGHX0oGIAD8kAAYJOFzwEBBQoMFACA="));
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
[
|
||||
{
|
||||
"title": "Program A",
|
||||
"file": "buffgym-program-a.json"
|
||||
},
|
||||
{
|
||||
"title": "Program B",
|
||||
"file": "buffgym-program-b.json"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,56 @@
|
|||
exports = class Program {
|
||||
constructor(params) {
|
||||
this.title = params.title;
|
||||
this.exercises = [];
|
||||
this.completed = false;
|
||||
this.on("redraw", redraw.bind(null, this));
|
||||
}
|
||||
|
||||
addExercises(exercises) {
|
||||
exercises.forEach(exercise => this.exercises.push(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;
|
||||
}
|
||||
|
||||
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() {
|
||||
if (this.canComplete()) {
|
||||
this.setCompleted();
|
||||
this.emit("redraw");
|
||||
return;
|
||||
}
|
||||
|
||||
// Call current exercise state machine
|
||||
this.currentExercise().next(this);
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,28 @@
|
|||
exports = class Set {
|
||||
constructor(maxReps) {
|
||||
this.minReps = 0;
|
||||
this.maxReps = maxReps;
|
||||
this.reps = 0;
|
||||
this.completed = false;
|
||||
}
|
||||
|
||||
isCompleted() {
|
||||
return !!this.completed;
|
||||
}
|
||||
|
||||
setCompleted() {
|
||||
this.completed = true;
|
||||
}
|
||||
|
||||
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--;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
/**
|
||||
* 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();
|
||||
const H = g.getHeight();
|
||||
const RED = "#d32e29";
|
||||
const PINK = "#f05a56";
|
||||
const WHITE = "#ffffff";
|
||||
|
||||
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(incImg, W - 10, 10);
|
||||
if (p.showBTN2) g.drawImage(okImg, W - 10, 60);
|
||||
if (p.showBTN3) g.drawImage(decImg, W - 10, 110);
|
||||
}
|
||||
|
||||
function drawSet(exercise) {
|
||||
const set = exercise.currentSet();
|
||||
if (set.isCompleted()) return;
|
||||
|
||||
g.clear();
|
||||
|
||||
// Draw exercise title
|
||||
g.setColor(PINK);
|
||||
g.fillRect(15, 0, W - 15, 18);
|
||||
g.setFontAlign(0, -1);
|
||||
g.setFont("6x8", 1);
|
||||
g.setColor(WHITE);
|
||||
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("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);
|
||||
|
||||
drawMenu({showBTN1: true, showBTN2: true, showBTN3: true});
|
||||
|
||||
g.flip();
|
||||
}
|
||||
|
||||
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});
|
||||
drawMenu({showBTN2: true});
|
||||
|
||||
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..";
|
||||
|
||||
if (exercise.restPeriod <= 0) {
|
||||
exercise.resetRestTimer();
|
||||
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();
|
||||
g.clear();
|
||||
|
||||
if (program.isCompleted()) {
|
||||
saveProg(program);
|
||||
drawProgDone(program);
|
||||
return;
|
||||
}
|
||||
|
||||
if (exercise.isRestTimerRunning()) {
|
||||
if (exercise.isLastSet()) {
|
||||
drawSetComp(program);
|
||||
} else {
|
||||
drawRestTimer(program);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
drawSet(exercise);
|
||||
}
|
||||
|
||||
function drawProgMenu(programs, selProgIdx) {
|
||||
g.clear();
|
||||
g.setFontAlign(0, -1);
|
||||
g.setColor(WHITE);
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString("BuffGym", W / 2, 10);
|
||||
|
||||
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;
|
||||
});
|
||||
g.flip();
|
||||
}
|
||||
|
||||
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();
|
After Width: | Height: | Size: 7.4 KiB |