1.5.2: send() never rejects (status, error, scene, change report: resolves the topic or "" and reports the failure through the bridge's error event when listened to - 1.5.1 rejected, and an un-caught .send() killed the host on any broker hiccup); types ship (declaration: true, dist/*.d.ts tracked; addSupportedModes takes {value, modeResources}, ActionMapping payload optional, addHealthProp accepts EndpointHealth or the string); disconnect()/connect() re-binds devices to the new client; registerDevice returns the existing device on a duplicate endpointId (warning via the log hook, console.warn without one); ThermostatController discovery lists targetSetpoint and drops adaptiveRecoveryStatus; the UNSUPORTED INTERFACE TYPE stderr spam goes through the log hook. Examples: require("alex2node"), an error listener in each, EndpointHealth.OK, neutral endpoint ids, BlindControl reads correlationToken from the header, the thermostat reports Fahrenheit as Fahrenheit, ExamplePowerController is power-only again + new ExamplePowerControllerWithBrightness. readme (install from Forgejo, 1.5.2 changelog, table syntax), LICENSE (MIT); tests for the non-rejecting send, reconnect, duplicate, thermostat discovery and the shipped declaration signatures (8/8 on an in-process broker).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
David 2026-09-28 04:58:39 +00:00
parent 717af632de
commit 275f00f8a7
35 changed files with 1011 additions and 176 deletions

166
readme.md
View file

@ -13,6 +13,27 @@ For more details on how to configure Alex2MQTT, visit [Alex2MQTT Documentation](
- Easily configure devices and their capabilities via simple API.
## What is new in 1.5.2
- `send()` (status, error and scene responses, change reports) never rejects any more: it resolves with the topic, or `""` when the publish failed, and the error goes to the bridge's `error` event when a listener is attached. In 1.5.1 a failing publish rejected the promise, and an un-caught `.send()` (every example, the Quick Start) then killed the host process.
- **Types ship.** `dist/index.d.ts` (and the other declarations) are generated and tracked, so TypeScript users get
types. `addSupportedModes` accepts the `{ value, modeResources }` objects a ModeController needs, `ActionMapping`'s
payload argument is optional, `addHealthProp` takes `EndpointHealth.OK` (or the plain string).
- **`disconnect()` then `connect()` works.** Devices registered before a `disconnect()` publish through the new
connection (1.5.1 left them bound to the closed client: discoverable, but they never answered a directive).
- **Duplicate endpointIds.** `registerDevice()` with an endpointId that is already registered returns the existing
device (with a warning: `console.warn`, or the log hook when one is set) instead of adding a second one that never
received a directive. EndpointIds are unique per root topic.
- **Thermostat discovery** lists `targetSetpoint` (with `lowerSetpoint`, `upperSetpoint`, `thermostatMode`) and no
longer advertises `adaptiveRecoveryStatus`, which nothing reported. A consumer (insteonDashboard) re-announces its
thermostats once on the next discovery because the payload changed - harmless.
- Discovery no longer prints `UNSUPORTED INTERFACE TYPE` to stderr for every interface the library has no property
list for; the bridge notes it through the log hook (debug) instead.
- Examples: `require("alex2node")`, an `error` listener in every one, `EndpointHealth.OK`, neutral endpoint ids;
BlindControl's ReportState reads the correlationToken from the header, the thermostat reports its Fahrenheit data
as Fahrenheit, ExamplePowerController is the plain on/off light again and the new
ExamplePowerControllerWithBrightness declares the BrightnessController it reports. A LICENSE file (MIT).
## What is new in 1.5.1
- **A broker outage no longer crashes your process.** 1.4.0 emitted `error` unconditionally, and Node terminates a
@ -41,7 +62,7 @@ const bridge = new Alex2MQTT(user, pass, rootTopic, false, { host: process.env.M
bridge.on("error", (e) => console.warn("broker:", e.message)); // optional - without it the error is swallowed, never thrown
bridge.on("connect", () => console.log("connected"));
bridge.connect();
const lamp = bridge.registerDevice("Dining Room Light", "5020AA", DisplayCategory.LIGHT);
const lamp = bridge.registerDevice("Dining Room Light", "dining-room-light-1", DisplayCategory.LIGHT);
lamp.addCapability(AlexaInterfaceType.POWER_CONTROLLER, { proactivelyReported: true });
lamp.on("Event", (directive) => { /* ... act, then: */ lamp.getStatusMessage(directive.header.correlationToken, true).addPowerControllerProp(PowerController.ON).send(); });
// later, when the light changes locally:
@ -52,12 +73,20 @@ lamp.getChangeReport("PHYSICAL_INTERACTION").addPowerControllerProp(PowerControl
### Installation
You can install **Alex2Node** via npm:
npm has **1.5.0**. Everything since - 1.5.1 (ChangeReport, configurable broker host, error events) and 1.5.2 (types,
the reconnect fix; see "What is new") - installs straight from the Forgejo repository, so nothing waits on an npm
publish:
```bash
npm install alex2node
npm install alex2node # 1.5.0 from npm
npm install git+https://git.stormysdream.club/apps/Alex2Node.git # latest main from Forgejo
npm install git+https://git.stormysdream.club/apps/Alex2Node.git#<commit> # pinned to one commit
```
The repository is public, so the `https` form needs no account. `#<commit>` (or `#<tag>`, when one exists) pins a
revision; either way npm records the resolved commit in `package-lock.json`, so `npm ci` reproduces it. `npm update
alex2node` re-resolves a branch ref such as `#main` to its latest commit; a commit or tag pin stays put.
Source: https://git.stormysdream.club/apps/Alex2Node
Then, import it into your JavaScript or TypeScript file:
@ -83,7 +112,7 @@ First, create an instance of the Alexa-to-MQTT client and initialize it with you
```js
require("dotenv").config(); // Load environment variables from .env file
const { Alex2MQTT, AlexaInterfaceType, PowerController } = require("alex2node");
const { Alex2MQTT, AlexaInterfaceType, PowerController, EndpointHealth } = require("alex2node");
const username = process.env.MQTT_USERNAME;
const password = process.env.MQTT_PASSWORD;
@ -91,13 +120,14 @@ const rootTopic = process.env.MQTT_ROOT_TOPIC;
const alex2NodeClient = new Alex2MQTT(username, password, rootTopic, false); // 1.5.1: add { host } as a fifth argument for another broker
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
```
Once initialized, you can begin to add virtual devices. In this example, we will add a PowerController for a light:
```js
const deviceName = "Bedroom Light";
const bedroomLight = alex2NodeClient.registerDevice(deviceName, "endpoint1");
const bedroomLight = alex2NodeClient.registerDevice(deviceName, "bedroom-light-1"); // the endpointId: unique per root topic
// Add the PowerController capability
bedroomLight.addCapability(AlexaInterfaceType.POWER_CONTROLLER);
@ -105,10 +135,10 @@ bedroomLight.addCapability(AlexaInterfaceType.POWER_CONTROLLER);
// Handle Alexa's ReportState directive to report the current state
let outputState = PowerController.OFF;
bedroomLight.on("ReportState", (payload) => {
const { correlationToken } = payload;
const { correlationToken } = payload.header; // the whole directive is passed; Alexa needs the token echoed
const status = bedroomLight.getStatusMessage(correlationToken);
status
.addHealthProp("OK")
.addHealthProp(EndpointHealth.OK)
.addPowerControllerProp(outputState)
.send();
});
@ -129,7 +159,7 @@ bedroomLight.on("Event", (directive, interfaceType) => {
const status = bedroomLight.getStatusMessage(token, true);
status
.addHealthProp("OK")
.addHealthProp(EndpointHealth.OK)
.addPowerControllerProp(outputState)
.send();
}
@ -141,66 +171,66 @@ bedroomLight.on("Event", (directive, interfaceType) => {
## Interface Types
| Alexa Interface Type | Status |
|-----------------------------------------------------|-------------|
| AlexaInterfaceType::ENDPOINT_HEALTH | Fully Supported |
| AlexaInterfaceType::POWER_CONTROLLER | Fully Supported |
| AlexaInterfaceType::BRIGHTNESS_CONTROLLER | Fully Supported |
| AlexaInterfaceType::TOGGLE_CONTROLLER | Fully Supported |
| AlexaInterfaceType::TEMPERATURE_SENSOR | Fully Supported |
| AlexaInterfaceType::COLOR_TEMPERATURE_CONTROLLER | Fully Supported |
| AlexaInterfaceType::AUTOMATION_MANAGEMENT | Supported* |
| AlexaInterfaceType::CHANNEL_CONTROLLER | Supported* |
| AlexaInterfaceType::COLOR_CONTROLLER | Supported* |
| AlexaInterfaceType::CONTACT_SENSOR | Supported* |
| AlexaInterfaceType::APPLICATION_STATE_REPORTER | Supported* |
| AlexaInterfaceType::AUDIO_PLAY_QUEUE | Supported* |
| AlexaInterfaceType::AUTHORIZATION_CONTROLLER | Supported* |
| AlexaInterfaceType::AUTOMOTIVE_VEHICLE_DATA | Supported* |
| AlexaInterfaceType::CAMERA_LIVE_VIEW_CONTROLLER | Supported* |
| AlexaInterfaceType::CAMERA_STREAM_CONTROLLER | Supported* |
| AlexaInterfaceType::COMMISSIONABLE | Supported* |
| AlexaInterfaceType::CONSENT_MANAGEMENT_CONSENT_REQUIRED_REPORTER | Supported* |
| AlexaInterfaceType::COOKING | Supported* |
| AlexaInterfaceType::DATA_CONTROLLER | Supported* |
| AlexaInterfaceType::DEVICE_USAGE_ESTIMATION | Supported* |
| AlexaInterfaceType::DEVICE_USAGE_METER | Supported* |
| AlexaInterfaceType::DOORBELL_EVENT_SOURCE | Supported* |
| AlexaInterfaceType::EQUALIZER_CONTROLLER | Supported* |
| AlexaInterfaceType::INPUT_CONTROLLER | Supported* |
| AlexaInterfaceType::INVENTORY_LEVEL_SENSOR | Supported* |
| AlexaInterfaceType::INVENTORY_LEVEL_USAGE_SENSOR | Supported* |
| AlexaInterfaceType::INVENTORY_USAGE_SENSOR | Supported* |
| AlexaInterfaceType::KEYPAD_CONTROLLER | Supported* |
| AlexaInterfaceType::LAUNCHER | Supported* |
| AlexaInterfaceType::LOCK_CONTROLLER | Supported* |
| AlexaInterfaceType::MEDIA_PLAYBACK | Supported* |
| AlexaInterfaceType::MEDIA_SEARCH | Supported* |
| AlexaInterfaceType::MODE_CONTROLLER | Supported* |
| AlexaInterfaceType::MOTION_SENSOR | Supported* |
| AlexaInterfaceType::PERCENTAGE_CONTROLLER | Supported* |
| AlexaInterfaceType::PLAYBACK_CONTROLLER | Supported* |
| AlexaInterfaceType::PLAYBACK_STATE_REPORTER | Supported* |
| AlexaInterfaceType::PROACTIVE_NOTIFICATION_SOURCE | Supported* |
| AlexaInterfaceType::RANGE_CONTROLLER | Supported* |
| AlexaInterfaceType::RECORD_CONTROLLER | Supported* |
| AlexaInterfaceType::REMOTE_VIDEO_PLAYER | Supported* |
| AlexaInterfaceType::RTC_SESSION_CONTROLLER | Supported* |
| AlexaInterfaceType::SCENE_CONTROLLER | Supported* |
| AlexaInterfaceType::SECURITY_PANEL_CONTROLLER | Supported* |
| AlexaInterfaceType::SEEK_CONTROLLER | Supported* |
| AlexaInterfaceType::SIMPLE_EVENT_SOURCE | Supported* |
| AlexaInterfaceType::SMART_VISION_OBJECT_DETECTION_SENSOR | Supported* |
| AlexaInterfaceType::SMART_VISION_SNAPSHOT_PROVIDER | Supported* |
| AlexaInterfaceType::SPEAKER | Supported* |
| AlexaInterfaceType::STEP_SPEAKER | Supported* |
| AlexaInterfaceType::THERMOSTAT_CONTROLLER | Supported* |
| AlexaInterfaceType::THERMOSTAT_CONTROLLER_CONFIGURATION | Supported* |
| AlexaInterfaceType::THERMOSTAT_CONTROLLER_HVAC_COMPONENTS | Supported* |
| AlexaInterfaceType::THERMOSTAT_CONTROLLER_SCHEDULE | Supported* |
| AlexaInterfaceType::TIME_HOLD_CONTROLLER | Supported* |
| AlexaInterfaceType::UI_CONTROLLER | Supported* |
| AlexaInterfaceType::USER_PREFERENCE | Supported* |
| AlexaInterfaceType::VIDEO_RECORDER | Supported* |
| AlexaInterfaceType::WAKE_ON_LAN_CONTROLLER | Supported* |
| AlexaInterfaceType.ENDPOINT_HEALTH | Fully Supported |
| AlexaInterfaceType.POWER_CONTROLLER | Fully Supported |
| AlexaInterfaceType.BRIGHTNESS_CONTROLLER | Fully Supported |
| AlexaInterfaceType.TOGGLE_CONTROLLER | Fully Supported |
| AlexaInterfaceType.TEMPERATURE_SENSOR | Fully Supported |
| AlexaInterfaceType.COLOR_TEMPERATURE_CONTROLLER | Fully Supported |
| AlexaInterfaceType.AUTOMATION_MANAGEMENT | Supported* |
| AlexaInterfaceType.CHANNEL_CONTROLLER | Supported* |
| AlexaInterfaceType.COLOR_CONTROLLER | Supported* |
| AlexaInterfaceType.CONTACT_SENSOR | Supported* |
| AlexaInterfaceType.APPLICATION_STATE_REPORTER | Supported* |
| AlexaInterfaceType.AUDIO_PLAY_QUEUE | Supported* |
| AlexaInterfaceType.AUTHORIZATION_CONTROLLER | Supported* |
| AlexaInterfaceType.AUTOMOTIVE_VEHICLE_DATA | Supported* |
| AlexaInterfaceType.CAMERA_LIVE_VIEW_CONTROLLER | Supported* |
| AlexaInterfaceType.CAMERA_STREAM_CONTROLLER | Supported* |
| AlexaInterfaceType.COMMISSIONABLE | Supported* |
| AlexaInterfaceType.CONSENT_MANAGEMENT_CONSENT_REQUIRED_REPORTER | Supported* |
| AlexaInterfaceType.COOKING | Supported* |
| AlexaInterfaceType.DATA_CONTROLLER | Supported* |
| AlexaInterfaceType.DEVICE_USAGE_ESTIMATION | Supported* |
| AlexaInterfaceType.DEVICE_USAGE_METER | Supported* |
| AlexaInterfaceType.DOORBELL_EVENT_SOURCE | Supported* |
| AlexaInterfaceType.EQUALIZER_CONTROLLER | Supported* |
| AlexaInterfaceType.INPUT_CONTROLLER | Supported* |
| AlexaInterfaceType.INVENTORY_LEVEL_SENSOR | Supported* |
| AlexaInterfaceType.INVENTORY_LEVEL_USAGE_SENSOR | Supported* |
| AlexaInterfaceType.INVENTORY_USAGE_SENSOR | Supported* |
| AlexaInterfaceType.KEYPAD_CONTROLLER | Supported* |
| AlexaInterfaceType.LAUNCHER | Supported* |
| AlexaInterfaceType.LOCK_CONTROLLER | Supported* |
| AlexaInterfaceType.MEDIA_PLAYBACK | Supported* |
| AlexaInterfaceType.MEDIA_SEARCH | Supported* |
| AlexaInterfaceType.MODE_CONTROLLER | Supported* |
| AlexaInterfaceType.MOTION_SENSOR | Supported* |
| AlexaInterfaceType.PERCENTAGE_CONTROLLER | Supported* |
| AlexaInterfaceType.PLAYBACK_CONTROLLER | Supported* |
| AlexaInterfaceType.PLAYBACK_STATE_REPORTER | Supported* |
| AlexaInterfaceType.PROACTIVE_NOTIFICATION_SOURCE | Supported* |
| AlexaInterfaceType.RANGE_CONTROLLER | Supported* |
| AlexaInterfaceType.RECORD_CONTROLLER | Supported* |
| AlexaInterfaceType.REMOTE_VIDEO_PLAYER | Supported* |
| AlexaInterfaceType.RTC_SESSION_CONTROLLER | Supported* |
| AlexaInterfaceType.SCENE_CONTROLLER | Supported* |
| AlexaInterfaceType.SECURITY_PANEL_CONTROLLER | Supported* |
| AlexaInterfaceType.SEEK_CONTROLLER | Supported* |
| AlexaInterfaceType.SIMPLE_EVENT_SOURCE | Supported* |
| AlexaInterfaceType.SMART_VISION_OBJECT_DETECTION_SENSOR | Supported* |
| AlexaInterfaceType.SMART_VISION_SNAPSHOT_PROVIDER | Supported* |
| AlexaInterfaceType.SPEAKER | Supported* |
| AlexaInterfaceType.STEP_SPEAKER | Supported* |
| AlexaInterfaceType.THERMOSTAT_CONTROLLER | Supported* |
| AlexaInterfaceType.THERMOSTAT_CONTROLLER_CONFIGURATION | Supported* |
| AlexaInterfaceType.THERMOSTAT_CONTROLLER_HVAC_COMPONENTS | Supported* |
| AlexaInterfaceType.THERMOSTAT_CONTROLLER_SCHEDULE | Supported* |
| AlexaInterfaceType.TIME_HOLD_CONTROLLER | Supported* |
| AlexaInterfaceType.UI_CONTROLLER | Supported* |
| AlexaInterfaceType.USER_PREFERENCE | Supported* |
| AlexaInterfaceType.VIDEO_RECORDER | Supported* |
| AlexaInterfaceType.WAKE_ON_LAN_CONTROLLER | Supported* |
(*Partial support or limited implementation advanced configuration is required)