SpeechRecognizerPy/state_machine.py

184 lines
7.6 KiB
Python
Raw Permalink Normal View History

# Модуль машины состояний (конечный автомат)
2022-05-21 17:06:44 +00:00
import speaker # Для воспроизведения
import listener # Для распознавание речи
import datetime # Для времени
2022-05-16 09:33:50 +00:00
from pyowm import OWM # Использование OpenWeatherMap для получения данных о погоде (pip install pyowm)
import random
2022-05-16 10:09:04 +00:00
import traceback # вывод traceback без остановки работы программы при отлове исключений
2022-05-21 17:06:44 +00:00
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
2022-05-21 17:06:44 +00:00
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,
2022-05-16 09:33:50 +00:00
"погода": switchToWeather,
"привет": switchToHello,
"здравствуй": switchToHello,
"доброе утро": switchToHello,
"добрый день": switchToHello,
"добрый вечер": switchToHello,
"доброй ночи": switchToHello,
"пока": switchToBye,
"до встречи": switchToBye,
"перезапуск": switchToReboot,
"завершение работы": turnOff
},
}
switchToHello()
speaker.speak(f"Ассистент {myName} начинает работу")
def work():
"""Обрабатывает конечный автомат"""
global current_state
while(True):
2022-05-21 17:06:44 +00:00
#word = input().lower() # Пока считаем с клавиатуры предложение
word = listener.recognize() # Слушаем и распознаем
# Если ассистент знает такое слово для перехода
if (word in machine[current_state].keys()):
# Выполняем действия и переход в новое состояние
current_state = machine[current_state][word]()
elif (current_state != 'Начало'):
# иначе переспросим
speaker.speak("Я не понял, пожалуйста повторите")
2022-05-21 17:06:44 +00:00
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 "Начало"
2022-05-21 19:45:03 +00:00
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():
2022-05-16 10:02:59 +00:00
location = "Murino,RU"
try:
open_weather_map = OWM(weather_api_key)
2022-05-21 19:45:03 +00:00
open_weather_map.config['language'] = 'ru' # Язык результатов
# запрос данных о текущем состоянии погоды
2022-05-21 19:45:03 +00:00
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 "Начало"
# разбивание данных на части для удобства работы с ними
2022-05-21 19:45:03 +00:00
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)
2022-05-16 09:33:50 +00:00
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