Skip to content

Commit 018aa2f

Browse files
author
thyttan
committed
Merge remote-tracking branch 'Koell/jwalk' into app-loader
2 parents 5f18505 + 7cc1d11 commit 018aa2f

File tree

5 files changed

+103
-81
lines changed

5 files changed

+103
-81
lines changed

apps/hrmmar/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Measurements from the build in PPG-Sensor (Photoplethysmograph) is sensitive to motion and can be corrupted with Motion Artifacts (MA). This module allows to remove these.
44

5+
**WARNING:** On Bangle.js 2 this has been found to make heart rate readings [substantially less accurate in some cases](https://github.com/orgs/espruino/discussions/7738#discussioncomment-13594093) (the HRM already has built in motion artefact removal).
6+
57
## Settings
68

79
* **MA removal**

apps/hrmmar/metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"shortName":"HRM MA removal",
55
"icon": "app.png",
66
"version":"0.02",
7-
"description": "Removes Motion Artifacts in Bangle.js's heart rate sensor data.",
7+
"description": "Removes Motion Artifacts in Bangle.js's heart rate sensor data. **WARNING:** On Bangle.js 2 this has been found to make heart rate readings substantially less accurate in some cases.",
88
"type": "bootloader",
99
"tags": "health",
1010
"supports": ["BANGLEJS","BANGLEJS2"],

apps/jwalk/app.js

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,30 @@ const FILE = "jwalk.json";
22
const DEFAULTS = {
33
totalDuration: 30,
44
intervalDuration: 3,
5-
startMode: 0
5+
startMode: 0,
6+
modeBuzzerDuration: 1000,
7+
finishBuzzerDuration: 1500,
68
};
79

8-
const PAUSE_BEEP_DURATION = 1000;
9-
const SECOND_BEEP_DURATION = 500;
10-
const MESSAGE_DISPLAY_DURATION = 1500;
11-
const FONT_SIZE_TIME = 40;
12-
const FONT_SIZE_BACKGROUND = 2;
13-
const FONT_SIZE_PAUSE = 15;
14-
const CIRCLE_RADIUS = 5;
15-
const TRIANGLE_OFFSET = 6;
10+
let settings = require("Storage").readJSON(FILE, 1) || DEFAULTS;
1611

17-
// Load settings
18-
var settings = require("Storage").readJSON(FILE, 1) || DEFAULTS;
19-
20-
// App state
21-
var state = {
12+
let state = {
2213
remainingTotal: settings.totalDuration * 60,
2314
intervalDuration: settings.intervalDuration * 60,
2415
remainingInterval: 0,
2516
intervalEnd: 0,
2617
paused: false,
27-
currentMode: settings.startMode === 1 ? "Intense" : "Relax"
18+
currentMode: settings.startMode === 1 ? "Intense" : "Relax",
19+
finished: false,
20+
forceDraw: false,
2821
};
2922

30-
var drawTimerInterval;
31-
var cachedLeftTime = "";
23+
let drawTimerInterval;
24+
let cachedLeftTime = "";
3225

3326
function formatTime(seconds) {
34-
var mins = Math.floor(seconds / 60);
35-
var secs = (seconds % 60).toString().padStart(2, '0');
27+
let mins = Math.floor(seconds / 60);
28+
let secs = (seconds % 60).toString().padStart(2, '0');
3629
return `${mins}:${secs}`;
3730
}
3831

@@ -41,97 +34,118 @@ function updateCachedLeftTime() {
4134
}
4235

4336
function getTimeStr() {
44-
var d = new Date();
37+
let d = new Date();
4538
return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
4639
}
4740

48-
4941
function drawUI() {
50-
var y = Bangle.appRect.y + 8;
42+
let y = Bangle.appRect.y + 8;
5143
g.reset().setBgColor(g.theme.bg).clearRect(Bangle.appRect);
5244

53-
// Background
54-
g.setColor(g.theme.bg);
55-
g.fillRect(0, y + 25, g.getWidth(), g.getHeight());
56-
5745
g.setColor(g.theme.fg);
5846

59-
var displayInterval = state.paused
47+
let displayInterval = state.paused
6048
? state.remainingInterval
6149
: Math.max(0, Math.floor((state.intervalEnd - Date.now()) / 1000));
6250

63-
g.setFont("Vector", FONT_SIZE_TIME);
51+
g.setFont("Vector", 40);
6452
g.setFontAlign(0, 0);
6553
g.drawString(formatTime(displayInterval), g.getWidth() / 2, y + 70);
6654

67-
var cy = y + 100;
55+
let cy = y + 100;
6856
if (state.paused) {
69-
g.setFont("Vector", FONT_SIZE_PAUSE);
57+
g.setFont("Vector", 15);
7058
g.drawString("PAUSED", g.getWidth() / 2, cy);
7159
} else {
72-
var cx = g.getWidth() / 2;
73-
// Use accent color for mode indicators for nice contrast
60+
let cx = g.getWidth() / 2;
7461
g.setColor(g.theme.accent || g.theme.fg2 || g.theme.fg);
75-
7662
if (state.currentMode === "Relax") {
77-
g.fillCircle(cx, cy, CIRCLE_RADIUS);
63+
g.fillCircle(cx, cy, 5);
7864
} else {
7965
g.fillPoly([
80-
cx, cy - TRIANGLE_OFFSET,
81-
cx - TRIANGLE_OFFSET, cy + TRIANGLE_OFFSET,
82-
cx + TRIANGLE_OFFSET, cy + TRIANGLE_OFFSET
66+
cx, cy - 6,
67+
cx - 6, cy + 6,
68+
cx + 6, cy + 6
8369
]);
8470
}
85-
86-
// Reset to normal fg for text below
8771
g.setColor(g.theme.fg);
8872
}
8973

90-
g.setFont("6x8", FONT_SIZE_BACKGROUND);
74+
g.setFont("6x8", 2);
9175
g.setFontAlign(0, -1);
9276
g.drawString(state.currentMode, g.getWidth() / 2, y + 15);
93-
9477
g.drawString(cachedLeftTime, g.getWidth() / 2, cy + 15);
95-
78+
9679
g.setFontAlign(1, 0);
9780
g.drawString(getTimeStr(), g.getWidth() - 4, y);
98-
99-
100-
10181
g.flip();
10282
}
10383

10484
function toggleMode() {
10585
state.currentMode = state.currentMode === "Relax" ? "Intense" : "Relax";
106-
Bangle.buzz();
86+
Bangle.buzz(settings.modeBuzzerDuration);
87+
state.forceDraw = true;
88+
}
89+
90+
function finishWorkout() {
91+
clearInterval(drawTimerInterval);
92+
Bangle.buzz(settings.finishBuzzerDuration);
93+
state.finished = true;
94+
95+
setTimeout(() => {
96+
g.clear();
97+
g.setFont("Vector", 30);
98+
g.setFontAlign(0, 0);
99+
g.drawString("Well done!", g.getWidth() / 2, g.getHeight() / 2);
100+
g.flip();
101+
102+
const exitHandler = () => {
103+
Bangle.removeListener("touch", exitHandler);
104+
Bangle.removeListener("btn1", exitHandler);
105+
load(); // Exit app
106+
};
107+
108+
Bangle.on("touch", exitHandler);
109+
setWatch(exitHandler, BTN1, { repeat: false });
110+
}, 500);
107111
}
108112

113+
109114
function startNextInterval() {
110115
if (state.remainingTotal <= 0) {
111-
clearInterval(drawTimerInterval);
112-
Bangle.buzz(PAUSE_BEEP_DURATION);
113-
setTimeout(() => Bangle.buzz(SECOND_BEEP_DURATION), MESSAGE_DISPLAY_DURATION);
114-
E.showMessage("Done!");
116+
finishWorkout();
115117
return;
116118
}
117119

118120
state.remainingInterval = Math.min(state.intervalDuration, state.remainingTotal);
119121
state.remainingTotal -= state.remainingInterval;
120122
updateCachedLeftTime();
121123
state.intervalEnd = Date.now() + state.remainingInterval * 1000;
124+
125+
state.forceDraw = true;
122126
}
123127

124128
function tick() {
129+
if (state.finished) return;
130+
125131
if (!state.paused) {
126132
if ((state.intervalEnd - Date.now()) / 1000 <= 0) {
127133
toggleMode();
128134
startNextInterval();
135+
return;
129136
}
130137
}
131-
drawUI();
138+
139+
if (!Bangle.isLocked() || state.forceDraw) {
140+
drawUI();
141+
state.forceDraw = false;
142+
}
132143
}
133144

145+
134146
function togglePause() {
147+
if (state.finished) return;
148+
135149
if (!state.paused) {
136150
state.remainingInterval = Math.max(0, Math.floor((state.intervalEnd - Date.now()) / 1000));
137151
state.paused = true;
@@ -142,12 +156,12 @@ function togglePause() {
142156
drawUI();
143157
}
144158

145-
// Button / touch handlers
159+
// Setup
146160
Bangle.on("touch", togglePause);
147-
148161
Bangle.loadWidgets();
149162
Bangle.drawWidgets();
150163

164+
updateCachedLeftTime();
151165
startNextInterval();
152166
drawUI();
153167
drawTimerInterval = setInterval(tick, 1000);

apps/jwalk/settings.js

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
totalDuration: 30,
55
intervalDuration: 3,
66
startMode: 0,
7+
modeBuzzerDuration: 1000,
8+
finishBuzzerDuration: 1500,
79
};
810

911
let settings = require("Storage").readJSON(FILE, 1) || DEFAULTS;
@@ -13,41 +15,36 @@
1315
}
1416

1517
function showSettingsMenu() {
16-
const menu = {
18+
E.showMenu({
1719
'': { title: 'Japanese Walking' },
1820
'< Back': back,
1921
'Total Time (min)': {
2022
value: settings.totalDuration,
21-
min: 10,
22-
max: 60,
23-
step: 1,
24-
onchange: v => {
25-
settings.totalDuration = v;
26-
saveSettings();
27-
}
23+
min: 10, max: 60, step: 1,
24+
onchange: v => { settings.totalDuration = v; saveSettings(); }
2825
},
2926
'Interval (min)': {
3027
value: settings.intervalDuration,
31-
min: 1,
32-
max: 10,
33-
step: 1,
34-
onchange: v => {
35-
settings.intervalDuration = v;
36-
saveSettings();
37-
}
28+
min: 1, max: 10, step: 1,
29+
onchange: v => { settings.intervalDuration = v; saveSettings(); }
3830
},
3931
'Start Mode': {
4032
value: settings.startMode,
41-
min: 0,
42-
max: 1,
33+
min: 0, max: 1,
4334
format: v => v ? "Intense" : "Relax",
44-
onchange: v => {
45-
settings.startMode = v;
46-
saveSettings();
47-
}
48-
}
49-
};
50-
E.showMenu(menu);
35+
onchange: v => { settings.startMode = v; saveSettings(); }
36+
},
37+
'Mode Buzz (ms)': {
38+
value: settings.modeBuzzerDuration,
39+
min: 0, max: 2000, step: 50,
40+
onchange: v => { settings.modeBuzzerDuration = v; saveSettings(); }
41+
},
42+
'Finish Buzz (ms)': {
43+
value: settings.finishBuzzerDuration,
44+
min: 0, max: 5000, step: 100,
45+
onchange: v => { settings.finishBuzzerDuration = v; saveSettings(); }
46+
},
47+
});
5148
}
5249

5350
showSettingsMenu();

apps/setting/README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,20 @@ This is Bangle.js's main settings menu:
88
* **Alerts** - Set how Bangle.js alerts you (including Quiet mode)
99
* **Utils** - Utilities, including resetting settings
1010

11+
**New Users:** these are some settings you'll probably want to change right away:
12+
13+
* **Calibrate LCD** Make sure that the LCD touchscreen responds to touches where you expect them to
14+
* **Locale** Set whether you want 12 hour time, and what day of the week the week starts on.
15+
1116
See below for options under each heading:
17+
18+
## Apps - App-specific settings
19+
20+
This is where you adjust settings for an individual app. (eg. Health app: Adjust how often heart rate tracking should fire.)
1221

1322
## System - System settings
1423

15-
* **Theme** Adjust the colour scheme
24+
* **Theme** Adjust the colour scheme. Choose between light mode, dark mode, or a custom theme. To adjust themes in more detail you can also use the [Theme Switcher App](https://banglejs.com/apps/?id=themesetter)
1625
* **LCD** Configure settings about the screen. How long it stays on, how bright it is, and when it turns on - see below.
1726
* **Locale** set time zone, the time format (12/24h, for supported clocks) and the first day of the week
1827
* **Clock** if you have more than one clock face, select the default one
@@ -44,9 +53,9 @@ See below for options under each heading:
4453
* **Rotation** allows you to rotate (or mirror) what's displayed on the screen, eg. for left-handed wearers (needs 2v16 or 2v15 cutting edge firmware to work reliably)
4554
* **Wake on X** should the given activity wake up the Bangle.js LCD?
4655
* On Bangle.js 2 when locked the touchscreen is turned off to save power. Because of this,
47-
`Wake on Touch` actually uses the accelerometer, and you need to actually tap the display to wake Bangle.js (we recently renamed the menu item to `Wake on Tap`).
56+
`Wake on Tap` actually uses the accelerometer, and you need to actually tap the display to wake Bangle.js.
4857
* **Twist X** these options adjust the sensitivity of `Wake on Twist` to ensure Bangle.js wakes up with just the right amount of wrist movement.
49-
* **Calibrate** on Bangle.js 2, pop up a screen allowing you to calibrate the touchscreen (calibration only works on 2v16 or 2v15 cutting edge builds)
58+
* **Calibrate** on Bangle.js 2, pop up a screen allowing you to calibrate the touchscreen, ensuring your touches are mapped to the right place on the screen. (Highly reccomended for new users!)
5059

5160
## Locale
5261

0 commit comments

Comments
 (0)