Discovery

Finding devices on a user's network has been made easier by the introduction of ManagerDiscovery since Homey v2.5.0.

Currently Homey can automatically find devices on the user's Wi-Fi network using mDNS-SD, SSDP and MAC.

Choosing a discovery strategy

Find out which discovery strategy your device uses. Most devices nowadays use mDNS-SD. On macOS, you can use Discovery and Bonjour browser for Windows.

For other protocols, refer to your device's API documentation.

Discovery Types

mDNS-SD

Multicast-DNS Service Discovery is a widely used protocol to find devices on a network. It is also known as Avahi or Bonjour. A service broadcasts under a name (as specified by the manufacturer) and a protocol (tcp or udp).

For example, Homey broadcasts with the name homey using the tcp protocol.

A DiscoveryResultMDNSSD has a txt property that contains the (lowercased) TXT values of the broadcast.

/app.json

{
  ...
  "discovery": {
    "nanoleaf-aurora": {
      "type": "mdns-sd",
      "mdns-sd": {
        "name": "nanoleafapi",
        "protocol": "tcp"
      },
      "id": "{{txt.id}}",
      "conditions": [
        [
          {
            "field": "txt.md",
            "match": {
              "type": "string",
              "value": "NL22"
            }
          }
        ]
      ]
    }
  }
}

SSDP

Devices using the Simple Service Discovery Protocol can be found by specifying a search property.

A DiscoveryResultSSDP has a headers property that contains the (lowercased) headers of the response.

/app.json

{
  ...
  "discovery": {
    "denon-heos": {
      "type": "ssdp",
      "ssdp": {
        "search": "urn:schemas-denon-com:device:ACT-Denon:1"
      },
      "id": "{{headers.usn}}",
      "conditions": [
        [
          {
            "field": "headers.st",
            "match": {
              "type": "string",
              "value": "urn:schemas-denon-com:device:ACT-Denon:1"
            }
          }
        ]
      ]
    }
  }
}

MAC

MAC Address discovery works by specifying the first 3 bytes of a network device's MAC address. These first three bytes are reserved for the manufacturer and can thus be used to find a device on the network using Address Resolution Protocol (ARP).

A DiscoveryResultMAC only has an address property, which contains the IP address of the device.

For example, to find a device with a MAC address that starts with 00:24:6d or 00:24:6e:

/app.json

{
  ...
  "discovery": {
    "weinzierl": {
      "type": "mac",
      "mac": {
        "manufacturer": [
           [ 0, 36, 109 ],
           [ 0, 36, 110 ]
       ]
      }
    }
  }
}

Note that the MAC address must be specified in decimal numbers.

Defining your discovery strategy

The Discovery Result

Depending on your discovery type, some properties are available to match on. For example, a mDNS-SD discovery type has a txt object and the SSDP discovery type has an headers object.

Discovery Result ID

For the mdns-sd and ssdp discovery types, the app must define how a DiscoveryResult can be identified when it has been found multiple times, regardless if the IP address has changed.

For the mac discovery type, the mac address is used as the ID.

Find a unique and consistent property in the DiscoveryResult and define it as id in the /app.json. Homey will then be able to match the result to previous results, and notify your app the device has been found again, instead of seeing the device as a new discovery result.

Examples:

"id": "{{txt.id}}"
"id": "{{headers.uuid}}"

All properties available in the DiscoveryResult are available between double curly braces ({{ and }}).

Discovery Result Conditions

A discovery strategy can have a set of conditions that must be true before the result is sent to the app.

The conditions property is an Array with one or more Arrays in it. This array contains Objects rules. When all rules within an array are true, the result is considered a match. Using multiple arrays behaves as rulesArray1 OR rulesArray2 OR ....

There are two match types available: string and regex.

"conditions": [
  [
    {
      "field": "txt.md",
      "match": {
        "type": "string",
        "value": "NL29"
      }
    },
    // AND:
    {
      "field": "txt.version",
      "match": {
        "type": "string",
        "value": "1"
      }
    }
  ],
  // OR:
  [
    {
      "field": "txt.md",
      "match": {
        "type": "regex",
        "value": "NL\\d\\d" // double slashes because of JSON
      }
    }    
  ]
]

Conditions are matched case-insensitive.

Using discovery with a Driver

The recommended way to use Homey's built-in discovery is to link a discovery strategy to a Driver.

To do so, add the discovery property to your driver's entry in /app.json.

For example:

/app.json

{
  "discovery": {
    "my_discovery": {
      "type": "mdns-sd",
      "mdns-sd": {
        "protocol": "tcp",
        "name": "my_service"
      }
    }
  },
  "drivers": [
    {
      "id": "my_driver",
      "discovery": "my_discovery",
      ...
    }
  ]

In your driver.js, you can call this.getDiscoveryStrategy() to get the current strategy.

/drivers/my_driver/driver.js

module.exports = class MyDriver extends Homey.Driver {
  onPairListDevices(data, callback) {
    const discoveryStrategy = this.getDiscoveryStrategy();
    const discoveryResults = discoveryStrategy.getDiscoveryResults();
    
    const devices = Object.values(discoveryResults).map(discoveryResult => {
      return {
        name: discoveryResult.txt.name,
        data: {
          id: discoveryResult.id,
        }
      };
    });
    callback(null, devices);
  }
}

In your device.js, overload the methods starting with onDiscovery.

module.exports = class MyDevice extends Homey.Device {
  
  onDiscoveryResult(discoveryResult) {
    // Return a truthy value here if the discovery result matches your device.
    return discoveryResult.id === this.getData().id;
  }
  
  async onDiscoveryAvailable(discoveryResult) {
    // This method will be executed once when the device has been found (onDiscoveryResult returned true)
    this.api = new MyDeviceAPI(discoveryResult.address);
    await this.api.connect(); // When this throws, the device will become unavailable.
  }
  
  onDiscoveryAddressChanged(discoveryResult) {
    // Update your connection details here, reconnect when the device is offline
  }
  
  onDiscoveryLastSeenChanged(discoveryResult) {
    // When the device is offline, try to reconnect here
  }
}

Using discovery standalone

Simply call ManagerDiscovery.getDiscoveryStrategy() with the discovery strategy id as defined in your /app.json.

const discoveryStrategy = ManagerDiscovery.getDiscoveryStrategy('my_strategy');
discoveryStrategy.on('result', discoveryResult => {
  console.log('Got result:', discoveryResult);
});
discoveryStrategy.getDiscoveryResults(); // { "my_result_id": DiscoveryResult }