From ca8af276731f0325b4acd6ef4466dff6999c392e Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 17 Jul 2022 22:02:18 +0100 Subject: [PATCH] Tidy up python client --- data/templates/xap/client/python/routes.py.j2 | 7 ++- lib/python/qmk/cli/xap/xap.py | 8 +-- lib/python/xap_client/device.py | 49 +++++++++------ lib/python/xap_client/routes.py | 61 ++++++++++--------- 4 files changed, 68 insertions(+), 57 deletions(-) diff --git a/data/templates/xap/client/python/routes.py.j2 b/data/templates/xap/client/python/routes.py.j2 index 456d41f0984..7497e8afa1d 100644 --- a/data/templates/xap/client/python/routes.py.j2 +++ b/data/templates/xap/client/python/routes.py.j2 @@ -1,9 +1,10 @@ -# TODO: assumption of only one level of children + +class XAPRoutes(): {%- for id, route in xap.routes | dictsort %} {%- if route.routes %} -# {{route.define}} + # {{route.define}} {%- for subid, subroute in route.routes | dictsort %} -{{route.define}}_{{subroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}' + {{route.define}}_{{subroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}' {%- endfor %} {%- endif %} {%- endfor %} \ No newline at end of file diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 668cbb155b4..34f36c1ccc2 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -47,7 +47,7 @@ def _list_devices(): device = XAPClient().connect(dev) data = device.info() - cli.log.info(' %04x:%04x %s %s [API:%s]', dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['xap']) + cli.log.info(' %04x:%04x %s %s [API:%s]', dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['_version']['xap']) if cli.config.general.verbose: # TODO: better formatting like 'lsusb -v'? @@ -65,10 +65,10 @@ class XAPShell(cmd.Cmd): self.keycodes = get_xap_keycodes(device.version()['xap']) def do_about(self, arg): - """Prints out the current version of QMK with a build date + """Prints out the version info of QMK """ - # TODO: request stuff? - print(self.device.info()['xap']) + data = self.device.version() + print_dotted_output(data) def do_status(self, arg): """Prints out the current device state diff --git a/lib/python/xap_client/device.py b/lib/python/xap_client/device.py index c1f3ada33f1..4f728ae8883 100644 --- a/lib/python/xap_client/device.py +++ b/lib/python/xap_client/device.py @@ -2,8 +2,9 @@ """ import hid import json -import random +import time import gzip +import random import threading import functools from struct import Struct, pack, unpack @@ -11,7 +12,8 @@ from collections import namedtuple from platform import platform from .types import XAPSecureStatus, XAPFlags, XAPRouteError -from .routes import XAP_VERSION_QUERY +from .routes import XAPRoutes +from .util import u32toBCD RequestPacket = namedtuple('RequestPacket', 'token length data') RequestStruct = Struct('H', token))[0] -def _u32toBCD(val): # noqa: N802 - """Create BCD string - """ - return f'{val>>24}.{val>>16 & 0xFF}.{val & 0xFFFF}' - - class XAPDevice: def __init__(self, dev): """Constructor opens hid device and starts dependent services """ self.responses = {} + self.do_read = True self.dev = hid.Device(path=dev['path']) self.bg = threading.Thread(target=self._read_loop, daemon=True) self.bg.start() + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + self.close() + + def close(self): + self.do_read = False + time.sleep(1) + self.dev.close() + def _read_loop(self): """Background thread to signal waiting transactions """ - while 1: + while self.do_read: array_alpha = self.dev.read(ResponseStruct.size, 100) if array_alpha: token = int.from_bytes(array_alpha[:2], 'little') @@ -59,14 +67,14 @@ class XAPDevice: event.set() def _query_device_info(self): - datalen = int.from_bytes(self.transaction(b'\x01\x05') or bytes(0), 'little') + datalen = int.from_bytes(self.transaction(XAPRoutes.QMK_CONFIG_BLOB_LEN) or bytes(0), 'little') if not datalen: return {} data = [] offset = 0 while offset < datalen: - chunk = self.transaction(b'\x01\x06', offset) + chunk = self.transaction(XAPRoutes.QMK_CONFIG_BLOB_CHUNK, offset) data += chunk offset += len(chunk) str_data = gzip.decompress(bytearray(data[:datalen])) @@ -127,13 +135,14 @@ class XAPDevice: @functools.lru_cache def subsystem(self): - sub = int.from_bytes(self._transaction(b'\x00\x02') or bytes(0), 'little') + sub = int.from_bytes(self._transaction(XAPRoutes.XAP_SUBSYSTEM_QUERY) or bytes(0), 'little') return sub @functools.lru_cache def version(self): - ver = int.from_bytes(self._transaction(XAP_VERSION_QUERY) or bytes(0), 'little') - return {'xap': _u32toBCD(ver)} + xap = int.from_bytes(self._transaction(XAPRoutes.XAP_VERSION_QUERY) or bytes(0), 'little') + qmk = int.from_bytes(self._transaction(XAPRoutes.QMK_VERSION_QUERY) or bytes(0), 'little') + return {'xap': u32toBCD(xap), 'qmk': u32toBCD(qmk)} def _ensure_route(self, route): (sub, rt) = route @@ -152,23 +161,23 @@ class XAPDevice: @functools.lru_cache def info(self): data = self._query_device_info() - data['_id'] = self.transaction(b'\x01\x08') - data['xap'] = self.version()['xap'] + data['_id'] = self.transaction(XAPRoutes.QMK_HARDWARE_ID) + data['_version'] = self.version() return data def status(self): - lock = int.from_bytes(self.transaction(b'\x00\x03') or bytes(0), 'little') + lock = int.from_bytes(self.transaction(XAPRoutes.XAP_SECURE_STATUS) or bytes(0), 'little') data = {} data['lock'] = XAPSecureStatus(lock).name return data def unlock(self): - self.transaction(b'\x00\x04') + self.transaction(XAPRoutes.XAP_SECURE_UNLOCK) def lock(self): - self.transaction(b'\x00\x05') + self.transaction(XAPRoutes.XAP_SECURE_LOCK) def reset(self): - status = int.from_bytes(self.transaction(b'\x01\x07') or bytes(0), 'little') + status = int.from_bytes(self.transaction(XAPRoutes.QMK_BOOTLOADER_JUMP) or bytes(0), 'little') return status == 1 diff --git a/lib/python/xap_client/routes.py b/lib/python/xap_client/routes.py index 0cdb9a148d0..f7e67a20e32 100644 --- a/lib/python/xap_client/routes.py +++ b/lib/python/xap_client/routes.py @@ -26,33 +26,34 @@ # ################################################################################ -# TODO: assumption of only one level of children -# XAP -XAP_VERSION_QUERY = b'\x00\x00' -XAP_CAPABILITIES_QUERY = b'\x00\x01' -XAP_SUBSYSTEM_QUERY = b'\x00\x02' -XAP_SECURE_STATUS = b'\x00\x03' -XAP_SECURE_UNLOCK = b'\x00\x04' -XAP_SECURE_LOCK = b'\x00\x05' -# QMK -QMK_VERSION_QUERY = b'\x01\x00' -QMK_CAPABILITIES_QUERY = b'\x01\x01' -QMK_BOARD_IDENTIFIERS = b'\x01\x02' -QMK_BOARD_MANUFACTURER = b'\x01\x03' -QMK_PRODUCT_NAME = b'\x01\x04' -QMK_CONFIG_BLOB_LEN = b'\x01\x05' -QMK_CONFIG_BLOB_CHUNK = b'\x01\x06' -QMK_BOOTLOADER_JUMP = b'\x01\x07' -QMK_HARDWARE_ID = b'\x01\x08' -# KEYMAP -KEYMAP_CAPABILITIES_QUERY = b'\x04\x01' -KEYMAP_GET_LAYER_COUNT = b'\x04\x02' -KEYMAP_GET_KEYMAP_KEYCODE = b'\x04\x03' -KEYMAP_GET_ENCODER_KEYCODE = b'\x04\x04' -# REMAPPING -REMAPPING_CAPABILITIES_QUERY = b'\x05\x01' -REMAPPING_GET_DYNAMIC_LAYER_COUNT = b'\x05\x02' -REMAPPING_SET_KEYMAP_KEYCODE = b'\x05\x03' -REMAPPING_SET_ENCODER_KEYCODE = b'\x05\x04' -# LIGHTING -LIGHTING_CAPABILITIES_QUERY = b'\x06\x01' + +class XAPRoutes(): + # XAP + XAP_VERSION_QUERY = b'\x00\x00' + XAP_CAPABILITIES_QUERY = b'\x00\x01' + XAP_SUBSYSTEM_QUERY = b'\x00\x02' + XAP_SECURE_STATUS = b'\x00\x03' + XAP_SECURE_UNLOCK = b'\x00\x04' + XAP_SECURE_LOCK = b'\x00\x05' + # QMK + QMK_VERSION_QUERY = b'\x01\x00' + QMK_CAPABILITIES_QUERY = b'\x01\x01' + QMK_BOARD_IDENTIFIERS = b'\x01\x02' + QMK_BOARD_MANUFACTURER = b'\x01\x03' + QMK_PRODUCT_NAME = b'\x01\x04' + QMK_CONFIG_BLOB_LEN = b'\x01\x05' + QMK_CONFIG_BLOB_CHUNK = b'\x01\x06' + QMK_BOOTLOADER_JUMP = b'\x01\x07' + QMK_HARDWARE_ID = b'\x01\x08' + # KEYMAP + KEYMAP_CAPABILITIES_QUERY = b'\x04\x01' + KEYMAP_GET_LAYER_COUNT = b'\x04\x02' + KEYMAP_GET_KEYMAP_KEYCODE = b'\x04\x03' + KEYMAP_GET_ENCODER_KEYCODE = b'\x04\x04' + # REMAPPING + REMAPPING_CAPABILITIES_QUERY = b'\x05\x01' + REMAPPING_GET_DYNAMIC_LAYER_COUNT = b'\x05\x02' + REMAPPING_SET_KEYMAP_KEYCODE = b'\x05\x03' + REMAPPING_SET_ENCODER_KEYCODE = b'\x05\x04' + # LIGHTING + LIGHTING_CAPABILITIES_QUERY = b'\x06\x01'