mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 08:52:08 +02:00
Add 19 payment provider modules needed by the sale module:
payment_adyen, payment_aps, payment_asiapay, payment_authorize,
payment_buckaroo, payment_demo, payment_dpo, payment_flutterwave,
payment_iyzico, payment_mercado_pago, payment_mollie, payment_nuvei,
payment_paymob, payment_paypal, payment_razorpay, payment_redsys,
payment_stripe, payment_worldline, payment_xendit
Add 3 IoT modules needed for point_of_sale:
iot_base, iot_box_image, iot_drivers
Note: Stripe test API keys replaced with placeholders.
🤖 assisted by claude
170 lines
7.2 KiB
Python
170 lines
7.2 KiB
Python
import json
|
|
import logging
|
|
import platform
|
|
import pprint
|
|
import requests
|
|
import time
|
|
import urllib.parse
|
|
import websocket
|
|
|
|
from threading import Thread
|
|
|
|
from odoo.addons.iot_drivers import main
|
|
from odoo.addons.iot_drivers.tools import helpers
|
|
from odoo.addons.iot_drivers.server_logger import close_server_log_sender_handler
|
|
from odoo.addons.iot_drivers.webrtc_client import webrtc_client
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
websocket.enableTrace(True, level=logging.getLevelName(_logger.getEffectiveLevel()))
|
|
|
|
|
|
@helpers.require_db
|
|
def send_to_controller(params, method="send_websocket", server_url=None):
|
|
"""Confirm the operation's completion by sending a response back to the Odoo server
|
|
|
|
:param params: the parameters to send back to the server
|
|
:param method: method to call on the IoT box controller
|
|
:param server_url: URL of the Odoo server (provided by decorator).
|
|
"""
|
|
request_path = f"{server_url}/iot/box/{method}"
|
|
try:
|
|
response = requests.post(request_path, json={'params': params}, timeout=5)
|
|
response.raise_for_status()
|
|
except requests.exceptions.RequestException:
|
|
_logger.exception('Could not reach database URL: %s', request_path)
|
|
|
|
|
|
def on_error(ws, error):
|
|
_logger.error("websocket received an error: %s", error)
|
|
|
|
|
|
@helpers.require_db
|
|
class WebsocketClient(Thread):
|
|
channel = ""
|
|
|
|
def on_open(self, ws):
|
|
"""
|
|
When the client is setup, this function send a message to subscribe to the iot websocket channel
|
|
"""
|
|
ws.send(json.dumps({
|
|
'event_name': 'subscribe',
|
|
'data': {
|
|
'channels': [self.channel],
|
|
'last': self.last_message_id,
|
|
'identifier': helpers.get_identifier(),
|
|
}
|
|
}))
|
|
|
|
def on_message(self, ws, messages):
|
|
"""Synchronously handle messages received by the websocket."""
|
|
for message in json.loads(messages):
|
|
_logger.debug("websocket received a message: %s", pprint.pformat(message))
|
|
self.last_message_id = message['id']
|
|
payload = message['message']['payload']
|
|
|
|
if not helpers.get_identifier() in payload.get('iot_identifiers', []):
|
|
continue
|
|
|
|
match message['message']['type']:
|
|
case 'iot_action':
|
|
for device_identifier in payload['device_identifiers']:
|
|
if device_identifier in main.iot_devices:
|
|
_logger.debug("device '%s' action started with: %s", device_identifier, pprint.pformat(payload))
|
|
main.iot_devices[device_identifier].action(payload)
|
|
else:
|
|
# Notify the controller that the device is not connected
|
|
send_to_controller({
|
|
'session_id': payload.get('session_id', '0'),
|
|
'iot_box_identifier': helpers.get_identifier(),
|
|
'device_identifier': device_identifier,
|
|
'status': 'disconnected',
|
|
})
|
|
case 'server_clear':
|
|
helpers.disconnect_from_server()
|
|
close_server_log_sender_handler()
|
|
case 'server_update':
|
|
helpers.update_conf({
|
|
'remote_server': payload['server_url']
|
|
})
|
|
helpers.get_odoo_server_url.cache_clear()
|
|
case 'restart_odoo':
|
|
ws.close()
|
|
helpers.odoo_restart()
|
|
case 'webrtc_offer':
|
|
answer = webrtc_client.offer(payload['offer'])
|
|
send_to_controller({
|
|
'iot_box_identifier': helpers.get_identifier(),
|
|
'answer': answer,
|
|
}, method="webrtc_answer")
|
|
case 'remote_debug':
|
|
if platform.system() == 'Windows':
|
|
continue
|
|
if not payload.get("status"):
|
|
helpers.toggle_remote_connection(payload.get("token", ""))
|
|
time.sleep(1)
|
|
send_to_controller({
|
|
'session_id': 0,
|
|
'iot_box_identifier': helpers.get_identifier(),
|
|
'device_identifier': None,
|
|
'status': 'success',
|
|
'result': {'enabled': helpers.is_ngrok_enabled()}
|
|
})
|
|
case _:
|
|
continue
|
|
|
|
def on_close(self, ws, close_status_code, close_msg):
|
|
_logger.debug("websocket closed with status: %s", close_status_code)
|
|
helpers.update_conf({'last_websocket_message_id': self.last_message_id})
|
|
|
|
def __init__(self, channel, server_url=None):
|
|
"""This class will not be instantiated if no db is connected.
|
|
|
|
:param str channel: the channel to subscribe to
|
|
:param str server_url: URL of the Odoo server (provided by decorator).
|
|
"""
|
|
self.channel = channel
|
|
self.last_message_id = int(helpers.get_conf('last_websocket_message_id') or 0)
|
|
self.server_url = server_url
|
|
url_parsed = urllib.parse.urlsplit(server_url)
|
|
scheme = url_parsed.scheme.replace("http", "ws", 1)
|
|
self.websocket_url = urllib.parse.urlunsplit((scheme, url_parsed.netloc, 'websocket', '', ''))
|
|
self.db_name = helpers.get_conf('db_name') or ''
|
|
self.session_id = ''
|
|
super().__init__()
|
|
|
|
def run(self):
|
|
if self.db_name:
|
|
session_response = requests.get(
|
|
self.server_url + "/web/login?db=" + self.db_name,
|
|
allow_redirects=False,
|
|
timeout=10,
|
|
)
|
|
if session_response.status_code in [200, 302]:
|
|
self.session_id = session_response.cookies['session_id']
|
|
else:
|
|
_logger.error("Failed to get session ID, status %s", session_response.status_code)
|
|
|
|
self.ws = websocket.WebSocketApp(self.websocket_url,
|
|
header={"User-Agent": "OdooIoTBox/1.0", "Cookie": f"session_id={self.session_id}"},
|
|
on_open=self.on_open, on_message=self.on_message,
|
|
on_error=on_error, on_close=self.on_close)
|
|
|
|
# The IoT synchronised servers can stop in 2 ways that we need to handle:
|
|
# A. Gracefully:
|
|
# In this case a disconnection signal is sent to the IoT-box
|
|
# The websocket is properly closed, but it needs to be established a new connection when
|
|
# the server will be back.
|
|
#
|
|
# B. Forced/killed:
|
|
# In this case there is no disconnection signal received
|
|
#
|
|
# This will also happen with the graceful quit as `reconnect` will trigger if the server
|
|
# is offline while attempting the new connection
|
|
while True:
|
|
try:
|
|
run_res = self.ws.run_forever(reconnect=10)
|
|
_logger.debug("websocket run_forever return with %s", run_res)
|
|
except Exception:
|
|
_logger.exception("An unexpected exception happened when running the websocket")
|
|
_logger.debug('websocket will try to restart in 10 seconds')
|
|
time.sleep(10)
|