--==============================================================================
-- ILLUMRA by Ad Hoc Electronics
-- For support contact: support@ILLUMRA.com or 801-225-2226
-- Filename: AHD0345C
-- Multiple Occupancy Sensors with Single Daylighting Sensor using 4CH SLT
-- Controls Wireless Relays
-- Copyright 2010 Ad Hoc Electronics
--==============================================================================
if adhoc_defined == nil then -- *** Variables and Functions ***
    adhoc_defined = true
    learnable = 0 -- 1 makes gateway able to be taught occ sensors and SLT
    -- learnable = 0 -- 0 makes gateway unable to be taught occ sensors and SLT

    vm.eo_flush()  -- clear the RX queue

    -- Occ sensor variables and functions
    -- Number of occupancy sensors available for this script (maximum 39):
    sensor_loop = 39 -- Sensor 1 is AV1/BV1, sensor 2 is AV2/BV2, etc.
    any_occupied = 0 -- This variable keeps track of whether any learned occ sensor
                     -- is sensing motion. 0=no motion, 1=motion.
    occ_timeout = 120 -- occupancy sensor timeout in seconds (approximately)
    occ_timer = occ_timeout -- initialize

    --SLT variables
    count_down_timeout=60      -- light sensor time out in seconds (approx.)
    count_down_to_off=count_down_timeout -- initialize
    heard_from_slt = 0 -- initialize
    timer_expired = 0 -- initialize
    -- SLT ID is always stored in location 40
    slt_sensor_id = ME.AV40_Present_Value -- SLT Sensor ID (received by teach-in)
    slt_sensor_on = ME.BV40_Present_Value -- SLT Sensor Value (1 means slt is on)


    -- relay variables
    -- wireless relay is hardcoded to relay #21
    wireless_relay_state = ME.BO21_Present_Value
    wireless_relay_press = ME.BO41_Present_Value

    -- This function returns a matching occ sensor ID and its value.
    -- If no match is found, it returns an available zero ID,
    -- or if all available IDs are already taken, it returns nil.
    sensor_find = function(check_id)
        for index=1,39,1 do
            object = ME["AV" .. index .. "_Present_Value"]
            if object.value == check_id then
                -- Found a matching sensor ID, return both objects:
                return object, ME["BV" .. index .. "_Present_Value"]
            end
        end

        for index=1,39,1 do
            object = ME["AV" .. index .. "_Present_Value"]
            if object.value == 0 then
                -- Found a matching sensor ID, return both objects:
                return object, ME["BV" .. index .. "_Present_Value"]
            end
        end

        -- No matching ID and all available IDs are already taken:
        return nil, nil -- Return both the ID and value objects as nil
    end

else -- *** Program Execution ***

    -- decrement SLT counter when the most recent SLT packet was an ON packet (~1 per second)
    -- if this gateway misses an OFF packet from  the SLT, then this counter will eventaully
    -- cause this this counter to expire. SLT ON packets reset the counter.
    if last_rx_packet == 1 then
        count_down_to_off=count_down_to_off-1
    end

    -- print some status messages
    if last_rx_packet == 0 then
        print("SLT OFF", "OCC", occ_timer)
    elseif last_rx_packet == 1 then
        print("SLT ON", count_down_to_off, "OCC", occ_timer)
    elseif countdown_expired == 1 then
        print("SLT EXPIRED")
    end

    -- decrememnt occ sensor timer so long as it hasn't already expired.
    if occ_timer >= 0 then
        occ_timer = occ_timer-1
    end

    -- check to see of occ sensor timer already expired.
    if occ_timer < (0) then
         any_occupied = 0
         print("OCC TIMEOUT EXPIRED")
    end

    eo_packet = vm.eo_read(-1)  -- Retrieve oldest packet from RX queue
    if eo_packet ~= nil then    -- Check if a valid packet was received

        -- Check if the received packet is occ sensor teach-in telegram:
        if (learnable == 1)
            and (eo_packet.bytes[6] == 0x80)        -- DB0
            and (eo_packet.bytes[7] == 0xFF)        -- DB1
            and (eo_packet.bytes[8] == 0x0F)        -- DB2
            and (eo_packet.bytes[9] == 0x1C) then   -- DB3

            -- A teach-in was received, look for an empty sensor ID:
            sensor_id, sensor_on = sensor_find(eo_packet.enocean_id)
            if sensor_id ~= nil then
                if sensor_id.value == 0 then
                    sensor_id.value = eo_packet.enocean_id
                    print("Learned Occ: ", sensor_id.value)
                else
                    print("Occ Already Learned: ", sensor_id.value)
                end
             else
                print("No room for another Occ:", sensor_id.value)
            end
        else
            -- Otherwise, check if for a matching sensor ID:
            sensor_id, sensor_on = sensor_find(eo_packet.enocean_id)
            if sensor_id ~= nil then
                if sensor_id.value ~= 0 then
                    -- A sensor value was received, keep it:
                    if eo_packet.bytes[7] >= 127 then       -- DB1: PIR is ON
                        sensor_on.value = 1     -- Sensor is occupied
                        any_occupied = 1        -- sensor detected motion
                        occ_timer = occ_timeout -- just heard from an occ sensor, reset timer
                        print("Occupied", sensor_id.value)
                    else                                    -- DB1: PIR is OFF
                        sensor_on.value = 0                 -- Sensor is not occupied
                        any_occupied = 0
                        print("Unoccupied", sensor_id.value)
                    end
                end
            end
        end




        -- Check if the received packet is a SLT teach-in telegram:
        if (learnable == 1)
            and (eo_packet.bytes[6] == 0x80)        -- DB0
            and (eo_packet.bytes[7] == 0x08)        -- DB1
            and (eo_packet.bytes[8] == 0x08)        -- DB2
            and (eo_packet.bytes[9] == 0xFC) then   -- DB3

            -- A teach-in was received, check if link exists:
            if slt_sensor_id.value == 0 then
                -- No sensor linked yet, keep sensor ID:
                slt_sensor_id.value = eo_packet.enocean_id
                print("Learned SLT: ", slt_sensor_id.value)
            end

        -- Otherwise, check if packet ID matches kept ID:
        elseif eo_packet.enocean_id == slt_sensor_id.value then

            -- A sensor value was received, keep it:
            -- if eo_packet.bytes[9] == 0x74 then   -- DB3 is an ON packet for a single CH SLT
               if (eo_packet.bytes[9] == 0x87)
                  and (eo_packet.bytes[6] == 0xEB) then   -- an action ON packet for CH 1 of 4 CH SLT
                        slt_sensor_on.value = 1             -- SLT is on
                        heard_from_slt = 1                  -- we heard from the SLT
                        countdown_expired = 0               -- countdown didn't expire
                        last_rx_packet = 1                  -- last RX packet was an ON command
                        count_down_to_off=count_down_timeout  -- reset the counter
                        print("SLT COMMAND ON")
                        -- for debugging only
                        -- print("Time=", scl.clock())
               elseif (eo_packet.bytes[9] == 0x00)
                    and (eo_packet.bytes[6] == 0xEF) then  -- an action OFF packet for CH 1 of 4 CH SLT
                        slt_sensor_on.value = 0               -- SLT is off
                        heard_from_slt = 1                    -- we heard from the SLT
                        last_rx_packet = 0                    -- last RX packet was an OFF command
                        print("SLT COMMAND OFF")
                elseif (eo_packet.bytes[9] == 0x87)
                    and (eo_packet.bytes[6] == 0xFB) then  -- a status ON packet for CH 1 of 4 CH SLT
                        slt_sensor_on.value = 1             -- SLT is on
                        heard_from_slt = 1                  -- we heard from the SLT
                        countdown_expired = 0               -- countdown didn't expire
                        last_rx_packet = 1                  -- last RX packet was an ON packet
                        count_down_to_off=count_down_timeout  -- reset the counter
                        print("SLT STATUS ON")
                elseif (eo_packet.bytes[9] == 0x87)
                    and (eo_packet.bytes[6] == 0xFF) then  -- a status OFF packet for CH 1 of 4 CH SLT
                        slt_sensor_on.value = 0               -- SLT is off
                        heard_from_slt = 1                    -- we heard from the SLT
                        last_rx_packet = 0                    -- last RX packet was an OFF packet
                        print("SLT STATUS OFF")
               end
        end

        -- check to see if we've ever heard from the slt
        if heard_from_slt == 0 then
            print ("NO SLT YET")
            count_down_to_off = -1
        end

        if count_down_to_off <= 0 then  --  has the countdown timer expired?
           if countdown_expired == 0 then  -- was it already expired?
              countdown_expired = 1        -- no it was not already expired
              slt_sensor_on.value = 0      -- haven't heard from SLT so assume it off
              count_down_to_off = -1       -- coerce so counter doesn't overflow with unpredictable outcome
              last_rx_packet = -1          -- coerce
              print ("SLT COUNTDOWN EXPIRED")
           else
              count_down_to_off = -1    -- yes it was already expired
           end




            --[[ Print each packet ID and content (for debug purposes only):
            print("Debug:", eo_packet.enocean_id,
                eo_packet.bytes[9], eo_packet.bytes[8],
                eo_packet.bytes[7], eo_packet.bytes[6]) --]]
        end
    end -- packet received

    -- manage the wireless relay(s)
    -- only wireless relays which are taught to the gateway will respond.
    -- the relay(s) will only be modified when necessary,
    -- i.e. on change, and refreshed/forced every X seconds.
    if slt_sensor_on.value == 1 then
        new_state = 0
        debug_txt = "Sunlight present, so turn OFF lights"
    elseif any_occupied == 1 then
        new_state = 1
        debug_txt = "Sunlight NOT present, but occupied, so turn ON lights"
    else
        new_state = 0
        debug_txt = "Sunlight NOT present, NOT occupied, so turn OFF lights"
    end

    -- checking the conditions before sending a telegram to the relay(s)...
    ME.AV38_Present_Value.value = ME.AV38_Present_Value.value + 1
    if (new_state ~= old_state) -- either the state needs to be changed,
        or (ME.AV38_Present_Value.value >= 10) then -- Or X seconds elapsed:
	wireless_relay_press.value = 0  -- transmit the state with a press
        print("========")
        if (new_state ~= old_state) then
		print("The state changed")
	end
	if (scl.sysclock() - wireless_relay_state.time > 10) then
		print("The clock expired")
		ME.AV38_Present_Value.value = 0
	end
	print("Clock Time: ", scl.sysclock())
	print("Last Change Time: ", wireless_relay_state.time)
	print("========")

        old_state = new_state -- keeping the last sent state for comparison
        wireless_relay_state.value = new_state
        wireless_relay_press.value = 1  -- transmit the state with a press
        print(debug_txt)
    end

end -- *** End of Script ***