hap-nodejs 0.12.3-beta.11 → 0.12.3-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BridgedCore.js +14 -14
- package/dist/BridgedCore.js.map +1 -1
- package/dist/Core.js +14 -14
- package/dist/Core.js.map +1 -1
- package/dist/accessories/AirConditioner_accessory.js +24 -24
- package/dist/accessories/AirConditioner_accessory.js.map +1 -1
- package/dist/accessories/AppleTVRemote_accessory.js +23 -23
- package/dist/accessories/AppleTVRemote_accessory.js.map +1 -1
- package/dist/accessories/Camera_accessory.js +292 -373
- package/dist/accessories/Camera_accessory.js.map +1 -1
- package/dist/accessories/Fan_accessory.js +15 -21
- package/dist/accessories/Fan_accessory.js.map +1 -1
- package/dist/accessories/GarageDoorOpener_accessory.js +12 -12
- package/dist/accessories/GarageDoorOpener_accessory.js.map +1 -1
- package/dist/accessories/Light-AdaptiveLighting_accessory.js +26 -26
- package/dist/accessories/Light-AdaptiveLighting_accessory.js.map +1 -1
- package/dist/accessories/Light_accessory.js +45 -48
- package/dist/accessories/Light_accessory.js.map +1 -1
- package/dist/accessories/Lock_accessory.js +11 -11
- package/dist/accessories/Lock_accessory.js.map +1 -1
- package/dist/accessories/MotionSensor_accessory.js +8 -8
- package/dist/accessories/MotionSensor_accessory.js.map +1 -1
- package/dist/accessories/Outlet_accessory.js +10 -10
- package/dist/accessories/Outlet_accessory.js.map +1 -1
- package/dist/accessories/SmartSpeaker_accessory.js +11 -11
- package/dist/accessories/SmartSpeaker_accessory.js.map +1 -1
- package/dist/accessories/Sprinkler_accessory.js +19 -19
- package/dist/accessories/Sprinkler_accessory.js.map +1 -1
- package/dist/accessories/TV_accessory.js +17 -17
- package/dist/accessories/TV_accessory.js.map +1 -1
- package/dist/accessories/TemperatureSensor_accessory.js +6 -6
- package/dist/accessories/TemperatureSensor_accessory.js.map +1 -1
- package/dist/accessories/Wi-FiRouter_accessory.js +3 -3
- package/dist/accessories/Wi-FiRouter_accessory.js.map +1 -1
- package/dist/accessories/Wi-FiSatellite_accessory.js +4 -4
- package/dist/accessories/Wi-FiSatellite_accessory.js.map +1 -1
- package/dist/accessories/gstreamer-audioProducer.js +36 -47
- package/dist/accessories/gstreamer-audioProducer.js.map +1 -1
- package/dist/accessories/types.js +2 -2
- package/dist/accessories/types.js.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/Accessory.d.ts.map +1 -1
- package/dist/lib/Accessory.js +780 -1093
- package/dist/lib/Accessory.js.map +1 -1
- package/dist/lib/AccessoryLoader.js +40 -40
- package/dist/lib/AccessoryLoader.js.map +1 -1
- package/dist/lib/Advertiser.js +392 -524
- package/dist/lib/Advertiser.js.map +1 -1
- package/dist/lib/Bridge.js +6 -10
- package/dist/lib/Bridge.js.map +1 -1
- package/dist/lib/Characteristic.js +1465 -501
- package/dist/lib/Characteristic.js.map +1 -1
- package/dist/lib/HAPServer.js +215 -265
- package/dist/lib/HAPServer.js.map +1 -1
- package/dist/lib/Service.js +519 -322
- package/dist/lib/Service.js.map +1 -1
- package/dist/lib/camera/Camera.js +14 -14
- package/dist/lib/camera/Camera.js.map +1 -1
- package/dist/lib/camera/RTPProxy.js +112 -104
- package/dist/lib/camera/RTPProxy.js.map +1 -1
- package/dist/lib/camera/RTPStreamManagement.js +284 -257
- package/dist/lib/camera/RTPStreamManagement.js.map +1 -1
- package/dist/lib/camera/RecordingManagement.js +318 -381
- package/dist/lib/camera/RecordingManagement.js.map +1 -1
- package/dist/lib/camera/index.js +1 -1
- package/dist/lib/controller/AdaptiveLightingController.js +198 -209
- package/dist/lib/controller/AdaptiveLightingController.js.map +1 -1
- package/dist/lib/controller/CameraController.js +190 -249
- package/dist/lib/controller/CameraController.js.map +1 -1
- package/dist/lib/controller/DoorbellController.js +38 -39
- package/dist/lib/controller/DoorbellController.js.map +1 -1
- package/dist/lib/controller/RemoteController.js +343 -401
- package/dist/lib/controller/RemoteController.js.map +1 -1
- package/dist/lib/controller/index.js +1 -1
- package/dist/lib/datastream/DataStreamManagement.js +56 -57
- package/dist/lib/datastream/DataStreamManagement.js.map +1 -1
- package/dist/lib/datastream/DataStreamParser.js +259 -304
- package/dist/lib/datastream/DataStreamParser.js.map +1 -1
- package/dist/lib/datastream/DataStreamServer.d.ts.map +1 -1
- package/dist/lib/datastream/DataStreamServer.js +252 -269
- package/dist/lib/datastream/DataStreamServer.js.map +1 -1
- package/dist/lib/datastream/index.js +1 -1
- package/dist/lib/definitions/CharacteristicDefinitions.js +2089 -2858
- package/dist/lib/definitions/CharacteristicDefinitions.js.map +1 -1
- package/dist/lib/definitions/ServiceDefinitions.js +864 -1096
- package/dist/lib/definitions/ServiceDefinitions.js.map +1 -1
- package/dist/lib/definitions/generate-definitions.js +383 -679
- package/dist/lib/definitions/generate-definitions.js.map +1 -1
- package/dist/lib/definitions/generator-configuration.js +29 -29
- package/dist/lib/definitions/generator-configuration.js.map +1 -1
- package/dist/lib/definitions/index.js +1 -1
- package/dist/lib/model/AccessoryInfo.js +101 -136
- package/dist/lib/model/AccessoryInfo.js.map +1 -1
- package/dist/lib/model/ControllerStorage.js +86 -89
- package/dist/lib/model/ControllerStorage.js.map +1 -1
- package/dist/lib/model/HAPStorage.js +15 -16
- package/dist/lib/model/HAPStorage.js.map +1 -1
- package/dist/lib/model/IdentifierCache.js +49 -49
- package/dist/lib/model/IdentifierCache.js.map +1 -1
- package/dist/lib/tv/AccessControlManagement.js +40 -44
- package/dist/lib/tv/AccessControlManagement.js.map +1 -1
- package/dist/lib/util/checkName.d.ts +2 -1
- package/dist/lib/util/checkName.d.ts.map +1 -1
- package/dist/lib/util/checkName.js +7 -11
- package/dist/lib/util/checkName.js.map +1 -1
- package/dist/lib/util/clone.js +5 -27
- package/dist/lib/util/clone.js.map +1 -1
- package/dist/lib/util/color-utils.js +8 -12
- package/dist/lib/util/color-utils.js.map +1 -1
- package/dist/lib/util/eventedhttp.js +288 -384
- package/dist/lib/util/eventedhttp.js.map +1 -1
- package/dist/lib/util/hapCrypto.js +31 -32
- package/dist/lib/util/hapCrypto.js.map +1 -1
- package/dist/lib/util/hapStatusError.js +9 -12
- package/dist/lib/util/hapStatusError.js.map +1 -1
- package/dist/lib/util/net-utils.js +32 -53
- package/dist/lib/util/net-utils.js.map +1 -1
- package/dist/lib/util/once.js +3 -8
- package/dist/lib/util/once.js.map +1 -1
- package/dist/lib/util/promise-utils.js +8 -13
- package/dist/lib/util/promise-utils.js.map +1 -1
- package/dist/lib/util/request-util.js +2 -3
- package/dist/lib/util/request-util.js.map +1 -1
- package/dist/lib/util/time.js +5 -5
- package/dist/lib/util/time.js.map +1 -1
- package/dist/lib/util/tlv.js +57 -76
- package/dist/lib/util/tlv.js.map +1 -1
- package/dist/lib/util/uuid.js +15 -19
- package/dist/lib/util/uuid.js.map +1 -1
- package/package.json +2 -2
package/dist/lib/HAPServer.js
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HAPServer = exports.HAPServerEventTypes = exports.HAPPairingHTTPCode = exports.HAPHTTPCode = exports.Status = exports.Codes = exports.HAPStatus = exports.TLVErrorCode = void 0;
|
|
4
4
|
exports.IsKnownHAPStatusError = IsKnownHAPStatusError;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const crypto_1 = tslib_1.__importDefault(require("crypto"));
|
|
7
|
+
const debug_1 = tslib_1.__importDefault(require("debug"));
|
|
8
|
+
const events_1 = require("events");
|
|
9
|
+
const fast_srp_hap_1 = require("fast-srp-hap");
|
|
10
|
+
const tweetnacl_1 = tslib_1.__importDefault(require("tweetnacl"));
|
|
11
|
+
const url_1 = require("url");
|
|
12
|
+
const internal_types_1 = require("../internal-types");
|
|
13
|
+
const eventedhttp_1 = require("./util/eventedhttp");
|
|
14
|
+
const hapCrypto = tslib_1.__importStar(require("./util/hapCrypto"));
|
|
15
|
+
const once_1 = require("./util/once");
|
|
16
|
+
const tlv = tslib_1.__importStar(require("./util/tlv"));
|
|
17
|
+
const debug = (0, debug_1.default)("HAP-NodeJS:HAPServer");
|
|
18
18
|
/**
|
|
19
19
|
* TLV error codes for the `TLVValues.ERROR_CODE` field.
|
|
20
20
|
*
|
|
@@ -228,36 +228,36 @@ var HAPServerEventTypes;
|
|
|
228
228
|
* @group HAP Accessory Server
|
|
229
229
|
*/
|
|
230
230
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
231
|
+
class HAPServer extends events_1.EventEmitter {
|
|
232
|
+
accessoryInfo;
|
|
233
|
+
httpServer;
|
|
234
|
+
unsuccessfulPairAttempts = 0; // after 100 unsuccessful attempts the server won't accept any further attempts. Will currently be reset on a reboot
|
|
235
|
+
allowInsecureRequest;
|
|
236
|
+
constructor(accessoryInfo) {
|
|
237
|
+
super();
|
|
238
|
+
this.accessoryInfo = accessoryInfo;
|
|
239
|
+
this.allowInsecureRequest = false;
|
|
238
240
|
// internal server that does all the actual communication
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
return _this;
|
|
241
|
+
this.httpServer = new eventedhttp_1.EventedHTTPServer();
|
|
242
|
+
this.httpServer.on("listening" /* EventedHTTPServerEvent.LISTENING */, this.onListening.bind(this));
|
|
243
|
+
this.httpServer.on("request" /* EventedHTTPServerEvent.REQUEST */, this.handleRequestOnHAPConnection.bind(this));
|
|
244
|
+
this.httpServer.on("connection-closed" /* EventedHTTPServerEvent.CONNECTION_CLOSED */, this.handleConnectionClosed.bind(this));
|
|
244
245
|
}
|
|
245
|
-
|
|
246
|
-
if (port === void 0) { port = 0; }
|
|
246
|
+
listen(port = 0, host) {
|
|
247
247
|
if (host === "::") {
|
|
248
248
|
// this will work around "EAFNOSUPPORT: address family not supported" errors
|
|
249
249
|
// on systems where IPv6 is not supported/enabled, we just use the node default then by supplying undefined
|
|
250
250
|
host = undefined;
|
|
251
251
|
}
|
|
252
252
|
this.httpServer.listen(port, host);
|
|
253
|
-
}
|
|
254
|
-
|
|
253
|
+
}
|
|
254
|
+
stop() {
|
|
255
255
|
this.httpServer.stop();
|
|
256
|
-
}
|
|
257
|
-
|
|
256
|
+
}
|
|
257
|
+
destroy() {
|
|
258
258
|
this.stop();
|
|
259
259
|
this.removeAllListeners();
|
|
260
|
-
}
|
|
260
|
+
}
|
|
261
261
|
/**
|
|
262
262
|
* Send an even notification for given characteristic and changed value to all connected clients.
|
|
263
263
|
* If `originator` is specified, the given {@link HAPConnection} will be excluded from the broadcast.
|
|
@@ -269,48 +269,47 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
269
269
|
* @param immediateDelivery - The HAP spec requires some characteristics to be delivery immediately.
|
|
270
270
|
* Namely, for the {@link Characteristic.ButtonEvent} and the {@link Characteristic.ProgrammableSwitchEvent} characteristics.
|
|
271
271
|
*/
|
|
272
|
-
|
|
272
|
+
sendEventNotifications(aid, iid, value, originator, immediateDelivery) {
|
|
273
273
|
try {
|
|
274
274
|
this.httpServer.broadcastEvent(aid, iid, value, originator, immediateDelivery);
|
|
275
275
|
}
|
|
276
276
|
catch (error) {
|
|
277
277
|
console.warn("[" + this.accessoryInfo.username + "] Error when sending event notifications: " + error.message);
|
|
278
278
|
}
|
|
279
|
-
}
|
|
280
|
-
|
|
279
|
+
}
|
|
280
|
+
onListening(port, hostname) {
|
|
281
281
|
this.emit("listening" /* HAPServerEventTypes.LISTENING */, port, hostname);
|
|
282
|
-
}
|
|
282
|
+
}
|
|
283
283
|
// Called when an HTTP request was detected.
|
|
284
|
-
|
|
285
|
-
var _this = this;
|
|
284
|
+
handleRequestOnHAPConnection(connection, request, response) {
|
|
286
285
|
debug("[%s] HAP Request: %s %s", this.accessoryInfo.username, request.method, request.url);
|
|
287
|
-
|
|
288
|
-
request.on("data",
|
|
289
|
-
request.on("end",
|
|
290
|
-
|
|
291
|
-
|
|
286
|
+
const buffers = [];
|
|
287
|
+
request.on("data", data => buffers.push(data));
|
|
288
|
+
request.on("end", () => {
|
|
289
|
+
const url = new url_1.URL(request.url, "http://hap-nodejs.local"); // parse the url (query strings etc)
|
|
290
|
+
const handler = this.getHandler(url);
|
|
292
291
|
if (!handler) {
|
|
293
|
-
debug("[%s] WARNING: Handler for %s not implemented",
|
|
292
|
+
debug("[%s] WARNING: Handler for %s not implemented", this.accessoryInfo.username, request.url);
|
|
294
293
|
response.writeHead(404 /* HAPHTTPCode.NOT_FOUND */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
295
294
|
response.end(JSON.stringify({ status: -70409 /* HAPStatus.RESOURCE_DOES_NOT_EXIST */ }));
|
|
296
295
|
}
|
|
297
296
|
else {
|
|
298
|
-
|
|
297
|
+
const data = Buffer.concat(buffers);
|
|
299
298
|
try {
|
|
300
299
|
handler(connection, url, request, data, response);
|
|
301
300
|
}
|
|
302
301
|
catch (error) {
|
|
303
|
-
debug("[%s] Error executing route handler: %s",
|
|
302
|
+
debug("[%s] Error executing route handler: %s", this.accessoryInfo.username, error.stack);
|
|
304
303
|
response.writeHead(500 /* HAPHTTPCode.INTERNAL_SERVER_ERROR */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
305
304
|
response.end(JSON.stringify({ status: -70403 /* HAPStatus.RESOURCE_BUSY */ })); // resource busy try again, does somehow fit?
|
|
306
305
|
}
|
|
307
306
|
}
|
|
308
307
|
});
|
|
309
|
-
}
|
|
310
|
-
|
|
308
|
+
}
|
|
309
|
+
handleConnectionClosed(connection) {
|
|
311
310
|
this.emit("connection-closed" /* HAPServerEventTypes.CONNECTION_CLOSED */, connection);
|
|
312
|
-
}
|
|
313
|
-
|
|
311
|
+
}
|
|
312
|
+
getHandler(url) {
|
|
314
313
|
switch (url.pathname.toLowerCase()) {
|
|
315
314
|
case "/identify":
|
|
316
315
|
return this.handleIdentifyRequest.bind(this);
|
|
@@ -331,32 +330,31 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
331
330
|
default:
|
|
332
331
|
return undefined;
|
|
333
332
|
}
|
|
334
|
-
}
|
|
333
|
+
}
|
|
335
334
|
/**
|
|
336
335
|
* UNPAIRED Accessory identification.
|
|
337
336
|
*/
|
|
338
|
-
|
|
339
|
-
var _this = this;
|
|
337
|
+
handleIdentifyRequest(connection, url, request, data, response) {
|
|
340
338
|
// POST body is empty
|
|
341
339
|
if (this.accessoryInfo.paired() && !this.allowInsecureRequest) {
|
|
342
340
|
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
343
341
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
344
342
|
return;
|
|
345
343
|
}
|
|
346
|
-
this.emit("identify" /* HAPServerEventTypes.IDENTIFY */, (0, once_1.once)(
|
|
344
|
+
this.emit("identify" /* HAPServerEventTypes.IDENTIFY */, (0, once_1.once)(err => {
|
|
347
345
|
if (!err) {
|
|
348
|
-
debug("[%s] Identification success",
|
|
346
|
+
debug("[%s] Identification success", this.accessoryInfo.username);
|
|
349
347
|
response.writeHead(204 /* HAPHTTPCode.NO_CONTENT */);
|
|
350
348
|
response.end();
|
|
351
349
|
}
|
|
352
350
|
else {
|
|
353
|
-
debug("[%s] Identification error: %s",
|
|
351
|
+
debug("[%s] Identification error: %s", this.accessoryInfo.username, err.message);
|
|
354
352
|
response.writeHead(500 /* HAPHTTPCode.INTERNAL_SERVER_ERROR */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
355
353
|
response.end(JSON.stringify({ status: -70403 /* HAPStatus.RESOURCE_BUSY */ }));
|
|
356
354
|
}
|
|
357
355
|
}));
|
|
358
|
-
}
|
|
359
|
-
|
|
356
|
+
}
|
|
357
|
+
handlePairSetup(connection, url, request, data, response) {
|
|
360
358
|
// Can only be directly paired with one iOS device
|
|
361
359
|
if (!this.allowInsecureRequest && this.accessoryInfo.paired()) {
|
|
362
360
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
@@ -369,8 +367,8 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
369
367
|
response.end(tlv.encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, 5 /* TLVErrorCode.MAX_TRIES */));
|
|
370
368
|
return;
|
|
371
369
|
}
|
|
372
|
-
|
|
373
|
-
|
|
370
|
+
const tlvData = tlv.decode(data);
|
|
371
|
+
const sequence = tlvData[6 /* TLVValues.SEQUENCE_NUM */][0]; // value is single byte with sequence number
|
|
374
372
|
if (sequence === 1 /* PairingStates.M1 */) {
|
|
375
373
|
this.handlePairSetupM1(connection, request, response);
|
|
376
374
|
}
|
|
@@ -386,34 +384,33 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
386
384
|
response.end(tlv.encode(6 /* TLVValues.STATE */, sequence + 1, 7 /* TLVValues.ERROR_CODE */, 1 /* TLVErrorCode.UNKNOWN */));
|
|
387
385
|
return;
|
|
388
386
|
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
var _this = this;
|
|
387
|
+
}
|
|
388
|
+
handlePairSetupM1(connection, request, response) {
|
|
392
389
|
debug("[%s] Pair step 1/5", this.accessoryInfo.username);
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
fast_srp_hap_1.SRP.genKey(32).then(
|
|
390
|
+
const salt = crypto_1.default.randomBytes(16);
|
|
391
|
+
const srpParams = fast_srp_hap_1.SRP.params.hap;
|
|
392
|
+
fast_srp_hap_1.SRP.genKey(32).then(key => {
|
|
396
393
|
// create a new SRP server
|
|
397
|
-
|
|
398
|
-
|
|
394
|
+
const srpServer = new fast_srp_hap_1.SrpServer(srpParams, salt, Buffer.from("Pair-Setup"), Buffer.from(this.accessoryInfo.pincode), key);
|
|
395
|
+
const srpB = srpServer.computeB();
|
|
399
396
|
// attach it to the current TCP session
|
|
400
397
|
connection.srpServer = srpServer;
|
|
401
398
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
402
399
|
response.end(tlv.encode(6 /* TLVValues.SEQUENCE_NUM */, 2 /* PairingStates.M2 */, 2 /* TLVValues.SALT */, salt, 3 /* TLVValues.PUBLIC_KEY */, srpB));
|
|
403
400
|
connection._pairSetupState = 2 /* PairingStates.M2 */;
|
|
404
|
-
}).catch(
|
|
405
|
-
debug("[%s] Error occurred when generating srp key: %s",
|
|
401
|
+
}).catch(error => {
|
|
402
|
+
debug("[%s] Error occurred when generating srp key: %s", this.accessoryInfo.username, error.message);
|
|
406
403
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
407
404
|
response.end(tlv.encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, 1 /* TLVErrorCode.UNKNOWN */));
|
|
408
405
|
return;
|
|
409
406
|
});
|
|
410
|
-
}
|
|
411
|
-
|
|
407
|
+
}
|
|
408
|
+
handlePairSetupM3(connection, request, response, tlvData) {
|
|
412
409
|
debug("[%s] Pair step 2/5", this.accessoryInfo.username);
|
|
413
|
-
|
|
414
|
-
|
|
410
|
+
const A = tlvData[3 /* TLVValues.PUBLIC_KEY */]; // "A is a public key that exists only for a single login session."
|
|
411
|
+
const M1 = tlvData[4 /* TLVValues.PASSWORD_PROOF */]; // "M1 is the proof that you actually know your own password."
|
|
415
412
|
// pull the SRP server we created in stepOne out of the current session
|
|
416
|
-
|
|
413
|
+
const srpServer = connection.srpServer;
|
|
417
414
|
srpServer.setA(A);
|
|
418
415
|
try {
|
|
419
416
|
srpServer.checkM1(M1);
|
|
@@ -428,25 +425,25 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
428
425
|
return;
|
|
429
426
|
}
|
|
430
427
|
// "M2 is the proof that the server actually knows your password."
|
|
431
|
-
|
|
428
|
+
const M2 = srpServer.computeM2();
|
|
432
429
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
433
430
|
response.end(tlv.encode(6 /* TLVValues.SEQUENCE_NUM */, 4 /* PairingStates.M4 */, 4 /* TLVValues.PASSWORD_PROOF */, M2));
|
|
434
431
|
connection._pairSetupState = 4 /* PairingStates.M4 */;
|
|
435
|
-
}
|
|
436
|
-
|
|
432
|
+
}
|
|
433
|
+
handlePairSetupM5(connection, request, response, tlvData) {
|
|
437
434
|
debug("[%s] Pair step 3/5", this.accessoryInfo.username);
|
|
438
435
|
// pull the SRP server we created in stepOne out of the current session
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
436
|
+
const srpServer = connection.srpServer;
|
|
437
|
+
const encryptedData = tlvData[5 /* TLVValues.ENCRYPTED_DATA */];
|
|
438
|
+
const messageData = Buffer.alloc(encryptedData.length - 16);
|
|
439
|
+
const authTagData = Buffer.alloc(16);
|
|
443
440
|
encryptedData.copy(messageData, 0, 0, encryptedData.length - 16);
|
|
444
441
|
encryptedData.copy(authTagData, 0, encryptedData.length - 16, encryptedData.length);
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
442
|
+
const S_private = srpServer.computeK();
|
|
443
|
+
const encSalt = Buffer.from("Pair-Setup-Encrypt-Salt");
|
|
444
|
+
const encInfo = Buffer.from("Pair-Setup-Encrypt-Info");
|
|
445
|
+
const outputKey = hapCrypto.HKDF("sha512", encSalt, S_private, encInfo, 32);
|
|
446
|
+
let plaintext;
|
|
450
447
|
try {
|
|
451
448
|
plaintext = hapCrypto.chacha20_poly1305_decryptAndVerify(outputKey, Buffer.from("PS-Msg05"), null, messageData, authTagData);
|
|
452
449
|
}
|
|
@@ -458,20 +455,20 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
458
455
|
return;
|
|
459
456
|
}
|
|
460
457
|
// decode the client payload and pass it on to the next step
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
458
|
+
const M5Packet = tlv.decode(plaintext);
|
|
459
|
+
const clientUsername = M5Packet[1 /* TLVValues.USERNAME */];
|
|
460
|
+
const clientLTPK = M5Packet[3 /* TLVValues.PUBLIC_KEY */];
|
|
461
|
+
const clientProof = M5Packet[10 /* TLVValues.PROOF */];
|
|
465
462
|
this.handlePairSetupM5_2(connection, request, response, clientUsername, clientLTPK, clientProof, outputKey);
|
|
466
|
-
}
|
|
463
|
+
}
|
|
467
464
|
// M5-2
|
|
468
|
-
|
|
465
|
+
handlePairSetupM5_2(connection, request, response, clientUsername, clientLTPK, clientProof, hkdfEncKey) {
|
|
469
466
|
debug("[%s] Pair step 4/5", this.accessoryInfo.username);
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
467
|
+
const S_private = connection.srpServer.computeK();
|
|
468
|
+
const controllerSalt = Buffer.from("Pair-Setup-Controller-Sign-Salt");
|
|
469
|
+
const controllerInfo = Buffer.from("Pair-Setup-Controller-Sign-Info");
|
|
470
|
+
const outputKey = hapCrypto.HKDF("sha512", controllerSalt, S_private, controllerInfo, 32);
|
|
471
|
+
const completeData = Buffer.concat([outputKey, clientUsername, clientLTPK]);
|
|
475
472
|
if (!tweetnacl_1.default.sign.detached.verify(completeData, clientProof, clientLTPK)) {
|
|
476
473
|
debug("[%s] Invalid signature", this.accessoryInfo.username);
|
|
477
474
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
@@ -480,26 +477,25 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
480
477
|
return;
|
|
481
478
|
}
|
|
482
479
|
this.handlePairSetupM5_3(connection, request, response, clientUsername, clientLTPK, hkdfEncKey);
|
|
483
|
-
}
|
|
480
|
+
}
|
|
484
481
|
// M5 - F + M6
|
|
485
|
-
|
|
486
|
-
var _this = this;
|
|
482
|
+
handlePairSetupM5_3(connection, request, response, clientUsername, clientLTPK, hkdfEncKey) {
|
|
487
483
|
debug("[%s] Pair step 5/5", this.accessoryInfo.username);
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
484
|
+
const S_private = connection.srpServer.computeK();
|
|
485
|
+
const accessorySalt = Buffer.from("Pair-Setup-Accessory-Sign-Salt");
|
|
486
|
+
const accessoryInfo = Buffer.from("Pair-Setup-Accessory-Sign-Info");
|
|
487
|
+
const outputKey = hapCrypto.HKDF("sha512", accessorySalt, S_private, accessoryInfo, 32);
|
|
488
|
+
const serverLTPK = this.accessoryInfo.signPk;
|
|
489
|
+
const usernameData = Buffer.from(this.accessoryInfo.username);
|
|
490
|
+
const material = Buffer.concat([outputKey, usernameData, serverLTPK]);
|
|
491
|
+
const privateKey = Buffer.from(this.accessoryInfo.signSk);
|
|
492
|
+
const serverProof = tweetnacl_1.default.sign.detached(material, privateKey);
|
|
493
|
+
const message = tlv.encode(1 /* TLVValues.USERNAME */, usernameData, 3 /* TLVValues.PUBLIC_KEY */, serverLTPK, 10 /* TLVValues.PROOF */, serverProof);
|
|
494
|
+
const encrypted = hapCrypto.chacha20_poly1305_encryptAndSeal(hkdfEncKey, Buffer.from("PS-Msg06"), null, message);
|
|
499
495
|
// finally, notify listeners that we have been paired with a client
|
|
500
|
-
this.emit("pair" /* HAPServerEventTypes.PAIR */, clientUsername.toString(), clientLTPK, (0, once_1.once)(
|
|
496
|
+
this.emit("pair" /* HAPServerEventTypes.PAIR */, clientUsername.toString(), clientLTPK, (0, once_1.once)(err => {
|
|
501
497
|
if (err) {
|
|
502
|
-
debug("[%s] Error adding pairing info: %s",
|
|
498
|
+
debug("[%s] Error adding pairing info: %s", this.accessoryInfo.username, err.message);
|
|
503
499
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
504
500
|
response.end(tlv.encode(6 /* TLVValues.SEQUENCE_NUM */, 6 /* PairingStates.M6 */, 7 /* TLVValues.ERROR_CODE */, 1 /* TLVErrorCode.UNKNOWN */));
|
|
505
501
|
connection._pairSetupState = undefined;
|
|
@@ -510,10 +506,10 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
510
506
|
response.end(tlv.encode(6 /* TLVValues.SEQUENCE_NUM */, 6 /* PairingStates.M6 */, 5 /* TLVValues.ENCRYPTED_DATA */, Buffer.concat([encrypted.ciphertext, encrypted.authTag])));
|
|
511
507
|
connection._pairSetupState = undefined;
|
|
512
508
|
}));
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
509
|
+
}
|
|
510
|
+
handlePairVerify(connection, url, request, data, response) {
|
|
511
|
+
const tlvData = tlv.decode(data);
|
|
512
|
+
const sequence = tlvData[6 /* TLVValues.SEQUENCE_NUM */][0]; // value is single byte with sequence number
|
|
517
513
|
if (sequence === 1 /* PairingStates.M1 */) {
|
|
518
514
|
this.handlePairVerifyM1(connection, request, response, tlvData);
|
|
519
515
|
}
|
|
@@ -526,40 +522,40 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
526
522
|
response.end(tlv.encode(6 /* TLVValues.STATE */, sequence + 1, 7 /* TLVValues.ERROR_CODE */, 1 /* TLVErrorCode.UNKNOWN */));
|
|
527
523
|
return;
|
|
528
524
|
}
|
|
529
|
-
}
|
|
530
|
-
|
|
525
|
+
}
|
|
526
|
+
handlePairVerifyM1(connection, request, response, tlvData) {
|
|
531
527
|
debug("[%s] Pair verify step 1/2", this.accessoryInfo.username);
|
|
532
|
-
|
|
528
|
+
const clientPublicKey = tlvData[3 /* TLVValues.PUBLIC_KEY */]; // Buffer
|
|
533
529
|
// generate new encryption keys for this session
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
530
|
+
const keyPair = hapCrypto.generateCurve25519KeyPair();
|
|
531
|
+
const secretKey = Buffer.from(keyPair.secretKey);
|
|
532
|
+
const publicKey = Buffer.from(keyPair.publicKey);
|
|
533
|
+
const sharedSec = Buffer.from(hapCrypto.generateCurve25519SharedSecKey(secretKey, clientPublicKey));
|
|
534
|
+
const usernameData = Buffer.from(this.accessoryInfo.username);
|
|
535
|
+
const material = Buffer.concat([publicKey, usernameData, clientPublicKey]);
|
|
536
|
+
const privateKey = Buffer.from(this.accessoryInfo.signSk);
|
|
537
|
+
const serverProof = tweetnacl_1.default.sign.detached(material, privateKey);
|
|
538
|
+
const encSalt = Buffer.from("Pair-Verify-Encrypt-Salt");
|
|
539
|
+
const encInfo = Buffer.from("Pair-Verify-Encrypt-Info");
|
|
540
|
+
const outputKey = hapCrypto.HKDF("sha512", encSalt, sharedSec, encInfo, 32).slice(0, 32);
|
|
545
541
|
connection.encryption = new eventedhttp_1.HAPEncryption(clientPublicKey, secretKey, publicKey, sharedSec, outputKey);
|
|
546
542
|
// compose the response data in TLV format
|
|
547
|
-
|
|
548
|
-
|
|
543
|
+
const message = tlv.encode(1 /* TLVValues.USERNAME */, usernameData, 10 /* TLVValues.PROOF */, serverProof);
|
|
544
|
+
const encrypted = hapCrypto.chacha20_poly1305_encryptAndSeal(outputKey, Buffer.from("PV-Msg02"), null, message);
|
|
549
545
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
550
546
|
response.end(tlv.encode(6 /* TLVValues.SEQUENCE_NUM */, 2 /* PairingStates.M2 */, 5 /* TLVValues.ENCRYPTED_DATA */, Buffer.concat([encrypted.ciphertext, encrypted.authTag]), 3 /* TLVValues.PUBLIC_KEY */, publicKey));
|
|
551
547
|
connection._pairVerifyState = 2 /* PairingStates.M2 */;
|
|
552
|
-
}
|
|
553
|
-
|
|
548
|
+
}
|
|
549
|
+
handlePairVerifyM3(connection, request, response, objects) {
|
|
554
550
|
debug("[%s] Pair verify step 2/2", this.accessoryInfo.username);
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
551
|
+
const encryptedData = objects[5 /* TLVValues.ENCRYPTED_DATA */];
|
|
552
|
+
const messageData = Buffer.alloc(encryptedData.length - 16);
|
|
553
|
+
const authTagData = Buffer.alloc(16);
|
|
558
554
|
encryptedData.copy(messageData, 0, 0, encryptedData.length - 16);
|
|
559
555
|
encryptedData.copy(authTagData, 0, encryptedData.length - 16, encryptedData.length);
|
|
560
556
|
// instance of HAPEncryption (created in handlePairVerifyStepOne)
|
|
561
|
-
|
|
562
|
-
|
|
557
|
+
const enc = connection.encryption;
|
|
558
|
+
let plaintext;
|
|
563
559
|
try {
|
|
564
560
|
plaintext = hapCrypto.chacha20_poly1305_decryptAndVerify(enc.hkdfPairEncryptionKey, Buffer.from("PV-Msg03"), null, messageData, authTagData);
|
|
565
561
|
}
|
|
@@ -570,12 +566,12 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
570
566
|
connection._pairVerifyState = undefined;
|
|
571
567
|
return;
|
|
572
568
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
569
|
+
const decoded = tlv.decode(plaintext);
|
|
570
|
+
const clientUsername = decoded[1 /* TLVValues.USERNAME */];
|
|
571
|
+
const proof = decoded[10 /* TLVValues.PROOF */];
|
|
572
|
+
const material = Buffer.concat([enc.clientPublicKey, clientUsername, enc.publicKey]);
|
|
577
573
|
// since we're paired, we should have the public key stored for this client
|
|
578
|
-
|
|
574
|
+
const clientPublicKey = this.accessoryInfo.getClientPublicKey(clientUsername.toString());
|
|
579
575
|
// if we're not actually paired, then there's nothing to verify - this client thinks it's paired with us, but we
|
|
580
576
|
// disagree. Respond with invalid request (seems to match HomeKit Accessory Simulator behavior)
|
|
581
577
|
if (!clientPublicKey) {
|
|
@@ -598,90 +594,89 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
598
594
|
// now that the client has been verified, we must "upgrade" our pseudo-HTTP connection to include
|
|
599
595
|
// TCP-level encryption. We'll do this by adding some more encryption vars to the session, and using them
|
|
600
596
|
// in future calls to onEncrypt, onDecrypt.
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
597
|
+
const encSalt = Buffer.from("Control-Salt");
|
|
598
|
+
const infoRead = Buffer.from("Control-Read-Encryption-Key");
|
|
599
|
+
const infoWrite = Buffer.from("Control-Write-Encryption-Key");
|
|
604
600
|
enc.accessoryToControllerKey = hapCrypto.HKDF("sha512", encSalt, enc.sharedSecret, infoRead, 32);
|
|
605
601
|
enc.controllerToAccessoryKey = hapCrypto.HKDF("sha512", encSalt, enc.sharedSecret, infoWrite, 32);
|
|
606
602
|
// Our connection is now completely setup. We now want to subscribe this connection to special
|
|
607
603
|
connection.connectionAuthenticated(clientUsername.toString());
|
|
608
604
|
connection._pairVerifyState = undefined;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
var _this = this;
|
|
605
|
+
}
|
|
606
|
+
handlePairings(connection, url, request, data, response) {
|
|
612
607
|
// Only accept /pairing request if there is a secure session
|
|
613
608
|
if (!this.allowInsecureRequest && !connection.isAuthenticated()) {
|
|
614
609
|
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
615
610
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
616
611
|
return;
|
|
617
612
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
613
|
+
const objects = tlv.decode(data);
|
|
614
|
+
const method = objects[0 /* TLVValues.METHOD */][0]; // value is single byte with request type
|
|
615
|
+
const state = objects[6 /* TLVValues.STATE */][0];
|
|
621
616
|
if (state !== 1 /* PairingStates.M1 */) {
|
|
622
617
|
return;
|
|
623
618
|
}
|
|
624
619
|
if (method === 3 /* PairMethods.ADD_PAIRING */) {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
this.emit("add-pairing" /* HAPServerEventTypes.ADD_PAIRING */, connection, identifier, publicKey, permissions, (0, once_1.once)(
|
|
620
|
+
const identifier = objects[1 /* TLVValues.IDENTIFIER */].toString();
|
|
621
|
+
const publicKey = objects[3 /* TLVValues.PUBLIC_KEY */];
|
|
622
|
+
const permissions = objects[11 /* TLVValues.PERMISSIONS */][0];
|
|
623
|
+
this.emit("add-pairing" /* HAPServerEventTypes.ADD_PAIRING */, connection, identifier, publicKey, permissions, (0, once_1.once)((error) => {
|
|
629
624
|
if (error > 0) {
|
|
630
|
-
debug("[%s] Pairings: failed ADD_PAIRING with code %d",
|
|
625
|
+
debug("[%s] Pairings: failed ADD_PAIRING with code %d", this.accessoryInfo.username, error);
|
|
631
626
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
632
627
|
response.end(tlv.encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, error));
|
|
633
628
|
return;
|
|
634
629
|
}
|
|
635
630
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
636
631
|
response.end(tlv.encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */));
|
|
637
|
-
debug("[%s] Pairings: successfully executed ADD_PAIRING",
|
|
632
|
+
debug("[%s] Pairings: successfully executed ADD_PAIRING", this.accessoryInfo.username);
|
|
638
633
|
}));
|
|
639
634
|
}
|
|
640
635
|
else if (method === 4 /* PairMethods.REMOVE_PAIRING */) {
|
|
641
|
-
|
|
642
|
-
this.emit("remove-pairing" /* HAPServerEventTypes.REMOVE_PAIRING */, connection, identifier, (0, once_1.once)(
|
|
636
|
+
const identifier = objects[1 /* TLVValues.IDENTIFIER */].toString();
|
|
637
|
+
this.emit("remove-pairing" /* HAPServerEventTypes.REMOVE_PAIRING */, connection, identifier, (0, once_1.once)((error) => {
|
|
643
638
|
if (error > 0) {
|
|
644
|
-
debug("[%s] Pairings: failed REMOVE_PAIRING with code %d",
|
|
639
|
+
debug("[%s] Pairings: failed REMOVE_PAIRING with code %d", this.accessoryInfo.username, error);
|
|
645
640
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
646
641
|
response.end(tlv.encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, error));
|
|
647
642
|
return;
|
|
648
643
|
}
|
|
649
644
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
650
645
|
response.end(tlv.encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */));
|
|
651
|
-
debug("[%s] Pairings: successfully executed REMOVE_PAIRING",
|
|
646
|
+
debug("[%s] Pairings: successfully executed REMOVE_PAIRING", this.accessoryInfo.username);
|
|
652
647
|
}));
|
|
653
648
|
}
|
|
654
649
|
else if (method === 5 /* PairMethods.LIST_PAIRINGS */) {
|
|
655
|
-
this.emit("list-pairings" /* HAPServerEventTypes.LIST_PAIRINGS */, connection, (0, once_1.once)(
|
|
650
|
+
this.emit("list-pairings" /* HAPServerEventTypes.LIST_PAIRINGS */, connection, (0, once_1.once)((error, data) => {
|
|
656
651
|
if (error > 0) {
|
|
657
|
-
debug("[%s] Pairings: failed LIST_PAIRINGS with code %d",
|
|
652
|
+
debug("[%s] Pairings: failed LIST_PAIRINGS with code %d", this.accessoryInfo.username, error);
|
|
658
653
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" });
|
|
659
654
|
response.end(tlv.encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, error));
|
|
660
655
|
return;
|
|
661
656
|
}
|
|
662
657
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
663
|
-
|
|
664
|
-
data.forEach(
|
|
658
|
+
const tlvList = [];
|
|
659
|
+
data.forEach((value, index) => {
|
|
665
660
|
if (index > 0) {
|
|
666
661
|
tlvList.push(255 /* TLVValues.SEPARATOR */, Buffer.alloc(0));
|
|
667
662
|
}
|
|
668
663
|
tlvList.push(1 /* TLVValues.IDENTIFIER */, value.username, 3 /* TLVValues.PUBLIC_KEY */, value.publicKey, 11 /* TLVValues.PERMISSIONS */, value.permission);
|
|
669
664
|
});
|
|
670
|
-
|
|
665
|
+
const list = tlv.encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, ...tlvList);
|
|
671
666
|
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { "Content-Type": "application/pairing+tlv8" /* HAPMimeTypes.PAIRING_TLV8 */ });
|
|
672
667
|
response.end(list);
|
|
673
|
-
debug("[%s] Pairings: successfully executed LIST_PAIRINGS",
|
|
668
|
+
debug("[%s] Pairings: successfully executed LIST_PAIRINGS", this.accessoryInfo.username);
|
|
674
669
|
}));
|
|
675
670
|
}
|
|
676
|
-
}
|
|
677
|
-
|
|
671
|
+
}
|
|
672
|
+
handleAccessories(connection, url, request, data, response) {
|
|
678
673
|
if (!this.allowInsecureRequest && !connection.isAuthenticated()) {
|
|
679
674
|
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
680
675
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
681
676
|
return;
|
|
682
677
|
}
|
|
683
678
|
// call out to listeners to retrieve the latest accessories JSON
|
|
684
|
-
this.emit("accessories" /* HAPServerEventTypes.ACCESSORIES */, connection, (0, once_1.once)(
|
|
679
|
+
this.emit("accessories" /* HAPServerEventTypes.ACCESSORIES */, connection, (0, once_1.once)((error, result) => {
|
|
685
680
|
if (error) {
|
|
686
681
|
response.writeHead(error.httpCode, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
687
682
|
response.end(JSON.stringify({ status: error.status }));
|
|
@@ -691,88 +686,56 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
691
686
|
response.end(JSON.stringify(result));
|
|
692
687
|
}
|
|
693
688
|
}));
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
var e_1, _a;
|
|
689
|
+
}
|
|
690
|
+
handleCharacteristics(connection, url, request, data, response) {
|
|
697
691
|
if (!this.allowInsecureRequest && !connection.isAuthenticated()) {
|
|
698
692
|
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
699
693
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
700
694
|
return;
|
|
701
695
|
}
|
|
702
696
|
if (request.method === "GET") {
|
|
703
|
-
|
|
704
|
-
|
|
697
|
+
const searchParams = url.searchParams;
|
|
698
|
+
const idParam = searchParams.get("id");
|
|
705
699
|
if (!idParam) {
|
|
706
700
|
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
707
701
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
708
702
|
return;
|
|
709
703
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
iid: parseInt(split[1], 10), // (characteristic) instance id
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
722
|
-
finally {
|
|
723
|
-
try {
|
|
724
|
-
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
725
|
-
}
|
|
726
|
-
finally { if (e_1) throw e_1.error; }
|
|
704
|
+
const ids = [];
|
|
705
|
+
for (const entry of idParam.split(",")) { // ["1.9","2.14"]
|
|
706
|
+
const split = entry.split("."); // ["1","9"]
|
|
707
|
+
ids.push({
|
|
708
|
+
aid: parseInt(split[0], 10), // accessory id
|
|
709
|
+
iid: parseInt(split[1], 10), // (characteristic) instance id
|
|
710
|
+
});
|
|
727
711
|
}
|
|
728
|
-
|
|
712
|
+
const readRequest = {
|
|
729
713
|
ids: ids,
|
|
730
714
|
includeMeta: (0, internal_types_1.consideredTrue)(searchParams.get("meta")),
|
|
731
715
|
includePerms: (0, internal_types_1.consideredTrue)(searchParams.get("perms")),
|
|
732
716
|
includeType: (0, internal_types_1.consideredTrue)(searchParams.get("type")),
|
|
733
717
|
includeEvent: (0, internal_types_1.consideredTrue)(searchParams.get("ev")),
|
|
734
718
|
};
|
|
735
|
-
this.emit("get-characteristics" /* HAPServerEventTypes.GET_CHARACTERISTICS */, connection, readRequest, (0, once_1.once)(
|
|
736
|
-
var e_2, _a, e_3, _b;
|
|
719
|
+
this.emit("get-characteristics" /* HAPServerEventTypes.GET_CHARACTERISTICS */, connection, readRequest, (0, once_1.once)((error, readResponse) => {
|
|
737
720
|
if (error) {
|
|
738
721
|
response.writeHead(error.httpCode, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
739
722
|
response.end(JSON.stringify({ status: error.status }));
|
|
740
723
|
return;
|
|
741
724
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
errorOccurred = true;
|
|
749
|
-
break;
|
|
750
|
-
}
|
|
725
|
+
const characteristics = readResponse.characteristics;
|
|
726
|
+
let errorOccurred = false; // determine if we send a 207 Multi-Status
|
|
727
|
+
for (const data of characteristics) {
|
|
728
|
+
if (data.status) {
|
|
729
|
+
errorOccurred = true;
|
|
730
|
+
break;
|
|
751
731
|
}
|
|
752
732
|
}
|
|
753
|
-
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
754
|
-
finally {
|
|
755
|
-
try {
|
|
756
|
-
if (characteristics_1_1 && !characteristics_1_1.done && (_a = characteristics_1.return)) _a.call(characteristics_1);
|
|
757
|
-
}
|
|
758
|
-
finally { if (e_2) throw e_2.error; }
|
|
759
|
-
}
|
|
760
733
|
if (errorOccurred) { // on a 207 Multi-Status EVERY characteristic MUST include a status property
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
if (!data_2.status) { // a status is undefined if the request was successful
|
|
765
|
-
data_2.status = 0 /* HAPStatus.SUCCESS */; // a value of zero indicates success
|
|
766
|
-
}
|
|
734
|
+
for (const data of characteristics) {
|
|
735
|
+
if (!data.status) { // a status is undefined if the request was successful
|
|
736
|
+
data.status = 0 /* HAPStatus.SUCCESS */; // a value of zero indicates success
|
|
767
737
|
}
|
|
768
738
|
}
|
|
769
|
-
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
770
|
-
finally {
|
|
771
|
-
try {
|
|
772
|
-
if (characteristics_2_1 && !characteristics_2_1.done && (_b = characteristics_2.return)) _b.call(characteristics_2);
|
|
773
|
-
}
|
|
774
|
-
finally { if (e_3) throw e_3.error; }
|
|
775
|
-
}
|
|
776
739
|
}
|
|
777
740
|
// 207 "multi-status" is returned when an error occurs reading a characteristic. otherwise 200 is returned
|
|
778
741
|
response.writeHead(errorOccurred ? 207 /* HAPHTTPCode.MULTI_STATUS */ : 200 /* HAPHTTPCode.OK */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
@@ -792,32 +755,21 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
792
755
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
793
756
|
return;
|
|
794
757
|
}
|
|
795
|
-
|
|
796
|
-
this.emit("set-characteristics" /* HAPServerEventTypes.SET_CHARACTERISTICS */, connection, writeRequest, (0, once_1.once)(
|
|
797
|
-
var e_4, _a;
|
|
758
|
+
const writeRequest = JSON.parse(data.toString("utf8"));
|
|
759
|
+
this.emit("set-characteristics" /* HAPServerEventTypes.SET_CHARACTERISTICS */, connection, writeRequest, (0, once_1.once)((error, writeResponse) => {
|
|
798
760
|
if (error) {
|
|
799
761
|
response.writeHead(error.httpCode, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
800
762
|
response.end(JSON.stringify({ status: error.status }));
|
|
801
763
|
return;
|
|
802
764
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
multiStatus = true;
|
|
811
|
-
break;
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
816
|
-
finally {
|
|
817
|
-
try {
|
|
818
|
-
if (characteristics_3_1 && !characteristics_3_1.done && (_a = characteristics_3.return)) _a.call(characteristics_3);
|
|
765
|
+
const characteristics = writeResponse.characteristics;
|
|
766
|
+
let multiStatus = false;
|
|
767
|
+
for (const data of characteristics) {
|
|
768
|
+
if (data.status || data.value !== undefined) {
|
|
769
|
+
// also send multiStatus on write response requests
|
|
770
|
+
multiStatus = true;
|
|
771
|
+
break;
|
|
819
772
|
}
|
|
820
|
-
finally { if (e_4) throw e_4.error; }
|
|
821
773
|
}
|
|
822
774
|
if (multiStatus) {
|
|
823
775
|
// 207 is "multi-status" since HomeKit may be setting multiple things and any one can fail independently
|
|
@@ -835,9 +787,8 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
835
787
|
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ }); // method not allowed
|
|
836
788
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
837
789
|
}
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
var _this = this;
|
|
790
|
+
}
|
|
791
|
+
handlePrepareWrite(connection, url, request, data, response) {
|
|
841
792
|
if (!this.allowInsecureRequest && !connection.isAuthenticated()) {
|
|
842
793
|
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
843
794
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
@@ -849,18 +800,18 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
849
800
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
850
801
|
return;
|
|
851
802
|
}
|
|
852
|
-
|
|
853
|
-
if (
|
|
854
|
-
debug("[%s] Received prepare write request with pid %d and ttl %d", this.accessoryInfo.username,
|
|
803
|
+
const prepareRequest = JSON.parse(data.toString());
|
|
804
|
+
if (prepareRequest.pid && prepareRequest.ttl) {
|
|
805
|
+
debug("[%s] Received prepare write request with pid %d and ttl %d", this.accessoryInfo.username, prepareRequest.pid, prepareRequest.ttl);
|
|
855
806
|
if (connection.timedWriteTimeout) { // clear any currently existing timeouts
|
|
856
807
|
clearTimeout(connection.timedWriteTimeout);
|
|
857
808
|
}
|
|
858
|
-
connection.timedWritePid =
|
|
859
|
-
connection.timedWriteTimeout = setTimeout(
|
|
860
|
-
debug("[%s] Timed write request timed out for pid %d",
|
|
809
|
+
connection.timedWritePid = prepareRequest.pid;
|
|
810
|
+
connection.timedWriteTimeout = setTimeout(() => {
|
|
811
|
+
debug("[%s] Timed write request timed out for pid %d", this.accessoryInfo.username, prepareRequest.pid);
|
|
861
812
|
connection.timedWritePid = undefined;
|
|
862
813
|
connection.timedWriteTimeout = undefined;
|
|
863
|
-
},
|
|
814
|
+
}, prepareRequest.ttl);
|
|
864
815
|
response.writeHead(200 /* HAPHTTPCode.OK */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
865
816
|
response.end(JSON.stringify({ status: 0 /* HAPStatus.SUCCESS */ }));
|
|
866
817
|
return;
|
|
@@ -874,8 +825,8 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
874
825
|
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
875
826
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
876
827
|
}
|
|
877
|
-
}
|
|
878
|
-
|
|
828
|
+
}
|
|
829
|
+
handleResource(connection, url, request, data, response) {
|
|
879
830
|
if (!connection.isAuthenticated()) {
|
|
880
831
|
if (!(this.allowInsecureRequest && request.headers && request.headers.authorization === this.accessoryInfo.pincode)) {
|
|
881
832
|
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
@@ -889,9 +840,9 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
889
840
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
890
841
|
return;
|
|
891
842
|
}
|
|
892
|
-
|
|
843
|
+
const resourceRequest = JSON.parse(data.toString());
|
|
893
844
|
// call out to listeners to retrieve the resource, snapshot only right now
|
|
894
|
-
this.emit("request-resource" /* HAPServerEventTypes.REQUEST_RESOURCE */, resourceRequest, (0, once_1.once)(
|
|
845
|
+
this.emit("request-resource" /* HAPServerEventTypes.REQUEST_RESOURCE */, resourceRequest, (0, once_1.once)((error, resource) => {
|
|
895
846
|
if (error) {
|
|
896
847
|
response.writeHead(error.httpCode, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
897
848
|
response.end(JSON.stringify({ status: error.status }));
|
|
@@ -906,8 +857,7 @@ var HAPServer = /** @class */ (function (_super) {
|
|
|
906
857
|
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { "Content-Type": "application/hap+json" /* HAPMimeTypes.HAP_JSON */ }); // method not allowed
|
|
907
858
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
908
859
|
}
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
}(events_1.EventEmitter));
|
|
860
|
+
}
|
|
861
|
+
}
|
|
912
862
|
exports.HAPServer = HAPServer;
|
|
913
863
|
//# sourceMappingURL=HAPServer.js.map
|