Skip to content

Commit ef95bd9

Browse files
committed
Initial Contribution
1 parent 41685ce commit ef95bd9

11 files changed

Lines changed: 726 additions & 0 deletions

File tree

device/GlobalCache/constants.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
3+
module.exports = Object.freeze({
4+
BEACON_TYPE_CC: "CC",
5+
BEACON_TYPE_IR: "IR",
6+
BEACON_TYPE_SR: "SR",
7+
8+
BEACON_UUID: "UUID",
9+
BEACON_MODEL: "Model",
10+
BEACON_URL: "Config-URL",
11+
12+
LINK_TYPE_3RELAY: "3 RELAY",
13+
LINK_TYPE_3IR: "3 IR",
14+
LINK_TYPE_1SERIAL: "1 SERIAL",
15+
LINK_TYPE_1IR: "1 IR",
16+
LINK_TYPE_1IRBLASTER: "1 IR_BLASTER",
17+
LINK_TYPE_1IRTRIPORT: "1 IRTRIPORT",
18+
LINK_TYPE_1IRTRIPORTBLASTER: "1 IRTRIPORT_BLASTER",
19+
20+
COMMAND_GETDEVICES: "getdevices",
21+
COMMAND_GETSTATE: "getstate",
22+
COMMAND_SETSTATE: "setstate",
23+
COMMAND_SENDIR: "sendir",
24+
25+
RESPONSE_SETSTATE: "setstate",
26+
RESPONSE_STATE: "state",
27+
RESPONSE_IR: "completeir",
28+
29+
PORT_NUMBER: 4998
30+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
const BluePromise = require('bluebird');
4+
5+
const discovery = require('./deviceDiscovery');
6+
const constants = require('./constants');
7+
const gcUtils = require('./gcUtils');
8+
9+
module.exports.switchSet = function (deviceid, value) {
10+
const valueToSet = value === "true" ? '1' : '0';
11+
gcUtils.parseDeviceId(deviceid)
12+
.then(function (args) {
13+
return Promise.all([args, discovery.getDevice(args.uuid)]);
14+
})
15+
.then(function (results) {
16+
return results[1].setState(results[0].module, results[0].port, valueToSet);
17+
})
18+
.then(function (rc) {
19+
if (rc !== valueToSet) {
20+
throw new Error("State was not correctly set on the device - expecting " + valueToSet + " but got " + rc);
21+
}
22+
})
23+
.catch(function (e) {
24+
console.error("Error setting switch value. ", e.message);
25+
});
26+
}
27+
28+
module.exports.switchGet = function (deviceid) {
29+
return gcUtils.parseDeviceId(deviceid)
30+
.then(function (args) {
31+
return Promise.all([args, discovery.getDevice(args.uuid)]);
32+
})
33+
.then(function (results) {
34+
return results[1].getState(results[0].module, results[0].port);
35+
})
36+
.then(function (state) {
37+
return state !== '0';
38+
});
39+
}
40+
41+
module.exports.discoverDevices = function () {
42+
return discovery
43+
.getDevicePorts(constants.BEACON_TYPE_CC)
44+
.map(function (port) {
45+
return gcUtils.createDeviceDiscovery(port);
46+
});
47+
}

device/GlobalCache/device.js

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
'use strict';
2+
3+
const BluePromise = require('bluebird');
4+
const net = require('net');
5+
const rl = require('readline');
6+
const util = require('util');
7+
8+
const constants = require('./constants');
9+
const DevicePort = require('./devicePort');
10+
11+
module.exports = Device;
12+
13+
function Device(beacon) {
14+
this.beacon = beacon;
15+
this.created = Date.now();
16+
}
17+
18+
Device.prototype.getUUID = function () {
19+
return this.beacon[constants.BEACON_UUID];
20+
}
21+
22+
Device.prototype.getModel = function () {
23+
return this.beacon[constants.BEACON_MODEL];
24+
}
25+
26+
Device.prototype.getUrl = function () {
27+
return this.beacon[constants.BEACON_URL];
28+
}
29+
30+
Device.prototype.getIpAddress = function () {
31+
const url = this.beacon[constants.BEACON_URL];
32+
const idx = url.indexOf('://');
33+
if (idx >= 0) {
34+
return url.substring(idx + 3);
35+
} else {
36+
return url;
37+
}
38+
}
39+
40+
Device.prototype.getCreated = function () {
41+
return this.created;
42+
}
43+
44+
Device.prototype.getModulePorts = function () {
45+
const device = this;
46+
return new BluePromise(function (resolve, reject) {
47+
const socket = net.createConnection(constants.PORT_NUMBER, device.getIpAddress());
48+
socket
49+
.on('connect', function () {
50+
const devices = [];
51+
const cmd = util.format('%s', constants.COMMAND_GETDEVICES);
52+
53+
rl.createInterface(socket, socket).on('line', function (line) {
54+
console.log('Received (%s): %s', cmd, line);
55+
if (line === 'endlistdevices') {
56+
resolve(devices);
57+
socket.destroy();
58+
} else {
59+
const args = line.split(',');
60+
if (args.length === 3 && args[0] === 'device') {
61+
if (args[1] !== '0') { // ignore the root module (ethernet/wifi)
62+
switch (args[2]) {
63+
case constants.LINK_TYPE_3RELAY:
64+
devices.push(new DevicePort(device, args[1], 1, constants.BEACON_TYPE_CC));
65+
devices.push(new DevicePort(device, args[1], 2, constants.BEACON_TYPE_CC));
66+
devices.push(new DevicePort(device, args[1], 3, constants.BEACON_TYPE_CC));
67+
break;
68+
case constants.LINK_TYPE_1SERIAL:
69+
devices.push(new DevicePort(device, args[1], args[1], constants.BEACON_TYPE_SR));
70+
break;
71+
case constants.LINK_TYPE_1IR:
72+
case constants.LINK_TYPE_1IRBLASTER:
73+
devices.push(new DevicePort(device, args[1], 1, constants.BEACON_TYPE_IR));
74+
break;
75+
case constants.LINK_TYPE_3IR:
76+
case constants.LINK_TYPE_1IRTRIPORT:
77+
case constants.LINK_TYPE_1IRTRIPORTBLASTER:
78+
devices.push(new DevicePort(device, args[1], 1, constants.BEACON_TYPE_IR));
79+
devices.push(new DevicePort(device, args[1], 2, constants.BEACON_TYPE_IR));
80+
devices.push(new DevicePort(device, args[1], 3, constants.BEACON_TYPE_IR));
81+
break;
82+
default:
83+
console.error('Unknown port type: ' + line);
84+
break;
85+
}
86+
}
87+
} else {
88+
reject('Unknown response: ' + line);
89+
socket.destroy();
90+
}
91+
}
92+
});
93+
console.log('Sending %s', cmd);
94+
socket.write(cmd + '\r');
95+
})
96+
.on('error', function (err) {
97+
socket.destroy();
98+
reject(err);
99+
});
100+
});
101+
};
102+
103+
Device.prototype.setState = function (module, port, state) {
104+
const device = this;
105+
return new BluePromise(function (resolve, reject) {
106+
const socket = net.createConnection(constants.PORT_NUMBER, device.getIpAddress());
107+
socket
108+
.on('connect', function () {
109+
const cmd = util.format('%s,%s:%s,%s', constants.COMMAND_SETSTATE, module, port, state);
110+
111+
rl.createInterface(socket, socket).on('line', function (line) {
112+
console.log('Received (%s): %s', cmd, line);
113+
const args = line.split(',');
114+
if (args.length === 3 && (args[0] === constants.RESPONSE_STATE || args[0] === constants.RESPONSE_SETSTATE)) {
115+
resolve(args[2]);
116+
} else {
117+
reject('Unknown response: ' + line);
118+
}
119+
socket.destroy();
120+
});
121+
console.log('Sending %s', cmd);
122+
socket.write(cmd + '\r');
123+
})
124+
.on('error', function (err) {
125+
socket.destroy();
126+
reject(err);
127+
});
128+
});
129+
};
130+
131+
Device.prototype.getState = function (module, port) {
132+
const device = this;
133+
return new BluePromise(function (resolve, reject) {
134+
const socket = net.createConnection(constants.PORT_NUMBER, device.getIpAddress());
135+
socket
136+
.on('connect', function () {
137+
const cmd = util.format('%s,%s:%s', constants.COMMAND_GETSTATE, module, port);
138+
139+
rl.createInterface(socket, socket).on('line', function (line) {
140+
console.log('Received (%s): %s', cmd, line);
141+
const args = line.split(',');
142+
if (args.length === 3 && args[0] === constants.RESPONSE_STATE) {
143+
resolve(args[2]);
144+
} else {
145+
reject('Unknown response: ' + line);
146+
}
147+
socket.destroy();
148+
});
149+
console.log('Sending %s', cmd);
150+
socket.write(cmd + '\r');
151+
})
152+
.on('error', function (err) {
153+
socket.destroy();
154+
reject(err);
155+
});
156+
});
157+
};
158+
159+
Device.prototype.sendIR = function (module, port, state) {
160+
const device = this;
161+
return new BluePromise(function (resolve, reject) {
162+
const socket = net.createConnection(constants.PORT_NUMBER, device.getIpAddress());
163+
socket
164+
.on('connect', function () {
165+
const cmd = util.format('%s,%s:%s,%s', constants.COMMAND_SENDIR, module, port, state);
166+
167+
rl.createInterface(socket, socket).on('line', function (line) {
168+
console.log('Received (%s): %s', cmd, line);
169+
const args = line.split(',');
170+
if (args.length === 3 && (args[0] === constants.RESPONSE_IR)) {
171+
resolve(args[2]);
172+
} else {
173+
reject('Unknown response: ' + line);
174+
}
175+
socket.destroy();
176+
});
177+
console.log('Sending %s', cmd);
178+
socket.write(cmd + '\r');
179+
})
180+
.on('error', function (err) {
181+
socket.destroy();
182+
reject(err);
183+
});
184+
});
185+
};
186+
187+
Device.prototype.sendSerial = function (module, serial) {
188+
const device = this;
189+
return new BluePromise(function (resolve, reject) {
190+
const socket = net.createConnection(constants.PORT_NUMBER + module, device.getIpAddress());
191+
socket
192+
.on('connect', function () {
193+
console.log('Sending %s', serial);
194+
socket.write(serial);
195+
socket.destroy();
196+
})
197+
.on('error', function (err) {
198+
socket.destroy();
199+
reject(err);
200+
});
201+
});
202+
};
203+
204+
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
'use strict';
2+
3+
const BluePromise = require('bluebird');
4+
5+
const net = require('net');
6+
const dgram = require('dgram');
7+
const constants = require('./constants');
8+
const Device = require('./device');
9+
10+
const ITACH_BROADCAST_ADDR = '239.255.250.250';
11+
const ITACH_BROADCAST_PORT = 9131;
12+
const BEACON_TIMEOUT = 1000 * 60 * 5;
13+
14+
const knownDevices = {};
15+
16+
const server = dgram.createSocket(net.isIPv6(ITACH_BROADCAST_ADDR) ? 'udp6' : 'udp4')
17+
.on('listening', function () {
18+
const address = server.address();
19+
console.log('GlobalCache/ITACH beacon listener started on ' + address.port);
20+
server.addMembership(ITACH_BROADCAST_ADDR);
21+
})
22+
.on('message', function (message, remote) {
23+
checkStaleBeacons();
24+
if (message.toString().startsWith("AMXB")) {
25+
parseBeacon(message.toString());
26+
}
27+
})
28+
.on('error', function (err) {
29+
console.error("Error creating beach listener. ", err);
30+
})
31+
.bind(ITACH_BROADCAST_PORT);
32+
33+
module.exports.getDevice = function (uuid) {
34+
return new BluePromise(function (resolve, reject) {
35+
const device = knownDevices[uuid];
36+
if (device === undefined) {
37+
reject("Unknown device UUID: " + uuid);
38+
} else {
39+
resolve(device);
40+
}
41+
});
42+
}
43+
44+
module.exports.getDevicePorts = function (type) {
45+
const devicePorts = [];
46+
for (let uuid in knownDevices) {
47+
if (knownDevices.hasOwnProperty(uuid)) {
48+
const device = knownDevices[uuid];
49+
devicePorts.push(device.getModulePorts());
50+
}
51+
}
52+
53+
return BluePromise.all(devicePorts).then(function (ports) {
54+
return [].concat.apply([], ports)
55+
.filter(function (port) { return port.type === type; });
56+
});
57+
}
58+
59+
function parseBeacon(message) {
60+
//console.log("Potential beacon found " + message);
61+
const msgParts = message.replace(/>/g, '').substring(4).split("<-");
62+
const max = msgParts.length;
63+
64+
const beacon = { created: Date.now() };
65+
for (let i = 0; i < max; i++) {
66+
const idx = msgParts[i].indexOf("=");
67+
if (idx >= 0) {
68+
beacon[msgParts[i].substring(0, idx).trim()] = msgParts[i].substring(idx + 1).trim();
69+
}
70+
}
71+
72+
const device = new Device(beacon);
73+
74+
// purposely used '==' to catch null/undefined/empty
75+
if (device.getModel() == undefined || device.getUrl() == undefined) {
76+
console.log("Beacon message is invalid or incomplete %s", message);
77+
return;
78+
}
79+
80+
if (knownDevices.hasOwnProperty(device.getUUID())) {
81+
//console.log("Beacon %s being refreshed: %s at %s", device.getUUID(), device.getModel(), device.getUrl());
82+
} else {
83+
console.log("New Beacon %s found: %s at %s", device.getUUID(), device.getModel(), device.getUrl());
84+
}
85+
knownDevices[device.getUUID()] = device;
86+
}
87+
88+
function checkStaleBeacons() {
89+
for (let uuid in knownDevices) {
90+
if (knownDevices.hasOwnProperty(uuid)) {
91+
const device = knownDevices[uuid];
92+
if (device.getCreated() + BEACON_TIMEOUT < Date.now()) {
93+
console.log("Beacon %s has expired and is being removed", device.getUUID());
94+
delete knownDevices[uuid];
95+
}
96+
}
97+
}
98+
}
99+

0 commit comments

Comments
 (0)