// A fan that says no: the error types of Alexa as answers. A handler throws an AlexaError of AlexaErrors, or // answers with ctx.error(). Anything else a handler throws is answered with INTERNAL_ERROR, and a directive without // a handler with INVALID_DIRECTIVE. // // ALEX2MQTT_USERNAME=... ALEX2MQTT_PASSWORD=... ALEX2MQTT_ROOT_TOPIC=... node examples/errors.js // // "alex2node" is the installed package; inside this checkout it resolves to the built dist/. const { Alex2MQTT, AlexaErrors, PowerController, RangeController, text } = require("alex2node"); const missing = ["ALEX2MQTT_USERNAME", "ALEX2MQTT_PASSWORD", "ALEX2MQTT_ROOT_TOPIC"].filter((name) => !process.env[name]); if (missing.length > 0) { console.error(`${missing.join(", ")} not set. Set the MQTT user name, the password and the root topic of your Alex2MQTT account.`); process.exit(1); } const { ALEX2MQTT_USERNAME, ALEX2MQTT_PASSWORD, ALEX2MQTT_ROOT_TOPIC } = process.env; const bridge = new Alex2MQTT(ALEX2MQTT_USERNAME, ALEX2MQTT_PASSWORD, ALEX2MQTT_ROOT_TOPIC); bridge.on("connect", () => console.log("connected, discover the devices in the Alexa app")); bridge.on("error", (err) => console.error("bridge:", err.message)); const SPEEDS = { min: 1, max: 3 }; const fan = { on: false, speed: 1 }; const device = bridge.addDevice({ endpointId: "attic-fan", name: "Attic Fan", categories: ["FAN"] }); const power = device.add(PowerController); const speed = device.add(RangeController, { instance: "Fan.Speed", friendlyNames: [text("Speed", "en-US")], range: { min: SPEEDS.min, max: SPEEDS.max, precision: 1 }, }); device.state((s) => s .set(power, "powerState", fan.on ? "ON" : "OFF") .set(speed, "rangeValue", fan.speed) .health("OK")); power.on("TurnOn", (ctx) => { fan.on = true; return ctx.respond(); }); power.on("TurnOff", (ctx) => { fan.on = false; return ctx.respond(); }); speed.on("SetRangeValue", (ctx) => { const { rangeValue } = ctx.payload; // Without a throw: the type and the message if (!fan.on) return ctx.error("NOT_IN_OPERATION", "The fan is off"); // With a throw: every type has a helper, and the helper of a type with fields of its own takes them if (!Number.isInteger(rangeValue) || rangeValue < SPEEDS.min || rangeValue > SPEEDS.max) { throw AlexaErrors.valueOutOfRange( `The fan has the speeds ${SPEEDS.min} to ${SPEEDS.max}`, { minimumValue: SPEEDS.min, maximumValue: SPEEDS.max } ); } fan.speed = rangeValue; return ctx.respond(); }); // AdjustRangeValue has no handler: the bridge answers it with INVALID_DIRECTIVE bridge.connect();