Automating Heating using Shelly - Modbus TCP vs MQTT vs Node-RED

Hi all,

I would like to automate an Shelly 1PM Gen3 switch based on data from my Victron PV system (Quattro with Color Control GX), e.g., using the battery SoC and PV surplus. I am considering three approaches and would appreciate any guidance or feedback (since they might well be flawed):

Option 1: Purely Local – Modbus TCP

  • Use Modbus TCP to read battery SoC and PV power directly from the CCGX.
  • My Shelly should support Modbus TCP, so it should be able to directly read the registers from the Victron device and switch the heating based on defined thresholds (e.g., SoC > 80% or PV surplus > 1000W) programmed with simple rules in the Shelly.
  • This would work locally without internet and even if VRM is down.
  • However, it seems Modbus TCP settings can only be configured when I’m on the local network (which I am currently not) — remote access through Shelly Cloud doesn’t seem to expose these settings. That would make the system harder to maintain.

Option 2: Cloud-Based – MQTT

  • The second option could be to use MQTT to communicate with the Shelly.
  • This would work remotely over the internet, but I would need to run an MQTT broker on a Raspi or a home server, to run the automation rules

Option 3: Local or Cloud-Based – Node-RED

  • Another option is to use Node-RED to pull data from Victron (via Modbus TCP or MQTT) and control the Shelly using its HTTP API.
  • I believe Node-RED can be run locally (e.g. Raspi or NAS) or in the cloud (e.g., on a VPS).
  • This could combine some benefits of Modbus and MQTT while allowing more flexible automation logic, at the price of an extra layer of complexity, and dependency on Node-RED.

Questions:

  1. Has anyone successfully set up direct Modbus TCP automation between a Victron and Shelly? (Note I am interested in passing information from Victron to Shelly - I have already seen solutions in this forum that address the opposite direction using the Shelly’s outbound websocket.)
  2. Any best practices?

My preference would be for a simple solution, avoiding programming and maintenance.
If option 1 works, it could be a simple stable setup, not requiring me to update servers etc.

Thanks for any insights!
Bernhard

A little update here.

I now believe that the information that the Shelly 1PM gen3 supports Modbus TCP may have been erroneous.

Another piece of news is that the Shelly 1PM gen4 just came out, and it supports both Inbound and Outbound Web Sockets. I wonder if that might be another Option.

All the best
bernhard

Simplest solution for solar hot water diversion is to use ACout2 using assistants, but that won’t work for proportional power like an Eddy or Iboost. It’s on or off, so a lot of solar is wasted in the shoulders of the trigger points. Sorry I’ve not tried the other methods.

Hello, first option is gold, independent. But i use shelly script to get data from mqtt server, its works on Venus os. It have safety option if wifi is down or mqtt server. I am not using nodered, script is very easy to make in JS. And mqtt make you option to remote control parameters true vrm mqtt relay.

I have spent some time on this, exploring two paths:

  1. I found this info on the victron pages: “Once uploaded to the VRM Portal by a Color Control GX, or another device running our Venus OS, the data can be requested via our VRM JSON API:
    VRM API documentation

I think this may be an option, but I decided instead to try this:

  1. I found that Victron runs a MQTT brokers accessible from the internet. My CCGX device pushes MQTT to this, provided MQTT is activated in the remote console.
    It took me a while but I eventually managed to read the values of my installation (e.g., current solar power, battery SoC) from the respective server.
    Right now I am using mosquitto from a unix shell, but I’d hope to get it working on a shelly script.
    I am struggling: for starters, I do no manage to upload the certificate that I had to generate (and that works with mosquitto) into my Shelly. I am also struggling to get the script running, since I currently only have remote access and the debugging is a pain without a console.
    @Jaaskelainen: would you be willing to share your script?

@AI C: could you say a little more about this solution? Is there a web page where it’s described?

All the best and many thanks!
bernhard

Hello, there is no website, it is not published. i dont use tls, because its run on local lan. My Venus os is old 2.90, mqtt dont have login or password. So first what you need is in shelly setting setup mqtt server that run on Venus OS. Just login by shelly ip address in browser. In setting menu is mqtt, setup mqtt server. Mqtt explorer is your bestfriend.

You can try to understand this :grin:

var state = {
    run: false,
    charger_status: 0,
    power: 0,
    power_set: 0,
    soc: 0
  };
  
  var charger_string = ["", "", "", "Bulk", "Absorption", "Float"]; 
  var write = {
    currentlimit: 0
  };
  var fixedArray = [0, 0, 0, 0, 0];
  
  function run(obj) {
    if (obj.run === true) {
      Shelly.call("Switch.set", { 'id': 0, 'on': true });
      //print("Relay ON");
    } else {
      Shelly.call("Switch.set", { 'id': 0, 'on': false });
      //print("Relay OFF");
    }
  }
  
  function addValue(value) {
    fixedArray.push(value);
    if (fixedArray.length > 5) {
      fixedArray = fixedArray.slice(1);
      //print(fixedArray)
    }
  }

  function add(a, b) {
    return a + b;
  };
  
  function calculateAverage(arr) {
    let sum = 0;
    for (let i = 0; i < arr.length; i++) {
      sum = add(sum, arr[i]);
    }
    //print(sum/arr.length);
    return sum / arr.length;
  }
  
  function setChargingLimit() {
    const power = calculateAverage(fixedArray);
    if(power > 410){
      state.power_set = 1;    
    }else if(power < 150){
      state.power_set = 0;
    }
    
  }
  
  function subscription() {
    MQTT.subscribe("N/*your_ID*/system/0/Dc/Battery/Power", handleMessage);
    MQTT.subscribe("N/*your_ID*/solarcharger/289/State", handleMessage);
    MQTT.subscribe("N/*your_ID*/system/0/Dc/Battery/Soc", handleMessage);
  }
  
  function handleMessage(topic, message) {
    var messageContent = JSON.parse(message);
    if (topic === "N/*your_ID*/system/0/Dc/Battery/Power") {
      state.power = parseFloat(messageContent.value.toFixed(0));
      addValue(state.power);
    } else if (topic === "N/*your_ID*/solarcharger/289/State") {
      state.charger_status = messageContent.value;
    } else if (topic === "N/*your_ID*/system/0/Dc/Battery/Soc") {
      state.soc = parseFloat(messageContent.value.toFixed(0));
    }
  }
  
  function Keepalive() {
    if (MQTT.isConnected()) {
      print("Keepalive: MQTT is connected. Sending keepalive.");
      MQTT.publish("R/*your_ID*/keepalive", "");
      MQTT.publish("R/*your_ID*/solarcharger/289/State", "");
      MQTT.publish("N/*your_ID*/Battery1/State.power", JSON.stringify(state.power));
      MQTT.publish("N/*your_ID*/Battery1/State.power_set", JSON.stringify(state.power_set));
    } else {
      print("Keepalive: MQTT is not connected.");
      Shelly.call("Switch.set", { 'id': 0, 'on': false });
      print("Keepalive: Relay disabled.");
    }
    
  }
  
  function main() {
    try {
      setChargingLimit();
    } catch (e) {
      MQTT.publish("N/*your_ID*/Battery1/State.error", JSON.stringify(e)); //custom topic that send errors
    }
    
    if (state.charger_status === 4 || state.charger_status === 5 || state.soc > 99 || state.power_set === 1) {
      state.run = true;
    } else {
      state.run = false;
    }
    run(state);
    
    print("Charger status: " + charger_string[state.charger_status] + "\nSOC: " + state.soc + "%" + "\nRelay status: " + state.run);
  }  
  Keepalive();
  subscription();
  Timer.set(30000, true, Keepalive);
  Timer.set(10000, true, main);

I think you’re onto a more functional path than using assistants, just be aware the CCGX is limited in processing power.

I use the generator assistant in Ve.Config, as I already use the GX relay out 1 for my actual generator and relay 2 doesn’t have that functionality, but the GX generator control has more options to use that could then be connected to the Quattro digital inputs to trigger the Acout2.

Bear in mind the logic is reversed when using the generator assistant in the Quattro, so ‘when load is higher than 5,500W’ ACout2 will turn off, when the load is lower than 2,000W Acout2 will turn on.

Here’s a Video someone did using the programmable relay to do the same:

https://youtu.be/tJGP3G0P7no?si=g_CWvrF83IyZC9JB

Google "Victron Hot Water Diversion’ for other methods

Can i upload assistant to my second non-victron inverter? Assistant have limited function and you cant expand it. So i cant use it. True mqtt you can get whole system info,that is not available if you use assistant. So it depends on what you want to achieve.

Yeah, MQTT or Node Red are more elegant solutions, but require a more advanced setup / knowledge. Also a CCGX is not very powerful, it can run out of processing power quickly and cause reboots if not sticking to vanilla Venus GuiV1. That’s why I went from CCGX to Cerbo, then that also ran out of processing power for the Mods I use, so now I have just swapped it for an Ekrano.

I dont use mods on venus OS, but i run my venus OS in virtual machine on x86 miniPC. So i dont run out of prosessor power anyway, 4 core intel i5 and 4gig RAM, SSD.

@bernhard-victron Is looking for a solution for his CCGX, that may limit the options.

CCGX have stock mqtt software, i dont see any problem using it.