feat: Switch to noble fork

This commit is contained in:
2026-02-04 15:27:27 +07:00
parent 1dce024b1a
commit 12a20f6213
5 changed files with 621 additions and 988 deletions
+1 -2
View File
@@ -22,7 +22,7 @@
"author": "Yuzu <yuzu@kirameki.cafe>", "author": "Yuzu <yuzu@kirameki.cafe>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@abandonware/noble": "^1.9.2-24", "@stoprocent/noble": "^2.3.10",
"commander": "^14.0.3", "commander": "^14.0.3",
"ink": "^6.6.0", "ink": "^6.6.0",
"ink-big-text": "^2.0.0", "ink-big-text": "^2.0.0",
@@ -32,7 +32,6 @@
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.0.0", "@types/node": "^22.0.0",
"@types/noble": "^0.0.44",
"@types/react": "^19.2.10", "@types/react": "^19.2.10",
"tsup": "^8.3.5", "tsup": "^8.3.5",
"tsx": "^4.19.2", "tsx": "^4.19.2",
+554 -983
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -1,3 +1,8 @@
ignoredBuiltDependencies:
- '@serialport/bindings-cpp'
- '@stoprocent/bluetooth-hci-socket'
- '@stoprocent/noble'
onlyBuiltDependencies: onlyBuiltDependencies:
- '@abandonware/bluetooth-hci-socket' - '@abandonware/bluetooth-hci-socket'
- '@abandonware/noble' - '@abandonware/noble'
+33 -3
View File
@@ -1,8 +1,8 @@
import { EventEmitter } from "node:events"; import { EventEmitter } from "node:events";
EventEmitter.defaultMaxListeners = 20; EventEmitter.defaultMaxListeners = 20;
import noble from "@abandonware/noble"; import noble from "@stoprocent/noble";
import type { Peripheral, Characteristic } from "@abandonware/noble"; import type { Peripheral, Characteristic } from "@stoprocent/noble";
import type { BLEConfig, ProtocolConfig } from "../protocol/index.ts"; import type { BLEConfig, ProtocolConfig } from "../protocol/index.ts";
import { PacketType } from "../protocol/index.ts"; import { PacketType } from "../protocol/index.ts";
import { DeviceNotFoundError, ConnectionError } from "../utils/errors.ts"; import { DeviceNotFoundError, ConnectionError } from "../utils/errors.ts";
@@ -297,6 +297,12 @@ export class BleUploader {
return match[1]; // Return short UUID part return match[1]; // Return short UUID part
} }
// For short UUIDs (3-4 hex chars), pad with zeros to 4 chars for consistent comparison
// This handles cases like "1f1" vs "01f1"
if (cleaned.length <= 4) {
return cleaned.padStart(4, "0");
}
return cleaned; return cleaned;
} }
@@ -425,10 +431,34 @@ export class BleUploader {
// Stop scanning before connecting // Stop scanning before connecting
await noble.stopScanningAsync().catch(() => {}); await noble.stopScanningAsync().catch(() => {});
// Check peripheral state before connecting
logger.debug(
`Peripheral state before connect: ${this.peripheral!.state}`,
);
// If already connected, disconnect first to ensure clean state
if (
this.peripheral!.state === "connected" ||
this.peripheral!.state === "connecting"
) {
logger.debug(
"Device already connected/connecting, disconnecting first...",
);
await this.peripheral!.disconnectAsync().catch(() => {});
await this.sleep(500); // Brief delay after disconnect
}
// Connect to device // Connect to device
logger.info("Connecting to device...", LogEventType.CONNECT_START); logger.info("Connecting to device...", LogEventType.CONNECT_START);
const connectStartTime = Date.now();
await this.peripheral!.connectAsync(); await this.peripheral!.connectAsync();
logger.info("Connected to device", LogEventType.CONNECTED);
const connectDuration = Date.now() - connectStartTime;
logger.info(
`Connected to device (took ${connectDuration}ms)`,
LogEventType.CONNECTED,
);
// Handle disconnect events // Handle disconnect events
this.peripheral!.once("disconnect", () => { this.peripheral!.once("disconnect", () => {
+28
View File
@@ -32,6 +32,7 @@ export class BeamBoxUploader {
private imageProcessor: ImageProcessor; private imageProcessor: ImageProcessor;
private payloadBuilder: PayloadBuilder; private payloadBuilder: PayloadBuilder;
private imageConfig: ImageConfig; private imageConfig: ImageConfig;
private verbose: boolean = false;
constructor( constructor(
deviceAddress?: string, deviceAddress?: string,
@@ -41,6 +42,7 @@ export class BeamBoxUploader {
imageConfig?: ImageConfig, imageConfig?: ImageConfig,
verbose: boolean = false, verbose: boolean = false,
) { ) {
this.verbose = verbose;
this.imageConfig = imageConfig ?? DEFAULT_IMAGE_CONFIG; this.imageConfig = imageConfig ?? DEFAULT_IMAGE_CONFIG;
this.ble = new BleUploader( this.ble = new BleUploader(
deviceAddress ?? null, deviceAddress ?? null,
@@ -315,20 +317,46 @@ export class BeamBoxUploader {
// Wait for at least one status notification (PacketType.DEVICE_STATUS) // Wait for at least one status notification (PacketType.DEVICE_STATUS)
const startTime = Date.now(); const startTime = Date.now();
const checkInterval = 100; const checkInterval = 100;
let statusReceived = false;
let lastNotificationCount = 0;
while (Date.now() - startTime < timeoutMs) { while (Date.now() - startTime < timeoutMs) {
const deviceStatus = this.ble.getDeviceStatus(); const deviceStatus = this.ble.getDeviceStatus();
const notifications = this.ble.getNotifications();
// Log progress if we got new notifications
if (notifications.length > lastNotificationCount) {
logger.debug(`Received ${notifications.length} total notifications so far...`);
lastNotificationCount = notifications.length;
}
if (deviceStatus && deviceStatus.type === 13) { if (deviceStatus && deviceStatus.type === 13) {
logger.info( logger.info(
`Device status received: ${JSON.stringify(deviceStatus)}`, `Device status received: ${JSON.stringify(deviceStatus)}`,
); );
logger.info("Device is ready for upload"); logger.info("Device is ready for upload");
statusReceived = true;
break; break;
} }
await this.sleep(checkInterval); await this.sleep(checkInterval);
} }
if (!statusReceived) {
const notifications = this.ble.getNotifications();
logger.warning(
`Device status (type 13) not received within ${timeoutMs}ms timeout. Got ${notifications.length} notifications. Proceeding with available data.`,
);
// Log what we did receive if in verbose mode
if (notifications.length > 0 && this.verbose) {
logger.debug("Received notification types:");
notifications.forEach((n, i) => {
logger.debug(` [${i}] type: ${n.parsed?.jsonData?.type || 'unknown'}, data: ${JSON.stringify(n.parsed?.jsonData || {})}`);
});
}
}
const notifications = this.ble.getNotifications(); const notifications = this.ble.getNotifications();
logger.info(`Received ${notifications.length} notifications from device`); logger.info(`Received ${notifications.length} notifications from device`);