Wifi manager (#148)
Updates to include ability to change wifi networks.
This commit is contained in:
parent
e75a10a888
commit
7e3b919c62
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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'))
|
||||
|
||||
|
@ -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
|
||||
|
8
styles/z-bolt/images/delete.svg
Normal file
8
styles/z-bolt/images/delete.svg
Normal 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 |
@ -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 |
Loading…
x
Reference in New Issue
Block a user