Getting food data from Voedingscentrum in Home Assistant
I use an app to track my food intake and I wanted to get the data from that app into Home Assistant. The app is made by a publicly funded organization in the Netherlands called Voedingscentrum. I was hoping they would have an API I could use, so I emailed them.
Within a day I had a response. Unfortunately, they don't have an API available for this. So far for that plan. They do have a website where you can log in and see the same data that is in the app. So I thought I'd try scraping that. While I was looking into that, I noticed they had an XML download on the site. That made it a lot easier for me.
I made a simple python script that uses PlayWright to log in to the website, navigate to the page where the XML can be downloaded and then download the XML file. I then parse the XML file and extract the data I need. Finally, I use the Home Assistant API to send the data to Home Assistant. I have this script running every few hours using a cron job.
#!/bin/sh
""":"
# Find the directory of this script and run the local venv python
exec "$(dirname "$0")"/venv/bin/python "$0" "$@"
" """
import os
from playwright.sync_api import sync_playwright
import json
import xml.etree.ElementTree as ET
from collections import defaultdict
from dotenv import load_dotenv
import requests
load_dotenv()
HA_TOKEN = os.getenv('HA_TOKEN')
HA_URL = os.getenv('HA_URL')
with sync_playwright() as p:
browser = p.firefox.launch(headless=True)
page = browser.new_page()
page.goto("https://mijn.voedingscentrum.nl/nl/login/")
page.get_by_label("E-mailadres").fill(os.getenv('VOEDINGSCENTRUM_USERNAME'))
page.get_by_label("Wachtwoord").fill(os.getenv('VOEDINGSCENTRUM_PASSWORD'))
page.locator("#ctl00_ctl00_main_ctl00_lbLogin").click()
page.wait_for_load_state("networkidle")
page.goto("https://mijn.voedingscentrum.nl/nl/dashboard/eetmeter/overzicht/")
with page.expect_download() as download_info:
page.locator("#ctl00_ctl00_main_ctl00_lbXML").click()
download = download_info.value
temp_path = download.path()
with open(temp_path, 'r', encoding='utf-8') as f:
xml_content = f.read()
browser.close()
root = ET.fromstring(xml_content)
totals = defaultdict(float)
units = {}
for consumptie in root.findall('Consumptie'):
nutrienten = consumptie.find('Nutrienten')
if nutrienten is not None:
for nutrient in nutrienten:
name = nutrient.tag
value = float(nutrient.text or 0)
totals[name] = round(totals[name] + value, 2)
if totals and HA_TOKEN:
headers = {
"Authorization": f"Bearer {HA_TOKEN}",
"Content-Type": "application/json",
}
try:
response = requests.post(HA_URL, headers=headers, json=dict(totals), timeout=10)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"Error sending to Home Assistant: {e}")
To make the script work, you need to have Python installed, create a virtual environment in the same directory as the script and install the required packages:
python -m venv venv
source venv/bin/activate
pip install playwright python-dotenv requests
playwright install firefox
Then create a .env file in the same directory as the script with the following content:
VOEDINGSCENTRUM_USERNAME=your_voedingscentrum_username
VOEDINGSCENTRUM_PASSWORD=your_voedingscentrum_password
HA_TOKEN=your_home_assistant_token
HA_URL=your_home_assistant_url/api/states/sensor.food_data
HA_TOKEN is a long-lived access token that you can create in Home Assistant. HA_URL is the URL of your Home Assistant instance followed by /api/events/update_voedingscentrum.
Then in your Home Assistant configuration, you can create a sensor to receive the data:
template:
- trigger:
- platform: event
event_type: "update_voedingscentrum"
sensor:
# --- Energy & Macros ---
- name: "Eetmeter Energie"
unique_id: eetmeter_energie
state: "{{ trigger.event.data.Energie | float(0) }}"
unit_of_measurement: "kcal"
device_class: energy
state_class: measurement
- name: "Eetmeter Vet"
unique_id: eetmeter_vet
state: "{{ trigger.event.data.Vet | float(0) }}"
unit_of_measurement: "g"
state_class: measurement
- name: "Eetmeter Verzadigd Vet"
unique_id: eetmeter_verzadigd_vet
state: "{{ trigger.event.data.VerzadigdVet | float(0) }}"
unit_of_measurement: "g"
state_class: measurement
- name: "Eetmeter Koolhydraten"
unique_id: eetmeter_koolhydraten
state: "{{ trigger.event.data.Koolhydraten | float(0) }}"
unit_of_measurement: "g"
state_class: measurement
- name: "Eetmeter Suikers"
unique_id: eetmeter_suikers
state: "{{ trigger.event.data.Suikers | float(0) }}"
unit_of_measurement: "g"
state_class: measurement
- name: "Eetmeter Eiwit"
unique_id: eetmeter_eiwit
state: "{{ trigger.event.data.Eiwit | float(0) }}"
unit_of_measurement: "g"
state_class: measurement
- name: "Eetmeter Eiwit Plantaardig"
unique_id: eetmeter_eiwit_plantaardig
state: "{{ trigger.event.data.EiwitPlantaardig | float(0) }}"
unit_of_measurement: "g"
state_class: measurement
- name: "Eetmeter Vezels"
unique_id: eetmeter_vezels
state: "{{ trigger.event.data.Vezels | float(0) }}"
unit_of_measurement: "g"
state_class: measurement
- name: "Eetmeter Alcohol"
unique_id: eetmeter_alcohol
state: "{{ trigger.event.data.Alcohol | float(0) }}"
unit_of_measurement: "g"
state_class: measurement
# --- Hydration & Minerals ---
- name: "Eetmeter Water"
unique_id: eetmeter_water
state: "{{ trigger.event.data.Water | float(0) }}"
unit_of_measurement: "g"
state_class: measurement
- name: "Eetmeter Zout"
unique_id: eetmeter_zout
state: "{{ trigger.event.data.Zout | float(0) }}"
unit_of_measurement: "g"
state_class: measurement
- name: "Eetmeter Natrium"
unique_id: eetmeter_natrium
state: "{{ trigger.event.data.Natrium | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Kalium"
unique_id: eetmeter_kalium
state: "{{ trigger.event.data.Kalium | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Calcium"
unique_id: eetmeter_calcium
state: "{{ trigger.event.data.Calcium | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Magnesium"
unique_id: eetmeter_magnesium
state: "{{ trigger.event.data.Magnesium | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter IJzer"
unique_id: eetmeter_ijzer
state: "{{ trigger.event.data.IJzer | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Fosfor"
unique_id: eetmeter_fosfor
state: "{{ trigger.event.data.Fosfor | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Zink"
unique_id: eetmeter_zink
state: "{{ trigger.event.data.Zink | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Selenium"
unique_id: eetmeter_selenium
state: "{{ trigger.event.data.Selenium | float(0) }}"
unit_of_measurement: "µg"
state_class: measurement
- name: "Eetmeter Jodium"
unique_id: eetmeter_jodium
state: "{{ trigger.event.data.Jodium | float(0) }}"
unit_of_measurement: "µg"
state_class: measurement
# --- Vitamins ---
- name: "Eetmeter Vitamine A"
unique_id: eetmeter_vitamine_a
state: "{{ trigger.event.data.VitamineA | float(0) }}"
unit_of_measurement: "µg"
state_class: measurement
- name: "Eetmeter Vitamine D"
unique_id: eetmeter_vitamine_d
state: "{{ trigger.event.data.VitamineD | float(0) }}"
unit_of_measurement: "µg"
state_class: measurement
- name: "Eetmeter Vitamine E"
unique_id: eetmeter_vitamine_e
state: "{{ trigger.event.data.VitamineE | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Vitamine C"
unique_id: eetmeter_vitamine_c
state: "{{ trigger.event.data.VitamineC | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Vitamine B1"
unique_id: eetmeter_vitamine_b1
state: "{{ trigger.event.data.VitamineB1 | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Vitamine B2"
unique_id: eetmeter_vitamine_b2
state: "{{ trigger.event.data.VitamineB2 | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Vitamine B6"
unique_id: eetmeter_vitamine_b6
state: "{{ trigger.event.data.VitamineB6 | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement
- name: "Eetmeter Vitamine B12"
unique_id: eetmeter_vitamine_b12
state: "{{ trigger.event.data.VitamineB12 | float(0) }}"
unit_of_measurement: "µg"
state_class: measurement
- name: "Eetmeter Foliumzuur"
unique_id: eetmeter_foliumzuur
state: "{{ trigger.event.data.Foliumzuur | float(0) }}"
unit_of_measurement: "µg"
state_class: measurement
- name: "Eetmeter Nicotinezuur"
unique_id: eetmeter_nicotinezuur
state: "{{ trigger.event.data.Nicotinezuur | float(0) }}"
unit_of_measurement: "mg"
state_class: measurement