From 56e85e5dba6480e3739ba666034897de417d052e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BE=AE=E5=BE=AE=E4=B8=80=E7=AC=91?= <709648985@qq.com>
Date: Mon, 30 Mar 2026 16:05:03 +0800
Subject: [PATCH 1/4] =?UTF-8?q?=E4=BC=98=E5=8C=96100J=E5=8F=8C=E6=A8=A1?=
=?UTF-8?q?=E4=BD=93=E9=AA=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
api/100J/HBY100-J.js | 46 +++++++++++++++++++++++++++-----------------
1 file changed, 28 insertions(+), 18 deletions(-)
diff --git a/api/100J/HBY100-J.js b/api/100J/HBY100-J.js
index f470974..a99706e 100644
--- a/api/100J/HBY100-J.js
+++ b/api/100J/HBY100-J.js
@@ -1121,37 +1121,47 @@ function execWithBleFirst(bleExec, httpExec, logName, onWaiting, opts = {}) {
} catch (e) {}
};
+ // 协议层认为已连:仍可能被系统蓝牙关闭/底层已断而滞后,先校验适配器,避免先发蓝牙卡超时再回退
if (protocolInstance.isBleConnected && protocolInstance.bleDeviceId) {
- console.log('[100J] 语音上传:协议层已连接,执行蓝牙传文件');
- return doBle().catch(onBleSendFail);
+ return getBleAdapterAvailable().then((adapterOk) => {
+ if (!adapterOk) {
+ protocolInstance.setBleConnectionStatus(false, '');
+ return go4GOrReject('系统蓝牙已关闭,走4G');
+ }
+ console.log('[100J]', logName || '指令', '协议层已连接,走蓝牙');
+ return doBle().catch(onBleSendFail);
+ });
}
- console.log('[100J] 语音上传:协议层未就绪,将等待重连或走 4G', { isBleConnected: protocolInstance.isBleConnected, hasBleDeviceId: !!protocolInstance.bleDeviceId });
- // 无 bleDeviceId:系统蓝牙关闭则立即 4G;开启则短时等页面扫描连上(不再白等 12s)
+ console.log('[100J]', logName || '指令', '协议层未就绪', { isBleConnected: protocolInstance.isBleConnected, hasBleDeviceId: !!protocolInstance.bleDeviceId });
+ // 无 bleDeviceId:本地仅蓝牙场景仍弹「请稍候」并等扫描;可走 4G 时不弹 loading、不白等 5s,直接 4G
if (!protocolInstance.bleDeviceId) {
return getBleAdapterAvailable().then((adapterOk) => {
if (!adapterOk) {
+ protocolInstance.setBleConnectionStatus(false, '');
return go4GOrReject('系统蓝牙未开启,走4G');
}
- if (typeof onWaiting === 'function') onWaiting();
- else showWaitUi('请稍候…');
- return waitForBleConnection()
- .then((connected) => {
- return connected ? doBle().catch(onBleSendFail) : go4GOrReject('蓝牙未连接');
- })
- .finally(hideWaitUi);
+ if (no4G) {
+ if (typeof onWaiting === 'function') onWaiting();
+ else showWaitUi('请稍候…');
+ return waitForBleConnection()
+ .then((connected) => {
+ return connected ? doBle().catch(onBleSendFail) : go4GOrReject('蓝牙未连接');
+ })
+ .finally(hideWaitUi);
+ }
+ return waitForBleConnection(0, BLE_POLL_INTERVAL_MS).then((connected) =>
+ connected ? doBle().catch(onBleSendFail) : go4GOrReject('无蓝牙连接,走4G')
+ );
});
}
- // 有 bleDeviceId 但未连:系统蓝牙关则直接 4G,否则短时重连
+ // 有 bleDeviceId 但未连:用户刚关蓝牙/超出范围时,不再弹「请稍候」等重连 ~2s,双通道在线时直接 4G
return getBleAdapterAvailable().then((adapterOk) => {
if (!adapterOk) {
+ protocolInstance.setBleConnectionStatus(false, '');
return go4GOrReject('系统蓝牙未开启,走4G');
}
- if (typeof onWaiting !== 'function') showWaitUi('请稍候…');
- return tryReconnectBle()
- .then((reconnected) => {
- return reconnected ? doBle().catch(onBleSendFail) : go4GOrReject('蓝牙未连接');
- })
- .finally(hideWaitUi);
+ console.log('[100J]', logName || '指令', '蓝牙已断开,直接走4G(不阻塞重连)');
+ return go4GOrReject(null);
});
}
From d5269b02d03d0dfe1709aa91f33446b387b62de9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BE=AE=E5=BE=AE=E4=B8=80=E7=AC=91?= <709648985@qq.com>
Date: Mon, 30 Mar 2026 17:20:48 +0800
Subject: [PATCH 2/4] =?UTF-8?q?=E4=BC=98=E5=8C=96100J=E7=AE=A1=E6=8E=A7?=
=?UTF-8?q?=E9=A1=B5=E6=A0=B7=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pages/100J/HBY100-J.vue | 38 +++++++++++++++++++++++++++++---------
1 file changed, 29 insertions(+), 9 deletions(-)
diff --git a/pages/100J/HBY100-J.vue b/pages/100J/HBY100-J.vue
index 7e0df3d..d5cafbf 100644
--- a/pages/100J/HBY100-J.vue
+++ b/pages/100J/HBY100-J.vue
@@ -173,10 +173,10 @@
-
@@ -331,7 +331,7 @@
sta_VoiceType: '0',
volume: 10,
sta_LightType: '',
- strobeFrequency: 0.5,
+ strobeFrequency: 1,
lightBrightness: 10,
sta_system: '',
warnTime: 0,
@@ -552,8 +552,9 @@
// 设备按键, app同步
} else if (funcType == '14') {
// 调节亮度,音量,频率相关字段
- these.formData.strobeFrequency = led_strobe.frequency ||
- 0.5; //频率
+ these.formData.strobeFrequency = these.normalizeStrobeFreq(
+ led_strobe.frequency != null ? led_strobe.frequency : 1
+ );
these.formData.volume = volume || 10; //音量
these.formData.lightBrightness = brightness.red ||
10; //亮度值
@@ -693,6 +694,10 @@
this.$nextTick(() => this.sync100JBleUiFromHelper());
},
computed: {
+ /** 与 slider min/max(1~10) 对齐;离线未拉到详情时避免 0.5 等非法值导致滑块渲染飞出 */
+ strobeFrequencySlider() {
+ return this.normalizeStrobeFreq(this.formData.strobeFrequency);
+ },
getbleStatu() {
if (this.formData.bleStatu === true) {
return '已连接';
@@ -711,6 +716,15 @@
},
methods: {
+ /** 警示灯频率 UI:slider 为 1~10,与协议 0~12 取交集 */
+ normalizeStrobeFreq(v) {
+ const n = Number(v);
+ if (!Number.isFinite(n)) return 1;
+ const r = Math.round(n);
+ if (r < 1) return 1;
+ if (r > 10) return 10;
+ return r;
+ },
/** 与 BleHelper 实际连接状态对齐(系统关蓝牙再开、从后台回前台等) */
sync100JBleUiFromHelper() {
const mac = (this.device && this.device.deviceMac) || (this.deviceInfo && this.deviceInfo.deviceMac);
@@ -796,6 +810,7 @@
})
);
Object.assign(this.formData, validData);
+ that.formData.strobeFrequency = that.normalizeStrobeFreq(that.formData.strobeFrequency);
that.deviceInfo = res.data;
that.$nextTick(() => that.sync100JBleUiFromHelper && that.sync100JBleUiFromHelper());
const strobeEnable = res.data.strobeEnable ?? 0; // 0=关闭,1=开启
@@ -1417,15 +1432,17 @@
else if (this.formData.sta_VoiceType === '7') this.formData.sta_VoiceType = '-1';
}
if (parsedData.volume !== undefined) this.formData.volume = parsedData.volume;
- if (parsedData.strobeFrequency !== undefined) this.formData.strobeFrequency = parsedData
- .strobeFrequency;
+ if (parsedData.strobeFrequency !== undefined) {
+ this.formData.strobeFrequency = this.normalizeStrobeFreq(parsedData.strobeFrequency);
+ }
if (parsedData.redBrightness !== undefined) this.formData.lightBrightness = parsedData
.redBrightness;
}
// 0x09 音量、0x0D 亮度:单独响应时同步
if (fc === 0x09 && parsedData.volume !== undefined) this.formData.volume = parsedData.volume;
- if (fc === 0x0B && parsedData.strobeFrequency !== undefined) this.formData.strobeFrequency = parsedData
- .strobeFrequency;
+ if (fc === 0x0B && parsedData.strobeFrequency !== undefined) {
+ this.formData.strobeFrequency = this.normalizeStrobeFreq(parsedData.strobeFrequency);
+ }
if (fc === 0x0D && parsedData.redBrightness !== undefined) this.formData.lightBrightness = parsedData
.redBrightness;
},
@@ -1932,6 +1949,9 @@
.slider-container {
padding: 0px;
+ width: 100%;
+ overflow: hidden;
+ box-sizing: border-box;
}
.addIco {
From fe49ff631d67e1a10181a6a7bc2ebb650e314963 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BE=AE=E5=BE=AE=E4=B8=80=E7=AC=91?= <709648985@qq.com>
Date: Mon, 30 Mar 2026 17:45:49 +0800
Subject: [PATCH 3/4] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BC=98=E5=8C=96100j?=
=?UTF-8?q?=E4=BD=93=E9=AA=8C=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
api/100J/HBY100-J.js | 6 +++++-
pages/100J/HBY100-J.vue | 3 +++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/api/100J/HBY100-J.js b/api/100J/HBY100-J.js
index a99706e..3369ebb 100644
--- a/api/100J/HBY100-J.js
+++ b/api/100J/HBY100-J.js
@@ -209,12 +209,16 @@ class HBY100JProtocol {
if (this._fileResponseResolve) this._fileResponseResolve(result);
break;
case 0x04:
- // 5.5 获取设备电源状态: 电池容量8B + 电压8B + 百分比1B + 车载电源1B + 续航时间2B(分钟)
+ // 5.5 获取设备电源状态: 容量8B + 电压8B + 百分比1B + 车载1B + 续航2B(分钟) + [充电状态1B] + FF
+ // 充电状态为固件新增,在续航之后;旧固件仅 20 字节 payload,不影响 [16..19] 字段
if (data.length >= 20) {
result.batteryPercentage = data[16];
result.vehiclePower = data[17];
result.batteryRemainingTime = data[18] | (data[19] << 8); // 小端序,单位分钟
}
+ if (data.length >= 21) {
+ result.chargingStatus = data[20]; // 0未充电 1充电中 2已充满
+ }
break;
case 0x06:
// 06: 语音播报响应
diff --git a/pages/100J/HBY100-J.vue b/pages/100J/HBY100-J.vue
index d5cafbf..a28483f 100644
--- a/pages/100J/HBY100-J.vue
+++ b/pages/100J/HBY100-J.vue
@@ -1383,6 +1383,9 @@
if (parsedData.batteryRemainingTime !== undefined) {
this.$set(this.deviceInfo, 'batteryRemainingTime', parsedData.batteryRemainingTime);
}
+ if (parsedData.chargingStatus !== undefined) {
+ this.$set(this.deviceInfo, 'chargingStatus', parsedData.chargingStatus);
+ }
From c64b1407547d638021f6e5063d17df6968e42519 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BE=AE=E5=BE=AE=E4=B8=80=E7=AC=91?= <709648985@qq.com>
Date: Tue, 31 Mar 2026 13:57:21 +0800
Subject: [PATCH 4/4] =?UTF-8?q?100J=E8=93=9D=E7=89=99=E8=AE=BE=E5=A4=87?=
=?UTF-8?q?=E4=B8=8A=E6=8A=A5=E9=97=AE=E9=A2=98=E8=A7=A3=E5=86=B3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
api/100J/HBY100-J.js | 30 +++++++++++++++++++-----------
utils/BleHelper.js | 22 ++++++----------------
utils/BleReceive.js | 3 ++-
3 files changed, 27 insertions(+), 28 deletions(-)
diff --git a/api/100J/HBY100-J.js b/api/100J/HBY100-J.js
index 3369ebb..76f4448 100644
--- a/api/100J/HBY100-J.js
+++ b/api/100J/HBY100-J.js
@@ -163,7 +163,12 @@ class HBY100JProtocol {
this.onNotifyCallback = callback;
}
- parseBleData(buffer) {
+ /**
+ * @param {Uint8Array|ArrayBuffer} buffer
+ * @param {{ skipSideEffects?: boolean }} [options] skipSideEffects=true:仅解析字段,不打日志、不触发 onNotify/文件回调(供 BleReceive 与设备页 bleValueNotify 双订阅时避免重复)
+ */
+ parseBleData(buffer, options = {}) {
+ const skipSideEffects = !!options.skipSideEffects;
const view = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
if (view.length < 3) return null;
@@ -175,8 +180,10 @@ class HBY100JProtocol {
const macBytes = view.slice(1, 7);
const macAddress = Array.from(macBytes).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(':');
const result = { type: 'mac', macAddress };
- console.log('[100J-蓝牙] 设备上报MAC:', macAddress, '原始:', Array.from(view).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' '));
- if (this.onNotifyCallback) this.onNotifyCallback(result);
+ if (!skipSideEffects) {
+ console.log('[100J-蓝牙] 设备上报MAC:', macAddress, '原始:', Array.from(view).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' '));
+ if (this.onNotifyCallback) this.onNotifyCallback(result);
+ }
return result;
}
@@ -206,7 +213,7 @@ class HBY100JProtocol {
// 05: 文件更新响应 FB 05 [fileType] [status] FF,status: 1=成功 2=失败
if (data.length >= 1) result.fileType = data[0];
if (data.length >= 2) result.fileStatus = data[1]; // 1=Success, 2=Failure
- if (this._fileResponseResolve) this._fileResponseResolve(result);
+ if (!skipSideEffects && this._fileResponseResolve) this._fileResponseResolve(result);
break;
case 0x04:
// 5.5 获取设备电源状态: 容量8B + 电压8B + 百分比1B + 车载1B + 续航2B(分钟) + [充电状态1B] + FF
@@ -265,10 +272,11 @@ class HBY100JProtocol {
const funcNames = { 0x01: '复位', 0x02: '基础信息', 0x03: '位置', 0x04: '电源状态', 0x05: '文件更新', 0x06: '语音播报', 0x09: '音量', 0x0A: '爆闪模式', 0x0B: '爆闪频率', 0x0C: '强制报警', 0x0D: 'LED亮度', 0x0E: '工作方式' };
const name = funcNames[funcCode] || ('0x' + funcCode.toString(16));
- console.log('[100J-蓝牙] 设备响应 FB:', name, '解析:', JSON.stringify(result), '原始:', hexStr);
-
- if (this.onNotifyCallback) {
- this.onNotifyCallback(result);
+ if (!skipSideEffects) {
+ console.log('[100J-蓝牙] 设备响应 FB:', name, '解析:', JSON.stringify(result), '原始:', hexStr);
+ if (this.onNotifyCallback) {
+ this.onNotifyCallback(result);
+ }
}
return result;
}
@@ -883,9 +891,9 @@ export function cache100JVoiceFileForBle(deviceId, voiceListId, filePath) {
});
}
-// 暴露给页面:解析蓝牙接收到的数据
-export function parseBleData(buffer) {
- return protocolInstance.parseBleData(buffer);
+// 暴露给页面:解析蓝牙接收到的数据(options 见类上 parseBleData 注释)
+export function parseBleData(buffer, options) {
+ return protocolInstance.parseBleData(buffer, options);
}
// 暴露给页面:蓝牙连接后主动拉取电源状态(电量、续航)
diff --git a/utils/BleHelper.js b/utils/BleHelper.js
index f8ed745..6795947 100644
--- a/utils/BleHelper.js
+++ b/utils/BleHelper.js
@@ -1728,28 +1728,18 @@ class BleHelper {
return linkDevice(deviceId);
}).then((res) => {
- if (res) { //新连接
+ if (res) { //新连接(含 createBLEConnection 刚成功)
// console.log("11111111");
if (fIndex == -1) {
// console.log("开始获取服务", targetServiceId)
return this.getService(deviceId, targetServiceId, writeCharId,
notifyCharId); //获取服务
} else {
- if (f.wirteCharactId && f.notifyCharactId) {
- if (!f.notifyState) {
- // console.log("开始订阅特征");
- this.subScribe(deviceId, true);
- } else {
- console.log("不订阅消息");
- }
- return Promise.resolve(true);
- } else {
- console.log("开始获取服务", targetServiceId)
- return this.getService(deviceId, targetServiceId, writeCharId,
- notifyCharId);
- }
-
-
+ // 设备已在 LinkedList(例如缓存/重连):不能仅用缓存的 write/notify UUID 直接订阅。
+ // 重连后须先 getBLEDeviceServices,否则部分机型 notifyBLECharacteristicValueChange 报 no service(10004),notify 收不到任何数据。
+ console.log("已缓存设备重新连接,重新发现服务并订阅", deviceId);
+ return this.getService(deviceId, targetServiceId, writeCharId,
+ notifyCharId);
}
} else { //已连接过,直接订阅消息
// console.log("11111111");
diff --git a/utils/BleReceive.js b/utils/BleReceive.js
index 4a62373..6667509 100644
--- a/utils/BleReceive.js
+++ b/utils/BleReceive.js
@@ -693,7 +693,8 @@ class BleReceive {
let receiveData = {};
try {
if (!receive.bytes || receive.bytes.length < 3) return receiveData;
- const parsed = parseBleData(receive.bytes);
+ // 与 HBY100-J 页 bleValueNotify 共用 notify,避免 parseBleData 执行两次:重复日志、FB05 双次 resolve、onNotify 双次
+ const parsed = parseBleData(receive.bytes, { skipSideEffects: true });
if (!parsed) return receiveData;
if (parsed.longitude !== undefined) receiveData.longitude = parsed.longitude;
if (parsed.latitude !== undefined) receiveData.latitude = parsed.latitude;