2025-07-21 22:28:31 +02:00
|
|
|
import mqtt, { MqttClient } from "mqtt";
|
|
|
|
|
|
|
|
export function connectHub(opts: { hubIp: string; username: string; password: string; }): Promise<MqttClient> {
|
|
|
|
const url = `mqtt://${opts.hubIp || "futurehome-smarthub.local"}`;
|
2025-07-21 23:04:44 +02:00
|
|
|
return makeClient(url, 1884, opts.username, opts.password);
|
2025-07-21 22:28:31 +02:00
|
|
|
}
|
|
|
|
|
2025-07-22 23:21:34 +02:00
|
|
|
export async function connectHA(opts: { mqttHost: string; mqttPort: number; mqttUsername: string; mqttPassword: string; }): Promise<{ ha: MqttClient; retainedMessages: RetainedMessage[] }> {
|
2025-07-21 23:04:44 +02:00
|
|
|
const url = `mqtt://${opts.mqttHost}`;
|
2025-07-22 23:21:34 +02:00
|
|
|
let ha = await makeClient(url, opts.mqttPort, opts.mqttUsername, opts.mqttPassword);
|
|
|
|
let retainedMessages = await waitForHARetainedMessages(ha)
|
|
|
|
|
|
|
|
return { ha, retainedMessages };
|
2025-07-21 22:28:31 +02:00
|
|
|
}
|
|
|
|
|
2025-07-21 23:04:44 +02:00
|
|
|
function makeClient(url: string, port: number, username: string, password: string): Promise<MqttClient> {
|
2025-07-21 22:28:31 +02:00
|
|
|
return new Promise((resolve, reject) => {
|
2025-07-21 23:04:44 +02:00
|
|
|
const client = mqtt.connect(url, { port, username, password, protocolVersion: 4 });
|
2025-07-21 22:28:31 +02:00
|
|
|
client.once("connect", () => resolve(client));
|
|
|
|
client.once("error", reject);
|
|
|
|
});
|
|
|
|
}
|
2025-07-22 23:21:34 +02:00
|
|
|
|
|
|
|
type RetainedMessage = { topic: string; message: string };
|
|
|
|
|
|
|
|
async function waitForHARetainedMessages(
|
|
|
|
client: MqttClient,
|
|
|
|
timeoutMs = 3000
|
|
|
|
): Promise<RetainedMessage[]> {
|
|
|
|
const topicPattern = /^homeassistant\/device\/futurehome.*$/;
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const retainedMessages: RetainedMessage[] = [];
|
|
|
|
|
|
|
|
const messageHandler = (topic: string, message: Buffer, packet: any) => {
|
|
|
|
if (packet.retain && topicPattern.test(topic)) {
|
|
|
|
retainedMessages.push({ topic, message: message.toString() });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const errorHandler = (err: Error) => {
|
|
|
|
cleanup();
|
|
|
|
reject(err);
|
|
|
|
};
|
|
|
|
|
|
|
|
const cleanup = () => {
|
|
|
|
client.off('message', messageHandler);
|
|
|
|
client.off('error', errorHandler);
|
|
|
|
};
|
|
|
|
|
|
|
|
client.on('message', messageHandler);
|
|
|
|
client.on('error', errorHandler);
|
|
|
|
|
|
|
|
client.subscribe('#', { qos: 1 }, (err) => {
|
|
|
|
if (err) {
|
|
|
|
cleanup();
|
|
|
|
reject(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
cleanup();
|
|
|
|
resolve(retainedMessages);
|
|
|
|
}, timeoutMs);
|
|
|
|
});
|
|
|
|
}
|