hap-nodejs 1.0.0-beta.8 → 1.0.0

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.
Files changed (195) hide show
  1. package/README.md +7 -3
  2. package/dist/accessories/AirConditioner_accessory.js +24 -24
  3. package/dist/accessories/AirConditioner_accessory.js.map +1 -1
  4. package/dist/accessories/AppleTVRemote_accessory.js +23 -23
  5. package/dist/accessories/AppleTVRemote_accessory.js.map +1 -1
  6. package/dist/accessories/Camera_accessory.js +295 -380
  7. package/dist/accessories/Camera_accessory.js.map +1 -1
  8. package/dist/accessories/Fan_accessory.js +15 -21
  9. package/dist/accessories/Fan_accessory.js.map +1 -1
  10. package/dist/accessories/GarageDoorOpener_accessory.js +12 -12
  11. package/dist/accessories/GarageDoorOpener_accessory.js.map +1 -1
  12. package/dist/accessories/Light-AdaptiveLighting_accessory.js +31 -21
  13. package/dist/accessories/Light-AdaptiveLighting_accessory.js.map +1 -1
  14. package/dist/accessories/Light_accessory.js +45 -48
  15. package/dist/accessories/Light_accessory.js.map +1 -1
  16. package/dist/accessories/Lock_accessory.js +11 -11
  17. package/dist/accessories/Lock_accessory.js.map +1 -1
  18. package/dist/accessories/MotionSensor_accessory.js +8 -8
  19. package/dist/accessories/MotionSensor_accessory.js.map +1 -1
  20. package/dist/accessories/Outlet_accessory.js +10 -10
  21. package/dist/accessories/Outlet_accessory.js.map +1 -1
  22. package/dist/accessories/SmartSpeaker_accessory.js +11 -11
  23. package/dist/accessories/SmartSpeaker_accessory.js.map +1 -1
  24. package/dist/accessories/Sprinkler_accessory.js +19 -19
  25. package/dist/accessories/Sprinkler_accessory.js.map +1 -1
  26. package/dist/accessories/TV_accessory.js +17 -17
  27. package/dist/accessories/TV_accessory.js.map +1 -1
  28. package/dist/accessories/TemperatureSensor_accessory.js +6 -6
  29. package/dist/accessories/TemperatureSensor_accessory.js.map +1 -1
  30. package/dist/accessories/Wi-FiRouter_accessory.js +3 -3
  31. package/dist/accessories/Wi-FiRouter_accessory.js.map +1 -1
  32. package/dist/accessories/Wi-FiSatellite_accessory.js +4 -4
  33. package/dist/accessories/Wi-FiSatellite_accessory.js.map +1 -1
  34. package/dist/accessories/gstreamer-audioProducer.js +45 -55
  35. package/dist/accessories/gstreamer-audioProducer.js.map +1 -1
  36. package/dist/accessories/types.js +2 -2
  37. package/dist/accessories/types.js.map +1 -1
  38. package/dist/index.d.ts +0 -14
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +6 -31
  41. package/dist/index.js.map +1 -1
  42. package/dist/internal-types.d.ts +4 -4
  43. package/dist/internal-types.d.ts.map +1 -1
  44. package/dist/internal-types.js +10 -6
  45. package/dist/internal-types.js.map +1 -1
  46. package/dist/lib/Accessory.d.ts +19 -74
  47. package/dist/lib/Accessory.d.ts.map +1 -1
  48. package/dist/lib/Accessory.js +775 -1155
  49. package/dist/lib/Accessory.js.map +1 -1
  50. package/dist/lib/Advertiser.d.ts +5 -6
  51. package/dist/lib/Advertiser.d.ts.map +1 -1
  52. package/dist/lib/Advertiser.js +402 -521
  53. package/dist/lib/Advertiser.js.map +1 -1
  54. package/dist/lib/Bridge.js +6 -10
  55. package/dist/lib/Bridge.js.map +1 -1
  56. package/dist/lib/Characteristic.d.ts +31 -93
  57. package/dist/lib/Characteristic.d.ts.map +1 -1
  58. package/dist/lib/Characteristic.js +1474 -668
  59. package/dist/lib/Characteristic.js.map +1 -1
  60. package/dist/lib/HAPServer.d.ts +10 -22
  61. package/dist/lib/HAPServer.d.ts.map +1 -1
  62. package/dist/lib/HAPServer.js +224 -286
  63. package/dist/lib/HAPServer.js.map +1 -1
  64. package/dist/lib/Service.d.ts +9 -27
  65. package/dist/lib/Service.d.ts.map +1 -1
  66. package/dist/lib/Service.js +476 -314
  67. package/dist/lib/Service.js.map +1 -1
  68. package/dist/lib/camera/RTPProxy.d.ts +0 -2
  69. package/dist/lib/camera/RTPProxy.d.ts.map +1 -1
  70. package/dist/lib/camera/RTPProxy.js +112 -104
  71. package/dist/lib/camera/RTPProxy.js.map +1 -1
  72. package/dist/lib/camera/RTPStreamManagement.d.ts +2 -67
  73. package/dist/lib/camera/RTPStreamManagement.d.ts.map +1 -1
  74. package/dist/lib/camera/RTPStreamManagement.js +272 -292
  75. package/dist/lib/camera/RTPStreamManagement.js.map +1 -1
  76. package/dist/lib/camera/RecordingManagement.d.ts +0 -1
  77. package/dist/lib/camera/RecordingManagement.d.ts.map +1 -1
  78. package/dist/lib/camera/RecordingManagement.js +325 -392
  79. package/dist/lib/camera/RecordingManagement.js.map +1 -1
  80. package/dist/lib/camera/index.d.ts +0 -1
  81. package/dist/lib/camera/index.d.ts.map +1 -1
  82. package/dist/lib/camera/index.js +1 -2
  83. package/dist/lib/camera/index.js.map +1 -1
  84. package/dist/lib/controller/AdaptiveLightingController.d.ts +19 -4
  85. package/dist/lib/controller/AdaptiveLightingController.d.ts.map +1 -1
  86. package/dist/lib/controller/AdaptiveLightingController.js +220 -221
  87. package/dist/lib/controller/AdaptiveLightingController.js.map +1 -1
  88. package/dist/lib/controller/CameraController.d.ts +0 -6
  89. package/dist/lib/controller/CameraController.d.ts.map +1 -1
  90. package/dist/lib/controller/CameraController.js +192 -258
  91. package/dist/lib/controller/CameraController.js.map +1 -1
  92. package/dist/lib/controller/Controller.d.ts +1 -1
  93. package/dist/lib/controller/Controller.d.ts.map +1 -1
  94. package/dist/lib/controller/Controller.js +3 -3
  95. package/dist/lib/controller/Controller.js.map +1 -1
  96. package/dist/lib/controller/DoorbellController.js +38 -39
  97. package/dist/lib/controller/DoorbellController.js.map +1 -1
  98. package/dist/lib/controller/RemoteController.d.ts +4 -20
  99. package/dist/lib/controller/RemoteController.d.ts.map +1 -1
  100. package/dist/lib/controller/RemoteController.js +351 -424
  101. package/dist/lib/controller/RemoteController.js.map +1 -1
  102. package/dist/lib/controller/index.js +1 -1
  103. package/dist/lib/datastream/DataStreamManagement.js +57 -58
  104. package/dist/lib/datastream/DataStreamManagement.js.map +1 -1
  105. package/dist/lib/datastream/DataStreamParser.d.ts +0 -1
  106. package/dist/lib/datastream/DataStreamParser.d.ts.map +1 -1
  107. package/dist/lib/datastream/DataStreamParser.js +261 -306
  108. package/dist/lib/datastream/DataStreamParser.js.map +1 -1
  109. package/dist/lib/datastream/DataStreamServer.d.ts +0 -8
  110. package/dist/lib/datastream/DataStreamServer.d.ts.map +1 -1
  111. package/dist/lib/datastream/DataStreamServer.js +262 -275
  112. package/dist/lib/datastream/DataStreamServer.js.map +1 -1
  113. package/dist/lib/datastream/index.js +1 -1
  114. package/dist/lib/definitions/CharacteristicDefinitions.d.ts +50 -40
  115. package/dist/lib/definitions/CharacteristicDefinitions.d.ts.map +1 -1
  116. package/dist/lib/definitions/CharacteristicDefinitions.js +2059 -2754
  117. package/dist/lib/definitions/CharacteristicDefinitions.js.map +1 -1
  118. package/dist/lib/definitions/ServiceDefinitions.d.ts +14 -8
  119. package/dist/lib/definitions/ServiceDefinitions.d.ts.map +1 -1
  120. package/dist/lib/definitions/ServiceDefinitions.js +834 -1053
  121. package/dist/lib/definitions/ServiceDefinitions.js.map +1 -1
  122. package/dist/lib/definitions/generate-definitions.js +383 -679
  123. package/dist/lib/definitions/generate-definitions.js.map +1 -1
  124. package/dist/lib/definitions/generator-configuration.d.ts.map +1 -1
  125. package/dist/lib/definitions/generator-configuration.js +163 -35
  126. package/dist/lib/definitions/generator-configuration.js.map +1 -1
  127. package/dist/lib/definitions/index.js +1 -1
  128. package/dist/lib/model/AccessoryInfo.d.ts +0 -1
  129. package/dist/lib/model/AccessoryInfo.d.ts.map +1 -1
  130. package/dist/lib/model/AccessoryInfo.js +102 -137
  131. package/dist/lib/model/AccessoryInfo.js.map +1 -1
  132. package/dist/lib/model/ControllerStorage.js +86 -89
  133. package/dist/lib/model/ControllerStorage.js.map +1 -1
  134. package/dist/lib/model/HAPStorage.js +15 -16
  135. package/dist/lib/model/HAPStorage.js.map +1 -1
  136. package/dist/lib/model/IdentifierCache.js +49 -49
  137. package/dist/lib/model/IdentifierCache.js.map +1 -1
  138. package/dist/lib/tv/AccessControlManagement.d.ts +0 -1
  139. package/dist/lib/tv/AccessControlManagement.d.ts.map +1 -1
  140. package/dist/lib/tv/AccessControlManagement.js +43 -46
  141. package/dist/lib/tv/AccessControlManagement.js.map +1 -1
  142. package/dist/lib/util/checkName.d.ts +8 -0
  143. package/dist/lib/util/checkName.d.ts.map +1 -0
  144. package/dist/lib/util/checkName.js +17 -0
  145. package/dist/lib/util/checkName.js.map +1 -0
  146. package/dist/lib/util/clone.js +6 -29
  147. package/dist/lib/util/clone.js.map +1 -1
  148. package/dist/lib/util/color-utils.js +8 -12
  149. package/dist/lib/util/color-utils.js.map +1 -1
  150. package/dist/lib/util/eventedhttp.d.ts +5 -9
  151. package/dist/lib/util/eventedhttp.d.ts.map +1 -1
  152. package/dist/lib/util/eventedhttp.js +306 -405
  153. package/dist/lib/util/eventedhttp.js.map +1 -1
  154. package/dist/lib/util/hapCrypto.d.ts +0 -1
  155. package/dist/lib/util/hapCrypto.d.ts.map +1 -1
  156. package/dist/lib/util/hapCrypto.js +39 -41
  157. package/dist/lib/util/hapCrypto.js.map +1 -1
  158. package/dist/lib/util/hapStatusError.js +9 -12
  159. package/dist/lib/util/hapStatusError.js.map +1 -1
  160. package/dist/lib/util/net-utils.d.ts.map +1 -1
  161. package/dist/lib/util/net-utils.js +34 -54
  162. package/dist/lib/util/net-utils.js.map +1 -1
  163. package/dist/lib/util/once.js +4 -10
  164. package/dist/lib/util/once.js.map +1 -1
  165. package/dist/lib/util/promise-utils.d.ts +0 -1
  166. package/dist/lib/util/promise-utils.d.ts.map +1 -1
  167. package/dist/lib/util/promise-utils.js +10 -16
  168. package/dist/lib/util/promise-utils.js.map +1 -1
  169. package/dist/lib/util/request-util.js +8 -10
  170. package/dist/lib/util/request-util.js.map +1 -1
  171. package/dist/lib/util/time.d.ts +0 -1
  172. package/dist/lib/util/time.d.ts.map +1 -1
  173. package/dist/lib/util/time.js +7 -8
  174. package/dist/lib/util/time.js.map +1 -1
  175. package/dist/lib/util/tlv.d.ts +0 -28
  176. package/dist/lib/util/tlv.d.ts.map +1 -1
  177. package/dist/lib/util/tlv.js +83 -126
  178. package/dist/lib/util/tlv.js.map +1 -1
  179. package/dist/lib/util/uuid.d.ts +0 -10
  180. package/dist/lib/util/uuid.d.ts.map +1 -1
  181. package/dist/lib/util/uuid.js +22 -40
  182. package/dist/lib/util/uuid.js.map +1 -1
  183. package/dist/types.d.ts +0 -35
  184. package/dist/types.d.ts.map +1 -1
  185. package/dist/types.js +1 -1
  186. package/dist/types.js.map +1 -1
  187. package/package.json +29 -28
  188. package/dist/lib/AccessoryLoader.d.ts +0 -28
  189. package/dist/lib/AccessoryLoader.d.ts.map +0 -1
  190. package/dist/lib/AccessoryLoader.js +0 -167
  191. package/dist/lib/AccessoryLoader.js.map +0 -1
  192. package/dist/lib/camera/Camera.d.ts +0 -44
  193. package/dist/lib/camera/Camera.d.ts.map +0 -1
  194. package/dist/lib/camera/Camera.js +0 -36
  195. package/dist/lib/camera/Camera.js.map +0 -1
@@ -1,29 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HAPConnection = exports.HAPConnectionEvent = exports.HAPConnectionState = exports.EventedHTTPServer = exports.EventedHTTPServerEvent = exports.HAPEncryption = void 0;
4
- var tslib_1 = require("tslib");
5
- var domain_formatter_1 = require("@homebridge/ciao/lib/util/domain-formatter");
6
- var assert_1 = tslib_1.__importDefault(require("assert"));
7
- var debug_1 = tslib_1.__importDefault(require("debug"));
8
- var events_1 = require("events");
9
- var http_1 = tslib_1.__importDefault(require("http"));
10
- var net_1 = tslib_1.__importDefault(require("net"));
11
- var os_1 = tslib_1.__importDefault(require("os"));
12
- var hapCrypto = tslib_1.__importStar(require("./hapCrypto"));
13
- var net_utils_1 = require("./net-utils");
14
- var uuid = tslib_1.__importStar(require("./uuid"));
15
- var debug = (0, debug_1.default)("HAP-NodeJS:EventedHTTPServer");
16
- var debugCon = (0, debug_1.default)("HAP-NodeJS:EventedHTTPServer:Connection");
17
- var debugEvents = (0, debug_1.default)("HAP-NodeJS:EventEmitter");
4
+ const tslib_1 = require("tslib");
5
+ const domain_formatter_1 = require("@homebridge/ciao/lib/util/domain-formatter");
6
+ const assert_1 = tslib_1.__importDefault(require("assert"));
7
+ const debug_1 = tslib_1.__importDefault(require("debug"));
8
+ const events_1 = require("events");
9
+ const http_1 = tslib_1.__importDefault(require("http"));
10
+ const net_1 = tslib_1.__importDefault(require("net"));
11
+ const os_1 = tslib_1.__importDefault(require("os"));
12
+ const hapCrypto = tslib_1.__importStar(require("./hapCrypto"));
13
+ const net_utils_1 = require("./net-utils");
14
+ const uuid = tslib_1.__importStar(require("./uuid"));
15
+ const debug = (0, debug_1.default)("HAP-NodeJS:EventedHTTPServer");
16
+ const debugCon = (0, debug_1.default)("HAP-NodeJS:EventedHTTPServer:Connection");
17
+ const debugEvents = (0, debug_1.default)("HAP-NodeJS:EventEmitter");
18
18
  /**
19
19
  * Simple struct to hold vars needed to support HAP encryption.
20
20
  *
21
21
  * @group Cryptography
22
22
  */
23
- var HAPEncryption = /** @class */ (function () {
24
- function HAPEncryption(clientPublicKey, secretKey, publicKey, sharedSecret, hkdfPairEncryptionKey) {
25
- this.accessoryToControllerCount = 0;
26
- this.controllerToAccessoryCount = 0;
23
+ class HAPEncryption {
24
+ clientPublicKey;
25
+ secretKey;
26
+ publicKey;
27
+ sharedSecret;
28
+ hkdfPairEncryptionKey;
29
+ accessoryToControllerCount = 0;
30
+ controllerToAccessoryCount = 0;
31
+ accessoryToControllerKey;
32
+ controllerToAccessoryKey;
33
+ incompleteFrame;
34
+ constructor(clientPublicKey, secretKey, publicKey, sharedSecret, hkdfPairEncryptionKey) {
27
35
  this.clientPublicKey = clientPublicKey;
28
36
  this.secretKey = secretKey;
29
37
  this.publicKey = publicKey;
@@ -32,8 +40,7 @@ var HAPEncryption = /** @class */ (function () {
32
40
  this.accessoryToControllerKey = Buffer.alloc(0);
33
41
  this.controllerToAccessoryKey = Buffer.alloc(0);
34
42
  }
35
- return HAPEncryption;
36
- }());
43
+ }
37
44
  exports.HAPEncryption = HAPEncryption;
38
45
  /**
39
46
  * @group HAP Accessory Server
@@ -44,7 +51,7 @@ var EventedHTTPServerEvent;
44
51
  EventedHTTPServerEvent["CONNECTION_OPENED"] = "connection-opened";
45
52
  EventedHTTPServerEvent["REQUEST"] = "request";
46
53
  EventedHTTPServerEvent["CONNECTION_CLOSED"] = "connection-closed";
47
- })(EventedHTTPServerEvent = exports.EventedHTTPServerEvent || (exports.EventedHTTPServerEvent = {}));
54
+ })(EventedHTTPServerEvent || (exports.EventedHTTPServerEvent = EventedHTTPServerEvent = {}));
48
55
  /**
49
56
  * EventedHTTPServer provides an HTTP-like server that supports HAP "extensions" for security and events.
50
57
  *
@@ -64,75 +71,67 @@ var EventedHTTPServerEvent;
64
71
  *
65
72
  * @group HAP Accessory Server
66
73
  */
67
- var EventedHTTPServer = /** @class */ (function (_super) {
68
- tslib_1.__extends(EventedHTTPServer, _super);
69
- function EventedHTTPServer() {
70
- var _this = _super.call(this) || this;
71
- /**
72
- * Set of all currently connected HAP connections.
73
- */
74
- _this.connections = new Set();
75
- /**
76
- * Session dictionary indexed by username/identifier. The username uniquely identifies every person added to the home.
77
- * So there can be multiple sessions open for a single username (multiple devices connected to the same Apple ID).
78
- */
79
- _this.connectionsByUsername = new Map();
80
- _this.tcpServer = net_1.default.createServer();
81
- return _this;
82
- }
83
- EventedHTTPServer.prototype.scheduleNextConnectionIdleTimeout = function () {
84
- var e_1, _a;
74
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
75
+ class EventedHTTPServer extends events_1.EventEmitter {
76
+ static CONNECTION_TIMEOUT_LIMIT = 16; // if we have more (or equal) # connections we start the timeout
77
+ static MAX_CONNECTION_IDLE_TIME = 60 * 60 * 1000; // 1h
78
+ tcpServer;
79
+ /**
80
+ * Set of all currently connected HAP connections.
81
+ */
82
+ connections = new Set();
83
+ /**
84
+ * Session dictionary indexed by username/identifier. The username uniquely identifies every person added to the home.
85
+ * So there can be multiple sessions open for a single username (multiple devices connected to the same Apple ID).
86
+ */
87
+ connectionsByUsername = new Map();
88
+ connectionIdleTimeout;
89
+ connectionLoggingInterval;
90
+ constructor() {
91
+ super();
92
+ this.tcpServer = net_1.default.createServer();
93
+ }
94
+ scheduleNextConnectionIdleTimeout() {
85
95
  this.connectionIdleTimeout = undefined;
86
96
  if (!this.tcpServer.listening) {
87
97
  return;
88
98
  }
89
99
  debug("Running idle timeout timer...");
90
- var currentTime = new Date().getTime();
91
- var nextTimeout = -1;
92
- try {
93
- for (var _b = tslib_1.__values(this.connections), _c = _b.next(); !_c.done; _c = _b.next()) {
94
- var connection = _c.value;
95
- var timeDelta = currentTime - connection.lastSocketOperation;
96
- if (timeDelta >= EventedHTTPServer.MAX_CONNECTION_IDLE_TIME) {
97
- debug("[%s] Closing connection as it was inactive for " + timeDelta + "ms");
98
- connection.close();
99
- }
100
- else {
101
- nextTimeout = Math.max(nextTimeout, EventedHTTPServer.MAX_CONNECTION_IDLE_TIME - timeDelta);
102
- }
100
+ const currentTime = new Date().getTime();
101
+ let nextTimeout = -1;
102
+ for (const connection of this.connections) {
103
+ const timeDelta = currentTime - connection.lastSocketOperation;
104
+ if (timeDelta >= EventedHTTPServer.MAX_CONNECTION_IDLE_TIME) {
105
+ debug("[%s] Closing connection as it was inactive for " + timeDelta + "ms");
106
+ connection.close();
103
107
  }
104
- }
105
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
106
- finally {
107
- try {
108
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
108
+ else {
109
+ nextTimeout = Math.max(nextTimeout, EventedHTTPServer.MAX_CONNECTION_IDLE_TIME - timeDelta);
109
110
  }
110
- finally { if (e_1) throw e_1.error; }
111
111
  }
112
112
  if (this.connections.size >= EventedHTTPServer.CONNECTION_TIMEOUT_LIMIT) {
113
113
  this.connectionIdleTimeout = setTimeout(this.scheduleNextConnectionIdleTimeout.bind(this), nextTimeout);
114
114
  }
115
- };
116
- EventedHTTPServer.prototype.address = function () {
115
+ }
116
+ address() {
117
117
  return this.tcpServer.address();
118
- };
119
- EventedHTTPServer.prototype.listen = function (targetPort, hostname) {
120
- var _this = this;
121
- this.tcpServer.listen(targetPort, hostname, function () {
122
- var address = _this.tcpServer.address(); // address() is only a string when listening to unix domain sockets
123
- debug("Server listening on %s:%s", address.family === "IPv6" ? "[".concat(address.address, "]") : address.address, address.port);
124
- _this.connectionLoggingInterval = setInterval(function () {
125
- var connectionInformation = tslib_1.__spreadArray([], tslib_1.__read(_this.connections), false).map(function (connection) { return "".concat(connection.remoteAddress, ":").concat(connection.remotePort); })
118
+ }
119
+ listen(targetPort, hostname) {
120
+ this.tcpServer.listen(targetPort, hostname, () => {
121
+ const address = this.tcpServer.address(); // address() is only a string when listening to unix domain sockets
122
+ debug("Server listening on %s:%s", address.family === "IPv6" ? `[${address.address}]` : address.address, address.port);
123
+ this.connectionLoggingInterval = setInterval(() => {
124
+ const connectionInformation = [...this.connections]
125
+ .map(connection => `${connection.remoteAddress}:${connection.remotePort}`)
126
126
  .join(", ");
127
- debug("Currently %d hap connections open: %s", _this.connections.size, connectionInformation);
128
- }, 60000);
129
- _this.connectionLoggingInterval.unref();
130
- _this.emit("listening" /* EventedHTTPServerEvent.LISTENING */, address.port, address.address);
127
+ debug("Currently %d hap connections open: %s", this.connections.size, connectionInformation);
128
+ }, 60_000);
129
+ this.connectionLoggingInterval.unref();
130
+ this.emit("listening" /* EventedHTTPServerEvent.LISTENING */, address.port, address.address);
131
131
  });
132
132
  this.tcpServer.on("connection", this.onConnection.bind(this));
133
- };
134
- EventedHTTPServer.prototype.stop = function () {
135
- var e_2, _a;
133
+ }
134
+ stop() {
136
135
  if (this.connectionLoggingInterval != null) {
137
136
  clearInterval(this.connectionLoggingInterval);
138
137
  this.connectionLoggingInterval = undefined;
@@ -142,24 +141,14 @@ var EventedHTTPServer = /** @class */ (function (_super) {
142
141
  this.connectionIdleTimeout = undefined;
143
142
  }
144
143
  this.tcpServer.close();
145
- try {
146
- for (var _b = tslib_1.__values(this.connections), _c = _b.next(); !_c.done; _c = _b.next()) {
147
- var connection = _c.value;
148
- connection.close();
149
- }
144
+ for (const connection of this.connections) {
145
+ connection.close();
150
146
  }
151
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
152
- finally {
153
- try {
154
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
155
- }
156
- finally { if (e_2) throw e_2.error; }
157
- }
158
- };
159
- EventedHTTPServer.prototype.destroy = function () {
147
+ }
148
+ destroy() {
160
149
  this.stop();
161
150
  this.removeAllListeners();
162
- };
151
+ }
163
152
  /**
164
153
  * Send an event notification for given characteristic and changed value to all connected clients.
165
154
  * If `originator` is specified, the given {@link HAPConnection} will be excluded from the broadcast.
@@ -171,32 +160,20 @@ var EventedHTTPServer = /** @class */ (function (_super) {
171
160
  * @param immediateDelivery - The HAP spec requires some characteristics to be delivery immediately.
172
161
  * Namely, for the {@link Characteristic.ButtonEvent} and the {@link Characteristic.ProgrammableSwitchEvent} characteristics.
173
162
  */
174
- EventedHTTPServer.prototype.broadcastEvent = function (aid, iid, value, originator, immediateDelivery) {
175
- var e_3, _a;
176
- try {
177
- for (var _b = tslib_1.__values(this.connections), _c = _b.next(); !_c.done; _c = _b.next()) {
178
- var connection = _c.value;
179
- if (connection === originator) {
180
- debug("[%s] Muting event '%s' notification for this connection since it originated here.", connection.remoteAddress, aid + "." + iid);
181
- continue;
182
- }
183
- connection.sendEvent(aid, iid, value, immediateDelivery);
163
+ broadcastEvent(aid, iid, value, originator, immediateDelivery) {
164
+ for (const connection of this.connections) {
165
+ if (connection === originator) {
166
+ debug("[%s] Muting event '%s' notification for this connection since it originated here.", connection.remoteAddress, aid + "." + iid);
167
+ continue;
184
168
  }
169
+ connection.sendEvent(aid, iid, value, immediateDelivery);
185
170
  }
186
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
187
- finally {
188
- try {
189
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
190
- }
191
- finally { if (e_3) throw e_3.error; }
192
- }
193
- };
194
- EventedHTTPServer.prototype.onConnection = function (socket) {
195
- var _this = this;
171
+ }
172
+ onConnection(socket) {
196
173
  // eslint-disable-next-line @typescript-eslint/no-use-before-define
197
- var connection = new HAPConnection(this, socket);
198
- connection.on("request" /* HAPConnectionEvent.REQUEST */, function (request, response) {
199
- _this.emit("request" /* EventedHTTPServerEvent.REQUEST */, connection, request, response);
174
+ const connection = new HAPConnection(this, socket);
175
+ connection.on("request" /* HAPConnectionEvent.REQUEST */, (request, response) => {
176
+ this.emit("request" /* EventedHTTPServerEvent.REQUEST */, connection, request, response);
200
177
  });
201
178
  connection.on("authenticated" /* HAPConnectionEvent.AUTHENTICATED */, this.handleConnectionAuthenticated.bind(this, connection));
202
179
  connection.on("closed" /* HAPConnectionEvent.CLOSED */, this.handleConnectionClose.bind(this, connection));
@@ -206,23 +183,23 @@ var EventedHTTPServer = /** @class */ (function (_super) {
206
183
  if (this.connections.size >= EventedHTTPServer.CONNECTION_TIMEOUT_LIMIT && !this.connectionIdleTimeout) {
207
184
  this.scheduleNextConnectionIdleTimeout();
208
185
  }
209
- };
210
- EventedHTTPServer.prototype.handleConnectionAuthenticated = function (connection, username) {
211
- var connections = this.connectionsByUsername.get(username);
186
+ }
187
+ handleConnectionAuthenticated(connection, username) {
188
+ const connections = this.connectionsByUsername.get(username);
212
189
  if (!connections) {
213
190
  this.connectionsByUsername.set(username, [connection]);
214
191
  }
215
192
  else if (!connections.includes(connection)) { // ensure this doesn't get added more than one time
216
193
  connections.push(connection);
217
194
  }
218
- };
219
- EventedHTTPServer.prototype.handleConnectionClose = function (connection) {
195
+ }
196
+ handleConnectionClose(connection) {
220
197
  this.emit("connection-closed" /* EventedHTTPServerEvent.CONNECTION_CLOSED */, connection);
221
198
  this.connections.delete(connection);
222
199
  if (connection.username) { // aka connection was authenticated
223
- var connections = this.connectionsByUsername.get(connection.username);
200
+ const connections = this.connectionsByUsername.get(connection.username);
224
201
  if (connections) {
225
- var index = connections.indexOf(connection);
202
+ const index = connections.indexOf(connection);
226
203
  if (index !== -1) {
227
204
  connections.splice(index, 1);
228
205
  }
@@ -231,7 +208,7 @@ var EventedHTTPServer = /** @class */ (function (_super) {
231
208
  }
232
209
  }
233
210
  }
234
- };
211
+ }
235
212
  /**
236
213
  * This method is to be called when a given {@link HAPConnection} performs a request that should result in the disconnection
237
214
  * of all other {@link HAPConnection} with the same {@link HAPUsername}.
@@ -242,29 +219,15 @@ var EventedHTTPServer = /** @class */ (function (_super) {
242
219
  * @param initiator - The connection that requested to disconnect all connections of the same username.
243
220
  * @param username - The username for which all connections shall be closed.
244
221
  */
245
- EventedHTTPServer.destroyExistingConnectionsAfterUnpair = function (initiator, username) {
246
- var e_4, _a;
247
- var connections = initiator.server.connectionsByUsername.get(username);
222
+ static destroyExistingConnectionsAfterUnpair(initiator, username) {
223
+ const connections = initiator.server.connectionsByUsername.get(username);
248
224
  if (connections) {
249
- try {
250
- for (var connections_1 = tslib_1.__values(connections), connections_1_1 = connections_1.next(); !connections_1_1.done; connections_1_1 = connections_1.next()) {
251
- var connection = connections_1_1.value;
252
- connection.closeConnectionAsOfUnpair(initiator);
253
- }
254
- }
255
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
256
- finally {
257
- try {
258
- if (connections_1_1 && !connections_1_1.done && (_a = connections_1.return)) _a.call(connections_1);
259
- }
260
- finally { if (e_4) throw e_4.error; }
225
+ for (const connection of connections) {
226
+ connection.closeConnectionAsOfUnpair(initiator);
261
227
  }
262
228
  }
263
- };
264
- EventedHTTPServer.CONNECTION_TIMEOUT_LIMIT = 16; // if we have more (or equal) # connections we start the timeout
265
- EventedHTTPServer.MAX_CONNECTION_IDLE_TIME = 60 * 60 * 1000; // 1h
266
- return EventedHTTPServer;
267
- }(events_1.EventEmitter));
229
+ }
230
+ }
268
231
  exports.EventedHTTPServer = EventedHTTPServer;
269
232
  /**
270
233
  * @private
@@ -280,7 +243,7 @@ var HAPConnectionState;
280
243
  HAPConnectionState[HAPConnectionState["TO_BE_TEARED_DOWN"] = 3] = "TO_BE_TEARED_DOWN";
281
244
  HAPConnectionState[HAPConnectionState["CLOSING"] = 4] = "CLOSING";
282
245
  HAPConnectionState[HAPConnectionState["CLOSED"] = 5] = "CLOSED";
283
- })(HAPConnectionState = exports.HAPConnectionState || (exports.HAPConnectionState = {}));
246
+ })(HAPConnectionState || (exports.HAPConnectionState = HAPConnectionState = {}));
284
247
  /**
285
248
  * @group HAP Accessory Server
286
249
  */
@@ -289,87 +252,106 @@ var HAPConnectionEvent;
289
252
  HAPConnectionEvent["REQUEST"] = "request";
290
253
  HAPConnectionEvent["AUTHENTICATED"] = "authenticated";
291
254
  HAPConnectionEvent["CLOSED"] = "closed";
292
- })(HAPConnectionEvent = exports.HAPConnectionEvent || (exports.HAPConnectionEvent = {}));
255
+ })(HAPConnectionEvent || (exports.HAPConnectionEvent = HAPConnectionEvent = {}));
293
256
  /**
294
257
  * Manages a single iOS-initiated HTTP connection during its lifetime.
295
258
  * @group HAP Accessory Server
296
259
  */
297
- var HAPConnection = /** @class */ (function (_super) {
298
- tslib_1.__extends(HAPConnection, _super);
299
- function HAPConnection(server, clientSocket) {
300
- var _this = _super.call(this) || this;
301
- _this.state = 0 /* HAPConnectionState.CONNECTING */;
302
- _this.lastSocketOperation = new Date().getTime();
303
- _this.pendingClientSocketData = Buffer.alloc(0); // data received from client before HTTP proxy is fully setup
304
- _this.handlingRequest = false; // true while we are composing an HTTP response (so events can wait)
305
- _this.registeredEvents = new Set();
306
- _this.queuedEvents = [];
307
- /**
308
- * If true, the above {@link queuedEvents} contains events which are set to be delivered immediately!
309
- */
310
- _this.eventsQueuedForImmediateDelivery = false;
311
- _this.server = server;
312
- _this.sessionID = uuid.generate(clientSocket.remoteAddress + ":" + clientSocket.remotePort);
313
- _this.localAddress = clientSocket.localAddress;
314
- _this.remoteAddress = clientSocket.remoteAddress; // cache because it becomes undefined in 'onClientSocketClose'
315
- _this.remotePort = clientSocket.remotePort;
316
- _this.networkInterface = HAPConnection.getLocalNetworkInterface(clientSocket);
260
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
261
+ class HAPConnection extends events_1.EventEmitter {
262
+ /**
263
+ * @private file-private API
264
+ */
265
+ server;
266
+ sessionID; // uuid unique to every HAP connection
267
+ state = 0 /* HAPConnectionState.CONNECTING */;
268
+ localAddress;
269
+ remoteAddress; // cache because it becomes undefined in 'onClientSocketClose'
270
+ remotePort;
271
+ networkInterface;
272
+ tcpSocket;
273
+ internalHttpServer;
274
+ httpSocket; // set when in state FULLY_SET_UP
275
+ internalHttpServerPort;
276
+ internalHttpServerAddress;
277
+ lastSocketOperation = new Date().getTime();
278
+ pendingClientSocketData = Buffer.alloc(0); // data received from client before HTTP proxy is fully setup
279
+ handlingRequest = false; // true while we are composing an HTTP response (so events can wait)
280
+ username; // username is unique to every user in the home, basically identifies an Apple ID
281
+ encryption; // created in handlePairVerifyStepOne
282
+ srpServer;
283
+ _pairSetupState; // TODO ensure those two states are always correctly reset?
284
+ _pairVerifyState;
285
+ registeredEvents = new Set();
286
+ eventsTimer;
287
+ queuedEvents = [];
288
+ /**
289
+ * If true, the above {@link queuedEvents} contains events which are set to be delivered immediately!
290
+ */
291
+ eventsQueuedForImmediateDelivery = false;
292
+ timedWritePid;
293
+ timedWriteTimeout;
294
+ constructor(server, clientSocket) {
295
+ super();
296
+ this.server = server;
297
+ this.sessionID = uuid.generate(clientSocket.remoteAddress + ":" + clientSocket.remotePort);
298
+ this.localAddress = clientSocket.localAddress;
299
+ this.remoteAddress = clientSocket.remoteAddress; // cache because it becomes undefined in 'onClientSocketClose'
300
+ this.remotePort = clientSocket.remotePort;
301
+ this.networkInterface = HAPConnection.getLocalNetworkInterface(clientSocket);
317
302
  // clientSocket is the socket connected to the actual iOS device
318
- _this.tcpSocket = clientSocket;
319
- _this.tcpSocket.on("data", _this.onTCPSocketData.bind(_this));
320
- _this.tcpSocket.on("close", _this.onTCPSocketClose.bind(_this));
303
+ this.tcpSocket = clientSocket;
304
+ this.tcpSocket.on("data", this.onTCPSocketData.bind(this));
305
+ this.tcpSocket.on("close", this.onTCPSocketClose.bind(this));
321
306
  // we MUST register for this event, otherwise the error will bubble up to the top and crash the node process entirely.
322
- _this.tcpSocket.on("error", _this.onTCPSocketError.bind(_this));
323
- _this.tcpSocket.setNoDelay(true); // disable Nagle algorithm
307
+ this.tcpSocket.on("error", this.onTCPSocketError.bind(this));
308
+ this.tcpSocket.setNoDelay(true); // disable Nagle algorithm
324
309
  // "HAP accessory servers must not use keepalive messages, which periodically wake up iOS devices".
325
310
  // Thus, we don't configure any tcp keepalive
326
311
  // create our internal HTTP server for this connection that we will proxy data to and from
327
- _this.internalHttpServer = http_1.default.createServer();
328
- _this.internalHttpServer.timeout = 0; // clients expect to hold connections open as long as they want
329
- _this.internalHttpServer.keepAliveTimeout = 0; // workaround for https://github.com/nodejs/node/issues/13391
330
- _this.internalHttpServer.on("listening", _this.onHttpServerListening.bind(_this));
331
- _this.internalHttpServer.on("request", _this.handleHttpServerRequest.bind(_this));
332
- _this.internalHttpServer.on("error", _this.onHttpServerError.bind(_this));
312
+ this.internalHttpServer = http_1.default.createServer();
313
+ this.internalHttpServer.timeout = 0; // clients expect to hold connections open as long as they want
314
+ this.internalHttpServer.keepAliveTimeout = 0; // workaround for https://github.com/nodejs/node/issues/13391
315
+ this.internalHttpServer.on("listening", this.onHttpServerListening.bind(this));
316
+ this.internalHttpServer.on("request", this.handleHttpServerRequest.bind(this));
317
+ this.internalHttpServer.on("error", this.onHttpServerError.bind(this));
333
318
  // close event is added later on the "connect" event as possible listen retries would throw unnecessary close events
334
- _this.internalHttpServer.listen(0, _this.internalHttpServerAddress = (0, net_utils_1.getOSLoopbackAddressIfAvailable)());
335
- return _this;
336
- }
337
- HAPConnection.prototype.debugListenerRegistration = function (event, registration, beforeCount) {
338
- if (registration === void 0) { registration = true; }
339
- if (beforeCount === void 0) { beforeCount = -1; }
340
- var stackTrace = new Error().stack.split("\n")[3];
341
- var eventCount = this.listeners(event).length;
342
- var tabs1 = event === "authenticated" /* HAPConnectionEvent.AUTHENTICATED */ ? "\t" : "\t\t";
343
- var tabs2 = !registration ? "\t" : "\t\t";
319
+ this.internalHttpServer.listen(0, this.internalHttpServerAddress = (0, net_utils_1.getOSLoopbackAddressIfAvailable)());
320
+ }
321
+ debugListenerRegistration(event, registration = true, beforeCount = -1) {
322
+ const stackTrace = new Error().stack.split("\n")[3];
323
+ const eventCount = this.listeners(event).length;
324
+ const tabs1 = event === "authenticated" /* HAPConnectionEvent.AUTHENTICATED */ ? "\t" : "\t\t";
325
+ const tabs2 = !registration ? "\t" : "\t\t";
344
326
  // eslint-disable-next-line max-len
345
- debugEvents("[".concat(this.remoteAddress, "] ").concat(registration ? "Registered" : "Unregistered", " event '").concat(String(event).toUpperCase(), "' ").concat(tabs1, "(total: ").concat(eventCount).concat(!registration ? " Before: " + beforeCount : "", ") ").concat(tabs2).concat(stackTrace));
346
- };
327
+ debugEvents(`[${this.remoteAddress}] ${registration ? "Registered" : "Unregistered"} event '${String(event).toUpperCase()}' ${tabs1}(total: ${eventCount}${!registration ? " Before: " + beforeCount : ""}) ${tabs2}${stackTrace}`);
328
+ }
347
329
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
348
- HAPConnection.prototype.on = function (event, listener) {
349
- var result = _super.prototype.on.call(this, event, listener);
330
+ on(event, listener) {
331
+ const result = super.on(event, listener);
350
332
  this.debugListenerRegistration(event);
351
333
  return result;
352
- };
334
+ }
353
335
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
354
- HAPConnection.prototype.addListener = function (event, listener) {
355
- var result = _super.prototype.addListener.call(this, event, listener);
336
+ addListener(event, listener) {
337
+ const result = super.addListener(event, listener);
356
338
  this.debugListenerRegistration(event);
357
339
  return result;
358
- };
340
+ }
359
341
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
360
- HAPConnection.prototype.removeListener = function (event, listener) {
361
- var beforeCount = this.listeners(event).length;
362
- var result = _super.prototype.removeListener.call(this, event, listener);
342
+ removeListener(event, listener) {
343
+ const beforeCount = this.listeners(event).length;
344
+ const result = super.removeListener(event, listener);
363
345
  this.debugListenerRegistration(event, false, beforeCount);
364
346
  return result;
365
- };
347
+ }
366
348
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
367
- HAPConnection.prototype.off = function (event, listener) {
368
- var result = _super.prototype.off.call(this, event, listener);
369
- var beforeCount = this.listeners(event).length;
349
+ off(event, listener) {
350
+ const result = super.off(event, listener);
351
+ const beforeCount = this.listeners(event).length;
370
352
  this.debugListenerRegistration(event, false, beforeCount);
371
353
  return result;
372
- };
354
+ }
373
355
  /**
374
356
  * This method is called once the connection has gone through pair-verify.
375
357
  * As any HomeKit controller will initiate a pair-verify after the pair-setup procedure, this method gets
@@ -377,22 +359,22 @@ var HAPConnection = /** @class */ (function (_super) {
377
359
  *
378
360
  * Once this method has been called, the connection is authenticated and encryption is turned on.
379
361
  */
380
- HAPConnection.prototype.connectionAuthenticated = function (username) {
362
+ connectionAuthenticated(username) {
381
363
  this.state = 2 /* HAPConnectionState.AUTHENTICATED */;
382
364
  this.username = username;
383
365
  this.emit("authenticated" /* HAPConnectionEvent.AUTHENTICATED */, username);
384
- };
385
- HAPConnection.prototype.isAuthenticated = function () {
366
+ }
367
+ isAuthenticated() {
386
368
  return this.state === 2 /* HAPConnectionState.AUTHENTICATED */;
387
- };
388
- HAPConnection.prototype.close = function () {
369
+ }
370
+ close() {
389
371
  if (this.state >= 4 /* HAPConnectionState.CLOSING */) {
390
372
  return; // already closed/closing
391
373
  }
392
374
  this.state = 4 /* HAPConnectionState.CLOSING */;
393
375
  this.tcpSocket.destroy();
394
- };
395
- HAPConnection.prototype.closeConnectionAsOfUnpair = function (initiator) {
376
+ }
377
+ closeConnectionAsOfUnpair(initiator) {
396
378
  if (this === initiator) {
397
379
  // the initiator of the unpair request is this connection, meaning it unpaired itself.
398
380
  // we still need to send the response packet to the unpair request.
@@ -402,16 +384,16 @@ var HAPConnection = /** @class */ (function (_super) {
402
384
  // as HomeKit requires it, destroy any active session which got unpaired
403
385
  this.close();
404
386
  }
405
- };
406
- HAPConnection.prototype.sendEvent = function (aid, iid, value, immediateDelivery) {
387
+ }
388
+ sendEvent(aid, iid, value, immediateDelivery) {
407
389
  (0, assert_1.default)(aid != null, "HAPConnection.sendEvent: aid must be defined!");
408
390
  (0, assert_1.default)(iid != null, "HAPConnection.sendEvent: iid must be defined!");
409
- var eventName = aid + "." + iid;
391
+ const eventName = aid + "." + iid;
410
392
  if (!this.registeredEvents.has(eventName)) {
411
393
  // non verified connections can't register events, so this case is covered!
412
394
  return;
413
395
  }
414
- var event = {
396
+ const event = {
415
397
  aid: aid,
416
398
  iid: iid,
417
399
  value: value,
@@ -431,8 +413,8 @@ var HAPConnection = /** @class */ (function (_super) {
431
413
  // we search the list of queued events in reverse order.
432
414
  // if the last element with the same aid and iid has the same value we don't want to send the event notification twice.
433
415
  // BUT, we do not want to override previous event notifications which have a different value. Automations must be executed!
434
- for (var i = this.queuedEvents.length - 1; i >= 0; i--) {
435
- var queuedEvent = this.queuedEvents[i];
416
+ for (let i = this.queuedEvents.length - 1; i >= 0; i--) {
417
+ const queuedEvent = this.queuedEvents[i];
436
418
  if (queuedEvent.aid === aid && queuedEvent.iid === iid) {
437
419
  if (queuedEvent.value === value) {
438
420
  return; // the same event was already queued. do not add it again!
@@ -446,17 +428,16 @@ var HAPConnection = /** @class */ (function (_super) {
446
428
  this.eventsTimer = setTimeout(this.handleEventsTimeout.bind(this), 250);
447
429
  this.eventsTimer.unref();
448
430
  }
449
- };
450
- HAPConnection.prototype.handleEventsTimeout = function () {
431
+ }
432
+ handleEventsTimeout() {
451
433
  this.eventsTimer = undefined;
452
434
  if (this.state > 2 /* HAPConnectionState.AUTHENTICATED */) {
453
435
  // connection is closed or about to be closed. no need to send any further events
454
436
  return;
455
437
  }
456
438
  this.writeQueuedEventNotifications();
457
- };
458
- HAPConnection.prototype.writeQueuedEventNotifications = function () {
459
- var e_5, _a;
439
+ }
440
+ writeQueuedEventNotifications() {
460
441
  if (this.queuedEvents.length === 0 || this.handlingRequest) {
461
442
  return; // don't send empty event notifications or if we are currently handling a request
462
443
  }
@@ -465,29 +446,19 @@ var HAPConnection = /** @class */ (function (_super) {
465
446
  clearTimeout(this.eventsTimer);
466
447
  this.eventsTimer = undefined;
467
448
  }
468
- var eventData = {
449
+ const eventData = {
469
450
  characteristics: [],
470
451
  };
471
- try {
472
- for (var _b = tslib_1.__values(this.queuedEvents), _c = _b.next(); !_c.done; _c = _b.next()) {
473
- var queuedEvent = _c.value;
474
- if (!this.registeredEvents.has(queuedEvent.aid + "." + queuedEvent.iid)) {
475
- continue; // client unregistered that event in the meantime
476
- }
477
- eventData.characteristics.push(queuedEvent);
478
- }
479
- }
480
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
481
- finally {
482
- try {
483
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
452
+ for (const queuedEvent of this.queuedEvents) {
453
+ if (!this.registeredEvents.has(queuedEvent.aid + "." + queuedEvent.iid)) {
454
+ continue; // client unregistered that event in the meantime
484
455
  }
485
- finally { if (e_5) throw e_5.error; }
456
+ eventData.characteristics.push(queuedEvent);
486
457
  }
487
458
  this.queuedEvents.splice(0, this.queuedEvents.length);
488
459
  this.eventsQueuedForImmediateDelivery = false;
489
460
  this.writeEventNotification(eventData);
490
- };
461
+ }
491
462
  /**
492
463
  * This will create an EVENT/1.0 notification header with the provided event notification.
493
464
  * If currently an HTTP request is in progress the assembled packet will be
@@ -495,36 +466,36 @@ var HAPConnection = /** @class */ (function (_super) {
495
466
  *
496
467
  * @param notification - The event which should be sent out
497
468
  */
498
- HAPConnection.prototype.writeEventNotification = function (notification) {
469
+ writeEventNotification(notification) {
499
470
  debugCon("[%s] Sending HAP event notifications %o", this.remoteAddress, notification.characteristics);
500
471
  (0, assert_1.default)(!this.handlingRequest, "Can't write event notifications while handling a request!");
501
472
  // Apple backend processes events in reverse order, so we need to reverse the array
502
473
  // so that events are processed in chronological order.
503
474
  notification.characteristics.reverse();
504
- var dataBuffer = Buffer.from(JSON.stringify(notification), "utf8");
505
- var header = Buffer.from("EVENT/1.0 200 OK\r\n" +
475
+ const dataBuffer = Buffer.from(JSON.stringify(notification), "utf8");
476
+ const header = Buffer.from("EVENT/1.0 200 OK\r\n" +
506
477
  "Content-Type: application/hap+json\r\n" +
507
478
  "Content-Length: " + dataBuffer.length + "\r\n" +
508
479
  "\r\n", "utf8");
509
- var buffer = Buffer.concat([header, dataBuffer]);
480
+ const buffer = Buffer.concat([header, dataBuffer]);
510
481
  this.tcpSocket.write(this.encrypt(buffer), this.handleTCPSocketWriteFulfilled.bind(this));
511
- };
512
- HAPConnection.prototype.enableEventNotifications = function (aid, iid) {
482
+ }
483
+ enableEventNotifications(aid, iid) {
513
484
  this.registeredEvents.add(aid + "." + iid);
514
- };
515
- HAPConnection.prototype.disableEventNotifications = function (aid, iid) {
485
+ }
486
+ disableEventNotifications(aid, iid) {
516
487
  this.registeredEvents.delete(aid + "." + iid);
517
- };
518
- HAPConnection.prototype.hasEventNotifications = function (aid, iid) {
488
+ }
489
+ hasEventNotifications(aid, iid) {
519
490
  return this.registeredEvents.has(aid + "." + iid);
520
- };
521
- HAPConnection.prototype.getRegisteredEvents = function () {
491
+ }
492
+ getRegisteredEvents() {
522
493
  return this.registeredEvents;
523
- };
524
- HAPConnection.prototype.clearRegisteredEvents = function () {
494
+ }
495
+ clearRegisteredEvents() {
525
496
  this.registeredEvents.clear();
526
- };
527
- HAPConnection.prototype.encrypt = function (data) {
497
+ }
498
+ encrypt(data) {
528
499
  // if accessoryToControllerKey is not empty, then encryption is enabled for this connection. However, we'll
529
500
  // need to be careful to ensure that we don't encrypt the last few bytes of the response from handlePairVerifyStepTwo.
530
501
  // Since all communication calls are asynchronous, we could easily receive this 'encrypt' event for those bytes.
@@ -533,18 +504,17 @@ var HAPConnection = /** @class */ (function (_super) {
533
504
  return hapCrypto.layerEncrypt(data, this.encryption);
534
505
  }
535
506
  return data; // otherwise, we don't encrypt and return plaintext
536
- };
537
- HAPConnection.prototype.decrypt = function (data) {
507
+ }
508
+ decrypt(data) {
538
509
  if (this.encryption && this.encryption.controllerToAccessoryKey.length > 0) {
539
510
  // below call may throw an error if decryption failed
540
511
  return hapCrypto.layerDecrypt(data, this.encryption);
541
512
  }
542
513
  return data; // otherwise, we don't decrypt and return plaintext
543
- };
544
- HAPConnection.prototype.onHttpServerListening = function () {
545
- var _this = this;
546
- var addressInfo = this.internalHttpServer.address(); // address() is only a string when listening to unix domain sockets
547
- var addressString = addressInfo.family === "IPv6" ? "[".concat(addressInfo.address, "]") : addressInfo.address;
514
+ }
515
+ onHttpServerListening() {
516
+ const addressInfo = this.internalHttpServer.address(); // address() is only a string when listening to unix domain sockets
517
+ const addressString = addressInfo.family === "IPv6" ? `[${addressInfo.address}]` : addressInfo.address;
548
518
  this.internalHttpServerPort = addressInfo.port;
549
519
  debugCon("[%s] Internal HTTP server listening on %s:%s", this.remoteAddress, addressString, addressInfo.port);
550
520
  this.internalHttpServer.on("close", this.onHttpServerClose.bind(this));
@@ -555,25 +525,25 @@ var HAPConnection = /** @class */ (function (_super) {
555
525
  // we MUST register for this event, otherwise the error will bubble up to the top and crash the node process entirely.
556
526
  this.httpSocket.on("error", this.onHttpSocketError.bind(this));
557
527
  this.httpSocket.on("close", this.onHttpSocketClose.bind(this));
558
- this.httpSocket.on("connect", function () {
528
+ this.httpSocket.on("connect", () => {
559
529
  // we are now fully set up:
560
530
  // - clientSocket is connected to the iOS device
561
531
  // - serverSocket is connected to the httpServer
562
532
  // - ready to proxy data!
563
- _this.state = 1 /* HAPConnectionState.FULLY_SET_UP */;
564
- debugCon("[%s] Internal HTTP socket connected. HAPConnection now fully set up!", _this.remoteAddress);
533
+ this.state = 1 /* HAPConnectionState.FULLY_SET_UP */;
534
+ debugCon("[%s] Internal HTTP socket connected. HAPConnection now fully set up!", this.remoteAddress);
565
535
  // start by flushing any pending buffered data received from the client while we were setting up
566
- if (_this.pendingClientSocketData && _this.pendingClientSocketData.length > 0) {
567
- _this.httpSocket.write(_this.pendingClientSocketData);
536
+ if (this.pendingClientSocketData && this.pendingClientSocketData.length > 0) {
537
+ this.httpSocket.write(this.pendingClientSocketData);
568
538
  }
569
- _this.pendingClientSocketData = undefined;
539
+ this.pendingClientSocketData = undefined;
570
540
  });
571
- };
541
+ }
572
542
  /**
573
543
  * This event handler is called when we receive data from a HomeKit controller on our tcp socket.
574
544
  * We store the data if the internal http server is not read yet, or forward it to the http server.
575
545
  */
576
- HAPConnection.prototype.onTCPSocketData = function (data) {
546
+ onTCPSocketData(data) {
577
547
  if (this.state > 2 /* HAPConnectionState.AUTHENTICATED */) {
578
548
  // don't accept data of a connection which is about to be closed or already closed
579
549
  return;
@@ -594,13 +564,13 @@ var HAPConnection = /** @class */ (function (_super) {
594
564
  else {
595
565
  this.httpSocket.write(data); // proxy it along to the HTTP server
596
566
  }
597
- };
567
+ }
598
568
  /**
599
569
  * This event handler is called when the internal http server receives a request.
600
570
  * Meaning we received data from the HomeKit controller in {@link onTCPSocketData}, which then send the
601
571
  * data unencrypted to the internal http server. And now it landed here, fully parsed as a http request.
602
572
  */
603
- HAPConnection.prototype.handleHttpServerRequest = function (request, response) {
573
+ handleHttpServerRequest(request, response) {
604
574
  if (this.state > 2 /* HAPConnectionState.AUTHENTICATED */) {
605
575
  // don't accept data of a connection which is about to be closed or already closed
606
576
  return;
@@ -608,20 +578,19 @@ var HAPConnection = /** @class */ (function (_super) {
608
578
  debugCon("[%s] HTTP request: %s", this.remoteAddress, request.url);
609
579
  request.socket.setNoDelay(true);
610
580
  this.emit("request" /* HAPConnectionEvent.REQUEST */, request, response);
611
- };
581
+ }
612
582
  /**
613
583
  * This event handler is called by the socket which is connected to our internal http server.
614
584
  * It is called with the response returned from the http server.
615
585
  * In this method we have to encrypt and forward the message back to the HomeKit controller.
616
586
  */
617
- HAPConnection.prototype.handleHttpServerResponse = function (data) {
618
- var _this = this;
587
+ handleHttpServerResponse(data) {
619
588
  data = this.encrypt(data);
620
589
  this.tcpSocket.write(data, this.handleTCPSocketWriteFulfilled.bind(this));
621
590
  debugCon("[%s] HTTP Response is finished", this.remoteAddress);
622
591
  this.handlingRequest = false;
623
592
  if (this.state === 3 /* HAPConnectionState.TO_BE_TEARED_DOWN */) {
624
- setTimeout(function () { return _this.close(); }, 10);
593
+ setTimeout(() => this.close(), 10);
625
594
  }
626
595
  else if (this.state < 3 /* HAPConnectionState.TO_BE_TEARED_DOWN */) {
627
596
  if (!this.eventsTimer || this.eventsQueuedForImmediateDelivery) {
@@ -630,15 +599,15 @@ var HAPConnection = /** @class */ (function (_super) {
630
599
  this.writeQueuedEventNotifications();
631
600
  }
632
601
  }
633
- };
634
- HAPConnection.prototype.handleTCPSocketWriteFulfilled = function () {
602
+ }
603
+ handleTCPSocketWriteFulfilled() {
635
604
  this.lastSocketOperation = new Date().getTime();
636
- };
637
- HAPConnection.prototype.onTCPSocketError = function (err) {
605
+ }
606
+ onTCPSocketError(err) {
638
607
  debugCon("[%s] Client connection error: %s", this.remoteAddress, err.message);
639
608
  // onTCPSocketClose will be called next
640
- };
641
- HAPConnection.prototype.onTCPSocketClose = function () {
609
+ }
610
+ onTCPSocketClose() {
642
611
  this.state = 5 /* HAPConnectionState.CLOSED */;
643
612
  debugCon("[%s] Client connection closed", this.remoteAddress);
644
613
  if (this.httpSocket) {
@@ -647,159 +616,91 @@ var HAPConnection = /** @class */ (function (_super) {
647
616
  this.internalHttpServer.close();
648
617
  this.emit("closed" /* HAPConnectionEvent.CLOSED */); // sending final closed event
649
618
  this.removeAllListeners(); // cleanup listeners, we are officially dead now
650
- };
651
- HAPConnection.prototype.onHttpServerError = function (err) {
619
+ }
620
+ onHttpServerError(err) {
652
621
  debugCon("[%s] HTTP server error: %s", this.remoteAddress, err.message);
653
622
  if (err.code === "EADDRINUSE") {
654
623
  this.internalHttpServerPort = undefined;
655
624
  this.internalHttpServer.close();
656
625
  this.internalHttpServer.listen(0, this.internalHttpServerAddress = (0, net_utils_1.getOSLoopbackAddressIfAvailable)());
657
626
  }
658
- };
659
- HAPConnection.prototype.onHttpServerClose = function () {
627
+ }
628
+ onHttpServerClose() {
660
629
  debugCon("[%s] HTTP server was closed", this.remoteAddress);
661
630
  // make sure the iOS side is closed as well
662
631
  this.close();
663
- };
664
- HAPConnection.prototype.onHttpSocketError = function (err) {
632
+ }
633
+ onHttpSocketError(err) {
665
634
  debugCon("[%s] HTTP connection error: ", this.remoteAddress, err.message);
666
635
  // onHttpSocketClose will be called next
667
- };
668
- HAPConnection.prototype.onHttpSocketClose = function () {
636
+ }
637
+ onHttpSocketClose() {
669
638
  debugCon("[%s] HTTP connection was closed", this.remoteAddress);
670
639
  // we only support a single long-lived connection to our internal HTTP server. Since it's closed,
671
640
  // we'll need to shut it down entirely.
672
641
  this.internalHttpServer.close();
673
- };
674
- HAPConnection.prototype.getLocalAddress = function (ipVersion) {
675
- var e_6, _a, e_7, _b;
676
- var _c;
677
- var infos = (_c = os_1.default.networkInterfaces()[this.networkInterface]) !== null && _c !== void 0 ? _c : [];
678
- if (ipVersion === "ipv4") {
679
- try {
680
- for (var infos_1 = tslib_1.__values(infos), infos_1_1 = infos_1.next(); !infos_1_1.done; infos_1_1 = infos_1.next()) {
681
- var info = infos_1_1.value;
682
- // @ts-expect-error Nodejs 18+ uses the number 4 the string "IPv4"
683
- if (info.family === "IPv4" || info.family === 4) {
684
- return info.address;
685
- }
686
- }
687
- }
688
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
689
- finally {
690
- try {
691
- if (infos_1_1 && !infos_1_1.done && (_a = infos_1.return)) _a.call(infos_1);
692
- }
693
- finally { if (e_6) throw e_6.error; }
694
- }
642
+ }
643
+ getLocalAddress(ipVersion) {
644
+ const interfaceDetails = os_1.default.networkInterfaces()[this.networkInterface];
645
+ if (!interfaceDetails) {
695
646
  throw new Error("Could not find " + ipVersion + " address for interface " + this.networkInterface);
696
647
  }
697
- else {
698
- var localUniqueAddress = undefined;
699
- try {
700
- for (var infos_2 = tslib_1.__values(infos), infos_2_1 = infos_2.next(); !infos_2_1.done; infos_2_1 = infos_2.next()) {
701
- var info = infos_2_1.value;
702
- // @ts-expect-error Nodejs 18+ uses the number 6 instead of the string "IPv6"
703
- if (info.family === "IPv6" || info.family === 6) {
704
- if (!info.scopeid) {
705
- return info.address;
706
- }
707
- else if (!localUniqueAddress) {
708
- localUniqueAddress = info.address;
709
- }
710
- }
711
- }
712
- }
713
- catch (e_7_1) { e_7 = { error: e_7_1 }; }
714
- finally {
715
- try {
716
- if (infos_2_1 && !infos_2_1.done && (_b = infos_2.return)) _b.call(infos_2);
717
- }
718
- finally { if (e_7) throw e_7.error; }
719
- }
720
- if (!localUniqueAddress) {
721
- throw new Error("Could not find " + ipVersion + " address for interface " + this.networkInterface);
722
- }
723
- return localUniqueAddress;
724
- }
725
- };
726
- HAPConnection.getLocalNetworkInterface = function (socket) {
727
- var e_8, _a, e_9, _b, e_10, _c, e_11, _d;
728
- var localAddress = socket.localAddress;
729
- if (localAddress.startsWith("::ffff:")) { // IPv4-Mapped IPv6 Address https://tools.ietf.org/html/rfc4291#section-2.5.5.2
730
- localAddress = localAddress.substring(7);
731
- }
732
- else {
733
- var index = localAddress.indexOf("%");
734
- if (index !== -1) { // link-local ipv6
735
- localAddress = localAddress.substring(0, index);
648
+ // Find our first local IPv4 address.
649
+ if (ipVersion === "ipv4") {
650
+ const ipv4Info = interfaceDetails.find(info => info.family === "IPv4");
651
+ if (ipv4Info) {
652
+ return ipv4Info.address;
736
653
  }
654
+ throw new Error("Could not find " + ipVersion + " address for interface " + this.networkInterface + ".");
737
655
  }
738
- var interfaces = os_1.default.networkInterfaces();
739
- try {
740
- for (var _e = tslib_1.__values(Object.entries(interfaces)), _f = _e.next(); !_f.done; _f = _e.next()) {
741
- var _g = tslib_1.__read(_f.value, 2), name = _g[0], infos = _g[1];
742
- try {
743
- for (var _h = (e_9 = void 0, tslib_1.__values(infos !== null && infos !== void 0 ? infos : [])), _j = _h.next(); !_j.done; _j = _h.next()) {
744
- var info = _j.value;
745
- if (info.address === localAddress) {
746
- return name;
747
- }
748
- }
749
- }
750
- catch (e_9_1) { e_9 = { error: e_9_1 }; }
751
- finally {
752
- try {
753
- if (_j && !_j.done && (_b = _h.return)) _b.call(_h);
754
- }
755
- finally { if (e_9) throw e_9.error; }
756
- }
656
+ let localUniqueAddress;
657
+ for (const v6entry of interfaceDetails.filter(entry => entry.family === "IPv6")) {
658
+ if (!v6entry.scopeid) {
659
+ return v6entry.address;
757
660
  }
661
+ localUniqueAddress ??= v6entry.address;
758
662
  }
759
- catch (e_8_1) { e_8 = { error: e_8_1 }; }
760
- finally {
761
- try {
762
- if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
763
- }
764
- finally { if (e_8) throw e_8.error; }
663
+ if (localUniqueAddress) {
664
+ return localUniqueAddress;
765
665
  }
766
- // we couldn't map the address from above, we try now to match subnets (see https://github.com/homebridge/HAP-NodeJS/issues/847)
767
- var family = net_1.default.isIPv4(localAddress) ? "IPv4" : "IPv6";
768
- try {
769
- for (var _k = tslib_1.__values(Object.entries(interfaces)), _l = _k.next(); !_l.done; _l = _k.next()) {
770
- var _m = tslib_1.__read(_l.value, 2), name = _m[0], infos = _m[1];
771
- try {
772
- for (var _o = (e_11 = void 0, tslib_1.__values(infos !== null && infos !== void 0 ? infos : [])), _p = _o.next(); !_p.done; _p = _o.next()) {
773
- var info = _p.value;
774
- if (info.family !== family) {
775
- continue;
776
- }
777
- // check if the localAddress is in the same subnet
778
- if ((0, domain_formatter_1.getNetAddress)(localAddress, info.netmask) === (0, domain_formatter_1.getNetAddress)(info.address, info.netmask)) {
779
- return name;
780
- }
781
- }
782
- }
783
- catch (e_11_1) { e_11 = { error: e_11_1 }; }
784
- finally {
785
- try {
786
- if (_p && !_p.done && (_d = _o.return)) _d.call(_o);
787
- }
788
- finally { if (e_11) throw e_11.error; }
789
- }
666
+ throw new Error("Could not find " + ipVersion + " address for interface " + this.networkInterface);
667
+ }
668
+ static getLocalNetworkInterface(socket) {
669
+ let localAddress = socket.localAddress;
670
+ // Grab the list of network interfaces.
671
+ const interfaces = os_1.default.networkInterfaces();
672
+ // Default to the first non-loopback interface we see.
673
+ const defaultInterface = () => Object.entries(interfaces).find(([, addresses]) => addresses?.some(address => !address.internal))?.[0] ?? "unknown";
674
+ // No local address return our default.
675
+ if (!localAddress) {
676
+ return defaultInterface();
677
+ }
678
+ // Handle IPv4-mapped IPv6 addresses.
679
+ localAddress = localAddress.replace(/^::ffff:/i, "");
680
+ // Handle edge cases where we have an IPv4-mapped IPv6 address without the requisite prefix.
681
+ if (/^::(?:\d{1,3}\.){3}\d{1,3}$/.test(localAddress)) {
682
+ localAddress = localAddress.replace(/^::/, "");
683
+ }
684
+ // Handle link-local IPv6 addresses.
685
+ localAddress = localAddress.split("%")[0];
686
+ // Let's find an exact match using the IP.
687
+ for (const [name, addresses] of Object.entries(interfaces)) {
688
+ if (addresses?.some(({ address }) => address === localAddress)) {
689
+ return name;
790
690
  }
791
691
  }
792
- catch (e_10_1) { e_10 = { error: e_10_1 }; }
793
- finally {
794
- try {
795
- if (_l && !_l.done && (_c = _k.return)) _c.call(_k);
692
+ // We couldn't find an interface to match the address from above, so we attempt to match subnets (see https://github.com/homebridge/HAP-NodeJS/issues/847).
693
+ const family = net_1.default.isIPv4(localAddress) ? "IPv4" : "IPv6";
694
+ // Let's find a match based on the subnet.
695
+ for (const [name, addresses] of Object.entries(interfaces)) {
696
+ if (addresses?.some(entry => entry.family === family && (0, domain_formatter_1.getNetAddress)(localAddress, entry.netmask) === (0, domain_formatter_1.getNetAddress)(entry.address, entry.netmask))) {
697
+ return name;
796
698
  }
797
- finally { if (e_10) throw e_10.error; }
798
699
  }
799
- console.log("WARNING couldn't map socket coming from remote address ".concat(socket.remoteAddress, ":").concat(socket.remotePort, " at local address ").concat(socket.localAddress, " to a interface!"));
800
- return Object.keys(interfaces)[1]; // just use the first interface after the loopback interface as fallback
801
- };
802
- return HAPConnection;
803
- }(events_1.EventEmitter));
700
+ console.log("WARNING: unable to determine which interface to use for socket coming from " + socket.remoteAddress + ":" + socket.remotePort + " to " +
701
+ socket.localAddress + ".");
702
+ return defaultInterface();
703
+ }
704
+ }
804
705
  exports.HAPConnection = HAPConnection;
805
706
  //# sourceMappingURL=eventedhttp.js.map