智能手表接入指南

概述

MBE 为智能手表/手环设备提供轻量级 HTTP REST API,支持:

  • 语音问答(超简洁回复,适合小屏幕)
  • 健康数据上报(心率、步数、睡眠、血氧)
  • 快捷操作(定时器、闹钟、运动追踪)
  • 通知推送

API 端点

端点 方法 描述
/api/watch/query POST 语音问答
/api/watch/health POST 健康数据上报
/api/watch/quick POST 快捷操作
/api/watch/ping GET 健康检查
/api/watch/debug GET 调试页面

服务地址

生产环境: https://mbe.hi-maker.com
调试页面: https://mbe.hi-maker.com/api/watch/debug

1. 语音问答接口

请求

POST /api/watch/query
Content-Type: application/json

{
  "q": "今天天气怎么样",
  "wid": "watch_001",
  "uid": "user_123",
  "hr": 72,
  "steps": 5234,
  "battery": 80
}
字段 类型 必填 说明
q string 语音指令/问题
wid string 手表ID
uid string 用户ID
hr int 当前心率
steps int 今日步数
battery int 电池电量 %

响应

{
  "ok": true,
  "a": "今天多云,气温18-25度,适合户外运动",
  "display": "多云 18-25度",
  "tts": "今天多云,气温18到25度",
  "action": null
}
字段 说明
ok 是否成功
a 完整回答(最多100字符)
display 显示文字(最多40字符,适合小屏幕)
tts 语音播报文本(最多60字符)
action 快捷操作(null表示纯问答)

快捷操作响应

识别为快捷操作时返回动作指令:

{
  "ok": true,
  "a": "已设定5分钟",
  "display": "已设定5分钟",
  "tts": "好的,5分钟后提醒您",
  "action": "timer",
  "params": {"seconds": 300}
}

支持的快捷操作

语音指令 action params 说明
定时5分钟 timer {seconds: 300} 定时器
设闹钟7点30 alarm {hour: 7, minute: 30} 闹钟
测心率 measure_hr {} 测量心率
测血氧 measure_spo2 {} 测量血氧
开始跑步 start_workout {type: "running"} 开始运动
结束运动 stop_workout {} 结束运动
找手机 find_phone {} 呼叫手机
开启勿扰 dnd_on {} 勿扰模式
关闭勿扰 dnd_off {} 关闭勿扰
屏幕常亮 screen_on {} 保持亮屏

2. 健康数据上报

请求

POST /api/watch/health
Content-Type: application/json

{
  "wid": "watch_001",
  "uid": "user_123",
  "ts": 1706198400000,
  
  "hr": 72,
  "hr_min": 55,
  "hr_max": 120,
  "hr_resting": 62,
  
  "steps": 8234,
  "distance": 5200,
  "calories": 320,
  "floors": 5,
  "active_minutes": 45,
  
  "sleep_duration": 420,
  "sleep_deep": 90,
  "sleep_light": 240,
  "sleep_rem": 90,
  "sleep_score": 85,
  
  "spo2": 98,
  "stress": 35,
  
  "battery": 80
}
字段 类型 说明
wid string 手表ID
ts int 时间戳 (毫秒)
hr int 当前心率 (次/分)
hr_resting int 静息心率
steps int 今日步数
distance float 距离 (米)
calories int 消耗卡路里
sleep_duration int 睡眠时长 (分钟)
sleep_score int 睡眠评分 (0-100)
spo2 int 血氧饱和度 %
stress int 压力指数 (0-100)
battery int 电池电量 %

响应

{
  "ok": true,
  "goals": {
    "steps": {
      "current": 8234,
      "target": 8000,
      "progress": 100
    }
  },
  "tips": "太棒了!今日步数目标已达成"
}

可能的告警响应

{
  "ok": true,
  "alert": {
    "type": "high_hr",
    "message": "心率偏高(110),请注意休息"
  }
}

3. 快捷操作接口

请求

POST /api/watch/quick
Content-Type: application/json

{
  "wid": "watch_001",
  "action": "timer",
  "params": {"seconds": 300}
}

支持的操作

action params 说明
timer {seconds} 定时器
alarm {hour, minute} 闹钟
find_phone {} 找手机
dnd_on {} 开启勿扰
dnd_off {} 关闭勿扰
start_workout {type} 开始运动
stop_workout {} 结束运动
measure_hr {} 测心率
measure_spo2 {} 测血氧
screen_on {} 屏幕常亮

响应

{
  "ok": true,
  "action": "timer",
  "display": "定时器已设置",
  "tts": "定时器已设置",
  "params": {"seconds": 300}
}

集成代码示例

Android Wear (Kotlin)

import okhttp3.*
import org.json.JSONObject

class MBEWatchClient(private val baseUrl: String = "https://mbe.hi-maker.com") {
    private val client = OkHttpClient.Builder()
        .connectTimeout(3, java.util.concurrent.TimeUnit.SECONDS)
        .readTimeout(3, java.util.concurrent.TimeUnit.SECONDS)
        .build()
    
    fun query(
        question: String,
        watchId: String,
        heartRate: Int? = null,
        steps: Int? = null,
        callback: (Result<WatchResponse>) -> Unit
    ) {
        val json = JSONObject().apply {
            put("q", question)
            put("wid", watchId)
            heartRate?.let { put("hr", it) }
            steps?.let { put("steps", it) }
        }
        
        val request = Request.Builder()
            .url("$baseUrl/api/watch/query")
            .post(RequestBody.create(
                MediaType.parse("application/json"),
                json.toString()
            ))
            .build()
        
        client.newCall(request).enqueue(object : Callback {
            override fun onResponse(call: Call, response: Response) {
                val body = response.body()?.string() ?: ""
                val result = JSONObject(body)
                callback(Result.success(WatchResponse(
                    ok = result.getBoolean("ok"),
                    display = result.optString("display", ""),
                    tts = result.optString("tts", ""),
                    action = result.optString("action", null)
                )))
            }
            override fun onFailure(call: Call, e: IOException) {
                callback(Result.failure(e))
            }
        })
    }
    
    fun reportHealth(watchId: String, hr: Int?, steps: Int?, spo2: Int?) {
        val json = JSONObject().apply {
            put("wid", watchId)
            put("ts", System.currentTimeMillis())
            hr?.let { put("hr", it) }
            steps?.let { put("steps", it) }
            spo2?.let { put("spo2", it) }
        }
        
        val request = Request.Builder()
            .url("$baseUrl/api/watch/health")
            .post(RequestBody.create(
                MediaType.parse("application/json"),
                json.toString()
            ))
            .build()
        
        client.newCall(request).enqueue(object : Callback {
            override fun onResponse(call: Call, response: Response) {}
            override fun onFailure(call: Call, e: IOException) {}
        })
    }
}

data class WatchResponse(
    val ok: Boolean,
    val display: String,
    val tts: String,
    val action: String?
)

WatchOS (Swift)

import Foundation

class MBEWatchClient {
    let baseURL: String
    
    init(baseURL: String = "https://mbe.hi-maker.com") {
        self.baseURL = baseURL
    }
    
    func query(
        question: String,
        watchId: String,
        heartRate: Int? = nil,
        steps: Int? = nil,
        completion: @escaping (Result<WatchResponse, Error>) -> Void
    ) {
        guard let url = URL(string: "\(baseURL)/api/watch/query") else {
            completion(.failure(URLError(.badURL)))
            return
        }
        
        var body: [String: Any] = [
            "q": question,
            "wid": watchId
        ]
        if let hr = heartRate { body["hr"] = hr }
        if let s = steps { body["steps"] = s }
        
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try? JSONSerialization.data(withJSONObject: body)
        request.timeoutInterval = 3
        
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let error = error {
                completion(.failure(error))
                return
            }
            
            guard let data = data,
                  let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
                completion(.failure(URLError(.badServerResponse)))
                return
            }
            
            let response = WatchResponse(
                ok: json["ok"] as? Bool ?? false,
                display: json["display"] as? String ?? "",
                tts: json["tts"] as? String ?? "",
                action: json["action"] as? String
            )
            completion(.success(response))
        }.resume()
    }
}

struct WatchResponse {
    let ok: Bool
    let display: String
    let tts: String
    let action: String?
}

// 使用示例
let client = MBEWatchClient()
client.query(question: "定时5分钟", watchId: "my_watch") { result in
    switch result {
    case .success(let response):
        print("Display: \(response.display)")
        if let action = response.action {
            // 执行本地操作
            handleAction(action)
        }
    case .failure(let error):
        print("Error: \(error)")
    }
}

Python (测试)

import requests

class MBEWatchClient:
    def __init__(self, base_url="https://mbe.hi-maker.com"):
        self.base_url = base_url
    
    def query(self, question, watch_id, hr=None, steps=None):
        """语音问答"""
        payload = {"q": question, "wid": watch_id}
        if hr: payload["hr"] = hr
        if steps: payload["steps"] = steps
        
        resp = requests.post(
            f"{self.base_url}/api/watch/query",
            json=payload,
            timeout=3
        )
        return resp.json()
    
    def report_health(self, watch_id, **data):
        """上报健康数据"""
        payload = {"wid": watch_id, **data}
        resp = requests.post(
            f"{self.base_url}/api/watch/health",
            json=payload,
            timeout=3
        )
        return resp.json()
    
    def quick_action(self, watch_id, action, params=None):
        """快捷操作"""
        resp = requests.post(
            f"{self.base_url}/api/watch/quick",
            json={"wid": watch_id, "action": action, "params": params or {}},
            timeout=3
        )
        return resp.json()

# 使用示例
client = MBEWatchClient()

# 语音问答
result = client.query("今天天气怎么样", "my_watch", hr=72, steps=5000)
print(f"显示: {result['display']}")
print(f"TTS: {result['tts']}")

# 上报健康数据
client.report_health("my_watch", hr=75, steps=6000, spo2=98)

# 快捷操作
client.quick_action("my_watch", "timer", {"seconds": 300})

设计要点

1. 屏幕适配

智能手表屏幕小(通常 390x390 或更小),MBE 返回三种文本:

字段 长度 用途
display 40字符 屏幕显示
tts 60字符 语音播报
a 100字符 完整内容

2. 响应时间

场景 超时设置 典型响应
语音问答 2秒 0.2-0.5秒
快捷操作 - <0.05秒
健康上报 3秒 0.02秒

3. 电量优化

  • 使用短连接而非长连接
  • 批量上报健康数据(每5-10分钟一次)
  • 本地缓存常用回复

4. 离线处理

快捷操作(定时器、闹钟、运动等)应在手表本地实现,MBE 只负责语义理解。


架构说明

┌───────────┐                    ┌─────────────┐
│  智能手表  │ ──── HTTP ────→  │   MBE API   │
│  WearOS/  │                    │             │
│  WatchOS  │ ←─── JSON ─────  │  /api/watch │
└───────────┘                    └─────────────┘
      │                                 │
      │  蓝牙                           ▼
      ▼                          ┌─────────────┐
┌───────────┐                    │  专家系统   │
│   手机    │                    │  知识库     │
│   APP     │                    └─────────────┘
└───────────┘

数据流

  1. 手表通过蓝牙连接手机网络
  2. HTTP请求发送到MBE
  3. MBE返回简洁回复 + 操作指令
  4. 操作指令由手表本地执行

常见问题

1. 如何处理网络延迟?

  • 设置严格的超时(2秒)
  • 快捷操作本地优先处理
  • 超时时返回友好提示

2. 如何节省电量?

  • 不使用WebSocket长连接
  • 批量上报数据
  • 本地缓存常用回复

3. 如何适配不同手表?

API 统一,客户端根据屏幕大小选择使用 display(小屏)或 a(大屏)字段。