Skip to content

Instantly share code, notes, and snippets.

@Lokno
Last active January 27, 2023 04:36
Show Gist options
  • Save Lokno/271f36547530ed33f9a94c9d304cf705 to your computer and use it in GitHub Desktop.
Save Lokno/271f36547530ed33f9a94c9d304cf705 to your computer and use it in GitHub Desktop.
Python 3 Script for automatically connecting a PC to hotspot created by the Nintendo Switch to serve media to a smart device
# Python 3 Script for automatically connecting a PC to the temporary hotspot
# created by the Nintendo Switch to serve media to a smartphone
#
# How to Set Up the hotspot transfer from the Nintendo Switch:
# 1. From the HOME Menu select Album, then select a screenshot or video capture.
# 2. Select Sharing and Editing, then select Send to Smartphone.
# 3. Select Only This One or Send a Batch.
# 4. A screen with a QR code will appear with the encoded SSID and password.
# This is where the script will take over.
#
# Media served by Switch will likely be at: http://192.168.0.1/index.html
#
# Requires Python 3.6+
# Requires opencv-python
# You must have a wifi adapter
# Supports Windows, Linux, and Mac OS.
# Linux requires nmcli and must be run with root privileges
from sys import exit,argv,platform
import subprocess
try:
import cv2
except ModuleNotFoundError:
print('OpenCV Python Module Not Found')
print(' install via pip:')
print(' python -m pip install opencv-python')
exit(-1)
import os
from time import sleep
import signal
import keyboard
import webbrowser
# OpenCV index of the device capturing your Nintendo Switch
# Note that if you have a webcam, that might be assigned to 0
video_capture_device_id = 0
# delay after adding the wifi profile
delay_new_profile = 3 # seconds
# delay after detecting the QR code encoding the url
delay_detect_url = 1 # seconds
supported_platforms = ['win32','linux','darwin']
class MacWifi():
def __init__(self):
self.SSID = ''
self.password = ''
wifi_interface = os.popen("networksetup -listallhardwareports | grep -A 1 Wi-Fi | grep Device | cut -d ' ' -f 2").read()
self.wifi_interface = wifi_interface.strip()
def create_new_connection(self, SSID, password):
self.SSID = SSID
self.password = password
command = f'networksetup -addpreferredwirelessnetworkatindex {self.wifi_interface} "{self.SSID}" 0 WPA2 {self.password}'
os.system(command)
def connect(self):
command = f'networksetup -setairportnetwork {self.wifi_interface} "{self.SSID}"'
os.system(command)
def delete_profile(self):
command = f'networksetup -removepreferredwirelessnetwork {self.wifi_interface} "{self.SSID}"'
os.system(command)
def disconnect(self):
os.system(f"networksetup -setairportpower {self.wifi_interface} off")
class LinuxWifi():
def __init__(self):
self.SSID = ''
self.password = ''
user_groups = os.popen("groups").read().strip().split(' ')
if 'root' not in user_groups:
print('ERROR: User not authorized to control networking')
exit(-1)
try:
subprocess.run(['nmcli', '--version'], check=True)
except subprocess.CalledProcessError:
print('ERROR: nmcli not found. Please install network-manager')
exit(-1)
wifi_interface = os.popen("nmcli -t -f device,type device | awk -F: '/wifi/ {print $1; exit}'").read()
self.wifi_interface = wifi_interface.strip()
def create_new_connection(self, SSID, password):
self.SSID = SSID
self.password = password
def connect(self):
command = f'nmcli device wifi connect "{self.SSID}" password "{self.password}" ifname {self.wifi_interface}'
os.system(command)
def delete_profile(self):
command = f'nmcli connection delete "{self.SSID}"'
os.system(command)
def disconnect(self):
os.system(f"nmcli device disconnect {self.wifi_interface}")
class WinWifi():
def __init__(self):
self.SSID = ''
self.password = ''
def create_new_connection(self, SSID, password):
self.SSID = SSID
self.password = password
config = """<?xml version=\"1.0\"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
<name>"""+self.SSID+"""</name>
<SSIDConfig>
<SSID>
<name>"""+self.SSID+"""</name>
</SSID>
</SSIDConfig>
<connectionType>ESS</connectionType>
<connectionMode>auto</connectionMode>
<MSM>
<security>
<authEncryption>
<authentication>WPA2PSK</authentication>
<encryption>AES</encryption>
<useOneX>false</useOneX>
</authEncryption>
<sharedKey>
<keyType>passPhrase</keyType>
<protected>false</protected>
<keyMaterial>"""+self.password+"""</keyMaterial>
</sharedKey>
</security>
</MSM>
</WLANProfile>"""
file_path = os.path.expandvars(f'%TEMP%\\{self.SSID:s}.xml')
command = f'netsh wlan add profile filename="{file_path:s}" interface=Wi-Fi'
with open(file_path, 'w') as file:
file.write(config)
os.system(command)
def connect(self):
os.system("netsh wlan connect name=\""+self.SSID+"\" ssid=\""+self.SSID+"\" interface=Wi-Fi")
def delete_profile(self):
os.system(f'netsh wlan delete profile name="{self.SSID:s}" interface=Wi-Fi')
def disconnect(self):
os.system("netsh wlan disconnect interface=Wi-Fi")
# Expected Format:
# WIFI:S:<<ssid>>;T:<<type>>;P:<<pw>>;;
def decode_ssid(s):
ssid_str = None
type_str = None
pw_str = None
for p in s.split(';'):
items = p.split(':')
if p.startswith('WIFI') and len(items) == 3:
ssid_str = items[2]
elif p.startswith('T') and len(items) == 2:
type_str = items[1]
elif p.startswith('P') and len(items) == 2:
pw_str = items[1]
return ssid_str,type_str,pw_str
loop = True
def handler(signum, frame):
global loop
loop = False
if __name__ == "__main__":
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)
script_name = os.path.basename(argv[0])
print(f'Connect to Nintendo Switch Python 3 Script ({script_name:s})\n-----------------\nType ctrl+c to quit\n')
if platform not in supported_platforms:
print('ERROR: Operating system not supported')
exit(-1)
# Optionally read video capture device index from arguments
if len(argv) > 1 and argv[1].isnumeric():
video_capture_device_id = int(argv[1])
if len(argv) > 2:
print(f'Additional arguments ignored (usage: {script_name:s} <device index (integer)>)')
elif len(argv) > 1:
print(f'Arguments ignored (usage: {script_name:s} <device index (integer)>)')
print(f'Opening capture device index {video_capture_device_id:d}...', end = '')
vid = cv2.VideoCapture(video_capture_device_id)
if not vid.isOpened:
print(f'\nCould not open video capture device index {video_capture_device_id:d}')
exit(-1)
else:
print(f' opened!')
detect = cv2.QRCodeDetector()
last_value = None
waiting_on_ssid_pw = True
waiting_on_url = False
print('Watching video for QR code...')
if platform == 'win32':
wc = WinWifi()
elif platform == "darwin":
wc = MacWifi()
elif platform == "linux":
wc = LinuxWifi()
else:
raise Exception(f"Unsupported operating system: {platform}")
while loop:
if waiting_on_ssid_pw or waiting_on_url:
ret, frame = vid.read()
value, _, _ = detect.detectAndDecode(frame)
if value and last_value != value:
print('New QR Code Detected!')
last_value = value
recognized = False
if waiting_on_ssid_pw:
if value.startswith('WIFI'):
print('SSID/PW received!')
recognized = True
ssid_str,_,pw_str = decode_ssid(value)
print(f'Adding profile for ssid {ssid_str:s}')
wc.create_new_connection(ssid_str, pw_str)
sleep(delay_new_profile)
print(f'Requesting connection to ssid {ssid_str:s}')
wc.connect()
waiting_on_ssid_pw = False
waiting_on_url = True
print('Waiting for QR code with encoded url...')
else:
if value.startswith('http'):
print('URL received!')
recognized = True
url_str = value
sleep(delay_detect_url)
waiting_on_url = False
webbrowser.open(url_str)
if not recognized:
print('Encoded value not recognized, continuing to wait...')
if keyboard.is_pressed("q"):
loop = False
print('Disconnecting and cleaning up...')
if not waiting_on_ssid_pw:
wc.disconnect()
wc.delete_profile()
vid.release()
cv2.destroyAllWindows()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment