Banging my head on URNs

I’m trying to finish off my first LUUP scene script, and am hitting an issue that should be easy to fix, but I’m just missing it. I am trying to trigger multiple devices using luup.call_action, but I guess I have a parameter not quite right. I noticed in the samples, the urn’s typically end in BinaryLight1 or DimmableLight1, but when I look at the devices in UI5 > advanced, I see BinaryLight:1 and DimmableLight:1. I also tried BinaryLight1 / DimmableLight1 - no change. I feel like I’m missing something basic here, but can’t figure out what it is.

Here’s the errors logged when I trigger my scene:
08 10/29/12 23:53:42.791 Scene::RunScene running 15 Keyfob lightbulb <0x2cedb680>
08 10/29/12 23:53:42.792 JobHandler_LuaUPnP::HandleActionRequest device: 5 service: urn:schemas-upnp-org:device:BinaryLight:1 action: ^[[36;1mSetTarget^[[0m <0x2cedb680>
08 10/29/12 23:53:42.792 JobHandler_LuaUPnP::HandleActionRequest argument newTargetValue=1 <0x2cedb680>
01 10/29/12 23:53:42.792 ^[[31;1mJobHandler_LuaUPnP::HandleActionRequest can’t find urn:schemas-upnp-org:device:BinaryLight:1^[[0m <0x2cedb680>
08 10/29/12 23:53:42.793 JobHandler_LuaUPnP::HandleActionRequest device: 3 service: urn:schemas-upnp-org:device:DimmableLight:1 action: ^[[36;1mSetLoadLevelTarget^[[0m <0x2cedb680>
08 10/29/12 23:53:42.793 JobHandler_LuaUPnP::HandleActionRequest argument newLoadLevelTarget=100 <0x2cedb680>
01 10/29/12 23:53:42.793 ^[[31;1mJobHandler_LuaUPnP::HandleActionRequest can’t find urn:schemas-upnp-org:device:DimmableLight:1^[[0m <0x2cedb680>

Hi Patrick,

There are two parallel namespaces, one called the Service Type and one called the Service ID. The mapping (normally 1:1 but not always) is in the device XML file (D_*.xml).

Most things Vera does are with the Service ID, so the string you need is the one that looks like “urn:upnp-org:serviceId:SwitchPower1”.

Is there any global table that happens to contain the mappings?

I built a table already that contains a mapping between the (probably incorrect) ServiceType, read method name, write method name, and name of parameter used in write function.

My end goal is to have a scene that collects the current state of N devices, triggers them “on” whether they’re switches or dimmers, then restores the original setting after a given time. For example - if I press the button on a remote, turn the driveway, entry lights, and certain interior lights on. If the driveway lights were already on due to another scene, leave them on after the timeout.

I’ll post the code tonight when I get home, hopefully the ServiceID will fix it.

You can look at the service id for the variables that your interested in by clicking on the advanced tab of the device and hovering your mouse over the variable you want.

  • Garrett

No, because each device type is allowed to have different mappings (and some do). The authoritative list of service ID → service type is in each device’s XML file.

That’s generally speaking. In practice you are working with only a very small set of device types, so you can get away with hard-coding them rather than looking them up in the XML file. (That is what I do with the Combination Switch.)

Do you have a way to look up device Ids without parsing XML?

Here’s the code as-is, it seems to be working well enough for now. It could be shorter, but it’s a pretty straightforward example as-is. I trigger it based on events from my alarm system to turn on entry/exit and some outdoor lights for 30 minutes.

-- Change this to reflect all devices to be triggered in the scene
local devicesToTrigger = {
  { Schema = "urn:upnp-org:serviceId:SwitchPower1", Device = 5 }, -- Driveway Light
  --{ Schema = "urn:upnp-org:serviceId:Dimming1", Device = 3 } -- Paper Lanterns
  { Schema = "urn:upnp-org:serviceId:SwitchPower1", Device = 3 } -- Paper Lanterns
}

-- Duration lights will be left on (15 minutes)
local waitInSeconds = 15 * 60

-- schema, readmethod, writemethod
local schemaToMethods = {}
schemaToMethods["urn:upnp-org:serviceId:SwitchPower1"] = { Read = "Status", Write="SetTarget", WriteParameter = "newTargetValue", OnValue = 1 }
schemaToMethods["urn:upnp-org:serviceId:Dimming1"] = { Read = "LoadLevelStatus", Write = "SetLoadLevelTarget", WriteParameter = "newLoadLevelTarget", OnValue = 100}

-- get & store current values, then set desired values
for i,v in ipairs(devicesToTrigger) do
  local currentDevice = v
  v.OriginalValue = luup.variable_get(v.Schema, schemaToMethods[v.Schema].Read, v.Device)
  v.ModifiedValue = schemaToMethods[v.Schema].OnValue
  luup.log("Device #" .. v.Device .. " Original Value " .. v.OriginalValue .. ", setting to " .. v.ModifiedValue) 
  luup.call_action(v.Schema,
                   schemaToMethods[v.Schema].Write,
                   { [schemaToMethods[v.Schema].WriteParameter] = v.ModifiedValue },
                   v.Device)
end

-- function to restore values later
function RestoreValues() 
  luup.log("Attempting to restore original values")
  for i,v in ipairs(devicesToTrigger) do
    local currentValue = luup.variable_get(v.Schema, schemaToMethods[v.Schema].Read, v.Device)
    currentValue = tonumber(currentValue)
    luup.log("Device #" .. v.Device .. " Current Value " .. currentValue .. " Original value " .. v.OriginalValue .. " Modified Value " .. v.ModifiedValue)
    if currentValue == v.ModifiedValue and currentValue ~= v.OriginalValue then
      -- restore original
      luup.log("Restoring original value for Device # " .. v.Device)
      luup.call_action(v.Schema,
      schemaToMethods[v.Schema].Write,
      { [schemaToMethods[v.Schema].WriteParameter] = v.OriginalValue },
      v.Device)
    end
  end
end

-- schedule timer to restore original values
luup.call_delay("RestoreValues", waitInSeconds)

I don’t think so.

In the Combination Switch plugin I get by with an approximation, using luup.device_supports_service(). It’s good enough though it seems to give some false positives.

At least Vera ships with a Lua XML library, so you’re not reduced to using string match patterns.