Philips Wiz (not Hue) Bulbs: how can we advocate for an API

It appears you can poll the bulbs with method getPilot:

echo '{"method":"getPilot","params":{}}' | nc -u -w 1 [ip] 38899
returns:
{"method":"getPilot","env":"pro","result":{"mac":"[mac]","rssi":-53,"src":"","state":false,"sceneId":0,"r":0,"g":255,"b":0,"c":0,"w":0,"dimming":100}}

{"method":"getPilot","env":"pro","result":{"mac":"[mac]","rssi":-53,"src":"","state":true,"sceneId":0,"r":0,"g":255,"b":0,"c":0,"w":0,"dimming":100}}
etc…

Actually, if I wasn’t allergic to Java and looked at the openhab binding more I would have noticed the author apparently did reverse engineer all the local commands:

He posted in Ability to control Philips/Wiz Connected WIFI Smart LED lights? a few days ago.

Anyway, I modified [wilvancleve]'s nodeRed flow a bit to just ignore the command passed in the topic so I can send the full params object and use the MQTT template schema to configure the light. For now I think I’ll just do template lights using the smartthings entities I already have setup for these bulbs for state, I mostly wanted the faster response time on the commands. (light.template apparently doesn’t support RGB :()

[
    {
        "id": "3bfa4c0a.55a6e4",
        "type": "tab",
        "label": "MQTT to Wiz",
        "disabled": false,
        "info": ""
    },
    {
        "id": "f8e03d02.b904a",
        "type": "json",
        "z": "3bfa4c0a.55a6e4",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 310,
        "y": 340,
        "wires": [
            [
                "5b5538a.6f768c8"
            ]
        ]
    },
    {
        "id": "39ef9bf6.b126f4",
        "type": "comment",
        "z": "3bfa4c0a.55a6e4",
        "name": "Relay MQTT to UDP",
        "info": "",
        "x": 170,
        "y": 260,
        "wires": []
    },
    {
        "id": "5b5538a.6f768c8",
        "type": "function",
        "z": "3bfa4c0a.55a6e4",
        "name": "relay to udp",
        "func": "// expects the mqtt topic to be wizlight/ip/command\n\nvar command = msg.topic.split(\"/\")[2];\n\nmsg.ip = msg.topic.split(\"/\")[1];\n\nvar obj = {}; \nobj = msg.payload;\n            \nvar p = {};\n\nvar pl = {method:\"setPilot\", \n          id:527, \n          env:\"pro\", \n          params : obj};\n\nmsg.payload =  pl\n\nmsg.port = 38899\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 490,
        "y": 340,
        "wires": [
            [
                "eb39d4e7.006d18"
            ]
        ]
    },
    {
        "id": "62afb6be.881218",
        "type": "mqtt in",
        "z": "3bfa4c0a.55a6e4",
        "name": "Wizlight In",
        "topic": "wizlight/#",
        "qos": "0",
        "datatype": "auto",
        "broker": "",
        "x": 160,
        "y": 340,
        "wires": [
            [
                "f8e03d02.b904a"
            ]
        ]
    },
    {
        "id": "fae3fd3b.f7733",
        "type": "udp out",
        "z": "3bfa4c0a.55a6e4",
        "name": "UDP auto port/ip",
        "addr": "",
        "iface": "",
        "port": "",
        "ipv": "udp4",
        "outport": "38899",
        "base64": false,
        "multicast": "false",
        "x": 1070,
        "y": 200,
        "wires": []
    },
    {
        "id": "eb39d4e7.006d18",
        "type": "json",
        "z": "3bfa4c0a.55a6e4",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 690,
        "y": 340,
        "wires": [
            [
                "6840d6e1.111ea8"
            ]
        ]
    },
    {
        "id": "f345a1c5.4bbbf",
        "type": "repeat",
        "z": "3bfa4c0a.55a6e4",
        "name": "Triple It Up",
        "repetitions": "3",
        "elseOutput": false,
        "outputs": 1,
        "x": 810,
        "y": 200,
        "wires": [
            [
                "fae3fd3b.f7733",
                "eb39d4e7.006d18"
            ]
        ]
    },
    {
        "id": "6840d6e1.111ea8",
        "type": "delay",
        "z": "3bfa4c0a.55a6e4",
        "name": "",
        "pauseType": "delay",
        "timeout": "25",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "x": 870,
        "y": 340,
        "wires": [
            [
                "f345a1c5.4bbbf"
            ]
        ]
    }
]
light:
  - platform: mqtt
    name: "wizbulb MQTT"
    schema: template
    command_topic: "wizlight/ip_address/set"
    command_on_template: >
      {"state": true
      {%- if brightness is defined -%}
      ,"dimming": {{ (brightness/254*100)| int }}
      {%- endif -%}
      {%- if red is defined and green is defined and blue is defined -%}
      ,"r":{{ red }},"g":{{ green }},"b":{{ blue }}
      {%- endif -%}
      {%- if color_temp is defined -%}
      ,"temp":{{ (1000000/color_temp)| int}}
      {%- endif -%}
      {%- if white_value is defined -%}
      ,"w":{{ white_value }}
      {%- endif -%}
      }
    command_off_template: '{"state":false}'
#note these are just populated to get the MQTT platform to enable the relevant supported_features, not currently reading state from the bulb
    brightness_template: '{{ value_json.dimming }}'
    red_template: '{{ value_json.r }}'
    green_template: '{{ value_json.g }}'
    blue_template: '{{ value_json.b }}'
    color_temp_template: '{{ value_json.temp }}'
    white_value_template: '{{ value_json.w }}'

Would it be possible to use HA and OpenHAB on the same system, that way you could use HA to send messages to OpenHAB in order to turn the bulbs on and off.

I don’t know much about OpenHAB, but given how easily node-red and mqtt coexist with HA, I think it’d be overkill. Now that someone has posted information about to query the bulbs, I think this is a really easy node-red solution. I’m going to work to implement the query API call tonight and I’ll post an updated node-red flow in the next little while…

Ok, give this a whirl. It queries all bulbs for which IP addresses are given every 10 seconds, and sends this to MQTT, and queries again on a change to a bulb (via UDP).

This works very nicely for me, and faster than the Wiz app. I struggled a little bit with the templating for the RGB bulbs, but otherwise I think this is pretty tight.

[{"id":"21cbe739.15d9c8","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"b579249.841f4d8","type":"udp in","z":"21cbe739.15d9c8","name":"UDP2MQTT <gw listening UDP port>","iface":"","port":"38900","ipv":"udp4","multicast":"false","group":"","datatype":"utf8","x":210,"y":660,"wires":[["2ae4d01b.79f26"]]},{"id":"4e9174c8.cb986c","type":"comment","z":"21cbe739.15d9c8","name":"Relay UDP to MQTT","info":"","x":130,"y":600,"wires":[]},{"id":"fa19997f.c4a078","type":"function","z":"21cbe739.15d9c8","name":"relay to udp","func":"var ipArr = [\n\"192.168.164.x\",\n\"192.168.164.x\",\n\"192.168.164.x\",\n\"192.168.164.x\"\n    ];\n\nmultiMsg = {};\n\nipArr.forEach(myFunction); \n\nfunction myFunction(item, index) \n{ \n    var msg = {}\n    msg.ip = item;\n    msg.port = 38899;\n    \n    var pl = {method:\"getPilot\", \n          params : {}};\n          \n    msg.payload = pl;\n    multiMsg[item] = msg.payload;\n}\n\nmsg.payload = multiMsg;\nreturn msg;","outputs":1,"noerr":0,"x":330,"y":240,"wires":[["4a40c54.eb1663c"]]},{"id":"9738fe47.c95a1","type":"inject","z":"21cbe739.15d9c8","name":"Repeat q 10 s","topic":"","payload":"","payloadType":"date","repeat":"10","crontab":"","once":true,"onceDelay":"5","x":100,"y":240,"wires":[["fa19997f.c4a078"]]},{"id":"ba6b39ae.da6508","type":"udp out","z":"21cbe739.15d9c8","name":"UDP auto port/ip","addr":"","iface":"","port":"38899","ipv":"udp4","outport":"38900","base64":false,"multicast":"false","x":810,"y":240,"wires":[]},{"id":"4a40c54.eb1663c","type":"split","z":"21cbe739.15d9c8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"ip","x":490,"y":240,"wires":[["c5c478f6.506058"]]},{"id":"c5c478f6.506058","type":"json","z":"21cbe739.15d9c8","name":"","property":"payload","action":"","pretty":false,"x":630,"y":240,"wires":[["ba6b39ae.da6508"]]},{"id":"645e1d4b.17b4b4","type":"comment","z":"21cbe739.15d9c8","name":"Query All Bulbs Every 10 Seconds","info":"","x":160,"y":180,"wires":[]},{"id":"4c6937dc.999c28","type":"json","z":"21cbe739.15d9c8","name":"","property":"payload","action":"","pretty":false,"x":230,"y":500,"wires":[["c4e6fab9.e40268"]]},{"id":"d45bf5f9.053ac8","type":"comment","z":"21cbe739.15d9c8","name":"Relay MQTT to UDP","info":"","x":110,"y":360,"wires":[]},{"id":"c4e6fab9.e40268","type":"function","z":"21cbe739.15d9c8","name":"relay to udp","func":"// expects the mqtt topic to be wizlight/ip/command\n\nmsg.ip = msg.topic.split(\"/\")[1];\n\nvar obj = {}; \nobj = msg.payload;\n            \nvar p = {};\n\nvar pl = {method:\"setPilot\", \n          id:527, \n          env:\"pro\", \n          params : obj};\n\nmsg.payload =  pl;\nmsg.command = msg.topic.split(\"/\")[2];\n\nmsg.port = 38899\n\nreturn msg;","outputs":1,"noerr":0,"x":390,"y":500,"wires":[["d3371846.5b01a8"]]},{"id":"1ea8ec1d.0d2d74","type":"mqtt in","z":"21cbe739.15d9c8","name":"Wizlight In","topic":"wizlight/#","qos":"0","datatype":"auto","broker":"7ca43ccf.bcea44","x":100,"y":440,"wires":[["4c6937dc.999c28"]]},{"id":"b98d58c7.ccb928","type":"udp out","z":"21cbe739.15d9c8","name":"UDP auto port/ip","addr":"","iface":"","port":"","ipv":"udp4","outport":"38899","base64":false,"multicast":"false","x":1010,"y":400,"wires":[]},{"id":"47549dc1.9ba0a4","type":"json","z":"21cbe739.15d9c8","name":"","property":"payload","action":"","pretty":false,"x":650,"y":500,"wires":[["8764c6c2.3d27d8"]]},{"id":"a0d26ebc.76d71","type":"repeat","z":"21cbe739.15d9c8","name":"Triple It Up","repetitions":"3","elseOutput":false,"outputs":1,"x":730,"y":400,"wires":[["b98d58c7.ccb928","47549dc1.9ba0a4","f086575b.7055a8"]]},{"id":"8764c6c2.3d27d8","type":"delay","z":"21cbe739.15d9c8","name":"","pauseType":"delay","timeout":"25","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":830,"y":500,"wires":[["a0d26ebc.76d71"]]},{"id":"2ae4d01b.79f26","type":"json","z":"21cbe739.15d9c8","name":"","property":"payload","action":"","pretty":false,"x":470,"y":660,"wires":[["7e2173a7.c445ec"]]},{"id":"7e2173a7.c445ec","type":"function","z":"21cbe739.15d9c8","name":"Create MQTT message","func":"// expects the mqtt topic to be wizlight/ip/command\n\n// convert state to on/off\n\n\n\nmsg.payload =  msg.payload.result;\nmsg.topic = \"wizlight/\" + msg.ip + \"/status\"\n\nif (msg.payload.state === true) {\n    msg.payload.state = \"on\"\n} else {\n    msg.payload.state = \"off\"\n}\n\n\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":660,"wires":[["8e010fb5.fb0bd"]]},{"id":"8e010fb5.fb0bd","type":"mqtt out","z":"21cbe739.15d9c8","name":"Send to MQTT","topic":"","qos":"0","retain":"","broker":"a6852ac6.173b78","x":900,"y":660,"wires":[]},{"id":"d3371846.5b01a8","type":"switch","z":"21cbe739.15d9c8","name":"","property":"command","propertyType":"msg","rules":[{"t":"eq","v":"set","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":530,"y":560,"wires":[["47549dc1.9ba0a4"]]},{"id":"f086575b.7055a8","type":"repeat","z":"21cbe739.15d9c8","name":"Only Allow Once","repetitions":"1","elseOutput":false,"outputs":1,"x":710,"y":340,"wires":[["22de1803.bbafd8"]]},{"id":"22de1803.bbafd8","type":"delay","z":"21cbe739.15d9c8","name":"Wait One Second","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":930,"y":340,"wires":[["fa19997f.c4a078"]]},{"id":"7ca43ccf.bcea44","type":"mqtt-broker","z":"","name":"<IP of your MQTT broker>","broker":"<IP of your MQTT broker>","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"a6852ac6.173b78","type":"mqtt-broker","z":"","name":"<IP of your MQTT broker>","broker":"<IP of your MQTT broker>","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""}]
2 Likes

Here is my light.mqtt config I am using with the updated NodeRed flow. The color_temp handling when in RGB mode is kind of a hack, probably a better way to handle that. In theory the NodeRed flow wouldn’t need to actually poll the bulbs as you can use the registration command to have them send updates automatically to a given IP. This is easier though, since I don’t know what the persistence on that registration is, and making sure it stays maintained might be more of a pain that it is worth. Another improvement could be made to have the NodeRed convert all the json to comply with the mqtt json schema, as that would make the config a lot less ugly than all the templates needed to match what the bulb expects/replies with. And if the flow is doing the work to comply with a standard schema, could go one step further and use the correct format and topics for autodiscovery so you could just put the ips in the node red flow and be done with it. That is assuming someone doesn’t just write a custom component (and potentially eventual official integration) for these bulbs now that we know how to talk to them. Hopefully Wiz doesn’t tighten local security in response to that guys CVE he submitted.

light:
  - platform: mqtt
    name: "A Wiz Light"
    schema: template
    command_topic: "wizlight/<ip>/set"
    state_topic: "wizlight/<ip>/status"
    command_on_template: >
      {"state": true
      {%- if brightness is defined -%}
      ,"dimming": {{ (brightness/254*100)| int }}
      {%- endif -%}
      {%- if red is defined and green is defined and blue is defined -%}
      ,"r":{{ red }},"g":{{ green }},"b":{{ blue }}
      {%- endif -%}
      {%- if color_temp is defined -%}
      ,"temp":{{ (1000000/color_temp)| int}}
      {%- endif -%}
      {%- if white_value is defined -%}
      ,"w":{{ white_value }}
      {%- endif -%}
      }
    command_off_template: '{"state":false}'
    state_template: '{{ value_json.state}}'
    brightness_template: '{{ (value_json.dimming/100*254)| int }}'
    red_template: >
      {%- if value_json.r is defined -%}
      {{ value_json.r }}
      {%- else -%}
      255
      {%- endif -%}
    green_template: >
      {%- if value_json.g is defined -%}
      {{ value_json.g }}
      {%- else -%}
      255
      {%- endif -%}
    blue_template: >
      {%- if value_json.b is defined -%}
      {{ value_json.b }}
      {%- else -%}
      255
      {%- endif -%}
    color_temp_template: >
      {%- if value_json.temp is defined -%}
      {{ (1000000/value_json.temp)| int }}
      {%- else -%}
      370
      {%- endif -%}
    white_value_template: >
      {%- if value_json.w is defined -%}
      {{ value_json.w }}
      {%- else -%}
      {{ (value_json.dimming/100*254)| int }}
      {%- endif -%}

Thank you @ogremustcrush for this. It mostly works for me…curious if the UI should report back if its on? Currently if I click on, it turns the LED on, but the UI indicator flips back to off a second later…any thoughts?

If you look at my Node Red code, you can see my Node Red flows query the bulbs after a command to change state, and this updates the UI if you configure your MQTT settings for the bulb correctly.

Did you make sure you have all the IPs you are querying in the NodeRed flow as well as make sure the correct ip is in the state_topic? This is what I believe would happen if HA never receives a state update, or received a state update for another bulb (that is off in this case.) You wont have that behavior if you comment out the state_topic line, but then you won’t have state updates at all (HA will just maintain its internal state for the entity, but it won’t respond to changing the bulb through the Wiz app etc.)

Thank you for the replies. I think my conflict is I had two red flows, probably an earlier one and your newer one. Ultimately I deleted everything, reimported your yaml/red, configured IP’s and voila, worked like it should. Good job!

If you’re interested, I noticed in packet captures that every once in a while they beacon out a broadcast, in theory could be used to auto-find them? May be too much work when you could just input IP’s. Regardless, if you want that json, let me know.

1 Like

Sorry everyone for the noob questions. I just installed hassio yesterday, so I don’t know all the basics yet. I want to be able to control my Wiz lights through HA. I have a fresh install, so I need to add NodeRed and MQTT through the add-on stores? Where do I put these yaml and json files? @codinSorian what did you do exactly?

@atomken this integration isn’t a “drop in” solution : you do need to have a little bit of an understanding of how node-red and mqtt work and how your bulbs are communicating with node-red. You do need to install the node-red and mqtt integrations into HA, and then you can import the flow I’ve posted and the configuration that @ogremustcrush has given. Essentially, the yaml that @ogremustcrush provides tells HA to create entities that will send and receive mqtt messages when you either interact with them via the UI or when they’re changed by the Wiz app.

Then, the node-red flows look for those same MQTT messages and send commands to the bulbs. In addition, they poll the bulbs for changes and send corresponding messages to MQTT that update the bulb status.

Best of luck to you. It’s a fun project, and it continues to work well for me.

2 Likes

@atomken Yeah, agreed with @wilvancleve…spent an entire weekend on setting up Home Assistant, those two addons and trying to figure out what went wrong (mqtt needed me to make a username, woops). For the most part, unless you get into the nitty gritty, just follow addon instructions and import their code above, then configure the IP address fields/mqtt servers. Definitely more hands on than some stuff in HASSIO. Enjoyed it though, forced me to understand quite a few things.

1 Like

I’ve got to kinda work one bulb too, thanks a lot to @wilvancleve and @ogremustcrush for your groundbreaking work :slight_smile:

One thing I’ve noticed is when I use “scenes” from the app, the msg.payload will contain the scene id like in "state":"on","sceneId":13,"speed":100,"temp":6500,"dimming":100, but if setting a color via the color picker there will be the actual RGB values (e.g. "state":"on","sceneId":0,"r":0,"g":255,"b":126,"c":0,"w":52,"dimming":68); now the widget in Lovelace UI will show proper RGB values only when the bulb is not set to a scene. I’m trying to tie “effect” to “scenes” although would need to map them manually first.

Question: I can see the status in the light card in Lovelace, and it updates correctly, I can also issue changes to the bulb via MQTT, but when changing anything on the card it doesn’t create any MQTT message… am I missing something? I’d expect to see a command_topic to be created and sent.

1 Like

Hey,

I’m still working on a python3 translation/implementation to push a home assistant integration.
Contributions are welcome :smile: - the repo. can be found on GitHub: https://github.com/sbidy/pywizlight
Currently only tested with the SLV Play bulb.

1 Like

Hi guys

thanks for all the useful information regarding UDP commands. I just recently discovered the WiZ light bulbs and started experimenting with them.

@Sbidy Stephan: I think the parameter ‘w’ stands for warm white and ‘c’ for cold white. At least the WiZ bulb I am using has both types of LEDs in it.

Felix

1 Like

I’ve been controlling these using scripts from HA. Some more info if helpful to your work…
I’m trying to see if i can somehow connect them to a RPi and control via music (something that Hue and other bulbs natively support thru their apps)

  • sceneId - calls one of the predefined scenes (int from 0 to 32)
    scene 0 - Red
    scene 0 - Green
    scene 0 - Blue
    scene 0 - Yellow
    scene 1 - Ocean
    scene 2 - Romance
    scene 3 - Sunset
    scene 4 - Party
    scene 5 - Fireplace
    scene 6 - Cozy
    scene 7 - Forest
    scene 8 - Pastel Colors
    scene 9 - Wake-up
    scene 10 - Bedtime
    scene 11 - Warm White
    scene 12 - Day light
    scene 13 - Cool white
    scene 14 - Night light
    scene 15 - Focus
    scene 16 - Relax
    scene 17 - True colors
    scene 18 - TV time
    scene 19 - Plant growth
    scene 20 - Spring
    scene 21 - Summer
    scene 22 - Fall
    scene 23 - Deep dive
    scene 24 - Jungle
    scene 25 - Mojito
    scene 26 - Club
    scene 27 - Christmas
    scene 28 - Halloween
    scene 29 - Candlelight
    scene 30 - Golden white
    scene 31 - Pulse
    scene 32 - Steampunk
  • speed - sets the color changing speed in percent
  • dimming - sets the dimmer of the bulb in percent
  • r - red color range 0-255
  • g - green color range 0-255
  • b - blue color range 0-255
  • c - cool?? range 0-255
  • w - warm?? range 0-255
  • id - the bulb id

Let me know if you need any help with the integration of the component and if you can make it as a custom component that would help with validation…

1 Like

Thanks for the feedback!!
I’m still upgrading the class and try to implement all features from the bulb :wink:

A fist integration is done under https://github.com/sbidy/wiz_light you can clone this to your HA for testing. Currently on/off, brightness and the color temperature is working. But still under “heavy” development.

The documentation for the light platform integration is not really detailed.

2 Likes

Soo… the first usable version is ready for testing :slight_smile:

Implemented is:

  • Brightness
  • RGB Color with picker
  • On/Off
  • White Color Temperature

My problem is that I have only one SLV LED Bulb here.
Can somebody test the integration with other Bulb types? I think the code should be getting more flexible to support different types of lights (eg with or without RGB).

Next steps will be some improvements to the stability and I’ll try to implement and improve the pywizlight class to support the different scenes.

Doc. and code can be found her: https://github.com/sbidy/wiz_light

Please leave a star in the repo if you like the integration :upside_down_face:

3 Likes

Yw. This is great. I’ll check your component later today. One more param that is supported by the API is temp which can help control brightness
{“method”:“setPilot”,“params”:{“sceneId”:13,“r”:0,“g”:0,“b”:0,“c”:0,“w”:0,“temp”:5500,“dimming”:100}}

1 Like