node-switchbot
Version:
The node-switchbot is a Node.js module which allows you to control your Switchbot Devices through Bluetooth (BLE) with automatic OpenAPI fallback.
109 lines • 3.52 kB
JavaScript
/* Copyright(C) 2024-2026, donavanbecker (https://github.com/donavanbecker). All rights reserved.
*
* utils/bot-ble.ts: SwitchBot v4.0.0 - Bot BLE Password Helpers
*/
import { Buffer } from 'node:buffer';
/**
* CRC32 polynomial for password encryption
*/
const CRC32_POLYNOMIAL = 0xEDB88320;
/**
* Precomputed CRC32 lookup table
*/
const CRC32_TABLE = new Uint32Array(256);
// Initialize CRC32 table
for (let i = 0; i < 256; i++) {
let crc = i;
for (let j = 0; j < 8; j++) {
crc = (crc & 1) !== 0 ? (crc >>> 1) ^ CRC32_POLYNOMIAL : (crc >>> 1);
}
CRC32_TABLE[i] = crc >>> 0;
}
/**
* Calculate CRC32 checksum of a string
* @param input - String to calculate CRC32 for
* @returns CRC32 checksum as unsigned 32-bit integer
*/
function calculateCRC32(input) {
let crc = 0xFFFFFFFF;
for (let i = 0; i < input.length; i++) {
const byte = input.charCodeAt(i);
crc = (crc >>> 8) ^ CRC32_TABLE[(crc ^ byte) & 0xFF];
}
return (crc ^ 0xFFFFFFFF) >>> 0;
}
/**
* Valid Bot BLE action values
*/
export const BOT_BLE_ACTIONS = {
PRESS: 0x00,
TURN_ON: 0x01,
TURN_OFF: 0x02,
};
// Regex pattern at module scope to avoid recompilation
const PASSWORD_REGEX = /^[A-Z0-9]{4}$/i;
/**
* Validate Bot BLE password format
* Password must be exactly 4 alphanumeric characters (case-sensitive)
* @param password - Password to validate
* @throws Error if password format is invalid
*/
export function validateBotPassword(password) {
if (!PASSWORD_REGEX.test(password)) {
throw new Error('Invalid Bot password. Password must be exactly 4 alphanumeric characters (case-sensitive).');
}
}
/**
* Build Bot BLE command buffer
* @param action - Bot action (0x00=press, 0x01=on, 0x02=off)
* @param password - Optional 4-character password for encrypted commands
* @returns Command buffer ready to send via BLE
* @throws Error if action or password format is invalid
*/
export function buildBotBleCommand(action, password) {
// Validate action
const validActions = [
BOT_BLE_ACTIONS.PRESS,
BOT_BLE_ACTIONS.TURN_ON,
BOT_BLE_ACTIONS.TURN_OFF,
];
if (!validActions.includes(action)) {
throw new Error(`Invalid Bot BLE action: 0x${action.toString(16)}. Expected 0x00, 0x01, or 0x02.`);
}
// Plain command (no password)
if (!password) {
return Buffer.from([0x57, 0x01, action]);
}
// Encrypted command (with password)
validateBotPassword(password);
const crc = calculateCRC32(password);
// Command format: [0x57, 0x11, CRC32_BE (4 bytes), action]
return Buffer.from([
0x57,
0x11,
(crc >>> 24) & 0xFF, // MSB first (big-endian)
(crc >>> 16) & 0xFF,
(crc >>> 8) & 0xFF,
crc & 0xFF, // LSB
action,
]);
}
/**
* Parse Bot BLE response buffer
* @param response - Response buffer from Bot device
* @returns True if command was successful
* @throws Error if response indicates failure
*/
export function parseBotBleResponse(response) {
if (response.length !== 3) {
throw new Error(`Invalid Bot response length: ${response.length}, expected 3`);
}
const statusCode = response.readUInt8(0);
// Success codes: 0x01 (success) or 0x05 (success, encrypted)
if (statusCode === 0x01 || statusCode === 0x05) {
return true;
}
// Error response
throw new Error(`Bot command failed with status: 0x${response.toString('hex')}`);
}
//# sourceMappingURL=bot-ble.js.map