Skip to content

Instantly share code, notes, and snippets.

@fishfitz
Last active April 26, 2020 21:01
Show Gist options
  • Save fishfitz/67844a2029fd702692586e0fa7d79b07 to your computer and use it in GitHub Desktop.
Save fishfitz/67844a2029fd702692586e0fa7d79b07 to your computer and use it in GitHub Desktop.
<!--
Actually this is a VueJS template (.vue).
The recognition itself uses an API that is included in Chrome and only works in Chrome (04/2020).
https://developer.mozilla.org/fr/docs/Web/API/Web_Speech_API
You may also need the following packages:
- npm install ky (http requests)
- npm install talisman (fuzzy matching)
-->
<template>
<button class="button" @click="start" :disabled="!recognition"> Start </button>
</template>
<script>
import ky from 'ky';
import { lig3 } from 'talisman/metrics/lig';
export default {
data() {
const data = {
recognition: null,
voice: null,
endpoint: process.env.HOME_URL,
httpConfig: { headers: { authorization: `Bearer ${process.env.HOME_TOKEN}` } },
wakeword: /(brett)|(((be*)|(ma))*lette)/g, // Note: in JS regex are stateful
lastWakewordOccurence: null,
end: false
};
return {
...data,
actions: [
{
phrase: "éteins l'écran",
handler() {
this.speak('J\'éteins l\'écran');
setTimeout(() => {
ky.post(`${data.endpoint}services/shell_command/monitor_off`, data.httpConfig);
}, 2000);
}
},
{
phrase: "mode casque",
async handler() {
this.speak('Bip');
await ky.post(`${data.endpoint}services/shell_command/audio_headset`, data.httpConfig);
this.speak('Boop');
}
},
{
phrase: "mode enceinte",
async handler() {
this.speak('Boop');
await ky.post(`${data.endpoint}services/shell_command/audio_speakers`, data.httpConfig);
this.speak('Bip');
}
},
{
phrase: 'désactivation',
handler() {
this.speak('Désactivation');
this.end = true;
}
},
{
phrase: 'allume la lumière',
async handler() {
this.speak('J\'allume la lumière');
await ky.post(`${data.endpoint}services/light/toggle`, {
...data.httpConfig,
json: {
brightness: 255,
entity_id: 'light.bedroom'
}
});
}
},
{
phrase: 'éteins la lumière',
async handler() {
this.speak('J\'éteins la lumière');
await ky.post(`${data.endpoint}services/light/toggle`, {
...data.httpConfig,
json: {
brightness: 0,
entity_id: 'light.bedroom'
}
});
}
}
]
};
},
async mounted() {
window.speechSynthesis.onvoiceschanged = () => { // Wait for speechsynthesis to be loaded
this.recognition = new window.webkitSpeechRecognition();
this.recognition.continuous = true;
this.recognition.lang = 'fr-FR';
this.recognition.interimResults = false;
this.recognition.maxAlternatives = 1;
this.recognition.onresult = this.handler;
this.recognition.onend = () => {
setTimeout(this.start, 1500); // When an action was recognized, restart the recognition
};
this.voice = speechSynthesis.getVoices().find(v => v.voiceURI === 'Google français');
};
},
methods: {
start() {
if (!this.recognition) Notification.requestPermission();
if (!this.end) this.recognition.start();
},
speak(text) {
const phrase = new SpeechSynthesisUtterance(text);
phrase.voice = this.voice;
phrase.pitch = 0.6;
phrase.rate = 1.2;
speechSynthesis.speak(phrase);
},
handler(event) {
let text = Array.from(event.results).map(r => r[0].transcript).join(' ').replace(/\s{2,}/g, ' ');
this.lastWakewordOccurence = this.wakeword.exec(text) || this.lastWakewordOccurence;
if (this.lastWakewordOccurence === null) return; // No wake word recognized, idle
text = text.slice(this.lastWakewordOccurence.index + this.lastWakewordOccurence[0].length + 1).trim();
console.info('...', text); // Text recognized after the last keyword occurence
const triggered = this.actions.find((action) => {
if (lig3(text, action.phrase) > 0.80) { // Fuzzy match an action phrase (normalized distance)
this.stop();
console.info('> Trigger', action.phrase);
new Notification('Belette', { body: action.phrase });
if (action.handler) action.handler.call(this, text);
return true;
}
});
if (triggered || text.length > 4000) this.stop(); // Avoid getting too much text to recognize
},
stop() {
this.recognition.abort();
this.wakeword.lastIndex = 0; // Reset regex state
this.lastWakewordOccurence = null;
}
}
};
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment