@ -0,0 +1,13 @@ |
|||||||
|
# These are supported funding model platforms |
||||||
|
|
||||||
|
github: [jnweiger] |
||||||
|
patreon: # Replace with a single Patreon username |
||||||
|
open_collective: # Replace with a single Open Collective username |
||||||
|
ko_fi: # Replace with a single Ko-fi username |
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel |
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry |
||||||
|
liberapay: # Replace with a single Liberapay username |
||||||
|
issuehunt: # Replace with a single IssueHunt username |
||||||
|
otechie: # Replace with a single Otechie username |
||||||
|
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry |
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] |
@ -1,527 +1,3 @@ |
|||||||
#! /usr/bin/python3 |
import lednamebadge |
||||||
# -*- encoding: utf-8 -*- |
|
||||||
# |
|
||||||
# (C) 2019 juergen@fabmail.org |
|
||||||
# |
|
||||||
# This is an upload tool for e.g. |
|
||||||
# https://www.sertronics-shop.de/computer/pc-peripheriegeraete/usb-gadgets/led-name-tag-11x44-pixel-usb |
|
||||||
# The font_11x44[] data was downloaded from such a device. |
|
||||||
# |
|
||||||
# Ubuntu install: |
|
||||||
# --------------- |
|
||||||
# sudo apt-get install python3-usb |
|
||||||
# |
|
||||||
# Optional for image support: |
|
||||||
# sudo apt-get install python3-pil |
|
||||||
# |
|
||||||
# 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 |
|
||||||
# |
|
||||||
|
|
||||||
# |
lednamebadge.main() |
||||||
# v0.1, 2019-03-05, jw initial draught. HID code is much simpler than expected. |
|
||||||
# v0.2, 2019-03-07, jw support for loading bitmaps added. |
|
||||||
# v0.3 jw option -p to preload graphics for inline use in text. |
|
||||||
# v0.4, 2019-03-08, jw Warning about unused images added. Examples added to the README. |
|
||||||
# v0.5, jw Deprecated -p and CTRL-characters. We now use embedding within colons(:) |
|
||||||
# Added builtin icons and -l to list them. |
|
||||||
# v0.6, 2019-03-14, jw Added --mode-help with hints and example for making animations. |
|
||||||
# Options -b --blink, -a --ants added. Removed -p from usage. |
|
||||||
# v0.7, 2019-05-20, jw Support pyhidapi, fallback to usb.core. Added python2 compatibility. |
|
||||||
# v0.8, 2019-05-23, jw Support usb.core on windows via libusb-win32 |
|
||||||
# v0.9, 2019-07-17, jw Support 48x12 configuration too. |
|
||||||
# v0.10, 2019-09-09, jw Support for loading monochrome images. Typos fixed. |
|
||||||
# v0.11, 2019-09-29, jw New option --brightness added. |
|
||||||
# v0.12, 2019-12-27, jw hint at pip3 -- as discussed in https://github.com/jnweiger/led-name-badge-ls32/issues/19 |
|
||||||
|
|
||||||
import sys, os, re, time, argparse |
|
||||||
from datetime import datetime |
|
||||||
from array import array |
|
||||||
try: |
|
||||||
if sys.version_info[0] < 3: raise Exception("prefer usb.core with python-2.x because of https://github.com/jnweiger/led-badge-ls32/issues/9") |
|
||||||
import pyhidapi |
|
||||||
pyhidapi.hid_init() |
|
||||||
have_pyhidapi = True |
|
||||||
except: |
|
||||||
have_pyhidapi = False |
|
||||||
try: |
|
||||||
import usb.core |
|
||||||
except: |
|
||||||
print("ERROR: Need the pyhidapi or usb.core module.") |
|
||||||
if sys.platform == "darwin": |
|
||||||
print("""Please try |
|
||||||
pip3 install pyhidapi |
|
||||||
pip install pyhidapi |
|
||||||
brew install hidapi""") |
|
||||||
elif sys.platform == "linux": |
|
||||||
print("""Please try |
|
||||||
sudo pip3 install pyhidapi |
|
||||||
sudo pip install pyhidapi |
|
||||||
sudo apt-get install libhidapi-hidraw0 |
|
||||||
sudo ln -s /usr/lib/x86_64-linux-gnu/libhidapi-hidraw.so.0 /usr/local/lib/ |
|
||||||
or |
|
||||||
sudo apt-get install python3-usb""") |
|
||||||
else: # windows? |
|
||||||
print("""Please with Linux or MacOS or help us implement support for """ + sys.platform) |
|
||||||
sys.exit(1) |
|
||||||
|
|
||||||
|
|
||||||
__version = "0.12" |
|
||||||
|
|
||||||
font_11x44 = ( |
|
||||||
# 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
|
||||||
0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
|
||||||
0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0xfc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, |
|
||||||
0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, |
|
||||||
0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0xf0, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xce, 0xc6, 0xc6, 0x7e, 0x00, |
|
||||||
0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
|
||||||
0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, |
|
||||||
0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, |
|
||||||
0x00, 0xe6, 0x66, 0x6c, 0x6c, 0x78, 0x6c, 0x6c, 0x66, 0xe6, 0x00, |
|
||||||
0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, |
|
||||||
0x00, 0x82, 0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
|
||||||
0x00, 0x86, 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0xf0, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x06, |
|
||||||
0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0xe6, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, |
|
||||||
0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, |
|
||||||
0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x82, 0x00, |
|
||||||
0x00, 0xc6, 0xc6, 0x6c, 0x7c, 0x38, 0x7c, 0x6c, 0xc6, 0xc6, 0x00, |
|
||||||
0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, |
|
||||||
0x00, 0xfe, 0xc6, 0x86, 0x0c, 0x18, 0x30, 0x62, 0xc6, 0xfe, 0x00, |
|
||||||
|
|
||||||
# 'abcdefghijklmnopqrstuvwxyz' |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, |
|
||||||
0x00, 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0x1c, 0x36, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, |
|
||||||
0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0xe6, 0x00, |
|
||||||
0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, |
|
||||||
0x0c, 0x0c, 0x00, 0x1c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, |
|
||||||
0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0xe6, 0x00, |
|
||||||
0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x7c, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0xde, 0x76, 0x60, 0x60, 0x60, 0xf0, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x34, 0x18, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0xfe, 0x8c, 0x18, 0x30, 0x62, 0xfe, 0x00, |
|
||||||
|
|
||||||
# '0987654321^ !"\0$%&/()=?` °\\}][{' |
|
||||||
0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0xfe, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x1e, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, |
|
||||||
0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, |
|
||||||
0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3c, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, |
|
||||||
0x66, 0x66, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x7c, 0x04, 0x14, 0x18, 0x10, 0x10, 0x20, |
|
||||||
0x10, 0x7c, 0xd6, 0xd6, 0x70, 0x1c, 0xd6, 0xd6, 0x7c, 0x10, 0x10, |
|
||||||
0x00, 0x60, 0x92, 0x96, 0x6c, 0x10, 0x6c, 0xd2, 0x92, 0x0c, 0x00, |
|
||||||
0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0x76, 0x00, |
|
||||||
0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, |
|
||||||
0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, |
|
||||||
0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, |
|
||||||
0x18, 0x18, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7c, |
|
||||||
0x00, 0x10, 0x28, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x80, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, 0x00, |
|
||||||
0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x00, |
|
||||||
0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, |
|
||||||
0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, |
|
||||||
0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, |
|
||||||
|
|
||||||
# "@ ~ |<>,;.:-_#'+* " |
|
||||||
0x00, 0x00, 0x3c, 0x42, 0x9d, 0xa5, 0xad, 0xb6, 0x40, 0x3c, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x08, 0x08, 0x7c, 0x08, 0x08, 0x18, 0x18, 0x28, 0x28, 0x48, 0x18, |
|
||||||
0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, |
|
||||||
0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, |
|
||||||
0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x10, 0x20, |
|
||||||
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x08, 0x10, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, |
|
||||||
0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, |
|
||||||
0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
|
|
||||||
# "äöüÄÖÜߊ" |
|
||||||
0x00, 0xcc, 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, |
|
||||||
0x00, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, |
|
||||||
0xc6, 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
|
||||||
0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
0x00, 0x3c, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x6c, 0x60, |
|
||||||
0x28, 0x10, 0x7c, 0xc6, 0xe0, 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, |
|
||||||
) |
|
||||||
|
|
||||||
charmap = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + \ |
|
||||||
u'abcdefghijklmnopqrstuvwxyz' + \ |
|
||||||
u'0987654321^ !"\0$%&/()=?` °\\}][{' + \ |
|
||||||
u"@ ~ |<>,;.:-_#'+* " + \ |
|
||||||
u"äöüÄÖÜߊ" |
|
||||||
|
|
||||||
char_offset = {} |
|
||||||
for i in range(len(charmap)): |
|
||||||
char_offset[charmap[i]] = 11 * i |
|
||||||
# print(i, charmap[i], char_offset[charmap[i]]) |
|
||||||
|
|
||||||
|
|
||||||
bitmap_preloaded = [ ([],0) ] |
|
||||||
bitmaps_preloaded_unused = False |
|
||||||
|
|
||||||
bitmap_named = { |
|
||||||
'ball': (array('B', ( |
|
||||||
0b00000000, |
|
||||||
0b00000000, |
|
||||||
0b00111100, |
|
||||||
0b01111110, |
|
||||||
0b11111111, |
|
||||||
0b11111111, |
|
||||||
0b11111111, |
|
||||||
0b11111111, |
|
||||||
0b01111110, |
|
||||||
0b00111100, |
|
||||||
0b00000000 |
|
||||||
)), 1, '\x1e'), |
|
||||||
'happy': (array('B', ( |
|
||||||
0b00000000, # 0x00 |
|
||||||
0b00000000, # 0x00 |
|
||||||
0b00111100, # 0x3c |
|
||||||
0b01000010, # 0x42 |
|
||||||
0b10100101, # 0xa5 |
|
||||||
0b10000001, # 0x81 |
|
||||||
0b10100101, # 0xa5 |
|
||||||
0b10011001, # 0x99 |
|
||||||
0b01000010, # 0x42 |
|
||||||
0b00111100, # 0x3c |
|
||||||
0b00000000 # 0x00 |
|
||||||
)), 1, '\x1d'), |
|
||||||
'happy2': (array('B', (0x00, 0x08, 0x14, 0x08, 0x01, 0x00, 0x00, 0x61, 0x30, 0x1c, 0x07, |
|
||||||
0x00, 0x20, 0x50, 0x20, 0x00, 0x80, 0x80, 0x86, 0x0c, 0x38, 0xe0)), 2, '\x1c'), |
|
||||||
'heart': (array('B', (0x00, 0x00, 0x6c, 0x92, 0x82, 0x82, 0x44, 0x28, 0x10, 0x00, 0x00)), 1, '\x1b'), |
|
||||||
'HEART': (array('B', (0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00)), 1, '\x1a'), |
|
||||||
'heart2': (array('B', (0x00, 0x0c, 0x12, 0x21, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, |
|
||||||
0x00, 0x60, 0x90, 0x08, 0x08, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00)), 2, '\x19'), |
|
||||||
'HEART2': (array('B', (0x00, 0x0c, 0x1e, 0x3f, 0x3f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, |
|
||||||
0x00, 0x60, 0xf0, 0xf8, 0xf8, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00)), 2, '\x18'), |
|
||||||
'fablab': (array('B', (0x07, 0x0e, 0x1b, 0x03, 0x21, 0x2c, 0x2e, 0x26, 0x14, 0x1c, 0x06, |
|
||||||
0x80, 0x60, 0x30, 0x80, 0x88, 0x38, 0xe8, 0xc8, 0x10, 0x30, 0xc0)), 2, '\x17'), |
|
||||||
'bicycle': (array('B', (0x01, 0x02, 0x00, 0x01, 0x07, 0x09, 0x12, 0x12, 0x10, 0x08, 0x07, |
|
||||||
0x00, 0x87, 0x81, 0x5f, 0x22, 0x94, 0x49, 0x5f, 0x49, 0x80, 0x00, |
|
||||||
0x00, 0x80, 0x00, 0x80, 0x70, 0xc8, 0x24, 0xe4, 0x04, 0x88, 0x70)), 3, '\x16'), |
|
||||||
'bicycle_r':(array('B', (0x00, 0x00, 0x00, 0x00, 0x07, 0x09, 0x12, 0x13, 0x10, 0x08, 0x07, |
|
||||||
0x00, 0xf0, 0x40, 0xfd, 0x22, 0x94, 0x49, 0xfd, 0x49, 0x80, 0x00, |
|
||||||
0x40, 0xa0, 0x80, 0x40, 0x70, 0xc8, 0x24, 0x24, 0x04, 0x88, 0x70)), 3, '\x15'), |
|
||||||
'owncloud': (array('B', (0x00, 0x01, 0x02, 0x03, 0x06, 0x0c, 0x1a, 0x13, 0x11, 0x19, 0x0f, |
|
||||||
0x78, 0xcc, 0x87, 0xfc, 0x42, 0x81, 0x81, 0x81, 0x81, 0x43, 0xbd, |
|
||||||
0x00, 0x00, 0x00, 0x80, 0x80, 0xe0, 0x30, 0x10, 0x28, 0x28, 0xd0)), 3, '\x14'), |
|
||||||
} |
|
||||||
bitmap_builtin = {} |
|
||||||
for i in bitmap_named: |
|
||||||
bitmap_builtin[bitmap_named[i][2]] = bitmap_named[i] |
|
||||||
|
|
||||||
|
|
||||||
def bitmap_char(ch): |
|
||||||
""" Returns a tuple of 11 bytes, |
|
||||||
ch = '_' returns (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255) |
|
||||||
The bits in each byte are horizontal, highest bit is left. |
|
||||||
""" |
|
||||||
if ord(ch) < 32: |
|
||||||
if ch in bitmap_builtin: |
|
||||||
return bitmap_builtin[ch][:2] |
|
||||||
|
|
||||||
global bitmaps_preloaded_unused |
|
||||||
bitmaps_preloaded_unused = False |
|
||||||
return bitmap_preloaded[ord(ch)] |
|
||||||
|
|
||||||
o = char_offset[ch] |
|
||||||
return (font_11x44[o:o+11],1) |
|
||||||
|
|
||||||
|
|
||||||
def bitmap_text(text): |
|
||||||
""" returns a tuple of (buffer, length_in_byte_columns_aka_chars) |
|
||||||
We preprocess the text string for substitution patterns |
|
||||||
"::" is replaced with a single ":" |
|
||||||
":1: is replaced with CTRL-A referencing the first preloaded or loaded image. |
|
||||||
":happy:" is replaced with a reference to a builtin smiley glyph |
|
||||||
":heart:" is replaced with a reference to a builtin heart glyph |
|
||||||
":gfx/logo.png:" preloads the file gfx/logo.png and is replaced the corresponding control char. |
|
||||||
""" |
|
||||||
def colonrepl(m): |
|
||||||
name = m.group(1) |
|
||||||
if name == '': |
|
||||||
return ':' |
|
||||||
if re.match('^[0-9]*$', name): # py3 name.isdecimal() |
|
||||||
return chr(int(name)) |
|
||||||
if '.' in name: |
|
||||||
bitmap_preloaded.append(bitmap_img(name)) |
|
||||||
return chr(len(bitmap_preloaded)-1) |
|
||||||
b = bitmap_named[name] |
|
||||||
return b[2] |
|
||||||
|
|
||||||
text = re.sub(r':([^:]*):', colonrepl, text) |
|
||||||
buf = array('B') |
|
||||||
cols = 0 |
|
||||||
for c in text: |
|
||||||
(b,n) = bitmap_char(c) |
|
||||||
buf.extend(b) |
|
||||||
cols += n |
|
||||||
return (buf, cols) |
|
||||||
|
|
||||||
|
|
||||||
def bitmap_img(file): |
|
||||||
""" returns a tuple of (buffer, length_in_byte_columns) |
|
||||||
""" |
|
||||||
from PIL import Image |
|
||||||
|
|
||||||
im = Image.open(file) |
|
||||||
print("fetching bitmap from file %s -> (%d x %d)" % (file, im.width, im.height)) |
|
||||||
if im.height != 11: |
|
||||||
sys.exit("%s: image height must be 11px. Seen %d" % (file, im.height)) |
|
||||||
buf = array('B') |
|
||||||
cols = int((im.width+7)/8) |
|
||||||
for col in range(cols): |
|
||||||
for row in range(11): # [0..10] |
|
||||||
byte_val = 0 |
|
||||||
for bit in range(8): # [0..7] |
|
||||||
bit_val = 0 |
|
||||||
x = 8*col+bit |
|
||||||
if x < im.width: |
|
||||||
pixel_color = im.getpixel( (x, row) ) |
|
||||||
if isinstance(pixel_color, tuple): |
|
||||||
monochrome_color = sum(pixel_color[:3]) / len(pixel_color[:3]) |
|
||||||
elif isinstance(pixel_color, int): |
|
||||||
monochrome_color = pixel_color |
|
||||||
else: |
|
||||||
sys.exit("%s: Unknown pixel format detected (%s)!" % (file, pixel_color)) |
|
||||||
if monochrome_color > 127: |
|
||||||
bit_val = 1 << (7-bit) |
|
||||||
byte_val += bit_val |
|
||||||
buf.append(byte_val) |
|
||||||
im.close() |
|
||||||
return (buf, cols) |
|
||||||
|
|
||||||
|
|
||||||
def bitmap(arg): |
|
||||||
""" if arg is a valid and existing path name, we load it as an image. |
|
||||||
Otherwise we take it as a string. |
|
||||||
""" |
|
||||||
if os.path.exists(arg): |
|
||||||
return bitmap_img(arg) |
|
||||||
return bitmap_text(arg) |
|
||||||
|
|
||||||
|
|
||||||
proto_header = ( |
|
||||||
0x77, 0x61, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
def header(lengths, speeds, modes, blink, ants, brightness=100): |
|
||||||
""" lengths[0] is the number of chars of the first text |
|
||||||
|
|
||||||
Speeds come in as 1..8, but are needed 0..7 here. |
|
||||||
""" |
|
||||||
a = [int(x) for x in re.split(r'[\s,]+', ants)] |
|
||||||
a = a + [a[-1]]*(8-len(a)) # repeat last element |
|
||||||
|
|
||||||
b = [int(x) for x in re.split(r'[\s,]+', blink)] |
|
||||||
b = b + [b[-1]]*(8-len(b)) # repeat last element |
|
||||||
|
|
||||||
s = [int(x)-1 for x in re.split(r'[\s,]+', speeds)] |
|
||||||
s = s + [s[-1]]*(8-len(s)) # repeat last element |
|
||||||
|
|
||||||
m = [int(x) for x in re.split(r'[\s,]+', modes)] |
|
||||||
m = m + [m[-1]]*(8-len(m)) # repeat last element |
|
||||||
|
|
||||||
h = list(proto_header) |
|
||||||
|
|
||||||
if brightness <= 25: |
|
||||||
h[5] = 0x40 |
|
||||||
elif brightness <= 50: |
|
||||||
h[5] = 0x20 |
|
||||||
elif brightness <= 75: |
|
||||||
h[5] = 0x10 |
|
||||||
|
|
||||||
for i in range(8): |
|
||||||
h[6] += b[i]<<i |
|
||||||
h[7] += a[i]<<i |
|
||||||
|
|
||||||
for i in range(8): |
|
||||||
h[8+i] = 16*s[i] + m[i] |
|
||||||
|
|
||||||
for i in range(len(lengths)): |
|
||||||
h[17+(2*i)-1] = lengths[i] // 256 |
|
||||||
h[17+(2*i)] = lengths[i] % 256 |
|
||||||
|
|
||||||
|
|
||||||
cdate = datetime.now() |
|
||||||
h[38+0] = cdate.year % 100 |
|
||||||
h[38+1] = cdate.month |
|
||||||
h[38+2] = cdate.day |
|
||||||
h[38+3] = cdate.hour |
|
||||||
h[38+4] = cdate.minute |
|
||||||
h[38+5] = cdate.second |
|
||||||
|
|
||||||
return h |
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description='Upload messages or graphics to a 11x44 led badge via USB HID.\nVersion %s from https://github.com/jnweiger/led-badge-ls32\n -- see there for more examples and for updates.' % __version, 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('-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") |
|
||||||
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('-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", version=':'+': :'.join(bitmap_named.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") |
|
||||||
parser.add_argument('--mode-help', action='version', help=argparse.SUPPRESS, version=""" |
|
||||||
|
|
||||||
-m 5 "Animation" |
|
||||||
|
|
||||||
Animation frames are 6 character (or 48px) wide. Upload an animation of |
|
||||||
N frames as one image N*48 pixels wide, 11 pixels high. |
|
||||||
Frames run from left to right and repeat endless. |
|
||||||
Speeds [1..8] result in ca. [1.2 1.3 2.0 2.4 2.8 4.5 7.5 15] fps. |
|
||||||
|
|
||||||
Example of a slowly beating heart: |
|
||||||
sudo %s -s1 -m5 " :heart2: :HEART2:" |
|
||||||
|
|
||||||
-m 9 "Smooth" |
|
||||||
-m 10 "Rotate" |
|
||||||
|
|
||||||
These modes are mentioned in the BMP Badge software. |
|
||||||
Text is shown static, or sometimes (longer texts?) not shown at all. |
|
||||||
One significant difference is: The text of the first message stays visible after |
|
||||||
upload, even if the USB cable remains connected. |
|
||||||
(No "rotation" or "smoothing"(?) effect can be expected, though) |
|
||||||
""" % sys.argv[0]) |
|
||||||
args = parser.parse_args() |
|
||||||
if have_pyhidapi: |
|
||||||
devinfo = pyhidapi.hid_enumerate(0x0416, 0x5020) |
|
||||||
#dev = pyhidapi.hid_open(0x0416, 0x5020) |
|
||||||
else: |
|
||||||
dev = usb.core.find(idVendor=0x0416, idProduct=0x5020) |
|
||||||
|
|
||||||
if have_pyhidapi: |
|
||||||
if devinfo: |
|
||||||
dev = pyhidapi.hid_open_path(devinfo[0].path) |
|
||||||
print("using [%s %s] int=%d page=%s via pyHIDAPI" % (devinfo[0].manufacturer_string, devinfo[0].product_string, devinfo[0].interface_number, devinfo[0].usage_page)) |
|
||||||
else: |
|
||||||
print("No led tag with vendorID 0x0416 and productID 0x5020 found.") |
|
||||||
print("Connect the led tag and run this tool as root.") |
|
||||||
sys.exit(1) |
|
||||||
else: |
|
||||||
if dev is None: |
|
||||||
print("No led tag with vendorID 0x0416 and productID 0x5020 found.") |
|
||||||
print("Connect the led tag and run this tool as root.") |
|
||||||
sys.exit(1) |
|
||||||
try: |
|
||||||
# win32: NotImplementedError: is_kernel_driver_active |
|
||||||
if dev.is_kernel_driver_active(0): |
|
||||||
dev.detach_kernel_driver(0) |
|
||||||
except: |
|
||||||
pass |
|
||||||
dev.set_configuration() |
|
||||||
print("using [%s %s] bus=%d dev=%d" % (dev.manufacturer, dev.product, dev.bus, dev.address)) |
|
||||||
|
|
||||||
if args.preload: |
|
||||||
for file in args.preload: |
|
||||||
bitmap_preloaded.append(bitmap_img(file)) |
|
||||||
bitmaps_preloaded_unused = True |
|
||||||
|
|
||||||
msgs = [] |
|
||||||
for arg in args.message: |
|
||||||
msgs.append(bitmap(arg)) |
|
||||||
|
|
||||||
if bitmaps_preloaded_unused == True: |
|
||||||
print("\nWARNING:\n Your preloaded images are not used.\n Try without '-p' or embed the control character '^A' in your message.\n") |
|
||||||
|
|
||||||
if '12' in args.type or '12' in sys.argv[0]: |
|
||||||
print("Type: 12x48") |
|
||||||
for msg in msgs: |
|
||||||
# trivial hack to support 12x48 badges: |
|
||||||
# patch extra empty lines into the message stream. |
|
||||||
for o in reversed(range(1, int(len(msg[0])/11)+1)): |
|
||||||
msg[0][o*11:o*11] = array('B', [0]) |
|
||||||
else: |
|
||||||
print("Type: 11x44") |
|
||||||
|
|
||||||
buf = array('B') |
|
||||||
buf.extend(header(list(map(lambda x: x[1], msgs)), args.speed, args.mode, args.blink, args.ants, int(args.brightness))) |
|
||||||
|
|
||||||
for msg in msgs: |
|
||||||
buf.extend(msg[0]) |
|
||||||
|
|
||||||
needpadding = len(buf)%64 |
|
||||||
if needpadding: |
|
||||||
buf.extend( (0,) * (64-needpadding) ) |
|
||||||
|
|
||||||
# print(buf) # array('B', [119, 97, 110, 103, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 126, 255, 255, 255, 255, 126, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) |
|
||||||
|
|
||||||
if len(buf) > 8192: |
|
||||||
print ("Writing more than 8192 bytes damages the display!") |
|
||||||
sys.exit(1) |
|
||||||
|
|
||||||
if have_pyhidapi: |
|
||||||
pyhidapi.hid_write(dev, buf) |
|
||||||
else: |
|
||||||
for i in range(int(len(buf)/64)): |
|
||||||
time.sleep(0.1) |
|
||||||
dev.write(1, buf[i*64:i*64+64]) |
|
||||||
|
|
||||||
if have_pyhidapi: |
|
||||||
pyhidapi.hid_close(dev) |
|
||||||
|
@ -0,0 +1,672 @@ |
|||||||
|
#! /usr/bin/python3 |
||||||
|
# -*- encoding: utf-8 -*- |
||||||
|
# |
||||||
|
# (C) 2019 juergen@fabmail.org |
||||||
|
# |
||||||
|
# This is an upload tool for e.g. |
||||||
|
# https://www.sertronics-shop.de/computer/pc-peripheriegeraete/usb-gadgets/led-name-tag-11x44-pixel-usb |
||||||
|
# The font_11x44[] data was downloaded from such a device. |
||||||
|
# |
||||||
|
# Ubuntu install: |
||||||
|
# --------------- |
||||||
|
# sudo apt-get install python3-usb |
||||||
|
# |
||||||
|
# Optional for image support: |
||||||
|
# sudo apt-get install python3-pil |
||||||
|
# |
||||||
|
# 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 |
||||||
|
# |
||||||
|
|
||||||
|
# |
||||||
|
# v0.1, 2019-03-05, jw initial draught. HID code is much simpler than expected. |
||||||
|
# v0.2, 2019-03-07, jw support for loading bitmaps added. |
||||||
|
# v0.3 jw option -p to preload graphics for inline use in text. |
||||||
|
# v0.4, 2019-03-08, jw Warning about unused images added. Examples added to the README. |
||||||
|
# v0.5, jw Deprecated -p and CTRL-characters. We now use embedding within colons(:) |
||||||
|
# Added builtin icons and -l to list them. |
||||||
|
# v0.6, 2019-03-14, jw Added --mode-help with hints and example for making animations. |
||||||
|
# Options -b --blink, -a --ants added. Removed -p from usage. |
||||||
|
# v0.7, 2019-05-20, jw Support pyhidapi, fallback to usb.core. Added python2 compatibility. |
||||||
|
# v0.8, 2019-05-23, jw Support usb.core on windows via libusb-win32 |
||||||
|
# v0.9, 2019-07-17, jw Support 48x12 configuration too. |
||||||
|
# v0.10, 2019-09-09, jw Support for loading monochrome images. Typos fixed. |
||||||
|
# v0.11, 2019-09-29, jw New option --brightness added. |
||||||
|
# v0.12, 2019-12-27, jw hint at pip3 -- as discussed in https://github.com/jnweiger/led-name-badge-ls32/issues/19 |
||||||
|
# v0.13, 2023-11-14, bs modularization. |
||||||
|
# Some comments about this big change: |
||||||
|
# * I wanted to keep this one-python-file-for-complete-command-line-usage, but also needed to introduce importable |
||||||
|
# classes for writing own content to the device (my upcoming GUI editor). Therefore, the file was renamed to an |
||||||
|
# importable python file, and forwarding python files are introduced with the old file names for full |
||||||
|
# compatibility. |
||||||
|
# * A bit of code rearranging and cleanup was necessary for that goal, but I hope the original parts are still |
||||||
|
# recognizable, as I tried to keep all this refactoring as less, as possible and sense-making, but did not do |
||||||
|
# the full clean-codish refactoring. Keeping the classes in one file is part of that refactoring-omittance. |
||||||
|
# * There is some initialization code executed in the classes not needed, if not imported. This is nagging me |
||||||
|
# somehow, but it is acceptable, as we do not need to save every processor cycle, here :) |
||||||
|
# * Have fun! |
||||||
|
|
||||||
|
|
||||||
|
import argparse |
||||||
|
import os |
||||||
|
import re |
||||||
|
import sys |
||||||
|
import time |
||||||
|
from array import array |
||||||
|
from datetime import datetime |
||||||
|
|
||||||
|
|
||||||
|
__version = "0.13" |
||||||
|
|
||||||
|
|
||||||
|
class SimpleTextAndIcons: |
||||||
|
font_11x44 = ( |
||||||
|
# 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
||||||
|
0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
||||||
|
0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0xfc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, |
||||||
|
0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, |
||||||
|
0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0xf0, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xce, 0xc6, 0xc6, 0x7e, 0x00, |
||||||
|
0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
||||||
|
0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, |
||||||
|
0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, |
||||||
|
0x00, 0xe6, 0x66, 0x6c, 0x6c, 0x78, 0x6c, 0x6c, 0x66, 0xe6, 0x00, |
||||||
|
0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, |
||||||
|
0x00, 0x82, 0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
||||||
|
0x00, 0x86, 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0xf0, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x06, |
||||||
|
0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0xe6, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, |
||||||
|
0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, |
||||||
|
0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x82, 0x00, |
||||||
|
0x00, 0xc6, 0xc6, 0x6c, 0x7c, 0x38, 0x7c, 0x6c, 0xc6, 0xc6, 0x00, |
||||||
|
0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, |
||||||
|
0x00, 0xfe, 0xc6, 0x86, 0x0c, 0x18, 0x30, 0x62, 0xc6, 0xfe, 0x00, |
||||||
|
|
||||||
|
# 'abcdefghijklmnopqrstuvwxyz' |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0x00, 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x1c, 0x36, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, |
||||||
|
0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0xe6, 0x00, |
||||||
|
0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, |
||||||
|
0x0c, 0x0c, 0x00, 0x1c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, |
||||||
|
0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0xe6, 0x00, |
||||||
|
0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7c, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0xde, 0x76, 0x60, 0x60, 0x60, 0xf0, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x34, 0x18, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0xfe, 0x8c, 0x18, 0x30, 0x62, 0xfe, 0x00, |
||||||
|
|
||||||
|
# '0987654321^ !"\0$%&/()=?` °\\}][{' |
||||||
|
0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0xfe, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x1e, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, |
||||||
|
0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, |
||||||
|
0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3c, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, |
||||||
|
0x66, 0x66, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7c, 0x04, 0x14, 0x18, 0x10, 0x10, 0x20, |
||||||
|
0x10, 0x7c, 0xd6, 0xd6, 0x70, 0x1c, 0xd6, 0xd6, 0x7c, 0x10, 0x10, |
||||||
|
0x00, 0x60, 0x92, 0x96, 0x6c, 0x10, 0x6c, 0xd2, 0x92, 0x0c, 0x00, |
||||||
|
0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, |
||||||
|
0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, |
||||||
|
0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, |
||||||
|
0x18, 0x18, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7c, |
||||||
|
0x00, 0x10, 0x28, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x80, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, 0x00, |
||||||
|
0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x00, |
||||||
|
0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, |
||||||
|
0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, |
||||||
|
0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, |
||||||
|
|
||||||
|
# "@ ~ |<>,;.:-_#'+* " |
||||||
|
0x00, 0x00, 0x3c, 0x42, 0x9d, 0xa5, 0xad, 0xb6, 0x40, 0x3c, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x08, 0x08, 0x7c, 0x08, 0x08, 0x18, 0x18, 0x28, 0x28, 0x48, 0x18, |
||||||
|
0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, |
||||||
|
0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, |
||||||
|
0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x10, 0x20, |
||||||
|
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x08, 0x10, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, |
||||||
|
0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, |
||||||
|
0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||||
|
|
||||||
|
# "äöüÄÖÜߊ" |
||||||
|
0x00, 0xcc, 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0x00, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0xc6, 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
||||||
|
0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x3c, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x6c, 0x60, |
||||||
|
0x28, 0x10, 0x7c, 0xc6, 0xe0, 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
|
||||||
|
# "àäòöùüèéêëôöûîïÿç" |
||||||
|
0x00, 0x60, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0x00, 0x6c, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0x00, 0x60, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x60, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0x00, 0x6c, 0x6c, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0x00, 0x60, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x18, 0x60, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x10, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x10, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, |
||||||
|
0x00, 0x10, 0x6c, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, |
||||||
|
0x00, 0x10, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, |
||||||
|
0x00, 0x6c, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, |
||||||
|
0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, |
||||||
|
0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x10, 0x30, |
||||||
|
|
||||||
|
# "ÀÅÄÉÈÊËÖÔÜÛÙŸ" |
||||||
|
0x60, 0x18, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
||||||
|
0x10, 0x6c, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
||||||
|
0x6c, 0x6c, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, |
||||||
|
0x18, 0x60, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, |
||||||
|
0x60, 0x18, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, |
||||||
|
0x10, 0x6c, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, |
||||||
|
0x6c, 0x6c, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, |
||||||
|
0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Ö |
||||||
|
0x10, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Ô |
||||||
|
0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Ü |
||||||
|
0x10, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Û |
||||||
|
0x60, 0x18, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Ù |
||||||
|
0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x00, # Ÿ |
||||||
|
) |
||||||
|
|
||||||
|
charmap = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + \ |
||||||
|
u'abcdefghijklmnopqrstuvwxyz' + \ |
||||||
|
u'0987654321^ !"\0$%&/()=?` °\\}][{' + \ |
||||||
|
u"@ ~ |<>,;.:-_#'+* " + \ |
||||||
|
u"äöüÄÖÜߊ" + \ |
||||||
|
u"àäòöùüèéêëôöûîïÿç" + \ |
||||||
|
u"ÀÅÄÉÈÊËÖÔÜÛÙŸ" |
||||||
|
|
||||||
|
char_offsets = {} |
||||||
|
for i in range(len(charmap)): |
||||||
|
char_offsets[charmap[i]] = 11 * i |
||||||
|
# print(i, charmap[i], char_offsets[charmap[i]]) |
||||||
|
|
||||||
|
bitmap_named = { |
||||||
|
'ball': (array('B', ( |
||||||
|
0b00000000, |
||||||
|
0b00000000, |
||||||
|
0b00111100, |
||||||
|
0b01111110, |
||||||
|
0b11111111, |
||||||
|
0b11111111, |
||||||
|
0b11111111, |
||||||
|
0b11111111, |
||||||
|
0b01111110, |
||||||
|
0b00111100, |
||||||
|
0b00000000 |
||||||
|
)), 1, '\x1e'), |
||||||
|
'happy': (array('B', ( |
||||||
|
0b00000000, # 0x00 |
||||||
|
0b00000000, # 0x00 |
||||||
|
0b00111100, # 0x3c |
||||||
|
0b01000010, # 0x42 |
||||||
|
0b10100101, # 0xa5 |
||||||
|
0b10000001, # 0x81 |
||||||
|
0b10100101, # 0xa5 |
||||||
|
0b10011001, # 0x99 |
||||||
|
0b01000010, # 0x42 |
||||||
|
0b00111100, # 0x3c |
||||||
|
0b00000000 # 0x00 |
||||||
|
)), 1, '\x1d'), |
||||||
|
'happy2': (array('B', (0x00, 0x08, 0x14, 0x08, 0x01, 0x00, 0x00, 0x61, 0x30, 0x1c, 0x07, |
||||||
|
0x00, 0x20, 0x50, 0x20, 0x00, 0x80, 0x80, 0x86, 0x0c, 0x38, 0xe0)), 2, '\x1c'), |
||||||
|
'heart': (array('B', (0x00, 0x00, 0x6c, 0x92, 0x82, 0x82, 0x44, 0x28, 0x10, 0x00, 0x00)), 1, '\x1b'), |
||||||
|
'HEART': (array('B', (0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00)), 1, '\x1a'), |
||||||
|
'heart2': (array('B', (0x00, 0x0c, 0x12, 0x21, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, |
||||||
|
0x00, 0x60, 0x90, 0x08, 0x08, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00)), 2, '\x19'), |
||||||
|
'HEART2': (array('B', (0x00, 0x0c, 0x1e, 0x3f, 0x3f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, |
||||||
|
0x00, 0x60, 0xf0, 0xf8, 0xf8, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00)), 2, '\x18'), |
||||||
|
'fablab': (array('B', (0x07, 0x0e, 0x1b, 0x03, 0x21, 0x2c, 0x2e, 0x26, 0x14, 0x1c, 0x06, |
||||||
|
0x80, 0x60, 0x30, 0x80, 0x88, 0x38, 0xe8, 0xc8, 0x10, 0x30, 0xc0)), 2, '\x17'), |
||||||
|
'bicycle': (array('B', (0x01, 0x02, 0x00, 0x01, 0x07, 0x09, 0x12, 0x12, 0x10, 0x08, 0x07, |
||||||
|
0x00, 0x87, 0x81, 0x5f, 0x22, 0x94, 0x49, 0x5f, 0x49, 0x80, 0x00, |
||||||
|
0x00, 0x80, 0x00, 0x80, 0x70, 0xc8, 0x24, 0xe4, 0x04, 0x88, 0x70)), 3, '\x16'), |
||||||
|
'bicycle_r': (array('B', (0x00, 0x00, 0x00, 0x00, 0x07, 0x09, 0x12, 0x13, 0x10, 0x08, 0x07, |
||||||
|
0x00, 0xf0, 0x40, 0xfd, 0x22, 0x94, 0x49, 0xfd, 0x49, 0x80, 0x00, |
||||||
|
0x40, 0xa0, 0x80, 0x40, 0x70, 0xc8, 0x24, 0x24, 0x04, 0x88, 0x70)), 3, '\x15'), |
||||||
|
'owncloud': (array('B', (0x00, 0x01, 0x02, 0x03, 0x06, 0x0c, 0x1a, 0x13, 0x11, 0x19, 0x0f, |
||||||
|
0x78, 0xcc, 0x87, 0xfc, 0x42, 0x81, 0x81, 0x81, 0x81, 0x43, 0xbd, |
||||||
|
0x00, 0x00, 0x00, 0x80, 0x80, 0xe0, 0x30, 0x10, 0x28, 0x28, 0xd0)), 3, '\x14'), |
||||||
|
} |
||||||
|
|
||||||
|
bitmap_builtin = {} |
||||||
|
for i in bitmap_named: |
||||||
|
bitmap_builtin[bitmap_named[i][2]] = bitmap_named[i] |
||||||
|
|
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
self.bitmap_preloaded = [([], 0)] |
||||||
|
self.bitmaps_preloaded_unused = False |
||||||
|
|
||||||
|
def add_preload_img(self, filename): |
||||||
|
"""Still used by main, but deprecated. PLease use ":"-notation for bitmap() / bitmap_text()""" |
||||||
|
self.bitmap_preloaded.append(SimpleTextAndIcons.bitmap_img(filename)) |
||||||
|
self.bitmaps_preloaded_unused = True |
||||||
|
|
||||||
|
|
||||||
|
def are_preloaded_unused(self): |
||||||
|
"""Still used by main, but deprecated. PLease use ":"-notation for bitmap() / bitmap_text()""" |
||||||
|
return self.bitmaps_preloaded_unused == True |
||||||
|
|
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def _get_named_bitmaps_keys(): |
||||||
|
return SimpleTextAndIcons.bitmap_named.keys() |
||||||
|
|
||||||
|
|
||||||
|
def bitmap_char(self, ch): |
||||||
|
"""Returns a tuple of 11 bytes, it is the bitmap data of given character. |
||||||
|
Example: ch = '_' returns (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255). |
||||||
|
The bits in each byte are horizontal, highest bit is left. |
||||||
|
""" |
||||||
|
if ord(ch) < 32: |
||||||
|
if ch in SimpleTextAndIcons.bitmap_builtin: |
||||||
|
return SimpleTextAndIcons.bitmap_builtin[ch][:2] |
||||||
|
|
||||||
|
self.bitmaps_preloaded_unused = False |
||||||
|
return self.bitmap_preloaded[ord(ch)] |
||||||
|
|
||||||
|
o = SimpleTextAndIcons.char_offsets[ch] |
||||||
|
return (SimpleTextAndIcons.font_11x44[o:o + 11], 1) |
||||||
|
|
||||||
|
|
||||||
|
def bitmap_text(self, text): |
||||||
|
"""Returns a tuple of (buffer, length_in_byte_columns_aka_chars) |
||||||
|
We preprocess the text string for substitution patterns |
||||||
|
"::" is replaced with a single ":" |
||||||
|
":1: is replaced with CTRL-A referencing the first preloaded or loaded image. |
||||||
|
":happy:" is replaced with a reference to a builtin smiley glyph |
||||||
|
":heart:" is replaced with a reference to a builtin heart glyph |
||||||
|
":gfx/logo.png:" preloads the file gfx/logo.png and is replaced the corresponding control char. |
||||||
|
""" |
||||||
|
|
||||||
|
def replace_symbolic(m): |
||||||
|
name = m.group(1) |
||||||
|
if name == '': |
||||||
|
return ':' |
||||||
|
if re.match('^[0-9]*$', name): # py3 name.isdecimal() |
||||||
|
return chr(int(name)) |
||||||
|
if '.' in name: |
||||||
|
self.bitmap_preloaded.append(SimpleTextAndIcons.bitmap_img(name)) |
||||||
|
return chr(len(self.bitmap_preloaded) - 1) |
||||||
|
return SimpleTextAndIcons.bitmap_named[name][2] |
||||||
|
|
||||||
|
text = re.sub(r':([^:]*):', replace_symbolic, text) |
||||||
|
buf = array('B') |
||||||
|
cols = 0 |
||||||
|
for c in text: |
||||||
|
(b, n) = self.bitmap_char(c) |
||||||
|
buf.extend(b) |
||||||
|
cols += n |
||||||
|
return (buf, cols) |
||||||
|
|
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def bitmap_img(file): |
||||||
|
"""Returns a tuple of (buffer, length_in_byte_columns) representing the given image file. |
||||||
|
It has to be an 8-bit grayscale image or a color image with 8 bit per channel. Color pixels are converted to |
||||||
|
grayscale by arithmetic mean. Threshold for an active led is then > 127. |
||||||
|
If the width is not a multiple on 8 it will be padded with empty pixel-columns. |
||||||
|
""" |
||||||
|
from PIL import Image |
||||||
|
|
||||||
|
im = Image.open(file) |
||||||
|
print("fetching bitmap from file %s -> (%d x %d)" % (file, im.width, im.height)) |
||||||
|
if im.height != 11: |
||||||
|
sys.exit("%s: image height must be 11px. Seen %d" % (file, im.height)) |
||||||
|
buf = array('B') |
||||||
|
cols = int((im.width + 7) / 8) |
||||||
|
for col in range(cols): |
||||||
|
for row in range(11): # [0..10] |
||||||
|
byte_val = 0 |
||||||
|
for bit in range(8): # [0..7] |
||||||
|
bit_val = 0 |
||||||
|
x = 8 * col + bit |
||||||
|
if x < im.width and row < im.height: |
||||||
|
pixel_color = im.getpixel((x, row)) |
||||||
|
if isinstance(pixel_color, tuple): |
||||||
|
monochrome_color = sum(pixel_color[:3]) / len(pixel_color[:3]) |
||||||
|
elif isinstance(pixel_color, int): |
||||||
|
monochrome_color = pixel_color |
||||||
|
else: |
||||||
|
sys.exit("%s: Unknown pixel format detected (%s)!" % (file, pixel_color)) |
||||||
|
if monochrome_color > 127: |
||||||
|
bit_val = 1 << (7 - bit) |
||||||
|
byte_val += bit_val |
||||||
|
buf.append(byte_val) |
||||||
|
im.close() |
||||||
|
return (buf, cols) |
||||||
|
|
||||||
|
|
||||||
|
def bitmap(self, arg): |
||||||
|
"""If arg is a valid and existing path name, we load it as an image. |
||||||
|
Otherwise, we take it as a string (with ":"-notation, see bitmap_text()). |
||||||
|
""" |
||||||
|
if os.path.exists(arg): |
||||||
|
return SimpleTextAndIcons.bitmap_img(arg) |
||||||
|
return self.bitmap_text(arg) |
||||||
|
|
||||||
|
|
||||||
|
class LedNameBadge: |
||||||
|
_protocol_header_template = ( |
||||||
|
0x77, 0x61, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
||||||
|
) |
||||||
|
_have_pyhidapi = False |
||||||
|
|
||||||
|
try: |
||||||
|
if sys.version_info[0] < 3: |
||||||
|
print("Preferring Pyusb over Pyhidapi with Python 2.x") |
||||||
|
raise Exception("Prefer usb.core with python-2.x because of https://github.com/jnweiger/led-badge-ls32/issues/9") |
||||||
|
import pyhidapi |
||||||
|
pyhidapi.hid_init() |
||||||
|
_have_pyhidapi = True |
||||||
|
print("Pyhidapi detected") |
||||||
|
except: |
||||||
|
try: |
||||||
|
import usb.core |
||||||
|
print("Pyusb detected") |
||||||
|
except: |
||||||
|
print("ERROR: Need the pyhidapi or usb.core module.") |
||||||
|
if sys.platform == "darwin": |
||||||
|
print("""Please try |
||||||
|
pip3 install pyhidapi |
||||||
|
pip install pyhidapi |
||||||
|
brew install hidapi |
||||||
|
""") |
||||||
|
elif sys.platform == "linux": |
||||||
|
print("""Please try |
||||||
|
sudo pip3 install pyhidapi |
||||||
|
sudo pip install pyhidapi |
||||||
|
sudo apt-get install libhidapi-hidraw0 |
||||||
|
sudo ln -s /usr/lib/x86_64-linux-gnu/libhidapi-hidraw.so.0 /usr/local/lib/ |
||||||
|
or |
||||||
|
sudo apt-get install python3-usb |
||||||
|
""") |
||||||
|
else: # windows? |
||||||
|
print("""Please try with Linux or MacOS or help us implement support for """ + sys.platform) |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def _prepare_iterable(iterable, min_, max_): |
||||||
|
try: |
||||||
|
iterable = [min(max(x, min_), max_) for x in iterable] |
||||||
|
iterable = tuple(iterable) + (iterable[-1],) * (8 - len(iterable)) # repeat last element |
||||||
|
return iterable |
||||||
|
except: |
||||||
|
raise TypeError("Please give a list or tuple with at least one number: " + str(iterable)) |
||||||
|
|
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def header(lengths, speeds, modes, blinks, ants, brightness=100, date=datetime.now()): |
||||||
|
"""Create a protocol header |
||||||
|
* length, speeds, modes, blinks, ants are iterables with at least one element |
||||||
|
* lengths[0] is the number of chars/byte-columns of the first text/bitmap, lengths[1] of the second, |
||||||
|
and so on... |
||||||
|
* len(length) should match the designated bitmap data |
||||||
|
* speeds come in as 1..8, but will be decremented to 0..7, here. |
||||||
|
* modes: 0..8 |
||||||
|
* blinks and ants: 0..1 or even False..True, |
||||||
|
* brightness, if given, is any number, but it'll be limited to 25, 50, 75, 100 (percent), here |
||||||
|
* date, if given, is a datetime object. It will be written in the header, but is not to be seen on the |
||||||
|
devices screen. |
||||||
|
""" |
||||||
|
try: |
||||||
|
lengths_sum = sum(lengths) |
||||||
|
except: |
||||||
|
raise TypeError("Please give a list or tuple with at least one number: " + str(lengths)) |
||||||
|
if lengths_sum > (8192 - len(LedNameBadge._protocol_header_template)) / 11 + 1: |
||||||
|
raise ValueError("The given lengths seem to be far too high: " + str(lengths)) |
||||||
|
|
||||||
|
|
||||||
|
ants = LedNameBadge._prepare_iterable(ants, 0, 1) |
||||||
|
blinks = LedNameBadge._prepare_iterable(blinks, 0, 1) |
||||||
|
speeds = LedNameBadge._prepare_iterable(speeds, 1, 8) |
||||||
|
modes = LedNameBadge._prepare_iterable(modes, 0, 8) |
||||||
|
|
||||||
|
speeds = [x - 1 for x in speeds] |
||||||
|
|
||||||
|
h = list(LedNameBadge._protocol_header_template) |
||||||
|
|
||||||
|
if brightness <= 25: |
||||||
|
h[5] = 0x40 |
||||||
|
elif brightness <= 50: |
||||||
|
h[5] = 0x20 |
||||||
|
elif brightness <= 75: |
||||||
|
h[5] = 0x10 |
||||||
|
# else default 100% == 0x00 |
||||||
|
|
||||||
|
for i in range(8): |
||||||
|
h[6] += blinks[i] << i |
||||||
|
h[7] += ants[i] << i |
||||||
|
|
||||||
|
for i in range(8): |
||||||
|
h[8 + i] = 16 * speeds[i] + modes[i] |
||||||
|
|
||||||
|
for i in range(len(lengths)): |
||||||
|
h[17 + (2 * i) - 1] = lengths[i] // 256 |
||||||
|
h[17 + (2 * i)] = lengths[i] % 256 |
||||||
|
|
||||||
|
try: |
||||||
|
h[38 + 0] = date.year % 100 |
||||||
|
h[38 + 1] = date.month |
||||||
|
h[38 + 2] = date.day |
||||||
|
h[38 + 3] = date.hour |
||||||
|
h[38 + 4] = date.minute |
||||||
|
h[38 + 5] = date.second |
||||||
|
except: |
||||||
|
raise TypeError("Please give a datetime object: " + str(date)) |
||||||
|
|
||||||
|
return h |
||||||
|
|
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def write(buf): |
||||||
|
"""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. |
||||||
|
""" |
||||||
|
need_padding = len(buf) % 64 |
||||||
|
if need_padding: |
||||||
|
buf.extend((0,) * (64 - need_padding)) |
||||||
|
|
||||||
|
if len(buf) > 8192: |
||||||
|
print("Writing more than 8192 bytes damages the display!") |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
if LedNameBadge._have_pyhidapi: |
||||||
|
dev_info = LedNameBadge.pyhidapi.hid_enumerate(0x0416, 0x5020) |
||||||
|
# dev = pyhidapi.hid_open(0x0416, 0x5020) |
||||||
|
if dev_info: |
||||||
|
dev = LedNameBadge.pyhidapi.hid_open_path(dev_info[0].path) |
||||||
|
print("using [%s %s] int=%d page=%s via pyHIDAPI" % ( |
||||||
|
dev_info[0].manufacturer_string, dev_info[0].product_string, dev_info[0].interface_number, dev_info[0].usage_page)) |
||||||
|
else: |
||||||
|
print("No led tag with vendorID 0x0416 and productID 0x5020 found.") |
||||||
|
print("Connect the led tag and run this tool as root.") |
||||||
|
sys.exit(1) |
||||||
|
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]) |
||||||
|
LedNameBadge.pyhidapi.hid_write(dev, sendbuf) |
||||||
|
LedNameBadge.pyhidapi.hid_close(dev) |
||||||
|
else: |
||||||
|
dev = LedNameBadge.usb.core.find(idVendor=0x0416, idProduct=0x5020) |
||||||
|
if dev is None: |
||||||
|
print("No led tag with vendorID 0x0416 and productID 0x5020 found.") |
||||||
|
print("Connect the led tag and run this tool as root.") |
||||||
|
sys.exit(1) |
||||||
|
try: |
||||||
|
# win32: NotImplementedError: is_kernel_driver_active |
||||||
|
if dev.is_kernel_driver_active(0): |
||||||
|
dev.detach_kernel_driver(0) |
||||||
|
except: |
||||||
|
pass |
||||||
|
dev.set_configuration() |
||||||
|
print("using [%s %s] bus=%d dev=%d" % (dev.manufacturer, dev.product, dev.bus, dev.address)) |
||||||
|
for i in range(int(len(buf) / 64)): |
||||||
|
time.sleep(0.1) |
||||||
|
dev.write(1, buf[i * 64:i * 64 + 64]) |
||||||
|
|
||||||
|
|
||||||
|
def split_to_ints(list_str): |
||||||
|
return [int(x) for x in re.split(r'[\s,]+', list_str)] |
||||||
|
|
||||||
|
def main(): |
||||||
|
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, |
||||||
|
description='Upload messages or graphics to a 11x44 led badge via USB HID.\nVersion %s from https://github.com/jnweiger/led-badge-ls32\n -- see there for more examples and for updates.' % __version, |
||||||
|
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="Set to 1 to ensure connect via HID API, program will then not fallback to usb.core library") |
||||||
|
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") |
||||||
|
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('-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", |
||||||
|
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") |
||||||
|
parser.add_argument('--mode-help', action='version', help=argparse.SUPPRESS, version=""" |
||||||
|
|
||||||
|
-m 5 "Animation" |
||||||
|
|
||||||
|
Animation frames are 6 character (or 48px) wide. Upload an animation of |
||||||
|
N frames as one image N*48 pixels wide, 11 pixels high. |
||||||
|
Frames run from left to right and repeat endless. |
||||||
|
Speeds [1..8] result in ca. [1.2 1.3 2.0 2.4 2.8 4.5 7.5 15] fps. |
||||||
|
|
||||||
|
Example of a slowly beating heart: |
||||||
|
sudo %s -s1 -m5 " :heart2: :HEART2:" |
||||||
|
|
||||||
|
-m 9 "Smooth" |
||||||
|
-m 10 "Rotate" |
||||||
|
|
||||||
|
These modes are mentioned in the BMP Badge software. |
||||||
|
Text is shown static, or sometimes (longer texts?) not shown at all. |
||||||
|
One significant difference is: The text of the first message stays visible after |
||||||
|
upload, even if the USB cable remains connected. |
||||||
|
(No "rotation" or "smoothing"(?) effect can be expected, though) |
||||||
|
""" % sys.argv[0]) |
||||||
|
args = parser.parse_args() |
||||||
|
|
||||||
|
creator = SimpleTextAndIcons() |
||||||
|
|
||||||
|
if args.preload: |
||||||
|
for filename in args.preload: |
||||||
|
creator.add_preload_img(filename) |
||||||
|
|
||||||
|
msg_bitmaps = [] |
||||||
|
for msg_arg in args.message: |
||||||
|
msg_bitmaps.append(creator.bitmap(msg_arg)) |
||||||
|
|
||||||
|
if creator.are_preloaded_unused(): |
||||||
|
print( |
||||||
|
"\nWARNING:\n Your preloaded images are not used.\n Try without '-p' or embed the control character '^A' in your message.\n") |
||||||
|
|
||||||
|
if '12' in args.type or '12' in sys.argv[0]: |
||||||
|
print("Type: 12x48") |
||||||
|
for msg_bitmap in msg_bitmaps: |
||||||
|
# trivial hack to support 12x48 badges: |
||||||
|
# patch extra empty lines into the message stream. |
||||||
|
for i in reversed(range(1, int(len(msg_bitmap[0]) / 11) + 1)): |
||||||
|
msg_bitmap[0][i * 11:i * 11] = array('B', [0]) |
||||||
|
else: |
||||||
|
print("Type: 11x44") |
||||||
|
|
||||||
|
lengths = [b[1] for b in msg_bitmaps] |
||||||
|
speeds = split_to_ints(args.speed) |
||||||
|
modes = split_to_ints(args.mode) |
||||||
|
blinks = split_to_ints(args.blink) |
||||||
|
ants = split_to_ints(args.ants) |
||||||
|
brightness = int(args.brightness) |
||||||
|
|
||||||
|
buf = array('B') |
||||||
|
buf.extend(LedNameBadge.header(lengths, speeds, modes, blinks, ants, brightness)) |
||||||
|
|
||||||
|
for msg_bitmap in msg_bitmaps: |
||||||
|
buf.extend(msg_bitmap[0]) |
||||||
|
|
||||||
|
if not LedNameBadge._have_pyhidapi: |
||||||
|
if args.hid != "0": |
||||||
|
sys.exit("HID API access is needed but not initialized. Fix your setup") |
||||||
|
|
||||||
|
LedNameBadge.write(buf) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
main() |
After Width: | Height: | Size: 4.3 MiB |
After Width: | Height: | Size: 2.6 KiB |
@ -0,0 +1,35 @@ |
|||||||
|
@startuml bitmap_data_onebyte |
||||||
|
salt |
||||||
|
title One byte |
||||||
|
{# |
||||||
|
{ most significant bit 7 |
||||||
|
leftmost pixel } | bit 6 | ... | bit 1 | { least significant bit 0 |
||||||
|
rightmost pixel } |
||||||
|
} |
||||||
|
@enduml |
||||||
|
|
||||||
|
@startuml bitmap_data_onescene |
||||||
|
salt |
||||||
|
title One scene |
||||||
|
{# |
||||||
|
byte 0 == 8 pixel | byte 11 == 8 pixel | byte 22 == 8 pixel | ... |
||||||
|
byte 1 ... | byte 12 ... | byte 23 ... | ... |
||||||
|
byte 2 ... | byte 13 ... | byte 24 ... | ... |
||||||
|
byte 3 ... | byte 14 ... | byte 25 ... | ... |
||||||
|
byte 4 ... | byte 15 ... | byte 26 ... | ... |
||||||
|
byte 5 ... | byte 16 ... | byte 27 ... | ... |
||||||
|
byte 6 ... | byte 17 ... | byte 28 ... | ... |
||||||
|
byte 7 ... | byte 18 ... | byte 29 ... | ... |
||||||
|
byte 8 ... | byte 19 ... | byte 30 ... | ... |
||||||
|
byte 9 ... | byte 20 ... | byte 31 ... | ... |
||||||
|
byte 10 ... | byte 21 ... | byte 32 ... | ... |
||||||
|
} |
||||||
|
@enduml |
||||||
|
|
||||||
|
@startuml bitmap_data_all |
||||||
|
salt |
||||||
|
title Complete bitmap data |
||||||
|
{# |
||||||
|
scene 0 == x bytes | ... | scene n == z bytes |
||||||
|
} |
||||||
|
@enduml |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 3.7 MiB |
After Width: | Height: | Size: 133 B |
@ -0,0 +1,8 @@ |
|||||||
|
import sys |
||||||
|
import unittest |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
sys.path.append("..") |
||||||
|
suite = unittest.TestLoader().discover(".") |
||||||
|
unittest.TextTestRunner().run(suite) |
||||||
|
|
@ -0,0 +1,34 @@ |
|||||||
|
from array import array |
||||||
|
from unittest import TestCase |
||||||
|
|
||||||
|
from lednamebadge import SimpleTextAndIcons as testee |
||||||
|
|
||||||
|
|
||||||
|
class Test(TestCase): |
||||||
|
def test_bitmap_png(self): |
||||||
|
creator = testee() |
||||||
|
buf = creator.bitmap("resources/bitpatterns.png") |
||||||
|
self.assertEqual((array('B', |
||||||
|
[128, 64, 32, 16, 8, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 64, 32, 0, 1, 2, 3, |
||||||
|
4, 5, 15, 31, 63, 127, 255]), |
||||||
|
3), buf) |
||||||
|
|
||||||
|
def test_bitmap_text(self): |
||||||
|
creator = testee() |
||||||
|
buf = creator.bitmap("/:HEART2:\\") |
||||||
|
self.assertEqual((array('B', |
||||||
|
[0, 0, 2, 6, 12, 24, 48, 96, 192, 128, 0, 0, 12, 30, 63, 63, 63, 31, 15, 7, 3, 1, 0, 96, |
||||||
|
240, 248, 248, 248, 240, 224, 192, 128, 0, 0, 128, 192, 96, 48, 24, 12, 6, 2, 0, 0]), |
||||||
|
4), buf) |
||||||
|
|
||||||
|
def test_preload(self): |
||||||
|
creator = testee() |
||||||
|
self.assertFalse(creator.are_preloaded_unused()) |
||||||
|
creator.add_preload_img("resources/bitpatterns.png") |
||||||
|
self.assertTrue(creator.are_preloaded_unused()) |
||||||
|
buf = creator.bitmap("\x01") |
||||||
|
self.assertFalse(creator.are_preloaded_unused()) |
||||||
|
self.assertEqual((array('B', |
||||||
|
[128, 64, 32, 16, 8, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 64, 32, 0, 1, 2, 3, |
||||||
|
4, 5, 15, 31, 63, 127, 255]), |
||||||
|
3), buf) |
@ -0,0 +1,79 @@ |
|||||||
|
import datetime |
||||||
|
from unittest import TestCase |
||||||
|
|
||||||
|
from lednamebadge import LedNameBadge as testee |
||||||
|
|
||||||
|
|
||||||
|
class Test(TestCase): |
||||||
|
def setUp(self): |
||||||
|
self.test_date = datetime.datetime(2022, 11, 13, 17, 38, 24) |
||||||
|
|
||||||
|
def test_prepare_collection_expand(self): |
||||||
|
self.assertEqual((1, 1, 1, 1, 1, 1, 1, 1), testee._prepare_iterable((1,), 1, 8)) |
||||||
|
self.assertEqual((1, 2, 3, 4, 4, 4, 4, 4), testee._prepare_iterable([1, 2, 3, 4], 1, 8)) |
||||||
|
self.assertEqual((1, 2, 3, 4, 5, 6, 7, 8), testee._prepare_iterable((1, 2, 3, 4, 5, 6, 7, 8), 1, 8)) |
||||||
|
# Weired, but possible: |
||||||
|
self.assertEqual(('A', 'B', 'C', 'D', 'D', 'D', 'D', 'D'), testee._prepare_iterable("ABCD", 'A', 'Z')) |
||||||
|
self.assertEqual((True, False, True, True, True, True, True, True), testee._prepare_iterable((True, False, True), False, True)) |
||||||
|
|
||||||
|
def test_prepare_collection_limit(self): |
||||||
|
self.assertEqual((1, 8, 8, 8, 8, 8, 8, 8), testee._prepare_iterable([-1, 9], 1, 8)) |
||||||
|
# Weired, but possible: |
||||||
|
self.assertEqual(('C', 'C', 'C', 'D', 'E', 'F', 'F', 'F'), testee._prepare_iterable("ABCDEFGH", 'C', 'F')) |
||||||
|
self.assertEqual((True, 1, True, True, True, True, True, True), testee._prepare_iterable((True, False, True), 1, 8)) |
||||||
|
self.assertEqual((0, False, 0, 0, 0, 0, 0, 0), testee._prepare_iterable((True, False, True), -2, 0)) |
||||||
|
|
||||||
|
def test_prepare_collection_type(self): |
||||||
|
with self.assertRaises(TypeError): |
||||||
|
testee._prepare_iterable(4, 1, 8) |
||||||
|
with self.assertRaises(TypeError): |
||||||
|
testee._prepare_iterable([], 1, 8) |
||||||
|
|
||||||
|
def test_header_2msgs(self): |
||||||
|
buf = testee.header((6, 7), (5, 3), (6, 2), (0, 1), (1, 0), 75, self.test_date) |
||||||
|
self.assertEqual([119, 97, 110, 103, 0, 16, 254, 1, 70, 34, 34, 34, 34, 34, 34, 34, 0, 6, 0, 7, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 11, 13, 17, 38, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0], buf) |
||||||
|
|
||||||
|
def test_header_8msgs(self): |
||||||
|
buf = testee.header((1, 2, 3, 4, 5, 6, 7, 8), |
||||||
|
(1, 2, 3, 4, 5, 6, 7, 8), |
||||||
|
(1, 2, 3, 4, 5, 6, 7, 8), |
||||||
|
(0, 1, 0, 1, 0, 1, 0, 1), |
||||||
|
(1, 0, 1, 0, 1, 0, 1, 0), |
||||||
|
25, |
||||||
|
self.test_date) |
||||||
|
self.assertEqual([119, 97, 110, 103, 0, 64, 170, 85, 1, 18, 35, 52, 69, 86, 103, 120, 0, 1, 0, 2, 0, 3, 0, 4, 0, |
||||||
|
5, 0, 6, 0, 7, 0, 8, 0, 0, 0, 0, 0, 0, 22, 11, 13, 17, 38, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0], buf) |
||||||
|
|
||||||
|
def test_header_brightness(self): |
||||||
|
buf = testee.header((6,), (4,), (4,), (0,), (0,), 25, self.test_date) |
||||||
|
self.assertEqual([119, 97, 110, 103, 0, 64, 0, 0, 52, 52, 52, 52, 52, 52, 52, 52, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 11, 13, 17, 38, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0], buf) |
||||||
|
buf = testee.header((6,), (4,), (4,), (0,), (0,), 26, self.test_date) |
||||||
|
self.assertEqual([119, 97, 110, 103, 0, 32, 0, 0, 52, 52, 52, 52, 52, 52, 52, 52, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 11, 13, 17, 38, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0], buf) |
||||||
|
buf = testee.header((6,), (4,), (4,), (0,), (0,), 60, self.test_date) |
||||||
|
self.assertEqual([119, 97, 110, 103, 0, 16, 0, 0, 52, 52, 52, 52, 52, 52, 52, 52, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 11, 13, 17, 38, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0], buf) |
||||||
|
buf = testee.header((6,), (4,), (4,), (0,), (0,), 80, self.test_date) |
||||||
|
self.assertEqual([119, 97, 110, 103, 0, 0, 0, 0, 52, 52, 52, 52, 52, 52, 52, 52, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 11, 13, 17, 38, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0], buf) |
||||||
|
|
||||||
|
def test_header_date(self): |
||||||
|
buf1 = testee.header((6,), (4,), (4,), (0,), (0,), 100, self.test_date) |
||||||
|
buf2 = testee.header((6,), (4,), (4,), (0,), (0,), 100) |
||||||
|
self.assertEqual(buf1[0:38], buf2[0:38]) |
||||||
|
self.assertEqual(buf1[38 + 6:], buf2[38 + 6:]) |
||||||
|
self.assertNotEqual(buf1[38:38 + 6], buf2[38:38 + 6]) |
||||||
|
|
||||||
|
def test_header_type(self): |
||||||
|
with self.assertRaises(TypeError): |
||||||
|
testee.header(("nan",), (4,), (4,), (0,), (0,), 80, self.test_date) |
||||||
|
with self.assertRaises(ValueError): |
||||||
|
testee.header((370,380), (4,), (4,), (0,), (0,), 80, self.test_date) |