Kinio 回報問題 登入

應用端 API

讀完你會知道:登入拿到哪兩種 token、資料在哪個環境、常用的 API 有哪些。

App、網頁或第三方服務都走這套。平台只認使用者的 token,設備的事情由平台代為處理。

登入在一個地方,資料在各自的環境

帳號與登入統一在 identity 服務。登入成功後拿到兩個 token:

  • access token:短期(1 小時),打 API 用。
  • refresh token:30 天,換新的 access token;每換一次舊的就失效(rotation)。
POST https://<identity 主機名>/v1/auth/login
{ "client_id": "<你的 client id>", "email": "...", "password": "..." }

client_id 決定帳號池與品牌。同一個帳號池的帳號用哪個品牌的 App 都直接登入,不會因為第一次用這個品牌而被擋,也沒有「加入品牌」這一步(舊的 join_brand / JOIN_BRAND_REQUIRED 已經拿掉,送了也沒作用)。

品牌條款

登入、註冊、refresh 的回應與 GET /v1/me 都帶 memberships 與 tos_required:

"tos_required": { "brand": "<品牌>", "current_tos_version": "2026-09", "url": "https://…", "accepted_tos_version": "2025-12" }
  • null:這個品牌的現行條款已經同意過(或品牌沒設條款),照常使用。
  • 不是 null:使用者還沒同意這個品牌的現行條款(第一次用這個品牌,或條款改版了;accepted_tos_version 是之前同意的舊版,沒有就是從沒同意過)。顯示 url 的條款,使用者同意後:
POST https://<identity 主機名>/v1/me/brands/<品牌>/accept-terms
Authorization: Bearer <access token>
{ "tos_version": "<畫面上顯示的 current_tos_version>" }

成功回 200 {membership, memberships, tos_required: null}。同一版重送不會改同意時間。

  • 409 TOS_VERSION_STALE:你顯示的那一版已經不是現行版(剛好改版)。回應的 detail 帶新的 current_tos_version 與 url,用新版重新顯示、再請使用者同意。
  • 403 BRAND_MISMATCH:只能同意發這顆 token 的 App 自己品牌的條款。
  • 註冊或登入前已經讓使用者看過條款的話,請求帶 tos_version 就直接記同意,回應的 tos_required 會是 null。

tos_required 不擋任何 API;要不要在同意前限制使用由 App 決定(本 portal 是先顯示條款頁,不同意就登出)。

家戶就是環境

GET https://<identity 主機名>/v1/me

回應包含使用者的家戶清單,每個家戶帶著它所屬的環境與端點集。你要操作某個家戶的設備,就打那個家戶端點集裡的 API,不是固定一個網址。

切家戶等於切環境,不用重新登入。

常用端點

家戶與設備(打家戶所屬環境的 api):

端點用途
POST /v1/homes建家戶
GET /v1/homes / GET /v1/homes/{id}家戶清單與細節
POST /v1/homes/{id}/members邀請成員
GET /v1/homes/{id}/things/{device}讀 Thing 與 shadow
PUT /v1/homes/{id}/things/{device}/shadow/desired寫 desired(控制設備)
DELETE /v1/homes/{id}/things/{device}解除綁定
POST /v1/homes/{id}/things/claim接管既有設備(舊平台設備用 DID,其他品牌原廠設備用 MAC)

所有 thing 操作都會先驗你是不是這個家戶的成員。

控制設備 = 寫 desired

控制不是「呼叫設備」,是寫下你要的狀態:

PUT /v1/homes/{home}/things/{device}/shadow/desired
{ "kinio_motion": { "enabled": false } }

平台把差異推給設備,設備收斂後回報 reported。UI 要顯示「收斂中」而不是假裝馬上成功 —— 設備可能離線,等它上線才會套用。

配對一台新設備

POST /v1/pairing/sessions      { "home_id": "...", "device_ref": "...", "method": "ble|ap|qr" }
→ { "binding_id": "bind-…", "nonce": "…", "expires_at": "…" }

nonce 只回這一次。App 透過 BLE、SoftAP 或 QR 把 binding_id 與 nonce 交給設備;設備用 AID 私鑰簽 sha256(binding_id + "." + nonce),把憑證與簽章交回 App:

POST /v1/pairing/sessions/{id}/redeem   { "cert_pem": "...", "signature": "..." }

平台驗憑證鏈、驗 CN 等於該 AID、驗這是目前有效的憑證、驗簽章,然後把設備綁進家戶並回傳要派發給設備的端點集。

這代表只有真的握有私鑰的設備能完成配對,App 沒辦法憑空把一台設備綁走。

即時影像

影像走 WebRTC。App 打平台的 signaling(WSS),平台負責把 offer/answer 傳給設備,並依當下負載派中繼節點。你不需要自己管 TURN 憑證。

錯誤

錯誤一律回 {"error": "<UPPER_SNAKE>", "message": "…", "detail": {…}}:程式只比對 error;message 一定有,可以直接給人看;機器可讀的補充放 detail(沒有就不帶),例如 403 REAUTH_REQUIRED 的 detail.max_age_seconds。遇到 401 先試 refresh;403 代表權限不足,重試沒用。限流回 429,照回應的建議時間退避。

列表回 {"<資源複數名>": [...], "next_cursor": "…"},下一頁帶 ?cursor=;沒有下一頁時不帶 next_cursor。