1
+ var ExStats = require ( "exstats" ) ;
1
2
var B2 = process . env . HWVERSION == 2 ;
2
3
var Layout = require ( "Layout" ) ;
3
4
var locale = require ( "locale" ) ;
4
5
var fontHeading = "6x8:2" ;
5
6
var fontValue = B2 ? "6x15:2" : "6x8:3" ;
6
7
var headingCol = "#888" ;
7
- var running = false ;
8
8
var fixCount = 0 ;
9
- var startTime ;
10
- var startSteps ;
11
- // This & previous GPS readings
12
- var lastGPS , thisGPS ;
13
- var distance = 0 ; ///< distance in meters
14
- var startSteps = Bangle . getStepCount ( ) ; ///< number of steps when we started
15
- var lastStepCount = startSteps ; // last time 'step' was called
16
- var stepHistory = new Uint8Array ( 60 ) ; // steps each second for the last minute (0 = current minute)
17
9
18
10
g . clear ( ) ;
19
11
Bangle . loadWidgets ( ) ;
20
12
Bangle . drawWidgets ( ) ;
21
13
22
14
// ---------------------------
23
-
24
- function formatTime ( ms ) {
25
- let hrs = Math . floor ( ms / 3600000 ) . toString ( ) ;
26
- let mins = ( Math . floor ( ms / 60000 ) % 60 ) . toString ( ) ;
27
- let secs = ( Math . floor ( ms / 1000 ) % 60 ) . toString ( ) ;
28
-
29
- if ( hrs === '0' )
30
- return mins . padStart ( 2 , 0 ) + ":" + secs . padStart ( 2 , 0 ) ;
31
- else
32
- return hrs + ":" + mins . padStart ( 2 , 0 ) + ":" + secs . padStart ( 2 , 0 ) ; // dont pad hours
33
- }
34
-
35
- // Format speed in meters/second
36
- function formatPace ( speed ) {
37
- if ( speed < 0.1667 ) {
38
- return `__:__` ;
39
- }
40
- const pace = Math . round ( 1000 / speed ) ; // seconds for 1km
41
- const min = Math . floor ( pace / 60 ) ; // minutes for 1km
42
- const sec = pace % 60 ;
43
- return ( '0' + min ) . substr ( - 2 ) + `:` + ( '0' + sec ) . substr ( - 2 ) ;
44
- }
45
-
15
+ let settings = Object . assign ( {
16
+ B1 : "dist" ,
17
+ B2 : "time" ,
18
+ B3 : "pacea" ,
19
+ B4 : "bpm" ,
20
+ B5 : "step" ,
21
+ B6 : "caden" ,
22
+ paceLength : 1000
23
+ } , require ( "Storage" ) . readJSON ( "run.json" , 1 ) || { } ) ;
24
+ var statIDs = [ settings . B1 , settings . B2 , settings . B3 , settings . B4 , settings . B5 , settings . B6 ] . filter ( s => s != "" ) ;
25
+ var exs = ExStats . getStats ( statIDs , settings ) ;
46
26
// ---------------------------
47
27
48
- function clearState ( ) {
49
- distance = 0 ;
50
- startSteps = Bangle . getStepCount ( ) ;
51
- stepHistory . fill ( 0 ) ;
52
- layout . dist . label = locale . distance ( distance ) ;
53
- layout . time . label = "00:00" ;
54
- layout . pace . label = formatPace ( 0 ) ;
55
- layout . hrm . label = "--" ;
56
- layout . steps . label = 0 ;
57
- layout . cadence . label = "0" ;
58
- layout . status . bgCol = "#f00" ;
59
- }
60
-
28
+ // Called to start/stop running
61
29
function onStartStop ( ) {
62
- running = ! running ;
30
+ var running = ! exs . state . active ;
63
31
if ( running ) {
64
- clearState ( ) ;
65
- startTime = Date . now ( ) ;
32
+ exs . start ( ) ;
33
+ } else {
34
+ exs . stop ( ) ;
66
35
}
67
36
layout . button . label = running ? "STOP" : "START" ;
68
37
layout . status . label = running ? "RUN" : "STOP" ;
@@ -72,107 +41,44 @@ function onStartStop() {
72
41
layout . render ( ) ;
73
42
}
74
43
44
+ var lc = [ ] ;
45
+ // Load stats in pair by pair
46
+ for ( var i = 0 ; i < statIDs . length ; i += 2 ) {
47
+ var sa = exs . stats [ statIDs [ i + 0 ] ] ;
48
+ var sb = exs . stats [ statIDs [ i + 1 ] ] ;
49
+ lc . push ( { type :"h" , filly :1 , c :[
50
+ { type :"txt" , font :fontHeading , label :sa . title . toUpperCase ( ) , fillx :1 , col :headingCol } ,
51
+ { type :"txt" , font :fontHeading , label :sb . title . toUpperCase ( ) , fillx :1 , col :headingCol }
52
+ ] } , { type :"h" , filly :1 , c :[
53
+ { type :"txt" , font :fontValue , label :sa . getString ( ) , id :sa . id , fillx :1 } ,
54
+ { type :"txt" , font :fontValue , label :sb . getString ( ) , id :sb . id , fillx :1 }
55
+ ] } ) ;
56
+ sa . on ( 'changed' , e => layout [ e . id ] . label = e . getString ( ) ) ;
57
+ sb . on ( 'changed' , e => layout [ e . id ] . label = e . getString ( ) ) ;
58
+ }
59
+ // At the bottom put time/GPS state/etc
60
+ lc . push ( { type :"h" , filly :1 , c :[
61
+ { type :"txt" , font :fontHeading , label :"GPS" , id :"gps" , fillx :1 , bgCol :"#f00" } ,
62
+ { type :"txt" , font :fontHeading , label :"00:00" , id :"clock" , fillx :1 , bgCol :g . theme . fg , col :g . theme . bg } ,
63
+ { type :"txt" , font :fontHeading , label :"STOP" , id :"status" , fillx :1 }
64
+ ] } ) ;
65
+ // Now calculate the layout
75
66
var layout = new Layout ( {
76
- type :"v" , c : [
77
- { type :"h" , filly :1 , c :[
78
- { type :"txt" , font :fontHeading , label :"DIST" , fillx :1 , col :headingCol } ,
79
- { type :"txt" , font :fontHeading , label :"TIME" , fillx :1 , col :headingCol }
80
- ] } , { type :"h" , filly :1 , c :[
81
- { type :"txt" , font :fontValue , label :"0.00" , id :"dist" , fillx :1 } ,
82
- { type :"txt" , font :fontValue , label :"00:00" , id :"time" , fillx :1 }
83
- ] } , { type :"h" , filly :1 , c :[
84
- { type :"txt" , font :fontHeading , label :"PACE" , fillx :1 , col :headingCol } ,
85
- { type :"txt" , font :fontHeading , label :"HEART" , fillx :1 , col :headingCol }
86
- ] } , { type :"h" , filly :1 , c :[
87
- { type :"txt" , font :fontValue , label :`__'__"` , id :"pace" , fillx :1 } ,
88
- { type :"txt" , font :fontValue , label :"--" , id :"hrm" , fillx :1 }
89
- ] } , { type :"h" , filly :1 , c :[
90
- { type :"txt" , font :fontHeading , label :"STEPS" , fillx :1 , col :headingCol } ,
91
- { type :"txt" , font :fontHeading , label :"CADENCE" , fillx :1 , col :headingCol }
92
- ] } , { type :"h" , filly :1 , c :[
93
- { type :"txt" , font :fontValue , label :"0" , id :"steps" , fillx :1 } ,
94
- { type :"txt" , font :fontValue , label :"0" , id :"cadence" , fillx :1 }
95
- ] } , { type :"h" , filly :1 , c :[
96
- { type :"txt" , font :fontHeading , label :"GPS" , id :"gps" , fillx :1 , bgCol :"#f00" } ,
97
- { type :"txt" , font :fontHeading , label :"00:00" , id :"clock" , fillx :1 , bgCol :g . theme . fg , col :g . theme . bg } ,
98
- { type :"txt" , font :fontHeading , label :"STOP" , id :"status" , fillx :1 }
99
- ] } ,
100
-
101
- ]
67
+ type :"v" , c : lc
102
68
} , { lazy :true , btns :[ { label :"START" , cb : onStartStop , id :"button" } ] } ) ;
103
- clearState ( ) ;
69
+ delete lc ;
104
70
layout . render ( ) ;
105
71
106
- function onTimer ( ) {
107
- layout . clock . label = locale . time ( new Date ( ) , 1 ) ;
108
- if ( ! running ) {
109
- layout . render ( ) ;
110
- return ;
111
- }
112
- // called once a second
113
- var duration = Date . now ( ) - startTime ; // in ms
114
- // set cadence based on steps over last minute
115
- var stepsInMinute = E . sum ( stepHistory ) ;
116
- var cadence = 60000 * stepsInMinute / Math . min ( duration , 60000 ) ;
117
- // update layout
118
- layout . time . label = formatTime ( duration ) ;
119
- layout . steps . label = Bangle . getStepCount ( ) - startSteps ;
120
- layout . cadence . label = Math . round ( cadence ) ;
121
- layout . render ( ) ;
122
- // move step history onwards
123
- stepHistory . set ( stepHistory , 1 ) ;
124
- stepHistory [ 0 ] = 0 ;
125
- }
126
-
127
- function radians ( a ) {
128
- return a * Math . PI / 180 ;
129
- }
130
-
131
- // distance between 2 lat and lons, in meters, Mean Earth Radius = 6371km
132
- // https://www.movable-type.co.uk/scripts/latlong.html
133
- function calcDistance ( a , b ) {
134
- var x = radians ( a . lon - b . lon ) * Math . cos ( radians ( ( a . lat + b . lat ) / 2 ) ) ;
135
- var y = radians ( b . lat - a . lat ) ;
136
- return Math . round ( Math . sqrt ( x * x + y * y ) * 6371000 ) ;
137
- }
138
-
72
+ // Handle GPS state change for icon
139
73
Bangle . on ( "GPS" , function ( fix ) {
140
74
layout . gps . bgCol = fix . fix ? "#0f0" : "#f00" ;
141
- if ( ! fix . fix ) { return ; } // only process actual fixes
75
+ if ( ! fix . fix ) return ; // only process actual fixes
142
76
if ( fixCount ++ == 0 ) {
143
77
Bangle . buzz ( ) ; // first fix, does not need to respect quiet mode
144
- lastGPS = fix ; // initialise on first fix
145
- }
146
-
147
- thisGPS = fix ;
148
-
149
- if ( running ) {
150
- var d = calcDistance ( lastGPS , thisGPS ) ;
151
- distance += d ;
152
- layout . dist . label = locale . distance ( distance ) ;
153
- var duration = Date . now ( ) - startTime ; // in ms
154
- var speed = distance * 1000 / duration ; // meters/sec
155
- layout . pace . label = formatPace ( speed ) ;
156
- lastGPS = fix ;
157
- }
158
- } ) ;
159
- Bangle . on ( "HRM" , function ( h ) {
160
- layout . hrm . label = h . bpm ;
161
- } ) ;
162
- Bangle . on ( "step" , function ( steps ) {
163
- if ( running ) {
164
- layout . steps . label = steps - Bangle . getStepCount ( ) ;
165
- stepHistory [ 0 ] += steps - lastStepCount ;
166
78
}
167
- lastStepCount = steps ;
168
79
} ) ;
169
-
170
- let settings = require ( "Storage" ) . readJSON ( 'run.json' , 1 ) || { "use_gps" :true , "use_hrm" :true } ;
171
-
172
- // We always call ourselves once a second, if only to update the time
173
- setInterval ( onTimer , 1000 ) ;
174
-
175
- /* Turn GPS and HRM on right at the start to ensure
176
- we get the highest chance of a lock. */
177
- if ( settings . use_hrm ) Bangle . setHRMPower ( true , "app" ) ;
178
- if ( settings . use_gps ) Bangle . setGPSPower ( true , "app" ) ;
80
+ // We always call ourselves once a second to update
81
+ setInterval ( function ( ) {
82
+ layout . clock . label = locale . time ( new Date ( ) , 1 ) ;
83
+ layout . render ( ) ;
84
+ } , 1000 ) ;
0 commit comments