{"_id":"@danidoble/webserial-board2droid","name":"@danidoble/webserial-board2droid","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@danidoble/webserial-board2droid","type":"module","version":"1.0.0","description":"A strongly-typed, event-driven USB board2droid driver for the Web Serial API, built on top of webserial-core.","author":{"name":"Danidoble","email":"ddanidoble@gmail.com"},"license":"GPL-3.0-only","homepage":"https://github.com/danidoble/webserial-board2droid#readme","repository":{"type":"git","url":"git+https://github.com/danidoble/webserial-board2droid.git"},"bugs":{"url":"https://github.com/danidoble/webserial-board2droid/issues"},"exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./package.json":"./package.json"},"devDependencies":{"@eslint/js":"^10.0.1","@typescript/native-preview":"7.0.0-dev.20260328.1","bumpp":"^11.0.1","eslint":"^10.2.1","globals":"^17.5.0","prettier":"3.8.3","tsdown":"^0.21.10","typescript":"^6.0.3","typescript-eslint":"^8.59.0","vitest":"^4.1.5","webserial-core":"^2.1.0"},"peerDependencies":{"webserial-core":"^2.1.0"},"main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.cts","scripts":{"build":"tsdown","dev":"prettier --write ./src/**/*.ts && tsdown --watch","test":"vitest","typecheck":"tsc --noEmit","release":"bumpp","lint":"eslint ./src/**/*.ts ./tests/**/*.ts","format":"prettier --write ./src/ ./tests/ ./README.md ./package.json"},"_id":"@danidoble/webserial-board2droid@1.0.0","_integrity":"sha512-w0cMBMyaOnJXw0kphb6CDrDciEx0ENt860CDn4n0IvCVfUUc++WX0+GYfmek6DVKs6HujxDW2Ov9Q62JOs+jJA==","_resolved":"/tmp/a0fb726c35c7d9213b7ef3f91c7a43c5/danidoble-webserial-board2droid-1.0.0.tgz","_from":"file:danidoble-webserial-board2droid-1.0.0.tgz","_nodeVersion":"24.12.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-w0cMBMyaOnJXw0kphb6CDrDciEx0ENt860CDn4n0IvCVfUUc++WX0+GYfmek6DVKs6HujxDW2Ov9Q62JOs+jJA==","shasum":"414d87250f19b81e2df0a2efebd66258bca3d2b2","tarball":"https://registry.npmjs.org/@danidoble/webserial-board2droid/-/webserial-board2droid-1.0.0.tgz","fileCount":7,"unpackedSize":101660,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC7SPF+3kHnnsJiFFFzLk+IYVc4B6/CfBqBdJ3fEX3t9QIgUGMnVMJ9Z8LEup3eXnLCPmG3s11VrSeq+2JKWUOyYh8="}]},"_npmUser":{"name":"danidoble","email":"ddanidoble@gmail.com"},"directories":{},"maintainers":[{"name":"danidoble","email":"ddanidoble@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/webserial-board2droid_1.0.0_1777237028579_0.137669733164814"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-26T20:57:08.450Z","1.0.0":"2026-04-26T20:57:08.732Z","modified":"2026-04-26T20:57:08.954Z"},"maintainers":[{"name":"danidoble","email":"ddanidoble@gmail.com"}],"description":"A strongly-typed, event-driven USB board2droid driver for the Web Serial API, built on top of webserial-core.","homepage":"https://github.com/danidoble/webserial-board2droid#readme","repository":{"type":"git","url":"git+https://github.com/danidoble/webserial-board2droid.git"},"author":{"name":"Danidoble","email":"ddanidoble@gmail.com"},"bugs":{"url":"https://github.com/danidoble/webserial-board2droid/issues"},"license":"GPL-3.0-only","readme":"# @danidoble/webserial-board2droid\n\nTypeScript driver for **Board2Droid** vending-machine controllers, built on top of [`webserial-core`](https://github.com/danidoble/webserial-core) v2.\n\n## Installation\n\n```sh\nnpm install @danidoble/webserial-board2droid webserial-core\n# or\npnpm add @danidoble/webserial-board2droid webserial-core\n```\n\n`webserial-core` is a **peer dependency** — you must install it alongside this package.\n\n## Quick start\n\n```ts\nimport { Board2Droid } from '@danidoble/webserial-board2droid';\n\nconst board = new Board2Droid({ filters: [{ usbVendorId: 0x1a86 }] });\n\nboard.on('b2d:pong', ({ firmware, deviceNumber }) =>\n  console.log('Board connected — firmware:', firmware, '— device:', deviceNumber),\n);\n\nawait board.connect();  // opens the serial port and runs handshake\nawait board.sendPing(); // optional extra ping\n```\n\n## Protocol\n\nBinary, fixed **16-byte** frames.\n\n```\n[SOF=0xF1][CMD][D0..D11][EOF=0xF2][CHK]\n```\n\n- `CHK = (CMD + D0 + … + D11 + EOF) & 0xFF`\n- `D11` = target device number (0 = broadcast)\n- Multi-board addressing over RS-232 bus\n\n## Constructor options (`Board2DroidOptions`)\n\n| Option            | Type                    | Description                                      |\n|-------------------|-------------------------|--------------------------------------------------|\n| `filters`         | `SerialPortFilter[]`    | USB vendor/product filters for `requestPort()`   |\n| `provider`        | `SerialProvider`        | Custom transport (WebUSB, BT, WebSocket, …)       |\n| `polyfillOptions` | `SerialPolyfillOptions` | Extra options forwarded to the core polyfill      |\n\nDefaults: `baudRate=115200`, `8N1`, `bufferSize=512`, `commandTimeout=3000 ms`, `autoReconnect=true`.\n\n## Events (incoming)\n\nListen with `board.on(event, handler)`.\n\n| Event                      | Payload type          | Fires when…                                           |\n|----------------------------|-----------------------|-------------------------------------------------------|\n| `b2d:pong`                | `PongEvent`              | Board replies to PING — carries `firmware` + `deviceNumber` |\n| `b2d:coin-in`             | `CoinInEvent`            | A coin was inserted (`raw`, `type`, `centavos`)       |\n| `b2d:bill-in`             | `BillInEvent`            | Bill activity — discriminated by `kind` (`'bill'` or `'status'`) |\n| `b2d:coin-config`         | —                        | Coin changer config accepted                         |\n| `b2d:bill-config`         | `BillConfigEvent`        | Bill acceptor denomination config (`config: number[]`) |\n| `b2d:tubes`               | `TubesEvent`             | Coin tube fill levels (`counts: number[]`, `fullMask`) |\n| `b2d:bill-channels`       | `BillChannelsEvent`      | Bill channel config read from EEPROM                 |\n| `b2d:coins-out`           | —                        | Coin dispense cycle completed                        |\n| `b2d:product`             | `ProductEvent`           | Motor dispense finished (`success: boolean`)         |\n| `b2d:door`                | `DoorEvent`              | Cabinet door status changed (`open: boolean`)        |\n| `b2d:temperature`         | `TemperatureEvent`       | ADC temperature reading (`adcRaw: number`)           |\n| `b2d:relay`               | `RelayEvent`             | Relay acknowledgement (`relay`, `state`)             |\n| `b2d:save-memory`         | —                        | EEPROM write completed                               |\n| `b2d:read-memory`         | `ReadMemoryEvent`        | EEPROM read result (`addr`, `value`)                 |\n| `b2d:cashless`            | `CashlessEvent`          | MDB cashless sub-event (`subEvent`, `data`)          |\n| `b2d:motor-status`        | `MotorStatusEvent`       | Motor/slot connectivity (`selector`, `connected`)    |\n| `b2d:device-number`       | `DeviceNumberEvent`      | Device number confirmed after assignment             |\n| `b2d:hopper`              | `HopperEvent`            | Hopper result code + optional data                   |\n| `b2d:temperature-report`  | `TemperatureReportEvent` | Periodic auto-temp report (`adcRaw`, `relayState`, `autoActive`) |\n| `b2d:unknown`             | `UnknownFrameEvent`      | Frame arrived but EVT code not recognised (`frame`)  |\n\n### `BillInEvent` discriminated union\n\n```ts\nboard.on('b2d:bill-in', (evt) => {\n  if (evt.kind === 'bill') {\n    console.log(evt.routing, evt.billType, evt.centavos);\n  } else {\n    console.log('Bill status:', evt.statusCode);\n  }\n});\n```\n\n## Commands (outgoing)\n\nAll commands accept an optional `device` parameter (default `0` = broadcast).\n\n### General\n\n| Method | Parameters | Description |\n|--------|-----------|-------------|\n| `sendPing(device?)` | — | Ping the board — fires `b2d:pong` |\n| `sendSetupBill(device?)` | — | Re-initialise bill acceptor/recycler |\n| `sendSetupCoin(device?)` | — | Re-initialise coin changer |\n| `sendReadTubes(device?)` | — | Request tube fill levels → `b2d:tubes` |\n| `sendReadBillChannels(device?)` | — | Read bill denom config from EEPROM → `b2d:bill-channels` |\n| `sendEscrow(accept, device?)` | `accept: boolean` | Stack (`true`) or return (`false`) escrowed bill |\n| `sendDispenseBills(count0, count1, device?)` | counts | Dispense bills from recycler |\n| `sendDispenseCoins(counts, device?)` | `counts: number[]` (up to 6) | Dispense coins by denomination |\n| `sendProductOut(sel1, sel2?, mode?, device?)` | selector | Activate product motor |\n| `sendSaveMemory(addr, value, device?)` | 16-bit addr, byte | Write byte to EEPROM → `b2d:save-memory` |\n| `sendReadMemory(addr, device?)` | 16-bit addr | Read byte from EEPROM → `b2d:read-memory` |\n| `sendLed(state, device?)` | `0`=off, `1`=on, `2`=pulse | Control product LED |\n| `sendReadTemperature(device?)` | — | Request ADC temperature → `b2d:temperature` |\n| `sendRelay(relay, state, device?)` | relay number, `0`/`1` | Control relay output → `b2d:relay` |\n| `sendMotorStatus(selector, device?)` | motor selector | Check slot connectivity → `b2d:motor-status` |\n| `sendSetDeviceNumber(newNum, currentDevice?)` | new number | Assign device number → `b2d:device-number` |\n| `sendTemperatureConfig(options, device?)` | `TemperatureConfigOptions` | Configure auto-temperature control |\n\n#### `TemperatureConfigOptions`\n\n```ts\ninterface TemperatureConfigOptions {\n  enable: boolean;   // enable auto-relay control\n  adcHigh: number;   // ADC threshold to activate relay\n  adcLow: number;    // ADC threshold to deactivate relay\n  relay: number;     // relay number to control\n  interval: number;  // report interval in seconds\n}\n```\n\n### Cashless (MDB)\n\n| Method | Parameters | Description |\n|--------|-----------|-------------|\n| `sendCashlessEnable(device?)` | — | Enable cashless reader |\n| `sendCashlessDisable(device?)` | — | Disable cashless reader |\n| `sendCashlessSetPrice(price, sel, device?)` | centavos, selection | Pre-configure price |\n| `sendCashlessVendRequest(price, sel, device?)` | centavos, selection | MDB VEND REQUEST |\n| `sendCashlessVendSuccess(sel, device?)` | selection | MDB VEND SUCCESS |\n| `sendCashlessVendFailure(device?)` | — | MDB VEND FAILURE |\n| `sendCashlessVendCancel(device?)` | — | Cancel pending vend |\n| `sendCashlessSessionComplete(device?)` | — | MDB SESSION COMPLETE |\n| `sendCashlessCashSale(price, sel, device?)` | centavos, selection | Log equivalent cash sale |\n| `sendCashlessRevalueRequest(amount, device?)` | centavos | Request card revalue (L2+) |\n| `sendCashlessRevalueLimit(device?)` | — | Query max revalue amount |\n| `sendCashlessTimeDateResponse(date?, device?)` | `Date` | Reply to time/date request (L2+) |\n| `sendCashlessDataEntryResponse(data, device?)` | `number[]` | Reply to data-entry/PIN request (L2+) |\n\n### Hopper\n\n| Method | Parameters | Description |\n|--------|-----------|-------------|\n| `sendHopperInit(device?)` | — | Initialise hopper → `b2d:hopper` |\n| `sendHopperStatus(device?)` | — | Query tube fill levels |\n| `sendHopperDispense(count, tubeType, device?)` | count, type | Dispense N coins of one denomination |\n| `sendHopperPayout(amounts, device?)` | `amounts: number[]` (up to 8) | Multi-denomination payout |\n| `sendHopperStop(device?)` | — | Emergency stop |\n\n## Protocol constants\n\nAll constant maps are exported for direct use:\n\n```ts\nimport { CMD, EVT, CL_CMD, CL_EVT, HOPPER_ACT, HOPPER_EVT, BILL_ROUTING, BILL_STATUS, LED_STATE, MOTOR_MODE } from '@danidoble/webserial-board2droid';\n```\n\n## Multi-board addressing\n\nSet the `device` parameter on any command to target a specific board over the RS-232 bus. `0` broadcasts to all boards.\n\n```ts\nawait board.sendPing(2);        // ping board #2 only\nawait board.sendRelay(1, 1, 3); // turn relay 1 on — board #3 only\n```\n\n## TypeScript\n\nAll events and method signatures are fully typed. The package ships with `.d.mts` / `.d.cts` declaration files. Commonly used types and all built-in providers are re-exported:\n\n```ts\nimport {\n  Board2Droid,\n  WebUsbProvider,\n  createBluetoothProvider,\n  createWebSocketProvider,\n} from '@danidoble/webserial-board2droid';\n\nimport type {\n  Board2DroidOptions,\n  PongEvent,\n  CoinInEvent,\n  BillInEvent,\n  BillConfigEvent,\n  BillChannelsEvent,\n  ReadMemoryEvent,\n  DeviceNumberEvent,\n  TemperatureReportEvent,\n  TemperatureConfigOptions,\n  SerialPortFilter,\n  SerialProvider,\n} from '@danidoble/webserial-board2droid';\n```\n\n## License\n\n[GPL-3.0-only](./LICENSE.md) © Danidoble\n\n","readmeFilename":"README.md","_rev":"1-174297e7c97a46e95f1f442c9c4a10d4"}