From ddd7fb7ed6c598d8141f000337df0212a6a57bc1 Mon Sep 17 00:00:00 2001 From: Florian Schrofner Date: Wed, 16 Feb 2022 23:10:26 +0100 Subject: [PATCH] add support for setting temperature, start apply command, refactor --- .../fi/schro/data/ConfigurationRepository.kt | 26 ++++++++++++++ .../fi/schro/data/ElgatoLightRepository.kt | 12 +++---- .../kotlin/fi/schro/data/LightRepository.kt | 25 +++++++++++--- src/nativeMain/kotlin/fi/schro/di/Modules.kt | 3 ++ .../kotlin/fi/schro/ui/HappyCatCommand.kt | 10 +++--- .../kotlin/fi/schro/util/FileUtil.kt | 34 +++++++++++++++++++ 6 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 src/nativeMain/kotlin/fi/schro/data/ConfigurationRepository.kt create mode 100644 src/nativeMain/kotlin/fi/schro/util/FileUtil.kt diff --git a/src/nativeMain/kotlin/fi/schro/data/ConfigurationRepository.kt b/src/nativeMain/kotlin/fi/schro/data/ConfigurationRepository.kt new file mode 100644 index 0000000..70d5e89 --- /dev/null +++ b/src/nativeMain/kotlin/fi/schro/data/ConfigurationRepository.kt @@ -0,0 +1,26 @@ +package fi.schro.data + +import fi.schro.util.FileUtil +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import platform.posix.fopen + +interface ConfigurationRepository { + suspend fun applyConfiguration(configurationFilePath: String, lightAddress: String, port: Int? = null) +} + +class ConfigurationRepositoryImpl( + private val lightRepository: LightRepository +): ConfigurationRepository { + override suspend fun applyConfiguration(configurationFilePath: String, lightAddress: String, port: Int?) { + val configString = FileUtil.readAllText(configurationFilePath) + TODO("Not yet implemented") + } + + private suspend fun applyLightStatus(lightAddress: String, port: Int?, status: LightStatus){ + val currentStatus = lightRepository.getLightStatus(lightAddress) + currentStatus.getNecessaryChanges(status)?.let { statusUpdate -> + lightRepository.setLightStatus(lightAddress, port, statusUpdate) + } + } +} \ No newline at end of file diff --git a/src/nativeMain/kotlin/fi/schro/data/ElgatoLightRepository.kt b/src/nativeMain/kotlin/fi/schro/data/ElgatoLightRepository.kt index 7f41b41..7c0421b 100644 --- a/src/nativeMain/kotlin/fi/schro/data/ElgatoLightRepository.kt +++ b/src/nativeMain/kotlin/fi/schro/data/ElgatoLightRepository.kt @@ -1,6 +1,6 @@ package fi.schro.data -import fi.schro.ui.LightPowerState +import fi.schro.ui.LightPowerStatus import io.ktor.client.* import io.ktor.client.request.* import io.ktor.client.statement.* @@ -8,7 +8,6 @@ import io.ktor.http.* import io.ktor.utils.io.core.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import platform.posix.stat import kotlin.math.roundToInt class ElgatoLightRepository(private val httpClient: HttpClient): LightRepository { @@ -73,7 +72,7 @@ data class ElgatoLight( ){ fun toLightStatus(): LightStatus { return LightStatus( - powerState = on?.let { LightPowerState.fromInt(it) }, + powerStatus = on?.let { LightPowerStatus.fromInt(it) }, brightness = brightness, temperature = temperature?.let { convertElgatoTemperatureToKelvin(it) } ) @@ -82,7 +81,7 @@ data class ElgatoLight( companion object { fun fromLightStatus(status: LightStatus): ElgatoLight { return ElgatoLight( - on = status.powerState?.intValue, + on = status.powerStatus?.intValue, brightness = status.brightness, temperature = status.temperature?.let { convertKelvinToElgatoTemperature(it) } ) @@ -90,9 +89,10 @@ data class ElgatoLight( } } +//TODO: these calculations seem weird, maybe they could be improved private fun convertKelvinToElgatoTemperature(kelvinTemperature: Int): Int { - //TODO: implement real conversion - return 319 + //based on: https://github.com/justinforlenza/keylight-control/blob/main/src/keylight.js + return (((kelvinTemperature - 1993300/201f) * 201f)/-4100f).roundToInt() } private fun convertElgatoTemperatureToKelvin(elgatoTemperature: Int): Int { diff --git a/src/nativeMain/kotlin/fi/schro/data/LightRepository.kt b/src/nativeMain/kotlin/fi/schro/data/LightRepository.kt index 7fdc0aa..48abb7a 100644 --- a/src/nativeMain/kotlin/fi/schro/data/LightRepository.kt +++ b/src/nativeMain/kotlin/fi/schro/data/LightRepository.kt @@ -1,22 +1,37 @@ package fi.schro.data -import fi.schro.ui.LightPowerState +import fi.schro.ui.LightPowerStatus +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable interface LightRepository { suspend fun setLightStatus(lightAddress: String, port: Int? = null, status: LightStatus) suspend fun getLightStatus(lightAddress: String, port: Int? = null): LightStatus } +@Serializable data class LightStatus( - val powerState: LightPowerState?, - val brightness: Int?, - val temperature: Int? + @SerialName("power") val powerStatus: LightPowerStatus?, + @SerialName("brightness") val brightness: Int?, + @SerialName("temperature") val temperature: Int? ){ override fun toString(): String { val stringList = mutableListOf() - powerState?.let { stringList.add("status: ${powerState.stringValue}") } + powerStatus?.let { stringList.add("power: ${powerStatus.stringValue}") } brightness?.let { stringList.add("brightness: $brightness") } temperature?.let { stringList.add("temperature: $temperature") } return stringList.joinToString(separator = "\n") } + + fun getNecessaryChanges(otherStatus: LightStatus): LightStatus? { + val statusUpdates = LightStatus( + powerStatus = if(powerStatus != otherStatus.powerStatus) otherStatus.powerStatus else null, + brightness = if(brightness != otherStatus.brightness) otherStatus.brightness else null, + temperature = if(temperature != otherStatus.temperature) otherStatus.temperature else null + ) + + return if(statusUpdates.powerStatus != null || statusUpdates.brightness != null || statusUpdates.temperature != null){ + statusUpdates + } else null + } } diff --git a/src/nativeMain/kotlin/fi/schro/di/Modules.kt b/src/nativeMain/kotlin/fi/schro/di/Modules.kt index 4004e94..b47d0b7 100644 --- a/src/nativeMain/kotlin/fi/schro/di/Modules.kt +++ b/src/nativeMain/kotlin/fi/schro/di/Modules.kt @@ -1,5 +1,7 @@ package fi.schro.di +import fi.schro.data.ConfigurationRepository +import fi.schro.data.ConfigurationRepositoryImpl import fi.schro.data.ElgatoLightRepository import fi.schro.data.LightRepository import fi.schro.ui.ApplyCommand @@ -18,6 +20,7 @@ val commandModule = module { val dataModule = module { single { ElgatoLightRepository(get()) } + single { ConfigurationRepositoryImpl(get()) } } val networkModule = module { diff --git a/src/nativeMain/kotlin/fi/schro/ui/HappyCatCommand.kt b/src/nativeMain/kotlin/fi/schro/ui/HappyCatCommand.kt index 031f00c..c491f65 100644 --- a/src/nativeMain/kotlin/fi/schro/ui/HappyCatCommand.kt +++ b/src/nativeMain/kotlin/fi/schro/ui/HappyCatCommand.kt @@ -16,7 +16,7 @@ import org.koin.core.component.inject const val ARG_TARGET_LAMP = "TARGET_LAMP" const val ARG_CONFIGURATION_FILE = "CONFIGURATION_FILE" -class HappyCatCommand: CliktCommand(), KoinComponent { +class HappyCatCommand: CliktCommand(name = "hc", help = "A commandline utility to control your elgato keylight"), KoinComponent { private val applyCommand: ApplyCommand by inject() private val getCommand: GetCommand by inject() private val setCommand: SetCommand by inject() @@ -48,12 +48,12 @@ class SetCommand( it in 1000..10000 } - private val powerState: LightPowerState? by option("-p", "--powerstate", help = "The state to be set").enum() + private val powerStatus: LightPowerStatus? by option("-p", "--power", help = "The power status to be set").enum() override fun run() { val status = LightStatus( - powerState = powerState, + powerStatus = powerStatus, brightness = brightness, temperature = temperature ) @@ -86,12 +86,12 @@ class ApplyCommand: CliktCommand(name = "apply", help = "Applies the given confi } } -enum class LightPowerState(val stringValue: String, val intValue: Int) { +enum class LightPowerStatus(val stringValue: String, val intValue: Int) { ON("ON", 1), OFF("OFF", 0); companion object { - fun fromInt(intValue: Int): LightPowerState? { + fun fromInt(intValue: Int): LightPowerStatus? { return values().firstOrNull { it.intValue == intValue } } } diff --git a/src/nativeMain/kotlin/fi/schro/util/FileUtil.kt b/src/nativeMain/kotlin/fi/schro/util/FileUtil.kt new file mode 100644 index 0000000..8a2522b --- /dev/null +++ b/src/nativeMain/kotlin/fi/schro/util/FileUtil.kt @@ -0,0 +1,34 @@ +package fi.schro.util + +import kotlinx.cinterop.ByteVar +import kotlinx.cinterop.allocArray +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.toKString +import platform.posix.fclose +import platform.posix.fgets +import platform.posix.fopen + +object FileUtil { + fun readAllText(filePath: String): String { + val returnBuffer = StringBuilder() + val file = fopen(filePath, "r") ?: + throw IllegalArgumentException("Cannot open input file $filePath") + + try { + memScoped { + val readBufferLength = 64 * 1024 + val buffer = allocArray(readBufferLength) + var line = fgets(buffer, readBufferLength, file)?.toKString() + while (line != null) { + returnBuffer.append(line) + line = fgets(buffer, readBufferLength, file)?.toKString() + } + } + } finally { + fclose(file) + } + + return returnBuffer.toString() + } + +} \ No newline at end of file