2024-02-21 20:45:50 +00:00
var week = 1 ;
var day = 1 ;
var time ;
var loop ; // To store how many times we will have to do a countdown
2024-02-22 15:39:13 +00:00
var rep ; // The current rep counter
var counter ; // The seconds counter
2024-02-21 20:45:50 +00:00
var currentMode ; // Either "run" or "walk"
var mainInterval ; // Ticks every second, checking if a new countdown is needed
var activityInterval ; // Ticks every second, doing the countdown
2024-02-22 15:39:13 +00:00
var extraInfoWatch ; // Watch for button presses to show additional info
var paused = false ; // Track pause state
var pauseOrResumeWatch ; // Watch for button presses to pause/resume countdown
var defaultFontSize = ( process . env . HWVERSION == 2 ) ? 7 : 8 ; // Default font size, Banglejs 2 has smaller
var activityBgColour ; // Background colour of current activity
2024-02-21 20:45:50 +00:00
function outOfTime ( ) {
2024-02-22 15:39:13 +00:00
buzz ( ) ;
2024-02-21 20:45:50 +00:00
// Once we're done
if ( loop == 0 ) {
2024-02-22 15:39:13 +00:00
clearWatch ( extraInfoWatch ) ; // Don't watch for button presses anymore
if ( pauseOrResumeWatch ) clearWatch ( pauseOrResumeWatch ) ; // Don't watch for button presses anymore
2024-02-21 20:45:50 +00:00
g . setBgColor ( "#75C0E0" ) ; // Blue background for the "Done" text
2024-02-22 15:39:13 +00:00
drawText ( "Done" , defaultFontSize ) ; // Write "Done" to screen
2024-02-21 20:45:50 +00:00
g . reset ( ) ;
setTimeout ( E . showMenu , 5000 , mainmenu ) ; // Show the main menu again after 5secs
clearInterval ( mainInterval ) ; // Stop the main interval from starting a new activity
mainInterval = undefined ;
}
}
2024-02-22 15:39:13 +00:00
// Buzz 3 times on state transitions
function buzz ( ) {
Bangle . buzz ( 500 )
. then ( ( ) => new Promise ( resolve => setTimeout ( resolve , 200 ) ) )
. then ( ( ) => Bangle . buzz ( 500 ) )
. then ( ( ) => new Promise ( resolve => setTimeout ( resolve , 200 ) ) )
. then ( ( ) => Bangle . buzz ( 500 ) ) ;
}
function drawText ( text , size ) {
2024-02-21 20:45:50 +00:00
g . clear ( ) ;
2024-02-21 21:38:48 +00:00
g . setFontAlign ( 0 , 0 ) ; // center font
2024-02-22 15:39:13 +00:00
g . setFont ( "6x8" , size ) ;
g . drawString ( text , g . getWidth ( ) / 2 , g . getHeight ( ) / 2 ) ;
}
function countDown ( ) {
if ( ! paused ) {
var text = "" ;
var size = defaultFontSize ;
if ( time ) {
var w = week - 1 ;
var d = day - 1 ;
var total = ( "walk" in PLAN [ w ] [ d ] ) ? PLAN [ w ] [ d ] . repetition : 1 ;
text += rep + "/" + total + "\n" ; // Show the current/total rep count when time is shown
size -= ( process . env . HWVERSION == 2 ) ? 2 : 1 ; // Use smaller font size to fit everything nicely on the screen
}
text += ( currentMode === "run" ) ? "Run\n" + counter : "Walk\n" + counter ; // Switches output text
if ( time ) text += "\n" + time ;
drawText ( text , size ) ; // draw the current mode and seconds
Bangle . setLCDPower ( 1 ) ; // keep the watch LCD lit up
counter -- ; // Reduce the seconds
// If the current activity is done
if ( counter < 0 ) {
clearInterval ( activityInterval ) ;
activityInterval = undefined ;
outOfTime ( ) ;
return ;
}
2024-02-21 20:45:50 +00:00
}
}
function startTimer ( w , d ) {
// If something is already running, do nothing
2024-02-22 15:39:13 +00:00
if ( activityInterval ) return ;
2024-02-21 20:45:50 +00:00
// Switches between the two modes
if ( ! currentMode || currentMode === "walk" ) {
currentMode = "run" ;
2024-02-21 21:38:48 +00:00
rep ++ ; // Increase the rep counter every time a "run" activity starts
2024-02-21 20:45:50 +00:00
counter = PLAN [ w ] [ d ] . run * 60 ;
2024-02-22 15:39:13 +00:00
activityBgColour = "#ff5733" ; // Red background for running
2024-02-21 20:45:50 +00:00
}
else {
currentMode = "walk" ;
counter = PLAN [ w ] [ d ] . walk * 60 ;
2024-02-22 15:39:13 +00:00
activityBgColour = "#4da80a" ; // Green background for walking
2024-02-21 20:45:50 +00:00
}
2024-02-22 15:39:13 +00:00
g . setBgColor ( activityBgColour ) ;
2024-02-21 20:45:50 +00:00
countDown ( ) ;
if ( ! activityInterval ) {
loop -- ; // Reduce the number of iterations
activityInterval = setInterval ( countDown , 1000 ) ; // Start a new activity
}
}
function showTime ( ) {
if ( time ) return ; // If clock is already shown, don't do anything even if the button was pressed again
// Get the time and format it with a leading 0 if necessary
var d = new Date ( ) ;
var h = d . getHours ( ) ;
var m = d . getMinutes ( ) ;
time = h + ":" + m . toString ( ) . padStart ( 2 , 0 ) ;
setTimeout ( function ( ) { time = undefined ; } , 5000 ) ; // Hide clock after 5secs
}
// Populate the PLAN menu
function populatePlan ( ) {
for ( var i = 0 ; i < PLAN . length ; i ++ ) {
for ( var j = 0 ; j < PLAN [ i ] . length ; j ++ ) {
// Ever line will have the following format:
// w{week}d{day}(r:{run mins}|w:{walk mins}|x{number of reps})
var name = "w" + ( i + 1 ) + "d" + ( j + 1 ) ;
2024-02-22 15:39:13 +00:00
if ( process . env . HWVERSION == 2 ) name += "\n" ; // Print in 2 lines to accomodate the Bangle.js 2 screen
2024-02-21 20:45:50 +00:00
name += "(r:" + PLAN [ i ] [ j ] . run ;
if ( "walk" in PLAN [ i ] [ j ] ) name += "|w:" + PLAN [ i ] [ j ] . walk ;
if ( "repetition" in PLAN [ i ] [ j ] ) name += "|x" + PLAN [ i ] [ j ] . repetition ;
name += ")" ;
// Each menu item will have a function that start the program at the selected day
planmenu [ name ] = getFunc ( i , j ) ;
}
}
}
// Helper function to generate functions for the PLAN menu
function getFunc ( i , j ) {
return function ( ) {
week = i + 1 ;
day = j + 1 ;
startActivity ( ) ;
} ;
}
function startActivity ( ) {
var w = week - 1 ;
var d = day - 1 ;
2024-02-22 15:39:13 +00:00
loop = ( "walk" in PLAN [ w ] [ d ] ) ? PLAN [ w ] [ d ] . repetition * 2 : 1 ;
rep = 0 ;
E . showMenu ( ) ; // Hide the main menu
extraInfoWatch = setWatch ( showTime , ( process . env . HWVERSION == 2 ) ? BTN1 : BTN2 , { repeat : true } ) ; // Show the clock on button press
if ( process . env . HWVERSION == 1 ) pauseOrResumeWatch = setWatch ( pauseOrResumeActivity , BTN1 , { repeat : true } ) ; // Pause or resume on button press (Bangle.js 1 only)
buzz ( ) ;
mainInterval = setInterval ( function ( ) { startTimer ( w , d ) ; } , 1000 ) ; // Check every second if we need to do something
}
// Pause or resume current activity
function pauseOrResumeActivity ( ) {
paused = ! paused ;
buzz ( ) ;
if ( paused ) {
g . setBgColor ( "#fdd835" ) ; // Yellow background for pause screen
drawText ( "Paused" , ( process . env . HWVERSION == 2 ) ? defaultFontSize - 3 : defaultFontSize - 2 ) ; // Although the font size is configured here, this feature does not work on Bangle.js 2 as the only physical button is tied to the extra info screen already
2024-02-21 20:45:50 +00:00
}
else {
2024-02-22 15:39:13 +00:00
g . setBgColor ( activityBgColour ) ;
2024-02-21 20:45:50 +00:00
}
}
const PLAN = [
[
{ "run" : 1 , "walk" : 1.5 , "repetition" : 8 } ,
{ "run" : 1 , "walk" : 1.5 , "repetition" : 8 } ,
{ "run" : 1 , "walk" : 1.5 , "repetition" : 8 } ,
] ,
[
{ "run" : 1.5 , "walk" : 2 , "repetition" : 6 } ,
{ "run" : 1.5 , "walk" : 2 , "repetition" : 6 } ,
{ "run" : 1.5 , "walk" : 2 , "repetition" : 6 } ,
] ,
[
{ "run" : 2 , "walk" : 2 , "repetition" : 5 } ,
{ "run" : 2.5 , "walk" : 2.5 , "repetition" : 4 } ,
{ "run" : 2.5 , "walk" : 2.5 , "repetition" : 4 } ,
] ,
[
{ "run" : 3 , "walk" : 2 , "repetition" : 5 } ,
{ "run" : 3 , "walk" : 2 , "repetition" : 5 } ,
{ "run" : 4 , "walk" : 2.5 , "repetition" : 3 } ,
] ,
[
{ "run" : 5 , "walk" : 2 , "repetition" : 3 } ,
{ "run" : 8 , "walk" : 5 , "repetition" : 2 } ,
{ "run" : 20 } ,
] ,
[
{ "run" : 6 , "walk" : 3 , "repetition" : 2 } ,
{ "run" : 10 , "walk" : 3 , "repetition" : 2 } ,
{ "run" : 25 } ,
] ,
[
{ "run" : 25 } ,
{ "run" : 25 } ,
{ "run" : 25 } ,
] ,
[
{ "run" : 30 } ,
{ "run" : 30 } ,
{ "run" : 30 } ,
] ,
] ;
// Main menu
var mainmenu = {
"" : {
"title" : "-- C25K --"
} ,
"Week" : {
value : week ,
min : 1 , max : PLAN . length , step : 1 ,
onchange : v => { week = v ; }
} ,
"Day" : {
value : day ,
min : 1 , max : 3 , step : 1 ,
onchange : v => { day = v ; }
} ,
"View plan" : function ( ) { E . showMenu ( planmenu ) ; } ,
"Start" : function ( ) { startActivity ( ) ; } ,
"Exit" : function ( ) { load ( ) ; } ,
} ;
// Plan view
var planmenu = {
"" : {
title : "-- Plan --" ,
back : function ( ) { E . showMenu ( mainmenu ) ; } ,
}
} ;
// Populate the PLAN menu view
populatePlan ( ) ;
// Actually display the menu
E . showMenu ( mainmenu ) ;