163 lines
4.6 KiB
Python
163 lines
4.6 KiB
Python
# Copyright 2018 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.
|
|
#
|
|
|
|
from os import getpid
|
|
from os.path import basename
|
|
import json
|
|
import websocket
|
|
from threading import Thread, Lock
|
|
|
|
from mycroft.messagebus.client.ws import WebsocketClient
|
|
from mycroft.messagebus.message import Message
|
|
|
|
|
|
bus = None
|
|
buffer = None # content will show on the CLI "GUI" representation
|
|
msgs = []
|
|
|
|
loaded = []
|
|
skill = None
|
|
page = None
|
|
vars = {}
|
|
|
|
|
|
def start_qml_gui(messagebus, output_buf):
|
|
global bus
|
|
global buffer
|
|
|
|
bus = messagebus
|
|
buffer = output_buf
|
|
|
|
# Initiate the QML GUI
|
|
log_message("Announcing CLI GUI")
|
|
bus.on('mycroft.gui.port', handle_gui_ready)
|
|
bus.emit(Message("mycroft.gui.connected",
|
|
{"gui_id": "cli_" + str(getpid())}))
|
|
log_message("Announced CLI GUI")
|
|
|
|
|
|
def log_message(msg):
|
|
global msgs
|
|
msgs.append(msg)
|
|
if len(msgs) > 20:
|
|
del msgs[0]
|
|
build_output_buffer()
|
|
|
|
|
|
def build_output_buffer():
|
|
global buffer
|
|
buffer.clear()
|
|
try:
|
|
if skill:
|
|
buffer.append("Active Skill: {}".format(skill))
|
|
buffer.append("Page: {}".format(basename(page)))
|
|
buffer.append("vars: ")
|
|
for v in vars[skill]:
|
|
buffer.append(" {}: {}".format(v, vars[skill][v]))
|
|
except Exception as e:
|
|
buffer.append(repr(e))
|
|
buffer.append("-----------------")
|
|
buffer.append("MESSAGES")
|
|
buffer.append("-----------------")
|
|
for m in msgs:
|
|
if len(buffer) > 20: # cap out at 20 lines total
|
|
return
|
|
buffer.append(m)
|
|
|
|
|
|
def handle_gui_ready(msg):
|
|
# Attempt to connect to the port
|
|
gui_id = msg.data.get("gui_id")
|
|
if not gui_id == "cli_" + str(getpid()):
|
|
# Not us, ignore!
|
|
return
|
|
|
|
# Create the websocket for GUI communications
|
|
port = msg.data.get("port")
|
|
if port:
|
|
log_message("Connecting CLI GUI on "+str(port))
|
|
ws = websocket.WebSocketApp("ws://0.0.0.0:" + str(port) + "/gui",
|
|
on_message=on_gui_message,
|
|
on_error=on_gui_error,
|
|
on_close=on_gui_close)
|
|
|
|
log_message("WS = "+str(ws))
|
|
event_thread = Thread(target=gui_connect, args=[ws])
|
|
event_thread.setDaemon(True)
|
|
event_thread.start()
|
|
|
|
|
|
def gui_connect(ws):
|
|
# Once the websocket has connected, just watch it for speak events
|
|
log_message("GUI Connected"+str(ws))
|
|
ws.on_open = on_gui_open
|
|
ws.run_forever()
|
|
|
|
|
|
def on_gui_open(ws):
|
|
log_message("GUI Opened")
|
|
|
|
|
|
def on_gui_message(ws, payload):
|
|
global loaded
|
|
global skill
|
|
global page
|
|
global vars
|
|
try:
|
|
msg = json.loads(payload)
|
|
log_message("Msg: "+str(payload))
|
|
type = msg.get("type")
|
|
if type == "mycroft.session.set":
|
|
skill = msg.get("namespace")
|
|
data = msg.get("data")
|
|
if skill not in vars:
|
|
vars[skill] = {}
|
|
for d in data:
|
|
vars[skill][d] = data[d]
|
|
elif type == "mycroft.session.list.insert":
|
|
# Insert new namespace
|
|
skill = msg.get('data')[0]['skill_id']
|
|
loaded.insert(0, [skill, []])
|
|
elif type == "mycroft.gui.list.insert":
|
|
# Insert a page in an existing namespace
|
|
page = msg['data'][0]['url']
|
|
pos = msg.get('position')
|
|
loaded[0][1].insert(pos, page)
|
|
skill = loaded[0][0]
|
|
elif type == "mycroft.session.list.move":
|
|
# Move the namespace at "pos" to the top of the stack
|
|
pos = msg.get('from')
|
|
loaded.insert(0, loaded.pop(pos))
|
|
elif type == "mycroft.events.triggered":
|
|
# Switch selected page of namespace
|
|
skill = msg['namespace']
|
|
pos = msg['data']['number']
|
|
for n in loaded:
|
|
if n[0] == skill:
|
|
page = n[1][pos]
|
|
|
|
build_output_buffer()
|
|
except Exception as e:
|
|
log_message(repr(e))
|
|
log_message("Invalid JSON: "+str(payload))
|
|
|
|
|
|
def on_gui_close(ws):
|
|
log_message("GUI closed")
|
|
|
|
|
|
def on_gui_error(ws, err):
|
|
log_message("GUI error: "+str(err))
|