def main(): global ws lock = Lock('skills') # prevent multiple instances of this service # Connect this Skill management process to the websocket ws = WebsocketClient() ConfigurationManager.init(ws) ignore_logs = ConfigurationManager.instance().get("ignore_logs") # Listen for messages and echo them for logging def _echo(message): try: _message = json.loads(message) if _message.get("type") in ignore_logs: return if _message.get("type") == "registration": # do not log tokens from registration messages _message["data"]["token"] = None message = json.dumps(_message) except: pass logger.debug(message) ws.on('message', _echo) # Startup will be called after websocket is full live ws.once('open', _starting_up) ws.run_forever()
def main(): global ws global config ws = WebsocketClient() Configuration.init(ws) config = Configuration.get() speech.init(ws) # Setup control of pulse audio setup_pulseaudio_handlers(config.get('Audio').get('pulseaudio')) def echo(message): try: _message = json.loads(message) if 'mycroft.audio.service' not in _message.get('type'): return message = json.dumps(_message) except: pass LOG.debug(message) LOG.info("Staring Audio Services") ws.on('message', echo) ws.once('open', load_services_callback) try: ws.run_forever() except KeyboardInterrupt, e: LOG.exception(e) speech.shutdown() sys.exit()
def simple_cli(): global bus global bSimple bSimple = True bus = WebsocketClient() # Mycroft messagebus connection event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() bus.on('speak', handle_speak) try: while True: # Sleep for a while so all the output that results # from the previous command finishes before we print. time.sleep(1.5) print("Input (Ctrl+C to quit):") line = sys.stdin.readline() bus.emit(Message("recognizer_loop:utterance", {'utterances': [line.strip()]})) except KeyboardInterrupt as e: # User hit Ctrl+C to quit print("") except KeyboardInterrupt as e: LOG.exception(e) event_thread.exit() sys.exit()
def main(): global client global loop client = WebsocketClient() device_index = config.get('speech_client').get('device_index') if device_index: device_index = int(device_index) loop = RecognizerLoop(device_index=device_index) loop.on('recognizer_loop:utterance', handle_utterance) loop.on('recognizer_loop:record_begin', handle_record_begin) loop.on('recognizer_loop:wakeword', handle_wakeword) loop.on('recognizer_loop:record_end', handle_record_end) loop.on('speak', handle_speak) client.on('speak', handle_speak) client.on( 'multi_utterance_intent_failure', handle_multi_utterance_intent_failure) client.on('recognizer_loop:sleep', handle_sleep) client.on('recognizer_loop:wake_up', handle_wake_up) client.on('mycroft.stop', handle_stop) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: subprocess.call('echo "eyes.reset" >/dev/ttyAMA0', shell=True) except: pass try: loop.run() except KeyboardInterrupt, e: logger.exception(e) event_thread.exit() sys.exit()
class WebSocketHandler(tornado.websocket.WebSocketHandler): peer = "unknown" def create_internal_emitter(self): # connect to mycroft internal websocket self.emitter = WebsocketClient() self.register_internal_messages() self.emitter_thread = Thread(target=self.connect_to_internal_emitter) self.emitter_thread.setDaemon(True) self.emitter_thread.start() def register_internal_messages(self): # catch all messages self.emitter.on('speak', self.handle_speak) def connect_to_internal_emitter(self): self.emitter.run_forever() def open(self): LOG.info('Client IP: ' + self.request.remote_ip) self.peer = self.request.remote_ip clients[self.peer] = self self.create_internal_emitter() self.write_message("Welcome to Jarbas Web Client") def on_message(self, message): utterance = message.strip() LOG.info("Utterance : " + utterance) if utterance: if utterance == '"mic_on"': create_signal('startListening') else: data = {"utterances": [utterance], "lang": lang} context = { "source": self.peer, "destinatary": "skills", "client_name": platform, "peer": self.peer } self.emitter.emit( Message("recognizer_loop:utterance", data, context)) def handle_speak(self, event): if event.context.get("client_name", platform) == platform: peer = event.context.get("peer", "") if peer == self.peer: self.write_message(event.data['utterance']) def on_close(self): global clients self.emitter.remove("speak", self.handle_speak) clients.pop(self.peer)
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()
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") host = config.get("host") port = config.get("port") route = config.get("route") validate_param(host, "websocket.host") validate_param(port, "websocket.port") validate_param(route, "websocket.route") url = "http://" + str(ip)+":"+str(port) print "*********************************************************" print "* Access from web browser " + url print "*********************************************************" routes = [ (route, WebsocketEventHandler), 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) tornado.ioloop.IOLoop.instance().start()
def main(): global client global loop client = WebsocketClient() device_index = config.get('speech_client').get('device_index') if device_index: device_index = int(device_index) loop = RecognizerLoop(device_index=device_index) loop.on('recognizer_loop:listening', handle_listening) loop.on('recognizer_loop:wakeword', handle_wakeword) loop.on('recognizer_loop:utterance', handle_utterance) loop.on('speak', handle_speak) client.on('speak', handle_speak) client.on( 'multi_utterance_intent_failure', handle_multi_utterance_intent_failure) client.on('recognizer_loop:sleep', handle_sleep) client.on('recognizer_loop:wake_up', handle_wake_up) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: loop.run() except KeyboardInterrupt, e: event_thread.exit() sys.exit()
def main(): global client global loop client = WebsocketClient() device_index = config.get('speech_client').get('device_index') if device_index: device_index = int(device_index) loop = RecognizerLoop(device_index=device_index) loop.on('recognizer_loop:listening', handle_listening) loop.on('recognizer_loop:wakeword', handle_wakeword) loop.on('recognizer_loop:utterance', handle_utterance) loop.on('speak', handle_speak) client.on('speak', handle_speak) client.on('multi_utterance_intent_failure', handle_multi_utterance_intent_failure) client.on('recognizer_loop:sleep', handle_sleep) client.on('recognizer_loop:wake_up', handle_wake_up) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: loop.run() except KeyboardInterrupt, e: event_thread.exit() sys.exit()
def main(): global bus reset_sigint_handler() # Create PID file, prevent multiple instancesof this service mycroft.lock.Lock('skills') # Connect this Skill management process to the Mycroft Messagebus bus = WebsocketClient() Configuration.init(bus) bus.on('message', create_echo_function('SKILLS')) # Startup will be called after the connection with the Messagebus is done bus.once('open', _starting_up) create_daemon(bus.run_forever) wait_for_exit_signal() shutdown()
def main(): global bus reset_sigint_handler() # Create PID file, prevent multiple instancesof this service mycroft.lock.Lock('skills') # Connect this Skill management process to the Mycroft Messagebus bus = WebsocketClient() Configuration.init(bus) bus.on('message', create_echo_function('SKILLS')) # Startup will be called after the connection with the Messagebus is done bus.once('open', _starting_up) create_daemon(bus.run_forever) wait_for_exit_signal() shutdown()
def main(): """ Main function. Run when file is invoked. """ reset_sigint_handler() ws = WebsocketClient() Configuration.init(ws) speech.init(ws) LOG.info("Starting Audio Services") ws.on('message', create_echo_function('AUDIO', ['mycroft.audio.service'])) audio = AudioService(ws) # Connect audio service instance to message bus create_daemon(ws.run_forever) wait_for_exit_signal() speech.shutdown() audio.shutdown()
def main(): global client client = WebsocketClient() if not '--quiet' in sys.argv: client.on('speak', handle_speak) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: while True: print("Input:") line = sys.stdin.readline() client.emit(Message("recognizer_loop:utterance", metadata={'utterances': [line.strip()]})) except KeyboardInterrupt, e: event_thread.exit() sys.exit()
class Enclosure: """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ def __init__(self): self.__init_serial() self.client = WebsocketClient() self.reader = EnclosureReader(self.serial, self.client) self.writer = EnclosureWriter(self.serial, self.client) self.eyes = EnclosureEyes(self.client, self.writer) self.mouth = EnclosureMouth(self.client, self.writer) self.system = EnclosureArduino(self.client, self.writer) self.__init_events() def __init_serial(self): try: self.config = ConfigurationManager.get_config().get("enclosure") self.port = self.config.get("port") self.rate = int(self.config.get("rate")) self.timeout = int(self.config.get("timeout")) self.serial = serial.serial_for_url( url=self.port, baudrate=self.rate, timeout=self.timeout) LOGGER.info( "Connected to: " + self.port + " rate: " + str(self.rate) + " timeout: " + str(self.timeout)) except: LOGGER.error( "It is not possible to connect to serial port: " + self.port) raise def __init_events(self): self.client.on('recognizer_loop:listening', self.mouth.listen) self.client.on('recognizer_loop:audio_output_start', self.mouth.talk) self.client.on('recognizer_loop:audio_output_end', self.mouth.reset) self.client.on('recognizer_loop:wakeword', self.eyes.blink) def run(self): try: self.client.run_forever() except Exception as e: LOGGER.error("Client error: {0}".format(e)) self.stop() def stop(self): self.writer.stop() self.reader.stop() self.serial.close()
def main(): """ Main function. Run when file is invoked. """ reset_sigint_handler() check_for_signal("isSpeaking") bus = WebsocketClient() # Connect to the Mycroft Messagebus Configuration.init(bus) speech.init(bus) LOG.info("Starting Audio Services") bus.on('message', create_echo_function('AUDIO', ['mycroft.audio.service'])) audio = AudioService(bus) # Connect audio service instance to message bus create_daemon(bus.run_forever) wait_for_exit_signal() speech.shutdown() audio.shutdown()
class Enclosure: """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ def __init__(self): self.__init_serial() self.client = WebsocketClient() self.reader = EnclosureReader(self.serial, self.client) self.writer = EnclosureWriter(self.serial, self.client) self.eyes = EnclosureEyes(self.client, self.writer) self.mouth = EnclosureMouth(self.client, self.writer) self.system = EnclosureArduino(self.client, self.writer) self.__init_events() def __init_serial(self): try: self.config = ConfigurationManager.get_config().get("enclosure") self.port = self.config.get("port") self.rate = int(self.config.get("rate")) self.timeout = int(self.config.get("timeout")) self.serial = serial.serial_for_url(url=self.port, baudrate=self.rate, timeout=self.timeout) LOGGER.info("Connected to: " + self.port + " rate: " + str(self.rate) + " timeout: " + str(self.timeout)) except: LOGGER.error("It is not possible to connect to serial port: " + self.port) raise def __init_events(self): self.client.on('recognizer_loop:listening', self.mouth.listen) self.client.on('recognizer_loop:audio_output_start', self.mouth.talk) self.client.on('recognizer_loop:audio_output_end', self.mouth.reset) self.client.on('recognizer_loop:wakeword', self.eyes.blink) def run(self): try: self.client.run_forever() except Exception as e: LOGGER.error("Client error: {0}".format(e)) self.stop() def stop(self): self.writer.stop() self.reader.stop() self.serial.close()
def main(): global ws ws = WebsocketClient() if '--quiet' not in sys.argv: ws.on('speak', handle_speak) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: while True: print("Input:") line = sys.stdin.readline() ws.emit( Message("recognizer_loop:utterance", {'utterances': [line.strip()]})) except KeyboardInterrupt, e: logger.exception(e) event_thread.exit() sys.exit()
def main(): global client client = WebsocketClient() def echo(message): try: _message = json.loads(message) if _message.get("message_type") == "registration": # do not log tokens from registration messages _message["metadata"]["token"] = None message = json.dumps(_message) except: pass logger.debug(message) client.on('message', echo) client.once('open', load_skills_callback) client.run_forever()
def main(): global ws ws = WebsocketClient() ConfigurationManager.init(ws) def echo(message): try: _message = json.loads(message) if _message.get("type") == "registration": # do not log tokens from registration messages _message["data"]["token"] = None message = json.dumps(_message) except: pass logger.debug(message) ws.on('message', echo) ws.once('open', load_skills_callback) ws.run_forever()
def main(): global ws ws = WebsocketClient() ConfigurationManager.init(ws) def echo(message): try: _message = json.loads(message) if _message.get("type") == "registration": # do not log tokens from registration messages _message["data"]["token"] = None message = json.dumps(_message) except: pass logger.debug(message) ws.on('message', echo) ws.once('open', load_skills_callback) ws.run_forever()
class SkillContainer(object): def __init__(self, args): parser = argparse.ArgumentParser() parser.add_argument("--dependency-dir", default="./lib") parser.add_argument("--messagebus-host", default=messagebus_config.get("host")) parser.add_argument("--messagebus-port", type=int, default=messagebus_config.get("port")) parser.add_argument("--use-ssl", action='store_true', default=False) parser.add_argument("--enable-intent-skill", action='store_true', default=False) parser.add_argument("skill_directory", default=os.path.dirname(__file__)) parsed_args = parser.parse_args(args) if os.path.exists(parsed_args.dependency_dir): sys.path.append(parsed_args.dependency_dir) self.skill_directory = parsed_args.skill_directory self.enable_intent_skill = parsed_args.enable_intent_skill self.client = WebsocketClient(host=parsed_args.messagebus_host, port=parsed_args.messagebus_port, ssl=parsed_args.use_ssl) def try_load_skill(self): if self.enable_intent_skill: intent_skill = create_intent_skill() intent_skill.bind(self.client) intent_skill.initialize() skill_descriptor = create_skill_descriptor(self.skill_directory) load_skill(skill_descriptor, self.client) def run(self): self.client.on('message', logger.debug) self.client.on('open', self.try_load_skill) self.client.on('error', logger.error) self.client.run_forever()
def listener(): rospy.init_node('mycroft_skills') rospy.loginfo(rospy.get_caller_id() + " started") rospy.Subscriber("mycroft/utterance", String, handle_utterance) rospy.Subscriber("mycroft/remove_skill", String, handle_remove_skill) s = rospy.Service('mycroft/register_skill', MycroftService, handle_register_skill) global bus reset_sigint_handler() # Create PID file, prevent multiple instancesof this service mycroft.lock.Lock('skills') # Connect this Skill management process to the Mycroft Messagebus bus = WebsocketClient() Configuration.init(bus) config = Configuration.get() # Set the active lang to match the configured one set_active_lang(config.get('lang', 'en-us')) bus.on('skill.manager.initialised', initialise_response_server) bus.on('message', create_echo_function('SKILLS')) # Startup will be called after the connection with the Messagebus is done bus.once('open', _starting_up) bus.on('skill.converse.request', check_working) create_daemon(bus.run_forever) wait_for_exit_signal() shutdown() rospy.spin()
def catchInput(queryInput): bus = dbus.SessionBus() remote_object = bus.get_object("com.mycroftkde.KDEPlasmoid","/ComMycroftkdeKDEPlasmoidInterface") global client client = WebsocketClient() if '--quiet' not in sys.argv: client.on('speak', handle_speak) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: while True: print("DebuggingGui") line = remote_object.sendQuery(guioutputstring,dbus_interface = "com.mycroftkde.KDEPlasmoid") client.emit( Message("recognizer_loop:utterance", metadata={'utterances': [line.strip()]})) line = ("") return except KeyboardInterrupt, e: logger.exception(e) event_thread.exit() sys.exit()
class SkillContainer(object): def __init__(self, args): params = self.__build_params(args) if params.config: ConfigurationManager.load_local([params.config]) if exists(params.lib) and isdir(params.lib): sys.path.append(params.lib) sys.path.append(params.dir) self.dir = params.dir self.enable_intent_skill = params.enable_intent_skill self.__init_client(params) @staticmethod def __build_params(args): parser = argparse.ArgumentParser() parser.add_argument("--config", default="./mycroft.ini") parser.add_argument("--dir", default=dirname(__file__)) parser.add_argument("--lib", default="./lib") parser.add_argument("--host", default=None) parser.add_argument("--port", default=None) parser.add_argument("--use-ssl", action='store_true', default=False) parser.add_argument("--enable-intent-skill", action='store_true', default=False) return parser.parse_args(args) def __init_client(self, params): config = ConfigurationManager.get().get("messagebus_client") if not params.host: params.host = config.get('host') if not params.port: params.port = config.get('port') self.client = WebsocketClient(host=params.host, port=params.port, ssl=params.use_ssl) def try_load_skill(self): if self.enable_intent_skill: intent_skill = create_intent_skill() intent_skill.bind(self.client) intent_skill.initialize() skill_descriptor = create_skill_descriptor(self.dir) load_skill(skill_descriptor, self.client) def run(self): self.client.on('message', logger.debug) self.client.on('open', self.try_load_skill) self.client.on('error', logger.error) self.client.run_forever()
def catchInput(queryInput): bus = dbus.SessionBus() remote_object = bus.get_object("org.gnome.Shell","/com/mycroftaignome/MycroftGnomeResult") getText = remote_object.sendQuery(guioutputstring,dbus_interface = "com.mycroftaignome.MycroftAiGnomeBox") print getText global client client = WebsocketClient() client.on('speak', handle_speak) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: while True: print("Input:") queryInput= object.sendQuery(guioutputstring,dbus_interface = "com.mycroftaignome.MycroftAiGnomeBox") client.emit( Message("recognizer_loop:utterance", metadata={'utterances': [queryInput]})) break except KeyboardInterrupt, e: logger.exception(e) event_thread.exit() sys.exit()
def main(): rospy.init_node('mycroft_tts') rospy.loginfo(rospy.get_caller_id() + " started") rospy.Subscriber("mycroft/speak", String, handle_speak) rospy.Subscriber("mycroft/stop", String, handle_stop) """ Main function. Run when file is invoked. """ global bus reset_sigint_handler() check_for_signal("isSpeaking") bus = WebsocketClient() # Connect to the Mycroft Messagebus Configuration.init(bus) speech.init(bus) LOG.info("Starting Audio Services") bus.on('message', create_echo_function('AUDIO', ['mycroft.audio.service'])) audio = AudioService(bus) # Connect audio service instance to message bus create_daemon(bus.run_forever) wait_for_exit_signal() speech.shutdown() audio.shutdown() # keep node running rospy.spin()
def simple_cli(): global ws ws = WebsocketClient() event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() ws.on('speak', handle_speak) try: while True: # Sleep for a while so all the output that results # from the previous command finishes before we print. time.sleep(1.5) print("Input (Ctrl+C to quit):") line = sys.stdin.readline() ws.emit( Message("recognizer_loop:utterance", {'utterances': [line.strip()]})) except KeyboardInterrupt as e: # User hit Ctrl+C to quit print("") except KeyboardInterrupt as e: LOG.exception(e) event_thread.exit() sys.exit()
class SkillContainer(object): def __init__(self, args): params = self.__build_params(args) if params.config: ConfigurationManager.load_local([params.config]) if exists(params.lib) and isdir(params.lib): sys.path.append(params.lib) sys.path.append(params.dir) self.dir = params.dir self.enable_intent_skill = params.enable_intent_skill self.__init_client(params) @staticmethod def __build_params(args): parser = argparse.ArgumentParser() parser.add_argument("--config", default="./mycroft.ini") parser.add_argument("dir", nargs='?', default=dirname(__file__)) parser.add_argument("--lib", default="./lib") parser.add_argument("--host", default=None) parser.add_argument("--port", default=None) parser.add_argument("--use-ssl", action='store_true', default=False) parser.add_argument("--enable-intent-skill", action='store_true', default=False) return parser.parse_args(args) def __init_client(self, params): config = ConfigurationManager.get().get("messagebus_client") if not params.host: params.host = config.get('host') if not params.port: params.port = config.get('port') self.client = WebsocketClient(host=params.host, port=params.port, ssl=params.use_ssl) def try_load_skill(self): if self.enable_intent_skill: intent_skill = create_intent_skill() intent_skill.bind(self.client) intent_skill.initialize() skill_descriptor = create_skill_descriptor(self.dir) load_skill(skill_descriptor, self.client) def run(self): self.client.on('message', logger.debug) self.client.on('open', self.try_load_skill) self.client.on('error', logger.error) self.client.run_forever()
def launch(config=None): global app, ws, intents, timeout config = config or Configuration.get().get("hivemind", {}).get("flask_node", {}) # connect to internal mycroft ws = WebsocketClient() ws.on("mycroft.skill.handler.complete", end_wait) ws.on("complete_intent_failure", end_wait) ws.on("speak", listener) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() port = config.get("port", 6712) timeout = config.get("timeout", timeout) intents = MicroIntentService(ws) start(app, port)
class DevicePairingClient(object): def __init__(self, config=_config, pairing_code=None): self.config = config self.paired = False self.ws_client = WebsocketClient(host=config.get("host"), port=config.get("port"), path=config.get("route"), ssl=str2bool(config.get("ssl"))) self.identity_manager = IdentityManager() self.identity = self.identity_manager.identity self.pairing_code = ( pairing_code if pairing_code else generate_pairing_code()) def on_registration(self, message): # TODO: actually accept the configuration message and store it in # identity identity = self.identity_manager.get() register_payload = message.metadata if register_payload.get("device_id") == identity.device_id: identity.token = register_payload.get('token') identity.owner = register_payload.get('user') self.identity_manager.update(identity) self.ws_client.close() self.paired = True def send_device_info(self): msg = Message("device_info", metadata={ "pairing_code": self.pairing_code, "device_id": self.identity.device_id }) self.ws_client.emit(msg) @staticmethod def print_error(message): print(repr(message)) def run(self): self.ws_client.on('registration', self.on_registration) self.ws_client.on('open', self.send_device_info) self.ws_client.on('error', self.print_error) self.ws_client.run_forever()
class DevicePairingClient(object): def __init__(self, config=_config, pairing_code=None): self.config = config self.paired = False self.ws_client = WebsocketClient(host=config.get("host"), port=config.get("port"), path=config.get("route"), ssl=str2bool(config.get("ssl"))) self.identity_manager = IdentityManager() self.identity = self.identity_manager.identity self.pairing_code = (pairing_code if pairing_code else generate_pairing_code()) def on_registration(self, message): # TODO: actually accept the configuration message and store it in # identity identity = self.identity_manager.get() register_payload = message.metadata if register_payload.get("device_id") == identity.device_id: identity.token = register_payload.get('token') identity.owner = register_payload.get('user') self.identity_manager.update(identity) self.ws_client.close() self.paired = True def send_device_info(self): msg = Message("device_info", metadata={ "pairing_code": self.pairing_code, "device_id": self.identity.device_id }) self.ws_client.emit(msg) @staticmethod def print_error(message): print(repr(message)) def run(self): self.ws_client.on('registration', self.on_registration) self.ws_client.on('open', self.send_device_info) self.ws_client.on('error', self.print_error) self.ws_client.run_forever()
class SkillContainer(object): def __init__(self, args): parser = argparse.ArgumentParser() parser.add_argument("--dependency-dir", default="./lib") parser.add_argument("--messagebus-host", default=messagebus_config.get("host")) parser.add_argument("--messagebus-port", type=int, default=messagebus_config.get("port")) parser.add_argument("--use-ssl", action='store_true', default=False) parser.add_argument("--enable-intent-skill", action='store_true', default=False) parser.add_argument("skill_directory", default=os.path.dirname(__file__)) parsed_args = parser.parse_args(args) if os.path.exists(parsed_args.dependency_dir): sys.path.append(parsed_args.dependency_dir) sys.path.append(parsed_args.skill_directory) self.skill_directory = parsed_args.skill_directory self.enable_intent_skill = parsed_args.enable_intent_skill self.client = WebsocketClient(host=parsed_args.messagebus_host, port=parsed_args.messagebus_port, ssl=parsed_args.use_ssl) def try_load_skill(self): if self.enable_intent_skill: intent_skill = create_intent_skill() intent_skill.bind(self.client) intent_skill.initialize() skill_descriptor = create_skill_descriptor(self.skill_directory) load_skill(skill_descriptor, self.client) def run(self): self.client.on('message', logger.debug) self.client.on('open', self.try_load_skill) self.client.on('error', logger.error) self.client.run_forever()
def init_display_manager_bus_connection(): """ Connects the display manager to the messagebus """ LOG.info("Connecting display manager to messagebus") # Should remove needs to be an object so it can be referenced in functions # [https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference] display_manager = DisplayManager() should_remove = [True] def check_flag(flag): if flag[0] is True: display_manager.remove_active() def set_delay(event=None): should_remove[0] = True Timer(2, check_flag, [should_remove]).start() def set_remove_flag(event=None): should_remove[0] = False def connect(): bus.run_forever() def remove_wake_word(): data = _read_data() if "active_skill" in data and data["active_skill"] == "wakeword": display_manager.remove_active() def set_wakeword_skill(event=None): display_manager.set_active("wakeword") Timer(10, remove_wake_word).start() bus = WebsocketClient() bus.on('recognizer_loop:audio_output_end', set_delay) bus.on('recognizer_loop:audio_output_start', set_remove_flag) bus.on('recognizer_loop:record_begin', set_wakeword_skill) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start()
def init_display_manager_bus_connection(): """ Connects the display manager to the messagebus """ LOG.info("Connecting display manager to messagebus") # Should remove needs to be an object so it can be referenced in functions # [https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference] display_manager = DisplayManager() should_remove = [True] def check_flag(flag): if flag[0] is True: display_manager.remove_active() def set_delay(event=None): should_remove[0] = True Timer(2, check_flag, [should_remove]).start() def set_remove_flag(event=None): should_remove[0] = False def connect(): bus.run_forever() def remove_wake_word(): data = _read_data() if "active_skill" in data and data["active_skill"] == "wakeword": display_manager.remove_active() def set_wakeword_skill(event=None): display_manager.set_active("wakeword") Timer(10, remove_wake_word).start() bus = WebsocketClient() bus.on('recognizer_loop:audio_output_end', set_delay) bus.on('recognizer_loop:audio_output_start', set_remove_flag) bus.on('recognizer_loop:record_begin', set_wakeword_skill) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start()
from mycroft.messagebus.message import Message if len(sys.argv) == 2: messageToSend = sys.argv[1] elif len(sys.argv) > 2: messageToSend = " ".join(sys.argv[2:]) else: filename = os.path.basename(__file__) print filename print "Simple command line interface to the messagebus." print "Usage: messagebus_emit <utterance>\n" print " where <utterance> is treated as if spoken to Mycroft." print "Example: " + filename + " mycroft.wifi.start" exit() def onConnected(event=None): print "Sending message...'" + messageToSend + "'" messagebusClient.emit(Message(messageToSend)) messagebusClient.close() exit() # Establish a connection with the messagebus messagebusClient = WebsocketClient() messagebusClient.on('connected', onConnected) # This will block until the client gets closed messagebusClient.run_forever()
class WiFi: def __init__(self): self.iface = pyw.winterfaces()[0] self.ap = AccessPoint(self.iface) self.server = None self.ws = WebsocketClient() self.enclosure = EnclosureAPI(self.ws) self.init_events() self.conn_monitor = None self.conn_monitor_stop = threading.Event() self.starting = False def init_events(self): ''' Register handlers for various websocket events used to communicate with outside systems. ''' # This event is generated by an outside mechanism. On a # Mark 1 unit this comes from the Enclosure's WIFI menu # item being selected. self.ws.on('mycroft.wifi.start', self.start) # Similar to the above. Resets to factory defaults self.ws.on('mycroft.wifi.reset', self.reset) # Similar to the above. Enable/disable SSH self.ws.on('mycroft.enable.ssh', self.ssh_enable) self.ws.on('mycroft.disable.ssh', self.ssh_disable) # These events are generated by Javascript in the captive # portal. self.ws.on('mycroft.wifi.stop', self.stop) self.ws.on('mycroft.wifi.scan', self.scan) self.ws.on('mycroft.wifi.connect', self.connect) def start(self, event=None): ''' Fire up the MYCROFT access point for the user to connect to with a phone or computer. ''' if self.starting: return self.starting = True LOG.info("Starting access point...") self.intro_msg = "" if event and event.data.get("msg"): self.intro_msg = event.data.get("msg") self.allow_timeout = True if event and event.data.get("allow_timeout"): self.allow_timeout = event.data.get("allow_timeout") # Fire up our access point self.ap.up() if not self.server: LOG.info("Creating web server...") self.server = WebServer(self.ap.ip, 80) LOG.info("Starting web server...") self.server.start() LOG.info("Created web server.") LOG.info("Access point started!\n%s" % self.ap.__dict__) self._start_connection_monitor() def _connection_prompt(self, intro): while self.ap.password is None or self.ap.password == "": sleep(1) # give it time to load # Speak the connection instructions and show the password on screen passwordSpelled = ", ".join(self.ap.password) self._speak_and_show(intro + " Use your mobile device or computer to connect " "to the wifi network 'MYCROFT'. Then enter the " "password " + passwordSpelled, self.ap.password) def _speak_and_show(self, speak, show): ''' Communicate with the user throughout the process ''' self.ws.emit(Message("speak", {'utterance': speak})) if show is None: return wait_while_speaking() self.enclosure.mouth_text(show) def _start_connection_monitor(self): LOG.info("Starting monitor thread...\n") if self.conn_monitor is not None: LOG.info("Killing old thread...\n") self.conn_monitor_stop.set() self.conn_monitor_stop.wait() self.conn_monitor = threading.Thread( target=self._do_connection_monitor, args={}) self.conn_monitor.daemon = True self.conn_monitor.start() LOG.info("Monitor thread setup complete.\n") def _stop_connection_monitor(self): ''' Set flag that will let monitoring thread close ''' self.conn_monitor_stop.set() def _do_connection_monitor(self): LOG.info("Invoked monitor thread...\n") mtimeLast = os.path.getmtime('/var/lib/misc/dnsmasq.leases') bHasConnected = False cARPFailures = 0 timeStarted = time.time() timeLastAnnounced = timeStarted - 45 # first reminder in 90 secs self.conn_monitor_stop.clear() while not self.conn_monitor_stop.isSet(): # do our monitoring... mtime = os.path.getmtime('/var/lib/misc/dnsmasq.leases') if mtimeLast != mtime: # Something changed in the dnsmasq lease file - # presumably a (re)new lease bHasConnected = True cARPFailures = 0 mtimeLast = mtime timeStarted = time.time() # reset start time after connection timeLastAnnounced = time.time() - 45 # announce how to connect if time.time() - timeStarted > 60 * 5 and self.allow_timeout: # After 5 minutes, shut down the access point (unless the # system has never been setup, in which case we stay up # indefinitely) LOG.info("Auto-shutdown of access point after 5 minutes") self.stop() continue if time.time() - timeLastAnnounced >= 45: if bHasConnected: self._speak_and_show( "Follow the prompt on your mobile device or computer " "and choose a wifi network. If you don't get a " "prompt, open your browser and go to start dot " "mycroft dot A I.", "start.mycroft.ai") else: if self.intro_msg: self._connection_prompt(self.intro_msg) self.intro_msg = None # only speak the intro once else: self._connection_prompt("Allow me to walk you through " "the wifi setup process.") timeLastAnnounced = time.time() if bHasConnected: # Flush the ARP entries associated with our access point # This will require all network hardware to re-register # with the ARP tables if still present. if cARPFailures == 0: res = cli_no_output('ip', '-s', '-s', 'neigh', 'flush', self.ap.subnet + '.0/24') # Give ARP system time to re-register hardware sleep(5) # now look at the hardware that has responded, if no entry # shows up on our access point after 2*5=10 seconds, the user # has disconnected if not self._is_ARP_filled(): cARPFailures += 1 if cARPFailures > 2: self._connection_prompt("Connection lost.") bHasConnected = False else: cARPFailures = 0 sleep(5) # wait a bit to prevent thread from hogging CPU LOG.info("Exiting monitor thread...\n") self.conn_monitor_stop.clear() def _is_ARP_filled(self): res = cli_no_output('/usr/sbin/arp', '-n') out = str(res.get("stdout")) if out: # Parse output, skipping header for o in out.split("\n")[1:]: if o[0:len(self.ap.subnet)] == self.ap.subnet: if "(incomplete)" in o: # ping the IP to get the ARP table entry reloaded ip_disconnected = o.split(" ")[0] cli_no_output('/bin/ping', '-c', '1', '-W', '3', ip_disconnected) else: return True # something on subnet is connected! return False def scan(self, event=None): LOG.info("Scanning wifi connections...") networks = {} status = self.get_status() for cell in Cell.all(self.iface): if "x00" in cell.ssid: continue # ignore hidden networks update = True ssid = cell.ssid quality = self.get_quality(cell.quality) # If there are duplicate network IDs (e.g. repeaters) only # report the strongest signal if networks.__contains__(ssid): update = networks.get(ssid).get("quality") < quality if update and ssid: networks[ssid] = { 'quality': quality, 'encrypted': cell.encrypted, 'connected': self.is_connected(ssid, status) } self.ws.emit(Message("mycroft.wifi.scanned", {'networks': networks})) LOG.info("Wifi connections scanned!\n%s" % networks) @staticmethod def get_quality(quality): values = quality.split("/") return float(values[0]) / float(values[1]) def connect(self, event=None): if event and event.data: ssid = event.data.get("ssid") connected = self.is_connected(ssid) if connected: LOG.warn("Mycroft is already connected to %s" % ssid) else: self.disconnect() LOG.info("Connecting to: %s" % ssid) nid = wpa(self.iface, 'add_network') wpa(self.iface, 'set_network', nid, 'ssid', '"' + ssid + '"') if event.data.__contains__("pass"): psk = '"' + event.data.get("pass") + '"' wpa(self.iface, 'set_network', nid, 'psk', psk) else: wpa(self.iface, 'set_network', nid, 'key_mgmt', 'NONE') wpa(self.iface, 'enable', nid) connected = self.get_connected(ssid) if connected: wpa(self.iface, 'save_config') self.ws.emit(Message("mycroft.wifi.connected", {'connected': connected})) LOG.info("Connection status for %s = %s" % (ssid, connected)) def disconnect(self): status = self.get_status() nid = status.get("id") if nid: ssid = status.get("ssid") wpa(self.iface, 'disable', nid) LOG.info("Disconnecting %s id: %s" % (ssid, nid)) def get_status(self): res = cli('wpa_cli', '-i', self.iface, 'status') out = str(res.get("stdout")) if out: return dict(o.split("=") for o in out.split("\n")[:-1]) return {} def get_connected(self, ssid, retry=5): connected = self.is_connected(ssid) while not connected and retry > 0: sleep(2) retry -= 1 connected = self.is_connected(ssid) return connected def is_connected(self, ssid, status=None): status = status or self.get_status() state = status.get("wpa_state") return status.get("ssid") == ssid and state == "COMPLETED" def stop(self, event=None): LOG.info("Stopping access point...") if is_speaking(): stop_speaking() # stop any assistance being spoken self._stop_connection_monitor() self.ap.down() self.enclosure.mouth_reset() # remove "start.mycroft.ai" self.starting = False if self.server: self.server.server.shutdown() self.server.server.server_close() self.server.join() self.server = None LOG.info("Access point stopped!") def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def reset(self, event=None): """Reset the unit to the factory defaults """ LOG.info("Resetting the WPA_SUPPLICANT File") try: call( "echo '" + WPA_SUPPLICANT + "'> /etc/wpa_supplicant/wpa_supplicant.conf", shell=True) # UGLY BUT WORKS call(RM_SKILLS) except Exception as e: LOG.error("Error: {0}".format(e)) def ssh_enable(self, event=None): LOG.info("Enabling SSH") try: call('systemctl enable ssh.service', shell=True) call('systemctl start ssh.service', shell=True) except Exception as e: LOG.error("Error: {0}".format(e)) def ssh_disable(self, event=None): LOG.info("Disabling SSH") try: call('systemctl stop ssh.service', shell=True) call('systemctl disable ssh.service', shell=True) except Exception as e: LOG.error("Error: {0}".format(e))
class SkillContainer(object): def __init__(self, args): params = self.__build_params(args) if params.config: Configuration.get([params.config]) if exists(params.lib) and isdir(params.lib): sys.path.append(params.lib) sys.path.append(params.dir) self.dir = params.dir self.enable_intent = params.enable_intent self.__init_client(params) @staticmethod def __build_params(args): parser = argparse.ArgumentParser() parser.add_argument("--config", default="./mycroft.conf") parser.add_argument("dir", nargs='?', default=dirname(__file__)) parser.add_argument("--lib", default="./lib") parser.add_argument("--host", default=None) parser.add_argument("--port", default=None) parser.add_argument("--use-ssl", action='store_true', default=False) parser.add_argument("--enable-intent", action='store_true', default=False) return parser.parse_args(args) def __init_client(self, params): config = Configuration.get().get("websocket") if not params.host: params.host = config.get('host') if not params.port: params.port = config.get('port') self.ws = WebsocketClient(host=params.host, port=params.port, ssl=params.use_ssl) # Connect configuration manager to message bus to receive updates Configuration.init(self.ws) def load_skill(self): if self.enable_intent: IntentService(self.ws) skill_descriptor = create_skill_descriptor(self.dir) self.skill = load_skill(skill_descriptor, self.ws, hash(self.dir)) def run(self): try: self.ws.on('message', LOG.debug) self.ws.on('open', self.load_skill) self.ws.on('error', LOG.error) self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def stop(self): if self.skill: self.skill.shutdown()
def main(): global ws global loop ws = WebsocketClient() tts.init(ws) ConfigurationManager.init(ws) loop = RecognizerLoop() loop.on('recognizer_loop:utterance', handle_utterance) loop.on('recognizer_loop:record_begin', handle_record_begin) loop.on('recognizer_loop:wakeword', handle_wakeword) loop.on('recognizer_loop:record_end', handle_record_end) loop.on('speak', handle_speak) ws.on('open', handle_open) ws.on('speak', handle_speak) ws.on( 'multi_utterance_intent_failure', handle_multi_utterance_intent_failure) ws.on('recognizer_loop:sleep', handle_sleep) ws.on('recognizer_loop:wake_up', handle_wake_up) ws.on('mycroft.stop', handle_stop) ws.on("mycroft.paired", handle_paired) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: loop.run() except KeyboardInterrupt, e: logger.exception(e) event_thread.exit() sys.exit()
def main(): global bus global loop global config reset_sigint_handler() PIDLock("voice") bus = WebsocketClient() # Mycroft messagebus, see mycroft.messagebus Configuration.init(bus) config = Configuration.get() # Register handlers on internal RecognizerLoop bus loop = RecognizerLoop() loop.on('recognizer_loop:utterance', handle_utterance) loop.on('recognizer_loop:speech.recognition.unknown', handle_unknown) loop.on('speak', handle_speak) loop.on('recognizer_loop:record_begin', handle_record_begin) loop.on('recognizer_loop:awoken', handle_awoken) loop.on('recognizer_loop:wakeword', handle_wakeword) loop.on('recognizer_loop:record_end', handle_record_end) loop.on('recognizer_loop:no_internet', handle_no_internet) # Register handlers for events on main Mycroft messagebus bus.on('open', handle_open) bus.on('complete_intent_failure', handle_complete_intent_failure) bus.on('recognizer_loop:sleep', handle_sleep) bus.on('recognizer_loop:wake_up', handle_wake_up) bus.on('mycroft.mic.mute', handle_mic_mute) bus.on('mycroft.mic.unmute', handle_mic_unmute) bus.on('mycroft.mic.get_status', handle_mic_get_status) bus.on("mycroft.paired", handle_paired) bus.on('recognizer_loop:audio_output_start', handle_audio_start) bus.on('recognizer_loop:audio_output_end', handle_audio_end) bus.on('mycroft.stop', handle_stop) create_daemon(bus.run_forever) create_daemon(loop.run) wait_for_exit_signal()
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ _last_internet_notification = 0 def __init__(self): self.ws = WebsocketClient() ConfigurationManager.init(self.ws) self.config = ConfigurationManager.instance().get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws) self.writer = EnclosureWriter(self.serial, self.ws) # Send a message to the Arduino across the serial line asking # for a reply with version info. self.writer.write("system.version") # When the Arduino responds, it will generate this message self.ws.on("enclosure.started", self.on_arduino_responded) self.arduino_responded = False # Start a 5 second timer. If the serial port hasn't received # any acknowledgement of the "system.version" within those # 5 seconds, assume there is nothing on the other end (e.g. # we aren't running a Mark 1 with an Arduino) Timer(5, self.check_for_response).start() # Notifications from mycroft-core self.ws.on("enclosure.notify.no_internet", self.on_no_internet) def on_arduino_responded(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.arduino_responded = True # verify internet connection and prompt user on bootup if needed if not connected(): # We delay this for several seconds to ensure that the other # clients are up and connected to the messagebus in order to # receive the "speak". This was sometimes happening too # quickly and the user wasn't notified what to do. Timer(5, self._do_net_check).start() def on_no_internet(self, event=None): if connected(): # One last check to see if connection was established return if time.time()-Enclosure._last_internet_notification < 30: # don't bother the user with multiple notifications with 30 secs return Enclosure._last_internet_notification = time.time() # TODO: This should go into EnclosureMark1 subclass of Enclosure. if has_been_paired(): # Handle the translation within that code. self.ws.emit(Message("speak", { 'utterance': "This device is not connected to the Internet. " "Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu"})) else: # enter wifi-setup mode automatically self.ws.emit(Message("mycroft.wifi.start")) def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url( url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: " + self.port) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def check_for_response(self): if not self.arduino_responded: # There is nothing on the other end of the serial port # close these serial-port readers and this process self.writer.stop() self.reader.stop() self.serial.close() self.ws.close() def _do_net_check(self): # TODO: This should live in the derived Enclosure, e.g. Enclosure_Mark1 LOG.info("Checking internet connection") if not connected(): # and self.conn_monitor is None: if has_been_paired(): # TODO: Enclosure/localization self.ws.emit(Message("speak", { 'utterance': "This unit is not connected to the Internet." " Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu" })) else: # Begin the unit startup process, this is the first time it # is being run with factory defaults. # TODO: This logic should be in Enclosure_Mark1 # TODO: Enclosure/localization # Don't listen to mic during this out-of-box experience self.ws.emit(Message("mycroft.mic.mute", None)) # Kick off wifi-setup automatically self.ws.emit(Message("mycroft.wifi.start", {'msg': "Hello I am Mycroft, your new " "assistant. To assist you I need to be " "connected to the internet. You can " "either plug me in with a network cable," " or use wifi. To setup wifi ", 'allow_timeout': False}))
def gui_main(stdscr): global scr global bus global line global log_line_lr_scroll global longest_visible_line global find_str global last_key global history global screen_lock scr = stdscr init_screen() scr.keypad(1) scr.notimeout(1) bus = WebsocketClient() # Mycroft messagebus connection bus.on('speak', handle_speak) bus.on('message', handle_message) bus.on('recognizer_loop:utterance', handle_utterance) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() gui_thread = ScreenDrawThread() gui_thread.setDaemon(True) # this thread won't prevent prog from exiting gui_thread.start() hist_idx = -1 # index, from the bottom c = 0 try: while True: set_screen_dirty() try: c = scr.get_wch() # unicode char or int for special keys except KeyboardInterrupt: # User hit Ctrl+C to quit if find_str: # End the find session find_str = None rebuild_filtered_log() continue # Consumed the Ctrl+C, get next character else: c = 24 # treat as Ctrl+X (Exit) except curses.error: # This happens in odd cases, such as when you Ctrl+Z suspend # the CLI and then resume. Curses fails on get_wch(). continue if isinstance(c, int): code = c else: code = ord(c) # Convert VT100 ESC codes generated by some terminals if code == 27: # NOTE: Not sure exactly why, but the screen can get corrupted # if we draw to the screen while doing a scr.getch(). So # lock screen updates until the VT100 sequence has been # completely read. with screen_lock: scr.timeout(0) c1 = -1 start = time.time() while c1 == -1: c1 = scr.getch() if time.time()-start > 1: break # 1 second timeout waiting for ESC code c2 = -1 while c2 == -1: c2 = scr.getch() if time.time()-start > 1: # 1 second timeout break # 1 second timeout waiting for ESC code scr.timeout(-1) if c1 == 79 and c2 == 120: c = curses.KEY_UP elif c1 == 79 and c2 == 116: c = curses.KEY_LEFT elif c1 == 79 and c2 == 114: c = curses.KEY_DOWN elif c1 == 79 and c2 == 118: c = curses.KEY_RIGHT elif c1 == 79 and c2 == 121: c = curses.KEY_PPAGE # aka PgUp elif c1 == 79 and c2 == 115: c = curses.KEY_NPAGE # aka PgDn elif c1 == 79 and c2 == 119: c = curses.KEY_HOME elif c1 == 79 and c2 == 113: c = curses.KEY_END else: c = c1 if c1 != -1: last_key = str(c) + ",ESC+" + str(c1) + "+" + str(c2) code = c else: last_key = "ESC" else: if code < 33: last_key = str(code) else: last_key = str(code) if code == 27: # Hitting ESC twice clears the entry line hist_idx = -1 line = "" elif c == curses.KEY_RESIZE: # Generated by Curses when window/screen has been resized y, x = scr.getmaxyx() curses.resizeterm(y, x) # resizeterm() causes another curses.KEY_RESIZE, so # we need to capture that to prevent a loop of resizes c = scr.get_wch() elif screen_mode == SCR_HELP: # in Help mode, any key goes to next page show_next_help() continue elif c == '\n' or code == 10 or code == 13 or code == 343: # ENTER sends the typed line to be processed by Mycroft if line == "": continue if line[:1] == ":": # Lines typed like ":help" are 'commands' if handle_cmd(line[1:]) == 1: break else: # Treat this as an utterance bus.emit(Message("recognizer_loop:utterance", {'utterances': [line.strip()], 'lang': 'en-us'})) hist_idx = -1 line = "" elif code == 16 or code == 545: # Ctrl+P or Ctrl+Left (Previous) # Move up the history stack hist_idx = clamp(hist_idx + 1, -1, len(history) - 1) if hist_idx >= 0: line = history[len(history) - hist_idx - 1] else: line = "" elif code == 14 or code == 560: # Ctrl+N or Ctrl+Right (Next) # Move down the history stack hist_idx = clamp(hist_idx - 1, -1, len(history) - 1) if hist_idx >= 0: line = history[len(history) - hist_idx - 1] else: line = "" elif c == curses.KEY_LEFT: # scroll long log lines left log_line_lr_scroll += curses.COLS // 4 elif c == curses.KEY_RIGHT: # scroll long log lines right log_line_lr_scroll -= curses.COLS // 4 if log_line_lr_scroll < 0: log_line_lr_scroll = 0 elif c == curses.KEY_HOME: # HOME scrolls log lines all the way to the start log_line_lr_scroll = longest_visible_line elif c == curses.KEY_END: # END scrolls log lines all the way to the end log_line_lr_scroll = 0 elif c == curses.KEY_UP: scroll_log(False, 1) elif c == curses.KEY_DOWN: scroll_log(True, 1) elif c == curses.KEY_NPAGE: # aka PgDn # PgDn to go down a page in the logs scroll_log(True) elif c == curses.KEY_PPAGE: # aka PgUp # PgUp to go up a page in the logs scroll_log(False) elif code == 2 or code == 550: # Ctrl+B or Ctrl+PgDn scroll_log(True, max_log_lines) elif code == 20 or code == 555: # Ctrl+T or Ctrl+PgUp scroll_log(False, max_log_lines) elif code == curses.KEY_BACKSPACE or code == 127: # Backspace to erase a character in the utterance line = line[:-1] elif code == 6: # Ctrl+F (Find) line = ":find " elif code == 18: # Ctrl+R (Redraw) scr.erase() elif code == 24: # Ctrl+X (Exit) if find_str: # End the find session find_str = None rebuild_filtered_log() else: break elif code > 31 and isinstance(c, str): # Accept typed character in the utterance line += c finally: scr.erase() scr.refresh() scr = None
class Enclosure: def __init__(self): # Establish Enclosure's websocket connection to the messagebus self.bus = WebsocketClient() # Load full config Configuration.init(self.bus) config = Configuration.get() self.lang = config['lang'] self.config = config.get("enclosure") self.global_config = config # This datastore holds the data associated with the GUI provider. Data # is stored in Namespaces, so you can have: # self.datastore["namespace"]["name"] = value # Typically the namespace is a meaningless identifier, but there is a # special "SYSTEM" namespace. self.datastore = {} # self.loaded is a list, each element consists of a namespace named # tuple. # The namespace namedtuple has the properties "name" and "pages" # The name contains the namespace name as a string and pages is a # mutable list of loaded pages. # # [Namespace name, [List of loaded qml pages]] # [ # ["SKILL_NAME", ["page1.qml, "page2.qml", ... , "pageN.qml"] # [...] # ] self.loaded = [] # list of lists in order. self.explicit_move = True # Set to true to send reorder commands # Listen for new GUI clients to announce themselves on the main bus self.GUIs = {} # GUIs, either local or remote self.active_namespaces = [] self.bus.on("mycroft.gui.connected", self.on_gui_client_connected) self.register_gui_handlers() # First send any data: self.bus.on("gui.value.set", self.on_gui_set_value) self.bus.on("gui.page.show", self.on_gui_show_page) self.bus.on("gui.page.delete", self.on_gui_delete_page) self.bus.on("gui.clear.namespace", self.on_gui_delete_namespace) self.bus.on("gui.event.send", self.on_gui_send_event) def run(self): try: self.bus.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() ###################################################################### # GUI client API def send(self, *args, **kwargs): """ Send to all registered GUIs. """ for gui in self.GUIs.values(): if gui.socket: gui.socket.send(*args, **kwargs) else: LOG.error('GUI connection {} has no socket!'.format(gui)) def on_gui_send_event(self, message): """ Send an event to the GUIs. """ try: data = {'type': 'mycroft.events.triggered', 'namespace': message.data.get('__from'), 'event_name': message.data.get('event_name'), 'params': message.data.get('params')} self.send(data) except Exception as e: LOG.error('Could not send event ({})'.format(repr(e))) def on_gui_set_value(self, message): data = message.data namespace = data.get("__from", "") # Pass these values on to the GUI renderers for key in data: if key not in RESERVED_KEYS: try: self.set(namespace, key, data[key]) except Exception as e: LOG.exception(repr(e)) def set(self, namespace, name, value): """ Perform the send of the values to the connected GUIs. """ if namespace not in self.datastore: self.datastore[namespace] = {} if self.datastore[namespace].get(name) != value: self.datastore[namespace][name] = value # If the namespace is loaded send data to GUI if namespace in [l.name for l in self.loaded]: msg = {"type": "mycroft.session.set", "namespace": namespace, "data": {name: value}} self.send(msg) def on_gui_delete_page(self, message): """ Bus handler for removing pages. """ page, namespace, _ = _get_page_data(message) try: self.remove_pages(namespace, page) except Exception as e: LOG.exception(repr(e)) def on_gui_delete_namespace(self, message): """ Bus handler for removing namespace. """ try: namespace = message.data['__from'] self.remove_namespace(namespace) except Exception as e: LOG.exception(repr(e)) def on_gui_show_page(self, message): try: page, namespace, index = _get_page_data(message) # Pass the request to the GUI(s) to pull up a page template self.show(namespace, page, index) except Exception as e: LOG.exception(repr(e)) def __find_namespace(self, namespace): for i, skill in enumerate(self.loaded): if skill[0] == namespace: return i return None def __insert_pages(self, namespace, pages): """ Insert pages into the namespace Args: namespace (str): Namespace to add to pages (list): Pages (str) to insert """ LOG.debug("Inserting new pages") if not isinstance(pages, list): raise ValueError('Argument must be list of pages') self.send({"type": "mycroft.gui.list.insert", "namespace": namespace, "position": len(self.loaded[0].pages), "data": [{"url": p} for p in pages] }) # Insert the pages into local reprensentation as well. self.loaded[0].pages += pages def __remove_page(self, namespace, pos): """ Delete page. Args: namespace (str): Namespace to remove from pos (int): Page position to remove """ LOG.debug("Deleting {} from {}".format(pos, namespace)) self.send({"type": "mycroft.gui.list.remove", "namespace": namespace, "position": pos, "items_number": 1 }) # Remove the page from the local reprensentation as well. self.loaded[0].pages.pop(pos) def __insert_new_namespace(self, namespace, pages): """ Insert new namespace and pages. This first sends a message adding a new namespace at the highest priority (position 0 in the namespace stack) Args: namespace (str): The skill namespace to create pages (str): Pages to insert (name matches QML) """ LOG.debug("Inserting new namespace") self.send({"type": "mycroft.session.list.insert", "namespace": "mycroft.system.active_skills", "position": 0, "data": [{"skill_id": namespace}] }) # Load any already stored Data data = self.datastore.get(namespace, {}) for key in data: msg = {"type": "mycroft.session.set", "namespace": namespace, "data": {key: data[key]}} self.send(msg) LOG.debug("Inserting new page") self.send({"type": "mycroft.gui.list.insert", "namespace": namespace, "position": 0, "data": [{"url": p} for p in pages] }) # Make sure the local copy is updated self.loaded.insert(0, Namespace(namespace, pages)) def __move_namespace(self, from_pos, to_pos): """ Move an existing namespace to a new position in the stack. Args: from_pos (int): Position in the stack to move from to_pos (int): Position to move to """ LOG.debug("Activating existing namespace") # Seems like the namespace is moved to the top automatically when # a page change is done. Deactivating this for now. if self.explicit_move: LOG.debug("move {} to {}".format(from_pos, to_pos)) self.send({"type": "mycroft.session.list.move", "namespace": "mycroft.system.active_skills", "from": from_pos, "to": to_pos, "items_number": 1}) # Move the local representation of the skill from current # position to position 0. self.loaded.insert(to_pos, self.loaded.pop(from_pos)) def __switch_page(self, namespace, pages): """ Switch page to an already loaded page. Args: pages (list): pages (str) to switch to namespace (str): skill namespace """ try: num = self.loaded[0].pages.index(pages[0]) except Exception as e: LOG.exception(repr(e)) num = 0 LOG.debug('Switching to already loaded page at ' 'index {} in namespace {}'.format(num, namespace)) self.send({"type": "mycroft.events.triggered", "namespace": namespace, "event_name": "page_gained_focus", "data": {"number": num}}) def show(self, namespace, page, index): """ Show a page and load it as needed. Args: page (str or list): page(s) to show namespace (str): skill namespace index (int): ??? TODO: Unused in code ??? TODO: - Update sync to match. - Separate into multiple functions/methods """ LOG.debug("GUIConnection activating: " + namespace) pages = page if isinstance(page, list) else [page] # find namespace among loaded namespaces try: index = self.__find_namespace(namespace) if index is None: # This namespace doesn't exist, insert them first so they're # shown. self.__insert_new_namespace(namespace, pages) return else: # Namespace exists if index > 0: # Namespace is inactive, activate it by moving it to # position 0 self.__move_namespace(index, 0) # Find if any new pages needs to be inserted new_pages = [p for p in pages if p not in self.loaded[0].pages] if new_pages: self.__insert_pages(namespace, new_pages) else: # No new pages, just switch self.__switch_page(namespace, pages) except Exception as e: LOG.exception(repr(e)) def remove_namespace(self, namespace): """ Remove namespace. Args: namespace (str): namespace to remove """ index = self.__find_namespace(namespace) if index is None: return else: LOG.debug("Removing namespace {} at {}".format(namespace, index)) self.send({"type": "mycroft.session.list.remove", "namespace": "mycroft.system.active_skills", "position": index, "items_number": 1 }) # Remove namespace from loaded namespaces self.loaded.pop(index) def remove_pages(self, namespace, pages): """ Remove the listed pages from the provided namespace. Args: namespace (str): The namespace to modify pages (list): List of page names (str) to delete """ try: index = self.__find_namespace(namespace) if index is None: return else: # Remove any pages that doesn't exist in the namespace pages = [p for p in pages if p in self.loaded[index].pages] # Make sure to remove pages from the back indexes = [self.loaded[index].pages.index(p) for p in pages] indexes = sorted(indexes) indexes.reverse() for page_index in indexes: self.__remove_page(namespace, page_index) except Exception as e: LOG.exception(repr(e)) ###################################################################### # GUI client socket # # The basic mechanism is: # 1) GUI client announces itself on the main messagebus # 2) Mycroft prepares a port for a socket connection to this GUI # 3) The port is announced over the messagebus # 4) The GUI connects on the socket # 5) Connection persists for graphical interaction indefinitely # # If the connection is lost, it must be renegotiated and restarted. def on_gui_client_connected(self, message): # GUI has announced presence LOG.debug("on_gui_client_connected") gui_id = message.data.get("gui_id") # Spin up a new communication socket for this GUI if gui_id in self.GUIs: # TODO: Close it? pass self.GUIs[gui_id] = GUIConnection(gui_id, self.global_config, self.callback_disconnect, self) LOG.debug("Heard announcement from gui_id: {}".format(gui_id)) # Announce connection, the GUI should connect on it soon self.bus.emit(Message("mycroft.gui.port", {"port": self.GUIs[gui_id].port, "gui_id": gui_id})) def callback_disconnect(self, gui_id): LOG.info("Disconnecting!") # TODO: Whatever is needed to kill the websocket instance LOG.info(self.GUIs.keys()) LOG.info('deleting: {}'.format(gui_id)) if gui_id in self.GUIs: del self.GUIs[gui_id] else: LOG.warning('ID doesn\'t exist') def register_gui_handlers(self): # TODO: Register handlers for standard (Mark 1) events # self.bus.on('enclosure.eyes.on', self.on) # self.bus.on('enclosure.eyes.off', self.off) # self.bus.on('enclosure.eyes.blink', self.blink) # self.bus.on('enclosure.eyes.narrow', self.narrow) # self.bus.on('enclosure.eyes.look', self.look) # self.bus.on('enclosure.eyes.color', self.color) # self.bus.on('enclosure.eyes.level', self.brightness) # self.bus.on('enclosure.eyes.volume', self.volume) # self.bus.on('enclosure.eyes.spin', self.spin) # self.bus.on('enclosure.eyes.timedspin', self.timed_spin) # self.bus.on('enclosure.eyes.reset', self.reset) # self.bus.on('enclosure.eyes.setpixel', self.set_pixel) # self.bus.on('enclosure.eyes.fill', self.fill) # self.bus.on('enclosure.mouth.reset', self.reset) # self.bus.on('enclosure.mouth.talk', self.talk) # self.bus.on('enclosure.mouth.think', self.think) # self.bus.on('enclosure.mouth.listen', self.listen) # self.bus.on('enclosure.mouth.smile', self.smile) # self.bus.on('enclosure.mouth.viseme', self.viseme) # self.bus.on('enclosure.mouth.text', self.text) # self.bus.on('enclosure.mouth.display', self.display) # self.bus.on('enclosure.mouth.display_image', self.display_image) # self.bus.on('enclosure.weather.display', self.display_weather) # self.bus.on('recognizer_loop:record_begin', self.mouth.listen) # self.bus.on('recognizer_loop:record_end', self.mouth.reset) # self.bus.on('recognizer_loop:audio_output_start', self.mouth.talk) # self.bus.on('recognizer_loop:audio_output_end', self.mouth.reset) pass
class TestMessagebusMethods(unittest.TestCase): """This class is for testing the messsagebus. It currently only tests send and receive. The tests could include more. """ def setUp(self): """ This sets up for testing the message buss This requires starting the mycroft service and creating two WebsocketClient object to talk with eachother. Not this is threaded and will require cleanup """ # start the mycroft service. and get the pid of the script. self.pid = Popen(["python", "mycroft/messagebus/service/main.py"]).pid # delay to allow the service to start up. time.sleep(10) # Create the two web clients self.ws1 = WebsocketClient() self.ws2 = WebsocketClient() # init the flags for handler's self.handle1 = False self.handle2 = False # Start threads to handle websockets Thread(target=self.ws1.run_forever).start() Thread(target=self.ws2.run_forever).start() # Sleep to give the websockets to startup before adding handlers time.sleep(10) # Setup handlers for each of the messages. self.ws1.on('ws1.message', self.onHandle1) self.ws2.on('ws2.message', self.onHandle2) def onHandle1(self, event): """This is the handler for ws1.message This for now simply sets a flag to true when received. Args: event(Message): this is the message received """ self.handle1 = True def onHandle2(self, event): """This is the handler for ws2.message This for now simply sets a flag to true when received. Args: event(Message): this is the message received """ self.handle2 = True def tearDown(self): """This is the clean up for the tests This will close the websockets ending the threads then kill the mycroft service that was started in setUp. """ self.ws1.close() self.ws2.close() retcode = call(["kill", "-9", str(self.pid)]) def test_ClientServer(self): """This is the test to send a message from each of the websockets to the other. """ # Send the messages self.ws2.emit(Message('ws1.message')) self.ws1.emit(Message('ws2.message')) # allow time for messages to be processed time.sleep(10) # Check that both of the handlers were called. self.assertTrue(self.handle1) self.assertTrue(self.handle2)
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ _last_internet_notification = 0 def __init__(self): self.ws = WebsocketClient() ConfigurationManager.init(self.ws) self.config = ConfigurationManager.get().get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws) self.writer = EnclosureWriter(self.serial, self.ws) # Send a message to the Arduino across the serial line asking # for a reply with version info. self.writer.write("system.version") # When the Arduino responds, it will generate this message self.ws.on("enclosure.started", self.on_arduino_responded) self.arduino_responded = False # Start a 5 second timer. If the serial port hasn't received # any acknowledgement of the "system.version" within those # 5 seconds, assume there is nothing on the other end (e.g. # we aren't running a Mark 1 with an Arduino) Timer(5, self.check_for_response).start() # Notifications from mycroft-core self.ws.on("enclosure.notify.no_internet", self.on_no_internet) def on_arduino_responded(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.arduino_responded = True # verify internet connection and prompt user on bootup if needed if not connected(): # We delay this for several seconds to ensure that the other # clients are up and connected to the messagebus in order to # receive the "speak". This was sometimes happening too # quickly and the user wasn't notified what to do. Timer(5, self.on_no_internet).start() def on_no_internet(self, event=None): if connected(): # One last check to see if connection was established return if time.time()-Enclosure._last_internet_notification < 30: # don't bother the user with multiple notifications with 30 secs return Enclosure._last_internet_notification = time.time() # TODO: This should go into EnclosureMark1 subclass of Enclosure. # Handle the translation within that code. self.ws.emit(Message("speak", { 'utterance': "This device is not connected to the Internet. " "Either plug in a network cable or hold the button " "on top for two seconds, then select wifi from the " "menu"})) def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url( url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: " + self.port) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def check_for_response(self): if not self.arduino_responded: # There is nothing on the other end of the serial port # close these serial-port readers and this process self.writer.stop() self.reader.stop() self.serial.close() self.ws.close()
class TestMessagebusMethods(unittest.TestCase): """This class is for testing the messsagebus. It currently only tests send and receive. The tests could include more. """ def setUp(self): """ This sets up for testing the message buss This requires starting the mycroft service and creating two WebsocketClient object to talk with eachother. Not this is threaded and will require cleanup """ # start the mycroft service. and get the pid of the script. self.pid = Popen(["python", "mycroft/messagebus/service/main.py"]).pid # Create the two web clients self.ws1 = WebsocketClient() self.ws2 = WebsocketClient() # init the flags for handler's self.handle1 = False self.handle2 = False # Start threads to handle websockets Thread(target=self.ws1.run_forever).start() Thread(target=self.ws2.run_forever).start() # Setup handlers for each of the messages. self.ws1.on('ws1.message', self.onHandle1) self.ws2.on('ws2.message', self.onHandle2) def onHandle1(self, event): """This is the handler for ws1.message This for now simply sets a flag to true when received. Args: event(Message): this is the message received """ self.handle1 = True def onHandle2(self, event): """This is the handler for ws2.message This for now simply sets a flag to true when received. Args: event(Message): this is the message received """ self.handle2 = True def tearDown(self): """This is the clean up for the tests This will close the websockets ending the threads then kill the mycroft service that was started in setUp. """ self.ws1.close() self.ws2.close() retcode = call(["kill", "-9", str(self.pid)]) def test_ClientServer(self): """This is the test to send a message from each of the websockets to the other. """ # Send the messages self.ws2.emit(Message('ws1.message')) self.ws1.emit(Message('ws2.message')) # allow time for messages to be processed time.sleep(0.2) # Check that both of the handlers were called. self.assertTrue(self.handle1) self.assertTrue(self.handle2)
class Enclosure(): def __init__(self): # Establish Enclosure's websocket connection to the messagebus self.bus = WebsocketClient() # Load full config Configuration.init(self.bus) config = Configuration.get() self.lang = config['lang'] self.config = config.get("enclosure") self.global_config = config # Listen for new GUI clients to announce themselves on the main bus self.GUIs = {} # GUIs, either local or remote self.active_namespaces = [] self.bus.on("mycroft.gui.connected", self.on_gui_client_connected) self.register_gui_handlers() # First send any data: self.bus.on("gui.value.set", self.on_gui_set_value) self.bus.on("gui.page.show", self.on_gui_show_page) def run(self): try: self.bus.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() ###################################################################### # GUI client API def _gui_activate(self, namespace, move_to_top=False): if not namespace: return if namespace not in self.active_namespaces: if move_to_top: self.active_namespaces.insert(0, namespace) else: self.active_namespaces.append(namespace) elif move_to_top: self.active_namespaces.remove(namespace) self.active_namespaces.insert(0, namespace) # TODO: Keep a timestamp and auto-cull? def on_gui_set_value(self, message): data = message.data namespace = data.get("__from", "") self._gui_activate(namespace) # Pass these values on to the GUI renderers for id in self.GUIs: for key in data: if key != "__from": self.GUIs[id].set(namespace, key, data[key]) def on_gui_show_page(self, message): data = message.data # Note: 'page' can be either a string or a list of strings if 'page' not in data: return if 'index' in data: index = data['index'] else: index = 0 namespace = data.get("__from", "") self._gui_activate(namespace, move_to_top=True) # Pass the request to the GUI(s) to pull up a page template for id in self.GUIs: self.GUIs[id].show(namespace, data['page'], index) ###################################################################### # GUI client socket # # The basic mechanism is: # 1) GUI client announces itself on the main messagebus # 2) Mycroft prepares a port for a socket connection to this GUI # 3) The port is announced over the messagebus # 4) The GUI connects on the socket # 5) Connection persists for graphical interaction indefinitely # # If the connection is lost, it must be renegotiated and restarted. def on_gui_client_connected(self, message): # GUI has announced presence DEBUG("on_gui_client_connected") gui_id = message.data.get("gui_id") # Spin up a new communication socket for this GUI if gui_id in self.GUIs: # TODO: Close it? pass self.GUIs[gui_id] = GUIConnection(gui_id, self.global_config, self.callback_disconnect, self) DEBUG("Heard announcement from gui_id: {}".format(gui_id)) # Announce connection, the GUI should connect on it soon self.bus.emit( Message("mycroft.gui.port", { "port": self.GUIs[gui_id].port, "gui_id": gui_id })) def callback_disconnect(self, gui_id): DEBUG("Disconnecting!") # TODO: Whatever is needed to kill the websocket instance del self.GUIs[gui_id] def register_gui_handlers(self): # TODO: Register handlers for standard (Mark 1) events # self.bus.on('enclosure.eyes.on', self.on) # self.bus.on('enclosure.eyes.off', self.off) # self.bus.on('enclosure.eyes.blink', self.blink) # self.bus.on('enclosure.eyes.narrow', self.narrow) # self.bus.on('enclosure.eyes.look', self.look) # self.bus.on('enclosure.eyes.color', self.color) # self.bus.on('enclosure.eyes.level', self.brightness) # self.bus.on('enclosure.eyes.volume', self.volume) # self.bus.on('enclosure.eyes.spin', self.spin) # self.bus.on('enclosure.eyes.timedspin', self.timed_spin) # self.bus.on('enclosure.eyes.reset', self.reset) # self.bus.on('enclosure.eyes.setpixel', self.set_pixel) # self.bus.on('enclosure.eyes.fill', self.fill) # self.bus.on('enclosure.mouth.reset', self.reset) # self.bus.on('enclosure.mouth.talk', self.talk) # self.bus.on('enclosure.mouth.think', self.think) # self.bus.on('enclosure.mouth.listen', self.listen) # self.bus.on('enclosure.mouth.smile', self.smile) # self.bus.on('enclosure.mouth.viseme', self.viseme) # self.bus.on('enclosure.mouth.text', self.text) # self.bus.on('enclosure.mouth.display', self.display) # self.bus.on('enclosure.mouth.display_image', self.display_image) # self.bus.on('enclosure.weather.display', self.display_weather) # self.bus.on('recognizer_loop:record_begin', self.mouth.listen) # self.bus.on('recognizer_loop:record_end', self.mouth.reset) # self.bus.on('recognizer_loop:audio_output_start', self.mouth.talk) # self.bus.on('recognizer_loop:audio_output_end', self.mouth.reset) pass
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ def __init__(self): self.ws = WebsocketClient() ConfigurationManager.init(self.ws) self.config = ConfigurationManager.get().get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws) self.writer = EnclosureWriter(self.serial, self.ws) self.writer.write("system.version") self.ws.on("enclosure.start", self.start) self.started = False Timer(5, self.stop).start() # WHY? This at least needs an explaination, this is non-obvious behavior def start(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.started = True def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url( url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: " + self.port) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def stop(self): if not self.started: self.writer.stop() self.reader.stop() self.serial.close() self.ws.close()
def main(): global ws global loop global config global tts global tts_hash lock = PIDLock("voice") ws = WebsocketClient() config = ConfigurationManager.get() tts = TTSFactory.create() tts.init(ws) tts_hash = config.get('tts') ConfigurationManager.init(ws) loop = RecognizerLoop() loop.on('recognizer_loop:utterance', handle_utterance) loop.on('recognizer_loop:record_begin', handle_record_begin) loop.on('recognizer_loop:wakeword', handle_wakeword) loop.on('recognizer_loop:record_end', handle_record_end) loop.on('speak', handle_speak) loop.on('recognizer_loop:no_internet', handle_no_internet) ws.on('open', handle_open) ws.on('speak', handle_speak) ws.on( 'multi_utterance_intent_failure', handle_multi_utterance_intent_failure) ws.on('recognizer_loop:sleep', handle_sleep) ws.on('recognizer_loop:wake_up', handle_wake_up) ws.on('mycroft.mic.mute', handle_mic_mute) ws.on('mycroft.mic.unmute', handle_mic_unmute) ws.on('mycroft.stop', handle_stop) ws.on("mycroft.paired", handle_paired) ws.on('recognizer_loop:audio_output_start', handle_audio_start) ws.on('recognizer_loop:audio_output_end', handle_audio_end) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: loop.run() except KeyboardInterrupt, e: tts.playback.stop() tts.playback.join() logger.exception(e) sys.exit()
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ def __init__(self): self.ws = WebsocketClient() ConfigurationManager.init(self.ws) self.config = ConfigurationManager.get().get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws) self.writer = EnclosureWriter(self.serial, self.ws) self.writer.write("system.version") self.ws.on("enclosure.start", self.start) self.started = False Timer(5, self.stop).start() # WHY? This at least # needs an explanation, this is non-obvious behavior def start(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.started = True def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url(url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: " + self.port) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def stop(self): if not self.started: self.writer.stop() self.reader.stop() self.serial.close() self.ws.close()
def main(): global ws global loop ws = WebsocketClient() tts.init(ws) ConfigurationManager.init(ws) loop = RecognizerLoop() loop.on('recognizer_loop:utterance', handle_utterance) loop.on('recognizer_loop:record_begin', handle_record_begin) loop.on('recognizer_loop:wakeword', handle_wakeword) loop.on('recognizer_loop:record_end', handle_record_end) loop.on('speak', handle_speak) ws.on('open', handle_open) ws.on('speak', handle_speak) ws.on('multi_utterance_intent_failure', handle_multi_utterance_intent_failure) ws.on('recognizer_loop:sleep', handle_sleep) ws.on('recognizer_loop:wake_up', handle_wake_up) ws.on('mycroft.stop', handle_stop) ws.on("mycroft.paired", handle_paired) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: loop.run() except KeyboardInterrupt, e: logger.exception(e) event_thread.exit() sys.exit()
def main(): global ws global loop global config lock = PIDLock("voice") ws = WebsocketClient() config = Configuration.get() Configuration.init(ws) loop = RecognizerLoop() loop.on('recognizer_loop:utterance', handle_utterance) loop.on('speak', handle_speak) loop.on('recognizer_loop:record_begin', handle_record_begin) loop.on('recognizer_loop:wakeword', handle_wakeword) loop.on('recognizer_loop:record_end', handle_record_end) loop.on('recognizer_loop:no_internet', handle_no_internet) ws.on('open', handle_open) ws.on('complete_intent_failure', handle_complete_intent_failure) ws.on('recognizer_loop:sleep', handle_sleep) ws.on('recognizer_loop:wake_up', handle_wake_up) ws.on('mycroft.mic.mute', handle_mic_mute) ws.on('mycroft.mic.unmute', handle_mic_unmute) ws.on("mycroft.paired", handle_paired) ws.on('recognizer_loop:audio_output_start', handle_audio_start) ws.on('recognizer_loop:audio_output_end', handle_audio_end) ws.on('mycroft.stop', handle_stop) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() try: loop.run() except KeyboardInterrupt, e: LOG.exception(e) sys.exit()
class Enclosure(object): """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ _last_internet_notification = 0 def __init__(self): self.ws = WebsocketClient() self.ws.on("open", self.on_ws_open) Configuration.init(self.ws) global_config = Configuration.get() self.lang = global_config['lang'] self.config = global_config.get("enclosure") self.__init_serial() self.reader = EnclosureReader(self.serial, self.ws, self.lang) self.writer = EnclosureWriter(self.serial, self.ws) # initiates the web sockets on display manager # NOTE: this is a temporary place to initiate display manager sockets initiate_display_manager_ws() def on_ws_open(self, event=None): # Mark 1 auto-detection: # # Prepare to receive message when the Arduino responds to the # following "system.version" self.ws.on("enclosure.started", self.on_arduino_responded) self.arduino_responded = False # Send a message to the Arduino across the serial line asking # for a reply with version info. self.writer.write("system.version") # Start a 5 second timer. If the serial port hasn't received # any acknowledgement of the "system.version" within those # 5 seconds, assume there is nothing on the other end (e.g. # we aren't running a Mark 1 with an Arduino) Timer(5, self.check_for_response).start() # Notifications from mycroft-core self.ws.on("enclosure.notify.no_internet", self.on_no_internet) def on_arduino_responded(self, event=None): self.eyes = EnclosureEyes(self.ws, self.writer) self.mouth = EnclosureMouth(self.ws, self.writer) self.system = EnclosureArduino(self.ws, self.writer) self.weather = EnclosureWeather(self.ws, self.writer) self.__register_events() self.__reset() self.arduino_responded = True # verify internet connection and prompt user on bootup if needed if not connected(): # We delay this for several seconds to ensure that the other # clients are up and connected to the messagebus in order to # receive the "speak". This was sometimes happening too # quickly and the user wasn't notified what to do. Timer(5, self._do_net_check).start() Timer(60, self._hack_check_for_duplicates).start() def on_no_internet(self, event=None): if connected(): # One last check to see if connection was established return if time.time() - Enclosure._last_internet_notification < 30: # don't bother the user with multiple notifications with 30 secs return Enclosure._last_internet_notification = time.time() # TODO: This should go into EnclosureMark1 subclass of Enclosure. if has_been_paired(): # Handle the translation within that code. self.ws.emit(Message("speak", { 'utterance': "This device is not connected to the Internet. " "Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu"})) else: # enter wifi-setup mode automatically self.ws.emit(Message('system.wifi.setup', {'lang': self.lang})) def __init_serial(self): try: self.port = self.config.get("port") self.rate = self.config.get("rate") self.timeout = self.config.get("timeout") self.serial = serial.serial_for_url( url=self.port, baudrate=self.rate, timeout=self.timeout) LOG.info("Connected to: %s rate: %s timeout: %s" % (self.port, self.rate, self.timeout)) except: LOG.error("Impossible to connect to serial port: "+str(self.port)) raise def __register_events(self): self.ws.on('enclosure.mouth.events.activate', self.__register_mouth_events) self.ws.on('enclosure.mouth.events.deactivate', self.__remove_mouth_events) self.ws.on('enclosure.reset', self.__reset) self.__register_mouth_events() def __register_mouth_events(self, event=None): self.ws.on('recognizer_loop:record_begin', self.mouth.listen) self.ws.on('recognizer_loop:record_end', self.mouth.reset) self.ws.on('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self, event=None): self.ws.remove('recognizer_loop:record_begin', self.mouth.listen) self.ws.remove('recognizer_loop:record_end', self.mouth.reset) self.ws.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.ws.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __reset(self, event=None): # Reset both the mouth and the eye elements to indicate the unit is # ready for input. self.writer.write("eyes.reset") self.writer.write("mouth.reset") def speak(self, text): self.ws.emit(Message("speak", {'utterance': text})) def run(self): try: self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop() def check_for_response(self): if not self.arduino_responded: # There is nothing on the other end of the serial port # close these serial-port readers and this process self.writer.stop() self.reader.stop() self.serial.close() self.ws.close() def _handle_pairing_complete(self, Message): """ Handler for 'mycroft.paired', unmutes the mic after the pairing is complete. """ self.ws.emit(Message("mycroft.mic.unmute")) def _do_net_check(self): # TODO: This should live in the derived Enclosure, e.g. Enclosure_Mark1 LOG.info("Checking internet connection") if not connected(): # and self.conn_monitor is None: if has_been_paired(): # TODO: Enclosure/localization self.speak("This unit is not connected to the Internet. " "Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu") else: # Begin the unit startup process, this is the first time it # is being run with factory defaults. # TODO: This logic should be in Enclosure_Mark1 # TODO: Enclosure/localization # Don't listen to mic during this out-of-box experience self.ws.emit(Message("mycroft.mic.mute")) # Setup handler to unmute mic at the end of on boarding # i.e. after pairing is complete self.ws.once('mycroft.paired', self._handle_pairing_complete) self.speak(mycroft.dialog.get('mycroft.intro')) wait_while_speaking() time.sleep(2) # a pause sounds better than just jumping in # Kick off wifi-setup automatically data = {'allow_timeout': False, 'lang': self.lang} self.ws.emit(Message('system.wifi.setup', data)) def _hack_check_for_duplicates(self): # TEMPORARY HACK: Look for multiple instance of the # mycroft-speech-client and/or mycroft-skills services, which could # happen when upgrading a shipping Mark 1 from release 0.8.17 or # before. When found, force the unit to reboot. import psutil LOG.info("Hack to check for duplicate service instances") count_instances = 0 needs_reboot = False for process in psutil.process_iter(): if process.cmdline() == ['python2.7', '/usr/local/bin/mycroft-speech-client']: count_instances += 1 if (count_instances > 1): LOG.info("Duplicate mycroft-speech-client found") needs_reboot = True count_instances = 0 for process in psutil.process_iter(): if process.cmdline() == ['python2.7', '/usr/local/bin/mycroft-skills']: count_instances += 1 if (count_instances > 1): LOG.info("Duplicate mycroft-skills found") needs_reboot = True if needs_reboot: LOG.info("Hack reboot...") self.reader.process("unit.reboot") self.ws.emit(Message("enclosure.eyes.spin")) self.ws.emit(Message("enclosure.mouth.reset"))
def gui_main(stdscr): global scr global ws global line global log_line_lr_scroll global longest_visible_line global find_str global last_key scr = stdscr init_screen() ws = WebsocketClient() ws.on('speak', handle_speak) ws.on('message', handle_message) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() history = [] hist_idx = -1 # index, from the bottom try: input = "" while True: draw_screen() c = scr.getch() # Convert VT100 ESC codes generated by some terminals if c == 27: c1 = scr.getch() c2 = scr.getch() if c1 == 79 and c2 == 120: c = curses.KEY_UP elif c1 == 79 and c2 == 116: c = curses.KEY_LEFT elif c1 == 79 and c2 == 114: c = curses.KEY_DOWN elif c1 == 79 and c2 == 118: c = curses.KEY_RIGHT elif c1 == 79 and c2 == 121: c = curses.KEY_PPAGE # aka PgUp elif c1 == 79 and c2 == 115: c = curses.KEY_NPAGE # aka PgDn elif c1 == 79 and c2 == 119: c = curses.KEY_HOME elif c1 == 79 and c2 == 113: c = curses.KEY_END else: c = c2 last_key = str(c) + ",ESC+" + str(c1) + "+" + str(c2) else: last_key = str(c) if c == curses.KEY_ENTER or c == 10 or c == 13: # ENTER sends the typed line to be processed by Mycroft if line == "": continue if line[:1] == ":": # Lines typed like ":help" are 'commands' if handle_cmd(line[1:]) == 1: break else: # Treat this as an utterance history.append(line) chat.append(line) ws.emit( Message("recognizer_loop:utterance", { 'utterances': [line.strip()], 'lang': 'en-us' })) hist_idx = -1 line = "" elif c == 16 or c == 545: # Ctrl+P or Ctrl+Left (Previous) # Move up the history stack hist_idx = clamp(hist_idx + 1, -1, len(history) - 1) if hist_idx >= 0: line = history[len(history) - hist_idx - 1] else: line = "" elif c == 14 or c == 560: # Ctrl+N or Ctrl+Right (Next) # Move down the history stack hist_idx = clamp(hist_idx - 1, -1, len(history) - 1) if hist_idx >= 0: line = history[len(history) - hist_idx - 1] else: line = "" elif c == curses.KEY_LEFT: # scroll long log lines left log_line_lr_scroll += curses.COLS // 4 elif c == curses.KEY_RIGHT: # scroll long log lines right log_line_lr_scroll -= curses.COLS // 4 if log_line_lr_scroll < 0: log_line_lr_scroll = 0 elif c == curses.KEY_HOME: # HOME scrolls log lines all the way to the start log_line_lr_scroll = longest_visible_line elif c == curses.KEY_END: # END scrolls log lines all the way to the end log_line_lr_scroll = 0 elif c == curses.KEY_UP: scroll_log(False, 1) elif c == curses.KEY_DOWN: scroll_log(True, 1) elif c == curses.KEY_NPAGE: # aka PgDn # PgDn to go down a page in the logs scroll_log(True) elif c == curses.KEY_PPAGE: # aka PgUp # PgUp to go up a page in the logs scroll_log(False) elif c == curses.KEY_RESIZE: # Generated by Curses when window/screen has been resized y, x = scr.getmaxyx() curses.resizeterm(y, x) # resizeterm() causes another curses.KEY_RESIZE, so # we need to capture that to prevent a loop of resizes c = scr.getch() elif c == curses.KEY_BACKSPACE or c == 127: # Backspace to erase a character in the utterance line = line[:-1] elif c == 6: # Ctrl+F (Find) line = ":find " elif c == 24: # Ctrl+X (Exit) if find_str: # End the find session find_str = None rebuild_filtered_log() elif curses.ascii.isascii(c): # Accept typed character in the utterance line += chr(c) # DEBUG: Uncomment the following code to see what key codes # are generated when an unknown key is pressed. # else: # line += str(c) except KeyboardInterrupt as e: # User hit Ctrl+C to quit pass except KeyboardInterrupt as e: LOG.exception(e) finally: scr.erase() scr.refresh() scr = None pass
class Enclosure: """ Serves as a communication interface between Arduino and Mycroft Core. ``Enclosure`` initializes and aggregates all enclosures implementation. E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` It also listens to the basis events in order to perform those core actions on the unit. E.g. Start and Stop talk animation """ def __init__(self): self.__init_serial() self.client = WebsocketClient() self.reader = EnclosureReader(self.serial, self.client) self.writer = EnclosureWriter(self.serial, self.client) self.eyes = EnclosureEyes(self.client, self.writer) self.mouth = EnclosureMouth(self.client, self.writer) self.system = EnclosureArduino(self.client, self.writer) self.weather = EnclosureWeather(self.client, self.writer) self.__register_events() def setup(self): must_upload = self.config.get('must_upload') if must_upload is not None and str2bool(must_upload): ConfigurationManager.set('enclosure', 'must_upload', False) time.sleep(5) self.client.emit(Message("speak", metadata={ 'utterance': "I am currently uploading to the arduino."})) self.client.emit(Message("speak", metadata={ 'utterance': "I will be finished in just a moment."})) self.upload_hex() self.client.emit(Message("speak", metadata={ 'utterance': "Arduino programing complete."})) must_start_test = self.config.get('must_start_test') if must_start_test is not None and str2bool(must_start_test): ConfigurationManager.set('enclosure', 'must_start_test', False) time.sleep(0.5) # Ensure arduino has booted self.client.emit(Message("speak", metadata={ 'utterance': "Begining hardware self test."})) self.writer.write("test.begin") @staticmethod def upload_hex(): old_path = os.getcwd() try: os.chdir('/opt/enclosure/') subprocess.check_call('./upload.sh') finally: os.chdir(old_path) def __init_serial(self): try: self.config = ConfigurationManager.get().get("enclosure") self.port = self.config.get("port") self.rate = int(self.config.get("rate")) self.timeout = int(self.config.get("timeout")) self.serial = serial.serial_for_url( url=self.port, baudrate=self.rate, timeout=self.timeout) LOGGER.info( "Connected to: " + self.port + " rate: " + str(self.rate) + " timeout: " + str(self.timeout)) except: LOGGER.error( "It is not possible to connect to serial port: " + self.port) raise def __register_events(self): self.client.on('mycroft.paired', self.__update_events) self.client.on('enclosure.mouth.listeners', self.__mouth_listeners) self.__register_mouth_events() def __mouth_listeners(self, event=None): if event and event.metadata: active = event.metadata['active'] if active: self.__register_mouth_events() else: self.__remove_mouth_events() def __register_mouth_events(self): self.client.on('recognizer_loop:record_begin', self.mouth.listen) self.client.on('recognizer_loop:record_end', self.mouth.reset) self.client.on('recognizer_loop:audio_output_start', self.mouth.talk) self.client.on('recognizer_loop:audio_output_end', self.mouth.reset) def __remove_mouth_events(self): self.client.remove('recognizer_loop:record_begin', self.mouth.listen) self.client.remove('recognizer_loop:record_end', self.mouth.reset) self.client.remove('recognizer_loop:audio_output_start', self.mouth.talk) self.client.remove('recognizer_loop:audio_output_end', self.mouth.reset) def __update_events(self, event=None): if event and event.metadata: if event.metadata.get('paired', False): self.__register_mouth_events() else: self.__remove_mouth_events() def run(self): try: self.client.run_forever() except Exception as e: LOGGER.error("Client error: {0}".format(e)) self.stop() def stop(self): self.writer.stop() self.reader.stop() self.serial.close()
def gui_main(stdscr): global scr global ws global line global log_line_lr_scroll global longest_visible_line global find_str global last_key scr = stdscr init_screen() ws = WebsocketClient() ws.on('speak', handle_speak) ws.on('message', handle_message) event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() history = [] hist_idx = -1 # index, from the bottom try: input = "" while True: draw_screen() c = scr.getch() # Convert VT100 ESC codes generated by some terminals if c == 27: c1 = scr.getch() c2 = scr.getch() if c1 == 79 and c2 == 120: c = curses.KEY_UP elif c1 == 79 and c2 == 116: c = curses.KEY_LEFT elif c1 == 79 and c2 == 114: c = curses.KEY_DOWN elif c1 == 79 and c2 == 118: c = curses.KEY_RIGHT elif c1 == 79 and c2 == 121: c = curses.KEY_PPAGE # aka PgUp elif c1 == 79 and c2 == 115: c = curses.KEY_NPAGE # aka PgDn elif c1 == 79 and c2 == 119: c = curses.KEY_HOME elif c1 == 79 and c2 == 113: c = curses.KEY_END else: c = c2 last_key = str(c)+",ESC+"+str(c1)+"+"+str(c2) else: last_key = str(c) if c == curses.KEY_ENTER or c == 10 or c == 13: # ENTER sends the typed line to be processed by Mycroft if line == "": continue if line[:1] == ":": # Lines typed like ":help" are 'commands' if handle_cmd(line[1:]) == 1: break else: # Treat this as an utterance history.append(line) chat.append(line) ws.emit(Message("recognizer_loop:utterance", {'utterances': [line.strip()], 'lang': 'en-us'})) hist_idx = -1 line = "" elif c == 16 or c == 545: # Ctrl+P or Ctrl+Left (Previous) # Move up the history stack hist_idx = clamp(hist_idx + 1, -1, len(history) - 1) if hist_idx >= 0: line = history[len(history) - hist_idx - 1] else: line = "" elif c == 14 or c == 560: # Ctrl+N or Ctrl+Right (Next) # Move down the history stack hist_idx = clamp(hist_idx - 1, -1, len(history) - 1) if hist_idx >= 0: line = history[len(history) - hist_idx - 1] else: line = "" elif c == curses.KEY_LEFT: # scroll long log lines left log_line_lr_scroll += curses.COLS // 4 elif c == curses.KEY_RIGHT: # scroll long log lines right log_line_lr_scroll -= curses.COLS // 4 if log_line_lr_scroll < 0: log_line_lr_scroll = 0 elif c == curses.KEY_HOME: # HOME scrolls log lines all the way to the start log_line_lr_scroll = longest_visible_line elif c == curses.KEY_END: # END scrolls log lines all the way to the end log_line_lr_scroll = 0 elif c == curses.KEY_UP: scroll_log(False, 1) elif c == curses.KEY_DOWN: scroll_log(True, 1) elif c == curses.KEY_NPAGE: # aka PgDn # PgDn to go down a page in the logs scroll_log(True) elif c == curses.KEY_PPAGE: # aka PgUp # PgUp to go up a page in the logs scroll_log(False) elif c == 2 or c == 550: # Ctrl+B or Ctrl+PgDn scroll_log(True, max_log_lines) elif c == 20 or c == 555: # Ctrl+T or Ctrl+PgUp scroll_log(False, max_log_lines) elif c == curses.KEY_RESIZE: # Generated by Curses when window/screen has been resized y, x = scr.getmaxyx() curses.resizeterm(y, x) # resizeterm() causes another curses.KEY_RESIZE, so # we need to capture that to prevent a loop of resizes c = scr.getch() elif c == curses.KEY_BACKSPACE or c == 127: # Backspace to erase a character in the utterance line = line[:-1] elif c == 6: # Ctrl+F (Find) line = ":find " elif c == 24: # Ctrl+X (Exit) if find_str: # End the find session find_str = None rebuild_filtered_log() elif curses.ascii.isascii(c): # Accept typed character in the utterance line += chr(c) # DEBUG: Uncomment the following code to see what key codes # are generated when an unknown key is pressed. # else: # line += str(c) except KeyboardInterrupt as e: # User hit Ctrl+C to quit pass except KeyboardInterrupt as e: LOG.exception(e) finally: scr.erase() scr.refresh() scr = None pass