Adrian Jagielak 3cbf43f358
lint & format
2025-07-23 20:38:17 +02:00

139 lines
4.0 KiB
TypeScript

import { v4 as uuidv4 } from "uuid";
import { log } from "../logger";
import { IMqttClient } from "../mqtt/interface";
let fimp: IMqttClient | undefined = undefined;
export function setFimp(client: IMqttClient) {
fimp = client;
}
export type FimpResponse = {
corid?: any;
ctime?: any;
props?: any;
serv?: any;
tags?: any;
type?: string | null;
uid?: any;
val?: any;
val_t?: FimpValueType;
ver?: any;
};
type FimpValueType = 'string' | 'int' | 'float' | 'bool' | 'null' | 'str_array' | 'int_array' | 'float_array' | 'str_map' | 'int_map' | 'float_map' | 'bool_map' | 'object' | 'bin';
export async function sendFimpMsg({
address,
service,
cmd,
val,
val_t,
timeoutMs = 10000,
}: {
address: string;
service: string;
cmd: string;
val: unknown;
val_t: FimpValueType;
timeoutMs?: number;
}): Promise<FimpResponse> {
const uid = uuidv4();
const topic = `pt:j1/mt:cmd${address}`;
const message = JSON.stringify(
{
corid: null,
ctime: new Date().toISOString(),
props: {},
resp_to: 'pt:j1/mt:rsp/rt:app/rn:ha-futurehome/ad:addon',
serv: service,
src: 'ha-futurehome',
tags: [],
'type': cmd,
uid: uid,
val: val,
val_t: val_t,
ver: '1',
},
);
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
fimp?.removeListener('message', onResponse);
const error = new Error(`Timeout waiting for FIMP response (uid: ${uid}, service: ${service}, cmd: ${cmd})`);
log.warn(error.message, error.stack);
reject(error);
}, timeoutMs);
const onResponse = (topic: string, buffer: any) => {
const msg = JSON.parse(buffer.toString());
if (msg.corid === uid) {
if (msg.type === 'evt.error.report') {
fimp?.removeListener('message', onResponse);
const error = new Error(`Received FIMP response for message ${uid}: error (evt.error.report) (matched using uid)`);
log.warn(error.message, error.stack);
reject(error);
return;
}
log.debug(`Received FIMP response for message ${uid} (matched using uid).`);
clearTimeout(timeout);
fimp?.removeListener('message', onResponse);
resolve(msg);
return;
}
if (msg.topic === `pt:j1/mt:evt${address}`) {
if (msg.type === 'evt.error.report') {
fimp?.removeListener('message', onResponse);
const error = new Error(`Received FIMP response for message ${uid}: error (evt.error.report) (matched using topic)`);
log.warn(error.message, error.stack);
reject(error);
return;
}
log.debug(`Received FIMP response for message ${uid} (matched using topic).`);
clearTimeout(timeout);
fimp?.removeListener('message', onResponse);
resolve(msg);
return;
}
const hasValidType = msg.type != null && msg.type.startsWith('evt.');
const reqCmdParts = cmd.split('.');
const resCmdParts = msg.type?.split('.') ?? [];
const hasThreeParts = resCmdParts.length === 3 && reqCmdParts.length === 3;
const middlePartMatches = resCmdParts[1] === reqCmdParts[1];
const endsWithLastPart = cmd.endsWith(resCmdParts.at(-1)!);
const reqEndsWithSetAndResEndsWithReport = reqCmdParts[2] === 'set' && resCmdParts[2] === 'report'
const sameService = msg.serv === service;
if (hasValidType && hasThreeParts && middlePartMatches && (endsWithLastPart || reqEndsWithSetAndResEndsWithReport) && sameService) {
log.debug(`Received FIMP response for message ${uid} (matched using event name).`);
clearTimeout(timeout);
fimp?.removeListener('message', onResponse);
resolve(msg);
return;
}
};
fimp?.on('message', onResponse);
log.debug(`
Sending FIMP message:
address: "${address}",
service: "${service}",
uid: "${uid}",
cmd: "${cmd}",
val: "${JSON.stringify(val)}",
val_t: "${val_t}"
`);
fimp?.publish(topic, message, { qos: 1 });
});
}