@andreydk1981/node-red-dashboard-2-ui-projector 0.1.18 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,8 @@
1
- # Node-RED Multimedia Suite
2
-
3
- Package with multiple Node-RED nodes for projector automation.
4
-
5
- Included nodes:
6
- - `ui-multimedia-projector`: Dashboard 2.0 widget with projector UI and ON/OFF actions.
7
- - `ui-projector`: legacy alias for backward compatibility with existing flows.
8
- - `projector-command`: helper node that normalizes different ON/OFF inputs to stable commands.
1
+ # Node-RED Dashboard 2.0 - Projector Widget
2
+
3
+ **Release note (v0.1.6):** added compatibility metadata (Node.js >=16, Node-RED >=3.1.15) and example flow.
4
+
5
+ A simple Node-RED Dashboard 2.0 widget that displays a projector icon.
9
6
 
10
7
  ## Installation
11
8
 
@@ -21,9 +18,9 @@ npm run build
21
18
 
22
19
  3. Link to your Node-RED installation (for development):
23
20
  ```bash
24
- npm link
25
- cd ~/.node-red
26
- npm link @andreydk1981/node-red-dashboard-2-ui-projector
21
+ npm link
22
+ cd ~/.node-red
23
+ npm link @andreydk1981/node-red-dashboard-2-ui-projector
27
24
  ```
28
25
 
29
26
  4. Restart Node-RED
@@ -37,41 +34,20 @@ npm run dev
37
34
 
38
35
  Then open http://localhost:5173 in your browser.
39
36
 
40
- ## Usage
41
-
42
- After installation, both nodes appear in the Node-RED palette:
43
- - `ui-multimedia-projector` in Dashboard category.
44
- - `ui-projector` (legacy, kept for old projects).
45
- - `projector-command` in Function category.
46
-
47
- Typical flow:
48
- `inject/switch -> projector-command -> tcp/serial/request node`
49
-
50
- ## Structure
51
-
52
- - `/nodes` - Node-RED node definition files
53
- - `/ui` - Vue.js component files
54
- - `/resources` - Built widget files (generated)
55
- - `vite.config.mjs` - Vite build configuration
56
- - `package.json` - Package definition
57
-
58
- ## Add new node in this package
59
-
60
- 1. Create `nodes/<node-name>.js` and register type with `RED.nodes.registerType`.
61
- 2. Create `nodes/<node-name>.html` with editor form and help.
62
- 3. Add the node entry to `package.json`:
63
- ```json
64
- "node-red": {
65
- "nodes": {
66
- "<node-name>": "nodes/<node-name>.js"
67
- }
68
- }
69
- ```
70
- 4. If it is a Dashboard 2.0 widget, also add metadata in `node-red-dashboard-2.widgets` and build the UI bundle.
71
- 5. Restart Node-RED and verify the new node appears in the palette.
37
+ ## Usage
72
38
 
73
- ## License
39
+ After installation, you'll find the "ui-projector" node in the Node-RED palette under the Dashboard category. Drag it onto your flow, configure it to be part of a Dashboard 2.0 group, and deploy.
74
40
 
75
- Apache-2.0
41
+ The widget will display a projector icon on your dashboard.
76
42
 
43
+ ## Structure
77
44
 
45
+ - `/nodes` - Node-RED node definition files
46
+ - `/ui` - Vue.js component files
47
+ - `/resources` - Built widget files (generated)
48
+ - `vite.config.mjs` - Vite build configuration
49
+ - `package.json` - Package definition
50
+
51
+ ## License
52
+
53
+ Apache-2.0
@@ -1,124 +1,123 @@
1
- <script type="text/javascript">
2
- RED.nodes.registerType('ui-projector', {
3
- category: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.label.category'),
4
- color: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.colors.light'),
5
- defaults: {
6
- name: { value: "" },
7
- group: { type: 'ui-group', required: true },
8
- order: { value: 0 },
9
- width: {
10
- value: 0,
11
- validate: function (v) {
12
- const width = v || 0
13
- const currentGroup = $('#node-input-group').val() || this.group
14
- const groupNode = RED.nodes.node(currentGroup)
15
- const valid = !groupNode || +width <= +groupNode.width
16
- $('#node-input-size').toggleClass('input-error', !valid)
17
- return valid
18
- }
19
- },
20
- height: { value: 0 }
21
- },
22
- inputs: 1,
23
- outputs: 1,
24
- icon: "font-awesome/fa-video-camera",
25
- label: function() {
26
- return this.name || "ui-projector";
27
- },
28
- oneditprepare: function () {
29
- $('#node-input-size').elementSizer({
30
- width: '#node-input-width',
31
- height: '#node-input-height',
32
- group: '#node-input-group'
33
- });
34
- }
35
- });
36
- </script>
37
-
38
- <script type="text/html" data-template-name="ui-projector">
39
- <div class="form-row">
40
- <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
41
- <input type="text" id="node-input-name" placeholder="Name">
42
- </div>
43
- <div class="form-row">
44
- <label for="node-input-group"><i class="fa fa-table"></i> Group</label>
45
- <input type="text" id="node-input-group">
46
- </div>
47
- <div class="form-row">
48
- <label><i class="fa fa-object-group"></i> <span data-i18n="ui-projector.label.size"></label>
49
- <input type="hidden" id="node-input-width">
50
- <input type="hidden" id="node-input-height">
51
- <button class="editor-button" id="node-input-size"></button>
52
- </div>
53
- </script>
54
-
55
- <script type="text/html" data-help-name="ui-projector">
56
- <p>Dashboard 2.0 widget with a projector icon.</p>
57
-
58
- <h3>Properties</h3>
59
- <dl class="message-properties">
60
- <dt>Name <span class="property-type">string</span></dt>
61
- <dd>The widget name displayed above the icon. Can be updated using <code>msg.name</code>.</dd>
62
- </dl>
63
-
64
- <h3>Incoming messages</h3>
65
- <dl class="message-properties">
66
- <dt>msg.name <span class="property-type">string</span></dt>
67
- <dd>Set a new name for the widget. The name is persisted and used as <code>msg.topic</code> for outgoing messages.</dd>
68
- <dt>msg.color <span class="property-type">string</span></dt>
69
- <dd>Set the icon color (any CSS color). To reset, send <code>msg.color = null</code>.</dd>
70
- <dt>msg.ires <span class="property-type">string</span></dt>
71
- <dd>Set resolution text displayed under the icon (e.g. <code>"1920x1080"</code>).</dd>
72
- </dl>
73
-
74
- <h3>Behavior</h3>
75
- <p>When the <strong>ON</strong> button is pressed the widget sends <code>msg.payload = "%1POWR 1"</code>. When the <strong>OFF</strong> button is pressed it sends <code>msg.payload = "%1POWR 0"</code>. Outgoing <code>msg.topic</code> is the widget Name.</p>
76
-
77
- <h3>Example flow</h3>
78
- <p>Import the JSON below into Node‑RED (Menu → Import → Clipboard):</p>
79
- <pre><code>{
80
- "id": "example-1",
81
- "type": "tab",
82
- "label": "UIProjector example",
83
- "nodes": [
84
- {
85
- "id": "n1",
86
- "type": "ui-projector",
87
- "z": "example-1",
88
- "name": "Projector1",
89
- "group": "your-dashboard-group-id",
90
- "wires": [["debug1"]]
91
- },
92
- {
93
- "id": "inject-on",
94
- "type": "inject",
95
- "z": "example-1",
96
- "name": "Send ON string",
97
- "props": [{"p":"payload","v":"%1POWR 1","vt":"str"}],
98
- "topic": "",
99
- "wires": [["n1"]]
100
- },
101
- {
102
- "id": "inject-off",
103
- "type": "inject",
104
- "z": "example-1",
105
- "name": "Send OFF string",
106
- "props": [{"p":"payload","v":"%1POWR 0","vt":"str"}],
107
- "topic": "",
108
- "wires": [["n1"]]
109
- },
110
- {
111
- "id": "debug1",
112
- "type": "debug",
113
- "z": "example-1",
114
- "name": "debug",
115
- "active": true,
116
- "complete": "payload"
117
- }
118
- ]
119
- }</code></pre>
120
-
121
- <h3>Details</h3>
122
- <p>The widget saves the last received color and resolution and restores them on reload. Icon and text scale with widget size.</p>
123
- </script>
124
-
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('ui-projector', {
3
+ category: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.label.category'),
4
+ color: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.colors.light'),
5
+ defaults: {
6
+ name: { value: "" },
7
+ group: { type: 'ui-group', required: true },
8
+ order: { value: 0 },
9
+ width: {
10
+ value: 0,
11
+ validate: function (v) {
12
+ const width = v || 0
13
+ const currentGroup = $('#node-input-group').val() || this.group
14
+ const groupNode = RED.nodes.node(currentGroup)
15
+ const valid = !groupNode || +width <= +groupNode.width
16
+ $('#node-input-size').toggleClass('input-error', !valid)
17
+ return valid
18
+ }
19
+ },
20
+ height: { value: 0 }
21
+ },
22
+ inputs: 1,
23
+ outputs: 1,
24
+ icon: "font-awesome/fa-video-camera",
25
+ label: function() {
26
+ return this.name || "ui-projector";
27
+ },
28
+ oneditprepare: function () {
29
+ $('#node-input-size').elementSizer({
30
+ width: '#node-input-width',
31
+ height: '#node-input-height',
32
+ group: '#node-input-group'
33
+ });
34
+ }
35
+ });
36
+ </script>
37
+
38
+ <script type="text/html" data-template-name="ui-projector">
39
+ <div class="form-row">
40
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
41
+ <input type="text" id="node-input-name" placeholder="Name">
42
+ </div>
43
+ <div class="form-row">
44
+ <label for="node-input-group"><i class="fa fa-table"></i> Group</label>
45
+ <input type="text" id="node-input-group">
46
+ </div>
47
+ <div class="form-row">
48
+ <label><i class="fa fa-object-group"></i> <span data-i18n="ui-projector.label.size"></label>
49
+ <input type="hidden" id="node-input-width">
50
+ <input type="hidden" id="node-input-height">
51
+ <button class="editor-button" id="node-input-size"></button>
52
+ </div>
53
+ </script>
54
+
55
+ <script type="text/html" data-help-name="ui-projector">
56
+ <p>Dashboard 2.0 widget with a projector icon.</p>
57
+
58
+ <h3>Properties</h3>
59
+ <dl class="message-properties">
60
+ <dt>Name <span class="property-type">string</span></dt>
61
+ <dd>The widget name displayed above the icon. Can be updated using <code>msg.name</code>.</dd>
62
+ </dl>
63
+
64
+ <h3>Incoming messages</h3>
65
+ <dl class="message-properties">
66
+ <dt>msg.name <span class="property-type">string</span></dt>
67
+ <dd>Set a new name for the widget. The name is persisted and used as <code>msg.topic</code> for outgoing messages.</dd>
68
+ <dt>msg.color <span class="property-type">string</span></dt>
69
+ <dd>Set the icon color (any CSS color). To reset, send <code>msg.color = null</code>.</dd>
70
+ <dt>msg.ires <span class="property-type">string</span></dt>
71
+ <dd>Set resolution text displayed under the icon (e.g. <code>"1920x1080"</code>).</dd>
72
+ </dl>
73
+
74
+ <h3>Behavior</h3>
75
+ <p>When the <strong>ON</strong> button is pressed the widget sends <code>msg.payload = "%1POWR 1"</code>. When the <strong>OFF</strong> button is pressed it sends <code>msg.payload = "%1POWR 0"</code>. Outgoing <code>msg.topic</code> is the widget Name.</p>
76
+
77
+ <h3>Example flow</h3>
78
+ <p>Import the JSON below into NodeRED (Menu Import Clipboard):</p>
79
+ <pre><code>{
80
+ "id": "example-1",
81
+ "type": "tab",
82
+ "label": "UIProjector example",
83
+ "nodes": [
84
+ {
85
+ "id": "n1",
86
+ "type": "ui-projector",
87
+ "z": "example-1",
88
+ "name": "Projector1",
89
+ "group": "your-dashboard-group-id",
90
+ "wires": [["debug1"]]
91
+ },
92
+ {
93
+ "id": "inject-on",
94
+ "type": "inject",
95
+ "z": "example-1",
96
+ "name": "Send ON string",
97
+ "props": [{"p":"payload","v":"%1POWR 1","vt":"str"}],
98
+ "topic": "",
99
+ "wires": [["n1"]]
100
+ },
101
+ {
102
+ "id": "inject-off",
103
+ "type": "inject",
104
+ "z": "example-1",
105
+ "name": "Send OFF string",
106
+ "props": [{"p":"payload","v":"%1POWR 0","vt":"str"}],
107
+ "topic": "",
108
+ "wires": [["n1"]]
109
+ },
110
+ {
111
+ "id": "debug1",
112
+ "type": "debug",
113
+ "z": "example-1",
114
+ "name": "debug",
115
+ "active": true,
116
+ "complete": "payload"
117
+ }
118
+ ]
119
+ }</code></pre>
120
+
121
+ <h3>Details</h3>
122
+ <p>The widget saves the last received color and resolution and restores them on reload. Icon and text scale with widget size.</p>
123
+ </script>
@@ -59,5 +59,5 @@ module.exports = function (RED) {
59
59
  }
60
60
  }
61
61
 
62
- RED.nodes.registerType('ui-projector', UIProjectorNode)
63
- }
62
+ RED.nodes.registerType('ui-projector', UIProjectorNode)
63
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@andreydk1981/node-red-dashboard-2-ui-projector",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "description": "A Node-RED package with projector-focused nodes for Dashboard 2.0 and control flows",
7
+ "description": "A Node-RED Dashboard 2.0 widget with a projector icon",
8
8
  "keywords": [
9
9
  "node-red",
10
10
  "node-red-node",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "repository": {
21
21
  "type": "git",
22
- "url": "git+https://github.com/andreydk1981/node-red-dashboard-2-ui-projector.git"
22
+ "url": "https://github.com/andreydk1981/node-red-dashboard-2-ui-projector.git"
23
23
  },
24
24
  "bugs": {
25
25
  "url": "https://github.com/andreydk1981/node-red-dashboard-2-ui-projector/issues"
@@ -28,8 +28,7 @@
28
28
  "node-red": {
29
29
  "version": ">=3.1.15 <5.0.0",
30
30
  "nodes": {
31
- "ui-projector": "nodes/ui-projector.js",
32
- "projector-command": "nodes/projector-command.js"
31
+ "ui-projector": "nodes/ui-projector.js"
33
32
  }
34
33
  },
35
34
  "node-red-dashboard-2": {
@@ -1,76 +0,0 @@
1
- <script type="text/javascript">
2
- RED.nodes.registerType('projector-command', {
3
- category: 'function',
4
- color: '#dedede',
5
- defaults: {
6
- name: { value: '' },
7
- topic: { value: '' },
8
- commandFormat: { value: 'onoff' },
9
- onValue: { value: 'ON' },
10
- offValue: { value: 'OFF' }
11
- },
12
- inputs: 1,
13
- outputs: 1,
14
- icon: 'font-awesome/fa-terminal',
15
- label: function () {
16
- return this.name || 'projector-command'
17
- },
18
- oneditprepare: function () {
19
- function toggleValues () {
20
- var mode = $('#node-input-commandFormat').val()
21
- var disabled = mode === 'pjlink'
22
- $('#node-input-onValue').prop('disabled', disabled)
23
- $('#node-input-offValue').prop('disabled', disabled)
24
- }
25
-
26
- $('#node-input-commandFormat').on('change', toggleValues)
27
- toggleValues()
28
- }
29
- })
30
- </script>
31
-
32
- <script type="text/html" data-template-name="projector-command">
33
- <div class="form-row">
34
- <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
35
- <input type="text" id="node-input-name" placeholder="Name">
36
- </div>
37
- <div class="form-row">
38
- <label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label>
39
- <input type="text" id="node-input-topic" placeholder="projector-1">
40
- </div>
41
- <div class="form-row">
42
- <label for="node-input-commandFormat"><i class="fa fa-cogs"></i> Format</label>
43
- <select id="node-input-commandFormat">
44
- <option value="onoff">ON/OFF</option>
45
- <option value="pjlink">PJLink (%1POWR 1/0)</option>
46
- </select>
47
- </div>
48
- <div class="form-row">
49
- <label for="node-input-onValue"><i class="fa fa-arrow-up"></i> ON value</label>
50
- <input type="text" id="node-input-onValue">
51
- </div>
52
- <div class="form-row">
53
- <label for="node-input-offValue"><i class="fa fa-arrow-down"></i> OFF value</label>
54
- <input type="text" id="node-input-offValue">
55
- </div>
56
- </script>
57
-
58
- <script type="text/html" data-help-name="projector-command">
59
- <p>Converts various ON/OFF inputs into stable control commands.</p>
60
-
61
- <h3>Input</h3>
62
- <dl class="message-properties">
63
- <dt>msg.command <span class="property-type">string | boolean | number</span></dt>
64
- <dd>Optional command value. If present, it has priority over <code>msg.payload</code>.</dd>
65
- <dt>msg.payload <span class="property-type">string | boolean | number</span></dt>
66
- <dd>Supports <code>true/false</code>, <code>1/0</code>, <code>ON/OFF</code>, and <code>%1POWR 1/0</code>.</dd>
67
- </dl>
68
-
69
- <h3>Output</h3>
70
- <dl class="message-properties">
71
- <dt>msg.payload <span class="property-type">string</span></dt>
72
- <dd>Formatted command in selected mode (ON/OFF, PJLink, or custom values).</dd>
73
- <dt>msg.topic <span class="property-type">string</span></dt>
74
- <dd>Set from node config if the incoming message does not contain a topic.</dd>
75
- </dl>
76
- </script>
@@ -1,60 +0,0 @@
1
- module.exports = function (RED) {
2
- function ProjectorCommandNode (config) {
3
- RED.nodes.createNode(this, config)
4
- const node = this
5
-
6
- node.topic = config.topic || ''
7
- node.commandFormat = config.commandFormat || 'onoff'
8
- node.onValue = config.onValue || 'ON'
9
- node.offValue = config.offValue || 'OFF'
10
-
11
- function normalizeState (msg) {
12
- const value = typeof msg.command !== 'undefined' ? msg.command : msg.payload
13
- if (typeof value === 'boolean') {
14
- return value
15
- }
16
-
17
- if (typeof value === 'number') {
18
- if (value === 1) return true
19
- if (value === 0) return false
20
- }
21
-
22
- if (typeof value === 'string') {
23
- const normalized = value.trim().toLowerCase()
24
- if (['on', '1', 'true', '%1powr 1'].includes(normalized)) return true
25
- if (['off', '0', 'false', '%1powr 0'].includes(normalized)) return false
26
- }
27
-
28
- return null
29
- }
30
-
31
- function formatPayload (isOn) {
32
- if (node.commandFormat === 'pjlink') {
33
- return isOn ? '%1POWR 1' : '%1POWR 0'
34
- }
35
-
36
- return isOn ? node.onValue : node.offValue
37
- }
38
-
39
- node.on('input', function (msg, send, done) {
40
- const state = normalizeState(msg)
41
- if (state === null) {
42
- node.status({ fill: 'red', shape: 'ring', text: 'unsupported payload' })
43
- if (done) done()
44
- return
45
- }
46
-
47
- msg.payload = formatPayload(state)
48
- if (!msg.topic && node.topic) {
49
- msg.topic = node.topic
50
- }
51
-
52
- node.status({ fill: 'green', shape: 'dot', text: state ? 'ON' : 'OFF' })
53
- send(msg)
54
-
55
- if (done) done()
56
- })
57
- }
58
-
59
- RED.nodes.registerType('projector-command', ProjectorCommandNode)
60
- }