| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775 |
- export interface HeartRateControllerCallbacks {
- onHeartRate: (bpm: number) => void
- onStatus: (message: string) => void
- onError: (message: string) => void
- onConnectionChange: (connected: boolean, deviceName: string | null) => void
- onDeviceListChange: (devices: HeartRateDiscoveredDevice[]) => void
- }
- export interface HeartRateDiscoveredDevice {
- deviceId: string
- name: string
- rssi: number | null
- lastSeenAt: number
- isPreferred: boolean
- }
- type BluetoothDeviceLike = {
- deviceId?: string
- name?: string
- localName?: string
- RSSI?: number
- advertisServiceUUIDs?: string[]
- }
- const HEART_RATE_SERVICE_UUID = '180D'
- const HEART_RATE_MEASUREMENT_UUID = '2A37'
- const DISCOVERY_TIMEOUT_MS = 12000
- const DEVICE_STALE_MS = 15000
- const PREFERRED_HEART_RATE_DEVICE_STORAGE_KEY = 'cmr.preferredHeartRateDevice'
- function normalizeUuid(uuid: string | undefined | null): string {
- return String(uuid || '').replace(/[^0-9a-f]/gi, '').toUpperCase()
- }
- function matchesShortUuid(uuid: string | undefined | null, shortUuid: string): boolean {
- const normalized = normalizeUuid(uuid)
- const normalizedShort = normalizeUuid(shortUuid)
- if (!normalized || !normalizedShort) {
- return false
- }
- return normalized === normalizedShort
- || normalized.indexOf(`0000${normalizedShort}00001000800000805F9B34FB`) === 0
- || normalized.endsWith(normalizedShort)
- }
- function getDeviceDisplayName(device: BluetoothDeviceLike | null | undefined): string {
- if (!device) {
- return '心率带'
- }
- return device.name || device.localName || '未命名心率带'
- }
- function isHeartRateDevice(device: BluetoothDeviceLike): boolean {
- const serviceIds = Array.isArray(device.advertisServiceUUIDs) ? device.advertisServiceUUIDs : []
- if (serviceIds.some((uuid) => matchesShortUuid(uuid, HEART_RATE_SERVICE_UUID))) {
- return true
- }
- const name = `${device.name || ''} ${device.localName || ''}`.toUpperCase()
- return name.indexOf('HR') !== -1
- || name.indexOf('HEART') !== -1
- || name.indexOf('POLAR') !== -1
- || name.indexOf('GARMIN') !== -1
- || name.indexOf('COOSPO') !== -1
- }
- function parseHeartRateMeasurement(buffer: ArrayBuffer): number | null {
- if (!buffer || buffer.byteLength < 2) {
- return null
- }
- const view = new DataView(buffer)
- const flags = view.getUint8(0)
- const isUint16 = (flags & 0x01) === 0x01
- if (isUint16) {
- if (buffer.byteLength < 3) {
- return null
- }
- return view.getUint16(1, true)
- }
- return view.getUint8(1)
- }
- export class HeartRateController {
- callbacks: HeartRateControllerCallbacks
- scanning: boolean
- connecting: boolean
- connected: boolean
- currentDeviceId: string | null
- currentDeviceName: string | null
- discoveredDevices: HeartRateDiscoveredDevice[]
- lastDeviceId: string | null
- lastDeviceName: string | null
- manualDisconnect: boolean
- reconnecting: boolean
- disconnecting: boolean
- disconnectingDeviceId: string | null
- autoConnectDeviceId: string | null
- measurementServiceId: string | null
- measurementCharacteristicId: string | null
- discoveryTimer: number
- reconnectTimer: number
- deviceFoundHandler: ((result: any) => void) | null
- characteristicHandler: ((result: any) => void) | null
- connectionStateHandler: ((result: any) => void) | null
- constructor(callbacks: HeartRateControllerCallbacks) {
- this.callbacks = callbacks
- this.scanning = false
- this.connecting = false
- this.connected = false
- this.currentDeviceId = null
- this.currentDeviceName = null
- this.discoveredDevices = []
- this.lastDeviceId = null
- this.lastDeviceName = null
- this.manualDisconnect = false
- this.reconnecting = false
- this.disconnecting = false
- this.disconnectingDeviceId = null
- this.autoConnectDeviceId = null
- this.measurementServiceId = null
- this.measurementCharacteristicId = null
- this.discoveryTimer = 0
- this.reconnectTimer = 0
- this.deviceFoundHandler = null
- this.characteristicHandler = null
- this.connectionStateHandler = null
- this.restorePreferredDevice()
- }
- startScanAndConnect(): void {
- if (this.connected) {
- this.callbacks.onStatus(`心率带已连接: ${this.currentDeviceName || '设备'}`)
- return
- }
- if (this.disconnecting) {
- this.callbacks.onStatus('心率带断开中,请稍后再试')
- return
- }
- if (this.scanning || this.connecting) {
- this.callbacks.onStatus('心率带连接进行中')
- return
- }
- this.manualDisconnect = false
- this.reconnecting = false
- this.clearReconnectTimer()
- this.withFreshBluetoothAdapter(() => {
- if (this.lastDeviceId) {
- this.callbacks.onStatus(`正在扫描并优先连接 ${this.lastDeviceName || '心率带'}`)
- this.beginDiscovery()
- return
- }
- this.beginDiscovery()
- })
- }
- disconnect(): void {
- this.clearDiscoveryTimer()
- this.clearReconnectTimer()
- this.stopDiscovery()
- const deviceId = this.currentDeviceId
- this.connecting = false
- this.reconnecting = false
- this.manualDisconnect = true
- this.disconnecting = true
- if (!deviceId) {
- this.disconnecting = false
- this.clearConnectionState()
- this.callbacks.onStatus('心率带未连接')
- return
- }
- this.disconnectingDeviceId = deviceId
- const wxAny = wx as any
- wxAny.closeBLEConnection({
- deviceId,
- complete: () => {
- this.disconnecting = false
- this.clearConnectionState()
- this.callbacks.onStatus('心率带已断开')
- },
- })
- }
- destroy(): void {
- this.clearDiscoveryTimer()
- this.clearReconnectTimer()
- this.stopDiscovery()
- this.detachListeners()
- const deviceId = this.currentDeviceId
- if (deviceId) {
- const wxAny = wx as any
- wxAny.closeBLEConnection({
- deviceId,
- complete: () => {},
- })
- }
- const wxAny = wx as any
- if (typeof wxAny.closeBluetoothAdapter === 'function') {
- wxAny.closeBluetoothAdapter({
- complete: () => {},
- })
- }
- this.clearConnectionState()
- }
- beginDiscovery(): void {
- if (this.scanning || this.connecting || this.connected) {
- return
- }
- this.bindListeners()
- this.autoConnectDeviceId = this.lastDeviceId
- const wxAny = wx as any
- wxAny.startBluetoothDevicesDiscovery({
- allowDuplicatesKey: false,
- services: [HEART_RATE_SERVICE_UUID],
- success: () => {
- this.scanning = true
- this.pruneDiscoveredDevices()
- this.callbacks.onStatus(this.autoConnectDeviceId ? '正在扫描心率带并等待自动连接' : '正在扫描心率带,请选择设备')
- this.clearDiscoveryTimer()
- this.discoveryTimer = setTimeout(() => {
- this.discoveryTimer = 0
- if (!this.scanning || this.connected || this.connecting) {
- return
- }
- this.stopDiscovery()
- this.callbacks.onError(this.discoveredDevices.length ? '已发现心率带,请从列表选择连接' : '未发现可连接的心率带')
- }, DISCOVERY_TIMEOUT_MS) as unknown as number
- },
- fail: (error: any) => {
- const message = error && error.errMsg ? error.errMsg : 'startBluetoothDevicesDiscovery 失败'
- this.callbacks.onError(`扫描心率带失败: ${message}`)
- },
- })
- }
- stopDiscovery(): void {
- this.clearDiscoveryTimer()
- if (!this.scanning) {
- return
- }
- this.scanning = false
- this.autoConnectDeviceId = null
- const wxAny = wx as any
- wxAny.stopBluetoothDevicesDiscovery({
- complete: () => {},
- })
- }
- bindListeners(): void {
- const wxAny = wx as any
- if (!this.deviceFoundHandler) {
- this.deviceFoundHandler = (result: any) => {
- const devices = Array.isArray(result && result.devices)
- ? result.devices
- : result && result.deviceId
- ? [result]
- : []
- this.mergeDiscoveredDevices(devices)
- if (!this.scanning || this.connecting || this.connected) {
- return
- }
- const targetDevice = this.selectTargetDevice(devices)
- if (!targetDevice || !targetDevice.deviceId) {
- return
- }
- this.stopDiscovery()
- this.connectToDevice(targetDevice.deviceId, getDeviceDisplayName(targetDevice))
- }
- if (typeof wxAny.onBluetoothDeviceFound === 'function') {
- wxAny.onBluetoothDeviceFound(this.deviceFoundHandler)
- }
- }
- if (!this.characteristicHandler) {
- this.characteristicHandler = (result: any) => {
- if (!result || !result.value) {
- return
- }
- if (this.currentDeviceId && result.deviceId && result.deviceId !== this.currentDeviceId) {
- return
- }
- if (!matchesShortUuid(result.characteristicId, HEART_RATE_MEASUREMENT_UUID)) {
- return
- }
- const bpm = parseHeartRateMeasurement(result.value)
- if (bpm === null || !Number.isFinite(bpm) || bpm <= 0) {
- return
- }
- this.callbacks.onHeartRate(Math.round(bpm))
- }
- if (typeof wxAny.onBLECharacteristicValueChange === 'function') {
- wxAny.onBLECharacteristicValueChange(this.characteristicHandler)
- }
- }
- if (!this.connectionStateHandler) {
- this.connectionStateHandler = (result: any) => {
- if (!result || !result.deviceId) {
- return
- }
- if (result.connected) {
- return
- }
- if (this.disconnectingDeviceId && result.deviceId === this.disconnectingDeviceId) {
- this.disconnectingDeviceId = null
- return
- }
- if (!this.currentDeviceId || result.deviceId !== this.currentDeviceId) {
- return
- }
- const disconnectedDeviceId = this.currentDeviceId
- const disconnectedDeviceName = this.currentDeviceName
- this.clearConnectionState()
- this.callbacks.onStatus('心率带连接已断开')
- this.scheduleAutoReconnect(disconnectedDeviceId, disconnectedDeviceName)
- }
- if (typeof wxAny.onBLEConnectionStateChange === 'function') {
- wxAny.onBLEConnectionStateChange(this.connectionStateHandler)
- }
- }
- }
- detachListeners(): void {
- const wxAny = wx as any
- if (this.deviceFoundHandler && typeof wxAny.offBluetoothDeviceFound === 'function') {
- wxAny.offBluetoothDeviceFound(this.deviceFoundHandler)
- }
- if (this.characteristicHandler && typeof wxAny.offBLECharacteristicValueChange === 'function') {
- wxAny.offBLECharacteristicValueChange(this.characteristicHandler)
- }
- if (this.connectionStateHandler && typeof wxAny.offBLEConnectionStateChange === 'function') {
- wxAny.offBLEConnectionStateChange(this.connectionStateHandler)
- }
- this.deviceFoundHandler = null
- this.characteristicHandler = null
- this.connectionStateHandler = null
- }
- connectToDevice(deviceId: string, deviceName: string, fallbackToDiscovery: boolean = false): void {
- this.connecting = true
- this.reconnecting = false
- this.disconnecting = false
- this.manualDisconnect = false
- this.disconnectingDeviceId = null
- this.autoConnectDeviceId = deviceId
- this.currentDeviceId = deviceId
- this.currentDeviceName = deviceName
- this.callbacks.onStatus(`正在连接 ${deviceName}`)
- const wxAny = wx as any
- wxAny.createBLEConnection({
- deviceId,
- timeout: 10000,
- success: () => {
- this.discoverMeasurementCharacteristic(deviceId, deviceName)
- },
- fail: (error: any) => {
- const message = error && error.errMsg ? error.errMsg : 'createBLEConnection 失败'
- this.clearConnectionState()
- if (fallbackToDiscovery && !this.manualDisconnect) {
- this.callbacks.onStatus(`直连失败,转入扫描: ${deviceName}`)
- this.beginDiscovery()
- return
- }
- this.callbacks.onError(`连接心率带失败: ${message}`)
- },
- })
- }
- discoverMeasurementCharacteristic(deviceId: string, deviceName: string): void {
- const wxAny = wx as any
- wxAny.getBLEDeviceServices({
- deviceId,
- success: (serviceResult: any) => {
- const services = Array.isArray(serviceResult && serviceResult.services) ? serviceResult.services : []
- const service = services.find((item: any) => matchesShortUuid(item && item.uuid, HEART_RATE_SERVICE_UUID))
- if (!service || !service.uuid) {
- this.failConnection(deviceId, '未找到标准心率服务')
- return
- }
- wxAny.getBLEDeviceCharacteristics({
- deviceId,
- serviceId: service.uuid,
- success: (characteristicResult: any) => {
- const characteristics = Array.isArray(characteristicResult && characteristicResult.characteristics)
- ? characteristicResult.characteristics
- : []
- const characteristic = characteristics.find((item: any) => {
- const properties = item && item.properties ? item.properties : {}
- return matchesShortUuid(item && item.uuid, HEART_RATE_MEASUREMENT_UUID)
- && (properties.notify || properties.indicate)
- })
- if (!characteristic || !characteristic.uuid) {
- this.failConnection(deviceId, '未找到心率通知特征')
- return
- }
- this.measurementServiceId = service.uuid
- this.measurementCharacteristicId = characteristic.uuid
- wxAny.notifyBLECharacteristicValueChange({
- state: true,
- deviceId,
- serviceId: service.uuid,
- characteristicId: characteristic.uuid,
- success: () => {
- this.connecting = false
- this.connected = true
- this.lastDeviceId = deviceId
- this.lastDeviceName = deviceName
- this.persistPreferredDevice()
- this.manualDisconnect = false
- this.reconnecting = false
- this.disconnectingDeviceId = null
- this.autoConnectDeviceId = deviceId
- this.refreshPreferredFlags()
- this.clearReconnectTimer()
- this.callbacks.onConnectionChange(true, deviceName)
- this.callbacks.onStatus(`心率带已连接: ${deviceName}`)
- },
- fail: (error: any) => {
- const message = error && error.errMsg ? error.errMsg : 'notifyBLECharacteristicValueChange 失败'
- this.failConnection(deviceId, `心率订阅失败: ${message}`)
- },
- })
- },
- fail: (error: any) => {
- const message = error && error.errMsg ? error.errMsg : 'getBLEDeviceCharacteristics 失败'
- this.failConnection(deviceId, `读取心率特征失败: ${message}`)
- },
- })
- },
- fail: (error: any) => {
- const message = error && error.errMsg ? error.errMsg : 'getBLEDeviceServices 失败'
- this.failConnection(deviceId, `读取心率服务失败: ${message}`)
- },
- })
- }
- failConnection(deviceId: string, message: string): void {
- const wxAny = wx as any
- wxAny.closeBLEConnection({
- deviceId,
- complete: () => {
- this.clearConnectionState()
- this.callbacks.onError(message)
- this.scheduleAutoReconnect(this.lastDeviceId, this.lastDeviceName)
- },
- })
- }
- clearConnectionState(): void {
- const wasConnected = this.connected
- this.scanning = false
- this.connecting = false
- this.connected = false
- this.disconnecting = false
- this.disconnectingDeviceId = null
- this.autoConnectDeviceId = null
- this.currentDeviceId = null
- this.measurementServiceId = null
- this.measurementCharacteristicId = null
- const previousDeviceName = this.currentDeviceName
- this.currentDeviceName = null
- if (wasConnected || previousDeviceName) {
- this.callbacks.onConnectionChange(false, null)
- }
- }
- clearDiscoveryTimer(): void {
- if (this.discoveryTimer) {
- clearTimeout(this.discoveryTimer)
- this.discoveryTimer = 0
- }
- }
- clearReconnectTimer(): void {
- if (this.reconnectTimer) {
- clearTimeout(this.reconnectTimer)
- this.reconnectTimer = 0
- }
- }
- selectTargetDevice(devices: BluetoothDeviceLike[]): BluetoothDeviceLike | null {
- if (!Array.isArray(devices) || !devices.length) {
- return null
- }
- if (this.autoConnectDeviceId) {
- const rememberedDevice = devices.find((device) => device && device.deviceId === this.autoConnectDeviceId)
- if (rememberedDevice && isHeartRateDevice(rememberedDevice)) {
- return rememberedDevice
- }
- return null
- }
- return null
- }
- scheduleAutoReconnect(deviceId: string | null, deviceName: string | null): void {
- if (this.manualDisconnect || !deviceId || this.connected || this.connecting || this.scanning) {
- return
- }
- this.lastDeviceId = deviceId
- this.lastDeviceName = deviceName || this.lastDeviceName
- this.clearReconnectTimer()
- this.reconnecting = true
- this.callbacks.onStatus(`心率带已断开,等待自动重连: ${this.lastDeviceName || '设备'}`)
- this.reconnectTimer = setTimeout(() => {
- this.reconnectTimer = 0
- if (this.manualDisconnect || this.connected || this.connecting || this.scanning) {
- return
- }
- const rememberedDeviceId = this.lastDeviceId
- const rememberedDeviceName = this.lastDeviceName || '心率带'
- this.reconnecting = false
- this.withFreshBluetoothAdapter(() => {
- if (rememberedDeviceId) {
- this.callbacks.onStatus(`正在自动重连 ${rememberedDeviceName}`)
- this.resetConnectionAndConnect(rememberedDeviceId, rememberedDeviceName, true)
- return
- }
- this.beginDiscovery()
- })
- }, 600) as unknown as number
- }
- resetConnectionAndConnect(deviceId: string, deviceName: string, fallbackToDiscovery: boolean): void {
- const wxAny = wx as any
- this.disconnectingDeviceId = deviceId
- wxAny.closeBLEConnection({
- deviceId,
- complete: () => {
- if (this.disconnectingDeviceId === deviceId) {
- this.disconnectingDeviceId = null
- }
- setTimeout(() => {
- if (this.connected || this.connecting || this.scanning || this.disconnecting) {
- return
- }
- this.connectToDevice(deviceId, deviceName, fallbackToDiscovery)
- }, 320)
- },
- })
- }
- connectToDiscoveredDevice(deviceId: string): void {
- if (!deviceId) {
- return
- }
- if (this.disconnecting) {
- this.callbacks.onStatus('心率带断开中,请稍后再试')
- return
- }
- const targetDevice = this.discoveredDevices.find((device) => device.deviceId === deviceId)
- const deviceName = targetDevice ? targetDevice.name : (this.lastDeviceId === deviceId ? (this.lastDeviceName || '心率带') : '心率带')
- this.lastDeviceId = deviceId
- this.lastDeviceName = deviceName
- this.refreshPreferredFlags()
- this.stopDiscovery()
- this.withFreshBluetoothAdapter(() => {
- this.callbacks.onStatus(`正在连接 ${deviceName}`)
- this.resetConnectionAndConnect(deviceId, deviceName, true)
- })
- }
- clearPreferredDevice(): void {
- this.lastDeviceId = null
- this.lastDeviceName = null
- this.autoConnectDeviceId = null
- this.removePreferredDevice()
- this.refreshPreferredFlags()
- this.callbacks.onStatus('已清除首选心率带')
- }
- mergeDiscoveredDevices(devices: BluetoothDeviceLike[]): void {
- if (!Array.isArray(devices) || !devices.length) {
- return
- }
- const now = Date.now()
- let changed = false
- const nextDevices = [...this.discoveredDevices]
- for (const rawDevice of devices) {
- if (!rawDevice || !rawDevice.deviceId || !isHeartRateDevice(rawDevice)) {
- continue
- }
- const name = getDeviceDisplayName(rawDevice)
- const rssi = typeof rawDevice.RSSI === 'number' && Number.isFinite(rawDevice.RSSI) ? rawDevice.RSSI : null
- const existingIndex = nextDevices.findIndex((item) => item.deviceId === rawDevice.deviceId)
- const nextDevice: HeartRateDiscoveredDevice = {
- deviceId: rawDevice.deviceId,
- name,
- rssi,
- lastSeenAt: now,
- isPreferred: rawDevice.deviceId === this.lastDeviceId,
- }
- if (existingIndex >= 0) {
- nextDevices[existingIndex] = nextDevice
- } else {
- nextDevices.push(nextDevice)
- }
- changed = true
- }
- if (!changed) {
- return
- }
- this.discoveredDevices = this.sortDiscoveredDevices(nextDevices.filter((device) => now - device.lastSeenAt <= DEVICE_STALE_MS))
- this.callbacks.onDeviceListChange([...this.discoveredDevices])
- }
- pruneDiscoveredDevices(): void {
- const now = Date.now()
- const nextDevices = this.sortDiscoveredDevices(
- this.discoveredDevices.filter((device) => now - device.lastSeenAt <= DEVICE_STALE_MS),
- )
- const changed = nextDevices.length !== this.discoveredDevices.length
- || nextDevices.some((device, index) => {
- const previous = this.discoveredDevices[index]
- return !previous || previous.deviceId !== device.deviceId || previous.isPreferred !== device.isPreferred || previous.rssi !== device.rssi
- })
- this.discoveredDevices = nextDevices
- if (changed) {
- this.callbacks.onDeviceListChange([...this.discoveredDevices])
- }
- }
- refreshPreferredFlags(): void {
- if (!this.discoveredDevices.length) {
- this.callbacks.onDeviceListChange([])
- return
- }
- this.discoveredDevices = this.sortDiscoveredDevices(
- this.discoveredDevices.map((device) => ({
- ...device,
- isPreferred: !!this.lastDeviceId && device.deviceId === this.lastDeviceId,
- })),
- )
- this.callbacks.onDeviceListChange([...this.discoveredDevices])
- }
- sortDiscoveredDevices(devices: HeartRateDiscoveredDevice[]): HeartRateDiscoveredDevice[] {
- return [...devices].sort((a, b) => {
- if (a.isPreferred !== b.isPreferred) {
- return a.isPreferred ? -1 : 1
- }
- const aRssi = a.rssi === null ? -999 : a.rssi
- const bRssi = b.rssi === null ? -999 : b.rssi
- if (aRssi !== bRssi) {
- return bRssi - aRssi
- }
- return b.lastSeenAt - a.lastSeenAt
- })
- }
- withFreshBluetoothAdapter(onReady: () => void): void {
- const wxAny = wx as any
- const openAdapter = () => {
- wxAny.openBluetoothAdapter({
- success: () => {
- onReady()
- },
- fail: (error: any) => {
- const message = error && error.errMsg ? error.errMsg : 'openBluetoothAdapter 失败'
- this.callbacks.onError(`蓝牙不可用: ${message}`)
- },
- })
- }
- if (typeof wxAny.closeBluetoothAdapter !== 'function') {
- openAdapter()
- return
- }
- wxAny.closeBluetoothAdapter({
- complete: () => {
- setTimeout(() => {
- openAdapter()
- }, 180)
- },
- })
- }
- restorePreferredDevice(): void {
- try {
- const stored = wx.getStorageSync(PREFERRED_HEART_RATE_DEVICE_STORAGE_KEY)
- if (!stored || typeof stored !== 'object') {
- return
- }
- const normalized = stored as { deviceId?: unknown; name?: unknown }
- if (typeof normalized.deviceId !== 'string' || !normalized.deviceId) {
- return
- }
- this.lastDeviceId = normalized.deviceId
- this.lastDeviceName = typeof normalized.name === 'string' && normalized.name ? normalized.name : '心率带'
- } catch {}
- }
- persistPreferredDevice(): void {
- if (!this.lastDeviceId) {
- return
- }
- try {
- wx.setStorageSync(PREFERRED_HEART_RATE_DEVICE_STORAGE_KEY, {
- deviceId: this.lastDeviceId,
- name: this.lastDeviceName || '心率带',
- })
- } catch {}
- }
- removePreferredDevice(): void {
- try {
- wx.removeStorageSync(PREFERRED_HEART_RATE_DEVICE_STORAGE_KEY)
- } catch {}
- }
- }
|