Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect printer model and media type #53

Merged
merged 11 commits into from
Aug 4, 2024
64 changes: 55 additions & 9 deletions brother_ql/backends/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@

logger = logging.getLogger(__name__)

def discover(backend_identifier='linux_kernel'):

def discover(backend_identifier="pyusb"):
if backend_identifier is None:
logger.info("Backend for discovery not specified, defaulting to pyusb")
backend_identifier = "pyusb"
be = backend_factory(backend_identifier)
list_available_devices = be['list_available_devices']
BrotherQLBackend = be['backend_class']
vulpes2 marked this conversation as resolved.
Show resolved Hide resolved

available_devices = list_available_devices()
return available_devices
list_available_devices = be["list_available_devices"]
return list_available_devices()

def send(instructions, printer_identifier=None, backend_identifier=None, blocking=True):
"""
Expand All @@ -47,8 +46,8 @@ def send(instructions, printer_identifier=None, backend_identifier=None, blockin
try:
selected_backend = guess_backend(printer_identifier)
except:
logger.info("No backend stated. Selecting the default linux_kernel backend.")
selected_backend = 'linux_kernel'
logger.info("No backend stated. Selecting the default pyusb backend.")
selected_backend = "pyusb"

be = backend_factory(selected_backend)
list_available_devices = be['list_available_devices']
Expand Down Expand Up @@ -101,3 +100,50 @@ def send(instructions, printer_identifier=None, backend_identifier=None, blockin
logger.info("Printing was successful. Waiting for the next job.")

return status


def status(
printer_identifier=None,
backend_identifier=None,
):
"""
Retrieve status info from the printer, including model and currently loaded media size.

:param str printer_identifier: Identifier for the printer.
:param str backend_identifier: Can enforce the use of a specific backend.
"""

selected_backend = None
if backend_identifier:
selected_backend = backend_identifier
else:
try:
selected_backend = guess_backend(printer_identifier)
except ValueError:
logger.info("No backend stated. Selecting the default pyusb backend.")
selected_backend = "pyusb"
if selected_backend == "network":
# Not implemented due to lack of an available test device
raise NotImplementedError

be = backend_factory(selected_backend)
BrotherQLBackend = be["backend_class"]
printer = BrotherQLBackend(printer_identifier)

logger.info("Sending status information request to the printer.")
printer.write(b"\x1b\x69\x53") # "ESC i S" Status information request
data = printer.read()
try:
result = interpret_response(data)
except ValueError:
logger.error("Failed to parse response data: %s", data)

logger.info(f"Printer Series Code: 0x{result["series_code"]:02x}")
logger.info(f"Printer Model Code: 0x{result["model_code"]:02x}")
logger.info(f"Printer Status Type: {result["status_type"]} ")
logger.info(f"Printer Phase Type: {result["phase_type"]})")
logger.info(f"Printer Errors: {result["errors"]}")
logger.info(f"Media Type: {result["media_type"]}")
logger.info(f"Media Size: {result["media_width"]} x {result["media_length"]} mm")
vulpes2 marked this conversation as resolved.
Show resolved Hide resolved

return result
8 changes: 5 additions & 3 deletions brother_ql/backends/pyusb.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def list_available_devices():

returns: devices: a list of dictionaries with the keys 'identifier' and 'instance': \
[ {'identifier': 'usb://0x04f9:0x2015/C5Z315686', 'instance': pyusb.core.Device()}, ]
The 'identifier' is of the format idVendor:idProduct_iSerialNumber.
The 'identifier' is of the format idVendor:idProduct/iSerialNumber.
"""

class find_class(object):
Expand All @@ -44,8 +44,10 @@ def __call__(self, device):

def identifier(dev):
try:
serial = usb.util.get_string(dev, 256, dev.iSerialNumber)
return 'usb://0x{:04x}:0x{:04x}_{}'.format(dev.idVendor, dev.idProduct, serial)
serial = usb.util.get_string(dev, dev.iSerialNumber)
return "usb://0x{:04x}:0x{:04x}/{}".format(
dev.idVendor, dev.idProduct, serial
)
except:
return 'usb://0x{:04x}:0x{:04x}'.format(dev.idVendor, dev.idProduct)

Expand Down
6 changes: 2 additions & 4 deletions brother_ql/brother_ql_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@
logger = logging.getLogger(__name__)

class BrotherQL_USBdebug(object):

def __init__(self, dev, instructions_data, backend='linux_kernel'):

be_cls = backend_factory(backend)['backend_class']
def __init__(self, dev, instructions_data, backend="pyusb"):
be_cls = backend_factory(backend)["backend_class"]
self.be = be_cls(dev)

self.sleep_time = 0.0
Expand Down
6 changes: 4 additions & 2 deletions brother_ql/brother_ql_print.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ def main():
try:
selected_backend = guess_backend(args.printer)
except:
logger.info("No backend stated. Selecting the default linux_kernel backend.")
selected_backend = 'linux_kernel'
logger.info(
"No backend stated. Selecting the default pyusb backend."
)
vulpes2 marked this conversation as resolved.
Show resolved Hide resolved
selected_backend = "pyusb"

# List any printers found, if explicitly asked to do so or if no identifier has been provided.
if args.list_printers or not args.printer:
Expand Down
68 changes: 59 additions & 9 deletions brother_ql/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

# Python standard library
import logging
import os
from urllib.parse import urlparse

# external dependencies
import click

# imports from this very package
from brother_ql.devicedependent import models, label_sizes, label_type_specs, DIE_CUT_LABEL, ENDLESS_LABEL, ROUND_DIE_CUT_LABEL
from brother_ql.models import ModelsManager
from brother_ql.backends import available_backends, backend_factory


Expand Down Expand Up @@ -41,16 +44,50 @@ def cli(ctx, *args, **kwargs):
@cli.command()
@click.pass_context
def discover(ctx):
""" find connected label printers """
backend = ctx.meta.get('BACKEND', 'pyusb')
discover_and_list_available_devices(backend)
"""find connected label printers"""
backend = ctx.meta.get("BACKEND", "pyusb")
if backend is None:
logger.info("Defaulting to pyusb as backend for discovery")
backend = "pyusb"
from brother_ql.backends.helpers import discover, status

def discover_and_list_available_devices(backend):
from brother_ql.backends.helpers import discover
available_devices = discover(backend_identifier=backend)
from brother_ql.output_helpers import log_discovered_devices, textual_description_discovered_devices
log_discovered_devices(available_devices)
print(textual_description_discovered_devices(available_devices))
vulpes2 marked this conversation as resolved.
Show resolved Hide resolved
for device in available_devices:
device_status = None
result = {"model": "unknown"}

# skip network discovery since it's not supported
if backend == "pyusb" or backend == "linux_kernel":
logger.info(f"Probing device at {device["identifier"]}")
vulpes2 marked this conversation as resolved.
Show resolved Hide resolved

# check permissions before accessing lp* devices
if backend == "linux_kernel":
url = urlparse(device["identifier"])
if not os.access(url.path, os.W_OK):
logger.info(
f"Cannot access device {device["identifier"]} due to insufficient permissions"
)
continue

# send status request
device_status = status(
printer_identifier=device["identifier"],
backend_identifier=backend,
)

# look up series code and model code
for m in ModelsManager().iter_elements():
if (
device_status["series_code"] == m.series_code
and device_status["model_code"] == m.model_code
):
result = {"model": m.identifier}
break

result.update(device)
logger.info(
"Found a label printer at: {identifier} (model: {model})".format(**result),
)

@cli.group()
@click.pass_context
Expand Down Expand Up @@ -120,7 +157,7 @@ def env(ctx, *args, **kwargs):

@cli.command('print', short_help='Print a label')
@click.argument('images', nargs=-1, type=click.File('rb'), metavar='IMAGE [IMAGE] ...')
@click.option('-l', '--label', type=click.Choice(label_sizes), envvar='BROTHER_QL_LABEL', help='The label (size, type - die-cut or endless). Run `brother_ql info labels` for a full list including ideal pixel dimensions.')
@click.option('-l', '--label', required=True, type=click.Choice(label_sizes), envvar='BROTHER_QL_LABEL', help='The label (size, type - die-cut or endless). Run `brother_ql info labels` for a full list including ideal pixel dimensions.')
@click.option('-r', '--rotate', type=click.Choice(('auto', '0', '90', '180', '270')), default='auto', help='Rotate the image (counterclock-wise) by this amount of degrees.')
@click.option('-t', '--threshold', type=float, default=70.0, help='The threshold value (in percent) to discriminate between black and white pixels.')
@click.option('-d', '--dither', is_flag=True, help='Enable dithering when converting the image to b/w. If set, --threshold is meaningless.')
Expand Down Expand Up @@ -160,7 +197,20 @@ def analyze_cmd(ctx, *args, **kwargs):
@click.pass_context
def send_cmd(ctx, *args, **kwargs):
from brother_ql.backends.helpers import send

send(instructions=kwargs['instructions'].read(), printer_identifier=ctx.meta.get('PRINTER'), backend_identifier=ctx.meta.get('BACKEND'), blocking=True)


@cli.command(name="status", short_help="query printer status and the loaded media size")
@click.pass_context
def status_cmd(ctx, *args, **kwargs):
from brother_ql.backends.helpers import status

status(
printer_identifier=ctx.meta.get("PRINTER"),
backend_identifier=ctx.meta.get("BACKEND"),
)


if __name__ == '__main__':
cli()
Loading