8

I'm trying to create a C# app for controlling WeMo devices. I've followed the code samples and directions given by Bernacules here.

The relevant bit of code is pasted below.

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:"))
        {
            ....
        }
    }
}

When I iterate the devices found by UPnPDeviceFinder, I come up with all sorts of things, my media server, some app running on my Android phone, my Chromecast, but no WeMos, nothing that even remotely had Belkin in the urn.

After a bit more Googling, I found this article which allowed me to figure out how to query the 'setup.xml' for each device. By replacing the "upnp:rootdevice" in the code above with the value from <DeviceType> in the xml (I believe it was "urn:Belkin:device:controllee:1"), I came up with 0 results from the UPnPDeviceFinder. I'm guessing Belkin has recently changed their configuration, but can someone with a bit more network saaviness clue me in on what value I can use for 'deviceType' which will find my WeMos?

Duck Jones
  • 380
  • 1
  • 5
  • 16

4 Answers4

4

What version of windows are you using?

My UPnP scanner finds the Wemo just fine on Windows 7 but it doesn't work on Windows 8.1

On Windows 8.1 all my other UPnP devices show up, but the Wemo does not appear at all.

  • 1
    Holy smokes, you nailed it. I've been using Windows 8.1. I ran the code on a Windows 7 machine and it detected the WeMos just fine. At least now I know it's something related to the UPNPLib.dll and not a change in the WeMo firmware. Now that begs the question... is there a workaround? – Duck Jones Sep 12 '14 at 15:11
  • Sorry to bump such an old post. But have anyone gotten this to work on 8.1? – Inrego Jun 10 '15 at 22:13
  • 1
    I found another way! I will write up a small library to post on GitHub and get back here with a link. – Inrego Jun 11 '15 at 15:35
  • 2
    @Inrego have you posted it on GitHub yet? – jLynx Nov 09 '15 at 19:26
  • I feel the urge to bump! Inrego have you posted it? – Charles Feb 02 '16 at 03:54
  • @DarkN3ss I haven't posted to GitHub yet, but I have provided an answer here on the question that contains a full code sample for a solution to the problem. It's a little hacky, and a completely other approach that I posted about last year, but the old method stopped working in Windows 10. – Inrego Apr 01 '16 at 16:14
  • @Charles check out my reply to DarkN3ss above, and see the answer I submitted to this question. – Inrego Apr 01 '16 at 16:15
  • @DuckJones I believe Bernacules stated in his YouTube video that there was an issue with the uPnp library in Windows and he had to use another route. – The Muffin Man May 05 '16 at 17:26
4

I managed to make a workaround. It's a little hacky, and doesn't really use the upnp protocol (as I was unable to get that working at all on Windows 10).

Basically, what we're doing is making use of a netsh command to get a list of devices, and their addresses. Then we try to access the setup.xml on the different ports that we know the wemo devices can run on. When we get a proper response, it contains all the upnp device info, which we can then parse for anything we'd like to know about the device. In this sample, I just made a simplified parsing, just checking if it contains the word "Belkin". It does kind of work, but we should be parsing the UDN to determine model, and friendlyName to show to the end-user. Else, other devices made by Belkin might show up.

This method does work for me on a Windows 10 pc. I have not tried other platforms. It's kind of a brute-force method, but all requests are made async and in parallel, so we get a response rather quickly. Anyway, here's the class that does all the magic:

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)
        {

        }
    }
}

And a few model classes:

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; }
}

Usage is fairly simple. If you want it to run in an async manner (in the background), simply hook up to event OnWemoDeviceFound and call StartScanning() like so:

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

Else, if you want a method that runs synchronously and returns a list of devices, just call GetWemoDevices(). Mind you, that this isn't really recommended, as it will not return until a timeout has happened on all the "invalid" requests. You could modify the timeout for the web requests if you wanted to make this time shorter.

Inrego
  • 1,524
  • 1
  • 15
  • 25
  • Okay took a while for me to try this. I agree it's a bit hackish. But hey... You've succeeded where no one else did. Good job, it works. – Charles Jul 02 '16 at 07:38
  • Sorry for the double post, it is too late to edit the previous one. Here's two things that could help the next guy. 1) You may add p.StartInfo.CreateNoWindow = true; to not have a flickering netsh.exe window. 2) Results from netsh.exe are translated to the OS language, You may want to also check your language. For instance what I did was : var addresses = GetAddresses().Where(a => a.Type == "Dynamic" || a.Type == "Dynamique"); To support both french and english (My OS is in french) – Charles Jul 03 '16 at 06:04
0

I know this is a very old thread, but I had issues getting any of the above solutions working on Windows 10 in 2018. I was able to get it going on Windows 10 with a combination of the code from @Inrego and the .NET TcpClient class, checking for an open port 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;
                }
            }
        }
Steve Danner
  • 21,818
  • 7
  • 41
  • 51
-1

With the attribute upnp:rootdevice the WeMo is not found on Windows 10 either. Inrego did you post your "another way". Other wise I'm thinking of recording the MAC, then doing an ARP each time to see if the IP Address has changed, the checking ports 49152, 49153, 49154 (as I understand they can change)

MWiesner
  • 8,868
  • 11
  • 36
  • 70
jmone
  • 1
  • 2
  • Unfortunately, the "other way" that I found, stopped working in Windows 10 (it worked in 8.1). Instead, I found a kind of hacky/bruteforcy way to discover Wemo devices. See the answer I submitted today. – Inrego Apr 01 '16 at 16:25