From 37e9a307e6e16ce12b7ad06af2b6a0c1cdb972a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Elio=20Petten=C3=B2?= Date: Tue, 30 Jul 2019 17:45:38 +0100 Subject: FreeStyle devices: don't error out for non-ASCII patient names. This was brought up in issue #60. While the devices don't appear to care much for patient names, and can't be expected to support UTF-8 (the Windows software don't appear to allow that), it's worth protecting against this and just replacing the non-ASCII characters. To make this easier, the decoding is pushed down to the end of the processing of the multi-packet response, rather than being done per-packet. --- glucometerutils/support/freestyle.py | 42 +++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/glucometerutils/support/freestyle.py b/glucometerutils/support/freestyle.py index 13674df..b2d58d1 100644 --- a/glucometerutils/support/freestyle.py +++ b/glucometerutils/support/freestyle.py @@ -14,7 +14,7 @@ import logging import re try: - from typing import Iterator, List, Optional, Text, Tuple + from typing import AnyStr, Iterator, List, Optional, Text, Tuple except ImportError: pass @@ -35,10 +35,10 @@ _FREESTYLE_MESSAGE = construct.Struct( construct.Prefixed(construct.Byte, construct.GreedyBytes)), ) -_TEXT_COMPLETION_RE = re.compile('CMD (?:OK|Fail!)') +_TEXT_COMPLETION_RE = re.compile(b'CMD (?:OK|Fail!)') _TEXT_REPLY_FORMAT = re.compile( - '^(?P.*)CKSM:(?P[0-9A-F]{8})\r\n' - 'CMD (?POK|Fail!)\r\n$', re.DOTALL) + b'^(?P.*)CKSM:(?P[0-9A-F]{8})\r\n' + b'CMD (?POK|Fail!)\r\n$', re.DOTALL) _MULTIRECORDS_FORMAT = re.compile( '^(?P.+\r\n)(?P[0-9]+),(?P[0-9A-F]{8})\r\n$', @@ -46,12 +46,12 @@ _MULTIRECORDS_FORMAT = re.compile( def _verify_checksum(message, expected_checksum_hex): - # type: (Text, Text) -> None + # type: (AnyStr, AnyStr) -> None """Calculate the simple checksum of the message and compare with expected. Args: message: (str) message to calculate the checksum of. - expected_checksum_hex: (str) hexadecimal string representing the checksum + expected_checksum_hex: hexadecimal string representing the checksum expected to match the message. Raises: @@ -59,7 +59,12 @@ def _verify_checksum(message, expected_checksum_hex): received. """ expected_checksum = int(expected_checksum_hex, 16) - calculated_checksum = sum(ord(c) for c in message) + if isinstance(message, bytes): + all_bytes = (c for c in message) + else: + all_bytes = (ord(c) for c in message) + + calculated_checksum = sum(all_bytes) if expected_checksum != calculated_checksum: raise exceptions.InvalidChecksum(expected_checksum, calculated_checksum) @@ -147,16 +152,19 @@ class FreeStyleHidDevice(hiddevice.HidDevice): self._send_command(self.TEXT_CMD, command) # Reply can stretch multiple buffers - full_content = '' + full_content = b'' while True: message_type, content = self._read_response() + logging.debug( + 'Received message: type %02x content %r', message_type, content) + if message_type != self.TEXT_REPLY_CMD: raise exceptions.InvalidResponse( - 'Message type %02x does not match expectations: %s' % - (message_type, content.decode('ascii'))) + 'Message type %02x does not match expectations: %r' % + (message_type, content)) - full_content += content.decode('ascii') + full_content += content if _TEXT_COMPLETION_RE.search(full_content): break @@ -168,10 +176,14 @@ class FreeStyleHidDevice(hiddevice.HidDevice): message = match.group('message') _verify_checksum(message, match.group('checksum')) - if match.group('status') != 'OK': + if match.group('status') != b'OK': raise exceptions.InvalidResponse(message or "Command failed") - return message + # If there is anything in the response that is not ASCII-safe, this is + # probably in the patient name. The Windows utility does not seem to + # validate those, so just replace anything non-ASCII with the correct + # unknown codepoint. + return message.decode('ascii', 'replace') # Some of the commands are also shared across devices that use this HID # protocol, but not many. Only provide here those that do seep to change @@ -196,11 +208,11 @@ class FreeStyleHidDevice(hiddevice.HidDevice): def set_patient_name(self, name): # type: (Text) -> None try: - name = name.encode('ascii') + encoded_name = name.encode('ascii') except UnicodeDecodeError: raise ValueError('Only ASCII-safe names are tested working') - result = self._send_text_command(b'$ptname,' + name) + result = self._send_text_command(b'$ptname,' + encoded_name) def get_datetime(self): # type: () -> datetime.datetime -- cgit v1.2.3