Skip to content

Commit

Permalink
Add support for Freestyle Libre 3
Browse files Browse the repository at this point in the history
The Libre 3 reports results in a different way:
* The history reports are missing a column of unknown meaning.
* The reading reports are missing custom comments and error values.

The current workaround makes the Libre 3 results to look like the
results for earlier models and is for demonstration and documentation
purposes only. A better implementation would abstract the difference in
reporting format into the drivers for each device.
  • Loading branch information
isotherm committed Jan 14, 2025
1 parent 9b87a5b commit 5e8127d
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 0 deletions.
48 changes: 48 additions & 0 deletions glucometerutils/drivers/fslibre3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
#
# SPDX-FileCopyrightText: © 2023 The glucometerutils Authors
# SPDX-License-Identifier: MIT
"""Driver for FreeStyle Libre 3 devices.
Supported features:
The same as the fslibre driver.
Expected device path: /dev/hidraw9 or similar HID device. Optional when using
HIDAPI.
This driver is a shim on top of the fslibre driver, forcing encryption to be
enabled for the session and normalizing the returned records.
Further information on the device protocol can be found at
https://protocols.glucometers.tech/abbott/freestyle-libre
https://protocols.glucometers.tech/abbott/freestyle-libre-2
"""

from collections.abc import Sequence
from typing import Optional

from glucometerutils.support import freestyle_libre


class Device(freestyle_libre.LibreDevice):
_MODEL_NAME = "FreeStyle Libre 3"

def __init__(self, device_path: Optional[str]) -> None:
super().__init__(0x3960, device_path, encoding="utf-8", encrypted=True)

@staticmethod
def _normalize_history_record(record: Sequence[str]) -> Sequence[str]:
"""Overridden function as one of the unknown columns is missing."""
record.insert(10, "0")
return record

@staticmethod
def _normalize_result_record(record: Sequence[str]) -> Sequence[str]:
"""Overridden function as error values and custom comments are missing."""
record.insert(19, "0")
record.insert(28, 0)
if len(record) > 29:
record = record[:29] + 6*["\"\""] + record[29:]
return record
12 changes: 12 additions & 0 deletions glucometerutils/support/freestyle_libre.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ class LibreDevice(freestyle.FreeStyleHidDevice):

_MODEL_NAME: str

@staticmethod
def _normalize_history_record(record: Sequence[str]) -> Sequence[str]:
"""Normalize a history record to the base column layout."""
return record

@staticmethod
def _normalize_result_record(record: Sequence[str]) -> Sequence[str]:
"""Normalize a result record to the base column layout."""
return record

def get_meter_info(self) -> common.MeterInfo:
"""Return the device information in structured form."""
return common.MeterInfo(
Expand Down Expand Up @@ -231,6 +241,7 @@ def get_readings(self) -> Generator[common.AnyReading, None, None]:
# First of all get the usually longer list of sensor readings, and
# convert them to Readings objects.
for record in self._session.query_multirecord(b"$history?"):
record = self._normalize_history_record(record)
parsed_record = _parse_record(record, _HISTORY_ENTRY_MAP)

if not parsed_record or parsed_record["errors"] != 0:
Expand All @@ -248,6 +259,7 @@ def get_readings(self) -> Generator[common.AnyReading, None, None]:
# Then get the results of explicit scans and blood tests (and other
# events).
for record in self._session.query_multirecord(b"$arresult?"):
record = self._normalize_result_record(record)
logging.debug(f"Retrieved arresult: {record!r}")
reading = _parse_arresult(record)
if reading:
Expand Down

0 comments on commit 5e8127d

Please sign in to comment.