I was gifted a "Lunvon Smart LED Bulb" (like this one) and, as one can expect from these cheap bluetooth-controlled RGB lights, both the iOS and Android apps are garbage. Analysing the bluetooth data from the Android app it is possible to decode the protocol used to set the light color and turn it on/off. The bulb exposes a few BLE services, but I wasn't able to get anything useful from those.
Below the source code of a python script that test the bulb RGB capabilities, tested on a Raspberry Pi 3.
import bluetooth
from string import hexdigits
from time import sleep
LAMP_ADDR = "f4:4e:fd:00:00:00"
LAMP_UUID = "00001101-0000-1000-8000-00805f9b34fb"
LAMP_LOGIN = "3031323334353637" # '01234567'
CMD_BASE = "01fe000053831000"
CMD_TURNON = "0000000050ff0000"
CMD_TURNOFF = "0000000050000000"
# RGB: BASE + "GGBBRR0050000000" where GG is green hex, etc.
# Bluetooth connection socket
btsock = None
def bt_send(data, debug=False):
cleanPayload = data.replace(":", "")
if debug:
print("> ", cleanPayload)
try:
btsock.send(bluetooth.binascii.unhexlify(cleanPayload))
except bluetooth.btcommon.BluetoothError as e:
print("! lamp disconnected")
lamp_connect()
def bt_read(n, debug=False):
d = bluetooth.binascii.hexlify(btsock.recv(n))
if debug:
print("< ", str(d))
return d
def lamp_turnon():
bt_send(CMD_BASE + CMD_TURNON)
def lamp_turnoff():
bt_send(CMD_BASE + CMD_TURNOFF)
def lamp_setrgb(rgb):
assert len(rgb) == 6
assert all(c in hexdigits for c in rgb)
hexR, hexG, hexB = rgb[0:2], rgb[2:4], rgb[4:6]
gbr = hexG + hexB + hexR + "0050000000"
bt_send(CMD_BASE + gbr)
def hsv2rgb(h,s,v):
assert 0 <= h < 360
assert 0 <= s <= 1
assert 0 <= v <= 1
c = s*v
x = c * (1 - abs((h/60) % 2 - 1))
m = v - c
r = g = b = 0
if 0 <= h < 60:
r, g = c, x
elif 60 <= h < 120:
r, g = x, c
elif 120 <= h < 180:
g, b = c, x
elif 180 <= h < 240:
g, b = x, c
elif 240 <= h < 300:
r, b = x, c
else:
r, b = c, x
return [ (r+m)*255, (g+m)*255, (b+m)*255 ]
def lamp_sethsv(h,s,v):
r, g, b = hsv2rgb(h,s,v)
rgbStr = "%.2X" % r + "%.2X" % g + "%.2X" % b
lamp_setrgb(rgbStr)
def lamp_connect(rgbTest=False):
global btsock
services = []
print("? searching for compatible devices ...")
# wait for the lamp to be avaible
while True:
services = bluetooth.find_service(
uuid=LAMP_UUID, address=LAMP_ADDR)
if len(services) > 0: break
print("? device not found, retrying in 5 seconds ...")
sleep(2)
lamp = services[0]
print("> lamp found [{}], connecting ...".format(LAMP_ADDR))
btsock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
btsock.connect((lamp['host'], lamp['port']))
# Login
print("> lamp 'login' ...")
bt_send(LAMP_LOGIN)
sleep(0.5)
if rgbTest:
print("> RGB test")
lamp_setrgb("220000")
sleep(0.4)
lamp_setrgb("002200")
sleep(0.4)
lamp_setrgb("000022")
sleep(0.4)
def lamp_color_loop():
print("> color loop ...")
while True:
for i in range(1, 360):
lamp_sethsv(i, 1, 1)
sleep(0.25)
if __name__ == "__main__":
lamp_connect(rgbTest=True)
lamp_color_loop()