feat(hass): 新增小爱音箱接入 Home Assistant Assist 的完整示例
- 新增完整的 Home Assistant 集成示例,支持文本对话与上下文会话 - 实现小爱音箱事件解析与 TTS 播报,支持实时打断功能 - 添加连续会话管理,支持区域上下文注入和会话超时结束 - 提供完整的配置系统、类型定义和错误处理 - 包含 Rust 扩展模块用于小爱音箱通信,支持音频流处理 - 添加单元测试、集成测试脚本和 Docker 部署支持 - 提供详细的使用文档和配置说明
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
use open_xiaoai::services::connect::message::MessageManager;
|
||||
use open_xiaoai::services::connect::rpc::RPC;
|
||||
use open_xiaoai::services::audio::config::AudioConfig;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyBytes;
|
||||
use serde_json::json;
|
||||
use server::AppServer;
|
||||
|
||||
pub mod macros;
|
||||
pub mod python;
|
||||
pub mod server;
|
||||
|
||||
#[pyfunction]
|
||||
fn on_output_data(py: Python, data: Py<PyBytes>) -> PyResult<Bound<PyAny>> {
|
||||
let bytes = data.as_bytes(py).to_vec();
|
||||
pyo3_async_runtimes::tokio::future_into_py(py, async move {
|
||||
let _ = MessageManager::instance()
|
||||
.send_stream("play", bytes, None)
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn start_server(py: Python) -> PyResult<Bound<PyAny>> {
|
||||
pyo3_async_runtimes::tokio::future_into_py(py, async {
|
||||
AppServer::run().await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn run_shell(py: Python, script: String, timeout_millis: f64) -> PyResult<Bound<PyAny>> {
|
||||
pyo3_async_runtimes::tokio::future_into_py(py, async move {
|
||||
let res = RPC::instance()
|
||||
.call_remote("run_shell", Some(json!(script)), Some(timeout_millis as u64))
|
||||
.await;
|
||||
let result = match res {
|
||||
Err(e) => format!("run_shell error: {}", e),
|
||||
Ok(res) => match res.data {
|
||||
Some(data) => serde_json::to_string(&data).unwrap_or_default(),
|
||||
None => String::new(),
|
||||
},
|
||||
};
|
||||
Ok(result)
|
||||
})
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn start_recording(py: Python, config_json: Option<String>) -> PyResult<Bound<PyAny>> {
|
||||
pyo3_async_runtimes::tokio::future_into_py(py, async move {
|
||||
let config = config_json
|
||||
.and_then(|text| serde_json::from_str::<AudioConfig>(&text).ok());
|
||||
let res = RPC::instance()
|
||||
.call_remote("start_recording", config.map(|c| json!(c)), None)
|
||||
.await;
|
||||
Ok(match res {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn stop_recording(py: Python) -> PyResult<Bound<PyAny>> {
|
||||
pyo3_async_runtimes::tokio::future_into_py(py, async move {
|
||||
let res = RPC::instance().call_remote("stop_recording", None, None).await;
|
||||
Ok(match res {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn start_play(py: Python, config_json: Option<String>) -> PyResult<Bound<PyAny>> {
|
||||
pyo3_async_runtimes::tokio::future_into_py(py, async move {
|
||||
let config = config_json
|
||||
.and_then(|text| serde_json::from_str::<AudioConfig>(&text).ok());
|
||||
let res = RPC::instance()
|
||||
.call_remote("start_play", config.map(|c| json!(c)), None)
|
||||
.await;
|
||||
Ok(match res {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn stop_play(py: Python) -> PyResult<Bound<PyAny>> {
|
||||
pyo3_async_runtimes::tokio::future_into_py(py, async move {
|
||||
let res = RPC::instance().call_remote("stop_play", None, None).await;
|
||||
Ok(match res {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[pymodule]
|
||||
fn open_xiaoai_server(_py: Python, m: Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(start_server, &m)?)?;
|
||||
m.add_function(wrap_pyfunction!(on_output_data, &m)?)?;
|
||||
m.add_function(wrap_pyfunction!(run_shell, &m)?)?;
|
||||
m.add_function(wrap_pyfunction!(start_recording, &m)?)?;
|
||||
m.add_function(wrap_pyfunction!(stop_recording, &m)?)?;
|
||||
m.add_function(wrap_pyfunction!(start_play, &m)?)?;
|
||||
m.add_function(wrap_pyfunction!(stop_play, &m)?)?;
|
||||
crate::python::init_module(&m)?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user