fix: BLE protocol handling matches actual device behavior
- discoverChars uses properties (write/notify) to distinguish FFE1 command characteristic from FFE1 service UUID - handleValueChange: vendor_33 mode emits vendor_status (FFE4 heartbeat) and vendor_data separately, no longer tries parseFrame on raw bytes - parseVendorStatus handles 1-byte heartbeat and 33-byte state readback with proper IO1-IO5 field parsing
这个提交包含在:
@@ -82,13 +82,20 @@ function handleNotification(frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleValueChange(res) {
|
function handleValueChange(res) {
|
||||||
|
if (protocol.PROTOCOL_MODE === 'vendor_33') {
|
||||||
|
var bytes = protocol.bufferToBytes(res.value)
|
||||||
|
var uuid = (res.characteristicId || '').toUpperCase()
|
||||||
|
var isStatus = uuid.indexOf(CHAR.STATUS) !== -1
|
||||||
|
if (isStatus) {
|
||||||
|
emit('vendor_status', { raw: bytes, heartbeat: bytes[0] })
|
||||||
|
} else {
|
||||||
|
emit('vendor_data', { raw: bytes, hex: protocol.bytesToHex(bytes) })
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
var frame = protocol.parseFrame(res.value)
|
var frame = protocol.parseFrame(res.value)
|
||||||
if (frame) {
|
if (frame) {
|
||||||
handleNotification(frame)
|
handleNotification(frame)
|
||||||
return
|
|
||||||
}
|
|
||||||
if (protocol.PROTOCOL_MODE === 'vendor_33') {
|
|
||||||
emit('status', protocol.parseVendorStatus(res.value))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,9 +111,14 @@ function discoverChars(deviceId, serviceId, group) {
|
|||||||
for (var i = 0; i < chars.length; i++) {
|
for (var i = 0; i < chars.length; i++) {
|
||||||
var c = chars[i]
|
var c = chars[i]
|
||||||
var uuid = c.uuid.toUpperCase()
|
var uuid = c.uuid.toUpperCase()
|
||||||
|
var props = c.properties || {}
|
||||||
if (uuid.indexOf(CHAR.DEVICE_INFO) !== -1) _chars.deviceInfo = { uuid: c.uuid, serviceId: serviceId }
|
if (uuid.indexOf(CHAR.DEVICE_INFO) !== -1) _chars.deviceInfo = { uuid: c.uuid, serviceId: serviceId }
|
||||||
if (uuid.indexOf(CHAR.COMMAND) !== -1) _chars.command = { uuid: c.uuid, serviceId: serviceId }
|
if (uuid.indexOf(CHAR.COMMAND) !== -1 && (props.write || props.writeNoResponse)) {
|
||||||
if (uuid.indexOf(CHAR.STATUS) !== -1) _chars.status = { uuid: c.uuid, serviceId: serviceId }
|
_chars.command = { uuid: c.uuid, serviceId: serviceId }
|
||||||
|
}
|
||||||
|
if (uuid.indexOf(CHAR.STATUS) !== -1 && props.notify) {
|
||||||
|
_chars.status = { uuid: c.uuid, serviceId: serviceId }
|
||||||
|
}
|
||||||
if (uuid.indexOf(CHAR.BOND_INFO) !== -1) _chars.bondInfo = { uuid: c.uuid, serviceId: serviceId }
|
if (uuid.indexOf(CHAR.BOND_INFO) !== -1) _chars.bondInfo = { uuid: c.uuid, serviceId: serviceId }
|
||||||
if (uuid.indexOf(CHAR.OTA_CONTROL) !== -1) _chars.otaControl = { uuid: c.uuid, serviceId: serviceId }
|
if (uuid.indexOf(CHAR.OTA_CONTROL) !== -1) _chars.otaControl = { uuid: c.uuid, serviceId: serviceId }
|
||||||
if (uuid.indexOf(CHAR.OTA_DATA) !== -1) _chars.otaData = { uuid: c.uuid, serviceId: serviceId }
|
if (uuid.indexOf(CHAR.OTA_DATA) !== -1) _chars.otaData = { uuid: c.uuid, serviceId: serviceId }
|
||||||
|
|||||||
@@ -196,11 +196,28 @@ function buildVendorCommand33(options) {
|
|||||||
|
|
||||||
function parseVendorStatus(buffer) {
|
function parseVendorStatus(buffer) {
|
||||||
var bytes = bufferToBytes(buffer)
|
var bytes = bufferToBytes(buffer)
|
||||||
|
if (bytes.length === 1) {
|
||||||
|
return { heartbeat: bytes[0], is_heartbeat: true, raw_hex: bytesToHex(bytes) }
|
||||||
|
}
|
||||||
|
if (bytes.length === 33) {
|
||||||
|
var ios = []
|
||||||
|
for (var i = 0; i < 5; i++) {
|
||||||
|
var off = i * 6
|
||||||
|
ios.push({
|
||||||
|
red: bytes[off], infrared: bytes[off + 1],
|
||||||
|
uv: bytes[off + 2], warm_yellow: bytes[off + 3],
|
||||||
|
gain: (bytes[off + 4] << 8) | bytes[off + 5]
|
||||||
|
})
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
mode_state: bytes.length > 0 ? bytes[0] : MODE_STATE.IDLE,
|
is_heartbeat: false,
|
||||||
raw_bytes: bytes,
|
ios: ios,
|
||||||
|
hold_time: (bytes[30] << 8) | bytes[31],
|
||||||
|
checksum: bytes[32],
|
||||||
raw_hex: bytesToHex(bytes)
|
raw_hex: bytesToHex(bytes)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return { is_heartbeat: false, raw_bytes: bytes, raw_hex: bytesToHex(bytes) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- frame encoding/decoding ---
|
// --- frame encoding/decoding ---
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户