How to connect to WiFi network on Windows?

12,328

Solution 1

Is there a pressing need for this to be a library? It can be achieved easily without one (or if you prefer, save it as a module and import it).

If the wireless interface is wlan and the SSID and Profile name are the same (usually true) then just use

cmd = "netsh wlan connect name={0} ssid={0}".format(tt)
k = subprocess.run(cmd, capture_output=True, text=True).stdout

In the snippet above, tt is the name of the SSID you wish to connect to - it's a variable in my code because it connects to different SSIDs in turn.

I know that using subprocess is something of a kludge, but the snippet above doesn't add any significant overhead.

This is part of a script I wrote to 'harvest' data from my parents' solar inverters: the inverters have their own SSID, so the script has to connect to each in turn; fetch real-time data using requests; store the data in a PostgreSQL db; and then reconnect to household wifi.

I know the devices also store historical data, however the data API was disabled by the manufacturer in ~2018 to drive owners towards their app (hence giving them user data to package and sell).

Solution 2

To connect wifi using python in windows, the better option is to use the winwifi module:

I recommend you to install plumbum before installing winwifi. This is the link to download plumbum: https://pypi.org/project/plumbum/

After this install winwifi from here:https://pypi.org/project/winwifi/ It's better to install it in 32-bit python folder.

After installing you could check the module by the following code (This is to connect router which was connected to the device before):

import winwifi
winwifi.WinWiFi.connect('the_exact_ssid_or_name_of_your_known_wifi_router')

On running this code on your IDLE, you could see that the wifi is connected to your device. If you want to connect a new device you could use the code after adding profile:

import winwifi
winwifi.WinWiFi.addprofile('ssid_of_router')
winwifi.WinWiFi.connect('the_ssid_of_router', 'password')

You can disconnect the current Wifi using the command:

import winwifi
winwifi.WinWiFi.disconnect()

There are many more commands on this module, try to explore them. Just refer to the main.py file in winwifi folder for many more.

Solution 3

Notes (about [PyPI]: wireless 0.3.2]):

  • Does not (yet) support Python 3: as somewhere in the code it uses the cmp function (which is only available in Python 2)
    • I wanted to submit a pull request (as the fix is trivial), but apparently on GitHub it was already fixed, yet the PyPI repository was not updated (since 2016)
  • Does not work on Win (only Nix drivers are listed on the homepage - basically it only launches shell commands)

As a consequence, I'd suggest looking for alternatives:

Alright, after a lot of browsing of:

, I was able to come up with something.

code00.py:

#!/usr/bin/env python3

import sys
import time
import ctypes
import comtypes
import traceback
from win32wifi import Win32Wifi as ww


ERROR_SUCCESS = 0

WLAN_CONNECTION_HIDDEN_NETWORK = 0x00000001


class WLANException(Exception): pass


class ConnectCallbackContext(ctypes.Structure):
    _fields_ = [
        ("guid", ctypes.c_wchar_p),
        ("start", ctypes.c_byte),
        ("end", ctypes.c_byte),
        ("fail", ctypes.c_byte),
    ]


def _wlan_connect_callback(data, context_addr):
    if context_addr:
        context = ConnectCallbackContext.from_address(context_addr)
        if str(data.interfaceGuid) == context.guid and data.notificationSource == ww.WLAN_NOTIFICATION_SOURCE_DICT[ww.WLAN_NOTIFICATION_SOURCE_ACM]:
            if data.notificationCode == ww.WLAN_NOTIFICATION_ACM_ENUM.wlan_notification_acm_connection_start.name:
                context.start += 1
            elif context.start:
                if data.notificationCode == ww.WLAN_NOTIFICATION_ACM_ENUM.wlan_notification_acm_connection_complete.name:
                    context.end += 1
                elif data.notificationCode == ww.WLAN_NOTIFICATION_ACM_ENUM.wlan_notification_acm_connection_attempt_fail.name:
                    context.fail += 1


def wireless_connect(
        ssid,
        password,
        timeout=15,  # secs
        authentication="WPA2PSK",  # "open", 
        encryption="AES",  # "WEP",
        key_type="passPhrase",  # "networkKey", 
        interface_index=0,  # Don't modify this (until PCs with more than 1 WLAN adapter arise :) )
    ):
    interfaces = ww.getWirelessInterfaces()
    if interface_index < 0 or len(interfaces) < interface_index:
        raise WLANException(-1, "No WLAN interface for given index")
    interface = interfaces[interface_index]
    profile_name = ssid + "_profile_tmp"
    ssid_hex = "".join((hex(ord(c))[2:] for c in ssid)).upper()
    profile_string = f"""<?xml version=\"1.0\"?>
        <WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\">
            <name>{profile_name}</name>
            <SSIDConfig>
                <SSID>
                    <hex>{ssid_hex}</hex>
                    <name>{ssid}</name>
                </SSID>
            </SSIDConfig>
            <connectionType>ESS</connectionType>
            <connectionMode>manual</connectionMode>
            <MSM>
                <security>
                    <authEncryption>
                        <authentication>{authentication}</authentication>
                        <encryption>{encryption}</encryption>
                        <useOneX>false</useOneX>
                    </authEncryption>
                    <sharedKey>
                        <keyType>{key_type}</keyType>
                        <protected>false</protected>
                        <keyMaterial>{password}</keyMaterial>
                    </sharedKey>
                </security>
            </MSM>
        </WLANProfile>
    """
    connection_params = {
        "connectionMode": "wlan_connection_mode_temporary_profile",
        "profile": profile_string,
        "ssid": None,
        "bssidList": None,
        "bssType": "dot11_BSS_type_infrastructure",
        "flags": WLAN_CONNECTION_HIDDEN_NETWORK,
    }

    ctx = ConnectCallbackContext(interface.guid_string, 0, 0, 0)
    notification_obj = ww.registerNotification(_wlan_connect_callback, context=ctypes.pointer(ctx))

    try:
        res = ww.connect(interface, connection_params)
    except Exception as e:
        ww.unregisterNotification(notification_obj)
        raise WLANException("WlanConnect failed") from e

    end_time = time.time() + timeout;
    while time.time() < end_time:
        time.sleep(0.5)
        if ctx.end:
            break
    ww.unregisterNotification(notification_obj)
    if ctx.end:
        if ctx.fail:
            raise WLANException(-2, "Connection failed")
    else:
        raise WLANException(-3, "Connection timed out")
    return interface.guid_string


def wireless_disconnect(interface_guid):  # Borrowed (and improved) this func from win32wifi.Win32Wifi, to avoid creting the interface when only its guid is required
    handle = ww.WlanOpenHandle()
    try:
        ww.WlanDisconnect(handle, comtypes.GUID(interface_guid))
    except Exception as e:
        raise WLANException("WlanDisconnect failed") from e
    finally:
        ww.WlanCloseHandle(handle)


def main(argv):
    if argv:
        try:
            guid = argv[0]
            print("Disconnecting wireless interface {:s} ...".format(guid))
            wireless_disconnect(guid)
        except:
            traceback.print_exc()
    else:
        try:
            print("Connecting to wireless network ...")
            ssid = "Network SSID"  # ssid and pwd here are (deliberately) dummy
            pwd = "Network password"
            guid = wireless_connect(ssid, pwd)
            print("Connected interface {:s}".format(guid))
        except:
            traceback.print_exc()


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main(sys.argv[1:])
    print("\nDone.")

script.bat:

setlocal enableextensions enabledelayedexpansion

set _EXE_PTYHON="e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe"

time <nul

ping www.google.com

%_EXE_PTYHON% code00.py
ping www.google.com

%_EXE_PTYHON% code00.py {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806}
ping www.google.com

time <nul

Output:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q056721759]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[prompt]> script.bat

[prompt]> time  0<nul
The current time is:  1:45:08.31
Enter the new time:
[prompt]> ping www.google.com
Ping request could not find host www.google.com. Please check the name and try again.

[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32

Connecting to wireless network ...
Connected interface {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806}

Done.

[prompt]> ping www.google.com

Pinging www.google.com [2a00:1450:400d:809::2004] with 32 bytes of data:
Reply from 2a00:1450:400d:809::2004: time=11ms
Reply from 2a00:1450:400d:809::2004: time=12ms
Reply from 2a00:1450:400d:809::2004: time=12ms
Reply from 2a00:1450:400d:809::2004: time=19ms

Ping statistics for 2a00:1450:400d:809::2004:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 11ms, Maximum = 19ms, Average = 13ms

[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806}
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32

Disconnecting wireless interface {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806} ...

Done.

[prompt]> ping www.google.com
Ping request could not find host www.google.com. Please check the name and try again.

[prompt]> time  0<nul
The current time is:  1:45:12.82
Enter the new time:

Notes:

  • In order to create a POC I had to add some code (e.g. wireless_disconnect) not necessarily related to the question, which adds complexity.
    BTW, the code is waaay more complex than I initially anticipated (that's why I didn't bother to explain it - as it would be an overkill), but I don't see any way of trimming it down
  • script.bat (and time <nul) are just to prove in console that the code is connecting / disconnecting from the wireless network (and that I'm not connecting from Win in parallel)
    • I don't know where the " 0" part from time 0<nul (in the output) comes from
  • As I specified, this is more like a POC, there are some limitations in (my and Win32Wifi) code. Some scenarios (networks) might not work without (small) code changes
  • Although connection to the network succeeds (and works), in the System Tray, the network status still appears as disconnected (actually for a fraction of a second it appears connected, but then it automatically changes). Also, the System Tray network icon shows as Connected. I'm not sure whether this is on my side (I forgot to somehow notify Win - although this doesn't make much sense), or Win doesn't like "someone else" to connect to wireless networks
  • THE MOST IMPORTANT ONE: The above code will not work OOTB, because Win32Wifi is buggy. I found 2 bugs that are fatal (critical) for this scenario, and a bunch of other smaller ones.
    I've just submitted [GitHub]: kedos/win32wifi - Fixes (some critical) and improvements. Not sure what its outcome it's going to be (considering the inactivity period).

    As an alternative, you could download the patch, and apply the changes locally. Check [SO]: Run/Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (@CristiFati's answer) (Patching utrunner section) for how to apply patches on Win (basically, every line that starts with one "+" sign goes in, and every line that starts with one "-" sign goes out). I am using Cygwin, btw.

Share:
12,328
GBDGBDA
Author by

GBDGBDA

Engineer in the making.

Updated on June 09, 2022

Comments

  • GBDGBDA
    GBDGBDA almost 2 years

    I am trying to write a script in Python 3 but all modules available today work on python 2 which will enable me to search for wireless networks and to connect to them. Is there any Python 3 library for this?

    The code I tried for python 2

    from wireless import Wireless
    wireless = Wireless()
    wireless.connect(ssid='ssid', password='password')
    

    which is giving me an error

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "C:\Users\Himanshu Poddar\AppData\Local\Programs\Python\Python36-32\lib\site-packages\wireless\Wireless.py", line 23, in __init__
        self._driver_name = self._detectDriver()
      File "C:\Users\Himanshu Poddar\AppData\Local\Programs\Python\Python36-32\lib\site-packages\wireless\Wireless.py", line 50, in _detectDriver
        compare = self.vercmp(ver, "0.9.9.0")
      File "C:\Users\Himanshu Poddar\AppData\Local\Programs\Python\Python36-32\lib\site-packages\wireless\Wireless.py", line 71, in vercmp
        return cmp(normalize(actual), normalize(test))
    NameError: name 'cmp' is not defined
    

    But this is not working since it is based on python 2. Is there any way to connect to a wifi using Python 3

  • GBDGBDA
    GBDGBDA almost 5 years
    Thats the main thing. I want to connect to a wifi network. If you know by any way we can achieve this
  • GBDGBDA
    GBDGBDA almost 5 years
    No i ticked it because it seemed to me like you did a lot of research for this question
  • CristiFati
    CristiFati almost 5 years
    @PoddarHimanshu: Sorry, wrong answer to comment. Anyway without the 3rd-party patch, you won't be able to achieve your goal. So (no ticks seen here :) ): [SO]: What should I do when someone answers my question?. Let me know where you have trouble (cause I have a feeling that they'll come - especially when it comes to patching files).
  • Himanshuman
    Himanshuman almost 5 years
    @CristiFati there were multiple packages in Python 2 to connect to a wireless network. Why isn't there any one of them for Python 3. Can you suggest me to create a wifi package in python. What things should I study and how should I start. What things will help me in talking to wifi drivers of windows in Python.
  • CristiFati
    CristiFati almost 5 years
    @HimanshuPoddar: Could you please name one (that works on Win)?
  • CristiFati
    CristiFati almost 5 years
    Unfortunately (for you) this seems to be the only one. But with my patch, you could get smth really nice going on here.
  • Himanshuman
    Himanshuman almost 5 years
    @CristiFati Wireless actually used to work on Windows using Python 2. Can you please tell me how can I create a wifi package using python. I mean just tell me what will be required cuz I really want to create one and I really have have no idea how should I start.
  • CristiFati
    CristiFati almost 5 years
    What exactly was working on Python 2? connecting to a wireless network? Apparently there's no way known to human kind doing this from Python (in C there's no problem)
  • Himanshuman
    Himanshuman almost 5 years
    So you are saying there is currently no way one can make a python 3 package to connect to a wireless wifi network?
  • CristiFati
    CristiFati almost 5 years
  • GBDGBDA
    GBDGBDA about 4 years
    where do I provide the password?
  • GBDGBDA
    GBDGBDA about 4 years
    TypeError: __init__() got an unexpected keyword argument 'capture_output'
  • GT.
    GT. about 4 years
    @GBDGBDA - ordinarily there's no requirement for a password (in fact the only thing required is the profile, which is the name parameter). Run netsh wlan show profile at the command prompt to get a list of profiles. As for the 'unexpected keyword' error: it could be a bunch of things - possibly that your wireless device is not wlan; possibly that you've set tt to be something other than the SSID you're trying to connect to.
  • xiaxio
    xiaxio over 3 years
    Just FYI on troubleshooting wifi. Sometimes the router disconnects my computer from the internet. I have ssid and password. However, when trying winwifi.WinWiFi.connect('network_ssid', 'network_pwd') it does not reconnect. But if instead I only do winwifi.WinWiFi.connect('network_ssid') -without the pwd- it will reconnect to the wifi. I have Python 3.8, Windows 10 and PyCharm Community Edition 2020.2
  • Ashiq Firoz
    Ashiq Firoz over 3 years
    Bro, if you connected to your wifi in your pc before trying this , windows will save your ssid and password. Then you just need to use: winwifi.WinWiFi.connect('network_ssid')