Skip to content

Commit 637377c

Browse files
committed
Add endpoints for setting hotspot SSID and Password
1 parent da1d3ea commit 637377c

File tree

8 files changed

+132
-34
lines changed

8 files changed

+132
-34
lines changed

Dockerfile.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ ENV FLASK_ENV=production
2626
ENV PATH=/root/.local/bin:$PATH
2727

2828
# Set working directory
29-
WORKDIR /usr/src/app
29+
WORKDIR /app
3030

3131
# Install dependencies
3232
RUN apk add --no-cache \

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,46 @@ Check whether the API is available. Accessing this path will not log anything in
189189
}
190190
```
191191

192+
### http://your-device:9090/v1/set_hotspot_password
193+
194+
Allows setting the hotspot password. Using this endpoint will store the passed string in a file and will override the environment variable password. Ensure the `./db` folder is mounted as a volume for this change to be persistent.
195+
196+
#### POST
197+
198+
```
199+
{
200+
"password": "new-password" // Minimum of 8 characters
201+
}
202+
```
203+
204+
#### Response status 200
205+
206+
```
207+
{
208+
"message": "ok"
209+
}
210+
```
211+
212+
### http://your-device:9090/v1/set_hotspot_ssid
213+
214+
Allows setting the hotspot SSID. Using this endpoint will store the passed string in a file and will override any environment variable ssid. Ensure the `./db` folder is mounted as a volume for this change to be persistent.
215+
216+
#### POST
217+
218+
```
219+
{
220+
"ssid": "new SSID"
221+
}
222+
```
223+
224+
#### Response status 200
225+
226+
```
227+
{
228+
"message": "ok"
229+
}
230+
```
231+
192232
### http://your-device:9090/v1/set_interface
193233

194234
By default the Wi-Fi network interface is auto-detected. If you need to specify a network interface, you can do so using this endpoint.

docker-compose.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@ services:
2929
dockerfile: Dockerfile.template
3030
network_mode: "host"
3131
restart: always
32+
volumes:
33+
- "py_wifi_connect_db:/app/db" # Optional if not setting the hotspot ssid and password via the API
3234
labels:
3335
io.balena.features.dbus: "1"
3436
cap_add:
3537
- NET_ADMIN
3638
privileged: true # This can be removed if you do not need the LED indicator.
39+
40+
volumes:
41+
py_wifi_connect_db:

src/config.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
import os
2+
from dotenv import dotenv_values
3+
4+
# Check db directory exists
5+
if not os.path.exists("db"):
6+
os.makedirs("db")
7+
8+
# Import database file
9+
env_file = dotenv_values("db/.db")
210

311
# Set default Wi-Fi SSID.
4-
if "PWC_HOTSPOT_SSID" in os.environ:
12+
if "PWC_HOTSPOT_SSID" in env_file:
13+
hotspot_ssid = env_file["PWC_HOTSPOT_SSID"]
14+
elif "PWC_HOTSPOT_SSID" in os.environ:
515
hotspot_ssid = os.environ["PWC_HOTSPOT_SSID"]
616
else:
717
hotspot_ssid = "Py Wi-Fi Connect"
818

919
# Set default hotspot password.
10-
if "PWC_HOTSPOT_PASSWORD" in os.environ:
20+
if "PWC_HOTSPOT_PASSWORD" in env_file:
21+
hotspot_password = env_file["PWC_HOTSPOT_PASSWORD"]
22+
elif "PWC_HOTSPOT_PASSWORD" in os.environ:
1123
hotspot_password = os.environ["PWC_HOTSPOT_PASSWORD"]
1224
else:
1325
hotspot_password = None

src/requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
python-dotenv
12
Flask-Cors
23
Flask-RESTful
34
python-networkmanager==2.1

src/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ jinja2==3.0.3
2424
# via flask
2525
markupsafe==2.1.0
2626
# via jinja2
27+
python-dotenv==0.19.2
28+
# via -r requirements.in
2729
python-networkmanager==2.1
2830
# via -r requirements.in
2931
pytz==2021.3

src/resources/wifi_routes.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import config
2+
import dotenv
23
import threading
34
from common.errors import logger
45
from common.wifi import check_internet_status
56
from common.wifi import check_wifi_status
67
from common.wifi import connect
78
from common.wifi import forget
89
from common.wifi import list_access_points
10+
from dotenv import dotenv_values
911
from flask import request
1012
from flask_restful import Resource
1113

@@ -78,6 +80,39 @@ def get(self):
7880
return {"ssids": ssids, "iw_compatible": iw_status}
7981

8082

83+
class wifi_set_hotspot_password(Resource):
84+
def post(self):
85+
content = request.get_json()
86+
87+
if ("password" not in content) or (len(content["password"]) < 8):
88+
return {
89+
"message": "Passwords must be 8 characters or longer."
90+
}, 400
91+
92+
if not dotenv_values("db/.db"):
93+
with open("db/.db", "w") as db:
94+
db.write("PWC_HOTSPOT_PASSWORD=" + content["password"])
95+
else:
96+
dotenv.set_key(
97+
"db/.db", "PWC_HOTSPOT_PASSWORD", content["password"]
98+
)
99+
100+
return {"message": "ok"}, 200
101+
102+
103+
class wifi_set_hotspot_ssid(Resource):
104+
def post(self):
105+
content = request.get_json()
106+
107+
if not dotenv_values("db/.db"):
108+
with open("db/.db", "w") as db:
109+
db.write("PWC_HOTSPOT_SSID=" + content["ssid"])
110+
else:
111+
dotenv.set_key("db/.db", "PWC_HOTSPOT_SSID", content["ssid"])
112+
113+
return {"message": "ok"}, 200
114+
115+
81116
class wifi_set_interface(Resource):
82117
def post(self):
83118
# Check entry exists

src/run.py

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
from resources.wifi_routes import wifi_connection_status
2020
from resources.wifi_routes import wifi_forget
2121
from resources.wifi_routes import wifi_list_access_points
22+
from resources.wifi_routes import wifi_set_hotspot_password
23+
from resources.wifi_routes import wifi_set_hotspot_ssid
2224
from resources.wifi_routes import wifi_set_interface
2325
from waitress import serve
2426

@@ -32,6 +34,34 @@
3234
# Load Flask-Restful API
3335
api = Api(app, errors=errors)
3436

37+
# Begin loading program
38+
logger.info("Checking for previously configured Wi-Fi connections...")
39+
40+
# Start dnsmasq service for assigning IPs to connected devices
41+
dnsmasq()
42+
43+
# Allow time for an exsiting saved Wi-Fi connection to connect.
44+
time.sleep(10)
45+
46+
# Log interface status
47+
if config.interface.lower() != config.auto_interface:
48+
logger.info(f"Interface set to {config.interface}")
49+
50+
# If the Wi-Fi connection or device is already active, do nothing
51+
if check_wifi_status() or check_device_state():
52+
led(1)
53+
logger.info("A Wi-Fi connection or hotspot is already active.")
54+
logger.info("Ready...")
55+
# If the Wi-Fi connection and device are not active, start a hotspot
56+
else:
57+
led(0)
58+
refresh_networks(retries=1)
59+
if config.auto_connect_kargs:
60+
logger.info("Attempting auto-connect...")
61+
auto_connect(**config.auto_connect_kargs)
62+
else:
63+
connect()
64+
3565
# Health check routes
3666
api.add_resource(system_health_check, "/healthcheck")
3767

@@ -40,36 +70,9 @@
4070
api.add_resource(wifi_connection_status, "/v1/connection_status")
4171
api.add_resource(wifi_forget, "/v1/forget")
4272
api.add_resource(wifi_list_access_points, "/v1/list_access_points")
73+
api.add_resource(wifi_set_hotspot_password, "/v1/set_hotspot_password")
74+
api.add_resource(wifi_set_hotspot_ssid, "/v1/set_hotspot_ssid")
4375
api.add_resource(wifi_set_interface, "/v1/set_interface")
4476

45-
if __name__ == "__main__":
46-
# Begin loading program
47-
logger.info("Checking for previously configured Wi-Fi connections...")
48-
49-
# Start dnsmasq service for assigning IPs to connected devices
50-
dnsmasq()
51-
52-
# Allow time for an exsiting saved Wi-Fi connection to connect.
53-
time.sleep(10)
54-
55-
# Log interface status
56-
if config.interface.lower() != config.auto_interface:
57-
logger.info(f"Interface set to {config.interface}")
58-
59-
# If the Wi-Fi connection or device is already active, do nothing
60-
if check_wifi_status() or check_device_state():
61-
led(1)
62-
logger.info("A Wi-Fi connection or hotspot is already active.")
63-
logger.info("Ready...")
64-
# If the Wi-Fi connection and device are not active, start a hotspot
65-
else:
66-
led(0)
67-
refresh_networks(retries=1)
68-
if config.auto_connect_kargs:
69-
logger.info("Attempting auto-connect...")
70-
auto_connect(**config.auto_connect_kargs)
71-
else:
72-
connect()
73-
74-
logger.info(f"Listening on {host} port {port}")
75-
serve(app, host=host, port=port)
77+
logger.info(f"Listening on {host} port {port}")
78+
serve(app, host=host, port=port)

0 commit comments

Comments
 (0)