Index: /L_ElkAlarmPanel1.lua
===================================================================
--- /L_ElkAlarmPanel1.lua	(revision 3)
+++ /L_ElkAlarmPanel1.lua	(revision 3)
@@ -0,0 +1,1050 @@
+-- Constants
+local VERSION = "1.0 alpha"
+
+local ELK_PANEL_SID = "urn:micasaverde-com:serviceId:ElkAlarmPanel1"
+local ALARM_PARTITION_SID = "urn:micasaverde-com:serviceId:AlarmPartition2"
+local SECURITY_SID = "urn:micasaverde-com:serviceId:SecuritySensor1"
+local DEBUG_MODE = true
+
+local TASK_ERROR = 2
+local TASK_ERROR_PERM = -2
+local TASK_SUCCESS = 4
+local TASK_BUSY = 1
+
+-- Globals
+local g_remoteArmEnabled = false
+local g_remoteDisarmEnabled = false
+local g_remotePanicEnabled = false
+
+local g_alarmDevice
+local g_isStartupMode = true
+local g_taskHandle = -1
+
+local g_childDevices = {
+    partitions = {},
+    zones = {},
+}
+
+local log = luup.log
+
+
+---- Generic Utilities ----
+
+local function debug (text)
+    if (DEBUG_MODE == true) then
+        log (text)
+    end
+end
+
+
+local function task (text, mode)
+    log (string.format ("task '%s'", text))
+    if (mode == TASK_ERROR_PERM) then
+        g_taskHandle = luup.task (text, TASK_ERROR, "DSCAlarmPanel", g_taskHandle)
+    else
+        g_taskHandle = luup.task (text, mode, "DSCAlarmPanel", g_taskHandle)
+
+        -- Clear the previous error, since they're all transient
+        if (mode ~= TASK_SUCCESS) then
+            luup.call_delay ("clearTask", 30, "", false)
+        end
+    end
+end
+
+-- Has to be "non-local" in order for MiOS to call it :(
+function clearTask()
+    task ("Clearing...", TASK_SUCCESS)
+end
+
+-- Concatenates the given strings.
+local function strCat (s, ...)
+    if (select ("#", ...) == 0) then
+        return s
+    end
+    s = (s == nil) and "" or tostring (s)
+
+    return s .. strCat (...)
+end
+
+
+function table:contains (item)
+    for _, v in pairs (self) do
+        if v == item then
+            return true
+        end
+    end
+
+    return false
+end
+
+-- Trims leading and trailing spaces.
+local function trimSpaces (s)
+    return s:gsub("^%s*", ""):gsub("%s*$", "")
+end
+
+
+local function rpad(s, len, ch)
+    -- TODO: Not strictly correct, since "ch" isn"t guaranteed to be one char,
+    --       and the length could end up negative.
+    --
+    local ch = (ch or " ")
+    return s .. ch:rep(len - s:len())
+end
+
+
+local function setMiOSAttribute(name, value, device)
+    local setAttr = luup.attr_set
+
+    --
+    -- Only call the method if it exists in the MiOS Version we're currently running...
+    --
+    if (setAttr ~= nil) then
+        setAttr(name, value, device)
+    else
+        debug("setMiOSAttribute: Function luup.attr_set not present in MiOS unit")
+    end
+end
+
+
+local function setDSCPanelState(variable, data)
+    luup.variable_set(ELK_PANEL_SID, variable, data, g_alarmDevice)
+end
+
+
+local function setPartitionState(partition, variable, data)
+    local partitionDevice = g_childDevices.partitions[partition]
+
+    if (partitionDevice ~= nil) then
+        luup.variable_set(ALARM_PARTITION_SID, variable, data, partitionDevice)
+    else
+        debug(string.format("setPartitionState: Setting non-existant partition %s, variable %s, data %s",
+            partition, variable, (data or "N/A")))
+    end
+end
+
+
+--
+-- Decode the broadcast Label messages for Zones and Partitions
+-- TODO: For future use.
+--
+local function setPanelLabel(data)
+    local code, label = data:match("^(%d%d%d)(.*)$")
+
+    code = tonumber(code)
+    label = trimSpaces(label)
+
+    if (code &gt;= 1 and code &lt;= 64) then
+        debug("setPanelLabel: Zone Label " .. code .. ":" .. label)
+        local zoneDevice = g_childDevices.zones[code]
+
+        if (zoneDevice ~= nil) then
+            setMiOSAttribute("name", label, zoneDevice)
+        end
+    elseif (code == 65) then
+        debug("setPanelLabel Not set: Fire Alarm Label " .. code .. ":" .. label)
+    elseif (code == 66) then
+        debug("setPanelLabel Not set: Failed to Arm Label Label " .. code .. ":" .. label)
+    elseif (code == 67) then
+        debug("setPanelLabel Not set: Alarm when Armed Label " .. code .. ":" .. label)
+    elseif (code &gt;= 101 and code &lt;= 108) then
+        code = code - 100
+        debug("setPanelLabel: Partition Label " .. code .. ":" .. label)
+        local partitionDevice = g_childDevices.partitions[code]
+
+        if (partitionDevice ~= nil) then
+            setMiOSAttribute("name", label, partitionDevice)
+        end
+
+        --
+        -- Gross hack, apologies to my CS101 teachers.  I need a spot when I "know" that all the state
+        -- establishing calls (Zone Status, Partition Status) have "come through" during startup, so
+        -- we tack this on here to clear out the startup bit...
+        --
+        g_isStartupMode = false
+    elseif (code &gt;= 120 and code &lt;= 151) then
+        debug("setPanelLabel Not set: Command Output Label " .. code .. ":" .. label)
+    else
+        debug("setPanelLabel Not set: Unknown code " .. code .. ":" ..label)
+    end
+end
+
+local function setZoneState(zone, value)
+    local zonedev = g_childDevices.zones[zone]
+
+    --
+    -- This can happen during startup, for example, since it sends out ALL
+    -- potential Zones, whether you have them or not.
+    --
+    if (not zonedev) then
+        debug(string.format("Warning: No device for Zone %d", zone))
+        return nil, "No such Zone device"
+    end
+
+    if (value == "1") then
+        debug(string.format("Zone %d Open", zone))
+    else
+        debug(string.format("Zone %d Restored", zone))
+    end
+
+    luup.variable_set(SECURITY_SID, "Tripped", value, zonedev)
+end
+
+local PANEL_LED_STATUS = {
+    ["1"] = "Ready",
+    ["2"] = "Armed",
+    ["3"] = "Memory",
+    ["4"] = "Bypass",
+    ["5"] = "Trouble",
+    ["6"] = "Program",
+    ["7"] = "Fire",
+    ["8"] = "Backlight",
+    ["9"] = "AC"
+}
+
+local PANEL_LED_MODES = {
+    ["0"] = "OFF",
+    ["1"] = "ON",
+    ["2"] = "Flashing"
+}
+
+local PANEL_ERRORS = {
+    ["017"] = "Keybus Busy - Installer Mode",
+    ["021"] = "Requested Partition is out of Range",
+    ["023"] = "Partition is Not Armed",
+    ["024"] = "Partition is Not Ready to Arm",
+    ["026"] = "User Code Not Required",
+    ["028"] = "Virtual Keypad is Disabled",
+    ["029"] = "Not Valid Parameter",
+    ["030"] = "Keypad Does Not Come Out of Blank Mode",
+    ["031"] = "IT-100 is already in Thermostat menu",
+    ["032"] = "IT-100 is Not in Thermostat menu",
+    ["033"] = "No Response from Thermostat (or Escort Module)"
+}
+
+-- NOTE: IT100 Programmer's guide is incorrect here.  It shows these as being "3-digit" when
+--       they're really 1-digit.
+--
+local PARTITION_ARMED_MODES = {
+    ["0"] = "Away",
+    ["1"] = "Stay",
+    ["2"] = "Away, No Delay",
+    ["3"] = "Stay, No Delay",
+    ["4"] = "Night"
+}
+
+
+--[[
+local PANEL_EVENTS = {
+    ["501"] = {"panel", "Command Error",
+            function (cmd, zone)
+            debug("Panel::Command Error")
+        end},
+    ["502"] = {"panel", "System Error",
+            function (cmd, data)
+            debug(string.format("Panel::System Error (%s)",
+                                (PANEL_ERRORS[data] or "wtf")))
+        end},
+    ["570"] = {"panel", "Broadcast Labels",
+            function (cmd, data)
+            debug(string.format("Panel::Broadcast Labels (%s)", (data or "N/A")))
+            setPanelLabel(data)
+        end},
+    ["601"] = {"partition-zone", "Zone Alarm",
+            function (cmd, partition, zone)
+            debug(string.format("Zone::Zone Alarm, Partition %d, Zone %d", partition, zone))
+        end},
+    ["602"] = {"partition-zone", "Zone Alarm Restore",
+            function (cmd, partition, zone)
+            debug(string.format("Zone::Zone Alarm Restore, Partition %d, Zone %d", partition, zone))
+        end},
+    ["603"] = {"partition-zone", "Zone Tamper",
+            function (cmd, partition, zone)
+            debug(string.format("Partition-Zone::Tamper not implemented (%s, %s)", partition, zone))
+        end},
+    ["604"] = {"partition-zone", "Zone Tamper Restore",
+            function (cmd, partition, zone)
+            debug(string.format("Partition-Zone::Tamper Restore not implemented (%s, %s)", partition, zone))
+        end},
+    ["605"] = {"zone", "Zone Fault",
+            function (cmd, zone)
+            debug(string.format("Zone::Zone Fault not implemented (%s)", zone))
+        end},
+    ["606"] = {"zone", "Zone Fault Restore",
+            function (cmd, zone)
+            debug(string.format("Zone::Zone Fault Restore not implemented (%s)", zone))
+        end},
+    ["609"] = {"zone", "Zone %s Open",
+            function (cmd, zone)    -- zone open
+            setZoneState(zone, "1")
+        end},
+    ["610"] = {"zone", "Zone %s Restored",
+            function (cmd, zone)    -- zone restored
+            setZoneState(zone, "0")
+        end},
+    ["621"] = {"panel", "Fire Key Alarm Set",
+            function (cmd, data)    -- fire key alarm
+            setDSCPanelState("FireKeyAlarm", "1")
+            task("Fire Key Alarm Set", TASK_ERROR_PERM)
+        end},
+    ["622"] = {"panel", "Fire Key Alarm Restore",
+            function (cmd, data)    -- fire key alarm restored
+            setDSCPanelState("FireKeyAlarm", "0")
+            task("Fire Key Alarm Restore", TASK_SUCCESS)
+        end},
+    ["623"] = {"panel", "Aux Key Alarm Set",
+            function (cmd, data)    -- aux key alarm
+            setDSCPanelState("AuxKeyAlarm", "1")
+            task("Aux Key Alarm Set", TASK_ERROR_PERM)
+        end},
+    ["624"] = {"panel", "Aux Key Alarm Restore",
+            function (cmd, data)    -- aux key alarm restored
+            setDSCPanelState("AuxKeyAlarm", "0")
+            task("Aux Key Alarm Restore", TASK_SUCCESS)
+        end},
+    ["625"] = {"panel", "Panic Key Alarm Set",
+            function (cmd, data)    -- panic key alarm
+            setDSCPanelState("PanicKeyAlarm", "1")
+            task("Panic Key Alarm Set", TASK_ERROR_PERM)
+        end},
+    ["626"] = {"panel", "Panic Key Alarm Restore",
+            function (cmd, data)    -- panic key alarm restored
+            setDSCPanelState("PanicKeyAlarm", "0")
+            task("Panic Key Alarm Restore", TASK_SUCCESS)
+        end},
+    ["631"] = {"panel", "Aux Input Alarm Set",
+            function (cmd, data)    -- Aux input alarm
+            setDSCPanelState("AuxInputAlarm", "1")
+            task("Aux Input Alarm Set", TASK_ERROR_PERM)
+        end},
+    ["632"] = {"panel", "Aux Input Alarm Restore",
+            function (cmd, data)    -- Aux input alarm restored
+            setDSCPanelState("AuxInputAlarm", "0")
+            task("Aux Input Alarm Restore", TASK_SUCCESS)
+        end},
+    ["650"] = {"partition", "Ready",
+            function (cmd, partition, data)
+            debug("Partition::Ready " .. cmd .. " partition: " .. partition)
+
+            setPartitionState(partition, "ArmMode", "Disarmed")
+            setPartitionState(partition, "DetailedArmMode", "Ready")
+
+            --
+            -- This happens at a few points, but notably during startup, so we need to "clear" all
+            -- prior state.
+            --
+            if (g_isStartupMode) then
+                setPartitionState(partition, "ArmMode", "Disarmed")
+            end
+        end},
+    ["651"] = {"partition", "Not Ready",
+            function (cmd, partition, data)
+            debug("Partition::Not Ready " .. cmd .. " partition: " .. partition)
+
+            setPartitionState(partition, "ArmMode", "Disarmed")
+            setPartitionState(partition, "DetailedArmMode", "NotReady")
+
+            --
+            -- This happens at a few points, but notably during startup, so we need to "clear" all
+            -- prior state.
+            --
+            if (g_isStartupMode) then
+                setPartitionState(partition, "ArmMode", "Disarmed")
+            end
+        end},
+    ["652"] = {"partition", "Partition Armed - Descriptive Mode",
+            function (cmd, partition, data)
+            debug(string.format("Partition::Partition Armed - Descriptive Mode %s, Partition %d (%s)",
+                cmd, partition, (PARTITION_ARMED_MODES[data] or "N/A")))
+
+            setPartitionState(partition, "ArmMode", "Armed")
+
+            if (data=="0") then
+                setPartitionState(partition, "DetailedArmMode", "Armed")
+            elseif (data=="1") then
+                setPartitionState(partition, "DetailedArmMode", "Stay")
+            elseif (data=="2") then
+                setPartitionState(partition, "DetailedArmMode", "Vacation")
+            elseif (data=="3") then
+                setPartitionState(partition, "DetailedArmMode", "StayInstant")
+            elseif (data=="4") then
+                setPartitionState(partition, "DetailedArmMode", "Night")
+            else
+                log("Received invalid partition state " .. data)
+                setPartitionState(partition, "DetailedArmMode", "Armed")
+            end
+        end},
+    ["654"] = {"partition", "Alarm",
+            function (cmd, partition, data)
+            debug("Partition::Alarm " .. cmd .. " partition: " .. partition)
+
+            setPartitionState(partition, "Alarm", "Active")
+            setPartitionState(partition, "AlarmMemory", "1")
+            setPartitionState(partition, "LastAlarmActive", os.date())
+
+            if (g_isStartupMode) then
+                setPartitionState(partition, "ArmMode", "Armed")
+                setPartitionState(partition, "DetailedArmMode", "Armed")
+            end
+        end},
+    ["655"] = {"partition", "Disarmed",
+            function (cmd, partition, data)
+            debug("Partition::Disarmed " .. cmd .. " partition: " .. partition)
+
+            setPartitionState(partition, "ArmMode", "Disarmed")
+            setPartitionState(partition, "DetailedArmMode", "Disarmed")
+            setPartitionState(partition, "Alarm", "None")
+            setPartitionState(partition, "AlarmMemory", "0")
+            setPartitionState(partition, "LastAlarmActive", "0")
+        end},
+    ["656"] = {"partition", "Exit Delay in Progress",
+            function (cmd, partition, data)
+            debug("Partition::Exit Delay in Progress " .. cmd .. " partition: " .. partition)
+
+            setPartitionState(partition, "ArmMode", "Disarmed")
+            setPartitionState(partition, "DetailedArmMode", "ExitDelay")
+
+            if (g_isStartupMode) then
+                setPartitionState(partition, "Alarm", "None")
+            end
+        end},
+    ["657"] = {"partition", "Entry Delay in Progress",
+            function (cmd, partition, data)
+            debug("Partition::Entry Delay in Progress " .. cmd .. " partition: " .. partition)
+
+            setPartitionState(partition, "ArmMode", "Armed")
+            setPartitionState(partition, "DetailedArmMode", "EntryDelay")
+
+            if (g_isStartupMode) then
+                setPartitionState(partition, "Alarm", "None")
+            end
+        end},
+    ["658"] = {"partition", "Keypad Lockout",
+            function (cmd, partition, data)
+            debug("Partition::Keypad Lockout " .. cmd .. " partition: " .. partition)
+        end},
+    ["659"] = {"partition", "Keypad Blanking",
+            function (cmd, partition, data)
+            debug("Partition::Keypad Blanking " .. cmd .. " partition: " .. partition)
+        end},
+    ["660"] = {"partition", "Command Output In Progress",
+            function (cmd, partition, data)
+            debug("Partition::Command Output In Progress " .. cmd .. " partition: " .. partition)
+        end},
+    ["670"] = {"partition", "Invalid Access Code",
+            function (cmd, partition, data)
+            debug("Partition::Invalid Access Code " .. cmd .. " partition: " .. partition)
+        end},
+    ["671"] = {"partition", "Function Not Available",
+            function (cmd, partition, data)
+            debug("Partition::Function Not Available " .. cmd .. " partition: " .. partition)
+        end},
+    ["672"] = {"partition", "Failed To Arm",
+            function (cmd, partition, data)
+            debug("Partition::Failed To Arm " .. cmd .. " partition: " .. partition)
+
+            setPartitionState(partition, "ArmMode", "Disarmed")
+            setPartitionState(partition, "DetailedArmMode", "FailedToArm")
+
+            if (g_isStartupMode) then
+                setPartitionState(partition, "Alarm", "None")
+            end
+
+            task("Failed to Arm Partition", TASK_ERROR)
+        end},
+    ["673"] = {"partition", "Partition Busy",
+            function (cmd, partition, data)
+            debug("Partition::Partition Busy " .. cmd .. " partition: " .. partition)
+        end},
+    ["700"] = {"partition", "User Closing",
+            function (cmd, partition, data)
+            debug("Partition::User Closing " .. cmd .. " partition: " .. partition)
+        end},
+    ["701"] = {"partition", "Special Closing",
+            function (cmd, partition, data)
+            debug("Partition::Special Closing " .. cmd .. " partition: " .. partition)
+        end},
+    ["702"] = {"partition", "Partial Closing",
+            function (cmd, partition, data)
+            debug("Partition::Partial Closing " .. cmd .. " partition: " .. partition)
+        end},
+    ["750"] = {"partition", "User Opening",
+            function (cmd, partition, data)
+            debug("Partition::User Opening " .. cmd .. " partition: " .. partition)
+        end},
+    ["751"] = {"partition", "Special Opening",
+            function (cmd, partition, data)
+            debug("Partition::Special Opening " .. cmd .. " partition: " .. partition)
+        end},
+    ["800"] = {"panel", "Panel Battery Trouble",
+            function (cmd, data)
+            debug("Panel::Panel Battery Trouble " .. cmd)
+            task("Panel Battery Trouble", TASK_ERROR_PERM)
+        end},
+    ["801"] = {"panel", "Panel Battery Restore",
+            function (cmd, data)
+            debug("Panel::Panel Battery Restore " .. cmd)
+            task("Panel Battery Restore", TASK_SUCCESS)
+        end},
+    ["802"] = {"panel", "Panel AC Trouble",
+            function (cmd, data)
+            debug("Panel::Panel AC Trouble " .. cmd)
+            task("Panel AC Trouble", TASK_ERROR_PERM)
+        end},
+    ["803"] = {"panel", "Panel AC Restore",
+            function (cmd, data)
+            debug("Panel::Panel AC Restore " .. cmd)
+            task("Panel AC Restore", TASK_SUCCESS)
+        end},
+    ["806"] = {"panel", "System Bell Trouble",
+            function (cmd, data)
+            debug("Panel::System Bell Trouble " .. cmd)
+            task("System Bell Trouble", TASK_ERROR_PERM)
+        end},
+    ["807"] = {"panel", "System Bell Restore",
+            function (cmd, data)
+            debug("System Bell Restore " .. cmd)
+            task("System Bell Restore", TASK_SUCCESS)
+        end},
+    ["829"] = {"panel", "General System Tamper",
+            function (cmd, data)
+            debug("Panel::General System Tamper " .. cmd)
+            task("System General System Tamper", TASK_ERROR_PERM)
+        end},
+    ["830"] = {"panel", "General System Tamper Restore",
+            function (cmd, data)
+            debug("General System Tamper Restore" .. cmd)
+            task("General System Tamper Restore", TASK_SUCCESS)
+        end},
+    ["840"] = {"partition", "Trouble Status (LED ON)",
+            function (cmd, partition, data)
+            debug("Partition::Trouble Status (LED ON) " .. cmd .. " partition: " .. partition)
+        end},
+    ["841"] = {"partition", "Trouble Status (LED OFF)",
+            function (cmd, partition, data)
+            debug("Partition::Trouble Status (LED OFF) " .. cmd .. " partition: " .. partition)
+        end},
+    ["842"] = {"panel", "Fire Trouble Alarm",
+            function (cmd, data)
+            debug("Panel::Fire Trouble Alarm " .. cmd)
+            task("Fire Trouble Alarm", TASK_ERROR_PERM)
+        end},
+    ["843"] = {"panel", "Fire Trouble Alarm Restored",
+            function (cmd, data)
+            debug("Fire Trouble Alarm Restored " .. cmd)
+            task("Fire Trouble Alarm Restored", TASK_SUCCESS)
+        end},
+    ["896"] = {"panel", "Keybus Fault",
+            function (cmd, data)
+            debug("Panel::Keybus Fault " .. cmd)
+            task("Keybus Fault", TASK_ERROR_PERM)
+        end},
+    ["897"] = {"panel", "Keybus Fault Restored",
+            function (cmd, data)
+            debug("Panel::Keybus Fault Restored " .. cmd)
+            task("Keybus Fault Restored", TASK_SUCCESS)
+        end},
+    ["900"] = {"partition", "Code Required",
+            function (cmd, partition, data)
+            debug("Partition::Code Required " .. cmd .. " partition: " .. partition)
+        end},
+    ["901"] = {"panel", "LCD Update",
+            function (cmd, data)    -- LCD Update
+            -- Strip out the first 6 characters (formatting/layout)
+            debug("Panel::LCD Update " .. cmd .. data)
+            setDSCPanelState("LCD", trimSpaces(data:sub(6)))
+        end},
+    ["903"] = {"panel", "LED Status",
+            function (cmd, data)
+            debug(string.format("Panel::LED Status %s (%s)",
+                                (PANEL_LED_STATUS[data:sub(1,1)] or "wtf"),
+                                 (PANEL_LED_MODES[data:sub(2,2)] or "wtf")))
+        end},
+    ["904"] = {"panel", "Beep Status",
+            function (cmd, data)
+            debug("Panel::Beep Status " .. cmd .. " data: " .. data)
+        end},
+    ["905"] = {"panel", "Tone Status",
+            function (cmd, data)
+            debug("Panel::Tone Status " .. cmd .. " data: " .. data)
+        end},
+    ["906"] = {"panel", "Buzzer Status",
+            function (cmd, data)
+            debug("Panel::Buzzer Status " .. cmd .. " data: " .. data)
+        end},
+    ["908"] = {"panel", "Software Version",
+            function (cmd, data)
+            debug("Panel::Software Version " .. cmd .. " data: " .. data)
+            setDSCPanelState("FirmwareVersion", data)
+        end}
+}]]
+
+local ZONE_DEFINITIONS = {
+    ['0'] = {"Disabled"},
+    ['1'] = {"Burglar Entry/Exit 1"},
+    ['2'] = {"Burglar Entry/Exit 2"},
+    ['3'] = {"Burglar Perimeter Instant"},
+    ['4'] = {"Burglar Interior"},
+    ['5'] = {"Burglar Interior Follower"},
+    ['6'] = {"Burglar Interior Night"},
+    ['7'] = {"Burglar Interior Night Delay"},
+    ['8'] = {"Burglar 24 Hour"},
+    ['9'] = {"Burglar Box Tamper"},
+    [':'] = {"Fire Alarm"},
+    [';'] = {"Fire Verified"},
+    ['<'] = {"Fire Supervisory"},
+    ['='] = {"Aux Alarm 1"},
+    ['>'] = {"Aux Alarm 2"},
+    ['?'] = {"Keyfob"},
+    ['@'] = {"Non Alarm"},
+    ['A'] = {"Carbon Monoxide"},
+    ['B'] = {"Emergency Alarm"},
+    ['C'] = {"Freeze Alarm"},
+    ['D'] = {"Gas Alarm"},
+    ['E'] = {"Heat Alarm"},
+    ['F'] = {"Medical Alarm"},
+    ['G'] = {"Police Alarm"},
+    ['H'] = {"Police No Indication"},
+    ['I'] = {"Water Alarm"},
+    ['J'] = {"Key Momentary Arm / Disarm"},
+    ['K'] = {"Key Momentary Arm Away"},
+    ['L'] = {"Key Momentary Arm Stay"},
+    ['M'] = {"Key Momentary Disarm"},
+    ['N'] = {"Key On/Off"},
+    ['O'] = {"Mute Audibles"},
+    ['P'] = {"Power Supervisory"},
+    ['Q'] = {"Temperature"},
+    ['R'] = {"Analog Zone"},
+    ['S'] = {"Phone Key"},
+    ['T'] = {"Intercom Key"}
+}
+
+
+local ZONE_STATUSES = {
+    ['0'] = {"Normal", "Unconfigured"},
+    ['1'] = {"Normal", "Open"},
+    ['2'] = {"Normal", "EOL"},
+    ['3'] = {"Normal", "Short"},
+    ['4'] = nil,
+    ['5'] = {"Trouble", "Open"},
+    ['6'] = {"Trouble", "EOL"},
+    ['7'] = {"Trouble", "Short"},
+    ['8'] = nil,
+    ['9'] = {"Violated", "Open"},
+    ['A'] = {"Violated", "EOL"},
+    ['B'] = {"Violated", "Short"},
+    ['C'] = {"Bypassed", nil},
+    ['D'] = {"Bypassed", "Open"},
+    ['E'] = {"Bypassed", "EOL"},
+    ['F'] = {"Bypassed", "Short"}
+}
+
+
+local function getZoneDefinition (data)
+    for i = 1, #data do
+        c = data:sub (i, i)
+        if (c ~= '0') then
+            g_childDevices.zones[i] = {description = ZONE_DEFINITIONS[c]}
+        end
+    end
+end
+
+
+local function getZonePartitionReport (data)
+    for i = 1, #data do
+        c = data:sub (i, i)
+        if (g_childDevices.zones[i]) then
+            if (c ~= '0')
+                g_childDevices.zones[i].partition = c;
+                if (not table.contains (g_childDevices.partitions, c)) then
+                    table.insert (g_childDevices.partitions, c)
+                end
+            else
+                g_childDevices.zones[i].partition = "no partition";
+            end
+        end
+    end
+    table.sort (g_childDevices.partitions)
+end
+
+
+local function getZoneStatusReport (data)
+    for i = 1, #data do
+        c = data:sub (i, i)
+        if (g_childDevices.zones[i]) then
+            g_childDevices.zones[i].status = ZONE_STATUSES[c][1]
+        end
+    end
+end
+
+
+local PANEL_RESPONSES = {
+    ["ZD"] = {
+        "Zone Definition Data",
+        function (data)
+            debug ("Message type description: Zone Definition Data")
+            getZoneDefinition (data)
+        end
+    },
+    ["ZP"] = {
+        "Zone Partition Report",
+        function (data)
+            debug ("Message type description: Zone Partion Report")
+            getZonePartitionReport (data)
+        end
+    },
+    ["ZS"] = {
+        "Zone Status Report",
+        function (data)
+            debug ("Message type description: Zone Status Report")
+            getZoneStatusReport (data)
+        end
+    }
+}
+
+
+local function sendCommand (command, data)
+    data = data or ""
+    
+    local length = 2 + #data + 2 + 2 -- command length + data length + 00 length + checksum length
+    local checksum = calculateChecksum (string.format ("%0.2X%s%s00", length, command, data))
+    local cmd = string.format ("%0.2X%s%s00%0.2X", length, command, data, checksum)
+    if (luup.io.write (cmd) == false) then
+        local message = string.format ("ERROR: Cannot send command: '%s'", cmd)
+        log (message)
+        luup.set_failure (true)
+
+        return false, message
+    end
+
+    return true, "Ok"
+end
+
+
+-- Get and return the variable. If the variable is 'nil' it is created and
+-- initialized to the given default value.
+local function presetVariable (serviceId, variable, device, defaultValue)
+    local v = luup.variable_get (serviceId, variable, device)
+    if (v == nil) then
+        luup.variable_set (serviceId, variable, (defaultValue or ""), device)
+    end
+
+    return v
+end
+
+
+local function presetParameters (t)
+    local params = {}
+    for _, param in ipairs (t) do
+        table.insert (params, string.format ("%s,%s=%s", unpack (param)))
+    end
+
+    return table.concat (params, "\n")
+end
+
+
+local function appendChildDevices (mainDevice, rootptr, partitions, id, description, deviceType, deviceFilename, parameters)
+    for item in partitions:gmatch ("[^,]+") do
+        item = trimSpaces (item)
+        if (item and item ~= "") then
+            debug (string.format ("Appending Child: %s", item))
+            luup.chdev.append (mainDevice, rootptr, id:format (item), description:format (item), deviceType, deviceFilename, nil, (parameters or ""), false)
+        else
+            debug ("Data formatting problem for CSV: " .. partitions)
+        end
+    end
+end
+
+
+local function initializeParameters (device)
+    local enableRemoteArming = luup.variable_get (ELK_PANEL_SID, "EnableRemoteArm", device)
+    if (enableRemoteArming == nil) then
+        g_remoteArmEnabled = false
+        g_remoteDisarmEnabled = false
+        luup.variable_set (ELK_PANEL_SID, "EnableRemoteArm", "false", device)
+    elseif (enableRemoteArming  == "false") then
+        g_remoteArmEnabled = false
+        g_remoteDisarmEnabled = false
+    elseif (enableRemoteArming == "arm") then
+        g_remoteArmEnabled = true
+        g_remoteDisarmEnabled = false
+    elseif (enableRemoteArming == "disarm") then
+        g_remoteArmEnabled = true
+        g_remoteDisarmEnabled = true
+    else
+        return false, "EnableRemoteArm parameter must be either blank, false or a value from the wiki...", "ElkAlarmPanel"
+    end
+
+    local enableRemotePanic = luup.variable_get (ELK_PANEL_SID, "EnableRemotePanic", device)
+    if (enableRemotePanic == nil) then
+        g_remotePanicEnabled = false
+        luup.variable_set (ELK_PANEL_SID, "EnableRemotePanic", "false", device)
+    elseif (enableRemotePanic == "false") then
+        g_remotePanicEnabled = false
+    elseif (enableRemotePanic == "panic") then
+        g_remotePanicEnabled = true
+    else
+        return false, "EnableRemotePanic parameter must be either blank, false or a value from the wiki..."
+    end
+
+    return true, "Ok"
+end
+
+
+local function initializeLabelsAndAlarmState (device)
+    local status, message
+
+    -- Trigger the Panel to emit its State information for the Panel, Partitions and Zones.
+    status, message = sendCommand ("001")
+    if (status) then
+        -- Trigger the Panel to emit it's labels for all Partitions and Zones.
+        status, message = sendCommand("002")
+    end
+
+    return status, message
+end
+
+function sendPartitionCommand(cmd, device, ...)
+    local partition = extractPartition(luup.devices[device].id)
+    assert(partition ~= nil)
+
+    sendCommand(cmd, partition, ...)
+end
+
+----------------------------------------------------------------------
+
+function calculateChecksum (s)
+    local total = 0
+    for i = 1, #s do
+        total = total + string.byte (s, i)
+    end
+
+    return 256 - (total % 256)
+end
+
+
+function processEvent (msgType, data)
+    local response = PANEL_RESPONSES[msgType]
+
+    if (response == nil) then
+        debug ("Unhandled event " .. msgType)
+        return
+    end
+
+    local eventDescription, eventFunction = response[1], response[2]
+
+    if (type (eventFunction) ~= "function") then
+        log ("ERROR: Unknown event, or event handled incorrectly.")
+        return
+    end
+
+    --[[if (eventType == "zone") then
+        return eventFunction(cmd, tonumber(data))
+    elseif (eventType == "panel") then
+        return eventFunction(cmd, data)
+    elseif (eventType == "partition") then
+        local partition, data = data:match("^([1-8])(.*)$")
+        partition = tonumber(partition)
+
+        setPartitionState(partition, "VendorStatusCode", cmd)
+        setPartitionState(partition, "VendorStatus", eventDescription)
+
+        return eventFunction(cmd, partition, data)
+    elseif (eventType == "partition-zone") then
+        local partition, zone = data:match("^(%d)(%d%d%d)")
+        return eventFunction(cmd, tonumber(partition), tonumber(zone))
+    else
+        debug("wtf? :" .. cmd .. "(" .. eventType .. ")")
+    end]]
+end
+
+
+-- If the input line is a response to a pending command, resumes it.
+-- If it's an error, handles it.
+-- If it's an event, calls handler.
+--local pending = nil
+function processIncoming (s)
+    local length, msgType, data, checksum = s:match ("^(%x%x)(%a%w)(.*)00(%x%x)$")
+    debug (string.format ("processIncoming: length=%s, message type=%s, data='%s', checksum=%s", length, msgType, data, checksum))
+    
+    --
+    -- Validate received message.
+    --
+    local calculatedLength = #msgType + #data + 2 + #checksum
+    if (calculatedLength ~= tonumber (length, 16)) then
+        log ("Data transmission error: the received message length is incorrect.")
+        task ("Data transmission error.", TASK_ERROR)
+        return
+    end
+    local calculatedChecksum = calculateChecksum (length .. msgType .. data .. "00")
+    if (calculatedChecksum ~= tonumber (checksum, 16)) then
+        log ("Data transmission error: the calculated checksum is incorrect.")
+        task ("Data transmission error.", TASK_ERROR)
+        return
+    end
+    --[[if (msgType == "500" or cmd == "900") then
+        if (type(pending)=="function") then
+            debug("continue with pending callback")
+            return pending(cmd, data)
+        end
+    else
+        return processEvent(cmd, data)
+    end]]
+    return processEvent (msgType, data)
+end
+
+function commandCallback(cmd, data, cb)
+    pending = nil
+
+    local status, message = sendCommand(cmd, data)
+    if (status) then
+        debug(string.format("sent (%s,%s), setup callback (%s)", cmd, data, tostring(cb)))
+        pending = cb
+    end
+end
+
+function command(cmd, data, cb)
+    return commandCallback(cmd, data, function(rcmd, rdata)
+        if (rcmd == "500" and rdata == cmd) then
+            return cb and cb(rcmd, rdata) or true
+        end
+    end)
+end
+
+function commandAccess(cmd, data, accesscode, cb)
+    return commandCallback(cmd, data, function(rcmd, rdata)
+        if (rcmd == "500" and rdata == cmd) then
+            return cb and cb(rcmd, rdata) or true
+        elseif (rcmd == "900") then
+            return commandCallback("200", accesscode, function(rcmd, rdata)
+                if (rcmd == "500" and rdata == cmd) then
+                    return cb and cb(rcmd, rdata) or true
+                end
+            end)
+        end
+    end)
+end
+
+--------------------------------------------------------------------------------------------------------------
+
+local function extractPartition (id)
+    local partition = id:match ("^elk_partition_(%d+)$")
+    debug ("extractPartition: Partition " .. (partition or "N/A"))
+
+    return partition
+end
+
+
+local function extractZone (id)
+    local zone = id:match ("^elk_zone_(%d+)$")
+    debug("extractZone:Zone " .. (zone or "nil"))
+
+    return zone
+end
+
+
+local function initializeChildren()
+    g_childDevices.partitions = {}
+    
+    for k, v in pairs (luup.devices) do
+        if (v.device_num_parent == g_alarmDevice) then
+            debug (string.format ("Scanning luup.devices[%s(%s)]", tostring (k), type (k)))
+            local zone = extractZone (v.id)
+            if (zone ~= nil) then
+                g_childDevices.zones[tonumber (zone)].device = k
+            else
+                local partition = extractPartition (v.id)
+                if (partition ~= nil) then
+                    g_childDevices.partitions[tonumber (partition)] = k
+                else
+                    debug (string.format ("Found unexpected child: device #%d, id=%s", k, v.id))
+                end
+            end
+        end
+    end
+
+    return true, "Ok"
+end
+
+
+local function getZoneParams (zoneNo)
+    local armed
+    if ((not g_childDevices.zones[zoneNo].status) or (g_childDevices.zones[zoneNo].status == "Bypassed")) then
+        armed = "0"
+    else
+        armed = "1"
+    end
+    
+    return presetParameters ({{SECURITY_SID, "Armed", armed}})
+end
+
+
+local function getDeviceInfo (zoneNo)
+    -- TODO: the actual implementation
+    return {type = "urn:schemas-micasaverde-com:device:MotionSensor:1", file = "D_MotionSensor1.xml"}
+end
+
+
+local function appendZones (rootPtr)
+    for k, v in pairs (g_childDevices.zones) do
+        debug (string.format ("Appending Zone %d", k))
+        local device = getDeviceInfo (k)
+        luup.chdev.append (g_alarmDevice, rootPtr, string.format ("elk_zone_%d", k), string.format ("Zone %d: %s", k, v.description), device.type, device.file, nil, getZoneParams (k), false)
+    end
+end
+
+
+local function appendPartitions (rootPtr)
+    for i = 1, #g_childDevices.partitions do
+        local p = g_childDevices.partitions[i]
+        debug (string.format ("Appending Partition %c", p))
+        luup.chdev.append (g_alarmDevice, rootPtr, string.format ("elk_partition_%c", p), string.format ("Partition %c", p), "urn:schemas-micasaverde-com:device:ElkAlarmPartition:1", "D_ElkAlarmPartition1.xml")
+    end
+end
+
+
+local function getZonesAndPartitions()
+    -- Get zone information.
+    local status, message = sendCommand ("zd")
+    if (status) then
+        -- Get partition information.
+        status, message = sendCommand ("zp")
+    end
+    
+    return status, message
+end
+
+
+function alarmStartup (lul_device)
+    log ("Elk M1 Alarm Panel Plugin ver. " .. VERSION .." (ID = " .. tostring (lul_device) .. ")")
+    
+    g_alarmDevice = lul_device
+    
+    local rootPtr = luup.chdev.start (lul_device)
+
+    -- Get all the zones and active partitions.
+    local status, message = getZonesAndPartitions()
+    if (not status) then
+        return status, message, "Elk Alarm Panel"
+    end
+    
+    -- Create a device for each active partition.
+    appendPartitions (rootPtr)
+    
+    -- Get each Zone's status.
+    local status, message = sendCommand("zs")
+    
+    -- Create a device for each zone.
+    appendZones (rootPtr)
+    
+    luup.chdev.sync (lul_device, rootPtr)
+
+    local status, message = initializeParameters (lul_device)
+    if (status) then
+        status, message = initializeChildren (lul_device)
+        if (status) then
+            status, message = initializeLabelsAndAlarmState (lul_device)
+        end
+    end
+
+    return status, message, "Elk Alarm Panel"
+end
Index: /S_ElkAlarmPanel1.xml
===================================================================
--- /S_ElkAlarmPanel1.xml	(revision 3)
+++ /S_ElkAlarmPanel1.xml	(revision 3)
@@ -0,0 +1,80 @@
+<?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>ActivePartitions</name>
+      <datatype>string</datatype>
+    </stateVariable>
+ 
+    <stateVariable sendEvents="no">
+      <name>DoorZones</name>
+      <datatype>string</datatype>
+    </stateVariable>
+    <stateVariable sendEvents="no">
+      <name>MotionZones</name>
+      <datatype>string</datatype>
+    </stateVariable>
+    <stateVariable sendEvents="no">
+      <name>SmokeZones</name>
+      <datatype>string</datatype>
+    </stateVariable>
+ 
+    <stateVariable sendEvents="no">
+      <name>LCD</name>
+      <datatype>string</datatype>
+    </stateVariable>
+
+    <stateVariable sendEvents="yes">
+      <name>FireKeyAlarm</name>
+      <datatype>boolean</datatype>
+      <defaultValue>0</defaultValue>
+    </stateVariable>
+    <stateVariable sendEvents="yes">
+      <name>AuxKeyAlarm</name>
+      <datatype>boolean</datatype>
+      <defaultValue>0</defaultValue>
+    </stateVariable>
+    <stateVariable sendEvents="yes">
+      <name>PanicKeyAlarm</name>
+      <datatype>boolean</datatype>
+      <defaultValue>0</defaultValue>
+    </stateVariable>
+    <stateVariable sendEvents="yes">
+      <name>AuxInputAlarm</name>
+      <datatype>boolean</datatype>
+      <defaultValue>0</defaultValue>
+    </stateVariable>
+    <stateVariable sendEvents="yes">
+      <name>FirmwareVersion</name>
+      <datatype>string</datatype>
+    </stateVariable>
+  </serviceStateTable>
+  <actionList>
+    <action>
+      <name>SyncTime</name>
+      <argumentList>
+        <argument>
+          <name>PINCode</name>
+          <direction>in</direction>
+        </argument>
+      </argumentList>
+    </action>
+    <action>
+      <name>SendCommand</name>
+      <argumentList>
+        <argument>
+          <name>Command</name>
+          <direction>in</direction>
+        </argument>
+        <argument>
+          <name>Data</name>
+          <direction>in</direction>
+        </argument>
+      </argumentList>
+    </action>
+  </actionList>
+</scpd>
Index: /D_ElkAlarmPanel1.xml
===================================================================
--- /D_ElkAlarmPanel1.xml	(revision 3)
+++ /D_ElkAlarmPanel1.xml	(revision 3)
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+  <specVersion>
+    <major>1</major>
+    <minor>0</minor>
+  </specVersion>
+  <device>
+    <deviceType>urn:schemas-micasaverde-com:device:AlarmPanel:1</deviceType>
+    <staticJson>D_ElkAlarmPanel.json</staticJson>
+    <friendlyName>Elk Alarm Panel</friendlyName>
+    <manufacturer>Elk</manufacturer>
+    <protocol>crlf</protocol>
+    <handleChildren>1</handleChildren>
+    <commUse>rs232</commUse>
+    <serviceList>
+      <service>
+        <serviceType>urn:schemas-micasaverde-com:service:ElkAlarmPanel:1</serviceType>
+        <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPanel1</serviceId>
+        <SCPDURL>S_ElkAlarmPanel1.xml</SCPDURL>
+      </service>
+    </serviceList>
+    <implementationList>
+      <implementationFile>I_ElkAlarmPanel1.xml</implementationFile>
+    </implementationList>
+    <eventList> {
+        "event_1":{
+            "label":"Fire Key Alarm Triggered",
+            "serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPanel1",
+            "argumentList": {
+                "argument_1": {
+                    "dataType": "boolean",
+                    "defaultValue": "1",
+                    "allowedValueList": {
+                        "Triggered": "1",
+                        "Cleared": "0"
+                    },
+                    "name": "FireKeyAlarm",
+                    "comparisson": "=",
+                    "prefix": "(",
+                    "suffix": ")"
+                }
+            }
+        }
+    } </eventList>
+  </device>
+</root>
Index: /D_ElkAlarmPanel1.json
===================================================================
--- /D_ElkAlarmPanel1.json	(revision 3)
+++ /D_ElkAlarmPanel1.json	(revision 3)
@@ -0,0 +1,198 @@
+{
+    "flashicon": "icons\/generic_sensor.swf",
+    "imgIconBody": "",
+    "imgIconDimmable": "",
+    "imgIconTurnable": "",
+    "imgIconMin": "",
+    "imgIconMax": "",
+    "halloIconsDir": "pics\/hallo",
+    "DisplayStatus": {
+        
+    },
+    "doc_url":{
+        "doc_forced_url":{
+            "text":"http://wiki.micasaverde.com/index.php/DscAlarmPanel"
+        }
+    },
+    "Tabs": [
+        {
+            "Label": {
+                "lang_tag": "Elk_alarm_panel",
+                "text": "Elk Alarm Panel"
+            },
+            "Position": "0",
+            "TabType": "flash",
+            "Control": [
+                {
+                    "ControlType": "variable",
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:ElkAlarmPanel1",
+                        "Variable": "LCD",
+                        "Top": 60,
+                        "Left": 10,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlGroup": "2",
+                    "ControlHeader": "1",
+                    "ControlType": "label",
+                    "Label": {
+                        "lang_tag": "device_settings_page",
+                        "text": "PIN Options"
+                    },
+                    "Display": {
+                        "Top": 100,
+                        "Left": 50,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlGroup": "2",
+                    "ControlPair": "1",
+                    "ControlType": "input",
+                    "ID": "PINCode",
+                    "Display": {
+                        "Top": 120,
+                        "Left": 50,
+                        "Width": 100,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlGroup": "2",
+                    "ControlPair": "1",
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_set",
+                        "text": "Set Time"
+                    },
+                    "Display": {
+                        "Top": 120,
+                        "Left": 160,
+                        "Width": 100,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:DSCAlarmPanel1",
+                        "Action": "SyncTime",
+                        "Parameters": [
+                            {
+                                "Name": "PINCode",
+                                "ID": "PINCode"
+                            }
+                        ]
+                    }
+                }
+            ]
+        },
+        {
+            "Label": {
+                "lang_tag": "notifications",
+                "text": "Notifications" 
+            },
+            "Position": "5",
+            "TabType": "javascript",
+            "ScriptName": "shared.js",
+            "Function": "device_notifications" 
+        },
+        {
+            "Label": {
+                "lang_tag": "settings",
+                "text": "Settings" 
+            },
+            "Position": "1",
+            "TabType": "javascript",
+            "ScriptName": "shared.js",
+            "Function": "simple_device" 
+        },
+        {
+            "Label": {
+                "lang_tag": "advanced",
+                "text": "Advanced" 
+            },
+            "Position": "2",
+            "TabType": "javascript",
+            "ScriptName": "shared.js",
+            "Function": "advanced_device" 
+        } 
+    ],
+    "eventList": {
+        "event_1": {
+            "label":"Fire Key Alarm",
+            "serviceId": "urn:micasaverde-com:serviceId:DSCAlarmPanel1",
+            "argumentList": {
+                "argument_1": {
+                    "dataType": "boolean",
+                    "defaultValue": "1",
+                    "allowedValueList": {
+                        "Cleared": "0",
+                        "Triggered": "1"
+                    },
+                    "name": "FireKeyAlarm",
+                    "comparisson": "=",
+                    "prefix": "When Fire Alarm is",
+                    "suffix": ""
+                }
+            }
+        },
+        "event_2": {
+            "label":"Auxiliary Key Alarm",
+            "serviceId": "urn:micasaverde-com:serviceId:DSCAlarmPanel1",
+            "argumentList": {
+                "argument_1": {
+                    "dataType": "boolean",
+                    "defaultValue": "1",
+                    "allowedValueList": {
+                        "Cleared": "0",
+                        "Triggered": "1"
+                    },
+                    "name": "AuxKeyAlarm",
+                    "comparisson": "=",
+                    "prefix": "When Alarm is",
+                    "suffix": ""
+                }
+            }
+        },
+        "event_3": {
+            "label":"Panic Key Alarm",
+            "serviceId": "urn:micasaverde-com:serviceId:DSCAlarmPanel1",
+            "argumentList": {
+                "argument_1": {
+                    "dataType": "boolean",
+                    "defaultValue": "1",
+                    "allowedValueList": {
+                        "Cleared": "0",
+                        "Triggered": "1"
+                    },
+                    "name": "PanicKeyAlarm",
+                    "comparisson": "=",
+                    "prefix": "When Panic Key is",
+                    "suffix": ""
+                }
+            }
+        },
+        "event_4": {
+            "label":"Auxiliary Input Alarm",
+            "serviceId": "urn:micasaverde-com:serviceId:DSCAlarmPanel1",
+            "argumentList": {
+                "argument_1": {
+                    "dataType": "boolean",
+                    "defaultValue": "1",
+                    "allowedValueList": {
+                        "Cleared": "0",
+                        "Triggered": "1"
+                    },
+                    "name": "AuxInputAlarm",
+                    "comparisson": "=",
+                    "prefix": "When Auxliary Input is",
+                    "suffix": ""
+                }
+            }
+        }
+    },
+    "DeviceType": "urn:schemas-micasaverde-com:device:DSCAlarmPanel:1",
+    "device_type": "urn:schemas-micasaverde-com:device:DSCAlarmPanel:1" 
+}
Index: /S_AlarmPartition2.xml
===================================================================
--- /S_AlarmPartition2.xml	(revision 3)
+++ /S_AlarmPartition2.xml	(revision 3)
@@ -0,0 +1,343 @@
+<?xml version="1.0"?>
+<scpd xmlns="urn:schemas-upnp-org:service-1-0">
+  <specVersion>
+    <major>2</major>
+    <minor>0</minor>
+  </specVersion>
+  <serviceStateTable>
+    <!-- The High Level State of the Partition, representing whether it's Armed or Disarmed.
+
+         Partition implementations are only required to implement the "Armed" and "Disarmed" states
+         since not every Panel provides the ability to "see" the other statuses and reflect
+         them to the UPnP consumer.
+
+         Disarmed - The Partition is in an un-armed state, and ready to be Armed.
+         Armed - The Partition is currently armed, or in an "away" state.
+    -->
+    <stateVariable sendEvents="yes">
+      <name>ArmMode</name>
+      <dataType>string</dataType>
+      <shortCode>armmode</shortCode>
+      <defaultValue>Disarmed</defaultValue>
+      <allowedValueList>
+        <allowedValue>Disarmed</allowedValue>
+        <allowedValue>Armed</allowedValue>
+      </allowedValueList>
+    </stateVariable>
+
+
+    <!-- The Detailed state of the Partition, representing whether it's Armed, Disarmed or one
+         of the common intermediary states.
+
+         Partition implementations are required to implement the "Armed" and "Disarmed" states,
+         corresponding to the State variable above.  Not every Panel provides the required API
+         to "see" the other statuses and reflect them to the UPnP consumer.
+
+         Note that some implementations may consider the "ExitDelay" state to be "Armed"
+         whilst others may consider them to be "Disarmed".  No association between DetailedArmMode
+         and State variables should be assumed by UPnP clients, due to this potential difference.
+
+         Some of the states may be transitional, and no guarantees are provided that the Panel
+         will complete any subsequent transition to a corresponding permanent state.  
+         UPnP clients should make no assumptions about the "next state" from what is
+         immediately presented, since this may be Vendor and/or Implementation specific.
+
+         Some of the states, like Night/NightInstant and Vacation, are included for specific Panel
+         vendors like the Elk and may not be present on other Panels.  Control Point authors should
+         be prepared to display these DetailedArmMode using an CP-specific visualization (an Icon,
+         Translated Text and so-on)
+
+         Disarmed - The Partition is disarmed.
+         Armed - The Partition is currently armed, or in an "away" state.
+         Stay - The Partition is in an "at-home" state, delays are permitted on Entry doors.
+         StayInstant - The Partition is in an "at-home" state, Entry doors trigger Alarm immediately.
+         Night - The Partition is in an "at-home, at-night" state, delays are permitted on Entry doors.
+         NightInstant - The Partition is in an "at-home, at-night" state, Entry doors trigger Alarm immediately.
+         Force - The Partition is in an "away" state, but one or more Zones are open.
+         Ready - The Partition is in an un-armed state, and ready to be Armed.
+         Vacation - The Partition is currently armed, or in an "away" state.
+         NotReady - The Partition is in an un-armed state, but one or more Zones are open.
+         FailedToArm - The Partition is in an un-armed state, and an arming attempt just failed.
+         EntryDelay - The Partition is currently moving towards the Disarmed state.
+         ExitDelay - The Partition is currently moving towards the Armed state.
+    -->
+    <stateVariable sendEvents="yes">
+      <name>DetailedArmMode</name>
+      <dataType>string</dataType>
+      <shortCode>detailedarmmode</shortCode>
+      <defaultValue>Ready</defaultValue>
+      <allowedValueList>
+        <allowedValue>Disarmed</allowedValue>
+        <allowedValue>Armed</allowedValue>
+        <allowedValue>Stay</allowedValue>
+        <allowedValue>StayInstant</allowedValue>
+        <allowedValue>Night</allowedValue>
+        <allowedValue>NightInstant</allowedValue>
+        <allowedValue>Force</allowedValue>
+        <allowedValue>Ready</allowedValue>
+        <allowedValue>Vacation</allowedValue>
+        <allowedValue>NotReady</allowedValue>
+        <allowedValue>FailedToArm</allowedValue>
+        <allowedValue>EntryDelay</allowedValue>
+        <allowedValue>ExitDelay</allowedValue>
+      </allowedValueList>
+    </stateVariable>
+
+
+    <!-- An indicator of whether the Partition is in Alarm state or not.
+
+      Partition implementations are only required to implement the "None" and "Active"
+      states.
+
+      Many alarm systems will "automatically" clear a Alarm state after some, often legally
+      mandated, period of time.  If this happens, the Partition should move to the "None"
+      state, but also has the option of setting the "AlarmMemory" state.
+
+      None - The Partition is not currently in Alarm, and no prior record of Alarm.
+      Active - The Partition is currently showing as an Alarm actively going off.
+    -->
+    <stateVariable sendEvents="yes">
+      <name>Alarm</name>
+      <dataType>string</dataType>
+      <shortCode>alarm</shortCode>
+      <defaultValue>None</defaultValue>
+      <allowedValueList>
+        <allowedValue>None</allowedValue>
+        <allowedValue>Active</allowedValue>
+      </allowedValueList>
+    </stateVariable>
+
+
+    <!-- An indicator of whether the Partition has Chime mode enabled.
+
+      Some Alarm Systems (DSC, Paradox, GE Caddx) can run in a mode where each Zone
+      that's opened will trigger a Chime, or Bell, to ring for a short period of
+      time.  This setting is independant of any Armed or Disarmed state of the 
+      Partition.
+
+      If the Panel supports intraspection of this property, then the value should
+      be brought forward.  Some panels (Paradox) support the setting, but don't
+      support the intraspection of the setting.  In these cases, the Partition
+      implementation must return "false" for this attribute.
+
+      true - The Partition is in Chime mode, and will ring a Bell when Zones are opened.
+      false - Chimes are disabled.  No chime/sound will occur when a Zone is opened.
+    -->
+    <stateVariable sendEvents="yes">
+      <name>ChimeEnabled</name>
+      <dataType>boolean</dataType>
+      <shortCode>chimeenabled</shortCode>
+      <defaultValue>false</defaultValue>
+    </stateVariable>
+
+
+    <!-- An indicator of whether the Partition has previously been in an Alarm state.
+
+      Each Panel, has a Panel-specific mechanism through which it will transition/clear
+      it's "AlarmMemory" state.
+
+      true - A prior Alarm state occurred, but it has since cleared
+      false - No prior Alarm state is known, or the Prior Alarm condition has been cleared.
+    -->
+    <stateVariable sendEvents="yes" allowRepeats="yes">
+      <name>AlarmMemory</name>
+      <dataType>boolean</dataType>
+      <shortCode>alarmmemory</shortCode>
+    </stateVariable>
+
+
+    <!-- An indicator of when the Partition last entered "Active" state.
+
+      Not all Alarm systems support the information necessary to know that a Alarm has been
+      "reset" distinctly from it being "cleared" by some automated means.
+
+      If any implementation returns "Memory", for Alarm, then it must supply a non-Zero
+      value for this StateVariable.
+
+      0 - Default value if "Memory" is not supported, or if no prior record of Alarm.
+      TODO: represented as seconds past epoch???
+    -->
+    <stateVariable sendEvents="yes">
+      <name>LastAlarmActive</name>
+      <dataType>ui4</dataType>
+      <shortCode>lastalarmactive</shortCode>
+      <defaultValue>0</defaultValue>
+    </stateVariable>
+
+
+    <!-- The Last User to initiate an event against the Partition.
+
+      An Alarm-specific string representing the "User" who last initiated a command against
+      the Partition (typically, but not necessarily) via a Keypad input to the Panel.
+
+      Partitions that don't support the notion of "Users" should leave this blank.
+
+      A Partition implementation may choose to blank-out the value of this stateVariable,
+      after any time period that seems reasonable, to meet local security requirements.
+    -->
+    <stateVariable sendEvents="yes" allowRepeats="yes">
+      <name>LastUser</name>
+      <dataType>string</dataType>
+      <shortCode>lastuser</shortCode>
+      <defaultValue></defaultValue> 
+    </stateVariable>
+
+
+    <!-- The Vendor Status (label) indicated by the Partition.
+
+      An Alarm/Vendor-specific string representing the Human readable "Status" last indicated
+      by the Partition.
+
+      This string may also show the Last status (label) of the Alarm Panel, and can be used
+      by UPnP Clients to display "progress" or current state.
+
+      eg. "Password not valid", "George's home is Arming", "Zones in Bypass 003, 004"
+
+      A Partition implementation may choose to blank-out the value of this stateVariable,
+      after any time period that seems reasonable, to meet local security requirements.
+
+      The string value is intended to be Human readable, as it may be presented within
+      a UPnP client's native display.
+    -->
+    <stateVariable sendEvents="yes" allowRepeats="yes">
+      <name>VendorStatus</name>
+      <dataType>string</dataType>
+      <shortCode>vendorstatus</shortCode>
+      <defaultValue></defaultValue>
+    </stateVariable>
+
+
+    <!-- The Vendor Status Code indicated by the Partition.
+
+      An Alarm/Vendor-specific string representing the internal Status "code" last
+      indicated by the Partition.
+
+      This string may also show the Last status (label) of the Alarm Panel, and can be used
+      by UPnP Clients to display "progress" or current state.
+
+      These can also be used by "events" to provide Partition-specific mechanisms to
+      trigger things like "Fail to Arm" events and the like.
+
+      eg. "1378" (Elk) - "Rule Triggered Voice Telephone Dial"
+      eg. "658" (DSC) - "Keypad Lock-out"
+      eg. "040" (Paradox) - "Fail to Communicate on telephone Number"
+
+      A Partition implementation may choose to blank-out the value of this stateVariable,
+      after any time period that seems reasonable, to meet local security requirements.
+
+      The string value is intended to be used for Vendor specific scripting purposes.  Consult
+      your specific Vendor for the set of Valid Vendor Status codes.
+    -->
+    <stateVariable sendEvents="yes" allowRepeats="yes">
+      <name>VendorStatusCode</name>
+      <dataType>string</dataType>
+      <shortCode>vendorstatuscode</shortCode>
+    </stateVariable>
+
+
+    <!-- The Vendor Status Data for the Vendor Status Code.
+
+      An Alarm/Vendor-specific string representing the internal state "data", associated with
+      the Vendor Status Code above.
+
+      The string value is intended to be used for Vendor specific scripting purposes.  Consult
+      your specific Vendor for the set of Valid Vendor Status data.
+    -->
+    <stateVariable sendEvents="yes" allowRepeats="yes">
+      <name>VendorStatusData</name>
+      <dataType>string</dataType>
+      <shortCode>vendorstatusdata</shortCode>
+    </stateVariable>
+  </serviceStateTable>
+ <actionList>
+    <!-- Request the Partition move into a named Arming mode, using a specific PIN Code..
+
+      There is no guarantee that requesting "Stay" mode wont result in the
+      State becoming "Armed", as some Panels do not differentiate the Stay Armed
+      status.
+
+      The following values must be accepted for the Arming mode, but Vendor specific values
+      may be input as well.  If a particular Vendor doesn't support the mode requested
+      it is free to "promote" the mode to an equivalent, or higher, mode within it's own
+      set.
+
+      eg. Requested State "Night" -> Executed State "Stay"
+
+      Disarmed - The Partition is disarmed.
+      Armed - The Partition is currently armed, or in an "away" mode.
+      Stay - The Partition is in an "at-home" mode, delays are permitted on Entry doors.
+      StayInstant - The Partition is in an "at-home" mode, Entry doors trigger Alarm immediately.
+      Night - The Partition is in an "at-home, at-night" mode, delays are permitted on Entry doors.
+      NightInstant - The Partition is in an "at-home, at-night" mode, Entry doors trigger Alarm immediately.
+      Force - The Partition is in an "away" mode, but one or more Zones are open.
+      Vacation - The Partition is currently armed, or in an "away" mode.
+    -->
+    <action>
+      <name>RequestArmMode</name>
+      <argumentList>
+        <argument>
+          <name>State</name>
+          <direction>in</direction>
+          <relatedStateVariable>DetailedArmMode</relatedStateVariable>
+        </argument>
+        <argument>
+          <name>PINCode</name>
+          <direction>in</direction>
+          <relatedStateVariable>ui4</relatedStateVariable>
+        </argument>
+      </argumentList>
+    </action>
+
+
+    <!-- Request the Partition move into a named Arming mode (No PIN Code).
+
+      The following values must be accepted for the Arming mode, but Vendor specific values
+      may be input as well.  If a particular Vendor doesn't support the mode requested
+      it is free to "promote" the mode to an equivalent, or higher, mode within it's own
+      set.
+
+      eg. Requested State "Night" -> Executed State "Stay"
+
+      Disarmed - The Partition is disarmed.
+      Armed - The Partition is currently armed, or in an "away" mode.
+      Stay - The Partition is in an "at-home" mode, delays are permitted on Entry doors.
+      StayInstant - The Partition is in an "at-home" mode, Entry doors trigger Alarm immediately.
+      Night - The Partition is in an "at-home, at-night" mode, delays are permitted on Entry doors.
+      NightInstant - The Partition is in an "at-home, at-night" mode, Entry doors trigger Alarm immediately.
+      Force - The Partition is in an "away" mode, but one or more Zones are open.
+      Vacation - The Partition is currently armed, or in an "away" mode.
+    -->
+    <action>
+      <name>RequestQuickArmMode</name>
+      <argumentList>
+        <argument>
+          <name>State</name>
+          <direction>in</direction>
+          <relatedStateVariable>DetailedArmMode</relatedStateVariable>
+        </argument>
+      </argumentList>
+    </action>
+
+
+    <!-- Request the initiation of a named Panic mode.
+
+      The following values must be accepted for the Panic mode, but Vendor specific values
+      may be input as well.  If a particular named mode doesn't exist, then the Vendor 
+      implementation is free to map this in a Vendor-specific manner.
+
+      Police - The Police Panic mode
+      Fire - The Fire Panic mode
+      Medical - The Medical Panic mode
+    -->
+    <action>
+      <name>RequestPanicMode</name>
+      <argumentList>
+        <argument>
+          <name>State</name>
+          <direction>in</direction>
+          <relatedStateVariable>string</relatedStateVariable>
+        </argument>
+      </argumentList>
+    </action>
+  </actionList>
+</scpd>
Index: /D_ElkAlarmPartition1.xml
===================================================================
--- /D_ElkAlarmPartition1.xml	(revision 3)
+++ /D_ElkAlarmPartition1.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:schemas-micasaverde-com:device:ElkAlarmPartition:1</deviceType>
+    <staticJson>D_ElkAlarmPartition1.json</staticJson>
+    <friendlyName>Elk Alarm Partition</friendlyName>
+    <manufacturer>Elk</manufacturer>
+    <protocol>crlf</protocol>
+    <handleChildren>1</handleChildren>
+    <commUse>rs232</commUse>
+    <serviceList>
+      <service>
+        <serviceType>urn:schemas-micasaverde-com:service:AlarmPartition:2</serviceType>
+        <serviceId>urn:micasaverde-com:serviceId:AlarmPartition2</serviceId>
+        <SCPDURL>S_AlarmPartition2.xml</SCPDURL>
+      </service>
+    </serviceList>
+  </device>
+</root>
Index: /D_ElkAlarmPartition1.json
===================================================================
--- /D_ElkAlarmPartition1.json	(revision 3)
+++ /D_ElkAlarmPartition1.json	(revision 3)
@@ -0,0 +1,755 @@
+{
+    "flashicon": "icons\/Door_Lock.swf",
+    "imgIconBody": "",
+    "imgIconDimmable": "",
+    "imgIconTurnable": "",
+    "imgIconMin": "pics\/devices\/Door_UNLOCKED.png",
+    "imgIconMax": "pics\/devices\/Door_LOCKED.png",
+    "halloIconsDir": "pics\/hallo",
+    "DisplayStatus": {
+        "Style": "label",
+        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+        "Variable": "ArmMode",
+        "MinValue": "Disarmed",
+        "MaxValue": "Armed"
+    },
+    "doc_url": {
+        "doc_language": 1,
+        "doc_manual": 1,
+        "doc_version": 1,
+        "doc_platform": 0,
+        "doc_page": "devices"
+    },
+    "ToggleButton": 1,
+    "Tabs": [
+        {
+            "Label": {
+                "lang_tag": "tabname_control",
+                "text": "Alarm Partition"
+            },
+            "Position": "0",
+            "TabType": "flash",
+            "ControlGroup": [
+                {
+                    "id": "1"
+                },
+                {
+                    "id": "2"
+                }
+            ],
+            "Control": [
+                {
+                    "ControlGroup": "1",
+                    "ControlHeader": "1",
+                    "ControlType": "label",
+                    "Label": {
+                        "lang_tag": "device_settings_page",
+                        "text": "Status"
+                    },
+                    "Display": {
+                        "Top": 0,
+                        "Left": 50,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlGroup": "1",
+                    "ControlHeader": "1",
+                    "ControlType": "variable",
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "DetailedArmMode",
+                        "Top": 20,
+                        "Left": 50,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlType": "label",
+                    "Label": {
+                        "lang_tag": "device_settings_page",
+                        "text": "One Touch Options"
+                    },
+                    "Display": {
+                        "Top": 60,
+                        "Left": 50,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlGroup": "1",
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_lock",
+                        "text": "Arm"
+                    },
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "ArmMode",
+                        "Value": "Armed",
+                        "Top": 80,
+                        "Left": 50,
+                        "Width": 75,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestQuickArmMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "Armed"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlGroup": "1",
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_lock",
+                        "text": "Force"
+                    },
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "DetailedArmMode",
+                        "Value": "Force",
+                        "Top": 100,
+                        "Left": 50,
+                        "Width": 75,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestQuickArmMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "ArmedInstant"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlGroup": "1",
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_lock",
+                        "text": "Stay"
+                    },
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "DetailedArmMode",
+                        "Value": "Stay",
+                        "Top": 80,
+                        "Left": 135,
+                        "Width": 75,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestQuickArmMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "Stay"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlGroup": "1",
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_lock",
+                        "text": "iStay"
+                    },
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "DetailedArmMode",
+                        "Value": "StayInstant",
+                        "Top": 100,
+                        "Left": 135,
+                        "Width": 75,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestQuickArmMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "StayInstant"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlGroup": "1",
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_lock",
+                        "text": "Night"
+                    },
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "DetailedArmMode",
+                        "Value": "Night",
+                        "Top": 80,
+                        "Left": 220,
+                        "Width": 75,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestQuickArmMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "Night"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlGroup": "1",
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_lock",
+                        "text": "iNight"
+                    },
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "DetailedArmMode",
+                        "Value": "NightInstant",
+                        "Top": 100,
+                        "Left": 220,
+                        "Width": 75,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestQuickArmMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "NightInstant"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlGroup": "2",
+                    "ControlHeader": "1",
+                    "ControlType": "label",
+                    "Label": {
+                        "lang_tag": "device_settings_page",
+                        "text": "PIN Options"
+                    },
+                    "Display": {
+                        "Top": 140,
+                        "Left": 50,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlGroup": "2",
+                    "ControlPair": "1",
+                    "ControlType": "input",
+                    "ID": "PINCode",
+                    "Display": {
+                        "Top": 160,
+                        "Left": 50,
+                        "Width": 100,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlGroup": "2",
+                    "ControlPair": "1",
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_set",
+                        "text": "Arm"
+                    },
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "ArmMode",
+                        "Value": "Armed",
+                        "Top": 160,
+                        "Left": 160,
+                        "Width": 50,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestArmMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "Armed"
+                            },
+                            {
+                                "Name": "PINCode",
+                                "ID": "PINCode"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlGroup": "2",
+                    "ControlPair": "1",
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_set",
+                        "text": "Stay"
+                    },
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "DetailedArmMode",
+                        "Value": "Stay",
+                        "Top": 180,
+                        "Left": 160,
+                        "Width": 50,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestArmMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "Stay"
+                            },
+                            {
+                                "Name": "PINCode",
+                                "ID": "PINCode"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlGroup": "2",
+                    "ControlPair": "1",
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_set",
+                        "text": "Disarm"
+                    },
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "ArmMode",
+                        "Value": "Disarmed",
+                        "Top": 200,
+                        "Left": 160,
+                        "Width": 50,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestArmMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "Disarmed"
+                            },
+                            {
+                                "Name": "PINCode",
+                                "ID": "PINCode"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlType": "label",
+                    "Label": {
+                        "lang_tag": "device_settings_page",
+                        "text": "Panic Alarm Options"
+                    },
+                    "Display": {
+                        "Top": 240,
+                        "Left": 50,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_set",
+                        "text": "Police"
+                    },
+                    "Display": {
+                        "Top": 260,
+                        "Left": 50,
+                        "Width": 75,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestPanicMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "Police"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_set",
+                        "text": "Medical"
+                    },
+                    "Display": {
+                        "Top": 260,
+                        "Left": 135,
+                        "Width": 75,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestPanicMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "Medical"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlType": "button",
+                    "Label": {
+                        "lang_tag": "cmd_set",
+                        "text": "Fire"
+                    },
+                    "Display": {
+                        "Top": 260,
+                        "Left": 220,
+                        "Width": 75,
+                        "Height": 20
+                    },
+                    "Command": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Action": "RequestPanicMode",
+                        "Parameters": [
+                            {
+                                "Name": "State",
+                                "Value": "Fire"
+                            }
+                        ]
+                    }
+                },
+                {
+                    "ControlType": "label",
+                    "Label": {
+                        "lang_tag": "device_settings_page",
+                        "text": "Vendor Options"
+                    },
+                    "Display": {
+                        "Top": 300,
+                        "Left": 50,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlType": "variable",
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "VendorStatus",
+                        "Top": 320,
+                        "Left": 50,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlType": "variable",
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "VendorStatusCode",
+                        "Top": 340,
+                        "Left": 50,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                },
+                {
+                    "ControlType": "variable",
+                    "Display": {
+                        "Service": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                        "Variable": "VendorStatusData",
+                        "Top": 360,
+                        "Left": 50,
+                        "Width": 200,
+                        "Height": 20
+                    }
+                }
+            ]
+        },
+        {
+            "Label": {
+                "lang_tag": "notifications",
+                "text": "Notifications"
+            },
+            "Position": "2",
+            "TabType": "javascript",
+            "ScriptName": "shared.js",
+            "Function": "device_notifications"
+        },
+        {
+            "Label": {
+                "lang_tag": "logs",
+                "text": "Logs"
+            },
+            "Position": "3",
+            "TabType": "javascript",
+            "ScriptName": "shared.js",
+            "Function": "device_logs"
+        },
+        {
+            "Label": {
+                "lang_tag": "advanced",
+                "text": "Advanced"
+            },
+            "Position": "4",
+            "TabType": "javascript",
+            "ScriptName": "shared.js",
+            "Function": "advanced_device"
+        }
+    ],
+    "DeviceType": "urn:schemas-micasaverde-com:device:ElkAlarmPartition:1",
+    "eventList": {
+        "event_1": {
+            "label": "Armed state",
+            "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+            "argumentList" : {
+                "argument_1" : {
+                    "dataType": "string",
+                    "defaultValue": "Armed",
+                    "allowedValueList" : {
+                        "Yes" : "Armed",
+                        "No" : "Disarmed"
+                    },
+                    "name": "ArmMode",
+                    "comparisson": "=",
+                    "prefix": "Is Armed?",
+                    "suffix": ""
+                }
+            }
+        },
+        "event_2": {
+            "label": "Armed detailed state",
+            "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+            "argumentList" : {
+                "argument_1" : {
+                    "dataType": "string",
+                    "defaultValue": "Armed",
+                    "allowedValueList" : {
+                        "Disarmed" : "Disarmed",
+                        "Armed" : "Armed",
+                        "Stay" : "Stay",
+                        "StayInstant" : "StayInstant",
+                        "Night" : "Night",
+                        "NightInstant" : "NightInstant",
+                        "Force" : "Force",
+                        "Ready" : "Ready",
+                        "Vacation" : "Vacation",
+                        "NotReady" : "NotReady",
+                        "FailedToArm" : "FailedToArm",
+                        "EntryDelay" : "EntryDelay",
+                        "ExitDelay" : "ExitDelay"
+                    },
+                    "name": "DetailedArmMode",
+                    "comparisson": "=",
+                    "prefix": "Armed state",
+                    "suffix": ""
+                }
+            }
+        },
+        "event_3": {
+            "label": "Alarm Active",
+            "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+            "argumentList" : {
+                "argument_1" : {
+                    "dataType": "boolean",
+                    "defaultValue": "Active",
+                    "allowedValueList" : {
+                        "Active" : "Active",
+                        "NotActive" : "None"
+                    },
+                    "name": "Alarm",
+                    "comparisson": "=",
+                    "prefix": "Is Active?",
+                    "suffix": ""
+                }
+            }
+        },
+        "event_4": {
+            "label": "A PIN Code is entered",
+            "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+            "argumentList" : {
+                "argument_1" : {
+                    "dataType": "string",
+                    "defaultValue": "",
+                    "name": "LastUser",
+                    "comparisson": "=",
+                    "prefix": "User",
+                    "suffix": ""
+                }
+            }
+        },
+        "event_5": {
+            "label": "Vendor Status Code",
+            "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+            "argumentList" : {
+                "argument_1" : {
+                    "dataType": "string",
+                    "defaultValue": "",
+                    "name": "VendorStatusCode",
+                    "comparisson": "=",
+                    "prefix": "Code",
+                    "suffix": ""
+                }
+            }
+        }
+    },
+    "sceneList": {
+        "group_1": {
+            "cmd_1": {
+                "label": "Set Partition ... (PIN)",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestArmMode",
+                "argumentList": {
+                    "argument_1": {
+                        "dataType": "string",
+                        "defaultValue": "Disarmed",
+                        "allowedValueList": {
+                            "Disarmed" : "Disarmed",
+                            "Armed" : "Armed",
+                            "Stay" : "Stay",
+                            "StayInstant" : "StayInstant",
+                            "Night" : "Night",
+                            "NightInstant" : "NightInstant",
+                            "Force" : "Force",
+                            "Vacation" : "Vacation"
+                        },
+                        "name": "State",
+                        "prefix": "State",
+                        "suffix": "" 
+                    },
+                    "argument_2": {
+                        "dataType": "ui4",
+                        "defaultValue": "1234",
+                        "allowedValueRange": {
+                            "minimum": "0",
+                            "maximum": "999999999999"
+                        },
+                        "prefix" : "PIN",
+                        "suffix" : "0-99999999, (insecure)",
+                        "name": "PINCode"
+                    }
+                }
+            }
+        },
+        "group_2": {
+            "cmd_1": {
+                "label": "Set Partition Disarmed (No PIN)",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestQuickArmMode",
+                "arguments": {
+                    "State": "Disarmed"
+                }
+            },
+            "cmd_2": {
+                "label": "Set Partition Armed (No PIN)",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestQuickArmMode",
+                "arguments": {
+                    "State": "Armed"
+                }
+            },
+            "cmd_3": {
+                "label": "Set Partition Stay (No PIN)",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestQuickArmMode",
+                "arguments": {
+                    "State": "Stay"
+                }
+            },
+            "cmd_4": {
+                "label": "Set Partition StayInstant (No PIN)",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestQuickArmMode",
+                "arguments": {
+                    "State": "StayInstant"
+                }
+            },
+            "cmd_5": {
+                "label": "Set Partition Night (No PIN)",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestQuickArmMode",
+                "arguments": {
+                    "State": "Night"
+                }
+            },
+            "cmd_6": {
+                "label": "Set Partition NightInstant (No PIN)",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestQuickArmMode",
+                "arguments": {
+                    "State": "NightInstant"
+                }
+            },
+            "cmd_7": {
+                "label": "Set Partition Force (No PIN)",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestQuickArmMode",
+                "arguments": {
+                    "State": "Force"
+                }
+            },
+            "cmd_8": {
+                "label": "Set Partition Vacation (No PIN)",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestQuickArmMode",
+                "arguments": {
+                    "State": "Vacation"
+                }
+            }
+        },
+        "group_3": {
+            "cmd_1": {
+                "label": "Request Medical Panic",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestPanicMode",
+                "arguments": {
+                    "State": "Medical"
+                }
+            },
+            "cmd_2": {
+                "label": "Request Police Panic",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestPanicMode",
+                "arguments": {
+                    "State": "Police"
+                }
+            },
+            "cmd_3": {
+                "label": "Request Fire Panic",
+                "serviceId": "urn:micasaverde-com:serviceId:AlarmPartition2",
+                "action": "RequestPanicMode",
+                "arguments": {
+                    "State": "Fire"
+                }
+            }
+        }
+    }
+}
Index: /I_ElkAlarmPanel1.xml
===================================================================
--- /I_ElkAlarmPanel1.xml	(revision 3)
+++ /I_ElkAlarmPanel1.xml	(revision 3)
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<implementation>
+  <functions>
+
+    function lug_startup (lul_device)
+    end
+
+  </functions>
+  <startup>lug_startup</startup>
+  <incoming>
+    <lua>
+        got_input(tostring(lul_data))
+    </lua>
+  </incoming>
+  <actionList>
+    <action>
+        <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPanel1</serviceId>
+        <name>SetAccessCode</name>
+        <run>
+            luup.log('setting access code to: ' .. lul_settings.NewAccessCode)
+            luup.variable_set('urn:micasaverde-com:serviceId:ElkAlarmPanel1', 'accesscode', lul_settings.NewAccessCode)
+        </run>
+    </action>
+    <action>
+      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
+      <name>Disarm</name>
+      <run>
+        local index = tonumber(lul_settings.DeviceNum)
+        return arm_partition(luup.devices[index].id,0)
+      </run>
+    </action>
+    <action>
+      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
+      <name>AwayArm</name>
+      <run>
+        local index = tonumber(lul_settings.DeviceNum)
+        return arm_partition(luup.devices[index].id,1)
+      </run>
+    </action>
+    <action>
+      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
+      <name>StayArm</name>
+      <run>
+        local index = tonumber(lul_settings.DeviceNum)
+        return arm_partition(luup.devices[index].id,2)
+      </run>
+    </action>
+    <action>
+      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
+      <name>InstantStayArm</name>
+      <run>
+        local index = tonumber(lul_settings.DeviceNum)
+        return arm_partition(luup.devices[index].id,3)
+      </run>
+    </action>
+    <action>
+      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
+      <name>NightArm</name>
+      <run>
+        local index = tonumber(lul_settings.DeviceNum)
+        return arm_partition(luup.devices[index].id,4)
+      </run>
+    </action>
+    <action>
+      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
+      <name>InstantNightArm</name>
+      <run>
+        local index = tonumber(lul_settings.DeviceNum)
+        return arm_partition(luup.devices[index].id,5)
+      </run>
+    </action>
+    <action>
+      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
+      <name>VacationArm</name>
+      <run>
+        local index = tonumber(lul_settings.DeviceNum)
+        return arm_partition(luup.devices[index].id,6)
+      </run>
+    </action>
+  </actionList>
+  <settings>
+    <handleChildren>1</handleChildren>
+  </settings>
+</implementation>
Index: ElkAlarmPanel.xml
===================================================================
--- /S_ElkAlarmPanel.xml	(revision 1)
+++ 	(revision )
@@ -1,47 +1,0 @@
-<?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>accesscode</name>
-      <datatype>string</datatype>
-      <defaultValue>123456</defaultValue>
-    </stateVariable>   
-    <stateVariable sendEvents="no">
-      <name>Command</name>
-      <datatype>string</datatype>
-      <defaultValue></defaultValue>
-    </stateVariable>
-    <stateVariable  sendEvents="yes" allowRepeats="no">
-        <name>ZoneTripped</name>
-        <datatype>ui2</datatype>
-    </stateVariable>
-  </serviceStateTable>
-  <actionList>
-    <action>
-      <name>SetAccessCode</name>
-      <argumentList>
-        <argument>
-          <name>NewAccessCode</name>
-          <direction>in</direction>
-        </argument>
-      </argumentList>
-    </action>
-    <action>
-      <name>SendCommand</name>
-      <argumentList>
-        <argument>
-          <name>Command</name>
-          <direction>in</direction>
-        </argument>
-        <argument>
-          <name>Data</name>
-          <direction>in</direction>
-        </argument>
-      </argumentList>
-    </action>
-  </actionList>
-</scpd>
Index: ElkAlarmPanel.json
===================================================================
--- /D_ElkAlarmPanel.json	(revision 1)
+++ 	(revision )
@@ -1,148 +1,0 @@
-{
-    "flashicon": "icons\/devices.swf",
-    "imgIconBody": "",
-    "imgIconDimmable": "",
-    "imgIconTurnable": "",
-    "imgIconMin": "",
-    "imgIconMax": "",
-    "halloIconsDir": "pics\/hallo",
-    "DisplayStatus": {
-        
-    },
-    "doc_url":{
-		"doc_forced_url":{
-			"text":"http://wiki.micasaverde.com/index.php/ElkAlarmPanel"
-		}
-    },
-    "Tabs": [
-		{
-			"Label": {
-				"lang_tag": "Elk_alarm_panel",
-				"text": "Elk Alarm Panel"
-			},
-			"Position": "0",
-			"TabType": "flash",
-			"Control": [
-				{
-					"ControlType": "variable",
-					"Display": {
-						"Service": "urn:micasaverde-com:serviceId:AlarmPanel1",
-						"Variable": "LCD",
-						"Top": 60,
-						"Left": 10,
-						"Width": 200,
-						"Height": 20
-					}
-				},
-				{
-					"ControlType": "label",
-					"Label": {
-						"lang_tag": "accesscode",
-						"text": "Access Code"
-					},
-					"Display": {
-						"Top": 90,
-						"Left": 10,
-						"Width": 80,
-						"Height": 20
-					}
-				},
-				{
-					"ControlType":"input",
-					"ID": "NewAccessCode",
-					"Style":"numeric",
-					"Display":{
-						"Top": 90,
-						"Left":95
-					}
-				},
-				{
-					"ControlType": "button",
-					"Label": {
-						"lang_tag": "setaccesscode",
-						"text": "Set"
-					},
-					"Display": {
-						"Top": 88,
-						"Left": 350
-					},
-					"Command": {
-						"Service": "urn:micasaverde-com:serviceId:ElkAlarmPanel1",
-						"Action":  "SetAccessCode",
-						"Parameters": [
-							{
-								"Name": "NewAccessCode",
-								"ID": "NewAccessCode"
-							}
-						]
-					}
-				},
-				{
-					"ControlType":"button",
-					"Label": {
-						"lang_tag": "reset_clock",
-						"text": "Set Clock"
-					},
-					"Display": {
-						"Top": 120,
-						"Left": 10
-					},
-					"Command": {
-						"Service": "urn:micasaverde-com:serviceId:AlarmPanel1",
-						"Action": "SetTime",
-					}
-				}
-			]
-		},
-        {
-            "Label": {
-                "lang_tag": "notifications",
-                "text": "Notifications" 
-            },
-            "Position": "5",
-            "TabType": "javascript",
-            "ScriptName": "shared.js",
-            "Function": "device_notifications" 
-        },
-        {
-            "Label": {
-                "lang_tag": "settings",
-                "text": "Settings" 
-            },
-            "Position": "1",
-            "TabType": "javascript",
-            "ScriptName": "shared.js",
-            "Function": "simple_device" 
-        },
-        {
-            "Label": {
-                "lang_tag": "advanced",
-                "text": "Advanced" 
-            },
-            "Position": "2",
-            "TabType": "javascript",
-            "ScriptName": "shared.js",
-            "Function": "advanced_device" 
-        } 
-    ],
-    "sceneList": {
-       
-    },
-    "eventList": {
-        "event_1":{
-			"label":"Zone Tripped",
-			"serviceId": "urn:micasaverde-com:serviceId:AlarmPanel1",
-			"argumentList": {
-				"argument_1": {
-					"dataType": "ui2",
-					"name": "ZoneTripped",
-					"comparisson": "=",
-					"prefix": "Which Zone Tripped",
-					"suffix": ""
-				}
-			}
-		}
-    },
-    "DeviceType": "urn:schemas-micasaverde-com:device:ElkAlarmPanel:1",
-    "device_type": "urn:schemas-micasaverde-com:device:ElkAlarmPanel:1" 
-}
Index: ElkAlarmPanel.xml
===================================================================
--- /D_ElkAlarmPanel.xml	(revision 1)
+++ 	(revision )
@@ -1,26 +1,0 @@
-<?xml version="1.0"?>
-<root xmlns="urn:schemas-upnp-org:device-1-0">
-  <specVersion>
-    <major>1</major>
-    <minor>0</minor>
-  </specVersion>
-  <device>
-    <deviceType>urn:schemas-micasaverde-com:device:ElkAlarmPanel:1</deviceType>
-    <staticJson>D_ElkAlarmPanel.json</staticJson>
-    <friendlyName>Elk Alarm Panel</friendlyName>
-    <manufacturer>Elk</manufacturer>
-    <protocol>crlf</protocol>
-    <handleChildren>1</handleChildren>
-    <commUse>rs232</commUse>
-    <serviceList>
-      <service>
-        <serviceType>urn:schemas-micasaverde-com:service:ElkAlarmPanel:1</serviceType>
-        <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPanel1</serviceId>
-        <SCPDURL>S_ElkAlarmPanel.xml</SCPDURL>
-      </service>
-    </serviceList>
-    <implementationList>
-      <implementationFile>I_ElkAlarmPanel.xml</implementationFile>
-    </implementationList>
-  </device>
-</root>
Index: kAlarmPanel.js
===================================================================
--- /ElkAlarmPanel.js	(revision 1)
+++ 	(revision )
@@ -1,233 +1,0 @@
-
-var ALPANEL_SID='urn:micasaverde-com:serviceId:AlarmPanel1';
-var statusMsgs = {
-	'650': 'Ready',
-	'651': 'Not Ready',
-	'654': 'Alarm',
-	'655': 'Disarmed',
-	'656': 'Exit Delay in Progress',
-	'657': 'Entry Delay in Progress',
-	'658': 'Keypad Lock-out',
-	'659': 'Keypad Blanking',
-	'660': 'Command Output in Progress',
-	'670': 'Invalid Access Code',
-	'671': 'Function not Available',
-}
-
-function dsc_main_panel (deviceID) {
-	function update_main_panel () {
-		lu_status (deviceID, function (devstate){
-			var lcd = get_variable_from_response (devstate, ALPANEL_SID, 'LCD');
-			document.getElementById('dsc_btns_LCD').innerHTML = lcd;
-			setTimeout(update_main_panel, 1000);
-		});
-	};
-	
-	var accesscode = get_device_state (deviceID, ALPANEL_SID, 'accesscode');
-	var html = '<table class="content" width="100%">';
-	html += '<tr>									\
-				<th id="dsc_btns_LCD" colspan="2" align="center" style="border:inset;padding:3px;"></th>		\
-			</tr>';
-	html += '<tr>							\
-				<th>code:</th>		\
-				<td><input type="text" id="accesscode" value="'+accesscode+'" onchange="do_setcode('+deviceID+');"/></td>		\
-			</tr>';
-	html += '</table><br/>';
-	html += '<input type="button" value="Set Clock" onclick="set_time('+deviceID+');"/><br/>';
-// 	console.log (html);
-	set_panel_html (html);
-	update_main_panel ();
-}
-
-function dsc_main_partition (deviceID) {
-	function update_partition_status () {
-		lu_status (deviceID, function (devstate){
-			var partstat = get_variable_from_response (devstate, ALPANEL_SID, 'PartitionStatusLabel');
-			document.getElementById ('partition_status').innerHTML = partstat;
-			setTimeout(update_partition_status, 1000);
-		});
-	}
-	
-	var html = '<table class="content">';
-	html += '<tr>									\
-				<th id="partition_status" colspan="5" align="center" style="border:inset;padding:3px;"></th>		\
-			</tr>';
-	html += '<tr>					\
-				<td><input type="button" value="Away" onclick="do_arm(\'030\','+deviceID+');" /></td>	\
-				<td><input type="button" value="Stay" onclick="do_arm(\'031\','+deviceID+');" /></td>	\
-				<td><input type="button" value="No Delay" onclick="do_arm(\'032\','+deviceID+');" /></td>	\
-				<td><input type="button" value="w/Code" onclick="do_arm_code(\'033\','+deviceID+');" /></td>	\
-				<td><input type="button" value="Disarm" onclick="do_arm_code(\'040\','+deviceID+');" /></td>	\
-			</tr>';
-	html += '</table><br/>';
-	set_panel_html (html);
-	update_partition_status ();
-}
-
-function dsc_buttons_panel (deviceID) {
-	function update_buttons_panel () {
-		lu_status (deviceID);
-		document.getElementById('dsc_btns_LCD').innerHTML = get_device_state (deviceID, ALPANEL_SID, 'LCD');
-		for (var i=1; i <= 8; ++i) {
-			var zonstat = get_device_state(deviceID, ALPANEL_SID, 'ZoneStatusLabel_'+i);
-			console.log (i, zonstat);
-			document.getElementById ('zonestatus_'+i).innerHTML = zonstat;
-		}
-		setTimeout(update_buttons_panel, 1000);
-	};
-	
-	var accesscode = get_device_state (deviceID, ALPANEL_SID, 'accesscode');
-	var html = '<table class="content">';
-	html += '<tr>									\
-				<th id="dsc_btns_LCD" colspan="6" align="center" style="border:inset;padding:3px;"></th>		\
-			</tr>';
-	html += '<tr>							\
-				<th>code:</th>		\
-				<td colspan="3"><input type="text" id="accesscode" value="'+accesscode+'" onchange="do_setcode('+deviceID+');"/></td>		\
-			</tr>';
-// 	for (var i=1; i <= 8; ++i) {
-	var partitions = get_partitions (deviceID);
-	for (var i=0; i<partitions.length; ++i) {
-		var n = partitions[i];
-		html += '<tr>					\
-					<th align="right">Zone #'+n+': </th>	\
-					<td><input type="button" value="Away" onclick="do_arm(\'030\','+deviceID+','+n+');" /></td>	\
-					<td><input type="button" value="Stay" onclick="do_arm(\'031\','+deviceID+','+n+');" /></td>	\
-					<td><input type="button" value="No Delay" onclick="do_arm(\'032\','+deviceID+','+n+');" /></td>	\
-					<td><input type="button" value="w/Code" onclick="do_arm_code(\'033\','+deviceID+','+n+');" /></td>	\
-					<td><input type="button" value="Disarm" onclick="do_arm_code(\'040\','+deviceID+','+n+');" /></td>	\
-					<td id="zonestatus_'+n+'"></td>				\
-				</tr>';
-	}
-	html += '</table><br/>';
-	html += '<input type="button" value="Set Clock" onclick="set_time('+deviceID+');"/><br/>';
-// 	console.log (html);
-	set_panel_html (html);
-	update_buttons_panel ();
-}
-
-
-function get_partitions (deviceID) {
-	var s = get_device_state (deviceID, ALPANEL_SID, 'PartitionsPresent');
-	if (! s) {
-		s = '1,2,3,4,5,6,7,8';
-	}
-	return s.split(',');
-}
-
-
-function do_setcode (deviceID) {
-	var accesscode = document.getElementById('accesscode').value;
-	set_device_state (deviceID,ALPANEL_SID,'accesscode',accesscode);
-	reset_last_update(deviceID);
-}
-
-function set_time (deviceID) {
-	call_action (deviceID, ALPANEL_SID, 'SetTime');
-};
-	
-
-function do_arm (cmd, device, zone) {
-	if (! zone) {
-		zone = get_device_state (device, ALPANEL_SID, 'ItemSpec');
-	}
-	call_action (device, ALPANEL_SID, 'SendCommand', {'Command':cmd, 'Data':zone});
-}
-function do_arm_code (cmd, device, zone) {
-	if (! zone) {
-		zone = get_device_state (device, ALPANEL_SID, 'ItemSpec');
-	}
-	var accesscode = document.getElementById('accesscode').value;
-	call_action (device, ALPANEL_SID, 'SendCommand', {'Command':cmd, 'Data':zone+accesscode});
-}
-
-function call_action (device, sid, actname, args) {
-	var q={
-		'id':'lu_action',
-		'output_format':'xml',
-		'DeviceNum':device,
-		'serviceId':sid,
-		'action':actname
-	};
-	if (args) {
-		Object.extend(q, args);
-	}
-	new Ajax.Request (command_url+'/data_request', {
-		method: 'get',
-		parameters: q,
-		onSuccess: function (response) {
-			console.log (response);
-		}
-	});
-}
-
-function lu_status (deviceID, cb) {
-	new Ajax.Request (command_url+'/data_request', {
-		method: 'get',
-		evalJSON: 'force',
-		parameters: {
-			id: 'lu_status',
-			output_format: 'json',
-			DeviceNum: deviceID,
-		},
-		onSuccess: function (response) {
-			console.log ('lu_status response:', response);
-			if (cb) {
-				cb (response.responseJSON['Device_Num_'+deviceID]);
-			}
-		}
-	});
-}
-
-function get_variable_from_response (devstate, sid, varname) {
-	var states = devstate.states;
-	var numvars = states.length;
-	for (var i=0; i < numvars; ++i) {
-		var v = states[i];
-		if (v.service==sid && v.variable==varname) {
-			return v.value;
-		}
-	}
-}
-
-
-function luup_variable_set (device, sid, varname, val) {
-	new Ajax.Request (command_url+'/data_request', {
-		method: 'get',
-		evalJS: 'force',
-		parameters: {
-			id: 'lu_variableset',
-			DeviceNum: device,
-			serviceId: sid,
-			Variable: varname,
-			Value: val
-		}
-	})
-}
-
-function dsc_panel_config (deviceID) {
-	var html = '<ul>';
-	for (var i=1; i <= 8; ++i) {
-		html += '<li>Partition #'+i+': <input type="checkbox" id="partition_'+i+'" onchange="set_panel_config('+deviceID+');"/></li>';
-	}
-	html += '</ul>';
-	set_panel_html (html);
-	
-	var active_parts = get_partitions(deviceID);
-	for (var i=0; i< active_parts.length; ++i) {
-		document.getElementById('partition_'+active_parts[i]).checked = true;
-	}
-}
-
-function set_panel_config (deviceID) {
-	var partitions=[];
-	for (var i=1; i <= 8; ++i) {
-		if (document.getElementById('partition_'+i).checked) {
-			partitions.push (i);
-		}
-	}
-	luup_variable_set (deviceID, ALPANEL_SID, 'PartitionsPresent', partitions.join(','));
-// 	reset_last_update(deviceID);
-}
-
-callback();
Index: ElkAlarmPartition.xml
===================================================================
--- /S_ElkAlarmPartition.xml	(revision 1)
+++ 	(revision )
@@ -1,86 +1,0 @@
-<?xml version="1.0"?>
-<scpd xmlns="urn:schemas-upnp-org:service-1-0">
-  <specVersion>
-    <major>1</major>
-    <minor>0</minor>
-  </specVersion>
-  <serviceStateTable>
-    <stateVariable sendEvents="yes">
-      <name>ArmingStatus</name>
-      <datatype>string</datatype>
-      <defaultValue>Disarmed</defaultValue>
-      <allowedValueList>
-        <allowedValue>Disarmed</allowedValue>
-        <allowedValue>Armed Away</allowedValue>
-        <allowedValue>Armed Stay</allowedValue>
-        <allowedValue>Armed Stay Instant</allowedValue>
-        <allowedValue>Armed to Night</allowedValue>
-        <allowedValue>Armed to Night Instant</allowedValue>
-        <allowedValue>Armed to Vacation</allowedValue>
-      </allowedValueList>
-    </stateVariable>
-    <stateVariable sendEvents="yes">
-      <name>ArmUpStatus</name>
-      <datatype>string</datatype>
-      <defaultValue>Not Ready To Arm</defaultValue>
-      <allowedValueList>
-        <allowedValue>Not Ready To Arm</allowedValue>
-        <allowedValue>Ready To Arm</allowedValue>
-        <allowedValue>Ready To Arm Zone Violated</allowedValue>
-        <allowedValue>Armed Exit timer active</allowedValue>
-        <allowedValue>Armed Fully</allowedValue>
-        <allowedValue>Force Armed</allowedValue>
-        <allowedValue>Armed with Bypass</allowedValue>
-      </allowedValueList>
-    </stateVariable>
-     <stateVariable sendEvents="yes">
-         <name>AlarmStatus</name>
-         <datatype>string</datatype>
-         <defaultValue>No Alarm</defaultValue>
-         <allowedValueList>
-           <allowedValue>No Alarm</allowedValue>
-           <allowedValue>Entrance Delay</allowedValue>
-           <allowedValue>Alarm Abort Delay</allowedValue>
-           <allowedValue>Fire Alarm</allowedValue>
-           <allowedValue>Medical Alarm</allowedValue>
-           <allowedValue>Police Alarm</allowedValue>
-           <allowedValue>Burglar Alarm</allowedValue>
-           <allowedValue>Aux1 Alarm</allowedValue>
-           <allowedValue>Aux2 Alarm</allowedValue>
-           <allowedValue>Aux3 Alarm</allowedValue>
-           <allowedValue>Aux4 Alarm</allowedValue>
-           <allowedValue>Carbon Monoxide Alarm</allowedValue>
-           <allowedValue>Emergency Alarm</allowedValue>
-           <allowedValue>Freeze Alarm</allowedValue>
-           <allowedValue>Gas Alarm</allowedValue>
-           <allowedValue>Heat Alarm</allowedValue>
-           <allowedValue>Water Alarm</allowedValue>
-           <allowedValue>Fire Supervisory State</allowedValue>
-           <allowedValue>Verify Fire State</allowedValue>
-         </allowedValueList>
-       </stateVariable>
-  </serviceStateTable>
-  <actionList>
-    <action>
-      <name>Disarm</name>
-    </action>
-    <action>
-      <name>AwayArm</name>
-    </action>
-    <action>
-      <name>StayArm</name>
-    </action>
-    <action>
-      <name>InstantStayArm</name>
-    </action>
-    <action>
-      <name>NightArm</name>
-    </action>
-    <action>
-      <name>InstantNightArm</name>
-    </action>
-    <action>
-      <name>VacationArm</name>
-    </action>
-  </actionList>
-</scpd>
Index: ElkAlarmPartition.json
===================================================================
--- /D_ElkAlarmPartition.json	(revision 1)
+++ 	(revision )
@@ -1,262 +1,0 @@
-{
-    "flashicon": "icons\/devices.swf",
-    "imgIconBody": "",
-    "imgIconDimmable": "",
-    "imgIconTurnable": "",
-    "imgIconMin": "",
-    "imgIconMax": "",
-    "halloIconsDir": "pics\/hallo",
-    "DisplayStatus": {
-        
-    },
-    "Tabs": [
-		{
-			"Label": {
-				"lang_tag": "Elk_alarm_partition",
-				"text": "Elk Alarm Partition"
-			},
-			"Position": 0,
-			"TabType": "flash",
-			"Control": [
-				{
-					"ControlType": "variable",
-					"Display": {
-						"Service": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-						"Variable": "ArmingStatus",
-						"Top": 60,
-						"Left": 10,
-						"Width": 200,
-						"Height": 20
-					}
-				},
-				{
-					"ControlType":"button",
-					"Label": {
-						"lang_tag": "away",
-						"text": "Away"
-					},
-					"Display": {
-						"Top": 86,
-						"Left": 10
-					},
-					"Command": {
-						"Service": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-						"Action": "AwayArm",
-					}
-				},
-				{
-					"ControlType":"button",
-					"Label": {
-						"lang_tag": "stay",
-						"text": "Stay"
-					},
-					"Display": {
-						"Top": 86,
-						"Left": 120
-					},
-					"Command": {
-						"Service": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-						"Action": "StayArm",
-					}
-				},
-				{
-					"ControlType":"button",
-					"Label": {
-						"lang_tag": "no_delay",
-						"text": "Instant Stay"
-					},
-					"Display": {
-						"Top": 86,
-						"Left": 230
-					},
-					"Command": {
-						"Service": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-						"Action": "InstantStayArm",
-					}
-				},
-				{
-					"ControlType":"button",
-					"Label": {
-						"lang_tag": "with_code",
-						"text": "Night"
-					},
-					"Display": {
-						"Top": 86,
-						"Left": 340
-					},
-					"Command": {
-						"Service": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-						"Action": "NightArm",
-					}
-				},
-				{
-					"ControlType":"button",
-					"Label": {
-						"lang_tag": "disarm",
-						"text": "Disarm"
-					},
-					"Display": {
-						"Top": 86,
-						"Left": 450
-					},
-					"Command": {
-						"Service": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-						"Action": "Disarm",
-					}
-				}
-			]
-		},
-        {
-            "Label": {
-                "lang_tag": "notifications",
-                "text": "Notifications" 
-            },
-            "Position": "5",
-            "TabType": "javascript",
-            "ScriptName": "shared.js",
-            "Function": "device_notifications" 
-        },
-        {
-            "Label": {
-                "lang_tag": "settings",
-                "text": "Settings" 
-            },
-            "Position": "1",
-            "TabType": "javascript",
-            "ScriptName": "shared.js",
-            "Function": "simple_device" 
-        },
-        {
-            "Label": {
-                "lang_tag": "advanced",
-                "text": "Advanced" 
-            },
-            "Position": "2",
-            "TabType": "javascript",
-            "ScriptName": "shared.js",
-            "Function": "advanced_device" 
-        } 
-    ],
-    "sceneList": {
-        "group_1": {
-            "cmd_1": {
-                "label": "Disarm",
-                "serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-                "action": "Disarm"
-            },
-            "cmd_2": {
-                "label": "Away Arm",
-                "serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-                "action": "AwayArm"
-            },
-            "cmd_3": {
-                "label": "Stay Arm",
-                "serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-                "action": "StayArm"
-            },
-            "cmd_4": {
-                "label": "Instant Stay Arm",
-                "serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-                "action": "InstantStayArm"
-            },
-            "cmd_5": {
-                "label": "Night Arm",
-                "serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-                "action": "NightArm"
-            },
-            "cmd_6": {
-                "label": "Instant Night Arm",
-                "serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-                "action": "InstantNightArm"
-            },
-            "cmd_7": {
-                "label": "Vacation Arm",
-                "serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-                "action": "VacationArm"
-            }
-        }
-    },
-    "eventList": {
-		"event_1":{
-			"label":"Partition Arming Status Changes",
-			"serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-			"argumentList": {
-				"argument_1": {
-					"dataType": "string",
-					"defaultValue": "Ready",
-					"name": "ArmingStatus",
-					"allowedValueList": {
-                            "Disarmed":"Disarmed",
-                            "Armed_Away":"Armed Away",
-                            "Armed_Stay":"Armed Stay",
-                            "Armed_Stay_Instant":"Armed Stay Instant",
-                            "Armed_to_Night":"Armed to Night",
-                            "Armed_to_Night_Instant":"Armed to Night Instant",
-                            "Armed_to_Vacation":"Armed to Vacation"
-                    },
-					"comparisson": "=",
-					"prefix": "Which Arming Status",
-					"suffix": ""
-				}
-			}
-		},
-		"event_2":{
-			"label":"Partition Arm Up Status Changes",
-			"serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-			"argumentList": {
-				"argument_1": {
-					"dataType": "string",
-					"name": "ArmUpStatus",
-					"allowedValueList": {
-                            "Not_Ready_To_Arm":"Not Ready To Arm",
-                            "Ready_To_Arm":"Ready To Arm",
-                            "Ready_To_Arm_Zone_Violated":"Ready To Arm Zone Violated",
-                            "Armed_Exit_timer_active":"Armed Exit timer active",
-                            "Armed_Fully":"Armed Fully",
-                            "Force_Armed":"Force Armed",
-                            "Armed_with_Bypass":"Armed with Bypass"
-                    },
-					"comparisson": "=",
-					"prefix": "Which Arm Up Status",
-					"suffix": ""
-				}
-			}
-		},
-		"event_3":{
-			"label":"Partition Alarm Status Changes",
-			"serviceId": "urn:micasaverde-com:serviceId:ElkAlarmPartition1",
-			"argumentList": {
-				"argument_1": {
-					"dataType": "string",
-					"name": "AlarmStatus",
-					"allowedValueList": {
-                            "No_Alarm":"No Alarm",
-                            "Entrance_Delay":"Entrance Delay",
-                            "Alarm_Abort_Delay":"Alarm Abort Delay",
-                            "Fire_Alarm":"Fire Alarm",
-                            "Medical_Alarm":"Medical Alarm",
-                            "Police_Alarm":"Police Alarm",
-                            "Burglar_Alarm":"Burglar Alarm",
-                            "Aux1_Alarm":"Aux1 Alarm",
-                            "Aux2_Alarm":"Aux2 Alarm",
-                            "Aux3_Alarm":"Aux3 Alarm",
-                            "Aux4_Alarm":"Aux4 Alarm",
-                            "Carbon_Monoxide_Alarm":"Carbon Monoxide Alarm",
-                            "Emergency_Alarm":"Emergency Alarm",
-                            "Freeze_Alarm":"Freeze Alarm",
-                            "Gas_Alarm":"Gas Alarm",
-                            "Heat_Alarm":"Heat Alarm",
-                            "Water_Alarm":"Water Alarm",
-                            "Fire_Supervisory_State":"Fire Supervisory State",
-                            "Verify_Fire_State":"Verify Fire State"
-                    },	
-					"comparisson": "=",
-					"prefix": "Which Alarm Status",
-					"suffix": ""
-				}
-			}
-		}
-    },
-    "DeviceType": "urn:schemas-micasaverde-com:device:ElkAlarmPartition:1",
-    "device_type": "urn:schemas-micasaverde-com:device:ElkAlarmPartition:1" 
-}
Index: ElkAlarmPartition.xml
===================================================================
--- /D_ElkAlarmPartition.xml	(revision 1)
+++ 	(revision )
@@ -1,20 +1,0 @@
-<?xml version="1.0"?>
-<root xmlns="urn:schemas-upnp-org:device-1-0">
-  <specVersion>
-    <major>1</major>
-    <minor>0</minor>
-  </specVersion>
-  <device>
-    <deviceType>urn:schemas-micasaverde-com:device:ElkAlarmPartition:1</deviceType>
-    <staticJson>D_ElkAlarmPartition.json</staticJson>
-    <friendlyName>Elk Alarm Panel Zone</friendlyName>
-    <manufacturer>Elk</manufacturer>
-    <serviceList>
-      <service>
-        <serviceType>urn:schemas-micasaverde-com:service:ElkAlarmPartition:1</serviceType>
-        <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
-        <SCPDURL>S_ElkAlarmPartition.xml</SCPDURL>
-      </service>
-    </serviceList>
-  </device>
-</root>
Index: ElkAlarmPanel.xml
===================================================================
--- /I_ElkAlarmPanel.xml	(revision 1)
+++ 	(revision )
@@ -1,349 +1,0 @@
-<?xml version="1.0"?>
-<implementation>
-  <functions>
-  
-    local partitions={}
-    
-	function lug_startup(lul_device)
-		if( luup.io.is_connected(lul_device)==false ) then
-			luup.log('No port for Elk',1)
-			luup.task('Choose the Serial Port for the Elk Alarm',2,'Elk Alarm Interface',-1)
-			return false
-		end
-		
-		luup.log('About to get root device')
-		local rootdev = luup.chdev.start (lul_device)
-		
-		for i=1,8 do
-          s=string.format("%1d", i)
-          luup.log("Adding partition "..s)
-          luup.chdev.append(lul_device,rootdev,s,"Elk Partition "..s,"urn:schemas-micasaverde-com:device:ElkAlarmPartition:1","D_ElkAlarmPartition.xml","","",false)
-        end
-        luup.chdev.sync(lul_device,rootdev)
-        
-        for k,v in pairs (luup.devices) do
-            if v.device_num_parent == lul_device then
-                partitions[tonumber(v.id)] = k
-                luup.log("Partition #" .. v.id .. " is device#" .. k)
-            end
-        end
-        
-        
-		luup.log("Elk Alarm " .. lul_device .. "started")
-	end
-	
-   
-	
-
-	-- sends data, returns false on error (and logs message)
-	function send_command (cmd, data)
-		if luup.io.write (buildcommand(cmd, data)) == false then
-			luup.log (string.format ('cannot send command: "%s(%s)"', cmd, data))
-			luup.set_failure (true)
-			return false
-		end
-		return true
-	end
-	
-	function send_data(message)
-        local buffer = btos(#message+4) .. message .. "00"
-        buffer = buffer .. checksum(buffer) .. string.char(0xd, 0xa)
-        if luup.io.write (buffer) == false then
-            luup.log('cannot send data: %s', buffer)
-            return false
-        end
-        return true
-    end
-    
-    
-    function checksum(message,cc)
-        if cc == nil then
-            cc = 0
-        end
-        local i
-        for i = 1, #message do
-            cc = cc + message:byte(i)
-            cc = cc &#37; 256
-        end
-        cc = 255-cc
-        cc = cc+1
-        cc = cc&#37;256
-        local byte1 = btos(cc)
-        return byte1
-    end
-    
-    function isvalid(message)
-        local iLength = stob(2,message)
-        local iCheck = stob(2,string.sub(message,iLength+1, iLength+3))
-        print(iCheck)
-        return checksum(string.sub(message,1,iLength),iCheck) == "00"
-    end
-    
-    
-    function stob(length,message)
-        if length &gt; #message then
-            return -1
-        end
-        local retval = 0
-        local i
-        for i = 1, length do
-            retval = retval * 16
-            local x = message:byte(i)
-            if x&gt;64 then
-                x = x-55
-            else
-                x = x - 48
-            end
-            retval = retval + x
-        end
-        return retval
-    end
-    
-    function btos(byte)
-        local nib1 = ntos(byte/16)
-        byte = byte * 16
-        byte = byte&#37;256
-        byte = byte / 16
-        local nib2 = ntos(byte)
-        return nib1 .. nib2
-    end
-    
-    function ntos(nibble)
-        local nibb = math.floor(nibble)
-        if nibb &lt; 10 then
-            return string.char(nibb+48)
-        else
-            return string.char(nibb+55)
-        end
-    end	
-    
-    
-	function got_input (s)
-        if(isvalid(s) == false) then
-    	   luup.log('Error: data had invalid checksum')
-    	   return false
-	    end
-	    local length = stob(2,string.sub(s,1,2))
-	    local type = string.sub(s,3,3)
-	    local subtype = string.sub(s,4,4)
-	    local cmddata = ''
-	    if length &gt; 6 then
-	       cmddata = string.sub(s,5,length-2)
-        end
-    	luup.log (string.format ('%s|%s|%s', type, subtype, cmddata))
-    	if type=='A' and subtype=='S' then
-    	   return arming_status(cmddata)
-    	elseif type=='Z' and subtype=='C' then
-    		return zone_status(cmddata)
-    	else
-    		return true
-    	end
-	end
-	
-	function arming_status(s)
-	   local temp = ''
-	   for i = 1,8 do
-	       temp = arming_status_lookup(string.sub(s,i,i))
-	       luup.variable_set('urn:micasaverde-com:serviceId:ElkAlarmPartition1', 'ArmingStatus', temp, partitions[i])
-	   end
-	   for i = 9,16 do
-	       temp = armup_status_lookup(string.sub(s,i,i))
-	       luup.variable_set('urn:micasaverde-com:serviceId:ElkAlarmPartition1', 'ArmUpStatus', temp, partitions[i%8])
-	   end
-	   for i = 17,24 do
-	       temp = alarm_status_lookup(string.sub(s,i,i))
-	       luup.variable_set('urn:micasaverde-com:serviceId:ElkAlarmPartition1', 'AlarmStatus', temp, partitions[i%8])
-       end
-       return true
-	end
-	
-	function arming_status_lookup(s)
-	    if s == '0' then
-	       return 'Disarmed'
-        elseif s == '1' then
-            return 'Armed Away'
-        elseif s == '2' then
-            return 'Armed Stay'
-        elseif s == '3' then
-            return 'Armed Stay Instant'
-        elseif s == '4' then
-            return 'Armed to Night'
-        elseif s == '5' then
-            return 'Armed to Night Instant'
-        elseif s == '6' then
-            return 'Armed to Vacation'
-        end
-        return 'Disarmed'
-	end
-	function armup_status_lookup(s)
-	    if s == '0' then
-	       return 'Not Ready To Arm'
-        elseif s == '1' then
-            return 'Ready To Arm'
-        elseif s == '2' then
-            return 'Ready To Arm Zone Violated'
-        elseif s == '3' then
-            return 'Armed Exit timer active'
-        elseif s == '4' then
-            return 'Armed Fully'
-        elseif s == '5' then
-            return 'Force Armed'
-        elseif s == '6' then
-            return 'Armed with Bypass'
-        end
-        return 'Not Ready To Arm'
-	end
-	function alarm_status_lookup(s)
-	   if s == '0' then
-	       return 'No Alarm'
-        elseif s == '1' then
-            return 'Entrance Delay'
-        elseif s == '2' then
-            return 'Alarm Abort Delay'
-        elseif s == '3' then
-            return 'Fire Alarm'
-        elseif s == '4' then
-            return 'Medical Alarm'
-        elseif s == '5' then
-            return 'Police Alarm'
-        elseif s == '6' then
-            return 'Burglar Alarm'
-        elseif s == '7' then
-            return 'Aux1 Alarm'
-        elseif s == '8' then
-            return 'Aux2 Alarm'
-        elseif s == '9' then
-            return 'Aux3 Alarm'
-        elseif s == ':' then
-            return 'Aux4 Alarm'
-        elseif s == ';' then
-            return 'Carbon Monoxide Alarm'
-        elseif s == '&lt;' then
-            return 'Emergency Alarm'
-        elseif s == '=' then
-            return 'Freeze Alarm'
-        elseif s == '&gt;' then
-            return 'Gas Alarm'
-        elseif s == '?' then
-            return 'Heat Alarm'
-        elseif s == '@' then
-            return 'Water Alarm'
-        elseif s == 'A' then
-            return 'Fire Supervisory State'
-        elseif s == 'B' then
-            return 'Verify Fire State'
-        end
-        return 'No Alarm'
-	end
-	
-	function zone_status(s)
-	   local zone_num = tonumber(string.sub(s,1,3))
-	   local status_code = string.sub(s,4,4)
-	   local temp = zone_status_lookup(status_code)
-       if temp ~= 'null' then
-           if temp == 'Trouble' or temp == 'Violated' then
-               luup.variable_set('urn:micasaverde-com:serviceId:ElkAlarmPanel1', 'ZoneTripped', "" .. zone_num, luup.device )
-           end
-       end
-	end
-	
-	function zone_status_lookup(s)
-	   if s == '0' then
-	       return 'null'
-       elseif s == '1' or s == '2' or s == '3' then
-           return 'Normal'
-       elseif s == '5' or s == '6' or s == '7' then
-           return 'Trouble'
-       elseif s == '9' or s == 'A' or s == 'B' then
-           return 'Violated'
-       else
-           return 'null'
-       end
-	end
-	
-	function arm_partition(partition_num,arm_level)
-	   luup.log('arming a partition')
-	   luup.log('our id is: ' .. luup.device)
-	   luup.log('trying to arm partition ' .. partition_num)
-	   local user_code = luup.variable_get('urn:micasaverde-com:serviceId:ElkAlarmPanel1','accesscode', luup.device)
-	   local command = 'a' .. arm_level .. partition_num .. user_code
-	   return send_data(command)
-	end
-	
-  </functions>
-  <startup>lug_startup</startup>
-  <incoming>
-	<lua>
-		got_input(tostring(lul_data))
-	</lua>
-  </incoming>
-  <actionList>
-    <action>
-        <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPanel1</serviceId>
-        <name>SetAccessCode</name>
-        <run>
-            luup.log('setting access code to: ' .. lul_settings.NewAccessCode)
-            luup.variable_set('urn:micasaverde-com:serviceId:ElkAlarmPanel1', 'accesscode', lul_settings.NewAccessCode)
-        </run>
-    </action>
-    <action>
-      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
-      <name>Disarm</name>
-      <run>
-        local index = tonumber(lul_settings.DeviceNum)
-        return arm_partition(luup.devices[index].id,0)
-      </run>
-    </action>
-    <action>
-      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
-      <name>AwayArm</name>
-      <run>
-        local index = tonumber(lul_settings.DeviceNum)
-        return arm_partition(luup.devices[index].id,1)
-      </run>
-    </action>
-    <action>
-      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
-      <name>StayArm</name>
-      <run>
-        local index = tonumber(lul_settings.DeviceNum)
-        return arm_partition(luup.devices[index].id,2)
-      </run>
-    </action>
-    <action>
-      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
-      <name>InstantStayArm</name>
-      <run>
-        local index = tonumber(lul_settings.DeviceNum)
-        return arm_partition(luup.devices[index].id,3)
-      </run>
-    </action>
-    <action>
-      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
-      <name>NightArm</name>
-      <run>
-        local index = tonumber(lul_settings.DeviceNum)
-        return arm_partition(luup.devices[index].id,4)
-      </run>
-    </action>
-    <action>
-      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
-      <name>InstantNightArm</name>
-      <run>
-        local index = tonumber(lul_settings.DeviceNum)
-        return arm_partition(luup.devices[index].id,5)
-      </run>
-    </action>
-    <action>
-      <serviceId>urn:micasaverde-com:serviceId:ElkAlarmPartition1</serviceId>
-      <name>VacationArm</name>
-      <run>
-        local index = tonumber(lul_settings.DeviceNum)
-        return arm_partition(luup.devices[index].id,6)
-      </run>
-    </action>
-  </actionList>
-  <settings>
-    <handleChildren>1</handleChildren>
-  </settings>
-</implementation>
Index: k.lua
===================================================================
--- /elk.lua	(revision 1)
+++ 	(revision )
@@ -1,78 +1,0 @@
-function send(message)
-    local buffer = btos(#message+4) .. message .. "00"
-    buffer = buffer .. checksum(buffer) .. string.char(0xd, 0xa)
-    print(isvalid(buffer .. checksum(buffer)))
-    print(buffer)
-end
-
-function checksum(message)
-    return checksum2(message,0)
-end
-
-function checksum2(message,cc)
-    local i
-    for i = 1, #message do
-        print('message: ' .. message .. ' i: ' .. i .. ' cc so far: ' ..cc .. ' byte value of char: ' .. message:byte(i))
-        cc = cc + message:byte(i)
-        cc = cc % 256
-    end
-    cc = 255-cc
-    cc = cc+1
-    cc = cc%256
-    local byte1 = btos(cc)
-    return byte1
-end
-
-function isvalid(message)
-    local iLength = stob(2,message)
-    print(iLength)
-    local iCheck = stob(2,string.sub(message,iLength+1, iLength+3))
-    print(iCheck)
-    return checksum2(string.sub(message,1,iLength),iCheck) == "00"
-end
-
-
-function stob(length,message)
-    if length > #message then
-        return -1
-    end
-    local retval = 0
-    local i
-    for i = 1, length do
-        retval = retval * 16
-        local x = message:byte(i)
-        if x>64 then
-            x = x-55
-        else
-            x = x - 48
-        end
-        retval = retval + x
-    end
-    return retval
-end
-
-function btos(byte)
-    local nib1 = ntos(byte/16)
-    byte = byte * 16
-    byte = byte%256
-    byte = byte / 16
-    local nib2 = ntos(byte)
-    return nib1 .. nib2
-end
-
-function ntos(nibble)
-    if nibble < 10 then
-        return string.char(nibble+0x30)
-    else
-        return string.char(nibble+0x37)
-    end
-end
-
-msg="06zd00"
-
-print(stob(2,"23"))
-
-send("zt123")
-send("zv123")
-
-print(isvalid("0FEE100600600013"))
