Skip to content

Create Calorie Tracking app w/ health integration#4142

Open
RKBoss6 wants to merge 79 commits intoespruino:masterfrom
RKBoss6:caloriesApp
Open

Create Calorie Tracking app w/ health integration#4142
RKBoss6 wants to merge 79 commits intoespruino:masterfrom
RKBoss6:caloriesApp

Conversation

@RKBoss6
Copy link
Contributor

@RKBoss6 RKBoss6 commented Jan 25, 2026

This is just a draft right now, but creates an app for calorie tracking based on #4092, that counts calories burned in a day and splits it by BMR, Active, and total calories. Uses MyProfile to get user health data, and hooks onto health events for new data. Also modifies myprofile to use RHR readings as an additional piece of data, with its own measurement UI.

Check it out at my app loader

RKBoss6 and others added 30 commits January 23, 2026 19:09
Add metadata for Calorie Tracker application
Expanded the README to include the calorie calculation formula and its components, enhancing user understanding of the app's functionality.
Refactor logic for setting resting heart rate and round max heart rate calculation.
Added instructions for taking Resting Heart Rate (RHR) measurements, including tips for optimal conditions and app navigation.
@thyttan
Copy link
Collaborator

thyttan commented Feb 6, 2026

Ah! I can try it out now :)

@RKBoss6
Copy link
Contributor Author

RKBoss6 commented Feb 6, 2026

Sorry, I just put it on my app loader right now

Copy link
Collaborator

@thyttan thyttan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Here's my initial review :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add white space in line with the preferred code style: https://www.espruino.com/Code+Style

Maybe easiest to use some auto formatter for this initial contribution to the repo?

And same for the other js files in the calories directory.

For the changes to the health and myprofile apps please follow the style there as well, but don't auto format the whole files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Just changed this app.js, does that look better?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thanks! I think so. But you lost all semi-colons at the same time? I think that will make e.g. the web ide editor complain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no! I didn't even see that 😂, just fixed it!

Copy link
Collaborator

@thyttan thyttan Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unresolved since the other files in the 'calories' directory is yet to be updated wrt white space.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unresolved since the other files in the 'calories' directory is yet to be updated wrt whitespace.

@@ -0,0 +1,127 @@
// Takes object with bpm, movement (in duration), steps (in duration), and duration in minutes
let calcAge=function(rawBday){
let birth = new Date(rawBday);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get this error, probably because I have not done any setup in the myprofile app?:

Uncaught TypeError: Variables of type undefined are not supported in date constructor
    at calcAge (calories:2:41)
let birth=new Date(rawBday);let now=new Date();let diffInDays=(now-birth)/8...
                                   ^
    at calcBMR (calories:3:93)
...=calcAge(myProfile.birthday);if(age<18){if(myProfile.gender!=undefined&&...
                                          ^
    at intermittentBMRUpdate (.boot0:30:140)
...calModule.calcBMR(myProfile)*((now-lastBMRWrite)/60000));cal...
                              ^
    at .boot0 (.boot0:32:512)
...()});intermittentBMRUpdate();;
                              ^

If so we should make this not happen some way. Either by using some default value somewhere or something. Not sure what's the best solution.

Copy link
Collaborator

@thyttan thyttan Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we catch the error and display a prompt about entering info in myprofile? From the prompt the user could be directed to the myprofile app via a positive action button.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we catch the error and display a prompt about entering info in myprofile. From the prompt the user could be directed to the myprofile app via a positive action button.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds really nice actually - I'll get on that

// returns cals/minute
exports.calcBMR=function(myProfile){
let bmr=0;
let weight = myProfile.weight;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get this error, probably since I haven't added any info to myprofile. Either we try to make sure the user have already entered info there. Or we nudge the user to add their weight. And/or add some default average value. What do you think?

>
{"t":"act","stp":0,"hrm":0,"mov":175,"act":"UNKNOWN"}
Uncaught Error: Calories: Not enough myProfile data to calculate!
    at calcCalories (calories:10:234)
...yProfile data to calculate!);}let weight=myProfile.weight;let age=...
                                    ^
    at .boot0:32:13
}),myProfile)if(!cd)return;calData.activeCaloriesBurned+=cd.activeC...
                   ^

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may be the best solution as of now? I feel like nudging the user to add certain elements means we need several checks that are separate for each element. I've added a table in the README for all the required fields, so if the user sees it, they can quickly check the readme/update the fields that are missing that way. Wdyt?

Copy link
Collaborator

@thyttan thyttan Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea adding to the readme!

I think we can also do the same thing with catching the error and presenting the user with a prompt like suggested for that other error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, sounds good! We'll just have one prompt then, that says Not enough myprofile data, and then prompts to open the app settings?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think that's good :)

Copy link
Contributor Author

@RKBoss6 RKBoss6 Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just committed, check it out!

});
};

menu[/*LANG*/`Resting HR: ${myprofile.restingHrm?myprofile.restingHrm:"--"}`]=RHRReading;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how best to handle but just looking in the settings page it looks weird having a "HR min" entry and a "Resting HR" entry. Even more so when they don't show the same number.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better if we do the full names like Minimum HR, Resting HR, Maximum HR?

Copy link
Collaborator

@thyttan thyttan Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you elaborated on this somewhere - could you link me to that thread? 🙏🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, I don't think I did at all... But will that make it any better in your eyes if we did do the full name?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was actually on this PR, sorry :p

#4142 (comment)

Could we change this a bit? I think it would be better if we just kept the one field 'HR min' and then have the resting heart rate measurement set the 'HR min' value once it finishes. That way we don't introduce a new store of the same data we already keep in the 'HR min' entry.

My suggestion in points:

  1. Rename the 'Resting HR: xx' entry to 'Take resting HR' or similar.
  2. Move that entry up so it's just below the 'HR min' entry.
  3. Make the now renamed 'Take resting HR' entry update the 'HR min' fields value.
  4. Update other files and readme to accommodate this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah - sorry, I think I had described Resting HR as the same as minimum hr for simplicity, but in reality, minimum HR is the absolute lowest your heart can go, while resting HR is bpm while your body is resting, which might be used differently. I would rather keep both because they can be slightly different, and specifically for formulas that need accuracy I don't think we should change it - would it be better if we kept it in a submenu instead with max hr, min hr and resting hr?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give a source for there being that distinction? When I searched they seemed to be synonymous.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... Or rather: Resting heart rate seems to be the established term and 'Min HR' is just what we happened to call it on the Bangle.

} else {
const age = (new Date()).getFullYear() - date.getFullYear();
const newMaxHRM = 220-age;
const newMaxHRM = Math.round(208-0.7*age);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a source saying the new calculation is actually better/more accurate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many, one I found is this

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks. That page does not say explicitly that it's proven to be better. But it's good enough I think. Maybe we put that web adress in a comment at the end of the line?

@thyttan
Copy link
Collaborator

thyttan commented Feb 27, 2026

Should we try to get this one over the finish line? I'll probably have some time to look closer again sunday morning :)

@RKBoss6
Copy link
Contributor Author

RKBoss6 commented Feb 27, 2026

Yes, that sounds good! I won't have that much time to do it for a while, but I can take a look at it in bits and pieces and get it done gradually :)

@thyttan
Copy link
Collaborator

thyttan commented Feb 27, 2026

Ok, no stress 🙂

@RKBoss6
Copy link
Contributor Author

RKBoss6 commented Mar 3, 2026

@thyttan do you happen to have another smartwatch lying around that can track calories already? I want to compare the accuracy of the tracker, so I will wear both for a day and see the difference in calories, with an old garmin lying around. If you can, can you do the same? That way we can test for different ages, values, etc...

@thyttan
Copy link
Collaborator

thyttan commented Mar 3, 2026

Nope, don't have a Gramin or similar I'm afraid.

@RKBoss6
Copy link
Contributor Author

RKBoss6 commented Mar 3, 2026

Ok, no worries! I'll collect some data to see if it's accurate


// Validate heart rate
if (!hr || hr < 40 || hr > myProfile.maxHrm) {
throw new Error("Invalid or missing heart rate data");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just got this error message:

|  __|___ ___ ___ _ _|_|___ ___
|  __|_ -| . |  _| | | |   | . |
|____|___|  _|_| |___|_|_|_|___|
         |_| espruino.com
 2v29.17 (c) 2025 G.Williams
>����OK
>
{"t":"act","stp":46,"hrm":74,"mov":276,"act":"UNKNOWN"}
{"t":"status","bat":13,"chg":0}
{"t":"act","stp":8,"hrm":0,"mov":172,"act":"UNKNOWN"}
Uncaught Error: Unhandled promise rejection: Error: Invalid or missing heart rate data
    at calcCalories (calories:11:455)
... or missing heart rate data);}let ageMultiplier=1;if(age<18){age...
                                  ^
    at .boot0:32:13
}),myProfile)if(!cd)return;calData.activeCaloriesBurned+=cd.activeC...
                   ^
> 

HRM was off in health app.

So maybe if the error is triggered the app should look at health settings json to see if HRM is off and if so suggest to turn it on?

But also, does the calories app fall back to calculating without hrm data? If not maybe that should be added?

throw new Error("Invalid or missing heart rate data");
}

// Age-adjusted MET values for better accuracy across age groups
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to read a source on age-adjusting metabolic rate values. Do you have one?

Copy link
Contributor Author

@RKBoss6 RKBoss6 Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I'm not sure - I ran it through AI to see if it would scale appropriately for different ages, and it told me this. I'm not sure whether it makes a difference, as I haven't tested this fully yet, but I'll get to testing hopefully soon.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. If you don't find a source backing it up I think we should pull it out and not have age affect the calculation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll wait to pull it out until after i've tested with a garmin, so if any formula changes need to be made, I'll do it all in one swoop :)

Copy link
Collaborator

@thyttan thyttan Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of changes are you thinking about doing if the Garmin and this app show very different values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many ways of getting calories, through RHR, or through steps and hr alone, so I'll look into those and see which performs better

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting that by better you mean 'more like Garmin'. Which is probably an ok comparison given they have more resources to get it right. But at the same time they don't represent some perfect ground truth (which we'll never have of course).

@thyttan
Copy link
Collaborator

thyttan commented Mar 7, 2026

It seems the calories goal drop down notification will persist indefinitely if not dismissed. I think it would be good if it timed out after a minute or two, or even just 10 seconds. What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants