mirror of
https://github.com/adrianjagielak/home-assistant-futurehome.git
synced 2026-01-18 15:45:38 +00:00
Format the codebase using prettier
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
import { connectHub, connectHA, RetainedMessage } from "./client";
|
||||
import { log } from "./logger";
|
||||
import { FimpResponse, sendFimpMsg, setFimp } from "./fimp/fimp";
|
||||
import { haCommandHandlers, setHa, setHaCommandHandlers } from "./ha/globals";
|
||||
import { CommandHandlers, haPublishDevice } from "./ha/publish_device";
|
||||
import { haUpdateState, haUpdateStateSensorReport } from "./ha/update_state";
|
||||
import { VinculumPd7Device } from "./fimp/vinculum_pd7_device";
|
||||
import { haUpdateAvailability } from "./ha/update_availability";
|
||||
import { delay } from "./utils";
|
||||
import { connectHub, connectHA, RetainedMessage } from './client';
|
||||
import { log } from './logger';
|
||||
import { FimpResponse, sendFimpMsg, setFimp } from './fimp/fimp';
|
||||
import { haCommandHandlers, setHa, setHaCommandHandlers } from './ha/globals';
|
||||
import { CommandHandlers, haPublishDevice } from './ha/publish_device';
|
||||
import { haUpdateState, haUpdateStateSensorReport } from './ha/update_state';
|
||||
import { VinculumPd7Device } from './fimp/vinculum_pd7_device';
|
||||
import { haUpdateAvailability } from './ha/update_availability';
|
||||
import { delay } from './utils';
|
||||
|
||||
(async () => {
|
||||
const hubIp = process.env.FH_HUB_IP || "futurehome-smarthub.local";
|
||||
const hubIp = process.env.FH_HUB_IP || 'futurehome-smarthub.local';
|
||||
const hubUsername = process.env.FH_USERNAME || '';
|
||||
const hubPassword = process.env.FH_PASSWORD || '';
|
||||
const demoMode = (process.env.DEMO_MODE || '').toLowerCase().includes('true');
|
||||
@@ -20,13 +20,20 @@ import { delay } from "./utils";
|
||||
const mqttPassword = process.env.MQTT_PWD || '';
|
||||
|
||||
// 1) Connect to HA broker (for discovery + state + availability + commands)
|
||||
log.info("Connecting to HA broker...");
|
||||
const { ha, retainedMessages } = await connectHA({ mqttHost, mqttPort, mqttUsername, mqttPassword, });
|
||||
log.info('Connecting to HA broker...');
|
||||
const { ha, retainedMessages } = await connectHA({
|
||||
mqttHost,
|
||||
mqttPort,
|
||||
mqttUsername,
|
||||
mqttPassword,
|
||||
});
|
||||
setHa(ha);
|
||||
log.info("Connected to HA broker");
|
||||
log.info('Connected to HA broker');
|
||||
|
||||
if (!demoMode && (!hubUsername || !hubPassword)) {
|
||||
log.info("Empty username or password in non-demo mode. Removing all Futurehome devices from Home Assistant...");
|
||||
log.info(
|
||||
'Empty username or password in non-demo mode. Removing all Futurehome devices from Home Assistant...',
|
||||
);
|
||||
|
||||
const publishWithDelay = (messages: RetainedMessage[], index = 0) => {
|
||||
if (index >= messages.length) return;
|
||||
@@ -42,17 +49,22 @@ import { delay } from "./utils";
|
||||
}
|
||||
|
||||
// 2) Connect to Futurehome hub (FIMP traffic)
|
||||
log.info("Connecting to Futurehome hub...");
|
||||
const fimp = await connectHub({ hubIp, username: hubUsername, password: hubPassword, demo: demoMode });
|
||||
fimp.subscribe("#");
|
||||
log.info('Connecting to Futurehome hub...');
|
||||
const fimp = await connectHub({
|
||||
hubIp,
|
||||
username: hubUsername,
|
||||
password: hubPassword,
|
||||
demo: demoMode,
|
||||
});
|
||||
fimp.subscribe('#');
|
||||
setFimp(fimp);
|
||||
log.info("Connected to Futurehome hub");
|
||||
log.info('Connected to Futurehome hub');
|
||||
|
||||
const house = await sendFimpMsg({
|
||||
address: '/rt:app/rn:vinculum/ad:1',
|
||||
service: 'vinculum',
|
||||
cmd: 'cmd.pd7.request',
|
||||
val: { cmd: "get", component: null, param: { components: ['house'] } },
|
||||
val: { cmd: 'get', component: null, param: { components: ['house'] } },
|
||||
val_t: 'object',
|
||||
timeoutMs: 30000,
|
||||
});
|
||||
@@ -62,16 +74,20 @@ import { delay } from "./utils";
|
||||
address: '/rt:app/rn:vinculum/ad:1',
|
||||
service: 'vinculum',
|
||||
cmd: 'cmd.pd7.request',
|
||||
val: { cmd: "get", component: null, param: { components: ['device'] } },
|
||||
val: { cmd: 'get', component: null, param: { components: ['device'] } },
|
||||
val_t: 'object',
|
||||
timeoutMs: 30000,
|
||||
});
|
||||
|
||||
const haConfig = retainedMessages.filter(msg => msg.topic.endsWith("/config"));
|
||||
const haConfig = retainedMessages.filter((msg) =>
|
||||
msg.topic.endsWith('/config'),
|
||||
);
|
||||
|
||||
const regex = new RegExp(`^homeassistant/device/futurehome_${hubId}_([a-zA-Z0-9]+)/config$`);
|
||||
const regex = new RegExp(
|
||||
`^homeassistant/device/futurehome_${hubId}_([a-zA-Z0-9]+)/config$`,
|
||||
);
|
||||
for (const haDevice of haConfig) {
|
||||
log.debug('Found existing HA device', haDevice.topic)
|
||||
log.debug('Found existing HA device', haDevice.topic);
|
||||
|
||||
const match = haDevice.topic.match(regex);
|
||||
|
||||
@@ -80,15 +96,18 @@ import { delay } from "./utils";
|
||||
const idNumber = Number(deviceId);
|
||||
|
||||
if (!isNaN(idNumber)) {
|
||||
const basicDeviceData: { services?: { [key: string]: any } } = devices.val.param.device.find((d: any) => d?.id === idNumber);
|
||||
const firstServiceAddr = basicDeviceData?.services ? Object.values(basicDeviceData.services)[0]?.addr : undefined;;
|
||||
const basicDeviceData: { services?: { [key: string]: any } } =
|
||||
devices.val.param.device.find((d: any) => d?.id === idNumber);
|
||||
const firstServiceAddr = basicDeviceData?.services
|
||||
? Object.values(basicDeviceData.services)[0]?.addr
|
||||
: undefined;
|
||||
|
||||
if (!basicDeviceData || !firstServiceAddr) {
|
||||
log.debug('Device was removed, removing from HA.');
|
||||
ha?.publish(haDevice.topic, '', { retain: true, qos: 2 });
|
||||
await delay(50);
|
||||
}
|
||||
} else if (deviceId.toLowerCase() === "hub") {
|
||||
} else if (deviceId.toLowerCase() === 'hub') {
|
||||
// Hub admin tools, ignore
|
||||
} else {
|
||||
log.debug('Invalid format, removing.');
|
||||
@@ -106,11 +125,15 @@ import { delay } from "./utils";
|
||||
const commandHandlers: CommandHandlers = {};
|
||||
for (const device of devices.val.param.device) {
|
||||
try {
|
||||
const vinculumDeviceData: VinculumPd7Device = device
|
||||
const deviceId = vinculumDeviceData.id.toString()
|
||||
const firstServiceAddr = vinculumDeviceData.services ? Object.values(vinculumDeviceData.services)[0]?.addr : undefined;;
|
||||
const vinculumDeviceData: VinculumPd7Device = device;
|
||||
const deviceId = vinculumDeviceData.id.toString();
|
||||
const firstServiceAddr = vinculumDeviceData.services
|
||||
? Object.values(vinculumDeviceData.services)[0]?.addr
|
||||
: undefined;
|
||||
|
||||
if (!firstServiceAddr) { continue; }
|
||||
if (!firstServiceAddr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is problematic when the adapter doesn't respond, so we are not getting the inclusion report for now. I'm leaving it here since we might want it in the future.
|
||||
// // Get additional metadata like manufacutrer or sw/hw version directly from the adapter
|
||||
@@ -119,14 +142,27 @@ import { delay } from "./utils";
|
||||
// const deviceInclusionReport = await getInclusionReport({ adapterAddress, adapterService, deviceId });
|
||||
const deviceInclusionReport = undefined;
|
||||
|
||||
const result = haPublishDevice({ hubId, vinculumDeviceData, deviceInclusionReport });
|
||||
const result = haPublishDevice({
|
||||
hubId,
|
||||
vinculumDeviceData,
|
||||
deviceInclusionReport,
|
||||
});
|
||||
await delay(50);
|
||||
|
||||
Object.assign(commandHandlers, result.commandHandlers);
|
||||
|
||||
if (!retainedMessages.some(msg => msg.topic === `homeassistant/device/futurehome_${hubId}_${deviceId}/availability`)) {
|
||||
if (
|
||||
!retainedMessages.some(
|
||||
(msg) =>
|
||||
msg.topic ===
|
||||
`homeassistant/device/futurehome_${hubId}_${deviceId}/availability`,
|
||||
)
|
||||
) {
|
||||
// Set initial availability
|
||||
haUpdateAvailability({ hubId, deviceAvailability: { address: deviceId, status: 'UP' } });
|
||||
haUpdateAvailability({
|
||||
hubId,
|
||||
deviceAvailability: { address: deviceId, status: 'UP' },
|
||||
});
|
||||
await delay(50);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -142,10 +178,12 @@ import { delay } from "./utils";
|
||||
// todo
|
||||
// exposeSmarthubTools();
|
||||
|
||||
fimp.on("message", async (topic, buf) => {
|
||||
fimp.on('message', async (topic, buf) => {
|
||||
try {
|
||||
const msg: FimpResponse = JSON.parse(buf.toString());
|
||||
log.debug(`Received FIMP message on topic "${topic}":\n${JSON.stringify(msg, null, 0)}`);
|
||||
log.debug(
|
||||
`Received FIMP message on topic "${topic}":\n${JSON.stringify(msg, null, 0)}`,
|
||||
);
|
||||
|
||||
switch (msg.type) {
|
||||
case 'evt.pd7.response': {
|
||||
@@ -163,8 +201,12 @@ import { delay } from "./utils";
|
||||
if (devices) {
|
||||
const newDeviceIds = new Set(devices.map((d: any) => d?.id));
|
||||
|
||||
const addedDeviceIds = [...newDeviceIds].filter(id => !knownDeviceIds.has(id));
|
||||
const removedDeviceIds = [...knownDeviceIds].filter(id => !newDeviceIds.has(id));
|
||||
const addedDeviceIds = [...newDeviceIds].filter(
|
||||
(id) => !knownDeviceIds.has(id),
|
||||
);
|
||||
const removedDeviceIds = [...knownDeviceIds].filter(
|
||||
(id) => !newDeviceIds.has(id),
|
||||
);
|
||||
|
||||
log.info(`Added devices: ${addedDeviceIds}`);
|
||||
log.info(`Removed devices: ${removedDeviceIds}`);
|
||||
@@ -190,14 +232,19 @@ import { delay } from "./utils";
|
||||
case 'evt.open.report':
|
||||
case 'evt.lvl.report':
|
||||
case 'evt.alarm.report':
|
||||
case 'evt.binary.report':
|
||||
{
|
||||
haUpdateStateSensorReport({ topic, value: msg.val, attrName: msg.type.split('.')[1] })
|
||||
break;
|
||||
}
|
||||
case 'evt.binary.report': {
|
||||
haUpdateStateSensorReport({
|
||||
topic,
|
||||
value: msg.val,
|
||||
attrName: msg.type.split('.')[1],
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'evt.network.all_nodes_report': {
|
||||
const devicesAvailability = msg.val;
|
||||
if (!devicesAvailability) { return; }
|
||||
if (!devicesAvailability) {
|
||||
return;
|
||||
}
|
||||
for (const deviceAvailability of devicesAvailability) {
|
||||
haUpdateAvailability({ hubId, deviceAvailability });
|
||||
await delay(50);
|
||||
@@ -206,50 +253,59 @@ import { delay } from "./utils";
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log.warn("Bad FIMP JSON", e, topic, buf);
|
||||
log.warn('Bad FIMP JSON', e, topic, buf);
|
||||
}
|
||||
});
|
||||
|
||||
const pollState = () => {
|
||||
log.debug("Refreshing Vinculum state after 30 seconds...");
|
||||
log.debug('Refreshing Vinculum state after 30 seconds...');
|
||||
|
||||
sendFimpMsg({
|
||||
address: '/rt:app/rn:vinculum/ad:1',
|
||||
service: 'vinculum',
|
||||
cmd: 'cmd.pd7.request',
|
||||
val: { cmd: "get", component: null, param: { components: ['state'] } },
|
||||
val: { cmd: 'get', component: null, param: { components: ['state'] } },
|
||||
val_t: 'object',
|
||||
timeoutMs: 30000,
|
||||
}).catch(e => log.warn("Failed to request state", e));
|
||||
}).catch((e) => log.warn('Failed to request state', e));
|
||||
};
|
||||
// Request initial state
|
||||
pollState();
|
||||
// Then poll every 30 seconds
|
||||
if (!demoMode) { setInterval(pollState, 30 * 1000); }
|
||||
if (!demoMode) {
|
||||
setInterval(pollState, 30 * 1000);
|
||||
}
|
||||
|
||||
const pollDevices = () => {
|
||||
log.debug("Refreshing Vinculum devices after 30 minutes...");
|
||||
log.debug('Refreshing Vinculum devices after 30 minutes...');
|
||||
|
||||
sendFimpMsg({
|
||||
address: '/rt:app/rn:vinculum/ad:1',
|
||||
service: 'vinculum',
|
||||
cmd: 'cmd.pd7.request',
|
||||
val: { cmd: "get", component: null, param: { components: ['device'] } },
|
||||
val: { cmd: 'get', component: null, param: { components: ['device'] } },
|
||||
val_t: 'object',
|
||||
timeoutMs: 30000,
|
||||
}).catch(e => log.warn("Failed to request state", e));
|
||||
}).catch((e) => log.warn('Failed to request state', e));
|
||||
};
|
||||
// Poll devices every 30 minutes (1800000 ms)
|
||||
if (!demoMode) { setInterval(pollDevices, 30 * 60 * 1000); }
|
||||
if (!demoMode) {
|
||||
setInterval(pollDevices, 30 * 60 * 1000);
|
||||
}
|
||||
|
||||
ha.on('message', (topic, buf) => {
|
||||
// Handle Home Assistant command messages
|
||||
const handler = haCommandHandlers?.[topic];
|
||||
if (handler) {
|
||||
log.debug(`Handling Home Assistant command topic: ${topic}, payload: ${buf.toString()}`);
|
||||
log.debug(
|
||||
`Handling Home Assistant command topic: ${topic}, payload: ${buf.toString()}`,
|
||||
);
|
||||
handler(buf.toString()).catch((e) => {
|
||||
log.warn(`Failed executing handler for topic: ${topic}, payload: ${buf.toString()}`, e);
|
||||
log.warn(
|
||||
`Failed executing handler for topic: ${topic}, payload: ${buf.toString()}`,
|
||||
e,
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user