clearTimeout not working?

868 views
Skip to first unread message

Julian Knight

unread,
Jun 20, 2015, 4:44:49 PM6/20/15
to node...@googlegroups.com
Hi, having had the freezer door left open - again! I decided to get a LightwaveRF magnetic switch, only £11 from Maplin, to connect up to my home hub.

Now I seem to have hit a slight issue. I want to trigger a warning a number of minutes after the door has opened (e.g. the switch has triggered).

I thought I could use a function node to detect when the switch turns on and set a timeout. When the timeout expires, do a node.send to trigger the warning.

Well the timer works just fine but of course I need to be able to cancel the timer if the door is closed. The normal way to do that is to capture the timeout object and pass it to the clearTimeout function.

Unfortunately, the clearTimeout function does not seem to be defined!

20 Jun 20:38:14 - [error] [function:context.state = "Closed"] ReferenceError: clearTimeout is not defined (line 27, col 9)
20 Jun 20:38:14 - [info] [debug:ERROR:Timers]
{ topic: 'SENSORS/S/MagSw_MS01/LWRF/RFX',
  payload
: 'Off',
  qos
: 0,
  retain
: false,
  _topic
: 'SENSORS/S/MagSw_MS01/LWRF/RFX',
  _msgid
: 'b6ca4dcb.4935b',
  error
:
   
{ message: 'ReferenceError: clearTimeout is not defined (line 27, col 9)',
     source
: { id: 'cbcc38af.fde778', type: 'function', count: 1 } } }

The function code is:
if ( msg.payload == "On" ) {
    node
.log( "open");
    context
.state = "Open";
    context
.timer = setTimeout(function(){
       
if ( context.state = "Open" ) {
            node
.log("State is OPEN - triggered from timeout");
            node
.send( msg );
       
} else {
            node
.log("State is CLOSED - triggered from timeout");
       
}
   
}, 5000);
} else {
    context
.state = "Closed";
    clearTimeout
(context.timer);
    node
.log("State is CLOSED - triggered direct");
}



Any ideas?

Mike Biddell

unread,
Jun 20, 2015, 5:08:00 PM6/20/15
to node...@googlegroups.com
Julian

cant you use a global to inhibit the warning flow (on receipt of CLOSED) after the OPEN timer to stop it and after a suitable delay, reset the global?

Mike

Nicholas O'Leary

unread,
Jun 20, 2015, 5:15:12 PM6/20/15
to Node-RED Mailing LIst
HI Julian,

good question - you can see here what we explicitly add to the sandbox the Function node runs in - https://github.com/node-red/node-red/blob/master/nodes/core/core/80-function.js#L65 - we add setTimeout but not clearTimeout etc. Something for us to fix there.

That said, you should checkout the trigger node that can be used for exactly this sort of watchdog timer without resorting to code.

Nick

--
http://nodered.org
---
You received this message because you are subscribed to the Google Groups "Node-RED" group.
To unsubscribe from this group and stop receiving emails from it, send an email to node-red+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Mark Setrem

unread,
Jun 20, 2015, 6:05:36 PM6/20/15
to node...@googlegroups.com
More importantly you need to add a light sensor in the freezer so you can answer the important question .

When the door is shut is the light on? :-)

Julian Knight

unread,
Jun 20, 2015, 6:28:21 PM6/20/15
to node...@googlegroups.com

Thanks Nick. Hopefully that will indeed get fixed in the future. I possibly could have also used the contrib-async node but I wanted to do thinks with native nodes if possible, if only to see how far I can take things :)

Unfortunately, I don't think the trigger node will do what I need it to. Whilst it can do the kind of timeout you'd get from, lets say, a PIR - e.g trigger on then trigger off n time later unless another on trigger is received. What I need is a little more complex. I won't receive another on unless the door is closed then reopened so I have to check after n time to see if the door is still open, if it is then I need to trigger the alarm. Ideally, the trigger will keep warning me if the door remains open. When the door is finally closed, I want a final message telling me how long the door was open. Here is the code I'm using as a work around:

// Calculate the elapsed time
function elapsed() {
   
var elapsed = {};
   
// Milliseconds
   
if (context.openTime) {
        elapsed
.ms = new Date() - context.openTime;
   
} else {
        elapsed
.ms = 0;
   
}
   
// seconds
    elapsed
.sec = Math.round( elapsed.ms / 1000 ); // or .toFixed(numdp);
   
// minutes
    elapsed
.min = Math.round( elapsed.ms / 60000 ); // or .toFixed(numdp);
   
// return object
   
return elapsed;
}

// What to do after the timout interval
function action() {
   
// When countdown expires, check if door is still open
   
if ( context.state == "Open" ) {
       
// Check whether door has really been open all the time or whether
       
// it has been closed then reopened. In the later case, the elapsed
       
// time will be less than the timeout. Wouldn't need this if we could use clearTimeout()!
       
if ( elapsed()['ms'] >= timeout ) {
           
//node.warn( "Freezer door is still open! - triggered from timeout - Elapsed time: " + elapsed()['min'] );
           
// Send the msg downstream
            node
.send( {'payload': 'Freezer door has been open for ' + elapsed()['min'] + ' minutes!'} );
           
// Set up another reminder in timeout's time (keep going till someone shuts the door!)
            setTimeout
(action, timeout);
       
} else {
           
//node.warn("Freezer door was closed then opened again - triggered from timeout - Elapsed time: " + elapsed()['min'] );
       
}
   
} else {
       
//node.warn("Freezer door already closed - triggered from timeout - Elapsed time: " + elapsed()['min'] );
   
}
}
// How many minutes do we want to wait until we trigger the alarm?
// 5 minute countdown = 5*60*1000= 300000
var timeout = 300000;


if ( msg.payload == "On" ) {

    node
.warn( "Freezer door opened");
   
   
// Keep track of how long the door is open
   
if (!context.openTime) context.openTime = new Date();
   
// Track state
    context
.state = "Open";
   
// Create a new timer
    context
.timeout1 = setTimeout(action, timeout);
} else {
    node
.warn( "Freezer door has been closed - Elapsed time: " + elapsed()['min'] );
   
// track state
    context
.state = "Closed";
   
// If the door was open a long time, send a msg that it is now closed
   
if ( elapsed()['ms'] >= timeout ) {
        node
.send( {'payload': 'Freezer door is now closed but was open for ' + elapsed()['min'] + ' minutes!'} );
   
}
   
// Reset the open time
   
if (context.openTime) context.openTime = false;
   
// Cancel the timeout
   
//clearTimeout(context.timeout1);
}

And here is the full flow - at the top of the email - wretched Google Groups web interface!

It works though maybe slightly clunky.

Julian Knight

unread,
Jun 20, 2015, 6:29:25 PM6/20/15
to node...@googlegroups.com
Ha ha! When my drone arrives, maybe I'll set it to flying patrol round the house!

Dave C-J

unread,
Jun 22, 2015, 4:09:03 AM6/22/15
to node...@googlegroups.com
pushed a small fix to git with clearTimeout added....

Julian Knight

unread,
Jun 22, 2015, 7:05:31 AM6/22/15
to node...@googlegroups.com
Thanks Dave.

The workaround is fine for now but that will be useful in the future. Did that fix include clearInterval() as well? ;)

I'll post a flow at some point when I've finished tweaking things. I've been playing with different alerting methods including SMS and PushBullet. I'm pushing out alerts every interval at the moment which could get expensive when using SMS alerting so I may split the output into three: start/finish alert, ongoing alerts and debug.

I'm also toying with the idea of turning this into a more generic node for reuse. Not sure I have the time though at the moment. Certainly a "timerAlert" node might be useful as it would enable everything to be done with a single node that current requires 3 one of which is some fairly hairy (certainly for beginners) JavaScript.

Dave C-J

unread,
Jun 22, 2015, 7:28:43 AM6/22/15
to node...@googlegroups.com
Hi Julian,

no I just added the clearTimeout to go along with setTimeout. Currently setInterval is not exposed so no need for clearInterval :-)

Julian Knight

unread,
Jun 22, 2015, 9:24:25 AM6/22/15
to node...@googlegroups.com
I'm not going to ask ... but ... will it be at some point? Or a different question, any reason it isn't? I think that async processing wasn't really supported until you exposed the node.send() function, is that the reason? Just interested.

Dave C-J

unread,
Jun 22, 2015, 9:40:28 AM6/22/15
to node...@googlegroups.com

​sort of a combination of both... it's a sandbox so we have to explicitly add things to it..​. and we don't really want to add the whole of node.js to it... and indeed while we didn't handle async that was "easy". Now that we do then adding setTimeout was sort of inevitable. The fact remains that the whole thing is supposed to be triggered by an event - and setInterval sort of goes against that...as the loop may never end... what happens when the node is re-triggered ? just needs thinking about some more.



Of course there is nothing to stop you adding it via the settings file etc...

Julian Knight

unread,
Jun 22, 2015, 12:58:59 PM6/22/15
to node...@googlegroups.com
Good points. I wonder if it might be a good idea to have a "power function" node at some point - that comes with health warnings but allows much richer access to Node.JS's async features? The idea of a "safe" and a "power" function node feels like a good balance. Safety for those people getting started and people playing around, power for when you've got a feel for things & are happy you might break something badly.

In my use case here, I'm more than happy to have something re-triggered as long as I can pick up the parent nodes context again. If I can't then it would be better to make sure I can't do any async stuff and force me to do it elsewhere.

By the way, wouldn't it be helpful to indicate to end users that the function node runs in a sandpit with limited functions available? That isn't mentioned certainly in the info box and the only reference in the documentation for writing functions is "code is wrapped into a full function declaration and then run within a secure sandbox" which may not be clear to non-programmers that it means you only have access to a subset of functions.

Julian Knight

unread,
Jun 22, 2015, 1:02:12 PM6/22/15
to node...@googlegroups.com
Oh, and "adding it via the settings file"? Not documented?

While I think of it, it would be terribly useful if we could search the documentation.


On Monday, 22 June 2015 14:40:28 UTC+1, Dave C-J wrote:

Dave C-J

unread,
Jun 22, 2015, 3:51:07 PM6/22/15
to node...@googlegroups.com
Yes, we have thought about an "unprotected" function node - which is basically what you are asking for - not considered making into an extension of the existing one... though that may be a bit too easy for people to do dangerous things.

re - retriggering - it means you would need to keep an array of running timers - in context - (so you have a handle to each one so you can still kill them), etc etc.
unless you stop the running one and restart on the retrigger.

adding via settings file... - you can add anything to the global context in there as you have been doing :-)  - that includes functions like setInterval, not just external libraries.  http://nodered.org/docs/configuration.html  - Doing it that way then the owner knows what has been added and what potential holes there are.

and yes - the docs can always be improved !

Nicholas O'Leary

unread,
Jun 22, 2015, 8:28:57 PM6/22/15
to node...@googlegroups.com

Adding via settings file very much documented on the page dedicated to writing functions....
http://nodered.org/docs/writing-functions.html

Nick


Julian Knight

unread,
Jun 23, 2015, 4:06:01 AM6/23/15
to node...@googlegroups.com
I'd actually thought Dave meant that we could add clearTimeout() to the sandbox via some hidden setting. I get that I can either add globals or use settings for my own nodes.

Dave C-J

unread,
Jun 23, 2015, 4:49:24 AM6/23/15
to node...@googlegroups.com
hidden settings ?

not intentionally ;-) 

If anyone fancies writing an FAQ or somesuch we can certainly add it to the docs.
Reply all
Reply to author
Forward
0 new messages