From 41963ef516c8fae0290a4f7853ed47270fc76303 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Wed, 11 May 2022 14:13:22 +0000 Subject: [PATCH 01/31] Processing yaml config through entry setup --- homeassistant/components/mqtt/__init__.py | 48 ++++++++++++++++++- homeassistant/components/mqtt/const.py | 2 + homeassistant/components/mqtt/discovery.py | 18 +++---- homeassistant/components/mqtt/fan.py | 11 ++++- .../components/mqtt/light/__init__.py | 23 +++++++-- homeassistant/components/mqtt/mixins.py | 31 ++++++++++++ 6 files changed, 118 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 162e344f852c6..1fbed56b09f1c 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -23,6 +23,7 @@ from homeassistant import config_entries from homeassistant.components import websocket_api +from homeassistant.config import async_hass_config_yaml from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_ENTITY_ID, @@ -81,6 +82,8 @@ CONF_TLS_VERSION, CONF_TOPIC, CONF_WILL_MESSAGE, + CONFIG_ENTRY_IS_SETUP, + DATA_CONFIG_ENTRY_LOCK, DATA_MQTT_CONFIG, DATA_MQTT_RELOAD_NEEDED, DEFAULT_BIRTH, @@ -171,6 +174,10 @@ Platform.VACUUM, ] +PLATFORMS_YAML_SETUP: list[Platform] = [ + Platform.FAN, + Platform.LIGHT, +] CLIENT_KEY_AUTH_MSG = ( "client_key and client_cert must both be present in " @@ -187,7 +194,14 @@ required=True, ) -CONFIG_SCHEMA_BASE = vol.Schema( +PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( + { + vol.Optional(component.value): vol.Coerce(list) + for component in PLATFORMS_YAML_SETUP + } +) + +CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend( { vol.Optional(CONF_CLIENT_ID): cv.string, vol.Optional(CONF_KEEPALIVE, default=DEFAULT_KEEPALIVE): vol.All( @@ -643,6 +657,24 @@ def _merge_extended_config(entry, conf): return {**conf, **entry.data} +async def async_get_platform_components( + hass: HomeAssistant, + available_platforms: list[Platform] | list[str], +) -> list[Platform]: + """Return a list of platforms with config from configuration.yaml.""" + config_yaml = await async_hass_config_yaml(hass) + if (integration_config := config_yaml.get(DOMAIN)) is None: + return [] + available_components: list[str] = [ + Platform(platform).value for platform in available_platforms + ] + return [ + Platform(component) + for component in integration_config + if component in available_components + ] + + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Load a config entry.""" # Merge basic configuration, and add missing defaults for basic options @@ -774,6 +806,20 @@ async def finish_dump(_): ), ) + # setup platforms and discovery + hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock() + hass.data[CONFIG_ENTRY_IS_SETUP] = set() + + yaml_platforms = await async_get_platform_components(hass, PLATFORMS_YAML_SETUP) + async with hass.data[DATA_CONFIG_ENTRY_LOCK]: + for component in yaml_platforms: + config_entries_key = f"{component}.mqtt" + if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]: + hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) + hass.async_add_job( + hass.config_entries.async_forward_entry_setup(entry, component) + ) + if conf.get(CONF_DISCOVERY): await _async_setup_discovery(hass, conf, entry) diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 698657337636a..106d03101587a 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -28,6 +28,8 @@ CONF_TLS_INSECURE = "tls_insecure" CONF_TLS_VERSION = "tls_version" +CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup" +DATA_CONFIG_ENTRY_LOCK = "mqtt_config_entry_lock" DATA_MQTT_CONFIG = "mqtt_config" DATA_MQTT_RELOAD_NEEDED = "mqtt_reload_needed" diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index fae443dc41168..dc269f524cfcb 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -27,6 +27,8 @@ ATTR_DISCOVERY_TOPIC, CONF_AVAILABILITY, CONF_TOPIC, + CONFIG_ENTRY_IS_SETUP, + DATA_CONFIG_ENTRY_LOCK, DOMAIN, ) @@ -62,8 +64,6 @@ ALREADY_DISCOVERED = "mqtt_discovered_components" PENDING_DISCOVERED = "mqtt_pending_components" -CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup" -DATA_CONFIG_ENTRY_LOCK = "mqtt_config_entry_lock" DATA_CONFIG_FLOW_LOCK = "mqtt_discovery_config_flow_lock" DISCOVERY_UNSUBSCRIBE = "mqtt_discovery_unsubscribe" INTEGRATION_UNSUBSCRIBE = "mqtt_integration_discovery_unsubscribe" @@ -236,16 +236,20 @@ async def discovery_done(_): # pylint: disable-next=import-outside-toplevel from . import device_automation - await device_automation.async_setup_entry(hass, config_entry) + hass.async_add_job( + device_automation.async_setup_entry(hass, config_entry) + ) elif component == "tag": # Local import to avoid circular dependencies # pylint: disable-next=import-outside-toplevel from . import tag - await tag.async_setup_entry(hass, config_entry) + hass.async_add_job(tag.async_setup_entry(hass, config_entry)) else: - await hass.config_entries.async_forward_entry_setup( - config_entry, component + hass.async_add_job( + hass.config_entries.async_forward_entry_setup( + config_entry, component + ) ) hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) @@ -258,9 +262,7 @@ async def discovery_done(_): hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None ) - hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock() hass.data[DATA_CONFIG_FLOW_LOCK] = asyncio.Lock() - hass.data[CONFIG_ENTRY_IS_SETUP] = set() hass.data[ALREADY_DISCOVERED] = {} hass.data[PENDING_DISCOVERED] = {} diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index ff2eab7a68fab..87b7835ea226e 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -49,6 +49,7 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, ) @@ -201,7 +202,7 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT fan through configuration.yaml.""" + """Set up MQTT fan through configuration.yaml (deprecated).""" await async_setup_platform_helper( hass, fan.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -212,7 +213,13 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT fan dynamically through MQTT discovery.""" + """Set up MQTT fan through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + for config in await async_get_platform_config_from_yaml( + hass, fan.DOMAIN, PLATFORM_SCHEMA + ): + await _async_setup_entity(hass, async_add_entities, config, config_entry) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index d78cd5e7baa03..6013e72d9dd1b 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -6,11 +6,16 @@ import voluptuous as vol from homeassistant.components import light +from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from ..mixins import async_setup_entry_helper, async_setup_platform_helper +from ..mixins import ( + async_get_platform_config_from_yaml, + async_setup_entry_helper, + async_setup_platform_helper, +) from .schema import CONF_SCHEMA, MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import ( DISCOVERY_SCHEMA_BASIC, @@ -66,14 +71,24 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT light through configuration.yaml.""" + """Set up MQTT light through configuration.yaml (deprecated).""" await async_setup_platform_helper( hass, light.DOMAIN, config, async_add_entities, _async_setup_entity ) -async def async_setup_entry(hass, config_entry, async_add_entities): - """Set up MQTT light dynamically through MQTT discovery.""" +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up MQTT light through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + for config in await async_get_platform_config_from_yaml( + hass, light.DOMAIN, PLATFORM_SCHEMA + ): + await _async_setup_entity(hass, async_add_entities, config, config_entry) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 43f75f08459b1..57fa22276f33c 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -9,6 +9,7 @@ import voluptuous as vol +from homeassistant.config import async_hass_config_yaml from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_CONFIGURATION_URL, @@ -23,6 +24,7 @@ CONF_ICON, CONF_MODEL, CONF_NAME, + CONF_PLATFORM, CONF_UNIQUE_ID, CONF_VALUE_TEMPLATE, ) @@ -237,6 +239,35 @@ async def __call__( """Define setup_entities type.""" +async def async_get_platform_config_from_yaml( + hass: HomeAssistant, domain: str, schema: vol.Schema +) -> list[ConfigType]: + """Return a list of validated configurations for the platform read from configuration.yaml.""" + + def check_schema(config: dict, schema: vol.Schema) -> ConfigType: + """Update the platform for schema compatibility and check the schema.""" + if CONF_PLATFORM in config: + error_string = f"Invalid keyword 'platform' found, please remove it from your configuration: {json.dumps(config)}" + raise ValueError(error_string) + config[CONF_PLATFORM] = DOMAIN + return schema(config) + + config_yaml = await async_hass_config_yaml(hass) + if not (integration_config := config_yaml.get(DOMAIN)) or not ( + platform_configs := integration_config.get(domain) + ): + return [] + errors = [] + try: + config = [check_schema(config, schema) for config in platform_configs] + except (ValueError, vol.MultipleInvalid) as exc: + errors.append(exc) + if errors: + raise vol.MultipleInvalid(errors) + + return config + + async def async_setup_entry_helper(hass, domain, async_setup, schema): """Set up entity, automation or tag creation dynamically through MQTT discovery.""" From b95abc36296bebbf28f3c4b6c9116ff477f18f84 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Wed, 11 May 2022 14:47:59 +0000 Subject: [PATCH 02/31] Setup all platforms --- homeassistant/components/mqtt/__init__.py | 32 ++-------------------- homeassistant/components/mqtt/discovery.py | 12 +++----- 2 files changed, 6 insertions(+), 38 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 1fbed56b09f1c..8eb29c492a715 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -23,7 +23,6 @@ from homeassistant import config_entries from homeassistant.components import websocket_api -from homeassistant.config import async_hass_config_yaml from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_ENTITY_ID, @@ -174,11 +173,6 @@ Platform.VACUUM, ] -PLATFORMS_YAML_SETUP: list[Platform] = [ - Platform.FAN, - Platform.LIGHT, -] - CLIENT_KEY_AUTH_MSG = ( "client_key and client_cert must both be present in " "the MQTT broker configuration" @@ -195,10 +189,7 @@ ) PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( - { - vol.Optional(component.value): vol.Coerce(list) - for component in PLATFORMS_YAML_SETUP - } + {vol.Optional(component.value): vol.Coerce(list) for component in PLATFORMS} ) CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend( @@ -657,24 +648,6 @@ def _merge_extended_config(entry, conf): return {**conf, **entry.data} -async def async_get_platform_components( - hass: HomeAssistant, - available_platforms: list[Platform] | list[str], -) -> list[Platform]: - """Return a list of platforms with config from configuration.yaml.""" - config_yaml = await async_hass_config_yaml(hass) - if (integration_config := config_yaml.get(DOMAIN)) is None: - return [] - available_components: list[str] = [ - Platform(platform).value for platform in available_platforms - ] - return [ - Platform(component) - for component in integration_config - if component in available_components - ] - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Load a config entry.""" # Merge basic configuration, and add missing defaults for basic options @@ -810,9 +783,8 @@ async def finish_dump(_): hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock() hass.data[CONFIG_ENTRY_IS_SETUP] = set() - yaml_platforms = await async_get_platform_components(hass, PLATFORMS_YAML_SETUP) async with hass.data[DATA_CONFIG_ENTRY_LOCK]: - for component in yaml_platforms: + for component in PLATFORMS: config_entries_key = f"{component}.mqtt" if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]: hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index dc269f524cfcb..8685c790fd2c4 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -236,20 +236,16 @@ async def discovery_done(_): # pylint: disable-next=import-outside-toplevel from . import device_automation - hass.async_add_job( - device_automation.async_setup_entry(hass, config_entry) - ) + await device_automation.async_setup_entry(hass, config_entry) elif component == "tag": # Local import to avoid circular dependencies # pylint: disable-next=import-outside-toplevel from . import tag - hass.async_add_job(tag.async_setup_entry(hass, config_entry)) + await tag.async_setup_entry(hass, config_entry) else: - hass.async_add_job( - hass.config_entries.async_forward_entry_setup( - config_entry, component - ) + await hass.config_entries.async_forward_entry_setup( + config_entry, component ) hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) From fea71838f0a65d48ed697f6b260c76af73673386 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 12 May 2022 09:22:32 +0200 Subject: [PATCH 03/31] Update homeassistant/components/mqtt/__init__.py Co-authored-by: Martin Hjelmare --- homeassistant/components/mqtt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 8eb29c492a715..c3b35b353f148 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -788,7 +788,7 @@ async def finish_dump(_): config_entries_key = f"{component}.mqtt" if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]: hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) - hass.async_add_job( + hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component) ) From f5f1ff9f4e9fac4290ad581eef75145c99d0076a Mon Sep 17 00:00:00 2001 From: jbouwh Date: Thu, 12 May 2022 16:21:58 +0000 Subject: [PATCH 04/31] adjust mock_mqtt - reference config from cache --- homeassistant/components/mqtt/mixins.py | 4 ++-- tests/conftest.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 57fa22276f33c..772a9eda01241 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -9,7 +9,6 @@ import voluptuous as vol -from homeassistant.config import async_hass_config_yaml from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_CONFIGURATION_URL, @@ -66,6 +65,7 @@ CONF_ENCODING, CONF_QOS, CONF_TOPIC, + DATA_MQTT_CONFIG, DATA_MQTT_RELOAD_NEEDED, DEFAULT_ENCODING, DEFAULT_PAYLOAD_AVAILABLE, @@ -252,7 +252,7 @@ def check_schema(config: dict, schema: vol.Schema) -> ConfigType: config[CONF_PLATFORM] = DOMAIN return schema(config) - config_yaml = await async_hass_config_yaml(hass) + config_yaml: ConfigType = hass.data.get(DATA_MQTT_CONFIG, {}) if not (integration_config := config_yaml.get(DOMAIN)) or not ( platform_configs := integration_config.get(domain) ): diff --git a/tests/conftest.py b/tests/conftest.py index 37bdb05faf7ef..6f54aaf1732be 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -547,7 +547,7 @@ def _unsubscribe(topic): @pytest.fixture -async def mqtt_mock(hass, mqtt_client_mock, mqtt_config): +async def mqtt_mock(hass, mqtt_client_mock, mqtt_config, setup_components: list = []): """Fixture to mock MQTT component.""" if mqtt_config is None: mqtt_config = {mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_BIRTH_MESSAGE: {}} @@ -561,9 +561,9 @@ async def mqtt_mock(hass, mqtt_client_mock, mqtt_config): ) entry.add_to_hass(hass) - - assert await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + with patch("homeassistant.components.mqtt.PLATFORMS", setup_components): + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() mqtt_component_mock = MagicMock( return_value=hass.data["mqtt"], From a7da58c3c20f2eb925d0a58d164e5655f5aeb12c Mon Sep 17 00:00:00 2001 From: jbouwh Date: Thu, 12 May 2022 16:41:50 +0000 Subject: [PATCH 05/31] Fix test config entry override --- tests/components/mqtt/test_init.py | 3 ++- tests/conftest.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 543174653ecf1..45dc4e302241d 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1628,7 +1628,8 @@ async def test_setup_entry_with_config_override(hass, device_reg, mqtt_client_mo # User sets up a config entry entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) entry.add_to_hass(hass) - assert await hass.config_entries.async_setup(entry.entry_id) + with patch("homeassistant.components.mqtt.PLATFORMS", []): + assert await hass.config_entries.async_setup(entry.entry_id) # Discover a device to verify the entry was setup correctly async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data) diff --git a/tests/conftest.py b/tests/conftest.py index 6f54aaf1732be..e02c258dbe057 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -561,6 +561,7 @@ async def mqtt_mock(hass, mqtt_client_mock, mqtt_config, setup_components: list ) entry.add_to_hass(hass) + # Do not forward the entry setup to the components here with patch("homeassistant.components.mqtt.PLATFORMS", setup_components): assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() From 84b30edc1a0158ca4a300ce65903cf0dd7cee3df Mon Sep 17 00:00:00 2001 From: jbouwh Date: Fri, 13 May 2022 00:18:14 +0000 Subject: [PATCH 06/31] Add tests yaml setup --- homeassistant/components/mqtt/fan.py | 3 ++ .../components/mqtt/light/__init__.py | 4 +++ homeassistant/components/mqtt/mixins.py | 4 +-- tests/components/mqtt/test_common.py | 30 +++++++++++++++++++ tests/components/mqtt/test_fan.py | 10 +++++++ tests/components/mqtt/test_light.py | 10 +++++++ tests/components/mqtt/test_light_json.py | 10 +++++++ tests/components/mqtt/test_light_template.py | 10 +++++++ tests/conftest.py | 4 +-- 9 files changed, 80 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 87b7835ea226e..007d4952d87db 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -21,6 +21,7 @@ CONF_OPTIMISTIC, CONF_PAYLOAD_OFF, CONF_PAYLOAD_ON, + CONF_PLATFORM, CONF_STATE, ) from homeassistant.core import HomeAssistant, callback @@ -177,6 +178,7 @@ def valid_preset_mode_configuration(config): _PLATFORM_SCHEMA_BASE, valid_speed_range_configuration, valid_preset_mode_configuration, + cv.deprecated(CONF_PLATFORM), # Deprecated in HA Core 2022.6 ) DISCOVERY_SCHEMA = vol.All( @@ -203,6 +205,7 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT fan through configuration.yaml (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, fan.DOMAIN, config, async_add_entities, _async_setup_entity ) diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index 6013e72d9dd1b..b6301f29dc65a 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -7,7 +7,9 @@ from homeassistant.components import light from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_PLATFORM from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -57,6 +59,7 @@ def validate_mqtt_light(value): DISCOVERY_SCHEMA = vol.All( MQTT_LIGHT_SCHEMA_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_light_discovery, + cv.deprecated(CONF_PLATFORM), # Deprecated in HA Core 2022.6 ) @@ -72,6 +75,7 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT light through configuration.yaml (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, light.DOMAIN, config, async_add_entities, _async_setup_entity ) diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 772a9eda01241..40ec02a2819d0 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -253,9 +253,7 @@ def check_schema(config: dict, schema: vol.Schema) -> ConfigType: return schema(config) config_yaml: ConfigType = hass.data.get(DATA_MQTT_CONFIG, {}) - if not (integration_config := config_yaml.get(DOMAIN)) or not ( - platform_configs := integration_config.get(domain) - ): + if not (platform_configs := config_yaml.get(domain)): return [] errors = [] try: diff --git a/tests/components/mqtt/test_common.py b/tests/components/mqtt/test_common.py index 8cf7353d196fc..56f8843b8fa3f 100644 --- a/tests/components/mqtt/test_common.py +++ b/tests/components/mqtt/test_common.py @@ -1690,3 +1690,33 @@ async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config): assert hass.states.get(f"{domain}.test_new_1") assert hass.states.get(f"{domain}.test_new_2") assert hass.states.get(f"{domain}.test_new_3") + + +async def help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config +): + """Test setup from yaml through configuration entry.""" + yaml_config = copy.deepcopy(config) + yaml_config["name"] = "test" + # Remove platform key from the config since this is not valid here + del yaml_config["platform"] + new_yaml_config_file = tmp_path / "configuration.yaml" + new_yaml_config = yaml.dump({mqtt.DOMAIN: {platform: [yaml_config]}}) + new_yaml_config_file.write_text(new_yaml_config) + assert new_yaml_config_file.read_text() == new_yaml_config + + await async_setup_component( + hass, mqtt.DOMAIN, {mqtt.DOMAIN: {platform: [yaml_config]}} + ) + # Mock config entry + entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) + entry.add_to_hass(hass) + + with patch.object(hass_config, "YAML_CONFIG_FILE", new_yaml_config_file), patch( + "paho.mqtt.client.Client" + ) as mock_client: + mock_client().connect = lambda *args: 0 + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/components/mqtt/test_fan.py b/tests/components/mqtt/test_fan.py index 64b5d272af837..3a48cce7c90cb 100644 --- a/tests/components/mqtt/test_fan.py +++ b/tests/components/mqtt/test_fan.py @@ -55,6 +55,7 @@ help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -1805,3 +1806,12 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): domain = fan.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = fan.DOMAIN + config = DEFAULT_CONFIG[platform] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index ee59928c0c879..117a6678364a4 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -237,6 +237,7 @@ help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -3674,3 +3675,12 @@ async def test_sending_mqtt_effect_command_with_template(hass, mqtt_mock): state = hass.states.get("light.test") assert state.state == STATE_ON assert state.attributes.get("effect") == "colorloop" + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = light.DOMAIN + config = DEFAULT_CONFIG[platform] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index 93bb9b0f57357..6490d641b515a 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -131,6 +131,7 @@ help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -2038,3 +2039,12 @@ async def test_encoding_subscribable_topics( init_payload, skip_raw_test=True, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = light.DOMAIN + config = DEFAULT_CONFIG[platform] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) diff --git a/tests/components/mqtt/test_light_template.py b/tests/components/mqtt/test_light_template.py index 4461cf14ef49a..73e490c391b05 100644 --- a/tests/components/mqtt/test_light_template.py +++ b/tests/components/mqtt/test_light_template.py @@ -69,6 +69,7 @@ help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -1213,3 +1214,12 @@ async def test_encoding_subscribable_topics( attribute_value, init_payload, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = light.DOMAIN + config = DEFAULT_CONFIG[platform] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) diff --git a/tests/conftest.py b/tests/conftest.py index e02c258dbe057..8ea6e114e9ad7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -547,7 +547,7 @@ def _unsubscribe(topic): @pytest.fixture -async def mqtt_mock(hass, mqtt_client_mock, mqtt_config, setup_components: list = []): +async def mqtt_mock(hass, mqtt_client_mock, mqtt_config): """Fixture to mock MQTT component.""" if mqtt_config is None: mqtt_config = {mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_BIRTH_MESSAGE: {}} @@ -562,7 +562,7 @@ async def mqtt_mock(hass, mqtt_client_mock, mqtt_config, setup_components: list entry.add_to_hass(hass) # Do not forward the entry setup to the components here - with patch("homeassistant.components.mqtt.PLATFORMS", setup_components): + with patch("homeassistant.components.mqtt.PLATFORMS", []): assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() From 0dacd33a1f99daa3707f0358a76a4c9073482cad Mon Sep 17 00:00:00 2001 From: jbouwh Date: Fri, 13 May 2022 12:01:43 +0000 Subject: [PATCH 07/31] additional tests --- tests/components/mqtt/test_common.py | 41 ++++++++++++++++----- tests/components/mqtt/test_init.py | 53 ++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/tests/components/mqtt/test_common.py b/tests/components/mqtt/test_common.py index 56f8843b8fa3f..b1b837796706d 100644 --- a/tests/components/mqtt/test_common.py +++ b/tests/components/mqtt/test_common.py @@ -1693,21 +1693,41 @@ async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config): async def help_test_setup_manual_entity_from_yaml( - hass, caplog, tmp_path, platform, config + hass, + caplog, + tmp_path, + platform, + config, + assert_entity_exists=True, + remove_platform=True, ): - """Test setup from yaml through configuration entry.""" - yaml_config = copy.deepcopy(config) - yaml_config["name"] = "test" + """Test setup from yaml through configuration entry. + + This helper has some optional parameters. + With the default settings a manual MQTT item will be setup with + the new config schema under the `mqtt->platform->[manual_config]` key. + A `platform` key will be removed from the config if the new schema is used + and `remove_platform` is set, this way we can share the PLATFORM_SCHEMA. + - `assert_entity_exists` adds a check to assert an entity with name platform.test exists. + - `remove_platform` mocks the forwarding og the entry setup to the platforms. + - `legacy_schema` uses the deprecated config schema to setup the manual MQTT item. + """ + configs = [] + if config: + yaml_config = copy.deepcopy(config) + if assert_entity_exists: + yaml_config["name"] = "test" + if remove_platform: + del yaml_config["platform"] + configs.append(yaml_config) + config_structure = {mqtt.DOMAIN: {platform: configs}} # Remove platform key from the config since this is not valid here - del yaml_config["platform"] new_yaml_config_file = tmp_path / "configuration.yaml" - new_yaml_config = yaml.dump({mqtt.DOMAIN: {platform: [yaml_config]}}) + new_yaml_config = yaml.dump(config_structure) new_yaml_config_file.write_text(new_yaml_config) assert new_yaml_config_file.read_text() == new_yaml_config - await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {platform: [yaml_config]}} - ) + await async_setup_component(hass, mqtt.DOMAIN, config_structure) # Mock config entry entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) entry.add_to_hass(hass) @@ -1719,4 +1739,7 @@ async def help_test_setup_manual_entity_from_yaml( assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() + if not assert_entity_exists: + return + assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 45dc4e302241d..59d7d808b8742 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -30,6 +30,8 @@ from homeassistant.setup import async_setup_component from homeassistant.util.dt import utcnow +from .test_common import help_test_setup_manual_entity_from_yaml + from tests.common import ( MockConfigEntry, async_fire_mqtt_message, @@ -1279,6 +1281,57 @@ def mock_usename_password_set(username, password): assert calls_username_password_set[0][1] == "somepassword" +async def test_setup_manual_mqtt_with_platform_key(hass, caplog, tmp_path): + """Test set up a manual MQTT item with a platform key.""" + config = {"platform": "mqtt", "name": "test", "command_topic": "test-topic"} + await help_test_setup_manual_entity_from_yaml( + hass, + caplog, + tmp_path, + "light", + config, + assert_entity_exists=False, + remove_platform=False, + ) + assert ( + "Invalid keyword 'platform' found, please remove it from your configuration" + in caplog.text + ) + + +async def test_setup_manual_mqtt_with_invalid_config(hass, caplog, tmp_path): + """Test set up a manual MQTT item with an invalid config.""" + config = {"name": "test"} + await help_test_setup_manual_entity_from_yaml( + hass, + caplog, + tmp_path, + "light", + config, + assert_entity_exists=False, + remove_platform=False, + ) + assert ( + "voluptuous.error.MultipleInvalid: required key not provided @ data['command_topic']" + in caplog.text + ) + + +async def test_setup_manual_mqtt_empty_platform(hass, caplog, tmp_path): + """Test set up a manual MQTT platform without items.""" + config = None + await help_test_setup_manual_entity_from_yaml( + hass, + caplog, + tmp_path, + "light", + config, + assert_entity_exists=False, + remove_platform=False, + ) + assert "voluptuous.error.MultipleInvalid" not in caplog.text + + async def test_setup_mqtt_client_protocol(hass): """Test MQTT client protocol setup.""" entry = MockConfigEntry( From 91ada977dd2264c52f5839af08aba05a31e289d6 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Mon, 16 May 2022 11:45:48 +0000 Subject: [PATCH 08/31] Introduce PLATFORM_SCHEMA_MODERN --- homeassistant/components/mqtt/__init__.py | 24 ++++++++++++++--- homeassistant/components/mqtt/fan.py | 23 +++++++++++++--- .../components/mqtt/light/__init__.py | 26 +++++++++++++++---- .../components/mqtt/light/schema_basic.py | 16 ++++++++++-- .../components/mqtt/light/schema_json.py | 12 +++++++-- .../components/mqtt/light/schema_template.py | 11 ++++++-- homeassistant/components/mqtt/mixins.py | 11 +------- 7 files changed, 96 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index c3b35b353f148..a2ccec85b2613 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -258,18 +258,36 @@ vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string, } -MQTT_BASE_PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(SCHEMA_BASE) +MQTT_BASE_SCHEMA = vol.Schema(SCHEMA_BASE) -# Sensor type platforms subscribe to MQTT events +# Will be removed when all platforms support a modern platform schema +MQTT_BASE_PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA +# Will be removed when all platforms support a modern platform schema MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( { vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, } ) +# Will be removed when all platforms support a modern platform schema +MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( + { + vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic, + } +) + +# Sensor type platforms subscribe to MQTT events +MQTT_RO_SCHEMA = MQTT_BASE_SCHEMA.extend( + { + vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, + } +) # Switch type platforms publish to MQTT and may subscribe -MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( +MQTT_RW_SCHEMA = MQTT_BASE_SCHEMA.extend( { vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 007d4952d87db..9c7ab1243f5c2 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -124,7 +124,7 @@ def valid_preset_mode_configuration(config): return config -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, @@ -174,13 +174,30 @@ def valid_preset_mode_configuration(config): } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) +# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( - _PLATFORM_SCHEMA_BASE, + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), valid_speed_range_configuration, valid_preset_mode_configuration, cv.deprecated(CONF_PLATFORM), # Deprecated in HA Core 2022.6 ) +PLATFORM_SCHEMA_MODERN = vol.All( + # CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_LIST, CONF_SPEED_STATE_TOPIC, CONF_SPEED_VALUE_TEMPLATE and + # Speeds SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH SPEED_OFF, + # are no longer supported, support was removed in release 2021.12 + cv.removed(CONF_PAYLOAD_HIGH_SPEED), + cv.removed(CONF_PAYLOAD_LOW_SPEED), + cv.removed(CONF_PAYLOAD_MEDIUM_SPEED), + cv.removed(CONF_SPEED_COMMAND_TOPIC), + cv.removed(CONF_SPEED_LIST), + cv.removed(CONF_SPEED_STATE_TOPIC), + cv.removed(CONF_SPEED_VALUE_TEMPLATE), + _PLATFORM_SCHEMA_BASE, + valid_speed_range_configuration, + valid_preset_mode_configuration, +) + DISCOVERY_SCHEMA = vol.All( # CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_LIST, CONF_SPEED_STATE_TOPIC, CONF_SPEED_VALUE_TEMPLATE and # Speeds SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH SPEED_OFF, @@ -219,7 +236,7 @@ async def async_setup_entry( """Set up MQTT fan through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml for config in await async_get_platform_config_from_yaml( - hass, fan.DOMAIN, PLATFORM_SCHEMA + hass, fan.DOMAIN, PLATFORM_SCHEMA_MODERN ): await _async_setup_entity(hass, async_add_entities, config, config_entry) # setup for discovery diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index b6301f29dc65a..917c9165c614b 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -22,22 +22,25 @@ from .schema_basic import ( DISCOVERY_SCHEMA_BASIC, PLATFORM_SCHEMA_BASIC, + PLATFORM_SCHEMA_MODERN_BASIC, async_setup_entity_basic, ) from .schema_json import ( DISCOVERY_SCHEMA_JSON, PLATFORM_SCHEMA_JSON, + PLATFORM_SCHEMA_MODERN_JSON, async_setup_entity_json, ) from .schema_template import ( DISCOVERY_SCHEMA_TEMPLATE, + PLATFORM_SCHEMA_MODERN_TEMPLATE, PLATFORM_SCHEMA_TEMPLATE, async_setup_entity_template, ) def validate_mqtt_light_discovery(value): - """Validate MQTT light schema.""" + """Validate MQTT light schema for.""" schemas = { "basic": DISCOVERY_SCHEMA_BASIC, "json": DISCOVERY_SCHEMA_JSON, @@ -56,17 +59,30 @@ def validate_mqtt_light(value): return schemas[value[CONF_SCHEMA]](value) +def validate_mqtt_light_modern(value): + """Validate MQTT light schema.""" + schemas = { + "basic": PLATFORM_SCHEMA_MODERN_BASIC, + "json": PLATFORM_SCHEMA_MODERN_JSON, + "template": PLATFORM_SCHEMA_MODERN_TEMPLATE, + } + return schemas[value[CONF_SCHEMA]](value) + + DISCOVERY_SCHEMA = vol.All( MQTT_LIGHT_SCHEMA_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_light_discovery, - cv.deprecated(CONF_PLATFORM), # Deprecated in HA Core 2022.6 ) - +# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( - MQTT_LIGHT_SCHEMA_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_light + cv.PLATFORM_SCHEMA.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema, extra=vol.ALLOW_EXTRA), + validate_mqtt_light, + cv.deprecated(CONF_PLATFORM), ) +PLATFORM_SCHEMA_MODERN = vol.All(MQTT_LIGHT_SCHEMA_SCHEMA, validate_mqtt_light_modern) + async def async_setup_platform( hass: HomeAssistant, @@ -89,7 +105,7 @@ async def async_setup_entry( """Set up MQTT light through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml for config in await async_get_platform_config_from_yaml( - hass, light.DOMAIN, PLATFORM_SCHEMA + hass, light.DOMAIN, PLATFORM_SCHEMA_MODERN ): await _async_setup_entity(hass, async_add_entities, config, config_entry) # setup for discovery diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index 497e8186fd0d8..3d247e57627cc 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -156,7 +156,7 @@ ] _PLATFORM_SCHEMA_BASE = ( - mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( + mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_BRIGHTNESS_COMMAND_TEMPLATE): cv.template, vol.Optional(CONF_BRIGHTNESS_COMMAND_TOPIC): mqtt.valid_publish_topic, @@ -220,13 +220,14 @@ .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) +# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 PLATFORM_SCHEMA_BASIC = vol.All( # CONF_WHITE_VALUE_* is deprecated, support will be removed in release 2022.9 cv.deprecated(CONF_WHITE_VALUE_COMMAND_TOPIC), cv.deprecated(CONF_WHITE_VALUE_SCALE), cv.deprecated(CONF_WHITE_VALUE_STATE_TOPIC), cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), - _PLATFORM_SCHEMA_BASE, + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), ) DISCOVERY_SCHEMA_BASIC = vol.All( @@ -240,6 +241,17 @@ _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), ) +PLATFORM_SCHEMA_MODERN_BASIC = vol.All( + # CONF_VALUE_TEMPLATE is no longer supported, support was removed in 2022.2 + cv.removed(CONF_VALUE_TEMPLATE), + # CONF_WHITE_VALUE_* is deprecated, support will be removed in release 2022.9 + cv.deprecated(CONF_WHITE_VALUE_COMMAND_TOPIC), + cv.deprecated(CONF_WHITE_VALUE_SCALE), + cv.deprecated(CONF_WHITE_VALUE_STATE_TOPIC), + cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), + _PLATFORM_SCHEMA_BASE, +) + async def async_setup_entity_basic( hass, config, async_add_entities, config_entry, discovery_data=None diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index c1e0d7467e0a3..c8aa907b489de 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -103,7 +103,7 @@ def valid_color_configuration(config): _PLATFORM_SCHEMA_BASE = ( - mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( + mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_BRIGHTNESS, default=DEFAULT_BRIGHTNESS): cv.boolean, vol.Optional( @@ -146,10 +146,11 @@ def valid_color_configuration(config): .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) +# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 PLATFORM_SCHEMA_JSON = vol.All( # CONF_WHITE_VALUE is deprecated, support will be removed in release 2022.9 cv.deprecated(CONF_WHITE_VALUE), - _PLATFORM_SCHEMA_BASE, + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), valid_color_configuration, ) @@ -160,6 +161,13 @@ def valid_color_configuration(config): valid_color_configuration, ) +PLATFORM_SCHEMA_MODERN_JSON = vol.All( + # CONF_WHITE_VALUE is deprecated, support will be removed in release 2022.9 + cv.deprecated(CONF_WHITE_VALUE), + _PLATFORM_SCHEMA_BASE, + valid_color_configuration, +) + async def async_setup_entity_json( hass, config: ConfigType, async_add_entities, config_entry, discovery_data diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index a98f634642de0..6641a83bde6c9 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -67,7 +67,7 @@ CONF_WHITE_VALUE_TEMPLATE = "white_value_template" _PLATFORM_SCHEMA_BASE = ( - mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( + mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_BLUE_TEMPLATE): cv.template, vol.Optional(CONF_BRIGHTNESS_TEMPLATE): cv.template, @@ -90,10 +90,11 @@ .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) +# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 PLATFORM_SCHEMA_TEMPLATE = vol.All( # CONF_WHITE_VALUE_TEMPLATE is deprecated, support will be removed in release 2022.9 cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), - _PLATFORM_SCHEMA_BASE, + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), ) DISCOVERY_SCHEMA_TEMPLATE = vol.All( @@ -102,6 +103,12 @@ _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), ) +PLATFORM_SCHEMA_MODERN_TEMPLATE = vol.All( + # CONF_WHITE_VALUE_TEMPLATE is deprecated, support will be removed in release 2022.9 + cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), + _PLATFORM_SCHEMA_BASE, +) + async def async_setup_entity_template( hass, config, async_add_entities, config_entry, discovery_data diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 40ec02a2819d0..eee276b70fe11 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -23,7 +23,6 @@ CONF_ICON, CONF_MODEL, CONF_NAME, - CONF_PLATFORM, CONF_UNIQUE_ID, CONF_VALUE_TEMPLATE, ) @@ -244,20 +243,12 @@ async def async_get_platform_config_from_yaml( ) -> list[ConfigType]: """Return a list of validated configurations for the platform read from configuration.yaml.""" - def check_schema(config: dict, schema: vol.Schema) -> ConfigType: - """Update the platform for schema compatibility and check the schema.""" - if CONF_PLATFORM in config: - error_string = f"Invalid keyword 'platform' found, please remove it from your configuration: {json.dumps(config)}" - raise ValueError(error_string) - config[CONF_PLATFORM] = DOMAIN - return schema(config) - config_yaml: ConfigType = hass.data.get(DATA_MQTT_CONFIG, {}) if not (platform_configs := config_yaml.get(domain)): return [] errors = [] try: - config = [check_schema(config, schema) for config in platform_configs] + config = [schema(config) for config in platform_configs] except (ValueError, vol.MultipleInvalid) as exc: errors.append(exc) if errors: From 5acbec1345398af8aa1b9bf4ca8bc2ca31d90bcf Mon Sep 17 00:00:00 2001 From: jbouwh Date: Mon, 16 May 2022 12:05:48 +0000 Subject: [PATCH 09/31] recover temporary MQTT_BASE_PLATFORM_SCHEMA --- homeassistant/components/mqtt/__init__.py | 2 +- tests/components/mqtt/test_light.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index a2ccec85b2613..c4a9a65aa4a74 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -261,7 +261,7 @@ MQTT_BASE_SCHEMA = vol.Schema(SCHEMA_BASE) # Will be removed when all platforms support a modern platform schema -MQTT_BASE_PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA +MQTT_BASE_PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(SCHEMA_BASE) # Will be removed when all platforms support a modern platform schema MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( { diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index 117a6678364a4..fa4a444cfa6e9 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -347,7 +347,6 @@ async def test_legacy_controlling_state_via_topic(hass, mqtt_mock): """Test the controlling of the state via topic for legacy light (white_value).""" config = { light.DOMAIN: { - "platform": "mqtt", "name": "test", "state_topic": "test_light_rgb/status", "command_topic": "test_light_rgb/set", From 6d56d48c794f8a158ecb2acbfec930a3b2e1f3e6 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Mon, 16 May 2022 12:33:02 +0000 Subject: [PATCH 10/31] Allow extra key in light base schema, restore test --- homeassistant/components/mqtt/light/__init__.py | 5 ++++- tests/components/mqtt/test_light.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index 917c9165c614b..853e30593b489 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -81,7 +81,10 @@ def validate_mqtt_light_modern(value): cv.deprecated(CONF_PLATFORM), ) -PLATFORM_SCHEMA_MODERN = vol.All(MQTT_LIGHT_SCHEMA_SCHEMA, validate_mqtt_light_modern) +PLATFORM_SCHEMA_MODERN = vol.All( + MQTT_LIGHT_SCHEMA_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), + validate_mqtt_light_modern, +) async def async_setup_platform( diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index fa4a444cfa6e9..117a6678364a4 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -347,6 +347,7 @@ async def test_legacy_controlling_state_via_topic(hass, mqtt_mock): """Test the controlling of the state via topic for legacy light (white_value).""" config = { light.DOMAIN: { + "platform": "mqtt", "name": "test", "state_topic": "test_light_rgb/status", "command_topic": "test_light_rgb/set", From 4447887494e7d4df9856a317644e3be86903ad51 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Mon, 16 May 2022 12:39:48 +0000 Subject: [PATCH 11/31] Fix test for exception on platform key --- tests/components/mqtt/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 59d7d808b8742..1e6fbe029d562 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1294,7 +1294,7 @@ async def test_setup_manual_mqtt_with_platform_key(hass, caplog, tmp_path): remove_platform=False, ) assert ( - "Invalid keyword 'platform' found, please remove it from your configuration" + "voluptuous.error.MultipleInvalid: extra keys not allowed @ data['platform']" in caplog.text ) From 95e316c18044b10c691de977d8d15b1abc261bee Mon Sep 17 00:00:00 2001 From: jbouwh Date: Tue, 17 May 2022 11:29:59 +0000 Subject: [PATCH 12/31] One deprecation message per platform --- homeassistant/components/mqtt/__init__.py | 2 +- homeassistant/components/mqtt/fan.py | 4 ++-- .../components/mqtt/light/__init__.py | 4 ++-- homeassistant/components/mqtt/mixins.py | 21 +++++++++++++++++++ tests/components/mqtt/test_init.py | 21 +++++++++++++++++++ 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index c4a9a65aa4a74..4449fb88086c4 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -189,7 +189,7 @@ ) PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( - {vol.Optional(component.value): vol.Coerce(list) for component in PLATFORMS} + {vol.Optional(component.value): cv.ensure_list for component in PLATFORMS} ) CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend( diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 9c7ab1243f5c2..796fd284e55eb 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -21,7 +21,6 @@ CONF_OPTIMISTIC, CONF_PAYLOAD_OFF, CONF_PAYLOAD_ON, - CONF_PLATFORM, CONF_STATE, ) from homeassistant.core import HomeAssistant, callback @@ -53,6 +52,7 @@ async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + validate_modern_schema, ) CONF_PERCENTAGE_STATE_TOPIC = "percentage_state_topic" @@ -179,7 +179,7 @@ def valid_preset_mode_configuration(config): cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), valid_speed_range_configuration, valid_preset_mode_configuration, - cv.deprecated(CONF_PLATFORM), # Deprecated in HA Core 2022.6 + validate_modern_schema(fan.DOMAIN), ) PLATFORM_SCHEMA_MODERN = vol.All( diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index 853e30593b489..12de2098f3e91 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -7,7 +7,6 @@ from homeassistant.components import light from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_PLATFORM from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -17,6 +16,7 @@ async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + validate_modern_schema, ) from .schema import CONF_SCHEMA, MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import ( @@ -78,7 +78,7 @@ def validate_mqtt_light_modern(value): PLATFORM_SCHEMA = vol.All( cv.PLATFORM_SCHEMA.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema, extra=vol.ALLOW_EXTRA), validate_mqtt_light, - cv.deprecated(CONF_PLATFORM), + validate_modern_schema(light.DOMAIN), ) PLATFORM_SCHEMA_MODERN = vol.All( diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index eee276b70fe11..a624c8df51280 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -224,6 +224,27 @@ def validate_device_has_at_least_one_identifier(value: ConfigType) -> ConfigType ) +def validate_modern_schema(domain: str) -> Callable: + """Warn once when a legacy platform schema is used.""" + warned = set() + + def validator(config: ConfigType) -> ConfigType: + """Return a validator.""" + nonlocal warned + + if domain in warned: + return config + + _LOGGER.warning( + "Manual configured MQTT item found at platform key '%s'. Manual MQTT item configurations have been moved to the integration key", + domain, + ) + warned.add(domain) + return config + + return validator + + class SetupEntity(Protocol): """Protocol type for async_setup_entities.""" diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 1e6fbe029d562..53b310e76ce1a 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1,5 +1,6 @@ """The tests for the MQTT component.""" import asyncio +import copy from datetime import datetime, timedelta from functools import partial import json @@ -2467,3 +2468,23 @@ async def async_mqtt_connected(status): assert len(mqtt_connected_calls) == 2 assert mqtt_connected_calls[0] is True assert mqtt_connected_calls[1] is False + + +async def test_one_deprecation_warning_per_platform(hass, mqtt_mock, caplog): + """Test a deprecation warning is is logged once per platform.""" + platform = "light" + config = {"platform": "mqtt", "command_topic": "test-topic"} + config1 = copy.deepcopy(config) + config1["name"] = "test1" + config2 = copy.deepcopy(config) + config2["name"] = "test2" + await async_setup_component(hass, platform, {platform: [config1, config2]}) + await hass.async_block_till_done() + count = 0 + for record in caplog.records: + if record.levelname == "WARNING" and ( + f"Manual configured MQTT item found at platform key '{platform}'" + in record.message + ): + count += 1 + assert count == 1 From 4b7855a2f1f28db5df8ef937f70b1af02526931e Mon Sep 17 00:00:00 2001 From: jbouwh Date: Tue, 17 May 2022 14:14:39 +0000 Subject: [PATCH 13/31] Remove deprecation checks from modern schema --- homeassistant/components/mqtt/fan.py | 10 ---------- homeassistant/components/mqtt/light/schema_basic.py | 11 +---------- homeassistant/components/mqtt/light/schema_json.py | 2 -- .../components/mqtt/light/schema_template.py | 6 +----- 4 files changed, 2 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 796fd284e55eb..9f2d9bcbd2443 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -183,16 +183,6 @@ def valid_preset_mode_configuration(config): ) PLATFORM_SCHEMA_MODERN = vol.All( - # CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_LIST, CONF_SPEED_STATE_TOPIC, CONF_SPEED_VALUE_TEMPLATE and - # Speeds SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH SPEED_OFF, - # are no longer supported, support was removed in release 2021.12 - cv.removed(CONF_PAYLOAD_HIGH_SPEED), - cv.removed(CONF_PAYLOAD_LOW_SPEED), - cv.removed(CONF_PAYLOAD_MEDIUM_SPEED), - cv.removed(CONF_SPEED_COMMAND_TOPIC), - cv.removed(CONF_SPEED_LIST), - cv.removed(CONF_SPEED_STATE_TOPIC), - cv.removed(CONF_SPEED_VALUE_TEMPLATE), _PLATFORM_SCHEMA_BASE, valid_speed_range_configuration, valid_preset_mode_configuration, diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index 3d247e57627cc..eb4ec264981c1 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -241,16 +241,7 @@ _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), ) -PLATFORM_SCHEMA_MODERN_BASIC = vol.All( - # CONF_VALUE_TEMPLATE is no longer supported, support was removed in 2022.2 - cv.removed(CONF_VALUE_TEMPLATE), - # CONF_WHITE_VALUE_* is deprecated, support will be removed in release 2022.9 - cv.deprecated(CONF_WHITE_VALUE_COMMAND_TOPIC), - cv.deprecated(CONF_WHITE_VALUE_SCALE), - cv.deprecated(CONF_WHITE_VALUE_STATE_TOPIC), - cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), - _PLATFORM_SCHEMA_BASE, -) +PLATFORM_SCHEMA_MODERN_BASIC = _PLATFORM_SCHEMA_BASE async def async_setup_entity_basic( diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index c8aa907b489de..bb4817b25972b 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -162,8 +162,6 @@ def valid_color_configuration(config): ) PLATFORM_SCHEMA_MODERN_JSON = vol.All( - # CONF_WHITE_VALUE is deprecated, support will be removed in release 2022.9 - cv.deprecated(CONF_WHITE_VALUE), _PLATFORM_SCHEMA_BASE, valid_color_configuration, ) diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 6641a83bde6c9..e1773d130de82 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -103,11 +103,7 @@ _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), ) -PLATFORM_SCHEMA_MODERN_TEMPLATE = vol.All( - # CONF_WHITE_VALUE_TEMPLATE is deprecated, support will be removed in release 2022.9 - cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), - _PLATFORM_SCHEMA_BASE, -) +PLATFORM_SCHEMA_MODERN_TEMPLATE = _PLATFORM_SCHEMA_BASE async def async_setup_entity_template( From 25caa55c5736b387397f6106ae1e5675893d88b0 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 18 May 2022 10:18:56 +0200 Subject: [PATCH 14/31] Update homeassistant/components/mqtt/fan.py Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 9f2d9bcbd2443..4675927cb76de 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -174,7 +174,7 @@ def valid_preset_mode_configuration(config): } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 +# Configuring MQTT Fans under the fan platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), valid_speed_range_configuration, From b9c1cbfc7f75a3285983e9d3f3fc09d123b2d55d Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 18 May 2022 10:19:04 +0200 Subject: [PATCH 15/31] Update homeassistant/components/mqtt/fan.py Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 4675927cb76de..65e7a4dd35502 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -211,7 +211,7 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT fan through configuration.yaml (deprecated).""" + """Set up MQTT fans configured under the fan platform key (deprecated).""" # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, fan.DOMAIN, config, async_add_entities, _async_setup_entity From a2aeb35a34b52ad7cd1b22c00379233454428429 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 18 May 2022 10:19:17 +0200 Subject: [PATCH 16/31] Update homeassistant/components/mqtt/light/__init__.py Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/light/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index 12de2098f3e91..472958c2dfe33 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -74,7 +74,7 @@ def validate_mqtt_light_modern(value): validate_mqtt_light_discovery, ) -# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 +# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( cv.PLATFORM_SCHEMA.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema, extra=vol.ALLOW_EXTRA), validate_mqtt_light, From 8a5c436e859d662d35dc2dbc6e2431d88732f0de Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 18 May 2022 10:19:34 +0200 Subject: [PATCH 17/31] Update homeassistant/components/mqtt/light/__init__.py Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/light/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index 472958c2dfe33..667dc89cb37ec 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -105,7 +105,7 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT light through configuration.yaml and dynamically through MQTT discovery.""" + """Set up MQTT lights configured under the light platform key (deprecated).""" # load and initialize platform config from configuration.yaml for config in await async_get_platform_config_from_yaml( hass, light.DOMAIN, PLATFORM_SCHEMA_MODERN From f2b37636dacb6a6d14c07994f1256ba658f43fab Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 18 May 2022 10:19:43 +0200 Subject: [PATCH 18/31] Update homeassistant/components/mqtt/light/schema_json.py Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/light/schema_json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index bb4817b25972b..2049818ab3197 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -146,7 +146,7 @@ def valid_color_configuration(config): .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) -# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 +# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA_JSON = vol.All( # CONF_WHITE_VALUE is deprecated, support will be removed in release 2022.9 cv.deprecated(CONF_WHITE_VALUE), From 183b032eb1f640fb4b48fff6554ff6ec0463dfc6 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 18 May 2022 10:19:50 +0200 Subject: [PATCH 19/31] Update homeassistant/components/mqtt/light/schema_template.py Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/light/schema_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index e1773d130de82..0165bfc8efa33 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -90,7 +90,7 @@ .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) -# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 +# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA_TEMPLATE = vol.All( # CONF_WHITE_VALUE_TEMPLATE is deprecated, support will be removed in release 2022.9 cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), From aac9ba2568917ff2ed5f8cb6d5f647e0426fe841 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 18 May 2022 10:20:22 +0200 Subject: [PATCH 20/31] Update homeassistant/components/mqtt/mixins.py Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/mixins.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index a624c8df51280..4b29214527d73 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -236,7 +236,9 @@ def validator(config: ConfigType) -> ConfigType: return config _LOGGER.warning( - "Manual configured MQTT item found at platform key '%s'. Manual MQTT item configurations have been moved to the integration key", + "Manually configured MQTT item(s) found under platform key '%s'. Manually configured MQTT item configurations have been moved to the integration key, please move the configuration of MQTT %s to mqtt->%s", + domain, + domain, domain, ) warned.add(domain) From 4a5ccf82b0db6e693235e87a9567268755add757 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Wed, 18 May 2022 08:29:33 +0000 Subject: [PATCH 21/31] rename validate_modern_schema --- homeassistant/components/mqtt/fan.py | 4 ++-- homeassistant/components/mqtt/light/__init__.py | 4 ++-- homeassistant/components/mqtt/mixins.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 65e7a4dd35502..0f40a2ce47a45 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -52,7 +52,7 @@ async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, - validate_modern_schema, + warn_for_legacy_schema, ) CONF_PERCENTAGE_STATE_TOPIC = "percentage_state_topic" @@ -179,7 +179,7 @@ def valid_preset_mode_configuration(config): cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), valid_speed_range_configuration, valid_preset_mode_configuration, - validate_modern_schema(fan.DOMAIN), + warn_for_legacy_schema(fan.DOMAIN), ) PLATFORM_SCHEMA_MODERN = vol.All( diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index 667dc89cb37ec..d0465326ecb49 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -16,7 +16,7 @@ async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, - validate_modern_schema, + warn_for_legacy_schema, ) from .schema import CONF_SCHEMA, MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import ( @@ -78,7 +78,7 @@ def validate_mqtt_light_modern(value): PLATFORM_SCHEMA = vol.All( cv.PLATFORM_SCHEMA.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema, extra=vol.ALLOW_EXTRA), validate_mqtt_light, - validate_modern_schema(light.DOMAIN), + warn_for_legacy_schema(light.DOMAIN), ) PLATFORM_SCHEMA_MODERN = vol.All( diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 4b29214527d73..f7f94c45ca7e5 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -224,7 +224,7 @@ def validate_device_has_at_least_one_identifier(value: ConfigType) -> ConfigType ) -def validate_modern_schema(domain: str) -> Callable: +def warn_for_legacy_schema(domain: str) -> Callable: """Warn once when a legacy platform schema is used.""" warned = set() From a48ebbbd4a24899af1e51cf617e83ba8470d4936 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Wed, 18 May 2022 11:00:27 +0000 Subject: [PATCH 22/31] Do not fail platform if a single config is broken --- homeassistant/components/mqtt/mixins.py | 27 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index f7f94c45ca7e5..2cb8a952a7420 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -9,6 +9,7 @@ import voluptuous as vol +from homeassistant.config import async_log_exception from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_CONFIGURATION_URL, @@ -264,20 +265,26 @@ async def __call__( async def async_get_platform_config_from_yaml( hass: HomeAssistant, domain: str, schema: vol.Schema ) -> list[ConfigType]: - """Return a list of validated configurations for the platform read from configuration.yaml.""" + """Return a list of validated configurations for the domain read from configuration.yaml.""" + + def async_validate_config( + hass: HomeAssistant, + config: list[ConfigType], + ) -> list[ConfigType]: + """Validate config.""" + validated_config = [] + for config_item in config: + try: + validated_config.append(schema(config_item)) + except vol.MultipleInvalid as err: + async_log_exception(err, domain, config_item, hass) + + return validated_config config_yaml: ConfigType = hass.data.get(DATA_MQTT_CONFIG, {}) if not (platform_configs := config_yaml.get(domain)): return [] - errors = [] - try: - config = [schema(config) for config in platform_configs] - except (ValueError, vol.MultipleInvalid) as exc: - errors.append(exc) - if errors: - raise vol.MultipleInvalid(errors) - - return config + return async_validate_config(hass, platform_configs) async def async_setup_entry_helper(hass, domain, async_setup, schema): From 7d4cc0373c04012e7766ec4d13963a0b3c13f034 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 18 May 2022 13:11:05 +0200 Subject: [PATCH 23/31] Update homeassistant/components/mqtt/__init__.py Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 4449fb88086c4..e17a31480b1a3 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -189,7 +189,10 @@ ) PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( - {vol.Optional(component.value): cv.ensure_list for component in PLATFORMS} + { + vol.Optional(Platform.FAN.value): cv.ensure_list, + vol.Optional(Platform.LIGHT.value): cv.ensure_list, + } ) CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend( From 17bf63556b71ae6687fb12b407396cb17ccb22d0 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Wed, 18 May 2022 11:54:00 +0000 Subject: [PATCH 24/31] Fix tests on asserting log --- tests/components/mqtt/test_init.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 53b310e76ce1a..46c3535622286 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1295,7 +1295,7 @@ async def test_setup_manual_mqtt_with_platform_key(hass, caplog, tmp_path): remove_platform=False, ) assert ( - "voluptuous.error.MultipleInvalid: extra keys not allowed @ data['platform']" + "Invalid config for [light]: [platform] is an invalid option for [light]. Check: light->platform. (See ?, line ?)" in caplog.text ) @@ -1313,7 +1313,7 @@ async def test_setup_manual_mqtt_with_invalid_config(hass, caplog, tmp_path): remove_platform=False, ) assert ( - "voluptuous.error.MultipleInvalid: required key not provided @ data['command_topic']" + "Invalid config for [light]: required key not provided @ data['command_topic']. Got None. (See ?, line ?)" in caplog.text ) @@ -2483,7 +2483,7 @@ async def test_one_deprecation_warning_per_platform(hass, mqtt_mock, caplog): count = 0 for record in caplog.records: if record.levelname == "WARNING" and ( - f"Manual configured MQTT item found at platform key '{platform}'" + f"Manually configured MQTT item(s) found under platform key '{platform}'" in record.message ): count += 1 From 717aeae9ebad165d7fb29fdb07a7cf94e83cd98a Mon Sep 17 00:00:00 2001 From: jbouwh Date: Thu, 19 May 2022 09:45:07 +0000 Subject: [PATCH 25/31] Update log. Make helper transparant, remove patch --- homeassistant/components/mqtt/mixins.py | 3 +- tests/components/mqtt/test_common.py | 38 ++------------------ tests/components/mqtt/test_fan.py | 5 ++- tests/components/mqtt/test_init.py | 2 +- tests/components/mqtt/test_light.py | 5 ++- tests/components/mqtt/test_light_json.py | 5 ++- tests/components/mqtt/test_light_template.py | 5 ++- 7 files changed, 22 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 2cb8a952a7420..1456d94600198 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -237,7 +237,8 @@ def validator(config: ConfigType) -> ConfigType: return config _LOGGER.warning( - "Manually configured MQTT item(s) found under platform key '%s'. Manually configured MQTT item configurations have been moved to the integration key, please move the configuration of MQTT %s to mqtt->%s", + "Manually configured MQTT %s(s) found under platform key '%s', " + "please move to the mqtt integration key, see https://www.home-assistant.io/integrations/%s.mqtt/", domain, domain, domain, diff --git a/tests/components/mqtt/test_common.py b/tests/components/mqtt/test_common.py index b1b837796706d..b5bb5732617f7 100644 --- a/tests/components/mqtt/test_common.py +++ b/tests/components/mqtt/test_common.py @@ -1698,48 +1698,16 @@ async def help_test_setup_manual_entity_from_yaml( tmp_path, platform, config, - assert_entity_exists=True, - remove_platform=True, ): - """Test setup from yaml through configuration entry. - - This helper has some optional parameters. - With the default settings a manual MQTT item will be setup with - the new config schema under the `mqtt->platform->[manual_config]` key. - A `platform` key will be removed from the config if the new schema is used - and `remove_platform` is set, this way we can share the PLATFORM_SCHEMA. - - `assert_entity_exists` adds a check to assert an entity with name platform.test exists. - - `remove_platform` mocks the forwarding og the entry setup to the platforms. - - `legacy_schema` uses the deprecated config schema to setup the manual MQTT item. - """ - configs = [] - if config: - yaml_config = copy.deepcopy(config) - if assert_entity_exists: - yaml_config["name"] = "test" - if remove_platform: - del yaml_config["platform"] - configs.append(yaml_config) - config_structure = {mqtt.DOMAIN: {platform: configs}} - # Remove platform key from the config since this is not valid here - new_yaml_config_file = tmp_path / "configuration.yaml" - new_yaml_config = yaml.dump(config_structure) - new_yaml_config_file.write_text(new_yaml_config) - assert new_yaml_config_file.read_text() == new_yaml_config + """Help to test setup from yaml through configuration entry.""" + config_structure = {mqtt.DOMAIN: {platform: config}} await async_setup_component(hass, mqtt.DOMAIN, config_structure) # Mock config entry entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) entry.add_to_hass(hass) - with patch.object(hass_config, "YAML_CONFIG_FILE", new_yaml_config_file), patch( - "paho.mqtt.client.Client" - ) as mock_client: + with patch("paho.mqtt.client.Client") as mock_client: mock_client().connect = lambda *args: 0 assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - - if not assert_entity_exists: - return - - assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/components/mqtt/test_fan.py b/tests/components/mqtt/test_fan.py index 3a48cce7c90cb..1a533db63c060 100644 --- a/tests/components/mqtt/test_fan.py +++ b/tests/components/mqtt/test_fan.py @@ -1811,7 +1811,10 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): """Test setup manual configured MQTT entity.""" platform = fan.DOMAIN - config = DEFAULT_CONFIG[platform] + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] await help_test_setup_manual_entity_from_yaml( hass, caplog, tmp_path, platform, config ) + assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 46c3535622286..05eb1fbe480f2 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -2483,7 +2483,7 @@ async def test_one_deprecation_warning_per_platform(hass, mqtt_mock, caplog): count = 0 for record in caplog.records: if record.levelname == "WARNING" and ( - f"Manually configured MQTT item(s) found under platform key '{platform}'" + f"Manually configured MQTT {platform}(s) found under platform key '{platform}'" in record.message ): count += 1 diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index 117a6678364a4..957178da14fcc 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -3680,7 +3680,10 @@ async def test_sending_mqtt_effect_command_with_template(hass, mqtt_mock): async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): """Test setup manual configured MQTT entity.""" platform = light.DOMAIN - config = DEFAULT_CONFIG[platform] + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] await help_test_setup_manual_entity_from_yaml( hass, caplog, tmp_path, platform, config ) + assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index 6490d641b515a..962bf534370f6 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -2044,7 +2044,10 @@ async def test_encoding_subscribable_topics( async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): """Test setup manual configured MQTT entity.""" platform = light.DOMAIN - config = DEFAULT_CONFIG[platform] + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] await help_test_setup_manual_entity_from_yaml( hass, caplog, tmp_path, platform, config ) + assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/components/mqtt/test_light_template.py b/tests/components/mqtt/test_light_template.py index 73e490c391b05..a88fc094f6dbd 100644 --- a/tests/components/mqtt/test_light_template.py +++ b/tests/components/mqtt/test_light_template.py @@ -1219,7 +1219,10 @@ async def test_encoding_subscribable_topics( async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): """Test setup manual configured MQTT entity.""" platform = light.DOMAIN - config = DEFAULT_CONFIG[platform] + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] await help_test_setup_manual_entity_from_yaml( hass, caplog, tmp_path, platform, config ) + assert hass.states.get(f"{platform}.test") is not None From 911c934743419327bbf07639a36893f5375f27b1 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Thu, 19 May 2022 10:18:40 +0000 Subject: [PATCH 26/31] Perform parallel processing --- homeassistant/components/mqtt/fan.py | 13 +++++++++---- homeassistant/components/mqtt/light/__init__.py | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 0f40a2ce47a45..f2b738cd2bb2c 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -1,6 +1,7 @@ """Support for MQTT fans.""" from __future__ import annotations +import asyncio import functools import logging import math @@ -225,10 +226,14 @@ async def async_setup_entry( ) -> None: """Set up MQTT fan through configuration.yaml and dynamically through MQTT discovery.""" # load and initialize platform config from configuration.yaml - for config in await async_get_platform_config_from_yaml( - hass, fan.DOMAIN, PLATFORM_SCHEMA_MODERN - ): - await _async_setup_entity(hass, async_add_entities, config, config_entry) + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, fan.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index d0465326ecb49..ab2a3462615a3 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -1,6 +1,7 @@ """Support for MQTT lights.""" from __future__ import annotations +import asyncio import functools import voluptuous as vol @@ -107,10 +108,14 @@ async def async_setup_entry( ) -> None: """Set up MQTT lights configured under the light platform key (deprecated).""" # load and initialize platform config from configuration.yaml - for config in await async_get_platform_config_from_yaml( - hass, light.DOMAIN, PLATFORM_SCHEMA_MODERN - ): - await _async_setup_entity(hass, async_add_entities, config, config_entry) + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, light.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry From b948ad2d18ec08960046fffef363dbc81d0444e1 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 19 May 2022 12:20:47 +0200 Subject: [PATCH 27/31] Update tests/components/mqtt/test_init.py Co-authored-by: Martin Hjelmare --- tests/components/mqtt/test_init.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 05eb1fbe480f2..aff2bfef41a29 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1313,7 +1313,8 @@ async def test_setup_manual_mqtt_with_invalid_config(hass, caplog, tmp_path): remove_platform=False, ) assert ( - "Invalid config for [light]: required key not provided @ data['command_topic']. Got None. (See ?, line ?)" + "Invalid config for [light]: required key not provided @ data['command_topic']." + " Got None. (See ?, line ?)" in caplog.text ) From 52bcc23c22bdd622fdb2bec5b5bfb96729d42ffb Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 19 May 2022 12:22:01 +0200 Subject: [PATCH 28/31] Apply suggestions from code review Co-authored-by: Martin Hjelmare --- homeassistant/components/mqtt/mixins.py | 2 +- tests/components/mqtt/test_init.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 1456d94600198..d443df0669786 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -266,7 +266,7 @@ async def __call__( async def async_get_platform_config_from_yaml( hass: HomeAssistant, domain: str, schema: vol.Schema ) -> list[ConfigType]: - """Return a list of validated configurations for the domain read from configuration.yaml.""" + """Return a list of validated configurations for the domain.""" def async_validate_config( hass: HomeAssistant, diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index aff2bfef41a29..72bc040c67882 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1295,7 +1295,8 @@ async def test_setup_manual_mqtt_with_platform_key(hass, caplog, tmp_path): remove_platform=False, ) assert ( - "Invalid config for [light]: [platform] is an invalid option for [light]. Check: light->platform. (See ?, line ?)" + "Invalid config for [light]: [platform] is an invalid option for [light]. " + "Check: light->platform. (See ?, line ?)" in caplog.text ) From c08eed5c4a9f54b3b70ac274e3530f8280113a3a Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 19 May 2022 12:22:38 +0200 Subject: [PATCH 29/31] Update homeassistant/components/mqtt/mixins.py Co-authored-by: Martin Hjelmare --- homeassistant/components/mqtt/mixins.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index d443df0669786..50230533bff2c 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -238,7 +238,8 @@ def validator(config: ConfigType) -> ConfigType: _LOGGER.warning( "Manually configured MQTT %s(s) found under platform key '%s', " - "please move to the mqtt integration key, see https://www.home-assistant.io/integrations/%s.mqtt/", + "please move to the mqtt integration key, see " + "https://www.home-assistant.io/integrations/%s.mqtt/", domain, domain, domain, From 94e1c606b3f57751bdcc0ab5baa9eb2374e4a388 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Thu, 19 May 2022 10:52:36 +0000 Subject: [PATCH 30/31] black --- tests/components/mqtt/test_init.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 72bc040c67882..7a5a41bbfe035 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1296,8 +1296,7 @@ async def test_setup_manual_mqtt_with_platform_key(hass, caplog, tmp_path): ) assert ( "Invalid config for [light]: [platform] is an invalid option for [light]. " - "Check: light->platform. (See ?, line ?)" - in caplog.text + "Check: light->platform. (See ?, line ?)" in caplog.text ) @@ -1315,8 +1314,7 @@ async def test_setup_manual_mqtt_with_invalid_config(hass, caplog, tmp_path): ) assert ( "Invalid config for [light]: required key not provided @ data['command_topic']." - " Got None. (See ?, line ?)" - in caplog.text + " Got None. (See ?, line ?)" in caplog.text ) From a9f7ae8101553a9cc6d0b3d1e6f62d56bcc06da4 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Thu, 19 May 2022 11:42:08 +0000 Subject: [PATCH 31/31] Fix tests and add #new_format anchor --- homeassistant/components/mqtt/mixins.py | 2 +- tests/components/mqtt/test_init.py | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 50230533bff2c..a46debeae5480 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -239,7 +239,7 @@ def validator(config: ConfigType) -> ConfigType: _LOGGER.warning( "Manually configured MQTT %s(s) found under platform key '%s', " "please move to the mqtt integration key, see " - "https://www.home-assistant.io/integrations/%s.mqtt/", + "https://www.home-assistant.io/integrations/%s.mqtt/#new_format", domain, domain, domain, diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 7a5a41bbfe035..a370bd67ec1c4 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1291,8 +1291,6 @@ async def test_setup_manual_mqtt_with_platform_key(hass, caplog, tmp_path): tmp_path, "light", config, - assert_entity_exists=False, - remove_platform=False, ) assert ( "Invalid config for [light]: [platform] is an invalid option for [light]. " @@ -1309,8 +1307,6 @@ async def test_setup_manual_mqtt_with_invalid_config(hass, caplog, tmp_path): tmp_path, "light", config, - assert_entity_exists=False, - remove_platform=False, ) assert ( "Invalid config for [light]: required key not provided @ data['command_topic']." @@ -1327,8 +1323,6 @@ async def test_setup_manual_mqtt_empty_platform(hass, caplog, tmp_path): tmp_path, "light", config, - assert_entity_exists=False, - remove_platform=False, ) assert "voluptuous.error.MultipleInvalid" not in caplog.text