Skip to content

Commit

Permalink
Guest WiFi Butler initial release
Browse files Browse the repository at this point in the history
 - add QR generation
 - add daily passphrase reset
 - add config file support
 - add gallery feature
 - add settings feature for display brightness
 - add correct mime type for mobileconfig files
  • Loading branch information
r0binary committed May 8, 2019
1 parent 687abbd commit 57e046f
Show file tree
Hide file tree
Showing 37 changed files with 1,116 additions and 0 deletions.
53 changes: 53 additions & 0 deletions .kivy/config.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[kivy]
keyboard_repeat_delay = 300
keyboard_repeat_rate = 30
log_dir = logs
log_enable = 1
log_level = info
log_name = kivy_%y-%m-%d_%_.txt
window_icon =
keyboard_mode =
keyboard_layout = qwertz
desktop = 1
exit_on_escape = 1
config_version = 10

[graphics]
display = -1
fullscreen = no
height = 600
left = 0
maxfps = 60
multisamples = 2
position = auto
rotation = 0
show_cursor = 1
top = 0
width = 800
resizable = 1

[input]
mouse = mouse
mtdev_%(name)s = probesysfs,provider=mtdev
hid_%(name)s = probesysfs,provider=hidinput

[postproc]
double_tap_distance = 20
double_tap_time = 250
ignore = []
jitter_distance = 0
jitter_ignore_devices = mouse,mactouch,
retain_distance = 50
retain_time = 0
triple_tap_distance = 20
triple_tap_time = 375

[widgets]
scroll_timeout = 250
scroll_distance = 20
scroll_friction = 1.
scroll_stoptime = 300
scroll_moves = 5

[modules]

11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Guest Wifi Butler Change Log

- **0.2.1** Add correct mime type "application/x-apple-aspen-config" for mobileconfig files

- **0.2.0** Add settings feature for display brightness

- **0.1.0** Initial Release
- QR generation
- daily passphrase reset
- config file
- Gallery feature
6 changes: 6 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include requirements.txt
include guest_wifi_butler/guest_wifi_config.ini
include guest_wifi_butler/data/*
include guest_wifi_butler/images/*
include guest_wifi_butler/ui/*.kv

86 changes: 86 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Guest WiFi Butler

This project turns a Raspberry Pi with WiFi adapter and attached touchscreen into a WiFi access
point. It is based on an idea from the german IT magazine c't. Your guests can easily connect to the
WiFi by scanning a QR code. It is regenerated every day to ensure your guests do not have unlimited
access. When the device is not requested to show the WiFi QR codes it turns into a digital photo
frame that show a slideshow of your favorite photographs.

## Build

python setup.py sdist bdist_wheel

## Installation

The following instructions are copied from the [Kivy
installation guide for Raspberry Pi](https://kivy.org/docs/installation/installation-rpi.html).

sudo pip install -U Cython==0.28.2
sudo pip install git+https://github.com/kivy/kivy.git@master

And finally we need to install the `guest_wifi_butler` package

sudo pip install guest_wifi_butler-X.X.X.tar.gz

## Enable Raspberry Pi Touchscreen

By default Kivy will not work properly with the Raspberry Pi's touchscreen.
To change that add these lines to `~/.kivy/config.ini` into input section

mouse = mouse
mtdev_%(name)s = probesysfs,provider=mtdev
hid_%(name)s = probesysfs,provider=hidinput

The config is the one of the non-root user e.g. `pi`, so in this case it
would be `/home/pi/.kivy/config.ini`.

## Configuration

### Force the screen to stay on

To force the Raspberry Pi's screen to stay on, adjust LightDM's
configuration like this:

sudo nano /etc/lightdm/lightdm.conf

Add the following line to the `[SeatDefaults]` section:

xserver-command=X -s 0 dpms

### Access Point configuration

In order to let bridged IP and ARP packets pass the access point, it is
required to set the following sysctl variables either manually or persistent
in `/etc/sysctl.conf`:

net.bridge.bridge-nf-call-iptables=0
net.bridge.bridge-nf-call-arptables=0
net.bridge.bridge-nf-call-ip6tables=0

### Autostart on Boot

To automatically start the guest wifi butler on Raspian startup create a file `~/.config/autostart/guest_wifi_butler.desktop` with the following content:

[Desktop Entry]
Encoding=UTF-8
Type=Application
Name=Guest WiFi Butler
Exec=sudo butler
StartupNotify=false
Hidden=false

Once you reboot Raspian it will show the wifi butler after a few seconds.

## Development guide

Any help for this project will be appreciated. You can contribute through
bug reports, ideas for new features or own pull requests. To make sure you
did not break anything, you can run the (by far not complete) test suite.

### Runs Tests

pytest guest_wifi_butler\test

### Code Coverage

pytest --cov=guest_wifi_butler guest_wifi_butler\test
2 changes: 2 additions & 0 deletions guest_wifi_butler/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import os.path
script_location = os.path.dirname(os.path.realpath(__file__))
95 changes: 95 additions & 0 deletions guest_wifi_butler/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import threading
import subprocess
import time
import schedule
import atexit
import shutil
import random
import string
import sys
from guest_wifi_butler.ui.app import GuestWifiButlerApp
from guest_wifi_butler.wifi_qr_generator import WifiQrGenerator
from guest_wifi_butler.config.config_handler import ConfigHandler
from guest_wifi_butler.config.hostapd_config_builder import HostapdConfigBuilder
from guest_wifi_butler.ios.configuration_profile_creator import iOSConfigurationProfileCreator
from guest_wifi_butler.ios.configuration_profile_server import iOSConfigurationProfileServer


def hostapd_restart():
sys.stdout.write('Restarting Hostapd...')
assert subprocess.call('/usr/sbin/service hostapd stop'.split(' '),
shell=False) == 0, 'Cannot stop Hostapd service'
time.sleep(8)
assert subprocess.call('/usr/sbin/service hostapd start'.split(' '),
shell=False) == 0, 'Cannot start Hostapd service'
sys.stdout.write(' Done\n')


def update_wifi_information(config):
passphrase = generate_passphrase(config.passphrase_length)

ios_config_creator = iOSConfigurationProfileCreator(
config.config_directory)
config_filename = ios_config_creator.generate(config.ssid,
passphrase,
config.identifier)
config_uri = '%s://%s:%d/%s' % (config.public_protocol,
config.public_domain,
config.public_port,
config_filename)
# Is the mime types set correctly?

wifi_qr_generator = WifiQrGenerator(config.temp_directory)
wifi_qr_generator.generate_android(
config.encryption, config.ssid, passphrase)
wifi_qr_generator.generate_windows(
config.encryption, config.ssid, passphrase)
wifi_qr_generator.generate_ios(config_uri)

HostapdConfigBuilder(
'/etc/hostapd/hostapd.conf').generate(config.ssid, passphrase)
hostapd_restart()
return passphrase


def start_task_scheduler():
def run():
while True:
schedule.run_pending()
time.sleep(1)

scheduler_thread = threading.Thread(target=run)
scheduler_thread.daemon = True
scheduler_thread.start()


def generate_passphrase(length):
allowed_characters = string.letters + string.digits
return ''.join(random.choice(allowed_characters) for _ in range(length))


def main():
config = ConfigHandler()
passphrase = update_wifi_information(config)
app = GuestWifiButlerApp(config, passphrase)

server = iOSConfigurationProfileServer()
server.startServer(config.listen_address, config.listen_port)

def run_update_wifi():
passphrase = update_wifi_information(config)
app.update_wifi_information(passphrase)

schedule.every().day.at(config.update_passphrase_time).do(run_update_wifi)
start_task_scheduler()

def remove_temp_directory():
shutil.rmtree(config.temp_directory)

atexit.register(remove_temp_directory)

app.run()


if __name__ == "__main__":
main()
4 changes: 4 additions & 0 deletions guest_wifi_butler/butler
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env python
import guest_wifi_butler.__main__ as guest_wifi_butler

guest_wifi_butler.main()
Empty file.
51 changes: 51 additions & 0 deletions guest_wifi_butler/config/config_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import os
from configparser import RawConfigParser
from guest_wifi_butler import script_location


DEFAULT_CONFIG = os.path.join(script_location, 'guest_wifi_config.ini')


class ConfigHandler:
def __init__(self, config_filepath=DEFAULT_CONFIG):
config = RawConfigParser(allow_no_value=True)
config.read(config_filepath)

self.temp_directory = self.__assertWritableDirectory(
config.get('general', 'temp_directory'))

self.ssid = config.get('wifi', 'ssid')
self.encryption = config.get('wifi', 'encryption')
self.passphrase_length = config.getint('wifi', 'passphrase_length')
self.update_passphrase_time = config.get(
'wifi', 'update_passphrase_time')
self.seconds_to_gallery = config.getint(
'wifi', 'seconds_to_gallery')

self.public_domain = config.get('ios_config_server', 'public_domain')
self.public_protocol = config.get(
'ios_config_server', 'public_protocol')
self.public_port = config.getint('ios_config_server', 'public_port')

self.listen_port = config.getint('ios_config_server', 'listen_port')
self.listen_address = config.get('ios_config_server', 'listen_address')
self.config_directory = self.__assertWritableDirectory(
config.get('ios_config_server', 'config_directory'))
self.identifier = '.'.join(self.public_domain.split('.')[::-1])

self.image_directory = self.__assertWritableDirectory(
config.get('gallery', 'image_directory'))
self.image_display_seconds = config.getint(
'gallery', 'image_display_seconds')

def __assertWritableDirectory(self, filepath):
if not os.path.isabs(filepath):
filepath = os.path.join(script_location, filepath)

if not os.path.exists(filepath):
os.mkdir(filepath)

if not os.access(filepath, os.W_OK):
raise OSError('Permission error. Cannot write to %s' % filepath)

return filepath
20 changes: 20 additions & 0 deletions guest_wifi_butler/config/hostapd_config_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from string import Template
from uuid import uuid4
from guest_wifi_butler import script_location
import os


class HostapdConfigBuilder:
def __init__(self, config_filepath):
self.__config_filepath = config_filepath

def generate(self, ssid, passphrase):
config_in_filename = 'hostapd.conf.in'
config_in_filepath = os.path.join(
script_location, 'data', config_in_filename)

with open(config_in_filepath) as config_in_file, open(self.__config_filepath, 'w') as config_file:
config_template = Template(config_in_file.read())
config = config_template.substitute(
ssid=ssid, passphrase=passphrase)
config_file.write(config)
Loading

0 comments on commit 57e046f

Please sign in to comment.