diff --git a/src/main/kotlin/dev/zerozipp/Main.kt b/src/main/kotlin/dev/zerozipp/Main.kt index e5e9451..3cc3529 100644 --- a/src/main/kotlin/dev/zerozipp/Main.kt +++ b/src/main/kotlin/dev/zerozipp/Main.kt @@ -1,21 +1,23 @@ package dev.zerozipp import dev.zerozipp.assistant.* +import dev.zerozipp.assistant.languages.* import javax.sound.sampled.* -import kotlinx.coroutines.* import org.vosk.* -fun main() = runBlocking { +fun main() { LibVosk.setLogLevel(LogLevel.WARNINGS) val format = AudioFormat(16000f, 16, 1, true, false) val assistant = System.getenv("HA_URL") ?: error("Set HA_URL env var") val token = System.getenv("HA_TOKEN") ?: error("Set HA_TOKEN env var") - val name = System.getenv("COMMAND_NAME") ?: "computer" + val name = System.getenv("ASSISTANT_NAME") ?: "computer" + val lang = System.getenv("ASSISTANT_LANG") ?: "english" val vosk = System.getenv("VOSK_MODEL") ?: "vosk" Home(assistant, token).use { home -> - val formatted: String = name.trim().lowercase() - Assistant(vosk, formatted, home, format).use { + val formatted = name.trim().lowercase() + val dict = if(lang == "german") German() else English() + Assistant(vosk, home, dict, formatted, format).use { assistant -> assistant.listen() } } diff --git a/src/main/kotlin/dev/zerozipp/assistant/Action.kt b/src/main/kotlin/dev/zerozipp/assistant/Action.kt new file mode 100644 index 0000000..c58aac0 --- /dev/null +++ b/src/main/kotlin/dev/zerozipp/assistant/Action.kt @@ -0,0 +1,12 @@ +package dev.zerozipp.assistant + +abstract class Action { + abstract val triggers: Set + abstract val options: Map + + fun run(command: String) = triggers.forEach { + if(!command.startsWith(it)) return@forEach + val option = command.replaceFirst(it, "") + options[option.trim()]?.run() + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/zerozipp/assistant/Assistant.kt b/src/main/kotlin/dev/zerozipp/assistant/Assistant.kt index 2bf0e32..0d17932 100644 --- a/src/main/kotlin/dev/zerozipp/assistant/Assistant.kt +++ b/src/main/kotlin/dev/zerozipp/assistant/Assistant.kt @@ -2,21 +2,26 @@ package dev.zerozipp.assistant import org.vosk.Model import org.vosk.Recognizer +import dev.zerozipp.assistant.actions.* import javax.sound.sampled.* import com.google.gson.* -class Assistant(path: String, +class Assistant( + path: String, home: Home, + private val dict: Dictionary, private val name: String, - private val home: Home, format: AudioFormat ) : AutoCloseable { private val model = Model(path) - private val microphone = line(format) private val buffer = ByteArray(4096) - private val action = "mach das licht" + private val microphone = line(format) + private val actions = listOf( + Light(home) + ) + private val recognizer = Recognizer( model, format.sampleRate, Gson().toJson( - listOf(name, action) + Light.modes.keys + listOf(name) + dict.words ) ) @@ -26,19 +31,19 @@ class Assistant(path: String, AudioSystem.getLine(info) as TargetDataLine }.also { it.open(format) } - private suspend fun process() { + private fun process() { val bytes = microphone.read(buffer, 0, buffer.size) if(bytes <= 0 || !recognizer.acceptWaveForm(buffer, bytes)) return val json = Gson().fromJson(recognizer.result, JsonObject::class.java) - val text = json.let { it["text"].asString.orEmpty().trim() }.split(action) + val text = json.let { it["text"].asString.orEmpty().trim() } - text.takeIf { it.size == 2 }?.also { (name, key) -> - Light.modes[key.trim()]?.takeIf { name.trim() == this.name } - ?.let { home.light("all", it.state, it.level, it.color) } - } + if(!text.startsWith(name)) return + val command = text.replaceFirst("name", text) + val translated = dict.translate(command.trim()) + actions.forEach { it.run(translated) } } - suspend fun listen() { + fun listen() { microphone.start() while(true) process() } diff --git a/src/main/kotlin/dev/zerozipp/assistant/Dictionary.kt b/src/main/kotlin/dev/zerozipp/assistant/Dictionary.kt new file mode 100644 index 0000000..2526d4b --- /dev/null +++ b/src/main/kotlin/dev/zerozipp/assistant/Dictionary.kt @@ -0,0 +1,8 @@ +package dev.zerozipp.assistant + +abstract class Dictionary { + abstract val mappings: Map + val words: Set get() = mappings.keys + fun translate(text: String) = text.split(" ") + .mapNotNull { mappings[it] }.joinToString(" ") +} \ No newline at end of file diff --git a/src/main/kotlin/dev/zerozipp/assistant/Home.kt b/src/main/kotlin/dev/zerozipp/assistant/Home.kt index 6ca5ea3..5de2e36 100644 --- a/src/main/kotlin/dev/zerozipp/assistant/Home.kt +++ b/src/main/kotlin/dev/zerozipp/assistant/Home.kt @@ -1,6 +1,7 @@ package dev.zerozipp.assistant import com.google.gson.* +import kotlinx.coroutines.* import io.ktor.client.engine.cio.* import io.ktor.client.request.* import io.ktor.client.* @@ -13,8 +14,8 @@ class Home( override fun close() = http.close() private val http = HttpClient(CIO) - suspend fun light(entity: String, - state: Boolean, level: Int?, color: String?) { + fun light(entity: String, state: Boolean, + level: Int?, color: String?) = runBlocking { val service = if(state) "turn_on" else "turn_off" val url = "$host/api/services/light/$service" val type = ContentType.Application.Json diff --git a/src/main/kotlin/dev/zerozipp/assistant/Light.kt b/src/main/kotlin/dev/zerozipp/assistant/Light.kt deleted file mode 100644 index 388a2ea..0000000 --- a/src/main/kotlin/dev/zerozipp/assistant/Light.kt +++ /dev/null @@ -1,37 +0,0 @@ -package dev.zerozipp.assistant - -object Light { - val on = Mode(true, null, null) - val off = Mode(false, null, null) - fun color(name: String) = Mode(true, name, null) - fun level(level: Int) = Mode(true, null, level) - - data class Mode( - val state: Boolean, - val color: String?, - val level: Int? - ) - - val modes = mapOf( - "rot" to color("red"), - "grün" to color("green"), - "blau" to color("blue"), - "weiß" to color("white"), - "gelb" to color("yellow"), - "orange" to color("orange"), - "lila" to color("purple"), - "rosa" to color("pink"), - "türkis" to color("cyan"), - "magenta" to color("magenta"), - "braun" to color("brown"), - "grau" to color("gray"), - "schwarz" to color("black"), - "maximal" to level(255), - "minimal" to level(0), - "dunkel" to level(64), - "mittel" to level(128), - "hell" to level(191), - "aus" to off, - "an" to on - ) -} \ No newline at end of file diff --git a/src/main/kotlin/dev/zerozipp/assistant/actions/Light.kt b/src/main/kotlin/dev/zerozipp/assistant/actions/Light.kt new file mode 100644 index 0000000..e27070e --- /dev/null +++ b/src/main/kotlin/dev/zerozipp/assistant/actions/Light.kt @@ -0,0 +1,22 @@ +package dev.zerozipp.assistant.actions + +import dev.zerozipp.assistant.Action +import dev.zerozipp.assistant.Home + +class Light(private val home: Home) : Action() { + private val on = Runnable { home.light("all", true, null, null) } + private val off = Runnable { home.light("all", false, null, null) } + private fun color(name: String) = Runnable { home.light("all", true, null, name) } + private fun level(level: Int) = Runnable { home.light("all", true, level, null) } + override val triggers = setOf("turn the light", "light") + + override val options = mapOf( + "maximum" to level(255), "minimum" to level(0), + "dark" to level(64), "mid" to level(128), "bright" to level(191), + "red" to color("red"), "green" to color("green"), "blue" to color("blue"), + "white" to color("white"), "yellow" to color("yellow"), "orange" to color("orange"), + "purple" to color("purple"), "pink" to color("pink"), "magenta" to color("magenta"), + "cyan" to color("cyan"), "brown" to color("brown"), + "on" to on, "off" to off + ) +} \ No newline at end of file diff --git a/src/main/kotlin/dev/zerozipp/assistant/languages/English.kt b/src/main/kotlin/dev/zerozipp/assistant/languages/English.kt new file mode 100644 index 0000000..2aacbd8 --- /dev/null +++ b/src/main/kotlin/dev/zerozipp/assistant/languages/English.kt @@ -0,0 +1,16 @@ +package dev.zerozipp.assistant.languages + +import dev.zerozipp.assistant.Dictionary + +class English : Dictionary() { + override val mappings = mapOf( + "turn" to "turn", "the" to "the", "light" to "light", + "maximum" to "maximum", "minimum" to "minimum", + "dark" to "dark", "mid" to "mid", "bright" to "bright", + "red" to "red", "green" to "green", "blue" to "blue", + "white" to "white", "yellow" to "yellow", "orange" to "orange", + "purple" to "purple", "pink" to "pink", "magenta" to "magenta", + "cyan" to "cyan", "brown" to "brown", + "on" to "on", "off" to "off" + ) +} \ No newline at end of file diff --git a/src/main/kotlin/dev/zerozipp/assistant/languages/German.kt b/src/main/kotlin/dev/zerozipp/assistant/languages/German.kt new file mode 100644 index 0000000..306fdea --- /dev/null +++ b/src/main/kotlin/dev/zerozipp/assistant/languages/German.kt @@ -0,0 +1,16 @@ +package dev.zerozipp.assistant.languages + +import dev.zerozipp.assistant.Dictionary + +class German : Dictionary() { + override val mappings = mapOf( + "mach" to "turn", "the" to "das", "licht" to "light", + "maximum" to "maximum", "minimum" to "minimum", + "dunkel" to "dark", "mitte" to "mid", "hell" to "bright", + "rot" to "red", "grün" to "green", "blau" to "blue", + "weiß" to "white", "gelb" to "yellow", "orange" to "orange", + "lila" to "purple", "rosa" to "pink", "magenta" to "magenta", + "türkis" to "cyan", "braun" to "brown", + "an" to "on", "aus" to "off" + ) +} \ No newline at end of file