Small Lua coding examples required

Discussion in 'C-Bus Automation Controllers' started by N.W.B, Apr 9, 2020.

  1. N.W.B

    N.W.B

    Joined:
    Jul 18, 2019
    Messages:
    32
    Likes Received:
    2
    Hi all, I have a test bench rigged up with a Shac and some dimmers and relays. I was wondering if anyone had any small Lua code examples to get me familiar with using the coding side of the shac. Any help would be much appreciated.
     
    N.W.B, Apr 9, 2020
    #1
  2. N.W.B

    Pie Boy

    Joined:
    Nov 21, 2012
    Messages:
    248
    Likes Received:
    31
    Location:
    New Zealand
    what sort of function do you want to achieve?
     
    Pie Boy, Apr 9, 2020
    #2
  3. N.W.B

    N.W.B

    Joined:
    Jul 18, 2019
    Messages:
    32
    Likes Received:
    2
    Hi Pie Boy,

    Ultimately I would like to create a function for a toilet extraction fan and light on the same input. For example if light is on longer then 1minute turn fan on. When light is off run fan for a further minute.

    There is an example under a Thread called “Fan Timer” that seems to fit my situation but could not get that to work.
     
    N.W.B, Apr 9, 2020
    #3
  4. N.W.B

    Pie Boy

    Joined:
    Nov 21, 2012
    Messages:
    248
    Likes Received:
    31
    Location:
    New Zealand
    Timers and delays are a complex thing in the shac unfortunately, there may be a better way than this but here goes, the other thing you could try is the PulseCBusLevel() but i couldn't get it to work like a delay timer as below...

    set a resident script to run with 1 second delay with this code
    Code:
    local timer_t  = storage.get('obj_timers')
    if timer_t  then
      
      for index, val in pairs(timer_t) do
        s = string.split(index, '/')
        nettag, apptag, grouptag = CBusLookupAddresses(tonumber(s[1]), tonumber(s[2]), tonumber(s[3]))
        if index then
             if (os.time() - val[1]) > val[2] then
              SetCBusLevel(s[1], s[2], s[3], val[3], val[4])
              log('Timer Switched object '..s[1]..'/'..s[2]..'/'..s[3].. ', '..grouptag..' to level ' ..val[3] .. ' at '.. os.date("%H:%M:%S"))
            timer_t[index] = nil
             end
            else
             index = nil
            end
        end
    end
     
    storage.set('obj_timers', timer_t)
    
    then in common functions put this code
    Code:
    function timer_Start(obj, delay, level_after_delay, rate_after_delay)
      local timer_t = storage.get('obj_timers')
      local s = string.split(obj, '/')
      local index = s[1]..'/'..s[2]..'/'..s[3]
     
        if s[4] ~= '-1' then
      SetCBusLevel(s[1], s[2], s[3], s[4], s[5])
      nettag, apptag, grouptag = CBusLookupAddresses(tonumber(s[1]), tonumber(s[2]), tonumber(s[3]))
      log('Timer Switched object '..index.. ', '..grouptag..' to level ' .. s[4] .. ' at '.. os.date("%H:%M:%S"))
        end
     
      if timer_t then
       else
          timer_t = {}
       end
         timer_t[index] = {os.time(),delay,level_after_delay,rate_after_delay} 
      storage.set('obj_timers',timer_t)
    end
    
    
    function Timer_Stop(obj)
     local timer_t = storage.get('obj_timers')
     
       for obj, val in pairs(timer_t) do
           s = string.split(obj, '/')
           if obj then
          log('Timer stopped for object '.. s[1]..'/'..s[2]..'/'..s[3]..' at '.. os.date("%H:%M:%S"))
                timer_t[obj] = nil
            end
        end
      storage.set('obj_timers',timer_t)
     
    end
    
    To use the timer functions use event script using the ga for the light as the trigger to start the event script
    parameters for timer is
    timer_start(network/app/group/level/ramp rate/delay time in sec/level after delay/ramp rate after delay)

    Code:
    --Guest toilet
    local level = event.getvalue()
    fan_ output_ga = 76
    
    if level == 255 then
      -- delay 60 sec then switch Fan on 
       timer_Start('0/56/fan_ output_ga/0/0' , 60, 255, 0)
    end
    
    if level == 0 then
      -- if off button is pressed straight away after on
      Timer_Stop('0/56/fan_ output_ga')
     
     -- switch fan on if off then set timeoutafter 2 min
      if GetLightingLevel(fan_ output_ga)  ==  255 then
        timer_Start('0/56/fan_ output_ga/255/0' , 120, 0, 0)
      end
     
    end
    
     
    Last edited: Apr 10, 2020
    Pie Boy, Apr 9, 2020
    #4
  5. N.W.B

    N.W.B

    Joined:
    Jul 18, 2019
    Messages:
    32
    Likes Received:
    2
    Thanks a lot for helping out, i just tried inserting the resident script and keep getting the error message "Lua syntax error at line 1: unfinished string near ''obj_timersf timer_t then" is there something i am missing?
     
    N.W.B, Apr 10, 2020
    #5
  6. N.W.B

    Pie Boy

    Joined:
    Nov 21, 2012
    Messages:
    248
    Likes Received:
    31
    Location:
    New Zealand
    for some reason the forum is not formatting the code correctly... check your messages,
    in some instances it removes ') not sure why as when i go to edit it it comes up as correct, when i save it it removes some char
     
    Pie Boy, Apr 10, 2020
    #6
  7. N.W.B

    pspeirs

    Joined:
    Nov 23, 2013
    Messages:
    185
    Likes Received:
    10
    Location:
    Sydney
    Hi,

    You asked me about the code I'm using, referring to the earlier mentioned fan timer.

    Start with your two groups, lets call them "UPSTAIRS TOILET LIGHT" and "UPSTAIRS TOILET FAN".

    I've added some keywords to each of these called "upstairs_toilet_light" and "upstairs_toilet_fan" respectively.

    I'm doing somethine a little different which is calling the database directly for a couple of functions which weren't handled via inbuilt functions.

    Create a user library, I generally create and name them based on functionality. Call it userfunctions or something if you prefer.

    Add the following function to the new library.

    Code:
    function db_GetObjectById(id)
    
        -- Return a table of object parameters based on the id
        local query = string.format('SELECT * FROM objects WHERE id = "%u"', id)
        liste = db:getall(query)
    
        if (table.maxn(liste) ~= 0) then
            return liste
        else
            return false
        end
        
    end
    

    Attach an event script to the light group ad add the following code. The below script relies on the above mentioned tags being the first tags (multiple tage are allowed)

    Code:
    value = event.getvalue()
    
    -- DB Call to return object based on the raw id number
    local objLight = db_GetObjectById(event.dstraw)
    
    if (objLight == false) then
        log(string.format('FAN TIMER: Object \'%s\' was not found', event.dstraw))
    else
    
        -- Pull out tagcache table
        local tagcache = split(objLight[1].tagcache, ',')
        local light_keyword = tagcache[1]
    
        -- Substitute the tags to get the fan keyword
        fan_keyword = string.gsub(light_keyword, "_light", "_fan")
    
        -- Get CBUS Level
        objFan = GetCBusByKW(fan_keyword, 'or')
    
        key = light_keyword
    
        -- If the light is switched on AND the fan is currently off, add to the storage variable
        if (value == 255 and objFan[1].value == 0) then
            log(string.format('FAN CONTROL: Light \'%s\' ON', light_keyword))
            storage.set(key, os.time())
        
        elseif (value == 0) then
            log(string.format('FAN CONTROL: Light \'%s\' OFF', light_keyword))
    
        end
    
    end
    

    Create a 1 second resident script and call it "Fan Control Timings" or something. Now put the light and fan keywords into the timer_mappings object in the below code. You can repeat for as many fans as you want to control.

    So what happens is that once the light is turned on, it will run the below script once a second and check if on_delay has decremented to zero. If so and the light is still on then turn on the fan. Once the light has turned off the fan will continue to run for off_delay seconds.


    Code:
    -- Script runs each second (or whatever you want) and checks for stored values based on the timer_mappings table
    -- Add an entry for each light and fan combination needing to be controlled
    
    
    if not timer_mappings then                                                        -- set up light to fan mappings table
        timer_mappings = {
            {input = 'downstairs_bath_light', output = 'downstairs_bath_fan', status='idle', on_delay=120, off_delay=180},
            {input = 'upstairs_toilet_light', output = 'upstairs_toilet_fan', status='idle', on_delay=120, off_delay=180},
            {input = 'test_light', output = 'test_fan', status='idle', on_delay=5, off_delay=10}
        }
    end
    
    
    for _, timer in ipairs(timer_mappings) do                                        -- Loop through each line in the table
    
        
        keyval = storage.get(timer.input, 0)                                        -- Get the stored value matching the input if it exists
        now = os.time()
    
        if (keyval > 0) then                                                        -- If the storage key exists (ie, greater than a value of zero)
    
            delta = now - keyval                                                    -- Calculate the difference between now and the stored value
            
            objLight = GetCBusByKW(timer.input, 'or')                                    -- Get the lighting object and extract the current state into light_state
            light_state = objLight[1].value
    
            objFan = GetCBusByKW(timer.input, 'or')
            local send_to_log = objFan[1].log                                        -- Get the groups 'log' flag
    
            
            if (delta >= timer.on_delay and timer.status == 'idle') then                -- Turn on the fan if required
                if(light_state == 255) then                                            -- Check if the light is still on and if so, turn ON the fan
                    SetCBusByKW({timer.output}, 'or', {target = 255, ramprate = 0})    -- Set the fan ON
                    timer.status = 'fan_on'                                        -- Set OFF delay time
                    storage.set(timer.input, os.time())                                -- Save current time and light keyword to storage
                    
                    if(send_to_log) then
                        log(string.format('FAN CONTROL: Turning Fan \'%s\' ON', timer.output))
                    end
                    
                else
                    -- Set op back to idle if light if now OFF
                    timer.op = 'idle'
                    storage.delete(timer.input)
                    
                end
                
            elseif (timer.status == 'fan_on' and light_state == 255) then                -- If light and fan are both ON
                storage.set(timer.input, os.time())                                    -- Keep resetting the current time for when light goes OFF
                
            elseif (delta >= timer.off_delay and timer.status == 'fan_on' and light_state == 0) then
                SetCBusByKW({timer.output}, 'or', {target = 0, ramprate = 0})        -- Light off, timer expired, turn fan OFF
                storage.delete(timer.input)                                            -- Delete the storage variable
                timer.status = 'idle'                                                        -- Set timer op back to nothing
                
                if(send_to_log) then
                    log(string.format('FAN CONTROL: Turning Fan \'%s\' OFF', timer.output))
                end
            else
            
            end
        
        elseif (keyval == 0 and timer.status ~= 'idle') then                                -- In case something messes up and gets out of sync
            timer.op = 'idle'
        else
        end
    end
    
     
    pspeirs, Apr 10, 2020
    #7
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.