17 Commits

Author SHA1 Message Date
n07070
1e46de4b3c Update line lenght of docstring 2026-05-22 10:58:41 +02:00
n07070
e9bf334abd Update raspberry pi class to print via the print queue 2026-05-22 10:58:26 +02:00
n07070
1ba3d1bee3 Add docstring & comments, remove dead code 2026-05-22 10:43:38 +02:00
n07070
20f1145b60 Add comments about the code structure 2026-05-22 10:43:09 +02:00
n07070
d2f670bb68 Apply linting 2026-05-21 02:57:27 +02:00
n07070
f52d7493c8 Restructure main class to activate worker and use tasks, print queue,
update Printer
2026-05-21 02:34:12 +02:00
n07070
b48e7072bf Restructure web class to use print queue and tasks 2026-05-21 02:33:51 +02:00
n07070
cbd5d59445 Add worker class 2026-05-21 02:33:40 +02:00
n07070
e7a7c84664 Add printing queue objects 2026-05-21 02:33:25 +02:00
n07070
60f4eff26c Add task objects 2026-05-21 02:33:12 +02:00
n07070
e78f811904 Update numpy 2026-05-20 16:34:57 +02:00
n07070
0f848ba790 Add an alert if the webcam print fails 2026-05-20 16:34:45 +02:00
n07070
24260834ff Update printing routes for the form 2026-05-20 16:34:33 +02:00
n07070
306cab6606 Fix error flashing and transmission 2026-05-20 16:34:01 +02:00
n07070
26c2de12b6 Add new web route, restructure API route 2026-05-20 13:29:23 +02:00
n07070
f2d3d99e8f Add new functions for discovery and parsing of printers, WIP 2026-05-19 10:52:36 +02:00
n07070
f9831f15c7 Add new dependencies for brother ql printers 2026-05-19 10:52:23 +02:00
3 changed files with 107 additions and 16 deletions

View File

@@ -7,7 +7,7 @@ authors = [
] ]
license = "AGPLv3" license = "AGPLv3"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.14"
dependencies = [ dependencies = [
"flask (>=3.1.3,<4.0.0)", "flask (>=3.1.3,<4.0.0)",
"numpy (>=2.3.4)", "numpy (>=2.3.4)",

View File

@@ -1,6 +1,4 @@
"""
This class manages connexion to a Printer
"""
# import brother_ql # import brother_ql
from time import sleep from time import sleep
import os.path import os.path
@@ -12,7 +10,7 @@ import numpy as np
import escpos.printer import escpos.printer
class Printer(): class Printer(object):
""" """
# The connection is based on the ESC/POS library # The connection is based on the ESC/POS library
@@ -33,7 +31,7 @@ class Printer():
ready = False ready = False
def __init__(self, app, device_id, vendor_id): def __init__(self, app, device_id, vendor_id):
super().__init__() super(Printer, self).__init__()
self.app = app self.app = app
self.ready = False self.ready = False
self.printer = None self.printer = None
@@ -71,7 +69,7 @@ class Printer():
# TODO: This could happen directly when creating a new Printer class # TODO: This could happen directly when creating a new Printer class
if os.getenv("FLASK_DEBUG"): if os.getenv("FLASK_DEBUG"):
waiting_elapsed = 3 waiting_elapsed = 15
else: else:
waiting_elapsed = 10 waiting_elapsed = 10
@@ -84,23 +82,25 @@ class Printer():
p = escpos.printer.Usb( p = escpos.printer.Usb(
self.device_id, self.vendor_id, 0, profile="TM-P80" self.device_id, self.vendor_id, 0, profile="TM-P80"
) )
except RuntimeError as e: except Exception as e:
self.app.logger.error( self.app.logger.error(
"The USB device is not plugged in, trying again %s : %s", "The USB device is not plugged in, trying again %s : %s",
waiting_elapsed, waiting_elapsed,
str(e), str(e),
) )
pass
try: try:
if p.is_online(): if p.is_online():
self.ready = True self.ready = True
self.app.logger.debug("Printer online !") self.app.logger.debug("Printer online !")
except RuntimeError as e: except Exception as e:
self.app.logger.error( self.app.logger.error(
"Error while getting the printer online %s : %s", "Error while getting the printer online %s : %s",
waiting_elapsed, waiting_elapsed,
str(e), str(e),
) )
pass
sleep(1) sleep(1)
waiting_elapsed -= 1 waiting_elapsed -= 1
@@ -154,7 +154,7 @@ class Printer():
self.app.logger.warning( self.app.logger.warning(
"Could not print message of this length: " + str(len(clean_msg)) "Could not print message of this length: " + str(len(clean_msg))
) )
raise RuntimeError( raise Exception(
"Could not print message of this length :" "Could not print message of this length :"
+ str(len(clean_msg)) + str(len(clean_msg))
+ ", needs to be below 4096 caracters long." + ", needs to be below 4096 caracters long."
@@ -164,7 +164,7 @@ class Printer():
self.app.logger.warning( self.app.logger.warning(
"Could not print signature of this length: " + str(len(clean_signature)) "Could not print signature of this length: " + str(len(clean_signature))
) )
raise RuntimeError( raise Exception(
"Could not print signature of this length :" "Could not print signature of this length :"
+ str(len(clean_signature)) + str(len(clean_signature))
+ ", needs to be below 256 caracters long." + ", needs to be below 256 caracters long."
@@ -208,8 +208,8 @@ class Printer():
+ str(path) + str(path)
+ " wasn't found. Please try again." + " wasn't found. Please try again."
) )
else:
self.app.logger.debug("Printing file from " + str(path)) self.app.logger.debug("Printing file from " + str(path))
if process: if process:
try: try:
@@ -256,7 +256,7 @@ class Printer():
self.printer.open(self.usb_args) self.printer.open(self.usb_args)
self.printer.qr(content, center=True) self.printer.qr(content, center=True)
self.printer.close() self.printer.close()
except RuntimeError as e: except Exception as e:
self.printer.close() self.printer.close()
self.app.logger.error(str(e)) self.app.logger.error(str(e))
return False return False
@@ -310,8 +310,7 @@ def _process_image(self, path):
self.app.logger.debug("Resized the image") self.app.logger.debug("Resized the image")
# # Convert to grayscale for dithering # # Convert to grayscale for dithering
# dithered_img = original_img.convert("L").convert("1") # dithered_img = original_img.convert("L").convert("1") # Dithering using default method (FloydSteinberg)
# Dithering using default method (FloydSteinberg)
# self.app.logger.debug("Dithered the image") # self.app.logger.debug("Dithered the image")
# Compute brightness of original image (grayscale average) # Compute brightness of original image (grayscale average)
@@ -345,3 +344,94 @@ def _process_image(self, path):
self.app.logger.debug("Processed and saved image.") self.app.logger.debug("Processed and saved image.")
return jpeg_path return jpeg_path
def discover_printers():
"""
We try to find all the connected printers ( 0 or n ) to this system.
For every type of supported printer, we try to autodiscover them.
http://www.linux-usb.org/usb.ids A list of USB vendor IDs
04b8 Seiko Epson Corp.
04f9 Brother Industries, Ltd
"""
def find_and_parse_borther_ql_printer():
## We might be able to no use this because there is a `discover` command in https://github.com/pklaus/brother_ql#usage
## Code stolen from https://framagit.org/stickoeur/diagnostickoeur/-/blob/no-masters/printit.py?ref_type=heads
"""Find and parse Brother QL printer information."""
model_manager = ModelsManager()
# Debug print to show we're searching
# print("Searching for Brother QL printer...")
for backend_name in ["pyusb", "linux_kernel"]:
try:
# print(f"Trying backend: {backend_name}")
backend = backend_factory(backend_name)
available_devices = backend["list_available_devices"]()
# print(f"Found {len(available_devices)} devices with {backend_name} backend")
for printer in available_devices:
# print(f"Found device: {printer}")
identifier = printer["identifier"]
parts = identifier.split("/")
if len(parts) < 4:
# print(f"Skipping device with invalid identifier format: {identifier}")
continue
protocol = parts[0]
device_info = parts[2]
serial_number = parts[3]
try:
vendor_id, product_id = device_info.split(":")
except ValueError:
# print(f"Invalid device info format: {device_info}")
continue
# Default model
model = "QL-570"
# Try to match product ID to determine actual model
try:
product_id_int = int(product_id, 16)
for m in model_manager.iter_elements():
if m.product_id == product_id_int:
model = m.identifier
break
# print(f"Matched printer model: {model}")
except ValueError:
# print(f"Invalid product ID format: {product_id}")
continue
printer_info = {
"identifier": identifier,
"backend": backend_name,
"model": model,
"protocol": protocol,
"vendor_id": vendor_id,
"product_id": product_id,
"serial_number": serial_number,
}
# print(f"Found printer: {printer_info}")
return printer_info
except Exception as e:
# print(f"Error with backend {backend_name}: {str(e)}")
continue
print("No Brother QL printer found")
return None
def fint_and_parse_epson_printer():
pass

View File

@@ -18,6 +18,7 @@ from abc import ABC, abstractmethod
## See https://docs.python.org/3/library/abc.html to learn more about this ## See https://docs.python.org/3/library/abc.html to learn more about this
# from dataclasses import dataclass
from enum import Enum from enum import Enum
import uuid import uuid