diff --git a/labeler/domain/objects.py b/labeler/domain/objects.py index 950efba..a9a1cbe 100644 --- a/labeler/domain/objects.py +++ b/labeler/domain/objects.py @@ -108,6 +108,7 @@ class MediaDefinition(BaseModel): minimal_margin_vertical: Dimension minimal_margin_horizontal: Dimension dpi: int + description: str @property def printable_width(self) -> Dimension: diff --git a/labeler/infra/e550w_printer/media_definitions.py b/labeler/infra/e550w_printer/media_definitions.py new file mode 100644 index 0000000..20afe3b --- /dev/null +++ b/labeler/infra/e550w_printer/media_definitions.py @@ -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) diff --git a/labeler/infra/e550w_printer/printer.py b/labeler/infra/e550w_printer/printer.py new file mode 100644 index 0000000..62850da --- /dev/null +++ b/labeler/infra/e550w_printer/printer.py @@ -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] diff --git a/labeler_docs/brother/technical_reference_manual.pdf b/labeler_docs/brother/technical_reference_manual.pdf new file mode 100644 index 0000000..305e9a0 Binary files /dev/null and b/labeler_docs/brother/technical_reference_manual.pdf differ diff --git a/tests/fixtures.py b/tests/fixtures.py index 5167dfc..3ddb020 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -3,6 +3,8 @@ from typing import Callable import pytest +from labeler.domain.objects import Dimension, MediaDefinition + @pytest.fixture 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) 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 diff --git a/tests/labeler/domain/test_objects.py b/tests/labeler/domain/test_objects.py index fc20f24..128e80f 100644 --- a/tests/labeler/domain/test_objects.py +++ b/tests/labeler/domain/test_objects.py @@ -24,6 +24,7 @@ def test_infinite_media(): minimal_margin_vertical=Dimension(mm=1), minimal_margin_horizontal=Dimension(mm=2), dpi=300, + description="test media", ) assert media.printable_length == Dimension(mm=math.inf) diff --git a/tests/labeler/infra/test_renderer.py b/tests/labeler/infra/test_renderer.py index f3597bc..8d6a6e0 100644 --- a/tests/labeler/infra/test_renderer.py +++ b/tests/labeler/infra/test_renderer.py @@ -28,7 +28,7 @@ def test_multiline_label(get_test_image): 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() 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 -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() - expected_label = get_test_image("multiline_no_fixed_width.png") + expected_label = get_test_image("multiline_label_no_fixed_width.png") definition = LabelDefinition( text="dolphin\nis\nawesome", width=Dimension(mm=10), dpi=600