Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
475fcea
Move the binary script to glucometerutils/ and create a starting shim.
Flameeyes Dec 28, 2017
26021fd
Rename the shim to match the old name.
Flameeyes Dec 28, 2017
613b2d7
Rewrite setup.py to use setuptools.
Flameeyes Dec 28, 2017
b8aa129
Add driver dependencies to setup.py, and document how to install this.
Flameeyes Dec 28, 2017
fb49b60
deps: correct dependency for fsoptium.
Flameeyes Dec 28, 2017
09a5fe1
freestyle: replace the custom struct and bytemangling with construct.
Flameeyes Dec 28, 2017
7ceb792
Fix dependency name for absl-py.
Flameeyes Dec 28, 2017
95461d5
Fix error in meter info output, after conversion of Unit to enum.
Flameeyes Dec 29, 2017
1832178
Add more tests to cover the basic translation of objects to strings.
Flameeyes Dec 29, 2017
e9c3a20
Use pytest to run tests
yukiisbored Dec 30, 2017
04d69b6
Run test and build on Travis CI
yukiisbored Dec 30, 2017
2220764
freestyle: rename miscopied constant name.
Flameeyes Dec 30, 2017
fc7e86d
serial devices: disable xonxoff by default.
Flameeyes Dec 31, 2017
4ec3a8e
sdcodefree: rewrite using construct and improve readability.
Flameeyes Dec 31, 2017
0af0315
otultraeasy: rewrite using construct for parsing.
Flameeyes Jan 1, 2018
cfbf51d
otultraeasy: factor out the construct Timestamp implementation.
Flameeyes Jan 1, 2018
7f6a3ee
otverio2015: rewrite using construct.
Flameeyes Jan 1, 2018
44bc1fa
otultraeasy: wrap around construct exceptions to MalformedCommand.
Flameeyes Jan 1, 2018
9032396
otultraeasy: rename _ZERO_LOG_REQUEST to _MEMORY_ERASE_REQUEST.
Flameeyes Jan 1, 2018
c3d89aa
otultraeasy: merge _send_request and _read_response and match otverio…
Flameeyes Jan 1, 2018
efd4264
otverio2015: reorder functions to match otultraeasy.
Flameeyes Jan 1, 2018
60eef1b
test_lifescan: rename from test_otultraeasy, and cleanup.
Flameeyes Jan 1, 2018
bf2df60
tests: improve code quality by passing the linter.
Flameeyes Jan 1, 2018
3999c3e
lifescan_binary_protocol: create a new module to support LifeScan dri…
Flameeyes Jan 6, 2018
349b1c7
lifescan_binary_protocol: factor out glucose unit mappings.
Flameeyes Jan 6, 2018
57d7199
otverioiq: add totally untested driver.
Flameeyes Jan 6, 2018
1aa5295
otultraeasy: fix AttributeErrors after factoring out link_control str…
Flameeyes Jan 6, 2018
b5784bb
lifescan binary protocol: make the packet generator a function.
Flameeyes Jan 6, 2018
005fec0
otverioiq: update version and serial number requests.
Flameeyes Jan 6, 2018
34b8153
lifescan binary protocol: factor out _COMMAND_SUCCESS.
Flameeyes Jan 6, 2018
67a70d1
otverioiq: link-control is not used.
Flameeyes Jan 6, 2018
7075275
otverioiq: implement full parsing of the response structure.
Flameeyes Jan 7, 2018
6467d51
otverioiq: add to the list and to the dependency file.
Flameeyes Jan 7, 2018
11d8d56
otverioiq: fix typo.
Flameeyes Jan 7, 2018
6459cf5
otverioiq: fix up syntax.
Flameeyes Jan 7, 2018
7752b59
otverioiq: fix up a couple of syntax errors, and update baud rate.
Flameeyes Jan 7, 2018
058ddbf
otverioiq: fix definition of the reading response, fix get_reading_co…
Flameeyes Jan 7, 2018
d176a98
otverioiq: fix up set_datetime method.
Flameeyes Jan 7, 2018
eeb982f
otverioiq: zero log is tested, it works.
Flameeyes Jan 7, 2018
dc8c15c
otverioiq: remove untested marking and list supported features.
Flameeyes Jan 7, 2018
f70de2d
Until Issue #38 is fixed, expect construct 2.8, and not 2.9.
Flameeyes Feb 11, 2018
682fd34
construct: the construct API is significantly unstable, fix to 2.8.22…
Flameeyes Feb 11, 2018
4819131
construct-code is (almost) uptodate
arekbulski Feb 16, 2018
9d3e0ee
construct code fixed improper embedding
arekbulski Feb 16, 2018
38d8d3a
corected timestamp comment
arekbulski Feb 17, 2018
5f8a6fc
SymmetricMapping was renamed to Mapping
arekbulski Feb 18, 2018
8287a1b
CString supports UTF-16/32-LE/BE
arekbulski Mar 7, 2018
8fe51bc
Update sdcodefree.py
arekbulski Mar 7, 2018
84aad72
Update lifescan_binary_protocol.py
arekbulski Mar 7, 2018
8cc54e3
fsoptium: add debug logging when sending commands.
Flameeyes Mar 17, 2018
45f68d0
freestyle support: add debug logging of commands sent and received.
Flameeyes Mar 17, 2018
83466bf
Add double quotes according to @arvchristos suggestion on 'Example Us…
naokazuterada Apr 14, 2018
eee15ba
Fix for non-integer errors
BlueNalgene May 22, 2018
7b000e0
change 999 to inf
BlueNalgene May 22, 2018
fbeef8a
fix invalid self-reference
Jul 8, 2018
f956fee
Fix sdcodefree driver, the same as the lifescan changes.
Flameeyes Jul 24, 2018
75d8f07
test-requirements: add some minimum version specifications.
Flameeyes Jul 24, 2018
8b76658
Add files via upload
garberw Nov 15, 2018
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
/MANIFEST
/dist/
__pycache__/
.cache
build
*.egg-info/
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: python

python:
- 3.4
- 3.5
- 3.6

install:
- pip install -r test-requirements.txt

script:
- py.test
- python setup.py bdist_wheel
- pip install ./dist/glucometerutils-*.whl
46 changes: 31 additions & 15 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,41 @@ follows:
* `datetime` reads or updates the date and time of the device clock.
* `zero` deletes all the recorded readings (only implemented for few devices).

## Example Usage

Most of the drivers require optional dependencies, and those are listed in the
table below. If you do not want to install the dependencies manually, you should
be able to set this up using `virtualenv` and `pip`:

```shell
$ python3 -m venv $(pwd)/glucometerutils-venv
$ . glucometerutils-venv/bin/activate
(glucometerutils-venv) $ DRIVER=myglucometer-driver # see table below
(glucometerutils-venv) $ pip install "git+https://github.com/Flameeyes/glucometerutils.git#egg=project[${DRIVER}]"
(glucometerutils-venv) $ glucometer --driver ${DRIVER} help
```

## Supported devices

Please see the following table for the driver for each device that is known and
supported.

| Manufacturer | Model Name | Driver | Dependencies |
| --- | --- | --- | --- |
| LifeScan | OneTouch Ultra 2 | `otultra2` | [pyserial] |
| LifeScan | OneTouch Ultra Easy | `otultraeasy` | [pyserial] |
| LifeScan | OneTouch Ultra Mini | `otultraeasy` | [pyserial] |
| LifeScan | OneTouch Verio (USB) | `otverio2015` | [python-scsi] |
| LifeScan | OneTouch Select Plus | `otverio2015` | [python-scsi] |
| Abbott | FreeStyle InsuLinx† | `fsinsulinx` | [hidapi]‡ |
| Abbott | FreeStyle Libre | `fslibre` | [hidapi]‡ |
| Abbott | FreeStyle Optium | `fsoptium` | [pyserial] |
| Abbott | FreeStyle Precision Neo | `fsprecisionneo` | [hidapi]‡ |
| Abbott | FreeStyle Optium Neo | `fsprecisionneo` | [hidapi]‡ |
| Abbott | FreeStyle Optium Neo H | `fsprecisionneo` | [hidapi]‡ |
| Roche | Accu-Chek Mobile | `accuchek_reports` | |
| SD Biosensor | SD CodeFree | `sdcodefree` | [pyserial] |
| Manufacturer | Model Name | Driver | Dependencies |
| --- | --- | --- | --- |
| LifeScan | OneTouch Ultra 2 | `otultra2` | [pyserial] |
| LifeScan | OneTouch Ultra Easy | `otultraeasy` | [construct] [pyserial] |
| LifeScan | OneTouch Ultra Mini | `otultraeasy` | [construct] [pyserial] |
| LifeScan | OneTouch Verio IQ | `otverioiq` | [construct] [pyserial] |
| LifeScan | OneTouch Verio (USB) | `otverio2015` | [construct] [python-scsi] |
| LifeScan | OneTouch Select Plus | `otverio2015` | [construct] [python-scsi] |
| Abbott | FreeStyle InsuLinx† | `fsinsulinx` | [construct] [hidapi]‡ |
| Abbott | FreeStyle Libre | `fslibre` | [construct] [hidapi]‡ |
| Abbott | FreeStyle Optium | `fsoptium` | [pyserial] |
| Abbott | FreeStyle Precision Neo | `fsprecisionneo` | [construct] [hidapi]‡ |
| Abbott | FreeStyle Optium Neo | `fsprecisionneo` | [construct] [hidapi]‡ |
| Abbott | FreeStyle Optium Neo H | `fsprecisionneo` | [construct] [hidapi]‡ |
| Roche | Accu-Chek Mobile | `accuchek_reports` | |
| SD Biosensor | SD CodeFree | `sdcodefree` | [construct] [pyserial] |

† Untested.
‡ Optional dependency on Linux; required on other operating systems.
Expand All @@ -46,6 +61,7 @@ If you have knowledge of a protocol of a glucometer you would have supported,
please provide a reference, possibly by writing a specification and contribute
it to https://github.com/Flameeyes/glucometer-protocols/.

[construct]: https://construct.readthedocs.io/en/latest/
[pyserial]: https://pythonhosted.org/pyserial/
[python-scsi]: https://github.com/rosjat/python-scsi
[hidapi]: https://pypi.python.org/pypi/hidapi
Expand Down
149 changes: 3 additions & 146 deletions glucometer.py
Original file line number Diff line number Diff line change
@@ -1,150 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Utility to manage glucometers' data."""
# -*- python -*-

__author__ = 'Diego Elio Pettenò'
__email__ = 'flameeyes@flameeyes.eu'
__copyright__ = 'Copyright © 2013-2017, Diego Elio Pettenò'
__license__ = 'MIT'

import argparse
import importlib
import inspect
import logging
import sys

from glucometerutils import common
from glucometerutils import exceptions

def main():
if sys.version_info < (3, 4):
raise Exception(
'Unsupported Python version, please use at least Python 3.4')

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="action")

parser.add_argument(
'--driver', action='store', required=True,
help='Select the driver to use for connecting to the glucometer.')
parser.add_argument(
'--device', action='store', required=False,
help=('Select the path to the glucometer device. Some devices require this '
'argument, others will try autodetection.'))

parser.add_argument(
'--vlog', action='store', required=False, type=int,
help=('Python logging level. See the levels at '
'https://docs.python.org/3/library/logging.html#logging-levels'))

subparsers.add_parser(
'help', help=('Display a description of the driver, including supported '
'features and known quirks.'))
subparsers.add_parser(
'info', help='Display information about the meter.')
subparsers.add_parser(
'zero', help='Zero out the data log of the meter.')

parser_dump = subparsers.add_parser(
'dump', help='Dump the readings stored in the device.')
parser_dump.add_argument(
'--unit', action='store',
choices=[unit.value for unit in common.Unit],
help='Select the unit to use for the dumped data.')
parser_dump.add_argument(
'--sort-by', action='store', default='timestamp',
choices=common._ReadingBase._fields,
help='Field to order the dumped data by.')
parser_dump.add_argument(
'--with-ketone', action='store_true', default=False,
help='Enable ketone reading if available on the glucometer.')

parser_date = subparsers.add_parser(
'datetime', help='Reads or sets the date and time of the glucometer.')
parser_date.add_argument(
'--set', action='store', nargs='?', const='now', default=None,
help='Set the date rather than just reading it from the device.')

args = parser.parse_args()

logging.basicConfig(level=args.vlog)

try:
driver = importlib.import_module('glucometerutils.drivers.' + args.driver)
except ImportError as e:
logging.error(
'Error importing driver "%s", please check your --driver parameter:\n%s',
args.driver, e)
return 1

# This check needs to happen before we try to initialize the device, as the
# help action does not require a --device at all.
if args.action == 'help':
print(inspect.getdoc(driver))
return 0

device = driver.Device(args.device)

device.connect()
device_info = device.get_meter_info()

try:
if args.action == 'info':
try:
time_str = device.get_datetime()
except NotImplementedError:
time_str = 'N/A'
print("{device_info}Time: {time}".format(
device_info=str(device_info), time=time_str))
elif args.action == 'dump':
unit = args.unit
if unit is None:
unit = device_info.native_unit

readings = device.get_readings()

if not args.with_ketone:
readings = (reading for reading in readings
if not isinstance(reading, common.KetoneReading))

if args.sort_by is not None:
readings = sorted(
readings, key=lambda reading: getattr(reading, args.sort_by))

for reading in readings:
print(reading.as_csv(unit))
elif args.action == 'datetime':
if args.set == 'now':
print(device.set_datetime())
elif args.set:
try:
from dateutil import parser as date_parser
new_date = date_parser.parse(args.set)
except ImportError:
logging.error(
'Unable to import module "dateutil", please install it.')
return 1
except ValueError:
logging.error('%s: not a valid date', args.set)
return 1
print(device.set_datetime(new_date))
else:
print(device.get_datetime())
elif args.action == 'zero':
confirm = input('Delete the device data log? (y/N) ')
if confirm.lower() in ['y', 'ye', 'yes']:
device.zero_log()
print('\nDevice data log zeroed.')
else:
print('\nDevice data log not zeroed.')
return 1
else:
return 1
except exceptions.Error as err:
print('Error while executing \'%s\': %s' % (args.action, str(err)))
return 1

device.disconnect()
from glucometerutils import glucometer

if __name__ == "__main__":
main()
glucometer.main()
2 changes: 1 addition & 1 deletion glucometerutils/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,4 @@ def __str__(self):
Native Unit: {native_unit}
""").format(model=self.model, serial_number=self.serial_number,
version_information_string=version_information_string,
native_unit=self.native_unit)
native_unit=self.native_unit.value)
Loading