MQTT RGB light with brightness / color temperature - how to get HA to actually send brightness/color_temp?

Hi!

I’m new to Home Assistant, and I am trying to make a new ESP8266 based light with physical RGBWW LEDs, but only want to expose RGB + Color Temperature to Home Assistant. The two whites are mixed in hardware for the ability to have “full spectrum white with color tint” illumination. AFAIK, otherwise it would be “either RGB or tunable white” and never mix them.

The issue I am facing is that I can’t seem to get the discovery message right, so Home Assistant actually sends color temperature. And the brightness slider doesn’t work, either. Can someone point me in the right direction? I’m using the json schema here.

According to the documentation, the “supported color modes” (sup_clrm) should allow the lamp to work in different “modes” and Home Assistant should be able to actively send the according messages, matching each possible lamp mode. The color mode is never sent by Home Assistant (as stated in the documentation) but instead can be sent by the lamp for disambiguation. Which is what I do. So why aren’t brightness or color_temperature ever being sent?

Config message:

{
  "name":"esp dimmer",
  "schema":"json",
  "clrm":"true",
  "brightness":"true",
  "bri_scl":"255",
  "sup_clrm":["rgb","color_temp","brightness","white"],
  "dev":{
    "identifiers":["ESP-dimmer-13490202"],
    "name":"esp dimmer"
  },
  "stat_t":"homeassistant/light/ESP-dimmer-13490202/state",
  "cmd_t":"homeassistant/light/ESP-dimmer-13490202/set",
  "uniq_id":"ESP-dimmer-13490202"
}

State message my lamp returns (in this example, it’s full brightness):

{
  "state":"ON",
  "color":{
    "r":255,
    "g":255,
    "b":255
  },
  "color_temp":215,
  "brightness":255,
  "color_mode":"rgb"
}

When I move the brightness slider in Home Assistant, I only get a message with {“state”: “ON”}, no brightness values and no RGB values calculated to mimic brightness, either. When I change the color temperature through an automation (no color temp slider appeared in light card, only brightness and color wheel), I get a message with state and rgb, but not the “color_temp” that I need.

Oh and the logs are clean. No message about not accepting some configuration values.
What did I miss, where did I forget something?

In case it is interesting or useful for getting to the solution, here is some info about how I intend to mix colors for this device.

Incoming values are standard RGB with 0…255 values and color_temp in mirads. I then first calculate kelvin from that, limit to my lamp’s range (2300K-7000K) and convert that into 0…255 values for warm white and cold white.

#define WW_TEMP 2300
#define CW_TEMP 7000
volatile int valueCTin=215; // in mirads
volatile int valueCTK=4650; // in kelvin

// (more code here)

// mixing color temperature
valueCTK = 1000000/valueCTin; // convert mirads to K
// limit value to [WW_TEMP..CW_TEMP]
if(valueCTK < WW_TEMP){valueCTK = WW_TEMP;}
if(valueCTK > CW_TEMP){valueCTK = CW_TEMP;}
// calculate values for WW and CW if they were at full brightness (still INPUT values, so 8 bit range)
int valueCWfull = ((255 * (valueCTK - WW_TEMP)) / (CW_TEMP - WW_TEMP));
int valueWWfull = (255 - valueCWfull);

Then, I take the minimum of R,G,B (called the “white content” here) and replace that by white, reducing the R,G,B values accordingly. And I look up 14 bit PWM values from a translation table I calculated using Stephen’s Power Law.

volatile int valueRin=255;
volatile int valueGin=255;
volatile int valueBin=255;
volatile int valueCTin=215; // in mirads
volatile int valueCTK=4650; // in kelvin
volatile int pwmEndValues[5];

// (more code here)

int whiteContent = min(valueRin, min(valueGin, valueBin));
pwmEndValues[0] = pwmTable[(valueRin - whiteContent)];
pwmEndValues[1] = pwmTable[(valueGin - whiteContent)];
pwmEndValues[2] = pwmTable[(valueBin - whiteContent)];
pwmEndValues[3] = pwmTable[valueWWfull * whiteContent / 255];
pwmEndValues[4] = pwmTable[valueCWfull * whiteContent / 255];

Afterwards, I do dimming to the target colors (that’s why the array is called “end” values).

…so as you can see, you only need R,G,B,color_temp to fully mix all of R,G,B,WW,CW together for seamless dimming between colored and tunable white modes, with the option to get anything in between.

This is likely to be spectacularly unhelpful given the impressive complexity your ESPhome code has already gained, however the standard design pattern for custom LED control tends to be WLED (and yes, it can support RGBWW):
https://kno.wled.ge/

HASS has a custom Add-On for discovery and integration with WLED so control doesn’t use the ESPhome API, but there is a possibility that either the whole project, or just their implementation might give you some inspiration (GitHub link on the right).

I had a look at how WLED did it, and found out that MQTT autodiscovery was removed in favor of the native WLED integration somewhere in 2019. In code before that, their discovery message had the “default” scheme (that doesn’t support flashing and transitions, according to the Home Assistant MQTT Light documentation) where I use the “json” scheme (which does). So while it is interesting to read, it doesn’t point me in the right direction, unfortunately. I do want transitions.

Another way to look at it would be to mimic the behavior of the native integration of WLED. So my lamp identifies as a WLED in order to make it talk with Home Assistant. I might look into this if I don’t get the MQTT stuff working properly.

Connecting my lamps to unmodified WLED or modifying it slightly and then using it, seems like a “last resort” to me. WLED can do a lot more than I need, and I like keeping it simple where possible. I mean for starters, I don’t have adressable LEDs. I have a 24V RGB+CCT strip with one common positive and five negatives, one per color. I am controling it with five mosfets on five GPIOs of the ESP8266. I don’t have effects and most of the other features WLED has.

Oh and I don’t use ESPhome, either. I use the PubSubClient, ESP8266wifi and ArduinoJson libraries directly. The rest is hand-written code. I figured it’s just publishing and subscribing a few topics, doing some simple math with the values, a few "if"s here and there, nothing too complicated. And it does work. When it gets the correct commands, as documented in the Home Assistant MQTT Light documentation, it does work. But Home Assistant for some reason refuses to accept the fact that in the discovery message, the lamp identifies as having color temperature and brightness controls. This is literally the only thing that doesn’t work. Apart from that, it’s done. It’s so frustrating!

I’ve got a Shelly RGBW2 (RGBW) which handles 4x alog PWM on the ‘next projects’ pile, and intend to use either Tasmota or WLED as it’s just an ESP8266. There are other COTS controllers out there, such as one from Quindor.
https://www.shelly.cloud/en-us/products/product-overview/shelly-rgbw2

https://tasmota.github.io/docs/devices/Shelly-RGBW2/

I have written my own client devices using Python + MQTT, and the only advice I can offer is that MQTT discovery is instant - e.g. if you experiment with mosquitto_pub, HASS will react instantly with changes to the advertised device so you can try different ways of defining your device in real time.

There were some issues with Zigbee RGBWW bulbs, but when better support for different device definitions came in last year (e.g. mapping between colour spaces) the definitions changed and my Lidi Livarno cheap bulb works with both a COLOR wheel and TEMPERATURE slider. Again, might be worth a read for inspiration.

Found out why. Finally. It seems if there is an “unsupported color mode” in the discovery message, like ever, even once, Home Assistant throws an exception and discovery stops completely until Home Assistant is rebooted. If there is that exception in the Home Assistant Core log: reboot, try a new topic. I have no idea why it doesn’t continue working and accepting other discovery messages. In my opinion, that’s a huge inconvenience and a bug.

Here’s what I found out:

  • There seems to be a limitation with the “brightness” color mode. It is exclusive, can’t be shared with other modes. Use the “white” color mode instead, that one’s not so picky.
  • Boolean values shouldn’t have quotes around them.
  • Numbers shouldn’t have quotes around them.

Basic discovery message example with RGB and color temp and brightness (aka “white”):

{
  "~": "homeassistant/light/ESP-dimmer-13490202",
  "name": "esp_dimmer",
  "unique_id": "ESP-dimmer-13490202",
  "cmd_t": "~/set",
  "stat_t": "~/state",
  "schema": "json",
  "clrm": true,
  "sup_clrm": [
    "color_temp",
    "rgb",
    "white"
  ]
}

This way, you get the color wheel, the “white” slider and the color temperature slider.

Hope that helps someone out there with similar issues.