health: Added graphs for Temperature and Battery, also reduced duplication in graph code

master
Gordon Williams 2025-03-14 11:13:45 +00:00
parent f706ba79f1
commit 95d8b54803
3 changed files with 82 additions and 92 deletions

View File

@ -32,4 +32,5 @@
0.28: Calculate distance from steps if myprofile is installed and stride length is set
0.29: Minor code improvements
0.30: Minor code improvements
0.31: Add support for new health format (storing more data)
0.31: Add support for new health format (storing more data)
Added graphs for Temperature and Battery

View File

@ -79,7 +79,8 @@ require("health").readDay(d, cb)
## TODO
* More features in app:
* Viewing stored altitude/battery/temperature/bpm min/max graphs
* Viewing stored altitude/bpm min/max graphs
* Currently we only graph per hour but we have 10 min data - should it be shown?
* Pie chart to show percent of time in each activity
* Calendar view showing steps per day
* Yearly view

View File

@ -8,6 +8,8 @@ function menuMain() {
/*LANG*/"Step Counting": () => menuStepCount(),
/*LANG*/"Movement": () => menuMovement(),
/*LANG*/"Heart Rate": () => menuHRM(),
/*LANG*/"Battery": () => menuBattery(),
/*LANG*/"Temperature": () => menuTemperature(),
/*LANG*/"Settings": () => eval(require("Storage").read("health.settings.js"))(()=>{loadSettings();menuMain();})
});
}
@ -16,8 +18,11 @@ function menuStepCount() {
const menu = {
"": { title:/*LANG*/"Steps" },
/*LANG*/"< Back": () => menuMain(),
/*LANG*/"per hour": () => stepsPerHour(menuStepCount),
/*LANG*/"per day": () => stepsPerDay(menuStepCount)
/*LANG*/"per hour": () => showGraph({id:"stepsPerHour",range:"hour",field:"steps", back:menuStepCount}),
/*LANG*/"per day": () => {
showGraph({id:"stepsPerHour",range:"day",field:"steps", back:menuStepCount})
drawHorizontalLine(settings.stepGoal);
}
};
if (myprofile.strideLength) {
menu[/*LANG*/"distance"] = () => menuDistance();
@ -31,8 +36,11 @@ function menuDistance() {
E.showMenu({
"": { title:/*LANG*/"Distance" },
/*LANG*/"< Back": () => menuStepCount(),
/*LANG*/"per hour": () => stepsPerHour(menuDistance, distMult),
/*LANG*/"per day": () => stepsPerDay(menuDistance, distMult)
/*LANG*/"per hour": () => showGraph({id:"stepsPerHour",range:"hour",field:"steps",mult:distMult, back:menuDistance}),
/*LANG*/"per day": () => {
showGraph({id:"stepsPerDay",range:"day",field:"steps",mult:distMult, back:menuDistance})
drawHorizontalLine(settings.stepGoal * (mult || 1));
}
});
}
@ -40,8 +48,8 @@ function menuMovement() {
E.showMenu({
"": { title:/*LANG*/"Movement" },
/*LANG*/"< Back": () => menuMain(),
/*LANG*/"per hour": () => movementPerHour(),
/*LANG*/"per day": () => movementPerDay(),
/*LANG*/"per hour": () => showGraph({id:"movementPerHour",range:"hour",field:"movement",average:true,back:menuMovement}),
/*LANG*/"per day": () => showGraph({id:"movementPerDay",range:"day",field:"movement",average:true,back:menuMovement}),
});
}
@ -49,96 +57,76 @@ function menuHRM() {
E.showMenu({
"": { title:/*LANG*/"Heart Rate" },
/*LANG*/"< Back": () => menuMain(),
/*LANG*/"per hour": () => hrmPerHour(),
/*LANG*/"per day": () => hrmPerDay(),
/*LANG*/"per hour": () => showGraph({id:"hrmPerHour",range:"hour",field:"bpm",ignoreZero:true, average:true,back:menuHRM}),
/*LANG*/"per day": () => showGraph({id:"hrmPerDay",range:"day",field:"bpm",ignoreZero:true, average:true,back:menuHRM}),
});
}
function stepsPerHour(back, mult) {
E.showMessage(/*LANG*/"Loading...");
current_selection = "stepsPerHour";
var data = new Uint16Array(24);
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
if (mult !== undefined) {
// Calculate distance from steps
data.forEach((d, i) => data[i] = d*mult+0.5);
function menuBattery() {
E.showMenu({
"": { title:/*LANG*/"Battery" },
/*LANG*/"< Back": () => menuMain(),
/*LANG*/"per hour": () => showGraph({id:"batPerHour",range:"hour",field:"battery",average:true,back:menuBattery}),
/*LANG*/"per day": () => showGraph({id:"batPerDay",range:"day",field:"battery",average:true,back:menuBattery}),
});
}
function menuTemperature() {
E.showMenu({
"": { title:/*LANG*/"Temperature" },
/*LANG*/"< Back": () => menuMain(),
/*LANG*/"per hour": () => showGraph({id:"batPerHour",range:"hour",field:"temperature",average:true,back:menuTemperature}),
/*LANG*/"per day": () => showGraph({id:"batPerDay",range:"day",field:"temperature",average:true,back:menuTemperature}),
});
}
/*
Display a graph. Options are:
{
range: "day"/"hour"
id: "stepsPerHour" // id of graph
field: "steps" // field name
mult: 1.0, // optional multiplier
ignoreZero: bool, // if set, ignore record that were 0 in average
average: bool, // if set, average records (ignoring)
back: fn() // callback for back button
}
setButton(back, mult);
barChart(/*LANG*/"HOUR", data, mult);
}
function stepsPerDay(back, mult) {
*/
function showGraph(options) {
E.showMessage(/*LANG*/"Loading...");
current_selection = "stepsPerDay";
var data = new Uint16Array(32);
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
// Include data for today
if (data[(new Date()).getDate()] === 0) {
data[(new Date()).getDate()] = Bangle.getHealthStatus("day").steps;
current_selection = options.id;
var data,cnt,title;
if (options.range=="hour") {
title = /*LANG*/"HOUR";
data = new Uint16Array(24);
cnt = new Uint8Array(24);
require("health").readDay(new Date(), h=>{
data[h.hr]+=h[options.field];
if (!options.ignoreZero || h[options.field]) cnt[h.hr]++;
});
} else if (options.range=="day") {
title = /*LANG*/"DAY";
var data = new Uint16Array(32);
var cnt = new Uint8Array(32);
require("health").readDailySummaries(new Date(), h=>{
data[h.day]+=h[options.field];
if (!options.ignoreZero || h[options.field]) cnt[h.day]++;
});
// Include data for today
var day = (new Date()).getDate();
if (data[day] === 0) {
data[day] = Bangle.getHealthStatus("day")[options.field];
if (!options.ignoreZero || data[day]) cnt[day]++;
}
} else throw new Error("Unknown range");
if (options.average)
data.forEach((d,i)=>data[i] = d/cnt[i]+0.5);
if (options.mult !== undefined) { // Calculate distance from steps
data.forEach((d, i) => data[i] = d*options.mult+0.5);
}
if (mult !== undefined) {
// Calculate distance from steps
data.forEach((d, i) => data[i] = d*mult+0.5);
}
setButton(back, mult);
barChart(/*LANG*/"DAY", data, mult);
drawHorizontalLine(settings.stepGoal * (mult || 1));
}
function hrmPerHour() {
E.showMessage(/*LANG*/"Loading...");
current_selection = "hrmPerHour";
var data = new Uint16Array(24);
var cnt = new Uint8Array(24);
require("health").readDay(new Date(), h=>{
data[h.hr]+=h.bpm;
if (h.bpm) cnt[h.hr]++;
});
data.forEach((d,i)=>data[i] = d/cnt[i]+0.5);
setButton(menuHRM);
barChart(/*LANG*/"HOUR", data);
}
function hrmPerDay() {
E.showMessage(/*LANG*/"Loading...");
current_selection = "hrmPerDay";
var data = new Uint16Array(32);
var cnt = new Uint8Array(32);
require("health").readDailySummaries(new Date(), h=>{
data[h.day]+=h.bpm;
if (h.bpm) cnt[h.day]++;
});
data.forEach((d,i)=>data[i] = d/cnt[i]+0.5);
setButton(menuHRM);
barChart(/*LANG*/"DAY", data);
}
function movementPerHour() {
E.showMessage(/*LANG*/"Loading...");
current_selection = "movementPerHour";
var data = new Uint16Array(24);
var cnt = new Uint8Array(24);
require("health").readDay(new Date(), h=>{
data[h.hr]+=h.movement;
cnt[h.hr]++;
});
data.forEach((d,i)=>data[i] = d/cnt[i]+0.5);
setButton(menuMovement);
barChart(/*LANG*/"HOUR", data);
}
function movementPerDay() {
E.showMessage(/*LANG*/"Loading...");
current_selection = "movementPerDay";
var data = new Uint16Array(32);
var cnt = new Uint8Array(32);
require("health").readDailySummaries(new Date(), h=>{
data[h.day]+=h.movement;
cnt[h.day]++;
});
data.forEach((d,i)=>data[i] = d/cnt[i]+0.5);
setButton(menuMovement);
barChart(/*LANG*/"DAY", data);
setButton(options.back, options.mult);
barChart(title, data, options.mult);
}
// Bar Chart Code