跳转至

设备接入手册

面向盒子固件开发者。固件只需实现一个取数路径与少量辅助接口;本文给出完整调用时序、参数与错误处理建议。

前置依赖(重要):设备首次校时依赖 NTP(TLS 证书校验需要正确时钟,server_time 无法承担首次校时)。固件必须先通过 NTP 同步时间(如 ntp.aliyun.com,MicroPython 内置 ntptime 可配),NTP 不可达时不得发起 HTTPS 请求。


1. 鉴权方式

所有设备接口(激活、公告除外)携带请求头:

X-Device-SN: XMBOX-2026-000001
X-Device-Token: <64位hex,激活时下发>
  • Token 由服务端随机生成,库中仅存哈希;解绑/管理员重置/重复激活都会使旧 Token 立即失效;
  • 收到 40102 时,固件应回退到出厂密钥重新激活(§2)。

2. 接入时序

开机 → NTP 校时 → POST /device/activate(换 Token)
     → 循环:POST /device/heartbeat(每 next_interval 秒)
     → GET  /data/sources(开机 + 每 30 分钟刷新菜单)
     → GET  /data/{source_code}(按 poll_interval 轮询渲染)
     → GET  /media/**(音频类源按 payload.audio_url 下载,见 §3.1)

2.1 激活 POST /api/v1/device/activate

首次联网或 Token 失效(40102)后调用,无需鉴权头。

{
  "sn": "XMBOX-2026-000001",
  "model": "xmbox_v1",
  "fw_version": "1.0.3",
  "activation_key": "<出厂预置型号密钥>"
}

响应 data:

{ "device_token": "9f86d081…", "heartbeat_interval": 600, "server_time": 1788000000 }
  • activation_key(出厂预置型号密钥):平台为该型号配置了密钥时必传且须完全一致;平台未配置型号密钥的部署(开放激活模式)可不传、平台不校验——不确定时随请求携带即可;
  • 错误:40104(SN 不存在或密钥错误 → 提示联系售后)、40301(设备被禁用);
  • 合法重复激活 = Token 轮换,旧 Token 立即失效。

2.2 心跳 POST /api/v1/device/heartbeat

请求体:{"fw_version": "1.0.3"}。响应 data:

{
  "server_time": 1788000000,
  "next_interval": 600,
  "pending_ops": [
    { "type": "bind_confirm", "code": "482913", "expire_at": "2026-08-31 14:25:00" }
  ]
}
  • next_interval 为服务端建议的心跳间隔(默认 600 秒,可配置),固件应按其调度;
  • pending_ops 为待执行指令:当前仅 bind_confirm(用户发起绑定后的 6 位确认码,设备应在屏幕显示并语音/提示用户回填网页);未来可能扩展新 type,固件必须忽略不认识的 type;
  • 离线重连后若错过心跳响应,可调 GET /api/v1/device/pending 兜底拉取相同指令。

2.3 数据源清单 GET /api/v1/data/sources

固件渲染菜单/轮播的唯一数据来源,建议开机及每 30 分钟刷新:

{
  "sources": [
    {
      "source": "lottery_ssq", "version": "v1", "name": "双色球",
      "category": "lottery", "poll_interval": 600,
      "updated_at": "2026-08-30 21:35:00",
      "source_name": "数据来自中国福利彩票官网,仅供信息展示,不构成任何投注建议"
    }
  ]
}

poll_interval 为建议轮询间隔(秒)=min(cache_ttl, 600);固件可自行节流,但不得高于限流约束(默认 10 次/分/设备/源)。

VIP 设备:出厂预导入入库的设备(库内 VIP),清单返回全部已上线数据源(含付费源),取数免订阅;非 VIP 设备仅返回已订阅或免订阅源(2026-09-19)。

3. 统一取数 GET /api/v1/data/{source_code} ★核心

GET /api/v1/data/lottery_ssq?version=v1
X-Device-SN: XMBOX-2026-000001
X-Device-Token: 9f86d0...
参数 类型 必填 说明
version string 否(默认 v1) 数据源版本
<业务参数> query 平铺 按源 schema 如天气 &city=上海;复杂结构用 params=<urlencoded JSON>
format string 否 预留,恒为 json

成功响应(data.stale=false 为新鲜数据):

{
  "code": 0, "msg": "success",
  "data": {
    "source": "lottery_ssq", "version": "v1",
    "updated_at": "2026-08-30 21:35:00", "stale": false,
    "payload": { }
  },
  "server_time": 1788000000
}

固件建议展示“数据更新于 {updated_at}”;各源 payload 字段与独立接入指导见数据源目录。

限流响应:42901,data.retry_after 给出建议等待秒数,按其退避重试。

3.1 媒体文件下载 GET /media/**

音频等二进制产物放不进取数接口的 payload(<4KB 硬约束),由服务端落盘后以静态文件提供。设备端流程(如 news_hourly 整点新闻播报):

  1. 正常取数 GET /api/v1/data/news_hourly,payload 中拿到 audio_url(如 /media/news_audio/latest.wav)、script_url(播报稿全文)、hour_label(档期);
  2. 向同一主机拼接完整地址发起普通 GET 下载(无需鉴权头):
GET https://api.wwzu.com/media/news_audio/latest.wav   → 音频(WAV 24kHz/16bit/单声道 PCM,可直接 I2S 播放,约 8MB)
GET https://api.wwzu.com/media/news_audio/latest.mp3   → 同一播报的 MP3(约 1MB,供网页端/调试,设备端无需使用)
GET https://api.wwzu.com/media/news_audio/latest.txt   → 本档播报稿全文(UTF-8)
  • latest.* 每个整点档滚动覆盖;按档归档 news_YYYYMMDD_HH.wav/mp3/txt 保留最近 24 份,回放历史档期可用;
  • 服务端以临时文件 + 原子替换落盘,任意时刻发起下载都能拿到完整文件;响应带 Content-Length,无 chunked / gzip / 3xx 跳转,固件按 Content-Length 校验完整性即可;
  • 播报档期为每日 9:00~22:00 整点(服务端每档提前 5 分钟生成),设备按整点读取取数接口后下载当档音频播放。

3.2 听老故事连续播放 comic_audio

三级浏览 + 整本连续播放。音频经服务端 /media 站内缓存下发(源站地址与真实文件名均不下发,文件名为页号顺序名;需固件 mp3dec/play_mp3());数据契约与订阅开通(邀请码兑换)见听老故事接入指导:

  1. 取标签:GET /api/v1/data/comic_audio(默认 action=tags),渲染标签菜单(name+count);
  2. 取故事:GET /api/v1/data/comic_audio?action=stories&tag=英雄模范&sort=hits(11 本/页),渲染书目(cover_url 封面 / pages 页数 / title),并记住每本的 htmlname(书目对外标识,翻页/取清单都要用它);
  3. 取清单:GET /api/v1/data/comic_audio?action=playlist&story_id=<htmlname> —— 一次拿到整本播放清单:base_url(站内相对路径)+ files[](从第 1 页正文语音开始,文件名即页号:001.mp3 → 第 1 页,无音页号自然缺号;顺序即播放顺序);
  4. 连续播放(边播边预取,整本不中断):
  5. 下载第 N 条(同主机 + base_url + 文件名,§3.1 同主机规则),起播同时后台预取第 N+1 条(双缓冲);
  6. 当前条播完(ended)无缝切下一条;单条下载失败重试 1 次,仍失败跳过该条继续(不中断整本);个别条目 404 表示服务端仍在备货(长书快进场景),退避 1 秒重试;
  7. more=true 的超长书:余量不足 10 条时提前带 &page=next_page 取下一批清单;
  8. 末条播完即停;用户切换书目/页码时取消旧调度链再重建(避免新旧链条叠加)。

带屏设备可在浏览/播放时另取页图 GET /api/v1/data/comic_audio?action=pages&story_id=<htmlname>(10 张/页,no 对应页码,与语音清单互不干扰)。

固件升级提示(2026-09-19 契约改版):stories 列表不再返回数字 id(改 htmlname,11 本/页);playlist/pages 的 story_id 传 htmlname;清单不再有梗概语音前置条,文件名为页号顺序名(001.mp3…)而非源站真实文件名。旧固件按数字 ID 请求将得到 50401(故事不存在)。

4. 缓存与降级语义

  • 服务端两级缓存 + 全设备共享:多台设备取同一源,命中缓存,updated_at 即数据时间;
  • 上游故障时,若缓存仍在降级窗口内,返回 stale=true 的旧数据(updated_at 不变)——固件应照常渲染并保留“更新于”提示;
  • 降级窗口也过期后返回 50401——固件显示“暂无数据”,保留上次画面,按轮询间隔继续重试。

5. 固件实现建议

  • 所有请求走 HTTPS + 证书校验;请求失败按指数退避,不得高频重试;
  • code 为唯一判断依据,msg 仅用于展示/日志;
  • 心跳/取数均为幂等或可安全重试;绑定确认是一次性动作(确认码一次即焚);
  • Token 只存 RAM,不上报日志;序列号 SN 可在设备 web 页面的【系统】页签中查看,供用户绑定。

6. 错误码总表

本表为对外摘录,与内部《API 接口规范》错误码母版同步维护,新增错误码先改母版再同步本表。

code HTTP 含义 设备端建议表现
0 200 成功 正常渲染
40001 400 参数缺失/校验失败 提示参数错误并告警日志
40101 401 设备未绑定 显示绑定引导(二维码/SN)
40102 401 设备 Token 无效/已吊销 用出厂密钥重新激活
40103 401 用户未登录/JWT 失效(Web) 跳转登录
40104 401 激活失败:SN 不存在或密钥错误 显示联系售后
40301 403 设备被禁用 提示联系管理员
40302 403 未开通该数据源(未订阅) 提示去后台开通
40303 403 无权限(越权/非管理员,Web) 提示无权限
40304 403 设备已被他人绑定 提示联系绑定者解绑
40305 403 绑定设备数超上限(默认 5) 提示先解绑
40306 403 确认码错误或已过期 允许重新获取
40308 403 邀请码无效(Web 兑换) 设备侧不出现
40309 403 该数据源需邀请码开通订阅 提示需邀请码开通(用户在用户中心兑换)
40401 404 数据源或版本不存在 从清单接口刷新后重试
42901 429 请求过于频繁 按 data.retry_after 退避
42902 429 登录失败次数过多 显示剩余锁定分钟
50301 503 数据源已停用/下线/维护 显示占位页
50302 503 数据源版本已下线(日落到期) 升级请求版本
50401 504 上游获取失败且无可用缓存 显示“暂无数据”,保留上次画面
50000 500 服务器内部错误 指数退避重试