Addressing of specific connected device.

So, multiple devices can be connected at the same time and one can get a list and choose via command line parameter.
pull/8/head^2
Ben Sartori 7 months ago
parent 90266f4cc8
commit 6de3b1dce8
  1. 225
      lednamebadge.py

@ -409,6 +409,43 @@ class SimpleTextAndIcons:
class WriteMethod:
def __init__(self):
self.devices = None
def __del__(self):
self.close()
def open(self, device_id):
if self.is_ready():
self.devices = self._get_available_devices()
if self.devices and len(self.devices) > 0:
if device_id == 'auto' and len(self.devices) > 0:
# /***/ was, wenn kein gerät angeschlossen?
device_id = sorted(self.devices.keys())[0]
elif device_id == 'list':
self.print_devices()
sys.exit(0)
elif device_id not in self.devices.keys():
print("Device ID '%s' is unknown. Please choose from:" % (device_id,))
self.print_devices()
sys.exit(1)
return self._open(device_id)
# /***/ was wenn kein Device? -> Alle Meldungen hier raus in die vorbereitung
return None
def close(self):
raise NotImplementedError()
def print_devices(self):
for k, d in self.devices.items():
print("'%s': %s" % (k, d[0]))
def _open(self, device_id):
raise NotImplementedError()
def _get_available_devices(self):
raise NotImplementedError()
@staticmethod
def add_padding(buf, blocksize):
need_padding = len(buf) % blocksize
@ -421,6 +458,9 @@ class WriteMethod:
print("Writing more than %d bytes damages the display!" % (maxsize,))
sys.exit(1)
def is_ready(self):
raise NotImplementedError()
def has_device(self):
raise NotImplementedError()
@ -442,15 +482,62 @@ class WriteLibUsb(WriteMethod):
except:
pass
def __init__(self, endpoint):
def __init__(self):
WriteMethod.__init__(self)
self.description = None
self.dev = None
self.endpoint = None
def _open(self, device_id):
self.description = self.devices[device_id][0]
self.dev = self.devices[device_id][1]
self.endpoint = self.devices[device_id][2]
print("Libusb device initialized")
return True
def close(self):
if self.devices:
for k, d in self.devices.items():
d[1].reset()
WriteLibUsb.usb.util.dispose_resources(d[1])
self.description = None
self.dev = None
if WriteLibUsb._module_loaded:
self.dev = WriteLibUsb.usb.core.find(idVendor=0x0416, idProduct=0x5020)
if self.dev:
print("Libusb device initialized")
self.endpoint = None
print("Libusb: device resources freed")
def _get_available_devices(self):
if not self.is_ready():
return {}
devs = WriteLibUsb.usb.core.find(idVendor=0x0416, idProduct=0x5020, find_all=True)
devices = {}
for d in devs:
try:
# win32: NotImplementedError: is_kernel_driver_active
if d.is_kernel_driver_active(0):
d.detach_kernel_driver(0)
except:
pass
try:
d.set_configuration()
except(WriteLibUsb.usb.core.USBError):
print("No write access to device!")
# /***/
print("Maybe, you have to run this program with administrator rights.")
if sys.platform.startswith('linux'):
print("* Try with sudo or add a udev rule like described in README.md.")
sys.exit(1)
@staticmethod
def is_ready():
cfg = d.get_active_configuration()[0, 0]
eps = WriteLibUsb.usb.util.find_descriptor(cfg, find_all=True, custom_match = lambda e: \
WriteLibUsb.usb.util.endpoint_direction(e.bEndpointAddress) == WriteLibUsb.usb.util.ENDPOINT_OUT)
for ep in eps:
id = "%d:%d:%d" % (d.bus, d.address, ep.bEndpointAddress)
descr = "'%s %s' (bus=%d dev=%d endpoint=%d)" % (d.manufacturer, d.product, d.bus, d.address, ep.bEndpointAddress)
devices[id] = (descr, d, ep)
return devices
def is_ready(self):
return WriteLibUsb._module_loaded
def has_device(self):
@ -476,14 +563,10 @@ class WriteLibUsb(WriteMethod):
print("* Try with sudo or add a udev rule like described in README.md.")
sys.exit(1)
print("Write using [%s %s] bus=%d dev=%d via libusb" %
(self.dev.manufacturer, self.dev.product, self.dev.bus, self.dev.address))
print("Write using %s via libusb" % (self.description))
for i in range(int(len(buf) / 64)):
time.sleep(0.1)
self.dev.write(1, buf[i * 64:i * 64 + 64])
WriteLibUsb.usb.util.dispose_resources(self.dev)
self.dev.reset()
self.dev = None
self.endpoint.write(buf[i * 64:i * 64 + 64])
class WriteUsbHidApi(WriteMethod):
@ -496,40 +579,55 @@ class WriteUsbHidApi(WriteMethod):
except:
pass
def __init__(self, endpoint):
def __init__(self):
WriteMethod.__init__(self)
self.description = None
self.path = None
self.dev = None
self.dev_info = None
if WriteUsbHidApi._module_loaded:
self.dev_info = WriteUsbHidApi.pyhidapi.hid_enumerate(0x0416, 0x5020)
if self.dev_info:
self.dev = WriteUsbHidApi.pyhidapi.hid_open_path(self.dev_info[0].path)
if self.dev:
print("Hidapi device initialized")
# alternative: self.dev = WriteUsbHidApi.pyhidapi.hid_open(0x0416, 0x5020)
def _open(self, device_id):
self.description = self.devices[device_id][0]
self.path = self.devices[device_id][1]
self.dev = WriteUsbHidApi.pyhidapi.hid_open_path(self.path)
if self.dev:
print("Hidapi device initialized")
@staticmethod
def is_ready():
return self.dev is not None
def close(self):
if self.dev is not None:
WriteUsbHidApi.pyhidapi.hid_close(self.dev)
self.description = None
self.path = None
self.dev = None
print("Hidapi: device resources freed")
def _get_available_devices(self):
device_infos = WriteUsbHidApi.pyhidapi.hid_enumerate(0x0416, 0x5020)
devices = {}
for d in device_infos:
id = "%s" % (str(d.path.decode('ascii')),)
descr = "'%s %s' (if=%d)" % (d.manufacturer_string, d.product_string, d.interface_number)
devices[id] = (descr, d.path)
return devices
def is_ready(self):
return WriteUsbHidApi._module_loaded
def has_device(self):
return self.dev is not None
def _write(self, buf):
if not self.dev or not self.dev_info:
if not self.dev:
return
print("Write using [%s %s] int=%d page=%s via hidapi" % (
self.dev_info[0].manufacturer_string, self.dev_info[0].product_string,
self.dev_info[0].interface_number, self.dev_info[0].usage_page))
print("Write using [%s] via hidapi" % (self.description,))
for i in range(int(len(buf)/64)):
# sendbuf must contain "report ID" as first byte. "0" does the job here.
sendbuf = array('B', [0])
# Then, put the 64 payload bytes into the buffer
sendbuf.extend(buf[i*64:i*64+64])
WriteUsbHidApi.pyhidapi.hid_write(self.dev, sendbuf)
WriteUsbHidApi.pyhidapi.hid_close(self.dev)
self.dev = None
class LedNameBadge:
@ -612,30 +710,27 @@ class LedNameBadge:
raise TypeError("Please give a list or tuple with at least one number: " + str(iterable))
@staticmethod
def write(buf, method = 'auto', endpoint = 'auto'):
def write(buf, method = 'auto', device_id = 'auto'):
"""Write the given buffer to the device.
It has to begin with a protocol header as provided by header() and followed by the bitmap data.
In short: the bitmap data is organized in bytes with 8 horizontal pixels per byte and 11 resp. 12
bytes per (8 pixels wide) byte-column. Then just put one byte-column after the other and one bitmap
after the other.
"""
write_method = LedNameBadge._find_write_method(method, endpoint)
write_method = LedNameBadge._find_write_method(method, device_id)
if write_method:
write_method.write(buf)
@staticmethod
def _find_write_method(method, endpoint):
if method is None:
method = 'auto'
if endpoint is None:
endpoint = 'auto'
def _find_write_method(method, device_id):
if method not in ('libusb', 'hidapi', 'auto'):
print("Unknown write method '%s'." % (method,))
print("Available options: 'libusb', 'hidapi' and 'auto' (default)")
sys.exit(1)
libusb = WriteLibUsb()
hidapi = WriteUsbHidApi()
# Python2 only with libusb
if method == 'auto':
if sys.version_info[0] < 3:
@ -647,7 +742,7 @@ class LedNameBadge:
elif sys.platform.startswith('win'):
method = 'libusb'
print("Selected method 'libusb' with Windows")
elif not WriteLibUsb.is_ready() and not WriteUsbHidApi.is_ready():
elif not libusb.is_ready() and not hidapi.is_ready():
if sys.version_info[0] < 3 or sys.platform.startswith('win'):
print("You need the usb.core module.")
LedNameBadge._print_libusb_install_hints()
@ -667,7 +762,7 @@ class LedNameBadge:
print("For MacOs, please use method 'hidapi' or 'auto'.")
print("Or help us implementing support for MacOs.")
sys.exit(1)
elif not WriteLibUsb.is_ready():
elif not libusb.is_ready():
LedNameBadge._print_libusb_install_hints()
sys.exit(1)
@ -679,30 +774,30 @@ class LedNameBadge:
elif sys.version_info[0] < 3:
print("Please use method 'libusb' or 'auto' with python-2.x because of https://github.com/jnweiger/led-badge-ls32/issues/9")
sys.exit(1)
elif not WriteUsbHidApi.is_ready():
elif not hidapi.is_ready():
LedNameBadge._print_hidapi_install_hints()
sys.exit(1)
if (method == 'auto' or method == 'hidapi') and WriteUsbHidApi.is_ready():
method_obj = WriteUsbHidApi(endpoint)
if method_obj.has_device():
if (method == 'auto' or method == 'hidapi'):
method_obj = hidapi
if method_obj.open(device_id):
return method_obj
if (method == 'auto' or method == 'libusb') and WriteLibUsb.is_ready():
method_obj = WriteLibUsb(endpoint)
if method_obj.has_device():
if (method == 'auto' or method == 'libusb'):
method_obj = libusb
if method_obj.open(device_id):
return method_obj
endpoint_str = ''
if endpoint != 'auto':
endpoint = int(endpoint)
endpoint_str = ' on endpoint %d' % (endpoint,)
device_id_str = ''
if device_id != 'auto':
device_id_str = ' with device_id %s' % (device_id,)
print("The device is not available with write method '%s'%s." % (method, endpoint_str))
print("The device is not available with write method '%s'%s." % (method, device_id_str))
print("* Is a led tag device with vendorID 0x0416 and productID 0x5020 connected?")
if endpoint != 'auto':
print("* Have you given the right endpoint?")
if device_id != 'auto':
print("* Have you given the right device_id?")
if sys.platform.startswith('linux'):
# /***/
print(" Try this to find the available endpoint addresses:")
print(' $ lsusb -d 0416:5020 -v | grep -i "endpoint.*out"')
print("* If it is connected and still do not work, maybe you have to run")
@ -753,22 +848,22 @@ def main():
epilog='Example combining image and text:\n sudo %s "I:HEART2:you"' % sys.argv[0])
parser.add_argument('-t', '--type', default='11x44',
help="Type of display: supported values are 12x48 or (default) 11x44. Rename the program to led-badge-12x48, to switch the default.")
parser.add_argument('-H', '--hid', default='0', help="Deprecated, only for backwards compatibility, please use -M! Set to 1 to ensure connect via HID API, program will then not fallback to usb.core library")
parser.add_argument('-M', '--method', default='auto', help="Force using the given write method ('hidapi' or 'libusb')")
parser.add_argument('-E', '--endpoint', default='auto', help="Force using the given device endpoint")
parser.add_argument('-s', '--speed', default='4', help="Scroll speed (Range 1..8). Up to 8 comma-separated values")
parser.add_argument('-H', '--hid', default='0', help="Deprecated, only for backwards compatibility, please use -M! Set to 1 to ensure connect via HID API, program will then not fallback to usb.core library.")
parser.add_argument('-M', '--method', default='auto', help="Force using the given write method ('hidapi' or 'libusb').")
parser.add_argument('-D', '--device-id', default='auto', help="Force using the given device id, if ambiguous. Usue one of 'auto', 'list' or whatever list is printing.")
parser.add_argument('-s', '--speed', default='4', help="Scroll speed (Range 1..8). Up to 8 comma-separated values.")
parser.add_argument('-B', '--brightness', default='100',
help="Brightness for the display in percent: 25, 50, 75, or 100")
help="Brightness for the display in percent: 25, 50, 75, or 100.")
parser.add_argument('-m', '--mode', default='0',
help="Up to 8 mode values: Scroll-left(0) -right(1) -up(2) -down(3); still-centered(4); animation(5); drop-down(6); curtain(7); laser(8); See '--mode-help' for more details.")
parser.add_argument('-b', '--blink', default='0', help="1: blinking, 0: normal. Up to 8 comma-separated values")
parser.add_argument('-a', '--ants', default='0', help="1: animated border, 0: normal. Up to 8 comma-separated values")
parser.add_argument('-b', '--blink', default='0', help="1: blinking, 0: normal. Up to 8 comma-separated values.")
parser.add_argument('-a', '--ants', default='0', help="1: animated border, 0: normal. Up to 8 comma-separated values.")
parser.add_argument('-p', '--preload', metavar='FILE', action='append',
help=argparse.SUPPRESS) # "Load bitmap images. Use ^A, ^B, ^C, ... in text messages to make them visible. Deprecated, embed within ':' instead")
parser.add_argument('-l', '--list-names', action='version', help="list named icons to be embedded in messages and exit",
parser.add_argument('-l', '--list-names', action='version', help="list named icons to be embedded in messages and exit.",
version=':' + ': :'.join(SimpleTextAndIcons._get_named_bitmaps_keys()) + ': :: or e.g. :path/to/some_icon.png:')
parser.add_argument('message', metavar='MESSAGE', nargs='+',
help="Up to 8 message texts with embedded builtin icons or loaded images within colons(:) -- See -l for a list of builtins")
help="Up to 8 message texts with embedded builtin icons or loaded images within colons(:) -- See -l for a list of builtins.")
parser.add_argument('--mode-help', action='version', help=argparse.SUPPRESS, version="""
-m 5 "Animation"
@ -837,7 +932,7 @@ def main():
else:
sys.exit("Parameter values are ambiguous. Please use either -H or -M.")
LedNameBadge.write(buf, method, args.endpoint)
LedNameBadge.write(buf, method, args.device_id)
def split_to_ints(list_str):

Loading…
Cancel
Save