184 lines
7.6 KiB
Python
184 lines
7.6 KiB
Python
# Модуль машины состояний (конечный автомат)
|
||
|
||
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 |