diff --git a/dist/Alex2Node.js b/dist/Alex2Node.js index f32cff7..7e63d2e 100644 --- a/dist/Alex2Node.js +++ b/dist/Alex2Node.js @@ -5,11 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", { value: true }); const mqtt_1 = __importDefault(require("mqtt")); const Device_1 = __importDefault(require("./Device")); -const events_1 = require("events"); -const DisplayCategory_1 = require("./DisplayCategory"); -class Alex2MQTT extends events_1.EventEmitter { +class Alex2MQTT { constructor(username, password, rootTopic, debugLogging) { - super(); // Initialize EventEmitter this.username = username; this.password = password; this.rootTopic = rootTopic; @@ -33,8 +30,7 @@ class Alex2MQTT extends events_1.EventEmitter { console.log("[Alex2Node.ts] Subscribed to topics"); } if (err) { - console.error("[Alex2Node.ts] MQTT connection error:", err); - this.emit("error", err); + throw err; } }); }); @@ -52,12 +48,11 @@ class Alex2MQTT extends events_1.EventEmitter { if (deviceArray.length > 0) { this.client.publish(topic + "_r", JSON.stringify(deviceArray), (err) => { if (err) { - console.error("[Alex2Node.ts] MQTT connection error:", err); - this.emit("error", err); + throw err; } else if (this.debugLogging) { console.log(`[Alex2Node.ts] Discovery payloads published to ${topic + "_r"}`); - console.log(JSON.stringify(deviceArray, null, 2)); + console.log(deviceArray); } }); } @@ -99,23 +94,17 @@ class Alex2MQTT extends events_1.EventEmitter { if (this.debugLogging) { console.error("[Alex2Node.ts] Connection failed:", err); } - console.error("[Alex2Node.ts] MQTT connection error:", err); - this.emit("error", err); + throw err; }); } - registerDevice(name, endpointId, displayCategory) { + registerDevice(name, endpointId) { if (!this.client) { throw new Error("Must call connect before creating devices"); } if (this.debugLogging) { console.log(`[Alex2Node.ts] Creating new device with endpoint: ${endpointId}`); } - const normalizedCategory = displayCategory === null - ? [DisplayCategory_1.DisplayCategory.LIGHT] - : Array.isArray(displayCategory) - ? displayCategory - : [displayCategory || DisplayCategory_1.DisplayCategory.LIGHT]; - const device = new Device_1.default(this.client, this.rootTopic, name, endpointId, normalizedCategory); + const device = new Device_1.default(this.client, this.rootTopic, name, endpointId); this.devices.push(device); return device; } diff --git a/dist/AlexaErrorResponse.js b/dist/AlexaErrorResponse.js deleted file mode 100644 index 40577f5..0000000 --- a/dist/AlexaErrorResponse.js +++ /dev/null @@ -1,125 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AlexaErrorResponse = exports.AlexaErrorType = void 0; -const uuid_1 = require("uuid"); -var AlexaErrorType; -(function (AlexaErrorType) { - AlexaErrorType["ALREADY_IN_OPERATION"] = "ALREADY_IN_OPERATION"; - AlexaErrorType["AUTHORIZATION_REQUIRED"] = "AUTHORIZATION_REQUIRED"; - AlexaErrorType["BRIDGE_UNREACHABLE"] = "BRIDGE_UNREACHABLE"; - AlexaErrorType["BYPASS_NEEDED"] = "BYPASS_NEEDED"; - AlexaErrorType["CLOUD_CONTROL_DISABLED"] = "CLOUD_CONTROL_DISABLED"; - AlexaErrorType["CHILD_LOCK"] = "CHILD_LOCK"; - AlexaErrorType["CONFIGURATION_UPDATE_NOT_ALLOWED"] = "CONFIGURATION_UPDATE_NOT_ALLOWED"; - AlexaErrorType["COOK_DURATION_TOO_LONG"] = "COOK_DURATION_TOO_LONG"; - AlexaErrorType["COOLING_LOCKOUT_TEMPERATURE_VALUE_OUT_OF_RANGE"] = "COOLING_LOCKOUT_TEMPERATURE_VALUE_OUT_OF_RANGE"; - AlexaErrorType["COOLING_STAGES_EXCEEDS_LIMIT"] = "COOLING_STAGES_EXCEEDS_LIMIT"; - AlexaErrorType["DATA_DELETION_NOT_SUPPORTED"] = "DATA_DELETION_NOT_SUPPORTED"; - AlexaErrorType["DATA_RETRIEVAL_NOT_SUPPORTED"] = "DATA_RETRIEVAL_NOT_SUPPORTED"; - AlexaErrorType["DEVICE_STUCK"] = "DEVICE_STUCK"; - AlexaErrorType["DISABLED_BY_USER"] = "DISABLED_BY_USER"; - AlexaErrorType["DO_NOT_DISTURB_MODE"] = "DO_NOT_DISTURB_MODE"; - AlexaErrorType["DOOR_CLOSED_TOO_LONG"] = "DOOR_CLOSED_TOO_LONG"; - AlexaErrorType["DOOR_OPEN"] = "DOOR_OPEN"; - AlexaErrorType["DUAL_SETPOINTS_UNSUPPORTED"] = "DUAL_SETPOINTS_UNSUPPORTED"; - AlexaErrorType["ENDPOINT_BUSY"] = "ENDPOINT_BUSY"; - AlexaErrorType["ENDPOINT_CONTROL_UNAVAILABLE"] = "ENDPOINT_CONTROL_UNAVAILABLE"; - AlexaErrorType["ENDPOINT_LOW_POWER"] = "ENDPOINT_LOW_POWER"; - AlexaErrorType["ENDPOINT_UNREACHABLE"] = "ENDPOINT_UNREACHABLE"; - AlexaErrorType["EXCEEDED_PIN_ATTEMPTS"] = "EXCEEDED_PIN_ATTEMPTS"; - AlexaErrorType["EXPIRED_AUTHORIZATION_CREDENTIAL"] = "EXPIRED_AUTHORIZATION_CREDENTIAL"; - AlexaErrorType["FAILED_TO_BOOTSTRAP_COMMISSIONING_PROCESS"] = "FAILED_TO_BOOTSTRAP_COMMISSIONING_PROCESS"; - AlexaErrorType["FIRMWARE_OUT_OF_DATE"] = "FIRMWARE_OUT_OF_DATE"; - AlexaErrorType["HARDWARE_MALFUNCTION"] = "HARDWARE_MALFUNCTION"; - AlexaErrorType["HEATING_LOCKOUT_TEMPERATURE_VALUE_OUT_OF_RANGE"] = "HEATING_LOCKOUT_TEMPERATURE_VALUE_OUT_OF_RANGE"; - AlexaErrorType["HEATING_STAGES_EXCEEDS_LIMIT"] = "HEATING_STAGES_EXCEEDS_LIMIT"; - AlexaErrorType["INSUFFICIENT_PERMISSIONS"] = "INSUFFICIENT_PERMISSIONS"; - AlexaErrorType["INSUFFICIENT_RESOURCE"] = "INSUFFICIENT_RESOURCE"; - AlexaErrorType["INSUFFICIENT_SPACE"] = "INSUFFICIENT_SPACE"; - AlexaErrorType["INTERNAL_ERROR"] = "INTERNAL_ERROR"; - AlexaErrorType["INVALID_AUTHORIZATION_CREDENTIAL"] = "INVALID_AUTHORIZATION_CREDENTIAL"; - AlexaErrorType["INVALID_AUXILIARY_HEATING_SYSTEM_TYPE"] = "INVALID_AUXILIARY_HEATING_SYSTEM_TYPE"; - AlexaErrorType["INVALID_DIRECTIVE"] = "INVALID_DIRECTIVE"; - AlexaErrorType["INVALID_SYSTEM_TYPE"] = "INVALID_SYSTEM_TYPE"; - AlexaErrorType["INVALID_TARGET_STATE"] = "INVALID_TARGET_STATE"; - AlexaErrorType["INVALID_TEMPERATURE_SCALE"] = "INVALID_TEMPERATURE_SCALE"; - AlexaErrorType["INVALID_TERMINAL_CONNECTION"] = "INVALID_TERMINAL_CONNECTION"; - AlexaErrorType["INVALID_VALUE"] = "INVALID_VALUE"; - AlexaErrorType["MAINTENANCE_REQUIRED"] = "MAINTENANCE_REQUIRED"; - AlexaErrorType["MAX_COMMISSIONING_LIMIT_REACHED"] = "MAX_COMMISSIONING_LIMIT_REACHED"; - AlexaErrorType["MISSING_SETUP_INFORMATION"] = "MISSING_SETUP_INFORMATION"; - AlexaErrorType["NO_SUCH_ENDPOINT"] = "NO_SUCH_ENDPOINT"; - AlexaErrorType["NOT_CALIBRATED"] = "NOT_CALIBRATED"; - AlexaErrorType["NOT_IN_OPERATION"] = "NOT_IN_OPERATION"; - AlexaErrorType["NOT_READY"] = "NOT_READY"; - AlexaErrorType["NOT_SUPPORTED_IN_CURRENT_MODE"] = "NOT_SUPPORTED_IN_CURRENT_MODE"; - AlexaErrorType["NOT_SUPPORTED_WITH_CURRENT_BATTERY_CHARGE_STATE"] = "NOT_SUPPORTED_WITH_CURRENT_BATTERY_CHARGE_STATE"; - AlexaErrorType["OBSTACLE_DETECTED"] = "OBSTACLE_DETECTED"; - AlexaErrorType["PARTNER_APPLICATION_REDIRECTION"] = "PARTNER_APPLICATION_REDIRECTION"; - AlexaErrorType["PIN_SETUP_REQUIRED"] = "PIN_SETUP_REQUIRED"; - AlexaErrorType["POWER_LEVEL_NOT_SUPPORTED"] = "POWER_LEVEL_NOT_SUPPORTED"; - AlexaErrorType["PREHEAT_REQUIRED"] = "PREHEAT_REQUIRED"; - AlexaErrorType["PROBE_REQUIRED"] = "PROBE_REQUIRED"; - AlexaErrorType["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED"; - AlexaErrorType["REMOTE_START_NOT_SUPPORTED"] = "REMOTE_START_NOT_SUPPORTED"; - AlexaErrorType["REMOVE_PROBE"] = "REMOVE_PROBE"; - AlexaErrorType["REMOTE_START_DISABLED"] = "REMOTE_START_DISABLED"; - AlexaErrorType["REQUESTED_SETPOINTS_TOO_CLOSE"] = "REQUESTED_SETPOINTS_TOO_CLOSE"; - AlexaErrorType["SAFETY_BEAM_BREACHED"] = "SAFETY_BEAM_BREACHED"; - AlexaErrorType["SUBSCRIPTION_REQUIRED"] = "SUBSCRIPTION_REQUIRED"; - AlexaErrorType["TEMPERATURE_VALUE_OUT_OF_RANGE"] = "TEMPERATURE_VALUE_OUT_OF_RANGE"; - AlexaErrorType["THERMOSTAT_IS_OFF"] = "THERMOSTAT_IS_OFF"; - AlexaErrorType["TOO_MANY_FAILED_ATTEMPTS"] = "TOO_MANY_FAILED_ATTEMPTS"; - AlexaErrorType["TRIPLE_SETPOINTS_UNSUPPORTED"] = "TRIPLE_SETPOINTS_UNSUPPORTED"; - AlexaErrorType["UNABLE_TO_CHARGE"] = "UNABLE_TO_CHARGE"; - AlexaErrorType["UNAUTHORIZED"] = "UNAUTHORIZED"; - AlexaErrorType["UNCLEARED_ALARM"] = "UNCLEARED_ALARM"; - AlexaErrorType["UNSUPPORTED_THERMOSTAT_MODE"] = "UNSUPPORTED_THERMOSTAT_MODE"; - AlexaErrorType["UNCLEARED_TROUBLE"] = "UNCLEARED_TROUBLE"; - AlexaErrorType["UNWILLING_TO_SET_SCHEDULE"] = "UNWILLING_TO_SET_SCHEDULE"; - AlexaErrorType["UNWILLING_TO_SET_VALUE"] = "UNWILLING_TO_SET_VALUE"; - AlexaErrorType["VALUE_OUT_OF_RANGE"] = "VALUE_OUT_OF_RANGE"; -})(AlexaErrorType || (exports.AlexaErrorType = AlexaErrorType = {})); -class AlexaErrorResponse { - constructor(correlationToken, rootTopic, endpointId, mqttClient) { - this.rootTopic = rootTopic; - this.endpointId = endpointId; - this.mqttClient = mqttClient; - this.event = { - header: { - namespace: "Alexa", - name: "ErrorResponse", - messageId: this.generateMessageId(), - correlationToken, - payloadVersion: "3", - }, - endpoint: { - endpointId, - }, - payload: {}, - }; - } - generateMessageId() { - return (0, uuid_1.v4)(); - } - setErrorMessage(type, message, otherParams = {}) { - this.event.payload = Object.assign({ type, - message }, otherParams); - } - send(sendAsync = false) { - const payload = { - event: this.event, - }; - const topic = `${this.rootTopic}/${this.endpointId}/${sendAsync ? "deferredResponse" : "alexaResponce"}`; //Yes this should be response but it is incorrect in both Alex2MQTT and Alex2ESP so for consistency is is wrong here too - const payloadStr = JSON.stringify(payload); - this.mqttClient.publish(topic, payloadStr, (err) => { - if (err) { - console.error("[AlexaErrorResponse] Failed to publish status message:", err); - } - else { - console.log(`[AlexaErrorResponse] Sent message to ${topic}`); - } - }); - } -} -exports.AlexaErrorResponse = AlexaErrorResponse; diff --git a/dist/AlexaInterface.js b/dist/AlexaInterface.js index 0febeab..452ed28 100644 --- a/dist/AlexaInterface.js +++ b/dist/AlexaInterface.js @@ -82,7 +82,6 @@ class AlexaInterface { this.instance = instance; this.friendlyNames = []; this.actionMappings = []; - this.supportedModes = []; } addActionMapping(mapping) { this.actionMappings.push(mapping); @@ -90,9 +89,6 @@ class AlexaInterface { addFriendlyName(name, locale) { this.friendlyNames.push({ text: name, locale }); } - addSupportedModes(modes) { - this.supportedModes = modes; - } setInstance(name) { this.instance = name; } @@ -168,12 +164,11 @@ class AlexaInterface { case AlexaInterfaceType.SCENE_CONTROLLER: case AlexaInterfaceType.SEEK_CONTROLLER: case AlexaInterfaceType.TEMPERATURE_SENSOR: + case AlexaInterfaceType.THERMOSTAT_CONTROLLER: case AlexaInterfaceType.TOGGLE_CONTROLLER: return "3"; case AlexaInterfaceType.LAUNCHER: return "1.1"; - case AlexaInterfaceType.THERMOSTAT_CONTROLLER: - return "3.2"; case AlexaInterfaceType.ENDPOINT_HEALTH: return "3.3"; default: @@ -200,17 +195,8 @@ class AlexaInterface { return ["connectivity"]; case AlexaInterfaceType.TOGGLE_CONTROLLER: return ["toggleState"]; - case AlexaInterfaceType.MODE_CONTROLLER: - return ["mode"]; case AlexaInterfaceType.TEMPERATURE_SENSOR: return ["temperature"]; - case AlexaInterfaceType.THERMOSTAT_CONTROLLER: - return [ - "lowerSetpoint", - "upperSetpoint", - "thermostatMode", - "adaptiveRecoveryStatus", - ]; case AlexaInterfaceType.APPLICATION_STATE_REPORTER: case AlexaInterfaceType.AUDIO_PLAY_QUEUE: case AlexaInterfaceType.AUTHORIZATION_CONTROLLER: @@ -234,6 +220,7 @@ class AlexaInterface { case AlexaInterfaceType.LOCK_CONTROLLER: case AlexaInterfaceType.MEDIA_PLAYBACK: case AlexaInterfaceType.MEDIA_SEARCH: + case AlexaInterfaceType.MODE_CONTROLLER: case AlexaInterfaceType.MOTION_SENSOR: case AlexaInterfaceType.PERCENTAGE_CONTROLLER: case AlexaInterfaceType.PLAYBACK_CONTROLLER: @@ -251,6 +238,7 @@ class AlexaInterface { case AlexaInterfaceType.SMART_VISION_SNAPSHOT_PROVIDER: case AlexaInterfaceType.SPEAKER: case AlexaInterfaceType.STEP_SPEAKER: + case AlexaInterfaceType.THERMOSTAT_CONTROLLER: case AlexaInterfaceType.THERMOSTAT_CONTROLLER_CONFIGURATION: case AlexaInterfaceType.THERMOSTAT_CONTROLLER_HVAC_COMPONENTS: case AlexaInterfaceType.THERMOSTAT_CONTROLLER_SCHEDULE: @@ -273,21 +261,9 @@ class AlexaInterface { properties: { retrievable: this.retrievable, proactivelyReported: this.proactivelyReported, - supported: this.getProps().map(name => ({ name })), + supported: [{ name: this.getProps()[0] }], }, }; - if (this.type == AlexaInterfaceType.THERMOSTAT_CONTROLLER) { - doc.configuration = { - "supportedModes": ["HEAT", "COOL", "AUTO", "OFF"], - "supportsScheduling": false - }; - } - else if (this.supportedModes.length > 0) { - doc["configuration"] = { - ordered: true, - supportedModes: this.supportedModes, - }; - } if (this.instance) { doc.instance = this.instance; } @@ -302,7 +278,6 @@ class AlexaInterface { }; friendlyNamesArray.push(friendlyNameObj); }); - capabilityResources.friendlyNames = friendlyNamesArray; doc["capabilityResources"] = capabilityResources; } if (this.actionMappings.length > 0) { diff --git a/dist/AlexaStatusMessage.js b/dist/AlexaStatusMessage.js index 64dbeba..6bfd1fb 100644 --- a/dist/AlexaStatusMessage.js +++ b/dist/AlexaStatusMessage.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.AlexaStatusMessage = exports.TemperatureSensorScale = exports.PowerController = exports.ThermostatMode = exports.EndpointHealth = void 0; +exports.AlexaStatusMessage = exports.TemperatureSensorScale = exports.PowerController = exports.EndpointHealth = void 0; const uuid_1 = require("uuid"); const AlexaInterface_1 = require("./AlexaInterface"); var EndpointHealth; @@ -8,15 +8,6 @@ var EndpointHealth; EndpointHealth["OK"] = "OK"; EndpointHealth["UNREACHABLE"] = "UNREACHABLE"; })(EndpointHealth || (exports.EndpointHealth = EndpointHealth = {})); -var ThermostatMode; -(function (ThermostatMode) { - ThermostatMode["OFF"] = "OFF"; - ThermostatMode["HEAT"] = "HEAT"; - ThermostatMode["COOL"] = "COOL"; - ThermostatMode["AUTO"] = "AUTO"; - ThermostatMode["ECO"] = "ECO"; - ThermostatMode["CUSTOM"] = "CUSTOM"; -})(ThermostatMode || (exports.ThermostatMode = ThermostatMode = {})); var PowerController; (function (PowerController) { PowerController["ON"] = "ON"; @@ -28,20 +19,15 @@ var TemperatureSensorScale; TemperatureSensorScale["FAHRENHEIT"] = "FAHRENHEIT"; })(TemperatureSensorScale || (exports.TemperatureSensorScale = TemperatureSensorScale = {})); class AlexaStatusMessage { - constructor(correlationToken, rootTopic, endpointId, mqttClient, isResponse = false, isDeferred = false) { + constructor(correlationToken, rootTopic, endpointId, mqttClient, isResponse = false) { this.context = { properties: [] }; this.rootTopic = rootTopic; this.endpointId = endpointId; this.mqttClient = mqttClient; - this.isDeferred = isDeferred; this.event = { header: { namespace: "Alexa", - name: isDeferred - ? "DeferredResponse" - : isResponse - ? "Response" - : "StateReport", + name: isResponse ? "Response" : "StateReport", payloadVersion: "3", messageId: this.generateMessageId(), correlationToken, @@ -71,32 +57,6 @@ class AlexaStatusMessage { this.context.properties.push(prop); return this; } - addModeControllerProp(instance, value, uncertaintyInMs = 0) { - return this.addProperty(AlexaInterface_1.AlexaInterfaceType.MODE_CONTROLLER, "mode", value, uncertaintyInMs, instance); - } - addThermostatModeProp(mode, uncertaintyInMs = 0) { - return this.addProperty(AlexaInterface_1.AlexaInterfaceType.THERMOSTAT_CONTROLLER, "thermostatMode", mode, uncertaintyInMs); - } - addEstimatedDeferralTime(seconds) { - if (this.isDeferred) { - this.event.payload = { - estimatedDeferralInSeconds: seconds, - }; - } - else { - console.warn("[AlexaStatusMessage.ts] Attempted to add estimated deferral time, but message is not marked as DeferredResponse."); - } - return this; - } - addThermostatControllerProp(name, scale, value, uncertaintyInMs = 0) { - const tempValue = { - value: scale === TemperatureSensorScale.FAHRENHEIT - ? (value - 32) * (5 / 9) - : value, - scale: "CELSIUS", - }; - return this.addProperty(AlexaInterface_1.AlexaInterfaceType.THERMOSTAT_CONTROLLER, name, tempValue, uncertaintyInMs); - } addHealthProp(health, uncertaintyInMs = 0) { return this.addProperty(AlexaInterface_1.AlexaInterfaceType.ENDPOINT_HEALTH, "connectivity", { value: health }, uncertaintyInMs); } @@ -125,19 +85,19 @@ class AlexaStatusMessage { this.context.properties.push(prop); return this; } - send(sendAsync) { + send() { const payload = { - context: this.isDeferred ? null : this.context, + context: this.context, event: this.event, }; - const topic = `${this.rootTopic}/${this.endpointId}/${sendAsync ? "deferredResponse" : "alexaResponce"}`; //Yes this should be response but it is incorrect in both Alex2MQTT and Alex2ESP so for consistency is is wrong here too + const topic = `${this.rootTopic}/${this.endpointId}/alexaResponce`; //Yes this should be response but it is incorrect in both Alex2MQTT and Alex2ESP so for consistency is is wrong here too const payloadStr = JSON.stringify(payload); this.mqttClient.publish(topic, payloadStr, (err) => { if (err) { console.error("[AlexaStatusMessage] Failed to publish status message:", err); } else { - console.log(`[AlexaStatusMessage] Sent message to ${topic}`); + // console.log(`[AlexaStatusMessage] Sent message to ${topic}`); } }); } diff --git a/dist/Device.js b/dist/Device.js index e2abf6b..7decf86 100644 --- a/dist/Device.js +++ b/dist/Device.js @@ -4,9 +4,8 @@ const AlexaInterface_1 = require("./AlexaInterface"); const DisplayCategory_1 = require("./DisplayCategory"); const events_1 = require("events"); const AlexaStatusMessage_1 = require("./AlexaStatusMessage"); -const AlexaErrorResponse_1 = require("./AlexaErrorResponse"); class Device extends events_1.EventEmitter { - constructor(mqttClient, rootTopic, name, endpointId, displayCategory, description = "Alexa to Node.js bridge", manufacturerName = "Alex2Node", manufacturer = "Alex2Node", model = "Alex2Node_v1.0.0") { + constructor(mqttClient, rootTopic, name, endpointId, displayCategory = DisplayCategory_1.DisplayCategory.LIGHT, description = "Alexa to Node.js bridge", manufacturerName = "Alex2Node", manufacturer = "Alex2Node", model = "Alex2Node_v1.0.0") { super(); this.mqttClient = mqttClient; this.rootTopic = rootTopic; @@ -30,10 +29,10 @@ class Device extends events_1.EventEmitter { return this.endpointId; } setDisplayCategory(category) { - this.displayCategory = Array.isArray(category) ? category : [category]; + this.displayCategory = category; } getDisplayCategory() { - return this.displayCategory || [DisplayCategory_1.DisplayCategory.LIGHT]; + return this.displayCategory; } setDescription(description) { this.description = description; @@ -41,11 +40,8 @@ class Device extends events_1.EventEmitter { getDescription() { return this.description; } - getErrorMessage(correlationToken) { - return new AlexaErrorResponse_1.AlexaErrorResponse(correlationToken, this.rootTopic, this.endpointId, this.mqttClient); - } - getStatusMessage(correlationToken, isResponse = false, isDeferred = false) { - return new AlexaStatusMessage_1.AlexaStatusMessage(correlationToken, this.rootTopic, this.endpointId, this.mqttClient, isResponse, isDeferred); + getStatusMessage(correlationToken, isResponse = false) { + return new AlexaStatusMessage_1.AlexaStatusMessage(correlationToken, this.rootTopic, this.endpointId, this.mqttClient, isResponse); } setManufacturerName(name) { this.manufacturerName = name; @@ -69,12 +65,10 @@ class Device extends events_1.EventEmitter { return this.softwareVersion; } addCapability(type) { - // const existing = this.capabilities.find( - // (iface) => iface.getType() === type - // ); - // if (existing) { - // return existing; - // } + const existing = this.capabilities.find((iface) => iface.getType() === type); + if (existing) { + return existing; + } const newCapability = new AlexaInterface_1.AlexaInterface(type); this.capabilities.push(newCapability); // console.log( @@ -88,7 +82,7 @@ class Device extends events_1.EventEmitter { friendlyName: this.name, description: this.description, manufacturerName: this.manufacturerName, - displayCategories: this.getDisplayCategory(), + displayCategories: [this.displayCategory], additionalAttributes: { manufacturer: this.manufacturer, model: this.model, diff --git a/dist/index.js b/dist/index.js index 12e62bf..77b248c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.AlexaErrorResponse = exports.AlexaErrorType = exports.ThermostatMode = exports.TemperatureSensorScale = exports.EndpointHealth = exports.PowerController = exports.DisplayCategory = exports.AlexaActions = exports.ActionMapping = exports.AlexaInterfaceType = exports.Alex2MQTT = void 0; +exports.PowerController = exports.AlexaActions = exports.ActionMapping = exports.AlexaInterfaceType = exports.Alex2MQTT = void 0; var Alex2Node_1 = require("./Alex2Node"); Object.defineProperty(exports, "Alex2MQTT", { enumerable: true, get: function () { return __importDefault(Alex2Node_1).default; } }); var AlexaInterface_1 = require("./AlexaInterface"); @@ -11,13 +11,5 @@ Object.defineProperty(exports, "AlexaInterfaceType", { enumerable: true, get: fu var ActionMapping_1 = require("./ActionMapping"); Object.defineProperty(exports, "ActionMapping", { enumerable: true, get: function () { return ActionMapping_1.ActionMapping; } }); Object.defineProperty(exports, "AlexaActions", { enumerable: true, get: function () { return ActionMapping_1.AlexaActions; } }); -var DisplayCategory_1 = require("./DisplayCategory"); -Object.defineProperty(exports, "DisplayCategory", { enumerable: true, get: function () { return DisplayCategory_1.DisplayCategory; } }); var AlexaStatusMessage_1 = require("./AlexaStatusMessage"); Object.defineProperty(exports, "PowerController", { enumerable: true, get: function () { return AlexaStatusMessage_1.PowerController; } }); -Object.defineProperty(exports, "EndpointHealth", { enumerable: true, get: function () { return AlexaStatusMessage_1.EndpointHealth; } }); -Object.defineProperty(exports, "TemperatureSensorScale", { enumerable: true, get: function () { return AlexaStatusMessage_1.TemperatureSensorScale; } }); -Object.defineProperty(exports, "ThermostatMode", { enumerable: true, get: function () { return AlexaStatusMessage_1.ThermostatMode; } }); -var AlexaErrorResponse_1 = require("./AlexaErrorResponse"); -Object.defineProperty(exports, "AlexaErrorType", { enumerable: true, get: function () { return AlexaErrorResponse_1.AlexaErrorType; } }); -Object.defineProperty(exports, "AlexaErrorResponse", { enumerable: true, get: function () { return AlexaErrorResponse_1.AlexaErrorResponse; } }); diff --git a/examples/ExampleModeController.js b/examples/ExampleModeController.js deleted file mode 100644 index 0313d5e..0000000 --- a/examples/ExampleModeController.js +++ /dev/null @@ -1,118 +0,0 @@ -// Import required modules and types from the compiled TypeScript distribution (via dist/index.js) -const { - Alex2MQTT, - AlexaInterfaceType, - PowerController, - DisplayCategory, -} = require("../dist/index.js"); - -require("dotenv").config(); // Load environment variables from .env file - -// Simulated in-memory device state (used for example/demo purposes) -let currentMode = "mode.fanmode.Normal"; - -// Load MQTT connection credentials and root topic from environment variables -const username = process.env.MQTT_USERNAME; -const password = process.env.MQTT_PASSWORD; -const rootTopic = process.env.MQTT_ROOT_TOPIC; - -// Create and initialize a new Alexa-to-MQTT client -const alex2NodeClient = new Alex2MQTT(username, password, rootTopic, false); -alex2NodeClient.connect(); // Connect to the MQTT broker - -// Define the device name (Test mode controller) -const deviceName = "Test mode controller"; - -// Register the device with a unique endpoint ID (you can use something like "endpoint1") -const modeControllerDevice = alex2NodeClient.registerDevice( - deviceName, - "endpoint6", - [DisplayCategory.OTHER] -); - -// Add the 'MODE_CONTROLLER' capability for controlling device modes -const modeController = modeControllerDevice.addCapability( - AlexaInterfaceType.MODE_CONTROLLER -); - -modeController.setInstance("mode.fanmode"); -modeController.addFriendlyName("Fan Mode", "en-US"); -modeController.addSupportedModes([ - { - value: "mode.fanmode.Normal", - modeResources: { - friendlyNames: [ - { - "@type": "text", - value: { - text: "Normal", - locale: "en-US", - }, - }, - ], - }, - }, - { - value: "mode.fanmode.Low", - modeResources: { - friendlyNames: [ - { - "@type": "text", - value: { - text: "Low", - locale: "en-US", - }, - }, - ], - }, - }, -]); - -// Log the device name to verify registration -console.log(modeControllerDevice.getName()); - -/** - * Handle Alexa's ReportState directive. - * This occurs when Alexa queries the current state of the device (e.g., during routines or device status checks). - */ -modeControllerDevice.on("ReportState", (payload) => { - console.log("ReportState received!", payload); - - const { correlationToken } = payload.header; - - const status = modeControllerDevice.getStatusMessage(correlationToken); - - status - .addHealthProp("OK") // Device is healthy - .addModeControllerProp("mode.fanmode", currentMode); - - status.send(); // Send the state report back to Alexa -}); - -/** - * Handle incoming control directives (e.g., SetMode). - * These directives come from Alexa when a user issues a command. - */ -modeControllerDevice.on("Event", (directive, interfaceType) => { - console.log("Event received", { directive, interfaceType }); - // Ensure the interfaceType is either PowerController or other valid interfaces - if (interfaceType === AlexaInterfaceType.MODE_CONTROLLER) { - const name = directive.header.name; - const token = directive.header.correlationToken; - const instance = directive.header.instance; - - if (instance == "mode.fanmode") { - if (name === "SetMode") { - currentMode = directive.payload.mode; - } - } - - const status = modeControllerDevice.getStatusMessage(token, true); - - status - .addHealthProp("OK") // Device is healthy - .addModeControllerProp("mode.fanmode", currentMode); - - status.send(); // Send the state report back to Alexa - } -}); diff --git a/examples/ExamplePowerController.js b/examples/ExamplePowerController.js index 8e6bfb2..5658a55 100644 --- a/examples/ExamplePowerController.js +++ b/examples/ExamplePowerController.js @@ -23,7 +23,7 @@ alex2NodeClient.connect(); // Connect to the MQTT broker const deviceName = "Bedroom Light"; // Register the device with a unique endpoint ID (you can use something like "endpoint1") -const bedroomLight = alex2NodeClient.registerDevice(deviceName, "2E3AE9"); +const bedroomLight = alex2NodeClient.registerDevice(deviceName, "endpoint2"); // Add the PowerController capability (for turning on/off the device) bedroomLight.addCapability(AlexaInterfaceType.POWER_CONTROLLER); @@ -31,9 +31,6 @@ bedroomLight.addCapability(AlexaInterfaceType.POWER_CONTROLLER); // Log the device name to verify registration console.log(bedroomLight.getName()); -const momentaryModeInstance = "mode.signal.beep"; -const defaultMomentaryMode = "mode.signal.beep.Normal"; - /** * Handle Alexa's ReportState directive. * This occurs when Alexa queries the current state of the device (e.g., during routines or device status checks). @@ -41,15 +38,13 @@ const defaultMomentaryMode = "mode.signal.beep.Normal"; bedroomLight.on("ReportState", (payload) => { console.log("ReportState received!", payload); - const { correlationToken } = payload.header; + const { correlationToken } = payload; const status = bedroomLight.getStatusMessage(correlationToken); status .addHealthProp("OK") // Device is healthy - .addPowerControllerProp(outputState) // Report the current power state (ON/OFF) - .addBrightnessControllerProp(100) - .addModeControllerProp(momentaryModeInstance, defaultMomentaryMode); + .addPowerControllerProp(outputState); // Report the current power state (ON/OFF) status.send(); // Send the state report back to Alexa }); @@ -80,7 +75,6 @@ bedroomLight.on("Event", (directive, interfaceType) => { status .addHealthProp("OK") .addPowerControllerProp(outputState) - .addBrightnessControllerProp(100) .send(); } }); diff --git a/examples/ExamplePowerControllerDeferred.js b/examples/ExamplePowerControllerDeferred.js deleted file mode 100644 index 1661318..0000000 --- a/examples/ExamplePowerControllerDeferred.js +++ /dev/null @@ -1,90 +0,0 @@ -// Import required modules and types from the compiled TypeScript distribution (via dist/index.js) -const { - Alex2MQTT, - AlexaInterfaceType, - PowerController -} = require("../dist/index.js"); - -require("dotenv").config(); // Load environment variables from .env file - -// Simulated in-memory device state (used for example/demo purposes) -let outputState = PowerController.OFF; // Initialize the power state to OFF - -// Load MQTT connection credentials and root topic from environment variables -const username = process.env.MQTT_USERNAME; -const password = process.env.MQTT_PASSWORD; -const rootTopic = process.env.MQTT_ROOT_TOPIC; - -// Create and initialize a new Alexa-to-MQTT client -const alex2NodeClient = new Alex2MQTT(username, password, rootTopic, false); -alex2NodeClient.connect(); // Connect to the MQTT broker - -// Define the device name (deferred light) -const deviceName = "Deferred Light"; - -// Register the device with a unique endpoint ID (you can use something like "endpoint1") -const deferredLight = alex2NodeClient.registerDevice(deviceName, "endpoint3"); - -// Add the PowerController capability (for turning on/off the device) -deferredLight.addCapability(AlexaInterfaceType.POWER_CONTROLLER); - -// Log the device name to verify registration -console.log(deferredLight.getName()); - -/** - * Handle Alexa's ReportState directive. - * This occurs when Alexa queries the current state of the device (e.g., during routines or device status checks). - */ -deferredLight.on("ReportState", (payload) => { - console.log("ReportState received!", payload); - - const { correlationToken } = payload.header; - - // Step 1: Send a DeferredResponse after ~1 second - setTimeout(() => { - const deferred = deferredLight.getStatusMessage(correlationToken, false, true); // isDeferred = true - deferred.addEstimatedDeferralTime(20).send(); - console.log("Sent DeferredResponse"); - }, 2000); // simulate slight delay before deferred response - - // Step 2: Send actual StateReport after 10 seconds - setTimeout(() => { - const status = deferredLight.getStatusMessage(correlationToken,false,false); // normal StateReport - status - .addHealthProp("OK") - .addPowerControllerProp(outputState) - .send(true); - console.log("Sent actual StateReport with device status"); - }, 8000); -}); - - -/** - * Handle incoming control directives (e.g., TurnOn, TurnOff). - * These directives come from Alexa when a user issues a command. - */ -deferredLight.on("Event", (directive, interfaceType) => { - console.log("Event received", { directive, interfaceType }); - - // Ensure the interfaceType is either PowerController or other valid interfaces - if (interfaceType === AlexaInterfaceType.POWER_CONTROLLER) { - const name = directive.header.name; - const token = directive.header.correlationToken; - - // Update the internal state based on the command (TurnOn / TurnOff) - if (name === "TurnOn") { - outputState = PowerController.ON; - console.log("Turning ON the Deferred Light"); - } else if (name === "TurnOff") { - outputState = PowerController.OFF; - console.log("Turning OFF the Deferred Light"); - } - - // Respond to the directive with the updated device status - const status = deferredLight.getStatusMessage(token, true); - status - .addHealthProp("OK") - .addPowerControllerProp(outputState) - .send(); - } -}); diff --git a/examples/ExamplePowerControllerWithErrors.js b/examples/ExamplePowerControllerWithErrors.js deleted file mode 100644 index de20d68..0000000 --- a/examples/ExamplePowerControllerWithErrors.js +++ /dev/null @@ -1,92 +0,0 @@ -const { - Alex2MQTT, - AlexaInterfaceType, - PowerController, - AlexaErrorType, -} = require("../dist/index.js"); - -require("dotenv").config(); - -let outputState = PowerController.OFF; - -const username = process.env.MQTT_USERNAME; -const password = process.env.MQTT_PASSWORD; -const rootTopic = process.env.MQTT_ROOT_TOPIC; - -const alex2NodeClient = new Alex2MQTT(username, password, rootTopic, false); -alex2NodeClient.connect(); - -const errorLight = alex2NodeClient.registerDevice("bad light", "endpoint4"); -errorLight.addCapability(AlexaInterfaceType.POWER_CONTROLLER); - -console.log(errorLight.getName()); - -// Error cycling setup -const errorCycle = [ - AlexaErrorType.ENDPOINT_LOW_POWER, - AlexaErrorType.VALUE_OUT_OF_RANGE, - AlexaErrorType.HARDWARE_MALFUNCTION, - AlexaErrorType.BRIDGE_UNREACHABLE, - AlexaErrorType.FIRMWARE_OUT_OF_DATE, - AlexaErrorType.NOT_CALIBRATED, - AlexaErrorType.RATE_LIMIT_EXCEEDED, -]; -let currentErrorIndex = 0; - -/** - * Always return HARDWARE_MALFUNCTION error on state queries. - */ -errorLight.on("ReportState", (payload) => { - console.log("ReportState received!", payload); - - const { correlationToken } = payload.header; - const errorResponse = errorLight.getErrorMessage(correlationToken); - - errorResponse.setErrorMessage( - AlexaErrorType.HARDWARE_MALFUNCTION, - "The device is currently busy and cannot report its state." - ); - console.log("Sending error: HARDWARE_MALFUNCTION"); - errorResponse.send(); -}); - -/** - * Handle control directives with 90% error chance, cycling through fixed errors. - */ -errorLight.on("Event", (directive, interfaceType) => { - if (interfaceType === AlexaInterfaceType.POWER_CONTROLLER) { - const name = directive.header.name; - const token = directive.header.correlationToken; - - const shouldError = Math.random() < 0.9; - - if (shouldError) { - const errorType = errorCycle[currentErrorIndex]; - currentErrorIndex = (currentErrorIndex + 1) % errorCycle.length; - - const errorResponse = errorLight.getErrorMessage(token); - errorResponse.setErrorMessage( - errorType, - `Failed to process ${name} due to ${errorType.replace(/_/g, " ").toLowerCase()}.` - ); - console.log(`Sending error: ${errorType}`); - errorResponse.send(); - return; - } - - // Success path - if (name === "TurnOn") { - outputState = PowerController.ON; - console.log("Turning ON the Light"); - } else if (name === "TurnOff") { - outputState = PowerController.OFF; - console.log("Turning OFF the Light"); - } - - const status = errorLight.getStatusMessage(token, true); - status - .addHealthProp("OK") - .addPowerControllerProp(outputState) - .send(); - } -}); diff --git a/examples/ExamplePowerControllerWithMomentary.js b/examples/ExamplePowerControllerWithMomentary.js deleted file mode 100644 index eb0558b..0000000 --- a/examples/ExamplePowerControllerWithMomentary.js +++ /dev/null @@ -1,109 +0,0 @@ -// Import required modules and types from the compiled TypeScript distribution (via dist/index.js) -const { - Alex2MQTT, - AlexaInterfaceType, - PowerController, - DisplayCategory -} = require("../dist/index.js"); - -require("dotenv").config(); // Load environment variables from .env file - -// Simulated in-memory device state -let outputState = PowerController.OFF; -const momentaryModeInstance = "mode.signal.beep"; -const defaultMomentaryMode = "mode.signal.beep.Normal"; - -// Load MQTT connection credentials and root topic from environment variables -const username = process.env.MQTT_USERNAME; -const password = process.env.MQTT_PASSWORD; -const rootTopic = process.env.MQTT_ROOT_TOPIC; - -// Create and initialize a new Alexa-to-MQTT client -const alex2NodeClient = new Alex2MQTT(username, password, rootTopic, false); -alex2NodeClient.connect(); // Connect to the MQTT broker - -// Define the device name and register it -const deviceName = "Kitchen Light"; -const kitchenLight = alex2NodeClient.registerDevice( - deviceName, - "endpoint7", - [DisplayCategory.LIGHT] -); - -// Add PowerController capability -kitchenLight.addCapability(AlexaInterfaceType.POWER_CONTROLLER); - -// Add ModeController for momentary "beep" functionality -const momentaryController = kitchenLight.addCapability(AlexaInterfaceType.MODE_CONTROLLER); -momentaryController.setInstance(momentaryModeInstance); -momentaryController.addFriendlyName("Signal", "en-US"); -momentaryController.addSupportedModes([ - { - value: "mode.signal.beep.Normal", - modeResources: { - friendlyNames: [{ - "@type": "text", - value: { text: "Normal", locale: "en-US" } - }] - } - }, - { - value: "mode.signal.beep.Beep", - modeResources: { - friendlyNames: [{ - "@type": "text", - value: { text: "Beep", locale: "en-US" } - }] - } - } -]); - -// Log the device name to verify registration -console.log(kitchenLight.getName()); - -/** - * Handle Alexa's ReportState directive - */ -kitchenLight.on("ReportState", (payload) => { - console.log("ReportState received!", payload); - const { correlationToken } = payload.header; - const status = kitchenLight.getStatusMessage(correlationToken); - status - .addHealthProp("OK") - .addPowerControllerProp(outputState) - .addModeControllerProp(momentaryModeInstance, defaultMomentaryMode); - status.send(); -}); - -/** - * Handle incoming directives (PowerController and ModeController) - */ -kitchenLight.on("Event", (directive, interfaceType) => { - console.log("Event received", { directive, interfaceType }); - const { name, correlationToken, instance } = directive.header; - - const status = kitchenLight.getStatusMessage(correlationToken, true).addHealthProp("OK"); - - // PowerController logic - if (interfaceType === AlexaInterfaceType.POWER_CONTROLLER) { - if (name === "TurnOn") { - outputState = PowerController.ON; - console.log("Turning ON the Kitchen Light"); - } else if (name === "TurnOff") { - outputState = PowerController.OFF; - console.log("Turning OFF the Kitchen Light"); - } - status.addPowerControllerProp(outputState); - } - - // Momentary ModeController logic - else if (interfaceType === AlexaInterfaceType.MODE_CONTROLLER && instance === momentaryModeInstance) { - if (name === "SetMode") { - const modeValue = directive.payload.mode; - console.log(`[Momentary Trigger] Received mode: ${modeValue}`); - } - status.addModeControllerProp(momentaryModeInstance, defaultMomentaryMode); - } - - status.send(); -}); diff --git a/examples/ExampleThermostat.js b/examples/ExampleThermostat.js deleted file mode 100644 index a8737b9..0000000 --- a/examples/ExampleThermostat.js +++ /dev/null @@ -1,206 +0,0 @@ -// Import required modules and types from the compiled TypeScript distribution (via dist/index.js) -const { - Alex2MQTT, - AlexaInterfaceType, - PowerController, - TemperatureSensorScale, - DisplayCategory, - ThermostatMode, -} = require("../dist/index.js"); - -require("dotenv").config(); // Load environment variables from .env file - -// Simulated in-memory device state (used for example/demo purposes) -let outputState = PowerController.OFF; // Initialize the power state to OFF (if thermostat supports ON/OFF modes) - -// Load MQTT connection credentials and root topic from environment variables -const username = process.env.MQTT_USERNAME; -const password = process.env.MQTT_PASSWORD; -const rootTopic = process.env.MQTT_ROOT_TOPIC; - -// Create and initialize a new Alexa-to-MQTT client -const alex2NodeClient = new Alex2MQTT(username, password, rootTopic, false); -alex2NodeClient.connect(); // Connect to the MQTT broker - -// Define the device name (Thermostat Test) -const deviceName = "Thermostat Test"; - -// Register the thermostat device with a unique endpoint ID -const thermostat = alex2NodeClient.registerDevice( - deviceName, - "thermostatEndpoint1", - [DisplayCategory.TEMPERATURE_SENSOR, DisplayCategory.THERMOSTAT,DisplayCategory.OTHER] -); - -// Add thermostat-related capabilities -thermostat.addCapability(AlexaInterfaceType.THERMOSTAT_CONTROLLER); // Allows Alexa to control thermostat modes and setpoints -thermostat.addCapability(AlexaInterfaceType.TEMPERATURE_SENSOR); // Allows Alexa to read current temperature from the sensor -const modeController = thermostat.addCapability( - AlexaInterfaceType.MODE_CONTROLLER -); - -modeController.setInstance("mode.fanmode"); -modeController.addFriendlyName("Fan", "en-US"); -modeController.addSupportedModes([ - { - value: "mode.fanmode.Auto", - modeResources: { - friendlyNames: [ - { - "@type": "text", - value: { - text: "Bannana", - locale: "en-US", - }, - }, - ], - }, - }, - { - value: "mode.fanmode.On", - modeResources: { - friendlyNames: [ - { - "@type": "text", - value: { - text: "Apple", - locale: "en-US", - }, - }, - ], - }, - }, -]); - -// Log the device name to verify registration -console.log(thermostat.getName()); - -/** - * Handle Alexa's ReportState directive. - * This occurs when Alexa queries the current state of the thermostat (e.g., during routines or device status checks). - */ - -const deviceData = { - lowerSetpoint: 50, - upperSetpoint: 70, - targetSetpoint: 60, - current: 69, - thermostatMode: ThermostatMode.HEAT, - fanMode:"mode.fanmode.Auto" -}; -thermostat.on("ReportState", (payload) => { - // console.log("ReportState received!", payload); - - const { correlationToken } = payload.header; - - let status = thermostat.getStatusMessage(correlationToken); - - status - .addHealthProp("OK") // Device is healthy and reachable - .addModeControllerProp("mode.fanmode", deviceData.fanMode) - .addTemperatureSensorProp( - TemperatureSensorScale.CELSIUS, - deviceData.current - ) // Report current temperature - .addThermostatModeProp(deviceData.thermostatMode); // Report current thermostat mode - - if (deviceData.thermostatMode == "AUTO") { - status = status - .addThermostatControllerProp( - "lowerSetpoint", - TemperatureSensorScale.CELSIUS, - deviceData.lowerSetpoint - ) // Lower bound of temperature range - .addThermostatControllerProp( - "upperSetpoint", - TemperatureSensorScale.CELSIUS, - deviceData.upperSetpoint - ); // Upper bound of temperature range - } else { - status = status.addThermostatControllerProp( - "targetSetpoint", - TemperatureSensorScale.CELSIUS, - deviceData.targetSetpoint - ); // Lower bound of temperature range - } - status.send(); // Send the full state report back to Alexa -}); - -/** - * Handle incoming control directives for the thermostat. - * These directives come from Alexa when a user issues a command (e.g., to change temperature or mode). - */ -thermostat.on("Event", (directive, interfaceType) => { - console.log("Event received", { directive, interfaceType }); - if (interfaceType === AlexaInterfaceType.THERMOSTAT_CONTROLLER) { - const name = directive.header.name; - const token = directive.header.correlationToken; - - if (name == "SetTargetTemperature") { - console.log({ name: directive.payload }); - if (directive.payload.upperSetpoint) { - deviceData.upperSetpoint = parseTempValue( - directive.payload.upperSetpoint - ); - } - if (directive.payload.lowerSetpoint) { - deviceData.lowerSetpoint = parseTempValue( - directive.payload.lowerSetpoint - ); - } - if (directive.payload.targetSetpoint) { - deviceData.targetSetpoint = parseTempValue( - directive.payload.targetSetpoint - ); - } - } - if (name == "SetThermostatMode") { - console.log(directive.payload.thermostatMode); - if (directive.payload.thermostatMode) { - deviceData.thermostatMode = directive.payload.thermostatMode.value; - } - } - - let status = thermostat.getStatusMessage(token, true); - - status - .addHealthProp("OK") // Device is healthy and reachable - .addModeControllerProp("mode.fanmode", deviceData.fanMode) - .addTemperatureSensorProp( - TemperatureSensorScale.FAHRENHEIT, - deviceData.current - ) // Report current temperature - .addThermostatModeProp(deviceData.thermostatMode); // Report current thermostat mode - if (deviceData.thermostatMode == "AUTO") { - status = status - .addThermostatControllerProp( - "lowerSetpoint", - TemperatureSensorScale.FAHRENHEIT, - deviceData.lowerSetpoint - ) // Lower bound of temperature range - .addThermostatControllerProp( - "upperSetpoint", - TemperatureSensorScale.FAHRENHEIT, - deviceData.upperSetpoint - ); // Upper bound of temperature range - } else { - status = status.addThermostatControllerProp( - "targetSetpoint", - TemperatureSensorScale.FAHRENHEIT, - deviceData.targetSetpoint - ); // Lower bound of temperature range - } - status.send(); // Send the full state report back to Alexa - } -}); - -function parseTempValue(data) { - console.log(data); - - if (data.scale === "FAHRENHEIT") { - return data.value; - } - - const fahrenheit = (data.value * 9) / 5 + 32; - return fahrenheit; -} diff --git a/examples/ExampleToggleController.js b/examples/ExampleToggleController.js deleted file mode 100644 index 0d6deca..0000000 --- a/examples/ExampleToggleController.js +++ /dev/null @@ -1,77 +0,0 @@ -const { - Alex2MQTT, - AlexaInterfaceType, - PowerController, - } = require("../dist/index.js"); - - require("dotenv").config(); - - const username = process.env.MQTT_USERNAME; - const password = process.env.MQTT_PASSWORD; - const rootTopic = process.env.MQTT_ROOT_TOPIC; - - const alex2NodeClient = new Alex2MQTT(username, password, rootTopic, true); - alex2NodeClient.connect(); - - const deviceName = "Custom Device"; - const customToggle = alex2NodeClient.registerDevice(deviceName, "endpoint8"); - - // Define 8 toggle instances and friendly names - const switchCount = 8; - const instanceNames = Array.from({ length: switchCount }, (_, i) => `nodejs.toggle.switch${i + 1}`); - const friendlyNames = Array.from({ length: switchCount }, (_, i) => `Switch ${i + 1}`); - - // Add toggle capabilities and store toggle objects - const toggles = []; - for (let i = 0; i < switchCount; i++) { - const toggle = customToggle.addCapability(AlexaInterfaceType.TOGGLE_CONTROLLER); - toggle.setInstance(instanceNames[i]); - toggle.addFriendlyName(friendlyNames[i], "en-US"); - toggles.push(toggle); - } - - // Output states for all switches - const toggleStates = Array(switchCount).fill(PowerController.OFF); - - console.log(customToggle.getName()); - - customToggle.on("ReportState", (payload) => { - console.log("ReportState received!", payload); - const { correlationToken } = payload.header; - const status = customToggle.getStatusMessage(correlationToken); - - status.addHealthProp("OK"); - - // Report all toggle states - for (let i = 0; i < switchCount; i++) { - status.addToggleControllerProp(toggleStates[i], instanceNames[i]); - } - - status.send(); - }); - - customToggle.on("Event", (directive, interfaceType) => { - console.log("Event received", { directive, interfaceType }); - - if (interfaceType === AlexaInterfaceType.TOGGLE_CONTROLLER) { - const { name, correlationToken, instance } = directive.header; - - const index = instanceNames.indexOf(instance); - if (index !== -1) { - if (name === "TurnOn") { - toggleStates[index] = PowerController.ON; - console.log(`Turning ON ${friendlyNames[index]}`); - } else if (name === "TurnOff") { - toggleStates[index] = PowerController.OFF; - console.log(`Turning OFF ${friendlyNames[index]}`); - } - - const status = customToggle.getStatusMessage(correlationToken, true); - status - .addHealthProp("OK") - .addToggleControllerProp(toggleStates[index], instance) - .send(); - } - } - }); - \ No newline at end of file diff --git a/package.json b/package.json index 9b4779e..92dc81d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "alex2node", - "version": "1.5.0", + "version": "1.0.0", "description": "A Node.js library for creating Alexa-compatible devices using MQTT, based on Alex2MQTT.", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/Alex2Node.ts b/src/Alex2Node.ts index dc3b821..bf18271 100644 --- a/src/Alex2Node.ts +++ b/src/Alex2Node.ts @@ -1,9 +1,7 @@ import mqtt, { MqttClient } from "mqtt"; import Device from "./Device"; -import { EventEmitter } from "events"; -import { DisplayCategory } from "./DisplayCategory"; -class Alex2MQTT extends EventEmitter { +class Alex2MQTT { private client: MqttClient | null = null; private devices: Device[] = []; private MqttHost = "mqtt://Alex2MQTT.stormysdream.club"; @@ -13,9 +11,7 @@ class Alex2MQTT extends EventEmitter { private password: string, private rootTopic: string, private debugLogging: boolean - ) { - super(); // Initialize EventEmitter - } + ) {} connect(): void { const options = { @@ -35,8 +31,7 @@ class Alex2MQTT extends EventEmitter { console.log("[Alex2Node.ts] Subscribed to topics"); } if (err) { - console.error("[Alex2Node.ts] MQTT connection error:", err); - this.emit("error", err); + throw err; } }); }); @@ -61,15 +56,14 @@ class Alex2MQTT extends EventEmitter { JSON.stringify(deviceArray), (err) => { if (err) { - console.error("[Alex2Node.ts] MQTT connection error:", err); - this.emit("error", err); + throw err; } else if (this.debugLogging) { console.log( `[Alex2Node.ts] Discovery payloads published to ${ topic + "_r" }` ); - console.log(JSON.stringify(deviceArray, null, 2)); + console.log(deviceArray); } } ); @@ -106,8 +100,8 @@ class Alex2MQTT extends EventEmitter { ); } device.emit("ReportState", payload); - } else { - device.emit("Event", payload, payload.header.namespace); + }else{ + device.emit("Event", payload,payload.header.namespace); } } }); @@ -115,16 +109,11 @@ class Alex2MQTT extends EventEmitter { if (this.debugLogging) { console.error("[Alex2Node.ts] Connection failed:", err); } - console.error("[Alex2Node.ts] MQTT connection error:", err); - this.emit("error", err); + throw err; }); } - registerDevice( - name: string, - endpointId: string, - displayCategory: DisplayCategory | DisplayCategory[] | null - ): Device { + registerDevice(name: string, endpointId: string): Device { if (!this.client) { throw new Error("Must call connect before creating devices"); } @@ -133,20 +122,7 @@ class Alex2MQTT extends EventEmitter { `[Alex2Node.ts] Creating new device with endpoint: ${endpointId}` ); } - const normalizedCategory = - displayCategory === null - ? [DisplayCategory.LIGHT] - : Array.isArray(displayCategory) - ? displayCategory - : [displayCategory||DisplayCategory.LIGHT]; - - const device = new Device( - this.client!, - this.rootTopic, - name, - endpointId, - normalizedCategory - ); + const device = new Device(this.client!, this.rootTopic, name, endpointId); this.devices.push(device); return device; } diff --git a/src/AlexaErrorResponse.ts b/src/AlexaErrorResponse.ts deleted file mode 100644 index ee845df..0000000 --- a/src/AlexaErrorResponse.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; -import { AlexaInterface, AlexaInterfaceType } from "./AlexaInterface"; -import { MqttClient } from "mqtt"; - -export enum AlexaErrorType { - ALREADY_IN_OPERATION = "ALREADY_IN_OPERATION", - AUTHORIZATION_REQUIRED = "AUTHORIZATION_REQUIRED", - BRIDGE_UNREACHABLE = "BRIDGE_UNREACHABLE", - BYPASS_NEEDED = "BYPASS_NEEDED", - CLOUD_CONTROL_DISABLED = "CLOUD_CONTROL_DISABLED", - CHILD_LOCK = "CHILD_LOCK", - CONFIGURATION_UPDATE_NOT_ALLOWED = "CONFIGURATION_UPDATE_NOT_ALLOWED", - COOK_DURATION_TOO_LONG = "COOK_DURATION_TOO_LONG", - COOLING_LOCKOUT_TEMPERATURE_VALUE_OUT_OF_RANGE = "COOLING_LOCKOUT_TEMPERATURE_VALUE_OUT_OF_RANGE", - COOLING_STAGES_EXCEEDS_LIMIT = "COOLING_STAGES_EXCEEDS_LIMIT", - DATA_DELETION_NOT_SUPPORTED = "DATA_DELETION_NOT_SUPPORTED", - DATA_RETRIEVAL_NOT_SUPPORTED = "DATA_RETRIEVAL_NOT_SUPPORTED", - DEVICE_STUCK = "DEVICE_STUCK", - DISABLED_BY_USER = "DISABLED_BY_USER", - DO_NOT_DISTURB_MODE = "DO_NOT_DISTURB_MODE", - DOOR_CLOSED_TOO_LONG = "DOOR_CLOSED_TOO_LONG", - DOOR_OPEN = "DOOR_OPEN", - DUAL_SETPOINTS_UNSUPPORTED = "DUAL_SETPOINTS_UNSUPPORTED", - ENDPOINT_BUSY = "ENDPOINT_BUSY", - ENDPOINT_CONTROL_UNAVAILABLE = "ENDPOINT_CONTROL_UNAVAILABLE", - ENDPOINT_LOW_POWER = "ENDPOINT_LOW_POWER", - ENDPOINT_UNREACHABLE = "ENDPOINT_UNREACHABLE", - EXCEEDED_PIN_ATTEMPTS = "EXCEEDED_PIN_ATTEMPTS", - EXPIRED_AUTHORIZATION_CREDENTIAL = "EXPIRED_AUTHORIZATION_CREDENTIAL", - FAILED_TO_BOOTSTRAP_COMMISSIONING_PROCESS = "FAILED_TO_BOOTSTRAP_COMMISSIONING_PROCESS", - FIRMWARE_OUT_OF_DATE = "FIRMWARE_OUT_OF_DATE", - HARDWARE_MALFUNCTION = "HARDWARE_MALFUNCTION", - HEATING_LOCKOUT_TEMPERATURE_VALUE_OUT_OF_RANGE = "HEATING_LOCKOUT_TEMPERATURE_VALUE_OUT_OF_RANGE", - HEATING_STAGES_EXCEEDS_LIMIT = "HEATING_STAGES_EXCEEDS_LIMIT", - INSUFFICIENT_PERMISSIONS = "INSUFFICIENT_PERMISSIONS", - INSUFFICIENT_RESOURCE = "INSUFFICIENT_RESOURCE", - INSUFFICIENT_SPACE = "INSUFFICIENT_SPACE", - INTERNAL_ERROR = "INTERNAL_ERROR", - INVALID_AUTHORIZATION_CREDENTIAL = "INVALID_AUTHORIZATION_CREDENTIAL", - INVALID_AUXILIARY_HEATING_SYSTEM_TYPE = "INVALID_AUXILIARY_HEATING_SYSTEM_TYPE", - INVALID_DIRECTIVE = "INVALID_DIRECTIVE", - INVALID_SYSTEM_TYPE = "INVALID_SYSTEM_TYPE", - INVALID_TARGET_STATE = "INVALID_TARGET_STATE", - INVALID_TEMPERATURE_SCALE = "INVALID_TEMPERATURE_SCALE", - INVALID_TERMINAL_CONNECTION = "INVALID_TERMINAL_CONNECTION", - INVALID_VALUE = "INVALID_VALUE", - MAINTENANCE_REQUIRED = "MAINTENANCE_REQUIRED", - MAX_COMMISSIONING_LIMIT_REACHED = "MAX_COMMISSIONING_LIMIT_REACHED", - MISSING_SETUP_INFORMATION = "MISSING_SETUP_INFORMATION", - NO_SUCH_ENDPOINT = "NO_SUCH_ENDPOINT", - NOT_CALIBRATED = "NOT_CALIBRATED", - NOT_IN_OPERATION = "NOT_IN_OPERATION", - NOT_READY = "NOT_READY", - NOT_SUPPORTED_IN_CURRENT_MODE = "NOT_SUPPORTED_IN_CURRENT_MODE", - NOT_SUPPORTED_WITH_CURRENT_BATTERY_CHARGE_STATE = "NOT_SUPPORTED_WITH_CURRENT_BATTERY_CHARGE_STATE", - OBSTACLE_DETECTED = "OBSTACLE_DETECTED", - PARTNER_APPLICATION_REDIRECTION = "PARTNER_APPLICATION_REDIRECTION", - PIN_SETUP_REQUIRED = "PIN_SETUP_REQUIRED", - POWER_LEVEL_NOT_SUPPORTED = "POWER_LEVEL_NOT_SUPPORTED", - PREHEAT_REQUIRED = "PREHEAT_REQUIRED", - PROBE_REQUIRED = "PROBE_REQUIRED", - RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED", - REMOTE_START_NOT_SUPPORTED = "REMOTE_START_NOT_SUPPORTED", - REMOVE_PROBE = "REMOVE_PROBE", - REMOTE_START_DISABLED = "REMOTE_START_DISABLED", - REQUESTED_SETPOINTS_TOO_CLOSE = "REQUESTED_SETPOINTS_TOO_CLOSE", - SAFETY_BEAM_BREACHED = "SAFETY_BEAM_BREACHED", - SUBSCRIPTION_REQUIRED = "SUBSCRIPTION_REQUIRED", - TEMPERATURE_VALUE_OUT_OF_RANGE = "TEMPERATURE_VALUE_OUT_OF_RANGE", - THERMOSTAT_IS_OFF = "THERMOSTAT_IS_OFF", - TOO_MANY_FAILED_ATTEMPTS = "TOO_MANY_FAILED_ATTEMPTS", - TRIPLE_SETPOINTS_UNSUPPORTED = "TRIPLE_SETPOINTS_UNSUPPORTED", - UNABLE_TO_CHARGE = "UNABLE_TO_CHARGE", - UNAUTHORIZED = "UNAUTHORIZED", - UNCLEARED_ALARM = "UNCLEARED_ALARM", - UNSUPPORTED_THERMOSTAT_MODE = "UNSUPPORTED_THERMOSTAT_MODE", - UNCLEARED_TROUBLE = "UNCLEARED_TROUBLE", - UNWILLING_TO_SET_SCHEDULE = "UNWILLING_TO_SET_SCHEDULE", - UNWILLING_TO_SET_VALUE = "UNWILLING_TO_SET_VALUE", - VALUE_OUT_OF_RANGE = "VALUE_OUT_OF_RANGE", -} - -export class AlexaErrorResponse { - private event: any; - private rootTopic: string; - private endpointId: string; - private mqttClient: MqttClient; - - constructor( - correlationToken: string, - rootTopic: string, - endpointId: string, - mqttClient: MqttClient - ) { - this.rootTopic = rootTopic; - this.endpointId = endpointId; - this.mqttClient = mqttClient; - - this.event = { - header: { - namespace: "Alexa", - name: "ErrorResponse", - messageId: this.generateMessageId(), - correlationToken, - payloadVersion: "3", - }, - endpoint: { - endpointId, - }, - payload: {}, - }; - } - private generateMessageId(): string { - return uuidv4(); - } - setErrorMessage( - type: string, - message: string, - otherParams: Record = {} - ) { - this.event.payload = { - type, - message, - ...otherParams, - }; - } - - send(sendAsync = false) { - const payload = { - event: this.event, - }; - const topic = `${this.rootTopic}/${this.endpointId}/${ - sendAsync ? "deferredResponse" : "alexaResponce" - }`; //Yes this should be response but it is incorrect in both Alex2MQTT and Alex2ESP so for consistency is is wrong here too - const payloadStr = JSON.stringify(payload); - - this.mqttClient.publish(topic, payloadStr, (err) => { - if (err) { - console.error( - "[AlexaErrorResponse] Failed to publish status message:", - err - ); - } else { - console.log(`[AlexaErrorResponse] Sent message to ${topic}`); - } - }); - } -} diff --git a/src/AlexaInterface.ts b/src/AlexaInterface.ts index 31fac2d..9cdc58d 100644 --- a/src/AlexaInterface.ts +++ b/src/AlexaInterface.ts @@ -81,7 +81,6 @@ interface FriendlyName { export class AlexaInterface { private friendlyNames: FriendlyName[] = []; private actionMappings: ActionMapping[] = []; - private supportedModes: string[] = []; constructor( public type: AlexaInterfaceType, @@ -97,9 +96,7 @@ export class AlexaInterface { addFriendlyName(name: string, locale: string): void { this.friendlyNames.push({ text: name, locale }); } - addSupportedModes(modes: string[]): void { - this.supportedModes = modes; - } + setInstance(name: string): void { this.instance = name; } @@ -179,12 +176,11 @@ export class AlexaInterface { case AlexaInterfaceType.SCENE_CONTROLLER: case AlexaInterfaceType.SEEK_CONTROLLER: case AlexaInterfaceType.TEMPERATURE_SENSOR: + case AlexaInterfaceType.THERMOSTAT_CONTROLLER: case AlexaInterfaceType.TOGGLE_CONTROLLER: return "3"; case AlexaInterfaceType.LAUNCHER: return "1.1"; - case AlexaInterfaceType.THERMOSTAT_CONTROLLER: - return "3.2"; case AlexaInterfaceType.ENDPOINT_HEALTH: return "3.3"; default: @@ -212,17 +208,9 @@ export class AlexaInterface { return ["connectivity"]; case AlexaInterfaceType.TOGGLE_CONTROLLER: return ["toggleState"]; - case AlexaInterfaceType.MODE_CONTROLLER: - return ["mode"]; case AlexaInterfaceType.TEMPERATURE_SENSOR: return ["temperature"]; - case AlexaInterfaceType.THERMOSTAT_CONTROLLER: - return [ - "lowerSetpoint", - "upperSetpoint", - "thermostatMode", - "adaptiveRecoveryStatus", - ]; + case AlexaInterfaceType.APPLICATION_STATE_REPORTER: case AlexaInterfaceType.AUDIO_PLAY_QUEUE: case AlexaInterfaceType.AUTHORIZATION_CONTROLLER: @@ -246,6 +234,7 @@ export class AlexaInterface { case AlexaInterfaceType.LOCK_CONTROLLER: case AlexaInterfaceType.MEDIA_PLAYBACK: case AlexaInterfaceType.MEDIA_SEARCH: + case AlexaInterfaceType.MODE_CONTROLLER: case AlexaInterfaceType.MOTION_SENSOR: case AlexaInterfaceType.PERCENTAGE_CONTROLLER: case AlexaInterfaceType.PLAYBACK_CONTROLLER: @@ -263,6 +252,7 @@ export class AlexaInterface { case AlexaInterfaceType.SMART_VISION_SNAPSHOT_PROVIDER: case AlexaInterfaceType.SPEAKER: case AlexaInterfaceType.STEP_SPEAKER: + case AlexaInterfaceType.THERMOSTAT_CONTROLLER: case AlexaInterfaceType.THERMOSTAT_CONTROLLER_CONFIGURATION: case AlexaInterfaceType.THERMOSTAT_CONTROLLER_HVAC_COMPONENTS: case AlexaInterfaceType.THERMOSTAT_CONTROLLER_SCHEDULE: @@ -287,20 +277,9 @@ export class AlexaInterface { properties: { retrievable: this.retrievable, proactivelyReported: this.proactivelyReported, - supported: this.getProps().map(name => ({ name })), + supported: [{ name: this.getProps()[0] }], }, }; - if(this.type==AlexaInterfaceType.THERMOSTAT_CONTROLLER){ - doc.configuration={ - "supportedModes": ["HEAT", "COOL", "AUTO", "OFF"], - "supportsScheduling": false - } - }else if (this.supportedModes.length > 0) { - doc["configuration"] = { - ordered:true, - supportedModes: this.supportedModes, - }; - } if (this.instance) { doc.instance = this.instance; } @@ -315,7 +294,6 @@ export class AlexaInterface { }; friendlyNamesArray.push(friendlyNameObj); }); - capabilityResources.friendlyNames=friendlyNamesArray doc["capabilityResources"] = capabilityResources; } if (this.actionMappings.length > 0) { diff --git a/src/AlexaStatusMessage.ts b/src/AlexaStatusMessage.ts index 7855730..e8f4f38 100644 --- a/src/AlexaStatusMessage.ts +++ b/src/AlexaStatusMessage.ts @@ -7,15 +7,6 @@ export enum EndpointHealth { UNREACHABLE = "UNREACHABLE", } -export enum ThermostatMode { - OFF = "OFF", - HEAT = "HEAT", - COOL = "COOL", - AUTO = "AUTO", - ECO = "ECO", - CUSTOM = "CUSTOM", -} - export enum PowerController { ON = "ON", OFF = "OFF", @@ -57,28 +48,22 @@ export class AlexaStatusMessage { private rootTopic: string; private endpointId: string; private mqttClient: MqttClient; - private isDeferred: boolean; constructor( correlationToken: string, rootTopic: string, endpointId: string, mqttClient: MqttClient, - isResponse: boolean = false, - isDeferred: boolean = false + isResponse: boolean = false ) { this.rootTopic = rootTopic; this.endpointId = endpointId; this.mqttClient = mqttClient; - this.isDeferred = isDeferred; + this.event = { header: { namespace: "Alexa", - name: isDeferred - ? "DeferredResponse" - : isResponse - ? "Response" - : "StateReport", + name: isResponse ? "Response" : "StateReport", payloadVersion: "3", messageId: this.generateMessageId(), correlationToken, @@ -97,6 +82,7 @@ export class AlexaStatusMessage { private getTimestamp(): string { return new Date().toISOString(); } + private addProperty( namespace: AlexaInterfaceType, name: string, @@ -115,56 +101,6 @@ export class AlexaStatusMessage { this.context.properties.push(prop); return this; } - public addModeControllerProp(instance: string, value: string, uncertaintyInMs = 0): this { - return this.addProperty( - AlexaInterfaceType.MODE_CONTROLLER, - "mode", - value, - uncertaintyInMs, - instance - ); - } - public addThermostatModeProp(mode: string, uncertaintyInMs = 0): this { - return this.addProperty( - AlexaInterfaceType.THERMOSTAT_CONTROLLER, - "thermostatMode", - mode, - uncertaintyInMs - ); - } - public addEstimatedDeferralTime(seconds: number): this { - if (this.isDeferred) { - this.event.payload = { - estimatedDeferralInSeconds: seconds, - }; - } else { - console.warn( - "[AlexaStatusMessage.ts] Attempted to add estimated deferral time, but message is not marked as DeferredResponse." - ); - } - return this; - } - - public addThermostatControllerProp( - name: "lowerSetpoint" | "upperSetpoint" | "targetSetpoint", - scale: TemperatureSensorScale, - value: number, - uncertaintyInMs = 0 - ): this { - const tempValue = { - value: - scale === TemperatureSensorScale.FAHRENHEIT - ? (value - 32) * (5 / 9) - : value, - scale: "CELSIUS", - }; - return this.addProperty( - AlexaInterfaceType.THERMOSTAT_CONTROLLER, - name, - tempValue, - uncertaintyInMs - ); - } public addHealthProp(health: EndpointHealth, uncertaintyInMs = 0): this { return this.addProperty( @@ -250,12 +186,12 @@ export class AlexaStatusMessage { return this; } - public send(sendAsync: boolean): void { + public send(): void { const payload = { - context: this.isDeferred ? null : this.context, + context: this.context, event: this.event, }; - const topic = `${this.rootTopic}/${this.endpointId}/${sendAsync?"deferredResponse":"alexaResponce"}`; //Yes this should be response but it is incorrect in both Alex2MQTT and Alex2ESP so for consistency is is wrong here too + const topic = `${this.rootTopic}/${this.endpointId}/alexaResponce`;//Yes this should be response but it is incorrect in both Alex2MQTT and Alex2ESP so for consistency is is wrong here too const payloadStr = JSON.stringify(payload); this.mqttClient.publish(topic, payloadStr, (err) => { @@ -265,7 +201,7 @@ export class AlexaStatusMessage { err ); } else { - console.log(`[AlexaStatusMessage] Sent message to ${topic}`); + // console.log(`[AlexaStatusMessage] Sent message to ${topic}`); } }); } diff --git a/src/Device.ts b/src/Device.ts index 8a1871d..80fa436 100644 --- a/src/Device.ts +++ b/src/Device.ts @@ -3,7 +3,6 @@ import { DisplayCategory } from "./DisplayCategory"; import { EventEmitter } from "events"; import { MqttClient } from "mqtt"; import { AlexaStatusMessage } from "./AlexaStatusMessage"; -import { AlexaErrorResponse } from "./AlexaErrorResponse"; class Device extends EventEmitter { protected softwareVersion: string = "1.0.0"; @@ -14,7 +13,7 @@ class Device extends EventEmitter { private rootTopic: string, public name: string, public endpointId: string, - public displayCategory: Array | null, + public displayCategory: DisplayCategory = DisplayCategory.LIGHT, public description: string = "Alexa to Node.js bridge", public manufacturerName: string = "Alex2Node", public manufacturer: string = "Alex2Node", @@ -35,12 +34,12 @@ class Device extends EventEmitter { return this.endpointId; } - setDisplayCategory(category: DisplayCategory | DisplayCategory[]): void { - this.displayCategory = Array.isArray(category) ? category : [category]; + setDisplayCategory(category: DisplayCategory): void { + this.displayCategory = category; } - getDisplayCategory(): Array { - return this.displayCategory || [DisplayCategory.LIGHT]; + getDisplayCategory(): DisplayCategory { + return this.displayCategory; } setDescription(description: string): void { @@ -50,26 +49,16 @@ class Device extends EventEmitter { getDescription(): string { return this.description; } - getErrorMessage(correlationToken: string): AlexaErrorResponse { - return new AlexaErrorResponse( - correlationToken, - this.rootTopic, - this.endpointId, - this.mqttClient - ); - } getStatusMessage( correlationToken: string, - isResponse = false, - isDeferred = false + isResponse = false ): AlexaStatusMessage { return new AlexaStatusMessage( correlationToken, this.rootTopic, this.endpointId, this.mqttClient, - isResponse, - isDeferred + isResponse ); } setManufacturerName(name: string): void { @@ -101,12 +90,12 @@ class Device extends EventEmitter { } addCapability(type: AlexaInterfaceType): AlexaInterface { - // const existing = this.capabilities.find( - // (iface) => iface.getType() === type - // ); - // if (existing) { - // return existing; - // } + const existing = this.capabilities.find( + (iface) => iface.getType() === type + ); + if (existing) { + return existing; + } const newCapability = new AlexaInterface(type); this.capabilities.push(newCapability); @@ -122,7 +111,7 @@ class Device extends EventEmitter { friendlyName: this.name, description: this.description, manufacturerName: this.manufacturerName, - displayCategories: this.getDisplayCategory(), + displayCategories: [this.displayCategory], additionalAttributes: { manufacturer: this.manufacturer, model: this.model, diff --git a/src/index.ts b/src/index.ts index f381872..77b9e07 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,4 @@ export { default as Alex2MQTT } from './Alex2Node'; export { AlexaInterfaceType } from './AlexaInterface'; export { ActionMapping, AlexaActions } from './ActionMapping'; -export {DisplayCategory} from "./DisplayCategory"; -export { PowerController,EndpointHealth,TemperatureSensorScale,ThermostatMode } from './AlexaStatusMessage'; -export { AlexaErrorType,AlexaErrorResponse} from './AlexaErrorResponse'; +export { PowerController } from './AlexaStatusMessage';