Poll list of devices every 30min

This commit is contained in:
Adrian Jagielak 2025-07-24 00:33:49 +02:00
parent 3a65e11948
commit 0989543a9d
No known key found for this signature in database
GPG Key ID: 0818CF7AF6C62BFB
3 changed files with 80 additions and 31 deletions

View File

@ -30,7 +30,6 @@ Devices commonly consist of multiple services: for example, a presence sensor mi
Some services are more common than others. Some are deprecated entirely. Some services are more common than others. Some are deprecated entirely.
todo periodical refresh of devices
todo links to the .ts service implementations below todo links to the .ts service implementations below
| Service | Example device | Implementation status | | Service | Example device | Implementation status |

View File

@ -1,6 +1,6 @@
# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config # https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config
name: Futurehome name: Futurehome
version: "0.0.22" version: "0.0.23"
slug: futurehome slug: futurehome
description: Local Futurehome Smarthub integration description: Local Futurehome Smarthub integration
url: "https://github.com/adrianjagielak/home-assistant-futurehome" url: "https://github.com/adrianjagielak/home-assistant-futurehome"

View File

@ -100,37 +100,42 @@ import { delay } from "./utils";
} }
} }
const commandHandlers: CommandHandlers = {}; const vinculumDevicesToHa = async (devices: FimpResponse) => {
for (const device of devices.val.param.device) { const commandHandlers: CommandHandlers = {};
try { for (const device of devices.val.param.device) {
const vinculumDeviceData: VinculumPd7Device = device try {
const deviceId = vinculumDeviceData.id.toString() const vinculumDeviceData: VinculumPd7Device = device
const firstServiceAddr = vinculumDeviceData.services ? Object.values(vinculumDeviceData.services)[0]?.addr : undefined;; 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. // 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 // // Get additional metadata like manufacutrer or sw/hw version directly from the adapter
// const adapterAddress = adapterAddressFromServiceAddress(firstServiceAddr) // const adapterAddress = adapterAddressFromServiceAddress(firstServiceAddr)
// const adapterService = adapterServiceFromServiceAddress(firstServiceAddr) // const adapterService = adapterServiceFromServiceAddress(firstServiceAddr)
// const deviceInclusionReport = await getInclusionReport({ adapterAddress, adapterService, deviceId }); // const deviceInclusionReport = await getInclusionReport({ adapterAddress, adapterService, deviceId });
const deviceInclusionReport = undefined; 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`)) {
// Set initial availability
haUpdateAvailability({ hubId, deviceAvailability: { address: deviceId, status: 'UP' } });
await delay(50); await delay(50);
Object.assign(commandHandlers, result.commandHandlers);
if (!retainedMessages.some(msg => msg.topic === `homeassistant/device/futurehome_${hubId}_${deviceId}/availability`)) {
// Set initial availability
haUpdateAvailability({ hubId, deviceAvailability: { address: deviceId, status: 'UP' } });
await delay(50);
}
} catch (e) {
log.error('Failed publishing device', device, e);
} }
} catch (e) {
log.error('Failed publishing device', device, e);
} }
} setHaCommandHandlers(commandHandlers);
setHaCommandHandlers(commandHandlers); };
vinculumDevicesToHa(devices);
let knownDeviceIds = new Set(devices.val.param.device.map((d: any) => d?.id));
// todo // todo
// exposeSmarthubTools(); // exposeSmarthubTools();
@ -142,11 +147,39 @@ import { delay } from "./utils";
switch (msg.type) { switch (msg.type) {
case 'evt.pd7.response': { case 'evt.pd7.response': {
// Handle vinculum 'state'
const devicesState = msg.val?.param?.state?.devices; const devicesState = msg.val?.param?.state?.devices;
if (!devicesState) { return; } if (devicesState) {
for (const deviceState of devicesState) { for (const deviceState of devicesState) {
haUpdateState({ hubId, deviceState }); haUpdateState({ hubId, deviceState });
await delay(50); await delay(50);
}
}
// Handle vinculum 'device's
const devices = msg.val.param.device;
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));
log.info(`Added devices: ${addedDeviceIds}`);
log.info(`Removed devices: ${removedDeviceIds}`);
for (const id of removedDeviceIds) {
const topic = `homeassistant/device/futurehome_${hubId}_${id}/config`;
ha?.publish(topic, '', { retain: true, qos: 2 });
await delay(50);
const availTopic = `homeassistant/device/futurehome_${hubId}_${id}/availability`;
ha?.publish(availTopic, '', { retain: true, qos: 2 });
await delay(50);
}
knownDeviceIds = newDeviceIds;
vinculumDevicesToHa(msg);
} }
break; break;
} }
@ -176,6 +209,8 @@ import { delay } from "./utils";
}); });
const pollState = () => { const pollState = () => {
log.debug("Refreshing Vinculum state after 30 seconds...");
sendFimpMsg({ sendFimpMsg({
address: '/rt:app/rn:vinculum/ad:1', address: '/rt:app/rn:vinculum/ad:1',
service: 'vinculum', service: 'vinculum',
@ -190,6 +225,21 @@ import { delay } from "./utils";
// Then poll every 30 seconds // Then poll every 30 seconds
setInterval(pollState, 30000); setInterval(pollState, 30000);
const pollDevices = () => {
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_t: 'object',
timeoutMs: 30000,
}).catch(e => log.warn("Failed to request state", e));
};
// Poll devices every 30 minutes (1800000 ms)
setTimeout(pollDevices, 30 * 60 * 1000);
ha.on('message', (topic, buf) => { ha.on('message', (topic, buf) => {
// Handle Home Assistant command messages // Handle Home Assistant command messages
const handler = haCommandHandlers?.[topic]; const handler = haCommandHandlers?.[topic];