I opened an issue on the protocol docs a couple of weeks ago, but perhaps these discussions are a better forum. I’m looking for details on how to get/set groups using the LAN protocol. I’ve written a Go client lib based on the docs, but this is a missing part of the puzzle that would make interacting with bulbs easier - having to specify every bulb when issuing commands that target an area is not ideal.
As you noticed, group support has not yet been added to the protocol documentation. We are planning to continue updating the protocol documentation, including documenting new features. I’ll make sure that this ends up in the list of things to be documented, and I’ll update this thread when I have any further news.
Ah, so do the bulbs themselves “know” that they are in a group? If so, that’s good news. I was thinking that groups were just a feature of the Lifx app and Lifx Cloud.
Personally, I am having trouble getting multiple bulbs to switch colour simultaneously using the LAN protocol - and this is something that the mobile app and the Cloud seem to be able to do very easily.
When you use the app to change the colour of a group, what does the app actually do? Does it quickly unicast messages to each of the bulbs in the group, one after the other? Or does it broadcast a message to the whole LAN that only the bulbs in the group respond to?
Yes, the bulbs store which group they belong to.
On iOS we send a unicast message to each bulb in the group and wait for all their responses using a dispatch group. We make heavy use of Grand Central Dispatch for handling concurrency. The HTTP API does something similar.
Oh that’s cool. In that case, it might be good if a Light::State message also included the group? It seems like the group and location are the only important pieces of data that are missing, if you compare you compare Light::State to the HTTP API’s List Lights command.
@daniel_hall - any ideas on achieve something similar in Python? I’m thinking that I might have to bite the bullet and learn something about multithreading?
So, you’re waiting until you receive ACKs from all devices in the group as a means of syncing sends? What happens in the case of missing ACKs? Does the whole group block while you retry?
Short answer: Yes.
Long answer: The dispatch group will only call its completion handler after all of its operations call their completion handlers. An operation only calls it’s completion handler after the first successful message, or three failed messages. The maximum we wait until we consider a message failed is 500ms if an ACK is required. All of this is happening in a background queue.
In practice it happens pretty fast, but it’s why under certain circumstances you may see some lights respond faster than others when changing the colour of a group.
@rasputin303 In Python I think the best way to handle this is to use Threads unfortunately. In the API I’m building I have a receiving thread that is responsible for all incoming packets, and in the future probably a sending thread too. If you want to track messages that need acknowledgments you’ll also need a thread for that. Remember that in Python only a single thread can be running Python code can be running at once, but since most of these threads will be doing IO that should be fine.
You could also possibly do it with an event loop, but I personally prefer threads.
So I just added the documentation for Device::GetLocation, Device::StateLocation, Device::GetGroup and Device::StateGroup. Using these you can inspect the groups that are set on the bulbs. We will be following up with a guide to using them, but for now here are a few tips:
You group bulbs by the value of the
groupfield, this is the identifier for the group.
updated_atfield is set by the applications when the group changes, or the
labelyou show in your UI should be set to the
labelset on the bulb with the highest
updated_atfor that group. This is because the
labelmight be changed when some of the bulb in the group are off.
The same essentially applies to locations, but groups that span locations are currently undefined.
The client is responsible for messaging each of the bulbs in a group when performing an action on that group.
Multithreading achieved! It was actually not as difficult as I thought - the “Pool” function from multiprocessing.dummy in Python is really handy. I’m seeing some good results with sending multiple SetColor messages to different bulbs and getting them to respond more-or-less simultaneously. Now I just need to refactor the rest of my code to work in a threaded way.
Incidentally, I’m kinda surprised at the extent of the packet loss on my network - but a timeout of 100ms and 10 retries seems to be enough to get all my bulbs to respond within a reasonable timeframe.
I wouldn’t be too quick to chalk this up to packet loss - my diagnostics show LIFX bulbs tend to be the weak link processing packets here.