added support for printing simple labels
This commit is contained in:
parent
a3a1c567e1
commit
ff55c26d7b
6 changed files with 151 additions and 14 deletions
|
|
@ -1,6 +1,14 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from telegram.ext import CommandHandler, ApplicationBuilder
|
from telegram import Update
|
||||||
|
from telegram.ext import (
|
||||||
|
CommandHandler,
|
||||||
|
ApplicationBuilder,
|
||||||
|
ConversationHandler,
|
||||||
|
CallbackContext,
|
||||||
|
filters,
|
||||||
|
MessageHandler,
|
||||||
|
)
|
||||||
|
|
||||||
from labeler.app.labeler import Application
|
from labeler.app.labeler import Application
|
||||||
from labeler.infra.e550w_printer.printer import E550W
|
from labeler.infra.e550w_printer.printer import E550W
|
||||||
|
|
@ -8,19 +16,67 @@ from labeler.infra.renderer import PILRenderer
|
||||||
|
|
||||||
|
|
||||||
class LabelingBot:
|
class LabelingBot:
|
||||||
def __init__(self, app):
|
def __init__(self, app: Application):
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
async def media_info(self, update, context):
|
async def media_info(self, update, context):
|
||||||
media = self.app.get_installed_media()
|
media = self.app.get_installed_media()
|
||||||
await update.message.reply_text(f"Installed media: {media.description}")
|
await update.message.reply_text(f"Installed media: {media.description}")
|
||||||
|
|
||||||
|
async def label_length(self, update, context):
|
||||||
|
await update.message.reply_text(
|
||||||
|
"Hello! Please tell me the length of the label, enter 0 for auto:"
|
||||||
|
)
|
||||||
|
return LABEL_LENGTH
|
||||||
|
|
||||||
|
async def label_text(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
user_input = update.message.text
|
||||||
|
context.user_data["length"] = int(user_input)
|
||||||
|
await update.message.reply_text("Now, please tell me the text of the label:")
|
||||||
|
return LABEL_TEXT
|
||||||
|
|
||||||
|
async def simple_label(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
user_input = update.message.text
|
||||||
|
context.user_data["label"] = user_input
|
||||||
|
try:
|
||||||
|
label = self.app.print_label(
|
||||||
|
text=context.user_data["label"], length=context.user_data["length"]
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
await update.message.reply_text(f"There was an exception: {e}")
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
await update.message.reply_photo(
|
||||||
|
label.bytes, f'Your label is: {context.user_data["label"]}'
|
||||||
|
)
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
async def cancel(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
await update.message.reply_text("Cancelled.")
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
application = Application(PILRenderer(), E550W(os.environ.get("PRINTER_IP")))
|
application = Application(PILRenderer(), E550W(os.environ.get("PRINTER_IP")))
|
||||||
bot = LabelingBot(application)
|
bot = LabelingBot(application)
|
||||||
|
|
||||||
|
LABEL_LENGTH, LABEL_TEXT = range(2)
|
||||||
|
|
||||||
|
conv_handler = ConversationHandler(
|
||||||
|
entry_points=[CommandHandler("simple_label", bot.label_length)],
|
||||||
|
states={
|
||||||
|
LABEL_LENGTH: [
|
||||||
|
MessageHandler(filters.Text() & ~filters.Command(), bot.label_text)
|
||||||
|
],
|
||||||
|
LABEL_TEXT: [
|
||||||
|
MessageHandler(filters.Text() & ~filters.Command(), bot.simple_label)
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fallbacks=[CommandHandler("cancel", bot.cancel)],
|
||||||
|
)
|
||||||
|
|
||||||
app = ApplicationBuilder().token(os.environ["TELEGRAM_TOKEN"]).build()
|
app = ApplicationBuilder().token(os.environ["TELEGRAM_TOKEN"]).build()
|
||||||
app.add_handler(CommandHandler("media_info", bot.media_info))
|
app.add_handler(CommandHandler("media_info", bot.media_info))
|
||||||
|
app.add_handler(conv_handler)
|
||||||
|
|
||||||
app.run_polling()
|
app.run_polling()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,11 @@
|
||||||
from labeler.domain.objects import Label, LabelRequest, LabelDefinition, MediaDefinition
|
from labeler.domain.objects import (
|
||||||
|
Label,
|
||||||
|
LabelRequest,
|
||||||
|
LabelDefinition,
|
||||||
|
MediaDefinition,
|
||||||
|
Dimension,
|
||||||
|
Image,
|
||||||
|
)
|
||||||
from labeler.interfaces import Renderer, Printer
|
from labeler.interfaces import Renderer, Printer
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -7,16 +14,16 @@ class Application:
|
||||||
self.renderer = renderer
|
self.renderer = renderer
|
||||||
self.printer = printer
|
self.printer = printer
|
||||||
|
|
||||||
def render_preview(self, label_request: LabelRequest):
|
def render_preview(self, text: str, length: int = None) -> Label:
|
||||||
media = self.printer.get_installed_media()
|
media = self.printer.get_installed_media()
|
||||||
|
|
||||||
if label_request.length is not None:
|
if length != 0:
|
||||||
label_length = label_request.length - 2 * media.minimal_margin_horizontal
|
label_length = Dimension(mm=length) - 2 * media.minimal_margin_horizontal
|
||||||
else:
|
else:
|
||||||
label_length = media.printable_length
|
label_length = None
|
||||||
|
|
||||||
label_definition = LabelDefinition(
|
label_definition = LabelDefinition(
|
||||||
text=label_request.text,
|
text=text,
|
||||||
length=label_length,
|
length=label_length,
|
||||||
width=media.printable_width,
|
width=media.printable_width,
|
||||||
dpi=media.dpi,
|
dpi=media.dpi,
|
||||||
|
|
@ -24,5 +31,24 @@ class Application:
|
||||||
|
|
||||||
self.renderer.render_label(label_definition)
|
self.renderer.render_label(label_definition)
|
||||||
|
|
||||||
|
def print_label(self, text: str, length: int = None) -> Image:
|
||||||
|
media = self.printer.get_installed_media()
|
||||||
|
|
||||||
|
if length != 0:
|
||||||
|
label_length = Dimension(mm=length) - 2 * media.minimal_margin_horizontal
|
||||||
|
else:
|
||||||
|
label_length = None
|
||||||
|
|
||||||
|
label_definition = LabelDefinition(
|
||||||
|
text=text,
|
||||||
|
length=label_length,
|
||||||
|
width=media.printable_width,
|
||||||
|
dpi=media.dpi,
|
||||||
|
)
|
||||||
|
|
||||||
|
label = self.renderer.render_label(label_definition)
|
||||||
|
self.printer.print_label(label)
|
||||||
|
return label
|
||||||
|
|
||||||
def get_installed_media(self) -> MediaDefinition:
|
def get_installed_media(self) -> MediaDefinition:
|
||||||
return self.printer.get_installed_media()
|
return self.printer.get_installed_media()
|
||||||
|
|
|
||||||
|
|
@ -107,16 +107,17 @@ class MediaDefinition(BaseModel):
|
||||||
length: Dimension
|
length: Dimension
|
||||||
minimal_margin_vertical: Dimension
|
minimal_margin_vertical: Dimension
|
||||||
minimal_margin_horizontal: Dimension
|
minimal_margin_horizontal: Dimension
|
||||||
|
minimum_length: Dimension = Field(default_factory=lambda: Dimension(mm=5))
|
||||||
dpi: int
|
dpi: int
|
||||||
description: str
|
description: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def printable_width(self) -> Dimension:
|
def printable_width(self) -> Dimension:
|
||||||
return self.width - 2 * self.minimal_margin_horizontal
|
return self.width - 2 * self.minimal_margin_vertical
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def printable_length(self) -> Dimension:
|
def printable_length(self) -> Dimension:
|
||||||
return self.length - 2 * self.minimal_margin_vertical
|
return self.length - 2 * self.minimal_margin_horizontal
|
||||||
|
|
||||||
|
|
||||||
class Label(BaseModel):
|
class Label(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
from math import inf
|
from math import inf
|
||||||
|
|
||||||
|
from brother_ql import BrotherQLRaster, create_label
|
||||||
|
from brother_ql.backends import guess_backend, backend_factory
|
||||||
from pysnmp.entity.engine import SnmpEngine
|
from pysnmp.entity.engine import SnmpEngine
|
||||||
from pysnmp.hlapi import getCmd, CommunityData, UdpTransportTarget, ContextData
|
from pysnmp.hlapi import getCmd, CommunityData, UdpTransportTarget, ContextData
|
||||||
from pysnmp.smi.rfc1902 import ObjectType, ObjectIdentity
|
from pysnmp.smi.rfc1902 import ObjectType, ObjectIdentity
|
||||||
|
|
||||||
from labeler.domain.objects import MediaDefinition, Dimension
|
from labeler.domain.objects import MediaDefinition, Dimension, Image
|
||||||
from labeler.infra.e550w_printer.media_definitions import (
|
from labeler.infra.e550w_printer.media_definitions import (
|
||||||
media_width,
|
media_width,
|
||||||
tape_color,
|
tape_color,
|
||||||
|
|
@ -16,6 +20,12 @@ from labeler.infra.e550w_printer.media_definitions import (
|
||||||
TYPE_BYTE,
|
TYPE_BYTE,
|
||||||
)
|
)
|
||||||
from labeler.interfaces import Printer
|
from labeler.interfaces import Printer
|
||||||
|
from PIL import Image as PILImage
|
||||||
|
|
||||||
|
PRINTABLE_WIDTH = {
|
||||||
|
12: Dimension.from_points(150, 360),
|
||||||
|
24: Dimension.from_points(320, 360),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class E550W(Printer):
|
class E550W(Printer):
|
||||||
|
|
@ -26,6 +36,45 @@ class E550W(Printer):
|
||||||
def get_installed_media(self) -> MediaDefinition:
|
def get_installed_media(self) -> MediaDefinition:
|
||||||
return self.__get_printer_status()
|
return self.__get_printer_status()
|
||||||
|
|
||||||
|
def print_label(self, label: Image):
|
||||||
|
im = PILImage.open(io.BytesIO(label.bytes))
|
||||||
|
|
||||||
|
qlr = BrotherQLRaster("PT-E550W")
|
||||||
|
create_label(
|
||||||
|
qlr,
|
||||||
|
im,
|
||||||
|
self.__media_width_to_type(label.height),
|
||||||
|
red=False,
|
||||||
|
threshold=70,
|
||||||
|
cut=True,
|
||||||
|
rotate=270,
|
||||||
|
compress=True,
|
||||||
|
dpi_600=True,
|
||||||
|
hq=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
selected_backend = guess_backend(f"tcp://{self.ip_address}:9100")
|
||||||
|
except ValueError:
|
||||||
|
logging.error(
|
||||||
|
"Couln't guess the backend to use from the printer string descriptor"
|
||||||
|
)
|
||||||
|
BACKEND_CLASS = backend_factory(selected_backend)["backend_class"]
|
||||||
|
be = BACKEND_CLASS(f"tcp://{self.ip_address}:9100")
|
||||||
|
be.write(qlr.data)
|
||||||
|
be.dispose()
|
||||||
|
del be
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("Exception happened: %s", e)
|
||||||
|
|
||||||
|
def __media_width_to_type(self, height: int):
|
||||||
|
metric_width = Dimension.from_points(height, 360)
|
||||||
|
if metric_width == Dimension.from_points(150, 360):
|
||||||
|
return "pt512"
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unsupported media width: {metric_width}")
|
||||||
|
|
||||||
def __get_printer_status(self):
|
def __get_printer_status(self):
|
||||||
raw_snmp_data = self.__get_snmp_status().asNumbers()
|
raw_snmp_data = self.__get_snmp_status().asNumbers()
|
||||||
width = media_width(raw_snmp_data[WIDTH_BYTE])
|
width = media_width(raw_snmp_data[WIDTH_BYTE])
|
||||||
|
|
@ -36,9 +85,9 @@ class E550W(Printer):
|
||||||
return MediaDefinition(
|
return MediaDefinition(
|
||||||
width=Dimension(mm=width),
|
width=Dimension(mm=width),
|
||||||
length=Dimension(mm=inf),
|
length=Dimension(mm=inf),
|
||||||
minimal_margin_vertical=Dimension(mm=1),
|
minimal_margin_vertical=(Dimension(mm=width) - PRINTABLE_WIDTH[width]) / 2,
|
||||||
minimal_margin_horizontal=Dimension(mm=2),
|
minimal_margin_horizontal=Dimension(mm=1),
|
||||||
dpi=600,
|
dpi=360,
|
||||||
description=f"{tape_type} - {width}mm, {media_text_color} on {media_tape_color} background",
|
description=f"{tape_type} - {width}mm, {media_text_color} on {media_tape_color} background",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,7 @@ class Printer(abc.ABC):
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_installed_media(self) -> MediaDefinition:
|
def get_installed_media(self) -> MediaDefinition:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def print_label(self, label: Image):
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ as provide info about printer status and other useful information.
|
||||||
|
|
||||||
## Supported commands
|
## Supported commands
|
||||||
- `/media_info` - show info about currently installed media
|
- `/media_info` - show info about currently installed media
|
||||||
|
- `/simple_label` - print a simple label
|
||||||
|
|
||||||
### usage example
|
### usage example
|
||||||
You need to things:
|
You need to things:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue