oca-ocb-core/odoo-bringout-oca-ocb-iot_drivers/iot_drivers/websocket_client.py
Ernad Husremovic aee3ee8bf7 add missing payment providers and iot modules for 19.0
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
2026-03-09 15:45:22 +01:00

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)