private async Task SendDiscoveryRequestAsync()
        {
            using (UdpClient client = new UdpClient(PORT_NUMBER)
            {
                EnableBroadcast = true
            })
            {
                var discoveryJson = JObject.FromObject(new
                {
                    system = new { get_sysinfo = (object)null },
                    emeter = new { get_realtime = (object)null }
                }).ToString(Newtonsoft.Json.Formatting.None);
                var discoveryPacket = SmartHomeProtocolEncoder.Encrypt(discoveryJson).ToArray();

                var bytes = discoveryPacket.Skip(4).ToArray();

                int sentBytes = await client.SendAsync(bytes, bytes.Length, new IPEndPoint(IPAddress.Broadcast, PORT_NUMBER)).ConfigureAwait(false);

                //int sentBytes = client.Send(bytes, bytes.Length, new IPEndPoint(IPAddress.Broadcast, PORT_NUMBER));

                // no bytes sents
                if (sentBytes == 0)
                {
                    throw new InvalidOperationException();
                }
            }
        }
        private async Task ReceiveAsync(int waitTimeOutInMs)
        {
            if (discoveryComplete) //Prevent ObjectDisposedException/NullReferenceException when the Close() function is called
            {
                return;
            }

            UdpClient udpListener = new UdpClient(PORT_NUMBER)
            {
                EnableBroadcast = true
            };

            // set timeout
            //udpListener.Client.ReceiveTimeout = Math.Max(waitTimeOutInMs, 100);
            //IPEndPoint ip = default;
            //byte[] buffer = udpListener.Receive(ref ip);

            UdpReceiveResult response = await udpListener.ReceiveAsync().ConfigureAwait(false);

            IPEndPoint ip      = response.RemoteEndPoint;
            var        message = Encoding.ASCII.GetString(SmartHomeProtocolEncoder.Decrypt(response.Buffer));

            try
            {
                TPLinkSmartDevice device   = null;
                dynamic           sys_info = ((dynamic)JObject.Parse(message)).system.get_sysinfo;
                string            model    = (string)sys_info.model;

                if (model != null)
                {
                    if (model.StartsWith("HS110", StringComparison.OrdinalIgnoreCase))
                    {
                        device = new TPLinkSmartMeterPlug(ip.Address.ToString());
                    }
                    else if (model.StartsWith("HS", StringComparison.OrdinalIgnoreCase))
                    {
                        device = new TPLinkSmartPlug(ip.Address.ToString());
                    }
                    else if (model.StartsWith("KL", StringComparison.OrdinalIgnoreCase) || model.StartsWith("LB", StringComparison.OrdinalIgnoreCase))
                    {
                        device = await TPLinkKL130.CreateNew(ip.Address.ToString());
                    }

                    // new device found, store in list and raise event
                    if (device != null)
                    {
                        DiscoveredDevices.Add(device);
                        OnDeviceFound(device);
                    }
                }
            }
            catch (RuntimeBinderException ex)
            {
                //discovered wrong device
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                // ensure the socket is closed even if exception happenned
                udpListener.Dispose();
            }

            discoveryComplete = true;
        }