Door opener control

I have door opener that I will put on greenhouse door where I do measure temperature. Total time to fully open door is 48 sec. Thought of setting few steps so that it would open 1/4 way when temp reaches 20C, at 24C would open 1/2, at 26C 3/4 and at 28 would fully open. Thought to use function node with several ifs and storing door opening level as variable. Or is there smarter way to control it?

I would do somthing like this, it can take any input e.g. 20, 20.34, 21.1 etc

[{"id":"7cde4c08.59790c","type":"inject","z":"5a245aa1.510164","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"19.7","payloadType":"num","x":80,"y":1680,"wires":[["3d0febb5.d5c57c"]]},{"id":"3d0febb5.d5c57c","type":"function","z":"5a245aa1.510164","name":"","func":"const position = [28,26,24,20,19];\nlet current = flow.get(\"current.position\") || 19;\nfor(var val of position){\n    if(msg.payload>=val) break;\n}\nmsg.delay = Math.abs(position.indexOf(current) - position.indexOf(val)) * 12000\nlet timestamp = new Date().valueOf();\nif(timestamp - flow.get(\"current.time\") < msg.delay+250) return null;\nif(val < current){\n    msg.payload =\"down\";\n}else if(val > current){\n    msg.payload = \"up\";\n}else{\n    return null;\n}\nflow.set(\"current\", {position:val,time:timestamp})\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":320,"y":1760,"wires":[["2313a34d.98b944"]]},{"id":"2313a34d.98b944","type":"trigger","z":"5a245aa1.510164","name":"","op1":"","op2":"stop","op1type":"pay","op2type":"str","duration":"250","extend":false,"overrideDelay":true,"units":"ms","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":530,"y":1680,"wires":[["c3652dc3.7bff38"]]},{"id":"7e7e1422.7b001c","type":"inject","z":"5a245aa1.510164","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"20","payloadType":"num","x":86.33332824707031,"y":1723,"wires":[["3d0febb5.d5c57c"]]},{"id":"73276516.506054","type":"inject","z":"5a245aa1.510164","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"24.2","payloadType":"num","x":90,"y":1760,"wires":[["3d0febb5.d5c57c"]]},{"id":"16f90a5f.05d0fe","type":"inject","z":"5a245aa1.510164","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"27.5","payloadType":"num","x":90,"y":1800,"wires":[["3d0febb5.d5c57c"]]},{"id":"355bcc13.f250bc","type":"inject","z":"5a245aa1.510164","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"28","payloadType":"num","x":97.33332824707031,"y":1842,"wires":[["3d0febb5.d5c57c"]]},{"id":"c3652dc3.7bff38","type":"debug","z":"5a245aa1.510164","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"delay","statusType":"msg","x":580,"y":1760,"wires":[]}]
const position = [28,26,24,20,19];
let current = flow.get("current.position") || 19;
for(var val of position){
    if(msg.payload>=val) break;
msg.delay = Math.abs(position.indexOf(current) - position.indexOf(val)) * 12000
let timestamp = new Date().valueOf();
if(timestamp - flow.get("current.time") < msg.delay+250) return null;
if(val < current){
    msg.payload ="down";
}else if(val > current){
    msg.payload = "up";
    return null;
flow.set("current", {position:val,time:timestamp})
return msg;

This is rate limited to opening and closing delay.

1 Like

I stuck a rotary encoder on my door to measure position.

@ E1cid I copied your flow, but I am not sure I understand how this should work. Do I need to have those 5 inject nodes? Do I understand correct that I should make them recurring so that they are called every minute? I do have current temperature saved into "Temp-Air" variant. I have started flow and it seems like working, will monitor for some time. But would like to understand it :slight_smile:

Also, how do you post flow code in one line in this forum?

To post code in one line
export your flow in compact mode. Then paste as below
To use my example flow you do not need the injects, they are example inputs.
Input the temperature in the msg.payload. The out put can be up down or stop. You can configure any output you want by replacing the up down and stop strings in the js code and the trigger node.

The temp injection can be what ever rate you wish, the flow will rate limit the messages, when calling up or down the flow will not allow any other message to pass till the up down command has sent stop.

What does the line

if(val === current){ msg.reset = true;}

do? empties message payload?


That line of code doesn't touch payload.

In English it says "if the value of val is the same as the value of current, set the property named reset of the object msg to true"

but what value does it add to this function? What would happen if that line is omitted? Trying to understand what it means for the flow?

It resets the trigger node. Its a command that tells the trigger node to reset.

[edit] the code was updated in the my original post, best look at newer version as it no longer uses the msg.reset.

I still struggling to understand the concept of the function you provided and then I do not feel confident changing it. I have rewrote complete flow where I do following sterps:

  1. Just recurring Inject node so that code would run every 5 or 10 min to verify temp at a later step.
  2. Main function that checks air temp stored in context, as well as door position (values 0;12;24;26 or 48). Function calculates tm value which is "time to move" door opener value.
  3. Switch function checks if value is below zero therefore we need to call close door opener function, or above zero meaning that open door opener function is needed.
    4 Depending on tm value either close or open function is started
    5 Another function is reading tm value and calculated into seconds multiplying by 1000. msg.delay is called. Thought the code should stop here and wait for calculated number of seconds before executing next node . But it doesn't closing or opening is lasting split of the second.
    6.Sending stop command to door opener.

I would appreciate if you can comment my flow, highlight possible errors. This might possibly be not the best way to write this code, but at least I think I can follow logic of it.

[{"id":"88de674f.e0ed78","type":"function","z":"123bd5e5.5a9a9a","name":"","func":"var current = flow.get(\"Temp-Air\") || 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":260,"y":1220,"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":1220,"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":1220,"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":1280,"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":1180,"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":730,"y":1240,"wires":[["976f3ff8.bfd66"]]},{"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":1240,"wires":[[]]},{"id":"976f3ff8.bfd66","type":"trigger","z":"123bd5e5.5a9a9a","name":"","op1":"","op2":"stop","op1type":"pay","op2type":"str","duration":"5000","extend":false,"overrideDelay":true,"units":"ms","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":900,"y":1240,"wires":[["a396b36a.30e59"]]},{"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}]

Do not have your api node installed so can not say much without more info.

How does you api send "open" , "close" and "stop" ?

My function takes a temp reading value set in msg.payload. This value can be feed in every second or every 5mins, any time gap you wish.
It outputs"up", "down" or "stop" to msg.payload. This can be set to any values you wish.
This value would then feed into your api node.
So basically if you api node takes an up down or stop value my function should work as is.

const position = [28,26,24,20,0];
let current = flow.get("current.position") || 19;  //gets current position or sets it to closed
for(var val of position){
    if(msg.payload>=val) break; // finds current position of msg.payload
msg.delay = Math.abs(position.indexOf(current) - position.indexOf(val)) * 12000; //calcultes delay by finding index of last and new reading
let timestamp = new Date().valueOf(); //get current time stamp
if(timestamp - flow.get("current.time") < msg.delay+250) return null; // check if  time difference to see if last command is running if still running stop function
//are we going up or down
if(val < current){
    msg.payload ="down";
}else if(val > current){
    msg.payload = "up";
    return null; //if no change stop function
flow.set("current", {position:val,time:timestamp}) // store position and time stamp
return msg; //send command and delay for stop

Just a few thoughts about your door. I went through the exercise of the garage door several months ago so if I may post a few thoughts. You are going through a lot of coding but have you considered that your door position won't be where you think it is? You state it takes 48 seconds to open but is it the same to close? If you start at 1/4 open and want to go to 1/2 open is that a consistent time? Some doors and openers when the load changes slow down or speed up. If the power goes out how do you re-sync the door position to your code? In other words node red thinks the door is at half but the door is really full open. If someone has the ability to manually put the door up or down how do you account for that? What ever you use to open or close the door, a relay or SSR or whatever how consistent is that time? Not to be a negative nellie but I'm interested to see what you come up with as a final flow.

@E1cid My API is calling home assistant service. I am using same note to call three different HA services. So when I used your initial function, I got msg.payload either "open" or "close". Then I used switch, that based on msg.payload output would start either opening or closing of the door. Then it should follow with some node that would wait for defined number of seconds and call home assistant service "stop". I was not sure how to implement this with your function example. It was kind of workng, but opening and closing lasted for a split of a second, not for 12 sec as I expected and I was not sure why...

@gerry Thanks for commenting. I checked door opener in my in house conditions. You are right, possibly it will not open exactly 25% of the full move under different weather conditions (wind). I thought as follows. Temp in a greenhouse will be most likely below 20 during the night and will be above 26 degrees during days (most of the days). So I was thinking of calling close function for 48 sec when temp is below 20 and at the same time setting Position variant value to zero. When temp is > 26 then I would run Open door for 48 sec (or more) and set position variant value to 48. Setting open and close timer longer than there is "physical space" is fine and door opener just stops when reaching end of chain.

Can you show the settings of the HA api nodes and what payload they accept?

Here it is, the only diff in close node is Service name: close_cover

Can you answer these questions, with out info it's very difficult to help you.

I thought I did with above post, but perhaps I misunderstood question.

First function in below flow comes with msg.payload to either open or close depending on temp. Switch then uses msg.payload and splits the flow into two. I then use Home Assistant "call service" node, and what is inside that node is posted in my previous post. In short there is link established to HA server, then domain "cover" is used as that is domain my device is defined under in Home Assistant. Entity ID is ID of my device in HA. All above parameters are the same in all my "call service" nodes. Last parameter under Service is what differs and sends either open or close or stop command. So all three blue nodes are the same as posted in my previous node, just service text is different.

Question 1 - you never answered i still have no idea what the command for open is or stop.

question 2 - i asked to see the settings of the three nodes, as i used a plural, nodes. You supplied one. I asked this as you never answered the first question.

Here are all three nodes. As i mentioned they are identical with one difference, which is name of the service. Close_cover will close door, open_cover will open door and stop_cover will stop either of two previous actions.