{ "html": "
\n
\n
\n Green\n
\n
\n
\n Amber\n
\n
\n
\n Red\n
\n
\n
Connecting\u2026
\n
\u2014
", "css": "*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n:root {\n --bg: #09111a;\n --text0: #d8eeff;\n --text1: #6a9bbf;\n --green: #00e09a;\n --amber: #ffb830;\n --red: #ff3a5a;\n --dim: #1a2d42;\n --border: #1e3050;\n --mono: 'Courier New', monospace;\n}\nbody {\n background: var(--bg);\n color: var(--text0);\n font-family: var(--mono);\n height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n user-select: none;\n}\n#circles { display: flex; gap: 12px; align-items: center; justify-content: center; margin-bottom: 10px; }\n.circle-wrap { display: flex; flex-direction: column; align-items: center; gap: 4px; }\n.circle {\n width: 36px; height: 36px;\n border-radius: 50%;\n border: 2px solid var(--border);\n background: var(--dim);\n transition: background 0.3s, border-color 0.3s, box-shadow 0.3s;\n}\n.circle.green { background: var(--green); border-color: var(--green); box-shadow: 0 0 12px var(--green); }\n.circle.amber { background: var(--amber); border-color: var(--amber); box-shadow: 0 0 12px var(--amber); }\n.circle.red { background: var(--red); border-color: var(--red); animation: flash-red 1s ease-in-out infinite; }\n@keyframes flash-red {\n 0%, 100% { box-shadow: 0 0 16px var(--red); }\n 50% { box-shadow: 0 0 32px var(--red), 0 0 8px #ff000088; }\n}\n.circle-label { font-size: 9px; color: var(--text1); text-transform: uppercase; letter-spacing: 0.08em; }\n.circle-label.active { color: var(--text0); font-weight: bold; }\n#message { font-size: 11px; color: var(--text1); text-align: center; max-width: 180px; line-height: 1.4; min-height: 16px; }\n#message.green { color: var(--green); }\n#message.amber { color: var(--amber); }\n#message.red { color: var(--red); }\n#footer { margin-top: 6px; font-size: 9px; color: #2a4060; }", "js": "// Config\nvar VARIABLE = 'rov_failsafe';\nvar POLL_MS = 500;\nvar STATE_GREEN = 0, STATE_AMBER = 1, STATE_RED = 2;\nvar MESSAGES = {};\nMESSAGES[STATE_GREEN] = 'Systems nominal';\nMESSAGES[STATE_AMBER] = 'Parameter degraded \u2014 check system';\nMESSAGES[STATE_RED] = 'CRITICAL \u2014 Safe action triggered';\n\n// DOM\nvar cGreen = document.getElementById('c-green');\nvar cAmber = document.getElementById('c-amber');\nvar cRed = document.getElementById('c-red');\nvar lGreen = document.getElementById('l-green');\nvar lAmber = document.getElementById('l-amber');\nvar lRed = document.getElementById('l-red');\nvar msgEl = document.getElementById('message');\nvar footEl = document.getElementById('footer');\n\nfunction applyState(state, reason) {\n [cGreen, cAmber, cRed].forEach(function(c) { c.classList.remove('green','amber','red'); });\n [lGreen, lAmber, lRed].forEach(function(l) { l.classList.remove('active'); });\n msgEl.classList.remove('green','amber','red');\n if (state === STATE_GREEN) {\n cGreen.classList.add('green'); lGreen.classList.add('active');\n msgEl.classList.add('green'); msgEl.textContent = reason || MESSAGES[STATE_GREEN];\n } else if (state === STATE_AMBER) {\n cAmber.classList.add('amber'); lAmber.classList.add('active');\n msgEl.classList.add('amber'); msgEl.textContent = reason || MESSAGES[STATE_AMBER];\n } else if (state === STATE_RED) {\n cRed.classList.add('red'); lRed.classList.add('active');\n msgEl.classList.add('red'); msgEl.textContent = reason || MESSAGES[STATE_RED];\n } else {\n msgEl.textContent = 'Waiting for data\u2026';\n }\n}\n\nfunction poll() {\n try {\n if (typeof window.cockpit === 'undefined' || typeof window.cockpit.getDataLakeValue !== 'function') {\n applyState(null); footEl.textContent = 'API not ready'; return;\n }\n var raw = window.cockpit.getDataLakeValue(VARIABLE);\n if (raw === null || raw === undefined) {\n applyState(null); footEl.textContent = 'Waiting for ' + VARIABLE; return;\n }\n applyState(parseInt(raw, 10), null);\n footEl.textContent = 'Updated ' + new Date().toLocaleTimeString();\n } catch(err) {\n applyState(null); footEl.textContent = 'Poll error';\n console.error('[Health Widget]', err);\n }\n}\n\nsetTimeout(poll, 300);\nsetInterval(poll, POLL_MS);" }