odoo-driver-plugin-display-escpos 1.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (16) hide show
  1. odoo_driver_plugin_display_escpos-1.0.4/PKG-INFO +94 -0
  2. odoo_driver_plugin_display_escpos-1.0.4/README.md +67 -0
  3. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/__init__.py +17 -0
  4. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/display_escpos_driver.py +175 -0
  5. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/display_escpos_route_odoo.py +22 -0
  6. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/display_escpos_route_website.py +23 -0
  7. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/i18n/babel.cfg +2 -0
  8. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/i18n/fr/LC_MESSAGES/messages.mo +0 -0
  9. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/i18n/fr/LC_MESSAGES/messages.po +120 -0
  10. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/i18n/messages.pot +119 -0
  11. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/static/img/aures__ocd_300.png +0 -0
  12. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/static/img/generic_device.png +0 -0
  13. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/static/img/oxhoo__af240.png +0 -0
  14. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/templates/driver_display_escpos.html.jinja +117 -0
  15. odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/usb_devices.py +4 -0
  16. odoo_driver_plugin_display_escpos-1.0.4/pyproject.toml +49 -0
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: odoo-driver-plugin-display-escpos
3
+ Version: 1.0.4
4
+ Summary: Plugin for odoo-driver. Support escpos display devices (LCD).
5
+ License: AGPLv3+
6
+ Keywords: Odoo,Point Of Sale,Driver,Device,Display,LCD,ESC/POS,escpos,Aures ®,Bixolon ®,Oxhoo ®
7
+ Author: Sylvain LE GAL
8
+ Author-email: sylvain.legal@grap.coop
9
+ Maintainer: Sylvain LE GAL
10
+ Maintainer-email: sylvain.legal@grap.coop
11
+ Requires-Python: >=3.9,<4.0
12
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Operating System :: Unix
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Framework :: Odoo
23
+ Requires-Dist: odoo-driver
24
+ Project-URL: Repository, https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos
25
+ Description-Content-Type: text/markdown
26
+
27
+ # Odoo Driver Plugin - Display ESC/POS
28
+
29
+ ![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)
30
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/odoo-driver-plugin-display-escpos)
31
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/odoo-driver-plugin-display-escpos)
32
+ ![GitLab last commit](https://img.shields.io/gitlab/last-commit/81585306)
33
+ ![GitLab stars](https://img.shields.io/gitlab/stars/81585306?style=social)
34
+
35
+ This project provide a plugin for ``odoo-driver``
36
+ to communicate with LCD Display with ECS/POS protocol.
37
+
38
+ This plugin requires the following odoo module : [`pos_odoo_driver_display`](https://github.com/grap/odoo-addons-pos)
39
+
40
+ To install this plugin, refer to the main project page : https://gitlab.com/grap-rhone-alpes/odoo-driver/.
41
+
42
+ For developpers, refer to this page : https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-printer-escpos/-/blob/main/DEVELOP.md.
43
+
44
+ ## Table of contents
45
+
46
+ 1. [Compatible Devices](#compatible-devices)
47
+ 2. [Credits](#credits)
48
+
49
+
50
+ <a name="compatible-devices"></a>
51
+
52
+ ## Compatible Devices
53
+
54
+ <table style="width: 100%;">
55
+ <tbody>
56
+ <tr>
57
+ <td>Aures - OCD 300</td>
58
+ <td>
59
+ <img src="https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos/-/raw/main/odoo_driver_plugin_display_escpos/static/img/aures__ocd_300.png" width="200" height="200" />
60
+ </td>
61
+ </tr>
62
+ <tr>
63
+ <td>Oxhoo - AF 240</td>
64
+ <td>
65
+ <img src="https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos/-/raw/main/odoo_driver_plugin_display_escpos/static/img/oxhoo__af240.png" width="200" height="200" />
66
+ </td>
67
+ </tr>
68
+ </tbody>
69
+ </table>
70
+
71
+ <a name="credits"></a>
72
+
73
+ ## Credits
74
+
75
+ ### Authors
76
+
77
+ * GRAP <https://www.grap.coop>
78
+
79
+ ### Contributors
80
+
81
+ * Sylvain LE GAL <https://www.grap.coop/>
82
+
83
+ ### Extra authorship
84
+
85
+ Part of the code in this project comes from the following projects, including:
86
+
87
+ * [Pywebdriver](https://github.com/pywebdriver/pywebdriver) (AGPL-3.0).
88
+
89
+ * [pyposdisplay](https://github.com/akretion/pyposdisplay) (AGPL-3.0).
90
+
91
+ ### Images
92
+
93
+ * [LCD Customer Display Icon](https://www.flaticon.com/fr/icone-gratuite/lcd_9622586), created by Iconic Panda (Flaticon).
94
+
@@ -0,0 +1,67 @@
1
+ # Odoo Driver Plugin - Display ESC/POS
2
+
3
+ ![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)
4
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/odoo-driver-plugin-display-escpos)
5
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/odoo-driver-plugin-display-escpos)
6
+ ![GitLab last commit](https://img.shields.io/gitlab/last-commit/81585306)
7
+ ![GitLab stars](https://img.shields.io/gitlab/stars/81585306?style=social)
8
+
9
+ This project provide a plugin for ``odoo-driver``
10
+ to communicate with LCD Display with ECS/POS protocol.
11
+
12
+ This plugin requires the following odoo module : [`pos_odoo_driver_display`](https://github.com/grap/odoo-addons-pos)
13
+
14
+ To install this plugin, refer to the main project page : https://gitlab.com/grap-rhone-alpes/odoo-driver/.
15
+
16
+ For developpers, refer to this page : https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-printer-escpos/-/blob/main/DEVELOP.md.
17
+
18
+ ## Table of contents
19
+
20
+ 1. [Compatible Devices](#compatible-devices)
21
+ 2. [Credits](#credits)
22
+
23
+
24
+ <a name="compatible-devices"></a>
25
+
26
+ ## Compatible Devices
27
+
28
+ <table style="width: 100%;">
29
+ <tbody>
30
+ <tr>
31
+ <td>Aures - OCD 300</td>
32
+ <td>
33
+ <img src="https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos/-/raw/main/odoo_driver_plugin_display_escpos/static/img/aures__ocd_300.png" width="200" height="200" />
34
+ </td>
35
+ </tr>
36
+ <tr>
37
+ <td>Oxhoo - AF 240</td>
38
+ <td>
39
+ <img src="https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos/-/raw/main/odoo_driver_plugin_display_escpos/static/img/oxhoo__af240.png" width="200" height="200" />
40
+ </td>
41
+ </tr>
42
+ </tbody>
43
+ </table>
44
+
45
+ <a name="credits"></a>
46
+
47
+ ## Credits
48
+
49
+ ### Authors
50
+
51
+ * GRAP <https://www.grap.coop>
52
+
53
+ ### Contributors
54
+
55
+ * Sylvain LE GAL <https://www.grap.coop/>
56
+
57
+ ### Extra authorship
58
+
59
+ Part of the code in this project comes from the following projects, including:
60
+
61
+ * [Pywebdriver](https://github.com/pywebdriver/pywebdriver) (AGPL-3.0).
62
+
63
+ * [pyposdisplay](https://github.com/akretion/pyposdisplay) (AGPL-3.0).
64
+
65
+ ### Images
66
+
67
+ * [LCD Customer Display Icon](https://www.flaticon.com/fr/icone-gratuite/lcd_9622586), created by Iconic Panda (Flaticon).
@@ -0,0 +1,17 @@
1
+ from pathlib import Path
2
+
3
+ from single_source import get_version
4
+
5
+ from . import usb_devices
6
+ from .display_escpos_driver import DriverDisplayEscPos
7
+ from .display_escpos_route_odoo import driver_routes_odoo
8
+ from .display_escpos_route_website import driver_routes_website
9
+
10
+ __version__ = get_version(__name__, Path(__file__).parent.parent)
11
+ __package_name__ = __name__
12
+
13
+ DRIVER_FUNCTION = "display"
14
+ PLUGIN_NAME = "display_escpos"
15
+ DRIVER_CLASS = DriverDisplayEscPos
16
+
17
+ DRIVER_ROUTES_GROUPS = [driver_routes_odoo, driver_routes_website]
@@ -0,0 +1,175 @@
1
+ from flask_babel import gettext as _
2
+ from loguru import logger
3
+ from odoo_driver.drivers.driver_abstract_with_queue import DriverAbstractWithQueue
4
+ from odoo_driver.tools.tools_serial_code import FF, US
5
+
6
+
7
+ class DriverDisplayEscPos(DriverAbstractWithQueue):
8
+ _use_serial = True
9
+ _driver_function = "display"
10
+ _plugin_name = "display_escpos"
11
+ _default_max_queue_size = 3
12
+ _default_delay = 1
13
+
14
+ @property
15
+ def name(self):
16
+ return _("LCD Display")
17
+
18
+ @logger.catch
19
+ def configure(self):
20
+ with self._get_serial() as serial:
21
+ # Note: letter looks to be encoded in ISO-8859-1
22
+ # C:43 ; D: 44; T:54 ; U:55 ...
23
+
24
+ logger.debug(
25
+ "Display: Set Mode: ESC/POS with the message STX ENQ 05 C 31 ETX ..."
26
+ )
27
+ serial.write(b"\x02\x05\x43\x31\x03")
28
+
29
+ logger.debug(
30
+ "Display: Set Character: France with the message STX ENQ 05 T 01 ETX ..."
31
+ )
32
+ serial.write(b"\x02\x05\x54\x01\x03")
33
+
34
+ logger.debug(
35
+ "Display: Set Encoding: CP858 with the message STX ENQ 05 U 13 ETX ..."
36
+ )
37
+ serial.write(b"\x02\x05\x55\x13\x03")
38
+
39
+ @logger.catch
40
+ def _process_task_serial(self, serial, task):
41
+ lines_ascii = self._convert_task_to_ascii_lines(task)
42
+
43
+ self._clear_screen(serial)
44
+
45
+ for row, text in enumerate(lines_ascii, 1):
46
+ self._position_cursor(serial, 1, row)
47
+ self._send_message(serial, [text])
48
+
49
+ def _convert_task_to_ascii_lines(self, task):
50
+ lines_ascii = []
51
+
52
+ action = task.data["action"]
53
+
54
+ if action == "display_lines":
55
+ lines_ascii = task.data.get("text_lines", [])
56
+ if type(lines_ascii) is str:
57
+ lines_ascii = lines_ascii.split("\n")
58
+
59
+ elif action in [
60
+ "add_line",
61
+ "update_quantity",
62
+ "update_unit_price",
63
+ "update_discount",
64
+ ]:
65
+ # First Line
66
+ left_part = task.data.get("product_name")
67
+ right_part = self._prepare_percent(task.data.get("discount"))
68
+ line_1 = self._prepare_line(left_part, right_part)
69
+
70
+ # Second Line Line
71
+ left_part = (
72
+ self._prepare_qty(task.data.get("quantity"))
73
+ + "×"
74
+ + self._prepare_price(task.data.get("unit_price"))
75
+ )
76
+ right_part = self._prepare_price(task.data.get("total"))
77
+ line_2 = self._prepare_line(left_part, right_part)
78
+
79
+ lines_ascii = [line_1, line_2]
80
+
81
+ elif action == "remove_line":
82
+ lines_ascii = [
83
+ _("Deleting Line ..."),
84
+ self._prepare_line(task.data.get("product_name")),
85
+ ]
86
+
87
+ elif action == "payment_balance":
88
+ total = task.data.get("total")
89
+ total_paid = task.data.get("total_paid")
90
+ total_due = task.data.get("total_due")
91
+ total_to_pay = total - total_paid
92
+ line_1 = self._prepare_line(_("Total:"), self._prepare_price(total))
93
+ line_2 = ""
94
+ if total_paid != 0.0:
95
+ if total_to_pay > 0:
96
+ line_2 = self._prepare_line(
97
+ _("To Pay:"), self._prepare_price(total_to_pay)
98
+ )
99
+ elif total_due < 0:
100
+ line_2 = self._prepare_line(
101
+ _("Change:"), self._prepare_price(-total_due)
102
+ )
103
+ lines_ascii = [line_1, line_2]
104
+
105
+ else:
106
+ raise NotImplementedError(
107
+ f"{action} is not a valid action for the Display device."
108
+ )
109
+
110
+ return lines_ascii
111
+
112
+ def _clear_screen(self, serial):
113
+ self._send_message(serial, [FF], "Clear Screen")
114
+
115
+ def _position_cursor(self, serial, col=1, row=1):
116
+ self._send_message(
117
+ serial, [US, chr(36), chr(col), chr(row)], f"Move to col {col} / row {row}"
118
+ )
119
+
120
+ def _send_message(self, serial, message_list, action_description=""):
121
+ message_list = [x for x in message_list if x]
122
+ if not message_list:
123
+ return
124
+ message = b"".join([x.encode("cp858", errors="ignore") for x in message_list])
125
+ if action_description:
126
+ logger.debug(
127
+ f"Display: {action_description} with the message {message} ..."
128
+ )
129
+
130
+ serial.write(message)
131
+
132
+ def _display_float(self, float_repr, reduce_txt=True, suffix=""):
133
+ if reduce_txt:
134
+ while float_repr:
135
+ if float_repr[-1:] == "0":
136
+ float_repr = float_repr[:-1]
137
+ else:
138
+ break
139
+ if float_repr[-1:] == ".":
140
+ float_repr = float_repr[:-1]
141
+ return float_repr.replace(".", ",") + suffix
142
+
143
+ def _prepare_percent(self, percent):
144
+ try:
145
+ percent = float(percent)
146
+ except ValueError:
147
+ return ""
148
+ except TypeError:
149
+ return ""
150
+ if not percent:
151
+ return ""
152
+ return self._display_float("-{:.1f}".format(percent), suffix="%")
153
+
154
+ def _prepare_qty(self, quantity):
155
+ try:
156
+ quantity = float(quantity)
157
+ except ValueError:
158
+ quantity = 0.0
159
+ return self._display_float("{:.3f}".format(quantity))
160
+
161
+ def _prepare_price(self, price):
162
+ try:
163
+ price = float(price)
164
+ except ValueError:
165
+ price = 0.0
166
+ return self._display_float("{:.2f}".format(price), reduce_txt=False, suffix="€")
167
+
168
+ def _prepare_line(self, left_part, right_part=""):
169
+ result = left_part[:20].ljust(20, " ")
170
+ if right_part:
171
+ right_part = right_part[-20:]
172
+ result = result[0 : (20 - 1 - len(right_part))]
173
+ result += " " + right_part
174
+
175
+ return result
@@ -0,0 +1,22 @@
1
+ import simplejson
2
+ from flask import Blueprint, jsonify, request
3
+ from loguru import logger
4
+ from odoo_driver.tools.tools_interface import interface
5
+
6
+ driver_routes_odoo = Blueprint(
7
+ "route_display_escpos_odoo", __name__, template_folder="templates"
8
+ )
9
+
10
+
11
+ @driver_routes_odoo.route("/hw_proxy/display_show", methods=["POST", "PUT"])
12
+ @logger.catch
13
+ def display_show():
14
+ driver = interface.drivers.get("display")
15
+ data = request.json.get("params", {}).get("data", {})
16
+ interface.log_data(data, "display", "DEBUG")
17
+
18
+ if type(data) is str:
19
+ data = simplejson.loads(data)
20
+
21
+ driver.add_task(data)
22
+ return jsonify(jsonrpc="2.0", result=True)
@@ -0,0 +1,23 @@
1
+ from flask import Blueprint, jsonify, render_template
2
+ from loguru import logger
3
+ from odoo_driver.tools.tools_interface import interface
4
+
5
+ driver_routes_website = Blueprint(
6
+ "route_display_escpos_website", __name__, template_folder="templates"
7
+ )
8
+
9
+
10
+ @driver_routes_website.route("/driver/display_escpos.html")
11
+ @logger.catch
12
+ def driver_display_escpos():
13
+ driver = interface.drivers.get("display")
14
+ return render_template("driver_display_escpos.html.jinja", driver=driver)
15
+
16
+
17
+ @driver_routes_website.route("/display_escpos/configure", methods=["POST", "PUT"])
18
+ @logger.catch
19
+ def display_escpos_configure():
20
+ driver = interface.drivers.get("display")
21
+ driver.configure()
22
+
23
+ return jsonify(jsonrpc="2.0", result=True)
@@ -0,0 +1,2 @@
1
+ [python: **.py]
2
+ [jinja2: **/templates/**.html.jinja]
@@ -0,0 +1,120 @@
1
+ # French translations for PROJECT.
2
+ # Copyright (C) 2026 ORGANIZATION
3
+ # This file is distributed under the same license as the PROJECT project.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, 2026.
5
+ #
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: PROJECT VERSION\n"
9
+ "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
10
+ "POT-Creation-Date: 2026-04-24 23:49+0200\n"
11
+ "PO-Revision-Date: 2026-04-24 23:44+0200\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language: fr\n"
14
+ "Language-Team: fr <LL@li.org>\n"
15
+ "Plural-Forms: nplurals=2; plural=(n > 1);\n"
16
+ "MIME-Version: 1.0\n"
17
+ "Content-Type: text/plain; charset=utf-8\n"
18
+ "Content-Transfer-Encoding: 8bit\n"
19
+ "Generated-By: Babel 2.18.0\n"
20
+
21
+ #: display_escpos_driver.py:15
22
+ msgid "LCD Display"
23
+ msgstr "Afficheur LCD"
24
+
25
+ #: display_escpos_driver.py:82
26
+ msgid "Deleting Line ..."
27
+ msgstr "Supprimer une ligne ..."
28
+
29
+ #: display_escpos_driver.py:91
30
+ msgid "Total:"
31
+ msgstr "Total :"
32
+
33
+ #: display_escpos_driver.py:96
34
+ msgid "To Pay:"
35
+ msgstr "A payer :"
36
+
37
+ #: display_escpos_driver.py:100
38
+ msgid "Change:"
39
+ msgstr "Rendu :"
40
+
41
+ #: templates/driver_display_escpos.html.jinja:7
42
+ msgid "Test communication with LCD Display"
43
+ msgstr "Test de communication avec l'afficheur LCD"
44
+
45
+ #: templates/driver_display_escpos.html.jinja:8
46
+ msgid "Configure"
47
+ msgstr "Configurer"
48
+
49
+ #: templates/driver_display_escpos.html.jinja:10
50
+ msgid "Set Mode"
51
+ msgstr "Définir le mode"
52
+
53
+ #: templates/driver_display_escpos.html.jinja:14
54
+ msgid "Set Character"
55
+ msgstr "Définir les charactères"
56
+
57
+ #: templates/driver_display_escpos.html.jinja:18
58
+ msgid "Set Encoding"
59
+ msgstr "Définir l'encodage"
60
+
61
+ #: templates/driver_display_escpos.html.jinja:22
62
+ msgid "Configure Display"
63
+ msgstr "Configurer l'afficheur"
64
+
65
+ #: templates/driver_display_escpos.html.jinja:24
66
+ msgid "Send Text"
67
+ msgstr "Envoyer le texte"
68
+
69
+ #: templates/driver_display_escpos.html.jinja:26
70
+ msgid "Line 1"
71
+ msgstr "Ligne 1"
72
+
73
+ #: templates/driver_display_escpos.html.jinja:30
74
+ msgid "Line 2"
75
+ msgstr "Ligne 2"
76
+
77
+ #: templates/driver_display_escpos.html.jinja:34
78
+ msgid "Send text to display"
79
+ msgstr "Envoyer le texte à l'afficheur"
80
+
81
+ #: templates/driver_display_escpos.html.jinja:36
82
+ msgid "Send Product Information"
83
+ msgstr "Envoyer les informations produits"
84
+
85
+ #: templates/driver_display_escpos.html.jinja:38
86
+ msgid "Product Name"
87
+ msgstr "Nom du produit"
88
+
89
+ #: templates/driver_display_escpos.html.jinja:42
90
+ msgid "Quantity"
91
+ msgstr "Quantité"
92
+
93
+ #: templates/driver_display_escpos.html.jinja:44
94
+ msgid "Unit Price"
95
+ msgstr "Prix unitaire"
96
+
97
+ #: templates/driver_display_escpos.html.jinja:49
98
+ msgid "Discount"
99
+ msgstr "Remise"
100
+
101
+ #: templates/driver_display_escpos.html.jinja:52
102
+ msgid "Total"
103
+ msgstr "Total"
104
+
105
+ #: templates/driver_display_escpos.html.jinja:57
106
+ msgid "Action"
107
+ msgstr "Action"
108
+
109
+ #: templates/driver_display_escpos.html.jinja:59
110
+ msgid "Add Line"
111
+ msgstr "Ajouter une ligne"
112
+
113
+ #: templates/driver_display_escpos.html.jinja:60
114
+ msgid "Remove Line"
115
+ msgstr "Supprimer une ligne"
116
+
117
+ #: templates/driver_display_escpos.html.jinja:64
118
+ msgid "Send product information to display"
119
+ msgstr "Envoyer les informations produits à l'afficheur"
120
+
@@ -0,0 +1,119 @@
1
+ # Translations template for PROJECT.
2
+ # Copyright (C) 2026 ORGANIZATION
3
+ # This file is distributed under the same license as the PROJECT project.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, 2026.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: PROJECT VERSION\n"
10
+ "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
11
+ "POT-Creation-Date: 2026-04-24 23:49+0200\n"
12
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=utf-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Generated-By: Babel 2.18.0\n"
19
+
20
+ #: display_escpos_driver.py:15
21
+ msgid "LCD Display"
22
+ msgstr ""
23
+
24
+ #: display_escpos_driver.py:82
25
+ msgid "Deleting Line ..."
26
+ msgstr ""
27
+
28
+ #: display_escpos_driver.py:91
29
+ msgid "Total:"
30
+ msgstr ""
31
+
32
+ #: display_escpos_driver.py:96
33
+ msgid "To Pay:"
34
+ msgstr ""
35
+
36
+ #: display_escpos_driver.py:100
37
+ msgid "Change:"
38
+ msgstr ""
39
+
40
+ #: templates/driver_display_escpos.html.jinja:7
41
+ msgid "Test communication with LCD Display"
42
+ msgstr ""
43
+
44
+ #: templates/driver_display_escpos.html.jinja:8
45
+ msgid "Configure"
46
+ msgstr ""
47
+
48
+ #: templates/driver_display_escpos.html.jinja:10
49
+ msgid "Set Mode"
50
+ msgstr ""
51
+
52
+ #: templates/driver_display_escpos.html.jinja:14
53
+ msgid "Set Character"
54
+ msgstr ""
55
+
56
+ #: templates/driver_display_escpos.html.jinja:18
57
+ msgid "Set Encoding"
58
+ msgstr ""
59
+
60
+ #: templates/driver_display_escpos.html.jinja:22
61
+ msgid "Configure Display"
62
+ msgstr ""
63
+
64
+ #: templates/driver_display_escpos.html.jinja:24
65
+ msgid "Send Text"
66
+ msgstr ""
67
+
68
+ #: templates/driver_display_escpos.html.jinja:26
69
+ msgid "Line 1"
70
+ msgstr ""
71
+
72
+ #: templates/driver_display_escpos.html.jinja:30
73
+ msgid "Line 2"
74
+ msgstr ""
75
+
76
+ #: templates/driver_display_escpos.html.jinja:34
77
+ msgid "Send text to display"
78
+ msgstr ""
79
+
80
+ #: templates/driver_display_escpos.html.jinja:36
81
+ msgid "Send Product Information"
82
+ msgstr ""
83
+
84
+ #: templates/driver_display_escpos.html.jinja:38
85
+ msgid "Product Name"
86
+ msgstr ""
87
+
88
+ #: templates/driver_display_escpos.html.jinja:42
89
+ msgid "Quantity"
90
+ msgstr ""
91
+
92
+ #: templates/driver_display_escpos.html.jinja:44
93
+ msgid "Unit Price"
94
+ msgstr ""
95
+
96
+ #: templates/driver_display_escpos.html.jinja:49
97
+ msgid "Discount"
98
+ msgstr ""
99
+
100
+ #: templates/driver_display_escpos.html.jinja:52
101
+ msgid "Total"
102
+ msgstr ""
103
+
104
+ #: templates/driver_display_escpos.html.jinja:57
105
+ msgid "Action"
106
+ msgstr ""
107
+
108
+ #: templates/driver_display_escpos.html.jinja:59
109
+ msgid "Add Line"
110
+ msgstr ""
111
+
112
+ #: templates/driver_display_escpos.html.jinja:60
113
+ msgid "Remove Line"
114
+ msgstr ""
115
+
116
+ #: templates/driver_display_escpos.html.jinja:64
117
+ msgid "Send product information to display"
118
+ msgstr ""
119
+
@@ -0,0 +1,117 @@
1
+ <!DOCTYPE html>
2
+
3
+ {% extends "layout.html.jinja" %}
4
+
5
+ {% block content %}
6
+
7
+ <h1>{{_('Test communication with LCD Display')}}</h1>
8
+ <h2>{{_('Configure')}}</h2>
9
+ <div class="input-group">
10
+ <span class="input-group-text">{{_('Set Mode')}}</span>
11
+ <input type="text" class="form-control" value="ESC/POS" readonly/>
12
+ </div>
13
+ <div class="input-group">
14
+ <span class="input-group-text">{{_('Set Character')}}</span>
15
+ <input type="text" class="form-control" value="France" readonly/>
16
+ </div>
17
+ <div class="input-group">
18
+ <span class="input-group-text">{{_('Set Encoding')}}</span>
19
+ <input type="text" class="form-control" value="CP858" readonly/>
20
+ </div>
21
+ <div class="input-group">
22
+ <button type="button" class="btn {%if driver.is_connected %}btn-primary{% else %}btn-secondary{% endif %}" onclick="test_display_configure(); return false;">{{_('Configure Display')}}</button>
23
+ </div>
24
+ <h2>{{_('Send Text')}}</h2>
25
+ <div class="input-group">
26
+ <span class="input-group-text">{{_('Line 1')}}</span>
27
+ <input type="text" class="form-control" id="display_line_1">
28
+ </div>
29
+ <div class="input-group">
30
+ <span class="input-group-text">{{_('Line 2')}}</span>
31
+ <input type="text" class="form-control" id="display_line_2">
32
+ </div>
33
+ <div class="input-group">
34
+ <button type="button" class="btn {%if driver.is_connected %}btn-primary{% else %}btn-secondary{% endif %}" onclick="test_display_text(); return false;">{{_('Send text to display')}}</button>
35
+ </div>
36
+ <h2>{{_('Send Product Information')}}</h2>
37
+ <div class="input-group">
38
+ <span class="input-group-text">{{_('Product Name')}}</span>
39
+ <input type="text" class="form-control" id="display_product_name" />
40
+ </div>
41
+ <div class="input-group">
42
+ <span class="input-group-text">{{_('Quantity')}}</span>
43
+ <input type="text" class="form-control" id="display_quantity" />
44
+ <span class="input-group-text">{{_('Unit Price')}}</span>
45
+ <input type="text" class="form-control" id="display_unit_price" />
46
+ <span class="input-group-text">€</span>
47
+ </div>
48
+ <div class="input-group">
49
+ <span class="input-group-text">{{_('Discount')}}</span>
50
+ <input type="text" class="form-control" id="display_discount" />
51
+ <span class="input-group-text">%</span>
52
+ <span class="input-group-text">{{_('Total')}}</span>
53
+ <input type="text" class="form-control" id="display_total" />
54
+ <span class="input-group-text">€</span>
55
+ </div>
56
+ <div class="input-group">
57
+ <span class="input-group-text">{{_('Action')}}</span>
58
+ <select type="select" class="form-control" id="display_action">
59
+ <option value="add_line" selected>{{_('Add Line')}}</option>
60
+ <option value="remove_line">{{_('Remove Line')}}</option>
61
+ </select>
62
+ </div>
63
+ <div class="input-group">
64
+ <button type="button" class="btn {%if driver.is_connected %}btn-primary{% else %}btn-secondary{% endif %}" onclick="test_display_product(); return false;">{{_('Send product information to display')}}</button>
65
+ </div>
66
+
67
+ <script>
68
+
69
+ /* ***************************
70
+ Device Display
71
+ ******************************/
72
+ $( document ).ready(function() {
73
+ $("#display_line_1").focus();
74
+ });
75
+
76
+
77
+ function test_display_configure() {
78
+ _call_ajax("/display_escpos/configure")
79
+ }
80
+
81
+
82
+ function test_display_text() {
83
+ const data = {
84
+ params: {
85
+ data: {
86
+ text_lines:
87
+ [
88
+ $("#display_line_1").val(),
89
+ $("#display_line_2").val(),
90
+ ],
91
+ action: "display_lines",
92
+ }
93
+ }
94
+ }
95
+ _call_ajax("/hw_proxy/display_show", "display", data)
96
+ }
97
+
98
+ function test_display_product() {
99
+ const data = {
100
+ params: {
101
+ data: {
102
+ product_name: $("#display_product_name").val(),
103
+ quantity: $("#display_quantity").val(),
104
+ discount: $("#display_discount").val(),
105
+ unit_price: $("#display_unit_price").val(),
106
+ total: $("#display_total").val(),
107
+ action: $("#display_action").val(),
108
+ }
109
+ }
110
+ }
111
+ _call_ajax("/hw_proxy/display_show", "display", data)
112
+ }
113
+
114
+
115
+ </script>
116
+
117
+ {% endblock %}
@@ -0,0 +1,4 @@
1
+ USB_DEVICES = {
2
+ (0x0416, 0xF011): {"name": "Oxhoo - AF240", "image": "oxhoo__af240.png"},
3
+ (0x0416, 0xF012): {"name": "Aures - OCD 300", "image": "aures__ocd_300.png"},
4
+ }
@@ -0,0 +1,49 @@
1
+ [project]
2
+ name = "odoo-driver-plugin-display-escpos"
3
+ version = "1.0.4"
4
+ description = "Plugin for odoo-driver. Support escpos display devices (LCD)."
5
+ authors = [
6
+ {name = "Sylvain LE GAL", email = "sylvain.legal@grap.coop"},
7
+ ]
8
+ maintainers = [
9
+ {name = "Sylvain LE GAL", email = "sylvain.legal@grap.coop"},
10
+ ]
11
+ readme = "README.md"
12
+ license = "AGPLv3+"
13
+ keywords = ["Odoo", "Point Of Sale", "Driver", "Device", "Display", "LCD", "ESC/POS", "escpos", "Aures ®", "Bixolon ®", "Oxhoo ®"]
14
+ classifiers = [
15
+ "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
16
+ "Intended Audience :: Developers",
17
+ "Development Status :: 3 - Alpha",
18
+ "Operating System :: Unix",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
+ "Framework :: Odoo",
26
+ ]
27
+
28
+ requires-python = ">=3.9,<4.0"
29
+ dependencies = [
30
+ "odoo-driver",
31
+ ]
32
+
33
+
34
+ [project.urls]
35
+ repository = "https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos"
36
+
37
+ [project.entry-points.'odoo_driver.plugins']
38
+ display_escpos = 'odoo_driver_plugin_display_escpos'
39
+
40
+ [build-system]
41
+ requires = ["poetry-core"]
42
+ build-backend = "poetry.core.masonry.api"
43
+
44
+ [tool.black]
45
+ line-length = 88
46
+
47
+ [tool.isort]
48
+ profile = "black"
49
+ line_length = 88