Wifi manager (#148)

Updates to include ability to change wifi networks.
This commit is contained in:
jordanruthe 2021-05-13 20:53:58 -04:00 committed by GitHub
parent e75a10a888
commit 7e3b919c62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 808 additions and 255 deletions

View File

@ -49,6 +49,31 @@ try:
except:
pass
def get_network_interfaces():
stream = os.popen("ip addr | grep ^'[0-9]' | cut -d ' ' -f 2 | grep -o '[a-zA-Z0-9\.]*'")
return [i for i in stream.read().strip().split('\n') if not i.startswith('lo')]
def get_wireless_interfaces():
p = subprocess.Popen(["which","iwconfig"], stdout=subprocess.PIPE)
while p.poll() is None:
time.sleep(.1)
if p.poll() != 0:
return None
try:
p = subprocess.Popen(["iwconfig"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
result = p.stdout.read().decode('ascii').split('\n')
except:
logging.info("Error with running iwconfig command")
return None
interfaces = []
for line in result:
match = re.search('^(\S+)\s+.*$', line)
if match:
interfaces.append(match.group(1))
return interfaces
def get_software_version():
prog = ('git', '-C', os.path.dirname(__file__), 'describe', '--always',

View File

@ -2,82 +2,197 @@ import os, signal
import json
import logging
import re
import socket
import subprocess
import threading
import time
from contextlib import suppress
from threading import Thread
RESCAN_INTERVAL = 120
from subprocess import PIPE, Popen, STDOUT
from queue import Queue, Empty
class WifiManager(Thread):
iw_regexes = [
re.compile(r"^ESSID:\"(?P<essid>.*)\"$"),
re.compile(r"^Protocol:(?P<protocol>.+)$"),
re.compile(r"^Mode:(?P<mode>.+)$"),
re.compile(r"^Frequency:(?P<frequency>[\d.]+) (?P<frequency_units>.+) \(Channel (?P<channel>\d+)\)$"),
re.compile(r"^Encryption key:(?P<encryption>.+)$"),
re.compile(r"^Quality=(?P<signal_quality>\d+)/(?P<signal_total>\d+)\s+Signal level=(?P<signal_level_dBm>.+) d.+$"),
re.compile(r"^Signal level=(?P<signal_quality>\d+)/(?P<signal_total>\d+).*$")
]
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, GLib
RESCAN_INTERVAL = 180
KS_SOCKET_FILE = "/tmp/.KS_wpa_supplicant"
class WifiManager():
networks_in_supplicant = []
wpa = {
"wpa": re.compile(r"IE:\ WPA\ Version\ 1$"),
"wpa2": re.compile(r"IE:\ IEEE\ 802\.11i/WPA2\ Version\ 1$")
}
connected = False
_stop_loop = False
thread = None
def __init__(self, *args, **kwargs):
def __init__(self, interface, *args, **kwargs):
super().__init__(*args, **kwargs)
self.loop = None
self._poll_task = None
self._scanning = False
self._callbacks = {
"connected": [],
"connecting_status": [],
"scan_results": []
}
self._stop_loop = False
self.connected = False
self.connected_ssid = None
self.connecting_info = []
self.event = threading.Event()
self.initialized = False
self.interface = interface
self.networks = {}
self.supplicant_networks = {}
self.queue = Queue()
self.tasks = []
self.timeout = None
self.scan_time = 0
if os.path.exists(KS_SOCKET_FILE):
os.remove(KS_SOCKET_FILE)
try:
self.soc = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
self.soc.bind(KS_SOCKET_FILE)
self.soc.connect("/var/run/wpa_supplicant/%s" % interface)
except:
logging.info("Error connecting to wifi socket: %s" % interface)
return
self.wpa_thread = WpaSocket(self, self.queue, self.callback)
self.wpa_thread.start()
self.initialized = True
self.wpa_cli("ATTACH", False)
self.wpa_cli("SCAN", False)
GLib.idle_add(self.read_wpa_supplicant)
self.timeout = GLib.timeout_add_seconds(RESCAN_INTERVAL, self.rescan)
def add_callback(self, name, callback):
if name in self._callbacks and callback not in self._callbacks[name]:
self._callbacks[name].append(callback)
def add_network(self, ssid, psk):
for id in list(self.supplicant_networks):
if self.supplicant_networks[id]['ssid'] == ssid:
#Modify network
return
# TODO: Add wpa_cli error checking
network_id = self.wpa_cli("ADD_NETWORK")
commands = [
'ENABLE_NETWORK %s' % (network_id),
'SET_NETWORK %s ssid "%s"' % (network_id, ssid.replace('"','\"')),
'SET_NETWORK %s psk "%s"' % (network_id, psk.replace('"','\"'))
]
self.wpa_cli_batch(commands)
self.read_wpa_supplicant()
def run(self):
event = threading.Event()
logging.debug("Setting up wifi event loop")
while self._stop_loop == False:
try:
self.scan()
event.wait(RESCAN_INTERVAL)
except:
logging.exception("Poll wifi error")
def stop(self):
self.loop.call_soon_threadsafe(self.loop.stop)
def stop_loop(self):
self._stop_loop = True
def get_current_wifi(self, interface="wlan0"):
p = subprocess.Popen(["iwconfig",interface], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
content = p.stdout.read().decode('utf-8').split('\n')
essid = None
mac = None
for line in content:
match = re.match(r'^.*ESSID:"(.*)"$', line.strip())
if match:
essid = match.group(1)
continue
match = re.match(r'^.*Access\s+Point:\s+([0-9A-Fa-f:]+)$', line.strip())
if match:
mac = match.group(1)
id = None
for i in list(self.supplicant_networks):
if self.supplicant_networks[i]['ssid'] == ssid:
id = i
break
if essid is None or mac is None:
self.connected = False
return None
self.connected = True
return [essid, mac]
if id == None:
logging.info("Error adding network")
return False
def get_network_info(self, essid=None, mac=None):
if essid is not None and essid in self.networks:
return self.networks[essid]
if mac is not None and essid is None:
self.save_wpa_conf()
return True
def callback(self, type, msg):
if type in self._callbacks:
for cb in self._callbacks[type]:
Gdk.threads_add_idle(
GLib.PRIORITY_DEFAULT_IDLE,
cb,
msg)
def connect(self, ssid):
id = None
for netid, net in self.supplicant_networks.items():
if net['ssid'] == ssid:
id = netid
break
if id == None:
logging.info("Wifi network is not defined in wpa_supplicant")
return False
logging.info("Attempting to connect to wifi: %s" % id)
self.connecting_info = ["Attempting to connect to %s" % ssid]
self.wpa_cli("SELECT_NETWORK %s" % id)
self.save_wpa_conf()
def delete_network(self, ssid):
id = None
for i in list(self.supplicant_networks):
if self.supplicant_networks[i]['ssid'] == ssid:
id = i
break
if id == None:
logging.debug("Unable to find network in wpa_supplicant")
return
self.wpa_cli("REMOVE_NETWORK %s" % id)
for id in list(self.supplicant_networks):
if self.supplicant_networks[id]['ssid'] == ssid:
del self.supplicant_networks[id]
break
self.save_wpa_conf()
def get_connected_ssid(self):
return self.connected_ssid
def get_current_wifi(self, interface="wlan0"):
logging.info("Getting current wifi information")
status = self.wpa_cli("STATUS").split('\n')
vars = {}
for line in status:
arr = line.split('=')
vars[arr[0]] = "=".join(arr[1:])
prev_ssid = self.connected_ssid
if "ssid" in vars and "bssid" in vars:
self.connected = True
self.connected_ssid = vars['ssid']
for ssid, val in self.networks.items():
if ssid == vars['ssid']:
self.networks[ssid]['connected'] = True
else:
self.networks[ssid]['connected'] = False
if prev_ssid != self.connected_ssid:
for cb in self._callbacks['connected']:
Gdk.threads_add_idle(
GLib.PRIORITY_DEFAULT_IDLE,
cb, self.connected_ssid, prev_ssid)
return [vars['ssid'], vars['bssid']]
else:
logging.info("Resetting connected_ssid")
self.connected = False
self.connected_ssid = None
for ssid, val in self.networks.items():
self.networks[ssid]['connected'] = False
if prev_ssid != self.connected_ssid:
for cb in self._callbacks['connected']:
Gdk.threads_add_idle(
GLib.PRIORITY_DEFAULT_IDLE,
cb, self.connected_ssid, prev_ssid)
return None
def get_current_wifi_idle_add(self):
self.get_current_wifi()
return False
def get_network_info(self, ssid=None, mac=None):
if ssid is not None and ssid in self.networks:
return self.networks[ssid]
if mac is not None and ssid is None:
for net in self.networks:
if mac == net['mac']:
return net
@ -86,77 +201,272 @@ class WifiManager(Thread):
def get_networks(self):
return list(self.networks)
def get_supplicant_networks(self):
return self.supplicant_networks
def is_connected(self):
return self.connected
def parse(self, data):
aps = []
lines = data.split('\n')
for line in lines:
line = line.strip()
match = re.match(r'^Cell\s+([0-9]+)\s+-\s+Address:\s+(?P<mac>[0-9A-Fa-f:]+)$', line)
if match:
aps.append({"mac":match.group(2)})
continue
if len(aps) < 1:
continue
for w, wreg in self.wpa.items():
t = wreg.search(line)
if t is not None:
aps[-1].update({'encryption': w})
for exp in self.iw_regexes:
result = exp.search(line)
if result is not None:
if "encryption" in result.groupdict():
if result.groupdict()['encryption'] == 'on' :
aps[-1].update({'encryption': 'wep'})
else:
aps[-1].update({'encryption': 'off'})
else:
aps[-1].update(result.groupdict())
return aps
def is_initialized(self):
return self.initialized
def read_wpa_supplicant(self):
wpaconf = "/etc/wpa_supplicant/wpa_supplicant.conf"
if not os.path.exists(wpaconf):
return None
regexes = [
re.compile(r'^ssid\s*=\s*"(?P<ssid>.*)"$'),
re.compile(r'^psk\s*=\s*"(?P<psk>.*)"$')
]
networks = []
p = subprocess.Popen(["sudo","cat",wpaconf], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
contents = p.stdout.read().decode('utf-8').split('\n')
for line in contents:
if re.match(r'^network\s*=\s*{$', line.strip()):
networks.append({})
continue
if len(networks) < 1:
continue
for exp in regexes:
result = exp.search(line.strip())
if result is not None:
networks[-1].update(result.groupdict())
results = self.wpa_cli("LIST_NETWORKS").split('\n')
results.pop(0)
self.supplicant_networks = {}
self.networks_in_supplicant = []
for network in networks:
self.networks_in_supplicant.append(network)
for net in [n.split('\t') for n in results]:
self.supplicant_networks[net[0]] = {
"ssid": net[1],
"bssid": net[2],
"flags": net[3] if len(net) == 4 else ""
}
self.networks_in_supplicant.append(self.supplicant_networks[net[0]])
def remove_callback(self, name, callback):
if name in self._callbacks and callback in self._callbacks[name]:
self._callbacks[name].remove(callback)
def rescan(self):
self.wpa_cli("SCAN", False)
return True
def save_wpa_conf(self):
logging.info("Saving WPA config")
self.wpa_cli("SAVE_CONFIG")
def scan_results(self, interface='wlan0'):
new_networks = []
deleted_networks = list(self.networks)
logging.info("Trying to get scan results")
results = self.wpa_cli("SCAN_RESULTS").split('\n')
results.pop(0)
aps = []
for res in results:
match = re.match("^([a-f0-9:]+)\s+([0-9]+)\s+([\-0-9]+)\s+(\S+)\s+(.+)?", res)
if match:
net = {
"mac": match.group(1),
"channel": WifiChannels.lookup(match.group(2))[1],
"connected": False,
"configured": False,
"frequency": match.group(2),
"flags": match.group(4),
"signal_level_dBm": match.group(3),
"ssid": match.group(5)
}
if "WPA2" in net['flags']:
net['encryption'] = "WPA2"
elif "WPA" in net['flags']:
net['encryption'] = "WPA"
elif "WEP" in net['flags']:
net['encryption'] = "WEP"
else:
net['encryption'] = "off"
aps.append(net)
def scan(self, interface='wlan0'):
p = subprocess.Popen(["sudo","iwlist",interface,"scan"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
aps = self.parse(p.stdout.read().decode('utf-8'))
cur_info = self.get_current_wifi()
self.networks = {}
for ap in aps:
self.networks[ap['essid']] = ap
if cur_info is not None and cur_info[0] == ap['essid'] and cur_info[1] == ap['mac']:
self.networks[ap['essid']]['connected'] = True
for net in self.networks_in_supplicant:
if ap['essid'] == net['ssid'] and "psk" in net:
ap['psk'] = net['psk']
break
self.networks[ap['ssid']] = ap
if cur_info is not None and cur_info[0] == ap['ssid'] and cur_info[1].lower() == ap['mac'].lower():
self.networks[ap['ssid']]['connected'] = True
for net in list(self.networks):
if net in deleted_networks:
deleted_networks.remove(net)
else:
new_networks.append(net)
if len(new_networks) > 0 or len(deleted_networks) > 0:
for cb in self._callbacks['scan_results']:
Gdk.threads_add_idle(
GLib.PRIORITY_DEFAULT_IDLE,
cb, new_networks, deleted_networks)
def wpa_cli(self, command, wait=True):
if wait == False:
self.wpa_thread.skip_command()
self.soc.send(command.encode())
if wait == True:
resp = self.queue.get()
return resp
def wpa_cli_batch(self, commands):
for cmd in commands:
self.wpa_cli(cmd)
class WpaSocket(Thread):
def __init__ (self, wm, queue, callback):
super().__init__()
self.queue = queue
self.callback = callback
self.soc = wm.soc
self._stop_loop = False
self.skip_commands = 0
self.wm = wm
def run(self):
event = threading.Event()
logging.debug("Setting up wifi event loop")
while self._stop_loop == False:
try:
msg = self.soc.recv(4096).decode().strip()
except:
# TODO: Socket error
continue
if msg.startswith("<"):
if "CTRL-EVENT-SCAN-RESULTS" in msg:
logging.info("Adding scan_results to callbacks")
Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, self.wm.scan_results)
elif "CTRL-EVENT-DISCONNECTED" in msg:
self.callback("connecting_status", msg)
match = re.match('<3>CTRL-EVENT-DISCONNECTED bssid=(\S+) reason=3 locally_generated=1', msg)
if match:
for net in self.wm.networks:
if self.wm.networks[net]['mac'] == match.group(1):
self.wm.networks[net]['connected'] = False
break
elif "Trying to associate" in msg:
self.callback("connecting_status", msg)
elif "CTRL-EVENT-REGDOM-CHANGE" in msg:
self.callback("connecting_status", msg)
elif "CTRL-EVENT-CONNECTED" in msg:
Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, self.wm.get_current_wifi_idle_add)
self.callback("connecting_status", msg)
else:
if self.skip_commands > 0:
self.skip_commands = self.skip_commands - 1
else:
self.queue.put(msg)
logging.info("Wifi event loop ended")
def skip_command(self):
self.skip_commands = self.skip_commands + 1
def stop(self):
self._stop_loop = True
class WifiChannels:
@staticmethod
def lookup(freq):
if freq == "2412":
return ("2.4","1")
if freq == "2417":
return ("2.4","2")
if freq == "2422":
return ("2.4","3")
if freq == "2427":
return ("2.4","4")
if freq == "2432":
return ("2.4","5")
if freq == "2437":
return ("2.4","6")
if freq == "2442":
return ("2.4","7")
if freq == "2447":
return ("2.4","8")
if freq == "2452":
return ("2.4","9")
if freq == "2457":
return ("2.4","10")
if freq == "2462":
return ("2.4","11")
if freq == "2467":
return ("2.4","12")
if freq == "2472":
return ("2.4","13")
if freq == "2484":
return ("2.4","14")
if freq == "5035":
return ("5","7")
if freq == "5040":
return ("5","8")
if freq == "5045":
return ("5","9")
if freq == "5055":
return ("5","11")
if freq == "5060":
return ("5","12")
if freq == "5080":
return ("5","16")
if freq == "5170":
return ("5","34")
if freq == "5180":
return ("5","36")
if freq == "5190":
return ("5","38")
if freq == "5200":
return ("5","40")
if freq == "5210":
return ("5","42")
if freq == "5220":
return ("5","44")
if freq == "5230":
return ("5","46")
if freq == "5240":
return ("5","48")
if freq == "5260":
return ("5","52")
if freq == "5280":
return ("5","56")
if freq == "5300":
return ("5","60")
if freq == "5320":
return ("5","64")
if freq == "5500":
return ("5","100")
if freq == "5520":
return ("5","104")
if freq == "5540":
return ("5","108")
if freq == "5560":
return ("5","112")
if freq == "5580":
return ("5","116")
if freq == "5600":
return ("5","120")
if freq == "5620":
return ("5","124")
if freq == "5640":
return ("5","128")
if freq == "5660":
return ("5","132")
if freq == "5680":
return ("5","136")
if freq == "5700":
return ("5","140")
if freq == "5720":
return ("5","144")
if freq == "5745":
return ("5","149")
if freq == "5765":
return ("5","153")
if freq == "5785":
return ("5","157")
if freq == "5805":
return ("5","161")
if freq == "5825":
return ("5","165")
if freq == "4915":
return ("5","183")
if freq == "4920":
return ("5","184")
if freq == "4925":
return ("5","185")
if freq == "4935":
return ("5","187")
if freq == "4940":
return ("5","188")
if freq == "4945":
return ("5","189")
if freq == "4960":
return ("5","192")
if freq == "4980":
return ("5","196")
return None;

View File

@ -1,5 +1,7 @@
import gi
import json
import logging
import netifaces
import os
import re
@ -14,7 +16,6 @@ def create_panel(*args):
class NetworkPanel(ScreenPanel):
networks = {}
network_list = []
interface = "wlan0"
def initialize(self, menu):
_ = self.lang.gettext
@ -25,8 +26,20 @@ class NetworkPanel(ScreenPanel):
stream = os.popen('hostname -A')
hostname = stream.read()
# Get IP Address
stream = os.popen('hostname -I')
ip = stream.read()
gws = netifaces.gateways()
if "default" in gws and netifaces.AF_INET in gws["default"]:
self.interface = gws["default"][netifaces.AF_INET][1]
else:
ints = netifaces.interfaces()
if 'lo' in ints:
ints.pop('lo')
self.interfaces = ints[0]
res = netifaces.ifaddresses(self.interface)
if netifaces.AF_INET in res and len(res[netifaces.AF_INET]) > 0:
ip = res[netifaces.AF_INET][0]['addr']
else:
ip = "0.0.0.0"
self.labels['networks'] = {}
@ -48,41 +61,65 @@ class NetworkPanel(ScreenPanel):
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
box.set_vexpand(True)
box.pack_start(sbox, False, False, 0)
box.pack_start(scroll, True, True, 0)
self.labels['networklist'] = Gtk.Grid()
self.files = {}
GLib.idle_add(self.load_networks)
if self._screen.wifi != None and self._screen.wifi.is_initialized():
box.pack_start(sbox, False, False, 0)
box.pack_start(scroll, True, True, 0)
scroll.add(self.labels['networklist'])
GLib.idle_add(self.load_networks)
scroll.add(self.labels['networklist'])
#self.labels['networkinfo'] = Gtk.Label(
# _("Network Info") + "\n\n%s%s" % (hostname, ip)
#)
#self.labels['networkinfo'].get_style_context().add_class('temperature_entry')
#grid.attach(self.labels['networkinfo'], 1, 0, 1, 1)
self._screen.wifi.add_callback("connected", self.connected_callback)
self._screen.wifi.add_callback("scan_results", self.scan_callback)
self.timeout = GLib.timeout_add_seconds(5, self.update_all_networks)
else:
self.labels['networkinfo'] = Gtk.Label("")
self.labels['networkinfo'].get_style_context().add_class('temperature_entry')
box.pack_start(self.labels['networkinfo'], False, False, 0)
self.update_single_network_info()
self.timeout = GLib.timeout_add_seconds(5, self.update_single_network_info)
self.content.add(box)
self.labels['main_box'] = box
def load_networks(self):
networks = self._screen.wifi.get_networks()
conn_ssid = self._screen.wifi.get_connected_ssid()
if conn_ssid in networks:
networks.remove(conn_ssid)
self.add_network(conn_ssid, False)
for net in networks:
self.add_network(net, False)
self.update_all_networks()
self.content.show_all()
def add_network(self, essid, show=True):
def add_network(self, ssid, show=True):
_ = self.lang.gettext
netinfo = self._screen.wifi.get_network_info(essid)
if ssid == None:
return
ssid = ssid.strip()
if ssid in list(self.networks):
logging.info("SSID already listed")
return
netinfo = self._screen.wifi.get_network_info(ssid)
if netinfo == None:
logging.debug("Couldn't get netinfo")
return
# For now, only add connected network
if "connected" not in netinfo:
return
configured_networks = self._screen.wifi.get_supplicant_networks()
network_id = -1
for net in list(configured_networks):
if configured_networks[net]['ssid'] == ssid:
network_id = net
frame = Gtk.Frame()
frame.set_property("shadow-type",Gtk.ShadowType.NONE)
@ -90,42 +127,15 @@ class NetworkPanel(ScreenPanel):
name = Gtk.Label()
name.set_markup("<big><b>%s</b></big>" % (essid))
name.set_markup("<big><b>%s</b></big>" % (ssid))
name.set_hexpand(True)
name.set_halign(Gtk.Align.START)
name.set_line_wrap(True)
name.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR)
stream = os.popen('ip add show dev %s' % self.interface)
content = stream.read()
ipv4_re = re.compile(r'inet ([0-9\.]+)/[0-9]+', re.MULTILINE)
ipv6_re = re.compile(r'inet6 ([a-fA-F0-9:\.]+)/[0-9+]', re.MULTILINE)
match = ipv4_re.search(content)
ipv4 = ""
if match:
ipv4 = "<b>%s:</b> %s " % (_("IPv4"), match.group(1))
match = ipv6_re.search(content)
ipv6 = ""
if match:
ipv6 = "<b>%s:</b> %s " % (_("IPv6"), match.group(1))
stream = os.popen('hostname -f')
hostname = stream.read().strip()
connected = ""
if "connected" in netinfo:
connected = "<b>%s</b>\n<b>%s:</b> %s\n%s%s\n" % (_("Connected"),_("Hostname"),hostname, ipv4, ipv6)
elif "psk" in netinfo:
connected = "Password saved."
freq = "2.4 GHz" if netinfo['frequency'][0:1] == "2" else "5 Ghz"
info = Gtk.Label()
info.set_markup("%s%s <small>%s %s %s %s%s</small>" % ( connected,
"" if netinfo['encryption'] == "off" else netinfo['encryption'].upper(),
freq, _("Channel"), netinfo['channel'], netinfo['signal_level_dBm'], _("dBm")
))
info.set_halign(Gtk.Align.START)
#info.set_markup(self.get_file_info_str(essid))
#info.set_markup(self.get_file_info_str(ssid))
labels = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
labels.add(name)
labels.add(info)
@ -133,37 +143,270 @@ class NetworkPanel(ScreenPanel):
labels.set_valign(Gtk.Align.CENTER)
labels.set_halign(Gtk.Align.START)
actions = self._gtk.ButtonImage("print",None,"color3")
#actions.connect("clicked", self.confirm_print, essid)
actions.set_hexpand(False)
actions.set_halign(Gtk.Align.END)
connect = self._gtk.ButtonImage("load",None,"color3")
connect.connect("clicked", self.connect_network, ssid)
connect.set_hexpand(False)
connect.set_halign(Gtk.Align.END)
delete = self._gtk.ButtonImage("delete","","color3")
delete.connect("clicked", self.remove_wifi_network, ssid)
delete.set_size_request(60,0)
delete.set_hexpand(False)
delete.set_halign(Gtk.Align.END)
network = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
network.set_hexpand(True)
network.set_vexpand(False)
network.add(labels)
if not "connected" in netinfo:
network.add(actions)
self.networks[essid] = frame
buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
if network_id != -1:
buttons.pack_end(delete, False, False, 0)
if netinfo['connected'] == False:
buttons.pack_end(connect, False, False, 0)
network.add(buttons)
self.networks[ssid] = frame
frame.add(network)
reverse = False
nets = sorted(self.networks, reverse=reverse)
pos = nets.index(essid)
if "connected" in netinfo:
pos = 0
elif self._screen.wifi.is_connected():
pos += 1
self.labels['networks'][essid] = {
pos = 0
if netinfo['connected'] == True:
pos = 0
else:
connected_ssid = self._screen.wifi.get_connected_ssid()
nets = list(self.networks)
if connected_ssid != None:
if connected_ssid in nets:
nets.remove(connected_ssid)
nets = sorted(nets, reverse=reverse)
pos = nets.index(ssid)
if connected_ssid != None:
pos += 1
self.labels['networks'][ssid] = {
"connect": connect,
"delete": delete,
"info": info,
"name": name,
"row": network
}
self.labels['networklist'].insert_row(pos)
self.labels['networklist'].attach(self.networks[essid], 0, pos, 1, 1)
self.labels['networklist'].attach(self.networks[ssid], 0, pos, 1, 1)
if show == True:
self.labels['networklist'].show_all()
self.labels['networklist'].show()
def add_new_network(self, widget, ssid, connect=False):
networks = self._screen.wifi.get_networks()
psk = self.labels['network_psk'].get_text()
result = self._screen.wifi.add_network(ssid, psk)
self.close_add_network(widget, ssid)
if connect == True:
if result == True:
self.connect_network(widget, ssid, False)
else:
self._screen.show_popup_message("Error adding network %s" % ssid)
def check_missing_networks(self):
networks = self._screen.wifi.get_networks()
for net in list(self.networks):
if net in networks:
networks.remove(net)
for net in networks:
self.add_network(net)
self.labels['networklist'].show_all()
def close_add_network(self, widget, ssid):
for child in self.content.get_children():
self.content.remove(child)
self.content.add(self.labels['main_box'])
self.content.show()
def close_dialog(self, widget, response_id):
widget.destroy()
def connected_callback(self, ssid, prev_ssid):
logging.info("Now connected to a new network")
if ssid != None:
self.remove_network(ssid)
if prev_ssid != None:
self.remove_network(prev_ssid)
self.check_missing_networks()
def connect_network(self, widget, ssid, showadd=True):
_ = self.lang.gettext
snets = self._screen.wifi.get_supplicant_networks()
isdef = False
for id, net in snets.items():
if net['ssid'] == ssid:
isdef = True
break
if isdef == False:
if showadd == True:
self.show_add_network(widget, ssid)
return
self.prev_network = self._screen.wifi.get_connected_ssid()
buttons = [
{"name": _("Close"), "response": Gtk.ResponseType.CANCEL}
]
scroll = Gtk.ScrolledWindow()
scroll.set_property("overlay-scrolling", False)
scroll.set_hexpand(True)
scroll.set_vexpand(True)
scroll.set_size_request(800,400)
self.labels['connecting_info'] = Gtk.Label(_("Starting WiFi Re-association"))
self.labels['connecting_info'].set_halign(Gtk.Align.START)
self.labels['connecting_info'].set_valign(Gtk.Align.START)
scroll.add(self.labels['connecting_info'])
dialog = self._gtk.Dialog(self._screen, buttons, scroll, self.close_dialog)
self._screen.show_all()
if ssid in self.networks:
self.remove_network(ssid)
if self.prev_network in self.networks:
self.remove_network(self.prev_network)
#GLib.timeout_add(500, self.add_network, self.prev_network)
self._screen.wifi.add_callback("connecting_status", self.connecting_status_callback)
self._screen.wifi.connect(ssid)
def connecting_status_callback(self, msg):
self.labels['connecting_info'].set_text(self.labels['connecting_info'].get_text() + "\n" + msg)
self.labels['connecting_info'].show_all()
def remove_network(self, ssid, show=True):
if ssid not in self.networks:
return
i = 0
while self.labels['networklist'].get_child_at(0, i) != None:
if self.networks[ssid] == self.labels['networklist'].get_child_at(0, i):
self.labels['networklist'].remove_row(i)
self.labels['networklist'].show()
del self.networks[ssid]
del self.labels['networks'][ssid]
return
i = i+1
return
def remove_network_wid(self, widget, ssid):
self.remove_network(ssid)
def remove_wifi_network(self, widget, ssid):
self._screen.wifi.delete_network(ssid)
self.remove_network(ssid)
self.check_missing_networks()
def scan_callback(self, new_networks, old_networks):
for net in old_networks:
self.remove_network(net, False)
for net in new_networks:
self.add_network(net, False)
self.content.show_all()
def show_add_network(self, widget, ssid):
_ = self.lang.gettext
for child in self.content.get_children():
self.content.remove(child)
if "add_network" in self.labels:
del self.labels['add_network']
self.labels['add_network'] = Gtk.VBox()
self.labels['add_network'].set_valign(Gtk.Align.START)
box = Gtk.Box(spacing=5)
box.set_size_request(self._gtk.get_content_width(), self._gtk.get_content_height() -
self._screen.keyboard_height - 20)
box.set_hexpand(True)
box.set_vexpand(False)
self.labels['add_network'].add(box)
l = self._gtk.Label("%s %s:" % (_("PSK for"), ssid))
l.set_hexpand(False)
entry = Gtk.Entry()
entry.set_hexpand(True)
save = self._gtk.ButtonImage("sd",_("Save"),"color3")
save.set_hexpand(False)
save.connect("clicked", self.add_new_network, ssid, True)
self.labels['network_psk'] = entry
box.pack_start(l, False, False, 5)
box.pack_start(entry, True, True, 5)
box.pack_start(save, False, False, 5)
self.show_create = True
self.labels['network_psk'].set_text('')
self.content.add(self.labels['add_network'])
self.content.show()
self._screen.show_keyboard()
self.labels['network_psk'].grab_focus_without_selecting()
def update_all_networks(self):
for network in list(self.networks):
self.update_network_info(network)
return True
def update_network_info(self, ssid):
_ = self.lang.gettext
if ssid not in self.networks or ssid not in self.labels['networks']:
return
netinfo = self._screen.wifi.get_network_info(ssid)
if netinfo == None:
logging.debug("Couldn't get netinfo for update")
return
connected = ""
if netinfo['connected'] == True:
stream = os.popen('hostname -f')
hostname = stream.read().strip()
ifadd = netifaces.ifaddresses(self.interface)
ipv4 = ""
ipv6 = ""
if netifaces.AF_INET in ifadd and len(ifadd[netifaces.AF_INET]) > 0:
ipv4 = "<b>%s:</b> %s " % (_("IPv4"), ifadd[netifaces.AF_INET][0]['addr'])
if netifaces.AF_INET6 in ifadd and len(ifadd[netifaces.AF_INET6]) > 0:
ipv6 = ipv6 = "<b>%s:</b> %s " % (_("IPv6"), ifadd[netifaces.AF_INET6][0]['addr'].split('%')[0])
connected = "<b>%s</b>\n<b>%s:</b> %s\n%s%s\n" % (_("Connected"),_("Hostname"),hostname, ipv4, ipv6)
elif "psk" in netinfo:
connected = "Password saved."
freq = "2.4 GHz" if netinfo['frequency'][0:1] == "2" else "5 Ghz"
self.labels['networks'][ssid]['info'].set_markup("%s%s <small>%s %s %s %s%s</small>" % ( connected,
"" if netinfo['encryption'] == "off" else netinfo['encryption'].upper(),
freq, _("Channel"), netinfo['channel'], netinfo['signal_level_dBm'], _("dBm")
))
self.labels['networks'][ssid]['info'].show_all()
def update_single_network_info(self):
_ = self.lang.gettext
stream = os.popen('hostname -f')
hostname = stream.read().strip()
ifadd = netifaces.ifaddresses(self.interface)
ipv4 = ""
ipv6 = ""
if netifaces.AF_INET in ifadd and len(ifadd[netifaces.AF_INET]) > 0:
ipv4 = "<b>%s:</b> %s " % (_("IPv4"), ifadd[netifaces.AF_INET][0]['addr'])
if netifaces.AF_INET6 in ifadd and len(ifadd[netifaces.AF_INET6]) > 0:
ipv6 = ipv6 = "<b>%s:</b> %s " % (_("IPv6"), ifadd[netifaces.AF_INET6][0]['addr'].split('%')[0])
connected = "<b>%s</b>\n\n<small><b>%s</b></small>\n<b>%s:</b> %s\n%s\n%s\n" % (self.interface, _("Connected"),_("Hostname"),
hostname, ipv4, ipv6)
self.labels['networkinfo'].set_markup(connected)
self.labels['networkinfo'].show_all()

View File

@ -7,6 +7,7 @@ import time
import threading
import json
import netifaces
import requests
import websocket
import importlib
@ -88,8 +89,12 @@ class KlipperScreen(Gtk.Window):
self.lang = gettext.translation('KlipperScreen', localedir='ks_includes/locales', fallback=True)
self._config = KlipperScreenConfig(configfile, self.lang, self)
self.wifi = WifiManager()
self.wifi.start()
self.network_interfaces = netifaces.interfaces()
self.wireless_interfaces = [int for int in self.network_interfaces if int.startswith('w')]
self.wifi = None
if len(self.wireless_interfaces) > 0:
logging.info("Found wireless interfaces: %s" % self.wireless_interfaces)
self.wifi = WifiManager(self.wireless_interfaces[0])
logging.debug("OS Language: %s" % os.getenv('LANG'))

View File

@ -1,6 +1,7 @@
humanize==3.5.0
jinja2==2.11.3
matplotlib==3.4.1
netifaces==0.10.9
requests==2.25.1
vext==0.7.6
websocket-client==0.59.0

View File

@ -0,0 +1,8 @@
<svg id="Capa_1" enable-background="new 0 0 512 512" height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg">
<g style="fill:#ffffff;">
<path d="m256 441.142c8.284 0 15-6.716 15-15v-201.409c0-8.284-6.716-15-15-15s-15 6.716-15 15v201.409c0 8.284 6.716 15 15 15z"/>
<path d="m173.412 427.552c.78 8.263 8.115 14.303 16.344 13.523 8.248-.779 14.302-8.096 13.523-16.344l-19.018-201.409c-.779-8.247-8.083-14.303-16.344-13.523-8.248.779-14.302 8.096-13.523 16.344z"/>
<path d="m322.244 441.076c8.238.779 15.564-5.269 16.344-13.523l19.018-201.409c.779-8.248-5.276-15.565-13.523-16.344-8.26-.784-15.565 5.276-16.344 13.523l-19.018 201.409c-.779 8.247 5.276 15.565 13.523 16.344z"/>
<path d="m57.646 168.875h8.967l43.448 330.083c.982 7.463 7.344 13.042 14.872 13.042h262.135c7.528 0 13.889-5.579 14.872-13.042l43.448-330.083h8.967c8.284 0 15-6.716 15-15v-65.629c0-8.284-6.716-15-15-15h-128.357v-5.911c0-37.128-30.207-67.335-67.335-67.335h-5.325c-37.128 0-67.335 30.207-67.335 67.335v5.911h-128.357c-8.284 0-15 6.716-15 15v65.629c0 8.284 6.715 15 15 15zm316.267 313.125h-235.826l-41.215-313.125h318.257zm-157.911-414.665c0-20.586 16.749-37.335 37.335-37.335h5.325c20.586 0 37.335 16.749 37.335 37.335v5.911h-79.995zm-143.356 35.911h366.709v35.629c-3.207 0-362.709 0-366.709 0z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,71 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg10"
sodipodi:docname="settings.svg"
inkscape:version="1.0.2 (e86c870879, 2021-01-15, custom)">
<metadata
id="metadata16">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>folder</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs14" />
<sodipodi:namedview
pagecolor="#bfbfbf"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1600"
inkscape:window-height="837"
id="namedview12"
showgrid="false"
inkscape:pagecheckerboard="false"
inkscape:zoom="5.4137931"
inkscape:cx="22.518245"
inkscape:cy="36.93885"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg10"
inkscape:snap-bbox="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:document-rotation="0" />
<!-- Generator: Sketch 52.5 (67469) - http://www.bohemiancoding.com/sketch -->
<title
id="title2">folder</title>
<desc
id="desc4">Created with Sketch.</desc>
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 43.839401,23.937493 3.706641,-2.524183 0.259084,-0.134983 5.3744,-0.294648 1.48338,3.511368 -3.957867,3.647777 -0.27739,0.09165 -4.393702,0.897813 0.03883,5.53838 4.405864,0.836122 0.278643,0.08775 4.008616,3.591932 -1.434,3.53181 -5.378001,-0.21926 -0.260948,-0.131335 -3.74167,-2.471971 -3.888769,3.943682 2.524183,3.706641 0.134983,0.259084 0.294648,5.3744 -3.511369,1.48338 -3.647776,-3.957867 -0.09165,-0.27739 -0.897814,-4.393702 -5.538379,0.03883 -0.836123,4.405864 -0.08775,0.278643 -3.591932,4.008616 -3.53181,-1.434 0.21926,-5.378001 0.131334,-0.260948 2.471972,-3.74167 -3.943682,-3.888769 -3.706642,2.524183 -0.259083,0.134983 -5.3744,0.294648 -1.4833806,-3.511368 3.9578676,-3.647777 0.277389,-0.09165 4.393703,-0.897814 -0.03882,-5.538379 -4.405864,-0.836123 -0.278644,-0.08775 -4.0086153,-3.591932 1.4339993,-3.53181 5.378001,0.21926 0.260949,0.131334 3.74167,2.471972 3.888769,-3.943682 -2.524183,-3.706642 -0.134983,-0.259083 -0.294648,-5.3744 3.511368,-1.4833806 3.647777,3.9578676 0.09165,0.277389 0.897813,4.393703 5.53838,-0.03882 0.836122,-4.405864 0.08775,-0.278643 3.591931,-4.0086162 3.531811,1.4339992 -0.21926,5.378002 -0.131335,0.260948 -2.471971,3.74167 z"
id="path4605" />
<circle
r="7.4212298"
style="display:inline;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5114-6"
cx="32"
cy="32" />
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 478.703 478.703" style="stroke: #ffffff; fill: #ffffff; enable-background:new 0 0 478.703 478.703;" xml:space="preserve">
<g>
<g>
<path style="stroke: #ffffff; fill: #ffffff;" d="M454.2,189.101l-33.6-5.7c-3.5-11.3-8-22.2-13.5-32.6l19.8-27.7c8.4-11.8,7.1-27.9-3.2-38.1l-29.8-29.8
c-5.6-5.6-13-8.7-20.9-8.7c-6.2,0-12.1,1.9-17.1,5.5l-27.8,19.8c-10.8-5.7-22.1-10.4-33.8-13.9l-5.6-33.2
c-2.4-14.3-14.7-24.7-29.2-24.7h-42.1c-14.5,0-26.8,10.4-29.2,24.7l-5.8,34c-11.2,3.5-22.1,8.1-32.5,13.7l-27.5-19.8
c-5-3.6-11-5.5-17.2-5.5c-7.9,0-15.4,3.1-20.9,8.7l-29.9,29.8c-10.2,10.2-11.6,26.3-3.2,38.1l20,28.1
c-5.5,10.5-9.9,21.4-13.3,32.7l-33.2,5.6c-14.3,2.4-24.7,14.7-24.7,29.2v42.1c0,14.5,10.4,26.8,24.7,29.2l34,5.8
c3.5,11.2,8.1,22.1,13.7,32.5l-19.7,27.4c-8.4,11.8-7.1,27.9,3.2,38.1l29.8,29.8c5.6,5.6,13,8.7,20.9,8.7c6.2,0,12.1-1.9,17.1-5.5
l28.1-20c10.1,5.3,20.7,9.6,31.6,13l5.6,33.6c2.4,14.3,14.7,24.7,29.2,24.7h42.2c14.5,0,26.8-10.4,29.2-24.7l5.7-33.6
c11.3-3.5,22.2-8,32.6-13.5l27.7,19.8c5,3.6,11,5.5,17.2,5.5l0,0c7.9,0,15.3-3.1,20.9-8.7l29.8-29.8c10.2-10.2,11.6-26.3,3.2-38.1
l-19.8-27.8c5.5-10.5,10.1-21.4,13.5-32.6l33.6-5.6c14.3-2.4,24.7-14.7,24.7-29.2v-42.1
C478.9,203.801,468.5,191.501,454.2,189.101z M451.9,260.401c0,1.3-0.9,2.4-2.2,2.6l-42,7c-5.3,0.9-9.5,4.8-10.8,9.9
c-3.8,14.7-9.6,28.8-17.4,41.9c-2.7,4.6-2.5,10.3,0.6,14.7l24.7,34.8c0.7,1,0.6,2.5-0.3,3.4l-29.8,29.8c-0.7,0.7-1.4,0.8-1.9,0.8
c-0.6,0-1.1-0.2-1.5-0.5l-34.7-24.7c-4.3-3.1-10.1-3.3-14.7-0.6c-13.1,7.8-27.2,13.6-41.9,17.4c-5.2,1.3-9.1,5.6-9.9,10.8l-7.1,42
c-0.2,1.3-1.3,2.2-2.6,2.2h-42.1c-1.3,0-2.4-0.9-2.6-2.2l-7-42c-0.9-5.3-4.8-9.5-9.9-10.8c-14.3-3.7-28.1-9.4-41-16.8
c-2.1-1.2-4.5-1.8-6.8-1.8c-2.7,0-5.5,0.8-7.8,2.5l-35,24.9c-0.5,0.3-1,0.5-1.5,0.5c-0.4,0-1.2-0.1-1.9-0.8l-29.8-29.8
c-0.9-0.9-1-2.3-0.3-3.4l24.6-34.5c3.1-4.4,3.3-10.2,0.6-14.8c-7.8-13-13.8-27.1-17.6-41.8c-1.4-5.1-5.6-9-10.8-9.9l-42.3-7.2
c-1.3-0.2-2.2-1.3-2.2-2.6v-42.1c0-1.3,0.9-2.4,2.2-2.6l41.7-7c5.3-0.9,9.6-4.8,10.9-10c3.7-14.7,9.4-28.9,17.1-42
c2.7-4.6,2.4-10.3-0.7-14.6l-24.9-35c-0.7-1-0.6-2.5,0.3-3.4l29.8-29.8c0.7-0.7,1.4-0.8,1.9-0.8c0.6,0,1.1,0.2,1.5,0.5l34.5,24.6
c4.4,3.1,10.2,3.3,14.8,0.6c13-7.8,27.1-13.8,41.8-17.6c5.1-1.4,9-5.6,9.9-10.8l7.2-42.3c0.2-1.3,1.3-2.2,2.6-2.2h42.1
c1.3,0,2.4,0.9,2.6,2.2l7,41.7c0.9,5.3,4.8,9.6,10,10.9c15.1,3.8,29.5,9.7,42.9,17.6c4.6,2.7,10.3,2.5,14.7-0.6l34.5-24.8
c0.5-0.3,1-0.5,1.5-0.5c0.4,0,1.2,0.1,1.9,0.8l29.8,29.8c0.9,0.9,1,2.3,0.3,3.4l-24.7,34.7c-3.1,4.3-3.3,10.1-0.6,14.7
c7.8,13.1,13.6,27.2,17.4,41.9c1.3,5.2,5.6,9.1,10.8,9.9l42,7.1c1.3,0.2,2.2,1.3,2.2,2.6v42.1H451.9z"/>
<path style="stroke: #ffffff" d="M239.4,136.001c-57,0-103.3,46.3-103.3,103.3s46.3,103.3,103.3,103.3s103.3-46.3,103.3-103.3S296.4,136.001,239.4,136.001
z M239.4,315.601c-42.1,0-76.3-34.2-76.3-76.3s34.2-76.3,76.3-76.3s76.3,34.2,76.3,76.3S281.5,315.601,239.4,315.601z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB