From 64c132ab978b5e14beca7d6017686f828650e321 Mon Sep 17 00:00:00 2001 From: hughbarney Date: Mon, 15 Nov 2021 21:19:02 +0000 Subject: [PATCH] Health: added bar charts --- apps.json | 10 +-- apps/health/ChangeLog | 1 + apps/health/app.js | 174 ++++++++++++++++++++++++++++++------------ 3 files changed, 132 insertions(+), 53 deletions(-) diff --git a/apps.json b/apps.json index 2ee8a0e49..f307a6feb 100644 --- a/apps.json +++ b/apps.json @@ -82,7 +82,7 @@ { "id": "health", "name": "Health Tracking", - "version": "0.06", + "version": "0.07", "description": "Logs health data and provides an app to view it (BETA - requires firmware 2v11)", "icon": "app.png", "tags": "tool,system,health", @@ -3467,7 +3467,7 @@ "id": "widgps", "name": "GPS Widget", "version": "0.03", - "description": "Tiny widget to show the power on/off status of the GPS. Require firmware v2.08.167 or later", + "description": "Tiny widget to show the power on/off status of the GPS", "icon": "widget.png", "type": "widget", "tags": "widget,gps", @@ -3481,7 +3481,7 @@ "id": "widhrt", "name": "HRM Widget", "version": "0.03", - "description": "Tiny widget to show the power on/off status of the Heart Rate Monitor. Requires firmware v2.08.167 or later", + "description": "Tiny widget to show the power on/off status of the Heart Rate Monitor", "icon": "widget.png", "type": "widget", "tags": "widget,hrm", @@ -3524,7 +3524,7 @@ "id": "widcom", "name": "Compass Widget", "version": "0.02", - "description": "Tiny widget to show the power on/off status of the Compass. Requires firmware v2.08.167 or later", + "description": "Tiny widget to show the power on/off status of the Compass", "icon": "widget.png", "type": "widget", "tags": "widget,compass", @@ -3665,7 +3665,7 @@ "id": "kitchen", "name": "Kitchen Combo", "version": "0.13", - "description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later", + "description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'", "icon": "kitchen.png", "type": "clock", "tags": "tool,outdoors,gps", diff --git a/apps/health/ChangeLog b/apps/health/ChangeLog index ef1b1bffc..5eb96a0ea 100644 --- a/apps/health/ChangeLog +++ b/apps/health/ChangeLog @@ -5,3 +5,4 @@ Don't restart HRM when changing apps if we've already got a good BPM value 0.05: Fix daily summary calculation 0.06: Fix daily health summary for movement (a line got deleted!) +0.07: Added coloured bar charts diff --git a/apps/health/app.js b/apps/health/app.js index 4d692e638..eae45c190 100644 --- a/apps/health/app.js +++ b/apps/health/app.js @@ -7,6 +7,8 @@ function setSettings(s) { } function menuMain() { + swipe_enabled = false; + clearButton(); E.showMenu({ "":{title:"Health Tracking"}, "< Back":()=>load(), @@ -18,6 +20,8 @@ function menuMain() { } function menuSettings() { + swipe_enabled = false; + clearButton(); var s=getSettings(); E.showMenu({ "":{title:"Health Tracking"}, @@ -32,6 +36,8 @@ function menuSettings() { } function menuStepCount() { + swipe_enabled = false; + clearButton(); E.showMenu({ "":{title:"Step Counting"}, "< Back":()=>menuMain(), @@ -41,6 +47,8 @@ function menuStepCount() { } function menuMovement() { + swipe_enabled = false; + clearButton(); E.showMenu({ "":{title:"Movement"}, "< Back":()=>menuMain(), @@ -50,6 +58,8 @@ function menuMovement() { } function menuHRM() { + swipe_enabled = false; + clearButton(); E.showMenu({ "":{title:"Heart Rate"}, "< Back":()=>menuMain(), @@ -66,14 +76,8 @@ function stepsPerHour() { g.clear(1); Bangle.drawWidgets(); g.reset(); - require("graph").drawBar(g, data, { - y:24, - miny: 0, - axes : true, - gridx : 6, - gridy : 500 - }); - Bangle.setUI("updown", ()=>menuStepCount()); + setButton(menuStepCount); + barChart("HOUR", data); } function stepsPerDay() { @@ -83,14 +87,8 @@ function stepsPerDay() { g.clear(1); Bangle.drawWidgets(); g.reset(); - require("graph").drawBar(g, data, { - y:24, - miny: 0, - axes : true, - gridx : 5, - gridy : 2000 - }); - Bangle.setUI("updown", ()=>menuStepCount()); + setButton(menuStepCount); + barChart("DAY", data); } function hrmPerHour() { @@ -105,14 +103,8 @@ function hrmPerHour() { g.clear(1); Bangle.drawWidgets(); g.reset(); - require("graph").drawBar(g, data, { - y:24, - miny: 0, - axes : true, - gridx : 6, - gridy : 20 - }); - Bangle.setUI("updown", ()=>menuHRM()); + setButton(menuHRM); + barChart("HOUR", data); } function hrmPerDay() { @@ -127,14 +119,8 @@ function hrmPerDay() { g.clear(1); Bangle.drawWidgets(); g.reset(); - require("graph").drawBar(g, data, { - y:24, - miny: 0, - axes : true, - gridx : 5, - gridy : 20 - }); - Bangle.setUI("updown", ()=>menuHRM()); + setButton(menuHRM); + barChart("DAY", data); } function movementPerHour() { @@ -144,14 +130,8 @@ function movementPerHour() { g.clear(1); Bangle.drawWidgets(); g.reset(); - require("graph").drawLine(g, data, { - y:24, - miny: 0, - axes : true, - gridx : 6, - ylabel : null - }); - Bangle.setUI("updown", ()=>menuMovement()); + setButton(menuMovement); + barChart("HOUR", data); } function movementPerDay() { @@ -161,14 +141,112 @@ function movementPerDay() { g.clear(1); Bangle.drawWidgets(); g.reset(); - require("graph").drawBar(g, data, { - y:24, - miny: 0, - axes : true, - gridx : 5, - ylabel : null - }); - Bangle.setUI("updown", ()=>menuMovement()); + setButton(menuMovement); + barChart("DAY", data); +} + +// Bar Chart Code + +const w = g.getWidth(); +const h = g.getHeight(); + +var data_len; +var chart_index; +var chart_max_datum; +var chart_label; +var chart_data; +var swipe_enabled = false; +var btn; + +// find the max value in the array, using a loop due to array size +function max(arr) { + var m = -Infinity; + + for(var i=0; i< arr.length; i++) + if(arr[i] > m) m = arr[i]; + return m; +} + +// find the end of the data, the array might be for 31 days but only have 2 days of data in it +function get_data_length(arr) { + var nlen = arr.length; + + for(var i = arr.length - 1; i > 0 && arr[i] == 0; i--) + nlen--; + + return nlen; +} + +function barChart(label, 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_max_datum = max(dt); // find highest bar, for scaling + chart_label = label; + chart_data = dt; + drawBarChart(); + swipe_enabled = true; +} + +function drawBarChart() { + const bar_bot = 140; + const bar_width = (w - 2) / 9; // we want 9 bars, bar 5 in the centre + var bar_top; + var bar; + + g.setColor(g.theme.bg); + g.fillRect(0,24,w,h); + + for (bar = 1; bar < 10; bar++) { + if (bar == 5) { + g.setFont('6x8', 2); + g.setFontAlign(0,-1) + g.setColor(g.theme.fg); + g.drawString(chart_label + " " + (chart_index + bar -1) + " " + chart_data[chart_index + bar - 1], g.getWidth()/2, 150); + g.setColor("#00f"); + } else { + g.setColor("#0ff"); + } + + // draw a fake 0 height bar if chart_index is outside the bounds of the array + if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len) + bar_top = bar_bot - 100 * (chart_data[chart_index + bar - 1]) / chart_max_datum; + else + bar_top = bar_bot; + + g.fillRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top); + g.setColor(g.theme.fg); + g.drawRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top); + } +} + +function next_bar() { + chart_index = Math.min(data_len - 5, chart_index + 1); +} + +function prev_bar() { + // HOUR data starts at index 0, DAY data starts at index 1 + chart_index = Math.max((chart_label == "DAY") ? -3 : -4, chart_index - 1); +} + +Bangle.on('swipe', dir => { + if (!swipe_enabled) return; + if (dir == 1) prev_bar(); else next_bar(); + drawBarChart(); +}); + +// use setWatch() as Bangle.setUI("updown",..) interacts with swipes +function setButton(fn) { + if (process.env.HWVERSION == 1) + btn = setWatch(fn, BTN2); + else + btn = setWatch(fn, BTN1); +} + +function clearButton() { + if (btn !== undefined) { + clearWatch(btn); + btn = undefined; + } } Bangle.loadWidgets();