Index: /branches/Foscam/I_FoscamPTZ.xml
===================================================================
--- /branches/Foscam/I_FoscamPTZ.xml	(revision 30)
+++ /branches/Foscam/I_FoscamPTZ.xml	(revision 30)
@@ -0,0 +1,358 @@
+<?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.
+			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>
Index: /tags/2.6/I_FoscamPTZ.xml
===================================================================
--- /tags/2.6/I_FoscamPTZ.xml	(revision 20)
+++ /tags/2.6/I_FoscamPTZ.xml	(revision 30)
@@ -21,5 +21,5 @@
 			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
+			if os.difftime(now, lastTrip) >= ON_TIME then -- 20 minutes
 				luup.variable_set("urn:micasaverde-com:serviceId:SecuritySensor1", "Tripped", "0", sensorId)
 			end
@@ -30,5 +30,5 @@
 
 			local sensorId = tonumber(parameters.sensor_id, 10) or 0
-			if (sensorId == 0) then
+			if sensorId == 0 then
 				return "failed"
 			end
@@ -44,5 +44,5 @@
 
 		local function urlEncode (s)
-			if (s ~= nil) then
+			if s ~= nil then
 				s = s:gsub("\n", "\r\n")
 				s = s:gsub("([^%w ])", function (c)
@@ -64,5 +64,5 @@
 
 			local sensorId = tonumber(luup.variable_get(CMD_SID, "MotionSensorID", lug_device) , 10) or 0
-			if (sensorId > 0) then
+			if sensorId > 0 then
 				return sensorId
 			end
@@ -71,5 +71,5 @@
 			local altid = "foscam_".. lug_device .."_sensor"
 			for dev, attr in pairs(luup.devices) do
-				if (attr.id == altid) then
+				if attr.id == altid then
 					sensorId = dev
 					break
@@ -77,5 +77,5 @@
 			end
 
-			if (sensorId == 0) then
+			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 ..").")
@@ -96,5 +96,5 @@
 
 			-- Check if we have the correct category number.
-			if (luup.devices[lul_device].category_num ~= 6) then
+			if luup.devices[lul_device].category_num ~= 6 then
 				luup.attr_set("category_num", "6", lul_device)
 			end
@@ -105,11 +105,11 @@
 			lug_ip = luup.devices[lul_device].ip or ""
 
-			lug_path = luup.variable_get(CAM_SID, "URL", lul_device) or ""
-			if (lug_path == "") then
+			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
+			if lug_stepSize == "" then
 				lug_stepSize = "1"
 				luup.variable_set(CAM_SID, "StepSize", lug_stepSize, lul_device)
@@ -117,5 +117,5 @@
 
 			lug_reversePan = luup.variable_get(CAM_SID, "ReversePan", lul_device) or ""
-			if (lug_reversePan == "") then
+			if lug_reversePan == "" then
 				lug_reversePan = "0"
 				luup.variable_set(CAM_SID, "ReversePan", lug_reversePan, lul_device)
@@ -123,5 +123,5 @@
 
 			lug_reverseTilt = luup.variable_get(CAM_SID, "ReverseTilt", lul_device) or ""
-			if (lug_reverseTilt == "") then
+			if lug_reverseTilt == "" then
 				lug_reverseTilt = "0"
 				luup.variable_set(CAM_SID, "ReverseTilt", lug_reverseTilt, lul_device)
@@ -130,5 +130,5 @@
 			-- Disable zoom buttons by default.
 			local commands = luup.variable_get(HAD_SID, "Commands", lul_device)
-			if (not commands) then
+			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)
@@ -143,8 +143,8 @@
 			luup.log("(".. luup.devices[lul_device].description ..") State=".. state)
 
-			if (state == "") then
+			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
+				if state == "" then
 					-- Not an upgrade, the plugin is freshly installed.
 					state = STATE_MD_DISABLED
@@ -162,13 +162,13 @@
 			-- Motion detection disabled
 			----------------------------------------------------------------------------------------
-			if (state == STATE_MD_DISABLED) then -- Disable the motion detection.
+			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
+			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
+				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
@@ -176,9 +176,9 @@
 
 				local gatewayAddress = luup.variable_get(CMD_SID, "GatewayAddress", lul_device) or ""
-				if (gatewayAddress == "") then
+				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
+					if camSubnet ~= nil then
 						-- Try the WAN IP.
 						local stdout = io.popen("GetNetworkState.sh ip_wan")
@@ -186,5 +186,5 @@
 						stdout:close()
 
-						if (not gatewayAddress:find(camSubnet, 1, true)) then
+						if not gatewayAddress:find(camSubnet, 1, true) then
 							-- Try the LAN IP.
 							stdout = io.popen("GetNetworkState.sh ip_lan")
@@ -192,5 +192,5 @@
 							stdout:close()
 
-							if (not gatewayAddress:find(camSubnet, 1, true)) then
+							if not gatewayAddress:find(camSubnet, 1, true) then
 								-- The camera is in a different network than Vera.
 								gatewayAddress = ""
@@ -200,5 +200,5 @@
 				end
 
-				if (gatewayAddress == "") then
+				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.
@@ -212,15 +212,18 @@
 
 				-- 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", "Foscam #".. lul_device .." 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.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
+			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
+				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
@@ -229,5 +232,5 @@
 				-- Get the motion sensor's device ID.
 				local sensorId = getMotionSensorId(state)
-				if (not sensorId) then
+				if not sensorId then
 					luup.call_delay("showSysMessage", 2, "Reload Luup to create motion sensor")
 					return
@@ -240,5 +243,5 @@
 				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
+				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
@@ -254,4 +257,5 @@
 				-- The configuration was successful.
 				luup.variable_set(CMD_SID, "State", STATE_CONFIGURED_OK, lul_device)
+
 			----------------------------------------------------------------------------------------
 			-- Camera configured
@@ -261,11 +265,12 @@
 				-- Get the motion sensor's device ID.
 				local sensorId = getMotionSensorId(state)
-				if (not sensorId) then
-					return false, "Reload Luup to create motion sensor", luup.devices[lul_device].description
+				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
+				if onTime:find("^%d+$") ~= nil then
 					ON_TIME = tonumber(onTime, 10) or ON_TIME
 				else
@@ -275,5 +280,5 @@
 				-- 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
+				if os.difftime(os.time(), lastTrip) >= ON_TIME then
 					luup.variable_set(SES_SID, "Tripped", "0", sensorId)
 				end
Index: /trunk/I_VistaCamPT.xml
===================================================================
--- /trunk/I_VistaCamPT.xml	(revision 30)
+++ /trunk/I_VistaCamPT.xml	(revision 30)
@@ -0,0 +1,477 @@
+<?xml version="1.0"?>
+<implementation>
+	<functions>
+		----------------------------------------------------------------------------------------------------------------
+		-- Constants
+		----------------------------------------------------------------------------------------------------------------
+		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   = "0" -- Motion detection is disabled.
+		local STATE_CREATE_MS     = "1" -- Create the motion sensor.
+		local STATE_CONFIGURE_CAM = "2" -- Configure the camera.
+		local STATE_CONFIGURED_OK = "3" -- Everything is configured OK.
+
+		local ON_TIME = 1200    -- How many seconds a sensor stays tripped after it detected motion (20 minutes)
+		local SLEEP_STEP = 1000 -- How many milliseconds to sleep per step after sending a pan/tilt command (1 sec/step)
+
+		-- Camera types supported by the plugin
+		local VISTACAM_PT = "1"
+		local VISTACAM_HD = "2"
+
+		-- Camera specific info
+		local CAMERA = {
+			[VISTACAM_PT] = {
+				["NAME"] = "VistaCam PT",
+				["SNAPSHOT_URL"] = "/snapshot.cgi",
+				["CMD_TILT_UP"] = "/decoder_control.cgi?command=0",
+				["CMD_TILT_DOWN"] = "/decoder_control.cgi?command=2",
+				["CMD_PAN_LEFT"] = "/decoder_control.cgi?command=4",
+				["CMD_PAN_RIGHT"] = "/decoder_control.cgi?command=6",
+				["CMD_STOP"] = "/decoder_control.cgi?command=1",
+				["CMD_CALL_PRESET"] = "/decoder_control.cgi?command=",
+				["TEST_URL"] = "/get_status.cgi",
+				["TEST_URL_RESPONSE"] = "var id"
+			},
+			[VISTACAM_HD] = {
+				["NAME"] = "VistaCam HD",
+				["SNAPSHOT_URL"] = "/cgi-bin/video_snapshot.cgi?user=TOK_USER&amp;pwd=TOK_PWD",
+				["CMD_TILT_UP"] = "/cgi-bin/decoder_control.cgi?type=0&amp;cmd=0",
+				["CMD_TILT_DOWN"] = "/cgi-bin/decoder_control.cgi?type=0&amp;cmd=1",
+				["CMD_PAN_LEFT"] = "/cgi-bin/decoder_control.cgi?type=0&amp;cmd=2",
+				["CMD_PAN_RIGHT"] = "/cgi-bin/decoder_control.cgi?type=0&amp;cmd=3",
+				["CMD_STOP"] = "/cgi-bin/decoder_control.cgi?type=0&amp;command=10",
+				["CMD_CALL_PRESET"] = "/cgi-bin/decoder_control.cgi?type=2&amp;command=",
+				["TEST_URL"] = "/cgi-bin/get_status.cgi",
+				["TEST_URL_RESPONSE"] = "var ret_video_port"
+			}
+		}
+
+		-- List of camera variables which may contain credentials tokens (placeholders)
+		local VARIABLES = { "URL", "DirectStreamingURL", "VideoURLs" }
+
+		----------------------------------------------------------------------------------------------------------------
+		-- Globals
+		----------------------------------------------------------------------------------------------------------------
+		local lug_devNum
+		local lug_devName
+		local lug_ip
+		local lug_username
+		local lug_password
+		local lug_camType
+		local lug_stepSize
+		local lug_reversePan
+		local lug_reverseTilt
+
+		----------------------------------------------------------------------------------------------------------------
+		-- Functions
+		----------------------------------------------------------------------------------------------------------------
+		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
+				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", os.time(), sensorId)
+
+			luup.call_delay("Untrip", ON_TIME, parameters.sensor_id)
+			return "OK"
+		end
+
+
+		local function UrlEncode( s )
+			s = s:gsub("\n", "\r\n")
+			s = s:gsub("([^%w])", function (c)
+			                          return string.format("%%%02X", string.byte(c))
+			                      end)
+			return s
+		end
+
+
+		function ShowSysMessage( message )
+			luup.task( message, 1, lug_devName, -1 )
+		end
+
+
+		local function GetMotionSensorId( state )
+
+			local sensorId = tonumber(luup.variable_get(CMD_SID, "MotionSensorID", lug_devNum) , 10) or 0
+			if sensorId > 0 then
+				return sensorId
+			end
+
+			-- Get the motion sensor's device ID.
+			local altid = "camera_".. lug_devNum .."_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("(VistaCamPT::GetMotionSensorId-".. 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_devNum)
+				return
+			end
+
+			-- Store the motion sensor ID in a variable.
+			luup.log("(VistaCamPT::GetMotionSensorId-".. state ..") Motion sensor is device #".. sensorId)
+			luup.variable_set(CMD_SID, "MotionSensorID", tostring(sensorId), lug_devNum)
+			return sensorId
+		end
+
+
+		local function SetUpMotionDetection()
+
+			-- Check if the camera was configured for motion detection.
+			local state = luup.variable_get(CMD_SID, "State", lug_devNum) or ""
+			luup.log("(VistaCamPT::SetUpMotionDetection) State=".. state)
+			if state == "" then
+				state = STATE_MD_DISABLED
+				luup.log("(VistaCamPT::SetUpMotionDetection) Initializing the State variable to STATE_MD_DISABLED(".. state ..")")
+				luup.variable_set(CMD_SID, "State", state, lug_devNum)
+			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
+
+				local gatewayAddress = luup.variable_get(CMD_SID, "GatewayAddress", lug_devNum) 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", "", lug_devNum)
+				else
+					luup.variable_set(CMD_SID, "GatewayAddress", gatewayAddress, lug_devNum)
+				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, lug_devNum)
+
+				-- Create the motion sensor.
+				luup.log("(VistaCamPT::SetUpMotionDetection-".. state ..") Create the motion sensor")
+				local ptr = luup.chdev.start(lug_devNum)
+				luup.chdev.append(lug_devNum, ptr, "camera_".. lug_devNum .."_sensor", lug_devName .." 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(lug_devNum, 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", lug_devNum) or ""
+				if gatewayAddress == "" then
+					luup.log("(VistaCamPT::SetUpMotionDetection-".. state ..") GatewayAddress is empty")
+					return false, "Missing Gateway IP address", lug_devName
+				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_camera_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("(VistaCamPT::SetUpMotionDetection-".. state ..") Failed to configure camera")
+					return false, "Failed to configure camera", lug_devName
+				end
+
+				-- Initialize OnTime variable, which is the time until the sensor is considered untripped.
+				luup.variable_set(SES_SID, "OnTime", ON_TIME, sensorId)
+
+				-- Run the 'Trip' function when notified.
+				luup.log("(VistaCamPT::SetUpMotionDetection-".. state ..") Register handler")
+				luup.register_handler("Trip", "camera_tripped")
+
+				-- The configuration was successful.
+				luup.variable_set(CMD_SID, "State", STATE_CONFIGURED_OK, lug_devNum)
+
+			----------------------------------------------------------------------------------------
+			-- 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("(VistaCamPT::SetUpMotionDetection-".. state ..") Register handler")
+				luup.register_handler("Trip", "camera_tripped")
+			end
+
+		end
+
+
+		local function GetCameraType()
+			local camType
+
+			-- Try to find which type of VistaCam this is, based on the snapshot URL.
+			local snapshotUrl = luup.variable_get(CAM_SID, "URL", lug_devNum) or ""
+			if snapshotUrl:match("^(.-)\?") == CAMERA[VISTACAM_PT].SNAPSHOT_URL:match("^(.-)\?") then
+				camType = VISTACAM_PT
+			elseif snapshotUrl:match("^(.-)\?") == CAMERA[VISTACAM_HD].SNAPSHOT_URL:match("^(.-)\?") then
+				camType = VISTACAM_HD
+			else
+				-- Is this a VistaCam PT?
+				luup.log("(VistaCamPT::FindCameraType) Couldn't find camera type using snapshot URL, check if VistaCam PT with URL: http://".. lug_ip .. CAMERA[VISTACAM_PT].TEST_URL)
+				local status, response = luup.inet.wget("http://".. lug_ip .. CAMERA[VISTACAM_PT].TEST_URL, 5, lug_username, lug_password)
+				if status == 0 then
+					if response:find(CAMERA[VISTACAM_PT].TEST_URL_RESPONSE, 1, true) then
+						camType = VISTACAM_PT
+						if snapshotUrl == "" then
+							luup.variable_set(CAM_SID, "URL", CAMERA[camType].SNAPSHOT_URL, lug_devNum)
+						end
+					else
+						luup.log("(VistaCamPT::FindCameraType) ERROR: Got status 0, but different response than expected")
+					end
+					return camType
+				end
+				-- Is this a VistaCam HD?
+				luup.log("(VistaCamPT::FindCameraType) Check if VistaCam HD with URL: http://".. lug_ip .. CAMERA[VISTACAM_HD].TEST_URL)
+				local status, response = luup.inet.wget("http://".. lug_ip .. CAMERA[VISTACAM_HD].TEST_URL, 5, lug_username, lug_password)
+				if status == 0 then
+					if response:find(CAMERA[VISTACAM_HD].TEST_URL_RESPONSE, 1, true) then
+						camType = VISTACAM_HD
+						if snapshotUrl == "" then
+							luup.variable_set(CAM_SID, "URL", CAMERA[camType].SNAPSHOT_URL, lug_devNum)
+						end
+					else
+						luup.log("(VistaCamPT::FindCameraType) ERROR: Got status 0, but different response than expected")
+					end
+					return camType
+				end
+			end -- end if snapshotUrl
+			return camType
+		end -- end function
+
+
+		----------------------------------------------------------------------------------------------------------------
+		-- Main
+		----------------------------------------------------------------------------------------------------------------
+		function Startup( devNum )
+
+			lug_devNum = devNum
+			lug_devName = luup.devices[devNum].description
+
+			luup.log("VistaCamPT plugin, device #".. devNum .."-".. lug_devName)
+
+			-- Check if we have the correct category number.
+			if luup.devices[devNum].category_num ~= 6 then
+				luup.attr_set("category_num", "6", devNum)
+			end
+
+			-- Check if we know the camera IP.
+			lug_ip = luup.devices[devNum].ip or ""
+			if lug_ip == "" then
+				return false, "Camera IP is missing", lug_devName
+			end
+
+			lug_username = luup.devices[devNum].user or luup.variable_get(CAM_SID, "Username", devNum) or ""
+			lug_password = luup.devices[devNum].pass or luup.variable_get(CAM_SID, "Password", devNum) or ""
+
+			-- Find camera type.
+			lug_camType = GetCameraType()
+			if lug_camType then
+				luup.log("(VistaCamPT::Startup) Camera type: ".. CAMERA[lug_camType].NAME)
+			else
+				return false, "Couldn't find camera type", lug_devName
+			end
+
+			-- Replace credential tokens in URLs.
+			if lug_username ~= "" then
+				for _, variable in pairs(VARIABLES) do
+					local value = luup.variable_get(CAM_SID, variable, devNum) or ""
+					if value ~= "" then
+						local newValue = value:gsub("TOK_USER", lug_username):gsub("TOK_PWD", lug_password)
+						if newValue ~= value then
+							luup.log("(VistaCamPT::Startup) Replaced credential tokens in variable: ".. variable)
+							luup.variable_set(CAM_SID, variable, newValue, devNum)
+						end
+					end
+				end
+			end
+
+			-- Get and init step size.
+			lug_stepSize = tonumber( luup.variable_get(CAM_SID, "StepSize", devNum), 10 ) or 0
+			if lug_stepSize == 0 then
+				lug_stepSize = 1
+				luup.variable_set(CAM_SID, "StepSize", lug_stepSize, devNum)
+			end
+
+			-- Get and init ReversePan.
+			lug_reversePan = luup.variable_get(CAM_SID, "ReversePan", devNum) or ""
+			if lug_reversePan == "" then
+				lug_reversePan = "0"
+				luup.variable_set(CAM_SID, "ReversePan", lug_reversePan, devNum)
+			end
+
+			-- Get and init ReverseTilt.
+			lug_reverseTilt = luup.variable_get(CAM_SID, "ReverseTilt", devNum) or ""
+			if lug_reverseTilt == "" then
+				lug_reverseTilt = "0"
+				luup.variable_set(CAM_SID, "ReverseTilt", lug_reverseTilt, devNum)
+			end
+
+			-- Disable zoom buttons.
+			local commands = luup.variable_get(HAD_SID, "Commands", devNum)
+			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, devNum)
+			end
+
+			-- Set up motion detection.
+			if lug_camType == VISTACAM_PT then
+				SetUpMotionDetection()
+			end
+		end
+	</functions>
+	<startup>Startup</startup>
+	<actionList>
+		<action>
+			<serviceId>urn:micasaverde-com:serviceId:PanTiltZoom1</serviceId>
+			<name>MoveUp</name>
+			<job>
+				local command = (lug_reverseTilt == "0") and CAMERA[lug_camType].CMD_TILT_UP or CAMERA[lug_camType].CMD_TILT_DOWN
+				luup.inet.wget("http://".. lug_ip .. command .."&amp;user=".. lug_username .."&amp;pwd=".. UrlEncode(lug_password), 5)
+				luup.sleep(lug_stepSize * SLEEP_STEP)
+				luup.inet.wget("http://".. lug_ip .. CAMERA[lug_camType].CMD_STOP .."&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 CAMERA[lug_camType].CMD_TILT_DOWN or CAMERA[lug_camType].CMD_TILT_UP
+				luup.inet.wget("http://".. lug_ip .. command .."&amp;user=".. lug_username .."&amp;pwd=".. UrlEncode(lug_password), 5)
+				luup.sleep(lug_stepSize * SLEEP_STEP)
+				luup.inet.wget("http://".. lug_ip .. CAMERA[lug_camType].CMD_STOP .."&amp;user=".. lug_username .."&amp;pwd=".. UrlEncode(lug_password), 5)
+			</job>
+		</action>
+		<action>
+			<serviceId>urn:micasaverde-com:serviceId:PanTiltZoom1</serviceId>
+			<name>MoveLeft</name>
+			<job>
+				local command = (lug_reversePan == "0") and CAMERA[lug_camType].CMD_PAN_LEFT or CAMERA[lug_camType].CMD_PAN_RIGHT
+				luup.inet.wget("http://".. lug_ip .. command .."&amp;user=".. lug_username .."&amp;pwd=".. UrlEncode(lug_password), 5)
+				luup.sleep(lug_stepSize * SLEEP_STEP)
+				luup.inet.wget("http://".. lug_ip .. CAMERA[lug_camType].CMD_STOP .."&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 CAMERA[lug_camType].CMD_PAN_RIGHT or CAMERA[lug_camType].CMD_PAN_LEFT
+				luup.inet.wget("http://".. lug_ip .. command .."&amp;user=".. lug_username .."&amp;pwd=".. UrlEncode(lug_password), 5)
+				luup.sleep(lug_stepSize * SLEEP_STEP)
+				luup.inet.wget("http://".. lug_ip .. CAMERA[lug_camType].CMD_STOP .."&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)
+				if lug_camType == VISTACAM_PT then
+					presetNumber = 2 * (presetNumber - 1) + 31
+				elseif lug_camType == VISTACAM_HD then
+					presetNumber = presetNumber - 1
+				end
+				luup.inet.wget("http://".. lug_ip .. CAMERA[lug_camType].CMD_CALL_PRESET .. presetNumber .."&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, devNum)
+			</run>
+		</action>
+	</actionList>
+</implementation>
Index: unk/I_FoscamPTZ.xml
===================================================================
--- /trunk/I_FoscamPTZ.xml	(revision 20)
+++ 	(revision )
@@ -1,353 +1,0 @@
-<?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 ""
-
-			lug_path = luup.variable_get(CAM_SID, "URL", lul_device) or ""
-			if (lug_path == "") 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.
-				local ptr = luup.chdev.start(lul_device)
-				luup.chdev.append(lul_device, ptr, "foscam_".. lul_device .."_sensor", "Foscam #".. lul_device .." 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
-					return false, "Reload Luup to create motion sensor", luup.devices[lul_device].description
-				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>
