forked from Speedy1985/enigmalight
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
377 additions
and
1 deletion.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
elight-addons/wifilight/philips hue/enigmalight.conf.example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#[global] | ||
|
||
[device] | ||
name ambilight | ||
output python /home/elight-addons/wifilight/philips_hue/enigmalight_hue_LR.py | ||
channels 6 | ||
type popen | ||
interval 200000 | ||
debug off | ||
|
||
[color] | ||
name red | ||
rgb FF0000 | ||
|
||
[color] | ||
name green | ||
rgb 00FF00 | ||
|
||
[color] | ||
name blue | ||
rgb 0000FF | ||
|
||
[light] | ||
position left | ||
name 1HU | ||
color red ambilight 1 | ||
color green ambilight 2 | ||
color blue ambilight 3 | ||
hscan 0 25 | ||
vscan 0 100 | ||
|
||
[light] | ||
position right | ||
name 2HU | ||
color red ambilight 4 | ||
color green ambilight 5 | ||
color blue ambilight 6 | ||
hscan 75 100 | ||
vscan 0 100 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import sys | ||
import os | ||
import time | ||
import json | ||
import httplib | ||
from rgb_xy import Converter | ||
from rgb_xy import GamutC # or GamutA, GamutB (you must look for the type of your lamps in rgb_xy.py from line 42) | ||
counter = 12 | ||
|
||
|
||
def popen(): | ||
converter = Converter(GamutC) | ||
spidev = file( os.getcwd()+'/aufruf.log', "wb") | ||
key = "HIER DEN KEY DER BRIDGE EINTRAGEN" | ||
ip = "xxx.xxx.xxx.xxx" | ||
url = '/api/' + key + '/lights/' | ||
lurl = url + '10/state' | ||
rurl = url + '11/state' | ||
|
||
MINIMAL_VALUE=0.000000000 | ||
|
||
while True: | ||
eingabe = sys.stdin.readline() | ||
|
||
if len(eingabe)>0: | ||
global counter | ||
counter += 1 | ||
|
||
try: | ||
lr,lg,lb,rr,rg,rb,x = eingabe.split(' ') | ||
except ValueError: | ||
spidev.write("Not enough input parameter, do you have the same amount of lights (channels) in your enigmalight config?") | ||
spidev.flush() | ||
raise | ||
|
||
lr = (float(lr))*255 | ||
lg = (float(lg))*255 | ||
lb = (float(lb))*255 | ||
rr = (float(rr))*255 | ||
rg = (float(rg))*255 | ||
rb = (float(rb))*255 | ||
|
||
lll = calcLuminance(lr,lg,lb) | ||
llr = calcLuminance(rr,rg,rb) | ||
|
||
if (counter>=13): | ||
connection = httplib.HTTPConnection(ip, timeout=10) | ||
|
||
lparams = {'xy': converter.rgb_to_xy(lr,lg,lb), 'colormode': 'xy', 'bri': int(lll), 'on': True} | ||
connection.request('PUT', lurl, json.dumps(lparams)) | ||
response = connection.getresponse() | ||
|
||
rparams = {'xy': converter.rgb_to_xy(rr,rg,rb), 'colormode': 'xy', 'bri': int(llr), 'on': True} | ||
connection.request('PUT', rurl, json.dumps(rparams)) | ||
response = connection.getresponse() | ||
|
||
connection.close() | ||
counter=0 | ||
else: | ||
os.system("curl -d '{\"on\":false}' -X PUT 192.168.xxx.xxx/api/HIER DEN KEY DER BRIDGE EINTRAGEN/groups/0/action") | ||
break | ||
|
||
|
||
def calcLuminance(r,g,b): | ||
LUM_VALUE=20 | ||
luminance=1 | ||
if (r + g + b > 2): | ||
luminance= r + g + b + LUM_VALUE | ||
if (luminance>=255): | ||
luminance=254 | ||
|
||
return luminance | ||
|
||
import time | ||
time.sleep(1) | ||
popen() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,260 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Library for RGB / CIE1931 "x, y" coversion. | ||
Based on Philips implementation guidance: | ||
http://www.developers.meethue.com/documentation/color-conversions-rgb-xy | ||
Copyright (c) 2016 Benjamin Knight / MIT License. | ||
""" | ||
import math | ||
import random | ||
from collections import namedtuple | ||
|
||
|
||
# Represents a CIE 1931 XY coordinate pair. | ||
XYPoint = namedtuple('XYPoint', ['x', 'y']) | ||
|
||
# LivingColors Iris, Bloom, Aura, LightStrips | ||
GamutA = ( | ||
XYPoint(0.704, 0.296), | ||
XYPoint(0.2151, 0.7106), | ||
XYPoint(0.138, 0.08), | ||
) | ||
|
||
# Hue A19 bulbs | ||
GamutB = ( | ||
XYPoint(0.675, 0.322), | ||
XYPoint(0.4091, 0.518), | ||
XYPoint(0.167, 0.04), | ||
) | ||
|
||
# Hue BR30, A19 (Gen 3), Hue Go, LightStrips plus | ||
GamutC = ( | ||
XYPoint(0.692, 0.308), | ||
XYPoint(0.17, 0.7), | ||
XYPoint(0.153, 0.048), | ||
) | ||
|
||
|
||
def get_light_gamut(modelId): | ||
"""Gets the correct color gamut for the provided model id. | ||
Docs: http://www.developers.meethue.com/documentation/supported-lights | ||
""" | ||
if modelId in ('LST001', 'LLC010', 'LLC011', 'LLC012', 'LLC006', 'LLC007', 'LLC013'): | ||
return GamutA | ||
elif modelId in ('LCT001', 'LCT007', 'LCT002', 'LCT003', 'LLM001'): | ||
return GamutB | ||
elif modelId in ('LCT010', 'LCT014', 'LCT011', 'LLC020', 'LST002'): | ||
return GamutC | ||
else: | ||
raise ValueError | ||
return None | ||
|
||
|
||
class ColorHelper: | ||
|
||
def __init__(self, gamut=GamutB): | ||
self.Red = gamut[0] | ||
self.Lime = gamut[1] | ||
self.Blue = gamut[2] | ||
|
||
def hex_to_red(self, hex): | ||
"""Parses a valid hex color string and returns the Red RGB integer value.""" | ||
return int(hex[0:2], 16) | ||
|
||
def hex_to_green(self, hex): | ||
"""Parses a valid hex color string and returns the Green RGB integer value.""" | ||
return int(hex[2:4], 16) | ||
|
||
def hex_to_blue(self, hex): | ||
"""Parses a valid hex color string and returns the Blue RGB integer value.""" | ||
return int(hex[4:6], 16) | ||
|
||
def hex_to_rgb(self, h): | ||
"""Converts a valid hex color string to an RGB array.""" | ||
rgb = (self.hex_to_red(h), self.hex_to_green(h), self.hex_to_blue(h)) | ||
return rgb | ||
|
||
def rgb_to_hex(self, r, g, b): | ||
"""Converts RGB to hex.""" | ||
return '%02x%02x%02x' % (r, g, b) | ||
|
||
def random_rgb_value(self): | ||
"""Return a random Integer in the range of 0 to 255, representing an RGB color value.""" | ||
return random.randrange(0, 256) | ||
|
||
def cross_product(self, p1, p2): | ||
"""Returns the cross product of two XYPoints.""" | ||
return (p1.x * p2.y - p1.y * p2.x) | ||
|
||
def check_point_in_lamps_reach(self, p): | ||
"""Check if the provided XYPoint can be recreated by a Hue lamp.""" | ||
v1 = XYPoint(self.Lime.x - self.Red.x, self.Lime.y - self.Red.y) | ||
v2 = XYPoint(self.Blue.x - self.Red.x, self.Blue.y - self.Red.y) | ||
|
||
q = XYPoint(p.x - self.Red.x, p.y - self.Red.y) | ||
s = self.cross_product(q, v2) / self.cross_product(v1, v2) | ||
t = self.cross_product(v1, q) / self.cross_product(v1, v2) | ||
|
||
return (s >= 0.0) and (t >= 0.0) and (s + t <= 1.0) | ||
|
||
def get_closest_point_to_line(self, A, B, P): | ||
"""Find the closest point on a line. This point will be reproducible by a Hue lamp.""" | ||
AP = XYPoint(P.x - A.x, P.y - A.y) | ||
AB = XYPoint(B.x - A.x, B.y - A.y) | ||
ab2 = AB.x * AB.x + AB.y * AB.y | ||
ap_ab = AP.x * AB.x + AP.y * AB.y | ||
t = ap_ab / ab2 | ||
|
||
if t < 0.0: | ||
t = 0.0 | ||
elif t > 1.0: | ||
t = 1.0 | ||
|
||
return XYPoint(A.x + AB.x * t, A.y + AB.y * t) | ||
|
||
def get_closest_point_to_point(self, xy_point): | ||
# Color is unreproducible, find the closest point on each line in the CIE 1931 'triangle'. | ||
pAB = self.get_closest_point_to_line(self.Red, self.Lime, xy_point) | ||
pAC = self.get_closest_point_to_line(self.Blue, self.Red, xy_point) | ||
pBC = self.get_closest_point_to_line(self.Lime, self.Blue, xy_point) | ||
|
||
# Get the distances per point and see which point is closer to our Point. | ||
dAB = self.get_distance_between_two_points(xy_point, pAB) | ||
dAC = self.get_distance_between_two_points(xy_point, pAC) | ||
dBC = self.get_distance_between_two_points(xy_point, pBC) | ||
|
||
lowest = dAB | ||
closest_point = pAB | ||
|
||
if (dAC < lowest): | ||
lowest = dAC | ||
closest_point = pAC | ||
|
||
if (dBC < lowest): | ||
lowest = dBC | ||
closest_point = pBC | ||
|
||
# Change the xy value to a value which is within the reach of the lamp. | ||
cx = closest_point.x | ||
cy = closest_point.y | ||
|
||
return XYPoint(cx, cy) | ||
|
||
def get_distance_between_two_points(self, one, two): | ||
"""Returns the distance between two XYPoints.""" | ||
dx = one.x - two.x | ||
dy = one.y - two.y | ||
return math.sqrt(dx * dx + dy * dy) | ||
|
||
def get_xy_point_from_rgb(self, red, green, blue): | ||
"""Returns an XYPoint object containing the closest available CIE 1931 x, y coordinates | ||
based on the RGB input values.""" | ||
if (red<=0): red= 0.0000000000000001 | ||
if (green<=0): green= 0.0000000000000001 | ||
if (blue<=0): blue= 0.0000000000000001 | ||
|
||
r = ((red + 0.055) / (1.0 + 0.055))**2.4 if (red > 0.04045) else (red / 12.92) | ||
g = ((green + 0.055) / (1.0 + 0.055))**2.4 if (green > 0.04045) else (green / 12.92) | ||
b = ((blue + 0.055) / (1.0 + 0.055))**2.4 if (blue > 0.04045) else (blue / 12.92) | ||
|
||
|
||
X = r * 0.664511 + g * 0.154324 + b * 0.162028 | ||
Y = r * 0.283881 + g * 0.668433 + b * 0.047685 | ||
Z = r * 0.000088 + g * 0.072310 + b * 0.986039 | ||
|
||
cx = X / (X + Y + Z) | ||
cy = Y / (X + Y + Z) | ||
|
||
# Check if the given XY value is within the colourreach of our lamps. | ||
xy_point = XYPoint(cx, cy) | ||
in_reach = self.check_point_in_lamps_reach(xy_point) | ||
|
||
if not in_reach: | ||
xy_point = self.get_closest_point_to_point(xy_point) | ||
|
||
return xy_point | ||
|
||
def get_rgb_from_xy_and_brightness(self, x, y, bri=1): | ||
"""Inverse of `get_xy_point_from_rgb`. Returns (r, g, b) for given x, y values. | ||
Implementation of the instructions found on the Philips Hue iOS SDK docs: http://goo.gl/kWKXKl | ||
""" | ||
# The xy to color conversion is almost the same, but in reverse order. | ||
# Check if the xy value is within the color gamut of the lamp. | ||
# If not continue with step 2, otherwise step 3. | ||
# We do this to calculate the most accurate color the given light can actually do. | ||
xy_point = XYPoint(x, y) | ||
|
||
if not self.check_point_in_lamps_reach(xy_point): | ||
# Calculate the closest point on the color gamut triangle | ||
# and use that as xy value See step 6 of color to xy. | ||
xy_point = self.get_closest_point_to_point(xy_point) | ||
|
||
# Calculate XYZ values Convert using the following formulas: | ||
Y = bri | ||
X = (Y / xy_point.y) * xy_point.x | ||
Z = (Y / xy_point.y) * (1 - xy_point.x - xy_point.y) | ||
|
||
# Convert to RGB using Wide RGB D65 conversion | ||
r = X * 1.656492 - Y * 0.354851 - Z * 0.255038 | ||
g = -X * 0.707196 + Y * 1.655397 + Z * 0.036152 | ||
b = X * 0.051713 - Y * 0.121364 + Z * 1.011530 | ||
|
||
# Apply reverse gamma correction | ||
r, g, b = map( | ||
lambda x: (12.92 * x) if (x <= 0.0031308) else ((1.0 + 0.055) * pow(x, (1.0 / 2.4)) - 0.055), | ||
[r, g, b] | ||
) | ||
|
||
# Bring all negative components to zero | ||
r, g, b = map(lambda x: max(0, x), [r, g, b]) | ||
|
||
# If one component is greater than 1, weight components by that value. | ||
max_component = max(r, g, b) | ||
if max_component > 1: | ||
r, g, b = map(lambda x: x / max_component, [r, g, b]) | ||
|
||
r, g, b = map(lambda x: int(x * 255), [r, g, b]) | ||
|
||
# Convert the RGB values to your color object The rgb values from the above formulas are between 0.0 and 1.0. | ||
return (r, g, b) | ||
|
||
|
||
class Converter: | ||
|
||
def __init__(self, gamut=GamutB): | ||
self.color = ColorHelper(gamut) | ||
|
||
def hex_to_xy(self, h): | ||
"""Converts hexadecimal colors represented as a String to approximate CIE | ||
1931 x and y coordinates. | ||
""" | ||
rgb = self.color.hex_to_rgb(h) | ||
return self.rgb_to_xy(rgb[0], rgb[1], rgb[2]) | ||
|
||
def rgb_to_xy(self, red, green, blue): | ||
"""Converts red, green and blue integer values to approximate CIE 1931 | ||
x and y coordinates. | ||
""" | ||
point = self.color.get_xy_point_from_rgb(red, green, blue) | ||
return (point.x, point.y) | ||
|
||
def xy_to_hex(self, x, y, bri=1): | ||
"""Converts CIE 1931 x and y coordinates and brightness value from 0 to 1 | ||
to a CSS hex color.""" | ||
r, g, b = self.color.get_rgb_from_xy_and_brightness(x, y, bri) | ||
return self.color.rgb_to_hex(r, g, b) | ||
|
||
def xy_to_rgb(self, x, y, bri=1): | ||
"""Converts CIE 1931 x and y coordinates and brightness value from 0 to 1 | ||
to a CSS hex color.""" | ||
r, g, b = self.color.get_rgb_from_xy_and_brightness(x, y, bri) | ||
return (r, g, b) | ||
|
||
def get_random_xy_color(self): | ||
"""Returns the approximate CIE 1931 x,y coordinates represented by the | ||
supplied hexColor parameter, or of a random color if the parameter | ||
is not passed.""" | ||
r = self.color.random_rgb_value() | ||
g = self.color.random_rgb_value() | ||
b = self.color.random_rgb_value() | ||
return self.rgb_to_xy(r, g, b) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters