Webifi.me

Python Tutorial

 

Index

1. Download/Install Python Library
2. Tutorial Code
3. Test

1. Download/Install Python Library

The code for this tutorial can be downloaded from https://github.com/webifi-me/WebifiPythonTutorial.

To build the tutorial yourself, create a folder where you would like to store the files for this tutorial. The library files can either be installed using:
pip install webifi
or for Linux users:
sudo pip install webifi
The second option is to clone/download the library:
git clone https://github.com/webifi-me/WebifiPythonTutorial.git
Or go to https://github.com/webifi-me/WebifiPythonTutorial.git to download the library.

There are two important library files:

  • Webifi_p27.py
  • Webifi_p3.py

These files need to be stored in the same folder where you plan to create the tutorial files if you plan to download or clone them. The only difference between them is that the first one is used for Python 2.7 and the second one is used with Python 3. The latest Python libraries uses WebSocket for communication. If you cloned or downloaded the libraries you need to install websocket client. Please use the following command to install websocket client:
pip install websocket-client

2. Tutorial Code

In the same folder as the library files create a Python file called WebifiPythonTutorial_p27.py (or WebifiPythonTutorial_p3.py if you use Python 3). Copy and paste the following code into your file if you are using Python 2.7:

#!/usr/bin/python

import Webifi_p27
import Tkinter
import tkMessageBox

class GuiControls():
    def __init__(self):
        gui = Tkinter.Tk()
        pad_x = 5
        pad_y = 3
        self.gui = gui
        self.webifi = None
        self.gui_row_number = 0

        gui.title('Webifi Tutorial')

        # connect name, password, set network names and start button
        Tkinter.Label(gui, text='Connect name:').grid(row=self.get_row_num(False), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.entry_connect_name = Tkinter.Entry(gui, width=25)
        self.entry_connect_name.insert(0, 'connect name')
        self.entry_connect_name.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=0, sticky='W')
        Tkinter.Label(gui, text='Connect password:').grid(row=self.get_row_num(True), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.entry_connect_password = Tkinter.Entry(gui, width=25)
        self.entry_connect_password.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=0, sticky='W')
        self.entry_connect_password.insert(0, 'password')
        Tkinter.Label(gui, text='Network names:').grid(row=self.get_row_num(True), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.text_network_names = Tkinter.Text(gui, wrap=Tkinter.WORD, undo=True, height=5, width=15)
        self.text_network_names.grid(row=self.get_row_num(True), column=0, columnspan=1, padx=pad_x-2, pady=0, sticky='WE')
        self.scroll_network_names = Tkinter.Scrollbar(gui, command=self.text_network_names.yview)
        self.scroll_network_names.grid(row=self.get_row_num(False), column=0, sticky='nsew')
        self.text_network_names['yscrollcommand'] = self.scroll_network_names.set
        self.text_network_names.insert(Tkinter.INSERT, 'network 1')
        self.button_set_network_names = Tkinter.Button(gui, text='Set network names', width=16, command=self.click_button_set_network_names)
        self.button_set_network_names.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=pad_y, sticky='W')
        self.button_start = Tkinter.Button(gui, text='Start', width=12, command=self.click_button_start)
        self.button_start.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=pad_y, sticky='W')

        # send data controls
        Tkinter.Label(gui, text='Text to send:').grid(row=self.get_row_num(True), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.entry_send_data = Tkinter.Entry(gui, width=56)
        self.entry_send_data.grid(row=self.get_row_num(True), column=0, columnspan=2, padx=pad_x, pady=pad_y, sticky='W')
        self.entry_send_data.insert(0, 'test message')
        self.button_send = Tkinter.Button(gui, text='Send', width=12, command=self.click_send_data)
        self.button_send.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=pad_y, sticky='W')
        self.button_send_discovery = Tkinter.Button(gui, text='Send discovery', width=12, command=self.click_send_discovery)
        self.button_send_discovery.grid(row=self.get_row_num(False), column=1, padx=pad_x, pady=pad_y, sticky='W')

        # received data
        Tkinter.Label(gui, text='Text received:').grid(row=self.get_row_num(True), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.text_rec_data = Tkinter.Text(gui, wrap=Tkinter.WORD, undo=True, height=5, width=40)
        self.text_rec_data.grid(row=self.get_row_num(True), column=0, columnspan=3, padx=pad_x-2, pady=0, sticky='WE')
        self.scroll_rec_data = Tkinter.Scrollbar(gui, command=self.text_rec_data.yview)
        self.scroll_rec_data.grid(row=self.get_row_num(False), column=3, sticky='nsew')
        self.text_rec_data['yscrollcommand'] = self.scroll_rec_data.set
        self.button_clear_rec_data = Tkinter.Button(gui, text='Clear', width=12, command=self.click_clear_rec_data)
        self.button_clear_rec_data.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=pad_y, sticky='W')

        # messages
        Tkinter.Label(gui, text='Messages:').grid(row=self.get_row_num(True), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.text_messages = Tkinter.Text(gui, wrap=Tkinter.WORD, undo=True, height=5, width=40)
        self.text_messages.grid(row=self.get_row_num(True), column=0, columnspan=3, padx=pad_x-2, pady=0, sticky='WE')
        self.scroll_messages = Tkinter.Scrollbar(gui, command=self.text_messages.yview)
        self.scroll_messages.grid(row=self.get_row_num(False), column=3, sticky='nsew')
        self.text_messages['yscrollcommand'] = self.scroll_messages.set
        self.button_clear_messages = Tkinter.Button(gui, text='Clear', width=12, command=self.click_clear_messages)
        self.button_clear_messages.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=pad_y, sticky='W')

        # set callback for when the window closes. This will terminate the Webifi service and
        # stop all the threads
        self.gui.protocol("WM_DELETE_WINDOW", self.form_closing)

        gui.mainloop()

    def get_row_num(self, new_row):
        if new_row:
            self.gui_row_number += 1
        return  self.gui_row_number

    def add_message(self, message):
        self.text_messages.insert(Tkinter.END, message + '\n')
        self.text_messages.see(Tkinter.END)

    def data_received_callback(self, data, data_type, from_who):
        if self.webifi.get_discoverable() and data_type == 'Discovery Response':
            # valid discovery response received
            self.add_message('Device Discovered: ' + data + ', ID: ' + str(from_who))
        else:
            message = data + ', ' + data_type + ', ' + str(from_who)
            self.text_rec_data.insert(Tkinter.END, message + '\n')
            self.text_rec_data.see(Tkinter.END)

    def connection_status_callback(self, connected):
        if connected:  # connection was successful
            self.add_message('Connection successful')
        else:
            # there was an error
            self.add_message('Connection failed')
            self.button_start['text'] = 'Start'
            self.webifi = None

    def click_button_set_network_names(self):
        if self.webifi is not None:
            network_names = self.text_network_names.get("1.0", Tkinter.END).split('\n')
            self.webifi.set_network_names(network_names)

    def click_send_discovery(self):
        if self.webifi is not None:
            self.webifi.send_discovery()

    def click_button_start(self):
        if self.webifi is None:
            connect_name = self.entry_connect_name.get()
            connect_password = self.entry_connect_password.get()
            network_names = self.text_network_names.get("1.0", Tkinter.END).split('\n')
            input_error = False
            if connect_name is '':
                tkMessageBox.showerror('Connect name error', 'Please enter a valid connect name')
                input_error = True
            if connect_password is '':
                tkMessageBox.showerror('Password error', 'Please enter a valid password')
                input_error = True
            if not input_error:
                # start webifi service
                self.webifi = Webifi_p27.Webifi()
                self.webifi.set_connect_name(connect_name)
                self.webifi.set_connect_password(connect_password)
                self.webifi.set_network_names(network_names)
                self.webifi.set_connection_status_callback(self.connection_status_callback)
                self.webifi.set_data_received_callback(self.data_received_callback)
                self.webifi.name = 'Webifi Python Tutorial'
                self.webifi.start()
            self.button_start['text'] = 'Stop'
        else:
            self.webifi.close_connection()
            self.button_start['text'] = 'Start'
            self.webifi = None

    def click_send_data(self):
        if self.webifi is not None:
            send_data = Webifi_p27.CreateSendData()
            send_data.data = self.entry_send_data.get()
            send_data.data_type = 'data'
            if self.webifi.connected:
                self.webifi.send_data(send_data)
        else:
            tkMessageBox.showerror('Please start service', 'The service needs to be started before this action can be performed')

    def click_clear_rec_data(self):
        self.text_rec_data.delete(1.0, Tkinter.END)

    def click_clear_messages(self):
        self.text_messages.delete(1.0, Tkinter.END)

    def form_closing(self):
        if self.webifi is not None:
            self.webifi.close_connection(True)
        self.webifi = None
        self.gui.destroy()


if __name__ == "__main__":
    gui_controls = GuiControls()

If you are using Python 3 then copy and paste the following code into your new file:

#!/usr/bin/python

import Webifi_p3
import tkinter
from tkinter import messagebox

class GuiControls():
    def __init__(self):
        gui = tkinter.Tk()
        pad_x = 5
        pad_y = 3
        self.gui = gui
        self.webifi = None
        self.gui_row_number = 0

        gui.title('Webifi Tutorial')

        # connect name, password, set network names and start button
        tkinter.Label(gui, text='Connect name:').grid(row=self.get_row_num(False), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.entry_connect_name = tkinter.Entry(gui, width=25)
        self.entry_connect_name.insert(0, 'connect name')
        self.entry_connect_name.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=0, sticky='W')
        tkinter.Label(gui, text='Connect password:').grid(row=self.get_row_num(True), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.entry_connect_password = tkinter.Entry(gui, width=25)
        self.entry_connect_password.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=0, sticky='W')
        self.entry_connect_password.insert(0, 'password')
        tkinter.Label(gui, text='Network names:').grid(row=self.get_row_num(True), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.text_network_names = tkinter.Text(gui, wrap=tkinter.WORD, undo=True, height=5, width=15)
        self.text_network_names.grid(row=self.get_row_num(True), column=0, columnspan=1, padx=pad_x-2, pady=0, sticky='WE')
        #self.scroll_network_names = tkinter.Scrollbar(gui, command=self.text_network_names.yview)
        #self.scroll_network_names.grid(row=self.get_row_num(False), column=0, sticky='nsew')
        #self.text_network_names['yscrollcommand'] = self.scroll_network_names.set
        self.text_network_names.insert(tkinter.INSERT, 'network 1')
        self.button_set_network_names = tkinter.Button(gui, text='Set network names', width=16, command=self.click_button_set_network_names)
        self.button_set_network_names.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=pad_y, sticky='W')
        self.button_start = tkinter.Button(gui, text='Start', width=12, command=self.click_button_start)
        self.button_start.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=pad_y, sticky='W')

        # send data controls
        tkinter.Label(gui, text='Text to send:').grid(row=self.get_row_num(True), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.entry_send_data = tkinter.Entry(gui, width=56)
        self.entry_send_data.grid(row=self.get_row_num(True), column=0, columnspan=2, padx=pad_x, pady=pad_y, sticky='W')
        self.entry_send_data.insert(0, 'test message')
        self.button_send = tkinter.Button(gui, text='Send', width=12, command=self.click_send_data)
        self.button_send.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=pad_y, sticky='W')
        self.button_send_discovery = tkinter.Button(gui, text='Send discovery', width=12, command=self.click_send_discovery)
        self.button_send_discovery.grid(row=self.get_row_num(False), column=1, padx=pad_x, pady=pad_y, sticky='W')

        # received data
        tkinter.Label(gui, text='Text received:').grid(row=self.get_row_num(True), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.text_rec_data = tkinter.Text(gui, wrap=tkinter.WORD, undo=True, height=5, width=40)
        self.text_rec_data.grid(row=self.get_row_num(True), column=0, columnspan=3, padx=pad_x-2, pady=0, sticky='WE')
        #self.scroll_rec_data = tkinter.Scrollbar(gui, command=self.text_rec_data.yview)
        #self.scroll_rec_data.grid(row=self.get_row_num(False), column=3, sticky='nsew')
        #self.text_rec_data['yscrollcommand'] = self.scroll_rec_data.set
        self.button_clear_rec_data = tkinter.Button(gui, text='Clear', width=12, command=self.click_clear_rec_data)
        self.button_clear_rec_data.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=pad_y, sticky='W')
        
        # messages
        tkinter.Label(gui, text='Messages:').grid(row=self.get_row_num(True), column=0, padx=pad_x-2, pady=pad_y, sticky='W')
        self.text_messages = tkinter.Text(gui, wrap=tkinter.WORD, undo=True, height=5, width=40)
        self.text_messages.grid(row=self.get_row_num(True), column=0, columnspan=3, padx=pad_x-2, pady=0, sticky='WE')
        #self.scroll_messages = tkinter.Scrollbar(gui, command=self.text_debug_messages.yview)
        #self.scroll_messages.grid(row=self.get_row_num(False), column=3, sticky='nsew')
        #self.text_messages['yscrollcommand'] = self.scroll_messages.set
        self.button_clear_messages = tkinter.Button(gui, text='Clear', width=12, command=self.click_clear_messages)
        self.button_clear_messages.grid(row=self.get_row_num(True), column=0, padx=pad_x, pady=pad_y, sticky='W')

        # set callback for when the window closes. This will terminate the Webifi service and
        # stop all the threads
        self.gui.protocol("WM_DELETE_WINDOW", self.form_closing)

        gui.mainloop()

    def get_row_num(self, new_row):
        if new_row:
            self.gui_row_number += 1
        return  self.gui_row_number

    def add_message(self, message):
        self.text_messages.insert(tkinter.END, message + '\n')
        self.text_messages.see(tkinter.END)

    def data_received_callback(self, data, data_type, from_who):
        if self.webifi.get_discoverable() and data_type == 'Discovery Response':
            # valid discovery response received
            self.add_message('Device Discovered: ' + data + ', ID: ' + str(from_who))
        else:
            message = data + ', ' + data_type + ', ' + str(from_who)
            self.text_rec_data.insert(tkinter.END, message + '\n')
            self.text_rec_data.see(tkinter.END)

    def connection_status_callback(self, connected):
        if connected:  # connection was successful
            self.add_message('Connection successful')
        else:
            # there was an error
            self.add_message('Connection failed')
            self.button_start['text'] = 'Start'
            self.webifi = None

    def click_button_set_network_names(self):
        if self.webifi is not None:
            network_names = self.text_network_names.get("1.0", tkinter.END).split('\n')
            self.webifi.set_network_names(network_names)

    def click_send_discovery(self):
        if self.webifi is not None:
            self.webifi.send_discovery()

    def click_button_start(self):
        if self.webifi is None:
            connect_name = self.entry_connect_name.get()
            connect_password = self.entry_connect_password.get()
            network_names = self.text_network_names.get("1.0", tkinter.END).split('\n')
            input_error = False
            if connect_name is '':
                messagebox.showerror('Connect name error', 'Please enter a valid connect name')
                input_error = True
            if connect_password is '':
                messagebox.showerror('Password error', 'Please enter a valid password')
                input_error = True
            if not input_error:
                # start webifi service
                self.webifi = Webifi_p3.Webifi()
                self.webifi.set_connect_name(connect_name)
                self.webifi.set_connect_password(connect_password)
                self.webifi.set_network_names(network_names)
                self.webifi.set_connection_status_callback(self.connection_status_callback)
                self.webifi.set_data_received_callback(self.data_received_callback)
                self.webifi.name = 'Webifi Python Tutorial'
                self.webifi.start()
            self.button_start['text'] = 'Stop'
        else:
            self.webifi.close_connection()
            self.button_start['text'] = 'Start'
            self.webifi = None

    def click_send_data(self):
        if self.webifi is not None:
            send_data = Webifi_p3.CreateSendData()
            send_data.data = self.entry_send_data.get()
            send_data.data_type = 'data'
            if self.webifi.connected:
                self.webifi.send_data(send_data)
        else:
            messagebox.showerror('Please start service', 'The service needs to be started before this action can be performed')

    def click_clear_rec_data(self):
        self.text_rec_data.delete(1.0, tkinter.END)

    def click_clear_messages(self):
        self.text_messages.delete(1.0, tkinter.END)

    def form_closing(self):
        if self.webifi is not None:
            self.webifi.close_connection(True)
        self.webifi = None
        self.gui.destroy()


if __name__ == "__main__":
    gui_controls = GuiControls()

Before we run the Python file we will give a quick description of the functions. The initialisation function of the class on line 8 runs when the class gets created. Lines 9 to 14 initialises a couple of global variables. Most of the intimidating code on lines 18 to 73 are for building the GUI using TkInter. The rest of the functions in the class are described below:

Function Description
get_row_num Instead of putting a fixed number for the row of a control in the GUI we rather inserted this function to return the next row number. This makes it much easier to insert a component without the need to change a lot of row numbers each time.
add_message This function will add a message to the end of the messages text box and will always make sure that the message box is scrolled to the bottom so that the new message is visible.
data_received_callback The callback called by the Webifi library when data was received.
connection_status_callback Callback from the Webifi library when the connection status changes. This gets called when the service connects or disconnects.
click_button_set_network_names This function is called by the Set network names button. It sets the new network names that this instance will be part of.
click_send_discovery This function is called by the Send discovery button. A discovery message will be sent to all your connected devices.
click_button_start This function is called by the Start button and will start or stop the Webifi service. It sets the user’s credentials and configures the needed callbacks before starting the service.
click_send_data This function is called by the Send button. It will send the message entered in the Text to send text box to your default networks.
click_clear_rec_data Clear the receive data text box.
click_clear_messages Clear the messages text box.
form_closing Callback function passed to TkInter which must be called when the window is closing. It is important to call the close_connection function when the Webifi service is running and you need to close the application. There are a couple of threads that are running in the background which could cause your program to hang if they are not stopped properly.

3. Test

The last step is to test the Python file. Run the file by entering the following on the command line for Python 2.7:
python WebifiPythonTutorial_p27.py
or for Python 3:
python WebifiPythonTutorial_p3.py
Enter valid credentials and then click on the Start button. After a short delay the Messages text box should say “Connection Successful” if your connect details are correct. We need something to communicate with. We made a test page especially for this purpose. Go to our Test Page and enter your details in the connect name and password text boxes and click the Start button. Enter “test123” into the Data to send text box in the Python application and click the Send button. The message should be displayed on the Web page. Next click the Send data button on the test page and the message should be received in the Python application.

If both the tutorial and the web page connected successfully but no data can be sent between them then the most common cause is that the network names don’t match. If the devices are not on the same default networks then they cannot exchange data unless the data is specifically sent to the other one’s network. The network names can be set after connection by clicking the Set Network Names button.

Click on the Send Discovery button. After a short delay the Messages text box should display a list of devices that was discovered. We hope this tutorial gives you enough information to get going using our Python library. We also have a full featured example available at https://github.com/webifi-me/WebifiPythonLibraryExample