diff --git a/apps/health/ChangeLog b/apps/health/ChangeLog index 99e2490c9..78119fbde 100644 --- a/apps/health/ChangeLog +++ b/apps/health/ChangeLog @@ -39,4 +39,6 @@ Speed improvements (put temporary functions in RAM where possible) 0.34: Fix readFullDatabase (was skipping first month of data) 0.35: Update boot/lib.min.js -0.36: Fix Distance graphs that used '1*' to remove the suffix \ No newline at end of file +0.36: Fix Distance graphs that used '1*' to remove the suffix +0.37: Reduce movement limit for HRM off from 400 to 100 + Fix daily summary for movement (was not scaling down correctly) \ No newline at end of file diff --git a/apps/health/boot.js b/apps/health/boot.js index fca55fa10..b0d0a4a7c 100644 --- a/apps/health/boot.js +++ b/apps/health/boot.js @@ -6,7 +6,7 @@ function startMeasurement() { // if is charging, or hardly moved and face up/down, don't start HRM if (Bangle.isCharging() || - (Bangle.getHealthStatus("last").movement<400 && Math.abs(Bangle.getAccel().z)>0.99)) return; + (Bangle.getHealthStatus("last").movement<100 && Math.abs(Bangle.getAccel().z)>0.99)) return; // otherwise turn HRM on Bangle.setHRMPower(1, "health"); setTimeout(() => { @@ -81,12 +81,6 @@ Bangle.on("health", health => { require("Storage").write(fn, "HEALTH2\0", 0, DB_HEADER_LEN + DB_RECORDS_PER_MONTH*inf.r); // header (and allocate full new file) } var recordPos = DB_HEADER_LEN+(rec*inf.r); - - // scale down reported movement value in order to fit it within a - // uint8 DB field - health = Object.assign({}, health); - health.movement /= 8; - require("Storage").write(fn, inf.encode(health), recordPos); if (rec%DB_RECORDS_PER_DAY != DB_RECORDS_PER_DAY-2) return; // we're at the end of the day. Read in all of the data for the day and sum it up diff --git a/apps/health/boot.min.js b/apps/health/boot.min.js index 31c5a6781..31f69af99 100644 --- a/apps/health/boot.min.js +++ b/apps/health/boot.min.js @@ -1,5 +1,5 @@ -{let a=0|(require("Storage").readJSON("health.json",1)||{}).hrm;if(1==a||2==a){let d=function(b){function c(){Bangle.isCharging()||400>Bangle.getHealthStatus("last").movement&&.99{Bangle.setHRMPower(0,"health")},6E4*a))}c();1==a&&(setTimeout(c,2E5),setTimeout(c,4E5))};Bangle.on("health",d);Bangle.on("HRM",b=>{90Math.abs(Bangle.getHealthStatus().bpm-b.bpm)&&Bangle.setHRMPower(0, +{let a=0|(require("Storage").readJSON("health.json",1)||{}).hrm;if(1==a||2==a){let d=function(b){function c(){Bangle.isCharging()||100>Bangle.getHealthStatus("last").movement&&.99{Bangle.setHRMPower(0,"health")},6E4*a))}c();1==a&&(setTimeout(c,2E5),setTimeout(c,4E5))};Bangle.on("health",d);Bangle.on("HRM",b=>{90Math.abs(Bangle.getHealthStatus().bpm-b.bpm)&&Bangle.setHRMPower(0, "health")});90>Bangle.getHealthStatus().bpmConfidence&&d()}else Bangle.setHRMPower(!!a,"health")}Bangle.on("health",a=>{(Bangle.getPressure?Bangle.getPressure():Promise.resolve({})).then(d=>{Object.assign(a,d);d=new Date(Date.now()-59E4);if(a&&0=b.stepGoal&&(c=(new Date(Date.now())).toISOString().split("T")[0],!b.stepGoalNotificationDate||b.stepGoalNotificationDate< c)&&(Bangle.buzz(200,.5),require("notify").show({title:b.stepGoal+" steps",body:"You reached your step goal!",icon:atob("DAyBABmD6BaBMAsA8BCBCBCBCA8AAA==")}),b.stepGoalNotificationDate=c,require("Storage").writeJSON("health.json",b))}var g=function(f){return 145*(f.getDate()-1)+6*f.getHours()+(0|6*f.getMinutes()/60)}(d);d=function(f){return"health-"+f.getFullYear()+"-"+(f.getMonth()+1)+".raw"}(d);c=require("Storage").read(d);if(void 0!==c){b=require("health").getDecoder(c);var e=c.substr(8+g*b.r, -b.r);if(e!=b.clr){print("HEALTH ERR: Already written!");return}}else b=require("health").getDecoder("HEALTH2"),require("Storage").write(d,"HEALTH2\x00",0,8+4495*b.r);var h=8+g*b.r;a=Object.assign({},a);a.movement/=8;require("Storage").write(d,b.encode(a),h);if(143==g%145)if(g=h+b.r,c.substr(g,b.r)!=b.clr)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++)e=c.substr(h,b.r),e!=b.clr&&(e=b.decode(e),a.steps+=e.steps,a.bpm+=e.bpm, -a.movement+=e.movement,a.movCnt++,e.bpm&&a.bpmCnt++),h-=b.r;a.bpmCnt&&(a.bpm/=a.bpmCnt);a.movCnt&&(a.movement/=a.movCnt);require("Storage").write(d,b.encode(a),g)}})}) \ No newline at end of file +b.r);if(e!=b.clr){print("HEALTH ERR: Already written!");return}}else b=require("health").getDecoder("HEALTH2"),require("Storage").write(d,"HEALTH2\x00",0,8+4495*b.r);var h=8+g*b.r;require("Storage").write(d,b.encode(a),h);if(143==g%145)if(g=h+b.r,c.substr(g,b.r)!=b.clr)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++)e=c.substr(h,b.r),e!=b.clr&&(e=b.decode(e),a.steps+=e.steps,a.bpm+=e.bpm,a.movement+=e.movement,a.movCnt++, +e.bpm&&a.bpmCnt++),h-=b.r;a.bpmCnt&&(a.bpm/=a.bpmCnt);a.movCnt&&(a.movement/=a.movCnt);require("Storage").write(d,b.encode(a),g)}})}) \ No newline at end of file diff --git a/apps/health/lib.js b/apps/health/lib.js index 9a0f5148b..69375a8a7 100644 --- a/apps/health/lib.js +++ b/apps/health/lib.js @@ -61,7 +61,7 @@ exports.getDecoder = function(fileContents) { health.steps>>8,health.steps&255, // 16 bit steps health.bpmMin || health.bpm, // 8 bit bpm health.bpmMax || health.bpm, // 8 bit bpm - Math.min(health.movement, 255), + Math.min(health.movement >> 3, 255), E.getBattery()|(Bangle.isCharging()&&128), 0|Math.round(health.temperature*2), (alt>>8)|(Math.max(0,exports.ACTIVITY.indexOf(health.activity))<<5),alt&255, @@ -82,7 +82,7 @@ exports.getDecoder = function(fileContents) { encode : health => { "ram"; return String.fromCharCode( health.steps>>8,health.steps&255, // 16 bit steps health.bpm, // 8 bit bpm - Math.min(health.movement, 255)); + Math.min(health.movement >> 3, 255)); } }; } diff --git a/apps/health/lib.min.js b/apps/health/lib.min.js index 446452963..d3d80bbe6 100644 --- a/apps/health/lib.min.js +++ b/apps/health/lib.min.js @@ -1,6 +1,6 @@ function k(b){return"health-"+b.getFullYear()+"-"+(b.getMonth()+1)+".raw"}function l(b){return 145*(b.getDate()-1)+6*b.getHours()+(0|6*b.getMinutes()/60)}exports.ACTIVITY="UNKNOWN NOT_WORN WALKING EXERCISE LIGHT_SLEEP DEEP_SLEEP".split(" ");exports.getDecoder=function(b){return"HEALTH2"==b.substr(0,7)?{r:10,clr:"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",decode:a=>{"ram";a=a.charCodeAt.bind(a);a={steps:a(0)<<8|a(1),bpmMin:a(2),bpmMax:a(3),movement:8*a(4), -battery:a(5)&127,isCharging:!!(a(5)&128),temperature:a(6)/2,altitude:(a(7)&31)<<8|a(8),activity:exports.ACTIVITY[a(7)>>5]};80{"ram";var c=a.altitude&8191;return String.fromCharCode(a.steps>>8,a.steps&255,a.bpmMin||a.bpm,a.bpmMax||a.bpm,Math.min(a.movement,255),E.getBattery()|(Bangle.isCharging()&&128),0|Math.round(2*a.temperature),c>>8|Math.max(0,exports.ACTIVITY.indexOf(a.activity))<< -5,c&255,0)}}:{r:4,clr:"\xff\xff\xff\xff",decode:a=>{"ram";return{steps:a.charCodeAt(0)<<8|a.charCodeAt(1),bpm:a.charCodeAt(2),bpmMin:a.charCodeAt(2),bpmMax:a.charCodeAt(2),movement:8*a.charCodeAt(3)}},encode:a=>{"ram";return String.fromCharCode(a.steps>>8,a.steps&255,a.bpm,Math.min(a.movement,255))}}};exports.readAllRecords=function(b,a){b=k(b);b=require("Storage").read(b);if(void 0!==b)for(var c=exports.getDecoder(b),d={},e=8,f=0;31>f;f++){d.day=f+1;for(var g=0;24>g;g++){d.hr=g;for(var h= +battery:a(5)&127,isCharging:!!(a(5)&128),temperature:a(6)/2,altitude:(a(7)&31)<<8|a(8),activity:exports.ACTIVITY[a(7)>>5]};80{"ram";var c=a.altitude&8191;return String.fromCharCode(a.steps>>8,a.steps&255,a.bpmMin||a.bpm,a.bpmMax||a.bpm,Math.min(a.movement>>3,255),E.getBattery()|(Bangle.isCharging()&&128),0|Math.round(2*a.temperature),c>>8|Math.max(0,exports.ACTIVITY.indexOf(a.activity))<< +5,c&255,0)}}:{r:4,clr:"\xff\xff\xff\xff",decode:a=>{"ram";return{steps:a.charCodeAt(0)<<8|a.charCodeAt(1),bpm:a.charCodeAt(2),bpmMin:a.charCodeAt(2),bpmMax:a.charCodeAt(2),movement:8*a.charCodeAt(3)}},encode:a=>{"ram";return String.fromCharCode(a.steps>>8,a.steps&255,a.bpm,Math.min(a.movement>>3,255))}}};exports.readAllRecords=function(b,a){b=k(b);b=require("Storage").read(b);if(void 0!==b)for(var c=exports.getDecoder(b),d={},e=8,f=0;31>f;f++){d.day=f+1;for(var g=0;24>g;g++){d.hr=g;for(var h= 0;6>h;h++){d.min=10*h;var m=b.substr(e,c.r);m!=c.clr&&a(Object.assign(c.decode(m),d));e+=c.r}}e+=c.r}};exports.readFullDatabase=function(b){require("Storage").list(/health-[0-9]+-[0-9]+.raw/).forEach(a=>{a=a.split("-");var c=parseInt(a[1],10),d=parseInt(a[2].replace(".raw",""),10)-1;exports.readAllRecords(new Date(c,d,1),e=>{"ram";e.date=new Date(c,d,e.day,e.hr,e.min);b(e)})})};exports.readAllRecordsSince=function(b,a){for(var c=(new Date).getTime(),d=new Date(b.toISOString().substr(0,10));d.getTime()<= c;)exports.readDay(d,e=>{"ram";e.date=new Date(d.getFullYear(),d.getMonth(),d.getDate(),e.hr,e.min);a(e)}),d.setDate(d.getDate()+1)};exports.readDailySummaries=function(b,a){l(b);b=k(b);b=require("Storage").read(b);if(void 0!==b)for(var c=exports.getDecoder(b),d=8+144*c.r,e=0;31>e;e++){var f=b.substr(d,c.r);f!=c.clr&&a(Object.assign(c.decode(f),{day:e+1}));d+=145*c.r}};exports.readDay=function(b,a){l(b);var c=k(b);c=require("Storage").read(c);if(void 0!==c){var d=exports.getDecoder(c),e={};b=8+145* d.r*(b.getDate()-1);for(var f=0;24>f;f++){e.hr=f;for(var g=0;6>g;g++){e.min=10*g;var h=c.substr(b,d.r);h!=d.clr&&a(Object.assign(d.decode(h),e));b+=d.r}}}} \ No newline at end of file diff --git a/apps/health/metadata.json b/apps/health/metadata.json index 36497c5ba..9dbd28c00 100644 --- a/apps/health/metadata.json +++ b/apps/health/metadata.json @@ -2,7 +2,7 @@ "id": "health", "name": "Health Tracking", "shortName": "Health", - "version": "0.36", + "version": "0.37", "description": "Logs health data and provides an app to view it", "icon": "app.png", "screenshots" : [ { "url":"screenshot.png" } ],