1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
# -*- coding: utf-8 -*-
#
# SPDX-License-Identifier: MIT
"""Common routines and base driver class for HID-based meters.
"""
import logging
import os
from typing import BinaryIO, Optional, Text, Tuple
from glucometerutils import exceptions
class HidSession:
"""An access class to speak to USB HID based devices.
This class does not implement a full driver, but rather provide simpler read/write
methods abstracting the HID library.
"""
def __init__(self, usb_id, device, timeout_ms=0):
# type: (Optional[Tuple[int, int]], Optional[Text], int) -> None
"""Construct a new session object.
Args:
usb_id: Optional pair of vendor_id and product_id for the session.
This is required to use the hidapi library.
device: Optional path to Linux hidraw-style device path. If not provided,
usb_id needs to be provided instead.
timeout_ms: Timeout in milliseconds for read operations. Only relevant when
using hidapi library.
"""
self._timeout_ms = timeout_ms
if not usb_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=f"Path {device} does not exist.")
# If the user passed a device, try opening it.
if device:
self.handle_ = open(device, "w+b") # type: Optional[BinaryIO]
else:
self.handle_ = None
logging.info("No --device parameter provided, using hidapi library.")
try:
import hid
assert usb_id
vendor_id, product_id = usb_id
self.hidapi_handle_ = hid.device()
self.hidapi_handle_.open(vendor_id, product_id)
except ImportError:
raise exceptions.ConnectionFailed(
message='Missing requied "hidapi" module.'
)
except OSError as e:
raise exceptions.ConnectionFailed(
message=f"Unable to connect to meter: {e}."
)
def write(self, report):
# type: (bytes) -> None
"""Writes a report to the HID handle."""
if self.handle_:
written = self.handle_.write(report)
else:
written = self.hidapi_handle_.write(report)
if written < 0:
raise exceptions.CommandError()
def read(self, size=64):
# type: (int) -> bytes
"""Read a report from the HID handle.
This is important as it handles the one incompatible interface between
hidraw devices and hidapi handles.
"""
if self.handle_:
return bytes(self.handle_.read(size))
return bytes(self.hidapi_handle_.read(size, timeout_ms=self._timeout_ms))
|