Skip to content

Instantly share code, notes, and snippets.

@seanmtracey
Last active September 24, 2018 19:59
Show Gist options
  • Save seanmtracey/e018d1ce6c074d43dd624a8d0d3de4bd to your computer and use it in GitHub Desktop.
Save seanmtracey/e018d1ce6c074d43dd624a8d0d3de4bd to your computer and use it in GitHub Desktop.
A Node-RED flow that turns a glow orb different colors depending on how fast I'm talking
[
{
"id": "31356f51.50904",
"type": "mqtt out",
"z": "524a8e1d.0c46a",
"name": "GlowOrb",
"topic": "iot-2/evt/command/fmt/text",
"qos": "2",
"retain": "",
"broker": "",
"x": 810,
"y": 480,
"wires": []
},
{
"id": "fced1272.e046e",
"type": "inject",
"z": "524a8e1d.0c46a",
"name": "Blue",
"topic": "",
"payload": "#000099",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 595,
"y": 425,
"wires": [
[
"31356f51.50904"
]
]
},
{
"id": "290660ba.ce7f4",
"type": "inject",
"z": "524a8e1d.0c46a",
"name": "Orange",
"topic": "",
"payload": "#ffff00",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 595,
"y": 485,
"wires": [
[
"31356f51.50904"
]
]
},
{
"id": "6477a832.f02b48",
"type": "inject",
"z": "524a8e1d.0c46a",
"name": "Green",
"topic": "",
"payload": "#00ff00",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 595,
"y": 545,
"wires": [
[
"31356f51.50904"
]
]
},
{
"id": "6a82a91.05c6d58",
"type": "function",
"z": "524a8e1d.0c46a",
"name": "Word Counter",
"func": "const MAX_WORDS_ALLOWED = 185;\nconst ALLOWANCE = 40;\nconst WINDOW_TRANSCRIPTS = context.get('audio') || [];\n\nconst data = {\n time : Date.now() | 0,\n words : msg.transcription.split(' ')\n};\n\nWINDOW_TRANSCRIPTS.push(data);\n\nconst MINUTE_TRANSCRIPT_WINDOW = WINDOW_TRANSCRIPTS.filter(datum => {return (Date.now() | 0) - datum.time < 60000 });\n\ncontext.set('audio', MINUTE_TRANSCRIPT_WINDOW);\n\nconst WORDS_COUNTED = MINUTE_TRANSCRIPT_WINDOW.reduce( (acc, data) => {\n \n console.log('COUNT!:', acc, data);\n return acc + data.words.length\n \n}, 0 );\n\n\nconst TIME_ELAPSED = (MINUTE_TRANSCRIPT_WINDOW[MINUTE_TRANSCRIPT_WINDOW.length - 1].time) - MINUTE_TRANSCRIPT_WINDOW[0].time;\nconst MULT_FACTOR = (60000 / TIME_ELAPSED);\nconst WORDS_SPOKEN = WORDS_COUNTED * MULT_FACTOR;\n\nconsole.log('EST:', WORDS_SPOKEN, 'COUNTED:', WORDS_COUNTED, 'TIME ELAPSED:', TIME_ELAPSED);\n\n\nif(WORDS_SPOKEN > MAX_WORDS_ALLOWED){\n msg.payload = '#0000ff';\n} else if(WORDS_SPOKEN > MAX_WORDS_ALLOWED - ALLOWANCE){\n msg.payload = '#ffff00';\n} else {\n msg.payload = '#00ff00';\n}\n\nmsg.words_spoken = WORDS_SPOKEN;\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 605,
"y": 365,
"wires": [
[
"926f781d.f5f7f8",
"31356f51.50904",
"587489c9.99ec38"
]
]
},
{
"id": "926f781d.f5f7f8",
"type": "debug",
"z": "524a8e1d.0c46a",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"x": 795,
"y": 305,
"wires": []
},
{
"id": "b0100130.cea8d",
"type": "websocket in",
"z": "524a8e1d.0c46a",
"name": "",
"server": "acd8ae06.c9bf5",
"client": "",
"x": 215,
"y": 305,
"wires": [
[
"926f781d.f5f7f8",
"7dedb891.f246b8"
]
]
},
{
"id": "abc9929a.4b51",
"type": "http in",
"z": "524a8e1d.0c46a",
"name": "",
"url": "/word-count",
"method": "get",
"upload": false,
"swaggerDoc": "",
"x": 200,
"y": 225,
"wires": [
[
"46f05fe2.cfc3e"
]
]
},
{
"id": "ec3c81d1.9d524",
"type": "http response",
"z": "524a8e1d.0c46a",
"name": "",
"statusCode": "",
"headers": {},
"x": 540,
"y": 225,
"wires": []
},
{
"id": "46f05fe2.cfc3e",
"type": "template",
"z": "524a8e1d.0c46a",
"name": "",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "<!DOCTYPE html>\n<html>\n <head>\n <title>Simple Mic Streamer</title>\n </head>\n <body>\n \n <button id=\"toggle\" data-state=\"stopped\">Start</button>\n \n <script>\n\n (function(){\n \n 'use strict';\n \n const ws = new WebSocket('wss://' + window.location.host + '/ws/audio');\n const button = document.querySelector('button');\n \n let mR;\n\n const constraints = {\n video : false,\n audio : true\n };\n\n navigator.mediaDevices.getUserMedia(constraints)\n .then(function(stream){\n\n mR = new MediaRecorder(stream);\n \n mR.start();\n \n setInterval(function(){\n\n mR.ondataavailable = function(e){\n console.log(e.data);\n \n if(button.dataset.state === 'started'){\n console.log('Sending to server:', e.data);\n ws.send(e.data);\n }\n }\n\n mR.stop();\n\n mR = new MediaRecorder(stream);\n mR.start();\n\n }, 5000);\n\n\n })\n .catch(function(err){\n console.log('Media stream err:', err);\n })\n ;\n\n button.addEventListener('click', function(){\n\n this.dataset.state = this.dataset.state === 'stopped' ? 'started' : 'stopped';\n this.textContent = this.dataset.state === 'stopped' ? 'Start' : 'Stop';\n \n }, false);\n \n ws.addEventListener('open', function(){\n console.log('WS connection established'); \n }, false);\n \n ws.addEventListener('message', function(msg){\n console.log('WS message:', msg);\n }, false);\n \n ws.addEventListener('error', function(err){\n console.log('WS error:', err);\n }, false);\n \n ws.addEventListener('closed', function(e){\n console.log('WS connection closed:', e);\n });\n \n }());\n \n </script>\n \n </body>\n</html>",
"output": "str",
"x": 400,
"y": 225,
"wires": [
[
"ec3c81d1.9d524"
]
]
},
{
"id": "7dedb891.f246b8",
"type": "watson-speech-to-text",
"z": "524a8e1d.0c46a",
"name": "",
"alternatives": 1,
"speakerlabels": true,
"smartformatting": false,
"lang": "en-US",
"langhidden": "en-US",
"langcustom": "NoCustomisationSetting",
"langcustomhidden": "",
"band": "BroadbandModel",
"bandhidden": "BroadbandModel",
"password": "fCeIotZHKVpn",
"apikey": "",
"payload-response": false,
"streaming-mode": false,
"streaming-mute": true,
"auto-connect": false,
"discard-listening": false,
"disable-precheck": true,
"default-endpoint": true,
"service-endpoint": "https://stream.watsonplatform.net/speech-to-text/api",
"x": 405,
"y": 365,
"wires": [
[
"6a82a91.05c6d58"
]
]
},
{
"id": "587489c9.99ec38",
"type": "debug",
"z": "524a8e1d.0c46a",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "words_spoken",
"x": 835,
"y": 365,
"wires": []
},
{
"id": "c2e1a6cf.919628",
"type": "microphone",
"z": "524a8e1d.0c46a",
"name": "",
"x": 215,
"y": 365,
"wires": [
[
"7dedb891.f246b8"
]
]
},
{
"id": "acd8ae06.c9bf5",
"type": "websocket-listener",
"z": "",
"path": "/ws/audio",
"wholemsg": "false"
}
]

Steps for use:

  1. Import the flow to a Node-RED instance
  2. Add credentials to the speech-to-text node
  3. Configure your MQTT node to communicate with your Gloworb / MQTT enabled device
  4. Click 'Deploy'
  5. Open up https://<YOUR_NODE_RED_HOST>/word-count. You'll be asked to enable your microphone, this will begin the capture of audio from your mircophone for counting.
  6. Once you enable the microphone, you can click the 'Start' button to stream the audio over web sockets to your Node-RED instance.
  • NB: If you hit the 'stop' button, you'll need to refresh the page to start streaming again, I haven't finished that bit of the code yet.
  1. Voila! If you're speaking at an acceptable rate, your glow orb will be green. If you're going a little fast, it will turn orange. If you're WWAAAYYYYY out, it will turn blue (because red is an alarming color for anyone to see.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment