diff --git a/apps/health/ChangeLog b/apps/health/ChangeLog index 87a58a57b..fb97552b1 100644 --- a/apps/health/ChangeLog +++ b/apps/health/ChangeLog @@ -17,3 +17,4 @@ 0.16: Code tidyup, add back button in top left of health app graphs 0.17: Add automatic translation of bar chart labels 0.18: Show step goal in daily step chart +0.19: Can show notification when daily step goal is reached diff --git a/apps/health/README.md b/apps/health/README.md index 8769ff9b7..960e5565b 100644 --- a/apps/health/README.md +++ b/apps/health/README.md @@ -25,7 +25,8 @@ Stores: * **3 Min** - Turn HRM on every 3 minutes (for each heath entry) and turn it off after 1 minute, or when a good reading is found * **10 Min** - Turn HRM on every 10 minutes (for each heath entry) and turn it off after 2 minutes, or when a good reading is found * **Always** - Keep HRM on all the time (more accurate recording, but reduces battery life to ~36 hours) -* **Daily Step Goal** - Default 10000, daily step goal for pedometer apps to use +* **Daily Step Goal** - Default 10000, daily step goal for pedometer apps to use and for the step goal notification +* **Step Goal Notification** - True if you want a notification when the daily step goal is reached ## Technical Info diff --git a/apps/health/boot.js b/apps/health/boot.js index 7b9aa51aa..0f4e6c832 100644 --- a/apps/health/boot.js +++ b/apps/health/boot.js @@ -36,6 +36,10 @@ Bangle.on("health", health => { const DB_HEADER_LEN = 8; const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN; + if (health && health.steps > 0) { + handleStepGoalNotification(); + } + function getRecordFN(d) { return "health-"+d.getFullYear()+"-"+(d.getMonth()+1)+".raw"; } @@ -92,3 +96,21 @@ Bangle.on("health", health => { health.movement /= health.movCnt; require("Storage").write(fn, getRecordData(health), sumPos, DB_FILE_LEN); }); + +function handleStepGoalNotification() { + var settings = require("Storage").readJSON("health.json",1)||{}; + const steps = Bangle.getHealthStatus("day").steps; + if (settings.stepGoalNotification && settings.stepGoal > 0 && steps >= settings.stepGoal) { + const now = new Date(Date.now()).toISOString().split('T')[0]; // yyyy-mm-dd + if (!settings.stepGoalNotificationDate || settings.stepGoalNotificationDate < now) { // notification not yet shown today? + Bangle.buzz(200, 0.5); + require("notify").show({ + title : /*LANG*/ settings.stepGoal + " steps", + body : /*LANG*/ "You reached your step goal!", + icon : atob("DAyBABmD6BaBMAsA8BCBCBCBCA8AAA==") + }); + settings.stepGoalNotificationDate = now; + require("Storage").writeJSON("health.json", settings); + } + } +} diff --git a/apps/health/boot.min.js b/apps/health/boot.min.js index 00313a1f5..963da9c6b 100644 --- a/apps/health/boot.min.js +++ b/apps/health/boot.min.js @@ -1,4 +1,5 @@ -(function(){var a=0|(require("Storage").readJSON("health.json",1)||{}).hrm;if(1==a||2==a){function f(){Bangle.setHRMPower(1,"health");setTimeout(()=>Bangle.setHRMPower(0,"health"),6E4*a);if(1==a)for(var b=1;2>=b;b++)setTimeout(()=>{Bangle.setHRMPower(1,"health");setTimeout(()=>{Bangle.setHRMPower(0,"health")},2E5*b+6E4)},2E5*b)}Bangle.on("health",f);Bangle.on("HRM",b=>{80{function f(c){return String.fromCharCode(c.steps>>8,c.steps&255,c.bpm,Math.min(c.movement/8,255))}var b=new Date(Date.now()-59E4),e=function(c){return 145*(c.getDate()-1)+6*c.getHours()+(0|6*c.getMinutes()/60)}(b);b=function(c){return"health-"+c.getFullYear()+"-"+(c.getMonth()+1)+".raw"}(b);var g=require("Storage").read(b);if(g){var d=g.substr(8+4*e,4);if("\u00ff\u00ff\u00ff\u00ff"!=d){print("HEALTH ERR: Already written!");return}}else require("Storage").write(b, -"HEALTH1\x00",0,17988);var h=8+4*e;require("Storage").write(b,f(a),h,17988);if(143==e%145)if(e=h+4,"\u00ff\u00ff\u00ff\u00ff"!=g.substr(e,4))print("HEALTH ERR: Daily summary already written!");else{a={steps:0,bpm:0,movement:0,movCnt:0,bpmCnt:0};for(var k=0;144>k;k++)d=g.substr(h,4),"\u00ff\u00ff\u00ff\u00ff"!=d&&(a.steps+=(d.charCodeAt(0)<<8)+d.charCodeAt(1),a.movement+=d.charCodeAt(2),a.movCnt++,d=d.charCodeAt(2),a.bpm+=d,d&&a.bpmCnt++),h-=4;a.bpmCnt&&(a.bpm/=a.bpmCnt);a.movCnt&&(a.movement/=a.movCnt); -require("Storage").write(b,f(a),e,17988)}}) \ No newline at end of file +function l(){var a=require("Storage").readJSON("health.json",1)||{},d=Bangle.getHealthStatus("day").steps;a.stepGoalNotification&&0=a.stepGoal&&(d=(new Date(Date.now())).toISOString().split("T")[0],!a.stepGoalNotificationDate||a.stepGoalNotificationDate=b;b++)setTimeout(function(){Bangle.setHRMPower(1,"health");setTimeout(function(){Bangle.setHRMPower(0,"health")},2E5*b+6E4)},2E5*b)};Bangle.on("health",d);Bangle.on("HRM",function(b){80>8,c.steps&255,c.bpm,Math.min(c.movement/8,255))}var b=new Date(Date.now()-59E4);a&&0k;k++)e=g.substr(h,4),"\u00ff\u00ff\u00ff\u00ff"!=e&&(a.steps+=(e.charCodeAt(0)<<8)+e.charCodeAt(1),a.movement+=e.charCodeAt(2),a.movCnt++,e=e.charCodeAt(2),a.bpm+=e,e&&a.bpmCnt++),h-=4;a.bpmCnt&&(a.bpm/= +a.bpmCnt);a.movCnt&&(a.movement/=a.movCnt);require("Storage").write(b,d(a),f,17988)}}) \ No newline at end of file diff --git a/apps/health/metadata.json b/apps/health/metadata.json index ecaf9b563..704101138 100644 --- a/apps/health/metadata.json +++ b/apps/health/metadata.json @@ -2,7 +2,7 @@ "id": "health", "name": "Health Tracking", "shortName": "Health", - "version": "0.18", + "version": "0.19", "description": "Logs health data and provides an app to view it", "icon": "app.png", "tags": "tool,system,health", @@ -16,5 +16,6 @@ {"name":"health","url":"lib.min.js"}, {"name":"health.settings.js","url":"settings.js"} ], - "data": [{"name":"health.json"}] + "data": [{"name":"health.json"}], + "dependencies": {"notify":"type"} } diff --git a/apps/health/settings.js b/apps/health/settings.js index f94efbb16..e7b0a7c15 100644 --- a/apps/health/settings.js +++ b/apps/health/settings.js @@ -1,7 +1,8 @@ (function (back) { var settings = Object.assign({ hrm: 0, - stepGoal: 10000 + stepGoal: 10000, + stepGoalNotification: false }, require("Storage").readJSON("health.json", true) || {}); E.showMenu({ @@ -34,6 +35,14 @@ settings.stepGoal = v; setSettings(settings); } + }, + /*LANG*/"Step Goal Notification": { + value: "stepGoalNotification" in settings ? settings.stepGoalNotification : false, + format: () => (settings.stepGoalNotification ? 'Yes' : 'No'), + onchange: () => { + settings.stepGoalNotification = !settings.stepGoalNotification; + setSettings(); + } } });