Alexa.SecurityPanelController is described from its page: version 3 (the stub of 1.5.2 said 1), armState and the four alarms, Arm and Disarm. ctx.respond() answers Arm with Arm.Response under the namespace of the interface, its payload checked (exitDelayInSeconds 0 to 255, bypassedEndpoints), and Disarm with a Response. A Disarm carries the PIN the user said when the panel is declared with supportedAuthorizationTypes; the handler checks it and answers UNAUTHORIZED, which goes under Alexa.SecurityPanelController like the five other error types. A panel declared without options lists armState, where 1.5.2 listed no property; alarms are declared. 17 examples of the two pages are saved as fixtures. 215 tests pass, 207 before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
91 lines
5.2 KiB
JavaScript
91 lines
5.2 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.SecurityPanelController = void 0;
|
|
const schema_js_1 = require("../schema.js");
|
|
const types_js_1 = require("../types.js");
|
|
const ARM_STATES = ["ARMED_AWAY", "ARMED_STAY", "ARMED_NIGHT", "DISARMED"];
|
|
const ALARMS = ["burglaryAlarm", "fireAlarm", "carbonMonoxideAlarm", "waterAlarm"];
|
|
const alarm = schema_js_1.s.object({ value: schema_js_1.s.enum("OK", "ALARM") });
|
|
// A sensor that is left out when the panel arms. The endpointId is for a sensor that is an endpoint of its own.
|
|
const bypassed = schema_js_1.s.object({ friendlyName: schema_js_1.s.optional(schema_js_1.s.string({ min: 1 })), endpointId: schema_js_1.s.optional(schema_js_1.s.string({ min: 1 })) });
|
|
const armResponse = schema_js_1.s.object({
|
|
/** How long the occupants have to leave before the panel arms. 0: it arms at once. */
|
|
exitDelayInSeconds: schema_js_1.s.optional(schema_js_1.s.number({ min: 0, max: 255, integer: true })),
|
|
/** For an Arm that came with bypassType only. */
|
|
bypassedEndpoints: schema_js_1.s.optional(schema_js_1.s.array(bypassed)),
|
|
});
|
|
/**
|
|
* A security panel. Arm is answered with Arm.Response, Disarm with a Response, an error of errorTypes under the
|
|
* namespace of the interface (alexa-securitypanelcontroller-errorresponse.html).
|
|
*
|
|
* Alexa sends Disarm only when the user switched on disarming by voice in the Alexa app. A panel declared with
|
|
* supportedAuthorizationTypes gets the PIN the user said in payload.authorization and has to check it: a wrong PIN
|
|
* is answered with UNAUTHORIZED. Without authorization in the payload Alexa checked a voice code of its own.
|
|
*
|
|
* A panel that is ARMED_AWAY is disarmed before it is armed another way: Arm to ARMED_STAY or ARMED_NIGHT is
|
|
* then answered with AUTHORIZATION_REQUIRED. Arm to the state the panel is in is answered with Arm.Response.
|
|
*/
|
|
exports.SecurityPanelController = (0, types_js_1.defineInterface)({
|
|
namespace: "Alexa.SecurityPanelController",
|
|
version: "3",
|
|
doc: "https://developer.amazon.com/docs/alexaplus/device-apis/alexa-securitypanelcontroller.html",
|
|
kind: "controller",
|
|
tier: 2,
|
|
instanced: false,
|
|
properties: {
|
|
armState: { name: "armState", value: schema_js_1.s.enum(...ARM_STATES) },
|
|
burglaryAlarm: { name: "burglaryAlarm", value: alarm },
|
|
fireAlarm: { name: "fireAlarm", value: alarm },
|
|
carbonMonoxideAlarm: { name: "carbonMonoxideAlarm", value: alarm },
|
|
waterAlarm: { name: "waterAlarm", value: alarm },
|
|
},
|
|
directives: {
|
|
Arm: {
|
|
name: "Arm",
|
|
payload: schema_js_1.s.object({ armState: schema_js_1.s.enum(...ARM_STATES), bypassType: schema_js_1.s.optional(schema_js_1.s.enum("BYPASS_ALL")) }),
|
|
note: "bypassType: the user said to arm with the open sensors left out, which BYPASS_NEEDED listed",
|
|
},
|
|
Disarm: {
|
|
name: "Disarm",
|
|
payload: schema_js_1.s.object({
|
|
authorization: schema_js_1.s.optional(schema_js_1.s.object({
|
|
type: schema_js_1.s.enum("FOUR_DIGIT_PIN"),
|
|
value: schema_js_1.s.string({ pattern: /^\d{4}$/, expects: "a PIN of four digits" }),
|
|
})),
|
|
}),
|
|
note: "authorization: the PIN the user said, for the panel to check",
|
|
},
|
|
},
|
|
options: schema_js_1.s.object({
|
|
/** Default: Alexa takes the panel to have all four. */
|
|
supportedArmStates: schema_js_1.s.optional(schema_js_1.s.array(schema_js_1.s.enum(...ARM_STATES), { min: 1 })),
|
|
/** For a panel that has four digit PINs and checks the PIN of a Disarm itself. */
|
|
supportedAuthorizationTypes: schema_js_1.s.optional(schema_js_1.s.array(schema_js_1.s.enum("FOUR_DIGIT_PIN"), { min: 1 })),
|
|
/** The alarms the panel reports. Default: none. */
|
|
alarms: schema_js_1.s.optional(schema_js_1.s.array(schema_js_1.s.enum(...ALARMS))),
|
|
}, { unknownKeys: "reject" }),
|
|
discovery({ options: { supportedArmStates, supportedAuthorizationTypes, alarms = [] } }) {
|
|
const configuration = {
|
|
...(supportedArmStates && { supportedArmStates: supportedArmStates.map((value) => ({ value })) }),
|
|
...(supportedAuthorizationTypes && { supportedAuthorizationTypes: supportedAuthorizationTypes.map((type) => ({ type })) }),
|
|
};
|
|
return {
|
|
supported: ["armState", ...alarms],
|
|
...(Object.keys(configuration).length > 0 && { configuration }),
|
|
};
|
|
},
|
|
validate(capability) {
|
|
const { supportedArmStates = [], supportedAuthorizationTypes = [], alarms = [] } = capability.options;
|
|
const lists = [supportedArmStates, supportedAuthorizationTypes, alarms];
|
|
for (const list of lists) {
|
|
const twice = list.find((entry, i) => list.indexOf(entry) !== i);
|
|
if (twice)
|
|
throw new types_js_1.DeclarationError(capability, `${twice} is listed twice`);
|
|
}
|
|
},
|
|
responseFor: (directive) => (directive === "Arm"
|
|
? { namespace: "Alexa.SecurityPanelController", name: "Arm.Response", payload: armResponse }
|
|
: undefined),
|
|
errorNamespace: "Alexa.SecurityPanelController",
|
|
errorTypes: ["AUTHORIZATION_REQUIRED", "BYPASS_NEEDED", "NOT_READY", "UNAUTHORIZED", "UNCLEARED_ALARM", "UNCLEARED_TROUBLE"],
|
|
});
|