Getting Started with Plugin Development on VeraEdge

Hullo all,

I’m attempting to make a plugin for a blinds controller, and I’m using the VeraEdge as my testing platform.
I’ve found some information regarding the Lua extensions, Luup declarations, Http requests, and all the other things that I expected to need to get started.
At this point, it seems that actually programming the necessary commands will be straightforward.
This leaves the matter of having the functions inserted into the system correctly.
However I hit a road bump even a little earlier than that.
I cannot manage to make a basic device.
All I have managed to find was the Somfy plugin tutorial (http://wiki.micasaverde.com/index.php/Luup_Somfy_Walkthrough).
I’ve tried to get through this tutorial a few times, but I can’t get past step 2b.

Has MiOS simply moved beyond what this tutorial covers now?
Is there a newer or more expansive tutorial available?
If no would someone be so kind as to describe in a brief step by step how one would make a virtual device and then execute an arbitrary lua function from a generated UI?

Thank you,
Calem

I’ve been dealing with the same frustration for a couple weeks now trying to write a plugin for the Monoprice 6x6 whole-house amplifier. The documentation is terrible and the learning curve is VERY steep, I’ve found. And, in a lot of ways its like programming back in the 70’s and 80’s – no reasonable ability to debug or diagnose when something breaks. It won’t even do a good, easily-searchable, error when it can’t parse your lua. And worse, you’ll spend a lot of time fighting with code that isn’t quite right because of bad documentation and discover it wasn’t working because of bugs in Vera itself.

I ended up buying ZeroBrane Studio for Vera, which has its own weird idiosyncrasies but made programming for Vera orders of magnitude easier. Its still a miserable process, but at least you get some minimal ability to do some debugging. Without it, you’re basically left digging through the Vera logs and instrumenting your code and hoping you can figure out why its dying before it logs anything.

While this will be very brief, maybe it’ll help you over a little bit of the hump. It took me a good bit of time to even understand WTF each file was doing. And as a caveat, I’ve been doing this for two weeks, so I can’t guarantee any of this is completely correct, but I know what tripped me up …

A device is made up of a bunch of files that the Vera core system uses to do three things:

  • Define a UPNP endpoint representing a device … if you don’t know what UPNP is, I suggest spending an hour reading up on it, because 3/4 of the Vera screwiness stems from UPNP screwiness.
  • Define code to maintain the status of UPNP variables, and respond to UPNP actions. (For example, “is this light turned on?” and “turn this light on” are examples of the two)
  • Define a UI to allow apps to interact with your UPNP endpoints, and apparently do so in the most convoluted manner possible. Note: I can’t confirm the latter was a design requirement…

If you think about what you’re doing in those three buckets, it gets a little clearer. And you need to do them in that order.

Defining the endpoint is the point of the D_Plugin.xml file. Read through the ones that came on the Vera. They’re actually pretty simple to follow. Its basically all metadata. There’s a bunch of fields about what the device is (friendlyName, deviceType, etc). There’s some stuff that relates to interacting with Vera’s IO subsystem (protocol, for example), but the IO subsystem is a trainwreck and I eventually gave up on it working reliably with mine. The most important thing is the service list. That’s a list of UPNP services your device is exposing. Very has a slew of built-in ones, and it makes interoperating a lot easier if you use them, but you can DIY. I had to do both. For example, urn:micasaverde-com:service:DiscretePower:1 has defined variables for a device being on/off and the commands to turn it on and off. If you use those, any controller that understands urn:micasaverde-com:service:DiscretePower:1 will work with yours. urn:schemas-micasaverde-com:service:Volume:1 has basic audio volume/mute controls. There’s a ton of them. You basically say “this is the service I’m exposing, and this S_*.xml file is how its defined.” S_DiscretePower.xml (which comes on the Vera) defines the DiscretePower endpoints.

So first thing first, figure out if you can map what you’re trying to do with existing endpoint, and include them. My amp controller has HaDevice, DiscretePower, SwitchPower (actions for toggling power), Volume, RenderingControl, and an internal one that has commands to do things like turn all the zones off at once.

Now that, by itself, won’t do much. If you add the device, as it says in the “tutorial”, it won’t even show up. You have to hit the data_request API endpoint (http://yourip:3480/data_request?id=user_data&output_format=xml) to even see it, and have to use the same APIs to delete it.

The other things the D_.xml file defines are links to the second and third bullets above – the implementation and the UI crap. The implemenationList/implementationFile entry points to a file or set of files that are implementing the services you listed in the D_.xml file. So if you have D_FooPlugin.xml, you probably want to create an I_FooPlugin.xml file that has the implementation.

Look at the other I_*.xml files on your Vera – they’re also fairly straightforward. But what Vera does when it loads them isn’t… and that’s the source of where most of the weirdness comes from.

There’s a lot of ways to use the I_*.xml files, and the “tutorial” is both confusing and doing it in a way that makes debugging much harder.

There’s two things in the file:

  • metadata for defining how it starts up and responds to the actions defined in your D_ file.
  • lua code

So, for example, if you’re implementing DiscretePower1, you need to have an action entry for Off and On (the two commands defined in it). Note, you don’t have to implement all of them, just the ones you need. Well, if you want to “play nice” with other systems, you may want to implement them all or make a copy of the S_* file and remove them, but to get going, don’t worry about it…

You also tell Vera what code to run at startup, with the “startup” element. Now this is where it gets weird… “Startup” has to point to a lua function, and the actions have to be lua code. Nice and inconsistent. Yay. So what happens is, at startup Vera calls your startup function. When it first loads your code, it creates functions with special names wrapping the action code. If you turn on verbose logging in Settings, you can see the code it generates. This will drive you bonkers because the code you write isn’t exactly what is running.

Basically it takes everything in the “functions” element, and the dynamic functions it creates from your action code and creates a lua file at runtime.

One problem? Its a damn XML file, so make sure you remember to escape illegal XML characters. “if (this > that) then” won’t run and won’t give you any sort of a useful error even in the logs. You need to say “if (this < that) then”.

The best work around? Don’t put any more code in that file than you need. Put it in an external file (say, L_MyPlugin.lua). You can list the files to include in the “files” element in the implemenation file. So put L_MyPlugin.lua in the I_ file. Put your startup methods in there, and put a single line to call a function in there in your actions. Then all of your “real” code is real lua code. And, if you use a tool like ZeroBrane Studio, you can actually quickly edit and debug it.

So, in theory, you now have a rudimentary device defined as a UPNP endpoint with actions associated with it, and code behind those actions. You can do something as simple as a luup.log(“DiscretePower1:Off”) and verify it works in a log file. You won’t have an on/off button in the UI, but you can trigger them with a UPNP explorer or via the web API.

The last bit, again referenced in the D_ file, is the JSON that defines the UI. Its, as far as I can tell, almost entirely undocumented and most of the documentation is wrong. And half the ones that are in there are broken in some way.

I don’t have any good tips for the JSON file. Its just terrible to work with. The things to know, that I found:

  • Once you have a JSON, the device will show up, and you can get into the advanced screen, etc.
  • You seem to need default_icon and state_icons. Without both the icon will disappear.
  • The “simple” examples are anything but. They include a lot of stuff you probably don’t need up front.
  • You can control which controls you add that show up on the device page vs details view with the controlgroup entry in the control. I don’t get exactly how that works, but it does.
  • I haven’t found any docs for what the control types all are. Just look at samples to find what you need.

The control JSON basically maps the UI actions back to the UPNP actions. For example, this is one of mine for muting a zone:
{
“ControlGroup”: “1”,
“ControlType”: “button”,
“top”: “2”,
“left”: “2”,
“Label”: {
“lang_tag”: “cmd_mute”,
“text”: “Mute”
},
“Display”: {
“Service”: “urn:upnp-org:serviceId:RenderingControl”,
“Variable”: “Mute”,
“Value”: “1”,
“Top”: 30,
“Left”: 460,
“Width”: 50,
“Height”: 20
},
“Command”: {
“Service”: “urn:micasaverde-com:serviceId:Volume1”,
“Action”: “Mute”,
“Parameters”: []
}
}

Label is the text on the button. Display seems to be related to both how to show it and controlling what it looks like. For example, the system checks the “Mute” variable in the RenderingControl UPNP endpoint and if its set to 1, the Mute button turns green to show its muting. The “command” element is what to do when clicked. In this case, call the “Mute” action in the Volume1 implementation.

Now, on the second bullet point up top, where I said you need to maintain state in your variables – this is where that comes into play. For example, this is the actual action called by the JSON above:

urn:micasaverde-com:serviceId:Volume1
Mute

– Toggle Mute
local isMuted = luup.variable_get(UPNP_RENDERING_CONTROL_SID, “Mute”, lul_device)
local desiredMute = 1 - (tonumber(isMuted) or 0)
sendZoneCommand(lul_device, “MU”, desiredMute)
luup.variable_set(UPNP_RENDERING_CONTROL_SID, “Mute”, desiredMute, lul_device)

Notice in there, I’m both calling variable_get and variable_set on the device to set the “Mute” variable in the RenderingControl UPNP service. That variable_set call is what sets the value the JSON picks up to make the button green or not. So in this case, I get out the current value (which I retrieve from the amp in the startup and set into there, or when it refreshes every thirty seconds), I invert it 1->0 or 0->1 and then write it back. That maintains the UI state. The sendZoneCommand is a function in the L_*.lua file.

I would suggest starting simple – get a device showing up, with a simple set of actions like DiscretePower, write the lua to at least log when the actions are taken, and make sure you understand how all the bits work together. And, if you really want to streamline it, but ZeroBrane. I made more progress in the first hour after I bought it than I had in the prior week of screwing around without it. Vera really should just buy it and give it away.

Anyway, I hope that helps and didn’t make things even more confusing.