Files
home-assistant-futurehome/futurehome/src/services/_sensor_numeric.ts
Jagi ᯅ 1a8a7c2e2c 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 <noreply@anthropic.com>
2026-05-16 23:58:19 +02:00

100 lines
3.7 KiB
TypeScript

import {
VinculumPd7Device,
VinculumPd7Service,
} from '../fimp/vinculum_pd7_device';
import {
SensorDeviceClass,
SensorStateClass,
} from '../ha/mqtt_components/_enums';
import { ServiceComponentsCreationResult } from '../ha/publish_device';
export function _sensor_numeric__components(
topicPrefix: string,
device: VinculumPd7Device,
svc: VinculumPd7Service,
svcName: string,
): ServiceComponentsCreationResult | undefined {
type SensorNumericDataTuple = [
device_class: SensorDeviceClass | undefined,
name: string | undefined,
unit: string,
state_class?: SensorStateClass,
];
const data = (
{
sensor_accelx: [undefined, 'Acceleration, X-axis', 'm/s2'],
sensor_accely: [undefined, 'Acceleration, Y-axis', 'm/s2'],
sensor_accelz: [undefined, 'Acceleration, Z-axis', 'm/s2'],
sensor_airflow: [undefined, 'Air flow', 'm3/h'],
sensor_airq: ['aqi', undefined, 'pm25'],
sensor_anglepos: [
undefined,
'Angle Position',
'%',
svc.props?.sup_units?.[0] === '%' ? 'measurement' : 'measurement_angle',
],
sensor_atmo: ['atmospheric_pressure', undefined, 'kPa'],
sensor_baro: ['atmospheric_pressure', undefined, 'kPa'],
sensor_co: ['carbon_monoxide', undefined, 'mol/m3'],
sensor_co2: ['carbon_dioxide', undefined, 'ppm'],
sensor_current: ['current', undefined, 'A'],
sensor_dew: ['temperature', 'Dew', '°C'],
sensor_direct: ['wind_direction', 'Direction', '°'],
sensor_distance: ['distance', undefined, 'm'],
sensor_elresist: [undefined, 'Electrical resistivity', 'ohm/m'],
sensor_freq: ['frequency', undefined, 'Hz'],
sensor_gp: [undefined, 'Sensor', '%'],
sensor_gust: [undefined, 'Gust', 'km/h'],
sensor_humid: ['humidity', undefined, '%'],
sensor_lumin: ['illuminance', undefined, 'lx'],
sensor_moist: ['moisture', undefined, '%'],
sensor_noise: ['sound_pressure', undefined, 'dB'],
sensor_power: ['power', undefined, 'W'],
sensor_rain: ['precipitation_intensity', undefined, 'mm/h'],
sensor_rotation: [undefined, 'Rotation', 'rpm'],
sensor_seismicint: [undefined, 'Seismic intensity', 'EMCRO'],
sensor_seismicmag: [undefined, 'Seismic magnitude', 'MB'],
sensor_solarrad: [undefined, 'Solar radiation', 'W/m2'],
sensor_tank: ['volume_storage', undefined, 'l'],
sensor_temp: ['temperature', undefined, '°C'],
sensor_tidelvl: [undefined, 'Tide level', 'm'],
sensor_uv: [undefined, 'Ultraviolet', 'index'],
sensor_veloc: [undefined, 'Velocity', 'm/2'],
sensor_voltage: ['voltage', undefined, 'V'],
sensor_watflow: ['volume_flow_rate', 'Water flow', 'l/h'],
sensor_watpressure: ['pressure', 'Water pressure', 'kPa'],
sensor_wattemp: ['temperature', 'Water temperature', '°C'],
sensor_weight: ['weight', undefined, 'kg'],
sensor_wind: ['wind_speed', undefined, 'km/h'],
} as Record<string, SensorNumericDataTuple>
)[svcName];
if (!data) return undefined;
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 {
components: {
[svc.addr]: {
unique_id: svc.addr,
platform: 'sensor',
name: name,
device_class: device_class,
state_class: state_class,
unit_of_measurement: unit,
value_template: `{{ value_json['${svc.addr}'].sensor }}`,
},
},
};
}