UNPKG

homebridge-eqiva-swift-bridge

Version:

Homebridge Eqiva Plugin Bridge for Swift Eqiva eQ-3 Bluetooth Lock Controller.

106 lines 6.04 kB
import { SwiftLockState } from './types.js'; import { sortStatusJsonForLogging } from './utils.js'; export class EqivaLockAccessory { platform; accessory; swift; lockService; batteryService; targetStateResolver; constructor(platform, accessory, swift) { this.platform = platform; this.accessory = accessory; this.swift = swift; this.accessory.getService(this.platform.Service.AccessoryInformation) .setCharacteristic(this.platform.Characteristic.Manufacturer, this.platform.config.accessoryManufacturer) .setCharacteristic(this.platform.Characteristic.Model, this.platform.config.accessoryModel) .setCharacteristic(this.platform.Characteristic.SerialNumber, this.platform.config.accessorySerial) .setCharacteristic(this.platform.Characteristic.FirmwareRevision, this.platform.config.accessoryFirmwareRevision); this.lockService = this.accessory.getService(this.platform.Service.LockMechanism) || this.accessory.addService(this.platform.Service.LockMechanism); this.batteryService = this.accessory.getService(this.platform.Service.Battery) || this.accessory.addService(this.platform.Service.Battery); // HomeKit → Swift this.lockService.getCharacteristic(this.platform.Characteristic.LockTargetState) .onSet(this.handleTargetStateSet.bind(this)); // Swift → HomeKit this.swift.on('status', (msg) => this.handleStatus(msg)); // Important: Even on the latest firmware, it has been observed that when the lock fails to close properly, // it may incorrectly report its state as unknown (0), unlocked (2), or locked (3), seemingly at random. // Since HomeKit does not support requesting a state it already believes is current, we cannot simply resend a command to correct this. // Therefore, it is important to provide manual override switches that allow us to explicitly send "lock" (0), "unlock" (1), or "open" (2) commands. // These switches act as a fallback mechanism to manually recover from incorrect or stuck states, // particularly useful when the lock becomes jammed and does not respond to the standard "unlock" command. (this.accessory.getServiceById(this.platform.Service.Switch, 'Lock') || this.accessory.addService(new this.platform.Service.Switch('Lock', 'Lock'))) .getCharacteristic(this.platform.Characteristic.On) .onSet(() => this.swift.send('lock')); (this.accessory.getServiceById(this.platform.Service.Switch, 'Unlock') || this.accessory.addService(new this.platform.Service.Switch('Unlock', 'Unlock'))) .getCharacteristic(this.platform.Characteristic.On) .onSet(() => this.swift.send('unlock')); (this.accessory.getServiceById(this.platform.Service.Switch, 'Open') || this.accessory.addService(new this.platform.Service.Switch('Open', 'Open'))) .getCharacteristic(this.platform.Characteristic.On) .onSet(() => this.swift.send('open')); } // -------------------------------------------------------------------------- handleTargetStateSet(value) { this.platform.log.debug('HomeKit target state:', value); // Resolve the pending set Promise if any if (this.targetStateResolver) { this.targetStateResolver(); this.targetStateResolver = undefined; } return new Promise((resolve) => { // Send lock/unlock command to Swift if (value === this.platform.Characteristic.LockTargetState.SECURED) { this.swift.send('lock'); } else { this.swift.send('unlock'); } // Store the resolver to be called when a stable state is received this.targetStateResolver = resolve; }); } handleStatus(msg) { this.platform.log.debug('Status from Swift:', sortStatusJsonForLogging(msg)); if (msg.state !== SwiftLockState.moving) { const currentState = this.swiftToHKCurrent(msg.state); this.lockService.updateCharacteristic(this.platform.Characteristic.LockCurrentState, currentState); // Resolve the pending set Promise if any if (this.targetStateResolver) { this.targetStateResolver(); this.targetStateResolver = undefined; } this.lockService.updateCharacteristic(this.platform.Characteristic.LockTargetState, this.swiftToHKTarget(msg.state)); } const batteryLevel = msg.batteryLow ? this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; this.batteryService.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, batteryLevel); } // -------------------------------------------------------------------------- swiftToHKCurrent(state) { switch (state) { case SwiftLockState.locked: return this.platform.Characteristic.LockCurrentState.SECURED; case SwiftLockState.unlocked: case SwiftLockState.opened: return this.platform.Characteristic.LockCurrentState.UNSECURED; case SwiftLockState.unknownProbablyJammed: return this.platform.Characteristic.LockCurrentState.JAMMED; case SwiftLockState.moving: case SwiftLockState.trulyUnkonwn: default: return this.platform.Characteristic.LockCurrentState.UNKNOWN; } } swiftToHKTarget(state) { return this.swiftToHKCurrent(state) === this.platform.Characteristic.LockCurrentState.SECURED ? this.platform.Characteristic.LockTargetState.SECURED : this.platform.Characteristic.LockTargetState.UNSECURED; } } //# sourceMappingURL=eqivaLockAccessory.js.map