fix(editor): make Input Mode the top-level switch, hide wrong-mode fields
Prior behaviour: the Mode dropdown existed but nothing consumed it in the editor — analog fields (Scaling, Source Min/Max, Smoothing, …) were always visible, and the Channels JSON editor was always visible too. For a legacy node with no saved mode the dropdown defaulted blank so users reported "I cant even select digital or analog". Changes: - Initialize the Mode <select> from node.mode with an 'analog' fallback for legacy nodes (safe default — matches pre-digital behaviour). - Wrap analog-only fields and digital-only fields in labelled containers and toggle their display based on the selected mode. Mode change is live — no redeploy needed to see the right form. - Inline hint under the Mode dropdown tells the user what payload shape is expected for the current mode. - Channels JSON gets live validation — shows channel count + names on valid JSON, warns on missing key/type, errors on invalid JSON. - Label function appends ' [digital]' so the node visibly differs in a flow from an analog sibling. - oneditsave is mode-aware: only warns about incomplete scaling ranges in analog mode; in digital mode warns if the channels array is empty or unparseable. Runtime friendliness: - nodeClass node-status now shows 'digital · N channel(s)' on startup in digital mode, and 'digital · N/M ch updated' after each incoming msg so the editor has a live heartbeat even when there is no single scalar. - When analog mode receives an object payload (or digital receives a number), the node logs an actionable warn suggesting the mode switch instead of silently dropping the message. Explicit, not auto-detected: mode remains a deployment-time choice because the two modes take different editor config (scaling/smoothing vs channels map). Auto-detecting at runtime would leave the node unconfigured in whichever mode the user hadn't anticipated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -97,10 +97,18 @@ class nodeClass {
|
||||
*/
|
||||
_bindEvents() {
|
||||
|
||||
// Analog mode: the classic 'mAbs' event pushes a green dot with the
|
||||
// current value + unit to the editor.
|
||||
this.source.emitter.on('mAbs', (val) => {
|
||||
this.node.status({ fill: 'green', shape: 'dot', text: `${val} ${this.config.general.unit}` });
|
||||
});
|
||||
|
||||
|
||||
// Digital mode: summarise how many channels have ticked a value.
|
||||
// This runs on every accepted channel update so the editor shows live
|
||||
// activity instead of staying blank when no single scalar exists.
|
||||
if (this.source.mode === 'digital') {
|
||||
this.node.status({ fill: 'blue', shape: 'ring', text: `digital · ${this.source.channels.size} channel(s)` });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +176,16 @@ class nodeClass {
|
||||
// digital -> object payload keyed by channel name
|
||||
if (this.source.mode === 'digital') {
|
||||
if (msg.payload && typeof msg.payload === 'object' && !Array.isArray(msg.payload)) {
|
||||
this.source.handleDigitalPayload(msg.payload);
|
||||
const summary = this.source.handleDigitalPayload(msg.payload);
|
||||
// Summarise what actually got accepted on the node status so
|
||||
// the editor shows a heartbeat per message.
|
||||
const accepted = Object.values(summary).filter((s) => s.ok).length;
|
||||
const total = Object.keys(summary).length;
|
||||
this.node.status({ fill: 'green', shape: 'dot',
|
||||
text: `digital · ${accepted}/${total} ch updated` });
|
||||
} else if (typeof msg.payload === 'number') {
|
||||
// Helpful hint: the user probably configured the wrong mode.
|
||||
this.source.logger?.warn(`digital mode received a number (${msg.payload}); expected an object like {key: value, ...}. Switch Input Mode to 'analog' in the editor or send an object payload.`);
|
||||
} else {
|
||||
this.source.logger?.warn(`digital mode expects an object payload; got ${typeof msg.payload}`);
|
||||
}
|
||||
@@ -180,6 +197,11 @@ class nodeClass {
|
||||
} else {
|
||||
this.source.logger?.warn(`Invalid numeric measurement payload: ${msg.payload}`);
|
||||
}
|
||||
} else if (msg.payload && typeof msg.payload === 'object' && !Array.isArray(msg.payload)) {
|
||||
// Helpful hint: the payload is object-shaped but the node is
|
||||
// configured analog. Most likely the user wanted digital mode.
|
||||
const keys = Object.keys(msg.payload).slice(0, 3).join(', ');
|
||||
this.source.logger?.warn(`analog mode received an object payload (keys: ${keys}). Switch Input Mode to 'digital' in the editor and define channels, or feed a numeric payload.`);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user