UNPKG

@switchbot/homebridge-switchbot

Version:

The SwitchBot plugin allows you to access your SwitchBot device(s) from HomeKit.

203 lines (194 loc) • 6.54 kB
import type { Logger } from 'homebridge' import type { DeviceType, SwitchBotPluginConfig } from './settings.js' import { DEVICE_TYPE_NORMALIZATION_MAP } from './device-types.js' import { BlindTiltDevice, BotDevice, ContactSensorDevice, Curtain3Device, CurtainDevice, FanDevice, GenericDevice, Hub2Device, HumidifierDevice, LightDevice, LightStripDevice, LockDevice, MeterDevice, MotionSensorDevice, PlugDevice, PlugMiniDevice, RelaySwitch1PMDevice, RelaySwitchDevice, RollerShadeDevice, SmartFanDevice, StripLightDevice, TemperatureSensorDevice, VacuumDevice, WalletFinderDevice, WaterDetectorDevice, WoSweeperDevice, WoSweeperMiniDevice, WoSweeperMiniProDevice, } from './devices/genericDevice.js' import { SwitchBotClient } from './switchbotClient.js' export interface DeviceOptions { id: string type: DeviceType name?: string [key: string]: any } const DEVICE_CLASS_MAP: Record<string, any> = { // Primary device type keys (lowercase, simplified) 'bot': BotDevice, 'curtain': CurtainDevice, 'curtain3': Curtain3Device, 'fan': FanDevice, 'light': LightDevice, 'lightstrip': LightStripDevice, 'motion': MotionSensorDevice, 'contact': ContactSensorDevice, 'vacuum': VacuumDevice, // Canonical, normalized device type keys (lowercase, mapped to device classes) 'video doorbell': GenericDevice, 'smart radiator thermostat': GenericDevice, 'woiosensor': GenericDevice, 'garage door opener': GenericDevice, 'air purifier table pm2.5': GenericDevice, 'air purifier voc': GenericDevice, 'air purifier table voc': GenericDevice, 'meterplus': MeterDevice, 'meterpro': MeterDevice, 'meterpro(co2)': MeterDevice, 'walletfinder': WalletFinderDevice, 'plug': PlugDevice, 'plug mini (eu)': PlugMiniDevice, 'plug mini (jp)': PlugMiniDevice, 'plug mini (us)': PlugMiniDevice, 'relay switch 1pm': RelaySwitch1PMDevice, 'relay switch 2pm': RelaySwitch1PMDevice, 'k10+ pro': WoSweeperDevice, 'robot vacuum cleaner k10+ pro combo': WoSweeperDevice, 'robot vacuum cleaner k11+': WoSweeperDevice, 'robot vacuum cleaner k20 plus pro': WoSweeperDevice, 'ai hub': GenericDevice, 'hub': GenericDevice, 'hub 2': Hub2Device, 'hub 3': GenericDevice, 'hub mini': GenericDevice, 'hub plus': GenericDevice, 'indoor cam': GenericDevice, 'pan/tilt cam': GenericDevice, 'pan/tilt cam 2k': GenericDevice, 'pan/tilt cam plus 2k': GenericDevice, 'pan/tilt cam plus 3k': GenericDevice, 'humidifier': HumidifierDevice, // Includes evaporative humidifier mapping 'roller shade': RollerShadeDevice, 'strip light 3': StripLightDevice, 'circulator fan': FanDevice, 'smart lock pro': LockDevice, 'lock lite': LockDevice, 'keypad': LockDevice, 'lock vision pro': LockDevice, 'floor lamp': LightDevice, 'rgbicww floor lamp': LightStripDevice, 'rgbicww strip light': LightStripDevice, 'home climate panel': GenericDevice, // Climate panel family 'lock': LockDevice, 'humidifier2': HumidifierDevice, 'temperature': TemperatureSensorDevice, 'relay switch 1': RelaySwitchDevice, 'blind tilt': BlindTiltDevice, 'worollershade': RollerShadeDevice, 'wo rollershade': RollerShadeDevice, 'rollershade': RollerShadeDevice, 'meter': MeterDevice, 'meter plus (jp)': MeterDevice, 'water detector': WaterDetectorDevice, 'smart fan': SmartFanDevice, 'strip light': StripLightDevice, 'wosweeper': WoSweeperDevice, 'wosweepermini': WoSweeperMiniDevice, 'wosweeperminipro': WoSweeperMiniProDevice, 'k10+': WoSweeperDevice, 'k10+ pro (wosweeperminipro)': WoSweeperMiniProDevice, 'battery circulator fan': FanDevice, 'standing circulator fan': FanDevice, 'smart lock': LockDevice, 'smart lock ultra': LockDevice, 'keypad touch': LockDevice, 'keypad vision': LockDevice, 'keypad vision pro': LockDevice, 'color bulb': LightDevice, 'ceiling light': LightDevice, 'ceiling light pro': LightDevice, 'candle warmer lamp': LightDevice, 'rgbic neon rope light': LightStripDevice, 'rgbic neon wire rope light': LightStripDevice, 'robot vacuum cleaner s1': VacuumDevice, 'robot vacuum cleaner s1 plus': VacuumDevice, 'robot vacuum cleaner s10': VacuumDevice, 'robot vacuum cleaner s20': VacuumDevice, } function classForType(type: string) { const rawKey = (type || '').toLowerCase() const key = DEVICE_TYPE_NORMALIZATION_MAP[rawKey] ?? rawKey return DEVICE_CLASS_MAP[key] ?? GenericDevice } export async function createDevice(opts: DeviceOptions, cfg: SwitchBotPluginConfig, useMatter: boolean, log?: Logger) { // Always pass the logger to both device opts and config const logger = log || (cfg as any)?.logger || (cfg as any)?.log if (opts && opts.name && logger && typeof logger.info === 'function') { logger.info(`[Matter/Debug] createDevice: Passing opts for ${opts.name}:`, JSON.stringify(opts, null, 2)) } // Reuse existing client when provided via config to avoid creating // a new SwitchBotClient per device (which can be expensive). let client: any = (cfg as any)?._client ?? null if (!client) { client = new SwitchBotClient(cfg) await client.init() } // Pass client via config so devices can access it const mergedCfg = { ...(cfg as any), _client: client } // Always pass logger to mergedCfg if (logger) { mergedCfg.log = logger } // Pass encryptionKey and keyId to device opts if present const deviceOpts = { ...opts } if (opts.encryptionKey) { deviceOpts.encryptionKey = opts.encryptionKey } if (opts.keyId) { deviceOpts.keyId = opts.keyId } // Always pass logger to deviceOpts if (logger) { deviceOpts.log = logger } const DeviceCtor = classForType(opts.type) const device = new DeviceCtor(deviceOpts, mergedCfg) await device.init() // Attach a simple getState delegator to the client where appropriate const originalGetState = device.getState.bind(device) device.getState = async () => { try { // Prefer client-backed getDevice when available const dev = await client.getDevice(opts.id) if (dev) { return dev } } catch (e) { // ignore and fallback to device implementation } return originalGetState() } // Provide accessory factory based on platform selection return { instance: device, createAccessory: useMatter ? async (api: any) => await device.createMatterAccessory(api) : (api: any) => device.createHAPAccessory(api), protocol: useMatter ? 'matter' : 'hap', } }