-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathJ_CaddxNX584SerialConnection.js
315 lines (293 loc) · 13.4 KB
/
J_CaddxNX584SerialConnection.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/*
* Generic serial-port setup tab
* for devices which talk to a serial port directly attached to Vera
* or which can open a TCP socket to an IPv4 host.
*
* How to use this tab in your plugin:
*
* 1. In your static JSON file, define a tab like this:
* {
* "Label": {
* "lang_tag": "connection",
* "text": "Connection"
* },
* "Position": "1",
* "TabType": "javascript",
* "ScriptName": "J_SerialConnection.js",
* "Function": "serialConnection"
* },
*
* 2. In your Lua code startup function:
* - Check for the "urn:micasaverde-com:serviceId:HaDevice1"/"IODevice" variable.
* If set and not empty, this means a "local" serial port has been assigned by
* the user (it may be IPSerial or a physical serial port). Do nothing more.
* - Check for the "ip" special variable.
* If set and of the form "1.1.1.1:123", this means a TCP socket to an IPv4 host
* on the specified port should be opened using luup.io.open().
* If set and of the form "1.1.1.1", this means that the TCP port is a "default"
* value, which may mean something useful or not, depending on the plugin.
* If your plugin's implementation uses <ioPort> then Luup may open a
* connection for you anyway. Use luup.io.open(), do nothing, or signal an error.
* Other forms are reserved (for IPv6 or named hosts).
* - If neither is set, return false from the startup to signal an error.
*
* Copyright 2012 Deborah Pickett (futzle)
* This is free software and may be used under GPLv2 or BSD licences.
*/
var numberPattern255 = "(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])";
var numberPattern65535 = "(\\d|[1-9]\\d|[1-9]\\d\\d|[1-9]\\d\\d\\d|[1-5]\\d\\d\\\d\\d|6[0-4]\\d\\d\\d|65[0-4]\\d\\d|655[0-2]\\d|6553[0-5])";
var ipv4Pattern = new RegExp("^" + numberPattern255 + "\\."
+ numberPattern255 + "\\." + numberPattern255 + "\\." + numberPattern255 + "$");
var ipv4PortPattern = new RegExp("^(" + numberPattern255 + "\\."
+ numberPattern255 + "\\." + numberPattern255 + "\\." + numberPattern255 + "):("
+ numberPattern65535 + ")$");
var entityMap = {
"&": "&",
"<": "<",
">": ">",
'"': '"',
"'": ''',
"/": '/'
};
function escapeHtml(string) {
return String(string).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s];
});
}
function getConnectionType(deviceId)
{
var ioDevice = get_device_state(deviceId, "urn:micasaverde-com:serviceId:HaDevice1", "IODevice", 0);
var ipDevice = jQuery.grep(jsonp.ud.devices, function(o, i) { return o.id == deviceId; })[0];
if (ioDevice != undefined && ioDevice != "") { return "ioDevice"; }
if (ipDevice != undefined)
{
if (ipDevice.ip != "")
{
return "ip";
}
}
return "none";
}
/* Get a list of all serial devices. */
function getSerialDevices(deviceId)
{
return jQuery.grep(jsonp.ud.devices, function(d) {
return d.device_type == "urn:micasaverde-org:device:SerialPort:1";
} );
}
/* Find the device which has the given device ID
as its "IODevice" variable. This is the device
that "owns" the given serial device. */
function getDeviceOfSerialDevice(serialDeviceId)
{
var matches = jQuery.grep(jsonp.ud.devices, function(d) {
return jQuery.grep(d.states, function(state) {
return state.service == "urn:micasaverde-com:serviceId:HaDevice1" &&
state.variable == "IODevice" &&
state.value == serialDeviceId;
} ).length > 0;
} );
if (matches.length > 0) { return matches[0]; }
return undefined;
}
/* Get the IP address, coded in the ip special variable. */
function getIpAddress(deviceId)
{
var ipDevice = jQuery.grep(jsonp.ud.devices, function(o, i) { return o.id == deviceId; })[0];
var ip = ipDevice.ip;
if (ip == undefined) { return undefined; }
if (ip == "") { return undefined; }
if (ipv4Pattern.test(ip)) { return ip; }
var ipv4Match = ipv4PortPattern.exec(ip);
if (ipv4Match != undefined) { return ipv4Match[1]; }
return undefined;
}
/* Get the TCP port, coded in the ip special variable. */
function getTcpPort(deviceId)
{
var ipDevice = jQuery.grep(jsonp.ud.devices, function(o, i) { return o.id == deviceId; })[0];
var ip = ipDevice.ip;
if (ip == undefined) { return undefined; }
if (ip == "") { return undefined; }
if (ipv4Pattern.test(ip)) { return undefined; }
var ipv4Match = ipv4PortPattern.exec(ip);
if (ipv4Match != undefined) { return ipv4Match[6]; }
return undefined;
}
/* Mess with the DOM to disable parts of the UI. */
function enableSelectedOption(deviceId, currentConnectionType, ownedSerialDeviceId)
{
jQuery('#serialDevice').get(0).disabled = true;
jQuery('#serialSpeed').get(0).disabled = true;
jQuery('#serialData').get(0).disabled = true;
jQuery('#serialParity').get(0).disabled = true;
jQuery('#serialStop').get(0).disabled = true;
jQuery('#ipaddress').get(0).disabled = true;
jQuery('#tcpport').get(0).disabled = true;
if (currentConnectionType == "ioDevice")
{
jQuery('#serialDevice').get(0).disabled = false;
if (ownedSerialDeviceId != undefined && ownedSerialDeviceId != "")
{
var serialSpeed = get_device_state(ownedSerialDeviceId, "urn:micasaverde-org:serviceId:SerialPort1", "baud", 0);
if (serialSpeed == undefined || serialSpeed == "") serialSpeed = "9600"; // Default
jQuery('#serialSpeed').val(serialSpeed).get(0).disabled = false;
var serialData = get_device_state(ownedSerialDeviceId, "urn:micasaverde-org:serviceId:SerialPort1", "databits", 0);
if (serialData == undefined || serialData == "") serialData = "8"; // Default
jQuery('#serialData').val(serialData).get(0).disabled = false;
var serialParity = get_device_state(ownedSerialDeviceId, "urn:micasaverde-org:serviceId:SerialPort1", "parity", 0);
if (serialParity == undefined || serialParity == "") serialParity = "none"; // Default
jQuery('#serialParity').val(serialParity).get(0).disabled = false;
var serialStop = get_device_state(ownedSerialDeviceId, "urn:micasaverde-org:serviceId:SerialPort1", "stopbits", 0);
if (serialStop == undefined || serialStop == "") serialStop = "1"; // Default
jQuery('#serialStop').val(serialStop).get(0).disabled = false;
}
}
if (currentConnectionType == "ip")
{
jQuery('#ipaddress').get(0).disabled = false;
jQuery('#tcpport').get(0).disabled = false;
}
}
/* Set the IODevice variable, when
the radio button for "serial port" is selected,
or when the dropdown changes. */
function setSerialDevice(deviceId, serialDeviceId)
{
set_device_state(deviceId, "urn:micasaverde-com:serviceId:HaDevice1", "IODevice", serialDeviceId, 0);
var ipDevice = jQuery.grep(jsonp.ud.devices, function(o, i) { return o.id == deviceId; })[0];
ipDevice.ip = "";
enableSelectedOption(deviceId, "ioDevice", serialDeviceId);
}
/* Set the IODevice serial properties (speed, parity, etc.). */
function setSerialProperties(deviceId, serialDeviceId)
{
set_device_state(serialDeviceId, "urn:micasaverde-org:serviceId:SerialPort1", "baud", jQuery('#serialSpeed').val(), 0);
set_device_state(serialDeviceId, "urn:micasaverde-org:serviceId:SerialPort1", "databits", jQuery('#serialData').val(), 0);
set_device_state(serialDeviceId, "urn:micasaverde-org:serviceId:SerialPort1", "parity", jQuery('#serialParity').val(), 0);
set_device_state(serialDeviceId, "urn:micasaverde-org:serviceId:SerialPort1", "stopbits", jQuery('#serialStop').val(), 0);
}
/* Set the ip special variable, when
the radio button for "serial proxy" is selected,
or when the two text fields change. */
function setIPDevice(deviceId, ipAddress, tcpPort)
{
if (jQuery('#tcpport').get(0).disabled || tcpPort == undefined || tcpPort == "")
{
var ipDevice = jQuery.grep(jsonp.ud.devices, function(o, i) { return o.id == deviceId; })[0];
ipDevice.ip = "";
}
else
{
var ipDevice = jQuery.grep(jsonp.ud.devices, function(o, i) { return o.id == deviceId; })[0];
ipDevice.ip = ipAddress + ":" + tcpPort;
}
set_device_state(deviceId, "urn:micasaverde-com:serviceId:HaDevice1", "IODevice", "", 0);
enableSelectedOption(deviceId, "ip", undefined);
}
/* Fetch the named file over Ajax and execute function
f() on the resulting XML. */
function withUpnpFileName(deviceId, filename, f)
{
new Ajax.Request("../port_3480/" + filename, {
onSuccess : function(response) {
return f(response.responseXML);
}
}
);
}
/* Fetch the Implementation file (I_*.xml) and
execute the function f() on the resulting XML. */
function withImplementationFile(deviceId, f)
{
var device = jQuery.grep(jsonp.ud.devices, function(o, i) { return o.id == deviceId; })[0];
if (device.impl_file != "")
{
return withUpnpFileName(deviceId, device.impl_file, f);
}
else
{
return withUpnpFileName(deviceId, device.device_file, function(doc) {
var implementationElement = doc.querySelector("root > device > implementationList > implementationFile");
if (implementationElement != undefined)
{
var implementationFile = implementationElement.textContent.trim();
return withUpnpFileName(deviceId, implementationFile, f);
}
});
}
}
/* Entry point. This function sets the HTML for the tab. */
function serialConnection(deviceId)
{
var currentConnectionType = getConnectionType(deviceId);
var htmlResult = "<div>";
htmlResult += "<form>";
htmlResult += "<p>Select how the serial device is connected.</p>";
/* Serial port (including IPSerial) */
htmlResult += "<p><input type='radio' name='connectionType' value='serial' ";
if (currentConnectionType == "ioDevice") htmlResult += "checked='checked' ";
htmlResult += "onclick='setSerialDevice(" + deviceId + ",jQuery(\"#serialDevice\").val())'/>Serial port or IPSerial device ";
htmlResult += "<select id='serialDevice' onchange='setSerialDevice(" + deviceId + ",jQuery(\"#serialDevice\").val())'>";
htmlResult += "<option value=''>None</option>";
var serialDevices = getSerialDevices(deviceId);
var ownedSerialDeviceId;
jQuery.each(serialDevices, function(i, d) {
htmlResult += "<option value='" + d.id + "'";
var deviceOwner = getDeviceOfSerialDevice(d.id);
if (deviceOwner != undefined)
{
if (deviceId == deviceOwner.id)
{
// This is my serial device.
htmlResult += " selected='selected'";
ownedSerialDeviceId = d.id;
}
else
{
// Devices already owned by other plugins.
htmlResult += " disabled='disabled'";
}
}
htmlResult += ">" + escapeHtml(d.name);
// Tell user who owns this device (if it's not us).
if (deviceOwner != undefined && deviceOwner.id != deviceId)
{
htmlResult += " [" + escapeHtml(deviceOwner.name) + "]";
}
htmlResult += "</option>";
});
htmlResult += "</select>";
htmlResult += "</p>";
htmlResult += "<p style='margin-left: 4em;'>";
htmlResult += "Speed <select id='serialSpeed' onchange='setSerialProperties(" + deviceId + ",jQuery(\"#serialDevice\").val())'><option value='300'>300</option><option value='1200'>1200</option><option value='2400'>2400</option><option value='4800'>4800</option><option value='9600'>9600</option><option value='19200'>19200</option><option value='38400'>38400</option><option value='57600'>57600</option><option value='115200'>115200</option><option value='230400'>230400</option></select>";
htmlResult += " Data bits <select id='serialData' onchange='setSerialProperties(" + deviceId + ",jQuery(\"#serialDevice\").val())'><option value='7'>7</option><option value='8'>8</option></select>";
htmlResult += " Parity <select id='serialParity' onchange='setSerialProperties(" + deviceId + ",jQuery(\"#serialDevice\").val())'><option value='none'>None</option><option value='even'>Even</option><option value='odd'>Odd</option></select>";
htmlResult += " Stop bits <select id='serialStop' onchange='setSerialProperties(" + deviceId + ",jQuery(\"#serialDevice\").val())'><option value='0'>0</option><option value='1'>1</option><option value='2'>2</option></select>";
htmlResult += "</p>";
/* Serial proxy over Ethernet. */
htmlResult += "<p><input type='radio' name='connectionType' value='ip' ";
if (currentConnectionType == "ip") htmlResult += "checked='checked' ";
htmlResult += "onclick='setIPDevice(" + deviceId + ",jQuery(\"#ipaddress\").val(),jQuery(\"#tcpport\").val())'/>Serial proxy on another machine ";
var ipAddress = getIpAddress(deviceId);
if (ipAddress == undefined) { ipAddress = ""; }
htmlResult += "<p style='margin-left: 4em;'>IP address <input id='ipaddress' type='text' size='16' value='" + escapeHtml(ipAddress) + "' onchange='setIPDevice(" + deviceId + ",jQuery(\"#ipaddress\").val(),jQuery(\"#tcpport\").val())' />";
var tcpPort = getTcpPort(deviceId);
if (tcpPort == undefined) { tcpPort = ""; }
htmlResult += " TCP port <input id='tcpport' type='text' size='6' value='" + escapeHtml(tcpPort) + "' onchange='setIPDevice(" + deviceId + ",jQuery(\"#ipaddress\").val(),jQuery(\"#tcpport\").val())' /></p> ";
htmlResult += "</p>";
htmlResult += "</form>";
htmlResult += "</div>";
set_panel_html(htmlResult);
enableSelectedOption(deviceId, currentConnectionType, ownedSerialDeviceId);
/* See if this device has an <ioPort> element.
If so, don't let user edit the TCP port. */
withImplementationFile(deviceId, function(doc) {
var ioPortElement = doc.querySelector("implementation > settings > ioPort");
if (ioPortElement != undefined)
{
var ioPortHardcoded = ioPortElement.textContent.trim();
jQuery('#tcpport').val(ioPortHardcoded).get(0).disabled = true;
}
});
}