Skip to content

Commit be9ede0

Browse files
committed
Add automatic connection option
1 parent 11c5467 commit be9ede0

File tree

5 files changed

+73
-20
lines changed

5 files changed

+73
-20
lines changed

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
## Description
2-
32
An API for controlling Wi-Fi connections on [Balena](https://www.balena.io/os/) devices.
43

54
It does not contain an interface, instead it provides API endpoints to send requests to interact with the device. Any interface of your choice can be built to interact with the API. If you develop an interface that is open source, please do let me know so I can provide people links.
@@ -13,21 +12,28 @@ You can set your own default Wi-Fi SSID and a Wi-Fi password for your hotspot us
1312

1413
Enjoy and please do feel free to feedback experiences and issues.
1514

16-
## Securing the API
15+
## Automatic connections
16+
You can specify a Wi-Fi connection you would like your device to try and connect to the first time it loads by using the environment variables in the docker-compose.yml file. Once this connection is established, the device will stay connected after reboots until you use the `forget` endpoint. If the network is not available, the hotspot will start instead.
1717

18+
````
19+
PWC_SSID: "network-name" # The SSID of the network you would like to try and auto-connect.
20+
USERNAME: "username" # Optional, for enterprise networks
21+
PWC_PASSWORD: "your-password" # Optional, the password associated with the Wi-Fi network. Must be 8 characters or more.
22+
````
23+
24+
## Securing the API
1825
By default, the API is exposed so your interface can interact directly. In other words, anyone can go to `http://your-device:9090/v1/connect` to send commands to your device.
1926

20-
If you would prefer to only allow access from your backend, change the `host` environment variable to `127.0.0.1`. Then ensure your backend container is connected to the host network so it matches the API docker-compose.yml file in this repo:
27+
If you would prefer to only allow access from your backend, change the `PWC_HOST` environment variable to `127.0.0.1`. Then ensure your backend container is connected to the host network so it matches the API docker-compose.yml file in this repo:
2128

2229
`network_mode: "host"`
2330

2431
Users will then be unable to access the API `http://your-device:9090/v1/connect`. Your backend container on the device, however, can reach the API using `http://127.0.0.1:9090/v1/connect`. This is useful if your interface has a login process, and you only want users to be able to interact with Wi-Fi after logging in.
2532

26-
Alternatively, if you would rather have your backend use specified ports instead of the host network, you can change the `host` environment variable to `172.17.0.1` and access the API from `http://172.17.0.1:9090/v1/connect`.
33+
Alternatively, if you would rather have your backend use specified ports instead of the host network, you can change the `PWC_HOST` environment variable to `172.17.0.1` and access the API from `http://172.17.0.1:9090/v1/connect`.
2734

2835

2936
## Endpoints
30-
3137
### http://your-device:9090/v1/connect
3238
Connect to a nearby Wi-Fi access point.
3339

docker-compose.yml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,20 @@ version: '2.1'
33
services:
44
py-wifi-connect:
55
environment:
6-
HOTSPOT_SSID: "Py Wi-Fi Connect"
7-
#HOTSPOT_PASSWORD: "my-hotspot-password" # Hotspot passwords must be 8 characters or more.
8-
HOST: "0.0.0.0"
9-
PORT: 9090
6+
## Listening IP and port ##
7+
PWC_HOST: "0.0.0.0"
8+
PWC_PORT: 9090
9+
10+
## Hotspot details ##
11+
PWC_HOTSPOT_SSID: "Py Wi-Fi Connect"
12+
#HOTSPOT_PASSWORD: "my-hotspot-password" # Optional hotspot password. Must be 8 characters or more.
13+
14+
## Try to automatically set up a Wi-Fi network on first boot ##
15+
#PWC_SSID: "network-name" # Compulsory for this feature
16+
#USERNAME: "username" # Optional
17+
#PWC_PASSWORD: "your-password" # Optional. Must be 8 characters or more.
18+
19+
## System Variables ##
1020
DBUS_SYSTEM_BUS_ADDRESS: "unix:path=/host/run/dbus/system_bus_socket"
1121
build:
1222
context: .

src/common/wifi.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,24 @@ def analyse_access_point(ap):
4747
return entry
4848

4949

50+
def auto_connect(ssid=None,
51+
username=None,
52+
password=None):
53+
ssids, _ = list_access_points()
54+
55+
for ssid_item in ssids:
56+
if ssid_item['ssid'] == ssid:
57+
connect(conn_type=ssid_item['conn_type'],
58+
ssid=ssid,
59+
username=username,
60+
password=password)
61+
break
62+
else:
63+
logger.info('Auto-connect failed as the device could not find the '
64+
'specified network.')
65+
connect()
66+
67+
5068
def check_internet_status(host="8.8.8.8", port=53, timeout=5):
5169
try:
5270
socket.setdefaulttimeout(timeout)
@@ -236,7 +254,7 @@ def refresh_networks(retries=5):
236254
logger.error('Unknown error calling IW.')
237255
return False
238256
else:
239-
logger.info('IW succeeded.')
257+
logger.debug('IW succeeded.')
240258
return True
241259
finally:
242260
run += 1

src/config.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,41 @@
11
import os
22

33
# Set default Wi-Fi SSID.
4-
if "HOTSPOT_SSID" in os.environ:
5-
hotspot_ssid = os.environ['HOTSPOT_SSID']
4+
if "PWC_HOTSPOT_SSID" in os.environ:
5+
hotspot_ssid = os.environ['PWC_HOTSPOT_SSID']
66
else:
77
hotspot_ssid = "Py Wi-Fi Connect"
88

99
# Set default hotspot password.
10-
if "HOTSPOT_PASSWORD" in os.environ:
11-
hotspot_password = os.environ['HOTSPOT_PASSWORD']
10+
if "PWC_HOTSPOT_PASSWORD" in os.environ:
11+
hotspot_password = os.environ['PWC_HOTSPOT_PASSWORD']
1212
else:
1313
hotspot_password = None
1414

1515
# Set default host.
16-
if "HOST" in os.environ:
17-
host = os.environ['HOST']
16+
if "PWC_HOST" in os.environ:
17+
host = os.environ['PWC_HOST']
1818
else:
1919
host = '0.0.0.0'
2020

2121
# Set default port.
22-
if "PORT" in os.environ:
23-
port = os.environ['PORT']
22+
if "PWC_PORT" in os.environ:
23+
port = os.environ['PWC_PORT']
2424
else:
2525
port = 9090
2626

27+
# Compile kwargs for automatic connection
28+
if "PWC_SSID" in os.environ:
29+
auto_connect_kargs = \
30+
{"ssid": os.environ['PWC_SSID']}
31+
32+
if "PWC_USERNAME" in os.environ:
33+
auto_connect_kargs.update(username=os.environ['PWC_USERNAME'])
34+
if "PWC_PASSWORD" in os.environ:
35+
auto_connect_kargs.update(password=os.environ['PWC_PASSWORD'])
36+
else:
37+
auto_connect_kargs = False
38+
2739
# Default access point name. No need to change these under usual operation as
2840
# they are for use inside the app only. PWC is acronym for 'Py Wi-Fi Connect'.
2941
ap_name = 'PWC'

src/run.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import atexit
2+
import config
23
import signal
34
import sys
45
import time
56
from common.errors import errors
67
from common.errors import logger
78
from common.system import dnsmasq
9+
from common.wifi import auto_connect
810
from common.wifi import check_wifi_status
911
from common.wifi import connect
1012
from common.wifi import refresh_networks
@@ -29,6 +31,7 @@ def handle_sigterm(*args):
2931

3032
# Startup process
3133
if __name__ == '__main__':
34+
logger.info('Starting...')
3235
# Load Flask-Restful API
3336
api = Api(errors=errors)
3437

@@ -51,9 +54,13 @@ def handle_sigterm(*args):
5154
if check_wifi_status():
5255
logger.info('Wi-Fi connection already established.')
5356
else:
54-
logger.info('Starting hotspot...')
5557
refresh_networks(retries=1)
56-
connect()
58+
if config.auto_connect_kargs:
59+
logger.info('Attempting auto-connect...')
60+
auto_connect(**config.auto_connect_kargs)
61+
else:
62+
logger.info('Starting hotspot...')
63+
connect()
5764

5865
# Configure endpoints #
5966

0 commit comments

Comments
 (0)