Fitbit has been integrated into Home-Assistant as an official integration since 2016, but it has despite many requests always just had the option to add only one single user. For the official integration that is still the case, but not too long ago TheTroubleshooter from the Home-Assistant forum published a solution to the problem on GitHub.
This opens up a lot of possibilities, and one thing I have wanted to make is a “Fitbit scoreboard” for me and my wife. Perhaps this can be an extra motivation to become even more active, competition is at least always fun.
After some days of work, this is what I come up with:
In this blog post I’m going to publish the code, and and try to explain how this works.
In my code longer down in this article fitbit1=UserA and fitbit2=UserB.
There is also possible to add more people to the scoreboard, but requires a little more complex logic and more code. That is however not needed in this household, and because of that not something I have done.
How to integrate multiple Fitbit-accounts in Home-Assistant
It’s not very complicated, just follow the steps in the guide here:
TheTroubleshooter / Home-Assistant / Fitbit – Add Multiple Accounts
I will also add some extra tips to the above guide:
- Home-Assistant must be accessible on the internet trough HTTPS to pass the Fitbit API authentication.
- To download the Fitbit1,2,3.. folders from GitHub use this site: https://minhaskamal.github.io/DownGit/
- If you don’t see the notification to setup the Fitbit sensor in the Notifications section of the menu as described, you may need to add the external URL in Home-Assistant. This can be found under Configuration -> General (More info at https://www.home-assistant.io/docs/configuration/basic/)
When all steps are complete you should have all accounts integrated.
The Lovelace scoreboard and metrics
On the left I have a scoreboard and other stats. On the right side I have some graphs for steps and calorie consumption.
The Scoreboard is made by a combination of HTML and Jinja2 templates. The Scoreboard will also show a star on who ever has the best result of today for each category.
These stats will be in real-time as fast as the Fitbit API and trackers report data (quite quickly and stably in my experience).
The graphs on the right side is made with the custom Plotly Graph Card card (Available in HACS)
Some of the data shown is not part of the Fitbit API, these are calculated based on different sensor-metrics
Here are some examples:
Active calories in percent of BMR (Basal Metabolic Rate):sensor.fitbitX_activity_calorie
s / sensor.fitbitX_calories * 100
Lean body mass in KG:sensor.fitbitX_weight - sensor.fitbitX_body_fat * sensor.fitbitX_weight / 100
Lean body mass in percent:100 - sensor.fitbitX_body_fat
Body fat in KG:sensor.fitbitX_body_fat * sensor.fitbitX_weight / 100
The Lovelace graphs
The Steps graph:
type: custom:plotly-graph
title: Steps
entities:
- entity: sensor.fitbit1_steps
unit_of_measurement: Steps
name: UserA
line:
color: blue
shape: line
width: 2
- entity: sensor.fitbit2_steps
unit_of_measurement: Steps
name: UserB
line:
color: magenta
shape: line
width: 2
hours_to_show: 24
refresh_interval: 10
layout:
xaxis:
rangeselector:
'y': 1.2
buttons:
- count: 12
step: hour
- count: 24
step: hour
- count: 3
step: day
- count: 7
step: day
The Energy consumption graph:
type: custom:plotly-graph
title: _ Energy consumption
entities:
- entity: sensor.fitbit1_calories
unit_of_measurement: kcal
name: UserA
line:
color: blue
shape: line
width: 2
- entity: sensor.fitbit2_calories
unit_of_measurement: kcal
name: UserB
line:
color: magenta
shape: line
width: 2
- entity: sensor.fitbit1_calories_bmr
unit_of_measurement: kcal
name: UserA BMR
line:
color: lightblue
shape: line
width: 0.5
- entity: sensor.fitbit2_calories_bmr
unit_of_measurement: kcal
name: UserB BMR
line:
color: violet
shape: line
width: 0.5
hours_to_show: 24
refresh_interval: 10
layout:
xaxis:
rangeselector:
'y': 1.2
buttons:
- count: 12
step: hour
- count: 24
step: hour
- count: 3
step: day
- count: 7
step: day
The Scoreboard and metrics
I know the code below looks super-messy (and it is inevitable when using Jinja2 in a markdown card), but it will look better when imported.
Select ADD CARD -> Manual -> Paste the code -> SHOW CODE EDITOR and it will look as clean like this:
The Code:
type: markdown
content: "<table border=\"1\" width=100%\">\n\t<caption><h1><ha-icon icon=\"mdi:run-fast\"></ha-icon> Scoreboard</h1></caption>\n\t<thead>\n\t<tr>\n\t\t<th >Category</th>\n\t\t<th>UserA</th>\n\t\t<th>UserB</th>\n\t</tr>\n\t</thead>\n\t<tbody align=\"center\">\n\t<tr>\n\t\t<td>Steps</td>\n\t\t<td>{% if ( states.sensor.fitbit1_steps.state | int ) > ( states.sensor.fitbit2_steps.state | int ) %}<font color=\"gold\"><ha-icon icon=\"mdi:star\"></ha-icon></font>{% endif %}</br>{{ states(\"sensor.fitbit1_steps\") }} steps</td>\n\t\t<td>{% if ( states.sensor.fitbit1_steps.state | int ) < ( states.sensor.fitbit2_steps.state | int ) %}<font color=\"gold\"><ha-icon icon=\"mdi:star\"></ha-icon></font>{% endif %}</br>{{ states(\"sensor.fitbit2_steps\") }} steps</td>\n\t</tr>\n\t<tr>\n\t\t<td>Elevation.</td>\n\t\t<td>{% if ( states.sensor.fitbit1_elevation.state | int ) > ( states.sensor.fitbit2_elevation.state | int ) %}<font color=\"gold\"><ha-icon icon=\"mdi:star\"></ha-icon></font>{% endif %}\n</br>{{ states(\"sensor.fitbit1_elevation\") }} meters</td>\n\t\t<td>{% if ( states.sensor.fitbit1_elevation.state | int ) < ( states.sensor.fitbit2_elevation.state | int ) %}<font color=\"gold\"><ha-icon icon=\"mdi:star\"></ha-icon></font>{% endif %}\n</br>{{ states(\"sensor.fitbit2_elevation\") }} meters</td>\n\t</tr>\n\t<tr>\n\t\t<td>Distance</td>\n\t\t<td>{% if ( states.sensor.fitbit1_distance.state | float ) > ( states.sensor.fitbit2_distance.state | float ) %}<font color=\"gold\"><ha-icon icon=\"mdi:star\"></ha-icon></font>{% endif %}\n</br>{{ states(\"sensor.fitbit1_distance\") }} km</td>\n\t\t<td>{% if ( states.sensor.fitbit1_distance.state | float ) < ( states.sensor.fitbit2_distance.state | float ) %}<font color=\"gold\"><ha-icon icon=\"mdi:star\"></ha-icon></font>{% endif %}\n</br>{{ states(\"sensor.fitbit2_distance\") }} km</td>\n\t</tr>\n\t<tr>\n\t\t<td>Activity Calories</td>\n\t\t<td>{% if (( states.sensor.fitbit1_activity_calories.state | float ) / (states.sensor.fitbit1_calories.state | float ) * 100) > (( states.sensor.fitbit2_activity_calories.state | float ) / (states.sensor.fitbit2_calories.state | float ) * 100) %}<font color=\"gold\"><ha-icon icon=\"mdi:star\"></ha-icon></font>{% endif %}\n{% if (( states.sensor.fitbit1_activity_calories.state | float ) / (states.sensor.fitbit1_calories.state | float ) * 100) %}</br>{{ (( states.sensor.fitbit1_activity_calories.state | float ) / (states.sensor.fitbit1_calories.state | float ) * 100) | round(0) }}% of BMR ({{states.sensor.fitbit1_activity_calories.state}} kcal){% endif %}</td>\n\t\t<td>{% if (( states.sensor.fitbit1_activity_calories.state | float ) / (states.sensor.fitbit1_calories.state | float ) * 100) < (( states.sensor.fitbit2_activity_calories.state | float ) / (states.sensor.fitbit2_calories.state | float ) * 100) %}<font color=\"gold\"><ha-icon icon=\"mdi:star\"></ha-icon></font>{% endif %}\n{% if (( states.sensor.fitbit2_activity_calories.state | float ) / (states.sensor.fitbit2_calories.state | float ) * 100) %}</br>{{ (( states.sensor.fitbit2_activity_calories.state | float ) / (states.sensor.fitbit2_calories.state | float ) * 100) | round(0) }}% of BMR ({{states.sensor.fitbit2_activity_calories.state}} kcal){% endif %}</td>\n\t</tr>\n\t<tr>\n\t\t<td>Time Asleep</td>\n\t\t<td>{% if ( states.sensor.fitbit1_sleep_minutes_asleep.state | float ) > ( states.sensor.fitbit2_sleep_minutes_asleep.state | float ) %}<font color=\"gold\"><ha-icon icon=\"mdi:star\"></ha-icon></font>{% endif %}\n</br>{{ states(\"sensor.fitbit1_sleep_minutes_asleep\") }} minutes</td>\n\t\t<td>{% if ( states.sensor.fitbit1_sleep_minutes_asleep.state | float ) < ( states.sensor.fitbit2_sleep_minutes_asleep.state | float ) %}<font color=\"gold\"><ha-icon icon=\"mdi:star\"></ha-icon></font>{% endif %}\n</br>{{ states(\"sensor.fitbit2_sleep_minutes_asleep\") }} minutes</td>\n\t</tr>\n</tbody>\n</table>\n\n<table border=\"1\" width=100%\">\n\t<caption><h1><ha-icon icon=\"mdi:human-handsup\"></ha-icon> Body composition</h1></caption>\n\t<thead>\n\t<tr>\n\t\t<th ></th>\n\t\t<th>UserA</th>\n\t\t<th>UserB</th>\n\t</tr>\n\t</thead>\n\t<tbody align=\"center\">\n\t<tr>\n\t\t<td>Body weight</td>\n\t\t<td>{{ states(\"sensor.fitbit1_weight\") }}KG</td>\n\t\t<td>{{ states(\"sensor.fitbit2_weight\") }}KG</td>\n\t</tr>\n\t<tr>\n\t\t<td>Body mass index</td>\n\t\t<td>{{ states(\"sensor.fitbit1_bmi\") }}</td>\n\t\t<td>{{ states(\"sensor.fitbit2_bmi\") }}</td>\n\t</tr>\n\t<tr>\n\t\t<td>Lean body mass</td>\n\t\t<td>{{ ( (states.sensor.fitbit1_weight.state | float ) - (states.sensor.fitbit1_body_fat.state | float ) * (states.sensor.fitbit1_weight.state | float ) / 100 ) | round(1) }} KG / {{ ( 100 - (states.sensor.fitbit1_body_fat.state | float ) ) | round(1) }}%</td>\n\t\t<td>{{ ( (states.sensor.fitbit2_weight.state | float ) - (states.sensor.fitbit2_body_fat.state | float ) * (states.sensor.fitbit2_weight.state | float ) / 100 ) | round(1) }} KG / {{ ( 100 - (states.sensor.fitbit2_body_fat.state | float ) ) | round(1) }}%</td>\n\t</tr>\n\t<tr>\n\t\t<td>Body fat</td>\n\t\t<td>{{ ( (states.sensor.fitbit1_body_fat.state | float ) * (states.sensor.fitbit1_weight.state | float ) / 100 ) | round(1) }} KG / {{ states(\"sensor.fitbit1_body_fat\") }}%</td>\n\t\t<td>{{ ( (states.sensor.fitbit2_body_fat.state | float ) * (states.sensor.fitbit2_weight.state | float ) / 100 ) | round(1) }} KG / {{ states(\"sensor.fitbit2_body_fat\") }}%</td>\n\t</tr>\n\n</tbody>\n</table>\n\n<table border=\"1\" width=100%\">\n\t<caption><h1><ha-icon icon=\"mdi:heart-pulse \"></ha-icon> Heart health</h1></caption>\n\t<thead>\n\t<tr>\n\t\t<th ></th>\n\t\t<th>UserA</th>\n\t\t<th>UserB</th>\n\t</tr>\n\t</thead>\n\t<tbody align=\"center\">\n\t<tr>\n\t\t<td>Resting Heart Rate</td>\n\t\t<td>{{ states(\"sensor.fitbit1_resting_heart_rate\") }} bpm</td>\n\t\t<td>{{ states(\"sensor.fitbit2_resting_heart_rate\") }} bpm</td>\n\t</tr>\n\t<tr>\n\t\t<td>Time sedentary</td>\n\t\t<td>{{ states(\"sensor.fitbit1_minutes_sedentary\") }} minutes</td>\n\t\t<td>{{ states(\"sensor.fitbit2_minutes_sedentary\") }} minutes</td>\n\t</tr>\n\t<tr>\n\t\t<td>Time lightly active</td>\n\t\t<td>{{ states(\"sensor.fitbit1_minutes_lightly_active\") }} minutes</td>\n\t\t<td>{{ states(\"sensor.fitbit2_minutes_lightly_active\") }} minutes</td>\n\t</tr>\n\t<tr>\n\t\t<td>Time fairly active</td>\n\t\t<td>{{ states(\"sensor.fitbit1_minutes_fairly_active\") }} minutes</td>\n\t\t<td>{{ states(\"sensor.fitbit2_minutes_fairly_active\") }} minutes</td>\n\t</tr>\n\t<tr>\n\t\t<td>Time very active</td>\n\t\t<td>{{ states(\"sensor.fitbit1_minutes_very_active\") }} minutes</td>\n\t\t<td>{{ states(\"sensor.fitbit2_minutes_very_active\") }} minutes</td>\n\t</tr>\n</tbody>\n</table>"
I am very happy with the solution and hope this can be an inspiration to others.