#
# Copyright (c) 2017 Mycroft AI, Inc.
#
# This file is part of Mycroft Simple
# (see https://github.com/MatthewScholefield/mycroft-simple).
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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 json
import os
import subprocess
from os import chdir
from os.path import isfile, isdir
from subprocess import Popen, call
from threading import Event, Thread
from websocket_server import WebsocketServer
from mycroft.engines.intent_engine import IntentEngine, make_namespaced
[docs]class PadatiousEngine(IntentEngine):
"""Interface for Padatious intent engine"""
GIT_URL = 'https://github.com/MatthewScholefield/padatious-mycroft.git'
GIT_BRANCH = 'feature/mycroft-simple'
HOST = '127.0.0.1'
PORT = 8014
def __init__(self, path_manager):
"""Opens Padatious process and waits for it to connect"""
super().__init__(path_manager)
self.new_message = None
self.new_message_event = Event()
if not isfile(self.path_manager.padatious_exe):
self._build_padatious()
self.server, connected_event = self._create_server()
self._start_server()
self.process = self._create_process()
if not connected_event.wait(4):
raise TimeoutError('Could not connect websocket to Padatious')
def _build_padatious(self):
def cmd(a):
call(a.split(' '))
if not isdir('padatious'):
cmd('git clone -b ' + self.GIT_BRANCH + ' --single-branch ' + self.GIT_URL + ' padatious')
chdir('padatious')
cmd('sh build.sh')
chdir('..')
def _create_server(self):
"""Creates a websocket server to communicate with the padatious process"""
server = WebsocketServer(host=PadatiousEngine.HOST, port=PadatiousEngine.PORT)
def on_message(server, client, message):
self.new_message = message
self.new_message_event.set()
connected_event = Event()
def on_connected(server, client):
connected_event.set()
server.set_fn_message_received(on_message)
server.set_fn_new_client(on_connected)
return server, connected_event
def _create_process(self):
"""Opens the padatious process silently"""
return Popen([self.path_manager.padatious_exe], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
def _start_server(self):
"""Creates server in new thread as daemon (will run forever until program terminates)"""
Thread(target=self.server.run_forever, daemon=True).start()
def _send_request(self, request, parameters={}):
"""Ask the padatious process to do something"""
parameters['request'] = request
self.server.send_message_to_all(json.dumps(parameters))
[docs] def try_register_intent(self, skill_name, intent_name):
if not isinstance(intent_name, str):
return ""
intent_dir = self.path_manager.intent_dir(skill_name)
file_name = os.path.join(intent_dir, intent_name + '.intent')
if not os.path.isfile(file_name):
return ""
name = make_namespaced(intent_name, skill_name)
self._send_request('register_intent', {'name': name, 'file_name': file_name})
return name
[docs] def on_intents_loaded(self):
self._send_request('train')
[docs] def calc_intents(self, query):
self.new_message_event.clear()
self._send_request('calc_intents', {'query': query})
if not self.new_message_event.wait(4):
raise TimeoutError('When asking to calculate intents from Padatious')
return json.loads(self.new_message)