// 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 })); }, };