512 lines
15 KiB
Python
Executable File
512 lines
15 KiB
Python
Executable File
#!/usr/bin/env python
|
|
""" CLI
|
|
|
|
This module acts as a command line interface for python-escpos. It mirrors
|
|
closely the available ESCPOS commands while adding a couple extra ones for convience.
|
|
|
|
It requires you to have a configuration file. See documentation for details.
|
|
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
|
|
import argparse
|
|
import sys
|
|
import six
|
|
from . import config
|
|
|
|
# Used in demo method
|
|
# Key: The name of escpos function and the argument passed on the CLI. Some
|
|
# manual translation is done in the case of barcodes_a -> barcode.
|
|
# Value: A list of dictionaries to pass to the escpos function as arguments.
|
|
DEMO_FUNCTIONS = {
|
|
'text': [
|
|
{'txt': 'Hello, World!\n',}
|
|
],
|
|
'qr': [
|
|
{'text': 'This tests a QR code'},
|
|
{'text': 'https://en.wikipedia.org/'}
|
|
],
|
|
'barcodes_a': [
|
|
{'bc': 'UPC-A', 'code': '13243546576'},
|
|
{'bc': 'UPC-E', 'code': '132435'},
|
|
{'bc': 'EAN13', 'code': '1324354657687'},
|
|
{'bc': 'EAN8', 'code': '1324354'},
|
|
{'bc': 'CODE39', 'code': 'TEST'},
|
|
{'bc': 'ITF', 'code': '55867492279103'},
|
|
{'bc': 'NW7', 'code': 'A00000000A'},
|
|
],
|
|
'barcodes_b': [
|
|
{'bc': 'UPC-A', 'code': '13243546576', 'function_type': 'B'},
|
|
{'bc': 'UPC-E', 'code': '132435', 'function_type': 'B'},
|
|
{'bc': 'EAN13', 'code': '1324354657687', 'function_type': 'B'},
|
|
{'bc': 'EAN8', 'code': '1324354', 'function_type': 'B'},
|
|
{'bc': 'CODE39', 'code': 'TEST', 'function_type': 'B'},
|
|
{'bc': 'ITF', 'code': '55867492279103', 'function_type': 'B'},
|
|
{'bc': 'NW7', 'code': 'A00000000A', 'function_type': 'B'},
|
|
{'bc': 'CODE93', 'code': 'A00000000A', 'function_type': 'B'},
|
|
{'bc': 'CODE93', 'code': '1324354657687', 'function_type': 'B'},
|
|
{'bc': 'CODE128A', 'code': 'TEST', 'function_type': 'B'},
|
|
{'bc': 'CODE128B', 'code': 'TEST', 'function_type': 'B'},
|
|
{'bc': 'CODE128C', 'code': 'TEST', 'function_type': 'B'},
|
|
{'bc': 'GS1-128', 'code': '00123456780000000001', 'function_type': 'B'},
|
|
{'bc': 'GS1 DataBar Omnidirectional', 'code': '0000000000000', 'function_type': 'B'},
|
|
{'bc': 'GS1 DataBar Truncated', 'code': '0000000000000', 'function_type': 'B'},
|
|
{'bc': 'GS1 DataBar Limited', 'code': '0000000000000', 'function_type': 'B'},
|
|
{'bc': 'GS1 DataBar Expanded', 'code': '00AAAAAAA', 'function_type': 'B'},
|
|
],
|
|
}
|
|
|
|
# Used to build the CLI
|
|
# A list of dictionaries. Each dict is a CLI argument.
|
|
# Keys:
|
|
# parser: A dict of args for command_parsers.add_parser
|
|
# defaults: A dict of args for subparser.set_defaults
|
|
# arguments: A list of dicts of args for subparser.add_argument
|
|
ESCPOS_COMMANDS = [
|
|
{
|
|
'parser': {
|
|
'name': 'qr',
|
|
'help': 'Print a QR code',
|
|
},
|
|
'defaults': {
|
|
'func': 'qr',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--text',),
|
|
'help': 'Text to print as a qr code',
|
|
'required': True,
|
|
}
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'barcode',
|
|
'help': 'Print a barcode',
|
|
},
|
|
'defaults': {
|
|
'func': 'barcode',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--code',),
|
|
'help': 'Barcode data to print',
|
|
'required': True,
|
|
},
|
|
{
|
|
'option_strings': ('--bc',),
|
|
'help': 'Barcode format',
|
|
'required': True,
|
|
},
|
|
{
|
|
'option_strings': ('--height',),
|
|
'help': 'Barcode height in px',
|
|
'type': int,
|
|
},
|
|
{
|
|
'option_strings': ('--width',),
|
|
'help': 'Barcode width',
|
|
'type': int,
|
|
},
|
|
{
|
|
'option_strings': ('--pos',),
|
|
'help': 'Label position',
|
|
'choices': ['BELOW', 'ABOVE', 'BOTH', 'OFF'],
|
|
},
|
|
{
|
|
'option_strings': ('--font',),
|
|
'help': 'Label font',
|
|
'choices': ['A', 'B'],
|
|
},
|
|
{
|
|
'option_strings': ('--align_ct',),
|
|
'help': 'Align barcode center',
|
|
'type': bool,
|
|
},
|
|
{
|
|
'option_strings': ('--function_type',),
|
|
'help': 'ESCPOS function type',
|
|
'choices': ['A', 'B'],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'text',
|
|
'help': 'Print plain text',
|
|
},
|
|
'defaults': {
|
|
'func': 'text',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--txt',),
|
|
'help': 'Plain text to print',
|
|
'required': True,
|
|
}
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'block_text',
|
|
'help': 'Print wrapped text',
|
|
},
|
|
'defaults': {
|
|
'func': 'block_text',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--txt',),
|
|
'help': 'block_text to print',
|
|
'required': True,
|
|
},
|
|
{
|
|
'option_strings': ('--columns',),
|
|
'help': 'Number of columns',
|
|
'type': int,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'cut',
|
|
'help': 'Cut the paper',
|
|
},
|
|
'defaults': {
|
|
'func': 'cut',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--mode',),
|
|
'help': 'Type of cut',
|
|
'choices': ['FULL', 'PART'],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'cashdraw',
|
|
'help': 'Kick the cash drawer',
|
|
},
|
|
'defaults': {
|
|
'func': 'cashdraw',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--pin',),
|
|
'help': 'Which PIN to kick',
|
|
'choices': [2, 5],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'image',
|
|
'help': 'Print an image',
|
|
},
|
|
'defaults': {
|
|
'func': 'image',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--path_img',),
|
|
'help': 'Path to image',
|
|
'required': True,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'fullimage',
|
|
'help': 'Print a fullimage',
|
|
},
|
|
'defaults': {
|
|
'func': 'fullimage',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--img',),
|
|
'help': 'Path to img',
|
|
'required': True,
|
|
},
|
|
{
|
|
'option_strings': ('--max_height',),
|
|
'help': 'Max height of image in px',
|
|
'type': int,
|
|
},
|
|
{
|
|
'option_strings': ('--width',),
|
|
'help': 'Max width of image in px',
|
|
'type': int,
|
|
},
|
|
{
|
|
'option_strings': ('--histeq',),
|
|
'help': 'Equalize the histrogram',
|
|
'type': bool,
|
|
},
|
|
{
|
|
'option_strings': ('--bandsize',),
|
|
'help': 'Size of bands to divide into when printing',
|
|
'type': int,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'charcode',
|
|
'help': 'Set character code table',
|
|
},
|
|
'defaults': {
|
|
'func': 'charcode',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--code',),
|
|
'help': 'Character code',
|
|
'required': True,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'set',
|
|
'help': 'Set text properties',
|
|
},
|
|
'defaults': {
|
|
'func': 'set',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--align',),
|
|
'help': 'Horizontal alignment',
|
|
'choices': ['left', 'center', 'right'],
|
|
},
|
|
{
|
|
'option_strings': ('--font',),
|
|
'help': 'Font choice',
|
|
'choices': ['left', 'center', 'right'],
|
|
},
|
|
{
|
|
'option_strings': ('--text_type',),
|
|
'help': 'Text properties',
|
|
'choices': ['B', 'U', 'U2', 'BU', 'BU2', 'NORMAL'],
|
|
},
|
|
{
|
|
'option_strings': ('--width',),
|
|
'help': 'Width multiplier',
|
|
'type': int,
|
|
},
|
|
{
|
|
'option_strings': ('--height',),
|
|
'help': 'Height multiplier',
|
|
'type': int,
|
|
},
|
|
{
|
|
'option_strings': ('--density',),
|
|
'help': 'Print density',
|
|
'type': int,
|
|
},
|
|
{
|
|
'option_strings': ('--invert',),
|
|
'help': 'White on black printing',
|
|
'type': bool,
|
|
},
|
|
{
|
|
'option_strings': ('--smooth',),
|
|
'help': 'Text smoothing. Effective on >: 4x4 text',
|
|
'type': bool,
|
|
},
|
|
{
|
|
'option_strings': ('--flip',),
|
|
'help': 'Text smoothing. Effective on >: 4x4 text',
|
|
'type': bool,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'hw',
|
|
'help': 'Hardware operations',
|
|
},
|
|
'defaults': {
|
|
'func': 'hw',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--hw',),
|
|
'help': 'Operation',
|
|
'choices': ['INIT', 'SELECT', 'RESET'],
|
|
'required': True,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'control',
|
|
'help': 'Control sequences',
|
|
},
|
|
'defaults': {
|
|
'func': 'control',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--ctl',),
|
|
'help': 'Control sequence',
|
|
'choices': ['LF', 'FF', 'CR', 'HT', 'VT'],
|
|
'required': True,
|
|
},
|
|
{
|
|
'option_strings': ('--pos',),
|
|
'help': 'Horizontal tab position (1-4)',
|
|
'type': int,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'panel_buttons',
|
|
'help': 'Controls panel buttons',
|
|
},
|
|
'defaults': {
|
|
'func': 'panel_buttons',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--enable',),
|
|
'help': 'Feed button enabled',
|
|
'type': bool,
|
|
'required': True,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'parser': {
|
|
'name': 'raw',
|
|
'help': 'Raw data',
|
|
},
|
|
'defaults': {
|
|
'func': '_raw',
|
|
},
|
|
'arguments': [
|
|
{
|
|
'option_strings': ('--msg',),
|
|
'help': 'Raw data to send',
|
|
'required': True,
|
|
},
|
|
],
|
|
},
|
|
]
|
|
|
|
def main():
|
|
"""
|
|
|
|
Handles loading of configuration and creating and processing of command
|
|
line arguments. Called when run from a CLI.
|
|
|
|
"""
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description='CLI for python-escpos',
|
|
epilog='Printer configuration is defined in the python-escpos config'
|
|
'file. See documentation for details.',
|
|
)
|
|
|
|
# Allow config file location to be passed
|
|
parser.add_argument(
|
|
'-c', '--config',
|
|
help='Altnerate path to the configuration file',
|
|
)
|
|
|
|
# Everything interesting runs off of a subparser so we can use the format
|
|
# cli [subparser] -args
|
|
command_subparsers = parser.add_subparsers(
|
|
title='ESCPOS Command',
|
|
)
|
|
|
|
# Build the ESCPOS command arguments
|
|
for command in ESCPOS_COMMANDS:
|
|
parser_command = command_subparsers.add_parser(**command['parser'])
|
|
parser_command.set_defaults(**command['defaults'])
|
|
for argument in command['arguments']:
|
|
option_strings = argument.pop('option_strings')
|
|
parser_command.add_argument(*option_strings, **argument)
|
|
|
|
# Build any custom arguments
|
|
parser_command_demo = command_subparsers.add_parser('demo',
|
|
help='Demonstrates various functions')
|
|
parser_command_demo.set_defaults(func='demo')
|
|
demo_group = parser_command_demo.add_mutually_exclusive_group()
|
|
demo_group.add_argument(
|
|
'--barcodes-a',
|
|
help='Print demo barcodes for function type A',
|
|
action='store_true',
|
|
)
|
|
demo_group.add_argument(
|
|
'--barcodes-b',
|
|
help='Print demo barcodes for function type B',
|
|
action='store_true',
|
|
)
|
|
demo_group.add_argument(
|
|
'--qr',
|
|
help='Print some demo QR codes',
|
|
action='store_true',
|
|
)
|
|
demo_group.add_argument(
|
|
'--text',
|
|
help='Print some demo text',
|
|
action='store_true',
|
|
)
|
|
|
|
# Get only arguments actually passed
|
|
args_dict = vars(parser.parse_args())
|
|
if not args_dict:
|
|
parser.print_help()
|
|
sys.exit()
|
|
command_arguments = dict([k, v] for k, v in six.iteritems(args_dict) if v)
|
|
|
|
# If there was a config path passed, grab it
|
|
config_path = command_arguments.pop('config', None)
|
|
|
|
# Load the configuration and defined printer
|
|
saved_config = config.Config()
|
|
saved_config.load(config_path)
|
|
printer = saved_config.printer()
|
|
|
|
|
|
if not printer:
|
|
raise Exception('No printers loaded from config')
|
|
|
|
|
|
target_command = command_arguments.pop('func')
|
|
|
|
if hasattr(printer, target_command):
|
|
# print command with args
|
|
getattr(printer, target_command)(**command_arguments)
|
|
else:
|
|
command_arguments['printer'] = printer
|
|
globals()[target_command](**command_arguments)
|
|
|
|
def demo(printer, **kwargs):
|
|
"""
|
|
Prints specificed demos. Called when CLI is passed `demo`. This function
|
|
uses the DEMO_FUNCTIONS dictionary.
|
|
|
|
:param printer: A printer from escpos.printer
|
|
:param kwargs: A dict with a key for each function you want to test. It's
|
|
in this format since it usually comes from argparse.
|
|
"""
|
|
for demo_choice in kwargs.keys():
|
|
command = getattr(
|
|
printer,
|
|
demo_choice
|
|
.replace('barcodes_a', 'barcode')
|
|
.replace('barcodes_b', 'barcode')
|
|
)
|
|
for params in DEMO_FUNCTIONS[demo_choice]:
|
|
command(**params)
|
|
printer.cut()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|