[FIX] hw_drivers: IoT Box connection
In Chrome 84, mixed content will be completely blocked for security reasons ([see blog](https://blog.chromium.org/2019/10/no-more-mixed-messages-about-https.html)) Other browsers will probably follow in the next months. As IoT Boxes don't have a valid certificate before connecting to a DB, we used mixed passive content to communicate with the boxes. This won't be possible anymore, and we have no possible way to communicate directly from the browser to the box. - When an IoT Box boots without registered DB, a unique code will be created with a validity of 5 minutes. - This code will be shown on the customer display and printed on the status ticket. - The box will call a route on odoo.com and a record will be created in odoo.com with the unique code. - The user will have to enter the code manually in his DB to connect to the IoT Box, the DB will then contact odoo.com to search for a record containing the unique code. If it's found, the `openerp.enterprise.database` will be added to the record. - The box will then query odoo.com at regular intervals (10 seconds) to check if a DB is linked to the code. closes odoo/odoo#53623 Taskid: 2246535 X-original-commit: c55f12e32598a713105f3c049ac053e46ba15bb2 Related: odoo/enterprise#11421 Signed-off-by: pimodoo <pimodoo@users.noreply.github.com>
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
#!/usr/bin/python3
|
||||
from base64 import b64decode
|
||||
from datetime import datetime, timedelta
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
from importlib import util
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import requests
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -289,6 +291,63 @@ class EventManager(object):
|
||||
|
||||
event_manager = EventManager()
|
||||
|
||||
#----------------------------------------------------------
|
||||
# ConnectionManager
|
||||
#----------------------------------------------------------
|
||||
|
||||
class ConnectionManager(Thread):
|
||||
def __init__(self):
|
||||
super(ConnectionManager, self).__init__()
|
||||
self.pairing_code = False
|
||||
self.pairing_uuid = False
|
||||
|
||||
def run(self):
|
||||
if not helpers.get_odoo_server_url():
|
||||
end_time = datetime.now() + timedelta(minutes=5)
|
||||
while (datetime.now() < end_time):
|
||||
self._connect_box()
|
||||
time.sleep(10)
|
||||
self.pairing_code = False
|
||||
self.pairing_uuid = False
|
||||
self._refresh_displays()
|
||||
|
||||
def _connect_box(self):
|
||||
data = {
|
||||
'jsonrpc': 2.0,
|
||||
'params': {
|
||||
'pairing_code': self.pairing_code,
|
||||
'pairing_uuid': self.pairing_uuid,
|
||||
}
|
||||
}
|
||||
|
||||
urllib3.disable_warnings()
|
||||
req = requests.post('https://iot-proxy.odoo.com/odoo-enterprise/iot/connect-box', json=data, verify=False)
|
||||
result = req.json().get('result', {})
|
||||
|
||||
if all(key in result for key in ['pairing_code', 'pairing_uuid']):
|
||||
self.pairing_code = result['pairing_code']
|
||||
self.pairing_uuid = result['pairing_uuid']
|
||||
elif all(key in result for key in ['url', 'token', 'db_uuid', 'enterprise_code']):
|
||||
self._connect_to_server(result['url'], result['token'], result['db_uuid'], result['enterprise_code'])
|
||||
|
||||
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'])
|
||||
# Notify the DB, so that the kanban view already shows the IoT Box
|
||||
m.send_alldevices()
|
||||
# Restart to checkout the git branch, get a certificate, load the IoT handlers...
|
||||
subprocess.check_call(["sudo", "service", "odoo", "restart"])
|
||||
|
||||
def _refresh_displays(self):
|
||||
"""Refresh all displays to hide the pairing code"""
|
||||
for d in iot_devices:
|
||||
if iot_devices[d].device_type == 'display':
|
||||
iot_devices[d].action({
|
||||
'action': 'display_refresh'
|
||||
})
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Manager
|
||||
@@ -386,6 +445,10 @@ class Manager(Thread):
|
||||
# Must be started from main thread
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
|
||||
cm = ConnectionManager()
|
||||
cm.daemon = True
|
||||
cm.start()
|
||||
|
||||
m = Manager()
|
||||
m.daemon = True
|
||||
m.start()
|
||||
|
||||
@@ -13,6 +13,11 @@ 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
|
||||
|
||||
try:
|
||||
from odoo.addons.hw_drivers.controllers.driver import cm
|
||||
except:
|
||||
cm = None
|
||||
|
||||
path = os.path.realpath(os.path.join(os.path.dirname(__file__), '../../views'))
|
||||
loader = jinja2.FileSystemLoader(path)
|
||||
|
||||
@@ -215,4 +220,5 @@ class DisplayController(http.Controller):
|
||||
'cust_js': cust_js,
|
||||
'display_ifaces': display_ifaces,
|
||||
'display_identifier': display_identifier,
|
||||
'pairing_code': cm and cm.pairing_code,
|
||||
})
|
||||
|
||||
@@ -20,6 +20,11 @@ from odoo.addons.hw_drivers.iot_handlers.interfaces.PrinterInterface import PPDs
|
||||
from odoo.addons.hw_drivers.tools import helpers
|
||||
from odoo.addons.hw_proxy.controllers.main import drivers as old_drivers
|
||||
|
||||
try:
|
||||
from odoo.addons.hw_drivers.controllers.driver import cm
|
||||
except:
|
||||
cm = None
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
def print_star_error(deviceId):
|
||||
@@ -231,6 +236,7 @@ class PrinterDriver(Driver):
|
||||
ip = ''
|
||||
mac = ''
|
||||
homepage = ''
|
||||
pairing_code = ''
|
||||
|
||||
ssid = helpers.get_ssid()
|
||||
wlan = '\nWireless network:\n%s\n\n' % ssid
|
||||
@@ -256,11 +262,15 @@ class PrinterDriver(Driver):
|
||||
mac = '\nMAC Address:\n%s\n' % helpers.get_mac_address()
|
||||
homepage = '\nHomepage:\nhttp://%s:8069\n\n' % main_ips
|
||||
|
||||
code = cm and cm.pairing_code
|
||||
if code:
|
||||
pairing_code = '\nPairing Code:\n%s\n' % code
|
||||
|
||||
center = b'\x1b\x61\x01'
|
||||
title = b'\n\x1b\x21\x30\x1b\x4d\x01IoTBox Status\x1b\x4d\x00\x1b\x21\x00\n'
|
||||
cut = b'\x1d\x56\x41'
|
||||
|
||||
self.print_raw(center + title + wlan.encode() + mac.encode() + ip.encode() + homepage.encode() + cut + b'\n')
|
||||
self.print_raw(center + title + wlan.encode() + mac.encode() + ip.encode() + homepage.encode() + pairing_code.encode() + cut + b'\n')
|
||||
|
||||
def open_cashbox(self):
|
||||
"""Sends a signal to the current printer to open the connected cashbox."""
|
||||
|
||||
@@ -22,7 +22,12 @@
|
||||
background: linear-gradient(to right bottom, #77717e, #c9a8a9);
|
||||
height: 100vh;
|
||||
}
|
||||
.pos-display-status {
|
||||
.pos-display-boxes {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
}
|
||||
.pos-display-box {
|
||||
padding: 10px 20px;
|
||||
background: rgba(0, 0, 0, 0.17);
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
@@ -30,9 +35,10 @@
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
width: 500px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.pos-display-box hr {
|
||||
background-color: #fff;
|
||||
}
|
||||
.info-text {
|
||||
font-size: 15px;
|
||||
@@ -48,31 +54,40 @@
|
||||
<img style="width: 150px;" src="/web/static/src/img/logo_inverse_white_206px.png">
|
||||
<p class="mt-3" style="color: #fff;font-size: 30px;">IoTBox</p>
|
||||
</div>
|
||||
<div class="pos-display-status">
|
||||
<h4 class="text-center mb-3">POS Client display</h4>
|
||||
<table class="table table-hover table-sm table-pos-info">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Interface</th>
|
||||
<th>IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for display_iface in display_ifaces -%}
|
||||
<tr>
|
||||
<td><i class="fa fa-{{ display_iface.icon }}"/> {{ display_iface.essid }}</td>
|
||||
<td>{{ display_iface.addr }}</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="mb-2 info-text">
|
||||
<i class="fa fa-info-circle mr-1"></i>The customer cart will be displayed here once a Point of Sale session is started.
|
||||
</p>
|
||||
<p class="mb-2 info-text">
|
||||
<i class="fa fa-info-circle mr-1"></i>Odoo version 11 or above is required.
|
||||
</p>
|
||||
<div class="error-message alert alert-danger mb-2 d-none" role="alert" />
|
||||
<div class="pos-display-boxes">
|
||||
{% if pairing_code %}
|
||||
<div class="pos-display-box">
|
||||
<h4 class="text-center mb-3">Pairing Code</h4>
|
||||
<hr/>
|
||||
<h4 class="text-center mb-3">{{ pairing_code }}</h4>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="pos-display-box">
|
||||
<h4 class="text-center mb-3">POS Client display</h4>
|
||||
<table class="table table-hover table-sm table-pos-info">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Interface</th>
|
||||
<th>IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for display_iface in display_ifaces -%}
|
||||
<tr>
|
||||
<td><i class="fa fa-{{ display_iface.icon }}"/> {{ display_iface.essid }}</td>
|
||||
<td>{{ display_iface.addr }}</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="mb-2 info-text">
|
||||
<i class="fa fa-info-circle mr-1"></i>The customer cart will be displayed here once a Point of Sale session is started.
|
||||
</p>
|
||||
<p class="mb-2 info-text">
|
||||
<i class="fa fa-info-circle mr-1"></i>Odoo version 11 or above is required.
|
||||
</p>
|
||||
<div class="error-message alert alert-danger mb-2 d-none" role="alert" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user