// Import the library ("alex2node" is the installed package; inside this checkout it resolves to the built dist/ via package.json "exports") const { Alex2MQTT, AlexaInterfaceType, PowerController, EndpointHealth } = require("alex2node"); 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 let brightness = 100; // 0-100, what Alexa.BrightnessController reports // 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 alex2NodeClient.on("error", (e) => console.error("broker:", e.message)); // Log broker errors (on 1.5.0 this listener is also what keeps an outage from killing the process) // Define the device name (dimmable living room light) const deviceName = "Living Room Light"; // Register the device with a unique endpoint ID (unique per root topic, e.g. "living-room-light-1") const livingRoomLight = alex2NodeClient.registerDevice(deviceName, "living-room-light-1"); // Declare every capability the device reports: on/off AND brightness (a property Alexa has not been told about is ignored) livingRoomLight.addCapability(AlexaInterfaceType.POWER_CONTROLLER); livingRoomLight.addCapability(AlexaInterfaceType.BRIGHTNESS_CONTROLLER); // Log the device name to verify registration console.log(livingRoomLight.getName()); // Clamp a brightness to Alexa's 0-100 range const clamp = (value) => Math.max(0, Math.min(100, Math.round(value))); /** * Handle Alexa's ReportState directive. * This occurs when Alexa queries the current state of the device (e.g., during routines or device status checks). */ livingRoomLight.on("ReportState", (payload) => { console.log("ReportState received!", payload); const { correlationToken } = payload.header; const status = livingRoomLight.getStatusMessage(correlationToken); status .addHealthProp(EndpointHealth.OK) // Device is healthy .addPowerControllerProp(outputState) // Report the current power state (ON/OFF) .addBrightnessControllerProp(brightness); // Report the current brightness (0-100) status.send(); // Send the state report back to Alexa }); /** * Handle incoming control directives (TurnOn / TurnOff, SetBrightness / AdjustBrightness). * These directives come from Alexa when a user issues a command. */ livingRoomLight.on("Event", (directive, interfaceType) => { console.log("Event received", { directive, interfaceType }); const name = directive.header.name; const token = directive.header.correlationToken; if (interfaceType === AlexaInterfaceType.POWER_CONTROLLER) { // Update the internal state based on the command (TurnOn / TurnOff) if (name === "TurnOn") { outputState = PowerController.ON; console.log("Turning ON the Living Room Light"); } else if (name === "TurnOff") { outputState = PowerController.OFF; console.log("Turning OFF the Living Room Light"); } } else if (interfaceType === AlexaInterfaceType.BRIGHTNESS_CONTROLLER) { // "Set the light to 40 percent" -> SetBrightness { brightness }; "dim the light" -> AdjustBrightness { brightnessDelta } if (name === "SetBrightness") { brightness = clamp(directive.payload.brightness); } else if (name === "AdjustBrightness") { brightness = clamp(brightness + directive.payload.brightnessDelta); } outputState = brightness > 0 ? PowerController.ON : PowerController.OFF; // a dimmer at 0 is off console.log(`Living Room Light brightness is now ${brightness}%`); } else { return; // not one of ours } // Respond to the directive with the full device status (both properties, whichever one changed) const status = livingRoomLight.getStatusMessage(token, true); status .addHealthProp(EndpointHealth.OK) .addPowerControllerProp(outputState) .addBrightnessControllerProp(brightness) .send(); });