Jump to content

Welcome to Smart Home Forum by FIBARO

Dear Guest,

 

as you can notice parts of Smart Home Forum by FIBARO is not available for you. You have to register in order to view all content and post in our community. Don't worry! Registration is a simple free process that requires minimal information for you to sign up. Become a part of of Smart Home Forum by FIBARO by creating an account.

 

As a member you can:

  •     Start new topics and reply to others
  •     Follow topics and users to get email updates
  •     Get your own profile page and make new friends
  •     Send personal messages
  •     ... and learn a lot about our system!

 

Regards,

Smart Home Forum by FIBARO Team


  • 0

Timer


Bert_V

Question

Guys and Girls,

 

I want something in my HC2. It's no rocket sience but I can seem to make it work. Any clous?

 

Case:

 I have a Fan for toilet and Bathroom (humidity and smell). One fan for both. 

There is a Humidity sensor in the Bathroom which I want to use to start the fan above value "X" and stop when the humidity is below value "X". BUT... When someone has used the downstairs toilet and the have pressed a button for the fan I want the fan to start for lets say 5 minuits. 

 

To prevent that the bathroom Humidity or the 5 minuts timer shut the fan down when one of the values isn't matched I want to make something like this:

 

Bathroom, above value X = on

Toilet when pressed button (in app) = on and off after 5 minuits

Bathroom below value X AND timer of toilet has not been completed = OFF

Toilet timer is completed AND bathroom value is below X = OFF

 

I have been playing with Variables which seems the most logical option but there is no possiblity for a 300sec timer....

 

Fibaro can't be this dump to have not such possiblity so can you help me where I'm going wrong.

 

Bert.

Link to comment
Share on other sites

1 answer to this question

Recommended Posts

  • 0
21 minutes ago, Bert_V said:

there is no possiblity for a 300sec timer....

did you read that?

 

Quote

HC2 version 4 has a new primitive for timing: setTimeout() with arguments "function" and "time in ms". 
I's available in scenes, but it's not in Virtual Devices. Here's an example, starting from a script based on sleep:
ID=207

fibaro:sleep(1000)

fibaro:call(ID, "turnOn")

fibaro:sleep(2000)

fibaro:call(ID, "turnOff")

The semantics of "setTimeout" are different. It means "Schedule this function to run after this time interval has elapsed".
So the first argument is a function. Defining a function is easy: wrap the actual command you want to run in "function() .. end".
In the next script I've put the on/off code in functions, and scheduled them... but there are two pitfalls. 
One: don't use () after the first argument (that would make it a "function call" instead of a function). 
Two: because the timers in this script start at the same time, the second timer has to be 1000 + 2000 = 3000 to be equivalent to the first example.
ID=207

function lightOn()
  fibaro:call(ID, "turnOn")
end

function lightOff()
  fibaro:call(ID, "turnOff")
end

setTimeout(lightOn, 1000)

setTimeout(lightOff, 3000)

There's something important here and it's easy to miss. You see in setTimeout we refer to "lightOn". You 
think that is a function call? Sure? It isn't: it's a variable bound to a function, not a function *call*. 
A function *call* would be like this: "lightOn()". If you put the "()" like you are used to do... the script won't work. 
When the script arrives at the setTimeout call, it immediately executes the function call. So add "()" to lightOn and the light will switch on immediately, instead of after a 1 second delay. 
And after one second, you get a mysterious "LUA error: attempt to call a nil value". That's setTimeout barking at you: "Hey, where's the function you want me to call after time out?". 
Please note that if you can't use "()" then you also can't have parameters. So "lightOn(ID)" isn't going to work either. I'll write about that later.
Maybe you don't like the idea of the two functions being scheduled at the same time, maybe you want to see the second timer at "2000" in your script. 
Then you can refactor it like this. The first timer is OK. But the second timer should start in the function of the first timer, so they are "chained". 
When I do this, I change the name of the first function from lightOn to lightOnOff because that is what it will do. 
Then at the end of it, I put the second setTimeout, that was at the bottom, and change the time to 2000. Like this:
ID=207

function lightOnOff()
  fibaro:call(ID, "turnOn")
  setTimeout(lightOff, 2000)
end

function lightOff()
  fibaro:call(ID, "turnOff")
end

setTimeout(lightOnOff, 1000)
Lua supports anonymous functions, and that can make a good solution for simple things... 
But I think this often gets hard to read. I put this example in anonymous form. And I payed attention to the indentation. 
But you still have to go through the text a few times to decode the pieces. On the other hand, it's valid and concise.

setTimeout(
  function() 
    fibaro:call(ID, "turnOn")
    setTimeout(
      function()
        fibaro:call(ID, "turnOff")
      end
      , 2000)
  end
  , 1000)

But don't give up on this anonymous-function-thing. You'll need it if you want to use parameters with setTimeout. 
Remember I said you cant put parenthesis after the function name in setTimeout? 
I'll offer two solutions to work around this. First, you can wrap the function in "function()" 
<CODE HERE> "end". That defines an anonymous function, which contains the statements and the parameters you want:
my_ID=207

function lightOnOff(id)
  fibaro:call(id, "turnOn")
  setTimeout(function() lightOff(my_ID) end, 2000)
end

function lightOff(id)
  fibaro:call(id, "turnOff")
end

setTimeout(function() lightOnOff(my_ID) end, 1000)
The second solution is the real "functional programming" stuff. 
Can you design a function, that *makes* a function for you that does the right thing and can be called from setTimeout? 
That's what's happening here. Functions are "first class citizens" and can be passed around and can be used as variables. 
This is the same example, but this time the argument for setTimeout is created inside a function, makeLightOnOff(id). 
This works because, well, if you're not used to functional programming, this might blow your head off, functions in lua are "closures". 
That means, they capture the local variables from the context *outside* the function (I'm talking about "id").
my_ID = 207


function makeLightOnOff(id)
  return function()
    fibaro:call(id, "turnOn")
    setTimeout(
      function()
        fibaro:call(id, "turnOff")
      end,
      2000)
  end
end

my_on_off=makeLightOnOff(my_ID)
setTimeout(my_on_off, 1000)

================================================


The "Lua idiom" to setup a non-drift setTimeout loop is

Ex. a 60s loop

local time = os.time()
local  function loop()
     doWork() --- call function or do whatever needs to be done each loop
     time = time + 60 -- calculate next time we should be called
     setTimeout(loop,1000*(time-os.time())) -- subtract current time from wanted time
end
loop()
In this case, if 'doWork()' takes a millisecond or 30s it doesn't matter. The loop will run every 60s.

However, when in the minute doWork is called depends on when the loop is started. If it is started 10:05:26 it will loop 

10:05:26, 10:06:26, 10:07:26, 10:08:26 ...

 

This is easy to extend to make it run starting on the minute (works for hour too)

E.g. looping 10:06:00, 10:07:00, 10:08:00, 10:09:00 ...

  local interval = 60
  local time = os.time()
  local function loop()
    doWork()
    time = time + interval
    setTimeout(loop,1000*(time-os.time()))
  end
  time = math.floor(time/interval)*interval+interval
  setTimeout(loop,1000*(time-os.time()))
and then the pattern can be captured in a function

function loop(fun,interval,startOnInterval)
  local time = os.time()
  local function doLoop()
    fun()
    time = time + interval
    setTimeout(doLoop,1000*(time-os.time()))
  end
  if startOnInterval then 
    time = math.floor(time/interval)*interval+interval 
    setTimeout(doLoop,1000*(time-os.time()))
  else
    doLoop()
  end
end

loop(doWork,60,true) -- run doWork() every 60s starting on the next minute.
 

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...