UNPKG

homebridge-zigbee

Version:
222 lines (203 loc) 6.91 kB
const requireDir = require('require-dir') const isEmpty = require('./utils/isEmpty') const ZigBeeDevice = require('./ZigBeeDevice') const parsers = requireDir('./parsers') class HomeKitDevice { constructor({ name, model, manufacturer, ieeeAddr, accessory, platform, log, ...rest }) { // Create ZigBee device instance this.zigbee = new ZigBeeDevice({ model, ieeeAddr, log }) // Save ieeeAddr as the id of device this.ieeeAddr = ieeeAddr // Save accessory name this.name = name // Save HomeKit props this.Accessory = rest.Accessory this.Service = rest.Service this.Characteristic = rest.Characteristic this.UUIDGen = rest.UUIDGen // Save platform this.platform = platform // Save logger this.log = log // Verify accessory if (accessory) { this.accessory = accessory } else { const uuid = this.UUIDGen.generate(ieeeAddr) this.accessory = new this.Accessory(name, uuid) this.accessory.getService(this.Service.AccessoryInformation) .setCharacteristic(this.Characteristic.Manufacturer, manufacturer) .setCharacteristic(this.Characteristic.Model, model) .setCharacteristic(this.Characteristic.SerialNumber, ieeeAddr) this.platform.registerAccessory(this.accessory) } this.addServices([ ...this.getAvailbleServices(), ...this.getCustomServices(), ]) this.mountSetupCharacteristics() this.accessory.updateReachability() this.accessory.on('identify', this.handleAccessoryIdentify) this.onDeviceReady() } handleAccessoryIdentify(paired, callback) { callback() } addServices(items) { const oldServices = this.accessory.services const newServices = [] // Collect new services items.forEach((item) => { const Service = this.parseServiceType(item.type) const service = new Service(this.parseServiceName(item.name), item.subtype) newServices.push(service) }) // Push default info service to new services newServices.push(this.accessory.getService(this.Service.AccessoryInformation)) // Remove outdated services oldServices.forEach((oldService) => { const service = newServices.find(newService => ( oldService.UUID === newService.UUID && oldService.displayName === newService.displayName && oldService.subtype === newService.subtype )) if (service) { // Service already exist, just remove it from new services newServices.splice(newServices.indexOf(service), 1) } else { // Service doen't exist, just remove this service from accessory this.accessory.removeService(oldService) } }) // Add new services newServices.forEach((service) => { this.accessory.addService(service) }) } parseServiceType(service) { if (typeof service === 'function') { return service } return this.Service[service] } parseServiceName(name) { if (typeof name === 'function') { return name } return `${this.name}: ${name}` } parseCharacteristic(characteristic) { if (typeof characteristic === 'function') { return characteristic } return this.Characteristic[characteristic] } getService(service) { return this.accessory.getService(this.parseServiceName(service)) } getServiceCharacteristic(service, characteristic) { return this.getService(service) .getCharacteristic(this.parseCharacteristic(characteristic)) } getServiceSubtype(service, subtype) { return this.accessory .getServiceByUUIDAndSubType(this.parseServiceName(service), subtype) } getServiceSubtypeCharacteristic(service, subtype, characteristic) { return this.accessory .getServiceByUUIDAndSubType(this.parseServiceName(service), subtype) .getCharacteristic(this.parseCharacteristic(characteristic)) } mountServiceCharacteristic({ endpoint, cluster, service, characteristic, ...options }) { const serviceCharacteristic = this.getServiceCharacteristic(service, characteristic) const parser = Object.assign({}, parsers[options.parser], options) if (parser.report) { this.zigbee.subscribe( endpoint, cluster, parser.report, parser.reportMinInt, parser.reportMaxInt, parser.reportChange, (data) => { const value = parser.reportParser(data, this, serviceCharacteristic) if (!isEmpty(value)) { serviceCharacteristic.updateValue(value) } } ) } if (parser.get) { serviceCharacteristic.on('get', (callback) => { this.zigbee.read( endpoint, cluster, parser.get, (err, data) => { if (parser.getWaitResponse) { callback(err, parser.getParser(data, this, serviceCharacteristic)) } else if (!err) { serviceCharacteristic.updateValue(parser.getParser(data, this, serviceCharacteristic)) } } ) if (!parser.getWaitResponse) { callback(null, serviceCharacteristic.value) // Don't wait response } }) } if (parser.set) { serviceCharacteristic.on('set', (value, callback) => { this.zigbee.publish( endpoint, cluster, parser.set(value, this), parser.setParser(value, this, serviceCharacteristic), (err, data) => { if (parser.setWaitResponse) { callback(err, data) } } ) if (!parser.setWaitResponse) { callback() // Don't wait response } }) } } unstableStatusMountServiceCharacteristic( { endpoint, cluster, service, characteristic, ...options } ) { const serviceCharacteristic = this.getServiceCharacteristic(service, characteristic) const parser = Object.assign({}, parsers[options.parser], options) if (parser.status) { this.zigbee.unstableStatusSubscribe(endpoint, cluster, parser.status, (data) => { serviceCharacteristic.updateValue(parser.statusParser(data, this, serviceCharacteristic)) }) } } mountSetupCharacteristics() { // Unpair Device Characteristic const unpairDevice = this.getServiceCharacteristic('Setup', 'CustomSetupUnpairDevice') unpairDevice.on('set', (value, callback) => { this.platform.removeDevice(this).then(callback) }) unpairDevice.on('get', (callback) => { callback(null, false) }) } getCustomServices() { return [{ name: 'Setup', type: 'CustomSetup', }] } // Must be overriden getAvailbleServices() { throw new Error('HomeKitDevice.getAvailbleServices() must be overriden') } onDeviceReady() { throw new Error('HomeKitDevice.onDeviceReady() must be overriden') } } module.exports = HomeKitDevice