mysa-js-sdk 2.0.2 → 2.1.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.
- package/README.md +4 -3
- package/dist/index.cjs +123 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -2
- package/dist/index.d.ts +63 -2
- package/dist/index.js +124 -18
- package/dist/index.js.map +1 -1
- package/package.json +30 -28
package/README.md
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# Mysa Smart Thermostat JavaScript SDK
|
|
2
2
|
|
|
3
3
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
4
|
-
|
|
5
|
-
[](#contributors-)
|
|
6
|
-
|
|
4
|
+
[](#contributors-)
|
|
7
5
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
8
6
|
|
|
9
7
|
[](https://www.npmjs.com/package/mysa-js-sdk)
|
|
@@ -248,6 +246,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
248
246
|
<tr>
|
|
249
247
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jagmandan"><img src="https://avatars.githubusercontent.com/u/227265405?v=4?s=100" width="100px;" alt="jagmandan"/><br /><sub><b>jagmandan</b></sub></a><br /><a href="https://github.com/bourquep/mysa-js-sdk/commits?author=jagmandan" title="Code">💻</a></td>
|
|
250
248
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/remiolivier"><img src="https://avatars.githubusercontent.com/u/1379047?v=4?s=100" width="100px;" alt="remiolivier"/><br /><sub><b>remiolivier</b></sub></a><br /><a href="https://github.com/bourquep/mysa-js-sdk/commits?author=remiolivier" title="Code">💻</a></td>
|
|
249
|
+
<td align="center" valign="top" width="14.28%"><a href="https://getbindery.dev"><img src="https://avatars.githubusercontent.com/u/75899046?v=4?s=100" width="100px;" alt="vavallee"/><br /><sub><b>vavallee</b></sub></a><br /><a href="https://github.com/bourquep/mysa-js-sdk/commits?author=vavallee" title="Code">💻</a></td>
|
|
250
|
+
<td align="center" valign="top" width="14.28%"><a href="http://olicarbo.me"><img src="https://avatars.githubusercontent.com/u/1908784?v=4?s=100" width="100px;" alt="Olivier Carbonneau"/><br /><sub><b>Olivier Carbonneau</b></sub></a><br /><a href="https://github.com/bourquep/mysa-js-sdk/commits?author=olicarbo" title="Code">💻</a></td>
|
|
251
|
+
<td align="center" valign="top" width="14.28%"><a href="https://element26.net"><img src="https://avatars.githubusercontent.com/u/7200365?v=4?s=100" width="100px;" alt="Daniel Caspi"/><br /><sub><b>Daniel Caspi</b></sub></a><br /><a href="https://github.com/bourquep/mysa-js-sdk/commits?author=dxdc" title="Code">💻</a></td>
|
|
251
252
|
</tr>
|
|
252
253
|
</tbody>
|
|
253
254
|
</table>
|
package/dist/index.cjs
CHANGED
|
@@ -108,6 +108,8 @@ var MqttPublishError = class extends Error {
|
|
|
108
108
|
this.original = original;
|
|
109
109
|
this.name = "MqttPublishError";
|
|
110
110
|
}
|
|
111
|
+
attempts;
|
|
112
|
+
original;
|
|
111
113
|
};
|
|
112
114
|
|
|
113
115
|
// src/api/Logger.ts
|
|
@@ -222,6 +224,7 @@ var OutMessageType = /* @__PURE__ */ ((OutMessageType2) => {
|
|
|
222
224
|
OutMessageType2[OutMessageType2["DEVICE_LOG"] = 4] = "DEVICE_LOG";
|
|
223
225
|
OutMessageType2[OutMessageType2["DEVICE_POST_BOOT"] = 10] = "DEVICE_POST_BOOT";
|
|
224
226
|
OutMessageType2[OutMessageType2["DEVICE_V2_STATUS"] = 40] = "DEVICE_V2_STATUS";
|
|
227
|
+
OutMessageType2[OutMessageType2["DEVICE_AC_STATUS"] = 30] = "DEVICE_AC_STATUS";
|
|
225
228
|
OutMessageType2[OutMessageType2["DEVICE_STATE_CHANGE"] = 44] = "DEVICE_STATE_CHANGE";
|
|
226
229
|
return OutMessageType2;
|
|
227
230
|
})(OutMessageType || {});
|
|
@@ -231,6 +234,7 @@ var import_client_iot = require("@aws-sdk/client-iot");
|
|
|
231
234
|
var import_credential_providers = require("@aws-sdk/credential-providers");
|
|
232
235
|
var import_amazon_cognito_identity_js = require("amazon-cognito-identity-js");
|
|
233
236
|
var import_aws_iot_device_sdk_v2 = require("aws-iot-device-sdk-v2");
|
|
237
|
+
var import_crypto = require("crypto");
|
|
234
238
|
var import_dayjs = __toESM(require("dayjs"), 1);
|
|
235
239
|
var import_duration = __toESM(require("dayjs/plugin/duration.js"), 1);
|
|
236
240
|
var import_nanoid = require("nanoid");
|
|
@@ -257,8 +261,12 @@ var MysaApiClient = class {
|
|
|
257
261
|
_mqttConnectionPromise;
|
|
258
262
|
/** Stable per-process MQTT client id (prevents collisions between multiple processes). */
|
|
259
263
|
_mqttClientId;
|
|
264
|
+
/** Expiration time of the credentials currently in use by the MQTT client. */
|
|
265
|
+
_mqttCredentialsExpiration;
|
|
260
266
|
/** Interrupt timestamps for storm / collision detection. */
|
|
261
267
|
_mqttInterrupts = [];
|
|
268
|
+
/** Whether a forced MQTT reset is currently in progress (guards against re-entrancy). */
|
|
269
|
+
_mqttResetInProgress = false;
|
|
262
270
|
/** The device IDs that are currently being updated in real-time, mapped to their respective timeouts. */
|
|
263
271
|
_realtimeDeviceIds = /* @__PURE__ */ new Map();
|
|
264
272
|
/** The cached devices object, if any. */
|
|
@@ -483,6 +491,26 @@ var MysaApiClient = class {
|
|
|
483
491
|
}
|
|
484
492
|
return response.json();
|
|
485
493
|
}
|
|
494
|
+
/**
|
|
495
|
+
* Retrieves information about all homes associated with the user.
|
|
496
|
+
*
|
|
497
|
+
* @returns A promise that resolves to the homes information.
|
|
498
|
+
* @throws {@link MysaApiError} When the API request fails.
|
|
499
|
+
* @throws {@link UnauthenticatedError} When the user is not authenticated.
|
|
500
|
+
*/
|
|
501
|
+
async getHomes() {
|
|
502
|
+
this._logger.debug(`Fetching homes...`);
|
|
503
|
+
const session = await this._getFreshSession();
|
|
504
|
+
const response = await this._fetcher(`${MysaApiBaseUrl}/homes`, {
|
|
505
|
+
headers: {
|
|
506
|
+
Authorization: `${session.getIdToken().getJwtToken()}`
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
if (!response.ok) {
|
|
510
|
+
throw new MysaApiError(response);
|
|
511
|
+
}
|
|
512
|
+
return response.json();
|
|
513
|
+
}
|
|
486
514
|
/**
|
|
487
515
|
* Sets the state of a specific device by sending commands via MQTT.
|
|
488
516
|
*
|
|
@@ -542,7 +570,7 @@ var MysaApiClient = class {
|
|
|
542
570
|
resp: 2,
|
|
543
571
|
body: {
|
|
544
572
|
ver: 1,
|
|
545
|
-
type: device.Model.startsWith("BB-V1") ? 1 : device.Model.startsWith("AC-V1") ? 2 : device.Model.startsWith("BB-V2") ? device.Model.endsWith("-L") ? 5 : 4 : 0,
|
|
573
|
+
type: device.Model.startsWith("BB-V1") || device.Model.startsWith("v1") ? 1 : device.Model.startsWith("AC-V1") ? 2 : device.Model.startsWith("BB-V2") ? device.Model.endsWith("-L") ? 5 : 4 : 0,
|
|
546
574
|
cmd: [
|
|
547
575
|
{
|
|
548
576
|
tm: -1,
|
|
@@ -604,13 +632,14 @@ var MysaApiClient = class {
|
|
|
604
632
|
await this._publishWithRetry(mqttConnection, `/v1/dev/${deviceId}/in`, payload, import_aws_iot_device_sdk_v2.mqtt.QoS.AtLeastOnce);
|
|
605
633
|
const timer = setInterval(async () => {
|
|
606
634
|
this._logger.debug(`Sending request to keep-alive publishing device status for '${deviceId}'...`);
|
|
635
|
+
const connection = await this._getMqttConnection();
|
|
607
636
|
const payload2 = serializeMqttPayload({
|
|
608
637
|
Device: deviceId,
|
|
609
638
|
MsgType: 11 /* START_PUBLISHING_DEVICE_STATUS */,
|
|
610
639
|
Timestamp: (0, import_dayjs.default)().unix(),
|
|
611
640
|
Timeout: RealtimeKeepAliveInterval.asSeconds()
|
|
612
641
|
});
|
|
613
|
-
await this._publishWithRetry(
|
|
642
|
+
await this._publishWithRetry(connection, `/v1/dev/${deviceId}/in`, payload2, import_aws_iot_device_sdk_v2.mqtt.QoS.AtLeastOnce);
|
|
614
643
|
}, RealtimeKeepAliveInterval.subtract(10, "seconds").asMilliseconds());
|
|
615
644
|
this._realtimeDeviceIds.set(deviceId, timer);
|
|
616
645
|
}
|
|
@@ -709,6 +738,7 @@ var MysaApiClient = class {
|
|
|
709
738
|
"AWS_ERROR_MQTT_NO_CONNECTION",
|
|
710
739
|
"AWS_ERROR_MQTT_UNEXPECTED_HANGUP",
|
|
711
740
|
"UNEXPECTED_HANGUP",
|
|
741
|
+
"AWS_ERROR_MQTT_CONNECTION_DESTROYED",
|
|
712
742
|
"Time limit between request and response",
|
|
713
743
|
"timeout"
|
|
714
744
|
];
|
|
@@ -783,46 +813,120 @@ var MysaApiClient = class {
|
|
|
783
813
|
logger: this._logger
|
|
784
814
|
});
|
|
785
815
|
const credentials = await credentialsProvider();
|
|
816
|
+
if (!credentials.expiration) {
|
|
817
|
+
throw new Error("MQTT credentials do not have an expiration time.");
|
|
818
|
+
}
|
|
819
|
+
this._mqttCredentialsExpiration = (0, import_dayjs.default)(credentials.expiration);
|
|
820
|
+
this._logger.debug(`MQTT credentials expiration: ${this._mqttCredentialsExpiration.format()}`);
|
|
821
|
+
if (!this._mqttCredentialsExpiration.isAfter((0, import_dayjs.default)())) {
|
|
822
|
+
this._mqttCredentialsExpiration = void 0;
|
|
823
|
+
throw new Error("MQTT credentials are already expired.");
|
|
824
|
+
}
|
|
786
825
|
if (!this._mqttClientId) {
|
|
787
|
-
|
|
826
|
+
const username = ((_a = this.session) == null ? void 0 : _a.username) ?? "anon";
|
|
827
|
+
const usernameHash = (0, import_crypto.hash)("sha1", username);
|
|
828
|
+
this._mqttClientId = `mysa-js-sdk-${usernameHash}-${process.pid}-${getRandomClientId()}`;
|
|
788
829
|
}
|
|
789
830
|
const builder = import_aws_iot_device_sdk_v2.iot.AwsIotMqttConnectionConfigBuilder.new_with_websockets().with_credentials(AwsRegion, credentials.accessKeyId, credentials.secretAccessKey, credentials.sessionToken).with_endpoint(MqttEndpoint).with_client_id(this._mqttClientId).with_clean_session(false).with_keep_alive_seconds(30).with_ping_timeout_ms(3e3).with_protocol_operation_timeout_ms(6e4).with_reconnect_min_sec(1).with_reconnect_max_sec(30);
|
|
790
831
|
const config = builder.build();
|
|
832
|
+
config.websocket_handshake_transform = async (request, done) => {
|
|
833
|
+
try {
|
|
834
|
+
const freshSession = await this._getFreshSession();
|
|
835
|
+
const freshCredentialsProvider = (0, import_credential_providers.fromCognitoIdentityPool)({
|
|
836
|
+
clientConfig: {
|
|
837
|
+
region: AwsRegion
|
|
838
|
+
},
|
|
839
|
+
identityPoolId: CognitoIdentityPoolId,
|
|
840
|
+
logins: {
|
|
841
|
+
[CognitoLoginKey]: freshSession.getIdToken().getJwtToken()
|
|
842
|
+
},
|
|
843
|
+
logger: this._logger
|
|
844
|
+
});
|
|
845
|
+
const freshCredentials = await freshCredentialsProvider();
|
|
846
|
+
if (freshCredentials.expiration) {
|
|
847
|
+
this._mqttCredentialsExpiration = (0, import_dayjs.default)(freshCredentials.expiration);
|
|
848
|
+
}
|
|
849
|
+
await import_aws_iot_device_sdk_v2.auth.aws_sign_request(request, {
|
|
850
|
+
algorithm: import_aws_iot_device_sdk_v2.auth.AwsSigningAlgorithm.SigV4,
|
|
851
|
+
signature_type: import_aws_iot_device_sdk_v2.auth.AwsSignatureType.HttpRequestViaQueryParams,
|
|
852
|
+
provider: import_aws_iot_device_sdk_v2.auth.AwsCredentialsProvider.newStatic(
|
|
853
|
+
freshCredentials.accessKeyId,
|
|
854
|
+
freshCredentials.secretAccessKey,
|
|
855
|
+
freshCredentials.sessionToken
|
|
856
|
+
),
|
|
857
|
+
region: AwsRegion,
|
|
858
|
+
service: "iotdevicegateway",
|
|
859
|
+
signed_body_value: import_aws_iot_device_sdk_v2.auth.AwsSignedBodyValue.EmptySha256,
|
|
860
|
+
omit_session_token: true
|
|
861
|
+
});
|
|
862
|
+
done();
|
|
863
|
+
} catch (error) {
|
|
864
|
+
this._logger.error("Failed to sign MQTT websocket handshake with fresh credentials", error);
|
|
865
|
+
done(
|
|
866
|
+
3
|
|
867
|
+
/* AWS_ERROR_UNKNOWN: fail this attempt; the reconnect loop retries */
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
};
|
|
791
871
|
const client = new import_aws_iot_device_sdk_v2.mqtt.MqttClient();
|
|
792
872
|
const connection = client.new_connection(config);
|
|
793
873
|
connection.on("connect", () => {
|
|
794
|
-
this._logger.debug(
|
|
874
|
+
this._logger.debug(`MQTT connect (clientId=${this._mqttClientId})`);
|
|
795
875
|
});
|
|
796
876
|
connection.on("connection_success", () => {
|
|
797
|
-
this._logger.debug(
|
|
877
|
+
this._logger.debug(`MQTT connection_success (clientId=${this._mqttClientId})`);
|
|
798
878
|
});
|
|
799
879
|
connection.on("connection_failure", (e) => {
|
|
800
|
-
this._logger.error(
|
|
880
|
+
this._logger.error(`MQTT connection_failure (clientId=${this._mqttClientId})`, e);
|
|
801
881
|
});
|
|
802
882
|
connection.on("interrupt", async (e) => {
|
|
803
|
-
|
|
883
|
+
var _a2;
|
|
884
|
+
this._logger.warn(`MQTT interrupt (clientId=${this._mqttClientId})`, e);
|
|
804
885
|
const now = Date.now();
|
|
805
886
|
this._mqttInterrupts = this._mqttInterrupts.filter((t) => now - t < 6e4);
|
|
806
887
|
this._mqttInterrupts.push(now);
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
)
|
|
888
|
+
const areCredentialsExpired = !(((_a2 = this._mqttCredentialsExpiration) == null ? void 0 : _a2.isAfter((0, import_dayjs.default)())) ?? false);
|
|
889
|
+
if ((this._mqttInterrupts.length > 5 || areCredentialsExpired) && !this._mqttResetInProgress) {
|
|
890
|
+
this._mqttResetInProgress = true;
|
|
891
|
+
if (this._mqttInterrupts.length > 5) {
|
|
892
|
+
this._logger.warn(
|
|
893
|
+
`High interrupt rate (${this._mqttInterrupts.length}/60s). Possible clientId collision. Regenerating clientId and resetting connection...`
|
|
894
|
+
);
|
|
895
|
+
} else {
|
|
896
|
+
this._logger.warn(`Credentials expired. Regenerating clientId and resetting connection...`);
|
|
897
|
+
}
|
|
811
898
|
this._mqttClientId = void 0;
|
|
899
|
+
this._mqttCredentialsExpiration = void 0;
|
|
812
900
|
this._mqttInterrupts = [];
|
|
901
|
+
this._mqttConnectionPromise = void 0;
|
|
813
902
|
try {
|
|
903
|
+
connection.removeAllListeners();
|
|
814
904
|
await connection.disconnect();
|
|
815
|
-
|
|
816
|
-
this._logger.
|
|
817
|
-
|
|
905
|
+
try {
|
|
906
|
+
this._logger.debug("Old MQTT connection disconnected; establishing new connection...");
|
|
907
|
+
const newConnection = await this._getMqttConnection();
|
|
908
|
+
for (const deviceId of Array.from(this._realtimeDeviceIds.keys())) {
|
|
909
|
+
const topic = `/v1/dev/${deviceId}/out`;
|
|
910
|
+
this._logger.debug(`Re-subscribing to ${topic}`);
|
|
911
|
+
await newConnection.subscribe(topic, import_aws_iot_device_sdk_v2.mqtt.QoS.AtLeastOnce, (_topic, payload) => {
|
|
912
|
+
this._processMqttMessage(payload);
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
this._logger.info("MQTT connection rebuilt successfully after interrupt storm or credentials expiration");
|
|
916
|
+
} catch (err) {
|
|
917
|
+
this._logger.error("Failed to re-subscribe after interrupt storm or credentials expiration", err);
|
|
818
918
|
}
|
|
819
919
|
} catch (error) {
|
|
820
|
-
this._logger.error("
|
|
920
|
+
this._logger.error("Error during MQTT reset", error);
|
|
921
|
+
} finally {
|
|
922
|
+
this._mqttResetInProgress = false;
|
|
821
923
|
}
|
|
822
924
|
}
|
|
823
925
|
});
|
|
824
926
|
connection.on("resume", async (returnCode, sessionPresent) => {
|
|
825
|
-
this._logger.info(
|
|
927
|
+
this._logger.info(
|
|
928
|
+
`MQTT resume returnCode=${returnCode} sessionPresent=${sessionPresent} clientId=${this._mqttClientId}`
|
|
929
|
+
);
|
|
826
930
|
if (!sessionPresent) {
|
|
827
931
|
this._logger.info("No session present, re-subscribing each device");
|
|
828
932
|
try {
|
|
@@ -839,11 +943,12 @@ var MysaApiClient = class {
|
|
|
839
943
|
}
|
|
840
944
|
});
|
|
841
945
|
connection.on("error", (e) => {
|
|
842
|
-
this._logger.error(
|
|
946
|
+
this._logger.error(`MQTT error (clientId=${this._mqttClientId})`, e);
|
|
843
947
|
});
|
|
844
948
|
connection.on("closed", () => {
|
|
845
949
|
this._logger.info("MQTT connection closed");
|
|
846
950
|
this._mqttConnectionPromise = void 0;
|
|
951
|
+
this._mqttCredentialsExpiration = void 0;
|
|
847
952
|
});
|
|
848
953
|
await connection.connect();
|
|
849
954
|
return connection;
|
|
@@ -882,6 +987,7 @@ var MysaApiClient = class {
|
|
|
882
987
|
}
|
|
883
988
|
} else if (isMsgOutPayload(parsedPayload)) {
|
|
884
989
|
switch (parsedPayload.msg) {
|
|
990
|
+
case 30 /* DEVICE_AC_STATUS */:
|
|
885
991
|
case 40 /* DEVICE_V2_STATUS */:
|
|
886
992
|
this.emitter.emit("statusChanged", {
|
|
887
993
|
deviceId: parsedPayload.src.ref,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/api/Errors.ts","../src/api/Logger.ts","../src/lib/EventEmitter.ts","../src/lib/PayloadParser.ts","../src/lib/PayloadTypeGuards.ts","../src/types/mqtt/in/InMessageType.ts","../src/types/mqtt/out/OutMessageType.ts","../src/api/MysaApiClient.ts"],"sourcesContent":["export * from './api';\nexport * from './types';\n","/** Error thrown when attempting to access the Mysa API without proper authentication. */\nexport class UnauthenticatedError extends Error {\n /**\n * Creates a new UnauthenticatedError instance.\n *\n * @param message - The error message\n */\n constructor(message: string) {\n super(message);\n this.name = 'UnauthenticatedError';\n }\n}\n\n/** Error thrown when a Mysa API request fails. */\nexport class MysaApiError extends Error {\n /** The HTTP status code returned by the API */\n readonly status: number;\n /** The HTTP status text returned by the API */\n readonly statusText: string;\n\n /**\n * Creates a new MysaApiError instance.\n *\n * @param apiResponse - The failed Response object from the API call\n */\n constructor(apiResponse: Response) {\n super(\n `Failed to call the '${apiResponse.url}' Mysa API endpoint. The server responded with a status of ${apiResponse.status} (${apiResponse.statusText}).`\n );\n this.name = 'MysaApiError';\n this.status = apiResponse.status;\n this.statusText = apiResponse.statusText;\n }\n}\n\n/** Error thrown when an MQTT publish ultimately fails after retry attempts. */\nexport class MqttPublishError extends Error {\n /**\n * Creates a new MqttPublishError instance.\n *\n * @param message - A human-readable description of the publish failure.\n * @param attempts - The number of attempts that were made before giving up.\n * @param original - The original error object thrown by the underlying MQTT library (optional).\n */\n constructor(\n message: string,\n public attempts: number,\n public original?: unknown\n ) {\n super(message);\n this.name = 'MqttPublishError';\n }\n}\n","/** Interface for logging operations at different severity levels */\nexport interface Logger {\n /** Logs a debug message with optional metadata */\n debug(message: string, ...meta: unknown[]): void;\n\n /** Logs an info message with optional metadata */\n info(message: string, ...meta: unknown[]): void;\n\n /** Logs a warning message with optional metadata */\n warn(message: string, ...meta: unknown[]): void;\n\n /** Logs an error message with optional metadata */\n error(message: string, ...meta: unknown[]): void;\n}\n\n/** Logger implementation that silently discards all log messages. */\n/* eslint-disable @typescript-eslint/no-unused-vars */\nexport class VoidLogger implements Logger {\n debug(message: string, ...meta: unknown[]): void {}\n info(message: string, ...meta: unknown[]): void {}\n warn(message: string, ...meta: unknown[]): void {}\n error(message: string, ...meta: unknown[]): void {}\n}\n/* eslint-enable @typescript-eslint/no-unused-vars */\n","import { EventEmitter as NodeEventEmitter } from 'node:events';\n\n/**\n * Typed wrapper around Node's `EventEmitter` class.\n *\n * @remarks\n * Source: {@link https://blog.makerx.com.au/a-type-safe-event-emitter-in-node-js}\n */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport class EventEmitter<TEvents extends Record<string, any>> implements NodeJS.EventEmitter {\n private _emitter = new NodeEventEmitter();\n\n emit<TEventName extends keyof TEvents & string>(eventName: TEventName, ...eventArg: TEvents[TEventName]) {\n return this._emitter.emit(eventName, ...(eventArg as []));\n }\n\n on<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n handler: (...eventArg: TEvents[TEventName]) => void\n ) {\n this._emitter.on(eventName, handler as any);\n return this;\n }\n\n once<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n handler: (...eventArg: TEvents[TEventName]) => void\n ) {\n this._emitter.once(eventName, handler as any);\n return this;\n }\n\n off<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n handler: (...eventArg: TEvents[TEventName]) => void\n ) {\n this._emitter.off(eventName, handler as any);\n return this;\n }\n\n addListener<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n listener: (...args: TEvents[TEventName]) => void\n ) {\n this._emitter.addListener(eventName, listener);\n return this;\n }\n\n removeListener<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n listener: (...args: TEvents[TEventName]) => void\n ) {\n this._emitter.removeListener(eventName, listener);\n return this;\n }\n\n removeAllListeners<TEventName extends keyof TEvents & string>(eventName?: TEventName | undefined) {\n this._emitter.removeAllListeners(eventName);\n return this;\n }\n\n setMaxListeners(n: number) {\n this._emitter.setMaxListeners(n);\n return this;\n }\n\n getMaxListeners(): number {\n return this._emitter.getMaxListeners();\n }\n\n listeners<TEventName extends keyof TEvents & string>(eventName: TEventName) {\n return this._emitter.listeners(eventName);\n }\n\n rawListeners<TEventName extends keyof TEvents & string>(eventName: TEventName) {\n return this._emitter.rawListeners(eventName);\n }\n\n listenerCount<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n listener?: (...args: TEvents[TEventName]) => void\n ) {\n return this._emitter.listenerCount(eventName, listener);\n }\n\n prependListener<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n listener: (...args: TEvents[TEventName]) => void\n ) {\n this._emitter.prependListener(eventName, listener);\n return this;\n }\n\n prependOnceListener<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n listener: (...args: TEvents[TEventName]) => void\n ) {\n this._emitter.prependOnceListener(eventName, listener);\n return this;\n }\n\n eventNames() {\n return this._emitter.eventNames();\n }\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n","import { InPayload } from '@/types/mqtt/InPayload';\nimport { OutPayload } from '@/types/mqtt/OutPayload';\n\n/**\n * Parses an MQTT payload from binary data into a typed OutPayload object.\n *\n * Converts the raw ArrayBuffer received from MQTT messages into a structured TypeScript object representing device\n * status, state changes, or other outgoing message types from Mysa devices.\n *\n * @param payload - The raw binary MQTT message payload as ArrayBuffer\n * @returns The parsed payload as a typed OutPayload object\n * @throws Error if the payload cannot be decoded or parsed as valid JSON\n */\nexport function parseMqttPayload(payload: ArrayBuffer): OutPayload {\n try {\n const decoder = new TextDecoder('utf-8');\n const jsonString = decoder.decode(payload);\n return JSON.parse(jsonString);\n } catch (error) {\n console.error('Error parsing MQTT payload:', error);\n throw new Error('Failed to parse MQTT payload');\n }\n}\n\n/**\n * Serializes an InPayload object into binary data for MQTT transmission.\n *\n * Converts a typed TypeScript payload object into the binary ArrayBuffer format required for sending commands and\n * requests to Mysa devices via MQTT.\n *\n * @typeParam T - The specific InPayload type being serialized\n * @param payload - The typed payload object to serialize\n * @returns The serialized payload as ArrayBuffer ready for MQTT transmission\n */\nexport function serializeMqttPayload<T extends InPayload>(payload: T): Uint8Array<ArrayBuffer> {\n const jsonString = JSON.stringify(payload);\n const encoder = new TextEncoder();\n return encoder.encode(jsonString);\n}\n","import { MsgOutPayload } from '@/types/mqtt/MsgOutPayload';\nimport { MsgTypeOutPayload } from '@/types/mqtt/MsgTypeOutPayload';\nimport { OutPayload } from '@/types/mqtt/OutPayload';\n\n/**\n * Type guard function to determine if an OutPayload is a MsgType-based payload.\n *\n * Checks whether the payload uses the legacy MsgType field format for message type identification. This is used to\n * differentiate between different payload structures and ensure proper type narrowing in TypeScript.\n *\n * @param payload - The OutPayload to check\n * @returns True if the payload is a MsgTypeOutPayload, false otherwise\n */\nexport function isMsgTypeOutPayload(payload: OutPayload): payload is MsgTypeOutPayload {\n return 'MsgType' in payload;\n}\n\n/**\n * Type guard function to determine if an OutPayload is a message-based payload.\n *\n * Checks whether the payload uses the newer msg field format for message type identification. This is used to\n * differentiate between different payload structures and ensure proper type narrowing in TypeScript.\n *\n * @param payload - The OutPayload to check\n * @returns True if the payload is a MsgOutPayload, false otherwise\n */\nexport function isMsgOutPayload(payload: OutPayload): payload is MsgOutPayload {\n return 'msg' in payload;\n}\n","/**\n * Enumeration of message types for incoming MQTT messages from clients to devices.\n *\n * These message types determine how commands and requests are interpreted by Mysa devices. The enum values correspond\n * to specific numeric identifiers used in the MQTT protocol.\n */\nexport enum InMessageType {\n //\n // When the message type is reported in the `MsgType` field of the payload.\n //\n\n /** Request to check and retrieve current device settings */\n CHECK_DEVICE_SETTINGS = 6,\n\n /** Command to start publishing periodic device status updates */\n START_PUBLISHING_DEVICE_STATUS = 11,\n\n //\n // When the message type is reported in the `msg` field of the payload.\n //\n\n /** Command to change the current state of a device (temperature, mode, etc.) */\n CHANGE_DEVICE_STATE = 44\n}\n","/**\n * Enumeration of message types for outgoing MQTT messages from devices to clients.\n *\n * These message types identify different kinds of status updates, notifications, and data reports that Mysa devices can\n * send via MQTT. The enum values correspond to specific numeric identifiers used in the MQTT protocol.\n */\nexport enum OutMessageType {\n //\n // When the message type is reported in the `MsgType` field of the payload.\n //\n\n /** Version 1 device status report with basic device information */\n DEVICE_V1_STATUS = 0,\n\n /** Notification that a device's temperature setpoint has been changed */\n DEVICE_SETPOINT_CHANGE = 1,\n\n /** Device log entry or diagnostic information */\n DEVICE_LOG = 4,\n\n /** Notification sent when a device completes its boot sequence */\n DEVICE_POST_BOOT = 10,\n\n //\n // When the message type is reported in the `msg` field of the payload.\n //\n\n /** Version 2 device status report with enhanced device information */\n DEVICE_V2_STATUS = 40,\n\n /** Notification that a device's operational state has changed */\n DEVICE_STATE_CHANGE = 44\n}\n","import { MysaSession } from '@/api/MysaSession';\nimport { EventEmitter } from '@/lib/EventEmitter';\nimport { parseMqttPayload, serializeMqttPayload } from '@/lib/PayloadParser';\nimport { isMsgOutPayload, isMsgTypeOutPayload } from '@/lib/PayloadTypeGuards';\nimport { ChangeDeviceState } from '@/types/mqtt/in/ChangeDeviceState';\nimport { InMessageType } from '@/types/mqtt/in/InMessageType';\nimport { StartPublishingDeviceStatus } from '@/types/mqtt/in/StartPublishingDeviceStatus';\nimport { OutMessageType } from '@/types/mqtt/out/OutMessageType';\nimport { Devices, DeviceStates, Firmwares } from '@/types/rest';\nimport { DescribeThingCommand, IoTClient } from '@aws-sdk/client-iot';\nimport { fromCognitoIdentityPool } from '@aws-sdk/credential-providers';\nimport {\n AuthenticationDetails,\n CognitoAccessToken,\n CognitoIdToken,\n CognitoRefreshToken,\n CognitoUser,\n CognitoUserPool,\n CognitoUserSession\n} from 'amazon-cognito-identity-js';\nimport { iot, mqtt } from 'aws-iot-device-sdk-v2';\nimport dayjs from 'dayjs';\nimport duration from 'dayjs/plugin/duration.js';\nimport { customAlphabet } from 'nanoid';\nimport { MqttPublishError, MysaApiError, UnauthenticatedError } from './Errors';\nimport { Logger, VoidLogger } from './Logger';\nimport { MysaApiClientEventTypes } from './MysaApiClientEventTypes';\nimport { MysaApiClientOptions } from './MysaApiClientOptions';\nimport { MysaDeviceMode, MysaFanSpeedMode } from './MysaDeviceMode';\n\ndayjs.extend(duration);\n\nconst getRandomClientId = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 8);\n\n/** Options for MQTT publish operations. */\nexport interface MqttPublishOptions {\n /** Maximum number of publish attempts before failing (default: 5). */\n maxAttempts?: number;\n /** Base delay in milliseconds used for exponential backoff calculation (default: 500). */\n baseDelayMs?: number;\n}\n\nconst AwsRegion = 'us-east-1';\nconst CognitoUserPoolId = 'us-east-1_GUFWfhI7g';\nconst CognitoClientId = '19efs8tgqe942atbqmot5m36t3';\nconst CognitoIdentityPoolId = 'us-east-1:ebd95d52-9995-45da-b059-56b865a18379';\nconst CognitoLoginKey = `cognito-idp.${AwsRegion}.amazonaws.com/${CognitoUserPoolId}`;\nconst MqttEndpoint = 'a3q27gia9qg3zy-ats.iot.us-east-1.amazonaws.com';\nconst MysaApiBaseUrl = 'https://app-prod.mysa.cloud';\nconst RealtimeKeepAliveInterval = dayjs.duration(5, 'minutes');\n\n/**\n * Main client for interacting with the Mysa API and real-time device communication.\n *\n * The MysaApiClient provides a comprehensive interface for authenticating with Mysa services, managing device data, and\n * receiving real-time updates from Mysa thermostats and heating devices. It handles both REST API calls for device\n * management and MQTT connections for live status updates and control commands.\n *\n * @example\n *\n * ```typescript\n * const client = new MysaApiClient();\n *\n * await client.login('user@example.com', 'password');\n * const devices = await client.getDevices();\n *\n * client.emitter.on('statusChanged', (status) => {\n * console.log(`Device ${status.deviceId} temperature: ${status.temperature}°C`);\n * });\n *\n * for (const device of Object.entries(devices.DevicesObj)) {\n * await client.startRealtimeUpdates(device[0]);\n * }\n * ```\n */\nexport class MysaApiClient {\n /** The current session object, if any. */\n private _cognitoUserSession?: CognitoUserSession;\n\n /** The current user object, if any. */\n private _cognitoUser?: CognitoUser;\n\n /** The logger instance used by the client. */\n private _logger: Logger;\n\n /** The fetcher function used by the client. */\n private _fetcher: typeof fetch;\n\n /** A promise that resolves to the MQTT connection used for real-time updates. */\n private _mqttConnectionPromise?: Promise<mqtt.MqttClientConnection>;\n\n /** Stable per-process MQTT client id (prevents collisions between multiple processes). */\n private _mqttClientId?: string;\n\n /** Interrupt timestamps for storm / collision detection. */\n private _mqttInterrupts: number[] = [];\n\n /** The device IDs that are currently being updated in real-time, mapped to their respective timeouts. */\n private _realtimeDeviceIds: Map<string, NodeJS.Timeout> = new Map();\n\n /** The cached devices object, if any. */\n private _cachedDevices?: Devices;\n\n /**\n * Event emitter for client events.\n *\n * @see {@link MysaApiClientEventTypes} for the possible events and their payloads.\n */\n readonly emitter = new EventEmitter<MysaApiClientEventTypes>();\n\n /**\n * Gets the persistable session object.\n *\n * @returns The current persistable session object, if any.\n */\n get session(): MysaSession | undefined {\n if (!this._cognitoUserSession || !this._cognitoUser) {\n return undefined;\n }\n\n return {\n username: this._cognitoUser.getUsername(),\n idToken: this._cognitoUserSession.getIdToken().getJwtToken(),\n accessToken: this._cognitoUserSession.getAccessToken().getJwtToken(),\n refreshToken: this._cognitoUserSession.getRefreshToken().getToken()\n };\n }\n\n /**\n * Returns whether the client currently has an active session.\n *\n * @returns True if the client has an active session, false otherwise.\n */\n get isAuthenticated(): boolean {\n return !!this.session;\n }\n\n /**\n * Constructs a new instance of the MysaApiClient.\n *\n * @param session - The persistable session object, if any.\n * @param options - The options for the client.\n */\n constructor(session?: MysaSession, options?: MysaApiClientOptions) {\n this._logger = options?.logger || new VoidLogger();\n this._fetcher = options?.fetcher || fetch;\n\n if (session) {\n this._cognitoUser = new CognitoUser({\n Username: session.username,\n Pool: new CognitoUserPool({ UserPoolId: CognitoUserPoolId, ClientId: CognitoClientId })\n });\n this._cognitoUserSession = new CognitoUserSession({\n IdToken: new CognitoIdToken({ IdToken: session.idToken }),\n AccessToken: new CognitoAccessToken({ AccessToken: session.accessToken }),\n RefreshToken: new CognitoRefreshToken({ RefreshToken: session.refreshToken })\n });\n }\n }\n\n /**\n * Logs in the user with the given email address and password.\n *\n * This method authenticates the user with Mysa's Cognito user pool and establishes a session that can be used for\n * subsequent API calls. Upon successful login, a 'sessionChanged' event is emitted.\n *\n * @example\n *\n * ```typescript\n * try {\n * await client.login('user@example.com', 'password123');\n * console.log('Login successful!');\n * } catch (error) {\n * console.error('Login failed:', error.message);\n * }\n * ```\n *\n * @param emailAddress - The email address of the user.\n * @param password - The password of the user.\n * @throws {@link Error} When authentication fails due to invalid credentials or network issues.\n */\n async login(emailAddress: string, password: string): Promise<void> {\n this._cognitoUser = undefined;\n this._cognitoUserSession = undefined;\n this._mqttClientId = undefined;\n this._mqttInterrupts = [];\n\n this.emitter.emit('sessionChanged', this.session);\n\n return new Promise((resolve, reject) => {\n const user = new CognitoUser({\n Username: emailAddress,\n Pool: new CognitoUserPool({ UserPoolId: CognitoUserPoolId, ClientId: CognitoClientId })\n });\n\n user.authenticateUser(new AuthenticationDetails({ Username: emailAddress, Password: password }), {\n onSuccess: (session) => {\n this._cognitoUser = user;\n this._cognitoUserSession = session;\n this.emitter.emit('sessionChanged', this.session);\n\n resolve();\n },\n onFailure: (err) => {\n reject(err);\n }\n });\n });\n }\n\n /**\n * Retrieves the list of devices associated with the user.\n *\n * This method fetches all Mysa devices linked to the authenticated user's account, including device information such\n * as models, locations, and configuration details.\n *\n * @example\n *\n * ```typescript\n * const devices = await client.getDevices();\n * for (const [deviceId, device] of Object.entries(devices.DevicesObj)) {\n * console.log(`Device: ${device.DisplayName} (${device.Model})`);\n * }\n * ```\n *\n * @returns A promise that resolves to the list of devices.\n * @throws {@link MysaApiError} When the API request fails.\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n */\n async getDevices(): Promise<Devices> {\n this._logger.debug(`Fetching devices...`);\n\n const session = await this._getFreshSession();\n\n const response = await this._fetcher(`${MysaApiBaseUrl}/devices`, {\n headers: {\n Authorization: `${session.getIdToken().getJwtToken()}`\n }\n });\n\n if (!response.ok) {\n throw new MysaApiError(response);\n }\n\n return response.json();\n }\n\n /**\n * Retrieves the serial number for a specific device.\n *\n * This method uses AWS IoT's DescribeThing API to fetch the serial number attribute for the specified device. This\n * requires additional AWS IoT permissions and may not be available for all devices.\n *\n * @example\n *\n * ```typescript\n * const serialNumber = await client.getDeviceSerialNumber('device123');\n * if (serialNumber) {\n * console.log(`Device serial: ${serialNumber}`);\n * } else {\n * console.log('Serial number not available');\n * }\n * ```\n *\n * @param deviceId - The ID of the device to get the serial number for.\n * @returns A promise that resolves to the serial number, or undefined if not found.\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n */\n async getDeviceSerialNumber(deviceId: string): Promise<string | undefined> {\n this._logger.debug(`Fetching serial number for device ${deviceId}...`);\n\n const session = await this._getFreshSession();\n\n // Get AWS credentials for IoT client\n const credentialsProvider = fromCognitoIdentityPool({\n clientConfig: {\n region: AwsRegion\n },\n identityPoolId: CognitoIdentityPoolId,\n logins: {\n [CognitoLoginKey]: session.getIdToken().getJwtToken()\n }\n });\n\n const credentials = await credentialsProvider();\n const iotClient = new IoTClient({\n region: AwsRegion,\n credentials: {\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n sessionToken: credentials.sessionToken\n }\n });\n\n try {\n const command = new DescribeThingCommand({ thingName: deviceId });\n const response = await iotClient.send(command);\n return response.attributes?.['Serial'];\n } catch (error) {\n this._logger.warn(`Could not get serial number for device ${deviceId}:`, error);\n return undefined;\n }\n }\n\n /**\n * Retrieves firmware information for all devices.\n *\n * @returns A promise that resolves to the firmware information for all devices.\n * @throws {@link MysaApiError} When the API request fails.\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n */\n async getDeviceFirmwares(): Promise<Firmwares> {\n this._logger.debug(`Fetching device firmwares...`);\n\n const session = await this._getFreshSession();\n\n const response = await this._fetcher(`${MysaApiBaseUrl}/devices/firmware`, {\n headers: {\n Authorization: `${session.getIdToken().getJwtToken()}`\n }\n });\n\n if (!response.ok) {\n throw new MysaApiError(response);\n }\n\n return response.json();\n }\n\n /**\n * Retrieves the current state information for all devices.\n *\n * @returns A promise that resolves to the current state of all devices.\n * @throws {@link MysaApiError} When the API request fails.\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n */\n async getDeviceStates(): Promise<DeviceStates> {\n this._logger.debug(`Fetching device states...`);\n\n const session = await this._getFreshSession();\n\n const response = await this._fetcher(`${MysaApiBaseUrl}/devices/state`, {\n headers: {\n Authorization: `${session.getIdToken().getJwtToken()}`\n }\n });\n\n if (!response.ok) {\n throw new MysaApiError(response);\n }\n\n return response.json();\n }\n\n /**\n * Sets the state of a specific device by sending commands via MQTT.\n *\n * This method allows you to change the temperature set point and/or operating mode of a Mysa device. The command is\n * sent through the MQTT connection for real-time device control.\n *\n * @example\n *\n * ```typescript\n * // Set temperature to 22°C\n * await client.setDeviceState('device123', 22);\n *\n * // Turn device off\n * await client.setDeviceState('device123', undefined, 'off');\n *\n * // Set temperature and mode\n * await client.setDeviceState('device123', 20, 'heat');\n *\n * // Set fan speed\n * await client.setDeviceState('device123', undefined, undefined, 'auto');\n * ```\n *\n * @param deviceId - The ID of the device to control.\n * @param setPoint - The target temperature set point (optional).\n * @param mode - The operating mode to set (one of MysaDeviceMode values, or undefined to leave unchanged).\n * @param fanSpeed - The fan speed mode to set ('low', 'medium', 'high', 'max', 'auto', or undefined to leave\n * unchanged).\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n * @throws {@link Error} When MQTT connection or command sending fails.\n */\n async setDeviceState(deviceId: string, setPoint?: number, mode?: MysaDeviceMode, fanSpeed?: MysaFanSpeedMode) {\n this._logger.debug(`Setting device state for '${deviceId}'`);\n\n if (!this._cachedDevices) {\n this._cachedDevices = await this.getDevices();\n }\n\n const device = this._cachedDevices.DevicesObj[deviceId];\n\n this._logger.debug(`Initializing MQTT connection...`);\n const mqttConnection = await this._getMqttConnection();\n\n const now = dayjs();\n\n this._logger.debug(`Sending request to set device state for '${deviceId}'...`);\n const modeMap = { off: 1, auto: 2, heat: 3, cool: 4, fan_only: 5, dry: 6 };\n const fanSpeedMap = { auto: 1, low: 3, medium: 5, high: 7, max: 8 };\n\n const payload = serializeMqttPayload<ChangeDeviceState>({\n msg: InMessageType.CHANGE_DEVICE_STATE,\n id: now.valueOf(),\n time: now.unix(),\n ver: '1.0',\n src: {\n ref: this.session?.username ?? '',\n type: 100\n },\n dest: {\n ref: deviceId,\n type: 1\n },\n resp: 2,\n body: {\n ver: 1,\n type: device.Model.startsWith('BB-V1')\n ? 1\n : device.Model.startsWith('AC-V1')\n ? 2\n : device.Model.startsWith('BB-V2')\n ? device.Model.endsWith('-L')\n ? 5\n : 4\n : 0,\n cmd: [\n {\n tm: -1,\n sp: setPoint,\n md: mode ? modeMap[mode] : undefined,\n fn: fanSpeed ? fanSpeedMap[fanSpeed] : undefined\n }\n ]\n }\n });\n\n try {\n await this._publishWithRetry(mqttConnection, `/v1/dev/${deviceId}/in`, payload, mqtt.QoS.AtLeastOnce);\n this._logger.debug(`Device state publish succeeded for '${deviceId}'`);\n } catch (error) {\n this._logger.error(`Failed to set device state for '${deviceId}'`, error);\n throw error;\n }\n }\n\n /**\n * Starts receiving real-time updates for the specified device.\n *\n * This method establishes an MQTT subscription to receive live status updates from the device, including temperature,\n * humidity, set point changes, and other state information. The client will automatically send keep-alive messages to\n * maintain the connection.\n *\n * @example\n *\n * ```typescript\n * // Start receiving updates and listen for events\n * await client.startRealtimeUpdates('device123');\n *\n * client.emitter.on('statusChanged', (status) => {\n * console.log(`Temperature: ${status.temperature}°C`);\n * });\n * ```\n *\n * @param deviceId - The ID of the device to start receiving updates for.\n * @throws {@link Error} When MQTT connection or subscription fails.\n */\n async startRealtimeUpdates(deviceId: string) {\n this._logger.info(`Starting real-time updates for device '${deviceId}'`);\n\n if (this._realtimeDeviceIds.has(deviceId)) {\n this._logger.debug(`Real-time updates for device '${deviceId}' already started`);\n return;\n }\n\n this._logger.debug(`Initializing MQTT connection...`);\n const mqttConnection = await this._getMqttConnection();\n\n this._logger.debug(`Subscribing to MQTT topic '/v1/dev/${deviceId}/out'...`);\n await mqttConnection.subscribe(`/v1/dev/${deviceId}/out`, mqtt.QoS.AtLeastOnce, (_, payload) => {\n this._processMqttMessage(payload);\n });\n\n this._logger.debug(`Sending request to start publishing device status for '${deviceId}'...`);\n const payload = serializeMqttPayload<StartPublishingDeviceStatus>({\n Device: deviceId,\n MsgType: InMessageType.START_PUBLISHING_DEVICE_STATUS,\n Timestamp: dayjs().unix(),\n Timeout: RealtimeKeepAliveInterval.asSeconds()\n });\n await this._publishWithRetry(mqttConnection, `/v1/dev/${deviceId}/in`, payload, mqtt.QoS.AtLeastOnce);\n\n const timer = setInterval(async () => {\n this._logger.debug(`Sending request to keep-alive publishing device status for '${deviceId}'...`);\n const payload = serializeMqttPayload<StartPublishingDeviceStatus>({\n Device: deviceId,\n MsgType: InMessageType.START_PUBLISHING_DEVICE_STATUS,\n Timestamp: dayjs().unix(),\n Timeout: RealtimeKeepAliveInterval.asSeconds()\n });\n await this._publishWithRetry(mqttConnection, `/v1/dev/${deviceId}/in`, payload, mqtt.QoS.AtLeastOnce);\n }, RealtimeKeepAliveInterval.subtract(10, 'seconds').asMilliseconds());\n\n this._realtimeDeviceIds.set(deviceId, timer);\n }\n\n /**\n * Stops receiving real-time updates for the specified device.\n *\n * This method unsubscribes from the MQTT topic for the specified device and clears any associated timers to stop the\n * keep-alive messages.\n *\n * @param deviceId - The ID of the device to stop receiving real-time updates for.\n * @throws {@link Error} When MQTT unsubscription fails.\n */\n async stopRealtimeUpdates(deviceId: string) {\n this._logger.info(`Stopping real-time updates for device '${deviceId}'`);\n\n const timer = this._realtimeDeviceIds.get(deviceId);\n if (!timer) {\n this._logger.warn(`No real-time updates are running for device '${deviceId}'`);\n return;\n }\n\n this._logger.debug(`Initializing MQTT connection...`);\n const mqttConnection = await this._getMqttConnection();\n\n this._logger.debug(`Unsubscribing to MQTT topic '/v1/dev/${deviceId}/out'...`);\n await mqttConnection.unsubscribe(`/v1/dev/${deviceId}/out`);\n\n clearInterval(timer);\n this._realtimeDeviceIds.delete(deviceId);\n }\n\n /**\n * Ensures a valid, non-expired session is available.\n *\n * This method checks if the current session is valid and not expired. If the session is expired, it automatically\n * refreshes it using the refresh token.\n *\n * @returns A promise that resolves to a valid CognitoUserSession.\n * @throws {@link UnauthenticatedError} When no session exists or refresh fails.\n */\n private async _getFreshSession(): Promise<CognitoUserSession> {\n if (!this._cognitoUser || !this._cognitoUserSession) {\n throw new UnauthenticatedError('An attempt was made to access a resource without a valid session.');\n }\n\n if (\n this._cognitoUserSession.isValid() &&\n dayjs.unix(this._cognitoUserSession.getIdToken().getExpiration()).isAfter()\n ) {\n this._logger.debug('Session is valid, no need to refresh');\n return Promise.resolve(this._cognitoUserSession);\n }\n\n this._logger.debug('Session is not valid or expired, refreshing...');\n return new Promise<CognitoUserSession>((resolve, reject) => {\n this._cognitoUser!.refreshSession(this._cognitoUserSession!.getRefreshToken(), (error, session) => {\n if (error) {\n this._logger.error('Failed to refresh session:', error);\n reject(new UnauthenticatedError('Unable to refresh the authentication session.'));\n } else {\n this._logger.debug('Session refreshed successfully');\n this._cognitoUserSession = session;\n this.emitter.emit('sessionChanged', this.session);\n resolve(session);\n }\n });\n });\n }\n\n /**\n * Establishes and returns an MQTT connection for real-time communication.\n *\n * This method creates a new MQTT connection if one doesn't exist, using AWS IoT WebSocket connections with Cognito\n * credentials. The connection is cached and reused for subsequent calls.\n *\n * @returns A promise that resolves to an active MQTT connection.\n * @throws {@link Error} When connection establishment fails.\n */\n private _getMqttConnection(): Promise<mqtt.MqttClientConnection> {\n if (!this._mqttConnectionPromise) {\n this._mqttConnectionPromise = this._createMqttConnection().catch((err) => {\n this._mqttConnectionPromise = undefined;\n throw err;\n });\n }\n\n return this._mqttConnectionPromise;\n }\n\n /**\n * Determines whether an MQTT-related error is considered transient and worth retrying.\n *\n * Transient errors include timeouts, cancelled operations due to clean sessions, temporary connectivity loss, and\n * other recoverable network issues. Fatal errors (auth, permission, configuration) should not be retried at this\n * layer.\n *\n * @param err - The error object thrown by the underlying MQTT operation.\n * @returns True if the error appears transient and a retry should be attempted; false otherwise.\n */\n private _isTransientMqttError(err: unknown): boolean {\n if (!err || typeof err !== 'object') {\n return false;\n }\n\n const anyErr = err as { error_code?: unknown; error_name?: unknown; error?: unknown; message?: unknown };\n const code = anyErr.error_code || anyErr.error_name || anyErr.error;\n const msg = (anyErr.message || anyErr.error || '').toString();\n\n const transientMarkers = [\n 'AWS_ERROR_MQTT_TIMEOUT',\n 'AWS_ERROR_MQTT_NO_CONNECTION',\n 'AWS_ERROR_MQTT_UNEXPECTED_HANGUP',\n 'UNEXPECTED_HANGUP',\n 'Time limit between request and response',\n 'timeout'\n ];\n\n return transientMarkers.some((m) => (code && String(code).includes(m)) || msg.includes(m));\n }\n\n /**\n * Publishes an MQTT message with exponential backoff retries for transient failures.\n *\n * Retries occur for errors classified by `_isTransientMqttError`. Between attempts the delay grows exponentially with\n * jitter to avoid thundering herds after broker recovery. If the connection is not currently marked as connected, a\n * reconnect is attempted; if that fails, the connection is rebuilt (fresh credentials) before the next retry.\n *\n * On final failure (after maxAttempts) a {@link MqttPublishError} is thrown including the number of attempts and\n * original error for higher-level handling.\n *\n * @remarks\n * Retry options fields:\n *\n * - MaxAttempts: Maximum number of publish attempts before failing (default: 5).\n * - BaseDelayMs: Base delay in milliseconds used for exponential backoff calculation (default: 500).\n *\n * @param connection - The active MQTT client connection used to send the publish.\n * @param topic - The MQTT topic to publish to.\n * @param payload - The serialized payload (binary buffer or Uint8Array).\n * @param qos - The desired MQTT QoS level for the publish.\n * @param opts - Retry options (defaults: maxAttempts=5, baseDelayMs=500).\n * @returns A promise that resolves when the publish succeeds, or rejects with {@link MqttPublishError}.\n */\n private async _publishWithRetry(\n connection: mqtt.MqttClientConnection,\n topic: string,\n payload: ArrayBuffer | Uint8Array,\n qos: mqtt.QoS,\n opts: MqttPublishOptions = {}\n ): Promise<void> {\n const maxAttempts = opts.maxAttempts ?? 5;\n const baseDelayMs = opts.baseDelayMs ?? 500;\n\n let attempt = 0;\n\n while (true) {\n attempt++;\n try {\n await connection.publish(topic, payload, qos);\n return;\n } catch (err) {\n const isTransient = this._isTransientMqttError(err);\n\n if (!isTransient || attempt >= maxAttempts) {\n throw new MqttPublishError(`MQTT publish failed after ${attempt} attempts`, attempt, err);\n }\n\n // Apply jitter: delay is randomized between 75% and 125% of the base exponential backoff\n const JITTER_MIN_FACTOR = 0.75;\n const JITTER_RANGE = 0.5;\n const delay = baseDelayMs * Math.pow(2, attempt - 1) * (JITTER_MIN_FACTOR + Math.random() * JITTER_RANGE);\n\n this._logger.warn(\n `Transient MQTT publish error on '${topic}' (attempt ${attempt}/${maxAttempts}). Retrying in ${Math.round(\n delay\n )}ms`\n );\n\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n }\n\n /**\n * Creates a new MQTT connection using AWS IoT WebSocket connections with Cognito credentials.\n *\n * @returns A promise that resolves to an active MQTT connection.\n * @throws {@link Error} When connection establishment fails.\n */\n private async _createMqttConnection(): Promise<mqtt.MqttClientConnection> {\n const session = await this._getFreshSession();\n const credentialsProvider = fromCognitoIdentityPool({\n clientConfig: {\n region: AwsRegion\n },\n identityPoolId: CognitoIdentityPoolId,\n logins: {\n [CognitoLoginKey]: session.getIdToken().getJwtToken()\n },\n logger: this._logger\n });\n const credentials = await credentialsProvider();\n\n // Per-process stable client id. Random suffix avoids collisions with other running processes.\n if (!this._mqttClientId) {\n this._mqttClientId = `mysa-js-sdk-${this.session?.username ?? 'anon'}-${getRandomClientId()}`;\n }\n\n const builder = iot.AwsIotMqttConnectionConfigBuilder.new_with_websockets()\n .with_credentials(AwsRegion, credentials.accessKeyId, credentials.secretAccessKey, credentials.sessionToken)\n .with_endpoint(MqttEndpoint)\n .with_client_id(this._mqttClientId)\n .with_clean_session(false)\n .with_keep_alive_seconds(30)\n .with_ping_timeout_ms(3000)\n .with_protocol_operation_timeout_ms(60000)\n .with_reconnect_min_sec(1)\n .with_reconnect_max_sec(30);\n\n const config = builder.build();\n const client = new mqtt.MqttClient();\n const connection = client.new_connection(config);\n\n connection.on('connect', () => {\n this._logger.debug('MQTT connect');\n });\n\n connection.on('connection_success', () => {\n this._logger.debug('MQTT connection_success');\n });\n\n connection.on('connection_failure', (e) => {\n this._logger.error('MQTT connection_failure', e);\n });\n\n connection.on('interrupt', async (e) => {\n this._logger.warn('MQTT interrupt', e);\n\n // Track recent interrupts\n const now = Date.now();\n\n // Keep only last 60s\n this._mqttInterrupts = this._mqttInterrupts.filter((t) => now - t < 60000);\n this._mqttInterrupts.push(now);\n\n if (this._mqttInterrupts.length > 5) {\n this._logger.warn(\n `High interrupt rate (${this._mqttInterrupts.length}/60s). Possible clientId collision. Regenerating clientId and resetting connection...`\n );\n\n // Force new client id to escape collision; close current connection\n this._mqttClientId = undefined;\n\n // Clear interrupts\n this._mqttInterrupts = [];\n\n try {\n await connection.disconnect();\n\n if (this._mqttConnectionPromise) {\n this._logger.warn('MQTT connection promise still defined after disconnect; expected it to be cleared.');\n this._mqttConnectionPromise = undefined;\n }\n } catch (error) {\n this._logger.error('Failed to disconnect MQTT connection', error);\n }\n }\n });\n\n connection.on('resume', async (returnCode, sessionPresent) => {\n this._logger.info(`MQTT resume returnCode=${returnCode} sessionPresent=${sessionPresent}`);\n\n if (!sessionPresent) {\n this._logger.info('No session present, re-subscribing each device');\n try {\n for (const deviceId of Array.from(this._realtimeDeviceIds.keys())) {\n const topic = `/v1/dev/${deviceId}/out`;\n this._logger.debug(`Re-subscribing to ${topic}`);\n await connection.subscribe(topic, mqtt.QoS.AtLeastOnce, (_topic, payload) => {\n this._processMqttMessage(payload);\n });\n }\n } catch (err) {\n this._logger.error('Failed to re-subscribe after resume', err);\n }\n }\n });\n\n connection.on('error', (e) => {\n this._logger.error('MQTT error', e);\n });\n\n connection.on('closed', () => {\n this._logger.info('MQTT connection closed');\n this._mqttConnectionPromise = undefined;\n });\n\n await connection.connect();\n\n return connection;\n }\n\n /**\n * Processes incoming MQTT messages and emits appropriate events.\n *\n * This method parses MQTT payloads and converts them into typed events that can be listened to via the client's event\n * emitter. It handles both v1 and v2 device message formats and emits events like 'statusChanged', 'setPointChanged',\n * and 'stateChanged'.\n *\n * @param payload - The raw MQTT message payload to process.\n */\n private _processMqttMessage(payload: ArrayBuffer) {\n try {\n const parsedPayload = parseMqttPayload(payload);\n\n this.emitter.emit('rawRealtimeMessageReceived', parsedPayload);\n\n if (isMsgTypeOutPayload(parsedPayload)) {\n switch (parsedPayload.MsgType) {\n case OutMessageType.DEVICE_V1_STATUS:\n this.emitter.emit('statusChanged', {\n deviceId: parsedPayload.Device,\n temperature: parsedPayload.MainTemp,\n humidity: parsedPayload.Humidity,\n setPoint: parsedPayload.SetPoint,\n current: parsedPayload.Current\n });\n break;\n\n case OutMessageType.DEVICE_SETPOINT_CHANGE:\n this.emitter.emit('setPointChanged', {\n deviceId: parsedPayload.Device,\n newSetPoint: parsedPayload.Next,\n previousSetPoint: parsedPayload.Prev\n });\n break;\n }\n } else if (isMsgOutPayload(parsedPayload)) {\n switch (parsedPayload.msg) {\n case OutMessageType.DEVICE_V2_STATUS:\n this.emitter.emit('statusChanged', {\n deviceId: parsedPayload.src.ref,\n temperature: parsedPayload.body.ambTemp,\n humidity: parsedPayload.body.hum,\n setPoint: parsedPayload.body.stpt,\n dutyCycle: parsedPayload.body.dtyCycle\n });\n break;\n\n case OutMessageType.DEVICE_STATE_CHANGE: {\n const modeMap: Record<number, MysaDeviceMode> = {\n 1: 'off',\n 2: 'auto',\n 3: 'heat',\n 4: 'cool',\n 5: 'fan_only',\n 6: 'dry'\n };\n const fanSpeedMap: Record<number, MysaFanSpeedMode> = {\n 1: 'auto',\n 3: 'low',\n 5: 'medium',\n 7: 'high',\n 8: 'max'\n };\n\n this.emitter.emit('stateChanged', {\n deviceId: parsedPayload.src.ref,\n mode: parsedPayload.body.state.md ? modeMap[parsedPayload.body.state.md] : undefined,\n setPoint: parsedPayload.body.state.sp,\n fanSpeed: parsedPayload.body.state.fn !== undefined ? fanSpeedMap[parsedPayload.body.state.fn] : undefined\n });\n break;\n }\n }\n }\n } catch (error) {\n this._logger.error('Error handling MQTT message:', error);\n }\n }\n}\n"],"mappings":"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","names":["NodeEventEmitter","InMessageType","OutMessageType","dayjs","duration","payload"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/api/Errors.ts","../src/api/Logger.ts","../src/lib/EventEmitter.ts","../src/lib/PayloadParser.ts","../src/lib/PayloadTypeGuards.ts","../src/types/mqtt/in/InMessageType.ts","../src/types/mqtt/out/OutMessageType.ts","../src/api/MysaApiClient.ts"],"sourcesContent":["export * from './api';\nexport * from './types';\n","/** Error thrown when attempting to access the Mysa API without proper authentication. */\nexport class UnauthenticatedError extends Error {\n /**\n * Creates a new UnauthenticatedError instance.\n *\n * @param message - The error message\n */\n constructor(message: string) {\n super(message);\n this.name = 'UnauthenticatedError';\n }\n}\n\n/** Error thrown when a Mysa API request fails. */\nexport class MysaApiError extends Error {\n /** The HTTP status code returned by the API */\n readonly status: number;\n /** The HTTP status text returned by the API */\n readonly statusText: string;\n\n /**\n * Creates a new MysaApiError instance.\n *\n * @param apiResponse - The failed Response object from the API call\n */\n constructor(apiResponse: Response) {\n super(\n `Failed to call the '${apiResponse.url}' Mysa API endpoint. The server responded with a status of ${apiResponse.status} (${apiResponse.statusText}).`\n );\n this.name = 'MysaApiError';\n this.status = apiResponse.status;\n this.statusText = apiResponse.statusText;\n }\n}\n\n/** Error thrown when an MQTT publish ultimately fails after retry attempts. */\nexport class MqttPublishError extends Error {\n /**\n * Creates a new MqttPublishError instance.\n *\n * @param message - A human-readable description of the publish failure.\n * @param attempts - The number of attempts that were made before giving up.\n * @param original - The original error object thrown by the underlying MQTT library (optional).\n */\n constructor(\n message: string,\n public attempts: number,\n public original?: unknown\n ) {\n super(message);\n this.name = 'MqttPublishError';\n }\n}\n","/** Interface for logging operations at different severity levels */\nexport interface Logger {\n /** Logs a debug message with optional metadata */\n debug(message: string, ...meta: unknown[]): void;\n\n /** Logs an info message with optional metadata */\n info(message: string, ...meta: unknown[]): void;\n\n /** Logs a warning message with optional metadata */\n warn(message: string, ...meta: unknown[]): void;\n\n /** Logs an error message with optional metadata */\n error(message: string, ...meta: unknown[]): void;\n}\n\n/** Logger implementation that silently discards all log messages. */\n/* eslint-disable @typescript-eslint/no-unused-vars */\nexport class VoidLogger implements Logger {\n debug(message: string, ...meta: unknown[]): void {}\n info(message: string, ...meta: unknown[]): void {}\n warn(message: string, ...meta: unknown[]): void {}\n error(message: string, ...meta: unknown[]): void {}\n}\n/* eslint-enable @typescript-eslint/no-unused-vars */\n","import { EventEmitter as NodeEventEmitter } from 'node:events';\n\n/**\n * Typed wrapper around Node's `EventEmitter` class.\n *\n * @remarks\n * Source: {@link https://blog.makerx.com.au/a-type-safe-event-emitter-in-node-js}\n */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport class EventEmitter<TEvents extends Record<string, any>> implements NodeJS.EventEmitter {\n private _emitter = new NodeEventEmitter();\n\n emit<TEventName extends keyof TEvents & string>(eventName: TEventName, ...eventArg: TEvents[TEventName]) {\n return this._emitter.emit(eventName, ...(eventArg as []));\n }\n\n on<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n handler: (...eventArg: TEvents[TEventName]) => void\n ) {\n this._emitter.on(eventName, handler as any);\n return this;\n }\n\n once<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n handler: (...eventArg: TEvents[TEventName]) => void\n ) {\n this._emitter.once(eventName, handler as any);\n return this;\n }\n\n off<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n handler: (...eventArg: TEvents[TEventName]) => void\n ) {\n this._emitter.off(eventName, handler as any);\n return this;\n }\n\n addListener<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n listener: (...args: TEvents[TEventName]) => void\n ) {\n this._emitter.addListener(eventName, listener);\n return this;\n }\n\n removeListener<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n listener: (...args: TEvents[TEventName]) => void\n ) {\n this._emitter.removeListener(eventName, listener);\n return this;\n }\n\n removeAllListeners<TEventName extends keyof TEvents & string>(eventName?: TEventName | undefined) {\n this._emitter.removeAllListeners(eventName);\n return this;\n }\n\n setMaxListeners(n: number) {\n this._emitter.setMaxListeners(n);\n return this;\n }\n\n getMaxListeners(): number {\n return this._emitter.getMaxListeners();\n }\n\n listeners<TEventName extends keyof TEvents & string>(eventName: TEventName) {\n return this._emitter.listeners(eventName);\n }\n\n rawListeners<TEventName extends keyof TEvents & string>(eventName: TEventName) {\n return this._emitter.rawListeners(eventName);\n }\n\n listenerCount<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n listener?: (...args: TEvents[TEventName]) => void\n ) {\n return this._emitter.listenerCount(eventName, listener);\n }\n\n prependListener<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n listener: (...args: TEvents[TEventName]) => void\n ) {\n this._emitter.prependListener(eventName, listener);\n return this;\n }\n\n prependOnceListener<TEventName extends keyof TEvents & string>(\n eventName: TEventName,\n listener: (...args: TEvents[TEventName]) => void\n ) {\n this._emitter.prependOnceListener(eventName, listener);\n return this;\n }\n\n eventNames() {\n return this._emitter.eventNames();\n }\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n","import { InPayload } from '@/types/mqtt/InPayload';\nimport { OutPayload } from '@/types/mqtt/OutPayload';\n\n/**\n * Parses an MQTT payload from binary data into a typed OutPayload object.\n *\n * Converts the raw ArrayBuffer received from MQTT messages into a structured TypeScript object representing device\n * status, state changes, or other outgoing message types from Mysa devices.\n *\n * @param payload - The raw binary MQTT message payload as ArrayBuffer\n * @returns The parsed payload as a typed OutPayload object\n * @throws Error if the payload cannot be decoded or parsed as valid JSON\n */\nexport function parseMqttPayload(payload: ArrayBuffer): OutPayload {\n try {\n const decoder = new TextDecoder('utf-8');\n const jsonString = decoder.decode(payload);\n return JSON.parse(jsonString);\n } catch (error) {\n console.error('Error parsing MQTT payload:', error);\n throw new Error('Failed to parse MQTT payload');\n }\n}\n\n/**\n * Serializes an InPayload object into binary data for MQTT transmission.\n *\n * Converts a typed TypeScript payload object into the binary ArrayBuffer format required for sending commands and\n * requests to Mysa devices via MQTT.\n *\n * @typeParam T - The specific InPayload type being serialized\n * @param payload - The typed payload object to serialize\n * @returns The serialized payload as ArrayBuffer ready for MQTT transmission\n */\nexport function serializeMqttPayload<T extends InPayload>(payload: T): Uint8Array<ArrayBuffer> {\n const jsonString = JSON.stringify(payload);\n const encoder = new TextEncoder();\n return encoder.encode(jsonString);\n}\n","import { MsgOutPayload } from '@/types/mqtt/MsgOutPayload';\nimport { MsgTypeOutPayload } from '@/types/mqtt/MsgTypeOutPayload';\nimport { OutPayload } from '@/types/mqtt/OutPayload';\n\n/**\n * Type guard function to determine if an OutPayload is a MsgType-based payload.\n *\n * Checks whether the payload uses the legacy MsgType field format for message type identification. This is used to\n * differentiate between different payload structures and ensure proper type narrowing in TypeScript.\n *\n * @param payload - The OutPayload to check\n * @returns True if the payload is a MsgTypeOutPayload, false otherwise\n */\nexport function isMsgTypeOutPayload(payload: OutPayload): payload is MsgTypeOutPayload {\n return 'MsgType' in payload;\n}\n\n/**\n * Type guard function to determine if an OutPayload is a message-based payload.\n *\n * Checks whether the payload uses the newer msg field format for message type identification. This is used to\n * differentiate between different payload structures and ensure proper type narrowing in TypeScript.\n *\n * @param payload - The OutPayload to check\n * @returns True if the payload is a MsgOutPayload, false otherwise\n */\nexport function isMsgOutPayload(payload: OutPayload): payload is MsgOutPayload {\n return 'msg' in payload;\n}\n","/**\n * Enumeration of message types for incoming MQTT messages from clients to devices.\n *\n * These message types determine how commands and requests are interpreted by Mysa devices. The enum values correspond\n * to specific numeric identifiers used in the MQTT protocol.\n */\nexport enum InMessageType {\n //\n // When the message type is reported in the `MsgType` field of the payload.\n //\n\n /** Request to check and retrieve current device settings */\n CHECK_DEVICE_SETTINGS = 6,\n\n /** Command to start publishing periodic device status updates */\n START_PUBLISHING_DEVICE_STATUS = 11,\n\n //\n // When the message type is reported in the `msg` field of the payload.\n //\n\n /** Command to change the current state of a device (temperature, mode, etc.) */\n CHANGE_DEVICE_STATE = 44\n}\n","/**\n * Enumeration of message types for outgoing MQTT messages from devices to clients.\n *\n * These message types identify different kinds of status updates, notifications, and data reports that Mysa devices can\n * send via MQTT. The enum values correspond to specific numeric identifiers used in the MQTT protocol.\n */\nexport enum OutMessageType {\n //\n // When the message type is reported in the `MsgType` field of the payload.\n //\n\n /** Version 1 device status report with basic device information */\n DEVICE_V1_STATUS = 0,\n\n /** Notification that a device's temperature setpoint has been changed */\n DEVICE_SETPOINT_CHANGE = 1,\n\n /** Device log entry or diagnostic information */\n DEVICE_LOG = 4,\n\n /** Notification sent when a device completes its boot sequence */\n DEVICE_POST_BOOT = 10,\n\n //\n // When the message type is reported in the `msg` field of the payload.\n //\n\n /** Version 2 device status report with enhanced device information */\n DEVICE_V2_STATUS = 40,\n\n /** AC device status report with temperature, humidity, setpoint, and AC-specific fields */\n DEVICE_AC_STATUS = 30,\n\n /** Notification that a device's operational state has changed */\n DEVICE_STATE_CHANGE = 44\n}\n","import { MysaSession } from '@/api/MysaSession';\nimport { EventEmitter } from '@/lib/EventEmitter';\nimport { parseMqttPayload, serializeMqttPayload } from '@/lib/PayloadParser';\nimport { isMsgOutPayload, isMsgTypeOutPayload } from '@/lib/PayloadTypeGuards';\nimport { ChangeDeviceState } from '@/types/mqtt/in/ChangeDeviceState';\nimport { InMessageType } from '@/types/mqtt/in/InMessageType';\nimport { StartPublishingDeviceStatus } from '@/types/mqtt/in/StartPublishingDeviceStatus';\nimport { OutMessageType } from '@/types/mqtt/out/OutMessageType';\nimport { Devices, DeviceStates, Firmwares, Homes } from '@/types/rest';\nimport { DescribeThingCommand, IoTClient } from '@aws-sdk/client-iot';\nimport { fromCognitoIdentityPool } from '@aws-sdk/credential-providers';\nimport {\n AuthenticationDetails,\n CognitoAccessToken,\n CognitoIdToken,\n CognitoRefreshToken,\n CognitoUser,\n CognitoUserPool,\n CognitoUserSession\n} from 'amazon-cognito-identity-js';\nimport { auth, iot, mqtt } from 'aws-iot-device-sdk-v2';\nimport { hash } from 'crypto';\nimport dayjs, { Dayjs } from 'dayjs';\nimport duration from 'dayjs/plugin/duration.js';\nimport { customAlphabet } from 'nanoid';\nimport { MqttPublishError, MysaApiError, UnauthenticatedError } from './Errors';\nimport { Logger, VoidLogger } from './Logger';\nimport { MysaApiClientEventTypes } from './MysaApiClientEventTypes';\nimport { MysaApiClientOptions } from './MysaApiClientOptions';\nimport { MysaDeviceMode, MysaFanSpeedMode } from './MysaDeviceMode';\n\ndayjs.extend(duration);\n\nconst getRandomClientId = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 8);\n\n/** Options for MQTT publish operations. */\nexport interface MqttPublishOptions {\n /** Maximum number of publish attempts before failing (default: 5). */\n maxAttempts?: number;\n /** Base delay in milliseconds used for exponential backoff calculation (default: 500). */\n baseDelayMs?: number;\n}\n\nconst AwsRegion = 'us-east-1';\nconst CognitoUserPoolId = 'us-east-1_GUFWfhI7g';\nconst CognitoClientId = '19efs8tgqe942atbqmot5m36t3';\nconst CognitoIdentityPoolId = 'us-east-1:ebd95d52-9995-45da-b059-56b865a18379';\nconst CognitoLoginKey = `cognito-idp.${AwsRegion}.amazonaws.com/${CognitoUserPoolId}`;\nconst MqttEndpoint = 'a3q27gia9qg3zy-ats.iot.us-east-1.amazonaws.com';\nconst MysaApiBaseUrl = 'https://app-prod.mysa.cloud';\nconst RealtimeKeepAliveInterval = dayjs.duration(5, 'minutes');\n\n/**\n * Main client for interacting with the Mysa API and real-time device communication.\n *\n * The MysaApiClient provides a comprehensive interface for authenticating with Mysa services, managing device data, and\n * receiving real-time updates from Mysa thermostats and heating devices. It handles both REST API calls for device\n * management and MQTT connections for live status updates and control commands.\n *\n * @example\n *\n * ```typescript\n * const client = new MysaApiClient();\n *\n * await client.login('user@example.com', 'password');\n * const devices = await client.getDevices();\n *\n * client.emitter.on('statusChanged', (status) => {\n * console.log(`Device ${status.deviceId} temperature: ${status.temperature}°C`);\n * });\n *\n * for (const device of Object.entries(devices.DevicesObj)) {\n * await client.startRealtimeUpdates(device[0]);\n * }\n * ```\n */\nexport class MysaApiClient {\n /** The current session object, if any. */\n private _cognitoUserSession?: CognitoUserSession;\n\n /** The current user object, if any. */\n private _cognitoUser?: CognitoUser;\n\n /** The logger instance used by the client. */\n private _logger: Logger;\n\n /** The fetcher function used by the client. */\n private _fetcher: typeof fetch;\n\n /** A promise that resolves to the MQTT connection used for real-time updates. */\n private _mqttConnectionPromise?: Promise<mqtt.MqttClientConnection>;\n\n /** Stable per-process MQTT client id (prevents collisions between multiple processes). */\n private _mqttClientId?: string;\n\n /** Expiration time of the credentials currently in use by the MQTT client. */\n private _mqttCredentialsExpiration?: Dayjs;\n\n /** Interrupt timestamps for storm / collision detection. */\n private _mqttInterrupts: number[] = [];\n\n /** Whether a forced MQTT reset is currently in progress (guards against re-entrancy). */\n private _mqttResetInProgress = false;\n\n /** The device IDs that are currently being updated in real-time, mapped to their respective timeouts. */\n private _realtimeDeviceIds: Map<string, NodeJS.Timeout> = new Map();\n\n /** The cached devices object, if any. */\n private _cachedDevices?: Devices;\n\n /**\n * Event emitter for client events.\n *\n * @see {@link MysaApiClientEventTypes} for the possible events and their payloads.\n */\n readonly emitter = new EventEmitter<MysaApiClientEventTypes>();\n\n /**\n * Gets the persistable session object.\n *\n * @returns The current persistable session object, if any.\n */\n get session(): MysaSession | undefined {\n if (!this._cognitoUserSession || !this._cognitoUser) {\n return undefined;\n }\n\n return {\n username: this._cognitoUser.getUsername(),\n idToken: this._cognitoUserSession.getIdToken().getJwtToken(),\n accessToken: this._cognitoUserSession.getAccessToken().getJwtToken(),\n refreshToken: this._cognitoUserSession.getRefreshToken().getToken()\n };\n }\n\n /**\n * Returns whether the client currently has an active session.\n *\n * @returns True if the client has an active session, false otherwise.\n */\n get isAuthenticated(): boolean {\n return !!this.session;\n }\n\n /**\n * Constructs a new instance of the MysaApiClient.\n *\n * @param session - The persistable session object, if any.\n * @param options - The options for the client.\n */\n constructor(session?: MysaSession, options?: MysaApiClientOptions) {\n this._logger = options?.logger || new VoidLogger();\n this._fetcher = options?.fetcher || fetch;\n\n if (session) {\n this._cognitoUser = new CognitoUser({\n Username: session.username,\n Pool: new CognitoUserPool({ UserPoolId: CognitoUserPoolId, ClientId: CognitoClientId })\n });\n this._cognitoUserSession = new CognitoUserSession({\n IdToken: new CognitoIdToken({ IdToken: session.idToken }),\n AccessToken: new CognitoAccessToken({ AccessToken: session.accessToken }),\n RefreshToken: new CognitoRefreshToken({ RefreshToken: session.refreshToken })\n });\n }\n }\n\n /**\n * Logs in the user with the given email address and password.\n *\n * This method authenticates the user with Mysa's Cognito user pool and establishes a session that can be used for\n * subsequent API calls. Upon successful login, a 'sessionChanged' event is emitted.\n *\n * @example\n *\n * ```typescript\n * try {\n * await client.login('user@example.com', 'password123');\n * console.log('Login successful!');\n * } catch (error) {\n * console.error('Login failed:', error.message);\n * }\n * ```\n *\n * @param emailAddress - The email address of the user.\n * @param password - The password of the user.\n * @throws {@link Error} When authentication fails due to invalid credentials or network issues.\n */\n async login(emailAddress: string, password: string): Promise<void> {\n this._cognitoUser = undefined;\n this._cognitoUserSession = undefined;\n this._mqttClientId = undefined;\n this._mqttInterrupts = [];\n\n this.emitter.emit('sessionChanged', this.session);\n\n return new Promise((resolve, reject) => {\n const user = new CognitoUser({\n Username: emailAddress,\n Pool: new CognitoUserPool({ UserPoolId: CognitoUserPoolId, ClientId: CognitoClientId })\n });\n\n user.authenticateUser(new AuthenticationDetails({ Username: emailAddress, Password: password }), {\n onSuccess: (session) => {\n this._cognitoUser = user;\n this._cognitoUserSession = session;\n this.emitter.emit('sessionChanged', this.session);\n\n resolve();\n },\n onFailure: (err) => {\n reject(err);\n }\n });\n });\n }\n\n /**\n * Retrieves the list of devices associated with the user.\n *\n * This method fetches all Mysa devices linked to the authenticated user's account, including device information such\n * as models, locations, and configuration details.\n *\n * @example\n *\n * ```typescript\n * const devices = await client.getDevices();\n * for (const [deviceId, device] of Object.entries(devices.DevicesObj)) {\n * console.log(`Device: ${device.DisplayName} (${device.Model})`);\n * }\n * ```\n *\n * @returns A promise that resolves to the list of devices.\n * @throws {@link MysaApiError} When the API request fails.\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n */\n async getDevices(): Promise<Devices> {\n this._logger.debug(`Fetching devices...`);\n\n const session = await this._getFreshSession();\n\n const response = await this._fetcher(`${MysaApiBaseUrl}/devices`, {\n headers: {\n Authorization: `${session.getIdToken().getJwtToken()}`\n }\n });\n\n if (!response.ok) {\n throw new MysaApiError(response);\n }\n\n return response.json();\n }\n\n /**\n * Retrieves the serial number for a specific device.\n *\n * This method uses AWS IoT's DescribeThing API to fetch the serial number attribute for the specified device. This\n * requires additional AWS IoT permissions and may not be available for all devices.\n *\n * @example\n *\n * ```typescript\n * const serialNumber = await client.getDeviceSerialNumber('device123');\n * if (serialNumber) {\n * console.log(`Device serial: ${serialNumber}`);\n * } else {\n * console.log('Serial number not available');\n * }\n * ```\n *\n * @param deviceId - The ID of the device to get the serial number for.\n * @returns A promise that resolves to the serial number, or undefined if not found.\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n */\n async getDeviceSerialNumber(deviceId: string): Promise<string | undefined> {\n this._logger.debug(`Fetching serial number for device ${deviceId}...`);\n\n const session = await this._getFreshSession();\n\n // Get AWS credentials for IoT client\n const credentialsProvider = fromCognitoIdentityPool({\n clientConfig: {\n region: AwsRegion\n },\n identityPoolId: CognitoIdentityPoolId,\n logins: {\n [CognitoLoginKey]: session.getIdToken().getJwtToken()\n }\n });\n\n const credentials = await credentialsProvider();\n const iotClient = new IoTClient({\n region: AwsRegion,\n credentials: {\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n sessionToken: credentials.sessionToken\n }\n });\n\n try {\n const command = new DescribeThingCommand({ thingName: deviceId });\n const response = await iotClient.send(command);\n return response.attributes?.['Serial'];\n } catch (error) {\n this._logger.warn(`Could not get serial number for device ${deviceId}:`, error);\n return undefined;\n }\n }\n\n /**\n * Retrieves firmware information for all devices.\n *\n * @returns A promise that resolves to the firmware information for all devices.\n * @throws {@link MysaApiError} When the API request fails.\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n */\n async getDeviceFirmwares(): Promise<Firmwares> {\n this._logger.debug(`Fetching device firmwares...`);\n\n const session = await this._getFreshSession();\n\n const response = await this._fetcher(`${MysaApiBaseUrl}/devices/firmware`, {\n headers: {\n Authorization: `${session.getIdToken().getJwtToken()}`\n }\n });\n\n if (!response.ok) {\n throw new MysaApiError(response);\n }\n\n return response.json();\n }\n\n /**\n * Retrieves the current state information for all devices.\n *\n * @returns A promise that resolves to the current state of all devices.\n * @throws {@link MysaApiError} When the API request fails.\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n */\n async getDeviceStates(): Promise<DeviceStates> {\n this._logger.debug(`Fetching device states...`);\n\n const session = await this._getFreshSession();\n\n const response = await this._fetcher(`${MysaApiBaseUrl}/devices/state`, {\n headers: {\n Authorization: `${session.getIdToken().getJwtToken()}`\n }\n });\n\n if (!response.ok) {\n throw new MysaApiError(response);\n }\n\n return response.json();\n }\n\n /**\n * Retrieves information about all homes associated with the user.\n *\n * @returns A promise that resolves to the homes information.\n * @throws {@link MysaApiError} When the API request fails.\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n */\n async getHomes(): Promise<Homes> {\n this._logger.debug(`Fetching homes...`);\n\n const session = await this._getFreshSession();\n\n const response = await this._fetcher(`${MysaApiBaseUrl}/homes`, {\n headers: {\n Authorization: `${session.getIdToken().getJwtToken()}`\n }\n });\n\n if (!response.ok) {\n throw new MysaApiError(response);\n }\n\n return response.json();\n }\n\n /**\n * Sets the state of a specific device by sending commands via MQTT.\n *\n * This method allows you to change the temperature set point and/or operating mode of a Mysa device. The command is\n * sent through the MQTT connection for real-time device control.\n *\n * @example\n *\n * ```typescript\n * // Set temperature to 22°C\n * await client.setDeviceState('device123', 22);\n *\n * // Turn device off\n * await client.setDeviceState('device123', undefined, 'off');\n *\n * // Set temperature and mode\n * await client.setDeviceState('device123', 20, 'heat');\n *\n * // Set fan speed\n * await client.setDeviceState('device123', undefined, undefined, 'auto');\n * ```\n *\n * @param deviceId - The ID of the device to control.\n * @param setPoint - The target temperature set point (optional).\n * @param mode - The operating mode to set (one of MysaDeviceMode values, or undefined to leave unchanged).\n * @param fanSpeed - The fan speed mode to set ('low', 'medium', 'high', 'max', 'auto', or undefined to leave\n * unchanged).\n * @throws {@link UnauthenticatedError} When the user is not authenticated.\n * @throws {@link Error} When MQTT connection or command sending fails.\n */\n async setDeviceState(deviceId: string, setPoint?: number, mode?: MysaDeviceMode, fanSpeed?: MysaFanSpeedMode) {\n this._logger.debug(`Setting device state for '${deviceId}'`);\n\n if (!this._cachedDevices) {\n this._cachedDevices = await this.getDevices();\n }\n\n const device = this._cachedDevices.DevicesObj[deviceId];\n\n this._logger.debug(`Initializing MQTT connection...`);\n const mqttConnection = await this._getMqttConnection();\n\n const now = dayjs();\n\n this._logger.debug(`Sending request to set device state for '${deviceId}'...`);\n const modeMap = { off: 1, auto: 2, heat: 3, cool: 4, fan_only: 5, dry: 6 };\n const fanSpeedMap = { auto: 1, low: 3, medium: 5, high: 7, max: 8 };\n\n const payload = serializeMqttPayload<ChangeDeviceState>({\n msg: InMessageType.CHANGE_DEVICE_STATE,\n id: now.valueOf(),\n time: now.unix(),\n ver: '1.0',\n src: {\n ref: this.session?.username ?? '',\n type: 100\n },\n dest: {\n ref: deviceId,\n type: 1\n },\n resp: 2,\n body: {\n ver: 1,\n type:\n device.Model.startsWith('BB-V1') || device.Model.startsWith('v1')\n ? 1\n : device.Model.startsWith('AC-V1')\n ? 2\n : device.Model.startsWith('BB-V2')\n ? device.Model.endsWith('-L')\n ? 5\n : 4\n : 0,\n cmd: [\n {\n tm: -1,\n sp: setPoint,\n md: mode ? modeMap[mode] : undefined,\n fn: fanSpeed ? fanSpeedMap[fanSpeed] : undefined\n }\n ]\n }\n });\n\n try {\n await this._publishWithRetry(mqttConnection, `/v1/dev/${deviceId}/in`, payload, mqtt.QoS.AtLeastOnce);\n this._logger.debug(`Device state publish succeeded for '${deviceId}'`);\n } catch (error) {\n this._logger.error(`Failed to set device state for '${deviceId}'`, error);\n throw error;\n }\n }\n\n /**\n * Starts receiving real-time updates for the specified device.\n *\n * This method establishes an MQTT subscription to receive live status updates from the device, including temperature,\n * humidity, set point changes, and other state information. The client will automatically send keep-alive messages to\n * maintain the connection.\n *\n * @example\n *\n * ```typescript\n * // Start receiving updates and listen for events\n * await client.startRealtimeUpdates('device123');\n *\n * client.emitter.on('statusChanged', (status) => {\n * console.log(`Temperature: ${status.temperature}°C`);\n * });\n * ```\n *\n * @param deviceId - The ID of the device to start receiving updates for.\n * @throws {@link Error} When MQTT connection or subscription fails.\n */\n async startRealtimeUpdates(deviceId: string) {\n this._logger.info(`Starting real-time updates for device '${deviceId}'`);\n\n if (this._realtimeDeviceIds.has(deviceId)) {\n this._logger.debug(`Real-time updates for device '${deviceId}' already started`);\n return;\n }\n\n this._logger.debug(`Initializing MQTT connection...`);\n const mqttConnection = await this._getMqttConnection();\n\n this._logger.debug(`Subscribing to MQTT topic '/v1/dev/${deviceId}/out'...`);\n await mqttConnection.subscribe(`/v1/dev/${deviceId}/out`, mqtt.QoS.AtLeastOnce, (_, payload) => {\n this._processMqttMessage(payload);\n });\n\n this._logger.debug(`Sending request to start publishing device status for '${deviceId}'...`);\n const payload = serializeMqttPayload<StartPublishingDeviceStatus>({\n Device: deviceId,\n MsgType: InMessageType.START_PUBLISHING_DEVICE_STATUS,\n Timestamp: dayjs().unix(),\n Timeout: RealtimeKeepAliveInterval.asSeconds()\n });\n await this._publishWithRetry(mqttConnection, `/v1/dev/${deviceId}/in`, payload, mqtt.QoS.AtLeastOnce);\n\n const timer = setInterval(async () => {\n this._logger.debug(`Sending request to keep-alive publishing device status for '${deviceId}'...`);\n\n const connection = await this._getMqttConnection();\n const payload = serializeMqttPayload<StartPublishingDeviceStatus>({\n Device: deviceId,\n MsgType: InMessageType.START_PUBLISHING_DEVICE_STATUS,\n Timestamp: dayjs().unix(),\n Timeout: RealtimeKeepAliveInterval.asSeconds()\n });\n await this._publishWithRetry(connection, `/v1/dev/${deviceId}/in`, payload, mqtt.QoS.AtLeastOnce);\n }, RealtimeKeepAliveInterval.subtract(10, 'seconds').asMilliseconds());\n\n this._realtimeDeviceIds.set(deviceId, timer);\n }\n\n /**\n * Stops receiving real-time updates for the specified device.\n *\n * This method unsubscribes from the MQTT topic for the specified device and clears any associated timers to stop the\n * keep-alive messages.\n *\n * @param deviceId - The ID of the device to stop receiving real-time updates for.\n * @throws {@link Error} When MQTT unsubscription fails.\n */\n async stopRealtimeUpdates(deviceId: string) {\n this._logger.info(`Stopping real-time updates for device '${deviceId}'`);\n\n const timer = this._realtimeDeviceIds.get(deviceId);\n if (!timer) {\n this._logger.warn(`No real-time updates are running for device '${deviceId}'`);\n return;\n }\n\n this._logger.debug(`Initializing MQTT connection...`);\n const mqttConnection = await this._getMqttConnection();\n\n this._logger.debug(`Unsubscribing to MQTT topic '/v1/dev/${deviceId}/out'...`);\n await mqttConnection.unsubscribe(`/v1/dev/${deviceId}/out`);\n\n clearInterval(timer);\n this._realtimeDeviceIds.delete(deviceId);\n }\n\n /**\n * Ensures a valid, non-expired session is available.\n *\n * This method checks if the current session is valid and not expired. If the session is expired, it automatically\n * refreshes it using the refresh token.\n *\n * @returns A promise that resolves to a valid CognitoUserSession.\n * @throws {@link UnauthenticatedError} When no session exists or refresh fails.\n */\n private async _getFreshSession(): Promise<CognitoUserSession> {\n if (!this._cognitoUser || !this._cognitoUserSession) {\n throw new UnauthenticatedError('An attempt was made to access a resource without a valid session.');\n }\n\n if (\n this._cognitoUserSession.isValid() &&\n dayjs.unix(this._cognitoUserSession.getIdToken().getExpiration()).isAfter()\n ) {\n this._logger.debug('Session is valid, no need to refresh');\n return Promise.resolve(this._cognitoUserSession);\n }\n\n this._logger.debug('Session is not valid or expired, refreshing...');\n return new Promise<CognitoUserSession>((resolve, reject) => {\n this._cognitoUser!.refreshSession(this._cognitoUserSession!.getRefreshToken(), (error, session) => {\n if (error) {\n this._logger.error('Failed to refresh session:', error);\n reject(new UnauthenticatedError('Unable to refresh the authentication session.'));\n } else {\n this._logger.debug('Session refreshed successfully');\n this._cognitoUserSession = session;\n this.emitter.emit('sessionChanged', this.session);\n resolve(session);\n }\n });\n });\n }\n\n /**\n * Establishes and returns an MQTT connection for real-time communication.\n *\n * This method creates a new MQTT connection if one doesn't exist, using AWS IoT WebSocket connections with Cognito\n * credentials. The connection is cached and reused for subsequent calls.\n *\n * @returns A promise that resolves to an active MQTT connection.\n * @throws {@link Error} When connection establishment fails.\n */\n private _getMqttConnection(): Promise<mqtt.MqttClientConnection> {\n if (!this._mqttConnectionPromise) {\n this._mqttConnectionPromise = this._createMqttConnection().catch((err) => {\n this._mqttConnectionPromise = undefined;\n throw err;\n });\n }\n\n return this._mqttConnectionPromise;\n }\n\n /**\n * Determines whether an MQTT-related error is considered transient and worth retrying.\n *\n * Transient errors include timeouts, cancelled operations due to clean sessions, temporary connectivity loss, and\n * other recoverable network issues. Fatal errors (auth, permission, configuration) should not be retried at this\n * layer.\n *\n * @param err - The error object thrown by the underlying MQTT operation.\n * @returns True if the error appears transient and a retry should be attempted; false otherwise.\n */\n private _isTransientMqttError(err: unknown): boolean {\n if (!err || typeof err !== 'object') {\n return false;\n }\n\n const anyErr = err as { error_code?: unknown; error_name?: unknown; error?: unknown; message?: unknown };\n const code = anyErr.error_code || anyErr.error_name || anyErr.error;\n const msg = (anyErr.message || anyErr.error || '').toString();\n\n const transientMarkers = [\n 'AWS_ERROR_MQTT_TIMEOUT',\n 'AWS_ERROR_MQTT_NO_CONNECTION',\n 'AWS_ERROR_MQTT_UNEXPECTED_HANGUP',\n 'UNEXPECTED_HANGUP',\n 'AWS_ERROR_MQTT_CONNECTION_DESTROYED',\n 'Time limit between request and response',\n 'timeout'\n ];\n\n return transientMarkers.some((m) => (code && String(code).includes(m)) || msg.includes(m));\n }\n\n /**\n * Publishes an MQTT message with exponential backoff retries for transient failures.\n *\n * Retries occur for errors classified by `_isTransientMqttError`. Between attempts the delay grows exponentially with\n * jitter to avoid thundering herds after broker recovery. If the connection is not currently marked as connected, a\n * reconnect is attempted; if that fails, the connection is rebuilt (fresh credentials) before the next retry.\n *\n * On final failure (after maxAttempts) a {@link MqttPublishError} is thrown including the number of attempts and\n * original error for higher-level handling.\n *\n * @remarks\n * Retry options fields:\n *\n * - MaxAttempts: Maximum number of publish attempts before failing (default: 5).\n * - BaseDelayMs: Base delay in milliseconds used for exponential backoff calculation (default: 500).\n *\n * @param connection - The active MQTT client connection used to send the publish.\n * @param topic - The MQTT topic to publish to.\n * @param payload - The serialized payload (binary buffer or Uint8Array).\n * @param qos - The desired MQTT QoS level for the publish.\n * @param opts - Retry options (defaults: maxAttempts=5, baseDelayMs=500).\n * @returns A promise that resolves when the publish succeeds, or rejects with {@link MqttPublishError}.\n */\n private async _publishWithRetry(\n connection: mqtt.MqttClientConnection,\n topic: string,\n payload: ArrayBuffer | Uint8Array,\n qos: mqtt.QoS,\n opts: MqttPublishOptions = {}\n ): Promise<void> {\n const maxAttempts = opts.maxAttempts ?? 5;\n const baseDelayMs = opts.baseDelayMs ?? 500;\n\n let attempt = 0;\n\n while (true) {\n attempt++;\n try {\n await connection.publish(topic, payload, qos);\n return;\n } catch (err) {\n const isTransient = this._isTransientMqttError(err);\n\n if (!isTransient || attempt >= maxAttempts) {\n throw new MqttPublishError(`MQTT publish failed after ${attempt} attempts`, attempt, err);\n }\n\n // Apply jitter: delay is randomized between 75% and 125% of the base exponential backoff\n const JITTER_MIN_FACTOR = 0.75;\n const JITTER_RANGE = 0.5;\n const delay = baseDelayMs * Math.pow(2, attempt - 1) * (JITTER_MIN_FACTOR + Math.random() * JITTER_RANGE);\n\n this._logger.warn(\n `Transient MQTT publish error on '${topic}' (attempt ${attempt}/${maxAttempts}). Retrying in ${Math.round(\n delay\n )}ms`\n );\n\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n }\n\n /**\n * Creates a new MQTT connection using AWS IoT WebSocket connections with Cognito credentials.\n *\n * @returns A promise that resolves to an active MQTT connection.\n * @throws {@link Error} When connection establishment fails.\n */\n private async _createMqttConnection(): Promise<mqtt.MqttClientConnection> {\n const session = await this._getFreshSession();\n const credentialsProvider = fromCognitoIdentityPool({\n clientConfig: {\n region: AwsRegion\n },\n identityPoolId: CognitoIdentityPoolId,\n logins: {\n [CognitoLoginKey]: session.getIdToken().getJwtToken()\n },\n logger: this._logger\n });\n const credentials = await credentialsProvider();\n\n if (!credentials.expiration) {\n throw new Error('MQTT credentials do not have an expiration time.');\n }\n\n this._mqttCredentialsExpiration = dayjs(credentials.expiration);\n\n this._logger.debug(`MQTT credentials expiration: ${this._mqttCredentialsExpiration.format()}`);\n\n if (!this._mqttCredentialsExpiration.isAfter(dayjs())) {\n this._mqttCredentialsExpiration = undefined;\n throw new Error('MQTT credentials are already expired.');\n }\n\n // Per-process stable client id. Random suffix avoids collisions with other running processes.\n if (!this._mqttClientId) {\n const username = this.session?.username ?? 'anon';\n const usernameHash = hash('sha1', username);\n this._mqttClientId = `mysa-js-sdk-${usernameHash}-${process.pid}-${getRandomClientId()}`;\n }\n\n const builder = iot.AwsIotMqttConnectionConfigBuilder.new_with_websockets()\n .with_credentials(AwsRegion, credentials.accessKeyId, credentials.secretAccessKey, credentials.sessionToken)\n .with_endpoint(MqttEndpoint)\n .with_client_id(this._mqttClientId)\n .with_clean_session(false)\n .with_keep_alive_seconds(30)\n .with_ping_timeout_ms(3000)\n .with_protocol_operation_timeout_ms(60000)\n .with_reconnect_min_sec(1)\n .with_reconnect_max_sec(30);\n\n const config = builder.build();\n\n // `with_credentials` bakes the credentials fetched above into a static\n // signer. Cognito credentials expire after ~1 hour; if the connection\n // drops and the native reconnect loop is still retrying when they lapse,\n // every subsequent handshake fails with a stale signature and no JS event\n // is ever emitted (`interrupt` only fires when an *established* connection\n // drops) — the client wedges silently, forever. Override the handshake\n // transform so every (re)connect attempt signs with freshly refreshed\n // credentials instead.\n config.websocket_handshake_transform = async (request, done) => {\n try {\n const freshSession = await this._getFreshSession();\n const freshCredentialsProvider = fromCognitoIdentityPool({\n clientConfig: {\n region: AwsRegion\n },\n identityPoolId: CognitoIdentityPoolId,\n logins: {\n [CognitoLoginKey]: freshSession.getIdToken().getJwtToken()\n },\n logger: this._logger\n });\n const freshCredentials = await freshCredentialsProvider();\n if (freshCredentials.expiration) {\n this._mqttCredentialsExpiration = dayjs(freshCredentials.expiration);\n }\n\n await auth.aws_sign_request(request, {\n algorithm: auth.AwsSigningAlgorithm.SigV4,\n signature_type: auth.AwsSignatureType.HttpRequestViaQueryParams,\n provider: auth.AwsCredentialsProvider.newStatic(\n freshCredentials.accessKeyId,\n freshCredentials.secretAccessKey,\n freshCredentials.sessionToken\n ),\n region: AwsRegion,\n service: 'iotdevicegateway',\n signed_body_value: auth.AwsSignedBodyValue.EmptySha256,\n omit_session_token: true\n });\n done();\n } catch (error) {\n this._logger.error('Failed to sign MQTT websocket handshake with fresh credentials', error);\n done(3 /* AWS_ERROR_UNKNOWN: fail this attempt; the reconnect loop retries */);\n }\n };\n\n const client = new mqtt.MqttClient();\n const connection = client.new_connection(config);\n\n connection.on('connect', () => {\n this._logger.debug(`MQTT connect (clientId=${this._mqttClientId})`);\n });\n\n connection.on('connection_success', () => {\n this._logger.debug(`MQTT connection_success (clientId=${this._mqttClientId})`);\n });\n\n connection.on('connection_failure', (e) => {\n this._logger.error(`MQTT connection_failure (clientId=${this._mqttClientId})`, e);\n });\n\n connection.on('interrupt', async (e) => {\n this._logger.warn(`MQTT interrupt (clientId=${this._mqttClientId})`, e);\n\n // Track recent interrupts\n const now = Date.now();\n\n // Keep only last 60s\n this._mqttInterrupts = this._mqttInterrupts.filter((t) => now - t < 60000);\n this._mqttInterrupts.push(now);\n\n const areCredentialsExpired = !(this._mqttCredentialsExpiration?.isAfter(dayjs()) ?? false);\n\n if ((this._mqttInterrupts.length > 5 || areCredentialsExpired) && !this._mqttResetInProgress) {\n this._mqttResetInProgress = true;\n\n if (this._mqttInterrupts.length > 5) {\n this._logger.warn(\n `High interrupt rate (${this._mqttInterrupts.length}/60s). Possible clientId collision. Regenerating clientId and resetting connection...`\n );\n } else {\n this._logger.warn(`Credentials expired. Regenerating clientId and resetting connection...`);\n }\n\n // Force new client id to escape collision; close current connection\n this._mqttClientId = undefined;\n\n this._mqttCredentialsExpiration = undefined;\n\n // Clear interrupts\n this._mqttInterrupts = [];\n\n // Explicitly clear promise first to prevent reuse while disconnecting\n // (publishers calling _getMqttConnection() will create a new one)\n this._mqttConnectionPromise = undefined;\n\n try {\n // Strip listeners before disconnect. Without this, the 6 event-\n // handler closures registered above (`connect`,\n // `connection_success`, `connection_failure`, `interrupt`,\n // `resume`, `error`, `closed`) remain attached to the discarded\n // connection object. Each closure captures `this` and `_logger`,\n // and aws-crt's native binding retains the JS object via its\n // internal callback refs — the dead connection never gets GC'd.\n // After enough interrupt-storms the process heap grows until OOM.\n connection.removeAllListeners();\n await connection.disconnect();\n\n try {\n this._logger.debug('Old MQTT connection disconnected; establishing new connection...');\n const newConnection = await this._getMqttConnection();\n\n for (const deviceId of Array.from(this._realtimeDeviceIds.keys())) {\n const topic = `/v1/dev/${deviceId}/out`;\n this._logger.debug(`Re-subscribing to ${topic}`);\n await newConnection.subscribe(topic, mqtt.QoS.AtLeastOnce, (_topic, payload) => {\n this._processMqttMessage(payload);\n });\n }\n\n this._logger.info('MQTT connection rebuilt successfully after interrupt storm or credentials expiration');\n } catch (err) {\n this._logger.error('Failed to re-subscribe after interrupt storm or credentials expiration', err);\n }\n } catch (error) {\n this._logger.error('Error during MQTT reset', error);\n } finally {\n this._mqttResetInProgress = false;\n }\n }\n });\n\n connection.on('resume', async (returnCode, sessionPresent) => {\n this._logger.info(\n `MQTT resume returnCode=${returnCode} sessionPresent=${sessionPresent} clientId=${this._mqttClientId}`\n );\n\n if (!sessionPresent) {\n this._logger.info('No session present, re-subscribing each device');\n try {\n for (const deviceId of Array.from(this._realtimeDeviceIds.keys())) {\n const topic = `/v1/dev/${deviceId}/out`;\n this._logger.debug(`Re-subscribing to ${topic}`);\n await connection.subscribe(topic, mqtt.QoS.AtLeastOnce, (_topic, payload) => {\n this._processMqttMessage(payload);\n });\n }\n } catch (err) {\n this._logger.error('Failed to re-subscribe after resume', err);\n }\n }\n });\n\n connection.on('error', (e) => {\n this._logger.error(`MQTT error (clientId=${this._mqttClientId})`, e);\n });\n\n connection.on('closed', () => {\n this._logger.info('MQTT connection closed');\n this._mqttConnectionPromise = undefined;\n this._mqttCredentialsExpiration = undefined;\n });\n\n await connection.connect();\n\n return connection;\n }\n\n /**\n * Processes incoming MQTT messages and emits appropriate events.\n *\n * This method parses MQTT payloads and converts them into typed events that can be listened to via the client's event\n * emitter. It handles both v1 and v2 device message formats and emits events like 'statusChanged', 'setPointChanged',\n * and 'stateChanged'.\n *\n * @param payload - The raw MQTT message payload to process.\n */\n private _processMqttMessage(payload: ArrayBuffer) {\n try {\n const parsedPayload = parseMqttPayload(payload);\n\n this.emitter.emit('rawRealtimeMessageReceived', parsedPayload);\n\n if (isMsgTypeOutPayload(parsedPayload)) {\n switch (parsedPayload.MsgType) {\n case OutMessageType.DEVICE_V1_STATUS:\n this.emitter.emit('statusChanged', {\n deviceId: parsedPayload.Device,\n temperature: parsedPayload.MainTemp,\n humidity: parsedPayload.Humidity,\n setPoint: parsedPayload.SetPoint,\n current: parsedPayload.Current\n });\n break;\n\n case OutMessageType.DEVICE_SETPOINT_CHANGE:\n this.emitter.emit('setPointChanged', {\n deviceId: parsedPayload.Device,\n newSetPoint: parsedPayload.Next,\n previousSetPoint: parsedPayload.Prev\n });\n break;\n }\n } else if (isMsgOutPayload(parsedPayload)) {\n switch (parsedPayload.msg) {\n case OutMessageType.DEVICE_AC_STATUS:\n case OutMessageType.DEVICE_V2_STATUS:\n this.emitter.emit('statusChanged', {\n deviceId: parsedPayload.src.ref,\n temperature: parsedPayload.body.ambTemp,\n humidity: parsedPayload.body.hum,\n setPoint: parsedPayload.body.stpt,\n dutyCycle: parsedPayload.body.dtyCycle\n });\n break;\n\n case OutMessageType.DEVICE_STATE_CHANGE: {\n const modeMap: Record<number, MysaDeviceMode> = {\n 1: 'off',\n 2: 'auto',\n 3: 'heat',\n 4: 'cool',\n 5: 'fan_only',\n 6: 'dry'\n };\n const fanSpeedMap: Record<number, MysaFanSpeedMode> = {\n 1: 'auto',\n 3: 'low',\n 5: 'medium',\n 7: 'high',\n 8: 'max'\n };\n\n this.emitter.emit('stateChanged', {\n deviceId: parsedPayload.src.ref,\n mode: parsedPayload.body.state.md ? modeMap[parsedPayload.body.state.md] : undefined,\n setPoint: parsedPayload.body.state.sp,\n fanSpeed: parsedPayload.body.state.fn !== undefined ? fanSpeedMap[parsedPayload.body.state.fn] : undefined\n });\n break;\n }\n }\n }\n } catch (error) {\n this._logger.error('Error handling MQTT message:', error);\n }\n }\n}\n"],"mappings":"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","names":["NodeEventEmitter","InMessageType","OutMessageType","dayjs","duration","payload","_a"]}
|