From cf214f04244e2bedcbda48f52bd7c1ba80c7424d Mon Sep 17 00:00:00 2001 From: Cort Date: Fri, 9 Aug 2024 11:27:11 +0800 Subject: [PATCH] Updated hid_services to work with 1.23.0 --- public/editor.html | 2 +- public/extensions/hid_services.py | 717 ++++++++++++------------------ public/js/extensions.js | 2 +- 3 files changed, 292 insertions(+), 429 deletions(-) diff --git a/public/editor.html b/public/editor.html index cf19ac0..7f183b5 100644 --- a/public/editor.html +++ b/public/editor.html @@ -106,7 +106,7 @@ - + diff --git a/public/extensions/hid_services.py b/public/extensions/hid_services.py index a035fb8..fadd112 100644 --- a/public/extensions/hid_services.py +++ b/public/extensions/hid_services.py @@ -29,13 +29,6 @@ F_READ_WRITE = bluetooth.FLAG_READ | bluetooth.FLAG_WRITE F_READ_NOTIFY = bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY -ATT_F_READ = 0x01 -ATT_F_WRITE = 0x02 - -# Advertising payloads are repeated packets of the following form: -# 1 byte data length (N + 1) -# 1 byte type (see constants below) -# N bytes type-specific data _ADV_TYPE_FLAGS = const(0x01) _ADV_TYPE_NAME = const(0x09) _ADV_TYPE_UUID16_COMPLETE = const(0x3) @@ -46,7 +39,6 @@ _ADV_TYPE_UUID128_MORE = const(0x6) _ADV_TYPE_APPEARANCE = const(0x19) -# IRQ peripheral role event codes _IRQ_CENTRAL_CONNECT = const(1) _IRQ_CENTRAL_DISCONNECT = const(2) _IRQ_GATTS_WRITE = const(3) @@ -89,9 +81,14 @@ _PASSKEY_ACTION_DISP = const(3) _PASSKEY_ACTION_NUMCMP = const(4) -class Advertiser: +_GATTS_NO_ERROR = const(0x00) +_GATTS_ERROR_READ_NOT_PERMITTED = const(0x02) +_GATTS_ERROR_WRITE_NOT_PERMITTED = const(0x03) +_GATTS_ERROR_INSUFFICIENT_AUTHENTICATION = const(0x05) +_GATTS_ERROR_INSUFFICIENT_AUTHORIZATION = const(0x08) +_GATTS_ERROR_INSUFFICIENT_ENCRYPTION = const(0x0f) - # Generate a payload to be passed to gap_advertise(adv_data=...). +class Advertiser: def advertising_payload(self, limited_disc=False, br_edr=False, name=None, services=None, appearance=0): payload = bytearray() @@ -117,7 +114,6 @@ def _append(adv_type, value): elif len(b) == 16: _append(_ADV_TYPE_UUID128_COMPLETE, b) - # See org.bluetooth.characteristic.gap.appearance.xml if appearance: _append(_ADV_TYPE_APPEARANCE, struct.pack(" _IO_CAPABILITY_NO_INPUT_OUTPUT and not self.authenticated: + return _GATTS_ERROR_INSUFFICIENT_AUTHENTICATION + elif self.le_secure and (not self.encrypted or self.key_size < 16): + return _GATTS_ERROR_INSUFFICIENT_ENCRYPTION + else: + return _GATTS_NO_ERROR + elif event == _IRQ_GATTS_INDICATE_DONE: + conn_handle, value_handle, status = data + elif event == _IRQ_MTU_EXCHANGED: conn_handle, mtu = data - elif event == _IRQ_CONNECTION_UPDATE: # Connection parameters were updated - self.conn_handle, _, _, _, _ = data # The new parameters - elif event == _IRQ_ENCRYPTION_UPDATE: # Encryption updated - conn_handle, encrypted, authenticated, bonded, key_size = data - elif event == _IRQ_PASSKEY_ACTION: # Passkey actions: accept connection or show/enter passkey + self._ble.config(mtu=mtu) + elif event == _IRQ_CONNECTION_UPDATE: + self.conn_handle, conn_interval, conn_latency, supervision_timeout, status = data + return None + elif event == _IRQ_ENCRYPTION_UPDATE: + conn_handle, self.encrypted, self.authenticated, self.bonded, self.key_size = data + elif event == _IRQ_PASSKEY_ACTION: conn_handle, action, passkey = data - if action == _PASSKEY_ACTION_NUMCMP: # Do we accept this connection? + if action == _PASSKEY_ACTION_NUMCMP: accept = False - if self.passkey_callback is not None: # Is callback function set? - accept = self.passkey_callback() # Call callback for input + if self.passkey_callback is not None: + accept = self.passkey_callback() self._ble.gap_passkey(conn_handle, action, accept) - elif action == _PASSKEY_ACTION_DISP: # Show our passkey + elif action == _PASSKEY_ACTION_DISP: self._ble.gap_passkey(conn_handle, action, self.passkey) - elif action == _PASSKEY_ACTION_INPUT: # Enter passkey + elif action == _PASSKEY_ACTION_INPUT: pk = None - if self.passkey_callback is not None: # Is callback function set? - pk = self.passkey_callback() # Call callback for input + if self.passkey_callback is not None: + pk = self.passkey_callback() self._ble.gap_passkey(conn_handle, action, pk) - elif event == _IRQ_GATTS_INDICATE_DONE: - conn_handle, value_handle, status = data - elif event == _IRQ_SET_SECRET: # Set secret for bonding + elif event == _IRQ_SET_SECRET: sec_type, key, value = data key = sec_type, bytes(key) value = bytes(value) if value else None - if value is None: # If value is empty, and - if key in self.keys: # If key is known then - del self.keys[key] # Forget key - self.save_secrets() # Save bonding information + if value is None: + if key in self.keys: + del self.keys[key] + self.save_secrets() return True else: return False else: - self.keys[key] = value # Remember key/value - self.save_secrets() # Save bonding information + self.keys[key] = value + self.save_secrets() return True - elif event == _IRQ_GET_SECRET: # Get secret for bonding + elif event == _IRQ_GET_SECRET: sec_type, index, key = data if key is None: i = 0 @@ -292,72 +303,44 @@ def ble_irq(self, event, data): else: key = sec_type, bytes(key) return self.keys.get(key, None) - elif event == _IRQ_GATTS_READ_REQUEST: - conn_handle, attr_handle = data - self._ble.gatts_notify(conn_handle, attr_handle, struct.pack(" 100: self.battery_level = 100 @@ -451,121 +425,86 @@ def set_battery_level(self, level): else: self.battery_level = level - # Set device information - # Must be called before calling Start() - # Variables must be Strings def set_device_information(self, manufacture_name="Homebrew", model_number="1", serial_number="1"): self.manufacture_name = manufacture_name self.model_number = model_number self.serial_number = serial_number - # Set device revision - # Must be called before calling Start() - # Variables must be Strings def set_device_revision(self, firmware_revision="1", hardware_revision="1", software_revision="1"): self.firmware_revision = firmware_revision self.hardware_revision = hardware_revision self.software_revision = software_revision - # Set device pnp information - # Must be called before calling Start() - # Must use the following format: - # pnp_manufacturer_source: 0x01 for manufacturers uuid from the Bluetooth uuid list OR 0x02 from the USBs id list - # pnp_manufacturer_uuid: 0xFEB2 for Microsoft, 0xFE61 for Logitech, 0xFD65 for Razer with source 0x01 - # pnp_product_id: One byte, user defined - # pnp_product_version: Two bytes, user defined, format as 0xJJMN which corresponds to version JJ.M.N def set_device_pnp_information(self, pnp_manufacturer_source=0x01, pnp_manufacturer_uuid=0xFE61, pnp_product_id=0x01, pnp_product_version=0x0123): self.pnp_manufacturer_source = pnp_manufacturer_source self.pnp_manufacturer_uuid = pnp_manufacturer_uuid self.pnp_product_id = pnp_product_id self.pnp_product_version = pnp_product_version - # Set whether to use Bluetooth bonding def set_bonding(self, bond): self.bond = bond - # Set whether to use LE secure pairing def set_le_secure(self, le_secure): self.le_secure = le_secure - # Set input/output capability of this device - # Determines the pairing procedure, e.g., accept connection/passkey entry/just works - # Must be called before calling Start() - # Must use the following values: - # _IO_CAPABILITY_DISPLAY_ONLY - # _IO_CAPABILITY_DISPLAY_YESNO - # _IO_CAPABILITY_KEYBOARD_ONLY - # _IO_CAPABILITY_NO_INPUT_OUTPUT - # _IO_CAPABILITY_KEYBOARD_DISPLAY def set_io_capability(self, io_capability): self.io_capability = io_capability - # Set callback function for pairing events - # Depending on the I/O capability used, the callback function should return either a - # - boolean to accept or deny a connection, or a - # - passkey that was displayed by the main def set_passkey_callback(self, passkey_callback): self.passkey_callback = passkey_callback - # Set the passkey used during pairing when entering a passkey at the main def set_passkey(self, passkey): self.passkey = passkey - # Notifies the central by writing to the battery level handle def notify_battery_level(self): if self.is_connected(): self._ble.gatts_notify(self.conn_handle, self.h_bat, struct.pack("' +