Files
odoo_source/addons/hw_drivers/controllers/driver.py
T

433 lines
17 KiB
Python
Executable File

#!/usr/bin/python3
import logging
import time
from threading import Thread
import usb
import gatt
import subprocess
import netifaces as ni
import json
import re
from odoo import http
import urllib3
from odoo.http import request as httprequest
import datetime
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(name)s: %(message)s')
_logger = logging.getLogger('dispatcher')
owner_dict = {}
last_ping = {}
class StatusController(http.Controller):
@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/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'
#----------------------------------------------------------
# Driver common interface
#----------------------------------------------------------
class Driver(Thread):
ping_value = ""
def supported(self):
pass
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):
def __new__(cls, clsname, bases, attrs):
newclass = super(UsbMetaClass, cls).__new__(cls, clsname, bases, attrs)
usbdrivers.append(newclass)
return newclass
class USBDriver(Driver,metaclass=UsbMetaClass):
def __init__(self, dev):
super(USBDriver, self).__init__()
self.dev = dev
self.value = ""
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
#----------------------------------------------------------
# Bluetooth
#----------------------------------------------------------
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)
class BtManager(Thread):
gatt_manager = False
def run(self):
dm = GattBtManager(adapter_name='hci0')
self.gatt_manager = dm
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)