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 file_path(self): """ Returns the path to the yml file associated with this configuration Returns: path to this configuration yml """ file_path = join(self.path, self.name + ".yml") if not isfile(file_path): create_file(file_path) LOG.debug(f"New YAML created: {file_path}") return file_path
def on_gui_client_connected(self, message): # GUI has announced presence LOG.info('GUI HAS ANNOUNCED!') port = self.global_config["gui_websocket"]["base_port"] LOG.debug("on_gui_client_connected") gui_id = message.data.get("gui_id") 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": port, "gui_id": gui_id}))
def get_coordinates(gps_loc: dict) -> (float, float): """ Gets the latitude and longitude for the passed location :param gps_loc: dict of "city", "state", "country" :return: lat, lng float values """ coordinates = Nominatim(user_agent="neon-ai") try: location = coordinates.geocode(gps_loc) LOG.debug(f"{location}") return location.latitude, location.longitude except Exception as x: LOG.error(x) return -1, -1
def _write_yaml_file(self): """ Overwrites and/or updates the YML at the specified file_path. """ try: with self.lock: tmp_filename = join(self.path, f".{self.name}.tmp") LOG.debug(f"tmp_filename={tmp_filename}") shutil.copy2(self.file_path, tmp_filename) with open(self.file_path, 'w+') as f: self.parser.dump(self._content, f) LOG.debug(f"YAML updated {self.name}") self._loaded = os.path.getmtime(self.file_path) self._pending_write = False self._disk_content_hash = hash(repr(self._content)) except FileNotFoundError as x: LOG.error(f"Configuration file not found error: {x}")
def listen(self, source, stream): """Listens for chunks of audio that Mycroft should perform STT on. This will listen continuously for a wake-up-word, then return the audio chunk containing the spoken phrase that comes immediately afterwards. Args: source (AudioSource): Source producing the audio chunks stream (AudioStreamHandler): Stream target that will receive chunks of the utterance audio while it is being recorded Returns: (AudioData, lang): audio with the user's utterance (minus the wake-up-word), stt_lang """ assert isinstance(source, AudioSource), "Source must be an AudioSource" # If skipping wake words, just pass audio to our streaming STT # TODO: Check config updates? if self.loop.stt.can_stream and not self.use_wake_word: lang = self.loop.stt.lang self.loop.emit("recognizer_loop:record_begin") self.loop.stt.stream.stream_start() frame_data = get_silence(source.SAMPLE_WIDTH) LOG.debug("Stream starting!") # event set in OPM while not self.loop.stt.transcript_ready.is_set(): # Pass audio until STT tells us to stop (this is called again immediately) chunk = self.record_sound_chunk(source) if not is_speaking(): # Filter out Neon speech self.loop.stt.stream.stream_chunk(chunk) frame_data += chunk LOG.debug("stream ended!") audio_data = self._create_audio_data(frame_data, source) self.loop.emit("recognizer_loop:record_end") # If using wake words, wait until the wake_word is detected and then record the following phrase else: audio_data, lang = super().listen(source, stream) # one of the default plugins saves the speech to file and adds "filename" to context audio_data, context = self.audio_consumers.transform(audio_data) context["lang"] = lang return audio_data, context
def check_for_updates(self) -> dict: """ Reloads updated configuration from disk. Used to reload changes when other instances modify a configuration Returns:Updated configuration.content """ new_content = self._load_yaml_file() if new_content: LOG.debug(f"{self.name} Checked for Updates") self._content = new_content else: LOG.error("new_content is empty!!") new_content = self._load_yaml_file() if new_content: LOG.debug("second attempt success") self._content = new_content else: LOG.error("second attempt failed") return self._content
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_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) # Add a check to return any display to idle from position 0 if (pos == 0 and len(self.loaded[0].pages) == 0): self.bus.emit(Message("mycroft.device.show.idle"))
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 __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 __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. updated = Namespace(self.loaded[0].name, self.loaded[0].pages + pages) self.loaded[0] = updated
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 get_location(lat, lng) -> (str, str, str, str): """ Gets location name values for the passed coordinates. Note that some coordinates do not have a city, but may have a county. :param lat: latitude :param lng: longitude :return: city, county, state, country """ address = Nominatim(user_agent="neon-ai") location = address.reverse([lat, lng], language="en-US") LOG.debug(f"{location}") LOG.debug(f"{location.raw}") LOG.debug(f"{location.raw.get('address')}") city = location.raw.get('address').get('city') or location.raw.get('address').get('town') county = location.raw.get('address').get('county') state = location.raw.get('address').get('state') country = location.raw.get('address').get('country') return city, county, state, country
def update_yaml_file(self, header=None, sub_header=None, value="", multiple=False, final=False): """ Called by class's children to update, create, or initiate a new parameter in the specified YAML file. Creates and updates headers, adds or overwrites preference elements, associates value to the created or existing field. Recursive if creating a new header-preference-value combo. :param multiple: true if more than one continuous write is coming :param header: string with the new or existing main header :param sub_header: new or existing subheader (sublist) :param value: any value that should be associated with the headers. :param final: true if this is the last change when skip_reload was true :return: pre-existing parameter if nothing to update or error if invalid yaml_type. """ # with self.lock.acquire(30): self.check_reload() before_change = self._content LOG.debug(value) if header and sub_header: try: before_change[header][sub_header] = value except KeyError: before_change[header] = {sub_header: value} return elif header and not sub_header: try: before_change[header] = value except Exception as x: LOG.error(x) else: LOG.debug("No change needed") if not final: return if not multiple: self._write_yaml_file() else: LOG.debug("More than one change") self._pending_write = True