Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not fail MQTT setup if events or sensors configured via yaml can't be validated #102309

Merged
merged 3 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions homeassistant/components/mqtt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,7 @@ async def async_check_config_schema(
mqtt_config: list[dict[str, list[ConfigType]]] = config_yaml[DOMAIN]
for mqtt_config_item in mqtt_config:
for domain, config_items in mqtt_config_item.items():
if (schema := mqtt_data.reload_schema.get(domain)) is None:
continue
schema = mqtt_data.reload_schema[domain]
for config in config_items:
try:
schema(config)
Expand Down
14 changes: 2 additions & 12 deletions homeassistant/components/mqtt/config_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@
)
from homeassistant.helpers import config_validation as cv

from . import (
event as event_platform,
sensor as sensor_platform,
)
from .const import (
CONF_BIRTH_MESSAGE,
CONF_BROKER,
Expand All @@ -41,10 +37,7 @@
Platform.CLIMATE.value: vol.All(cv.ensure_list, [dict]),
Platform.COVER.value: vol.All(cv.ensure_list, [dict]),
Platform.DEVICE_TRACKER.value: vol.All(cv.ensure_list, [dict]),
Platform.EVENT.value: vol.All(
cv.ensure_list,
[event_platform.PLATFORM_SCHEMA_MODERN], # type: ignore[has-type]
),
Platform.EVENT.value: vol.All(cv.ensure_list, [dict]),
Platform.FAN.value: vol.All(cv.ensure_list, [dict]),
Platform.HUMIDIFIER.value: vol.All(cv.ensure_list, [dict]),
Platform.IMAGE.value: vol.All(cv.ensure_list, [dict]),
Expand All @@ -54,10 +47,7 @@
Platform.NUMBER.value: vol.All(cv.ensure_list, [dict]),
Platform.SCENE.value: vol.All(cv.ensure_list, [dict]),
Platform.SELECT.value: vol.All(cv.ensure_list, [dict]),
Platform.SENSOR.value: vol.All(
cv.ensure_list,
[sensor_platform.PLATFORM_SCHEMA_MODERN], # type: ignore[has-type]
),
Platform.SENSOR.value: vol.All(cv.ensure_list, [dict]),
Platform.SIREN.value: vol.All(cv.ensure_list, [dict]),
Platform.SWITCH.value: vol.All(cv.ensure_list, [dict]),
Platform.TEXT.value: vol.All(cv.ensure_list, [dict]),
Expand Down
27 changes: 10 additions & 17 deletions homeassistant/components/mqtt/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from __future__ import annotations

from collections.abc import Callable
import functools
import logging
from typing import Any

Expand All @@ -19,7 +18,7 @@
from homeassistant.core import HomeAssistant, callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.json import JSON_DECODE_EXCEPTIONS, json_loads_object

from . import subscription
Expand All @@ -35,7 +34,7 @@
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_setup_entry_helper,
async_mqtt_entry_helper,
write_state_on_attr_change,
)
from .models import (
Expand Down Expand Up @@ -83,21 +82,15 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT event through YAML and through MQTT discovery."""
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
await async_mqtt_entry_helper(
hass,
config_entry,
MqttEvent,
event.DOMAIN,
async_add_entities,
DISCOVERY_SCHEMA,
PLATFORM_SCHEMA_MODERN,
)
await async_setup_entry_helper(hass, event.DOMAIN, setup, DISCOVERY_SCHEMA)


async def _async_setup_entity(
hass: HomeAssistant,
async_add_entities: AddEntitiesCallback,
config: ConfigType,
config_entry: ConfigEntry,
discovery_data: DiscoveryInfoType | None = None,
) -> None:
"""Set up MQTT event."""
async_add_entities([MqttEvent(hass, config, config_entry, discovery_data)])


class MqttEvent(MqttEntity, EventEntity):
Expand Down
25 changes: 1 addition & 24 deletions homeassistant/components/mqtt/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from __future__ import annotations

from abc import ABC, abstractmethod
import asyncio
from collections.abc import Callable, Coroutine
import functools
from functools import partial, wraps
Expand Down Expand Up @@ -312,7 +311,7 @@ async def async_setup_entry_helper(
async_setup: partial[Coroutine[Any, Any, None]],
discovery_schema: vol.Schema,
) -> None:
"""Set up entity, automation or tag creation dynamically through MQTT discovery."""
"""Set up automation or tag creation dynamically through MQTT discovery."""
mqtt_data = get_mqtt_data(hass)

async def async_setup_from_discovery(
Expand All @@ -332,28 +331,6 @@ async def async_setup_from_discovery(
)
)

# The setup of manual configured MQTT entities will be migrated to async_mqtt_entry_helper.
# The following setup code will be cleaned up after the last entity platform has been migrated.
async def _async_setup_entities() -> None:
"""Set up MQTT items from configuration.yaml."""
mqtt_data = get_mqtt_data(hass)
if not (config_yaml := mqtt_data.config):
return
setups: list[Coroutine[Any, Any, None]] = [
async_setup(config)
for config_item in config_yaml
for config_domain, configs in config_item.items()
for config in configs
if config_domain == domain
]
if not setups:
return
await asyncio.gather(*setups)

# discover manual configured MQTT items
mqtt_data.reload_handlers[domain] = _async_setup_entities
await _async_setup_entities()


async def async_mqtt_entry_helper(
hass: HomeAssistant,
Expand Down
27 changes: 10 additions & 17 deletions homeassistant/components/mqtt/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from collections.abc import Callable
from datetime import datetime, timedelta
import functools
import logging
from typing import Any

Expand Down Expand Up @@ -33,7 +32,7 @@
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import dt as dt_util

from . import subscription
Expand All @@ -44,7 +43,7 @@
MQTT_ENTITY_COMMON_SCHEMA,
MqttAvailability,
MqttEntity,
async_setup_entry_helper,
async_mqtt_entry_helper,
write_state_on_attr_change,
)
from .models import (
Expand Down Expand Up @@ -106,21 +105,15 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT sensor through YAML and through MQTT discovery."""
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
await async_mqtt_entry_helper(
hass,
config_entry,
MqttSensor,
sensor.DOMAIN,
async_add_entities,
DISCOVERY_SCHEMA,
PLATFORM_SCHEMA_MODERN,
)
await async_setup_entry_helper(hass, sensor.DOMAIN, setup, DISCOVERY_SCHEMA)


async def _async_setup_entity(
hass: HomeAssistant,
async_add_entities: AddEntitiesCallback,
config: ConfigType,
config_entry: ConfigEntry,
discovery_data: DiscoveryInfoType | None = None,
) -> None:
"""Set up MQTT sensor."""
async_add_entities([MqttSensor(hass, config, config_entry, discovery_data)])


class MqttSensor(MqttEntity, RestoreSensor):
Expand Down
10 changes: 6 additions & 4 deletions tests/components/mqtt/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from unittest.mock import ANY, MagicMock, patch

import pytest
import voluptuous as vol
import yaml

from homeassistant import config as module_hass_config
Expand Down Expand Up @@ -363,6 +364,7 @@ async def help_test_default_availability_list_payload_any(

async def help_test_default_availability_list_single(
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
caplog: pytest.LogCaptureFixture,
domain: str,
config: ConfigType,
Expand All @@ -378,10 +380,10 @@ async def help_test_default_availability_list_single(
]
config[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic"

entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
entry.add_to_hass(hass)
with patch("homeassistant.config.load_yaml_config_file", return_value=config):
await entry.async_setup(hass)
with patch(
"homeassistant.config.load_yaml_config_file", return_value=config
), suppress(vol.MultipleInvalid):
await mqtt_mock_entry()

assert (
"two or more values in the same group of exclusion 'availability'"
Expand Down
13 changes: 6 additions & 7 deletions tests/components/mqtt/test_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,13 @@ async def test_default_availability_list_payload_any(


async def test_default_availability_list_single(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test availability list and availability_topic are mutually exclusive."""
await help_test_default_availability_list_single(
hass, caplog, event.DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry, caplog, event.DOMAIN, DEFAULT_CONFIG
)


Expand Down Expand Up @@ -271,11 +273,8 @@ async def test_invalid_device_class(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test device_class option with invalid value."""
with pytest.raises(AssertionError):
await mqtt_mock_entry()
assert (
"Invalid config for [mqtt]: expected EventDeviceClass or one of" in caplog.text
)
assert await mqtt_mock_entry()
assert "expected EventDeviceClass or one of" in caplog.text


async def test_setting_attribute_via_mqtt_json_message(
Expand Down
20 changes: 8 additions & 12 deletions tests/components/mqtt/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -708,11 +708,13 @@ async def test_default_availability_list_payload_any(


async def test_default_availability_list_single(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test availability list and availability_topic are mutually exclusive."""
await help_test_default_availability_list_single(
hass, caplog, sensor.DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry, caplog, sensor.DOMAIN, DEFAULT_CONFIG
)


Expand Down Expand Up @@ -754,11 +756,8 @@ async def test_invalid_device_class(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test device_class option with invalid value."""
with pytest.raises(AssertionError):
await mqtt_mock_entry()
assert (
"Invalid config for [mqtt]: expected SensorDeviceClass or one of" in caplog.text
)
assert await mqtt_mock_entry()
assert "expected SensorDeviceClass or one of" in caplog.text


@pytest.mark.parametrize(
Expand Down Expand Up @@ -818,11 +817,8 @@ async def test_invalid_state_class(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test state_class option with invalid value."""
with pytest.raises(AssertionError):
await mqtt_mock_entry()
assert (
"Invalid config for [mqtt]: expected SensorStateClass or one of" in caplog.text
)
assert await mqtt_mock_entry()
assert "expected SensorStateClass or one of" in caplog.text


@pytest.mark.parametrize(
Expand Down
Loading