public static List <(string ip, string type, string serial, string token)> SendDiscoverMessage(int port = 54321)
        {
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, MESSAGES_TIMEOUT);
            socket.Bind(new IPEndPoint(IPAddress.Any, 0));
            socket.SendTo(HELLO_REQUEST.ToByteArray(), new IPEndPoint(IPAddress.Broadcast, port));

            var discoveredDevices = new List <(string ip, string type, string serial, string token)>();

            try
            {
                var      buffer    = new byte[4096];
                var      bytesRead = 0;
                EndPoint endPoint  = new IPEndPoint(IPAddress.Any, 0);
                while ((bytesRead = socket.ReceiveFrom(buffer, ref endPoint)) > 0)
                {
                    var ip     = ((IPEndPoint)endPoint).Address.ToString();
                    var packet = new MiioPacket(new ArraySegment <byte>(buffer, 0, bytesRead).ToArray().ToHex());
                    discoveredDevices.Add((ip, packet.GetDeviceType(), packet.GetSerial(), packet.GetChecksum()));
                }
            }
            catch { } // Normal situation, no more data in socket

            socket.Close();

            return(discoveredDevices);
        }
        private async Task SendHelloPacketIfNeededAsync()
        {
            if (initialPacket == null)
            {
                await _udpClient.SendAsync(HELLO_REQUEST.ToByteArray(), _endpoint).ConfigureAwait(false);

                var receviedHello = (await _udpClient.ReceiveAsync().ConfigureAwait(false)).Buffer;

                if (receviedHello.Length != 32) // hello response message must be 32 bytes
                {
                    throw _helloException;
                }

                initialPacket = new MiioPacket(receviedHello.ToHex());
            }
        }
        private void SendHelloPacketIfNeeded()
        {
            if (initialPacket == null)
            {
                _udpClient.SendTo(HELLO_REQUEST.ToByteArray(), _endpoint);

                var receviedHello = _udpClient.Receive(ref _endpoint);

                if (receviedHello.Length != 32) // hello response message must be 32 bytes
                {
                    throw _helloException;
                }

                initialPacket = new MiioPacket(receviedHello.ToHex());
            }
        }
        public async Task <string> SendMessageAsync(string msg)
        {
            try
            {
                await SendHelloPacketIfNeededAsync().ConfigureAwait(false);

                var requestHex = initialPacket.BuildMessage(msg, _token);
                await _udpClient.SendAsync(requestHex.ToByteArray(), _endpoint).ConfigureAwait(false);

                var responseHex = (await _udpClient.ReceiveAsync().ConfigureAwait(false)).Buffer.ToHex();
                var miioPacket  = new MiioPacket(responseHex);

                return(miioPacket.GetResponseData(_token));
            }
            catch (TimeoutException)
            {
                throw _timeoutException;
            }
        }
        public string SendMessage(string msg)
        {
            try
            {
                SendHelloPacketIfNeeded();

                var requestHex = initialPacket.BuildMessage(msg, _token);
                _udpClient.SendTo(requestHex.ToByteArray(), _endpoint);

                var responseHex = _udpClient.Receive(ref _endpoint).ToHex();
                var miioPacket  = new MiioPacket(responseHex);

                return(miioPacket.GetResponseData(_token));
            }
            catch (TimeoutException)
            {
                throw _timeoutException;
            }
        }