Debounce GATT events
This commit is contained in:
49
web/app.js
49
web/app.js
@@ -15,7 +15,31 @@ const config = { sensitivity:600, deadZone:0.06, accelStrength:0.08, curve:0, ax
|
||||
let device=null, server=null, chars={}, userDisconnected=false;
|
||||
let currentChargeStatus=0, currentBattPct=null;
|
||||
|
||||
// ── GATT write queue (prevents "operation already in progress") ───────────────
|
||||
let _gattQueue = Promise.resolve();
|
||||
function gattWrite(char, value) {
|
||||
const p = _gattQueue.then(() => char.writeValueWithResponse(value));
|
||||
_gattQueue = p.catch(() => {});
|
||||
return p;
|
||||
}
|
||||
function gattCmd(char, value) {
|
||||
const p = _gattQueue.then(() => char.writeValueWithoutResponse(value));
|
||||
_gattQueue = p.catch(() => {});
|
||||
return p;
|
||||
}
|
||||
|
||||
// ── Logging ──────────────────────────────────────────────────────────────────
|
||||
(function() {
|
||||
const _methods = { log: '', warn: 'warn', error: 'err' };
|
||||
for (const [method, type] of Object.entries(_methods)) {
|
||||
const orig = console[method].bind(console);
|
||||
console[method] = (...args) => {
|
||||
orig(...args);
|
||||
log(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' '), type);
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
function log(msg, type='') {
|
||||
const el=document.getElementById('console');
|
||||
const now=new Date();
|
||||
@@ -153,7 +177,12 @@ function applyConfigToUI() {
|
||||
document.getElementById('tapModGui').checked = !!(config.tapMod & 0x08);
|
||||
}
|
||||
|
||||
async function writeConfigBlob() {
|
||||
let _writeConfigTimer = null;
|
||||
function writeConfigBlob() {
|
||||
clearTimeout(_writeConfigTimer);
|
||||
_writeConfigTimer = setTimeout(_doWriteConfigBlob, 150);
|
||||
}
|
||||
async function _doWriteConfigBlob() {
|
||||
if (!chars.configBlob) return;
|
||||
|
||||
// Gather current UI values into the config shadow
|
||||
@@ -184,7 +213,7 @@ async function writeConfigBlob() {
|
||||
view.setUint8(19, 0);
|
||||
|
||||
try {
|
||||
await chars.configBlob.writeValueWithResponse(buf);
|
||||
await gattWrite(chars.configBlob, buf);
|
||||
log(`Config written — sens=${config.sensitivity.toFixed(0)} tapThr=${config.tapThreshold} tapAction=${config.tapAction}`,'ok');
|
||||
} catch(e) { log(`Config write failed: ${e.message}`,'err'); }
|
||||
}
|
||||
@@ -192,10 +221,10 @@ async function writeConfigBlob() {
|
||||
// ── Individual control handlers ───────────────────────────────────────────────
|
||||
// These update the local config shadow then write the full blob
|
||||
|
||||
async function setCurve(val) {
|
||||
function setCurve(val) {
|
||||
config.curve = val;
|
||||
setCurveUI(val);
|
||||
await writeConfigBlob();
|
||||
writeConfigBlob();
|
||||
log(`Curve → ${['LINEAR','SQUARE','SQRT'][val]}`,'ok');
|
||||
}
|
||||
function setCurveUI(val) {
|
||||
@@ -203,10 +232,10 @@ function setCurveUI(val) {
|
||||
document.getElementById(id).classList.toggle('active', i===val));
|
||||
}
|
||||
|
||||
async function setChargeMode(val) {
|
||||
function setChargeMode(val) {
|
||||
config.chargeMode = val;
|
||||
setChargeModeUI(val);
|
||||
await writeConfigBlob();
|
||||
writeConfigBlob();
|
||||
log(`Charge → ${['OFF','SLOW 50mA','FAST 100mA'][val]}`,'warn');
|
||||
}
|
||||
function setChargeModeUI(val) {
|
||||
@@ -218,10 +247,10 @@ function setChargeModeUI(val) {
|
||||
document.getElementById('ciMode').textContent = ['Off (0mA)','50 mA','100 mA'][val] ?? '--';
|
||||
}
|
||||
|
||||
async function setTapAction(val) {
|
||||
function setTapAction(val) {
|
||||
config.tapAction = val;
|
||||
setTapActionUI(val);
|
||||
await writeConfigBlob();
|
||||
writeConfigBlob();
|
||||
}
|
||||
function setTapActionUI(val) {
|
||||
['tapActLeft','tapActRight','tapActMiddle','tapActKey'].forEach((id,i) =>
|
||||
@@ -237,7 +266,7 @@ function onTapKeyInput() {
|
||||
|
||||
async function sendCalibrate() {
|
||||
if (!chars.command) return;
|
||||
try { await chars.command.writeValueWithoutResponse(new Uint8Array([0x01])); log('Calibration sent — hold still!','warn'); }
|
||||
try { await gattCmd(chars.command, new Uint8Array([0x01])); log('Calibration sent — hold still!','warn'); }
|
||||
catch(e) { log(`Calibrate failed: ${e.message}`,'err'); }
|
||||
}
|
||||
function confirmReset() { document.getElementById('overlay').classList.add('show'); }
|
||||
@@ -245,7 +274,7 @@ function closeModal() { document.getElementById('overlay').classList.remove('s
|
||||
async function doReset() {
|
||||
closeModal(); if (!chars.command) return;
|
||||
try {
|
||||
await chars.command.writeValueWithoutResponse(new Uint8Array([0xFF]));
|
||||
await gattCmd(chars.command, new Uint8Array([0xFF]));
|
||||
log('Factory reset sent…','warn');
|
||||
setTimeout(async () => { await readConfigBlob(); log('Config reloaded','ok'); }, 1500);
|
||||
} catch(e) { log(`Reset failed: ${e.message}`,'err'); }
|
||||
|
||||
Reference in New Issue
Block a user