def setup_keyplus_label(self): try: self.device.open() settingsInfo = protocol.get_device_info(self.device) firmwareInfo = protocol.get_firmware_info(self.device) self.device.close() except TimeoutError as err: # Incase opening the device fails raise Exception ("Error Opening Device: {} | {}:{}" .format( self.device.path, self.device.vendor_id, self.device.product_id ), file=sys.stderr ) if settingsInfo.crc == settingsInfo.computed_crc: build_time_str = protocol.timestamp_to_str(settingsInfo.timestamp) self.label = QLabel('{} | {} | Firmware v{}.{}.{}\n' 'Device id: {}\n' 'Serial number: {}\n' 'Last time updated: {}' .format( self.device.manufacturer_string, self.device.product_string, firmwareInfo.version_major, firmwareInfo.version_minor, firmwareInfo.version_patch, settingsInfo.id, self.device.serial_number, build_time_str ) ) else: # CRC doesn't match if settingsInfo.is_empty: self.label = QLabel('??? | ??? | Firmware v{}.{}.{}\n' 'Warning: Empty settings!\n' 'Serial number: {}\n' .format( firmwareInfo.version_major, firmwareInfo.version_minor, firmwareInfo.version_patch, self.device.serial_number, ) ) else: # corrupt settings in the flash build_time_str = protocol.timestamp_to_str(settingsInfo.timestamp) self.label = QLabel('??? | ??? | Firmware v{}.{}.{}\n' 'WARNING: Settings are uninitialized\n' 'Serial number: {}\n' .format( firmwareInfo.version_major, firmwareInfo.version_minor, firmwareInfo.version_patch, self.device.serial_number, ) )
def print_device_info(device, indent=" "): dev_info = protocol.get_device_info(device) print(indent, "id: ", dev_info.id) print(indent, "name: '{}'".format(dev_info.device_name_str()) ) print(indent, "layout last updated: ", dev_info.timestamp_str()) print(indent, "default_report_mode: ", dev_info.default_report_mode_str()) print(indent, "scan_mode: ", dev_info.scan_mode) print(indent, "row_count: ", dev_info.row_count) print(indent, "col_count: ", dev_info.col_count)
def setup_keyplus_label(self): self.device.open() settingsInfo = protocol.get_device_info(self.device) firmwareInfo = protocol.get_firmware_info(self.device) errorInfo = protocol.get_error_info(self.device) self.has_critical_error = errorInfo.has_critical_error() self.device.close() if settingsInfo.crc == settingsInfo.computed_crc: build_time_str = protocol.timestamp_to_str(settingsInfo.timestamp) device_name = settingsInfo.device_name_str() device_name = device_name.strip('\x00').strip('\xff').strip() self.label.setText('{} | {} | Firmware v{}.{}.{}\n' 'Device id: {}\n' 'Serial number: {}\n' 'Last time updated: {}'.format( self.device.manufacturer_string, device_name, firmwareInfo.version_major, firmwareInfo.version_minor, firmwareInfo.version_patch, settingsInfo.id, self.device.serial_number, build_time_str)) else: # CRC doesn't match if settingsInfo.is_empty: self.label.setText('??? | ??? | Firmware v{}.{}.{}\n' 'Warning: Empty settings!\n' 'Serial number: {}\n'.format( firmwareInfo.version_major, firmwareInfo.version_minor, firmwareInfo.version_patch, self.device.serial_number, )) else: # corrupt settings in the flash build_time_str = protocol.timestamp_to_str( settingsInfo.timestamp) self.label.setText('??? | ??? | Firmware v{}.{}.{}\n' 'WARNING: Settings are uninitialized\n' 'Serial number: {}\n'.format( firmwareInfo.version_major, firmwareInfo.version_minor, firmwareInfo.version_patch, self.device.serial_number, ))
def setup_keyplus_label(self): try: self.device.open() settingsInfo = protocol.get_device_info(self.device) firmwareInfo = protocol.get_firmware_info(self.device) self.device.close() except TimeoutError as err: # Incase opening the device fails raise Exception("Error Opening Device: {} | {}:{}".format( self.device.path, self.device.vendor_id, self.device.product_id), file=sys.stderr) self.valid = True build_time_str = protocol.timestamp_to_str(settingsInfo.timestamp) self.label = QLabel('{} | {} | Firmware v{}.{}.{}\n' 'Device id: {}\n' 'Serial number: {}\n' 'Last time updated: {}'.format( self.device.manufacturer_string, self.device.product_string, firmwareInfo.version_major, firmwareInfo.version_minor, firmwareInfo.version_patch, settingsInfo.id, self.device.serial_number, build_time_str))
def programDeviceHandler(self, device_path): target_device = self.tryOpenDevicePath(device_path) if target_device == None: self.abort_update(target_device) return programmingMode = self.fileSelectorWidget.getProgramingInfo() if is_bootloader_device( target_device ) and programmingMode != FileSelector.ScopeFirmware: error_msg_box( "Can only upload firmware while bootloader is running. " "Either reset it, or upload a firmware hex instead") self.abort_update(target_device) return if programmingMode == FileSelector.ScopeLayout: self.statusBar().showMessage("Started updating layout", timeout=STATUS_BAR_TIMEOUT) layout_file = self.fileSelectorWidget.getLayoutFile() if layout_file == '': error_msg_box("No layout file given.") self.abort_update(target_device) return else: pass layout_json_obj = None with open(layout_file) as file_obj: try: layout_json_obj = yaml.safe_load(file_obj.read()) except Exception as err: error_msg_box("Syntax error in yaml file: " + str(err)) self.abort_update(target_device) return device_info = protocol.get_device_info(target_device) layout_data, settings_data = self.process_layout( layout_json_obj, layout_file, device_info.id) if layout_data == None or settings_data == None: return protocol.update_layout_section(target_device, layout_data) protocol.update_settings_section(target_device, settings_data, keep_rf=True) protocol.reset_device(target_device) self.statusBar().showMessage("Finished updating layout", timeout=STATUS_BAR_TIMEOUT) elif programmingMode == FileSelector.ScopeDevice: layout_file = self.fileSelectorWidget.getRFLayoutFile() rf_file = self.fileSelectorWidget.getRFFile() target_id = self.fileSelectorWidget.getTargetID() self.statusBar().showMessage("Started updating RF settings", timeout=STATUS_BAR_TIMEOUT) if layout_file == '': error_msg_box("No layout file given.") self.abort_update(target_device) return elif rf_file == '': error_msg_box("No RF settings file given.") self.abort_update(target_device) return elif target_id == None: error_msg_box("No device id file given.") self.abort_update(target_device) return layout_json_obj = None rf_json_obj = None with open(layout_file) as file_obj: try: layout_json_obj = yaml.safe_load(file_obj.read()) except Exception as err: error_msg_box("Syntax error in yaml file: " + str(err)) self.abort_update(target_device) return with open(rf_file) as file_obj: try: rf_json_obj = yaml.safe_load(file_obj.read()) except Exception as err: error_msg_box("Syntax error in yaml file: " + str(err)) self.abort_update(target_device) return try: settings_gen = layout.parser.SettingsGenerator( layout_json_obj, rf_json_obj) except ParseError as err: error_msg_box("Error Generating RF settings data: " + str(err)) self.abort_update(target_device) return layout_data = settings_gen.gen_layout_section(target_id) settings_data = settings_gen.gen_settings_section(target_id) protocol.update_settings_section(target_device, settings_data) protocol.update_layout_section(target_device, layout_data) protocol.reset_device(target_device) self.statusBar().showMessage("Finished updating RF settings", timeout=STATUS_BAR_TIMEOUT) elif programmingMode == FileSelector.ScopeFirmware: fw_file = self.fileSelectorWidget.getFirmwareFile() self.statusBar().showMessage("Starting update firmware", timeout=STATUS_BAR_TIMEOUT) if fw_file == '': error_msg_box("No firmware file given.") else: if is_xusb_bootloader_device(target_device): self.program_xusb_boot_firmware_hex(target_device, fw_file) elif is_keyplus_device(target_device): try: serial_num = target_device.serial_number boot_vid, boot_pid = protocol.enter_bootloader( target_device) self.bootloaderProgramTimer = QTimer() self.bootloaderProgramTimer.setInterval(3000) self.bootloaderProgramTimer.setSingleShot(True) self.bootloaderProgramTimer.timeout.connect( lambda: self.programFirmwareHex( boot_vid, boot_pid, serial_num, fw_file)) self.bootloaderProgramTimer.start() except (easyhid.HIDException, protocol.KBProtocolException): error_msg_box( "Programming hex file failed: '{}'".format( fw_file)) else: try: target_device.close() except: pass raise Exception("Unimplementend programming mode")
def infoDeviceHandler(self, device_path): device = self.tryOpenDevicePath(device_path) if device == None: return settingsInfo = protocol.get_device_info(device) firmwareInfo = protocol.get_firmware_info(device) rfInfo = protocol.get_rf_info(device) if firmwareInfo.has_at_least_version('0.2.2'): errorInfo = protocol.get_error_info(device) else: errorInfo = None device.close() header = ["Attribute", "Value"] device_settings = [ ("Device ID", settingsInfo.id), ("Device name", settingsInfo.device_name_str()), ("Device serial number", device.serial_number), ("Last layout update", settingsInfo.timestamp_str()), ("Default report mode", settingsInfo.default_report_mode_str()), ("Matrix scan mode", settingsInfo.scan_mode_str()), ("Matrix columns", settingsInfo.col_count), ("Matrix rows", settingsInfo.row_count), ("Settings stored CRC", hex(settingsInfo.crc)), ("Settings computed CRC", hex(settingsInfo.computed_crc)), ("USB", not (settingsInfo.has_usb_disabled() or not firmwareInfo.has_fw_support_usb())), ("I2C", not (settingsInfo.has_i2c_disabled() or not firmwareInfo.has_fw_support_i2c())), ("nRF24 wireless", not (settingsInfo.has_nrf24_disabled() or not firmwareInfo.has_fw_support_nrf24())), ("Unifying mouse", not (settingsInfo.has_unifying_mouse_disabled() or not firmwareInfo.has_fw_support_unifying())), ("Bluetooth", not (settingsInfo.has_bluetooth_disabled() or not firmwareInfo.has_fw_support_bluetooth())), ("RF pipe0", binascii.hexlify(rfInfo.pipe0).decode('ascii')), ("RF pipe1", binascii.hexlify(rfInfo.pipe1).decode('ascii')), ("RF pipe2", "{:02x}".format(rfInfo.pipe2)), ("RF pipe3", "{:02x}".format(rfInfo.pipe3)), ("RF pipe4", "{:02x}".format(rfInfo.pipe4)), ("RF pipe5", "{:02x}".format(rfInfo.pipe5)), ("RF channel", str(rfInfo.channel)), ("RF auto retransmit count", str(rfInfo.arc)), ("RF data rate", protocol.data_rate_to_str(rfInfo.data_rate)), ] firmware_settings = [ ("Firmware version", "{}.{}.{}".format(firmwareInfo.version_major, firmwareInfo.version_minor, firmwareInfo.version_patch)), ("Firmware build date", str(datetime.datetime.fromtimestamp(firmwareInfo.timestamp))), ("Firmware git hash", "{:08x}".format(firmwareInfo.git_hash)), ("Layout storage size", firmwareInfo.layout_flash_size), ("Bootloader VID", "{:04x}".format(firmwareInfo.bootloader_vid)), ("Bootloader PID", "{:04x}".format(firmwareInfo.bootloader_pid)), ("Support scanning", firmwareInfo.has_fw_support_scanning()), ("Support scanning col to row", firmwareInfo.has_fw_support_scanning_col_row()), ("Support scanning row to col", firmwareInfo.has_fw_support_scanning_row_col()), ("Media keys", firmwareInfo.has_fw_support_key_media()), ("Mouse keys", firmwareInfo.has_fw_support_key_mouse()), ("Layer keys", firmwareInfo.has_fw_support_key_layers()), ("Sticky keys", firmwareInfo.has_fw_support_key_sticky()), ("Tap keys", firmwareInfo.has_fw_support_key_tap()), ("Hold keys", firmwareInfo.has_fw_support_key_hold()), ("Support 6KRO", firmwareInfo.has_fw_support_6kro()), ("Support NKRO", firmwareInfo.has_fw_support_key_hold()), ("Support indicator LEDs", firmwareInfo.has_fw_support_led_indicators()), ("Support LED backlighting", firmwareInfo.has_fw_support_led_backlighting()), ("Support ws2812 LEDs", firmwareInfo.has_fw_support_led_ws2812()), ("Support USB", firmwareInfo.has_fw_support_usb()), ("Support nRF24 wireless", firmwareInfo.has_fw_support_nrf24()), ("Support Unifying", firmwareInfo.has_fw_support_unifying()), ("Support I2C", firmwareInfo.has_fw_support_i2c()), ("Support Bluetooth", firmwareInfo.has_fw_support_bluetooth()), ] if errorInfo: error_codes = [] for code in errorInfo.get_error_codes(): error_codes.append((errorInfo.error_code_to_name(code), code)) else: error_codes = [ ('Error codes require firmware version 0.2.2 or greater', ) ] self.info_window = DeviceInformationWindow( self, header, device_settings, firmware_settings, error_codes, ) self.info_window.setModal(True) self.info_window.exec_() self.deviceListWidget.updateList()
def task(self, args): layout_file = args.layout_file rf_file = args.rf_file hex_file = args.hex_file new_id = args.new_id if args.merge_hex: if (new_id == None or layout_file == None or rf_file == None or \ new_id == None): print("Error: To generate a merged hex file, need all settings" " files.", file=sys.stderr) exit(EXIT_COMMAND_ERROR) if len(args.merge_hex) != 3: print("Error: To generate a merged hex file, need to provide " "[settings_addr, layout_addr, layout_size] as arguments", file=sys.stderr) exit(EXIT_COMMAND_ERROR) if new_id != None and (layout_file == None or rf_file == None): print("Error: when providing a new ID, a layout and RF file " "must be provided", file=sys.stderr) exit(EXIT_COMMAND_ERROR) if layout_file == None and hex_file == None and rf_file == None: self.arg_parser.print_help() exit(0) if not args.merge_hex: device = self.find_matching_device(args) device.open() print("Programing start...") print_hid_info(device) print_device_info(device) print_layout_info(device) print("") if layout_file != None: with open(layout_file) as file_obj: try: layout_json_obj = yaml.safe_load(file_obj.read()) # except yaml.YAMLError as err: except Exception as err: print("Error in Layout Settings YAML file: " + str(err), file=sys.stderr) device.close() exit(EXIT_BAD_FILE) else: layout_json_obj = None if rf_file != None: with open(rf_file) as file_obj: try: rf_json_obj = yaml.safe_load(file_obj.read()) # except yaml.YAMLError as err: except Exception as err: print("Error in RF Settings YAML file: " + str(err), file=sys.stderr) device.close() exit(EXIT_BAD_FILE) else: rf_json_obj = None if layout_file != None: print("Parsing files...") if not args.merge_hex: device_info = protocol.get_device_info(device) if new_id == None: target_id = device_info.id else: target_id = new_id layout_data, settings_data = self.process_layout( layout_json_obj, rf_json_obj, layout_file, target_id ) if layout_data == None or settings_data == None: exit(EXIT_BAD_FILE) print("Parsing finished...") if args.merge_hex: # don't want to program the device, instead we want to build a # hexfile with the settings preprogrammed with open(hex_file) as f: fw_hex = intelhex.IntelHex(f) settings_addr = args.merge_hex[0] layout_addr = args.merge_hex[1] layout_size = args.merge_hex[2] if len(layout_data) > layout_size: print("Error: layout data to large. Got {} bytes, but only " "{} bytes available".format( len(layout_data), layout_size ), file=sys.stderr) exit(EXIT_INSUFFICIENT_SPACE) settings_hex = intelhex.IntelHex() settings_hex.frombytes( settings_data, offset = settings_addr ) layout_hex = intelhex.IntelHex() layout_hex.frombytes( layout_data, offset = layout_addr ) fw_hex.merge(settings_hex, overlap='replace') # first erase anything that is in the layout section for i in range(layout_addr, layout_addr+layout_size): fw_hex[i] = 0 # dummy, write so del works del fw_hex[i] fw_hex.merge(layout_hex, overlap='replace') if args.outfile: with open(args.outfile, 'w') as outfile: fw_hex.write_hex_file(outfile) else: fw_hex.write_hex_file(sys.stdout) exit(0) elif layout_file and not rf_file: print("Updating layout only...") protocol.update_settings_section(device, settings_data, keep_rf=True) protocol.update_layout_section(device, layout_data) elif layout_file and rf_file: print("Updating layout and rf settings...") protocol.update_settings_section(device, settings_data) protocol.update_layout_section(device, layout_data) elif layout_file and rf_file and hex_file: print("TODO: not implemented", file=sys.stderr) elif hex_file and not layout_file and not rf_file: print("TODO: not implemented", file=sys.stderr) else: pass print("Done!") protocol.reset_device(device) device.close()
def find_matching_device(self, args): hid_list = easyhid.Enumeration() target_vid = protocol.DEFAULT_VID target_pid = None if args.vid_pid: matches = args.vid_pid.split(":") if len(matches) == 1: try: target_vid = int(matches[0], base=16) if target_vid > 0xffff: raise Exception except: print("Bad VID/PID pair: " + args.vid_pid, file=sys.stderr) exit(EXIT_MATCH_DEVICE) elif len(matches) == 2: try: if matches[0] == '': target_vid = None else: target_vid = target_vid = int(matches[0], base=16) if matches[1] == '': target_pid = None else: target_pid = target_pid = int(matches[1], base=16) if target_vid and target_vid > 0xffff: raise Exception if target_pid and target_pid > 0xffff: raise Exception except: print("Bad VID/PID pair: " + args.vid_pid, file=sys.stderr) exit(EXIT_MATCH_DEVICE) if args.serial != None: args.serial = self.get_similar_serial_number(hid_list, args.serial) matching_devices = hid_list.find( vid=target_vid, pid=target_pid, serial=args.serial, interface=3 ) if len(matching_devices) == 0: print("Couldn't find a matching device to open", file=sys.stderr) exit(EXIT_MATCH_DEVICE) if args.dev_id != None: matching_id_list = [] for dev in matching_devices: try: dev.open() dev_info = protocol.get_device_info(dev) dev.close() if dev_info.id == args.dev_id: matching_id_list.append(dev) except: print("Warning: couldn't open device: " + str(dev), file=sys.stderr) dev.close() matching_devices = matching_id_list num_matches = len(matching_devices) if num_matches== 0: print("Couldn't find any matching devices.", file=sys.stderr) exit(EXIT_MATCH_DEVICE) elif num_matches == 1: return matching_devices[0] elif num_matches > 1: print("Error: found {} matching devices, select a specifc device or " "disconnect the other devices".format(num_matches), file=sys.stderr) exit(EXIT_MATCH_DEVICE)
def infoDeviceHandler(self, device_path): device = self.tryOpenDevicePath(device_path) if device == None: return settingsInfo = protocol.get_device_info(device) firmwareInfo = protocol.get_firmware_info(device) rfInfo = protocol.get_rf_info(device) device.close() header = ["Attribute", "Value"] device_settings = [ ("Device ID", settingsInfo.id), ("Device name", settingsInfo.name.decode('utf-8')), ("Device serial number", device.serial_number), ("Last layout update", protocol.timestamp_to_str(settingsInfo.timestamp)), ("Default report mode", protocol.report_mode_to_str(settingsInfo.default_report_mode)), ("Matrix scan mode", protocol.scan_mode_to_str(settingsInfo.scan_mode)), ("Matrix columns", settingsInfo.col_count), ("Matrix rows", settingsInfo.row_count), ("RF pipe0", binascii.hexlify(rfInfo.pipe0).decode('ascii')), ("RF pipe1", binascii.hexlify(rfInfo.pipe1).decode('ascii')), ("RF pipe2", "{:02x}".format(rfInfo.pipe2)), ("RF pipe3", "{:02x}".format(rfInfo.pipe3)), ("RF pipe4", "{:02x}".format(rfInfo.pipe4)), ("RF pipe5", "{:02x}".format(rfInfo.pipe5)), ("RF channel", str(rfInfo.channel)), ("RF auto retransmit count", str(rfInfo.arc)), ("RF data rate", protocol.data_rate_to_str(rfInfo.data_rate)), ] firmware_settings = [ ("Firmware version", "{}.{}.{}".format(firmwareInfo.version_major, firmwareInfo.version_minor, firmwareInfo.version_patch)), ("Firmware build date", str(datetime.datetime.fromtimestamp(firmwareInfo.timestamp))), ("Firmware git hash", "{:08x}".format(firmwareInfo.git_hash)), ("Layout storage size", firmwareInfo.layout_flash_size), ("Bootloader VID", "{:04x}".format(firmwareInfo.bootloader_vid)), ("Bootloader PID", "{:04x}".format(firmwareInfo.bootloader_pid)), ("Support scanning", protocol.has_fw_support_scanning(firmwareInfo)), ("Support scanning col to row", protocol.has_fw_support_scanning_col_row(firmwareInfo)), ("Support scanning row to col", protocol.has_fw_support_scanning_row_col(firmwareInfo)), ("Media keys", protocol.has_fw_support_key_media(firmwareInfo)), ("Mouse keys", protocol.has_fw_support_key_mouse(firmwareInfo)), ("Layer keys", protocol.has_fw_support_key_layers(firmwareInfo)), ("Sticky keys", protocol.has_fw_support_key_sticky(firmwareInfo)), ("Tap keys", protocol.has_fw_support_key_tap(firmwareInfo)), ("Hold keys", protocol.has_fw_support_key_hold(firmwareInfo)), ("Support 6KRO", protocol.has_fw_support_6kro(firmwareInfo)), ("Support NKRO", protocol.has_fw_support_key_hold(firmwareInfo)), ("Support indicator LEDs", protocol.has_fw_support_led_indicators(firmwareInfo)), ("Support LED backlighting", protocol.has_fw_support_led_backlighting(firmwareInfo)), ("Support ws2812 LEDs", protocol.has_fw_support_led_ws2812(firmwareInfo)), ("Support USB", protocol.has_fw_support_usb(firmwareInfo)), ("Support nRF24 wireless", protocol.has_fw_support_wireless(firmwareInfo)), ("Support Unifying", protocol.has_fw_support_unifying(firmwareInfo)), ("Support I2C", protocol.has_fw_support_i2c(firmwareInfo)), ("Support Bluetooth", protocol.has_fw_support_bluetooth(firmwareInfo)), ] self.info_window = DeviceInformationWindow(self, header, device_settings, firmware_settings) self.info_window.setModal(True) self.info_window.exec_() self.deviceListWidget.updateList()