10 lines
7.8 KiB
JSON
10 lines
7.8 KiB
JSON
{
|
|
"html": "<div id=\"w8-root\"><div id=\"w8-header\"><div id=\"w8-title\">MODE</div></div><div id=\"w8-body\"><div id=\"w8-value\">--</div></div><div id=\"w8-status\">NO DATA</div></div>",
|
|
|
|
"css": "/* ============================================================\n W8 — Mode Indicator Widget\n Displays the active operating-mode profile name (read-only).\n Colour by base mode: CYAN (#38bdf8) = ROV, GREEN (#00e5a0) = AUV,\n AMBER (#f59e0b) = hybrid/other. Shows NO DATA / NO API when the\n data lake variable has not been published.\n Style: matches W1-W6 dark theme.\n ============================================================ */\n\n/* Root container — fills the Cockpit widget panel */\n#w8-root {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: space-between;\n height: 100%;\n padding: 10px 8px;\n box-sizing: border-box;\n background: #0d1117;\n font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;\n color: #c9d1d9;\n user-select: none;\n}\n\n/* Header block — title only, no subtitle (no sensor to name) */\n#w8-header {\n text-align: center;\n width: 100%;\n}\n\n/* Widget title */\n#w8-title {\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: #8b949e;\n margin-bottom: 2px;\n}\n\n/* Body — centres the profile name vertically and horizontally */\n#w8-body {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n flex: 1;\n width: 100%;\n}\n\n/* Active profile name — large and prominent, colour updated by JS */\n#w8-value {\n font-size: 30px;\n font-weight: 700;\n color: #8b949e; /* Default grey; updated by JS */\n line-height: 1;\n letter-spacing: -0.01em;\n text-align: center;\n word-break: break-word; /* Long custom hybrid profile names wrap instead of overflowing */\n transition: color 0.3s ease;\n}\n\n/* Status label — blank when a mode is active; NO DATA / NO API otherwise */\n#w8-status {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #484f58; /* Default grey; updated by JS */\n transition: color 0.3s ease;\n padding-top: 4px;\n min-height: 12px; /* Reserves space so layout does not jump when status text appears/clears */\n}",
|
|
|
|
"js": "// ============================================================\n// W8 — Mode Indicator Widget\n// Argonaut 3 — Cockpit DIY Widget\n//\n// Reads the active operating-mode profile name from the Cockpit\n// data lake via cockpit_bridge and displays it, colour-coded by\n// base mode. READ-ONLY — this widget never writes to the data\n// lake or calls rov_api; switching modes lives in the setup\n// wizard (POST /mode on rov_api), not here.\n//\n// Data lake variables:\n// external/rov-mode (string) — active profile name,\n// e.g. 'ROV', 'AUV', or a\n// saved hybrid profile name\n// external/rov-mode-base (number) — 0 = ROV, 1 = AUV; any\n// other value (or absent)\n// is treated as hybrid\n// Published by: cockpit_bridge (ROS2 node on RPi5), sourced\n// from /rov/mode/profile (rov_interfaces/msg/ModeProfile)\n//\n// Colour by base:\n// ROV (CYAN) : base == 0\n// AUV (GREEN) : base == 1\n// HYBRID (AMBER) : base is anything else (unknown/other)\n// NO DATA (GREY) : rov-mode is null/empty, or the API is absent\n//\n// Confirmed working Cockpit data lake read method:\n// window.cockpit.getDataLakeVariableData(variableId)\n// (confirmed via probe widget, documented UI Design v1.5)\n// ============================================================\n\n(function () {\n 'use strict';\n\n // ----------------------------------------------------------\n // Constants\n // ----------------------------------------------------------\n\n // Cockpit data lake variable IDs populated by cockpit_bridge\n var DATA_LAKE_VAR_MODE = 'external/rov-mode';\n var DATA_LAKE_VAR_BASE = 'external/rov-mode-base';\n\n // Base mode numeric codes (mirrors rov_interfaces/msg/ModeProfile.base_mode)\n var BASE_ROV = 0;\n var BASE_AUV = 1;\n\n // Poll rate — 2Hz, matching cockpit_bridge broadcast rate\n var POLL_INTERVAL_MS = 500;\n\n // Colours — match W1-W6 project palette\n var COLOUR_ROV = '#38bdf8'; // Cyan — ROV base mode\n var COLOUR_AUV = '#00e5a0'; // Green — AUV base mode\n var COLOUR_HYBRID = '#f59e0b'; // Amber — hybrid / unrecognised base\n var COLOUR_NO_DATA = '#484f58'; // Grey — no data\n\n // ----------------------------------------------------------\n // DOM element references\n // Resolved once on load — the elements are guaranteed to\n // exist because this JS runs after the HTML is injected.\n // ----------------------------------------------------------\n var elValue = document.getElementById('w8-value');\n var elStatus = document.getElementById('w8-status');\n\n // ----------------------------------------------------------\n // update()\n // Called at POLL_INTERVAL_MS. Reads the current mode profile\n // name and base from the data lake and updates the display.\n // ----------------------------------------------------------\n function update() {\n // Guard: Cockpit API must be present\n if (typeof window.cockpit === 'undefined' ||\n typeof window.cockpit.getDataLakeVariableData !== 'function') {\n setNoData('NO API');\n return;\n }\n\n // Read current profile name from the data lake.\n var rawName = window.cockpit.getDataLakeVariableData(DATA_LAKE_VAR_MODE);\n\n // ----------------------------------------------------------\n // Case 1: No data — null, undefined, or empty string\n // Shown when mode_profile_loader has not published yet.\n // ----------------------------------------------------------\n if (rawName === null || rawName === undefined || rawName === '') {\n setNoData('NO DATA');\n return;\n }\n\n // ----------------------------------------------------------\n // Case 2: Data received — determine colour from base mode\n // ----------------------------------------------------------\n var rawBase = window.cockpit.getDataLakeVariableData(DATA_LAKE_VAR_BASE);\n var base = parseInt(rawBase, 10);\n\n var colour;\n if (base === BASE_ROV) {\n colour = COLOUR_ROV;\n } else if (base === BASE_AUV) {\n colour = COLOUR_AUV;\n } else {\n // Unknown/other base (including NaN) — treat as a hybrid profile\n colour = COLOUR_HYBRID;\n }\n\n // ----------------------------------------------------------\n // Update DOM elements\n // ----------------------------------------------------------\n elValue.textContent = String(rawName);\n elValue.style.color = colour;\n\n // Clear the status line — it is only used to report NO DATA / NO API\n elStatus.textContent = '';\n }\n\n // ----------------------------------------------------------\n // setNoData(label)\n // Resets the display to the no-data state.\n // Called when the mode variable is empty or the API is absent.\n // ----------------------------------------------------------\n function setNoData(label) {\n elValue.textContent = '--';\n elValue.style.color = COLOUR_NO_DATA;\n elStatus.textContent = label;\n elStatus.style.color = COLOUR_NO_DATA;\n }\n\n // ----------------------------------------------------------\n // Startup\n // Run one immediate update so the widget does not show a\n // blank state for the first 500ms after load.\n // Then start the 2Hz poll loop.\n // ----------------------------------------------------------\n update();\n setInterval(update, POLL_INTERVAL_MS);\n\n}());",
|
|
|
|
"inheritCockpitStyles": false
|
|
}
|