hivemind webchat
|
Before Width: | Height: | Size: 14 KiB |
186
README.md
@@ -1,190 +1,32 @@
|
||||
## Mycroft Webcaht Client and Bluetooth presence service for Mycroft
|
||||
Mycroft Webchat Client and Mycroft bluetooth service getting the MAC address of a Bluetooth device.
|
||||
# HiveMind - WebChat Terminal
|
||||
|
||||
Mycroft Webchat Terminal - Connecting to the HiveMind with javascript reference implementation
|
||||
|
||||
## Requirements
|
||||
This uses tornado to serve the webchat
|
||||
|
||||
This code requires the bluetooth module to be installed. On Mark1, Picroft or Ubuntu/Debian systems, this can usually be done with the following commands:
|
||||

|
||||
|
||||
sudo apt-get install bluetooth bluez blueman
|
||||
|
||||
sudo reboot now
|
||||
## Usage
|
||||
|
||||
For use with Mark1 or if you have problem with Picroft you need a [USB Bluetooth dongle ](https://www.amazon.com/Bluetooth-Dongle-Adapter-Raspberry-Windows/dp/B073H4GQ9Q/ref=sr_1_8?ie=UTF8&qid=1531953940&sr=8-8&keywords=raspberry+pi+3+usb+bluetooth+dongle) like this
|
||||

|
||||
change hivemind settings at ```tornado_webchat/static/js/app.js```
|
||||
|
||||
run ```python tornado_webchat/__main__.py```
|
||||
|
||||
## Installation
|
||||
Access from web browser http://your_ip_address:9090
|
||||
|
||||
For all installations
|
||||
## Privacy
|
||||
|
||||
cd /opt/mycroft
|
||||
Securing tornado is out of scope for this repo, it is currently served by HTTP, you probably want to set up nginx or equivalent with [let's encrypt](https://letsencrypt.org/) certificates
|
||||
|
||||
if the installation is in Mark1 or Picroft
|
||||
Hivemind connection is http by default! This is because browsers reject self signed certs, you probably want to use [let's encrypt](https://letsencrypt.org/) certificates and configure [HiveMind-core](https://github.com/OpenJarbas/HiveMind-core) to use them
|
||||
|
||||
sudo su mycroft
|
||||
Hivemind Encryption is not supported yet (don't set crypto key in [HiveMind-core](https://github.com/OpenJarbas/HiveMind-core))
|
||||
|
||||
For all installations
|
||||
|
||||
git clone https://github.com/jcasoft/external-services.git
|
||||
|
||||
if the installation is in Mark1 or Picroft
|
||||
|
||||
exit
|
||||
|
||||
For all installations
|
||||
|
||||
sudo chmod -R a+rwx /opt/mycroft/external-services
|
||||
|
||||
|
||||
## Installation of USB bluetooth dongle on Mark1 or if you have problem with Picroft
|
||||
|
||||
Plug the USB bluetooth dongle
|
||||
|
||||
Mark1 command line
|
||||
|
||||
hciconfig -a hci1
|
||||
|
||||
sudo hciconfig hci1 up
|
||||
|
||||
sudo reboot now
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
Locate your configuration file and add the section detailed belove
|
||||
|
||||
Linux
|
||||
|
||||
nano ~/.mycroft/mycroft.conf
|
||||
|
||||
Mark1 or Picroft
|
||||
|
||||
nano /home/pi/.mycroft/mycroft.conf
|
||||
|
||||
and
|
||||
|
||||
sudo su mycroft
|
||||
nano /home/mycroft/.mycroft/mycroft.conf
|
||||
exit
|
||||
|
||||
|
||||
Add this section to mycroft.conf
|
||||
|
||||
"Proximity": {
|
||||
"proximity_enabled": true,
|
||||
"proximity_data": [
|
||||
{"bt_id": "00:00:00:00:00:1A","name": "Juan Carlos"},
|
||||
{"bt_id": "00:00:00:00:00:1B","name": "Adriana"},
|
||||
{"bt_id": "00:00:00:00:00:1C","name": "Daniella"},
|
||||
{"bt_id": "00:00:00:00:00:1D","name": "Enzo"},
|
||||
{"bt_id": "00:00:00:00:00:1E","name": "Gianluca"}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Fill the bt_id with bluetooth MAC address of your smartphone
|
||||
|
||||

|
||||
|
||||
## How to start Presence service
|
||||
|
||||
Linux
|
||||
|
||||
cd /opt/mycroft/external-services
|
||||
bash start-presence.sh linux
|
||||
|
||||
cd ~/mycroft-core
|
||||
bash start-mycroft.sh debug
|
||||
|
||||
|
||||
Mark1 or Picroft
|
||||
|
||||
cd /opt/mycroft/external-services
|
||||
bash start-presence.sh mark1
|
||||
or
|
||||
bash start-presence.sh picroft
|
||||
|
||||
|
||||
## How to stop Presence service
|
||||
|
||||
Linux, Mark1 and Picroft
|
||||
|
||||
cd /opt/mycroft/external-services
|
||||
bash stop-presence.sh
|
||||
|
||||
|
||||
## How to start Webchat service
|
||||
|
||||
Linux
|
||||
|
||||
cd /opt/mycroft/external-services
|
||||
bash start-webchat.sh linux
|
||||
|
||||
cd ~/mycroft-core
|
||||
bash start-mycroft.sh debug
|
||||
|
||||
|
||||
Mark1 or Picroft
|
||||
|
||||
cd /opt/mycroft/external-services
|
||||
bash start-webchat.sh mark1
|
||||
or
|
||||
bash start-webchat.sh picroft
|
||||
|
||||
|
||||
## How to stop Webchat service
|
||||
|
||||
Linux, Mark1 and Picroft
|
||||
|
||||
cd /opt/mycroft/external-services
|
||||
bash stop-webchat.sh
|
||||
|
||||
|
||||
## How to start both service
|
||||
|
||||
Linux
|
||||
|
||||
cd /opt/mycroft/external-services
|
||||
bash start-all.sh linux
|
||||
|
||||
cd ~/mycroft-core
|
||||
bash start-mycroft.sh debug
|
||||
|
||||
|
||||
Mark1 or Picroft
|
||||
cd /opt/mycroft/external-services
|
||||
|
||||
bash start-all.sh mark1
|
||||
or
|
||||
bash start-all.sh picroft
|
||||
|
||||
|
||||
## How to stop both services
|
||||
|
||||
Linux, Mark1 and Picroft
|
||||
|
||||
cd /opt/mycroft/external-services
|
||||
bash stop-all.sh
|
||||
|
||||
## How to access Web chat from your browser
|
||||
|
||||
http://localhost:9090
|
||||
or
|
||||
http://MYCROFT-IP-ADDRESS:9090
|
||||
|
||||
|
||||
## How to use Webcaht Client and Bluetooth presence service
|
||||
[](https://www.youtube.com/watch?v=8cRSmOTRSBI)
|
||||
|
||||
|
||||
## Notes
|
||||
Give me a Github star and follow me on YouTube !, to continue developing and improving skills and services in Mycroft
|
||||
Basically this is unsafe to run outside your home network with default configurations
|
||||
|
||||
## Credits
|
||||
|
||||
Author: jcasoft
|
||||
Juan Carlos Argueta
|
||||
Original Webchat UI: [jcasoft](https://github.com/jcasoft/external-services)
|
||||
|
||||
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
# Copyright 2017 Mycroft AI Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
import random
|
||||
from io import open
|
||||
# from os.path import join, expanduser
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from mycroft.util.log import LOG
|
||||
|
||||
|
||||
__doc__ = """
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class MustacheDialogRenderer(object):
|
||||
"""
|
||||
A dialog template renderer based on the mustache templating language.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.templates = {}
|
||||
|
||||
def load_template_file(self, template_name, filename):
|
||||
"""
|
||||
Load a template by file name into the templates cache.
|
||||
|
||||
Args:
|
||||
template_name (str): a unique identifier for a group of templates
|
||||
filename (str): a fully qualified filename of a mustache template.
|
||||
"""
|
||||
with open(filename, 'r') as f:
|
||||
for line in f:
|
||||
template_text = line.strip()
|
||||
if template_name not in self.templates:
|
||||
self.templates[template_name] = []
|
||||
|
||||
# convert to standard python format string syntax. From
|
||||
# double (or more) '{' followed by any number of whitespace
|
||||
# followed by actual key followed by any number of whitespace
|
||||
# followed by double (or more) '}'
|
||||
template_text = re.sub('\{\{+\s*(.*?)\s*\}\}+', r'{\1}',
|
||||
template_text)
|
||||
|
||||
self.templates[template_name].append(template_text)
|
||||
|
||||
def render(self, template_name, context=None, index=None):
|
||||
"""
|
||||
Given a template name, pick a template and render it using the context
|
||||
|
||||
Args:
|
||||
template_name (str): the name of a template group.
|
||||
context (dict): dictionary representing values to be rendered
|
||||
index (int): optional, the specific index in the collection of
|
||||
templates
|
||||
|
||||
Returns:
|
||||
str: the rendered string
|
||||
|
||||
Raises:
|
||||
NotImplementedError: if no template can be found identified by
|
||||
template_name
|
||||
"""
|
||||
context = context or {}
|
||||
if template_name not in self.templates:
|
||||
raise NotImplementedError("Template not found: %s" % template_name)
|
||||
template_functions = self.templates.get(template_name)
|
||||
if index is None:
|
||||
index = random.randrange(len(template_functions))
|
||||
else:
|
||||
index %= len(template_functions)
|
||||
line = template_functions[index]
|
||||
line = line.format(**context)
|
||||
return line
|
||||
|
||||
|
||||
class DialogLoader(object):
|
||||
"""
|
||||
Loads a collection of dialog files into a renderer implementation.
|
||||
"""
|
||||
|
||||
def __init__(self, renderer_factory=MustacheDialogRenderer):
|
||||
self.__renderer = renderer_factory()
|
||||
|
||||
def load(self, dialog_dir):
|
||||
"""
|
||||
Load all dialog files within the specified directory.
|
||||
|
||||
Args:
|
||||
dialog_dir (str): directory that contains dialog files
|
||||
|
||||
Returns:
|
||||
a loaded instance of a dialog renderer
|
||||
"""
|
||||
if not os.path.exists(dialog_dir) or not os.path.isdir(dialog_dir):
|
||||
LOG.warning("No dialog found: " + dialog_dir)
|
||||
return self.__renderer
|
||||
|
||||
for f in sorted(
|
||||
filter(lambda x: os.path.isfile(
|
||||
os.path.join(dialog_dir, x)), os.listdir(dialog_dir))):
|
||||
dialog_entry_name = os.path.splitext(f)[0]
|
||||
self.__renderer.load_template_file(
|
||||
dialog_entry_name, os.path.join(dialog_dir, f))
|
||||
|
||||
return self.__renderer
|
||||
|
||||
|
||||
def get(phrase, lang=None, context=None):
|
||||
"""
|
||||
Looks up a resource file for the given phrase. If no file
|
||||
is found, the requested phrase is returned as the string.
|
||||
This will use the default language for translations.
|
||||
|
||||
Args:
|
||||
phrase (str): resource phrase to retrieve/translate
|
||||
lang (str): the language to use
|
||||
context (dict): values to be inserted into the string
|
||||
|
||||
Returns:
|
||||
str: a randomized and/or translated version of the phrase
|
||||
"""
|
||||
|
||||
if not lang:
|
||||
from mycroft.configuration import Configuration
|
||||
lang = Configuration.get().get("lang")
|
||||
|
||||
filename = "res/text/" + lang.lower() + "/" + phrase + ".dialog"
|
||||
#template = resolve_resource_file(filename)
|
||||
template = filename
|
||||
if not template:
|
||||
LOG.debug("Resource file not found: " + filename)
|
||||
return phrase
|
||||
|
||||
stache = MustacheDialogRenderer()
|
||||
stache.load_template_file("template", template)
|
||||
if not context:
|
||||
context = {}
|
||||
return stache.render("template", context)
|
||||
|
||||
157
presence.py
@@ -1,157 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Developed by: jcasoft
|
||||
# Juan Carlos Argueta
|
||||
#
|
||||
|
||||
|
||||
import sys
|
||||
import os, time, platform
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
import dialog
|
||||
from mycroft.configuration import Configuration
|
||||
from mycroft.messagebus.client.ws import WebsocketClient
|
||||
from mycroft.messagebus.message import Message
|
||||
from mycroft.util.log import LOG
|
||||
from mycroft.util import create_daemon, wait_for_exit_signal
|
||||
|
||||
|
||||
__author__ = 'jcasoft'
|
||||
|
||||
|
||||
# ----- Variables for Websocket
|
||||
configWS = Configuration.get().get('websocket')
|
||||
host = configWS.get('host')
|
||||
port = configWS.get('port')
|
||||
route = configWS.get('route')
|
||||
ssl = configWS.get('ssl')
|
||||
|
||||
# ----- Variables for Proximity
|
||||
configPr = Configuration.get().get('Proximity')
|
||||
proximity_enabled = configPr.get('proximity_enabled')
|
||||
proximity_data_array = configPr.get('proximity_data')
|
||||
|
||||
# ----- Variables for Language
|
||||
lang = Configuration.get().get("lang")
|
||||
|
||||
|
||||
ws = None
|
||||
|
||||
scanner_id = os.popen('uname -n').readline().strip()
|
||||
|
||||
devices_here = {}
|
||||
|
||||
AWAY_HEURISTIC = 10 # *** Can not be less than 10
|
||||
|
||||
|
||||
# *** Get all nearby IDs
|
||||
def get_bt_ids():
|
||||
sys = platform.system()
|
||||
ids = []
|
||||
|
||||
f = os.popen('hcitool inq --flush')
|
||||
unparsed_data = f.readlines()[1:]
|
||||
for u in unparsed_data:
|
||||
id = u.split()[0]
|
||||
ids.append(id)
|
||||
|
||||
return ids
|
||||
|
||||
# *** Scan the area for bluetooth devices. If a new device is seen, send data to messagebus.
|
||||
def scan():
|
||||
time = datetime.now()
|
||||
ids = get_bt_ids()
|
||||
|
||||
if (sys.version_info > (3, 0)):
|
||||
# Python 3 code in this block
|
||||
for id in ids:
|
||||
if not id in devices_here:
|
||||
send_to_messagebus({'time': time, 'device_id': id, 'scanner_id': scanner_id, 'event_type': 'ENTER'})
|
||||
devices_here[id] = time
|
||||
else:
|
||||
# Python 2 code in this block
|
||||
for id in ids:
|
||||
if not devices_here.has_key(id):
|
||||
send_to_messagebus({'time': time, 'device_id': id, 'scanner_id': scanner_id, 'event_type': 'ENTER'})
|
||||
devices_here[id] = time
|
||||
|
||||
# *** Clean up the list of nearby devices
|
||||
def cleanup():
|
||||
del_keys = []
|
||||
time = datetime.now()
|
||||
for device in devices_here:
|
||||
last_seen = devices_here[device]
|
||||
if (time - last_seen).seconds > AWAY_HEURISTIC:
|
||||
del_keys.append(device)
|
||||
send_to_messagebus({'time': time, 'device_id': device, 'scanner_id': scanner_id, 'event_type': 'EXIT'})
|
||||
|
||||
for k in del_keys:
|
||||
del devices_here[k]
|
||||
|
||||
def send_to_messagebus(params):
|
||||
LOG.debug('***** %s: device %s at time %s' % (params['event_type'], params['device_id'], params['time']))
|
||||
currentTime = datetime.now()
|
||||
currentTime.hour
|
||||
|
||||
dialog_name = 'greeting afternoon'
|
||||
if params['event_type'] == "ENTER":
|
||||
if currentTime.hour < 12:
|
||||
dialog_name = 'greeting morning'
|
||||
elif 12 <= currentTime.hour < 19:
|
||||
dialog_name = 'greeting afternoon'
|
||||
else:
|
||||
dialog_name = 'greeting night'
|
||||
elif params['event_type'] == "EXIT":
|
||||
dialog_name = 'bye bye'
|
||||
|
||||
name = ""
|
||||
found = False
|
||||
for x in range(0,len(proximity_data_array)):
|
||||
if ("bt_id",params['device_id']) in proximity_data_array[x].items():
|
||||
name = proximity_data_array[x]["name"]
|
||||
LOG.debug("***** Devide found, Owner " + name)
|
||||
found = True
|
||||
break
|
||||
else:
|
||||
found = False
|
||||
|
||||
if found:
|
||||
msg_data = {'name': name}
|
||||
payload = {'utterance':dialog.get(dialog_name, lang, msg_data)}
|
||||
ws.emit(Message("speak", payload))
|
||||
else:
|
||||
name = ""
|
||||
LOG.debug("***** Devide not found")
|
||||
msg_data = {'name': name}
|
||||
payload = {'utterance':dialog.get(dialog_name, lang, msg_data)}
|
||||
ws.emit(Message("speak", payload))
|
||||
|
||||
|
||||
|
||||
def BT_Scanner():
|
||||
LOG.debug("***** Proximity Bluetooth Enabled --> : Scanning Bluetooth devices... ")
|
||||
while True:
|
||||
# continuously scan for new devices
|
||||
scan()
|
||||
# and clean up the list of devices that are present
|
||||
cleanup()
|
||||
|
||||
def main():
|
||||
global ws
|
||||
ws = WebsocketClient()
|
||||
Configuration.init(ws)
|
||||
|
||||
create_daemon(ws.run_forever)
|
||||
|
||||
if proximity_enabled:
|
||||
BT_Scanner()
|
||||
|
||||
wait_for_exit_signal()
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
tornado
|
||||
@@ -1,3 +0,0 @@
|
||||
Bye bye {{name}}, see you later
|
||||
Bye bye {{name}}, nice to see you
|
||||
{{name}} hasta la vista baby!
|
||||
@@ -1,3 +0,0 @@
|
||||
Hello {{name}}, how are you
|
||||
Hello {{name}}, nice to see you
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Good morning {{name}}, how are you
|
||||
Hello {{name}}, Good morning!
|
||||
@@ -1,3 +0,0 @@
|
||||
Good night {{name}}, how was your day?
|
||||
Good night {{name}}
|
||||
Hello {{name}}, Good night!
|
||||
@@ -1,3 +0,0 @@
|
||||
Adios{{name}}
|
||||
Hasta luego {{name}}
|
||||
{{name}}, espero verte pronto
|
||||
@@ -1,3 +0,0 @@
|
||||
Hola {{name}} como estas
|
||||
Hola {{name}} que gusto verte
|
||||
Hola {{name}} que tal tu día
|
||||
@@ -1,3 +0,0 @@
|
||||
Buenos días {{name}}, como estas?
|
||||
Buenos días {{name}}, como te encuentras?
|
||||
Hola {{name}}, buenos dias!
|
||||
@@ -1,3 +0,0 @@
|
||||
Buenos noches {{name}}, como estuvo tu día?
|
||||
Buenos noches {{name}}, que tal tu día?
|
||||
Hola {{name}}, buenas noches!
|
||||
18
start-all.sh
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$1" = "mark1" ] || [ "$1" = "picroft" ]
|
||||
then
|
||||
echo "Selected Mark1 or Picroft"
|
||||
source /opt/venvs/mycroft-core/bin/activate
|
||||
python /opt/mycroft/external-services/presence.py > presence.log &
|
||||
python /opt/mycroft/external-services/webchat.py > webchat.log &
|
||||
elif [ "$1" = "linux" ]
|
||||
then
|
||||
echo "Selected mycroft-core"
|
||||
source ~/mycroft-core/.venv/bin/activate
|
||||
python -u /opt/mycroft/external-services/presence.py > presence.log &
|
||||
python -u /opt/mycroft/external-services/webchat.py > webchat.log &
|
||||
else
|
||||
echo "Usage: ./start-all.sh mark1, ./start-all.sh picroft or ./start-all.sh linux"
|
||||
fi
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if ! dpkg-query -s bluetooth 1> /dev/null 2>&1 ; then
|
||||
echo "Package wireless-tools is not currently installed."
|
||||
echo "--------------------------------------------------"
|
||||
echo "Install Bluetooth with: sudo apt-get install bluetooth"
|
||||
echo "Reboot after install"
|
||||
exit 1
|
||||
else
|
||||
echo "Package Bluetooth is currently installed."
|
||||
fi
|
||||
|
||||
|
||||
if [ "$1" = "mark1" ] || [ "$1" = "picroft" ]
|
||||
then
|
||||
echo "Selected Mark1 or Picroft"
|
||||
source /opt/venvs/mycroft-core/bin/activate
|
||||
python /opt/mycroft/external-services/presence.py > presence.log &
|
||||
elif [ "$1" = "linux" ]
|
||||
then
|
||||
echo "Selected mycroft-core"
|
||||
source ~/mycroft-core/.venv/bin/activate
|
||||
python -u /opt/mycroft/external-services/presence.py > presence.log &
|
||||
else
|
||||
echo "Usage: ./start-presence.sh mark1, ./start-presence.sh picroft or ./start-presence.sh linux"
|
||||
fi
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$1" = "mark1" ] || [ "$1" = "picroft" ]
|
||||
then
|
||||
echo "Selected Mark1 or Picroft"
|
||||
source /opt/venvs/mycroft-core/bin/activate
|
||||
python /opt/mycroft/external-services/webchat.py > webchat.log &
|
||||
elif [ "$1" = "linux" ]
|
||||
then
|
||||
echo "Selected mycroft-core"
|
||||
source ~/mycroft-core/.venv/bin/activate
|
||||
python -u /opt/mycroft/external-services/webchat.py > webchat.log &
|
||||
else
|
||||
echo "Usage: ./start-webchat.sh mark1, ./start-webchat.sh picroft or ./start-webchat.sh linux"
|
||||
fi
|
||||
|
||||
209
static/js/app.js
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
-------------------------------------------------------------
|
||||
Author: jcasoft - Juan Carlos Argueta
|
||||
Web Chat Client for Mycroft
|
||||
|
||||
to do:
|
||||
reload or refresh
|
||||
mic_on
|
||||
mic_off
|
||||
mute
|
||||
vol_up
|
||||
vol_down
|
||||
backward
|
||||
forward
|
||||
play
|
||||
pause
|
||||
|
||||
-------------------------------------------------------------
|
||||
*/
|
||||
|
||||
var ip = ""
|
||||
var por = ""
|
||||
|
||||
|
||||
function set_ip(val)
|
||||
{
|
||||
ip_alt=val
|
||||
console.log("Local computer IP="+ ip_alt)
|
||||
}
|
||||
|
||||
function set_port(val)
|
||||
{
|
||||
port_alt=val
|
||||
console.log("Socket Port="+ port_alt)
|
||||
}
|
||||
|
||||
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
var received = $('#received');
|
||||
|
||||
var parser = document.createElement('a');
|
||||
parser.href = document.URL;
|
||||
ip = parser.hostname;
|
||||
port = parser.port;
|
||||
|
||||
// Define Socket with local computer IP
|
||||
var socket = new WebSocket("ws://"+ip+":"+port+"/ws");
|
||||
console.log("ws://"+ip+":"+port+"/ws")
|
||||
|
||||
|
||||
socket.onopen = function(){
|
||||
console.log("connected");
|
||||
push_response('Web Chat Client by JCASOFT')
|
||||
};
|
||||
|
||||
socket.onmessage = function (message) {
|
||||
console.log("receiving: " + message.data);
|
||||
received.append(message.data);
|
||||
push_response(message.data)
|
||||
|
||||
};
|
||||
|
||||
socket.onclose = function(){
|
||||
console.log("disconnected");
|
||||
};
|
||||
|
||||
var sendMessage = function(message) {
|
||||
console.log("sending:" + message.data);
|
||||
socket.send(message.data);
|
||||
};
|
||||
|
||||
|
||||
|
||||
$('.chat[data-chat=person2]').addClass('active-chat')
|
||||
$('.person[data-chat=person2]').addClass('active')
|
||||
$('.left .person').mousedown(function () {
|
||||
if ($(this).hasClass('.active')) {
|
||||
return false
|
||||
}
|
||||
const findChat = $(this).attr('data-chat')
|
||||
const personName = $(this).find('.name').text()
|
||||
$('.right .top .name').html(personName)
|
||||
$('.chat').removeClass('active-chat')
|
||||
$('.left .person').removeClass('active')
|
||||
$(this).addClass('active')
|
||||
$('.chat[data-chat = ' + findChat + ']').addClass('active-chat')
|
||||
})
|
||||
|
||||
|
||||
function push_statment(msg) {
|
||||
$('.chat').append('<div class="bubble me"><i class="fa fa-user-circle" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
}
|
||||
|
||||
|
||||
function push_response(msg, callback) {
|
||||
if (msg == 'stop') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-window-close" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'reload') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-refresh" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'play') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-play" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'pause') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-pause" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'mute') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-volume-off" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'vol_up') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-volume-up" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'vol_down') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-volume-down" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'mic_on') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-microphone" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'mic_off') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-microphone-slash" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'forward') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-forward" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'backward') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-backward" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else if (msg == 'Web Chat Client from JCASOFT') {
|
||||
$('.chat').append('<div class="bubble you loading"><i class="fa fa-thumbs-o-up" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
} else {
|
||||
$('.chat').append('<div class="bubble you"><i class="fa fa-commenting" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
}
|
||||
}
|
||||
|
||||
function get_resp(q, is_response = false) {
|
||||
let query = q
|
||||
console.log("*****Utterance to send to main.py server="+query) // Send to tornado and wait for response
|
||||
sendMessage({ 'data' : query});
|
||||
|
||||
}
|
||||
|
||||
$('#stop').click(function(){
|
||||
sendMessage({ 'data' : 'stop'});
|
||||
push_response('stop')
|
||||
});
|
||||
|
||||
$('#pause').click(function(){
|
||||
sendMessage({ 'data' : 'pause'});
|
||||
push_response('pause')
|
||||
});
|
||||
|
||||
$('#play').click(function(){
|
||||
sendMessage({ 'data' : 'play'});
|
||||
push_response('play')
|
||||
});
|
||||
|
||||
$('#next_track').click(function(){
|
||||
sendMessage({ 'data' : 'next track'});
|
||||
push_response('next track')
|
||||
});
|
||||
|
||||
$('#previous_track').click(function(){
|
||||
sendMessage({ 'data' : 'previous track'});
|
||||
push_response('previous track')
|
||||
});
|
||||
|
||||
$('#reduce_volume').click(function(){
|
||||
sendMessage({ 'data' : 'reduce volume'});
|
||||
push_response('reduce volume')
|
||||
});
|
||||
|
||||
$('#increase_volume').click(function(){
|
||||
sendMessage({ 'data' : 'increase volume'});
|
||||
push_response('increase volume')
|
||||
});
|
||||
|
||||
$('#mute_volume').click(function(){
|
||||
sendMessage({ 'data' : 'mute volume'});
|
||||
push_response('mute volume')
|
||||
});
|
||||
|
||||
$('#mic_on').click(function(){
|
||||
sendMessage({ 'data' : 'mic_on'});
|
||||
push_response('start listening on Mycroft mic')
|
||||
});
|
||||
|
||||
$('#mic_off').click(function(){
|
||||
sendMessage({ 'data' : 'mic_off'});
|
||||
push_response('mic off')
|
||||
});
|
||||
|
||||
$('#reload').click(function(){
|
||||
sendMessage({ 'data' : 'reload'});
|
||||
push_response('reload')
|
||||
});
|
||||
|
||||
|
||||
$('#textbox_submit').click(function(){
|
||||
$(this).blur()
|
||||
$('.chat').append('<div class="bubble me"><i class="fa fa-user-circle" aria-hidden="true"></i> <i class="fa fa-volume-up"></i> ' + $('#textbox').val() + '</div>')
|
||||
get_resp($('#textbox').val())
|
||||
document.getElementById('textbox').value = ''
|
||||
return false
|
||||
})
|
||||
|
||||
|
||||
$('#textbox').keypress(function (e) {
|
||||
if (e.which == 13) {
|
||||
$(this).blur()
|
||||
push_statment($('#textbox').val())
|
||||
get_resp($('#textbox').val()+"|SILENT")
|
||||
document.getElementById('textbox').value = ''
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
pkill -f "presence.py"
|
||||
pkill -f "webchat.py"
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
pkill -f "presence.py"
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
pkill -f "webchat.py"
|
||||
50
tornado_webchat/__init__.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import tornado.httpserver
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import os.path
|
||||
import os
|
||||
import tornado.websocket
|
||||
import tornado.options
|
||||
import threading
|
||||
import asyncio
|
||||
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
self.render('index.html')
|
||||
|
||||
|
||||
class StaticFileHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
self.render('js/app.js')
|
||||
|
||||
|
||||
class WebChat(threading.Thread):
|
||||
def __init__(self, port, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.port = port
|
||||
|
||||
def run(self):
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
|
||||
routes = [
|
||||
tornado.web.url(r"/", MainHandler, name="main"),
|
||||
tornado.web.url(r"/static/(.*)", tornado.web.StaticFileHandler,
|
||||
{'path': './'})
|
||||
]
|
||||
|
||||
settings = {
|
||||
"debug": True,
|
||||
"template_path": os.path.join(os.path.dirname(__file__),
|
||||
"templates"),
|
||||
"static_path": os.path.join(os.path.dirname(__file__), "static"),
|
||||
}
|
||||
|
||||
application = tornado.web.Application(routes, **settings)
|
||||
httpServer = tornado.httpserver.HTTPServer(application)
|
||||
tornado.options.parse_command_line()
|
||||
httpServer.listen(self.port)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
|
||||
def stop(self):
|
||||
tornado.ioloop.IOLoop.instance().stop()
|
||||
45
tornado_webchat/__main__.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import socket
|
||||
import sys
|
||||
from time import sleep
|
||||
from tornado_webchat import WebChat
|
||||
|
||||
|
||||
def get_ip():
|
||||
return [l for l in (
|
||||
[ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if
|
||||
not ip.startswith("127.")][:1], [
|
||||
[(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s
|
||||
in
|
||||
[socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][
|
||||
0][0]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# TODO argpase
|
||||
|
||||
import tornado.options
|
||||
|
||||
tornado.options.parse_command_line()
|
||||
port = 9090
|
||||
|
||||
# TODO pass hivemind options
|
||||
# currently defined in tornado_webchat/static/js/app.js
|
||||
webchat = WebChat(port)
|
||||
|
||||
try:
|
||||
webchat.start()
|
||||
|
||||
ip = get_ip()
|
||||
url = "http://" + str(ip) + ":" + str(port)
|
||||
print("*********************************************************")
|
||||
print("* HiveMind WebChat Client ")
|
||||
print("*")
|
||||
print("* Access from web browser " + url)
|
||||
print("*********************************************************")
|
||||
|
||||
while True:
|
||||
sleep(5)
|
||||
except KeyboardInterrupt:
|
||||
webchat.stop()
|
||||
webchat.join()
|
||||
sys.exit()
|
||||
|
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 434 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
121
tornado_webchat/static/js/app.js
Executable file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
-------------------------------------------------------------
|
||||
Author: jcasoft - Juan Carlos Argueta
|
||||
Web Chat Client for Mycroft
|
||||
|
||||
Modified for the HiveMind by JarbasAI
|
||||
|
||||
-------------------------------------------------------------
|
||||
|
||||
*/
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
// HiveMind socket
|
||||
var user = "dummy_user";
|
||||
var key = "dummy_key";
|
||||
let ip = "0.0.0.0";
|
||||
let port = 5678;
|
||||
var authToken = btoa(user + ":" + key);
|
||||
|
||||
document.cookie = 'X-Authorization=' + authToken + '; path=/';
|
||||
|
||||
var ws = new WebSocket('ws://' + ip + ":" + port);
|
||||
|
||||
console.log('ws://' + ip + ":" + port);
|
||||
|
||||
|
||||
ws.onopen = function () {
|
||||
console.log("connected");
|
||||
push_response("Welcome to the HiveMind Webchat client!")
|
||||
};
|
||||
|
||||
ws.onmessage = function (message) {
|
||||
message = JSON.parse(message.data);
|
||||
|
||||
if (message.msg_type === "bus"){
|
||||
let mycroft_message =JSON.parse(message.payload);
|
||||
console.log("Received: " + mycroft_message.type);
|
||||
if (mycroft_message.type === "speak"){
|
||||
let utterance = mycroft_message.data.utterance;
|
||||
push_response(utterance)
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ws.onclose = function () {
|
||||
console.log("disconnected");
|
||||
};
|
||||
|
||||
var sendMessage = function (message) {
|
||||
// TODO hivemind message
|
||||
let hive_msg = message
|
||||
|
||||
console.log("sending:" + JSON.stringify(hive_msg));
|
||||
ws.send(JSON.stringify(message));
|
||||
};
|
||||
|
||||
|
||||
$('.chat[data-chat=person2]').addClass('active-chat')
|
||||
$('.person[data-chat=person2]').addClass('active')
|
||||
$('.left .person').mousedown(function () {
|
||||
if ($(this).hasClass('.active')) {
|
||||
return false
|
||||
}
|
||||
const findChat = $(this).attr('data-chat')
|
||||
const personName = $(this).find('.name').text()
|
||||
$('.right .top .name').html(personName)
|
||||
$('.chat').removeClass('active-chat')
|
||||
$('.left .person').removeClass('active')
|
||||
$(this).addClass('active')
|
||||
$('.chat[data-chat = ' + findChat + ']').addClass('active-chat')
|
||||
});
|
||||
|
||||
|
||||
function push_statment(msg) {
|
||||
$('.chat').append('<div class="bubble me"><i class="fa fa-user-circle" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
}
|
||||
|
||||
|
||||
function push_response(msg) {
|
||||
$('.chat').append('<div class="bubble you"><i class="fa fa-commenting" aria-hidden="true"></i> ' + msg + '</div>')
|
||||
|
||||
}
|
||||
|
||||
function get_resp(q) {
|
||||
let payload = {
|
||||
'type': "recognizer_loop:utterance",
|
||||
"data":{"utterances": [q]},
|
||||
"context": {"source": "WebChat", "destination": "HiveMind", "platform": "JarbasWebchatTerminalV0.1"}
|
||||
};
|
||||
sendMessage({
|
||||
'msg_type': "bus",
|
||||
"payload": payload,
|
||||
"context": {"source": "WebChat", "destination": "HiveMind", "platform": "JarbasWebchatTerminalV0.1"}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
$('#textbox_submit').click(function () {
|
||||
$(this).blur()
|
||||
$('.chat').append('<div class="bubble me"><i class="fa fa-user-circle" aria-hidden="true"></i> <i class="fa fa-volume-up"></i> ' + $('#textbox').val() + '</div>')
|
||||
get_resp($('#textbox').val())
|
||||
document.getElementById('textbox').value = ''
|
||||
return false
|
||||
})
|
||||
|
||||
|
||||
$('#textbox').keypress(function (e) {
|
||||
if (e.which == 13) {
|
||||
$(this).blur()
|
||||
push_statment($('#textbox').val())
|
||||
get_resp($('#textbox').val())
|
||||
document.getElementById('textbox').value = ''
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
@@ -3,17 +3,7 @@
|
||||
Author: jcasoft - Juan Carlos Argueta
|
||||
Web Chat Client for Mycroft
|
||||
|
||||
to do:
|
||||
reload or refresh
|
||||
mic_on
|
||||
mic_off
|
||||
mute
|
||||
vol_up
|
||||
vol_down
|
||||
backward
|
||||
forward
|
||||
play
|
||||
pause
|
||||
Modified by JarbasAI
|
||||
|
||||
-------------------------------------------------------------
|
||||
-->
|
||||
@@ -29,10 +19,11 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="cleartype" content="on">
|
||||
|
||||
<link rel="stylesheet" href="/static/css/bootstrap.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="/static/css/todc-bootstrap.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="/static/css/app.css">
|
||||
<link rel="stylesheet" href="/static/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="static/css/bootstrap.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="static/css/todc-bootstrap.min.css"
|
||||
type="text/css">
|
||||
<link rel="stylesheet" href="static/css/app.css">
|
||||
<link rel="stylesheet" href="static/css/font-awesome.css">
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -50,27 +41,13 @@
|
||||
<div class="mycroft">
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="#"><img src="/static/img/Logo2.png" alt="Mycroft Logo">
|
||||
<a class="navbar-brand" href="#"><img src="static/img/Logo2.png" alt="Mycroft Logo">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav nav-pills navbar-right">
|
||||
<li class="active"><a href="#" id="mic_on"><i class="fa fa-microphone"></i></a></li>
|
||||
<li class="active"><a href="#" id="mute_volume"><i class="fa fa-volume-off"></i></a></li>
|
||||
<li class="active"><a href="#" id="increase_volume"><i class="fa fa-volume-up"></i></a></li>
|
||||
<li class="active"><a href="#" id="reduce_volume"><i class="fa fa-volume-down"></i></a></li>
|
||||
<li class="active"><a href="#" id="previous_track"><i class="fa fa-backward"></i></a></li>
|
||||
<li class="active"><a href="#" id="next_track"><i class="fa fa-forward"></i></a></li>
|
||||
<li class="active"><a href="#" id="play"><i class="fa fa-play"></i></a></li>
|
||||
<li class="active"><a href="#" id="pause"><i class="fa fa-pause"></i></a></li>
|
||||
<li class="active"><a href="#" id="stop" style="margin-right:25px;"><i class="fa fa-stop"></i></a></li>
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="container">
|
||||
<div class="right">
|
||||
<div class="chat" data-chat="person2">
|
||||
@@ -91,10 +68,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
<script src="/static/js/jquery-3.2.1.min.js"></script>
|
||||
<script src="/static/js/app.js"></script>
|
||||
<script type="application/javascript">set_ip("{{ip}}");</script>
|
||||
<script type="application/javascript">set_port("{{port}}");</script>
|
||||
<script src="static/js/jquery-3.2.1.min.js"></script>
|
||||
<script src="static/js/app.js"></script>
|
||||
<script type="text/javascript">
|
||||
document.getElementById('writer').style.display = 'block';
|
||||
</script>
|
||||
BIN
usb_donle.jpeg
|
Before Width: | Height: | Size: 3.1 KiB |
BIN
webchat.png
Normal file
|
After Width: | Height: | Size: 101 KiB |
186
webchat.py
@@ -1,186 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Developed by: jcasoft
|
||||
# Juan Carlos Argueta
|
||||
#
|
||||
|
||||
from mycroft.configuration import ConfigurationManager
|
||||
from mycroft.messagebus.service.ws import WebsocketEventHandler
|
||||
from mycroft.util import validate_param, create_signal
|
||||
|
||||
__author__ = 'jcasoft'
|
||||
|
||||
|
||||
from mycroft.messagebus.client.ws import WebsocketClient
|
||||
from mycroft.messagebus.message import Message
|
||||
from threading import Thread
|
||||
|
||||
ws = None
|
||||
|
||||
import tornado.httpserver
|
||||
import tornado.websocket
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import tornado.options
|
||||
import os.path
|
||||
from tornado.options import define, options
|
||||
import multiprocessing
|
||||
import json
|
||||
import time
|
||||
import os
|
||||
import socket
|
||||
from subprocess import check_output
|
||||
|
||||
global ip
|
||||
ip = [l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1], [[(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0][0]
|
||||
|
||||
clients = []
|
||||
|
||||
input_queue = multiprocessing.Queue()
|
||||
output_queue = multiprocessing.Queue()
|
||||
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
self.render('index.html', ip=ip, port=port)
|
||||
|
||||
|
||||
class StaticFileHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
self.render('js/app.js')
|
||||
|
||||
|
||||
class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
||||
def open(self):
|
||||
clients.append(self)
|
||||
self.write_message("Welcome to Mycroft")
|
||||
|
||||
def on_message(self, message):
|
||||
utterance = json.dumps(message)
|
||||
print("*****Utterance : ", utterance)
|
||||
|
||||
if utterance:
|
||||
if utterance == '"mic_on"':
|
||||
create_signal('startListening')
|
||||
else:
|
||||
if "|SILENT" in utterance:
|
||||
utterance = utterance.split("|")
|
||||
utterance = utterance[0]
|
||||
data = {
|
||||
"lang": lang,
|
||||
"session": "",
|
||||
"utterances": [utterance],
|
||||
"client": "WebChat"}
|
||||
ws.emit(Message('chat_response', data))
|
||||
ws.emit(Message('recognizer_loop:utterance', data))
|
||||
else:
|
||||
data = {
|
||||
"lang": lang,
|
||||
"session": "",
|
||||
"utterances": [utterance]}
|
||||
ws.emit(Message('recognizer_loop:utterance', data))
|
||||
|
||||
t = Thread(target=self.newThread)
|
||||
t.start()
|
||||
|
||||
def newThread(self):
|
||||
global wait_response
|
||||
global skill_response
|
||||
timeout = 0
|
||||
while wait_response:
|
||||
wait_response = True
|
||||
time.sleep(1)
|
||||
timeout = timeout + 1
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
if len(skill_response) > 0:
|
||||
self.write_message(skill_response)
|
||||
|
||||
skill_response = ""
|
||||
wait_response = True
|
||||
|
||||
timeout = 0
|
||||
while timeout < 5 or wait_response:
|
||||
time.sleep(1)
|
||||
timeout = timeout + 1
|
||||
|
||||
if len(skill_response) > 0:
|
||||
self.write_message(skill_response)
|
||||
|
||||
wait_response = True
|
||||
skill_response = ""
|
||||
|
||||
def on_close(self):
|
||||
clients.remove(self)
|
||||
|
||||
|
||||
def connect():
|
||||
ws.run_forever()
|
||||
|
||||
|
||||
def handle_speak(event):
|
||||
response = event.data['utterance']
|
||||
global skill_response, wait_response
|
||||
skill_response = ""
|
||||
wait_response = False
|
||||
skill_response = response
|
||||
|
||||
|
||||
def main():
|
||||
global skill_response, wait_response, port, lang
|
||||
wait_response = True
|
||||
skill_response = ""
|
||||
|
||||
global ws
|
||||
ws = WebsocketClient()
|
||||
event_thread = Thread(target=connect)
|
||||
event_thread.setDaemon(True)
|
||||
event_thread.start()
|
||||
|
||||
ws.on('speak', handle_speak)
|
||||
|
||||
import tornado.options
|
||||
|
||||
tornado.options.parse_command_line()
|
||||
config = ConfigurationManager.get().get("websocket")
|
||||
lang = ConfigurationManager.get().get("lang")
|
||||
|
||||
port = "9090"
|
||||
url = ("http://" + str(ip) + ":" + str(port))
|
||||
print("*********************************************************")
|
||||
print("* JCASOFT - Mycroft Web Cliento ")
|
||||
print("*")
|
||||
print("* Access from web browser " + url)
|
||||
print("*********************************************************")
|
||||
|
||||
routes = [
|
||||
tornado.web.url(r"/", MainHandler, name="main"),
|
||||
tornado.web.url(r"/static/(.*)", tornado.web.StaticFileHandler, {'path': './'}),
|
||||
tornado.web.url(r"/ws", WebSocketHandler)
|
||||
]
|
||||
|
||||
settings = {
|
||||
"debug": True,
|
||||
"template_path": os.path.join(os.path.dirname(__file__), "templates"),
|
||||
"static_path": os.path.join(os.path.dirname(__file__), "static"),
|
||||
}
|
||||
|
||||
application = tornado.web.Application(routes, **settings)
|
||||
httpServer = tornado.httpserver.HTTPServer(application)
|
||||
tornado.options.parse_command_line()
|
||||
httpServer.listen(port)
|
||||
|
||||
try:
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
except KeyboardInterrupt:
|
||||
logger.exception(e)
|
||||
event_thread.exit()
|
||||
tornado.ioloop.IOLoop.instance().stop()
|
||||
sys.exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||