registry: SceneController events, DoorbellEventSource, SimpleEventSource, TimeHoldController, InventoryLevelSensor, WakeOnLANController
Six descriptors written from their pages replace the last stubs of tiers 1 and 2. A scene answers Activate and Deactivate through ctx.respond() with ActivationStarted and DeactivationStarted, the time and the cause filled in. device.raise(descriptor, name, payload) publishes DoorbellPress and the Event of a button on <root>/event with the endpoint and a new messageId; it throws a MessageError for an interface or instance the device did not declare, an event that answers a directive, a payload that does not fit and a message over 16000 bytes. TurnOn of a device with WakeOnLANController is deferred without the warning. On the wire: a doorbell has no properties object and proactivelyReported on the capability; SimpleEventSource is version 1.0, InventoryLevelSensor and WakeOnLANController version 3 (1.5.2: 1). A scene declared without options is announced as before. Alex2MQTT has no topic yet for the WakeUp event. 24 examples of the six pages are saved as fixtures. 267 tests pass, 241 before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
d7dfc5b122
commit
3a07c861b1
116 changed files with 3136 additions and 166 deletions
|
|
@ -4,8 +4,8 @@
|
|||
const { test } = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const {
|
||||
Alex2MQTT, AlexaErrors, LockController, MemoryPublisher, PowerLevelController, SecurityPanelController,
|
||||
ThermostatController, ThermostatControllerSchedule, registry,
|
||||
Alex2MQTT, AlexaErrors, LockController, MemoryPublisher, PowerController, PowerLevelController, SceneController,
|
||||
SecurityPanelController, ThermostatController, TimeHoldController, WakeOnLANController, ThermostatControllerSchedule, registry,
|
||||
} = require("alex2node");
|
||||
const interfaces = require("../fixtures/interfaces.js");
|
||||
const { doc, examples } = require("../helpers/fixtures.js");
|
||||
|
|
@ -181,3 +181,70 @@ test("Alexa.SecurityPanelController: a payload that is no Arm.Response is not se
|
|||
message: "payload.exitDelayInSeconds: expected an integer from 0 to 255, got 300",
|
||||
}]]);
|
||||
});
|
||||
|
||||
test("Alexa.SceneController: respond() answers Activate with ActivationStarted and Deactivate with DeactivationStarted", async () => {
|
||||
const sent = new MemoryPublisher();
|
||||
const bridge = new Alex2MQTT("u", "p", "root", false, { publisher: sent, log: () => {} });
|
||||
const scenes = ["party", "bedtime"].map((endpointId) => bridge.addDevice({
|
||||
endpointId, name: endpointId, categories: ["SCENE_TRIGGER"], description: `The ${endpointId} scene`,
|
||||
}));
|
||||
scenes[0].add(SceneController).on("*", (ctx) => ctx.respond());
|
||||
scenes[1].add(SceneController, { supportsDeactivation: false }).on("*", (ctx) => ctx.respond(undefined, { payload: { timestamp: "2017-02-03T16:20:50Z" } }));
|
||||
|
||||
const before = Date.now();
|
||||
let count = 0;
|
||||
for (const [endpointId, name] of [["party", "Activate"], ["party", "Deactivate"], ["bedtime", "Activate"], ["bedtime", "Deactivate"]]) {
|
||||
count += 1;
|
||||
const { header, payload } = doc("alexa-scenecontroller", `${name}.directive`).directive;
|
||||
const message = { header: { ...header, messageId: `message-${count}`, correlationToken: `ct-${count}` }, endpoint: { endpointId }, payload };
|
||||
await bridge.receive(`root/${endpointId}/alexaDirective`, JSON.stringify(message));
|
||||
}
|
||||
const events = sent.published.map(({ message }) => message.event);
|
||||
assert.deepEqual(sent.published.map(({ topic }) => topic), ["root/party/alexaResponce", "root/party/alexaResponce", "root/bedtime/alexaResponce", "root/bedtime/alexaResponce"]);
|
||||
assert.deepEqual(events.map(({ header }) => [header.namespace, header.name, header.correlationToken]), [
|
||||
["Alexa.SceneController", "ActivationStarted", "ct-1"],
|
||||
["Alexa.SceneController", "DeactivationStarted", "ct-2"],
|
||||
["Alexa.SceneController", "ActivationStarted", "ct-3"],
|
||||
["Alexa", "ErrorResponse", "ct-4"],
|
||||
]);
|
||||
assert.deepEqual(events[2].payload, doc("alexa-scenecontroller", "ActivationStarted.event").event.payload);
|
||||
assert.equal(events[3].payload.message, "Alexa.SceneController as bedtime declares it does not take Deactivate");
|
||||
for (const { payload } of events.slice(0, 2)) {
|
||||
assert.deepEqual(payload.cause, { type: "VOICE_INTERACTION" });
|
||||
assert.ok(Date.parse(payload.timestamp) >= before && Date.parse(payload.timestamp) <= Date.now(), payload.timestamp);
|
||||
}
|
||||
});
|
||||
|
||||
test("Alexa.TimeHoldController: Resume reaches a device declared with allowRemoteResume only", async () => {
|
||||
const { device, receive, answers } = bridgeWithout();
|
||||
device.add(TimeHoldController, { allowRemoteResume: false }).on("*", (ctx) => ctx.respond());
|
||||
await receive(doc("alexa-timeholdcontroller", "Hold.directive").directive);
|
||||
await receive(doc("alexa-timeholdcontroller", "Resume.directive").directive);
|
||||
assert.deepEqual(answers(), [
|
||||
["alexaResponce", "Alexa", "Response", {}],
|
||||
["alexaResponce", "Alexa", "ErrorResponse", {
|
||||
type: "INVALID_DIRECTIVE",
|
||||
message: "Alexa.TimeHoldController as device-1 declares it does not take Resume",
|
||||
}],
|
||||
]);
|
||||
});
|
||||
|
||||
test("Alexa.WakeOnLANController: TurnOn is deferred without a warning, TurnOff with the one of the PowerController", async () => {
|
||||
const { device, logged, receive, answers } = bridgeWithout();
|
||||
device.add(WakeOnLANController, { macAddresses: ["00-14-22-01-23-45"] });
|
||||
const power = device.add(PowerController);
|
||||
power.on("*", async (ctx) => {
|
||||
await ctx.defer(15);
|
||||
return ctx.respond((s) => s.set(power, "powerState", ctx.name === "TurnOn" ? "ON" : "OFF"));
|
||||
});
|
||||
await receive(doc("alexa-wakeonlancontroller", "TurnOn.directive").directive);
|
||||
assert.deepEqual(answers(), [
|
||||
["alexaResponce", "Alexa", "DeferredResponse", doc("alexa-wakeonlancontroller", "TurnOn.deferred").event.payload],
|
||||
["deferredResponse", "Alexa", "Response", doc("alexa-wakeonlancontroller", "TurnOn.response").event.payload],
|
||||
]);
|
||||
const warnings = () => logged.filter((line) => line.includes("DeferredResponse"));
|
||||
assert.deepEqual(warnings(), []);
|
||||
|
||||
await receive(doc("alexa-powercontroller", "TurnOff.directive").directive);
|
||||
assert.deepEqual(warnings(), ["warning: Amazon documents no DeferredResponse for Alexa.PowerController, Alexa may not wait for the answer"]);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue