mysa-js-sdk 2.1.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +18 -34
- package/dist/index.cjs +113 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -54
- package/dist/index.d.ts +71 -54
- package/dist/index.js +113 -90
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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 /** 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');\nconst PublishAckTimeout = dayjs.duration(30, 'seconds');\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\n // A failed publish request must not abort startup (or, in the keep-alive\n // below, crash the process via an unhandled rejection): the subscription\n // above still delivers the device's autonomous periodic status reports\n // even when the request-driven status stream is unavailable.\n try {\n await this._publishWithRetry(mqttConnection, `/v1/dev/${deviceId}/in`, payload, mqtt.QoS.AtLeastOnce);\n } catch (error) {\n this._logger.warn(`Failed to request status publishing for '${deviceId}'; relying on periodic reports`, error);\n }\n\n const timer = setInterval(async () => {\n this._logger.debug(`Sending request to keep-alive publishing device status for '${deviceId}'...`);\n\n try {\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 } catch (error) {\n this._logger.warn(`Failed to keep-alive status publishing for '${deviceId}'`, error);\n }\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 // Guard against publishes that never settle: a QoS1 publish the\n // broker silently ignores (e.g. an unauthorized topic) produces no\n // PUBACK and, in practice, no protocol-timeout rejection either —\n // the pending await would otherwise hang this call chain forever.\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(\n () =>\n reject(new Error(`MQTT publish timeout: no acknowledgement within ${PublishAckTimeout.asSeconds()}s`)),\n PublishAckTimeout.asMilliseconds()\n );\n connection.publish(topic, payload, qos).then(\n () => {\n clearTimeout(timer);\n resolve();\n },\n (err) => {\n clearTimeout(timer);\n reject(err);\n }\n );\n });\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"]}
|
|
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 * The underlying failure that prevented authentication.\n *\n * Callers need it to tell a credential rejection apart from a transport or service failure, since both surface as\n * this error. Declared explicitly rather than passed to `super` so the SDK keeps compiling against its current lib,\n * which predates the two-argument `Error` constructor.\n */\n readonly cause?: unknown;\n\n /**\n * Creates a new UnauthenticatedError instance.\n *\n * @param message - The error message\n * @param cause - The underlying failure that prevented authentication, if any.\n */\n constructor(message: string, cause?: unknown) {\n super(message);\n this.name = 'UnauthenticatedError';\n this.cause = cause;\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 a device id does not match any device on the account. */\nexport class UnknownDeviceError extends Error {\n /**\n * Creates a new UnknownDeviceError instance.\n *\n * @param deviceId - The device id that could not be resolved\n */\n constructor(public readonly deviceId: string) {\n super(`Unknown device id '${deviceId}': no such device on this account.`);\n this.name = 'UnknownDeviceError';\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 // No console output here: the SDK logger is the only sanctioned sink, and\n // the raw payload must not leak into output the consumer cannot route or\n // silence. The caller (_processMqttMessage) logs through the Logger.\n // The cause is attached via assignment because the project's TS lib\n // predates the ES2022 Error options constructor; Node supports it.\n const parseError = new Error('Failed to parse MQTT payload');\n (parseError as Error & { cause?: unknown }).cause = error;\n throw parseError;\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 { MysaCredentials } from '@/api/MysaCredentials';\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 { AuthenticationDetails, CognitoUser, CognitoUserPool, CognitoUserSession } 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, UnknownDeviceError } 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');\nconst PublishAckTimeout = dayjs.duration(30, 'seconds');\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({ username: 'user@example.com', password: 'password' });\n *\n * await client.login();\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 credentials of the Mysa account this client authenticates as. */\n private _credentials: MysaCredentials;\n\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 in-flight session acquisition, if any, so that concurrent callers share a single refresh or login. */\n private _freshSessionPromise?: Promise<CognitoUserSession>;\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 * Constructs a new instance of the MysaApiClient.\n *\n * @param credentials - The credentials of the Mysa account to authenticate as.\n * @param options - The options for the client.\n */\n constructor(credentials: MysaCredentials, options?: MysaApiClientOptions) {\n this._credentials = credentials;\n this._logger = options?.logger || new VoidLogger();\n this._fetcher = options?.fetcher || fetch;\n }\n\n /**\n * Ensures the client has a usable session, logging in with the credentials it was constructed with if needed.\n *\n * Calling this method is optional: the client authenticates on demand before its first API call, and re-authenticates\n * on its own whenever its session can no longer be refreshed. Call it explicitly at startup to fail fast on invalid\n * credentials instead of on the first API call. It is a no-op when the current session is still usable.\n *\n * @example\n *\n * ```typescript\n * try {\n * await client.login();\n * console.log('Login successful!');\n * } catch (error) {\n * console.error('Login failed:', error.message);\n * }\n * ```\n *\n * @throws {@link UnauthenticatedError} When authentication fails due to invalid credentials or network issues.\n */\n async login(): Promise<void> {\n // Goes through _getFreshSession so that an explicit login shares any acquisition already in flight instead of\n // racing a concurrent API call into a second Cognito login.\n await this._getFreshSession();\n }\n\n /**\n * Authenticates with Mysa's Cognito user pool and replaces the current session, if any.\n *\n * @returns A promise that resolves to the newly established session.\n * @throws {@link Error} When authentication fails due to invalid credentials or network issues.\n */\n private _login(): Promise<CognitoUserSession> {\n this._cognitoUser = undefined;\n this._cognitoUserSession = undefined;\n this._mqttClientId = undefined;\n this._mqttInterrupts = [];\n\n const { username, password } = this._credentials;\n\n return new Promise((resolve, reject) => {\n const user = new CognitoUser({\n Username: username,\n Pool: new CognitoUserPool({ UserPoolId: CognitoUserPoolId, ClientId: CognitoClientId })\n });\n\n user.authenticateUser(new AuthenticationDetails({ Username: username, Password: password }), {\n onSuccess: (session) => {\n this._cognitoUser = user;\n this._cognitoUserSession = session;\n\n resolve(session);\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 client cannot authenticate with its credentials.\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 client cannot authenticate with its credentials.\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 client cannot authenticate with its credentials.\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 client cannot authenticate with its credentials.\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 client cannot authenticate with its credentials.\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 client cannot authenticate with its credentials.\n * @throws {@link UnknownDeviceError} When the device id does not match any device on the account.\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 // Own-property check: an inherited key such as 'constructor' would otherwise pass the guard below and fail later\n // with a TypeError on device.Model instead of UnknownDeviceError.\n if (!Object.prototype.hasOwnProperty.call(this._cachedDevices.DevicesObj, deviceId)) {\n throw new UnknownDeviceError(deviceId);\n }\n\n const device = this._cachedDevices.DevicesObj[deviceId];\n\n // Validate the session before reaching for a possibly cached MQTT connection. _getMqttConnection reuses\n // _mqttConnectionPromise without checking auth, so without this a command could be published on a still-open\n // connection after the session died — with no source ref to attribute it to.\n await this._getFreshSession();\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._cognitoUser!.getUsername(),\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\n // A failed publish request must not abort startup (or, in the keep-alive\n // below, crash the process via an unhandled rejection): the subscription\n // above still delivers the device's autonomous periodic status reports\n // even when the request-driven status stream is unavailable.\n try {\n await this._publishWithRetry(mqttConnection, `/v1/dev/${deviceId}/in`, payload, mqtt.QoS.AtLeastOnce);\n } catch (error) {\n this._logger.warn(`Failed to request status publishing for '${deviceId}'; relying on periodic reports`, error);\n }\n\n const timer = setInterval(async () => {\n this._logger.debug(`Sending request to keep-alive publishing device status for '${deviceId}'...`);\n\n try {\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 } catch (error) {\n this._logger.warn(`Failed to keep-alive status publishing for '${deviceId}'`, error);\n }\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. If there is no session yet, or if the refresh token has itself expired or\n * been revoked, the client logs back in with its credentials.\n *\n * Concurrent callers share a single in-flight acquisition, so a burst of API calls never triggers more than one\n * refresh or login.\n *\n * @returns A promise that resolves to a valid CognitoUserSession.\n * @throws {@link UnauthenticatedError} When neither refreshing nor logging back in succeeds.\n */\n private async _getFreshSession(): Promise<CognitoUserSession> {\n if (\n this._cognitoUser &&\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 this._cognitoUserSession;\n }\n\n this._freshSessionPromise ??= this._acquireFreshSession().finally(() => {\n this._freshSessionPromise = undefined;\n });\n\n return this._freshSessionPromise;\n }\n\n /**\n * Refreshes the current session, falling back to a full login when it cannot be refreshed.\n *\n * @returns A promise that resolves to a valid CognitoUserSession.\n * @throws {@link UnauthenticatedError} When logging back in fails.\n */\n private async _acquireFreshSession(): Promise<CognitoUserSession> {\n if (this._cognitoUser && this._cognitoUserSession) {\n this._logger.debug('Session is not valid or expired, refreshing...');\n\n try {\n return await new Promise<CognitoUserSession>((resolve, reject) => {\n this._cognitoUser!.refreshSession(this._cognitoUserSession!.getRefreshToken(), (error, session) => {\n if (error) {\n reject(error);\n } else {\n this._logger.debug('Session refreshed successfully');\n this._cognitoUserSession = session;\n resolve(session);\n }\n });\n });\n } catch (error) {\n // The refresh token itself expired, was revoked, or was rotated away. Fall through to a full login.\n this._logger.warn('Failed to refresh session, logging back in:', error);\n }\n }\n\n try {\n this._logger.info('Logging in...');\n return await this._login();\n } catch (error) {\n this._logger.error('Failed to log in:', error);\n throw new UnauthenticatedError('Unable to establish an authentication session.', error);\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 // Guard against publishes that never settle: a QoS1 publish the\n // broker silently ignores (e.g. an unauthorized topic) produces no\n // PUBACK and, in practice, no protocol-timeout rejection either —\n // the pending await would otherwise hang this call chain forever.\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(\n () =>\n reject(new Error(`MQTT publish timeout: no acknowledgement within ${PublishAckTimeout.asSeconds()}s`)),\n PublishAckTimeout.asMilliseconds()\n );\n connection.publish(topic, payload, qos).then(\n () => {\n clearTimeout(timer);\n resolve();\n },\n (err) => {\n clearTimeout(timer);\n reject(err);\n }\n );\n });\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._cognitoUser?.getUsername() ?? '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"]}
|