"""Support for a local MQTT broker.""" import asyncio import logging import tempfile import voluptuous as vol from hbmqtt.broker import Broker, BrokerException from passlib.apps import custom_app_context from homeassistant.const import EVENT_HOMEASSISTANT_STOP import homeassistant.helpers.config_validation as cv from .const import PROTOCOL_311 _LOGGER = logging.getLogger(__name__) # None allows custom config to be created through generate_config HBMQTT_CONFIG_SCHEMA = vol.Any( None, vol.Schema( { vol.Optional("auth"): vol.Schema( {vol.Optional("password-file"): cv.isfile}, extra=vol.ALLOW_EXTRA ), vol.Optional("listeners"): vol.Schema( {vol.Required("default"): vol.Schema(dict), str: vol.Schema(dict)} ), }, extra=vol.ALLOW_EXTRA, ), ) @asyncio.coroutine def async_start(hass, password, server_config): """Initialize MQTT Server. This method is a coroutine. """ passwd = tempfile.NamedTemporaryFile() gen_server_config, client_config = generate_config(hass, passwd, password) try: if server_config is None: server_config = gen_server_config broker = Broker(server_config, hass.loop) yield from broker.start() except BrokerException: _LOGGER.exception("Error initializing MQTT server") return False, None finally: passwd.close() @asyncio.coroutine def async_shutdown_mqtt_server(event): """Shut down the MQTT server.""" yield from broker.shutdown() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_shutdown_mqtt_server) return True, client_config def generate_config(hass, passwd, password): """Generate a configuration based on current Home Assistant instance.""" config = { "listeners": { "default": { "max-connections": 50000, "bind": "0.0.0.0:1883", "type": "tcp", }, "ws-1": {"bind": "0.0.0.0:8080", "type": "ws"}, }, "auth": {"allow-anonymous": password is None}, "plugins": ["auth_anonymous"], "topic-check": {"enabled": True, "plugins": ["topic_taboo"]}, } if password: username = "homeassistant" # Encrypt with what hbmqtt uses to verify passwd.write( "homeassistant:{}\n".format(custom_app_context.encrypt(password)).encode( "utf-8" ) ) passwd.flush() config["auth"]["password-file"] = passwd.name config["plugins"].append("auth_file") else: username = None client_config = ("localhost", 1883, username, password, None, PROTOCOL_311) return config, client_config