def validate_connection(self): try: subprocess.call([BIN, '--version']) except: LOG.info("Failed to find mimic at: " + BIN) raise Exception( 'Mimic was not found. Run install-mimic.sh to install it.')
def __init__(self, cache=None): super(RemoteConf, self).__init__(None) cache = cache or '/opt/mycroft/web_config_cache.json' try: # Here to avoid cyclic import from mycroft.api import DeviceApi api = DeviceApi() setting = api.get_settings() location = api.get_location() if location: setting["location"] = location # Remove server specific entries config = {} translate_remote(config, setting) for key in config: self.__setitem__(key, config[key]) self.store(cache) except HTTPError as e: LOG.error("HTTPError fetching remote configuration: %s" % e.response.status_code) self.load_local(cache) except Exception as e: LOG.error("Failed to fetch remote configuration: %s" % repr(e), exc_info=True) self.load_local(cache)
def _read_data(): """ Writes the dictionary of state data from the IPC directory. Returns: dict: loaded state information """ managerIPCDir = os.path.join(get_ipc_directory(), "managers") path = os.path.join(managerIPCDir, "disp_info") permission = "r" if os.path.isfile(path) else "w+" if permission == "w+" and os.path.isdir(managerIPCDir) is False: os.makedirs(managerIPCDir) data = {} try: with open(path, permission) as dispFile: if os.stat(str(dispFile.name)).st_size != 0: data = json.load(dispFile) except Exception as e: LOG.error(e) os.remove(path) _read_data() return data
def _load(): LOG.debug('Loading identity') try: with FileSystemAccess('identity').open('identity2.json', 'r') as f: IdentityManager.__identity = DeviceIdentity(**json.load(f)) except Exception: IdentityManager.__identity = DeviceIdentity()
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()
def handler(message): # indicate fallback handling start ws.emit(Message("mycroft.skill.handler.start", data={'handler': "fallback"})) stopwatch = Stopwatch() handler_name = None with stopwatch: for _, handler in sorted(cls.fallback_handlers.items(), key=operator.itemgetter(0)): try: if handler(message): # indicate completion handler_name = get_handler_name(handler) ws.emit(Message( 'mycroft.skill.handler.complete', data={'handler': "fallback", "fallback_handler": handler_name})) break except Exception: LOG.exception('Exception in fallback.') else: # No fallback could handle the utterance ws.emit(Message('complete_intent_failure')) warning = "No fallback could handle intent." LOG.warning(warning) # indicate completion with exception ws.emit(Message('mycroft.skill.handler.complete', data={'handler': "fallback", 'exception': warning})) # Send timing metric if message.context and message.context['ident']: ident = message.context['ident'] report_timing(ident, 'fallback_handler', stopwatch, {'handler': handler_name})
def handle_utterance(event): LOG.info("Utterance: " + str(event['utterances'])) context = {'client_name': 'mycroft_listener'} if 'ident' in event: ident = event.pop('ident') context['ident'] = ident bus.emit(Message('recognizer_loop:utterance', event, context))
def during_download(self, first_run=False): LOG.info('Still downloading executable...') if first_run: # TODO: Localize self._snd_msg('mouth.text=Updating listener...') if not self.download_complete: self.show_download_progress = Timer(30, self.during_download) self.show_download_progress.start()
def load_module(module, hotword, config, lang, loop): LOG.info('Loading "{}" wake word via {}'.format(hotword, module)) instance = None complete = Event() def initialize(): nonlocal instance, complete try: clazz = HotWordFactory.CLASSES[module] instance = clazz(hotword, config, lang=lang) except TriggerReload: complete.set() sleep(0.5) loop.reload() except NoModelAvailable: LOG.warning('Could not found find model for {} on {}.'.format( hotword, module )) instance = None except Exception: LOG.exception( 'Could not create hotword. Falling back to default.') instance = None complete.set() Thread(target=initialize, daemon=True).start() if not complete.wait(INIT_TIMEOUT): LOG.info('{} is taking too long to load'.format(module)) complete.set() return instance
def __init__(self): super(GoVivaceSTT, self).__init__() self.default_uri = "https://services.govivace.com:49149/telephony" if not self.lang.startswith("en") and not self.lang.startswith("es"): LOG.error("GoVivace STT only supports english and spanish") raise NotImplementedError
def shutdown(self): for s in self.service: try: LOG.info('shutting down ' + s.name) s.shutdown() except Exception as e: LOG.error('shutdown of ' + s.name + ' failed: ' + repr(e)) # remove listeners self.bus.remove('mycroft.audio.service.play', self._play) self.bus.remove('mycroft.audio.service.queue', self._queue) self.bus.remove('mycroft.audio.service.pause', self._pause) self.bus.remove('mycroft.audio.service.resume', self._resume) self.bus.remove('mycroft.audio.service.stop', self._stop) self.bus.remove('mycroft.audio.service.next', self._next) self.bus.remove('mycroft.audio.service.prev', self._prev) self.bus.remove('mycroft.audio.service.track_info', self._track_info) self.bus.remove('mycroft.audio.service.seek_forward', self._seek_forward) self.bus.remove('mycroft.audio.service.seek_backward', self._seek_backward) self.bus.remove('recognizer_loop:audio_output_start', self._lower_volume) self.bus.remove('recognizer_loop:record_begin', self._lower_volume) self.bus.remove('recognizer_loop:audio_output_end', self._restore_volume) self.bus.remove('recognizer_loop:record_end', self._restore_volume) self.bus.remove('mycroft.stop', self._stop)
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 read(self, size, of_exc=False): """ Read data from stream. Arguments: size (int): Number of bytes to read of_exc (bool): flag determining if the audio producer thread should throw IOError at overflows. Returns: Data read from device """ frames = collections.deque() remaining = size while remaining > 0: to_read = min(self.wrapped_stream.get_read_available(), remaining) if to_read == 0: sleep(.01) continue result = self.wrapped_stream.read(to_read, exception_on_overflow=of_exc) frames.append(result) remaining -= to_read if self.muted: return self.muted_buffer input_latency = self.wrapped_stream.get_input_latency() if input_latency > 0.2: LOG.warning("High input latency: %f" % input_latency) audio = b"".join(list(frames)) return audio
def get(phrase, lang=None, context=None): """ Looks up a resource file for the given phrase. If no file is found, the requested phrase is returned as the string. This will use the default language for translations. Args: phrase (str): resource phrase to retrieve/translate lang (str): the language to use context (dict): values to be inserted into the string Returns: str: a randomized and/or translated version of the phrase """ if not lang: from mycroft.configuration import Configuration lang = Configuration.get().get("lang") filename = "text/" + lang.lower() + "/" + phrase + ".dialog" template = resolve_resource_file(filename) if not template: LOG.debug("Resource file not found: " + filename) return phrase stache = MustacheDialogRenderer() stache.load_template_file("template", template) if not context: context = {} return stache.render("template", context)
def _poll_skill_settings(self): """ If identifier exists for this skill poll to backend to request settings and store it if it changes TODO: implement as websocket Args: hashed_meta (int): the hashed identifier """ try: if not self._complete_intialization: self.initialize_remote_settings() if not self._complete_intialization: return # unable to do remote sync else: original = hash(str(self)) self.update_remote() # Call callback for updated settings if self.changed_callback and hash(str(self)) != original: self.changed_callback() except Exception as e: LOG.error(e) LOG.exception("") # this is used in core so do not delete! if self.is_alive: # continues to poll settings every 60 seconds t = Timer(60, self._poll_skill_settings) t.daemon = True t.start()
def __init__(self, emitter): FallbackSkill.__init__(self) self.config = ConfigurationManager.get()['padatious'] intent_cache = expanduser(self.config['intent_cache']) try: from padatious import IntentContainer except ImportError: LOG.error('Padatious not installed. Please re-run dev_setup.sh') try: call(['notify-send', 'Padatious not installed', 'Please run build_host_setup and dev_setup again']) except OSError: pass return ver = get_distribution('padatious').version if ver != PADATIOUS_VERSION: LOG.warning('Using Padatious v' + ver + '. Please re-run ' + 'dev_setup.sh to install ' + PADATIOUS_VERSION) self.container = IntentContainer(intent_cache) self.emitter = emitter self.emitter.on('padatious:register_intent', self.register_intent) self.emitter.on('padatious:register_entity', self.register_entity) self.register_fallback(self.handle_fallback, 5) self.finished_training_event = Event() self.train_delay = self.config['train_delay'] self.train_time = get_time() + self.train_delay self.wait_and_train()
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 process(self, audio): SessionManager.touch() payload = { 'utterance': self.wakeword_recognizer.key_phrase, 'session': SessionManager.get().session_id, } self.emitter.emit("recognizer_loop:wakeword", payload) if self._audio_length(audio) < self.MIN_AUDIO_SIZE: LOG.warning("Audio too short to be processed") else: stopwatch = Stopwatch() with stopwatch: transcription = self.transcribe(audio) if transcription: ident = str(stopwatch.timestamp) + str(hash(transcription)) # STT succeeded, send the transcribed speech on for processing payload = { 'utterances': [transcription], 'lang': self.stt.lang, 'session': SessionManager.get().session_id, 'ident': ident } self.emitter.emit("recognizer_loop:utterance", payload) self.metrics.attr('utterances', [transcription]) else: ident = str(stopwatch.timestamp) # Report timing metrics report_timing(ident, 'stt', stopwatch, {'transcription': transcription, 'stt': self.stt.__class__.__name__})
def _load_config(self): """ Load configuration parameters from configuration """ config = Configuration.get() self.config_core = config self._config_hash = hash(str(config)) self.lang = config.get('lang') self.config = config.get('listener') rate = self.config.get('sample_rate') device_index = self.config.get('device_index') device_name = self.config.get('device_name') if not device_index and device_name: device_index = find_input_device(device_name) LOG.debug('Using microphone (None = default): '+str(device_index)) self.microphone = MutableMicrophone(device_index, rate, mute=self.mute_calls > 0) # TODO:19.02 - channels are not been used, remove from mycroft.conf # and from code. self.microphone.CHANNELS = self.config.get('channels') self.wakeword_recognizer = self.create_wake_word_recognizer() # TODO - localization self.wakeup_recognizer = self.create_wakeup_recognizer() self.responsive_recognizer = ResponsiveRecognizer( self.wakeword_recognizer) self.state = RecognizerLoopState()
def initialize(self): """ Initialization function to be implemented by all Skills. Usually used to create intents rules and register them. """ LOG.debug("No initialize function implemented")
def _poll_skill_settings(self): """ If identifier exists for this skill poll to backend to request settings and store it if it changes TODO: implement as websocket """ original = hash(str(self)) try: if not is_paired(): pass elif not self._complete_intialization: self.initialize_remote_settings() else: self.update_remote() except Exception as e: LOG.exception('Failed to fetch skill settings: {}'.format(repr(e))) finally: # Call callback for updated settings if self._complete_intialization: if self.changed_callback and hash(str(self)) != original: self.changed_callback() if self._poll_timer: self._poll_timer.cancel() if not self._is_alive: return # continues to poll settings every minute self._poll_timer = Timer(1 * 60, self._poll_skill_settings) self._poll_timer.daemon = True self._poll_timer.start()
def handle_converse_request(self, message): """ Check if the targeted skill id can handle conversation If supported, the conversation is invoked. """ skill_id = int(message.data["skill_id"]) utterances = message.data["utterances"] lang = message.data["lang"] # loop trough skills list and call converse for skill with skill_id for skill in self.loaded_skills: if self.loaded_skills[skill]["id"] == skill_id: try: instance = self.loaded_skills[skill]["instance"] except BaseException: LOG.error("converse requested but skill not loaded") self.ws.emit(Message("skill.converse.response", { "skill_id": 0, "result": False})) return try: result = instance.converse(utterances, lang) self.ws.emit(Message("skill.converse.response", { "skill_id": skill_id, "result": result})) return except BaseException: LOG.error( "Converse method malformed for skill " + str(skill_id)) self.ws.emit(Message("skill.converse.response", {"skill_id": 0, "result": False}))
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 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 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 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 __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 supported_uris(self): """ Return supported uris of chromecast. """ LOG.info("Chromecasts found: " + str(self.cast)) if self.cast: return ['http', 'https'] else: return []
def ensure_directory_exists(directory, domain=None): """ Create a directory and give access rights to all Args: domain (str): The IPC domain. Basically a subdirectory to prevent overlapping signal filenames. Returns: str: a path to the directory """ if domain: directory = os.path.join(directory, domain) # Expand and normalize the path directory = os.path.normpath(directory) directory = os.path.expanduser(directory) if not os.path.isdir(directory): try: save = os.umask(0) os.makedirs(directory, 0o777) # give everyone rights to r/w here except OSError: LOG.warning("Failed to create: " + directory) pass finally: os.umask(save) return directory
def _get_sentence_from_cache(self, sentence_hash): cached_sentence = self.cache.cached_sentences[sentence_hash] audio_file, phoneme_file = cached_sentence LOG.info("Found {} in TTS cache".format(audio_file.name)) return audio_file, phoneme_file
def handle_wakeword(event): LOG.info("Wakeword Detected: " + event['utterance']) bus.emit(Message('recognizer_loop:wakeword', event))
def handle_awoken(): """Forward mycroft.awoken to the messagebus.""" LOG.info("Listener is now Awake: ") context = {'client_name': 'mycroft_listener', 'source': 'audio'} bus.emit(Message('mycroft.awoken', context=context))
def handle_no_internet(): LOG.debug("Notifying enclosure of no internet connection") context = {'client_name': 'mycroft_listener', 'source': 'audio'} bus.emit(Message('enclosure.notify.no_internet', context=context))
def handle_record_end(): """Forward internal bus message to external bus.""" LOG.info("End Recording...") context = {'client_name': 'mycroft_listener', 'source': 'audio'} bus.emit(Message('recognizer_loop:record_end', context=context))
def load_skill(skill_descriptor, bus, skill_id, BLACKLISTED_SKILLS=None): """ Load skill from skill descriptor. Args: skill_descriptor: descriptor of skill to load bus: Mycroft messagebus connection skill_id: id number for skill Returns: MycroftSkill: the loaded skill or None on failure """ BLACKLISTED_SKILLS = BLACKLISTED_SKILLS or [] path = skill_descriptor["path"] name = basename(path) LOG.info("ATTEMPTING TO LOAD SKILL: {} with ID {}".format(name, skill_id)) if name in BLACKLISTED_SKILLS: LOG.info("SKILL IS BLACKLISTED " + name) return None main_file = join(path, MainModule + '.py') try: with open(main_file, 'rb') as fp: skill_module = imp.load_module(name.replace('.', '_'), fp, main_file, ('.py', 'rb', imp.PY_SOURCE)) if (hasattr(skill_module, 'create_skill') and callable(skill_module.create_skill)): # v2 skills framework skill = skill_module.create_skill() skill.settings.allow_overwrite = True skill.settings.load_skill_settings_from_file() skill.bind(bus) try: skill.skill_id = skill_id skill.load_data_files(path) # Set up intent handlers skill._register_decorated() skill.initialize() except Exception as e: # If an exception occurs, make sure to clean up the skill skill.default_shutdown() raise e LOG.info("Loaded " + name) # The very first time a skill is run, speak the intro first_run = skill.settings.get("__mycroft_skill_firstrun", True) if first_run: LOG.info("First run of " + name) skill.settings["__mycroft_skill_firstrun"] = False skill.settings.store() intro = skill.get_intro_message() if intro: skill.speak(intro) return skill else: LOG.warning("Module {} does not appear to be skill".format(name)) except Exception: LOG.exception("Failed to load skill: " + name) return None
def on_close(ws): LOG.info("### socket closed ###")
def on_message(self, mqttc, obj, msg): # called when a new MQTT message is received # Sample Payload {"source":"basement", "message":"is dinner ready yet"} LOG.info('message received for location id: ' + str(self.location_id)) LOG.info("This device location is: " + DeviceApi().get()["description"]) try: mqtt_message = msg.payload.decode('utf-8') LOG.info(msg.topic + " " + str(msg.qos) + ", " + mqtt_message) new_message = json.loads(mqtt_message) if "command" in new_message: # example: {"source":"kitchen", "command":"what time is it"} LOG.info('Command Received! - ' + new_message["command"] + ', From: ' + new_message["source"]) self.response_location = new_message["source"] self.send_message(new_message["command"]) elif "message" in new_message: # example: {"source":"kitchen", "message":"is dinner ready yet"} self.response_location = '' LOG.info('Message Received! - ' + new_message["message"] + ', From: ' + new_message["source"]) self.speak_dialog('location', data={"result": new_message["source"]}, expect_response=False) wait_while_speaking() self.speak_dialog('message', data={"result": new_message["message"]}, expect_response=False) else: LOG.info('Unable to decode the MQTT Message') except Exception as e: LOG.error('Error: {0}'.format(e))
def get_mac_address( self): #used to create a unique UUID for this device that node = uuid.getnode() mac = uuid.UUID(int=node).hex[-12:] LOG.info("MQTT using UUID: " + mac) return mac
def run(self): global ws, loaded_skills, last_modified_skill # Load priority skills first by order load_skill_list(PRIORITY_SKILLS) self._loaded_priority.set() # Scan the file folder that contains Skills. If a Skill is updated, # unload the existing version from memory and reload from the disk. while not self._stop_event.is_set(): if exists(SKILLS_DIR): # checking skills dir and getting all skills there list = filter( lambda x: os.path.isdir(os.path.join(SKILLS_DIR, x)), os.listdir(SKILLS_DIR)) for skill_folder in list: if skill_folder not in loaded_skills: loaded_skills[skill_folder] = { "id": hash(os.path.join(SKILLS_DIR, skill_folder)) } skill = loaded_skills.get(skill_folder) skill["path"] = os.path.join(SKILLS_DIR, skill_folder) # checking if is a skill if not MainModule + ".py" in os.listdir(skill["path"]): continue # getting the newest modified date of skill last_mod = _get_last_modified_date(skill["path"]) skill["last_modified"] = last_mod modified = skill.get("last_modified", 0) # checking if skill is loaded and wasn't modified if skill.get("loaded") and modified <= last_modified_skill: continue # checking if skill was modified elif (skill.get("instance") and modified > last_modified_skill): # checking if skill should be reloaded if not skill["instance"].reload_skill: continue LOG.debug("Reloading Skill: " + skill_folder) # removing listeners and stopping threads skill["instance"].shutdown() # -2 since two local references that are known refs = sys.getrefcount(skill["instance"]) - 2 if refs > 0: LOG.warning("After shutdown of {} there are still " "{} references remaining. The skill " "won't be cleaned from memory.".format( skill['instance'].name, refs)) del skill["instance"] skill["loaded"] = True skill["instance"] = load_skill( create_skill_descriptor(skill["path"]), ws, skill["id"], BLACKLISTED_SKILLS) # get the last modified skill modified_dates = map(lambda x: x.get("last_modified"), loaded_skills.values()) if len(modified_dates) > 0: last_modified_skill = max(modified_dates) if not self._loaded_once.is_set(): self._loaded_once.set() # Pause briefly before beginning next scan time.sleep(2)
def scan_wifi(interface="wlan0", sudo=False): class _LineMatcher(object): def __init__(self, regexp, handler): self.regexp = re.compile(regexp) self.handler = handler def _handle_new_network(line, result, networks): # group(1) is the mac address networks.append({}) networks[-1]['Address'] = result.group(1) def _handle_essid(line, result, networks): # group(1) is the essid name networks[-1]['ESSID'] = result.group(1) def _handle_quality(line, result, networks): # group(1) is the quality value # group(2) is probably always 100 networks[-1]['Quality'] = result.group(1) + '/' + result.group(2) def _handle_unknown(line, result, networks): # group(1) is the key, group(2) is the rest of the line networks[-1][result.group(1)] = result.group(2) # if you are not using sudo you will only find your own wifi if sudo: args = ['sudo', '/sbin/iwlist', interface, 'scanning'] else: args = ['/sbin/iwlist', interface, 'scanning'] proc = subprocess.Popen(args, stdout=subprocess.PIPE) stdout, stderr = proc.communicate() lines = str(stdout)[2:-2].replace("\\n", "\n").split("\n")[1:-1] networks = [] matchers = [] # catch the line 'Cell ## - Address: XX:YY:ZZ:AA:BB:CC' matchers.append( _LineMatcher(r'\s+Cell \d+ - Address: (\S+)', _handle_new_network)) # catch the line 'ESSID:"network name" matchers.append(_LineMatcher(r'\s+ESSID:"([^"]+)"', _handle_essid)) # catch the line 'Quality:X/Y Signal level:X dBm Noise level:Y dBm' matchers.append(_LineMatcher(r'\s+Quality:(\d+)/(\d+)', _handle_quality)) # catch any other line that looks like this: # Key:value matchers.append(_LineMatcher(r'\s+([^:]+):(.+)', _handle_unknown)) # read each line of output, testing against the matches above # in that order (so that the key:value matcher will be tried last) for line in lines: # hack for signal strenght TODO use a matcher if line.rstrip().lstrip().startswith("Quality="): index = line.find("Signal level=") + len("Signal level=") line = line[index:].strip() networks[-1]["strength"] = line continue for m in matchers: result = m.regexp.match(line) if result: m.handler(line, result, networks) break LOG.info("scanned networks: " + str(networks)) return networks
def on_disconnect(self, mqttc, obj, flags, rc): self._is_setup = False LOG.info("MQTT has Dis-Connected")
def play(tracks, prefered_service): """ play starts playing the audio on the prefered service if it supports the uri. If not the next best backend is found. Args: tracks: list of tracks to play. prefered_service: indecates the service the user prefer to play the tracks. """ global current global service LOG.info('play') _stop() uri_type = tracks[0].split(':')[0] LOG.info('uri_type: ' + uri_type) # check if user requested a particular service if prefered_service and uri_type in prefered_service.supported_uris(): selected_service = prefered_service # check if default supports the uri elif default and uri_type in default.supported_uris(): LOG.info("Using default backend") LOG.info(default.name) selected_service = default else: # Check if any other service can play the media LOG.info("Searching the services") for s in service: LOG.info(str(s)) if uri_type in s.supported_uris(): LOG.info("Service " + str(s) + " supports URI " + uri_type) selected_service = s break else: LOG.info('No service found for uri_type: ' + uri_type) return LOG.info('Clear list') selected_service.clear_list() LOG.info('Add tracks' + str(tracks)) selected_service.add_list(tracks) LOG.info('Playing') selected_service.play() current = selected_service
import random import json import paho.mqtt.client as mqtt import paho.mqtt.publish as publish import re __author__ = 'PCWii' # Logger: used for debug lines, like "LOGGER.debug(xyz)". These # statements will show up in the command line when running Mycroft. LOGGER = getLogger(__name__) # clear any previously connected mqtt clients on first load try: mqttc LOG.info('Client exist') mqttc.loop_stop() mqttc.disconnect() LOG.info('Stopped old client loop') except NameError: mqttc = mqtt.Client() LOG.info('Client created') # The logic of each skill is contained within its own class, which inherits # base methods from the MycroftSkill class with the syntax you can see below: # "class ____Skill(MycroftSkill)" class MeshSkill(MycroftSkill): # The constructor of the skill, which calls Mycroft Skill's constructor def __init__(self):
def make_executable(dest): """Call back function to make the downloaded file executable.""" LOG.info('Make executable new voice binary executable') # make executable file_stat = os.stat(dest) os.chmod(dest, file_stat.st_mode | stat.S_IEXEC)
def execute(self, audio, language=None): LOG.warning("WITSTT language should be configured at wit.ai settings.") return self.recognizer.recognize_wit(audio, self.token)
def remove_git_locks(self): """If git gets killed from an abrupt shutdown it leaves lock files""" for i in glob(join(self.msm.skills_dir, '*/.git/index.lock')): LOG.warning('Found and removed git lock file: ' + i) os.remove(i)
def load_services_callback(): """ Main callback function for loading services. Sets up the globals service and default and registers the event handlers for the subsystem. """ global ws global default global service config = Configuration.get().get("Audio") service = load_services(config, ws) LOG.info(service) default_name = config.get('default-backend', '') LOG.info('Finding default backend...') for s in service: LOG.info('checking ' + s.name) if s.name == default_name: default = s LOG.info('Found ' + default.name) break else: default = None LOG.info('no default found') LOG.info('Default:' + str(default)) ws.on('mycroft.audio.service.play', _play) ws.on('mycroft.audio.service.pause', _pause) ws.on('mycroft.audio.service.resume', _resume) ws.on('mycroft.audio.service.stop', _stop) ws.on('mycroft.audio.service.next', _next) ws.on('mycroft.audio.service.prev', _prev) ws.on('mycroft.audio.service.track_info', _track_info) ws.on('recognizer_loop:audio_output_start', _lower_volume) ws.on('recognizer_loop:record_begin', _lower_volume) ws.on('recognizer_loop:audio_output_end', _restore_volume) ws.on('recognizer_loop:record_end', _restore_volume) ws.on('mycroft.stop', _stop)
def download_skills(self, speak=False): """ Invoke MSM to install default skills and/or update installed skills Args: speak (bool, optional): Speak the result? Defaults to False """ if not connected(): LOG.error('msm failed, network connection not available') if speak: self.bus.emit( Message("speak", { 'utterance': dialog.get("not connected to the internet") })) self.next_download = time.time() + 5 * MINUTES return False installed_skills = self.load_installed_skills() msm = SkillManager.create_msm() with msm.lock, self.thread_lock: default_groups = dict(msm.repo.get_default_skill_names()) if msm.platform in default_groups: platform_groups = default_groups[msm.platform] else: LOG.info('Platform defaults not found, using DEFAULT ' 'skills only') platform_groups = [] default_names = set( chain(default_groups['default'], platform_groups)) default_skill_errored = False def get_skill_data(skill_name): """ Get skill data structure from name. """ for e in msm.skills_data.get('skills', []): if e.get('name') == skill_name: return e # if skill isn't in the list return empty structure return {} def install_or_update(skill): """Install missing defaults and update existing skills""" if get_skill_data(skill.name).get('beta'): skill.sha = None # Will update to latest head if skill.is_local: skill.update() if skill.name not in installed_skills: skill.update_deps() elif skill.name in default_names: try: msm.install(skill, origin='default') except Exception: if skill.name in default_names: LOG.warning('Failed to install default skill: ' + skill.name) nonlocal default_skill_errored default_skill_errored = True raise installed_skills.add(skill.name) try: msm.apply(install_or_update, msm.list()) if SkillManager.manifest_upload_allowed and is_paired(): try: DeviceApi().upload_skills_data(msm.skills_data) except Exception: LOG.exception('Could not upload skill manifest') except MsmException as e: LOG.error('Failed to update skills: {}'.format(repr(e))) self.save_installed_skills(installed_skills) if speak: data = {'utterance': dialog.get("skills updated")} self.bus.emit(Message("speak", data)) # Schedule retry in 5 minutes on failure, after 10 shorter periods # Go back to 60 minutes wait if default_skill_errored and self.num_install_retries < 10: self.num_install_retries += 1 self.next_download = time.time() + 5 * MINUTES return False self.num_install_retries = 0 # Update timestamp on .msm file to be used when system is restarted with open(self.dot_msm, 'a'): os.utime(self.dot_msm, None) self.next_download = time.time() + self.update_interval return True
def pronounce_number_en(num, places=2, short_scale=True, scientific=False): """ Convert a number to its spoken equivalent For example, '5.2' would return 'five point two' Args: num(float or int): the number to pronounce (under 100) places(int): maximum decimal places to speak short_scale (bool) : use short (True) or long scale (False) https://en.wikipedia.org/wiki/Names_of_large_numbers scientific (bool): pronounce in scientific notation Returns: (str): The pronounced number """ if scientific: number = '%E' % num n, power = number.replace("+", "").split("E") power = int(power) if power != 0: # This handles negatives of powers separately from the normal # handling since each call disables the scientific flag return '{}{} times ten to the power of {}{}'.format( 'negative ' if float(n) < 0 else '', pronounce_number_en(abs(float(n)), places, short_scale, False), 'negative ' if power < 0 else '', pronounce_number_en(abs(power), places, short_scale, False)) if short_scale: number_names = _NUM_STRING_EN.copy() number_names.update(_SHORT_SCALE_EN) else: number_names = _NUM_STRING_EN.copy() number_names.update(_LONG_SCALE_EN) digits = [number_names[n] for n in range(0, 20)] tens = [number_names[n] for n in range(10, 100, 10)] if short_scale: hundreds = [_SHORT_SCALE_EN[n] for n in _SHORT_SCALE_EN.keys()] else: hundreds = [_LONG_SCALE_EN[n] for n in _LONG_SCALE_EN.keys()] # deal with negatives result = "" if num < 0: result = "negative " if scientific else "minus " num = abs(num) try: # deal with 4 digits # usually if it's a 4 digit num it should be said like a date # i.e. 1972 => nineteen seventy two if len(str(num)) == 4 and isinstance(num, int): _num = str(num) # deal with 1000, 2000, 2001, 2100, 3123, etc # is skipped as the rest of the # functin deals with this already if _num[1:4] == '000' or _num[1:3] == '00' or int(_num[0:2]) >= 20: pass # deal with 1900, 1300, etc # i.e. 1900 => nineteen hundred elif _num[2:4] == '00': first = number_names[int(_num[0:2])] last = number_names[100] return first + " " + last # deal with 1960, 1961, etc # i.e. 1960 => nineteen sixty # 1961 => nineteen sixty one else: first = number_names[int(_num[0:2])] if _num[3:4] == '0': last = number_names[int(_num[2:4])] else: second = number_names[int(_num[2:3])*10] last = second + " " + number_names[int(_num[3:4])] return first + " " + last # exception used to catch any unforseen edge cases # will default back to normal subroutine except Exception as e: LOG.error('Exception in pronounce_number_en: {}' + repr(e)) # check for a direct match if num in number_names: if num > 90: result += "one " result += number_names[num] else: def _sub_thousand(n): assert 0 <= n <= 999 if n <= 19: return digits[n] elif n <= 99: q, r = divmod(n, 10) return tens[q - 1] + (" " + _sub_thousand(r) if r else "") else: q, r = divmod(n, 100) return digits[q] + " hundred" + ( " and " + _sub_thousand(r) if r else "") def _short_scale(n): if n >= max(_SHORT_SCALE_EN.keys()): return "infinity" n = int(n) assert 0 <= n res = [] for i, z in enumerate(_split_by(n, 1000)): if not z: continue number = _sub_thousand(z) if i: number += " " number += hundreds[i] res.append(number) return ", ".join(reversed(res)) def _split_by(n, split=1000): assert 0 <= n res = [] while n: n, r = divmod(n, split) res.append(r) return res def _long_scale(n): if n >= max(_LONG_SCALE_EN.keys()): return "infinity" n = int(n) assert 0 <= n res = [] for i, z in enumerate(_split_by(n, 1000000)): if not z: continue number = pronounce_number_en(z, places, True, scientific) # strip off the comma after the thousand if i: # plus one as we skip 'thousand' # (and 'hundred', but this is excluded by index value) number = number.replace(',', '') number += " " + hundreds[i+1] res.append(number) return ", ".join(reversed(res)) if short_scale: result += _short_scale(num) else: result += _long_scale(num) # Deal with fractional part if not num == int(num) and places > 0: result += " point" place = 10 while int(num * place) % 10 > 0 and places > 0: result += " " + number_names[int(num * place) % 10] place *= 10 places -= 1 return result
def handle_complete_intent_failure(event): """Extreme backup for answering completely unhandled intent requests.""" LOG.info("Failed to find intent.") data = {'utterance': dialog.get('not.loaded')} bus.emit(Message('speak', data))
def _load_or_reload_skill(self, skill_path): """ Check if unloaded skill or changed skill needs reloading and perform loading if necessary. Returns True if the skill was loaded/reloaded """ skill_path = skill_path.rstrip('/') skill = self.loaded_skills.setdefault(skill_path, {}) skill.update({"id": basename(skill_path), "path": skill_path}) # check if folder is a skill (must have __init__.py) if not MainModule + ".py" in os.listdir(skill_path): return False # getting the newest modified date of skill modified = _get_last_modified_date(skill_path) last_mod = skill.get("last_modified", 0) # checking if skill is loaded and hasn't been modified on disk if skill.get("loaded") and modified <= last_mod: return False # Nothing to do! # check if skill was modified elif skill.get("instance") and modified > last_mod: # check if skill has been blocked from reloading if (not skill["instance"].reload_skill or not skill.get('active', True)): return False LOG.debug("Reloading Skill: " + basename(skill_path)) # removing listeners and stopping threads try: skill["instance"].default_shutdown() except Exception: LOG.exception("An error occured while shutting down {}".format( skill["instance"].name)) if DEBUG: gc.collect() # Collect garbage to remove false references # Remove two local references that are known refs = sys.getrefcount(skill["instance"]) - 2 if refs > 0: msg = ("After shutdown of {} there are still " "{} references remaining. The skill " "won't be cleaned from memory.") LOG.warning(msg.format(skill['instance'].name, refs)) del skill["instance"] self.bus.emit( Message("mycroft.skills.shutdown", { "path": skill_path, "id": skill["id"] })) skill["loaded"] = True desc = create_skill_descriptor(skill_path) skill["instance"] = load_skill(desc, self.bus, skill["id"], BLACKLISTED_SKILLS) skill["last_modified"] = modified if skill['instance'] is not None: self.bus.emit( Message( 'mycroft.skills.loaded', { 'path': skill_path, 'id': skill['id'], 'name': skill['instance'].name, 'modified': modified })) return True else: self.bus.emit( Message('mycroft.skills.loading_failure', { 'path': skill_path, 'id': skill['id'] })) return False
def handle_no_internet(): LOG.debug("Notifying enclosure of no internet connection") bus.emit(Message('enclosure.notify.no_internet'))
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 == 18: # Ctrl+R (Redraw) scr.erase() scr.refresh() 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
def handle_record_begin(): """Forward internal bus message to external bus.""" LOG.info("Begin Recording...") bus.emit(Message('recognizer_loop:record_begin'))
def handle_awoken(): """Forward mycroft.awoken to the messagebus.""" LOG.info("Listener is now Awake: ") bus.emit(Message('mycroft.awoken'))
def store(self, force=False): LOG.warning('DEPRECATED - use mycroft.skills.settings.save_settings()') save_settings(self._skill.root_dir, self._settings)
def handle_record_end(): """Forward internal bus message to external bus.""" LOG.info("End Recording...") bus.emit(Message('recognizer_loop:record_end'))
def _load_or_reload_skill(self, skill_folder): """ Check if unloaded skill or changed skill needs reloading and perform loading if necessary. """ if skill_folder not in self.loaded_skills: self.loaded_skills[skill_folder] = { "id": hash(os.path.join(SKILLS_DIR, skill_folder)) } skill = self.loaded_skills.get(skill_folder) skill["path"] = os.path.join(SKILLS_DIR, skill_folder) # check if folder is a skill (must have __init__.py) if not MainModule + ".py" in os.listdir(skill["path"]): return # getting the newest modified date of skill modified = _get_last_modified_date(skill["path"]) last_mod = skill.get("last_modified", 0) # checking if skill is loaded and hasn't been modified on disk if skill.get("loaded") and modified <= last_mod: return # Nothing to do! # check if skill was modified elif skill.get("instance") and modified > last_mod: # check if skill has been blocked from reloading if not skill["instance"].reload_skill: return LOG.debug("Reloading Skill: " + skill_folder) # removing listeners and stopping threads skill["instance"].shutdown() if DEBUG: gc.collect() # Collect garbage to remove false references # Remove two local references that are known refs = sys.getrefcount(skill["instance"]) - 2 if refs > 0: LOG.warning("After shutdown of {} there are still " "{} references remaining. The skill " "won't be cleaned from memory.".format( skill['instance'].name, refs)) del skill["instance"] self.ws.emit( Message("mycroft.skills.shutdown", { "folder": skill_folder, "id": skill["id"] })) # (Re)load the skill from disk with self.__msm_lock: # Make sure msm isn't running skill["loaded"] = True desc = create_skill_descriptor(skill["path"]) skill["instance"] = load_skill(desc, self.ws, skill["id"], BLACKLISTED_SKILLS) skill["last_modified"] = modified if skill['instance'] is not None: self.ws.emit( Message( 'mycroft.skills.loaded', { 'folder': skill_folder, 'id': skill['id'], 'name': skill['instance'].name, 'modified': modified })) else: self.ws.emit( Message('mycroft.skills.loading_failure', { 'folder': skill_folder, 'id': skill['id'] }))
def set_changed_callback(self, callback): LOG.warning('DEPRECATED - set the settings_change_callback attribute') self._skill.settings_change_callback = callback