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 test_at_least_one_alone(self): intent = IntentBuilder("OptionsForLunch") \ .one_of("Question", "Command") \ .build() for result in self.parser.parse("show"): result_intent = intent.validate(result.get('tags'), result.get('confidence')) assert result_intent.get('confidence') > 0.0 assert result_intent.get('Command') == "show"
def test_basic_intent_with_alternate_names(self): intent = IntentBuilder("play television intent")\ .require("PlayVerb", "Play Verb")\ .require("Television Show", "series")\ .build() for result in self.parser.parse("play the big bang theory"): result_intent = intent.validate(result.get('tags'), result.get('confidence')) assert result_intent.get('confidence') > 0.0 assert result_intent.get('Play Verb') == 'play' assert result_intent.get('series') == "the big bang theory"
def test_intent_using_alias(self): self.trie.insert("big bang", ("the big bang theory", "Television Show")) intent = IntentBuilder("play television intent")\ .require("PlayVerb", "Play Verb")\ .require("Television Show", "series")\ .build() for result in self.parser.parse("play the big bang theory"): result_intent = intent.validate(result.get('tags'), result.get('confidence')) assert result_intent.get('confidence') > 0.0 assert result_intent.get('Play Verb') == 'play' assert result_intent.get('series') == "the big bang theory"
def test_at_least_on_no_required(self): intent = IntentBuilder("play intent") \ .one_of("Television Show", "Radio Station") \ .build() for result in self.parser.parse("play the big bang theory"): result_intent = intent.validate(result.get('tags'), result.get('confidence')) assert result_intent.get('confidence') > 0.0 assert result_intent.get('Television Show') == "the big bang theory" for result in self.parser.parse("play the barenaked ladies"): result_intent = intent.validate(result.get('tags'), result.get('confidence')) assert result_intent.get('confidence') > 0.0 assert result_intent.get('Radio Station') == "barenaked ladies"
def test_intent_with_regex_entity(self): self.trie = Trie() self.tagger = EntityTagger(self.trie, self.tokenizer, self.regex_entities) self.parser = Parser(self.tokenizer, self.tagger) self.trie.insert("theory", ("theory", "Concept")) regex = re.compile(r"the (?P<Event>.*)") self.regex_entities.append(regex) intent = IntentBuilder("mock intent")\ .require("Event")\ .require("Concept").build() for result in self.parser.parse("the big bang theory"): result_intent = intent.validate(result.get('tags'), result.get('confidence')) assert result_intent.get('confidence') > 0.0 assert result_intent.get('Event') == 'big bang' assert result_intent.get('Concept') == "theory"
def _connect(self, message): url = 'http://localhost:6680' if self.base_conf: url = self.base_conf.get('mopidy_url', None) if self.config: url = self.config.get('mopidy_url', url) try: self.mopidy = Mopidy(url) except: if self.connection_attempts < 1: logger.debug('Could not connect to server, will retry quietly') self.connection_attempts += 1 time.sleep(10) self.emitter.emit(Message(self.name + '.connect')) return logger.info('Connected to mopidy server') self.albums = {} self.artists = {} self.genres = {} self.playlists = {} self.radios = {} logger.info('Loading content') self.albums['gmusic'] = self.mopidy.get_gmusic_albums() self.artists['gmusic'] = self.mopidy.get_gmusic_artists() self.genres['gmusic'] = self.mopidy.get_gmusic_radio() self.playlists['gmusic'] = {} self.albums['local'] = self.mopidy.get_local_albums() self.artists['local'] = self.mopidy.get_local_artists() self.genres['local'] = self.mopidy.get_local_genres() self.playlists['local'] = self.mopidy.get_local_playlists() self.albums['spotify'] = {} self.artists['spotify'] = {} self.genres['spotify'] = {} self.playlists['spotify'] = self.mopidy.get_spotify_playlists() self.playlist = {} for loc in ['local', 'gmusic', 'spotify']: logger.info(loc) self.playlist.update(self.playlists[loc]) logger.info(loc) self.playlist.update(self.genres[loc]) logger.info(loc) self.playlist.update(self.artists[loc]) logger.info(loc) self.playlist.update(self.albums[loc]) self.register_vocabulary(self.name, 'NameKeyword') for p in self.playlist.keys(): logger.debug("Playlist: " + p) self.register_vocabulary(p, 'PlaylistKeyword' + self.name) intent = IntentBuilder('PlayPlaylistIntent' + self.name)\ .require('PlayKeyword')\ .require('PlaylistKeyword' + self.name)\ .build() self.register_intent(intent, self.handle_play_playlist) intent = IntentBuilder('PlayFromIntent' + self.name)\ .require('PlayKeyword')\ .require('PlaylistKeyword')\ .require('NameKeyword')\ .build() self.register_intent(intent, self.handle_play_playlist) intent = IntentBuilder('SearchSpotifyIntent' + self.name)\ .require('SearchKeyword')\ .require('Source')\ .require('SpotifyKeyword')\ .build() self.register_intent(intent, self.search_spotify)
def initialize(self): i = IntentBuilder('a').require('Keyword') self.register_intent(i, self.handler)
def initialize(self): intent = IntentBuilder("PairingIntent") \ .require("PairingKeyword").require("DeviceKeyword").build() self.register_intent(intent, self.handle_pairing) self.emitter.on("mycroft.not.paired", self.not_paired)
class PersonDetectSkill(MycroftSkill): def __init__(self): super(PersonDetectSkill, self).__init__(name="PersonDetectSkill") @intent_handler( IntentBuilder("HowManyIntent").require("howmanykeyword").require( "youkeyword").build()) def handle_how_many_intent(self, message): capture = cv2.VideoCapture(1) capture.set(3, 640) capture.set(4, 480) frame_set = [] start_time = time.time() while (True): ret, frame = capture.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frame_set.append(gray) ##cv2.imshow('frame',gray) end_time = time.time() elapsed = end_time - start_time if elapsed > 2: break capture.release() with detection_graph.as_default(): with tf.Session(graph=detection_graph) as sess: # Definite input and output Tensors for detection_graph image_tensor = detection_graph.get_tensor_by_name( 'image_tensor:0') # Each box represents a part of the image where a particular object was detected. detection_boxes = detection_graph.get_tensor_by_name( 'detection_boxes:0') # Each score represent how level of confidence for each of the objects. # Score is shown on the result image, together with the class label. detection_scores = detection_graph.get_tensor_by_name( 'detection_scores:0') detection_classes = detection_graph.get_tensor_by_name( 'detection_classes:0') num_detections = detection_graph.get_tensor_by_name( 'num_detections:0') ##for image in frame_set: ##image_path = frame_set[0] ##image = Image.open(image_path) # the array based representation of the image will be used later in order to prepare the # result image with boxes and labels on it. image_np = frame_set[-1] # Expand dimensions since the model expects images to have shape: [1, None, None, 3] image_np_expanded = np.expand_dims(image_np, axis=0) # Actual detection. (boxes, scores, classes, num) = sess.run([ detection_boxes, detection_scores, detection_classes, num_detections ], feed_dict={image_tensor: image_np_expanded}) # Visualization of the results of a detection. ##width, height = image.size final_score = np.squeeze(scores) count = 0 for i in range(100): if scores is None or final_score[i] > 0.5: count = count + 1 if count > 1: self.speak("There are {} persons in front of me".format(count)) if count == 0: self.speak("I cant see anyone") if count == 1: self.speak("There is one person in front of me") def stop(self): pass
class AlgorithmSkill(MycroftSkill): def __init__(self): super(AlgorithmSkill, self).__init__(name="AlgorithmSkill") @intent_handler( IntentBuilder("MergeSortIntent").require("Algorithm").require( "MergeSort")) def handle_merge_sort_best_intent(self, message): self.speak_dialog("merge.sort") @intent_handler( IntentBuilder("CountingSortIntent").require("Algorithm").require( "CountingSort")) def handle_counting_sort_intent(self, message): self.speak_dialog("counting.sort") @intent_handler( IntentBuilder("LinearSortIntent").require("Algorithm").require( "LinearSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("linear.sort") @intent_handler( IntentBuilder("HeapSortIntent").require("Algorithm").require( "HeapSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("heap.sort") @intent_handler( IntentBuilder("QuickSortIntent").require("Algorithm").require( "QuickSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("quick.sort") @intent_handler( IntentBuilder("RadixSortIntent").require("Algorithm").require( "RadixSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("radix.sort") @intent_handler( IntentBuilder("TimSortIntent").require("Algorithm").require("TimSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("tim.sort") @intent_handler( IntentBuilder("BubbleSortIntent").require("Algorithm").require( "BubbleSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("bubble.sort") @intent_handler( IntentBuilder("SelectionSortIntent").require("Algorithm").require( "SelectionSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("selection.sort") @intent_handler( IntentBuilder("TreeSortIntent").require("Algorithm").require( "TreeSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("tree.sort") @intent_handler( IntentBuilder("BucketSortIntent").require("Algorithm").require( "BucketSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("bucket.sort") @intent_handler( IntentBuilder("ShellSortIntent").require("Algorithm").require( "ShellSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("shell.sort") @intent_handler( IntentBuilder("CubeSortIntent").require("Algorithm").require( "CubeSort")) def handle_linear_sort_intent(self, message): self.speak_dialog("cube.sort") def stop(self): pass
engine.register_entity(wk, "WeatherKeyword", domain='Domain1') weather_types = ["snow", "rain", "wind", "sleet", "sun"] for wt in weather_types: engine.register_entity(wt, "WeatherType", domain='Domain1') locations = ["Seattle", "San Francisco", "Tokyo"] for l in locations: engine.register_entity(l, "Location", domain='Domain1') # structure intent weather_intent = IntentBuilder("WeatherIntent")\ .require("WeatherKeyword")\ .optionally("WeatherType")\ .require("Location")\ .build() # define music vocabulary artists = [ "third eye blind", "the who", "the clash", "john mayer", "kings of leon", "adelle" ] for a in artists: engine.register_entity(a, "Artist", domain='Domain2') music_verbs = ["listen", "hear", "play"] for mv in music_verbs:
class RegSkill(MycroftSkill): def __init__(self): super(RegSkill, self).__init__(name="Regskill") #def initialize(self): #add_event_intent = IntentBuilder('EventIntent') \ #.require('Add') \ #.require('Event') \ #.require('Person') \ #.optionally('Location') \ #.optionally('time') \ #.build() #self.register_intent(add_event_intent, self.createevent) @property def utc_offset(self): return timedelta(seconds=self.location['timezone']['offset'] / 1000) @intent_handler( IntentBuilder("add_event_intent").require('Add').require( 'Person').optionally('Location').optionally('time').build()) def createevent(self, message): storage1 = Storage( '/opt/mycroft/skills/finalregskill.hanabouzid/info3.dat') credentials = storage1.get() if credentials is None or credentials.invalid == True: credentials = tools.run_flow(FLOW, storage1) print(credentials) # Create an httplib2.Http object to handle our HTTP requests and # authorize it with our good Credentials. http = httplib2.Http() http = credentials.authorize(http) service = build('calendar', 'v3', http=http) people_service = build(serviceName='people', version='v1', http=http) print("authorized") # To get the person information for any Google Account, use the following code: # profile = people_service.people().get('people/me', pageSize=100, personFields='names,emailAddresses').execute() # To get a list of people in the user's contacts, # results = service.people().connections().list(resourceName='people/me',personFields='names,emailAddresses',fields='connections,totalItems,nextSyncToken').execute() results = people_service.people().connections().list( resourceName='people/me', pageSize=100, personFields='names,emailAddresses,events', fields='connections,totalItems,nextSyncToken').execute() connections = results.get('connections', []) print("connections:", connections) utt = message.data.get("utterance", None) # extract the location #location = message.data.get("Location", None) print(utt) #listname1=utt.split(" named ") #listname2=listname1[1].split(" with ") #title =listname2[0] lister = utt.split(" in ") lister2 = lister[1].split(" starts ") location = lister2[0] print(location) strtdate = lister2[1] st = extract_datetime(strtdate) st = st[0] - self.utc_offset et = st + timedelta(hours=1) datestart = st.strftime('%Y-%m-%dT%H:%M:00') datend = et.strftime('%Y-%m-%dT%H:%M:00') datestart += UTC_TZ datend += UTC_TZ print(datestart) print(datend) listp = [] list1 = utt.split(" with ") #extract attendees list2 = list1[1].split(" in ") if ("and") in list2[0]: listp = list2[0].split(" and ") else: listp.append(list2[0]) print(listp) attendee = [] namerooms = [ 'midoune room', 'aiguilles room', 'barrouta room', 'kantaoui room', 'gorges room', 'ichkeul room', 'khemir room', 'tamaghza room', 'friguia room', 'ksour room', 'medeina room', 'thyna room' ] emailrooms = [ "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**" ] #freerooms freemails = [] freerooms = [] for i in range(0, len(emailrooms)): body = { "timeMin": datestart, "timeMax": datend, "timeZone": 'America/Los_Angeles', "items": [{ "id": emailrooms[i] }] } roomResult = service.freebusy().query(body=body).execute() room_dict = roomResult[u'calendars'] for cal_room in room_dict: print(cal_room, ':', room_dict[cal_room]) case = room_dict[cal_room] for j in case: if (j == 'busy' and case[j] == []): # la liste freerooms va prendre les noms des salles free freerooms.append(namerooms[i]) freemails.append(emailrooms[i]) suggroom = freerooms[0] suggmail = freemails[0] # extraire l'email des invitees et de la salle indiceroom = None for j, e in enumerate(namerooms): if e == location: indiceroom = j if (indiceroom != None): #register the room mail idmailr = emailrooms[indiceroom] #freebusy # freebusy body = { "timeMin": datestart, "timeMax": datend, "timeZone": 'America/Los_Angeles', "items": [{ "id": idmailr }] } eventsResult = service.freebusy().query(body=body).execute() cal_dict = eventsResult[u'calendars'] print(cal_dict) for cal_name in cal_dict: print(cal_name, ':', cal_dict[cal_name]) statut = cal_dict[cal_name] for i in statut: if (i == 'busy' and statut[i] == []): self.speak_dialog("roomfree", data={"room": location}) # ajouter l'email de x ala liste des attendee meetroom = location attendee.append(idmailr) elif (i == 'busy' and statut[i] != []): self.speak_dialog("roombusy", data={"room": location}) self.speak_dialog("suggestionroom", data={"suggroom": suggroom}) x = self.get_response( "Do you agree making a reservation for this meeting room" ) if x == "yes": meetroom = suggroom attendee.append(suggmail) else: s = ",".join(freerooms) # print("les salles disponibles a cette date sont", freerooms) self.speak_dialog("freerooms", data={"s": s}) room = self.get_response( 'which Room do you want to make a reservation for??' ) for i in range(0, len(freerooms)): if (freerooms[i] == room): # ajouter l'email de room dans la liste des attendees meetroom = room attendee.append(freemails[i]) else: self.speak_dialog("notRoom") meetroom = "Focus corporation" # liste de contacts nameliste = [] adsmails = [] for person in connections: emails = person.get('emailAddresses', []) adsmails.append(emails[0].get('value')) names = person.get('names', []) nameliste.append(names[0].get('displayName')) #recherche des mails des invités n = len(listp) for i in listp: indiceperson = None for j, e in enumerate(nameliste): if e == i: att = i indiceperson = j if (indiceperson != None): self.speak_dialog("exist", data={"att": att}) idmailp = adsmails[indiceperson] print(idmailp) print(att) #freebusy body = { "timeMin": datestart, "timeMax": datend, "timeZone": 'America/Los_Angeles', "items": [{ "id": idmailp }] } eventsResult = service.freebusy().query(body=body).execute() cal_dict = eventsResult[u'calendars'] print(cal_dict) for cal_name in cal_dict: print(cal_name, ':', cal_dict[cal_name]) statut = cal_dict[cal_name] for i in statut: if (i == 'busy' and statut[i] == []): self.speak_dialog("attendeefree", data={"att": att}) # ajouter l'email de x ala liste des attendee attendee.append(idmailp) elif (i == 'busy' and statut[i] != []): self.speak_dialog("attendeebusy", data={"att": att}) n -= 1 else: self.speak_dialog("notExist", data={"att": att}) # creation d'un evenement attendeess = [] for i in range(len(attendee)): email = {'email': attendee[i]} attendeess.append(email) event = { 'summary': 'meeting', 'location': meetroom, 'description': '', 'start': { 'dateTime': datestart, 'timeZone': 'America/Los_Angeles', }, 'end': { 'dateTime': datend, 'timeZone': 'America/Los_Angeles', }, 'recurrence': ['RRULE:FREQ=DAILY;COUNT=1'], 'attendees': attendeess, 'reminders': { 'useDefault': False, 'overrides': [ { 'method': 'email', 'minutes': 24 * 60 }, { 'method': 'popup', 'minutes': 10 }, ], }, } if n == 0: self.speak_dialog("cancellEvent") elif n == len(listp): event = service.events().insert(calendarId='primary', sendNotifications=True, body=event).execute() print('Event created: %s' % (event.get('htmlLink'))) self.speak_dialog("eventCreated") else: res = self.get_response( 'Some of the attendees are busy would you like to continue creating the event yes or no?' ) if res == 'yes': event = service.events().insert(calendarId='primary', sendNotifications=True, body=event).execute() print('Event created: %s' % (event.get('htmlLink'))) self.speak_dialog("eventCreated") elif res == 'no': self.speak_dialog("eventCancelled")
class WhiteNoise(MycroftSkill): def __init__(self): MycroftSkill.__init__(self) self.endtime = None self.process = None self.stopped = False self.audio_length = 0 def initialize(self): #Build play list self.play_list = { 'ocean': join(abspath(dirname(__file__)), 'sounds', 'ocean.wav'), 'wind': join(abspath(dirname(__file__)), 'sounds', 'wind.wav'), 'rain': join(abspath(dirname(__file__)), 'sounds', 'rain.wav'), } #Play random noise or a specific noise from list @intent_file_handler('noise.white.intent') def handle_single_whitenoise(self, message): print("inside handler") wait_while_speaking() self.stopped = False now = datetime.now() print(message.data.get('sound')) if message.data.get('sound') is not None: print("inside not None") title = message.data.get('sound') score = match_one(title, self.play_list) print(score) if score[1] > 0.5: self.process = play_wav(score[0]) fname = score[0] #Loop Infinitely with contextlib.closing(wave.open(fname, 'r')) as f: frames = f.getnframes() rate = f.getframerate() duration = frames / float(rate) self.audio_length = duration print(duration) self.songTimer = { "file": fname, "expires": now + timedelta(seconds=self.audio_length) } self.check_replay(None) else: self.speak('Sorry I could not find that sound in my library') return None else: print("inside None") sound_file = list(self.play_list.values()) sound_file = random.choice(sound_file) print(sound_file) #if os.path.isfile(sound_file): wait_while_speaking() self.process = play_wav(sound_file) #Loop Infinitely fname = sound_file with contextlib.closing(wave.open(fname, 'r')) as f: frames = f.getnframes() rate = f.getframerate() duration = frames / float(rate) self.audio_length = duration print(duration) self.songTimer = { "file": fname, "expires": now + timedelta(seconds=self.audio_length) } self.check_replay(None) #Handles Loop Call @intent_file_handler('whitenoiseloop.intent') def handle_loop_whitenoise(self, message): print("inside loop handler") wait_while_speaking() print(message.data.get('sound')) self.stopped = False now = datetime.now() if message.data.get('sound') is not None: print("inside not None") title = message.data.get('sound') score = match_one(title, self.play_list) print(score) if score[1] > 0.5: self.process = play_wav(score[0]) fname = score[0] with contextlib.closing(wave.open(fname, 'r')) as f: frames = f.getnframes() rate = f.getframerate() duration = frames / float(rate) self.audio_length = duration print(duration) self.songTimer = { "file": fname, "expires": now + timedelta(seconds=self.audio_length) } self.check_replay(None) else: return None self.speak('Sorry I could not find that sound in my library') else: print("inside None") sound_file = list(self.play_list.values()) sound_file = random.choice(sound_file) print(sound_file) #if os.path.isfile(sound_file): wait_while_speaking() self.process = play_wav(sound_file) fname = sound_file with contextlib.closing(wave.open(fname, 'r')) as f: frames = f.getnframes() rate = f.getframerate() duration = frames / float(rate) self.audio_length = duration print(duration) self.songTimer = { "file": fname, "expires": now + timedelta(seconds=self.audio_length) } self.check_replay(None) #Extract Time and Duration of Audio Play utt = normalize(message.data.get('utterance', "").lower()) extract = extract_duration(utt) print(extract) if extract: total_duration = extract[0] self.endtime = extract[0] utt = extract[1] utc = pytz.UTC print("Current Duration:") secs = self.endtime.total_seconds() time_expires = now + timedelta(seconds=secs) self.timer = {"duration": secs, "expires": time_expires} self.update_time(None) def update_time(self, message): print("inside update_time") # Check if there is an expired timer now = datetime.now() # Calc remaining time and show using faceplate if (self.timer["expires"] > now): if self.stopped == False: # Timer still running remaining = (self.timer["expires"] - now).seconds print(remaining) self.cancel_scheduled_event('ShowTimer') self.schedule_repeating_event(self.update_time, None, 1, name='ShowTimer') else: # Timer has expired but not been cleared, flash eyes overtime = (now - self.timer["expires"]).seconds print(overtime) if (self.stopped == False): self.speak("Playtime is over!") self.cancel_scheduled_event('ShowTimer') self.stop() def check_replay(self, message): print("inside check_replay") # Check if there is an expired timer now = datetime.now() if self.stopped == False: # Calc remaining time and show using faceplate if (self.songTimer["expires"] > now): if self.stopped == False: # Timer still running remaining = (self.songTimer["expires"] - now).seconds print(remaining) self.cancel_scheduled_event('Replay') self.schedule_repeating_event(self.check_replay, None, 1, name='Replay') else: # Timer has expired but not been cleared, flash eyes overtime = (now - self.songTimer["expires"]).seconds print(overtime) self.cancel_scheduled_event('Replay') sound_file = self.songTimer["file"] self.process = play_wav(sound_file) self.songTimer = { "file": sound_file, "expires": now + timedelta(seconds=self.audio_length) } self.schedule_repeating_event(self.check_replay, None, 1, name='Replay') else: self.cancel_scheduled_event('Replay') self.stop() def stop_playing(self): if self.process is not None: self.process.terminate() return True return False @intent_handler(IntentBuilder("").require("Stop")) def stop(self): # abort current laugh self.stopped = self.stop_playing() return self.stopped
def initialize(self): self.load_data_files(dirname(__file__)) intent = IntentBuilder("TimeIntent").require("TimeKeyword") \ .optionally("Location").build() self.register_intent(intent, self.handle_intent)
class LaughSkill(MycroftSkill): def __init__(self): MycroftSkill.__init__(self) self.random_laugh = False self.sounds = {"male": [], "female": []} if "gender" not in self.settings: self.settings["gender"] = "male" if "sounds_dir" not in self.settings: self.settings["sounds_dir"] = join(dirname(__file__), "sounds") self.p = None self.settings.set_changed_callback(self._fix_gender) def _fix_gender(self): if "f" in self.settings["gender"].lower(): self.settings["gender"] = "female" else: self.settings["gender"] = "male" def initialize(self): sounds_dir = join(self.settings["sounds_dir"], "male") self.sounds["male"] = [ join(sounds_dir, sound) for sound in listdir(sounds_dir) if ".wav" in sound or ".mp3" in sound ] sounds_dir = join(self.settings["sounds_dir"], "female") self.sounds["female"] = [ join(sounds_dir, sound) for sound in listdir(sounds_dir) if ".wav" in sound or ".mp3" in sound ] # stop laughs for speech execution self.add_event("speak", self.stop_laugh) def laugh(self): # dont laugh over a speech message if is_speaking(): wait_while_speaking() sound = random.choice(self.sounds[self.settings["gender"]]) if ".mp3" in sound: self.p = play_mp3(sound) else: self.p = play_wav(sound) @intent_file_handler("Laugh.intent") def handle_laugh_intent(self, message): self.laugh() @intent_file_handler("RandomLaugh.intent") def handle_random_intent(self, message): # initiate random laughing self.log.info("Laughing skill: Triggering random laughing") self.random_laugh = True self.handle_laugh_event(message) @intent_handler( IntentBuilder('StopLaughing').require('Stop').require('Laugh')) def halt_laughing(self, message): self.log.info("Laughing skill: Stopping") # if in random laugh mode, cancel the scheduled event if self.random_laugh: self.log.info("Laughing skill: Stopping random laugh event") self.random_laugh = False self.cancel_scheduled_event('random_laugh') self.speak_dialog("cancel") else: self.speak_dialog("cancel_fail") def handle_laugh_event(self, message): # create a scheduled event to laugh at a random interval between 1 # minute and half an hour if not self.random_laugh: return self.log.info("Laughing skill: Handling laugh event") self.laugh() self.cancel_scheduled_event('random_laugh') self.schedule_event(self.handle_laugh_event, datetime.now() + timedelta(seconds=random.randrange(60, 1800)), name='random_laugh') def stop_laugh(self): if self.p is not None: self.p.terminate() return True return False def stop(self): # abort current laugh stopped = self.stop_laugh() # stop random laughs if self.random_laugh: self.halt_laughing(None) stopped = True return stopped
class WhatCanYouDoSkill(MycroftSkill): def __init__(self): super(WhatCanYouDoSkill, self).__init__(name="WhatCanYouDoSkill") @intent_handler( IntentBuilder("").require("What").require("Can").require("Do")) def handle_what_can_do__intent(self, message): self.speak_dialog("what.i.can") # tell user what he can do self.getSkills( ) # execute function getSkills -> get list of installed skills def getSkills(self): self.myskills = os.popen('msm list | grep installed').read( ) # get list of skills via msm and search for "installed" self.myskills = self.myskills.replace('\n', ', ').replace( '\r', ', ').replace('[installed],', ',').replace( '\t', '') # replace unwanted characters and make nice list nr_skills = len(self.myskills.split()) # get number of skills if nr_skills < 1: # if msm did not give us what we want (no matter why) do alternative skill search self.myskills = os.popen('ls /opt/mycroft/skills/').read( ) # Get folders in /opt/mycroft/skills self.myskills = self.myskills.replace('\n', ', ').replace( '\r', ', ').replace( '\t', '') # replace unwanted characters and make nice list nr_skills = len(self.myskills.split()) # get number of skills if nr_skills < 1: # if msm and alternative skill search fails than tell user that we couldn't do the job wait_while_speaking() # always wait self.speak_dialog( "not.found") # tell user that we couldn't do the job return # if all fails, return wait_while_speaking() # always wait self.speak_dialog('found', {'nrskills': nr_skills }) # we found skills -> yeah. tell user how many! wait_while_speaking() # always wait self.should_getskills = self.get_response( 'ask.getskills' ) # ask user if we should give him a list of all his skills. self.yes_words = set( self.translate_list('yes')) # get list of confirmation words self.listSkills( ) # execute function listSkills -> if user confirmed -> give him a list of all his skills, else -> exit def listSkills(self): if self.should_getskills: # if user said something resp_getskills = self.should_getskills.split( ) # split user sentence into list if any(word in resp_getskills for word in self.yes_words ): # if any of the words from the user sentences is yes self.speak_dialog( 'my.skills' ) # Introduction that we will give user list of skills self.speak(self.myskills.strip()) # tell user list of skills else: # no word in sentence from user was yes self.speak_dialog('no.skills') # give user feedback def shutdown(self): super(WhatCanYouDoSkill, self).shutdown() def stop(self): pass
class PVOutputSkill(MycroftSkill): def __init__(self): super().__init__(name="PVOutput") # import httplib2 # httplib2.debuglevel = 1 @property def use_24hour(self): return self.config_core.get('time_format') == 'full' @property def timezone(self): timezone = self.location_timezone if not timezone: return None return pytz.timezone(timezone) def time_to_str(self, time): if self.use_24hour: return time.strftime("%H:%M") return time.strftime("%I:%M %p") def get_pvoutput(self) -> Optional[PVOutput]: api_key = self.settings.get("api_key") system_id = self.settings.get("system_id") if api_key and system_id: LOG.info("Set up pv output for system id: {}".format(system_id)) return PVOutput(api_key=api_key, system_id=system_id) self.speak_dialog("pvoutput.not.setup") LOG.info("No pvoutput setup id: {}".format(system_id)) return None def format_date(self, date: datetime.date): return nice_date(datetime.datetime(date.year, date.month, date.day), now=datetime.datetime.now(tz=self.timezone)) def nice_format_period(self, date1, date2): if date1 == date2: return self.format_date(date1) return self.format_period(date1, date2) def format_period(self, date1, date2): return self.translate("between.dates", data={ "date1": self.format_date(date1), "date2": self.format_date(date2) }) def get_date(self, message): utterance = message.data.get("utterance", "") now = datetime.datetime.now(tz=self.timezone) today = now.date() result = extract_datetime(utterance, anchorDate=now) if result is None: return today date = result[0].date() if (date - today).days > 3: # date is in the future result = extract_datetime( utterance, anchorDate=(now - datetime.timedelta(days=365))) date = result[0].date() return date def get_this_week_start_date(self): today = datetime.datetime.now(tz=self.timezone).date() weekday = ( today.weekday() + 1 ) % 7 # TODO only add 1 if that's normal for someone's language/region return today - datetime.timedelta(days=weekday) def get_period(self, message: Message): utterance = message.data.get("utterance", "") today = datetime.datetime.now(tz=self.timezone).date() start = None end = None if self.voc_match(utterance, "LastMonth"): if today.month == 1: start = datetime.date(today.year - 1, 12, 1) else: start = datetime.date(today.year, today.month - 1, 1) end = datetime.date( start.year, start.month, calendar.monthrange(start.year, start.month)[1]) elif self.voc_match(utterance, "ThisMonth"): start = datetime.date(today.year, today.month, 1) end = today elif self.voc_match(utterance, "LastYear"): start = datetime.date(today.year - 1, 1, 1) end = datetime.date(today.year - 1, 12, 31) elif self.voc_match(utterance, "ThisYear"): start = datetime.date(today.year, 1, 1) end = today elif self.voc_match(utterance, "LastWeek"): start = self.get_this_week_start_date() - datetime.timedelta( days=7) end = start + datetime.timedelta(days=6) elif self.voc_match(utterance, "ThisWeek"): start = self.get_this_week_start_date() end = start + datetime.timedelta(days=6) if not start: return None return start, end def handle_errors(self, function, date_string): date_string = date_string or self.format_date( datetime.datetime.now(tz=self.timezone).date()) try: function() except (NoStatusPVOutputException, NoOutputsPVOutputException) as e: LOG.info(e) self.speak_dialog("no.status.for.date", {"date": date_string}) except InvalidApiKeyPVOutputException as e: LOG.info(e) self.speak_dialog("invalid.api.key") def process_message_for_statistic(self, message, process_statistic, consumption_and_import=False): pvo = self.get_pvoutput() if not pvo: return period = self.get_period(message) if not period: date = self.get_date(message) period = (date, date) def period_function(): statistic = pvo.get_statistic( date_from=period[0], date_to=period[1], consumption_and_import=consumption_and_import) date_string = self.nice_format_period(statistic.actual_date_from, statistic.actual_date_to) process_statistic(statistic, date_string) self.handle_errors(period_function, self.nice_format_period(period[0], period[1])) @intent_handler( IntentBuilder("Energy Generated").require("Energy").require( "Generated").optionally("Solar").optionally("PVOutput")) def energy_generated(self, message): def process_statistic(statistic, date_string): generated_watt_hours = statistic.energy_generated self.speak_dialog("energy.generated", data={ "amount": generated_watt_hours / 1000.0, "date": date_string }) self.process_message_for_statistic(message, process_statistic) @intent_handler( IntentBuilder("Energy Used").require("Energy").require( "Used").optionally("Solar").optionally("PVOutput")) def energy_used(self, message): def process_statistic(statistic, date_string): consumed_watt_hours = statistic.energy_consumed self.speak_dialog("energy.used", data={ "amount": consumed_watt_hours / 1000.0, "date": date_string }) self.process_message_for_statistic(message, process_statistic, consumption_and_import=True) @intent_handler( IntentBuilder("Power Generating Now").require("Power").require( "Generating").optionally("Now").optionally("Solar").optionally( "PVOutput")) def power_generating_now(self, message): pvo = self.get_pvoutput() if not pvo: return def function(): generating_watts = pvo.get_status().power_generation self.speak_dialog("power.generating.now", data={"amount": generating_watts / 1000.0}) self.handle_errors(function, None) @intent_handler( IntentBuilder("Power Using Now").require("Power").require("Using"). optionally("Now").optionally("Solar").optionally("PVOutput")) def power_using_now(self, message): pvo = self.get_pvoutput() if not pvo: return def function(): using_watts = pvo.get_status().power_consumption self.speak_dialog("power.using.now", data={"amount": using_watts / 1000.0}) self.handle_errors(function, None) @intent_handler( IntentBuilder("Peak Power").require("PeakPower").optionally( "Solar").optionally("PVOutput")) def peak_power(self, message): pvo = self.get_pvoutput() if not pvo: return date = self.get_date(message) def function(): status: DayStatistics = pvo.get_status(date=date, day_statistics=True) peak_power = status.standard.peak_power time: datetime.time = status.standard.peak_power_time self.speak_dialog("peak.power", data={ "amount": peak_power / 1000.0, "time": self.time_to_str(time), "date": self.format_date(date) }) self.handle_errors(function, self.format_date(date))
class PairingSkill(MycroftSkill): poll_frequency = 10 # secs between checking server for activation def __init__(self): super(PairingSkill, self).__init__("PairingSkill") self.api = DeviceApi() self.data = None self.time_code_expires = None self.state = str(uuid4()) self.activator = None self.activator_lock = Lock() self.activator_cancelled = False self.counter_lock = Lock() self.count = -1 # for repeating pairing code. -1 = not running self.nato_dict = None self.mycroft_ready = False self.pair_dialog_lock = Lock() self.paired_dialog = 'pairing.paired' self.pairing_performed = False self.num_failed_codes = 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' 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 def not_paired(self, message): if not message.data.get('quiet', True): self.speak_dialog("pairing.not.paired") self.handle_pairing() @intent_handler(IntentBuilder("PairingIntent") .require("PairingKeyword").require("DeviceKeyword")) def handle_pairing(self, message=None): if check_remote_pairing(ignore_errors=True): # Already paired! Just tell user self.speak_dialog("already.paired") elif not self.data: # Kick off pairing... with self.counter_lock: if self.count > -1: # We snuck in to this handler somehow while the pairing # process is still being setup. Ignore it. self.log.debug("Ignoring call to handle_pairing") return # Not paired or already pairing, so start the process. self.count = 0 self.reload_skill = False # Prevent restart during the process self.log.debug("Kicking off pairing sequence") try: # Obtain a pairing code from the backend self.data = self.api.get_code(self.state) # Keep track of when the code was obtained. The codes expire # after 20 hours. self.time_code_expires = time.monotonic() + 72000 # 20 hours except Exception: time.sleep(10) # Call restart pairing here # Bail out after Five minutes (5 * 6 attempts at 10 seconds # interval) if self.num_failed_codes < 5 * 6: self.num_failed_codes += 1 self.abort_and_restart(quiet=True) else: self.end_pairing('connection.error') self.num_failed_codes = 0 return self.num_failed_codes = 0 # Reset counter on success mycroft.audio.wait_while_speaking() self.gui.show_page("pairing_start.qml", override_idle=True) self.speak_dialog("pairing.intro") self.enclosure.deactivate_mouth_events() self.enclosure.mouth_text("home.mycroft.ai ") # HACK this gives the Mark 1 time to scroll the address and # the user time to browse to the website. # TODO: mouth_text() really should take an optional parameter # to not scroll a second time. time.sleep(7) mycroft.audio.wait_while_speaking() if not self.activator: self.__create_activator() 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() def end_pairing(self, error_dialog): """Resets the pairing and don't restart it. Arguments: error_dialog: Reason for the ending of the pairing process. """ self.speak_dialog(error_dialog) self.bus.emit(Message("mycroft.mic.unmute", None)) self.data = None self.count = -1 def abort_and_restart(self, quiet=False): # restart pairing sequence self.log.debug("Aborting Pairing") self.enclosure.activate_mouth_events() if not quiet: self.speak_dialog("unexpected.error.restarting") # Reset state variables for a new pairing session with self.counter_lock: self.count = -1 self.activator = None self.data = None # Clear pairing code info self.log.info("Restarting pairing process") self.bus.emit(Message("mycroft.not.paired", data={'quiet': quiet})) def __create_activator(self): # Create a timer that will poll the backend in 10 seconds to see # if the user has completed the device registration process with self.activator_lock: if not self.activator_cancelled: self.activator = Timer(PairingSkill.poll_frequency, self.check_for_activate) self.activator.daemon = True self.activator.start() def speak_code(self): """Speak pairing code.""" code = self.data.get("code") self.log.info("Pairing code: " + code) data = {"code": '. '.join(map(self.nato_dict.get, code)) + '.'} # Make sure code stays on display self.enclosure.deactivate_mouth_events() self.enclosure.mouth_text(self.data.get("code")) self.gui['code'] = self.data.get("code") self.gui.show_page("pairing.qml", override_idle=True) self.speak_dialog("pairing.code", data) def shutdown(self): with self.activator_lock: self.activator_cancelled = True if self.activator: self.activator.cancel() if self.activator: self.activator.join()
class DesktopControlSkill(MycroftSkill): def __init__(self): super(DesktopControlSkill, self).__init__(name="DesktopControlSkill") def initialize(self): self.sm_amount = 2 self.med_amount = 6 self.lg_amount = 12 self.register_entity_file("smallscroll.entity") self.register_entity_file("medscroll.entity") self.register_entity_file("largescroll.entity") self.register_entity_file("down.entity") self.register_entity_file("up.entity") self.register_entity_file("x.entity") self.register_entity_file("y.entity") self.register_entity_file("key.entity") select_combination_intent = IntentBuilder("SelectCombinationIntent"). \ require("SelectAllKeyword").optionally("CopyKeyword"). \ optionally("CutKeyword"). \ optionally("PasteKeyword").\ optionally("DeleteKeyword").build() self.register_intent(select_combination_intent, self.handle_select_combination_intent) @intent_file_handler("scroll.intent") def handle_scroll(self, message): if message.data.get("smallscroll"): if message.data.get("down"): scroll_down = self.sm_amount * -1 pyautogui.scroll(scroll_down) if message.data.get("up"): scroll_up = self.sm_amount pyautogui.scroll(scroll_up) elif message.data.get("medscroll"): if message.data.get("down"): scroll_down = self.med_amount * -1 pyautogui.scroll(scroll_down) if message.data.get("up"): scroll_up = self.med_amount pyautogui.scroll(scroll_up) elif message.data.get("largescroll"): if message.data.get("down"): scroll_down = self.lg_amount * -1 pyautogui.scroll(scroll_down) if message.data.get("up"): scroll_up = self.lg_amount pyautogui.scroll(scroll_up) @intent_handler( IntentBuilder("TypeIntent").require("TypeKeyword").require("Text")) def handle_type_intent(self, message): self.speak_dialog("typing") text = message.data.get('Text') pyautogui.typewrite(text, interval=0.05) @intent_file_handler("absolutemousemove.intent") def handle_absolute_mouse_move_intent(self, message): x = message.data.get("x") y = message.data.get("y") pyautogui.moveTo(int(x), int(y)) self.speak_dialog("absolutemousemove", {"x": x, "y": y}) @intent_handler( IntentBuilder("ScreenResIntent").require("ScreenResKeyword")) def handle_screen_res_intent(self, message): screen = pyautogui.size() resx = screen[0] resy = screen[1] responsex = num2words(resx) responsey = num2words(resy) self.speak_dialog("screenresolution", {"x": responsex, "y": responsey}) @intent_file_handler("presskey.intent") def handle_press_key_intent(self, message): key = message.data.get("key") self.speak_dialog("keypress", {"key": key}) key = str(key) pyautogui.press(key) @intent_file_handler("holdkey.intent") def handle_hold_key_intent(self, message): key = message.data.get("key") self.speak_dialog("keyhold", {"key": key}) pyautogui.keyDown(key) @intent_file_handler("releasekey.intent") def handle_release_key_intent(self, message): key = message.data.get('key') self.speak_dialog("keyrelease", {"key": key}) pyautogui.keyUp(key) @intent_handler(IntentBuilder("CopyIntent").require("CopyKeyword")) def handle_copy_intent(self, message): pyautogui.hotkey("ctrl", "c") self.speak("Okay Copied!") @intent_handler(IntentBuilder("CutIntent").require("CutKeyword")) def handle_cut_intent(self, message): self.speak("Cutting to clipboard") pyautogui.hotkey("ctrl", "x") @intent_handler(IntentBuilder("PasteIntent").require("PasteKeyword")) def handle_paste_intent(self, message): self.speak("Pasting from clipboard") pyautogui.hotkey("ctrl", "v") def handle_select_combination_intent(self, message): self.speak("Selecting all") pyautogui.hotkey("ctrl", "a") time.sleep(1) if message.data.get("PasteKeyword"): self.handle_paste_intent(message) elif message.data.get("CopyKeyword"): self.handle_copy_intent(message) elif message.data.get("CutKeyword"): self.handle_cut_intent(message) elif message.data.get("DeleteKeyword"): self.speak("deleting") pyautogui.keyDown("delete") pyautogui.keyUp("delete") else: pass def stop(self): pass
class LectureSkill(MycroftSkill): def __init__(self): super(LectureSkill, self).__init__(name="LectureSkill") @intent_handler(IntentBuilder("LectureIntent").require("LectureKeyword").require("SubjectKeyword")) def handle_lecture_intent(self, message): f = open("/opt/mycroft/skills/lecture-subjects-skill/dates.txt", "r") #open textfile dateFound = False lectureNumber = 0 line = f.readline() g = open("/opt/mycroft/skills/lecture-subjects-skill/settings.txt", "r") #open textfile line2 = g.readline() if line2[0] == "0": self.speak_dialog("default") dateFound = True g.close() while(line and not dateFound): #while right date hasn't been found and there is lines in the textfile, the program will read new lines from textfile lectureNumber += 1 #for each line the lectureNumber will go up y = int(line[0:4]) #yyyy m = int(line[4:6]) #mm d = int(line[6:8]) #dd date = datetime.date(y, m, d) if datetime.date.today() == date: #check if date is today, speak accordingly self.speak_dialog("lecture" + str(lectureNumber)) dateFound = True line = f.readline() if not dateFound: #"no lecture today" if no date is found self.speak_dialog("nolecture") f.close() #close textfile @intent_handler(IntentBuilder("SettingsIntent").require("LectureKeyword").require("SettingsKeyword").require("ChangeKeyword")) def handle_settings_intent(self, message): f = open("/opt/mycroft/skills/lecture-subjects-skill/settings.txt", "r") #open textfile line = f.readline() f.close() f = open("/opt/mycroft/skills/lecture-subjects-skill/settings.txt", "w") #open textfile if line[0] == "1": f.write("0") self.speak_dialog("defset") else: f.write("1") self.speak_dialog("specset") f.close() #close textfile def stop(self): pass
class WinkIoTSkill(MycroftSkill): debug_level = 3 # 0-10, 10 showing the most debugging info def __init__(self): super(WinkIoTSkill, self).__init__(name="WinkIoTSkill") self.settings["access_token"] = None self.settings["refresh_token"] = None self.settings["token_expiration"] = to_timestamp( datetime.datetime.utcnow()) self.settings['email'] = "" self.settings['password'] = "" self._device_cache = None self._group_cache = None self._active_room = None def debug(self, message, level=1, char=None): # Debugging assistance. # Lower number are More important. Only number less than the # debug_level get logged. Indentation is the inverse of this, # with level 0 having 10 dashes as a prefix, 1 have 9, etc. if level > WinkIoTSkill.debug_level: return if not char: char = "-" self.log.debug(char * (10 - level) + " " + message) # TODO: Move in to MycroftSkill def translate_namedvalues(self, name, delim=None): """ Load translation dict containing names and values. This loads a simple CSV from the 'dialog' folders. The name is the first list item, the value is the second. Lines prefixed with # or // get ignored Args: name (str): name of the .value file, no extension needed delim (char): delimiter character used, default is ',' Returns: dict: name and value dictionary, or [] if load fails """ import csv from os.path import join delim = delim or ',' result = {} if not name.endswith(".value"): name += ".value" try: with open(join(self.root_dir, 'dialog', self.lang, name)) as f: reader = csv.reader(f, delimiter=delim) for row in reader: # skip comment lines if not row or row[0].startswith("#"): continue if len(row) != 2: continue result[row[0]] = row[1] return result except: return {} def get_remainder(self, message): # Remove words "consumed" by the intent match, e.g. if they # say 'Turn on the family room light' and there are entity # matches for "turn on" and "light", then it will leave # behind "the family room", which we normalize to "family room" utt = message.data["utterance"] for token in message.data["__tags__"]: utt = utt.replace(token["key"], "") return normalize(utt) @property def room_name(self): # Assume the "name" of the device is the "room name" device = DeviceApi().get() return device["name"] def _winkapi_auth(self): now = datetime.datetime.utcnow() # Check if access token exists and hasn't expired if (self.settings["access_token"] and to_timestamp(now) < self.settings["token_expiration"]): # Already logged in return True if not self.settings['email'] or not self.settings['password']: self.speak_dialog("need.to.configure") raise Exception("need.to.configure") # Attempt to authorize with the users ID and password body = { "client_id": client_id, "client_secret": client_secret, "username": self.settings['email'], "password": self.settings['password'], "grant_type": "password" } # Post res = requests.post("https://api.wink.com/oauth2/token", body) if res.status_code == requests.codes.ok: # Save the token for ongoing use data = json.loads(res.text) self.settings["access_token"] = data["access_token"] self.settings["refresh_token"] = data["refresh_token"] exp_secs = data["expires_in"] # seconds until expires expiration = now + datetime.timedelta(0, exp_secs) self.settings["token_expiration"] = to_timestamp(expiration) return True else: # Notify the user then exit completely (nothing else can # be done if not registered) self.speak_dialog("unable.to.login") raise Exception('unable.to.login') def _winkapi_get(self, path): if not self._winkapi_auth(): self.log.error("Failed to login") return False headers = {'Authorization': 'Bearer ' + self.settings["access_token"]} res = requests.get("https://winkapi.quirky.com" + path, headers=headers) if res.status_code == requests.codes.ok: # success self.debug("Read devices!", 9, char="*") return res.json() else: self.debug("Failed to read devices!", char="!") self.debug(res.status_code, 5) self.debug(res.text, 5) return None def _winkapi_put(self, path, body): if not self._winkapi_auth(): self.log.error("Failed to login") return False headers = { 'Authorization': 'Bearer ' + self.settings["access_token"], 'Content-Type': 'application/json' } res = requests.put("https://winkapi.quirky.com" + path, headers=headers, data=json.dumps(body)) if res.status_code == requests.codes.ok: # success self.debug("Successful PUT to device!", 2) return res.json() else: self.debug("Failed to PUT devices", char="!") self.debug("URL: " + "https://winkapi.quirky.com" + path, 5) self.debug(res.status_code, 5) self.debug(res.text, 5) return None @property def wink_devices(self): # Retrieve a list of devices associated with the account if not self._device_cache: self._device_cache = self._winkapi_get("/users/me/wink_devices") return self._device_cache @property def wink_groups(self): # Retrieve a list of groups of devices associated with the account if not self._group_cache: self._group_cache = self._winkapi_get("/users/me/groups") return self._group_cache def get_lights(self, search_name): if not search_name: return None name = normalize(search_name) self.debug("Searching for: " + name, 2, char="=") # First fuzzy search the groups best = None best_score = 0 if self.wink_groups: for group in self.wink_groups["data"]: groupname = normalize(group["name"]) score = fuzzy_match(groupname, name) self.debug(groupname + " : " + str(score), 5) if score > 0.6 and score > best_score: best_score = score best = group if not self.wink_devices: # can't even return group matches without device info return None best_group_score = best_score group_lights = [] group_IDs = [] if best: # Collect the light IDs from the group that was found for member in best["members"]: if member["object_type"] == "light_bulb": group_IDs.append(member["object_id"]) best = None for dev in self.wink_devices["data"]: if "light_bulb_id" in dev: # check if light_bulb # Gather group lights (just in case the group wins) if dev["light_bulb_id"] in group_IDs: group_lights.append(dev) # score the bulb name match lightname = normalize(dev["name"]) score = fuzzy_match(lightname, name) self.debug(lightname + " : " + str(score), 5) if score > best_score: best_score = score best = dev if group_lights and best_group_score >= best_score: self.debug("Group wins", 3, char="*") return group_lights elif best and best_score > 0.6: return [best] return None def set_light(self, lights, state, brightness=1.0): self.debug("Setting lights: " + str(state) + "@" + str(brightness), 1, "=") if not lights: return False for light in lights: self.debug( "Light: " + light["name"] + ":" + light["light_bulb_id"], 5) body = { "desired_state": { "powered": state, "brightness": brightness } } self._winkapi_put("/light_bulbs/" + light["light_bulb_id"], body) return True def find_lights(self, remainder, room): if room: remainder = None # First, see if the user specified a room ("turn on the light in # the kitchen") or just mentioned it in the request ("turn on the # kitchen light") lights = self.get_lights(room) if lights: self._active_room = room else: lights = self.get_lights(remainder) if lights: self._active_room = remainder # If no location specified, default to using the device name as # a room name... if not lights and not room: lights = self.get_lights(self.room_name) self._active_room = self.room_name return lights def scale_lights(self, message, scale_by): try: remainder = self.get_remainder(message) room = message.data.get("Room") self._device_cache = None # force update of states lights = self.find_lights(remainder, room) if lights: brightness = lights[0]["last_reading"]["brightness"] * scale_by self.set_light(lights, True, brightness) else: self.speak_dialog("couldnt.find.light") except: pass @intent_handler( IntentBuilder("").require("Light").require("Brighten").optionally( "Room")) def handle_dim_light(self, message): self.scale_lights(message, 1.5) @intent_handler( IntentBuilder("").require("Light").require("Dim").optionally("Room")) def handle_dim_light(self, message): self.scale_lights(message, 0.5) @intent_handler( IntentBuilder("").require("Light").optionally("Switch").require( "OnOff").optionally("Room")) def handle_change_light(self, message): try: on_off = message.data.get("OnOff") switch = message.data.get("Switch", "") room = message.data.get("Room") remainder = self.get_remainder(message) lights = self.find_lights(remainder, room) if lights: # Allow user to say "half", "full", "dim", etc. brightness = 1.0 intensities = self.translate_namedvalues("Intensities") for i in intensities: if contains_word(message.data.get("utterance"), i): self.debug("Match intensity: " + i) brightness = intensities[i] self.set_light(lights, on_off != "off", brightness) else: self.speak_dialog("couldnt.find.light") except: pass # Disabling for now. "What is your name" is triggering this intent # Might need Padatious to handle this? # # @intent_handler(IntentBuilder("").optionally("Light"). # require("Query").optionally("Room")) def handle_query_light(self, message): try: remainder = self.get_remainder(message) self._device_cache = None # force update of states lights = self.find_lights(remainder, message.data.get("Room")) if lights: # Just give the value of the first light if (not lights[0]["last_reading"]["powered"] or lights[0]["last_reading"]["brightness"] < 0.001): state = self.translate("off") elif lights[0]["last_reading"]["brightness"] < 0.75: state = self.translate("dimmed") else: state = self.translate("on") self.speak_dialog("light.level.is", data={"state": state}) else: self.speak_dialog("couldnt.find.light") except: pass def converse(self, utterances, lang='en-us'): if self._active_room: self._device_cache = None # force update of states lights = self.get_lights(self._active_room) if contains_word(utterances[0], self.translate_list("brighter")): self.debug("Conversational brighting") brightness = lights[0]["last_reading"]["brightness"] * 1.5 return self.set_light(lights, True, brightness) elif contains_word(utterances[0], self.translate_list("dimmer")): self.debug("Conversational dimming") brightness = lights[0]["last_reading"]["brightness"] * 0.5 return self.set_light(lights, True, brightness) return False
class WolframAlphaSkill(FallbackSkill): PIDS = ['Value', 'NotableFacts:PeopleData', 'BasicInformation:PeopleData', 'Definition', 'DecimalApproximation'] def __init__(self): FallbackSkill.__init__(self, name="WolframAlphaSkill") self.__init_client() self.question_parser = EnglishQuestionParser() self.last_query = None self.last_answer = None def __init_client(self): # TODO: Storing skill-specific settings in mycroft.conf is deprecated. # Should be stored in the skill's local settings.json instead. appID = self.config.get('api_key') if not appID: # Attempt to get an AppID skill settings instead (normally this # doesn't exist, but privacy-conscious might want to do this) appID = self.settings.get('appID', None) if appID and not self.config.get('proxy'): # user has a private AppID self.client = wolframalpha.Client(appID) else: # use the default API for Wolfram queries self.client = WAApi() def initialize(self): self.register_fallback(self.handle_fallback, 10) def get_result(self, res): try: return next(res.results).text except: result = None try: for pid in self.PIDS: result = self.__find_pod_id(res.pods, pid) if result: result = result[:5] break if not result: result = self.__find_num(res.pods, '200') return result except: return result def handle_fallback(self, message): utt = message.data.get('utterance') self.log.debug("WolframAlpha fallback attempt: " + utt) lang = message.data.get('lang') if not lang: lang = "en-us" # TODO: Localization. Wolfram only allows queries in English, # so perhaps autotranslation or other languages? That # would also involve auto-translation of the result, # which is a lot of room for introducting translation # issues. utterance = normalize(utt, lang, remove_articles=False) parsed_question = self.question_parser.parse(utterance) query = utterance if parsed_question: # Try to store pieces of utterance (None if not parsed_question) utt_word = parsed_question.get('QuestionWord') utt_verb = parsed_question.get('QuestionVerb') utt_query = parsed_question.get('Query') query = "%s %s %s" % (utt_word, utt_verb, utt_query) phrase = "know %s %s %s" % (utt_word, utt_query, utt_verb) self.log.debug("Querying WolframAlpha: " + query) else: # This utterance doesn't look like a question, don't waste # time with WolframAlpha. self.log.debug("Non-question, ignoring: " + utterance) return False try: self.enclosure.mouth_think() res = self.client.query(query) result = self.get_result(res) except HTTPError as e: if e.response.status_code == 401: self.emitter.emit(Message("mycroft.not.paired")) return True except Exception as e: self.log.exception(e) return False if result: response = self.process_wolfram_string(result) # remember for any later 'source' request self.last_query = query self.last_answer = response self.speak(response) return True else: return False @staticmethod def __find_pod_id(pods, pod_id): # Wolfram returns results in "pods". This searches a result # structure for a specific pod ID. # See https://products.wolframalpha.com/api/documentation/ for pod in pods: if pod_id in pod.id: return pod.text return None @staticmethod def __find_num(pods, pod_num): for pod in pods: if pod.node.attrib['position'] == pod_num: return pod.text return None def process_wolfram_string(self, text): # Remove extra whitespace text = re.sub(r" \s+", r" ", text) # Convert | symbols to commas text = re.sub(r" \| ", r", ", text) # Convert newlines to commas text = re.sub(r"\n", r", ", text) # Convert !s to factorial text = re.sub(r"!", r",factorial", text) with open(join(dirname(__file__), 'regex', self.lang, 'list.rx'), 'r') as regex: list_regex = re.compile(regex.readline()) match = list_regex.match(text) if match: text = match.group('Definition') return text @intent_handler(IntentBuilder("Info").require("Give").require("Source")) def handle_get_sources(self, message): if (self.last_query): # Send an email to the account this device is registered to data = {"query": self.last_query, "answer": self.last_answer, "url_query": self.last_query.replace(" ", "+")} self.send_email(self.__translate("email.subject", data), self.__translate("email.body", data)) self.speak_dialog("sent.email") def shutdown(self): self.remove_fallback(self.handle_fallback) super(WolframAlphaSkill, self).shutdown() def __translate(self, template, data=None): return self.dialog_renderer.render(template, data)
weather_keyword = ['weather'] for wk in weather_keyword: engine.register_entity(wk, "WeatherKeyword") weather_types = ['snow', 'rain', 'wind', 'sleet', 'sun'] for wt in weather_types: engine.register_entity(wt, "WeatherType") locations = ['Seattle', 'San Fransisco', 'Tokyo'] for loc in locations: engine.register_entity(loc, 'Location') weather_intent = IntentBuilder("WeatherIntent")\ .require("WeatherKeyword")\ .optionally("WeatherType")\ .require('Location')\ .build() hide_keyword = ['hide'] for hk in hide_keyword: engine.register_entity(hk, "HideKeyword") hide_intent = IntentBuilder("HideIntent").require("HideKeyword").build() engine.register_intent_parser(weather_intent) engine.register_intent_parser(hide_intent) # Existing user token = client.login_with_password(username="******", password=password)
class MaintenanceReportingSkill(MycroftSkill): def __init__(self): MycroftSkill.__init__(self) def initialize(self): # Set the address of your Rasa's REST endpoint self.conversation_active = False self.convoID = 1 self.RASA_API = "https://b230ed1edc6c.eu.ngrok.io/webhooks/rest/webhook" self.messages = [] def query_rasa(self, prompt=None): if self.conversation_active == False: return if prompt is None and len(self.messages) > 0: prompt = self.messages[-1] # Speak message to user and save the response msg = self.get_response(prompt) # If user doesn't respond, quietly stop, allowing user to resume later if msg is None: return # Else reset messages self.messages = [] # Send post requests to said endpoint using the below format. # "sender" is used to keep track of dialog streams for different users data = requests.post(self.RASA_API, json={ "message": msg, "sender": "user{}".format(self.convoID) }) # A JSON Array Object is returned: each element has a user field along # with a text, image, or other resource field signifying the output # print(json.dumps(data.json(), indent=2)) for next_response in data.json(): if "text" in next_response: self.messages.append(next_response["text"]) # Output all but one of the Rasa dialogs if len(self.messages) > 1: for rasa_message in self.messages[:-1]: print(rasa_message) # Kills code when Rasa stop responding if len(self.messages) == 0: self.messages = ["no response from rasa"] return # Use the last dialog from Rasa to prompt for next input from the user prompt = self.messages[-1] # Allows a stream of user inputs by re-calling query_rasa recursively # It will only stop when either user or Rasa stops providing data return self.query_rasa(prompt) @intent_handler(IntentBuilder("StartChat").require("Chatwithrasa")) def handle_talk_to_rasa_intent(self, message): self.convoID += 1 self.conversation_active = True prompt = "start" self.query_rasa(prompt) # Resume chat activator that would resume the conversation thread of a chat @intent_handler(IntentBuilder("ResumeChat").require("Resume")) def handle_resume_chat(self, message): self.conversation_active = True self.query_rasa() def stop(self): self.conversation_active = False
class PptControllerSkill(MycroftSkill): def __init__(self): super(PptControllerSkill, self).__init__(name="PptControllerSkill") self.url = "http://135.222.162.94:8001" self.file_opened = False @intent_handler(IntentBuilder("PPTIntent").require('PptController')) def handle_ppt_controller(self, message): self.speak_dialog('ppt.controller') @intent_handler( IntentBuilder("OpenPPTIntent").require('OpenPPT').require("Filename")) def handle_ppt_open(self, message): filename = message.data.get("Filename") self.enclosure.mouth_text("Nova opening file " + filename) self.file_opened = True # Send a rest request param = {'filename': filename} self.enclosure.mouth_text("Sending request to " + self.url) response = requests.get(self.url, param) resp = {'filename': filename} if response.status_code == requests.codes.ok: self.speak_dialog('ppt.open', data=resp) else: self.speak_dialog('ppt.filenotfound') @intent_handler(IntentBuilder("NextSlideIntent").require('NextSlide')) def handle_next_slide(self, message): if self.file_opened: # Send a rest request nurl = self.url + "/nextpage" self.enclosure.mouth_text("Sending request to " + nurl) response = requests.get(nurl) if response.status_code == requests.codes.ok: self.speak_dialog('ppt.next') else: self.speak_dialog('ppt.filenotfound') else: self.speak_dialog('ppt.filenotopen') @intent_handler(IntentBuilder("PrevSlideIntent").require('PrevSlide')) def handle_prev_slide(self, message): # Send a rest request if self.file_opened: # Send a rest request purl = self.url + "/prevpage" self.enclosure.mouth_text("Sending request to " + purl) response = requests.get(purl) if response.status_code == requests.codes.ok: self.speak_dialog('ppt.prev') else: self.speak_dialog('ppt.filenotfound') else: self.speak_dialog('ppt.filenotopen') @intent_handler(IntentBuilder("ClosePPTIntent").require('ClosePPT')) def handle_ppt_close(self, message): # Send a rest request if self.file_opened: purl = self.url + "/close" self.enclosure.mouth_text("Sending request to " + purl) response = requests.get(purl) if response.status_code == requests.codes.ok: self.speak_dialog('ppt.close') else: self.speak_dialog('ppt.filenotfound') else: self.speak_dialog('ppt.filenotopen')
def initialize(self): intent = IntentBuilder("BBCRadioIntent").require( "BBCRadioKeyword").build() self.register_intent(intent, self.handle_intent)
class MagicMirrorVoiceControlSkill(MycroftSkill): def __init__(self): super(MagicMirrorVoiceControlSkill, self).__init__(name="MagicMirrorVoiceControlSkill") # This skill requires MMM-Remote-Control be installed and working properly. # MMM-Remote-Control requires the module identifier to know which module to # perform ModuleActionKeywords on (HIDE|SHOW). This code parses the MODULE_DATA returned from # the MMM-Remote-Control and compares it to the file "AvailableModules.json" # It then creates another file called file "AvailableModulesWithIdentifier.json" # To store the module identifier that matches the ModuleKeyword. Modules identifiers may change # depending on their order in the MagicMirror config.js file. Everytime you install a new module # the module identifiers may change. If you run into issues, restart MagicMirror and Mycroft, # and this code should update the changed module identifiers. # The if statements match what Mycroft hears to a module. For instance a user would say # weather but if MMM-WunderGround is installed it would be considered "weather". # These adjustments are made by changing the "mycroftname" in the file "AvailableModules.json" # For example: search for "weather" in the file "AvailableModules.json" and change it's # mycroftname to something other than weather like 'weather old'or 'current weather'. # Then search for MMM-Wunderground and change it's mycroftname to 'weather'. # The change must be to a module name that is also reflected in the ModuleKeywords.voc # otherwise mycroft will not recognize the name. # ///////////DO NOT CHANGE THE FILE "AvailableModulesWithIdentifier.json"////////////////// # The "AvailableModulesWithIdentifier.json" file is recreated everytime the skill initiates. # For your changes to persist all modifications should be made to the file "AvailableModules.json" def initialize(self): self.url = 'http://0.0.0.0:8080/remote' self.voiceurl = 'http://0.0.0.0:8080/kalliope' self.mycroft_utterance = '' self.moduleData = '' self.connectionStatus = '' self.kalliopeStatus = '' self.ipAddress = '' try: with open(join(self._dir, 'ip.json')) as f: ip = json.load(f) ipAddress = ip['ipAddress'] self.ipAddress = ipAddress self.url = 'http://' + ipAddress + ':8080/remote' self.voiceurl = 'http://' + ipAddress + ':8080/kalliope' self.mycroft_utterance = '' payload = {'action': 'MODULE_DATA'} r = requests.get(url=self.url, params=payload) data = r.json() with open(join(self._dir, 'AvailableModules.json')) as f: AvailableData = json.load(f) for moduleData in AvailableData['moduleData']: for item in data['moduleData']: if moduleData['name'] == item['name']: moduleData['identifier'] = item['identifier'] self.moduleData = AvailableData # Added code to see if kalliope module is installed. if not, there is no need to send events to kalliope module data = self.moduleData installed_modules = '' for moduleData in data['moduleData']: mycroftname = moduleData['mycroftname'] identifier = moduleData['identifier'] if mycroftname == 'kalliope': if identifier != '': self.kalliopeStatus = 'installed' else: self.kalliopeStatus = 'not installed' self.connectionStatus = 'connected' self.speak('I have successfully connected to the magic mirror.') except requests.exceptions.ConnectionError: if ipAddress == '0.0.0.0': self.connectionStatus = 'disconnected' self.speak( 'I was unable to connect to the magic mirror at the default ip address. To activate the magic-mirror-voice-control-skill I need to know the I P address of the magic mirror. What is the I P address of the magic mirror you would like to control with your voice?', expect_response=True) else: self.connectionStatus = 'disconnected' self.speak_dialog('not.connected') except IOError: self.connectionStatus = 'disconnected' self.speak( 'To activate the magic-mirror-voice-control-skill I need to know the I P address of the magic mirror. What is the I P address of the magic mirror you would like to control with your voice', expect_response=True) self.add_event('recognizer_loop:wakeword', self.handle_listen) self.add_event('recognizer_loop:utterance', self.handle_utterance) self.add_event('speak', self.handle_speak) self.add_event('recognizer_loop:audio_output_start', self.handle_output) self.add_event('recognizer_loop:audio_output_end', self.handle_output_end) def handle_listen(self, message): if self.connectionStatus == 'connected': if self.kalliopeStatus == 'installed': voice_payload = { "notification": "KALLIOPE", "payload": "Listening" } r = requests.post(url=self.voiceurl, data=voice_payload) def handle_utterance(self, message): if self.connectionStatus == 'connected': if self.kalliopeStatus == 'installed': utterance = message.data.get('utterances') voice_payload = { "notification": "KALLIOPE", "payload": utterance } r = requests.post(url=self.voiceurl, data=voice_payload) def handle_speak(self, message): if self.connectionStatus == 'connected': if self.kalliopeStatus == 'installed': self.mycroft_utterance = message.data.get('utterance') #voice_payload = {"notification":"KALLIOPE", "payload": self.mycroft_utterance} #r = requests.post(url=self.voiceurl, data=voice_payload) def handle_output(self, message): if self.connectionStatus == 'connected': if self.kalliopeStatus == 'installed': voice_payload = { "notification": "KALLIOPE", "payload": self.mycroft_utterance } r = requests.post(url=self.voiceurl, data=voice_payload) def handle_output_end(self, message): if self.connectionStatus == 'connected': if self.kalliopeStatus == 'installed': voice_payload = { "notification": "REMOVE_MESSAGE", "payload": "REMOVE_MESSAGE" } r = requests.post(url=self.voiceurl, data=voice_payload) def handle_not_connected(self): if self.ipAddress == '0.0.0.0': self.speak( 'I was unable to connect to the magic mirror at the default ip address. To activate the magic-mirror-voice-control-skill I need to know the I P address of the magic mirror. What is the I P address of the magic mirror you would like to control with your voice?', expect_response=True) else: self.speak_dialog('not.connected') # The following intent handler is used to set the ip address of the MagicMirror by saving it to a file ip.json # The file is saved into the skill's directory which causes Mycroft to reload the skill. After the skill reloads # the above initialize self code will find the ip.json file and load the MagicMirror ip address. If it is not the # correct address, or if the MagicMirror is not accessible the initilize self code will prompt the user to check the ip address @intent_handler( IntentBuilder('SetMirrorIpAddress').require( 'SetIpKeywords').optionally('IpAddress')) def handle_Set_Ip_command(self, message): keywords = message.data.get('SetIpKeywords') utterance = message.data['utterance'] utterance = utterance.replace(keywords, '') utterance = utterance.replace(' ', '') self.speak('I am setting the I P address to {}'.format(utterance)) try: ipaddress.ip_address(utterance) ip = {'ipAddress': utterance} with open(join(self._dir, 'ip.json'), 'w') as f: json.dump(ip, f) except: self.speak( 'Im sorry that is not a valid ip address. please try again', expect_response=True) # This code builds the SystemActionIntent which are commands that are not directed at a specific module @intent_handler( IntentBuilder('SystemActionIntent').require( 'SystemActionKeywords').require('SystemKeywords')) def handle_System_command(self, message): if self.connectionStatus == 'connected': system_action = message.data.get('SystemActionKeywords') if system_action in ('hide', 'conceal'): system_action = 'HIDE' if system_action in ('show', 'display'): system_action = 'SHOW' System = message.data.get('SystemKeywords') # This part of the SystemActionIntent handles one word remote System actions like shutdown, reboot, restart, refresh and update # if commands do not make sense as far as 'raspberry pi', 'pi', 'mirror', 'screen', # errors will result in mycroft asking the user to rephrase if System in ('raspberry pi', 'pi', 'mirror', 'screen'): if system_action in ('shutdown', 'reboot', 'restart', 'refresh', 'update', 'save'): system_action = system_action.upper( ) # MMM-Remote-Control wants actions in uppercase payload = {'action': system_action} if system_action == 'turn off': if System in ('raspberry pi', 'pi'): system_action = 'SHUTDOWN' payload = {'action': system_action} if System in ('raspberry pi', 'pi'): if system_action in ('turn on', 'SHOW', 'HIDE', 'save'): self.speak_dialog('incorrect_command', expect_response=True) # This part of the SystemActionIntent turns on/off the monitor if System in ('monitor', 'mirror', 'screen', 'modules'): if system_action in ('turn on', 'wake up', 'SHOW'): system_action = 'MONITORON' payload = {'action': system_action} if system_action in ('turn off', 'go to sleep', 'HIDE'): system_action = 'MONITOROFF' payload = {'action': system_action} # This part of the SystemActionIntent will show/hide neewsfeed article details. # It defaults to hide article details if System == 'article details': if system_action in ('SHOW', 'turn on', 'refresh'): system_action = 'NOTIFICATION' System = 'ARTICLE_MORE_DETAILS' else: system_action = 'NOTIFICATION' System = 'ARTICLE_LESS_DETAILS' payload = {'action': system_action, 'notification': System} r = requests.get(url=self.url, params=payload) status = r.json() response = status['status'] if response == 'success': self.speak_dialog('success') else: reason = status['reason'] reason = reason.replace('_', ' ') self.speak( 'There was an error processing your request. The error was caused by', reason) else: self.handle_not_connected() # This intent will have mycroft read the installed modules 'mycroftname' so that the user knows which mdules are installed @intent_handler( IntentBuilder('ListInstalledModulesIntent').require( 'ListInstalledKeywords').require('SingleModuleKeywords')) def handle_list_installed_modules_command(self, message): if self.connectionStatus == 'connected': data = self.moduleData installed_modules = '' for moduleData in data['moduleData']: mycroftname = moduleData['mycroftname'] identifier = moduleData['identifier'] if identifier != "": installed_modules = installed_modules + ', ' + mycroftname self.speak('The currently installed modules are{}'.format( installed_modules)) else: self.handle_not_connected() # This intent handles change page commands to be used with the MMM-pages module. The MMM-pages module must be installed # for this intent to work. Find it on github @ https://github.com/edward-shen/MMM-pages @intent_handler( IntentBuilder('ChangePagesIntent').require( 'PageActionKeywords').require('PageKeywords')) def handle_change_pages_command(self, message): if self.connectionStatus == 'connected': page = message.data.get('PageKeywords') if page in ('one', '1', 'home'): integer = 0 if page in ('two', '2'): integer = 1 if page in ('three', '3'): integer = 2 if page in ('four', '4', 'for'): integer = 3 if page in ('five', '5'): integer = 4 if page in ('six', '6'): integer = 5 if page in ('seven', '7'): integer = 6 if page in ('eight', '8'): integer = 7 if page in ('nine', '9'): integer = 8 if page in ('ten', '10'): integer = 9 notification = 'PAGE_CHANGED' action = 'NOTIFICATION' payload = { 'action': action, 'notification': notification, 'payload': integer } r = requests.get(url=self.url, params=payload) status = r.json() response = status['status'] if response == 'success': self.speak_dialog('success') else: reason = status['reason'] reason = reason.replace('_', ' ') self.speak( 'There was an error processing your request. The error was caused by', reason) else: self.handle_not_connected() # This intent handles swipe commands to be used with the MMM-pages module. The MMM-pages module must be installed # for the swipe intent to work. Find it on github @ https://github.com/edward-shen/MMM-pages @intent_handler( IntentBuilder('HandleSwipeIntent').require( 'SwipeActionKeywords').require('LeftRightKeywords')) def handle_pages_command(self, message): if self.connectionStatus == 'connected': direction = message.data.get('LeftRightKeywords') if direction == 'right': System = 'PAGE_DECREMENT' if direction == 'left': System = 'PAGE_INCREMENT' action = 'NOTIFICATION' payload = {'action': action, 'notification': System} r = requests.get(url=self.url, params=payload) status = r.json() response = status['status'] if response == 'success': self.speak_dialog('success') else: reason = status['reason'] reason = reason.replace('_', ' ') self.speak( 'There was an error processing your request. The error was caused by', reason) else: self.handle_not_connected() # This intent handles a number of different user utterances for the brightness value, including # numbers, numbers followed by %, numbers as words, numbers as words including the word percent. # Not all references need to include (%|percent), this can be a value between 10 - 200 @intent_handler( IntentBuilder('AdjustBrightnessIntent').require( 'BrightnessActionKeywords').require('BrightnessValueKeywords')) def handle_adjust_brightness_command(self, message): if self.connectionStatus == 'connected': action = 'BRIGHTNESS' value = message.data.get('BrightnessValueKeywords') value_without_spaces = value.replace(' ', '') iswords = str.isalpha(value_without_spaces) if iswords == True: percent_present = re.search('percent', value) if percent_present != None: value = value.replace(' percent', '') with open(join(self._dir, 'numberwords.json')) as f: data = json.load(f) for item in data['numberwords']: if value == item['word']: value = item['number'] break value = ((value / 100) * 200) else: with open(join(self._dir, 'numberwords.json')) as f: data = json.load(f) for item in data['numberwords']: if value == item['word']: value = item['number'] break # This else handles numbers including numbers with the '%' sign else: percent_present = (re.search('%', value)) if percent_present != None: value = (re.sub('[%]', '', value)) value = int(value) value = ((value / 100) * 200) action = 'BRIGHTNESS' if value < 10: value = 10 else: value = int(value) payload = {'action': action, 'value': value} r = requests.get(url=self.url, params=payload) status = r.json() response = status['status'] if response == 'success': self.speak_dialog('success') else: reason = status['reason'] reason = reason.replace('_', ' ') self.speak( 'There was an error processing your request. The error was caused by', reason) else: self.handle_not_connected() # This intent handles commands directed at specific modules. Commands include: hide # show, display, conceal, install, add, turn on, turn off, update. # TODO The add module needs to be changed to 'add' the recently installed module's configuration # to the config.js of the MagicMirror. this is the intended functionallity. currently it is # set up to be another way to say install the module. @intent_handler( IntentBuilder('ModuleActionIntent').require( 'ModuleActionKeywords').require('ModuleKeywords')) def handle_module_command(self, message): if self.connectionStatus == 'connected': module_action = message.data.get('ModuleActionKeywords') if module_action in ('hide', 'conceal', 'turn off'): module_action = 'HIDE' if module_action in ('show', 'display', 'turn on'): module_action = 'SHOW' module = message.data.get('ModuleKeywords') data = self.moduleData for item in data['moduleData']: if module == item['mycroftname']: module_id = item['identifier'] module_url = item['URL'] module_name = item['name'] if module_action in ('HIDE', 'SHOW'): module_action = module_action.upper() payload = {'action': module_action, 'module': module_id} if module_action in ('install', 'add'): module_action = 'INSTALL' payload = {'action': module_action, 'url': module_url} if module_action == 'update': module_action = module_action.upper() payload = {'action': module_action, 'module': module_name} r = requests.get(url=self.url, params=payload) status = r.json() response = status['status'] if response == 'success': self.speak_dialog('success') else: reason = status['reason'] reason = reason.replace('_', ' ') self.speak_dialog('No.Such.Module') else: self.handle_not_connected() def stop(self): pass
def __build_lighting_intent(self): intent = IntentBuilder("LightingIntent").require( "LightActionKeyword").require("Action").require("Entity").build() # TODO - Locks, Temperature, Identity location self.register_intent(intent, self.handle_lighting_intent)
class SpotifySkill(MycroftSkill): """Spotify control through the Spotify Connect API.""" def __init__(self): super(SpotifySkill, self).__init__() self.index = 0 self.spotify = None self.process = None self.device_name = None self.dev_id = None self.idle_count = 0 self.ducking = False self.mouth_text = None self.__device_list = None self.__devices_fetched = 0 self.OAUTH_ID = 1 self.DEFAULT_VOLUME = 65 self._playlists = None def launch_librespot(self): """ Launch the librespot binary for the Mark-1. TODO: Discovery mode """ platform = self.config_core.get("enclosure").get("platform", "unknown") path = self.settings.get('librespot_path', None) if platform == 'mycroft_mark_1' and not path: path = 'librespot' if (path and self.device_name and 'user' in self.settings and 'password' in self.settings): # TODO: Error message when provided username/password don't work self.process = Popen([ path, '-n', self.device_name, '-u', self.settings['user'], '-p', self.settings['password'] ]) time.sleep(3) # give libreSpot time to start-up # Lower the volume since max volume sounds terrible on the Mark-1 dev = self.device_by_name(self.device_name) if dev: self.spotify.volume(dev['id'], self.DEFAULT_VOLUME) def initialize(self): # Setup handlers for playback control messages self.add_event('mycroft.audio.service.next', self.next_track) self.add_event('mycroft.audio.service.prev', self.prev_track) self.add_event('mycroft.audio.service.pause', self.pause) self.add_event('mycroft.audio.service.resume', self.resume) # Check and then monitor for credential changes self.settings.set_changed_callback(self.on_websettings_changed) self.on_websettings_changed() def on_websettings_changed(self): if not self.spotify: if 'user' in self.settings and 'password' in self.settings: try: self.load_credentials() except: # Retry in 5 minutes self.schedule_repeating_event(self.on_websettings_changed, None, 5 * 60, name='SpotifyLogin') def load_credentials(self): """ Retrieve credentials from the backend and connect to Spotify """ try: creds = MycroftSpotifyCredentials(self.OAUTH_ID) self.spotify = SpotifyConnect(client_credentials_manager=creds) except HTTPError: LOG.info('Couldn\'t fetch credentials') self.spotify = None if self.spotify: # Spotfy connection worked, prepare for usage # TODO: Repeat occasionally on failures? # If not able to authorize, the method will be repeated after 60 # seconds self.create_intents() # Should be safe to set device_name here since home has already # been connected self.device_name = DeviceApi().get().get('name') self.cancel_scheduled_event('SpotifyLogin') self.launch_librespot() ###################################################################### # Handle auto ducking when listener is started. def handle_listener_started(self, message): """ Handle auto ducking when listener is started. """ if self.spotify.is_playing(): self.__pause() self.ducking = True # Start idle check self.idle_count = 0 self.cancel_scheduled_event('IdleCheck') self.schedule_repeating_event(self.check_for_idle, None, 1, name='IdleCheck') def check_for_idle(self): """ Repeating event checking for end of auto ducking. """ if not self.ducking: self.cancel_scheduled_event('IdleCheck') return active = DisplayManager.get_active() if not active == '' or active == "SpotifySkill": # No activity, start to fall asleep self.idle_count += 1 if self.idle_count >= 5: # Resume playback after 5 seconds of being idle self.cancel_scheduled_event('IdleCheck') self.ducking = False self.resume() else: self.idle_count = 0 ###################################################################### # Mycroft display handling def start_monitor(self): """ Monitoring and current song display. """ # Clear any existing event self.stop_monitor() # Schedule a new one every 5 seconds to monitor/update display self.schedule_repeating_event(self._update_display, None, 5, name='MonitorSpotify') self.add_event('recognizer_loop:record_begin', self.handle_listener_started) def stop_monitor(self): # Clear any existing event self.cancel_scheduled_event('MonitorSpotify') def _update_display(self, message): # Checks once a second for feedback status = self.spotify.status() if self.spotify else {} if not status or not status.get('is_playing'): self.stop_monitor() self.mouth_text = None self.enclosure.mouth_reset() return # Get the current track info try: text = status["item"]["artists"][0]["name"] + ": " except: text = "" try: text += status["item"]["name"] except: pass # Update the "Now Playing" display if needed if text != self.mouth_text: self.mouth_text = text self.enclosure.mouth_text(text) ###################################################################### # Intent handling def create_intents(self): """ Create intents for start playback handlers.""" # play playlists self.register_intent_file('Play.intent', self.play_playlist) self.register_intent_file('PlayOn.intent', self.play_playlist_on) # play album intent = IntentBuilder('').require('Play').require('AlbumTitle') \ .optionally('Spotify') self.register_intent(intent, self.play_album) # play artist intent = IntentBuilder('').require('Play').require('Artist') \ .optionally('Spotify') self.register_intent(intent, self.play_artist) @property def playlists(self): """ Playlists, cached for 5 minutes """ now = time.time() if not self._playlists or (now - self.__playlists_fetched > 5 * 60): self._playlists = {} playlists = self.spotify.current_user_playlists().get('items', []) for p in playlists: self._playlists[p['name']] = p self.__playlists_fetched = now return self._playlists @property def devices(self): """ Devices, cached for 60 seconds """ now = time.time() if not self.__device_list or (now - self.__devices_fetched > 60): self.__device_list = self.spotify.get_devices() self.__devices_fetched = now return self.__device_list def device_by_name(self, name): """ Get a Spotify devices from the API Args: name (str): The device name (fuzzy matches) Returns: (dict) None or the matching device's description """ devices = self.devices if devices and len(devices) > 0: # Otherwise get a device with the selected name devices_by_name = {d['name']: d for d in devices} key, confidence = match_one(name, devices_by_name.keys()) if confidence > 0.5: return devices_by_name[key] return None def get_default_device(self): """ Get preferred playback device """ if self.spotify: # When there is an active Spotify device somewhere, use it if (self.devices and len(self.devices) > 0 and self.spotify.is_playing()): for dev in self.devices: if dev['is_active']: return dev # Use this device # No playing device found, use the local Spotify instance dev = self.device_by_name(self.device_name) if not dev: dev = self.device_by_name(gethostname()) if dev and not dev['is_active']: self.spotify.transfer_playback(dev["id"], False) return dev return None def get_best_playlist(self, playlist): """ Get best playlist matching the provided name Arguments: playlist (str): Playlist name Returns: (str) best match """ key, confidence = match_one(playlist, self.playlists.keys()) if confidence > 0.5: return key else: return None def play_playlist(self, message): """ Play user playlist on default device. """ playlist = message.data.get('playlist') if not playlist or playlist == 'spotify': self.continue_current_playlist(message) elif self.playback_prerequisits_ok(): dev = self.get_default_device() self.start_playback(dev, self.get_best_playlist(playlist)) def playback_prerequisits_ok(self): """ Check that playback is possible, launch client if neccessary. """ if self.spotify is None: self.speak_dialog('NotAuthorized') return False if not self.process: self.launch_librespot() return True @intent_handler(IntentBuilder('').require('Play').require('Spotify')) def play_spotify(self, message): # Play anything if self.playback_prerequisits_ok(): message.data['utterance'] = 'play spotify' # play anything! self.play_playlist(message) else: self.speak_dialog("NotAuthorized") def spotify_play(self, dev_id, uris=None, context_uri=None): """ Start spotify playback and catch any exceptions. """ try: LOG.info(u'spotify_play: {}'.format(dev_id)) self.spotify.play(dev_id, uris, context_uri) self.start_monitor() self.dev_id = dev_id # self.show_notes() except spotipy.SpotifyException as e: # TODO: Catch other conditions? self.speak_dialog('NotAuthorized') except Exception as e: LOG.exception(e) self.speak_dialog('NotAuthorized') def start_playback(self, dev, playlist_name): LOG.info(u'Playlist: {}'.format(playlist_name)) playlist = None if playlist_name: playlist = self.get_best_playlist(playlist_name) if not playlist: LOG.info(u'Playlists: {}'.format(self.playlists)) if not self.playlists: return # different default action when no lists defined? playlist = self.get_best_playlist(self.playlists.keys()[0]) if dev and playlist: LOG.info(u'playing {} using {}'.format(playlist, dev['name'])) self.speak_dialog('listening_to', data={'tracks': playlist}) time.sleep(2) pl = self.playlists[playlist] tracks = self.spotify.user_playlist_tracks(pl['owner']['id'], pl['id']) uris = [t['track']['uri'] for t in tracks['items']] self.spotify_play(dev['id'], uris=uris) # self.show_notes() elif dev: LOG.info(u'couldn\'t find {}'.format(playlist)) else: LOG.info('No spotify devices found') def play_playlist_on(self, message): """ Play playlist on specific device. """ if self.playback_prerequisits_ok(): playlist = self.get_best_playlist(message.data.get('playlist')) dev = self.device_by_name(message.data.get('device')) if dev: # Assume we are about to act on this device, # transfer playback to it. if not dev['is_active']: self.spotify.transfer_playback(dev["id"], False) self.start_playback(dev, playlist) @intent_file_handler('PlaySpotify.intent') def continue_current_playlist(self, message): if self.playback_prerequisits_ok(): dev = self.get_default_device() if dev: self.spotify_play(dev['id']) else: self.speak_dialog('NoDevicesAvailable') @intent_handler( IntentBuilder('').require('Search').require('AlbumTitle').require( 'Spotify')) def search_album(self, message): if self.playback_prerequisits_ok(): return self.search(message.data['AlbumTitle'], 'album') def play_album(self, message): if self.playback_prerequisits_ok(): return self.search(message.data['AlbumTitle'], 'album') def play_artist(self, message): if self.playback_prerequisits_ok(): return self.search(message.data['Artist'], 'artist') def search(self, query, search_type): """ Search for an album, playlist or artist. Arguments: query: search query (album title, artist, etc.) search_type: weather to search for an 'album', 'artist' or 'playlist' TODO: improve results of albums by checking artist """ dev = self.get_default_device() res = None if search_type == 'album' and len(query.split('by')) > 1: title, artist = query.split('by') result = self.spotify.search(title, type=search_type) else: result = self.spotify.search(query, type=search_type) if search_type == 'album': if len(result['albums']['items']) > 0 and dev: album = result['albums']['items'][0] LOG.info(album) res = album elif search_type == 'artist': LOG.info(result['artists']) artist = result['artists']['items'][0] LOG.info(artist) res = artist else: LOG.info('ERROR') return self.speak_dialog('listening_to', data={'tracks': res['name']}) time.sleep(2) self.spotify_play(dev['id'], context_uri=res['uri']) def __pause(self): # if authorized and playback was started by the skill if self.spotify: LOG.info('Pausing Spotify...') self.spotify.pause(self.dev_id) def pause(self, message=None): """ Handler for playback control pause. """ self.ducking = False self.__pause() def resume(self, message=None): """ Handler for playback control resume. """ # if authorized and playback was started by the skill if self.spotify: LOG.info('Resume Spotify') if not self.dev_id: self.dev_id = self.get_default_device() self.spotify_play(self.dev_id) def next_track(self, message): """ Handler for playback control next. """ # if authorized and playback was started by the skill if self.spotify and self.dev_id: LOG.info('Next Spotify track') self.spotify.next(self.dev_id) self.start_monitor() def prev_track(self, message): """ Handler for playback control prev. """ # if authorized and playback was started by the skill if self.spotify and self.dev_id: LOG.info('Previous Spotify track') self.spotify.prev(self.dev_id) self.start_monitor() @intent_handler(IntentBuilder('').require('Spotify').require('Device')) def list_devices(self, message): """ List available devices. """ LOG.info(self) if self.spotify: devices = [d['name'] for d in self.spotify.get_devices()] if len(devices) == 1: self.speak(devices[0]) elif len(devices) > 1: self.speak_dialog( 'AvailableDevices', { "devices": '. '.join(devices[:-1]) + ". " + self.translate("And") + ". " + devices[-1] }) else: self.speak_dialog('NoDevicesAvailable') else: self.speak_dialog('NotAuthorized') @intent_handler( IntentBuilder('').require('Transfer').require('Spotify').require( 'ToDevice')) def transfer_playback(self, message): """ Move playback from one device to another. """ if self.spotify and self.spotify.is_playing(): dev = self.device_by_name(message.data['ToDevice']) if dev: self.spotify.transfer_playback(dev['id']) def show_notes(self): """ show notes, HA HA """ self.schedule_repeating_event(self._update_notes, datetime.datetime.now(), 2, name='dancing_notes') def display_notes(self): """ Start timer thread displaying notes on the display. """ pass def clear_display(self): """ Clear display. """ self.enclosure.mouth_display(img_code="HIAAAAAAAAAAAAAA", refresh=False) self.enclosure.mouth_display(img_code="HIAAAAAAAAAAAAAA", x=24, refresh=False) def draw_notes(self, index): """ Draw notes on display. """ notes = [['IIAEAOOHGAGEGOOHAA', 'IIAAACAHPDDADCDHPD'], ['IIAAACAHPDDADCDHPD', 'IIAEAOOHGAGEGOOHAA']] # draw notes for pos in range(4): self.enclosure.mouth_display(img_code=notes[index][pos % 2], x=pos * 8, refresh=False) def _update_notes(self): """ Repeating event updating the display. """ if self._should_display_notes(): self.draw_notes(self.index) self.index = ((self.index + 1) % 2) def stop(self): """ Stop playback. """ if not self.spotify or not self.spotify.is_playing(): self.dev_id = None return False dev = self.get_default_device() self.dev_id = dev['id'] if self.dev_id: # self.remove_event('dancing_notes') self.pause(None) # Clear playing device id self.dev_id = None return True def stop_librespot(self): """ Send Terminate signal to librespot if it's running. """ if self.process and self.process.poll() is None: self.process.send_signal(signal.SIGTERM) self.process.communicate() # Communicate to remove zombie def shutdown(self): """ Remove the monitor at shutdown. """ # Do normal shutdown procedure super(SpotifySkill, self).shutdown() self.stop_monitor() self.stop_librespot() def _should_display_notes(self): _get_active = DisplayManager.get_active if _get_active() == '' or _get_active() == self.name: return True else: return False
class MozillaIotGateway(MycroftSkill): def __init__(self): MycroftSkill.__init__(self) self.host = 'https://gateway.local' self.token = '' def get_headers(self): return { 'Authorization': 'Bearer ' + self.token, 'Accept': 'application/json', 'Content-Type': 'application/json', } def get_oauth_token(self): try: token = DeviceApi().get_oauth_token(1172752248686736379) except requests.HTTPError: return None return token['access_token'] def get_oauth_host(self): try: token = DeviceApi().get_oauth_token(1172752248686736379) except requests.HTTPError: return None jwt = token['access_token'] parts = jwt.split(".") if len(parts) < 2: return None payload_raw = parts[1] if len(payload_raw) % 3 > 0: payload_raw += "=" * (3 - (len(payload_raw) % 3)) try: payload_medium_rare = base64.b64decode(payload_raw) payload = json.loads(payload_medium_rare) return payload['iss'] except ValueError: return None @intent_handler( IntentBuilder('CommandIntent').require('Action').require('Type')) # .require('Thing')) def handle_command_intent(self, message): host = self.settings.get('host') if not host: host = self.get_oauth_host() if host: self.host = host token = self.settings.get('token') if not token: token = self.get_oauth_token() if token: self.token = token if not self.token: self.speak_dialog('needs.configure') return utt = message.data.get('utterance') response = requests.post(self.host + '/commands', json={'text': utt}, headers=self.get_headers()) data = response.json() if response.status_code != 201: self.speak(data['message']) return verb = '' preposition = '' keyword = data['payload']['keyword'] if keyword == 'make': verb = 'making' elif keyword == 'change': verb = 'changing' elif keyword == 'set': verb = 'setting' preposition = 'to ' elif keyword == 'dim': verb = 'dimming' preposition = 'by ' elif keyword == 'brighten': verb = 'brightening' preposition = 'by ' else: verb = keyword + 'ing' value = data['payload']['value'] if value is None: value = '' thing = data['payload']['thing'] message = 'Okay, ' + verb + ' the ' + thing + ' ' + preposition + value self.speak(message)
def initialize(self): self.load_data_files(dirname(__file__)) intent = IntentBuilder("BitcoinPriceIntent").require("BitcoinPriceKeyword").build() self.register_intent(intent, self.handle_intent)
def initialize(self): self.load_vocab_files(join(dirname(__file__), 'vocab', self.lang)) self.load_regex_files(join(dirname(__file__), 'regex', self.lang)) intent = IntentBuilder("SendSMSIntent").require( "SendSMSKeyword").require("Contact").require("Message").build() self.register_intent(intent, self.handle_intent)
class SonosSkill(CommonPlaySkill): """Sonos control through the Sonos Connect API.""" def __init__(self): super(SonosSkill, self).__init__() self.index = 0 self.spotify = None self.process = None self.device_name = None self.dev_id = None self.idle_count = 0 self.ducking = False self.mouth_text = None self.librespot_starting = False self.__device_list = None self.__devices_fetched = 0 self.OAUTH_ID = 1 self.DEFAULT_VOLUME = 80 self._playlists = None def launch_librespot(self): """ Launch the librespot binary for the Mark-1. TODO: Discovery mode """ self.librespot_starting = True platform = self.config_core.get('enclosure').get('platform', 'unknown') path = self.settings.get('librespot_path', None) if platform == 'mycroft_mark_1' and not path: path = 'librespot' if (path and self.device_name and 'user' in self.settings and 'password' in self.settings): # Disable librespot logging if not specifically requested outs = None if 'librespot_log' in self.settings else DEVNULL # TODO: Error message when provided username/password don't work self.process = Popen([ path, '-n', self.device_name, '-u', self.settings['user'], '-p', self.settings['password'] ], stdout=outs, stderr=outs) time.sleep(3) # give libreSpot time to start-up if self.process and self.process.poll() is not None: # libreSpot shut down immediately. Bad user/password? if self.settings['user']: self.speak_dialog("FailedToStart") self.process = None self.librespot_starting = False return # Lower the volume since max volume sounds terrible on the Mark-1 dev = self.device_by_name(self.device_name) if dev: self.spotify.volume(dev['id'], self.DEFAULT_VOLUME) self.librespot_starting = False def initialize(self): # Make sure the spotify login scheduled event is shutdown super().initialize() self.cancel_scheduled_event('SonosLogin') # Setup handlers for playback control messages self.add_event('mycroft.audio.service.next', self.next_track) self.add_event('mycroft.audio.service.prev', self.prev_track) self.add_event('mycroft.audio.service.pause', self.pause) self.add_event('mycroft.audio.service.resume', self.resume) # Check and then monitor for credential changes self.settings.set_changed_callback(self.on_websettings_changed) # Retry in 5 minutes self.schedule_repeating_event(self.on_websettings_changed, None, 5 * 60, name='SonosLogin') self.on_websettings_changed() def on_websettings_changed(self): pass ###################################################################### # Handle auto ducking when listener is started. def handle_listener_started(self, message): """ Handle auto ducking when listener is started. The ducking is enabled/disabled using the skill settings on home. TODO: Evaluate the Idle check logic """ if self.spotify.is_playing() and \ self.settings.get('use_ducking', False): self.__pause() self.ducking = True # Start idle check self.idle_count = 0 self.cancel_scheduled_event('IdleCheck') self.schedule_repeating_event(self.check_for_idle, None, 1, name='IdleCheck') def check_for_idle(self): """ Repeating event checking for end of auto ducking. """ if not self.ducking: self.cancel_scheduled_event('IdleCheck') return active = self.enclosure.display_manager.get_active() if not active == '' or active == 'SonosSkill': # No activity, start to fall asleep self.idle_count += 1 if self.idle_count >= 5: # Resume playback after 5 seconds of being idle self.cancel_scheduled_event('IdleCheck') self.ducking = False self.resume() else: self.idle_count = 0 ###################################################################### # Mycroft display handling def start_monitor(self): """ Monitoring and current song display. """ # Clear any existing event self.stop_monitor() # Schedule a new one every 5 seconds to monitor/update display self.schedule_repeating_event(self._update_display, None, 5, name='MonitorSonos') self.add_event('recognizer_loop:record_begin', self.handle_listener_started) def stop_monitor(self): # Clear any existing event self.cancel_scheduled_event('MonitorSonos') def _update_display(self, message): # Checks once a second for feedback status = self.spotify.status() if self.spotify else {} if not status or not status.get('is_playing'): self.stop_monitor() self.mouth_text = None self.enclosure.mouth_reset() return # Get the current track info try: text = status['item']['artists'][0]['name'] + ': ' except: text = "" try: text += status['item']['name'] except: pass # Update the "Now Playing" display if needed if text != self.mouth_text: self.mouth_text = text self.enclosure.mouth_text(text) ###################################################################### # Intent handling def CPS_match_query_phrase(self, phrase): # Not ready to play if not self.playback_prerequisits_ok(): return None if 'spotify' in phrase: bonus = 0.1 else: bonus = 0 phrase = re.sub(self.translate('on_spotify_regex'), '', phrase) confidence, data = self.continue_playback(phrase, bonus) if not data: confidence, data = self.specific_query(phrase, bonus) if not data: confidence, data = self.generic_query(phrase, bonus) if data: if confidence > 0.9: confidence = CPSMatchLevel.EXACT elif confidence > 0.7: confidence = CPSMatchLevel.MULTI_KEY elif confidence > 0.5: confidence = CPSMatchLevel.TITLE else: confidence = CPSMatchLevel.CATEGORY return phrase, confidence, data def continue_playback(self, phrase, bonus): if phrase.strip() == 'spotify': return (1.0, {'data': None, 'name': None, 'type': 'continue'}) else: return None, None def specific_query(self, phrase, bonus): # Check if playlist match = re.match(self.translate('playlist_regex'), phrase) if match: bonus += 0.1 playlist, conf = self.get_best_playlist( match.groupdict()['playlist']) confidence = min(conf + bonus, 1.0) if not playlist: return uri = self.playlists[playlist] return (conf, {'data': uri, 'name': playlist, 'type': 'playlist'}) # Check album match = re.match(self.translate('album_regex'), phrase) if match: bonus += 0.1 album = match.groupdict()['album'] return self.query_album(album, bonus) # Check artist match = re.match(self.translate('artist_regex'), phrase) if match: bonus += 0.1 artist = match.groupdict()['artist'] data = self.spotify.search(artist, type='artist') if data and data['artists']['items']: best = data['artists']['items'][0]['name'] confidence = min( fuzzy_match(best, artist.lower()) + bonus, 1.0) return (confidence, { 'data': data, 'name': None, 'type': 'artist' }) match = re.match(self.translate('song_regex'), phrase) if match: data = self.spotify.search(match.groupdict()['track'], type='track') if data: return (1.0, {'data': data, 'name': None, 'type': 'track'}) return None, None def generic_query(self, phrase, bonus): playlist, conf = self.get_best_playlist(phrase) if conf > 0.5: uri = self.playlists[playlist] return (conf, {'data': uri, 'name': playlist, 'type': 'playlist'}) else: return self.query_album(phrase, bonus) def query_album(self, album, bonus): data = None by_word = ' {} '.format(self.translate('by')) if len(album.split(by_word)) > 1: album, artist = album.split(by_word) album = '*{}* artist:{}'.format(album, artist) bonus += 0.1 data = self.spotify.search(album, type='album') if data and data['albums']['items']: best = data['albums']['items'][0]['name'] confidence = min(fuzzy_match(best.lower(), album) + bonus, 1.0) return (confidence, {'data': data, 'name': None, 'type': 'album'}) return None, None def CPS_start(self, phrase, data): # Wait for librespot to start if self.librespot_starting: self.log.info('Restarting Librespot...') for i in range(10): time.sleep(0.5) if not self.librespot_starting: break else: self.log.error('LIBRESPOT NOT STARTED') dev = self.get_default_device() if data['type'] == 'continue': self.continue_current_playlist(None) elif data['type'] == 'playlist': self.start_playlist_playback(dev, data['name'], data['data']) else: # artist, album track print('playing {}'.format(data['type'])) try: self.play(data=data['data'], data_type=data['type']) except: self.log.exception() def create_intents(self): # Create intents intent = IntentBuilder('').require('Sonos').require('Search') \ .require('For') self.register_intent(intent, self.search_spotify) self.register_intent_file('ShuffleOn.intent', self.shuffle_on) self.register_intent_file('ShuffleOff.intent', self.shuffle_off) @property def playlists(self): """ Playlists, cached for 5 minutes """ if not self.spotify: return [] # No connection, no playlists now = time.time() if not self._playlists or (now - self.__playlists_fetched > 5 * 60): self._playlists = {} playlists = self.spotify.current_user_playlists().get('items', []) for p in playlists: self._playlists[p['name'].lower()] = p self.__playlists_fetched = now return self._playlists @property def devices(self): """ Devices, cached for 60 seconds """ if not self.spotify: return [] # No connection, no devices now = time.time() if not self.__device_list or (now - self.__devices_fetched > 60): self.__device_list = self.spotify.get_devices() self.__devices_fetched = now return self.__device_list def device_by_name(self, name): """ Get a Sonos devices from the API Args: name (str): The device name (fuzzy matches) Returns: (dict) None or the matching device's description """ devices = self.devices if devices and len(devices) > 0: # Otherwise get a device with the selected name devices_by_name = {d['name']: d for d in devices} key, confidence = match_one(name, list(devices_by_name.keys())) if confidence > 0.5: return devices_by_name[key] return None def get_default_device(self): """ Get preferred playback device """ if self.spotify: # When there is an active Sonos device somewhere, use it if (self.devices and len(self.devices) > 0 and self.spotify.is_playing()): for dev in self.devices: if dev['is_active']: return dev # Use this device # No playing device found, use the default Sonos device default_device = self.settings.get('default_device', '') dev = None if default_device: dev = self.device_by_name(default_device) # if not set or missing try playing on this device if not dev: dev = self.device_by_name(self.device_name) # if not check if a desktop spotify client is playing if not dev: dev = self.device_by_name(gethostname()) # use first best device if none of the prioritized works if not dev and len(self.devices) > 0: dev = self.devices[0] if dev and not dev['is_active']: self.spotify.transfer_playback(dev['id'], False) return dev return None def get_best_playlist(self, playlist): """ Get best playlist matching the provided name Arguments: playlist (str): Playlist name Returns: (str) best match """ key, confidence = match_one(playlist.lower(), list(self.playlists.keys())) if confidence > 0.7: return key, confidence else: return None, 0 def continue_current_playlist(self, message): if self.playback_prerequisits_ok(): dev = self.get_default_device() if dev: self.spotify_play(dev['id']) else: self.speak_dialog('NoDevicesAvailable') def playback_prerequisits_ok(self): """ Check that playback is possible, launch client if neccessary. """ if self.spotify is None: return False devs = [d['name'] for d in self.devices] if self.process and self.device_name not in devs: self.log.info('Librespot not responding, restarting...') self.stop_librespot() self.__devices_fetched = 0 # Make sure devices are fetched again if not self.process: self.schedule_event(self.launch_librespot, 0, name='launch_librespot') return True def spotify_play(self, dev_id, uris=None, context_uri=None): """ Start spotify playback and catch any exceptions. """ try: LOG.info(u'spotify_play: {}'.format(dev_id)) self.spotify.play(dev_id, uris, context_uri) self.start_monitor() self.dev_id = dev_id except spotipy.SonosException as e: # TODO: Catch other conditions? self.speak_dialog('NotAuthorized') except Exception as e: LOG.exception(e) self.speak_dialog('NotAuthorized') def start_playlist_playback(self, dev, name, uri): if dev and uri: LOG.info(u'playing {} using {}'.format(name, dev['name'])) self.speak_dialog('ListeningToPlaylist', data={'playlist': name}) time.sleep(2) tracks = self.spotify.user_playlist_tracks(uri['owner']['id'], uri['id']) uris = [t['track']['uri'] for t in tracks['items']] self.spotify_play(dev['id'], uris=uris) return True elif not dev: LOG.info('No spotify devices found') else: LOG.info('No playlist found') return False def play(self, data, data_type='track', genre_name=None): """ Plays the provided data in the manner appropriate for 'data_type' If the type is 'genre' then genre_name should be specified to populate the output dialog. A 'track' is played as just an individual track. An 'album' queues up all the tracks contained in that album and starts with the first track. A 'genre' expects data returned from self.spotify.search, and will use that genre to play a selection similar to it. Args: data (dict): Data returned by self.spotify.search data_type (str): The type of data contained in the passed-in object. 'track', 'album', or 'genre' are currently supported. genre_name (str): If type is 'genre', also include the genre's name here, for output purposes. default None """ dev = self.get_default_device() if dev is None: LOG.error("Unable to get a default device while trying " "to play something.") self.speak_dialog('NoDevicesAvailable') else: try: if data_type == 'track': (song, artists, uri) = get_song_info(data) self.speak_dialog('ListeningToSongBy', data={ 'tracks': song, 'artist': artists[0] }) time.sleep(2) self.spotify_play(dev['id'], uris=[uri]) elif data_type == 'artist': (artist, uri) = get_artist_info(data) self.speak_dialog('ListeningToArtist', data={'artist': artist}) time.sleep(2) self.spotify_play(dev['id'], context_uri=uri) elif data_type == 'album': (album, artists, uri) = get_album_info(data) self.speak_dialog('ListeningToAlbumBy', data={ 'album': album, 'artist': artists[0] }) time.sleep(2) self.spotify_play(dev['id'], context_uri=uri) elif data_type == 'genre': items = data['tracks']['items'] random.shuffle(items) uris = [] for item in items: uris.append(item['uri']) datai = { 'genre': genre_name, 'track': items[0]['name'], 'artist': items[0]['artists'][0]['name'] } self.speak_dialog('ListeningToGenre', data) time.sleep(2) self.spotify_play(dev['id'], uris=uris) else: print('wrong data_type') except Exception as e: LOG.error("Unable to obtain the name, artist, " "and/or URI information while asked to play " "something. " + str(e)) def search(self, query, search_type): """ Search for an album, playlist or artist. Arguments: query: search query (album title, artist, etc.) search_type: whether to search for an 'album', 'artist', 'playlist', 'track', or 'genre' TODO: improve results of albums by checking artist """ res = None if search_type == 'album' and len(query.split('by')) > 1: title, artist = query.split('by') result = self.spotify.search(title, type=search_type) else: result = self.spotify.search(query, type=search_type) if search_type == 'album': if len(result['albums']['items']) > 0: album = result['albums']['items'][0] LOG.info(album) res = album elif search_type == 'artist': LOG.info(result['artists']) if len(result['artists']['items']) > 0: artist = result['artists']['items'][0] LOG.info(artist) res = artist elif search_type == 'genre': LOG.info("TODO! Genre") else: LOG.info('ERROR') return return res def search_spotify(self, message): """ Intent handler for "search spotify for X". """ utterance = message.data['utterance'] if len(utterance.split(self.translate('ForAlbum'))) == 2: query = utterance.split(self.translate('ForAlbum'))[1].strip() data = self.spotify.search(query, type='album') self.play(data=data, data_type='album') elif len(utterance.split(self.translate('ForArtist'))) == 2: query = utterance.split(self.translate('ForArtist'))[1].strip() data = self.spotify.search(query, type='artist') self.play(data=data, data_type='artist') else: for_word = ' ' + self.translate('For') query = for_word.join(utterance.split(for_word)[1:]).strip() data = self.spotify.search(query, type='track') self.play(data=data, data_type='track') def shuffle_on(self): """ Get preferred playback device """ if self.spotify: self.spotify.shuffle(True) def shuffle_off(self): """ Get preferred playback device """ if self.spotify: self.spotify.shuffle(False) def __pause(self): # if authorized and playback was started by the skill if self.spotify: LOG.info('Pausing Sonos...') self.spotify.pause(self.dev_id) def pause(self, message=None): """ Handler for playback control pause. """ self.ducking = False self.__pause() def resume(self, message=None): """ Handler for playback control resume. """ # if authorized and playback was started by the skill if self.spotify: LOG.info('Resume Sonos') if not self.dev_id: self.dev_id = self.get_default_device() self.spotify_play(self.dev_id) def next_track(self, message): """ Handler for playback control next. """ # if authorized and playback was started by the skill if self.spotify and self.dev_id: LOG.info('Next Sonos track') self.spotify.next(self.dev_id) self.start_monitor() return True return False def prev_track(self, message): """ Handler for playback control prev. """ # if authorized and playback was started by the skill if self.spotify and self.dev_id: LOG.info('Previous Sonos track') self.spotify.prev(self.dev_id) self.start_monitor() @intent_handler(IntentBuilder('').require('Sonos').require('Device')) def list_devices(self, message): """ List available devices. """ if self.spotify: devices = [d['name'] for d in self.spotify.get_devices()] if len(devices) == 1: self.speak(devices[0]) elif len(devices) > 1: self.speak_dialog( 'AvailableDevices', { 'devices': ' '.join(devices[:-1]) + ' ' + self.translate('And') + ' ' + devices[-1] }) else: self.speak_dialog('NoDevicesAvailable') else: self.speak_dialog('NotAuthorized') @intent_handler( IntentBuilder('').require('Transfer').require('Sonos').require( 'ToDevice')) def transfer_playback(self, message): """ Move playback from one device to another. """ if self.spotify and self.spotify.is_playing(): dev = self.device_by_name(message.data['ToDevice']) if dev: self.spotify.transfer_playback(dev['id']) def stop(self): """ Stop playback. """ if self.spotify and self.spotify.is_playing(): dev = self.get_default_device() self.dev_id = dev['id'] if self.dev_id: self.pause(None) # Clear playing device id self.dev_id = None return True self.dev_id = None return False def stop_librespot(self): """ Send Terminate signal to librespot if it's running. """ if self.process and self.process.poll() is None: self.process.send_signal(signal.SIGTERM) self.process.communicate() # Communicate to remove zombie self.process = None def shutdown(self): """ Remove the monitor at shutdown. """ self.cancel_scheduled_event('SonosLogin') self.stop_monitor() self.stop_librespot() # Do normal shutdown procedure super(SonosSkill, self).shutdown()
class AudioRecordSkill(MycroftSkill): def __init__(self): super(AudioRecordSkill, self).__init__("AudioRecordSkill") self.play_process = None self.record_process = None self.start_time = 0 self.last_index = 24 # index of last pixel in countdowns self.settings["min_free_disk"] = 100 # min mb to leave free on disk self.settings["rate"] = 16000 # sample rate, hertz self.settings["channels"] = 1 # recording channels (1 = mono) self.settings["file_path"] = "/tmp/mycroft-recording.wav" self.settings["duration"] = -1 # default = unknown def remaining_time(self): return self.settings["duration"] - (now_local() - self.start_time).total_seconds() def has_free_disk_space(self): space = (self.remaining_time() * self.settings["channels"] * self.settings["rate"] / 1024 / 1024) free_mb = psutil.disk_usage('/')[2] / 1024 / 1024 return free_mb - space > self.settings["min_free_disk"] @staticmethod def stop_process(process): if process.poll() is None: # None means still running process.terminate() # No good reason to wait, plus it interferes with # how stop button on the Mark 1 operates. # process.wait() return True else: return False # Handle: "Delete recording" @intent_handler(IntentBuilder('').require('Delete').require('Recording')) def handle_delete(self, message): if not exists(self.settings["file_path"]): self.speak_dialog('audio.record.no.recording') else: try: os.remove(self.settings["file_path"]) self.speak_dialog('audio.record.removed') except: pass # Standard Stop handler def stop(self): if self.record_process: self.end_recording() return True if self.play_process: self.end_playback() return True return False # Show a countdown using the eyes def render_countdown(self, r_fore, g_fore, b_fore): display_owner = self.enclosure.display_manager.get_active() if display_owner == "": # Initialization, first time we take ownership self.enclosure.mouth_reset() # clear any leftover bits self.enclosure.eyes_color(r_fore, g_fore, b_fore) # foreground self.last_index = 24 if display_owner == "AudioRecordSkill": remaining_pct = self.remaining_time() / self.settings["duration"] fill_to_index = int(24 * remaining_pct) while self.last_index > fill_to_index: if self.last_index < 24 and self.last_index > -1: # fill background with gray self.enclosure.eyes_setpixel(self.last_index, 64, 64, 64) self.last_index -= 1 ###################################################################### # Recording @intent_file_handler('StartRecording.intent') def handle_record(self, message): utterance = message.data.get('utterance') # Calculate how long to record self.start_time = now_local() stop_time, _ = extract_datetime(utterance, lang=self.lang) self.settings["duration"] = (stop_time - self.start_time).total_seconds() if self.settings["duration"] <= 0: self.settings["duration"] = 60 # default recording duration # Throw away any previous recording try: os.remove(self.settings["file_path"]) except: pass if self.has_free_disk_space(): record_for = nice_duration(self, self.settings["duration"], lang=self.lang) self.speak_dialog('audio.record.start.duration', {'duration': record_for}) # Initiate recording wait_while_speaking() self.start_time = now_local() # recalc after speaking completes self.record_process = record(self.settings["file_path"], int(self.settings["duration"]), self.settings["rate"], self.settings["channels"]) self.enclosure.eyes_color(255, 0, 0) # set color red self.last_index = 24 self.schedule_repeating_event(self.recording_feedback, None, 1, name='RecordingFeedback') else: self.speak_dialog("audio.record.disk.full") def recording_feedback(self, message): if not self.record_process: self.end_recording() return # Show recording countdown self.render_countdown(255, 0, 0) # Verify there is still adequate disk space to continue recording if self.record_process.poll() is None: if not self.has_free_disk_space(): # Out of space self.end_recording() self.speak_dialog("audio.record.disk.full") else: # Recording ended for some reason self.end_recording() def end_recording(self): self.cancel_scheduled_event('RecordingFeedback') if self.record_process: # Stop recording self.stop_process(self.record_process) self.record_process = None # Calc actual recording duration self.settings["duration"] = (now_local() - self.start_time).total_seconds() # Reset eyes self.enclosure.eyes_color(34, 167, 240) # Mycroft blue self.bus.emit(Message('mycroft.eyes.default')) ###################################################################### # Playback @intent_file_handler('PlayRecording.intent') def handle_play(self, message): if exists(self.settings["file_path"]): # Initialize for playback self.start_time = now_local() # Playback the recording, with visual countdown self.play_process = play_wav(self.settings["file_path"]) self.enclosure.eyes_color(64, 255, 64) # set color greenish self.last_index = 24 self.schedule_repeating_event(self.playback_feedback, None, 1, name='PlaybackFeedback') else: self.speak_dialog('audio.record.no.recording') def playback_feedback(self, message): if not self.play_process or self.play_process.poll() is not None: self.end_playback() return if self.settings["duration"] > -1: # Show playback countdown self.render_countdown(64, 255, 64) # greenish color else: # unknown duration, can't display countdown pass def end_playback(self): self.cancel_scheduled_event('PlaybackFeedback') if self.play_process: self.stop_process(self.play_process) self.play_process = None # Reset eyes self.enclosure.eyes_color(34, 167, 240) # Mycroft blue self.bus.emit(Message('mycroft.eyes.default'))
class NoAgendaSkill(MycroftSkill): def __init__(self): super(NoAgendaSkill, self).__init__(name="NoAgendaSkill") self.process = None self.audioservice = None def initialize(self): if AudioService: self.audioservice = AudioService(self.emitter) @property def url_rss(self): pre_select = self.settings.get("pre_select", "") url_rss = self.settings.get("url_rss") if "not_set" in pre_select: # Use a custom RSS URL url_rss = self.settings.get("url_rss") else: # Use the selected preset's URL url_rss = pre_select if not url_rss and 'url_rss' in self.config: url_rss = self.config['url_rss'] return url_rss @intent_handler( IntentBuilder("anycollusion").require("anycollusion").build()) def handle_anycollusion_intent(self, message): try: self.stop() self.speak_dialog('NoAgenda') feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[0] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('anycollusion') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it sleep(1.0) wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("mymillenials").require("mymillenials").build()) def handle_mymillenials_intent(self, message): try: self.stop() self.speak_dialog('NoAgenda') feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[0] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('mymillenials') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it sleep(1.0) wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler(IntentBuilder("buildawall").require("buildawall").build()) def handle_buildawall_intent(self, message): try: self.stop() self.speak_dialog('NoAgenda') feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[0] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('buildawall') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it sleep(1.0) wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("resistwemuch").require("resistwemuch").build()) def handle_resistwemuch_intent(self, message): try: self.stop() self.speak_dialog('NoAgenda') feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[0] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('resistwemuch') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it sleep(1.0) wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler(IntentBuilder("needs").require("needs").build()) def handle_needs_intent(self, message): try: self.stop() self.speak_dialog('NoAgenda') feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[0] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('needs') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it sleep(1.0) wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("inthemorning").require("inthemorning").build()) def handle_inthemorning_intent(self, message): try: self.stop() self.speak_dialog('NoAgenda') feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[0] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('inthemorning') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it sleep(1.0) wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler(IntentBuilder("triggered").require("triggered").build()) def handle_triggered_intent(self, message): try: self.stop() self.speak_dialog('NoAgenda') feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[0] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('triggered') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it sleep(1.0) wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("penultimate").optionally("Play").require( "penultimate").require("NoAgenda").build()) def handle_penultimate_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[1] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('penultimate') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("third").optionally("Play").require("3rd").require( "NoAgenda").build()) def handle_third_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[2] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('third') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("fourth").optionally("Play").require("4th").require( "NoAgenda").build()) def handle_fourth_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[3] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('fourth') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("fifth").optionally("Play").require("5th").require( "NoAgenda").build()) def handle_fifth_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[4] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('fifth') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("sixth").optionally("Play").require("6th").require( "NoAgenda").build()) def handle_sixth_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[5] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('sixth') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("seventh").optionally("Play").require("7th").require( "NoAgenda").build()) def handle_seventh_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[6] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('seventh') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("eighth").optionally("Play").require("8th").require( "NoAgenda").build()) def handle_eighth_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[7] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('eighth') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("ninth").optionally("Play").require("9th").require( "NoAgenda").build()) def handle_ninth_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[8] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('ninth') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("tenth").optionally("Play").require("10th").require( "NoAgenda").build()) def handle_tenth_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[9] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('tenth') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("eleventh").optionally("Play").require("11th").require( "NoAgenda").build()) def handle_eleventh_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[10] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('eleventh') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("twelfth").optionally("Play").require("12th").require( "NoAgenda").build()) def handle_twelfth_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[11] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('twelfth') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("thirteenth").optionally("Play").require("13th").require( "NoAgenda").build()) def handle_thirteenth_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[12] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('thirteenth') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("fourteenth").optionally("Play").require("14th").require( "NoAgenda").build()) def handle_fourteenth_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[13] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('fourteenth') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("fifteenth").optionally("Play").require("15th").require( "NoAgenda").build()) def handle_fifteenth_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[14] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('fifteenth') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("live").require("Jack").require("Live").optionally( "NoAgenda").require("Stream").build()) def handle_live_intent(self, message): try: # Stop anything already playing self.stop() url = 'https://listen.noagendastream.com/noagenda.pls' LOG.info('live') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("query").require("query").optionally("latest").require( "NoAgenda").build()) def handle_query_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[0] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('query') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) def stop(self): if self.audioservice: self.audioservice.stop() else: if self.process and self.process.poll() is None: self.process.terminate() self.process.wait() @intent_handler( IntentBuilder("random").optionally("Play").optionally( "random").require("NoAgenda").build()) def handle_random_intent(self, message): try: self.stop() random_episode = random.randint(0, 14) feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[random_episode] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('random') LOG.info(random_episode) LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e)) @intent_handler( IntentBuilder("latest").optionally("Play").optionally( "latest").require("NoAgenda").build()) def handle_latest_intent(self, message): try: self.stop() feeddata = feedparser.parse(self.url_rss) data = feeddata.entries[0] # Stop anything already playing url = data.enclosures[0]['url'] LOG.info('latest') LOG.info(url) # After the intro, start the no agenda stream # if audio service module is available use it wait_while_speaking() if self.audioservice: LOG.info('AudioService') self.audioservice.play(url, message.data['utterance']) else: # othervice use normal mp3 playback LOG.info('playmp3') self.process = play_mp3(url) except Exception as e: LOG.error("Error: {0}".format(e))
from adapt.intent import IntentBuilder from adapt.engine import IntentDeterminationEngine engine = IntentDeterminationEngine() schema = json.loads(sys.argv[1]) for entity in schema["entities"]: if entity["type"] == "string": for value in entity["values"]: engine.register_entity(value, entity["name"]) elif entity["type"] == "regex": engine.register_regex_entity(entity["pattern"]) for intent in schema["intents"]: ib = IntentBuilder(intent["name"].encode("utf-8")) for requirement in intent["requirements"]: ib.require(requirement["entity"], requirement["attribute"]) for optional in intent["optionals"]: ib.optionally(optional["entity"], optional["attribute"]) engine.register_intent_parser(ib.build()) if __name__ == "__main__": while True: line = sys.stdin.readline() query = json.loads(line) intents = list(engine.determine_intent(query["input"])) response = {"intents": intents} print(json.dumps(response)) sys.stdout.flush()