Add fuzzy and text-classification light model #1
					 11 changed files with 353 additions and 84 deletions
				
			
		
							
								
								
									
										18
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								README.md
									
										
									
									
									
								
							|  | @ -1,3 +1,19 @@ | |||
| # ratatouille | ||||
| 
 | ||||
| Projet ratatouille: interface tampon entre Rhasspy, Hass, Mpd, Bookstack, Systemd et autre | ||||
| Projet ratatouille: interface tampon entre Rhasspy, Hass, Mpd, Bookstack, Systemd et autre. | ||||
| 
 | ||||
| To work with models (intent.py), transformers and pytorch is needed. | ||||
| 
 | ||||
| See https://pytorch.org/get-started/locally/ | ||||
| 
 | ||||
| ## Run | ||||
| 
 | ||||
| ``` | ||||
| python main.py server -i 127.0.0.1 -p 7777 --mpd 10.10.10.10 | ||||
| ``` | ||||
| 
 | ||||
| ou | ||||
| 
 | ||||
| ``` | ||||
| python main.py prompt | ||||
| ``` | ||||
|  |  | |||
							
								
								
									
										12
									
								
								await.sh
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										12
									
								
								await.sh
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| while [[ 1 == 1 ]]; do | ||||
| 	/bin/sleep 10 | ||||
| 	code=$(curl -o /dev/null -s -w "%{http_code}\n" 10.10.10.8) | ||||
| 	echo "10.10.10.8 return code $code" | ||||
| 
 | ||||
| 	if [ $code = '200' ]; then  | ||||
| 		exit 0;  | ||||
| 	else  | ||||
| 		echo "retrying in 10s";  | ||||
| 	fi | ||||
| done | ||||
| 
 | ||||
							
								
								
									
										85
									
								
								main.py
									
										
									
									
									
								
							
							
						
						
									
										85
									
								
								main.py
									
										
									
									
									
								
							|  | @ -1,37 +1,92 @@ | |||
| import logging | ||||
| import time | ||||
| import schedule | ||||
| import argparse | ||||
| 
 | ||||
| # import schedule | ||||
| 
 | ||||
| import config | ||||
| 
 | ||||
| from src.rhasspy import rhasspy_mqtt as yoda_listener | ||||
| from src.rhasspy import Rhasspy | ||||
| from src.ratatouille import Ratatouille | ||||
| from src.mpd import Mpd | ||||
| from src.hass import Hass | ||||
| from src.httpServer import get_server | ||||
| from src.intent import AlexaIntent | ||||
| from src.fuzzy import fuzz_predict | ||||
| from src.intent import BertIntent | ||||
| from src.tools.simple_sentence_corrector import simple_sentence_corrector | ||||
| 
 | ||||
| 
 | ||||
| # --------- setup args ------------- | ||||
| 
 | ||||
| parser = argparse.ArgumentParser( | ||||
|     prog='Ratatouille', | ||||
|     description='Ratatouille le cerveau domotique !') | ||||
| 
 | ||||
| parser.add_argument('mode') | ||||
| parser.add_argument('-i', '--ip', required=False) | ||||
| parser.add_argument('-p', '--port', required=False, type=int) | ||||
| parser.add_argument('-m', '--mpd', required=False) | ||||
| 
 | ||||
| args = parser.parse_args() | ||||
| 
 | ||||
| if args.mode == "server": | ||||
|     if args.ip is None or args.port is None: | ||||
|         logging.error(" --ip or --port argument missing") | ||||
|         exit() | ||||
| 
 | ||||
| 
 | ||||
| # -------- setup logging ------------ | ||||
| 
 | ||||
| logging.basicConfig( | ||||
|     level=10, | ||||
|     format="%(asctime)s %(filename)s:%(lineno)s %(levelname)s %(message)s" | ||||
| ) | ||||
| 
 | ||||
| IP = "10.10.10.11" | ||||
| PORT = 5555 | ||||
| logging.info("Loading ratatouilles modules") | ||||
| 
 | ||||
| 
 | ||||
| # ---------- other --------------- | ||||
| 
 | ||||
| walle = Hass(config.hass_url, config.hass_token) | ||||
| yoda = None #Rhasspy(config.rhasspy_url) | ||||
| mopidy = Mpd('10.10.10.10', yoda) | ||||
| ratatouille = Ratatouille(yoda, walle, mopidy, schedule) | ||||
| alexa = AlexaIntent() # we are not doing any request to the evil amazon but we are using one of its dataset | ||||
| yoda = None  # Rhasspy(config.rhasspy_url) | ||||
| 
 | ||||
| mopidy = None | ||||
| 
 | ||||
| if args.mpd is not None: | ||||
|     mopidy = Mpd(args.mpd) | ||||
| else: | ||||
|     logging.warning('Starting without MPD connection') | ||||
| 
 | ||||
| ratatouille = Ratatouille(yoda, walle, mopidy, None) | ||||
| # alexa = AlexaIntent() # we are not doing any request to the evil amazon but we are using one of its dataset | ||||
| bert = BertIntent() | ||||
| 
 | ||||
| 
 | ||||
| def answer(sentence): | ||||
|     return ratatouille.parseAlexa(alexa.predict(sentence)) | ||||
|     #return "42" | ||||
|     # return ratatouille.parse_alexa(alexa.predict(sentence)) | ||||
|     sentence_corrected = simple_sentence_corrector(sentence) | ||||
|     prediction = bert.predict(sentence_corrected) | ||||
|     return ratatouille.parse_fuzzy(prediction) | ||||
|     # return "42" | ||||
| 
 | ||||
| server = get_server(IP,PORT,answer) | ||||
| 
 | ||||
| logging.info('Running server on '+IP+':'+str(PORT)) | ||||
| server.serve_forever() | ||||
| def run_server(ip, port): | ||||
|     server = get_server(ip, port, answer) | ||||
|     logging.info('Running server on '+ip+':'+str(port)) | ||||
|     server.serve_forever() | ||||
| 
 | ||||
| 
 | ||||
| def run_prompt(): | ||||
|     question = "empty" | ||||
|     while question != "stop": | ||||
|         question = input("?") | ||||
|         if question != "stop": | ||||
|             print(answer(question)) | ||||
| 
 | ||||
| 
 | ||||
| logging.info("Ratatouille is ready !") | ||||
| 
 | ||||
| # run_server() | ||||
| if args.mode == "server": | ||||
|     run_server(str(args.ip), args.port) | ||||
| else: | ||||
|     run_prompt() | ||||
|  |  | |||
|  | @ -2,5 +2,6 @@ paho-mqtt<1.6 | |||
| requests<2.26 | ||||
| schedule<1.1.0 | ||||
| python-mpd2<3.1 | ||||
| transformers<4.28.0 | ||||
| torch<2.1.0 | ||||
| #transformers<4.28.0 | ||||
| #torch<2.1.0 | ||||
| rapidfuzz<4.0.0 | ||||
							
								
								
									
										69
									
								
								src/fuzzy.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/fuzzy.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | |||
| from rapidfuzz import process, fuzz, utils | ||||
| 
 | ||||
| 
 | ||||
| ROOMS = ["cuisine", "salon", "chambre", "bureau"] | ||||
| 
 | ||||
| 
 | ||||
| MUSIQUE_GENRE_SHORT = ["synthpop", "jazz classique", "jazz manouche", "latine", "classique", "rock", "jazz", | ||||
|                        "blues", "film", "francaise", "pop",  "reggae", "folk", | ||||
|                        "électro", "punk", "corse", "arabe", "persane",  "piano", "rap", "slam"] | ||||
| 
 | ||||
| MUSIQUE_GENRE_LONG = ["synthpop", "jazz classique", "jazz manouche", "chanson latine", "classique", "rock", "jazz", | ||||
|                       "blues", "musique de film", "chanson francaise", "pop",  "reggae", "folk", | ||||
|                       "électro", "punk", "chanson corse", "chanson arabe", "chanson persane",  "piano", "rap", "slam", "chanson classique"] | ||||
| 
 | ||||
| 
 | ||||
| def compute_sentences_lamp_on(): | ||||
|     return ["allume la lumière de la" + room for room in ROOMS] + ["allume la" + room for room in ROOMS] | ||||
| 
 | ||||
| 
 | ||||
| def compute_sentences_lamp_off(): | ||||
|     return ["éteins la lumière de la" + room for room in ROOMS] + ["éteins la" + room for room in ROOMS] | ||||
| 
 | ||||
| 
 | ||||
| def compute_sentences_musique_genre_long(): | ||||
|     return ["met du" + genre for genre in MUSIQUE_GENRE_LONG] | ||||
| 
 | ||||
| 
 | ||||
| def compute_sentences_musique_genre_short(): | ||||
|     return ["met de la musique" + genre for genre in MUSIQUE_GENRE_SHORT] | ||||
| 
 | ||||
| 
 | ||||
| SENTENCES_HOUR = ["quel heure est-il ?"] | ||||
| SENTENCES_LAMP_ON = compute_sentences_lamp_on() | ||||
| SENTENCES_LAMP_OFF = compute_sentences_lamp_off() | ||||
| SENTENCES_MUSIQUE_GENRE_LONG = compute_sentences_musique_genre_long() | ||||
| SENTENCES_MUSIQUE_GENRE_SHORT = compute_sentences_musique_genre_short() | ||||
| 
 | ||||
| 
 | ||||
| def fuzz_predict(text): | ||||
|     choices = SENTENCES_HOUR + SENTENCES_LAMP_ON + SENTENCES_LAMP_OFF + \ | ||||
|         SENTENCES_MUSIQUE_GENRE_LONG + SENTENCES_MUSIQUE_GENRE_SHORT | ||||
|     result = process.extractOne( | ||||
|         text, choices, scorer=fuzz.WRatio, processor=utils.default_process) | ||||
| 
 | ||||
|     choosen_sentence = result[0] | ||||
|     if choosen_sentence in SENTENCES_HOUR: | ||||
|         return {'intentName': 'GetTime'} | ||||
| 
 | ||||
|     if choosen_sentence in SENTENCES_LAMP_ON: | ||||
|         return {'intentName': 'LightOn', 'intentArg': [find_matching(ROOMS, text)]} | ||||
| 
 | ||||
|     if choosen_sentence in SENTENCES_LAMP_OFF: | ||||
|         return {'intentName': 'LightOff', 'intentArg': [find_matching(ROOMS, text)]} | ||||
| 
 | ||||
|     if choosen_sentence in SENTENCES_MUSIQUE_GENRE_LONG: | ||||
|         return {'intentName': 'PlayMusicGenre', 'intentArg': [find_matching(MUSIQUE_GENRE_SHORT, text)]} | ||||
| 
 | ||||
|     if choosen_sentence in SENTENCES_MUSIQUE_GENRE_SHORT: | ||||
|         return {'intentName': 'PlayMusicGenre', 'intentArg': [find_matching(MUSIQUE_GENRE_SHORT, text)]} | ||||
| 
 | ||||
|     return {'intentName': 'Unknown'} | ||||
| 
 | ||||
| 
 | ||||
| def find_matching(list_str, text): | ||||
|     for search in list_str: | ||||
|         if search in text: | ||||
|             return search | ||||
| 
 | ||||
|     return None | ||||
|  | @ -2,24 +2,24 @@ import http.server | |||
| import json | ||||
| import logging | ||||
| 
 | ||||
| 
 | ||||
| def get_server(ip, port, answer_function): | ||||
|     class Server(http.server.BaseHTTPRequestHandler): | ||||
|      | ||||
| 
 | ||||
|         def do_POST(self): | ||||
|             length = int(self.headers.get('content-length')) | ||||
|             field_data = self.rfile.read(length) | ||||
|             fields = json.loads(field_data) | ||||
|             text = fields['text'].strip() | ||||
|              | ||||
| 
 | ||||
|             res = answer_function(text) | ||||
| 
 | ||||
|             logging.info('Get request:' + text) | ||||
|             print(text) | ||||
|             self.send_response(200) | ||||
|             self.send_header('Content-type','text/plain') | ||||
|             self.send_header('Content-type', 'text/plain; charset=utf-8') | ||||
|             self.end_headers() | ||||
| 
 | ||||
|              | ||||
|             self.wfile.write(res.encode()) | ||||
| 
 | ||||
|     return http.server.HTTPServer((ip, port), Server) | ||||
|     return http.server.HTTPServer((ip, port), Server) | ||||
|  |  | |||
|  | @ -1,12 +1,14 @@ | |||
| import unittest | ||||
| from transformers import AutoTokenizer, AutoModelForTokenClassification, TokenClassificationPipeline | ||||
| from transformers import AutoModelForSequenceClassification, TextClassificationPipeline | ||||
| from transformers import pipeline | ||||
| 
 | ||||
| DOMOTIQUE_OBJ_ON = ['iot_wemo_on'] | ||||
| DOMOTIQUE_OBJ_OFF = ['iot_wemo_off'] | ||||
| 
 | ||||
| 
 | ||||
| class AlexaIntent(): | ||||
|      | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         self.get_intents = self.init_intent_classification() | ||||
|         self.get_entities = self.init_entities_classification() | ||||
|  | @ -15,21 +17,24 @@ class AlexaIntent(): | |||
|         model_name = 'qanastek/XLMRoberta-Alexa-Intents-Classification' | ||||
|         tokenizer = AutoTokenizer.from_pretrained(model_name) | ||||
|         model = AutoModelForSequenceClassification.from_pretrained(model_name) | ||||
|         classifier = TextClassificationPipeline(model=model, tokenizer=tokenizer) | ||||
|         classifier = TextClassificationPipeline( | ||||
|             model=model, tokenizer=tokenizer) | ||||
|         return classifier | ||||
| 
 | ||||
|     def init_entities_classification(self): | ||||
|         tokenizer = AutoTokenizer.from_pretrained('qanastek/XLMRoberta-Alexa-Intents-NER-NLU') | ||||
|         model = AutoModelForTokenClassification.from_pretrained('qanastek/XLMRoberta-Alexa-Intents-NER-NLU') | ||||
|         tokenizer = AutoTokenizer.from_pretrained( | ||||
|             'qanastek/XLMRoberta-Alexa-Intents-NER-NLU') | ||||
|         model = AutoModelForTokenClassification.from_pretrained( | ||||
|             'qanastek/XLMRoberta-Alexa-Intents-NER-NLU') | ||||
|         predict = TokenClassificationPipeline(model=model, tokenizer=tokenizer) | ||||
|         return predict | ||||
| 
 | ||||
|     def simple_sentence_corrector(self,sentence):     | ||||
|         sentence = sentence.replace('étant','éteins') | ||||
|         sentence = sentence.replace('dépeint','éteins') | ||||
|     def simple_sentence_corrector(self, sentence): | ||||
|         sentence = sentence.replace('étant', 'éteins') | ||||
|         sentence = sentence.replace('dépeint', 'éteins') | ||||
|         return sentence | ||||
| 
 | ||||
|     def intent_corrector(self,intents): | ||||
|     def intent_corrector(self, intents): | ||||
|         for intent in intents: | ||||
|             if intent['label'] in DOMOTIQUE_OBJ_ON: | ||||
|                 intent['label'] = 'iot_hue_lighton' | ||||
|  | @ -37,7 +42,7 @@ class AlexaIntent(): | |||
|                 intent['label'] = 'iot_hue_lightoff' | ||||
|         return intents | ||||
| 
 | ||||
|     def predict(self,sentence): | ||||
|     def predict(self, sentence): | ||||
|         sentence = self.simple_sentence_corrector(sentence) | ||||
|         return { | ||||
|             'intents': self.intent_corrector(self.get_intents(sentence)), | ||||
|  | @ -45,6 +50,47 @@ class AlexaIntent(): | |||
|         } | ||||
| 
 | ||||
| 
 | ||||
| class BertIntent(): | ||||
|     def __init__(self): | ||||
|         self.classifier = pipeline("text-classification", | ||||
|                                    model="Tjiho/french-intents-classificaton") | ||||
| 
 | ||||
|     def predict(self, sentence): | ||||
|         # sentence = self.simple_sentence_corrector(sentence) | ||||
|         classification = self.classifier(sentence) | ||||
| 
 | ||||
|         if classification[0]["score"] < 0.7:  # score too low | ||||
|             return {'intentName': ''} | ||||
| 
 | ||||
|         label = classification[0]["label"] | ||||
| 
 | ||||
|         if label == "HEURE": | ||||
|             return {'intentName': 'GetTime'} | ||||
|         elif label == "DATE": | ||||
|             return {'intentName': 'GetDate'} | ||||
|         elif label == "ETEINDRE_CUISINE": | ||||
|             return {'intentName': 'LightOff', 'intentArg': ['cuisine']} | ||||
|         elif label == "ETEINDRE_BUREAU": | ||||
|             return {'intentName': 'LightOff', 'intentArg': ['bureau']} | ||||
|         elif label == "ETEINDRE_SALON": | ||||
|             return {'intentName': 'LightOff', 'intentArg': ['salon']} | ||||
|         elif label == "ETEINDRE_CHAMBRE": | ||||
|             return {'intentName': 'LightOff', 'intentArg': ['chambre']} | ||||
|         elif label == "ALLUMER_CUISINE": | ||||
|             return {'intentName': 'LightOn', 'intentArg': ['cuisine']} | ||||
|         elif label == "ALLUMER_SALON": | ||||
|             return {'intentName': 'LightOn', 'intentArg': ['salon']} | ||||
|         elif label == "ALLUMER_BUREAU": | ||||
|             return {'intentName': 'LightOn', 'intentArg': ['bureau']} | ||||
|         elif label == "ALLUMER_CHAMBRE": | ||||
|             return {'intentName': 'LightOn', 'intentArg': ['chambre']} | ||||
|         elif label == "METEO": | ||||
|             return {'intentName': 'Meteo'} | ||||
|         elif label == "TEMPERATURE_EXTERIEUR": | ||||
|             return {'intentName': 'Temperature_ext'} | ||||
|         elif label == "TEMPERATURE_INTERIEUR": | ||||
|             return {'intentName': 'Temperature_int'} | ||||
| 
 | ||||
| 
 | ||||
| class TestAlexa(unittest.TestCase): | ||||
|     @classmethod | ||||
|  | @ -60,11 +106,11 @@ class TestAlexa(unittest.TestCase): | |||
|         self.assertEqual(res['intents'][0]['label'], 'iot_hue_lighton') | ||||
|         self.assertEqual(res['entities'][0]['word'], '▁cuisine') | ||||
| 
 | ||||
| 
 | ||||
|     def test_bad_transcribe(self): | ||||
|         res = self.alexa.predict("dépeint la cuisine") | ||||
|         self.assertEqual(res['intents'][0]['label'], 'iot_hue_lightoff') | ||||
|         self.assertEqual(res['entities'][0]['word'], '▁cuisine') | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
|     unittest.main() | ||||
|  |  | |||
							
								
								
									
										78
									
								
								src/mpd.py
									
										
									
									
									
								
							
							
						
						
									
										78
									
								
								src/mpd.py
									
										
									
									
									
								
							|  | @ -2,16 +2,16 @@ import logging | |||
| import random | ||||
| from mpd import MPDClient | ||||
| 
 | ||||
| 
 | ||||
| class Mpd(): | ||||
| 
 | ||||
|     def __init__(self,ip, yoda): | ||||
|     def __init__(self, ip): | ||||
|         self.ip = ip | ||||
|         self.port = 6600 | ||||
|         self.client = MPDClient()               # create client object | ||||
|         self.client.timeout = 10                # network timeout in seconds (floats allowed), default: None | ||||
|         # network timeout in seconds (floats allowed), default: None | ||||
|         self.client.timeout = 10 | ||||
|         self.client.idletimeout = None | ||||
|         self.yoda = yoda | ||||
| 
 | ||||
| 
 | ||||
|         self.client.connect(ip, self.port) | ||||
|         logging.debug(self.client.mpd_version) | ||||
|  | @ -32,46 +32,66 @@ class Mpd(): | |||
|         self.client.disconnect() | ||||
| 
 | ||||
|     def play_genre(self, genre): | ||||
|         res = "" | ||||
|         self.client.connect(self.ip, self.port) | ||||
|         logging.debug('Listing '+self.normalize_genre(genre)) | ||||
|         list_album = [] | ||||
|         try: | ||||
|             # todo: select only one album instead of the artist | ||||
|             list_album = self.client.lsinfo("Files/Genres/"+self.normalize_genre(genre)) | ||||
|             list_album = self.client.lsinfo( | ||||
|                 "Subsonic/Genre/"+self.normalize_genre(genre)) | ||||
| 
 | ||||
|             if (list_album): | ||||
|                 random_album = random.choice(list_album)["directory"] | ||||
|                 self.play_album(random_album) | ||||
|                 res = "C'est parti !" | ||||
|             else: | ||||
|                 res = "Je n\'ai rien trouvé." | ||||
|         except: | ||||
|             list_album = [] | ||||
|             res = "Il y a eu une erreur durant le lancement de la musique" | ||||
|         finally: | ||||
|             self.client.close() | ||||
|             self.client.disconnect() | ||||
| 
 | ||||
|         if(list_album): | ||||
|             random_album = random.choice(list_album)["directory"] | ||||
|             self.play_album(random_album) | ||||
|         else: | ||||
|             self.yoda.say("Je n\'ai rien trouvé") | ||||
|         return res | ||||
| 
 | ||||
|         self.client.close() | ||||
|         self.client.disconnect() | ||||
| 
 | ||||
|     def play_album(self,directory): | ||||
|     def play_album(self, directory): | ||||
|         self.client.stop() | ||||
|         self.yoda.say("Lancement de "+directory.split('/')[-1]) | ||||
|         # self.yoda.say("Lancement de "+directory.split('/')[-1]) | ||||
|         logging.debug('Playing '+directory) | ||||
|         self.client.clear() | ||||
|         self.client.add(directory) | ||||
|         self.client.play(0) | ||||
| 
 | ||||
|     def normalize_genre(self,genre): | ||||
|     def normalize_genre(self, genre): | ||||
|         return NORMALIZED_GENRE[genre.lower()] | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| NORMALIZED_GENRE = { | ||||
|     'classique': 'classique', | ||||
|     'musique classique': 'classique', | ||||
|     'jazz': 'jazz', | ||||
|     'chanson française': 'chanson francaise', | ||||
|     'chanson anglaise': 'chanson anglaise', | ||||
|     'musique de film': 'musique de film', | ||||
|     'électro': 'electro', | ||||
|     'musique électronique': 'electro', | ||||
|     'rock': 'rock', | ||||
|     'pop': 'pop', | ||||
|     'chanson latine': 'chanson latine', | ||||
|     'classique': 'Classique', | ||||
|     'musique classique': 'Classique', | ||||
|     'jazz': 'Jazz', | ||||
|     'chanson française': 'Chanson Francaise', | ||||
|     'francaise': 'Chanson Francaise', | ||||
|     'chanson anglaise': 'Chanson anglaise', | ||||
|     'reggae': 'Reggae', | ||||
|     'folk': 'Folk', | ||||
|     'électro': 'Electro', | ||||
|     'musique électronique': 'Electro', | ||||
|     'punk': 'Punk', | ||||
|     'rock': 'Rock', | ||||
|     'pop': 'Pop', | ||||
|     'latine': 'Latin', | ||||
|     'arabe': 'Arabic', | ||||
|     'corse': 'Chants Corse', | ||||
|     'persane': 'Chanson Persane', | ||||
|     'piano': 'Piano', | ||||
|     'rap': 'Rap', | ||||
|     'slam': 'Slam', | ||||
|     'synthpop': 'Synthpop', | ||||
|     'jazz classique': 'Classique Jazz', | ||||
|     'jazz manouche': 'Jazz Manouche', | ||||
|     'blues': 'Blues', | ||||
|     'film': 'Soundtrack', | ||||
|     'musique de film': 'Soundtrack', | ||||
| } | ||||
|  |  | |||
|  | @ -11,9 +11,10 @@ from src.tools.parse_entities import parse_entities | |||
| 
 | ||||
| from src.const.temperature_keyword import TEMPERATURE_KEYWORD | ||||
| 
 | ||||
| 
 | ||||
| class Ratatouille(): | ||||
| 
 | ||||
|     def __init__(self,yoda, walle, mopidy, schedule): | ||||
|     def __init__(self, yoda, walle, mopidy, schedule): | ||||
|         self.yoda = yoda | ||||
|         self.walle = walle | ||||
|         self.mopidy = mopidy | ||||
|  | @ -22,9 +23,9 @@ class Ratatouille(): | |||
|         # schedule.every().day.at(config.hibernate_time).do(self.hibernate) | ||||
|         # schedule.every().day.at(config.wakeup_time).do(self.clear_hibernate) | ||||
|         # yoda.say('Ratatouille a bien démmaré') | ||||
|         logging.info('loaded') | ||||
|         # logging.info('loaded') | ||||
| 
 | ||||
|     def parse_rhasspy_command(self,payload): | ||||
|     def parse_rhasspy_command(self, payload): | ||||
|         command = payload['intent']['intentName'] | ||||
|         print(command) | ||||
|         response = '' | ||||
|  | @ -40,11 +41,11 @@ class Ratatouille(): | |||
|             response = self.pause_music() | ||||
|         elif command == 'StartMusic': | ||||
|             response = self.start_music() | ||||
|          | ||||
| 
 | ||||
|         self.yoda.say(response) | ||||
|         return | ||||
| 
 | ||||
|     def parseAlexa(self,payload): | ||||
|     def parse_alexa(self, payload): | ||||
|         print(payload) | ||||
|         intent = payload['intents'][0]['label'] | ||||
|         entities = payload['entities'] | ||||
|  | @ -59,12 +60,26 @@ class Ratatouille(): | |||
|             return self.weather_query(parse_entities(entities)) | ||||
|         return '42' | ||||
| 
 | ||||
|     def weather_query(self,entities): | ||||
|     def parse_fuzzy(self, payload): | ||||
|         command = payload['intentName'] | ||||
|         if command == 'GetTime': | ||||
|             return self.send_hour() | ||||
|         elif command == 'GetDate': | ||||
|             return self.send_date() | ||||
|         elif command == "LightOn": | ||||
|             return self.light_on_single(payload['intentArg'][0]) | ||||
|         elif command == "LightOff": | ||||
|             return self.light_off_single(payload['intentArg'][0]) | ||||
|         elif command == "PlayMusicGenre": | ||||
|             return self.mopidy.play_genre(payload['intentArg'][0]) | ||||
|         return '42' | ||||
| 
 | ||||
|     def weather_query(self, entities): | ||||
|         if any(a in entities['B-weather_descriptor'] for a in TEMPERATURE_KEYWORD): | ||||
|             res = self.send_temperature() | ||||
|             return res | ||||
| 
 | ||||
|     def play_genre(self,genre): | ||||
|     def play_genre(self, genre): | ||||
|         try: | ||||
|             self.mopidy.play_genre(genre) | ||||
|         except Exception as e: | ||||
|  | @ -99,10 +114,31 @@ class Ratatouille(): | |||
|     def send_temperature(self): | ||||
|         logging.info('Send temperature') | ||||
|         data = self.walle.get('weather.toulouse') | ||||
|         temperature = str(data['attributes']['temperature']).replace('.',' virgule ') | ||||
|         temperature = str(data['attributes']['temperature'] | ||||
|                           ).replace('.', ' virgule ') | ||||
|         return 'il fait '+temperature+' degrés' | ||||
| 
 | ||||
|     def light_off(self,entities): | ||||
|     def light_off_single(self, lamp): | ||||
|         try: | ||||
|             self.walle.light_off(lamp) | ||||
|         except Exception as e: | ||||
|             logging.warning("Error light off:") | ||||
|             logging.warning(e) | ||||
|             return "J'ai pas pu éteindre la lampe '" + lamp + "'." | ||||
| 
 | ||||
|         return "J'ai éteint la lampe '" + lamp + "'." | ||||
| 
 | ||||
|     def light_on_single(self, lamp): | ||||
|         try: | ||||
|             self.walle.light_on(lamp) | ||||
|         except Exception as e: | ||||
|             logging.warning("Error light on:") | ||||
|             logging.warning(e) | ||||
|             return "J'ai pas pu allumer la lampe '" + lamp + "'." | ||||
| 
 | ||||
|         return "J'ai allumé la lampe '" + lamp + "'." | ||||
| 
 | ||||
|     def light_off(self, entities): | ||||
|         number_error = 0 | ||||
|         for lamp in entities: | ||||
|             try: | ||||
|  | @ -115,9 +151,9 @@ class Ratatouille(): | |||
|         if number_error == 0: | ||||
|             return 'Et voila !' | ||||
|         else: | ||||
|             return 'J\'ai pas pu eteindre ' + int_to_str(number_error,'f') + ' lampes' | ||||
|             return 'J\'ai pas pu eteindre ' + int_to_str(number_error, 'f') + ' lampes' | ||||
| 
 | ||||
|     def light_on(self,entities): | ||||
|     def light_on(self, entities): | ||||
|         number_error = 0 | ||||
|         for lamp in entities: | ||||
|             try: | ||||
|  | @ -130,19 +166,19 @@ class Ratatouille(): | |||
|         if number_error == 0: | ||||
|             return 'Et voila !' | ||||
|         else: | ||||
|             return 'J\'ai pas pu allumer ' + int_to_str(number_error,'f') + ' lampes' | ||||
| 
 | ||||
|             return 'J\'ai pas pu allumer ' + int_to_str(number_error, 'f') + ' lampes' | ||||
| 
 | ||||
|     # -- -- hibernate -- -- | ||||
| 
 | ||||
|     def can_hibernate(self): | ||||
|         lamp_desk = self.walle.get('switch.prise_bureau_switch') | ||||
|         if lamp_desk: | ||||
|             desk_is_on = lamp_desk['attributes'].get('state','OFF') == 'ON' | ||||
|             desk_is_on = lamp_desk['attributes'].get('state', 'OFF') == 'ON' | ||||
| 
 | ||||
|         lamp_bathroom = self.walle.get('light.chihiro_chihiro') | ||||
|         if lamp_bathroom: | ||||
|             bathroom_is_on = lamp_bathroom.get('state','OFF') == 'on' | ||||
|         #bathroom_is_on = self.walle.get('switch.prise_bureau_switch')['attributes']['state'] == 'ON' | ||||
|             bathroom_is_on = lamp_bathroom.get('state', 'OFF') == 'on' | ||||
|         # bathroom_is_on = self.walle.get('switch.prise_bureau_switch')['attributes']['state'] == 'ON' | ||||
|         return lamp_desk and lamp_bathroom and not desk_is_on and not bathroom_is_on | ||||
| 
 | ||||
|     def hibernate(self): | ||||
|  | @ -153,11 +189,13 @@ class Ratatouille(): | |||
|             time.sleep(5) | ||||
|         else: | ||||
|             self.schedule.clear('hourly-hibernate') | ||||
|             self.schedule.every(3).minutes.do(self.hibernate).tag('hourly-hibernate') | ||||
|             self.schedule.every(3).minutes.do( | ||||
|                 self.hibernate).tag('hourly-hibernate') | ||||
| 
 | ||||
|             logging.info('retry to hibernate in 3 minutes') | ||||
| 
 | ||||
|         return | ||||
| 
 | ||||
|     def clear_hibernate(self): | ||||
|         self.schedule.clear('hourly-hibernate') | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,10 @@ | |||
| from src.tools.str import int_to_str | ||||
| 
 | ||||
| MONTHS = ['janvier','février','mars','avril','mai','juin','juillet','aout','septembre','octobre','novembre','decembre'] | ||||
| WEEKDAY = ['lundi','mardi','mercredi','jeudi','vendredi','samedi','dimanche'] | ||||
| MONTHS = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', | ||||
|           'juillet', 'aout', 'septembre', 'octobre', 'novembre', 'decembre'] | ||||
| WEEKDAY = ['lundi', 'mardi', 'mercredi', | ||||
|            'jeudi', 'vendredi', 'samedi', 'dimanche'] | ||||
| 
 | ||||
| 
 | ||||
| def get_time(date): | ||||
|     hour = date.hour | ||||
|  | @ -13,18 +16,20 @@ def get_time(date): | |||
|     elif minute == 45: | ||||
|         return 'il est '+format_hour(hour+1)+' moins le quart' | ||||
|     elif minute == 15: | ||||
|         return 'il est '+format_hour(hour+1)+' et quart' | ||||
|         return 'il est '+format_hour(hour)+' et quart' | ||||
|     else: | ||||
|         return 'il est '+format_hour(hour)+' '+str(minute) | ||||
|     return | ||||
| 
 | ||||
| 
 | ||||
| def get_date(date): | ||||
|     week_day = date.weekday() | ||||
|     day = date.day | ||||
|     month = date.month | ||||
|     year = date.year | ||||
|     return 'Nous somme le '+format_weekday(week_day)+' '+str(day)+' '+format_month(month)+' '+str(year) | ||||
|     #self.yoda.say('Nous somme le '+str(week_day)+' '+str(day)+' '+str(month)+' '+str(year)) | ||||
|     # self.yoda.say('Nous somme le '+str(week_day)+' '+str(day)+' '+str(month)+' '+str(year)) | ||||
| 
 | ||||
| 
 | ||||
| def format_hour(hour): | ||||
|     if hour == 12: | ||||
|  | @ -34,8 +39,10 @@ def format_hour(hour): | |||
| 
 | ||||
|     return int_to_str(hour, 'f') + ' heure' | ||||
| 
 | ||||
| 
 | ||||
| def format_month(month): | ||||
|     return MONTHS[month - 1] | ||||
| 
 | ||||
| 
 | ||||
| def format_weekday(week_day): | ||||
|     return WEEKDAY[week_day] | ||||
|  |  | |||
							
								
								
									
										5
									
								
								src/tools/simple_sentence_corrector.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/tools/simple_sentence_corrector.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| def simple_sentence_corrector(sentence): | ||||
|     sentence = sentence.replace('étant', 'éteins') | ||||
|     sentence = sentence.replace('dépeint', 'éteins') | ||||
|     sentence = sentence.replace('mais', 'mets') | ||||
|     return sentence | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue