VYSLO DEVELOPER PLATFORM
Back to vyslo.ai

ANDROID SDK · v0.1

Android SDK

No third-party dependencies. One SDK covers the cloud path (authorization, the token store, recordings, insights, transcription and webhook verification) and a direct Bluetooth connection to the card: scan, pair, start and stop recording, card status and events, audio export, live audio, smart wake, voiceprint enrolment, firmware updates, and LED and haptic feedback.

零第三方依賴。同一個 SDK 覆蓋雲通路(授權、令牌存儲、錄音、洞察、轉寫與 webhook 校驗),以及與卡片的藍牙直連: 掃描、配對、起停錄音、卡片狀態與事件、音頻導出、實時音頻、智能喚醒、聲紋註冊、固件升級,以及 LED 與振動反饋。

PlatformStatusNotes
AndroidAVAILABLECloud path and direct Bluetooth to the card. Delivered as source.
iOSAVAILABLEThe same surface in Swift. See iOS SDK.
BLE direct connectAVAILABLEScan, pair, start/stop capture, card events, audio export, Wi-Fi fast transfer.
Live audioAVAILABLEHold the card's button and speak; audio streams into your foreground app.
Device managementAVAILABLESmart wake, card name, voiceprint enrolment, firmware updates, delete and unbind with on-card confirmation.
LED and hapticsAVAILABLELED per card state, vibration per card event, saved on the card.
平台狀態說明
Android可用雲通路與卡片藍牙直連。以源碼交付。
iOS可用同一套能力的 Swift 版,見 iOS SDK。
BLE 直連可用掃描、配對、起停錄音、卡片事件、音頻導出、Wi-Fi 快傳。
實時音頻可用按住卡片按鍵說話,音頻實時流入你的前台 App。
設備管理可用智能喚醒、卡片名稱、聲紋註冊、固件升級,刪除與解綁需在卡上確認。
LED 與振動可用按卡片狀態設置 LED、按卡片事件設置振動,保存在卡片上。

Install

The SDK ships as source under com.vyslo.opensdk, with VysloOpen as the entry point. Copy it into your module, or ask us for the current drop at support@vyslo.ai. The cloud path needs only INTERNET; the Bluetooth features add the permissions listed under BLE direct connection. It uses platform APIs only: HttpURLConnection, org.json, javax.crypto, android.bluetooth and MediaCodec.

SDK 以源碼交付,包名 com.vyslo.opensdk,入口類 VysloOpen。直接拷進你的 module, 或寫信到 support@vyslo.ai 索取當前版本。雲通路只需要 INTERNET 權限;藍牙功能另需BLE 直連一節列出的權限。只用平台自帶 API: HttpURLConnection、org.json、javax.crypto、 android.bluetooth 與 MediaCodec。

kotlin
import com.vyslo.opensdk.VysloOpen

// appId comes from POST /open/apps — see the Quickstart.
val vyslo = VysloOpen(context, appId = 42L)

// Optional third argument for a self-hosted or staging instance:
// VysloOpen(context, appId = 42L, baseUrl = "https://your-instance/api/v1/app")

vyslo.isAuthorized     // Boolean — is a token stored?
vyslo.token            // String? — the raw capability token

The token is persisted in the app's private SharedPreferences (vyslo_open_sdk), so it survives restarts. Every network call must run on a worker thread — the SDK does no threading for you.

令牌持久化在應用私有的 SharedPreferences(vyslo_open_sdk)裡,重啟後仍在。 所有網絡調用必須在工作線程執行——SDK 不替你切線程。

Authorization

kotlin
// 1. Send the user to the VYSLO app's consent screen.
try {
    startActivity(vyslo.authorizeIntent(
        scopes = VysloOpen.SCOPE_RECORDINGS_READ or VysloOpen.SCOPE_RECORDINGS_AUDIO,
        callbackUri = "notesbot://vyslo-auth",
    ))
} catch (e: ActivityNotFoundException) {
    // VYSLO app not installed — send them to install it.
}

// 2. In the activity registered for notesbot://vyslo-auth
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    if (vyslo.handleAuthorizationCallback(intent.data)) {
        // Token stored. Carry on.
    } else {
        // The user denied, or the callback carried no token.
    }
}

authorizeIntent builds this; document it if you need to trigger authorization from somewhere the SDK cannot reach, such as a web page or another framework.

authorizeIntent 構造的就是下面這條。如果你要從 SDK 碰不到的地方(網頁、其他框架)發起授權, 就按它拼。

deep link
vyslo://open/authorize
    ?appId=<your app id>
    &scopes=<scope bitmap>
    &callback=<url-encoded callback uri>

# approved  ->  <callback>?vyslo_token=<capability token>
# denied    ->  <callback>?vyslo_error=denied
THE CALLBACK MUST BE REGISTERED

callback has to match an entry in your app's callback allowlist — exactly, or with ?/# and your own parameters appended, so notesbot://vyslo-auth covers notesbot://vyslo-auth?state=abc. The server checks this before issuing, so nobody can borrow your appId and have the token delivered somewhere else. Register entries with callbackUris at creation or POST /open/apps/{appId}/callbacks later.

回跳地址必須先登記

callback 必須命中你應用的回跳白名單——完全相等,或在登記項後追加 ?/# 及你自己的參數,所以登記 notesbot://vyslo-auth 可命中 notesbot://vyslo-auth?state=abc。服務端在簽發前校驗,別人無法冒用你的 appId 把令牌送到別處。建立應用時用 callbackUris 登記,或之後調 POST /open/apps/{appId}/callbacks 更新。

Reading data

kotlin · worker thread
val rec: VysloRecording = vyslo.fetchRecording(recordingId)

rec.recordingId
rec.title
rec.segments               // List<VysloRecording.Segment>(speaker, text, timestamp, identity)
rec.summaryLines           // List<String>
rec.identity               // VysloIdentity for the recording as a whole
rec.ownerVerified          // shorthand: identity.verdict == OWNER

// Needs SCOPE_RECORDINGS_AUDIO in the grant.
val bytes: ByteArray = vyslo.fetchRecordingAudio(recordingId)

Speaker identity

kotlin
val id: VysloIdentity = rec.segments.first().identity

id.verdict                 // OWNER, LIKELY_OWNER or OTHER
id.source                  // DEVICE, HOSTED_REVIEW or SELF_HOSTED
id.evidence                // VERIFIED, UNVERIFIED or UNAVAILABLE
id.serverId                // which VYSLO server issued this result
SHOW THE SOURCE WITH THE VERDICT

A verdict is only as strong as its source. DEVICE with VERIFIED evidence is a result the card signed for that recording; HOSTED_REVIEW is the VYSLO cloud review; SELF_HOSTED is a claim made by a server someone else runs. Voiceprint templates, embeddings and raw scores are never exposed.

展示判決時一併展示來源

判決的可信度取決於來源。DEVICE 且證據為 VERIFIED,表示由卡片為該錄音簽名的結果; HOSTED_REVIEW 是 VYSLO 雲端複核;SELF_HOSTED 是他人自行部署的服務器所作的聲明。 聲紋模板、embedding 與原始分數永不暴露。

Lists, insights and transcription

kotlin · worker thread
// Needs SCOPE_RECORDINGS_READ. Paged, newest first.
val page = vyslo.listRecordings(from = weekStart, to = now, includesMe = true, cursor = null)
page.items                 // List<VysloRecordingSummary>
page.nextCursor

// Needs SCOPE_RECORDINGS_READ.
val hits = vyslo.searchTranscripts("budget", from = monthStart)

// Needs SCOPE_INSIGHTS_READ.
val week = vyslo.fetchWeeklyInsights()      // talk time, speaking share, topics, to-dos

// Needs SCOPE_DEVICE_STATUS_READ. Cloud snapshot, with observedAt.
val cards = vyslo.listDevices()

// Needs SCOPE_TRANSCRIPTION_CREATE. Any audio file, not only card recordings.
val job = vyslo.transcribe(File(filesDir, "interview.m4a"), summarize = true)

Cloud status from listDevices() is the last snapshot the card reported through a phone, returned with observedAt; for live values connect over BLE.

listDevices() 返回的是卡片最近一次經手機上報的快照,附 observedAt;要實時值請經 BLE 連接。

BLE direct connection

The same VysloOpen instance also talks to the card directly over Bluetooth LE: scan, pair, start and stop recording, read card status, receive card events and pull audio off the card. The VYSLO app does not need to be running. Cloud and BLE share one authorization: the card checks your capability token itself, so the scopes the user approved are enforced on the card.

同一個 VysloOpen 實例也能經藍牙 LE 直接連接卡片:掃描、配對、起停錄音、讀取卡片狀態、 接收卡片事件、從卡上導出音頻。運行時不需要 VYSLO App。雲通路與 BLE 共用同一份授權:卡片自己校驗你的能力令牌, 用戶同意的權限在卡片上就被執行。

Permissions

xml · AndroidManifest.xml
<!-- Android 12 and later -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
                 android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- Android 11 and earlier -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />

<!-- Fast transfer over the card's Wi-Fi hotspot -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

Request BLUETOOTH_SCAN and BLUETOOTH_CONNECT at runtime before the first scan. The SDK does not show permission prompts for you.

首次掃描前,請在運行時申請 BLUETOOTH_SCAN 與 BLUETOOTH_CONNECT。SDK 不會替你彈權限框。

Scan and connect

kotlin
val scan = vyslo.ble.startScan { card: VysloCard ->
    card.name                 // the name the user gave the card in the VYSLO app
    card.address
    card.rssi
}
// … once the user has picked a card
scan.stop()

// Worker thread. Reuses the system bond if this phone is already paired with the card
// (for example through the VYSLO app); otherwise Android shows its pairing prompt.
val session: VysloCardSession = vyslo.ble.connect(card)

// Needs SCOPE_DEVICE_STATUS_READ.
val s = session.status()
s.batteryPercent
s.charging
s.storageUsedBytes
s.firmwareVersion
s.isRecording
s.isListening               // smart wake is listening, nothing is being stored
s.smartWakeEnabled

session.close()               // disconnects and releases the control lease if you hold it

Start and stop recording

kotlin · worker thread
// Needs SCOPE_CAPTURE_CONTROL.
session.acquireControl()      // exclusive control lease; throws LEASE_HELD if another app has it
session.startRecording()
session.pauseRecording()
session.resumeRecording()
session.stopRecording()
session.releaseControl()
ONE CONTROLLER AT A TIME

Several apps can be connected to the same card at once, and all of them can read status and receive events. Only the holder of the control lease can start or stop recording, and that includes the VYSLO app itself. The lease ends when you release it, close the session or lose the link. A recording the user starts with the card's own button is never blocked by a lease.

同一時間只有一個控制方

多個 App 可以同時連接同一張卡,都能讀狀態、收事件;只有持有控制權租約的一方能起停錄音——VYSLO App 自己也不例外。 主動釋放、關閉會話或連接斷開時,租約即結束。用戶用卡片按鍵發起的錄音,永遠不會被租約擋住。

Card events

kotlin
// Needs SCOPE_EVENTS_SUBSCRIBE. Delivered on a background thread.
session.onEvent { e ->
    when (e) {
        is VysloCardEvent.RecordingStarted   -> …
        is VysloCardEvent.RecordingStopped   -> …   // e.recordingId
        is VysloCardEvent.SmartWakeTriggered -> …
        is VysloCardEvent.ListeningChanged   -> …   // e.listening
        is VysloCardEvent.ButtonPressed      -> …
        is VysloCardEvent.ChargingChanged    -> …   // e.charging
    }
}

Events carry no audio and no transcript text.

事件裡不含音頻,也不含轉寫正文。

Pull audio off the card

kotlin · worker thread
// Needs SCOPE_RECORDINGS_READ to list, SCOPE_RECORDINGS_AUDIO to export.
val onCard: List<VysloCardRecording> = session.listRecordings()
// id, startedAt, durationMs, sizeBytes

session.exportAudio(
    recordingId = onCard.first().id,
    format = VysloAudioFormat.WAV,          // OPUS, WAV or PCM
    out = File(filesDir, "recording.wav"),
) { progress -> /* 0.0 – 1.0 */ }
FormatWhat you get
OPUSThe file exactly as the card stores it: Opus, 16 kHz, mono. Smallest and fastest
WAV16-bit PCM in a WAV container, 16 kHz, mono. Decoded on the phone
PCMRaw 16-bit little-endian samples, 16 kHz, mono. Decoded on the phone
格式得到什麼
OPUS卡上存儲的原樣檔案:Opus、16 kHz、單聲道。體積最小、最快
WAVWAV 封裝的 16 位 PCM,16 kHz、單聲道。在手機上解碼
PCM原始 16 位小端採樣,16 kHz、單聲道。在手機上解碼

Hidden and rejected recordings are never listed or exported over BLE, the same rule as the cloud.

隱藏與被拒的錄音在 BLE 上同樣不會被列出、也不能導出,與雲端規則一致。

Fast transfer over Wi-Fi

kotlin · worker thread
// Needs SCOPE_RECORDINGS_AUDIO. For many or long recordings.
session.fastTransfer(
    recordingIds = onCard.map { it.id },
    format = VysloAudioFormat.OPUS,
    outDir = File(filesDir, "vyslo"),
) { done, total -> }

The card turns on its own Wi-Fi hotspot and the phone joins it; Android asks the user once to allow the connection. Bluetooth stays connected for control while the files move, and the card turns the hotspot off when the transfer ends.

卡片打開自己的 Wi-Fi 熱點,手機加入;Android 會請用戶確認一次連接。傳輸期間藍牙保持連接用於控制, 傳完卡片自動關閉熱點。

Live audio

kotlin
// Needs SCOPE_AUDIO_LIVE. Your app must be in the foreground.
val stream = session.startLiveAudio(format = VysloAudioFormat.PCM) { frame: ByteArray ->
    // 16 kHz mono frames while the user holds the card's button
}
stream.onPushToTalk { pressed -> }   // button down / up
stream.stop()

Hold the card's button and speak: audio streams into the app the user is looking at, for dictation, voice input or your own speech pipeline. While the stream is open the button works as push-to-talk instead of starting a recording; nothing is stored on the card, and the stream stops when your app leaves the foreground.

按住卡片按鍵說話,音頻實時流入用戶眼前的 App,可用於聽寫、語音輸入或你自己的語音處理鏈路。串流期間按鍵作為按住說話使用、不會發起錄音, 卡上不存儲任何內容;你的 App 退到後台時串流自動停止。

What the card checks

BLE errors

BLE calls throw VysloBleException; branch on reason:

BLE 調用拋出 VysloBleException,按 reason 分支:

reasonMeaning
BLUETOOTH_OFFBluetooth is off. Ask the user to turn it on
PERMISSION_DENIEDA runtime Bluetooth permission has not been granted
NOT_AUTHORIZEDNo token is stored, or the card refused it: expired, revoked, another user's card, or a scope missing
LEASE_HELDAnother app holds the control lease
CARD_BUSYThe card cannot take the request right now, for example during a firmware update
DISCONNECTEDThe link dropped. Call connect(card) again
NOT_FOREGROUNDLive audio was requested while your app is not in the foreground
CONFIRMATION_TIMEOUTAn operation that needs a press on the card got none within 10 seconds, and was not carried out
reason含義
BLUETOOTH_OFF藍牙未開啟,請用戶打開
PERMISSION_DENIED運行時藍牙權限未授予
NOT_AUTHORIZED沒有本地令牌,或卡片拒絕了它:過期、已撤銷、他人的卡,或缺少權限
LEASE_HELD控制權租約在其他 App 手上
CARD_BUSY卡片暫時無法處理請求,例如正在升級固件
DISCONNECTED連接斷開,重新調用 connect(card)
NOT_FOREGROUND你的 App 不在前台時請求了實時音頻
CONFIRMATION_TIMEOUT需要在卡上按鍵確認的操作,10 秒內沒有收到按鍵,操作未執行

Device management

Everything the VYSLO app does to a card goes through these same calls: settings, voiceprint enrolment, firmware updates and deletion. All of them run over a BLE session and need SCOPE_DEVICE_MANAGE.

VYSLO App 對卡片做的所有操作,走的都是這同一組調用:設置、聲紋註冊、固件升級與刪除。全部經 BLE 會話執行,需要 SCOPE_DEVICE_MANAGE。

Smart wake and card settings

kotlin · worker thread
session.updateSettings(smartWakeEnabled = true)
session.updateSettings(name = "Work card")

val s = session.status()
s.smartWakeEnabled
s.isListening

With smart wake on, the card listens for its owner's voice and starts recording only when it hears them; nothing is stored while it listens. With it off, the card records only when the button is pressed or an app starts a recording. Smart wake changes arrive as SmartWakeTriggered and ListeningChanged card events.

開啟智能喚醒後,卡片監聽機主的聲音,聽到機主說話才開始錄音;監聽期間不存儲任何內容。關閉後,卡片只在按鍵或 App 發起時錄音。智能喚醒的變化以 SmartWakeTriggered 與 ListeningChanged 卡片事件送達。

Voiceprint enrolment

kotlin · worker thread
// About 12 seconds of the user's own voice. Needs network.
session.enrollVoiceprint { step ->
    when (step) {
        is EnrollStep.Recording  -> showLevel(step.level)   // the user speaks now
        is EnrollStep.Processing -> showProgress()
        is EnrollStep.Done       -> showDone()
    }
}

The card records the sample, the cloud builds the voiceprint and writes it back to the card, where smart wake and speaker identity use it. Replacing an existing voiceprint asks for a press on the card first.

卡片錄下樣本,雲端生成聲紋並寫回卡片,供智能喚醒與說話人身份使用。若卡上已有聲紋,替換前需要先在卡上按鍵確認。

Firmware updates

kotlin · worker thread
val update = session.checkFirmwareUpdate()     // null when the card is up to date
if (update != null) {
    session.installFirmware(update, overWifi = true) { progress -> }
}

The card accepts only images VYSLO signed; the SDK fetches the release meant for this card. Over Wi-Fi the card downloads through its own hotspot, much faster than BLE. The new image takes over on the next restart, and if it does not come up healthy the card rolls back to the previous one by itself.

卡片只接受 VYSLO 簽名的鏡像;SDK 會取回適用於這張卡的版本。走 Wi-Fi 時卡片經自己的熱點下載,比 BLE 快得多。 新鏡像在下次重啟時接管;若啟動後不健康,卡片會自動回滾到上一個版本。

Deleting, unbinding and on-card confirmation

kotlin · worker thread
session.deleteRecording(recordingId)

// These wait for a press on the card, then run.
session.deleteAllRecordings()
session.resetVoiceprint()
session.unbind()
THE USER CONFIRMS ON THE CARD

Deleting all recordings, resetting a voiceprint and unbinding put the card into a pending state: the LED shows the confirmation signal and the user has 10 seconds to press the button. No press, no action, and the call throws CONFIRMATION_TIMEOUT. The rule is enforced by the card, so it applies to every app, the VYSLO app included.

由用戶在卡片上確認

刪除全部錄音、重置聲紋與解綁會讓卡片進入待確認狀態:LED 顯示確認提示,用戶需在 10 秒內按鍵。沒有按鍵就不執行, 調用拋出 CONFIRMATION_TIMEOUT。這條規則由卡片執行,對所有 App 生效,包括 VYSLO App 自己。

LED and haptics

Choose how the card's LED looks in each state and how it vibrates on each event. Settings are saved on the card and keep working offline. Every connected app sees the same settings, and a change saved by one app is pushed to the others. Reading and writing both need SCOPE_DEVICE_MANAGE.

按卡片狀態設置 LED 的樣子,按卡片事件設置振動方式。設置保存在卡片上,離線照樣生效。所有已連接的 App 看到的是同一份設置, 一個 App 保存後會推送給其他 App。讀取與寫入都需要 SCOPE_DEVICE_MANAGE。

kotlin · worker thread
val caps = session.feedback.getCapabilities()  // states, events, modes, ranges, effect catalogue
val current = session.feedback.getSettings()   // your overrides, defaults, resolved values, revision

session.feedback.updateSettings(
    patch = FeedbackPatch()
        .setLed("recording.smartWakeOn",
            LedProfile.breathe(colorRgb = Rgb(0, 60, 100), brightnessPercent = 40,
                               periodMs = 2400, minBrightnessPercent = 0))
        .setLed("idle.smartWakeOff", LedProfile.off())
        .setHaptic("recording.started", HapticPattern.effect("START_CONFIRM"))
        .setHaptic("recording.stopped.SILENCE",
            HapticPattern.pulses(levelPercent = 60, onMs = 80, offMs = 120, count = 2)),
    expectedRevision = current.revision,        // rejected if another app saved in between
)

// Try before saving. A preview never saves and never fakes a real event.
val preview = session.feedback.previewLed(profile, durationMs = 3000)
session.feedback.stopPreview(preview.id)
session.feedback.previewHaptic(pattern)

session.feedback.restoreDefaults(listOf("idle.smartWakeOff"))   // or restoreDefaults() for all
session.feedback.onChange { change -> }        // saved elsewhere, defaults restored, state changed
session.feedback.getStatus()                    // the state being shown right now, and why

LED modes

ModeParameters
offNone. The LED is dark in this state; recording and power are unaffected
steadycolorRgb, brightnessPercent
breathecolorRgb, brightnessPercent, periodMs, minBrightnessPercent
blinkcolorRgb, brightnessPercent, onMs, offMs
模式參數
off無。該狀態下熄燈,不影響錄音與電源
steadycolorRgb、brightnessPercent
breathecolorRgb、brightnessPercent、periodMs、minBrightnessPercent
blinkcolorRgb、brightnessPercent、onMs、offMs

colorRgb takes 0–100 per channel and brightnessPercent 0–100. Times are multiples of timeQuantumMs from getCapabilities(). A value the card cannot show exactly is refused with the reason, never silently rounded.

colorRgb 每通道 0–100,brightnessPercent 0–100。時間須為 getCapabilities() 給出的 timeQuantumMs 的整數倍。卡片無法精確呈現的值會被拒絕並說明原因, 不會被悄悄取整。

LED states

StateWhenDefault
idle.smartWakeOn
idle.smartWakeOff
Standing bySteady, 2%
monitoringSmart wake is listeningSame as standby
recording.smartWakeOn
recording.smartWakeOff
RecordingBreathe, peak 30%, 2 s
recording.pausedRecording pausedSame as recording
enrollmentRecording a voiceprint sampleSame as recording
chargingChargingSteady amber
chargeCompleteCharge completeThe state underneath
lowBatteryBelow 10% and not chargingAmber blink, 500 ms on and off
pairingPairing window openThe state underneath
authorizationPendingWaiting for an on-card confirmationConfirmation signal; cannot be turned fully off
bondClearFeedbackUnbind completedRed blink, three times
powerOn
powerOff
Power-on and power-off windowsOff
狀態何時默認
idle.smartWakeOn
idle.smartWakeOff
待機常亮,2%
monitoring智能喚醒監聽中同待機
recording.smartWakeOn
recording.smartWakeOff
錄音中呼吸,峰值 30%,週期 2 秒
recording.paused錄音已暫停同錄音
enrollment正在錄聲紋樣本同錄音
charging充電中琥珀色常亮
chargeComplete充滿沿用下層狀態
lowBattery電量低於 10% 且未充電琥珀色閃爍,亮滅各 500 ms
pairing配對窗口打開沿用下層狀態
authorizationPending等待卡上確認確認提示;不能完全熄滅
bondClearFeedback解綁完成紅色閃爍三次
powerOn
powerOff
開機與關機窗口熄滅

The card picks the state from what is actually happening; your settings change only how that state looks. When several hold at once the higher one wins: unbind feedback, power off, confirmation pending, low battery, recording, power on, charging, charge complete, pairing, listening, standby. A state can also inherit another one, a specific key or @idle, @recording or @underlying (the next state down).

卡片按實際發生的事選擇狀態;你的設置只改變該狀態的呈現。多個狀態同時成立時取優先級高者:解綁反饋、關機、待確認、 低電量、錄音、開機、充電、充滿、配對、監聽、待機。狀態也可以繼承另一個狀態:具體的 key,或 @idle、 @recording、@underlying(下一層狀態)。

Haptic events

EventWhenDefault
recording.startedA recording actually starts, from the button, an app or smart wakeSTART_CONFIRM
recording.stoppedA recording stopsSTOP_CONFIRM
recording.stopped.<origin>Stopped for a specific reason: silence, smart wake turned off, a firmware update, and othersSame as recording.stopped
recording.paused
recording.resumed
Pause and resumeOff
monitoring.started
monitoring.ended
Smart wake starts or stops listeningOff
recording.discarded.l2Reject
recording.discarded.noSpeech
Smart wake heard someone else, or no speech, and kept nothingOff
recording.interrupted.<reason>A recording cut short by pairing, shutdown or a critical batteryOff
pairing.startedPairing window opensPAIRING
operation.rejectedThe card refused a request, per reason: low battery, busy, shutting downREJECT
power.on
power.off
power.off.criticalBattery
Power on and off by the button, or off on a critical batteryDouble buzz
事件何時默認
recording.started錄音真正開始:按鍵、App 或智能喚醒發起START_CONFIRM
recording.stopped錄音停止STOP_CONFIRM
recording.stopped.<origin>因特定原因停止:靜音、關閉智能喚醒、固件升級等同 recording.stopped
recording.paused
recording.resumed
暫停與繼續關閉
monitoring.started
monitoring.ended
智能喚醒開始或結束監聽關閉
recording.discarded.l2Reject
recording.discarded.noSpeech
智能喚醒聽到的是別人或沒有語音,未保留任何內容關閉
recording.interrupted.<reason>錄音因配對、關機或電量耗盡被中斷關閉
pairing.started配對窗口打開PAIRING
operation.rejected卡片拒絕了請求,可按原因區分:低電量、忙碌、正在關機REJECT
power.on
power.off
power.off.criticalBattery
按鍵開關機,或電量耗盡關機雙振

A haptic pattern is either an effect from the card's catalogue (getCapabilities() lists the ones this card's motor supports) or pulses: levelPercent 0–100, onMs, offMs and count. Limits come from getCapabilities(); anything outside them is refused with the reason, never silently changed.

振動方式可以是卡片效果目錄中的效果(getCapabilities() 列出這張卡的馬達支援哪些),也可以是脈衝: levelPercent 0–100、onMs、offMs 與 count。上下限由 getCapabilities() 給出;超出範圍的請求會被拒絕並說明原因,不會被悄悄改動。

FEEDBACK NEVER OVERRIDES PROTECTION

Battery protection, data protection and on-card confirmation cannot be switched off from feedback settings. A custom colour can make two states look alike, so show state in your own UI from status(), not from the LED.

反饋設置不會蓋過保護機制

電池保護、數據保護與卡上確認,都不能通過反饋設置關閉。自定義顏色可能讓兩個狀態看起來一樣,所以在你自己的界面裡請以 status() 顯示狀態,而不是看 LED。

Token lifecycle

kotlin
// Call on foreground start, on a worker thread. Internally rate-limited:
// does nothing if the stored token is younger than minAgeDays.
vyslo.renewIfNeeded()                 // default 7 days
vyslo.renewIfNeeded(minAgeDays = 3)

vyslo.setToken(token)                 // adopt a token you obtained yourself
vyslo.clearToken()                    // forget it (e.g. on your own sign-out)
BehaviourValue
Token lifetime from issue60 days — the offline grace period
Renewal cadencerenewIfNeeded() acts once the token is 7 days old
User revoked, or app banned403 — the SDK clears the stored token, then throws
Invalid or expired token401 — the SDK clears the stored token, then throws
Where the user revokesVYSLO app → 我的 → 已授權的應用
行為取值
令牌自簽發起的有效期60 天——離線寬限期
續簽節奏renewIfNeeded() 在令牌滿 7 天後才動作
用戶撤銷或應用被封禁403——SDK 先清除本地令牌再拋異常
令牌無效或過期401——SDK 先清除本地令牌再拋異常
用戶在哪裡撤銷VYSLO App → 我的 → 已授權的應用

Errors

kotlin
try {
    val rec = vyslo.fetchRecording(recordingId)
} catch (e: VysloOpenException) {
    when (e.statusCode) {
        401, 403 -> reauthorize()     // token already cleared by the SDK
        404      -> dropLocalCopy()   // hidden, deleted, or not this user's recording
        else     -> retryLater(e.body)
    }
}

VysloOpenException carries statusCode and the raw response body. Note that 404 is deliberately indistinguishable between "does not exist" and "exists but is hidden or belongs to someone else" — the API does not confirm the existence of a recording you may not read.

VysloOpenException 帶 statusCode 與原始回應 body。 注意 404 故意不區分「不存在」與「存在但被隱藏或屬於他人」——API 不會確認一條 你無權讀取的錄音是否存在。

Webhook signature helper

kotlin
// Reference implementation — your server does the same thing in its own language.
val ok = VysloOpen.verifyWebhookSignature(
    webhookSecret = secret,
    rawBody = body,                       // exactly the bytes you received
    signature = header("X-Vyslo-Signature"),
)

See Webhooks for the payload, the retry schedule and examples in other languages.

載荷、重試節奏與其他語言的示例見 Webhook 事件。

Reference

Member Meaning
VysloOpen(context, appId, baseUrl)Construct; baseUrl defaults to the official cloud
authorizeIntent(scopes, callbackUri)Intent that opens the VYSLO consent screen
handleAuthorizationCallback(uri)Extract and store the token from the callback URI; true on success
renewIfNeeded(minAgeDays)Rate-limited renewal; true if it actually renewed
fetchRecording(id)Transcript segments, summary, speaker identity
fetchRecordingAudio(id)Original audio bytes; needs the audio scope
listRecordings(...) / searchTranscripts(...)Paged recording list / search across transcripts
fetchWeeklyInsights()Weekly insights centred on the user
listDevices()Cards on the account with a cloud status snapshot
transcribe(file, summarize)Transcribe any audio file, optionally summarised
setToken(t) / clearToken()Adopt or forget the stored token
token / isAuthorizedCurrent token and authorization state
ble.startScan(onCard) / ble.connect(card)Scan for nearby cards / connect and present the token, returning a VysloCardSession
session.status()Battery, charging, storage, firmware, recording, listening, smart wake
session.acquireControl() / releaseControl()Take or give back the exclusive control lease
session.startRecording() … stopRecording()Start, pause, resume, stop; needs the lease
session.onEvent(listener)Card events: recording, smart wake, listening, button, charging
session.listRecordings() / exportAudio(...)Recordings on the card / export one over BLE
session.fastTransfer(...)Bulk export over the card's Wi-Fi hotspot
session.startLiveAudio(format, onFrame)Push-to-talk live audio stream
session.updateSettings(...)Smart wake on or off, card name
session.enrollVoiceprint(onStep)Voiceprint enrolment
session.checkFirmwareUpdate() / installFirmware(...)Check for / install signed firmware, over BLE or Wi-Fi
session.deleteRecording(id) / deleteAllRecordings() / resetVoiceprint() / unbind()Delete and unbind; the last three need a press on the card
session.feedback.*LED and haptics: capabilities, settings, preview, restore defaults, change notifications
session.close()Disconnect and release the lease
VysloOpen.verifyWebhookSignature(...)HMAC-SHA256 verification reference
VysloOpen.DEFAULT_BASE_URLhttps://api.vyslo.ai:8443/api/v1/app
SCOPE_RECORDINGS_READ … SCOPE_TRANSCRIPTION_CREATEScope constants — see Scopes & tokens