Controlling BLE ceiling light with HA

Sorry - I meant the mobile app codebase.

Oh… I’ve used a decompiler for the Java part, and Ghidra for the native library.

Sounds like our lights have reversed positions for warm white and vppd white output, as well as different minimum brightness. That means we’ll need to introduce some additional properties to the YAML config to support that - something like reverse-cw-ww and min-brightness-value.

Let me know how your tests go.

EDIT:
Regarding your code changes:

  • BRIGHTNESS_LEVELS is unused (I forgot to remove it), so changing it won’t do a thing.
  • I suggest that instead of negating the values of cwf and wwf, just replace the order of the arguments on line 125 like so send_packet(0x21, wwi, cwi);
  • As for minimum brightness, it’s set to 0x1a based on the code of Lampify. I tried sending 0x01 to my light, and it turned it off, so I went back to the 0x1a value. Definitely possible that it can go lower than that, and also possible that the minimum value differs between brands… So experiment (replace the 0x1a on lines 116-117)

EDIT #2: I updated the code with the required changes to support the above. Please check it out and let me know if it works. Notice that it requires the latest version of ESPHome (2023.5+) to build, otherwise it will complain about the CONF_MIN_BRIGHTNESS import in the Python file.

Thanks for the quick update!

I’ve tested the code this morning:
Colour change worked, but i’ll try you version to see if that’s better

The Brightness level change made it not dimm at all… so it did do something :wink:

I’ll test your code and get back to you!

(i’m not able to change the 0x1a on lines 116-117 as there is no 0x1a value never mind, changed the lines to something else :wink: )
And how are you able to send the specific 0x01 value?

Ok, the results of your last code change:

Color Temperatures: Correct (ish), my light can get a lot Yellower / whiter with the remote
Brightness: If the value is above 50% or so, the temperature can still change, below the 50% value, the color temperature stays the same, so i’m not able to dim to a warmer color.

Question: Is it possible to retrieve/see the state the light is in when controlling it with the app or the remote? This way i might be able to see what my maximum and minimum values are.

Color Temperatures: Correct (ish), my light can get a lot Yellower / whiter with the remote

Can you try setting the cold_white_color_temperature and warm_white_color_temperature configuration options for the light? I haven’t documented it (will do), and it defaults to the values supported by my light - but the values are configurable. See here for an example: Cold White + Warm White Light — ESPHome

Brightness: If the value is above 50% or so, the temperature can still change, below the 50% value, the color temperature stays the same, so i’m not able to dim to a warmer color.

Interesting. Not sure what the issue might be… I suggest we resolve the temperatures correctly (see above), before we tackle this issue.

Question: Is it possible to retrieve/see the state the light is in when controlling it with the app or the remote? This way i might be able to see what my maximum and minimum values are.

Not currently, and I haven’t explored the possibility. I’ll have to see whether the lamp broadcasts any packets that I can catch. You could try recording the packets from the remote - though my code is not using packets compatible with the remote, so not sure I’ll be able to understand that data. How about controlling the light with the LampSmart Pro app - does that work correctly? Do you get the same range of temperature/brightness as with the remote?

I can’t set the brightness or the color as low as the remote with the app.

After setting the white values, things changed!
with the current settings:

light:
  - platform: lampsmart_pro_light
    name: Slaapkamer Lamp
    duration: 1000
    default_transition_length: 0s
    cold_white_color_temperature: 7000 K
    warm_white_color_temperature: 1800 K

and the following brightness levels:

  uint8_t cwi = 0x01 + (uint8_t)((0xff - 0x01) * cwf);
  uint8_t wwi = 0x01 + (uint8_t)((0xff - 0x01) * wwf);

The device dimms nicely yellowish like i’d like to.
However, like you stated, the values are to low.
When setting the values (in the brightness) to 0x01, Home Assistant is sending these values:

11:29:46	[D]	[lampsmartpro:119]	
LampSmartProLight::write_state called! Requested cw: 1, ww: 7
11:29:48	[D]	[light:035]	
'Slaapkamer Lamp' Setting:
11:29:48	[D]	[light:050]	
  Brightness: 22%
11:29:48	[D]	[light:050]	
  Brightness: 22%

So at 22% (warm white) the WW value is only 7, which results in 19% being just 3 and turning the light off.

If it’s 0x1A however, the CW value stays 26 the whole time, making the warm white a little to cold.

I’ll try changing the min_brightness Value and seeing what’ll happen.

EDIT:
changing the min_brightness value to 0xa kept the light on when dimming below 20%, however, when setting the light to warm white, the cold white value should be just off (not staying at around 10). If it stays at 10, when dimming the light, the cold white stays visible and thus making the light not as yellow as it should be.

I can’t set the brightness or the color as low as the remote with the app.

That means this might be a limiting factor - i.e., we might not reach the values possible with a remote, since we’re using the protocol used by the app… But maybe if we adjust the values correctly, it’ll work.

If it’s 0x1A however, the CW value stays 26 the whole time, making the warm white a little to cold.

I see now the problem with my code… There’s no reason for the minimum brightness to be applied to both the cold and warm values. I will push a fix soon (and edit this post when it’s done).

Also note that you don’t have to change the minimum brightness at the cwi and wwi values manually in code - you can just use the min_brightness configuration in YAML, and it should sort it out.

thanks! i tought the same, just didn’t know how to fix it fast :wink:

Could you give an example of the Yaml Code? (with the Min_brightness added)

Done, please check the latest version of the code :slight_smile:

Fixed the minimum brightness logic, now the temperature will be correct at lowest brightness settings (though, of course, at minimum brightness, the temperature resolution is rather abysmal - it’s either warm, cold, or both - nothing in-between).

Regarding a sample YAML - here you go:

# Enable LightSmart Pro Light
light:
  - platform: lampsmart_pro_light
    name: Kitchen Light
    duration: 1000
    default_transition_length: 0s
    min_brightness: 0x7

But you don’t need that any longer - I’ve updated the default value for minimum brightness to 7, it seems to work well. 0x1a (and 0x10) were definitely too high.

1 Like

Worked like a charm.
didn’t add the brightness part,
changed the wwi - cwi part in the code, but i guess this can be changed by adding:

light:
  - platform: lampsmart_pro_light
    name: Kitchen Light
    duration: 1000
    default_transition_length: 0s
    reversed: true

Thanks for the quick updates and the awesome work!!

No problem - glad to hear it worked! And yeah, reversed: true should take care of it and allow you to use the code as-is, without changes.

@aronsky I am so happy you got something going. :+1: I have 3 lights…are we saying I can only add 1 light or I can add 3 but will control them all with the same values? I tried putting the code into a DualR3 yesterday but getting some errors while compiling. Will setup another ESP32 only for this later to test.

Not sure what’s a DualR3… I’ve only tested this on a regular ESP32. Is the DualR3 based on ESP32? Dose it have BLE?

Regarding multiple lights - yeah, if you have multiple lights - they will all appear as a single light, as far as ESPHome and Home Assistant are concerned. It should be possible to differentiate in the future, but I need help with that in terms of implementing it correctly on the ESPHome side (I think I have a pretty good idea regarding how it should work on the lights side, but it requires some testing).

  [reversed] is an invalid option for [light.lampsmart_pro_light]. Please check the indentation.
  reversed: True

Sadly no luck. → got it working after a few tries
Will keep my local version running

Multiple lights would be awesome, if I can help out with testing let me know!
Would help to know the BLE Mac address of the device it’s connected to, is it possible to add this as a sensor? (or see it in the logs)

Sadly no luck.
Will keep my local version running

Strange, it works for me… Any chance the light.py file wasn’t updated with my latest changes? It’s complaining about indentation - any chance you mixed spaces and tabs, maybe that’s the issue?

Multiple lights would be awesome, if I can help out with testing let me know!
Would help to know the BLE Mac address of the device it’s connected to, is it possible to add this as a sensor? (or see it in the logs)

Currently, the component is strictly one-way (the ESP32 controls the light, and doesn’t receive any information back), and the MAC address isn’t used for that control. The light simply listens to broadcast BLE traffic, and whatever packet matches the predefined structure (and, I think, includes an identifier that is broadcast during the 5-second pairing period) is processed and its command is executed. So as of now, knowing the MAC address of the light is irrelevant.

If you take a look at my code, specifically at the packet structure that’s being used in the send_packet method, one of the fields (called identifier) is set to a predefined value 0xcafebabe. I think that by using different values for different lights, control over separate lights is possible. But I’m not sure how to do it correctly on the ESPHome side.

I’d like to register a service per each instance of the light component (well, ideally - I’d like to register a single service, and receive the component instance as an argument to the service). But for some reason, I can’t seem to debug the LampSmartProLight::setup method properly - it’s definitely getting called (the service is registered), but I’m not sure at which point - and logging doesn’t work that early (maybe it would work over USB, I don’t know).

We’d need to generate a unique, 32-bit ID during construction of LampSmartProLight (and base it on the entity ID - another problem I faced, as I don’t seem to be able to get that info using ESPHome API). And then this ID would be used instead of 0xcafebabe, making each instance unique.

You’re welcome to give it a go and create a PR, if successful.

It is the Sonoff DualR3 and it is an ESP32 based with BLE. I am already using one as the Bluetooth Proxy.

I see. If you have any logs, I can try and help. Also, I think that using the BT proxy and the LampSmart Pro component on the same unit might not work - maybe that’s the issue?

Removed all of Bluetooth Proxy and still get error. Not sure where I went wrong? Log as below.

INFO Reading configuration /config/esphome/esp32-bluetooth-proxy-1856c8.yaml...
INFO Generating C++ source...
INFO Compiling app...
Processing esp32-bluetooth-proxy-1856c8 (board: esp32dev; framework: espidf; platform: platformio/[email protected])
--------------------------------------------------------------------------------
Removing unused dependencies...
Library Manager: Removing Improv @ 1.2.3
INFO Removing Improv @ 1.2.3
Library Manager: [email protected] has been removed!
INFO [email protected] has been removed!
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
 - framework-espidf @ 3.40404.0 (4.4.4) 
 - tool-cmake @ 3.16.4 
 - tool-ninja @ 1.7.1 
 - toolchain-esp32ulp @ 2.35.0-20220830 
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
Reading CMake configuration...
Generating assembly for certificate bundle...
Dependency Graph
|-- noise-c @ 0.1.4
|   |-- libsodium @ 1.10018.1
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/api/api_connection.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/api/api_frame_helper.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/api/api_pb2.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/api/api_pb2_service.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/api/api_server.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/api/list_entities.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/api/proto.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/api/subscribe_state.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/api/user_services.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/binary_sensor/automation.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/binary_sensor/binary_sensor.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/binary_sensor/filter.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/esp32/core.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/esp32/gpio.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/esp32/preferences.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/esp32_ble/ble.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/esp32_ble/ble_advertising.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/esp32_ble/ble_uuid.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/lampsmart_pro_light/lampsmart_pro_light.o
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/light/addressable_light.o
src/esphome/components/lampsmart_pro_light/lampsmart_pro_light.cpp: In function 'void esphome::lampsmartpro::ble_whiten(uint8_t*, uint8_t, uint8_t, uint8_t)':
src/esphome/components/lampsmart_pro_light/lampsmart_pro_light.cpp:73:44: error: suggest parentheses around '+' in operand of '&' [-Werror=parentheses]
     buf[i] ^= XBOXES[(seed + i + 9) & 0x1f + (salt & 0x3) * 0x20];
                                       ~~~~~^~~~~~~~~~~~~~~~~~~~~
src/esphome/components/lampsmart_pro_light/lampsmart_pro_light.cpp: In member function 'void esphome::lampsmartpro::LampSmartProLight::send_packet(uint16_t, uint8_t, uint8_t)':
src/esphome/components/lampsmart_pro_light/lampsmart_pro_light.cpp:163:4: warning: missing initializer for member 'esphome::lampsmartpro::adv_data_t::<unnamed struct>::crc16' [-Wmissing-field-initializers]
   }};
    ^
Compiling /data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/light/automation.o
cc1plus: some warnings being treated as errors
*** [/data/esp32-bluetooth-proxy-1856c8/.pioenvs/esp32-bluetooth-proxy-1856c8/src/esphome/components/lampsmart_pro_light/lampsmart_pro_light.o] Error 1
========================= [FAILED] Took 20.11 seconds =========================

My yaml config…

substitutions:

  devicename: esp32-bluetooth-proxy-1856c8

  friendly_name: Living BT-Proxy

esphome:

  name: $devicename

  name_add_mac_suffix: false

  friendly_name: $friendly_name

esp32:

  board: esp32dev

  framework:

    type: esp-idf

# Enable logging

logger:

  baud_rate: 0

  level: VERBOSE

# Enable Home Assistant API

api:

  encryption:

    key: !secret esphome_api_key

ota:

  password: !secret esphome_ota_password

wifi:

  ssid: !secret esphome_wifi_ssid

  password: !secret esphome_wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails

  ap:

    ssid: "$friendly_name Fallback Hotspot"

    password: !secret esphome_wifi_ap_password

#LampSmart Pro

external_components:

  # shorthand

  source: github://aronsky/esphome-components

sensor:

  - platform: uptime

    name: Uptime Sensor

  - platform: wifi_signal

    name: WiFi Signal

    update_interval: 60s

  - platform: template

    name: Free Memory

    lambda: return heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024;

    unit_of_measurement: 'kB'

    state_class: measurement

binary_sensor:

  - platform: status

    name: Status

#LampSmart Pro

light:

  - platform: lampsmart_pro_light

    name: Main Hallway

    duration: 1000

    default_transition_length: 0s

I’m not in front of my computer, so can’t verify versions. Can you try and remove the framework specification from your YAML? Might help… That definitely seems like some issue with a compiler version, since it’s complaining about syntax that’s treated correctly on my side…