def handle_paired(event): """Update identity information with pairing data. This is done here to make sure it's only done in a single place. TODO: Is there a reason this isn't done directly in the pairing skill? """ IdentityManager.update(event.data)
def refresh_token(self): LOG.debug('Refreshing token') if identity_lock.acquire(blocking=False): try: data = self.send({ "path": "auth/token", "headers": { "Authorization": "Bearer " + self.identity.refresh, "Device": self.identity.uuid } }) IdentityManager.save(data, lock=False) LOG.debug('Saved credentials') except HTTPError as e: if e.response.status_code == 401: LOG.error('Could not refresh token, invalid refresh code.') else: raise finally: identity_lock.release() else: # Someone is updating the identity wait for release with identity_lock: LOG.debug('Refresh is already in progress, waiting until done') time.sleep(1.2) os.sync() self.identity = IdentityManager.load(lock=False) LOG.debug('new credentials loaded')
def refresh_token(self): LOG.debug('Refreshing token') if identity_lock.acquire(blocking=False): try: data = self.send({ "path": "auth/token", "headers": { "Authorization": "Bearer " + self.identity.refresh } }) IdentityManager.save(data, lock=False) LOG.debug('Saved credentials') except HTTPError as e: if e.response.status_code == 401: LOG.error('Could not refresh token, invalid refresh code.') else: raise finally: identity_lock.release() else: # Someone is updating the identity wait for release with identity_lock: LOG.debug('Refresh is already in progress, waiting until done') time.sleep(1.2) os.sync() self.identity = IdentityManager.load(lock=False) LOG.debug('new credentials loaded')
def refresh_token(self): data = self.send({ "path": "auth/token", "headers": { "Authorization": "Bearer " + self.identity.refresh } }) IdentityManager.save(data)
def __init__(self, config=_config, pairing_code=None): self.config = config self.ws_client = WebsocketClient(host=config.get("host"), port=config.get("port"), path=config.get("route"), ssl=str2bool(config.get("ssl"))) self.identity_manager = IdentityManager() self.identity = self.identity_manager.identity self.pairing_code = pairing_code if pairing_code else generate_pairing_code()
def check_token(self): # If the identity hasn't been loaded, load it if not self.identity.has_refresh(): self.identity = IdentityManager.load() # If refresh is needed perform a refresh if self.identity.refresh and self.identity.is_expired(): self.identity = IdentityManager.load() # if no one else has updated the token refresh it if self.identity.is_expired(): self.refresh_token()
def check_token(self): if self.identity.refresh and self.identity.is_expired(): self.identity = IdentityManager.load() if self.identity.is_expired(): data = self.send({ "path": "auth/token", "headers": { "Authorization": "Bearer " + self.identity.refresh } }) IdentityManager.save(data)
def activate(self): try: token = self.data.get("token") login = self.api.activate(self.state, token) self.enclosure.activate_mouth_events() self.speak_dialog("pairing.paired") IdentityManager.save(login) self.emitter.emit(Message("mycroft.paired", login)) except: self.data["expiration"] -= self.delay if self.data.get("expiration") <= 0: self.data = None self.handle_pairing() else: self.__create_activator()
def handle_update_intent(self, message): identity = IdentityManager().get() if not identity.owner: self.speak_dialog("not.paired") else: ConfigurationManager.load_remote() self.speak_dialog("config.updated")
def __init__(self, path): self.path = path config = ConfigurationManager().get() config_server = config.get("server") self.url = config_server.get("url") self.version = config_server.get("version") self.identity = IdentityManager.get()
def __init__(self, path): self.path = path config = ConfigurationManager.get() config_server = config.get("server") self.url = config_server.get("url") self.version = config_server.get("version") self.identity = IdentityManager.get()
def handle_update_request(self, message): identity = IdentityManager().get() if not identity.owner: self.speak_dialog("not.paired") else: rc = RemoteConfiguration() rc.update() self.speak_dialog("config.updated")
def __init__(self, path): self.path = path config = Configuration.get( [DEFAULT_CONFIG, SYSTEM_CONFIG, USER_CONFIG], cache=False) config_server = config.get("server") self.url = config_server.get("url") self.version = config_server.get("version") self.identity = IdentityManager.get()
def select_stt(self, message): selection = message.data["engine"] self.send_stop_signal("pairing.stt.menu.stop") if selection == "google": self.change_to_chromium() elif selection == "kaldi": self.change_to_kaldi() if not self.using_mock: self.enable_mock() # create pairing file with dummy data login = {"uuid": self.state, "access": "OVOSdbF1wJ4jA5lN6x6qmVk_QvJPqBQZTUJQm7fYzkDyY_Y=", "refresh": "OVOS66c5SpAiSpXbpHlq9HNGl1vsw_srX49t5tCv88JkhuE=", "expires_at": time.time() + 999999} IdentityManager.save(login) self.handle_tts_menu()
def transcribe(self, audio, language="en-US", show_all=False, metrics=None): timer = Stopwatch() timer.start() identity = IdentityManager().get() headers = {} if identity.token: headers['Authorization'] = 'Bearer %s:%s' % (identity.device_id, identity.token) response = requests.post(config.get("proxy_host") + "/stt/google_v2?language=%s&version=%s" % (language, self.version), audio.get_flac_data(), headers=headers) if metrics: t = timer.stop() metrics.timer("mycroft.cerberus.proxy.client.time_s", t) metrics.timer("mycroft.stt.remote.time_s", t) if response.status_code == 401: raise CerberusAccessDenied() try: actual_result = response.json() except: raise UnknownValueError() log.info("STT JSON: " + json.dumps(actual_result)) if show_all: return actual_result # return the best guess if "alternative" not in actual_result: raise UnknownValueError() alternatives = actual_result["alternative"] if len([alt for alt in alternatives if alt.get('confidence')]) > 0: # if there is at least one element with confidence, force it to # the front alternatives.sort(key=lambda e: e.get('confidence', 0.0), reverse=True) for entry in alternatives: if "transcript" in entry: return entry["transcript"] if len(alternatives) > 0: log.error("Found %d entries, but none with a transcript." % len(alternatives)) # no transcriptions available raise UnknownValueError()
def __init__(self, path): self.path = path config = Configuration.get([LocalConf(DEFAULT_CONFIG), LocalConf(SYSTEM_CONFIG), LocalConf(USER_CONFIG)], cache=False) config_server = config.get("server") self.url = config_server.get("url") self.version = config_server.get("version") self.identity = IdentityManager.get()
class DevicePairingClient(object): def __init__(self, config=_config, pairing_code=None): self.config = config self.paired = False self.ws_client = WebsocketClient(host=config.get("host"), port=config.get("port"), path=config.get("route"), ssl=str2bool(config.get("ssl"))) self.identity_manager = IdentityManager() self.identity = self.identity_manager.identity self.pairing_code = (pairing_code if pairing_code else generate_pairing_code()) def on_registration(self, message): # TODO: actually accept the configuration message and store it in # identity identity = self.identity_manager.get() register_payload = message.metadata if register_payload.get("device_id") == identity.device_id: identity.token = register_payload.get('token') identity.owner = register_payload.get('user') self.identity_manager.update(identity) self.ws_client.close() self.paired = True def send_device_info(self): msg = Message("device_info", metadata={ "pairing_code": self.pairing_code, "device_id": self.identity.device_id }) self.ws_client.emit(msg) @staticmethod def print_error(message): print(repr(message)) def run(self): self.ws_client.on('registration', self.on_registration) self.ws_client.on('open', self.send_device_info) self.ws_client.on('error', self.print_error) self.ws_client.run_forever()
def has_been_paired(): """ Determine if this device has ever been paired with a web backend Returns: bool: True if ever paired with backend (not factory reset) """ # This forces a load from the identity file in case the pairing state # has recently changed id = IdentityManager.load() return id.uuid is not None and id.uuid != ""
def __init__(self, path): self.path = path # Load the config, skipping the remote config since we are # getting the info needed to get to it! config = Configuration.get(cache=False, remote=False) config_server = config.get("server") self.url = config_server.get("url") self.version = config_server.get("version") self.identity = IdentityManager.get()
class DevicePairingClient(object): def __init__(self, config=_config, pairing_code=None): self.config = config self.paired = False self.ws_client = WebsocketClient(host=config.get("host"), port=config.get("port"), path=config.get("route"), ssl=str2bool(config.get("ssl"))) self.identity_manager = IdentityManager() self.identity = self.identity_manager.identity self.pairing_code = ( pairing_code if pairing_code else generate_pairing_code()) def on_registration(self, message): # TODO: actually accept the configuration message and store it in # identity identity = self.identity_manager.get() register_payload = message.metadata if register_payload.get("device_id") == identity.device_id: identity.token = register_payload.get('token') identity.owner = register_payload.get('user') self.identity_manager.update(identity) self.ws_client.close() self.paired = True def send_device_info(self): msg = Message("device_info", metadata={ "pairing_code": self.pairing_code, "device_id": self.identity.device_id }) self.ws_client.emit(msg) @staticmethod def print_error(message): print(repr(message)) def run(self): self.ws_client.on('registration', self.on_registration) self.ws_client.on('open', self.send_device_info) self.ws_client.on('error', self.print_error) self.ws_client.run_forever()
def on_activate(self): try: # wait for a signal from the backend that pairing is complete token = self.data.get("token") login = self.api.activate(self.state, token) # shut down thread that repeats the code to the user if self.repeater: self.repeater.cancel() self.repeater = None # is_speaking() and stop_speaking() support is mycroft-core 0.8.16+ try: if mycroft.util.is_speaking(): # Assume speaking is the pairing code. Stop TTS mycroft.util.stop_speaking() except: pass self.enclosure.activate_mouth_events() # clears the display self.speak_dialog("pairing.paired") # wait_while_speaking() support is mycroft-core 0.8.16+ try: mycroft.util.wait_while_speaking() except: pass IdentityManager.save(login) self.emitter.emit(Message("mycroft.paired", login)) # Un-mute. Would have been muted during onboarding for a new # unit, and not dangerous to do if pairing was started # independently. self.emitter.emit(Message("mycroft.mic.unmute", None)) except: if self.last_request < time.time(): self.data = None self.handle_pairing() else: self.__create_activator()
def __init__(self, path): self.path = path # Load the config, skipping the REMOTE_CONFIG since we are # getting the info needed to get to it! config = Configuration.get( [DEFAULT_CONFIG, SYSTEM_CONFIG, USER_CONFIG], cache=False) config_server = config.get("server") self.url = config_server.get("url") self.version = config_server.get("version") self.identity = IdentityManager.get()
def refresh_token(self): LOG.debug('Refreshing token') if identity_lock.acquire(blocking=False): try: data = self.send({ "path": "auth/token", "headers": { "Authorization": "Bearer " + self.identity.refresh } }) IdentityManager.save(data, lock=False) LOG.debug('Saved credentials') finally: identity_lock.release() else: # Someone is updating the identity wait for release with identity_lock: LOG.debug('Refresh is already in progress, waiting until done') time.sleep(1.2) os.sync() self.identity = IdentityManager.load(lock=False) LOG.debug('new credentials loaded')
def __init__(self, path): self.path = path # Load the config, skipping the REMOTE_CONFIG since we are # getting the info needed to get to it! config = Configuration.get([DEFAULT_CONFIG, SYSTEM_CONFIG, USER_CONFIG], cache=False) config_server = config.get("server") self.url = config_server.get("url") self.version = config_server.get("version") self.identity = IdentityManager.get()
def query(self, query): """ Query Wolfram|Alpha with query using the v2.0 API """ identity = IdentityManager().get() bearer_token = 'Bearer %s:%s' % (identity.device_id, identity.token) query = urllib.parse.urlencode(dict(input=query)) url = 'https://cerberus.mycroft.ai/wolframalpha/v2/query?' + query headers = {'Authorization': bearer_token} response = requests.get(url, headers=headers) if response.status_code == 401: raise CerberusAccessDenied() return wolframalpha.Result(StringIO(response.content))
def load(config=None): RemoteConfiguration.validate_config(config) identity = IdentityManager().get() config_remote = config.get("remote_configuration", {}) enabled = str2bool(config_remote.get("enabled", "False")) if enabled and identity.token: url = config_remote.get("url") auth_header = "Bearer %s:%s" % (identity.device_id, identity.token) try: response = requests.get(url, headers={"Authorization": auth_header}) user = response.json() RemoteConfiguration.__load_attributes(config, user) except Exception as e: logger.error("Failed to fetch remote configuration: %s" % repr(e)) else: logger.debug( "Device not paired, cannot retrieve remote configuration.") return config
def get_code(self, state): IdentityManager.update() return self.request({ "path": "/code?state=" + state })
def check_token(self): if self.identity.refresh and self.identity.is_expired(): self.identity = IdentityManager.load() if self.identity.is_expired(): self.refresh_token()
def _wait_until_wake_word(self, source, sec_per_buffer): """Listen continuously on source until a wake word is spoken Args: source (AudioSource): Source producing the audio chunks sec_per_buffer (float): Fractional number of seconds in each chunk """ num_silent_bytes = int(self.SILENCE_SEC * source.SAMPLE_RATE * source.SAMPLE_WIDTH) silence = '\0' * num_silent_bytes # bytearray to store audio in byte_data = silence buffers_per_check = self.SEC_BETWEEN_WW_CHECKS / sec_per_buffer buffers_since_check = 0.0 # Max bytes for byte_data before audio is removed from the front max_size = self.sec_to_bytes(self.SAVED_WW_SEC, source) test_size = self.sec_to_bytes(self.TEST_WW_SEC, source) said_wake_word = False # Rolling buffer to track the audio energy (loudness) heard on # the source recently. An average audio energy is maintained # based on these levels. energies = [] idx_energy = 0 avg_energy = 0.0 energy_avg_samples = int(5 / sec_per_buffer) # avg over last 5 secs counter = 0 while not said_wake_word and not self._stop_signaled: if self._skip_wake_word(): break chunk = self.record_sound_chunk(source) energy = self.calc_energy(chunk, source.SAMPLE_WIDTH) if energy < self.energy_threshold * self.multiplier: self._adjust_threshold(energy, sec_per_buffer) if len(energies) < energy_avg_samples: # build the average energies.append(energy) avg_energy += float(energy) / energy_avg_samples else: # maintain the running average and rolling buffer avg_energy -= float(energies[idx_energy]) / energy_avg_samples avg_energy += float(energy) / energy_avg_samples energies[idx_energy] = energy idx_energy = (idx_energy + 1) % energy_avg_samples # maintain the threshold using average if energy < avg_energy * 1.5: if energy > self.energy_threshold: # bump the threshold to just above this value self.energy_threshold = energy * 1.2 # Periodically output energy level stats. This can be used to # visualize the microphone input, e.g. a needle on a meter. if counter % 3: with open(self.mic_level_file, 'w') as f: f.write("Energy: cur=" + str(energy) + " thresh=" + str(self.energy_threshold)) f.close() counter += 1 # At first, the buffer is empty and must fill up. After that # just drop the first chunk bytes to keep it the same size. needs_to_grow = len(byte_data) < max_size if needs_to_grow: byte_data += chunk else: # Remove beginning of audio and add new chunk to end byte_data = byte_data[len(chunk):] + chunk buffers_since_check += 1.0 self.wake_word_recognizer.update(chunk) if buffers_since_check > buffers_per_check: buffers_since_check -= buffers_per_check chopped = byte_data[-test_size:] \ if test_size < len(byte_data) else byte_data audio_data = chopped + silence said_wake_word = \ self.wake_word_recognizer.found_wake_word(audio_data) # if a wake word is success full then record audio in temp # file. if self.save_wake_words and said_wake_word: audio = self._create_audio_data(byte_data, source) if not isdir(self.save_wake_words_dir): mkdir(self.save_wake_words_dir) dr = self.save_wake_words_dir ww_module = self.wake_word_recognizer.__class__.__name__ ww = self.wake_word_name.replace(' ', '-') md = str(abs(hash(ww_module))) stamp = str(int(1000 * get_time())) sid = SessionManager.get().session_id uid = IdentityManager.get().uuid fn = join(dr, '.'.join([ww, md, stamp, sid, uid]) + '.wav') with open(fn, 'wb') as f: f.write(audio.get_wav_data()) if self.upload_config['enable'] or self.config['opt_in']: t = Thread(target=self._upload_file, args=(fn, )) t.daemon = True t.start()
def handle_paired(event): IdentityManager.update(event.data)
def __init__(self, identity=None): self.identity = identity or IdentityManager().get() self.config_manager = ConfigurationManager()
def owm(self): return OWM(API_key=self.config.get('api_key', ''), identity=IdentityManager().get())
def check_for_activate(self): """Method is called every 10 seconds by Timer. Checks if user has activated the device yet on home.mycroft.ai and if not repeats the pairing code every 60 seconds. """ try: # Attempt to activate. If the user has completed pairing on the, # backend, this will succeed. Otherwise it throws and HTTPError() token = self.data.get("token") login = self.api.activate(self.state, token) # HTTPError() thrown # When we get here, the pairing code has been entered on the # backend and pairing can now be saved. # The following is kinda ugly, but it is really critical that we # get this saved successfully or we need to let the user know that # they have to perform pairing all over again at the website. try: IdentityManager.save(login) except Exception as e: self.log.debug("First save attempt failed: " + repr(e)) time.sleep(2) try: IdentityManager.save(login) except Exception as e2: # Something must be seriously wrong self.log.debug("Second save attempt failed: " + repr(e2)) self.abort_and_restart() if mycroft.audio.is_speaking(): # Assume speaking is the pairing code. Stop TTS of that. mycroft.audio.stop_speaking() self.enclosure.activate_mouth_events() # clears the display # Notify the system it is paired self.gui.show_page("pairing_done.qml", override_idle=False) self.bus.emit(Message("mycroft.paired", login)) self.pairing_performed = True with self.pair_dialog_lock: if self.mycroft_ready: # Tell user they are now paired self.speak_dialog(self.paired_dialog) mycroft.audio.wait_while_speaking() else: self.speak_dialog("wait.for.startup") mycroft.audio.wait_while_speaking() # Un-mute. Would have been muted during onboarding for a new # unit, and not dangerous to do if pairing was started # independently. self.bus.emit(Message("mycroft.mic.unmute", None)) # Send signal to update configuration self.bus.emit(Message("configuration.updated")) # Allow this skill to auto-update again self.reload_skill = True except HTTPError: # speak pairing code every 60th second with self.counter_lock: if self.count == 0: self.speak_code() self.count = (self.count + 1) % 6 if time.monotonic() > self.time_code_expires: # After 20 hours the token times out. Restart # the pairing process. with self.counter_lock: self.count = -1 self.data = None self.handle_pairing() else: # trigger another check in 10 seconds self.__create_activator() except Exception as e: self.log.debug("Unexpected error: " + repr(e)) self.abort_and_restart()