AN07 Lua script based scheduler | NETIO products: Smart power sockets controlled over LAN and WiFi
Tags: 
User library

The AN07 Application Note shows a Lua script that turns individual 110/230VAC sockets on and off at specific times according to a predefined schedule. The state table can contain dozens of rows with actions to be performed at specified hours or days of week.

 

Do you have any questions?

NETIO 4x includes a native graphical calendar/timer (Scheduler function). However, the Scheduler is configured for each output separately and may be impractical for short sequences.
 

The AN07 Lua script allows to specify in a text form when and how to perform actions with the outputs. The action can also be a “no action” (value 5, output state unchanged). The value of 5 may be useful when the output is controlled via a M2M protocol or with its button.

 

The script controls the outputs at specified times

"1111,22:30:00,0001000"

Outputs,time,week-days

 

  • Outputs = Action for each of the outputs – O1 O2 O3 O4
  • Time = Time when to perform the action
  • WeekDays = Days of the week when the action shall be performed 

 

 

Supported devices: NETIO 4All, NETIO 4C, NETIO 4

 

Creating the rule

To create and run the Lua script, do the following:

1) In the Actions section of NETIO 4 web administration, click Create Rule to add a rule.


 

2) Fill in the following parameters:

  • Enable rule: checked
  • Name: Lua scheduler (user-defined)
  • Description: Lua script for switching outputs based on time (user-defined)
  • Trigger: System started up
  • Schedule: Always

 

3) Copy the following code and paste it into the field for the Lua script:

 

 ------------NETIO AN07------------

------------Section 1------------
-- Actions for all outputs (1-4), time and days
-- Format:
-- 4 x number with action (0 - turn off, 1 - turn on, 2 - short off, 3 short on, 4 - toggle, 5 - keep previous state), time (hours:minutes:seconds), day (Mo-Sun, 0 - not active in this day, 1 - active in this day)

-- Example – Toggle O1 and turn off other Outputs at 18:30 every day except Wednesday: "4000,18:30:00,1101111"

local states = {
"4444,16:14:00,1111111",
"4444,16:14:10,1111111",
"4444,16:14:10,1111111",
"4444,00:00:00,1111111"
}

local initialState = "0055" -- state to which outputs will be set after restart (0-5)

local shortTimeMs = 1000 -- time used for action 2 and 3 [milliseconds]
---------End of Section 1---------

local sortedStates = {}
local swapped = false


function checkFormat()
  for i=1,4 do
    local test = tonumber(initialState:sub(i,i))
    if test == nil or test>5 or test<0 then
      logf("Action in initialState for outlet %d is invalid!", i)        
      return false
    end
  end
  for i=1,#states do
    for j=1,4 do
      local test = tonumber(states[i]:sub(j,j))
      if test == nil or test>5 or test<0 then
        logf("Action in state %d for outlet %d is invalid!", i,j)        
        return false
      end
    end 
    if states[i]:sub(5,5) ~= "," or states[i]:sub(14,14) ~= "," then
      logf("Time or days in state %d are not separated by comma!", i)
      return false
    end
    local hours = tonumber(states[i]:sub(6,6))
    local minutes = tonumber(states[i]:sub(9,9))
    local seconds = tonumber(states[i]:sub(12,12))
    if hours == nil or hours > 2 or hours < 0 or 
      tonumber(states[i]:sub(7,7)) == nil or
      states[i]:sub(8,8) ~= ":" or 
      minutes == nil or minutes > 5 or minutes < 0 == nil or 
      tonumber(states[i]:sub(10,10)) == nil or 
      states[i]:sub(11,11) ~= ":" or 
      seconds == nil or seconds > 5 or seconds < 0 == nil or 
      tonumber(states[i]:sub(13,13)) == nil then
      logf("Time in state %d is invalid!",i)
      return false
    end
    for j=15,21 do
      local day = tonumber(states[i]:sub(j,j))
      if day == nil or (day ~= 0 and day ~= 1) then
        logf("Value for day %d in state %d is invalid", (j-14), i)        
        return false
      end
    end 
  end 
  log("FORMAT OK")
  return true
end

function loadStates()
  for i=1,#states do
    sortedStates[i] = {state,time}
    sortedStates[i].state = states[i]
    sortedStates[i].time = (3600*tonumber(states[i]:sub(6,7)) + 60*tonumber(states[i]:sub(9,10)) + tonumber(states[i]:sub(12,13)))
  end
end

function sortStates()
  for i=1,#states-1 do
    swapped = false
    for j=1, #states-1 do
      if sortedStates[j].time > sortedStates[j+1].time then
        local temp = sortedStates[j]
        sortedStates[j] = sortedStates[j+1]
        sortedStates[j+1] = temp
        swapped = true;
      end  
    end
    if not swapped then
      break
    end
  end
end

function startScheduler()
  -- Current time
  local stringTime = os.date("%X")
  local time = (3600*tonumber(stringTime:sub(1,2)) + 60*tonumber(stringTime:sub(4,5)) + tonumber(stringTime:sub(7,8)))
  local nextState = sortedStates[1].state
  local timeLeft = (86400-time+sortedStates[1].time)
  local stateIndex = 1
  for i=1,#sortedStates do
    if time < sortedStates[i].time then
      nextState = sortedStates[i].state
      timeLeft = (sortedStates[i].time - time)
      stateIndex = i
      break
    end
  end
  
  -- Delay between states must be at least 1s
  if timeLeft <= 0 then timeLeft = 1 end
  delay(timeLeft,function() scheduler(nextState,stateIndex) end)
end

function scheduler(currentState, stateIndex)
  if checkDay(currentState) then
    setOutputs(currentState:sub(1,4))
  end
  local nextIndex = stateIndex%#sortedStates + 1
  
  local currentTime = sortedStates[stateIndex].time
  local stringTime = os.date("%X")
  local realTime = (3600*tonumber(stringTime:sub(1,2)) + 60*tonumber(stringTime:sub(4,5)) + tonumber(stringTime:sub(7,8))) 
  if currentTime ~= realTime then
    currentTime = realTime 
  end

  local timeLeft = 0
  if nextIndex == 1 then
    timeLeft = (86400-currentTime+sortedStates[nextIndex].time)
  else
    timeLeft = sortedStates[nextIndex].time - currentTime
  end
    
  -- Delay between states must be at least 1s
  if timeLeft <= 0 then timeLeft = 1 end
  delay(timeLeft,function() scheduler(sortedStates[nextIndex].state,nextIndex) end)
end

function checkDay(state)
  local day = tonumber(os.date("%w"))
  -- os.date("%w") returns 0 for Sunday
  if day == 0 then day = 7 end
    if tonumber(state:sub(14+day,14+day)) == 1 then
      return true
    end
  return false
end

function setOutputs(state)
  for i=1,4 do
    value = tonumber(state:sub(i,i))
    if value == 0 then -- turn off
      devices.system.SetOut{output = i, value = false}
    elseif value == 1 then -- turn on
      devices.system.SetOut{output = i, value = true}
    elseif value == 2 then -- short off
      devices.system.SetOut{output = i, value = false}
      milliDelay(shortTimeMs,function() devices.system.SetOut{output=i,value=true} end)
    elseif value == 3 then -- short on
      devices.system.SetOut{output = i, value = true}
      milliDelay(shortTimeMs,function() devices.system.SetOut{output=i,value=false} end)   
    elseif value == 4 then -- toggle
      if devices.system["output" ..i.. "_state"] == 'on' then 
        devices.system.SetOut{output=i,value=false}
      else
        devices.system.SetOut{output=i, value=true}
      end
    elseif value == 5 then
      -- do nothing
    end
  end
end

function initiate()
  setOutputs(initialState)
end

log("Lua scheduler started")
checkFormat()
initiate()
loadStates()
sortStates()
startScheduler()

 

4) To finish creating the rule, click Create Rule at the bottom of the screen.

 

 

Setting the variables

  • states
    • Variable that specifies the actions, times and days of week. It is a table of strings. Each string defines the actions at one particular time. Individual strings are enclosed in double quotes and separated with commas
    • Each string has the form of "aaaa,hh:mm:ss,mtwtfss".
    • action (aaaa)
      • The first 4 symbols specify the actions to be performed with the outputs at the given time.
      • The first number corresponds to the first output, the second number to the second output, and so on.
      • Action numbers follow the standard numbering in NETIO devices:
      • 0 – output switched off
      • 1 – output switched on
      • 2 – “short off” (output is set to 0, and after a delay specified in the shortTimeMs variable, the output is set to 1)
      • 3 – “short on” (output is set to 1, and after a delay specified in the shortTimeMs variable, the output is set to 0)
      • 4 – “toggle”, if the output was on, it is turned off, and vice versa
      • 5 – the output is unchanged
      • Example – to switch on output 3, switch off outputs 1 and 2, and leave output 4 unchanged: 0015
    • time  (hh:mm:ss)
      • The second part of the string specifies the time when the action shall take place.
      • The script takes the time from the NETIO device (can be set in the web administration in Settings - Date/Time).
      • The time format is: hours:minutes:seconds
      • The hour must be specified in the two-digit 24-hour format (9:00am is written as 09:00:00, 5pm is written as 17:00:00).
      • Example – to set 5:30am: 05:30:00 
    • day (mtwtfss)
      • The action can be restricted to certain days of the week.
      • This part of the string specifies on which days of the week the action shall take place.
      • Individual digits correspond to individual days of the week (from Monday to Sunday).
      • Possible values are 0 and 1.
      • 0 – the action is not performed on the given day.
      • 1 – the action is performed on the given day.
      • Example – to perform an action on weekends only: 0000011 
    • Individual parts of the string are separated with commas and the whole string is enclosed in double quotes.
    • Example – to toggle all outputs at 9:30am every workday (Monday to Friday): "4444,09:30:00,1111100".
    • Example – to switch off all outputs every Friday at 6pm: "0000,18:00:00,0000100" 

 

  • initialState
    • This variable contains the actions to perform with individual outputs whenever the device is restarted.
    • It is a string enclosed in double quotes.
    • The string has the form of "aaaa". Replace each "a" symbol with the number of the action to perform with the given output.
    • The action values are the same as in the states variable.
    • Example – to switch off outputs 1 and 2 and leave outputs 3 and 4 unchanged: local initialState = "0055 

 

  • shortTimeMs
    • Specifies (in milliseconds) for how long is the output on and off for actions 2 and 3 respectively
    • The minimum value is 100 ms
    • Example for 2-second pulses: shortTimeMs = 2000 

 

Starting the script

After configuring all the parameters and saving the script, the NETIO smart sockets device needs to be restarted. After the device boots, the script starts and the outputs are set to the states determined by the initialState variable.

 

FAQ:

 

1) How many individual actions can this script contain?

The number of actions is unlimited. The states variable can contain as many strings as needed.
 

2) What happens with the output if its state is changed through a different channel (e.g. using the button at the device, over the Web, using another M2M protocol, or from the mobile app)?

The script starts at the specified time and sets the output according to the table. If the script is supposed to switch on an output that is already switched on, nothing happens and the output remains switched on.

To keep an output unchanged (regardless of whether its state is 0 or 1), use action 5 (5 = leave the output state unchanged).

 

3) What happens when the time changes to Daylight Savings Time or back?

When the time changes, it is possible that an action scheduled during the transition will not take place. Subsequent actions will be performed normally.

 

4) What if the device is powered off at the time specified in the table?

In this case, the action is not performed. After the NETIO sockets device is powered on, the outlets are set according to the initialState variable at the beginning of the Lua script, and the sequence continues according to the current time.

 

5) Do the actions in the table have to be in chronological order?

It is not necessary but we recommend doing it nevertheless for clarity.

 

6) What if I encode two actions at the same time?

The first one is performed, and the second one is performed one second later.

 


 

Supported FW versions:

3.0.0 and higher (Archiv firmware)

 


 

 

This Application Note is compatible with:

Smart power socket NETIO

 

NETIO 4

NETIO 4 is smart power socket (smart power strip) with four 230V/8A sockets, connected to LAN and WiFi. Each of the four power sockets can be individually switched on/off using various M2M API protocols. NETIO 4 is a unique product designed for IT, industry, smart homes, multimedia installations and other applications. Use the product whenever you need 230V sockets controlled by a mobile app, by a computer program (via M2M API) or by a custom script (Lua), and featuring a timer (Scheduler) or auto reboot functionality (IP WatchDog). 

More about NETIO 4

 

Smart power socket NETIO 4All

 

NETIO 4All

NETIO 4All is a PDU module featuring four 230V/8A power sockets with consumption metering for each socket as well as LAN and WiFi connectivity. Each of the four sockets can be individually switched on/off over the Web or using various M2M API protocols. Electricity consumption (A, W, kWh) can be measured at each power socket. NETIO 4All smart sockets are designed for remote measurement and control of electrical sockets. Use the product whenever you need 230V sockets controlled by a mobile app, by a computer program (via M2M API) or by a custom script (Lua) that runs directly in the NETIO 4All smart socket device.

More about NETIO 4All

Smart power socket NETIO

 

NETIO 4C

NETIO 4C is a small 110/230V PDU (Power Distribution Unit) with four controlled IEC-320 C13 (max 8A) power outlets. It includes an Ethernet switch and two LAN ports for network connections. Each of the four power outputs can be individually switched on/off over the web, from a mobile app, or using various M2M APIs (SNMP, MQTT, XML, Modbus/TCP, ..). Custom Lua scripts can run directly in the NETIO 4C device. As a distinctive feature, NETIO 4C provides a RS-232 serial port (3-pin) that can be controlled with a Lua script or used as a remote serial port.

More about NETIO 4C

 

Ask for a price or technical parameters

For device testing use name/password demo/demo