Alex2Node/dist/cjs/messages/build.js
David 3a07c861b1 registry: SceneController events, DoorbellEventSource, SimpleEventSource, TimeHoldController, InventoryLevelSensor, WakeOnLANController
Six descriptors written from their pages replace the last stubs of tiers 1 and 2. A scene answers Activate and
Deactivate through ctx.respond() with ActivationStarted and DeactivationStarted, the time and the cause filled
in. device.raise(descriptor, name, payload) publishes DoorbellPress and the Event of a button on <root>/event
with the endpoint and a new messageId; it throws a MessageError for an interface or instance the device did
not declare, an event that answers a directive, a payload that does not fit and a message over 16000 bytes.
TurnOn of a device with WakeOnLANController is deferred without the warning.

On the wire: a doorbell has no properties object and proactivelyReported on the capability; SimpleEventSource
is version 1.0, InventoryLevelSensor and WakeOnLANController version 3 (1.5.2: 1). A scene declared without
options is announced as before. Alex2MQTT has no topic yet for the WakeUp event.
24 examples of the six pages are saved as fixtures. 267 tests pass, 241 before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 21:01:53 +00:00

138 lines
6.4 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sameProperty = exports.MessageError = void 0;
exports.response = response;
exports.stateReport = stateReport;
exports.deferredResponse = deferredResponse;
exports.errorResponse = errorResponse;
exports.changeReport = changeReport;
exports.sceneEvent = sceneEvent;
exports.proactiveEvent = proactiveEvent;
exports.doorbellPress = doorbellPress;
exports.simpleEvent = simpleEvent;
// Every message the bridge publishes, built from plain values. Nothing here reads the clock or makes an id unless
// the caller leaves messageId or a time out, so a test that passes both compares whole objects.
const crypto_1 = require("crypto");
const errors_js_1 = require("./errors.js");
const property_js_1 = require("./property.js");
/** A message that would be dropped on its way to Alexa, refused where it is built. */
class MessageError extends Error {
constructor(endpointId, problem) {
super(`${endpointId}: ${problem}`);
this.endpointId = endpointId;
this.problem = problem;
this.name = "MessageError";
}
}
exports.MessageError = MessageError;
// The fields in the order of the examples
function header(namespace, name, fields) {
return {
namespace,
name,
...(fields.instance ? { instance: fields.instance } : {}),
messageId: fields.messageId ?? (0, crypto_1.randomUUID)(),
...(fields.correlationToken !== undefined ? { correlationToken: fields.correlationToken } : {}),
payloadVersion: fields.payloadVersion ?? "3",
};
}
/**
* The answer to a directive (alexa-response.html, "Synchronous response"). An endpoint with nothing to report
* answers with an empty list of properties, not without a context (state-reporting-for-smart-home-addons.html,
* "Directive response example for a property that isn't retrievable").
*/
function response(fields) {
return {
event: {
header: header(fields.namespace ?? "Alexa", fields.name ?? "Response", fields),
endpoint: { endpointId: fields.endpointId },
payload: fields.payload ?? {},
},
context: { properties: [...(fields.context ?? [])] },
};
}
/** The answer to ReportState: every retrievable property of the endpoint. */
function stateReport(fields) {
return response({ ...fields, name: "StateReport", namespace: "Alexa", payload: {} });
}
/**
* "The directive arrived, the answer follows": no context, the state is in the Response that follows
* (alexa-response.html, "Deferred response example").
*/
function deferredResponse(fields) {
const { estimatedDeferralInSeconds } = fields;
return {
event: {
header: header("Alexa", "DeferredResponse", fields),
endpoint: { endpointId: fields.endpointId },
payload: estimatedDeferralInSeconds === undefined ? {} : { estimatedDeferralInSeconds },
},
};
}
/** The answer to a directive the endpoint could not follow (alexa-errorresponse.html). */
function errorResponse(fields) {
return {
event: {
header: header(fields.namespace ?? (0, errors_js_1.errorNamespace)(fields.type), "ErrorResponse", fields),
endpoint: { endpointId: fields.endpointId },
payload: { type: fields.type, message: fields.message, ...fields.extra },
},
};
}
/** The same property of the same instance, whatever its value. */
const sameProperty = (a, b) => a.namespace === b.namespace && a.name === b.name && (a.instance ?? "") === (b.instance ?? "");
exports.sameProperty = sameProperty;
/**
* A change of state nobody asked for. The header has no correlationToken (message-guide.html, "Header object").
* A property that changed is left out of the context: it is reported in one of the two (same page, "Context
* object"). Throws MessageError when nothing changed: Alex2MQTT drops such a report without a word.
*/
function changeReport(fields) {
const { endpointId, changed, context = [] } = fields;
if (changed.length === 0) {
throw new MessageError(endpointId, "a ChangeReport needs at least one property that changed, this one has none. "
+ "Add the changed property before unchanged(), or send no report when nothing changed");
}
return {
event: {
header: header("Alexa", "ChangeReport", { messageId: fields.messageId }),
endpoint: { endpointId },
payload: { change: { cause: { type: fields.cause ?? "PHYSICAL_INTERACTION" }, properties: [...changed] } },
},
context: { properties: context.filter((property) => !changed.some((other) => (0, exports.sameProperty)(property, other))) },
};
}
/** The answer to Activate and Deactivate of a scene (alexa-scenecontroller.html). */
function sceneEvent(fields) {
return {
event: {
header: header("Alexa.SceneController", fields.activated ? "ActivationStarted" : "DeactivationStarted", fields),
endpoint: { endpointId: fields.endpointId },
payload: { cause: { type: fields.cause ?? "VOICE_INTERACTION" }, timestamp: (0, property_js_1.isoTime)(fields.timestamp) },
},
context: {},
};
}
/** An event a device raises by itself, of any interface. The header has no correlationToken: nobody asked. */
function proactiveEvent(fields) {
const { namespace, name, instance, messageId, payloadVersion, payload } = fields;
return {
event: {
header: header(namespace, name, { instance, messageId, payloadVersion }),
endpoint: { endpointId: fields.endpointId },
payload,
},
};
}
/** Somebody rang (alexa-doorbelleventsource.html). */
function doorbellPress(fields) {
const { endpointId, messageId } = fields;
const payload = { cause: { type: fields.cause ?? "PHYSICAL_INTERACTION" }, timestamp: (0, property_js_1.isoTime)(fields.timestamp) };
return proactiveEvent({ endpointId, messageId, namespace: "Alexa.DoorbellEventSource", name: "DoorbellPress", payload });
}
/** An event of a button or a sensor that routines start on (alexa-simpleeventsource.html). */
function simpleEvent(fields) {
const { endpointId, messageId, instance } = fields;
const payload = { id: fields.id, timestamp: (0, property_js_1.isoTime)(fields.timestamp) };
return proactiveEvent({ endpointId, messageId, instance, namespace: "Alexa.SimpleEventSource", name: "Event", payloadVersion: "1.0", payload });
}