Allow ADR 0007 compliant schema for mqtt (#94305)
* Enforce listed entities in MQTT yaml config * Add tests for setup with listed items * Fix test * Remove validator add comment * Update homeassistant/components/mqtt/__init__.py Co-authored-by: Erik Montnemery <erik@montnemery.com> --------- Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
f9a0877bb9
commit
7c22225cd1
28 changed files with 176 additions and 48 deletions
|
@ -5,7 +5,7 @@ import asyncio
|
|||
from collections.abc import Callable
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
from typing import Any, TypeVar, cast
|
||||
|
||||
import jinja2
|
||||
import voluptuous as vol
|
||||
|
@ -42,7 +42,7 @@ from .client import ( # noqa: F401
|
|||
publish,
|
||||
subscribe,
|
||||
)
|
||||
from .config_integration import PLATFORM_CONFIG_SCHEMA_BASE
|
||||
from .config_integration import CONFIG_SCHEMA_BASE
|
||||
from .const import ( # noqa: F401
|
||||
ATTR_PAYLOAD,
|
||||
ATTR_QOS,
|
||||
|
@ -130,25 +130,54 @@ CONFIG_ENTRY_CONFIG_KEYS = [
|
|||
CONF_WILL_MESSAGE,
|
||||
]
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
REMOVED_OPTIONS = vol.All(
|
||||
cv.removed(CONF_BIRTH_MESSAGE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_BROKER), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CERTIFICATE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CLIENT_ID), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CLIENT_CERT), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CLIENT_KEY), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_DISCOVERY), # Removed in HA Core 2022.3
|
||||
cv.removed(CONF_DISCOVERY_PREFIX), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_KEEPALIVE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_PASSWORD), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_PORT), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_PROTOCOL), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_TLS_INSECURE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_USERNAME), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_WILL_MESSAGE), # Removed in HA Core 2023.4
|
||||
)
|
||||
|
||||
# We accept 2 schemes for configuring manual MQTT items
|
||||
#
|
||||
# Preferred style:
|
||||
#
|
||||
# mqtt:
|
||||
# - {domain}:
|
||||
# name: ""
|
||||
# ...
|
||||
# - {domain}:
|
||||
# name: ""
|
||||
# ...
|
||||
# ```
|
||||
#
|
||||
# Legacy supported style:
|
||||
#
|
||||
# mqtt:
|
||||
# {domain}:
|
||||
# - name: ""
|
||||
# ...
|
||||
# - name: ""
|
||||
# ...
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.All(
|
||||
cv.removed(CONF_BIRTH_MESSAGE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_BROKER), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CERTIFICATE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CLIENT_ID), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CLIENT_CERT), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_CLIENT_KEY), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_DISCOVERY), # Removed in HA Core 2022.3
|
||||
cv.removed(CONF_DISCOVERY_PREFIX), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_KEEPALIVE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_PASSWORD), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_PORT), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_PROTOCOL), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_TLS_INSECURE), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_USERNAME), # Removed in HA Core 2023.4
|
||||
cv.removed(CONF_WILL_MESSAGE), # Removed in HA Core 2023.4
|
||||
PLATFORM_CONFIG_SCHEMA_BASE,
|
||||
cv.ensure_list,
|
||||
cv.remove_falsy,
|
||||
[REMOVED_OPTIONS],
|
||||
[CONFIG_SCHEMA_BASE],
|
||||
)
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
|
@ -190,7 +219,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
# Fetch configuration
|
||||
conf = dict(entry.data)
|
||||
hass_config = await conf_util.async_hass_config_yaml(hass)
|
||||
mqtt_yaml = PLATFORM_CONFIG_SCHEMA_BASE(hass_config.get(DOMAIN, {}))
|
||||
mqtt_yaml = CONFIG_SCHEMA(hass_config).get(DOMAIN, [])
|
||||
await async_create_certificate_temp_files(hass, conf)
|
||||
client = MQTT(hass, entry, conf)
|
||||
if DOMAIN in hass.data:
|
||||
|
|
|
@ -52,7 +52,7 @@ from .const import (
|
|||
|
||||
DEFAULT_TLS_PROTOCOL = "auto"
|
||||
|
||||
PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema(
|
||||
CONFIG_SCHEMA_BASE = vol.Schema(
|
||||
{
|
||||
Platform.ALARM_CONTROL_PANEL.value: vol.All(
|
||||
cv.ensure_list,
|
||||
|
|
|
@ -309,9 +309,16 @@ async def async_setup_entry_helper(
|
|||
mqtt_data = get_mqtt_data(hass)
|
||||
if not (config_yaml := mqtt_data.config):
|
||||
return
|
||||
if domain not in config_yaml:
|
||||
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(*[async_setup(config) for config in config_yaml[domain]])
|
||||
await asyncio.gather(*setups)
|
||||
|
||||
# discover manual configured MQTT items
|
||||
mqtt_data.reload_handlers[domain] = _async_setup_entities
|
||||
|
|
|
@ -289,7 +289,7 @@ class MqttData:
|
|||
"""Keep the MQTT entry data."""
|
||||
|
||||
client: MQTT
|
||||
config: ConfigType
|
||||
config: list[ConfigType]
|
||||
debug_info_entities: dict[str, EntityDebugInfo] = field(default_factory=dict)
|
||||
debug_info_triggers: dict[tuple[str, str], TriggerDebugInfo] = field(
|
||||
default_factory=dict
|
||||
|
|
|
@ -1096,7 +1096,11 @@ async def test_reloadable(
|
|||
await help_test_reloadable(hass, mqtt_client_mock, domain, config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -1203,7 +1203,11 @@ async def test_skip_restoring_state_with_over_due_expire_trigger(
|
|||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -545,7 +545,11 @@ async def test_reloadable(
|
|||
await help_test_reloadable(hass, mqtt_client_mock, domain, config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -439,7 +439,11 @@ async def test_reloadable(
|
|||
await help_test_reloadable(hass, mqtt_client_mock, domain, config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -2484,7 +2484,11 @@ async def test_reloadable(
|
|||
await help_test_reloadable(hass, mqtt_client_mock, domain, config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -3642,7 +3642,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -2220,7 +2220,11 @@ async def test_reloadable(
|
|||
await help_test_reloadable(hass, mqtt_client_mock, domain, config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -1545,7 +1545,11 @@ async def test_reloadable(
|
|||
await help_test_reloadable(hass, mqtt_client_mock, domain, config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -2106,8 +2106,8 @@ async def test_setup_manual_mqtt_with_invalid_config(
|
|||
with pytest.raises(AssertionError):
|
||||
await mqtt_mock_entry()
|
||||
assert (
|
||||
"Invalid config for [mqtt]: required key not provided @ data['mqtt']['light'][0]['command_topic']."
|
||||
" Got None. (See ?, line ?)" in caplog.text
|
||||
"Invalid config for [mqtt]: required key not provided @ data['mqtt'][0]['light'][0]['command_topic']. "
|
||||
"Got None. (See ?, line ?)" in caplog.text
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1087,7 +1087,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -3440,7 +3440,11 @@ async def test_sending_mqtt_xy_command_with_template(
|
|||
assert state.attributes["xy_color"] == (0.151, 0.343)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -2441,7 +2441,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -1354,7 +1354,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -1006,7 +1006,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -1097,7 +1097,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -251,7 +251,11 @@ async def test_reloadable(
|
|||
await help_test_reloadable(hass, mqtt_client_mock, domain, config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -762,7 +762,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -1385,7 +1385,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -1067,7 +1067,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -809,7 +809,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -738,7 +738,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -738,7 +738,11 @@ async def test_encoding_subscribable_topics(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -696,7 +696,11 @@ async def test_entity_id_update_discovery_update(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
|
@ -1087,7 +1087,11 @@ async def test_reloadable(
|
|||
await help_test_reloadable(hass, mqtt_client_mock, domain, config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[DEFAULT_CONFIG, {"mqtt": [DEFAULT_CONFIG["mqtt"]]}],
|
||||
ids=["platform_key", "listed"],
|
||||
)
|
||||
async def test_setup_manual_entity_from_yaml(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
|
Loading…
Add table
Reference in a new issue