Ten recipes: lamp, colour lamp, thermostat, blind, lock (deferred), contact sensor (ChangeReport), scene, doorbell (raise), typed errors, and a plug as an ES module. Each reads ALEX2MQTT_USERNAME, _PASSWORD and _ROOT_TOPIC and names the ones that are missing. The nine examples of 1.5.2 move to examples/legacy unchanged. test/examples.test.js starts every file with node, its broker connection sent to a broker on 127.0.0.1 (test/helpers/loopback.js), discovers the device, sends directives and checks the answers. A legacy file is started and its discovery compared with the answer the 1.5.2 build gave, recorded in test/fixtures/legacy-examples. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
54 lines
2.6 KiB
JavaScript
54 lines
2.6 KiB
JavaScript
// A roller blind: Alexa.RangeController with the position in percent, 0 closed and 100 open. The semantics say
|
|
// what "open", "close", "raise" and "lower" do, and which positions count as open and as closed.
|
|
//
|
|
// ALEX2MQTT_USERNAME=... ALEX2MQTT_PASSWORD=... ALEX2MQTT_ROOT_TOPIC=... node examples/blind.js
|
|
//
|
|
// "alex2node" is the installed package; inside this checkout it resolves to the built dist/.
|
|
const { Alex2MQTT, RangeController, asset, text, semantics } = require("alex2node");
|
|
|
|
const missing = ["ALEX2MQTT_USERNAME", "ALEX2MQTT_PASSWORD", "ALEX2MQTT_ROOT_TOPIC"].filter((name) => !process.env[name]);
|
|
if (missing.length > 0) {
|
|
console.error(`${missing.join(", ")} not set. Set the MQTT user name, the password and the root topic of your Alex2MQTT account.`);
|
|
process.exit(1);
|
|
}
|
|
const { ALEX2MQTT_USERNAME, ALEX2MQTT_PASSWORD, ALEX2MQTT_ROOT_TOPIC } = process.env;
|
|
|
|
const bridge = new Alex2MQTT(ALEX2MQTT_USERNAME, ALEX2MQTT_PASSWORD, ALEX2MQTT_ROOT_TOPIC);
|
|
bridge.on("connect", () => console.log("connected, discover the devices in the Alexa app"));
|
|
bridge.on("error", (err) => console.error("bridge:", err.message));
|
|
|
|
const blind = { position: 0 };
|
|
|
|
const device = bridge.addDevice({ endpointId: "bedroom-blind", name: "Bedroom Blind", categories: ["INTERIOR_BLIND"] });
|
|
const lift = device.add(RangeController, {
|
|
instance: "Blind.Lift",
|
|
friendlyNames: [asset("Alexa.Setting.Opening"), text("Position", "en-US")],
|
|
range: { min: 0, max: 100, precision: 1 },
|
|
unit: "Alexa.Unit.Percent",
|
|
semantics: semantics()
|
|
.action("Close", "SetRangeValue", { rangeValue: 0 })
|
|
.action("Open", "SetRangeValue", { rangeValue: 100 })
|
|
.action("Lower", "AdjustRangeValue", { rangeValueDelta: -10, rangeValueDeltaDefault: false })
|
|
.action("Raise", "AdjustRangeValue", { rangeValueDelta: 10, rangeValueDeltaDefault: false })
|
|
.state("Closed", 0)
|
|
.stateRange("Open", 1, 100),
|
|
});
|
|
|
|
// lift carries the instance: the property is reported under "Blind.Lift"
|
|
device.state((s) => s.set(lift, "rangeValue", blind.position).health("OK"));
|
|
|
|
const within = (position) => Math.min(100, Math.max(0, Math.round(position)));
|
|
|
|
lift.on("SetRangeValue", (ctx) => {
|
|
blind.position = within(ctx.payload.rangeValue);
|
|
return ctx.respond();
|
|
});
|
|
// rangeValueDeltaDefault: the user named no amount, and the blind picks its own step
|
|
lift.on("AdjustRangeValue", (ctx) => {
|
|
const { rangeValueDelta, rangeValueDeltaDefault } = ctx.payload;
|
|
const delta = rangeValueDeltaDefault ? Math.sign(rangeValueDelta) * 10 : rangeValueDelta;
|
|
blind.position = within(blind.position + delta);
|
|
return ctx.respond();
|
|
});
|
|
|
|
bridge.connect();
|