Custom firmware ESPHome-Xiaomi_bslamp2

Understand now. So… this would mean creating a new Integration for flashed Yeelights with device triggers?

Yeah, I guess that would be the way to implement it. I was hoping for a way to configure device triggers fully from the firmware itself via the API. If a custom integration on the Home Assistant side would be neede, then it’s not really worth the trouble I think.

I now discovered the ESPHome integration - It already has some basic device types so a better starting point

Finally took the base off and had a look. Did you check the datasheet for the RT8471 IC?

Yes, there are some types that do work already. For example binary sensors will show up with with " turned on" and " turned off" triggers for the device. And the light component will also be available with on/off events.

A hack for propagating for example a double-click on the power button, could be to add a specific binary_sensor for it, which is named as such (e.g. “Lamp power double click”). Then an automation could be created that triggers on “Lamp power double click turned on”. This could be done for every button event that you would like to use from within Home Assistant.
It works, but it’s less pretty than what I envisioned. For example, there would adlso exist a “Lamp power double click turned off” event, which doesn’t make much sense.

For a more clean solution, the ESPHome firmware should be able to inform Home Assistant via the API about the device triggers that can occur, and I don’t think that is currently implemented. I might look into that, once I have finished up the remaining code for this device.

I read through the RT8471 datasheet RT8471 and it is a stepdown converter for driving LEDs. The PWM input is not directly controlling the LEDs but gating a 300Hz ramp generator that drives the stepdown converter. The datasheet suggests a base frequency of 500Hz for the PWM with the pulse width then controlling the LED brightness.

I implemented a sensor component, that is used to publish touch events for the front panel slider. It will publish slider levels (0.01 - 1.00) when touching or releasing the slider. The example YAML file now contains a section that uses this sensor to control the brightness of the device:

sensor:
  - platform: yeelight_bs2
    id: ${name}_slider_level
    on_value:
      then:
        - light.turn_on:
            id: ${name}
            brightness: !lambda return x;

The lowest value is 0.01, which will trigger the night light mode. This means that night light can now easily be enabled from the front panel.

I will add an extra option to configure the type of output for this sensor, so you can configure if you want the raw slider levels (1 to 20), or if you want them translated to a float value (0.01 - 1.00) which can directly be used for brightness control. I lean towards making the float value the default one, since that is the main use case for this I presume.

I did check the datasheet, it might even be in my reverse engineering repo. But what I eventually based my code on, was actual measurements of GPIO outputs, while using the original firmware. That was a lot of work, but eventually the winning method to get the light to behave compatible with the original.

1 Like

Okay, here’s what I ended up with, after playing with some options: the slider sensor now has two extra configuration options: range_from and range_to. These define respectively the lower and upper boundary of the values that can be produced by the slider sensor. Values inbetween are interpolated linearly.

This allows a configuration like:

  - platform: yeelight_bs2
    id: yadayadayada
    range_from: 0.02
    range_to: 0.8
    on_value:
      then:
        - light.turn_on:
            id: my_light
            brightness: !lambda return x;

The value of 0.02 can be used to not trigger the light night mode when moving the slider all the way down (because night light is triggered by brightness value 0.01).
The value of 0.8 might for example be used to limit the maximum brightness that can be controlled via the slider.

These options should also provide enough flexibility to handle use cases where the slider controls something completely different from the light itself (e.g. changing the volume of the living room audio system, it’s Home Assistant, so your options are endless). If a range from 0 - 100 is need, then that can be configured using the new parameters too.

On to the next, and I think the last, component: a float output that can be used to light up the front panel light / slider level indicator.

Update: WHOOP WHOOP!

A float output component was added to the code. This finalizes my list of required components.
The yeelight_bs2 platform integration now provides:

  • light: controls the LEDs
  • binary_sensor: touch/release sensors for power button, color button and slider
  • sensor: sensor to report the level at which the slider was touched
  • outpout: controls the front panel light and its level indicator

You can check out the example.yaml for specific information on these. Using the current version of the yaml configuration, I was able to do the following:

Now it’s time to play around with the configuration file, to see if we’ve got all the flexibility that we need. Please let me know if you have a fun use case implemented or are running into issues trying to implement one.

I will now try to come up with a yaml file that makes the device behave like it does when using the original device firmware. Except for the phone home to China bits and frequent home-assistant disconnect bits of course :wink:

BS2 on sale

I just noticed that the BS2 is now Xiaomi Mijia Bedside Lamp 2 - TechPunt. It’s €29,95 instead of €39,95.

Update: the integration will be renamed

I figured that I’m going to rename the component to “xiaomi_bslamp2”.
When I bought my lamp, it was branded as “Yeelight” by the shop, but it turns out that this is only the manufacturer. Some details on the decision can be found on github:

For those that are already using the alpha releases, moving to the new situation would comprise of:

  • Renaming the custom_components/yeelight_bs2 folder to custom_components/xiaomi_bslamp2
  • Updating all references of yeelight_bs2 in the yaml file to xiaomi_bslamp2

The repository will be renamed too, but this won’t break the use of the original repo URLs. So you can still pull new versions after the move.

Update: renaming completed + beta release ready

I’m forced to add updates to my last post unfortunately, because I’m not allowed to send more than 3 messages in a row.

I just released a beta version of the integration.
Details on this release and the renaming operation can be found on the GitHub release page

4 Likes

Extremely good stuff, mmakaay! Can’t even keep up with you reading!

1 Like

The Xiaomi naming makes sense to me.

I am currently working on an easy to use way to have presets configured for the lamp.

This can be used to emulate another bit of original functionality of the original firmware: switching between preset colors by touching/holding the color button.

The progress can be followed on GitHub:

I’m almost done with the presets feature.
Presets can be configured and they can be grouped in one or more groups.
Two automation actions were implemented for switching between configured presets (activate next group and activate next preset).

The above can be used to emulate the behavior of the original device firmware:
Here are the relevant bits of configuration that are involved in getting the color button to operate like the original one (long press switches between RGB and color temperature mode; short press selects the next preset within the active mode).

light:
  - platform: xiaomi_bslamp2
    presets:
      RGB:
        red:         { red: 100% }
        green:       { green: 100% }
        blue:        { blue: 100% }
        yellow:      { red: 100%, green: 100% }
        purple:      { red: 100%, blue: 100% }
      white:
        cold:        { color_temperature: 153 }
        CHILLY:      { color_temperature: 275 }
        luke:        { color_temperature: 400 }
        # bug? 588 shows error about white not being valid
        WARM:        { color_temperature: 587 }

binary_sensor:
  - platform: xiaomi_bslamp2
    id: ${id_color_button}
    part: color button
    on_multi_click:
      - timing:
          - ON for at most 0.6s
        then:
          - preset.activate:
              next: preset
      - timing:
          - ON for at least 0.6s
        then:
          - preset.activate:
              next: group

Note that you can define as many groups as you like, and it is possible to mix RGB and color temperature presets within the same group. Only need one RGB setting and one color temperature setting? No problem, then something like this will work just fine.

light:
  - platform: xiaomi_bslamp2
    presets:
      my:
        color: { red: 100%, green: 80%, blue: 20% }
        white: { color_temperature: 587 }

This has been such an awesome thread to follow. I don’t have one of these lights but this makes me want to get one. I can’t imagine how many hours this has taken so far. Well done on your efforts!

Epic :+1:

4 Likes

Thank you, that is an awesome compliment to start the weekend with :hugs:

1 Like

I finalized the support for presets.
This makes it possible to mimic the original firmware behavior where tapping the color button changes the light color, and holding the color button switches between RGB and color temperature light.
Of course, this is just one of the options. You can use the templates as you see fit.

For details on the configuration, check out the information from the pull request:

Here’s a video that shows the preset support in action, using the configuration from doc/example.yaml from the repo. The logging on the screen display the names of the presets that are being activated by the color button presses:

1 Like

Documentation added

The last few days, I have worked on extending the documentation for the Bedside Lamp 2 component. The biggest change would be the addition of the configuration guide, in which all components of the system are described.

Upcoming RC1 release

The code-base is now feature complete. If I’m right, the lamp can do everything that the original lamp can do. Therefore, I will soon move a bit closer towards a stable release by publishing 1.0.0-RC1.

Looking at the stability of the code, I think it would be fine to directly move towards a stable release. However, for now I will wait a bit with that. There are two external dependencies that would combine well with a stable release:

  • My stability patch for AsyncTCP
  • The upcoming external_components support in ESPHome (more on this below)

They would combine well, because they make it possible to fully configure and compile the firmware from the ESPHome dashboard, without having to manually move files into place. When these dependencies take too long to make it into the ESPHome stable release, then I might still choose to move to stable a bit earlier.

External components in ESPHome

The ESPHome team is working on a great option to work with components that are hosted on GitHub. They can be pulled into the build project by adding a bit of configuration to the device’s yaml conifguration file.

This makes it very easy to use third party components, without all the hassle of downloading the code, and manually moving it to the correct directories on the build system. For example the Bedside Lamp 2 code could be included in the build by adding something like this to the yaml config:

external_components:
  - source:
      type: git
      url: https://github.com/mmakaay/esphome-xiaomi_bslamp2
      ref: 1.0.0-RC1

To make this work, the repository needs to be structured in a standard way though, and the current repository is not. Because of the big jump in usability, I will happily make my repo compatible with the requirements.

I will take care of this change, before the next release.
It is tracked in issue #16.
A preview of the repo structure is already visible in this branch of the code.

2 Likes

The amount of work you put on this project is impressive :exploding_head:

That’s brilliant, congratulations for what you have done so far, it’s gold for the community :star_struck:

I’m keeping my eyes closed for a stable release :fire:

@mmakaay which specific FTDI cable is needed for the flashing? A TTL-232RG cable? 3V3 or 5V?

3.3V or 5V doesn’t matter. It is best to power the board inside the lamp using the power supply that comes with the lamp. There is a debug pad on the board which measures as 3.3V, and I have managed to flash the board after powering it with 3.3V on that pad, but it was a hit and miss situation. Someone else also had problems with flashing, which were solved after simply powering the lamp using its own power supply.

I think any cable that converts TTL RX/TX-level output should be fine. In the photo below, you can see my setup and my serial USB device (with only GND, TX and RX connected).
I’ve got the extra fancy setup, with a button to trigger the flash mode :-p

One thing to beware of is that you might need to install a driver to make the COM-port work in the OS. Under Windows10, I had to install the driver for the chipset on the USB device. It’s not hard, but just something to beware of (could have spared me a half hour of my life, had I realized that, before questioning my hardware setup :wink: ).

1 Like