From 65e53b825111531e675b0b615cec4dc2e89b11d5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 2 Sep 2020 20:16:21 -0500 Subject: [PATCH] Support reloading mqtt yaml configuration (#39531) --- homeassistant/components/mqtt/__init__.py | 14 ++++++ .../components/mqtt/alarm_control_panel.py | 4 ++ .../components/mqtt/binary_sensor.py | 4 ++ homeassistant/components/mqtt/camera.py | 4 ++ homeassistant/components/mqtt/climate.py | 4 ++ homeassistant/components/mqtt/cover.py | 4 ++ homeassistant/components/mqtt/fan.py | 4 ++ .../components/mqtt/light/__init__.py | 3 ++ homeassistant/components/mqtt/lock.py | 4 ++ homeassistant/components/mqtt/sensor.py | 4 ++ homeassistant/components/mqtt/services.yaml | 3 ++ homeassistant/components/mqtt/switch.py | 4 ++ .../components/mqtt/vacuum/__init__.py | 3 ++ tests/components/mqtt/test_light.py | 45 ++++++++++++++++++- tests/fixtures/mqtt/configuration.yaml | 4 ++ 15 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/mqtt/configuration.yaml diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 869dc27cfd6..d4263ee6ba3 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -136,6 +136,20 @@ CONNECTION_FAILED_RECOVERABLE = "connection_failed_recoverable" DISCOVERY_COOLDOWN = 2 TIMEOUT_ACK = 1 +PLATFORMS = [ + "alarm_control_panel", + "binary_sensor", + "camera", + "climate", + "cover", + "fan", + "light", + "lock", + "sensor", + "switch", + "vacuum", +] + def validate_device_has_at_least_one_identifier(value: ConfigType) -> ConfigType: """Validate that a device info entry has at least one identifying value.""" diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index 35c9d028c52..505c7616a0a 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -31,6 +31,7 @@ from homeassistant.const import ( from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, HomeAssistantType from . import ( @@ -39,6 +40,8 @@ from . import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, + DOMAIN, + PLATFORMS, MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @@ -100,6 +103,7 @@ async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None ): """Set up MQTT alarm control panel through configuration.yaml.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) await _async_setup_entity(config, async_add_entities) diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 9afb9401bfe..c9fd2bba2b1 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -24,6 +24,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect import homeassistant.helpers.event as evt from homeassistant.helpers.event import async_track_point_in_utc_time +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, HomeAssistantType from homeassistant.util import dt as dt_util @@ -31,6 +32,8 @@ from . import ( ATTR_DISCOVERY_HASH, CONF_QOS, CONF_STATE_TOPIC, + DOMAIN, + PLATFORMS, MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @@ -72,6 +75,7 @@ async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None ): """Set up MQTT binary sensor through configuration.yaml.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) await _async_setup_entity(config, async_add_entities) diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index d0311d5c694..82e5cb8b272 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -9,11 +9,14 @@ from homeassistant.const import CONF_DEVICE, CONF_NAME, CONF_UNIQUE_ID from homeassistant.core import callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, HomeAssistantType from . import ( ATTR_DISCOVERY_HASH, CONF_QOS, + DOMAIN, + PLATFORMS, MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @@ -46,6 +49,7 @@ async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None ): """Set up MQTT camera through configuration.yaml.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) await _async_setup_entity(config, async_add_entities) diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 20ee183d693..68579559e35 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -48,13 +48,16 @@ from homeassistant.const import ( from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, HomeAssistantType from . import ( ATTR_DISCOVERY_HASH, CONF_QOS, CONF_RETAIN, + DOMAIN, MQTT_BASE_PLATFORM_SCHEMA, + PLATFORMS, MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @@ -239,6 +242,7 @@ async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None ): """Set up MQTT climate device through configuration.yaml.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) await _async_setup_entity(hass, config, async_add_entities) diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 772c2900eed..b8a5b778a98 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -34,6 +34,7 @@ from homeassistant.const import ( from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, HomeAssistantType from . import ( @@ -42,6 +43,8 @@ from . import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, + DOMAIN, + PLATFORMS, MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @@ -170,6 +173,7 @@ async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None ): """Set up MQTT cover through configuration.yaml.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) await _async_setup_entity(config, async_add_entities) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index e70ff62b0fb..b1ec7aabeef 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -26,6 +26,7 @@ from homeassistant.const import ( from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, HomeAssistantType from . import ( @@ -34,6 +35,8 @@ from . import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, + DOMAIN, + PLATFORMS, MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @@ -111,6 +114,7 @@ async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None ): """Set up MQTT fan through configuration.yaml.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) await _async_setup_entity(config, async_add_entities) diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index fe33756e51e..2375fb86e5a 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -10,8 +10,10 @@ from homeassistant.components.mqtt.discovery import ( clear_discovery_hash, ) from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, HomeAssistantType +from .. import DOMAIN, PLATFORMS from .schema import CONF_SCHEMA, MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import PLATFORM_SCHEMA_BASIC, async_setup_entity_basic from .schema_json import PLATFORM_SCHEMA_JSON, async_setup_entity_json @@ -39,6 +41,7 @@ async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None ): """Set up MQTT light through configuration.yaml.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) await _async_setup_entity(hass, config, async_add_entities) diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index 744eeb17e6f..f6d56a30431 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -15,6 +15,7 @@ from homeassistant.const import ( from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, HomeAssistantType from . import ( @@ -23,6 +24,8 @@ from . import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, + DOMAIN, + PLATFORMS, MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @@ -73,6 +76,7 @@ async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None ): """Set up MQTT lock panel through configuration.yaml.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) await _async_setup_entity(config, async_add_entities) diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index f2cb8f22e84..eb241bba7a0 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -22,6 +22,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_point_in_utc_time +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.typing import ConfigType, HomeAssistantType from homeassistant.util import dt as dt_util @@ -29,6 +30,8 @@ from . import ( ATTR_DISCOVERY_HASH, CONF_QOS, CONF_STATE_TOPIC, + DOMAIN, + PLATFORMS, MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @@ -66,6 +69,7 @@ async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None ): """Set up MQTT sensors through configuration.yaml.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) await _async_setup_entity(config, async_add_entities) diff --git a/homeassistant/components/mqtt/services.yaml b/homeassistant/components/mqtt/services.yaml index 2af3c22fe50..04dce23f5de 100644 --- a/homeassistant/components/mqtt/services.yaml +++ b/homeassistant/components/mqtt/services.yaml @@ -35,3 +35,6 @@ dump: description: how long we should listen for messages in seconds example: 5 default: 5 + +reload: + description: Reload all mqtt entities from yaml. diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index 8a164019f10..364f060ac14 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -19,6 +19,7 @@ from homeassistant.const import ( from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, HomeAssistantType @@ -28,6 +29,8 @@ from . import ( CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, + DOMAIN, + PLATFORMS, MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, @@ -69,6 +72,7 @@ async def async_setup_platform( hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None ): """Set up MQTT switch through configuration.yaml.""" + await async_setup_reload_service(hass, DOMAIN, PLATFORMS) await _async_setup_entity(config, async_add_entities, discovery_info) diff --git a/homeassistant/components/mqtt/vacuum/__init__.py b/homeassistant/components/mqtt/vacuum/__init__.py index b16ec7aaf74..b954a97e8f9 100644 --- a/homeassistant/components/mqtt/vacuum/__init__.py +++ b/homeassistant/components/mqtt/vacuum/__init__.py @@ -10,7 +10,9 @@ from homeassistant.components.mqtt.discovery import ( ) from homeassistant.components.vacuum import DOMAIN from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.reload import async_setup_reload_service +from .. import DOMAIN as MQTT_DOMAIN, PLATFORMS from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE from .schema_legacy import PLATFORM_SCHEMA_LEGACY, async_setup_entity_legacy from .schema_state import PLATFORM_SCHEMA_STATE, async_setup_entity_state @@ -31,6 +33,7 @@ PLATFORM_SCHEMA = vol.All( async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up MQTT vacuum through configuration.yaml.""" + await async_setup_reload_service(hass, MQTT_DOMAIN, PLATFORMS) await _async_setup_entity(config, async_add_entities, discovery_info) diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index d83cd7fda7f..56a5b4012c8 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -153,11 +153,14 @@ light: payload_off: "off" """ +from os import path + import pytest +from homeassistant import config as hass_config from homeassistant.components import light, mqtt from homeassistant.components.mqtt.discovery import async_start -from homeassistant.const import ATTR_ASSUMED_STATE, STATE_OFF, STATE_ON +from homeassistant.const import ATTR_ASSUMED_STATE, SERVICE_RELOAD, STATE_OFF, STATE_ON import homeassistant.core as ha from homeassistant.setup import async_setup_component @@ -1558,3 +1561,43 @@ async def test_max_mireds(hass, mqtt_mock): state = hass.states.get("light.test") assert state.attributes.get("min_mireds") == 153 assert state.attributes.get("max_mireds") == 370 + + +async def test_reloadable(hass, mqtt_mock): + """Test reloading an mqtt light.""" + config = { + light.DOMAIN: { + "platform": "mqtt", + "name": "test", + "command_topic": "test/set", + } + } + + assert await async_setup_component(hass, light.DOMAIN, config) + await hass.async_block_till_done() + + assert hass.states.get("light.test") + assert len(hass.states.async_all()) == 1 + + yaml_path = path.join( + _get_fixtures_base_path(), + "fixtures", + "mqtt/configuration.yaml", + ) + with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path): + await hass.services.async_call( + "mqtt", + SERVICE_RELOAD, + {}, + blocking=True, + ) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 1 + + assert hass.states.get("light.test") is None + assert hass.states.get("light.reload") + + +def _get_fixtures_base_path(): + return path.dirname(path.dirname(path.dirname(__file__))) diff --git a/tests/fixtures/mqtt/configuration.yaml b/tests/fixtures/mqtt/configuration.yaml new file mode 100644 index 00000000000..96c7e57f72b --- /dev/null +++ b/tests/fixtures/mqtt/configuration.yaml @@ -0,0 +1,4 @@ +light: + - platform: mqtt + name: reload + command_topic: "test/set"