Skip to content

Commit 5e8127d

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 5e8127d

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed
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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,16 @@ class LibreDevice(freestyle.FreeStyleHidDevice):
203203

204204
_MODEL_NAME: str
205205

206+
@staticmethod
207+
def _normalize_history_record(record: Sequence[str]) -> Sequence[str]:
208+
"""Normalize a history record to the base column layout."""
209+
return record
210+
211+
@staticmethod
212+
def _normalize_result_record(record: Sequence[str]) -> Sequence[str]:
213+
"""Normalize a result record to the base column layout."""
214+
return record
215+
206216
def get_meter_info(self) -> common.MeterInfo:
207217
"""Return the device information in structured form."""
208218
return common.MeterInfo(
@@ -231,6 +241,7 @@ def get_readings(self) -> Generator[common.AnyReading, None, None]:
231241
# First of all get the usually longer list of sensor readings, and
232242
# convert them to Readings objects.
233243
for record in self._session.query_multirecord(b"$history?"):
244+
record = self._normalize_history_record(record)
234245
parsed_record = _parse_record(record, _HISTORY_ENTRY_MAP)
235246

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

0 commit comments

Comments
 (0)