Skip to content

Commit 28ac9ad

Browse files
committed
WIP: compiles
1 parent 4300667 commit 28ac9ad

File tree

4 files changed

+158
-4
lines changed

4 files changed

+158
-4
lines changed

shared/src/commonMain/kotlin/baaahs/controller/NanoleafManager.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import baaahs.util.Time
1717
import kotlinx.coroutines.GlobalScope
1818
import kotlinx.coroutines.launch
1919
import kotlinx.coroutines.withContext
20+
import kotlinx.datetime.Instant
2021
import kotlinx.serialization.SerialName
2122
import kotlinx.serialization.Serializable
2223
import kotlinx.serialization.Transient
@@ -63,10 +64,10 @@ class NanoleafManager(
6364
data class State(
6465
override val title: String,
6566
override val address: String,
66-
override val onlineSince: Time?,
67+
override val onlineSince: Instant?,
6768
override val firmwareVersion: String? = null,
6869
override val lastErrorMessage: String? = null,
69-
override val lastErrorAt: Time? = null,
70+
override val lastErrorAt: Instant? = null,
7071
val hostName: String,
7172
val port: Int,
7273
val deviceId: String,

shared/src/jsMain/kotlin/baaahs/app/ui/controllers/NanoleafControllerEditorView.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import baaahs.scene.EditingController
66
import baaahs.ui.unaryMinus
77
import baaahs.ui.value
88
import baaahs.ui.xComponent
9-
import js.core.jso
9+
import js.objects.jso
1010
import mui.material.Container
1111
import mui.material.TextField
1212
import mui.system.sx
@@ -42,7 +42,7 @@ private val NanoleafControllerEditorView = xComponent<NanoleafControllerEditorPr
4242
}
4343

4444
Container {
45-
attrs.classes = jso { root = -styles.propertiesEditSection }
45+
attrs.className = -styles.propertiesEditSection
4646
attrs.sx {
4747
display = Display.flex
4848
flexDirection = FlexDirection.column
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package baaahs.controller
2+
3+
import baaahs.util.Clock
4+
import kotlin.coroutines.CoroutineContext
5+
6+
actual class NanoleafAdapter actual constructor(coroutineContext: CoroutineContext, clock: Clock) {
7+
actual fun start(callback: (NanoleafDeviceMetadata) -> Unit) {
8+
}
9+
10+
actual fun stop() {
11+
}
12+
13+
actual fun getAccessToken(deviceMetadata: NanoleafDeviceMetadata): String {
14+
TODO("not implemented")
15+
}
16+
17+
actual fun openDevice(
18+
deviceMetadata: NanoleafDeviceMetadata,
19+
accessToken: String
20+
): NanoleafDevice = TODO("not implemented")
21+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package baaahs.controller
2+
3+
import baaahs.device.PixelFormat
4+
import baaahs.io.ByteArrayWriter
5+
import baaahs.util.Clock
6+
import baaahs.util.Logger
7+
import io.github.rowak.nanoleafapi.Frame
8+
import io.github.rowak.nanoleafapi.NanoleafDevice
9+
import io.github.rowak.nanoleafapi.NanoleafSearchCallback
10+
import io.github.rowak.nanoleafapi.StaticEffect
11+
import io.github.rowak.nanoleafapi.util.NanoleafDeviceMeta
12+
import io.github.rowak.nanoleafapi.util.NanoleafSetup
13+
import kotlinx.datetime.Instant
14+
import kotlin.coroutines.CoroutineContext
15+
import kotlin.time.Duration.Companion.milliseconds
16+
17+
actual class NanoleafAdapter actual constructor(
18+
coroutineContext: CoroutineContext,
19+
private val clock: Clock
20+
) {
21+
private val deviceMetas = mutableSetOf<NanoleafDeviceMeta>()
22+
23+
actual fun start(callback: (NanoleafDeviceMetadata) -> Unit) {
24+
deviceMetas.clear()
25+
26+
NanoleafSetup.findNanoleafDevicesAsync(object : NanoleafSearchCallback {
27+
override fun onDeviceFound(meta: NanoleafDeviceMeta) {
28+
println("Found nanoleaf device: ${meta.deviceId} ${meta.deviceName} at ${meta.hostName}:${meta.port}")
29+
val deviceMeta = NanoleafDeviceMetadataData(meta.hostName, meta.port, meta.deviceId, meta.deviceName)
30+
callback(deviceMeta)
31+
}
32+
33+
override fun onTimeout() {
34+
println("timeout!")
35+
}
36+
}, 10_000)
37+
}
38+
39+
actual fun stop() {
40+
}
41+
42+
actual fun getAccessToken(deviceMetadata: NanoleafDeviceMetadata): String {
43+
return NanoleafSetup.createAccessToken(deviceMetadata.hostName, deviceMetadata.port)
44+
}
45+
46+
actual fun openDevice(
47+
deviceMetadata: NanoleafDeviceMetadata,
48+
accessToken: String
49+
): baaahs.controller.NanoleafDevice {
50+
return JvmNanoleafDevice(
51+
deviceMetadata.hostName, deviceMetadata.port, deviceMetadata.deviceId, deviceMetadata.deviceName,
52+
accessToken
53+
)
54+
}
55+
56+
inner class JvmNanoleafDevice(
57+
override val hostName: String,
58+
override val port: Int,
59+
override val deviceId: String,
60+
override val deviceName: String,
61+
val accessToken: String
62+
) : baaahs.controller.NanoleafDevice {
63+
// val accessToken = "xI2UVHZzcbuWWMIbig1Zv9NhajekW0oC"
64+
val device: NanoleafDevice = NanoleafDevice.createDevice(hostName, port, accessToken)
65+
.also { it.enableExternalStreaming() }
66+
init {
67+
println("device.globalOrientation = ${device.globalOrientation}")
68+
}
69+
val devicePanels = device.panelsRotated
70+
override val panels = devicePanels.map {
71+
NanoleafPanel(it.id, it.x, it.y, it.orientation, it.shape.toString())
72+
}
73+
private var lastFrameSentAtMillis: Instant? = null
74+
private var consecutiveFramesSent: Int = 0
75+
76+
override fun deliverComponents(
77+
componentCount: Int,
78+
bytesPerComponent: Int,
79+
pixelFormat: PixelFormat,
80+
fn: (componentIndex: Int, buf: ByteArrayWriter) -> Unit
81+
) {
82+
if (bytesPerComponent != 3) error("Expected 3 bytes per component but got $bytesPerComponent")
83+
84+
val nowMillis = clock.now()
85+
val elapsedMillis = lastFrameSentAtMillis?.let { nowMillis - it } ?: 0.milliseconds
86+
if (elapsedMillis < minMillisBetweenFrames) {
87+
logger.warn { "Dropping frame for $deviceId after $consecutiveFramesSent successfully sent," +
88+
" last frame was ${elapsedMillis}ms ago." }
89+
consecutiveFramesSent = 0
90+
return
91+
}
92+
lastFrameSentAtMillis = nowMillis
93+
consecutiveFramesSent++
94+
95+
val componentRange = 0 until componentCount
96+
val out = ByteArrayWriter()
97+
for (i in componentRange) fn(i, out)
98+
99+
val reader = out.reader()
100+
val builder = StaticEffect.Builder(devicePanels)
101+
for (i in componentRange) {
102+
reader.offset = i * 3
103+
pixelFormat.readColorInts(reader) { r: Int, g: Int, b: Int ->
104+
builder.setPanel(panels[i].id, Frame(r, g, b, 1))
105+
}
106+
}
107+
device.sendStaticEffectExternalStreaming(builder.build("sparklemotion!"))
108+
109+
// val sendComponents = if (shuffleSend) componentRange.toList().shuffled() else componentRange
110+
// for (i in sendComponents) {
111+
// reader.offset = i * 3
112+
// pixelFormat.readColorInts(reader) { r: Int, g: Int, b: Int ->
113+
// device.setPanelExternalStreaming(panels[i].id, r, g, b, 1)
114+
// }
115+
// }
116+
}
117+
118+
init {
119+
println("token=${accessToken}")
120+
121+
for (panel in panels) {
122+
println("panel = ${panel.id} ${panel.x} ${panel.y} ${panel.orientation} ${panel.shape}")
123+
}
124+
}
125+
}
126+
127+
companion object {
128+
val minMillisBetweenFrames = 50.milliseconds
129+
const val shuffleSend = true
130+
private val logger = Logger<NanoleafAdapter>()
131+
}
132+
}

0 commit comments

Comments
 (0)