[
    {
        "id": "pgtg_tab",
        "type": "tab",
        "label": "PgTg Bridge Control",
        "disabled": false,
        "info": "Control and monitor PgTg Bridge via WebSocket API.\nConnect to ws://127.0.0.1:4990/command for control.\nConnect to ws://127.0.0.1:4990/data for meter telemetry."
    },
    {
        "id": "ws_command_in",
        "type": "websocket in",
        "z": "pgtg_tab",
        "name": "",
        "server": "4e3c911202b34ef9",
        "client": "",
        "x": 210,
        "y": 400,
        "wires": [
            [
                "parse_command_response",
                "0df2f7274619875d"
            ]
        ]
    },
    {
        "id": "ws_data_in",
        "type": "websocket in",
        "z": "pgtg_tab",
        "name": "",
        "server": "32125c9fc1fda031",
        "client": "",
        "x": 210,
        "y": 600,
        "wires": [
            [
                "parse_data_message",
                "9cc53b8b353ad163"
            ]
        ]
    },
    {
        "id": "btn_start",
        "type": "ui_button",
        "z": "pgtg_tab",
        "name": "RequestStart",
        "group": "pgtg_ui_group_control",
        "order": 1,
        "width": 2,
        "height": 1,
        "passthru": false,
        "label": "Start",
        "tooltip": "Start the PgTg Bridge",
        "color": "",
        "bgcolor": "#4CAF50",
        "className": "",
        "icon": "play_arrow",
        "payload": "RequestStart",
        "payloadType": "str",
        "topic": "",
        "topicType": "str",
        "x": 150,
        "y": 60,
        "wires": [
            [
                "build_command"
            ]
        ]
    },
    {
        "id": "btn_stop",
        "type": "ui_button",
        "z": "pgtg_tab",
        "name": "RequestStop",
        "group": "pgtg_ui_group_control",
        "order": 2,
        "width": 2,
        "height": 1,
        "passthru": false,
        "label": "Stop",
        "tooltip": "Stop the PgTg Bridge",
        "color": "",
        "bgcolor": "#F44336",
        "className": "",
        "icon": "stop",
        "payload": "RequestStop",
        "payloadType": "str",
        "topic": "",
        "topicType": "str",
        "x": 150,
        "y": 100,
        "wires": [
            [
                "build_command"
            ]
        ]
    },
    {
        "id": "btn_restart",
        "type": "ui_button",
        "z": "pgtg_tab",
        "name": "RequestRestart",
        "group": "pgtg_ui_group_control",
        "order": 3,
        "width": 2,
        "height": 1,
        "passthru": false,
        "label": "Restart",
        "tooltip": "Restart the PgTg Bridge",
        "color": "",
        "bgcolor": "#FF9800",
        "className": "",
        "icon": "refresh",
        "payload": "RequestRestart",
        "payloadType": "str",
        "topic": "",
        "topicType": "str",
        "x": 160,
        "y": 140,
        "wires": [
            [
                "build_command"
            ]
        ]
    },
    {
        "id": "btn_status",
        "type": "ui_button",
        "z": "pgtg_tab",
        "d": true,
        "name": "RequestStatus",
        "group": "pgtg_ui_group_control",
        "order": 4,
        "width": 2,
        "height": 1,
        "passthru": false,
        "label": "Status",
        "tooltip": "Poll current bridge status",
        "color": "",
        "bgcolor": "#2196F3",
        "className": "",
        "icon": "info",
        "payload": "RequestStatus",
        "payloadType": "str",
        "topic": "",
        "topicType": "str",
        "x": 160,
        "y": 180,
        "wires": [
            [
                "build_command"
            ]
        ]
    },
    {
        "id": "build_command",
        "type": "function",
        "z": "pgtg_tab",
        "name": "Build Command",
        "func": "// Build a WsCommandRequest JSON message from the button payload.\n// Input:  msg.payload = command string (e.g. \"RequestStart\")\n// Output: msg.payload = JSON string for WebSocket send\n\nmsg.payload = JSON.stringify({\n    type: \"command\",\n    command: msg.payload\n});\n\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 400,
        "y": 200,
        "wires": [
            [
                "1abc06236b5ffabe",
                "d875dfcacbf52f37"
            ]
        ]
    },
    {
        "id": "parse_command_response",
        "type": "function",
        "z": "pgtg_tab",
        "name": "Parse Command Response",
        "func": "// Parse incoming messages from the /command WebSocket endpoint.\n// Handles both \"response\" (command replies) and \"statusChange\" (unsolicited pushes).\n//\n// Outputs:\n//   [0] → status text for UI display\n//   [1] → status color for UI indicator\n//   [2] → debug/log output\n\nlet data;\ntry {\n    data = JSON.parse(msg.payload);\n} catch (e) {\n    return [null, null, { payload: \"Parse error: \" + msg.payload }];\n}\n\nlet state = \"\";\nlet command = \"\";\n\nif (data.type === \"response\") {\n    // Direct reply to a command we sent\n    state = data.state || \"Unknown\";\n    command = data.command || \"\";\n} else if (data.type === \"statusChange\") {\n    // Unsolicited state transition push from server\n    state = data.state || \"Unknown\";\n    command = \"(push)\";\n} else {\n    return [null, null, { payload: \"Unknown type: \" + data.type }];\n}\n\n// Store state in flow context for other nodes to read\nflow.set(\"bridgeState\", state);\n\n// Map state to a UI-friendly color\nconst stateColors = {\n    \"Running\":      \"#4CAF50\",  // green\n    \"Starting\":     \"#FF9800\",  // orange\n    \"Restarting\":   \"#FF9800\",  // orange\n    \"ReadyToStart\": \"#2196F3\",  // blue\n    \"Stopping\":     \"#FF9800\",  // orange\n    \"Initializing\": \"#9E9E9E\",  // grey\n    \"Error\":        \"#F44336\",  // red\n    \"TrialExpired\": \"#F44336\",  // red\n    \"NotDetected\":  \"#9E9E9E\",  // grey\n};\n\nlet color = stateColors[state] || \"#9E9E9E\";\n\n// Output 0: status text\nlet statusMsg = { payload: state };\n\n// Output 1: status color for indicator\nlet colorMsg = { payload: color, topic: state };\n\n// Output 2: debug log\nlet debugMsg = { payload: `[${command}] → ${state}` };\nnode.status({text:\"Status:\" + statusMsg + \" Debug:\" + debugMsg})\nreturn [statusMsg, colorMsg, debugMsg];",
        "outputs": 3,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 600,
        "y": 400,
        "wires": [
            [
                "ui_status_text"
            ],
            [
                "ui_status_indicator"
            ],
            [
                "debug_command"
            ]
        ]
    },
    {
        "id": "parse_data_message",
        "type": "function",
        "z": "pgtg_tab",
        "name": "Parse Data Messages",
        "func": "// Parse incoming messages from the /data WebSocket endpoint.\n// Handles meterConfig, meterData, and txFrequency messages.\n//\n// Outputs:\n//   [0] → meter gauge values (one msg per meter, topic = meter name)\n//   [1] → frequency display text\n//   [2] → debug/log output\n\nlet data;\ntry {\n    data = JSON.parse(msg.payload);\n} catch (e) {\n    return [null, null, { payload: \"Parse error: \" + msg.payload }];\n}\n\nif (data.type === \"meterConfig\") {\n    // Store meter config in flow context for reference\n    flow.set(\"meterConfig\", data.meters);\n    \n    let names = data.meters.map(m => m.name).join(\", \");\n    return [null, null, { payload: \"Meter config: \" + names }];\n}\n\nif (data.type === \"meterData\") {\n    // Send one message per meter reading for individual gauges\n    let msgs = [];\n    let config = flow.get(\"meterConfig\") || [];\n    \n    for (let [name, reading] of Object.entries(data.readings)) {\n        // Find config entry for this meter to get max scale\n        let cfg = config.find(m => m.name === name) || {};\n        \n        msgs.push({\n            topic: name,\n            payload: reading.value,\n            units: reading.units,\n            min: cfg.min || reading.min || 0,\n            max: cfg.max || reading.max || 100,\n            isTxMode: data.isTxMode\n        });\n    }\n    \n    // Return array of messages on output 0\n    return [msgs, null, null];\n}\n\nif (data.type === \"txFrequency\") {\n    var freqMhz = data.frequencyKhz.toFixed(0);\n    var bandText = data.band ? data.band + \" - \" : \"\";\n    var fullText = bandText + freqMhz + \" KHz\"\n    var freqMsg = {payload: fullText} ;\n    node.status({text: fullText});\n    return [null, freqMsg, null];\n}\n\nreturn [null, null, { payload: \"Unknown data type: \" + data.type }];",
        "outputs": 3,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 520,
        "y": 600,
        "wires": [
            [
                "meter_router"
            ],
            [
                "ui_frequency"
            ],
            [
                "debug_data"
            ]
        ]
    },
    {
        "id": "meter_router",
        "type": "function",
        "z": "pgtg_tab",
        "name": "Meter Router",
        "func": "// Routes individual meter readings to separate outputs by meter name.\n// This allows connecting each output to a dedicated gauge widget.\n//\n// Outputs:\n//   [0] → AMP_FWD  (Forward Power, Watts)\n//   [1] → AMP_RL   (SWR)\n//   [2] → AMP_TEMP (Temperature, C)\n//   [3] → TUNER_FWD (Tuner Forward Power, Watts)\n//   [4] → TUNER_RL  (Tuner SWR)\n\nconst routes = {\n    \"AMP_FWD\":   0,\n    \"AMP_RL\":    1,\n    \"AMP_TEMP\":  2,\n    \"TUNER_FWD\": 3,\n    \"TUNER_RL\":  4\n};\n\nlet outputs = [null, null, null, null, null];\nlet idx = routes[msg.topic];\n\nif (idx !== undefined) {\n    outputs[idx] = msg;\n}\n\nreturn outputs;",
        "outputs": 5,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 790,
        "y": 580,
        "wires": [
            [
                "ui_gauge_amp_fwd"
            ],
            [
                "ui_gauge_amp_swr"
            ],
            [
                "ui_gauge_amp_temp"
            ],
            [
                "ui_gauge_tuner_fwd"
            ],
            [
                "ui_gauge_tuner_swr"
            ]
        ]
    },
    {
        "id": "ui_status_text",
        "type": "ui_text",
        "z": "pgtg_tab",
        "group": "pgtg_ui_group_status",
        "order": 1,
        "width": 4,
        "height": 1,
        "name": "Bridge State",
        "label": "State:",
        "format": "{{msg.payload}}",
        "layout": "row-left",
        "className": "",
        "style": false,
        "font": "",
        "fontSize": "",
        "color": "#000000",
        "x": 860,
        "y": 370,
        "wires": []
    },
    {
        "id": "ui_status_indicator",
        "type": "ui_text",
        "z": "pgtg_tab",
        "group": "pgtg_ui_group_status",
        "order": 2,
        "width": 2,
        "height": 1,
        "name": "Status Light",
        "label": "",
        "format": "<span style=\"display:inline-block;width:20px;height:20px;border-radius:50%;background:{{msg.payload}}\"></span>",
        "layout": "row-center",
        "x": 860,
        "y": 410,
        "wires": []
    },
    {
        "id": "ui_frequency",
        "type": "ui_text",
        "z": "pgtg_tab",
        "group": "pgtg_ui_group_status",
        "order": 3,
        "width": 6,
        "height": 1,
        "name": "TX Frequency",
        "label": "TX Freq:",
        "format": "{{msg.payload}}",
        "layout": "row-left",
        "className": "",
        "style": false,
        "font": "",
        "fontSize": "",
        "color": "#000000",
        "x": 980,
        "y": 740,
        "wires": []
    },
    {
        "id": "ui_gauge_amp_fwd",
        "type": "ui_gauge",
        "z": "pgtg_tab",
        "name": "AMP FWD",
        "group": "pgtg_ui_group_meters",
        "order": 1,
        "width": "2",
        "height": "2",
        "gtype": "gage",
        "title": "AMP FWD",
        "label": "Watts",
        "format": "{{value | number:0}}",
        "min": 0,
        "max": "1500",
        "colors": [
            "#4caf50",
            "#ff9800",
            "#f44336"
        ],
        "seg1": "750",
        "seg2": "1200",
        "diff": false,
        "className": "",
        "x": 990,
        "y": 540,
        "wires": []
    },
    {
        "id": "ui_gauge_amp_swr",
        "type": "ui_gauge",
        "z": "pgtg_tab",
        "name": "AMP SWR",
        "group": "pgtg_ui_group_meters",
        "order": 2,
        "width": "2",
        "height": "2",
        "gtype": "gage",
        "title": "AMP SWR",
        "label": "SWR",
        "format": "{{value | number:1}}",
        "min": "1",
        "max": "3",
        "colors": [
            "#4caf50",
            "#ff9800",
            "#f44336"
        ],
        "seg1": "1.5",
        "seg2": "2.5",
        "diff": false,
        "className": "",
        "x": 990,
        "y": 580,
        "wires": []
    },
    {
        "id": "ui_gauge_amp_temp",
        "type": "ui_gauge",
        "z": "pgtg_tab",
        "name": "AMP Temp",
        "group": "pgtg_ui_group_meters",
        "order": 3,
        "width": "2",
        "height": "2",
        "gtype": "gage",
        "title": "AMP TEMP",
        "label": "°C",
        "format": "{{value | number:0}}",
        "min": 0,
        "max": "100",
        "colors": [
            "#2196f3",
            "#4caf50",
            "#f44336"
        ],
        "seg1": "40",
        "seg2": "75",
        "diff": false,
        "className": "",
        "x": 990,
        "y": 620,
        "wires": []
    },
    {
        "id": "ui_gauge_tuner_fwd",
        "type": "ui_gauge",
        "z": "pgtg_tab",
        "d": true,
        "name": "TUNER FWD",
        "group": "pgtg_ui_group_meters",
        "order": 4,
        "width": "2",
        "height": "2",
        "gtype": "gage",
        "title": "TUNER FWD",
        "label": "Watts",
        "format": "{{value | number:0}}",
        "min": 0,
        "max": "600",
        "colors": [
            "#4caf50",
            "#ff9800",
            "#f44336"
        ],
        "seg1": "300",
        "seg2": "500",
        "diff": false,
        "className": "",
        "x": 980,
        "y": 660,
        "wires": []
    },
    {
        "id": "ui_gauge_tuner_swr",
        "type": "ui_gauge",
        "z": "pgtg_tab",
        "d": true,
        "name": "TUNER SWR",
        "group": "pgtg_ui_group_meters",
        "order": 5,
        "width": "2",
        "height": "2",
        "gtype": "gage",
        "title": "TUNER SWR",
        "label": "SWR",
        "format": "{{value | number:1}}",
        "min": "1",
        "max": "3",
        "colors": [
            "#4caf50",
            "#ff9800",
            "#f44336"
        ],
        "seg1": "1.5",
        "seg2": "2.5",
        "diff": false,
        "className": "",
        "x": 980,
        "y": 700,
        "wires": []
    },
    {
        "id": "debug_command",
        "type": "debug",
        "z": "pgtg_tab",
        "name": "Command Debug",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 880,
        "y": 450,
        "wires": []
    },
    {
        "id": "debug_data",
        "type": "debug",
        "z": "pgtg_tab",
        "name": "Data Debug",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 590,
        "y": 680,
        "wires": []
    },
    {
        "id": "0df2f7274619875d",
        "type": "debug",
        "z": "pgtg_tab",
        "name": "debug 23",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 280,
        "y": 440,
        "wires": []
    },
    {
        "id": "1abc06236b5ffabe",
        "type": "debug",
        "z": "pgtg_tab",
        "name": "debug 24",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 480,
        "y": 260,
        "wires": []
    },
    {
        "id": "57e3b755b5a8e538",
        "type": "inject",
        "z": "pgtg_tab",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "1",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "RequestStatus",
        "payloadType": "str",
        "x": 180,
        "y": 240,
        "wires": [
            [
                "build_command"
            ]
        ]
    },
    {
        "id": "d875dfcacbf52f37",
        "type": "websocket out",
        "z": "pgtg_tab",
        "name": "",
        "server": "4e3c911202b34ef9",
        "client": "",
        "x": 730,
        "y": 200,
        "wires": []
    },
    {
        "id": "9cc53b8b353ad163",
        "type": "debug",
        "z": "pgtg_tab",
        "name": "debug 33",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 280,
        "y": 660,
        "wires": []
    },
    {
        "id": "7734f44afe0e0423",
        "type": "comment",
        "z": "pgtg_tab",
        "name": "Point this to your IP where PgTg is installed with /command endpoint",
        "info": "ws://192.168.111.220:4990/command",
        "x": 800,
        "y": 160,
        "wires": []
    },
    {
        "id": "5404ce223870ac42",
        "type": "comment",
        "z": "pgtg_tab",
        "name": "Point this to your IP where PgTg is installed with /command endpoint",
        "info": "ws://192.168.111.220:4990/command",
        "x": 280,
        "y": 360,
        "wires": []
    },
    {
        "id": "cbda5b8c670b61ca",
        "type": "comment",
        "z": "pgtg_tab",
        "name": "Point this to your IP where PgTg is installed with /data endpoint",
        "info": "ws://192.168.111.220:4990/data",
        "x": 270,
        "y": 560,
        "wires": []
    },
    {
        "id": "846e84b4a2d4b629",
        "type": "comment",
        "z": "pgtg_tab",
        "name": "Sample flow to control and monitor PgTgBridge",
        "info": "",
        "x": 660,
        "y": 40,
        "wires": []
    },
    {
        "id": "4e3c911202b34ef9",
        "type": "websocket-listener",
        "path": "ws://192.168.111.220:4990/command",
        "wholemsg": "false"
    },
    {
        "id": "32125c9fc1fda031",
        "type": "websocket-listener",
        "path": "ws://192.168.111.220:4990/data",
        "wholemsg": "false"
    },
    {
        "id": "pgtg_ui_group_control",
        "type": "ui_group",
        "name": "PgTg Control",
        "tab": "pgtg_ui_tab",
        "order": 1,
        "disp": true,
        "width": 6,
        "collapse": false,
        "className": ""
    },
    {
        "id": "pgtg_ui_group_status",
        "type": "ui_group",
        "name": "PgTg Status",
        "tab": "pgtg_ui_tab",
        "order": 2,
        "disp": true,
        "width": 6,
        "collapse": false,
        "className": ""
    },
    {
        "id": "pgtg_ui_group_meters",
        "type": "ui_group",
        "name": "Meters",
        "tab": "pgtg_ui_tab",
        "order": 3,
        "disp": true,
        "width": 12,
        "collapse": false
    },
    {
        "id": "pgtg_ui_tab",
        "type": "ui_tab",
        "name": "PgTg Control",
        "icon": "dashboard",
        "order": 1,
        "disabled": false,
        "hidden": false
    },
    {
        "id": "1c361e994ecb153a",
        "type": "global-config",
        "env": [],
        "modules": {
            "node-red-dashboard": "3.6.6"
        }
    }
]