Add Pablo: the friendly label printer helper #5
7 changed files with 183 additions and 3 deletions
|
|
@ -108,6 +108,7 @@ class MediaDefinition(BaseModel):
|
||||||
minimal_margin_vertical: Dimension
|
minimal_margin_vertical: Dimension
|
||||||
minimal_margin_horizontal: Dimension
|
minimal_margin_horizontal: Dimension
|
||||||
dpi: int
|
dpi: int
|
||||||
|
description: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def printable_width(self) -> Dimension:
|
def printable_width(self) -> Dimension:
|
||||||
|
|
|
||||||
81
labeler/infra/e550w_printer/media_definitions.py
Normal file
81
labeler/infra/e550w_printer/media_definitions.py
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
"""
|
||||||
|
Values from technical reference manual, can be found in /labeler_docs/brother/technical_reference_manual.pdf
|
||||||
|
"""
|
||||||
|
|
||||||
|
WIDTH_BYTE = 10
|
||||||
|
TYPE_BYTE = 11
|
||||||
|
COLOR_BYTE = 24
|
||||||
|
TEXT_COLOR_BYTE = 25
|
||||||
|
|
||||||
|
|
||||||
|
def media_width(code):
|
||||||
|
if code == 0:
|
||||||
|
raise ValueError("NO TAPE")
|
||||||
|
elif code == 4:
|
||||||
|
return 3.5
|
||||||
|
else:
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
def media_type(code):
|
||||||
|
media = {
|
||||||
|
0: "NO TAPE",
|
||||||
|
1: "Laminated tape",
|
||||||
|
0x11: "Heat-Shrink Tube",
|
||||||
|
0x03: "Non-laminated tape",
|
||||||
|
0xFF: "Incompatible tape",
|
||||||
|
}
|
||||||
|
return media.get(code)
|
||||||
|
|
||||||
|
|
||||||
|
def tape_color(code):
|
||||||
|
colors = {
|
||||||
|
0x01: "White",
|
||||||
|
0x02: "Other",
|
||||||
|
0x03: "Clear",
|
||||||
|
0x04: "Red",
|
||||||
|
0x05: "Blue",
|
||||||
|
0x06: "Yellow",
|
||||||
|
0x07: "Green",
|
||||||
|
0x08: "Black",
|
||||||
|
0x09: "Clear(White text)",
|
||||||
|
0x20: "Matte White",
|
||||||
|
0x21: "Matte Clear",
|
||||||
|
0x22: "Matte Silver",
|
||||||
|
0x23: "Satin Gold",
|
||||||
|
0x24: "Satin Silver",
|
||||||
|
0x30: "Blue(D)",
|
||||||
|
0x31: "Red(D)",
|
||||||
|
0x40: "Fluorescent Orange",
|
||||||
|
0x41: "Fluorescent Yellow",
|
||||||
|
0x50: "Berry Pink(S)",
|
||||||
|
0x51: "Light Gray(S)",
|
||||||
|
0x52: "Lime Green(S)",
|
||||||
|
0x60: "Yellow(F)",
|
||||||
|
0x61: "Pink(F)",
|
||||||
|
0x62: "Blue(F)",
|
||||||
|
0x70: "White(Heat-shrink Tube)",
|
||||||
|
0x90: "White(Flex. ID)",
|
||||||
|
0x91: "Yellow(Flex. ID)",
|
||||||
|
0xF0: "Cleaning",
|
||||||
|
0xF1: "Stencil",
|
||||||
|
0xFF: "Incompatible",
|
||||||
|
}
|
||||||
|
|
||||||
|
return colors.get(code)
|
||||||
|
|
||||||
|
|
||||||
|
def text_color(code):
|
||||||
|
colors = {
|
||||||
|
0x01: "White",
|
||||||
|
0x04: "Red",
|
||||||
|
0x05: "Blue",
|
||||||
|
0x08: "Black",
|
||||||
|
0x0A: "Gold",
|
||||||
|
0x62: "Blue(F)",
|
||||||
|
0xF0: "Cleaning",
|
||||||
|
0xF1: "Stencil",
|
||||||
|
0x02: "Other",
|
||||||
|
0xFF: "Incompatible",
|
||||||
|
}
|
||||||
|
return colors.get(code)
|
||||||
74
labeler/infra/e550w_printer/printer.py
Normal file
74
labeler/infra/e550w_printer/printer.py
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
from math import inf
|
||||||
|
|
||||||
|
from pysnmp.entity.engine import SnmpEngine
|
||||||
|
from pysnmp.hlapi import getCmd, CommunityData, UdpTransportTarget, ContextData
|
||||||
|
from pysnmp.smi.rfc1902 import ObjectType, ObjectIdentity
|
||||||
|
|
||||||
|
from labeler.domain.objects import MediaDefinition, Dimension
|
||||||
|
from labeler.infra.e550w_printer.media_definitions import (
|
||||||
|
media_width,
|
||||||
|
tape_color,
|
||||||
|
text_color,
|
||||||
|
media_type,
|
||||||
|
WIDTH_BYTE,
|
||||||
|
COLOR_BYTE,
|
||||||
|
TEXT_COLOR_BYTE,
|
||||||
|
TYPE_BYTE,
|
||||||
|
)
|
||||||
|
from labeler.interfaces import Printer
|
||||||
|
|
||||||
|
|
||||||
|
class E550W(Printer):
|
||||||
|
def __init__(self, ip_address: str):
|
||||||
|
self.ip_address = ip_address
|
||||||
|
self.snmp_port = 161
|
||||||
|
|
||||||
|
def get_installed_media(self) -> MediaDefinition:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __get_printer_status(self):
|
||||||
|
raw_snmp_data = self.__get_snmp_status().asNumbers()
|
||||||
|
width = media_width(raw_snmp_data[WIDTH_BYTE])
|
||||||
|
media_tape_color = tape_color(raw_snmp_data[COLOR_BYTE])
|
||||||
|
media_text_color = text_color(raw_snmp_data[TEXT_COLOR_BYTE])
|
||||||
|
tape_type = media_type(raw_snmp_data[TYPE_BYTE])
|
||||||
|
|
||||||
|
return MediaDefinition(
|
||||||
|
width=Dimension(mm=width),
|
||||||
|
length=Dimension(mm=inf),
|
||||||
|
minimal_margin_vertical=Dimension(mm=1),
|
||||||
|
minimal_margin_horizontal=Dimension(mm=2),
|
||||||
|
dpi=600,
|
||||||
|
description=f"{tape_type} - {width}mm, {media_text_color} on {media_tape_color} background",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __get_snmp_status(self):
|
||||||
|
"""
|
||||||
|
This oid was found by using wireshark, however it's also documented here:
|
||||||
|
https://support.brother.com/g/s/es/dev/en/command/faq/index.html?c=eu_ot&lang=en&navi=offall&comple=on&redirect=on
|
||||||
|
just not for the E550W model.
|
||||||
|
"""
|
||||||
|
oid = "1.3.6.1.4.1.2435.3.3.9.1.6.1.0"
|
||||||
|
|
||||||
|
error_indication, error_status, error_index, var_binds = next(
|
||||||
|
getCmd(
|
||||||
|
SnmpEngine(),
|
||||||
|
CommunityData("public", mpModel=0),
|
||||||
|
UdpTransportTarget((self.ip_address, self.snmp_port)),
|
||||||
|
ContextData(),
|
||||||
|
ObjectType(ObjectIdentity(oid)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if error_indication:
|
||||||
|
raise Exception(error_indication)
|
||||||
|
elif error_status:
|
||||||
|
raise Exception(
|
||||||
|
"%s at %s"
|
||||||
|
% (
|
||||||
|
error_status.prettyPrint(),
|
||||||
|
error_index and var_binds[int(error_index) - 1][0] or "?",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return var_binds[0][1]
|
||||||
BIN
labeler_docs/brother/technical_reference_manual.pdf
Normal file
BIN
labeler_docs/brother/technical_reference_manual.pdf
Normal file
Binary file not shown.
|
|
@ -3,6 +3,8 @@ from typing import Callable
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from labeler.domain.objects import Dimension, MediaDefinition
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def current_dir(request) -> str:
|
def current_dir(request) -> str:
|
||||||
|
|
@ -23,3 +25,24 @@ def save_test_image(current_dir) -> Callable[[str, bytes], None]:
|
||||||
open(os.path.join(current_dir, "test_images", name), "wb").write(data)
|
open(os.path.join(current_dir, "test_images", name), "wb").write(data)
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def create_test_media() -> Callable[[int, int, int, int, int], MediaDefinition]:
|
||||||
|
def f(
|
||||||
|
width: int,
|
||||||
|
height: int,
|
||||||
|
dpi: int = 600,
|
||||||
|
margin_horizontal: int = 0,
|
||||||
|
margin_vertical: int = 0,
|
||||||
|
):
|
||||||
|
return MediaDefinition(
|
||||||
|
width=Dimension(mm=width),
|
||||||
|
length=Dimension(mm=height),
|
||||||
|
minimal_margin_horizontal=Dimension(mm=margin_horizontal),
|
||||||
|
minimal_margin_vertical=Dimension(mm=margin_vertical),
|
||||||
|
dpi=dpi,
|
||||||
|
description=f"test media {width}mm x{height}mm @ {dpi}dpi",
|
||||||
|
)
|
||||||
|
|
||||||
|
return f
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ def test_infinite_media():
|
||||||
minimal_margin_vertical=Dimension(mm=1),
|
minimal_margin_vertical=Dimension(mm=1),
|
||||||
minimal_margin_horizontal=Dimension(mm=2),
|
minimal_margin_horizontal=Dimension(mm=2),
|
||||||
dpi=300,
|
dpi=300,
|
||||||
|
description="test media",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert media.printable_length == Dimension(mm=math.inf)
|
assert media.printable_length == Dimension(mm=math.inf)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ def test_multiline_label(get_test_image):
|
||||||
assert label.bytes == expected_label
|
assert label.bytes == expected_label
|
||||||
|
|
||||||
|
|
||||||
def test_simple_label_no_fixed_width(get_test_image, save_test_image):
|
def test_simple_label_no_fixed_width(get_test_image):
|
||||||
renderer = PILRenderer()
|
renderer = PILRenderer()
|
||||||
expected_label = get_test_image("no_fixed_width.png")
|
expected_label = get_test_image("no_fixed_width.png")
|
||||||
|
|
||||||
|
|
@ -38,9 +38,9 @@ def test_simple_label_no_fixed_width(get_test_image, save_test_image):
|
||||||
assert label.bytes == expected_label
|
assert label.bytes == expected_label
|
||||||
|
|
||||||
|
|
||||||
def test_multiline_label_no_fixed_width(get_test_image, save_test_image):
|
def test_multiline_label_no_fixed_width(get_test_image):
|
||||||
renderer = PILRenderer()
|
renderer = PILRenderer()
|
||||||
expected_label = get_test_image("multiline_no_fixed_width.png")
|
expected_label = get_test_image("multiline_label_no_fixed_width.png")
|
||||||
|
|
||||||
definition = LabelDefinition(
|
definition = LabelDefinition(
|
||||||
text="dolphin\nis\nawesome", width=Dimension(mm=10), dpi=600
|
text="dolphin\nis\nawesome", width=Dimension(mm=10), dpi=600
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue