health: calculate distance from steps

- multiplies step counts with stride length
- distance menu under step counting is only available if stride length is
set
master
Erik Andresen 2023-12-03 20:45:28 +01:00
parent 4f38e475a8
commit dbf8995b14
4 changed files with 118 additions and 52 deletions

View File

@ -29,3 +29,4 @@
0.26: Implement API for activity fetching 0.26: Implement API for activity fetching
0.27: Fix typo in daily summary graph code causing graph not to load 0.27: Fix typo in daily summary graph code causing graph not to load
Fix daily summaries for 31st of the month Fix daily summaries for 31st of the month
0.28: Calculate distance from steps if new stride length setting is set

View File

@ -1,3 +1,5 @@
let settings;
function menuMain() { function menuMain() {
E.showMenu({ E.showMenu({
"": { title: /*LANG*/"Health Tracking" }, "": { title: /*LANG*/"Health Tracking" },
@ -5,16 +7,30 @@ function menuMain() {
/*LANG*/"Step Counting": () => menuStepCount(), /*LANG*/"Step Counting": () => menuStepCount(),
/*LANG*/"Movement": () => menuMovement(), /*LANG*/"Movement": () => menuMovement(),
/*LANG*/"Heart Rate": () => menuHRM(), /*LANG*/"Heart Rate": () => menuHRM(),
/*LANG*/"Settings": () => eval(require("Storage").read("health.settings.js"))(()=>menuMain()) /*LANG*/"Settings": () => eval(require("Storage").read("health.settings.js"))(()=>{loadSettings();menuMain();})
}); });
} }
function menuStepCount() { function menuStepCount() {
E.showMenu({ const menu = {
"": { title:/*LANG*/"Steps" }, "": { title:/*LANG*/"Steps" },
/*LANG*/"< Back": () => menuMain(), /*LANG*/"< Back": () => menuMain(),
/*LANG*/"per hour": () => stepsPerHour(), /*LANG*/"per hour": () => stepsPerHour(menuStepCount),
/*LANG*/"per day": () => stepsPerDay() /*LANG*/"per day": () => stepsPerDay(menuStepCount)
};
if (settings.strideLength) {
menu[/*LANG*/"distance"] = () => menuDistance();
}
E.showMenu(menu);
}
function menuDistance() {
E.showMenu({
"": { title:/*LANG*/"Distance" },
/*LANG*/"< Back": () => menuStepCount(),
/*LANG*/"per hour": () => stepsPerHour(menuDistance, settings.strideLength),
/*LANG*/"per day": () => stepsPerDay(menuDistance, settings.strideLength)
}); });
} }
@ -36,23 +52,23 @@ function menuHRM() {
}); });
} }
function stepsPerHour() { function stepsPerHour(back, mult) {
E.showMessage(/*LANG*/"Loading..."); E.showMessage(/*LANG*/"Loading...");
current_selection = "stepsPerHour"; current_selection = "stepsPerHour";
var data = new Uint16Array(24); var data = new Uint16Array(24);
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps); require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
setButton(menuStepCount); setButton(back, mult);
barChart(/*LANG*/"HOUR", data); barChart(/*LANG*/"HOUR", data, mult);
} }
function stepsPerDay() { function stepsPerDay(back, mult) {
E.showMessage(/*LANG*/"Loading..."); E.showMessage(/*LANG*/"Loading...");
current_selection = "stepsPerDay"; current_selection = "stepsPerDay";
var data = new Uint16Array(32); var data = new Uint16Array(32);
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
setButton(menuStepCount); setButton(back, mult);
barChart(/*LANG*/"DAY", data); barChart(/*LANG*/"DAY", data, mult);
drawHorizontalLine(settings.stepGoal); drawHorizontalLine(settings.stepGoal * (mult || 1));
} }
function hrmPerHour() { function hrmPerHour() {
@ -139,7 +155,11 @@ function get_data_length(arr) {
return nlen; return nlen;
} }
function barChart(label, dt) { function barChart(label, dt, mult) {
if (mult !== undefined) {
// Calculate distance from steps
dt.forEach((val, i) => dt[i] = val*mult);
}
data_len = get_data_length(dt); data_len = get_data_length(dt);
chart_index = Math.max(data_len - 5, -5); // choose initial index that puts the last day on the end chart_index = Math.max(data_len - 5, -5); // choose initial index that puts the last day on the end
chart_max_datum = max(dt); // find highest bar, for scaling chart_max_datum = max(dt); // find highest bar, for scaling
@ -180,7 +200,7 @@ function drawHorizontalLine(value) {
g.setColor(g.theme.fg).drawLine(0, top ,g.getWidth(), top); g.setColor(g.theme.fg).drawLine(0, top ,g.getWidth(), top);
} }
function setButton(fn) { function setButton(fn, mult) {
Bangle.setUI({mode:"custom", Bangle.setUI({mode:"custom",
back:fn, back:fn,
swipe:(lr,ud) => { swipe:(lr,ud) => {
@ -194,12 +214,16 @@ function setButton(fn) {
} }
drawBarChart(); drawBarChart();
if (current_selection == "stepsPerDay") { if (current_selection == "stepsPerDay") {
drawHorizontalLine(settings.stepGoal); drawHorizontalLine(settings.stepGoal * (mult || 1));
} }
}}); }});
} }
function loadSettings() {
settings = require("Storage").readJSON("health.json",1)||{};
}
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
var settings = require("Storage").readJSON("health.json",1)||{}; loadSettings();
menuMain(); menuMain();

View File

@ -2,7 +2,7 @@
"id": "health", "id": "health",
"name": "Health Tracking", "name": "Health Tracking",
"shortName": "Health", "shortName": "Health",
"version": "0.27", "version": "0.28",
"description": "Logs health data and provides an app to view it", "description": "Logs health data and provides an app to view it",
"icon": "app.png", "icon": "app.png",
"tags": "tool,system,health", "tags": "tool,system,health",

View File

@ -8,44 +8,85 @@
function setSettings() { function setSettings() {
require("Storage").writeJSON("health.json", settings); require("Storage").writeJSON("health.json", settings);
} }
E.showMenu({
"": { title: /*LANG*/"Health Tracking" },
/*LANG*/"< Back": () => back(), function settingsMenu() {
E.showMenu({
"": { title: /*LANG*/"Health Tracking" },
/*LANG*/"HRM Interval": { /*LANG*/"< Back": () => back(),
value: settings.hrm,
min: 0,
max: 3,
format: v => [
/*LANG*/"Off",
/*LANG*/"3 min",
/*LANG*/"10 min",
/*LANG*/"Always"
][v],
onchange: v => {
settings.hrm = v;
setSettings();
}
},
/*LANG*/"Daily Step Goal": { /*LANG*/"HRM Interval": {
value: settings.stepGoal, value: settings.hrm,
min: 0, min: 0,
max: 20000, max: 3,
step: 250, format: v => [
onchange: v => { /*LANG*/"Off",
settings.stepGoal = v; /*LANG*/"3 min",
/*LANG*/"10 min",
/*LANG*/"Always"
][v],
onchange: v => {
settings.hrm = v;
setSettings();
}
},
/*LANG*/"Daily Step Goal": {
value: settings.stepGoal,
min: 0,
max: 20000,
step: 250,
onchange: v => {
settings.stepGoal = v;
setSettings();
}
},
/*LANG*/"Step Goal Notification": {
value: "stepGoalNotification" in settings ? settings.stepGoalNotification : false,
format: () => (settings.stepGoalNotification ? 'Yes' : 'No'),
onchange: () => {
settings.stepGoalNotification = !settings.stepGoalNotification;
setSettings();
}
},
/*LANG*/"Stride length": () => strideLengthMenu()
});
}
function strideLengthMenu() {
const menu = {
"" : { title : /*LANG*/"Stride length" },
"< Back" : () => {
setSettings(); setSettings();
} settingsMenu();
}, },
/*LANG*/"Step Goal Notification": {
value: "stepGoalNotification" in settings ? settings.stepGoalNotification : false, "x 0.01" : {
format: () => (settings.stepGoalNotification ? 'Yes' : 'No'), value : settings.strideLength === undefined ? 0 : settings.strideLength,
onchange: () => { min:0,
settings.stepGoalNotification = !settings.stepGoalNotification; step:0.01,
setSettings(); format: v => require("locale").distance(v, 2),
} onchange : v => {
} settings.strideLength=v;
}); menu["x 0.1"].value = v;
},
},
"x 0.1" : {
value : settings.strideLength === undefined ? 0 : settings.strideLength,
min:0,
step:0.1,
format: v => require("locale").distance(v, 2),
onchange : v => {
settings.strideLength=v;
menu["x 0.01"].value = v;
},
},
};
E.showMenu(menu);
}
settingsMenu();
}) })