Index: /trunk/S_Mochad1.xml
===================================================================
--- /trunk/S_Mochad1.xml	(revision 3)
+++ /trunk/S_Mochad1.xml	(revision 3)
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<scpd xmlns="urn:schemas-upnp-org:service-1-0">
+  <specVersion>
+    <major>1</major>
+    <minor>0</minor>
+  </specVersion>
+  <serviceStateTable>
+ 
+    <stateVariable sendEvents="no">
+      <name>PowerLineCommand</name>
+      <datatype>boolean</datatype>
+    </stateVariable>
+ 
+    <stateVariable sendEvents="no">
+      <name>BinaryModules</name>
+      <datatype>string</datatype>
+    </stateVariable>
+	
+    <stateVariable sendEvents="no">
+      <name>DimmableModules</name>
+      <datatype>string</datatype>
+    </stateVariable>
+	
+    <stateVariable sendEvents="no">
+      <name>SoftstartModules</name>
+      <datatype>string</datatype>
+    </stateVariable>
+ 
+     <stateVariable sendEvents="no">
+      <name>MotionSensors</name>
+      <datatype>string</datatype>
+    </stateVariable>
+ 
+    <stateVariable sendEvents="no">
+      <name>RFSecMotionSensors</name>
+      <datatype>string</datatype>
+    </stateVariable>
+ 
+    <stateVariable sendEvents="no">
+      <name>RFSecDoorSensors</name>
+      <datatype>string</datatype>
+    </stateVariable>
+ 
+    <stateVariable sendEvents="no">
+      <name>LCD</name>
+      <datatype>string</datatype>
+    </stateVariable>
+
+</scpd>
Index: /trunk/D_Mochad1.xml
===================================================================
--- /trunk/D_Mochad1.xml	(revision 3)
+++ /trunk/D_Mochad1.xml	(revision 3)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+  <specVersion>
+    <major>1</major>
+    <minor>0</minor>
+  </specVersion>
+  <device>
+    <deviceType>urn:x10-com:device:controller:1</deviceType>
+    <friendlyName>Mochad Control of cm15a over ethernet</friendlyName>
+    <manufacturer>X10</manufacturer>
+    <manufacturerURL>x10.com</manufacturerURL>
+    <modelDescription>X10 RF/PL Controller</modelDescription>
+    <modelName>cm15a</modelName>
+    <modelNumber>cm15a</modelNumber>
+    <protocol>crlf</protocol>
+    <handleChildren>1</handleChildren>
+    <serviceList>
+    </serviceList>
+    <implementationList>
+      <implementationFile>I_Mochad1.xml</implementationFile>
+    </implementationList>
+  </device>
+</root>
Index: /trunk/I_Mochad1.xml
===================================================================
--- /trunk/I_Mochad1.xml	(revision 3)
+++ /trunk/I_Mochad1.xml	(revision 3)
@@ -0,0 +1,374 @@
+<?xml version="1.0"?>
+<implementation>
+  <settings>
+      <protocol>crlf</protocol>
+  </settings>
+
+  <functions>
+
+  local SECURITY_SID  = "urn:micasaverde-com:serviceId:SecuritySensor1"
+  local MOCHAD_SID    = "urn:micasaverde-com:serviceId:mochad1"
+  local SWITCHPWR_SID = "urn:upnp-org:serviceId:SwitchPower1"
+  local HADEVICE_SID  = "urn:micasaverde-com:serviceId:HaDevice1"
+  local DIMMING_SID   = "urn:upnp-org:serviceId:Dimming1"
+  local DEBUG_MODE = true
+
+  local ipAddress
+  local ipPort = 1099
+  local buffer = ""
+  
+  local controller_id
+  
+  local power_line_command
+  
+  local child_id_lookup_table = {}
+  
+  local socket = require("socket")
+  
+  ------------------------------------------------------------
+  local function trim(s)
+      return s:gsub("^%s*", ""):gsub("%s*$","")
+  end
+  
+  ------------------------------------------------------------  
+  local function log(text, level)
+  	    luup.log("MOCHAD: " .. text, (level or 1))
+  end
+	
+  ------------------------------------------------------------  	
+  local function debug(text)
+	  if (DEBUG_MODE == true) then
+	      log("debug: " .. (text or "empty"), 50) -- had to remove the angle brackets since this isn't in a lua file...?
+	  end
+   end
+  
+   ------------------------------------------------------------
+   local function x10_id(dev_id)
+       local xid =  dev_id:sub(3)
+       return xid
+   end
+  
+   ------------------------------------------------------------
+   local function sendCommand(command)
+        if luup.io.write(command)==false then
+            log("cannot send: " .. tostring(command),1)
+            luup.set_failure(true)
+            return false
+        else
+            log('Wrote: ' .. command);
+            return true
+        end
+   end
+    
+   ------------------------------------------------------------
+   -- convert a 'sep' seperated string into a lua list
+   local function split_deliminated_string(s,sep)
+      if s==nil then
+          return {}
+      end
+      s = s .. sep        -- ending seperator
+   	  local t = {}        -- table to collect fields
+	  local fieldstart = 1
+	  repeat
+		  local nexti = string.find(s, sep, fieldstart)
+		  table.insert(t, string.sub(s, fieldstart, nexti-1))
+		  fieldstart = nexti + 1
+	  until fieldstart > string.len(s)
+      return t
+	end
+	
+   ------------------------------------------------------------
+   local function is_type(dev, type)
+      local named_devs = luup.variable_get(MOCHAD_SID, type, controller_id)
+      if( named_devs==nil ) then
+          return false
+      end
+      t = split_deliminated_string(named_devs,',')
+      for i,element in ipairs(t) do
+          if element == dev then
+              return true
+          end
+      end
+      return false
+   end
+   
+   ------------------------------------------------------------
+   local function add_children(parent, child_list_ptr, prefix, schema, dev_file, dev_type, csv_dev_list)
+      local dev_list = split_deliminated_string(csv_dev_list,',')
+      for idx, dev_name in ipairs(dev_list) do
+          dev_name = trim(dev_name)
+          if (dev_name and dev_name ~= "") then
+             log("MOCHAD: Adding " .. dev_type .. " " .. dev_name)
+             luup.chdev.append(parent, child_list_ptr, prefix .. dev_name, "X10 " .. dev_name, schema, dev_file, "", "", false)
+          end
+       end
+    end
+
+   
+   ------------------------------------------------------------
+   -- STARTUP
+   ------------------------------------------------------------
+   function lug_startup(lul_device)
+   
+       ------------------------------------------------------------
+       -- Open Connection to Mochad
+       ------------------------------------------------------------
+       controller_id = lul_device
+       
+       ipAddress = luup.devices[lul_device].ip
+      
+       if (ipAddress ~= "") then
+            log("Running Network Attached I_Mochad1.xml on " .. ipAddress)
+            luup.io.open(lul_device, ipAddress, ipPort)
+       else
+           log("Can not connect to mochad on " .. ipAddress)
+           return false, "Mochad", "Cannot connect to Mochad on " .. ipAddress
+       end
+      
+       ------------------------------------------------------------
+       -- Create a new Child Device List
+       child_devices = luup.chdev.start(lul_device);
+       ------------------------------------------------------------
+       
+       -- Vera gets angry with me when I accidently add two child devices
+       -- with the same name but different schemas. (as well it should!)
+       -- To avoid that I am appending a prefix before the X10 code of the child device
+       -- A-A01 is an applicance module at A01
+       -- D-A01 is a dimmer at A01
+       -- X-A01 is a dimmer at A01
+       -- M-A01 is a motion sensor at A01
+
+
+       ---------------
+       -- Get a list of child devices
+       local app_ID     = luup.variable_get(MOCHAD_SID, "BinaryModules",      lul_device)
+       local dim_ID     = luup.variable_get(MOCHAD_SID, "DimmableModules",    lul_device)
+       local xdim_ID    = luup.variable_get(MOCHAD_SID, "SoftstartModules",   lul_device)
+       local motion_ID  = luup.variable_get(MOCHAD_SID, "MotionSensors",      lul_device)
+	   local rfsec_m_ID = luup.variable_get(MOCHAD_SID, "RFSecMotionSensors", lul_device)
+	   local rfsec_d_ID = luup.variable_get(MOCHAD_SID, "RFSecDoorSensors",   lul_device)
+       
+       ---------------
+       -- If all child devices are empty add a few examples
+        if ((app_ID == nil) and (dim_ID == nil) and (xdim_ID == nil) and (motion_ID == nil) and (rfsec_m_ID==nill) and (rfsec_d_ID==nill)) then
+            xdim_ID = "A1,A2"
+            motion_ID = "M1,M2"
+			
+			luup.variable_set(MOCHAD_SID, "BinaryModules",      "",        lul_device)
+			luup.variable_set(MOCHAD_SID, "DimmableModules",    "",        lul_device)
+			luup.variable_set(MOCHAD_SID, "SoftstartModules",   xdim_ID,   lul_device)
+			luup.variable_set(MOCHAD_SID, "MotionSensors",      motion_ID, lul_device)
+			luup.variable_set(MOCHAD_SID, "RFSecMotionSensors", "",        lul_device)
+			luup.variable_set(MOCHAD_SID, "RFSecDoorSensors",   "",        lul_device)	
+        end
+
+		power_line_command = luup.variable_get(MOCHAD_SID, "PowerLineCommand", lul_device)
+		if (power_line_command == nil) then
+		    power_line_command = 0
+			luup.variable_set(MOCHAD_SID, "PowerLineCommand", power_line_command, lul_device)
+		end
+		
+       ------------------------------------------------------------
+       -- APPLIANCE MODULES
+       add_children(lul_device, child_devices, 'A-', "urn:schemas-micasaverde-com:device:BinaryLight:1",   "D_BinaryLight1.xml",   "Binary Light", app_ID)
+       -- DIMMABLE LIGHTS
+       add_children(lul_device, child_devices, 'D-', "urn:schemas-micasaverde-com:device:DimmableLight:1", "D_DimmableLight1.xml", "Dimmable Light", dim_ID)
+       -- SOFTSTART DIMMABLE LIGHTS --
+       add_children(lul_device, child_devices, 'X-', "urn:schemas-micasaverde-com:device:DimmableLight:1", "D_DimmableLight1.xml", "Binary Light", xdim_ID)
+       -- MOTION SENSORS --
+       add_children(lul_device, child_devices, 'M-', "urn:schemas-micasaverde-com:device:MotionSensor:1",  "D_MotionSensor1.xml",  "Motion Sensor", motion_ID)
+	   -- RFSEC MOTION SENSORS --
+       add_children(lul_device, child_devices, 'R-', "urn:schemas-micasaverde-com:device:MotionSensor:1",  "D_MotionSensor1.xml",  "RFSec Motion Sensor", rfsec_m_ID)
+	   -- RFSEC DOOR/WINDOW SENSORS --
+       add_children(lul_device, child_devices, 'S-', "urn:schemas-micasaverde-com:device:MotionSensor:1",  "D_MotionSensor1.xml",  "RFSec Door/Window Sensor", rfsec_d_ID)
+	   
+       luup.chdev.sync(lul_device, child_devices)
+       
+       ------------------------------------------------------------
+       -- Find my children and build lookup table of altid -> id
+       ------------------------------------------------------------
+       -- loop over all the devices registered on Vera
+       for k, v in pairs(luup.devices) do
+           -- if I am the parent device
+           if v.device_num_parent == luup.device then
+               log('Found Child ID: ' .. k .. ' AltID: ' .. v.id)
+               child_id_lookup_table[v.id] = k
+           end
+       end
+       
+   end
+   
+  </functions>
+  
+  <incoming>
+     <lua>
+
+         local data = tostring(lul_data)
+         
+         local t = split_deliminated_string(data,' ')
+         
+		 local rx_type = t[4]
+		 local addr = t[6]
+		 local new_state = t[8]
+		 
+         if (t[3] == "Rx") then
+		 
+             log("MOCHAD: Recieved "..data)
+			 
+			 ------------
+			 -- Check to see if it is an RFSEC device:
+			 if (rx_type == "RFSEC") then
+
+			    local trip
+				local altit
+
+			    -- Handle RFSec Motion Sensors:
+				if ( is_type(addr, "RFSecMotionSensors") ) then
+				    altid = 'R-' .. addr
+			        if (new_state == "Motion_alert_MS10A") then
+					    trip = '1'
+				    elseif (new_state == "Motion_normal_MS10A") then
+					    trip = '0'
+				    end
+
+				-- Handle RFSec Door Sensors:	
+			    elseif (is_type(addr, RFSecDoorSensors) ) then
+					altid = 'S-' .. addr
+   				    if( new_state:sub(1, 13) == "Contact_alert" ) then
+					    trip = '1'
+					elseif (new_state:sub(1, 14) == "Contact_normal") then
+					    trip = '0'
+				    end
+				end
+				
+				if (trip ~= nil_) then
+                    luup.variable_set(SECURITY_SID, "Tripped", trip, child_id_lookup_table[altid])
+			    end
+
+		    -------------
+			-- Otherwise this is a normal X10 House/Unit Code Command:
+			 elseif  (rx_type == "RF") then
+			    
+				 if (is_type(addr, "MotionSensors")) then
+					 local altid = "M-" .. addr
+					 if (new_state == 'On') then
+						 luup.variable_set(SECURITY_SID, "Tripped", '1', child_id_lookup_table[altid])
+					 else
+						 luup.variable_set(SECURITY_SID, "Tripped", '0', child_id_lookup_table[altid])
+					 end
+				 end
+				 
+		    end
+         end
+     </lua>
+    </incoming>
+  
+  <startup>lug_startup</startup>
+  
+  <actionList>
+  
+    <action>
+      <serviceId>urn:upnp-org:serviceId:SwitchPower1</serviceId>
+      <name>SetTarget</name>
+      <run>
+
+        local dev_x10_id = x10_id(luup.devices[lul_device].id)
+        local lul_command = 'pl ' .. dev_x10_id .. ' off'
+        
+        local lul_reverse = luup.variable_get(HADEVICE_SID,"ReverseOnOff",lul_device)
+        if( lul_settings.newTargetValue=="1" or (lul_settings.newTargetValue=="0" and lul_reverse=="1") ) then
+            lul_command = 'pl ' ..  dev_x10_id .. ' on'
+        end
+
+        luup.variable_set(SWITCHPWR_SID,"Status",lul_settings.newTargetValue, lul_device)
+        sendCommand(lul_command)
+
+      </run>
+    </action>
+    
+    <action>
+      <serviceId>urn:upnp-org:serviceId:Dimming1</serviceId>
+      <name>SetLoadLevelTarget</name>
+      <run>
+        local dim_level
+        local lul_command
+        local dev_x10_id = x10_id(luup.devices[lul_device].id)
+		local current_dim_level
+		
+		if power_line_command == 1 then
+			----------------------------------------------------------
+			-- For PL commands we can send a target dim level:
+			-- We need to support both extended dim (xdim) and normal dim X10 modules
+			-- Older x10 modules use "dim" and scale from 0 to 31.
+			-- Newer "soft-start" modules use "xdim" and scale from 0 to 63.
+			if 'X' == string.sub(luup.devices[lul_device].id,1,1) then
+				dim_level = math.floor(lul_settings.newLoadlevelTarget *63/100)
+				lul_command = 'pl ' .. dev_x10_id .. ' xdim ' .. dim_level
+			else
+				dim_level = math.floor(lul_settings.newLoadlevelTarget *31/100)
+				lul_command = 'pl ' .. dev_x10_id .. ' dim ' .. dim_level
+			end
+			sendCommand(lul_command)
+		else
+			----------------------------------------------------------
+			-- For RF commands we have to send a series of "dim" / "brighten" commands
+			-- ActiveHome sends 9 dim/brights to move from 0% to 100%. Of course,
+			-- it does seem to be a bit broken because it will send a dim/brighten
+			-- as well for every precentage you move. For now I'm going to assume that 
+			-- each dim/brighten corrisponds to 10%
+			
+			-- if we want 0% or 100% just send on/off
+			if (lul_settings.newLoadlevelTarget == '0') then
+				sendCommand('rf ' .. dev_x10_id .. ' off')
+            elseif (lul_settings.newLoadlevelTarget == '100') then
+				sendCommand('rf ' .. dev_x10_id .. ' on')
+			else
+				-- X10 devices don't save their dim levels between after being turned off.
+				-- if the light is current off then the current dim is 0
+				if luup.variable_get(SWITCHPWR_SID,"Status", lul_device) then
+					current_dim_level = luup.variable_get(DIMMING_SID,"LoadLevelStatus",lul_device)
+				else
+					current_dim_level = 0
+				end
+				
+				local curr_step = math.floor(current_dim_level/10)
+				local desired_step = math.floor(lul_settings.newLoadlevelTarget/10)
+				
+				while (desired_step > curr_step) do
+					sendCommand('rf ' .. dev_x10_id .. ' bright')
+					curr_step = curr_step + 1
+					luup.sleep(1000)
+				end
+				
+				while (curr_step > desired_step) do
+					sendCommand('rf ' .. dev_x10_id .. ' dim')
+					curr_step = curr_step - 1
+					luup.sleep(1000)
+				end
+			end
+			
+        end
+		
+        luup.variable_set(DIMMING_SID,"LoadLevelStatus",lul_settings.newLoadlevelTarget, lul_device)
+      </run>
+    </action>
+    
+    <action>
+      <serviceId>urn:micasaverde-com:serviceId:SecuritySensor1</serviceId>
+      <name>SetArmed</name>
+      <run>
+        debug("Action::SetArmed:"..lul_settings.newArmedValue)
+        luup.variable_set(SECURITY_SID, "Armed", lul_settings.newArmedValue, lul_device)
+      </run>
+    </action>
+
+    <action>
+      <serviceId>urn:micasaverde-com:serviceId:HaDevice1</serviceId>
+      <name>ToggleState</name>
+      <run>
+        debug("Action::ToggleState:" .. lul_device)
+      </run>
+    </action>
+    
+  </actionList>
+</implementation>
