Freestyle Libre Bloodsugar sensor based on Last Notification

Hello all,

My son has a Freestyle Libre2 bloodsugar sensor, which has no direct integration into HA AFAIK. He shares his values with me. When he scans, I receive a notification on my phone.
Via the HA mobile app, it is possible to catch this message via the Last Notification which looks like this:

I was able to make an automation, in combination with an input_number helper, where the value is extracted from the state when the post time changes and the following requirements are fulfilled:

  1. the state starts with the word “Glucosewaarde”
  2. attribute channel_id=default_channel
alias: "Get Freestyle BG value"
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.telefoon_bart_last_notification
    attribute: post_time
condition:
  - condition: state
    entity_id: sensor.telefoon_bart_last_notification
    attribute: channel_id
    state: default_channel
  - condition: template
    value_template: >-
      {{ states.sensor.telefoon_bart_last_notification.state.split(':')[0] == "Glucosewaarde" }}
action:
  - service: input_number.set_value
    data:
      value: >-
        {% set value = states.sensor.telefoon_bart_last_notification.state %} 
        {% set pattern = '\d+.?\d+' %} 
        {{ (value | regex_findall_index(pattern)) if value is search(pattern) else null }}
    target:
      entity_id: input_number.bg
mode: single

This is in principle not a safe or elegant method, as the input_number can be changed/manipulated manually.

I would like to convert it into a template sensor , however I am getting stuck with setting the trigger and validating the conditions. Is this possible at all, and if yes, can you help me further?

- sensor:
  - name: "Freestyle BG"
    icon: mdi-water
    unit_of_measurement: "mmol/l"
    state_class: measurement
    state: >-
      {% set value = states.sensor.telefoon_bart_last_notification.state %}
      {% set pattern = '\d+.?\d+' %}
      {{ (value | regex_findall_index(pattern)) if value is search(pattern) else null }}

Thanks for your suggestions!

Use a trigger-based template sensor.

template:
  - trigger:
      - platform: template
        value_template:  >
          {{ states( 'sensor.telefoon_bart_last_notification') is search('Glucosewaarde') }}
        variables:
          value: "{{ states( 'sensor.telefoon_bart_last_notification') }}"
    sensor:
      - name: "Freestyle BG"
        icon: mdi-water
        unit_of_measurement: "mmol/l"
        state_class: measurement
        state: >-
          {% set pattern = '\d+.?\d+' %}
          {{ value | regex_findall_index(pattern) }}

If you want, or need, a more specific trigger, you can incorporate the regex there as well:

{{ states('sensor.telefoon_bart_last_notification')
| regex_findall('Glucosewaarde: \d+.?\d+ mmol/l' ) 
| count | bool }}

Big thanks! Will try it out!

Difference with your example and my automation is that mine initiates on a change in post-date and then starts validating. Further, my son’s app also generates other messages over other channels, hence I would be validating the channel_id as well to make sure I have the right message.

I am curious to understand whether that will also trigger in case two consecutive measurements would produce the same value.

I will try them in parallel!

Unfortunately it does not seem to work as expected/hoped for. My automation does consistently show latest values, but the sensor does not show measurements. I think indeed that no new trigger is generated in the case that there are two consecutive messages that are the like, e.g. “Glucosewaarde: 5.7 mmol/l →” followed by “Glucosewaarde: 6.7 mmol/l →” does not result in a trigger.

Fiddling in the templates shows that:

  • The regex statement {{ states('sensor.telefoon_bart_last_notification') | regex_findall('Glucosewaarde: \d+.?\d+ mmol/l' ) | count | bool }} does result in a true
  • The regex of the sensor does extract the value correctly.

The complexity may be that you need to fire a trigger in a AND like manner when:

  • the Last Notification changes AND
  • the Last Notification fulfills the format rule.

However, I don’t know whether that is possible. A list of triggers is rather OR focused:

- trigger:
  - platform: state
    entity_id:
      - sensor.telefoon_bart_last_notification
    attribute: post_time
  - platform: template
        value_template:  >
          {{ states( 'sensor.telefoon_bart_last_notification') is search('Glucosewaarde') }}

In that sense, it would be handy if the template sensor could be expanded with conditions, comparable to an automation.

You are absolutely correct. I had not consider consecutive messages but I played around with it this morning… the following is working for me catching consecutive messages and skipping messages that don’t meet the regex criteria. The only caveat is that I’m testing using email-derived notifications since I can easily manipulate those YMMV :slight_smile: .

  - trigger:
      - platform: state
        entity_id:
          - sensor.telefoon_bart_last_notification
        not_to:
          - unknown
          - unavailable 
    sensor:
      - name: "Freestyle BG"
        icon: mdi-water
        unit_of_measurement: "mmol/l"
        state_class: measurement
        state: >-
          {% set current = this.state %}
          {% set value = trigger.to_state.state %}
          {% set pattern = '\d+.?\d+' %}
          {% if not (value | regex_findall('Glucosewaarde: '~pattern~' mmol/l') 
          | count | bool) %}{{ current | default('-1.0') }}
          {% else %}{{ value | regex_findall_index(pattern) }}{% endif %}

You could also include your channel criteria if desired

Template with channel criteria
{% set current = this.state %}
{% set value = trigger.to_state.state %}
{% set ch_id_bool = trigger.to_state.attributes.channel_id == 'default_channel' %}
{% set pattern = '\d+.?\d+' %}
{% if not ch_id_bool or not 
(value | regex_findall('Glucosewaarde: '~pattern~' mmol/l') | count | bool) %}
{{ current | default('-1.0') }}
{% else %}
{{ value | regex_findall_index(pattern) }}
{% endif %}
1 Like

Wow! Will try that out.

I do not understand how it actually skips the message. Especially I do not understand what {{ current | default('-1.0') }} is doing.

current stores the value of the sensor’s state before it is set/changed by the rest of the template. When a value comes in from the last_notification, it is rejected if that value doesn’t
fit the regex and freestyle_bg keeps the value from current.

You will likely never see the default value manifest, but I included it in case something goes wrong. You could use a non-numeric string instead of what I used, but a sensor with a unit of measurement expects a numeric value. IIRC, it would just go to “unknown” if it defaulted to a non-numeric string. With a negative number, you have a value that is both unique and is impossible IRL, so it can be used as a trigger for a warning notification.

Trying it out now! Impressive and resourceful skills you have!

Just for the context: this can contribute to my son’s health e.g. by sending him a reminder, after say 1 hour, once his levels tested too high. You are the hero of the day! :clap: :clap: :clap:

Really appreciated!

Thanks, for this post!
Finally, got my Freestyle libre3 data into my home assistant!

In addition to get “always” the actual glucose level in ha

  • I am using an old table that has been unused and forgotten
  • plugged it to power for always running
  • and using the link up app there
  • setting the alarm levels to the absolute max/mins, so it will continuously send alerts and updates to ha.
  • (I prefer to use this extern device because you have to shut down this annoying alarm noise for the freestyle libre 3 - which can’t be configured in playback-sound and volume anymore)
  • catch the alarm and parse out the glucose level as float and trend to use in ha

SO - here is my first screenshot (dont worry, my suggar is fine - the chart has just started and showing wrong numbers)

2 Likes

Hello all,
I am also a Freestyle Libre 3 user. and I would also love to be able to see blood glucose in HomeAssistant. The idea with a tablet and the FreeStyle Link app seems to be the best solution so far. However, it would be great if the developers could somehow make it so that an integration for this is already provided.

Can someone help me how to get this feature to the developers?

1 Like

Hi can you exactly show, how you setup your tablet und setup your ha so that i see the same as you in your screenshot. Very thanks.

1 Like

This Python code gets current Libre data from the Libre server directly. It would be great to have a custom component that does this.

LIBRELINKUP_USERNAME = 'email@address'
LIBRELINKUP_PASSWORD = 'password'
LIBRELINKUP_URL = 'https://api-au.libreview.io' or other country server
LIBRELINKUP_VERSION = '4.7.0'
LIBRELINKUP_PRODUCT = 'llu.ios'

import requests, sys, os, json, time
from datetime import datetime

LIBRELINKUP_HEADERS = {
    "version": LIBRELINKUP_VERSION,
    "product": LIBRELINKUP_PRODUCT,
}

LIBRELINKUP_TOKEN = None
script_dir = os.path.dirname(__file__)
auth_token_path = os.path.join(script_dir, 'librelinkup-authtoken.txt')
if os.path.isfile(auth_token_path):
    with open(auth_token_path) as json_file:
            auth = json.load(json_file)
            if auth['expires'] > time.time():
                LIBRELINKUP_TOKEN = auth['token']

if LIBRELINKUP_TOKEN is None:
    try:
        response = requests.post(f'{LIBRELINKUP_URL}/llu/auth/login',
            headers=LIBRELINKUP_HEADERS, json = {'email': LIBRELINKUP_USERNAME, 'password': LIBRELINKUP_PASSWORD})
        response.raise_for_status()
    except requests.exceptions.HTTPError as err:
        print("HTTP request failed")
        sys.exit(1)

    data = response.json()
    if data['status'] == 4:
        print("Log out of LibreLinkUp. Log in again and accept new terms of use.")
        sys.exit(1)
    if not 'authTicket' in data['data']:
        print("Authentication failed")
        sys.exit(1)

    with open(auth_token_path, 'w') as outfile:
        json.dump(data['data']['authTicket'], outfile)

    LIBRELINKUP_TOKEN = data['data']['authTicket']['token']

LIBRELINKUP_HEADERS['Authorization'] = 'Bearer ' + LIBRELINKUP_TOKEN

try:
    response = requests.get(f'{LIBRELINKUP_URL}/llu/connections', headers=LIBRELINKUP_HEADERS)
    print(response.text)
    response.raise_for_status()
except requests.exceptions.HTTPError as err:
    print("HTTP request failed")
    sys.exit(1)

connections = response.json()
if not 'data' in connections or len(connections['data']) < 1:
    print("Accept an invitation in the mobile app first.")
    sys.exit(1)

try:
    response = requests.get(f'{LIBRELINKUP_URL}/llu/connections/{connections["data"][0]["patientId"]}/graph', headers=LIBRELINKUP_HEADERS)
    response.raise_for_status()
except requests.exceptions.HTTPError as err:
    print("HTTP request failed")
    sys.exit(1)

data = response.json()
print(response.text)

Details of JSON Response are here: HTTP dump of Libre Link Up used in combination with FreeStyle Libre 3 ¡ GitHub

Hi, thank you for that. I am new in this selection of HA, can you give me an example where i import/write thie Code in HA so i get this Data to a Sensor in HA? I hope you understand me, what i mean.

Thank you for sharing this!

It would indeed be awesome if someone could turn this into an integration. I wish I could do it myself, but unfortunateIy lack the skills.

Please …

1 Like

This may help someone. This is a simple Automation using Android Last Notification as a trigger. When the Low Glucose Alarm in the Freestyle Libre3 app triggers, a notification is sent to a specific phone via home assistant mobile app.

alias: Low Sugar Alert
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.main_phone_last_notification 
    attribute: android.title
    to: Low Glucose Alarm
condition: []
action:
  - service: notify.mobile_app_dads_phone
    data:
      message: Low Sugar Alert
mode: single

main_phone has to have the official Freestyle Libre3 app installed with the Low Glucose Alarm setup. Automations can also be setup for the Urgent Low Glucose Alarm, High Glucose Alarm, Signal Loss Alarm.

Hi I Have the following configuration running :
3 docker images
For the connection with Freestyle i use a docker container :
timoschlueter/nightscout-librelink-up:latest
This sends the data to :
docker container : nightscout/cgm-remote-monitor:latest
Nightscout save the data in :
docker container : mongo:4.4.9
And home assistant has a Nightscout integration.
This will create an entity : sensor.blood_sugar
with measurrment mg/dL.
then i created a template sensor in the helper section with the following :

{{ (((states(‘sensor.blood_sugar’) | float * 0.0555) * 10) / 10) | round(1) }}

it was some work but it works,
i created a new account for the connection of the librelink up.

Hallo, I’m very interested in your solution. Would you please specify how you got this work together?
Do you have an docker-compose.yml? How did you get the data from one to another container etc.?
Thank you!

Hi Philipp, is there any chance you share your configuration with us, it would be very helpful and could be live changing for me and my sugar. Thank you so much.

This LibreLink integration works: GitHub - gillesvs/librelink: Librelink integration for Home Assistant
I now use the data via complications to display my current BGL and Trend on my Apple Watch.