Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add neutral wifi command to list Tessel Wifi Information #282

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions bin/tessel-2.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,13 @@ function closeSuccessfulCommand() {
}

function closeFailedCommand(err) {
// If the returned value is an error
if (err instanceof Error) {
// Throw it
throw err;
}
// Otherwise
else {
// Print a stern warning
} else {
// Print a stern warning by default
logs.warn(err);
}
// Exit with non-zero code
// NOTE: Exit code is non-zero
process.exit(1);
}

Expand Down Expand Up @@ -186,12 +182,22 @@ parser.command('init')

parser.command('wifi')
.callback(function(opts) {
//TODO: Refactor switch case into controller.wifi
// TODO: Refactor switch case into controller.wifi
if (opts.list) {
controller.printAvailableNetworks(opts)
.then(closeSuccessfulCommand, closeFailedCommand);
} else if (opts.ssid && opts.password) {
controller.connectToNetwork(opts)
} else if (opts.ssid || opts.password) {
if (opts.ssid && opts.password) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we move the SSID and Password check here (which makes sense so you don't have to wait for it to connect to a Tessel) we might as well remove the check that already exists here: https://github.com/tessel/t2-cli/pull/282/files#diff-ad1c3af214118b233a97e4dd4f67d729R127

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrNice On second thought, I take this back. Once we make this CLI more usable from other Node programs, it would be great if we could export functions so having this extra check doesn't hurt. In fact, because this check is removed they are causing this test and this test to fail right now.

controller.connectToNetwork(opts)
.then(closeSuccessfulCommand, closeFailedCommand);
} else {
var msg = opts.ssid ?
'Please provide a password with -p <password>' :
'Please provide a network name (SSID) with -n <name>';
closeFailedCommand(new Error(msg));
}
} else {
controller.getWifiInfo(opts)
.then(closeSuccessfulCommand, closeFailedCommand);
}
})
Expand Down
42 changes: 42 additions & 0 deletions lib/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,48 @@ controller.printAvailableNetworks = function(opts) {
});
};

controller.getWifiInfo = function(opts) {
return Tessel.get(opts)
.then(function(selectedTessel) {
return selectedTessel.getWifiInfo()
.then(function(network) {
// Grab inet lines, flatmap them, remove empty
var ips = network.ips.filter(function(item) {
return /inet/.exec(item);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the Tessel isn't connected to WiFi?

})
.map(function(line) {
return line.split(' ');
})
.reduce(function(a, b) {
return a.concat(b);
})
.filter(function(item) {
return /addr/.exec(item);
})
.map(function(chunk) {
return chunk.split(':')[1];
})
.filter(function(addr) {
return addr.length;
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see this consolidated. The last three could be a single reduce

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the intent of the code is clear, why consolidate

I can replace the last three with the following

.reduce(function(list, ip) {
  if (/addr/.exec(item)) {
    var ip = chunk.split(':')[1];
    if (ip.length) list.push[ip];
  }
  return list;
});

But it doesn't make it more clear (to me) what is happening.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:/

This is what reduce is for: consolidating map/filter operations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change it if that's wanted, but I don't think it's clearer.

On Tue, Sep 8, 2015 at 10:08 AM, Rick Waldron notifications@github.com
wrote:

In lib/controller.js
#282 (comment):

  •        .map(function(line) {
    
  •          return line.split(' ');
    
  •        })
    
  •        .reduce(function(a, b) {
    
  •          return a.concat(b);
    
  •        })
    
  •        .filter(function(item) {
    
  •          return /addr/.exec(item);
    
  •        })
    
  •        .map(function(chunk) {
    
  •          return chunk.split(':')[1];
    
  •        })
    
  •        .filter(function(addr) {
    
  •          return addr.length;
    
  •        });
    

:/

This is what reduce is for: consolidating map/filter operations.


Reply to this email directly or view it on GitHub
https://github.com/tessel/t2-cli/pull/282/files#r38951868.

logs.info('Connected to "' + network.ssid + '"');
ips.forEach(function(ip) {
logs.info('IP Address: ' + ip);
});
logs.info('Signal Strength: (' + network.quality + '/' + network.quality_max + ')');
logs.info('Bitrate: ' + Math.round(network.bitrate / 1000) + 'mbps');
})
.catch(function(err) {
// Handle no Wi-Fi
logs.err(err.message);
})
.finally(function() {
return controller.closeTesselConnections(selectedTessel);
});
});
};

controller.connectToNetwork = function(opts) {
// Grab the preferred Tessel
return Tessel.get(opts)
Expand Down
6 changes: 6 additions & 0 deletions lib/tessel/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ module.exports.readFile = function(filepath) {
module.exports.scanWiFi = function() {
return ['ubus', 'call', 'iwinfo', 'scan', '{"device":"wlan0"}'];
};
module.exports.getWifiInfo = function() {
return ['ubus', 'call', 'iwinfo', 'info', '{"device":"wlan0"}'];
};
module.exports.getIPAddress = function() {
return ['ifconfig', 'wlan0'];
};
module.exports.stopRunningScript = function() {
return ['/etc/init.d/tessel-app', 'stop'];
};
Expand Down
99 changes: 82 additions & 17 deletions lib/tessel/wifi.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,29 @@ Tessel.prototype.findAvailableNetworks = function() {
logs.info('Scanning for available networks...');
return self.connection.exec(commands.scanWiFi())
.then(function(remoteProcess) {

var err = '';
var resultsJSON = '';

// Gather errors and results
remoteProcess.stderr.on('data', function(d) {
logs.err(d.toString());
err += d.toString();
});

// Gather the results
remoteProcess.stdout.on('data', function(d) {
resultsJSON += d;
resultsJSON += d.toString();
});

// Wait for the transfer to finish...
remoteProcess.once('close', function() {
if (err.length) {
reject(new Error(err));
}
var networks;

// Parse the response
try {
networks = JSON.parse(resultsJSON).results;
} catch (err) {
self.connection.end();
// Throw any unfortunate errors
if (err) {
return reject(err);
}
return reject(err);
}

// Sort by signal strength
Expand All @@ -56,6 +54,78 @@ Tessel.prototype.findAvailableNetworks = function() {
});
};

Tessel.prototype.getWifiInfo = function() {
var self = this;
return new Promise(function(resolve, reject) {
return self.connection.exec(commands.getWifiInfo())
.then(function(remoteProcess) {
var err = '';
var resultsJSON = '';

remoteProcess.stderr.on('data', function(d) {
err += d.toString();
});
remoteProcess.stdout.on('data', function(d) {
resultsJSON += d.toString();
});

remoteProcess.once('close', function() {
var network;
if (err.length) {
return reject(new Error(err));
}

try {
network = JSON.parse(resultsJSON);
} catch (err) {
return self.connection.end()
.then(function() {
reject(err);
});
}

if (network.ssid === undefined) {
var msg = self.name + ' is not connected to Wi-Fi (run "tessel wifi -l" to see available networks)';
return reject(new Error(msg));
}

return self.connection.exec(commands.getIPAddress())
.then(function(rp) {
var err = '';
var result = '';

rp.stderr.on('data', function(d) {
err += d.toString();
});
rp.stdout.on('data', function(d) {
result += d.toString();
});

rp.once('close', function() {
if (err.length) {
reject(new Error(err));
}

try {
network.ips = result.split('\n');
} catch (err) {
return self.connection.end()
.then(function() {
reject(err);
});
}

return self.connection.end()
.then(function() {
return resolve(network);
});
});
});
});
});
});
};

function compareBySignal(a, b) {
if ((a.quality / a.quality_max) > (b.quality / b.quality_max)) {
return -1;
Expand All @@ -71,11 +141,7 @@ Tessel.prototype.connectToNetwork = function(opts) {
var ssid = opts.ssid;
var password = opts.password;

return new Promise(function(resolve, reject) {
if (!ssid || !password) {
return reject(new Error('Invalid credentials. Must set ssid and password'));
}

return new Promise(function(resolve) {
logs.info('Setting SSID:', ssid, 'and password:', password);

// Set the network SSID
Expand All @@ -95,7 +161,7 @@ Tessel.prototype.connectToNetwork = function(opts) {
.then(function(remoteProcess) {
// Once the credentials have been comitted
remoteProcess.once('close', function() {
// Restart the wifi
// Restart the wifi
return self.connection.exec(commands.reconnectWifi())
.then(function(remoteProcess) {
// Once the wifi restart process closes
Expand All @@ -108,7 +174,6 @@ Tessel.prototype.connectToNetwork = function(opts) {
});
});
});

});
};

Expand Down
89 changes: 86 additions & 3 deletions test/unit/wifi.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
var _ = require('lodash');
var sinon = require('sinon');
var Tessel = require('../../lib/tessel/tessel');
var commands = require('../../lib/tessel/commands');
var logs = require('../../lib/logs');
var TesselSimulator = require('../common/tessel-simulator');

exports['Tessel.prototype.findAvailableNetworks'] = {
module.exports['Tessel.prototype.findAvailableNetworks'] = {
setUp: function(done) {

this.findAvailableNetworks = sinon.spy(Tessel.prototype, 'findAvailableNetworks');
Expand Down Expand Up @@ -121,7 +122,6 @@ exports['Tessel.prototype.findAvailableNetworks'] = {

module.exports['Tessel.prototype.connectToNetwork'] = {
setUp: function(done) {

this.connectToNetwork = sinon.spy(Tessel.prototype, 'connectToNetwork');
this.logsWarn = sinon.stub(logs, 'warn', function() {});
this.logsInfo = sinon.stub(logs, 'info', function() {});
Expand All @@ -130,7 +130,6 @@ module.exports['Tessel.prototype.connectToNetwork'] = {
this.commitWirelessCredentials = sinon.spy(commands, 'commitWirelessCredentials');
this.reconnectWifi = sinon.spy(commands, 'reconnectWifi');
this.tessel = TesselSimulator();

done();
},
tearDown: function(done) {
Expand Down Expand Up @@ -202,3 +201,87 @@ module.exports['Tessel.prototype.connectToNetwork'] = {
});
}
};

module.exports['Tessel.prototype.getWifiInfo'] = {
setUp: function(done) {
this.getWifiInfo = sinon.spy(Tessel.prototype, 'getWifiInfo');
this.logsWarn = sinon.stub(logs, 'warn', _.noop);
this.logsInfo = sinon.stub(logs, 'info', _.noop);
this.logsErr = sinon.stub(logs, 'err', _.noop);
this.getWifiCmd = sinon.spy(commands, 'getWifiInfo');
this.getIPAddress = sinon.spy(commands, 'getIPAddress');
this.tessel = TesselSimulator();
done();
},
tearDown: function(done) {
this.tessel.mockClose();
this.logsWarn.restore();
this.logsInfo.restore();
this.logsErr.restore();
this.getWifiInfo.restore();
done();
},
noWifi: function(test) {
test.expect(3);

var self = this;
this.tessel.getWifiInfo({
timeout: 5
})
.then(function() {
test.fail('getWifiInfo did not get rejected');
})
.catch(function(err) {
test.ok(err);
test.equal(self.getWifiCmd.callCount, 1);
test.equal(self.getIPAddress.callCount, 0);
test.done();
});

// Force Wifi down
this.tessel._rps.stdout.push('{}');
setImmediate(function() {
self.tessel._rps.emit('close');
});
},
success: function(test) {
test.expect(2);

var self = this;
this.tessel.getWifiInfo({
timeout: 5
})
.then(function(network) {
test.equal(network.ssid, 'piano');
test.equal(self.getWifiCmd.callCount, 1);
test.equal(self.getIPAddress.callCount, 1);
test.done();
})
.catch(function(err) {
test.fail(err);
});

// Force Wifi up
this.tessel._rps.stdout.push('{"ssid":"piano"}');
setImmediate(function() {
self.tessel._rps.emit('close');
// mock getIPAddress
setTimeout(function() {
/*jshint multistr: true */
self.tessel._rps.stdout.push(
'wlan0 Link encap:Ethernet HWaddr 02:A3:E4:26:A9:75\
inet addr:10.0.0.2 Bcast:10.0.0.255 Mask:255.255.255.0\
inet6 addr: fe80::a3:e4ff:fe26:a975/64 Scope:Link\
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1\
RX packets:19697 errors:0 dropped:0 overruns:0 frame:0\
TX packets:2248 errors:0 dropped:0 overruns:0 carrier:0\
collisions:0 txqueuelen:1000\
RX bytes:2225847 (2.1 MiB) TX bytes:503558 (491.7 KiB)'
);
setImmediate(function() {
self.tessel._rps.emit('close');
});
}, 1000);
});
}
};