Merge pull request #950 from jg76379/master

Add new Interval Timer App
pull/982/head
Gordon Williams 2021-11-29 10:01:40 +00:00 committed by GitHub
commit c4238ebae7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 357 additions and 0 deletions

View File

@ -4457,6 +4457,21 @@
{"name":"timecal.app.js","url":"timecal.app.js"} {"name":"timecal.app.js","url":"timecal.app.js"}
] ]
}, },
{
"id":"intervalTimer",
"name":"Interval Timer",
"shortName":"Interval Timer",
"icon": "app.png",
"version":"0.01",
"description": "Interval Timer for workouts, HIIT, or whatever else.",
"tags": "timer, interval, hiit, workout",
"readme":"README.md",
"supports":["BANGLEJS2"],
"storage": [
{"name":"intervalTimer.app.js","url":"app.js"},
{"name":"intervalTimer.img","url":"app-icon.js","evaluate":true}
]
},
{ "id": "93dub", { "id": "93dub",
"name": "93 Dub", "name": "93 Dub",
"shortName":"93 Dub", "shortName":"93 Dub",

View File

@ -0,0 +1 @@
0.01: First Release

View File

@ -0,0 +1,34 @@
# Interval Timer
An interval timer for workouts and whatever else!
## Usage
First set the active time (i.e. the number of seconds to perform exercises).
![Set Active Time](images/set-active.png)
Next set the rest time (i.e. number of seconds to rest between exercises).
![Set Rest Time](images/set-rest.png)
Finally choose the number of sets to perform.
![Set Number Sets](images/set-sets.png)
Active time will be shown in red, rest time in green. The watch will buzz whenever active or rest time gets to 0.
![Timer (active)](images/timer1.png)
![Timer (rest)](images/timer2.png)
You can press the physical button during timer countdown to pause the timer.
![Paused](images/pause.png)
View after all sets are completed. Press menu to change settings or restart to start timer again with the same settings.
![Completed view](images/done.png)
## Creator
James Gough

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwg96hWq1WgDCgXWxGZzOICqQABC4QABCyIXFDBsICIeJyfznAFBwAWPC4Of///mYYMCwgXBl4XB/4xCFxwABn4XCDAQwICw2ICwf/+YwJxGDHoQXHGARGIn/4C5QwBJAwQDC5QLCIw6GEC5BIGIwQLBJAgXGJAwXEJAgXPHgoXIEYIXFLwRIFC484C4h2DJAoIFPA+Ix4MGAAJoDHYgXKf4QXUJAYJGC5p5CF6hIBO44XNABIXGEw4AIU4rXFC5jvFc5AAHxAXGQwwAHQAIXcPCB2FC4RgOB4IXFJBxGHJB5GHJAYwKFwIXIJAIwKFwJGHGAYYICwIuIGAeImYWFmYJBFxIYEwZjC+YtCCxZJDAA4WMDBIWODIwVRAH4AXA=="))

306
apps/intervalTimer/app.js Normal file
View File

@ -0,0 +1,306 @@
/**
Interval Timer
An app for the Bangle.js watch
*/
var Layout = require("Layout");
// Globals
var timerMode; // 'active' || 'rest'
var numSets = 1;
var activeTime = 20;
var restTime = 10;
var counter;
var setsRemaining;
var counterInterval;
var outOfTimeTimeout;
var timerIsPaused;
var timerLayout;
/** Called to initialize the timer layout */
function initTimerLayout() {
timerLayout = new Layout( {
type:"v", c: [
{type:"txt", font:"40%", pad: 10, label:"00:00", id:"time" },
{type:"txt", font:"6x8:2", label:"0", id:"set" }
]
}, {btns: [
{label: "Stop", cb: l => {
if (timerIsPaused){
timerIsPaused = false;
resumeTimer();
}
else{
timerIsPaused = true;
pauseTimer();
}
}
}
]
});
}
/** Pauses the timer by clearing the counterInterval */
function pauseTimer() {
if (counterInterval){
clearTimeout(counterInterval);
counterInterval = undefined;
}
// update layout to display "Paused"
timerLayout.clear(timerLayout.time);
timerLayout.time.label = "||";
timerLayout.clear(timerLayout.set);
timerLayout.set.label = "Paused";
timerLayout.render();
}
/** Reumes the timer by setting the counterInterval again */
function resumeTimer() {
if (!counterInterval){
counterInterval = setInterval(countDown, 1000);
}
// display the timer values again.
timerLayout.clear(timerLayout.time);
timerLayout.time.label = counter;
timerLayout.clear(timerLayout.set);
timerLayout.set.label = `Sets: ${setsRemaining}`;
timerLayout.render();
}
/** Display 'Done' view, called when all sets are completed */
function outOfTime() {
var stopLayout = new Layout( {
type:"v", c: [
{type:"txt", font:"30%", label:"Done!", id:"time" },
]
}, {btns: [
// menu button allows user to modify times and sets
{label:"Menu", cb: l=> {
if (outOfTimeTimeout){
clearTimeout(outOfTimeTimeout);
outOfTimeTimeout = undefined;
}
//stopLayout.remove();
setup();
}
},
// restart button runs timer again with the same settings
{label:"Restart", cb: l=> {
if (outOfTimeTimeout){
clearTimeout(outOfTimeTimeout);
outOfTimeTimeout = undefined;
}
//stopLayout.remove();
timerMode = 'active';
startTimer();
}
}
]});
if (counterInterval) return;
setsRemaining = numSets;
g.clear();
stopLayout.render();
Bangle.buzz(500);
Bangle.beep(200, 4000)
.then(() => new Promise(resolve => setTimeout(resolve,200)))
.then(() => Bangle.beep(200, 3000));
}
/** Function called by the counterInterval at each second.
Updates the timer display values.
*/
function countDown() {
// Out of time
if (counter<=0) {
if(timerMode === 'active'){
timerMode = 'rest';
startTimer();
return;
}
else{
--setsRemaining;
if (setsRemaining === 0){
clearInterval(counterInterval);
counterInterval = undefined;
//setWatch(startTimer, (process.env.HWVERSION==2) ? BTN1 : BTN2);
outOfTime();
return;
}
timerMode = 'active';
startTimer();
return;
}
}
timerLayout.clear(timerLayout.time);
timerLayout.time.label = counter;
timerLayout.render();
counter--;
}
/** Start the interval timer. */
function startTimer() {
timerIsPaused = false;
g.clear();
if(timerMode === 'active'){
counter = activeTime;
timerLayout.time.col = '#f00';
}
else{
counter = restTime;
timerLayout.time.col = '#0f0';
}
timerLayout.clear(timerLayout.set);
timerLayout.set.label = `Sets: ${setsRemaining}`;
timerLayout.render();
Bangle.buzz();
countDown();
if (!counterInterval){
counterInterval = setInterval(countDown, 1000);
}
}
/** Menu step in which user sets the number of sets to be performed. */
function setNumSets(){
g.clear();
var menuLayout = new Layout( {
type:"v", c: [
{type:"txt", font:"6x8:2", label:"Number Sets", id:"title" },
{type:"txt", font:"30%", pad: 20, label: numSets, id:"value" },
{type:"btn", font:"6x8:2", label:"Back", cb: l => {
setRestTime();
}
}
]
}, {btns: [
{label:"+", cb: l=> {
incrementNumSets();
}},
{label:"Go", cb: l=> {
setsRemaining = numSets;
initTimerLayout();
startTimer();
}},
{label:"-", cb: l=>{
decrementNumSets();
}}
]});
menuLayout.render();
const incrementNumSets = () => {
++numSets;
menuLayout.clear(menuLayout.numSets);
menuLayout.value.label = numSets;
menuLayout.render();
};
const decrementNumSets = () => {
if(numSets === 1){
return;
}
--numSets;
menuLayout.clear(menuLayout.numSets);
menuLayout.value.label = numSets;
menuLayout.render();
};
}
/** Menu step in which user sets the number of seconds of rest time for each set. */
function setRestTime(){
g.clear();
var menuLayout = new Layout( {
type:"v", c: [
{type:"txt", font:"6x8:2", label:"Rest Time", id:"title" },
{type:"txt", font:"30%", pad: 20, label: restTime, id:"value" },
{type:"btn", font:"6x8:2", label:"Back", cb: l => {
setActiveTime();
}
}
]
}, {btns: [
{label:"+", cb: l=> {
incrementRestTime();
}},
{label:"OK", cb: l=>setNumSets()},
{label:"-", cb: l=>{
decrementRestTime();
}}
]});
menuLayout.render();
const incrementRestTime = () => {
restTime += 5;
menuLayout.clear(menuLayout.restTime);
menuLayout.value.label = restTime;
menuLayout.render();
};
const decrementRestTime = () => {
if(restTime === 0){
return;
}
restTime -= 5;
menuLayout.clear(menuLayout.restTime);
menuLayout.value.label = restTime;
menuLayout.render();
};
}
/** Menu step in which user sets the number of seconds of active time for each set. */
function setActiveTime(){
g.clear();
var menuLayout = new Layout( {
type:"v", c: [
{type:"txt", font:"6x8:2", label:"Active Time", id:"title" },
{type:"txt", font:"30%", pad: 20, label: activeTime, id:"value" }
]
}, {btns: [
{font:"20%", label:"+", fillx:1, cb: l=> {
incrementActiveTime();
}},
{label:"OK", cb: l => setRestTime()},
{type:"btn", font:"20%", label:"-", fillx:1, cb: l=> {
decrementActiveTime();
}
}
]});
menuLayout.render();
const incrementActiveTime = () => {
activeTime += 5;
menuLayout.clear(menuLayout.activeTime);
menuLayout.value.label = activeTime;
menuLayout.render();
};
const decrementActiveTime = () => {
if(activeTime === 0){
return;
}
activeTime -= 5;
menuLayout.clear(menuLayout.activeTime);
menuLayout.value.label = activeTime;
menuLayout.render();
};
}
/** Start the setup menu, walks through setting active time, rest time, and number of sets. */
function setup(){
if (timerLayout){
// remove timerLayout, otherwise it's pause button callback will still be registered
timerLayout.remove(timerLayout);
timerLayout = undefined;
}
Bangle.setUI(); // remove all existing input handlers
timerMode = 'active';
setActiveTime();
}
// this keeps the watch LCD lit up
Bangle.setLCDPower(1);
setup();

BIN
apps/intervalTimer/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB