UNPKG

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.

174 lines 6.04 kB
/* Copyright(C) 2024-2026, donavanbecker (https://github.com/donavanbecker). All rights reserved. * * devices/wo-hand.ts: SwitchBot v4.0.0 - Bot (WoHand) Device */ import { DEVICE_COMMANDS } from '../settings.js'; import { BOT_BLE_ACTIONS, buildBotBleCommand, parseBotBleResponse, validateBotPassword } from '../utils/index.js'; import { DeviceOverrideStateDuringConnection } from './device-override-state-during-connection.js'; /** * Bot (WoHand) Device - Press or switch button device * Supports optional BLE password protection */ export class WoHand extends DeviceOverrideStateDuringConnection { /** * Get device status (BLE-first, API-fallback) */ async getStatus() { return this.getStatusWithFallback(bleData => ({ deviceId: this.info.id, connectionType: 'ble', power: bleData.state ? 'on' : 'off', state: bleData.state, mode: bleData.mode, battery: bleData.battery, version: bleData.version, updatedAt: new Date(), }), apiStatus => ({ deviceId: this.info.id, connectionType: 'api', power: apiStatus.power || 'off', state: apiStatus.state, mode: apiStatus.mode, battery: apiStatus.battery, version: apiStatus.version, updatedAt: new Date(), })); } password; constructor(info, options = {}) { super(info, options); // Validate and store password if provided if (options.password) { try { validateBotPassword(options.password); this.password = options.password; this.logger.info('Bot password configured'); } catch (error) { this.logger.error('Invalid password format', error); throw error; } } } /** * Turn on (switch mode) */ async turnOn() { // Use password-protected command if password is configured if (this.password) { return await this.executePasswordCommand(BOT_BLE_ACTIONS.TURN_ON); } // Standard command const result = await this.sendCommand(DEVICE_COMMANDS.BOT.TURN_ON, 'turnOn'); return result.success; } /** * Turn off (switch mode) */ async turnOff() { // Use password-protected command if password is configured if (this.password) { return await this.executePasswordCommand(BOT_BLE_ACTIONS.TURN_OFF); } // Standard command const result = await this.sendCommand(DEVICE_COMMANDS.BOT.TURN_OFF, 'turnOff'); return result.success; } /** * Press (press mode) */ async press() { // Use password-protected command if password is configured if (this.password) { return await this.executePasswordCommand(BOT_BLE_ACTIONS.PRESS); } // Standard command const result = await this.sendCommand(DEVICE_COMMANDS.BOT.PRESS, 'press'); return result.success; } /** * Set Bot mode */ async setMode(mode) { const modeByte = mode === 'switch' ? 0x01 : 0x00; return this.sendCommand([...DEVICE_COMMANDS.BOT.SET_MODE, modeByte], 'setMode', mode); } /** * Set Bot long-press duration (1-255 deciseconds) */ async setLongPress(duration) { const clampedDuration = Math.min(255, Math.max(1, Math.trunc(duration))); const result = await this.sendCommand([...DEVICE_COMMANDS.BOT.SET_LONG_PRESS, clampedDuration], 'setLongPress', clampedDuration); return result.success; } /** * Raise Bot arm */ async handUp() { const result = await this.sendCommand(DEVICE_COMMANDS.BOT.UP, 'turnOff'); return result.success; } /** * Lower Bot arm */ async handDown() { const result = await this.sendCommand(DEVICE_COMMANDS.BOT.DOWN, 'turnOn'); return result.success; } /** * Execute password-protected Bot command * @param action - Bot action to perform * @returns True if command was successful */ async executePasswordCommand(action) { if (!this.password) { throw new Error('Password not configured for this Bot device'); } if (!this.hasBLE()) { throw new Error('BLE not available - password-protected commands require BLE connection'); } try { // Build encrypted command const command = buildBotBleCommand(action, this.password); this.logger.debug('Sending password-protected command', { action }); // Send command via BLE const mac = this.info.mac ?? `id:${this.info.bleId}`; await this.bleConnection.write(mac, command); // Read response const responseBuffer = await this.bleConnection.read(mac); // Parse and validate response parseBotBleResponse(responseBuffer); this.info.activeConnection = 'ble'; this.emit('command', { type: 'ble', success: true, encrypted: true }); return true; } catch (error) { this.logger.error('Password-protected command failed', error); this.emit('error', { type: 'ble', error, encrypted: true }); throw error; } } /** * Set or update Bot password * @param password - 4-character alphanumeric password (case-sensitive) */ setPassword(password) { validateBotPassword(password); this.password = password; this.logger.info('Bot password updated'); } /** * Clear Bot password */ clearPassword() { this.password = undefined; this.logger.info('Bot password cleared'); } /** * Check if password is configured */ hasPassword() { return !!this.password; } } //# sourceMappingURL=wo-hand.js.map