Bluetooth-Devices / led-ble

Control a wide range of LED BLE devices
Apache License 2.0
12 stars 8 forks source link

Support for Govee LED strips #40

Open amaciuc opened 10 months ago

amaciuc commented 10 months ago

Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Would there be a chance to bring support for BLE Govee LED RGB strips ?
I own some strips with product model H6159, but there are many other models of strips with BLE.

Describe the solution you'd like A clear and concise description of what you want to happen.

Additional context Add any other context or screenshots about the feature request here.

amaciuc commented 10 months ago

I think it has more sense this issue to be moved in govee-ble project.

I found on github a project where it has been implemented a library by using reverse engineering to control BLE RGB light bulb from Govee. It is using pygatt python library in order to interact with BLE adapter.

I gave it a chance to interact with H6159 and H6109 RGB LED strips (From what I can notice, it has support also for white control. I couldn't test it) and surprising, it works pretty well. That leads me to think that this project can support many others BLE LEDs devices from Govee.

I used an ASUS UB-BT500 BLE adapter in order to communicate with the devices.

I own 4 RGB LED strips: 3 strips with model H6159 and 1 strip H6109.

The github project offers a python script for testing. I made the following tests:

from govee_btled import BluetoothLED, ConnectionTimeout

try:

Replace this with your LED's MAC address

# led = BluetoothLED('A4:C1:38:47:95:79') # bedroom - short one
led = BluetoothLED('A4:C1:38:9C:76:A0') # bedroom - long one
# led = BluetoothLED('A4:C1:38:A6:3B:77') # living - short one
# led = BluetoothLED('A4:C1:38:A6:AD:AC') # living - long one

print('Switching on LED')
led.set_state(True)
time.sleep(.5)
while True:
    print('Changing colors in RGB')
    for color in ['red', 'green', 'blue', 'purple', 'yellow', 'cyan', 'orange', 'white']:
        print(f'[*] {color}')
        led.set_color(color)
        time.sleep(.5)

    print('Changing brightness')
    for i in range(5+1):
        val = i/5
        print(f'[*] {int(val*100):03d}%')
        led.set_brightness(val)
        time.sleep(.5)

# print('Changing colors in white-mode')
# for i in range(-20, 20+1):
#     val = i/20
#     print(f'[*] {abs(int(val*100)):03d}% {"warm" if val <= 0 else "cold"} white')
#     led.set_color_white(val)
#     time.sleep(.2)

# print('Switching off LED')
# led.set_state(False)

except ConnectionTimeout as err: print(err) except KeyboardInterrupt: print('Switching off LED') led.set_state(False) print('^C')

-  changing colors and brightness into a script where all 4 strips are connected & controlled simultaneously on threads:
```python
import time

from govee_btled import BluetoothLED, ConnectionTimeout
from pygatt import exceptions as pygatt_e
import logging
from threading import Thread, current_thread
import time

def thread_led(mac_add):
    try:
        print(f'[{mac_add}] Connecting ...')
        led = BluetoothLED(mac_add)
        time.sleep(.5)

        print(f'[{mac_add}] Switching on LED')
        led.set_state(True)
        time.sleep(.5)

        th = current_thread()
        th.alive = True

        while th.alive:
            print(f'[{mac_add}] Changing colors in RGB')
            for color in ['red', 'green', 'blue', 'purple', 'yellow', 'cyan', 'orange', 'white']:
                print(f'[{mac_add}] [*] {color}')
                led.set_color(color)
                time.sleep(.5)

            print(f'[{mac_add}] Changing brightness')
            for i in range(5+1):
                val = i/5
                print(f'[{mac_add}] [*] {int(val*100):03d}%')
                led.set_brightness(val)
                time.sleep(.5)

        if not th.alive:
            print(f'[{mac_add}] Switching off LED')
            led.set_state(False)

    except ConnectionTimeout as err:
        print(f'[{mac_add}] {err}')

        # My LEDs don't have while mode #
        # print('Changing colors in white-mode')
        # for i in range(-20, 20+1):
        #     val = i/20
        #     print(f'[*] {abs(int(val*100)):03d}% {"warm" if val <= 0 else "cold"} white')
        #     led.set_color_white(val)
        #     time.sleep(.2)

# ---------------------- #
def main ():
    # BT lib is logging through log lib. Uncomment it if you want BT cmds #
    # logging.basicConfig(format="%(asctime)s: %(message)s", level=logging.INFO,
    #                     datefmt="%H:%M:%S")

    mac_adds_list = ['A4:C1:38:47:95:79', 'A4:C1:38:9C:76:A0', 'A4:C1:38:A6:3B:77', 'A4:C1:38:A6:AD:AC']
    # mac_adds_list = ['A4:C1:38:47:95:79', 'A4:C1:38:9C:76:A0']
    threads_list = []

    try:
        print("Creating threads ...")
        for mac in mac_adds_list:
            th = Thread(target = thread_led, args=(mac,))
            threads_list.append(th)

        print("Starting threads ...")
        for th in threads_list:
            th.start()

        print("Waiting to finish ...")
        for th in threads_list:
            th.join()
    except KeyboardInterrupt:
        print("Keyboard interrupt received. Stopping threads ...")
        for th in threads_list:
            th.alive = False
            th.join()

if __name__ == "__main__":
    main()

In this situation, I almost can say that it works, but there are some issues. I am not sure that this test on threads is so relevant for integration, I thought it maybe helps to see if USB adapter is reliable with multiple connection in the same time.

In all my tests I have monitored also the bluetooth events by using bluetoothctl. When I ran that script with 4 strips, the connections haven't established with success all the time. Sometimes only 3 connections have been established. Once a connection is established, I noticed that it is reliable. If a try a re connection, the pygatt lib is resetting the BLE adapter (I found that in the start() method from GATTToolBackend class of pygatt lib) and all the other established connection will be dead, therefore this problem could be from a lower level (not quite from this wrapper). As I mentioned above, I am not sure that this test is relevant. If it raises suspicions, I will try to deep into it.

As a conclusion, I found this python wrapper a robust one and it works well so far.

@bdraco, what do you think?

bdraco commented 10 months ago

https://reddit.com/r/Govee/s/6WIWyqIUef

amaciuc commented 10 months ago

How fast you found it :smiley: Around 2 weeks ago I had searched after an alternative to integrate Govee in hassio through BLE.

Anyway, I installed it and it works fine for the moment.

I think you can close it.