<?xml version="1.0"?>
<implementation>
	<functions>
		local CAM_SID = "urn:micasaverde-com:serviceId:Camera1"
		local CMD_SID = "urn:micasaverde-com:serviceId:CameraMotionDetection1"
		local SES_SID = "urn:micasaverde-com:serviceId:SecuritySensor1"
		local HAD_SID = "urn:micasaverde-com:serviceId:HaDevice1"

		local STATE_MD_DISABLED   = "-1" -- Motion detection is disabled.
		local STATE_CREATE_MS     =  "0" -- Create the motion sensor.
		local STATE_CONFIGURE_CAM =  "1" -- Configure the camera.
		local STATE_CONFIGURED_OK =  "2" -- Everything is configured OK.

		local ON_TIME = 1200 -- 20 minutes

		local lug_device


		function untrip (sensorId)
			sensorId = tonumber(sensorId, 10)
			local lastTrip = luup.variable_get("urn:micasaverde-com:serviceId:SecuritySensor1", "LastTrip", sensorId) or 0
			local now = os.time()
			if os.difftime(now, lastTrip) >= ON_TIME then -- 20 minutes
				luup.variable_set("urn:micasaverde-com:serviceId:SecuritySensor1", "Tripped", "0", sensorId)
			end
		end


		function trip (request, parameters)

			local sensorId = tonumber(parameters.sensor_id, 10) or 0
			if sensorId == 0 then
				return "failed"
			end

			luup.variable_set("urn:micasaverde-com:serviceId:SecuritySensor1", "Tripped", "1", sensorId)
			luup.variable_set("urn:micasaverde-com:serviceId:SecuritySensor1", "LastTrip", tostring(os.time()), sensorId)

			luup.call_delay("untrip", ON_TIME, parameters.sensor_id)

			return "OK"
		end


		local function urlEncode (s)
			if s ~= nil then
				s = s:gsub("\n", "\r\n")
				s = s:gsub("([^%w ])", function (c)
				                           return string.format("%%%02X", string.byte(c))
				                       end)
				s = s:gsub(" ", "%%20")
			end

			return (s or "nil")
		end


		function showSysMessage (message)
			luup.task(message, 1, luup.devices[lug_device].description, -1)
		end


		local function getMotionSensorId (state)

			local sensorId = tonumber(luup.variable_get(CMD_SID, "MotionSensorID", lug_device) , 10) or 0
			if sensorId > 0 then
				return sensorId
			end

			-- Get the motion sensor's device ID.
			local altid = "foscam_".. lug_device .."_sensor"
			for dev, attr in pairs(luup.devices) do
				if attr.id == altid then
					sensorId = dev
					break
				end
			end

			if sensorId == 0 then
				-- We haven't created the motion sensor. Set State to STATE_CREATE_MS.
				luup.log("(".. luup.devices[lug_device].description .."::State=".. state ..") Motion sensor doesn't exist. Set State to STATE_CREATE_MS(".. STATE_CREATE_MS ..").")
				luup.variable_set(CMD_SID, "State", STATE_CREATE_MS, lug_device)
				return
			end

			-- Store the motion sensor ID in a variable.
			luup.log("(".. luup.devices[lug_device].description .."::State=".. state ..") Motion sensor is device #".. sensorId ..".")
			luup.variable_set(CMD_SID, "MotionSensorID", tostring(sensorId), lug_device)
			return sensorId
		end


		function lug_startup (lul_device)

			lug_device = lul_device

			-- Check if we have the correct category number.
			if luup.devices[lul_device].category_num ~= 6 then
				luup.attr_set("category_num", "6", lul_device)
			end

			lug_username = luup.devices[lul_device].user or luup.variable_get(CAM_SID, "Username", lul_device) or ""
			lug_password = luup.devices[lul_device].pass or luup.variable_get(CAM_SID, "Password", lul_device) or ""

			lug_ip = luup.devices[lul_device].ip or ""

			local snapshotUrl = luup.variable_get(CAM_SID, "URL", lul_device) or ""
			if snapshotUrl == "" then
				luup.variable_set(CAM_SID, "URL", "/snapshot.cgi", lul_device)
			end

			lug_stepSize = luup.variable_get(CAM_SID, "StepSize", lul_device) or ""
			if lug_stepSize == "" then
				lug_stepSize = "1"
				luup.variable_set(CAM_SID, "StepSize", lug_stepSize, lul_device)
			end

			lug_reversePan = luup.variable_get(CAM_SID, "ReversePan", lul_device) or ""
			if lug_reversePan == "" then
				lug_reversePan = "0"
				luup.variable_set(CAM_SID, "ReversePan", lug_reversePan, lul_device)
			end

			lug_reverseTilt = luup.variable_get(CAM_SID, "ReverseTilt", lul_device) or ""
			if lug_reverseTilt == "" then
				lug_reverseTilt = "0"
				luup.variable_set(CAM_SID, "ReverseTilt", lug_reverseTilt, lul_device)
			end

			-- Disable zoom buttons by default.
			local commands = luup.variable_get(HAD_SID, "Commands", lul_device)
			if not commands then
				commands = "camera_up,camera_down,camera_left,camera_right,camera_full_screen,camera_preset,camera_archive_snapshot"
				luup.variable_set(HAD_SID, "Commands", commands, lul_device)
			end

			------------------------------------------------------------------------------------------------------------
			-- Motion detection stuff
			------------------------------------------------------------------------------------------------------------

			-- Check if the camera was configured for motion detection.
			local state = luup.variable_get(CMD_SID, "State", lul_device) or ""
			luup.log("(".. luup.devices[lul_device].description ..") State=".. state)

			if state == "" then
				-- If it's an upgrade from an older plugin version, State is ConfiguredMD.
				state = luup.variable_get(CMD_SID, "ConfiguredMD", lul_device) or ""
				if state == "" then
					-- Not an upgrade, the plugin is freshly installed.
					state = STATE_MD_DISABLED
					luup.log("(".. luup.devices[lul_device].description ..") Initializing the State variable to STATE_MD_DISABLED(".. state ..").")
					luup.variable_set(CMD_SID, "State", state, lul_device)
				else
					-- Copy the value from ConfiguredMD to State and attempt to remove the ConfiguredMD variable.
					luup.log("(".. luup.devices[lul_device].description ..") Upgrade from an older plugin version. Set State to '".. state .."' and remove ConfiguredMD.")
					luup.variable_set(CMD_SID, "State", state, lul_device)
					luup.inet.wget("http://127.0.0.1:3480/data_request?id=variableset&amp;DeviceNum=".. lul_device .."&amp;serviceId=".. CMD_SID .."&amp;Variable=ConfiguredMD&amp;Value=")
				end
			end

			----------------------------------------------------------------------------------------
			-- Motion detection disabled
			----------------------------------------------------------------------------------------
			if state == STATE_MD_DISABLED then -- Disable the motion detection.
				return
			----------------------------------------------------------------------------------------
			-- Motion sensor not created. Create the motion sensor.
			----------------------------------------------------------------------------------------
			elseif state == STATE_CREATE_MS then

				-- We can't configure the camera for motion detection if we don't know its URL/IP address.
				if lug_ip == "" then
					luup.log("(".. luup.devices[lul_device].description .."::State=".. state ..") Missing camera IP.")
					return false, "Missing camera IP address", luup.devices[lul_device].description
				end

				local gatewayAddress = luup.variable_get(CMD_SID, "GatewayAddress", lul_device) or ""
				if gatewayAddress == "" then
					-- Vera's IP address hasn't been specified. Try to get it based on the camera's IP address.
					-- Get the camera subnet.
					local camSubnet = lug_ip:match("%d+%.%d+%.%d+")
					if camSubnet ~= nil then
						-- Try the WAN IP.
						local stdout = io.popen("GetNetworkState.sh ip_wan")
						gatewayAddress = stdout:read("*a")
						stdout:close()

						if not gatewayAddress:find(camSubnet, 1, true) then
							-- Try the LAN IP.
							stdout = io.popen("GetNetworkState.sh ip_lan")
							gatewayAddress = stdout:read("*a")
							stdout:close()

							if not gatewayAddress:find(camSubnet, 1, true) then
								-- The camera is in a different network than Vera.
								gatewayAddress = ""
							end
						end
					end
				end

				if gatewayAddress == "" then
					-- Vera's IP address isn't specified and we couldn't get it based on the camera's IP address.
					-- Create the variable so that it can be set up by the user.
					luup.variable_set(CMD_SID, "GatewayAddress", "", lul_device)
				else
					luup.variable_set(CMD_SID, "GatewayAddress", gatewayAddress, lul_device)
				end

				-- Set the State variable here, because no value can be saved in the user_data after luup.chdev.sync has run.
				luup.variable_set(CMD_SID, "State", STATE_CONFIGURE_CAM, lul_device)

				-- Create the motion sensor.
				luup.log("(".. luup.devices[lul_device].description .."::State=".. state ..") Create the motion sensor.")
				local ptr = luup.chdev.start(lul_device)
				luup.chdev.append(lul_device, ptr, "foscam_".. lul_device .."_sensor", luup.devices[lul_device].description .." Motion Sensor",
					"urn:schemas-micasaverde-com:device:MotionSensor:1", "D_MotionSensor1.xml", "", SES_SID ..",Armed=1\n".. SES_SID ..",Tripped=0", true, false)
				luup.chdev.sync(lul_device, ptr)

			----------------------------------------------------------------------------------------
			-- Motion sensor created. Configure the camera for motion detection reporting.
			----------------------------------------------------------------------------------------
			elseif state == STATE_CONFIGURE_CAM then

				-- We can't configure the camera if we don't know Vera's URL / IP address.
				local gatewayAddress = luup.variable_get(CMD_SID, "GatewayAddress", lul_device) or ""
				if gatewayAddress == "" then
					luup.log("(".. luup.devices[lul_device].description .."::State=".. state ..") GatewayAddress is empty.")
					return false, "Missing Gateway IP address", luup.devices[lul_device].description
				end

				-- Get the motion sensor's device ID.
				local sensorId = getMotionSensorId(state)
				if not sensorId then
					luup.call_delay("showSysMessage", 2, "Reload Luup to create motion sensor")
					return
				end

				-- Configure the camera:
				-- 1. Enable motion detection.
				-- 2. Enable HTTP notification on motion detection.
				-- 3. Set the URL to send the notification to.
				local url = "http://".. gatewayAddress ..":3480/data_request?id=lr_foscam_tripped&amp;sensor_id=".. sensorId
				local status = luup.inet.wget("http://".. lug_ip .."/set_alarm.cgi?motion_armed=1&amp;http=1&amp;http_url=".. urlEncode(url) .."&amp;user=".. lug_username .."&amp;pwd=".. urlEncode(lug_password), 5)
				if status ~= 0 then
					luup.log("(".. luup.devices[lul_device].description .."::State=".. state ..") Failed to configure camera, status = ".. status ..".")
					return false, "Failed to configure camera", luup.devices[lul_device].description
				end

				-- Initialize OnTime variable, which is the time until the sensor is considered untripped.
				luup.variable_set(SES_SID, "OnTime", tostring(ON_TIME), sensorId)

				-- Run the 'trip' function when notified.
				luup.log("(".. luup.devices[lul_device].description .."::State=".. state ..") Register handler.")
				luup.register_handler("trip", "foscam_tripped")

				-- The configuration was successful.
				luup.variable_set(CMD_SID, "State", STATE_CONFIGURED_OK, lul_device)

			----------------------------------------------------------------------------------------
			-- Camera configured
			----------------------------------------------------------------------------------------
			else

				-- Get the motion sensor's device ID.
				local sensorId = getMotionSensorId(state)
				if not sensorId then
					luup.call_delay("showSysMessage", 2, "Reload Luup to create motion sensor")
					return
				end

				-- Get and update the time until the sensor is considered untripped.
				local onTime = luup.variable_get(SES_SID, "OnTime", sensorId) or ""
				if onTime:find("^%d+$") ~= nil then
					ON_TIME = tonumber(onTime, 10) or ON_TIME
				else
					luup.variable_set(SES_SID, "OnTime", tostring(ON_TIME), sensorId)
				end

				-- Untrip the sensor if the OnTime has passed.
				local lastTrip = luup.variable_get(SES_SID, "LastTrip", sensorId) or 0
				if os.difftime(os.time(), lastTrip) >= ON_TIME then
					luup.variable_set(SES_SID, "Tripped", "0", sensorId)
				end

				-- Run the 'trip' function when notified.
				luup.log("(".. luup.devices[lul_device].description .."::State=".. state ..") Register handler.")
				luup.register_handler("trip", "foscam_tripped")
			end
			------------------------------------------------------------------------------------------------------------
		end
	</functions>
	<startup>lug_startup</startup>
	<actionList>
		<action>
			<serviceId>urn:micasaverde-com:serviceId:PanTiltZoom1</serviceId>
			<name>MoveLeft</name>
			<job>
				local command = (lug_reversePan == "0") and "4" or "6"
				luup.inet.wget("http://".. lug_ip .."/decoder_control.cgi?command=".. command .."&amp;onestep=".. lug_stepSize .."&amp;user=".. lug_username .."&amp;pwd=".. urlEncode(lug_password), 5)
			</job>
		</action>
		<action>
			<serviceId>urn:micasaverde-com:serviceId:PanTiltZoom1</serviceId>
			<name>MoveRight</name>
			<job>
				local command = (lug_reversePan == "0") and "6" or "4"
				luup.inet.wget("http://".. lug_ip .."/decoder_control.cgi?command=".. command .."&amp;onestep=".. lug_stepSize .."&amp;user=".. lug_username .."&amp;pwd=".. urlEncode(lug_password), 5)
			</job>
		</action>
		<action>
			<serviceId>urn:micasaverde-com:serviceId:PanTiltZoom1</serviceId>
			<name>MoveUp</name>
			<job>
				local command = (lug_reverseTilt == "0") and "2" or "0"
				luup.inet.wget("http://".. lug_ip .."/decoder_control.cgi?command=".. command .."&amp;onestep=".. lug_stepSize .."&amp;user=".. lug_username .."&amp;pwd=".. urlEncode(lug_password), 5)
			</job>
		</action>
		<action>
			<serviceId>urn:micasaverde-com:serviceId:PanTiltZoom1</serviceId>
			<name>MoveDown</name>
			<job>
				local command = (lug_reverseTilt == "0") and "0" or "2"
				luup.inet.wget("http://".. lug_ip .."/decoder_control.cgi?command=".. command .."&amp;onestep=".. lug_stepSize .."&amp;user=".. lug_username .."&amp;pwd=".. urlEncode(lug_password), 5)
			</job>
		</action>
		<action>
			<serviceId>urn:micasaverde-com:serviceId:PanTiltZoom1</serviceId>
			<name>VerticalPatrol</name>
			<job>
				luup.inet.wget("http://".. lug_ip .."/decoder_control.cgi?command=26&amp;user=".. lug_username .."&amp;pwd=".. urlEncode(lug_password), 5)
			</job>
		</action>
		<action>
			<serviceId>urn:micasaverde-com:serviceId:PanTiltZoom1</serviceId>
			<name>HorizontalPatrol</name>
			<job>
				luup.inet.wget("http://".. lug_ip .."/decoder_control.cgi?command=28&amp;user=".. lug_username .."&amp;pwd=".. urlEncode(lug_password), 5)
			</job>
		</action>
		<action>
			<serviceId>urn:micasaverde-com:serviceId:PanTiltZoom1</serviceId>
			<name>GoToPreset</name>
			<job>
				local presetNumber = tonumber(lul_settings.presetNumber, 10) or 1
				local command = 2 * (presetNumber - 1) + 31
				luup.inet.wget("http://".. lug_ip .."/decoder_control.cgi?command=".. command .."&amp;user=".. lug_username .."&amp;pwd=".. urlEncode(lug_password), 5)
			</job>
		</action>
		<action>
			<serviceId>urn:micasaverde-com:serviceId:SecuritySensor1</serviceId>
			<name>SetArmed</name>
			<run>
				luup.variable_set(SES_SID, "Armed", lul_settings.newArmedValue, lul_device)
			</run>
		</action>
	</actionList>
</implementation>
