Skip to content

Commit 5121cc6

Browse files
committed
Add support for Freestyle Libre 3
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.
1 parent 9b87a5b commit 5121cc6

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

glucometerutils/drivers/fslibre3.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# SPDX-FileCopyrightText: © 2023 The glucometerutils Authors
4+
# SPDX-License-Identifier: MIT
5+
"""Driver for FreeStyle Libre 3 devices.
6+
7+
Supported features:
8+
The same as the fslibre driver.
9+
10+
Expected device path: /dev/hidraw9 or similar HID device. Optional when using
11+
HIDAPI.
12+
13+
This driver is a shim on top of the fslibre driver, forcing encryption to be
14+
enabled for the session and normalizing the returned records.
15+
16+
Further information on the device protocol can be found at
17+
18+
https://protocols.glucometers.tech/abbott/freestyle-libre
19+
https://protocols.glucometers.tech/abbott/freestyle-libre-2
20+
21+
"""
22+
23+
from collections.abc import Sequence
24+
from typing import Optional
25+
26+
from glucometerutils.support import freestyle_libre
27+
28+
29+
class Device(freestyle_libre.LibreDevice):
30+
_MODEL_NAME = "FreeStyle Libre 3"
31+
32+
def __init__(self, device_path: Optional[str]) -> None:
33+
super().__init__(0x3960, device_path, encoding="utf-8", encrypted=True)
34+
35+
@staticmethod
36+
def _normalize_history_record(record: Sequence[str]) -> Sequence[str]:
37+
"""Overridden function as one of the unknown columns is missing."""
38+
record.insert(10, "0")
39+
return record
40+
41+
@staticmethod
42+
def _normalize_result_record(record: Sequence[str]) -> Sequence[str]:
43+
"""Overridden function as error values and custom comments are missing."""
44+
record.insert(19, "0")
45+
record.insert(28, 0)
46+
if len(record) > 29:
47+
record = record[:29] + 6*["\"\""] + record[29:]
48+
return record

glucometerutils/support/freestyle_libre.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ def _parse_arresult(record: Sequence[str]) -> Optional[common.AnyReading]:
119119
else:
120120
return None
121121

122+
print(parsed_record)
122123
# Check right away if we have rapid insulin
123124
if parsed_record["rapid-acting-flag"]:
124125
parsed_record.update(_parse_record(record, _ARRESULT_RAPID_INSULIN_ENTRY_MAP))
@@ -203,6 +204,16 @@ class LibreDevice(freestyle.FreeStyleHidDevice):
203204

204205
_MODEL_NAME: str
205206

207+
@staticmethod
208+
def _normalize_history_record(record: Sequence[str]) -> Sequence[str]:
209+
"""Normalize a history record to the base column layout."""
210+
return record
211+
212+
@staticmethod
213+
def _normalize_result_record(record: Sequence[str]) -> Sequence[str]:
214+
"""Normalize a result record to the base column layout."""
215+
return record
216+
206217
def get_meter_info(self) -> common.MeterInfo:
207218
"""Return the device information in structured form."""
208219
return common.MeterInfo(
@@ -231,6 +242,7 @@ def get_readings(self) -> Generator[common.AnyReading, None, None]:
231242
# First of all get the usually longer list of sensor readings, and
232243
# convert them to Readings objects.
233244
for record in self._session.query_multirecord(b"$history?"):
245+
record = self._normalize_history_record(record)
234246
parsed_record = _parse_record(record, _HISTORY_ENTRY_MAP)
235247

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

0 commit comments

Comments
 (0)