I built a ceiling light to extend the ambilight of my TV. As I am using an integrated Sonoff Basic R3 for turning the relay for the power supply on and off as well as controlling SK6812WWA LEDs in the bottom am the SK6812 RGBW LEDs in the ring going with ESPHome seemed to be the best solution to integrate it in my setup.
I get the ambilight information as json-file from the TV. Right now json is read by HomeAssistant, processed and RGB information is sent to the ESP. The issue with this is that I have to split the LED strip in ESPHome in partitions to have individual light instances in HomeAssistant. That’s why I limited it to 6 sections:
It works, but has some delay, which is acceptable because it only lights up the upper room which is not in direct vision but gives a nice immersive feeling.
Now to improve delays, and address finer sections of the LED strip I want to integrate as much as possible towards the ESP and make the processing independent of HomeAssistant. Therefore, json parsing and mapping onto the LED strip has to happen on the ESP. I have that basically working with this code:
- platform: neopixelbus
id: wz_rgbw
name: "WZ RGB Licht"
variant: SK6812
type: GRBW
method: ESP8266_DMA
pin: GPIO3
num_leds: 96
on_turn_on:
...
on_turn_off:
...
effects:
- addressable_lambda:
name: "ambilight"
update_interval: 1s
lambda: |-
WiFiClient wifiClient;
HTTPClient http;
ESP_LOGD("ambi", "started lambda");
http.useHTTP10(true);
http.begin(wifiClient, "http://internalip/1/ambilight/measured");
int htstt = http.GET();
if(htstt <= 0) {
http.end();
ESP_LOGD("ambi", "couldnt connect to json, wait and rety");
delay(1000);
}
ESP_LOGD("ambi", "got json");
DynamicJsonDocument doc(2000);
deserializeJson(doc, http.getStream());
ESP_LOGD("ambi", "buffered json");
it.range(0, 5) = Color(doc["layer1"]["top"]["8"]["r"].as<int>(), doc["layer1"]["top"]["8"]["g"].as<int>(), doc["layer1"]["top"]["8"]["b"].as<int>());
it.range(5, 10) = Color(doc["layer1"]["top"]["7"]["r"].as<int>(), doc["layer1"]["top"]["7"]["g"].as<int>(), doc["layer1"]["top"]["7"]["b"].as<int>());
it.range(10, 15) = Color(doc["layer1"]["top"]["6"]["r"].as<int>(), doc["layer1"]["top"]["6"]["g"].as<int>(), doc["layer1"]["top"]["6"]["b"].as<int>());
it.range(15, 21) = Color(doc["layer1"]["top"]["5"]["r"].as<int>(), doc["layer1"]["top"]["5"]["g"].as<int>(), doc["layer1"]["top"]["5"]["b"].as<int>());
it.range(21, 27) = Color(doc["layer1"]["top"]["4"]["r"].as<int>(), doc["layer1"]["top"]["4"]["g"].as<int>(), doc["layer1"]["top"]["4"]["b"].as<int>());
it.range(27, 33) = Color(doc["layer1"]["top"]["3"]["r"].as<int>(), doc["layer1"]["top"]["3"]["g"].as<int>(), doc["layer1"]["top"]["3"]["b"].as<int>());
...
ESP_LOGD("ambi", "wrote rgb");
http.end();
ESP_LOGD("ambi", "wait 1sec");
delay(1000);
The issue is that this doesn’t use transitions and makes the lights flash to the next state with each iteration. Is there a possibility to add the transition information or define a default before that is considered in this assignment?
I guess another way that supports transitions would be something like:
auto call = id(wz_rgbw).make_call();
call.set_brightness(0.72);
call.set_transition_length(800);
call.set_rgb(doc["layer1"]["left"]["0"]["r"].as<int>(),doc["layer1"]["left"]["0"]["g"].as<int>(),doc["layer1"]["left"]["0"]["b"].as<int>());
call.perform();
This way multiple conditions can be set and activated at once with perform(). But this would require to split the addressable light up into partitions and I could only set one segment at a time. I would need 17 segments. Something like a “call.set_range” would do what I’m looking for.
Is there a possibility to write different sections of an addressable LED strip and then “active” the values at one with a transition?
Thanks for any hints and ideas!