Debounce GATT events
This commit is contained in:
+39
-10
@@ -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 device=null, server=null, chars={}, userDisconnected=false;
|
||||||
let currentChargeStatus=0, currentBattPct=null;
|
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 ──────────────────────────────────────────────────────────────────
|
// ── 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='') {
|
function log(msg, type='') {
|
||||||
const el=document.getElementById('console');
|
const el=document.getElementById('console');
|
||||||
const now=new Date();
|
const now=new Date();
|
||||||
@@ -153,7 +177,12 @@ function applyConfigToUI() {
|
|||||||
document.getElementById('tapModGui').checked = !!(config.tapMod & 0x08);
|
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;
|
if (!chars.configBlob) return;
|
||||||
|
|
||||||
// Gather current UI values into the config shadow
|
// Gather current UI values into the config shadow
|
||||||
@@ -184,7 +213,7 @@ async function writeConfigBlob() {
|
|||||||
view.setUint8(19, 0);
|
view.setUint8(19, 0);
|
||||||
|
|
||||||
try {
|
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');
|
log(`Config written — sens=${config.sensitivity.toFixed(0)} tapThr=${config.tapThreshold} tapAction=${config.tapAction}`,'ok');
|
||||||
} catch(e) { log(`Config write failed: ${e.message}`,'err'); }
|
} catch(e) { log(`Config write failed: ${e.message}`,'err'); }
|
||||||
}
|
}
|
||||||
@@ -192,10 +221,10 @@ async function writeConfigBlob() {
|
|||||||
// ── Individual control handlers ───────────────────────────────────────────────
|
// ── Individual control handlers ───────────────────────────────────────────────
|
||||||
// These update the local config shadow then write the full blob
|
// These update the local config shadow then write the full blob
|
||||||
|
|
||||||
async function setCurve(val) {
|
function setCurve(val) {
|
||||||
config.curve = val;
|
config.curve = val;
|
||||||
setCurveUI(val);
|
setCurveUI(val);
|
||||||
await writeConfigBlob();
|
writeConfigBlob();
|
||||||
log(`Curve → ${['LINEAR','SQUARE','SQRT'][val]}`,'ok');
|
log(`Curve → ${['LINEAR','SQUARE','SQRT'][val]}`,'ok');
|
||||||
}
|
}
|
||||||
function setCurveUI(val) {
|
function setCurveUI(val) {
|
||||||
@@ -203,10 +232,10 @@ function setCurveUI(val) {
|
|||||||
document.getElementById(id).classList.toggle('active', i===val));
|
document.getElementById(id).classList.toggle('active', i===val));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setChargeMode(val) {
|
function setChargeMode(val) {
|
||||||
config.chargeMode = val;
|
config.chargeMode = val;
|
||||||
setChargeModeUI(val);
|
setChargeModeUI(val);
|
||||||
await writeConfigBlob();
|
writeConfigBlob();
|
||||||
log(`Charge → ${['OFF','SLOW 50mA','FAST 100mA'][val]}`,'warn');
|
log(`Charge → ${['OFF','SLOW 50mA','FAST 100mA'][val]}`,'warn');
|
||||||
}
|
}
|
||||||
function setChargeModeUI(val) {
|
function setChargeModeUI(val) {
|
||||||
@@ -218,10 +247,10 @@ function setChargeModeUI(val) {
|
|||||||
document.getElementById('ciMode').textContent = ['Off (0mA)','50 mA','100 mA'][val] ?? '--';
|
document.getElementById('ciMode').textContent = ['Off (0mA)','50 mA','100 mA'][val] ?? '--';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setTapAction(val) {
|
function setTapAction(val) {
|
||||||
config.tapAction = val;
|
config.tapAction = val;
|
||||||
setTapActionUI(val);
|
setTapActionUI(val);
|
||||||
await writeConfigBlob();
|
writeConfigBlob();
|
||||||
}
|
}
|
||||||
function setTapActionUI(val) {
|
function setTapActionUI(val) {
|
||||||
['tapActLeft','tapActRight','tapActMiddle','tapActKey'].forEach((id,i) =>
|
['tapActLeft','tapActRight','tapActMiddle','tapActKey'].forEach((id,i) =>
|
||||||
@@ -237,7 +266,7 @@ function onTapKeyInput() {
|
|||||||
|
|
||||||
async function sendCalibrate() {
|
async function sendCalibrate() {
|
||||||
if (!chars.command) return;
|
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'); }
|
catch(e) { log(`Calibrate failed: ${e.message}`,'err'); }
|
||||||
}
|
}
|
||||||
function confirmReset() { document.getElementById('overlay').classList.add('show'); }
|
function confirmReset() { document.getElementById('overlay').classList.add('show'); }
|
||||||
@@ -245,7 +274,7 @@ function closeModal() { document.getElementById('overlay').classList.remove('s
|
|||||||
async function doReset() {
|
async function doReset() {
|
||||||
closeModal(); if (!chars.command) return;
|
closeModal(); if (!chars.command) return;
|
||||||
try {
|
try {
|
||||||
await chars.command.writeValueWithoutResponse(new Uint8Array([0xFF]));
|
await gattCmd(chars.command, new Uint8Array([0xFF]));
|
||||||
log('Factory reset sent…','warn');
|
log('Factory reset sent…','warn');
|
||||||
setTimeout(async () => { await readConfigBlob(); log('Config reloaded','ok'); }, 1500);
|
setTimeout(async () => { await readConfigBlob(); log('Config reloaded','ok'); }, 1500);
|
||||||
} catch(e) { log(`Reset failed: ${e.message}`,'err'); }
|
} catch(e) { log(`Reset failed: ${e.message}`,'err'); }
|
||||||
|
|||||||
Reference in New Issue
Block a user