hap-node-client
Advanced tools
Comparing version 0.2.8-beta.0 to 0.2.8-beta.1
@@ -13,2 +13,4 @@ 'use strict'; | ||
var monitorBridgeUpdates = require('./lib/monitorBridgeUpdates.js'); | ||
axiosRetry(axios, { retries: 3 }); | ||
@@ -23,6 +25,2 @@ | ||
module.exports = { | ||
HAPNodeJSClient: HAPNodeJSClient | ||
}; | ||
/** | ||
@@ -45,68 +43,459 @@ * HAPNodeJSClient - Client for Homebridge and HAP-NodeJS in insecure mode. | ||
function HAPNodeJSClient(options) { | ||
// console.log('Options', options); | ||
this.debug = options.debug || false; | ||
this.refresh = options.refresh || 900; | ||
this.timeout = options.timeout || 20; | ||
this.reqTimeout = options.reqTimeout || 7000; | ||
this.RegisterPin('default', options.pin || '031-45-154'); | ||
filter = options.filter || false; | ||
if (this.debug) { | ||
let debugEnable = require('debug'); | ||
let namespaces = debugEnable.disable(); | ||
var populateCacheTimeout; | ||
// this.log('DEBUG-1', namespaces); | ||
if (namespaces) { | ||
namespaces = namespaces + ',hap*'; | ||
} else { | ||
namespaces = 'hap*'; | ||
class HAPNodeJSClient { | ||
constructor(options) { | ||
// console.log('Options', options); | ||
this.debug = options.debug || false; | ||
this.refresh = options.refresh || 900; | ||
this.timeout = options.timeout || 20; | ||
this.reqTimeout = options.reqTimeout || 7000; | ||
this.RegisterPin('default', options.pin || '031-45-154'); | ||
filter = options.filter || false; | ||
if (this.debug) { | ||
let debugEnable = require('debug'); | ||
let namespaces = debugEnable.disable(); | ||
// this.log('DEBUG-1', namespaces); | ||
if (namespaces) { | ||
namespaces = namespaces + ',hap*'; | ||
} else { | ||
namespaces = 'hap*'; | ||
} | ||
// this.log('DEBUG-2', namespaces); | ||
debugEnable.enable(namespaces); | ||
} | ||
// this.log('DEBUG-2', namespaces); | ||
debugEnable.enable(namespaces); | ||
} | ||
this.eventRegistry = {}; | ||
_discovery.call(this); | ||
this._eventBus = new EventEmitter(); | ||
setInterval(_discovery.bind(this), this.refresh * 1000); | ||
this.eventRegistry = {}; | ||
_discovery.call(this); // Inital discovery of devices | ||
this._eventBus = new EventEmitter(); | ||
this.discoveryTimer = setInterval(_discovery.bind(this), this.refresh * 1000); | ||
/** | ||
* HomeKit Accessory Characteristic event pass thru | ||
* | ||
* @event HAPNodeJSClient#Disconnected | ||
* @Type {object} | ||
* @property {string} server - IP Address and port of disconnected homebridge | ||
* @example Sample Message | ||
* | ||
* { host: '192.168.1.4', port: 51826, aid: 16, iid: 11, status: false } | ||
*/ | ||
this._eventBus.on('Disconnected', _reconnectServer.bind(this)); | ||
this._eventBus.on('Event', function (events) { | ||
debug('Events', JSON.stringify(events)); | ||
/** | ||
* HomeKit Accessory Characteristic event pass thru | ||
* | ||
* @event HAPNodeJSClient#hapEvent | ||
* @event HAPNodeJSClient#Disconnected | ||
* @Type {object} | ||
* @property {string} host - IP Address of homebridge instance generating event | ||
* @property {number} port - Port of homebridge instance generating event | ||
* @property {number} deviceID - deviceID of homebridge instance generating event | ||
* @property {number} aid - Accessory ID of accessory generating event | ||
* @property {number} iid - Instance ID of accessory characteristic generating event | ||
* @property {object} value - Updated characteristic value | ||
* @property {string} server - IP Address and port of disconnected homebridge | ||
* @example Sample Message | ||
* | ||
* [{"host":"192.168.1.13","port":43787,"deviceID":"76:59:CE:25:B9:6E","aid":1,"iid":13,"value":true,"status":true}] | ||
* { host: '192.168.1.4', port: 51826, aid: 16, iid: 11, status: false } | ||
*/ | ||
this.emit('hapEvent', events); | ||
this.emit(events[0].host + events[0].port + events[0].aid, events); | ||
events.forEach(function (event) { | ||
// debug('hapEvent', event.host + event.port + event.aid + event.iid, event); | ||
this.emit(event.host + event.port + event.aid + event.iid, event); | ||
this.emit(event.deviceID + event.aid + event.iid, event); | ||
this._eventBus.on('Disconnected', _reconnectServer.bind(this)); | ||
this._eventBus.on('Event', function (events) { | ||
debug('Events', JSON.stringify(events)); | ||
/** | ||
* HomeKit Accessory Characteristic event pass thru | ||
* | ||
* @event HAPNodeJSClient#hapEvent | ||
* @Type {object} | ||
* @property {string} host - IP Address of homebridge instance generating event | ||
* @property {number} port - Port of homebridge instance generating event | ||
* @property {number} deviceID - deviceID of homebridge instance generating event | ||
* @property {number} aid - Accessory ID of accessory generating event | ||
* @property {number} iid - Instance ID of accessory characteristic generating event | ||
* @property {object} value - Updated characteristic value | ||
* @example Sample Message | ||
* | ||
* [{"host":"192.168.1.13","port":43787,"deviceID":"76:59:CE:25:B9:6E","aid":1,"iid":13,"value":true,"status":true}] | ||
*/ | ||
this.emit('hapEvent', events); | ||
this.emit(events[0].host + events[0].port + events[0].aid, events); | ||
events.forEach(function (event) { | ||
// debug('hapEvent', event.host + event.port + event.aid + event.iid, event); | ||
this.emit(event.host + event.port + event.aid + event.iid, event); | ||
this.emit(event.deviceID + event.aid + event.iid, event); | ||
}.bind(this)); | ||
}.bind(this)); | ||
}.bind(this)); | ||
// debug('This', this); | ||
// debug('This', this); | ||
} | ||
/** | ||
* HAPNodeJSClient.prototype.RegisterPin - Register pin numbers () | ||
* | ||
* @class | ||
* @param {type} key Unique identifier of homebridge instance (ip:port or deviceID) | ||
* @param {type} pin Homebridge PIN | ||
* @return {type} bool updated | ||
*/ | ||
RegisterPin(key, pin) { | ||
if (!key || (key in pins && pins[key] === pin)) { | ||
return false; | ||
} | ||
key = key.toLowerCase(); | ||
pins[key] = pin; | ||
debug('Registered/updated PIN for `%s`: %s', key, pin); | ||
return true; | ||
} | ||
/** | ||
* HAPNodeJSClient.prototype.HAPaccessories - Returns an array of all homebridge instances, and the accessories for each. | ||
* | ||
* @class | ||
* @param {type} callback description | ||
* @return {type} description | ||
*/ | ||
HAPaccessories(callback) { | ||
// This is a callback as in the future may need to call something | ||
callback(discovered); | ||
} | ||
/** | ||
* HAPNodeJSClient.prototype.mdnsCache | ||
* | ||
* @returns mdnsCacheObject | ||
*/ | ||
mdnsCache() { | ||
return mdnsCache; | ||
} | ||
// curl -X PUT http://127.0.0.1:51826/characteristics --header "Content-Type:Application/json" | ||
// --header "authorization: 031-45-154" --data "{ \"characteristics\": [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] }" | ||
/** | ||
* HAPNodeJSClient.prototype.HAPcontrolByDeviceID - Send a characteristic PUT Message to a particular homebridge instance | ||
* | ||
* @param {type} deviceID deviceID of homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPcontrolByDeviceID(deviceID, body, callback) { | ||
_mdnsLookup(deviceID, function (err, instance) { | ||
if (err) { | ||
callback(err); | ||
} else { | ||
HAPNodeJSClient.prototype.HAPcontrol.call(this, instance.host, instance.port, body, function (err, response) { | ||
if (err) { | ||
_mdnsError(deviceID); | ||
} | ||
callback(err, response); | ||
}, instance); | ||
} | ||
}.bind(this)); | ||
} | ||
/** | ||
* HAPNodeJSClient.prototype.HAPcontrol - Send a characteristic PUT Message to a particular homebridge instance | ||
* | ||
* @param {type} ipAddress IP Address of homebridge instance | ||
* @param {type} port Port of homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPcontrol(ipAddress, port, body, callback, instance) { | ||
axios({ | ||
eventBus: this._eventBus, | ||
method: 'PUT', | ||
url: instance.url + '/characteristics', | ||
timeout: this.reqTimeout, | ||
headers: { | ||
'Content-Type': 'Application/json', | ||
'authorization': _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), | ||
'connection': 'keep-alive' | ||
}, | ||
data: body, | ||
validateStatus: function (status) { | ||
return true; // Resolve only if the status code is less than 500 | ||
} | ||
}).then(function (response) { | ||
// debug('HAPcontrol-then', response.status, response.statusText, response.headers, response.data, response.config); | ||
switch (response.status) { | ||
case 204: | ||
callback(null, null); | ||
break; | ||
case 207: | ||
callback(null, response.data); | ||
break; | ||
case 401: | ||
case 470: | ||
debug('Homebridge auth failed, invalid PIN %s %s:%s', _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), ipAddress, port, body, response.data); | ||
callback(new Error('Homebridge auth failed, invalid PIN ' + _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port))); | ||
break; | ||
default: | ||
debug('Homebridge Control failed %s:%s Status: %s ', ipAddress, port, response.status, body, response.data); | ||
callback(new Error('Homebridge control failed')); | ||
} | ||
}).catch(function (err) { | ||
// Response s/b 200 OK | ||
debug('HAPcontrol-catch', err); | ||
debug('Homebridge Control failed %s:%s', ipAddress, port, body, err.message); | ||
callback(err); | ||
}); | ||
} | ||
/** | ||
* HAPNodeJSClient.prototype.HAPeventByDeviceID - Send a characteristic PUT Message to a particular homebridge instance, this maintains a socket connection for use in returning Events | ||
* | ||
* @param {type} deviceID deviceID homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPeventByDeviceID(deviceID, body, callback) { | ||
// console.log('This-0', this); | ||
_mdnsLookup(deviceID, function (err, instance) { | ||
// debug('This-1', instance); | ||
if (err) { | ||
callback(err); | ||
} else { | ||
hapRequest({ | ||
eventBus: this._eventBus, | ||
method: 'PUT', | ||
deviceID: deviceID, | ||
url: instance.url + '/characteristics', | ||
timeout: this.reqTimeout, | ||
headers: { | ||
'Content-Type': 'Application/json', | ||
'authorization': _findPinByKey(deviceID), | ||
'connection': 'keep-alive' | ||
}, | ||
body: body | ||
}, function (err, response) { | ||
// Response s/b 200 OK | ||
if (err) { | ||
debug('Homebridge event reg failed %s:%s', instance.host, instance.port, body, err.message); | ||
_mdnsError(deviceID); | ||
callback(err); | ||
} else if (response.statusCode !== 207 && response.statusCode !== 204) { | ||
if (response.statusCode === 401 || response.statusCode === 470) { | ||
debug('Homebridge auth failed, invalid PIN %s', _findPinByKey(deviceID), deviceID, body, err, response.body); | ||
_mdnsError(deviceID); | ||
callback(new Error('Homebridge auth failed, invalid PIN ' + _findPinByKey(deviceID))); | ||
} else { | ||
debug('Homebridge event reg failed %s:%s Status: %s ', deviceID, response.statusCode, body, err, response.body); | ||
_mdnsError(deviceID); | ||
callback(new Error('Homebridge event reg failed')); | ||
} | ||
} else { | ||
var rsp; | ||
if (!this.eventRegistry[deviceID]) { | ||
this.eventRegistry[deviceID] = []; | ||
} | ||
// debug('1', JSON.parse(body).characteristics); | ||
this.eventRegistry[deviceID] = this.eventRegistry[deviceID].concat(JSON.parse(body).characteristics); | ||
// debug('2', JSON.stringify(this.eventRegistry[key])); | ||
this.eventRegistry[deviceID].sort((a, b) => (JSON.stringify(a) > JSON.stringify(b)) ? 1 : ((JSON.stringify(b) > JSON.stringify(a)) ? -1 : 0)); | ||
// debug('3', JSON.stringify(this.eventRegistry[key])); | ||
this.eventRegistry[deviceID] = Array.from(new Set(this.eventRegistry[deviceID].map(JSON.stringify))).map(JSON.parse); | ||
// debug('4', JSON.stringify(this.eventRegistry[key])); | ||
try { | ||
rsp = JSON.parse(response.body); | ||
} catch (ex) { | ||
debug('Homebridge Response Failed %s:%s', deviceID, response.statusCode, response.statusMessage); | ||
debug('Homebridge Response Failed %s:%s', deviceID, response.body, ex); | ||
callback(new Error(ex)); | ||
return; | ||
} | ||
callback(null, rsp); | ||
} | ||
}.bind(this)); | ||
} | ||
}.bind(this)); | ||
} | ||
/** | ||
* HAPNodeJSClient.prototype.HAPevent - Send a characteristic PUT Message to a particular homebridge instance, this maintains a socket connection for use in returning Events | ||
* | ||
* @param {type} ipAddress IP Address of homebridge instance | ||
* @param {type} port Port of homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPevent(ipAddress, port, body, callback, instance) { | ||
hapRequest({ | ||
eventBus: this._eventBus, | ||
method: 'PUT', | ||
url: instance.url + '/characteristics', | ||
timeout: this.reqTimeout, | ||
headers: { | ||
'Content-Type': 'Application/json', | ||
'authorization': _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), | ||
'connection': 'keep-alive' | ||
}, | ||
body: body | ||
}, function (err, response) { | ||
// Response s/b 200 OK | ||
if (err) { | ||
debug('Homebridge event reg failed %s:%s', ipAddress, port, body, err.message); | ||
callback(err); | ||
} else if (response.statusCode !== 207 && response.statusCode !== 204) { | ||
if (response.statusCode === 401 || response.statusCode === 470) { | ||
debug('Homebridge auth failed, invalid PIN %s %s:%s', _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), ipAddress, port, body, err, response.body); | ||
callback(new Error('Homebridge auth failed, invalid PIN ' + _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port))); | ||
} else { | ||
debug('Homebridge event reg failed %s:%s Status: %s ', ipAddress, port, response.statusCode, body, err, response.body); | ||
callback(new Error('Homebridge event reg failed')); | ||
} | ||
} else { | ||
var rsp; | ||
var key = ipAddress + ':' + port; | ||
if (!this.eventRegistry[key]) { | ||
this.eventRegistry[key] = []; | ||
} | ||
// debug('1', JSON.parse(body).characteristics); | ||
this.eventRegistry[key] = this.eventRegistry[key].concat(JSON.parse(body).characteristics); | ||
// debug('2', JSON.stringify(this.eventRegistry[key])); | ||
this.eventRegistry[key].sort((a, b) => (JSON.stringify(a) > JSON.stringify(b)) ? 1 : ((JSON.stringify(b) > JSON.stringify(a)) ? -1 : 0)); | ||
// debug('3', JSON.stringify(this.eventRegistry[key])); | ||
this.eventRegistry[key] = Array.from(new Set(this.eventRegistry[key].map(JSON.stringify))).map(JSON.parse); | ||
// debug('4', JSON.stringify(this.eventRegistry[key])); | ||
try { | ||
rsp = JSON.parse(response.body); | ||
} catch (ex) { | ||
debug('Homebridge Response Failed %s:%s', ipAddress, port, response.statusCode, response.statusMessage); | ||
debug('Homebridge Response Failed %s:%s', ipAddress, port, response.body, ex); | ||
callback(new Error(ex)); | ||
return; | ||
} | ||
callback(null, rsp); | ||
} | ||
}.bind(this)); | ||
} | ||
/** | ||
* HAPNodeJSClient.prototype.HAPresourceByDeviceID - Send a characteristic PUT Message to a particular homebridge instance using resource interface, ie camera | ||
* | ||
* @param {type} DeviceID DeviceID of homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPresourceByDeviceID(deviceID, body, callback) { | ||
// console.log('This-0', this); | ||
_mdnsLookup(deviceID, function (err, instance) { | ||
// console.log('This-1', this); | ||
if (err) { | ||
callback(err); | ||
} else { | ||
HAPNodeJSClient.prototype.HAPresource.call(this, instance.host, instance.port, body, function (err, response) { | ||
if (err) { | ||
_mdnsError(deviceID); | ||
} | ||
callback(err, response); | ||
}, instance); | ||
} | ||
}.bind(this)); | ||
} | ||
/** | ||
* HAPNodeJSClient.prototype.HAPresource - Send a characteristic PUT Message to a particular homebridge instance using resource interface, ie camera | ||
* | ||
* @param {type} ipAddress IP Address of homebridge instance | ||
* @param {type} port Port of homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPresource(ipAddress, port, body, callback, instance) { | ||
axios({ | ||
eventBus: this._eventBus, | ||
method: 'POST', | ||
url: instance.url + '/resource', | ||
timeout: this.reqTimeout, | ||
responseType: 'arraybuffer', | ||
headers: { | ||
'Content-Type': 'Application/json', | ||
'authorization': _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), | ||
'connection': 'keep-alive' | ||
}, | ||
data: body, | ||
validateStatus: function (status) { | ||
return true; // Resolve only if the status code is less than 500 | ||
} | ||
}).then(function (response) { | ||
// debug('HAPcontrol-then', response.status, response.statusText, response.headers, response.config); | ||
switch (response.status) { | ||
case 200: | ||
callback(null, response.data); | ||
break; | ||
case 401: | ||
case 470: | ||
debug('Homebridge auth failed, invalid PIN %s %s:%s', _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), ipAddress, port, body, response.data); | ||
callback(new Error('Homebridge auth failed, invalid PIN ' + _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port))); | ||
break; | ||
default: | ||
debug('Homebridge Resource failed %s:%s Status: %s ', ipAddress, port, response.status, body, response.data); | ||
callback(new Error('Homebridge Resource failed')); | ||
} | ||
}).catch(function (err) { | ||
// Response s/b 200 OK | ||
debug('HAPcontrol-catch', err); | ||
debug('Homebridge Resource failed %s:%s', ipAddress, port, body, err.message); | ||
callback(err); | ||
}); | ||
} | ||
/** | ||
* HAPNodeJSClient.prototype.HAPstatusByDeviceID - Get current status for characteristics | ||
* | ||
* @param {type} deviceID deviceID of homebridge instance | ||
* @param {type} body description | ||
* @param {type} callback Callback to execute upon completion of characteristic getting, function(err, response) | ||
*/ | ||
HAPstatusByDeviceID(deviceID, body, callback) { | ||
// console.log('This-0', this); | ||
_mdnsLookup(deviceID, function (err, instance) { | ||
// console.log('This-1', this); | ||
if (err) { | ||
callback(err); | ||
} else { | ||
HAPNodeJSClient.prototype.HAPstatus.call(this, instance.host, instance.port, body, function (err, response) { | ||
if (err) { | ||
_mdnsError(deviceID); | ||
} | ||
callback(err, response); | ||
}, instance); | ||
} | ||
}.bind(this)); | ||
} | ||
/** | ||
* HAPNodeJSClient.prototype.HAPstatus - Get current status for characteristics | ||
* | ||
* @param {type} ipAddress IP Address of homebridge instance | ||
* @param {type} port Port of homebridge instance | ||
* @param {type} body description | ||
* @param {type} callback Callback to execute upon completion of characteristic getting, function(err, response) | ||
*/ | ||
HAPstatus(ipAddress, port, body, callback, instance) { | ||
axios({ | ||
eventBus: this._eventBus, | ||
method: 'GET', | ||
url: instance.url + '/characteristics' + body, | ||
timeout: this.reqTimeout, | ||
headers: { | ||
'Content-Type': 'Application/json', | ||
'authorization': _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), | ||
'connection': 'keep-alive' | ||
}, | ||
validateStatus: function (status) { | ||
return true; // Resolve only if the status code is less than 500 | ||
} | ||
}).then(function (response) { | ||
// debug('HAPstatus-then', response.status, response.statusText, response.headers, response.data, response.config); | ||
switch (response.status) { | ||
case 200: | ||
callback(null, response.data); | ||
break; | ||
case 207: | ||
callback(null, response.data); | ||
break; | ||
case 401: | ||
case 470: | ||
debug('Homebridge auth failed, invalid PIN %s %s:%s', _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), ipAddress, port, body, response.data); | ||
callback(new Error('Homebridge auth failed, invalid PIN ' + _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port))); | ||
break; | ||
default: | ||
debug('Homebridge Status failed %s:%s Status: %s ', ipAddress, port, response.status, body, response.data); | ||
callback(new Error('Homebridge Status failed')); | ||
} | ||
}).catch(function (err) { | ||
// Response s/b 200 OK | ||
debug('HAPstatus-catch', err); | ||
debug('Homebridge Status failed %s:%s', ipAddress, port, body, err.message); | ||
callback(err); | ||
}); | ||
} | ||
/** | ||
* Destroy and shutdown HAPNodeJSClient - Used by testing | ||
*/ | ||
async destroy() { | ||
clearInterval(this.discoveryTimer); | ||
clearInterval(populateCacheTimeout); | ||
bonjour.destroy(); | ||
} | ||
} | ||
@@ -198,3 +587,3 @@ | ||
}); | ||
setTimeout(function () { | ||
populateCacheTimeout = setTimeout(function () { | ||
// debug('Timeout:'); | ||
@@ -219,122 +608,8 @@ browser.stop(); | ||
/** | ||
* HAPNodeJSClient.prototype.RegisterPin - Register pin numbers () | ||
* | ||
* @class | ||
* @param {type} key Unique identifier of homebridge instance (ip:port or deviceID) | ||
* @param {type} pin Homebridge PIN | ||
* @return {type} bool updated | ||
*/ | ||
HAPNodeJSClient.prototype.RegisterPin = function (key, pin) { | ||
if (!key || (key in pins && pins[key] === pin)) { | ||
return false; | ||
} | ||
key = key.toLowerCase(); | ||
pins[key] = pin; | ||
debug('Registered/updated PIN for `%s`: %s', key, pin); | ||
return true; | ||
}; | ||
/** | ||
* HAPNodeJSClient.prototype.HAPaccessories - Returns an array of all homebridge instances, and the accessories for each. | ||
* | ||
* @class | ||
* @param {type} callback description | ||
* @return {type} description | ||
*/ | ||
HAPNodeJSClient.prototype.HAPaccessories = function (callback) { | ||
// This is a callback as in the future may need to call something | ||
callback(discovered); | ||
}; | ||
/** | ||
* HAPNodeJSClient.prototype.mdnsCache | ||
* | ||
* @returns mdnsCacheObject | ||
*/ | ||
HAPNodeJSClient.prototype.mdnsCache = function () { | ||
return mdnsCache; | ||
}; | ||
// curl -X PUT http://127.0.0.1:51826/characteristics --header "Content-Type:Application/json" | ||
// --header "authorization: 031-45-154" --data "{ \"characteristics\": [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] }" | ||
/** | ||
* HAPNodeJSClient.prototype.HAPcontrolByDeviceID - Send a characteristic PUT Message to a particular homebridge instance | ||
* | ||
* @param {type} deviceID deviceID of homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPNodeJSClient.prototype.HAPcontrolByDeviceID = function (deviceID, body, callback) { | ||
_mdnsLookup(deviceID, function (err, instance) { | ||
if (err) { | ||
callback(err); | ||
} else { | ||
HAPNodeJSClient.prototype.HAPcontrol.call(this, instance.host, instance.port, body, function (err, response) { | ||
if (err) { | ||
_mdnsError(deviceID); | ||
} | ||
callback(err, response); | ||
}, instance); | ||
} | ||
}.bind(this)); | ||
}; | ||
/** | ||
* HAPNodeJSClient.prototype.HAPcontrol - Send a characteristic PUT Message to a particular homebridge instance | ||
* | ||
* @param {type} ipAddress IP Address of homebridge instance | ||
* @param {type} port Port of homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPNodeJSClient.prototype.HAPcontrol = function (ipAddress, port, body, callback, instance) { | ||
axios({ | ||
eventBus: this._eventBus, | ||
method: 'PUT', | ||
url: instance.url + '/characteristics', | ||
timeout: this.reqTimeout, | ||
headers: { | ||
'Content-Type': 'Application/json', | ||
'authorization': _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), | ||
'connection': 'keep-alive' | ||
}, | ||
data: body, | ||
validateStatus: function (status) { | ||
return true; // Resolve only if the status code is less than 500 | ||
} | ||
}).then(function (response) { | ||
// debug('HAPcontrol-then', response.status, response.statusText, response.headers, response.data, response.config); | ||
switch (response.status) { | ||
case 204: | ||
callback(null, null); | ||
break; | ||
case 207: | ||
callback(null, response.data); | ||
break; | ||
case 401: | ||
case 470: | ||
debug('Homebridge auth failed, invalid PIN %s %s:%s', _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), ipAddress, port, body, response.data); | ||
callback(new Error('Homebridge auth failed, invalid PIN ' + _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port))); | ||
break; | ||
default: | ||
debug('Homebridge Control failed %s:%s Status: %s ', ipAddress, port, response.status, body, response.data); | ||
callback(new Error('Homebridge control failed')); | ||
} | ||
}).catch(function (err) { | ||
// Response s/b 200 OK | ||
debug('HAPcontrol-catch', err); | ||
debug('Homebridge Control failed %s:%s', ipAddress, port, body, err.message); | ||
callback(err); | ||
}); | ||
}; | ||
/** | ||
* _reconnectServer - Reconnect to event server | ||
@@ -426,289 +701,11 @@ * | ||
/** | ||
* HAPNodeJSClient.prototype.HAPeventByDeviceID - Send a characteristic PUT Message to a particular homebridge instance, this maintains a socket connection for use in returning Events | ||
* | ||
* @param {type} deviceID deviceID homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPNodeJSClient.prototype.HAPeventByDeviceID = function (deviceID, body, callback) { | ||
// console.log('This-0', this); | ||
_mdnsLookup(deviceID, function (err, instance) { | ||
// debug('This-1', instance); | ||
if (err) { | ||
callback(err); | ||
} else { | ||
hapRequest({ | ||
eventBus: this._eventBus, | ||
method: 'PUT', | ||
deviceID: deviceID, | ||
url: instance.url + '/characteristics', | ||
timeout: this.reqTimeout, | ||
headers: { | ||
'Content-Type': 'Application/json', | ||
'authorization': _findPinByKey(deviceID), | ||
'connection': 'keep-alive' | ||
}, | ||
body: body | ||
}, function (err, response) { | ||
// Response s/b 200 OK | ||
if (err) { | ||
debug('Homebridge event reg failed %s:%s', instance.host, instance.port, body, err.message); | ||
_mdnsError(deviceID); | ||
callback(err); | ||
} else if (response.statusCode !== 207 && response.statusCode !== 204) { | ||
if (response.statusCode === 401 || response.statusCode === 470) { | ||
debug('Homebridge auth failed, invalid PIN %s', _findPinByKey(deviceID), deviceID, body, err, response.body); | ||
_mdnsError(deviceID); | ||
callback(new Error('Homebridge auth failed, invalid PIN ' + _findPinByKey(deviceID))); | ||
} else { | ||
debug('Homebridge event reg failed %s:%s Status: %s ', deviceID, response.statusCode, body, err, response.body); | ||
_mdnsError(deviceID); | ||
callback(new Error('Homebridge event reg failed')); | ||
} | ||
} else { | ||
var rsp; | ||
if (!this.eventRegistry[deviceID]) { | ||
this.eventRegistry[deviceID] = []; | ||
} | ||
// debug('1', JSON.parse(body).characteristics); | ||
this.eventRegistry[deviceID] = this.eventRegistry[deviceID].concat(JSON.parse(body).characteristics); | ||
// debug('2', JSON.stringify(this.eventRegistry[key])); | ||
this.eventRegistry[deviceID].sort((a, b) => (JSON.stringify(a) > JSON.stringify(b)) ? 1 : ((JSON.stringify(b) > JSON.stringify(a)) ? -1 : 0)); | ||
// debug('3', JSON.stringify(this.eventRegistry[key])); | ||
this.eventRegistry[deviceID] = Array.from(new Set(this.eventRegistry[deviceID].map(JSON.stringify))).map(JSON.parse); | ||
// debug('4', JSON.stringify(this.eventRegistry[key])); | ||
try { | ||
rsp = JSON.parse(response.body); | ||
} catch (ex) { | ||
debug('Homebridge Response Failed %s:%s', deviceID, response.statusCode, response.statusMessage); | ||
debug('Homebridge Response Failed %s:%s', deviceID, response.body, ex); | ||
callback(new Error(ex)); | ||
return; | ||
} | ||
callback(null, rsp); | ||
} | ||
}.bind(this)); | ||
} | ||
}.bind(this)); | ||
module.exports = { | ||
HAPNodeJSClient: HAPNodeJSClient | ||
}; | ||
/** | ||
* HAPNodeJSClient.prototype.HAPevent - Send a characteristic PUT Message to a particular homebridge instance, this maintains a socket connection for use in returning Events | ||
* | ||
* @param {type} ipAddress IP Address of homebridge instance | ||
* @param {type} port Port of homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPNodeJSClient.prototype.HAPevent = function (ipAddress, port, body, callback, instance) { | ||
hapRequest({ | ||
eventBus: this._eventBus, | ||
method: 'PUT', | ||
url: instance.url + '/characteristics', | ||
timeout: this.reqTimeout, | ||
headers: { | ||
'Content-Type': 'Application/json', | ||
'authorization': _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), | ||
'connection': 'keep-alive' | ||
}, | ||
body: body | ||
}, function (err, response) { | ||
// Response s/b 200 OK | ||
if (err) { | ||
debug('Homebridge event reg failed %s:%s', ipAddress, port, body, err.message); | ||
callback(err); | ||
} else if (response.statusCode !== 207 && response.statusCode !== 204) { | ||
if (response.statusCode === 401 || response.statusCode === 470) { | ||
debug('Homebridge auth failed, invalid PIN %s %s:%s', _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), ipAddress, port, body, err, response.body); | ||
callback(new Error('Homebridge auth failed, invalid PIN ' + _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port))); | ||
} else { | ||
debug('Homebridge event reg failed %s:%s Status: %s ', ipAddress, port, response.statusCode, body, err, response.body); | ||
callback(new Error('Homebridge event reg failed')); | ||
} | ||
} else { | ||
var rsp; | ||
var key = ipAddress + ':' + port; | ||
if (!this.eventRegistry[key]) { | ||
this.eventRegistry[key] = []; | ||
} | ||
// debug('1', JSON.parse(body).characteristics); | ||
this.eventRegistry[key] = this.eventRegistry[key].concat(JSON.parse(body).characteristics); | ||
// debug('2', JSON.stringify(this.eventRegistry[key])); | ||
this.eventRegistry[key].sort((a, b) => (JSON.stringify(a) > JSON.stringify(b)) ? 1 : ((JSON.stringify(b) > JSON.stringify(a)) ? -1 : 0)); | ||
// debug('3', JSON.stringify(this.eventRegistry[key])); | ||
this.eventRegistry[key] = Array.from(new Set(this.eventRegistry[key].map(JSON.stringify))).map(JSON.parse); | ||
// debug('4', JSON.stringify(this.eventRegistry[key])); | ||
try { | ||
rsp = JSON.parse(response.body); | ||
} catch (ex) { | ||
debug('Homebridge Response Failed %s:%s', ipAddress, port, response.statusCode, response.statusMessage); | ||
debug('Homebridge Response Failed %s:%s', ipAddress, port, response.body, ex); | ||
callback(new Error(ex)); | ||
return; | ||
} | ||
callback(null, rsp); | ||
} | ||
}.bind(this)); | ||
}; | ||
/** | ||
* HAPNodeJSClient.prototype.HAPresourceByDeviceID - Send a characteristic PUT Message to a particular homebridge instance using resource interface, ie camera | ||
* | ||
* @param {type} DeviceID DeviceID of homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPNodeJSClient.prototype.HAPresourceByDeviceID = function (deviceID, body, callback) { | ||
// console.log('This-0', this); | ||
_mdnsLookup(deviceID, function (err, instance) { | ||
// console.log('This-1', this); | ||
if (err) { | ||
callback(err); | ||
} else { | ||
HAPNodeJSClient.prototype.HAPresource.call(this, instance.host, instance.port, body, function (err, response) { | ||
if (err) { | ||
_mdnsError(deviceID); | ||
} | ||
callback(err, response); | ||
}, instance); | ||
} | ||
}.bind(this)); | ||
}; | ||
/** | ||
* HAPNodeJSClient.prototype.HAPresource - Send a characteristic PUT Message to a particular homebridge instance using resource interface, ie camera | ||
* | ||
* @param {type} ipAddress IP Address of homebridge instance | ||
* @param {type} port Port of homebridge instance | ||
* @param {type} body An array of HomeKit characteristic updates, [{ \"aid\": 2, \"iid\": 9, \"value\": 0}] | ||
* @param {type} callback Callback to execute upon completion of characteristic setting, function(err, response) | ||
*/ | ||
HAPNodeJSClient.prototype.HAPresource = function (ipAddress, port, body, callback, instance) { | ||
axios({ | ||
eventBus: this._eventBus, | ||
method: 'POST', | ||
url: instance.url + '/resource', | ||
timeout: this.reqTimeout, | ||
responseType: 'arraybuffer', | ||
headers: { | ||
'Content-Type': 'Application/json', | ||
'authorization': _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), | ||
'connection': 'keep-alive' | ||
}, | ||
data: body, | ||
validateStatus: function (status) { | ||
return true; // Resolve only if the status code is less than 500 | ||
} | ||
}).then(function (response) { | ||
// debug('HAPcontrol-then', response.status, response.statusText, response.headers, response.config); | ||
switch (response.status) { | ||
case 200: | ||
callback(null, response.data); | ||
break; | ||
case 401: | ||
case 470: | ||
debug('Homebridge auth failed, invalid PIN %s %s:%s', _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), ipAddress, port, body, response.data); | ||
callback(new Error('Homebridge auth failed, invalid PIN ' + _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port))); | ||
break; | ||
default: | ||
debug('Homebridge Resource failed %s:%s Status: %s ', ipAddress, port, response.status, body, response.data); | ||
callback(new Error('Homebridge Resource failed')); | ||
} | ||
}).catch(function (err) { | ||
// Response s/b 200 OK | ||
debug('HAPcontrol-catch', err); | ||
debug('Homebridge Resource failed %s:%s', ipAddress, port, body, err.message); | ||
callback(err); | ||
}); | ||
}; | ||
/** | ||
* HAPNodeJSClient.prototype.HAPstatusByDeviceID - Get current status for characteristics | ||
* | ||
* @param {type} deviceID deviceID of homebridge instance | ||
* @param {type} body description | ||
* @param {type} callback Callback to execute upon completion of characteristic getting, function(err, response) | ||
*/ | ||
HAPNodeJSClient.prototype.HAPstatusByDeviceID = function (deviceID, body, callback) { | ||
// console.log('This-0', this); | ||
_mdnsLookup(deviceID, function (err, instance) { | ||
// console.log('This-1', this); | ||
if (err) { | ||
callback(err); | ||
} else { | ||
HAPNodeJSClient.prototype.HAPstatus.call(this, instance.host, instance.port, body, function (err, response) { | ||
if (err) { | ||
_mdnsError(deviceID); | ||
} | ||
callback(err, response); | ||
}, instance); | ||
} | ||
}.bind(this)); | ||
}; | ||
/** | ||
* HAPNodeJSClient.prototype.HAPstatus - Get current status for characteristics | ||
* | ||
* @param {type} ipAddress IP Address of homebridge instance | ||
* @param {type} port Port of homebridge instance | ||
* @param {type} body description | ||
* @param {type} callback Callback to execute upon completion of characteristic getting, function(err, response) | ||
*/ | ||
HAPNodeJSClient.prototype.HAPstatus = function (ipAddress, port, body, callback, instance) { | ||
axios({ | ||
eventBus: this._eventBus, | ||
method: 'GET', | ||
url: instance.url + '/characteristics' + body, | ||
timeout: this.reqTimeout, | ||
headers: { | ||
'Content-Type': 'Application/json', | ||
'authorization': _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), | ||
'connection': 'keep-alive' | ||
}, | ||
validateStatus: function (status) { | ||
return true; // Resolve only if the status code is less than 500 | ||
} | ||
}).then(function (response) { | ||
// debug('HAPstatus-then', response.status, response.statusText, response.headers, response.data, response.config); | ||
switch (response.status) { | ||
case 200: | ||
callback(null, response.data); | ||
break; | ||
case 207: | ||
callback(null, response.data); | ||
break; | ||
case 401: | ||
case 470: | ||
debug('Homebridge auth failed, invalid PIN %s %s:%s', _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port), ipAddress, port, body, response.data); | ||
callback(new Error('Homebridge auth failed, invalid PIN ' + _findPinByKey(instance ? instance.deviceID : ipAddress + ':' + port))); | ||
break; | ||
default: | ||
debug('Homebridge Status failed %s:%s Status: %s ', ipAddress, port, response.status, body, response.data); | ||
callback(new Error('Homebridge Status failed')); | ||
} | ||
}).catch(function (err) { | ||
// Response s/b 200 OK | ||
debug('HAPstatus-catch', err); | ||
debug('Homebridge Status failed %s:%s', ipAddress, port, body, err.message); | ||
callback(err); | ||
}); | ||
}; | ||
function _getAccessories(instance, callback) { | ||
@@ -715,0 +712,0 @@ // debug('_getAccessories()', filter, instance.url + '/accessories'); |
@@ -313,2 +313,5 @@ const { _responseLineRegex } = require('./lib/httpParser'); | ||
}); | ||
afterAll(async () => { | ||
await homebridges.destroy(); | ||
}, 30000); | ||
}); |
{ | ||
"name": "hap-node-client", | ||
"version": "0.2.8-beta.0", | ||
"version": "0.2.8-beta.1", | ||
"description": "Client for Hap-NodeJS", | ||
@@ -15,2 +15,4 @@ "main": "HAPNodeJSClient.js", | ||
"devDependencies": { | ||
"@types/jest": "^29.5.10", | ||
"@types/node": "^20.10.0", | ||
"documentation": "^14.0.3", | ||
@@ -37,3 +39,3 @@ "hap-nodejs": "^0.11.2", | ||
"ignore": [], | ||
"exec": "jest", | ||
"exec": "jest --detectOpenHandles", | ||
"signal": "SIGTERM", | ||
@@ -40,0 +42,0 @@ "env": { |
93902
19
2102
7