Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Freestyle Libre 3 #131

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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