使用UPnP.FindByType查找Belkin WeMo设备

时间:2014-08-22 17:38:00

标签: c# upnp

我正在尝试创建一个用于控制WeMo设备的C#应用​​。我已按照Bernacules here提供的代码示例和说明进行操作。

相关的代码位粘贴在下面。

private void GetAllDevices()
{
    var finder = new UPnPDeviceFinder();
    var foundDevices = new List<UPnPDevice>();

    var deviceType = "upnp:rootdevice";
    var devices = finder.FindByType(deviceType, 1);

    foreach (UPnPDevice upnpDevice in devices)
    {
        if (upnpDevice.Type.StartsWith("urn:Belkin:"))
        {
            ....
        }
    }
}

当我迭代UPnPDeviceFinder找到的设备时,我想出了各种各样的东西,我的媒体服务器,在我的Android手机上运行的一些应用程序,我的Chromecast,但没有WeMos,甚至没有任何东西甚至在贝尔金钟中有贝尔金。

经过一番谷歌搜索后,我发现this article让我弄明白了如何查询&#39; setup.xml&#39;对于每个设备。通过替换&#34; upnp:rootdevice&#34;在上面的代码中,其值来自&lt; DeviceType&gt;在xml中(我相信它是&#34; urn:Belkin:device:controllee:1&#34;),我从UPnPDeviceFinder得到了0个结果。我猜测Belkin最近改变了他们的配置,但是有更多网络笨拙的人能够了解我可以用于什么价值的设备&#39; deviceType&#39;哪个会找到我的WeMos?

4 个答案:

答案 0 :(得分:3)

您使用的是哪个版本的Windows?

我的UPnP扫描程序在Windows 7上发现Wemo就好了,但它在Windows 8.1上不起作用

在Windows 8.1上,所有其他UPnP设备都会显示,但Wemo根本没有出现。

答案 1 :(得分:3)

我设法解决了问题。它有点hacky,并没有真正使用upnp协议(因为我无法在Windows 10上完全使用它)。

基本上,我们正在做的是利用netsh命令获取设备列表及其地址。然后我们尝试访问我们知道wemo设备可以运行的不同端口上的setup.xml。当我们得到正确的响应时,它包含所有upnp设备信息,然后我们可以解析我们想要了解的有关设备的任何信息。在这个示例中,我只是进行了简化的解析,只是检查它是否包含单词“Belkin”。它确实有点工作,但是我们应该解析UDN以确定模型,并将friendlyName显示给最终用户。否则,贝尔金制造的其他设备可能会出现。

此方法适用于Windows 10 pc。我没有尝试过其他平台。这是一种强力方法,但所有请求都是异步并行的,所以我们得到的响应很快。无论如何,这是完成所有魔术的课程:

public class WemoScanner
{
    public delegate void WemoDeviceFound(WemoDevice device);

    public event WemoDeviceFound OnWemoDeviceFound;

    public void StartScanning()
    {
        var addresses = GetAddresses().Where(a => a.Type == "Dynamic");
        var tasks = addresses.SelectMany(CheckWemoDevice);
        Task.Run(async () => { await Task.WhenAll(tasks).ConfigureAwait(false); });
    }

    public List<WemoDevice> GetWemoDevices()
    {
        var devices = new List<WemoDevice>();
        OnWemoDeviceFound += device => devices.Add(device);
        var addresses = GetAddresses().Where(a => a.Type == "Dynamic");
        var tasks = addresses.SelectMany(CheckWemoDevice);
        Task.WhenAll(tasks).GetAwaiter().GetResult();
        return devices;
    } 

    private NetshResult[] GetAddresses()
    {
        Process p = new Process();
        p.StartInfo.FileName = "netsh.exe";
        p.StartInfo.Arguments = "interface ip show ipnet";
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.Start();

        string output = p.StandardOutput.ReadToEnd();
        var lines =
            output.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)
                .SkipWhile(l => !l.StartsWith("--------"))
                .Skip(1);
        var results =
            lines.Select(l => new NetshResult(l.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)))
                .ToArray();
        return results;
    }
    private IEnumerable<Task> CheckWemoDevice(NetshResult netshResult)
    {
        var tasks = new List<Task>();
        for (uint i = 49150; i <= 49156; i++)
        {
            tasks.Add(CheckWemoDevice(netshResult.IpAddress, i));
        }
        return tasks;
    }

    private async Task CheckWemoDevice(string ip, uint port)
    {
        var url = $"http://{ip}:{port}/setup.xml";
        try
        {
            using (var wc = new WebClient())
            {
                var resp = await wc.DownloadStringTaskAsync(url);
                if (resp.Contains("Belkin")) // TODO: Parse upnp device data and determine device
                    OnWemoDeviceFound?.Invoke(new WemoDevice {Address = ip, Port = port});
            }
        }
        catch (Exception)
        {

        }
    }
}

以及一些模型类:

public class NetshResult
{
    public NetshResult(string[] columns)
    {
        PhysicalAddress = columns[0];
        IpAddress = columns[1];
        Type = columns[2];
        Interface = columns[3];
    }
    public string PhysicalAddress { get; private set; }
    public string IpAddress { get; private set; }
    public string Type { get; private set; }
    public string Interface { get; private set; }
}

public class WemoDevice
{
    public string Address { get; set; }
    public uint Port { get; set; }
}

用法很简单。如果您希望它以异步方式运行(在后台),只需连接到事件OnWemoDeviceFound并像这样调用StartScanning()

var scanner = new WemoScanner();
scanner.OnWemoDeviceFound += device => Console.WriteLine($"{device.Address}:{device.Port}");
scanner.StartScanning();

否则,如果您想要一个同步运行并返回设备列表的方法,只需调用GetWemoDevices()即可。请注意,这不是真正推荐的,因为在所有“无效”请求发生超时之前它不会返回。如果您希望缩短此时间,可以修改Web请求的超时。

答案 2 :(得分:0)

我知道这是一个非常老的线程,但是我无法在2018年使以上任何解决方案在Windows 10上正常工作。我可以结合使用@Inrego和.NET TcpClient类,检查端口49153是否打开。

        public IList<WemoDevice> GetWemoIPs()
        {
            var wemos = new List<WemoDevice>();

            Parallel.For(1, 255, (i) =>
            {
                var ip = string.Format(this.IpFormat, i);
                var device = new WemoDevice(ip);

                if (IsWemo(ip))
                    wemos.Add(device);
            });


            return wemos;
        }       

        public bool IsWemo(string ipAddress)
        {
            using (TcpClient tcpClient = new TcpClient())
            {
                try
                {
                    tcpClient.Connect(ipAddress, 49153);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
        }

答案 3 :(得分:-1)

使用属性upnp:rootdevice,Windows 10上也找不到WeMo。 Inrego你发布了“另一种方式”。其他明智的我正在考虑记录MAC,然后每次做一个ARP来查看IP地址是否已经改变,检查端口49152,49153,49154(据我所知他们可以改变)