WAP list with graph - help please

I have some code that lists all the visible WAPs.
(Though I don't think it is going to work for what I am wanting to do.) :frowning:

(python code)

#wifiscan.py
import subprocess
import json
import re
 
child = subprocess.Popen('sudo iwlist wlan0 scan | grep ESSID',shell=True,stdout=subprocess.PIPE)
output = child.communicate()[0]
output = output.decode("utf8")
output = output.replace('                   ESSID:','')
output = output.replace(' "','')
output = output.replace('"','')
output = output.splitlines()
 
output = json.dumps(output)
 
print (output)

What I am wanting - now - is to also have the strength also included.

Then, run that code in the python node and get the output and show a graph with all the SSID names / strengths.

(Sorry folks. the hamster is on holiday just now and I can't resolve how to parse the message and make a graph.)

Ok, so I change the line to:
grep -B2 ESSID and I get the strength of the signal also.

Now I'm stuck on how to get ONLY the FIRST and LAST lines.
I don't need the middle one.

                    Quality=70/70  Signal level=-37 dBm  
                    Encryption key:on
                    ESSID:"Marys_Farm_2.4"

So all I need is the Signal level and ESSID.

This is what works for me - running on an ESP32-S2 Mini using MicroPython.

import network
import time
import ubinascii

# init wifi
wlan = network.WLAN(network.STA_IF) #initialize the wlan object
wlan.active(True) #activates the wlan interface
wlan.disconnect()

def scan():
    accessPoints = wlan.scan() #perform a WiFi Access Points scan

    num_rows = len(accessPoints)
    print("num wifi: ",num_rows)

    for ap in accessPoints: #this loop prints each AP found in a single row on shell
        ssid = ap[0].decode()
        if (ssid != ''):
            bssid =  ubinascii.hexlify(ap[1],":").decode()
            ch = ap[2]
            rssi = ap[3]
            auth = hex(ap[4])
            vis = hex(ap[5])
            print("{:30s} | {:s} | {:2d} | {:3d} | {:s} | {:s}".format(ssid, bssid, ch, rssi, auth, vis))

# code starts here
start=time.time()
print("Start time: ",time.time(),"\n")

scan()
 
end=time.time()
print("\nEnd time: ",time.time())

print("\nrun time: ",end-start)

My code is run on a RasPi.

The code you showed me errors:

Traceback (most recent call last):
  File "wifiscan2.py", line 1, in <module>
    import network
ModuleNotFoundError: No module named 'network'

I am very new to python. (3 - sorry)
I understand the network module is needed. But that may be because you are using the ESP32 and micropython.

I'll try it on a RPi when I get back home later today.

1 Like

Sorry Thanks (?) Dave.... I appreciate the help.

I'm not near a RPi (so can't try this) - this is what ChatGPT suggests for a RPi...
Looks very similar to your listing.
The function returns a 'tupple' so you can collect whatever parts you need.

import subprocess
import re

def scan_wifi():
    # Run the iwlist command to scan for wireless networks
    cmd = subprocess.Popen(['sudo', 'iwlist', 'wlan0', 'scan'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output, _ = cmd.communicate()

    # Decode the output from bytes to string
    output = output.decode()

    # Extract the relevant information using regular expressions
    networks = re.findall(r'ESSID:"(.*?)".*?Frequency:(.*?)\s', output, re.DOTALL)

    # Return the list of networks
    return networks

wifi_networks = scan_wifi()

# Print the details of each network
for network in wifi_networks:
    ssid = network[0]
    frequency = network[1]
    print(f"Network: {ssid}, Frequency: {frequency}")
1 Like

(I can't get onto chatgpt.... But haven't tried that many times.)

That looks good.

Alas not quite. (Not your fault)

This is what I get:

Network: Marys_Farm_2.4, Frequency: 2.462
Network: MyRepublic 49D9, Frequency: 5.745
Network: WiFi-AFB6-5G, Frequency: 5.765
Network: Marys_Farm_5, Frequency: 5.32
Network: TelstraCA5E31, Frequency: 5.32
Network: \x00\x00\x00\x00\x00\x00\x00\x00\x00, Frequency: 2.437
Network: WiFi-F852, Frequency: 5.18
Network: Optus_ABA172_5GHz, Frequency: 2.412
Network: PiNet, Frequency: 2.437
Network: Optus_ABA172, Frequency: 2.427
Network: WiFi-AFB6, Frequency: 2.412
Network: Aussie Broadband 4928, Frequency: 2.412
Network: LIFX Color 1000_142486_AJ, Frequency: 2.412
Network: OPTUS_69E639M, Frequency: 5.18
Network: WiFi-F852-5G, Frequency: 5.18

So that is the frequency not signal strength.
I'll give it a go to see if I can work out what I need to get it showing me what I want.

Again: Thanks.

Try this...

import subprocess
import re

def scan_wifi():
    # Run the iwlist command to scan for wireless networks
    cmd = subprocess.Popen(['sudo', 'iwlist', 'wlan0', 'scan'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output, _ = cmd.communicate()

    # Decode the output from bytes to string
    output = output.decode()

    # Extract the relevant information using regular expressions
    networks = re.findall(r'ESSID:"(.*?)".*?Frequency:(.*?)\s.*?Signal level=(.*?)\s', output, re.DOTALL)

    # Return the list of networks
    return networks

wifi_networks = scan_wifi()

for network in wifi_networks:
    ssid = network[0]
    frequency = network[1]
    signal_level = network[2]
    print(f"Network: {ssid}, Frequency: {frequency}, Signal Level: {signal_level}")

Sorry, but two bits of code?
(Train no longer on the tracks.)

If I run the top one - which looks like a starting point - I get nothing back.

I was about to ask how to get to see the entire network[] so I could change the fist one to give me what I want.

What is the second bit of code?

Sorry finger trouble at this end - try the listing above (again) as I've corrected it.

1 Like

Love you Dave. :wink: (No, not in that way.)

I will delete the frequency part. Not needed.

I'll get it working I hope.
Oh, ok, one quirk: I need to add a separator between the names. (For the split node downstream).
so to put a , between then I just put that at the end of the print line - yes?

Sounds as if you are now back on "track".
As I said, I have no access to a RPi, so can't test anything until I get home later today.
I'm sure another member of the Forum may well jump in and help with the Python syntax.

That's ok. And again: THANKS!

Yes, you were amazing helping me.

(I'd better mark the solution)

(I'll have to do more digging and it isn't outputting what NR likes.)

This is what works

output = child.communicate()[0]
output = output.decode("utf8")
output = output.replace('                   ESSID:','')
output = output.replace(' "','')
output = output.replace('"','')
output = output.splitlines()
 
output = json.dumps(output)
 
print (output)

So I will have to study the structure of that and apply it to the new code.

If you use egrep instead of grep ("extended grep", I believe it is installed by default) you can search for lines containing either "ESSID" or "Signal", which might simplify further processing.

sudo iwlist wlan0 scan | egrep 'ESSID|Signal'
                    Quality=47/70  Signal level=-63 dBm
                    ESSID:"TALKTALK9B7661"
                    Quality=28/70  Signal level=-82 dBm
                    ESSID:"BT-JGAW38"
                    Quality=48/70  Signal level=-62 dBm
                    ESSID:"GreyWagtail561"
                    Quality=39/70  Signal level=-71 dBm
                    ESSID:"BT-S9A63M"
                    Quality=40/70  Signal level=-70 dBm
                    ESSID:"BTWi-fi"
                    Quality=45/70  Signal level=-65 dBm
                    ESSID:"BTWi-fi"
                    Quality=41/70  Signal level=-69 dBm
                    ESSID:"BTHub6-WX8H"
                    Quality=36/70  Signal level=-74 dBm
                    ESSID:"BT-75CPCX"
                    Quality=27/70  Signal level=-83 dBm

I usually use awk to further process multi-line shell output records, but you can use other Bash tools to reduce it to single lines. For example (sorry it's not obvious what it does)

 sudo iwlist wlan0 scan | egrep 'ESSID|Signal' |sed -z 's/\n//g; s/ *Q/\nQ/g; s/  */ /g; s/l l/l_l/g; s/:/=/g'

Quality=52/70 Signal_level=-58 dBm ESSID="TALKTALK9B7661"
Quality=28/70 Signal_level=-82 dBm ESSID="BT-JGAW38"
Quality=49/70 Signal_level=-61 dBm ESSID="GreyWagtail561"
Quality=42/70 Signal_level=-68 dBm ESSID="BTWi-fi"
Quality=43/70 Signal_level=-67 dBm ESSID="BTWi-fi"
Quality=45/70 Signal_level=-65 dBm ESSID="BTHub6-WX8H"
Quality=28/70 Signal_level=-82 dBm ESSID="BT-75CPCX"
Quality=31/70 Signal_level=-79 dBm ESSID="BTWi-fi"

1 Like

How about creating a json object that you can (eventually) send to Node-RED via MQTT??
Here's a snippet for you...

output = json.dumps({"ssid": network[0], "signal_level": network[2]})
print(output)
1 Like

Thanks.

I am still in the shallow part of the scheme for the O/S commands, so that is another problem for me.

I'll look at it but I will also look at Dave's reply too.

Sorry Dave, something is being lost in translation for me.

(error)

exit code: 1,   File "wifiscan5.py", line 25
    print(f"Network: {ssid}, Frequency: {frequency}, Signal Level: {signal_level}")
                                                                                 ^
SyntaxError: invalid syntax

Code:

import json
import subprocess
import re

def scan_wifi():
    # Run the iwlist command to scan for wireless networks
    cmd = subprocess.Popen(['sudo', 'iwlist', 'wlan0', 'scan'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output, _ = cmd.communicate()

    # Decode the output from bytes to string
    output = output.decode()

    # Extract the relevant information using regular expressions
    networks = re.findall(r'ESSID:"(.*?)".*?Frequency:(.*?)\s.*?Signal level=(.*?)\s', output, re.DOTALL)

    # Return the list of networks
    return networks

wifi_networks = scan_wifi()

for network in wifi_networks:
    ssid = network[0]
    frequency = network[1]
    signal_level = network[2]
    print(f"Network: {ssid}, Frequency: {frequency}, Signal Level: {signal_level}")
#    this = print(f,"Network: {ssid}, Level: {signal_level}")

    output = json.dumps({"ssid": network[0], "signal_level": network[2]})
    print(output)

I am bad with code at the start and leave a lot of old stuff in so I know what to NOT do in subsequent tries. When all working I take out all the comments that aren't needed/old lines.

For the sake of it this is how it is done in NR

[{"id":"057c784d0fb29dfc","type":"pythonshell in","z":"c56bddee.ca0a18","name":"wifiscan","pyfile":"/home/pi/python_stuff/wifiscan5.py","virtualenv":"","continuous":false,"stdInData":false,"x":145,"y":3490,"wires":[["b7ae270d16a37445","cab9900ce3a0acc3"]],"l":false},{"id":"cab9900ce3a0acc3","type":"debug","z":"c56bddee.ca0a18","name":"debug 10","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":450,"y":3330,"wires":[]}]

OK, sorry, I goofed!

Line 25. I commented it out and it works.

Hang on.

I think you are nearly there now. ( so please don’t get diverted ) but this tool may help in future -jc | CLI tool and python library that converts the output of popular command-line tools, file-types, and common strings to JSON, YAML, or Dictionaries. This allows piping of output to tools like jq and simplifying automation scripts.

1 Like