Files
xilica-node-red-integration/flows/showroom_flow.json
2025-11-03 13:29:04 +00:00

403 lines
13 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

ii[
{
"id": "tabXilicaDash",
"type": "tab",
"label": "Xilica FR1 Dashboard",
"disabled": false,
"info": ""
},
{
"id": "8ec2e59f8a7e993a",
"type": "ui-text",
"z": "tabXilicaDash",
"group": "ui_group_master",
"order": "",
"width": "",
"height": "",
"name": "Current (dB)",
"label": "Current (dB)",
"layout": "row-spread",
"style": false,
"font": "",
"fontSize": "",
"color": "#000000",
"wrapText": false,
"className": "",
"value": "payload",
"valueType": "msg",
"x": 1090,
"y": 400,
"wires": []
},
{
"id": "f64df68a17fafed8",
"type": "ui-slider",
"z": "tabXilicaDash",
"group": "ui_group_master",
"name": "MASTER_GAIN (dB)",
"label": "MASTER_GAIN (dB)",
"order": "",
"width": "1",
"height": "7",
"passthru": false,
"outs": "all",
"topic": "",
"topicType": "str",
"thumbLabel": "true",
"showTicks": "false",
"min": -100,
"max": -18,
"step": 0.5,
"className": "",
"iconPrepend": "",
"iconAppend": "",
"color": "grey",
"colorTrack": "blue",
"colorThumb": "black",
"showTextField": false,
"x": 540,
"y": 240,
"wires": [
[
"ebeeed016133d1b1"
]
]
},
{
"id": "ebeeed016133d1b1",
"type": "function",
"z": "tabXilicaDash",
"name": "build SET MASTER_GAIN <v>",
"func": "const v = Number(msg.payload);\n// show value on the slider label while sending\nnode.status({text: v.toFixed(1)+\" dB\"});\nmsg.payload = `SET MASTER_GAIN ${v}`;\nreturn msg;",
"outputs": 1,
"timeout": "",
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 850,
"y": 240,
"wires": [
[
"37a2739c39fb28c6"
]
]
},
{
"id": "ff5d04b8e8358ac7",
"type": "inject",
"z": "tabXilicaDash",
"name": "poll GET",
"props": [
{
"p": "payload"
}
],
"repeat": "0.1",
"crontab": "",
"once": true,
"onceDelay": "0.5",
"topic": "",
"payload": "GET MASTER_GAIN",
"payloadType": "str",
"x": 480,
"y": 320,
"wires": [
[
"37a2739c39fb28c6"
]
]
},
{
"id": "37a2739c39fb28c6",
"type": "function",
"z": "tabXilicaDash",
"name": "append \\r",
"func": "if (typeof msg.payload !== 'string') msg.payload = String(msg.payload);\nmsg.payload += \"\\r\";\nreturn msg;",
"outputs": 1,
"x": 740,
"y": 340,
"wires": [
[
"6f693811595db658"
]
]
},
{
"id": "6f693811595db658",
"type": "tcp request",
"z": "tabXilicaDash",
"name": "FR1 192.168.1.244:10007",
"server": "192.168.1.244",
"port": "10007",
"out": "sit",
"ret": "string",
"splitc": " ",
"newline": "",
"trim": false,
"tls": "",
"x": 550,
"y": 400,
"wires": [
[
"fd2ea6ae724caf88",
"2f4b662b4ed88da3",
"8d72b821fca250d0"
]
]
},
{
"id": "2f4b662b4ed88da3",
"type": "function",
"z": "tabXilicaDash",
"name": "parse GET reply → number",
"func": "// Expected: MASTER_GAIN=-6.0\\r or OK\\r after SET\nconst s = (msg.payload||\"\").trim();\nif (/^MASTER_GAIN\\s*=/.test(s)) {\n const m = s.match(/=\\s*([-+]?\\d+(?:\\.\\d+)?)/);\n if (m) {\n const val = Number(m[1]);\n // update text and slider\n node.send([{payload: val.toFixed(1)}, {payload: val}]);\n }\n}\nreturn null; // outputs handled via node.send",
"outputs": 2,
"x": 820,
"y": 400,
"wires": [
[
"8ec2e59f8a7e993a"
],
[
"8746aa1d3cf6fc69"
]
]
},
{
"id": "fd2ea6ae724caf88",
"type": "debug",
"z": "tabXilicaDash",
"name": "Raw replies",
"active": false,
"tosidebar": true,
"complete": "payload",
"statusVal": "",
"statusType": "auto",
"x": 850,
"y": 500,
"wires": []
},
{
"id": "8746aa1d3cf6fc69",
"type": "link out",
"z": "tabXilicaDash",
"name": "link out 2",
"mode": "link",
"links": [
"f267313c2d612dea"
],
"x": 985,
"y": 440,
"wires": []
},
{
"id": "f267313c2d612dea",
"type": "link in",
"z": "tabXilicaDash",
"name": "link in 2",
"links": [
"8746aa1d3cf6fc69"
],
"x": 265,
"y": 240,
"wires": [
[
"f64df68a17fafed8"
]
]
},
{
"id": "c2fce4e36d067d34",
"type": "inject",
"z": "tabXilicaDash",
"name": "poll VU_MASTER",
"props": [
{
"p": "payload"
}
],
"repeat": "0.2",
"crontab": "",
"once": true,
"onceDelay": "0.5",
"topic": "",
"payload": "GET VU_MASTER",
"payloadType": "str",
"x": 150,
"y": 400,
"wires": [
[
"2100bc16276dbd8d"
]
]
},
{
"id": "8d72b821fca250d0",
"type": "function",
"z": "tabXilicaDash",
"name": "parse VU_MASTER replies",
"func": "const s = String(msg.payload || '').trim();\nconst m = s.match(/^VU[_\\s]?MASTER\\s*=\\s*([-+]?\\d+(?:\\.\\d+)?)/i);\nif (!m) return null;\nmsg.payload = Number(m[1]); // e.g. -28.7\nreturn msg;\n",
"outputs": 1,
"timeout": "",
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 600,
"y": 840,
"wires": [
[
"a3665ff2cf1a93da"
]
]
},
{
"id": "2100bc16276dbd8d",
"type": "function",
"z": "tabXilicaDash",
"name": "append \\r",
"func": "if (typeof msg.payload !== 'string') msg.payload = String(msg.payload);\nmsg.payload += \"\\r\";\nreturn msg;",
"outputs": 1,
"x": 320,
"y": 400,
"wires": [
[
"6f693811595db658"
]
]
},
{
"id": "3d0401ed2bb481cd",
"type": "debug",
"z": "tabXilicaDash",
"name": "Template OUT",
"active": true,
"tosidebar": true,
"complete": "payload",
"statusVal": "",
"statusType": "auto",
"x": 1100,
"y": 840,
"wires": []
},
{
"id": "a3665ff2cf1a93da",
"type": "ui-template",
"z": "tabXilicaDash",
"group": "ui_group_meters",
"page": "",
"ui": "",
"name": "vu_master",
"order": 0,
"width": 0,
"height": 0,
"head": "",
"format": "<template>\n <div class=\"vu2\">\n <div class=\"vu2-hdr\">\n <strong>VU_MASTER</strong>\n <span class=\"vu2-readout\">{{ level.toFixed(1) }} dB</span>\n </div>\n\n <div class=\"vu2-bar\">\n <div class=\"vu2-fill\" :style=\"{ width: percent + '%' }\"></div>\n <div class=\"vu2-peak\" :style=\"{ left: peakPercent + '%' }\"></div>\n <span class=\"vu2-tick\" v-for=\"t in ticks\" :key=\"t\" :style=\"{ left: t + '%' }\"></span>\n </div>\n\n <div class=\"vu2-ftr\">\n <span>{{ min }} dB</span>\n <span>12</span>\n <span>3</span>\n <span>{{ max }} dB</span>\n </div>\n </div>\n</template>\n\n<script>\nexport default {\n // IMPORTANT for Dashboard 2: msg must be a prop to be reactive\n props: ['msg'],\n\n data: () => ({\n min: -60,\n max: 0,\n level: -60,\n peak: -60,\n decayDbPerSec: 6, // peak falls this many dB per second\n _timer: null,\n ticks: [0, 80, 95, 100] // % marks along the bar\n }),\n\n computed: {\n percent () {\n const v = Math.min(this.max, Math.max(this.min, this.level));\n return ((v - this.min) / (this.max - this.min)) * 100;\n },\n peakPercent () {\n const v = Math.min(this.max, Math.max(this.min, this.peak));\n return ((v - this.min) / (this.max - this.min)) * 100;\n }\n },\n\n watch: {\n // react to any incoming message\n msg: {\n deep: true,\n immediate: true,\n handler (m) {\n if (!m) return;\n\n if (m.min !== undefined && Number.isFinite(+m.min)) this.min = +m.min;\n if (m.max !== undefined && Number.isFinite(+m.max)) this.max = +m.max;\n\n if (m.payload !== undefined) {\n const n = Number(m.payload);\n if (Number.isFinite(n)) {\n this.level = n;\n if (n > this.peak) this.peak = n; // update peak on rise\n }\n }\n }\n }\n },\n\n mounted () {\n // peak decay loop\n const dt = 100; // ms\n this._timer = setInterval(() => {\n const step = this.decayDbPerSec * (dt / 1000);\n if (this.peak > this.level) this.peak = Math.max(this.level, this.peak - step);\n else this.peak = this.level;\n }, dt);\n },\n\n unmounted () {\n if (this._timer) clearInterval(this._timer);\n }\n}\n</script>\n\n<style>\n.vu2 { width:100%; font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif; }\n.vu2-hdr, .vu2-ftr {\n display:flex; justify-content:space-between; align-items:center;\n}\n.vu2-readout { font-variant-numeric: tabular-nums; opacity:.8; }\n\n.vu2-bar {\n position:relative; width:100%; height:18px; margin:6px 0 4px; border-radius:4px;\n background:#0b0f14; overflow:hidden; box-shadow:inset 0 0 0 1px rgba(255,255,255,.06);\n}\n.vu2-fill {\n position:absolute; top:0; bottom:0; left:0; z-index:1; transition:width 90ms linear;\n background:linear-gradient(90deg,\n #10b981 0%, #10b981 80%, /* green up to -12 dB */\n #f59e0b 80%, #f59e0b 95%, /* amber to -3 dB */\n #ef4444 95%, #ef4444 100%); /* red above -3 dB */\n}\n.vu2-peak {\n position:absolute; top:0; bottom:0; width:2px; background:rgba(255,255,255,.9);\n box-shadow:0 0 4px rgba(255,255,255,.9); z-index:2;\n}\n.vu2-tick {\n position:absolute; top:0; bottom:0; width:1px; background:rgba(255,255,255,.15); z-index:0;\n}\n.vu2-ftr span { font-size:12px; color:rgba(255,255,255,.6); }\n</style>\n",
"storeOutMessages": true,
"passthru": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 870,
"y": 840,
"wires": [
[
"3d0401ed2bb481cd"
]
]
},
{
"id": "ui_group_master",
"type": "ui-group",
"name": "Master Gain",
"page": "ui_page_solaro",
"width": "2",
"height": "8",
"showTitle": true,
"className": "",
"visible": "true",
"disabled": "false",
"groupType": "default"
},
{
"id": "ui_group_meters",
"type": "ui-group",
"name": "Meters",
"page": "ui_page_solaro",
"width": "6",
"height": "6",
"showTitle": true,
"className": "",
"visible": "true",
"disabled": "false",
"groupType": "default"
},
{
"id": "ui_page_solaro",
"type": "ui-page",
"name": "Solaro FR1",
"ui": "ui_base_main",
"path": "",
"icon": "volume-high",
"layout": "grid",
"theme": "efd7cd1777464c3d",
"breakpoints": [
{
"name": "Default",
"px": "0",
"cols": "3"
},
{
"name": "Tablet",
"px": "576",
"cols": "6"
},
{
"name": "Small Desktop",
"px": "768",
"cols": "9"
},
{
"name": "Desktop",
"px": "1024",
"cols": "12"
}
],
"className": "",
"visible": "true",
"disabled": "false"
},
{
"id": "ui_base_main",
"type": "ui-base",
"name": "Main UI",
"path": "/ui",
"headerContent": "page",
"titleBarStyle": "default",
"showReconnectNotification": true,
"notificationDisplayTime": 5,
"showDisconnectNotification": true,
"allowInstall": true
},
{
"id": "efd7cd1777464c3d",
"type": "ui-theme",
"name": "Theme Name",
"colors": {
"surface": "#ffffff",
"primary": "#0094ce",
"bgPage": "#eeeeee",
"groupBg": "#ffffff",
"groupOutline": "#cccccc"
},
"sizes": {
"density": "default",
"pagePadding": "12px",
"groupGap": "12px",
"groupBorderRadius": "4px",
"widgetGap": "12px"
}
}
]