Răsfoiți Sursa

蓝牙使用py中继方案

Moki 2 săptămâni în urmă
părinte
comite
ddb998f06b
7 a modificat fișierele cu 335 adăugiri și 8 ștergeri
  1. 5 0
      .gitignore
  2. 13 1
      Makefile
  3. 53 5
      README.md
  4. 28 2
      cmd/monitor/main.go
  5. 149 0
      docs/api.md
  6. 50 0
      scripts/build_ble_relay.bat
  7. 37 0
      scripts/build_ble_relay.sh

+ 5 - 0
.gitignore

@@ -38,3 +38,8 @@ logs/
 
 # Frontend build artifact
 internal/web/html/
+
+# BLE relay build artifact (embedded into Go binary)
+cmd/monitor/ble_relay
+cmd/monitor/ble_relay.exe
+data/.venv/

+ 13 - 1
Makefile

@@ -1,4 +1,4 @@
-.PHONY: build clean tidy run serve build-all build-linux build-darwin build-windows
+.PHONY: build clean tidy run serve build-all build-linux build-darwin build-windows build-with-ble
 
 BINARY_NAME=opencode-monitor
 VERSION=$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
@@ -58,3 +58,15 @@ release: build-all
 	@cd dist && zip -q $(BINARY_NAME)-windows-amd64.zip $(BINARY_NAME)-windows-amd64.exe
 	@cd dist && zip -q $(BINARY_NAME)-windows-arm64.zip $(BINARY_NAME)-windows-arm64.exe
 	@echo "Release archives created in dist/"
+
+build-with-ble:
+	@echo "Step 1: Building BLE relay with PyInstaller..."
+	python -m PyInstaller --onefile --name ble_relay --distpath bin --specpath build --workpath build scripts/ble_relay.py
+	@echo "Step 2: Copying BLE relay for embedding..."
+	cp bin/ble_relay cmd/monitor/ble_relay
+	@echo "Step 3: Building Go binary with BLE embedded..."
+	$(GO) mod tidy
+	$(GO) build -tags ble -ldflags "-X main.Version=$(VERSION)" -o bin/$(BINARY_NAME) ./cmd/monitor
+	@echo "Step 4: Cleaning up..."
+	rm -f cmd/monitor/ble_relay
+	@echo "Done: bin/$(BINARY_NAME)"

+ 53 - 5
README.md

@@ -1,13 +1,14 @@
 # OpenCode Monitor
 
-OpenCode 状态监控工具,支持实时监控多个 OpenCode 实例的状态,通过 MQTT 推送状态信息。
+OpenCode 状态监控工具,支持实时监控多个 OpenCode 实例的状态,通过 MQTT 或 BLE 蓝牙推送状态信息。
 
 ## 功能特性
 
 - 🔍 **自动发现** - 自动扫描并发现运行中的 OpenCode 实例
 - 📊 **实时监控** - 通过 SSE 事件流实时获取状态变化
 - 📡 **MQTT 推送** - 支持将状态信息推送到 MQTT Broker
-- 💾 **配置管理** - 使用 SQLite 存储 MQTT 配置
+- 🟢 **BLE 蓝牙推送** - 通过蓝牙将状态推送到 AI-Light 等 BLE 设备
+- 💾 **配置管理** - 使用 SQLite 存储 MQTT 和 BLE 配置
 - 🌐 **HTTP API** - 提供 RESTful API 接口管理配置(详见 [API 文档](docs/api.md))
 - 🔌 **WebSocket** - 支持通过 WebSocket 实时推送状态到网页(详见 [API 文档](docs/api.md))
 - 🖥️ **跨平台** - 支持 Linux、macOS、Windows
@@ -32,9 +33,12 @@ OpenCode 状态监控工具,支持实时监控多个 OpenCode 实例的状态
 git clone <repository-url>
 cd AI-Status-Light
 
-# 编译当前平台
+# 编译当前平台(不含 BLE)
 go build -o bin/opencode-monitor ./cmd/monitor
 
+# 编译当前平台(含 BLE 嵌入)
+make build-with-ble
+
 # 编译所有平台
 make build-all
 ```
@@ -81,6 +85,31 @@ API 接口详情请参阅 [API 文档](docs/api.md)。
 ./opencode-monitor config delete 1
 ```
 
+### BLE 蓝牙配置
+
+BLE 中继已嵌入 Go 二进制(使用 `make build-with-ble` 构建时),无需额外文件或 Python 环境。
+
+```bash
+# 查看 BLE 配置
+./opencode-monitor config ble list
+
+# 设置 BLE 配置(使用默认设备 AI-Light)
+./opencode-monitor config ble set
+
+# 设置 BLE 配置(自定义设备)
+./opencode-monitor config ble set --device MyLight --service-uuid "xxx" --char-uuid "yyy"
+
+# 删除 BLE 配置
+./opencode-monitor config ble delete 1
+```
+
+启动监控时,如果存在启用的 BLE 配置,会自动启动蓝牙中继:
+
+```bash
+# 启动监控(自动根据配置启用 MQTT 和/或 BLE)
+./opencode-monitor monitor --api-addr :8080
+```
+
 ## MQTT 消息格式
 
 消息格式为 JSON:
@@ -152,11 +181,26 @@ API 接口详情请参阅 [API 文档](docs/api.md)。
   --db              数据库路径 (默认 "./data/config.db")
 ```
 
+### config ble set 命令
+
+```bash
+./opencode-monitor config ble set [选项]
+
+选项:
+  --device          蓝牙设备名称 (默认 "AI-Light")
+  --service-uuid    BLE 服务 UUID (默认 "b8b7e001-7a6b-4f4f-9a8b-11c0ffee0001")
+  --char-uuid       BLE 特征 UUID (默认 "b8b7e002-7a6b-4f4f-9a8b-11c0ffee0001")
+  --enabled         是否启用 (true/false, 默认 true)
+```
+
 ## 项目结构
 
 ```
 AI-Status-Light/
-├── cmd/monitor/          # 程序入口
+├── cmd/monitor/
+│   ├── main.go                    # 程序入口
+│   ├── ble_embed.go               # BLE 嵌入声明 (go:build ble)
+│   └── ble_embed_none.go          # BLE 空声明 (go:build !ble)
 ├── internal/
 │   ├── api/              # HTTP API 模块
 │   ├── database/         # SQLite 数据库模块
@@ -164,8 +208,12 @@ AI-Status-Light/
 │   ├── event/            # 事件处理模块
 │   ├── monitor/          # 监控器核心模块
 │   └── mqtt/             # MQTT 客户端模块
+├── scripts/
+│   ├── ble_relay.py              # Python BLE 蓝牙中继脚本
+│   ├── build_ble_relay.bat       # Windows 打包脚本
+│   ├── build_ble_relay.sh        # Linux/macOS 打包脚本
+│   └── requirements.txt          # Python 依赖
 ├── docs/                 # 文档
-├── scripts/              # 构建脚本
 └── Makefile              # 构建配置
 ```
 

+ 28 - 2
cmd/monitor/main.go

@@ -8,6 +8,7 @@ import (
 	"os"
 	"os/exec"
 	"os/signal"
+	"path/filepath"
 	"sort"
 	"strconv"
 	"strings"
@@ -410,14 +411,39 @@ func printConfigUsage() {
 }
 
 func startBLERelay(bleCfg *database.BLEConfig, ctx context.Context) io.WriteCloser {
+	if len(embeddedBLERelay) == 0 {
+		logger.Warn("BLE 中继未嵌入,请使用 make build-with-ble 构建")
+		fmt.Println("警告: BLE 中继未嵌入,已跳过。请使用 make build-with-ble 构建")
+		return nil
+	}
+
+	// 释放嵌入的 exe 到临时目录
+	tmpDir := filepath.Join(os.TempDir(), "ai-status-light")
+	if err := os.MkdirAll(tmpDir, 0755); err != nil {
+		logger.Error("创建临时目录失败: %v", err)
+		return nil
+	}
+
+	var tmpExe string
+	if strings.Contains(strings.ToLower(os.Getenv("OS")), "windows") {
+		tmpExe = filepath.Join(tmpDir, "ble_relay.exe")
+	} else {
+		tmpExe = filepath.Join(tmpDir, "ble_relay")
+	}
+
+	if err := os.WriteFile(tmpExe, embeddedBLERelay, 0755); err != nil {
+		logger.Error("释放 BLE 中继失败: %v", err)
+		return nil
+	}
+	logger.Debug("BLE 中继已释放到: %s", tmpExe)
+
 	args := []string{
-		"scripts/ble_relay.py",
 		"--device", bleCfg.DeviceName,
 		"--service-uuid", bleCfg.ServiceUUID,
 		"--char-uuid", bleCfg.CharUUID,
 	}
 
-	cmd := exec.CommandContext(ctx, "python", args...)
+	cmd := exec.CommandContext(ctx, tmpExe, args...)
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
 

+ 149 - 0
docs/api.md

@@ -178,6 +178,110 @@ DELETE /api/mqtt/:id
 }
 ```
 
+### 7. 获取所有 BLE 配置
+
+```
+GET /api/ble
+```
+
+**响应示例:**
+```json
+{
+  "code": 0,
+  "message": "ok",
+  "data": [
+    {
+      "id": 1,
+      "device_name": "AI-Light",
+      "service_uuid": "b8b7e001-7a6b-4f4f-9a8b-11c0ffee0001",
+      "char_uuid": "b8b7e002-7a6b-4f4f-9a8b-11c0ffee0001",
+      "enabled": true
+    }
+  ]
+}
+```
+
+### 8. 创建 BLE 配置
+
+```
+POST /api/ble
+```
+
+**请求体:**
+```json
+{
+  "device_name": "AI-Light",
+  "service_uuid": "b8b7e001-7a6b-4f4f-9a8b-11c0ffee0001",
+  "char_uuid": "b8b7e002-7a6b-4f4f-9a8b-11c0ffee0001",
+  "enabled": true
+}
+```
+
+| 字段 | 类型 | 必填 | 默认值 | 说明 |
+|------|------|------|--------|------|
+| device_name | string | 否 | AI-Light | BLE 设备名称 |
+| service_uuid | string | 否 | b8b7e001-... | BLE 服务 UUID |
+| char_uuid | string | 否 | b8b7e002-... | BLE 特征 UUID |
+| enabled | boolean | 否 | true | 是否启用 |
+
+**响应示例:**
+```json
+{
+  "code": 0,
+  "message": "创建成功",
+  "data": {
+    "id": 1,
+    "device_name": "AI-Light",
+    "service_uuid": "b8b7e001-7a6b-4f4f-9a8b-11c0ffee0001",
+    "char_uuid": "b8b7e002-7a6b-4f4f-9a8b-11c0ffee0001",
+    "enabled": true
+  }
+}
+```
+
+### 9. 获取单个 BLE 配置
+
+```
+GET /api/ble/:id
+```
+
+**路径参数:**
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| id | integer | 配置 ID |
+
+### 10. 更新 BLE 配置
+
+```
+PUT /api/ble/:id
+```
+
+**路径参数:**
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| id | integer | 配置 ID |
+
+**请求体:**
+```json
+{
+  "device_name": "MyLight",
+  "service_uuid": "xxx",
+  "char_uuid": "yyy",
+  "enabled": false
+}
+```
+
+### 11. 删除 BLE 配置
+
+```
+DELETE /api/ble/:id
+```
+
+**路径参数:**
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| id | integer | 配置 ID |
+
 ## 错误响应
 
 所有错误响应格式:
@@ -265,6 +369,51 @@ API 已启用 CORS,支持跨域请求。
 | 等待权限 | permission | AI 向用户申请权限 |
 | 错误 | error | 会话错误 |
 
+## BLE 蓝牙推送说明
+
+配置 BLE 后,监控到状态变化时会自动通过蓝牙发送到设备。BLE 中继已嵌入 Go 二进制,无需额外文件或 Python 环境。
+
+### 构建含 BLE 的版本
+
+```bash
+# 一键构建(打包 Python 脚本 + 嵌入 Go 二进制)
+make build-with-ble
+
+# 或手动两步
+scripts\build_ble_relay.bat
+go build -tags ble -o bin/opencode-monitor.exe ./cmd/monitor
+```
+
+### Python 依赖(仅开发时需要)
+
+```bash
+pip install -r scripts/requirements.txt
+```
+
+### 状态映射
+
+| Go 状态码 | 蓝牙模式 | 说明 |
+|-----------|---------|------|
+| idle | idle | 空闲 |
+| busy | busy | 工作中 |
+| retry | thinking | 重试中 |
+| pending | thinking | 等待/修改中 |
+| reasoning | thinking | 思考中 |
+| using_tool | ai | 使用工具 |
+| running | ai | 工具运行中 |
+| completed | success | 完成 |
+| session_completed | success | 会话完成 |
+| permission | alarm | 等待权限 |
+| error | error | 错误 |
+
+### Python 依赖(仅 Python 方式需要)
+
+```bash
+pip install -r scripts/requirements.txt
+```
+
+如果使用预编译二进制则不需要安装。
+
 ## WebSocket 实时状态
 
 启动监控服务后,可以通过浏览器访问 `http://localhost:8080` 查看实时状态页面。

+ 50 - 0
scripts/build_ble_relay.bat

@@ -0,0 +1,50 @@
+@echo off
+setlocal
+
+echo === BLE Relay Build Script ===
+echo.
+
+REM Check Python
+where python >nul 2>nul
+if %errorlevel% neq 0 (
+    echo ERROR: Python not found. Please install Python 3 and add to PATH.
+    exit /b 1
+)
+
+REM Install dependencies
+echo Installing dependencies...
+python -m pip install pyinstaller bleak
+if %errorlevel% neq 0 (
+    echo ERROR: Failed to install dependencies.
+    exit /b 1
+)
+
+REM Create output dir
+if not exist bin mkdir bin
+
+REM Build
+echo.
+echo Building ble_relay.exe...
+python -m PyInstaller --onefile --name ble_relay --distpath bin --specpath build --workpath build scripts\ble_relay.py
+if %errorlevel% neq 0 (
+    echo ERROR: PyInstaller build failed.
+    exit /b 1
+)
+
+REM Copy to cmd/monitor for go:embed
+echo.
+echo Copying to cmd/monitor/ for embedding...
+copy /Y bin\ble_relay.exe cmd\monitor\ble_relay.exe >nul
+if %errorlevel% neq 0 (
+    echo ERROR: Failed to copy ble_relay.exe
+    exit /b 1
+)
+
+echo.
+echo === Build complete ===
+echo   bin\ble_relay.exe
+echo   cmd\monitor\ble_relay.exe
+echo.
+echo Now run: go build -tags ble -o bin\opencode-monitor.exe .\cmd\monitor
+
+endlocal

+ 37 - 0
scripts/build_ble_relay.sh

@@ -0,0 +1,37 @@
+#!/bin/bash
+set -e
+
+echo "=== BLE Relay Build Script ==="
+echo ""
+
+# Check Python
+if ! command -v python3 &> /dev/null && ! command -v python &> /dev/null; then
+    echo "ERROR: Python not found. Please install Python 3."
+    exit 1
+fi
+
+PYTHON=$(command -v python3 || command -v python)
+
+# Install dependencies
+echo "Installing dependencies..."
+$PYTHON -m pip install pyinstaller bleak
+
+# Create output directory
+mkdir -p bin
+
+# Build
+echo ""
+echo "Building ble_relay..."
+$PYTHON -m PyInstaller --onefile --name ble_relay --distpath bin --specpath build --workpath build scripts/ble_relay.py
+
+# Copy to cmd/monitor for go:embed
+echo ""
+echo "Copying to cmd/monitor/ for embedding..."
+cp bin/ble_relay cmd/monitor/ble_relay
+
+echo ""
+echo "=== Build complete ==="
+echo "  bin/ble_relay"
+echo "  cmd/monitor/ble_relay"
+echo ""
+echo "Now run: go build -tags ble -o bin/opencode-monitor ./cmd/monitor"