core/homeassistant/components/zeroconf/__init__.py

102 lines
2.9 KiB
Python
Raw Normal View History

"""Support for exposing Home Assistant via Zeroconf."""
# PyLint bug confuses absolute/relative imports
# https://github.com/PyCQA/pylint/issues/1931
# pylint: disable=no-name-in-module
2016-04-10 22:34:04 +00:00
import logging
import ipaddress
import voluptuous as vol
from zeroconf import ServiceBrowser, ServiceInfo, ServiceStateChange, Zeroconf
from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, __version__)
from homeassistant.generated.zeroconf import ZEROCONF
2016-04-10 22:34:04 +00:00
_LOGGER = logging.getLogger(__name__)
2016-04-10 22:34:04 +00:00
DOMAIN = 'zeroconf'
ATTR_HOST = 'host'
ATTR_PORT = 'port'
ATTR_HOSTNAME = 'hostname'
ATTR_TYPE = 'type'
ATTR_NAME = 'name'
ATTR_PROPERTIES = 'properties'
2016-04-10 22:34:04 +00:00
ZEROCONF_TYPE = '_home-assistant._tcp.local.'
2016-04-10 22:34:04 +00:00
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({}),
}, extra=vol.ALLOW_EXTRA)
2016-04-10 22:34:04 +00:00
def setup(hass, config):
"""Set up Zeroconf and make Home Assistant discoverable."""
zeroconf_name = '{}.{}'.format(hass.config.location_name, ZEROCONF_TYPE)
2016-04-10 22:34:04 +00:00
params = {
'version': __version__,
'base_url': hass.config.api.base_url,
# always needs authentication
'requires_api_password': True,
}
2016-04-10 22:34:04 +00:00
info = ServiceInfo(ZEROCONF_TYPE, zeroconf_name,
port=hass.http.server_port, properties=params)
zeroconf = Zeroconf()
zeroconf.register_service(info)
2016-04-10 22:34:04 +00:00
def service_update(zeroconf, service_type, name, state_change):
"""Service state changed."""
if state_change is ServiceStateChange.Added:
service_info = zeroconf.get_service_info(service_type, name)
info = info_from_service(service_info)
_LOGGER.debug("Discovered new device %s %s", name, info)
for domain in ZEROCONF[service_type]:
hass.add_job(
hass.config_entries.flow.async_init(
domain, context={'source': DOMAIN}, data=info
)
)
for service in ZEROCONF:
ServiceBrowser(zeroconf, service, handlers=[service_update])
def stop_zeroconf(_):
"""Stop Zeroconf."""
zeroconf.unregister_service(info)
zeroconf.close()
2016-04-10 22:34:04 +00:00
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zeroconf)
2016-04-10 22:34:04 +00:00
return True
def info_from_service(service):
"""Return prepared info from mDNS entries."""
properties = {}
for key, value in service.properties.items():
try:
if isinstance(value, bytes):
value = value.decode('utf-8')
properties[key.decode('utf-8')] = value
except UnicodeDecodeError:
_LOGGER.warning("Unicode decode error on %s: %s", key, value)
address = service.address or service.address6
info = {
ATTR_HOST: str(ipaddress.ip_address(address)),
ATTR_PORT: service.port,
ATTR_HOSTNAME: service.server,
ATTR_TYPE: service.type,
ATTR_NAME: service.name,
ATTR_PROPERTIES: properties,
}
return info