1

Willing to add wifi connections from a kivy app and I'm using a simple function to edit wpa_supplicant.conf file adding there new networks.

My function correctly writes the configuration there and seems identical to the configs aded through raspbian GUI...

But when I reboot the raspberry, it says no networks interfaces were found but it get solved if I delete the last added lines from the wpa_supplicant.conf file. For some reasons raspbian fails to read properly this file after the edit, but I don't see what I'm doing wrong here which defers from the default configurations.

Hope someone can provide me some hint... I run the script as sudo, so can't be any permission issues, I tried to look into any diferences in the way I write the config and the config provided by raspbian, but no clue...

Here you can see the code:

def CreateWifiConfig(SSID, password):
        config = (
                '\nnetwork={{\n' +
                '\tssid="{}"\n' +
                '\tpsk="{}"\n' + '}}').format(SSID, password)
        print(config)
        with open("/etc/wpa_supplicant/wpa_supplicant.conf", "a+") as wifi:
            wifi.write(config)
        wifi.close()
        print("Wifi config added")```
lopalcar
  • 33
  • 1
  • 9

3 Answers3

1

This is just a drive-by comment - I've not run your code, I'm just interpreting it based on what I'm reading - but a couple of things strike me here:

1) I don't see a key_mgmt value in your config. Usually this is like WPA-PSK, but you can see some addtional possible values here

2) Because you're using the "with" statement, the file should be automatically closed for you (you shouldn't need to call close() on it a second time).

3) The way you're constructing the config string looks a little strange to me, how about something like this (totally subjective, just a bit more readable to my eyes):

def CreateWifiConfig(SSID, password):
  config_lines = [
    '\n',
    'network={',
    '\tssid="{}"'.format(SSID),
    '\tpsk="{}"'.format(password),
    '\tkey_mgmt=WPA-PSK',
    '}'
  ]

  config = '\n'.join(config_lines)
  print(config)

  with open("/etc/wpa_supplicant/wpa_supplicant.conf", "a+") as wifi:
    wifi.write(config)

  print("Wifi config added")

4) The other thing to note here is that this code is not idempotent - you'll not be able to run this multiple times without compromising the integrity of the file content. You're probably aware of that already and may not especially care, but just pointing it out anyway.

Hopefully the missing key_mgmt is all it is.

gaelicyoda
  • 71
  • 1
  • 8
  • Hi, long time since I asked this question, already moved on to other methods... anyways, thanks a lot for taking the time to answer, I remember I replicated this file to be exactly the same "visually" as the one which generated raspbian itself when I connected to the network using the GUI... So I don't think the key_mgmt is something missing... Also, what does the idempotent means exactly? If I remember correctly, this code was just adding additional networks to the config file, you mean problem in case network duplicated? – lopalcar Apr 30 '20 at 15:55
  • No worries - I actually should've checked out the date stamp and figured that :D – gaelicyoda May 01 '20 at 01:03
  • But looking a bit more, I'm not entirely sure the `key_mgmt` thing is the problem now myself either... reading the docs, it indicates that if it's not set, it will default to use `WPA-PSK WPA-EA` anyway, so that whould probably have you covered. But by "idempotent" I mean it would leave the file content in the same state if you ran the function once or a hundred times, i.e. it would take account of the current state and only update it if it was necessary to do so. There's a more in-depth explanation [here](https://stackoverflow.com/questions/1077412/what-is-an-idempotent-operation) – gaelicyoda May 01 '20 at 01:11
1

this is my solution to add or update wifi. The json argument in the method is a dictionary with value: {"ssid": "wifi_name", "psk": "password"}. This script can be easily modified to add id_str or priority to a networks.

def command_add_wifi(json):
  print("-> Adding wifi")
  
  # Read file WPA suppliant
  networks = []
  with open("/etc/wpa_supplicant/wpa_supplicant.conf", "r") as f:
    in_lines = f.readlines()  

  # Discover networks
  out_lines = []
  networks = []
  i = 0
  isInside = False
  for line in in_lines:
    if "network={" == line.strip().replace(" ", ""):
      networks.append({})
      isInside = True
    elif "}" == line.strip().replace(" ", ""):
      i += 1
      isInside = False
    elif isInside:      
      key_value = line.strip().split("=")
      networks[i][key_value[0]] = key_value[1]
    else:
      out_lines.append(line)

  # Update password or add new
  isFound = False
  for network in networks:
    if network["ssid"] == f"\"{json['ssid']}\"":
      network["psk"] = f"\"{json['psk']}\""
      isFound = True
      break
  if not isFound:
    networks.append({
      'ssid': f"\"{json['ssid']}\"",
      'psk': f"\"{json['psk']}\"",
      'key_mgmt': "WPA-PSK"
    })

  # Generate new WPA Supplicant
  for network in networks:
    out_lines.append("network={\n")
    for key, value in network.items():
      out_lines.append(f"    {key}={value}\n")      
    out_lines.append("}\n\n")

  # Write to WPA Supplicant
  with open('/etc/wpa_supplicant/wpa_supplicant.conf', 'w') as f:
    for line in out_lines:
      f.write(line)

  print("-> Wifi added !")

MaxThom
  • 1,165
  • 1
  • 13
  • 20
0

I tried rewriting the 'wpa_supplicant.conf' file using a Python script and the whole Wi-Fi system just broke down, and I have to reformat and reinstall Raspbian OS on the SD card all over again. Creating a new 'wpa_supplicant.conf' file in the boot drive of the SD card doesn't work either. Luckily, I found a solution to connect to a new network using a Python script, but before that, you'll need to follow these steps first.

Solution: Instead of rewriting 'wpa_supplicant.conf' using with open('/etc/wpa_supplicant/wpa_supplicant.conf', 'w') as file:, rewrite the 'interfaces' file from the /etc/network/interfaces path.

Add this code below the source-directory /etc/network/interfaces.d line of the 'interfaces' file:

auto wlan0
iface wlan0 inet dhcp
wpa-ssid "WIFI NETWORK NAME HERE"
wpa-psk "WIFI NETWORK PASSWORD HERE"

Save the 'interfaces' file and open the 'wpa_supplicant.conf' file.

Add update_config=1 between the lines of ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev and network={.... From my observation, this kinda ignores the contents of network={... and divert its retrieval of Wi-Fi information to the /etc/network/interfaces file.

Save the file and reboot.

Once rebooted, you can rewrite the /etc/network/interfaces file by executing a Python script whenever you want to change to a new network (the script must lead to a system reboot at the end in order for the new Wi-Fi changes to take effect). (I don't know whether the 'interfaces' file allows multiple networks to be added into, so far I only tested with one network at a time, since this has already fulfilled my project goal.) Here's a Python code sample to change the network information in the 'interfaces' file:

import os
import time

SSID = input('Enter SSID: ')
password = input("Enter password: ")

with open('/etc/network/interfaces', 'w') as file:
    content = \
        '# interfaces(5) file used by ifup(8) and ifdown(8)\n\n' + \
        '# Please note that this file is written to be used with dhcpcd\n' + \
        "# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'\n" + \
        '# Include files from /etc/network/interfaces.d:\n' + \
        'source-directory /etc/network/interfaces.d\n' + \
        'auto wlan0\n' + \
        'iface wlan0 inet dhcp\n' + \
        'wpa-ssid "' + SSID + '"\n' + \
        'wpa-psk "' + password + '"\n'

    file.write(content)

print("Write successful. Rebooting now.")
time.sleep(2)
os.system('sudo reboot now')
Shawn Khoo
  • 21
  • 5