diff --git a/docs/network_discovery.md b/docs/network_discovery.md new file mode 100644 index 00000000..dc17386b --- /dev/null +++ b/docs/network_discovery.md @@ -0,0 +1,187 @@ +--- +title: "Networking and Discovery" +sidebar_label: "Networking and Discovery" +--- + +Some integrations may need to discover devices on the network via [mDNS/Zeroconf](https://en.wikipedia.org/wiki/Zero-configuration_networking), [SSDP](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol), or another method once they have been enabled. The primary use case is to find devices that do not have a known fixed IP Address or for integrations that can dynamically add and remove any number of compatible discoverable devices. + +Home Assistant has built-in helpers to support mDNS/Zeroconf and SSDP. If your integration uses another discovery method that needs to determine which network interfaces to use to broadcast traffic, the [Network](https://www.home-assistant.io/integrations/network/) integration provides a helper API to access the user's interface preferences. + +## mDNS/Zeroconf + +Home Assistant uses the [python-zeroconf](https://github.com/jstasiak/python-zeroconf) package for mDNS support. As running multiple mDNS implementations on a single host is not recommended, Home Assistant provides internal helper APIs to access the running `Zeroconf` and `AsyncZeroconf` instances. + +Before using these helpers, be sure to add `zeroconf` to `dependencies` in your integration's [`manifest.json`](creating_integration_manifest.md) + +### Obtaining the `AsyncZeroconf` object + +```python +from homeassistant.components import zeroconf + +... +aiozc = await zeroconf.async_get_async_instance(hass) + +``` + +### Obtaining the `Zeroconf` object + +```python +from homeassistant.components import zeroconf + +... +zc = await zeroconf.async_get_instance(hass) + +``` + +### Using the `AsyncZeroconf` and `Zeroconf` objects + +`python-zeroconf` provides examples on how to use both objects [examples](https://github.com/jstasiak/python-zeroconf/tree/master/examples). + +## SSDP + +Home Assistant provides built-in discovery via SSDP. + +Before using these helpers, be sure to add `ssdp` to `dependencies` in your integration's [`manifest.json`](creating_integration_manifest.md) + +### Obtaining the list of discovered devices + +The list of discovered SSDP devices can be obtained using the following built-in +helper APIs. The SSDP integration provides the following helper APIs to lookup existing +SSDP discoveries from the cache: `ssdp.async_get_discovery_info_by_udn_st`, `ssdp.async_get_discovery_info_by_st`, `ssdp.async_get_discovery_info_by_udn` + +### Looking up a specific device + +The `ssdp.async_get_discovery_info_by_udn_st` API returns a single `discovery_info` +or `None` when provided an `SSDP`, `UDN` and `ST`. + +``` +from homeassistant.components import ssdp + +... + +discovery_info = ssdp.async_get_discovery_info_by_udn_st(hass, udn, st) +``` + +### Looking up devices by `ST` + +If you want to look for a specific type of discovered devices, calling +`ssdp.async_get_discovery_info_by_st` will return a list of all discovered devices that +match the `SSDP` `ST`. The below example returns a list of discovery info for every +Sonos player discovered on the network. + +``` +from homeassistant.components import ssdp + +... + +discovery_infos = ssdp.async_get_discovery_info_by_st(hass, "urn:schemas-upnp-org:device:ZonePlayer:1") +for discovery_info in discovery_infos: + ... + +``` + + +### Looking up devices by `UDN` + +If you want to see a list of the services provided by a specific `UDN`, calling +`ssdp.async_get_discovery_info_by_udn` will return a list of all discovered devices that +match the `UPNP` `UDN`. + +``` +from homeassistant.components import ssdp + +... + +discovery_infos = ssdp.async_get_discovery_info_by_udn(hass, udn) +for discovery_info in discovery_infos: + ... + +``` + +### Subscribing to SSDP discoveries + +Some integrations may need to know when a device is discovered right away. The SSDP integration provides a registration API to receive callbacks when a new device is discovered that matches specific key values. The same format for `ssdp` in [`manifest.json`](creating_integration_manifest.md) is used for matching. + +The function `ssdp.async_register_callback` is provided to enable this ability. The function returns a callback that will cancel the registration when called. + +The below example shows registering to get callbacks when a Sonos player is seen +on the network. + +``` +from homeassistant.components import ssdp + +... + +entry.async_on_unload( + ssdp.async_register_callback( + hass, _async_discovered_player, {"st": "urn:schemas-upnp-org:device:ZonePlayer:1"} + ) +) +``` + +## Network + +For integrations that use a discovery method that is not built-in and need to access the user's network adapter configuration, the following helper API should be used. + + +```python +from homeassistant.components import network + +... +adapters = await network.async_get_adapters(hass) +``` + +### Example `async_get_adapters` data structure + +```python +[ + { + "auto": True, + "default": False, + "enabled": True, + "ipv4": [], + "ipv6": [ + { + "address": "2001:db8::", + "network_prefix": 8, + "flowinfo": 1, + "scope_id": 1, + } + ], + "name": "eth0", + }, + { + "auto": True, + "default": False, + "enabled": True, + "ipv4": [{"address": "192.168.1.5", "network_prefix": 23}], + "ipv6": [], + "name": "eth1", + }, + { + "auto": False, + "default": False, + "enabled": False, + "ipv4": [{"address": "169.254.3.2", "network_prefix": 16}], + "ipv6": [], + "name": "vtun0", + }, +] +``` + +### Obtaining the IP Network from an adapter + +```python +from ipaddress import ip_network +from homeassistant.components import network + +... + +adapters = await network.async_get_adapters(hass) + +for adapter in adapters: + for ip_info in adapater["ipv4"]: + local_ip = ip_info["address"] + network_prefix = ip_info["network_prefix"] + ip_net = ip_network(f"{local_ip}/{network_prefix}", False) +``` diff --git a/sidebars.js b/sidebars.js index f3a618a0..9b78580e 100644 --- a/sidebars.js +++ b/sidebars.js @@ -128,6 +128,7 @@ module.exports = { "creating_component_generic_discovery", "integration_fetching_data", "integration_events", + "network_discovery", ], "Building Entity Integrations": [ "reproduce_state_index",