[IMP] hw_*,point_of_sale: Cleaning IoT Box
- Split driver.py to multiple files - Remove hw_proxy module as most of its routes were not used and move remaingin routes to a controller in hw_drivers - Add missing coding and license lines at the beginning of files - Reorder imports and remove unused ones - Rename device_id to device_identifier for clarity - Rename screen to display for consistency - Other minors improvements Taskid: 1973956 X-original-commit: 841c016913a87133bf7257c62ae5f6bf7d99e06d
This commit is contained in:
@@ -1 +1,10 @@
|
||||
from . import controllers
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import connection_manager
|
||||
from . import controllers
|
||||
from . import driver
|
||||
from . import event_manager
|
||||
from . import exception_logger
|
||||
from . import interface
|
||||
from . import main
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
import subprocess
|
||||
import requests
|
||||
from threading import Thread
|
||||
import time
|
||||
import urllib3
|
||||
|
||||
from odoo.modules.module import get_resource_path
|
||||
from odoo.addons.hw_drivers.main import iot_devices, manager
|
||||
from odoo.addons.hw_drivers.tools import helpers
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class ConnectionManager(Thread):
|
||||
def __init__(self):
|
||||
super(ConnectionManager, self).__init__()
|
||||
self.pairing_code = False
|
||||
self.pairing_uuid = False
|
||||
|
||||
def run(self):
|
||||
if not helpers.get_odoo_server_url():
|
||||
end_time = datetime.now() + timedelta(minutes=5)
|
||||
while (datetime.now() < end_time):
|
||||
self._connect_box()
|
||||
time.sleep(10)
|
||||
self.pairing_code = False
|
||||
self.pairing_uuid = False
|
||||
self._refresh_displays()
|
||||
|
||||
def _connect_box(self):
|
||||
data = {
|
||||
'jsonrpc': 2.0,
|
||||
'params': {
|
||||
'pairing_code': self.pairing_code,
|
||||
'pairing_uuid': self.pairing_uuid,
|
||||
}
|
||||
}
|
||||
|
||||
urllib3.disable_warnings()
|
||||
req = requests.post('https://iot-proxy.odoo.com/odoo-enterprise/iot/connect-box', json=data, verify=False)
|
||||
result = req.json().get('result', {})
|
||||
|
||||
if all(key in result for key in ['pairing_code', 'pairing_uuid']):
|
||||
self.pairing_code = result['pairing_code']
|
||||
self.pairing_uuid = result['pairing_uuid']
|
||||
elif all(key in result for key in ['url', 'token', 'db_uuid', 'enterprise_code']):
|
||||
self._connect_to_server(result['url'], result['token'], result['db_uuid'], result['enterprise_code'])
|
||||
|
||||
def _connect_to_server(self, url, token, db_uuid, enterprise_code):
|
||||
if db_uuid and enterprise_code:
|
||||
helpers.add_credential(db_uuid, enterprise_code)
|
||||
|
||||
# Save DB URL and token
|
||||
subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server.sh'), url, '', token, 'noreboot'])
|
||||
# Notify the DB, so that the kanban view already shows the IoT Box
|
||||
manager.send_alldevices()
|
||||
# Restart to checkout the git branch, get a certificate, load the IoT handlers...
|
||||
subprocess.check_call(["sudo", "service", "odoo", "restart"])
|
||||
|
||||
def _refresh_displays(self):
|
||||
"""Refresh all displays to hide the pairing code"""
|
||||
for d in iot_devices:
|
||||
if iot_devices[d].device_type == 'display':
|
||||
iot_devices[d].action({
|
||||
'action': 'display_refresh'
|
||||
})
|
||||
|
||||
connection_manager = ConnectionManager()
|
||||
connection_manager.daemon = True
|
||||
connection_manager.start()
|
||||
@@ -1 +1,5 @@
|
||||
from . import driver
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import driver
|
||||
from . import proxy
|
||||
|
||||
@@ -1,40 +1,32 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from base64 import b64decode
|
||||
from datetime import datetime, timedelta
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
from importlib import util
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import requests
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
from threading import Thread, Event
|
||||
import time
|
||||
import urllib3
|
||||
|
||||
from odoo import http, tools
|
||||
from odoo.http import send_file
|
||||
from odoo.modules.module import get_resource_path
|
||||
|
||||
from odoo.addons.hw_drivers.event_manager import event_manager
|
||||
from odoo.addons.hw_drivers.main import iot_devices, manager
|
||||
from odoo.addons.hw_drivers.tools import helpers
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Controllers
|
||||
#----------------------------------------------------------
|
||||
|
||||
class StatusController(http.Controller):
|
||||
class DriverController(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):
|
||||
def action(self, session_id, device_identifier, 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)
|
||||
iot_device = iot_devices.get(device_identifier)
|
||||
if iot_device:
|
||||
iot_device.data['owner'] = session_id
|
||||
data = json.loads(data)
|
||||
@@ -53,7 +45,7 @@ class StatusController(http.Controller):
|
||||
@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
|
||||
listener is a dict in witch there are a sessions_id and a dict of device_identifier to listen
|
||||
"""
|
||||
req = event_manager.add_request(listener)
|
||||
if req['event'].wait(50):
|
||||
@@ -82,7 +74,7 @@ class StatusController(http.Controller):
|
||||
helpers.add_credential(db_uuid, enterprise_code)
|
||||
try:
|
||||
subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server.sh'), url, '', token, 'noreboot'])
|
||||
m.send_alldevices()
|
||||
manager.send_alldevices()
|
||||
image = get_resource_path('hw_drivers', 'static/img', 'True.jpg')
|
||||
helpers.odoo_restart(3)
|
||||
except subprocess.CalledProcessError as e:
|
||||
@@ -100,355 +92,3 @@ class StatusController(http.Controller):
|
||||
res = send_file(tools.config['logfile'], mimetype="text/plain", as_attachment=True)
|
||||
res.headers['Cache-Control'] = 'no-cache'
|
||||
return res
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Log Exceptions
|
||||
#----------------------------------------------------------
|
||||
|
||||
class ExceptionLogger:
|
||||
"""
|
||||
Redirect Exceptions to the logger to keep track of them in the log file.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger()
|
||||
|
||||
def write(self, message):
|
||||
if message != '\n':
|
||||
self.logger.error(message)
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
sys.stderr = ExceptionLogger()
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Drivers
|
||||
#----------------------------------------------------------
|
||||
|
||||
drivers = []
|
||||
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 = sorted([d for d in drivers if d.connection_type == self.connection_type], key=lambda d: d.priority, reverse=True)
|
||||
|
||||
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)
|
||||
if hasattr(newclass, 'priority'):
|
||||
newclass.priority += 1
|
||||
else:
|
||||
newclass.priority = 0
|
||||
drivers.append(newclass)
|
||||
return newclass
|
||||
|
||||
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._device_manufacturer = ''
|
||||
|
||||
@property
|
||||
def device_name(self):
|
||||
return self._device_name
|
||||
|
||||
@property
|
||||
def device_identifier(self):
|
||||
return self.dev.identifier
|
||||
|
||||
@property
|
||||
def device_manufacturer(self):
|
||||
return self._device_manufacturer
|
||||
|
||||
@property
|
||||
def device_connection(self):
|
||||
"""
|
||||
On specific driver override this method to give connection type of device
|
||||
return string
|
||||
possible value : direct - network - bluetooth - serial - hdmi
|
||||
"""
|
||||
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 - keyboard - scanner - display - 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()
|
||||
|
||||
|
||||
event_manager = EventManager()
|
||||
|
||||
#----------------------------------------------------------
|
||||
# ConnectionManager
|
||||
#----------------------------------------------------------
|
||||
|
||||
class ConnectionManager(Thread):
|
||||
def __init__(self):
|
||||
super(ConnectionManager, self).__init__()
|
||||
self.pairing_code = False
|
||||
self.pairing_uuid = False
|
||||
|
||||
def run(self):
|
||||
if not helpers.get_odoo_server_url():
|
||||
end_time = datetime.now() + timedelta(minutes=5)
|
||||
while (datetime.now() < end_time):
|
||||
self._connect_box()
|
||||
time.sleep(10)
|
||||
self.pairing_code = False
|
||||
self.pairing_uuid = False
|
||||
self._refresh_displays()
|
||||
|
||||
def _connect_box(self):
|
||||
data = {
|
||||
'jsonrpc': 2.0,
|
||||
'params': {
|
||||
'pairing_code': self.pairing_code,
|
||||
'pairing_uuid': self.pairing_uuid,
|
||||
}
|
||||
}
|
||||
|
||||
urllib3.disable_warnings()
|
||||
req = requests.post('https://iot-proxy.odoo.com/odoo-enterprise/iot/connect-box', json=data, verify=False)
|
||||
result = req.json().get('result', {})
|
||||
|
||||
if all(key in result for key in ['pairing_code', 'pairing_uuid']):
|
||||
self.pairing_code = result['pairing_code']
|
||||
self.pairing_uuid = result['pairing_uuid']
|
||||
elif all(key in result for key in ['url', 'token', 'db_uuid', 'enterprise_code']):
|
||||
self._connect_to_server(result['url'], result['token'], result['db_uuid'], result['enterprise_code'])
|
||||
|
||||
def _connect_to_server(self, url, token, db_uuid, enterprise_code):
|
||||
if db_uuid and enterprise_code:
|
||||
helpers.add_credential(db_uuid, enterprise_code)
|
||||
|
||||
# Save DB URL and token
|
||||
subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server.sh'), url, '', token, 'noreboot'])
|
||||
# Notify the DB, so that the kanban view already shows the IoT Box
|
||||
m.send_alldevices()
|
||||
# Restart to checkout the git branch, get a certificate, load the IoT handlers...
|
||||
subprocess.check_call(["sudo", "service", "odoo", "restart"])
|
||||
|
||||
def _refresh_displays(self):
|
||||
"""Refresh all displays to hide the pairing code"""
|
||||
for d in iot_devices:
|
||||
if iot_devices[d].device_type == 'display':
|
||||
iot_devices[d].action({
|
||||
'action': 'display_refresh'
|
||||
})
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Manager
|
||||
#----------------------------------------------------------
|
||||
|
||||
class Manager(Thread):
|
||||
|
||||
def load_iot_handlers(self):
|
||||
"""
|
||||
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_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()
|
||||
|
||||
def send_alldevices(self):
|
||||
"""
|
||||
This method send IoT Box and devices informations to Odoo database
|
||||
"""
|
||||
server = helpers.get_odoo_server_url()
|
||||
if server:
|
||||
subject = helpers.read_file_first_line('odoo-subject.conf')
|
||||
if subject:
|
||||
domain = helpers.get_ip().replace('.', '-') + subject.strip('*')
|
||||
else:
|
||||
domain = helpers.get_ip()
|
||||
iot_box = {
|
||||
'name': socket.gethostname(),
|
||||
'identifier': helpers.get_mac_address(),
|
||||
'ip': domain,
|
||||
'token': helpers.get_token(),
|
||||
'version': helpers.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,
|
||||
'manufacturer': iot_devices[device].device_manufacturer,
|
||||
'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 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()
|
||||
self.send_alldevices()
|
||||
self.load_iot_handlers()
|
||||
self.previous_iot_devices = []
|
||||
for interface in interfaces.values():
|
||||
i = interface()
|
||||
i.daemon = True
|
||||
i.start()
|
||||
while 1:
|
||||
if iot_devices != self.previous_iot_devices:
|
||||
self.send_alldevices()
|
||||
self.previous_iot_devices = iot_devices.copy()
|
||||
time.sleep(3)
|
||||
|
||||
# Must be started from main thread
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
|
||||
cm = ConnectionManager()
|
||||
cm.daemon = True
|
||||
cm.start()
|
||||
|
||||
m = Manager()
|
||||
m.daemon = True
|
||||
m.start()
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import http
|
||||
|
||||
proxy_drivers = {}
|
||||
|
||||
class ProxyController(http.Controller):
|
||||
@http.route('/hw_proxy/hello', type='http', auth='none', cors='*')
|
||||
def hello(self):
|
||||
return "ping"
|
||||
|
||||
@http.route('/hw_proxy/handshake', type='json', auth='none', cors='*')
|
||||
def handshake(self):
|
||||
return True
|
||||
|
||||
@http.route('/hw_proxy/status_json', type='json', auth='none', cors='*')
|
||||
def status_json(self):
|
||||
statuses = {}
|
||||
for driver in proxy_drivers:
|
||||
statuses[driver] = proxy_drivers[driver].get_status()
|
||||
return statuses
|
||||
@@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from threading import Thread
|
||||
|
||||
from odoo.addons.hw_drivers.main import drivers, iot_devices
|
||||
|
||||
|
||||
class DriverMetaClass(type):
|
||||
def __new__(cls, clsname, bases, attrs):
|
||||
newclass = super(DriverMetaClass, cls).__new__(cls, clsname, bases, attrs)
|
||||
if hasattr(newclass, 'priority'):
|
||||
newclass.priority += 1
|
||||
else:
|
||||
newclass.priority = 0
|
||||
drivers.append(newclass)
|
||||
return newclass
|
||||
|
||||
|
||||
class Driver(Thread, metaclass=DriverMetaClass):
|
||||
"""
|
||||
Hook to register the driver into the drivers list
|
||||
"""
|
||||
connection_type = ''
|
||||
|
||||
def __init__(self, identifier, device):
|
||||
super(Driver, self).__init__()
|
||||
self.dev = device
|
||||
self.device_identifier = identifier
|
||||
self.device_name = ''
|
||||
self.device_connection = ''
|
||||
self.device_type = ''
|
||||
self.device_manufacturer = ''
|
||||
self.data = {'value': ''}
|
||||
|
||||
@classmethod
|
||||
def supported(cls, device):
|
||||
"""
|
||||
On specific driver override this method to check if device is supported or not
|
||||
return True or False
|
||||
"""
|
||||
return False
|
||||
|
||||
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]
|
||||
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from threading import Event
|
||||
import time
|
||||
|
||||
|
||||
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_identifier'] = device.device_identifier
|
||||
self.sessions[session]['event'].set()
|
||||
|
||||
|
||||
event_manager = EventManager()
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
|
||||
class ExceptionLogger:
|
||||
"""
|
||||
Redirect Exceptions to the logger to keep track of them in the log file.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger()
|
||||
|
||||
def write(self, message):
|
||||
if message != '\n':
|
||||
self.logger.error(message)
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
sys.stderr = ExceptionLogger()
|
||||
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
from threading import Thread
|
||||
import time
|
||||
|
||||
from odoo.addons.hw_drivers.main import drivers, interfaces, iot_devices
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
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 = sorted([d for d in drivers if d.connection_type == self.connection_type], key=lambda d: d.priority, reverse=True)
|
||||
|
||||
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 identifier in removed:
|
||||
if identifier in iot_devices:
|
||||
iot_devices[identifier].disconnect()
|
||||
_logger.info('Device %s is now disconnected', identifier)
|
||||
|
||||
for identifier in added:
|
||||
for driver in self.drivers:
|
||||
if driver.supported(devices[identifier]):
|
||||
_logger.info('Device %s is now connected', identifier)
|
||||
d = driver(identifier, devices[identifier])
|
||||
d.daemon = True
|
||||
d.start()
|
||||
iot_devices[identifier] = d
|
||||
break
|
||||
|
||||
def get_devices(self):
|
||||
raise NotImplementedError()
|
||||
@@ -1,17 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import jinja2
|
||||
import json
|
||||
import logging
|
||||
import netifaces as ni
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
import urllib3
|
||||
|
||||
from odoo import http
|
||||
from odoo.addons.hw_drivers.connection_manager import connection_manager
|
||||
from odoo.addons.hw_drivers.driver import Driver
|
||||
from odoo.addons.hw_drivers.event_manager import event_manager
|
||||
from odoo.addons.hw_drivers.main import iot_devices
|
||||
from odoo.addons.hw_drivers.tools import helpers
|
||||
from odoo.addons.hw_drivers.controllers.driver import Driver, event_manager, iot_devices, cm
|
||||
|
||||
path = os.path.realpath(os.path.join(os.path.dirname(__file__), '../../views'))
|
||||
loader = jinja2.FileSystemLoader(path)
|
||||
@@ -27,11 +32,11 @@ _logger = logging.getLogger(__name__)
|
||||
class DisplayDriver(Driver):
|
||||
connection_type = 'display'
|
||||
|
||||
def __init__(self, device):
|
||||
super(DisplayDriver, self).__init__(device)
|
||||
self._device_type = 'display'
|
||||
self._device_connection = 'hdmi'
|
||||
self._device_name = device['name']
|
||||
def __init__(self, identifier, device):
|
||||
super(DisplayDriver, self).__init__(identifier, device)
|
||||
self.device_type = 'display'
|
||||
self.device_connection = 'hdmi'
|
||||
self.device_name = device['name']
|
||||
self.event_data = threading.Event()
|
||||
self.owner = False
|
||||
self.rendered_html = ''
|
||||
@@ -39,10 +44,6 @@ class DisplayDriver(Driver):
|
||||
self._x_screen = device.get('x_screen', '0')
|
||||
self.load_url()
|
||||
|
||||
@property
|
||||
def device_identifier(self):
|
||||
return self.dev['identifier']
|
||||
|
||||
@classmethod
|
||||
def supported(cls, device):
|
||||
return True # All devices with connection_type == 'display' are supported
|
||||
@@ -93,7 +94,7 @@ class DisplayDriver(Driver):
|
||||
urllib3.disable_warnings()
|
||||
http = urllib3.PoolManager(cert_reqs='CERT_NONE')
|
||||
try:
|
||||
response = http.request('GET', "%s/iot/box/%s/screen_url" % (helpers.get_odoo_server_url(), helpers.get_mac_address()))
|
||||
response = http.request('GET', "%s/iot/box/%s/display_url" % (helpers.get_odoo_server_url(), helpers.get_mac_address()))
|
||||
if response.status == 200:
|
||||
data = json.loads(response.data.decode('utf8'))
|
||||
url = data[self.device_identifier]
|
||||
@@ -215,5 +216,5 @@ class DisplayController(http.Controller):
|
||||
'cust_js': cust_js,
|
||||
'display_ifaces': display_ifaces,
|
||||
'display_identifier': display_identifier,
|
||||
'pairing_code': cm.pairing_code,
|
||||
'pairing_code': connection_manager.pairing_code,
|
||||
})
|
||||
|
||||
@@ -8,17 +8,19 @@ import logging
|
||||
from lxml import etree
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import time
|
||||
from threading import Lock
|
||||
import usb
|
||||
import urllib3
|
||||
from queue import Queue, Empty
|
||||
import subprocess
|
||||
from threading import Lock
|
||||
import time
|
||||
import urllib3
|
||||
from usb import util
|
||||
|
||||
from odoo import http, _
|
||||
from odoo.addons.hw_proxy.controllers.main import drivers as old_drivers
|
||||
from odoo.addons.hw_drivers.controllers.proxy import proxy_drivers
|
||||
from odoo.addons.hw_drivers.driver import Driver
|
||||
from odoo.addons.hw_drivers.event_manager import event_manager
|
||||
from odoo.addons.hw_drivers.main import iot_devices
|
||||
from odoo.addons.hw_drivers.tools import helpers
|
||||
from odoo.addons.hw_drivers.controllers.driver import event_manager, Driver, iot_devices
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
xlib = ctypes.cdll.LoadLibrary('libX11.so.6')
|
||||
@@ -29,14 +31,14 @@ class KeyboardUSBDriver(Driver):
|
||||
keyboard_layout_groups = []
|
||||
available_layouts = []
|
||||
|
||||
def __init__(self, device):
|
||||
def __init__(self, identifier, device):
|
||||
if not hasattr(KeyboardUSBDriver, 'display'):
|
||||
os.environ['XAUTHORITY'] = "/run/lightdm/pi/xauthority"
|
||||
KeyboardUSBDriver.display = xlib.XOpenDisplay(bytes(":0.0", "utf-8"))
|
||||
|
||||
super(KeyboardUSBDriver, self).__init__(device)
|
||||
self._device_connection = 'direct'
|
||||
self._device_name = self._set_name()
|
||||
super(KeyboardUSBDriver, self).__init__(identifier, device)
|
||||
self.device_connection = 'direct'
|
||||
self.device_name = self._set_name()
|
||||
|
||||
# from https://github.com/xkbcommon/libxkbcommon/blob/master/test/evdev-scancodes.h
|
||||
self._scancode_to_modifier = {
|
||||
@@ -52,9 +54,9 @@ class KeyboardUSBDriver(Driver):
|
||||
KeyboardUSBDriver.load_layouts_list()
|
||||
KeyboardUSBDriver.send_layouts_list()
|
||||
|
||||
for device in [evdev.InputDevice(path) for path in evdev.list_devices()]:
|
||||
if (self.dev.idVendor == device.info.vendor) and (self.dev.idProduct == device.info.product):
|
||||
self.input_device = device
|
||||
for evdev_device in [evdev.InputDevice(path) for path in evdev.list_devices()]:
|
||||
if (device.idVendor == evdev_device.info.vendor) and (device.idProduct == evdev_device.info.product):
|
||||
self.input_device = evdev_device
|
||||
|
||||
self._set_device_type('scanner') if self._is_scanner() else self._set_device_type()
|
||||
|
||||
@@ -108,12 +110,8 @@ class KeyboardUSBDriver(Driver):
|
||||
|
||||
def _set_name(self):
|
||||
try:
|
||||
if usb.__version__ == '1.0.0b1':
|
||||
manufacturer = usb.util.get_string(self.dev, 256, self.dev.iManufacturer)
|
||||
product = usb.util.get_string(self.dev, 256, self.dev.iProduct)
|
||||
else:
|
||||
manufacturer = usb.util.get_string(self.dev, self.dev.iManufacturer)
|
||||
product = usb.util.get_string(self.dev, self.dev.iProduct)
|
||||
manufacturer = util.get_string(self.dev, self.dev.iManufacturer)
|
||||
product = util.get_string(self.dev, self.dev.iProduct)
|
||||
return ("%s - %s") % (manufacturer, product)
|
||||
except ValueError as e:
|
||||
_logger.warning(e)
|
||||
@@ -134,7 +132,6 @@ class KeyboardUSBDriver(Driver):
|
||||
self.data['value'] = ''
|
||||
event_manager.device_changed(self)
|
||||
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
for event in self.input_device.read_loop():
|
||||
@@ -228,7 +225,7 @@ class KeyboardUSBDriver(Driver):
|
||||
"""Read the device type from the saved filed and set it as current type.
|
||||
If no file or no device type is found we try to detect it automatically.
|
||||
"""
|
||||
device_name = self._device_name.lower()
|
||||
device_name = self.device_name.lower()
|
||||
scanner_name = ['barcode', 'scanner', 'reader']
|
||||
is_scanner = any(x in device_name for x in scanner_name) or self.dev.interface_protocol == '0'
|
||||
|
||||
@@ -276,14 +273,14 @@ class KeyboardUSBDriver(Driver):
|
||||
type (string): Type wanted to switch
|
||||
"""
|
||||
if device_type == 'scanner':
|
||||
self._device_type = 'scanner'
|
||||
self.device_type = 'scanner'
|
||||
self.key_input = self._barcode_scanner_input
|
||||
self._barcodes = Queue()
|
||||
self._current_barcode = ''
|
||||
self.input_device.grab()
|
||||
self.read_barcode_lock = Lock()
|
||||
else:
|
||||
self._device_type = 'keyboard'
|
||||
self.device_type = 'keyboard'
|
||||
self.key_input = self._keyboard_input
|
||||
self.load_layout()
|
||||
|
||||
@@ -355,8 +352,8 @@ class KeyboardUSBDriver(Driver):
|
||||
except Empty:
|
||||
return ''
|
||||
|
||||
proxy_drivers['scanner'] = KeyboardUSBDriver
|
||||
|
||||
old_drivers['scanner'] = KeyboardUSBDriver
|
||||
|
||||
class KeyboardUSBController(http.Controller):
|
||||
@http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
|
||||
|
||||
@@ -4,21 +4,24 @@
|
||||
from base64 import b64decode
|
||||
from cups import IPPError, IPP_PRINTER_IDLE, IPP_PRINTER_PROCESSING, IPP_PRINTER_STOPPED
|
||||
import dbus
|
||||
import io
|
||||
import logging
|
||||
import netifaces as ni
|
||||
import os
|
||||
import io
|
||||
import base64
|
||||
from PIL import Image, ImageOps
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
from PIL import Image, ImageOps
|
||||
from uuid import getnode as get_mac
|
||||
|
||||
from odoo import http, _
|
||||
from odoo.addons.hw_drivers.controllers.driver import event_manager, Driver, iot_devices, cm
|
||||
from odoo import http
|
||||
from odoo.addons.hw_drivers.connection_manager import connection_manager
|
||||
from odoo.addons.hw_drivers.controllers.proxy import proxy_drivers
|
||||
from odoo.addons.hw_drivers.driver import Driver
|
||||
from odoo.addons.hw_drivers.event_manager import event_manager
|
||||
from odoo.addons.hw_drivers.iot_handlers.interfaces.PrinterInterface import PPDs, conn, cups_lock
|
||||
from odoo.addons.hw_drivers.main import iot_devices
|
||||
from odoo.addons.hw_drivers.tools import helpers
|
||||
from odoo.addons.hw_proxy.controllers.main import drivers as old_drivers
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -48,15 +51,15 @@ def print_star_error(deviceId):
|
||||
process = subprocess.Popen(["lp", "-d", deviceId], stdin=subprocess.PIPE)
|
||||
process.communicate(error_page.encode("utf-8"))
|
||||
|
||||
def cups_notification_handler(message, uri, device_id, state, reason, accepting_jobs):
|
||||
if device_id in iot_devices:
|
||||
def cups_notification_handler(message, uri, device_identifier, state, reason, accepting_jobs):
|
||||
if device_identifier in iot_devices:
|
||||
reason = reason if reason != 'none' else None
|
||||
state_value = {
|
||||
IPP_PRINTER_IDLE: 'connected',
|
||||
IPP_PRINTER_PROCESSING: 'processing',
|
||||
IPP_PRINTER_STOPPED: 'stopped'
|
||||
}
|
||||
iot_devices[device_id].update_status(state_value[state], message, reason)
|
||||
iot_devices[device_identifier].update_status(state_value[state], message, reason)
|
||||
|
||||
# Create a Cups subscription if it doesn't exist yet
|
||||
try:
|
||||
@@ -76,18 +79,18 @@ bus.add_signal_receiver(cups_notification_handler, signal_name="PrinterStateChan
|
||||
class PrinterDriver(Driver):
|
||||
connection_type = 'printer'
|
||||
|
||||
def __init__(self, device):
|
||||
super(PrinterDriver, self).__init__(device)
|
||||
self._device_type = 'printer'
|
||||
self._device_connection = self.dev['device-class'].lower()
|
||||
self._device_name = self.dev['device-make-and-model']
|
||||
def __init__(self, identifier, device):
|
||||
super(PrinterDriver, self).__init__(identifier, device)
|
||||
self.device_type = 'printer'
|
||||
self.device_connection = device['device-class'].lower()
|
||||
self.device_name = device['device-make-and-model']
|
||||
self.state = {
|
||||
'status': 'connecting',
|
||||
'message': 'Connecting to printer',
|
||||
'reason': None,
|
||||
}
|
||||
self.send_status()
|
||||
if 'direct' in self._device_connection and 'CMD:ESC/POS;' in self.dev['device-id']:
|
||||
if 'direct' in self.device_connection and 'CMD:ESC/POS;' in device['device-id']:
|
||||
self.print_status()
|
||||
|
||||
@classmethod
|
||||
@@ -137,15 +140,11 @@ class PrinterDriver(Driver):
|
||||
status = 'connected' if any(iot_devices[d].device_type == "printer" and iot_devices[d].device_connection == 'direct' for d in iot_devices) else 'disconnected'
|
||||
return {'status': status, 'messages': ''}
|
||||
|
||||
@property
|
||||
def device_identifier(self):
|
||||
return self.dev['identifier']
|
||||
|
||||
def action(self, data):
|
||||
if data.get('action') == 'cashbox':
|
||||
self.open_cashbox()
|
||||
elif data.get('action') == 'print_receipt':
|
||||
self.print_receipt(base64.b64decode(data['receipt']))
|
||||
self.print_receipt(b64decode(data['receipt']))
|
||||
else:
|
||||
self.print_raw(b64decode(data['document']))
|
||||
|
||||
@@ -257,7 +256,7 @@ class PrinterDriver(Driver):
|
||||
mac = '\nMAC Address:\n%s\n' % helpers.get_mac_address()
|
||||
homepage = '\nHomepage:\nhttp://%s:8069\n\n' % main_ips
|
||||
|
||||
code = cm.pairing_code
|
||||
code = connection_manager.pairing_code
|
||||
if code:
|
||||
pairing_code = '\nPairing Code:\n%s\n' % code
|
||||
|
||||
@@ -286,4 +285,4 @@ class PrinterController(http.Controller):
|
||||
return True
|
||||
return False
|
||||
|
||||
old_drivers['printer'] = PrinterDriver
|
||||
proxy_drivers['printer'] = PrinterDriver
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from collections import namedtuple
|
||||
from contextlib import contextmanager
|
||||
import logging
|
||||
import serial
|
||||
from threading import Lock
|
||||
import time
|
||||
import traceback
|
||||
from collections import namedtuple
|
||||
from threading import Lock
|
||||
from contextlib import contextmanager
|
||||
|
||||
import serial
|
||||
|
||||
from odoo.tools.translate import _
|
||||
from odoo.addons.hw_drivers.controllers.driver import Driver, event_manager
|
||||
from odoo import _
|
||||
from odoo.addons.hw_drivers.event_manager import event_manager
|
||||
from odoo.addons.hw_drivers.driver import Driver
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -58,26 +58,22 @@ class SerialDriver(Driver):
|
||||
STATUS_ERROR = 'error'
|
||||
STATUS_CONNECTING = 'connecting'
|
||||
|
||||
def __init__(self, device):
|
||||
def __init__(self, identifier, device):
|
||||
""" Attributes initialization method for `SerialDriver`.
|
||||
|
||||
:param device: path to the device
|
||||
:type device: str
|
||||
"""
|
||||
|
||||
super().__init__(device)
|
||||
super(SerialDriver, self).__init__(identifier, device)
|
||||
self._actions = {
|
||||
'get_status': self._push_status,
|
||||
}
|
||||
self._device_connection = 'serial'
|
||||
self.device_connection = 'serial'
|
||||
self._device_lock = Lock()
|
||||
self._status = {'status': self.STATUS_CONNECTING, 'message_title': '', 'message_body': ''}
|
||||
self._set_name()
|
||||
|
||||
@property
|
||||
def device_identifier(self):
|
||||
return self.dev['identifier']
|
||||
|
||||
def _get_raw_response(connection):
|
||||
pass
|
||||
|
||||
@@ -91,10 +87,10 @@ class SerialDriver(Driver):
|
||||
"""Tries to build the device's name based on its type and protocol name but falls back on a default name if that doesn't work."""
|
||||
|
||||
try:
|
||||
name = ('%s serial %s' % (self._protocol.name, self._device_type)).title()
|
||||
name = ('%s serial %s' % (self._protocol.name, self.device_type)).title()
|
||||
except Exception:
|
||||
name = 'Unknown Serial Device'
|
||||
self._device_name = name
|
||||
self.device_name = name
|
||||
|
||||
def _take_measure(self):
|
||||
pass
|
||||
@@ -126,7 +122,7 @@ class SerialDriver(Driver):
|
||||
if self._connection and self._connection.isOpen():
|
||||
self._do_action(data)
|
||||
else:
|
||||
with serial_connection(self.dev['identifier'], self._protocol) as connection:
|
||||
with serial_connection(self.device_identifier, self._protocol) as connection:
|
||||
self._connection = connection
|
||||
self._do_action(data)
|
||||
|
||||
@@ -134,7 +130,7 @@ class SerialDriver(Driver):
|
||||
"""Continuously gets new measures from the device."""
|
||||
|
||||
try:
|
||||
with serial_connection(self.dev['identifier'], self._protocol) as connection:
|
||||
with serial_connection(self.device_identifier, self._protocol) as connection:
|
||||
self._connection = connection
|
||||
self._status['status'] = self.STATUS_CONNECTED
|
||||
self._push_status()
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
|
||||
import threading
|
||||
from collections import namedtuple
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
from collections import namedtuple
|
||||
|
||||
import serial
|
||||
import threading
|
||||
import time
|
||||
|
||||
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.controllers.proxy import proxy_drivers
|
||||
from odoo.addons.hw_drivers.event_manager import event_manager
|
||||
from odoo.addons.hw_drivers.iot_handlers.drivers.SerialBaseDriver import SerialDriver, SerialProtocol, serial_connection
|
||||
|
||||
|
||||
@@ -95,9 +93,9 @@ class ScaleDriver(SerialDriver):
|
||||
"""Abstract base class for scale drivers."""
|
||||
last_sent_value = None
|
||||
|
||||
def __init__(self, device):
|
||||
self._device_type = 'scale'
|
||||
super().__init__(device)
|
||||
def __init__(self, identifier, device):
|
||||
super(ScaleDriver, self).__init__(identifier, device)
|
||||
self.device_type = 'scale'
|
||||
self._set_actions()
|
||||
self._is_reading = True
|
||||
|
||||
@@ -105,7 +103,7 @@ class ScaleDriver(SerialDriver):
|
||||
# Only the last scale connected is kept
|
||||
global ACTIVE_SCALE
|
||||
ACTIVE_SCALE = self
|
||||
old_drivers['scale'] = ACTIVE_SCALE
|
||||
proxy_drivers['scale'] = ACTIVE_SCALE
|
||||
|
||||
# Ensures compatibility with older versions of Odoo
|
||||
# and allows using the `ProxyDevice` in the point of sale to retrieve the status
|
||||
@@ -212,9 +210,9 @@ class Toledo8217Driver(ScaleDriver):
|
||||
"""Driver for the Toldedo 8217 serial scale."""
|
||||
_protocol = Toledo8217Protocol
|
||||
|
||||
def __init__(self, device):
|
||||
super().__init__(device)
|
||||
self._device_manufacturer = 'Toledo'
|
||||
def __init__(self, identifier, device):
|
||||
super(Toledo8217Driver, self).__init__(identifier, device)
|
||||
self.device_manufacturer = 'Toledo'
|
||||
|
||||
@classmethod
|
||||
def supported(cls, device):
|
||||
@@ -249,11 +247,11 @@ class AdamEquipmentDriver(ScaleDriver):
|
||||
_protocol = ADAMEquipmentProtocol
|
||||
priority = 0 # Test the supported method of this driver last, after all other serial drivers
|
||||
|
||||
def __init__(self, device):
|
||||
super().__init__(device)
|
||||
def __init__(self, identifier, device):
|
||||
super(AdamEquipmentDriver, self).__init__(identifier, device)
|
||||
self._is_reading = False
|
||||
self._last_weight_time = 0
|
||||
self._device_manufacturer = 'Adam'
|
||||
self.device_manufacturer = 'Adam'
|
||||
|
||||
def _check_last_weight_time(self):
|
||||
"""The ADAM doesn't make the difference between a value of 0 and "the same value as last time":
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from re import sub, finditer
|
||||
import subprocess
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import Interface
|
||||
from odoo.addons.hw_drivers.interface import Interface
|
||||
|
||||
|
||||
class DisplayInterface(Interface):
|
||||
@@ -29,7 +32,6 @@ class DisplayInterface(Interface):
|
||||
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",
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from cups import Connection as cups_connection
|
||||
from re import sub
|
||||
from threading import Lock
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import Interface
|
||||
from odoo.addons.hw_drivers.interface import Interface
|
||||
|
||||
conn = cups_connection()
|
||||
PPDs = conn.getPPDs()
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from glob import glob
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import Interface
|
||||
from odoo.addons.hw_drivers.interface import Interface
|
||||
|
||||
|
||||
class SerialInterface(Interface):
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from usb import core
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import Interface
|
||||
from odoo.addons.hw_drivers.interface import Interface
|
||||
|
||||
|
||||
class USBInterface(Interface):
|
||||
@@ -18,9 +21,9 @@ class USBInterface(Interface):
|
||||
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
|
||||
identifier = "usb_%04x:%04x" % (dev.idVendor, dev.idProduct)
|
||||
if identifier in usb_devices:
|
||||
identifier += '_%s' % cpt
|
||||
cpt += 1
|
||||
usb_devices[dev.identifier] = dev
|
||||
usb_devices[identifier] = dev
|
||||
return usb_devices
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
import json
|
||||
import logging
|
||||
import socket
|
||||
from threading import Thread
|
||||
import time
|
||||
import urllib3
|
||||
|
||||
from odoo.addons.hw_drivers.tools import helpers
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
drivers = []
|
||||
interfaces = {}
|
||||
iot_devices = {}
|
||||
|
||||
|
||||
class Manager(Thread):
|
||||
def send_alldevices(self):
|
||||
"""
|
||||
This method send IoT Box and devices informations to Odoo database
|
||||
"""
|
||||
server = helpers.get_odoo_server_url()
|
||||
if server:
|
||||
subject = helpers.read_file_first_line('odoo-subject.conf')
|
||||
if subject:
|
||||
domain = helpers.get_ip().replace('.', '-') + subject.strip('*')
|
||||
else:
|
||||
domain = helpers.get_ip()
|
||||
iot_box = {
|
||||
'name': socket.gethostname(),
|
||||
'identifier': helpers.get_mac_address(),
|
||||
'ip': domain,
|
||||
'token': helpers.get_token(),
|
||||
'version': helpers.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,
|
||||
'manufacturer': iot_devices[device].device_manufacturer,
|
||||
'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 run(self):
|
||||
"""
|
||||
Thread that will load interfaces and drivers and contact the odoo server with the updates
|
||||
"""
|
||||
|
||||
helpers.check_git_branch()
|
||||
helpers.check_certificate()
|
||||
|
||||
# We first add the IoT Box to the connected DB because IoT handlers cannot be downloaded if
|
||||
# the identifier of the Box is not found in the DB. So add the Box to the DB.
|
||||
self.send_alldevices()
|
||||
helpers.download_iot_handlers()
|
||||
helpers.load_iot_handlers()
|
||||
|
||||
# Start the interfaces
|
||||
for interface in interfaces.values():
|
||||
i = interface()
|
||||
i.daemon = True
|
||||
i.start()
|
||||
|
||||
# Check every 3 secondes if the list of connected devices has changed and send the updated
|
||||
# list to the connected DB.
|
||||
self.previous_iot_devices = []
|
||||
while 1:
|
||||
if iot_devices != self.previous_iot_devices:
|
||||
self.send_alldevices()
|
||||
self.previous_iot_devices = iot_devices.copy()
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
# Must be started from main thread
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
|
||||
manager = Manager()
|
||||
manager.daemon = True
|
||||
manager.start()
|
||||
@@ -1,21 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import netifaces
|
||||
from pathlib import Path
|
||||
import datetime
|
||||
from OpenSSL import crypto
|
||||
import urllib3
|
||||
from importlib import util
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import netifaces
|
||||
from OpenSSL import crypto
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import urllib3
|
||||
import zipfile
|
||||
from threading import Thread
|
||||
import time
|
||||
|
||||
from odoo import _
|
||||
from odoo import _, http
|
||||
from odoo.modules.module import get_resource_path
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -232,6 +233,24 @@ def download_iot_handlers(auto=True):
|
||||
_logger.error('Could not reach configured server')
|
||||
_logger.error('A error encountered : %s ' % e)
|
||||
|
||||
def load_iot_handlers():
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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()
|
||||
|
||||
def odoo_restart(delay):
|
||||
IR = IoTRestart(delay)
|
||||
IR.start()
|
||||
|
||||
@@ -16,7 +16,6 @@ to open ESC/POS controlled cashdrawers in the point of sale and other modules
|
||||
that would need such functionality.
|
||||
|
||||
""",
|
||||
'depends': ['hw_proxy'],
|
||||
'external_dependencies': {
|
||||
'python' : ['pyusb','pyserial','qrcode'],
|
||||
},
|
||||
|
||||
@@ -27,7 +27,7 @@ except ImportError:
|
||||
usb = None
|
||||
|
||||
from odoo import http, _
|
||||
from odoo.addons.hw_proxy.controllers import main as hw_proxy
|
||||
from odoo.addons.hw_drivers.controllers import proxy
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -78,10 +78,7 @@ class EscposDriver(Thread):
|
||||
|
||||
for printer in printers:
|
||||
try:
|
||||
if usb.__version__ == '1.0.0b1':
|
||||
description = usb.util.get_string(printer, 256, printer.iManufacturer) + " " + usb.util.get_string(printer, 256, printer.iProduct)
|
||||
else:
|
||||
description = usb.util.get_string(printer, printer.iManufacturer) + " " + usb.util.get_string(printer, printer.iProduct)
|
||||
description = usb.util.get_string(printer, printer.iManufacturer) + " " + usb.util.get_string(printer, printer.iProduct)
|
||||
except Exception as e:
|
||||
_logger.error("Can not get printer description: %s" % e)
|
||||
description = 'Unknown printer'
|
||||
@@ -326,9 +323,10 @@ class EscposDriver(Thread):
|
||||
|
||||
driver = EscposDriver()
|
||||
|
||||
hw_proxy.drivers['escpos'] = driver
|
||||
proxy.proxy_drivers['escpos'] = driver
|
||||
|
||||
class EscposProxy(hw_proxy.Proxy):
|
||||
|
||||
class EscposProxy(proxy.ProxyController):
|
||||
|
||||
@http.route('/hw_proxy/open_cashbox', type='json', auth='none', cors='*')
|
||||
def open_cashbox(self):
|
||||
|
||||
@@ -19,6 +19,5 @@ If you activate this module, you won't be able to access the
|
||||
regular Odoo interface anymore.
|
||||
|
||||
""",
|
||||
'depends': ['hw_proxy'],
|
||||
'installable': False,
|
||||
}
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
import json
|
||||
import jinja2
|
||||
import subprocess
|
||||
import socket
|
||||
import sys
|
||||
import netifaces
|
||||
import odoo
|
||||
from odoo import http
|
||||
import logging
|
||||
import os
|
||||
from odoo.tools import misc
|
||||
from pathlib import Path
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
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.tools import helpers
|
||||
from odoo.addons.hw_drivers.controllers.driver import iot_devices
|
||||
from odoo import http
|
||||
from odoo.http import Response
|
||||
from odoo.modules.module import get_resource_path
|
||||
|
||||
from odoo.addons.hw_drivers.main import iot_devices
|
||||
from odoo.addons.hw_drivers.tools import helpers
|
||||
from odoo.addons.web.controllers import main as web
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -80,7 +77,7 @@ class IoTboxHomepage(web.Home):
|
||||
iot_device.append({
|
||||
'name': iot_devices[device].device_name + ' : ' + str(iot_devices[device].data['value']),
|
||||
'type': iot_devices[device].device_type.replace('_', ' '),
|
||||
'message': iot_devices[device].device_identifier + iot_devices[device].get_message()
|
||||
'identifier': iot_devices[device].device_identifier,
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
.device-status {
|
||||
margin: 6px 0;
|
||||
}
|
||||
.device-status .message {
|
||||
.device-status .identifier {
|
||||
font-size: 0.8rem;
|
||||
max-width: 350px;
|
||||
}
|
||||
@@ -130,7 +130,7 @@
|
||||
{% for device in iot_devices.list %}
|
||||
<div class="device-status">
|
||||
<span class="device">{{ device['name'] }}</span>
|
||||
<div class="message">{{ device['message'] }}</div>
|
||||
<div class="identifier">{{ device['identifier'] }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
url:'/hw_proxy/perform_upgrade/'
|
||||
}).done(function() {
|
||||
$('.message-title').text('Upgrade successful');
|
||||
var cpt = 110;
|
||||
var cpt = 25;
|
||||
setInterval(function() {
|
||||
--cpt;
|
||||
if (cpt === 0) {location.reload();}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import controllers
|
||||
@@ -1,21 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Hardware Proxy',
|
||||
'category': 'Sales/Point of Sale',
|
||||
'sequence': 6,
|
||||
'summary': 'Connect the Web Client to Hardware Peripherals',
|
||||
'website': 'https://www.odoo.com/page/point-of-sale-hardware',
|
||||
'description': """
|
||||
Hardware Poxy
|
||||
=============
|
||||
|
||||
This module allows you to remotely use peripherals connected to this server.
|
||||
|
||||
This modules only contains the enabling framework. The actual devices drivers
|
||||
are found in other modules that must be installed separately.
|
||||
|
||||
""",
|
||||
'installable': False,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import main
|
||||
@@ -1,218 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from __future__ import print_function
|
||||
import logging
|
||||
import subprocess
|
||||
import time
|
||||
import subprocess
|
||||
from threading import Lock
|
||||
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Those are the builtin raspberry pi USB modules, they should
|
||||
# not appear in the list of connected devices.
|
||||
BANNED_DEVICES = {
|
||||
"0424:9514", # Standard Microsystem Corp. Builtin Ethernet module
|
||||
"1d6b:0002", # Linux Foundation 2.0 root hub
|
||||
"0424:ec00", # Standard Microsystem Corp. Other Builtin Ethernet module
|
||||
"0424:2514", # Standard Microsystems Corp. USB 2.0 Hub (rpi3b+)
|
||||
"0424:7800", # Standard Microsystems Corp. (rpi3b+)
|
||||
}
|
||||
|
||||
|
||||
# drivers modules must add to drivers an object with a get_status() method
|
||||
# so that 'status' can return the status of all active drivers
|
||||
drivers = {}
|
||||
|
||||
# keep a list of RS-232 devices that have been recognized by a driver,
|
||||
# so other drivers can skip them during probes
|
||||
rs232_devices = {} # {'/path/to/device': 'driver'}
|
||||
rs232_lock = Lock() # must be held to update `rs232_devices`
|
||||
|
||||
class Proxy(http.Controller):
|
||||
|
||||
def get_status(self):
|
||||
statuses = {}
|
||||
for driver in drivers:
|
||||
statuses[driver] = drivers[driver].get_status()
|
||||
return statuses
|
||||
|
||||
@http.route('/hw_proxy/hello', type='http', auth='none', cors='*')
|
||||
def hello(self):
|
||||
return "ping"
|
||||
|
||||
@http.route('/hw_proxy/handshake', type='json', auth='none', cors='*')
|
||||
def handshake(self):
|
||||
return True
|
||||
|
||||
@http.route('/hw_proxy/status', type='http', auth='none', cors='*')
|
||||
def status_http(self, debug=None, **kwargs):
|
||||
resp = """
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Odoo's IoTBox</title>
|
||||
<style>
|
||||
body {
|
||||
width: 480px;
|
||||
margin: 60px auto;
|
||||
font-family: sans-serif;
|
||||
text-align: justify;
|
||||
color: #6B6B6B;
|
||||
}
|
||||
.device {
|
||||
border-bottom: solid 1px rgb(216,216,216);
|
||||
padding: 9px;
|
||||
}
|
||||
.device:nth-child(2n) {
|
||||
background:rgb(240,240,240);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hardware Status</h1>
|
||||
<p>The list of enabled drivers and their status</p>
|
||||
"""
|
||||
statuses = self.get_status()
|
||||
for driver in statuses:
|
||||
|
||||
status = statuses[driver]
|
||||
|
||||
if status['status'] == 'connecting':
|
||||
color = 'black'
|
||||
elif status['status'] == 'connected':
|
||||
color = 'green'
|
||||
else:
|
||||
color = 'red'
|
||||
|
||||
resp += "<h3 style='color:"+color+";'>"+driver+' : '+status['status']+"</h3>\n"
|
||||
resp += "<ul>\n"
|
||||
for msg in status['messages']:
|
||||
resp += '<li>'+msg+'</li>\n'
|
||||
resp += "</ul>\n"
|
||||
resp += """
|
||||
<h2>Connected Devices</h2>
|
||||
<p>The list of connected USB devices as seen by the IoTBox</p>
|
||||
"""
|
||||
if debug is None:
|
||||
resp += """(<a href="/hw_proxy/status?debug=1">debug version</a>)"""
|
||||
devices = subprocess.check_output("lsusb").decode('utf-8').split('\n')
|
||||
count = 0
|
||||
resp += "<div class='devices'>\n"
|
||||
for device in devices:
|
||||
device_name = device[device.find('ID')+2:]
|
||||
if device_name: # to avoid last empty line
|
||||
device_id = device_name.split()[0]
|
||||
if not (device_id in BANNED_DEVICES):
|
||||
resp += "<div class='device' data-device='"+device+"'>"+device_name+"</div>\n"
|
||||
count += 1
|
||||
|
||||
if count == 0:
|
||||
resp += "<div class='device'>No USB Device Found</div>"
|
||||
|
||||
resp += "</div>\n</body>\n</html>\n\n"
|
||||
|
||||
if debug is not None:
|
||||
resp += """
|
||||
|
||||
<h3>Debug version</h3>
|
||||
<p><tt>lsusb -v</tt> output:</p>
|
||||
<pre>
|
||||
%s
|
||||
</pre>
|
||||
|
||||
""" % subprocess.check_output(['lsusb', '-v']).decode('utf-8')
|
||||
|
||||
return request.make_response(resp,{
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET',
|
||||
})
|
||||
|
||||
@http.route('/hw_proxy/status_json', type='json', auth='none', cors='*')
|
||||
def status_json(self):
|
||||
return self.get_status()
|
||||
|
||||
@http.route('/hw_proxy/scan_item_success', type='json', auth='none', cors='*')
|
||||
def scan_item_success(self, ean):
|
||||
"""
|
||||
A product has been scanned with success
|
||||
"""
|
||||
print('scan_item_success: %s', ean)
|
||||
|
||||
@http.route('/hw_proxy/scan_item_error_unrecognized', type='json', auth='none', cors='*')
|
||||
def scan_item_error_unrecognized(self, ean):
|
||||
"""
|
||||
A product has been scanned without success
|
||||
"""
|
||||
print('scan_item_error_unrecognized: %s', ean)
|
||||
|
||||
@http.route('/hw_proxy/help_needed', type='json', auth='none', cors='*')
|
||||
def help_needed(self):
|
||||
"""
|
||||
The user wants an help (ex: light is on)
|
||||
"""
|
||||
print("help_needed")
|
||||
|
||||
@http.route('/hw_proxy/help_canceled', type='json', auth='none', cors='*')
|
||||
def help_canceled(self):
|
||||
"""
|
||||
The user stops the help request
|
||||
"""
|
||||
print("help_canceled")
|
||||
|
||||
@http.route('/hw_proxy/payment_request', type='json', auth='none', cors='*')
|
||||
def payment_request(self, price):
|
||||
"""
|
||||
The PoS will activate the method payment
|
||||
"""
|
||||
print("payment_request: price:%s" % price)
|
||||
return 'ok'
|
||||
|
||||
@http.route('/hw_proxy/payment_status', type='json', auth='none', cors='*')
|
||||
def payment_status(self):
|
||||
print("payment_status")
|
||||
return { 'status':'waiting' }
|
||||
|
||||
@http.route('/hw_proxy/payment_cancel', type='json', auth='none', cors='*')
|
||||
def payment_cancel(self):
|
||||
print("payment_cancel")
|
||||
|
||||
@http.route('/hw_proxy/transaction_start', type='json', auth='none', cors='*')
|
||||
def transaction_start(self):
|
||||
print('transaction_start')
|
||||
|
||||
@http.route('/hw_proxy/transaction_end', type='json', auth='none', cors='*')
|
||||
def transaction_end(self):
|
||||
print('transaction_end')
|
||||
|
||||
@http.route('/hw_proxy/cashier_mode_activated', type='json', auth='none', cors='*')
|
||||
def cashier_mode_activated(self):
|
||||
print('cashier_mode_activated')
|
||||
|
||||
@http.route('/hw_proxy/cashier_mode_deactivated', type='json', auth='none', cors='*')
|
||||
def cashier_mode_deactivated(self):
|
||||
print('cashier_mode_deactivated')
|
||||
|
||||
@http.route('/hw_proxy/open_cashbox', type='json', auth='none', cors='*')
|
||||
def open_cashbox(self):
|
||||
print('open_cashbox')
|
||||
|
||||
@http.route('/hw_proxy/print_receipt', type='json', auth='none', cors='*')
|
||||
def print_receipt(self, receipt):
|
||||
print('print_receipt %s', receipt)
|
||||
|
||||
@http.route('/hw_proxy/log', type='json', auth='none', cors='*')
|
||||
def log(self, arguments):
|
||||
_logger.info(' '.join(str(v) for v in arguments))
|
||||
|
||||
@http.route('/hw_proxy/print_pdf_invoice', type='json', auth='none', cors='*')
|
||||
def print_pdf_invoice(self, pdfinvoice):
|
||||
print('print_pdf_invoice %s' % pdfinvoice)
|
||||
@@ -31,7 +31,7 @@ function _start() {
|
||||
while ! test -a /run/systemd/timesync/synchronized && ! ip route | grep 10.11.12.1 && ((cnt--)); do
|
||||
sleep 1
|
||||
done
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER:$USER --background --make-pidfile --exec $DAEMON -- --config $CONFIG --logfile $LOGFILE --load=web,hw_proxy,hw_posbox_homepage,hw_escpos,hw_drivers --limit-time-cpu=600 --limit-time-real=1200
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER:$USER --background --make-pidfile --exec $DAEMON -- --config $CONFIG --logfile $LOGFILE --load=web,hw_posbox_homepage,hw_escpos,hw_drivers --limit-time-cpu=600 --limit-time-real=1200
|
||||
}
|
||||
|
||||
function _stop() {
|
||||
|
||||
@@ -27,77 +27,65 @@ apt-get update && apt-get -y upgrade
|
||||
# Firmware 4.44 seems to prevent the LED mechanism from working
|
||||
|
||||
PKGS_TO_INSTALL="
|
||||
fswebcam \
|
||||
nginx-full \
|
||||
dnsmasq \
|
||||
console-data \
|
||||
cups \
|
||||
cups-ipp-utils \
|
||||
dbus \
|
||||
dbus-x11 \
|
||||
cups \
|
||||
printer-driver-all \
|
||||
cups-ipp-utils \
|
||||
libcups2-dev \
|
||||
pcscd \
|
||||
localepurge \
|
||||
vim \
|
||||
mc \
|
||||
mg \
|
||||
screen \
|
||||
iw \
|
||||
hostapd \
|
||||
git \
|
||||
rsync \
|
||||
kpartx \
|
||||
swig \
|
||||
console-data \
|
||||
lightdm \
|
||||
xserver-xorg-video-fbdev \
|
||||
xserver-xorg-input-evdev \
|
||||
dnsmasq \
|
||||
firefox-esr \
|
||||
xdotool \
|
||||
unclutter \
|
||||
x11-utils \
|
||||
xserver-xorg-video-dummy \
|
||||
openbox \
|
||||
rpi-update \
|
||||
adduser \
|
||||
fswebcam \
|
||||
git \
|
||||
hostapd \
|
||||
iw \
|
||||
kpartx \
|
||||
libcups2-dev \
|
||||
libpq-dev \
|
||||
lightdm \
|
||||
localepurge \
|
||||
nginx-full \
|
||||
openbox \
|
||||
printer-driver-all \
|
||||
python-cups \
|
||||
python3 \
|
||||
python3-pyscard \
|
||||
python3-urllib3 \
|
||||
python3-babel \
|
||||
python3-dateutil \
|
||||
python3-decorator \
|
||||
python3-dev \
|
||||
python3-docutils \
|
||||
python3-feedparser \
|
||||
python3-pil \
|
||||
python3-html2text \
|
||||
python3-jinja2 \
|
||||
python3-ldap \
|
||||
python3-libsass \
|
||||
python3-lxml \
|
||||
python3-mako \
|
||||
python3-mock \
|
||||
python3-openid \
|
||||
python3-netifaces \
|
||||
python3-passlib \
|
||||
python3-pil \
|
||||
python3-pip \
|
||||
python3-psutil \
|
||||
python3-psycopg2 \
|
||||
python3-babel \
|
||||
python3-pydot \
|
||||
python3-pyparsing \
|
||||
python3-pypdf2 \
|
||||
python3-qrcode \
|
||||
python3-reportlab \
|
||||
python3-requests \
|
||||
python3-simplejson \
|
||||
python3-stdnum \
|
||||
python3-tz \
|
||||
python3-werkzeug \
|
||||
python3-serial \
|
||||
python3-pip \
|
||||
python3-dev \
|
||||
python3-netifaces \
|
||||
python3-passlib \
|
||||
python3-libsass \
|
||||
python3-qrcode \
|
||||
python3-html2text \
|
||||
python3-unittest2 \
|
||||
python3-simplejson"
|
||||
python3-tz \
|
||||
python3-urllib3 \
|
||||
python3-werkzeug \
|
||||
rsync \
|
||||
screen \
|
||||
unclutter \
|
||||
vim \
|
||||
x11-utils \
|
||||
xdotool \
|
||||
xserver-xorg-input-evdev \
|
||||
xserver-xorg-video-dummy \
|
||||
xserver-xorg-video-fbdev"
|
||||
|
||||
echo "Acquire::Retries "16";" > /etc/apt/apt.conf.d/99acquire-retries
|
||||
# KEEP OWN CONFIG FILES DURING PACKAGE CONFIGURATION
|
||||
@@ -114,12 +102,12 @@ rm -rfv /usr/share/doc
|
||||
# so we install the latest pyusb that works with this libusb.
|
||||
# Even in stretch, we had an error with langid (but worked otherwise)
|
||||
PIP_TO_INSTALL="
|
||||
pyusb==1.0.0b1 \
|
||||
evdev \
|
||||
gatt \
|
||||
v4l2 \
|
||||
polib \
|
||||
pycups"
|
||||
pycups \
|
||||
pyusb \
|
||||
v4l2"
|
||||
|
||||
pip3 install ${PIP_TO_INSTALL}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ determined connection type. Creating a new interface requires
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import Interface
|
||||
from odoo.addons.hw_drivers.interface import Interface
|
||||
|
||||
class InterfaceName(Interface):
|
||||
connection_type = 'ConnectionType'
|
||||
@@ -96,16 +96,16 @@ Creating a new Driver requires:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import Driver
|
||||
from odoo.addons.hw_drivers.driver import Driver
|
||||
|
||||
class DriverName(Driver):
|
||||
connection_type = 'ConnectionType'
|
||||
|
||||
def __init__(self, device):
|
||||
super(NewDriver, self).__init__(device)
|
||||
self._device_type = 'DeviceType'
|
||||
self._device_connection = 'DeviceConnection'
|
||||
self._device_name = 'DeviceName'
|
||||
def __init__(self, identifier, device):
|
||||
super(NewDriver, self).__init__(identifier, device)
|
||||
self.device_type = 'DeviceType'
|
||||
self.device_connection = 'DeviceConnection'
|
||||
self.device_name = 'DeviceName'
|
||||
|
||||
@classmethod
|
||||
def supported(cls, device):
|
||||
@@ -128,7 +128,7 @@ instantiated using the IP of the IoT Box and the device identifier.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
var DeviceProxy = require('iot.widgets').DeviceProxy;
|
||||
var DeviceProxy = require('iot.DeviceProxy');
|
||||
|
||||
var iot_device = new DeviceProxy({
|
||||
iot_ip: iot_ip,
|
||||
@@ -182,7 +182,7 @@ with `self.data` as argument.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from odoo.addons.hw_drivers.controllers.driver import event_manager
|
||||
from odoo.addons.hw_drivers.event_manager import event_manager
|
||||
|
||||
class DriverName(Driver):
|
||||
connection_type = 'ConnectionType'
|
||||
|
||||
Reference in New Issue
Block a user