node-hue-api
Version:
Philips Hue API Library for Node.js
120 lines (119 loc) • 5.79 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LocalBootstrap = void 0;
const https = __importStar(require("https"));
const Api_1 = require("../Api");
const httpClient = __importStar(require("./HttpClientFetch"));
const ApiError_1 = require("../../ApiError");
const Transport_1 = require("./Transport");
const sslCertificate_1 = require("./sslCertificate");
const urlUtil_1 = require("./urlUtil");
const DEBUG = /node-hue-api/.test(process.env.NODE_DEBUG || '');
const INITIAL_HTTPS_AGENT = new https.Agent({
rejectUnauthorized: false
});
class LocalBootstrap {
/**
* Create a Local Network Bootstrap for connecting to the Hue Bridge. The connection is ALWAYS over TLS/HTTPS.
*
* @param {String} hostname The hostname or ip address of the hue bridge on the local network.
* @param {number=} port The port number for the connections, defaults to 443 and should not need to be specified in the majority of use cases.
*/
constructor(hostname, rateLimits, port) {
this.baseUrl = (0, urlUtil_1.getHttpsUrl)(hostname, port || 443);
this.hostname = (0, urlUtil_1.cleanHostname)(hostname);
this.rateLimits = rateLimits;
}
/**
* Connects to the Hue Bridge using the local network.
*
* The connection will perform checks on the Hue Bridge TLS Certificate to verify it is correct before sending any
* sensitive information.
*
* @param {String=} username The username to use when connecting, can be null, but will severely limit the endpoints that you can call/access
* @param {String=} clientkey The clientkey for the user, used by the entertainment API, can be null
* @param {Number=} timeout The timeout for requests sent to the Hue Bridge. If not set will default to 20 seconds.
* @returns {Promise<Api>} The API for interacting with the hue bridge.
*/
connect(username, clientkey, timeout) {
const self = this, hostname = self.hostname, baseUrl = self.baseUrl.href;
return httpClient.request({
method: 'GET',
url: `${baseUrl}api/config`,
json: true,
httpsAgent: INITIAL_HTTPS_AGENT,
timeout: getTimeout(timeout),
}).then(res => {
const bridgeId = res.data.bridgeid.toLowerCase();
return (0, sslCertificate_1.getSSLCertificate)(hostname)
.then((cert) => {
const subjectCn = cert.subject.CN.toLowerCase();
if (DEBUG) {
console.log('Bridge Certificate:\n'
+ ` subject: ${JSON.stringify(cert.subject)}\n`
+ ` issuer: ${JSON.stringify(cert.subject)}\n`
+ ` valid from: ${cert.valid_from}\n`
+ ` valid to: ${cert.valid_to}\n`
+ ` serial number: ${cert.serialNumber}\n`);
console.log(`Performing validation of bridgeId "${bridgeId}" against certificate subject "${subjectCn}"; matched? ${subjectCn === bridgeId}`);
}
if (subjectCn === bridgeId) {
return new https.Agent({
keepAlive: true,
keepAliveMsecs: 10000,
maxSockets: 50,
// timeout: getTimeout(timeout), //node-fetch appears to ignore this
rejectUnauthorized: false,
// ca: cert.pemEncoded //TODO there are still issues here, as the certificate being self signed is failing somewhere deeper in TLS code
});
}
else {
throw new ApiError_1.ApiError('The hue bridge certificate does not match the expected issuer');
}
}).catch(error => {
throw new ApiError_1.ApiError(error);
})
.then(agent => {
const apiBaseUrl = `${baseUrl}api`, fetchConfig = {
baseURL: apiBaseUrl,
httpsAgent: agent,
timeout: getTimeout(timeout)
}, transport = new Transport_1.Transport(httpClient.create(fetchConfig), this.rateLimits.transportRateLimit, username), config = {
remote: false,
baseUrl: apiBaseUrl,
bridgeName: this.hostname,
clientKey: clientkey,
username: username,
};
return new Api_1.Api(config, transport, this.rateLimits);
});
});
}
}
exports.LocalBootstrap = LocalBootstrap;
function getTimeout(timeout) {
return timeout || 20000;
}