mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
update:websocket播报6位验证码 (#821)
This commit is contained in:
@@ -160,7 +160,7 @@ server:
|
|||||||
|
|
||||||
```
|
```
|
||||||
智控台地址: https://2662r3426b.vicp.fun
|
智控台地址: https://2662r3426b.vicp.fun
|
||||||
OTA接口地址: htts://2662r3426b.vicp.fun/xiaozhi/ota/
|
OTA接口地址: https://2662r3426b.vicp.fun/xiaozhi/ota/
|
||||||
Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|
Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -52,4 +52,7 @@ public interface ErrorCode {
|
|||||||
int PARAM_BOOLEAN_INVALID = 10038;
|
int PARAM_BOOLEAN_INVALID = 10038;
|
||||||
int PARAM_ARRAY_INVALID = 10039;
|
int PARAM_ARRAY_INVALID = 10039;
|
||||||
int PARAM_JSON_INVALID = 10040;
|
int PARAM_JSON_INVALID = 10040;
|
||||||
|
|
||||||
|
int OTA_DEVICE_NOT_FOUND = 10041;
|
||||||
|
int OTA_DEVICE_NEED_BIND = 10042;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -9,6 +9,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import xiaozhi.common.exception.ErrorCode;
|
||||||
import xiaozhi.common.exception.RenException;
|
import xiaozhi.common.exception.RenException;
|
||||||
import xiaozhi.common.redis.RedisKeys;
|
import xiaozhi.common.redis.RedisKeys;
|
||||||
import xiaozhi.common.redis.RedisUtils;
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
@@ -83,7 +84,12 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
// 根据MAC地址查找设备
|
// 根据MAC地址查找设备
|
||||||
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
|
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
throw new RenException("设备未找到");
|
// 如果设备,去redis里看看有没有需要连接的设备
|
||||||
|
String cachedCode = deviceService.geCodeByDeviceId(macAddress);
|
||||||
|
if (StringUtils.isNotBlank(cachedCode)) {
|
||||||
|
throw new RenException(ErrorCode.OTA_DEVICE_NEED_BIND, cachedCode);
|
||||||
|
}
|
||||||
|
throw new RenException(ErrorCode.OTA_DEVICE_NOT_FOUND, "not found device");
|
||||||
}
|
}
|
||||||
// 获取智能体信息
|
// 获取智能体信息
|
||||||
AgentEntity agent = agentService.getAgentById(device.getAgentId());
|
AgentEntity agent = agentService.getAgentById(device.getAgentId());
|
||||||
|
|||||||
@@ -37,9 +37,7 @@ public class OTAController {
|
|||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<String> checkOTAVersion(
|
public ResponseEntity<String> checkOTAVersion(
|
||||||
@RequestBody DeviceReportReqDTO deviceReportReqDTO,
|
@RequestBody DeviceReportReqDTO deviceReportReqDTO,
|
||||||
|
|
||||||
@Parameter(name = "Device-Id", description = "设备唯一标识", required = true, in = ParameterIn.HEADER) @RequestHeader("Device-Id") String deviceId,
|
@Parameter(name = "Device-Id", description = "设备唯一标识", required = true, in = ParameterIn.HEADER) @RequestHeader("Device-Id") String deviceId,
|
||||||
|
|
||||||
@Parameter(name = "Client-Id", description = "客户端标识", required = true, in = ParameterIn.HEADER) @RequestHeader("Client-Id") String clientId) {
|
@Parameter(name = "Client-Id", description = "客户端标识", required = true, in = ParameterIn.HEADER) @RequestHeader("Client-Id") String clientId) {
|
||||||
if (StringUtils.isAnyBlank(deviceId, clientId)) {
|
if (StringUtils.isAnyBlank(deviceId, clientId)) {
|
||||||
return createResponse(DeviceReportRespDTO.createError("Device ID is required"));
|
return createResponse(DeviceReportRespDTO.createError("Device ID is required"));
|
||||||
@@ -50,10 +48,9 @@ public class OTAController {
|
|||||||
if (!deviceId.equals(macAddress) || !macAddressValid || deviceReportReqDTO.getApplication() == null) {
|
if (!deviceId.equals(macAddress) || !macAddressValid || deviceReportReqDTO.getApplication() == null) {
|
||||||
return createResponse(DeviceReportRespDTO.createError("Invalid OTA request"));
|
return createResponse(DeviceReportRespDTO.createError("Invalid OTA request"));
|
||||||
}
|
}
|
||||||
return createResponse(deviceService.checkDeviceActive(macAddress, deviceId, clientId, deviceReportReqDTO));
|
return createResponse(deviceService.checkDeviceActive(macAddress, clientId, deviceReportReqDTO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取 OTA 提示信息")
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<String> getOTAPrompt() {
|
public ResponseEntity<String> getOTAPrompt() {
|
||||||
return createResponse(DeviceReportRespDTO.createError("请提交正确的ota参数"));
|
return createResponse(DeviceReportRespDTO.createError("请提交正确的ota参数"));
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public interface DeviceService {
|
|||||||
/**
|
/**
|
||||||
* 检查设备是否激活
|
* 检查设备是否激活
|
||||||
*/
|
*/
|
||||||
DeviceReportRespDTO checkDeviceActive(String macAddress, String deviceId, String clientId,
|
DeviceReportRespDTO checkDeviceActive(String macAddress, String clientId,
|
||||||
DeviceReportReqDTO deviceReport);
|
DeviceReportReqDTO deviceReport);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,4 +74,12 @@ public interface DeviceService {
|
|||||||
* @return 设备信息
|
* @return 设备信息
|
||||||
*/
|
*/
|
||||||
DeviceEntity getDeviceByMacAddress(String macAddress);
|
DeviceEntity getDeviceByMacAddress(String macAddress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据设备ID获取激活码
|
||||||
|
*
|
||||||
|
* @param deviceId 设备ID
|
||||||
|
* @return 激活码
|
||||||
|
*/
|
||||||
|
String geCodeByDeviceId(String deviceId);
|
||||||
}
|
}
|
||||||
+59
-37
@@ -120,7 +120,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DeviceReportRespDTO checkDeviceActive(String macAddress, String deviceId, String clientId,
|
public DeviceReportRespDTO checkDeviceActive(String macAddress, String clientId,
|
||||||
DeviceReportReqDTO deviceReport) {
|
DeviceReportReqDTO deviceReport) {
|
||||||
DeviceReportRespDTO response = new DeviceReportRespDTO();
|
DeviceReportRespDTO response = new DeviceReportRespDTO();
|
||||||
response.setServer_time(buildServerTime());
|
response.setServer_time(buildServerTime());
|
||||||
@@ -132,46 +132,12 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
firmware.setUrl("http://localhost:8002/xiaozhi/ota/download");
|
firmware.setUrl("http://localhost:8002/xiaozhi/ota/download");
|
||||||
response.setFirmware(firmware);
|
response.setFirmware(firmware);
|
||||||
|
|
||||||
DeviceEntity deviceById = getDeviceById(deviceId);
|
DeviceEntity deviceById = getDeviceById(macAddress);
|
||||||
if (deviceById != null) { // 如果设备存在,则更新上次连接时间
|
if (deviceById != null) { // 如果设备存在,则更新上次连接时间
|
||||||
deviceById.setLastConnectedAt(new Date());
|
deviceById.setLastConnectedAt(new Date());
|
||||||
deviceDao.updateById(deviceById);
|
deviceDao.updateById(deviceById);
|
||||||
} else { // 如果设备不存在,则生成激活码
|
} else { // 如果设备不存在,则生成激活码
|
||||||
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
DeviceReportRespDTO.Activation code = buildActivation(macAddress, deviceReport);
|
||||||
String dataKey = String.format("ota:activation:data:%s", safeDeviceId);
|
|
||||||
|
|
||||||
Map<Object, Object> cacheMap = redisTemplate.opsForHash().entries(dataKey);
|
|
||||||
DeviceReportRespDTO.Activation code = new DeviceReportRespDTO.Activation();
|
|
||||||
|
|
||||||
if (cacheMap != null && cacheMap.containsKey("activation_code")) {
|
|
||||||
String cachedCode = (String) cacheMap.get("activation_code");
|
|
||||||
code.setCode(cachedCode);
|
|
||||||
code.setMessage(frontedUrl + "\n" + cachedCode);
|
|
||||||
} else {
|
|
||||||
String newCode = RandomUtil.randomNumbers(6);
|
|
||||||
code.setCode(newCode);
|
|
||||||
code.setMessage(frontedUrl + "\n" + newCode);
|
|
||||||
|
|
||||||
Map<String, Object> dataMap = new HashMap<>();
|
|
||||||
dataMap.put("id", deviceId);
|
|
||||||
dataMap.put("mac_address", macAddress);
|
|
||||||
dataMap.put("board", (deviceReport.getChipModelName() != null) ? deviceReport.getChipModelName()
|
|
||||||
: (deviceReport.getBoard() != null ? deviceReport.getBoard().getType() : "unknown"));
|
|
||||||
dataMap.put("app_version", (deviceReport.getApplication() != null)
|
|
||||||
? deviceReport.getApplication().getVersion()
|
|
||||||
: null);
|
|
||||||
dataMap.put("deviceId", deviceId);
|
|
||||||
dataMap.put("activation_code", newCode);
|
|
||||||
|
|
||||||
// 写入主数据 key
|
|
||||||
redisTemplate.opsForHash().putAll(dataKey, dataMap);
|
|
||||||
redisTemplate.expire(dataKey, 24, TimeUnit.HOURS);
|
|
||||||
|
|
||||||
// 写入反查激活码 key
|
|
||||||
String codeKey = "ota:activation:code:" + newCode;
|
|
||||||
redisTemplate.opsForValue().set(codeKey, deviceId, 24, TimeUnit.HOURS);
|
|
||||||
}
|
|
||||||
|
|
||||||
response.setActivation(code);
|
response.setActivation(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,4 +224,60 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
serverTime.setTimezone_offset(tz.getOffset(System.currentTimeMillis()) / (60 * 1000));
|
serverTime.setTimezone_offset(tz.getOffset(System.currentTimeMillis()) / (60 * 1000));
|
||||||
return serverTime;
|
return serverTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String geCodeByDeviceId(String deviceId) {
|
||||||
|
String dataKey = getDeviceCacheKey(deviceId);
|
||||||
|
|
||||||
|
Map<Object, Object> cacheMap = redisTemplate.opsForHash().entries(dataKey);
|
||||||
|
if (cacheMap != null && cacheMap.containsKey("activation_code")) {
|
||||||
|
String cachedCode = (String) cacheMap.get("activation_code");
|
||||||
|
return cachedCode;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDeviceCacheKey(String deviceId) {
|
||||||
|
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
||||||
|
String dataKey = String.format("ota:activation:data:%s", safeDeviceId);
|
||||||
|
return dataKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceReportRespDTO.Activation buildActivation(String deviceId, DeviceReportReqDTO deviceReport) {
|
||||||
|
DeviceReportRespDTO.Activation code = new DeviceReportRespDTO.Activation();
|
||||||
|
|
||||||
|
String cachedCode = geCodeByDeviceId(deviceId);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(cachedCode)) {
|
||||||
|
code.setCode(cachedCode);
|
||||||
|
code.setMessage(frontedUrl + "\n" + cachedCode);
|
||||||
|
} else {
|
||||||
|
String newCode = RandomUtil.randomNumbers(6);
|
||||||
|
code.setCode(newCode);
|
||||||
|
code.setMessage(frontedUrl + "\n" + newCode);
|
||||||
|
|
||||||
|
Map<String, Object> dataMap = new HashMap<>();
|
||||||
|
dataMap.put("id", deviceId);
|
||||||
|
dataMap.put("mac_address", deviceId);
|
||||||
|
|
||||||
|
dataMap.put("board", (deviceReport.getChipModelName() != null) ? deviceReport.getChipModelName()
|
||||||
|
: (deviceReport.getBoard() != null ? deviceReport.getBoard().getType() : "unknown"));
|
||||||
|
dataMap.put("app_version", (deviceReport.getApplication() != null)
|
||||||
|
? deviceReport.getApplication().getVersion()
|
||||||
|
: null);
|
||||||
|
|
||||||
|
dataMap.put("deviceId", deviceId);
|
||||||
|
dataMap.put("activation_code", newCode);
|
||||||
|
|
||||||
|
// 写入主数据 key
|
||||||
|
String dataKey = getDeviceCacheKey(deviceId);
|
||||||
|
redisTemplate.opsForHash().putAll(dataKey, dataMap);
|
||||||
|
redisTemplate.expire(dataKey, 24, TimeUnit.HOURS);
|
||||||
|
|
||||||
|
// 写入反查激活码 key
|
||||||
|
String codeKey = "ota:activation:code:" + newCode;
|
||||||
|
redisTemplate.opsForValue().set(codeKey, deviceId, 24, TimeUnit.HOURS);
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -41,3 +41,6 @@
|
|||||||
10038=\u53C2\u6570\u503C\u5FC5\u987B\u662Ftrue\u6216false
|
10038=\u53C2\u6570\u503C\u5FC5\u987B\u662Ftrue\u6216false
|
||||||
10039=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u6570\u7EC4\u683C\u5F0F
|
10039=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u6570\u7EC4\u683C\u5F0F
|
||||||
10040=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u683C\u5F0F
|
10040=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u683C\u5F0F
|
||||||
|
|
||||||
|
10041=\u8BBE\u5907\u672A\u627E\u5230
|
||||||
|
10042={0}
|
||||||
|
|||||||
@@ -40,4 +40,7 @@
|
|||||||
10037=Parameter value must be a valid number
|
10037=Parameter value must be a valid number
|
||||||
10038=Parameter value must be true or false
|
10038=Parameter value must be true or false
|
||||||
10039=Parameter value must be a valid JSON array format
|
10039=Parameter value must be a valid JSON array format
|
||||||
10040=Parameter value must be a valid JSON format
|
10040=Parameter value must be a valid JSON format
|
||||||
|
|
||||||
|
10041=Device not found
|
||||||
|
10042={0}
|
||||||
@@ -40,4 +40,7 @@
|
|||||||
10037=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684\u6570\u5B57
|
10037=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684\u6570\u5B57
|
||||||
10038=\u53C2\u6570\u503C\u5FC5\u987B\u662Ftrue\u6216false
|
10038=\u53C2\u6570\u503C\u5FC5\u987B\u662Ftrue\u6216false
|
||||||
10039=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u6570\u7EC4\u683C\u5F0F
|
10039=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u6570\u7EC4\u683C\u5F0F
|
||||||
10040=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u683C\u5F0F
|
10040=\u53C2\u6570\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u683C\u5F0F
|
||||||
|
|
||||||
|
10041=\u8BBE\u5907\u672A\u627E\u5230
|
||||||
|
10042={0}
|
||||||
@@ -40,4 +40,7 @@
|
|||||||
10037=\u53C3\u6578\u503C\u5FC5\u9808\u662F\u6709\u6548\u7684\u6578\u5B57
|
10037=\u53C3\u6578\u503C\u5FC5\u9808\u662F\u6709\u6548\u7684\u6578\u5B57
|
||||||
10038=\u53C3\u6578\u503C\u5FC5\u9808\u662Ftrue\u6216false
|
10038=\u53C3\u6578\u503C\u5FC5\u9808\u662Ftrue\u6216false
|
||||||
10039=\u53C3\u6578\u503C\u5FC5\u9808\u662F\u6709\u6548\u7684JSON\u6578\u7D44\u683C\u5F0F
|
10039=\u53C3\u6578\u503C\u5FC5\u9808\u662F\u6709\u6548\u7684JSON\u6578\u7D44\u683C\u5F0F
|
||||||
10040=\u53C3\u6578\u503C\u5FC5\u9808\u662F\u6709\u6548\u7684JSON\u683C\u5F0F
|
10040=\u53C3\u6578\u503C\u5FC5\u9808\u662F\u6709\u6548\u7684JSON\u683C\u5F0F
|
||||||
|
|
||||||
|
10041=\u8A2D\u5099\u672A\u627E\u5230
|
||||||
|
10042={0}
|
||||||
@@ -33,3 +33,5 @@ timbre.ttsModelId.require=\u97F3\u8272\u7684tts\u4E3B\u952E\u4E0D\u53EF\u4EE5\u4
|
|||||||
timbre.ttsVoice.require=\u97F3\u8272\u7684\u7F16\u7801\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
|
timbre.ttsVoice.require=\u97F3\u8272\u7684\u7F16\u7801\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
|
||||||
timbre.voiceDemo.require=\u97F3\u8272\u7684\u64AD\u653EDemo\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
|
timbre.voiceDemo.require=\u97F3\u8272\u7684\u64AD\u653EDemo\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
|
||||||
|
|
||||||
|
ota.device.not.found=\u8BBE\u5907\u672A\u627E\u5230
|
||||||
|
ota.device.need.bind={0}
|
||||||
|
|||||||
@@ -32,3 +32,5 @@ timbre.ttsModelId.require=The TTS model ID of the timbre cannot be empty
|
|||||||
timbre.ttsVoice.require=The TTS voice of the timbre cannot be empty
|
timbre.ttsVoice.require=The TTS voice of the timbre cannot be empty
|
||||||
timbre.voiceDemo.require=The voice demo of the timbre cannot be empty
|
timbre.voiceDemo.require=The voice demo of the timbre cannot be empty
|
||||||
|
|
||||||
|
ota.device.not.found=Device not found
|
||||||
|
ota.device.need.bind={0}
|
||||||
@@ -32,3 +32,5 @@ timbre.ttsModelId.require=\u97F3\u8272\u7684tts\u4E3B\u9375\u4E0D\u53EF\u4EE5\u7
|
|||||||
timbre.ttsVoice.require=\u97F3\u8272\u7684\u7DE8\u78BC\u4E0D\u53EF\u4EE5\u70BA\u7A7A
|
timbre.ttsVoice.require=\u97F3\u8272\u7684\u7DE8\u78BC\u4E0D\u53EF\u4EE5\u70BA\u7A7A
|
||||||
timbre.voiceDemo.require=\u97F3\u8272\u7684\u64AD\u653E\u5730\u5740\u4E0D\u53EF\u4EE5\u70BA\u7A7A
|
timbre.voiceDemo.require=\u97F3\u8272\u7684\u64AD\u653E\u5730\u5740\u4E0D\u53EF\u4EE5\u70BA\u7A7A
|
||||||
|
|
||||||
|
ota.device.not.found=\u8A2D\u5099\u672A\u627E\u5230
|
||||||
|
ota.device.need.bind={0}
|
||||||
@@ -31,3 +31,6 @@ timbre.remark.require=\u97F3\u8272\u7684\u5907\u6CE8\u4E0D\u53EF\u4EE5\u4E3A\u7A
|
|||||||
timbre.ttsModelId.require=\u97F3\u8272\u7684tts\u4E3B\u952E\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
|
timbre.ttsModelId.require=\u97F3\u8272\u7684tts\u4E3B\u952E\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
|
||||||
timbre.ttsVoice.require=\u97F3\u8272\u7684\u7F16\u7801\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
|
timbre.ttsVoice.require=\u97F3\u8272\u7684\u7F16\u7801\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
|
||||||
timbre.voiceDemo.require=\u97F3\u8272\u7684\u64AD\u653EDemo\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
|
timbre.voiceDemo.require=\u97F3\u8272\u7684\u64AD\u653EDemo\u4E0D\u53EF\u4EE5\u4E3A\u7A7A
|
||||||
|
|
||||||
|
ota.device.not.found=\u8A2D\u5099\u672A\u627E\u5230
|
||||||
|
ota.device.need.bind={0}
|
||||||
@@ -4,6 +4,17 @@ import requests
|
|||||||
import yaml
|
import yaml
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceNotFoundException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceBindException(Exception):
|
||||||
|
def __init__(self, bind_code):
|
||||||
|
self.bind_code = bind_code
|
||||||
|
super().__init__(f"设备绑定异常,绑定码: {bind_code}")
|
||||||
|
|
||||||
|
|
||||||
# 添加全局配置缓存
|
# 添加全局配置缓存
|
||||||
_config_cache = None
|
_config_cache = None
|
||||||
|
|
||||||
@@ -83,7 +94,11 @@ def _make_api_request(api_url, secret, endpoint, json_data=None):
|
|||||||
response = requests.post(f"{api_url}{endpoint}", json=json_data)
|
response = requests.post(f"{api_url}{endpoint}", json=json_data)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
if result.get("code") != 0:
|
if result.get("code") == 10041:
|
||||||
|
raise DeviceNotFoundException(result.get("msg"))
|
||||||
|
elif result.get("code") == 10042:
|
||||||
|
raise DeviceBindException(result.get("msg"))
|
||||||
|
elif result.get("code") != 0:
|
||||||
raise Exception(f"API返回错误: {result.get('msg', '未知错误')}")
|
raise Exception(f"API返回错误: {result.get('msg', '未知错误')}")
|
||||||
return result.get("data")
|
return result.get("data")
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ from core.handle.functionHandler import FunctionHandler
|
|||||||
from plugins_func.register import Action, ActionResponse
|
from plugins_func.register import Action, ActionResponse
|
||||||
from core.auth import AuthMiddleware, AuthenticationError
|
from core.auth import AuthMiddleware, AuthenticationError
|
||||||
from core.mcp.manager import MCPManager
|
from core.mcp.manager import MCPManager
|
||||||
from config.config_loader import get_private_config_from_api
|
from config.config_loader import (
|
||||||
|
get_private_config_from_api,
|
||||||
|
DeviceNotFoundException,
|
||||||
|
DeviceBindException,
|
||||||
|
)
|
||||||
|
|
||||||
TAG = __name__
|
TAG = __name__
|
||||||
|
|
||||||
@@ -46,6 +50,9 @@ class ConnectionHandler:
|
|||||||
self.logger = setup_logging()
|
self.logger = setup_logging()
|
||||||
self.auth = AuthMiddleware(config)
|
self.auth = AuthMiddleware(config)
|
||||||
|
|
||||||
|
self.need_bind = False
|
||||||
|
self.bind_code = None
|
||||||
|
|
||||||
self.websocket = None
|
self.websocket = None
|
||||||
self.headers = None
|
self.headers = None
|
||||||
self.client_ip = None
|
self.client_ip = None
|
||||||
@@ -206,7 +213,15 @@ class ConnectionHandler:
|
|||||||
)
|
)
|
||||||
private_config["delete_audio"] = bool(self.config.get("delete_audio", True))
|
private_config["delete_audio"] = bool(self.config.get("delete_audio", True))
|
||||||
self.logger.bind(tag=TAG).info(f"获取差异化配置成功: {private_config}")
|
self.logger.bind(tag=TAG).info(f"获取差异化配置成功: {private_config}")
|
||||||
|
except DeviceNotFoundException as e:
|
||||||
|
self.need_bind = True
|
||||||
|
private_config = {}
|
||||||
|
except DeviceBindException as e:
|
||||||
|
self.need_bind = True
|
||||||
|
self.bind_code = e.bind_code
|
||||||
|
private_config = {}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
self.need_bind = True
|
||||||
self.logger.bind(tag=TAG).error(f"获取差异化配置失败: {e}")
|
self.logger.bind(tag=TAG).error(f"获取差异化配置失败: {e}")
|
||||||
private_config = {}
|
private_config = {}
|
||||||
|
|
||||||
@@ -345,7 +360,6 @@ class ConnectionHandler:
|
|||||||
response_message = []
|
response_message = []
|
||||||
processed_chars = 0 # 跟踪已处理的字符位置
|
processed_chars = 0 # 跟踪已处理的字符位置
|
||||||
try:
|
try:
|
||||||
start_time = time.time()
|
|
||||||
# 使用带记忆的对话
|
# 使用带记忆的对话
|
||||||
future = asyncio.run_coroutine_threadsafe(
|
future = asyncio.run_coroutine_threadsafe(
|
||||||
self.memory.query_memory(query), self.loop
|
self.memory.query_memory(query), self.loop
|
||||||
@@ -367,9 +381,6 @@ class ConnectionHandler:
|
|||||||
if self.client_abort:
|
if self.client_abort:
|
||||||
break
|
break
|
||||||
|
|
||||||
end_time = time.time()
|
|
||||||
# self.logger.bind(tag=TAG).debug(f"大模型返回时间: {end_time - start_time} 秒, 生成token={content}")
|
|
||||||
|
|
||||||
# 合并当前全部文本并处理未分割部分
|
# 合并当前全部文本并处理未分割部分
|
||||||
full_text = "".join(response_message)
|
full_text = "".join(response_message)
|
||||||
current_text = full_text[processed_chars:] # 从未处理的位置开始
|
current_text = full_text[processed_chars:] # 从未处理的位置开始
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from config.logger import setup_logging
|
from config.logger import setup_logging
|
||||||
import time
|
import time
|
||||||
|
import asyncio
|
||||||
from core.utils.util import remove_punctuation_and_length
|
from core.utils.util import remove_punctuation_and_length
|
||||||
from core.handle.sendAudioHandle import send_stt_message
|
from core.handle.sendAudioHandle import send_stt_message
|
||||||
from core.handle.intentHandler import handle_user_intent
|
from core.handle.intentHandler import handle_user_intent
|
||||||
@@ -35,9 +36,7 @@ async def handleAudioMessage(conn, audio):
|
|||||||
if len(conn.asr_audio) < 15:
|
if len(conn.asr_audio) < 15:
|
||||||
conn.asr_server_receive = True
|
conn.asr_server_receive = True
|
||||||
else:
|
else:
|
||||||
text, file_path = await conn.asr.speech_to_text(
|
text, _ = await conn.asr.speech_to_text(conn.asr_audio, conn.session_id)
|
||||||
conn.asr_audio, conn.session_id
|
|
||||||
)
|
|
||||||
logger.bind(tag=TAG).info(f"识别文本: {text}")
|
logger.bind(tag=TAG).info(f"识别文本: {text}")
|
||||||
text_len, _ = remove_punctuation_and_length(text)
|
text_len, _ = remove_punctuation_and_length(text)
|
||||||
if text_len > 0:
|
if text_len > 0:
|
||||||
@@ -49,6 +48,9 @@ async def handleAudioMessage(conn, audio):
|
|||||||
|
|
||||||
|
|
||||||
async def startToChat(conn, text):
|
async def startToChat(conn, text):
|
||||||
|
if conn.need_bind:
|
||||||
|
await check_bind_device(conn)
|
||||||
|
return
|
||||||
# 首先进行意图分析
|
# 首先进行意图分析
|
||||||
intent_handled = await handle_user_intent(conn, text)
|
intent_handled = await handle_user_intent(conn, text)
|
||||||
|
|
||||||
@@ -85,3 +87,44 @@ async def no_voice_close_connect(conn):
|
|||||||
"请你以“时间过得真快”未来头,用富有感情、依依不舍的话来结束这场对话吧。"
|
"请你以“时间过得真快”未来头,用富有感情、依依不舍的话来结束这场对话吧。"
|
||||||
)
|
)
|
||||||
await startToChat(conn, prompt)
|
await startToChat(conn, prompt)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_bind_device(conn):
|
||||||
|
if conn.bind_code:
|
||||||
|
# 确保bind_code是6位数字
|
||||||
|
if len(conn.bind_code) != 6:
|
||||||
|
logger.bind(tag=TAG).error(f"无效的绑定码格式: {conn.bind_code}")
|
||||||
|
text = "绑定码格式错误,请检查配置。"
|
||||||
|
await send_stt_message(conn, text)
|
||||||
|
return
|
||||||
|
|
||||||
|
text = f"请登录控制面板,输入{conn.bind_code},绑定设备。"
|
||||||
|
await send_stt_message(conn, text)
|
||||||
|
conn.tts_first_text_index = 0
|
||||||
|
conn.tts_last_text_index = 6
|
||||||
|
conn.llm_finish_task = True
|
||||||
|
|
||||||
|
# 播放提示音
|
||||||
|
music_path = "config/assets/bind_code.wav"
|
||||||
|
opus_packets, _ = conn.tts.audio_to_opus_data(music_path)
|
||||||
|
conn.audio_play_queue.put((opus_packets, text, 0))
|
||||||
|
|
||||||
|
# 逐个播放数字
|
||||||
|
for i in range(6): # 确保只播放6位数字
|
||||||
|
try:
|
||||||
|
digit = conn.bind_code[i]
|
||||||
|
num_path = f"config/assets/bind_code/{digit}.wav"
|
||||||
|
num_packets, _ = conn.tts.audio_to_opus_data(num_path)
|
||||||
|
conn.audio_play_queue.put((num_packets, text, i + 1))
|
||||||
|
except Exception as e:
|
||||||
|
logger.bind(tag=TAG).error(f"播放数字音频失败: {e}")
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
text = f"没有找到该设备的版本信息,请正确配置 OTA地址,然后重新编译固件。"
|
||||||
|
await send_stt_message(conn, text)
|
||||||
|
conn.tts_first_text_index = 0
|
||||||
|
conn.tts_last_text_index = 0
|
||||||
|
conn.llm_finish_task = True
|
||||||
|
music_path = "config/assets/bind_not_found.wav"
|
||||||
|
opus_packets, _ = conn.tts.audio_to_opus_data(music_path)
|
||||||
|
conn.audio_play_queue.put((opus_packets, text, 0))
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class TTSProvider(TTSProviderBase):
|
|||||||
self.voice = config.get("voice")
|
self.voice = config.get("voice")
|
||||||
self.response_format = config.get("response_format")
|
self.response_format = config.get("response_format")
|
||||||
self.sample_rate = config.get("sample_rate")
|
self.sample_rate = config.get("sample_rate")
|
||||||
self.speed = float(config.get("speed"))
|
self.speed = float(config.get("speed", 1.0))
|
||||||
self.gain = config.get("gain")
|
self.gain = config.get("gain")
|
||||||
|
|
||||||
self.host = "api.siliconflow.cn"
|
self.host = "api.siliconflow.cn"
|
||||||
|
|||||||
Reference in New Issue
Block a user