Ground truth for Govee Flow Plus Light Bars (H6056): phantom segments, bar-to-segment mapping, API auth and rate limits. Language-agnostic facts; Kotlin/Ktor reference example.
68
86%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Use this skill whenever you need to light up Govee Flow Plus light bars (H6056).
segmentedColorRgb, min=1, max=15).12, 13, 14 are phantom. The API returns 200 OK when you address them; no light turns on. If you don't slice your ranges correctly, your "all on" commands will look broken on stage.bar_a (call it Yankee): segments 0, 1, 2, 3, 4, 5bar_b (Golf): segments 6, 7, 8, 9, 10, 11segment[0] is at the TOP. Bottom-up fill on Yankee = lighting segments 5 → 0.GET /router/api/v1/user/devices with your API key to enumerate SKUs and device IDs.https://openapi.api.govee.comGovee-API-Key: <key> (NOT bearer, NOT query param)POST /router/api/v1/device/controltype = "devices.capabilities.segment_color_setting"instance = "segmentedColorRgb"value = {"segment": [<indices>], "rgb": <packed_int>} where rgb = (r shl 16) or (g shl 8) or biot-actuator-patterns-kotlin's debounce controller (min-interval 1.2 s).rgb=(0,0,0) packs to 0x000000. Some firmware paths treat that as a no-op and retain the prior segment state. To reliably clear a segment, send rgb=(1,1,1) — near-black but non-zero. The user can't perceive the 1/255 brightness.
On session shutdown, always issue an explicit all-segments command:
Runtime.getRuntime().addShutdownHook(Thread {
runBlocking { client.setSegments((0..11).toList(), Triple(1, 1, 1)) }
})import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.coroutines.runBlocking
import java.util.UUID
class GoveeClient(
private val apiKey: String,
private val sku: String, // "H6056"
private val device: String // from GET /router/api/v1/user/devices
) {
private val client = HttpClient(CIO)
private val base = "https://openapi.api.govee.com"
suspend fun setSegments(segments: List<Int>, rgb: Triple<Int, Int, Int>): Int {
val (r, g, b) = rgb
val packed = (r shl 16) or (g shl 8) or b
val segArr = segments.joinToString(",", "[", "]")
val payload = """
{"requestId":"${UUID.randomUUID()}",
"payload":{"sku":"$sku","device":"$device",
"capability":{"type":"devices.capabilities.segment_color_setting",
"instance":"segmentedColorRgb",
"value":{"segment":$segArr,"rgb":$packed}}}}
""".trimIndent()
return client.post("$base/router/api/v1/device/control") {
header("Content-Type", "application/json")
header("Govee-API-Key", apiKey)
setBody(payload)
}.status.value
}
companion object {
// Use these constants, NOT raw indices, to stay phantom-safe and bar-aware
val YANKEE = (0..5).toList()
val GOLF = (6..11).toList()
val ALL_PHYSICAL = (0..11).toList()
}
}
fun main() = runBlocking {
val client = GoveeClient(
apiKey = System.getenv("GOVEE_API_KEY"),
sku = "H6056",
device = System.getenv("GOVEE_H6056_DEVICE")
)
// Light Yankee green, Golf red:
client.setSegments(GoveeClient.YANKEE, Triple(0, 200, 0))
client.setSegments(GoveeClient.GOLF, Triple(200, 0, 0))
}Because segment[0] is the TOP, a bottom-up "thermometer" fill means lighting from the high-index end:
fun yankeeBottomUp(lit: Int): IntRange = (6 - lit) until 6 // total=6
// lit=1 → segment 5 (just the bottom)
// lit=3 → segments 3, 4, 5
// lit=6 → segments 0..5 (full bar)See render-progress-bar-kotlin for the full gradient pattern.
suspend fun discoverDevices(client: HttpClient, apiKey: String): List<Map<String, Any?>> {
val resp = client.get("https://openapi.api.govee.com/router/api/v1/user/devices") {
header("Govee-API-Key", apiKey)
}
// resp.bodyAsText() → {"code":200, "data":[{sku, device, deviceName, ...}], ...}
// Parse with kotlinx.serialization or Jackson; extract entries where sku == "H6056"
TODO("parse JSON, return entries")
}15 as the segment count — sends 3 wasted commands to phantom segments.0..4, 5..9, 10..14 for a semaphore — middle band spans Yankee + Golf, top band addresses phantoms.rgb=(0,0,0) for "off" — silently no-ops on some firmware paths./devices endpoint.export GOVEE_API_KEY=<your-developer-api-key> # from developer.govee.com
export GOVEE_H6056_SKU=H6056
export GOVEE_H6056_DEVICE=<the device-id field from /devices response>