[IMP] hw_*: IoT refactoring - With longpolling
Improve the implementation of IoT. Allow both-direction communication between iot box and devices. task: 1906910 closes odoo/odoo#30440 Signed-off-by: Christophe Simonis <chs@odoo.com>
This commit is contained in:
@@ -1,2 +1 @@
|
||||
from . import driver
|
||||
from . import loading
|
||||
from . import driver
|
||||
@@ -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 = "<html><head></head><body>List of drivers and values: <br/> <ul>"
|
||||
for path in drivers:
|
||||
result += "<li>" + path + ":" + str(drivers[path].value) + "</li>"
|
||||
result += "</ul>"
|
||||
result +=" </body></html>"
|
||||
return result
|
||||
|
||||
@http.route('/hw_drivers/driverdetails/<string:identifier>', 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/<string:identifier>', 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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 546 B |
Binary file not shown.
|
After Width: | Height: | Size: 542 B |
@@ -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 "<meta http-equiv='refresh' content='20; url=http://" + self.get_ip_iotbox() + ":8069/list_drivers'>"
|
||||
return "<meta http-equiv='refresh' content='20; url=http://" + get_ip() + ":8069/list_drivers'>"
|
||||
|
||||
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 "<meta http-equiv='refresh' content='0; url=http://" + self.get_ip_iotbox() + ":8069'>"
|
||||
return "<meta http-equiv='refresh' content='0; url=http://" + get_ip() + ":8069'>"
|
||||
|
||||
@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 "<meta http-equiv='refresh' content='0; url=http://" + self.get_ip_iotbox() + ":8069'>"
|
||||
return "<meta http-equiv='refresh' content='0; url=http://" + get_ip() + ":8069'>"
|
||||
|
||||
@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 "<meta http-equiv='refresh' content='0; url=http://" + self.get_ip_iotbox() + ":8069/list_drivers'>"
|
||||
return "<meta http-equiv='refresh' content='0; url=http://" + get_ip() + ":8069/list_drivers'>"
|
||||
|
||||
@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',
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
}
|
||||
.device-status .message {
|
||||
font-size: 0.8rem;
|
||||
max-width: 350px;
|
||||
}
|
||||
.device-status .indicator {
|
||||
margin-left: 4px;
|
||||
@@ -90,7 +91,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="heading">Version</td>
|
||||
<td>V18.12 <a class="btn btn-sm float-right" href='/hw_proxy/upgrade/'>update</a></td>
|
||||
<td>V19.04 <a class="btn btn-sm float-right" href='/hw_proxy/upgrade/'>update</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="heading">IP Address</td>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
{% block content %}
|
||||
<h2 class="text-center">Remote Debugging</h2>
|
||||
<p class='text-red'>
|
||||
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.
|
||||
</p>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
+20
@@ -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):
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user