private IEnumerable <UpnpAction> GetActions(string url)
        {
            var document = new XmlDocument();

            document.Load(url);
            var namespaceManager = new XmlNamespaceManager(document.NameTable);

            namespaceManager.AddNamespace("upnp", "urn:schemas-upnp-org:service-1-0");

            var actions = document.SelectNodes("//upnp:actionList/upnp:action", namespaceManager)?.OfType <XmlNode>().ToList();

            if (actions == null)
            {
                yield break;
            }

            foreach (var action in actions)
            {
                var childNodes = action.ChildNodes.OfType <XmlNode>().ToList();
                var name       = childNodes.SingleOrDefault(n => n.Name.Equals("name"))?.InnerText;
                var arguments  = childNodes.SingleOrDefault(n => n.Name.Equals("argumentList"))?.ChildNodes;
                var dto        = new UpnpAction {
                    Name = name
                };

                if (arguments == null)
                {
                    yield return(dto);

                    continue;
                }

                foreach (var argument in arguments.Cast <XmlNode>().Where(n => n.HasChildNodes))
                {
                    var argumentChildNodes = argument.ChildNodes.Cast <XmlNode>().ToList();
                    var argumentName       = argumentChildNodes.SingleOrDefault(n => n.Name.Equals("name"))?.InnerText;
                    var direction          = argumentChildNodes.SingleOrDefault(n => n.Name.Equals("direction"))?.InnerText;

                    if ("in".Equals(direction))
                    {
                        dto.InputArguments.Add(argumentName);
                    }
                    else if ("out".Equals(direction))
                    {
                        dto.OutputArguments.Add(argumentName);
                    }
                }

                yield return(dto);
            }
        }
        public async Task<Dictionary<string, string>> ExecuteAsync(SonosDevice device, UpnpService service, UpnpAction action, Dictionary<string, string> values)
        {
            var uri = new Uri($"http://{device.IpAddress}:1400{service.ControlUrl}");
            var soapAction = $"{service.Id}#{action.Name}";

            var body = new StringBuilder();
            body.Append($"<u:{action.Name} xmlns:u=\"{service.Type}\">");

            foreach (var argument in action.InputArguments)
            {
                string value;
                if (values.TryGetValue(argument, out value))
                {
                    body.Append($"<{argument}>{value}</{argument}>");
                }
            }
            body.Append($"</u:{action.Name}>");

            var result = new Dictionary<string, string>();

            try
            {
                var document = await PostRequestInternalAsync(uri, soapAction, body.ToString());

                foreach (var argument in action.OutputArguments)
                {
                    var value = document.SelectSingleNode("//" + argument)?.InnerText;
                    result.Add(argument, value);
                }
            }
            catch (WebException e)
            {
                _log.Error($"Unable to execute action {action.Name} for device {device.Name}: {e.Message}");
            }
            catch (Exception e)
            {
                _log.Error($"Unable to execute action {action.Name} for device {device.Name}: {e.Message}", e);
            }

            return result;
        }
        public async Task <Dictionary <string, string> > ExecuteAsync(SonosDevice device, UpnpService service, UpnpAction action, Dictionary <string, string> values)
        {
            var uri        = new Uri($"http://{device.IpAddress}:1400{service.ControlUrl}");
            var soapAction = $"{service.Id}#{action.Name}";

            var body = new StringBuilder();

            body.Append($"<u:{action.Name} xmlns:u=\"{service.Type}\">");

            foreach (var argument in action.InputArguments)
            {
                string value;
                if (values.TryGetValue(argument, out value))
                {
                    body.Append($"<{argument}>{value}</{argument}>");
                }
            }
            body.Append($"</u:{action.Name}>");

            var result = new Dictionary <string, string>();

            try
            {
                var document = await PostRequestInternalAsync(uri, soapAction, body.ToString());

                foreach (var argument in action.OutputArguments)
                {
                    var value = document.SelectSingleNode("//" + argument)?.InnerText;
                    result.Add(argument, value);
                }
            }
            catch (WebException e)
            {
                _log.Error($"Unable to execute action {action.Name} for device {device.Name}: {e.Message}");
            }
            catch (Exception e)
            {
                _log.Error($"Unable to execute action {action.Name} for device {device.Name}: {e.Message}", e);
            }

            return(result);
        }
        private IEnumerable<UpnpAction> GetActions(string url)
        {
            var document = new XmlDocument();
            document.Load(url);
            var namespaceManager = new XmlNamespaceManager(document.NameTable);
            namespaceManager.AddNamespace("upnp", "urn:schemas-upnp-org:service-1-0");

            var actions = document.SelectNodes("//upnp:actionList/upnp:action", namespaceManager)?.OfType<XmlNode>().ToList();

            if (actions == null)
            {
                yield break;
            }

            foreach (var action in actions)
            {
                var childNodes = action.ChildNodes.OfType<XmlNode>().ToList();
                var name = childNodes.SingleOrDefault(n => n.Name.Equals("name"))?.InnerText;
                var arguments = childNodes.SingleOrDefault(n => n.Name.Equals("argumentList"))?.ChildNodes;
                var dto = new UpnpAction {Name = name};

                if (arguments == null)
                {
                    yield return dto;
                    continue;
                }

                foreach (var argument in arguments.Cast<XmlNode>().Where(n => n.HasChildNodes))
                {
                    var argumentChildNodes = argument.ChildNodes.Cast<XmlNode>().ToList();
                    var argumentName = argumentChildNodes.SingleOrDefault(n => n.Name.Equals("name"))?.InnerText;
                    var direction = argumentChildNodes.SingleOrDefault(n => n.Name.Equals("direction"))?.InnerText;

                    if ("in".Equals(direction))
                    {
                        dto.InputArguments.Add(argumentName);
                    }
                    else if ("out".Equals(direction))
                    {
                        dto.OutputArguments.Add(argumentName);
                    }
                }

                yield return dto;
            }
        }