Ejemplo n.º 1
0
    def run(self):
        """Load skills and update periodically from disk and internet."""
        self._remove_git_locks()
        self._connected_event.wait()
        self._load_on_startup()

        # Sync backend and skills.
        if is_paired() and not self.upload_queue.started:
            self._start_settings_update()

        # 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():
            try:
                self._reload_modified_skills()
                self._load_new_skills()
                self._unload_removed_skills()
                self._update_skills()
                if (is_paired() and self.upload_queue.started
                        and len(self.upload_queue) > 0):
                    self.msm.clear_cache()
                    self.skill_updater.post_manifest()
                    self.upload_queue.send()

                sleep(2)  # Pause briefly before beginning next scan
            except Exception:
                LOG.exception('Something really unexpected has occured '
                              'and the skill manager loop safety harness was '
                              'hit.')
                sleep(30)
Ejemplo n.º 2
0
def check_connection():
    """
        Check for network connection. If not paired trigger pairing.
        Runs as a Timer every second until connection is detected.
    """
    if connected():
        enclosure = EnclosureAPI(ws)

        if is_paired():
            # Skip the sync message when unpaired because the prompt to go to
            # home.mycrof.ai will be displayed by the pairing skill
            enclosure.mouth_text(mycroft.dialog.get("message_synching.clock"))
        # Force a sync of the local clock with the internet
        ws.emit(Message("system.ntp.sync"))
        time.sleep(15)  # TODO: Generate/listen for a message response...

        # Check if the time skewed significantly.  If so, reboot
        skew = abs((monotonic.monotonic() - start_ticks) -
                   (time.time() - start_clock))
        if skew > 60 * 60:
            # Time moved by over an hour in the NTP sync. Force a reboot to
            # prevent weird things from occcurring due to the 'time warp'.
            #
            ws.emit(
                Message(
                    "speak",
                    {'utterance': mycroft.dialog.get("time.changed.reboot")}))
            wait_while_speaking()

            # provide visual indicators of the reboot
            enclosure.mouth_text(mycroft.dialog.get("message_rebooting"))
            enclosure.eyes_color(70, 65, 69)  # soft gray
            enclosure.eyes_spin()

            # give the system time to finish processing enclosure messages
            time.sleep(1.0)

            # reboot
            ws.emit(Message("system.reboot"))
            return

        ws.emit(Message('mycroft.internet.connected'))
        # check for pairing, if not automatically start pairing
        if not is_paired():
            # begin the process
            payload = {'utterances': ["pair my device"], 'lang': "en-us"}
            ws.emit(Message("recognizer_loop:utterance", payload))
        else:
            if is_paired():
                # Skip the  message when unpaired because the prompt to go
                # to home.mycrof.ai will be displayed by the pairing skill
                enclosure.mouth_text(mycroft.dialog.get("message_updating"))

            from mycroft.api import DeviceApi
            api = DeviceApi()
            api.update_version()
    else:
        thread = Timer(1, check_connection)
        thread.daemon = True
        thread.start()
Ejemplo n.º 3
0
 def skill_gid(self):
     """ Finalizes the skill gid to include device uuid if needed. """
     if is_paired():
         return self.__skill_gid.replace('@|', '@{}|'.format(
             DeviceApi().identity.uuid))
     else:
         return self.__skill_gid
Ejemplo n.º 4
0
    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
        """
        original = hash(str(self))
        try:
            if not is_paired():
                pass
            elif not self._complete_intialization:
                self.initialize_remote_settings()
                if not self._complete_intialization:
                    return  # unable to do remote sync
            else:
                self.update_remote()

        except Exception as e:
            LOG.error('Failed to fetch skill settings:\n{}'.format(e))
        finally:
            # Call callback for updated settings
            if self.changed_callback and hash(str(self)) != original:
                self.changed_callback()

        # this is used in core so do not delete!
        if self.is_alive:
            # continues to poll settings every 5 minutes
            t = Timer(300, self._poll_skill_settings)
            t.daemon = True
            t.start()
Ejemplo n.º 5
0
 def handle_mycroft_ready(self, message):
     """Catch info that skills are loaded and ready."""
     with self.pair_dialog_lock:
         if is_paired() and self.pairing_performed:
             self.speak_dialog(self.paired_dialog)
         else:
             self.mycroft_ready = True
Ejemplo n.º 6
0
    def download(self, message=None):
        """Download the settings stored on the backend and check for changes

        When used as a messagebus handler a message is passed but not used.
        """
        if not self.sync_enabled:
            return
        if is_paired():
            remote_settings = self._get_remote_settings()
            if remote_settings:
                settings_changed = self.last_download_result != remote_settings
                if settings_changed:
                    LOG.debug('Skill settings changed since last download')
                    self._emit_settings_change_events(remote_settings)
                    self.last_download_result = remote_settings
                    save_remote_settings_cache(remote_settings)
                else:
                    LOG.debug('No skill settings changes since last download')
        else:
            LOG.debug('Settings not downloaded - device is not paired')
        # If this method is called outside of the timer loop, ensure the
        # existing timer is canceled before starting a new one.
        if self.download_timer:
            self.download_timer.cancel()

        if self.continue_downloading:
            self.download_timer = Timer(ONE_MINUTE, self.download)
            self.download_timer.daemon = True
            self.download_timer.start()
Ejemplo n.º 7
0
    def download(self):
        """Download the settings stored on the backend and check for changes"""
        if is_paired():
            download_success = self._get_remote_settings()
            if download_success:
                self.settings_changed = (self.last_download_result !=
                                         self.remote_settings)
                if self.settings_changed:
                    LOG.debug('Skill settings changed since last download')
                    self._emit_settings_change_events()
                    self.last_download_result = self.remote_settings
                else:
                    LOG.debug('No skill settings changes since last download')
        else:
            LOG.debug('Settings not downloaded - device is not paired')

        # If this method is called outside of the timer loop, ensure the
        # existing timer is canceled before starting a new one.
        if self.download_timer:
            self.download_timer.cancel()

        if self.continue_downloading:
            self.download_timer = Timer(ONE_MINUTE, self.download)
            self.download_timer.daemon = True
            self.download_timer.start()
Ejemplo n.º 8
0
    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()
                if not self._complete_intialization:
                    return  # unable to do remote sync
            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.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(60, self._poll_skill_settings)
        self._poll_timer.daemon = True
        self._poll_timer.start()
Ejemplo n.º 9
0
    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()
Ejemplo n.º 10
0
    def upload(self):
        """Upload the contents of the settingsmeta file to Mycroft servers.

        The settingsmeta file does not change often, if at all.  Only perform
        the upload if a change in the file is detected.
        """
        synced = False
        if is_paired():
            self.api = DeviceApi()
            if self.api.identity.uuid:
                settings_meta_file_exists = (self.json_path.is_file()
                                             or self.yaml_path.is_file())
                if settings_meta_file_exists:
                    self._load_settings_meta_file()

                self._update_settings_meta()
                LOG.debug('Uploading settings meta for ' + self.skill_gid)
                synced = self._issue_api_call()
            else:
                LOG.debug('settingsmeta.json not uploaded - no identity')
        else:
            LOG.debug('settingsmeta.json not uploaded - device is not paired')

        if not synced and not self._stopped:
            self.upload_timer = Timer(ONE_MINUTE, self.upload)
            self.upload_timer.daemon = True
            self.upload_timer.start()
Ejemplo n.º 11
0
    def __init__(self, cache=None):
        super(RemoteConf, self).__init__(None)

        cache = cache or '/opt/mycroft/web_config_cache.json'
        from mycroft.api import is_paired
        if not is_paired():
            self.load_local(cache)
            return

        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("RequestException 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)
Ejemplo n.º 12
0
    def initialize_remote_settings(self):
        """ initializes the remote settings to the server """
        # if the settingsmeta file exists (and is valid)
        # this block of code is a control flow for
        # different scenarios that may arises with settingsmeta
        self.load_skill_settings_from_file()  # loads existing settings.json
        settings_meta = self._load_settings_meta()
        if not settings_meta:
            return

        if not is_paired():
            return

        self._device_identity = self.api.identity.uuid
        self._api_path = "/" + self._device_identity + "/skill"
        try:
            self._user_identity = self.api.get()['user']['uuid']
        except RequestException:
            return

        settings = self._request_my_settings(self.skill_gid)
        if settings:
            self.save_skill_settings(settings)

        # TODO if this skill_gid is not a modified version check if a modified
        # version exists on the server and delete it

        # Always try to upload settingsmeta on startup
        self._upload_meta(settings_meta, self.skill_gid)

        self._complete_intialization = True
Ejemplo n.º 13
0
 def update_skills_json(self):
     skills_config = self.config_core['skills']
     upload_allowed = skills_config.get('upload_skill_manifest', False)
     if upload_allowed and is_paired():
         try:
             DeviceApi().upload_skills_data(self.msm.device_skill_state)
         except Exception:
             self.log.exception('Could not upload skill manifest')
Ejemplo n.º 14
0
 def handle_paired(self, message):
     draw_file(self.find_resource('5-pairing-success.fb', 'ui'))
     time.sleep(5)
     draw_file(self.find_resource('6-intro.fb', 'ui'))
     time.sleep(15)
     draw_file(self.find_resource('mycroft.fb', 'ui'))
     if not is_paired():
         self.bus.remove('enclosure.mouth.text', self.handle_show_text)
Ejemplo n.º 15
0
    def _get_pairing_status(self):
        """Set an instance attribute indicating the device's pairing status"""
        try:
            self.is_paired = is_paired(ignore_errors=False)
        except BackendDown:
            LOG.error('Cannot complete device updates due to backend issues.')
            self.backend_down = True

        if self.is_paired:
            LOG.info('Device is paired')
Ejemplo n.º 16
0
    def __init__(self, cache=None):
        super().__init__(None)
        cache = cache or WEB_CONFIG_CACHE
        from mycroft.api import is_paired
        if not is_paired():
            self.load_local(cache)
            return
        try:
            # Here to avoid cyclic import
            from mycroft.api import DeviceApi
            from mycroft.api import is_backend_disabled

            if is_backend_disabled():
                # disable options that require backend
                config = {
                    "server": {
                        "metrics": False,
                        "sync_skill_settings": False
                    },
                    "skills": {"upload_skill_manifest": False},
                    "opt_in": False
                }
                for key in config:
                    self.__setitem__(key, config[key])
            else:
                api = DeviceApi()
                setting = api.get_settings()
                location = None
                try:
                    location = api.get_location()
                except RequestException as e:
                    LOG.error("RequestException fetching remote location: {}"
                              .format(str(e)))
                    if exists(cache) and isfile(cache):
                        location = load_commented_json(cache).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 RequestException as e:
            LOG.error("RequestException fetching remote configuration: {}"
                      .format(str(e)))
            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)
Ejemplo n.º 17
0
 def post_manifest(self, reload_skills_manifest=False):
     """Post the manifest of the device's skills to the backend."""
     upload_allowed = self.config['skills'].get('upload_skill_manifest')
     if upload_allowed and is_paired():
         if reload_skills_manifest:
             self.msm.clear_cache()
         try:
             device_api = DeviceApi()
             device_api.upload_skills_data(self.msm.device_skill_state)
         except Exception:
             LOG.exception('Could not upload skill manifest')
Ejemplo n.º 18
0
def try_update_system(platform):
    bus.emit(Message('system.update'))
    msg = Message('system.update', {
        'paired': is_paired(),
        'platform': platform
    })
    resp = bus.wait_for_response(msg, 'system.update.processing')

    if resp and (resp.data or {}).get('processing', True):
        bus.wait_for_response(Message('system.update.waiting'),
                              'system.update.complete', 1000)
Ejemplo n.º 19
0
def try_update_system(platform):
    bus.emit(Message('system.update'))
    msg = Message('system.update', {
        'paired': is_paired(),
        'platform': platform
    })
    resp = bus.wait_for_response(msg, 'system.update.processing')

    if resp and (resp.data or {}).get('processing', True):
        bus.wait_for_response(Message('system.update.waiting'),
                              'system.update.complete', 1000)
Ejemplo n.º 20
0
 def _init_blank_meta(self):
     """ Send blank settingsmeta to remote. """
     try:
         if not is_paired() and self.is_alive:
             self._blank_poll_timer = Timer(60, self._init_blank_meta)
             self._blank_poll_timer.daemon = True
             self._blank_poll_timer.start()
         else:
             self.initialize_remote_settings()
     except Exception as e:
         LOG.exception('Failed to send blank meta: {}'.format(repr(e)))
Ejemplo n.º 21
0
 def handle_internet_connected(self, message):
     """ System came online later after booting. """
     if is_paired():
         self.enclosure.mouth_reset()
     else:
         # If we are not paired the pairing process will begin.
         # Cannot handle from mycroft.not.paired event because
         # we trigger first pairing with an utterance.
         draw_file(self.find_resource('3-wifi-success.fb', 'ui'))
         time.sleep(5)
         draw_file(self.find_resource('4-pairing-home.fb', 'ui'))
         self.bus.on('enclosure.mouth.text', self.handle_show_text)
Ejemplo n.º 22
0
def check_connection():
    if connected():
        ws.emit(Message('mycroft.internet.connected'))
        # check for pairing, if not automatically start pairing
        if not is_paired():
            # begin the process
            payload = {'utterances': ["pair my device"], 'lang': "en-us"}
            ws.emit(Message("recognizer_loop:utterance", payload))
    else:
        thread = Timer(1, check_connection)
        thread.daemon = True
        thread.start()
Ejemplo n.º 23
0
 def schedule_update_skills(self, message=None):
     """ Schedule a skill update to take place directly. """
     if direct_update_needed():
         # Update skills at next opportunity
         LOG.info('Skills will be updated directly')
         self.schedule_now()
         # Skip the  message when unpaired because the prompt to go
         # to home.mycrof.ai will be displayed by the pairing skill
         if not is_paired():
             self.enclosure.mouth_text(dialog.get("message_updating"))
     else:
         LOG.info('Skills will be updated at a later time')
         self.next_download = time.time() + 60 * MINUTES
Ejemplo n.º 24
0
def report_metric(name, data):
    """
    Report a general metric to the Mycroft servers

    Args:
        name (str): Name of metric. Must use only letters and hyphens
        data (dict): JSON dictionary to report. Must be valid JSON
    """
    try:
        if is_paired() and Configuration().get()['opt_in']:
            DeviceApi().report_metric(name, data)
    except requests.RequestException as e:
        LOG.error('Metric couldn\'t be uploaded, due to a network error ({})'
                  .format(e))
Ejemplo n.º 25
0
def report_metric(name, data):
    """
    Report a general metric to the Mycroft servers

    Args:
        name (str): Name of metric. Must use only letters and hyphens
        data (dict): JSON dictionary to report. Must be valid JSON
    """
    try:
        if is_paired() and Configuration().get()['opt_in']:
            DeviceApi().report_metric(name, data)
    except requests.RequestException as e:
        LOG.error('Metric couldn\'t be uploaded, due to a network error ({})'
                  .format(e))
Ejemplo n.º 26
0
    def initialize_remote_settings(self):
        """ initializes the remote settings to the server """
        # if settingsmeta.json exists (and is valid)
        # this block of code is a control flow for
        # different scenarios that may arises with settingsmeta
        self.load_skill_settings_from_file()  # loads existing settings.json
        settings_meta = self._load_settings_meta()
        if not settings_meta:
            return

        if not is_paired():
            return

        self._device_identity = self.api.identity.uuid
        self._api_path = "/" + self._device_identity + "/skill"
        try:
            self._user_identity = self.api.get()['user']['uuid']
        except RequestException:
            return

        hashed_meta = self._get_meta_hash(settings_meta)
        skill_settings = self._request_other_settings(hashed_meta)
        # if hash is new then there is a diff version of settingsmeta
        if self._is_new_hash(hashed_meta):
            # first look at all other devices on user account to see
            # if the settings exist. if it does then sync with device
            if skill_settings:
                # not_owner flags that this settings is loaded from
                # another device. If a skill settings doesn't have
                # not_owner, then the skill is created from that device
                self['not_owner'] = True
                self.save_skill_settings(skill_settings)
            else:  # upload skill settings if
                uuid = self._load_uuid()
                if uuid is not None:
                    self._delete_metadata(uuid)
                self._upload_meta(settings_meta, hashed_meta)
        else:  # hash is not new
            if skill_settings is not None:
                self['not_owner'] = True
                self.save_skill_settings(skill_settings)
            else:
                settings = self._request_my_settings(hashed_meta)
                if settings is None:
                    # metadata got deleted from Home, send up
                    self._upload_meta(settings_meta, hashed_meta)
                else:
                    self.save_skill_settings(settings)
        self._complete_intialization = True
Ejemplo n.º 27
0
    def initialize_remote_settings(self):
        """ initializes the remote settings to the server """
        # if settingsmeta.json exists (and is valid)
        # this block of code is a control flow for
        # different scenarios that may arises with settingsmeta
        self.load_skill_settings_from_file()  # loads existing settings.json
        settings_meta = self._load_settings_meta()
        if not settings_meta:
            return

        if not is_paired():
            return

        self._device_identity = self.api.identity.uuid
        self._api_path = "/" + self._device_identity + "/skill"
        try:
            self._user_identity = self.api.get()['user']['uuid']
        except RequestException:
            return

        hashed_meta = self._get_meta_hash(settings_meta)
        skill_settings = self._request_other_settings(hashed_meta)
        # if hash is new then there is a diff version of settingsmeta
        if self._is_new_hash(hashed_meta):
            # first look at all other devices on user account to see
            # if the settings exist. if it does then sync with device
            if skill_settings:
                # not_owner flags that this settings is loaded from
                # another device. If a skill settings doesn't have
                # not_owner, then the skill is created from that device
                self['not_owner'] = True
                self.save_skill_settings(skill_settings)
            else:  # upload skill settings if
                uuid = self._load_uuid()
                if uuid is not None:
                    self._delete_metadata(uuid)
                self._upload_meta(settings_meta, hashed_meta)
        else:  # hash is not new
            if skill_settings is not None:
                self['not_owner'] = True
                self.save_skill_settings(skill_settings)
            else:
                settings = self._request_my_settings(hashed_meta)
                if settings is None:
                    # metadata got deleted from Home, send up
                    self._upload_meta(settings_meta, hashed_meta)
                else:
                    self.save_skill_settings(settings)
        self._complete_intialization = True
Ejemplo n.º 28
0
def check_connection():
    if connected():
        ws.emit(Message('mycroft.internet.connected'))
        # check for pairing, if not automatically start pairing
        if not is_paired():
            # begin the process
            payload = {
                'utterances': ["pair my device"],
                'lang': "en-us"
            }
            ws.emit(Message("recognizer_loop:utterance", payload))
    else:
        thread = Timer(1, check_connection)
        thread.daemon = True
        thread.start()
Ejemplo n.º 29
0
def check_connection():
    """
        Check for network connection. If not paired trigger pairing.
        Runs as a Timer every second until connection is detected.
    """
    if connected():
        ws.emit(Message('mycroft.internet.connected'))
        # check for pairing, if not automatically start pairing
        if not is_paired():
            # begin the process
            payload = {'utterances': ["pair my device"], 'lang': "en-us"}
            ws.emit(Message("recognizer_loop:utterance", payload))
    else:
        thread = Timer(1, check_connection)
        thread.daemon = True
        thread.start()
Ejemplo n.º 30
0
    def initialize(self):
        self.add_event("mycroft.not.paired", self.not_paired)
        self.nato_dict = self.translate_namedvalues('codes')

        # If the device isn't paired catch mycroft.ready to report
        # that the device is ready for use.
        # This assumes that the pairing skill is loaded as a priority skill
        # before the rest of the skills are loaded.
        if not is_paired():
            self.add_event("mycroft.ready", self.handle_mycroft_ready)

        platform = self.config_core['enclosure'].get('platform', 'unknown')
        if platform in PLATFORMS_WITH_BUTTON:
            self.paired_dialog = 'pairing.paired'
        else:
            self.paired_dialog = 'pairing.paired.no.button'
Ejemplo n.º 31
0
    def __init__(self, cache=None):
        super(RemoteConf, self).__init__(None)

        cache = cache or join(xdg.BaseDirectory.xdg_cache_home, 'mycroft',
                              'web_cache.json')
        from mycroft.api import is_paired
        if not is_paired():
            self.load_local(cache)
            return

        try:
            # Here to avoid cyclic import
            from mycroft.api import DeviceApi
            api = DeviceApi()
            setting = api.get_settings()

            location = None
            try:
                location = api.get_location()
            except RequestException as e:
                LOG.error(
                    "RequestException fetching remote location: {}".format(
                        str(e)))
                if exists(cache) and isfile(cache):
                    location = load_commented_json(cache).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 RequestException as e:
            LOG.error(
                "RequestException fetching remote configuration: {}".format(
                    str(e)))
            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)
Ejemplo n.º 32
0
    def write_skills_data(skills_data):
        """ Backwards compatibility write function.

            Converts the old style skill.json storage to new style storage.

            TODO: Remove in 19.02
        """
        msm = SkillManager.create_msm()
        with msm.lock, SkillManager.get_lock():
            msm_data = msm.load_skills_data()
            skills = []
            for key in skills_data:
                skills_data[key]['name'] = key
                skills.append(skills_data[key])
            msm_data['skills'] = skills
            msm.skills_data_hash = ''  # Force write
            msm.write_skills_data(msm_data)
            if is_paired():
                DeviceApi().upload_skills_data(msm_data)
Ejemplo n.º 33
0
    def write_skills_data(skills_data):
        """ Backwards compatibility write function.

            Converts the old style skill.json storage to new style storage.

            TODO: Remove in 19.02
        """
        msm = SkillManager.create_msm()
        with msm.lock, SkillManager.get_lock():
            msm_data = msm.load_skills_data()
            skills = []
            for key in skills_data:
                skills_data[key]['name'] = key
                skills.append(skills_data[key])
            msm_data['skills'] = skills
            msm.skills_data_hash = ''  # Force write
            msm.write_skills_data(msm_data)
            if is_paired():
                DeviceApi().upload_skills_data(msm_data)
Ejemplo n.º 34
0
    def __init__(self, cache=None):
        super(RemoteConf, self).__init__(None)

        cache = cache or WEB_CONFIG_CACHE
        from mycroft.api import is_paired
        if not is_paired():
            self.load_local(cache)
            return

        try:
            # Here to avoid cyclic import
            from mycroft.api import DeviceApi
            api = DeviceApi()
            setting = api.get_settings()

            try:
                location = api.get_location()
            except RequestException as e:
                LOG.error("RequestException fetching remote location: {}"
                          .format(str(e)))
                if exists(cache) and isfile(cache):
                    location = load_commented_json(cache).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 RequestException as e:
            LOG.error("RequestException fetching remote configuration: {}"
                      .format(str(e)))
            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)
Ejemplo n.º 35
0
    def initialize(self):
        # specific distros can override this
        if "pairing_url" not in self.settings:
            self.settings["pairing_url"] = "home.mycroft.ai"
        if "color" not in self.settings:
            self.settings["color"] = "#FF0000"

        if not is_paired():
            # If the device isn't paired catch mycroft.ready to report
            # that the device is ready for use.
            # This assumes that the pairing skill is loaded as a priority skill
            # before the rest of the skills are loaded.
            self.add_event("mycroft.ready", self.handle_mycroft_ready)
            self.in_pairing = True
            self.make_active()  # to enable converse

        # show loading screen once wifi setup ends
        if not connected():
            self.bus.once("ovos.wifi.setup.completed",
                          self.show_loading_screen)
        else:
            # this is usually the first skill to load
            # ASSUMPTION: is the first skill in priority list
            self.show_loading_screen()

        self.add_event("mycroft.not.paired", self.not_paired)

        # events for GUI interaction
        self.gui.register_handler("mycroft.device.set.backend",
                                  self.handle_backend_selected_event)
        self.gui.register_handler("mycroft.device.confirm.backend",
                                  self.handle_backend_confirmation_event)
        self.gui.register_handler("mycroft.return.select.backend",
                                  self.handle_return_event)
        self.gui.register_handler("mycroft.device.confirm.stt",
                                  self.select_stt)
        self.gui.register_handler("mycroft.device.confirm.tts",
                                  self.select_tts)
        self.nato_dict = self.translate_namedvalues('codes')
Ejemplo n.º 36
0
def check_connection():
    """
        Check for network connection. If not paired trigger pairing.
        Runs as a Timer every second until connection is detected.
    """
    if connected():
        ws.emit(Message('mycroft.internet.connected'))
        # check for pairing, if not automatically start pairing
        if not is_paired():
            # begin the process
            payload = {
                'utterances': ["pair my device"],
                'lang': "en-us"
            }
            ws.emit(Message("recognizer_loop:utterance", payload))
        else:
            from mycroft.api import DeviceApi
            api = DeviceApi()
            api.update_version()
    else:
        thread = Timer(1, check_connection)
        thread.daemon = True
        thread.start()
Ejemplo n.º 37
0
    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))

        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

        with open(self.dot_msm, 'a'):
            os.utime(self.dot_msm, None)
        self.next_download = time.time() + self.update_interval

        return True
Ejemplo n.º 38
0
def check_connection():
    """
        Check for network connection. If not paired trigger pairing.
        Runs as a Timer every second until connection is detected.
    """
    if connected():
        enclosure = EnclosureAPI(bus)

        if is_paired():
            # Skip the sync message when unpaired because the prompt to go to
            # home.mycrof.ai will be displayed by the pairing skill
            enclosure.mouth_text(dialog.get("message_synching.clock"))

        # Force a sync of the local clock with the internet
        config = Configuration.get()
        platform = config['enclosure'].get("platform", "unknown")
        if platform in ['mycroft_mark_1', 'picroft']:
            bus.wait_for_response(Message('system.ntp.sync'),
                                  'system.ntp.sync.complete', 15)

        if not is_paired():
            try_update_system(platform)

        # Check if the time skewed significantly.  If so, reboot
        skew = abs((time.monotonic() - start_ticks) -
                   (time.time() - start_clock))
        if skew > 60 * 60:
            # Time moved by over an hour in the NTP sync. Force a reboot to
            # prevent weird things from occcurring due to the 'time warp'.
            #
            data = {'utterance': dialog.get("time.changed.reboot")}
            bus.emit(Message("speak", data))
            wait_while_speaking()

            # provide visual indicators of the reboot
            enclosure.mouth_text(dialog.get("message_rebooting"))
            enclosure.eyes_color(70, 65, 69)  # soft gray
            enclosure.eyes_spin()

            # give the system time to finish processing enclosure messages
            time.sleep(1.0)

            # reboot
            bus.emit(Message("system.reboot"))
            return
        else:
            bus.emit(Message("enclosure.mouth.reset"))
            time.sleep(0.5)

        enclosure.eyes_color(189, 183, 107)  # dark khaki
        enclosure.mouth_text(dialog.get("message_loading.skills"))

        bus.emit(Message('mycroft.internet.connected'))
        # check for pairing, if not automatically start pairing
        try:
            if not is_paired(ignore_errors=False):
                payload = {'utterances': ["pair my device"], 'lang': "en-us"}
                bus.emit(Message("recognizer_loop:utterance", payload))
            else:
                from mycroft.api import DeviceApi
                api = DeviceApi()
                api.update_version()
        except BackendDown:
            data = {'utterance': dialog.get("backend.down")}
            bus.emit(Message("speak", data))
            bus.emit(Message("backend.down"))

    else:
        thread = Timer(1, check_connection)
        thread.daemon = True
        thread.start()
Ejemplo n.º 39
0
    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))

        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

        with open(self.dot_msm, 'a'):
            os.utime(self.dot_msm, None)
        self.next_download = time.time() + self.update_interval

        return True
Ejemplo n.º 40
0
def check_connection():
    """
        Check for network connection. If not paired trigger pairing.
        Runs as a Timer every second until connection is detected.
    """
    if connected():
        enclosure = EnclosureAPI(bus)

        if is_paired():
            # Skip the sync message when unpaired because the prompt to go to
            # home.mycrof.ai will be displayed by the pairing skill
            enclosure.mouth_text(dialog.get("message_synching.clock"))

        # Force a sync of the local clock with the internet
        config = Configuration.get()
        platform = config['enclosure'].get("platform", "unknown")
        if platform in ['mycroft_mark_1', 'picroft']:
            bus.wait_for_response(Message('system.ntp.sync'),
                                  'system.ntp.sync.complete', 15)

        if not is_paired():
            try_update_system(platform)

        # Check if the time skewed significantly.  If so, reboot
        skew = abs((time.monotonic() - start_ticks) -
                   (time.time() - start_clock))
        if skew > 60 * 60:
            # Time moved by over an hour in the NTP sync. Force a reboot to
            # prevent weird things from occcurring due to the 'time warp'.
            #
            data = {'utterance': dialog.get("time.changed.reboot")}
            bus.emit(Message("speak", data))
            wait_while_speaking()

            # provide visual indicators of the reboot
            enclosure.mouth_text(dialog.get("message_rebooting"))
            enclosure.eyes_color(70, 65, 69)  # soft gray
            enclosure.eyes_spin()

            # give the system time to finish processing enclosure messages
            time.sleep(1.0)

            # reboot
            bus.emit(Message("system.reboot"))
            return
        else:
            bus.emit(Message("enclosure.mouth.reset"))
            time.sleep(0.5)

        enclosure.eyes_color(189, 183, 107)  # dark khaki
        enclosure.mouth_text(dialog.get("message_loading.skills"))

        bus.emit(Message('mycroft.internet.connected'))
        # check for pairing, if not automatically start pairing
        try:
            if not is_paired(ignore_errors=False):
                payload = {
                    'utterances': ["pair my device"],
                    'lang': "en-us"
                }
                bus.emit(Message("recognizer_loop:utterance", payload))
            else:
                from mycroft.api import DeviceApi
                api = DeviceApi()
                api.update_version()
        except BackendDown:
            data = {'utterance': dialog.get("backend.down")}
            bus.emit(Message("speak", data))
            bus.emit(Message("backend.down"))

    else:
        thread = Timer(1, check_connection)
        thread.daemon = True
        thread.start()
Ejemplo n.º 41
0
def check_connection():
    """
        Check for network connection. If not paired trigger pairing.
        Runs as a Timer every second until connection is detected.
    """
    if connected():
        enclosure = EnclosureAPI(ws)

        if is_paired():
            # Skip the sync message when unpaired because the prompt to go to
            # home.mycrof.ai will be displayed by the pairing skill
            enclosure.mouth_text(mycroft.dialog.get("message_synching.clock"))
        # Force a sync of the local clock with the internet
        ws.emit(Message("system.ntp.sync"))
        time.sleep(15)   # TODO: Generate/listen for a message response...

        # Check if the time skewed significantly.  If so, reboot
        skew = abs((monotonic.monotonic() - start_ticks) -
                   (time.time() - start_clock))
        if skew > 60*60:
            # Time moved by over an hour in the NTP sync. Force a reboot to
            # prevent weird things from occcurring due to the 'time warp'.
            #
            ws.emit(Message("speak", {'utterance':
                    mycroft.dialog.get("time.changed.reboot")}))
            wait_while_speaking()

            # provide visual indicators of the reboot
            enclosure.mouth_text(mycroft.dialog.get("message_rebooting"))
            enclosure.eyes_color(70, 65, 69)  # soft gray
            enclosure.eyes_spin()

            # give the system time to finish processing enclosure messages
            time.sleep(1.0)

            # reboot
            ws.emit(Message("system.reboot"))
            return

        ws.emit(Message('mycroft.internet.connected'))
        # check for pairing, if not automatically start pairing
        if not is_paired():
            # begin the process
            payload = {
                'utterances': ["pair my device"],
                'lang': "en-us"
            }
            ws.emit(Message("recognizer_loop:utterance", payload))
        else:
            if is_paired():
                # Skip the  message when unpaired because the prompt to go
                # to home.mycrof.ai will be displayed by the pairing skill
                enclosure.mouth_text(mycroft.dialog.get("message_updating"))

            from mycroft.api import DeviceApi
            api = DeviceApi()
            api.update_version()
    else:
        thread = Timer(1, check_connection)
        thread.daemon = True
        thread.start()