LAN API updates not documented?

I’ve been working on a LAN client in Elixir and was having some issues with the discovery packet. I eventually got it working, but not without banging my head against the wall. I’m not sure if the documentation is out of date, but I had to reverse engineer the python SDK to get the proper values. I’m mostly wondering if I have something wrong in my packet creation/parsing.

Here is my discovery packet

base16

24000014FAB0A10000000000000000000000000000000100000000000000000002000000

My discovery packet, parsed. Note, the field order here does not respect the order in the actual binary sent.

%LifxClient.Packet{
    frame_header: %LifxClient.FrameHeader{
        addressable: 0, 
        origin: 0,
        protocol: 1025, 
        size: 36, 
        source: 786233207, 
        tagged: 0
    }, 
    frame_address: %LifxClient.FrameAddress{
        ack_required: 0,
        res_required: 1, 
        reserved: 0, 
        reserved1: 0, 
        sequence: 0, 
        target: 0
    },
    protocol_header: %LifxClient.ProtocolHeader{
        reserved: 0, 
        reserved1: 0,
        type: 2
    }, 
    payload: %{}
}

I’ve had to bump up the protocol to 1025, this probably took me the longest to debug, 1024 doesn’t get a reponse. I’ve also had to set tagged and origin to false (0) sending 1 doesn’t get a response.

And now I get 2 responses back from my light.

%LifxClient.Packet{
    frame_header: %LifxClient.FrameHeader{
        addressable: 0, 
        origin: 0,
        protocol: 1029, 
        size: 41, 
        source: 786233207, 
        tagged: 0
    },
    frame_address: %LifxClient.FrameAddress{
        ack_required: 0,
        res_required: 0, 
        reserved: 83877596517938, 
        reserved1: 0, 
        sequence: 0,
        target: 15020583458663104512
    },
    
    protocol_header: %LifxClient.ProtocolHeader{
        reserved: 2372447284767974420,
        reserved1: 0, 
        type: 3
    },
    payload: %{port: 56700, service: 1}
}
  
%LifxClient.Packet{
    frame_header: %LifxClient.FrameHeader{
        addressable: 0, 
        origin: 0,
        protocol: 1029, 
        size: 41, 
        source: 786233207, 
        tagged: 0
    },
    frame_address: %LifxClient.FrameAddress{
        ack_required: 0,
        res_required: 0, 
        reserved: 83877596517938, 
        reserved1: 0, 
        sequence: 0,
        target: 15020583458663104512
    },
    protocol_header: %LifxClient.ProtocolHeader{
        reserved: 2320630608875709460,
        reserved1: 0, 
        type: 3
    },
    payload: %{port: 56700, service: 5}
}

The responses are using protocol 1029 and I get 2 different service responses 1 and 5 and the reserved in the ProtocolHeader are slightly different. Just wondering if this is to be expected, and if I missed some sort of documentation.

When I parse that data that you posted above I get:

lifx_packet(
  frame_header=frame_header(
    size=36,
    origin=0,
    tagged=0,
    addressable=1,
    protocol=1024,
    source=10596602),
  frame_address=frame_address(
    target=0,
    reserved1=0,
    reserved2=0,
    ack_required=0,
    res_required=1,
    sequence=0),
  protocol_header=protocol_header(
    reserved1=0,
    pkt_type=2,
    reserved2=0),
  payload=payload_getservice()
)

So it looks like you are actually sending a protocol of 1024 in your discovery packet. There might be something wrong with your packet parser.

Most bulbs will send back more than one service record, the only one documented for third party use is 1, which is the UDP service that understands the LIFX protocol. You can safely ignore all other service responses from the bulb.

You should ignore all reserved values being returned from the bulb.

The protocol is little-endian encoded correct? I’m guessing it has to do with my packet creation code, which you can see here. https://github.com/NationalAssociationOfRealtors/lifx_client/blob/master/lib/lifx_client.ex#L232

Any ideas what’s wrong with that? unsigned is the default for integers, maybe I need to indicate signedness for my integers?

Is it possible my field ordering is wrong, the diagrams and the struct code snippet show different order of the fields.

packet = <<
    fh.origin::little-integer-size(2),
    fh.tagged::little-integer-size(1),
    fh.addressable::little-integer-size(1),
    fh.protocol::little-integer-size(12),
    fh.source::little-integer-size(32),

    fa.target::little-integer-size(64),
    fa.reserved::little-integer-size(48),
    fa.reserved1::little-integer-size(6),
    fa.ack_required::little-integer-size(1),
    fa.res_required::little-integer-size(1),
    fa.sequence::little-integer-size(8),

    ph.reserved::little-integer-size(64),
    ph.type::little-integer-size(16),
    ph.reserved1::little-integer-size(16),
>> <> payload
<<byte_size(packet)+2::little-integer-size(16)>> <> packet

Ok, I figured it out. origin, tagged, addressable and protocol are treated as 2 bytes and swapped as one.

def create_packet(%FrameHeader{} = fh, %FrameAddress{} = fa, %ProtocolHeader{} = ph, payload \\ <<>>) when is_binary(payload) do
    otap = reverse_bits(<<fh.origin::size(2),
        fh.tagged::size(1),
        fh.addressable::size(1),
        fh.protocol::size(12),
    >>)
    rar = reverse_bits(<<
        fa.reserved1::size(6),
        fa.ack_required::size(1),
        fa.res_required::size(1),
    >>)
    packet = <<
        otap::bits-size(16),
        fh.source::little-integer-size(32),

        fa.target::little-integer-size(64),
        fa.reserved::little-integer-size(48),
        rar::bits-size(8),
        fa.sequence::little-integer-size(8),

        ph.reserved::little-integer-size(64),
        ph.type::little-integer-size(16),
        ph.reserved1::little-integer-size(16),
    >>
    <<byte_size(packet)+2::little-integer-size(16)>> <> packet <> payload
end
1 Like