diff --git a/addons/hw_drivers/controllers/__init__.py b/addons/hw_drivers/controllers/__init__.py
index 3bd726d7111..8a59703b3c1 100644
--- a/addons/hw_drivers/controllers/__init__.py
+++ b/addons/hw_drivers/controllers/__init__.py
@@ -1,2 +1 @@
-from . import driver
-from . import loading
\ No newline at end of file
+from . import driver
\ No newline at end of file
diff --git a/addons/hw_drivers/controllers/driver.py b/addons/hw_drivers/controllers/driver.py
index 3772dfa32f9..64d45a1fcfc 100755
--- a/addons/hw_drivers/controllers/driver.py
+++ b/addons/hw_drivers/controllers/driver.py
@@ -1,432 +1,401 @@
#!/usr/bin/python3
import logging
import time
-from threading import Thread
-import usb
-import gatt
+from threading import Thread, Event
+from usb import core
+from gatt import DeviceManager as Gatt_DeviceManager
import subprocess
-import netifaces as ni
+import netifaces
import json
-import re
-from odoo import http
+from re import sub
import urllib3
-from odoo.http import request as httprequest
-import datetime
+import os
+import socket
+from importlib import util
+import v4l2
+from fcntl import ioctl
+from cups import Connection as cups_connection
+from glob import glob
+from base64 import b64decode
+from pathlib import Path
-logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(name)s: %(message)s')
-_logger = logging.getLogger('dispatcher')
+from odoo import http, _
+from odoo.modules.module import get_resource_path
-owner_dict = {}
-last_ping = {}
+_logger = logging.getLogger(__name__)
+
+
+#----------------------------------------------------------
+# Helper
+#----------------------------------------------------------
+
+def get_mac_address():
+ try:
+ return netifaces.ifaddresses('eth0')[netifaces.AF_LINK][0]['addr']
+ except:
+ return netifaces.ifaddresses('wlan0')[netifaces.AF_LINK][0]['addr']
+
+def get_ip():
+ try:
+ return netifaces.ifaddresses('eth0')[netifaces.AF_INET][0]['addr']
+ except:
+ return netifaces.ifaddresses('wlan0')[netifaces.AF_INET][0]['addr']
+
+def read_file_first_line(filename):
+ path = Path.home() / filename
+ if path.exists():
+ with path.open('r') as f:
+ return f.readline().strip('\n')
+ return ''
+
+def get_odoo_server_url():
+ return read_file_first_line('odoo-remote-server.conf')
+
+def get_token():
+ return read_file_first_line('token')
+
+def get_version():
+ return '19_04'
+
+#----------------------------------------------------------
+# Controllers
+#----------------------------------------------------------
class StatusController(http.Controller):
+ @http.route('/hw_drivers/action', type='json', auth='none', cors='*', csrf=False, save_session=False)
+ def action(self, session_id, device_id, data):
+ """
+ This route is called when we want to make a action with device (take picture, printing,...)
+ We specify in data from which session_id that action is called
+ And call the action of specific device
+ """
+ iot_device = iot_devices.get(device_id)
+ if iot_device:
+ iot_device.data['owner'] = session_id
+ data = json.loads(data)
+ iot_device.action(data)
+ return True
+ return False
- @http.route('/hw_drivers/owner/check', type='json', auth='none', cors='*', csrf=False)
- def check_cantakeowner(self): #, devices, tab
- data = httprequest.jsonrequest
- for device in data['devices']:
- if owner_dict.get(device) and owner_dict[device] != data['tab']:
- before_date = datetime.datetime.now() - datetime.timedelta(seconds=10)
- if last_ping.get(owner_dict[device]) and last_ping.get(owner_dict[device]) > before_date:
- return 'no'
- else:
- old_tab = owner_dict[device]
- for dev2 in owner_dict:
- if owner_dict[dev2] == old_tab:
- owner_dict[dev2] = ''
- return 'yes'
+ @http.route('/hw_drivers/event', type='json', auth='none', cors='*', csrf=False, save_session=False)
+ def event(self, listener):
+ """
+ listener is a dict in witch there are a sessions_id and a dict of device_id to listen
+ """
+ req = event_manager.add_request(listener)
+ if req['event'].wait(50):
+ req['event'].clear()
+ req['result']['session_id'] = req['session_id']
+ return req['result']
- @http.route('/hw_drivers/owner/take', type='json', auth='none', cors='*', csrf=False)
- def take_ownership(self): #, devices, tab
- data = httprequest.jsonrequest
- for device in data['devices']:
- owner_dict[device] = data['tab']
- last_ping[data['tab']] = datetime.datetime.now()
- return data['tab']
-
- @http.route('/hw_drivers/owner/ping', type='json', auth='none', cors='*', csrf=False)
- def ping_trigger(self): #, tab
- data = httprequest.jsonrequest
- ping_dict = {}
- last_ping[data['tab']] = datetime.datetime.now()
- for dev in data['devices']:
- if owner_dict.get(dev) and owner_dict[dev] == data['tab']:
- for driver_path in drivers:
- if driver_path.find(dev) == 0 and drivers[driver_path].ping_value:
- ping_dict[dev] = drivers[driver_path].ping_value
- drivers[driver_path].ping_value = '' # or set it to nothing
- else:
- ping_dict[dev] = 'STOP'
- return ping_dict
-
- @http.route('/hw_drivers/box/connect', type='json', auth='none', cors='*', csrf=False)
- def connect_box(self):
- data = httprequest.jsonrequest
- server = "" # read from file
- try:
- f = open('/home/pi/odoo-remote-server.conf', 'r')
- for line in f:
- server += line
- f.close()
- server = server.split('\n')[0]
- except:
- server = ''
- if server:
- return 'This IoTBox has already been connected'
- else:
- iotname = ''
- token = data['token'].split('|')[1]
- url = data['token'].split('|')[0]
- reboot = 'noreboot'
- subprocess.call(['/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/connect_to_server.sh', url, iotname, token, reboot])
- send_iot_box_device(False)
- return 'IoTBox connected'
-
- @http.route('/hw_drivers/drivers/status', type='http', auth='none', cors='*')
- def status(self):
- result = "
List of drivers and values:
"
- for path in drivers:
- result += "- " + path + ":" + str(drivers[path].value) + "
"
- result += "
"
- result +=" "
- return result
-
- @http.route('/hw_drivers/driverdetails/', type='http', auth='none', cors='*')
- def statusdetail(self, identifier):
- for device in drivers:
- if device.find(identifier) != -1:
- return str(drivers[device].value)
- return ''
-
- @http.route('/hw_drivers/driveraction/', type='json', auth='none', cors='*', csrf=False)
- def driveraction(self, identifier):
- data = httprequest.jsonrequest
- result = 'device not found'
- if data.get('action') == 'print':
- with open('/tmp/toprinter', 'w') as file:
- file.write(data['data'])
- subprocess.call("cat /tmp/toprinter | base64 -d | lp -d " + identifier, shell=True)
- result = 'ok'
- if data.get('action') == 'camera':
- cameras = subprocess.check_output("v4l2-ctl --list-devices", shell=True).decode('utf-8').split('\n\n')
- adrress = '/dev/video0'
- for camera in cameras:
- if camera:
- camera = camera.split('\n\t')
- serial = re.sub('[^a-zA-Z0-9 ]+', '', camera[0].split(': ')[0]).replace(' ','_')
- if serial == data.get('identifier'):
- adrress = camera[1]
- picture = subprocess.check_output("v4l2-ctl --list-formats-ext|grep 'Size'|awk '{print $3}'|sort -rn|awk NR==1", shell=True).decode('utf-8')
- subprocess.call("fswebcam -d " + adrress + " /tmp/testimage -r " + picture, shell=True)
- image_bytes = subprocess.check_output('cat /tmp/testimage | base64',shell=True)
- result = {'image': image_bytes}
- return result
-
- @http.route('/hw_drivers/send_iot_box', type='http', auth='none', cors='*')
- def send_iot_box(self):
- send_iot_box_device(False)
- return 'ok'
+ @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 = get_odoo_server_url()
+ image = get_resource_path('hw_drivers', 'static/img', 'False.jpg')
+ if server == '':
+ token = b64decode(token).decode('utf-8')
+ url, token = token.split('|')
+ try:
+ subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server.sh'), url, '', token, 'noreboot'])
+ m.send_alldevices()
+ image = get_resource_path('hw_drivers', 'static/img', 'True.jpg')
+ 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()
#----------------------------------------------------------
-# Driver common interface
+# Drivers
#----------------------------------------------------------
-class Driver(Thread):
- ping_value = ""
- def supported(self):
- pass
+drivers = []
+bt_devices = {}
+iot_devices = {}
- def value(self):
- pass
-
- def get_name(self):
- pass
-
- def get_connection(self):
- pass
-
- def action(self, action):
- pass
-
-#----------------------------------------------------------
-# Usb drivers
-#----------------------------------------------------------
-usbdrivers = []
-drivers = {}
-
-class UsbMetaClass(type):
+class DriverMetaClass(type):
def __new__(cls, clsname, bases, attrs):
- newclass = super(UsbMetaClass, cls).__new__(cls, clsname, bases, attrs)
- usbdrivers.append(newclass)
+ newclass = super(DriverMetaClass, cls).__new__(cls, clsname, bases, attrs)
+ drivers.append(newclass)
return newclass
-class USBDriver(Driver,metaclass=UsbMetaClass):
- def __init__(self, dev):
- super(USBDriver, self).__init__()
+class Driver(Thread, metaclass=DriverMetaClass):
+ """
+ Hook to register the driver into the drivers list
+ """
+ connection_type = ""
+
+ def __init__(self, device):
+ super(Driver, self).__init__()
+ self.dev = device
+ self.data = {'value': ''}
+ self.gatt_device = False
+
+ @property
+ def device_name(self):
+ return self._device_name
+
+ @property
+ def device_identifier(self):
+ return self._device_identifier
+
+ @property
+ def device_connection(self):
+ """
+ On specific driver override this method to give connection type of device
+ return string
+ possible value : direct - network - bluetooth
+ """
+ return self._device_connection
+
+ @property
+ def device_type(self):
+ """
+ On specific driver override this method to give type of device
+ return string
+ possible value : printer - camera - device
+ """
+ return self._device_type
+
+ @classmethod
+ def supported(cls, device):
+ """
+ On specific driver override this method to check if device is supported or not
+ return True or False
+ """
+ pass
+
+ def get_message(self):
+ return ''
+
+ def action(self, data):
+ """
+ On specific driver override this method to make a action with device (take picture, printing,...)
+ """
+ raise NotImplementedError()
+
+ def disconnect(self):
+ del iot_devices[self.device_identifier]
+
+
+#----------------------------------------------------------
+# Device manager
+#----------------------------------------------------------
+
+class EventManager(object):
+ def __init__(self):
+ self.sessions = {}
+
+ def _delete_expired_sessions(self, max_time=70):
+ '''
+ Clears sessions that are no longer called.
+
+ :param max_time: time a session can stay unused before being deleted
+ '''
+ now = time.time()
+ expired_sessions = [session for session in self.sessions if now - self.sessions[session]['time_request'] > max_time]
+ for session in expired_sessions:
+ del self.sessions[session]
+
+ def add_request(self, listener):
+ self.session = {
+ 'session_id': listener['session_id'],
+ 'devices': listener['devices'],
+ 'event': Event(),
+ 'result': {},
+ 'time_request': time.time(),
+ }
+ self._delete_expired_sessions()
+ self.sessions[listener['session_id']] = self.session
+ return self.sessions[listener['session_id']]
+
+ def device_changed(self, device):
+ for session in self.sessions:
+ if device.device_identifier in self.sessions[session]['devices']:
+ self.sessions[session]['result'] = device.data
+ self.sessions[session]['result']['device_id'] = device.device_identifier
+ self.sessions[session]['event'].set()
+
+
+class IoTDevice(object):
+
+ def __init__(self, dev, connection_type):
self.dev = dev
- self.value = ""
+ self.connection_type = connection_type
- def get_name(self):
- lsusb = str(subprocess.check_output('lsusb')).split("\\n")
- for usbpath in lsusb: # Should filter on usb devices or inverse loops?
- device = self.dev
- if "%04x:%04x" % (device.idVendor, device.idProduct) in usbpath:
- return usbpath.split("%04x:%04x" % (device.idVendor, device.idProduct))[1]
- return str(device.idVendor) + ":" + str(device.idProduct)
-
- def get_connection(self):
- return 'direct'
-
- def value(self):
- return self.value
+event_manager = EventManager()
#----------------------------------------------------------
-# Bluetooth
+# Manager
#----------------------------------------------------------
-class GattBtManager(gatt.DeviceManager):
+
+class Manager(Thread):
+
+ def __init__(self):
+ super(Manager, self).__init__()
+ self.load_drivers()
+
+ def load_drivers(self):
+ """
+ This method loads local files: 'odoo/addons/hw_drivers/drivers'
+ And execute these python drivers
+ """
+ path = get_resource_path('hw_drivers', 'drivers')
+ driversList = os.listdir(path)
+ for driver in driversList:
+ path_file = os.path.join(path, driver)
+ spec = util.spec_from_file_location(driver, path_file)
+ if spec:
+ module = util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+
+ def send_alldevices(self):
+ """
+ This method send IoT Box and devices informations to Odoo database
+ """
+ server = get_odoo_server_url()
+ if server:
+ iot_box = {
+ 'name': socket.gethostname(),
+ 'identifier': get_mac_address(),
+ 'ip': get_ip(),
+ 'token': get_token(),
+ 'version': get_version()
+ }
+ devices_list = {}
+ for device in iot_devices:
+ identifier = iot_devices[device].device_identifier
+ devices_list[identifier] = {
+ 'name': iot_devices[device].device_name,
+ 'type': iot_devices[device].device_type,
+ 'connection': iot_devices[device].device_connection,
+ }
+ data = {
+ 'params': {
+ 'iot_box' : iot_box,
+ 'devices' : devices_list,
+ }
+ }
+ # disable certifiacte verification
+ urllib3.disable_warnings()
+ http = urllib3.PoolManager(cert_reqs='CERT_NONE')
+ try:
+ http.request(
+ 'POST',
+ server + "/iot/setup",
+ body = json.dumps(data).encode('utf8'),
+ headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
+ )
+ except Exception as e:
+ _logger.error('Could not reach configured server')
+ _logger.error('A error encountered : %s ' % e)
+ else:
+ _logger.warning('Odoo server not set')
+
+ def usb_loop(self):
+ usb_devices = {}
+ devs = core.find(find_all=True)
+ for dev in devs:
+ path = "usb_%04x:%04x_%03d_%03d_" % (dev.idVendor, dev.idProduct, dev.bus, dev.address)
+ iot_device = IoTDevice(dev, 'usb')
+ usb_devices[path] = iot_device
+ return usb_devices
+
+ def video_loop(self):
+ camera_devices = {}
+ videos = glob('/dev/video*')
+ for video in videos:
+ with open(video, 'w') as path:
+ dev = v4l2.v4l2_capability()
+ ioctl(path, v4l2.VIDIOC_QUERYCAP, dev)
+ dev.interface = video
+ iot_device = IoTDevice(dev, 'video')
+ camera_devices[dev.bus_info.decode('utf-8')] = iot_device
+ return camera_devices
+
+ def printer_loop(self):
+ printer_devices = {}
+ devices = conn.getDevices()
+ for path in [printer_lo for printer_lo in devices if devices[printer_lo]['device-make-and-model'] != 'Unknown']:
+ if 'uuid=' in path:
+ serial = sub('[^a-zA-Z0-9 ]+', '', path.split('uuid=')[1])
+ elif 'serial=' in path:
+ serial = sub('[^a-zA-Z0-9 ]+', '', path.split('serial=')[1])
+ else:
+ serial = sub('[^a-zA-Z0-9 ]+', '', path)
+ devices[path]['identifier'] = serial
+ devices[path]['url'] = path
+ iot_device = IoTDevice(devices[path], 'printer')
+ printer_devices[serial] = iot_device
+ return printer_devices
+
+ def run(self):
+ """
+ Thread that will check connected/disconnected device, load drivers if needed and contact the odoo server with the updates
+ """
+ devices = {}
+ updated_devices = {}
+ self.send_alldevices()
+ cpt = 0
+ while 1:
+ updated_devices = self.usb_loop()
+ updated_devices.update(self.video_loop())
+ updated_devices.update(bt_devices)
+ if cpt % 40 == 0:
+ printer_devices = self.printer_loop()
+ cpt = 0
+ updated_devices.update(printer_devices)
+ cpt += 1
+ added = updated_devices.keys() - devices.keys()
+ removed = devices.keys() - updated_devices.keys()
+ devices = updated_devices
+ for path in [device_rm for device_rm in removed if device_rm in iot_devices]:
+ iot_devices[path].disconnect()
+ for path in [device_add for device_add in added if device_add not in iot_devices]:
+ for driverclass in [d for d in drivers if d.connection_type == devices[path].connection_type]:
+ if driverclass.supported(device = updated_devices[path].dev):
+ _logger.info('For device %s will be driven', path)
+ d = driverclass(device = updated_devices[path].dev)
+ d.daemon = True
+ d.start()
+ iot_devices[path] = d
+ self.send_alldevices()
+ break
+ time.sleep(3)
+
+class GattBtManager(Gatt_DeviceManager):
def device_discovered(self, device):
- # TODO: need some kind of updated_devices mechanism or not?
path = "bt_%s" % (device.mac_address,)
- if path not in drivers:
- for driverclass in btdrivers:
- d = driverclass(device = device, manager=self)
- if d.supported():
- drivers[path] = d
- d.connect()
- send_iot_box_device(False)
-
+ if path not in bt_devices:
+ device.manager = self
+ iot_device = IoTDevice(device, 'bluetooth')
+ bt_devices[path] = iot_device
class BtManager(Thread):
- gatt_manager = False
def run(self):
dm = GattBtManager(adapter_name='hci0')
- self.gatt_manager = dm
+ for device in [device_con for device_con in dm.devices() if device_con.is_connected()]:
+ device.disconnect()
dm.start_discovery()
dm.run()
-#----------------------------------------------------------
-# Bluetooth drivers
-#----------------------------------------------------------
-btdrivers = []
-
-class BtMetaClass(type):
- def __new__(cls, clsname, bases, attrs):
- newclass = super(BtMetaClass, cls).__new__(cls, clsname, bases, attrs)
- btdrivers.append(newclass)
- return newclass
-
-
-class BtDriver(Driver, metaclass=BtMetaClass):
-
-
- def __init__(self, device, manager):
- super(BtDriver, self).__init__()
- self.dev = device
- self.manager = manager
- self.value = ''
- self.gatt_device = False
-
- def disconnect(self):
- path = "bt_%s" % (self.dev.mac_address,)
- del drivers[path]
-
- def get_name(self):
- return self.dev.alias()
-
- def value(self):
- return self.value
-
- def action(self, action):
- pass
-
- def get_connection(self):
- return 'bluetooth'
-
- def connect(self):
- pass
-
-
-
-
-
-
-class USBDeviceManager(Thread):
- devices = {}
- def run(self):
- first_time = True
- send_iot_box_device(False)
- while 1:
- sendJSON = False
- devs = usb.core.find(find_all=True)
- updated_devices = {}
- for dev in devs:
- path = "usb_%04x:%04x_%03d_%03d_" % (dev.idVendor, dev.idProduct, dev.bus, dev.address)
- updated_devices[path] = self.devices.get(path, dev)
- added = updated_devices.keys() - self.devices.keys()
- removed = self.devices.keys() - updated_devices.keys()
- self.devices = updated_devices
- if (removed):
- for path in list(drivers):
- if (path in removed):
- del drivers[path]
- sendJSON = True
- for path in added:
- dev = updated_devices[path]
- for driverclass in usbdrivers:
- d = driverclass(updated_devices[path])
- if d.supported():
- _logger.info('For device %s will be driven', path)
- drivers[path] = d
- # launch thread
- d.daemon = True
- d.start()
- sendJSON = True
- if sendJSON or first_time:
- send_iot_box_device(send_printer = first_time)
- first_time = False
- time.sleep(3)
-
-
-
-
-
-def send_iot_box_device(send_printer):
- maciotbox = subprocess.check_output("/sbin/ifconfig eth0 |grep -Eo ..\(\:..\){5}", shell=True).decode('utf-8').split('\n')[0]
- server = "" # read from file
- try:
- f = open('/home/pi/odoo-remote-server.conf', 'r')
- for line in f:
- server += line
- f.close()
- except: #In case the file does not exist
- server=''
- server = server.split('\n')[0]
- if server:
- url = server + "/iot/setup"
- interfaces = ni.interfaces()
- for iface_id in interfaces:
- iface_obj = ni.ifaddresses(iface_id)
- ifconfigs = iface_obj.get(ni.AF_INET, [])
- for conf in ifconfigs:
- if conf.get('addr') and conf.get('addr') != '127.0.0.1':
- ips = conf.get('addr')
- break
-
- # Build device JSON
- devicesList = {}
- for path in drivers:
- device_name = drivers[path].get_name()
- device_connection = drivers[path].get_connection()
- identifier = path.split('_')[0] + '_' + path.split('_')[1]
- devicesList[identifier] = {'name': device_name,
- 'type': 'device',
- 'connection': device_connection}
-
- # Build camera JSON
- try:
- cameras = subprocess.check_output("v4l2-ctl --list-devices", shell=True).decode('utf-8').split('\n\n')
- for camera in cameras:
- if camera:
- camera = camera.split('\n\t')
- serial = re.sub('[^a-zA-Z0-9 ]+', '', camera[0].split(': ')[0]).replace(' ','_')
- devicesList[serial] = {
- 'name': camera[0].split(': ')[0],
- 'connection': 'direct',
- 'type': 'camera'
- }
- except:
- pass
-
- # Build printer JSON
- printerList = {}
- if send_printer:
- printers = subprocess.check_output("sudo lpinfo -lv", shell=True).decode('utf-8').split('Device')
- printers_installed = ''
- try:
- printers_installed = subprocess.check_output("sudo lpstat -a", shell=True).decode('utf-8')
- except:
- pass
- for printer in printers:
- printerTab = printer.split('\n')
- if printer and printerTab[4].split('=')[1] != ' ' or 'lpd://' in printerTab[0]:
- device_connection = printerTab[1].split('= ')[1]
- model = ''
- for device_id in printerTab[4].split('= ')[1].split(';'):
- if any(x in device_id for x in ['MDL','MODEL']):
- model = device_id.split(':')[1]
- serial = re.sub('[^a-zA-Z0-9 ]+', '', model).replace(' ','_')
- identifier = ''
- if device_connection == 'direct':
- identifier = serial + '_' + maciotbox #name + macIOTBOX
- elif device_connection == 'network' and 'socket' in printerTab[0]:
- socketIP = printerTab[0].split('://')[1]
- macprinter = subprocess.check_output("arp -a " + socketIP + " |awk NR==1'{print $4}'", shell=True).decode('utf-8').split('\n')[0]
- identifier = macprinter # macPRINTER
- elif device_connection == 'network' and 'lpd' in printerTab[0]:
- identifier = re.sub('[^a-zA-Z0-9 ]+', '', printerTab[0].split('://')[1])
- model = printerTab[2].split('= ')[1]
- elif device_connection == 'network' and 'dnssd' in printerTab[0]:
- hostname_printer = subprocess.check_output("ippfind -n \"" + model + "\" | awk \'{split($0,a,\"/\"); print a[3]}\' | awk \'{split($0,b,\":\"); print b[1]}\'", shell=True).decode('utf-8').split('\n')[0]
- if hostname_printer:
- macprinter = subprocess.check_output("arp -a " + hostname_printer + " |awk NR==1'{print $4}'", shell=True).decode('utf-8').split('\n')[0]
- identifier = macprinter # macprinter
-
- identifier = identifier.replace(':','_')
- if identifier and identifier not in printerList:
- printerList[identifier] = {
- 'name': model,
- 'connection': device_connection,
- 'type': 'printer'
- }
-
- if identifier not in printers_installed:
- # install these printers
- try:
- ppd = subprocess.check_output("sudo lpinfo -m |grep '" + model + "'", shell=True).decode('utf-8').split('\n')
- if len(ppd) > 2:
- subprocess.call("sudo lpadmin -p '" + identifier + "' -E -v '" + printerTab[0].split('= ')[1] + "'", shell=True)
- else:
- subprocess.call("sudo lpadmin -p '" + identifier + "' -E -v '" + printerTab[0].split('= ')[1] + "' -m '" + ppd[0].split(' ')[0] + "'", shell=True)
- except:
- subprocess.call("sudo lpadmin -p '" + identifier + "' -E -v '" + printerTab[0].split('= ')[1] + "'", shell=True)
- subprocess.call('> /tmp/printers', shell=True)
- for printer in printerList:
- subprocess.call('echo "' + printerList[printer]['name'] + '" >> /tmp/printers', shell=True)
-
- if devicesList:
- subprocess.call('> /tmp/devices', shell=True)
- for device in devicesList:
- subprocess.call('echo "' + str(device) + '|' + devicesList[device]['name'] + '" >> /tmp/devices', shell=True)
-
- #build JSON with all devices
- hostname = subprocess.check_output('hostname').decode('utf-8').split('\n')[0]
- token = "" # read from file
- try:
- f = open('/home/pi/token', 'r')
- for line in f:
- token += line
- f.close()
- except: #In case the file does not exist
- token=''
- token = token.split('\n')[0]
- data = {'name': hostname,'identifier': maciotbox, 'ip': ips, 'token': token}
- devicesList.update(printerList)
- data['devices'] = devicesList
- data_json = json.dumps(data).encode('utf8')
- headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
- urllib3.disable_warnings()
- http = urllib3.PoolManager(cert_reqs='CERT_NONE')
- req = False
- try:
- req = http.request('POST',
- url,
- body=data_json,
- headers=headers)
- except Exception as e:
- _logger.warning('Could not reach configured server')
- _logger.error('A error encountered : %s ' % e)
+conn = cups_connection()
+PPDs = conn.getPPDs()
+printers = conn.getPrinters()
+m = Manager()
+m.daemon = True
+m.start()
+bm = BtManager()
+bm.daemon = True
+bm.start()
diff --git a/addons/hw_drivers/controllers/loading.py b/addons/hw_drivers/controllers/loading.py
deleted file mode 100644
index cd02d3dd82f..00000000000
--- a/addons/hw_drivers/controllers/loading.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import importlib.util
-import os
-from .driver import BtManager
-from .driver import USBDeviceManager
-from odoo import modules
-
-driversList = os.listdir("/home/pi/odoo/addons/hw_drivers/drivers")
-for driver in driversList:
- path = "/home/pi/odoo/addons/hw_drivers/drivers/" + driver
- spec = importlib.util.spec_from_file_location(driver, path)
- if spec:
- foo = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(foo)
-
-
-if not getattr(modules, '_iot_daemon_started', False):
- # ----------------------------------------------------------
- # Bluetooth start
- # ----------------------------------------------------------
- bm = BtManager()
- bm.daemon = True
- bm.start()
-
- #----------------------------------------------------------
- #USB start
- #----------------------------------------------------------
-
- udm = USBDeviceManager()
- udm.daemon = True
- udm.start()
-
- # Did this because of the
- modules._iot_daemon_started = True
-
diff --git a/addons/hw_drivers/static/img/False.jpg b/addons/hw_drivers/static/img/False.jpg
new file mode 100644
index 00000000000..8f8e6489b30
Binary files /dev/null and b/addons/hw_drivers/static/img/False.jpg differ
diff --git a/addons/hw_drivers/static/img/True.jpg b/addons/hw_drivers/static/img/True.jpg
new file mode 100644
index 00000000000..0488574a699
Binary files /dev/null and b/addons/hw_drivers/static/img/True.jpg differ
diff --git a/addons/hw_posbox_homepage/controllers/main.py b/addons/hw_posbox_homepage/controllers/main.py
index f3de5a0d138..538b6acbf42 100644
--- a/addons/hw_posbox_homepage/controllers/main.py
+++ b/addons/hw_posbox_homepage/controllers/main.py
@@ -7,7 +7,7 @@ import jinja2
import subprocess
import socket
import sys
-import netifaces as ni
+import netifaces
import odoo
from odoo import http
import zipfile
@@ -15,12 +15,28 @@ import io
import os
from odoo.tools import misc
import urllib3
+from pathlib import Path
from uuid import getnode as get_mac
from odoo.addons.hw_proxy.controllers import main as hw_proxy
+from odoo.addons.web.controllers import main as web
+from odoo.modules.module import get_resource_path
+from odoo.addons.hw_drivers.controllers.driver import iot_devices, get_ip, get_odoo_server_url
_logger = logging.getLogger(__name__)
+
+#----------------------------------------------------------
+# Helper
+#----------------------------------------------------------
+
+def access_point():
+ return get_ip() == '10.11.12.1'
+
+#----------------------------------------------------------
+# Controllers
+#----------------------------------------------------------
+
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'))
@@ -38,18 +54,7 @@ driver_list_template = jinja_env.get_template('driver_list.html')
remote_connect_template = jinja_env.get_template('remote_connect.html')
configure_wizard_template = jinja_env.get_template('configure_wizard.html')
-class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
-
- def get_ip_iotbox(self):
- interfaces = ni.interfaces()
- for iface_id in interfaces:
- iface_obj = ni.ifaddresses(iface_id)
- ifconfigs = iface_obj.get(ni.AF_INET, [])
- for conf in ifconfigs:
- if conf.get('addr') and conf.get('addr') != '127.0.0.1':
- ips = conf.get('addr')
- break
- return ips
+class IoTboxHomepage(web.Home):
def get_hw_screen_message(self):
return """
@@ -67,18 +72,6 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
statuses[driver] = hw_proxy.drivers[driver].get_status()
return statuses
- def get_server_status(self):
- server_template = ""
- try:
- f = open('/home/pi/odoo-remote-server.conf', 'r')
- for line in f:
- server_template += line
- f.close()
- except:
- return ''
-
- return server_template.split('\n')[0]
-
def get_homepage_data(self):
hostname = str(socket.gethostname())
mac = get_mac()
@@ -88,7 +81,7 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
if wired == 'up':
network = 'Ethernet'
elif ssid:
- if self.get_ip_iotbox() == '10.11.12.1':
+ if access_point():
network = 'Wifi access point'
else:
network = 'Wifi : ' + ssid
@@ -105,70 +98,33 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
'message': ' '.join(pos_device[status]['messages'])
})
- hdmi_name = subprocess.check_output('tvservice -n', shell=True).decode('utf-8')
- if hdmi_name.find('=') != -1:
- hdmi_name = hdmi_name.split('=')[1]
- hdmi_message = subprocess.check_output('tvservice -s', shell=True).decode('utf-8')
+ for device in iot_devices:
iot_device.append({
- 'name': 'display : ' + hdmi_name,
- 'type': 'device',
- 'message': hdmi_message
- })
-
- try:
- f = open('/tmp/devices', 'r')
- for line in f:
- url = 'http://' + self.get_ip_iotbox() + ':8069/hw_drivers/driverdetails/' + line.split('|')[0]
- urllib3.disable_warnings()
- http = urllib3.PoolManager(cert_reqs='CERT_NONE')
- value = ''
- try:
- req = http.request('GET', url)
- except Exception as e:
- req = ''
- _logger.warning('Could not reach configured server')
- _logger.error('A error encountered : %s ' % e)
-
- if req:
- value = req.data.decode('utf-8')
- iot_device.append({
- 'name': line.split('|')[1],
- 'message': line.split('|')[0] + ' : ' + value,
- 'type': 'device',
- })
- f.close()
- except:
- pass
-
- try:
- f = open('/tmp/printers', 'r')
- for line in f:
- iot_device.append({
- 'name': line,
- 'type': 'printer',
- })
- f.close()
- except:
- pass
+ 'name': iot_devices[device].device_name + ' : ' + str(iot_devices[device].data['value']),
+ 'type': iot_devices[device].device_type,
+ 'message': iot_devices[device].device_identifier + iot_devices[device].get_message()
+ })
return {
'hostname': hostname,
- 'ip': self.get_ip_iotbox(),
+ 'ip': get_ip(),
'mac': ":".join(i + next(h) for i in h),
'iot_device_status': iot_device,
- 'server_status': self.get_server_status() or 'Not Configured',
+ 'server_status': get_odoo_server_url() or 'Not Configured',
'network_status': network,
}
@http.route('/', type='http', auth='none')
def index(self):
- if (os.path.isfile('/home/pi/wifi_network.txt') == False or os.path.isfile('/home/pi/odoo-remote-server.conf') == False) and self.get_ip_iotbox() == '10.11.12.1':
+ wifi = Path.home() / 'wifi_network.txt'
+ remote_server = Path.home() / 'odoo-remote-server.conf'
+ if (wifi.exists() == False or remote_server.exists() == False) and access_point():
return configure_wizard_template.render({
'title': 'Configure IoT Box',
'breadcrumb': 'Configure IoT Box',
'loading_message': 'Configuring your IoT Box',
'ssid': self.get_wifi_essid(),
- 'server': self.get_server_status(),
+ 'server': get_odoo_server_url(),
'hostname': subprocess.check_output('hostname').decode('utf-8'),
})
else:
@@ -177,7 +133,7 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
@http.route('/list_drivers', type='http', auth='none', website=True)
def list_drivers(self):
drivers_list = []
- for driver in os.listdir("/home/pi/odoo/addons/hw_drivers/drivers"):
+ for driver in os.listdir(get_resource_path('hw_drivers', 'drivers')):
if driver != '__pycache__':
drivers_list.append(driver)
return driver_list_template.render({
@@ -188,13 +144,13 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
@http.route('/load_drivers', type='http', auth='none', website=True)
def load_drivers(self):
- subprocess.call("sudo mount -o remount,rw /", shell=True)
- subprocess.call("sudo mount -o remount,rw /root_bypass_ramdisks", shell=True)
+ subprocess.check_call("sudo mount -o remount,rw /", shell=True)
+ subprocess.check_call("sudo mount -o remount,rw /root_bypass_ramdisks", shell=True)
mac = subprocess.check_output("/sbin/ifconfig eth0 |grep -Eo ..\(\:..\){5}", shell=True).decode('utf-8').split('\n')[0]
#response = requests.get(url, auth=(username, db_uuid.split('\n')[0]), stream=True)
- server = self.get_server_status()
+ server = get_odoo_server_url()
if server:
urllib3.disable_warnings()
pm = urllib3.PoolManager(cert_reqs='CERT_NONE')
@@ -205,17 +161,16 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
server,
fields={'mac': mac})
except Exception as e:
- req = ''
- _logger.warning('Could not reach configured server')
+ _logger.error('Could not reach configured server')
_logger.error('A error encountered : %s ' % e)
if resp and resp.data:
zip_file = zipfile.ZipFile(io.BytesIO(resp.data))
- zip_file.extractall("/home/pi/odoo/addons/hw_drivers/drivers")
- subprocess.call("sudo service odoo restart", shell=True)
- subprocess.call("sudo mount -o remount,ro /", shell=True)
- subprocess.call("sudo mount -o remount,ro /root_bypass_ramdisks", shell=True)
+ zip_file.extractall(get_resource_path('hw_drivers', 'drivers'))
+ subprocess.check_call("sudo service odoo restart", shell=True)
+ subprocess.check_call("sudo mount -o remount,ro /", shell=True)
+ subprocess.check_call("sudo mount -o remount,ro /root_bypass_ramdisks", shell=True)
- return ""
+ return ""
def get_wifi_essid(self):
wifi_options = []
@@ -247,8 +202,8 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
else:
persistent = ""
- subprocess.call(['/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/connect_to_wifi.sh', essid, password, persistent])
- server = self.get_server_status()
+ subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_wifi.sh'), essid, password, persistent])
+ server = get_odoo_server_url()
res_payload = {
'message': 'Connecting to ' + essid,
}
@@ -262,29 +217,29 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
@http.route('/wifi_clear', type='http', auth='none', cors='*', csrf=False)
def clear_wifi_configuration(self):
- os.system('/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/clear_wifi_configuration.sh')
+ os.system(get_resource_path('point_of_sale', 'tools/posbox/configuration/clear_wifi_configuration.sh'))
- return ""
+ return ""
@http.route('/server_clear', type='http', auth='none', cors='*', csrf=False)
def clear_server_configuration(self):
- os.system('/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/clear_server_configuration.sh')
+ os.system(get_resource_path('point_of_sale', 'tools/posbox/configuration/clear_server_configuration.sh'))
- return ""
+ return ""
@http.route('/drivers_clear', type='http', auth='none', cors='*', csrf=False)
def clear_drivers_list(self):
- os.system('/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/clear_drivers_list.sh')
+ os.system(get_resource_path('point_of_sale', 'tools/posbox/configuration/clear_drivers_list.sh'))
- return ""
+ return ""
@http.route('/server_connect', type='http', auth='none', cors='*', csrf=False)
def connect_to_server(self, token, iotname):
url = token.split('|')[0]
token = token.split('|')[1]
reboot = 'reboot'
- subprocess.call(['/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/connect_to_server.sh', url, iotname, token, reboot])
- return 'http://' + self.get_ip_iotbox() + ':8069'
+ subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server.sh'), url, iotname, token, reboot])
+ return 'http://' + get_ip() + ':8069'
@http.route('/steps', type='http', auth='none', cors='*', csrf=False)
def step_by_step_configure_page(self):
@@ -293,7 +248,7 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
'breadcrumb': 'Configure IoT Box',
'loading_message': 'Configuring your IoT Box',
'ssid': self.get_wifi_essid(),
- 'server': self.get_server_status(),
+ 'server': get_odoo_server_url(),
'hostname': subprocess.check_output('hostname').decode('utf-8').strip('\n'),
})
@@ -304,7 +259,7 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
token = token.split('|')[1]
else:
url = ''
- subprocess.call(['/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/connect_to_server_wifi.sh',url, iotname, token, essid, password, persistent])
+ subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server_wifi.sh'), url, iotname, token, essid, password, persistent])
return url
# Set server address
@@ -314,12 +269,19 @@ class IoTboxHomepage(odoo.addons.web.controllers.main.Home):
'title': 'IoT -> Odoo server configuration',
'breadcrumb': 'Configure Odoo Server',
'hostname': subprocess.check_output('hostname').decode('utf-8').strip('\n'),
- 'server_status': self.get_server_status() or 'Not configured yet',
+ 'server_status': get_odoo_server_url() or 'Not configured yet',
'loading_message': 'Configure Domain Server'
})
@http.route('/remote_connect', type='http', auth='none', cors='*')
def remote_connect(self):
+ """
+ Establish a link with a customer box trough internet with a ssh tunnel
+ 1 - take a new auth_token on https://dashboard.ngrok.com/
+ 2 - copy past this auth_token on the IoT Box : http://IoT_Box:8069/remote_connect
+ 3 - check on ngrok the port and url to get access to the box
+ 4 - you can connect to the box with this command : ssh -p port -v pi@url
+ """
return remote_connect_template.render({
'title': 'Remote debugging',
'breadcrumb': 'Remote Debugging',
diff --git a/addons/hw_posbox_homepage/views/homepage.html b/addons/hw_posbox_homepage/views/homepage.html
index 71c0f26c565..b14573e9805 100644
--- a/addons/hw_posbox_homepage/views/homepage.html
+++ b/addons/hw_posbox_homepage/views/homepage.html
@@ -28,6 +28,7 @@
}
.device-status .message {
font-size: 0.8rem;
+ max-width: 350px;
}
.device-status .indicator {
margin-left: 4px;
@@ -90,7 +91,7 @@
| Version |
- V18.12 update |
+ V19.04 update |
| IP Address |
diff --git a/addons/hw_posbox_homepage/views/remote_connect.html b/addons/hw_posbox_homepage/views/remote_connect.html
index e83ced4653c..ab9628ec36a 100644
--- a/addons/hw_posbox_homepage/views/remote_connect.html
+++ b/addons/hw_posbox_homepage/views/remote_connect.html
@@ -31,7 +31,7 @@
{% block content %}
Remote Debugging
- This allows someone to gain remote access to your IoT Box, and
+ This allows someone who give a ngrok authtoken to gain remote access to your IoT Box, and
thus your entire local network. Only enable this for someone
you trust.
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/cups/cupsd.conf b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/cups/cupsd.conf
index ee6cbbf93d4..cefc3cf06ab 100644
--- a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/cups/cupsd.conf
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/cups/cupsd.conf
@@ -20,6 +20,9 @@ Listen /run/cups/cups.sock
Browsing On
BrowseLocalProtocols dnssd
+# Local printers are not shared by default
+DefaultShared No
+
# Default authentication type, when authentication is required...
DefaultAuthType Basic
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/nginx/sites-enabled/default b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/nginx/sites-enabled/default
index cd95a43753a..fc802829d69 100644
--- a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/nginx/sites-enabled/default
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/nginx/sites-enabled/default
@@ -85,7 +85,7 @@ server {
root /var/www/;
index index.html;
#
-# location / {
-# try_files $uri $uri/ =404;
-# }
+ location /hw_drivers/ {
+ proxy_pass http://127.0.0.1:8069;
+ }
}
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
index 96ea352c761..d7b5349a74f 100755
--- 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
@@ -15,6 +15,7 @@ if [ -e /home/pi/odoo-remote-server.conf ]
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
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/home/pi/odoo/addons/point_of_sale/__manifest__.py b/addons/point_of_sale/tools/posbox/overwrite_after_init/home/pi/odoo/addons/point_of_sale/__manifest__.py
new file mode 100644
index 00000000000..67dee8c60db
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/home/pi/odoo/addons/point_of_sale/__manifest__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/local/lib/python3.5/dist-packages/v4l2.py.iotpatch b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/local/lib/python3.5/dist-packages/v4l2.py.iotpatch
new file mode 100644
index 00000000000..5cc87a78c88
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/local/lib/python3.5/dist-packages/v4l2.py.iotpatch
@@ -0,0 +1,20 @@
+--- v4l2_old.py 2019-01-23 12:49:01.081564000 +0100
++++ v4l2_new.py 2019-01-23 12:50:32.369730171 +0100
+@@ -194,7 +194,7 @@
+ V4L2_BUF_TYPE_SLICED_VBI_OUTPUT,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY,
+ V4L2_BUF_TYPE_PRIVATE,
+-) = range(1, 9) + [0x80]
++) = list(range(1, 9)) + [0x80]
+
+
+ v4l2_ctrl_type = enum
+@@ -245,7 +245,7 @@
+ V4L2_PRIORITY_INTERACTIVE,
+ V4L2_PRIORITY_RECORD,
+ V4L2_PRIORITY_DEFAULT,
+-) = range(0, 4) + [2]
++) = list(range(0, 4)) + [2]
+
+
+ class v4l2_rect(ctypes.Structure):
diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh
index 85c1933f096..53ab15a8748 100755
--- a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh
+++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh
@@ -28,6 +28,8 @@ PKGS_TO_INSTALL="
cups \
printer-driver-all \
cups-ipp-utils \
+ libcups2-dev \
+ pcscd \
localepurge \
vim \
mc \
@@ -37,6 +39,7 @@ PKGS_TO_INSTALL="
hostapd \
git \
rsync \
+ swig \
console-data \
lightdm \
xserver-xorg-video-fbdev \
@@ -49,7 +52,9 @@ PKGS_TO_INSTALL="
rpi-update \
adduser \
postgresql \
+ python-cups \
python3 \
+ python3-pyscard \
python3-urllib3 \
python3-dateutil \
python3-decorator \
@@ -104,9 +109,14 @@ rm -rf /usr/share/doc
# this may be fixed with libusb>2:1.0.11-1, but that's the most recent one in raspbian
# so we install the latest pyusb that works with this libusb.
# Even in stretch, we had an error with langid (but worked otherwise)
-pip3 install pyusb==1.0.0b1
-pip3 install evdev
-pip3 install gatt
+PIP_TO_INSTALL="
+ pyusb==1.0.0b1 \
+ evdev \
+ gatt \
+ v4l2 \
+ pycups"
+
+pip3 install ${PIP_TO_INSTALL}
groupadd usbusers
@@ -148,6 +158,7 @@ if [ $SYSTEMD -eq 1 ]; then
systemctl set-default graphical.target
ln -fs /etc/systemd/system/autologin@.service /etc/systemd/system/getty.target.wants/getty@tty1.service
rm /etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service
+ rm /etc/systemd/system/hostapd.service
else
update-rc.d lightdm enable 2
fi
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 0c517da73d7..6401444200c 100755
--- a/addons/point_of_sale/tools/posbox/posbox_create_image.sh
+++ b/addons/point_of_sale/tools/posbox/posbox_create_image.sh
@@ -120,6 +120,14 @@ qemu-system-arm "${QEMU_OPTS[@]}"
mount "${LOOP_MAPPER_PATH}" "${MOUNT_POINT}"
cp -av "${OVERWRITE_FILES_AFTER_INIT_DIR}"/* "${MOUNT_POINT}"
+find "${MOUNT_POINT}"/usr -type f -name "*.iotpatch"|while read iotpatch; do
+ DIR=$(dirname "${iotpatch}")
+ BASE=$(basename "${iotpatch%.iotpatch}")
+ find "${DIR}" -type f -name "${BASE}" ! -name "*.iotpatch"|while read file; do
+ patch -f "${file}" < "${iotpatch}"
+ done
+done
+
# cleanup
sleep 2
umount "${MOUNT_POINT}"
diff --git a/addons/point_of_sale/tools/posbox/posbox_download_images.sh b/addons/point_of_sale/tools/posbox/posbox_download_images.sh
index b13505ce16b..939e9879c2c 100755
--- a/addons/point_of_sale/tools/posbox/posbox_download_images.sh
+++ b/addons/point_of_sale/tools/posbox/posbox_download_images.sh
@@ -1,8 +1,7 @@
#!/bin/sh
-wget 'http://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-06-29/2018-06-27-raspbian-stretch-lite.zip' -O raspbian.img.zip
+wget 'http://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-11-15/2018-11-13-raspbian-stretch-lite.zip' -O raspbian.img.zip
unzip raspbian.img.zip
-wget 'https://github.com/dhruvvyas90/qemu-rpi-kernel/raw/master/kernel-qemu-4.9.59-stretch' -O kernel-qemu
+wget 'https://github.com/dhruvvyas90/qemu-rpi-kernel/raw/master/kernel-qemu-4.14.79-stretch' -O kernel-qemu
wget 'https://github.com/dhruvvyas90/qemu-rpi-kernel/raw/master/versatile-pb.dtb'
-