From 1a8a7c2e2cc201a9f7b42ac4b58edbcd17cbd1f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jagi=20=E1=AF=85?= Date: Sat, 16 May 2026 23:58:19 +0200 Subject: [PATCH] Fix invalid device_class + unit_of_measurement combinations for HA 2026.5 (#32) HA 2026.5 enforces strict validation of device_class/unit_of_measurement pairs in MQTT discovery payloads. Four categories were broken: - power_factor: unit was sent as "power_factor" (the FIMP field name); HA requires the field to be omitted or empty for this device class. Fixed by normalising the display unit separately from the FIMP data-path key. - illuminance: unit "Lux" is rejected; normalise to "lx". When a sensor reports luminance as "%" (some Z-Wave devices), the illuminance device class is dropped so the entity becomes a generic sensor rather than triggering a validation error. - reactive_power (regular/export meter): unit "VAr" is rejected; HA requires lowercase "var". - reactive_power (extended meter values, e.g. p_import_react): the unit determination logic fell through to the generic power branch and sent "W"; reactive power extended values now emit "var", apparent power values emit "VA", and reactive energy values emit "kvarh". Fixes #31 Co-authored-by: Claude --- futurehome/src/services/_meter.ts | 44 ++++++++++++++++++---- futurehome/src/services/_sensor_numeric.ts | 5 ++- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/futurehome/src/services/_meter.ts b/futurehome/src/services/_meter.ts index 0c10d9e..855c165 100644 --- a/futurehome/src/services/_meter.ts +++ b/futurehome/src/services/_meter.ts @@ -370,16 +370,25 @@ export function _meter__components( break; } + // Map FIMP unit names to HA-compatible unit_of_measurement values + const haUnit = + unit === 'power_factor' ? '' : + unit === 'VAr' ? 'var' : + unit === 'kVArh' ? 'kvarh' : unit; + const component: SensorComponent = { unique_id: componentId, platform: 'sensor', entity_category: 'diagnostic', name: friendlyName, - unit_of_measurement: unit, state_class: stateClass, value_template: `{{ value_json['${svc.addr}'].meter.${unit}.val | default(0) }}`, }; + if (haUnit) { + component.unit_of_measurement = haUnit; + } + if (deviceClass) { component.device_class = deviceClass; } @@ -460,16 +469,24 @@ export function _meter__components( break; } + // Map FIMP unit names to HA-compatible unit_of_measurement values + const haUnit = + unit === 'VAr' ? 'var' : + unit === 'kVArh' ? 'kvarh' : unit; + const component: SensorComponent = { unique_id: componentId, platform: 'sensor', entity_category: 'diagnostic', name: friendlyName, - unit_of_measurement: unit, state_class: stateClass, value_template: `{{ value_json['${svc.addr}'].meter_export.${unit}.val | default(0) }}`, }; + if (haUnit) { + component.unit_of_measurement = haUnit; + } + if (deviceClass) { component.device_class = deviceClass; } @@ -510,14 +527,25 @@ export function _meter__components( // Determine unit based on value name let unit = ''; - if ( + const isPhasePower = valueName.startsWith('p_') || valueName.startsWith('p1') || valueName.startsWith('p2') || - valueName.startsWith('p3') || - valueName === 'dc_p' - ) { + valueName.startsWith('p3'); + if (isPhasePower && valueName.includes('_react')) { + unit = 'var'; + } else if (isPhasePower && valueName.includes('_apparent')) { + unit = 'VA'; + } else if (isPhasePower || valueName === 'dc_p') { unit = 'W'; + } else if ( + (valueName.startsWith('e_') || + valueName.startsWith('e1') || + valueName.startsWith('e2') || + valueName.startsWith('e3')) && + valueName.includes('_react') + ) { + unit = 'kvarh'; } else if ( valueName.startsWith('e_') || valueName.startsWith('e1') || @@ -565,9 +593,9 @@ export function _meter__components( } // Set suggested display precision - if (unit === 'kWh') { + if (unit === 'kWh' || unit === 'kvarh') { component.suggested_display_precision = 3; - } else if (unit === 'W' || unit === 'V' || unit === 'A') { + } else if (unit === 'W' || unit === 'V' || unit === 'A' || unit === 'var' || unit === 'VA') { component.suggested_display_precision = 1; } else if (unit === 'Hz') { component.suggested_display_precision = 2; diff --git a/futurehome/src/services/_sensor_numeric.ts b/futurehome/src/services/_sensor_numeric.ts index da35121..d00594c 100644 --- a/futurehome/src/services/_sensor_numeric.ts +++ b/futurehome/src/services/_sensor_numeric.ts @@ -72,12 +72,15 @@ export function _sensor_numeric__components( if (!data) return undefined; - const device_class = data[0]; + let device_class = data[0]; const name = data[1]; let unit = svc.props?.sup_units?.[0] ?? data[2]; if (unit === 'C') unit = '°C'; if (unit === 'F') unit = '°F'; if (unit === 'kph') unit = 'km/h'; + if (unit === 'Lux') unit = 'lx'; + // HA rejects illuminance + %; drop device_class for percentage-reporting sensors + if (device_class === 'illuminance' && unit === '%') device_class = undefined; const state_class = data[3]; return {