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