Skip to content

Commit

Permalink
Add ability to unpair device via custom property
Browse files Browse the repository at this point in the history
  • Loading branch information
Anton Petrov committed Nov 6, 2018
1 parent 921d763 commit 4115f22
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 12 deletions.
32 changes: 27 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ const castArray = require('./lib/utils/castArray')
const parseModel = require('./lib/utils/parseModel')
const routerPolling = require('./lib/utils/routerPolling')
const findSerialPort = require('./lib/utils/findSerialPort')
const addCustomTypes = require('./lib/utils/customTypes')
const PermitJoinAccessory = require('./lib/PermitJoinAccessory')

const PLUGIN_NAME = 'homebridge-zigbee'
const PLATFORM_NAME = 'ZigBeePlatform'

const devices = Object.values(requireDir('./lib/devices'))

// Only for beta period
Expand All @@ -24,11 +28,12 @@ process.on('unhandledRejection', (reason) => {
let Accessory, Service, Characteristic, UUIDGen

module.exports = function main(homebridge) {
addCustomTypes(homebridge.hap)
Accessory = homebridge.platformAccessory
Service = homebridge.hap.Service
Characteristic = homebridge.hap.Characteristic
UUIDGen = homebridge.hap.uuid
homebridge.registerPlatform('homebridge-zigbee', 'ZigBeePlatform', ZigBeePlatform, true)
homebridge.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, ZigBeePlatform, true)
}

class ZigBeePlatform {
Expand Down Expand Up @@ -161,7 +166,7 @@ class ZigBeePlatform {
const accessory = this.getAccessory(uuid)
// Sometimes we can unpair device which doesn't exist in HomeKit
if (accessory) {
this.api.unregisterPlatformAccessories('homebridge-zigbee', 'ZigBeePlatform', [accessory])
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory])
delete this.devices[ieeeAddr]
delete this.accessories[uuid]
}
Expand Down Expand Up @@ -215,7 +220,7 @@ class ZigBeePlatform {

registerAccessory(accessory) {
this.setAccessory(accessory)
this.api.registerPlatformAccessories('homebridge-zigbee', 'ZigBeePlatform', [accessory])
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory])
}

recognizeDevice({ model, manufacturer }) {
Expand Down Expand Up @@ -292,7 +297,24 @@ class ZigBeePlatform {
this.setAccessory(accessory)
}

removeAccessory() {
this.log('removeAccessory is not implemented yet')
removeAccessory(accessory) {
try {
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory])
} catch (error) {
// Do nothing
}
}

async removeDevice(device) {
try {
this.log('Removing device:', device.ieeeAddr)
await zigbee.remove(device.ieeeAddr)
} catch (error) {
this.log('Unable to remove properly, trying to unregister device:', device.ieeeAddr)
await zigbee.unregister(device.ieeeAddr)
} finally {
this.log('Device has been removed:', device.ieeeAddr)
this.removeAccessory(device.accessory)
}
}
}
32 changes: 28 additions & 4 deletions lib/HomeKitDevice.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,13 @@ class HomeKitDevice {
.setCharacteristic(this.Characteristic.Manufacturer, manufacturer)
.setCharacteristic(this.Characteristic.Model, model)
.setCharacteristic(this.Characteristic.SerialNumber, ieeeAddr)
this.getAvailbleServices().forEach((service) => {
const Service = this.parseServiceType(service.type)
this.accessory.addService(new Service(this.parseServiceName(service.name), service.subtype))
})
this.addServices(this.getAvailbleServices())
this.addServices(this.getCustomServices())
this.platform.registerAccessory(this.accessory)
}
this.accessory.updateReachability()
this.accessory.on('identify', this.handleAccessoryIdentify)
this.mountCustomServiceCharacteristics()
this.onDeviceReady()
}

Expand Down Expand Up @@ -82,6 +81,13 @@ class HomeKitDevice {
.getCharacteristic(this.parseCharacteristic(characteristic))
}

addServices(services) {
services.forEach((service) => {
const Service = this.parseServiceType(service.type)
this.accessory.addService(new Service(this.parseServiceName(service.name), service.subtype))
})
}

mountServiceCharacteristic({ endpoint, cluster, service, characteristic, ...options }) {
const serviceCharacteristic = this.getServiceCharacteristic(service, characteristic)
const parser = Object.assign({}, parsers[options.parser], options)
Expand Down Expand Up @@ -152,6 +158,24 @@ class HomeKitDevice {
}
}

mountCustomServiceCharacteristics() {
// 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')
Expand Down
34 changes: 34 additions & 0 deletions lib/utils/customTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const CUSTOM_UUID_SUFFIX = '03a1-4971-92bf-af2b7d833922'

module.exports = function addCustomTypes({ Characteristic, Service }) {
// Characteristics
Characteristic.CustomSetupUnpairDevice = class CustomSetupUnpairDevice extends Characteristic {
static get UUID() {
return `00000009-${CUSTOM_UUID_SUFFIX}`
}

constructor() {
super('Unpair Device', CustomSetupUnpairDevice.UUID)
this.setProps({
format: Characteristic.Formats.BOOL,
perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY],
})
this.value = this.getDefaultValue()
}
}

// Services
Service.CustomSetup = class CustomSetup extends Service {
static get UUID() {
return `F00000FF-${CUSTOM_UUID_SUFFIX}`
}

constructor(displayName, subtype) {
super(displayName, CustomSetup.UUID, subtype)
// Optional Characteristics
this.addOptionalCharacteristic(Characteristic.CustomSetupUnpairDevice)
// Optional Characteristics standard
this.addOptionalCharacteristic(Characteristic.Name)
}
}
}
2 changes: 1 addition & 1 deletion lib/utils/xiaomi.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function parseXiaomiBatteryInfo(rawData) {

function updateXiaomiButteryCharacteristics(device, data) {
const value = parseXiaomiBatteryInfo(data)
device.log('buttery:', value)
device.log('battery info received:', value)
const chargingStateCharacteristic = device.getServiceCharacteristic(
'Battery', device.Characteristic.ChargingState
)
Expand Down
12 changes: 11 additions & 1 deletion lib/zigbee.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const ZShepherd = require('zigbee-shepherd')
const promisify = require('./utils/promisify')

/* eslint-disable no-underscore-dangle */
class ZigBee {
constructor() {
this.shepherd = null
Expand Down Expand Up @@ -57,12 +58,21 @@ class ZigBee {
}

ping(addr) {
const device = this.shepherd._findDevByAddr(addr) // eslint-disable-line no-underscore-dangle
const device = this.shepherd._findDevByAddr(addr)
if (device) {
return this.shepherd.controller.checkOnline(device)
}
}

remove(addr) {
return promisify(this.shepherd.remove.bind(this.shepherd), addr)
}

unregister(addr) {
const device = this.shepherd._findDevByAddr(addr)
return promisify(this.shepherd._unregisterDev.bind(this.shepherd), device)
}

permitJoin(timeout, callback) {
return this.shepherd.permitJoin(timeout, callback)
}
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4115f22

Please sign in to comment.