Files
encoach_backend_new_v2/odoo/addons/iot_drivers/webrtc_client.py
Yamen Ahmad 3e83d8d7d5 feat: add complete EnCoach backend — Odoo 19 + all addons
Includes:
- Odoo 19 framework (odoo/)
- 27 custom EnCoach addons (new_project/custom_addons/)
  - encoach_core, encoach_api, encoach_lms_api, encoach_adaptive_api
  - encoach_exam, encoach_taxonomy, encoach_adaptive, encoach_assignment
  - encoach_ai, encoach_ai_grading, encoach_ai_generation, encoach_ai_media
  - encoach_courseware, encoach_communication, encoach_subscription
  - encoach_notification, encoach_approval, encoach_branding
  - encoach_classroom, encoach_registration, encoach_stats
  - encoach_faq, encoach_ticket, encoach_training, encoach_resources
  - encoach_adaptive_ai, encoach_sis
- 21 OpenEduCat Enterprise modules (new_project/enterprise-19/)
- 14 OpenEduCat Community modules (new_project/openeducat_erp-19.0/)
- Configuration: odoo.conf, requirements.txt, scripts
- 200+ REST API endpoints with JWT authentication
- SRS and test documentation

Made-with: Cursor
2026-04-01 17:10:04 +04:00

121 lines
4.7 KiB
Python

import asyncio
import json
import logging
from threading import Thread
import time
try:
from aiortc import RTCDataChannel, RTCPeerConnection, RTCSessionDescription
webrtc_client = True
except ImportError:
webrtc_client = None
from odoo.addons.iot_drivers import main
from odoo.addons.iot_drivers.tools import helpers
_logger = logging.getLogger(__name__)
class WebRtcClient(Thread):
def __init__(self):
super().__init__(daemon=True)
self.connections: set[RTCDataChannel] = set()
self.chunked_message_in_progress: dict[RTCDataChannel, str] = {}
self.event_loop = asyncio.new_event_loop()
def offer(self, request: dict):
return asyncio.run_coroutine_threadsafe(
self._offer(request), self.event_loop
).result()
def send(self, data: dict):
asyncio.run_coroutine_threadsafe(
self._send(data), self.event_loop
)
async def _offer(self, request: dict):
offer = RTCSessionDescription(sdp=request["sdp"], type=request["type"])
peer_connection = RTCPeerConnection()
@peer_connection.on("datachannel")
def on_datachannel(channel: RTCDataChannel):
self.connections.add(channel)
@channel.on("message")
async def on_message(message_str: str):
# Handle chunked message
if self.chunked_message_in_progress.get(channel) is not None:
if message_str == "chunked_end":
message_str = self.chunked_message_in_progress.pop(channel)
else:
self.chunked_message_in_progress[channel] += message_str
return
elif message_str == "chunked_start":
self.chunked_message_in_progress[channel] = ""
return
# Handle regular message
message = json.loads(message_str)
message_type = message["message_type"]
_logger.info("Received message of type %s", message_type)
if message_type == "iot_action":
device_identifier = message["device_identifier"]
data = message["data"]
data["session_id"] = message["session_id"]
if device_identifier in main.iot_devices:
start_operation_time = time.perf_counter()
_logger.info("device '%s' action started", device_identifier)
await self.event_loop.run_in_executor(None, lambda: main.iot_devices[device_identifier].action(data))
_logger.info("device '%s' action finished - %.*f", device_identifier, 3, time.perf_counter() - start_operation_time)
else:
# Notify that the device is not connected
self.send({
'owner': message['session_id'],
'device_identifier': device_identifier,
'time': time.time(),
'status': 'disconnected',
})
elif message_type == "test_protocol":
self.send({
'owner': message['session_id'],
'device_identifier': helpers.get_identifier(),
'time': time.time(),
'status': 'success',
})
elif message_type == "restart_odoo":
self.send({
'owner': message['session_id'],
'device_identifier': helpers.get_identifier(),
'time': time.time(),
'status': 'success',
})
await self.event_loop.run_in_executor(None, helpers.odoo_restart)
@channel.on("close")
def on_close():
self.connections.discard(channel)
@peer_connection.on("connectionstatechange")
async def on_connectionstatechange():
if peer_connection.connectionState == "failed":
await peer_connection.close()
await peer_connection.setRemoteDescription(offer)
answer = await peer_connection.createAnswer()
await peer_connection.setLocalDescription(answer)
return {"sdp": peer_connection.localDescription.sdp, "type": peer_connection.localDescription.type}
async def _send(self, data: dict):
for connection in self.connections:
connection.send(json.dumps(data))
def run(self):
self.event_loop.run_forever()
if webrtc_client:
webrtc_client = WebRtcClient()
webrtc_client.start()