[IMP] hw_drivers: Easier detection of new devices
Until now, to detect new types of devices on the IoT Box, we had to modify driver.py. If partners wanted to connect a new type of device, they had to modify directly the files on the Box, meaning that after each new build they had to re-apply all their modifications. We remove the loops that detected the connected devices and replace them by `Interfaces`. All interfaces will be loaded from the connected Odoo instance with the corresponding drivers. TaskID: 2082282
This commit is contained in:
@@ -1,27 +1,18 @@
|
||||
#!/usr/bin/python3
|
||||
import logging
|
||||
import time
|
||||
from threading import Thread, Event, Lock
|
||||
from usb import core
|
||||
from gatt import DeviceManager as Gatt_DeviceManager
|
||||
import subprocess
|
||||
from base64 import b64decode
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
from importlib import util
|
||||
import json
|
||||
from re import sub, finditer
|
||||
import urllib3
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
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
|
||||
import socket
|
||||
import ctypes
|
||||
from threading import Thread, Event
|
||||
import time
|
||||
import urllib3
|
||||
|
||||
from odoo import http, tools, _
|
||||
from odoo import http, tools
|
||||
from odoo.http import send_file
|
||||
from odoo.modules.module import get_resource_path
|
||||
from odoo.addons.hw_drivers.tools import helpers
|
||||
@@ -91,7 +82,7 @@ class StatusController(http.Controller):
|
||||
subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server.sh'), url, '', token, 'noreboot'])
|
||||
helpers.check_certificate()
|
||||
m.send_alldevices()
|
||||
m.load_drivers()
|
||||
m.load_iot_handlers()
|
||||
image = get_resource_path('hw_drivers', 'static/img', 'True.jpg')
|
||||
except subprocess.CalledProcessError as e:
|
||||
_logger.error('A error encountered : %s ' % e.output)
|
||||
@@ -135,10 +126,56 @@ sys.stderr = ExceptionLogger()
|
||||
#----------------------------------------------------------
|
||||
|
||||
drivers = []
|
||||
bt_devices = {}
|
||||
socket_devices = {}
|
||||
interfaces = {}
|
||||
iot_devices = {}
|
||||
|
||||
class InterfaceMetaClass(type):
|
||||
def __new__(cls, clsname, bases, attrs):
|
||||
if clsname in interfaces:
|
||||
return interfaces[clsname]
|
||||
new_interface = super(InterfaceMetaClass, cls).__new__(cls, clsname, bases, attrs)
|
||||
interfaces[clsname] = new_interface
|
||||
return new_interface
|
||||
|
||||
class Interface(Thread, metaclass=InterfaceMetaClass):
|
||||
_loop_delay = 3 # Delay (in seconds) between calls to get_devices or 0 if it should be called only once
|
||||
_detected_devices = {}
|
||||
connection_type = ''
|
||||
|
||||
def __init__(self):
|
||||
super(Interface, self).__init__()
|
||||
self.drivers = [d for d in drivers if d.connection_type == self.connection_type]
|
||||
|
||||
def run(self):
|
||||
while self.connection_type and self.drivers:
|
||||
self.update_iot_devices(self.get_devices())
|
||||
if not self._loop_delay:
|
||||
break
|
||||
time.sleep(self._loop_delay)
|
||||
|
||||
def update_iot_devices(self, devices={}):
|
||||
added = devices.keys() - self._detected_devices
|
||||
removed = self._detected_devices - devices.keys()
|
||||
self._detected_devices = devices.keys()
|
||||
|
||||
for path in removed:
|
||||
if path in iot_devices:
|
||||
iot_devices[path].disconnect()
|
||||
_logger.info('Device %s is now disconnected', path)
|
||||
|
||||
for path in added:
|
||||
for driver in self.drivers:
|
||||
if driver.supported(device=devices[path]):
|
||||
_logger.info('Device %s is now connected', path)
|
||||
d = driver(device=devices[path])
|
||||
d.daemon = True
|
||||
d.start()
|
||||
iot_devices[path] = d
|
||||
break
|
||||
|
||||
def get_devices(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
class DriverMetaClass(type):
|
||||
def __new__(cls, clsname, bases, attrs):
|
||||
newclass = super(DriverMetaClass, cls).__new__(cls, clsname, bases, attrs)
|
||||
@@ -160,7 +197,6 @@ class Driver(Thread, metaclass=DriverMetaClass):
|
||||
super(Driver, self).__init__()
|
||||
self.dev = device
|
||||
self.data = {'value': ''}
|
||||
self.gatt_device = False
|
||||
self._device_manufacturer = ''
|
||||
|
||||
@property
|
||||
@@ -253,12 +289,6 @@ class EventManager(object):
|
||||
self.sessions[session]['event'].set()
|
||||
|
||||
|
||||
class IoTDevice(object):
|
||||
|
||||
def __init__(self, dev, connection_type):
|
||||
self.dev = dev
|
||||
self.connection_type = connection_type
|
||||
|
||||
event_manager = EventManager()
|
||||
|
||||
|
||||
@@ -268,21 +298,22 @@ event_manager = EventManager()
|
||||
|
||||
class Manager(Thread):
|
||||
|
||||
def load_drivers(self):
|
||||
def load_iot_handlers(self):
|
||||
"""
|
||||
This method loads local files: 'odoo/addons/hw_drivers/drivers'
|
||||
And execute these python drivers
|
||||
This method loads local files: 'odoo/addons/hw_drivers/iot_handlers/drivers' and
|
||||
'odoo/addons/hw_drivers/iot_handlers/interfaces'
|
||||
And execute these python drivers and interfaces
|
||||
"""
|
||||
helpers.download_drivers()
|
||||
path = get_resource_path('hw_drivers', 'drivers')
|
||||
driversList = os.listdir(path)
|
||||
self.devices = {}
|
||||
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)
|
||||
helpers.download_iot_handlers()
|
||||
for directory in ['interfaces', 'drivers']:
|
||||
path = get_resource_path('hw_drivers', 'iot_handlers', directory)
|
||||
filesList = os.listdir(path)
|
||||
for file in filesList:
|
||||
path_file = os.path.join(path, file)
|
||||
spec = util.spec_from_file_location(file, path_file)
|
||||
if spec:
|
||||
module = util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
http.addons_manifest = {}
|
||||
http.root = http.Root()
|
||||
|
||||
@@ -335,222 +366,28 @@ class Manager(Thread):
|
||||
else:
|
||||
_logger.warning('Odoo server not set')
|
||||
|
||||
def get_connected_displays(self):
|
||||
display_devices = {}
|
||||
|
||||
displays = subprocess.check_output(['tvservice', '-l']).decode()
|
||||
x_screen = 0
|
||||
for match in finditer('Display Number (\d), type HDMI (\d)', displays):
|
||||
display_id, hdmi_id = match.groups()
|
||||
tvservice_output = subprocess.check_output(['tvservice', '-nv', display_id]).decode().rstrip()
|
||||
if tvservice_output:
|
||||
display_name = tvservice_output.split('=')[1]
|
||||
display_identifier = sub('[^a-zA-Z0-9 ]+', '', display_name).replace(' ', '_') + "_" + str(hdmi_id)
|
||||
iot_device = IoTDevice({
|
||||
'identifier': display_identifier,
|
||||
'name': display_name,
|
||||
'x_screen': str(x_screen),
|
||||
}, 'display')
|
||||
display_devices[display_identifier] = iot_device
|
||||
x_screen += 1
|
||||
|
||||
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/*'):
|
||||
iot_device = IoTDevice({'identifier': identifier, }, 'serial')
|
||||
serial_devices[identifier] = iot_device
|
||||
return serial_devices
|
||||
|
||||
def usb_loop(self):
|
||||
"""
|
||||
Loops over the connected usb devices, assign them an identifier, instantiate
|
||||
an `IoTDevice` for them.
|
||||
|
||||
USB devices are identified by a combination of their `idVendor` and
|
||||
`idProduct`. We can't be sure this combination in unique per equipment.
|
||||
To still allow connecting multiple similar equipments, we complete the
|
||||
identifier by a counter. The drawbacks are we can't be sure the equipments
|
||||
will get the same identifiers after a reboot or a disconnect/reconnect.
|
||||
|
||||
:return: a dict of the `IoTDevices` instances indexed by their identifier.
|
||||
"""
|
||||
usb_devices = {}
|
||||
devs = core.find(find_all=True)
|
||||
cpt = 2
|
||||
for dev in devs:
|
||||
dev.identifier = "usb_%04x:%04x" % (dev.idVendor, dev.idProduct)
|
||||
if dev.identifier in usb_devices:
|
||||
dev.identifier += '_%s' % cpt
|
||||
cpt += 1
|
||||
iot_device = IoTDevice(dev, 'usb')
|
||||
usb_devices[dev.identifier] = 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
|
||||
dev.identifier = dev.bus_info.decode('utf-8')
|
||||
iot_device = IoTDevice(dev, 'video')
|
||||
camera_devices[dev.identifier] = iot_device
|
||||
return camera_devices
|
||||
|
||||
def printer_loop(self):
|
||||
printer_devices = {}
|
||||
with cups_lock:
|
||||
devices = conn.getDevices()
|
||||
for path in devices:
|
||||
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
|
||||
"""
|
||||
helpers.check_git_branch()
|
||||
helpers.check_certificate()
|
||||
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
|
||||
self.load_iot_handlers()
|
||||
self.previous_iot_devices = []
|
||||
for interface in interfaces.values():
|
||||
i = interface()
|
||||
i.daemon = True
|
||||
i.start()
|
||||
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())
|
||||
if cpt % 40 == 0:
|
||||
printer_devices = self.printer_loop()
|
||||
cpt = 0
|
||||
updated_devices.update(printer_devices)
|
||||
cpt += 1
|
||||
added = updated_devices.keys() - self.devices.keys()
|
||||
removed = self.devices.keys() - updated_devices.keys()
|
||||
self.devices = updated_devices
|
||||
send_devices = False
|
||||
for path in [device_rm for device_rm in removed if device_rm in iot_devices]:
|
||||
iot_devices[path].disconnect()
|
||||
_logger.info('Device %s is now disconnected', path)
|
||||
send_devices = True
|
||||
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 == self.devices[path].connection_type]:
|
||||
if driverclass.supported(device = updated_devices[path].dev):
|
||||
_logger.info('Device %s is now connected', path)
|
||||
d = driverclass(device = updated_devices[path].dev)
|
||||
d.daemon = True
|
||||
d.start()
|
||||
iot_devices[path] = d
|
||||
send_devices = True
|
||||
break
|
||||
if send_devices:
|
||||
if iot_devices != self.previous_iot_devices:
|
||||
self.send_alldevices()
|
||||
self.previous_iot_devices = iot_devices.copy()
|
||||
time.sleep(3)
|
||||
|
||||
class GattBtManager(Gatt_DeviceManager):
|
||||
|
||||
def device_discovered(self, device):
|
||||
path = "bt_%s" % (device.mac_address,)
|
||||
if path not in bt_devices:
|
||||
device.manager = self
|
||||
device.identifier = path
|
||||
iot_device = IoTDevice(device, 'bluetooth')
|
||||
bt_devices[path] = iot_device
|
||||
|
||||
class BtManager(Thread):
|
||||
|
||||
def run(self):
|
||||
dm = GattBtManager(adapter_name='hci0')
|
||||
for device in [device_con for device_con in dm.devices() if device_con.is_connected()]:
|
||||
device.disconnect()
|
||||
dm.start_discovery()
|
||||
dm.run()
|
||||
|
||||
class SocketManager(Thread):
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.bind(('', 9000))
|
||||
sock.listen(1)
|
||||
dev, addr = sock.accept()
|
||||
if addr and addr[0] not in socket_devices:
|
||||
iot_device = IoTDevice(type('', (), {'dev': dev}), 'socket')
|
||||
socket_devices[addr[0]] = iot_device
|
||||
except OSError as e:
|
||||
_logger.error(_('Error in SocketManager: %s') % (e.strerror))
|
||||
|
||||
class MPDManager(Thread):
|
||||
def __init__(self):
|
||||
super(MPDManager, self).__init__()
|
||||
self.devices = {}
|
||||
self.mpd_session = ctypes.c_void_p()
|
||||
|
||||
def run(self):
|
||||
eftapi.EFT_CreateSession(ctypes.byref(self.mpd_session))
|
||||
while True:
|
||||
if self.terminal_connected(terminal_id):
|
||||
self.devices[terminal_id] = IoTDevice(terminal_id, 'mpd')
|
||||
elif terminal_id in self.devices:
|
||||
self.devices = {}
|
||||
time.sleep(20)
|
||||
|
||||
def terminal_connected(self, terminal_id):
|
||||
eftapi.EFT_QueryStatus(self.mpd_session)
|
||||
device_status = ctypes.c_long()
|
||||
eftapi.EFT_GetDeviceStatusCode(self.mpd_session, ctypes.byref(device_status))
|
||||
return device_status.value in [0, 1]
|
||||
|
||||
|
||||
conn = cups_connection()
|
||||
PPDs = conn.getPPDs()
|
||||
printers = conn.getPrinters()
|
||||
cups_lock = Lock() # We can only make one call to Cups at a time
|
||||
|
||||
mpdm = MPDManager()
|
||||
terminal_id = helpers.read_file_first_line('odoo-six-payment-terminal.conf')
|
||||
subprocess.call(["pkill", "-9", "eftdvs"]) # Kill any running MPD server
|
||||
if terminal_id:
|
||||
subprocess.Popen(["eftdvs", "/ConfigDir", "/usr/share/eftdvs/", "/TID", terminal_id]) # Start MPD server
|
||||
eftapi = ctypes.CDLL("eftapi.so") # Library given by Six
|
||||
mpdm.daemon = True
|
||||
mpdm.start()
|
||||
# Must be started from main thread
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
|
||||
m = Manager()
|
||||
m.daemon = True
|
||||
m.start()
|
||||
|
||||
bm = BtManager()
|
||||
bm.daemon = True
|
||||
bm.start()
|
||||
|
||||
sm = SocketManager()
|
||||
sm.daemon = True
|
||||
sm.start()
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ 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'))
|
||||
path = os.path.realpath(os.path.join(os.path.dirname(__file__), '../../views'))
|
||||
loader = jinja2.FileSystemLoader(path)
|
||||
|
||||
jinja_env = jinja2.Environment(loader=loader, autoescape=True)
|
||||
@@ -188,7 +188,7 @@ class DisplayController(http.Controller):
|
||||
cust_js = None
|
||||
interfaces = ni.interfaces()
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "../static/src/js/worker.js")) as js:
|
||||
with open(os.path.join(os.path.dirname(__file__), "../../static/src/js/worker.js")) as js:
|
||||
cust_js = js.read()
|
||||
|
||||
display_ifaces = []
|
||||
+2
-1
@@ -15,7 +15,8 @@ import tempfile
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
from odoo import http, _
|
||||
from odoo.addons.hw_drivers.controllers.driver import event_manager, Driver, PPDs, conn, printers, cups_lock, iot_devices
|
||||
from odoo.addons.hw_drivers.controllers.driver import event_manager, Driver, iot_devices
|
||||
from odoo.addons.hw_drivers.iot_handlers.interfaces.PrinterInterface import PPDs, conn, printers, cups_lock
|
||||
from odoo.addons.hw_drivers.tools import helpers
|
||||
from odoo.addons.hw_proxy.controllers.main import drivers as old_drivers
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import serial
|
||||
from odoo import http
|
||||
from odoo.addons.hw_proxy.controllers.main import drivers as old_drivers
|
||||
from odoo.addons.hw_drivers.controllers.driver import event_manager
|
||||
from odoo.addons.hw_drivers.drivers.SerialBaseDriver import SerialDriver, SerialProtocol, serial_connection
|
||||
from odoo.addons.hw_drivers.iot_handlers.drivers.SerialBaseDriver import SerialDriver, SerialProtocol, serial_connection
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -0,0 +1,36 @@
|
||||
from re import sub, finditer
|
||||
import subprocess
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import Interface
|
||||
|
||||
|
||||
class DisplayInterface(Interface):
|
||||
_loop_delay = 0
|
||||
connection_type = 'display'
|
||||
|
||||
def get_devices(self):
|
||||
display_devices = {}
|
||||
displays = subprocess.check_output(['tvservice', '-l']).decode()
|
||||
x_screen = 0
|
||||
for match in finditer('Display Number (\d), type HDMI (\d)', displays):
|
||||
display_id, hdmi_id = match.groups()
|
||||
tvservice_output = subprocess.check_output(['tvservice', '-nv', display_id]).decode().strip()
|
||||
if tvservice_output:
|
||||
display_name = tvservice_output.split('=')[1]
|
||||
display_identifier = sub('[^a-zA-Z0-9 ]+', '', display_name).replace(' ', '_') + "_" + str(hdmi_id)
|
||||
iot_device = {
|
||||
'identifier': display_identifier,
|
||||
'name': display_name,
|
||||
'x_screen': str(x_screen),
|
||||
}
|
||||
display_devices[display_identifier] = iot_device
|
||||
x_screen += 1
|
||||
|
||||
if not len(display_devices):
|
||||
# No display connected, create "fake" device to be accessed from another computer
|
||||
display_devices['distant_display'] = {
|
||||
'identifier': "distant_display",
|
||||
'name': "Distant Display",
|
||||
}
|
||||
|
||||
return display_devices
|
||||
@@ -0,0 +1,30 @@
|
||||
from cups import Connection as cups_connection
|
||||
from re import sub
|
||||
from threading import Lock
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import Interface
|
||||
|
||||
conn = cups_connection()
|
||||
PPDs = conn.getPPDs()
|
||||
printers = conn.getPrinters()
|
||||
cups_lock = Lock() # We can only make one call to Cups at a time
|
||||
|
||||
class PrinterInterface(Interface):
|
||||
_loop_delay = 120
|
||||
connection_type = 'printer'
|
||||
|
||||
def get_devices(self):
|
||||
printer_devices = {}
|
||||
with cups_lock:
|
||||
devices = conn.getDevices()
|
||||
for path in devices:
|
||||
if 'uuid=' in path:
|
||||
identifier = sub('[^a-zA-Z0-9 ]+', '', path.split('uuid=')[1])
|
||||
elif 'serial=' in path:
|
||||
identifier = sub('[^a-zA-Z0-9 ]+', '', path.split('serial=')[1])
|
||||
else:
|
||||
identifier = sub('[^a-zA-Z0-9 ]+', '', path)
|
||||
devices[path]['identifier'] = identifier
|
||||
devices[path]['url'] = path
|
||||
printer_devices[identifier] = devices[path]
|
||||
return printer_devices
|
||||
@@ -0,0 +1,15 @@
|
||||
from glob import glob
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import Interface
|
||||
|
||||
|
||||
class SerialInterface(Interface):
|
||||
connection_type = 'serial'
|
||||
|
||||
def get_devices(self):
|
||||
serial_devices = {}
|
||||
for identifier in glob('/dev/serial/by-path/*'):
|
||||
serial_devices[identifier] = {
|
||||
'identifier': identifier
|
||||
}
|
||||
return serial_devices
|
||||
@@ -0,0 +1,26 @@
|
||||
from usb import core
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import Interface
|
||||
|
||||
|
||||
class USBInterface(Interface):
|
||||
connection_type = 'usb'
|
||||
|
||||
def get_devices(self):
|
||||
"""
|
||||
USB devices are identified by a combination of their `idVendor` and
|
||||
`idProduct`. We can't be sure this combination in unique per equipment.
|
||||
To still allow connecting multiple similar equipments, we complete the
|
||||
identifier by a counter. The drawbacks are we can't be sure the equipments
|
||||
will get the same identifiers after a reboot or a disconnect/reconnect.
|
||||
"""
|
||||
usb_devices = {}
|
||||
devs = core.find(find_all=True)
|
||||
cpt = 2
|
||||
for dev in devs:
|
||||
dev.identifier = "usb_%04x:%04x" % (dev.idVendor, dev.idProduct)
|
||||
if dev.identifier in usb_devices:
|
||||
dev.identifier += '_%s' % cpt
|
||||
cpt += 1
|
||||
usb_devices[dev.identifier] = dev
|
||||
return usb_devices
|
||||
@@ -79,7 +79,8 @@ def check_git_branch():
|
||||
|
||||
if db_branch != local_branch:
|
||||
subprocess.check_call(["sudo", "mount", "-o", "remount,rw", "/"])
|
||||
subprocess.check_call(["rm", "/home/pi/odoo/addons/hw_drivers/drivers/*"])
|
||||
subprocess.check_call(["rm", "/home/pi/odoo/addons/hw_drivers/iot_handlers/drivers/*"])
|
||||
subprocess.check_call(["rm", "/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')
|
||||
@@ -195,7 +196,7 @@ def load_certificate():
|
||||
subprocess.check_call(["sudo", "mount", "-o", "remount,rw", "/root_bypass_ramdisks/etc/cups"])
|
||||
subprocess.check_call(["sudo", "service", "nginx", "restart"])
|
||||
|
||||
def download_drivers(auto=True):
|
||||
def download_iot_handlers(auto=True):
|
||||
"""
|
||||
Get the drivers from the configured Odoo server
|
||||
"""
|
||||
@@ -203,13 +204,13 @@ def download_drivers(auto=True):
|
||||
if server:
|
||||
urllib3.disable_warnings()
|
||||
pm = urllib3.PoolManager(cert_reqs='CERT_NONE')
|
||||
server = server + '/iot/get_drivers'
|
||||
server = server + '/iot/get_handlers'
|
||||
try:
|
||||
resp = pm.request('POST', server, fields={'mac': get_mac_address(), 'auto': auto})
|
||||
if resp.data:
|
||||
subprocess.check_call(["sudo", "mount", "-o", "remount,rw", "/"])
|
||||
zip_file = zipfile.ZipFile(io.BytesIO(resp.data))
|
||||
zip_file.extractall(get_resource_path('hw_drivers', 'drivers'))
|
||||
zip_file.extractall(get_resource_path('hw_drivers', 'iot_handlers'))
|
||||
subprocess.check_call(["sudo", "mount", "-o", "remount,ro", "/"])
|
||||
subprocess.check_call(["sudo", "mount", "-o", "remount,rw", "/root_bypass_ramdisks/etc/cups"])
|
||||
except Exception as e:
|
||||
|
||||
@@ -42,7 +42,7 @@ jinja_env.filters["json"] = json.dumps
|
||||
homepage_template = jinja_env.get_template('homepage.html')
|
||||
server_config_template = jinja_env.get_template('server_config.html')
|
||||
wifi_config_template = jinja_env.get_template('wifi_config.html')
|
||||
driver_list_template = jinja_env.get_template('driver_list.html')
|
||||
handler_list_template = jinja_env.get_template('handler_list.html')
|
||||
remote_connect_template = jinja_env.get_template('remote_connect.html')
|
||||
configure_wizard_template = jinja_env.get_template('configure_wizard.html')
|
||||
six_payment_terminal_template = jinja_env.get_template('six_payment_terminal.html')
|
||||
@@ -103,24 +103,29 @@ class IoTboxHomepage(web.Home):
|
||||
else:
|
||||
return homepage_template.render(self.get_homepage_data())
|
||||
|
||||
@http.route('/list_drivers', type='http', auth='none', website=True)
|
||||
def list_drivers(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', 'drivers')):
|
||||
for driver in os.listdir(get_resource_path('hw_drivers', 'iot_handlers/drivers')):
|
||||
if driver != '__pycache__':
|
||||
drivers_list.append(driver)
|
||||
return driver_list_template.render({
|
||||
'title': "Odoo's IoT Box - Drivers list",
|
||||
'breadcrumb': 'Drivers list',
|
||||
interfaces_list = []
|
||||
for interface in os.listdir(get_resource_path('hw_drivers', 'iot_handlers/interfaces')):
|
||||
if interface != '__pycache__':
|
||||
interfaces_list.append(interface)
|
||||
return handler_list_template.render({
|
||||
'title': "Odoo's IoT Box - Handlers list",
|
||||
'breadcrumb': 'Handlers list',
|
||||
'drivers_list': drivers_list,
|
||||
'interfaces_list': interfaces_list,
|
||||
'server': helpers.get_odoo_server_url()
|
||||
})
|
||||
|
||||
@http.route('/load_drivers', type='http', auth='none', website=True)
|
||||
def load_drivers(self):
|
||||
helpers.download_drivers(False)
|
||||
@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"])
|
||||
return "<meta http-equiv='refresh' content='20; url=http://" + helpers.get_ip() + ":8069/list_drivers'>"
|
||||
return "<meta http-equiv='refresh' content='20; url=http://" + helpers.get_ip() + ":8069/list_handlers'>"
|
||||
|
||||
@http.route('/list_credential', type='http', auth='none', website=True)
|
||||
def list_credential(self):
|
||||
@@ -188,12 +193,13 @@ class IoTboxHomepage(web.Home):
|
||||
helpers.unlink_file('odoo-remote-server.conf')
|
||||
return "<meta http-equiv='refresh' content='0; url=http://" + helpers.get_ip() + ":8069'>"
|
||||
|
||||
@http.route('/drivers_clear', type='http', auth='none', cors='*', csrf=False)
|
||||
def clear_drivers_list(self):
|
||||
for driver in os.listdir(get_resource_path('hw_drivers', 'drivers')):
|
||||
if driver != '__pycache__':
|
||||
helpers.unlink_file(get_resource_path('hw_drivers', 'drivers', driver))
|
||||
return "<meta http-equiv='refresh' content='0; url=http://" + helpers.get_ip() + ":8069/list_drivers'>"
|
||||
@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))
|
||||
return "<meta http-equiv='refresh' content='0; url=http://" + helpers.get_ip() + ":8069/list_handlers'>"
|
||||
|
||||
@http.route('/server_connect', type='http', auth='none', cors='*', csrf=False)
|
||||
def connect_to_server(self, token, iotname):
|
||||
|
||||
+16
-10
@@ -3,42 +3,48 @@
|
||||
{% block head %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#load_driver_btn').on('click', function(e){
|
||||
$('#load_handler_btn').on('click', function(e){
|
||||
e.preventDefault();
|
||||
$('.loading-block').removeClass('o_hide');
|
||||
$.ajax({
|
||||
url: '/load_drivers',
|
||||
url: '/load_iot_handlers',
|
||||
}).done(function () {
|
||||
$('.message-status').html('Driver loaded successfully <br> Refreshing page');
|
||||
$('.message-status').html('Handlers loaded successfully <br> Refreshing page');
|
||||
setTimeout(function () {
|
||||
location.reload(true);
|
||||
}, 20000);
|
||||
}, 25000);
|
||||
}).fail(function () {
|
||||
setTimeout(function () {
|
||||
location.reload(true);
|
||||
}, 20000);
|
||||
}, 25000);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h2 class="text-center text-green">Interfaces list</h2>
|
||||
<table align="center" width="50%" cellpadding="3">
|
||||
{% for interfaces in interfaces_list -%}
|
||||
<tr><td>{{ interfaces }}</td></tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
<h2 class="text-center text-green">Drivers list</h2>
|
||||
<table align="center" cellpadding="3">
|
||||
<table align="center" width="50%" cellpadding="3">
|
||||
{% for driver in drivers_list -%}
|
||||
<tr><td>{{ driver }}</td></tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
{% if server %}
|
||||
<div style="margin-top: 20px;" class="text-center">
|
||||
<a id="load_driver_btn" class="btn" href='/load_drivers'>Load drivers</a>
|
||||
<a id="load_handler_btn" class="btn" href='/load_iot_handlers'>Load handlers</a>
|
||||
</div>
|
||||
<div class="text-center font-small" style="margin: 10px auto;">
|
||||
You can clear the drivers configuration
|
||||
<form style="display: inline-block;margin-left: 4px;" action='/drivers_clear'>
|
||||
You can clear the handlers configuration
|
||||
<form style="display: inline-block;margin-left: 4px;" action='/handlers_clear'>
|
||||
<input class="btn btn-sm" type="submit" value="Clear"/>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ loading_block_ui('Loading Drivers') }}
|
||||
{{ loading_block_ui('Loading Handlers') }}
|
||||
{% endblock %}
|
||||
@@ -137,7 +137,7 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<br><center><a class="btn btn-sm" href='/list_drivers'>drivers list</a></center>
|
||||
<br><center><a class="btn btn-sm" href='/list_handlers'>handlers list</a></center>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -168,7 +168,7 @@ echo "disable_overscan=1" >> /boot/config.txt
|
||||
sed -i '/dtoverlay/d' /boot/config.txt
|
||||
|
||||
# exclude /drivers folder from git info to be able to load specific drivers
|
||||
echo "addons/hw_drivers/drivers/" > /home/pi/odoo/.git/info/exclude
|
||||
echo "addons/hw_drivers/iot_devices/" > /home/pi/odoo/.git/info/exclude
|
||||
|
||||
# create dirs for ramdisks
|
||||
create_ramdisk_dir () {
|
||||
|
||||
Reference in New Issue
Block a user