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