From e50be2ee66f94271db69e685883cc43908a3621e Mon Sep 17 00:00:00 2001 From: znetsixe Date: Mon, 13 Apr 2026 13:42:31 +0200 Subject: [PATCH] feat: permissive unit check for user-defined measurement types + measurement digital-mode schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MeasurementContainer.isUnitCompatible now short-circuits to accept any unit when the measurement type is not in the built-in measureMap. Known types (pressure, flow, power, temperature, volume, length, mass, energy) still validate strictly. This unblocks user-defined types in the measurement node's new digital/MQTT mode — e.g. 'humidity' with unit '%', 'co2' with 'ppm' — without forcing those units into the convert-module unit system. measurement.json schema: add 'mode.current' (analog | digital) and 'channels' (array) so the validator stops stripping them from the runtime config. Ignored in analog mode. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/configs/measurement.json | 28 ++++++++++++++++++++++++ src/measurements/MeasurementContainer.js | 10 +++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/configs/measurement.json b/src/configs/measurement.json index f2e8efd..3840967 100644 --- a/src/configs/measurement.json +++ b/src/configs/measurement.json @@ -411,6 +411,34 @@ } } }, + "mode": { + "current": { + "default": "analog", + "rules": { + "type": "enum", + "values": [ + { + "value": "analog", + "description": "Single-scalar input mode (classic 4-20mA / PLC style). msg.payload is a number; the node runs one offset/scaling/smoothing/outlier pipeline and emits one MeasurementContainer slot." + }, + { + "value": "digital", + "description": "Multi-channel input mode (MQTT / IoT JSON style). msg.payload is an object keyed by channel names declared under config.channels; the node routes each key through its own pipeline and emits N slots from one input message." + } + ], + "description": "Selects how incoming msg.payload is interpreted." + } + } + }, + "channels": { + "default": [], + "rules": { + "type": "array", + "itemType": "object", + "minLength": 0, + "description": "Channel map used in digital mode. Each entry is a self-contained pipeline definition: {key, type, position, unit, scaling?, smoothing?, outlierDetection?, distance?}. Ignored in analog mode." + } + }, "outlierDetection": { "enabled": { "default": false, diff --git a/src/measurements/MeasurementContainer.js b/src/measurements/MeasurementContainer.js index c11d06a..369f741 100644 --- a/src/measurements/MeasurementContainer.js +++ b/src/measurements/MeasurementContainer.js @@ -141,11 +141,17 @@ class MeasurementContainer { } isUnitCompatible(measurementType, unit) { - const desc = this._describeUnit(unit); - if (!desc) return false; + // Unknown type (not in measureMap): accept any unit. This lets user- + // defined measurement types (e.g. 'humidity', 'co2', arbitrary IoT + // channels in digital mode) pass through without being rejected just + // because their unit string ('%', 'ppm', …) is not a known physical + // unit to the convert module. Known types are still validated strictly. const normalizedType = this._normalizeType(measurementType); const expectedMeasure = this.measureMap[normalizedType]; if (!expectedMeasure) return true; + + const desc = this._describeUnit(unit); + if (!desc) return false; return desc.measure === expectedMeasure; }