update
This commit is contained in:
@@ -1,21 +1,23 @@
|
|||||||
package dev.zerozipp
|
package dev.zerozipp
|
||||||
|
|
||||||
import dev.zerozipp.assistant.*
|
import dev.zerozipp.assistant.*
|
||||||
|
import dev.zerozipp.assistant.languages.*
|
||||||
import javax.sound.sampled.*
|
import javax.sound.sampled.*
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import org.vosk.*
|
import org.vosk.*
|
||||||
|
|
||||||
fun main() = runBlocking {
|
fun main() {
|
||||||
LibVosk.setLogLevel(LogLevel.WARNINGS)
|
LibVosk.setLogLevel(LogLevel.WARNINGS)
|
||||||
val format = AudioFormat(16000f, 16, 1, true, false)
|
val format = AudioFormat(16000f, 16, 1, true, false)
|
||||||
val assistant = System.getenv("HA_URL") ?: error("Set HA_URL env var")
|
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 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"
|
val vosk = System.getenv("VOSK_MODEL") ?: "vosk"
|
||||||
|
|
||||||
Home(assistant, token).use { home ->
|
Home(assistant, token).use { home ->
|
||||||
val formatted: String = name.trim().lowercase()
|
val formatted = name.trim().lowercase()
|
||||||
Assistant(vosk, formatted, home, format).use {
|
val dict = if(lang == "german") German() else English()
|
||||||
|
Assistant(vosk, home, dict, formatted, format).use {
|
||||||
assistant -> assistant.listen()
|
assistant -> assistant.listen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/main/kotlin/dev/zerozipp/assistant/Action.kt
Normal file
12
src/main/kotlin/dev/zerozipp/assistant/Action.kt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package dev.zerozipp.assistant
|
||||||
|
|
||||||
|
abstract class Action {
|
||||||
|
abstract val triggers: Set<String>
|
||||||
|
abstract val options: Map<String, Runnable>
|
||||||
|
|
||||||
|
fun run(command: String) = triggers.forEach {
|
||||||
|
if(!command.startsWith(it)) return@forEach
|
||||||
|
val option = command.replaceFirst(it, "")
|
||||||
|
options[option.trim()]?.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,21 +2,26 @@ package dev.zerozipp.assistant
|
|||||||
|
|
||||||
import org.vosk.Model
|
import org.vosk.Model
|
||||||
import org.vosk.Recognizer
|
import org.vosk.Recognizer
|
||||||
|
import dev.zerozipp.assistant.actions.*
|
||||||
import javax.sound.sampled.*
|
import javax.sound.sampled.*
|
||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
|
|
||||||
class Assistant(path: String,
|
class Assistant(
|
||||||
|
path: String, home: Home,
|
||||||
|
private val dict: Dictionary,
|
||||||
private val name: String,
|
private val name: String,
|
||||||
private val home: Home,
|
|
||||||
format: AudioFormat
|
format: AudioFormat
|
||||||
) : AutoCloseable {
|
) : AutoCloseable {
|
||||||
private val model = Model(path)
|
private val model = Model(path)
|
||||||
private val microphone = line(format)
|
|
||||||
private val buffer = ByteArray(4096)
|
private val buffer = ByteArray(4096)
|
||||||
private val action = "mach das licht"
|
private val microphone = line(format)
|
||||||
|
private val actions = listOf<Action>(
|
||||||
|
Light(home)
|
||||||
|
)
|
||||||
|
|
||||||
private val recognizer = Recognizer(
|
private val recognizer = Recognizer(
|
||||||
model, format.sampleRate, Gson().toJson(
|
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
|
AudioSystem.getLine(info) as TargetDataLine
|
||||||
}.also { it.open(format) }
|
}.also { it.open(format) }
|
||||||
|
|
||||||
private suspend fun process() {
|
private fun process() {
|
||||||
val bytes = microphone.read(buffer, 0, buffer.size)
|
val bytes = microphone.read(buffer, 0, buffer.size)
|
||||||
if(bytes <= 0 || !recognizer.acceptWaveForm(buffer, bytes)) return
|
if(bytes <= 0 || !recognizer.acceptWaveForm(buffer, bytes)) return
|
||||||
val json = Gson().fromJson(recognizer.result, JsonObject::class.java)
|
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) ->
|
if(!text.startsWith(name)) return
|
||||||
Light.modes[key.trim()]?.takeIf { name.trim() == this.name }
|
val command = text.replaceFirst("name", text)
|
||||||
?.let { home.light("all", it.state, it.level, it.color) }
|
val translated = dict.translate(command.trim())
|
||||||
}
|
actions.forEach { it.run(translated) }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun listen() {
|
fun listen() {
|
||||||
microphone.start()
|
microphone.start()
|
||||||
while(true) process()
|
while(true) process()
|
||||||
}
|
}
|
||||||
|
|||||||
8
src/main/kotlin/dev/zerozipp/assistant/Dictionary.kt
Normal file
8
src/main/kotlin/dev/zerozipp/assistant/Dictionary.kt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package dev.zerozipp.assistant
|
||||||
|
|
||||||
|
abstract class Dictionary {
|
||||||
|
abstract val mappings: Map<String, String>
|
||||||
|
val words: Set<String> get() = mappings.keys
|
||||||
|
fun translate(text: String) = text.split(" ")
|
||||||
|
.mapNotNull { mappings[it] }.joinToString(" ")
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package dev.zerozipp.assistant
|
package dev.zerozipp.assistant
|
||||||
|
|
||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import io.ktor.client.engine.cio.*
|
import io.ktor.client.engine.cio.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
@@ -13,8 +14,8 @@ class Home(
|
|||||||
override fun close() = http.close()
|
override fun close() = http.close()
|
||||||
private val http = HttpClient(CIO)
|
private val http = HttpClient(CIO)
|
||||||
|
|
||||||
suspend fun light(entity: String,
|
fun light(entity: String, state: Boolean,
|
||||||
state: Boolean, level: Int?, color: String?) {
|
level: Int?, color: String?) = runBlocking {
|
||||||
val service = if(state) "turn_on" else "turn_off"
|
val service = if(state) "turn_on" else "turn_off"
|
||||||
val url = "$host/api/services/light/$service"
|
val url = "$host/api/services/light/$service"
|
||||||
val type = ContentType.Application.Json
|
val type = ContentType.Application.Json
|
||||||
|
|||||||
@@ -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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
22
src/main/kotlin/dev/zerozipp/assistant/actions/Light.kt
Normal file
22
src/main/kotlin/dev/zerozipp/assistant/actions/Light.kt
Normal file
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
16
src/main/kotlin/dev/zerozipp/assistant/languages/English.kt
Normal file
16
src/main/kotlin/dev/zerozipp/assistant/languages/English.kt
Normal file
@@ -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"
|
||||||
|
)
|
||||||
|
}
|
||||||
16
src/main/kotlin/dev/zerozipp/assistant/languages/German.kt
Normal file
16
src/main/kotlin/dev/zerozipp/assistant/languages/German.kt
Normal file
@@ -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"
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user