The SUSI Smart Speaker is an AI assistant device which runs SUS.AI. To learn to set up your own smart speaker, head up to SUSI Installer. One of the new features of the smart speaker is the ability to control it via a webpage, the smarts speaker now allows the user to control various playback features such play/pause music directly via their mobile phones or laptops which are in the same network. The web page is served via the sound server running locally on the Raspberry Pi. The soundserver provides various methods of the vlcplayer as endpoints. The webpage uses these endpoints to control the smart speaker. Also, an external application such as an android/ios app can use these endpoints(or the webpage) to control the music playback on the device.
Making the Front-end
The front end is served via the flask server on ‘ / ’ endpoint and on the port 7070. Currently, the Front End contains the volume control slider and various buttons to control the audio playback of the device. The responses are sent to the server via javascript. Bootstrap is used for the CSS framework and Fontawesome is used for various icon support. Since the smart speaker should be able to run offline, CDN links for Bootstrap and Fontawesome are not used and the required files are served via the flask server on /static.
Adding required frameworks:
<link href=”{{ url_for(‘static’, filename=’bootstrap.min.css’) }}” rel=”stylesheet”> <script type=”text/javascript” src=”{{ url_for(‘static’,filename=’fontawesome.min.js’) }}”></script> |
Web Page front-end
<div class=”form-signin”> <img class=”mb-4″ src=”{{ url_for(‘static’, filename=’SUSI.AI_Icon_2017a.svg’) }}” alt=”” width=”256″ height=”256″> {SUSI.AI Icon} <h1 class=”h3 mb-3 font-weight-normal”>Smart Speaker Control</h1> <div class=”form-group”> <fieldset class=”the-fieldset”> <legend class=”text-left w-auto”>Volume Control</legend> <span class=”font-weight-bold”>0</span> <i class=”fas fa-volume-down”></i> <input id=”vol-control” class=”slider” type=”range” min=”0″ max=”100″ value=”100″ step=”1″ oninput=”SetVolume(this.value)” onchange=”SetVolume(this.value)”></input> {Volume Control Slider} <i class=”fas fa-volume-up”></i> <span class=”font-weight-bold”>100</span> </fieldset> <fieldset class=”the-fieldset”> <legend class=”text-left w-auto”>Playback Control</legend> <button onclick=”control(‘pause’)” class=”btn btn-outline-primary m-2″> Pause <i class=”fas fa-pause”></i> </button> {pause control button} {similar to the pause button other required buttons are added} </fieldset> <button onclick=”window.location.href = ‘/set_password’;” class=”btn btn-warning m-2″> Set or Change Password <i class=”fa fa-key”></i> </button> </div> </div> |
Sending Response to Server
Since this is a control webpage, on sending of a response, the webpage should not reload. To accomplish this all the buttons point to a javascript function which then sends out an HTTP POST request to the server. For this purpose XMLHttpRequest Object is used. The XMLHttpRequest object is used to exchange data with a web server behind the scenes.
Here the SetVolume function is used to send a request to the /volume endpoint which is used to control the volume of the device. The control function is used to send a post request to audio control endpoints such as /pause /stop /shuffle etc.
function control(action){ console.log(action) var http = new XMLHttpRequest(); var url = ‘/’+action; http.open(‘POST’, url, true); http.send(); } function SetVolume(val){ console.log(val) var http = new XMLHttpRequest(); var url = ‘/volume?val=’+val; http.open(‘POST’, url, true); http.send(); } |
Features
The endpoints on the server provide the different audio control features via the vlc player. The endpoints used are listed below –
Play
The play functionality currently is only used directly via the busy state. It currently supports playback via youtube URL or MRLs.
To play using an MRL(Media Resource Locator) the request URL should have an argument called MRL with the needed MRL value. This also supports multiple semicolon ‘ ; ‘ separated MRLs in a single request.
Example Request URL: http://127.0.0.1:7070/play?mrl=/home/user/Desktop/song1.mp3;/home/user/Desktop/song2.mp3
@app.route(‘/play’, methods=[‘POST’, ‘PUT’]) def play_route(): if ‘ytb’ in request.args: vlcplayer.playytb(request.args.get(‘ytb’)) return do_return(‘Ok’, 200) elif ‘mrl’ in request.args: vlcplayer.play(request.args.get(‘mrl’)) return do_return(‘Ok’, 200) else: return do_return(‘Unknown play mode’, 400) |
Stop, Next, Previous, Pause and Resume
Stop, next and previous use the inbuilt methods of the MediaListPlayer class and are implemented in the same way. The request type must be POST and the request URL doesn’t require any arguments.
@app.route(‘/stop’, methods=[‘POST’, ‘PUT’]) def stop_route(): vlcplayer.stop() return do_return(‘Ok’, 200) |
Pause and Resume are also implemented in the same way but both of these use the same method pause of the MediaListPlayer class as that method acts as a toggle.
Shuffle
The shuffle endpoint shuffles the currently playing song list. It uses the shuffle method of the random library to shuffle the list containing MRLs of all the songs and then initiates a new MediaListPlayer object for playback. The Request URL doesn’t need any arguments.
def shuffle(self): if self.is_playing(): self.list_player.stop() random.shuffle(self.mrl) media_list = self.instance.media_list_new(self.mrl) self.list_player.set_media_list(media_list) self.list_player.play() self.softvolume(100, self.player) |
Restart
The restart endpoint is used to restart the currently playing audio. It does so by going back in the playlist and playing the current audio again. The Request URL doesn’t need any arguments.
@app.route(‘/restart’, methods=[‘POST’, ‘PUT’]) def restart_route(): vlcplayer.restart() return do_return(‘Ok’, 200) |
Volume
The Volume endpoint is used to set the volume of the device, The volume control slider uses this endpoint. A single argument val is needed in the URL of the POST request. Val can have a value ranging from 0 to 100, where 0 means mute and 100 means full volume.
@app.route(‘/volume’, methods=[‘POST’, ‘PUT’]) def volume_route(): try: vlcplayer.volume(request.args.get(‘val’)) return do_return(‘Ok’, 200) except Exception as e: logger.error(e) return do_return(‘Volume adjustment error’ + e, 400) |
Resources
Javascript XML HTTP Requests – https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
Flask HTML Templates –
https://pythonhow.com/html-templates-in-flask/
Tags
SUSI Smart Speaker, SUSI.AI, FOSSASIA, GSoC19