Add support for sending Error Responses

This commit is contained in:
David 2025-05-12 16:03:15 +00:00
parent 61b032c889
commit 4af45ff726
9 changed files with 396 additions and 19 deletions

125
dist/AlexaErrorResponse.js vendored Normal file
View file

@ -0,0 +1,125 @@
"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;

4
dist/Device.js vendored
View file

@ -4,6 +4,7 @@ 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") {
super();
@ -40,6 +41,9 @@ 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);
}

5
dist/index.js vendored
View file

@ -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.ThermostatMode = exports.TemperatureSensorScale = exports.EndpointHealth = exports.PowerController = exports.DisplayCategory = exports.AlexaActions = exports.ActionMapping = exports.AlexaInterfaceType = exports.Alex2MQTT = void 0;
exports.AlexaErrorResponse = exports.AlexaErrorType = exports.ThermostatMode = exports.TemperatureSensorScale = exports.EndpointHealth = exports.PowerController = exports.DisplayCategory = 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");
@ -18,3 +18,6 @@ Object.defineProperty(exports, "PowerController", { enumerable: true, get: funct
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; } });

View file

@ -0,0 +1,80 @@
// 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 (bedroom light)
const deviceName = "Bedroom Light";
// Register the device with a unique endpoint ID (you can use something like "endpoint1")
const bedroomLight = alex2NodeClient.registerDevice(deviceName, "endpoint2");
// Add the PowerController capability (for turning on/off the device)
bedroomLight.addCapability(AlexaInterfaceType.POWER_CONTROLLER);
// Log the device name to verify registration
console.log(bedroomLight.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).
*/
bedroomLight.on("ReportState", (payload) => {
console.log("ReportState received!", payload);
const { correlationToken } = payload.header;
const status = bedroomLight.getStatusMessage(correlationToken);
status
.addHealthProp("OK") // Device is healthy
.addPowerControllerProp(outputState); // Report the current power state (ON/OFF)
status.send(); // Send the state report back to Alexa
});
/**
* Handle incoming control directives (e.g., TurnOn, TurnOff).
* These directives come from Alexa when a user issues a command.
*/
bedroomLight.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 Bedroom Light");
} else if (name === "TurnOff") {
outputState = PowerController.OFF;
console.log("Turning OFF the Bedroom Light");
}
// Respond to the directive with the updated device status
const status = bedroomLight.getStatusMessage(token, true);
status
.addHealthProp("OK")
.addPowerControllerProp(outputState)
.send();
}
});

View file

@ -45,11 +45,11 @@ console.log(thermostat.getName());
*/
const deviceData = {
lowerSetpoint: 20,
upperSetpoint: 20,
targetSetpoint: 20,
current: 20,
thermostatMode: ThermostatMode.AUTO,
lowerSetpoint: 50,
upperSetpoint: 70,
targetSetpoint: 60,
current: 69,
thermostatMode: ThermostatMode.HEAT,
};
thermostat.on("ReportState", (payload) => {
// console.log("ReportState received!", payload);
@ -101,17 +101,17 @@ thermostat.on("Event", (directive, interfaceType) => {
if (name == "SetTargetTemperature") {
console.log({ name: directive.payload });
if (directive.payload.upperSetpoint) {
deviceData.upperSetpoint = praseTempValue(
deviceData.upperSetpoint = parseTempValue(
directive.payload.upperSetpoint
);
}
if (directive.payload.lowerSetpoint) {
deviceData.lowerSetpoint = praseTempValue(
deviceData.lowerSetpoint = parseTempValue(
directive.payload.lowerSetpoint
);
}
if (directive.payload.targetSetpoint) {
deviceData.targetSetpoint = praseTempValue(
deviceData.targetSetpoint = parseTempValue(
directive.payload.targetSetpoint
);
}
@ -128,7 +128,7 @@ thermostat.on("Event", (directive, interfaceType) => {
status
.addHealthProp("OK") // Device is healthy and reachable
.addTemperatureSensorProp(
TemperatureSensorScale.CELSIUS,
TemperatureSensorScale.FAHRENHEIT,
deviceData.current
) // Report current temperature
.addThermostatModeProp(deviceData.thermostatMode); // Report current thermostat mode
@ -136,18 +136,18 @@ thermostat.on("Event", (directive, interfaceType) => {
status = status
.addThermostatControllerProp(
"lowerSetpoint",
TemperatureSensorScale.CELSIUS,
TemperatureSensorScale.FAHRENHEIT,
deviceData.lowerSetpoint
) // Lower bound of temperature range
.addThermostatControllerProp(
"upperSetpoint",
TemperatureSensorScale.CELSIUS,
TemperatureSensorScale.FAHRENHEIT,
deviceData.upperSetpoint
); // Upper bound of temperature range
} else {
status = status.addThermostatControllerProp(
"targetSetpoint",
TemperatureSensorScale.CELSIUS,
TemperatureSensorScale.FAHRENHEIT,
deviceData.targetSetpoint
); // Lower bound of temperature range
}
@ -155,6 +155,13 @@ thermostat.on("Event", (directive, interfaceType) => {
}
});
function praseTempValue(data) {
return data.value;
function parseTempValue(data) {
console.log(data);
if (data.scale === "FAHRENHEIT") {
return data.value;
}
const fahrenheit = (data.value * 9) / 5 + 32;
return fahrenheit;
}

View file

@ -1,6 +1,6 @@
{
"name": "alex2node",
"version": "1.2.0",
"version": "1.3.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",

148
src/AlexaErrorResponse.ts Normal file
View file

@ -0,0 +1,148 @@
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<string, any> = {}
) {
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}`);
}
});
}
}

View file

@ -3,6 +3,7 @@ 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";
@ -39,7 +40,7 @@ class Device extends EventEmitter {
}
getDisplayCategory(): Array<DisplayCategory> {
return this.displayCategory ||[DisplayCategory.LIGHT];
return this.displayCategory || [DisplayCategory.LIGHT];
}
setDescription(description: string): void {
@ -49,10 +50,18 @@ 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,
isDeferred = false
): AlexaStatusMessage {
return new AlexaStatusMessage(
correlationToken,

View file

@ -3,3 +3,4 @@ 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';