feat: 펌웨어 버전 앱 내 표시 (Settings + ClinicalHome)

연결 시 자동으로 받던 mid?/rid: device_info를 UI에 노출. 이전에는 BLE 디버그 로그에만
기록되어 사용자가 접근 불가했음.

BleManager:
- firmwareVersion / hardwareVersion / serialNumber 3개 MutableState 추가
- rid: 응답 파싱 — "VBTHW0100   VBT26040001 VBTFW0111" 형식을
  공백 split + prefix 매칭(VBTHW/VBTFW/VBT)으로 안전 추출
- disconnect 시 3 필드 모두 clear

PiezoMonitoringView Settings overlay:
- 기존 "Version" 행을 "App" + "Firmware" 두 행으로 분리
- FW 없으면 "—" 표시 (연결 대기 중)
- dev 모드 한정 "Serial" 행 추가 (회사 자산 추적용)

ClinicalHomeView BLE 연결 카드:
- deviceName 아래 "FW: VBTFW0116" 한 줄 추가
- 검사자가 별도 클릭 없이 펌웨어 버전 즉시 확인 가능

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 09:41:59 +09:00
parent 3b8fc9ca5d
commit e1fa0d1620
3 changed files with 58 additions and 2 deletions
@@ -70,6 +70,10 @@ class BleManager private constructor(private val context: Context) {
val isScanning = mutableStateOf(false)
val isConnected = mutableStateOf(false)
val connectedDeviceName = mutableStateOf("")
// mid? → rid: 응답에서 파싱. 연결 직후 자동 채워짐.
val firmwareVersion = mutableStateOf("") // "VBTFW0116"
val hardwareVersion = mutableStateOf("") // "VBTHW0100"
val serialNumber = mutableStateOf("") // "VBT26040001"
val batteryLevel = mutableStateOf(0)
val bluetoothEnabled = mutableStateOf(bluetoothAdapter?.isEnabled == true)
val isServiceReady = mutableStateOf(false)
@@ -755,6 +759,9 @@ class BleManager private constructor(private val context: Context) {
isConnected.value = false
isServiceReady.value = false
connectedDeviceName.value = ""
firmwareVersion.value = ""
hardwareVersion.value = ""
serialNumber.value = ""
batteryLevel.value = 0
val idx = discoveredDevices.indexOfFirst { it.address == gatt.device.address }
@@ -971,6 +978,14 @@ class BleManager private constructor(private val context: Context) {
val info = if (data.size > 4) String(data, 4, data.size - 4, Charsets.US_ASCII).trim() else "?"
debugLogger.rx("rid", data.size, "device_info=$info")
debugLogger.info("DEVICE_INFO $info")
// 형식: "VBTHW0100 VBT26040001 VBTFW0111" (HW, Serial, FW)
val parts = info.split(Regex("\\s+")).filter { it.isNotBlank() }
handler.post {
parts.firstOrNull { it.startsWith("VBTHW") }?.let { hardwareVersion.value = it }
parts.firstOrNull { it.startsWith("VBTFW") }?.let { firmwareVersion.value = it }
parts.firstOrNull { it.startsWith("VBT") && !it.startsWith("VBTHW") && !it.startsWith("VBTFW") }
?.let { serialNumber.value = it }
}
}
"rls:" -> {
val state = if (data.size >= 5) data[4].toInt() and 0xFF else -1