Docstrings added

pull/8/head^2
Ben Sartori 7 months ago
parent 1e3c081c29
commit 9b517d1229
  1. 4
      README.md
  2. 137
      lednamebadge.py

@ -98,7 +98,8 @@ in a quite low level way and in a quite old version:
- Please use version 1.2.6.0 of 'usblib-win32`. It's still available on the
[old project repo on SourceForge](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/)
- Then
- Extract `bin/inf-wizard.exe` from the downloaded zip file. Right click and `Run as Administrator`
- Extract the downloaded zip file and go to the directory `libusb-win32-bin-1.2.6.0\bin`
- Right click on `inf-wizard.exe` and `Run as Administrator`
- `Next` -> Select `0x0416 0x5020 LS32 Custm HID` (or similar with the same IDs)
- `Next` -> `Next` -> Save as dialog `LS32_Sustm_HID.inf` -> `Save` (just to proceed, we don't need that file)
- `Install Now...` -> Driver Install Complete -> `OK`
@ -117,6 +118,7 @@ or specific versions from [here](https://www.python.org/downloads/windows/)
- `[x]` install Launcher for all Users
- `[x]` Add Python X.Y to PATH
- Click the `Install Now ...` text message.
- Optionally click on the 'Disable path length limit' text message. This is always a good thing to do.
Install needed the Python packages. On some systems (esp. those with Python 2
*and* 3 installed), you have to address Python 3 explicitly by using the

@ -16,22 +16,43 @@
#
# Windows install:
# ----------------
## https://sourceforge.net/projects/libusb-win32/ ->
## -> https://kent.dl.sourceforge.net/project/libusb-win32/libusb-win32-releases/1.2.6.0/libusb-win32-bin-1.2.6.0.zip
## cd libusb-win32-bin-1.2.6.0\bin
## download inf-wizard.exe to your desktop. Right click 'Run as Administrator'
# -> Click 0x0416 0x5020 LS32 Custm HID
# -> Next -> Next -> Dokumente LS32_Sustm_HID.inf -> Save
# -> Install Now... -> Driver Install Complete -> OK
# download python from python.org
# [x] install Launcher for all Users
# [x] Add Python 3.7 to PATH
# -> Click the 'Install Now ...' text message.
# -> Optionally click on the 'Disable path length limit' text message. This is always a good thing to do.
# run cmd.exe as Administrator, enter:
# pip install pyusb
# pip install pillow
# For Windows, we need to set up the libusb API for the LED badge device.
# The way described here, uses [libusb-win32](https://github.com/mcuee/libusb-win32/wiki)
# in a quite low level way and in a quite old version:
#
# - Please use version 1.2.6.0 of 'usblib-win32`. It's still available on the
# [old project repo on SourceForge](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/)
# - Then
# - Extract the downloaded zip file and go to the directory `libusb-win32-bin-1.2.6.0\bin`
# - Right click on `inf-wizard.exe` and `Run as Administrator`
# - `Next` -> Select `0x0416 0x5020 LS32 Custm HID` (or similar with the same IDs)
# - `Next` -> `Next` -> Save as dialog `LS32_Sustm_HID.inf` -> `Save` (just to proceed, we don't need that file)
# - `Install Now...` -> Driver Install Complete -> `OK`
#
# There are other - meanwhile recommended, but untested here - ways to install and setup
# newer versions of `libus-win32`: use
# [Zadig](https://zadig.akeo.ie/) (it is also available from the old libusb-win32 repo on
# [GitHub repo](https://github.com/mcuee/libusb-win32/releases) of newer releases)
# or [libusbK](https://libusbk.sourceforge.net/UsbK3/index.html)
#
# Of course, Python is needed:
#
# - Download latest python from [python.org](https://www.python.org/downloads/),
# or specific versions from [here](https://www.python.org/downloads/windows/)
# - Checkmark the following options
# - `[x]` install Launcher for all Users
# - `[x]` Add Python X.Y to PATH
# - Click the `Install Now ...` text message.
# - Optionally click on the 'Disable path length limit' text message. This is always a good thing to do.
#
# Install needed the Python packages. On some systems (esp. those with Python 2
# *and* 3 installed), you have to address Python 3 explicitly by using the
# command `pip3` instead of `pip`.
#
# - Run cmd.exe as Administrator, enter:
#
# pip install pyusb
# pip install pillow
#
# v0.1, 2019-03-05, jw initial draught. HID code is much simpler than expected.
@ -61,8 +82,9 @@
# somehow, but it is acceptable, as we do not need to save every processor cycle, here :)
# * Have fun!
# v0.14, 2024-06-02, bs extending write methods.
# * Preparation for further write methods, like bluetooth.
# * Automatic or manual device and endpoint selection, See -M and -D (substituting -H)
# * Preparation for further or updated write methods, like bluetooth.
# * Automatic or manual write method and device selection, See -M and -D (substituting -H) resp.
# get_available_methods() and get_available_device_ids().
import argparse
@ -411,19 +433,37 @@ class SimpleTextAndIcons:
class WriteMethod:
"""Base class for a write method. That is a way to communicate with a device. Think of using different access
libraries or interfaces for communication. Basically it implements the common parts of the functionalities
'device detection' and 'write data' and defines te interfaces to the user and the concrete write method class.
"""
def __init__(self):
"""Call it from your concrete class in your __init__ method with!
"""
self.devices = {}
def __del__(self):
self.close()
def get_name(self):
"""Returns the name of the write method.
This method is to be implemented in your concrete class. It should just return a short and unique name.
"""
raise NotImplementedError()
def get_description(self):
"""Returns a description of the write method.
This method is to be implemented in your concrete class. It should just return a short description
of how the write method communicates with the device (think of libraries and interfaces).
"""
raise NotImplementedError()
def open(self, device_id):
"""Opens the communication channel to the device, similar to open a file. The device id is one of the ids
returned by get_available_devices() or 'auto', which selects just the first device in that dict.
It is the common part of the opening process. The concrete open is done in _open() and is to be implemented
individually.
"""
if self.is_ready() and self.is_device_present():
actual_device_id = None
if device_id == 'auto':
@ -437,50 +477,94 @@ class WriteMethod:
return False
def close(self):
"""Close the communication channel to the device, similar to closing a file.
This method is to be implemented in your concrete class. It should close and free all handles and resources.
"""
raise NotImplementedError()
def get_available_devices(self):
"""Get all devices available via the concrete write method. It returns a dict with the device ids as keys
and the device descriptions as values. These device ids are used with 'open()' to specify the wanted device.
It the common part of this process. The concrete part is to be implemented in _get_available_devices()
individually.
"""
if self.is_ready() and not self.devices:
self.devices = self._get_available_devices()
return {id: data[0] for id, data in self.devices.items()}
def is_device_present(self):
"""Returns True if there is one or more devices available via the concrete write method, False otherwise.
"""
self.get_available_devices()
return self.devices and len(self.devices) > 0
def _open(self, device_id):
"""The concrete open action. This method is to be implemented in your concrete class. It shall open
the communication channel to the device with the specified id, which is one of the ids returned by
_get_available_devices(). It shall return True, if successful, otherwise False. The special id 'auto'
is handled in open(). So, this method is called only with device ids from the dict returned by
_get_available_devices().
"""
raise NotImplementedError()
def _get_available_devices(self):
"""The concrete get-the-list action. This method is to be implemented in your concrete class. It shall
Return a dict with one entry per available device. The key of an entry is the device id, like it will be
used in open() / _open(). The value af an entry is a tuple with any data according to the needs of your
write method. The only defined element is the first one, which shall be a description of the individual
device (e.g. manufacturer or bus number / address). E.g. { '1:5': ('Nametag 5 on bus 1', any, data)}
"""
raise NotImplementedError()
def is_ready(self):
"""Returns True, if the concrete write method is basically ready for operation, otherwise False.
This method is to be implemented in your concrete class. Basically, if the import instruction for the
needed Python modules and potentially a library / module initialization was successful, it shall return True.
This method does not make any statement about concrete devices or device availability.
"""
raise NotImplementedError()
def has_device(self):
"""Returns True, if there is at least one device available with the concrete write method, otherwise False.
This method is to be implemented in your concrete class.
"""
raise NotImplementedError()
def write(self, buf):
"""Call this to write data to the opened device.
The concrete write action is to be implemented in _write()."""
self.add_padding(buf, 64)
self.check_length(buf, 8192)
self._write(buf)
@staticmethod
def add_padding(buf, blocksize):
need_padding = len(buf) % blocksize
def add_padding(buf, block_size):
"""The given data array will be extended with zeros according to the given block size. SO, afterwards the
length of the array is a multiple of block_size.
"""
need_padding = len(buf) % block_size
if need_padding:
buf.extend((0,) * (blocksize - need_padding))
buf.extend((0,) * (block_size - need_padding))
@staticmethod
def check_length(buf, maxsize):
if len(buf) > maxsize:
print("Writing more than %d bytes damages the display! Nothing written." % (maxsize,))
def check_length(buf, max_size):
"""Just checks the length of the given data array and abort the program execution if it exceeds max_size.
"""
if len(buf) > max_size:
print("Writing more than %d bytes damages the display! Nothing written." % (max_size,))
sys.exit(1)
def _write(self, buf):
"""Write the given data array to the opened device.
This method is to be implemented in your concrete class. It shall write the given data array to the opened
device.
"""
raise NotImplementedError()
class WriteLibUsb(WriteMethod):
"""Write to a device using pyusb and libusb. The device ids consist of the bus number, the device number on that bus
and the endpoint number.
"""
_module_loaded = False
try:
import usb.core
@ -580,6 +664,9 @@ class WriteLibUsb(WriteMethod):
class WriteUsbHidApi(WriteMethod):
"""Write to a device connected to USB using pyhidapi and libhidapi. The device ids are simply the device paths as
used by libhidapi.
"""
_module_loaded = False
try:
import pyhidapi
@ -752,6 +839,10 @@ class LedNameBadge:
@staticmethod
def _find_write_method(method, device_id):
"""Here we try to concentrate all special cases, decisions and messages around the manual or automatic
selection of write methods and device. This way it is a bit easier to extend or modify the different
working run time environments (think of operating system, python version, installed libraries and python
modules, ands so on.)"""
auto_order_methods = LedNameBadge.get_auto_order_method_list()
hidapi = [m for m in auto_order_methods if m.get_name() == 'hidapi'][0]
libusb = [m for m in auto_order_methods if m.get_name() == 'libusb'][0]

Loading…
Cancel
Save