summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOdoo's Mergebot <xmo+roboto@odoo.com>2019-09-04 17:07:43 +0200
committerGitHub <noreply@github.com>2019-09-04 17:07:43 +0200
commit24c93d3f7f2e17674a59e81764684fe4493403f0 (patch)
tree6d511bb1e99449f996a0495bef81eb039218d1ca
parentf75fc4ed6394cbeb00286b58d48e2cd2784f8b90 (diff)
parent5d0afddaf221756378fa5d3cb459fd25aa147f93 (diff)
[IMP] Customer Display as IoT Device
Linked to odoo/enterprise#5266 Move all the logic that was inside hw_screen into an IoT Driver in order to have consistency between devices. Move the configuration of screen_url from the IoT Box form to the IoT Device. If an URL different than the default one is used, refresh the page every minute. -- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr closes odoo/odoo#36074 Signed-off-by: Quentin Lejeune (qle) <qle@odoo.com>
-rwxr-xr-xaddons/hw_drivers/controllers/driver.py27
-rw-r--r--addons/hw_drivers/drivers/DisplayDriver.py202
-rw-r--r--addons/hw_drivers/static/src/js/worker.js (renamed from addons/hw_screen/static/src/js/worker.js)16
-rw-r--r--addons/hw_drivers/views/pos_display.html (renamed from addons/hw_screen/views/pos_display.html)0
-rw-r--r--addons/hw_posbox_homepage/controllers/main.py10
-rw-r--r--addons/hw_screen/__init__.py4
-rw-r--r--addons/hw_screen/__manifest__.py21
-rw-r--r--addons/hw_screen/controllers/__init__.py4
-rw-r--r--addons/hw_screen/controllers/main.py154
-rw-r--r--addons/point_of_sale/static/src/js/models.js6
-rw-r--r--addons/point_of_sale/static/src/js/screens.js1
-rwxr-xr-xaddons/point_of_sale/tools/posbox/overwrite_after_init/etc/xdg/openbox/autostart30
-rw-r--r--addons/point_of_sale/tools/posbox/overwrite_after_init/usr/share/firefox-esr/browser/defaults/preferences/all-posbox.js5
-rwxr-xr-xaddons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo2
14 files changed, 241 insertions, 241 deletions
diff --git a/addons/hw_drivers/controllers/driver.py b/addons/hw_drivers/controllers/driver.py
index 7cda51f9f9d..5b01df584f4 100755
--- a/addons/hw_drivers/controllers/driver.py
+++ b/addons/hw_drivers/controllers/driver.py
@@ -142,7 +142,7 @@ class Driver(Thread, metaclass=DriverMetaClass):
"""
On specific driver override this method to give connection type of device
return string
- possible value : direct - network - bluetooth - serial
+ possible value : direct - network - bluetooth - serial - hdmi
"""
return self._device_connection
@@ -151,7 +151,7 @@ class Driver(Thread, metaclass=DriverMetaClass):
"""
On specific driver override this method to give type of device
return string
- possible value : printer - camera - keyboard - scanner - device
+ possible value : printer - camera - keyboard - scanner - display - device
"""
return self._device_type
@@ -294,6 +294,26 @@ class Manager(Thread):
else:
_logger.warning('Odoo server not set')
+ def get_connected_displays(self):
+ display_devices = {}
+ hdmi = subprocess.check_output(['tvservice', '-n']).decode('utf-8')
+ if hdmi.find('=') != -1:
+ hdmi_serial = sub('[^a-zA-Z0-9 ]+', '', hdmi.split('=')[1]).replace(' ', '_')
+ iot_device = IoTDevice({
+ 'identifier': hdmi_serial,
+ 'name': hdmi.split('=')[1],
+ }, 'display')
+ display_devices[hdmi_serial] = iot_device
+
+ if not len(display_devices):
+ # No display connected, create "fake" device to be accessed from another computer
+ display_devices['distant_display'] = IoTDevice({
+ 'identifier': "distant_display",
+ 'name': "Distant Display",
+ }, 'display')
+
+ return display_devices
+
def serial_loop(self):
serial_devices = {}
for identifier in glob('/dev/serial/by-path/*'):
@@ -364,11 +384,14 @@ class Manager(Thread):
updated_devices = {}
self.send_alldevices()
self.load_drivers()
+ # The list of devices doesn't change after the Raspberry has booted
+ display_devices = self.get_connected_displays()
cpt = 0
while 1:
updated_devices = self.usb_loop()
updated_devices.update(self.video_loop())
updated_devices.update(mpdm.devices)
+ updated_devices.update(display_devices)
updated_devices.update(bt_devices)
updated_devices.update(socket_devices)
updated_devices.update(self.serial_loop())
diff --git a/addons/hw_drivers/drivers/DisplayDriver.py b/addons/hw_drivers/drivers/DisplayDriver.py
new file mode 100644
index 00000000000..35547fbcbe1
--- /dev/null
+++ b/addons/hw_drivers/drivers/DisplayDriver.py
@@ -0,0 +1,202 @@
+import jinja2
+import json
+import logging
+import netifaces as ni
+import os
+from pathlib import Path
+import subprocess
+import threading
+import time
+import urllib3
+
+from odoo import http
+from odoo.addons.hw_drivers.tools import helpers
+from odoo.addons.hw_drivers.controllers.driver import Driver, event_manager, iot_devices
+
+path = os.path.realpath(os.path.join(os.path.dirname(__file__), '../views'))
+loader = jinja2.FileSystemLoader(path)
+
+jinja_env = jinja2.Environment(loader=loader, autoescape=True)
+jinja_env.filters["json"] = json.dumps
+
+pos_display_template = jinja_env.get_template('pos_display.html')
+
+_logger = logging.getLogger(__name__)
+
+
+class DisplayDriver(Driver):
+ connection_type = 'display'
+
+ def __init__(self, device):
+ super(DisplayDriver, self).__init__(device)
+ self._device_type = 'display'
+ self._device_connection = 'hdmi'
+ self._device_name = device['name']
+ self.event_data = threading.Event()
+ self.owner = False
+ self.rendered_html = ''
+ self.load_url()
+
+ @property
+ def device_identifier(self):
+ return self.dev['identifier']
+
+ @classmethod
+ def supported(cls, device):
+ return True # All devices with connection_type == 'display' are supported
+
+ @classmethod
+ def get_default_display(cls):
+ displays = list(filter(lambda d: iot_devices[d].device_type == 'display', iot_devices))
+ return len(displays) and iot_devices[displays[0]]
+
+ def action(self, data):
+ if data.get('action') == "update_url":
+ self.update_url(data.get('url'))
+ elif data.get('action') == "display_refresh":
+ self.call_xdotools('F5')
+ elif data.get('action') == "take_control":
+ self.take_control(self.data['owner'], data.get('html'))
+ elif data.get('action') == "customer_facing_display":
+ self.update_customer_facing_display(self.data['owner'], data.get('html'))
+ elif data.get('action') == "get_owner":
+ self.data = {
+ 'value': '',
+ 'owner': self.owner,
+ }
+ event_manager.device_changed(self)
+
+ def run(self):
+ while True:
+ if self.url != 'http://localhost:8069/point_of_sale/display/':
+ # Refresh the page every minute
+ time.sleep(60)
+ self.call_xdotools('F5')
+
+ def update_url(self, url=None):
+ firefox_env = os.environ.copy()
+ firefox_env['HOME'] = '/tmp'
+ firefox_env['DISPLAY'] = ':0.0'
+ firefox_env['XAUTHORITY'] = '/run/lightdm/pi/xauthority'
+ self.url = url or 'http://localhost:8069/point_of_sale/display/'
+ if self.device_identifier != 'distant_display':
+ subprocess.Popen(['firefox', self.url], env=firefox_env)
+
+ def load_url(self):
+ if helpers.get_odoo_server_url():
+ # disable certifiacte verification
+ urllib3.disable_warnings()
+ http = urllib3.PoolManager(cert_reqs='CERT_NONE')
+ try:
+ response = http.request('GET', "%s/iot/box/%s/screen_url" % (helpers.get_odoo_server_url(), helpers.get_mac_address()))
+ urls = json.loads(response.data.decode('utf8'))
+ return self.update_url(urls[self.device_identifier])
+ except json.decoder.JSONDecodeError:
+ return self.update_url(response.data.decode('utf8'))
+ except Exception:
+ pass
+ return self.update_url()
+
+ def call_xdotools(self, keystroke):
+ os.environ['DISPLAY'] = ":0.0"
+ os.environ['XAUTHORITY'] = "/run/lightdm/pi/xauthority"
+ try:
+ subprocess.call(['xdotool', 'key', keystroke])
+ return "xdotool succeeded in stroking " + keystroke
+ except:
+ return "xdotool threw an error, maybe it is not installed on the IoTBox"
+
+ def update_customer_facing_display(self, origin, html=None):
+ if origin == self.owner:
+ self.rendered_html = html
+ self.event_data.set()
+
+ def get_serialized_order(self):
+ # IMPLEMENTATION OF LONGPOLLING
+ # Times out 2 seconds before the JS request does
+ if self.event_data.wait(28):
+ self.event_data.clear()
+ return {'rendered_html': self.rendered_html}
+ return {'rendered_html': False}
+
+ def take_control(self, new_owner, html=None):
+ # ALLOW A CASHIER TO TAKE CONTROL OVER THE POSBOX, IN CASE OF MULTIPLE CASHIER PER DISPLAY
+ self.owner = new_owner
+ self.rendered_html = html
+ self.data = {
+ 'value': '',
+ 'owner': self.owner,
+ }
+ event_manager.device_changed(self)
+ self.event_data.set()
+
+class DisplayController(http.Controller):
+
+ @http.route('/hw_proxy/display_refresh', type='json', auth='none', cors='*')
+ def display_refresh(self):
+ display = DisplayDriver.get_default_display()
+ if display:
+ return display.call_xdotools('F5')
+
+ @http.route('/hw_proxy/customer_facing_display', type='json', auth='none', cors='*')
+ def customer_facing_display(self, html=None):
+ display = DisplayDriver.get_default_display()
+ if display:
+ display.update_customer_facing_display(http.request.httprequest.remote_addr, html)
+ return {'status': 'updated'}
+ return {'status': 'failed'}
+
+ @http.route('/hw_proxy/take_control', type='json', auth='none', cors='*')
+ def take_control(self, html=None):
+ display = DisplayDriver.get_default_display()
+ if display:
+ display.take_control(http.request.httprequest.remote_addr, html)
+ return {
+ 'status': 'success',
+ 'message': 'You now have access to the display',
+ }
+
+ @http.route('/hw_proxy/test_ownership', type='json', auth='none', cors='*')
+ def test_ownership(self):
+ display = DisplayDriver.get_default_display()
+ if display and display.owner == http.request.httprequest.remote_addr:
+ return {'status': 'OWNER'}
+ return {'status': 'NOWNER'}
+
+ @http.route(['/point_of_sale/get_serialized_order'], type='json', auth='none')
+ def get_serialized_order(self):
+ display = DisplayDriver.get_default_display()
+ if display:
+ return display.get_serialized_order()
+ return {
+ 'rendered_html': False,
+ 'error': "No display found",
+ }
+
+ @http.route(['/point_of_sale/display'], type='http', auth='none')
+ def display(self):
+ cust_js = None
+ interfaces = ni.interfaces()
+
+ with open(os.path.join(os.path.dirname(__file__), "../static/src/js/worker.js")) as js:
+ cust_js = js.read()
+
+ display_ifaces = []
+ for iface_id in interfaces:
+ if 'wlan' in iface_id or 'eth' in iface_id:
+ iface_obj = ni.ifaddresses(iface_id)
+ ifconfigs = iface_obj.get(ni.AF_INET, [])
+ for conf in ifconfigs:
+ if conf.get('addr'):
+ display_ifaces.append({
+ 'iface_id': iface_id,
+ 'addr': conf.get('addr'),
+ 'icon': 'sitemap' if 'eth' in iface_id else 'wifi',
+ })
+
+ return pos_display_template.render({
+ 'title': "Odoo -- Point of Sale",
+ 'breadcrumb': 'POS Client display',
+ 'cust_js': cust_js,
+ 'display_ifaces': display_ifaces,
+ })
diff --git a/addons/hw_screen/static/src/js/worker.js b/addons/hw_drivers/static/src/js/worker.js
index 5949ab39cef..a485a946588 100644
--- a/addons/hw_screen/static/src/js/worker.js
+++ b/addons/hw_drivers/static/src/js/worker.js
@@ -4,7 +4,6 @@
// It allows to transform the <head> only once
var mergedHead = false;
var current_client_url = "";
- var stop_longpolling = false;
function longpolling() {
$.ajax({
@@ -15,14 +14,13 @@
data: JSON.stringify({jsonrpc: '2.0'}),
success: function(data) {
- if (typeof data.result.stop_longpolling !== 'undefined') {
- stop_longpolling = data.result.stop_longpolling;
- }
if (data.result.error) {
$('.error-message').text(data.result.error);
$('.error-message').removeClass('d-none');
+ setTimeout(longpolling, 5000);
+ return;
}
- if (data.result.ip_from && data.result.rendered_html) {
+ if (data.result.rendered_html) {
var trimmed = $.trim(data.result.rendered_html);
var $parsedHTML = $('<div>').html($.parseHTML(trimmed,true)); // WARNING: the true here will executes any script present in the string to parse
var new_client_url = $parsedHTML.find(".resources > base").attr('href');
@@ -47,15 +45,11 @@
foreign_js();
}
}
- if (!stop_longpolling) {
- longpolling();
- }
+ longpolling();
},
error: function (jqXHR, status, err) {
- if (!stop_longpolling) {
- setTimeout(longpolling, 5000);
- }
+ setTimeout(longpolling, 5000);
},
timeout: 30000,
diff --git a/addons/hw_screen/views/pos_display.html b/addons/hw_drivers/views/pos_display.html
index b7cb5fd52bb..b7cb5fd52bb 100644
--- a/addons/hw_screen/views/pos_display.html
+++ b/addons/hw_drivers/views/pos_display.html
diff --git a/addons/hw_posbox_homepage/controllers/main.py b/addons/hw_posbox_homepage/controllers/main.py
index 54712e32083..b1353e98cf1 100644
--- a/addons/hw_posbox_homepage/controllers/main.py
+++ b/addons/hw_posbox_homepage/controllers/main.py
@@ -49,16 +49,6 @@ list_credential_template = jinja_env.get_template('list_credential.html')
class IoTboxHomepage(web.Home):
- def get_hw_screen_message(self):
- return """
- <p>
- The activate the customer display feature, you will need to reinstall the IoT Box software.
- You can find the latest images on the <a href="http://nightly.odoo.com/master/posbox/">Odoo Nightly builds</a> website.
- Make sure to download at least the version 16.<br/>
- Odoo version 11, or above, is required to use the customer display feature.
- </p>
- """
-
def get_pos_device_status(self):
statuses = {}
for driver in hw_proxy.drivers:
diff --git a/addons/hw_screen/__init__.py b/addons/hw_screen/__init__.py
deleted file mode 100644
index dd15b5c32e5..00000000000
--- a/addons/hw_screen/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from . import controllers
diff --git a/addons/hw_screen/__manifest__.py b/addons/hw_screen/__manifest__.py
deleted file mode 100644
index 756d385a550..00000000000
--- a/addons/hw_screen/__manifest__.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-{
- 'name': 'Screen Driver',
- 'version': '1.0',
- 'category': 'Hardware Drivers',
- 'sequence': 6,
- 'summary': 'Add a customer facing displays for POS',
- 'website': 'https://www.odoo.com/page/point-of-sale-hardware',
- 'description': """
-Screen Driver
-=============
-
-This module allows the POS client to send rendered HTML to a remotely
-installed screen. This module then displays this HTML using a web
-browser.
-""",
- 'depends': ['hw_proxy'],
- 'installable': False,
- 'auto_install': False,
-}
diff --git a/addons/hw_screen/controllers/__init__.py b/addons/hw_screen/controllers/__init__.py
deleted file mode 100644
index 5d4b25db9c0..00000000000
--- a/addons/hw_screen/controllers/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from . import main
diff --git a/addons/hw_screen/controllers/main.py b/addons/hw_screen/controllers/main.py
deleted file mode 100644
index 1ee346ae3f7..00000000000
--- a/addons/hw_screen/controllers/main.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import http
-from odoo.tools import config
-from odoo.addons.web.controllers import main as web
-from openerp.addons.hw_posbox_homepage.controllers import main as homepage
-
-import jinja2
-import json
-import logging
-import netifaces as ni
-import os
-from subprocess import call
-import sys
-import time
-import threading
-
-self_port = str(config['http_port'] or 8069)
-
-_logger = logging.getLogger(__name__)
-
-if hasattr(sys, 'frozen'):
- # When running on compiled windows binary, we don't have access to package loader.
- path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'views'))
- loader = jinja2.FileSystemLoader(path)
-else:
- loader = jinja2.PackageLoader('odoo.addons.hw_screen', "views")
-
-jinja_env = jinja2.Environment(loader=loader, autoescape=True)
-jinja_env.filters["json"] = json.dumps
-
-pos_display_template = jinja_env.get_template('pos_display.html')
-
-
-class Homepage(homepage.IoTboxHomepage):
-
- def get_hw_screen_message(self):
- return """
-<p>
-If you need to display the current customer basket on another device, you can do it <a href='/point_of_sale/display'>here</a>.
-</p>
-"""
-
-class HardwareScreen(web.Home):
-
- event_data = threading.Event()
- pos_client_data = {'rendered_html': False,
- 'ip_from': False}
- display_in_use = ''
- failure_count = {}
-
- def _call_xdotools(self, keystroke):
- os.environ['DISPLAY'] = ":0.0"
- os.environ['XAUTHORITY'] = "/run/lightdm/pi/xauthority"
- try:
- call(['xdotool', 'key', keystroke])
- return "xdotool succeeded in stroking " + keystroke
- except:
- return "xdotool threw an error, maybe it is not installed on the IoTBox"
-
- @http.route('/hw_proxy/display_refresh', type='json', auth='none', cors='*')
- def display_refresh(self):
- return self._call_xdotools('F5')
-
- # POS CASHIER'S ROUTES
- @http.route('/hw_proxy/customer_facing_display', type='json', auth='none', cors='*')
- def update_user_facing_display(self, html=None):
- request_ip = http.request.httprequest.remote_addr
- if request_ip == HardwareScreen.pos_client_data.get('ip_from', ''):
- HardwareScreen.pos_client_data['rendered_html'] = html
- HardwareScreen.event_data.set()
-
- return {'status': 'updated'}
- else:
- return {'status': 'failed'}
-
- @http.route('/hw_proxy/take_control', type='json', auth='none', cors='*')
- def take_control(self, html=None):
- # ALLOW A CASHIER TO TAKE CONTROL OVER THE POSBOX, IN CASE OF MULTIPLE CASHIER PER POSBOX
- HardwareScreen.pos_client_data['rendered_html'] = html
- HardwareScreen.pos_client_data['ip_from'] = http.request.httprequest.remote_addr
- HardwareScreen.event_data.set()
-
- return {'status': 'success',
- 'message': 'You now have access to the display'}
-
- @http.route('/hw_proxy/test_ownership', type='json', auth='none', cors='*')
- def test_ownership(self):
- if HardwareScreen.pos_client_data.get('ip_from') == http.request.httprequest.remote_addr:
- return {'status': 'OWNER'}
- else:
- return {'status': 'NOWNER'}
-
- # POSBOX ROUTES (SELF)
- @http.route('/point_of_sale/display', type='http', auth='none')
- def render_main_display(self):
- return self._get_html()
-
- @http.route('/point_of_sale/get_serialized_order', type='json', auth='none')
- def get_serialized_order(self):
- request_addr = http.request.httprequest.remote_addr
- result = HardwareScreen.pos_client_data
- if HardwareScreen.display_in_use and request_addr != HardwareScreen.display_in_use:
- if not HardwareScreen.failure_count.get(request_addr):
- HardwareScreen.failure_count[request_addr] = 0
- if HardwareScreen.failure_count[request_addr] > 0:
- time.sleep(10)
- HardwareScreen.failure_count[request_addr] += 1
- return {
- 'rendered_html': False,
- 'error': "Not Authorized. Another browser is in use to display for the client. Please refresh.",
- 'stop_longpolling': True,
- 'ip_from': request_addr,
- }
-
- # IMPLEMENTATION OF LONGPOLLING
- # Times out 2 seconds before the JS request does
- if HardwareScreen.event_data.wait(28):
- HardwareScreen.event_data.clear()
- HardwareScreen.failure_count[request_addr] = 0
- return result
- return {
- 'rendered_html': False,
- 'ip_from': HardwareScreen.pos_client_data['ip_from'],
- }
-
- def _get_html(self):
- cust_js = None
- interfaces = ni.interfaces()
- HardwareScreen.display_in_use = http.request.httprequest.remote_addr
-
- with open(os.path.join(os.path.dirname(__file__), "../static/src/js/worker.js")) as js:
- cust_js = js.read()
-
- display_ifaces = []
- for iface_id in interfaces:
- if 'wlan' in iface_id or 'eth' in iface_id:
- iface_obj = ni.ifaddresses(iface_id)
- ifconfigs = iface_obj.get(ni.AF_INET, [])
- for conf in ifconfigs:
- if conf.get('addr'):
- display_ifaces.append({
- 'iface_id': iface_id,
- 'addr': conf.get('addr'),
- 'icon': 'sitemap' if 'eth' in iface_id else 'wifi',
- })
-
- return pos_display_template.render({
- 'title': "Odoo -- Point of Sale",
- 'breadcrumb': 'POS Client display',
- 'cust_js': cust_js,
- 'display_ifaces': display_ifaces,
- })
diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js
index 1bfe646ebee..de64e9cb139 100644
--- a/addons/point_of_sale/static/src/js/models.js
+++ b/addons/point_of_sale/static/src/js/models.js
@@ -652,6 +652,7 @@ exports.PosModel = Backbone.Model.extend({
var order = new exports.Order({},{pos:this});
this.get('orders').add(order);
this.set('selectedOrder', order);
+ this.send_current_order_to_customer_facing_display();
return order;
},
/**
@@ -2252,9 +2253,6 @@ exports.Order = Backbone.Model.extend({
this.paymentlines.on('remove', function(){ this.save_to_db("paymentline:rem"); }, this);
if (this.pos.config.iface_customer_facing_display) {
- this.orderlines.on('change', this.pos.send_current_order_to_customer_facing_display, this.pos);
- // removing last orderline does not trigger change event
- this.orderlines.on('remove', this.pos.send_current_order_to_customer_facing_display, this.pos);
this.paymentlines.on('change', this.pos.send_current_order_to_customer_facing_display, this.pos);
// removing last paymentline does not trigger change event
this.paymentlines.on('remove', this.pos.send_current_order_to_customer_facing_display, this.pos);
@@ -2655,6 +2653,8 @@ exports.Order = Backbone.Model.extend({
if(line.has_product_lot){
this.display_lot_popup();
}
+
+ this.pos.send_current_order_to_customer_facing_display();
},
get_selected_orderline: function(){
return this.selected_orderline;
diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js
index c988db5cfdb..80ddbde9191 100644
--- a/addons/point_of_sale/static/src/js/screens.js
+++ b/addons/point_of_sale/static/src/js/screens.js
@@ -551,6 +551,7 @@ var OrderWidget = PosBaseWidget.extend({
selected_orderline.price_manually_set = true;
selected_orderline.set_unit_price(val);
}
+ this.pos.send_current_order_to_customer_facing_display();
}
},
change_selected_order: function() {
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/xdg/openbox/autostart b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/xdg/openbox/autostart
deleted file mode 100755
index d7b5349a74f..00000000000
--- a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/xdg/openbox/autostart
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-xset s off
-xset -dpms
-
-export HOME=/tmp
-
-DISPLAY_URL='http://localhost:8069/point_of_sale/display'
-URL=${DISPLAY_URL}
-
-maciotbox=$(/sbin/ifconfig eth0 |grep -Eo ..\(\:..\){5})
-
-if [ -e /home/pi/odoo-remote-server.conf ]
- then
- URL_SERVER=$(grep "" /home/pi/odoo-remote-server.conf)/iot/box/${maciotbox}/screen_url
- var=0
- while [ ${var} -lt 30 ]; do
- var=$((var+1))
- sleep 1
- status_code=$(wget ${URL_SERVER} --server-response 2>&1|grep 'HTTP/'|awk '{print $2}')
- if [ ${status_code} -eq 200 ]
- then
- URL=$(wget ${URL_SERVER} -q -O -)
- break
- fi
- done
-fi
-# wait until odoo is ready, otherwise you will have to plug a
-# keyboard into the posbox just to refresh firefox
-until wget -q "${DISPLAY_URL}" -O /dev/null ; do sleep 1 ; done
-/usr/bin/firefox "${URL}" & \ No newline at end of file
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/share/firefox-esr/browser/defaults/preferences/all-posbox.js b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/share/firefox-esr/browser/defaults/preferences/all-posbox.js
index 3a1fcac9cbd..a519ae55745 100644
--- a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/share/firefox-esr/browser/defaults/preferences/all-posbox.js
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/share/firefox-esr/browser/defaults/preferences/all-posbox.js
@@ -4,4 +4,7 @@ pref("app.update.checkInstallTime", false);
pref("devtools.webide.widget.autoinstall", false);
pref("xpinstall.customConfirmationUI", false);
pref("xpinstall.signatures.required", false);
-pref("browser.shell.checkDefaultBrowser",false)
+pref("browser.shell.checkDefaultBrowser", false);
+// Open all links in the same tab
+// Needed to change URL without having multiple tabs running
+pref("browser.link.open_newwindow", 1);
diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo
index f633be2b7c7..73f9b777c88 100755
--- a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo
+++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo
@@ -26,7 +26,7 @@ test -x $DAEMON || exit 0
set -e
function _start() {
- start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER:$USER --background --make-pidfile --exec $DAEMON -- --config $CONFIG --logfile $LOGFILE --load=web,hw_proxy,hw_posbox_homepage,hw_posbox_upgrade,hw_escpos,hw_blackbox_be,hw_screen,hw_drivers
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER:$USER --background --make-pidfile --exec $DAEMON -- --config $CONFIG --logfile $LOGFILE --load=web,hw_proxy,hw_posbox_homepage,hw_posbox_upgrade,hw_escpos,hw_blackbox_be,hw_drivers
}
function _stop() {