Skip to content

Commit

Permalink
Implement hybrid scene equivalent to dmx scene
Browse files Browse the repository at this point in the history
  • Loading branch information
sknull committed Dec 28, 2023
1 parent 59162a7 commit c60ec85
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,23 @@ class Scene(
}
}

fun fade(
other: Scene,
fadeDuration: Long,
preferences: Preferences
) {
val dmxFrameTime = preferences.dmx.frameTime // millis
val step = 1.0 / fadeDuration.toDouble() * dmxFrameTime.toDouble()
var factor = 0.0

while (factor < 1.0) {
fade(other, factor).write(preferences)
factor += step
Thread.sleep(dmxFrameTime)
}
other.write(preferences)
}

override fun fade(
other: Any,
factor: Double
Expand All @@ -51,21 +68,4 @@ class Scene(
}
else throw IllegalArgumentException("Cannot not fade another type")
}

fun fade(
other: Scene,
fadeDuration: Long,
preferences: Preferences
) {
val dmxFrameTime = preferences.dmx.frameTime // millis
val step = 1.0 / fadeDuration.toDouble() * dmxFrameTime.toDouble()
var factor = 0.0

while (factor < 1.0) {
fade(other, factor).write(preferences)
factor += step
Thread.sleep(dmxFrameTime)
}
other.write(preferences)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import java.io.File
import java.nio.file.Paths


@JsonIgnoreProperties("klanglichtDir", "dmxInterface", "fixtures", "serviceMap", "stageMap")
@JsonIgnoreProperties("klanglichtDir", "dmxInterface", "fixtures", "serviceMap", "shellyMap", "stageMap")
data class Preferences(
val name: String = "",
val services: List<Service> = listOf(),
Expand All @@ -29,6 +29,8 @@ data class Preferences(

var serviceMap: Map<String, Service> = mapOf()

var shellyMap: Map<String, ShellyDevice> = mapOf()

var stageMap: Map<String, HybridDevice> = mapOf()

fun init(klanglichtDir: File) {
Expand All @@ -47,6 +49,8 @@ data class Preferences(

stageMap = stage.associateBy { it.id }

shellyMap = shelly.associateBy { it.name }

val dmxInterface = DmxInterface.load(dmx.interfaceType)
dmxInterface.open(dmx.port)
this.dmxInterface = dmxInterface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import de.visualdigits.kotlin.klanglicht.model.hybrid.HybridDeviceType
import de.visualdigits.kotlin.klanglicht.rest.dmx.handler.DmxHandler
import de.visualdigits.kotlin.klanglicht.rest.common.configuration.ConfigHolder
import de.visualdigits.kotlin.klanglicht.rest.shelly.handler.ShellyHandler
import org.apache.commons.lang3.StringUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component

Expand Down Expand Up @@ -43,7 +42,6 @@ class HybridStageHandler {
.map { it.trim() }
val nd = lIds.size - 1
var d = 0
val hasIds = StringUtils.isNotEmpty(ids)

val lHexColors = hexColors
.split(",")
Expand All @@ -57,29 +55,28 @@ class HybridStageHandler {
.map { it.toFloat() }
val ng = lGains.size - 1
var g = 0
val overrideGains = StringUtils.isNotEmpty(gains)

val dmxIds: MutableList<String> = ArrayList()
val dmxHexColors: MutableList<String> = ArrayList()
val dmxGains: MutableList<Float> = ArrayList()
val shellyIds: MutableList<String> = ArrayList()
val shellyHexColors: MutableList<String> = ArrayList()
val shellyGains: MutableList<Float> = ArrayList()
if (hasIds) {
if (lIds.isNotEmpty()) {
for (id in lIds) {
val sid = id
val hexColor = lHexColors[h]
val gain = lGains[g]
val device = configHolder!!.preferences?.stageMap?.get(sid)
val device = configHolder!!.preferences?.stageMap?.get(id)
if (device != null) {
processDevice(
overrideGains,
lGains.isNotEmpty(),
dmxIds,
dmxHexColors,
dmxGains,
shellyIds,
shellyHexColors,
shellyGains,
sid,
id,
hexColor,
gain,
device
Expand All @@ -97,11 +94,11 @@ class HybridStageHandler {
}
} else {
configHolder!!.preferences?.stage?.forEach { device ->
val sid = device.id.trim()?:""
val sid = device.id.trim() ?: ""
val hexColor = lHexColors[h]
val gain = lGains[g]
processDevice(
overrideGains,
lGains.isNotEmpty(),
dmxIds,
dmxHexColors,
dmxGains,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package de.visualdigits.kotlin.klanglicht.rest.hybrid.model

import de.visualdigits.kotlin.klanglicht.model.color.RGBColor
import de.visualdigits.kotlin.klanglicht.model.hybrid.HybridDevice
import de.visualdigits.kotlin.klanglicht.model.hybrid.HybridDeviceType
import de.visualdigits.kotlin.klanglicht.model.parameter.Fadeable
import de.visualdigits.kotlin.klanglicht.model.parameter.IntParameter
import de.visualdigits.kotlin.klanglicht.model.parameter.ParameterSet
import de.visualdigits.kotlin.klanglicht.model.parameter.Scene
import de.visualdigits.kotlin.klanglicht.model.preferences.Preferences
import de.visualdigits.kotlin.klanglicht.rest.shelly.client.ShellyClient
import kotlin.math.min
import kotlin.math.roundToInt

class HybridScene(
val ids: String,
val hexColors: String,
val gains: String,
val preferences: Preferences
) : Fadeable<HybridScene> {

private val fadeables: List<Fadeable<*>>

init {
val lIds = ids
.split(",")
.filter { it.isNotEmpty() }
.map { it.trim() }
val nd = lIds.size - 1
var d = 0

val lHexColors = hexColors
.split(",")
.filter { it.isNotEmpty() }
val nh = lHexColors.size - 1
var h = 0

val lGains = gains
.split(",")
.filter { it.isNotEmpty() }
.map { it.toFloat() }
val ng = lGains.size - 1
var g = 0

fadeables = if (lIds.isNotEmpty()) {
lIds.mapNotNull { id ->
val device = preferences.stageMap[id]
val hexColor = lHexColors[min(nh, h++)]
val gain = lGains.getOrNull(min(ng, g++))
device?.let { d -> processDevice(d, preferences, id, gain, hexColor) }
}
} else {
preferences.stage.mapNotNull { device ->
val hexColor = lHexColors[min(nh, h++)]
val gain = lGains.getOrNull(min(ng, g++))
processDevice(device, preferences, device.id, gain, hexColor) }
}
}

override fun toString(): String {
return hexColors
.split(",")
.filter { it.isNotEmpty() }
.map { RGBColor(it).ansiColor() }
.joinToString("")
}

private fun processDevice(
device: HybridDevice,
preferences: Preferences,
id: String,
gain: Float?,
hexColor: String
): Fadeable<*>? {
return when (device.type) {
HybridDeviceType.dmx -> {
val dmxDevice = preferences.dmx.dmxDevices[id]
if (dmxDevice != null) {
ParameterSet(
baseChannel = dmxDevice.baseChannel,
parameters = mutableListOf(
IntParameter("MasterDimmer", (255 * (gain ?: dmxDevice.gain)).roundToInt()),
RGBColor(hexColor)
)
)
} else null
}

HybridDeviceType.shelly -> {
val shellyDevice = preferences.shellyMap[id]
if (shellyDevice != null) {
ShellyColor(shellyDevice.ipAddress, RGBColor(hexColor), gain ?: shellyDevice.gain)
} else null
}

else -> null
}
}

fun write(preferences: Preferences, write: Boolean = true) {
Scene(
name = "HybridScene",
parameterSet = fadeables.filterIsInstance<ParameterSet>()
).write(preferences, write)
fadeables
.filterIsInstance<ShellyColor>()
.forEach { shellyColor -> ShellyClient.setColor(shellyColor.ipAddress, shellyColor.color, shellyColor.gain) }
}

fun fade(
other: HybridScene,
fadeDuration: Long,
preferences: Preferences
) {
val dmxFrameTime = preferences.dmx.frameTime // millis
val step = 1.0 / fadeDuration.toDouble() * dmxFrameTime.toDouble()
var factor = 0.0

while (factor < 1.0) {
fade(other, factor).write(preferences)
factor += step
Thread.sleep(dmxFrameTime)
}
other.write(preferences)
}

override fun fade(other: Any, factor: Double): HybridScene {
return if (other is HybridScene) {
val fadedHexColors = fadeables
.zip(other.fadeables)
.mapNotNull {
val faded = it.first.fade(it.second, factor)
when (faded) {
is ShellyColor -> faded.color.hex()
is ParameterSet -> faded.parameters
.filterIsInstance<RGBColor>()
.firstOrNull()
?.let { c -> c.hex() }
else -> null
}
}.joinToString(",")
HybridScene(ids, fadedHexColors, gains, preferences)
} else {
throw IllegalArgumentException("Cannot not fade another type")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package de.visualdigits.kotlin.klanglicht.rest.hybrid.model

import de.visualdigits.kotlin.klanglicht.model.color.RGBColor
import de.visualdigits.kotlin.klanglicht.model.parameter.Fadeable

class ShellyColor(
val ipAddress: String,
val color: RGBColor,
val gain: Float
): Fadeable<ShellyColor> {

override fun fade(other: Any, factor: Double): ShellyColor {
return if (other is ShellyColor) {
ShellyColor(ipAddress, color.fade(other.color, factor), gain)
} else {
throw IllegalArgumentException("Cannot not fade another type")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import java.net.URL

object ShellyClient {

fun power(
fun setPower(
ipAddress: String,
command: String,
turnOn: Boolean,
Expand All @@ -16,27 +16,27 @@ object ShellyClient {
return URL("http://" + ipAddress + "/" + command + "?turn=" + (if (turnOn) "on" else "off") + "&transition=" + transitionDuration + "&").readText()
}

fun gain(
fun setGain(
ipAddress: String,
gain: Int,
transitionDuration: Long
): String {
return URL("http://$ipAddress/color/0?gain=$gain&transition=$transitionDuration&").readText()
}

fun status(
fun getStatus(
ipAddress: String
): Status {
val json = URL("http://$ipAddress/status").readText()
return Status.load(json)
}

fun color(
fun setColor(
ipAddress: String,
rgbColor: RGBColor,
gain: Float,
transitionDuration: Long,
turnOn: Boolean,
transitionDuration: Long = 0,
turnOn: Boolean = true,
): Light {
val json = URL(
"http://$ipAddress/color/0?" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import java.util.Arrays

@Component
class ShellyHandler {
Expand Down Expand Up @@ -214,7 +213,7 @@ class ShellyHandler {
configHolder.putColor(sid, lastColor) // update state
log.info("power: $ipAddress")
try {
ShellyClient.power(
ShellyClient.setPower(
ipAddress = ipAddress,
command = command,
turnOn = turnOn,
Expand Down Expand Up @@ -245,7 +244,7 @@ class ShellyHandler {
lastColor.gain = gain.toFloat()
configHolder.putColor(sid, lastColor) // update state
try {
ShellyClient.gain(ipAddress = ipAddress, gain = gain, transitionDuration = transitionDuration)
ShellyClient.setGain(ipAddress = ipAddress, gain = gain, transitionDuration = transitionDuration)
} catch (e: Exception) {
log.warn("Could not get gain for shelly at '$ipAddress'")
}
Expand All @@ -269,7 +268,7 @@ class ShellyHandler {
log.info("get status: $url")
var status: Status
try {
status = ShellyClient.status(ipAddress)
status = ShellyClient.getStatus(ipAddress)
} catch (e: Exception) {
log.warn("Could not get ststus for url '$url'")
status = Status()
Expand Down Expand Up @@ -304,7 +303,7 @@ class ShellyHandler {
val ipAddress: String = shellyDevice.ipAddress
log.info("setColor: $ipAddress")
try {
ShellyClient.color(
ShellyClient.setColor(
ipAddress = ipAddress,
rgbColor = rgbColor,
gain = gain,
Expand Down
Loading

0 comments on commit c60ec85

Please sign in to comment.