Developers who write plugins for devices that talk to serial ports: you might be interested in this JavaScript tab that I’m putting into my own plugins.
It’s a JavaScript tab that lets the user pick the serial device that the plugin needs to talk to. It works with IPSerial-type devices as well as with IP proxies where you specify an IP address and TCP port. It should also work for direct-connected USB devices, though that’s untested.
I’m planning to maintain this as a de facto standard plugin file that all developers are welcome to use in their own plugins. Feature requests are welcome. The aim is to keep users out of the Advanced tab at all costs.
Code and instructions for incorporating it into your plugin can be found here.
@mcvflorin,
It might make more sense to include this type of code into the standard tab/pane library. Having us all clone the code into our respective plugins, and give that lib a Unique name due to the file naming conventions, will be a little odd.
Doing it as a tab, instead of sending users to the Serial attachment UI (which is seriously buried in UI5) makes a lot of sense.
I think that it might be good to give this piece of JavaScript an apprenticeship, a bit like S_AlarmPartition2.xml did, so that it can evolve to fit everyone’s needs. It shouldn’t matter too much if multiple plugins want to install the same file, provided that developers keep updates roughly in sync. Even plugins on apps.mios.com should install the same filename from different plugins (this should be tested).
One omission I can already think of: it’s missing the baud/databits/stopbits/parity fields, without which we still have to send users to the “Develop Apps” page for physical serial ports.
can you have it read and set the {portnumber} data for the device?
That file can certainly be read by the JavaScript, but (someone correct me if I’m wrong) it can’t be written to. To be honest, I’m not clear on the semantics of so I don’t know how it should be incorporated into the UI on the tab. @autotoronto, please educate me.
Oh, and if anyone wants me to release it under a different open source licence to be compatible with their own plugins, PM me.
If you put an IP address in the ‘ip’ box in the advanced config tab for a device, and there’s a {portnum} - it goes next to the tag - then the Luup engine opens a connection for you automatically as if you had used luup.io.open(…). This is (now) documented on the wiki.
I don’t think you’d need to write to that file for port changes, but it would be nice to use the value of as a default if there’s nothing entered by the user.
I’m not sure how gets used if you want a serial port - could try a serial port id in there and see what happens but I don’t have any on my Vera to check.
Overall it would be really cool if you could tie into the automatic opening of the port by Luup and get rid of the luup.io.open(…) entirely.
Thanks for the info. It sounds like this is really something that affects the Lua code rather than the UI on this tab, so it’s only a little-bit related to what I’m trying to standardize.
The tab as it currently functions, you can put an IP address in the IP field, and leave the TCP port blank. In that case, provided that the Lua code in the plugin plays along, it’ll all happen just as you describe.
To that end, I think the best that the JavaScript can do is look for the device’s ioPort variable, and if it’s present, put a paragraph of text saying “TCP port defaults to 12345”. Whether the Lua code actually honours that, or whether it does a luup.io.open() anyway with its own port, is sort of up to the Lua code.
(Can you point at an existing plugin that acutally uses the ioPort value from the plugin file? I haven’t actually seen one.)
Thinking more about the device file’s ioPort element…
A plugin will either have this element in its D_*.xml I_*.xml file or not. If it has it, then clearly this plugin has some expectation that it needs to connect to the device at $ip on that TCP port. (Some dedicated Ethernet devices are like that, particularly the ones where we aren’t just doing a generic serial proxy.) Presenting a text box in the Connection tab getting the user to type a port number is misleading, since probably the plugin won’t even use it.
If the plugin doesn’t have an ioPort element, then the port number has to be provided some other way, which is what the Connection tab is already doing with its unsanctioned appending of the port number onto the special “ip” variable.
So I’m thinking of some logic like this:
Get the device file over AJAX, and look for an ioPort field.
If it’s present, make the TCP port field uneditable and put the value from the device file in there. Leave the IP address editable.
Otherwise the TCP port field remains editable.
There needs to be an understanding that the Lua code knows whether it’s supposed to expect the former or the latter convention, which isn’t unreasonable given that the author of the Lua code is probably the same as the author of the D_*.xml I_*.xml file.
How does that sound?
Edit: I_.xml file, not D_.xml file. I should stop writing posts from bed where I don’t have access to the files on Vera.
A plugin will either have this element in its D_*.xml file or not.
Actually mine's in the I_*.xml file...
I think what I’m really asking (takes me while to get there) is if you can override the tag at run-time.
If you can’t, then I wouldn’t worry about it with your code; let the module author decide if she wants to call luup.io.open(…) or use the auto-mechanism. If the latter there’s already an interface box for ip address, albeit not in its own tab.
If you can though, you might as well make your code override the in every case (i.e. set it if it’s not set) then nobody, ever, has to call luup.io.open(…) because your code will set the port to be opened automatically. And it means modules designed for tcp can run over serial, and vice-versa without any modification. That’s uber-cool. (Obvously the remote end needs to be adjusted to match).
Different question: do you prefer the two boxes over the ip_address:port syntax?
(Can you point at an existing plugin that acutally uses the ioPort value from the plugin file? I haven't actually seen one.)
Yes, I just wrote one, [url=http://forum.micasaverde.com/index.php/topic,9049.0.html]here.[/url]
The reason I think it’s important to use is because if MCV implements a feature, I think it should be generally be used, where appropriate, rather than making OS calls to do the same thing.
I’m pretty sure this is how it is. There just doesn’t seem to be any way for a plugin to even know that it’s running under the influence of an element, much less alter its behaviour.
Part of my aim is to keep users out of the Advanced tab, which is intimidating and far too easy to screw up by putting the wrong thing in the wrong field. The Connection tab I’m developing here in this thread is intended to protect users from Too Much Information. So I’m not fussed about repeating something that’s already in the Advanced tab.
Different question: do you prefer the two boxes over the ip_address:port syntax?
ip_address:port notation is the Antichrist. It glosses over the difference between TCP and UDP. It’s opaque and confusing for new users. It’s very, very not IPv6-friendly.
I hope I don’t sound like I’m overreacting.
(Can you point at an existing plugin that acutally uses the ioPort value from the plugin file? I haven't actually seen one.)
Yes, I just wrote one, [url=http://forum.micasaverde.com/index.php/topic,9049.0.html]here.[/url]
Brilliant. Nothing like a real-world example to motivate me.
There just doesn't seem to be any way for a plugin to even know that it's running under the influence of an element, much less alter its behaviour.
You don't need to 'know'. If your code is running then it can only be because the module requires comms. So then it's just a question of whether you can set the value, as well as read it. If it exists then the LuaUPnP will open the port. if it doesn't exist, let's set it with the data from your new tab, and (maybe) LuaUPnP will open that port too.
I wonder what the value of would be to open serial comms.
Seems to me the question is where in the module’s datastructure are these tags loaded at run-time? And when in the initialization of a module is the test on to determine whether to open a port? Can your code run before that point in time? If it can set that value wherever it needs to be, then the port gets opened for you, presto-hey.
I’ll do some experimenting on Sunday. Can’t tomorrow.
I take your points about keeping people out of the advanced tab etc.
(Minor rant: why won’t MCV let us bind to a port, as well? Or use UDP? Grrrrrr)
It would need to be whatever TCP port that the ser2net process that LuaUPnP created. Since that port number is apparently assigned randomly, It’d be silly to hardcode it into the in the implementation file.
(Stop me if I lost you with the part where LuaUPnP creates serial proxies. Luup plugins don’t ever talk directly to serial ports; there’s always a TCP-listening ser2net process passing the data back and forth to the true serial port.)
Seems to me the question is where in the module's datastructure are these tags loaded at run-time? And when in the initialization of a module is the test on to determine whether to open a port? Can your code run before that point in time? If it can set that value wherever it needs to be, then the port gets opened for you, presto-hey.
Not going to work. The first custom code that a plugin gets to run is the Lua code in the startup function. By that point, LuaUPnP has already made up its mind about what “serial” stream it’s going to give your plugin.
Includes fields for serial speed, data bits, parity and stop bits.
Disables the TCP Port field if the device’s implementation contains an element (and puts the value into the field), on the understanding that the plugin’s Lua is expecting LuaUPnP to open the port on its behalf.
Best Home Automation shopping experience. Shop at Ezlo!