The result is I've written a Python script that allows command-line control over the bulbs. They can be turned on and off, the brightness can be controlled, they can be set to colour or white mode, and the colour temperature of the white can be controlled.
I have also figured out how to set their internal timers and toggle their 'disco' mode but haven't bothered putting that in my script. For the timers, I have more faith in just setting cron jobs on the Pi to control things. Eg. come winter, I'm liking the thought of a light in my bedroom coming on when it's time for me to get up.
Finally, I've put in a status function to grab the bulb's current settings. It's mostly just for fun but can be useful in debugging.
Here's some examples of code output:
Code: Select all
pi@mcpi ~ $ sudo beewibulb
Correct usage is "[sudo] beewibulb <device address> <command> [argument]"
<device address> in the format XX:XX:XX:XX:XX:XX
Commands: toggle - toggle light on / off
on - turn light on
off - turn light off
bright - set brightness to 100%
medium - set brightness to 60%
dim - set brightness to 10%
colour RRGGBB - set bulb to colour RRGGBB
white - set bulb to white mode
warm - set white to warm CCT
cool - set white to cool CCT
Code: Select all
pi@mcpi ~ $ sudo beewibulb D0:39:72:XX:XX:XX status
bulb name = Zone 1 Bulb
firmware = V2.2 R141224
raw status = 01 b2 00 00 ff
bulbIsOn = True
bulbIsWhite = True
brightness = 100%
white tone = cool
Code: Select all
#!/usr/bin/env python2.7
# A python script to control the BeeWi 'BBL207 - Smart LED Color Bulb'
# BeeWi website: http://www.bee-wi.com/en.cfm
# Tested & working with this bulb in particular: http://www.bee-wi.com/bbl207,us,4,BBL207-A1.cfm
#
# by Stephanie Maks
#
# Figured out with help from -- and thanks to -- the following sources:
# http://stackoverflow.com/questions/24853597/ble-gatttool-cannot-connect-even-though-device-is-discoverable-with-hcitool-lesc
# http://mike.saunby.net/2013/04/raspberry-pi-and-ti-cc2541-sensortag.html
# https://github.com/sandeepmistry/node-yeelight-blue
#
# note: this needs to be called with 'sudo' or by root in order to work with some of the hci commands
import os
import sys
import socket
from subprocess import call, check_output
import pexpect
import time
def cycleHCI() :
# maybe a useless time waster but it makes sure our hci0 is starting fresh and clean
# nope in fact we need to call this before each time we do hci or gatt stuff or it doesn't work
call(['hciconfig', 'hci0', 'down'])
time.sleep(0.1)
call(['hciconfig', 'hci0', 'up'])
time.sleep(0.1)
def getSettings(mac) :
# read the settings characteristic (read handle is 0x0024)
# command return value = 'Characteristic value/descriptor: 00 b2 ff 00 00'
# function returns settings as array of five integers
cycleHCI();
settings_array = [0,0,0,0,0]
raw_input=check_output(['gatttool', '-i', 'hci0', '-b', mac, '--char-read', '--handle=0x0024']);
if ':' in raw_input:
raw_list=raw_input.split(':')
raw_data=raw_list[1]
raw_data=raw_data.strip()
octet_list=raw_data.split(' ')
if len(octet_list) > 4 :
for i in range(0, 5) :
j = int(octet_list[i], 16)
settings_array[i] = j
return settings_array
def getDeviceInfo(mac) :
deviceName = ''
deviceVers = ''
raw_input=check_output(['gatttool', '-i', 'hci0', '-b', mac, '--char-read', '--handle=0x0003']);
if ':' in raw_input:
raw_list=raw_input.split(':')
raw_data=raw_list[1]
raw_data=raw_data.strip()
octet_list=raw_data.split(' ')
for octet in octet_list :
j = int(octet, 16)
if j > 31 and j < 127 : deviceName += str(unichr(j))
raw_input=check_output(['gatttool', '-i', 'hci0', '-b', mac, '--char-read', '--handle=0x0018']);
if ':' in raw_input:
raw_list=raw_input.split(':')
raw_data=raw_list[1]
raw_data=raw_data.strip()
octet_list=raw_data.split(' ')
for octet in octet_list :
j = int(octet, 16)
if j > 31 and j < 127 : deviceVers += str(unichr(j))
cycleHCI()
return (deviceName, deviceVers)
def writeCommandToBulb(mac, the_command) :
# settings commands are sent to the 0x0021 characteristic
# all commands are prefixed with 0x55 (85) and suffixed with 0x0d 0x0a (CR LF)
if the_command != '' :
the_command = '55' + the_command + '0D0A'
cycleHCI()
hd = check_output(['/usr/local/bin/hcitool', '-i', 'hci0', 'lecc', mac])
ha = hd.split(' ');
connectionHandle=0
if ha[0] == 'Connection' and ha[1] == 'handle' : connectionHandle = ha[2]
if connectionHandle > 0 :
bulb = pexpect.spawn('gatttool -i hci0 -b ' + mac + ' -I')
bulb.expect('\[LE\]>', timeout=60)
bulb.sendline('connect')
bulb.expect('\[LE\]>', timeout=60)
bulb.sendline('char-write-cmd 0x0021 ' + the_command)
bulb.expect('\[LE\]>', timeout=60)
bulb.sendline('disconnect')
bulb.expect('\[LE\]>', timeout=60)
bulb.sendline('exit')
bulb.expect(pexpect.EOF, timeout=60)
call(['hcitool', '-i', 'hci0', 'ledc', connectionHandle])
cycleHCI()
def setBrightness(mac, value) :
# brightness command is 12 [02 to 0B] (18, [2 to 11])
if value == 'low' :
writeCommandToBulb(mac, '1202')
elif value == 'med' :
writeCommandToBulb(mac, '1207')
else :
writeCommandToBulb(mac, '120B')
def setBulbWhiteTemp(mac, tone) :
# brightness command is 11 [02 to 0B] (17, [2 to 11])
if tone == 'warm' :
writeCommandToBulb(mac, '110B')
elif tone == 'cool' :
writeCommandToBulb(mac, '1102')
def setBulbWhite(mac) :
# set to white command is 14 FF FF FF (20, -128, -128, -128)
writeCommandToBulb(mac, '14FFFFFF')
def setBulbColour(mac, colour) :
# colour command is 13 RR GG BB (19, Red, Green, Blue)
value = '13' + colour
writeCommandToBulb(mac, value)
def switchBulbOn(mac) :
# on command is 10 01 (16, 1)
writeCommandToBulb(mac, '1001')
def switchBulbOff(mac) :
# off command is 10 00 (16, 0)
writeCommandToBulb(mac, '1000')
def printHelp() :
print 'Correct usage is "[sudo] beewibulb <device address> <command> [argument]"'
print ' <device address> in the format XX:XX:XX:XX:XX:XX'
print ' Commands: toggle - toggle light on / off'
print ' on - turn light on'
print ' off - turn light off'
print ' bright - set brightness to 100%'
print ' medium - set brightness to 60%'
print ' dim - set brightness to 10%'
print ' colour RRGGBB - set bulb to colour RRGGBB'
print ' white - set bulb to white mode'
print ' warm - set white to warm CCT'
print ' cool - set white to cool CCT'
if __name__=='__main__' :
if os.geteuid() != 0 :
print 'WARNING: This script may not work correctly without sudo / root. Sorry.'
if len(sys.argv) < 3 :
printHelp()
else :
bulbIsOn = False
bulbIsWhite = False
device_address = sys.argv[1]
command = sys.argv[2]
command = command.lower()
extra = ''
error = ''
if len(sys.argv) == 4 : extra = sys.argv[3]
extra = extra.lower()
#
# address shortcuts
if device_address == 'pp' : device_address = 'D0:39:72:XX:XX:XX'
if device_address == 'z1' : device_address = 'D0:39:72:XX:XX:XX'
if device_address == 'z4' : device_address = 'D0:39:72:XX:XX:XX'
#
if len(device_address) != 17 :
print 'ERROR: device address must be in the format NN:NN:NN:NN:NN:NN'
exit()
settings = getSettings(device_address)
if (settings[0] % 2) == 1 : bulbIsOn = True
if (settings[1] & 15) > 0 : bulbIsWhite = True
if command == 'colour' and extra == 'ffffff' : command = 'white'
if 'stat' in command :
name, version = getDeviceInfo(device_address)
status = '%02x %02x %02x %02x %02x' % (settings[0], settings[1], settings[2], settings[3], settings[4])
colour = '%02x%02x%02x' % (settings[2], settings[3], settings[4])
brightness = (((settings[1] & 240) >> 4) - 1) * 10
print 'bulb name = ' + name
print 'firmware = ' + version
print 'raw status = ' + status
print 'bulbIsOn = ' + str(bulbIsOn)
print 'bulbIsWhite = ' + str(bulbIsWhite)
print 'brightness = ' + str(brightness) + '%'
if bulbIsWhite :
cct = (settings[1] & 15)
if cct == 2 : cct = 'cool'
if cct == 11 : cct = 'warm'
print 'white tone = ' + cct
else :
print 'colour = ' + colour
elif command == 'on' :
if not bulbIsOn : switchBulbOn(device_address)
elif command == 'off' :
if bulbIsOn : switchBulbOff(device_address)
elif command == 'toggle' :
if bulbIsOn :
switchBulbOff(device_address)
else :
switchBulbOn(device_address)
elif bulbIsOn :
if command == 'bright' :
setBrightness(device_address, 'high')
elif command == 'dim' :
setBrightness(device_address, 'low')
elif command == 'medium' :
setBrightness(device_address, 'med')
elif command == 'warm' or command == 'cool' :
if bulbIsWhite : setBulbWhiteTemp(device_address, command)
elif command == 'white' :
if not bulbIsWhite : setBulbWhite(device_address)
elif command == 'colour' :
if len(extra) == 6 :
setBulbColour(device_address, extra)
elif extra == '' :
extra = '%02x%02x%02x' % (settings[2], settings[3], settings[4])
setBulbColour(device_address, extra)
else :
error = 'ERROR: colour command requires a 6-digit colour argument, in hex. eg. 6633CC'
else :
printHelp()
else :
error = 'off'
if error != '' :
if error == 'off' : error = 'ERROR: Turn the bulb on before trying to change the settings.'
print error
exit()
Cheers!