node-red-contrib-room-tools

Authenticated visual control panel binding for Node-RED and MUSE Automator
3,065
Install
npm install node-red-contrib-room-tools
Latest Version:1.9.9
License:Proprietary
Last Updated:Oct 25, 2026
Links: npm
Maintainer: deaho

Room Tools

This package provides ui-control-panel for Node-RED and MUSE Automator.

1.9.9 release candidate

Reviewed against framework 1.2.0 and all five complete driver templates. The panel uses the same parameter/availability/telemetry contracts for TCP, UDP, TCP+UDP, HTTP(S), Telnet and SSH; no transport-specific panel configuration is needed. Template checks cover every exposed control and independent physical health scope, including multiple addressed gateway objects sharing one session.

Native text inputs and textareas now preserve a focused, unsubmitted draft while ordinary device feedback refreshes. Change submits the draft once; subsequent device feedback remains the source of actual state. The same draft protection already used for Vuetify text inputs is retained.

Framework control_send_policy() selects FIFO/latest after driver admission. The panel still coalesces continuous numeric inputs: changing a Python policy does not turn a slider into a lossless relative-action control. Use supported discrete parameters or explicit native Command/API flows for individual actions; arbitrary Command binding is not added. ACK, enqueue success and HTTP acceptance do not replace actual feedback. A normal business rejection must not gray out the device unless its physical online state changes.

Existing Flow IDs, bindings, login credentials and the schema version remain unchanged. This package does not modify offline synchronization or install itself into a running Automator or MUSE controller.

Changes retained from 1.9.8

The authenticated command endpoint now accepts the full existing 4096-character text limit for UTF-8 and JSON-escaped input within a bounded 32 KiB envelope. The character/type validation remains unchanged. Both user and binding-editor cookies automatically use Secure on direct HTTPS, including older configurations where the optional HTTPS-cookie checkbox is off. HTTP compatibility is retained.

The package uses ordinary AMX parameter descriptors and health/telemetry metadata; it does not depend on a particular Python framework version or TCP/UDP transport. Contract tests cover the current Precis, BLU-806, Meter Validation and Alcorn descriptors. These checks do not prove physical-device behavior.

Node-RED Flow node IDs, visual bindings and credential fields are retained. Offline dependency synchronization must preserve all original nodes and only add/remove its own helpers. Ordinary Automator Push keeps the online npm path; dependency packing/installing is an explicit manual-offline operation. Publishing this candidate and testing controller-side npm download remain separate steps.

Physical-device availability follows the common metadata contract. Drivers annotate a readonly boolean parameter with .metadata.availabilityRole: "online" at each physical-device scope. Re-Apply bindings after updating the driver descriptor to add deduplicated Status/Event health subscriptions. Without this metadata old bindings retain their existing behavior; this cannot infer physical health from a provider's virtual-device online status. Managed controls gray out, become inert/aria-disabled and freeze while unavailable. Commands are rejected server-side too. Ordinary controls resume on online=true without complete-snapshot or fresh-point gates. Legacy ready metadata is ignored. Telemetry frames still undergo ordinary data validation, which does not add a gray/disable condition. Binding editor selection remains available. The page author supplies no MUSE-specific health code, and no alert is displayed.

Controls without an annotated physical online route remain available. Separate drivers and same-model slots use independent routes, not a page-wide flag.

Hidden browser tabs pause ordinary SSE feedback as well as realtime subscriptions; returning to the tab reconnects to the latest snapshot without clearing login or changing device online state. This frees HTTP/1 connection slots for foreground commands. Telemetry sweeps yield between panels and keep at most one sweep in flight, so overloaded realtime displays do not build a timer backlog. XY transport reuses deeply frozen point arrays, while public snapshots remain independent copies.

Momentary release retries once on a transport failure, reusing its original sequence and session epoch. A retry cannot cancel a newer press. Authentication and validation failures are not retried; starts, renewals and ordinary commands are never automatically replayed. The existing server lease remains the fallback, and all button activation state still comes from actual device feedback.

ControlPanel

The page author writes ordinary HTML/CSS/Vue and pastes it into the ui-control-panel Template. After configuring a Dashboard page, room ID, control login, and separate editor login, save and Deploy. Reopen the node and select Open binding editor. Sign in with the editor account, right-click a supported control, choose its device points, and Apply. Open page opens the normal control page with its separate user login.

Both logins work over HTTP on a trusted local network. HTTPS is recommended for other networks because HTTP transmits credentials without encryption. Session cookies are marked Secure when the connection uses HTTPS.

Bindings are stored in visualBindings on the node. The runtime reads that mapping to authenticate commands, route feedback, and generate a native bridge. The page author does not put device paths, data-cp-* attributes, or device-send code in the Template. Author-provided data-cp-* attributes are rejected.

Supported controls include native range, checkbox, semantic pressed button, select, text input, meter, progress, and simple text display. Give each template control a stable standard ID or name; repeated instances also need stable unique keys. Simple top-level Vue v-model works, including object-list models such as item.volume, item.audio.volume, or items[index].volume, primitive array models such as values[index], and an inner <template v-for> for multiple controls per row. During Vue re-rendering, a temporary duplicate list key is ignored until the key becomes unique again, so bindings do not attach twice. Detached or disposed controls release their event listeners and cannot send commands. Controls with existing send handlers or ambiguous dynamic behavior are rejected until a suitable adapter exists. See CONTROL_PANEL_AI_GUIDE.md for examples and limits.

Native semantic buttons support a binding-editor Button behavior setting: Click to toggle (the default for existing bindings) or Hold to activate. Momentary buttons require both mode, send the mapped active value on press and inactive value on release, and always display real device feedback rather than a predicted local state. Pointer cancellation, keyboard release, focus/visibility loss, DOM removal, and disconnect release the hold. A 3-second server lease attempts release if the browser disappears; lease renewal does not repeat device writes. Concurrent holders of the same Write point within one panel are combined until the last release; opposite state mappings are rejected while held. Other panels and external controllers are not coordinated. Device or bridge outages can prevent physical release, so feedback remains the authority. Drivers keep their original parameter values; page authors do not implement hold events or lease traffic.

Two-state controls do not depend on device labels or a legacy explicit controlType. A boolean, an integer domain containing exactly two adjacent values, or a descriptor listing two distinct valid scalar options can match a switch. The editor offers one State reverse checkbox for both command and feedback conversion. Unchecked means the first value is false and the second is true; checked reverses both directions. This setting persists across point changes, mode changes, draft reopening and Apply. Read and write points may use different raw types/values but share the same normal/reversed order. On the next Apply, legacy separate mappings are migrated using the Write mapping when writable, otherwise the Feedback mapping; an explicit boolean stateReverse takes precedence. Existing running bindings are not rewritten merely by installing an update. Original string/number/boolean payloads are preserved, including enum Off/On, arbitrary A/B labels and integer 1/2, while the ordinary Vue model stays boolean. Enum targets remain available to select controls. Continuous float ranges, unknown domains, duplicate options and nonfinite values are not guessed into switches; unexpected feedback is rejected. Existing boolean/integer01 bindings keep their old behavior.

Writable native semantic buttons in either behavior automatically show a separate physical press effect (2px downward translation and inset shadow) for pointer/touch and Space/Enter. Release, cancellation, focus/visibility loss, disablement, DOM removal and loss of the authenticated feedback connection clear it. This effect changes neither the Vue model nor device-feedback ARIA, colors or icons; device feedback remains the sole source of the indicated mute/power state. Read-only buttons and editor mode do not show this effect. Page authors need no extra event handlers or internal press attributes.

Meter delivery does not change the page-authoring contract. Authors use ordinary meter, progress, v-progress-linear, or v-progress-circular controls with stable IDs and simple Vue fields. They do not declare devices, point paths, stream IDs, sockets, subscriptions, or frame formats. A volume binding is a writable gain setting with ordinary feedback. Readonly numeric parameters without enums or a two-state domain can bind to an indicator through native Status/Parameter Event feedback, without custom metadata or vendor/name heuristics. These can represent signal levels or other measurements; numeric structure alone cannot establish their physical meaning. They cannot bind to a volume slider, even as its feedback. Writable numeric controls cannot bind to a Meter. Split write-gain/read-gain parameters are not inferred as a pair. Native readonly values can still bind to text displays. When a target advertises Meter stream metadata, ControlPanel continues to share that stream, project complete frames, apply webpage-side range conversion, and handle stale/reconnected data. Native scalar events do not provide stream sequencing, sampling timestamps, atomic multichannel frames, or the same high-rate performance guarantees.

Readonly XY-series telemetry follows the same page boundary. Use a standard canvas with one simple Vue array binding, for example <canvas id="spectrum" :data-points="spectrum"></canvas>, or a map container with <div id="room-map" :data-coordinates="locations"></div>. Use [longitude, latitude] for geographic coordinates or [x, y] for local planar coordinates; the page owns the projection. The page receives complete [[x, y], ...] snapshots; ControlPanel owns subscription, latest-frame replacement, stale handling, and reconnection. The page does not declare a device, stream, socket, or frame protocol. XY-series is independent from Meter and Volume: it never sends commands and does not represent a single scalar value.

Different XY-series streams arrive independently. Room delivery aggregates their latest snapshots, not synchronized samples from a shared acquisition frame. The public framework defaults to 2048 points per stream and allows a configured maximum of 8192; the receiver enforces the manifest's maxPoints. A panel supports at most 256 XY-series bindings. These limits do not guarantee throughput at every frame rate or concurrency level; validate performance in the target environment.

Vuetify adapters cover v-slider, v-switch, v-checkbox, v-select, v-radio-group, v-text-field, v-textarea, and read-only v-progress-linear and v-progress-circular. Interactive components use a simple top-level v-model; progress may also use a simple :model-value field. Sliders accept fixed numeric min, max, and step props, including numeric Vue bindings such as :max="100". Text fields commit on change, blur, or Enter (v-text-field only). Selection values must match the device's enum values; circular progress uses a fixed 0-100 range. v-model.number is accepted for numeric sliders and scalar selections, but not for boolean or text controls. Dynamic bounds, array-valued switches, multi-select, object selections, custom true/false values, indeterminate progress, and controls with their own event handlers need a separate adapter.

Use paired Vuetify tags in Dashboard templates, such as <v-slider></v-slider>; self-closing component tags can absorb the following controls during Dashboard rendering and are rejected before deployment.

After clicking Apply in the page binding editor, the package validates the mapping, backs up the active Flow, and updates the panel and generated native bridge through the runtime Flow API with deployment type nodes. Affected runtime nodes restart. Separately, when Automator reports the external Flow change, its editor listener reloads the browser page once: that listener does not import, merge, clear or deploy Flow data. It does not preserve undeployed canvas drafts, so handle those before Apply. The bridge revision must match the saved mapping before a command can reach a device. A Node-RED simulation verifies software behavior, while live device operation requires a separate check.

An unchanged Apply still validates the draft and current revision, but skips the backup, Flow write and binding-page reload when the resulting nodes are identical, regardless of their array order. Editor configuration and the device catalog load concurrently. Transient runtime/catalog failures show an error with Retry instead of treating valid authentication as expired; only a 401 response requests credentials again. Ordinary user-page session restoration retries a short 404/503 restart window. Real updates still depend on the host's catalog, storage and node restart performance.

Template inspection reuses successful results for the exact source text in a bounded LRU cache (64 entries and a 4 MiB source/serialized-result accounting budget). Each caller receives independent candidates; source edits are compiled again and invalid templates are not cached. This cache contains no device catalog, live values or binding validation decisions. Authenticated editor responses include Server-Timing durations for config/catalog/Apply; Apply separates Flow reads, catalog reads, validation, bridge generation, backup and deployment. These headers contain only fixed stage names and durations, not credentials, device values or Flow content.

Deployment and Subscription Recovery

The node prefers the initialized Node-RED runtime Flow API, including its revision lock, rather than making HTTP calls back into its own Flow endpoints. Older hosts without that API retain the loopback HTTP fallback. Server-side catalog requests use bounded native Node HTTP on loopback; no unauthenticated LAN administration endpoint is added. Apply returns after the runtime acknowledges the update, without repeatedly polling the replaced editor route. Session restore and telemetry subscriptions coalesce their initial status/manifest reads. Feedback snapshots carry a runtime epoch, so a restored cookie does not cause new low-numbered feedback revisions to be rejected as stale.

Dashboard 1.31 installs custom widget socket handlers only at connection time. A scoped dispatcher now routes existing Dashboard sockets to the current panel generation after node replacement; it does not disconnect other rooms or patch Dashboard/vendor code. Meter/XY-series clients retry missing subscription confirmation and missing frames with bounded backoff, reject late confirmation from an old ticket, and dispose their timers/listeners on unmount. Normal live frames do not resubscribe. Socket tickets remain bound to the original authenticated session, room and socket.

One shared, non-overlapping catalog check monitors only already-bound device IDs (normally every two seconds, with error backoff). Observed removal/offline followed by return triggers a revision-protected refresh of the owning managed Event nodes, batched across affected panels. It does not restart the controller, write device settings, generate new bindings, or touch unmanaged Flows. For Meter/XY-series, a newly read manifest epoch followed by missing frames provides an additional recovery signal when a quick program replacement was missed by the catalog check. Silence or an unchanged manifest alone never triggers a native subscription restart. This is not network discovery or model validation; fixed IP/slot configuration is unchanged. A legacy scalar-only model with no lifecycle epoch and an unobservably brief catalog disappearance cannot be guaranteed to reveal replacement. Generated bridge output connections to existing external observer nodes are preserved on Apply.

The current runtime has one panel-scoped control path. The retired topic-based region gateway, fixed demo scenes, hand-written control tags, and sendControl authoring API are not supported. The page editor and server use the same target compatibility rules.

Installation

Install this package in the Node-RED user directory or declare it in the active project package.json. Installing on the Mac does not install the package on a MUSE controller.

The binding editor uses the running Node-RED server's loopback port and admin root to read the AMX device catalog, on both desktop Automator and MUSE. No desktop-only catalog environment variable is required. The AMX catalog routes must be available on that runtime.

Node.js 18.16.0 or newer is required. Dashboard 2 and the AMX Command/Status/Event nodes must be installed separately. The active Node-RED project must declare its Flow and credentials filenames; its user directory must be writable for private backups. Authenticated panel editors can Apply; native Flow administration stays inside the initialized runtime, with loopback as the older-host fallback. Direct TLS or an authenticated admin endpoint needs an additional transport adapter when that fallback is used.

The release includes npm-shrinkwrap.json to install the tested dependency versions. Deploy/Push must reference the intended registry version or a controller-accessible tarball; a file: dependency pointing at a Mac path is not portable to MUSE. Restart the controller runtime after replacing module code.

For development, run npm ci --ignore-scripts and npm test in this source directory with Node.js >=18.16. Tests need the local pinned dependencies; do not depend on another machine's installed Automator modules. The parent project's local-validation/test-source.sh also covers the Python framework, drivers, and offline Flow helpers. Its optional isolated runtime test uses a temporary user directory and random loopback port; it never deploys to the live Automator or MUSE.

Ordinary feedback, including native readonly numeric indicators, uses authenticated SSE; stream-backed Meter and XY-series use Dashboard Socket.IO. SSE feedback is serialized once per broadcast; a temporary backpressured write may drain, while a persistently blocked client is disconnected and reconnects to the latest snapshot rather than buffering history. Each panel allows at most 100 simultaneous SSE clients.

Realtime fan-out uses one volatile namespace broadcast to explicit authenticated socket IDs when the Dashboard socket exposes that API; other hosts keep the per-socket fallback. This avoids repeated server encoding without changing frame formats, authoring syntax, authorization or each viewer's network traffic. Shared streams are checked once when deciding whether a panel needs a new frame. Browser control lookup uses a DOM index invalidated on structure or binding-key changes; unrelated DOM mutations do not replay an XY array into the same model. Target recreation still restores the latest snapshot.

The parent project's local-validation/stress-panel.cjs adds opt-in multi-room, maximum-point, multi-viewer and SSE pressure scenarios. These use real HTTP/Socket.IO with simulated DOM/animation frames, not live Automator, MUSE or actual browser paint; target-device capacity must be verified separately.

Related Packages

@flowfuse/node-red-dashboard-2-ui-iframe

A Node-RED Dashboard 2.0 widget that allows you to embed an iframe into your das...

114,630 2
node-red-vue

Write Node-RED node templates using Vue.js

270,973 3
@flexdash/node-red-flexdash

Node-RED nodes for the FlexDash dashboard

247,442 10
@revolutionpi/node-red-dashboard-2-hmi

Human-Machine-Interface UI components for Node-RED and @flowfuse/node-red-dashbo...

1,185

Version History

Version Released License
1.9.9 Proprietary
1.9.8 Proprietary
1.9.7 Proprietary
1.9.6 Proprietary
1.9.5 Proprietary
Pre-releases (1)
Version Released License
1.8.3-visual-binding.17 Proprietary