1

I trying to write a menubar app to get control over my lights via Mac. I'm using the system of milight (limitless, easybulbs...). They have an open system were you can send commands via UDP.

I'm able to control my lights via python-limitless library in python, so I know the networking thing such as IP and port is right.

So I think I do anything wrong with this UDP stuff I never worked with. I'm trying to use SwiftSocket library to send my commands but nothing happens, I've been trying it since 2 days.

Here ist what I'm trying:

let host = "192.168.2.102"
let port = 5987

var client: UDPClient!

@IBAction func lightOn(_ sender: NSButton) {

    let bridgeon: [UInt8] = [0x31, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x01]
    let rgbwon: [UInt8] = [0x31, 0x00, 0x00, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0]

    print("Licht an")
    print(client.send(data: bridgeon))
    sleep(1)
    print(client.send(data: rgbwon))
    sleep(1)

}

@IBAction func lightOff(_ sender: NSButton) {
    print("Licht aus")
}

override func viewDidLoad() {
    super.viewDidLoad()

    client = UDPClient(address: host, port: Int32(port))
}

When I compare this with the complexity of the pythonlibrary I'm sure I forget something important. I haven't worked with networks yet so be lenient with me.

thanks and greetings.

kuemme01
  • 472
  • 5
  • 19

1 Answers1

4

I'm a bit late, but I hope it can help you :

Before sending your lighton request you have to send a first request to get, the wifi bridge session. You also need to compute what Milight called the "checksum" based on your request.

You also make sure about what kind of bulb you have, is it WW bulb or CW? I was stuck for severals days because I was sending wrong request..

I made an implementation, It's in php but you can use it same way in Objective-C.

Check it out : https://github.com/winosaure/MilightAPI

UPDATE :

According to limitlessled "documentation" (http://www.limitlessled.com/dev/) this is how a request is composed :

UDP Hex Send Format: 80 00 00 00 11 {WifiBridgeSessionID1} {WifiBridgeSessionID2} 00 {SequenceNumber} 00 {COMMAND} {ZONE NUMBER} 00 {Checksum}

This is why you must get wifibridge session first then you need to calculate the checksum.

Let me take one example about how to turn on the light.

The documentation says that :

31 00 00 08 04 01 00 00 00 = Light ON

31 00 00 08 04 01 00 00 00 refer to {COMMAND} above.

So far the full request must be :

80 00 00 00 11 {WifiBridgeSessionID1} {WifiBridgeSessionID2} 00 {SequenceNumber} 00 31 00 00 08 04 01 00 00 00 {ZONE NUMBER} 00 {Checksum}

Now let's get the Wifibridge session. The documention says :

to get the WifiBridgeSessionID1 and WifiBridgeSessionID2 send this command UDP.

SEND hex bytes: 20 00 00 00 16 02 62 3A D5 ED A3 01 AE 08 2D 46 61 41 A7 F6 DC AF (D3 E6) 00 00 1E <-- Send this to the ip address of the wifi bridge v6

That's why I'm doing this:

private function getWifiBridgeSession()
    {
        $command = array (
            0x20,0x00, 0x00,
            0x00, 0x16, 0x02,
            0x62, 0x3A, 0xD5,
            0xED, 0xA3, 0x01,
            0xAE, 0x08, 0x2D,
            0x46, 0x61, 0x41,
            0xA7, 0xF6, 0xDC,
            0xAF, 0xD3, 0xE6,
            0x00, 0x00, 0x1E);

        return $this->sendCommand($command);
    }

Once you send a UDP request with this command, you will get a result. The Wifi Bridge session1 refers to the 20th byte of the response and WifiBridge Session2 will refer to the 21th byte response (Don't forget that we start to count from 0, so you must take something like "response[19]" and "response[20]").

Let's say, after sending this request I get this response :

28 00 00 00 11 00 02 AC CF 23 F5 7A D4 69 F0 3C 23 00 01 05 00

So my "WifiBridgesession1" is 0x05 and "Wifibridgesession2" is 0x00

So now our request to "turn on" the light is :

80 00 00 00 11 0x05 0x00 00 {SequenceNumber} 00 31 00 00 08 04 01 00 00 00 {ZONE NUMBER} 00 {Checksum}

So now we need to find out {SequenceNumber} {Zone Number} and {Checksum}

What is a "Sequence Number"?

The doc says :

Sequential byte just helps with keeping commands in the correct order, and it helps to ignore duplicate packets already received. increment this byte for each new command by 1.

So put what you want and increase this value to 1 for each request. (Personnally I always send 0x01).

"Zone number" refers to which zone you synchronized your light.

Valid List for {ZONE NUMBER} 0x00 All 0x01 Zone1 0x02 Zone2 0x03 Zone3 0x04 Zone4

Let's say, our "zone" is 0x01.

Almost done. we just need now to calculate the "checksum".

The doc says :

take the 9 bytes of the command, and 1 byte of the zone, and add the 0 = the checksum = (checksum & 0xFF) e.g. SUM((31 00 00 08 04 01 00 00 00)(command) 01(zone) 00) = 3F(chksum)

So the checksum for our command is : 31+00+00+08+04+01+00+00+00+01+00 = 0x54

I add all byte of the command (turn on) + 0x01 for the zone + 0x00

So now we have everything and the full request to turn on the light is :

80 00 00 00 11 05 00 00 01 00 31 00 00 08 04 01 00 00 00 01 00 54

That's it.

Note : Do not just copy and paste the request, I calculated the value based on example, the request to turn on the light will change each time, based on what you will calculate.

Maybe you got noticed that I wrote "00 31 00 00 08 04 01 00 00 00" to do the "turn on" command, this will work only for CW bulb. The doc does not specify that... The same Command for WW bulb is 00 31 00 00 07 03 01 00 00 00 So the full command for WW bulb will be :

80 00 00 00 11 05 00 00 01 00 31 00 00 07 03 01 00 00 00 01 00 54

What is the difference between CW and WW bulb? I can tell that CW refers to "Cold White" and WW to "Warm White". But as I am not an expert in "led bulb" I cannot explain more, I don't know why we need to write a different request for both, either.

Anyway I wish I was clear enough. Let me know how things are working.

wilfleaji
  • 128
  • 8
  • Never to late! Still working on this project! All of my Bulbs are WW wheres the difference? In your code you also use WW so I can use them right? Can you explain more? How do I get the Bridgesession and why do I need the checksum, the sum of all received bytes!? – kuemme01 Apr 18 '17 at 11:25
  • I updated my comment to explain in more detail what I did. – wilfleaji Apr 19 '17 at 21:32
  • Man you such a nice guy! This was perfect! I'm not yet solved it because I have to figure out how to receive data with the UDP library I'm using, I think its not possible so I have to search for an other one. I will update you soon! Thanks so far! It could not have been better explained! – kuemme01 Apr 20 '17 at 17:11
  • You are welcome! I don't know well about swift, I can't help on that part unfortunately. I believe if you will be able to read data from UDP, you have done the most difficult part. I was also stuck for a while because the documentation of limitlessled is not really clear – wilfleaji Apr 20 '17 at 20:51
  • Yes, the doku is horrible! – kuemme01 Apr 20 '17 at 20:54