| 113 | return None |
| 114 | |
| 115 | def _run_connect( |
| 116 | self, ssid: str, password: str |
| 117 | ) -> subprocess.CompletedProcess[str]: |
| 118 | # First try connecting if profile already exists |
| 119 | result = subprocess.run( |
| 120 | ["netsh", "wlan", "connect", f"ssid={ssid}", f"name={ssid}"], |
| 121 | capture_output=True, |
| 122 | text=True, |
| 123 | timeout=15, |
| 124 | ) |
| 125 | if result.returncode == 0: |
| 126 | return result |
| 127 | |
| 128 | # Profile doesn't exist — create a temporary XML profile |
| 129 | profile_xml = f"""<?xml version="1.0"?> |
| 130 | <WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1"> |
| 131 | <name>{ssid}</name> |
| 132 | <SSIDConfig> |
| 133 | <SSID> |
| 134 | <name>{ssid}</name> |
| 135 | </SSID> |
| 136 | </SSIDConfig> |
| 137 | <connectionType>ESS</connectionType> |
| 138 | <connectionMode>manual</connectionMode> |
| 139 | <MSM> |
| 140 | <security> |
| 141 | <authEncryption> |
| 142 | <authentication>WPA2PSK</authentication> |
| 143 | <encryption>AES</encryption> |
| 144 | <useOneX>false</useOneX> |
| 145 | </authEncryption> |
| 146 | <sharedKey> |
| 147 | <keyType>passPhrase</keyType> |
| 148 | <protected>false</protected> |
| 149 | <keyMaterial>{password}</keyMaterial> |
| 150 | </sharedKey> |
| 151 | </security> |
| 152 | </MSM> |
| 153 | </WLANProfile>""" |
| 154 | with tempfile.NamedTemporaryFile(mode="w", suffix=".xml", delete=False) as f: |
| 155 | f.write(profile_xml) |
| 156 | profile_path = f.name |
| 157 | |
| 158 | try: |
| 159 | subprocess.run( |
| 160 | ["netsh", "wlan", "add", "profile", f"filename={profile_path}"], |
| 161 | capture_output=True, |
| 162 | text=True, |
| 163 | timeout=10, |
| 164 | ) |
| 165 | result = subprocess.run( |
| 166 | ["netsh", "wlan", "connect", f"ssid={ssid}", f"name={ssid}"], |
| 167 | capture_output=True, |
| 168 | text=True, |
| 169 | timeout=15, |
| 170 | ) |
| 171 | finally: |
| 172 | os.unlink(profile_path) |