tak čekám na nějaký výpadek, v modelaci to funguje
Pokud se to někomu bude hodit.
MPPT může mít každý jiný počet, tak je to udělané přes konzoli, jinak se samozřejmě na každé instalaci dá natáhnout node ke každému Solar Chargeru.
[
{
"id": "449f3aedf0f77d65",
"type": "tab",
"label": "Internet Failsafe",
"disabled": false,
"info": "Watches VRM reachability. After 5 minutes unreachable, zeroes ESS grid setpoint, disables DC feed-in, and restores any curtailed solar chargers, so remote-control loss cannot leave the battery selling indefinitely or PV curtailed indefinitely. See docs/superpowers/specs/2026-07-09-internet-failsafe-design.md.",
"env": []
},
{
"id": "0797247683b80486",
"type": "group",
"z": "449f3aedf0f77d65",
"name": "Connectivity Check",
"style": {
"label": true
},
"nodes": [
"564b9374d2445643",
"332b490b4d539220",
"7b1412957cbae47a"
],
"x": 54,
"y": 59,
"w": 400,
"h": 160
},
{
"id": "2fb17e3bdb8d0434",
"type": "group",
"z": "449f3aedf0f77d65",
"name": "Watchdog + Failsafe Actions",
"style": {
"label": true
},
"nodes": [
"061f126c73478eae",
"965e654296a40bb1",
"907df5205fcd1d9c",
"1898d4199ede8be0",
"be39483c8ec4f868",
"dbf89b233205f955"
],
"x": 494,
"y": 59,
"w": 620,
"h": 320
},
{
"id": "564b9374d2445643",
"type": "inject",
"z": "449f3aedf0f77d65",
"g": "0797247683b80486",
"name": "Tick (30s)",
"props": [],
"repeat": "30",
"crontab": "",
"once": true,
"onceDelay": 1,
"topic": "",
"x": 130,
"y": 100,
"wires": [
[
"332b490b4d539220"
]
]
},
{
"id": "332b490b4d539220",
"type": "http request",
"z": "449f3aedf0f77d65",
"g": "0797247683b80486",
"name": "Check VRM",
"method": "HEAD",
"ret": "txt",
"paytoqs": "ignore",
"url": "https://vrm.victronenergy.com",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"reqTimeout": 5000,
"headers": [],
"x": 300,
"y": 100,
"wires": [
[
"061f126c73478eae"
]
]
},
{
"id": "7b1412957cbae47a",
"type": "catch",
"z": "449f3aedf0f77d65",
"g": "0797247683b80486",
"name": "VRM Check Error",
"scope": [
"332b490b4d539220"
],
"uncaught": false,
"x": 300,
"y": 160,
"wires": [
[
"061f126c73478eae"
]
]
},
{
"id": "061f126c73478eae",
"type": "function",
"z": "449f3aedf0f77d65",
"g": "2fb17e3bdb8d0434",
"name": "Watchdog Evaluate",
"func": "// --- pure decision logic, byte-identical to scripts/watchdog-logic.js (embedded by scripts/build-flow.js) ---\nfunction evaluateTick(state, success, dryRun) {\n const THRESHOLD = 10;\n const ACTION_LABELS = [\n 'AcPowerSetPoint=0',\n 'OvervoltageFeedIn=0',\n 'Solarcharger Mode=1 (all instances)'\n ];\n\n let { failCount, fired } = state;\n let recovered = false;\n let triggered = false;\n\n if (success) {\n if (fired) recovered = true;\n failCount = 0;\n fired = false;\n } else {\n failCount += 1;\n if (failCount >= THRESHOLD && !fired) {\n triggered = true;\n fired = true;\n }\n }\n\n let statusText;\n if (triggered) {\n statusText = (dryRun ? 'DRY-RUN: would fire failsafe (' : 'FAILSAFE FIRED: (') + ACTION_LABELS.join(', ') + ')';\n } else if (recovered) {\n statusText = 'Recovered - connectivity restored';\n } else if (!success) {\n statusText = 'Check failed (' + failCount + '/' + THRESHOLD + ')';\n } else {\n statusText = 'OK';\n }\n\n return { state: { failCount, fired }, triggered, recovered, statusText };\n}\n\n// --- Node-RED wrapper ---\nconst DRY_RUN = false;\n\n// Node-RED's http request node can deliver the SAME failed check twice for one\n// underlying request: once via its own output and once via the catch node\n// (observed in the field: connection/DNS errors produced two identical-second\n// messages per 30s tick, doubling the effective failure rate). Both deliveries\n// carry the same _msgid, so ignore an immediate repeat.\nconst lastMsgId = context.get(\"lastMsgId\");\nif (msg._msgid && msg._msgid === lastMsgId) {\n return [null, null, null, null];\n}\ncontext.set(\"lastMsgId\", msg._msgid);\n\nconst success = typeof msg.statusCode === \"number\" && msg.statusCode >= 200 && msg.statusCode < 300;\nconst prevState = context.get(\"state\") || { failCount: 0, fired: false };\n\nconst result = evaluateTick(prevState, success, DRY_RUN);\ncontext.set(\"state\", result.state);\n\nnode.status({\n fill: result.triggered ? \"red\" : (success ? \"green\" : \"yellow\"),\n shape: \"dot\",\n text: result.statusText\n});\n\nconst doFire = result.triggered && !DRY_RUN;\nconst action1 = doFire ? { payload: 0 } : null;\nconst action2 = doFire ? { payload: 0 } : null;\nconst action3 = doFire ? { payload: 1 } : null; // restore solar chargers: Mode=1 on every discovered instance\nconst statusMsg = { payload: result.statusText };\n\nreturn [action1, action2, action3, statusMsg];\n",
"outputs": 4,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 560,
"y": 100,
"wires": [
[
"965e654296a40bb1"
],
[
"907df5205fcd1d9c"
],
[
"1898d4199ede8be0"
],
[
"dbf89b233205f955"
]
]
},
{
"id": "965e654296a40bb1",
"type": "victron-output-settings",
"z": "449f3aedf0f77d65",
"g": "2fb17e3bdb8d0434",
"service": "com.victronenergy.settings",
"path": "/Settings/CGwacs/AcPowerSetPoint",
"serviceObj": {
"service": "com.victronenergy.settings",
"name": "Venus settings"
},
"pathObj": {
"path": "/Settings/CGwacs/AcPowerSetPoint",
"type": "integer",
"name": "Grid set-point (W)",
"mode": "both"
},
"name": "Set Grid Setpoint = 0",
"onlyChanges": false,
"roundValues": "no",
"rateLimit": 0,
"outputs": 0,
"conditionalMode": false,
"condition1Operator": ">",
"condition2Enabled": false,
"condition2Service": "",
"condition2Path": "",
"condition2Operator": ">",
"logicOperator": "AND",
"outputTrue": "true",
"outputFalse": "false",
"outputOnChange": false,
"debounce": 2000,
"x": 820,
"y": 80,
"wires": []
},
{
"id": "907df5205fcd1d9c",
"type": "victron-output-settings",
"z": "449f3aedf0f77d65",
"g": "2fb17e3bdb8d0434",
"service": "com.victronenergy.settings",
"path": "/Settings/CGwacs/OvervoltageFeedIn",
"serviceObj": {
"service": "com.victronenergy.settings",
"name": "Venus settings"
},
"pathObj": {
"path": "/Settings/CGwacs/OvervoltageFeedIn",
"type": "enum",
"name": "Feed excess DC-coupled PV into grid",
"enum": {
"0": "Don't feed excess DC-tied PV into grid",
"1": "Feed excess DC-tied PV into the grid"
},
"mode": "both"
},
"name": "Set DC Feed-In Disabled",
"onlyChanges": false,
"roundValues": "no",
"rateLimit": 0,
"outputs": 0,
"conditionalMode": false,
"condition1Operator": ">",
"condition2Enabled": false,
"condition2Service": "",
"condition2Path": "",
"condition2Operator": ">",
"logicOperator": "AND",
"outputTrue": "true",
"outputFalse": "false",
"outputOnChange": false,
"debounce": 2000,
"x": 820,
"y": 130,
"wires": []
},
{
"id": "1898d4199ede8be0",
"type": "exec",
"z": "449f3aedf0f77d65",
"g": "2fb17e3bdb8d0434",
"command": "for svc in $(dbus -y | grep com.victronenergy.solarcharger | awk '{print $1}'); do dbus -y \"$svc\" /Mode SetValue 1; done",
"addpay": "",
"append": "",
"useSpawn": "false",
"timer": "10",
"winHide": false,
"oldrc": false,
"name": "Restore Solar Chargers (dbus, dynamic)",
"x": 820,
"y": 180,
"wires": [
[
"be39483c8ec4f868"
],
[
"be39483c8ec4f868"
],
[
"be39483c8ec4f868"
]
]
},
{
"id": "be39483c8ec4f868",
"type": "debug",
"z": "449f3aedf0f77d65",
"g": "2fb17e3bdb8d0434",
"name": "Solar Restore Exec Output",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 820,
"y": 230,
"wires": []
},
{
"id": "dbf89b233205f955",
"type": "debug",
"z": "449f3aedf0f77d65",
"g": "2fb17e3bdb8d0434",
"name": "Watchdog Status",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 820,
"y": 280,
"wires": []
}
]