From d431ce7c8e8003451ef00ec33f2425cb05ea7414 Mon Sep 17 00:00:00 2001 From: Adrian Jagielak Date: Fri, 25 Jul 2025 01:58:13 +0200 Subject: [PATCH] Skip redundant services when higher-level entities exist --- futurehome/CHANGELOG.md | 1 + futurehome/src/ha/publish_device.ts | 44 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/futurehome/CHANGELOG.md b/futurehome/CHANGELOG.md index 1f9d8f2..8862010 100644 --- a/futurehome/CHANGELOG.md +++ b/futurehome/CHANGELOG.md @@ -7,6 +7,7 @@ - Updated demo mode data. - Added extracting device manufacturer name. - Updated 'thermostat' service implementation. +- Added logic to skip redundant services already represented by higher-level entities (e.g., sensor_temp/thermostat, sensor_wattemp/water_heater). ## 0.1.1 (24.07.2025) diff --git a/futurehome/src/ha/publish_device.ts b/futurehome/src/ha/publish_device.ts index cf70303..cd6b658 100644 --- a/futurehome/src/ha/publish_device.ts +++ b/futurehome/src/ha/publish_device.ts @@ -213,6 +213,38 @@ const serviceHandlers: { water_heater: water_heater__components, }; +// Defines service exclusions based on higher-level MQTT entity types. +// For example, if a device has a `thermostat` service, we skip `sensor_temp` +// because the thermostat component itself already reads and exposes the +// temperature internally. Similarly, `sensor_wattemp` is skipped when +// `water_heater` is present to avoid creating redundant entities. +const serviceExclusionMap: Record = { + sensor_temp: ['thermostat'], + sensor_wattemp: ['water_heater'], +}; + +/** + * Determines whether a given service should be published as a separate entity. + * + * Certain services (e.g., `sensor_temp`) are excluded when higher-level + * services (e.g., `thermostat`) are present, because those higher-level + * services already consume the lower-level state and expose it through + * their own MQTT entities. + * + * @param svcName - The name of the service being evaluated. + * @param services - A map of all services available for the device. + * @returns `true` if the service should be published, `false` if it is excluded. + */ +function shouldPublishService( + svcName: string, + services: { [name: string]: VinculumPd7Service }, +): boolean { + const exclusions = serviceExclusionMap[svcName]; + if (!exclusions) return true; + + return !exclusions.some((excludedService) => excludedService in services); +} + export function haPublishDevice(parameters: { hubId: string; demoMode: boolean; @@ -237,6 +269,18 @@ export function haPublishDevice(parameters: { if (!svc.enabled) { continue; } + // Skip publishing services that are already represented by higher-level MQTT entities + if ( + !shouldPublishService( + svcName, + parameters.vinculumDeviceData.services ?? {}, + ) + ) { + log.debug( + `Skipping service ${svcName} because a higher-level service handles its data`, + ); + continue; + } const handler = serviceHandlers[svcName]; if (!handler) {