Merge branch 'customcommands'
This commit is contained in:
commit
1586ce92aa
9 files changed
+319
-8
No files matched your search
@@ -0,0 +1,3 @@
|
|||||||
|
*~
|
||||||
|
*.js
|
||||||
|
node_modules
|
||||||
+12
@@ -102,6 +102,12 @@ updateDevice = (currentRequest, actions, callback) ->
|
|||||||
updates["#{path}_writable"] = p[1] if p[1]?
|
updates["#{path}_writable"] = p[1] if p[1]?
|
||||||
updates["#{path}_timestamp"] = now
|
updates["#{path}_timestamp"] = now
|
||||||
|
|
||||||
|
if actions.customCommands?
|
||||||
|
for p in actions.customCommands
|
||||||
|
commandName = p[0]
|
||||||
|
updates["_customCommands.#{commandName}._value"] = p[1]
|
||||||
|
updates["_customCommands.#{commandName}._timestamp"] = now
|
||||||
|
|
||||||
if Object.keys(updates).length > 0 or Object.keys(deletes).length > 0
|
if Object.keys(updates).length > 0 or Object.keys(deletes).length > 0
|
||||||
db.devicesCollection.update({'_id' : currentRequest.deviceId}, {'$set' : updates, '$unset' : deletes}, {}, (err, count) ->
|
db.devicesCollection.update({'_id' : currentRequest.deviceId}, {'$set' : updates, '$unset' : deletes}, {}, (err, count) ->
|
||||||
if (err)
|
if (err)
|
||||||
@@ -174,6 +180,12 @@ runTask = (currentRequest, task, methodResponse) ->
|
|||||||
res = tr069.response(task._id, cwmpResponse)
|
res = tr069.response(task._id, cwmpResponse)
|
||||||
writeResponse(currentRequest, res)
|
writeResponse(currentRequest, res)
|
||||||
)
|
)
|
||||||
|
when tasks.STATUS_SAVE
|
||||||
|
db.saveTask(task, (err) ->
|
||||||
|
throw new Error(err) if err?
|
||||||
|
res = tr069.response(task._id, cwmpResponse)
|
||||||
|
writeResponse(currentRequest, res)
|
||||||
|
)
|
||||||
else
|
else
|
||||||
throw Error('Unknown task status')
|
throw Error('Unknown task status')
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -49,3 +49,5 @@ exports.ALIASES = aliases
|
|||||||
|
|
||||||
# load authentication scripts
|
# load authentication scripts
|
||||||
exports.auth = require('./config/auth')
|
exports.auth = require('./config/auth')
|
||||||
|
|
||||||
|
exports.CUSTOM_COMMANDS = require('./config/custom_commands')
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"seowon_frequencies" : "/^[0-9]+\\-SWU\\%2D9200\\-[0-9A-Z]+$/"
|
||||||
|
}
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
// Change frequency plans for Seowon devices.
|
||||||
|
// Args format: 3532500 5000,3537500 5000,3542500 5000,3547500 5000,3552500 5000,3557500 5000,3505000 10000,3515000 10000,3525000 10000,3535000 10000,3545000 10000,3555000 10000
|
||||||
|
var net = require('net');
|
||||||
|
var PORT = 23;
|
||||||
|
var USERNAME = 'admin';
|
||||||
|
var PASSWORD = 'admin';
|
||||||
|
|
||||||
|
var FREQUENCIES_REGEX = /\s*([\d]+)\.\s*Frequency\s*:\s*([\d]+)\s\(KHz\),\s*Bandwidth\s*:\s*([\d\.]+)\s*\(MHz\)/gm
|
||||||
|
|
||||||
|
var telnetConnect = function(host, callback) {
|
||||||
|
var client = net.connect(PORT, host, function() {
|
||||||
|
// enter username
|
||||||
|
telnetExecute(USERNAME, client, "Password: ", function(err, response) {
|
||||||
|
if (err) return callback(err, client);
|
||||||
|
if (!timeout) return;
|
||||||
|
// enter password
|
||||||
|
telnetExecute(PASSWORD, client, "MT7109> ", function(err, response) {
|
||||||
|
if (!timeout) return;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
return callback(err, client);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var timeout = setTimeout(function() {
|
||||||
|
timeout = null;
|
||||||
|
return callback('timeout', client);
|
||||||
|
}, 4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
var telnetExecute = function(command, connection, prompt, callback) {
|
||||||
|
command += "\n";
|
||||||
|
var response = '';
|
||||||
|
var listener = function(chunk) {
|
||||||
|
response += chunk.toString();
|
||||||
|
if (response.slice(0 - prompt.length) == prompt) {
|
||||||
|
if (!timeout) return;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
connection.removeListener('data', listener);
|
||||||
|
return callback(null, response.slice(command.length, 0 - prompt.length));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var end = function() {
|
||||||
|
if (!timeout) return;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
connection.removeListener('end', end);
|
||||||
|
return callback(null, response.slice(command.length));
|
||||||
|
};
|
||||||
|
|
||||||
|
var timeout = setTimeout(function() {
|
||||||
|
timeout = null;
|
||||||
|
return callback('timeout', response);
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
if (prompt)
|
||||||
|
connection.on('data', listener);
|
||||||
|
else
|
||||||
|
connection.on('end', end);
|
||||||
|
|
||||||
|
if (command)
|
||||||
|
connection.write(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseFrequencies = function(frequenciesString) {
|
||||||
|
var a = frequenciesString.split(',')
|
||||||
|
var l = [];
|
||||||
|
for (i in a) {
|
||||||
|
var sp = a[i].trim().split(' ');
|
||||||
|
var frequency = parseInt(sp[0]);
|
||||||
|
var bandwidth = parseFloat(sp[1]);
|
||||||
|
l.push([frequency, bandwidth]);
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
var findFrequency = function(haystack, needle) {
|
||||||
|
for (i in haystack) {
|
||||||
|
if (haystack[i][0] == needle[0] && haystack[i][1] == needle[1])
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var getDeviceIp = function(deviceId, callback) {
|
||||||
|
var db = require('../../db');
|
||||||
|
var URL = require('url');
|
||||||
|
|
||||||
|
db.devicesCollection.findOne({_id : deviceId}, {'InternetGatewayDevice.ManagementServer.ConnectionRequestURL._value' : 1}, function(err, device) {
|
||||||
|
var connectionRequestUrl = device.InternetGatewayDevice.ManagementServer.ConnectionRequestURL._value;
|
||||||
|
var url = URL.parse(connectionRequestUrl);
|
||||||
|
return callback(url.hostname);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.init = function(deviceId, args, callback) {
|
||||||
|
return exports.get(deviceId, args, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.get = function(deviceId, args, callback) {
|
||||||
|
var currentFrequencies = [];
|
||||||
|
|
||||||
|
getDeviceIp(deviceId, function(ip) {
|
||||||
|
telnetConnect(ip, function(err, client) {
|
||||||
|
// enter privileged mode
|
||||||
|
telnetExecute("enable", client, "MT7109# ", function(err, response) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
// show frequencies
|
||||||
|
telnetExecute("show wmx freq", client, "MT7109# ", function(err, response) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
while (f = FREQUENCIES_REGEX.exec(response)) {
|
||||||
|
var frequency = parseInt(f[2]);
|
||||||
|
var bandwidth = Math.floor(parseFloat(f[3]) * 1000);
|
||||||
|
var freqStr = String(frequency) + ' ' + String(bandwidth);
|
||||||
|
currentFrequencies.push(freqStr);
|
||||||
|
}
|
||||||
|
// log out
|
||||||
|
telnetExecute("logout", client, null, function(err, response) {
|
||||||
|
return callback(err, currentFrequencies.join(','));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.set = function(deviceId, args, callback) {
|
||||||
|
var newFrequencies = parseFrequencies(args);
|
||||||
|
var addFrequencies = [];
|
||||||
|
var delFrequencies = [];
|
||||||
|
|
||||||
|
getDeviceIp(deviceId, function(ip) {
|
||||||
|
telnetConnect(ip, function(err, client) {
|
||||||
|
// enter privileged mode
|
||||||
|
telnetExecute("enable", client, "MT7109# ", function(err, response) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
// show frequencies
|
||||||
|
telnetExecute("show wmx freq", client, "MT7109# ", function(err, response) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
while (f = FREQUENCIES_REGEX.exec(response)) {
|
||||||
|
var index = parseInt(f[1]);
|
||||||
|
var frequency = parseInt(f[2]);
|
||||||
|
var bandwidth = Math.floor(parseFloat(f[3]));
|
||||||
|
|
||||||
|
var i = findFrequency(newFrequencies, [frequency, bandwidth * 1000]);
|
||||||
|
if (i == -1)
|
||||||
|
delFrequencies.push(index);
|
||||||
|
else
|
||||||
|
newFrequencies.splice(i, 1);
|
||||||
|
}
|
||||||
|
for (f in newFrequencies)
|
||||||
|
addFrequencies.push(newFrequencies[f]);
|
||||||
|
|
||||||
|
if (addFrequencies.length == 0 && delFrequencies.length == 0) {
|
||||||
|
// log out
|
||||||
|
telnetExecute("logout", client, null, function(err, response) {
|
||||||
|
return callback(err, args);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
telnetExecute("wimax", client, "MT7109 (WiMax)# ", function(err, response) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
var add, del;
|
||||||
|
del = function() {
|
||||||
|
if (delFrequencies.length == 0) {
|
||||||
|
add();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var i = delFrequencies.pop();
|
||||||
|
telnetExecute("wmx freq del " + i, client, "MT7109 (WiMax)# ", function(err, response) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
del();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
add = function() {
|
||||||
|
if (addFrequencies.length == 0) {
|
||||||
|
// commit
|
||||||
|
telnetExecute("commit", client, "MT7109 (WiMax)# ", function(err, response) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
// log out
|
||||||
|
telnetExecute("logout", client, null, function(err, response) {
|
||||||
|
return callback(null, args);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var i = addFrequencies.pop();
|
||||||
|
telnetExecute("wmx freq add " + i[0] + ' ' + i[1]/1000, client, "MT7109 (WiMax)# ", function(err, response) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
add();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
del();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
config = require './config'
|
||||||
|
common = require './common'
|
||||||
|
|
||||||
|
|
||||||
|
exports.execute = (deviceId, command, callback) ->
|
||||||
|
args = command.split(/\s+/)
|
||||||
|
f = args.shift()
|
||||||
|
c = args.shift()
|
||||||
|
|
||||||
|
file = require "./config/custom_commands/#{f}"
|
||||||
|
file[c](deviceId, args.join(' '), callback)
|
||||||
|
|
||||||
|
|
||||||
|
exports.getFileCommands = (filename) ->
|
||||||
|
commands = []
|
||||||
|
f = require("./config/custom_commands/#{filename}")
|
||||||
|
for k,v of f
|
||||||
|
commands.push(k)
|
||||||
|
return commands
|
||||||
|
|
||||||
|
|
||||||
|
exports.getDeviceCustomCommands = (deviceId) ->
|
||||||
|
commands = {}
|
||||||
|
for k,v of config.CUSTOM_COMMANDS
|
||||||
|
if eval(v).test(deviceId)
|
||||||
|
commands[k] = exports.getFileCommands(k)
|
||||||
|
return commands
|
||||||
@@ -72,9 +72,13 @@ updateTask = (task, callback) ->
|
|||||||
|
|
||||||
|
|
||||||
saveTask = (task, callback) ->
|
saveTask = (task, callback) ->
|
||||||
task._id = mongodb.ObjectID(String(task._id))
|
# task ID can either be a string or a MongoDB ID
|
||||||
tasksCollection.save(task, (err) ->
|
id = String(task._id)
|
||||||
callback(err)
|
task._id = mongodb.ObjectID(id)
|
||||||
|
tasksCollection.save(task, (mongoErr) ->
|
||||||
|
memcached.del(id, (memcachedErr, res) ->
|
||||||
|
callback(mongoErr or memcachedErr)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ db = require './db'
|
|||||||
mongoQuery = require './mongo-query'
|
mongoQuery = require './mongo-query'
|
||||||
query = require './query'
|
query = require './query'
|
||||||
log = require('util').log
|
log = require('util').log
|
||||||
|
customCommands = require './custom-commands'
|
||||||
|
|
||||||
|
|
||||||
getPresets = (callback) ->
|
getPresets = (callback) ->
|
||||||
@@ -65,6 +66,7 @@ matchObject = (object, param) ->
|
|||||||
|
|
||||||
exports.assertPresets = (deviceId, presetsHash, callback) ->
|
exports.assertPresets = (deviceId, presetsHash, callback) ->
|
||||||
getPresets((presets, objects) ->
|
getPresets((presets, objects) ->
|
||||||
|
deviceCustomCommands = customCommands.getDeviceCustomCommands(deviceId)
|
||||||
# only fetch relevant params
|
# only fetch relevant params
|
||||||
projection = {_id : 1}
|
projection = {_id : 1}
|
||||||
for p in presets
|
for p in presets
|
||||||
@@ -75,6 +77,10 @@ exports.assertPresets = (deviceId, presetsHash, callback) ->
|
|||||||
switch c.type
|
switch c.type
|
||||||
when 'value', 'age'
|
when 'value', 'age'
|
||||||
projection[c.name] = 1
|
projection[c.name] = 1
|
||||||
|
when 'custom_command_value', 'custom_command_age'
|
||||||
|
commandfile = c.command.split(' ', 1)
|
||||||
|
if commandfile of deviceCustomCommands
|
||||||
|
projection["_customCommands.#{c.command.split(' ', 1)}"] = 1
|
||||||
when 'firmware'
|
when 'firmware'
|
||||||
projection['InternetGatewayDevice.DeviceInfo.SoftwareVersion'] = 1
|
projection['InternetGatewayDevice.DeviceInfo.SoftwareVersion'] = 1
|
||||||
when 'add_tag', 'delete_tag'
|
when 'add_tag', 'delete_tag'
|
||||||
@@ -120,6 +126,23 @@ exports.assertPresets = (deviceId, presetsHash, callback) ->
|
|||||||
getParameterValues.push(c.name)
|
getParameterValues.push(c.name)
|
||||||
else
|
else
|
||||||
expiry = Math.min(expiry, c.age - timeDiff)
|
expiry = Math.min(expiry, c.age - timeDiff)
|
||||||
|
when 'custom_command_value'
|
||||||
|
[filename, commandName] = c.command.split(' ', 2)
|
||||||
|
continue if not deviceCustomCommands[filename]? or commandName not in deviceCustomCommands[filename]
|
||||||
|
currentValue = try common.getParamValueFromPath(device, "_customCommands.#{filename}")._value catch then undefined
|
||||||
|
dst = common.matchType(currentValue, c.value)
|
||||||
|
if currentValue != dst
|
||||||
|
taskList.push({device : deviceId, name : 'customCommand', command : c.command})
|
||||||
|
when 'custom_command_age'
|
||||||
|
[filename, commandName] = c.command.split(' ', 2)
|
||||||
|
continue if not deviceCustomCommands[filename]? or commandName not in deviceCustomCommands[filename]
|
||||||
|
command = common.getParamValueFromPath(device, "_customCommands.#{filename}")
|
||||||
|
timeDiff = try (now - command._timestamp) / 1000 catch then 9999999
|
||||||
|
if (c.age - timeDiff < config.PRESETS_TIME_PADDING)
|
||||||
|
expiry = Math.min(expiry, c.age)
|
||||||
|
taskList.push({device : deviceId, name : 'customCommand', command : c.command})
|
||||||
|
else
|
||||||
|
expiry = Math.min(expiry, c.age - timeDiff)
|
||||||
when 'add_tag'
|
when 'add_tag'
|
||||||
add_tags.push(c.tag) if not device['_tags']? or c.tag not in device['_tags']
|
add_tags.push(c.tag) if not device['_tags']? or c.tag not in device['_tags']
|
||||||
when 'delete_tag'
|
when 'delete_tag'
|
||||||
@@ -200,6 +223,7 @@ getObjectHash = (object) ->
|
|||||||
hash += "#{k}=#{object[k]}"
|
hash += "#{k}=#{object[k]}"
|
||||||
return hash
|
return hash
|
||||||
|
|
||||||
|
|
||||||
accumulateConfigurations = (presets, objects) ->
|
accumulateConfigurations = (presets, objects) ->
|
||||||
maxWeights = {}
|
maxWeights = {}
|
||||||
configurations = {}
|
configurations = {}
|
||||||
|
|||||||
+42
-5
@@ -2,13 +2,28 @@ config = require './config'
|
|||||||
common = require './common'
|
common = require './common'
|
||||||
db = require './db'
|
db = require './db'
|
||||||
mongodb = require 'mongodb'
|
mongodb = require 'mongodb'
|
||||||
|
customCommands = require './custom-commands'
|
||||||
BATCH_SIZE = 16
|
BATCH_SIZE = 16
|
||||||
|
|
||||||
exports.STATUS_QUEUED = STATUS_QUEUED = 0
|
exports.STATUS_QUEUED = STATUS_QUEUED = 0
|
||||||
exports.STATUS_STARTED = STATUS_STARTED = 1
|
exports.STATUS_STARTED = STATUS_STARTED = 1
|
||||||
exports.STATUS_PENDING = STATUS_PENDING = 2
|
exports.STATUS_SAVE = STATUS_SAVE = 2
|
||||||
exports.STATUS_FAULT = STATUS_FAULT = 3
|
exports.STATUS_PENDING = STATUS_PENDING = 3
|
||||||
exports.STATUS_FINISHED = STATUS_FINISHED = 4
|
exports.STATUS_FAULT = STATUS_FAULT = 4
|
||||||
|
exports.STATUS_FINISHED = STATUS_FINISHED = 5
|
||||||
|
|
||||||
|
|
||||||
|
initCustomCommands = (deviceId, callback) ->
|
||||||
|
updates = {customCommands : []}
|
||||||
|
counter = 0
|
||||||
|
for k,v of customCommands.getDeviceCustomCommands(deviceId)
|
||||||
|
if 'init' in v
|
||||||
|
++counter
|
||||||
|
customCommands.execute(deviceId, "#{k} init", (err, value) ->
|
||||||
|
updates.customCommands.push([k, value])
|
||||||
|
callback(updates) if --counter <= 0
|
||||||
|
)
|
||||||
|
callback(updates) if counter <= 0
|
||||||
|
|
||||||
|
|
||||||
this.init = (task, methodResponse, callback) ->
|
this.init = (task, methodResponse, callback) ->
|
||||||
@@ -42,7 +57,13 @@ this.init = (task, methodResponse, callback) ->
|
|||||||
# Ignore GetParameterValues errors. A workaround for the crappy Seewon devices.
|
# Ignore GetParameterValues errors. A workaround for the crappy Seewon devices.
|
||||||
methodResponse = {parameterList : {}}
|
methodResponse = {parameterList : {}}
|
||||||
this.getParameterValues(task.subtask, methodResponse, (err, status, cwmpResponse, deviceUpdates) ->
|
this.getParameterValues(task.subtask, methodResponse, (err, status, cwmpResponse, deviceUpdates) ->
|
||||||
callback(err, status, cwmpResponse, deviceUpdates)
|
if status is STATUS_FINISHED
|
||||||
|
initCustomCommands(task.device, (updates) ->
|
||||||
|
common.extend(deviceUpdates, updates)
|
||||||
|
callback(err, status, cwmpResponse, deviceUpdates)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
callback(err, status, cwmpResponse, deviceUpdates)
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
throw Error('Unexpected subtask name')
|
throw Error('Unexpected subtask name')
|
||||||
@@ -241,7 +262,11 @@ this.addObject = (task, methodResponse, callback) ->
|
|||||||
task.subtask = {name : 'getParameterValues', parameterNames : task.parameterNames}
|
task.subtask = {name : 'getParameterValues', parameterNames : task.parameterNames}
|
||||||
subtask()
|
subtask()
|
||||||
else if status is STATUS_STARTED
|
else if status is STATUS_STARTED
|
||||||
callback(err, STATUS_STARTED, cwmpResponse, allDeviceUpdates)
|
if allDeviceUpdates.instanceName?
|
||||||
|
# Use STATUSS_SAVE to avoid adding duplicate object in case of error
|
||||||
|
callback(err, STATUS_SAVE, cwmpResponse, allDeviceUpdates)
|
||||||
|
else
|
||||||
|
callback(err, STATUS_STARTED, cwmpResponse, allDeviceUpdates)
|
||||||
else
|
else
|
||||||
throw Error('Unexpected subtask status')
|
throw Error('Unexpected subtask status')
|
||||||
)
|
)
|
||||||
@@ -343,5 +368,17 @@ this.download = (task, methodResponse, callback) ->
|
|||||||
callback(null, STATUS_FINISHED)
|
callback(null, STATUS_FINISHED)
|
||||||
|
|
||||||
|
|
||||||
|
this.customCommand = (task, methodResponse, callback) ->
|
||||||
|
# TODO implement timeout
|
||||||
|
customCommands.execute(task.device, task.command, (err, value) ->
|
||||||
|
if err?
|
||||||
|
task.fault = err
|
||||||
|
callback(null, STATUS_FAULT)
|
||||||
|
else
|
||||||
|
commandName = task.command.split(' ', 2)[0]
|
||||||
|
callback(null, STATUS_FINISHED, null, {customCommands : [[commandName, value]]})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
exports.task = (task, methodResponse, callback) ->
|
exports.task = (task, methodResponse, callback) ->
|
||||||
this[task.name](task, methodResponse, callback)
|
this[task.name](task, methodResponse, callback)
|
||||||
Reference in new issue
Block a user