Fibaro Lua Scenes

Scenes allow automated actions to be conditionally processed when status change reports are received from sensors or on a pre-defined timer basis. Scenes can also be triggered manually to provide assistance in complex or multiple step processes e.g. turn on power to the TV, switch the TV on and dim the lights. Scene logic can be "Magic (simple)", "Graphic Block" or "Lua".

Lua as a full programming language enables complex conditional processing based on input from and interrogation of multiple sensors e.g. if first the garden sensor and then within 10 seconds the backdoor sensor are breached someone has just entered the house via the backdoor. This ability provides for better automation, in this example we have both direction (into the house) and we have a sensor double check (in case insects are triggering the garden sensor again!).

Scene Triggers

A Lua scene identifies to the Fibaro API exactly which triggers should cause it to be run using a %% trigger_name statement and if necessary on the following lines parameters (filters) for the trigger, all within Lua multi-line comments (--[[ and --]]), see the examples below.

--[[
%% autostart
%% globals
TimeOfDay
--]]
or
--[[
%% properties
39 power
--]]

Trigger names can be:

Devices
Fibaro Globals

Use the buttons to the right for expanded descriptions of these statements.

Note that a scene will be invoked on each occaision that the trigger criteria are met. A wall plug for example may trigger a power dependent scene multiple times as an attached device is switched on and reaches normal operating power usage. You define how many concurrent copies of the scene may run, when this number is exceeded you will receive the too many instances error message. Testing is normally required to ensure that the trigger condition desired is the one being processed e.g.

  if ( power < 100) then
    fibaro:abort();
  end;

Will exit a scene if it was entered at some intermediate power usage level during device power up. The scene drops through the condition if it has been triggered for a power above 100W when presumably the attached device has completed power up. The number of times the scene is triggered will be dependent on the number of z-wave reports from the wall-plug and this number can be affected through the advanced wall-plug parameters in the device configuration.

  if ( fibaro:countScenes() > 1) then
    fibaro:abort();
  end;
  fibaro:sleep(10000);

If the above scene were long running e.g. it sleeps for some seconds, then we can check for concurrent trigger processing due to triggers at say 120W and again at 200W as shown in the example to the left.

Scene States

A scene may be in one of three basic states as detailed by the "run scene" dropdown list on the scene detail panel, "General" tab, these are:

Run SceneDescription
AutomaticScene is enabled to run when either the scene's defined trigger conditions have been met or when manually started via the play button (start in scene's advanced panel).
ManualScene is enabled to run when manually started via the play button (start in scene's advanced panel).
DisabledScene is disabled, it will not run when its trigger conditions are met nor when the play button is used.

A scene's state can also be determined directly from the scenes panel. The examples below show both the "run scene" state, from the scene detail "General" tab and the equivalent SceneRunConfig value as used by setSceneRunConfig() and returned by getSceneRunConfig() functions.

Automatic
'TRIGGER_AND_MANUAL'
Manual
'MANUAL_ONLY'
Disabled
'DISABLED'

Timed Commands

Unlike with block scenes there is no option for a Lua scene to be scheduled at a particular time. To execute commands at a particular time a scene must be coded to start when the home center starts, using the %% autostart directive. The scene must then sleep until the desired time to execute the command is reached.

The example below illustrates how commands can be issued at a fixed time of day.

--[[
%% autostart 
--]]
   local runDailyAt = "18:00";

function waitUntilTime( runTime)
   local sTime, rDay;
   local rTime = tonumber(string.sub(runTime,1,2))*60*60
               + tonumber(string.sub(runTime,4)*60);
   local dtab = os.date("*t");
   local cTime = tonumber(dtab["hour"])*60*60
               + tonumber(dtab["min"]*60)
               + tonumber(dtab["sec"]);
   if ( cTime < rTime) then
       sTime = rTime-cTime;
       rDay = "Today";
   else
       sTime = 24*60*60 - (cTime-rTime);
       rDay = "Tomorrow";
   end;
   fibaro:debug("Sleeping until "..runTime.." "..rDay);
   fibaro:sleep (sTime * 1000);
   fibaro:debug( "Sleep finished");
end;

   -- Mainline
   while true do  -- loop forever
      waitUntilTime( runDailyAt);
      fibaro:debug("Command executed");   -- commands go here
   end;

Notes

  1. The Lua os.date("*t") function returns the current time as a table.
  2. The "if ( cTime < rTime) then" is intentionally "<" and not "<=" as this would allow recursive calls if the scheduled command completed in less than a second.
  3. It would be simply enough to introduce a second parameter on the waitUntilTime() function, say "plusmin" which allows for randomised scheduling within plus or minus the number of minutes passed in this second parameter.
  4. Supporting multiple timed commands and sleeping until the next command in sequence needs to be scheduled would bring greater efficiencies. This could be achieved by saving schedule times and commands/scenes to be processed within a Fibaro global variable.

Lua Functions for Scenes

FunctionDescription
fibaro:getSourceTrigger(); Returns a table identifying the resource associated with the scene trigger. Values returned are as follows:
Requested TriggerReturned Value(s)Reason scene was started
%% autostarttype:autostartHome Center has started or the scene has been modified and saved.
%% globals
variable_name(s)
type:global
name:
The named global variable has had its value changed.
%% properties
device id and property
type:property
deviceID:
propertyName:
The named property for a device has changed. Device's id and the name of the property that has changed are provided.
type:otherThe scene has been manually triggered.
fibaro:getSourceTriggerType()Returns only the type value (as listed above). May be used when trigger has no associated parameters or when only a single device or variable may trigger the scene, the device or variable name then being implicit to the trigger.
Example:
fibaro:abort()Terminates current scene processing.
fibaro:countScenes() Returns the number of copies of this scene that are active, including the current scene. Multiple simultaneous copies of a scene run when the scene takes longer to complete than the time between trigger events.
fibaro:sleep( milli-seconds ); Suspends the scene for the number of milli-seconds specified.

Example: getSourceTrigger() and getSourceTriggerType()

In the following code snippet we test for being triggered due to a power change on the TV wall socket with id 77. In this case we record the actual power consumption in variable tvPower.

--[[
%% properties
77 power
--]]
  local idTV = 77;
  local tvPower;
  local trigger = fibaro:getSourceTrigger();
  if (( trigger['type'] == "property")
     and ( trigger['deviceID'] == idTV )) then
     tvPower = tonumber(fibaro:getValue( idTV, "power"));
  end;

Note:

  1. This technique is most appropriate when we have multiple devices in the trigger list. As it stands the scene will only run when device 77 has reported a change in power usage, so some of our tests are redundant.
  2. Although we assign the device ID 77 to idTV, unfortunately we still have to hardcode 77 in the trigger list.

Now the more simple case when we only need to know why the scene was invoked. In this case, presumably, we have completed some initialization and will now exit if we were triggered by the home center booting.

if ( getSourceTriggerType() == "autostart") then
    fibaro:abort();
end;

Additional Scene Functions

FunctionDescription
fibaro:countScenes( sceneID ) Returns the number of running copies of the specified scene. Multiple simultaneous copies of a scene run when the scene takes longer to complete than the time between trigger events.
fibaro:startScene( sceneID < ,{ table } > ) Starts the specified scene optionally passing parameters in a table (see Calling a Scene below for details). It is often useful to record the required sceneID in a global variable say myTestSceneID and then use the function fibaro:startScene( fibaro:getGlobalValue( 'myTestSceneID'));
fibaro:killScenes( sceneID ) Kills, terminates all running instances of the specified scene.
fibaro:setSceneEnabled( sceneID, [ true | false ] ) This binary scene switch seems unneccessary, being replaced with the tri-state switch setSceneRunConfig() that carries out granular function in the enabled state. setSceneEnabled enables or disables the specified scene. See scene states above.
fibaro:isSceneEnabled( sceneID ) Returns true if the specified scene is enabled, otherwise false.
fibaro:setSceneRunConfig( sceneID, [ 'TRIGGER_AND_MANUAL''MANUAL_ONLY''DISABLED' ] ) Sets whether the scene can be triggered, run on request or cannot run at all. This is equivalent to the "Run Scene" parameter on the Scene's General tab with options Automatic, Manual or Disabled. See scene states above.
fibaro:getSceneRunConfig( sceneID ) Returns a string containing one of the setRunConfig() parameter options.

Calling a Scene

Using the fibaro:startScene() function it is possible to call another scene, this function will also work from a virtual device. The code snippet below illustrates how to use the startScene() function and pass data parameters across to the other scene.

  fibaro:startScene(71, {"Charlie","Chaplin"});

Above is the call to scene 71 with a table containing two parameters for use by the scene. Note: the scene id is available on the general tab of the target scene.

--[[
--]]
  if (fibaro:args()) then
    local nameFirst, nameLast = fibaro:args()[1], fibaro:args()[2];
    fibaro:debug("Welcome "..nameFirst.." "..nameLast);
    fibaro:debug( string.format("Welcome %s %s", nameFirst, nameLast));
  end;

The called scene first checks for a passed table and then uses the supplied parameters. Note the two fibaro:debug functions write the exact same message to the log. The string.format() function will be familiar to C programmers (think printf) and this option is less overhead compared to concatenating the variables one at a time.

Example: Conditional Scene Call

A list of scenes to be called is held in lua table subScenes. This example allows us to use the ON/OFF slidebar on the scenes panel to decide whether a particular scene should be run. This saves going to the scene detail and changing the "run scene" state to 'Disabled' i.e. we are treating 'TRIGGER_AND_MANUAL' state as enabled and 'MANUAL_ONLY' as a pseudo disabled.

--[[ 
--]]
local subScenes = { 38, 39, 41};
for i, sceneId in pairs (subScenes) do
   if ( fibaro:getSceneRunConfig(sceneId) == 'TRIGGER_AND_MANUAL') then
      fibaro:startScene(sceneID);
      fibaro:debug( 'Scene '..sceneId..' scheduled');
   end;
end;

 

Devices
Next
Fibaro
Lua API
Contents

Questions or comments, please e-mail us at info@snys.nl