Greenhouse door control

I have discussed build up of my flow in another post, where I got a lot of good help. However I could not completely understand that flow, particularly how function code works and was afraid to put it to control real greenhouse door. So I built my own function code and it seems to work with my inhouse testing. I have one week to put it in place and would really appreciate if someone could verify my thinking

The idea is based on temp reading that I save into "Temp-Air1" flow context via another flow. I have then check it vs context "position" value and calculate "tm" (time to move) value, which is also save to flow context. It can be either negative or positive, which allows me to use switch and call either "Open" or "Close" action within Home Assistant. I then use delay node to wait for x amount of seconds saved in context "tm". After that, I send "Stop" action within Home Assistant to stop door opener.

[{"id":"88de674f.e0ed78","type":"function","z":"123bd5e5.5a9a9a","name":"","func":"var current = flow.get(\"Temp-Air1\") || 0;\nvar position = flow.get(\"position\") || 0;\nnode.warn([\"current=\",current]);\nnode.warn([\"position=\",position]);\nif(current < 20 ){\n    tm=0-position\n    flow.set('position',0);\n}else if(current > 26){\n    tm=48-position\n    flow.set('position',48);\n}else if(current <= 22){\n    tm=12-position\n    flow.set('position',12);\n}else if(current <= 24){\n    tm=24-position\n    flow.set('position',24);\n}else if(current <= 26){\n    tm=36-position\n    flow.set('position',36);\n}\n\nif (tm === 0) { return null; }\nif (tm<0){\n    msg.payload = \"close_cover\";\n} else if (tm>0){\n    msg.payload = \"open_cover\";\n}\n\n\nflow.set(\"tm\", tm)\nnode.warn([\"tm=\",tm]);\n\nmsg.payload=tm\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":280,"y":1400,"wires":[["7adb187b.c32098"]]},{"id":"d99dbe66.4b9fb","type":"inject","z":"123bd5e5.5a9a9a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":110,"y":1400,"wires":[["88de674f.e0ed78"]]},{"id":"7adb187b.c32098","type":"switch","z":"123bd5e5.5a9a9a","name":"","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"str"},{"t":"lt","v":"0","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":450,"y":1400,"wires":[["2a40cc86.3f3af4"],["672d3423.7827cc"]]},{"id":"672d3423.7827cc","type":"api-call-service","z":"123bd5e5.5a9a9a","name":"Close","server":"bbbd4030.fdb23","version":1,"debugenabled":false,"service_domain":"cover","service":"close_cover","entityId":"cover.bf9767fbce7a507ed4xp8g","data":"","dataType":"jsonata","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":590,"y":1440,"wires":[["79213eb.18dacc"]]},{"id":"2a40cc86.3f3af4","type":"api-call-service","z":"123bd5e5.5a9a9a","name":"Open","server":"bbbd4030.fdb23","version":1,"debugenabled":false,"service_domain":"cover","service":"open_cover","entityId":"cover.bf9767fbce7a507ed4xp8g","data":"","dataType":"jsonata","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":590,"y":1360,"wires":[["79213eb.18dacc"]]},{"id":"79213eb.18dacc","type":"function","z":"123bd5e5.5a9a9a","name":"","func":"var tm = flow.get(\"tm\") || 0;\nif (tm<0){\n    tm=tm*(-1)\n}\nmsg.delay=tm*1000\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":740,"y":1420,"wires":[["e28387ff.3664d8"]]},{"id":"a396b36a.30e59","type":"api-call-service","z":"123bd5e5.5a9a9a","name":"Stop","server":"bbbd4030.fdb23","version":1,"debugenabled":false,"service_domain":"cover","service":"Stop_cover","entityId":"cover.bf9767fbce7a507ed4xp8g","data":"","dataType":"jsonata","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1050,"y":1420,"wires":[[]]},{"id":"e28387ff.3664d8","type":"delay","z":"123bd5e5.5a9a9a","name":"","pauseType":"delayv","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":900,"y":1420,"wires":[["a396b36a.30e59"]]},{"id":"39264bd4.fc5374","type":"group","z":"123bd5e5.5a9a9a","name":"This part for manual \"Temp-Air\" entry, for test purposes","style":{"label":true,"color":"#000000","stroke":"#7fb7df","fill":"#bfdbef"},"nodes":["c7538224.afc3a","cf2b35ab.a4d0c8","8533f7cc.1f1578","72409071.3eade","ff2f5a10.b95cb8","c12bec98.c794f"],"x":54,"y":1459,"w":392,"h":242},{"id":"c7538224.afc3a","type":"function","z":"123bd5e5.5a9a9a","g":"39264bd4.fc5374","name":"","func":"var output = msg.payload\nif (output>0)\n {flow.set('Temp-Air1',output);}\n\n\n\nnode.warn([\"position=\",output]);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":360,"y":1500,"wires":[["88de674f.e0ed78"]]},{"id":"cf2b35ab.a4d0c8","type":"inject","z":"123bd5e5.5a9a9a","g":"39264bd4.fc5374","name":"19C - Position 0","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"19","payloadType":"num","x":180,"y":1500,"wires":[["c7538224.afc3a"]]},{"id":"8533f7cc.1f1578","type":"inject","z":"123bd5e5.5a9a9a","g":"39264bd4.fc5374","name":"21C - Position 12","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"21","payloadType":"num","x":180,"y":1540,"wires":[["c7538224.afc3a"]]},{"id":"72409071.3eade","type":"inject","z":"123bd5e5.5a9a9a","g":"39264bd4.fc5374","name":"23C - Position 24","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"23","payloadType":"num","x":180,"y":1580,"wires":[["c7538224.afc3a"]]},{"id":"ff2f5a10.b95cb8","type":"inject","z":"123bd5e5.5a9a9a","g":"39264bd4.fc5374","name":"25C - Position 36","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"25","payloadType":"num","x":180,"y":1620,"wires":[["c7538224.afc3a"]]},{"id":"c12bec98.c794f","type":"inject","z":"123bd5e5.5a9a9a","g":"39264bd4.fc5374","name":"28C - Position 48","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"28","payloadType":"num","x":180,"y":1660,"wires":[["c7538224.afc3a"]]},{"id":"bbbd4030.fdb23","type":"server","name":"Home Assistant","legacy":false,"addon":false,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

By the way, if you do not have Home Assistant nodes, those are very simple just issue one of three commands to the device (Open, Close or Stop). Those are tested, working fine and there is not complex code or variables there. My concern is mainly about first function, believe rest is correct even with my limited knowledge.


  • Due to some code malfunction door will not completely close at cold temp, or will not completely open when it is hot. I think the function that I wrote should work fine. If, for example, door does not close completely when temp drops below 20C (in my case), then it will keep closing when next recurring timestamp will be called.
  • Door will not move if RaspberryPi will hang, or HomeAssistant will hang, or Tuya service (needed to control device) will become irresponsive, or Wifi will fail, or electricity will fail. Do not have answer... But thinking of installing second temp readed, second RPI to cover those, also set some kind of warning system when some critical item from above list is down. To be continued...

What I would suggest is you write down all the conditions and actions that should take place. That should give you a guide of what to do

Thanks, here are conditions that I set up:

  • Complete close door when it is below 20C
  • Completely open door when it is above 26C

Total opening time from complete close to complete open is 48 sec. I have set up 5 possible positions by using seconds that I run open or close command and position that I save into flow context. I am not sure if parameter of closure can be read from device itself, that is why I did so. So my function should:

  • Be fully closed when it is below 20
  • Open 25% when it is between 20C and 22C
  • Open 50% when it is between 22C and 24C
  • Open 75% when it is between 24C and 26C
  • Open 100% when it is above 26C
    Each step should take approx 12 second to take as total opening or closing time is 48 seconds.
    I understand that mechanical device might take a bit longer, or a bit shorter time perform one step. Like it can take 11 sec to close vs. 13 sec to open for example. This is not critical in my case. Temp in greenhouse is gradually rising or decreasing. Most important is to fully open when it is hot and fully close when it is cold. I think that function I put in place should do the trick as I will run recurring inject node every 10 minutes (think that is right frequency for this kind of control) and device should be closed 100% and open 100% when needed, even if it takes one or two extra rounds.

I have acouple thoughts for you.

Have you thought about putting some kind of switch that would be activated when the door is completely closed. That would give you an indication that it did get fully closed.

How long does the door have to be below 20 to close the door? What happend when the temp fluctuate between 20.0 and 20.1 it will start to open then close then open then close. This will happen at each temperature event point. You may want a delay between readings of N seconds or minutes.

Could you attach a series of switches over the track of the door and something on the door that would tigger each switch as the door moved. That way you would have better control. And if the door is suppose to start moving and doesn't pass past one of the indicators in N seconds (plus a little) you could trigger an alarm.

Will you have a button that will manually trigger the opening closing of the door in case you need to get inside when it is below 20.

Will you have an override to close it incase a gusty thuderstorm is coming in?

Thanks @zenofmud , there has been two or three other threads about door control and using time to open or close. I've thought all along that it's using a software solution when it should be hardware,. I put a rotary encoder on mine to measure door position and I think that is one of the better ways but there are numerous other solutions. It does take some time and a little mechanical savvy to mount switches or whatever but I think in the long run it will work better longer. You could use micro switches or hall effect switches or even make a rotary encoder out of two hall effect switches. But of paramount importance is the ability to some what self heal if power goes out. I reset everything when the door is full down so if power goes out put the door down and everything lines back up.
If you rely only on time to position door I see a number of scenarios where you could lose track of where the door is.

I thought of putting switch, but haven't landed solution yet.

Air temp does not change rapidly and yes, idea is to set recurring event to run every 10 min, that should limit of opening and closing. But you making a good point. Perhaps I should leave some "not covered" intervals. So let say action is taken from 20 to 21C and then only from 22-23C . So that when temp is between 21 and 22C nothing happens. I am not even sure if I need 4 steps, most important is to open when sun hits greenhouse in the morning, as then temp within 10-20 min jumps to 30C when door is closed (today I need to open manually).

I might think of switches at the later stage.

To open door when it is below 20 I have two options. Yes, I can manually switch it in Home Assistant, but I also plan to have mechanical option to disconnect device from door, so that I can open using door handle.

My door opening device seem to be rather strong, I hope it will deal with stronger winds. But no, I do not have any solution to deal with strong gusts.

I have never used rotary encoder, curious how it works. Do you have working example and what exact device that can be used for this purpose?

How do you put the door down when power goes down? Don't you need electricity to close the door? Anyway, there is no point automatically close door if power is off. You do not want to close door if it is hot inside...

Your last sentence is really what I am searching with this post. What possible scenario can you see that this will not work and where other solution like rotary encoder would work???

Make sure to graph the temperature data!

My son had a small 8' x 12' greenhouse on the property he bought - although he 'owned' it, I used it. I stuck a temperature monitor in it and was plotting the data. What saw was the temperature strt to rise about 6:30 every morning and about 11:30 it started foing down till 12:30 then went back up till 2 and then started down again.

I couldn't understand the dip from 11:30 to 12:30 till I was their one day facing the greenhouse and turned around. There was a big tree in the back of the house across the street that the sun went behind from 11:30 to 12:30. (sadly he moved away and I no longer have a greenhouse to play in)

My other example of discovery looking at a graph was I setup a monitor in our church and discovered that - in the winter - the heat went on at 10PM saturday night and the church was up to temperature at 2AM Sunday morning but no one was thier till 8AM. So we were heating it for 6 hours for no reason. Once I showed the people in charge, the automatic thermostats were changes to save us a bit of money.

It is amazing what looking at a graph can tell you.

yes, temp monitoring is already there. Monitoring air temp in 3 different heights and locations as ground temperature. No I just need to properly implement door opening and closing to avoid mainly overheating plants. Have you seen my function code? does it look ok, or do you see some possible improvements?

Yes I took a quick look but you haven't documented it and it has confused me. Take the function in the 'testing' block:

var output = msg.payload
if (output>0)

return msg;
  1. why not use msg.payload instead of sticking it into a variable
if (msg.payload>0)

return msg;
  1. why are you setting a flow variable Temp-Air1 but using position in the debug?

in the next function in the path (naming nodes is a good way to talk abut them) at the end you have

if (tm === 0) { return null; }
if (tm<0){
    msg.payload = "close_cover";
} else if (tm>0){
    msg.payload = "open_cover";

flow.set("tm", tm)

return msg;

if tm < 0, you set msg.payload to "close cover" but the you set msg.payload to tm - not suer why you are doing this.

1 Like

If power is off you obviously cannot electrically put door down, unless you have a battery backup which mine does. But that is only used to get door down once for security purposes.
I don't know your motor or control setup but there will be some variations in control of the door, there always is. Under different load conditions the door will travel faster or slower. The door probably goes up a little slower at first than it goes down. If you are only going full up or down it doesn't matter but over time these variances will add up. If the door hovers partially open for a week l suspect you would be several feet from where you think you are. Node red would be controlling some type of external devices and there will be small amounts of timing difference in when it turns on or off. Once or twice no big deal but over time there will be drift. There is going to be mechanical drag that varies up or down that will impact your times that you won't be able to account for using simply timing.
My door used a screw drive so I put a crown gear in that screw to drive a rotary encoder. The encoder fed a node mcu which mqtt to the pi. There are 395 steps in a 86inch travel. That's about .2 inch accuracy. And as stated there is a microswitch at full down to set everything to zero every time. I was worried about slippage of the gear or turning so fast there would be missed pulses but it appears to be stable. This was only a could I do it for me as I had all the parts laying around. I only monitor position for fun. At a minimum I think you need to put some type of switches where you want the door to stop.

Relevant questions/suggestions.

  1. I do not know why :slight_smile: , I just started using functions. I added that group of nodes (blue background) just for testing purposes, so that inject temp. But you are right, I can possibly use msg.payload. Importan here is


So that I do not read real temperature coming from sensor in a separate flow, but inject it manually for testing purpose.
2. setting a flow variable Temp-Air1 but using position in the debug is definitely wrong, did not notice that yesterday when I put that code. Most important was to assign certain temp to 'Temp-Air1' context, I didn't monitor debug, but that is certainly logical mistake.

  • (naming nodes is a good way to talk about them) - agree, will do
  • (if tm < 0, you set msg.payload to "close cover" but the you set msg.payload to tm - not sure why you are doing this. ) Very good comment, i am not sure myself why did I do that... I was trying different options to make it work and final version that did work was with simple msg.payload=tm. I just did not delete previous attempt... My next step is switch, which based on tm value instructs either open or close door.

Thank you for good comments!

I see what you mean. Have updated my function as follows:

if(current < 20 ){
tm=0-position -12
}else if(current > 26){
tm=48-position + 12

That means on every complete close I will use 12sec of 25% of full length extra and for every full open will use also 12 sec extra that should ensure full closure and opening. I can change that 12 value to 100 if needed, but that should do a trick I think

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.