NEWS
Speech2ioBroker
-
<size size="150">speech2ioBroker</size>
Passend zu der Android App an der ich im Moment arbeite habe ich ein Skript erstellt, welches Sprachbefehle in Aktionen umsetzen soll.
Im Moment ist der Funktionsumfang gering, aber sehr einfach erweiterbar!
Falls jemand damit rumspielen will oder sogar erweitern möchte werde ich hier hin und wieder eine aktuelle Version des Skripts posten.
Wie kann das Skript verwendet werden?
speech2ioBroker funktioniert in 2 Stufen:
Zu erst wird der Eingangstext nach vorgegebenen Keywords untersucht, diese sind in patterns definiert.
Als nächstes wird in einem Array von Regeln (rules) nach einer, zu den gefundenen Keywords passenden, Regel gesucht. In dieser wird dann definiert, welche Aktionen durchgeführt werden sollen.
<size size="150">Patterns</size>:
Das Patterns-Objekt besteht aus einem Keyword und einem Regulären Ausdruck. Ausgehend von den von mir vordefinierten Keywords muss man Reguläre Ausdrücke nicht unbedingt verstehen um das Skript zu nutzen oder zu erweitern. Wichtig ist, dass in den Keywords keine Umlaute vorkommen dürfen.
Beispiele
kueche: "\\b(küchen?)"
Das Keyword ist hier "kueche" und der Reguläre Ausdruck "\b(küchen?)". Dabei bedeutet "\b", dass vor dem Wort ein Worttrenner (Leerzeichen) stehen muss. Das Fragezeichen bedeutet, dass der Buchstabe davor ("n") vorkommen kann, aber nicht muss. Am ende gibt es hier kein "\b", also darf hier ein weiteres Wort direkt anschließen. Dieses Pattern wird also sowohl bei "küche" als auch bei "küchenlampe" matchen, nicht aber bei "wohnküche".
Die Wörter können irgendwo in einem Satz eingebettet vorkommen, z. B. "Schalte das Licht in der Küche ein".
Bei diesem Satz würden bei den eingebauten Patterns diese Ids matchen: kueche, light, set, on
light: "(lichte?r?|lampen?|leuchten?)\\b"
Dies ist sozusagen ein Gegenstück zum oberen Keyword. Der senkrechte Strich trennt mehrere Pattern, so dass nur eins der Wörter vorkommen muss (licht, lichter, lampe, lampen, leuchte, leuchten), gefolgt von einem Worttrenner. Auf die Eingabe "Küchenlicht" würden nun die Keywords "kueche" und "light" matchen, da "Küchen" am Wortanfang und "licht" am Wortende steht.
deg: "\\b(\\d+)(?: grad)\\b",
Dies ist ein Beispiel für ein Keyword bei dem aus der Eingabe ein Wert extrahiert werden soll. Beispieleingabe: "Setze die Wohnzimmerheizung auf 21 Grad". Hier soll die "21" weiterverarbeitet werden, dazu ist es wichtig, dass nur dieser Teil extrahiert wird. Bei diesem Pattern steht zunächst einmal ein "\b" vorne und hinten, der ganzen Satzteil muss also zwischen zwei Leerzeichen/Worttrennern stehen. Der mittlere Teil des Patterns ist mittels Klammerung in zwei Teile getrennt, ein Teil beginnt ohne "?:", der andere mit "?:", wodurch die extraktion aktiv bzw. inaktiv ist. "\d+" steht für eine eine beliebig lange ("+") Ziffernfolge ("\d") und wird extrahiert. Das "grad" wird nicht extrahiert.
<size size="150">Rules</size>
Eine Regel hat einen Namen, ein Array von Keywords welche im Satz vorkommen müssen, ein Array von Keywords welche nicht vorkommen dürfen und eine Liste von States und Werten, welche gesetzt werden, falls die Regel aktiv wird.
Beispiel:
{ name: "Küchenlicht einschalten" keywords: [kueche, light, on], keywordsNot: [get], setStates: { 'hm-rpc.0.LEQ123456789.1.LEVEL' : 100, 'adapter.0.javascript.speech2ioBroker.response' : 'Küchenlicht eingeschaltet.' }, }
In diesem Beispiel wird die Regel ausgeführt, wenn in einem Satz die Pattern kueche, light und on vorkommen, und nicht get.
Es wird dann ein State auf 100% gesetzt und außerdem ein Antwortsatz ausgegeben.
Der Wert kann auch durch ein Keyword ersetzt werden, warum das wichtig ist, wird an diesem Beispiel deutlich:
{ name: "Küchenlicht dimmen" keywords: [kueche, light, pct], keywordsNot: [get], setStates: { 'hm-rpc.0.LEQ123456789.1.LEVEL' : '<pct>', 'adapter.0.javascript.speech2ioBroker.response' : 'Küchenlicht auf <pct> Prozent gedimmt.'; }, }</pct></pct>
Diese Regel würde beispielsweise bei folgendem Satz aktiv: "Das Küchenlicht auf 50% dimmen".
Dabei wird die "50" extrahiert und beim setzen der States eingefügt. Extrahiert wird bei den Patterns immer der Teil, welcher in Klammern steht, aber nicht mit "?:" startet.
Außerdem kann der Wert auch ein Javascript sein, welches die extrahierten Keywords umrechnen kann und dynamischere Ausgaben erzeugen kann.
Templates
Da viele Regeln wiederholt vorkommen, lediglich mit anderen Keywords (Küchenlicht, Wohnzimmerlicht, …) lohnt es sich dafür Templates zu bauen. Ich habe das exemplarisch für Dimmbare Lichter (get+set) und Temperaturausgabe (get) gemacht, siehe Skript.
Bei Licht habe ich gleich ein/aus/dimmen in eine einzige Regel verpackt.
<size size="150">Updates</size>
21.01.2016: responseId ändern, falls Befehl nicht verstanden wurde.
<size size="150">Skript</size>
var textId = "speech2ioBroker.text"; var responseId = "speech2ioBroker.response"; var adapter = "javascript.0."; createState(textId, ""); createState(responseId, ""); var patterns = { wohnung: "\\b(wohnungs?)", terasse: "\\b(terassen?|balkon)", wohnzimmer: "\\b(wohnzimmer)", kueche: "\\b(küchen?)", badezimmer: "\\b(badezimmer|bad)", wc: "\\b(gästebad|klo|wc)", arbeitszimmer: "\\b(arbeitszimmer)", gaestezimmer: "\\b(gästezimmer)", schlafzimmer: "\\b(schlafzimmer)", sofa: "\\b(sofa|couch)", esstisch: "\\b(esstisch)", buecherregal: "\\b(bücherregal)", light: "(lichte?r?|lampen?|leuchten?)\\b", heating: "(heizunge?n?|fußbodenheizunge?n?|warm)\\b", tv: "(fernseher|tv)\\b", windows: "(türe?|fenster)\\b", door: "\\b(türklingel|klingel|geklingelt)\\b", set: "(schalt|mach|dimm|setz|stell|änder)(e?n?)\\b", get: "\\b((?:an)?zeige?n?|(?:an)sage?n?|sind|ist)\\b", on: "\\b(ein|an)(?:$|\\B)", off: "\\b(aus|ab)(?:$|\\B)", open: "\\b(offen|geöffnet|auf)", closed: "\\b(geschlossen|zu)", pct: "\\b(\\d+)(?:%| prozent)(?:\\b|$|[^%])", deg: "\\b(\\d+)(?: grad)\\b", color: "\\b(weiß|rot|grün|blau|gelb|lila|rosa|pink|türkis|orange)(?:n?e?s?)\\b" }; var rules = [ templateGetTemperature("get Wohnzimmertemperatur", ['wohnzimmer', 'heating', 'get'], [], "hm-rpc.0.LEQ123456.1.TEMPERATURE"), templateSetLightDimmable("set Wohnzimmerlicht", ['wohnzimmer','light'], ['get'], "scene.licht.wohnzimmer"), templateGetLightDimmable("get Wohnzimmerlicht", ['wohnzimmer', 'light', 'get'], [], "scene.licht.wohnzimmer"), templateSetLightDimmable("set Sofalicht", ['sofa','light'], ['get'], "hue.0.hue_bridge.Wohnzimmer.level"), templateGetLightDimmable("get Sofalicht", ['sofa', 'light', 'get'], [], "hue.0.hue_bridge.Wohnzimmer.level"), ]; function templateSetLightDimmable(name, keys, keysNot, levelId) { var tmp = {}; tmp[levelId] = "var on = '<on>'; "+ "var off = '<off>'; "+ "var pct = '<pct>'; "+ "var level = 0; "+ "if (on !== 'undefined') {level = 100;} "+ "else if (off !== 'undefined') {level = 0;} "+ "else if (pct !== 'undefined') {level = parseInt(pct);} "+ "level; "; tmp[responseId] = "var on = '<on>'; "+ "var off = '<off>'; "+ "var pct = '<pct>'; "+ "var out = 'Licht ausgeschaltet.'; "+ "if (on !== 'undefined') {out = 'Licht eingeschaltet.'} "+ "else if (off !== 'undefined') {out = 'Licht ausgeschaltet.'} "+ "else if (pct !== 'undefined') {out = 'Licht auf '+pct+'% gedimmt.'} "+ "out; "; return { name: name, keywords: keys, keywordsNot: keysNot, setStates: tmp, }; } function templateGetLightDimmable(name, keys, keysNot, levelId) { var tmp = {}; tmp[responseId] = "var state = getState('"+levelId+"'); "+ "var out = ''; "+ "if (state.val === 'undefined' || state.val === null) {out = 'Lichtstatus unbekannt.'} "+ "if (state.val === 'uncertain') {out = 'Das Licht ist teilweise eingeschaltet.'} "+ "else if (parseInt(state.val) === 0) {out = 'Das Licht ist ausgeschaltet.'} "+ "else if (parseInt(state.val) === 100) {out = 'Das Licht ist eingeschaltet.'} "+ "else {out = 'Das Licht ist auf '+state.val+'% gedimmt.'} "+ "out; "; return { name: name, keywords: keys, keywordsNot: keysNot, setStates: tmp, }; } function templateGetTemperature(name, keys, keysNot, levelId) { var tmp = {}; tmp[responseId] = "var state = getState('"+levelId+"'); "+ "var out = ''; "+ "if (state.val === 'undefined' || state.val === null || state.val === 'uncertain') {out = 'Temperatur unbekannt.'} "+ "else {out = 'Es ist '+state.val+' Grad warm.'} "+ "out; "; return { name: name, keywords: keys, keywordsNot: keysNot, setStates: tmp, }; } on({id: adapter+textId, change: 'any'}, function (textObj) { log(textObj.newState.val); var text = textObj.newState.val.toLowerCase(); var keywords = []; var values = []; var matchedRules = Array(rules.length).fill(0); Object.getOwnPropertyNames(patterns).forEach(function(str){ var match = text.match(patterns[str]); if(match){ keywords.push(str); values.push(match[1]); matchedRules.forEach(function(val,index){ if(val < 0) return; if (rules[index].keywords.indexOf(str) > -1){ matchedRules[index]++; } if (rules[index].keywordsNot.indexOf(str) > -1){ matchedRules[index] = -1; } }); } }); log('keywords: '+keywords); var ruleFound = false; matchedRules.forEach(function(val,index){ if(val < rules[index].keywords.length) return; log("found rule "+rules[index].name); ruleFound = true; Object.getOwnPropertyNames(rules[index].setStates).forEach(function(str){ var value = rules[index].setStates[str]; if (typeof value == "string"){ var vars = value.match("<(\\w+)>"); while (vars !== null && vars.length > 1) { value = value.replace('<'+vars[1]+'>',values[keywords.indexOf(vars[1])]); vars = value.match("<(\\w+)>"); } } var finalVal = ""; try { finalVal = eval(value); }catch (e) { log("eval error: " + e); finalVal = value; } log("setting state "+str+" to "+finalVal); setState(str, finalVal, false); }); }); if (!ruleFound){ setState(responseId, "Befehl nicht verstanden.", false); } });</pct></off></on></pct></off></on>
-
Wow!
Das ist genau das, was ich schon längst schreiben wollte.
Nun wollte ich einen Adapter daraus machen.
Dein Skript wird definitiv als Basis benutzt.
Ich wollte das Skript nicht nur für Speech benutzen, sondern auch als Chat bot. Z.b Telegramm oder Skype oder email, pushbullet.
Will nur denn (irgendwann ) erweitern, dass auch Antwort gesendet wird und auch ob bei dem passenden Regel angehalten werden muss oder weiter gesucht.
-
Wow!
Das ist genau das, was ich schon längst schreiben wollte.
Nun wollte ich einen Adapter daraus machen.
Dein Skript wird definitiv als Basis benutzt.
Ich wollte das Skript nicht nur für Speech benutzen, sondern auch als Chat bot. Z.b Telegramm oder Skype oder email, pushbullet.
Will nur denn (irgendwann ) erweitern, dass auch Antwort gesendet wird und auch ob bei dem passenden Regel angehalten werden muss oder weiter gesucht. `
Ein einfach zu konfigurierender Adapter wäre prima, leider kenne ich mit dem Admin Interface so gar nicht aus
.
Woher die Eingabe kommt, also ob Sprache oder Textchat, ist dem Skript egal. Man kann damit natürlich auch einfach einen State für Email usw. überwachen.
Das Skript wertet alle Regeln aus, die auf den Input zutreffen, nicht nur die erste. Um falsche Regelerkennung auszuschließen kann man die Keyword-Blacklist nutzen, das klappt bei mir ganz gut. Manchmal kann es natürlich auch gewollt sein, dass mehrere Regeln aktiv werden.
Als Antwort kann man in beliebige States schreiben oder aber auch ein Skript eingeben, welches ggf. direkt eine Email sendet.
Die eigentliche Arbeit im Skript passiert in wenigen Zeilen Code, der Erfolg hängt natürlich stark von den Patterns und Rules ab, wobei ich mit denen im Skript wirklich sehr frei formulierte Sätze in den richtigen Befehl umwandeln kann, wichtig ist nur, dass im Satz der Ort ("Wohnzimmer"), das Objekt ("Licht") und der Befehl ("an"/"aus"/"xx%") vorkommt.
Wichtig ist auch, dass man die Patterns gut definiert. In der Deutschen Sprache gibt es viele Wortzusammensetzungen, daher habe ich immer definiert, ob ein Pattern am Wortanfang, Wortende oder ganz alleine stehen muss. Am einfachsten ist das Verbessern der Pattern aber, wenn man einen Satz gefunden hat, der falsch erkannt wird, oder einen der nicht richtig erkannt wird. Dann kann man die Patterns relativ einfach anpassen.
-
Hallo Pman,
ich hab dein Skript ausprobiert und bin begeistert! Super Arbeit!
Mit einigen Anpassungen hat es gleich geklappt, die Aussentemperatur abzufragen und den Dimmer im WC zu bedienen. Natürlich braucht es für die ganze Wohnung einiges an Anpassungen, aber der scenes-Adapter spielt da ja sehr gut rein. Die andere Seite der Ausgabe wird super vom Sayit-Adapter bedient. Ist ja schließlich kein Problem die response Variable mit der Sayit.text zu synchronisieren und ggf. noch eine Funktion zur Prüfung der Aussprache einzubauen (aus Punkt wird Komma, aus ° wird Grad, etc.).
Mir bereitet allerdings noch die Eingabe-Seite Kopfzerbrechen. Ich will ja meine Kommandos nicht eintippen :lol: und es kommt nur eine offline-Spracherkennung ins Haus. Ich habe mir http://jasperproject.github.io mal angesehen. Das läuft auf einem Pi und hat eine API. Was hast du im Kopf?
Nochmal: Tolle Arbeit!
Gruß,
Pix
-
Das liest sich sehr gut, was du da gebaut hast. Kann man denn auch schon deine Android App testen?
Momentan nutze ich das alles noch über den Umweg mit Tasker, geht auch, aber ein Adapter mit dazugehöriger App wäre natürlich wesentlich genialer
-
@pix:Mir bereitet allerdings noch die Eingabe-Seite Kopfzerbrechen. Ich will ja meine Kommandos nicht eintippen :lol: und es kommt nur eine offline-Spracherkennung ins Haus. Ich habe mir http://jasperproject.github.io mal angesehen. Das läuft auf einem Pi und hat eine API. Was hast du im Kopf? `
Ich nutze die Spracherkennung von Android, diese funktioniert auch offline. Vorteil ist, dass man nichts konfigurieren muss und die Spracherkennung auch bei schwierigen akustischen Situationen sehr gut funktioniert. Ab dem SDK für Android 6.0 kann man sogar offline Erkennung erzwingen, vorher wird diese nur genutzt wenn das Gerät nicht Online ist.Ich habe vor mir ein Wandtablet zu bauen, worauf die Android App laufen soll. Perfekt wäre das in Kombination mit einem Bluetooth Headset bzw. nur Mikrofon, da habe ich aber noch nichts passendes gefunden.
Das liest sich sehr gut, was du da gebaut hast. Kann man denn auch schon deine Android App testen?
Momentan nutze ich das alles noch über den Umweg mit Tasker, geht auch, aber ein Adapter mit dazugehöriger App wäre natürlich wesentlich genialer
`
Die App wird noch entwickelt, nutzbar noch nicht wirklich. Vor allem weil ein ioBroker Adapter oder Skript fehlt, welches die nötigen States automatisiert anlegt. Die States habe ich bei mir manuell in Admin angelegt. Ein wenig mehr dazu hier: -
Hallo alle
Ich wollte auch mal experimentieren und habe da Script im IBroker unter Sripts reinkopiert.
Und wie gehts jetzt weiter?
Müsste ich jetzt nicht unter Zustände ein javascript.0.speech2ioBroker.text Objekt finden in das ich "Wohnzimmer Licht an" schreiben kann?
Hab ich aber nicht. Nur unter Objekte gib es die.
Gruss Ralf
-
Hallo,
der Thread ist genau ein Jahr alt, was nicht heisst, dass das SKript nicht funktioniert.
Es gibt mittlerweile den text2command - Adapter.
Gruß
Pix
-
Hi Pix
Text2command finde ich nicht unter den angeboteen Adaptern
Sollte der nicht unter "Scripte und Logik" auftauchen? Ich habe die Adapterinfomationen aktualisiert, aber der kommt nicht…
Gruss Ralf
-
Hallo ralf,
@derrapf:Text2command finde ich nicht unter den angeboteen Adaptern
Sollte der nicht unter "Scripte und Logik" auftauchen? `
Genau da:
sonst mach es mal wie ich und such mit Strg-F
Gruß
RAiner
-
Hallo Rainer
Schon gemacht. Da is nix. Bei Dir ist es übrigens auch unter "Scripte und Logik".
Ich hab da nur die ersten drei.
Gruss Ralf
-
Hallo Rainer
Schon gemacht. Da is nix. Bei Dir ist es übrigens auch unter "Scripte und Logik".
Ich hab da nur die ersten drei.
Gruss Ralf `
Was für eine Repository verwendest du? -
Äh…keine Ahnung. Was ist das? Wo steht das? Wie finde ich es raus?
Gruss Ralf
-
System-Einstellungen - ganz rechts oben das Zahnrad
Dann unter Haupteinstellungen der aktive Verwahrungsort
den dort ausgewählten bitte auf dem nächsten Reiter "Verwahrungsorte" nachschlagen und den Pfad hier posten.
Gruß
Rainer
-
Ok. Danke.
Das sieht bei mir dann so aus:
default conf/sources-dist.json online https://raw.githubusercontent.com/ioBroker/ioBroker.js-controller/master/conf/sources-dist.json
Gruss Ralf
-
Und welche davon ist in den Haupteinstellungen ausgewählt?
Wie alt ist dein admin?
Stell auf jeden Fall mal auf online und mach einen refresh unter "Adapter "
Gruß
Rainer
-
Hallo Rainer
Das wars!
Nachdem ich von default auf online gestellt hatte und neu refreshed hatte, war's da.
Jetzt teste ich ein bischen am Wochenende.
Der Admin und Vis usw. ist übrigens leider ziemlich alt; aber das liegt daran, dass ich nicht updaten kann.
Jedesmal wenn ich VIS oder den Admin update, zerreist es den Iobroker. Das liegt wohl an der alten note.js Version.
Ich habe es aber auch noch nicht hinbekommen ibroker auf dem Bananapi mit Jessie zum laufen zu bringen.
Ich bekomme schon initial kein Note.js drauf. Ich hab mich vor knapp einem Jahr mal Wochen damit beschäftigt (Du warst glab ich auch in dem thread beteiligt) und hab dann aber aufgegeben.
Gruss Ralf
-
Hab jetzt doch noch rumgespielt. Irgend was stimmt nicht. Nach der Installation wollte ich wie üblich den Adapter konfigurieren:
Die Anleitung unter http://www.iobroker.net/docu/?page_id=4711&lang=en kann ich aber auch nicht nachvollziehen, denn ich habe z.B. nicht den angesprochenen Tab-Reiter "Adapter configuration". Die Screenshots sehen auch ganz anders aus wie bei mir (weil ich noch eine alte Version habe?)Ui und das sieht auch nicht gesund aus:
Gruss Ralf -
Hi Ihr, Ich weiss das ist ein Alter Post, aber ich finde Ihn Ideal.
OK, Ich bin anfänger und ein bisschen voll im Kopf mit dem Input der Installation und einrichtung, (inkl. fehler behebung ) Ich bin also fragebedürftig.
Was möchte Ich: am liebsten in mein micro am Pi und Patop Handy ect reden und verstanden werden ( Also nicht wie meine Frau ^^ ) Also ohne eine Taste zu drücken. wie lich an, und es geht an.
Dose Ist Installiert, eingetragen und lässt sich brav über vis steuern.
Die überschrift bedeutet "speech2ioBroker"
im text liest man nun "text2command"
wobei ich nicht erkennen kann wo der zusammenhang sein soll.
habe es zwar installiert, und reagiert auch, aber ich rede ja und übertrage keine geschriebenen worte aus meinen Mund ^^
( glaube ich wenigstens nicht ^^ )
Wie habt ihr das in den Begriff bekommen ?
Lg
p.s. kein Alexa bitte ^^, kenne auch andere namen wie alexa ^^
-
Hi
Speech2iobroker war mal ein Script das aber inzwischen überholt ist und durch text2command ersetzt wurde.
text2command bietet die Mögichkeit text den Du in den Datenpunkt text2command.0.text reinschreibst in Befehle umzuwandeln.
Daszu musst Du in der Konfiguratinsoberfläche von text2command entsprechende keywords einrichten worauf er reagieren soll. Beispiel:
Wie kommt der Text in die Variable?-
Entweder Du schreibst ihn manuell rein (Macht nur zu Testen Sinn)
-
Du schreibst ein Script das ihn da reinschreibt
-
Du (und das ist was Du willst) baust das Widget mit dem Namen "Speech to Text" in Deine VIS-View ein. Das Widget hört ständig zu, versucht zu erkennen was Du sagt und schreibt das dann in text2command.0.text.
Gruss Ralf
-