MycroftOS: Implementation of MycroftOS-WiFiSetup system.

- Start an Acces Point if no wlan is configured.
- Start a small flask webserver to configure the wifi.
This commit is contained in:
Peter Steenbergen 2019-12-29 14:03:51 +01:00
parent 34b9fdaadb
commit 34a89c5541
19 changed files with 537 additions and 8 deletions

View File

@ -4,6 +4,7 @@ menu "Mycroft A.I. Personal Assistant"
source "$BR2_EXTERNAL_MYCROFTOS_PATH/package/python-mycroft/Config.in"
source "$BR2_EXTERNAL_MYCROFTOS_PATH/package/mycroft-service/Config.in"
source "$BR2_EXTERNAL_MYCROFTOS_PATH/package/mycroft-splash/Config.in"
source "$BR2_EXTERNAL_MYCROFTOS_PATH/package/wifi-ap/Config.in"
menu "Additional drivers, libraries and/or applications"
source "$BR2_EXTERNAL_MYCROFTOS_PATH/package/alsa-plugins/Config.in"
source "$BR2_EXTERNAL_MYCROFTOS_PATH/package/fann/Config.in"

View File

@ -176,6 +176,7 @@ BR2_PACKAGE_PYTHON_COLORAMA=y
BR2_PACKAGE_PYTHON_CONFIGSHELL_FB=y
BR2_PACKAGE_PYTHON_CRYPTOGRAPHY=y
BR2_PACKAGE_PYTHON_DAEMONIZE=y
BR2_PACKAGE_PYTHON_FLASK=y
BR2_PACKAGE_PYTHON_GOBJECT=y
BR2_PACKAGE_PYTHON_INFLECTION=y
BR2_PACKAGE_PYTHON_IPADDRESS=y
@ -303,6 +304,7 @@ BR2_PACKAGE_PREPARE_SYSTEM_SERVICE=y
BR2_PACKAGE_PYTHON_MYCROFT=y
BR2_PACKAGE_MYCROFT_SERVICE=y
BR2_PACKAGE_MYCROFT_SPLASH=y
BR2_PACKAGE_WIFI_AP=y
BR2_PACKAGE_ALSA_PLUGINS=y
BR2_PACKAGE_FANN=y
BR2_PACKAGE_RESPEAKER=y

View File

@ -0,0 +1,7 @@
config BR2_PACKAGE_WIFI_AP
bool "wifi-ap"
help
Start a wifi AP when there is no wifi
configuration present.
https://www.j1nx.nl

View File

@ -0,0 +1,39 @@
################################################################################
#
# wifi-ap
#
################################################################################
WIFI_AP_VERSION = 0.1.0
WIFI_AP_SITE = $(BR2_EXTERNAL_MYCROFTOS_PATH)/package/wifi-ap
WIFI_AP_SITE_METHOD = local
WIFI_AP_LICENSE = Apache License 2.0
WIFI_AP_LICENSE_FILES = LICENSE
define WIFI_AP_INSTALL_TARGET_CMDS
mkdir -p $(TARGET_DIR)/etc/wpa_supplicant
$(INSTALL) -m 644 -D $(@D)/wpa_supplicant-ap0.conf \
$(TARGET_DIR)/etc/wpa_supplicant/wpa_supplicant-ap0.conf
mkdir -p $(TARGET_DIR)/etc/systemd/system/sys-subsystem-net-devices-ap0.device.wants
ln -fs ../../../../usr/lib/systemd/system/wpa_supplicant@.service \
$(TARGET_DIR)/etc/systemd/system/sys-subsystem-net-devices-ap0.device.wants/wpa_supplicant@ap0.service
$(INSTALL) -D -m 644 $(@D)/wifi-setup.service \
$(TARGET_DIR)/usr/lib/systemd/system/wifi-setup.service
ln -fs ../../../../usr/lib/systemd/system/wifi-setup.service \
$(TARGET_DIR)/etc/systemd/system/sys-subsystem-net-devices-ap0.device.wants/wifi-setup.service
$(INSTALL) -D -m 644 $(@D)/wireless-mode-ap.service \
$(TARGET_DIR)/usr/lib/systemd/system/wireless-mode-ap.service
mkdir -p $(TARGET_DIR)/etc/systemd/system/multi-user.target.wants
ln -fs ../../../../usr/lib/systemd/system/wireless-mode-ap.service \
$(TARGET_DIR)/etc/systemd/system/multi-user.target.wants/wireless-mode-ap.service
$(INSTALL) -D -m 644 $(@D)/wireless-mode-client.service \
$(TARGET_DIR)/usr/lib/systemd/system/wireless-mode-client.service
mkdir -p $(TARGET_DIR)/etc/systemd/system/multi-user.target.wants
ln -fs ../../../../usr/lib/systemd/system/wireless-mode-client.service \
$(TARGET_DIR)/etc/systemd/system/multi-user.target.wants/wireless-mode-client.service
endef
$(eval $(generic-package))

View File

@ -0,0 +1,18 @@
[Unit]
Description=MycroftOS-WiFiSetup webbased configurator
BindsTo=sys-subsystem-net-devices-ap0.device
After=sys-subsystem-net-devices-ap0.device
Before=network.target
Wants=network.target
# check existence of configuration file
ConditionPathExists= /etc/wpa_supplicant/wpa_supplicant-ap0.conf
ConditionPathExists=!/etc/wpa_supplicant/wpa_supplicant-wlan0.conf
[Service]
Type=idle
WorkingDirectory=/opt/mycroft/wifisetup
ExecStart=/usr/bin/python3 app.py > /dev/null 2>&1
[Install]
WantedBy=sys-subsystem-net-devices-ap0.device

View File

@ -0,0 +1,16 @@
[Unit]
Description=Wireless mode set AP
Before=wpa_supplicant@.service
Before=systemd-networkd.service
Wants=network.target
# check existence of configuration file
ConditionPathExists= /etc/wpa_supplicant/wpa_supplicant-ap0.conf
ConditionPathExists=!/etc/wpa_supplicant/wpa_supplicant-wlan0.conf
[Service]
Type=oneshot
ExecStart=/bin/ln -sf wifi.network.ap /etc/systemd/network/wifi.network
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,15 @@
[Unit]
Description=Wireless mode set client
Before=wpa_supplicant@.service
Before=systemd-networkd.service
Wants=network.target
# check existence of configuration file
ConditionPathExists=/etc/wpa_supplicant/wpa_supplicant-wlan0.conf
[Service]
Type=oneshot
ExecStart=/bin/ln -sf wifi.network.client /etc/systemd/network/wifi.network
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,8 @@
ctrl_interface=/var/run/wpa_supplicant
update_config=1
network={
ssid="MycroftOS-WiFiSetup"
mode=2
key_mgmt=NONE
}

View File

@ -0,0 +1,5 @@
[Match]
Name=ap0
[Network]
Address=172.16.127.1/28
DHCPServer=yes

View File

@ -0,0 +1 @@
SUBSYSTEM=="net", KERNEL=="wlan*", ACTION=="add", RUN+="/sbin/iw dev %k interface add ap%n type __ap"

View File

@ -0,0 +1,111 @@
from flask import Flask, render_template, request
import subprocess
import os
import time
from threading import Thread
import fileinput
app = Flask(__name__)
app.debug = True
@app.route('/')
def index():
wifi_ap_array = scan_wifi_networks()
return render_template('app.html', wifi_ap_array = wifi_ap_array)
@app.route('/manual_ssid_entry')
def manual_ssid_entry():
return render_template('manual_ssid_entry.html')
@app.route('/save_credentials', methods = ['GET', 'POST'])
def save_credentials():
ssid = request.form['ssid']
wifi_key = request.form['wifi_key']
create_wpa_supplicant(ssid, wifi_key)
# Call reboot_device() in a thread otherwise the reboot will prevent
# the response from getting to the browser
def sleep_and_reboot():
time.sleep(2)
reboot_device()
t = Thread(target=sleep_and_reboot)
t.start()
return render_template('save_credentials.html', ssid = ssid)
@app.route('/skip_wifi')
def skip_wifi():
empty_wpa_supplicant()
# Call reboot_device() in a thread otherwise the reboot will prevent
# the response from getting to the browser
def sleep_and_reboot():
time.sleep(2)
reboot_device()
t = Thread(target=sleep_and_reboot)
t.start()
return render_template('cancelled_wifi.html')
######## FUNCTIONS ##########
def scan_wifi_networks():
iwlist_raw = subprocess.Popen(['iw', 'dev', 'ap0', 'scan', 'ap-force'], stdout=subprocess.PIPE)
ap_list, err = iwlist_raw.communicate()
ap_array = []
for line in ap_list.decode('utf-8').rsplit('\n'):
if 'SSID' in line:
ap_ssid = line[7:]
if ap_ssid != '':
ap_array.append(ap_ssid)
return ap_array
def create_wpa_supplicant(ssid, wifi_key):
temp_conf_file = open('wpa_supplicant-wlan0.conf.tmp', 'w')
temp_conf_file.write('ctrl_interface=DIR=/var/run/wpa_supplicant\n')
temp_conf_file.write('update_config=1\n')
temp_conf_file.write('\n')
temp_conf_file.write('network={\n')
temp_conf_file.write(' ssid="' + ssid + '"\n')
if wifi_key == '':
temp_conf_file.write(' key_mgmt=NONE\n')
else:
temp_conf_file.write(' psk="' + wifi_key + '"\n')
temp_conf_file.write('}\n')
temp_conf_file.close
os.system('mv wpa_supplicant-wlan0.conf.tmp /etc/wpa_supplicant/wpa_supplicant-wlan0.conf')
def empty_wpa_supplicant():
temp_conf_file = open('wpa_supplicant-wlan0.conf.tmp', 'w')
temp_conf_file.write('ctrl_interface=DIR=/var/run/wpa_supplicant\n')
temp_conf_file.write('update_config=1\n')
temp_conf_file.write('\n')
temp_conf_file.write('network={\n')
temp_conf_file.write('}\n')
temp_conf_file.close
os.system('mv wpa_supplicant-wlan0.conf.tmp /etc/wpa_supplicant/wpa_supplicant-wlan0.conf')
def reboot_device():
os.system('reboot')
if __name__ == '__main__':
app.run(host = '0.0.0.0', port = '88')

View File

@ -0,0 +1,192 @@
/* General
---------------------------------------------------------*/
*, :after, :before {
box-sizing: border-box;
}
body,
html {
margin:0;
padding:0;
}
body{
background-color: #22A7F0;
margin:0;
color: #2C3E50;
}
div#mainContent{
padding:.3em;
background-color: #22A7F0;
width: 100%;
margin: 0 auto;
text-align: center;
font-size: 2.5rem;
color: #2C3E50;
}
div#mainContent h1 {
margin:0 0 .5em 0;
font-size:2rem;
}
div#mainContent ul{
list-style: none;
margin: 0;
padding: 0;
}
div#mainContent form {
margin-top:-2em;
}
div#wpaStatus{
padding: 4px;
margin: 0 0 10px 0;
width: 250px;
background-color: #928043;
text-align: left;
font-size: .3em;
}
div#wpaStatus a{
color: #fff;
}
label[for=ssid],
label[for=wifi_key]{
font-size:1.9rem;
}
/* Select
---------------------------------------------------------*/
li label {
display:inline-block;
margin: 0.5em 0;
padding: 0.5em 0;
}
li.wifiNetwork {
position: relative;
margin: 0;
}
li.wifiNetwork:after {
content: '▼';
position: absolute;
width: 25px;
color: #22A7F0;
font-weight: bold;
font-size: 1.4rem;
right: 0px;
top: 40%;
border-radius: 3px;
pointer-events: none;
z-index: 2;
}
select.wifiNetworkInputs {
position: relative;
width: 100%;
background: #111;
color: #22A7F0;
border: none;
outline: none;
font-size: 1.5rem;
padding: 1em;
margin: 0;
border-radius: 3px;
cursor: pointer;
height: auto;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.wifiNetworkInputs{
width: 100%;
padding:1em;
font-size: 1.4rem;
color: #2C3E50;
background: #22A7F0;
border:#2C3E50 solid 1px;
}
.wifiConnectButton{
width: 100%;
margin:2em 0;
padding:.5em;
background-color: #5B6984;
font-size: 2rem;
color: #fff;
}
.manualSSIDEntry{
font-size: 1rem;
color: #2C3E50;
}
div#wpaCredentialsForm{
margin: 60px 0 0 0;
}
.wpaEnabledCheckbox{
height: 46px;
width: 46px;
}
div#saveCredentialsOutput{
width: 60%;
margin: 0 0 0 20%;
text-align: center;
font-size: 1.7em;
}
.saveCredentialsSSID{
color: #2C3E50;
font-size: 2em;
}
/* Wifi Icon
---------------------------------------------------------*/
.wifiIcon {
padding: 10px;
}
.wifiIcon, .wifiIcon:before {
display: inline-block;
border: 40px double transparent;
border-top-color: currentColor;
border-radius: 50%;
}
.wifiIcon:before {
content: '';
width: 0; height: 0;
}
/* Desktop
---------------------------------------------------------*/
@media (min-width: 800px) {
div#mainContent h1 {
font-size:4.5rem;
}
div#mainContent {
max-width:768px;
}
li.wifiNetwork:after {
content: '▼';
position: absolute;
width: 50px;
color: #2C3E50;
font-weight: bold;
font-size: 2rem;
right: 0px;
bottom: 50px;
border-radius: 3px;
pointer-events: none;
z-index: 2;
}
}

View File

@ -0,0 +1,32 @@
{% extends 'layout.html' %}
{% block body %}
<div id="mainContent">
<h1>WiFi Setup</h1>
<div class="wifiIcon"></div>
<form action="{{ url_for('save_credentials') }}" method=post>
<ul>
<li><label for="ssid">Select your WiFi network</label></li>
<li class="wifiNetwork">
<select id="ssid" name="ssid", class="wifiNetworkInputs">
{% for ssid in wifi_ap_array %}
<option value='{{ ssid }}'>{{ ssid }}</option>
{% endfor %}
</select>
</li>
<li><a href="{{ url_for('manual_ssid_entry') }}" class="manualSSIDEntry">manual SSID entry -></a></li>
<li><label for="wifi_key">Your Wifi password</label></li>
<li><input type="password" name="wifi_key", class="wifiNetworkInputs"></li>
<input type="submit", class="wifiConnectButton", label="Configure">
<li><a href="{{ url_for('skip_wifi') }}" class="manualSSIDEntry">Skip WiFi Setup -></a></li>
</ul>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,8 @@
{% extends 'layout.html' %}
{% block body %}
<div id="saveCredentialsOutput">
<p>Wifi connection skipped for now</p>
<p>Your device will now reboot without wifi.</p>
</div>
{% endblock %}

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='stylesheets/app.css') }}">
<title>MycroftOS-WiFiSetup</title>
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,20 @@
{% extends 'layout.html' %}
{% block body %}
<div id="mainContent">
<h1>WiFi Setup</h1>
<div class="wifiIcon"></div>
<form action="{{ url_for('save_credentials') }}" method=post>
<ul>
<li><label for="ssid">Enter your network SSID:</label></li>
<li><input type="text" name="ssid", class="wifiNetworkInputs"></li>
<li><label for="wifi_key">Your Wifi password</label></li>
<li><input type="password" name="wifi_key", class="wifiNetworkInputs"></li>
<input type="submit", class="wifiConnectButton">
</ul>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,9 @@
{% extends 'layout.html' %}
{% block body %}
<div id="saveCredentialsOutput">
<p>Wifi connection configured for:</p>
<span class="saveCredentialsSSID">{{ ssid }}</span>
<p>Your device will now reboot and should be connected to your access point soon.</p>
</div>
{% endblock %}

View File

@ -1,25 +1,55 @@
From 66fde29524de8b91eb6b8f3827e1c82e09a0dd54 Mon Sep 17 00:00:00 2001
From 09ea96ba56e3437b16e6d7d48679f48019c1d17b Mon Sep 17 00:00:00 2001
From: Peter Steenbergen <info@j1nx.nl>
Date: Fri, 20 Dec 2019 19:13:19 +0100
Date: Fri, 27 Dec 2019 20:53:47 +0100
Subject: [PATCH 1/1] Enable systemd wifi wlan0 by default.
---
package/wpa_supplicant/wpa_supplicant.mk | 2 ++
1 file changed, 2 insertions(+)
package/wpa_supplicant/wpa_supplicant.mk | 5 ++++-
package/wpa_supplicant/wpa_supplicant@.service | 18 ++++++++++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
create mode 100644 package/wpa_supplicant/wpa_supplicant@.service
diff --git a/package/wpa_supplicant/wpa_supplicant.mk b/package/wpa_supplicant/wpa_supplicant.mk
index a518ecc217..18ccf25fe5 100644
index a518ecc217..de7301cee5 100644
--- a/package/wpa_supplicant/wpa_supplicant.mk
+++ b/package/wpa_supplicant/wpa_supplicant.mk
@@ -232,6 +232,8 @@ define WPA_SUPPLICANT_INSTALL_INIT_SYSTEMD
@@ -230,8 +230,11 @@ endef
define WPA_SUPPLICANT_INSTALL_INIT_SYSTEMD
$(INSTALL) -m 0644 -D $(@D)/$(WPA_SUPPLICANT_SUBDIR)/systemd/wpa_supplicant.service \
$(TARGET_DIR)/usr/lib/systemd/system/wpa_supplicant.service
$(INSTALL) -m 0644 -D $(@D)/$(WPA_SUPPLICANT_SUBDIR)/systemd/wpa_supplicant@.service \
- $(INSTALL) -m 0644 -D $(@D)/$(WPA_SUPPLICANT_SUBDIR)/systemd/wpa_supplicant@.service \
+ $(INSTALL) -m 0644 -D package/wpa_supplicant/wpa_supplicant@.service \
$(TARGET_DIR)/usr/lib/systemd/system/wpa_supplicant@.service
+ mkdir -p $(TARGET_DIR)/etc/systemd/system/sys-subsystem-net-devices-wlan0.device.wants
+ ln -fs ../../../../usr/lib/systemd/system/wpa_supplicant@.service \
+ $(TARGET_DIR)/etc/systemd/system/multi-user.target.wants/wpa_supplicant@wlan0.service
+ $(TARGET_DIR)/etc/systemd/system/sys-subsystem-net-devices-wlan0.device.wants/wpa_supplicant@wlan0.service
$(INSTALL) -m 0644 -D $(@D)/$(WPA_SUPPLICANT_SUBDIR)/systemd/wpa_supplicant-nl80211@.service \
$(TARGET_DIR)/usr/lib/systemd/system/wpa_supplicant-nl80211@.service
$(INSTALL) -m 0644 -D $(@D)/$(WPA_SUPPLICANT_SUBDIR)/systemd/wpa_supplicant-wired@.service \
diff --git a/package/wpa_supplicant/wpa_supplicant@.service b/package/wpa_supplicant/wpa_supplicant@.service
new file mode 100644
index 0000000000..b7d2d2d88d
--- /dev/null
+++ b/package/wpa_supplicant/wpa_supplicant@.service
@@ -0,0 +1,18 @@
+[Unit]
+Description=WPA supplicant daemon (interface-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+Before=network.target
+Wants=network.target
+
+# check existence of configuration file
+ConditionPathExists=/etc/wpa_supplicant/wpa_supplicant-%I.conf
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+ExecStart=/usr/sbin/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I
+
+[Install]
+WantedBy=sys-subsystem-net-devices-%i.device
--
2.24.0.rc1