Working on wake websocket

This commit is contained in:
Michael Hansen
2020-02-06 16:49:33 -05:00
parent a92d88ff8f
commit 9553691e88
7 changed files with 104 additions and 23 deletions
+3 -1
View File
@@ -172,4 +172,6 @@
!rhasspy/train/*.py
!rhasspy/train/jsgf2fst/*.py
!*.py
!VERSION
!VERSION
!pip
+44 -16
View File
@@ -30,7 +30,7 @@ from swagger_ui import quart_api_doc
from rhasspy.actor import ActorSystem, ConfigureEvent, RhasspyActor
from rhasspy.core import RhasspyCore
from rhasspy.events import IntentRecognized, ProfileTrainingFailed
from rhasspy.events import IntentRecognized, ProfileTrainingFailed, WakeWordDetected
from rhasspy.utils import (
FunctionLoggingHandler,
buffer_to_wav,
@@ -698,7 +698,7 @@ async def api_text_to_intent():
intent_json = json.dumps(intent)
logger.debug(intent_json)
await add_ws_event(WS_EVENT_INTENT, intent_json)
await add_ws_event(WS_EVENT_USER, "intent", intent_json)
if not no_hass:
# Send intent to Home Assistant
@@ -734,7 +734,7 @@ async def api_speech_to_intent() -> Response:
intent_json = json.dumps(intent)
logger.debug(intent_json)
await add_ws_event(WS_EVENT_INTENT, intent_json)
await add_ws_event(WS_EVENT_USER, "intent", intent_json)
if not no_hass:
# Send intent to Home Assistant
@@ -780,7 +780,7 @@ async def api_stop_recording() -> Response:
intent_json = json.dumps(intent)
logger.debug(intent_json)
await add_ws_event(WS_EVENT_INTENT, intent_json)
await add_ws_event(WS_EVENT_USER, "intent", intent_json)
if not no_hass:
# Send intent to Home Assistant
@@ -1115,25 +1115,25 @@ async def swagger_yaml() -> Response:
# WebSocket API
# -----------------------------------------------------------------------------
WS_EVENT_INTENT = 0
WS_EVENT_USER = 0
WS_EVENT_LOG = 1
ws_queues: List[List[asyncio.Queue]] = [[], []]
ws_locks: List[asyncio.Lock] = [asyncio.Lock(), asyncio.Lock()]
async def add_ws_event(event_type: int, text: str):
async def add_ws_event(event_type: int, message_type: str, text: str):
"""Send text out to all websockets for a specific event."""
async with ws_locks[event_type]:
for q in ws_queues[event_type]:
await q.put(text)
await q.put((message_type, text))
# Send logging messages out to websocket
logging.root.addHandler(
FunctionLoggingHandler(
lambda msg: asyncio.run_coroutine_threadsafe(
add_ws_event(WS_EVENT_LOG, msg), loop
add_ws_event(WS_EVENT_LOG, "log", msg), loop
)
)
)
@@ -1156,7 +1156,13 @@ class WebSocketObserver(RhasspyActor):
intent_json = json.dumps(message.intent)
self._logger.debug(intent_json)
asyncio.run_coroutine_threadsafe(
add_ws_event(WS_EVENT_INTENT, intent_json), loop
add_ws_event(WS_EVENT_USER, "intent", intent_json), loop
)
elif isinstance(message, WakeWordDetected):
assert core is not None
wake_json = json.dumps({"wakewordId": message.name, "siteId": core.siteId})
asyncio.run_coroutine_threadsafe(
add_ws_event(WS_EVENT_USER, "wake", wake_json), loop
)
@@ -1165,19 +1171,41 @@ async def api_events_intent() -> None:
"""Websocket endpoint to receive intents as JSON."""
# Add new queue for websocket
q: asyncio.Queue = asyncio.Queue()
async with ws_locks[WS_EVENT_INTENT]:
ws_queues[WS_EVENT_INTENT].append(q)
async with ws_locks[WS_EVENT_USER]:
ws_queues[WS_EVENT_USER].append(q)
try:
while True:
text = await q.get()
await websocket.send(text)
message_type, text = await q.get()
if message_type == "intent":
await websocket.send(text)
except Exception:
logger.exception("api_events_intent")
# Remove queue
async with ws_locks[WS_EVENT_INTENT]:
ws_queues[WS_EVENT_INTENT].remove(q)
async with ws_locks[WS_EVENT_USER]:
ws_queues[WS_EVENT_USER].remove(q)
@app.websocket("/api/events/wake")
async def api_events_wake() -> None:
"""Websocket endpoint to report wake up."""
# Add new queue for websocket
q: asyncio.Queue = asyncio.Queue()
async with ws_locks[WS_EVENT_USER]:
ws_queues[WS_EVENT_USER].append(q)
try:
while True:
message_type, text = await q.get()
if message_type == "wake":
await websocket.send(text)
except Exception:
logger.exception("api_events_wake")
# Remove queue
async with ws_locks[WS_EVENT_USER]:
ws_queues[WS_EVENT_USER].remove(q)
@app.websocket("/api/events/log")
@@ -1190,7 +1218,7 @@ async def api_events_log() -> None:
try:
while True:
text = await q.get()
_, text = await q.get()
await websocket.send(text)
except concurrent.futures.CancelledError:
pass
+5
View File
@@ -13,6 +13,11 @@ body {
z-index: 9999;
}
#logo {
border-color: red;
border-width: 0;
}
.response {
text-align: center;
}
+2 -2
View File
@@ -4,7 +4,7 @@ doit==0.31.1
fuzzywuzzy[speedup]==0.17.0
google-cloud-texttospeech==0.5.0
html5lib==1.0.1
json5==0.8.5
json5==0.7.0
multidict==4.6.1
networkx>=2.0
num2words==0.5.10
@@ -15,6 +15,6 @@ pydash==4.7.6
quart==0.6.15
quart-cors==0.1.3
requests==2.22.0
rhasspy-nlu==0.1.4.1
rhasspy-nlu==0.1.6
swagger-ui-py==0.1.7
webrtcvad==2.0.10
+1 -1
View File
@@ -88,7 +88,7 @@ class RhasspyCore:
self._session: Optional[aiohttp.ClientSession] = aiohttp.ClientSession()
self.dialogue_manager: Optional[RhasspyActor] = None
self.download_status: typing.List[str] = []
self.download_status: List[str] = []
# -------------------------------------------------------------------------
+23 -2
View File
@@ -3,7 +3,7 @@
<!-- Top Bar -->
<nav class="navbar navbar-expand-sm navbar-dark bg-dark fixed-top">
<a href="/">
<img class="navbar-brand" v-bind:class="spinnerClass" src="/img/logo.png">
<img id="logo" class="navbar-brand" v-bind:class="spinnerClass" src="/img/logo.png">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
@@ -191,7 +191,9 @@
version: '',
downloadStatus: ''
downloadStatus: '',
wakeSocket: null
}
},
@@ -365,6 +367,24 @@
if (this.downloading) {
setTimeout(this.updateDownloadStatus, 1000)
}
},
connectWakeSocket: function() {
// Connect to /api/events/intent websocket
var wsProtocol = 'ws://'
if (window.location.protocol == 'https:') {
wsProtocol = 'wss://'
}
var wsURL = wsProtocol + window.location.host + '/api/events/wake'
this.wakeSocket = new WebSocket(wsURL)
this.wakeSocket.onmessage = (evt) => {
$('#logo').css('border-width', '5px')
}
this.wakeSocket.onclose = () => {
// Try to reconnect
setTimeout(this.connectWakeSocket, 1000)
}
}
},
@@ -376,6 +396,7 @@
this.getCustomWords()
this.getUnknownWords()
this.getProblems()
/* this.connectWakeSocket() */
this.$options.sockets.onmessage = function(event) {
this.rhasspyLog = event.data + '\n' + this.rhasspyLog
}
+26 -1
View File
@@ -136,7 +136,9 @@
audioContext: null,
recorder: null,
sendHass: true
sendHass: true,
intentSocket: null
}
},
@@ -276,7 +278,30 @@
playLastVoiceCommand: function(event) {
TranscribeService.playRecording()
.catch(err => this.$parent.error(err))
},
connectIntentSocket: function() {
// Connect to /api/events/intent websocket
var wsProtocol = 'ws://'
if (window.location.protocol == 'https:') {
wsProtocol = 'wss://'
}
var wsURL = wsProtocol + window.location.host + '/api/events/intent'
this.intentSocket = new WebSocket(wsURL)
this.intentSocket.onmessage = (evt) => {
this.jsonSource = JSON.parse(evt.data)
this.sentence = this.jsonSource.raw_text
}
this.intentSocket.onclose = () => {
// Try to reconnect
setTimeout(this.connectIntentSocket, 1000)
}
}
},
mounted: function() {
this.connectIntentSocket()
}
}
</script>