mariusmotea / diyHue

Philips Hue emulator that is able to control multiple types of lights
Other
627 stars 107 forks source link

Trying to add own leds that are connected #28

Closed Crementif closed 7 years ago

Crementif commented 7 years ago

I currently use the pi-blaster library and that seems to work great so far manually. It's basically a file where you write to.

But now I'm trying to merge with my existing Philips Hue lights and my Google Home. So I've setup the bridge and it gets recognised by the app but I'm stuck with how I would need to add the led strip I've connected to my Raspberry Pi GPIO pins. Is there any way to do interface with a simple led strip? The Pi-Blaster library handles most of the conversion (I'm using a modified ikea dioder kit) and basically needs all a pin and a value between 0.0-1.0. How would I go about adding the leds to the project (add it to the bridge)? And could you give me a lead for a template I could build it on and what arguments are passed? Thanks a lot, it's working great so far! I would gladly make a pull request from a new fork where I've added compatibility for pi-blaster.

mariusmotea commented 7 years ago

Hi! To add this to bridge is possible but want to avoid it because i want to keep the things as simple as possible. There are many types of lights/strips from 1 to 5 channels (RGBW-CCT require 5 channels) and to support all require lot of code in bridge emulator. Also pi-blaster is great but some people will find this not so easy to install. The solution that i recommend is to create a new python script that will listen on a different port than bridge emulator (tcp80) and will control the lights on GET http requests. In your case i understand there will be only one pwm channel, so this script must interpret just "on" and "bri" arguments. Example of lights api:

http://192.168.10.30/set?light=1&bri=255  => this will set the maximum brightness
http://192.168.10.30/set?light=1&on=True => will turn the light on
http://192.168.10.30/set?light=1&on=False => will turn the light off

Some code example for you:

    def do_GET(self):
        self._set_headers()
        if self.path.startswith("/set"):
             get_parameters = parse_qs(urlparse(self.path).query)
             if "on" in get_parameters:
                //tell pi-blaster to turn on the light if get_parameters["on"][0] is "True", or of if "False"
             if "bri"  in get_parameters:
                 float brightness = int(get_parameters["bri"][0]) / 255 // this way you will have values from 0.0 to 1.0

Second part is to add the new light in Hue Emulator, this cannot be automatically discovered because of the different port used:

  1. kill HueEmulator process
  2. backup config.json and lights_address.json files
  3. edit config.json and add a new ligh (just copy the content of any existing light and increment the light key with +1)
  4. edit lights_address.json and add the new light. Ex:
    "1": {
        "ip": "192.168.10.30",
        "light_nr": 1,
        "protocol": "native"
    },

    In your case replace "1" with the id of the light you have just added in config.json and the ip with 127.0.0.1:{port used}

mariusmotea commented 7 years ago

BTW, you manage to pair Google Home with the bridge emulator?

Crementif commented 7 years ago

I'll try to pair it if my light strips are working, but I can do a limited test this week with only the led strips and my google assistant. I'll report back when I can test it with my philips hue lights. Thanks for the response! Also, I'm using multiple pins because each of the 3 pins represents a different color, I wasn't very clear in my example so my bad! It seems that I would have to create a whole different set of instructions right?

mariusmotea commented 7 years ago

This make the things a little bit more complicated. I can provide you the code that convert xy, ct and hue to 3ch RGB, just let me know when you are there.

Crementif commented 7 years ago

After fiddling around with the python (which I rarely if ever use) I couldn't even get something simple started. But I came up with a potential hack that would work. How hard would it to be to add a dummy Philips hue bulb and read the status and set the gpio pins according to the in my case 3rd 'philips hue bulb'?Also, I don't have access to my Philips Hue lights and bridge as of now but if I'm correct the documentation says it should automatically detect the lights if I'm on the same network as the bridge?

mariusmotea commented 7 years ago

Here you have 90% of your problem solved. Just implement pi-blaster library where you have print commands. it don't process hue color mode, but this is not a big deal since most of apps use xy and ct. Don't expect autodiscover to work, this is listening on port 81. you will need to manually add this light in configurations files exactly like in my previews examples.

#!/usr/bin/python
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from time import strftime, sleep
from urlparse import urlparse, parse_qs
from math import log
import json

lightstate = {"on" : False, "bri": 0, "xy": [0.0, 0.0], "ct": 153, "hue": 0, "sat": 0, "colormode": "ct"}

def convert_xy(x, y, bri): #needed for milight hub that don't work with xy values
    Y = bri / 250.0
    z = 1.0 - x - y

    X = (Y / y) * x
    Z = (Y / y) * z

  # sRGB 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

    if r > b and r > g and r > 1:
    # red is too big
        g = g / r
        b = b / r
        r = 1

    elif g > b and g > r and g > 1:
    #green is too big
        r = r / g
        b = b / g
        g = 1

    elif b > r and b > g and b > 1:
    # blue is too big
        r = r / b
        g = g / b
        b = 1

    r = 12.92 * r if r <= 0.0031308 else (1.0 + 0.055) * pow(r, (1.0 / 2.4)) - 0.055
    g = 12.92 * g if g <= 0.0031308 else (1.0 + 0.055) * pow(g, (1.0 / 2.4)) - 0.055
    b = 12.92 * b if b <= 0.0031308 else (1.0 + 0.055) * pow(b, (1.0 / 2.4)) - 0.055

    if r > b and r > g:
    # red is biggest
        if r > 1:
            g = g / r
            b = b / r
            r = 1
        elif g > b and g > r:
        # green is biggest
            if g > 1:
                r = r / g
                b = b / g
                g = 1

        elif b > r and b > g:
        # blue is biggest
            if b > 1:
                r = r / b
                g = g / b
                b = 1

    r = 0 if r < 0 else r
    g = 0 if g < 0 else g
    b = 0 if b < 0 else b

    return [int(r * 255), int(g * 255), int(b * 255)]

def convert_ct(ct, bri):
    hectemp = 10000 / ct
    r = 0
    g = 0
    b = 0
    if hectemp <= 66:
        r = 255;
        g = 99.4708025861 * log(hectemp) - 161.1195681661
        b = 0 if hectemp <= 19 else (138.5177312231 * log(hectemp - 10) - 305.0447927307)
    else:
        r = 329.698727446 * pow(hectemp - 60, -0.1332047592)
        g = 288.1221695283 * pow(hectemp - 60, -0.0755148492)
        b = 255;

    r = 255 if r > 255 else r
    g = 255 if g > 255 else g
    b = 255 if r > 255 else b
    return [ int(r * (bri / 255.0)), int(g * (bri / 255.0)), int(b * (bri / 255.0))]

class S(BaseHTTPRequestHandler):
    def _set_headers(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_GET(self):
        self._set_headers()
        if self.path.startswith("/get"):
            self.wfile.write(json.dumps(lightstate))
        elif self.path.startswith("/set"):
            get_parameters = parse_qs(urlparse(self.path).query)
            if "on" in get_parameters:
                if get_parameters["on"][0] == "True":
                    lightstate["on"] = True
                    print("start pwm on all channels. If last state is not automatically applied, then execute the function that is set in lightstate[\"colormode\"] again")
                elif get_parameters["on"][0] == "False":
                    lightstate["on"] = False
                    print("stop pwm on all channels")
            if "bri" in get_parameters:
                lightstate["bri"] = int(get_parameters["bri"][0])
                print("setup bri to " + get_parameters["bri"][0])
            if "ct" in get_parameters:
                lightstate["ct"] = int(get_parameters["ct"][0])
                lightstate["colormode"] = "ct"
            elif "x" in get_parameters:
                lightstate["xy"] = [float(get_parameters["x"][0]), float(get_parameters["y"][0])]
                lightstate["colormode"] = "xy"

            if lightstate["on"] == True:
                if lightstate["colormode"] == "xy":
                    raw_hue_rgb = convert_xy(lightstate["xy"][0], lightstate["xy"][1], lightstate["bri"])
                    #convert this from 0 <-> 255 to 0.0 <-> 1.0
                    pwm_rgb = [raw_hue_rgb[0] / 255.0, raw_hue_rgb[1] / 255.0, raw_hue_rgb[2] / 255.0]
                    print("red: " + str(pwm_rgb[0]) + ", green: " + str(pwm_rgb[1]) + ", blue: " + str(pwm_rgb[2]))

                elif lightstate["colormode"] == "ct":
                    raw_hue_rgb = convert_ct(lightstate["ct"], lightstate["bri"])
                    #convert this from 0 <-> 255 to 0.0 <-> 1.0
                    pwm_rgb = [raw_hue_rgb[0] / 255.0, raw_hue_rgb[1] / 255.0, raw_hue_rgb[2] / 255.0]
                    print("red: " + str(pwm_rgb[0]) + ", green: " + str(pwm_rgb[1]) + ", blue: " + str(pwm_rgb[2]))

            self.wfile.write("OK")
        else:
            self.wfile.write("WRONG PATH")

def run(server_class=HTTPServer, handler_class=S):
    server_address = ('', 81)
    httpd = server_class(server_address, handler_class)
    print 'Starting httpd...'
    httpd.serve_forever()

if __name__ == "__main__":
    try:
        run()
    except:
        print("server stopped")
Crementif commented 7 years ago

Wow, thanks a lot! I hope this will work. I'll make a fork so that you can refer to if someone asks. Great work man, I'll report back if the Google Assistant works.

mariusmotea commented 7 years ago

Must work, look here some output after i play from Hue application. Just pay attention when you edit the configuration files to not made some mistakes, and for local light set the address 127.0.0.1:81

127.0.0.1 - - [27/Jul/2017 14:21:50] "GET /set?light=1&bri=106 HTTP/1.1" 200 -
setup bri to 106
red: 0.41568627451, green: 0.411764705882, blue: 0.403921568627
127.0.0.1 - - [27/Jul/2017 14:02:18] "GET /set?light=1&ct=297 HTTP/1.1" 200 -
red: 0.996078431373, green: 0.725490196078, blue: 0.501960784314
127.0.0.1 - - [27/Jul/2017 14:02:19] "GET /set?light=1&ct=296 HTTP/1.1" 200 -
red: 0.996078431373, green: 0.725490196078, blue: 0.501960784314
127.0.0.1 - - [27/Jul/2017 14:02:19] "GET /set?light=1&ct=177 HTTP/1.1" 200 -
red: 0.996078431373, green: 0.933333333333, blue: 0.878431372549
127.0.0.1 - - [27/Jul/2017 14:02:21] "GET /set?light=1&ct=180 HTTP/1.1" 200 -
red: 0.996078431373, green: 0.925490196078, blue: 0.866666666667
127.0.0.1 - - [27/Jul/2017 14:02:22] "GET /set?light=1&ct=153 HTTP/1.1" 200 -
red: 0.996078431373, green: 0.992156862745, blue: 0.976470588235
127.0.0.1 - - [27/Jul/2017 14:21:54] "GET /set?light=1&bri=25 HTTP/1.1" 200 -
setup bri to 25
red: 0.0980392156863, green: 0.0941176470588, blue: 0.0941176470588
127.0.0.1 - - [27/Jul/2017 14:21:55] "GET /set?light=1&bri=137 HTTP/1.1" 200 -
setup bri to 137
red: 0.537254901961, green: 0.533333333333, blue: 0.525490196078
127.0.0.1 - - [27/Jul/2017 14:21:56] "GET /set?light=1&bri=185 HTTP/1.1" 200 -
setup bri to 185
red: 0.725490196078, green: 0.721568627451, blue: 0.709803921569
127.0.0.1 - - [27/Jul/2017 14:21:58] "GET /set?light=1&bri=216 HTTP/1.1" 200 -
setup bri to 216
red: 0.847058823529, green: 0.843137254902, blue: 0.827450980392
127.0.0.1 - - [27/Jul/2017 14:22:03] "GET /set?light=1&x=0.572488&y=0.396417 HTTP/1.1" 200 -
red: 0.996078431373, green: 0.596078431373, blue: 0.133333333333
127.0.0.1 - - [27/Jul/2017 14:22:04] "GET /set?light=1&x=0.610183&y=0.28656 HTTP/1.1" 200 -
red: 0.996078431373, green: 0.250980392157, blue: 0.372549019608
127.0.0.1 - - [27/Jul/2017 14:22:06] "GET /set?light=1&x=0.277992&y=0.112641 HTTP/1.1" 200 -
red: 0.686274509804, green: 0.145098039216, blue: 0.996078431373
127.0.0.1 - - [27/Jul/2017 14:22:07] "GET /set?light=1&x=0.168&y=0.041 HTTP/1.1" 200 -
red: 0.305882352941, green: 0.0, blue: 0.996078431373
127.0.0.1 - - [27/Jul/2017 14:22:08] "GET /set?light=1&x=0.250842&y=0.205303 HTTP/1.1" 200 -
red: 0.647058823529, green: 0.61568627451, blue: 0.996078431373
127.0.0.1 - - [27/Jul/2017 14:22:09] "GET /set?light=1&x=0.313577&y=0.329729 HTTP/1.1" 200 -
red: 0.913725490196, green: 0.945098039216, blue: 0.945098039216
127.0.0.1 - - [27/Jul/2017 14:22:11] "GET /set?light=1&x=0.461741&y=0.477603 HTTP/1.1" 200 -
red: 0.996078431373, green: 0.905882352941, blue: 0.239215686275
127.0.0.1 - - [27/Jul/2017 14:22:12] "GET /set?light=1&x=0.569897&y=0.398316 HTTP/1.1" 200 -

if you cannot restore the previews pwm levels when turn on the light, just add below line 106 this:

                if lightstate["colormode"] == "xy":
                    raw_hue_rgb = convert_xy(lightstate["xy"][0], lightstate["xy"][1], lightstate["bri"])
                    #convert this from 0 <-> 255 to 0.0 <-> 1.0
                    pwm_rgb = [raw_hue_rgb[0] / 255.0, raw_hue_rgb[1] / 255.0, raw_hue_rgb[2] / 255.0]
                    print("red: " + str(pwm_rgb[0]) + ", green: " + str(pwm_rgb[1]) + ", blue: " + str(pwm_rgb[2]))

                elif lightstate["colormode"] == "ct":
                    raw_hue_rgb = convert_ct(lightstate["ct"], lightstate["bri"])
                    #convert this from 0 <-> 255 to 0.0 <-> 1.0
                    pwm_rgb = [raw_hue_rgb[0] / 255.0, raw_hue_rgb[1] / 255.0, raw_hue_rgb[2] / 255.0]
                    print("red: " + str(pwm_rgb[0]) + ", green: " + str(pwm_rgb[1]) + ", blue: " + str(pwm_rgb[2]))
Crementif commented 7 years ago

The problem I'm having is that I have nothing to base the new light off in the config.json. I can't get this step to work:

edit config.json and add a new ligh (just copy the content of any existing light and increment the light key with +1)

You have an example of something that I can use inside the config.json? I don't have anything hooked up to the raspberry pi bridge right now, but I'll have some new philips hue lights later this week.

mariusmotea commented 7 years ago

in my test i change the bulb from kitchen to work with this new local service, and this light has the id "9".

config.json:

-----------------------
          "swversion": "66009461",
            "type": "Extended color light",
            "uniqueid": "4a:e0:ad:7f:cf:5c-1"
        },
        "9": {
            "modelid": "LCT001",
            "name": "Bec Bucatarie",
            "state": {
                "alert": "none",
                "bri": 106,
                "colormode": "ct",
                "ct": 153,
                "effect": "none",
                "hue": 0,
                "on": true,
                "reachable": true,
                "sat": 0,
                "xy": [
                    0.569897,
                    0.398316
                ]
            },
            "swversion": "66009461",
            "type": "Extended color light",
            "uniqueid": "45:a3:d7:7f:cf:5c-1"
        },
        "10": {
            "modelid": "LLM010",
            "name": "TRADFRI bulb E27 WS opal 980lm",
            "state": {
---------------------

and in lights_address.json:

-------------------------
        "light_nr": 1,
        "protocol": "native"
    },
    "9": {
        "ip": "127.0.0.1:81",
        "light_nr": 1,
        "protocol": "native"
    },
    "10": {
        "device_id": 65537,
        "ip": "192.168.10.24",
        "protocol": "ikea_tradfri",
---------------------------

In your case if there are no lights the id will be "1"

Crementif commented 7 years ago

Uh, it's throwing errors but it is showing up in the app. Here's my current configuration files, config.json and lights_address.json (the lights_address.json file keeps resetting to the default two brackets). The errors seem to be related to key and only seem to happen with PUT, the GET is not throwing any errors (atleast that's what it looks like). The errors thrown are (this one happens at launch)


Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
  File "./HueEmulator.py", line 240, in sendLightRequest
    if lights_address[light]["protocol"] == "native": #ESP8266 light or strip
KeyError: u'1'
update status for light 1```

and this one which happens when PUT* is being called.

```Exception in thread Thread-15:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
  File "./HueEmulator.py", line 240, in sendLightRequest
    if lights_address[light]["protocol"] == "native": #ESP8266 light or strip
KeyError: '1'```

I also deleted and reinstalled the whole github to be sure all of the code would work. Any ideas?
mariusmotea commented 7 years ago

issue is in lights_address.json

change to this:

{
    "1": {
        "ip": "127.0.0.1:81",
        "light_nr": 1,
        "protocol": "native"
    }
}
mariusmotea commented 7 years ago

Is this solved, can be closed?

Crementif commented 7 years ago

Sure, I'm now using another pair of led strips that are hue compatible and are getting discovered.

Eknem commented 5 years ago

First of all. My compliments and great thanks to the people who are responsible of making this project. I love it.

I use diyhue in combination with the HTTPServer example as shown here above, to control a SMD5050 led strip. If I use the bridge emulator (web app) to control the led strip the colors are near perfect, but if i use the "android Phillips hue app" the the color red is screwed up. If i select pure red there will be a large amount of blue and green mixed in. (red 254 green 49 blue 19) I don't understand where this difference is coming from? the colors are created by the "convert_xy" function as shown above

mariusmotea commented 5 years ago

May sound wired, but hue app is optimised for their bulbs and i believe you emulate LCT001 with wired colors. The fix is to change the modelid in config.json to different bulb. Now i put LCT015 that has much better color reproduction.

iORichy commented 5 years ago

Hi Guys, sry to write on a closed issue, but I have some issues to connect my 433MHz bridge to the diyHue… I have a working python-script as mentioned by @mariusmotea listening on port 81. It grabs the URL-parameters and controls my 433MHz devices. So with "/set?light=1&on=True" it works quite well. I also add a new light that will show up on the Hue-Emulator by modifying the config.json like this:

_ "groups": { "1": { "action": { "bri": 200, "ct": 461, "hue": 0, "on": false, "sat": 0, "xy": [ 0.0, 0.0 ] }, "class": "Living room", "lights": [ "2" ], "name": "Wohnzimmer", "state": { "all_on": false, "any_on": false }, "type": "Room" } }, "lights": { "1": { "modelid": "LCT001", "name": "MiLight cct 1", "state": { "alert": "select", "bri": 200, "colormode": "ct", "ct": 461, "effect": "none", "hue": 0, "on": false, "reachable": false, "sat": 0, "xy": [ 0.0, 0.0 ] }, "swversion": "66009461", "type": "Extended color light", "uniqueid": "1a2b3c482" }, "2": { "modelid": "LCT002", "name": "My own light", "state": { "alert": "select", "bri": 200, "colormode": "ct", "ct": 461, "effect": "none", "hue": 0, "on": false, "reachable": false, "sat": 0, "xy": [ 0.0, 0.0 ] }, "swversion": "66009461", "type": "light", "uniqueid": "00:17:88:01:00:e5:e3:82-0b" } }, "lights_address": { "1": { "device_id": "1", "group": 1, "ip": "192.168.1.100", "mode": "cct", "protocol": "milight" }, "2": { "device_id": "2", "ip": "127.0.0.1:81", "mac": "00:11:88:11:00:72", "protocol": "native" } }, "linkbutton": { "lastlinkbuttonpushed": "1554928507", "linkbutton_auth": "SHVlOkh1ZQ==" }, _

but if I log all request sent to "192.168.1.100:81/set?" I can't see something if I push the switch on within the Hue-App/Website… What am I doing wrong here? Thanks for a small hint guys…

mariusmotea commented 5 years ago

Hi,

Please note that this repo is outdated, you need to use code from DiyHue community. Regarding your issue, what is more exactly Hue-App/Website?

iORichy commented 5 years ago

Hi, Thanks for your quick response, really appreciate. I think I have choosen the latest automatic installation from this repo/wiki: https://diyhue.readthedocs.io/en/latest/getting_started.html#automatic-install Is this the old one, and if so, could you please point out to the new one for me?

After installation I can go to a webbrowser and point to „my-raspberry:80/hue“ that shows up the diyHue Emulator Website. That‘s what I meant by „Hue-App/Website“.

So I would suggest a signal/flow should go that way: iPhone-HueApp (switch on light) -> call‘s the diyHue (makes a lookup on light and than light_address) -> call‘s the url of the light_address to switch it on (this one seams to fail as I can’t see some „GET 127.0.0.1:81/set?light=1&on=true“ or something like this...

I would love to help/develop/share a new kind of lights-module for 433MHz devices on the diyHue like the „milight“ that’s build in. So that you can just listen to the 433MHz-code once and store it as light to use it in the diyHue...

mariusmotea commented 5 years ago

Your installation must be ok because i update the wiki url of this repo

my-raspberry:80/hue point to page that help you to proxy another hue bridge device (only lights are imported)

with curl to 127.0.0.1:81 are you able to see the requests? What about if you start hue emulator in debug mode to see what is going on more exactly?