In the project we have implemented a phone device control system using PHP 4.3+ dynamically generates a web interface for user registration, menu, device and command configuration and command execution. It also dynamically generate a voice interface in Voice XML 2.0 that allows users to login, navigate menus and execute commands. The user data is stored in a My SQL 3.2+ database.
This project was implemented on a web server running PHP 4.3+ on SunOS operating system.
SIP Test Client(siptc) is a simple SIP user agent used sending SIP messages, and it is developed at Columbia University's IRT lab. The system also be can run on a web server w/ PHP 4.2+ support on Linux or any other operating system for which siptc can be compiled for.
Both web and voice applications rely heavily on PEAR's DB 1.6.8 and HTML_QuickForm 3.2.4pl1 packages available for download from PEAR's web site and PHP Voice 1.8 beta module available for download from project's web site. All of these packages can be installed locally into the project's directory and enable to run the system without the need for an individual with administrative access to install them.
This project was implemented on a database server running My SQL 3.2+ on SunOS operating system and was accessed via tcp by the backend module. Because database interaction was implemented using PEAR DB package, a database abstraction library, the system can use other database systems to which the database SQL schema can be successfully converted, such as PostgreSQL.
Availability of a Voice XML 2.0 compliant gateway is a major requirement of this project. We have used BeVocal Cafe, a gateway available for free to voice application developers, in the development process, but any other Voice XML 2.0 compliant gateway can be used since no BeVocal specific Voice XML tags were used in the voice application.
The device database is an integral part of this system. It stores user's information, menus, devices and commands. It was designed to contain mixed menus containing sub menus and commands, so that the user can put the more commonly user commands higher in the menu hierarchy for faster access. It was also designed to allow for the user to move the menus and commands freely in the menu hierarchy and change the order in which they are presented in the voice interface. This was accomplished using the following tables:
This table stores user's name, sip address and sip server information together with web and voice login information. UserId is generated automatically using AUTO_INCREMENT to simplify generation of a unique ID every time a new user is added to the database. Username and Password fields are used for authenticating the user through a web interface and Phone and Pin fields are used for authenticating the user in the voice interface. Hence, Username and Pin are unique fields in the database because they are supposed identify users uniquely and both Password and Pin are stored in the database as MD5 hashes. User's SIP server and address information is used for SIP control messages. Updated fields stores the last time user information was updated.
+-----------+--------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+--------------------+------+-----+---------+----------------+ | UserId | int(11) unsigned | | PRI | NULL | auto_increment | | LastName | varchar(20) | | | | | | FirstName | varchar(10) | | | | | | Username | varchar(30) | | UNI | | | | Password | varchar(32) binary | | | | | | Phone | varchar(10) | | UNI | | | | Pin | varchar(32) binary | | | | | | Server | varchar(30) | | | | | | Address | varchar(60) | | | | | | Updated | timestamp(14) | YES | | NULL | | +-----------+--------------------+------+-----+---------+----------------+
This table stores the user's menu information. Unique id for each menu is again automatically generated by AUTO_INCREMENT every time a new menu is created. It also contains a name of the menu together with its owner's id, which was designed to be a foreign key into Users table with ON DELETE CASCADE option to simplify the deletion of the user's menu information whenever a user is removed from the database. However, the My SQL version available for the development didn't support InnoDB tables that support foreign key constraints, hence the consistency check have to be implemented in the application. MenuLocationId specifies the location of the menu in the user's menu hierarchy and MenuOrder specifies the order in which it should appear in the menu that it is located in. The name field is used to links in between menus for both web and voice interfaces and for this reason are supposed to contain only letters to avoid complication with special symbols when they are used to generate grammars for speech recognition engines.
+----------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------------+------------------+------+-----+---------+----------------+ | MenuId | int(11) unsigned | | PRI | NULL | auto_increment | | Name | varchar(30) | | | | | | OwnerId | int(11) unsigned | | | 0 | | | MenuLocationId | int(11) unsigned | | | 0 | | | MenuOrder | tinyint(4) | | | 0 | | +----------------+------------------+------+-----+---------+----------------+
For example, in the following table menus with ID's 1, 4 and 11 are main menus because they are located in themselves and all other menus are located in their respective main menus or sub menus of them. MenuOrder of menu #2 specifies that it should appear after menu #3 of the user #1's main menu. Note that MenuOrder was not designed to be unique to simplify the implementation; menu orders are supposed to be resolved using MenuId's in the cases where they are equal.
+--------+-----------+---------+----------------+-----------+
| MenuId | Name | OwnerId | MenuLocationId | MenuOrder |
+--------+-----------+---------+----------------+-----------+
| 1 | Main Menu | 1 | 1 | 0 |
| 2 | Bedroom | 1 | 1 | 2 |
| 3 | Basement | 1 | 1 | 1 |
| 4 | Main Menu | 2 | 4 | 0 |
| 5 | Bedroom | 2 | 4 | 0 |
| 6 | Bathroom | 2 | 5 | 0 |
| 11 | Main Menu | 5 | 11 | 0 |
+--------+-----------+---------+----------------+-----------+
Devices table was designed to store the information about devices for each user. Similarly to other tables, DeviceId is uniquely generated whenever a new device is added to the database and OwnerId was designed to a foreign key into the Users table with ON DELETE CASCADE option. This table also stores devices' names, sip addresses and counts how many times a message have been send to those devices, for use in CSeq field of SIP messages. Similarly to the Menus' Name field, the Device name is supposed to have only letters.
+----------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+------------------+------+-----+---------+----------------+ | DeviceId | int(11) unsigned | | PRI | NULL | auto_increment | | Name | varchar(30) | | | | | | OwnerId | int(11) unsigned | | | 0 | | | Address | varchar(60) | | | | | | Count | int(10) unsigned | | | 0 | | +----------+------------------+------+-----+---------+----------------+
Commands table was designed to store commands information for the user's devices. Here again, CommandId is uniquely generated using AUTO_INCREMENT and OwnerId, DeviceId, MenuLocationId were designed to be foreign keys into their respective tables with ON DELETE CASCADE, because the commands cannot function properly without the corresponding owner, device or menu information. Suppose a user wanted to delete a menu but wanted to keep the commands located in it, then he/she would have to move those them to the parent menu of the menu where it is located. The type field is provided as an extension to the system to allow other message types to be send i.e. email.
+----------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------------+------------------+------+-----+---------+----------------+ | CommandId | int(11) unsigned | | PRI | NULL | auto_increment | | Name | varchar(30) | | | | | | OwnerId | int(11) unsigned | | | 0 | | | DeviceId | int(11) unsigned | | | 0 | | | Type | tinyint(4) | | | 0 | | | Action | text | | | | | | MenuLocationId | int(11) unsigned | | | 0 | | | MenuOrder | tinyint(4) | | | 0 | | +----------------+------------------+------+-----+---------+----------------+
The detailed infomation about the modules, their description and functions is provided in PhpDocumentor generated documentation here. Next brief description of the source files with links to their sample outputs.
phone2dev.php - contains Phone2Dev class which abstracts database access to web and voice interfaces.
login.php - validates user input, authenticates the user and stores the user information is session variables.
Screen shot of the login page:
Screen shot of login form validating input:
register.php - validates user information and registers in in the database.
Screen shot of the registration script validating user input:
menu.php - lets the user to navigate between menus and choose commands.
Screen shot of the script generating user's main menu:
Screen shot of the script generating a sub menu:
command.php - executes commands selected by the user and displays confirmations if successful.
Screen shot of command execution feedback:
logout.php - logs out the user.
Screen shot of the logout confirmation:
login.vxml - login menu that retrieves user's phone and pin numbers.
Sample output:
<?xml version="1.0"?> <!DOCTYPE vxml PUBLIC "-//BeVocal Inc//VoiceXML 2.0//EN" "http://cafe.bevocal.com/libraries/dtd/vxml2-0-bevocal.dtd"> <vxml version="2.0" xmlns="http://www.w3.org/2001/vxml"> <var name="phone" expr="session.telephone.ani"/> <var name="pin"/> <!-- START OVER --> <link next="login.vxml"> <grammar version="1.0"> <![CDATA[ [(start over)] ]]> </grammar> </link> <!-- AUTHENTICATION ROUTINE --> <form id="greeting"> <!-- OUTPUT PHONE NUMBER --> <block> <prompt> Welcome to phone to device control system. </prompt> </block> <!-- RECEIVE PIN--> <field name="userPin" type="digits"> <prompt> Please speak or enter your four digit pin. </prompt> <filled> <if cond="userPin.length != 4"> <prompt> Sorry, you did not enter exactly four digits. </prompt> <clear/> <reprompt/> <else/> <assign name="pin" expr="userPin"/> <goto next="#auth"/> </if> </filled> </field> </form> <!-- SUBMIT INFORMATION FOR AUTHENTICATION --> <form id="auth"> <block> <submit next="voice_login.php" namelist="phone pin" method="post" fetchtimeout="10s"/> </block> </form> <!-- GOODBYE FORM --> <form id="goodbye"> <field> <prompt> Thank you for calling. Good bye. </prompt> <nomatch><exit/></nomatch> <noinput><exit/></noinput> </field> </form> </vxml> |
voice_error.php - custom error handling function for Voice XML documents.
<?xml version="1.0" encoding="ISO-8859-1"?> <vxml xmlns="http://www.w3.org/2001/vxml" version="2.0"> <form id="error"> <block> This is a test error. in /n/archon/u/student/svm2102/html/phone2dev/voice_login.php at line 21 </block> </form> </vxml> |
voice_login.php - verifies the login information and stores the user information is session variables.
<?xml version="1.0" encoding="ISO-8859-1"?> <vxml xmlns="http://www.w3.org/2001/vxml" version="2.0"> <catch event="connection.disconnect"> <exit/> </catch> <form id="login_success"> <block> <prompt> Login success. You telephone is <say-as type="telephone"> 9149540786 </say-as> . The pin you entered is <say-as type="number:digits"> 1234 </say-as> . </prompt> </block> <goto next="voice_menu.php"/> </form> </vxml> |
Error if phone and pin aren't supplied via POST.
<?xml version="1.0" encoding="ISO-8859-1"?> <vxml xmlns="http://www.w3.org/2001/vxml" version="2.0"> <catch event="connection.disconnect"> <exit/> </catch> <form id="login_failure"> <block> <prompt> You have not supplied required information. Unable to continue. Good bye. </prompt> </block> </form> </vxml> |
voice_menu.php - lets the user to navigate between menus and choose commands.
Output of a user's main menu:
<?xml version="1.0" encoding="ISO-8859-1"?> <vxml xmlns="http://www.w3.org/2001/vxml" version="2.0"> <catch event="connection.disconnect"> <exit/> </catch> <menu id="main_menu"> <prompt> Select one of the following menu choices: <enumerate/> </prompt> <choice next="voice_menu.php?menuId=2"> Bedroom </choice> <choice next="voice_menu.php?menuId=3"> Basement </choice> <choice next="voice_command.php?menuId=1&commandId=1&deviceId=1"> SIP Light Turn On </choice> <choice next="voice_command.php?menuId=1&commandId=2&deviceId=1"> SIP Light Turn Off </choice> </menu> </vxml> |
Output of a user's sub menu:
<?xml version="1.0" encoding="ISO-8859-1"?> <vxml xmlns="http://www.w3.org/2001/vxml" version="2.0"> <catch event="connection.disconnect"> <exit/> </catch> <menu id="main_menu"> <prompt> Select one of the following menu choices: <enumerate/> </prompt> <choice next="voice_command.php?menuId=2&commandId=6&deviceId=3"> SIP A.C. Switch On </choice> <choice next="voice_command.php?menuId=2&commandId=7&deviceId=3"> SIP A.C. Switch Off </choice> <choice next="voice_menu.php?menuId=1"> Return to main menu. </choice> <choice next="voice_menu.php?menuId=2"> Return to previous menu. </choice> </menu> </vxml> |
voice_command.php - executes commands selected by the user and returns confirmations if successful.
Output of a command execution feedback.
<?xml version="1.0" encoding="ISO-8859-1"?> <vxml xmlns="http://www.w3.org/2001/vxml" version="2.0"> <catch event="connection.disconnect"> <exit/> </catch> <menu id="command_menu"> <prompt> Command send failed with error code: 202 . Select one of the following menu choices <enumerate/> </prompt> <choice next="voice_menu.php?menuId=1"> Return to main menu. </choice> </menu> </vxml> |
User's credentials are supplied to the authentication routine via POST and used to authenticate in the database. Once the user is have been authenticated his/her userId and mainMenuId are stored as session variables to signify that the user is logged in and the user is redirected to the main menu. Depending on the user's choices appropriate scripts are invoked and necessary parameters are passed via GET to those scripts.