initial release of Alex2Node

This commit is contained in:
David 2025-04-20 21:19:57 +00:00
parent 0804bd7397
commit 4eb2a84d72
20 changed files with 2047 additions and 3 deletions

131
src/Alex2Node.ts Normal file
View file

@ -0,0 +1,131 @@
import mqtt, { MqttClient } from "mqtt";
import Device from "./Device";
class Alex2MQTT {
private client: MqttClient | null = null;
private devices: Device[] = [];
private MqttHost = "mqtt://Alex2MQTT.stormysdream.club";
constructor(
private username: string,
private password: string,
private rootTopic: string,
private debugLogging: boolean
) {}
connect(): void {
const options = {
username: this.username,
password: this.password,
};
this.client = mqtt.connect(this.MqttHost, options);
this.client.on("connect", () => {
if (this.debugLogging) {
console.log("[Alex2Node.ts] Connected to MQTT broker");
}
this.client!.subscribe(this.rootTopic + "/#", (err) => {
if (this.debugLogging && !err) {
console.log("[Alex2Node.ts] Subscribed to topics");
}
if (err) {
throw err;
}
});
});
this.client.on("message", (topic: string, message: Buffer) => {
if (this.debugLogging) {
console.log(`[Alex2Node.ts] MQTT Message Received`);
console.log(` Topic: ${topic}`);
console.log(` Payload: ${message.toString()}`);
}
if (topic == `${this.rootTopic}/discover`) {
if (this.debugLogging) {
console.log(
"[Alex2Node.ts] Discovery request received, getting device json..."
);
}
const deviceArray = this.devices.map((device) => device.getJSON());
if (deviceArray.length > 0) {
this.client!.publish(
topic + "_r",
JSON.stringify(deviceArray),
(err) => {
if (err) {
throw err;
} else if (this.debugLogging) {
console.log(
`[Alex2Node.ts] Discovery payloads published to ${
topic + "_r"
}`
);
console.log(deviceArray);
}
}
);
}
} else if (topic.split("/").length == 3) {
const [root, endpointId, directiveType] = topic.split("/");
if (directiveType != "alexaDirective") {
return;
}
const device = this.devices.find((d) => d.endpointId === endpointId);
if (!device) {
if (this.debugLogging) {
console.warn(
`[Alex2Node.ts] No device found for endpointId: ${endpointId}`
);
}
return;
}
let payload;
try {
payload = JSON.parse(message.toString());
} catch (e) {
console.error(`[Alex2Node.ts] Failed to parse payload JSON`, e);
return;
}
if (
payload.header &&
payload.header.namespace === "Alexa" &&
payload.header.name === "ReportState"
) {
if (this.debugLogging) {
console.log(
`[Alex2Node.ts] ReportState directive for device ${endpointId}`
);
}
device.emit("ReportState", payload);
}else{
device.emit("Event", payload,payload.header.namespace);
}
}
});
this.client.on("error", (err: Error) => {
if (this.debugLogging) {
console.error("[Alex2Node.ts] Connection failed:", err);
}
throw err;
});
}
registerDevice(name: string, endpointId: string): Device {
if (!this.client) {
throw new Error("Must call connect before creating devices");
}
if (this.debugLogging) {
console.log(
`[Alex2Node.ts] Creating new device with endpoint: ${endpointId}`
);
}
const device = new Device(this.client!, this.rootTopic, name, endpointId);
this.devices.push(device);
return device;
}
}
export default Alex2MQTT;