mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-21 22:53:56 +08:00
Merge pull request #999 from xinnan-tech/auto-ota-update
Auto ota update
This commit is contained in:
+23
-5
@@ -122,11 +122,11 @@ http://localhost:8002/xiaozhi/doc.html
|
||||
|
||||
这时,你需要使用浏览器,打开`智控台`,链接:http://127.0.0.1:8002 ,注册第一个用户。第一个用户即是超级管理员,以后的用户都是普通用户。普通用户只能绑定设备和配置智能体;超级管理员可以进行模型管理、用户管理、参数配置等功能。
|
||||
|
||||
接下来要做两件重要的事情:
|
||||
接下来要做三件重要的事情:
|
||||
|
||||
### 第一件重要的事情
|
||||
|
||||
使用超级管理员账号,登录系统,在顶部菜单找到`参数管理`,找到列表中第三条数据,参数编码是`server.secret`,复制它到`参数值`。
|
||||
使用超级管理员账号,登录系统,在顶部菜单找到`参数管理`,找到列表中第一条数据,参数编码是`server.secret`,复制它到`参数值`。
|
||||
|
||||
`server.secret`需要说明一下,这个`参数值`很重要,作用是让我们的`Server`端连接`manager-api`。`server.secret`是每次从零部署manager模块时,会自动随机生成的密钥。
|
||||
|
||||
@@ -138,7 +138,12 @@ manager-api:
|
||||
secret: 你的server.secret值
|
||||
```
|
||||
1、把你刚才从`智控台`复制过来的`server.secret`的`参数值`复制到`.config.yaml`文件里的`secret`里。
|
||||
2、注意,把`url`改成下面的`http://xiaozhi-esp32-server-web:8002/xiaozhi`
|
||||
|
||||
2、因为你是docker部署,把`url`改成下面的`http://xiaozhi-esp32-server-web:8002/xiaozhi`
|
||||
|
||||
3、因为你是docker部署,把`url`改成下面的`http://xiaozhi-esp32-server-web:8002/xiaozhi`
|
||||
|
||||
4、因为你是docker部署,把`url`改成下面的`http://xiaozhi-esp32-server-web:8002/xiaozhi`
|
||||
|
||||
类似这样的效果
|
||||
```
|
||||
@@ -182,6 +187,12 @@ Websocket接口:
|
||||
ws://你电脑局域网的ip:8000/xiaozhi/v1/
|
||||
```
|
||||
|
||||
### 第三件重要的事情
|
||||
|
||||
使用超级管理员账号,登录系统,在顶部菜单找到`参数管理`,找到参数编码是`server.websocket`,输入你的`Websocket接口`。
|
||||
|
||||
使用超级管理员账号,登录系统,在顶部菜单找到`参数管理`,找到数编码是`server.ota`,输入你的`OTA接口`。
|
||||
|
||||
接下来,你就可以开始操作你的esp32设备了,你可以`自行编译esp32固件`也可以配置使用`虾哥编译好的1.6.1以上版本的固件`。两个任选一个
|
||||
|
||||
1、 [编译自己的esp32固件](firmware-build.md)了。
|
||||
@@ -353,7 +364,7 @@ pip install -r requirements.txt
|
||||
|
||||
## 8.配置项目文件
|
||||
|
||||
使用超级管理员账号,登录智控台 http://127.0.0.1:8002 ,在顶部菜单找到`参数管理`,找到列表中第三条数据,参数编码是`server.secret`,复制它到`参数值`。
|
||||
使用超级管理员账号,登录智控台 http://127.0.0.1:8002 ,在顶部菜单找到`参数管理`,找到列表中第一条数据,参数编码是`server.secret`,复制它到`参数值`。
|
||||
|
||||
`server.secret`需要说明一下,这个`参数值`很重要,作用是让我们的`Server`端连接`manager-api`。`server.secret`是每次从零部署manager模块时,会自动随机生成的密钥。
|
||||
|
||||
@@ -394,7 +405,7 @@ python app.py
|
||||
25-02-23 12:01:09[core.websocket_server] - INFO - =======================================================
|
||||
```
|
||||
|
||||
由于你是全模块部署,因此你有两个重要的接口需要写入到esp32中。
|
||||
由于你是全模块部署,因此你有两个重要的接口。
|
||||
|
||||
OTA接口:
|
||||
```
|
||||
@@ -406,6 +417,13 @@ Websocket接口:
|
||||
ws://你电脑局域网的ip:8000/xiaozhi/v1/
|
||||
```
|
||||
|
||||
请你务必把以上两个接口地址写入到智控台中:他们将会影响websocket地址发放和自动升级功能。
|
||||
|
||||
1、使用超级管理员账号,登录系统,在顶部菜单找到`参数管理`,找到参数编码是`server.websocket`,输入你的`Websocket接口`。
|
||||
|
||||
2、使用超级管理员账号,登录系统,在顶部菜单找到`参数管理`,找到数编码是`server.ota`,输入你的`OTA接口`。
|
||||
|
||||
|
||||
接下来,你就可以开始操作你的esp32设备了,你可以`自行编译esp32固件`也可以配置使用`虾哥编译好的1.6.1以上版本的固件`。两个任选一个
|
||||
|
||||
1、 [编译自己的esp32固件](firmware-build.md)了。
|
||||
|
||||
@@ -36,24 +36,24 @@ ws://192.168.1.25:8000/xiaozhi/v1/
|
||||
|
||||
## 第4步 修改OTA地址
|
||||
|
||||
找到`OTA_VERSION_URL`的`default`的内容,把`https://api.tenclass.net/xiaozhi/ota/`
|
||||
找到`OTA_URL`的`default`的内容,把`https://api.tenclass.net/xiaozhi/ota/`
|
||||
改成你自己的地址,例如,我的接口地址是`http://192.168.1.25:8002/xiaozhi/ota/`,就把内容改成这个。
|
||||
|
||||
修改前:
|
||||
```
|
||||
config OTA_VERSION_URL
|
||||
string "OTA Version URL"
|
||||
config OTA_URL
|
||||
string "Default OTA URL"
|
||||
default "https://api.tenclass.net/xiaozhi/ota/"
|
||||
help
|
||||
The application will access this URL to check for updates.
|
||||
The application will access this URL to check for new firmwares and server address.
|
||||
```
|
||||
修改后:
|
||||
```
|
||||
config OTA_VERSION_URL
|
||||
string "OTA Version URL"
|
||||
config OTA_URL
|
||||
string "Default OTA URL"
|
||||
default "http://192.168.1.25:8002/xiaozhi/ota/"
|
||||
help
|
||||
The application will access this URL to check for updates.
|
||||
The application will access this URL to check for new firmwares and server address.
|
||||
```
|
||||
|
||||
## 第4步 设置编译参数
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package xiaozhi.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* RestTemplate配置
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,11 @@ public interface Constant {
|
||||
*/
|
||||
String SERVER_WEBSOCKET = "server.websocket";
|
||||
|
||||
/**
|
||||
* ota地址
|
||||
*/
|
||||
String SERVER_OTA = "server.ota";
|
||||
|
||||
/**
|
||||
* 是否允许用户注册
|
||||
*/
|
||||
|
||||
+13
@@ -7,6 +7,7 @@ import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -79,4 +80,16 @@ public class DeviceController {
|
||||
return new Result<Void>();
|
||||
}
|
||||
|
||||
@PutMapping("/enableOta/{id}/{status}")
|
||||
@Operation(summary = "启用/关闭OTA自动升级")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<Void> enableOtaUpgrade(@PathVariable String id, @PathVariable Integer status) {
|
||||
DeviceEntity entity = deviceService.selectById(id);
|
||||
if (entity == null) {
|
||||
return new Result<Void>().error("设备不存在");
|
||||
}
|
||||
entity.setAutoUpdate(status);
|
||||
deviceService.updateById(entity);
|
||||
return new Result<Void>();
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,14 @@ package xiaozhi.modules.device.service;
|
||||
import java.util.List;
|
||||
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.BaseService;
|
||||
import xiaozhi.modules.device.dto.DevicePageUserDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceReportReqDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceReportRespDTO;
|
||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||
import xiaozhi.modules.device.vo.UserShowDeviceListVO;
|
||||
|
||||
public interface DeviceService {
|
||||
|
||||
/**
|
||||
* 根据Mac地址获取设备信息
|
||||
*/
|
||||
DeviceEntity getDeviceById(String macAddress);
|
||||
public interface DeviceService extends BaseService<DeviceEntity> {
|
||||
|
||||
/**
|
||||
* 检查设备是否激活
|
||||
|
||||
+23
-20
@@ -13,7 +13,6 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
@@ -55,13 +54,6 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
||||
private final RedisUtils redisUtils;
|
||||
private final OtaService otaService;
|
||||
|
||||
@Override
|
||||
public DeviceEntity getDeviceById(String deviceId) {
|
||||
LambdaQueryWrapper<DeviceEntity> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(DeviceEntity::getId, deviceId);
|
||||
return deviceDao.selectOne(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deviceActivation(String agentId, String activationCode) {
|
||||
if (StringUtils.isBlank(activationCode)) {
|
||||
@@ -105,6 +97,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
||||
deviceEntity.setMacAddress(macAddress);
|
||||
deviceEntity.setUserId(user.getId());
|
||||
deviceEntity.setCreator(user.getId());
|
||||
deviceEntity.setAutoUpdate(1);
|
||||
deviceEntity.setCreateDate(currentTime);
|
||||
deviceEntity.setUpdater(user.getId());
|
||||
deviceEntity.setUpdateDate(currentTime);
|
||||
@@ -123,16 +116,22 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
||||
DeviceReportRespDTO response = new DeviceReportRespDTO();
|
||||
response.setServer_time(buildServerTime());
|
||||
|
||||
String type = deviceReport.getBoard() == null ? null : deviceReport.getBoard().getType();
|
||||
DeviceReportRespDTO.Firmware firmware = buildFirmwareInfo(type, macAddress);
|
||||
response.setFirmware(firmware);
|
||||
DeviceEntity deviceById = getDeviceByMacAddress(macAddress);
|
||||
|
||||
if (deviceById == null || deviceById.getAutoUpdate() != 0) {
|
||||
String type = deviceReport.getBoard() == null ? null : deviceReport.getBoard().getType();
|
||||
DeviceReportRespDTO.Firmware firmware = buildFirmwareInfo(type);
|
||||
response.setFirmware(firmware);
|
||||
} else {
|
||||
response.setFirmware(null);
|
||||
}
|
||||
|
||||
// 添加WebSocket配置
|
||||
DeviceReportRespDTO.Websocket websocket = new DeviceReportRespDTO.Websocket();
|
||||
// 从系统参数获取WebSocket URL,如果未配置则使用默认值
|
||||
String wsUrl = sysParamsService.getValue(Constant.SERVER_WEBSOCKET, true);
|
||||
if (StringUtils.isBlank(wsUrl) || wsUrl.equals("null")) {
|
||||
log.error("WebSocket URL is not configured");
|
||||
log.error("WebSocket地址未配置,请登录智控台,在参数管理找到【server.websocket】配置");
|
||||
wsUrl = "ws://xiaozhi.server.com:8000/xiaozhi/v1/";
|
||||
websocket.setUrl(wsUrl);
|
||||
} else {
|
||||
@@ -141,14 +140,13 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
||||
// 随机选择一个WebSocket URL
|
||||
websocket.setUrl(wsUrls[RandomUtil.randomInt(0, wsUrls.length)]);
|
||||
} else {
|
||||
log.error("WebSocket URL list is empty");
|
||||
log.error("WebSocket地址未配置,请登录智控台,在参数管理找到【server.websocket】配置");
|
||||
websocket.setUrl("ws://xiaozhi.server.com:8000/xiaozhi/v1/");
|
||||
}
|
||||
}
|
||||
|
||||
response.setWebsocket(websocket);
|
||||
|
||||
DeviceEntity deviceById = getDeviceById(macAddress);
|
||||
if (deviceById != null) { // 如果设备存在,则更新上次连接时间
|
||||
deviceById.setLastConnectedAt(new Date());
|
||||
deviceDao.updateById(deviceById);
|
||||
@@ -301,7 +299,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
||||
return code;
|
||||
}
|
||||
|
||||
private DeviceReportRespDTO.Firmware buildFirmwareInfo(String type, String macAddress) {
|
||||
private DeviceReportRespDTO.Firmware buildFirmwareInfo(String type) {
|
||||
if (StringUtils.isBlank(type)) {
|
||||
return null;
|
||||
}
|
||||
@@ -311,14 +309,19 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
||||
String downloadUrl = null;
|
||||
|
||||
if (ota != null) {
|
||||
// 获取当前请求的URL
|
||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
|
||||
.getRequest();
|
||||
String requestUrl = request.getRequestURL().toString();
|
||||
|
||||
String otaUrl = sysParamsService.getValue(Constant.SERVER_OTA, true);
|
||||
if (StringUtils.isBlank(otaUrl) || otaUrl.equals("null")) {
|
||||
log.error("OTA地址未配置,请登录智控台,在参数管理找到【server.ota】配置");
|
||||
// 尝试从请求中获取
|
||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
|
||||
.getRequest();
|
||||
otaUrl = request.getRequestURL().toString();
|
||||
}
|
||||
// 将URL中的/ota/替换为/otaMag/download/
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
redisUtils.set(RedisKeys.getOtaIdKey(uuid), ota.getId());
|
||||
downloadUrl = requestUrl.replace("/ota/", "/otaMag/download/") + uuid;
|
||||
downloadUrl = otaUrl.replace("/ota/", "/otaMag/download/") + uuid;
|
||||
}
|
||||
|
||||
firmware.setVersion(ota == null ? null : ota.getVersion());
|
||||
|
||||
+52
@@ -4,6 +4,8 @@ import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -12,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -47,6 +50,7 @@ import xiaozhi.modules.sys.utils.WebSocketValidator;
|
||||
public class SysParamsController {
|
||||
private final SysParamsService sysParamsService;
|
||||
private final ConfigService configService;
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
@GetMapping("page")
|
||||
@Operation(summary = "分页")
|
||||
@@ -97,6 +101,9 @@ public class SysParamsController {
|
||||
// 验证WebSocket地址列表
|
||||
validateWebSocketUrls(dto.getParamCode(), dto.getParamValue());
|
||||
|
||||
// 验证OTA地址
|
||||
validateOtaUrl(dto.getParamCode(), dto.getParamValue());
|
||||
|
||||
sysParamsService.update(dto);
|
||||
configService.getConfig(false);
|
||||
return new Result<Void>();
|
||||
@@ -118,6 +125,11 @@ public class SysParamsController {
|
||||
}
|
||||
for (String url : wsUrls) {
|
||||
if (StringUtils.isNotBlank(url)) {
|
||||
// 检查是否包含localhost或127.0.0.1
|
||||
if (url.contains("localhost") || url.contains("127.0.0.1")) {
|
||||
throw new RenException("WebSocket地址不能使用localhost或127.0.0.1");
|
||||
}
|
||||
|
||||
// 验证WebSocket地址格式
|
||||
if (!WebSocketValidator.validateUrlFormat(url)) {
|
||||
throw new RenException("WebSocket地址格式不正确: " + url);
|
||||
@@ -131,6 +143,46 @@ public class SysParamsController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证OTA地址
|
||||
*/
|
||||
private void validateOtaUrl(String paramCode, String url) {
|
||||
if (!paramCode.equals(Constant.SERVER_OTA)) {
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isBlank(url) || url.equals("null")) {
|
||||
throw new RenException("OTA地址不能为空");
|
||||
}
|
||||
|
||||
// 检查是否包含localhost或127.0.0.1
|
||||
if (url.contains("localhost") || url.contains("127.0.0.1")) {
|
||||
throw new RenException("OTA地址不能使用localhost或127.0.0.1");
|
||||
}
|
||||
|
||||
// 验证URL格式
|
||||
if (!url.toLowerCase().startsWith("http")) {
|
||||
throw new RenException("OTA地址必须以http或https开头");
|
||||
}
|
||||
if (!url.endsWith("/ota/")) {
|
||||
throw new RenException("OTA地址必须以/ota/结尾");
|
||||
}
|
||||
|
||||
try {
|
||||
// 发送GET请求
|
||||
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
|
||||
if (response.getStatusCode() != HttpStatus.OK) {
|
||||
throw new RenException("OTA接口访问失败,状态码:" + response.getStatusCode());
|
||||
}
|
||||
// 检查响应内容是否包含OTA相关信息
|
||||
String body = response.getBody();
|
||||
if (body == null || !body.contains("OTA")) {
|
||||
throw new RenException("OTA接口返回内容格式不正确,可能不是一个真实的OTA接口");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RenException("OTA接口验证失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
@Operation(summary = "删除")
|
||||
@LogOperation("删除")
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
-- 删除server.ip和server.port,改成在xiaozhi-server中配置,方便运行多个websocket
|
||||
delete from `sys_params` where id = 100;
|
||||
delete from `sys_params` where id = 101;
|
||||
|
||||
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (106, 'server.websocket', 'null', 'string', 1, 'websocket地址,多个用;分隔');
|
||||
|
||||
+9
-6
@@ -1,8 +1,9 @@
|
||||
-- 本文件用于初始化固件信息表,无需手动执行,在项目启动时会自动执行
|
||||
-- -------------------------------------------------------
|
||||
-- 初始化固件信息表
|
||||
drop table if exists `ai_ota`;
|
||||
CREATE TABLE `ai_ota` (
|
||||
-- 增加server.ota,用于配置ota地址
|
||||
delete from `sys_params` where id = 107;
|
||||
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (107, 'server.ota', 'null', 'string', 1, 'ota地址');
|
||||
|
||||
-- 增加固件信息表
|
||||
CREATE TABLE IF NOT EXISTS `ai_ota` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`firmware_name` varchar(100) DEFAULT NULL COMMENT '固件名称',
|
||||
`type` varchar(50) DEFAULT NULL COMMENT '固件类型',
|
||||
@@ -16,4 +17,6 @@ CREATE TABLE `ai_ota` (
|
||||
`creator` bigint DEFAULT NULL COMMENT '创建者',
|
||||
`create_date` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='固件信息表';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='固件信息表';
|
||||
|
||||
update ai_device set auto_update = 1;
|
||||
@@ -58,13 +58,6 @@ databaseChangeLog:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202504181536.sql
|
||||
- changeSet:
|
||||
id: 202504180758
|
||||
author: jiangkunyin
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202504180758.sql
|
||||
- changeSet:
|
||||
id: 202504221135
|
||||
author: John
|
||||
@@ -72,13 +65,6 @@ databaseChangeLog:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202504221135.sql
|
||||
- changeSet:
|
||||
id: 202504221646
|
||||
author: John
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202504221646.sql
|
||||
- changeSet:
|
||||
id: 202504221555
|
||||
author: John
|
||||
@@ -86,3 +72,10 @@ databaseChangeLog:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202504221555.sql
|
||||
- changeSet:
|
||||
id: 202504251421
|
||||
author: jiangkunyin
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202504251421.sql
|
||||
@@ -51,4 +51,20 @@ export default {
|
||||
});
|
||||
}).send();
|
||||
},
|
||||
enableOtaUpgrade(id, status, callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/device/enableOta/${id}/${status}`)
|
||||
.method('PUT')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(res)
|
||||
})
|
||||
.fail((err) => {
|
||||
console.error('更新OTA状态失败:', err)
|
||||
this.$message.error(err.msg || '更新OTA状态失败')
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.enableOtaUpgrade(id, status, callback)
|
||||
})
|
||||
}).send()
|
||||
},
|
||||
}
|
||||
@@ -15,15 +15,9 @@
|
||||
<div class="content-panel">
|
||||
<div class="content-area">
|
||||
<el-card class="device-card" shadow="never">
|
||||
<el-table
|
||||
ref="deviceTable"
|
||||
:data="paginatedDeviceList"
|
||||
class="transparent-table"
|
||||
:header-cell-class-name="headerCellClassName"
|
||||
v-loading="loading"
|
||||
element-loading-text="拼命加载中"
|
||||
element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)">
|
||||
<el-table ref="deviceTable" :data="paginatedDeviceList" class="transparent-table"
|
||||
:header-cell-class-name="headerCellClassName" v-loading="loading" element-loading-text="拼命加载中"
|
||||
element-loading-spinner="el-icon-loading" element-loading-background="rgba(255, 255, 255, 0.7)">
|
||||
<el-table-column label="选择" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.selected"></el-checkbox>
|
||||
@@ -34,7 +28,7 @@
|
||||
{{ getFirmwareTypeName(scope.row.model) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="固件版本" prop="firmwareVersion" align="center" ></el-table-column>
|
||||
<el-table-column label="固件版本" prop="firmwareVersion" align="center"></el-table-column>
|
||||
<el-table-column label="Mac地址" prop="macAddress" align="center"></el-table-column>
|
||||
<el-table-column label="绑定时间" prop="bindTime" align="center"></el-table-column>
|
||||
<el-table-column label="最近对话" prop="lastConversation" align="center"></el-table-column>
|
||||
@@ -53,8 +47,8 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="OTA升级" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.otaSwitch" size="mini" active-color="#13ce66"
|
||||
inactive-color="#ff4949"></el-switch>
|
||||
<el-switch v-model="scope.row.otaSwitch" size="mini" active-color="#13ce66" inactive-color="#ff4949"
|
||||
@change="handleOtaSwitchChange(scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
@@ -326,6 +320,16 @@ export default {
|
||||
const firmwareType = FIRMWARE_TYPES.find(item => item.key === type);
|
||||
return firmwareType ? firmwareType.name : type;
|
||||
},
|
||||
handleOtaSwitchChange(row) {
|
||||
Api.device.enableOtaUpgrade(row.device_id, row.otaSwitch ? 1 : 0, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success(row.otaSwitch ? '已设置成自动升级' : '已关闭自动升级')
|
||||
} else {
|
||||
row.otaSwitch = !row.otaSwitch
|
||||
this.$message.error(data.msg || '操作失败')
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -472,12 +476,13 @@ export default {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
::v-deep .el-card__body {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
::v-deep .el-card__body {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table_bottom {
|
||||
@@ -691,5 +696,4 @@ export default {
|
||||
::v-deep .el-table::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user