Network discovery
Use the zeroconf, SSDP, DHCP, USB, MQTT, and network APIs of Home Assistant to find devices while your integration runs.
Discovery through the manifest starts a config flow for new devices. Some integrations need more: a hub that adds devices as they appear, or an integration that looks up where a known device is now. Home Assistant shares its discovery integrations through the APIs on this page, so your integration does not need to run its own scanner.
Add the integration you use, like zeroconf or ssdp, to the dependencies in your manifest.
Home Assistant runs one mDNS implementation, python-zeroconf, and shares it. Running several mDNS implementations on one host is not recommended, so never start your own. Get the shared instance instead, and pass it to your library:
from homeassistant.components import zeroconf
aiozc = await zeroconf.async_get_async_instance(hass)Use zeroconf.async_get_instance(hass) for the synchronous Zeroconf object. The examples of python-zeroconf show what you can do with both.
The SSDP integration keeps a cache of the devices it found. Look them up with:
| Function | Returns |
|---|---|
ssdp.async_get_discovery_info_by_udn_st(hass, udn, st) |
The discovery info for one UDN and ST, or None |
ssdp.async_get_discovery_info_by_st(hass, st) |
A list of everything found with this ST |
ssdp.async_get_discovery_info_by_udn(hass, udn) |
A list of all services of one device |
from homeassistant.components import ssdp
discovery_infos = await ssdp.async_get_discovery_info_by_st(
hass, "urn:schemas-upnp-org:device:ZonePlayer:1"
)To hear about new devices right away, register a callback with ssdp.async_register_callback. It matches with the same format as ssdp in the manifest, and MATCH_ALL from homeassistant.const matches any value of a header:
from homeassistant.components import ssdp
entry.async_on_unload(
await ssdp.async_register_callback(
hass,
_async_player_discovered,
{"st": "urn:schemas-upnp-org:device:ZonePlayer:1"},
)
)dhcp.async_discovered_service_info returns the devices that are still in the DHCP cache, as DhcpServiceInfo objects with ip, hostname, and macaddress. The hostname is lowercase, and the MAC address is lowercase without colons: AA:BB:CC:12:34:56 becomes aabbcc123456.
from homeassistant.components import dhcp
for service_info in dhcp.async_discovered_service_info(hass):
...The USB integration looks for new devices when Home Assistant starts, when the user opens the integrations page, and when a device is plugged in, if the system supports that. To check for new compatible devices at the same moments, register a callback:
from homeassistant.components import usb
from homeassistant.core import callback
@callback
def _async_check_for_usb() -> None:
"""Check for new compatible USB devices."""
entry.async_on_unload(
usb.async_register_scan_request_callback(hass, _async_check_for_usb)
)Integrations that use MQTT have to wait until the MQTT client is available before they subscribe. async_wait_for_mqtt_client waits while MQTT is being set up. It returns True when the client is available, and False when MQTT is not set up, or not available within a timeout. The client does not have to be connected yet.
from homeassistant.components import mqtt
if not await mqtt.async_wait_for_mqtt_client(hass):
raise ConfigEntryNotReady("MQTT is not available")If your integration uses its own discovery method, it needs to know which network interfaces to use. The Network integration holds the preferences of the user. Ask it, instead of looking at the interfaces yourself:
from homeassistant.components import network
adapters = await network.async_get_adapters(hass)Each adapter is a dictionary like this:
{
"name": "eth1",
"enabled": True,
"auto": True,
"default": False,
"ipv4": [{"address": "192.168.1.5", "network_prefix": 23}],
"ipv6": [],
}Only use adapters that are enabled. To get the network of an address:
from ipaddress import ip_network
for adapter in adapters:
if not adapter["enabled"]:
continue
for ip_info in adapter["ipv4"]:
network_range = ip_network(
f"{ip_info['address']}/{ip_info['network_prefix']}", False
)