pliceclogs-og/rfid-daemon/recover.js
type-two e5b8504bc6 Import pliceclogs as-is — the original Discogs seller extension
Snapshot of the working tree exactly as it stood, no edits. This is the predecessor
PRICEGOD was rewritten from ("kept intact, untouched" per pricegod/README.md), and it
is still the only place the DYMO scale is actually implemented —
rfid-daemon/index.js:1068-1265: HID discovery, parseScaleReport, one-shot read, and a
streaming /weight + /scale/start|stop session whose JSON shape PRICEGOD's daemon.js
already speaks.

Preserved verbatim on purpose (hence -og), including the known bug: DYMO_PIDS at
index.js:1082 is [0x8003, 0x8004], so it cannot see the bench M25 (0x8009). Fix that
in whatever daemon inherits the scale, not here.

node_modules stays ignored (22M of the 24M tree). No credentials in the import: the
two PEM markers in utils.js/sheets.js only strip headers off a key read from settings,
and mrpadmin / johnking are an SSH and a Postgres username, both key/trust auth.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 13:47:09 +10:00

166 lines
5.8 KiB
JavaScript

'use strict';
/**
* H102 recovery script.
* Sends RFM_SET_GET_READMODE to switch back to RFID mode.
* Also tries factory-reset (RFM_REBOOT) if needed.
* Run: node recover.js
*/
const { SerialPort } = require('serialport');
const PORT = process.env.RFID_PORT || '/dev/cu.usbserial-4130';
const BAUDS = [115200, 9600, 19200, 38400, 57600];
function crc16(buf) {
let crc = 0xFFFF;
for (const b of buf) {
crc ^= b;
for (let i = 0; i < 8; i++) crc = (crc & 1) ? ((crc >> 1) ^ 0x8408) : (crc >> 1);
}
return crc;
}
function frame(cmd, data = Buffer.alloc(0), addr = 0xFF) {
const hdr = Buffer.from([0xCF, addr, cmd[0], cmd[1], data.length]);
const body = Buffer.concat([hdr, data]);
const chk = crc16(body);
return Buffer.concat([body, Buffer.from([(chk >> 8) & 0xFF, chk & 0xFF])]);
}
const hex = b => Buffer.from(b).toString('hex').replace(/../g, '$& ').trim().toUpperCase();
// Commands
const CMD_GET_PARAM = [0x00, 0x72];
const CMD_REBOOT = [0x00, 0x52];
const CMD_READMODE = [0x00, 0x8E];
// RFM_SET_GET_READMODE: set RFID mode (0x00 = RFID, 0x01 = barcode/QR)
// SET format: OPTION(0x01=set) + READMODE(1) + RECEV(7 reserved zeros)
const SET_RFID_MODE = frame(CMD_READMODE, Buffer.from([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
const GET_READMODE = frame(CMD_READMODE, Buffer.from([0x02])); // OPTION=0x02 = read
const FACTORY_RESET = frame(CMD_REBOOT, Buffer.alloc(0));
const GET_PARAM = frame(CMD_GET_PARAM, Buffer.alloc(0));
function tryAtBaud(baud, targetPort) {
return new Promise(resolve => {
console.log(`\n── Trying ${baud} baud on ${targetPort} ──`);
const port = new SerialPort({ path: targetPort, baudRate: baud, autoOpen: false });
let resolved = false;
port.open(err => {
if (err) {
console.log(` open failed: ${err.message}`);
return resolve(false);
}
let rxBuf = Buffer.alloc(0);
port.on('data', chunk => {
rxBuf = Buffer.concat([rxBuf, chunk]);
console.log(` RX: ${hex(chunk)}`);
});
const done = ok => {
if (resolved) return;
resolved = true;
setTimeout(() => port.close(() => resolve(ok)), 200);
};
// Step 1: GET_ALL_PARAM — checks if device responds to serial at all
console.log(` TX GET_PARAM: ${hex(GET_PARAM)}`);
port.write(GET_PARAM);
setTimeout(() => {
if (rxBuf.length === 0) {
console.log(` No response at ${baud} baud — trying next`);
return done(false);
}
console.log(`\n ✓ Device responding at ${baud} baud!`);
console.log(` Full RX so far: ${hex(rxBuf)}`);
rxBuf = Buffer.alloc(0);
// Step 2: GET current READMODE
console.log(`\n TX GET_READMODE: ${hex(GET_READMODE)}`);
port.write(GET_READMODE);
setTimeout(() => {
console.log(` READMODE response: ${hex(rxBuf)}`);
// rxBuf[5]=STATUS, rxBuf[6]=Option, rxBuf[7]=READMODE value
const readModeVal = rxBuf[7];
if (readModeVal === 0x01) {
console.log('\n !! Device is in BARCODE/QR mode (READMODE=0x01).');
console.log(' Switching back to RFID mode...');
} else {
console.log(`\n READMODE = 0x${(readModeVal||0).toString(16)} — may already be RFID mode, sending SET anyway`);
}
rxBuf = Buffer.alloc(0);
// Step 3: SET RFID mode
console.log(`\n TX SET_RFID_MODE: ${hex(SET_RFID_MODE)}`);
port.write(SET_RFID_MODE);
setTimeout(() => {
console.log(` SET_RFID_MODE response: ${hex(rxBuf)}`);
const setStatus = rxBuf[5];
if (setStatus === 0x00) {
console.log('\n ✓ RFID mode restored! Unplug and replug the H102, then restart the daemon.');
} else {
console.log(`\n SET_RFID_MODE returned status 0x${(setStatus||0).toString(16)}`);
console.log(' Trying factory reset...');
rxBuf = Buffer.alloc(0);
console.log(`\n TX FACTORY_RESET: ${hex(FACTORY_RESET)}`);
port.write(FACTORY_RESET);
setTimeout(() => {
console.log(` FACTORY_RESET response: ${hex(rxBuf)}`);
console.log('\n Factory reset sent. Unplug and replug the H102.');
done(true);
}, 2000);
return;
}
done(true);
}, 2000);
}, 1500);
}, 2000);
});
port.on('error', err => {
console.log(` port error: ${err.message}`);
if (!resolved) done(false);
});
});
}
async function main() {
// Check if port exists
const ports = await SerialPort.list();
console.log('Available ports:');
ports.forEach(p => console.log(` ${p.path} (${p.manufacturer || 'unknown'})`));
const usbPort = ports.find(p => /usbserial|usbmodem|ttyUSB|ttyACM/i.test(p.path));
const targetPort = process.env.RFID_PORT || (usbPort ? usbPort.path.replace('/dev/tty.', '/dev/cu.') : PORT);
console.log(`\nTargeting: ${targetPort}`);
for (const baud of BAUDS) {
const ok = await tryAtBaud(baud, targetPort);
if (ok) return;
}
console.log('\n━━━ No serial response at any baud rate ━━━');
console.log('The device may have switched to USB-HID keyboard mode entirely.');
console.log('\nHardware factory reset options for H102:');
console.log(' 1. Hold the scan TRIGGER button for 8-10 seconds while powered on');
console.log(' 2. Hold POWER + TRIGGER simultaneously for 5 seconds');
console.log(' 3. Check the back/bottom for a reset pinhole (use a paperclip)');
console.log(' 4. Connect via BLE: power cycle the device, then within 30 seconds');
console.log(' open the CfTech app → "BLE" → scan — the device only advertises');
console.log(' for ~30 seconds after power-on');
}
main().catch(console.error);