Example #1
0
        private Task <T> BroadcastMessageAsync <T>(string hostName, FrameHeader header, MessageType type, params object[] args)
            where T : LifxResponse

        {
            List <byte> payload = new List <byte>();

            if (args != null)
            {
                foreach (var arg in args)
                {
                    if (arg is UInt16)
                    {
                        payload.AddRange(BitConverter.GetBytes((UInt16)arg));
                    }
                    else if (arg is UInt32)
                    {
                        payload.AddRange(BitConverter.GetBytes((UInt32)arg));
                    }
                    else if (arg is byte)
                    {
                        payload.Add((byte)arg);
                    }
                    else if (arg is byte[])
                    {
                        payload.AddRange((byte[])arg);
                    }
                    else if (arg is string)
                    {
                        payload.AddRange(Encoding.UTF8.GetBytes(((string)arg).PadRight(32).Take(32).ToArray()));                         //All strings are 32 bytes
                    }
                    else
                    {
                        throw new NotSupportedException(args.GetType().FullName);
                    }
                }
            }
            return(BroadcastMessagePayloadAsync <T>(hostName, header, type, payload.ToArray()));
        }
Example #2
0
        /// <summary>
        /// Turns a bulb on or off using the provided transition time
        /// </summary>
        /// <param name="bulb"></param>
        /// <param name="transitionDuration"></param>
        /// <param name="isOn">True to turn on, false to turn off</param>
        /// <returns></returns>
        /// <seealso cref="TurnBulbOffAsync(LightBulb, TimeSpan)"/>
        /// <seealso cref="TurnBulbOnAsync(LightBulb, TimeSpan)"/>
        /// <seealso cref="TurnDeviceOnAsync(Device)"/>
        /// <seealso cref="TurnDeviceOffAsync(Device)"/>
        /// <seealso cref="SetDevicePowerStateAsync(Device, bool)"/>
        /// <seealso cref="GetLightPowerAsync(LightBulb)"/>
        public async Task SetLightPowerAsync(LightBulb bulb, TimeSpan transitionDuration, bool isOn)
        {
            if (bulb == null)
            {
                throw new ArgumentNullException(nameof(bulb));
            }
            if (transitionDuration.TotalMilliseconds > uint.MaxValue ||
                transitionDuration.Ticks < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(transitionDuration));
            }

            FrameHeader header = new FrameHeader(GetNextIdentifier(), true);

            var b = BitConverter.GetBytes((ushort)transitionDuration.TotalMilliseconds);

            Debug.WriteLine(
                $"Sending LightSetPower(on={isOn},duration={transitionDuration.TotalMilliseconds}ms) to {bulb.HostName}");

            await BroadcastMessageAsync <AcknowledgementResponse>(bulb.HostName, header, MessageType.LightSetPower,
                                                                  (ushort)(isOn ? 65535 : 0), b
                                                                  ).ConfigureAwait(false);
        }
        private async Task SetLightPowerAsync(LightBulb bulb, TimeSpan transitionDuration, bool isOn)
        {
            if (bulb == null)
            {
                throw new ArgumentNullException("bulb");
            }
            if (transitionDuration.TotalMilliseconds > UInt32.MaxValue ||
                transitionDuration.Ticks < 0)
            {
                throw new ArgumentOutOfRangeException("transitionDuration");
            }

            FrameHeader header = new FrameHeader()
            {
                Identifier          = (uint)randomizer.Next(),
                AcknowledgeRequired = true
            };

            var b = BitConverter.GetBytes((UInt16)transitionDuration.TotalMilliseconds);

            await BroadcastMessageAsync <AcknowledgementResponse>(bulb.HostName, header, MessageType.LightSetPower,
                                                                  (UInt16)(isOn ? 65535 : 0), b
                                                                  ).ConfigureAwait(false);
        }
        /// <summary>
        /// Sets color and temperature for a bulb and uses a transition time to the provided state
        /// </summary>
        /// <param name="bulb">Light bulb</param>
        /// <param name="hue">0..65535</param>
        /// <param name="saturation">0..65535</param>
        /// <param name="brightness">0..65535</param>
        /// <param name="kelvin">2700..9000</param>
        /// <param name="transitionDuration"></param>
        /// <returns></returns>
        public async Task SetColorAsync(LightBulb bulb,
                                        UInt16 hue,
                                        UInt16 saturation,
                                        UInt16 brightness,
                                        UInt16 kelvin,
                                        TimeSpan transitionDuration)
        {
            if (transitionDuration.TotalMilliseconds > UInt32.MaxValue ||
                transitionDuration.Ticks < 0)
            {
                throw new ArgumentOutOfRangeException("transitionDuration");
            }
            if (kelvin < 2500 || kelvin > 9000)
            {
                throw new ArgumentOutOfRangeException("kelvin", "Kelvin must be between 2500 and 9000");
            }

            System.Diagnostics.Debug.WriteLine("Setting color to {0}", bulb.HostName);
            FrameHeader header = new FrameHeader()
            {
                Identifier          = (uint)randomizer.Next(),
                AcknowledgeRequired = true
            };
            UInt32 duration      = (UInt32)transitionDuration.TotalMilliseconds;
            var    durationBytes = BitConverter.GetBytes(duration);
            var    h             = BitConverter.GetBytes(hue);
            var    s             = BitConverter.GetBytes(saturation);
            var    b             = BitConverter.GetBytes(brightness);
            var    k             = BitConverter.GetBytes(kelvin);

            await BroadcastMessageAsync <AcknowledgementResponse>(bulb.HostName, header,
                                                                  MessageType.LightSetColor, (byte)0x00, //reserved
                                                                  hue, saturation, brightness, kelvin,   //HSBK
                                                                  duration
                                                                  );
        }
        /// <summary>
        /// Request an arbitrary payload be echoed back.
        /// </summary>
        /// <param name="device"></param>
        /// <param name="payload"></param>
        /// <returns><see cref="EchoResponse"/></returns>
        /// <exception cref="ArrayTypeMismatchException"></exception>
        public async Task <EchoResponse> RequestEcho(Device device, byte[] payload)
        {
            if (device == null)
            {
                throw new ArrayTypeMismatchException(nameof(device));
            }
            FrameHeader header = new FrameHeader(GetNextIdentifier());
            // Truncate our input payload to be 64 bits exactly
            var realPayload = new byte[64];

            for (var i = 0; i < realPayload.Length; i++)
            {
                if (i < payload.Length)
                {
                    realPayload[i] = payload[i];
                }
                else
                {
                    realPayload[i] = 0;
                }
            }
            return(await BroadcastMessageAsync <EchoResponse>(device.HostName, header,
                                                              MessageType.DeviceEchoRequest, realPayload));
        }
Example #6
0
 internal StateServiceResponse(FrameHeader header, MessageType type, byte[] payload, UInt32 source) : base(header, type, payload, source)
 {
     Service = payload[0];
     Port    = BitConverter.ToUInt32(payload, 1);
 }
Example #7
0
 internal AcknowledgementResponse(FrameHeader header, MessageType type, byte[] payload, UInt32 source) : base(header, type, payload, source)
 {
 }
Example #8
0
 internal UnknownResponse(FrameHeader header, MessageType type, byte[] payload, UInt32 source) : base(header, type, payload, source)
 {
 }
Example #9
0
 internal StateVersionResponse(FrameHeader header, MessageType type, byte[] payload, UInt32 source) : base(header, type, payload, source)
 {
     Vendor  = BitConverter.ToUInt32(payload, 0);
     Product = BitConverter.ToUInt32(payload, 4);
     Version = BitConverter.ToUInt32(payload, 8);
 }
Example #10
0
 internal InfraredStateRespone(FrameHeader header, MessageType type, byte[] payload, UInt32 source) : base(header, type, payload, source)
 {
     Brightness = BitConverter.ToUInt16(payload, 0);
 }
Example #11
0
 internal LightPowerResponse(FrameHeader header, MessageType type, byte[] payload, UInt32 source) : base(header, type, payload, source)
 {
     IsOn = BitConverter.ToUInt16(payload, 0) > 0;
 }
Example #12
0
 private async Task WritePacketToStreamAsync(Stream outStream, FrameHeader header, UInt16 type, byte[] payload)
 {
     using (var dw = new BinaryWriter(outStream) /*ByteOrder = ByteOrder.LittleEndian*/ }
Example #13
0
        private async Task <T> BroadcastMessagePayloadAsync <T>(string hostName, FrameHeader header, MessageType type, byte[] payload)
            where T : LifxResponse
        {
#if DEBUG
            /// MemoryStream ms = new MemoryStream();
            /// await WritePacketToStreamAsync(ms.AsOutputStream(), header, (UInt16)type, payload).ConfigureAwait(false);
            /// var data = ms.ToArray();
            /// System.Diagnostics.Debug.WriteLine(
            ///     string.Join(",", (from a in data select a.ToString("X2")).ToArray()));
#endif
            if (hostName == null)
            {
                hostName = "255.255.255.255";
            }
            TaskCompletionSource <T> tcs = null;
            if (//header.AcknowledgeRequired &&
                header.Identifier > 0 &&
                typeof(T) != typeof(UnknownResponse))
            {
                tcs = new TaskCompletionSource <T>();
                Action <LifxResponse> action = (r) =>
                {
                    if (!tcs.Task.IsCompleted)
                    {
                        if (r.GetType() == typeof(T))
                        {
                            tcs.SetResult((T)r);
                        }
                        else
                        {
                        }
                    }
                };
                taskCompletions[header.Identifier] = action;
            }

            using (MemoryStream stream = new MemoryStream())
            {
                await WritePacketToStreamAsync(stream, header, (UInt16)type, payload).ConfigureAwait(false);

                var msg = stream.ToArray();
                await _socket.SendAsync(msg, msg.Length, hostName, Port);
            }
            //{
            //	await WritePacketToStreamAsync(stream, header, (UInt16)type, payload).ConfigureAwait(false);
            //}
            T result = default(T);
            if (tcs != null)
            {
                var _ = Task.Delay(1000).ContinueWith((t) =>
                {
                    if (!t.IsCompleted)
                    {
                        tcs.TrySetException(new TimeoutException());
                    }
                });
                try {
                    result = await tcs.Task.ConfigureAwait(false);
                }
                finally
                {
                    taskCompletions.Remove(header.Identifier);
                }
            }
            return(result);
        }
Example #14
0
        private async Task WritePacketToStreamAsync(IOutputStream outStream, FrameHeader header, UInt16 type, byte[] payload)
        {
            using (DataWriter dw = new DataWriter(outStream)
            {
                ByteOrder = ByteOrder.LittleEndian
            })
            {
                //BinaryWriter bw = new BinaryWriter(ms);
                #region Frame
                //size uint16
                dw.WriteUInt16((UInt16)((payload != null ? payload.Length : 0) + 36)); //length
                // origin (2 bits, must be 0), reserved (1 bit, must be 0), addressable (1 bit, must be 1), protocol 12 bits must be 0x400) = 0x1400
                dw.WriteUInt16(0x3400);                                                //protocol
                dw.WriteUInt32(header.Identifier);                                     //source identifier - unique value set by the client, used by responses. If 0, responses are broadcasted instead
                #endregion Frame

                #region Frame address
                //The target device address is 8 bytes long, when using the 6 byte MAC address then left -
                //justify the value and zero-fill the last two bytes. A target device address of all zeroes effectively addresses all devices on the local network
                dw.WriteBytes(header.TargetMacAddress);                 // target mac address - 0 means all devices
                dw.WriteBytes(new byte[] { 0, 0, 0, 0, 0, 0 });         //reserved 1

                //The client can use acknowledgements to determine that the LIFX device has received a message.
                //However, when using acknowledgements to ensure reliability in an over-burdened lossy network ...
                //causing additional network packets may make the problem worse.
                //Client that don't need to track the updated state of a LIFX device can choose not to request a
                //response, which will reduce the network burden and may provide some performance advantage. In
                //some cases, a device may choose to send a state update response independent of whether res_required is set.
                if (header.AcknowledgeRequired && header.ResponseRequired)
                {
                    dw.WriteByte(0x03);
                }
                else if (header.AcknowledgeRequired)
                {
                    dw.WriteByte(0x02);
                }
                else if (header.ResponseRequired)
                {
                    dw.WriteByte(0x01);
                }
                else
                {
                    dw.WriteByte(0x00);
                }
                //The sequence number allows the client to provide a unique value, which will be included by the LIFX
                //device in any message that is sent in response to a message sent by the client. This allows the client
                //to distinguish between different messages sent with the same source identifier in the Frame. See
                //ack_required and res_required fields in the Frame Address.
                dw.WriteByte(header.Sequence);
                #endregion Frame address

                #region Protocol Header
                //The at_time value should be zero for Set and Get messages sent by a client.
                //For State messages sent by a device, the at_time will either be the device
                //current time when the message was received or zero. StateColor is an example
                //of a message that will return a non-zero at_time value
                if (header.AtTime > DateTime.MinValue)
                {
                    var time = header.AtTime.ToUniversalTime();
                    dw.WriteUInt64((UInt64)(time - new DateTime(1970, 01, 01)).TotalMilliseconds * 10);                     //timestamp
                }
                else
                {
                    dw.WriteUInt64(0);
                }
                #endregion Protocol Header
                dw.WriteUInt16(type);              //packet _type
                dw.WriteUInt16(0);                 //reserved
                if (payload != null)
                {
                    dw.WriteBytes(payload);
                }
                await dw.StoreAsync();
            }
        }
Example #15
0
        private async Task <T> BroadcastMessagePayloadAsync <T>(string hostName, FrameHeader header, MessageType type, byte[] payload)
            where T : LifxResponse
        {
            uint attemptCount = 5;
            T    result       = default(T);

            do
            {
                if (hostName == null)
                {
                    hostName = "255.255.255.255";
                }
                TaskCompletionSource <T> tcs = null;
                if (//header.AcknowledgeRequired &&
                    header.Identifier > 0 &&
                    typeof(T) != typeof(UnknownResponse))
                {
                    tcs = new TaskCompletionSource <T>();
                    Action <LifxResponse> action = (r) =>
                    {
                        if (!tcs.Task.IsCompleted)
                        {
                            if (r.GetType() == typeof(T))
                            {
                                tcs.SetResult((T)r);
                            }
                        }
                    };
                    taskCompletions[header.Identifier] = action;
                }

                using (MemoryStream stream = new MemoryStream())
                {
                    WritePacketToStreamAsync(stream, header, (UInt16)type, payload);
                    var msg = stream.ToArray();
                    await _socket.SendAsync(msg, msg.Length, hostName, Port);
                }

                if (tcs != null)
                {
                    var _ = Task.Delay(1000).ContinueWith((t) =>
                    {
                        if (!tcs.Task.IsCompleted)
                        {
                            tcs.TrySetException(new TimeoutException());
                        }
                    });
                    try
                    {
                        result = await tcs.Task.ConfigureAwait(false);
                    }
                    catch
                    {
                    }
                    finally
                    {
                        taskCompletions.Remove(header.Identifier);
                    }
                }
                attemptCount--;
            } while (result == null && attemptCount > 0);
            return(result);
        }