Android

Proxy Android para impresora térmica ESC POS

Hoy voy a enseñarte cómo imprimir en una impresora térmica conectada por USB a una computadora en la LAN pero desde Android usando un proxy y el plugin ESC POS versión 3.

Estoy suponiendo que ya sabes consumir el plugin HTTP a ESC POS y que solo quieres ampliar los dispositivos desde donde se imprime.

Explicación del problema

Primero necesitas conocer por qué es necesario un proxy. El plugin de impresión ESC POS se ejecuta en una computadora con Windows usando un servidor HTTP.

Para imprimir en la LAN desde un dispositivo Android que se encuentra dentro de la misma red, lo más normal sería consumir el plugin expuesto en la computadora Windows a través de su IP. Por ejemplo, hacer una petición POST a http://ip.de.computadora.con.plugin:8000/imprimir

El problema es que los navegadores web no permiten hacer peticiones inseguras desde conexiones seguras o, dicho con otras palabras, no permite hacer peticiones http desde un sitio https. La restricción dice:

  1. No puedes hacer peticiones inseguras desde sitios seguros
  2. Solo puedes hacer peticiones inseguras a localhost

Entonces no podemos conectarnos a la IP remota de Windows, pero sí podemos conectarnos a localhost, así que necesitamos un servidor web HTTP local ejecutándose en Android que nos permita escapar del navegador para hacer la conexión así:

JavaScript en navegador web Android -> Proxy con localhost -> Petición insegura a PC con plugin

Ya que una vez que escapamos de las restricciones del navegador podemos hacer peticiones inseguras sin problemas. Lo que no se permite hacer es:

JS en navegador web Android -> Petición insegura a PC con plugin

Otra explicación de este problema la tengo con el plugin versión 1:

Y con el mismo plugin para imprimir de Windows a Windows. La explicación contiene ilustraciones:

Descripción de las pruebas

El propósito del puente es permitir consumir al plugin versión 3 con JavaScript desde el navegador Android, pero para hacer pruebas más rápidas estoy usando cURL desde mi computadora de modo que la comunicación es:

cURL en mi computadora -> Puente en Android -> Plugin versión 3 en mi computadora de nuevo

Cuando se haga con JavaScript, la comunicación será:

Navegador web en Android -> Puente en Android -> Plugin versión 3 en mi computadora

Esto de cURL es opcional y para probar. Al final te dejaré ejemplos con JavaScript.

Detalles técnicos

El puente para el plugin versión 3 crea un servidor web en Android en el puerto 8000 para permitir invocar al plugin versión 3 en red. El servidor HTTP expone únicamente 2 rutas:

  1. / (raíz) para reenviar peticiones
  2. /version para consultar la versión del puente

La función principal es la que sirve como un proxy, y para consumir el proxy hay que hacer una petición a:

http://localhost:8000

Enviando el host de reenvío en el encabezado x-reenviar-a. El método de petición será el mismo que se hace al puente.

Es decir, si haces un POST http://localhost:8000 se va a hacer una petición POST a la URL indicada en x-reenviar-a, si haces una petición GET a http://localhost:8000 se va a hacer una petición GET a la URL indicada en x-reenviar-a y así sucesivamente.

Si el servidor al que se le hace la petición genera un mensaje de error o un código HTTP de error, ese mismo error será pasado al cliente. El puente trata de ser un proxy transparente.

Para el caso de que ocurra un error en el puente, se va a devolver un encabezado x-error-propio en true, indicando que la respuesta no corresponde al servidor remoto, sino a un error propio del puente.

IP de los ejemplos

Para estos ejemplos voy a usar la IP de mi computadora donde escucha el plugin, misma que es 192.168.0.24 y que se puede obtener ejecutando ipconfig en un cmd fijándose en Dirección IPv4 por ejemplo:

Dirección IPv4. . . . . . . . . . . . . . : 192.168.0.24

La IP de mi dispositivo Android donde está el puente es 192.168.0.6. Tanto el plugin como el puente escuchan en el mismo puerto que es el 8000. No vas a necesitar la IP de Android siempre, yo la uso porque hago las pruebas desde mi computadora.

Para los casos de consumir el puente desde JavaScript siempre vas a hacer la petición a http://localhost:8000. Los siguientes ejemplos, excepto donde se menciona a JavaScript, son hechos desde mi computadora así: Computadora -> Puente Android -> Mi computadora de nuevo

Al final dejaré los ejemplos con JS de modo que sea: JavaScript en navegador Android -> Puente Android -> Mi computadora

Ping a puente

Para saber si el puente realmente está operativo en el dispositivo Android podemos hacer una petición GET a /version así:

C:\Users\parzibyte>curl http://192.168.0.9:8000/version
{"version":"1.0","plataforma":"Puente","sistemaOperativo":"Android"}

Eso nos dará un objeto codificado como JSON. Dicho objeto tiene la versión, la plataforma y el sistema operativo. Tiene el mismo formato de respuesta que el plugin y lo he hecho así para que sea detectable usando la misma ruta.

Siempre que se consulte a localhost:8000/version se va a devolver la información para saber si estamos en Android (con plataforma en Puente) o en Desktop (con plataforma en Desktop).

Consultar versión del plugin versión 3

Ya probamos que el puente funciona bien, ahora veamos si su función de proxy también lo hace. Para ello consultemos la versión del plugin indicando el x-reenviar-a:

C:\Users\parzibyte>curl --header "x-reenviar-a: http://192.168.0.24:8000/version" http://192.168.0.9:8000
{"version":"3.5.1","plataforma":"Desktop","sistemaOperativo":"windows"}

Obtener lista de impresoras

Simplemente cambiamos la URL ya que las impresoras se obtienen en /impresoras:

C:\Users\parzibyte>curl --header "x-reenviar-a: http://192.168.0.24:8000/impresoras" http://192.168.0.9:8000
["ImpTer"]

Imprimir hola mundo

Enviemos un hola mundo. Tengo un archivo llamado hola.json que contiene el nombre de la impresora para el plugin como se describe en la API ESC POS, cuyo contenido es el siguiente:

{
    "serial": "",
    "nombreImpresora": "ImpTer",
    "operaciones": [
        {
            "nombre": "EscribirTexto",
            "argumentos": [
                "Hola\nimpresora\ndesde puente\n"
            ]
        }
    ]
}

Para enviarlo, usamos:

C:\Users\parzibyte\Desktop\probar_puente_pluginv3>curl -H "Content-Type: application/json" -H "x-reenviar-a: http://192.168.0.24:8000/imprimir" -d @hola.json http://192.168.0.9:8000
{"ok":true,"message":""}

Probar con JavaScript

Entonces si tú quieres imprimir con JavaScript desde un navegador web con Android donde el puente de impresión ya está ejecutándose  debes hacer una petición a localhost:8000 indicando la URL completa donde escucha el plugin.

En mi caso específico mi computadora con Windows tiene la IP 192.168.0.24, el plugin escucha en el puerto 8000 y según la guía para imprimir hay que hacer la petición POST a la ruta imprimir, así que la URL completa es http://192.168.0.24:8000/imprimir

const respuestaHttp = await fetch("http://localhost:8000", {
    method: "POST",
    body: JSON.stringify({
        serial: "",
        // Nombre de tu impresora que seguramente es distinta a la mía
        nombreImpresora: "ImpTer",
        operaciones: [
            {
                "nombre": "EscribirTexto",
                "argumentos": [
                    "Hola\nimpresora\ndesde puente\ncon JavaScript\n"
                ]
            }
        ]
    }),
    headers: {
        "x-reenviar-a": "http://192.168.0.24:8000/imprimir",
    }
})

Solo hay que saber identificar las 2 URL. La URL del plugin Windows es la que va en el encabezado x-reenviar-a, y la URL del puente Android siempre será localhost:8000.

Al ejecutar ese código desde Android con JS vamos a imprimir un ticket en la impresora térmica conectada a una computadora Windows en la LAN.

Puedes ver una aplicación web de demostración aquí y ver el código de dicha app en GitHub. Recuerda que debes probar la aplicación en Android.

Detección automática con JavaScript

Veamos otro ejemplo para imprimir siempre el mismo recibo en la impresora térmica con JavaScript pero detectando si estamos en Android o en Desktop. La misma página web funcionará en computadora como en móvil.

Primero veamos cómo detectar. Tanto el puente como el plugin exponen su versión en http://localhost:8000/versión así que simplemente comparamos los resultados:

let estamosEnAndroid = false;
try {
    // Puente y plugin devuelven la versión en la misma ruta
    // Recordar que para usar await hay que estar dentro de una función async
    const respuestaHttp = await fetch("http://localhost:8000/version");
    const version = await respuestaHttp.json()
    estamosEnAndroid = version.plataforma === "Puente";
} catch (e) {
    alert("Error consultando versión. Asegúrese de que el plugin o el puente se están ejecutando: " + e.message);
    return;
}
// Si llegamos hasta aquí es porque se ha detectado la plataforma correctamente
if (estamosEnAndroid) {
    alert("Estamos en Android con el puente");
} else {
    alert("Estamos en Desktop con el plugin")
}

Después, a partir de la bandera estamosEnAndroid procedemos a imprimir directamente o a reenviar la impresión:

if (estamosEnAndroid) {
    const direccionRemota = "http://192.168.0.24:8000/imprimir"
    const respuestaHttp = await fetch("http://localhost:8000", {
        body: JSON.stringify(payload),
        method: "POST",
        headers: {
            "x-reenviar-a": direccionRemota,
        }
    });
    const respuesta = await respuestaHttp.json();
    if (respuesta.ok) {
        alert("Impreso correctamente");
    } else {
        alert("Error: " + respuesta.message);
    }
} else {
    const respuestaHttp = await fetch("http://localhost:8000/imprimir", {
        body: JSON.stringify(payload),
        method: "POST",
    });
    const respuesta = await respuestaHttp.json();
    if (respuesta.ok) {
        alert("Impreso correctamente");
    } else {
        alert("Error: " + respuesta.message);
    }
}

De igual manera puedes probar el ejemplo en vivo en este enlace cuyo código completo encuentras en GitHub.

Conseguir proxy

Si quieres la APK envíame un mensaje para cotizarte el envío.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Resetear GOOJPRT PT-210 MTP-II (Impresora térmica)

El día de hoy vamos a ver cómo restablecer la impresora térmica GOOJPRT PT-210 a…

1 mes hace

Android – Servidor web con servicio en segundo plano

En este post voy a enseñarte a programar un servidor web en Android asegurándonos de…

1 mes hace

Cancelar trabajo de impresión con C++

En este post te quiero compartir un código de C++ para listar y cancelar trabajos…

2 meses hace

Copiar bytes de Golang a JavaScript con WebAssembly

Gracias a WebAssembly podemos ejecutar código de otros lenguajes de programación desde el navegador web…

3 meses hace

Imprimir PDF con Ghostscript en Windows de manera programada

Revisando y buscando maneras de imprimir un PDF desde la línea de comandos me encontré…

3 meses hace

Hacer pruebas en impresora térmica Bluetooth Android

Esta semana estuve recreando la API del plugin para impresoras térmicas en Android (HTTP a…

3 meses hace