Zero feed-in photovoltaic system with Shelly and OpenDTU

Hello,
I would like to realize a “zero feed-in” with a small photovoltaic system. I have downloaded an example flow from the internet. I don’t quite understand the calculation in the function node. I have logically taken the entity-id for “Leistung alle 3 Phasen” from my Shelly. The first image shows the current power of the Shelly 3EM and the power that the inverter would like to switch.
The inverter limit of maxPower = 600 should remain. The power should be read in and if the value is positive from 1 to 600 W, then this value should be sent to the inverter (WR MQTT). The inverter then switches up the power. If the value from the Shelly is negative from 1 to 600 W, then the inverter should be reduced by this number.
My next problem is that I don’t know how the power change in Node Red is sent to the inverter. In Homeassistant/MQTT Explorer I have found out how to send values to the inverter. Please have a look at photo 5. But which function and parameters do I have to use in Node Red.

Thank you

Translated with DeepL.com (free version)





If you want to send the value of the shelly, without changing that value, you can use a trigger or event state node. The trigger node, every time the value of the shelly updates and is above zero, it will fire the value out of the top output. Below 0 it would come out the bottom.

With an event state it will only fire once when the value is above zero. Otherwise every time the shelly updates it will fire out the bottom. It will need to cross below zero and then it will fire out the top once it crosses zero again.

image

To use the mqtt node put the topic path shown in mqtt explorer. When you send that node a message, whatever value is in the payload position will be sent to the topic in the node. You can test this with an inject node. Place the number value in the inject and then send it to the mqtt topic.

image

image

Hi,
and thanks again. MQTT works fine now. :+1:

The trigger updates too frequently. I like every 30s or so.

I wanna go with the calculation but I still don´t know whats wrong with it. I guess somethings wrong with the ‘power’ definition.

As far as I can see the function does as it is set up to do. If the power reading comes back at anything below 600 it will return 600. If it is above 600 it will return 600. If it is below zero it will return 1.

You can use an inject node set to repeat with a current state node. Delete the data message in the output properties of the current state by clicking on the x to the right.

image

E. From what I am reading in the first post, there should be no function. It sounds like you want to send the value as is.

If the number comes in negative, what number do you expect to be sent to the mqtt node?
If it is between 1 and 600, what is the expected output?
If the number is above 600, what is the expected output?

The maximum output power value of the inverter should be able to be entered maxPower 600W.

I imagine the calculation as follows:

Shelly measures +300W, then the inverter should output 300W and save this value. The inverter is now set to 300W. If the Shelly now measures +100W, then the inverter must be set to 400W (300W+100W) and this value must be saved. If the Shelly now measures +300W, the inverter should be set to 600W (400W+300W > maxPower) and the value saved.
If the Shelly now measures -100W, the inverter should be set to 500W (maxPower - 100W) and save the value.

Actually, I can also read out the set value of the inverter via MQTT and offset it against the current value of the Shelly, or what makes more sense?

Ideally, the inverter would only adjust the power if the change in power is > 30W compared to the previous query.

Translated with DeepL.com (free version)

Replace the contents of the function node with the following.

//get previous value, if it does not exist use 600

const prevLevel = context.get('prevLevel') || 600;

const incomingLevel = msg.payload;

// if next level is above 600, limit to 600

let nextLevel = incomingLevel + prevLevel;
if (nextLevel > 600) {
  nextLevel = 600;
}

// No output if change is less than 30 

const levelTest = Math.abs(nextLevel- prevLevel);
if (levelTest < 30) {
  msg = null;
  node.status({fill:"yellow",shape:"ring",text:"change < 30"});
} 

// Change above 30, store value and send new level

else {
  context.set('prevLevel', nextLevel);
  msg.payload = nextLevel;
  node.status({fill:"green",shape:"dot",text:"sending " + nextLevel});
}

return (msg);

Thanks A lot. Today not enough time to test it. I´ll inform you the next days.

:+1: :+1: :+1: :+1:

Hi,

your code works really great. Thanks again…

I have tried to make the inverter limit visible is reached… unfortunately this was not successful. Can you take a look at it?

//get previous value, if it does not exist use 300
var maxPower = 400;
const prevLevel = context.get('prevLevel') || 300;
const incomingLevel = msg.payload;

// if next level is above maxPower, limit to maxPower

let nextLevel = incomingLevel + prevLevel;
if (nextLevel > maxPower) {
    nextLevel = maxPower;
}

// No output if change is less than 20 

const levelTest = Math.abs(nextLevel - prevLevel);
if (levelTest < 20) {
    msg = null;
    node.status({ fill: "yellow", shape: "ring", text: "Änderung < 20" });
}

// Change above 20, store value and send new level

else {
    context.set('prevLevel', nextLevel);
    msg.payload = nextLevel;
    node.status({ fill: "green", shape: "dot", text: "neue Leistung " + nextLevel });
}

//make Inverlimit visible

//else {
//if  (nextLevel > maxPower) {
//    msg = null;
//    node.status({ fill: "red", shape: "dot", text: "Inverterlimit reached" });
//}

return (msg);

As a last change for now, I would like to limit the inverter power to value X about an hour before sunset and change to “maxPower” an hour after sunrise. I’ll try to deal with this myself first.

Many thanks so far

If max power is going to be variable. This

if  (nextLevel > maxPower) {
    msg = null;
    node.status({ fill: "red", shape: "dot", text: "Inverterlimit reached" });
}

will be problematic. The next level could exceed max power because max power went from 600 to 400. The change to 400 will be ignored.

The easiest way to deal with day or night would be to use the elevation of the sun. I think somewhere around 6 degrees will be an hour after sunrise/ hour before sunset.

I also took what I think would be a logical step to use cloud cover in the equation. I use openweather map. If you use a different service the path to cloud cover will be different.

// calculate max power
function calcMax(){
  // get sun's elevation
  const elevation = global.get('homeassistant.homeAssistant.states["sun.sun"].attributes.elevation');
  // get cloud cover
  const cloudCover = global.get('homeassistant.homeAssistant.states["weather.openweathermap"].attributes.cloud_coverage');
  
  let maxPower = 400;
  
  if (elevation >= 6 && cloudCover <= 20) {
    maxPower = 600;
  }
  return maxPower
}


const maxPower = calcMax();


//get previous value, if it does not exist use current max power
const prevLevel = context.get('prevLevel') || maxPower;

const incomingLevel = msg.payload;

// if next level is above max power, limit to max power
let nextLevel = incomingLevel + prevLevel;
if (nextLevel > maxPower) {
  nextLevel = maxPower;
}

// No output if change less than 20 
const levelTest = Math.abs(nextLevel - prevLevel);
if (levelTest < 20) {
  msg = null;
  node.status({fill:"yellow",shape:"ring",text:"change < 30"});
} 
// Change above 20, store value and send new level
else {
  context.set('prevLevel', nextLevel);
  msg.payload = nextLevel;
  node.status({fill:"green",shape:"dot",text:"sending " + nextLevel});
}

return (msg);


Edit:

The cloud cover and elevation test can be expanded like this

  if (elevation >= 6 && cloudCover <= 20) {
    maxPower = 600;
  } else if (elevation >= 6 && cloudCover >= 20 && cloudCover < 60) {
    maxPower = 500;
}

Seems like great work again. Thanks.

if (elevation >= 6 && cloudCover <= 20) {
    maxPower = 600;
  } else if (elevation >= 6 && cloudCover >= 20 && cloudCover < 60) {
    maxPower = 500;
}

In your code extension is no “returm maxPower”. Is that correct?

Best regards

It would need to return max power, this is the complete function.

Hi and thanks again. I couldn´t test it today but will on the weekend.

Great job

Hi,

your code works really great!

I still have to make some changes due to the structure of my system.
The query of the Shelly’s current power should only take place when a switch is closed for at least 30 seconds.
So only forward “Timestamp” when switch X is on. I imagine that I make a calculation when “msg.payload = msg.payload + switch”, then msg.payload is forwarded from timestamp to the Shelly. If “msg.payload < msg.payload + switch” (switch should have a 0 or 1 as payload) then msg.payload=null is forwarded.
Is this feasible?

Switch off means also the inverter is off.

Thank you very much

Translated with DeepL.com


If the trigger for the flow is the switch, and you require it to be on for 30 seconds you can use an event state.

image

Hello,
the trigger is probably not a solution for me after all.

Can it work if I calculate the function node with two payloads? I have the Shelly, which supplies the current power, and I would use MQTT to get the actual inverter power setting([serial]/status/limit_absolute). Then ‘prevLevel’ would be replaced with “[serial]/status/limit_absolute” in the code.
I tried to find a solution, but I don’t know how to get two msg.payloads into the node and separate them and does the “[serial]/status/limit_absolute” provide values at the right time or does it not matter?
Beginner’s questions, I know

//get previous value, if it does not exist use 300
var maxPower = 400;
const prevLevel = context.get('prevLevel') || 300;
const incomingLevel = msg.payload;

// if next level is above maxPower, limit to maxPower

let nextLevel = incomingLevel + prevLevel;
if (nextLevel > maxPower) {
    nextLevel = maxPower;
}

// No output if change is less than 20 

const levelTest = Math.abs(nextLevel - prevLevel);
if (levelTest < 20) {
    msg = null;
    node.status({ fill: "yellow", shape: "ring", text: "Änderung < 20" });
}

// Change above 20, store value and send new level

else {
    context.set('prevLevel', nextLevel);
    msg.payload = nextLevel;
    node.status({ fill: "green", shape: "dot", text: "neue Leistung " + nextLevel });
}

//make Inverlimit visible

//else {
//if  (nextLevel > maxPower) {
//    msg = null;
//    node.status({ fill: "red", shape: "dot", text: "Inverterlimit reached" });
//}

return (msg);

If you haven’t already, create a sensor for the mqtt topic that supplies the actual reading. Once it is a sensor, change the definition of prevLevel to

const prevLevel = global.get('homeassistant.homeAssistant.states["sensor.actual_level"].state')

Just change the name sensor.actual_level to the name of your entity.

If the trigger does not work in the current state node add the time requirement for it to be on for 30 seconds.

Hi,
there ist a sensor for the mqtt topic. I changed the code and the output of the node says NaN.

I don´t understand really understand what do you mean with “If the trigger does not work in the current state node add the time requirement for it to be on for 30 seconds.”

Best regards

Use this instead for prev level

const prevLevel = Number(global.get('homeassistant.homeAssistant.states["sensor.actual_level"].state'))

You want the shelly switch to be on for 30 seconds before it is read? In the current state set the time req to 30 seconds.

image

Ah… ok

Thanks

I wanna thank you again for all your help. It´s fine for me now.

:clap: