add support for setting temperature, start apply command, refactor
This commit is contained in:
		
							parent
							
								
									6451ede58a
								
							
						
					
					
						commit
						ddd7fb7ed6
					
				
					 6 changed files with 94 additions and 16 deletions
				
			
		| 
						 | 
					@ -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 }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										34
									
								
								src/nativeMain/kotlin/fi/schro/util/FileUtil.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/nativeMain/kotlin/fi/schro/util/FileUtil.kt
									
										
									
									
									
										Normal 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()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue