136 lines
3.9 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
"""This module contains the implementation of the Network printer driver.
:author: python-escpos developers
:organization: `python-escpos <https://github.com/python-escpos>`_
:copyright: Copyright (c) 2012-2023 Bashlinux and python-escpos
:license: MIT
"""
import logging
import socket
from typing import Literal, Optional, Union
from ..escpos import Escpos
from ..exceptions import DeviceNotFoundError
def is_usable() -> bool:
"""Indicate whether this component can be used due to dependencies."""
return True
class Network(Escpos):
"""Network printer.
This class is used to attach to a networked printer.
You can also use this in order to attach to a printer that
is forwarded with ``socat``.
If you have a local printer on parallel port ``/dev/usb/lp0``
then you could start ``socat`` with:
.. code-block:: none
socat -u TCP4-LISTEN:4242,reuseaddr,fork OPEN:/dev/usb/lp0
Then you should be able to attach to port ``4242`` with this class.
Otherwise the normal use case would be to have a printer with
Ethernet interface.
This type of printer should work the same with this class.
For the address of the printer check its manuals.
inheritance:
.. inheritance-diagram:: escpos.printer.Network
:parts: 1
"""
_device: Union[Literal[False], Literal[None], socket.socket] = False
@staticmethod
def is_usable() -> bool:
"""Indicate whether this printer class is usable.
Will return True if dependencies are available.
Will return False if not.
"""
return is_usable()
def __init__(
self,
host: str = "",
port: int = 9100,
timeout: Union[int, float] = 60,
*args,
**kwargs,
):
"""Initialize network printer.
:param host: Printer's host name or IP address
:param port: Port to write to
:param timeout: timeout in seconds for the socket-library
"""
Escpos.__init__(self, *args, **kwargs)
self.host = host
self.port = port
self.timeout = timeout
def open(self, raise_not_found: bool = True) -> None:
"""Open TCP socket with ``socket``-library and set it as escpos device.
By default raise an exception if device is not found.
:param raise_not_found: Default True.
False to log error but do not raise exception.
:raises: :py:exc:`~escpos.exceptions.DeviceNotFoundError`
"""
if self._device:
self.close()
try:
# Open device
self.device: Optional[socket.socket] = socket.socket(
socket.AF_INET, socket.SOCK_STREAM
)
self.device.settimeout(self.timeout)
self.device.connect((self.host, self.port))
except OSError as e:
# Raise exception or log error and cancel
self.device = None
if raise_not_found:
raise DeviceNotFoundError(
f"Could not open socket for {self.host}:\n{e}"
)
else:
logging.error("Network device %s not found", self.host)
return
logging.info("Network printer enabled")
def _raw(self, msg):
"""Print any command sent in raw format.
:param msg: arbitrary code to be printed
:type msg: bytes
"""
self.device.sendall(msg)
def _read(self):
"""Read data from the TCP socket."""
return self.device.recv(16)
def close(self) -> None:
"""Close TCP connection."""
if not self._device:
return
logging.info("Closing Network connection to printer %s", self.host)
try:
self._device.shutdown(socket.SHUT_RDWR)
except socket.error:
pass
self._device.close()
self._device = False