Custom firmware ESPHome-Xiaomi_bslamp2

No need for that, I managed to implement an interrupt handler for it, using the tooling that is available from the ESPHome framework code :raised_hands:

Here’s a demo video, showing the responsiveness of the interrupt line handling. This looks damn fine to me, so now I can implement the code to read the events from the I2C bus.

1 Like

Wow…impressive :slight_smile:

So I can focus on implementing other new sensors to ESPHome this weekend :wink:

Hmm…still getting disconnects with the custom AsyncTCP package…

[09:34:06][C][api:096]:   Address: bedside_lamp.local:6053
ERROR Error while reading incoming messages: Error while receiving data: [Errno 9] Bad file descriptor
WARNING Disconnected from API: Timeout while waiting for message response!
INFO Connecting to bedside_lamp.local:6053 (10.0.100.92)
INFO Successfully connected to bedside_lamp.local

How can I make sure it does pick up the AsyncTCP package inside ./libs directory?

I do get these disconnects as well while connected to the logging. I simply ignore these errors for now and wait until I have a working connection, before I test something for which I need to see the logging output.
Another option is to not view logging via the network, but via the serial connection instead.

If you are not watching the logs and your lamp does not disconnect from home assistant occasionally, then you must be using my version. For me it fixed production mode, not development mode.
Btw, if you watch the serial console logs, and you see “ack timeout”, accompanied by a “Houston, we’ve got a problem”, then you also be sure that it is my version. I added the Houston bit especially for this purpose.

I do want to look further into this to also get the development mode stable, but right now I first want to get the firmware finished. One project at a time! :grin:

I had to learn quite a bit about the internal workings of the ESPHome configuration and code generation framework, but I managed to pull off some nice things that you can find in my latest commit (knowledge is power, but foremost fun :slight_smile: )

TL;DR

Things are shaping up really nicely.
If you are already using my component code, then make sure to update your device yaml configuration. The doc/example.yaml has been updated with the latest config requirements.
Main feat: it is way simpler now, and it will get even simpler in the upcoming commit.

Full commit info: Introduced a HUB component + front panel IRQ handling

A HUB component was introduced. This HUB component has all the knowledge
about the Yeelight Bedside Lamp 2 hardware. It known what pins are used,
that PWM frequencies to use, what pins to switch in binary mode, etc. etc.

No configuration is required for this HUB component. It’s automatically
loaded when the light component is loaded. The light component will use the
HUB component to access the pins that are required for driving the LED
circuitry.

Note that this simplifies the configuration by A LOT. There’s no need
anymore to configure the pinouts in the YAML file. This is a logical route
to take, since we’re talking about a factory-produced PCB with a soldered on
ESP32 chip, which uses the same GPIO’s and settings on all produced devices
(I presume). It would be quite redundant to force every user into
configuring these pinouts themselves.

Beware to update your device yaml configuration

There are a few pinouts left to move into the HUB. I will do that in the
next commit. Your device yaml configuration can be simplified along with
these changes. Some of the keys in the existing light configuration block
will no longer work and will have to be removed (red, green, blue, white).

Further development

The HUB will be extended make it the central component that also handles
the I2C communication. This way, there is a central place to regulate the
traffic to and from the front panel. We will be able to build upon this
by implementing extra, fully separated components that handle for example
the front panel light level, the power button, the color button and
the slider.

Interrupt handler for the I2C IRQ trigger pin

One requirement for the I2C communication has already been implemented: an
interrupt handler for the GPIO that is used by the front panel to signal the
ESP that a new touch or release event is avilable to be read.

It doens’t do anything functionally right now, but if you watch the log
file, you will see that touch events are detected and that they trigger some
log messages.

1 Like

Hello, I am working on a similar project for another yeelight lamp. I’m stuck on the I2C remote. I tried to put in the yaml but i2c device no found.
Your subject is interesting I put you but link of my two lamps 1 100% ok the other in progress. Maybe you will find some ideas.
Esphome YAML Lamp desk 1s 100% ok
yeelight screenbar In progress

Interesting stuff @dckiller51 , thanks for the lnks. Great to see that the desk lamp is fully controllable using the standard components.

Which desk lamp are you working on? The small linear one, or the larger circular one?

Thanks for all your hard work on this lamp! Now I just need to find time to attack my ‘No-LAN’ lamp and have a go at the upgrade.

Do not that this is still under heavy development. I assume you’re aware of this, since you commented in this thread, but mentioning it, just to be sure :wink:

I just got notified that new platform packages were available for the single core ESP32. I have updated my config to use these, in combination with my AsyncTCP fixes.
BTW: Otto Winter requested a pull request for the AsyncTCP fixes, so those fixes will likely end up in the standard release. I just created the said pull request.

I’m happy to announce that my network logging connection is now very stable. I don’t have multiple disconnects per minute anymore. Actually, I have none! :smiley:
Using 1.0.5, I did get an occasional reboot of the device while network logging was active.
Using 1.0.6 the device has been stable, even with logging active. I’ll still have to do some long term testing on this setup, but I very much like the results so far, so I updated the example configs in the repo.

If you want to test drive this version yourself, then update the following platformio settings in the yaml file:

esphome:
  platformio_options:
    platform: [email protected]
    platform_packages: |-4
          framework-arduinoespressif32 @ https://github.com/pauln/arduino-esp32.git#solo-no-mac-crc/1.0.6

After doing this change, it might be a good idea to clean out the build output folder for the project. @davorin has some issues after upgrading the config, which were fixed by cleaning out the build output.

My tuto forum french.

I’ll hold off for a while then. Happy to be a beta tester when you need some

1 Like

That’s the one that I have. Great work :grinning:

The light is already working. If you’re not too attached to having working front panel buttons on the device, then you can already do some beta testing.

I have just recently started on getting the front panel functionality implemented, so expect updates on those in the upcoming days.

I have the one working lamp as part of a light group that is mostly controlled by an Ikea switch and ControllerX (GitHub - xaviml/controllerx: Create controller-based automations with ease to control your home devices and scenes.).
My use of the front panel functionality is limited to seeing the brightness level set by the control.

I mentioned sometime back in the thread that being able to use the front panel control as a zone/ room controller would be a useful function, so this would be the next step for me.

Here’s a first update. I now have a good structure for handling the front panel interrupts that indicate that a new event is available. A few posts back, I showed an early preview of that.
Before implementing the whole event system, I first needed some more insight in the behavior in case the event rate is higher than the processing rate. I did some tests on this. The results can be found in my reverse engineering docs

TL;DR outcome

  • The front panel does not implement event message queueing.
  • Each event updates the panel’s output buffer with a new event.
  • This output buffer can be read as many times as we like after an
    event occurred. The resulting event will stay the same, until the
    next event.
  • Because of this, when events come in faster than they can be
    processed, events will get lost.

Based on this, I think it’s best to implement an event queue in my firmware. Mainly to prevent missing events where possible. → Update: It looks like this won’t be needed. The main loop looks quick enough to implement good event handling. Only when logging, things are a bit more sloppy. But that is not an issue for devices that are running in production. Simply tune down or disable the logging.

Update:

The firmware now reads the I2C messages from the front panel. I also added a parser that translates the incoming 7 byte I2C message into the correct event types.
This new code can be found here.

Below are some example loglines from the current code. Tomorrow I will continue with adding external triggers for these events, to make them available for automation purposes.

I found one extra level on the slider (on the low side)

On my device this one is quite hard to touch, because it is very close to the power button. In the further implementation, Therefore I might make both slider levels 1 and 2 work as the lowest brightness (which will be night ligt mode).

It would be interesting to see if slider level 1 is hard to reach on other people’s devices too.
If the lowest slider level is easy to reach on other devices, then I’ll either add config options for changing the behavior, or I’ll leave fixies for this up to the automations in the device YAML code.

Update

I have added some code to the front panel event parser that, if I did everything right, will not be hit in production at any time. The parser now logs any parsing error in a clear way. Here’s an example (in which I forced a wrong 0xff byte in the event code):

afbeelding

If any event was missed or misinterpreted, this will allow us to spot it and fix it.

I’ve got a first binary_sensor implementation working, and I started to do some tests with it. I created the following configuration:

light:
  - platform: yeelight_bs2
    name: Test RGBW Light
    id: test_rgbw_light

binary_sensor:
  - platform: yeelight_bs2
    id: test_rgbw_light_button
    on_multi_click:
    - timing:
        - ON for at most 0.8s
        - OFF for at least 0.2s
      then:
        - logger.log: "TEST click - turn on light"
        - light.turn_on: test_rgbw_light
    - timing:
        - ON for at most 0.8s
        - OFF for at most 0.2s
        - ON for at most 0.8s
        - OFF for at least 0s
      then:
        - logger.log: "TEST double click - turn off light"
        - light.turn_off: test_rgbw_light
    - timing:
        - ON for at least 0.8s
      then:
        - logger.log: "TEST hold - toggle light"
        - light.toggle: test_rgbw_light

And guess what …

The binary sensor currently reacts to all touch events, so double clicking…touching…whatever would also turn off the light. So that is the next step: being able to configure a binary_sensor for a specific part of the front panel (power button, color button or slider).

Just a thought: can we expose this as a device + events in HA?

It would be really cool to let the device / components show up in Home Assistant as a device, with some events that can be acted upon. That would make writing automations very simple and intuitive.

However, so far I haven’t seen integration like that. The general way in which events are propagated, seems to be to use service calls, Home Assistant events, MQTT and such to transport the data. Which is fine of course, it is not that much harder to attach Home Assistant automations to some event that is triggered from the device.

But having an actual device + events would give the end product a cute shiny polish, right? :wink:
I haven’t looked into this yet. If anyone known whether or not this is a possibility with ESPHome API integration, then please let me now (for better or for worse.)

Update: binary_sensor is available in the repo now

I pushed the new binary_sensor code to the repo.
Check out the example for the yaml file on how to use it

The binary_sensor has a property part. The value for this property can be one of:

  • any
  • power_button
  • color_button
  • slider

The part is used, as you might have guessed, to specify the part of the device for which the binary_sensor must handle the touch/release events.

Note that this is strictly a binary_sensor, so it will not tell you at what level the slider is touched. It will only tell you if the slider is touched or released. A new component will be written for having the slider level propagate as a sensor. That will be the next thing I will implement.

2 Likes

Sending events to HA from ESPHome is described here: Native API Component — ESPHome

Would this be the solution?

Yes, those are the Home Assistant events that I mentioned. It is easy to send these, and then configure automations that listen for this event. However, the type of integration that I was referring to is where you end up with device support that looks somewhat like this:

The biggest advantage here is that the user won’t have to know the exact event codes. It’s drop down menus all the way instead. Select the Yeelight BS2 device from the device drop down, select for example a “Power” button pressed trigger, and configure the actions for it.

Using events would of course still be possible when such integration were implemented.