如何在Odoo中实现单设备及跨设备播放扫码缺品提示音
解决方案:Odoo单设备与跨设备声音播放实现
一、先拆解原代码的核心问题
你当前的代码是直接在Odoo服务端执行音频生成和播放逻辑——这就是为什么单设备(比如本地开发时,服务端和客户端在同一机器)能正常出声,但多设备场景下,声音其实是在服务器上播放的,扫码的客户端设备根本听不到。要实现跨设备播放,必须把音频播放逻辑转移到客户端浏览器中。
二、单设备场景的优化方案
如果只需要在服务端所在设备播放声音,我们先修正原代码的语法问题,并添加必要的异常处理和依赖导入:
import math import pyaudio from odoo import api, models class YourModel(models.Model): _inherit = 'your.model.name' # 替换成你实际继承的业务模型 @api.multi def _product_sound(self): try: PyAudio = pyaudio.PyAudio() bitrate = 8000 frq = 500 LENGTH = 2 if frq > bitrate: bitrate = frq + 100 numberofframe = int(bitrate * LENGTH) restframe = numberofframe % bitrate wave = '' for x in range(numberofframe): wave += chr(int(math.sin(x / ((bitrate / frq) / math.pi)) * 124 + 128)) for x in range(restframe): wave += chr(128) p = PyAudio() stream = p.open( format=p.get_format_from_width(1), channels=1, rate=bitrate, output=True ) stream.write(wave) stream.stop_stream() stream.close() p.terminate() except Exception as e: # 记录错误日志,方便排查问题 self.env['ir.logging'].sudo().create({ 'name': 'Product Sound Playback Error', 'type': 'server', 'message': str(e), 'level': 'error', })
单设备场景注意点:
- 确保Odoo服务器所在设备安装了
pyaudio依赖:执行pip install pyaudio - 仅适用于服务端和客户端在同一机器的场景,比如本地开发或单工位专属设备
三、跨设备(多客户端)场景的正确实现
要让每个扫码的客户端设备播放声音,必须用前端JavaScript触发浏览器的音频播放,下面是两种常用方案:
方式1:预加载自定义音频文件播放
适合使用固定提示音的场景,步骤如下:
- 在模块的
static/src/sounds目录下放入你的提示音文件(比如not_found.wav) - 在XML视图中注入音频资源:
<template id="assets_backend" inherit_id="web.assets_backend"> <xpath expr="." position="inside"> <audio id="product_not_found_sound" preload="auto"> <source src="/your_module/static/src/sounds/not_found.wav" type="audio/wav"/> </audio> </xpath> </template>
- 在Python方法中触发前端动作:
from odoo import api, models class YourModel(models.Model): _inherit = 'your.model.name' @api.multi def _product_sound(self): # 通知前端播放声音 return { 'type': 'ir.actions.client', 'tag': 'play_product_not_found_sound', }
- 在JS文件中注册播放动作:
odoo.define('your_module.sound_playback', function(require) { "use strict"; var core = require('web.core'); core.action_registry.add('play_product_not_found_sound', function() { var audio = document.getElementById('product_not_found_sound'); if (audio) { audio.currentTime = 0; // 重置播放位置,确保每次从头播放 audio.play().catch(error => console.error("播放失败:", error)); } }); });
方式2:动态生成音频并播放
如果需要像原代码那样生成正弦波提示音,可以在Python中生成音频数据转成Base64,再传递给前端播放:
import math import base64 from io import BytesIO import wave from odoo import api, models class YourModel(models.Model): _inherit = 'your.model.name' @api.multi def _product_sound(self): # 生成正弦波音频数据 bitrate = 8000 frq = 500 duration = 2 sample_width = 1 channels = 1 if frq > bitrate: bitrate = frq + 100 num_frames = int(bitrate * duration) rest_frames = num_frames % bitrate # 生成音频帧 frames = bytearray() for x in range(num_frames): sample = int(math.sin(x / ((bitrate / frq) / math.pi)) * 124 + 128) frames.append(sample) for x in range(rest_frames): frames.append(128) # 写入WAV格式并转Base64 wav_buffer = BytesIO() with wave.open(wav_buffer, 'wb') as wav_file: wav_file.setnchannels(channels) wav_file.setsampwidth(sample_width) wav_file.setframerate(bitrate) wav_file.writeframes(frames) wav_base64 = base64.b64encode(wav_buffer.getvalue()).decode('utf-8') audio_data_url = f"data:audio/wav;base64,{wav_base64}" # 传递给前端播放 return { 'type': 'ir.actions.client', 'tag': 'play_dynamic_sound', 'params': {'audio_url': audio_data_url} }
对应的JS代码:
odoo.define('your_module.dynamic_sound', function(require) { "use strict"; var core = require('web.core'); core.action_registry.add('play_dynamic_sound', function(action) { var audio = new Audio(action.params.audio_url); audio.currentTime = 0; audio.play().catch(error => console.error("动态音频播放失败:", error)); }); });
跨设备场景关键注意事项
- 浏览器有自动播放限制,必须确保播放动作是由用户交互触发的(比如扫码操作本身就是用户交互,符合要求)
- 自定义音频文件需要在模块的
__manifest__.py中声明静态资源,确保Odoo能正确加载
内容的提问来源于stack exchange,提问作者Harshit Trivedi
相关产品推荐
相关产品推荐

