I am aware. I didn’t mean it in any way to denigrate Home Assistant.
So, based on the information in the security analysis, I was able to send a UDP packet that activated one of my lights. Sending commands seems like a matter, then, of configuring each device (IP, MAC, and bulb type for proper parameters). Where I’m a bit confused is how monitoring state would work. Would love to learn more from someone more experienced if it’s possible to configure HA to listen to UDP traffic on the network, or whether setting up some sort of regular “ping” to each bulb to advertise its state would be required.
Well that’s some exciting progress!
Here’s a node-red node that can capture mqtt data (i.e. create mqtt lights and send them to your mqtt server) and translates them into tripled-up UDP calls that the Wiz bulbs will respond to. For the commands the bulbs expect, look at the documentation linked higher in this thread.
The calls to the UDP node is tripled up because I’ve noticed that the bulbs will often “miss” commands if they’re sent rapidly, but tripling up seems to fix this problem.
I haven’t been as successful in writing code to update HASS when the bulbs are changed elsewhere (Alexa, the Wiz app). While I can register the bulbs to send UDP commands to my server (as thus node-red), the 5 second ongoing updates are somewhat overwhelming, and I haven’t been able to suss out an API call that responds with bulb state.
Hope this helps someone.
[
{
"id": "3d109b6e.9aeda4",
"type": "tab",
"label": "Demo Flow",
"disabled": false,
"info": ""
},
{
"id": "34d9e40a.f75bcc",
"type": "json",
"z": "3d109b6e.9aeda4",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 310,
"y": 340,
"wires": [
[
"15fbe611.434b8a"
]
]
},
{
"id": "290eaf47.9580f",
"type": "comment",
"z": "3d109b6e.9aeda4",
"name": "Relay MQTT to UDP",
"info": "",
"x": 170,
"y": 260,
"wires": []
},
{
"id": "15fbe611.434b8a",
"type": "function",
"z": "3d109b6e.9aeda4",
"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[command] = 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": [
[
"ef2ef336.20cd1"
]
]
},
{
"id": "d99585ca.e81038",
"type": "mqtt in",
"z": "3d109b6e.9aeda4",
"name": "Wizlight In",
"topic": "wizlight/#",
"qos": "0",
"datatype": "auto",
"broker": "",
"x": 160,
"y": 340,
"wires": [
[
"34d9e40a.f75bcc"
]
]
},
{
"id": "adbcb2c3.f7b18",
"type": "udp out",
"z": "3d109b6e.9aeda4",
"name": "UDP auto port/ip",
"addr": "",
"iface": "",
"port": "",
"ipv": "udp4",
"outport": "38899",
"base64": false,
"multicast": "false",
"x": 1070,
"y": 200,
"wires": []
},
{
"id": "ef2ef336.20cd1",
"type": "json",
"z": "3d109b6e.9aeda4",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 690,
"y": 340,
"wires": [
[
"5212efd2.fd1b9"
]
]
},
{
"id": "541bd62d.bf5178",
"type": "repeat",
"z": "3d109b6e.9aeda4",
"name": "Triple It Up",
"repetitions": "3",
"elseOutput": false,
"outputs": 1,
"x": 810,
"y": 200,
"wires": [
[
"adbcb2c3.f7b18",
"ef2ef336.20cd1"
]
]
},
{
"id": "5212efd2.fd1b9",
"type": "delay",
"z": "3d109b6e.9aeda4",
"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": [
[
"541bd62d.bf5178"
]
]
}
]
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":""}]
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.
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.
@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.
I’ve got to kinda work one bulb too, thanks a lot to @wilvancleve and @ogremustcrush for your groundbreaking work
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.
Hey,
I’m still working on a python3 translation/implementation to push a home assistant integration.
Contributions are welcome - the repo. can be found on GitHub: https://github.com/sbidy/pywizlight
Currently only tested with the SLV Play bulb.
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