""" Bluetooth-HID-Keyboard Micropython library for the Bluetooth-HID-Keyboard ======================================================= #https://github.com/Heerkog/MicroPythonBLEHID @dahanzimin From the Mixly Team """ import bluetooth import struct, time from micropython import const from ubinascii import hexlify from ble_advertising import advertising_payload from bluetooth import UUID, FLAG_READ, FLAG_WRITE ,FLAG_NOTIFY, FLAG_WRITE_NO_RESPONSE _IRQ_CENTRAL_CONNECT = const(1) _IRQ_CENTRAL_DISCONNECT = const(2) _IRQ_GATTS_WRITE = const(3) _IRQ_MTU_EXCHANGED = const(21) _IRQ_CONNECTION_UPDATE = const(27) _IRQ_GET_SECRET = const(29) _IRQ_SET_SECRET = const(30) _IRQ_PASSKEY_ACTION = const(31) _PASSKEY_ACTION_INPUT = const(2) _PASSKEY_ACTION_DISP = const(3) _PASSKEY_ACTION_NUMCMP = const(4) _HID_INPUT_REPORT = const(b'\x05\x01\t\x06\xa1\x01\x85\x01u\x01\x95\x08\x05\x07\x19\xe0)\xe7\x15\x00%\x01\x81\x02\x95\x01u\x08\x81\x01\x95\x05u\x01\x05\x08\x19\x01)\x05\x91\x02\x95\x01u\x03\x91\x01\x95\x06u\x08\x15\x00%e\x05\x07\x19\x00)e\x81\x00\xc0') _KEYCODE = const(b'\x00\x00\x00\x00\x00\x00\x00\x00*+(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00\x00,\x9e\xb4\xa0\xa1\xa2\xa44\xa6\xa7\xa5\xae6-78\'\x1e\x1f !"#$%&\xb33\xb6.\xb7\xb8\x9f\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d/10\xa3\xad5\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\xaf\xb1\xb0\xb5L') _DIS = (UUID(0x180A), ( (UUID(0x2A24), FLAG_READ), (UUID(0x2A25), FLAG_READ), (UUID(0x2A26), FLAG_READ), (UUID(0x2A27), FLAG_READ), (UUID(0x2A28), FLAG_READ), (UUID(0x2A29), FLAG_READ), (UUID(0x2A50), FLAG_READ), ), ) _BAS = (UUID(0x180F), ( (UUID(0x2A19), FLAG_READ | FLAG_NOTIFY, ( (UUID(0x2902), 0x01 | 0x02), (UUID(0x2904), 0x01),)), ), ) _HIDS = (UUID(0x1812), ((UUID(0x2A4A), FLAG_READ), (UUID(0x2A4B), FLAG_READ), (UUID(0x2A4C), FLAG_READ | FLAG_WRITE | FLAG_WRITE_NO_RESPONSE), (UUID(0x2A4D), FLAG_READ | FLAG_NOTIFY, ( (UUID(0x2902), 0x01 | 0x02), (UUID(0x2908), 0x01 | 0x02),)), (UUID(0x2A4D), FLAG_READ | FLAG_WRITE, ( (UUID(0x2902), 0x01 | 0x02), (UUID(0x2908), 0x01 | 0x02),)), (UUID(0x2A4E), FLAG_READ | FLAG_WRITE | FLAG_WRITE_NO_RESPONSE), ), ) class Keyboard: def __init__(self, name=None, passkey=1234, battery_level=100): if (name is '') or (name is None): name = "Mixgo_" + self.mac[-6:].upper() print("Keyboard name:", name) self._ble = bluetooth.BLE() self._ble.active(True) self._ble.irq(self._irq) self._ble.config(gap_name=name) self._ble.config(mtu=23) self.device_state = False self.conn_handle = None self.passkey = passkey self.battery_level = battery_level self.report = b'\x00' try: import ble_hid_key self.keys = ble_hid_key.keys except: self.keys = {} handles = self._ble.gatts_register_services((_DIS, _BAS, _HIDS)) self._service_characteristics(handles) self._payload = advertising_payload(name=name, services=[UUID(0x1812)], appearance=const(961)) self.advertise() def _irq(self, event, data): # Interrupt request callback function if event == _IRQ_CENTRAL_CONNECT: self.conn_handle, _, _ = data print("Keyboard connected: ", self.conn_handle) self.device_state = True elif event == _IRQ_CENTRAL_DISCONNECT: self.conn_handle = None conn_handle, addr_type, addr = data print("Keyboard disconnected: ", conn_handle) self.advertise() self.device_state = False elif event == _IRQ_MTU_EXCHANGED: conn_handle, mtu = data self._ble.config(mtu=mtu) print("MTU exchanged: ", mtu) elif event == _IRQ_CONNECTION_UPDATE: self.conn_handle, _, _, _, _ = data print("Connection update") elif event == _IRQ_PASSKEY_ACTION: conn_handle, action, passkey = data print("Passkey action", conn_handle, action, passkey) if action == _PASSKEY_ACTION_NUMCMP: self._ble.gap_passkey(conn_handle, action, False) elif action == _PASSKEY_ACTION_DISP: print("Displaying passkey") self._ble.gap_passkey(conn_handle, action, self.passkey) elif action == _PASSKEY_ACTION_INPUT: print(" Prompting for passkey") self._ble.gap_passkey(conn_handle, action, None) else: print("unknown action") elif event == _IRQ_GATTS_WRITE: conn_handle, attr_handle = data self.report = self._ble.gatts_read(attr_handle) elif event == _IRQ_SET_SECRET: sec_type, key, value = data key = sec_type, bytes(key) value = bytes(value) if value else None #print("Set secret: ", key, value) if value is None: if key in self.keys: del self.keys[key] self.key_secrets(self.keys) return True else: return False else: self.keys[key] = value self.key_secrets(self.keys) return True elif event == _IRQ_GET_SECRET: sec_type, index, key = data #print("Get secret: ", sec_type, index, bytes(key) if key else None) if key is None: i = 0 for (t, _key), value in self.keys.items(): if t == sec_type: if i == index: return value i += 1 return None else: key = sec_type, bytes(key) return self.keys.get(key, None) #else: #print("Unhandled IRQ event: ", event, data) def key_secrets(self, keys={}): with open("ble_hid_key.py", "w+") as s_f: s_f.write("keys=" + str(keys) + "\n") def _service_characteristics(self, handles): (h_mod, h_ser, h_fwr, h_hwr, h_swr, h_man, h_pnp) = handles[0] (self.h_bat, h_ccc, h_bfmt,) = handles[1] (h_info, h_hid, _, self.h_rep, _, h_d1, _, _, h_d2, h_proto) = handles[2] # Write DIS characteristics. self._ble.gatts_write(h_mod, b'1') self._ble.gatts_write(h_ser, b'1') self._ble.gatts_write(h_fwr, b'1') self._ble.gatts_write(h_hwr, b'1') self._ble.gatts_write(h_swr, b'1') self._ble.gatts_write(h_man, b'Homebrew') self._ble.gatts_write(h_pnp, struct.pack(" 5: break _keys[i] = general[i] else: _keys[0] = general # Pack the mouse state as described by the input report self._ble.gatts_notify(self.conn_handle, self.h_rep, bytes([special & 0xFF, 0]) + _keys) if release: time.sleep_ms(10) self._ble.gatts_notify(self.conn_handle, self.h_rep, b'\x00\x00\x00\x00\x00\x00\x00\x00') def notify_battery(self, level): if self.is_connected(): self.battery_level = max(min(level, 100), 0) # Notifies the client by writing to the battery level handle. self._ble.gatts_notify(self.conn_handle, self.h_bat, struct.pack("> 7 else 0x00, _KEYCODE[char] & 0x7F) time.sleep_ms(20 + delay) @property def mac(self): '''Get mac address''' return hexlify(self._ble.config('mac')[1]).decode()