discovery: generate capability JSON from the registry

A capability is a descriptor plus what the endpoint declares (device/Capability.ts), and its discovery object is
generated from the two. The new API is bridge.addDevice({ endpointId, name, categories, ... }) and
device.add(PowerController, options): both throw a DeclarationError that names the endpoint, the interface and
the instance (device/validate.ts), and leave the bridge and the device as they were. AlexaInterface is the same
Capability with the 1.x methods on it; it, ActionMapping and the enums moved to src/compat/, Device to src/device/.

What a 1.x caller can observe:
- every endpoint ends with { type: "AlexaInterface", interface: "Alexa", version: "3" } (alexa-interface.html);
  new Alex2MQTT(..., { alexaInterface: false }) leaves it out
- the fields of a capability object come in the order of Amazon's examples; their content is unchanged
- addCapability() with a name that is not an interface throws (1.5.2 announced it with the version "UNKNOWN")
- ActionMapping takes the payload as an object; a JSON string is parsed (1.5.2 sent the string), any other throws
- what Alexa would reject in a 1.x declaration is not refused: device.check() lists it and the bridge logs each
  line once, as "warning: ..." through the log hook, when it answers a discovery
- a device whose JSON cannot be built is left out of the answer and reported as an error event
- PowerController and EndpointHealth are the descriptors and keep ON/OFF and OK/UNREACHABLE; PowerState is new

Tests: six zoo devices declared the 1.x way give the JSON that Alexa accepted from 1.5.2 on 2026-09-28, plus the
Alexa capability. npm test: 85 pass (was 57) in 10-12 s, also on Node 18.20.8 and 20.20.2.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
David 2026-09-28 15:35:46 +00:00
parent aa0ffd64ea
commit c492ef74d1
90 changed files with 5539 additions and 1760 deletions

63
dist/cjs/index.js vendored
View file

@ -3,38 +3,51 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DisplayCategories = exports.States = exports.Actions = exports.Units = exports.Assets = exports.SchemaError = exports.DeclarationError = exports.registry = exports.AlexaErrorResponse = exports.AlexaErrorType = exports.ThermostatMode = exports.TemperatureSensorScale = exports.EndpointHealth = exports.PowerController = exports.AlexaStatusMessage = exports.DisplayCategory = exports.AlexaActions = exports.ActionMapping = exports.AlexaInterfaceType = exports.AlexaInterface = exports.Device = exports.DEFAULT_HOST = exports.Alex2MQTT = void 0;
exports.AlexaErrorResponse = exports.AlexaErrorType = exports.ThermostatMode = exports.TemperatureSensorScale = exports.AlexaStatusMessage = exports.PowerState = exports.DisplayCategory = exports.AlexaInterfaceType = exports.AlexaActions = exports.ActionMapping = exports.AlexaInterface = exports.DisplayCategories = exports.States = exports.Actions = exports.Units = exports.Assets = exports.text = exports.asset = exports.EndpointHealth = exports.PowerController = exports.TemperatureSensor = exports.BrightnessController = exports.Alexa = exports.SchemaError = exports.DeclarationError = exports.registry = exports.Capability = exports.Device = exports.DEFAULT_HOST = exports.Alex2MQTT = void 0;
// Relative specifiers carry ".js": Node's ES module loader resolves no extension, and TypeScript maps it back to the .ts.
var Alex2Node_js_1 = require("./Alex2Node.js");
Object.defineProperty(exports, "Alex2MQTT", { enumerable: true, get: function () { return __importDefault(Alex2Node_js_1).default; } });
Object.defineProperty(exports, "DEFAULT_HOST", { enumerable: true, get: function () { return Alex2Node_js_1.DEFAULT_HOST; } });
var Device_js_1 = require("./Device.js");
var Device_js_1 = require("./device/Device.js");
Object.defineProperty(exports, "Device", { enumerable: true, get: function () { return __importDefault(Device_js_1).default; } });
var AlexaInterface_js_1 = require("./AlexaInterface.js");
Object.defineProperty(exports, "AlexaInterface", { enumerable: true, get: function () { return AlexaInterface_js_1.AlexaInterface; } });
Object.defineProperty(exports, "AlexaInterfaceType", { enumerable: true, get: function () { return AlexaInterface_js_1.AlexaInterfaceType; } });
var ActionMapping_js_1 = require("./ActionMapping.js");
Object.defineProperty(exports, "ActionMapping", { enumerable: true, get: function () { return ActionMapping_js_1.ActionMapping; } });
Object.defineProperty(exports, "AlexaActions", { enumerable: true, get: function () { return ActionMapping_js_1.AlexaActions; } });
var DisplayCategory_js_1 = require("./DisplayCategory.js");
Object.defineProperty(exports, "DisplayCategory", { enumerable: true, get: function () { return DisplayCategory_js_1.DisplayCategory; } });
var AlexaStatusMessage_js_1 = require("./AlexaStatusMessage.js");
Object.defineProperty(exports, "AlexaStatusMessage", { enumerable: true, get: function () { return AlexaStatusMessage_js_1.AlexaStatusMessage; } });
Object.defineProperty(exports, "PowerController", { enumerable: true, get: function () { return AlexaStatusMessage_js_1.PowerController; } });
Object.defineProperty(exports, "EndpointHealth", { enumerable: true, get: function () { return AlexaStatusMessage_js_1.EndpointHealth; } });
Object.defineProperty(exports, "TemperatureSensorScale", { enumerable: true, get: function () { return AlexaStatusMessage_js_1.TemperatureSensorScale; } });
Object.defineProperty(exports, "ThermostatMode", { enumerable: true, get: function () { return AlexaStatusMessage_js_1.ThermostatMode; } });
var AlexaErrorResponse_js_1 = require("./AlexaErrorResponse.js");
Object.defineProperty(exports, "AlexaErrorType", { enumerable: true, get: function () { return AlexaErrorResponse_js_1.AlexaErrorType; } });
Object.defineProperty(exports, "AlexaErrorResponse", { enumerable: true, get: function () { return AlexaErrorResponse_js_1.AlexaErrorResponse; } });
// The interface registry and the vocabularies of the Smart Home API
var Capability_js_1 = require("./device/Capability.js");
Object.defineProperty(exports, "Capability", { enumerable: true, get: function () { return Capability_js_1.Capability; } });
// The interfaces, for device.add(), and the registry that holds them by name
var index_js_1 = require("./registry/index.js");
Object.defineProperty(exports, "registry", { enumerable: true, get: function () { return index_js_1.registry; } });
Object.defineProperty(exports, "DeclarationError", { enumerable: true, get: function () { return index_js_1.DeclarationError; } });
Object.defineProperty(exports, "SchemaError", { enumerable: true, get: function () { return index_js_1.SchemaError; } });
var index_js_2 = require("./registry/index.js");
Object.defineProperty(exports, "Assets", { enumerable: true, get: function () { return index_js_2.ASSETS; } });
Object.defineProperty(exports, "Units", { enumerable: true, get: function () { return index_js_2.UNITS_OF_MEASURE; } });
Object.defineProperty(exports, "Actions", { enumerable: true, get: function () { return index_js_2.ACTIONS; } });
Object.defineProperty(exports, "States", { enumerable: true, get: function () { return index_js_2.STATES; } });
Object.defineProperty(exports, "DisplayCategories", { enumerable: true, get: function () { return index_js_2.DISPLAY_CATEGORIES; } });
Object.defineProperty(exports, "Alexa", { enumerable: true, get: function () { return index_js_2.Alexa; } });
Object.defineProperty(exports, "BrightnessController", { enumerable: true, get: function () { return index_js_2.BrightnessController; } });
Object.defineProperty(exports, "TemperatureSensor", { enumerable: true, get: function () { return index_js_2.TemperatureSensor; } });
var enums_js_1 = require("./compat/enums.js");
Object.defineProperty(exports, "PowerController", { enumerable: true, get: function () { return enums_js_1.PowerController; } });
Object.defineProperty(exports, "EndpointHealth", { enumerable: true, get: function () { return enums_js_1.EndpointHealth; } });
var resources_js_1 = require("./registry/resources.js");
Object.defineProperty(exports, "asset", { enumerable: true, get: function () { return resources_js_1.asset; } });
Object.defineProperty(exports, "text", { enumerable: true, get: function () { return resources_js_1.text; } });
// The vocabularies of the Smart Home API
var index_js_3 = require("./registry/index.js");
Object.defineProperty(exports, "Assets", { enumerable: true, get: function () { return index_js_3.ASSETS; } });
Object.defineProperty(exports, "Units", { enumerable: true, get: function () { return index_js_3.UNITS_OF_MEASURE; } });
Object.defineProperty(exports, "Actions", { enumerable: true, get: function () { return index_js_3.ACTIONS; } });
Object.defineProperty(exports, "States", { enumerable: true, get: function () { return index_js_3.STATES; } });
Object.defineProperty(exports, "DisplayCategories", { enumerable: true, get: function () { return index_js_3.DISPLAY_CATEGORIES; } });
// 1.x
var AlexaInterface_js_1 = require("./compat/AlexaInterface.js");
Object.defineProperty(exports, "AlexaInterface", { enumerable: true, get: function () { return AlexaInterface_js_1.AlexaInterface; } });
var ActionMapping_js_1 = require("./compat/ActionMapping.js");
Object.defineProperty(exports, "ActionMapping", { enumerable: true, get: function () { return ActionMapping_js_1.ActionMapping; } });
var enums_js_2 = require("./compat/enums.js");
Object.defineProperty(exports, "AlexaActions", { enumerable: true, get: function () { return enums_js_2.AlexaActions; } });
Object.defineProperty(exports, "AlexaInterfaceType", { enumerable: true, get: function () { return enums_js_2.AlexaInterfaceType; } });
Object.defineProperty(exports, "DisplayCategory", { enumerable: true, get: function () { return enums_js_2.DisplayCategory; } });
Object.defineProperty(exports, "PowerState", { enumerable: true, get: function () { return enums_js_2.PowerState; } });
var AlexaStatusMessage_js_1 = require("./AlexaStatusMessage.js");
Object.defineProperty(exports, "AlexaStatusMessage", { enumerable: true, get: function () { return AlexaStatusMessage_js_1.AlexaStatusMessage; } });
Object.defineProperty(exports, "TemperatureSensorScale", { enumerable: true, get: function () { return AlexaStatusMessage_js_1.TemperatureSensorScale; } });
Object.defineProperty(exports, "ThermostatMode", { enumerable: true, get: function () { return AlexaStatusMessage_js_1.ThermostatMode; } });
var AlexaErrorResponse_js_1 = require("./AlexaErrorResponse.js");
Object.defineProperty(exports, "AlexaErrorType", { enumerable: true, get: function () { return AlexaErrorResponse_js_1.AlexaErrorType; } });
Object.defineProperty(exports, "AlexaErrorResponse", { enumerable: true, get: function () { return AlexaErrorResponse_js_1.AlexaErrorResponse; } });