# Logic implementation

The edge-app environment will automatically inject the variables set by the user into our app together with all functions defined for this installation.

The first thing we want is for the app to find each topic we need to use. I created a function to get the FunctionX with id as specified.

function findFunction(id)
	for i, fun in ipairs(functions) do
		if fun.id == id then
			return functions[i]
		end
	end
end

I added a message handler that we will be bound to run every time an MQTT message is delivered on a specific topic, in this case the topic for the trigger function.

function handleTrigger(topic, payload, retained)
	local data = json:decode(payload)

	-- Verify that the door was opened
	if data.value == openState then
		-- Turn on
		local payload = json:encode({ value = onValue })
		mq:pub(controlTopic, payload, false, 0)

		-- Start timer for off..
		timer:after(cfg.timeout, function()
			local payload = json:encode({ value = offValue })
			mq:pub(controlTopic, payload, false, 0)
		end)
	end
end

This function takes action if the state corresponds to openState, it also starts a timer to turn off again.

Then all of that needs to be initialized and bound to the MQTT events. This is done using the onStart callback function. The function will be called automatically when the app starts. Here we also set the values for the different states and variables.

function onStart()
-- Get all values for the trigger
	local triggerFunction = findFunction(cfg.trigger_function)
	openState = triggerFunction.meta.state_open
	closedState = triggerFunction.meta.state_closed

	local controlFunction = findFunction(cfg.control)
	controlTopic = controlFunction.meta.topic_write
	onValue = controlFunction.meta.state_on
	offValue = controlFunction.meta.state_off

	mq:sub(triggerTopic, 0)
	mq:bind(triggerTopic, handleTrigger)
end

Note: _the local keyword limits the variable to the current scope (function/if/loop) and without it the variable is global.

I had a slight problem with the value comparison in the handler function at first. This was due to all meta-data being strings, and I tried to compare it with the value from MQTT which is a number. To Solve this I added the build in tonumber function when accessing the meta values. This was added for all numbers from meta-data like this.

openState = tonumber(triggerFunction.meta.state_open)

I then realized that this app would not work correctly if the states where changed for the FunctionX object. I will need to update the values if the functions had been updated. Fortunately this is possible using the onFunctionsUpdated callback. This will be triggered whenever updates to the functions is done on the API:s.

function onFunctionsUpdated()
    -- Set new states so that they are in sync
	local triggerFunction = findFunction(cfg.trigger_function)
	openState = tonumber(triggerFunction.meta.state_open)
	closedState = tonumber(triggerFunction.meta.state_closed)

	local controlFunction = findFunction(cfg.control)
	controlTopic = controlFunction.meta.topic_write
	onValue = tonumber(controlFunction.meta.state_on)
	offValue = tonumber(controlFunction.meta.state_off)
end

Since this added a lot of duplicated code, getting functions and setting the global values I removed that from the onStart function and added a manual call to this function like so.

function onStart()
	-- Get all values for the trigger
	local triggerFunction = findFunction(cfg.triggerFunction)
	triggerTopic = triggerFunction.meta.topic_read

	-- Manually call the updated function to set the initial state of the variables
	onFunctionsUpdated()

	mq:sub(triggerTopic, 0)
	mq:bind(triggerTopic, handleTrigger)
end