summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDiego Elio Pettenò <flameeyes@flameeyes.eu>2017-06-04 23:57:34 +0200
committerDiego Elio Pettenò <flameeyes@flameeyes.eu>2017-06-04 23:57:34 +0200
commit1d5bade6a90e2a87207d269df8c7bc3f945a9369 (patch)
tree330b45e34d0b57992a096a15a1d43f07caeda15d
parentAbbott devices: add support for using hidapi library. (diff)
downloadglucometerutils-1d5bade6a90e2a87207d269df8c7bc3f945a9369.tar
glucometerutils-1d5bade6a90e2a87207d269df8c7bc3f945a9369.tar.gz
glucometerutils-1d5bade6a90e2a87207d269df8c7bc3f945a9369.tar.bz2
glucometerutils-1d5bade6a90e2a87207d269df8c7bc3f945a9369.tar.lz
glucometerutils-1d5bade6a90e2a87207d269df8c7bc3f945a9369.tar.xz
glucometerutils-1d5bade6a90e2a87207d269df8c7bc3f945a9369.tar.zst
glucometerutils-1d5bade6a90e2a87207d269df8c7bc3f945a9369.zip
-rw-r--r--glucometerutils/exceptions.py7
-rw-r--r--glucometerutils/support/freestyle.py40
-rw-r--r--glucometerutils/support/hiddevice.py79
3 files changed, 90 insertions, 36 deletions
diff --git a/glucometerutils/exceptions.py b/glucometerutils/exceptions.py
index 52c4e94..e1d9200 100644
--- a/glucometerutils/exceptions.py
+++ b/glucometerutils/exceptions.py
@@ -27,6 +27,13 @@ class ConnectionFailed(Error):
self.message = message
+class CommandError(Error):
+ """It was not possible to send a command to the device."""
+
+ def __init__(self, message="Unable to send command to device."):
+ self.message = message
+
+
class InvalidResponse(Error):
"""The response received from the meter was not understood"""
diff --git a/glucometerutils/support/freestyle.py b/glucometerutils/support/freestyle.py
index 737eda1..0e71c03 100644
--- a/glucometerutils/support/freestyle.py
+++ b/glucometerutils/support/freestyle.py
@@ -13,11 +13,11 @@ __license__ = 'MIT'
import csv
import datetime
-import os.path
import re
import struct
from glucometerutils import exceptions
+from glucometerutils.support import hiddevice
# Sequence of initialization messages sent to the device to establish HID
# protocol.
@@ -54,7 +54,7 @@ def _verify_checksum(message, expected_checksum_hex):
raise exceptions.InvalidChecksum(expected_checksum, calculated_checksum)
-class FreeStyleHidDevice(object):
+class FreeStyleHidDevice(hiddevice.HidDevice):
"""Base class implementing the FreeStyle HID common protocol.
This class implements opening, initializing the connection and sending
@@ -72,37 +72,6 @@ class FreeStyleHidDevice(object):
USB_VENDOR_ID = 0x1a61 # Abbott Diabetes Care
USB_PRODUCT_ID = None
- def __init__(self, device):
- # If we do not know for sure the device ID, rely on the user providing a
- # device path.
- if self.USB_PRODUCT_ID is None and not device:
- raise exceptions.CommandLineError(
- '--device parameter is required, should point to /dev/hidraw '
- 'for the meter')
-
- # If the user passed a device path that does not exist, raise an error.
- if device and not os.path.exists(device):
- raise exceptions.ConnectionFailed(
- message='Path %s does not exist.' % device)
-
- # If the user passed a device, try opening it. Note that I have had no
- # success on actually opening the /dev/hidraw path but that's a
- # different problem.
- try:
- if device:
- self.handle_ = open(device, 'w+b')
- else:
- try:
- import hid
- except ImportError:
- raise exceptions.ConnectionFailed(
- message='Missing requied "hidapi" module.')
- self.handle_ = hid.device()
- self.handle_.open(self.USB_VENDOR_ID, self.USB_PRODUCT_ID)
- except OSError:
- raise exceptions.ConnectionFailed(
- message='Unable to connect to meter.')
-
def connect(self):
"""Open connection to the device, starting the knocking sequence."""
for message in _INIT_SEQUENCE:
@@ -130,12 +99,11 @@ class FreeStyleHidDevice(object):
usb_packet = b'\x00' + _STRUCT_PREAMBLE.pack(
message_type, cmdlen) + command + bytes(62 - cmdlen)
- if self.handle_.write(usb_packet) < 0:
- raise exceptions.InvalidResponse()
+ self._write(usb_packet)
def _read_response(self):
"""Read the response from the device and extracts it."""
- usb_packet = self.handle_.read(64)
+ usb_packet = self._read()
assert usb_packet
message_type = usb_packet[0]
diff --git a/glucometerutils/support/hiddevice.py b/glucometerutils/support/hiddevice.py
new file mode 100644
index 0000000..a3f2776
--- /dev/null
+++ b/glucometerutils/support/hiddevice.py
@@ -0,0 +1,79 @@
+"""Common routines and base driver class for HID-based meters.
+"""
+
+__author__ = 'Diego Elio Pettenò'
+__email__ = 'flameeyes@flameeyes.eu'
+__copyright__ = 'Copyright © 2017, Diego Elio Pettenò'
+__license__ = 'MIT'
+
+import logging
+import os
+
+from glucometerutils import exceptions
+
+
+class HidDevice(object):
+ """A device speaking USB HID protocol driver base.
+
+ This class does not implement an actual driver by itself, but provides an
+ easier access to the boilerplate code required for speaking USB HID.
+
+ This helper wraps around an optional dependency on hidapi library: if
+ present the driver will auto-detect the device, if not the device path needs
+ to be provided and should point to a device implementing Linux's hidraw
+ interface.
+
+ The following constants can be set by the actual drivers:
+
+ USB_VENDOR_ID: (int) USB vendor ID for the device.
+ USB_PRODUCT_ID: (int) USB product ID for the device.
+
+ If the VID/PID pair is not provided, the driver will require a device path
+ to be used.
+ """
+
+ USB_VENDOR_ID = None
+ USB_PRODUCT_ID = None
+
+ def __init__(self, device):
+ if None in (self.USB_VENDOR_ID, self.USB_PRODUCT_ID) and not device:
+ raise exceptions.CommandLineError(
+ '--device parameter is required, should point to a /dev/hidraw '
+ 'device node representing the meter.')
+
+ # If the user passed a device path that does not exist, raise an
+ # error. This is to avoid writing to a file instead of to a device node.
+ if device and not os.path.exists(device):
+ raise exceptions.ConnectionFailed(
+ message='Path %s does not exist.' % device)
+
+ # If the user passed a device, try opening it.
+ if device:
+ self.handle_ = open(device, 'w+b')
+ else:
+ logging.info(
+ 'No --device parameter provided, using hidapi library.')
+ try:
+ import hid
+ self.handle_ = hid.device()
+ self.handle_.open(self.USB_VENDOR_ID, self.USB_PRODUCT_ID)
+ except ImportError:
+ raise exceptions.ConnectionFailed(
+ message='Missing requied "hidapi" module.')
+ except OSError as e:
+ raise exceptions.ConnectionFailed(
+ message='Unable to connect to meter: %s.' % e)
+
+ def _write(self, report):
+ """Writes a report to the HID handle."""
+
+ if self.handle_.write(report) < 0:
+ raise exceptions.CommandError()
+
+ def _read(self, size=64):
+ """Read a report from the HID handle.
+
+ This is important as it handles the one incompatible interface between
+ hidraw devices and hidapi handles.
+ """
+ return bytes(self.handle_.read(size))