From b7b8809ea8b7fe4ac8b24f43fe3f3a5e38a66696 Mon Sep 17 00:00:00 2001 From: lejeune quentin Date: Thu, 8 Sep 2022 14:19:31 +0000 Subject: [PATCH] [IMP] hw_*: Add possibility to run IoT in Windows Currently it is only possible to run the modules for the IoT Box on Raspios. The modifications brought by this commit brings the possibility that the various hw_* modules can be executed whatever the OS of the hardware (Linux or Windows). If the modules should be versioned according to the OS, the file will be renamed so that the end of the file includes *_L (for linux) or *_W (for windows) closes odoo/odoo#105938 Related: odoo/enterprise#34661 Signed-off-by: Quentin Lejeune (qle) --- addons/hw_drivers/connection_manager.py | 7 +- addons/hw_drivers/controllers/driver.py | 30 --- .../{DisplayDriver.py => DisplayDriver_L.py} | 0 ...ardUSBDriver.py => KeyboardUSBDriver_L.py} | 0 .../iot_handlers/drivers/L10nEGDrivers.py} | 0 .../{PrinterDriver.py => PrinterDriver_L.py} | 2 +- .../iot_handlers/drivers/PrinterDriver_W.py | 155 ++++++++++++++ ...playInterface.py => DisplayInterface_L.py} | 0 ...nterInterface.py => PrinterInterface_L.py} | 0 .../interfaces/PrinterInterface_W.py | 24 +++ .../interfaces/SerialInterface.py | 8 +- .../{USBInterface.py => USBInterface_L.py} | 0 addons/hw_drivers/main.py | 19 +- addons/hw_drivers/static/img/False.jpg | Bin 546 -> 0 bytes addons/hw_drivers/static/img/True.jpg | Bin 542 -> 0 bytes addons/hw_drivers/tools/helpers.py | 193 +++++++++++------- addons/hw_l10n_eg_eta/__init__.py | 1 - addons/hw_l10n_eg_eta/__manifest__.py | 32 --- addons/hw_l10n_eg_eta/controllers/__init__.py | 1 - addons/hw_posbox_homepage/controllers/main.py | 58 +++--- addons/hw_posbox_homepage/views/homepage.html | 22 +- .../{connect_to_server.sh => rename_iot.sh} | 18 +- .../tools/posbox/posbox_create_image.sh | 4 +- setup/win32/Makefile | 11 +- setup/win32/conf/nginx/nginx.conf | 19 ++ setup/win32/requirements-local-proxy.txt | 3 + setup/win32/setup.nsi | 89 +++++++- 27 files changed, 470 insertions(+), 226 deletions(-) rename addons/hw_drivers/iot_handlers/drivers/{DisplayDriver.py => DisplayDriver_L.py} (100%) rename addons/hw_drivers/iot_handlers/drivers/{KeyboardUSBDriver.py => KeyboardUSBDriver_L.py} (100%) rename addons/{hw_l10n_eg_eta/controllers/main.py => hw_drivers/iot_handlers/drivers/L10nEGDrivers.py} (100%) rename addons/hw_drivers/iot_handlers/drivers/{PrinterDriver.py => PrinterDriver_L.py} (99%) create mode 100644 addons/hw_drivers/iot_handlers/drivers/PrinterDriver_W.py rename addons/hw_drivers/iot_handlers/interfaces/{DisplayInterface.py => DisplayInterface_L.py} (100%) rename addons/hw_drivers/iot_handlers/interfaces/{PrinterInterface.py => PrinterInterface_L.py} (100%) create mode 100644 addons/hw_drivers/iot_handlers/interfaces/PrinterInterface_W.py rename addons/hw_drivers/iot_handlers/interfaces/{USBInterface.py => USBInterface_L.py} (100%) delete mode 100644 addons/hw_drivers/static/img/False.jpg delete mode 100644 addons/hw_drivers/static/img/True.jpg delete mode 100644 addons/hw_l10n_eg_eta/__init__.py delete mode 100644 addons/hw_l10n_eg_eta/__manifest__.py delete mode 100644 addons/hw_l10n_eg_eta/controllers/__init__.py rename addons/point_of_sale/tools/posbox/configuration/{connect_to_server.sh => rename_iot.sh} (72%) mode change 100755 => 100644 create mode 100644 setup/win32/conf/nginx/nginx.conf create mode 100644 setup/win32/requirements-local-proxy.txt diff --git a/addons/hw_drivers/connection_manager.py b/addons/hw_drivers/connection_manager.py index 1ae7fb5897177..24e1142bac860 100644 --- a/addons/hw_drivers/connection_manager.py +++ b/addons/hw_drivers/connection_manager.py @@ -54,15 +54,12 @@ def _connect_box(self): _logger.error('A error encountered : %s ' % e) def _connect_to_server(self, url, token, db_uuid, enterprise_code): - if db_uuid and enterprise_code: - helpers.add_credential(db_uuid, enterprise_code) - # Save DB URL and token - subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server.sh'), url, '', token, 'noreboot']) + helpers.save_conf_server(url, token, db_uuid, enterprise_code) # Notify the DB, so that the kanban view already shows the IoT Box manager.send_alldevices() # Restart to checkout the git branch, get a certificate, load the IoT handlers... - subprocess.check_call(["sudo", "service", "odoo", "restart"]) + helpers.odoo_restart(2) def _refresh_displays(self): """Refresh all displays to hide the pairing code""" diff --git a/addons/hw_drivers/controllers/driver.py b/addons/hw_drivers/controllers/driver.py index acc8eeaa474c8..42fa3a92a3914 100755 --- a/addons/hw_drivers/controllers/driver.py +++ b/addons/hw_drivers/controllers/driver.py @@ -65,36 +65,6 @@ def event(self, listener): req['result']['session_id'] = req['session_id'] return req['result'] - @http.route('/hw_drivers/box/connect', type='http', auth='none', cors='*', csrf=False, save_session=False) - def connect_box(self, token): - """ - This route is called when we want that a IoT Box will be connected to a Odoo DB - token is a base 64 encoded string and have 2 argument separate by | - 1 - url of odoo DB - 2 - token. This token will be compared to the token of Odoo. He have 1 hour lifetime - """ - server = helpers.get_odoo_server_url() - image = get_resource_path('hw_drivers', 'static/img', 'False.jpg') - if not server: - credential = b64decode(token).decode('utf-8').split('|') - url = credential[0] - token = credential[1] - if len(credential) > 2: - # IoT Box send token with db_uuid and enterprise_code only since V13 - db_uuid = credential[2] - enterprise_code = credential[3] - helpers.add_credential(db_uuid, enterprise_code) - try: - subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server.sh'), url, '', token, 'noreboot']) - manager.send_alldevices() - image = get_resource_path('hw_drivers', 'static/img', 'True.jpg') - helpers.odoo_restart(3) - except subprocess.CalledProcessError as e: - _logger.error('A error encountered : %s ' % e.output) - if os.path.isfile(image): - with open(image, 'rb') as f: - return f.read() - @http.route('/hw_drivers/download_logs', type='http', auth='none', cors='*', csrf=False, save_session=False) def download_logs(self): """ diff --git a/addons/hw_drivers/iot_handlers/drivers/DisplayDriver.py b/addons/hw_drivers/iot_handlers/drivers/DisplayDriver_L.py similarity index 100% rename from addons/hw_drivers/iot_handlers/drivers/DisplayDriver.py rename to addons/hw_drivers/iot_handlers/drivers/DisplayDriver_L.py diff --git a/addons/hw_drivers/iot_handlers/drivers/KeyboardUSBDriver.py b/addons/hw_drivers/iot_handlers/drivers/KeyboardUSBDriver_L.py similarity index 100% rename from addons/hw_drivers/iot_handlers/drivers/KeyboardUSBDriver.py rename to addons/hw_drivers/iot_handlers/drivers/KeyboardUSBDriver_L.py diff --git a/addons/hw_l10n_eg_eta/controllers/main.py b/addons/hw_drivers/iot_handlers/drivers/L10nEGDrivers.py similarity index 100% rename from addons/hw_l10n_eg_eta/controllers/main.py rename to addons/hw_drivers/iot_handlers/drivers/L10nEGDrivers.py diff --git a/addons/hw_drivers/iot_handlers/drivers/PrinterDriver.py b/addons/hw_drivers/iot_handlers/drivers/PrinterDriver_L.py similarity index 99% rename from addons/hw_drivers/iot_handlers/drivers/PrinterDriver.py rename to addons/hw_drivers/iot_handlers/drivers/PrinterDriver_L.py index 36d82b9aadd5e..1b9dfbeae7d0e 100644 --- a/addons/hw_drivers/iot_handlers/drivers/PrinterDriver.py +++ b/addons/hw_drivers/iot_handlers/drivers/PrinterDriver_L.py @@ -19,7 +19,7 @@ from odoo.addons.hw_drivers.controllers.proxy import proxy_drivers from odoo.addons.hw_drivers.driver import Driver from odoo.addons.hw_drivers.event_manager import event_manager -from odoo.addons.hw_drivers.iot_handlers.interfaces.PrinterInterface import PPDs, conn, cups_lock +from odoo.addons.hw_drivers.iot_handlers.interfaces.PrinterInterface_L import PPDs, conn, cups_lock from odoo.addons.hw_drivers.main import iot_devices from odoo.addons.hw_drivers.tools import helpers diff --git a/addons/hw_drivers/iot_handlers/drivers/PrinterDriver_W.py b/addons/hw_drivers/iot_handlers/drivers/PrinterDriver_W.py new file mode 100644 index 0000000000000..a9885788dc486 --- /dev/null +++ b/addons/hw_drivers/iot_handlers/drivers/PrinterDriver_W.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from PIL import Image, ImageOps +import logging +from base64 import b64decode +import io +import win32print +import ghostscript + +from odoo.addons.hw_drivers.controllers.proxy import proxy_drivers +from odoo.addons.hw_drivers.driver import Driver +from odoo.addons.hw_drivers.event_manager import event_manager +from odoo.addons.hw_drivers.main import iot_devices +from odoo.addons.hw_drivers.tools import helpers + +_logger = logging.getLogger(__name__) + +RECEIPT_PRINTER_COMMANDS = { + 'star': { + 'center': b'\x1b\x1d\x61\x01', # ESC GS a n + 'cut': b'\x1b\x64\x02', # ESC d n + 'title': b'\x1b\x69\x01\x01%s\x1b\x69\x00\x00', # ESC i n1 n2 + 'drawers': [b'\x07', b'\x1a'] # BEL & SUB + }, + 'escpos': { + 'center': b'\x1b\x61\x01', # ESC a n + 'cut': b'\x1d\x56\x41\n', # GS V m + 'title': b'\x1b\x21\x30%s\x1b\x21\x00', # ESC ! n + 'drawers': [b'\x1b\x3d\x01', b'\x1b\x70\x00\x19\x19', b'\x1b\x70\x01\x19\x19'] # ESC = n then ESC p m t1 t2 + } +} + +class PrinterDriver(Driver): + connection_type = 'printer' + + def __init__(self, identifier, device): + super().__init__(identifier, device) + self.device_type = 'printer' + self.device_connection = 'network' + self.device_name = device.get('identifier') + self.printer_handle = device.get('printer_handle') + self.state = { + 'status': 'connecting', + 'message': 'Connecting to printer', + 'reason': None, + } + self.send_status() + + self._actions.update({ + 'cashbox': self.open_cashbox, + 'print_receipt': self.print_receipt, + '': self._action_default, + }) + + self.receipt_protocol = 'escpos' + + @classmethod + def supported(cls, device): + return True + + @classmethod + def get_status(cls): + status = 'connected' if any(iot_devices[d].device_type == "printer" and iot_devices[d].device_connection == 'direct' for d in iot_devices) else 'disconnected' + return {'status': status, 'messages': ''} + + def disconnect(self): + self.update_status('disconnected', 'Printer was disconnected') + super(PrinterDriver, self).disconnect() + + def update_status(self, status, message, reason=None): + """Updates the state of the current printer. + + Args: + status (str): The new value of the status + message (str): A comprehensive message describing the status + reason (str): The reason fo the current status + """ + if self.state['status'] != status or self.state['reason'] != reason: + self.state = { + 'status': status, + 'message': message, + 'reason': reason, + } + self.send_status() + + def send_status(self): + """ Sends the current status of the printer to the connected Odoo instance. + """ + self.data = { + 'value': '', + 'state': self.state, + } + event_manager.device_changed(self) + + def print_raw(self, data): + win32print.StartDocPrinter(self.printer_handle, 1, ('', None, "RAW")) + win32print.StartPagePrinter(self.printer_handle) + win32print.WritePrinter(self.printer_handle, data) + win32print.EndPagePrinter(self.printer_handle) + win32print.EndDocPrinter(self.printer_handle) + + def print_report(self, data): + helpers.write_file('document.pdf', data, 'wb') + file_name = helpers.path_file('document.pdf') + printer = self.device_name + + args = [ + "-dPrinted", "-dBATCH", "-dNOSAFER", "-dNOPAUSE", "-dNOPROMPT" + "-q", + "-sDEVICE#mswinpr2", + f'-sOutputFile#%printer%{printer}', + f'{file_name}' + ] + + ghostscript.Ghostscript(*args) + + def print_receipt(self, data): + receipt = b64decode(data['receipt']) + im = Image.open(io.BytesIO(receipt)) + + # Convert to greyscale then to black and white + im = im.convert("L") + im = ImageOps.invert(im) + im = im.convert("1") + + print_command = getattr(self, 'format_%s' % self.receipt_protocol)(im) + self.print_raw(print_command) + + def format_escpos(self, im): + width = int((im.width + 7) / 8) + + raster_send = b'\x1d\x76\x30\x00' + max_slice_height = 255 + + raster_data = b'' + dots = im.tobytes() + while dots: + im_slice = dots[:width*max_slice_height] + slice_height = int(len(im_slice) / width) + raster_data += raster_send + width.to_bytes(2, 'little') + slice_height.to_bytes(2, 'little') + im_slice + dots = dots[width*max_slice_height:] + + return raster_data + RECEIPT_PRINTER_COMMANDS['escpos']['cut'] + + def open_cashbox(self, data): + """Sends a signal to the current printer to open the connected cashbox.""" + commands = RECEIPT_PRINTER_COMMANDS[self.receipt_protocol] + for drawer in commands['drawers']: + self.print_raw(drawer) + + def _action_default(self, data): + self.print_report(b64decode(data['document'])) + +proxy_drivers['printer'] = PrinterDriver diff --git a/addons/hw_drivers/iot_handlers/interfaces/DisplayInterface.py b/addons/hw_drivers/iot_handlers/interfaces/DisplayInterface_L.py similarity index 100% rename from addons/hw_drivers/iot_handlers/interfaces/DisplayInterface.py rename to addons/hw_drivers/iot_handlers/interfaces/DisplayInterface_L.py diff --git a/addons/hw_drivers/iot_handlers/interfaces/PrinterInterface.py b/addons/hw_drivers/iot_handlers/interfaces/PrinterInterface_L.py similarity index 100% rename from addons/hw_drivers/iot_handlers/interfaces/PrinterInterface.py rename to addons/hw_drivers/iot_handlers/interfaces/PrinterInterface_L.py diff --git a/addons/hw_drivers/iot_handlers/interfaces/PrinterInterface_W.py b/addons/hw_drivers/iot_handlers/interfaces/PrinterInterface_W.py new file mode 100644 index 0000000000000..c32c52d0f9c63 --- /dev/null +++ b/addons/hw_drivers/iot_handlers/interfaces/PrinterInterface_W.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import win32print + +from odoo.addons.hw_drivers.interface import Interface + +class PrinterInterface(Interface): + _loop_delay = 30 + connection_type = 'printer' + + def get_devices(self): + printer_devices = {} + printers = win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL) + + for printer in printers: + identifier = printer[2] + handle_printer = win32print.OpenPrinter(identifier) + win32print.GetPrinter(handle_printer, 2) + printer_devices[identifier] = { + 'identifier': identifier, + 'printer_handle': handle_printer, + } + return printer_devices diff --git a/addons/hw_drivers/iot_handlers/interfaces/SerialInterface.py b/addons/hw_drivers/iot_handlers/interfaces/SerialInterface.py index ae63969782f18..d51e50fcdc8cb 100644 --- a/addons/hw_drivers/iot_handlers/interfaces/SerialInterface.py +++ b/addons/hw_drivers/iot_handlers/interfaces/SerialInterface.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from glob import glob +import serial.tools.list_ports from odoo.addons.hw_drivers.interface import Interface @@ -11,8 +11,8 @@ class SerialInterface(Interface): def get_devices(self): serial_devices = {} - for identifier in glob('/dev/serial/by-path/*'): - serial_devices[identifier] = { - 'identifier': identifier + for port in serial.tools.list_ports.comports(): + serial_devices[port.device] = { + 'identifier': port.device } return serial_devices diff --git a/addons/hw_drivers/iot_handlers/interfaces/USBInterface.py b/addons/hw_drivers/iot_handlers/interfaces/USBInterface_L.py similarity index 100% rename from addons/hw_drivers/iot_handlers/interfaces/USBInterface.py rename to addons/hw_drivers/iot_handlers/interfaces/USBInterface_L.py diff --git a/addons/hw_drivers/main.py b/addons/hw_drivers/main.py index 1503aa0ea99ce..8e4c46969c458 100644 --- a/addons/hw_drivers/main.py +++ b/addons/hw_drivers/main.py @@ -1,9 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from traceback import format_exc - -from dbus.mainloop.glib import DBusGMainLoop import json +import platform import logging import socket from threading import Thread @@ -14,6 +13,12 @@ _logger = logging.getLogger(__name__) +try: + from dbus.mainloop.glib import DBusGMainLoop +except ImportError: + DBusGMainLoop = None + _logger.error('Could not import library dbus') + drivers = [] interfaces = {} iot_devices = {} @@ -72,7 +77,9 @@ def run(self): Thread that will load interfaces and drivers and contact the odoo server with the updates """ - helpers.check_git_branch() + helpers.start_nginx_server() + if platform.system() == 'Linux': + helpers.check_git_branch() helpers.check_certificate() # We first add the IoT Box to the connected DB because IoT handlers cannot be downloaded if @@ -93,16 +100,16 @@ def run(self): while 1: try: if iot_devices != self.previous_iot_devices: - self.send_alldevices() self.previous_iot_devices = iot_devices.copy() + self.send_alldevices() time.sleep(3) except Exception: # No matter what goes wrong, the Manager loop needs to keep running _logger.error(format_exc()) - # Must be started from main thread -DBusGMainLoop(set_as_default=True) +if DBusGMainLoop: + DBusGMainLoop(set_as_default=True) manager = Manager() manager.daemon = True diff --git a/addons/hw_drivers/static/img/False.jpg b/addons/hw_drivers/static/img/False.jpg deleted file mode 100644 index 8f8e6489b30a389e475551b03ade16dc9257cfe1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 546 zcmb78u^izoN#@|~P>({p-c^|Kg1Yq_h>i>!(ND?`Gh2UGSyM46ADv=e=9x0 zYXk&I9>SCKl%5c|KfX)zI{exqy~0X@aMA(vFaf#^=ziKy8PseiB}NV4{{|#cMf(8p l1?-GyRhx*N09D7MZEr|E)7{l4_Xk?JL#;Nzo=^r}J^_c1N>Ts- diff --git a/addons/hw_drivers/static/img/True.jpg b/addons/hw_drivers/static/img/True.jpg deleted file mode 100644 index 0488574a699b8dcf2e9cdd48ac8896aa339d3805..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 542 zcmb7=sE2fY_bi*omlvb)J zlqt)ecE&kttgY&Suj)`+>qkBeV@k;#9F9kEJcucFpO9ovq54YoV`Z!OZ{-IB4Fe-7 zKm?M3@*@)W$9HL7$6s6I7dS}}UV4D;kIN48Kg|ten$4udqyxg=fFx>Z7a+cXtr5L; d6R{Pb_Gt3i8j_FncgsDU{Ju^bU`Hea&mS2iNcjK& diff --git a/addons/hw_drivers/tools/helpers.py b/addons/hw_drivers/tools/helpers.py index 5494bd0fe9cbd..7b11d62f71793 100644 --- a/addons/hw_drivers/tools/helpers.py +++ b/addons/hw_drivers/tools/helpers.py @@ -3,6 +3,7 @@ import datetime from importlib import util +import platform import io import json import logging @@ -15,8 +16,9 @@ import zipfile from threading import Thread import time +import contextlib -from odoo import _, http +from odoo import _, http, service from odoo.tools.func import lazy_property from odoo.modules.module import get_resource_path @@ -36,14 +38,36 @@ def __init__(self, delay): def run(self): time.sleep(self.delay) - subprocess.check_call(["sudo", "service", "odoo", "restart"]) + service.server.restart() + + +if platform.system() == 'Windows': + writable = contextlib.nullcontext +elif platform.system() == 'Linux': + @contextlib.contextmanager + def writable(): + subprocess.call(["sudo", "mount", "-o", "remount,rw", "/"]) + subprocess.call(["sudo", "mount", "-o", "remount,rw", "/root_bypass_ramdisks/"]) + try: + yield + finally: + subprocess.call(["sudo", "mount", "-o", "remount,ro", "/"]) + subprocess.call(["sudo", "mount", "-o", "remount,ro", "/root_bypass_ramdisks/"]) + subprocess.call(["sudo", "mount", "-o", "remount,rw", "/root_bypass_ramdisks/etc/cups"]) def access_point(): return get_ip() == '10.11.12.1' -def add_credential(db_uuid, enterprise_code): - write_file('odoo-db-uuid.conf', db_uuid) - write_file('odoo-enterprise-code.conf', enterprise_code) +def start_nginx_server(): + if platform.system() == 'Windows': + path_nginx = get_path_nginx() + if path_nginx: + os.chdir(path_nginx) + _logger.info('Start Nginx server: %s\\nginx.exe', path_nginx) + os.popen('nginx.exe') + os.chdir('..\\server') + elif platform.system() == 'Linux': + subprocess.check_call(["sudo", "service", "nginx", "restart"]) def check_certificate(): """ @@ -51,7 +75,10 @@ def check_certificate(): """ server = get_odoo_server_url() if server: - path = Path('/etc/ssl/certs/nginx-cert.crt') + if platform.system() == 'Windows': + path = Path(get_path_nginx()).joinpath('conf/nginx-cert.crt') + elif platform.system() == 'Linux': + path = Path('/etc/ssl/certs/nginx-cert.crt') if path.exists(): with path.open('r') as f: cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read()) @@ -94,14 +121,12 @@ def check_git_branch(): local_branch = subprocess.check_output(git + ['symbolic-ref', '-q', '--short', 'HEAD']).decode('utf-8').rstrip() if db_branch != local_branch: - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/"]) - subprocess.check_call(["rm", "-rf", "/home/pi/odoo/addons/hw_drivers/iot_handlers/drivers/*"]) - subprocess.check_call(["rm", "-rf", "/home/pi/odoo/addons/hw_drivers/iot_handlers/interfaces/*"]) - subprocess.check_call(git + ['branch', '-m', db_branch]) - subprocess.check_call(git + ['remote', 'set-branches', 'origin', db_branch]) - os.system('/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/posbox_update.sh') - subprocess.call(["sudo", "mount", "-o", "remount,ro", "/"]) - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/root_bypass_ramdisks/etc/cups"]) + with writable(): + subprocess.check_call(["rm", "-rf", "/home/pi/odoo/addons/hw_drivers/iot_handlers/drivers/*"]) + subprocess.check_call(["rm", "-rf", "/home/pi/odoo/addons/hw_drivers/iot_handlers/interfaces/*"]) + subprocess.check_call(git + ['branch', '-m', db_branch]) + subprocess.check_call(git + ['remote', 'set-branches', 'origin', db_branch]) + os.system('/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/posbox_update.sh') except Exception as e: _logger.error('Could not reach configured server') @@ -130,39 +155,37 @@ def check_image(): version = checkFile.get(valueLastest, 'Error').replace('iotboxv', '').replace('.zip', '').split('_') return {'major': version[0], 'minor': version[1]} +def save_conf_server(url, token, db_uuid, enterprise_code): + """ + Save config to connect IoT to the server + """ + write_file('odoo-remote-server.conf', url) + write_file('token', token) + write_file('odoo-db-uuid.conf', db_uuid or '') + write_file('odoo-enterprise-code.conf', enterprise_code or '') + def get_img_name(): major, minor = get_version().split('.') return 'iotboxv%s_%s.zip' % (major, minor) def get_ip(): - while True: - try: - return netifaces.ifaddresses('eth0')[netifaces.AF_INET][0]['addr'] - except KeyError: - pass - - try: - return netifaces.ifaddresses('wlan0')[netifaces.AF_INET][0]['addr'] - except KeyError: - pass - - _logger.warning("Couldn't get IP, sleeping and retrying.") - time.sleep(5) + interfaces = netifaces.interfaces() + for interface in interfaces: + if netifaces.ifaddresses(interface).get(netifaces.AF_INET): + addr = netifaces.ifaddresses(interface).get(netifaces.AF_INET)[0]['addr'] + if addr != '127.0.0.1': + return addr def get_mac_address(): - while True: - try: - return netifaces.ifaddresses('eth0')[netifaces.AF_LINK][0]['addr'] - except KeyError: - pass + interfaces = netifaces.interfaces() + for interface in interfaces: + if netifaces.ifaddresses(interface).get(netifaces.AF_INET): + addr = netifaces.ifaddresses(interface).get(netifaces.AF_LINK)[0]['addr'] + if addr != '00:00:00:00:00:00': + return addr - try: - return netifaces.ifaddresses('wlan0')[netifaces.AF_LINK][0]['addr'] - except KeyError: - pass - - _logger.warning("Couldn't get MAC address, sleeping and retrying.") - time.sleep(5) +def get_path_nginx(): + return str(list(Path().absolute().parent.glob('*nginx*'))[0]) def get_ssid(): ap = subprocess.call(['systemctl', 'is-active', '--quiet', 'hostapd']) # if service is active return 0 else inactive @@ -173,16 +196,20 @@ def get_ssid(): return subprocess.check_output(['sed', 's/.*"\\(.*\\)"/\\1/'], stdin=process_grep.stdout).decode('utf-8').rstrip() def get_odoo_server_url(): - ap = subprocess.call(['systemctl', 'is-active', '--quiet', 'hostapd']) # if service is active return 0 else inactive - if not ap: - return False + if platform.system() == 'Linux': + ap = subprocess.call(['systemctl', 'is-active', '--quiet', 'hostapd']) # if service is active return 0 else inactive + if not ap: + return False return read_file_first_line('odoo-remote-server.conf') def get_token(): return read_file_first_line('token') def get_version(): - return subprocess.check_output(['cat', '/var/odoo/iotbox_version']).decode().rstrip() + if platform.system() == 'Linux': + return read_file_first_line('/var/odoo/iotbox_version') + elif platform.system() == 'Windows': + return 'W22_11' def get_wifi_essid(): wifi_options = [] @@ -219,16 +246,17 @@ def load_certificate(): result = json.loads(response.data.decode('utf8'))['result'] if result: write_file('odoo-subject.conf', result['subject_cn']) - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/"]) - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/root_bypass_ramdisks/"]) - Path('/etc/ssl/certs/nginx-cert.crt').write_text(result['x509_pem']) - Path('/root_bypass_ramdisks/etc/ssl/certs/nginx-cert.crt').write_text(result['x509_pem']) - Path('/etc/ssl/private/nginx-cert.key').write_text(result['private_key_pem']) - Path('/root_bypass_ramdisks/etc/ssl/private/nginx-cert.key').write_text(result['private_key_pem']) - subprocess.call(["sudo", "mount", "-o", "remount,ro", "/"]) - subprocess.call(["sudo", "mount", "-o", "remount,ro", "/root_bypass_ramdisks/"]) - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/root_bypass_ramdisks/etc/cups"]) - subprocess.check_call(["sudo", "service", "nginx", "restart"]) + if platform.system() == 'Linux': + with writable(): + Path('/etc/ssl/certs/nginx-cert.crt').write_text(result['x509_pem']) + Path('/root_bypass_ramdisks/etc/ssl/certs/nginx-cert.crt').write_text(result['x509_pem']) + Path('/etc/ssl/private/nginx-cert.key').write_text(result['private_key_pem']) + Path('/root_bypass_ramdisks/etc/ssl/private/nginx-cert.key').write_text(result['private_key_pem']) + elif platform.system() == 'Windows': + Path(get_path_nginx()).joinpath('conf/nginx-cert.crt').write_text(result['x509_pem']) + Path(get_path_nginx()).joinpath('conf/nginx-cert.key').write_text(result['private_key_pem']) + time.sleep(3) + start_nginx_server() def download_iot_handlers(auto=True): """ @@ -240,14 +268,13 @@ def download_iot_handlers(auto=True): pm = urllib3.PoolManager(cert_reqs='CERT_NONE') server = server + '/iot/get_handlers' try: - resp = pm.request('POST', server, fields={'mac': get_mac_address(), 'auto': auto}) + resp = pm.request('POST', server, fields={'mac': get_mac_address(), 'auto': auto}, timeout=8) if resp.data: - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/"]) - drivers_path = Path.home() / 'odoo/addons/hw_drivers/iot_handlers' - zip_file = zipfile.ZipFile(io.BytesIO(resp.data)) - zip_file.extractall(drivers_path) - subprocess.call(["sudo", "mount", "-o", "remount,ro", "/"]) - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/root_bypass_ramdisks/etc/cups"]) + with writable(): + drivers_path = ['odoo', 'addons', 'hw_drivers', 'iot_handlers'] + path = path_file(str(Path().joinpath(*drivers_path))) + zip_file = zipfile.ZipFile(io.BytesIO(resp.data)) + zip_file.extractall(path) except Exception as e: _logger.error('Could not reach configured server') _logger.error('A error encountered : %s ' % e) @@ -260,38 +287,46 @@ def load_iot_handlers(): """ for directory in ['interfaces', 'drivers']: path = get_resource_path('hw_drivers', 'iot_handlers', directory) - filesList = os.listdir(path) + filesList = list_file_by_os(path) for file in filesList: - path_file = os.path.join(path, file) - spec = util.spec_from_file_location(file, path_file) + spec = util.spec_from_file_location(file, str(Path(path).joinpath(file))) if spec: module = util.module_from_spec(spec) spec.loader.exec_module(module) lazy_property.reset_all(http.root) +def list_file_by_os(file_list): + platform_os = platform.system() + if platform_os == 'Linux': + return [x.name for x in Path(file_list).glob('*[!W].*')] + elif platform_os == 'Windows': + return [x.name for x in Path(file_list).glob('*[!L].*')] + def odoo_restart(delay): IR = IoTRestart(delay) IR.start() +def path_file(filename): + platform_os = platform.system() + if platform_os == 'Linux': + return Path().absolute().parent.joinpath(filename) + elif platform_os == 'Windows': + return Path().absolute().parent.joinpath('server/' + filename) + def read_file_first_line(filename): - path = Path.home() / filename - path = Path('/home/pi/' + filename) + path = path_file(filename) if path.exists(): with path.open('r') as f: return f.readline().strip('\n') - return '' def unlink_file(filename): - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/"]) - path = Path.home() / filename - if path.exists(): - path.unlink() - subprocess.call(["sudo", "mount", "-o", "remount,ro", "/"]) - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/root_bypass_ramdisks/etc/cups"]) - -def write_file(filename, text): - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/"]) - path = Path.home() / filename - path.write_text(text) - subprocess.call(["sudo", "mount", "-o", "remount,ro", "/"]) - subprocess.call(["sudo", "mount", "-o", "remount,rw", "/root_bypass_ramdisks/etc/cups"]) + with writable(): + path = path_file(filename) + if path.exists(): + path.unlink() + +def write_file(filename, text, mode='w'): + with writable(): + path = path_file(filename) + with open(path, mode) as f: + f.write(text) diff --git a/addons/hw_l10n_eg_eta/__init__.py b/addons/hw_l10n_eg_eta/__init__.py deleted file mode 100644 index e046e49fbe22a..0000000000000 --- a/addons/hw_l10n_eg_eta/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import controllers diff --git a/addons/hw_l10n_eg_eta/__manifest__.py b/addons/hw_l10n_eg_eta/__manifest__.py deleted file mode 100644 index d9c1ac599784a..0000000000000 --- a/addons/hw_l10n_eg_eta/__manifest__.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -{ - 'name': 'Egypt ETA Hardware Driver', - 'category': 'Accounting/Accounting', - 'website': 'https://www.odoo.com', - 'summary': 'Egypt ETA Hardware Driver', - 'description': """ -Egypt ETA Hardware Driver -======================= - -This module allows Odoo to digitally sign invoices using an USB key approved by the egyptian government - -Special thanks to Plementus for their help in developing this module. - -Requirements per system ------------------------ - -Windows: - - eps2003csp11.dll - -Linux/macOS: - - OpenSC - -""", - 'external_dependencies': { - 'python': ['PyKCS11'], - }, - 'installable': False, - 'license': 'LGPL-3', -} diff --git a/addons/hw_l10n_eg_eta/controllers/__init__.py b/addons/hw_l10n_eg_eta/controllers/__init__.py deleted file mode 100644 index 12a7e529b6741..0000000000000 --- a/addons/hw_l10n_eg_eta/controllers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import main diff --git a/addons/hw_posbox_homepage/controllers/main.py b/addons/hw_posbox_homepage/controllers/main.py index f5571ac5e1a4d..9df22d675a93d 100644 --- a/addons/hw_posbox_homepage/controllers/main.py +++ b/addons/hw_posbox_homepage/controllers/main.py @@ -1,7 +1,9 @@ +# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import json import jinja2 +import platform import logging import os from pathlib import Path @@ -10,10 +12,10 @@ import sys import threading -from odoo import http +from odoo import http, service from odoo.http import Response from odoo.modules.module import get_resource_path - +from odoo.addons.hw_drivers.connection_manager import connection_manager from odoo.addons.hw_drivers.main import iot_devices from odoo.addons.hw_drivers.tools import helpers from odoo.addons.web.controllers.home import Home @@ -59,8 +61,11 @@ def get_six_terminal(self): def get_homepage_data(self): hostname = str(socket.gethostname()) - ssid = helpers.get_ssid() - wired = subprocess.check_output(['cat', '/sys/class/net/eth0/operstate']).decode('utf-8').strip('\n') + if platform.system() == 'Linux': + ssid = helpers.get_ssid() + wired = helpers.read_file_first_line('/sys/class/net/eth0/operstate') + else: + wired = 'up' if wired == 'up': network = 'Ethernet' elif ssid: @@ -85,9 +90,11 @@ def get_homepage_data(self): 'mac': helpers.get_mac_address(), 'iot_device_status': iot_device, 'server_status': helpers.get_odoo_server_url() or 'Not Configured', + 'pairing_code': connection_manager.pairing_code, 'six_terminal': self.get_six_terminal(), 'network_status': network, 'version': helpers.get_version(), + 'system': platform.system(), } @http.route('/', type='http', auth='none') @@ -101,14 +108,8 @@ def index(self): @http.route('/list_handlers', type='http', auth='none', website=True) def list_handlers(self): - drivers_list = [] - for driver in os.listdir(get_resource_path('hw_drivers', 'iot_handlers/drivers')): - if driver != '__pycache__': - drivers_list.append(driver) - interfaces_list = [] - for interface in os.listdir(get_resource_path('hw_drivers', 'iot_handlers/interfaces')): - if interface != '__pycache__': - interfaces_list.append(interface) + drivers_list = helpers.list_file_by_os(get_resource_path('hw_drivers', 'iot_handlers', 'drivers')) + interfaces_list = helpers.list_file_by_os(get_resource_path('hw_drivers', 'iot_handlers', 'interfaces')) return handler_list_template.render({ 'title': "Odoo's IoT Box - Handlers list", 'breadcrumb': 'Handlers list', @@ -120,7 +121,7 @@ def list_handlers(self): @http.route('/load_iot_handlers', type='http', auth='none', website=True) def load_iot_handlers(self): helpers.download_iot_handlers(False) - subprocess.check_call(["sudo", "service", "odoo", "restart"]) + helpers.odoo_restart(0) return "" @http.route('/list_credential', type='http', auth='none', website=True) @@ -134,15 +135,16 @@ def list_credential(self): @http.route('/save_credential', type='http', auth='none', cors='*', csrf=False) def save_credential(self, db_uuid, enterprise_code): - helpers.add_credential(db_uuid, enterprise_code) - subprocess.check_call(["sudo", "service", "odoo", "restart"]) + helpers.write_file('odoo-db-uuid.conf', db_uuid) + helpers.write_file('odoo-enterprise-code.conf', enterprise_code) + helpers.odoo_restart(0) return "" @http.route('/clear_credential', type='http', auth='none', cors='*', csrf=False) def clear_credential(self): helpers.unlink_file('odoo-db-uuid.conf') helpers.unlink_file('odoo-enterprise-code.conf') - subprocess.check_call(["sudo", "service", "odoo", "restart"]) + helpers.odoo_restart(0) return "" @http.route('/wifi', type='http', auth='none', website=True) @@ -192,9 +194,9 @@ def clear_server_configuration(self): @http.route('/handlers_clear', type='http', auth='none', cors='*', csrf=False) def clear_handlers_list(self): for directory in ['drivers', 'interfaces']: - for file in os.listdir(get_resource_path('hw_drivers', 'iot_handlers', directory)): - if file != '__pycache__': - helpers.unlink_file(get_resource_path('hw_drivers', 'iot_handlers', directory, file)) + for file in list(Path(get_resource_path('hw_drivers', 'iot_handlers', directory)).glob('*')): + if file.name != '__pycache__': + helpers.unlink_file(str(file.relative_to(*file.parts[:3]))) return "" @http.route('/server_connect', type='http', auth='none', cors='*', csrf=False) @@ -203,16 +205,16 @@ def connect_to_server(self, token, iotname): credential = token.split('|') url = credential[0] token = credential[1] - if len(credential) > 2: - # IoT Box send token with db_uuid and enterprise_code only since V13 - db_uuid = credential[2] - enterprise_code = credential[3] - helpers.add_credential(db_uuid, enterprise_code) + db_uuid = credential[2] + enterprise_code = credential[3] + helpers.save_conf_server(url, token, db_uuid, enterprise_code) else: url = helpers.get_odoo_server_url() token = helpers.get_token() - reboot = 'reboot' - subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server.sh'), url, iotname, token, reboot]) + if iotname: + subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/rename_iot.sh'), iotname]) + else: + helpers.odoo_restart(3) return 'http://' + helpers.get_ip() + ':8069' @http.route('/steps', type='http', auth='none', cors='*', csrf=False) @@ -280,13 +282,13 @@ def six_payment_terminal(self): @http.route('/six_payment_terminal_add', type='http', auth='none', cors='*', csrf=False) def add_six_payment_terminal(self, terminal_id): helpers.write_file('odoo-six-payment-terminal.conf', terminal_id) - subprocess.check_call(["sudo", "service", "odoo", "restart"]) + service.server.restart() return 'http://' + helpers.get_ip() + ':8069' @http.route('/six_payment_terminal_clear', type='http', auth='none', cors='*', csrf=False) def clear_six_payment_terminal(self): helpers.unlink_file('odoo-six-payment-terminal.conf') - subprocess.check_call(["sudo", "service", "odoo", "restart"]) + service.server.restart() return "" @http.route('/hw_proxy/upgrade', type='http', auth='none', ) diff --git a/addons/hw_posbox_homepage/views/homepage.html b/addons/hw_posbox_homepage/views/homepage.html index d116b5cd4ded8..876b7ee3ed66e 100644 --- a/addons/hw_posbox_homepage/views/homepage.html +++ b/addons/hw_posbox_homepage/views/homepage.html @@ -155,7 +155,9 @@
Restart
- + {% if system == "Linux" %} + + {% endif %}
@@ -165,11 +167,11 @@

Your IoT Box is up and running

- + - + @@ -181,7 +183,7 @@

Your IoT Box is up and running

- + @@ -193,6 +195,12 @@

Your IoT Box is up and running

{% endif %} + {% if pairing_code %} + + + + + {% endif %}
Name {{ hostname }} configure {{ hostname }} {% if system == "Linux" %}configure{% endif %}
Version {{ version }} update {{ version }} {% if system == "Linux" %}update{% endif %}
IP Address
Network{{ network_status }} configure wifi{{ network_status }} {% if system == "Linux" %}configure wifi{% endif %}
Server {{ six_terminal }} configure
Pairing code{{ pairing_code }}
IOT Device @@ -220,8 +228,10 @@

Your IoT Box is up and running

POS Display - Remote Debug - Printers server + {% if system == "Linux" %} + Remote Debug + Printers server + {% endif %} {% if server_status != "Not Configured" %} Credential {% endif %} diff --git a/addons/point_of_sale/tools/posbox/configuration/connect_to_server.sh b/addons/point_of_sale/tools/posbox/configuration/rename_iot.sh old mode 100755 new mode 100644 similarity index 72% rename from addons/point_of_sale/tools/posbox/configuration/connect_to_server.sh rename to addons/point_of_sale/tools/posbox/configuration/rename_iot.sh index 3c6e8be534f2e..eeec51f28ad3d --- a/addons/point_of_sale/tools/posbox/configuration/connect_to_server.sh +++ b/addons/point_of_sale/tools/posbox/configuration/rename_iot.sh @@ -2,15 +2,10 @@ # Write the server configuration function connect () { - SERVER="${1}" - CURRENT_SERVER_FILE=/home/pi/odoo-remote-server.conf - TOKEN_FILE=/home/pi/token - TOKEN="${3}" - REBOOT="${4}" HOSTS=/root_bypass_ramdisks/etc/hosts HOST_FILE=/root_bypass_ramdisks/etc/hostname HOSTNAME="$(hostname)" - IOT_NAME="${2}" + IOT_NAME="${1}" IOT_NAME="${IOT_NAME//[^[:ascii:]]/}" IOT_NAME="${IOT_NAME//[^a-zA-Z0-9-]/}" if [ -z "$IOT_NAME" ] @@ -19,11 +14,6 @@ function connect () { fi sudo mount -o remount,rw / sudo mount -o remount,rw /root_bypass_ramdisks - if [ ! -z "${1}" ] - then - echo "${SERVER}" > ${CURRENT_SERVER_FILE} - echo "${TOKEN}" > ${TOKEN_FILE} - fi if [ "${IOT_NAME}" != "${HOSTNAME}" ] then sudo sed -i "s/${HOSTNAME}/${IOT_NAME}/g" ${HOSTS} @@ -39,10 +29,6 @@ function connect () { fi sudo mount -o remount,ro / sudo mount -o remount,ro /root_bypass_ramdisks - if [ "$REBOOT" == 'reboot' ] - then - sudo service odoo restart - fi } -connect "${1}" "${2}" "${3}" "${4}" \ No newline at end of file +connect "${1}" diff --git a/addons/point_of_sale/tools/posbox/posbox_create_image.sh b/addons/point_of_sale/tools/posbox/posbox_create_image.sh index 565e347ba0421..2c4c746b4f26d 100755 --- a/addons/point_of_sale/tools/posbox/posbox_create_image.sh +++ b/addons/point_of_sale/tools/posbox/posbox_create_image.sh @@ -28,8 +28,8 @@ __base="$(basename ${__file} .sh)" MOUNT_POINT="${__dir}/root_mount" OVERWRITE_FILES_BEFORE_INIT_DIR="${__dir}/overwrite_before_init" OVERWRITE_FILES_AFTER_INIT_DIR="${__dir}/overwrite_after_init" -VERSION=15.0 -VERSION_IOTBOX=21.10 +VERSION=16.0 +VERSION_IOTBOX=22.11 REPO=https://github.com/odoo/odoo.git if ! file_exists *raspios*.img ; then diff --git a/setup/win32/Makefile b/setup/win32/Makefile index b1c1e6d6d5804..526b3555f7569 100644 --- a/setup/win32/Makefile +++ b/setup/win32/Makefile @@ -26,15 +26,18 @@ server_clean: rm -rf $(SERVER_DIRECTORY)/.cyg* allinone: server_clean +# need to install requirements-local-proxy.txt for local proxy install cp $(SERVER_DIRECTORY)/requirements.txt $(WINPY32_DIR)/ + cp requirements-local-proxy.txt $(WINPY32_DIR)/ -(cd $(WINPY32_DIR) && ./python.exe -m pip install --upgrade pip) - -(cd $(WINPY32_DIR) && cat requirements.txt | while read PAC ; do Scripts/pip3.exe install "$${PAC%%#*}" ; done) - -(cd $(WINPY32_DIR) && Scripts/pip3.exe freeze) + -(cd $(WINPY32_DIR) && cat requirements*.txt | while read PAC ; do Scripts/pip3.exe install "$${PAC%%#*}" ; done) + -(cd $(WINPY32_DIR) && Scripts/pip3.exe list) rm $(WINPY32_DIR)/requirements.txt cp $(SERVER_DIRECTORY)/requirements.txt $(WINPY64_DIR)/ + cp requirements-local-proxy.txt $(WINPY64_DIR)/ -(cd $(WINPY64_DIR) && ./python.exe -m pip install --upgrade pip) - -(cd $(WINPY64_DIR) && cat requirements.txt | while read PAC ; do Scripts/pip3.exe install "$${PAC%%#*}" ; done) - -(cd $(WINPY64_DIR) && Scripts/pip3.exe freeze) + -(cd $(WINPY64_DIR) && cat requirements*.txt | while read PAC ; do Scripts/pip3.exe install "$${PAC%%#*}" ; done) + -(cd $(WINPY64_DIR) && Scripts/pip3.exe list) rm $(WINPY64_DIR)/requirements.txt (cd $(SERVER_DIRECTORY)/setup/win32 && $(LAUNCH_MAKENSIS)) (cd $(SERVER_DIRECTORY)/setup/win32 && mkdir -p $(FILES_DIRECTORY)) diff --git a/setup/win32/conf/nginx/nginx.conf b/setup/win32/conf/nginx/nginx.conf new file mode 100644 index 0000000000000..a039c8192264a --- /dev/null +++ b/setup/win32/conf/nginx/nginx.conf @@ -0,0 +1,19 @@ +events { + worker_connections 1024; +} + +http { + server { + listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; + server_name localhost; + ssl_certificate nginx-cert.crt; + ssl_certificate_key nginx-cert.key; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!MD5; + location / { + proxy_read_timeout 600s; + proxy_pass http://127.0.0.1:8069; + } + } +} diff --git a/setup/win32/requirements-local-proxy.txt b/setup/win32/requirements-local-proxy.txt new file mode 100644 index 0000000000000..7f8cf902ce7e5 --- /dev/null +++ b/setup/win32/requirements-local-proxy.txt @@ -0,0 +1,3 @@ +netifaces==0.11.0 +PyKCS11==1.5.10 +ghostscript==0.7 diff --git a/setup/win32/setup.nsi b/setup/win32/setup.nsi index 13dc3ba941771..20c2d93905c19 100644 --- a/setup/win32/setup.nsi +++ b/setup/win32/setup.nsi @@ -174,10 +174,12 @@ LangString DESC_PostgreSQL_Username ${LANG_ENGLISH} "Username" LangString DESC_PostgreSQL_Password ${LANG_ENGLISH} "Password" LangString Profile_AllInOne ${LANG_ENGLISH} "Odoo Server And PostgreSQL Server" LangString Profile_Server ${LANG_ENGLISH} "Odoo Server Only" -LangString Profile_LocalProxyMode ${LANG_ENGLISH} "Local Proxy Mode" +LangString Profile_IOT ${LANG_ENGLISH} "Odoo IoT" LangString TITLE_Odoo_Server ${LANG_ENGLISH} "Odoo Server" LangString TITLE_PostgreSQL ${LANG_ENGLISH} "PostgreSQL Database" -LangString TITLE_LocalProxyMode ${LANG_ENGLISH} "Local Proxy Mode" +LangString TITLE_IOT ${LANG_ENGLISH} "Odoo IoT" +LangString TITLE_Nginx ${LANG_ENGLISH} "Nginx WebServer" +LangString TITLE_Ghostscript ${LANG_ENGLISH} "Ghostscript interpreter" LangString DESC_FinishPageText ${LANG_ENGLISH} "Start Odoo" ; French @@ -197,16 +199,18 @@ LangString DESC_PostgreSQL_Username ${LANG_FRENCH} "Utilisateur" LangString DESC_PostgreSQL_Password ${LANG_FRENCH} "Mot de passe" LangString Profile_AllInOne ${LANG_FRENCH} "Serveur Odoo Et Serveur PostgreSQL" LangString Profile_Server ${LANG_FRENCH} "Seulement Le Serveur Odoo" -LangString Profile_LocalProxyMode ${LANG_FRENCH} "Mode Proxy Local" +LangString Profile_IOT ${LANG_FRENCH} "Odoo IoT" LangString TITLE_Odoo_Server ${LANG_FRENCH} "Serveur Odoo" LangString TITLE_PostgreSQL ${LANG_FRENCH} "Installation du serveur de base de données PostgreSQL" -LangString TITLE_LocalProxyMode ${LANG_FRENCH} "Mode Proxy Local" +LangString TITLE_IOT ${LANG_FRENCH} "Odoo IoT" +LangString TITLE_Nginx ${LANG_FRENCH} "Installation du serveur web Nginx" +LangString TITLE_Ghostscript ${LANG_FRENCH} "Installation de l'interpréteur Ghostscript" LangString DESC_FinishPageText ${LANG_FRENCH} "Démarrer Odoo" InstType /NOCUSTOM InstType $(Profile_AllInOne) InstType $(Profile_Server) -InstType $(Profile_LocalProxyMode) +InstType $(Profile_IOT) Section $(TITLE_Odoo_Server) SectionOdoo_Server SectionIn 1 2 3 @@ -247,6 +251,8 @@ Section $(TITLE_Odoo_Server) SectionOdoo_Server # Fix the addons path WriteIniStr "$INSTDIR\server\odoo.conf" "options" "addons_path" "$INSTDIR\server\odoo\addons" WriteIniStr "$INSTDIR\server\odoo.conf" "options" "bin_path" "$INSTDIR\thirdparty" + # Set data_dir + WriteIniStr "$INSTDIR\server\odoo.conf" "options" "data_dir" "$INSTDIR\sessions" # if we're going to install postgresql force it's path, # otherwise we consider it's always done and/or correctly tune by users @@ -256,17 +262,18 @@ Section $(TITLE_Odoo_Server) SectionOdoo_Server # Productivity Apps WriteIniStr "$INSTDIR\server\odoo.conf" "options" "default_productivity_apps" "True" - DetailPrint "Installing Windows service" nsExec::ExecTOLog '"$INSTDIR\python\python.exe" "$INSTDIR\server\odoo-bin" --stop-after-init --logfile "$INSTDIR\server\odoo.log" -s' ${If} ${RunningX64} nsExec::ExecToLog '"$INSTDIR\nssm\win64\nssm.exe" install ${SERVICENAME} "$INSTDIR\python\python.exe"' nsExec::ExecToLog '"$INSTDIR\nssm\win64\nssm.exe" set ${SERVICENAME} AppDirectory "$\"$INSTDIR\python$\""' nsExec::ExecToLog '"$INSTDIR\nssm\win64\nssm.exe" set ${SERVICENAME} AppParameters "\"$INSTDIR\server\odoo-bin\" -c "\"$INSTDIR\server\odoo.conf\"' + nsExec::ExecToLog '"$INSTDIR\nssm\win64\nssm.exe" set ${SERVICENAME} ObjectName "SERVICE LOCAL" ""' ${Else} nsExec::ExecToLog '"$INSTDIR\nssm\win32\nssm.exe" install ${SERVICENAME} "$INSTDIR\python\python.exe" ' nsExec::ExecToLog '"$INSTDIR\nssm\win32\nssm.exe" set ${SERVICENAME} AppDirectory "$\"$INSTDIR\python$\""' nsExec::ExecToLog '"$INSTDIR\nssm\win32\nssm.exe" set ${SERVICENAME} AppParameters "\"$INSTDIR\server\odoo-bin\" -c "\"$INSTDIR\server\odoo.conf\"' + nsExec::ExecToLog '"$INSTDIR\nssm\win32\nssm.exe" set ${SERVICENAME} ObjectName "SERVICE LOCAL" ""' ${EndIf} Call RestartOdooService @@ -309,18 +316,73 @@ Section $(TITLE_PostgreSQL) SectionPostgreSQL --serverport $TextPostgreSQLPort' SectionEnd -Section $(TITLE_LocalProxyMode) LocalProxy +Section $(TITLE_IOT) IOT SectionIn 3 - DetailPrint "Configuring Local Proxy Mode" - WriteIniStr "$INSTDIR\server\odoo.conf" "options" "server_wide_modules" "base,web,hw_l10n_eg_eta" + DetailPrint "Configuring TITLE_IOT" + WriteIniStr "$INSTDIR\server\odoo.conf" "options" "server_wide_modules" "web,hw_posbox_homepage,hw_drivers" WriteIniStr "$INSTDIR\server\odoo.conf" "options" "list_db" "False" - WriteIniStr "$INSTDIR\server\odoo.conf" "options" "max_cron_thread" "0" + WriteIniStr "$INSTDIR\server\odoo.conf" "options" "max_cron_threads" "0" nsExec::ExecToStack '"$INSTDIR\python\python.exe" "$INSTDIR\server\odoo-bin" genproxytoken' pop $0 pop $ProxyTokenPwd Call RestartOdooService SectionEnd + +Section $(TITLE_Nginx) Nginx + SectionIn 3 + SetOutPath '$TEMP' + VAR /GLOBAL nginx_zip_filename + VAR /GLOBAL nginx_url + + # need unzip plugin: + # https://nsis.sourceforge.io/mediawiki/images/5/5a/NSISunzU.zip + StrCpy $nginx_zip_filename "nginx-1.22.0.zip" + StrCpy $nginx_url "https://nginx.org/download/$nginx_zip_filename" + + DetailPrint "Downloading Nginx" + inetc::get "$nginx_url" "$TEMP\$nginx_zip_filename" /POPUP + DetailPrint "Temp dir: $TEMP\$nginx_zip_filename" + DetailPrint "Unzip Nginx" + nsisunz::UnzipToLog "$TEMP\$nginx_zip_filename" "$INSTDIR" + + Pop $0 + StrCmp $0 "success" ok + DetailPrint "$0" ;print error message to log + ok: + + FindFirst $0 $1 "$INSTDIR\nginx*" + DetailPrint "Setting up nginx" + SetOutPath "$INSTDIR\$1\conf" + CreateDirectory $INSTDIR\$1\temp + CreateDirectory $INSTDIR\$1\logs + FindClose $0 + File "conf\nginx\nginx.conf" + # Temporary certs for the first start + File "..\..\odoo\addons\point_of_sale\tools\posbox\overwrite_after_init\etc\ssl\certs\nginx-cert.crt" + File "..\..\odoo\addons\point_of_sale\tools\posbox\overwrite_after_init\etc\ssl\private\nginx-cert.key" +SectionEnd + +Section $(TITLE_Ghostscript) SectionGhostscript + SectionIn 3 + SetOutPath '$TEMP' + VAR /GLOBAL ghostscript_exe_filename + VAR /GLOBAL ghostscript_url + + StrCpy $ghostscript_exe_filename "gs1000w64.exe" + StrCpy $ghostscript_url "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs1000/$ghostscript_exe_filename" + + DetailPrint "Downloading Ghostscript" + inetc::get "$ghostscript_url" "$TEMP\$ghostscript_exe_filename" /POPUP + DetailPrint "Temp dir: $TEMP\$ghostscript_exe_filename" + + Rmdir /r "INSTDIR\Ghostscript" + DetailPrint "Installing Ghostscript" + ExecWait '"$TEMP\$ghostscript_exe_filename" \ + /S \ + /D=$INSTDIR\Ghostscript' +SectionEnd + Section -Post WriteRegExpandStr HKLM "${UNINSTALL_REGISTRY_KEY}" "UninstallString" "$INSTDIR\Uninstall.exe" WriteRegExpandStr HKLM "${UNINSTALL_REGISTRY_KEY}" "InstallLocation" "$INSTDIR" @@ -347,15 +409,20 @@ Section "Uninstall" Pop $R0 ReadRegStr $0 HKLM "${UNINSTALL_REGISTRY_KEY_SERVER}" "UninstallString" ExecWait '"$0" /S' + ExecWait '"$INSTDIR\Ghostscript\uninstgs.exe" /S' nsExec::Exec "net stop ${SERVICENAME}" nsExec::Exec "sc delete ${SERVICENAME}" sleep 2 Rmdir /r "$INSTDIR\server" + Rmdir /r "$INSTDIR\sessions" Rmdir /r "$INSTDIR\thirdparty" Rmdir /r "$INSTDIR\python" Rmdir /r "$INSTDIR\nssm" + FindFirst $0 $1 "$INSTDIR\nginx*" + Rmdir /R "$INSTDIR\$1" + FindClose $0 DeleteRegKey HKLM "${UNINSTALL_REGISTRY_KEY}" SectionEnd @@ -503,7 +570,7 @@ Function ShowProxyTokenDialogPage Abort ${EndIf} - ${NSD_CreateLabel} 0 0 100% 25% "Here is your access token for the Odoo Local Proxy, please write it down in a safe place, you will need it to configure the proxy" + ${NSD_CreateLabel} 0 0 100% 25% "Here is your access token for the Odoo IOT, please write it down in a safe place, you will need it to configure the IOT" Pop $ProxyTokenLabel ${NSD_CreateText} 0 30% 100% 13u $ProxyTokenPwd