1
0
Fork 0

add support for setting temperature, start apply command, refactor

master
Florian Schrofner 2022-02-16 23:10:26 +01:00
parent 6451ede58a
commit ddd7fb7ed6
6 changed files with 94 additions and 16 deletions

View File

@ -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)
}
}
}

View File

@ -1,6 +1,6 @@
package fi.schro.data package fi.schro.data
import fi.schro.ui.LightPowerState import fi.schro.ui.LightPowerStatus
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.statement.* import io.ktor.client.statement.*
@ -8,7 +8,6 @@ import io.ktor.http.*
import io.ktor.utils.io.core.* import io.ktor.utils.io.core.*
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import platform.posix.stat
import kotlin.math.roundToInt import kotlin.math.roundToInt
class ElgatoLightRepository(private val httpClient: HttpClient): LightRepository { class ElgatoLightRepository(private val httpClient: HttpClient): LightRepository {
@ -73,7 +72,7 @@ data class ElgatoLight(
){ ){
fun toLightStatus(): LightStatus { fun toLightStatus(): LightStatus {
return LightStatus( return LightStatus(
powerState = on?.let { LightPowerState.fromInt(it) }, powerStatus = on?.let { LightPowerStatus.fromInt(it) },
brightness = brightness, brightness = brightness,
temperature = temperature?.let { convertElgatoTemperatureToKelvin(it) } temperature = temperature?.let { convertElgatoTemperatureToKelvin(it) }
) )
@ -82,7 +81,7 @@ data class ElgatoLight(
companion object { companion object {
fun fromLightStatus(status: LightStatus): ElgatoLight { fun fromLightStatus(status: LightStatus): ElgatoLight {
return ElgatoLight( return ElgatoLight(
on = status.powerState?.intValue, on = status.powerStatus?.intValue,
brightness = status.brightness, brightness = status.brightness,
temperature = status.temperature?.let { convertKelvinToElgatoTemperature(it) } 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 { private fun convertKelvinToElgatoTemperature(kelvinTemperature: Int): Int {
//TODO: implement real conversion //based on: https://github.com/justinforlenza/keylight-control/blob/main/src/keylight.js
return 319 return (((kelvinTemperature - 1993300/201f) * 201f)/-4100f).roundToInt()
} }
private fun convertElgatoTemperatureToKelvin(elgatoTemperature: Int): Int { private fun convertElgatoTemperatureToKelvin(elgatoTemperature: Int): Int {

View File

@ -1,22 +1,37 @@
package fi.schro.data package fi.schro.data
import fi.schro.ui.LightPowerState import fi.schro.ui.LightPowerStatus
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
interface LightRepository { interface LightRepository {
suspend fun setLightStatus(lightAddress: String, port: Int? = null, status: LightStatus) suspend fun setLightStatus(lightAddress: String, port: Int? = null, status: LightStatus)
suspend fun getLightStatus(lightAddress: String, port: Int? = null): LightStatus suspend fun getLightStatus(lightAddress: String, port: Int? = null): LightStatus
} }
@Serializable
data class LightStatus( data class LightStatus(
val powerState: LightPowerState?, @SerialName("power") val powerStatus: LightPowerStatus?,
val brightness: Int?, @SerialName("brightness") val brightness: Int?,
val temperature: Int? @SerialName("temperature") val temperature: Int?
){ ){
override fun toString(): String { override fun toString(): String {
val stringList = mutableListOf<String>() val stringList = mutableListOf<String>()
powerState?.let { stringList.add("status: ${powerState.stringValue}") } powerStatus?.let { stringList.add("power: ${powerStatus.stringValue}") }
brightness?.let { stringList.add("brightness: $brightness") } brightness?.let { stringList.add("brightness: $brightness") }
temperature?.let { stringList.add("temperature: $temperature") } temperature?.let { stringList.add("temperature: $temperature") }
return stringList.joinToString(separator = "\n") 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
}
} }

View File

@ -1,5 +1,7 @@
package fi.schro.di package fi.schro.di
import fi.schro.data.ConfigurationRepository
import fi.schro.data.ConfigurationRepositoryImpl
import fi.schro.data.ElgatoLightRepository import fi.schro.data.ElgatoLightRepository
import fi.schro.data.LightRepository import fi.schro.data.LightRepository
import fi.schro.ui.ApplyCommand import fi.schro.ui.ApplyCommand
@ -18,6 +20,7 @@ val commandModule = module {
val dataModule = module { val dataModule = module {
single<LightRepository> { ElgatoLightRepository(get()) } single<LightRepository> { ElgatoLightRepository(get()) }
single<ConfigurationRepository> { ConfigurationRepositoryImpl(get()) }
} }
val networkModule = module { val networkModule = module {

View File

@ -16,7 +16,7 @@ import org.koin.core.component.inject
const val ARG_TARGET_LAMP = "TARGET_LAMP" const val ARG_TARGET_LAMP = "TARGET_LAMP"
const val ARG_CONFIGURATION_FILE = "CONFIGURATION_FILE" 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 applyCommand: ApplyCommand by inject()
private val getCommand: GetCommand by inject() private val getCommand: GetCommand by inject()
private val setCommand: SetCommand by inject() private val setCommand: SetCommand by inject()
@ -48,12 +48,12 @@ class SetCommand(
it in 1000..10000 it in 1000..10000
} }
private val powerState: LightPowerState? by option("-p", "--powerstate", help = "The state to be set").enum<LightPowerState>() private val powerStatus: LightPowerStatus? by option("-p", "--power", help = "The power status to be set").enum<LightPowerStatus>()
override fun run() { override fun run() {
val status = LightStatus( val status = LightStatus(
powerState = powerState, powerStatus = powerStatus,
brightness = brightness, brightness = brightness,
temperature = temperature 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), ON("ON", 1),
OFF("OFF", 0); OFF("OFF", 0);
companion object { companion object {
fun fromInt(intValue: Int): LightPowerState? { fun fromInt(intValue: Int): LightPowerStatus? {
return values().firstOrNull { it.intValue == intValue } return values().firstOrNull { it.intValue == intValue }
} }
} }

View File

@ -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<ByteVar>(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()
}
}