What about device scope variables? How do I store internal device attributes - they should be accessible between pieces of LUA code of the given device, but they shouldn’t override the same attribute of another device of the same kind.
What are visibility rules between variables owned by parent and child devices? Is there a kind of inheritance?
… read Luup Variables Wiki 3 times, but it’s way too short to answer my questions…
You mean ones you wrote? Or that come with the framework? Within the framework, there are only 2: lug_device, lug_room. (lug_ is our convention for ‘lua global’ vs. lul_). You shouldn’t ever modify or do anything but read lug_device, lug_room. As far as global variables you create, the scope is that any ‘top level’ device (the devices are nested in a tree–see user_data or the upnp device tree) has one scope for it and all it’s children.
If you create a serial device to control a TV, for example, and you have 2 such TV’s, then both are separate top level devices, and each will have their own instance, so there’s no conflict. The only time multiple devices share the same scope is with nested parent/child (ie embedded, like a “DVR” parent with multiple “Camera” children). But that’s always what you’d want, since in that case, all the devices actually are part of the same collection. if you had 2 DVR’s, each DVR would have it’s own instance, so there’d be no conflict; only the child devices (the cameras) within each DVR share an instance.
Does it mean that if I want to store my own device-scope attribute I have to find an appropriate service, or create one, and do setVariable and getVariable on it?
What if I need to “extend” existing service - to add a new parameter? Should I create a new service for that, or there’s more elegant way?
Does it mean that if I want to store my own device-scope attribute I have to find an appropriate service, or create one, and do setVariable and getVariable on it?
I’m not sure I understand the question… By device-scope attribute I assume you mean you want to have a variable, the value of which is preserved across Luup engine reloads, right? If so, the solution is to store them in a UPNP variable with setVariable. Vera saves the value of all the UPNP variables in the user_data config file, and keeps it current, and whenever the Luup engine reloads, it reads in those values and sets the initial values. So UPNP variables are preserved across reloads. Therefore, whenever we want to store some configuration data for a device, we always store it in the UPNP variable, which can be done with lu_SetVariable, or as a parameter to the lu_chdev_append. In both cases, Luup doesn’t validate that the variables actually exist. If you use a service+variable that is in the service spec file, then any UPNP Control Point will see the variable and the value. But if you call setVariable with “dummyservice”, “dummyvariable”, “somevalue” then Luup will still serialize the data, and when Vera is rebooted or Luup reloads, lu_getVariable for “dummyservice”, “dummyvariable” will still return “somevalue”, and, since dummyservice isn’t a valid UPNP service and doesn’t exist in the service spec xml file, no upnp control point will know that it’s there. so you can use this like a registry to store settings for your device.
What if I need to “extend” existing service - to add a new parameter? Should I create a new service for that, or there’s more elegant way?
AFAIK, the official UPNP position is that it’s ok to add extra actions/arguments, just not to remove/change the ones that are already there. Adding new actions/arguments to a service is considered benign since it won’t break the control point. However, we’ve avoided making modifications to any UPNP-forum sanctioned services/actions just to remain completely compliant. So if it’s a matter of adding an action/argument to an official UPNP service, I’d add my own service instead. On the other hand, if it’s an MCV service, then consider it ‘GPL’. Just tell me what arguments/actions you want added, and I’ll update the master file and push a new firmware. Our own custom services are considered fluid at the moment and subject to improvement. Also, if someone finds a upnp device/service that is gaining traction, we will drop ours and use that instead. For example, we couldn’t find any standard for a “UPNP door lock”, so we made our own up. But if there’s one that’s already in use by someone like Schlage, we’ll use that one instead.
Thinking forward, since we’re writing plugins, to be easily installable by other users they have to be decoupled from any other not built in resources, i.e. with least dependencies possible. If you guys allow modification of existing services, user will download them with the rest of the plugin files, it’ll be sure way to conflicts when plugins start to override each other’s resources…
Better way would be mimicking of inheritance, where plugin dev can specify “base” for his device or service, so the new one would take the base and add/override it with the changes.
Agreed about the inheritance. Your solution is cleaner. But, I fear it would break a lot of UPNP control points. If the control point reads a given ServiceType from one UPNP device, and then finds another device with the same ServiceType, afaik, it’s considered safe to not re-read the service definition but just use the existing one since the very nature of UPNP is that a given service identifier: XYZ is consistent across all devices.
WE could sort of get around this by adding unique version numbers: device 1 has the basic XYZ service type, device 2 adds a couple arguments to an action so we call the service type XYZ.b and so on. But I think we’re better off to do what the UPNP forum did, which is to try to finalize and set in stone the service/device specs, making the “ratified” version as future-proof and full-featured as possible so you don’t need to regularly add stuff to the specs.
Right now we consider the UPNP-ratified service/device files ‘set in stone’ and avoid touching them. The MCV service/device files are fluid at the moment, but once things finalize and we have some iPhone/Blackberry/etc. apps that are coded to work with them, then we’d like to set the MCV service/device files in stone too.
It’s never mandatory. Without any services the device won’t really do anything because it doesn’t have any actions or variables. But sometimes for virtual devices that simply serve as top-level devices for child devices that is ok. For example, there is the top level ‘micasaverde-com:serialportroot’ device. It doesn’t do anything except serve as a ‘folder’ or directory of the serial ports on a system. Similarly with the Somfy device, the top level Somfy device in D_Somfy… is just a controller for up to 16 blinds, which are child devices. The blinds, the child devices, are what have the actual actions (open/close) and variables (opened). The top level device, which is essentially the control module for all the blinds, doesn’t interact with the outside world. In the Somfy example, though, we gave the top level device an implementaton file (meaning the Lua file with all the code) and set the flag handleChildren=1 in the UPNP device file. This way all the actions/variables for the child devices, the blinds, are handled in the parent’s I_SomfyBlinds.xml. So, although the parent device doesn’t expose any actions/variables, it does handle all the actions/variables of the child devices. This is probably the normal setup for such a device.
What’s confusing here is that in very SomfyBlinds example tag isn’t used, not in top-level D_ file, nor in implementation file. The latter contains actions/variables, but where are the actual services listed?
I guess some alternative reference let Luup know what services SomfyBlinds employs, but I’m still not sure how. What I’m missing?
it should actually be the device type ‘window covering’, but it doesn’t matter–window coverings and binary lights both implement the services and behave the same way. If you look at ‘D_BinaryLight1.xml’, you’ll see it implements the service urn:upnp-org:serviceId:SwitchPower1 in S_SwitchPower1.xml, which contains the action SetTarget (to move the blind up or down).
So it’s the child device (the blind) that has the actual service/action which a upnp controller talks to. If the Somfy interface device (the parent) has 16 blinds (children), those 16 child devices show up in the upnp device tree and on vera’s interface, each with their own ‘on/off’ (ie up/down) buttons.
Now because D_SomfyBlinds.xml has handleChildren=1, that means a command that goes to any of those 16 blinds (children) should be processed in the parent devices implementation file: I_SomfyBlinds.xml. And in there you’ll see there’s an xml block for handling the SetTarget. That Lua code for SetTarget will be called with lul_device which is the actual target of the action (the child device). So the first thing the script does is lookup Somfy’s internal id for whatever blind is being controlled: luup.devices[lul_device].id. It then sends the command to the interface with that Somfy-specific ID for the blind.
That’s the way composite devices normally work. Like an alarm panel, which may have 100 sensors. Each sensor is it’s own ‘end point’ with it’s own states/actions/etc. So you create a single parent device (the alarm panel) which may or may not itself do anything, but it has an implementation file that knows how to talk to the alarm panel’s controller whenever something happens with one of the child devices (a sensor is tripped). Z-Wave works the same way. Inspect it in the UPNP tree and you’ll see a parent device (Z-Wave interface) with each Z-Wave node being a child. And composite nodes, like the express controls 3-in-1 sensor, has 3 children of it’s own (temp sensor, light sensor, motion sensor), since all 3 are effectively their own endpoints.
Best Home Automation shopping experience. Shop at Ezlo!