python-escpos/src/escpos/cli.py

594 lines
17 KiB
Python
Raw Normal View History

2016-03-13 00:50:57 +00:00
#!/usr/bin/env python
2016-07-23 08:00:40 +00:00
# PYTHON_ARGCOMPLETE_OK
2016-03-28 18:19:09 +00:00
""" 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 convenience.
2016-03-28 18:19:09 +00:00
It requires you to have a configuration file. See documentation for details.
"""
2016-03-13 00:50:57 +00:00
import argparse
2021-10-30 16:15:22 +00:00
2016-07-23 08:00:40 +00:00
try:
import argcomplete
except ImportError:
# this CLI works nevertheless without argcomplete
pass # noqa
2016-03-16 23:21:05 +00:00
import sys
import six
2016-03-16 20:47:51 +00:00
from . import config
from . import version
2016-03-13 00:50:57 +00:00
2016-06-19 10:25:40 +00:00
# Must be defined before it's used in DEMO_FUNCTIONS
def str_to_bool(string):
2021-10-30 16:15:22 +00:00
"""Used as a type in argparse so that we get back a proper
bool instead of always True
"""
2021-10-30 16:15:22 +00:00
return string.lower() in ("y", "yes", "1", "true")
2017-01-30 01:29:08 +00:00
# A list of functions that work better with a newline to be sent after them.
2021-10-30 16:15:22 +00:00
REQUIRES_NEWLINE = ("qr", "barcode", "text", "block_text")
2017-01-30 01:29:08 +00:00
2016-03-16 16:26:57 +00:00
# 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.
2016-03-16 23:24:23 +00:00
DEMO_FUNCTIONS = {
2021-10-30 16:15:22 +00:00
"text": [
{
"txt": "Hello, World!\n",
}
2016-03-15 23:01:07 +00:00
],
2021-10-30 16:15:22 +00:00
"qr": [
{"content": "This tests a QR code"},
{"content": "https://en.wikipedia.org/"},
2016-03-15 23:01:07 +00:00
],
2021-10-30 16:15:22 +00:00
"barcodes_a": [
{"bc": "UPC-A", "code": "13243546576"},
{"bc": "UPC-E", "code": "132435"},
{"bc": "EAN13", "code": "4006381333931"},
2021-10-30 16:15:22 +00:00
{"bc": "EAN8", "code": "1324354"},
{"bc": "CODE39", "code": "TEST"},
{"bc": "ITF", "code": "55867492279103"},
{"bc": "NW7", "code": "A00000000A"},
2016-03-15 23:01:07 +00:00
],
2021-10-30 16:15:22 +00:00
"barcodes_b": [
{"bc": "UPC-A", "code": "13243546576", "function_type": "B"},
{"bc": "UPC-E", "code": "132435", "function_type": "B"},
{"bc": "EAN13", "code": "4006381333931", "function_type": "B"},
2021-10-30 16:15:22 +00:00
{"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"},
2016-03-28 18:16:37 +00:00
],
2016-03-15 23:01:07 +00:00
}
2016-03-28 18:16:37 +00:00
# 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 = [
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "qr",
"help": "Print a QR code",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "qr",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--content",),
"help": "Text to print as a qr code",
"required": True,
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--size",),
"help": "QR code size (1-16) [default:3]",
"required": False,
"type": int,
},
2016-03-28 18:16:37 +00:00
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "barcode",
"help": "Print a barcode",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "barcode",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--code",),
"help": "Barcode data to print",
"required": True,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--bc",),
"help": "Barcode format",
"required": True,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--height",),
"help": "Barcode height in px",
"type": int,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--width",),
"help": "Barcode width",
"type": int,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--pos",),
"help": "Label position",
"choices": ["BELOW", "ABOVE", "BOTH", "OFF"],
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--font",),
"help": "Label font",
"choices": ["A", "B"],
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--align_ct",),
"help": "Align barcode center",
"type": str_to_bool,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--function_type",),
"help": "ESCPOS function type",
"choices": ["A", "B"],
2016-03-28 18:16:37 +00:00
},
{
"option_strings": ("--force_software",),
"help": "Force render and print barcode as an image",
"choices": ["graphics", "bitImageColumn", "bitImageRaster"],
},
2016-03-28 18:16:37 +00:00
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "text",
"help": "Print plain text",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "text",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--txt",),
"help": "Plain text to print",
"required": True,
2016-03-28 18:16:37 +00:00
}
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "block_text",
"help": "Print wrapped text",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "block_text",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--txt",),
"help": "block_text to print",
"required": True,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--columns",),
"help": "Number of columns",
"type": int,
2016-03-28 18:16:37 +00:00
},
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "cut",
"help": "Cut the paper",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "cut",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--mode",),
"help": "Type of cut",
"choices": ["FULL", "PART"],
2016-03-28 18:16:37 +00:00
},
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "cashdraw",
"help": "Kick the cash drawer",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "cashdraw",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--pin",),
"help": "Which PIN to kick",
"choices": [2, 5],
2016-03-28 18:16:37 +00:00
},
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "image",
"help": "Print an image",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "image",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--img_source",),
"help": "Path to image",
"required": True,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--impl",),
"help": "Implementation to use",
"choices": ["bitImageRaster", "bitImageColumn", "graphics"],
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--high_density_horizontal",),
"help": "Image density (horizontal)",
"type": str_to_bool,
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--high_density_vertical",),
"help": "Image density (vertical)",
"type": str_to_bool,
},
2016-03-28 18:16:37 +00:00
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "fullimage",
"help": "Print a fullimage",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "fullimage",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--img",),
"help": "Path to img",
"required": True,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--max_height",),
"help": "Max height of image in px",
"type": int,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--width",),
"help": "Max width of image in px",
"type": int,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--histeq",),
"help": "Equalize the histrogram",
"type": str_to_bool,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--bandsize",),
"help": "Size of bands to divide into when printing",
"type": int,
2016-03-28 18:16:37 +00:00
},
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "charcode",
"help": "Set character code table",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "charcode",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--code",),
"help": "Character code",
"required": True,
2016-03-28 18:16:37 +00:00
},
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "set",
"help": "Set text properties",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "set",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--align",),
"help": "Horizontal alignment",
"choices": ["left", "center", "right"],
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--font",),
"help": "Font choice",
"choices": ["left", "center", "right"],
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--text_type",),
"help": "Text properties",
"choices": ["B", "U", "U2", "BU", "BU2", "NORMAL"],
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--width",),
"help": "Width multiplier",
"type": int,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--height",),
"help": "Height multiplier",
"type": int,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--density",),
"help": "Print density",
"type": int,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--invert",),
"help": "White on black printing",
"type": str_to_bool,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--smooth",),
"help": "Text smoothing. Effective on >: 4x4 text",
"type": str_to_bool,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--flip",),
"help": "Text smoothing. Effective on >: 4x4 text",
"type": str_to_bool,
2016-03-28 18:16:37 +00:00
},
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "hw",
"help": "Hardware operations",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "hw",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--hw",),
"help": "Operation",
"choices": ["INIT", "SELECT", "RESET"],
"required": True,
2016-03-28 18:16:37 +00:00
},
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "control",
"help": "Control sequences",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "control",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--ctl",),
"help": "Control sequence",
"choices": ["LF", "FF", "CR", "HT", "VT"],
"required": True,
2016-03-28 18:16:37 +00:00
},
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--pos",),
"help": "Horizontal tab position (1-4)",
"type": int,
2016-03-28 18:16:37 +00:00
},
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "panel_buttons",
"help": "Controls panel buttons",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "panel_buttons",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--enable",),
"help": "Feed button enabled",
"type": str_to_bool,
"required": True,
2016-03-28 18:16:37 +00:00
},
],
},
{
2021-10-30 16:15:22 +00:00
"parser": {
"name": "raw",
"help": "Raw data",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"defaults": {
"func": "_raw",
2016-03-28 18:16:37 +00:00
},
2021-10-30 16:15:22 +00:00
"arguments": [
2016-03-28 18:16:37 +00:00
{
2021-10-30 16:15:22 +00:00
"option_strings": ("--msg",),
"help": "Raw data to send",
"required": True,
2016-03-28 18:16:37 +00:00
},
],
},
]
2016-06-19 10:25:40 +00:00
2016-03-15 23:01:07 +00:00
def main():
2016-03-16 16:26:57 +00:00
"""
Handles loading of configuration and creating and processing of command
2016-03-16 23:24:23 +00:00
line arguments. Called when run from a CLI.
2016-03-16 16:26:57 +00:00
"""
2016-03-28 18:16:37 +00:00
2016-03-15 23:01:07 +00:00
parser = argparse.ArgumentParser(
2021-10-30 16:15:22 +00:00
description="CLI for python-escpos",
epilog="Printer configuration is defined in the python-escpos config"
"file. See documentation for details.",
2016-03-15 23:01:07 +00:00
)
2021-10-30 16:15:22 +00:00
parser.register("type", "bool", str_to_bool)
2016-03-28 18:49:12 +00:00
# Allow config file location to be passed
parser.add_argument(
2021-10-30 16:15:22 +00:00
"-c",
"--config",
help="Alternate path to the configuration file",
2016-03-28 18:49:12 +00:00
)
# Everything interesting runs off of a subparser so we can use the format
# cli [subparser] -args
2016-03-15 23:01:07 +00:00
command_subparsers = parser.add_subparsers(
2021-10-30 16:15:22 +00:00
title="ESCPOS Command",
dest="parser",
2016-03-15 23:01:07 +00:00
)
# fix inconsistencies in the behaviour of some versions of argparse
2021-10-30 16:15:22 +00:00
command_subparsers.required = False # force 'required' testing
2016-03-15 23:01:07 +00:00
2016-03-28 18:16:37 +00:00
# Build the ESCPOS command arguments
for command in ESCPOS_COMMANDS:
2021-10-30 16:15:22 +00:00
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")
2016-03-28 18:16:37 +00:00
parser_command.add_argument(*option_strings, **argument)
2016-03-20 03:03:16 +00:00
2016-03-28 18:16:37 +00:00
# Build any custom arguments
2021-10-30 16:15:22 +00:00
parser_command_demo = command_subparsers.add_parser(
"demo", help="Demonstrates various functions"
)
parser_command_demo.set_defaults(func="demo")
2016-03-15 23:01:07 +00:00
demo_group = parser_command_demo.add_mutually_exclusive_group()
demo_group.add_argument(
2021-10-30 16:15:22 +00:00
"--barcodes-a",
help="Print demo barcodes for function type A",
action="store_true",
2016-03-15 23:01:07 +00:00
)
demo_group.add_argument(
2021-10-30 16:15:22 +00:00
"--barcodes-b",
help="Print demo barcodes for function type B",
action="store_true",
2016-03-15 23:01:07 +00:00
)
demo_group.add_argument(
2021-10-30 16:15:22 +00:00
"--qr",
help="Print some demo QR codes",
action="store_true",
2016-03-15 23:01:07 +00:00
)
demo_group.add_argument(
2021-10-30 16:15:22 +00:00
"--text",
help="Print some demo text",
action="store_true",
2016-03-15 23:01:07 +00:00
)
2021-10-30 16:15:22 +00:00
parser_command_version = command_subparsers.add_parser(
"version", help="Print the version of python-escpos"
)
parser_command_version.set_defaults(version=True)
2016-07-23 08:00:40 +00:00
# hook in argcomplete
2021-10-30 16:15:22 +00:00
if "argcomplete" in globals():
2016-07-23 08:00:40 +00:00
argcomplete.autocomplete(parser)
2016-03-15 23:01:07 +00:00
# Get only arguments actually passed
args_dict = vars(parser.parse_args())
2016-03-16 23:21:05 +00:00
if not args_dict:
parser.print_help()
sys.exit()
2021-10-30 16:15:22 +00:00
command_arguments = dict(
[k, v] for k, v in six.iteritems(args_dict) if v is not None
)
2016-03-15 23:01:07 +00:00
# If version should be printed, do this, then exit
2021-10-30 16:15:22 +00:00
print_version = command_arguments.pop("version", None)
if print_version:
print(version.version)
sys.exit()
2016-03-28 18:49:12 +00:00
# If there was a config path passed, grab it
2021-10-30 16:15:22 +00:00
config_path = command_arguments.pop("config", None)
2016-03-28 18:49:12 +00:00
# Load the configuration and defined printer
saved_config = config.Config()
saved_config.load(config_path)
printer = saved_config.printer()
2016-03-16 23:21:05 +00:00
if not printer:
2021-10-30 16:15:22 +00:00
raise Exception("No printers loaded from config")
2016-03-16 23:21:05 +00:00
2021-10-30 16:15:22 +00:00
target_command = command_arguments.pop("func")
2016-03-15 23:01:07 +00:00
# remove helper-argument 'parser' from dict
2021-10-30 16:15:22 +00:00
command_arguments.pop("parser", None)
2016-03-15 23:01:07 +00:00
if hasattr(printer, target_command):
# print command with args
getattr(printer, target_command)(**command_arguments)
if target_command in REQUIRES_NEWLINE:
printer.text("\n")
2016-03-15 23:01:07 +00:00
else:
2021-10-30 16:15:22 +00:00
command_arguments["printer"] = printer
2016-03-15 23:01:07 +00:00
globals()[target_command](**command_arguments)
2016-06-19 10:25:40 +00:00
2016-03-15 23:01:07 +00:00
def demo(printer, **kwargs):
2016-03-16 16:26:57 +00:00
"""
Prints demos. Called when CLI is passed `demo`. This function
2016-03-16 23:24:23 +00:00
uses the DEMO_FUNCTIONS dictionary.
2016-03-16 16:26:57 +00:00
: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.
"""
2016-03-15 23:01:07 +00:00
for demo_choice in kwargs.keys():
command = getattr(
printer,
2021-10-30 16:15:22 +00:00
demo_choice.replace("barcodes_a", "barcode").replace(
"barcodes_b", "barcode"
),
2016-03-15 23:01:07 +00:00
)
2016-03-16 23:24:23 +00:00
for params in DEMO_FUNCTIONS[demo_choice]:
2016-03-15 23:01:07 +00:00
command(**params)
2016-03-20 03:03:34 +00:00
printer.cut()
2016-03-15 23:01:07 +00:00
2017-01-30 01:29:08 +00:00
2021-10-30 16:15:22 +00:00
if __name__ == "__main__":
2016-03-15 23:01:07 +00:00
main()