add support for setting temperature, start apply command, refactor
parent
6451ede58a
commit
ddd7fb7ed6
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue