Skip to content

Commit a5def7a

Browse files
committed
Add prototype
1 parent 0678322 commit a5def7a

File tree

5 files changed

+127
-2
lines changed

5 files changed

+127
-2
lines changed

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
# laemp
2-
A bridge that allows you to control your Bluetooth-capable Philips Hue lights via HomeKit
1+
# lämp
2+
3+
A bridge that allows you to control your Bluetooth-capable Philips Hue lights via HomeKit.

hap_bridge.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import signal
2+
from pyhap.accessory import Bridge
3+
from pyhap.accessory_driver import AccessoryDriver
4+
from hap_lamp import LampAccessory
5+
6+
LAMPS = {
7+
'Hue Go': '00:11:22:33:44:55:66'
8+
}
9+
10+
def get_bridge(driver):
11+
bridge = Bridge(driver, 'Laemp Bridge')
12+
for name, address in LAMPS.items():
13+
lamp = LampAccessory(driver, display_name=name, address=address)
14+
bridge.add_accessory(lamp)
15+
return bridge
16+
17+
driver = AccessoryDriver(port=51826)
18+
driver.add_accessory(accessory=get_bridge(driver))
19+
20+
signal.signal(signal.SIGTERM, driver.signal_handler)
21+
22+
driver.start()

hap_lamp.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from pyhap.accessory import Accessory
2+
from pyhap.const import CATEGORY_LIGHTBULB
3+
from lamp import Lamp
4+
from colorsys import rgb_to_hsv, hsv_to_rgb
5+
import asyncio
6+
7+
class LampAccessory(Accessory):
8+
9+
category = CATEGORY_LIGHTBULB
10+
11+
def __init__(self, *args, address, **kwargs):
12+
super().__init__(*args, **kwargs)
13+
self.lamp = Lamp(address)
14+
15+
serv_light = self.add_preload_service('Lightbulb', chars=['On', 'Hue', 'Saturation', 'Brightness'])
16+
self.char_on = serv_light.configure_char('On', setter_callback=self._async_callback(self.set_on))
17+
self.char_hue = serv_light.configure_char('Hue', setter_callback=self._async_callback(self.set_hue))
18+
self.char_saturation = serv_light.configure_char('Saturation', setter_callback=self._async_callback(self.set_saturation))
19+
self.char_brightness = serv_light.configure_char('Brightness', setter_callback=self._async_callback(self.set_brightness))
20+
21+
def _async_callback(self, coro):
22+
return lambda val: asyncio.create_task(coro(val))
23+
24+
async def run(self):
25+
self.loop = asyncio.get_event_loop()
26+
self.lock = asyncio.Lock(loop=self.loop)
27+
await self.lamp.connect()
28+
29+
async def stop(self):
30+
await self.lamp.disconnect()
31+
32+
async def set_on(self, value):
33+
async with self.lock:
34+
await self.lamp.set_power(value)
35+
36+
async def set_hue(self, hue):
37+
async with self.lock:
38+
_, s, b = rgb_to_hsv(*await self.lamp.get_color_rgb())
39+
await self.lamp.set_color_rgb(*hsv_to_rgb(hue / 360, s, b))
40+
41+
async def set_saturation(self, saturation):
42+
async with self.lock:
43+
h, _, b = rgb_to_hsv(*await self.lamp.get_color_rgb())
44+
await self.lamp.set_color_rgb(*hsv_to_rgb(h, saturation / 100, b))
45+
46+
async def set_brightness(self, brightness):
47+
async with self.lock:
48+
await self.lamp.set_brightness(brightness / 100)

lamp.py

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from bleak import BleakClient
2+
from rgbxy import Converter, GamutC
3+
from struct import pack, unpack
4+
5+
CHAR_POWER = "932c32bd-0002-47a2-835a-a8d455b859dd"
6+
CHAR_BRIGHTNESS = "932c32bd-0003-47a2-835a-a8d455b859dd"
7+
CHAR_COLOR = "932c32bd-0005-47a2-835a-a8d455b859dd"
8+
9+
# Hue Go uses GamutC
10+
# you might need GamutA or GamutB if you have a different model
11+
converter = Converter(GamutC)
12+
13+
class Lamp(object):
14+
def __init__(self, address):
15+
self.client = BleakClient(address)
16+
17+
async def connect(self):
18+
await self.client.connect()
19+
20+
async def disconnect(self):
21+
await self.client.disconnect()
22+
23+
async def get_power(self):
24+
return await self.client.read_gatt_char(CHAR_POWER)[0]
25+
26+
async def set_power(self, on):
27+
await self.client.write_gatt_char(CHAR_POWER, bytes([1 if on else 0]), response=True)
28+
29+
async def get_brightness(self):
30+
return await self.client.read_gatt_char(CHAR_BRIGHTNESS)[0] / 255
31+
32+
async def set_brightness(self, brightness):
33+
await self.client.write_gatt_char(CHAR_BRIGHTNESS, bytes([max(min(int(brightness * 255), 254), 1)]), response=True)
34+
35+
async def get_color_xy(self):
36+
buf = await self.client.read_gatt_char(CHAR_COLOR)
37+
x, y = unpack('<HH', buf)
38+
return x / 0xFFFF, y / 0xFFFF
39+
40+
async def set_color_xy(self, x, y):
41+
# Hue expects CIE XY coordinates converted to two 16-bit little-endian integers
42+
buf = pack('<HH', int(x * 0xFFFF), int(y * 0xFFFF))
43+
await self.client.write_gatt_char(CHAR_COLOR, buf, response=True)
44+
45+
async def get_color_rgb(self):
46+
x, y = await self.get_color_xy()
47+
return converter.xy_to_rgb(x, y)
48+
49+
async def set_color_rgb(self, r, g, b):
50+
x, y = converter.rgb_to_xy(r, g, b)
51+
await self.set_color_xy(x, y)

requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
bleak==0.11.0
2+
HAP-python[QRCode]==3.4.1
3+
git+https://github.com/benknight/hue-python-rgb-converter.git@f73a4ec#egg=rgbxy

0 commit comments

Comments
 (0)