Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions pos_pywebdriver/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

===========================
POS PyWebDriver Integration
===========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:52ffe98ada0d1b8d9a5a25832cf759d3c861f2a9dc691bf2f6b22343ee06be71
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github
:target: https://github.com/OCA/pos/tree/19.0/pos_pywebdriver
:alt: OCA/pos
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/pos-19-0/pos-19-0-pos_pywebdriver
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/pos&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module enables Odoo 19 Point of Sale to print receipts via a
`PyWebDriver <https://github.com/pywebdriver/pywebdriver>`__ proxy, as
an alternative to the IoT Box.

PyWebDriver is an open-source Python service that exposes the same
``/hw_proxy/`` HTTP endpoints as the Odoo IoT Box, allowing direct
ESC/POS printing without proprietary hardware.

Key features:

- Adds a **PyWebDriver** section to POS Settings, independent of the
IoT Box configuration.
- Mutually exclusive with IoT Box: enabling PyWebDriver mode disables
``pos_iot``, and vice versa.
- Remaps the ``escpos`` driver key returned by PyWebDriver's
``status_json`` to the ``printer`` key expected by Odoo 19, so the
connection status indicator works correctly.
- Uses Odoo's built-in ``HWPrinter`` (no custom printer logic needed).
- Works on Odoo Community and Enterprise Editions (the latter may
install the incompatible ``pos_iot`` module).

**Table of contents**

.. contents::
:local:

Installation
============

PyWebDriver
-----------

Install and start
`PyWebDriver <https://github.com/pywebdriver/pywebdriver>`__ on the
machine connected to the ESC/POS printer. Refer to the `PyWebDriver
documentation <https://github.com/pywebdriver/pywebdriver#readme>`__ for
installation and configuration instructions.

HTTPS requirement
-----------------

In production (Odoo served over HTTPS), PyWebDriver must also run on
HTTPS to avoid mixed-content blocking by the browser.

The PyWebDriver Windows installer includes
``generate_certificates.bat``, which:

1. Installs the local CA into the OS/browser trust store
(``mkcert -install``)
2. Generates locally-trusted certificates for ``localhost``,
``127.0.0.1``, and ``::1``

Run ``generate_certificates.bat`` once after installation. The generated
``localhost+2.pem`` and ``localhost+2-key.pem`` are already referenced
in ``config\config.ini``. Set ``pywebdriver_ip`` in POS settings to
``https://127.0.0.1:<port>``.

pos_iot
-------

This module is **incompatible** with ``pos_iot``. If ``pos_iot`` is
installed, uninstall it before enabling PyWebDriver mode on any POS
configuration.

Configuration
=============

1. Go to **Point of Sale → Configuration → Settings** and select your
POS.
2. Ensure **IoT Box** is **not** checked.
3. Under the **PyWebDriver** section, enable **Enable PyWebDriver**.
4. Set the **Proxy IP Address** to the URL where PyWebDriver is
listening (default: ``https://127.0.0.1:8069``).
5. Save.

..

**Note:** Enabling **IoT Box** will automatically disable
PyWebDriver, and enabling PyWebDriver will automatically disable IoT
Box. They cannot be active at the same time.

Usage
=====

Once configured, open the POS session. The proxy connection is
established automatically on startup.

Status Indicator
----------------

The network status indicator in the POS navbar shows the proxy
connection state:

- **Green sitemap icon** — connected to PyWebDriver, printer ready.
- **Spinning icon** — connecting.
- **Red sitemap icon** — disconnected; check that PyWebDriver is
running and the IP address is correct.

Printing
--------

Use the **Print Receipt** button as normal. Receipts are rendered as
JPEG images and sent to PyWebDriver via
``/hw_proxy/default_printer_action``.

Troubleshooting
---------------

+----------------------------------+----------------------------------+
| Symptom | Likely cause |
+==================================+==================================+
| Status indicator stays | PyWebDriver not running, or |
| disconnected | wrong IP/port in settings |
+----------------------------------+----------------------------------+
| "Printing failed — unknown | PyWebDriver reachable but |
| error" | printer not responding; check |
| | ESC/POS connection |
+----------------------------------+----------------------------------+
| Browser print dialog opens | ``use_pywebdriver`` not saved, |
| instead | or session cache stale — reload |
| | the POS page |
+----------------------------------+----------------------------------+

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/pos/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/pos/issues/new?body=module:%20pos_pywebdriver%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Open Source Integrators

Contributors
------------

- Daniel Reis dreis@opensourceintegrators.com

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/pos <https://github.com/OCA/pos/tree/19.0/pos_pywebdriver>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions pos_pywebdriver/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
25 changes: 25 additions & 0 deletions pos_pywebdriver/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2026 Open Source Integrators
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

{
"name": "POS PyWebDriver Integration",
"version": "19.0.1.0.0",
"category": "Point of Sale",
"summary": "Print POS receipts via PyWebDriver proxy instead of IoT Box",
"author": "Open Source Integrators, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/pos",
"license": "LGPL-3",
"depends": ["point_of_sale"],
"excludes": ["pos_iot"],
"data": [
"views/pos_config_views.xml",
],
"assets": {
"point_of_sale._assets_pos": [
"pos_pywebdriver/static/src/**/*.esm.js",
],
},
"installable": True,
"auto_install": False,
"application": False,
}
2 changes: 2 additions & 0 deletions pos_pywebdriver/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import pos_config
from . import res_config_settings
52 changes: 52 additions & 0 deletions pos_pywebdriver/models/pos_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2026 Open Source Integrators
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

from odoo import api, fields, models
from odoo.exceptions import ValidationError


class PosConfig(models.Model):
_inherit = "pos.config"

use_pywebdriver = fields.Boolean(
string="Use PyWebDriver",
help="Connect receipt printer via a PyWebDriver proxy instead of IoT Box.",
default=False,
)
pywebdriver_ip = fields.Char(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry. I dont understand why not use native field. Proxy_ip

https://github.com/odoo/odoo/blob/19.0/addons/point_of_sale/models/pos_config.py#L125

string="PyWebDriver IP Address",
help="URL of the PyWebDriver proxy, e.g. https://127.0.0.1:8069",
default="https://127.0.0.1:8069",
)

@api.constrains("is_posbox", "use_pywebdriver")
def _check_pywebdriver_iot_exclusive(self):
for rec in self:
if rec.is_posbox and rec.use_pywebdriver:
raise ValidationError(
self.env._(
"IoT Box and PyWebDriver cannot both be enabled on "
"the same POS."
)
)

def write(self, vals):
# Auto-clear the other flag when one is enabled for better UX.
if vals.get("use_pywebdriver"):
vals.setdefault("is_posbox", False)
if vals.get("is_posbox"):
vals.setdefault("use_pywebdriver", False)
return super().write(vals)

@api.model
def _load_pos_data_read(self, records, config):
"""When use_pywebdriver is enabled, shim the standard proxy fields so
the POS JS connects via PyWebDriver. is_posbox=True is required for the
useProxy getter."""
read_records = super()._load_pos_data_read(records, config)
if read_records and config.use_pywebdriver and config.pywebdriver_ip:
record = read_records[0]
record["proxy_ip"] = config.pywebdriver_ip
record["is_posbox"] = True # required for useProxy getter
record["iface_print_via_proxy"] = True
return read_records
17 changes: 17 additions & 0 deletions pos_pywebdriver/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2026 Open Source Integrators
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

from odoo import fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

pos_use_pywebdriver = fields.Boolean(
related="pos_config_id.use_pywebdriver",
readonly=False,
)
pos_pywebdriver_ip = fields.Char(
related="pos_config_id.pywebdriver_ip",
readonly=False,
)
3 changes: 3 additions & 0 deletions pos_pywebdriver/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
11 changes: 11 additions & 0 deletions pos_pywebdriver/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
1. Go to **Point of Sale → Configuration → Settings** and select your
POS.
2. Ensure **IoT Box** is **not** checked.
3. Under the **PyWebDriver** section, enable **Enable PyWebDriver**.
4. Set the **Proxy IP Address** to the URL where PyWebDriver is
listening (default: `https://127.0.0.1:8069`).
5. Save.

> **Note:** Enabling **IoT Box** will automatically disable PyWebDriver,
> and enabling PyWebDriver will automatically disable IoT Box. They
> cannot be active at the same time.
1 change: 1 addition & 0 deletions pos_pywebdriver/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Daniel Reis <dreis@opensourceintegrators.com>
20 changes: 20 additions & 0 deletions pos_pywebdriver/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
This module enables Odoo 19 Point of Sale to print receipts via a
[PyWebDriver](https://github.com/pywebdriver/pywebdriver) proxy, as an
alternative to the IoT Box.

PyWebDriver is an open-source Python service that exposes the same
`/hw_proxy/` HTTP endpoints as the Odoo IoT Box, allowing direct
ESC/POS printing without proprietary hardware.

Key features:

- Adds a **PyWebDriver** section to POS Settings, independent of the
IoT Box configuration.
- Mutually exclusive with IoT Box: enabling PyWebDriver mode disables
`pos_iot`, and vice versa.
- Remaps the `escpos` driver key returned by PyWebDriver's
`status_json` to the `printer` key expected by Odoo 19, so the
connection status indicator works correctly.
- Uses Odoo's built-in `HWPrinter` (no custom printer logic needed).
- Works on Odoo Community and Enterprise Editions
(the latter may install the incompatible `pos_iot` module).
27 changes: 27 additions & 0 deletions pos_pywebdriver/readme/INSTALL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## PyWebDriver

Install and start [PyWebDriver](https://github.com/pywebdriver/pywebdriver)
on the machine connected to the ESC/POS printer. Refer to the
[PyWebDriver documentation](https://github.com/pywebdriver/pywebdriver#readme)
for installation and configuration instructions.

## HTTPS requirement

In production (Odoo served over HTTPS), PyWebDriver must also run on HTTPS to
avoid mixed-content blocking by the browser.

The PyWebDriver Windows installer includes `generate_certificates.bat`, which:

1. Installs the local CA into the OS/browser trust store (`mkcert -install`)
2. Generates locally-trusted certificates for `localhost`, `127.0.0.1`, and `::1`

Run `generate_certificates.bat` once after installation. The generated
`localhost+2.pem` and `localhost+2-key.pem` are already referenced in
`config\config.ini`. Set `pywebdriver_ip` in POS settings to
`https://127.0.0.1:<port>`.

## pos_iot

This module is **incompatible** with `pos_iot`. If `pos_iot` is
installed, uninstall it before enabling PyWebDriver mode on any POS
configuration.
25 changes: 25 additions & 0 deletions pos_pywebdriver/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Once configured, open the POS session. The proxy connection is
established automatically on startup.

## Status Indicator

The network status indicator in the POS navbar shows the proxy
connection state:

- **Green sitemap icon** — connected to PyWebDriver, printer ready.
- **Spinning icon** — connecting.
- **Red sitemap icon** — disconnected; check that PyWebDriver is running
and the IP address is correct.

## Printing

Use the **Print Receipt** button as normal. Receipts are rendered as
JPEG images and sent to PyWebDriver via `/hw_proxy/default_printer_action`.

## Troubleshooting

| Symptom | Likely cause |
|---|---|
| Status indicator stays disconnected | PyWebDriver not running, or wrong IP/port in settings |
| "Printing failed — unknown error" | PyWebDriver reachable but printer not responding; check ESC/POS connection |
| Browser print dialog opens instead | `use_pywebdriver` not saved, or session cache stale — reload the POS page |
Loading
Loading