Improve docstrings for sphinx.
[python_utils.git] / google_assistant.py
index 572b4ccdf25644992f77f66eae800ea9e306ce50..6b480ef367d4fab5eda046bbbb77cface814b7e3 100644 (file)
@@ -1,8 +1,15 @@
 #!/usr/bin/env python3
 
+# © Copyright 2021-2022, Scott Gasch
+
+"""A module to serve as a local client library around HTTP calls to
+the Google Assistant via a local gateway.
+"""
+
 import logging
-from typing import NamedTuple
-import sys
+import warnings
+from dataclasses import dataclass
+from typing import Optional
 
 import requests
 import speech_recognition as sr  # type: ignore
@@ -20,22 +27,33 @@ parser.add_argument(
     type=str,
     default="http://kiosk.house:3000",
     metavar="URL",
-    help="How to contact the Google Assistant bridge"
+    help="How to contact the Google Assistant bridge",
 )
 parser.add_argument(
     "--google_assistant_username",
     type=str,
     metavar="GOOGLE_ACCOUNT",
     default="scott.gasch",
-    help="The user account for talking to Google Assistant"
+    help="The user account for talking to Google Assistant",
 )
 
 
-class GoogleResponse(NamedTuple):
-    success: bool
-    response: str
-    audio_url: str
-    audio_transcription: str
+@dataclass
+class GoogleResponse:
+    """A Google response wrapper dataclass."""
+
+    success: bool = False
+    """Did the request succeed (True) or fail (False)?"""
+
+    response: str = ''
+    """The response as a text string, if available."""
+
+    audio_url: str = ''
+    """A URL that can be used to fetch the raw audio response."""
+
+    audio_transcription: Optional[str] = None
+    """A transcription of the audio response, if available.  Otherwise
+    None"""
 
     def __repr__(self):
         return f"""
@@ -51,12 +69,20 @@ def tell_google(cmd: str, *, recognize_speech=True) -> GoogleResponse:
 
 
 def ask_google(cmd: str, *, recognize_speech=True) -> GoogleResponse:
-    """Send a command string to Google via the google_assistant_bridge as the
-    user google_assistant_username and return the response.  If recognize_speech
-    is True, perform speech recognition on the audio response from Google so as
-    to translate it into text (best effort, YMMV).
+    """Send a command string to Google via the google_assistant_bridge as
+    the user google_assistant_username and return the response.  If
+    recognize_speech is True, perform speech recognition on the audio
+    response from Google so as to translate it into text (best effort,
+    YMMV).  e.g.::
+
+        >>> google_assistant.ask_google('What time is it?')
+        success: True
+        response: 9:27 PM.
+        audio_transcription: 9:27 p.m.
+        audio_url: http://kiosk.house:3000/server/audio?v=1653971233030
+
     """
-    logging.debug(f"Asking google: '{cmd}'")
+    logging.debug("Asking google: '%s'", cmd)
     payload = {
         "command": cmd,
         "user": config.config['google_assistant_username'],
@@ -66,15 +92,16 @@ def ask_google(cmd: str, *, recognize_speech=True) -> GoogleResponse:
     success = False
     response = ""
     audio = ""
-    audio_transcription = ""
+    audio_transcription: Optional[str] = ""
     if r.status_code == 200:
         j = r.json()
+        logger.debug(j)
         success = bool(j["success"])
         response = j["response"] if success else j["error"]
         if success:
             logger.debug('Google request succeeded.')
             if len(response) > 0:
-                logger.debug(f"Google said: '{response}'")
+                logger.debug("Google said: '%s'", response)
         audio = f"{config.config['google_assistant_bridge']}{j['audio']}"
         if recognize_speech:
             recognizer = sr.Recognizer()
@@ -90,10 +117,12 @@ def ask_google(cmd: str, *, recognize_speech=True) -> GoogleResponse:
                     audio_transcription = recognizer.recognize_google(
                         speech,
                     )
-                    logger.debug(f"Transcription: '{audio_transcription}'")
+                    logger.debug("Transcription: '%s'", audio_transcription)
                 except sr.UnknownValueError as e:
                     logger.exception(e)
-                    logger.warning('Unable to parse Google assistant\'s response.')
+                    msg = 'Unable to parse Google assistant\'s response.'
+                    logger.warning(msg)
+                    warnings.warn(msg, stacklevel=3)
                     audio_transcription = None
         return GoogleResponse(
             success=success,
@@ -110,4 +139,3 @@ def ask_google(cmd: str, *, recognize_speech=True) -> GoogleResponse:
             audio_url=audio,
             audio_transcription=audio_transcription,
         )
-        sys.exit(-1)