Is the Node Red script from ChatGPT usable?

Hello,

First, the concept:


:one: Basic Architecture (final)

            ┌─────────────────────────┐
            │        Cerbo GX         │
            │                         │
            │  Node-RED               │
            │   ├─ Power logic        │
            │   ├─ Consumer logic     │
            │   ├─ Grid logic (later) │
            │                         │
            │  Relay 1 → Grid relay   │ (later)
            │  Relay 2 → Consumers   │
            └─────────┬───────────────┘
                      │ MQTT
        ┌─────────────┴─────────────┐
        │                           │
┌───────▼────────┐         ┌────────▼─────────┐
│ Shelly 2PM G3  │         │ Shelly 1 Gen3    │
│ (PV inverter   │         │ Consumer 2      │
│  power)        │         │                 │
└───────┬────────┘         └────────┬─────────┘
        │                             │
  2× changeover relays           Consumer
  (utility / Fronius)


Node-RED flow (configuration & logic)

[
  {
    "id": "config_globals",
    "type": "function",
    "name": "CONFIG – System & Annahmen",
    "func": "// ======================================================\n// ZENTRALE SYSTEMKONFIGURATION\n// Alles hier ändern – nirgendwo sonst!\n// ======================================================\n\n// ------------------------------\n// MQTT / SYSTEM-ANNAHMEN\n// ------------------------------\n// MQTT-Broker läuft lokal auf dem Cerbo GX\nflow.set('MQTT_BROKER', '127.0.0.1');\nflow.set('MQTT_PORT', 1883);\n\n// Victron Standard MQTT Topics (Venus OS)\n// Falls Victron ein Update macht oder sich Topics ändern,\n// sind sie hier zentral angepasst\nflow.set('TOPIC_SOC', 'N/+/system/0/Dc/Battery/Soc');\nflow.set('TOPIC_AC_LOAD', 'N/+/system/0/Ac/Consumption/Total/Power');\nflow.set('TOPIC_GRID_POWER', 'N/+/system/0/Ac/Grid/Total/Power');\nflow.set('TOPIC_GRID_CONNECTED', 'N/+/system/0/Ac/ActiveIn/Connected');\n\n// Shelly MQTT Topics (Gen3)\nflow.set('TOPIC_SHELLY_PV_R1', 'shellies/shelly-2pm-gen3/relay/0/command');\nflow.set('TOPIC_SHELLY_PV_R2', 'shellies/shelly-2pm-gen3/relay/1/command');\nflow.set('TOPIC_SHELLY_CONSUMER', 'shellies/shelly-1-gen3/relay/0/command');\n\n// ------------------------------\n// BATTERIE / SOC\n// ------------------------------\n// Unterhalb dieses SOC wird die PV-Leistung komplett gesperrt\nflow.set('SOC_MIN', 20);\n\n// SOC, bei dem der PV-Wechselrichter grundsätzlich gedrosselt werden soll\n// (nahe voll, um Laden/Entladen zu vermeiden)\nflow.set('SOC_STOP_PV', 95);\n\n// Sicherheitsabstand für Verbraucherfreigaben\n// Beispiel: STOP_PV 95 % – 5 % = Freigabe ab 90 %\nflow.set('SOC_RELEASE_OFFSET', 5);\n\n// ------------------------------\n// NETZ\n// ------------------------------\n// Erlaubter Netzbezug in Watt\n// 0 = absolut kein Bezug\n// 50–200 W sinnvoll bei großen Verbrauchern\nflow.set('GRID_IMPORT_LIMIT_W', 50);\n\n// SOC, bei dem später (optional) das Netz wieder zugeschaltet wird\n// (für Sommer-Inselbetrieb, aktuell nur dokumentiert)\nflow.set('GRID_RECONNECT_SOC', 15);\n\n// ------------------------------\n// LASTSCHWELLEN (hausintern)\n// ------------------------------\n// Unterhalb: keine PV-Freigabe\nflow.set('LOAD_LOW_W', 1000);\n\n// Ab hier: erste Leistungsstufe (30 %)\nflow.set('LOAD_MEDIUM_W', 3000);\n\n// Ab hier: zweite Leistungsstufe (60 %)\nflow.set('LOAD_HIGH_W', 5000);\n\n// ------------------------------\n// HYSTERESE / TAKTVERMEIDUNG\n// ------------------------------\n// SOC-Hysterese in Prozentpunkten\nflow.set('SOC_HYST', 2);\n\n// Leistungshysterese in Watt\nflow.set('POWER_HYST', 200);\n\n// Mindestzeit zwischen Schaltvorgängen\nflow.set('MIN_SWITCH_TIME_SEC', 60);\n\n// ------------------------------\n// VERBRAUCHERFREIGABEN\n// ------------------------------\n// Verbraucher 1: GX Relais 2 (näher an Grundlast)\nflow.set('CONSUMER1_SOC', flow.get('SOC_STOP_PV') - 5);\n\n// Verbraucher 2: Shelly 1 (größer / optional)\nflow.set('CONSUMER2_SOC', flow.get('SOC_STOP_PV') - 8);\n\n// ======================================================\n// ENDE DER KONFIGURATION\n// ======================================================\nreturn null;",
    "x": 300,
    "y": 80,
    "wires": []
  },

  {
    "id": "mqtt_soc",
    "type": "mqtt in",
    "name": "SOC",
    "topic": "N/+/system/0/Dc/Battery/Soc",
    "broker": "broker",
    "x": 140,
    "y": 160,
    "wires": [["store_soc"]]
  },
  {
    "id": "store_soc",
    "type": "function",
    "name": "Store SOC",
    "func": "flow.set('soc', Number(msg.payload));\nreturn null;",
    "x": 320,
    "y": 160,
    "wires": []
  },

  {
    "id": "mqtt_load",
    "type": "mqtt in",
    "name": "AC Load",
    "topic": "N/+/system/0/Ac/Consumption/Total/Power",
    "broker": "broker",
    "x": 140,
    "y": 220,
    "wires": [["store_load"]]
  },
  {
    "id": "store_load",
    "type": "function",
    "name": "Store Load",
    "func": "flow.set('load', Number(msg.payload));\nreturn null;",
    "x": 320,
    "y": 220,
    "wires": []
  },

  {
    "id": "mqtt_grid",
    "type": "mqtt in",
    "name": "Grid Power",
    "topic": "N/+/system/0/Ac/Grid/Total/Power",
    "broker": "broker",
    "x": 140,
    "y": 280,
    "wires": [["store_grid"]]
  },
  {
    "id": "store_grid",
    "type": "function",
    "name": "Store Grid",
    "func": "flow.set('grid', Number(msg.payload));\nreturn null;",
    "x": 320,
    "y": 280,
    "wires": []
  },

  {
    "id": "mqtt_grid_state",
    "type": "mqtt in",
    "name": "Grid Connected",
    "topic": "N/+/system/0/Ac/ActiveIn/Connected",
    "broker": "broker",
    "x": 160,
    "y": 340,
    "wires": [["store_grid_state"]]
  },
  {
    "id": "store_grid_state",
    "type": "function",
    "name": "Store Grid State",
    "func": "flow.set('grid_connected', msg.payload === 1);\nreturn null;",
    "x": 350,
    "y": 340,
    "wires": []
  },

  {
    "id": "tick_pv",
    "type": "inject",
    "name": "PV Logic Tick (10s)",
    "repeat": "10",
    "once": true,
    "x": 160,
    "y": 420,
    "wires": [["pv_logic"]]
  },
  {
    "id": "pv_logic",
    "type": "function",
    "name": "PV Leistungslogik",
    "func": "// Liest nur gespeicherte Werte\nconst soc = flow.get('soc') || 0;\nconst load = flow.get('load') || 0;\nconst gridConnected = flow.get('grid_connected');\n\n// Offline → keine EVU-Regelung (Frequenzregelung übernimmt)\nif (!gridConnected) {\n    return [{ payload: 'off' }, { payload: 'off' }];\n}\n\n// Akku schützen\nif (soc < flow.get('SOC_MIN')) {\n    return [{ payload: 'off' }, { payload: 'off' }];\n}\n\n// Lastgeführte Leistungsstufen\nif (load >= flow.get('LOAD_HIGH_W')) {\n    return [{ payload: 'on' }, { payload: 'off' }]; // 60 %\n}\n\nif (load >= flow.get('LOAD_MEDIUM_W')) {\n    return [{ payload: 'off' }, { payload: 'on' }]; // 30 %\n}\n\n// Default: keine PV-Freigabe\nreturn [{ payload: 'off' }, { payload: 'off' }];",
    "x": 360,
    "y": 420,
    "wires": [["shelly_pv_r1"], ["shelly_pv_r2"]]
  },

  {
    "id": "shelly_pv_r1",
    "type": "mqtt out",
    "name": "Shelly 2PM – Relay 1",
    "topic": "shellies/shelly-2pm-gen3/relay/0/command",
    "broker": "broker",
    "x": 640,
    "y": 400,
    "wires": []
  },
  {
    "id": "shelly_pv_r2",
    "type": "mqtt out",
    "name": "Shelly 2PM – Relay 2",
    "topic": "shellies/shelly-2pm-gen3/relay/1/command",
    "broker": "broker",
    "x": 640,
    "y": 440,
    "wires": []
  },

  {
    "id": "tick_consumer",
    "type": "inject",
    "name": "Consumer Logic Tick (15s)",
    "repeat": "15",
    "once": true,
    "x": 160,
    "y": 520,
    "wires": [["consumer_logic"]]
  },
  {
    "id": "consumer_logic",
    "type": "function",
    "name": "Verbraucherfreigabe Logik",
    "func": "const soc = flow.get('soc') || 0;\nconst grid = flow.get('grid') || 0;\n\nconst c1 = (soc >= flow.get('CONSUMER1_SOC') && grid <= flow.get('GRID_IMPORT_LIMIT_W')) ? 1 : 0;\nconst c2 = (soc >= flow.get('CONSUMER2_SOC') && grid <= flow.get('GRID_IMPORT_LIMIT_W')) ? 1 : 0;\n\nreturn [{ payload: c1 }, { payload: c2 }];",
    "x": 380,
    "y": 520,
    "wires": [["gx_relay_2"], ["shelly_consumer"]]
  },

  {
    "id": "gx_relay_2",
    "type": "victron-output-relay",
    "name": "GX Relais 2 – Verbraucher",
    "relay": "2",
    "x": 640,
    "y": 500,
    "wires": []
  },
  {
    "id": "shelly_consumer",
    "type": "mqtt out",
    "name": "Shelly 1 – Verbraucher",
    "topic": "shellies/shelly-1-gen3/relay/0/command",
    "broker": "broker",
    "x": 640,
    "y": 540,
    "wires": []
  },

  {
    "id": "broker",
    "type": "mqtt-broker",
    "name": "Cerbo MQTT",
    "broker": "127.0.0.1",
    "port": "1883"
  }
]

Context

System setup

  • 3× Victron Multiplus 5000

  • Cerbo GX

  • 25 kWh battery

  • Charging via AC-Out using a Fronius IG Plus 100V-3 with Power control card

If the Multiplus 5000 units can reliably supply the required power in all operating states, I will install a grid disconnection relay.
This would allow continuous power control via frequency shifting, which is gentler on the batteries.

However, this will only become clear after about one year of operation.
For winter operation and special cases, the relay-based solution will definitely still be required.


Online operation (grid-connected)

  • Fronius is enabled via contact when:

    • there is load demand, or

    • the battery requires charging

  • No feed-in to the public grid


Offline operation (island mode)

  • Fronius fully enabled via contact

  • Power reduction via frequency control


Fronius configuration

  • Control card installed for power steps

  • Intermediate relays installed to enable instead of block

  • Shelly output OFF = inverter OFF

  • Frequency-based power control active


Victron configuration

In VEConfigure:

Grid Code / Assistants

  • :white_check_mark: Frequency Shift Power Control enabled

  • Start at e.g. 50.2 Hz

  • Full curtailment at 52 Hz

Configuration notes:

  • All devices configured identically

  • Master unit on phase L1

Important points:

  • ESS enabled

  • Grid setpoint = 0 W (or slightly positive)

  • Frequency Shift enabled (see above)

  • No feed-in to the grid

Seems like that was just fed into an AI and now you need someone else to evaluate if it spat out nonsense or not?

Whatis your understanding of the information there?

Have you ever used node red/venus os large?
Integrated shelleys before on a GX?
Set up the three phase?
Set up an ac coupled fronius before?

The assumption is basically correct. Chatgpt generates code that seems sensible at first glance, but a human evaluation would be more interesting to me.

And I have no experience in this area.

It’s all still theoretical; only the Fronius already exists. And it would be a three Phase Setup.

I’ve successfully heavily used Grok to help me write scripts for functions I use on node-red, I never ask the A.I. to do all the work including nodes and stuff. I also always check the code myself. It has become really good lately.

Start with somthing simpler then.

Program the inverters with good base programming get that working.
(I see why this is an issue for you the IG plus does not have zero feed in.)
Install the IG on ac2 out and use the generator assistant on the victron to drive that.

Then switch the connection to grid a simple flow switching to inverter only during high production or daylight hours.

Once you have gained experience you can fiddle with the shelleys and the rest if you then want to complicate your life. FYI i spot a couple of small issues in the code.

1 Like

I agree they have their place. But it requires some experience and working knowledge to start with.

1 Like

Your configuration isn’t going to work like that.

But when you have a proper flowchart and basic setup, send me an @bachete and I will try and help you out.

1 Like

Thanks for the Offer