2021-11-02 16:19:32 +00:00
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
|
|
|
</head>
|
|
|
|
<body>
|
2023-01-03 13:04:07 +00:00
|
|
|
<div id="content"></div>
|
2021-11-02 16:19:32 +00:00
|
|
|
|
|
|
|
<script src="../../core/lib/interface.js"></script>
|
2023-01-03 14:18:45 +00:00
|
|
|
<script type="module" src="chart.min.js"></script>
|
2021-11-02 16:19:32 +00:00
|
|
|
<script>
|
|
|
|
const DB_RECORD_LEN = 4;
|
|
|
|
const DB_RECORDS_PER_HR = 6;
|
|
|
|
const DB_RECORDS_PER_DAY = DB_RECORDS_PER_HR*24 + 1/*summary*/;
|
|
|
|
const DB_RECORDS_PER_MONTH = DB_RECORDS_PER_DAY*31;
|
|
|
|
const DB_HEADER_LEN = 8;
|
|
|
|
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
|
|
|
|
|
2023-01-03 13:04:07 +00:00
|
|
|
var domContent = document.getElementById("content");
|
2021-11-02 16:19:32 +00:00
|
|
|
|
|
|
|
function saveCSV(data, date, title) {
|
|
|
|
// date = "2021-9"/ etc
|
|
|
|
var csv = "Date,Time,Steps,Heartrate,Movement\n";
|
|
|
|
var f = data;
|
|
|
|
|
|
|
|
var idx = DB_HEADER_LEN;
|
|
|
|
for (var day=0;day<31;day++) {
|
|
|
|
for (var hr=0;hr<24;hr++) { // actually 25, see below
|
|
|
|
for (var m=0;m<DB_RECORDS_PER_HR;m++) {
|
|
|
|
var h = f.substr(idx, DB_RECORD_LEN);
|
|
|
|
if (h!="\xFF\xFF\xFF\xFF") {
|
|
|
|
var h = {
|
|
|
|
day:day+1, hr : hr, min:m*10,
|
|
|
|
steps : (h.charCodeAt(0)<<8) | h.charCodeAt(1),
|
|
|
|
bpm : h.charCodeAt(2),
|
|
|
|
movement : h.charCodeAt(3)
|
|
|
|
};
|
|
|
|
csv += [
|
|
|
|
date + "-" + h.day,
|
|
|
|
h.hr+":"+h.min.toString().padStart(2,0),
|
|
|
|
h.steps,
|
|
|
|
h.bpm||"",
|
|
|
|
h.movement
|
|
|
|
].join(",")+"\n";
|
|
|
|
}
|
|
|
|
idx += DB_RECORD_LEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
idx += DB_RECORD_LEN; // +1 because we have an extra record with totals for the end of the day
|
|
|
|
}
|
|
|
|
|
|
|
|
Util.saveCSV(title, csv);
|
|
|
|
}
|
|
|
|
|
|
|
|
function downloadHealth(filename, callback) {
|
2022-01-27 16:37:11 +00:00
|
|
|
Util.showModal("Downloading Health info...");
|
2021-11-02 16:19:32 +00:00
|
|
|
Util.readStorage(filename, data => {
|
|
|
|
Util.hideModal();
|
|
|
|
callback(data);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function getMonthList() {
|
|
|
|
Util.showModal("Loading...");
|
2023-01-03 13:04:07 +00:00
|
|
|
domContent.innerHTML = "";
|
2021-11-02 16:19:32 +00:00
|
|
|
Puck.eval(`require("Storage").list(/^health-.*\\.raw$/)`,files=>{
|
|
|
|
files = files.map(f => {
|
|
|
|
var m = f.match(/^health-([^\.]+)\.raw$/);
|
|
|
|
return {
|
|
|
|
filename : f,
|
|
|
|
date : m[1], // eg 2021-9
|
|
|
|
str : new Date(m[1]).toLocaleString(undefined, {month:'long',year:'numeric'})
|
|
|
|
}
|
|
|
|
})
|
2023-01-03 13:04:07 +00:00
|
|
|
var htmlOverview = `<table class="table table-striped table-hover">
|
2021-11-02 16:19:32 +00:00
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th>Month</th>
|
|
|
|
<th></th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>\n`;
|
|
|
|
files.forEach(f => {
|
2023-01-03 13:04:07 +00:00
|
|
|
htmlOverview += `
|
2021-11-02 16:19:32 +00:00
|
|
|
<tr>
|
|
|
|
<td>${f.str}</td>
|
|
|
|
<td>
|
|
|
|
<button class="btn btn-primary" filename="${f.filename}" date="${f.date}" task="downloadcsv">Download CSV</button>
|
2023-01-03 14:18:45 +00:00
|
|
|
<button class="btn btn-primary" filename="${f.filename}" date="${f.date}" monthstr="${f.str}" task="monthtable">Table</button>
|
|
|
|
<button class="btn btn-primary" filename="${f.filename}" date="${f.date}" monthstr="${f.str}" task="monthgraph">Graph</button>
|
2023-01-03 13:04:07 +00:00
|
|
|
<button class="btn btn-error" filename="${f.filename}" date="${f.date}" task="delete" style="float: right;margin-right: 5px;">Delete</button>
|
2021-11-02 16:19:32 +00:00
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
`;
|
|
|
|
});
|
|
|
|
if (files.length==0) {
|
2023-01-03 13:04:07 +00:00
|
|
|
htmlOverview += `
|
2021-11-02 16:19:32 +00:00
|
|
|
<tr>
|
|
|
|
<td>No data recorded</td>
|
|
|
|
<td></td>
|
|
|
|
</tr>
|
|
|
|
`;
|
|
|
|
}
|
2023-01-03 13:04:07 +00:00
|
|
|
htmlOverview += `
|
2021-11-02 16:19:32 +00:00
|
|
|
</tbody>
|
|
|
|
</table>`;
|
2023-01-03 13:04:07 +00:00
|
|
|
domContent.innerHTML = htmlOverview;
|
2021-11-02 16:19:32 +00:00
|
|
|
Util.hideModal();
|
2023-01-03 13:04:07 +00:00
|
|
|
var buttons = domContent.querySelectorAll("button");
|
2021-11-02 16:19:32 +00:00
|
|
|
for (var i=0;i<buttons.length;i++) {
|
|
|
|
buttons[i].addEventListener("click",event => {
|
|
|
|
var button = event.currentTarget;
|
|
|
|
var filename = button.getAttribute("filename");
|
|
|
|
var date = button.getAttribute("date");
|
|
|
|
if (!filename || !date) return;
|
2023-01-03 14:18:45 +00:00
|
|
|
var monthstr = button.getAttribute("monthstr");
|
2021-11-02 16:19:32 +00:00
|
|
|
var task = button.getAttribute("task");
|
|
|
|
if (task=="delete") {
|
|
|
|
Util.showModal("Deleting...");
|
|
|
|
Util.eraseStorage(filename,()=>{
|
|
|
|
Util.hideModal();
|
2022-06-16 06:48:40 +00:00
|
|
|
getMonthList();
|
2021-11-02 16:19:32 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
if (task=="downloadcsv") {
|
|
|
|
downloadHealth(filename, data => saveCSV(data, date, `Bangle.js Health ${date}`));
|
|
|
|
}
|
2023-01-03 14:18:45 +00:00
|
|
|
if (task=="monthtable") {
|
|
|
|
viewMonthDataAsTable(filename, date, monthstr);
|
|
|
|
}
|
|
|
|
if (task=="monthgraph") {
|
|
|
|
viewMonthDataAsGraph(filename, date, monthstr);
|
2023-01-03 13:04:07 +00:00
|
|
|
}
|
2021-11-02 16:19:32 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-01-03 14:18:45 +00:00
|
|
|
function getDailyData(data) {
|
|
|
|
var dailyData = [];
|
|
|
|
var idx = DB_HEADER_LEN;
|
|
|
|
for (var day = 0; day < 31; day++) {
|
|
|
|
var dayData = {steps: 0, bpm: 0, movement: 0};
|
|
|
|
for (var hr = 0; hr < 24; hr++) { // actually 25, see below
|
|
|
|
for (var m = 0; m < DB_RECORDS_PER_HR; m++) {
|
|
|
|
var h = data.substr(idx, DB_RECORD_LEN);
|
|
|
|
if (h != "\xFF\xFF\xFF\xFF") {
|
|
|
|
var h = {
|
|
|
|
day : day + 1,
|
|
|
|
hr : hr,
|
|
|
|
min : m * 10,
|
|
|
|
steps : (h.charCodeAt(0) << 8) | h.charCodeAt(1),
|
|
|
|
bpm : h.charCodeAt(2),
|
|
|
|
movement : h.charCodeAt(3)
|
|
|
|
};
|
|
|
|
dayData.steps += h.steps; // sum
|
2023-11-19 08:42:35 +00:00
|
|
|
if (h.bpm > 0) {
|
|
|
|
dayData.bpm = dayData.bpm > 0 ? (dayData.bpm + h.bpm) / 2 : h.bpm; // average
|
|
|
|
}
|
2023-01-03 14:18:45 +00:00
|
|
|
dayData.movement += h.movement; // sum
|
|
|
|
}
|
|
|
|
idx += DB_RECORD_LEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
idx += DB_RECORD_LEN; // +1 because we have an extra record with totals
|
|
|
|
// for the end of the day
|
|
|
|
|
|
|
|
dailyData[day + 1] = dayData;
|
|
|
|
}
|
|
|
|
return dailyData;
|
|
|
|
}
|
|
|
|
|
|
|
|
function viewMonthDataAsTable(filename, date, monthstr) {
|
|
|
|
Util.showModal("Reading Health info...");
|
|
|
|
Util.readStorage(
|
2023-01-03 13:04:07 +00:00
|
|
|
filename, data => {
|
|
|
|
Util.hideModal();
|
|
|
|
|
2023-01-03 14:18:45 +00:00
|
|
|
var htmlOverview = `<h1>` + monthstr + `</ h1>
|
2023-01-03 13:04:07 +00:00
|
|
|
<button class="btn btn-primary" id="backtomonth" style="float: right;margin-right: 5px;">Back</button>
|
|
|
|
<table class="table table-striped table-hover">
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th>Day</th>
|
|
|
|
<th>Steps</th>
|
|
|
|
<th>BPM</th>
|
|
|
|
<th>Movement</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>\n`;
|
|
|
|
|
2023-01-03 14:18:45 +00:00
|
|
|
var dailyData = getDailyData(data);
|
|
|
|
for (var i = 1; i < dailyData.length + 1; i++) {
|
|
|
|
var dayData = dailyData[i];
|
|
|
|
if (dayData) {
|
|
|
|
htmlOverview += `<tr>
|
|
|
|
<td>${i}</td>
|
|
|
|
<td>${dayData.steps}</td>
|
|
|
|
<td>${Math.round(dayData.bpm)}</td>
|
|
|
|
<td>${dayData.movement}</td></tr>`
|
2023-01-03 13:04:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
htmlOverview += `</tbody></table>`;
|
|
|
|
domContent.innerHTML = htmlOverview;
|
|
|
|
domContent.querySelector("#backtomonth").addEventListener("click",event => {
|
|
|
|
getMonthList();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-01-03 14:18:45 +00:00
|
|
|
function viewMonthDataAsGraph(filename, date, monthstr) {
|
|
|
|
Util.showModal("Reading Health info...");
|
|
|
|
Util.readStorage(
|
|
|
|
filename, data => {
|
|
|
|
Util.hideModal();
|
|
|
|
|
|
|
|
var html = `<h1>` + monthstr + `</ h1>
|
|
|
|
<button class="btn btn-primary" id="backtomonth" style="float: right;margin-right: 5px;">Back</button>
|
|
|
|
<h2>Steps</h2>
|
|
|
|
<canvas id="chartSteps"></canvas>
|
|
|
|
<h2>BPM</h2>
|
|
|
|
<canvas id="chartBPM"></canvas>
|
|
|
|
<h2>Movement</h2>
|
|
|
|
<canvas id="chartMovement"></canvas>`
|
|
|
|
domContent.innerHTML = html;
|
|
|
|
domContent.querySelector("#backtomonth").addEventListener("click",event => {
|
|
|
|
getMonthList();
|
|
|
|
});
|
|
|
|
|
|
|
|
var labels = [];
|
|
|
|
var dataSteps = [], dataBPM = [], dataMovement = [];
|
|
|
|
|
|
|
|
var dailyData = getDailyData(data);
|
|
|
|
for (var i = 1; i < dailyData.length + 1; i++) {
|
|
|
|
var dayData = dailyData[i];
|
|
|
|
if (dayData) {
|
|
|
|
labels.push(i);
|
|
|
|
dataSteps.push(dayData.steps);
|
|
|
|
dataBPM.push(dayData.bpm);
|
|
|
|
dataMovement.push(dayData.movement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
new Chart(document.getElementById("chartSteps"), {
|
|
|
|
type: 'bar',
|
|
|
|
data: {
|
|
|
|
labels: labels,
|
|
|
|
datasets: [{
|
|
|
|
label: '# of steps',
|
|
|
|
data: dataSteps,
|
|
|
|
borderWidth: 1
|
|
|
|
}]
|
|
|
|
},
|
|
|
|
options: {
|
|
|
|
scales: {
|
|
|
|
y: {
|
|
|
|
beginAtZero: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
new Chart(document.getElementById("chartBPM"), {
|
|
|
|
type: 'bar',
|
|
|
|
data: {
|
|
|
|
labels: labels,
|
|
|
|
datasets: [{
|
|
|
|
label: 'Beats per minute',
|
|
|
|
data: dataBPM,
|
|
|
|
borderWidth: 1
|
|
|
|
}]
|
|
|
|
},
|
|
|
|
options: {
|
|
|
|
scales: {
|
|
|
|
y: {
|
|
|
|
beginAtZero: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
new Chart(document.getElementById("chartMovement"), {
|
|
|
|
type: 'bar',
|
|
|
|
data: {
|
|
|
|
labels: labels,
|
|
|
|
datasets: [{
|
|
|
|
label: 'Movement',
|
|
|
|
data: dataMovement,
|
|
|
|
borderWidth: 1
|
|
|
|
}]
|
|
|
|
},
|
|
|
|
options: {
|
|
|
|
scales: {
|
|
|
|
y: {
|
|
|
|
beginAtZero: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-11-02 16:19:32 +00:00
|
|
|
function onInit() {
|
|
|
|
getMonthList();
|
|
|
|
}
|
|
|
|
|
|
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|