Alex2Node/dist/esm/messages/errors.js
David 46fa06728c messages: pure builders for every event the bridge sends
src/messages builds Response, StateReport, DeferredResponse, ErrorResponse, ChangeReport and the scene, doorbell
and simple events from plain values; messageId and times are parameters, so a test compares whole objects.
StateBuilder collects properties checked by the descriptors, AlexaError and AlexaErrors carry the payload fields
of an error type. AlexaStatusMessage and AlexaErrorResponse move to src/compat and are written on the builders.
On the wire, against 1.5.2: a DeferredResponse has no context key, a ChangeReport has no correlationToken, the
namespace of an ErrorResponse follows its type, and a ChangeReport without a changed property is not published:
send() resolves "" and the bridge reports an error that names the endpoint.
143 tests pass (108 before).

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

69 lines
3.5 KiB
JavaScript

// The errors a device answers a directive with (alexa-errorresponse.html). A helper for each type whose payload
// has fields of its own, so that the fields cannot be misspelt.
import { ERROR_TYPES } from "../registry/catalog.js";
/**
* The namespace an ErrorResponse of this type goes under: the one the table of error types lists it with
* (THERMOSTAT_IS_OFF: Alexa.ThermostatController, OBSTACLE_DETECTED: Alexa.Safety), Alexa for a type the table
* does not have.
*/
export function errorNamespace(type) {
return Object.prototype.hasOwnProperty.call(ERROR_TYPES, type) ? ERROR_TYPES[type] : "Alexa";
}
/** Thrown by a directive handler, or built to be sent: the directive is answered with this ErrorResponse. */
export class AlexaError extends Error {
constructor(type, message, extra = {}, namespace = errorNamespace(type)) {
super(`${type}: ${message}`);
this.name = "AlexaError";
this.type = type;
this.alexaMessage = message;
this.extra = extra;
this.namespace = namespace;
}
}
// A field that was not given is left out of the payload
function given(fields) {
return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
}
export const AlexaErrors = {
/** An error of any type. */
of(type, message, extra = {}) {
return new AlexaError(type, message, extra);
},
/** The value is outside what the endpoint takes. For a temperature: temperatureOutOfRange(). */
valueOutOfRange(message, validRange) {
return new AlexaError("VALUE_OUT_OF_RANGE", message, { validRange });
},
temperatureOutOfRange(message, validRange) {
return new AlexaError("TEMPERATURE_VALUE_OUT_OF_RANGE", message, { validRange });
},
/** A light showing a color asked for a color temperature: "COLOR". */
notSupportedInCurrentMode(message, currentDeviceMode) {
return new AlexaError("NOT_SUPPORTED_IN_CURRENT_MODE", message, { currentDeviceMode });
},
/** percentageState: what is left of the battery, 0 to 100. */
endpointLowPower(message, percentageState) {
return new AlexaError("ENDPOINT_LOW_POWER", message, given({ percentageState }));
},
endpointControlUnavailable(message, reason) {
return new AlexaError("ENDPOINT_CONTROL_UNAVAILABLE", message, { reason });
},
notSupportedWithCurrentBatteryChargeState(message, currentChargeState, currentChargeLevelInPercentage) {
return new AlexaError("NOT_SUPPORTED_WITH_CURRENT_BATTERY_CHARGE_STATE", message, given({ currentChargeState, currentChargeLevelInPercentage }));
},
/** What has to be refilled. */
insufficientResource(message, resourceType) {
return new AlexaError("INSUFFICIENT_RESOURCE", message, { resourceType });
},
/** What the user has to do first. */
maintenanceRequired(message, maintenanceAction) {
return new AlexaError("MAINTENANCE_REQUIRED", message, given({ maintenanceAction }));
},
/** Goes under Alexa.ThermostatController. minimumTemperatureDelta: how far apart the setpoints have to be. */
setpointsTooClose(message, minimumTemperatureDelta) {
return new AlexaError("REQUESTED_SETPOINTS_TOO_CLOSE", message, given({ minimumTemperatureDelta }));
},
/** Goes under Alexa.SecurityPanelController. With the endpoints listed, the user can bypass them by voice. */
bypassNeeded(message, endpointsNeedingBypass) {
return new AlexaError("BYPASS_NEEDED", message, given({ endpointsNeedingBypass }));
},
};