def is_paired(ignore_errors=True): """ Determine if this device is actively paired with a web backend Determines if the installation of Mycroft has been paired by the user with the backend system, and if that pairing is still active. Returns: bool: True if paired with backend """ global _paired_cache if _paired_cache: # NOTE: This assumes once paired, the unit remains paired. So # un-pairing must restart the system (or clear this value). # The Mark 1 does perform a restart on RESET. return True try: api = DeviceApi() device = api.get() _paired_cache = api.identity.uuid is not None and \ api.identity.uuid != "" return _paired_cache except HTTPError as e: if e.response.status_code == 401: return False except Exception as e: LOG.warning('Could not get device info: ' + repr(e)) if ignore_errors: return False if connected(): raise BackendDown raise InternetDown
def install_default_skills(speak=True): """ Install default skill set using msm. Args: speak (optional): Enable response for success. Default True """ if exists(MSM_BIN): p = subprocess.Popen(MSM_BIN + " default", stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True) (output, err) = p.communicate() res = p.returncode if res == 0 and speak: # ws.emit(Message("speak", { # 'utterance': mycroft.dialog.get("skills updated")})) pass elif not connected(): LOG.error('msm failed, network connection is not available') ws.emit(Message("speak", { 'utterance': mycroft.dialog.get("no network connection")})) elif res != 0: LOG.error('msm failed with error {}: {}'.format(res, output)) ws.emit(Message("speak", { 'utterance': mycroft.dialog.get( "sorry I couldn't install default skills")})) else: LOG.error("Unable to invoke Mycroft Skill Manager: " + MSM_BIN)
def is_paired(ignore_errors=True): """ Determine if this device is actively paired with a web backend Determines if the installation of Mycroft has been paired by the user with the backend system, and if that pairing is still active. Returns: bool: True if paired with backend """ global _paired_cache if _paired_cache: # NOTE: This assumes once paired, the unit remains paired. So # un-pairing must restart the system (or clear this value). # The Mark 1 does perform a restart on RESET. return True try: api = DeviceApi() device = api.get() _paired_cache = api.identity.uuid is not None and \ api.identity.uuid != "" return _paired_cache except HTTPError as e: if e.response.status_code == 401: return False except Exception as e: LOG.warning('Could not get device infO: ' + repr(e)) if ignore_errors: return False if connected(): raise BackendDown raise InternetDown
def _do_net_check(self): # TODO: This should live in the derived Enclosure, e.g. Enclosure_Mark1 LOG.info("Checking internet connection") if not connected(): # and self.conn_monitor is None: if has_been_paired(): # TODO: Enclosure/localization self.ws.emit(Message("speak", { 'utterance': "This unit is not connected to the Internet." " Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu" })) else: # Begin the unit startup process, this is the first time it # is being run with factory defaults. # TODO: This logic should be in Enclosure_Mark1 # TODO: Enclosure/localization # Don't listen to mic during this out-of-box experience self.ws.emit(Message("mycroft.mic.mute", None)) # Kick off wifi-setup automatically self.ws.emit(Message("mycroft.wifi.start", {'msg': "Hello I am Mycroft, your new " "assistant. To assist you I need to be " "connected to the internet. You can " "either plug me in with a network cable," " or use wifi. To setup wifi ", 'allow_timeout': False}))
def install_default_skills(speak=True): """ Install default skill set using msm. Args: speak (optional): Enable response for success. Default True """ if exists(MSM_BIN): p = subprocess.Popen(MSM_BIN + " default", stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True) (output, err) = p.communicate() res = p.returncode if res == 0 and speak: # ws.emit(Message("speak", { # 'utterance': mycroft.dialog.get("skills updated")})) pass elif not connected(): logger.error('msm failed, network connection is not available') ws.emit(Message("speak", { 'utterance': mycroft.dialog.get("no network connection")})) elif res != 0: logger.error('msm failed with error {}: {}'.format(res, output)) ws.emit(Message("speak", { 'utterance': mycroft.dialog.get( "sorry I couldn't install default skills")})) else: logger.error("Unable to invoke Mycroft Skill Manager: " + MSM_BIN)
def check_connection(): if connected(): ws.emit(Message('mycroft.internet.connected')) else: thread = Timer(1, check_connection) thread.daemon = True thread.start()
def on_no_internet(self, event=None): if connected(): # One last check to see if connection was established return if time.time() - Enclosure._last_internet_notification < 30: # don't bother the user with multiple notifications with 30 secs return Enclosure._last_internet_notification = time.time() # TODO: This should go into EnclosureMark1 subclass of Enclosure. if has_been_paired(): # Handle the translation within that code. self.ws.emit( Message( "speak", { 'utterance': "This device is not connected to the Internet. " "Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu" })) else: # enter wifi-setup mode automatically self.ws.emit(Message('system.wifi.setup', {'lang': self.lang}))
def _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 initialize(self): # Initialize... if self.settings.get('auto_brightness') is None: self.settings['auto_brightness'] = False if self.settings.get('eye color') is None: self.settings['eye color'] = "default" self.brightness_dict = self.translate_namedvalues('brightness.levels') self.color_dict = self.translate_namedvalues('colors') # Handle changing the eye color once Mark 1 is ready to go # (Part of the statup sequence) try: self.add_event('mycroft.internet.connected', self.handle_internet_connected) except: pass # TODO: Add MycroftSkill.register_entity_list() and use the # self.color_dict.keys() instead of duplicating data self.register_entity_file('color.entity') if connected(): # Connected at startup: setting eye color self.enclosure.mouth_reset() self.set_eye_color(self.settings['eye color'], initing=True)
def transcribe(self, audio): try: # Invoke the STT engine on the audio clip text = self.stt.execute(audio).lower().strip() LOG.debug("STT: " + text) return text except sr.RequestError as e: LOG.error("Could not request Speech Recognition {0}".format(e)) except ConnectionError as e: LOG.error("Connection Error: {0}".format(e)) self.emitter.emit("recognizer_loop:no_internet") except HTTPError as e: if e.response.status_code == 401: LOG.warning("Access Denied at mycroft.ai") return "pair my device" # phrase to start the pairing process else: LOG.error(e.__class__.__name__ + ': ' + str(e)) except RequestException as e: LOG.error(e.__class__.__name__ + ': ' + str(e)) except Exception as e: self.emitter.emit('recognizer_loop:speech.recognition.unknown') if isinstance(e, IndexError): LOG.info('no words were transcribed') else: LOG.error(e) LOG.error("Speech Recognition could not understand audio") return None if connected(): dialog_name = 'backend.down' else: dialog_name = 'not connected to the internet' self.emitter.emit('speak', {'utterance': dialog.get(dialog_name)})
def transcribe(self, audio): def send_unknown_intent(): """ Send message that nothing was transcribed. """ self.emitter.emit('recognizer_loop:speech.recognition.unknown') try: # Invoke the STT engine on the audio clip text = self.stt.execute(audio) if text is not None: text = text.lower().strip() LOG.debug("STT: " + text) else: send_unknown_intent() LOG.info('no words were transcribed') return text except sr.RequestError as e: LOG.error("Could not request Speech Recognition {0}".format(e)) except ConnectionError as e: LOG.error("Connection Error: {0}".format(e)) self.emitter.emit("recognizer_loop:no_internet") except RequestException as e: LOG.error(e.__class__.__name__ + ': ' + str(e)) except Exception as e: send_unknown_intent() LOG.error(e) LOG.error("Speech Recognition could not understand audio") return None if connected(): dialog_name = 'backend.down' else: dialog_name = 'not connected to the internet' self.emitter.emit('speak', {'utterance': dialog.get(dialog_name)})
def transcribe(self, audio_segments): utterances = [] threads = [] if connected(): for audio in audio_segments: if self._audio_length(audio) < self.MIN_AUDIO_SIZE: logger.debug("Audio too short to send to STT") continue target = self._create_remote_stt_runnable(audio, utterances) t = threading.Thread(target=target) t.start() threads.append(t) for thread in threads: thread.join() if len(utterances) > 0: payload = { 'utterances': utterances, 'session': SessionManager.get().session_id } self.emitter.emit("recognizer_loop:utterance", payload) self.metrics.attr('utterances', utterances) else: raise sr.UnknownValueError else: # TODO: Localization self.__speak("This device is not connected to the Internet")
def _do_net_check(self): # TODO: This should live in the derived Enclosure, e.g. EnclosureMark1 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 EnclosureMark1 # TODO: Enclosure/localization # Don't listen to mic during this out-of-box experience self.bus.emit(Message("mycroft.mic.mute")) # Setup handler to unmute mic at the end of on boarding # i.e. after pairing is complete self.bus.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.bus.emit(Message('system.wifi.setup', data))
def _do_net_check(self): # TODO: This should live in the derived Enclosure, e.g. Enclosure_Mark1 LOG.info("Checking internet connection") if not connected(): # and self.conn_monitor is None: if has_been_paired(): # TODO: Enclosure/localization self.ws.emit( Message( "speak", { 'utterance': "This unit is not connected to the Internet." " Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu" })) else: # Begin the unit startup process, this is the first time it # is being run with factory defaults. # TODO: This logic should be in Enclosure_Mark1 # TODO: Enclosure/localization # Don't listen to mic during this out-of-box experience self.ws.emit(Message("mycroft.mic.mute")) # 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')) # Kick off wifi-setup automatically data = {'allow_timeout': False, 'lang': self.lang} self.ws.emit(Message('mycroft.wifi.start', data)) else: # Indicate we are checking for updates from the internet now... self.writer.write("mouth.text=< < < UPDATING < < < ")
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()
def check_remote_pairing(ignore_errors): """Check that a basic backend endpoint accepts our pairing. Arguments: ignore_errors (bool): True if errors should be ignored when Returns: True if pairing checks out, otherwise False. """ try: DeviceApi().get() return True except HTTPError as e: if e.response.status_code == 401: return False error = e except Exception as e: error = e LOG.warning('Could not get device info: {}'.format(repr(error))) if ignore_errors: return False if isinstance(error, HTTPError): if connected(): raise BackendDown from error else: raise InternetDown from error else: raise error
def update_skills(self, quick=False): """Invoke MSM to install default skills and/or update installed skills Args: quick (bool): Expedite the download by running with more threads? """ LOG.info('Beginning skill update...') self.msm._device_skill_state = None # TODO: Proper msm method success = True if connected(): self._load_installed_skills() with self.msm_lock, self.msm.lock: self._apply_install_or_update(quick) self._save_installed_skills() # Schedule retry in 5 minutes on failure, after 10 shorter periods # Go back to 60 minutes wait if self.default_skill_install_error and self.install_retries < 10: self._schedule_retry() success = False else: self.install_retries = 0 self._update_download_time() else: self.handle_not_connected() success = False if success: LOG.info('Skill update complete') return success
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 """ # Don't invoke msm if already running if exists(MSM_BIN) and self.__msm_lock.acquire(): try: # Invoke the MSM script to do the hard work. LOG.debug("==== Invoking Mycroft Skill Manager: " + MSM_BIN) p = subprocess.Popen(MSM_BIN + " default", stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True) (output, err) = p.communicate() res = p.returncode # Always set next update to an hour from now if successful if res == 0: self.next_download = time.time() + 60 * MINUTES if res == 0 and speak: self.ws.emit( Message("speak", { 'utterance': mycroft.dialog.get("skills updated") })) return True elif not connected(): LOG.error('msm failed, network connection not available') if speak: self.ws.emit( Message( "speak", { 'utterance': mycroft.dialog.get( "not connected to the internet") })) self.next_download = time.time() + 5 * MINUTES return False elif res != 0: LOG.error('msm failed with error {}: {}'.format( res, output)) if speak: self.ws.emit( Message( "speak", { 'utterance': mycroft.dialog.get( "sorry I couldn't install default skills" ) })) self.next_download = time.time() + 5 * MINUTES return False finally: self.__msm_lock.release() else: LOG.error("Unable to invoke Mycroft Skill Manager: " + MSM_BIN)
def from_wifi(self, update=True): self.log.info("Retrieving location data from available wifi") if connected(): url = "https://www.googleapis.com/geolocation/v1/geolocate" \ "?key=" + self.geolocateApiKey # TODO this only supports connected to networks, change available = Cell.all('wlan0') mac = [] signal = [] channel = [] for wifi in available: channel.append(wifi.channel) mac.append(wifi.address) signal.append(wifi.signal) payload, headers = self.build_wifi_JSON(mac, signal, channel) try: response = requests.post(url, data=json.dumps(payload), headers=headers) text = json.loads(response.text) if response.ok == False: # Check if the response was ok if text['error']['errors'][0][ 'reason'] == 'dailyLimitExceeded': self.log.error('You have exceeded you daily limit') elif text['error']['errors'][0]['reason'] == 'keyInvalid': self.log.error('Your API key is not valid for the ' 'Google Maps Geolocation API') elif text['error']['errors'][0][ 'reason'] == 'userRateLimitExceeded': self.log.error( 'You\'ve exceeded the requests per ' 'second per user limit that you configured in the Google Developers Console' ) elif text['error']['errors'][0]['reason'] == 'notFound': self.log.error( 'The request was valid, but no results were returned' ) elif text['error']['errors'][0]['reason'] == 'parseError': self.log.error('The request body is not valid JSON') else: self.log.error( 'Unknown error in the geolocation ' 'response. Might be caught in an exception.') except Exception, e: self.log.error(str(e)) return decimal.getcontext( ).prec = 15 # Setting precision for lat/lng response lng = decimal.Decimal(text['location']['lng']) + 0 lat = decimal.Decimal(text['location']['lat']) + 0 accuracy = response['accuracy'] geolocator = Nominatim() location = geolocator.reverse(str(lat) + ", " + str(lng)) adress = location.adress
def handle_update_intent(self, message): if connected(): self.speak("updating location from ip address") config = self.get_location("ip") city = config.get("city", {}).get("name", "unknown city") country = config.get("city", {}).get("region").get("country").get( "name", "unknow country") else: self.speak("Cant do that offline")
def on_no_internet(self, event=None): if connected(): # One last check to see if connection was established return if time.time() - Enclosure._last_internet_notification < 30: # don't bother the user with multiple notifications with 30 secs return Enclosure._last_internet_notification = time.time()
def initialize(self): # Initialize... if self.settings.get('auto_brightness') is None: self.settings['auto_brightness'] = False if self.settings.get('eye color') is None: self.settings['eye color'] = "default" if self.settings.get('auto_dim_eyes') is None: self.settings['auto_dim_eyes'] = 'false' if self.settings.get('use_listenting_beep') is None: self.settings['use_listening_beep'] = 'true' self.brightness_dict = self.translate_namedvalues('brightness.levels') self.color_dict = self.translate_namedvalues('colors') try: # Handle changing the eye color once Mark 1 is ready to go # (Part of the statup sequence) self.add_event('mycroft.internet.connected', self.handle_internet_connected) # Handle the 'waking' visual self.add_event('recognizer_loop:record_begin', self.handle_listener_started) self.start_idle_check() # Handle the 'busy' visual self.emitter.on('mycroft.skill.handler.start', self.on_handler_started) self.emitter.on('mycroft.skill.handler.complete', self.on_handler_complete) self.emitter.on('recognizer_loop:audio_output_start', self.on_handler_interactingwithuser) self.emitter.on('enclosure.mouth.think', self.on_handler_interactingwithuser) self.emitter.on('enclosure.mouth.events.deactivate', self.on_handler_interactingwithuser) self.emitter.on('enclosure.mouth.text', self.on_handler_interactingwithuser) except: pass # TODO: Add MycroftSkill.register_entity_list() and use the # self.color_dict.keys() instead of duplicating data self.register_entity_file('color.entity') if connected(): # Connected at startup: setting eye color self.enclosure.mouth_reset() self.set_eye_color(self.settings['eye color'], initing=True) # Update use of wake-up beep self._sync_wake_beep_setting() self.settings.set_changed_callback(self.on_websettings_changed)
def _do_net_check(self): # give system 5 seconds to resolve network or get plugged in sleep(5) LOG.info("Checking internet connection again") if not connected() and self.conn_monitor is None: # TODO: Enclosure/localization self._speak_and_show( "This device is not connected to the Internet. Either plug " "in a network cable or hold the button on top for two " "seconds, then select wifi from the menu", None)
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()
def from_ip(self, update=True): self.log.info("Retrieving location data from ip adress") if connected(): response = unirest.get("https://ipapi.co/json/") city = response.body.get("city") region_code = response.body.get("region_code") country = response.body.get("country") country_name = response.body.get("country_name") region = response.body.get("region") lon = response.body.get("longitude") lat = response.body.get("latitude") timezone = response.body.get("timezone") region_data = { "code": region_code, "name": region, "country": { "code": country, "name": country_name } } city_data = { "code": city, "name": city, "state": region_data, "region": region_data } timezone_data = { "code": timezone, "name": timezone, "dstOffset": 3600000, "offset": -21600000 } coordinate_data = {"latitude": float(lat), "longitude": float(lon)} location_data = { "city": city_data, "coordinate": coordinate_data, "timezone": timezone_data } config = {"location": location_data} if update: try: # jarbas core skill function self.config_update(config) except: pass return config else: self.log.warning("No internet connection, could not update " "location from ip adress") return {}
def handle_update_intent(self, message): if connected(): # TODO source select from utterance source = self.settings["update_source"] self.speak("updating location from " + source.replace("-", " ").replace("_", " ")) self.update_location(source) city = self.location.get("city", {}).get("name", "unknown city") country = self.location.get("city", {}).get("state", {}).get( "country", {}).get("name", "unknown country") text = self.location.get("address", city + ", " + country) self.speak(text) else: self.speak("Cant do that offline")
def process(self, audio): SessionManager.touch() payload = { 'utterance': self.mycroft_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.warn("Audio too short to be processed") elif connected(): self.transcribe(audio) else: self.__speak("Mycroft seems not to be connected to the Internet")
def install_default_skills(speak=True): if exists(MSM_BIN): p = subprocess.Popen(MSM_BIN + " default", stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True) t = p.communicate()[0] if t.splitlines()[-1] == "Installed!" and speak: ws.emit(Message("speak", { 'utterance': mycroft.dialog.get("skills updated")})) elif not connected(): ws.emit(Message("speak", { 'utterance': mycroft.dialog.get("no network connection")})) else: logger.error("Unable to invoke Mycroft Skill Manager: " + MSM_BIN)
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()
def on_arduino_responded(self, event=None): self.eyes = EnclosureEyes(self.bus, self.writer) self.mouth = EnclosureMouth(self.bus, self.writer) self.system = EnclosureArduino(self.bus, self.writer) self.__register_events() self.__reset() self.arduino_responded = True # verify internet connection and prompt user on bootup if needed if not connected(): # We delay this for several seconds to ensure that the other # clients are up and connected to the messagebus in order to # receive the "speak". This was sometimes happening too # quickly and the user wasn't notified what to do. Timer(5, self._do_net_check).start()
def process(self, audio): SessionManager.touch() payload = { 'utterance': self.mycroft_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.warn("Audio too short to be processed") self.emitter.emit("recognizer_loop:tooshort", {}) elif connected(): self.transcribe(audio) else: self.__speak("Mycroft seems not to be connected to the Internet")
def on_arduino_responded(self, event=None): # self.eyes = EnclosureEyes(self.bus, self.writer) # self.mouth = EnclosureMouth(self.bus, self.writer) self.system = EnclosureArduino(self.bus, self.writer) self.__register_events() self.__reset() self.arduino_responded = True # verify internet connection and prompt user on bootup if needed if not connected(): # We delay this for several seconds to ensure that the other # clients are up and connected to the messagebus in order to # receive the "speak". This was sometimes happening too # quickly and the user wasn't notified what to do. Timer(5, self._do_net_check).start()
def _starting_up(): """ Start loading skills. Starts - SkillManager to load/reloading of skills when needed - a timer to check for internet connection - adapt intent service - padatious intent service """ global bus, skill_manager, event_scheduler, connect_to_mycroft_backend bus.on('intent_failure', FallbackSkill.make_intent_failure_handler(bus)) # Create the Intent manager, which converts utterances to intents # This is the heart of the voice invoked skill system service = IntentService(bus) try: PadatiousService(bus, service) except Exception as e: LOG.exception('Failed to create padatious handlers ' '({})'.format(repr(e))) event_scheduler = EventScheduler(bus) # Create a thread that monitors the loaded skills, looking for updates try: skill_manager = SkillManager(bus) except MsmException: # skill manager couldn't be created, wait for network connection and # retry LOG.info( 'Msm is uninitialized and requires network connection', 'to fetch skill information\n' 'Waiting for network connection...') while not connected(): time.sleep(30) skill_manager = SkillManager(bus) skill_manager.daemon = True # Wait until priority skills have been loaded before checking # network connection # print(skill_manager.msm.repo.get_default_skill_names()) skill_manager.load_priority() skill_manager.start() bus.emit(Message('skill.manager.initialised')) if connect_to_mycroft_backend: check_connection() else: check_connection_without_backend()
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()
def skills_manager(message): global skills_manager_timer, ws if connected(): if skills_manager_timer is None: ws.emit( Message("speak", {'utterance': mycroft.dialog.get("checking for updates")})) # Install default skills and look for updates via Github logger.debug("==== Invoking Mycroft Skill Manager: " + MSM_BIN) install_default_skills(False) # Perform check again once and hour skills_manager_timer = Timer(3600, _skills_manager_dispatch) skills_manager_timer.daemon = True skills_manager_timer.start()
def __init__(self): super().__init__() # Notifications from mycroft-core self.bus.on("enclosure.notify.no_internet", self.on_no_internet) # initiates the web sockets on display manager # NOTE: this is a temporary place to connect the display manager init_display_manager_bus_connection() # verify internet connection and prompt user on bootup if needed if not connected(): # We delay this for several seconds to ensure that the other # clients are up and connected to the messagebus in order to # receive the "speak". This was sometimes happening too # quickly and the user wasn't notified what to do. Timer(5, self._do_net_check).start()
def 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 """ # Don't invoke msm if already running if exists(MSM_BIN) and self.__msm_lock.acquire(): try: # Invoke the MSM script to do the hard work. LOG.debug("==== Invoking Mycroft Skill Manager: " + MSM_BIN) p = subprocess.Popen(MSM_BIN + " default", stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True) (output, err) = p.communicate() res = p.returncode # Always set next update to an hour from now if successful if res == 0: self.next_download = time.time() + 60 * MINUTES if res == 0 and speak: self.ws.emit(Message("speak", {'utterance': mycroft.dialog.get("skills updated")})) return True elif not connected(): LOG.error('msm failed, network connection not available') if speak: self.ws.emit(Message("speak", { 'utterance': mycroft.dialog.get( "not connected to the internet")})) self.next_download = time.time() + 5 * MINUTES return False elif res != 0: LOG.error( 'msm failed with error {}: {}'.format( res, output)) if speak: self.ws.emit(Message("speak", { 'utterance': mycroft.dialog.get( "sorry I couldn't install default skills")})) self.next_download = time.time() + 5 * MINUTES return False finally: self.__msm_lock.release() else: LOG.error("Unable to invoke Mycroft Skill Manager: " + MSM_BIN)
def skills_manager(message): global skills_manager_timer, ws if connected(): if skills_manager_timer is None: pass # ws.emit( # Message("speak", {'utterance': # mycroft.dialog.get("checking for updates")})) # Install default skills and look for updates via Github logger.debug("==== Invoking Mycroft Skill Manager: " + MSM_BIN) install_default_skills(False) # Perform check again once and hour skills_manager_timer = Timer(3600, _skills_manager_dispatch) skills_manager_timer.daemon = True skills_manager_timer.start()
def run(self): try: # When the system first boots up, check for a valid internet # connection. LOG.info("Checking internet connection") if not connected(): LOG.info("No connection initially, waiting 20...") self.net_check = threading.Thread( target=self._do_net_check, args={}) self.net_check.daemon = True self.net_check.start() else: LOG.info("Connection found!") self.ws.run_forever() except Exception as e: LOG.error("Error: {0}".format(e)) self.stop()
def on_no_internet(self, event=None): if connected(): # One last check to see if connection was established return if time.time() - Enclosure._last_internet_notification < 30: # don't bother the user with multiple notifications with 30 secs return Enclosure._last_internet_notification = time.time() # TODO: This should go into EnclosureMark1 subclass of Enclosure. if has_been_paired(): # Handle the translation within that code. self.bus.emit(Message("speak", { 'utterance': "This device is not connected to the Internet. " "Either plug in a network cable or set up your " "wifi connection."})) else: # enter wifi-setup mode automatically self.bus.emit(Message('system.wifi.setup', {'lang': self.lang}))
def _starting_up(): """ Start loading skills. Starts - SkillManager to load/reloading of skills when needed - a timer to check for internet connection - adapt intent service - padatious intent service """ global bus, skill_manager, event_scheduler bus.on('intent_failure', FallbackSkill.make_intent_failure_handler(bus)) # Create the Intent manager, which converts utterances to intents # This is the heart of the voice invoked skill system service = IntentService(bus) PadatiousService(bus, service) event_scheduler = EventScheduler(bus) # Create a thread that monitors the loaded skills, looking for updates try: skill_manager = SkillManager(bus) except MsmException: # skill manager couldn't be created, wait for network connection and # retry LOG.info('Msm is uninitialized and requires network connection', 'to fetch skill information\n' 'Waiting for network connection...') while not connected(): time.sleep(30) skill_manager = SkillManager(bus) skill_manager.daemon = True # Wait until priority skills have been loaded before checking # network connection skill_manager.load_priority() skill_manager.start() check_connection()
def on_no_internet(self, event=None): if connected(): # One last check to see if connection was established return if time.time()-Enclosure._last_internet_notification < 30: # don't bother the user with multiple notifications with 30 secs return Enclosure._last_internet_notification = time.time() # TODO: This should go into EnclosureMark1 subclass of Enclosure. if has_been_paired(): # Handle the translation within that code. self.ws.emit(Message("speak", { 'utterance': "This device is not connected to the Internet. " "Either plug in a network cable or hold the " "button on top for two seconds, then select " "wifi from the menu"})) else: # enter wifi-setup mode automatically self.ws.emit(Message("mycroft.wifi.start"))
def 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()
def install_default_skills(speak=True): """ Install default skill set using msm. Args: speak (optional): Enable response for success. Default True """ if exists(MSM_BIN): res = subprocess.call(MSM_BIN + " default", stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True) if res == 0 and speak: # ws.emit(Message("speak", { # 'utterance': mycroft.dialog.get("skills updated")})) pass elif not connected(): ws.emit(Message("speak", { 'utterance': mycroft.dialog.get("no network connection")})) elif res != 0: ws.emit(Message("speak", { 'utterance': mycroft.dialog.get( "sorry I couldn't install default skills")})) else: logger.error("Unable to invoke Mycroft Skill Manager: " + MSM_BIN)
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()
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()
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
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() default_groups = dict(self.msm.repo.get_default_skill_names()) if self.msm.platform in default_groups: platform_groups = default_groups[self.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 skills_data = self.load_skills_data() new_installs = [] updated_skills = [] def install_or_update(skill): """Install missing defaults and update existing skills""" if skills_data.get(skill.name, {}).get('beta'): skill.sha = None # Will update to latest version if skill.is_local: skill.update() updated_skills.append(skill.name) if skill.name not in installed_skills: skill.update_deps() installed_skills.add(skill.name) elif skill.name in default_names: try: new_installs.append(skill.name) skill.install() 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: self.msm.apply(install_or_update, self.msm.list()) except MsmException as e: LOG.error('Failed to update skills: {}'.format(repr(e))) for skill_name in new_installs + updated_skills: if skill_name not in skills_data or skill_name in new_installs: t = time.time() if skill_name in new_installs else 1 skills_data.setdefault(skill_name, {})['installed'] = t skills_data[skill_name]['updated'] = 0 self.write_skills_data(skills_data) 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