# Модуль машины состояний (конечный автомат) import speaker # Для воспроизведения import listener # Для распознавание речи import datetime # Для времени from pyowm import OWM # Использование OpenWeatherMap для получения данных о погоде (pip install pyowm) import random import traceback # вывод traceback без остановки работы программы при отлове исключений machine = None # Конечный автомат current_state = None # Текущее состояние weather_api_key = None # Ключ для OpenWeatherMap myName = None # Имя ассистента def init(assistant_name): """Инициализирует конечный автомат""" global machine global current_state global weather_api_key global myName listener.init_listener() # Инициализация распозавания myName = assistant_name with open("weatherApiKey.txt", "r") as key: weather_api_key = key.read() current_state = "Начало" machine = { "Начало":{ assistant_name.lower(): switchToListen }, "Слушаю задание":{ "время": switchToTime, "погода": switchToWeather, "привет": switchToHello, "здравствуй": switchToHello, "доброе утро": switchToHello, "добрый день": switchToHello, "добрый вечер": switchToHello, "доброй ночи": switchToHello, "пока": switchToBye, "до встречи": switchToBye, "перезапуск": switchToReboot, "завершение работы": turnOff }, } switchToHello() speaker.speak(f"Ассистент {myName} начинает работу") def work(): """Обрабатывает конечный автомат""" global current_state while(True): #word = input().lower() # Пока считаем с клавиатуры предложение word = listener.recognize() # Слушаем и распознаем # Если ассистент знает такое слово для перехода if (word in machine[current_state].keys()): # Выполняем действия и переход в новое состояние current_state = machine[current_state][word]() elif (current_state != 'Начало'): # иначе переспросим speaker.speak("Я не понял, пожалуйста повторите") print("Сейчас автомат в: " + current_state + ". Я услышал: " + word) # Переход на "Слушаю задание" def switchToListen(): speaker.speak("Слушаю") return "Слушаю задание" # Переход на "Говорю время" и возвращаюсь в "Начало" def switchToTime(): now = datetime.datetime.now() time = f'Сейчас {now.hour} ' if now.hour == 0 or 5 <= now.hour <= 20: time += 'часов ' if now.hour == 1 or now.hour == 21: time += 'час ' if 2 <= now.hour % 20 <= 4: time += 'часАА ' # тянем слог time += str(now.minute) if now.minute % 10 == 1 and now.minute != 11: time += ' минута' elif 5 <= now.minute <= 20 or 25 <= now.minute <= 30 or 35 <= now.minute <= 40 or 45 <= now.minute <= 50 or 55 <= now.minute: time += ' минут' else: time += ' минуты' speaker.speak(time) return "Начало" def _precipitation_today_message(precipitations:list): # Собираем время для проверки что осадки в будущем сегодня today = datetime.datetime.now().replace(hour = 23, minute = 59, second = 59) # Зададим конец дня for precipitation in precipitations: # Время осадков precipitation_time = datetime.datetime.fromisoformat(precipitation.reference_time('iso')).replace(tzinfo=None) # во избежания ошибок сравнения относительного и абсолютного времени # Если осадки в будущем сегодня if datetime.datetime.now() <= precipitation_time <= today: return f'Обещают {precipitation.detailed_status} в {precipitation_time.hour} часов' # Достаточно сказать об одних осадках return '' # Если осадков подходящих не нашлось - пустая строка # Переход на "Вопрос локации погоды" def switchToWeather(): location = "Murino,RU" try: open_weather_map = OWM(weather_api_key) open_weather_map.config['language'] = 'ru' # Язык результатов # запрос данных о текущем состоянии погоды now = open_weather_map.weather_manager().weather_at_place(location) forecast = open_weather_map.weather_manager().forecast_at_place(location, '3h') # 3h вернет недельный, так как daily не работает except: speaker.speak("Извините. Ошибка запроса к серверу погоды") traceback.print_exc() # Вывод трейсбека для отладки return "Начало" # разбивание данных на части для удобства работы с ними status = now.weather.detailed_status temperature = int(now.weather.temperature('celsius')["temp"]) # Начинаем собирать строку с погодой weather = f"Сейчас {status} {temperature} градусов." # Если в прогнозе есть дождь if forecast.will_have_rain(): weather += _precipitation_today_message(forecast.when_rain()) # Если в прогнозе есть снег if forecast.will_have_snow(): weather += _precipitation_today_message(forecast.when_rain()) speaker.speak(weather) return "Начало" # Переход на приветствие и на "Начало" def switchToHello(): helloWords = ["Привет", "Здравствуй", "Доброго времени суток"] random_index = random.randint(0, len(helloWords)) # Если выпадает больше нуля - берем готовую if (random_index > 0): hi = helloWords[random_index-1] else: # Иначе генерируем время суток now = datetime.datetime.now() if (now.hour < 5 or now.hour > 21): hi = "Доброй ночи" elif (now.hour < 12): hi = "Доброе утро" elif (now.hour < 16): hi = "Добрый день" else: hi = "Добрый вечер" speaker.speak(hi) return "Начало" # Переход на приветствие и на "Начало" def switchToBye(): helloWords = ["Пока", "Удачи", "Я всегда тут, буду ждать"] speaker.speak(random.choice(helloWords)) return "Начало" # Переход на запрос модуля для перезагрузки и на "Начало": def switchToReboot(): init(myName) return "Начало" # Завершение работы def turnOff(): raise SystemExit