def _hack_check_for_duplicates(self): # TEMPORARY HACK: Look for multiple instance of the # OwO-speech-client and/or OwO-skills services, which could # happen when upgrading a shipping Mark 1 from release 0.8.17 or # before. When found, force the unit to reboot. import psutil LOG.info("Hack to check for duplicate service instances") count_instances = 0 needs_reboot = False for process in psutil.process_iter(): if process.cmdline() == ['python2.7', '/usr/local/bin/OwO-speech-client']: count_instances += 1 if (count_instances > 1): LOG.info("Duplicate OwO-speech-client found") needs_reboot = True count_instances = 0 for process in psutil.process_iter(): if process.cmdline() == ['python2.7', '/usr/local/bin/OwO-skills']: count_instances += 1 if (count_instances > 1): LOG.info("Duplicate OwO-skills found") needs_reboot = True if needs_reboot: LOG.info("Hack reboot...") self.reader.process("unit.reboot") self.bus.emit(Message("enclosure.eyes.spin")) self.bus.emit(Message("enclosure.mouth.reset"))
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.bus.emit(Message("owo.mic.mute")) # Setup handler to unmute mic at the end of on boarding # i.e. after pairing is complete self.bus.once('owo.paired', self._handle_pairing_complete) self.speak(owo.dialog.get('owo.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 load_vocab_from_file(path, vocab_type, bus): """Load OwO vocabulary from file The vocab is sent to the intent handler using the message bus Args: path: path to vocabulary file (*.voc) vocab_type: keyword name bus: OwO messagebus connection skill_id(str): skill id """ if path.endswith('.voc'): with open(path, 'r') as voc_file: for line in voc_file.readlines(): if line.startswith("#"): continue parts = line.strip().split("|") entity = parts[0] bus.emit( Message("register_vocab", { 'start': entity, 'end': vocab_type })) for alias in parts[1:]: bus.emit( Message("register_vocab", { 'start': alias, 'end': vocab_type, 'alias_of': entity }))
def test_ClientServer(self): """This is the test to send a message from each of the websockets to the other. """ # Send the messages self.ws2.emit(Message('ws1.message')) self.ws1.emit(Message('ws2.message')) # allow time for messages to be processed time.sleep(0.2) # Check that both of the handlers were called. self.assertTrue(self.handle1) self.assertTrue(self.handle2)
def test_deserialize(self): """This test's the deserialize method """ messages = [] # create the messages from the serialized strings above messages.append(Message.deserialize(self.serialized[0])) messages.append(Message.deserialize(self.serialized[1])) messages.append(Message.deserialize(self.serialized[2])) # check the created messages match the strings self.assertEqual(messages[0].serialize(), self.serialized[0]) self.assertEqual(messages[1].serialize(), self.serialized[1]) self.assertEqual(messages[2].serialize(), self.serialized[2])
def check_state(self): """ Check if an event should be triggered. """ with self.event_lock: # Check all events pending_messages = [] for event in self.events: current_time = time.time() e = self.events[event] # Get scheduled times that has passed passed = [(t, r, d) for (t, r, d) in e if t <= current_time] # and remaining times that we're still waiting for remaining = [(t, r, d) for t, r, d in e if t > current_time] # Trigger registered methods for sched_time, repeat, data in passed: pending_messages.append(Message(event, data)) # if this is a repeated event add a new trigger time if repeat: next_time = repeat_time(sched_time, repeat) remaining.append((next_time, repeat, data)) # update list of events self.events[event] = remaining # Remove events have are now completed self.clear_empty() # Finally, emit the queued up events that triggered for msg in pending_messages: self.bus.emit(msg)
def eyes_timed_spin(self, length): """Make the eyes 'roll' for the given time. Args: length (int): duration in milliseconds of roll, None = forever """ self.bus.emit(Message("enclosure.eyes.timedspin", {'length': length}))
def simple_cli(): global bus global bSimple bSimple = True bus = WebsocketClient() # OwO messagebus connection event_thread = Thread(target=connect) event_thread.setDaemon(True) event_thread.start() bus.on('speak', handle_speak) try: while True: # Sleep for a while so all the output that results # from the previous command finishes before we print. time.sleep(1.5) print("Input (Ctrl+C to quit):") line = sys.stdin.readline() bus.emit( Message("recognizer_loop:utterance", {'utterances': [line.strip()]})) except KeyboardInterrupt as e: # User hit Ctrl+C to quit print("") except KeyboardInterrupt as e: LOG.exception(e) event_thread.exit() sys.exit()
def mouth_text(self, text=""): """Display text (scrolling as needed) Args: text (str): text string to display """ self.display_manager.set_active(self.name) self.bus.emit(Message("enclosure.mouth.text", {'text': text}))
def handle_utterance(event): LOG.info("Utterance: " + str(event['utterances'])) context = {'client_name': 'OwO_listener'} if 'ident' in event: ident = event.pop('ident') context['ident'] = ident bus.emit(Message('recognizer_loop:utterance', event, context))
def register_entity_file(self, entity_file): """ Register an Entity file with the intent service. An Entity file lists the exact values that an entity can hold. For example: === ask.day.intent === Is it {weekend}? === weekend.entity === Saturday Sunday Args: entity_file (string): name of file that contains examples of an entity. Must end with '.entity' """ if entity_file.endswith('.entity'): entity_file = entity_file.replace('.entity', '') filename = self.find_resource(entity_file + ".entity", 'vocab') if not filename: raise ValueError('Unable to find "' + entity_file + '.entity"') name = str(self.skill_id) + ':' + entity_file self.bus.emit( Message("padatious:register_entity", { "file_name": filename, "name": name }))
def get_scheduled_event_status(self, name): """ Get scheduled event data and return the amount of time left Args: name (str): Name of event Return: int: the time left in seconds """ event_name = self._unique_name(name) data = {'name': event_name} # making event_status an object so it's refrence can be changed event_status = [None] finished_callback = [False] def callback(message): if message.data is not None: event_time = int(message.data[0][0]) current_time = int(time.time()) time_left_in_seconds = event_time - current_time event_status[0] = time_left_in_seconds finished_callback[0] = True emitter_name = 'owo.event_status.callback.{}'.format(event_name) self.bus.once(emitter_name, callback) self.bus.emit(Message('owo.scheduler.get_event', data=data)) start_wait = time.time() while finished_callback[0] is False and time.time() - start_wait < 3.0: time.sleep(0.1) if time.time() - start_wait > 3.0: raise Exception("Event Status Messagebus Timeout") return event_status[0]
def default_shutdown(self): """Parent function called internally to shut down everything. Shuts down known entities and calls skill specific shutdown method. """ try: self.shutdown() except Exception as e: LOG.error('Skill specific shutdown function encountered ' 'an error: {}'.format(repr(e))) # Store settings if exists(self._dir): self.settings.store() self.settings.stop_polling() # removing events self.cancel_all_repeating_events() for e, f in self.events: self.bus.remove(e, f) self.events = [] # Remove reference to wrappers self.bus.emit( Message("detach_skill", {"skill_id": str(self.skill_id) + ":"})) try: self.stop() except: LOG.error("Failed to stop skill: {}".format(self.name), exc_info=True)
def test_open_envelope(self): name = 'Jerome' intent = IntentBuilder(name).require('Keyword') intent.name = name m = Message("register_intent", intent.__dict__) unpacked_intent = open_intent_envelope(m) self.assertEqual(intent.__dict__, unpacked_intent.__dict__)
def track_start(self, track): """ Callback method called from the services to indicate start of playback of a track. """ self.bus.emit(Message('owo.audio.playing_track', data={'track': track}))
def run(self): """ Load skills and update periodically from disk and internet """ self.remove_git_locks() self._connected_event.wait() has_loaded = False # check if skill updates are enabled update = Configuration.get()["skills"]["auto_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(): # Update skills once an hour if update is enabled if time.time() >= self.next_download and update: self.download_skills() # Look for recently changed skill(s) needing a reload # checking skills dir and getting all skills there skill_paths = glob(join(self.msm.skills_dir, '*/')) still_loading = False for skill_path in skill_paths: still_loading = (self._load_or_reload_skill(skill_path) or still_loading) if not has_loaded and not still_loading and len(skill_paths) > 0: has_loaded = True self.bus.emit(Message('owo.skills.initialized')) self._unload_removed(skill_paths) # Pause briefly before beginning next scan time.sleep(2)
def remove_context(self, context): """ remove_context removes a keyword from from the context manager. """ if not isinstance(context, str): raise ValueError('context should be a string') self.bus.emit(Message('remove_context', {'context': context}))
def make_active(self): """ Bump skill to active_skill list in intent_service This enables converse method to be called even without skill being used in last 5 minutes. """ self.bus.emit( Message('active_skill_request', {"skill_id": self.skill_id}))
def register_regex(self, regex_str): """ Register a new regex. Args: regex_str: Regex string """ regex = munge_regex(regex_str, self.skill_id) re.compile(regex) # validate regex self.bus.emit(Message('register_vocab', {'regex': regex}))
def eyes_color(self, r=255, g=255, b=255): """Change the eye color to the given RGB color Args: r (int): 0-255, red value g (int): 0-255, green value b (int): 0-255, blue value """ self.bus.emit(Message("enclosure.eyes.color", {'r': r, 'g': g, 'b': b}))
def eyes_volume(self, volume): """Indicate the volume using the eyes Args: volume (int): 0 to 11 """ if volume < 0 or volume > 11: raise ValueError('volume ({}) must be between 0-11'. format(str(volume))) self.bus.emit(Message("enclosure.eyes.volume", {'volume': volume}))
def __init__(self, config, bus, name='mopidy'): self.connection_attempts = 0 self.bus = bus self.config = config self.name = name self.mopidy = None self.bus.on('MopidyServiceConnect', self._connect) self.bus.emit(Message('MopidyServiceConnect'))
def update_scheduled_event(self, name, data=None): """ Change data of event. Args: name (str): Name of event """ data = data or {} data = {'event': self._unique_name(name), 'data': data} self.bus.emit(Message('owo.schedule.update_event', data=data))
def handle_stop(event): """ handle stop message """ global _last_stop_signal if check_for_signal("isSpeaking", -1): _last_stop_signal = time.time() tts.playback.clear_queue() tts.playback.clear_visimes() bus.emit(Message("owo.stop.handled", {"by": "TTS"}))
def eyes_look(self, side): """Make the eyes look to the given side Args: side (str): 'r' for right 'l' for left 'u' for up 'd' for down 'c' for crossed """ self.bus.emit(Message("enclosure.eyes.look", {'side': side}))
def eyes_fill(self, percentage): """Use the eyes as a type of progress meter Args: amount (int): 0-49 fills the right eye, 50-100 also covers left """ if percentage < 0 or percentage > 100: raise ValueError('percentage ({}) must be between 0-100'. format(str(percentage))) self.bus.emit(Message("enclosure.eyes.fill", {'percentage': percentage}))
def eyes_setpixel(self, idx, r=255, g=255, b=255): """Set individual pixels of the Mark 1 neopixel eyes Args: idx (int): 0-11 for the right eye, 12-23 for the left r (int): The red value to apply g (int): The green value to apply b (int): The blue value to apply """ if idx < 0 or idx > 23: raise ValueError('idx ({}) must be between 0-23'.format(str(idx))) self.bus.emit(Message("enclosure.eyes.setpixel", {'idx': idx, 'r': r, 'g': g, 'b': b}))
def register_vocabulary(self, entity, entity_type): """ Register a word to a keyword Args: entity: word to register entity_type: Intent handler entity to tie the word to """ self.bus.emit( Message('register_vocab', { 'start': entity, 'end': to_alnum(self.skill_id) + entity_type }))
def _track_info(self, message): """ Returns track info on the message bus. Args: message: message bus message, not used but required """ if self.current: track_info = self.current.track_info() else: track_info = {} self.bus.emit( Message('owo.audio.service.track_info_reply', data=track_info))
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 hold the " "button on top for two seconds, then select " "wifi from the menu"})) else: # enter wifi-setup mode automatically self.bus.emit(Message('system.wifi.setup', {'lang': self.lang}))