|
@@ -4,7 +4,9 @@ import (
|
|
|
"context"
|
|
"context"
|
|
|
"flag"
|
|
"flag"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
|
|
+ "io"
|
|
|
"os"
|
|
"os"
|
|
|
|
|
+ "os/exec"
|
|
|
"os/signal"
|
|
"os/signal"
|
|
|
"sort"
|
|
"sort"
|
|
|
"strconv"
|
|
"strconv"
|
|
@@ -52,7 +54,7 @@ func printUsage() {
|
|
|
fmt.Println("")
|
|
fmt.Println("")
|
|
|
fmt.Println("命令:")
|
|
fmt.Println("命令:")
|
|
|
fmt.Println(" monitor 启动监控")
|
|
fmt.Println(" monitor 启动监控")
|
|
|
- fmt.Println(" config 管理 MQTT 配置")
|
|
|
|
|
|
|
+ fmt.Println(" config 管理 MQTT 和 BLE 配置")
|
|
|
fmt.Println(" serve 启动 API 服务")
|
|
fmt.Println(" serve 启动 API 服务")
|
|
|
fmt.Println(" version 显示版本信息")
|
|
fmt.Println(" version 显示版本信息")
|
|
|
fmt.Println("")
|
|
fmt.Println("")
|
|
@@ -103,11 +105,16 @@ func runServe(args []string) {
|
|
|
|
|
|
|
|
fmt.Println("接口文档:")
|
|
fmt.Println("接口文档:")
|
|
|
fmt.Println(" GET /api/health - 健康检查")
|
|
fmt.Println(" GET /api/health - 健康检查")
|
|
|
- fmt.Println(" GET /api/mqtt - 获取所有配置")
|
|
|
|
|
- fmt.Println(" POST /api/mqtt - 创建配置")
|
|
|
|
|
- fmt.Println(" GET /api/mqtt/:id - 获取单个配置")
|
|
|
|
|
- fmt.Println(" PUT /api/mqtt/:id - 更新配置")
|
|
|
|
|
- fmt.Println(" DELETE /api/mqtt/:id - 删除配置")
|
|
|
|
|
|
|
+ fmt.Println(" GET /api/mqtt - 获取所有 MQTT 配置")
|
|
|
|
|
+ fmt.Println(" POST /api/mqtt - 创建 MQTT 配置")
|
|
|
|
|
+ fmt.Println(" GET /api/mqtt/:id - 获取单个 MQTT 配置")
|
|
|
|
|
+ fmt.Println(" PUT /api/mqtt/:id - 更新 MQTT 配置")
|
|
|
|
|
+ fmt.Println(" DELETE /api/mqtt/:id - 删除 MQTT 配置")
|
|
|
|
|
+ fmt.Println(" GET /api/ble - 获取所有 BLE 配置")
|
|
|
|
|
+ fmt.Println(" POST /api/ble - 创建 BLE 配置")
|
|
|
|
|
+ fmt.Println(" GET /api/ble/:id - 获取单个 BLE 配置")
|
|
|
|
|
+ fmt.Println(" PUT /api/ble/:id - 更新 BLE 配置")
|
|
|
|
|
+ fmt.Println(" DELETE /api/ble/:id - 删除 BLE 配置")
|
|
|
|
|
|
|
|
if err := server.Start(); err != nil {
|
|
if err := server.Start(); err != nil {
|
|
|
logger.Error("服务启动失败: %v", err)
|
|
logger.Error("服务启动失败: %v", err)
|
|
@@ -184,6 +191,17 @@ func runMonitor(args []string) {
|
|
|
logger.Info("未配置 MQTT,跳过 MQTT 连接")
|
|
logger.Info("未配置 MQTT,跳过 MQTT 连接")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ var bleStdin io.WriteCloser
|
|
|
|
|
+ bleCfg, err := db.GetBLEConfig()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ logger.Error("读取 BLE 配置失败: %v", err)
|
|
|
|
|
+ fmt.Printf("读取 BLE 配置失败: %v\n", err)
|
|
|
|
|
+ } else if bleCfg != nil {
|
|
|
|
|
+ bleStdin = startBLERelay(bleCfg, ctx)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ logger.Info("未配置 BLE,跳过 BLE 中继")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
var apiServer *api.Server
|
|
var apiServer *api.Server
|
|
|
if *apiAddr != "" {
|
|
if *apiAddr != "" {
|
|
|
apiServer = api.New(db, *apiAddr)
|
|
apiServer = api.New(db, *apiAddr)
|
|
@@ -209,7 +227,7 @@ func runMonitor(args []string) {
|
|
|
}()
|
|
}()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- callback := createCallback(mqttClient, apiServer)
|
|
|
|
|
|
|
+ callback := createCallback(mqttClient, apiServer, bleStdin)
|
|
|
|
|
|
|
|
if *portsFlag != "" {
|
|
if *portsFlag != "" {
|
|
|
runFixedMode(ctx, *host, *portsFlag, callback, sigChan)
|
|
runFixedMode(ctx, *host, *portsFlag, callback, sigChan)
|
|
@@ -352,6 +370,9 @@ func runConfig(args []string) {
|
|
|
logger.Info("MQTT 配置已删除: id=%d", id)
|
|
logger.Info("MQTT 配置已删除: id=%d", id)
|
|
|
fmt.Println("配置已删除")
|
|
fmt.Println("配置已删除")
|
|
|
|
|
|
|
|
|
|
+ case "ble":
|
|
|
|
|
+ runBleConfig(db, args[1:])
|
|
|
|
|
+
|
|
|
default:
|
|
default:
|
|
|
printConfigUsage()
|
|
printConfigUsage()
|
|
|
}
|
|
}
|
|
@@ -361,23 +382,158 @@ func printConfigUsage() {
|
|
|
fmt.Println("用法: opencode-monitor config <子命令> [选项]")
|
|
fmt.Println("用法: opencode-monitor config <子命令> [选项]")
|
|
|
fmt.Println("")
|
|
fmt.Println("")
|
|
|
fmt.Println("子命令:")
|
|
fmt.Println("子命令:")
|
|
|
- fmt.Println(" list 列出所有配置")
|
|
|
|
|
|
|
+ fmt.Println(" list 列出所有 MQTT 配置")
|
|
|
fmt.Println(" set 设置 MQTT 配置")
|
|
fmt.Println(" set 设置 MQTT 配置")
|
|
|
- fmt.Println(" delete <id> 删除配置")
|
|
|
|
|
|
|
+ fmt.Println(" delete <id> 删除 MQTT 配置")
|
|
|
|
|
+ fmt.Println(" ble list 列出所有 BLE 配置")
|
|
|
|
|
+ fmt.Println(" ble set 设置 BLE 配置")
|
|
|
|
|
+ fmt.Println(" ble delete <id> 删除 BLE 配置")
|
|
|
fmt.Println("")
|
|
fmt.Println("")
|
|
|
- fmt.Println("选项:")
|
|
|
|
|
|
|
+ fmt.Println("MQTT 选项:")
|
|
|
fmt.Println(" --broker MQTT Broker 地址")
|
|
fmt.Println(" --broker MQTT Broker 地址")
|
|
|
fmt.Println(" --client-id MQTT 客户端 ID")
|
|
fmt.Println(" --client-id MQTT 客户端 ID")
|
|
|
fmt.Println(" --username MQTT 用户名")
|
|
fmt.Println(" --username MQTT 用户名")
|
|
|
fmt.Println(" --password MQTT 密码")
|
|
fmt.Println(" --password MQTT 密码")
|
|
|
fmt.Println(" --topic MQTT 主题")
|
|
fmt.Println(" --topic MQTT 主题")
|
|
|
fmt.Println(" --enabled 是否启用 (true/false)")
|
|
fmt.Println(" --enabled 是否启用 (true/false)")
|
|
|
|
|
+ fmt.Println("")
|
|
|
|
|
+ fmt.Println("BLE 选项:")
|
|
|
|
|
+ fmt.Println(" --device 蓝牙设备名称 (默认: AI-Light)")
|
|
|
|
|
+ fmt.Println(" --service-uuid BLE 服务 UUID")
|
|
|
|
|
+ fmt.Println(" --char-uuid BLE 特征 UUID")
|
|
|
|
|
+ fmt.Println(" --enabled 是否启用 (true/false)")
|
|
|
|
|
+ fmt.Println("")
|
|
|
|
|
+ fmt.Println("全局选项:")
|
|
|
fmt.Println(" --db 数据库路径")
|
|
fmt.Println(" --db 数据库路径")
|
|
|
fmt.Println(" --log-file 日志文件路径(默认 ./logs/monitor.log)")
|
|
fmt.Println(" --log-file 日志文件路径(默认 ./logs/monitor.log)")
|
|
|
fmt.Println(" --log-level 日志级别 (debug/info/warn/error)")
|
|
fmt.Println(" --log-level 日志级别 (debug/info/warn/error)")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func createCallback(mqttClient *mqttcli.Client, apiServer *api.Server) monitor.EventCallback {
|
|
|
|
|
|
|
+func startBLERelay(bleCfg *database.BLEConfig, ctx context.Context) io.WriteCloser {
|
|
|
|
|
+ args := []string{
|
|
|
|
|
+ "scripts/ble_relay.py",
|
|
|
|
|
+ "--device", bleCfg.DeviceName,
|
|
|
|
|
+ "--service-uuid", bleCfg.ServiceUUID,
|
|
|
|
|
+ "--char-uuid", bleCfg.CharUUID,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ cmd := exec.CommandContext(ctx, "python", args...)
|
|
|
|
|
+ cmd.Stdout = os.Stdout
|
|
|
|
|
+ cmd.Stderr = os.Stderr
|
|
|
|
|
+
|
|
|
|
|
+ stdin, err := cmd.StdinPipe()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ logger.Error("创建 BLE 中继 stdin 管道失败: %v", err)
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := cmd.Start(); err != nil {
|
|
|
|
|
+ logger.Error("启动 BLE 中继失败: %v", err)
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ logger.Info("BLE 中继已启动: %s (PID: %d)", bleCfg.DeviceName, cmd.Process.Pid)
|
|
|
|
|
+ fmt.Printf("BLE 中继已启动: %s (PID: %d)\n", bleCfg.DeviceName, cmd.Process.Pid)
|
|
|
|
|
+
|
|
|
|
|
+ go func() {
|
|
|
|
|
+ if err := cmd.Wait(); err != nil {
|
|
|
|
|
+ logger.Error("BLE 中继退出: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }()
|
|
|
|
|
+
|
|
|
|
|
+ return stdin
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func runBleConfig(db *database.DB, args []string) {
|
|
|
|
|
+ if len(args) < 1 {
|
|
|
|
|
+ printBleConfigUsage()
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch args[0] {
|
|
|
|
|
+ case "list":
|
|
|
|
|
+ configs, err := db.ListBLEConfigs()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ logger.Error("查询 BLE 配置失败: %v", err)
|
|
|
|
|
+ fmt.Printf("查询失败: %v\n", err)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if len(configs) == 0 {
|
|
|
|
|
+ fmt.Println("未配置 BLE")
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ logger.Info("查询到 %d 条 BLE 配置", len(configs))
|
|
|
|
|
+ for _, cfg := range configs {
|
|
|
|
|
+ status := "禁用"
|
|
|
|
|
+ if cfg.Enabled {
|
|
|
|
|
+ status = "启用"
|
|
|
|
|
+ }
|
|
|
|
|
+ fmt.Printf("[%d] %s | %s | %s | %s\n", cfg.ID, cfg.DeviceName, cfg.ServiceUUID, cfg.CharUUID, status)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ case "set":
|
|
|
|
|
+ fs := flag.NewFlagSet("config ble set", flag.ExitOnError)
|
|
|
|
|
+ deviceName := fs.String("device", "AI-Light", "蓝牙设备名称")
|
|
|
|
|
+ serviceUUID := fs.String("service-uuid", "b8b7e001-7a6b-4f4f-9a8b-11c0ffee0001", "BLE 服务 UUID")
|
|
|
|
|
+ charUUID := fs.String("char-uuid", "b8b7e002-7a6b-4f4f-9a8b-11c0ffee0001", "BLE 特征 UUID")
|
|
|
|
|
+ enabled := fs.Bool("enabled", true, "是否启用")
|
|
|
|
|
+ fs.Parse(args[1:])
|
|
|
|
|
+
|
|
|
|
|
+ cfg := &database.BLEConfig{
|
|
|
|
|
+ DeviceName: *deviceName,
|
|
|
|
|
+ ServiceUUID: *serviceUUID,
|
|
|
|
|
+ CharUUID: *charUUID,
|
|
|
|
|
+ Enabled: *enabled,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := db.SaveBLEConfig(cfg); err != nil {
|
|
|
|
|
+ logger.Error("保存 BLE 配置失败: %v", err)
|
|
|
|
|
+ fmt.Printf("保存失败: %v\n", err)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ logger.Info("BLE 配置已保存: %s", cfg.DeviceName)
|
|
|
|
|
+ fmt.Println("配置已保存")
|
|
|
|
|
+
|
|
|
|
|
+ case "delete":
|
|
|
|
|
+ if len(args) < 2 {
|
|
|
|
|
+ fmt.Println("必须指定配置 ID")
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ id, err := strconv.Atoi(args[1])
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ logger.Warn("无效的配置 ID: %s", args[1])
|
|
|
|
|
+ fmt.Println("无效的 ID")
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := db.DeleteBLEConfig(id); err != nil {
|
|
|
|
|
+ logger.Error("删除 BLE 配置失败: id=%d, %v", id, err)
|
|
|
|
|
+ fmt.Printf("删除失败: %v\n", err)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ logger.Info("BLE 配置已删除: id=%d", id)
|
|
|
|
|
+ fmt.Println("配置已删除")
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
|
|
+ printBleConfigUsage()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func printBleConfigUsage() {
|
|
|
|
|
+ fmt.Println("用法: opencode-monitor config ble <子命令> [选项]")
|
|
|
|
|
+ fmt.Println("")
|
|
|
|
|
+ fmt.Println("子命令:")
|
|
|
|
|
+ fmt.Println(" list 列出所有 BLE 配置")
|
|
|
|
|
+ fmt.Println(" set 设置 BLE 配置")
|
|
|
|
|
+ fmt.Println(" delete <id> 删除 BLE 配置")
|
|
|
|
|
+ fmt.Println("")
|
|
|
|
|
+ fmt.Println("选项:")
|
|
|
|
|
+ fmt.Println(" --device 蓝牙设备名称 (默认: AI-Light)")
|
|
|
|
|
+ fmt.Println(" --service-uuid BLE 服务 UUID")
|
|
|
|
|
+ fmt.Println(" --char-uuid BLE 特征 UUID")
|
|
|
|
|
+ fmt.Println(" --enabled 是否启用 (true/false)")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func createCallback(mqttClient *mqttcli.Client, apiServer *api.Server, bleStdin io.Writer) monitor.EventCallback {
|
|
|
lastStatus := make(map[int]string)
|
|
lastStatus := make(map[int]string)
|
|
|
var mu sync.Mutex
|
|
var mu sync.Mutex
|
|
|
|
|
|
|
@@ -397,6 +553,12 @@ func createCallback(mqttClient *mqttcli.Client, apiServer *api.Server) monitor.E
|
|
|
if apiServer != nil {
|
|
if apiServer != nil {
|
|
|
apiServer.BroadcastStatus(port, status, code)
|
|
apiServer.BroadcastStatus(port, status, code)
|
|
|
}
|
|
}
|
|
|
|
|
+ if bleStdin != nil {
|
|
|
|
|
+ msg := fmt.Sprintf(`{"port":%d,"code":"%s"}`+"\n", port, code)
|
|
|
|
|
+ if _, err := bleStdin.Write([]byte(msg)); err != nil {
|
|
|
|
|
+ logger.Error("BLE 发送失败: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return func(port int, evt *event.SSEEvent) {
|
|
return func(port int, evt *event.SSEEvent) {
|
|
@@ -405,7 +567,7 @@ func createCallback(mqttClient *mqttcli.Client, apiServer *api.Server) monitor.E
|
|
|
fmt.Println(msg)
|
|
fmt.Println(msg)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if mqttClient == nil && apiServer == nil {
|
|
|
|
|
|
|
+ if mqttClient == nil && apiServer == nil && bleStdin == nil {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
|