diff --git a/dist/cjs/compat/enums.js b/dist/cjs/compat/enums.js index 31da9aa..2fed837 100644 --- a/dist/cjs/compat/enums.js +++ b/dist/cjs/compat/enums.js @@ -34,6 +34,8 @@ var AlexaInterfaceType; AlexaInterfaceType["DOORBELL_EVENT_SOURCE"] = "Alexa.DoorbellEventSource"; AlexaInterfaceType["ENDPOINT_HEALTH"] = "Alexa.EndpointHealth"; AlexaInterfaceType["EQUALIZER_CONTROLLER"] = "Alexa.EqualizerController"; + /** Since 2.0. */ + AlexaInterfaceType["HUMIDITY_SENSOR"] = "Alexa.HumiditySensor"; AlexaInterfaceType["INPUT_CONTROLLER"] = "Alexa.InputController"; AlexaInterfaceType["INVENTORY_LEVEL_SENSOR"] = "Alexa.InventoryLevelSensor"; AlexaInterfaceType["INVENTORY_LEVEL_USAGE_SENSOR"] = "Alexa.InventoryLevelUsageSensor"; diff --git a/dist/cjs/index.js b/dist/cjs/index.js index c3fcd76..28d305d 100644 --- a/dist/cjs/index.js +++ b/dist/cjs/index.js @@ -36,7 +36,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.AlexaErrorResponse = exports.AlexaStatusMessage = exports.ThermostatMode = exports.TemperatureSensorScale = exports.PowerState = exports.DisplayCategory = exports.AlexaInterfaceType = exports.AlexaErrorType = exports.AlexaActions = exports.ActionMapping = exports.AlexaInterface = exports.MemoryPublisher = exports.topics = exports.property = exports.StateBuilder = exports.MessageError = exports.AlexaErrors = exports.AlexaError = exports.messages = exports.DisplayCategories = exports.States = exports.Actions = exports.Units = exports.Assets = exports.SemanticsBuilder = exports.semantics = exports.text = exports.asset = exports.EndpointHealth = exports.PowerController = exports.ToggleController = exports.TemperatureSensor = exports.RangeController = exports.ModeController = exports.BrightnessController = exports.Alexa = exports.SchemaError = exports.DeclarationError = exports.registry = exports.Capability = exports.Device = exports.DEFAULT_HOST = exports.Alex2MQTT = void 0; +exports.AlexaStatusMessage = exports.ThermostatMode = exports.TemperatureSensorScale = exports.PowerState = exports.DisplayCategory = exports.AlexaInterfaceType = exports.AlexaErrorType = exports.AlexaActions = exports.ActionMapping = exports.AlexaInterface = exports.MemoryPublisher = exports.topics = exports.property = exports.StateBuilder = exports.MessageError = exports.AlexaErrors = exports.AlexaError = exports.messages = exports.DisplayCategories = exports.States = exports.Actions = exports.Units = exports.Assets = exports.SemanticsBuilder = exports.semantics = exports.text = exports.asset = exports.EndpointHealth = exports.PowerController = exports.ToggleController = exports.TemperatureSensor = exports.RangeController = exports.PowerLevelController = exports.PercentageController = exports.MotionSensor = exports.ModeController = exports.LockController = exports.HumiditySensor = exports.ContactSensor = exports.ColorTemperatureController = exports.ColorController = exports.BrightnessController = exports.Alexa = exports.SchemaError = exports.DeclarationError = exports.registry = exports.Capability = exports.Device = exports.DEFAULT_HOST = exports.Alex2MQTT = void 0; +exports.AlexaErrorResponse = 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; } }); @@ -53,7 +54,15 @@ Object.defineProperty(exports, "SchemaError", { enumerable: true, get: function var index_js_2 = require("./registry/index.js"); 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, "ColorController", { enumerable: true, get: function () { return index_js_2.ColorController; } }); +Object.defineProperty(exports, "ColorTemperatureController", { enumerable: true, get: function () { return index_js_2.ColorTemperatureController; } }); +Object.defineProperty(exports, "ContactSensor", { enumerable: true, get: function () { return index_js_2.ContactSensor; } }); +Object.defineProperty(exports, "HumiditySensor", { enumerable: true, get: function () { return index_js_2.HumiditySensor; } }); +Object.defineProperty(exports, "LockController", { enumerable: true, get: function () { return index_js_2.LockController; } }); Object.defineProperty(exports, "ModeController", { enumerable: true, get: function () { return index_js_2.ModeController; } }); +Object.defineProperty(exports, "MotionSensor", { enumerable: true, get: function () { return index_js_2.MotionSensor; } }); +Object.defineProperty(exports, "PercentageController", { enumerable: true, get: function () { return index_js_2.PercentageController; } }); +Object.defineProperty(exports, "PowerLevelController", { enumerable: true, get: function () { return index_js_2.PowerLevelController; } }); Object.defineProperty(exports, "RangeController", { enumerable: true, get: function () { return index_js_2.RangeController; } }); Object.defineProperty(exports, "TemperatureSensor", { enumerable: true, get: function () { return index_js_2.TemperatureSensor; } }); Object.defineProperty(exports, "ToggleController", { enumerable: true, get: function () { return index_js_2.ToggleController; } }); diff --git a/dist/cjs/registry/index.js b/dist/cjs/registry/index.js index fd7eba4..00b3f29 100644 --- a/dist/cjs/registry/index.js +++ b/dist/cjs/registry/index.js @@ -14,18 +14,34 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.SchemaError = exports.s = exports.defineInterface = exports.DeclarationError = exports.SemanticsBuilder = exports.semantics = exports.text = exports.asset = exports.ToggleController = exports.TemperatureSensor = exports.RangeController = exports.PowerController = exports.ModeController = exports.EndpointHealth = exports.BrightnessController = exports.Alexa = exports.registry = void 0; +exports.SchemaError = exports.s = exports.defineInterface = exports.DeclarationError = exports.SemanticsBuilder = exports.semantics = exports.text = exports.asset = exports.ToggleController = exports.TemperatureSensor = exports.RangeController = exports.PowerLevelController = exports.PowerController = exports.PercentageController = exports.MotionSensor = exports.ModeController = exports.LockController = exports.HumiditySensor = exports.EndpointHealth = exports.ContactSensor = exports.ColorTemperatureController = exports.ColorController = exports.BrightnessController = exports.Alexa = exports.registry = void 0; // The interfaces the library knows, by namespace. const Alexa_js_1 = require("./interfaces/Alexa.js"); Object.defineProperty(exports, "Alexa", { enumerable: true, get: function () { return Alexa_js_1.Alexa; } }); const BrightnessController_js_1 = require("./interfaces/BrightnessController.js"); Object.defineProperty(exports, "BrightnessController", { enumerable: true, get: function () { return BrightnessController_js_1.BrightnessController; } }); +const ColorController_js_1 = require("./interfaces/ColorController.js"); +Object.defineProperty(exports, "ColorController", { enumerable: true, get: function () { return ColorController_js_1.ColorController; } }); +const ColorTemperatureController_js_1 = require("./interfaces/ColorTemperatureController.js"); +Object.defineProperty(exports, "ColorTemperatureController", { enumerable: true, get: function () { return ColorTemperatureController_js_1.ColorTemperatureController; } }); +const ContactSensor_js_1 = require("./interfaces/ContactSensor.js"); +Object.defineProperty(exports, "ContactSensor", { enumerable: true, get: function () { return ContactSensor_js_1.ContactSensor; } }); const EndpointHealth_js_1 = require("./interfaces/EndpointHealth.js"); Object.defineProperty(exports, "EndpointHealth", { enumerable: true, get: function () { return EndpointHealth_js_1.EndpointHealth; } }); +const HumiditySensor_js_1 = require("./interfaces/HumiditySensor.js"); +Object.defineProperty(exports, "HumiditySensor", { enumerable: true, get: function () { return HumiditySensor_js_1.HumiditySensor; } }); +const LockController_js_1 = require("./interfaces/LockController.js"); +Object.defineProperty(exports, "LockController", { enumerable: true, get: function () { return LockController_js_1.LockController; } }); const ModeController_js_1 = require("./interfaces/ModeController.js"); Object.defineProperty(exports, "ModeController", { enumerable: true, get: function () { return ModeController_js_1.ModeController; } }); +const MotionSensor_js_1 = require("./interfaces/MotionSensor.js"); +Object.defineProperty(exports, "MotionSensor", { enumerable: true, get: function () { return MotionSensor_js_1.MotionSensor; } }); +const PercentageController_js_1 = require("./interfaces/PercentageController.js"); +Object.defineProperty(exports, "PercentageController", { enumerable: true, get: function () { return PercentageController_js_1.PercentageController; } }); const PowerController_js_1 = require("./interfaces/PowerController.js"); Object.defineProperty(exports, "PowerController", { enumerable: true, get: function () { return PowerController_js_1.PowerController; } }); +const PowerLevelController_js_1 = require("./interfaces/PowerLevelController.js"); +Object.defineProperty(exports, "PowerLevelController", { enumerable: true, get: function () { return PowerLevelController_js_1.PowerLevelController; } }); const RangeController_js_1 = require("./interfaces/RangeController.js"); Object.defineProperty(exports, "RangeController", { enumerable: true, get: function () { return RangeController_js_1.RangeController; } }); const TemperatureSensor_js_1 = require("./interfaces/TemperatureSensor.js"); @@ -37,9 +53,17 @@ const types_js_1 = require("./types.js"); const described = [ Alexa_js_1.Alexa, BrightnessController_js_1.BrightnessController, + ColorController_js_1.ColorController, + ColorTemperatureController_js_1.ColorTemperatureController, + ContactSensor_js_1.ContactSensor, EndpointHealth_js_1.EndpointHealth, + HumiditySensor_js_1.HumiditySensor, + LockController_js_1.LockController, ModeController_js_1.ModeController, + MotionSensor_js_1.MotionSensor, + PercentageController_js_1.PercentageController, PowerController_js_1.PowerController, + PowerLevelController_js_1.PowerLevelController, RangeController_js_1.RangeController, TemperatureSensor_js_1.TemperatureSensor, ToggleController_js_1.ToggleController, diff --git a/dist/cjs/registry/interfaces/ColorController.js b/dist/cjs/registry/interfaces/ColorController.js new file mode 100644 index 0000000..63d4275 --- /dev/null +++ b/dist/cjs/registry/interfaces/ColorController.js @@ -0,0 +1,29 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ColorController = void 0; +const schema_js_1 = require("../schema.js"); +const types_js_1 = require("../types.js"); +// alexa-property-schemas.html, "Color" +const color = schema_js_1.s.object({ + hue: schema_js_1.s.number({ min: 0, max: 360 }), + saturation: schema_js_1.s.number({ min: 0, max: 1 }), + brightness: schema_js_1.s.number({ min: 0, max: 1 }), +}); +/** + * A light changes its hue and saturation and keeps the brightness it has: "ignore the brightness value of 1 in the + * directive" (alexa-colorcontroller.html, "SetColor directive example"). + */ +exports.ColorController = (0, types_js_1.defineInterface)({ + namespace: "Alexa.ColorController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-colorcontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + color: { name: "color", value: color }, + }, + directives: { + SetColor: { name: "SetColor", payload: schema_js_1.s.object({ color }) }, + }, +}); diff --git a/dist/cjs/registry/interfaces/ColorTemperatureController.js b/dist/cjs/registry/interfaces/ColorTemperatureController.js new file mode 100644 index 0000000..0867a47 --- /dev/null +++ b/dist/cjs/registry/interfaces/ColorTemperatureController.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ColorTemperatureController = void 0; +const schema_js_1 = require("../schema.js"); +const types_js_1 = require("../types.js"); +// alexa-colortemperaturecontroller.html, "Reportable properties": "Valid values range from 1,000 to 10,000" +const kelvin = schema_js_1.s.number({ min: 1000, max: 10000, integer: true }); +/** + * The white of a tunable light. Alexa asks for 2200 (warm), 2700 (soft), 4000 (white), 5500 (daylight) and 7000 + * (cool); a light that cannot show the value takes the nearest it can. A light that shows a colour answers + * NOT_SUPPORTED_IN_CURRENT_MODE with currentDeviceMode COLOR. + */ +exports.ColorTemperatureController = (0, types_js_1.defineInterface)({ + namespace: "Alexa.ColorTemperatureController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-colortemperaturecontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + colorTemperatureInKelvin: { name: "colorTemperatureInKelvin", value: kelvin }, + }, + directives: { + SetColorTemperature: { name: "SetColorTemperature", payload: schema_js_1.s.object({ colorTemperatureInKelvin: kelvin }) }, + IncreaseColorTemperature: { name: "IncreaseColorTemperature", payload: schema_js_1.s.object({}), note: "towards cool" }, + DecreaseColorTemperature: { name: "DecreaseColorTemperature", payload: schema_js_1.s.object({}), note: "towards warm" }, + }, +}); diff --git a/dist/cjs/registry/interfaces/ContactSensor.js b/dist/cjs/registry/interfaces/ContactSensor.js new file mode 100644 index 0000000..54c706b --- /dev/null +++ b/dist/cjs/registry/interfaces/ContactSensor.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ContactSensor = void 0; +const schema_js_1 = require("../schema.js"); +const types_js_1 = require("../types.js"); +/** + * DETECTED is open: the two pieces of the sensor are apart. A sensor that cannot answer ReportState sends a + * ChangeReport right after discovery, so that the Alexa app shows its state (alexa-contactsensor.html). + */ +exports.ContactSensor = (0, types_js_1.defineInterface)({ + namespace: "Alexa.ContactSensor", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-contactsensor.html", + kind: "sensor", + tier: 1, + instanced: false, + properties: { + detectionState: { name: "detectionState", value: schema_js_1.s.enum("DETECTED", "NOT_DETECTED") }, + }, + directives: {}, +}); diff --git a/dist/cjs/registry/interfaces/HumiditySensor.js b/dist/cjs/registry/interfaces/HumiditySensor.js new file mode 100644 index 0000000..516d3d3 --- /dev/null +++ b/dist/cjs/registry/interfaces/HumiditySensor.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HumiditySensor = void 0; +const schema_js_1 = require("../schema.js"); +const types_js_1 = require("../types.js"); +/** The value is an object, { value: 45.5 }, as the reports of alexa-humiditysensor.html print it. */ +exports.HumiditySensor = (0, types_js_1.defineInterface)({ + namespace: "Alexa.HumiditySensor", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-humiditysensor.html", + kind: "sensor", + tier: 1, + instanced: false, + properties: { + relativeHumidity: { name: "relativeHumidity", value: schema_js_1.s.object({ value: schema_js_1.s.number({ min: 0, max: 100 }) }) }, + }, + directives: {}, +}); diff --git a/dist/cjs/registry/interfaces/LockController.js b/dist/cjs/registry/interfaces/LockController.js new file mode 100644 index 0000000..e0e45ef --- /dev/null +++ b/dist/cjs/registry/interfaces/LockController.js @@ -0,0 +1,29 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LockController = void 0; +const schema_js_1 = require("../schema.js"); +const types_js_1 = require("../types.js"); +/** + * A lock that takes more than five seconds answers with a DeferredResponse first and the Response when the bolt has + * moved (alexa-lockcontroller.html, "Lock response"). The user enables unlocking by voice per device, in the Alexa app. + */ +exports.LockController = (0, types_js_1.defineInterface)({ + namespace: "Alexa.LockController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-lockcontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + lockState: { + name: "lockState", + value: schema_js_1.s.enum("LOCKED", "UNLOCKED", "JAMMED"), + note: "JAMMED: the mechanism can reach neither LOCKED nor UNLOCKED", + }, + }, + directives: { + Lock: { name: "Lock", payload: schema_js_1.s.object({}) }, + Unlock: { name: "Unlock", payload: schema_js_1.s.object({}) }, + }, + deferrable: true, +}); diff --git a/dist/cjs/registry/interfaces/MotionSensor.js b/dist/cjs/registry/interfaces/MotionSensor.js new file mode 100644 index 0000000..3875cf1 --- /dev/null +++ b/dist/cjs/registry/interfaces/MotionSensor.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MotionSensor = void 0; +const schema_js_1 = require("../schema.js"); +const types_js_1 = require("../types.js"); +/** + * "When a motion sensor changes to DETECTED, and then changes to NOT_DETECTED, you must wait at least 30 seconds + * before sending the second change report" (alexa-motionsensor.html, "Change reporting"). + */ +exports.MotionSensor = (0, types_js_1.defineInterface)({ + namespace: "Alexa.MotionSensor", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-motionsensor.html", + kind: "sensor", + tier: 1, + instanced: false, + properties: { + detectionState: { name: "detectionState", value: schema_js_1.s.enum("DETECTED", "NOT_DETECTED") }, + }, + directives: {}, +}); diff --git a/dist/cjs/registry/interfaces/PercentageController.js b/dist/cjs/registry/interfaces/PercentageController.js new file mode 100644 index 0000000..fe24979 --- /dev/null +++ b/dist/cjs/registry/interfaces/PercentageController.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PercentageController = void 0; +const schema_js_1 = require("../schema.js"); +const types_js_1 = require("../types.js"); +/** + * For a device no other interface fits: "Use this interface only if there isn't a more specific controller + * interface that applies to your device" (alexa-percentagecontroller.html). + */ +exports.PercentageController = (0, types_js_1.defineInterface)({ + namespace: "Alexa.PercentageController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-percentagecontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + percentage: { name: "percentage", value: schema_js_1.s.number({ min: 0, max: 100, integer: true }) }, + }, + directives: { + SetPercentage: { + name: "SetPercentage", + payload: schema_js_1.s.object({ percentage: schema_js_1.s.number({ min: 0, max: 100, integer: true }) }), + }, + AdjustPercentage: { + name: "AdjustPercentage", + payload: schema_js_1.s.object({ percentageDelta: schema_js_1.s.number({ min: -100, max: 100, integer: true }) }), + }, + }, +}); diff --git a/dist/cjs/registry/interfaces/PowerLevelController.js b/dist/cjs/registry/interfaces/PowerLevelController.js new file mode 100644 index 0000000..08394a2 --- /dev/null +++ b/dist/cjs/registry/interfaces/PowerLevelController.js @@ -0,0 +1,27 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PowerLevelController = void 0; +const schema_js_1 = require("../schema.js"); +const types_js_1 = require("../types.js"); +/** The power of a device as a percentage, a dimmer switch for one (alexa-powerlevelcontroller.html). */ +exports.PowerLevelController = (0, types_js_1.defineInterface)({ + namespace: "Alexa.PowerLevelController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-powerlevelcontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + powerLevel: { name: "powerLevel", value: schema_js_1.s.number({ min: 0, max: 100, integer: true }) }, + }, + directives: { + SetPowerLevel: { + name: "SetPowerLevel", + payload: schema_js_1.s.object({ powerLevel: schema_js_1.s.number({ min: 0, max: 100, integer: true }) }), + }, + AdjustPowerLevel: { + name: "AdjustPowerLevel", + payload: schema_js_1.s.object({ powerLevelDelta: schema_js_1.s.number({ min: -100, max: 100, integer: true }) }), + }, + }, +}); diff --git a/dist/cjs/registry/interfaces/stubs.js b/dist/cjs/registry/interfaces/stubs.js index 07ebcb2..7717736 100644 --- a/dist/cjs/registry/interfaces/stubs.js +++ b/dist/cjs/registry/interfaces/stubs.js @@ -21,11 +21,8 @@ const TABLE = [ ["Alexa.Camera.LiveViewController", "1.7", [], LIST_OF_INTERFACES], ["Alexa.CameraStreamController", "3", [], "alexa-camerastreamcontroller.html"], ["Alexa.ChannelController", "3", ["channel"], "alexa-channelcontroller.html"], - ["Alexa.ColorController", "3", ["color"], "alexa-colorcontroller.html"], - ["Alexa.ColorTemperatureController", "3", ["colorTemperatureInKelvin"], "alexa-colortemperaturecontroller.html"], ["Alexa.Commissionable", "1", [], "alexa-commissionable.html"], ["Alexa.ConsentManagement.ConsentRequiredReporter", "1", [], "alexa-consentrequiredreporter.html"], - ["Alexa.ContactSensor", "3", ["detectionState"], "alexa-contactsensor.html"], ["Alexa.Cooking", "1", [], "alexa-cooking.html"], ["Alexa.Cooking.FoodTemperatureController", "1", [], "alexa-cooking-foodtemperaturecontroller.html"], ["Alexa.Cooking.FoodTemperatureSensor", "1", [], "alexa-cooking-foodtemperaturesensor.html"], @@ -44,15 +41,11 @@ const TABLE = [ ["Alexa.InventoryUsageSensor", "1", [], "alexa-inventoryusagesensor.html"], ["Alexa.KeypadController", "1", [], "alexa-keypadcontroller.html"], ["Alexa.Launcher", "1.1", [], "alexa-launcher.html"], - ["Alexa.LockController", "3", [], "alexa-lockcontroller.html"], ["Alexa.Media.PlayQueue", "1", [], "alexa-media-playqueue.html"], ["Alexa.Media.Playback", "1", [], "alexa-media-playback.html"], ["Alexa.Media.Search", "1", [], "alexa-media-search.html"], - ["Alexa.MotionSensor", "3", [], "alexa-motionsensor.html"], - ["Alexa.PercentageController", "3", [], "alexa-percentagecontroller.html"], ["Alexa.PlaybackController", "3", [], "alexa-playbackcontroller.html"], ["Alexa.PlaybackStateReporter", "1", [], "alexa-playbackcontroller.html"], - ["Alexa.PowerLevelController", "3", [], "alexa-powerlevelcontroller.html"], ["Alexa.ProactiveNotificationSource", "1", [], "alexa-proactivenotificationsource.html"], ["Alexa.RTCSessionController", "1", [], "alexa-rtcsessioncontroller.html"], ["Alexa.RecordController", "3", [], "alexa-recordcontroller.html"], diff --git a/dist/esm/compat/enums.d.ts b/dist/esm/compat/enums.d.ts index e253d48..66b7107 100644 --- a/dist/esm/compat/enums.d.ts +++ b/dist/esm/compat/enums.d.ts @@ -27,6 +27,8 @@ export declare enum AlexaInterfaceType { DOORBELL_EVENT_SOURCE = "Alexa.DoorbellEventSource", ENDPOINT_HEALTH = "Alexa.EndpointHealth", EQUALIZER_CONTROLLER = "Alexa.EqualizerController", + /** Since 2.0. */ + HUMIDITY_SENSOR = "Alexa.HumiditySensor", INPUT_CONTROLLER = "Alexa.InputController", INVENTORY_LEVEL_SENSOR = "Alexa.InventoryLevelSensor", INVENTORY_LEVEL_USAGE_SENSOR = "Alexa.InventoryLevelUsageSensor", diff --git a/dist/esm/compat/enums.js b/dist/esm/compat/enums.js index d6e5278..f285a61 100644 --- a/dist/esm/compat/enums.js +++ b/dist/esm/compat/enums.js @@ -31,6 +31,8 @@ export var AlexaInterfaceType; AlexaInterfaceType["DOORBELL_EVENT_SOURCE"] = "Alexa.DoorbellEventSource"; AlexaInterfaceType["ENDPOINT_HEALTH"] = "Alexa.EndpointHealth"; AlexaInterfaceType["EQUALIZER_CONTROLLER"] = "Alexa.EqualizerController"; + /** Since 2.0. */ + AlexaInterfaceType["HUMIDITY_SENSOR"] = "Alexa.HumiditySensor"; AlexaInterfaceType["INPUT_CONTROLLER"] = "Alexa.InputController"; AlexaInterfaceType["INVENTORY_LEVEL_SENSOR"] = "Alexa.InventoryLevelSensor"; AlexaInterfaceType["INVENTORY_LEVEL_USAGE_SENSOR"] = "Alexa.InventoryLevelUsageSensor"; diff --git a/dist/esm/index.d.ts b/dist/esm/index.d.ts index 8828d39..a1d0f1b 100644 --- a/dist/esm/index.d.ts +++ b/dist/esm/index.d.ts @@ -5,7 +5,7 @@ export type { EndpointDefinition, EndpointJson } from "./device/Device.js"; export { Capability } from "./device/Capability.js"; export type { CapabilityJson, CommonOptions, Declaration } from "./device/Capability.js"; export { registry, DeclarationError, SchemaError } from "./registry/index.js"; -export { Alexa, BrightnessController, ModeController, RangeController, TemperatureSensor, ToggleController, } from "./registry/index.js"; +export { Alexa, BrightnessController, ColorController, ColorTemperatureController, ContactSensor, HumiditySensor, LockController, ModeController, MotionSensor, PercentageController, PowerLevelController, RangeController, TemperatureSensor, ToggleController, } from "./registry/index.js"; export { PowerController, EndpointHealth } from "./compat/enums.js"; export { asset, text, semantics, SemanticsBuilder } from "./registry/index.js"; export { ASSETS as Assets, UNITS_OF_MEASURE as Units, ACTIONS as Actions, STATES as States, DISPLAY_CATEGORIES as DisplayCategories, } from "./registry/index.js"; diff --git a/dist/esm/index.js b/dist/esm/index.js index fa09b61..29aa773 100644 --- a/dist/esm/index.js +++ b/dist/esm/index.js @@ -4,7 +4,7 @@ export { default as Device } from "./device/Device.js"; export { Capability } from "./device/Capability.js"; // The interfaces, for device.add(), and the registry that holds them by name export { registry, DeclarationError, SchemaError } from "./registry/index.js"; -export { Alexa, BrightnessController, ModeController, RangeController, TemperatureSensor, ToggleController, } from "./registry/index.js"; +export { Alexa, BrightnessController, ColorController, ColorTemperatureController, ContactSensor, HumiditySensor, LockController, ModeController, MotionSensor, PercentageController, PowerLevelController, RangeController, TemperatureSensor, ToggleController, } from "./registry/index.js"; export { PowerController, EndpointHealth } from "./compat/enums.js"; export { asset, text, semantics, SemanticsBuilder } from "./registry/index.js"; // The vocabularies of the Smart Home API diff --git a/dist/esm/registry/index.d.ts b/dist/esm/registry/index.d.ts index 701579b..7b7dccd 100644 --- a/dist/esm/registry/index.d.ts +++ b/dist/esm/registry/index.d.ts @@ -1,8 +1,16 @@ import { Alexa } from "./interfaces/Alexa.js"; import { BrightnessController } from "./interfaces/BrightnessController.js"; +import { ColorController } from "./interfaces/ColorController.js"; +import { ColorTemperatureController } from "./interfaces/ColorTemperatureController.js"; +import { ContactSensor } from "./interfaces/ContactSensor.js"; import { EndpointHealth } from "./interfaces/EndpointHealth.js"; +import { HumiditySensor } from "./interfaces/HumiditySensor.js"; +import { LockController } from "./interfaces/LockController.js"; import { ModeController } from "./interfaces/ModeController.js"; +import { MotionSensor } from "./interfaces/MotionSensor.js"; +import { PercentageController } from "./interfaces/PercentageController.js"; import { PowerController } from "./interfaces/PowerController.js"; +import { PowerLevelController } from "./interfaces/PowerLevelController.js"; import { RangeController } from "./interfaces/RangeController.js"; import { TemperatureSensor } from "./interfaces/TemperatureSensor.js"; import { ToggleController } from "./interfaces/ToggleController.js"; @@ -15,7 +23,7 @@ export declare const registry: { /** Every descriptor, ordered by namespace. */ list(): AnyDescriptor[]; }; -export { Alexa, BrightnessController, EndpointHealth, ModeController, PowerController, RangeController, TemperatureSensor, ToggleController, }; +export { Alexa, BrightnessController, ColorController, ColorTemperatureController, ContactSensor, EndpointHealth, HumiditySensor, LockController, ModeController, MotionSensor, PercentageController, PowerController, PowerLevelController, RangeController, TemperatureSensor, ToggleController, }; export type { Mode } from "./interfaces/ModeController.js"; export { asset, text } from "./resources.js"; export { semantics, SemanticsBuilder } from "./semantics.js"; diff --git a/dist/esm/registry/index.js b/dist/esm/registry/index.js index d8a80e2..8fbe67e 100644 --- a/dist/esm/registry/index.js +++ b/dist/esm/registry/index.js @@ -1,9 +1,17 @@ // The interfaces the library knows, by namespace. import { Alexa } from "./interfaces/Alexa.js"; import { BrightnessController } from "./interfaces/BrightnessController.js"; +import { ColorController } from "./interfaces/ColorController.js"; +import { ColorTemperatureController } from "./interfaces/ColorTemperatureController.js"; +import { ContactSensor } from "./interfaces/ContactSensor.js"; import { EndpointHealth } from "./interfaces/EndpointHealth.js"; +import { HumiditySensor } from "./interfaces/HumiditySensor.js"; +import { LockController } from "./interfaces/LockController.js"; import { ModeController } from "./interfaces/ModeController.js"; +import { MotionSensor } from "./interfaces/MotionSensor.js"; +import { PercentageController } from "./interfaces/PercentageController.js"; import { PowerController } from "./interfaces/PowerController.js"; +import { PowerLevelController } from "./interfaces/PowerLevelController.js"; import { RangeController } from "./interfaces/RangeController.js"; import { TemperatureSensor } from "./interfaces/TemperatureSensor.js"; import { ToggleController } from "./interfaces/ToggleController.js"; @@ -12,9 +20,17 @@ import { DeclarationError } from "./types.js"; const described = [ Alexa, BrightnessController, + ColorController, + ColorTemperatureController, + ContactSensor, EndpointHealth, + HumiditySensor, + LockController, ModeController, + MotionSensor, + PercentageController, PowerController, + PowerLevelController, RangeController, TemperatureSensor, ToggleController, @@ -43,7 +59,7 @@ export const registry = { return [...descriptors.values()].sort((a, b) => (a.namespace < b.namespace ? -1 : 1)); }, }; -export { Alexa, BrightnessController, EndpointHealth, ModeController, PowerController, RangeController, TemperatureSensor, ToggleController, }; +export { Alexa, BrightnessController, ColorController, ColorTemperatureController, ContactSensor, EndpointHealth, HumiditySensor, LockController, ModeController, MotionSensor, PercentageController, PowerController, PowerLevelController, RangeController, TemperatureSensor, ToggleController, }; export { asset, text } from "./resources.js"; export { semantics, SemanticsBuilder } from "./semantics.js"; export { DeclarationError, defineInterface } from "./types.js"; diff --git a/dist/esm/registry/interfaces/ColorController.d.ts b/dist/esm/registry/interfaces/ColorController.d.ts new file mode 100644 index 0000000..4d7e663 --- /dev/null +++ b/dist/esm/registry/interfaces/ColorController.d.ts @@ -0,0 +1,25 @@ +/** + * A light changes its hue and saturation and keeps the brightness it has: "ignore the brightness value of 1 in the + * directive" (alexa-colorcontroller.html, "SetColor directive example"). + */ +export declare const ColorController: import("../types.js").InterfaceDescriptor<{ + color: { + name: string; + value: import("../schema.js").Schema; + saturation: import("../schema.js").Schema; + brightness: import("../schema.js").Schema; + }>>; + }; +}, { + SetColor: { + name: string; + payload: import("../schema.js").Schema; + saturation: import("../schema.js").Schema; + brightness: import("../schema.js").Schema; + }>>; + }>>; + }; +}, {}, false>; diff --git a/dist/esm/registry/interfaces/ColorController.js b/dist/esm/registry/interfaces/ColorController.js new file mode 100644 index 0000000..e9933d0 --- /dev/null +++ b/dist/esm/registry/interfaces/ColorController.js @@ -0,0 +1,26 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; +// alexa-property-schemas.html, "Color" +const color = s.object({ + hue: s.number({ min: 0, max: 360 }), + saturation: s.number({ min: 0, max: 1 }), + brightness: s.number({ min: 0, max: 1 }), +}); +/** + * A light changes its hue and saturation and keeps the brightness it has: "ignore the brightness value of 1 in the + * directive" (alexa-colorcontroller.html, "SetColor directive example"). + */ +export const ColorController = defineInterface({ + namespace: "Alexa.ColorController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-colorcontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + color: { name: "color", value: color }, + }, + directives: { + SetColor: { name: "SetColor", payload: s.object({ color }) }, + }, +}); diff --git a/dist/esm/registry/interfaces/ColorTemperatureController.d.ts b/dist/esm/registry/interfaces/ColorTemperatureController.d.ts new file mode 100644 index 0000000..3cd5124 --- /dev/null +++ b/dist/esm/registry/interfaces/ColorTemperatureController.d.ts @@ -0,0 +1,28 @@ +/** + * The white of a tunable light. Alexa asks for 2200 (warm), 2700 (soft), 4000 (white), 5500 (daylight) and 7000 + * (cool); a light that cannot show the value takes the nearest it can. A light that shows a colour answers + * NOT_SUPPORTED_IN_CURRENT_MODE with currentDeviceMode COLOR. + */ +export declare const ColorTemperatureController: import("../types.js").InterfaceDescriptor<{ + colorTemperatureInKelvin: { + name: string; + value: import("../schema.js").Schema; + }; +}, { + SetColorTemperature: { + name: string; + payload: import("../schema.js").Schema; + }>>; + }; + IncreaseColorTemperature: { + name: string; + payload: import("../schema.js").Schema>; + note: string; + }; + DecreaseColorTemperature: { + name: string; + payload: import("../schema.js").Schema>; + note: string; + }; +}, {}, false>; diff --git a/dist/esm/registry/interfaces/ColorTemperatureController.js b/dist/esm/registry/interfaces/ColorTemperatureController.js new file mode 100644 index 0000000..33345af --- /dev/null +++ b/dist/esm/registry/interfaces/ColorTemperatureController.js @@ -0,0 +1,25 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; +// alexa-colortemperaturecontroller.html, "Reportable properties": "Valid values range from 1,000 to 10,000" +const kelvin = s.number({ min: 1000, max: 10000, integer: true }); +/** + * The white of a tunable light. Alexa asks for 2200 (warm), 2700 (soft), 4000 (white), 5500 (daylight) and 7000 + * (cool); a light that cannot show the value takes the nearest it can. A light that shows a colour answers + * NOT_SUPPORTED_IN_CURRENT_MODE with currentDeviceMode COLOR. + */ +export const ColorTemperatureController = defineInterface({ + namespace: "Alexa.ColorTemperatureController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-colortemperaturecontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + colorTemperatureInKelvin: { name: "colorTemperatureInKelvin", value: kelvin }, + }, + directives: { + SetColorTemperature: { name: "SetColorTemperature", payload: s.object({ colorTemperatureInKelvin: kelvin }) }, + IncreaseColorTemperature: { name: "IncreaseColorTemperature", payload: s.object({}), note: "towards cool" }, + DecreaseColorTemperature: { name: "DecreaseColorTemperature", payload: s.object({}), note: "towards warm" }, + }, +}); diff --git a/dist/esm/registry/interfaces/ContactSensor.d.ts b/dist/esm/registry/interfaces/ContactSensor.d.ts new file mode 100644 index 0000000..af91060 --- /dev/null +++ b/dist/esm/registry/interfaces/ContactSensor.d.ts @@ -0,0 +1,10 @@ +/** + * DETECTED is open: the two pieces of the sensor are apart. A sensor that cannot answer ReportState sends a + * ChangeReport right after discovery, so that the Alexa app shows its state (alexa-contactsensor.html). + */ +export declare const ContactSensor: import("../types.js").InterfaceDescriptor<{ + detectionState: { + name: string; + value: import("../schema.js").EnumSchema<"DETECTED" | "NOT_DETECTED">; + }; +}, {}, {}, false>; diff --git a/dist/esm/registry/interfaces/ContactSensor.js b/dist/esm/registry/interfaces/ContactSensor.js new file mode 100644 index 0000000..f5ca176 --- /dev/null +++ b/dist/esm/registry/interfaces/ContactSensor.js @@ -0,0 +1,18 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; +/** + * DETECTED is open: the two pieces of the sensor are apart. A sensor that cannot answer ReportState sends a + * ChangeReport right after discovery, so that the Alexa app shows its state (alexa-contactsensor.html). + */ +export const ContactSensor = defineInterface({ + namespace: "Alexa.ContactSensor", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-contactsensor.html", + kind: "sensor", + tier: 1, + instanced: false, + properties: { + detectionState: { name: "detectionState", value: s.enum("DETECTED", "NOT_DETECTED") }, + }, + directives: {}, +}); diff --git a/dist/esm/registry/interfaces/HumiditySensor.d.ts b/dist/esm/registry/interfaces/HumiditySensor.d.ts new file mode 100644 index 0000000..59d28aa --- /dev/null +++ b/dist/esm/registry/interfaces/HumiditySensor.d.ts @@ -0,0 +1,9 @@ +/** The value is an object, { value: 45.5 }, as the reports of alexa-humiditysensor.html print it. */ +export declare const HumiditySensor: import("../types.js").InterfaceDescriptor<{ + relativeHumidity: { + name: string; + value: import("../schema.js").Schema; + }>>; + }; +}, {}, {}, false>; diff --git a/dist/esm/registry/interfaces/HumiditySensor.js b/dist/esm/registry/interfaces/HumiditySensor.js new file mode 100644 index 0000000..5086f56 --- /dev/null +++ b/dist/esm/registry/interfaces/HumiditySensor.js @@ -0,0 +1,15 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; +/** The value is an object, { value: 45.5 }, as the reports of alexa-humiditysensor.html print it. */ +export const HumiditySensor = defineInterface({ + namespace: "Alexa.HumiditySensor", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-humiditysensor.html", + kind: "sensor", + tier: 1, + instanced: false, + properties: { + relativeHumidity: { name: "relativeHumidity", value: s.object({ value: s.number({ min: 0, max: 100 }) }) }, + }, + directives: {}, +}); diff --git a/dist/esm/registry/interfaces/LockController.d.ts b/dist/esm/registry/interfaces/LockController.d.ts new file mode 100644 index 0000000..6ccc43c --- /dev/null +++ b/dist/esm/registry/interfaces/LockController.d.ts @@ -0,0 +1,20 @@ +/** + * A lock that takes more than five seconds answers with a DeferredResponse first and the Response when the bolt has + * moved (alexa-lockcontroller.html, "Lock response"). The user enables unlocking by voice per device, in the Alexa app. + */ +export declare const LockController: import("../types.js").InterfaceDescriptor<{ + lockState: { + name: string; + value: import("../schema.js").EnumSchema<"LOCKED" | "UNLOCKED" | "JAMMED">; + note: string; + }; +}, { + Lock: { + name: string; + payload: import("../schema.js").Schema>; + }; + Unlock: { + name: string; + payload: import("../schema.js").Schema>; + }; +}, {}, false>; diff --git a/dist/esm/registry/interfaces/LockController.js b/dist/esm/registry/interfaces/LockController.js new file mode 100644 index 0000000..b747ad1 --- /dev/null +++ b/dist/esm/registry/interfaces/LockController.js @@ -0,0 +1,26 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; +/** + * A lock that takes more than five seconds answers with a DeferredResponse first and the Response when the bolt has + * moved (alexa-lockcontroller.html, "Lock response"). The user enables unlocking by voice per device, in the Alexa app. + */ +export const LockController = defineInterface({ + namespace: "Alexa.LockController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-lockcontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + lockState: { + name: "lockState", + value: s.enum("LOCKED", "UNLOCKED", "JAMMED"), + note: "JAMMED: the mechanism can reach neither LOCKED nor UNLOCKED", + }, + }, + directives: { + Lock: { name: "Lock", payload: s.object({}) }, + Unlock: { name: "Unlock", payload: s.object({}) }, + }, + deferrable: true, +}); diff --git a/dist/esm/registry/interfaces/MotionSensor.d.ts b/dist/esm/registry/interfaces/MotionSensor.d.ts new file mode 100644 index 0000000..aab0454 --- /dev/null +++ b/dist/esm/registry/interfaces/MotionSensor.d.ts @@ -0,0 +1,10 @@ +/** + * "When a motion sensor changes to DETECTED, and then changes to NOT_DETECTED, you must wait at least 30 seconds + * before sending the second change report" (alexa-motionsensor.html, "Change reporting"). + */ +export declare const MotionSensor: import("../types.js").InterfaceDescriptor<{ + detectionState: { + name: string; + value: import("../schema.js").EnumSchema<"DETECTED" | "NOT_DETECTED">; + }; +}, {}, {}, false>; diff --git a/dist/esm/registry/interfaces/MotionSensor.js b/dist/esm/registry/interfaces/MotionSensor.js new file mode 100644 index 0000000..4770a22 --- /dev/null +++ b/dist/esm/registry/interfaces/MotionSensor.js @@ -0,0 +1,18 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; +/** + * "When a motion sensor changes to DETECTED, and then changes to NOT_DETECTED, you must wait at least 30 seconds + * before sending the second change report" (alexa-motionsensor.html, "Change reporting"). + */ +export const MotionSensor = defineInterface({ + namespace: "Alexa.MotionSensor", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-motionsensor.html", + kind: "sensor", + tier: 1, + instanced: false, + properties: { + detectionState: { name: "detectionState", value: s.enum("DETECTED", "NOT_DETECTED") }, + }, + directives: {}, +}); diff --git a/dist/esm/registry/interfaces/PercentageController.d.ts b/dist/esm/registry/interfaces/PercentageController.d.ts new file mode 100644 index 0000000..7e8d00f --- /dev/null +++ b/dist/esm/registry/interfaces/PercentageController.d.ts @@ -0,0 +1,23 @@ +/** + * For a device no other interface fits: "Use this interface only if there isn't a more specific controller + * interface that applies to your device" (alexa-percentagecontroller.html). + */ +export declare const PercentageController: import("../types.js").InterfaceDescriptor<{ + percentage: { + name: string; + value: import("../schema.js").Schema; + }; +}, { + SetPercentage: { + name: string; + payload: import("../schema.js").Schema; + }>>; + }; + AdjustPercentage: { + name: string; + payload: import("../schema.js").Schema; + }>>; + }; +}, {}, false>; diff --git a/dist/esm/registry/interfaces/PercentageController.js b/dist/esm/registry/interfaces/PercentageController.js new file mode 100644 index 0000000..3ebb9a7 --- /dev/null +++ b/dist/esm/registry/interfaces/PercentageController.js @@ -0,0 +1,27 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; +/** + * For a device no other interface fits: "Use this interface only if there isn't a more specific controller + * interface that applies to your device" (alexa-percentagecontroller.html). + */ +export const PercentageController = defineInterface({ + namespace: "Alexa.PercentageController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-percentagecontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + percentage: { name: "percentage", value: s.number({ min: 0, max: 100, integer: true }) }, + }, + directives: { + SetPercentage: { + name: "SetPercentage", + payload: s.object({ percentage: s.number({ min: 0, max: 100, integer: true }) }), + }, + AdjustPercentage: { + name: "AdjustPercentage", + payload: s.object({ percentageDelta: s.number({ min: -100, max: 100, integer: true }) }), + }, + }, +}); diff --git a/dist/esm/registry/interfaces/PowerLevelController.d.ts b/dist/esm/registry/interfaces/PowerLevelController.d.ts new file mode 100644 index 0000000..ab5d437 --- /dev/null +++ b/dist/esm/registry/interfaces/PowerLevelController.d.ts @@ -0,0 +1,20 @@ +/** The power of a device as a percentage, a dimmer switch for one (alexa-powerlevelcontroller.html). */ +export declare const PowerLevelController: import("../types.js").InterfaceDescriptor<{ + powerLevel: { + name: string; + value: import("../schema.js").Schema; + }; +}, { + SetPowerLevel: { + name: string; + payload: import("../schema.js").Schema; + }>>; + }; + AdjustPowerLevel: { + name: string; + payload: import("../schema.js").Schema; + }>>; + }; +}, {}, false>; diff --git a/dist/esm/registry/interfaces/PowerLevelController.js b/dist/esm/registry/interfaces/PowerLevelController.js new file mode 100644 index 0000000..4fd6dfa --- /dev/null +++ b/dist/esm/registry/interfaces/PowerLevelController.js @@ -0,0 +1,24 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; +/** The power of a device as a percentage, a dimmer switch for one (alexa-powerlevelcontroller.html). */ +export const PowerLevelController = defineInterface({ + namespace: "Alexa.PowerLevelController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-powerlevelcontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + powerLevel: { name: "powerLevel", value: s.number({ min: 0, max: 100, integer: true }) }, + }, + directives: { + SetPowerLevel: { + name: "SetPowerLevel", + payload: s.object({ powerLevel: s.number({ min: 0, max: 100, integer: true }) }), + }, + AdjustPowerLevel: { + name: "AdjustPowerLevel", + payload: s.object({ powerLevelDelta: s.number({ min: -100, max: 100, integer: true }) }), + }, + }, +}); diff --git a/dist/esm/registry/interfaces/stubs.js b/dist/esm/registry/interfaces/stubs.js index afb289b..ae3f2f7 100644 --- a/dist/esm/registry/interfaces/stubs.js +++ b/dist/esm/registry/interfaces/stubs.js @@ -18,11 +18,8 @@ const TABLE = [ ["Alexa.Camera.LiveViewController", "1.7", [], LIST_OF_INTERFACES], ["Alexa.CameraStreamController", "3", [], "alexa-camerastreamcontroller.html"], ["Alexa.ChannelController", "3", ["channel"], "alexa-channelcontroller.html"], - ["Alexa.ColorController", "3", ["color"], "alexa-colorcontroller.html"], - ["Alexa.ColorTemperatureController", "3", ["colorTemperatureInKelvin"], "alexa-colortemperaturecontroller.html"], ["Alexa.Commissionable", "1", [], "alexa-commissionable.html"], ["Alexa.ConsentManagement.ConsentRequiredReporter", "1", [], "alexa-consentrequiredreporter.html"], - ["Alexa.ContactSensor", "3", ["detectionState"], "alexa-contactsensor.html"], ["Alexa.Cooking", "1", [], "alexa-cooking.html"], ["Alexa.Cooking.FoodTemperatureController", "1", [], "alexa-cooking-foodtemperaturecontroller.html"], ["Alexa.Cooking.FoodTemperatureSensor", "1", [], "alexa-cooking-foodtemperaturesensor.html"], @@ -41,15 +38,11 @@ const TABLE = [ ["Alexa.InventoryUsageSensor", "1", [], "alexa-inventoryusagesensor.html"], ["Alexa.KeypadController", "1", [], "alexa-keypadcontroller.html"], ["Alexa.Launcher", "1.1", [], "alexa-launcher.html"], - ["Alexa.LockController", "3", [], "alexa-lockcontroller.html"], ["Alexa.Media.PlayQueue", "1", [], "alexa-media-playqueue.html"], ["Alexa.Media.Playback", "1", [], "alexa-media-playback.html"], ["Alexa.Media.Search", "1", [], "alexa-media-search.html"], - ["Alexa.MotionSensor", "3", [], "alexa-motionsensor.html"], - ["Alexa.PercentageController", "3", [], "alexa-percentagecontroller.html"], ["Alexa.PlaybackController", "3", [], "alexa-playbackcontroller.html"], ["Alexa.PlaybackStateReporter", "1", [], "alexa-playbackcontroller.html"], - ["Alexa.PowerLevelController", "3", [], "alexa-powerlevelcontroller.html"], ["Alexa.ProactiveNotificationSource", "1", [], "alexa-proactivenotificationsource.html"], ["Alexa.RTCSessionController", "1", [], "alexa-rtcsessioncontroller.html"], ["Alexa.RecordController", "3", [], "alexa-recordcontroller.html"], diff --git a/dist/types/compat/enums.d.ts b/dist/types/compat/enums.d.ts index e253d48..66b7107 100644 --- a/dist/types/compat/enums.d.ts +++ b/dist/types/compat/enums.d.ts @@ -27,6 +27,8 @@ export declare enum AlexaInterfaceType { DOORBELL_EVENT_SOURCE = "Alexa.DoorbellEventSource", ENDPOINT_HEALTH = "Alexa.EndpointHealth", EQUALIZER_CONTROLLER = "Alexa.EqualizerController", + /** Since 2.0. */ + HUMIDITY_SENSOR = "Alexa.HumiditySensor", INPUT_CONTROLLER = "Alexa.InputController", INVENTORY_LEVEL_SENSOR = "Alexa.InventoryLevelSensor", INVENTORY_LEVEL_USAGE_SENSOR = "Alexa.InventoryLevelUsageSensor", diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts index 8828d39..a1d0f1b 100644 --- a/dist/types/index.d.ts +++ b/dist/types/index.d.ts @@ -5,7 +5,7 @@ export type { EndpointDefinition, EndpointJson } from "./device/Device.js"; export { Capability } from "./device/Capability.js"; export type { CapabilityJson, CommonOptions, Declaration } from "./device/Capability.js"; export { registry, DeclarationError, SchemaError } from "./registry/index.js"; -export { Alexa, BrightnessController, ModeController, RangeController, TemperatureSensor, ToggleController, } from "./registry/index.js"; +export { Alexa, BrightnessController, ColorController, ColorTemperatureController, ContactSensor, HumiditySensor, LockController, ModeController, MotionSensor, PercentageController, PowerLevelController, RangeController, TemperatureSensor, ToggleController, } from "./registry/index.js"; export { PowerController, EndpointHealth } from "./compat/enums.js"; export { asset, text, semantics, SemanticsBuilder } from "./registry/index.js"; export { ASSETS as Assets, UNITS_OF_MEASURE as Units, ACTIONS as Actions, STATES as States, DISPLAY_CATEGORIES as DisplayCategories, } from "./registry/index.js"; diff --git a/dist/types/registry/index.d.ts b/dist/types/registry/index.d.ts index 701579b..7b7dccd 100644 --- a/dist/types/registry/index.d.ts +++ b/dist/types/registry/index.d.ts @@ -1,8 +1,16 @@ import { Alexa } from "./interfaces/Alexa.js"; import { BrightnessController } from "./interfaces/BrightnessController.js"; +import { ColorController } from "./interfaces/ColorController.js"; +import { ColorTemperatureController } from "./interfaces/ColorTemperatureController.js"; +import { ContactSensor } from "./interfaces/ContactSensor.js"; import { EndpointHealth } from "./interfaces/EndpointHealth.js"; +import { HumiditySensor } from "./interfaces/HumiditySensor.js"; +import { LockController } from "./interfaces/LockController.js"; import { ModeController } from "./interfaces/ModeController.js"; +import { MotionSensor } from "./interfaces/MotionSensor.js"; +import { PercentageController } from "./interfaces/PercentageController.js"; import { PowerController } from "./interfaces/PowerController.js"; +import { PowerLevelController } from "./interfaces/PowerLevelController.js"; import { RangeController } from "./interfaces/RangeController.js"; import { TemperatureSensor } from "./interfaces/TemperatureSensor.js"; import { ToggleController } from "./interfaces/ToggleController.js"; @@ -15,7 +23,7 @@ export declare const registry: { /** Every descriptor, ordered by namespace. */ list(): AnyDescriptor[]; }; -export { Alexa, BrightnessController, EndpointHealth, ModeController, PowerController, RangeController, TemperatureSensor, ToggleController, }; +export { Alexa, BrightnessController, ColorController, ColorTemperatureController, ContactSensor, EndpointHealth, HumiditySensor, LockController, ModeController, MotionSensor, PercentageController, PowerController, PowerLevelController, RangeController, TemperatureSensor, ToggleController, }; export type { Mode } from "./interfaces/ModeController.js"; export { asset, text } from "./resources.js"; export { semantics, SemanticsBuilder } from "./semantics.js"; diff --git a/dist/types/registry/interfaces/ColorController.d.ts b/dist/types/registry/interfaces/ColorController.d.ts new file mode 100644 index 0000000..4d7e663 --- /dev/null +++ b/dist/types/registry/interfaces/ColorController.d.ts @@ -0,0 +1,25 @@ +/** + * A light changes its hue and saturation and keeps the brightness it has: "ignore the brightness value of 1 in the + * directive" (alexa-colorcontroller.html, "SetColor directive example"). + */ +export declare const ColorController: import("../types.js").InterfaceDescriptor<{ + color: { + name: string; + value: import("../schema.js").Schema; + saturation: import("../schema.js").Schema; + brightness: import("../schema.js").Schema; + }>>; + }; +}, { + SetColor: { + name: string; + payload: import("../schema.js").Schema; + saturation: import("../schema.js").Schema; + brightness: import("../schema.js").Schema; + }>>; + }>>; + }; +}, {}, false>; diff --git a/dist/types/registry/interfaces/ColorTemperatureController.d.ts b/dist/types/registry/interfaces/ColorTemperatureController.d.ts new file mode 100644 index 0000000..3cd5124 --- /dev/null +++ b/dist/types/registry/interfaces/ColorTemperatureController.d.ts @@ -0,0 +1,28 @@ +/** + * The white of a tunable light. Alexa asks for 2200 (warm), 2700 (soft), 4000 (white), 5500 (daylight) and 7000 + * (cool); a light that cannot show the value takes the nearest it can. A light that shows a colour answers + * NOT_SUPPORTED_IN_CURRENT_MODE with currentDeviceMode COLOR. + */ +export declare const ColorTemperatureController: import("../types.js").InterfaceDescriptor<{ + colorTemperatureInKelvin: { + name: string; + value: import("../schema.js").Schema; + }; +}, { + SetColorTemperature: { + name: string; + payload: import("../schema.js").Schema; + }>>; + }; + IncreaseColorTemperature: { + name: string; + payload: import("../schema.js").Schema>; + note: string; + }; + DecreaseColorTemperature: { + name: string; + payload: import("../schema.js").Schema>; + note: string; + }; +}, {}, false>; diff --git a/dist/types/registry/interfaces/ContactSensor.d.ts b/dist/types/registry/interfaces/ContactSensor.d.ts new file mode 100644 index 0000000..af91060 --- /dev/null +++ b/dist/types/registry/interfaces/ContactSensor.d.ts @@ -0,0 +1,10 @@ +/** + * DETECTED is open: the two pieces of the sensor are apart. A sensor that cannot answer ReportState sends a + * ChangeReport right after discovery, so that the Alexa app shows its state (alexa-contactsensor.html). + */ +export declare const ContactSensor: import("../types.js").InterfaceDescriptor<{ + detectionState: { + name: string; + value: import("../schema.js").EnumSchema<"DETECTED" | "NOT_DETECTED">; + }; +}, {}, {}, false>; diff --git a/dist/types/registry/interfaces/HumiditySensor.d.ts b/dist/types/registry/interfaces/HumiditySensor.d.ts new file mode 100644 index 0000000..59d28aa --- /dev/null +++ b/dist/types/registry/interfaces/HumiditySensor.d.ts @@ -0,0 +1,9 @@ +/** The value is an object, { value: 45.5 }, as the reports of alexa-humiditysensor.html print it. */ +export declare const HumiditySensor: import("../types.js").InterfaceDescriptor<{ + relativeHumidity: { + name: string; + value: import("../schema.js").Schema; + }>>; + }; +}, {}, {}, false>; diff --git a/dist/types/registry/interfaces/LockController.d.ts b/dist/types/registry/interfaces/LockController.d.ts new file mode 100644 index 0000000..6ccc43c --- /dev/null +++ b/dist/types/registry/interfaces/LockController.d.ts @@ -0,0 +1,20 @@ +/** + * A lock that takes more than five seconds answers with a DeferredResponse first and the Response when the bolt has + * moved (alexa-lockcontroller.html, "Lock response"). The user enables unlocking by voice per device, in the Alexa app. + */ +export declare const LockController: import("../types.js").InterfaceDescriptor<{ + lockState: { + name: string; + value: import("../schema.js").EnumSchema<"LOCKED" | "UNLOCKED" | "JAMMED">; + note: string; + }; +}, { + Lock: { + name: string; + payload: import("../schema.js").Schema>; + }; + Unlock: { + name: string; + payload: import("../schema.js").Schema>; + }; +}, {}, false>; diff --git a/dist/types/registry/interfaces/MotionSensor.d.ts b/dist/types/registry/interfaces/MotionSensor.d.ts new file mode 100644 index 0000000..aab0454 --- /dev/null +++ b/dist/types/registry/interfaces/MotionSensor.d.ts @@ -0,0 +1,10 @@ +/** + * "When a motion sensor changes to DETECTED, and then changes to NOT_DETECTED, you must wait at least 30 seconds + * before sending the second change report" (alexa-motionsensor.html, "Change reporting"). + */ +export declare const MotionSensor: import("../types.js").InterfaceDescriptor<{ + detectionState: { + name: string; + value: import("../schema.js").EnumSchema<"DETECTED" | "NOT_DETECTED">; + }; +}, {}, {}, false>; diff --git a/dist/types/registry/interfaces/PercentageController.d.ts b/dist/types/registry/interfaces/PercentageController.d.ts new file mode 100644 index 0000000..7e8d00f --- /dev/null +++ b/dist/types/registry/interfaces/PercentageController.d.ts @@ -0,0 +1,23 @@ +/** + * For a device no other interface fits: "Use this interface only if there isn't a more specific controller + * interface that applies to your device" (alexa-percentagecontroller.html). + */ +export declare const PercentageController: import("../types.js").InterfaceDescriptor<{ + percentage: { + name: string; + value: import("../schema.js").Schema; + }; +}, { + SetPercentage: { + name: string; + payload: import("../schema.js").Schema; + }>>; + }; + AdjustPercentage: { + name: string; + payload: import("../schema.js").Schema; + }>>; + }; +}, {}, false>; diff --git a/dist/types/registry/interfaces/PowerLevelController.d.ts b/dist/types/registry/interfaces/PowerLevelController.d.ts new file mode 100644 index 0000000..ab5d437 --- /dev/null +++ b/dist/types/registry/interfaces/PowerLevelController.d.ts @@ -0,0 +1,20 @@ +/** The power of a device as a percentage, a dimmer switch for one (alexa-powerlevelcontroller.html). */ +export declare const PowerLevelController: import("../types.js").InterfaceDescriptor<{ + powerLevel: { + name: string; + value: import("../schema.js").Schema; + }; +}, { + SetPowerLevel: { + name: string; + payload: import("../schema.js").Schema; + }>>; + }; + AdjustPowerLevel: { + name: string; + payload: import("../schema.js").Schema; + }>>; + }; +}, {}, false>; diff --git a/src/compat/enums.ts b/src/compat/enums.ts index 405ce81..083ad3f 100644 --- a/src/compat/enums.ts +++ b/src/compat/enums.ts @@ -31,6 +31,8 @@ export enum AlexaInterfaceType { DOORBELL_EVENT_SOURCE = "Alexa.DoorbellEventSource", ENDPOINT_HEALTH = "Alexa.EndpointHealth", EQUALIZER_CONTROLLER = "Alexa.EqualizerController", + /** Since 2.0. */ + HUMIDITY_SENSOR = "Alexa.HumiditySensor", INPUT_CONTROLLER = "Alexa.InputController", INVENTORY_LEVEL_SENSOR = "Alexa.InventoryLevelSensor", INVENTORY_LEVEL_USAGE_SENSOR = "Alexa.InventoryLevelUsageSensor", diff --git a/src/index.ts b/src/index.ts index d683bd3..bd7d938 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,9 @@ export type { CapabilityJson, CommonOptions, Declaration } from "./device/Capabi // The interfaces, for device.add(), and the registry that holds them by name export { registry, DeclarationError, SchemaError } from "./registry/index.js"; export { - Alexa, BrightnessController, ModeController, RangeController, TemperatureSensor, ToggleController, + Alexa, BrightnessController, ColorController, ColorTemperatureController, ContactSensor, HumiditySensor, LockController, + ModeController, MotionSensor, PercentageController, PowerLevelController, RangeController, TemperatureSensor, + ToggleController, } from "./registry/index.js"; export { PowerController, EndpointHealth } from "./compat/enums.js"; export { asset, text, semantics, SemanticsBuilder } from "./registry/index.js"; diff --git a/src/registry/index.ts b/src/registry/index.ts index e7532e6..1ac46c7 100644 --- a/src/registry/index.ts +++ b/src/registry/index.ts @@ -1,9 +1,17 @@ // The interfaces the library knows, by namespace. import { Alexa } from "./interfaces/Alexa.js"; import { BrightnessController } from "./interfaces/BrightnessController.js"; +import { ColorController } from "./interfaces/ColorController.js"; +import { ColorTemperatureController } from "./interfaces/ColorTemperatureController.js"; +import { ContactSensor } from "./interfaces/ContactSensor.js"; import { EndpointHealth } from "./interfaces/EndpointHealth.js"; +import { HumiditySensor } from "./interfaces/HumiditySensor.js"; +import { LockController } from "./interfaces/LockController.js"; import { ModeController } from "./interfaces/ModeController.js"; +import { MotionSensor } from "./interfaces/MotionSensor.js"; +import { PercentageController } from "./interfaces/PercentageController.js"; import { PowerController } from "./interfaces/PowerController.js"; +import { PowerLevelController } from "./interfaces/PowerLevelController.js"; import { RangeController } from "./interfaces/RangeController.js"; import { TemperatureSensor } from "./interfaces/TemperatureSensor.js"; import { ToggleController } from "./interfaces/ToggleController.js"; @@ -14,9 +22,17 @@ import type { AnyDescriptor } from "./types.js"; const described: readonly AnyDescriptor[] = [ Alexa, BrightnessController, + ColorController, + ColorTemperatureController, + ContactSensor, EndpointHealth, + HumiditySensor, + LockController, ModeController, + MotionSensor, + PercentageController, PowerController, + PowerLevelController, RangeController, TemperatureSensor, ToggleController, @@ -49,8 +65,9 @@ export const registry = { }; export { - Alexa, BrightnessController, EndpointHealth, ModeController, PowerController, RangeController, TemperatureSensor, - ToggleController, + Alexa, BrightnessController, ColorController, ColorTemperatureController, ContactSensor, EndpointHealth, HumiditySensor, + LockController, ModeController, MotionSensor, PercentageController, PowerController, PowerLevelController, RangeController, + TemperatureSensor, ToggleController, }; export type { Mode } from "./interfaces/ModeController.js"; export { asset, text } from "./resources.js"; diff --git a/src/registry/interfaces/ColorController.ts b/src/registry/interfaces/ColorController.ts new file mode 100644 index 0000000..1e012cd --- /dev/null +++ b/src/registry/interfaces/ColorController.ts @@ -0,0 +1,28 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; + +// alexa-property-schemas.html, "Color" +const color = s.object({ + hue: s.number({ min: 0, max: 360 }), + saturation: s.number({ min: 0, max: 1 }), + brightness: s.number({ min: 0, max: 1 }), +}); + +/** + * A light changes its hue and saturation and keeps the brightness it has: "ignore the brightness value of 1 in the + * directive" (alexa-colorcontroller.html, "SetColor directive example"). + */ +export const ColorController = defineInterface({ + namespace: "Alexa.ColorController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-colorcontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + color: { name: "color", value: color }, + }, + directives: { + SetColor: { name: "SetColor", payload: s.object({ color }) }, + }, +}); diff --git a/src/registry/interfaces/ColorTemperatureController.ts b/src/registry/interfaces/ColorTemperatureController.ts new file mode 100644 index 0000000..a7dc7b7 --- /dev/null +++ b/src/registry/interfaces/ColorTemperatureController.ts @@ -0,0 +1,27 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; + +// alexa-colortemperaturecontroller.html, "Reportable properties": "Valid values range from 1,000 to 10,000" +const kelvin = s.number({ min: 1000, max: 10000, integer: true }); + +/** + * The white of a tunable light. Alexa asks for 2200 (warm), 2700 (soft), 4000 (white), 5500 (daylight) and 7000 + * (cool); a light that cannot show the value takes the nearest it can. A light that shows a colour answers + * NOT_SUPPORTED_IN_CURRENT_MODE with currentDeviceMode COLOR. + */ +export const ColorTemperatureController = defineInterface({ + namespace: "Alexa.ColorTemperatureController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-colortemperaturecontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + colorTemperatureInKelvin: { name: "colorTemperatureInKelvin", value: kelvin }, + }, + directives: { + SetColorTemperature: { name: "SetColorTemperature", payload: s.object({ colorTemperatureInKelvin: kelvin }) }, + IncreaseColorTemperature: { name: "IncreaseColorTemperature", payload: s.object({}), note: "towards cool" }, + DecreaseColorTemperature: { name: "DecreaseColorTemperature", payload: s.object({}), note: "towards warm" }, + }, +}); diff --git a/src/registry/interfaces/ContactSensor.ts b/src/registry/interfaces/ContactSensor.ts new file mode 100644 index 0000000..bdaba24 --- /dev/null +++ b/src/registry/interfaces/ContactSensor.ts @@ -0,0 +1,19 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; + +/** + * DETECTED is open: the two pieces of the sensor are apart. A sensor that cannot answer ReportState sends a + * ChangeReport right after discovery, so that the Alexa app shows its state (alexa-contactsensor.html). + */ +export const ContactSensor = defineInterface({ + namespace: "Alexa.ContactSensor", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-contactsensor.html", + kind: "sensor", + tier: 1, + instanced: false, + properties: { + detectionState: { name: "detectionState", value: s.enum("DETECTED", "NOT_DETECTED") }, + }, + directives: {}, +}); diff --git a/src/registry/interfaces/HumiditySensor.ts b/src/registry/interfaces/HumiditySensor.ts new file mode 100644 index 0000000..180f37a --- /dev/null +++ b/src/registry/interfaces/HumiditySensor.ts @@ -0,0 +1,16 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; + +/** The value is an object, { value: 45.5 }, as the reports of alexa-humiditysensor.html print it. */ +export const HumiditySensor = defineInterface({ + namespace: "Alexa.HumiditySensor", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-humiditysensor.html", + kind: "sensor", + tier: 1, + instanced: false, + properties: { + relativeHumidity: { name: "relativeHumidity", value: s.object({ value: s.number({ min: 0, max: 100 }) }) }, + }, + directives: {}, +}); diff --git a/src/registry/interfaces/LockController.ts b/src/registry/interfaces/LockController.ts new file mode 100644 index 0000000..9efae32 --- /dev/null +++ b/src/registry/interfaces/LockController.ts @@ -0,0 +1,27 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; + +/** + * A lock that takes more than five seconds answers with a DeferredResponse first and the Response when the bolt has + * moved (alexa-lockcontroller.html, "Lock response"). The user enables unlocking by voice per device, in the Alexa app. + */ +export const LockController = defineInterface({ + namespace: "Alexa.LockController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-lockcontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + lockState: { + name: "lockState", + value: s.enum("LOCKED", "UNLOCKED", "JAMMED"), + note: "JAMMED: the mechanism can reach neither LOCKED nor UNLOCKED", + }, + }, + directives: { + Lock: { name: "Lock", payload: s.object({}) }, + Unlock: { name: "Unlock", payload: s.object({}) }, + }, + deferrable: true, +}); diff --git a/src/registry/interfaces/MotionSensor.ts b/src/registry/interfaces/MotionSensor.ts new file mode 100644 index 0000000..401a5fe --- /dev/null +++ b/src/registry/interfaces/MotionSensor.ts @@ -0,0 +1,19 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; + +/** + * "When a motion sensor changes to DETECTED, and then changes to NOT_DETECTED, you must wait at least 30 seconds + * before sending the second change report" (alexa-motionsensor.html, "Change reporting"). + */ +export const MotionSensor = defineInterface({ + namespace: "Alexa.MotionSensor", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-motionsensor.html", + kind: "sensor", + tier: 1, + instanced: false, + properties: { + detectionState: { name: "detectionState", value: s.enum("DETECTED", "NOT_DETECTED") }, + }, + directives: {}, +}); diff --git a/src/registry/interfaces/PercentageController.ts b/src/registry/interfaces/PercentageController.ts new file mode 100644 index 0000000..5eef136 --- /dev/null +++ b/src/registry/interfaces/PercentageController.ts @@ -0,0 +1,28 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; + +/** + * For a device no other interface fits: "Use this interface only if there isn't a more specific controller + * interface that applies to your device" (alexa-percentagecontroller.html). + */ +export const PercentageController = defineInterface({ + namespace: "Alexa.PercentageController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-percentagecontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + percentage: { name: "percentage", value: s.number({ min: 0, max: 100, integer: true }) }, + }, + directives: { + SetPercentage: { + name: "SetPercentage", + payload: s.object({ percentage: s.number({ min: 0, max: 100, integer: true }) }), + }, + AdjustPercentage: { + name: "AdjustPercentage", + payload: s.object({ percentageDelta: s.number({ min: -100, max: 100, integer: true }) }), + }, + }, +}); diff --git a/src/registry/interfaces/PowerLevelController.ts b/src/registry/interfaces/PowerLevelController.ts new file mode 100644 index 0000000..ac6325e --- /dev/null +++ b/src/registry/interfaces/PowerLevelController.ts @@ -0,0 +1,25 @@ +import { s } from "../schema.js"; +import { defineInterface } from "../types.js"; + +/** The power of a device as a percentage, a dimmer switch for one (alexa-powerlevelcontroller.html). */ +export const PowerLevelController = defineInterface({ + namespace: "Alexa.PowerLevelController", + version: "3", + doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-powerlevelcontroller.html", + kind: "controller", + tier: 1, + instanced: false, + properties: { + powerLevel: { name: "powerLevel", value: s.number({ min: 0, max: 100, integer: true }) }, + }, + directives: { + SetPowerLevel: { + name: "SetPowerLevel", + payload: s.object({ powerLevel: s.number({ min: 0, max: 100, integer: true }) }), + }, + AdjustPowerLevel: { + name: "AdjustPowerLevel", + payload: s.object({ powerLevelDelta: s.number({ min: -100, max: 100, integer: true }) }), + }, + }, +}); diff --git a/src/registry/interfaces/stubs.ts b/src/registry/interfaces/stubs.ts index 09266fa..3b26020 100644 --- a/src/registry/interfaces/stubs.ts +++ b/src/registry/interfaces/stubs.ts @@ -24,11 +24,8 @@ const TABLE: readonly Row[] = [ ["Alexa.Camera.LiveViewController", "1.7", [], LIST_OF_INTERFACES], ["Alexa.CameraStreamController", "3", [], "alexa-camerastreamcontroller.html"], ["Alexa.ChannelController", "3", ["channel"], "alexa-channelcontroller.html"], - ["Alexa.ColorController", "3", ["color"], "alexa-colorcontroller.html"], - ["Alexa.ColorTemperatureController", "3", ["colorTemperatureInKelvin"], "alexa-colortemperaturecontroller.html"], ["Alexa.Commissionable", "1", [], "alexa-commissionable.html"], ["Alexa.ConsentManagement.ConsentRequiredReporter", "1", [], "alexa-consentrequiredreporter.html"], - ["Alexa.ContactSensor", "3", ["detectionState"], "alexa-contactsensor.html"], ["Alexa.Cooking", "1", [], "alexa-cooking.html"], ["Alexa.Cooking.FoodTemperatureController", "1", [], "alexa-cooking-foodtemperaturecontroller.html"], ["Alexa.Cooking.FoodTemperatureSensor", "1", [], "alexa-cooking-foodtemperaturesensor.html"], @@ -47,15 +44,11 @@ const TABLE: readonly Row[] = [ ["Alexa.InventoryUsageSensor", "1", [], "alexa-inventoryusagesensor.html"], ["Alexa.KeypadController", "1", [], "alexa-keypadcontroller.html"], ["Alexa.Launcher", "1.1", [], "alexa-launcher.html"], - ["Alexa.LockController", "3", [], "alexa-lockcontroller.html"], ["Alexa.Media.PlayQueue", "1", [], "alexa-media-playqueue.html"], ["Alexa.Media.Playback", "1", [], "alexa-media-playback.html"], ["Alexa.Media.Search", "1", [], "alexa-media-search.html"], - ["Alexa.MotionSensor", "3", [], "alexa-motionsensor.html"], - ["Alexa.PercentageController", "3", [], "alexa-percentagecontroller.html"], ["Alexa.PlaybackController", "3", [], "alexa-playbackcontroller.html"], ["Alexa.PlaybackStateReporter", "1", [], "alexa-playbackcontroller.html"], - ["Alexa.PowerLevelController", "3", [], "alexa-powerlevelcontroller.html"], ["Alexa.ProactiveNotificationSource", "1", [], "alexa-proactivenotificationsource.html"], ["Alexa.RTCSessionController", "1", [], "alexa-rtcsessioncontroller.html"], ["Alexa.RecordController", "3", [], "alexa-recordcontroller.html"], diff --git a/test/device/discovery.test.js b/test/device/discovery.test.js index 2742c20..bfa41e3 100644 --- a/test/device/discovery.test.js +++ b/test/device/discovery.test.js @@ -131,6 +131,23 @@ test("1.x declarations are announced as 1.5.2 announced them, with the Alexa int } }); +test("1.x declarations of the zoo's lock, motion sensor and heater list their properties, which 1.5.2 left to the caller", () => { + const T = AlexaInterfaceType; + const declarations = { + "zoo-lock": [T.LOCK_CONTROLLER], + "zoo-motion": [T.MOTION_SENSOR], + "zoo-heater": [T.POWER_CONTROLLER, T.POWER_LEVEL_CONTROLLER], + "zoo-color-lamp": [T.POWER_CONTROLLER, T.BRIGHTNESS_CONTROLLER, T.COLOR_CONTROLLER], + "zoo-warm-lamp": [T.POWER_CONTROLLER, T.COLOR_TEMPERATURE_CONTROLLER], + }; + for (const [endpointId, types] of Object.entries(declarations)) { + const device = likeTheZoo(endpointId); + for (const type of types) device.addCapability(type, { proactivelyReported: true }); + assert.deepEqual(device.getJSON().capabilities, [...zoo(endpointId).capabilities, ALEXA], endpointId); + assert.deepEqual(device.check(), [], endpointId); + } +}); + test("a RangeController blind declared with device.add() is the zoo's blind, whose JSON was written by hand", () => { const blinds = likeTheZoo("zoo-blinds"); blinds.add(RangeController, { @@ -329,16 +346,16 @@ test("discovery logs what check() says about the devices, each line once", async const lamp = bridge.registerDevice("Dave's Lamp", "lamp-1", DisplayCategory.LIGHT); lamp.addCapability(AlexaInterfaceType.POWER_CONTROLLER); lamp.addCapability(AlexaInterfaceType.POWER_CONTROLLER); - lamp.addCapability(AlexaInterfaceType.LOCK_CONTROLLER); + lamp.addCapability(AlexaInterfaceType.KEYPAD_CONTROLLER); bridge.registerDevice("Lamp", "lamp-2", DisplayCategory.LIGHT).addCapability(AlexaInterfaceType.POWER_CONTROLLER); // Announced all the same: 1.x announced it, and Alexa may have taken it const [first] = await discover(alexa, "root"); - assert.deepEqual(names(first), ["Alexa.PowerController", "Alexa.PowerController", "Alexa.LockController", "Alexa"]); + assert.deepEqual(names(first), ["Alexa.PowerController", "Alexa.PowerController", "Alexa.KeypadController", "Alexa"]); assert.deepEqual(warnings(), [ 'warning: lamp-1: the name "Dave\'s Lamp" takes up to 256 letters, digits and spaces, no punctuation', "warning: lamp-1: Alexa.PowerController: the interface is declared twice", - "warning: lamp-1: Alexa.LockController: no property list known, discovery lists it with no supported properties", + "warning: lamp-1: Alexa.KeypadController: no property list known, discovery lists it with no supported properties", ]); lamp.setName("Daves Lamp"); diff --git a/test/device/validate.test.js b/test/device/validate.test.js index 8f71cd9..c4fed23 100644 --- a/test/device/validate.test.js +++ b/test/device/validate.test.js @@ -295,12 +295,12 @@ test("a phrase is mapped once on an endpoint, across its Range, Mode and Toggle test("a stub is declared like any interface and checked for nothing but being declared twice", () => { const lock = endpoint("lock-1", "Front Door", ["SMARTLOCK"]); - const capability = lock.add(registry.get("Alexa.LockController"), { proactivelyReported: true }); + const capability = lock.add(registry.get("Alexa.KeypadController"), { proactivelyReported: true }); assert.deepEqual(capability.toJSON(), { type: "AlexaInterface", - interface: "Alexa.LockController", - version: "3", + interface: "Alexa.KeypadController", + version: "1", properties: { supported: [], proactivelyReported: true, retrievable: true }, }); - assert.equal(refused(() => lock.add(registry.get("Alexa.LockController"))).message, "lock-1: Alexa.LockController: the interface is declared twice"); + assert.equal(refused(() => lock.add(registry.get("Alexa.KeypadController"))).message, "lock-1: Alexa.KeypadController: the interface is declared twice"); }); diff --git a/test/dispatch/interfaces.test.js b/test/dispatch/interfaces.test.js new file mode 100644 index 0000000..c6560e3 --- /dev/null +++ b/test/dispatch/interfaces.test.js @@ -0,0 +1,82 @@ +"use strict"; +// The interfaces of test/fixtures/interfaces.js, given the directives of their pages: the handler gets the payload as +// the page prints it, and a payload that does not fit is answered with INVALID_VALUE without the handler. +const { test } = require("node:test"); +const assert = require("node:assert/strict"); +const { Alex2MQTT, LockController, MemoryPublisher, PowerLevelController, registry } = require("alex2node"); +const interfaces = require("../fixtures/interfaces.js"); +const { doc, examples } = require("../helpers/fixtures.js"); +const { directive } = require("../helpers/harness.js"); + +function bridgeWithout() { + const sent = new MemoryPublisher(); + const logged = []; + const bridge = new Alex2MQTT("u", "p", "root", false, { publisher: sent, log: (line) => logged.push(line) }); + const device = bridge.addDevice({ endpointId: "device-1", name: "Device", categories: ["OTHER"] }); + let count = 0; + // The examples of a page share one message id and one correlation token + const receive = ({ header, payload }) => { + count += 1; + const unique = { ...header, messageId: `message-${count}`, correlationToken: `ct-${count}` }; + const message = { header: unique, endpoint: { endpointId: "device-1" }, payload }; + return bridge.receive("root/device-1/alexaDirective", JSON.stringify(message)); + }; + /** What was published, as [the last part of the topic, the namespace and the name of the event, its payload]. */ + const answers = () => sent.published.map(({ topic, message: { event } }) => [topic.split("/").pop(), event.header.namespace, event.header.name, event.payload]); + return { device, sent, logged, receive, answers }; +} + +for (const { namespace, page, declared, refused } of interfaces.filter((entry) => entry.refused)) { + test(`${namespace}: the directives of ${page}.html reach the handler, a payload that does not fit is answered with INVALID_VALUE`, async () => { + const { device, receive, answers } = bridgeWithout(); + const seen = []; + device.add(registry.get(namespace), declared[0].options).on("*", (ctx) => { + seen.push([ctx.name, ctx.payload]); + return ctx.respond(); + }); + + const printed = examples(page, ".directive").map((name) => doc(page, name).directive); + for (const example of printed) await receive(example); + assert.deepEqual(seen, printed.map(({ header, payload }) => [header.name, payload])); + assert.deepEqual(answers(), printed.map(() => ["alexaResponce", "Alexa", "Response", {}])); + + for (const [name, payload] of refused) await receive(directive(namespace, name, "device-1", "ct-1", payload)); + assert.deepEqual( + answers().slice(printed.length), + refused.map(([, , message]) => ["alexaResponce", "Alexa", "ErrorResponse", { type: "INVALID_VALUE", message }]) + ); + assert.equal(seen.length, printed.length, "the handler is not called with a payload that does not fit"); + }); +} + +test("Alexa.LockController: a slow lock defers without a warning, and answers as the page does", async () => { + const { device, sent, logged, receive, answers } = bridgeWithout(); + const lock = device.add(LockController); + lock.on("Lock", async (ctx) => { + await ctx.defer(15); + return ctx.respond((s) => s.set(lock, "lockState", "LOCKED")); + }); + const level = device.add(PowerLevelController); + level.on("SetPowerLevel", async (ctx) => { + await ctx.defer(); + return ctx.respond((s) => s.set(level, "powerLevel", ctx.payload.powerLevel)); + }); + + await receive(doc("alexa-lockcontroller", "Lock.directive").directive); + const deferred = doc("alexa-lockcontroller", "Lock-slow.deferred"); + const answer = doc("alexa-lockcontroller", "Lock-slow.response"); + assert.deepEqual(answers(), [ + ["alexaResponce", "Alexa", "DeferredResponse", deferred.event.payload], + ["deferredResponse", "Alexa", "Response", answer.event.payload], + ]); + assert.equal("context" in sent.published[0].message, false); + const [{ namespace, name, value }] = answer.context.properties; + const reported = sent.published[1].message.context.properties.find((property) => property.namespace === namespace); + assert.deepEqual([reported.name, reported.value], [name, value]); + + // The page of the PowerLevelController has no DeferredResponse + await receive(doc("alexa-powerlevelcontroller", "SetPowerLevel.directive").directive); + assert.deepEqual(logged.filter((line) => line.includes("DeferredResponse")), [ + "warning: Amazon documents no DeferredResponse for Alexa.PowerLevelController, Alexa may not wait for the answer", + ]); +}); diff --git a/test/fixtures/alexa-docs/alexa-colorcontroller/ChangeReport.json b/test/fixtures/alexa-docs/alexa-colorcontroller/ChangeReport.json new file mode 100644 index 0000000..2e01eef --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colorcontroller/ChangeReport.json @@ -0,0 +1,46 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "ChangeReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": { + "change": { + "cause": { + "type": "PHYSICAL_INTERACTION" + }, + "properties": [ + { + "namespace": "Alexa.ColorController", + "name": "color", + "value": { + "hue": 280, + "saturation": 0.9, + "brightness": 0.9 + }, + "timeOfSample": "2024-02-03T16:17:00.00Z", + "uncertaintyInMilliseconds": 500 + } + ] + } + } + }, + "context": { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2024-02-03T16:15:00.00Z", + "uncertaintyInMilliseconds": 0 + } +} diff --git a/test/fixtures/alexa-docs/alexa-colorcontroller/SetColor.directive.json b/test/fixtures/alexa-docs/alexa-colorcontroller/SetColor.directive.json new file mode 100644 index 0000000..570ae0c --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colorcontroller/SetColor.directive.json @@ -0,0 +1,26 @@ +{ + "directive": { + "header": { + "namespace": "Alexa.ColorController", + "name": "SetColor", + "messageId": "Unique version 4 UUID", + "correlationToken": "Opaque correlation token", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID", + "cookie": {} + }, + "payload": { + "color": { + "hue": 350.5, + "saturation": 0.7138, + "brightness": 0.6524 + } + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-colorcontroller/SetColor.response.json b/test/fixtures/alexa-docs/alexa-colorcontroller/SetColor.response.json new file mode 100644 index 0000000..df00610 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colorcontroller/SetColor.response.json @@ -0,0 +1,34 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.ColorController", + "name": "color", + "value": { + "hue": 350.5, + "saturation": 0.7138, + "brightness": 0.6524 + }, + "timeOfSample": "2019-07-03T16:20:50Z", + "uncertaintyInMilliseconds": 500 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-colorcontroller/StateReport.json b/test/fixtures/alexa-docs/alexa-colorcontroller/StateReport.json new file mode 100644 index 0000000..f813354 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colorcontroller/StateReport.json @@ -0,0 +1,34 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "StateReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.ColorController", + "name": "color", + "value": { + "hue": 350.5, + "saturation": 0.7138, + "brightness": 0.6524 + }, + "timeOfSample": "2019-07-03T16:20:50Z", + "uncertaintyInMilliseconds": 500 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-colorcontroller/discovery.json b/test/fixtures/alexa-docs/alexa-colorcontroller/discovery.json new file mode 100644 index 0000000..1379fea --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colorcontroller/discovery.json @@ -0,0 +1,78 @@ +{ + "event": { + "header": { + "namespace": "Alexa.Discovery", + "name": "Discover.Response", + "payloadVersion": "3", + "messageId": "Unique identifier, preferably a version 4 UUID" + }, + "payload": { + "endpoints": [ + { + "endpointId": "Unique ID of the endpoint", + "manufacturerName": "Manufacturer of the endpoint", + "description": "Description to be shown in the Alexa app", + "friendlyName": "Living Room light", + "displayCategories": [ + "LIGHT" + ], + "additionalAttributes": { + "manufacturer": "Manufacturer of the endpoint", + "model": "Model of the device", + "customIdentifier": "Optional custom identifier for the device" + }, + "cookie": {}, + "capabilities": [ + { + "type": "AlexaInterface", + "interface": "Alexa.PowerController", + "version": "3", + "properties": { + "supported": [ + { + "name": "powerState" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.ColorController", + "version": "3", + "properties": { + "supported": [ + { + "name": "color" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.EndpointHealth", + "version": "3", + "properties": { + "supported": [ + { + "name": "connectivity" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa", + "version": "3" + } + ] + } + ] + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/ChangeReport.json b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/ChangeReport.json new file mode 100644 index 0000000..9156a68 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/ChangeReport.json @@ -0,0 +1,46 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "ChangeReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": { + "change": { + "cause": { + "type": "PHYSICAL_INTERACTION" + }, + "properties": [ + { + "namespace": "Alexa.ColorTemperatureController", + "name": "colorTemperatureInKelvin", + "value": 4000, + "timeOfSample": "2024-02-03T16:1519:20.00Z", + "uncertaintyInMilliseconds": 0 + } + ] + } + } + }, + "context": { + "properties": [ + { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2024-02-03T16:15:00.00Z", + "uncertaintyInMilliseconds": 100 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/DecreaseColorTemperature.directive.json b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/DecreaseColorTemperature.directive.json new file mode 100644 index 0000000..b130db6 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/DecreaseColorTemperature.directive.json @@ -0,0 +1,20 @@ +{ + "directive": { + "header": { + "namespace": "Alexa.ColorTemperatureController", + "name": "DecreaseColorTemperature", + "messageId": "Unique version 4 UUID", + "correlationToken": "Opaque correlation token", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID", + "cookie": {} + }, + "payload": {} + } +} diff --git a/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/DecreaseColorTemperature.response.json b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/DecreaseColorTemperature.response.json new file mode 100644 index 0000000..4135ac6 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/DecreaseColorTemperature.response.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.ColorTemperatureController", + "name": "colorTemperatureInKelvin", + "value": 2200, + "timeOfSample": "2020-02-03T16:21:10.02Z", + "uncertaintyInMilliseconds": 500 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/IncreaseColorTemperature.directive.json b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/IncreaseColorTemperature.directive.json new file mode 100644 index 0000000..8ae86b0 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/IncreaseColorTemperature.directive.json @@ -0,0 +1,20 @@ +{ + "directive": { + "header": { + "namespace": "Alexa.ColorTemperatureController", + "name": "IncreaseColorTemperature", + "messageId": "Unique version 4 UUID", + "correlationToken": "Opaque correlation token", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID", + "cookie": {} + }, + "payload": {} + } +} diff --git a/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/IncreaseColorTemperature.response.json b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/IncreaseColorTemperature.response.json new file mode 100644 index 0000000..3ccc08a --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/IncreaseColorTemperature.response.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.ColorTemperatureController", + "name": "colorTemperatureInKelvin", + "value": 7000, + "timeOfSample": "2020-02-03T16:21:10.02Z", + "uncertaintyInMilliseconds": 500 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/SetColorTemperature.directive.json b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/SetColorTemperature.directive.json new file mode 100644 index 0000000..a5da809 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/SetColorTemperature.directive.json @@ -0,0 +1,22 @@ +{ + "directive": { + "header": { + "namespace": "Alexa.ColorTemperatureController", + "name": "SetColorTemperature", + "messageId": "Unique version 4 UUID", + "correlationToken": "Opaque correlation token", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID", + "cookie": {} + }, + "payload": { + "colorTemperatureInKelvin": 5500 + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/SetColorTemperature.response.json b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/SetColorTemperature.response.json new file mode 100644 index 0000000..13dc7e3 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/SetColorTemperature.response.json @@ -0,0 +1,39 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.ColorTemperatureController", + "name": "colorTemperatureInKelvin", + "value": 5500, + "timeOfSample": "2020-02-03T16:21:10.02Z", + "uncertaintyInMilliseconds": 500 + }, + { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2020-02-03T16:20:50Z", + "uncertaintyInMilliseconds": 0 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/StateReport.json b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/StateReport.json new file mode 100644 index 0000000..d2af0b1 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/StateReport.json @@ -0,0 +1,39 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "StateReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.ColorTemperatureController", + "name": "colorTemperatureInKelvin", + "value": 2200, + "timeOfSample": "2024-02-03T16:18:10.00Z", + "uncertaintyInMilliseconds": 0 + }, + { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2024-02-03T16:15:00.00Z", + "uncertaintyInMilliseconds": 100 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/discovery.json b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/discovery.json new file mode 100644 index 0000000..ddc9aa1 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-colortemperaturecontroller/discovery.json @@ -0,0 +1,78 @@ +{ + "event": { + "header": { + "namespace": "Alexa.Discovery", + "name": "Discover.Response", + "payloadVersion": "3", + "messageId": "Unique identifier, preferably a version 4 UUID" + }, + "payload": { + "endpoints": [ + { + "endpointId": "Unique ID of the endpoint", + "manufacturerName": "Manufacturer of the endpoint", + "description": "Description to be shown in the Alexa app", + "friendlyName": "Device name, displayed in the Alexa app", + "displayCategories": [ + "LIGHT" + ], + "additionalAttributes": { + "manufacturer": "Manufacturer of the endpoint", + "model": "Model of the device", + "customIdentifier": "Optional custom identifier for the device" + }, + "cookie": {}, + "capabilities": [ + { + "type": "AlexaInterface", + "interface": "Alexa.PowerController", + "version": "3", + "properties": { + "supported": [ + { + "name": "powerState" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.ColorTemperatureController", + "version": "3", + "properties": { + "supported": [ + { + "name": "colorTemperatureInKelvin" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.EndpointHealth", + "version": "3.1", + "properties": { + "supported": [ + { + "name": "connectivity" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa", + "version": "3" + } + ] + } + ] + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-contactsensor/ChangeReport.json b/test/fixtures/alexa-docs/alexa-contactsensor/ChangeReport.json new file mode 100644 index 0000000..da0e59a --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-contactsensor/ChangeReport.json @@ -0,0 +1,46 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "ChangeReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": { + "change": { + "cause": { + "type": "PHYSICAL_INTERACTION" + }, + "properties": [ + { + "namespace": "Alexa.ContactSensor", + "name": "detectionState", + "value": "DETECTED", + "timeOfSample": "2018-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 0 + } + ] + } + } + }, + "context": { + "properties": [ + { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2017-02-03T16:08:15.02Z", + "uncertaintyInMilliseconds": 0 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-contactsensor/StateReport.json b/test/fixtures/alexa-docs/alexa-contactsensor/StateReport.json new file mode 100644 index 0000000..1774914 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-contactsensor/StateReport.json @@ -0,0 +1,39 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "StateReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.ContactSensor", + "name": "detectionState", + "value": "NOT_DETECTED", + "timeOfSample": "2017-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 0 + }, + { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2017-02-03T16:08:15.02Z", + "uncertaintyInMilliseconds": 0 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-contactsensor/discovery.json b/test/fixtures/alexa-docs/alexa-contactsensor/discovery.json new file mode 100644 index 0000000..58d88de --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-contactsensor/discovery.json @@ -0,0 +1,59 @@ +{ + "event": { + "header": { + "namespace": "Alexa.Discovery", + "name": "Discover.Response", + "payloadVersion": "3", + "messageId": "Unique identifier, preferably a version 4 UUID" + }, + "payload": { + "endpoints": [ + { + "endpointId": "Unique ID of the endpoint", + "manufacturerName": "Manufacturer of the endpoint", + "description": "Smart Sensor by Sensor Maker", + "friendlyName": "Bedroom window", + "displayCategories": [ + "CONTACT_SENSOR" + ], + "cookie": {}, + "capabilities": [ + { + "type": "AlexaInterface", + "interface": "Alexa.ContactSensor", + "version": "3", + "properties": { + "supported": [ + { + "name": "detectionState" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.EndpointHealth", + "version": "3", + "properties": { + "supported": [ + { + "name": "connectivity" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa", + "version": "3" + } + ] + } + ] + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-humiditysensor/ChangeReport.json b/test/fixtures/alexa-docs/alexa-humiditysensor/ChangeReport.json new file mode 100644 index 0000000..437be36 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-humiditysensor/ChangeReport.json @@ -0,0 +1,58 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "ChangeReport", + "messageId": "a unique identifier, preferably a version 4 UUID", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "an OAuth2 bearer token" + }, + "endpointId": "endpoint ID" + }, + "payload": { + "change": { + "cause": { + "type": "PERIODIC_POLL" + }, + "properties": [ + { + "namespace": "Alexa.HumiditySensor", + "name": "relativeHumidity", + "value": { + "value": 92.5 + }, + "timeOfSample": "2023-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 0 + } + ] + } + } + }, + "context": { + "properties": [ + { + "namespace": "Alexa.TemperatureSensor", + "name": "temperature", + "value": { + "value": 19.9, + "scale": "CELSIUS" + }, + "timeOfSample": "2023-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 0 + }, + { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2022-06-03T16:20:50Z", + "uncertaintyInMilliseconds": 0 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-humiditysensor/StateReport.json b/test/fixtures/alexa-docs/alexa-humiditysensor/StateReport.json new file mode 100644 index 0000000..c0cb3f8 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-humiditysensor/StateReport.json @@ -0,0 +1,51 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "StateReport", + "messageId": "a unique identifier, preferably a version 4 UUID", + "correlationToken": "an opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "an OAuth2 bearer token" + }, + "endpointId": "endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.HumiditySensor", + "name": "relativeHumidity", + "value": { + "value": 89 + }, + "timeOfSample": "2023-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 0 + }, + { + "namespace": "Alexa.TemperatureSensor", + "name": "temperature", + "value": { + "value": 19.9, + "scale": "CELSIUS" + }, + "timeOfSample": "2023-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 0 + }, + { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2023-02-03T16:20:50Z", + "uncertaintyInMilliseconds": 1000 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-humiditysensor/discovery.json b/test/fixtures/alexa-docs/alexa-humiditysensor/discovery.json new file mode 100644 index 0000000..be3eed7 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-humiditysensor/discovery.json @@ -0,0 +1,80 @@ +{ + "event": { + "header": { + "namespace": "Alexa.Discovery", + "name": "Discover.Response", + "payloadVersion": "3", + "messageId": "Unique identifier, preferably a version 4 UUID" + }, + "payload": { + "endpoints": [ + { + "endpointId": "Unique ID of the endpoint", + "manufacturerName": "Manufacturer name of the endpoint", + "description": "Description to be shown in the Alexa app", + "friendlyName": "Weather station", + "displayCategories": [ + "TEMPERATURE_SENSOR" + ], + "additionalAttributes": { + "manufacturer": "Manufacturer name of the endpoint", + "model": "Model of the device", + "serialNumber": "Serial number of the device", + "firmwareVersion": "Firmware version of the device", + "softwareVersion": "Software version of the device", + "customIdentifier": "Optional custom identifier for the device" + }, + "capabilities": [ + { + "type": "AlexaInterface", + "interface": "Alexa.HumiditySensor", + "version": "3", + "properties": { + "supported": [ + { + "name": "relativeHumidity" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.TemperatureSensor", + "version": "3", + "properties": { + "supported": [ + { + "name": "temperature" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.EndpointHealth", + "version": "3.2", + "properties": { + "supported": [ + { + "name": "connectivity" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa", + "version": "3" + } + ] + } + ] + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/ChangeReport.json b/test/fixtures/alexa-docs/alexa-lockcontroller/ChangeReport.json new file mode 100644 index 0000000..f704aa7 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/ChangeReport.json @@ -0,0 +1,42 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "ChangeReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": { + "change": { + "cause": { + "type": "PHYSICAL_INTERACTION" + }, + "properties": [ + { + "namespace": "Alexa.LockController", + "name": "lockState", + "value": "LOCKED", + "timeOfSample": "2017-02-03T16:20:50Z", + "uncertaintyInMilliseconds": 1000 + } + ] + } + } + }, + "context": { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2017-02-03T06:18:00Z", + "uncertaintyInMilliseconds": 0 + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/Lock-slow.deferred.json b/test/fixtures/alexa-docs/alexa-lockcontroller/Lock-slow.deferred.json new file mode 100644 index 0000000..1d02610 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/Lock-slow.deferred.json @@ -0,0 +1,14 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "DeferredResponse", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "payload": { + "estimatedDeferralInSeconds": 15 + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/Lock-slow.response.json b/test/fixtures/alexa-docs/alexa-lockcontroller/Lock-slow.response.json new file mode 100644 index 0000000..dfd39f6 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/Lock-slow.response.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.LockController", + "name": "lockState", + "value": "LOCKED", + "timeOfSample": "2017-02-03T16:20:00.00Z", + "uncertaintyInMilliseconds": 1000 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/Lock.directive.json b/test/fixtures/alexa-docs/alexa-lockcontroller/Lock.directive.json new file mode 100644 index 0000000..2776cd0 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/Lock.directive.json @@ -0,0 +1,20 @@ +{ + "directive": { + "header": { + "namespace": "Alexa.LockController", + "name": "Lock", + "messageId": "Unique version 4 UUID", + "correlationToken": "Opaque correlation token", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID", + "cookie": {} + }, + "payload": {} + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/Lock.response.json b/test/fixtures/alexa-docs/alexa-lockcontroller/Lock.response.json new file mode 100644 index 0000000..dfd39f6 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/Lock.response.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.LockController", + "name": "lockState", + "value": "LOCKED", + "timeOfSample": "2017-02-03T16:20:00.00Z", + "uncertaintyInMilliseconds": 1000 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/StateReport.json b/test/fixtures/alexa-docs/alexa-lockcontroller/StateReport.json new file mode 100644 index 0000000..de4a627 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/StateReport.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "StateReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.LockController", + "name": "lockState", + "value": "JAMMED", + "timeOfSample": "2017-02-03T16:20:00.00Z", + "uncertaintyInMilliseconds": 1000 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock-slow.deferred.json b/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock-slow.deferred.json new file mode 100644 index 0000000..8f9fba2 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock-slow.deferred.json @@ -0,0 +1,14 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "DeferredResponse", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "payload": { + "estimatedDeferralInSeconds": 20 + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock-slow.response.json b/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock-slow.response.json new file mode 100644 index 0000000..96bfa20 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock-slow.response.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.LockController", + "name": "lockState", + "value": "UNLOCKED", + "timeOfSample": "2017-02-03T16:20:00.00Z", + "uncertaintyInMilliseconds": 1000 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock.directive.json b/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock.directive.json new file mode 100644 index 0000000..a648105 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock.directive.json @@ -0,0 +1,20 @@ +{ + "directive": { + "header": { + "namespace": "Alexa.LockController", + "name": "Unlock", + "messageId": "Unique version 4 UUID", + "correlationToken": "Opaque correlation token", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID", + "cookie": {} + }, + "payload": {} + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock.response.json b/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock.response.json new file mode 100644 index 0000000..96bfa20 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/Unlock.response.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.LockController", + "name": "lockState", + "value": "UNLOCKED", + "timeOfSample": "2017-02-03T16:20:00.00Z", + "uncertaintyInMilliseconds": 1000 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-lockcontroller/discovery.json b/test/fixtures/alexa-docs/alexa-lockcontroller/discovery.json new file mode 100644 index 0000000..e494622 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-lockcontroller/discovery.json @@ -0,0 +1,59 @@ +{ + "event": { + "header": { + "namespace": "Alexa.Discovery", + "name": "Discover.Response", + "payloadVersion": "3", + "messageId": "Unique identifier, preferably a version 4 UUID" + }, + "payload": { + "endpoints": [ + { + "endpointId": "Unique ID of the endpoint", + "manufacturerName": "Manufacturer of the endpoint", + "description": "Description to be shown in the Alexa app", + "friendlyName": "Your device name, displayed in the Alexa app, for example Front Door>", + "displayCategories": [ + "SMARTLOCK" + ], + "cookie": {}, + "capabilities": [ + { + "type": "AlexaInterface", + "interface": "Alexa.LockController", + "version": "3", + "properties": { + "supported": [ + { + "name": "lockState" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.EndpointHealth", + "version": "3", + "properties": { + "supported": [ + { + "name": "connectivity" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa", + "version": "3" + } + ] + } + ] + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-motionsensor/ChangeReport.json b/test/fixtures/alexa-docs/alexa-motionsensor/ChangeReport.json new file mode 100644 index 0000000..7b33239 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-motionsensor/ChangeReport.json @@ -0,0 +1,46 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "ChangeReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": { + "change": { + "cause": { + "type": "PHYSICAL_INTERACTION" + }, + "properties": [ + { + "namespace": "Alexa.MotionSensor", + "name": "detectionState", + "value": "DETECTED", + "timeOfSample": "2024-07-03T10:20:50.52Z", + "uncertaintyInMilliseconds": 0 + } + ] + } + } + }, + "context": { + "properties": [ + { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2024-07-03T10:11:50.52Z", + "uncertaintyInMilliseconds": 0 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-motionsensor/StateReport.json b/test/fixtures/alexa-docs/alexa-motionsensor/StateReport.json new file mode 100644 index 0000000..59d867d --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-motionsensor/StateReport.json @@ -0,0 +1,39 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "StateReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.MotionSensor", + "name": "detectionState", + "value": "NOT_DETECTED", + "timeOfSample": "2024-07-03T10:20:50.52Z", + "uncertaintyInMilliseconds": 0 + }, + { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2024-07-03T09:56:50.12Z", + "uncertaintyInMilliseconds": 0 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-motionsensor/discovery.json b/test/fixtures/alexa-docs/alexa-motionsensor/discovery.json new file mode 100644 index 0000000..abc58c4 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-motionsensor/discovery.json @@ -0,0 +1,59 @@ +{ + "event": { + "header": { + "namespace": "Alexa.Discovery", + "name": "Discover.Response", + "payloadVersion": "3", + "messageId": "Unique identifier, preferably a version 4 UUID" + }, + "payload": { + "endpoints": [ + { + "endpointId": "Unique ID of the endpoint", + "manufacturerName": "Manufacturer of the endpoint", + "description": "Smart Sensor by Sensor Maker", + "friendlyName": "Front door", + "displayCategories": [ + "MOTION_SENSOR" + ], + "cookie": {}, + "capabilities": [ + { + "type": "AlexaInterface", + "interface": "Alexa.MotionSensor", + "version": "3", + "properties": { + "supported": [ + { + "name": "detectionState" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.EndpointHealth", + "version": "3.1", + "properties": { + "supported": [ + { + "name": "connectivity" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa", + "version": "3" + } + ] + } + ] + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-percentagecontroller/AdjustPercentage.directive.json b/test/fixtures/alexa-docs/alexa-percentagecontroller/AdjustPercentage.directive.json new file mode 100644 index 0000000..0fdc691 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-percentagecontroller/AdjustPercentage.directive.json @@ -0,0 +1,22 @@ +{ + "directive": { + "header": { + "namespace": "Alexa.PercentageController", + "name": "AdjustPercentage", + "messageId": "Unique version 4 UUID", + "correlationToken": "Opaque correlation token", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID", + "cookie": {} + }, + "payload": { + "percentageDelta": -20 + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-percentagecontroller/AdjustPercentage.response.json b/test/fixtures/alexa-docs/alexa-percentagecontroller/AdjustPercentage.response.json new file mode 100644 index 0000000..e530120 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-percentagecontroller/AdjustPercentage.response.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.PercentageController", + "name": "percentage", + "value": 54, + "timeOfSample": "2017-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 500 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-percentagecontroller/ChangeReport.json b/test/fixtures/alexa-docs/alexa-percentagecontroller/ChangeReport.json new file mode 100644 index 0000000..aadab2f --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-percentagecontroller/ChangeReport.json @@ -0,0 +1,42 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "ChangeReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": { + "change": { + "cause": { + "type": "PHYSICAL_INTERACTION" + }, + "properties": [ + { + "namespace": "Alexa.PercentageController", + "name": "percentage", + "value": 65, + "timeOfSample": "2024-05-01T19:20:05.05Z", + "uncertaintyInMilliseconds": 0 + } + ] + } + } + }, + "context": { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2024-05-01T19:10:22.05Z", + "uncertaintyInMilliseconds": 0 + } +} diff --git a/test/fixtures/alexa-docs/alexa-percentagecontroller/SetPercentage.directive.json b/test/fixtures/alexa-docs/alexa-percentagecontroller/SetPercentage.directive.json new file mode 100644 index 0000000..6914606 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-percentagecontroller/SetPercentage.directive.json @@ -0,0 +1,22 @@ +{ + "directive": { + "header": { + "namespace": "Alexa.PercentageController", + "name": "SetPercentage", + "messageId": "Unique version 4 UUID", + "correlationToken": "Opaque correlation token", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID", + "cookie": {} + }, + "payload": { + "percentage": 74 + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-percentagecontroller/SetPercentage.response.json b/test/fixtures/alexa-docs/alexa-percentagecontroller/SetPercentage.response.json new file mode 100644 index 0000000..5065035 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-percentagecontroller/SetPercentage.response.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.PercentageController", + "name": "percentage", + "value": 74, + "timeOfSample": "2017-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 500 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-percentagecontroller/StateReport.json b/test/fixtures/alexa-docs/alexa-percentagecontroller/StateReport.json new file mode 100644 index 0000000..da360a3 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-percentagecontroller/StateReport.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "StateReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.PercentageController", + "name": "percentage", + "value": 30, + "timeOfSample": "2017-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 0 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-percentagecontroller/discovery.json b/test/fixtures/alexa-docs/alexa-percentagecontroller/discovery.json new file mode 100644 index 0000000..d046301 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-percentagecontroller/discovery.json @@ -0,0 +1,64 @@ +{ + "event": { + "header": { + "namespace": "Alexa.Discovery", + "name": "Discover.Response", + "payloadVersion": "3", + "messageId": "Unique identifier, preferably a version 4 UUID" + }, + "payload": { + "endpoints": [ + { + "endpointId": "Unique ID of the endpoint", + "manufacturerName": "Manufacturer of the endpoint", + "description": "Description to be shown in the Alexa app", + "friendlyName": "Device name, displayed in the Alexa app", + "additionalAttributes": { + "manufacturer": "Manufacturer of the endpoint", + "model": "Model of the device", + "customIdentifier": "Optional custom identifier for the device" + }, + "displayCategories": [ + "OTHER" + ], + "cookie": {}, + "capabilities": [ + { + "type": "AlexaInterface", + "interface": "Alexa.PercentageController", + "version": "3", + "properties": { + "supported": [ + { + "name": "percentage" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.EndpointHealth", + "version": "3", + "properties": { + "supported": [ + { + "name": "connectivity" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa", + "version": "3" + } + ] + } + ] + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-powerlevelcontroller/AdjustPowerLevel.directive.json b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/AdjustPowerLevel.directive.json new file mode 100644 index 0000000..3f00713 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/AdjustPowerLevel.directive.json @@ -0,0 +1,22 @@ +{ + "directive": { + "header": { + "namespace": "Alexa.PowerLevelController", + "name": "AdjustPowerLevel", + "messageId": "Unique version 4 UUID", + "correlationToken": "Opaque correlation token", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID", + "cookie": {} + }, + "payload": { + "powerLevelDelta": 12 + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-powerlevelcontroller/AdjustPowerLevel.response.json b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/AdjustPowerLevel.response.json new file mode 100644 index 0000000..aac3aeb --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/AdjustPowerLevel.response.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.PowerLevelController", + "name": "powerLevel", + "value": 52, + "timeOfSample": "2017-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 500 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-powerlevelcontroller/ChangeReport.json b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/ChangeReport.json new file mode 100644 index 0000000..0d234f7 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/ChangeReport.json @@ -0,0 +1,42 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "ChangeReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": { + "change": { + "cause": { + "type": "PHYSICAL_INTERACTION" + }, + "properties": [ + { + "namespace": "Alexa.PowerLevelController", + "name": "powerLevel", + "value": 75, + "timeOfSample": "2024-05-01T09:32:05.05Z", + "uncertaintyInMilliseconds": 0 + } + ] + } + } + }, + "context": { + "namespace": "Alexa.EndpointHealth", + "name": "connectivity", + "value": { + "value": "OK" + }, + "timeOfSample": "2024-05-01T09:25:45.25Z", + "uncertaintyInMilliseconds": 0 + } +} diff --git a/test/fixtures/alexa-docs/alexa-powerlevelcontroller/SetPowerLevel.directive.json b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/SetPowerLevel.directive.json new file mode 100644 index 0000000..e6b26bb --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/SetPowerLevel.directive.json @@ -0,0 +1,22 @@ +{ + "directive": { + "header": { + "namespace": "Alexa.PowerLevelController", + "name": "SetPowerLevel", + "messageId": "Unique version 4 UUID", + "correlationToken": "Opaque correlation token", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID", + "cookie": {} + }, + "payload": { + "powerLevel": 40 + } + } +} diff --git a/test/fixtures/alexa-docs/alexa-powerlevelcontroller/SetPowerLevel.response.json b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/SetPowerLevel.response.json new file mode 100644 index 0000000..dad2c8a --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/SetPowerLevel.response.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "Response", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.PowerLevelController", + "name": "powerLevel", + "value": 40, + "timeOfSample": "2017-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 500 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-powerlevelcontroller/StateReport.json b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/StateReport.json new file mode 100644 index 0000000..50d0d07 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/StateReport.json @@ -0,0 +1,30 @@ +{ + "event": { + "header": { + "namespace": "Alexa", + "name": "StateReport", + "messageId": "Unique identifier, preferably a version 4 UUID", + "correlationToken": "Opaque correlation token that matches the request", + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": "OAuth2.0 bearer token" + }, + "endpointId": "Endpoint ID" + }, + "payload": {} + }, + "context": { + "properties": [ + { + "namespace": "Alexa.PowerLevelController", + "name": "powerLevel", + "value": 52, + "timeOfSample": "2017-02-03T16:20:50.52Z", + "uncertaintyInMilliseconds": 0 + } + ] + } +} diff --git a/test/fixtures/alexa-docs/alexa-powerlevelcontroller/discovery.json b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/discovery.json new file mode 100644 index 0000000..74bcff1 --- /dev/null +++ b/test/fixtures/alexa-docs/alexa-powerlevelcontroller/discovery.json @@ -0,0 +1,81 @@ +{ + "event": { + "header": { + "namespace": "Alexa.Discovery", + "name": "Discover.Response", + "payloadVersion": "3", + "messageId": "Unique identifier, preferably a version 4 UUID" + }, + "payload": { + "endpoints": [ + { + "endpointId": "Unique ID of the endpoint", + "manufacturerName": "Manufacturer of the endpoint", + "description": "Description to be shown in the Alexa app", + "friendlyName": "Living Room Light", + "displayCategories": [ + "LIGHT" + ], + "additionalAttributes": { + "manufacturer": "Manufacturer of the endpoint", + "model": "Model of the device", + "serialNumber": "Serial number of the device", + "firmwareVersion": "Firmware version of the device", + "softwareVersion": "Software version of the device", + "customIdentifier": "Optional custom identifier for the device" + }, + "cookie": {}, + "capabilities": [ + { + "type": "AlexaInterface", + "interface": "Alexa.PowerLevelController", + "version": "3", + "properties": { + "supported": [ + { + "name": "powerLevel" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.ColorController", + "version": "3", + "properties": { + "supported": [ + { + "name": "color" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa.EndpointHealth", + "version": "3", + "properties": { + "supported": [ + { + "name": "connectivity" + } + ], + "proactivelyReported": true, + "retrievable": true + } + }, + { + "type": "AlexaInterface", + "interface": "Alexa", + "version": "3" + } + ] + } + ] + } + } +} diff --git a/test/fixtures/interfaces.js b/test/fixtures/interfaces.js index 079f187..6f061dc 100644 --- a/test/fixtures/interfaces.js +++ b/test/fixtures/interfaces.js @@ -9,20 +9,74 @@ // page of the interface. The pages of RangeController and ModeController fold their discovery // examples away, so they were not saved: the range is taken from an example of the ToggleController // page, and ModeController has none. +// refused [[directive, payload, message]]: a payload the directive does not take, and the message of the +// INVALID_VALUE that answers it +// unfit [[property, value]]: a value the property does not take // -// test/registry/descriptors.test.js and test/device/discovery.test.js run the entries. The first one fails for a -// descriptor of tier 1 or 2 that has no entry. +// test/registry/descriptors.test.js, test/device/discovery.test.js and test/dispatch/interfaces.test.js run the +// entries. The first one fails for a descriptor of tier 1 or 2 that has no entry. const { asset, semantics, text } = require("alex2node"); const reported = { retrievable: true, proactivelyReported: true }; +const declared = [{ options: reported, example: "discovery" }]; const inThreeLanguages = (english, spanish, french) => [text(english, "en-US"), text(spanish, "es-MX"), text(french, "fr-CA")]; module.exports = [ { namespace: "Alexa", page: "alexa-interface", declared: [{ options: {}, example: "discovery" }] }, { namespace: "Alexa.BrightnessController", page: "alexa-brightnesscontroller", declared: [{ options: reported, example: "discovery" }] }, + { + namespace: "Alexa.ColorController", + page: "alexa-colorcontroller", + declared, + refused: [ + ["SetColor", { color: { hue: 361, saturation: 1, brightness: 1 } }, "payload.color.hue: expected a number from 0 to 360, got 361"], + ["SetColor", { color: "red" }, 'payload.color: expected an object with hue, saturation, brightness, got "red"'], + ], + unfit: [["color", { hue: 120, saturation: 50, brightness: 1 }], ["color", { hue: 120, saturation: 1 }]], + }, + { + namespace: "Alexa.ColorTemperatureController", + page: "alexa-colortemperaturecontroller", + declared, + refused: [ + ["SetColorTemperature", { colorTemperatureInKelvin: 900 }, "payload.colorTemperatureInKelvin: expected an integer from 1000 to 10000, got 900"], + ["IncreaseColorTemperature", "a lot", 'payload: expected an object, got "a lot"'], + ], + unfit: [["colorTemperatureInKelvin", 10001], ["colorTemperatureInKelvin", "warm"]], + }, + { namespace: "Alexa.ContactSensor", page: "alexa-contactsensor", declared, unfit: [["detectionState", "OPEN"], ["detectionState", true]] }, { namespace: "Alexa.EndpointHealth", page: "alexa-endpointhealth", declared: [{ options: reported, example: "discovery" }] }, + { namespace: "Alexa.HumiditySensor", page: "alexa-humiditysensor", declared, unfit: [["relativeHumidity", 45], ["relativeHumidity", { value: 101 }]] }, + { + namespace: "Alexa.LockController", + page: "alexa-lockcontroller", + declared, + refused: [["Lock", "now", 'payload: expected an object, got "now"']], + unfit: [["lockState", "OPEN"], ["lockState", "locked"]], + }, { namespace: "Alexa.ModeController", page: "alexa-modecontroller", declared: [] }, + { namespace: "Alexa.MotionSensor", page: "alexa-motionsensor", declared, unfit: [["detectionState", "MOTION"], ["detectionState", 1]] }, + { + namespace: "Alexa.PercentageController", + page: "alexa-percentagecontroller", + declared, + refused: [ + ["SetPercentage", { percentage: 101 }, "payload.percentage: expected an integer from 0 to 100, got 101"], + ["AdjustPercentage", { percentageDelta: 0.5 }, "payload.percentageDelta: expected an integer from -100 to 100, got 0.5"], + ], + unfit: [["percentage", -1], ["percentage", 50.5]], + }, { namespace: "Alexa.PowerController", page: "alexa-powercontroller", declared: [{ options: reported, example: "discovery" }] }, + { + namespace: "Alexa.PowerLevelController", + page: "alexa-powerlevelcontroller", + declared, + refused: [ + ["SetPowerLevel", { powerLevel: "half" }, 'payload.powerLevel: expected an integer from 0 to 100, got "half"'], + ["AdjustPowerLevel", {}, "payload.powerLevelDelta: expected an integer from -100 to 100, got nothing"], + ], + unfit: [["powerLevel", 101], ["powerLevel", null]], + }, { namespace: "Alexa.RangeController", page: "alexa-rangecontroller", diff --git a/test/registry/descriptors.test.js b/test/registry/descriptors.test.js index a0cd7d6..6b12615 100644 --- a/test/registry/descriptors.test.js +++ b/test/registry/descriptors.test.js @@ -3,7 +3,7 @@ // and every interface name of 1.x resolves. const { test } = require("node:test"); const assert = require("node:assert/strict"); -const { registry, AlexaInterface, AlexaInterfaceType, DeclarationError } = require("alex2node"); +const { registry, AlexaInterface, AlexaInterfaceType, DeclarationError, SchemaError } = require("alex2node"); const interfaces = require("../fixtures/interfaces.js"); const { doc, examples, capabilities, reported } = require("../helpers/fixtures.js"); @@ -30,7 +30,7 @@ test("a descriptor of tier 1 or 2 has an entry in test/fixtures/interfaces.js, a assert.deepEqual(described.map((descriptor) => descriptor.namespace), interfaces.map((entry) => entry.namespace).sort()); }); -for (const { namespace, page, declared } of interfaces) { +for (const { namespace, page, declared, unfit = [] } of interfaces) { test(`${namespace}: version, properties, directive payloads and property values of ${page}.html`, () => { const descriptor = registry.get(namespace); assert.ok(descriptor.doc.endsWith(`/${page}.html`), descriptor.doc); @@ -65,6 +65,9 @@ for (const { namespace, page, declared } of interfaces) { assert.deepEqual(known.value.parse(property.value, property.name), property.value, example); } if (Object.keys(descriptor.properties).length > 0) assert.ok(values.length > 0, "the page reports no property of the interface"); + for (const [name, value] of unfit) { + assert.throws(() => descriptor.properties[name].value.parse(value, name), SchemaError, `${name} ${JSON.stringify(value)}`); + } }); } @@ -116,9 +119,10 @@ test("registry.get: the namespace and the AlexaInterfaceType member give the sam assert.throws(() => registry.get(AlexaInterfaceType.UNKNOWN), DeclarationError); }); -test("every AlexaInterfaceType of 1.x is in the registry: described, or a stub with the version and properties of 1.5.2", () => { +test("every AlexaInterfaceType is in the registry: described, or a stub with the version and properties of 1.5.2", () => { const names = Object.values(AlexaInterfaceType).filter((name) => name !== AlexaInterfaceType.UNKNOWN); - assert.equal(names.length, 69); + // The 69 of 1.x and HUMIDITY_SENSOR + assert.equal(names.length, 70); assert.deepEqual(names.filter((name) => !registry.has(name)), []); // "Alexa" is the one interface that was never in the enum assert.deepEqual(registry.list().map((descriptor) => descriptor.namespace).filter((name) => !names.includes(name)), ["Alexa"]);