Various changes.
[python_utils.git] / google_assistant.py
1 #!/usr/bin/env python3
2
3 import logging
4 from typing import NamedTuple
5
6 import requests
7 import speech_recognition as sr  # type: ignore
8
9 import config
10
11 logger = logging.getLogger(__name__)
12
13 parser = config.add_commandline_args(
14     f"Google Assistant ({__file__})",
15     "Args related to contacting the Google Assistant",
16 )
17 parser.add_argument(
18     "--google_assistant_bridge",
19     type=str,
20     default="http://kiosk.house:3000",
21     metavar="URL",
22     help="How to contact the Google Assistant bridge"
23 )
24 parser.add_argument(
25     "--google_assistant_username",
26     type=str,
27     metavar="GOOGLE_ACCOUNT",
28     default="scott.gasch",
29     help="The user account for talking to Google Assistant"
30 )
31
32
33 class GoogleResponse(NamedTuple):
34     success: bool
35     response: str
36     audio_url: str
37     audio_transcription: str
38
39     def __repr__(self):
40         return f"""
41 success: {self.success}
42 response: {self.response}
43 audio_transcription: {self.audio_transcription}
44 audio_url: {self.audio_url}"""
45
46
47 def tell_google(cmd: str, *, recognize_speech=True) -> GoogleResponse:
48     return ask_google(cmd, recognize_speech=recognize_speech)
49
50
51 def ask_google(cmd: str, *, recognize_speech=True) -> GoogleResponse:
52     logging.debug(f"Asking google: '{cmd}'")
53     payload = {
54         "command": cmd,
55         "user": config.config['google_assistant_username'],
56     }
57     url = f"{config.config['google_assistant_bridge']}/assistant"
58     r = requests.post(url, json=payload)
59     success = False
60     response = ""
61     audio = ""
62     audio_transcription = ""
63     if r.status_code == 200:
64         j = r.json()
65         success = bool(j["success"])
66         response = j["response"] if success else j["error"]
67         if success:
68             logger.debug('Google request succeeded.')
69             if len(response) > 0:
70                 logger.debug(f"Google said: '{response}'")
71         audio = f"{config.config['google_assistant_bridge']}{j['audio']}"
72         if recognize_speech:
73             recognizer = sr.Recognizer()
74             r = requests.get(audio)
75             if r.status_code == 200:
76                 raw = r.content
77                 speech = sr.AudioData(
78                     frame_data=raw,
79                     sample_rate=24000,
80                     sample_width=2,
81                 )
82                 try:
83                     audio_transcription = recognizer.recognize_google(
84                         speech,
85                     )
86                     logger.debug(f"Transcription: '{audio_transcription}'")
87                 except sr.UnknownValueError as e:
88                     logger.exception(e)
89                     logger.warning('Unable to parse Google assistant\'s response.')
90                     audio_transcription = None
91     else:
92         logger.error(
93             f'HTTP request to {url} with {payload} failed; code {r.status_code}'
94         )
95     return GoogleResponse(
96         success=success,
97         response=response,
98         audio_url=audio,
99         audio_transcription=audio_transcription,
100     )