Обработка ошибок обрыва соединения.
[voicecontrol.git] / voicecontrol
index fafda2c756ffc60ec5848b31be73698823cfb622..7216fef829eee8765ed549cea6ae96a4a3b196f3 100755 (executable)
@@ -1,9 +1,9 @@
-#!/usr/bin/env python3.8
+#!/usr/bin/env python3
 
 import websockets,asyncio
 import sys
 from pyaudio import PyAudio, Stream, paInt16
-from contextlib import asynccontextmanager, contextmanager, AsyncExitStack
+from contextlib import asynccontextmanager, contextmanager, AsyncExitStack, ExitStack
 from typing import AsyncGenerator, Generator
 
 from urllib.parse import urlencode, quote
@@ -48,7 +48,7 @@ def SkipSource(source,seconds):
   global config
   try:
     if config["debug"]:
-     print("Skipping: ", seconds)
+      print("Skipping: ", seconds)
     bufs = int((seconds)*source._rate/source._frames_per_buffer)
     for i in range(bufs):
       buffer = source.read(source._frames_per_buffer)
@@ -57,6 +57,12 @@ def SkipSource(source,seconds):
   except:
     pass
 
+def Silence(speaker, seconds):
+  buf = bytes(speaker._frames_per_buffer)
+  bufs = int((seconds)*speaker._rate/speaker._frames_per_buffer)
+  for i in range(bufs):
+    speaker.write(buf)
+
 def PlayBack(pyaud, text, mic = None):
   global config, last_time
   
@@ -78,6 +84,7 @@ def PlayBack(pyaud, text, mic = None):
       decoder = MP3Decoder(req)
 
       speaker = pyaud.open(output=True, format=paInt16, channels=decoder.num_channels, rate=decoder.sample_rate)
+      Silence(speaker, 0.3) 
 
       for chunk in decoder:
         speaker.write(chunk)
@@ -117,42 +124,68 @@ def RunCommand(command, pyaud, mic = None):
 
   if command_url:
     try:
+      if config["debug"]:
+        print('Preparing command')
       if command_user:
         my_headers = urllib3.util.make_headers(basic_auth=command_user+':'+command_pwd)
       else:
         my_headers = urllib3.util.make_headers()  
       my_headers['Content-Type']='text/plain'
       my_headers['Accept']='apllication/json'
-      http.request('POST',command_url,headers=my_headers,body=command.encode('UTF-8'))
-      if reply_url:
-        sleep(0.5)
-        res="NULL"
-        for i in range(api_attempts):
-          try:
-            if command_user:
-              my_headers = urllib3.util.make_headers(basic_auth=command_user+':'+command_pwd)
-            else:
-              my_headers = urllib3.util.make_headers()  
-            req=http.request('GET',reply_url,headers=my_headers).data
-            res = json.loads(req)['state'].strip()
-            if config["debug"]:
-              print(res)
-            if not(res == 'NULL'):
-              break
-            sleep(1)  
-          except KeyboardInterrupt:
-            raise
-          except:  
-            sleep(1)
-        if res and not(res=="NULL"):
-          PlayBack(pyaud, res, mic=mic)
-        elif res=="NULL":
-          PlayBack(pyaud, "Сервер не ответил", mic=mic)  
-      http.request('POST',command_url, headers=my_headers, body="")
+      if config["debug"]:
+        print('Sending command')
+      sent = False
+      for i in range(api_attempts):
+        try:
+          http.request('POST',command_url,headers=my_headers,body=command.encode('UTF-8'))
+          sent = True
+          break
+        except Exception as e:
+          print('Exception: '+str(e))
+          sleep(0.5)
+      if sent:
+        if config["debug"]:
+          print('Command sent')
+        if reply_url:
+          sleep(0.5)
+          res="NULL"
+          for i in range(api_attempts):
+            try:
+              if command_user:
+                my_headers = urllib3.util.make_headers(basic_auth=command_user+':'+command_pwd)
+              else:
+                my_headers = urllib3.util.make_headers()  
+              req=http.request('GET',reply_url,headers=my_headers).data
+              res = json.loads(req)['state'].strip()
+              if config["debug"]:
+                print(res)
+              if not(res == 'NULL'):
+                break
+              sleep(1)  
+            except KeyboardInterrupt:
+              raise
+            except Exception as e:
+              print('Exception: '+str(e))
+              sleep(1)
+          if res and not(res=="NULL"):
+            PlayBack(pyaud, res, mic=mic)
+          elif res=="NULL":
+            PlayBack(pyaud, "Сервер не ответил", mic=mic)  
+        if command_user:
+          my_headers = urllib3.util.make_headers(basic_auth=command_user+':'+command_pwd)
+        else:
+          my_headers = urllib3.util.make_headers()  
+        my_headers['Content-Type']='text/plain'
+        my_headers['Accept']='apllication/json'
+        command=""
+        http.request('POST',command_url, headers=my_headers, body=command.encode('UTF-8'))
+      else:
+        PlayBack(pyaud, "Сервер недоступен", mic=mic)
     except KeyboardInterrupt:
       raise
-    except:
+    except Exception as e:
       try:
+        print('Exception: '+str(e))
         http.request('POST',command_url, headers=my_headers, body="")
       except:  
         pass
@@ -207,11 +240,10 @@ async def ListenPhrase(mic, server):
   except websockets.exceptions.ConnectionClosedError:
     raise  
   except:
-    raise
     return '',0
 
 
-async def hello(uri):
+async def main_loop(uri):
 
   global config, last_time
 
@@ -220,49 +252,58 @@ async def hello(uri):
   rec_attempts = config["rec_attempts"]
   commands = config["commands"]
 
-  async with AsyncExitStack() as stack:
-    ws = await stack.enter_async_context(websockets.connect(uri))
-    print('Type Ctrl-C to exit')
-    phrases = config["commands"]
-    phrases.append(config["keyphrase"])
-    phrases = json.dumps(phrases, ensure_ascii=False)
-    await ws.send('{"config" : { "phrase_list" : '+phrases+', "sample_rate" : 16000.0}}')
-
-    ws = await stack.enter_async_context(_polite_websocket(ws))
-    p = stack.enter_context(_pyaudio())
-    s = stack.enter_context(_pyaudio_open_stream(p,
+  
+  with ExitStack() as audio_stack:
+    p = audio_stack.enter_context(_pyaudio())
+    s = audio_stack.enter_context(_pyaudio_open_stream(p,
             format = paInt16, 
             channels = 1,
             rate = 16000,
             input = True, 
             frames_per_buffer = 2000))
-    while True:
-      phrase, confidence = await ListenPhrase(s, ws)
-      if config["debug"]:
-        print(phrase,confidence)
-      if phrase == keyphrase and confidence>=confidence_treshold :
-        PlayBack(p, "Я жду команду", mic=s)
-        command = ""
-
-        for i in range(rec_attempts):
-          phrase, confidence = await ListenPhrase(s, ws)
-          if config["debug"]:
-            print(phrase,confidence)
-          if confidence > confidence_treshold:
-            if (not commands) or (phrase in commands):
-              if config["debug"]:
-                print("Command: ", phrase)
-              command = phrase
-              RunCommand(command, p, s)
-              break
-            else:
-              PlayBack(p, "Не знаю такой команды: "+phrase, mic=s)
-          else:
-            PlayBack(p, "Не поняла, слишком неразборчиво", mic=s)
-
-        if not command:
-          PlayBack(p, "Так команду и не поняла...", mic=s)
 
+    while True:
+      try:    
+        async with AsyncExitStack() as web_stack:
+          ws = await web_stack.enter_async_context(websockets.connect(uri))
+          print('Type Ctrl-C to exit')
+          phrases = [] + config["commands"]
+          phrases.append(config["keyphrase"])
+          phrases = json.dumps(phrases, ensure_ascii=False)
+          await ws.send('{"config" : { "phrase_list" : '+phrases+', "sample_rate" : 16000.0}}')
+
+          ws = await web_stack.enter_async_context(_polite_websocket(ws))
+          while True:
+            phrase, confidence = await ListenPhrase(s, ws)
+            if config["debug"]:
+              print(phrase,confidence)
+            if phrase == keyphrase and confidence>=confidence_treshold :
+              PlayBack(p, "Я жду команду", mic=s)
+              command = ""
+  
+              for i in range(rec_attempts):
+                phrase, confidence = await ListenPhrase(s, ws)
+                if config["debug"]:
+                  print(phrase,confidence)
+                if confidence > confidence_treshold:
+                  if (not commands) or (phrase in commands):
+                    if config["debug"]:
+                      print("Command: ", phrase)
+                    command = phrase
+                    RunCommand(command, p, s)
+                    break
+                  else:
+                    PlayBack(p, "Не знаю такой команды: "+phrase, mic=s)
+                else:
+                  PlayBack(p, "Не поняла, слишком неразборчиво", mic=s)
+
+              if not command:
+                PlayBack(p, "Так команду и не поняла...", mic=s)
+      except KeyboardInterrupt:
+        raise
+      except Exception as e:
+        print('Exception: '+str(e))
+        pass
              
 def get_config(path):
   
@@ -379,7 +420,7 @@ while True:
 
     loop = asyncio.get_event_loop()
     loop.run_until_complete(
-        hello(f'ws://' + server))
+        main_loop(f'ws://' + server))
 
   except (Exception, KeyboardInterrupt) as e:
     loop.run_until_complete(