Add support for sending Error Responses
This commit is contained in:
parent
61b032c889
commit
4af45ff726
9 changed files with 396 additions and 19 deletions
125
dist/AlexaErrorResponse.js
vendored
Normal file
125
dist/AlexaErrorResponse.js
vendored
Normal 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
4
dist/Device.js
vendored
|
|
@ -4,6 +4,7 @@ const AlexaInterface_1 = require("./AlexaInterface");
|
||||||
const DisplayCategory_1 = require("./DisplayCategory");
|
const DisplayCategory_1 = require("./DisplayCategory");
|
||||||
const events_1 = require("events");
|
const events_1 = require("events");
|
||||||
const AlexaStatusMessage_1 = require("./AlexaStatusMessage");
|
const AlexaStatusMessage_1 = require("./AlexaStatusMessage");
|
||||||
|
const AlexaErrorResponse_1 = require("./AlexaErrorResponse");
|
||||||
class Device extends events_1.EventEmitter {
|
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, description = "Alexa to Node.js bridge", manufacturerName = "Alex2Node", manufacturer = "Alex2Node", model = "Alex2Node_v1.0.0") {
|
||||||
super();
|
super();
|
||||||
|
|
@ -40,6 +41,9 @@ class Device extends events_1.EventEmitter {
|
||||||
getDescription() {
|
getDescription() {
|
||||||
return this.description;
|
return this.description;
|
||||||
}
|
}
|
||||||
|
getErrorMessage(correlationToken) {
|
||||||
|
return new AlexaErrorResponse_1.AlexaErrorResponse(correlationToken, this.rootTopic, this.endpointId, this.mqttClient);
|
||||||
|
}
|
||||||
getStatusMessage(correlationToken, isResponse = false, isDeferred = false) {
|
getStatusMessage(correlationToken, isResponse = false, isDeferred = false) {
|
||||||
return new AlexaStatusMessage_1.AlexaStatusMessage(correlationToken, this.rootTopic, this.endpointId, this.mqttClient, isResponse, isDeferred);
|
return new AlexaStatusMessage_1.AlexaStatusMessage(correlationToken, this.rootTopic, this.endpointId, this.mqttClient, isResponse, isDeferred);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
dist/index.js
vendored
5
dist/index.js
vendored
|
|
@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
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");
|
var Alex2Node_1 = require("./Alex2Node");
|
||||||
Object.defineProperty(exports, "Alex2MQTT", { enumerable: true, get: function () { return __importDefault(Alex2Node_1).default; } });
|
Object.defineProperty(exports, "Alex2MQTT", { enumerable: true, get: function () { return __importDefault(Alex2Node_1).default; } });
|
||||||
var AlexaInterface_1 = require("./AlexaInterface");
|
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, "EndpointHealth", { enumerable: true, get: function () { return AlexaStatusMessage_1.EndpointHealth; } });
|
||||||
Object.defineProperty(exports, "TemperatureSensorScale", { enumerable: true, get: function () { return AlexaStatusMessage_1.TemperatureSensorScale; } });
|
Object.defineProperty(exports, "TemperatureSensorScale", { enumerable: true, get: function () { return AlexaStatusMessage_1.TemperatureSensorScale; } });
|
||||||
Object.defineProperty(exports, "ThermostatMode", { enumerable: true, get: function () { return AlexaStatusMessage_1.ThermostatMode; } });
|
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; } });
|
||||||
|
|
|
||||||
80
examples/ExamplePowerControllerWithErrors.js
Normal file
80
examples/ExamplePowerControllerWithErrors.js
Normal 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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -45,11 +45,11 @@ console.log(thermostat.getName());
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const deviceData = {
|
const deviceData = {
|
||||||
lowerSetpoint: 20,
|
lowerSetpoint: 50,
|
||||||
upperSetpoint: 20,
|
upperSetpoint: 70,
|
||||||
targetSetpoint: 20,
|
targetSetpoint: 60,
|
||||||
current: 20,
|
current: 69,
|
||||||
thermostatMode: ThermostatMode.AUTO,
|
thermostatMode: ThermostatMode.HEAT,
|
||||||
};
|
};
|
||||||
thermostat.on("ReportState", (payload) => {
|
thermostat.on("ReportState", (payload) => {
|
||||||
// console.log("ReportState received!", payload);
|
// console.log("ReportState received!", payload);
|
||||||
|
|
@ -101,17 +101,17 @@ thermostat.on("Event", (directive, interfaceType) => {
|
||||||
if (name == "SetTargetTemperature") {
|
if (name == "SetTargetTemperature") {
|
||||||
console.log({ name: directive.payload });
|
console.log({ name: directive.payload });
|
||||||
if (directive.payload.upperSetpoint) {
|
if (directive.payload.upperSetpoint) {
|
||||||
deviceData.upperSetpoint = praseTempValue(
|
deviceData.upperSetpoint = parseTempValue(
|
||||||
directive.payload.upperSetpoint
|
directive.payload.upperSetpoint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (directive.payload.lowerSetpoint) {
|
if (directive.payload.lowerSetpoint) {
|
||||||
deviceData.lowerSetpoint = praseTempValue(
|
deviceData.lowerSetpoint = parseTempValue(
|
||||||
directive.payload.lowerSetpoint
|
directive.payload.lowerSetpoint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (directive.payload.targetSetpoint) {
|
if (directive.payload.targetSetpoint) {
|
||||||
deviceData.targetSetpoint = praseTempValue(
|
deviceData.targetSetpoint = parseTempValue(
|
||||||
directive.payload.targetSetpoint
|
directive.payload.targetSetpoint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +128,7 @@ thermostat.on("Event", (directive, interfaceType) => {
|
||||||
status
|
status
|
||||||
.addHealthProp("OK") // Device is healthy and reachable
|
.addHealthProp("OK") // Device is healthy and reachable
|
||||||
.addTemperatureSensorProp(
|
.addTemperatureSensorProp(
|
||||||
TemperatureSensorScale.CELSIUS,
|
TemperatureSensorScale.FAHRENHEIT,
|
||||||
deviceData.current
|
deviceData.current
|
||||||
) // Report current temperature
|
) // Report current temperature
|
||||||
.addThermostatModeProp(deviceData.thermostatMode); // Report current thermostat mode
|
.addThermostatModeProp(deviceData.thermostatMode); // Report current thermostat mode
|
||||||
|
|
@ -136,18 +136,18 @@ thermostat.on("Event", (directive, interfaceType) => {
|
||||||
status = status
|
status = status
|
||||||
.addThermostatControllerProp(
|
.addThermostatControllerProp(
|
||||||
"lowerSetpoint",
|
"lowerSetpoint",
|
||||||
TemperatureSensorScale.CELSIUS,
|
TemperatureSensorScale.FAHRENHEIT,
|
||||||
deviceData.lowerSetpoint
|
deviceData.lowerSetpoint
|
||||||
) // Lower bound of temperature range
|
) // Lower bound of temperature range
|
||||||
.addThermostatControllerProp(
|
.addThermostatControllerProp(
|
||||||
"upperSetpoint",
|
"upperSetpoint",
|
||||||
TemperatureSensorScale.CELSIUS,
|
TemperatureSensorScale.FAHRENHEIT,
|
||||||
deviceData.upperSetpoint
|
deviceData.upperSetpoint
|
||||||
); // Upper bound of temperature range
|
); // Upper bound of temperature range
|
||||||
} else {
|
} else {
|
||||||
status = status.addThermostatControllerProp(
|
status = status.addThermostatControllerProp(
|
||||||
"targetSetpoint",
|
"targetSetpoint",
|
||||||
TemperatureSensorScale.CELSIUS,
|
TemperatureSensorScale.FAHRENHEIT,
|
||||||
deviceData.targetSetpoint
|
deviceData.targetSetpoint
|
||||||
); // Lower bound of temperature range
|
); // Lower bound of temperature range
|
||||||
}
|
}
|
||||||
|
|
@ -155,6 +155,13 @@ thermostat.on("Event", (directive, interfaceType) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function praseTempValue(data) {
|
function parseTempValue(data) {
|
||||||
return data.value;
|
console.log(data);
|
||||||
|
|
||||||
|
if (data.scale === "FAHRENHEIT") {
|
||||||
|
return data.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fahrenheit = (data.value * 9) / 5 + 32;
|
||||||
|
return fahrenheit;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "alex2node",
|
"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.",
|
"description": "A Node.js library for creating Alexa-compatible devices using MQTT, based on Alex2MQTT.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|
|
||||||
148
src/AlexaErrorResponse.ts
Normal file
148
src/AlexaErrorResponse.ts
Normal 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}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import { DisplayCategory } from "./DisplayCategory";
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
import { MqttClient } from "mqtt";
|
import { MqttClient } from "mqtt";
|
||||||
import { AlexaStatusMessage } from "./AlexaStatusMessage";
|
import { AlexaStatusMessage } from "./AlexaStatusMessage";
|
||||||
|
import { AlexaErrorResponse } from "./AlexaErrorResponse";
|
||||||
|
|
||||||
class Device extends EventEmitter {
|
class Device extends EventEmitter {
|
||||||
protected softwareVersion: string = "1.0.0";
|
protected softwareVersion: string = "1.0.0";
|
||||||
|
|
@ -39,7 +40,7 @@ class Device extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDisplayCategory(): Array<DisplayCategory> {
|
getDisplayCategory(): Array<DisplayCategory> {
|
||||||
return this.displayCategory ||[DisplayCategory.LIGHT];
|
return this.displayCategory || [DisplayCategory.LIGHT];
|
||||||
}
|
}
|
||||||
|
|
||||||
setDescription(description: string): void {
|
setDescription(description: string): void {
|
||||||
|
|
@ -49,10 +50,18 @@ class Device extends EventEmitter {
|
||||||
getDescription(): string {
|
getDescription(): string {
|
||||||
return this.description;
|
return this.description;
|
||||||
}
|
}
|
||||||
|
getErrorMessage(correlationToken: string): AlexaErrorResponse {
|
||||||
|
return new AlexaErrorResponse(
|
||||||
|
correlationToken,
|
||||||
|
this.rootTopic,
|
||||||
|
this.endpointId,
|
||||||
|
this.mqttClient
|
||||||
|
);
|
||||||
|
}
|
||||||
getStatusMessage(
|
getStatusMessage(
|
||||||
correlationToken: string,
|
correlationToken: string,
|
||||||
isResponse = false,
|
isResponse = false,
|
||||||
isDeferred = false,
|
isDeferred = false
|
||||||
): AlexaStatusMessage {
|
): AlexaStatusMessage {
|
||||||
return new AlexaStatusMessage(
|
return new AlexaStatusMessage(
|
||||||
correlationToken,
|
correlationToken,
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@ export { AlexaInterfaceType } from './AlexaInterface';
|
||||||
export { ActionMapping, AlexaActions } from './ActionMapping';
|
export { ActionMapping, AlexaActions } from './ActionMapping';
|
||||||
export {DisplayCategory} from "./DisplayCategory";
|
export {DisplayCategory} from "./DisplayCategory";
|
||||||
export { PowerController,EndpointHealth,TemperatureSensorScale,ThermostatMode } from './AlexaStatusMessage';
|
export { PowerController,EndpointHealth,TemperatureSensorScale,ThermostatMode } from './AlexaStatusMessage';
|
||||||
|
export { AlexaErrorType,AlexaErrorResponse} from './AlexaErrorResponse';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue