]> code.delx.au - monosys/blob - bin/wifi-scan
wifi-scan: alternate form of channel
[monosys] / bin / wifi-scan
1 #!/usr/bin/env node
2 'use strict'
3
4 const {exec} = require('child_process');
5 const fs = require('fs').promises;
6
7 function execAsync(command, opts) {
8 return new Promise((resolve, reject) => {
9 exec(command, opts, (error, stdout, stderr) => {
10 if (error) {
11 reject(error);
12 } else {
13 resolve({stdout, stderr});
14 }
15 });
16 });
17 }
18
19 function sleep(n) {
20 return new Promise((resolve) => setTimeout(resolve, n));
21 }
22
23 async function findInterface() {
24 const {stdout} = await execAsync('iw dev');
25 const lines = stdout.split('\n')
26 .map((line) => line.trim())
27 .filter((line) => line.startsWith('Interface '))
28 .map((line) => line.split(' ')[1]);
29 return lines[0];
30 }
31
32 async function scanInterface(iface) {
33 const {stdout} = await execAsync(`sudo iw dev ${iface} scan`);
34 return stdout;
35 }
36
37 function formatScanResult(scanResult) {
38 const results = [];
39 let partial = null;
40
41 for (let line of scanResult.split('\n')) {
42 if (line.startsWith('BSS ')) {
43 finishPartial();
44 partial = {};
45 partial.bssid = line.match(/[a-z0-9:]+/)[0];
46 }
47
48 line = line.trim()
49 if (line.startsWith('SSID: ')) {
50 partial.ssid = line.split(':')[1].trim();
51 }
52 if (line.startsWith('signal: ')) {
53 partial.signal = line.split(':')[1].trim();
54 }
55 if (line.startsWith('DS Parameter set: channel')) {
56 partial.channel = line.split(':')[1].trim();
57 }
58 if (line.startsWith('* primary channel:')) {
59 partial.channel = 'channel ' + line.split(':')[1].trim();
60 }
61 if (line.startsWith('freq: ')) {
62 partial.freq = 'freq ' + line.split(':')[1].trim();
63 }
64 }
65
66 function finishPartial() {
67 if (!partial) {
68 return;
69 }
70
71 partial.ssid = partial.ssid || '';
72 partial.channel = partial.channel || partial.freq || '';
73
74 const sortKey = [
75 parseFloat(partial.signal),
76 parseInt(partial.channel.split(' ')[1])
77 ];
78
79 results.push([sortKey, partial]);
80 }
81
82 return results
83 .sort()
84 .map(([, {bssid, ssid, signal, channel}]) => {
85 ssid = ssid.padStart(40, ' ').substr(0, 40);
86 channel = channel.padEnd(12, ' ');
87 return `${signal} ${channel} ${ssid} ${bssid}`;
88 })
89 .join('\n') + '\n';
90 }
91
92 async function main() {
93 const iface = process.argv[2] || await findInterface();
94
95 if (iface === '-') {
96 const scanResult = await fs.readFile('/dev/stdin', 'utf-8');
97 const prettyScanResult = formatScanResult(scanResult);
98 process.stdout.write(prettyScanResult);
99 } else {
100 for (;;) {
101 const scanResult = await scanInterface(iface).catch((err) => err.toString());
102 const prettyScanResult = formatScanResult(scanResult);
103 process.stdout.write('\x1b[2J\x1b[0f');
104 process.stdout.write(prettyScanResult);
105 await sleep(3000);
106 }
107 }
108 }
109
110 main().catch((err) => {
111 console.log('Unhandled error!', err);
112 process.exit(1);
113 });