Exemplo n.º 1
0
    def _make_interrupt_transfer(self,
                                 endpoint,
                                 data,
                                 size,
                                 actual_length,
                                 timeout=2000):
        """
        Read message from USB device
        :param endpoint: Endpoint address to listen on.
        :param data: Variable to hold received data.
        :param size: Size expected to be received.
        :param actual_length: Actual length received.
        """
        if self.interface is None:
            raise USBDeviceInterfaceNotClaimedError(self.dev_key)

        ret = usb.interrupt_transfer(
            self.__libusb_dev_handle__,  # ct.c_char_p
            endpoint,  # ct.ubyte
            ct.cast(data, ct.POINTER(ct.c_ubyte)),  # ct.POINTER(ct.c_ubyte)
            size,
            ct.cast(actual_length,
                    ct.POINTER(ct.c_int)),  # ct.POINTER(ct.c_int)
            timeout)  # ct.c_uint32

        if ret >= 0:
            _logger.debug(f'Read {size} ' +
                          _('bytes from device').format(size) +
                          f' {self.dev_key}.')
            return True

        usb_error(ret,
                  _('Failed to communicate with device') + f' {self.dev_key}.')
        return False
Exemplo n.º 2
0
def run():
    # Set global debug value and setup application logging.
    ToolContextManager.initialize_logging(tool_cmd)
    parser = ToolContextManager.get_argparser(tool_cmd, tool_desc)
    parser.add_argument('-c',
                        '--class-id',
                        help=_('filter by device class id'),
                        type=str)
    parser.add_argument('-d',
                        '--descriptors',
                        help=_('Show device descriptor values.'),
                        default=False,
                        action='store_true')

    args = parser.parse_args()

    # Verify class id is valid by looking in the Enum by value.
    if args.device_class_id:
        try:
            DeviceClassID(args.device_class_id)
        except ValueError:
            _logger.error('Invalid class id argument value.')
            return -1

    with ToolContextManager(tool_cmd, args) as tool_env:
        process = ListDevicesClass(args, tool_env)
        exit_code = process.run()
        return exit_code
Exemplo n.º 3
0
    def validate_config_base(self, config_file, resource_types):
        """
        Validate the configuration file against the base schema.
        :param config_file: Absolute path to configuration json file.
        :param resource_types: list of strings representing valid 'resourceType' values.
        :return: config dict.
        """
        # Read the base schema, all json configs must validate against this schema.
        base_schema = self.load_config_schema('base.schema')
        if not base_schema:
            return None

        try:
            with open(config_file) as h:
                config = json.loads(h.read())
        except JSONDecodeError:
            _logger.error(_('Configuration file is not valid JSON.'))
            return None

        try:
            validate(config, base_schema)
        except ValidationError as e:
            _logger.error(
                _('Configuration file did not validate against the base schema.'
                  ) + f'\n{e}')
            return None

        if config['resourceType'] not in resource_types:
            valid_types = ",".join(resource_types)
            _logger.error(
                _('Resource type does not match accepted types') +
                f' ({valid_types}).')
            return None

        return config
Exemplo n.º 4
0
 def get_argparser(tool_cmd, tool_desc):
     """
     :param tool_cmd: Tool command line id.
     :param tool_desc: Tool description.
     """
     # Setup program arguments.
     parser = argparse.ArgumentParser(prog=tool_cmd, description=tool_desc)
     parser.add_argument('--debug',
                         help=_('enable debug output'),
                         default=False,
                         action='store_true')
     parser.add_argument('--log-file',
                         help=_('write output to a log file'),
                         default=False,
                         action='store_true')
     parser.add_argument('-q',
                         '--quiet',
                         help=_('suppress normal output'),
                         default=False,
                         action='store_true')
     parser.add_argument('--bus',
                         help=_('filter by usb device bus number'),
                         type=int,
                         default=None)
     parser.add_argument('--address',
                         help=_('filter by usb device address number'),
                         type=int,
                         default=None)
     return parser
Exemplo n.º 5
0
    def _find_devices(self):
        """ Find all attached USB devices that match the filter. """
        self.error = False
        self.device_count = 0
        self._usb_devices = list()

        # Get device list from libusb.
        dev_list = ct.POINTER(ct.POINTER(usb.device))()
        cnt = usb.get_device_list(None, ct.byref(dev_list))
        if cnt < 0:
            usb_error(cnt, _('Failed to find attached USB devices.'))
            self.error = True
            return

        _logger.debug(_('Vendor filters: ') + ', '.join(self._filters))
        _logger.debug(_('Searching for Ultimarc USB devices...'))
        for dev in dev_list:
            if not dev:  # We must always look for the Null device and quit the loop.
                break

            dev_desc = usb.device_descriptor()
            ret = usb.get_device_descriptor(dev, ct.byref(dev_desc))
            if ret != usb.LIBUSB_SUCCESS:
                usb_error(ret, _('failed to get USB device descriptor.'))
                continue

            if not self._filters or f'{dev_desc.idVendor:04x}' in self._filters:
                ud = USBDeviceInfo(dev, dev_desc)
                self._usb_devices.append(ud)
                self.device_count += 1

        _logger.debug(_('Device search complete.'))

        usb.free_device_list(dev_list, 1)
Exemplo n.º 6
0
    def _get_device_handle(self):
        """
        Return the raw LibUSB device handle pointer for our device. Must only be called from __enter__().
        :return: LibUSBDeviceHandle
        """
        # Get device list from libusb.
        self.__dev_list__ = ct.POINTER(ct.POINTER(usb.device))()
        cnt = usb.get_device_list(None, ct.byref(self.__dev_list__))
        if cnt < 0:
            usb_error(cnt, _('Failed to find attached USB devices.'))
            return None

        for dev in self.__dev_list__:
            if not dev:  # We must always look for the Null device and quit the loop.
                break

            dev_desc = usb.device_descriptor()
            ret = usb.get_device_descriptor(dev, ct.byref(dev_desc))
            if ret != usb.LIBUSB_SUCCESS:
                usb_error(ret, _('failed to get USB device descriptor.'))
                continue

            if self.vendor_id == dev_desc.idVendor and self.product_id == dev_desc.idProduct and \
                    self.bus == usb.get_bus_number(dev) and self.address == usb.get_device_address(dev):
                dev_handle = ct.POINTER(usb.device_handle)()
                ret = usb.open(dev, ct.byref(dev_handle))
                if ret == usb.LIBUSB_SUCCESS:
                    return dev_handle
                usb_error(ret,
                          _('Failed to open device') + f' {self.dev_key}.')

        self._close_device_list_handle()
        _logger.debug(
            _('Device was not found on host when trying to open it.'))
        return None
Exemplo n.º 7
0
    def write_alt(self,
                  b_request,
                  report_id,
                  w_index,
                  data=None,
                  size=None,
                  request_type=USBRequestType.REQUEST_TYPE_CLASS,
                  recipient=USBRequestRecipient.RECIPIENT_INTERFACE):
        """
        Write message to USB device.
        :param b_request: Request field for the setup packet
        :param report_id: report_id portion of the Value field for the setup packet.
        :param w_index: Index field for the setup packet
        :param data: ctypes structure class.
        :param size: size of message.
        :param request_type: USBRequestType enum value.
        :param recipient: USBRequestRecipient enum value.
        :return: True if successful otherwise False.
        """
        if self.interface is None:
            raise USBDeviceInterfaceNotClaimedError(self.dev_key)
        if not isinstance(request_type, USBRequestType):
            raise ValueError(
                'Request type argument must be a USBRequestType enum value.')
        if not isinstance(recipient, USBRequestRecipient):
            raise ValueError(
                'Request type argument must be a USBRequestRecipient enum value.'
            )

        if isinstance(report_id, int):
            report_id = ct.c_uint8(report_id)

        # Combine direction, request type and recipient together.
        request_type = USBRequestDirection.ENDPOINT_OUT | request_type | recipient
        w_value = ct.c_uint16(USB_REPORT_TYPE_OUT.value | report_id.value)

        payload = (ct.c_ubyte * 5)(0)
        offset = 1 if report_id else 0
        pos = 0

        if report_id:
            payload[0] = report_id

        while pos < size:
            payload_size = 4 if size - pos > 4 else size - pos
            ct.memmove(
                ct.addressof(payload) + offset, ct.byref(data, pos),
                payload_size)

            ret = self._make_control_transfer(request_type, b_request, w_value,
                                              w_index, ct.byref(payload),
                                              payload_size + offset)
            pos += payload_size
            _logger.debug(_(' '.join(hex(x) for x in payload)))

        _logger.debug(
            _('Write operation complete, wrote {} bytes.').format(pos))
        return ret
Exemplo n.º 8
0
    def set_pin(self, pin_config):
        """ Write a pin to the current Mini-pac device """
        pin = pin_config[0]
        # Get the current configuration from the device
        cur_config = self.get_current_configuration()
        macros = self._create_macro_array_(cur_config)

        action_index, alternate_action_index, shift_index = PinMapping[pin]
        action = pin_config[1].upper()
        cur_config.bytes[action_index] = 0
        if action in IPACSeriesMapping:
            cur_config.bytes[action_index] = IPACSeriesMapping[action]
        else:
            macro_val = 0xe0
            for x in macros:
                if x['name'].upper() == action:
                    cur_config.bytes[action_index] = macro_val
                else:
                    macro_val += 1
        if cur_config.bytes[action_index] == 0:
            _logger.info(_(f'{pin} action "{action}" is not a valid value'))

        # Pin alternate action
        alternate_action = pin_config[2].upper()
        # Empty string means no value
        if len(alternate_action) > 0:
            cur_config.bytes[alternate_action_index] = 0
            if alternate_action in IPACSeriesMapping:
                cur_config.bytes[alternate_action_index] = IPACSeriesMapping[
                    alternate_action]
            else:
                macro_val = 0xe0
                for x in macros:
                    if x['name'].upper() == alternate_action:
                        cur_config.bytes[alternate_action_index] = macro_val
                    else:
                        macro_val += 1
            if cur_config.bytes[alternate_action_index] == 0:
                _logger.info(
                    _(f'{pin} alternate action "{alternate_action}" is not a valid value'
                      ))
        else:
            # No Alternate Value
            cur_config.bytes[alternate_action_index] = 0

        # Pin designated as shift
        cur_config.bytes[shift_index] = 0x40 if pin_config[3].lower() in [
            'true', '1', 't', 'y'
        ] else 0x0

        # Header - Setup to send back to device
        cur_config.header.type = 0x50
        cur_config.header.byte_2 = 0xdd
        cur_config.header.byte_3 = 0x0f

        return self.write_alt(USBRequestCode.SET_CONFIGURATION,
                              int(0x03), MINI_PAC_INDEX, cur_config,
                              ct.sizeof(cur_config))
Exemplo n.º 9
0
    def run(self):
        """
        Main program process
        :return: Exit code value
        """
        # write program main process here after setting 'tool_cmd' and 'tool_desc'...

        # Get devices we want to work with based on filters.
        devices = [
            dev for dev in self.tool_env.devices.filter(
                class_id=DeviceClassID.MiniPac,
                bus=self.args.bus,
                address=self.args.address)
        ]

        if not devices:
            _logger.error(_('No Mini-PAC devices found, aborting'))
            return -1

        # Set debounce value
        if self.args.set_debounce:
            for dev in devices:
                with dev as dev_h:
                    response = dev_h.set_debounce(self.args.set_debounce)
                    if response:
                        _logger.info(
                            f'{dev.dev_key} ({dev.bus},{dev.address}): ' +
                            _('debounce successfully applied to device.'))

        # Get config from device
        if self.args.get_config:
            for dev in devices:
                with dev as dev_h:
                    indent = int(
                        self.args.indent) if self.args.indent else None
                    response = dev_h.get_device_config(indent, self.args.file)
                    _logger.info(response)

        # Set mini-pac device configuration from a configuration file
        if self.args.set_config:
            for dev in devices:
                with dev as dev_h:
                    use_current = self.args.current
                    dev_h.set_config(self.args.set_config, use_current)
                    _logger.info(
                        f'{dev.dev_key} ({dev.bus},{dev.address}): ' +
                        _('configuration successfully applied to device.'))

        if self.args.set_pin:
            for dev in devices:
                with dev as dev_h:
                    dev_h.set_pin(self.args.set_pin)
                    _logger.info(f'{dev.dev_key} ({dev.bus},{dev.address}): ' +
                                 _('pin successfully applied to device.'))

        return 0
Exemplo n.º 10
0
    def __exit__(self, exc_type, exc_val, exc_tb):
        """ Clean up or close everything we need to """
        if self.__dev_handle__:
            _logger.debug(_('Closing USB device') + f' {self.dev_key}.')
            usb.close(self.__dev_handle__)
            self.__dev_handle__ = None

        self._close_device_list_handle()

        if exc_type is not None:
            # print((traceback.format_exc()))
            _logger.error(
                _('USB device encountered an unexpected error, quitting.'))
Exemplo n.º 11
0
    def __init__(self, vendor_filter: list = None):
        """
        :param vendor_filter: list of vendor/manufacturer IDs to capture.
        """
        if vendor_filter:
            if not isinstance(vendor_filter, list):
                raise TypeError(_('USB device filter must be a list.'))
            for vendor_id in vendor_filter:
                if not isinstance(vendor_id, str) or len(vendor_id) != 4:
                    raise ValueError(
                        _("Invalid USB vendor/manufacturer id") +
                        f' ({vendor_id}).')
            self._filters = vendor_filter

        self._find_devices()
Exemplo n.º 12
0
class AimTrakDevice(USBDeviceHandle):
    """
    Manage an AimTrak light gun device
    """
    class_id = 'aimtrak'  # Used to match/filter devices.
    class_descr = _('Aimtrak Light Gun')
    pass
Exemplo n.º 13
0
    def read_interrupt(self, endpoint, response, uses_report_id=True):
        """
        Read response from USB device on the interrupt endpoint.
        :param endpoint: Endpoint to receive message on.
        :param response: Variable holding the complete response from the device.
        :param uses_report_id: True if report_id is in the message.
        """
        if self.interface is None:
            raise USBDeviceInterfaceNotClaimedError(self.dev_key)

        actual_length = ct.c_int(0)
        length = 5 if uses_report_id else 4  # Expecting the report_id in the message
        payload = (ct.c_ubyte * length)(0)
        payload_ptr = ct.byref(payload)

        # Here don't add the report_id into the response structure, 4 instead of 5
        for pos in range(0, ct.sizeof(response), 4 if uses_report_id else 5):
            self._make_interrupt_transfer(endpoint, payload_ptr, length,
                                          ct.byref(actual_length))
            # Remove report_id (byte 0) if it is used
            actual_length = actual_length \
                if actual_length == length and not uses_report_id else ct.c_int(actual_length.value - 1)
            ct.memmove(
                ct.addressof(response) + pos,
                ct.byref(payload, 1) if uses_report_id else ct.byref(payload),
                actual_length.value)
            _logger.debug(_(' '.join(hex(x) for x in payload)))

        return response
Exemplo n.º 14
0
    def __exit__(self, exc_type, exc_val, exc_tb):
        """ Clean up or close everything we need to """
        self._env_config_obj.cleanup()

        remove_pidfile(self._command)

        if exc_type is not None:
            print((traceback.format_exc()))
            _logger.error(_('tool encountered an unexpected error, quitting.'))
            exit(1)
Exemplo n.º 15
0
    def _make_control_transfer(self,
                               request_type,
                               b_request,
                               w_value,
                               w_index,
                               data,
                               size,
                               timeout=2000):
        """
        Read/Write data from USB device.
        :param request_type: Request type value. Combines direction, type and recipient enum values.
                        Bit 7: Request direction (0=Host to device - Out, 1=Device to host - In).
                        Bits 5-6: Request type (0=standard, 1=class, 2=vendor, 3=reserved).
                        Bits 0-4: Recipient (0=device, 1=interface, 2=endpoint,3=other).
        :param b_request: Request field for the setup packet. The actual request, see USBRequestCodes Enum.
        :param w_value: Value field for the setup packet. A word-size value that varies according to the request.
                        For example, in the CLEAR_FEATURE request the value is used to select the feature, in the
                        GET_DESCRIPTOR request the value indicates the descriptor type and in the SET_ADDRESS
                        request the value contains the device address.
        :param w_index: Index field for the setup packet. A word-size value that varies according to the request.
                        The index is generally used to specify an endpoint or an interface.
        :param data: ctypes structure class.
        :param size: size of data.
        :return: True if successful otherwise False.
        https://www.jungo.com/st/support/documentation/windriver/802/wdusb_man_mhtml/node55.html#SECTION001213000000000000000
        """
        if self.interface is None:
            raise USBDeviceInterfaceNotClaimedError(self.dev_key)
        if not isinstance(b_request, USBRequestCode):
            raise ValueError(
                'b_request argument must be USBRequestCode enum value.')

        ret = usb.control_transfer(
            self.__libusb_dev_handle__,  # ct.c_char_p
            request_type,  # ct.c_uint8
            b_request,  # ct.c_uint8, must be a USBRequestCode Enum value.
            w_value,  # ct.c_uint16
            w_index,  # ct.c_uint16
            ct.cast(data, ct.POINTER(ct.c_ubyte)),  # ct.POINTER(ct.c_ubyte)
            ct.c_uint16(size),  # ct.c_uint16
            timeout)  # ct.c_uint32

        if ret >= 0:
            if request_type & USBRequestDirection.ENDPOINT_IN:
                _logger.debug('Read {} bytes from'.format(size) +
                              f' {self.dev_key}.')
            else:
                _logger.debug('Wrote {} bytes to'.format(size) +
                              f' {self.dev_key}.')
            return True

        usb_error(ret,
                  _('Failed to communicate with device') + f' {self.dev_key}.')
        return False
Exemplo n.º 16
0
    def to_json_str(self, pac_struct):
        """ Converts a PacStruct to a json object """
        json_obj = {
            'schemaVersion': 2.0,
            'resourceType': 'mini-pac-pins',
            'deviceClass': self.class_id
        }

        # header configuration
        header = PacConfigUnion()
        header.asByte = pac_struct.header.byte_4
        json_obj['debounce'] = get_ipac_series_debounce_key(
            header.config.debounce)

        # macros
        macros = self._create_macro_array_(pac_struct)
        if len(macros):
            json_obj['macros'] = macros

        # pins
        pins = []
        for key in PinMapping:
            action_index, alternate_action_index, shift_index = PinMapping[key]
            pin = {}
            if pac_struct.bytes[action_index]:
                pin['name'] = key
                pin['action'] = get_ipac_series_mapping_key(
                    pac_struct.bytes[action_index])
                if pin['action'] is None:
                    mi = get_ipac_series_macro_mapping_index(
                        pac_struct.bytes[action_index])
                    if mi is not None:
                        pin['action'] = macros[mi]['name']
                    else:
                        _logger.debug(_(f'{key} action is not a valid value'))
                if pac_struct.bytes[alternate_action_index]:
                    alt_action = get_ipac_series_mapping_key(
                        pac_struct.bytes[alternate_action_index])
                    if alt_action is None:
                        mi = get_ipac_series_macro_mapping_index(
                            pac_struct.bytes[alternate_action_index])
                        if mi is not None:
                            pin['alternate_action'] = macros[mi]['name']
                    else:
                        pin['alternate_action'] = alt_action
                if pac_struct.bytes[shift_index]:
                    pin['shift'] = True
                pins.append(pin)
        json_obj['pins'] = pins

        return json_obj if self.validate_config(json_obj,
                                                'mini-pac.schema') else None
Exemplo n.º 17
0
 def get_state(self):
     """
     Return the USB button click state.
     :return: 1 if clicked otherwise 0, None if error.
     """
     # TODO: Can't seem to make this work to return the button click state.  See USBButtonGetState() in PacDrive.cpp.
     data = USBButtonColorStruct(0x02, RGBValueStruct(0x0, 0x0, 0x0))
     ret = self.read(USBRequestCode.CLEAR_FEATURE, USBButtonReportID,
                     USBButtonWIndex, data, ct.sizeof(data))
     if ret:
         return data.red
     _logger.error(_('Failed to read usb button state.'))
     return None
Exemplo n.º 18
0
 def get_device_config(self, indent=None, file=None):
     """ Return a json string of the device configuration """
     config = self.get_current_configuration()
     json_obj = self.to_json_str(config)
     if file:
         try:
             with open(file, 'w') as h:
                 json.dump(json_obj, h, indent=indent)
         except FileNotFoundError as err:
             return err
         return _('Wrote Mini-pac configuration to file.')
     else:
         return json.dumps(json_obj, indent=indent) if config else None
Exemplo n.º 19
0
 def get_color(self):
     """
     Return override USB button RGB color.  Will return (0, 0, 0) if the device has not yet been written
     to using the set_color() method.
     :return: (Integer, Integer, Integer) or None
     """
     data = USBButtonColorStruct(0x01, RGBValueStruct(0x0, 0x0, 0x0))
     ret = self.read(USBRequestCode.CLEAR_FEATURE, USBButtonReportID,
                     USBButtonWIndex, data, ct.sizeof(data))
     if ret:
         return data.red, data.green, data.blue
     _logger.error(_('Failed to read color data from usb button.'))
     return None, None, None
Exemplo n.º 20
0
    def set_color(self, red, green, blue):
        """
        Set USB button color, overrides the current device configuration released color.
        :param red: integer between 0 and 255
        :param green: integer between 0 and 255
        :param blue: integer between 0 and 255
        :return: True if successful otherwise False.
        """
        for color in [red, green, blue]:
            if not isinstance(color, int) or not 0 <= color <= 255:
                raise ValueError(_('Color argument value is invalid'))

        data = USBButtonColorStruct(0x01, RGBValueStruct(red, green, blue))
        return self.write(USBRequestCode.SET_CONFIGURATION, USBButtonReportID,
                          USBButtonWIndex, data, ct.sizeof(data))
Exemplo n.º 21
0
    def get_descriptor_string(self, index):
        """
        Return the String value of a device descriptor property.
        :param index: integer
        :return: String or None
        """
        buf = ct.create_string_buffer(1024)
        ret = usb.get_string_descriptor_ascii(
            self.__libusb_dev_handle__, index,
            ct.cast(buf, ct.POINTER(ct.c_ubyte)), ct.sizeof(buf))
        if ret > 0:
            result = buf.value.decode('utf-8')
            return result

        usb_error(ret,
                  _('failed to get descriptor property field string value.'),
                  debug=True)
        return None
Exemplo n.º 22
0
    def __init__(self, command, args):
        """
        Initialize Tool Context Manager
        :param command: command name
        :param args: parsed argparser commandline arguments object.
        """
        if not command:
            _logger.error(_('command not set, aborting.'))
            exit(1)

        self._command = command
        # The Environment dict is where we can setup any information related to all tools.
        self._env = {'command': command, 'devices': USBDevices(_VENDOR_FILTER)}

        write_pidfile_or_die(command)

        if not self._env:
            remove_pidfile(command)
            exit(1)
Exemplo n.º 23
0
    def validate_config(self, config, schema_file):
        """
        Validate a configuration dict against a schema file.
        :param config: dict
        :param schema_file: relative or abspath of schema.
        :return: True if valid otherwise False.
        """
        schema = self.load_config_schema(schema_file)
        if not schema:
            return False

        try:
            validate(config, schema)
        except ValidationError as e:
            _logger.error(
                _('Configuration file did not validate against config schema.')
            )
            _logger.error(e)
            return False

        return True
Exemplo n.º 24
0
    def __enter__(self):
        """ Return object with properties set to config values """
        _logger.debug(_('Opening USB device') + f' {self.dev_key}')

        dev_handle = self._get_device_handle()
        if not dev_handle:
            raise USBDeviceNotFoundError(self.dev_key)

        # We need to claim the USB device interface.
        # usb.set_auto_detach_kernel_driver(dev_handle, 1)
        # status = usb.claim_interface(dev_handle, 0)
        # if status != usb.LIBUSB_SUCCESS:
        #     usb.close(dev_handle)
        #     self._close_device_list_handle()
        #     raise USBDeviceClaimInterfaceError(self.dev_key)

        self._close_device_list_handle()
        self.__dev_handle__ = dev_handle
        self.__dev_handle_obj__ = self.__dev_class__(self.__dev_handle__,
                                                     self.dev_key)
        return self.__dev_handle_obj__
Exemplo n.º 25
0
    def filter(self, class_id=None, bus=None, address=None):
        """
        Return iterator for all devices that match the filter.
        :param class_id: string
        :param bus: integer
        :param address: integer
        :return: iter(list)
        """
        if (class_id and not isinstance(class_id, (str, DeviceClassID))) or (bus and not isinstance(bus, int)) or \
                (address and not isinstance(address, int)):
            raise ValueError(_('Invalid filter method argument'))
        devices = list()
        for dev in self._usb_devices:
            if class_id and dev.class_id != (class_id if isinstance(
                    class_id, str) else class_id.value):
                continue
            if bus and dev.bus != bus:
                continue
            if address and dev.address != address:
                continue
            devices.append(dev)

        return iter(devices)
Exemplo n.º 26
0
    def load_config_schema(self, schema_file):
        """
        Load the requested schema file.
        :param schema_file: Schema file name only, no path included.
        :return: schema dict.
        """
        schema_paths = [
            './ultimarc/schemas', '../ultimarc/schemas',
            '../../ultimarc/schemas', '../schemas', './schemas'
        ]
        schema_path = None

        for path in schema_paths:
            schema_path = os.path.abspath(os.path.join(path, schema_file))
            if os.path.exists(schema_path):
                break

        if not schema_path:
            _logger.error(_('Unable to locate schema directory.'))
            return None

        with open(schema_path) as h:
            return json.loads(h.read())
Exemplo n.º 27
0
    def set_debounce(self, debounce):
        """ Set debounce value to the current Mini-pac device """
        val = debounce.lower()
        if val in IPACSeriesDebounce:
            # Get the current configuration from the device
            cur_config = self.get_current_configuration()

            header = PacConfigUnion()
            header.config.debounce = IPACSeriesDebounce[val]

            cur_config.header.byte_4 = header.asByte
        else:
            _logger.info(_(f'"{debounce}" is not a valid debounce value'))
            return None

        # Header - Setup to send back to device
        cur_config.header.type = 0x50
        cur_config.header.byte_2 = 0xdd
        cur_config.header.byte_3 = 0x0f

        return self.write_alt(USBRequestCode.SET_CONFIGURATION,
                              int(0x03), MINI_PAC_INDEX, cur_config,
                              ct.sizeof(cur_config))
Exemplo n.º 28
0
    def list_devices_found(self):
        """ List all the devices found. """
        # Find all Ultimarc USB devices.
        _logger.info(_('Device classes found') + ':')
        for cat in self.env.devices.get_device_classes():
            _logger.info(f' {cat}')

        _logger.info('\n' + _('Devices') + ':')
        for dev in self.get_devices():
            _logger.info(f' {dev}')

        if not self.args.descriptors:
            return

        try:
            desc_string_fields = [
                'iManufacturer', 'iProduct', 'iSerialNumber', 'idProduct',
                'idVendor'
            ]
            for dev in self.get_devices():
                _logger.info('')
                # Open device.
                with dev as dev_h:
                    _logger.info(
                        _('Showing device descriptor properties for') +
                        f' {dev.dev_key}')
                    _logger.info('  ' + _('Bus') + f': {dev.bus}')
                    _logger.info('  ' + _('Address') + f': {dev.address}')
                    for fld in dev_h.descriptor_fields:
                        desc_val = dev_h.get_descriptor_value(fld)
                        if fld in desc_string_fields:
                            desc_str = dev_h.get_descriptor_string(
                                dev_h.get_descriptor_value(fld)) or ''
                            _logger.info(
                                f'  {fld}: {desc_val:04x} (desc idx: 0x{desc_val:04x}, str: "{desc_str}")'
                            )
                        else:
                            _logger.info(f'  {fld}: {desc_val}')

        except (USBDeviceNotFoundError, USBDeviceClaimInterfaceError) as e:
            _logger.error(
                _('An error occurred while inspecting device') +
                f' {e.dev_key}.')
Exemplo n.º 29
0
    def run(self):
        """
        Main program process
        :return: Exit code value
        """
        # Get devices we want to work with based on filters.
        devices = [
            dev for dev in self.env.devices.filter(
                class_id=DeviceClassID.USBButton,
                bus=self.args.bus,
                address=self.args.address)
        ]

        if not devices:
            _logger.error(_('No USB button devices found, aborting'))
            return -1

        # See if we are setting a color from the command line args.
        if self.args.set_color:
            match = re.match(_RGB_STRING_REGEX, self.args.set_color)
            red, green, blue = match.groups()
            for dev in devices:
                with dev as dev_h:
                    dev_h.set_color(int(red), int(green), int(blue))
                    _logger.info(f'{dev.dev_key} ({dev.bus},{dev.address}): ' +
                                 _('Color') + f': RGB({red},{green},{blue}).')

        # Return the current color RGB values.
        elif self.args.get_color:
            for dev in devices:
                with dev as dev_h:
                    red, green, blue = dev_h.get_color()
                    if red is not None:
                        _logger.info(
                            f'{dev.dev_key} ({dev.bus},{dev.address}): ' +
                            _('Color') + f': RGB({red},{green},{blue}).')

        # Set a random RGB color.
        elif self.args.set_random_color:
            for dev in devices:
                red = random.randrange(255)
                green = random.randrange(255)
                blue = random.randrange(255)
                with dev as dev_h:
                    dev_h.set_color(red, green, blue)
                _logger.info(f'{dev.dev_key} ({dev.bus},{dev.address}): ' +
                             _('randomly set button color to') +
                             f' RGB({red},{green},{blue}).')

        # Apply a usb button config.
        elif self.args.set_config:
            for dev in devices:
                with dev as dev_h:
                    application = ConfigApplication.temporary if self.args.temporary else ConfigApplication.permanent
                    if dev_h.set_config(self.args.set_config, application):
                        _logger.info(
                            f'{dev.dev_key} ({dev.bus},{dev.address}): ' +
                            _('configuration successfully applied to device.'))
                    else:
                        _logger.error(
                            f'{dev.dev_key} ({dev.bus},{dev.address}): ' +
                            _('failed to apply configuration to device.'))

        return 0
Exemplo n.º 30
0
def run():
    # Set global debug value and setup application logging.
    ToolContextManager.initialize_logging(tool_cmd)
    parser = ToolContextManager.get_argparser(tool_cmd, tool_desc)

    # --set-color --get-color --load-config --export-config
    parser.add_argument('--set-color',
                        help=_('set usb button color with RGB value'),
                        type=str,
                        default=None,
                        metavar='INT,INT,INT')
    parser.add_argument('--set-random-color',
                        help=_('randomly set usb button color'),
                        default=False,
                        action='store_true')
    parser.add_argument('--get-color',
                        help=_('output current usb button color RGB value'),
                        default=False,
                        action='store_true')
    parser.add_argument('--set-config',
                        help=_('Set button config from config file.'),
                        type=str,
                        default=None,
                        metavar='CONFIG-FILE')
    parser.add_argument('--temporary',
                        help=_('Apply config until device unplugged.'),
                        default=False,
                        action='store_true')

    args = parser.parse_args()

    num_args = sum([
        bool(args.set_color), args.set_random_color, args.get_color,
        bool(args.set_config)
    ])
    if num_args == 0:
        _logger.warning(_('Nothing to do.'))
        return 0
    if num_args > 1 and (not args.set_config or not args.temporary):
        # Enhance this check and provide better feedback on which arguments are mutually exclusive.
        _logger.error(
            _('More than one mutually exclusive argument specified.'))
        return -1

    if args.set_color:
        if not re.match(_RGB_STRING_REGEX, args.set_color):
            _logger.error(
                _('Invalid RGB value found for --set-color argument.'))
            return -1

    if not args.set_config and args.temporary:
        _logger.error(
            _('The --temporary argument can only be used with the --set-config argument.'
              ))
        return -1

    if args.set_config:
        # Always force absolute path for config files.
        args.set_config = os.path.abspath(args.set_config)
        if not os.path.exists(args.set_config):
            _logger.error(
                _('Unable to find configuration file specified in argument.'))
            return -1

    with ToolContextManager(tool_cmd, args) as tool_env:
        process = USBButtonClass(args, tool_env)
        exit_code = process.run()
        return exit_code