]> code.delx.au - monosys/blob - bin/wifi-scan
wifi-scan: don't ignore the list scan result
[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 finishPartial();
67
68 function finishPartial() {
69 if (!partial) {
70 return;
71 }
72
73 partial.ssid = partial.ssid || '';
74 partial.channel = partial.channel || partial.freq || '';
75
76 const sortKey = [
77 parseFloat(partial.signal),
78 parseInt(partial.channel.split(' ')[1])
79 ];
80
81 results.push([sortKey, partial]);
82 }
83
84 return results
85 .sort()
86 .map(([, {bssid, ssid, signal, channel}]) => {
87 ssid = ssid.padStart(40, ' ').substr(0, 40);
88 channel = channel.padEnd(12, ' ');
89 return `${signal} ${channel} ${ssid} ${bssid}`;
90 })
91 .join('\n') + '\n';
92 }
93
94 async function main() {
95 const iface = process.argv[2] || await findInterface();
96
97 if (iface === '-') {
98 const scanResult = await fs.readFile('/dev/stdin', 'utf-8');
99 const prettyScanResult = formatScanResult(scanResult);
100 process.stdout.write(prettyScanResult);
101 } else {
102 for (;;) {
103 const scanResult = await scanInterface(iface).catch((err) => err.toString());
104 const prettyScanResult = formatScanResult(scanResult);
105 process.stdout.write('\x1b[2J\x1b[0f');
106 process.stdout.write(prettyScanResult);
107 await sleep(3000);
108 }
109 }
110 }
111
112 main().catch((err) => {
113 console.log('Unhandled error!', err);
114 process.exit(1);
115 });