3 # © Copyright 2021-2022, Scott Gasch
5 """A module to serve as a local client library around HTTP calls to
6 the Google Assistant via a local gateway.
11 from dataclasses import dataclass
12 from typing import Optional
15 import speech_recognition as sr # type: ignore
19 logger = logging.getLogger(__name__)
21 parser = config.add_commandline_args(
22 f"Google Assistant ({__file__})",
23 "Args related to contacting the Google Assistant",
26 "--google_assistant_bridge",
28 default="http://kiosk.house:3000",
30 help="How to contact the Google Assistant bridge",
33 "--google_assistant_username",
35 metavar="GOOGLE_ACCOUNT",
36 default="scott.gasch",
37 help="The user account for talking to Google Assistant",
43 """A Google response wrapper dataclass."""
46 """Did the request succeed (True) or fail (False)?"""
49 """The response as a text string, if available."""
52 """A URL that can be used to fetch the raw audio response."""
54 audio_transcription: Optional[str] = None
55 """A transcription of the audio response, if available. Otherwise
60 success: {self.success}
61 response: {self.response}
62 audio_transcription: {self.audio_transcription}
63 audio_url: {self.audio_url}"""
66 def tell_google(cmd: str, *, recognize_speech=True) -> GoogleResponse:
67 """Alias for ask_google."""
68 return ask_google(cmd, recognize_speech=recognize_speech)
71 def ask_google(cmd: str, *, recognize_speech=True) -> GoogleResponse:
72 """Send a command string to Google via the google_assistant_bridge as
73 the user google_assistant_username and return the response. If
74 recognize_speech is True, perform speech recognition on the audio
75 response from Google so as to translate it into text (best effort,
78 >>> google_assistant.ask_google('What time is it?')
81 audio_transcription: 9:27 p.m.
82 audio_url: http://kiosk.house:3000/server/audio?v=1653971233030
85 logging.debug("Asking google: '%s'", cmd)
88 "user": config.config['google_assistant_username'],
90 url = f"{config.config['google_assistant_bridge']}/assistant"
91 r = requests.post(url, json=payload)
95 audio_transcription: Optional[str] = ""
96 if r.status_code == 200:
99 success = bool(j["success"])
100 response = j["response"] if success else j["error"]
102 logger.debug('Google request succeeded.')
103 if len(response) > 0:
104 logger.debug("Google said: '%s'", response)
105 audio = f"{config.config['google_assistant_bridge']}{j['audio']}"
107 recognizer = sr.Recognizer()
108 r = requests.get(audio)
109 if r.status_code == 200:
111 speech = sr.AudioData(
117 audio_transcription = recognizer.recognize_google(
120 logger.debug("Transcription: '%s'", audio_transcription)
121 except sr.UnknownValueError as e:
123 msg = 'Unable to parse Google assistant\'s response.'
125 warnings.warn(msg, stacklevel=3)
126 audio_transcription = None
127 return GoogleResponse(
131 audio_transcription=audio_transcription,
134 message = f'HTTP request to {url} with {payload} failed; code {r.status_code}'
135 logger.error(message)
136 return GoogleResponse(
140 audio_transcription=audio_transcription,