This commit is contained in:
Franck Nijhof 2022-09-22 10:29:12 +02:00 committed by GitHub
commit b4f1683c40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 5395 additions and 4343 deletions

View File

@ -23,7 +23,8 @@ env:
CACHE_VERSION: 1
PIP_CACHE_VERSION: 1
HA_SHORT_VERSION: 2022.9
DEFAULT_PYTHON: 3.9
DEFAULT_PYTHON: 3.9.14
ALL_PYTHON_VERSIONS: "['3.9.14', '3.10.7']"
PRE_COMMIT_CACHE: ~/.cache/pre-commit
PIP_CACHE: /tmp/pip-cache
SQLALCHEMY_WARN_20: 1
@ -46,6 +47,7 @@ jobs:
pre-commit_cache_key: ${{ steps.generate_pre-commit_cache_key.outputs.key }}
python_cache_key: ${{ steps.generate_python_cache_key.outputs.key }}
requirements: ${{ steps.core.outputs.requirements }}
python_versions: ${{ steps.info.outputs.python_versions }}
test_full_suite: ${{ steps.info.outputs.test_full_suite }}
test_group_count: ${{ steps.info.outputs.test_group_count }}
test_groups: ${{ steps.info.outputs.test_groups }}
@ -143,6 +145,8 @@ jobs:
fi
# Output & sent to GitHub Actions
echo "python_versions: ${ALL_PYTHON_VERSIONS}"
echo "::set-output name=python_versions::${ALL_PYTHON_VERSIONS}"
echo "test_full_suite: ${test_full_suite}"
echo "::set-output name=test_full_suite::${test_full_suite}"
echo "integrations_glob: ${integrations_glob}"
@ -463,7 +467,7 @@ jobs:
timeout-minutes: 60
strategy:
matrix:
python-version: ["3.9", "3.10"]
python-version: ${{ fromJSON(needs.info.outputs.python_versions) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.0.2
@ -483,7 +487,7 @@ jobs:
with:
path: venv
key: >-
${{ runner.os }}-${{ matrix.python-version }}-${{
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Restore pip wheel cache
if: steps.cache-venv.outputs.cache-hit != 'true'
@ -491,10 +495,10 @@ jobs:
with:
path: ${{ env.PIP_CACHE }}
key: >-
${{ runner.os }}-${{ matrix.python-version }}-${{
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
steps.generate-pip-key.outputs.key }}
restore-keys: |
${{ runner.os }}-${{ matrix.python-version }}-pip-${{ env.PIP_CACHE_VERSION }}-${{ env.HA_SHORT_VERSION }}-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-pip-${{ env.PIP_CACHE_VERSION }}-${{ env.HA_SHORT_VERSION }}-
- name: Install additional OS dependencies
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
@ -541,7 +545,7 @@ jobs:
with:
path: venv
key: >-
${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-${{
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
@ -573,7 +577,7 @@ jobs:
with:
path: venv
key: >-
${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-${{
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
@ -606,7 +610,7 @@ jobs:
with:
path: venv
key: >-
${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-${{
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
@ -650,7 +654,7 @@ jobs:
with:
path: venv
key: >-
${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-${{
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
@ -682,7 +686,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10"]
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
name: Run pip check ${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
@ -698,7 +702,7 @@ jobs:
with:
path: venv
key: >-
${{ runner.os }}-${{ matrix.python-version }}-${{
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
@ -729,7 +733,7 @@ jobs:
fail-fast: false
matrix:
group: ${{ fromJson(needs.info.outputs.test_groups) }}
python-version: ["3.9", "3.10"]
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
name: >-
Run tests Python ${{ matrix.python-version }} (${{ matrix.group }})
steps:
@ -751,7 +755,7 @@ jobs:
uses: actions/cache@v3.0.8
with:
path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'

View File

@ -58,7 +58,7 @@ class AdapterDetails(TypedDict, total=False):
address: str
sw_version: str
hw_version: str
hw_version: str | None
passive_scan: bool

View File

@ -60,6 +60,7 @@ APPLE_DEVICE_ID_START_BYTE: Final = 0x10 # bluetooth_le_tracker
APPLE_START_BYTES_WANTED: Final = {APPLE_DEVICE_ID_START_BYTE, APPLE_HOMEKIT_START_BYTE}
RSSI_SWITCH_THRESHOLD = 6
NO_RSSI_VALUE = -1000
_LOGGER = logging.getLogger(__name__)
@ -83,7 +84,7 @@ def _prefer_previous_adv(
STALE_ADVERTISEMENT_SECONDS,
)
return False
if new.device.rssi - RSSI_SWITCH_THRESHOLD > old.device.rssi:
if new.device.rssi - RSSI_SWITCH_THRESHOLD > (old.device.rssi or NO_RSSI_VALUE):
# If new advertisement is RSSI_SWITCH_THRESHOLD more, the new one is preferred
if new.source != old.source:
_LOGGER.debug(

View File

@ -9,7 +9,7 @@
"bleak-retry-connector==1.17.1",
"bluetooth-adapters==0.4.1",
"bluetooth-auto-recovery==0.3.3",
"dbus-fast==1.4.0"
"dbus-fast==1.5.1"
],
"codeowners": ["@bdraco"],
"config_flow": true,

View File

@ -46,7 +46,7 @@ async def async_get_bluetooth_adapters() -> dict[str, AdapterDetails]:
adapters[adapter] = AdapterDetails(
address=adapter1["Address"],
sw_version=adapter1["Name"], # This is actually the BlueZ version
hw_version=adapter1["Modalias"],
hw_version=adapter1.get("Modalias"),
passive_scan="org.bluez.AdvertisementMonitorManager1" in details,
)
return adapters

View File

@ -2,7 +2,7 @@
"domain": "bmw_connected_drive",
"name": "BMW Connected Drive",
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"requirements": ["bimmer_connected==0.10.2"],
"requirements": ["bimmer_connected==0.10.4"],
"codeowners": ["@gerard33", "@rikroe"],
"config_flow": true,
"iot_class": "cloud_polling",

View File

@ -1,6 +1,7 @@
"""Config flow for Bond integration."""
from __future__ import annotations
import asyncio
import contextlib
from http import HTTPStatus
import logging
@ -83,8 +84,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
instead ask them to manually enter the token.
"""
host = self._discovered[CONF_HOST]
try:
if not (token := await async_get_token(self.hass, host)):
return
except asyncio.TimeoutError:
return
self._discovered[CONF_ACCESS_TOKEN] = token
try:

View File

@ -260,8 +260,6 @@ class BrightnessTrait(_Trait):
brightness = self.state.attributes.get(light.ATTR_BRIGHTNESS)
if brightness is not None:
response["brightness"] = round(100 * (brightness / 255))
else:
response["brightness"] = 0
return response

View File

@ -92,7 +92,10 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the valve off (closed)."""
"""Turn the switch off."""
if not self._attr_is_on:
return
try:
async with self._client:
await self._client.valve.close()
@ -103,7 +106,10 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
self.async_write_ha_state()
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the valve on (open)."""
"""Turn the switch on."""
if self._attr_is_on:
return
try:
async with self._client:
await self._client.valve.open()

View File

@ -3,7 +3,7 @@
"name": "HomeKit Controller",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
"requirements": ["aiohomekit==1.5.9"],
"requirements": ["aiohomekit==1.5.12"],
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."],
"bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }],
"dependencies": ["bluetooth", "zeroconf"],

View File

@ -2,7 +2,7 @@
"domain": "imap",
"name": "IMAP",
"documentation": "https://www.home-assistant.io/integrations/imap",
"requirements": ["aioimaplib==1.0.0"],
"requirements": ["aioimaplib==1.0.1"],
"codeowners": [],
"iot_class": "cloud_push",
"loggers": ["aioimaplib"]

View File

@ -20,13 +20,7 @@ from homeassistant.const import (
CONF_USERNAME,
SERVICE_RELOAD,
)
from homeassistant.core import (
CALLBACK_TYPE,
HassJob,
HomeAssistant,
ServiceCall,
callback,
)
from homeassistant.core import HassJob, HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import TemplateError, Unauthorized
from homeassistant.helpers import (
config_validation as cv,
@ -36,6 +30,7 @@ from homeassistant.helpers import (
)
from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import async_get_platforms
from homeassistant.helpers.reload import (
async_integration_yaml_config,
async_reload_integration_platforms,
@ -71,15 +66,7 @@ from .const import ( # noqa: F401
CONF_TLS_VERSION,
CONF_TOPIC,
CONF_WILL_MESSAGE,
CONFIG_ENTRY_IS_SETUP,
DATA_MQTT,
DATA_MQTT_CONFIG,
DATA_MQTT_DISCOVERY_REGISTRY_HOOKS,
DATA_MQTT_RELOAD_DISPATCHERS,
DATA_MQTT_RELOAD_ENTRY,
DATA_MQTT_RELOAD_NEEDED,
DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE,
DATA_MQTT_UPDATED_CONFIG,
DEFAULT_ENCODING,
DEFAULT_QOS,
DEFAULT_RETAIN,
@ -89,7 +76,7 @@ from .const import ( # noqa: F401
PLATFORMS,
RELOADABLE_PLATFORMS,
)
from .mixins import async_discover_yaml_entities
from .mixins import MqttData
from .models import ( # noqa: F401
MqttCommandTemplate,
MqttValueTemplate,
@ -177,6 +164,8 @@ async def _async_setup_discovery(
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Start the MQTT protocol service."""
mqtt_data: MqttData = hass.data.setdefault(DATA_MQTT, MqttData())
conf: ConfigType | None = config.get(DOMAIN)
websocket_api.async_register_command(hass, websocket_subscribe)
@ -185,7 +174,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
if conf:
conf = dict(conf)
hass.data[DATA_MQTT_CONFIG] = conf
mqtt_data.config = conf
if (mqtt_entry_status := mqtt_config_entry_enabled(hass)) is None:
# Create an import flow if the user has yaml configured entities etc.
@ -197,12 +186,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY},
data={},
)
hass.data[DATA_MQTT_RELOAD_NEEDED] = True
mqtt_data.reload_needed = True
elif mqtt_entry_status is False:
_LOGGER.info(
"MQTT will be not available until the config entry is enabled",
)
hass.data[DATA_MQTT_RELOAD_NEEDED] = True
mqtt_data.reload_needed = True
return True
@ -260,33 +249,34 @@ async def _async_config_entry_updated(hass: HomeAssistant, entry: ConfigEntry) -
Causes for this is config entry options changing.
"""
mqtt_client = hass.data[DATA_MQTT]
mqtt_data: MqttData = hass.data[DATA_MQTT]
assert (client := mqtt_data.client) is not None
if (conf := hass.data.get(DATA_MQTT_CONFIG)) is None:
if (conf := mqtt_data.config) is None:
conf = CONFIG_SCHEMA_BASE(dict(entry.data))
mqtt_client.conf = _merge_extended_config(entry, conf)
await mqtt_client.async_disconnect()
mqtt_client.init_client()
await mqtt_client.async_connect()
mqtt_data.config = _merge_extended_config(entry, conf)
await client.async_disconnect()
client.init_client()
await client.async_connect()
await discovery.async_stop(hass)
if mqtt_client.conf.get(CONF_DISCOVERY):
await _async_setup_discovery(hass, mqtt_client.conf, entry)
if client.conf.get(CONF_DISCOVERY):
await _async_setup_discovery(hass, cast(ConfigType, mqtt_data.config), entry)
async def async_fetch_config(hass: HomeAssistant, entry: ConfigEntry) -> dict | None:
"""Fetch fresh MQTT yaml config from the hass config when (re)loading the entry."""
if DATA_MQTT_RELOAD_ENTRY in hass.data:
mqtt_data: MqttData = hass.data[DATA_MQTT]
if mqtt_data.reload_entry:
hass_config = await conf_util.async_hass_config_yaml(hass)
mqtt_config = CONFIG_SCHEMA_BASE(hass_config.get(DOMAIN, {}))
hass.data[DATA_MQTT_CONFIG] = mqtt_config
mqtt_data.config = CONFIG_SCHEMA_BASE(hass_config.get(DOMAIN, {}))
# Remove unknown keys from config entry data
_filter_entry_config(hass, entry)
# Merge basic configuration, and add missing defaults for basic options
_merge_basic_config(hass, entry, hass.data.get(DATA_MQTT_CONFIG, {}))
_merge_basic_config(hass, entry, mqtt_data.config or {})
# Bail out if broker setting is missing
if CONF_BROKER not in entry.data:
_LOGGER.error("MQTT broker is not configured, please configure it")
@ -294,7 +284,7 @@ async def async_fetch_config(hass: HomeAssistant, entry: ConfigEntry) -> dict |
# If user doesn't have configuration.yaml config, generate default values
# for options not in config entry data
if (conf := hass.data.get(DATA_MQTT_CONFIG)) is None:
if (conf := mqtt_data.config) is None:
conf = CONFIG_SCHEMA_BASE(dict(entry.data))
# User has configuration.yaml config, warn about config entry overrides
@ -317,21 +307,20 @@ async def async_fetch_config(hass: HomeAssistant, entry: ConfigEntry) -> dict |
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Load a config entry."""
mqtt_data: MqttData = hass.data.setdefault(DATA_MQTT, MqttData())
# Merge basic configuration, and add missing defaults for basic options
if (conf := await async_fetch_config(hass, entry)) is None:
# Bail out
return False
hass.data[DATA_MQTT_DISCOVERY_REGISTRY_HOOKS] = {}
hass.data[DATA_MQTT] = MQTT(hass, entry, conf)
mqtt_data.client = MQTT(hass, entry, conf)
# Restore saved subscriptions
if DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE in hass.data:
hass.data[DATA_MQTT].subscriptions = hass.data.pop(
DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE
)
if mqtt_data.subscriptions_to_restore:
mqtt_data.client.subscriptions = mqtt_data.subscriptions_to_restore
mqtt_data.subscriptions_to_restore = []
entry.add_update_listener(_async_config_entry_updated)
await hass.data[DATA_MQTT].async_connect()
await mqtt_data.client.async_connect()
async def async_publish_service(call: ServiceCall) -> None:
"""Handle MQTT publish service calls."""
@ -380,7 +369,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
return
await hass.data[DATA_MQTT].async_publish(msg_topic, payload, qos, retain)
assert mqtt_data.client is not None and msg_topic is not None
await mqtt_data.client.async_publish(msg_topic, payload, qos, retain)
hass.services.async_register(
DOMAIN, SERVICE_PUBLISH, async_publish_service, schema=MQTT_PUBLISH_SCHEMA
@ -421,7 +411,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
# setup platforms and discovery
hass.data[CONFIG_ENTRY_IS_SETUP] = set()
async def async_setup_reload_service() -> None:
"""Create the reload service for the MQTT domain."""
@ -434,13 +423,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await async_reload_integration_platforms(hass, DOMAIN, RELOADABLE_PLATFORMS)
# Reload the modern yaml platforms
mqtt_platforms = async_get_platforms(hass, DOMAIN)
tasks = [
entity.async_remove()
for mqtt_platform in mqtt_platforms
for entity in mqtt_platform.entities.values()
if not entity._discovery_data # type: ignore[attr-defined] # pylint: disable=protected-access
if mqtt_platform.config_entry
and mqtt_platform.domain in RELOADABLE_PLATFORMS
]
await asyncio.gather(*tasks)
config_yaml = await async_integration_yaml_config(hass, DOMAIN) or {}
hass.data[DATA_MQTT_UPDATED_CONFIG] = config_yaml.get(DOMAIN, {})
mqtt_data.updated_config = config_yaml.get(DOMAIN, {})
await asyncio.gather(
*(
[
async_discover_yaml_entities(hass, component)
mqtt_data.reload_handlers[component]()
for component in RELOADABLE_PLATFORMS
if component in mqtt_data.reload_handlers
]
)
)
@ -476,13 +477,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Setup reload service after all platforms have loaded
await async_setup_reload_service()
# When the entry is reloaded, also reload manual set up items to enable MQTT
if DATA_MQTT_RELOAD_ENTRY in hass.data:
hass.data.pop(DATA_MQTT_RELOAD_ENTRY)
if mqtt_data.reload_entry:
mqtt_data.reload_entry = False
reload_manual_setup = True
# When the entry was disabled before, reload manual set up items to enable MQTT again
if DATA_MQTT_RELOAD_NEEDED in hass.data:
hass.data.pop(DATA_MQTT_RELOAD_NEEDED)
if mqtt_data.reload_needed:
mqtt_data.reload_needed = False
reload_manual_setup = True
if reload_manual_setup:
@ -592,7 +593,9 @@ def async_subscribe_connection_status(
def is_connected(hass: HomeAssistant) -> bool:
"""Return if MQTT client is connected."""
return hass.data[DATA_MQTT].connected
mqtt_data: MqttData = hass.data[DATA_MQTT]
assert mqtt_data.client is not None
return mqtt_data.client.connected
async def async_remove_config_entry_device(
@ -608,6 +611,10 @@ async def async_remove_config_entry_device(
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload MQTT dump and publish service when the config entry is unloaded."""
mqtt_data: MqttData = hass.data[DATA_MQTT]
assert mqtt_data.client is not None
mqtt_client = mqtt_data.client
# Unload publish and dump services.
hass.services.async_remove(
DOMAIN,
@ -620,7 +627,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Stop the discovery
await discovery.async_stop(hass)
mqtt_client: MQTT = hass.data[DATA_MQTT]
# Unload the platforms
await asyncio.gather(
*(
@ -630,26 +636,23 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
await hass.async_block_till_done()
# Unsubscribe reload dispatchers
while reload_dispatchers := hass.data.setdefault(DATA_MQTT_RELOAD_DISPATCHERS, []):
while reload_dispatchers := mqtt_data.reload_dispatchers:
reload_dispatchers.pop()()
hass.data[CONFIG_ENTRY_IS_SETUP] = set()
# Cleanup listeners
mqtt_client.cleanup()
# Trigger reload manual MQTT items at entry setup
if (mqtt_entry_status := mqtt_config_entry_enabled(hass)) is False:
# The entry is disabled reload legacy manual items when the entry is enabled again
hass.data[DATA_MQTT_RELOAD_NEEDED] = True
mqtt_data.reload_needed = True
elif mqtt_entry_status is True:
# The entry is reloaded:
# Trigger re-fetching the yaml config at entry setup
hass.data[DATA_MQTT_RELOAD_ENTRY] = True
mqtt_data.reload_entry = True
# Reload the legacy yaml platform to make entities unavailable
await async_reload_integration_platforms(hass, DOMAIN, RELOADABLE_PLATFORMS)
# Cleanup entity registry hooks
registry_hooks: dict[tuple, CALLBACK_TYPE] = hass.data[
DATA_MQTT_DISCOVERY_REGISTRY_HOOKS
]
registry_hooks = mqtt_data.discovery_registry_hooks
while registry_hooks:
registry_hooks.popitem()[1]()
# Wait for all ACKs and stop the loop
@ -657,6 +660,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Store remaining subscriptions to be able to restore or reload them
# when the entry is set up again
if mqtt_client.subscriptions:
hass.data[DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE] = mqtt_client.subscriptions
mqtt_data.subscriptions_to_restore = mqtt_client.subscriptions
return True

View File

@ -44,7 +44,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -146,9 +145,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT alarm control panel through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, alarm.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -42,7 +42,6 @@ from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttAvailability,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -102,9 +101,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT binary sensor through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, binary_sensor.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -25,7 +25,6 @@ from .const import (
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -81,9 +80,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT button through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, button.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -23,7 +23,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -105,9 +104,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT camera through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, camera.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -2,7 +2,7 @@
from __future__ import annotations
import asyncio
from collections.abc import Awaitable, Callable, Coroutine, Iterable
from collections.abc import Callable, Coroutine, Iterable
from functools import lru_cache, partial, wraps
import inspect
from itertools import groupby
@ -17,6 +17,7 @@ import attr
import certifi
from paho.mqtt.client import MQTTMessage
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_CLIENT_ID,
CONF_PASSWORD,
@ -52,7 +53,6 @@ from .const import (
MQTT_DISCONNECTED,
PROTOCOL_31,
)
from .discovery import LAST_DISCOVERY
from .models import (
AsyncMessageCallbackType,
MessageCallbackType,
@ -68,6 +68,9 @@ if TYPE_CHECKING:
# because integrations should be able to optionally rely on MQTT.
import paho.mqtt.client as mqtt
from .mixins import MqttData
_LOGGER = logging.getLogger(__name__)
DISCOVERY_COOLDOWN = 2
@ -97,8 +100,12 @@ async def async_publish(
encoding: str | None = DEFAULT_ENCODING,
) -> None:
"""Publish message to a MQTT topic."""
# Local import to avoid circular dependencies
# pylint: disable-next=import-outside-toplevel
from .mixins import MqttData
if DATA_MQTT not in hass.data or not mqtt_config_entry_enabled(hass):
mqtt_data: MqttData = hass.data.setdefault(DATA_MQTT, MqttData())
if mqtt_data.client is None or not mqtt_config_entry_enabled(hass):
raise HomeAssistantError(
f"Cannot publish to topic '{topic}', MQTT is not enabled"
)
@ -126,11 +133,13 @@ async def async_publish(
)
return
await hass.data[DATA_MQTT].async_publish(topic, outgoing_payload, qos, retain)
await mqtt_data.client.async_publish(
topic, outgoing_payload, qos or 0, retain or False
)
AsyncDeprecatedMessageCallbackType = Callable[
[str, ReceivePayloadType, int], Awaitable[None]
[str, ReceivePayloadType, int], Coroutine[Any, Any, None]
]
DeprecatedMessageCallbackType = Callable[[str, ReceivePayloadType, int], None]
@ -175,13 +184,18 @@ async def async_subscribe(
| DeprecatedMessageCallbackType
| AsyncDeprecatedMessageCallbackType,
qos: int = DEFAULT_QOS,
encoding: str | None = "utf-8",
encoding: str | None = DEFAULT_ENCODING,
):
"""Subscribe to an MQTT topic.
Call the return value to unsubscribe.
"""
if DATA_MQTT not in hass.data or not mqtt_config_entry_enabled(hass):
# Local import to avoid circular dependencies
# pylint: disable-next=import-outside-toplevel
from .mixins import MqttData
mqtt_data: MqttData = hass.data.setdefault(DATA_MQTT, MqttData())
if mqtt_data.client is None or not mqtt_config_entry_enabled(hass):
raise HomeAssistantError(
f"Cannot subscribe to topic '{topic}', MQTT is not enabled"
)
@ -206,7 +220,7 @@ async def async_subscribe(
cast(DeprecatedMessageCallbackType, msg_callback)
)
async_remove = await hass.data[DATA_MQTT].async_subscribe(
async_remove = await mqtt_data.client.async_subscribe(
topic,
catch_log_exception(
wrapped_msg_callback,
@ -309,15 +323,17 @@ class MQTT:
def __init__(
self,
hass,
config_entry,
conf,
hass: HomeAssistant,
config_entry: ConfigEntry,
conf: ConfigType,
) -> None:
"""Initialize Home Assistant MQTT client."""
# We don't import on the top because some integrations
# should be able to optionally rely on MQTT.
import paho.mqtt.client as mqtt # pylint: disable=import-outside-toplevel
self._mqtt_data: MqttData = hass.data[DATA_MQTT]
self.hass = hass
self.config_entry = config_entry
self.conf = conf
@ -634,7 +650,6 @@ class MQTT:
subscription.job,
)
continue
self.hass.async_run_hass_job(
subscription.job,
ReceiveMessage(
@ -694,10 +709,10 @@ class MQTT:
async def _discovery_cooldown(self):
now = time.time()
# Reset discovery and subscribe cooldowns
self.hass.data[LAST_DISCOVERY] = now
self._mqtt_data.last_discovery = now
self._last_subscribe = now
last_discovery = self.hass.data[LAST_DISCOVERY]
last_discovery = self._mqtt_data.last_discovery
last_subscribe = self._last_subscribe
wait_until = max(
last_discovery + DISCOVERY_COOLDOWN, last_subscribe + DISCOVERY_COOLDOWN
@ -705,7 +720,7 @@ class MQTT:
while now < wait_until:
await asyncio.sleep(wait_until - now)
now = time.time()
last_discovery = self.hass.data[LAST_DISCOVERY]
last_discovery = self._mqtt_data.last_discovery
last_subscribe = self._last_subscribe
wait_until = max(
last_discovery + DISCOVERY_COOLDOWN, last_subscribe + DISCOVERY_COOLDOWN

View File

@ -50,7 +50,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -350,9 +349,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT climate device through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, climate.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -18,7 +18,7 @@ from homeassistant.const import (
CONF_PROTOCOL,
CONF_USERNAME,
)
from homeassistant.core import callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult
from .client import MqttClientSetup
@ -30,12 +30,13 @@ from .const import (
CONF_BIRTH_MESSAGE,
CONF_BROKER,
CONF_WILL_MESSAGE,
DATA_MQTT_CONFIG,
DATA_MQTT,
DEFAULT_BIRTH,
DEFAULT_DISCOVERY,
DEFAULT_WILL,
DOMAIN,
)
from .mixins import MqttData
from .util import MQTT_WILL_BIRTH_SCHEMA
MQTT_TIMEOUT = 5
@ -164,9 +165,10 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow):
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Manage the MQTT broker configuration."""
mqtt_data: MqttData = self.hass.data.setdefault(DATA_MQTT, MqttData())
errors = {}
current_config = self.config_entry.data
yaml_config = self.hass.data.get(DATA_MQTT_CONFIG, {})
yaml_config = mqtt_data.config or {}
if user_input is not None:
can_connect = await self.hass.async_add_executor_job(
try_connection,
@ -214,9 +216,10 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow):
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Manage the MQTT options."""
mqtt_data: MqttData = self.hass.data.setdefault(DATA_MQTT, MqttData())
errors = {}
current_config = self.config_entry.data
yaml_config = self.hass.data.get(DATA_MQTT_CONFIG, {})
yaml_config = mqtt_data.config or {}
options_config: dict[str, Any] = {}
if user_input is not None:
bad_birth = False
@ -334,14 +337,22 @@ class MQTTOptionsFlowHandler(config_entries.OptionsFlow):
)
def try_connection(hass, broker, port, username, password, protocol="3.1"):
def try_connection(
hass: HomeAssistant,
broker: str,
port: int,
username: str | None,
password: str | None,
protocol: str = "3.1",
) -> bool:
"""Test if we can connect to an MQTT broker."""
# We don't import on the top because some integrations
# should be able to optionally rely on MQTT.
import paho.mqtt.client as mqtt # pylint: disable=import-outside-toplevel
# Get the config from configuration.yaml
yaml_config = hass.data.get(DATA_MQTT_CONFIG, {})
mqtt_data: MqttData = hass.data.setdefault(DATA_MQTT, MqttData())
yaml_config = mqtt_data.config or {}
entry_config = {
CONF_BROKER: broker,
CONF_PORT: port,
@ -351,7 +362,7 @@ def try_connection(hass, broker, port, username, password, protocol="3.1"):
}
client = MqttClientSetup({**yaml_config, **entry_config}).client
result = queue.Queue(maxsize=1)
result: queue.Queue[bool] = queue.Queue(maxsize=1)
def on_connect(client_, userdata, flags, result_code):
"""Handle connection result."""

View File

@ -30,16 +30,8 @@ CONF_CLIENT_CERT = "client_cert"
CONF_TLS_INSECURE = "tls_insecure"
CONF_TLS_VERSION = "tls_version"
CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup"
DATA_MQTT = "mqtt"
DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE = "mqtt_client_subscriptions"
DATA_MQTT_DISCOVERY_REGISTRY_HOOKS = "mqtt_discovery_registry_hooks"
DATA_MQTT_CONFIG = "mqtt_config"
MQTT_DATA_DEVICE_TRACKER_LEGACY = "mqtt_device_tracker_legacy"
DATA_MQTT_RELOAD_DISPATCHERS = "mqtt_reload_dispatchers"
DATA_MQTT_RELOAD_ENTRY = "mqtt_reload_entry"
DATA_MQTT_RELOAD_NEEDED = "mqtt_reload_needed"
DATA_MQTT_UPDATED_CONFIG = "mqtt_updated_config"
DEFAULT_PREFIX = "homeassistant"
DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status"

View File

@ -46,7 +46,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -242,9 +241,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT cover through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, cover.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -33,11 +33,13 @@ from .const import (
CONF_PAYLOAD,
CONF_QOS,
CONF_TOPIC,
DATA_MQTT,
DOMAIN,
)
from .discovery import MQTT_DISCOVERY_DONE
from .mixins import (
MQTT_ENTITY_DEVICE_INFO_SCHEMA,
MqttData,
MqttDiscoveryDeviceUpdate,
send_discovery_done,
update_device,
@ -81,8 +83,6 @@ TRIGGER_DISCOVERY_SCHEMA = MQTT_BASE_SCHEMA.extend(
extra=vol.REMOVE_EXTRA,
)
DEVICE_TRIGGERS = "mqtt_device_triggers"
LOG_NAME = "Device trigger"
@ -203,6 +203,7 @@ class MqttDeviceTrigger(MqttDiscoveryDeviceUpdate):
self.device_id = device_id
self.discovery_data = discovery_data
self.hass = hass
self._mqtt_data: MqttData = hass.data[DATA_MQTT]
MqttDiscoveryDeviceUpdate.__init__(
self,
@ -217,8 +218,8 @@ class MqttDeviceTrigger(MqttDiscoveryDeviceUpdate):
"""Initialize the device trigger."""
discovery_hash = self.discovery_data[ATTR_DISCOVERY_HASH]
discovery_id = discovery_hash[1]
if discovery_id not in self.hass.data.setdefault(DEVICE_TRIGGERS, {}):
self.hass.data[DEVICE_TRIGGERS][discovery_id] = Trigger(
if discovery_id not in self._mqtt_data.device_triggers:
self._mqtt_data.device_triggers[discovery_id] = Trigger(
hass=self.hass,
device_id=self.device_id,
discovery_data=self.discovery_data,
@ -230,7 +231,7 @@ class MqttDeviceTrigger(MqttDiscoveryDeviceUpdate):
value_template=self._config[CONF_VALUE_TEMPLATE],
)
else:
await self.hass.data[DEVICE_TRIGGERS][discovery_id].update_trigger(
await self._mqtt_data.device_triggers[discovery_id].update_trigger(
self._config
)
debug_info.add_trigger_discovery_data(
@ -246,16 +247,16 @@ class MqttDeviceTrigger(MqttDiscoveryDeviceUpdate):
)
config = TRIGGER_DISCOVERY_SCHEMA(discovery_data)
update_device(self.hass, self._config_entry, config)
device_trigger: Trigger = self.hass.data[DEVICE_TRIGGERS][discovery_id]
device_trigger: Trigger = self._mqtt_data.device_triggers[discovery_id]
await device_trigger.update_trigger(config)
async def async_tear_down(self) -> None:
"""Cleanup device trigger."""
discovery_hash = self.discovery_data[ATTR_DISCOVERY_HASH]
discovery_id = discovery_hash[1]
if discovery_id in self.hass.data[DEVICE_TRIGGERS]:
if discovery_id in self._mqtt_data.device_triggers:
_LOGGER.info("Removing trigger: %s", discovery_hash)
trigger: Trigger = self.hass.data[DEVICE_TRIGGERS][discovery_id]
trigger: Trigger = self._mqtt_data.device_triggers[discovery_id]
trigger.detach_trigger()
debug_info.remove_trigger_discovery_data(self.hass, discovery_hash)
@ -280,11 +281,10 @@ async def async_setup_trigger(
async def async_removed_from_device(hass: HomeAssistant, device_id: str) -> None:
"""Handle Mqtt removed from a device."""
mqtt_data: MqttData = hass.data[DATA_MQTT]
triggers = await async_get_triggers(hass, device_id)
for trig in triggers:
device_trigger: Trigger = hass.data[DEVICE_TRIGGERS].pop(
trig[CONF_DISCOVERY_ID]
)
device_trigger: Trigger = mqtt_data.device_triggers.pop(trig[CONF_DISCOVERY_ID])
if device_trigger:
device_trigger.detach_trigger()
discovery_data = cast(dict, device_trigger.discovery_data)
@ -296,12 +296,13 @@ async def async_get_triggers(
hass: HomeAssistant, device_id: str
) -> list[dict[str, str]]:
"""List device triggers for MQTT devices."""
mqtt_data: MqttData = hass.data[DATA_MQTT]
triggers: list[dict[str, str]] = []
if DEVICE_TRIGGERS not in hass.data:
if not mqtt_data.device_triggers:
return triggers
for discovery_id, trig in hass.data[DEVICE_TRIGGERS].items():
for discovery_id, trig in mqtt_data.device_triggers.items():
if trig.device_id != device_id or trig.topic is None:
continue
@ -324,12 +325,12 @@ async def async_attach_trigger(
trigger_info: TriggerInfo,
) -> CALLBACK_TYPE:
"""Attach a trigger."""
hass.data.setdefault(DEVICE_TRIGGERS, {})
mqtt_data: MqttData = hass.data[DATA_MQTT]
device_id = config[CONF_DEVICE_ID]
discovery_id = config[CONF_DISCOVERY_ID]
if discovery_id not in hass.data[DEVICE_TRIGGERS]:
hass.data[DEVICE_TRIGGERS][discovery_id] = Trigger(
if discovery_id not in mqtt_data.device_triggers:
mqtt_data.device_triggers[discovery_id] = Trigger(
hass=hass,
device_id=device_id,
discovery_data=None,
@ -340,6 +341,6 @@ async def async_attach_trigger(
qos=None,
value_template=None,
)
return await hass.data[DEVICE_TRIGGERS][discovery_id].add_trigger(
return await mqtt_data.device_triggers[discovery_id].add_trigger(
action, trigger_info
)

View File

@ -43,7 +43,7 @@ def _async_get_diagnostics(
device: DeviceEntry | None = None,
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
mqtt_instance: MQTT = hass.data[DATA_MQTT]
mqtt_instance: MQTT = hass.data[DATA_MQTT].client
redacted_config = async_redact_data(mqtt_instance.conf, REDACT_CONFIG)

View File

@ -7,6 +7,7 @@ import functools
import logging
import re
import time
from typing import TYPE_CHECKING
from homeassistant.const import CONF_DEVICE, CONF_PLATFORM
from homeassistant.core import HomeAssistant
@ -28,9 +29,13 @@ from .const import (
ATTR_DISCOVERY_TOPIC,
CONF_AVAILABILITY,
CONF_TOPIC,
DATA_MQTT,
DOMAIN,
)
if TYPE_CHECKING:
from .mixins import MqttData
_LOGGER = logging.getLogger(__name__)
TOPIC_MATCHER = re.compile(
@ -69,7 +74,6 @@ INTEGRATION_UNSUBSCRIBE = "mqtt_integration_discovery_unsubscribe"
MQTT_DISCOVERY_UPDATED = "mqtt_discovery_updated_{}"
MQTT_DISCOVERY_NEW = "mqtt_discovery_new_{}_{}"
MQTT_DISCOVERY_DONE = "mqtt_discovery_done_{}"
LAST_DISCOVERY = "mqtt_last_discovery"
TOPIC_BASE = "~"
@ -80,12 +84,12 @@ class MQTTConfig(dict):
discovery_data: dict
def clear_discovery_hash(hass: HomeAssistant, discovery_hash: tuple) -> None:
def clear_discovery_hash(hass: HomeAssistant, discovery_hash: tuple[str, str]) -> None:
"""Clear entry in ALREADY_DISCOVERED list."""
del hass.data[ALREADY_DISCOVERED][discovery_hash]
def set_discovery_hash(hass: HomeAssistant, discovery_hash: tuple):
def set_discovery_hash(hass: HomeAssistant, discovery_hash: tuple[str, str]):
"""Clear entry in ALREADY_DISCOVERED list."""
hass.data[ALREADY_DISCOVERED][discovery_hash] = {}
@ -94,11 +98,12 @@ async def async_start( # noqa: C901
hass: HomeAssistant, discovery_topic, config_entry=None
) -> None:
"""Start MQTT Discovery."""
mqtt_data: MqttData = hass.data[DATA_MQTT]
mqtt_integrations = {}
async def async_discovery_message_received(msg):
"""Process the received message."""
hass.data[LAST_DISCOVERY] = time.time()
mqtt_data.last_discovery = time.time()
payload = msg.payload
topic = msg.topic
topic_trimmed = topic.replace(f"{discovery_topic}/", "", 1)
@ -253,7 +258,7 @@ async def async_start( # noqa: C901
)
)
hass.data[LAST_DISCOVERY] = time.time()
mqtt_data.last_discovery = time.time()
mqtt_integrations = await async_get_mqtt(hass)
hass.data[INTEGRATION_UNSUBSCRIBE] = {}

View File

@ -50,7 +50,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -241,9 +240,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT fan through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, fan.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -46,7 +46,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -187,9 +186,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT humidifier through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, humidifier.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -14,7 +14,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from ..mixins import (
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -111,9 +110,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT lights configured under the light platform key (deprecated)."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, light.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -28,7 +28,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -102,9 +101,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT lock through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, lock.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -4,9 +4,10 @@ from __future__ import annotations
from abc import abstractmethod
import asyncio
from collections.abc import Callable, Coroutine
from dataclasses import dataclass, field
from functools import partial
import logging
from typing import Any, Protocol, cast, final
from typing import TYPE_CHECKING, Any, Protocol, cast, final
import voluptuous as vol
@ -38,7 +39,6 @@ from homeassistant.core import (
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
discovery,
entity_registry as er,
)
from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED
@ -60,7 +60,7 @@ from homeassistant.helpers.json import json_loads
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import debug_info, subscription
from .client import async_publish
from .client import MQTT, Subscription, async_publish
from .const import (
ATTR_DISCOVERY_HASH,
ATTR_DISCOVERY_PAYLOAD,
@ -70,11 +70,6 @@ from .const import (
CONF_QOS,
CONF_TOPIC,
DATA_MQTT,
DATA_MQTT_CONFIG,
DATA_MQTT_DISCOVERY_REGISTRY_HOOKS,
DATA_MQTT_RELOAD_DISPATCHERS,
DATA_MQTT_RELOAD_ENTRY,
DATA_MQTT_UPDATED_CONFIG,
DEFAULT_ENCODING,
DEFAULT_PAYLOAD_AVAILABLE,
DEFAULT_PAYLOAD_NOT_AVAILABLE,
@ -98,6 +93,9 @@ from .subscription import (
)
from .util import mqtt_config_entry_enabled, valid_subscribe_topic
if TYPE_CHECKING:
from .device_trigger import Trigger
_LOGGER = logging.getLogger(__name__)
AVAILABILITY_ALL = "all"
@ -274,6 +272,27 @@ def warn_for_legacy_schema(domain: str) -> Callable:
return validator
@dataclass
class MqttData:
"""Keep the MQTT entry data."""
client: MQTT | None = None
config: ConfigType | None = None
device_triggers: dict[str, Trigger] = field(default_factory=dict)
discovery_registry_hooks: dict[tuple[str, str], CALLBACK_TYPE] = field(
default_factory=dict
)
last_discovery: float = 0.0
reload_dispatchers: list[CALLBACK_TYPE] = field(default_factory=list)
reload_entry: bool = False
reload_handlers: dict[str, Callable[[], Coroutine[Any, Any, None]]] = field(
default_factory=dict
)
reload_needed: bool = False
subscriptions_to_restore: list[Subscription] = field(default_factory=list)
updated_config: ConfigType = field(default_factory=dict)
class SetupEntity(Protocol):
"""Protocol type for async_setup_entities."""
@ -288,29 +307,6 @@ class SetupEntity(Protocol):
"""Define setup_entities type."""
async def async_discover_yaml_entities(
hass: HomeAssistant, platform_domain: str
) -> None:
"""Discover entities for a platform."""
if DATA_MQTT_UPDATED_CONFIG in hass.data:
# The platform has been reloaded
config_yaml = hass.data[DATA_MQTT_UPDATED_CONFIG]
else:
config_yaml = hass.data.get(DATA_MQTT_CONFIG, {})
if not config_yaml:
return
if platform_domain not in config_yaml:
return
await asyncio.gather(
*(
discovery.async_load_platform(hass, platform_domain, DOMAIN, config, {})
for config in await async_get_platform_config_from_yaml(
hass, platform_domain, config_yaml
)
)
)
async def async_get_platform_config_from_yaml(
hass: HomeAssistant,
platform_domain: str,
@ -318,8 +314,9 @@ async def async_get_platform_config_from_yaml(
) -> list[ConfigType]:
"""Return a list of validated configurations for the domain."""
mqtt_data: MqttData = hass.data[DATA_MQTT]
if config_yaml is None:
config_yaml = hass.data.get(DATA_MQTT_CONFIG)
config_yaml = mqtt_data.config
if not config_yaml:
return []
if not (platform_configs := config_yaml.get(platform_domain)):
@ -331,9 +328,10 @@ async def async_setup_entry_helper(
hass: HomeAssistant,
domain: str,
async_setup: partial[Coroutine[HomeAssistant, str, None]],
schema: vol.Schema,
discovery_schema: vol.Schema,
) -> None:
"""Set up entity, automation or tag creation dynamically through MQTT discovery."""
mqtt_data: MqttData = hass.data[DATA_MQTT]
async def async_discover(discovery_payload):
"""Discover and add an MQTT entity, automation or tag."""
@ -347,7 +345,7 @@ async def async_setup_entry_helper(
return
discovery_data = discovery_payload.discovery_data
try:
config = schema(discovery_payload)
config = discovery_schema(discovery_payload)
await async_setup(config, discovery_data=discovery_data)
except Exception:
discovery_hash = discovery_data[ATTR_DISCOVERY_HASH]
@ -357,12 +355,37 @@ async def async_setup_entry_helper(
)
raise
hass.data.setdefault(DATA_MQTT_RELOAD_DISPATCHERS, []).append(
mqtt_data.reload_dispatchers.append(
async_dispatcher_connect(
hass, MQTT_DISCOVERY_NEW.format(domain, "mqtt"), async_discover
)
)
async def _async_setup_entities() -> None:
"""Set up MQTT items from configuration.yaml."""
mqtt_data: MqttData = hass.data[DATA_MQTT]
if mqtt_data.updated_config:
# The platform has been reloaded
config_yaml = mqtt_data.updated_config
else:
config_yaml = mqtt_data.config or {}
if not config_yaml:
return
if domain not in config_yaml:
return
await asyncio.gather(
*[
async_setup(config)
for config in await async_get_platform_config_from_yaml(
hass, domain, config_yaml
)
]
)
# discover manual configured MQTT items
mqtt_data.reload_handlers[domain] = _async_setup_entities
await _async_setup_entities()
async def async_setup_platform_helper(
hass: HomeAssistant,
@ -372,7 +395,8 @@ async def async_setup_platform_helper(
async_setup_entities: SetupEntity,
) -> None:
"""Help to set up the platform for manual configured MQTT entities."""
if DATA_MQTT_RELOAD_ENTRY in hass.data:
mqtt_data: MqttData = hass.data[DATA_MQTT]
if mqtt_data.reload_entry:
_LOGGER.debug(
"MQTT integration is %s, skipping setup of manually configured MQTT items while unloading the config entry",
platform_domain,
@ -597,7 +621,10 @@ class MqttAvailability(Entity):
@property
def available(self) -> bool:
"""Return if the device is available."""
if not self.hass.data[DATA_MQTT].connected and not self.hass.is_stopping:
mqtt_data: MqttData = self.hass.data[DATA_MQTT]
assert mqtt_data.client is not None
client = mqtt_data.client
if not client.connected and not self.hass.is_stopping:
return False
if not self._avail_topics:
return True
@ -632,7 +659,7 @@ async def cleanup_device_registry(
)
def get_discovery_hash(discovery_data: dict) -> tuple:
def get_discovery_hash(discovery_data: dict) -> tuple[str, str]:
"""Get the discovery hash from the discovery data."""
return discovery_data[ATTR_DISCOVERY_HASH]
@ -817,9 +844,8 @@ class MqttDiscoveryUpdate(Entity):
self._removed_from_hass = False
if discovery_data is None:
return
self._registry_hooks: dict[tuple, CALLBACK_TYPE] = hass.data[
DATA_MQTT_DISCOVERY_REGISTRY_HOOKS
]
mqtt_data: MqttData = hass.data[DATA_MQTT]
self._registry_hooks = mqtt_data.discovery_registry_hooks
discovery_hash: tuple[str, str] = discovery_data[ATTR_DISCOVERY_HASH]
if discovery_hash in self._registry_hooks:
self._registry_hooks.pop(discovery_hash)()
@ -897,7 +923,7 @@ class MqttDiscoveryUpdate(Entity):
def add_to_platform_abort(self) -> None:
"""Abort adding an entity to a platform."""
if self._discovery_data is not None:
discovery_hash: tuple = self._discovery_data[ATTR_DISCOVERY_HASH]
discovery_hash: tuple[str, str] = self._discovery_data[ATTR_DISCOVERY_HASH]
if self.registry_entry is not None:
self._registry_hooks[
discovery_hash

View File

@ -44,7 +44,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -138,9 +137,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT number through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, number.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -22,7 +22,6 @@ from .mixins import (
CONF_OBJECT_ID,
MQTT_AVAILABILITY_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -78,9 +77,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT scene through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, scene.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -30,7 +30,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -93,9 +92,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT select through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, select.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -1,7 +1,7 @@
"""Support for MQTT sensors."""
from __future__ import annotations
from datetime import timedelta
from datetime import datetime, timedelta
import functools
import logging
@ -30,7 +30,7 @@ from homeassistant.core import HomeAssistant, callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
from homeassistant.util import dt as dt_util
from . import subscription
@ -41,7 +41,6 @@ from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttAvailability,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -146,9 +145,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT sensor through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, sensor.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)
@ -346,7 +342,7 @@ class MqttSensor(MqttEntity, RestoreSensor):
self.async_write_ha_state()
@property
def native_unit_of_measurement(self):
def native_unit_of_measurement(self) -> str | None:
"""Return the unit this state is expressed in."""
return self._config.get(CONF_UNIT_OF_MEASUREMENT)
@ -356,7 +352,7 @@ class MqttSensor(MqttEntity, RestoreSensor):
return self._config[CONF_FORCE_UPDATE]
@property
def native_value(self):
def native_value(self) -> StateType | datetime:
"""Return the state of the entity."""
return self._state

View File

@ -51,7 +51,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -142,9 +141,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT siren through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, siren.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -42,7 +42,6 @@ from .debug_info import log_messages
from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
warn_for_legacy_schema,
@ -101,9 +100,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT switch through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, switch.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -11,11 +11,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from ..mixins import (
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
)
from ..mixins import async_setup_entry_helper, async_setup_platform_helper
from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE
from .schema_legacy import (
DISCOVERY_SCHEMA_LEGACY,
@ -90,9 +86,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MQTT vacuum through configuration.yaml and dynamically through MQTT discovery."""
# load and initialize platform config from configuration.yaml
await async_discover_yaml_entities(hass, vacuum.DOMAIN)
# setup for discovery
setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry
)

View File

@ -138,8 +138,8 @@ class NetatmoDataHandler:
@callback
def async_force_update(self, signal_name: str) -> None:
"""Prioritize data retrieval for given data class entry."""
self.publisher[signal_name].next_scan = time()
self._queue.rotate(-(self._queue.index(self.publisher[signal_name])))
# self.publisher[signal_name].next_scan = time()
# self._queue.rotate(-(self._queue.index(self.publisher[signal_name])))
async def handle_event(self, event: dict) -> None:
"""Handle webhook events."""

View File

@ -2,9 +2,10 @@
from __future__ import annotations
import asyncio
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from datetime import timedelta
from functools import partial
from functools import partial, wraps
from typing import Any
from regenmaschine import Client
@ -22,7 +23,7 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
from homeassistant.helpers import (
aiohttp_client,
config_validation as cv,
@ -152,9 +153,9 @@ class RainMachineData:
@callback
def async_get_controller_for_service_call(
def async_get_entry_for_service_call(
hass: HomeAssistant, call: ServiceCall
) -> Controller:
) -> ConfigEntry:
"""Get the controller related to a service call (by device ID)."""
device_id = call.data[CONF_DEVICE_ID]
device_registry = dr.async_get(hass)
@ -166,8 +167,7 @@ def async_get_controller_for_service_call(
if (entry := hass.config_entries.async_get_entry(entry_id)) is None:
continue
if entry.domain == DOMAIN:
data: RainMachineData = hass.data[DOMAIN][entry_id]
return data.controller
return entry
raise ValueError(f"No controller for device ID: {device_id}")
@ -288,15 +288,42 @@ async def async_setup_entry( # noqa: C901
entry.async_on_unload(entry.add_update_listener(async_reload_entry))
async def async_pause_watering(call: ServiceCall) -> None:
"""Pause watering for a set number of seconds."""
controller = async_get_controller_for_service_call(hass, call)
await controller.watering.pause_all(call.data[CONF_SECONDS])
def call_with_controller(update_programs_and_zones: bool = True) -> Callable:
"""Hydrate a service call with the appropriate controller."""
def decorator(func: Callable) -> Callable[..., Awaitable]:
"""Define the decorator."""
@wraps(func)
async def wrapper(call: ServiceCall) -> None:
"""Wrap the service function."""
entry = async_get_entry_for_service_call(hass, call)
data: RainMachineData = hass.data[DOMAIN][entry.entry_id]
try:
await func(call, data.controller)
except RainMachineError as err:
raise HomeAssistantError(
f"Error while executing {func.__name__}: {err}"
) from err
if update_programs_and_zones:
await async_update_programs_and_zones(hass, entry)
async def async_push_weather_data(call: ServiceCall) -> None:
return wrapper
return decorator
@call_with_controller()
async def async_pause_watering(call: ServiceCall, controller: Controller) -> None:
"""Pause watering for a set number of seconds."""
await controller.watering.pause_all(call.data[CONF_SECONDS])
@call_with_controller(update_programs_and_zones=False)
async def async_push_weather_data(
call: ServiceCall, controller: Controller
) -> None:
"""Push weather data to the device."""
controller = async_get_controller_for_service_call(hass, call)
await controller.parsers.post_data(
{
CONF_WEATHER: [
@ -309,9 +336,11 @@ async def async_setup_entry( # noqa: C901
}
)
async def async_restrict_watering(call: ServiceCall) -> None:
@call_with_controller()
async def async_restrict_watering(
call: ServiceCall, controller: Controller
) -> None:
"""Restrict watering for a time period."""
controller = async_get_controller_for_service_call(hass, call)
duration = call.data[CONF_DURATION]
await controller.restrictions.set_universal(
{
@ -319,30 +348,28 @@ async def async_setup_entry( # noqa: C901
"rainDelayDuration": duration.total_seconds(),
},
)
await async_update_programs_and_zones(hass, entry)
async def async_stop_all(call: ServiceCall) -> None:
@call_with_controller()
async def async_stop_all(call: ServiceCall, controller: Controller) -> None:
"""Stop all watering."""
controller = async_get_controller_for_service_call(hass, call)
await controller.watering.stop_all()
await async_update_programs_and_zones(hass, entry)
async def async_unpause_watering(call: ServiceCall) -> None:
@call_with_controller()
async def async_unpause_watering(call: ServiceCall, controller: Controller) -> None:
"""Unpause watering."""
controller = async_get_controller_for_service_call(hass, call)
await controller.watering.unpause_all()
await async_update_programs_and_zones(hass, entry)
async def async_unrestrict_watering(call: ServiceCall) -> None:
@call_with_controller()
async def async_unrestrict_watering(
call: ServiceCall, controller: Controller
) -> None:
"""Unrestrict watering."""
controller = async_get_controller_for_service_call(hass, call)
await controller.restrictions.set_universal(
{
"rainDelayStartTime": round(as_timestamp(utcnow())),
"rainDelayDuration": 0,
},
)
await async_update_programs_and_zones(hass, entry)
for service_name, schema, method in (
(

View File

@ -458,7 +458,7 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
)
if hostname := urlparse(discovery_info.ssdp_location or "").hostname:
self._host = hostname
self._manufacturer = discovery_info.upnp[ssdp.ATTR_UPNP_MANUFACTURER]
self._manufacturer = discovery_info.upnp.get(ssdp.ATTR_UPNP_MANUFACTURER)
self._abort_if_manufacturer_is_not_samsung()
# Set defaults, in case they cannot be extracted from device_info

View File

@ -15,7 +15,7 @@ from .const import DOMAIN
from .coordinator import SwitchbotDataUpdateCoordinator
from .entity import SwitchbotEntity
PARALLEL_UPDATES = 1
PARALLEL_UPDATES = 0
BINARY_SENSOR_TYPES: dict[str, BinarySensorEntityDescription] = {
"calibration": BinarySensorEntityDescription(

View File

@ -24,7 +24,7 @@ from .entity import SwitchbotEntity
# Initialize the logger
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 1
PARALLEL_UPDATES = 0
async def async_setup_entry(

View File

@ -29,7 +29,7 @@ SWITCHBOT_COLOR_MODE_TO_HASS = {
SwitchBotColorMode.COLOR_TEMP: ColorMode.COLOR_TEMP,
}
PARALLEL_UPDATES = 1
PARALLEL_UPDATES = 0
async def async_setup_entry(

View File

@ -2,7 +2,7 @@
"domain": "switchbot",
"name": "SwitchBot",
"documentation": "https://www.home-assistant.io/integrations/switchbot",
"requirements": ["PySwitchbot==0.19.9"],
"requirements": ["PySwitchbot==0.19.10"],
"config_flow": true,
"dependencies": ["bluetooth"],
"codeowners": [

View File

@ -21,7 +21,7 @@ from .const import DOMAIN
from .coordinator import SwitchbotDataUpdateCoordinator
from .entity import SwitchbotEntity
PARALLEL_UPDATES = 1
PARALLEL_UPDATES = 0
SENSOR_TYPES: dict[str, SensorEntityDescription] = {
"rssi": SensorEntityDescription(

View File

@ -19,7 +19,7 @@ from .entity import SwitchbotEntity
# Initialize the logger
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 1
PARALLEL_UPDATES = 0
async def async_setup_entry(

View File

@ -91,6 +91,16 @@ class SynoApi:
self._with_surveillance_station = bool(
self.dsm.apis.get(SynoSurveillanceStation.CAMERA_API_KEY)
)
if self._with_surveillance_station:
try:
self.dsm.surveillance_station.update()
except SYNOLOGY_CONNECTION_EXCEPTIONS:
self._with_surveillance_station = False
self.dsm.reset(SynoSurveillanceStation.API_KEY)
LOGGER.info(
"Surveillance Station found, but disabled due to missing user permissions"
)
LOGGER.debug(
"State of Surveillance_station during setup of '%s': %s",
self._entry.unique_id,

View File

@ -73,6 +73,8 @@ STATE_CODE_TO_STATE = {
17: STATE_CLEANING, # "Zoned cleaning"
18: STATE_CLEANING, # "Segment cleaning"
22: STATE_DOCKED, # "Emptying the bin" on s7+
23: STATE_DOCKED, # "Washing the mop" on s7maxV
26: STATE_RETURNING, # "Going to wash the mop" on s7maxV
100: STATE_DOCKED, # "Charging complete"
101: STATE_ERROR, # "Device offline"
}

View File

@ -7,7 +7,7 @@ from .backports.enum import StrEnum
MAJOR_VERSION: Final = 2022
MINOR_VERSION: Final = 9
PATCH_VERSION: Final = "5"
PATCH_VERSION: Final = "6"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)

View File

@ -17,7 +17,7 @@ bluetooth-auto-recovery==0.3.3
certifi>=2021.5.30
ciso8601==2.2.0
cryptography==37.0.4
dbus-fast==1.4.0
dbus-fast==1.5.1
fnvhash==0.1.0
hass-nabucasa==0.55.0
home-assistant-bluetooth==1.3.0

View File

@ -120,27 +120,30 @@ def log_exception(format_err: Callable[..., Any], *args: Any) -> None:
def catch_log_exception(
func: Callable[..., Coroutine[Any, Any, Any]], format_err: Callable[..., Any]
) -> Callable[..., Coroutine[Any, Any, None]]:
"""Overload for Callables that return a Coroutine."""
"""Overload for Coroutine that returns a Coroutine."""
@overload
def catch_log_exception(
func: Callable[..., Any], format_err: Callable[..., Any]
) -> Callable[..., None | Coroutine[Any, Any, None]]:
"""Overload for Callables that return Any."""
) -> Callable[..., None] | Callable[..., Coroutine[Any, Any, None]]:
"""Overload for a callback that returns a callback."""
def catch_log_exception(
func: Callable[..., Any], format_err: Callable[..., Any]
) -> Callable[..., None | Coroutine[Any, Any, None]]:
"""Decorate a callback to catch and log exceptions."""
) -> Callable[..., None] | Callable[..., Coroutine[Any, Any, None]]:
"""Decorate a function func to catch and log exceptions.
If func is a coroutine function, a coroutine function will be returned.
If func is a callback, a callback will be returned.
"""
# Check for partials to properly determine if coroutine function
check_func = func
while isinstance(check_func, partial):
check_func = check_func.func
wrapper_func: Callable[..., None | Coroutine[Any, Any, None]]
wrapper_func: Callable[..., None] | Callable[..., Coroutine[Any, Any, None]]
if asyncio.iscoroutinefunction(check_func):
async_func = cast(Callable[..., Coroutine[Any, Any, None]], func)

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "homeassistant"
version = "2022.9.5"
version = "2022.9.6"
license = {text = "Apache-2.0"}
description = "Open-source home automation platform running on Python 3."
readme = "README.rst"

View File

@ -37,7 +37,7 @@ PyRMVtransport==0.3.3
PySocks==1.7.1
# homeassistant.components.switchbot
PySwitchbot==0.19.9
PySwitchbot==0.19.10
# homeassistant.components.transport_nsw
PyTransportNSW==0.1.1
@ -171,7 +171,7 @@ aioguardian==2022.07.0
aioharmony==0.2.9
# homeassistant.components.homekit_controller
aiohomekit==1.5.9
aiohomekit==1.5.12
# homeassistant.components.emulated_hue
# homeassistant.components.http
@ -181,7 +181,7 @@ aiohttp_cors==0.7.0
aiohue==4.5.0
# homeassistant.components.imap
aioimaplib==1.0.0
aioimaplib==1.0.1
# homeassistant.components.apache_kafka
aiokafka==0.7.2
@ -402,7 +402,7 @@ beautifulsoup4==4.11.1
bellows==0.33.1
# homeassistant.components.bmw_connected_drive
bimmer_connected==0.10.2
bimmer_connected==0.10.4
# homeassistant.components.bizkaibus
bizkaibus==0.1.1
@ -538,7 +538,7 @@ datadog==0.15.0
datapoint==0.9.8
# homeassistant.components.bluetooth
dbus-fast==1.4.0
dbus-fast==1.5.1
# homeassistant.components.debugpy
debugpy==1.6.3

View File

@ -33,7 +33,7 @@ PyRMVtransport==0.3.3
PySocks==1.7.1
# homeassistant.components.switchbot
PySwitchbot==0.19.9
PySwitchbot==0.19.10
# homeassistant.components.transport_nsw
PyTransportNSW==0.1.1
@ -155,7 +155,7 @@ aioguardian==2022.07.0
aioharmony==0.2.9
# homeassistant.components.homekit_controller
aiohomekit==1.5.9
aiohomekit==1.5.12
# homeassistant.components.emulated_hue
# homeassistant.components.http
@ -326,7 +326,7 @@ beautifulsoup4==4.11.1
bellows==0.33.1
# homeassistant.components.bmw_connected_drive
bimmer_connected==0.10.2
bimmer_connected==0.10.4
# homeassistant.components.bluetooth
bleak-retry-connector==1.17.1
@ -415,7 +415,7 @@ datadog==0.15.0
datapoint==0.9.8
# homeassistant.components.bluetooth
dbus-fast==1.4.0
dbus-fast==1.5.1
# homeassistant.components.debugpy
debugpy==1.6.3

View File

@ -369,7 +369,7 @@ def async_fire_mqtt_message(hass, topic, payload, qos=0, retain=False):
if isinstance(payload, str):
payload = payload.encode("utf-8")
msg = ReceiveMessage(topic, payload, qos, retain)
hass.data["mqtt"]._mqtt_handle_message(msg)
hass.data["mqtt"].client._mqtt_handle_message(msg)
fire_mqtt_message = threadsafe_callback_factory(async_fire_mqtt_message)

View File

@ -116,6 +116,62 @@ async def test_switching_adapters_based_on_rssi(hass, enable_bluetooth):
)
async def test_switching_adapters_based_on_zero_rssi(hass, enable_bluetooth):
"""Test switching adapters based on zero rssi."""
address = "44:44:33:11:23:45"
switchbot_device_no_rssi = BLEDevice(address, "wohand_poor_signal", rssi=0)
switchbot_adv_no_rssi = AdvertisementData(
local_name="wohand_no_rssi", service_uuids=[]
)
inject_advertisement_with_source(
hass, switchbot_device_no_rssi, switchbot_adv_no_rssi, "hci0"
)
assert (
bluetooth.async_ble_device_from_address(hass, address)
is switchbot_device_no_rssi
)
switchbot_device_good_signal = BLEDevice(address, "wohand_good_signal", rssi=-60)
switchbot_adv_good_signal = AdvertisementData(
local_name="wohand_good_signal", service_uuids=[]
)
inject_advertisement_with_source(
hass, switchbot_device_good_signal, switchbot_adv_good_signal, "hci1"
)
assert (
bluetooth.async_ble_device_from_address(hass, address)
is switchbot_device_good_signal
)
inject_advertisement_with_source(
hass, switchbot_device_good_signal, switchbot_adv_no_rssi, "hci0"
)
assert (
bluetooth.async_ble_device_from_address(hass, address)
is switchbot_device_good_signal
)
# We should not switch adapters unless the signal hits the threshold
switchbot_device_similar_signal = BLEDevice(
address, "wohand_similar_signal", rssi=-62
)
switchbot_adv_similar_signal = AdvertisementData(
local_name="wohand_similar_signal", service_uuids=[]
)
inject_advertisement_with_source(
hass, switchbot_device_similar_signal, switchbot_adv_similar_signal, "hci0"
)
assert (
bluetooth.async_ble_device_from_address(hass, address)
is switchbot_device_good_signal
)
async def test_switching_adapters_based_on_stale(hass, enable_bluetooth):
"""Test switching adapters based on the previous advertisement being stale."""

View File

@ -1,6 +1,7 @@
"""Test the Bond config flow."""
from __future__ import annotations
import asyncio
from http import HTTPStatus
from typing import Any
from unittest.mock import MagicMock, Mock, patch
@ -268,6 +269,43 @@ async def test_zeroconf_form_token_unavailable(hass: core.HomeAssistant):
assert len(mock_setup_entry.mock_calls) == 1
async def test_zeroconf_form_token_times_out(hass: core.HomeAssistant):
"""Test we get the discovery form and we handle the token request timeout."""
with patch_bond_version(), patch_bond_token(side_effect=asyncio.TimeoutError):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_ZEROCONF},
data=zeroconf.ZeroconfServiceInfo(
host="test-host",
addresses=["test-host"],
hostname="mock_hostname",
name="ZXXX12345.some-other-tail-info",
port=None,
properties={},
type="mock_type",
),
)
await hass.async_block_till_done()
assert result["type"] == "form"
assert result["errors"] == {}
with patch_bond_version(), patch_bond_bridge(), patch_bond_device_ids(), _patch_async_setup_entry() as mock_setup_entry:
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_ACCESS_TOKEN: "test-token"},
)
await hass.async_block_till_done()
assert result2["type"] == "create_entry"
assert result2["title"] == "bond-name"
assert result2["data"] == {
CONF_HOST: "test-host",
CONF_ACCESS_TOKEN: "test-token",
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_zeroconf_form_with_token_available(hass: core.HomeAssistant):
"""Test we get the discovery form when we can get the token."""

View File

@ -381,7 +381,7 @@ async def test_query_message(hass):
"payload": {
"devices": {
"light.non_existing": {"online": False},
"light.demo_light": {"on": False, "online": True, "brightness": 0},
"light.demo_light": {"on": False, "online": True},
"light.another_light": {
"on": True,
"online": True,
@ -725,7 +725,6 @@ async def test_execute_times_out(hass, report_state, on, brightness, value):
"states": {
"on": on,
"online": True,
"brightness": brightness,
},
},
{

View File

@ -5,7 +5,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import alarm_control_panel
from homeassistant.components import alarm_control_panel, mqtt
from homeassistant.components.mqtt.alarm_control_panel import (
MQTT_ALARM_ATTRIBUTES_BLOCKED,
)
@ -61,58 +61,67 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import assert_setup_component, async_fire_mqtt_message
from tests.common import async_fire_mqtt_message
from tests.components.alarm_control_panel import common
CODE_NUMBER = "1234"
CODE_TEXT = "HELLO_CODE"
DEFAULT_CONFIG = {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "alarm/state",
"command_topic": "alarm/command",
}
}
}
DEFAULT_CONFIG_CODE = {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "alarm/state",
"command_topic": "alarm/command",
"code": "0123",
"code_arm_required": True,
}
}
}
DEFAULT_CONFIG_REMOTE_CODE = {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "alarm/state",
"command_topic": "alarm/command",
"code": "REMOTE_CODE",
"code_arm_required": True,
}
}
}
DEFAULT_CONFIG_REMOTE_CODE_TEXT = {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "alarm/state",
"command_topic": "alarm/command",
"code": "REMOTE_CODE_TEXT",
"code_arm_required": True,
}
}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[alarm_control_panel.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def alarm_control_panel_platform_only():
@ -123,47 +132,62 @@ def alarm_control_panel_platform_only():
yield
async def test_fail_setup_without_state_topic(hass, mqtt_mock_entry_no_yaml_config):
"""Test for failing with no state topic."""
with assert_setup_component(0, alarm_control_panel.DOMAIN) as config:
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
@pytest.mark.parametrize(
"config,valid",
[
(
{
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "alarm/command",
}
},
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert not config[alarm_control_panel.DOMAIN]
async def test_fail_setup_without_command_topic(hass, mqtt_mock_entry_no_yaml_config):
"""Test failing with no command topic."""
with assert_setup_component(0, alarm_control_panel.DOMAIN) as config:
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
{
alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"state_topic": "alarm/state",
}
},
False,
),
(
{
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: {
"name": "test",
"state_topic": "alarm/state",
}
}
},
False,
),
(
{
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: {
"name": "test",
"command_topic": "alarm/command",
"state_topic": "alarm/state",
}
}
},
True,
),
],
)
async def test_fail_setup_without_state_or_command_topic(hass, config, valid):
"""Test for failing setup with no state or command topic."""
assert (
await async_setup_component(
hass,
mqtt.DOMAIN,
config,
)
is valid
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert not config[alarm_control_panel.DOMAIN]
async def test_update_state_via_state_topic(hass, mqtt_mock_entry_with_yaml_config):
"""Test updating with via state topic."""
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
mqtt.DOMAIN,
DEFAULT_CONFIG,
)
await hass.async_block_till_done()
@ -195,7 +219,7 @@ async def test_ignore_update_state_if_unknown_via_state_topic(
"""Test ignoring updates via state topic."""
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
mqtt.DOMAIN,
DEFAULT_CONFIG,
)
await hass.async_block_till_done()
@ -227,7 +251,7 @@ async def test_publish_mqtt_no_code(
"""Test publishing of MQTT messages when no code is configured."""
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
mqtt.DOMAIN,
DEFAULT_CONFIG,
)
await hass.async_block_till_done()
@ -261,7 +285,7 @@ async def test_publish_mqtt_with_code(
"""Test publishing of MQTT messages when code is configured."""
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
mqtt.DOMAIN,
DEFAULT_CONFIG_CODE,
)
await hass.async_block_till_done()
@ -314,7 +338,7 @@ async def test_publish_mqtt_with_remote_code(
"""Test publishing of MQTT messages when remode code is configured."""
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
mqtt.DOMAIN,
DEFAULT_CONFIG_REMOTE_CODE,
)
await hass.async_block_till_done()
@ -358,7 +382,7 @@ async def test_publish_mqtt_with_remote_code_text(
"""Test publishing of MQTT messages when remote text code is configured."""
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
mqtt.DOMAIN,
DEFAULT_CONFIG_REMOTE_CODE_TEXT,
)
await hass.async_block_till_done()
@ -405,10 +429,10 @@ async def test_publish_mqtt_with_code_required_false(
code_trigger_required = False
"""
config = copy.deepcopy(DEFAULT_CONFIG_CODE)
config[alarm_control_panel.DOMAIN][disable_code] = False
config[mqtt.DOMAIN][alarm_control_panel.DOMAIN][disable_code] = False
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -453,13 +477,13 @@ async def test_disarm_publishes_mqtt_with_template(
When command_template set to output json
"""
config = copy.deepcopy(DEFAULT_CONFIG_CODE)
config[alarm_control_panel.DOMAIN]["code"] = "0123"
config[alarm_control_panel.DOMAIN][
config[mqtt.DOMAIN][alarm_control_panel.DOMAIN]["code"] = "0123"
config[mqtt.DOMAIN][alarm_control_panel.DOMAIN][
"command_template"
] = '{"action":"{{ action }}","code":"{{ code }}"}'
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -477,10 +501,10 @@ async def test_update_state_via_state_topic_template(
"""Test updating with template_value via state topic."""
assert await async_setup_component(
hass,
alarm_control_panel.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test-topic",
"state_topic": "test-topic",
@ -491,6 +515,7 @@ async def test_update_state_via_state_topic_template(
disarmed\
{% endif %}",
}
}
},
)
await hass.async_block_till_done()
@ -508,9 +533,9 @@ async def test_update_state_via_state_topic_template(
async def test_attributes_code_number(hass, mqtt_mock_entry_with_yaml_config):
"""Test attributes which are not supported by the vacuum."""
config = copy.deepcopy(DEFAULT_CONFIG)
config[alarm_control_panel.DOMAIN]["code"] = CODE_NUMBER
config[mqtt.DOMAIN][alarm_control_panel.DOMAIN]["code"] = CODE_NUMBER
assert await async_setup_component(hass, alarm_control_panel.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, config)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -524,9 +549,9 @@ async def test_attributes_code_number(hass, mqtt_mock_entry_with_yaml_config):
async def test_attributes_remote_code_number(hass, mqtt_mock_entry_with_yaml_config):
"""Test attributes which are not supported by the vacuum."""
config = copy.deepcopy(DEFAULT_CONFIG_REMOTE_CODE)
config[alarm_control_panel.DOMAIN]["code"] = "REMOTE_CODE"
config[mqtt.DOMAIN][alarm_control_panel.DOMAIN]["code"] = "REMOTE_CODE"
assert await async_setup_component(hass, alarm_control_panel.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, config)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -540,9 +565,9 @@ async def test_attributes_remote_code_number(hass, mqtt_mock_entry_with_yaml_con
async def test_attributes_code_text(hass, mqtt_mock_entry_with_yaml_config):
"""Test attributes which are not supported by the vacuum."""
config = copy.deepcopy(DEFAULT_CONFIG)
config[alarm_control_panel.DOMAIN]["code"] = CODE_TEXT
config[mqtt.DOMAIN][alarm_control_panel.DOMAIN]["code"] = CODE_TEXT
assert await async_setup_component(hass, alarm_control_panel.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, config)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -591,7 +616,7 @@ async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_confi
hass,
mqtt_mock_entry_with_yaml_config,
alarm_control_panel.DOMAIN,
DEFAULT_CONFIG_CODE,
DEFAULT_CONFIG,
)
@ -643,11 +668,11 @@ async def test_update_with_json_attrs_not_dict(
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
@ -670,16 +695,15 @@ async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplo
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one alarm per unique_id."""
config = {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"command_topic": "command-topic",
@ -687,6 +711,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, alarm_control_panel.DOMAIN, config
)
@ -694,7 +719,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
async def test_discovery_removal_alarm(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered alarm_control_panel."""
data = json.dumps(DEFAULT_CONFIG[alarm_control_panel.DOMAIN])
data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN])
await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, alarm_control_panel.DOMAIN, data
)
@ -704,8 +729,8 @@ async def test_discovery_update_alarm_topic_and_template(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test update of discovered alarm_control_panel."""
config1 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN])
config1["name"] = "Beer"
config2["name"] = "Milk"
config1["state_topic"] = "alarm/state1"
@ -739,8 +764,8 @@ async def test_discovery_update_alarm_template(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test update of discovered alarm_control_panel."""
config1 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN])
config1["name"] = "Beer"
config2["name"] = "Milk"
config1["state_topic"] = "alarm/state1"
@ -772,7 +797,7 @@ async def test_discovery_update_unchanged_alarm(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test update of discovered alarm_control_panel."""
config1 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN])
config1["name"] = "Beer"
data1 = json.dumps(config1)
@ -824,7 +849,7 @@ async def test_encoding_subscribable_topics(
mqtt_mock_entry_with_yaml_config,
caplog,
alarm_control_panel.DOMAIN,
DEFAULT_CONFIG[alarm_control_panel.DOMAIN],
DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN],
topic,
value,
)
@ -853,14 +878,20 @@ async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_
async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry update."""
await help_test_entity_device_info_update(
hass, mqtt_mock_entry_no_yaml_config, alarm_control_panel.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
alarm_control_panel.DOMAIN,
DEFAULT_CONFIG,
)
async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry remove."""
await help_test_entity_device_info_remove(
hass, mqtt_mock_entry_no_yaml_config, alarm_control_panel.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
alarm_control_panel.DOMAIN,
DEFAULT_CONFIG,
)
@ -877,7 +908,10 @@ async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_co
async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT discovery update when entity_id is updated."""
await help_test_entity_id_update_discovery_update(
hass, mqtt_mock_entry_no_yaml_config, alarm_control_panel.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
alarm_control_panel.DOMAIN,
DEFAULT_CONFIG,
)
@ -930,7 +964,7 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = alarm_control_panel.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_publishing_with_custom_encoding(
hass,
@ -951,33 +985,45 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = alarm_control_panel.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = alarm_control_panel.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = alarm_control_panel.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = alarm_control_panel.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = alarm_control_panel.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -6,7 +6,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import binary_sensor
from homeassistant.components import binary_sensor, mqtt
from homeassistant.const import (
EVENT_STATE_CHANGED,
STATE_OFF,
@ -45,25 +45,30 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import (
assert_setup_component,
async_fire_mqtt_message,
async_fire_time_changed,
mock_restore_cache,
)
DEFAULT_CONFIG = {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
}
}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[binary_sensor.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def binary_sensor_platform_only():
@ -78,16 +83,17 @@ async def test_setting_sensor_value_expires_availability_topic(
"""Test the expiration of the value."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"expire_after": 4,
"force_update": True,
"availability_topic": "availability-topic",
}
}
},
)
await hass.async_block_till_done()
@ -270,15 +276,16 @@ async def test_setting_sensor_value_via_mqtt_message(
"""Test the setting of the value via MQTT."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"payload_on": "ON",
"payload_off": "OFF",
}
}
},
)
await hass.async_block_till_done()
@ -307,15 +314,16 @@ async def test_invalid_sensor_value_via_mqtt_message(
"""Test the setting of the value via MQTT."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"payload_on": "ON",
"payload_off": "OFF",
}
}
},
)
await hass.async_block_till_done()
@ -348,10 +356,10 @@ async def test_setting_sensor_value_via_mqtt_message_and_template(
"""Test the setting of the value via MQTT."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"payload_on": "ON",
@ -359,6 +367,7 @@ async def test_setting_sensor_value_via_mqtt_message_and_template(
"value_template": '{%if is_state(entity_id,"on")-%}OFF'
"{%-else-%}ON{%-endif%}",
}
}
},
)
await hass.async_block_till_done()
@ -382,16 +391,17 @@ async def test_setting_sensor_value_via_mqtt_message_and_template2(
"""Test the setting of the value via MQTT."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"payload_on": "ON",
"payload_off": "OFF",
"value_template": "{{value | upper}}",
}
}
},
)
await hass.async_block_till_done()
@ -420,10 +430,10 @@ async def test_setting_sensor_value_via_mqtt_message_and_template_and_raw_state_
"""Test processing a raw value via MQTT."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"encoding": "",
"state_topic": "test-topic",
@ -431,6 +441,7 @@ async def test_setting_sensor_value_via_mqtt_message_and_template_and_raw_state_
"payload_off": "OFF",
"value_template": "{%if value|unpack('b')-%}ON{%else%}OFF{%-endif-%}",
}
}
},
)
await hass.async_block_till_done()
@ -454,16 +465,17 @@ async def test_setting_sensor_value_via_mqtt_message_empty_template(
"""Test the setting of the value via MQTT."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"payload_on": "ON",
"payload_off": "OFF",
"value_template": '{%if value == "ABC"%}ON{%endif%}',
}
}
},
)
await hass.async_block_till_done()
@ -486,14 +498,15 @@ async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config):
"""Test the setting of a valid sensor class."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"device_class": "motion",
"state_topic": "test-topic",
}
}
},
)
await hass.async_block_till_done()
@ -503,25 +516,22 @@ async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config):
assert state.attributes.get("device_class") == "motion"
async def test_invalid_device_class(hass, mqtt_mock_entry_no_yaml_config):
async def test_invalid_device_class(hass, caplog):
"""Test the setting of an invalid sensor class."""
assert await async_setup_component(
assert not await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"device_class": "abc123",
"state_topic": "test-topic",
}
}
},
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
state = hass.states.get("binary_sensor.test")
assert state is None
assert "Invalid config for [mqtt]: expected BinarySensorDeviceClass" in caplog.text
async def test_availability_when_connection_lost(
@ -529,28 +539,40 @@ async def test_availability_when_connection_lost(
):
"""Test availability after MQTT disconnection."""
await help_test_availability_when_connection_lost(
hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability without defined availability topic."""
await help_test_availability_without_topic(
hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic."""
await help_test_default_availability_payload(
hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic."""
await help_test_custom_availability_payload(
hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
@ -558,15 +580,16 @@ async def test_force_update_disabled(hass, mqtt_mock_entry_with_yaml_config):
"""Test force update option."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"payload_on": "ON",
"payload_off": "OFF",
}
}
},
)
await hass.async_block_till_done()
@ -594,16 +617,17 @@ async def test_force_update_enabled(hass, mqtt_mock_entry_with_yaml_config):
"""Test force update option."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"payload_on": "ON",
"payload_off": "OFF",
"force_update": True,
}
}
},
)
await hass.async_block_till_done()
@ -631,10 +655,10 @@ async def test_off_delay(hass, mqtt_mock_entry_with_yaml_config):
"""Test off_delay option."""
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"payload_on": "ON",
@ -642,6 +666,7 @@ async def test_off_delay(hass, mqtt_mock_entry_with_yaml_config):
"off_delay": 30,
"force_update": True,
}
}
},
)
await hass.async_block_till_done()
@ -680,14 +705,20 @@ async def test_setting_attribute_via_mqtt_json_message(
):
"""Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_via_mqtt_json_message(
hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_with_template(
hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
@ -704,11 +735,11 @@ async def test_update_with_json_attrs_not_dict(
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
@ -731,21 +762,21 @@ async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplo
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one sensor per unique_id."""
config = {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE",
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, config
)
@ -755,7 +786,7 @@ async def test_discovery_removal_binary_sensor(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test removal of discovered binary_sensor."""
data = json.dumps(DEFAULT_CONFIG[binary_sensor.DOMAIN])
data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN])
await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, binary_sensor.DOMAIN, data
)
@ -765,8 +796,8 @@ async def test_discovery_update_binary_sensor_topic_template(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test update of discovered binary_sensor."""
config1 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN])
config1["name"] = "Beer"
config2["name"] = "Milk"
config1["state_topic"] = "sensor/state1"
@ -802,8 +833,8 @@ async def test_discovery_update_binary_sensor_template(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test update of discovered binary_sensor."""
config1 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN])
config1["name"] = "Beer"
config2["name"] = "Milk"
config1["state_topic"] = "sensor/state1"
@ -861,7 +892,7 @@ async def test_encoding_subscribable_topics(
mqtt_mock_entry_with_yaml_config,
caplog,
binary_sensor.DOMAIN,
DEFAULT_CONFIG[binary_sensor.DOMAIN],
DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN],
topic,
value,
attribute,
@ -873,7 +904,7 @@ async def test_discovery_update_unchanged_binary_sensor(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test update of discovered binary_sensor."""
config1 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN])
config1["name"] = "Beer"
data1 = json.dumps(config1)
@ -908,42 +939,60 @@ async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog):
async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT binary sensor device registry integration."""
await help_test_entity_device_info_with_connection(
hass, mqtt_mock_entry_no_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT binary sensor device registry integration."""
await help_test_entity_device_info_with_identifier(
hass, mqtt_mock_entry_no_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry update."""
await help_test_entity_device_info_update(
hass, mqtt_mock_entry_no_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry remove."""
await help_test_entity_device_info_remove(
hass, mqtt_mock_entry_no_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config):
"""Test MQTT subscriptions are managed when entity_id is updated."""
await help_test_entity_id_update_subscriptions(
hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT discovery update when entity_id is updated."""
await help_test_entity_id_update_discovery_update(
hass, mqtt_mock_entry_no_yaml_config, binary_sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
binary_sensor.DOMAIN,
DEFAULT_CONFIG,
)
@ -961,16 +1010,18 @@ async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = binary_sensor.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = binary_sensor.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -991,11 +1042,11 @@ async def test_cleanup_triggers_and_restoring_state(
):
"""Test cleanup old triggers at reloading and restoring the state."""
domain = binary_sensor.DOMAIN
config1 = copy.deepcopy(DEFAULT_CONFIG[domain])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config1["name"] = "test1"
config1["expire_after"] = 30
config1["state_topic"] = "test-topic1"
config2 = copy.deepcopy(DEFAULT_CONFIG[domain])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config2["name"] = "test2"
config2["expire_after"] = 5
config2["state_topic"] = "test-topic2"
@ -1004,8 +1055,8 @@ async def test_cleanup_triggers_and_restoring_state(
assert await async_setup_component(
hass,
binary_sensor.DOMAIN,
{binary_sensor.DOMAIN: [config1, config2]},
mqtt.DOMAIN,
{mqtt.DOMAIN: {binary_sensor.DOMAIN: [config1, config2]}},
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -1021,7 +1072,7 @@ async def test_cleanup_triggers_and_restoring_state(
freezer.move_to("2022-02-02 12:01:10+01:00")
await help_test_reload_with_config(
hass, caplog, tmp_path, {domain: [config1, config2]}
hass, caplog, tmp_path, {mqtt.DOMAIN: {domain: [config1, config2]}}
)
assert "Clean up expire after trigger for binary_sensor.test1" in caplog.text
assert "Clean up expire after trigger for binary_sensor.test2" not in caplog.text
@ -1053,7 +1104,7 @@ async def test_skip_restoring_state_with_over_due_expire_trigger(
freezer.move_to("2022-02-02 12:02:00+01:00")
domain = binary_sensor.DOMAIN
config3 = copy.deepcopy(DEFAULT_CONFIG[domain])
config3 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config3["name"] = "test3"
config3["expire_after"] = 10
config3["state_topic"] = "test-topic3"
@ -1065,8 +1116,9 @@ async def test_skip_restoring_state_with_over_due_expire_trigger(
)
mock_restore_cache(hass, (fake_state,))
with assert_setup_component(1, domain):
assert await async_setup_component(hass, domain, {domain: config3})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {domain: config3}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert "Skip state recovery after reload for binary_sensor.test3" in caplog.text
@ -1075,17 +1127,27 @@ async def test_skip_restoring_state_with_over_due_expire_trigger(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = binary_sensor.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = binary_sensor.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = binary_sensor.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -4,7 +4,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import button
from homeassistant.components import button, mqtt
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME,
@ -38,14 +38,19 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
DEFAULT_CONFIG = {
button.DOMAIN: {"platform": "mqtt", "name": "test", "command_topic": "test-topic"}
mqtt.DOMAIN: {button.DOMAIN: {"name": "test", "command_topic": "test-topic"}}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[button.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def button_platform_only():
@ -59,16 +64,17 @@ async def test_sending_mqtt_commands(hass, mqtt_mock_entry_with_yaml_config):
"""Test the sending MQTT commands."""
assert await async_setup_component(
hass,
button.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
button.DOMAIN: {
"command_topic": "command-topic",
"name": "test",
"object_id": "test_button",
"payload_press": "beer press",
"platform": "mqtt",
"qos": "2",
}
}
},
)
await hass.async_block_till_done()
@ -97,14 +103,15 @@ async def test_command_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test the sending of MQTT commands through a command template."""
assert await async_setup_component(
hass,
button.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
button.DOMAIN: {
"command_topic": "command-topic",
"command_template": '{ "{{ value }}": "{{ entity_id }}" }',
"name": "test",
"payload_press": "milky_way_press",
"platform": "mqtt",
}
}
},
)
@ -147,13 +154,14 @@ async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config
async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic."""
config = {
mqtt.DOMAIN: {
button.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"payload_press": 1,
}
}
}
await help_test_default_availability_payload(
hass,
@ -169,13 +177,14 @@ async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_conf
async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic."""
config = {
mqtt.DOMAIN: {
button.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"payload_press": 1,
}
}
}
await help_test_custom_availability_payload(
hass,
@ -218,44 +227,56 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, button.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
button.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, button.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
button.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, button.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
button.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one button per unique_id."""
config = {
mqtt.DOMAIN: {
button.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE",
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, button.DOMAIN, config
)
@ -271,8 +292,8 @@ async def test_discovery_removal_button(hass, mqtt_mock_entry_no_yaml_config, ca
async def test_discovery_update_button(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered button."""
config1 = copy.deepcopy(DEFAULT_CONFIG[button.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[button.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][button.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][button.DOMAIN])
config1["name"] = "Beer"
config2["name"] = "Milk"
@ -366,52 +387,47 @@ async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
)
async def test_invalid_device_class(hass, mqtt_mock_entry_no_yaml_config):
async def test_invalid_device_class(hass):
"""Test device_class option with invalid value."""
assert await async_setup_component(
assert not await async_setup_component(
hass,
button.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
button.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"device_class": "foobarnotreal",
}
}
},
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
state = hass.states.get("button.test")
assert state is None
async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config):
"""Test device_class option with valid values."""
assert await async_setup_component(
hass,
button.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
button.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"command_topic": "test-topic",
"device_class": "update",
},
{
"platform": "mqtt",
"name": "Test 2",
"command_topic": "test-topic",
"device_class": "restart",
},
{
"platform": "mqtt",
"name": "Test 3",
"command_topic": "test-topic",
},
]
}
},
)
await hass.async_block_till_done()
@ -443,7 +459,7 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = button.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_publishing_with_custom_encoding(
hass,
@ -462,33 +478,45 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = button.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = button.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = button.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = button.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = button.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -7,7 +7,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import camera
from homeassistant.components import camera, mqtt
from homeassistant.components.mqtt.camera import MQTT_CAMERA_ATTRIBUTES_BLOCKED
from homeassistant.const import Platform
from homeassistant.setup import async_setup_component
@ -37,15 +37,18 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import async_fire_mqtt_message
DEFAULT_CONFIG = {
camera.DOMAIN: {"platform": "mqtt", "name": "test", "topic": "test_topic"}
}
DEFAULT_CONFIG = {mqtt.DOMAIN: {camera.DOMAIN: {"name": "test", "topic": "test_topic"}}}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[camera.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
@ -62,8 +65,8 @@ async def test_run_camera_setup(
topic = "test/camera"
await async_setup_component(
hass,
"camera",
{"camera": {"platform": "mqtt", "topic": topic, "name": "Test Camera"}},
mqtt.DOMAIN,
{mqtt.DOMAIN: {camera.DOMAIN: {"topic": topic, "name": "Test Camera"}}},
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -86,14 +89,15 @@ async def test_run_camera_b64_encoded(
topic = "test/camera"
await async_setup_component(
hass,
"camera",
mqtt.DOMAIN,
{
"camera": {
"platform": "mqtt",
mqtt.DOMAIN: {
camera.DOMAIN: {
"topic": topic,
"name": "Test Camera",
"encoding": "b64",
}
}
},
)
await hass.async_block_till_done()
@ -110,7 +114,7 @@ async def test_run_camera_b64_encoded(
assert body == "grass"
# Using CONF_ENCODING to set b64 encoding for images is deprecated Home Assistant 2022.9, use CONF_IMAGE_ENCODING instead
# Using CONF_ENCODING to set b64 encoding for images is deprecated in Home Assistant 2022.9, use CONF_IMAGE_ENCODING instead
async def test_legacy_camera_b64_encoded_with_availability(
hass, hass_client_no_auth, mqtt_mock_entry_with_yaml_config
):
@ -119,15 +123,16 @@ async def test_legacy_camera_b64_encoded_with_availability(
topic_availability = "test/camera_availability"
await async_setup_component(
hass,
"camera",
mqtt.DOMAIN,
{
"camera": {
"platform": "mqtt",
mqtt.DOMAIN: {
camera.DOMAIN: {
"topic": topic,
"name": "Test Camera",
"encoding": "b64",
"availability": {"topic": topic_availability},
}
}
},
)
await hass.async_block_till_done()
@ -155,16 +160,17 @@ async def test_camera_b64_encoded_with_availability(
topic_availability = "test/camera_availability"
await async_setup_component(
hass,
"camera",
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
"camera": {
"platform": "mqtt",
"topic": topic,
"name": "Test Camera",
"encoding": "utf-8",
"image_encoding": "b64",
"availability": {"topic": topic_availability},
}
}
},
)
await hass.async_block_till_done()
@ -248,44 +254,56 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, camera.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
camera.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, camera.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
camera.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, camera.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
camera.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one camera per unique_id."""
config = {
mqtt.DOMAIN: {
camera.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE",
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, camera.DOMAIN, config
)
@ -293,7 +311,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
async def test_discovery_removal_camera(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered camera."""
data = json.dumps(DEFAULT_CONFIG[camera.DOMAIN])
data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][camera.DOMAIN])
await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, camera.DOMAIN, data
)
@ -400,33 +418,45 @@ async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = camera.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = camera.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = camera.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = camera.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = camera.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -6,7 +6,7 @@ from unittest.mock import call, patch
import pytest
import voluptuous as vol
from homeassistant.components import climate
from homeassistant.components import climate, mqtt
from homeassistant.components.climate import DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP
from homeassistant.components.climate.const import (
ATTR_AUX_HEAT,
@ -16,10 +16,9 @@ from homeassistant.components.climate.const import (
ATTR_SWING_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
CURRENT_HVAC_ACTIONS,
DOMAIN as CLIMATE_DOMAIN,
PRESET_ECO,
ClimateEntityFeature,
HVACAction,
HVACMode,
)
from homeassistant.components.mqtt.climate import MQTT_CLIMATE_ATTRIBUTES_BLOCKED
@ -53,7 +52,7 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
@ -62,9 +61,10 @@ from tests.components.climate import common
ENTITY_CLIMATE = "climate.test"
DEFAULT_CONFIG = {
CLIMATE_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
climate.DOMAIN: {
"name": "test",
"mode_command_topic": "mode-topic",
"temperature_command_topic": "temperature-topic",
@ -84,8 +84,14 @@ DEFAULT_CONFIG = {
"activity",
],
}
}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[climate.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def climate_platform_only():
@ -96,7 +102,7 @@ def climate_platform_only():
async def test_setup_params(hass, mqtt_mock_entry_with_yaml_config):
"""Test the initial parameters."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -109,18 +115,14 @@ async def test_setup_params(hass, mqtt_mock_entry_with_yaml_config):
assert state.attributes.get("max_temp") == DEFAULT_MAX_TEMP
async def test_preset_none_in_preset_modes(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
async def test_preset_none_in_preset_modes(hass, caplog):
"""Test the preset mode payload reset configuration."""
config = copy.deepcopy(DEFAULT_CONFIG[CLIMATE_DOMAIN])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][climate.DOMAIN])
config["preset_modes"].append("none")
assert await async_setup_component(hass, CLIMATE_DOMAIN, {CLIMATE_DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert "Invalid config for [climate.mqtt]: not a valid value" in caplog.text
state = hass.states.get(ENTITY_CLIMATE)
assert state is None
assert not await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {climate.DOMAIN: config}}
)
assert "Invalid config for [mqtt]: not a valid value" in caplog.text
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9
@ -136,22 +138,19 @@ async def test_preset_none_in_preset_modes(
("hold_mode_state_template", "{{ value_json }}"),
],
)
async def test_preset_modes_deprecation_guard(
hass, mqtt_mock_entry_no_yaml_config, caplog, parameter, config_value
):
async def test_preset_modes_deprecation_guard(hass, caplog, parameter, config_value):
"""Test the configuration for invalid legacy parameters."""
config = copy.deepcopy(DEFAULT_CONFIG[CLIMATE_DOMAIN])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][climate.DOMAIN])
config[parameter] = config_value
assert await async_setup_component(hass, CLIMATE_DOMAIN, {CLIMATE_DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
state = hass.states.get(ENTITY_CLIMATE)
assert state is None
assert not await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {climate.DOMAIN: config}}
)
assert f"[{parameter}] is an invalid option for [mqtt]. Check: mqtt->mqtt->climate->0->{parameter}"
async def test_supported_features(hass, mqtt_mock_entry_with_yaml_config):
"""Test the supported_features."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -170,7 +169,7 @@ async def test_supported_features(hass, mqtt_mock_entry_with_yaml_config):
async def test_get_hvac_modes(hass, mqtt_mock_entry_with_yaml_config):
"""Test that the operation list returns the correct modes."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -193,7 +192,7 @@ async def test_set_operation_bad_attr_and_state(
Also check the state.
"""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -210,7 +209,7 @@ async def test_set_operation_bad_attr_and_state(
async def test_set_operation(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of new operation mode."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -225,9 +224,9 @@ async def test_set_operation(hass, mqtt_mock_entry_with_yaml_config):
async def test_set_operation_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting operation mode in pessimistic mode."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["mode_state_topic"] = "mode-state"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -249,9 +248,9 @@ async def test_set_operation_pessimistic(hass, mqtt_mock_entry_with_yaml_config)
async def test_set_operation_with_power_command(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of new operation mode with power command enabled."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["power_command_topic"] = "power-command"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -276,7 +275,7 @@ async def test_set_operation_with_power_command(hass, mqtt_mock_entry_with_yaml_
async def test_set_fan_mode_bad_attr(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test setting fan mode without required attribute."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -293,9 +292,9 @@ async def test_set_fan_mode_bad_attr(hass, mqtt_mock_entry_with_yaml_config, cap
async def test_set_fan_mode_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of new fan mode in pessimistic mode."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["fan_mode_state_topic"] = "fan-state"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -317,7 +316,7 @@ async def test_set_fan_mode_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
async def test_set_fan_mode(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of new fan mode."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -331,7 +330,7 @@ async def test_set_fan_mode(hass, mqtt_mock_entry_with_yaml_config):
async def test_set_swing_mode_bad_attr(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test setting swing mode without required attribute."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -348,9 +347,9 @@ async def test_set_swing_mode_bad_attr(hass, mqtt_mock_entry_with_yaml_config, c
async def test_set_swing_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting swing mode in pessimistic mode."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["swing_mode_state_topic"] = "swing-state"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -372,7 +371,7 @@ async def test_set_swing_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
async def test_set_swing(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of new swing mode."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -386,7 +385,7 @@ async def test_set_swing(hass, mqtt_mock_entry_with_yaml_config):
async def test_set_target_temperature(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting the target temperature."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -425,9 +424,9 @@ async def test_set_target_temperature_pessimistic(
hass, mqtt_mock_entry_with_yaml_config
):
"""Test setting the target temperature."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["temperature_state_topic"] = "temperature-state"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -449,7 +448,7 @@ async def test_set_target_temperature_pessimistic(
async def test_set_target_temperature_low_high(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting the low/high target temperature."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -467,10 +466,10 @@ async def test_set_target_temperature_low_highpessimistic(
hass, mqtt_mock_entry_with_yaml_config
):
"""Test setting the low/high target temperature."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["temperature_low_state_topic"] = "temperature-low-state"
config["climate"]["temperature_high_state_topic"] = "temperature-high-state"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -505,9 +504,9 @@ async def test_set_target_temperature_low_highpessimistic(
async def test_receive_mqtt_temperature(hass, mqtt_mock_entry_with_yaml_config):
"""Test getting the current temperature via MQTT."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["current_temperature_topic"] = "current_temperature"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -518,9 +517,9 @@ async def test_receive_mqtt_temperature(hass, mqtt_mock_entry_with_yaml_config):
async def test_handle_action_received(hass, mqtt_mock_entry_with_yaml_config):
"""Test getting the action received via MQTT."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["action_topic"] = "action"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -531,7 +530,7 @@ async def test_handle_action_received(hass, mqtt_mock_entry_with_yaml_config):
assert hvac_action is None
# Redefine actions according to https://developers.home-assistant.io/docs/core/entity/climate/#hvac-action
actions = ["off", "heating", "cooling", "drying", "idle", "fan"]
assert all(elem in actions for elem in CURRENT_HVAC_ACTIONS)
assert all(elem in actions for elem in HVACAction)
for action in actions:
async_fire_mqtt_message(hass, "action", action)
state = hass.states.get(ENTITY_CLIMATE)
@ -543,8 +542,8 @@ async def test_set_preset_mode_optimistic(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test setting of the preset mode."""
config = copy.deepcopy(DEFAULT_CONFIG)
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -591,9 +590,9 @@ async def test_set_preset_mode_pessimistic(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test setting of the preset mode."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["preset_mode_state_topic"] = "preset-mode-state"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -636,9 +635,9 @@ async def test_set_preset_mode_pessimistic(
async def test_set_aux_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of the aux heating in pessimistic mode."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["aux_state_topic"] = "aux-state"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -664,7 +663,7 @@ async def test_set_aux_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
async def test_set_aux(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of the aux heating."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -687,28 +686,28 @@ async def test_availability_when_connection_lost(
):
"""Test availability after MQTT disconnection."""
await help_test_availability_when_connection_lost(
hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability without defined availability topic."""
await help_test_availability_without_topic(
hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic."""
await help_test_default_availability_payload(
hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic."""
await help_test_custom_availability_payload(
hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
@ -716,13 +715,13 @@ async def test_get_target_temperature_low_high_with_templates(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test getting temperature high/low with templates."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["temperature_low_state_topic"] = "temperature-state"
config["climate"]["temperature_high_state_topic"] = "temperature-state"
config["climate"]["temperature_low_state_template"] = "{{ value_json.temp_low }}"
config["climate"]["temperature_high_state_template"] = "{{ value_json.temp_high }}"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -751,7 +750,7 @@ async def test_get_target_temperature_low_high_with_templates(
async def test_get_with_templates(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test getting various attributes with templates."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
# By default, just unquote the JSON-strings
config["climate"]["value_template"] = "{{ value_json }}"
config["climate"]["action_template"] = "{{ value_json }}"
@ -768,7 +767,7 @@ async def test_get_with_templates(hass, mqtt_mock_entry_with_yaml_config, caplog
config["climate"]["aux_state_topic"] = "aux-state"
config["climate"]["current_temperature_topic"] = "current-temperature"
config["climate"]["preset_mode_state_topic"] = "current-preset-mode"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -850,7 +849,7 @@ async def test_get_with_templates(hass, mqtt_mock_entry_with_yaml_config, caplog
async def test_set_and_templates(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test setting various attributes with templates."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
# Create simple templates
config["climate"]["fan_mode_command_template"] = "fan_mode: {{ value }}"
config["climate"]["preset_mode_command_template"] = "preset_mode: {{ value }}"
@ -860,7 +859,7 @@ async def test_set_and_templates(hass, mqtt_mock_entry_with_yaml_config, caplog)
config["climate"]["temperature_high_command_template"] = "temp_hi: {{ value }}"
config["climate"]["temperature_low_command_template"] = "temp_lo: {{ value }}"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -928,10 +927,10 @@ async def test_set_and_templates(hass, mqtt_mock_entry_with_yaml_config, caplog)
async def test_min_temp_custom(hass, mqtt_mock_entry_with_yaml_config):
"""Test a custom min temp."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["min_temp"] = 26
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -944,10 +943,10 @@ async def test_min_temp_custom(hass, mqtt_mock_entry_with_yaml_config):
async def test_max_temp_custom(hass, mqtt_mock_entry_with_yaml_config):
"""Test a custom max temp."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["max_temp"] = 60
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -960,10 +959,10 @@ async def test_max_temp_custom(hass, mqtt_mock_entry_with_yaml_config):
async def test_temp_step_custom(hass, mqtt_mock_entry_with_yaml_config):
"""Test a custom temp step."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["temp_step"] = 0.01
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -976,11 +975,11 @@ async def test_temp_step_custom(hass, mqtt_mock_entry_with_yaml_config):
async def test_temperature_unit(hass, mqtt_mock_entry_with_yaml_config):
"""Test that setting temperature unit converts temperature values."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["temperature_unit"] = "F"
config["climate"]["current_temperature_topic"] = "current_temperature"
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -995,7 +994,7 @@ async def test_setting_attribute_via_mqtt_json_message(
):
"""Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_via_mqtt_json_message(
hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
@ -1006,7 +1005,7 @@ async def test_setting_blocked_attribute_via_mqtt_json_message(
await help_test_setting_blocked_attribute_via_mqtt_json_message(
hass,
mqtt_mock_entry_no_yaml_config,
CLIMATE_DOMAIN,
climate.DOMAIN,
DEFAULT_CONFIG,
MQTT_CLIMATE_ATTRIBUTES_BLOCKED,
)
@ -1015,7 +1014,7 @@ async def test_setting_blocked_attribute_via_mqtt_json_message(
async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_with_template(
hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
@ -1024,39 +1023,50 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
climate.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, CLIMATE_DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
climate.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
climate.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one climate per unique_id."""
config = {
CLIMATE_DOMAIN: [
mqtt.DOMAIN: {
climate.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"power_state_topic": "test-topic",
"power_command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"power_state_topic": "test-topic",
"power_command_topic": "test_topic",
@ -1064,8 +1074,9 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, CLIMATE_DOMAIN, config
hass, mqtt_mock_entry_with_yaml_config, climate.DOMAIN, config
)
@ -1095,12 +1106,12 @@ async def test_encoding_subscribable_topics(
attribute_value,
):
"""Test handling of incoming encoded payload."""
config = copy.deepcopy(DEFAULT_CONFIG[CLIMATE_DOMAIN])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][climate.DOMAIN])
await help_test_encoding_subscribable_topics(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
CLIMATE_DOMAIN,
climate.DOMAIN,
config,
topic,
value,
@ -1111,9 +1122,9 @@ async def test_encoding_subscribable_topics(
async def test_discovery_removal_climate(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered climate."""
data = json.dumps(DEFAULT_CONFIG[CLIMATE_DOMAIN])
data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][climate.DOMAIN])
await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, CLIMATE_DOMAIN, data
hass, mqtt_mock_entry_no_yaml_config, caplog, climate.DOMAIN, data
)
@ -1122,7 +1133,7 @@ async def test_discovery_update_climate(hass, mqtt_mock_entry_no_yaml_config, ca
config1 = {"name": "Beer"}
config2 = {"name": "Milk"}
await help_test_discovery_update(
hass, mqtt_mock_entry_no_yaml_config, caplog, CLIMATE_DOMAIN, config1, config2
hass, mqtt_mock_entry_no_yaml_config, caplog, climate.DOMAIN, config1, config2
)
@ -1138,7 +1149,7 @@ async def test_discovery_update_unchanged_climate(
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
CLIMATE_DOMAIN,
climate.DOMAIN,
data1,
discovery_update,
)
@ -1150,52 +1161,53 @@ async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog):
data1 = '{ "name": "Beer", "power_command_topic": "test_topic#" }'
data2 = '{ "name": "Milk", "power_command_topic": "test_topic" }'
await help_test_discovery_broken(
hass, mqtt_mock_entry_no_yaml_config, caplog, CLIMATE_DOMAIN, data1, data2
hass, mqtt_mock_entry_no_yaml_config, caplog, climate.DOMAIN, data1, data2
)
async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT climate device registry integration."""
await help_test_entity_device_info_with_connection(
hass, mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT climate device registry integration."""
await help_test_entity_device_info_with_identifier(
hass, mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry update."""
await help_test_entity_device_info_update(
hass, mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry remove."""
await help_test_entity_device_info_remove(
hass, mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config):
"""Test MQTT subscriptions are managed when entity_id is updated."""
config = {
CLIMATE_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
climate.DOMAIN: {
"name": "test",
"mode_state_topic": "test-topic",
"availability_topic": "avty-topic",
}
}
}
await help_test_entity_id_update_subscriptions(
hass,
mqtt_mock_entry_with_yaml_config,
CLIMATE_DOMAIN,
climate.DOMAIN,
config,
["test-topic", "avty-topic"],
)
@ -1204,24 +1216,25 @@ async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_co
async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT discovery update when entity_id is updated."""
await help_test_entity_id_update_discovery_update(
hass, mqtt_mock_entry_no_yaml_config, CLIMATE_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, climate.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT debug info."""
config = {
CLIMATE_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
climate.DOMAIN: {
"name": "test",
"mode_command_topic": "command-topic",
"mode_state_topic": "test-topic",
}
}
}
await help_test_entity_debug_info_message(
hass,
mqtt_mock_entry_no_yaml_config,
CLIMATE_DOMAIN,
climate.DOMAIN,
config,
climate.SERVICE_TURN_ON,
command_topic="command-topic",
@ -1232,7 +1245,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
async def test_precision_default(hass, mqtt_mock_entry_with_yaml_config):
"""Test that setting precision to tenths works as intended."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1246,9 +1259,9 @@ async def test_precision_default(hass, mqtt_mock_entry_with_yaml_config):
async def test_precision_halves(hass, mqtt_mock_entry_with_yaml_config):
"""Test that setting precision to halves works as intended."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["precision"] = 0.5
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1262,9 +1275,9 @@ async def test_precision_halves(hass, mqtt_mock_entry_with_yaml_config):
async def test_precision_whole(hass, mqtt_mock_entry_with_yaml_config):
"""Test that setting precision to whole works as intended."""
config = copy.deepcopy(DEFAULT_CONFIG)
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["precision"] = 1.0
assert await async_setup_component(hass, CLIMATE_DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1364,10 +1377,10 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = climate.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
config = copy.deepcopy(DEFAULT_CONFIG)
if topic != "preset_mode_command_topic":
del config["preset_mode_command_topic"]
del config["preset_modes"]
del config[mqtt.DOMAIN][domain]["preset_mode_command_topic"]
del config[mqtt.DOMAIN][domain]["preset_modes"]
await help_test_publishing_with_custom_encoding(
hass,
@ -1385,34 +1398,46 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = CLIMATE_DOMAIN
config = DEFAULT_CONFIG[domain]
domain = climate.DOMAIN
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = CLIMATE_DOMAIN
config = DEFAULT_CONFIG[domain]
domain = climate.DOMAIN
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = CLIMATE_DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
platform = climate.DOMAIN
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = climate.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = climate.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -53,7 +53,7 @@ async def help_test_availability_when_connection_lost(
hass, mqtt_mock_entry_with_yaml_config, domain, config
):
"""Test availability after MQTT disconnection."""
assert await async_setup_component(hass, domain, config)
assert await async_setup_component(hass, mqtt.DOMAIN, config)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -72,8 +72,8 @@ async def help_test_availability_without_topic(
hass, mqtt_mock_entry_with_yaml_config, domain, config
):
"""Test availability without defined availability topic."""
assert "availability_topic" not in config[domain]
assert await async_setup_component(hass, domain, config)
assert "availability_topic" not in config[mqtt.DOMAIN][domain]
assert await async_setup_component(hass, mqtt.DOMAIN, config)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -96,10 +96,10 @@ async def help_test_default_availability_payload(
"""
# Add availability settings to config
config = copy.deepcopy(config)
config[domain]["availability_topic"] = "availability-topic"
config[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic"
assert await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -147,13 +147,13 @@ async def help_test_default_availability_list_payload(
"""
# Add availability settings to config
config = copy.deepcopy(config)
config[domain]["availability"] = [
config[mqtt.DOMAIN][domain]["availability"] = [
{"topic": "availability-topic1"},
{"topic": "availability-topic2"},
]
assert await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -213,14 +213,14 @@ async def help_test_default_availability_list_payload_all(
"""
# Add availability settings to config
config = copy.deepcopy(config)
config[domain]["availability_mode"] = "all"
config[domain]["availability"] = [
config[mqtt.DOMAIN][domain]["availability_mode"] = "all"
config[mqtt.DOMAIN][domain]["availability"] = [
{"topic": "availability-topic1"},
{"topic": "availability-topic2"},
]
assert await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -281,14 +281,14 @@ async def help_test_default_availability_list_payload_any(
"""
# Add availability settings to config
config = copy.deepcopy(config)
config[domain]["availability_mode"] = "any"
config[domain]["availability"] = [
config[mqtt.DOMAIN][domain]["availability_mode"] = "any"
config[mqtt.DOMAIN][domain]["availability"] = [
{"topic": "availability-topic1"},
{"topic": "availability-topic2"},
]
assert await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -331,7 +331,6 @@ async def help_test_default_availability_list_payload_any(
async def help_test_default_availability_list_single(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
domain,
config,
@ -345,22 +344,17 @@ async def help_test_default_availability_list_single(
"""
# Add availability settings to config
config = copy.deepcopy(config)
config[domain]["availability"] = [
config[mqtt.DOMAIN][domain]["availability"] = [
{"topic": "availability-topic1"},
]
config[domain]["availability_topic"] = "availability-topic"
assert await async_setup_component(
config[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic"
assert not await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
state = hass.states.get(f"{domain}.test")
assert state is None
assert (
"Invalid config for [sensor.mqtt]: two or more values in the same group of exclusion 'availability'"
"Invalid config for [mqtt]: two or more values in the same group of exclusion 'availability'"
in caplog.text
)
@ -380,12 +374,12 @@ async def help_test_custom_availability_payload(
"""
# Add availability settings to config
config = copy.deepcopy(config)
config[domain]["availability_topic"] = "availability-topic"
config[domain]["payload_available"] = "good"
config[domain]["payload_not_available"] = "nogood"
config[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic"
config[mqtt.DOMAIN][domain]["payload_available"] = "good"
config[mqtt.DOMAIN][domain]["payload_not_available"] = "nogood"
assert await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -434,17 +428,17 @@ async def help_test_discovery_update_availability(
await mqtt_mock_entry_no_yaml_config()
# Add availability settings to config
config1 = copy.deepcopy(config)
config1[domain]["availability_topic"] = "availability-topic1"
config1[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic1"
config2 = copy.deepcopy(config)
config2[domain]["availability"] = [
config2[mqtt.DOMAIN][domain]["availability"] = [
{"topic": "availability-topic2"},
{"topic": "availability-topic3"},
]
config3 = copy.deepcopy(config)
config3[domain]["availability_topic"] = "availability-topic4"
data1 = json.dumps(config1[domain])
data2 = json.dumps(config2[domain])
data3 = json.dumps(config3[domain])
config3[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic4"
data1 = json.dumps(config1[mqtt.DOMAIN][domain])
data2 = json.dumps(config2[mqtt.DOMAIN][domain])
data3 = json.dumps(config3[mqtt.DOMAIN][domain])
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1)
await hass.async_block_till_done()
@ -508,10 +502,10 @@ async def help_test_setting_attribute_via_mqtt_json_message(
"""
# Add JSON attributes settings to config
config = copy.deepcopy(config)
config[domain]["json_attributes_topic"] = "attr-topic"
config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic"
assert await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -535,8 +529,8 @@ async def help_test_setting_blocked_attribute_via_mqtt_json_message(
# Add JSON attributes settings to config
config = copy.deepcopy(config)
config[domain]["json_attributes_topic"] = "attr-topic"
data = json.dumps(config[domain])
config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic"
data = json.dumps(config[mqtt.DOMAIN][domain])
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
await hass.async_block_till_done()
val = "abc123"
@ -561,11 +555,13 @@ async def help_test_setting_attribute_with_template(
"""
# Add JSON attributes settings to config
config = copy.deepcopy(config)
config[domain]["json_attributes_topic"] = "attr-topic"
config[domain]["json_attributes_template"] = "{{ value_json['Timer1'] | tojson }}"
config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic"
config[mqtt.DOMAIN][domain][
"json_attributes_template"
] = "{{ value_json['Timer1'] | tojson }}"
assert await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -589,10 +585,10 @@ async def help_test_update_with_json_attrs_not_dict(
"""
# Add JSON attributes settings to config
config = copy.deepcopy(config)
config[domain]["json_attributes_topic"] = "attr-topic"
config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic"
assert await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -605,7 +601,7 @@ async def help_test_update_with_json_attrs_not_dict(
assert "JSON result was not a dictionary" in caplog.text
async def help_test_update_with_json_attrs_bad_JSON(
async def help_test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog, domain, config
):
"""Test JSON validation of attributes.
@ -614,10 +610,10 @@ async def help_test_update_with_json_attrs_bad_JSON(
"""
# Add JSON attributes settings to config
config = copy.deepcopy(config)
config[domain]["json_attributes_topic"] = "attr-topic"
config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic"
assert await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -640,11 +636,11 @@ async def help_test_discovery_update_attr(
await mqtt_mock_entry_no_yaml_config()
# Add JSON attributes settings to config
config1 = copy.deepcopy(config)
config1[domain]["json_attributes_topic"] = "attr-topic1"
config1[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic1"
config2 = copy.deepcopy(config)
config2[domain]["json_attributes_topic"] = "attr-topic2"
data1 = json.dumps(config1[domain])
data2 = json.dumps(config2[domain])
config2[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic2"
data1 = json.dumps(config1[mqtt.DOMAIN][domain])
data2 = json.dumps(config2[mqtt.DOMAIN][domain])
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1)
await hass.async_block_till_done()
@ -669,7 +665,7 @@ async def help_test_discovery_update_attr(
async def help_test_unique_id(hass, mqtt_mock_entry_with_yaml_config, domain, config):
"""Test unique id option only creates one entity per unique_id."""
assert await async_setup_component(hass, domain, config)
assert await async_setup_component(hass, mqtt.DOMAIN, config)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert len(hass.states.async_entity_ids(domain)) == 1
@ -881,7 +877,7 @@ async def help_test_encoding_subscribable_topics(
await hass.async_block_till_done()
assert await async_setup_component(
hass, domain, {domain: [config1, config2, config3]}
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {domain: [config1, config2, config3]}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -944,7 +940,7 @@ async def help_test_entity_device_info_with_identifier(
"""
await mqtt_mock_entry_no_yaml_config()
# Add device settings to config
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique"
@ -975,7 +971,7 @@ async def help_test_entity_device_info_with_connection(
"""
await mqtt_mock_entry_no_yaml_config()
# Add device settings to config
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_MAC)
config["unique_id"] = "veryunique"
@ -1005,7 +1001,7 @@ async def help_test_entity_device_info_remove(
"""Test device registry remove."""
await mqtt_mock_entry_no_yaml_config()
# Add device settings to config
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique"
@ -1037,7 +1033,7 @@ async def help_test_entity_device_info_update(
"""
await mqtt_mock_entry_no_yaml_config()
# Add device settings to config
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique"
@ -1067,19 +1063,19 @@ async def help_test_entity_id_update_subscriptions(
"""Test MQTT subscriptions are managed when entity_id is updated."""
# Add unique_id to config
config = copy.deepcopy(config)
config[domain]["unique_id"] = "TOTALLY_UNIQUE"
config[mqtt.DOMAIN][domain]["unique_id"] = "TOTALLY_UNIQUE"
if topics is None:
# Add default topics to config
config[domain]["availability_topic"] = "avty-topic"
config[domain]["state_topic"] = "test-topic"
config[mqtt.DOMAIN][domain]["availability_topic"] = "avty-topic"
config[mqtt.DOMAIN][domain]["state_topic"] = "test-topic"
topics = ["avty-topic", "test-topic"]
assert len(topics) > 0
registry = mock_registry(hass, {})
assert await async_setup_component(
hass,
domain,
mqtt.DOMAIN,
config,
)
await hass.async_block_till_done()
@ -1111,16 +1107,16 @@ async def help_test_entity_id_update_discovery_update(
# Add unique_id to config
await mqtt_mock_entry_no_yaml_config()
config = copy.deepcopy(config)
config[domain]["unique_id"] = "TOTALLY_UNIQUE"
config[mqtt.DOMAIN][domain]["unique_id"] = "TOTALLY_UNIQUE"
if topic is None:
# Add default topic to config
config[domain]["availability_topic"] = "avty-topic"
config[mqtt.DOMAIN][domain]["availability_topic"] = "avty-topic"
topic = "avty-topic"
ent_registry = mock_registry(hass, {})
data = json.dumps(config[domain])
data = json.dumps(config[mqtt.DOMAIN][domain])
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
await hass.async_block_till_done()
@ -1135,8 +1131,8 @@ async def help_test_entity_id_update_discovery_update(
ent_registry.async_update_entity(f"{domain}.test", new_entity_id=f"{domain}.milk")
await hass.async_block_till_done()
config[domain]["availability_topic"] = f"{topic}_2"
data = json.dumps(config[domain])
config[mqtt.DOMAIN][domain]["availability_topic"] = f"{topic}_2"
data = json.dumps(config[mqtt.DOMAIN][domain])
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
await hass.async_block_till_done()
assert len(hass.states.async_entity_ids(domain)) == 1
@ -1155,9 +1151,10 @@ async def help_test_entity_debug_info(
"""
await mqtt_mock_entry_no_yaml_config()
# Add device settings to config
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique"
config["platform"] = "mqtt"
registry = dr.async_get(hass)
@ -1192,7 +1189,7 @@ async def help_test_entity_debug_info_max_messages(
"""
await mqtt_mock_entry_no_yaml_config()
# Add device settings to config
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique"
@ -1256,7 +1253,7 @@ async def help_test_entity_debug_info_message(
"""
# Add device settings to config
await mqtt_mock_entry_no_yaml_config()
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique"
@ -1359,9 +1356,10 @@ async def help_test_entity_debug_info_remove(
"""
await mqtt_mock_entry_no_yaml_config()
# Add device settings to config
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique"
config["platform"] = "mqtt"
registry = dr.async_get(hass)
@ -1405,9 +1403,10 @@ async def help_test_entity_debug_info_update_entity_id(
"""
await mqtt_mock_entry_no_yaml_config()
# Add device settings to config
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique"
config["platform"] = "mqtt"
dev_registry = dr.async_get(hass)
ent_registry = mock_registry(hass, {})
@ -1461,7 +1460,7 @@ async def help_test_entity_disabled_by_default(
"""Test device registry remove."""
await mqtt_mock_entry_no_yaml_config()
# Add device settings to config
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["enabled_by_default"] = False
config["unique_id"] = "veryunique1"
@ -1500,7 +1499,7 @@ async def help_test_entity_category(
"""Test device registry remove."""
await mqtt_mock_entry_no_yaml_config()
# Add device settings to config
config = copy.deepcopy(config[domain])
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
ent_registry = er.async_get(hass)
@ -1564,7 +1563,7 @@ async def help_test_publishing_with_custom_encoding(
setup_config = []
service_data = {}
for test_id, test_data in test_config.items():
test_config_setup = copy.deepcopy(config)
test_config_setup = copy.deepcopy(config[mqtt.DOMAIN][domain])
test_config_setup.update(
{
topic: f"cmd/{test_id}",
@ -1573,7 +1572,7 @@ async def help_test_publishing_with_custom_encoding(
)
if test_data["encoding"] is not None:
test_config_setup["encoding"] = test_data["encoding"]
if test_data["cmd_tpl"]:
if template and test_data["cmd_tpl"]:
test_config_setup[
template
] = f"{{{{ (('%.1f'|format({tpl_par}))[0] if is_number({tpl_par}) else {tpl_par}[0]) | ord | pack('b') }}}}"
@ -1587,8 +1586,8 @@ async def help_test_publishing_with_custom_encoding(
# setup test entities
assert await async_setup_component(
hass,
domain,
{domain: setup_config},
mqtt.DOMAIN,
{mqtt.DOMAIN: {domain: setup_config}},
)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1698,24 +1697,29 @@ async def help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
):
"""Test reloading an MQTT platform."""
config = copy.deepcopy(config[mqtt.DOMAIN][domain])
# Create and test an old config of 2 entities based on the config supplied
old_config_1 = copy.deepcopy(config)
old_config_1["name"] = "test_old_1"
old_config_2 = copy.deepcopy(config)
old_config_2["name"] = "test_old_2"
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
old_config_3 = copy.deepcopy(config)
old_config_3["name"] = "test_old_3"
old_config_3.pop("platform")
old_config_3["platform"] = mqtt.DOMAIN
old_config_4 = copy.deepcopy(config)
old_config_4["name"] = "test_old_4"
old_config_4.pop("platform")
old_config_4["platform"] = mqtt.DOMAIN
old_config = {
domain: [old_config_1, old_config_2],
"mqtt": {domain: [old_config_3, old_config_4]},
mqtt.DOMAIN: {domain: [old_config_1, old_config_2]},
domain: [old_config_3, old_config_4],
}
assert await async_setup_component(hass, domain, old_config)
assert await async_setup_component(hass, mqtt.DOMAIN, old_config)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -1731,21 +1735,24 @@ async def help_test_reloadable(
new_config_1["name"] = "test_new_1"
new_config_2 = copy.deepcopy(config)
new_config_2["name"] = "test_new_2"
new_config_extra = copy.deepcopy(config)
new_config_extra["name"] = "test_new_5"
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
new_config_3 = copy.deepcopy(config)
new_config_3["name"] = "test_new_3"
new_config_3.pop("platform")
new_config_3["platform"] = mqtt.DOMAIN
new_config_4 = copy.deepcopy(config)
new_config_4["name"] = "test_new_4"
new_config_4.pop("platform")
new_config_5 = copy.deepcopy(config)
new_config_5["name"] = "test_new_5"
new_config_6 = copy.deepcopy(config)
new_config_6["name"] = "test_new_6"
new_config_6.pop("platform")
new_config_4["platform"] = mqtt.DOMAIN
new_config_extra_legacy = copy.deepcopy(config)
new_config_extra_legacy["name"] = "test_new_6"
new_config_extra_legacy["platform"] = mqtt.DOMAIN
new_config = {
domain: [new_config_1, new_config_2, new_config_5],
"mqtt": {domain: [new_config_3, new_config_4, new_config_6]},
mqtt.DOMAIN: {domain: [new_config_1, new_config_2, new_config_extra]},
domain: [new_config_3, new_config_4, new_config_extra_legacy],
}
await help_test_reload_with_config(hass, caplog, tmp_path, new_config)
@ -1760,9 +1767,12 @@ async def help_test_reloadable(
assert hass.states.get(f"{domain}.test_new_6")
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config):
"""Test reloading an MQTT platform when config entry is setup late."""
"""Test reloading an MQTT platform when config entry is setup is late."""
# Create and test an old config of 2 entities based on the config supplied
# using the deprecated platform schema
old_config_1 = copy.deepcopy(config)
old_config_1["name"] = "test_old_1"
old_config_2 = copy.deepcopy(config)
@ -1815,7 +1825,7 @@ async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config):
assert hass.states.get(f"{domain}.test_new_3")
async def help_test_setup_manual_entity_from_yaml(hass, platform, config):
async def help_test_setup_manual_entity_from_yaml(hass, config):
"""Help to test setup from yaml through configuration entry."""
calls = MagicMock()
@ -1823,9 +1833,7 @@ async def help_test_setup_manual_entity_from_yaml(hass, platform, config):
"""Mock reload."""
calls()
config_structure = {mqtt.DOMAIN: {platform: config}}
await async_setup_component(hass, mqtt.DOMAIN, config_structure)
assert await async_setup_component(hass, mqtt.DOMAIN, config)
# Mock config entry
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
entry.add_to_hass(hass)
@ -1864,14 +1872,14 @@ async def help_test_unload_config_entry_with_platform(
"""Test unloading the MQTT config entry with a specific platform domain."""
# prepare setup through configuration.yaml
config_setup = copy.deepcopy(config)
config_setup["name"] = "config_setup"
config_setup[mqtt.DOMAIN][domain]["name"] = "config_setup"
config_name = config_setup
assert await async_setup_component(hass, domain, {domain: [config_setup]})
assert await async_setup_component(hass, mqtt.DOMAIN, config_setup)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
# prepare setup through discovery
discovery_setup = copy.deepcopy(config)
discovery_setup = copy.deepcopy(config[mqtt.DOMAIN][domain])
discovery_setup["name"] = "discovery_setup"
async_fire_mqtt_message(
hass, f"homeassistant/{domain}/bla/config", json.dumps(discovery_setup)

View File

@ -155,7 +155,7 @@ async def test_manual_config_set(
assert await async_setup_component(hass, "mqtt", {"mqtt": {"broker": "bla"}})
await hass.async_block_till_done()
# do not try to reload
del hass.data["mqtt_reload_needed"]
hass.data["mqtt"].reload_needed = False
assert len(mock_finish_setup.mock_calls) == 0
mock_try_connection.return_value = True

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,11 @@
"""The tests for the MQTT device tracker platform using configuration.yaml."""
"""The tests for the MQTT device tracker platform using configuration.yaml with legacy schema."""
import json
from unittest.mock import patch
import pytest
from homeassistant.components.device_tracker.const import DOMAIN, SOURCE_TYPE_BLUETOOTH
from homeassistant.components import device_tracker
from homeassistant.components.device_tracker import SourceType
from homeassistant.config_entries import ConfigEntryDisabler
from homeassistant.const import CONF_PLATFORM, STATE_HOME, STATE_NOT_HOME, Platform
from homeassistant.setup import async_setup_component
@ -12,7 +13,6 @@ from homeassistant.setup import async_setup_component
from .test_common import (
MockConfigEntry,
help_test_entry_reload_with_new_config,
help_test_setup_manual_entity_from_yaml,
help_test_unload_config_entry,
)
@ -45,7 +45,14 @@ async def test_legacy_ensure_device_tracker_platform_validation(
dev_id = "paulus"
topic = "/location/paulus"
assert await async_setup_component(
hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: topic}}}
hass,
device_tracker.DOMAIN,
{
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: topic},
}
},
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -59,13 +66,15 @@ async def test_legacy_new_message(
"""Test new message."""
await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}"
entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/paulus"
location = "work"
hass.config.components = {"mqtt", "zone"}
assert await async_setup_component(
hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: topic}}}
hass,
device_tracker.DOMAIN,
{device_tracker.DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: topic}}},
)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
@ -79,7 +88,7 @@ async def test_legacy_single_level_wildcard_topic(
"""Test single level wildcard topic."""
await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}"
entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
subscription = "/location/+/paulus"
topic = "/location/room/paulus"
location = "work"
@ -87,8 +96,13 @@ async def test_legacy_single_level_wildcard_topic(
hass.config.components = {"mqtt", "zone"}
assert await async_setup_component(
hass,
DOMAIN,
{DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: subscription}}},
device_tracker.DOMAIN,
{
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: subscription},
}
},
)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
@ -102,7 +116,7 @@ async def test_legacy_multi_level_wildcard_topic(
"""Test multi level wildcard topic."""
await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}"
entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
subscription = "/location/#"
topic = "/location/room/paulus"
location = "work"
@ -110,8 +124,13 @@ async def test_legacy_multi_level_wildcard_topic(
hass.config.components = {"mqtt", "zone"}
assert await async_setup_component(
hass,
DOMAIN,
{DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: subscription}}},
device_tracker.DOMAIN,
{
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: subscription},
}
},
)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
@ -125,7 +144,7 @@ async def test_legacy_single_level_wildcard_topic_not_matching(
"""Test not matching single level wildcard topic."""
await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}"
entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
subscription = "/location/+/paulus"
topic = "/location/paulus"
location = "work"
@ -133,8 +152,13 @@ async def test_legacy_single_level_wildcard_topic_not_matching(
hass.config.components = {"mqtt", "zone"}
assert await async_setup_component(
hass,
DOMAIN,
{DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: subscription}}},
device_tracker.DOMAIN,
{
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: subscription},
}
},
)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
@ -148,7 +172,7 @@ async def test_legacy_multi_level_wildcard_topic_not_matching(
"""Test not matching multi level wildcard topic."""
await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}"
entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
subscription = "/location/#"
topic = "/somewhere/room/paulus"
location = "work"
@ -156,8 +180,13 @@ async def test_legacy_multi_level_wildcard_topic_not_matching(
hass.config.components = {"mqtt", "zone"}
assert await async_setup_component(
hass,
DOMAIN,
{DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: subscription}}},
device_tracker.DOMAIN,
{
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: subscription},
}
},
)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
@ -171,7 +200,7 @@ async def test_legacy_matching_custom_payload_for_home_and_not_home(
"""Test custom payload_home sets state to home and custom payload_not_home sets state to not_home."""
await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}"
entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/paulus"
payload_home = "present"
payload_not_home = "not present"
@ -179,9 +208,9 @@ async def test_legacy_matching_custom_payload_for_home_and_not_home(
hass.config.components = {"mqtt", "zone"}
assert await async_setup_component(
hass,
DOMAIN,
device_tracker.DOMAIN,
{
DOMAIN: {
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: topic},
"payload_home": payload_home,
@ -205,7 +234,7 @@ async def test_legacy_not_matching_custom_payload_for_home_and_not_home(
"""Test not matching payload does not set state to home or not_home."""
await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}"
entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/paulus"
payload_home = "present"
payload_not_home = "not present"
@ -214,9 +243,9 @@ async def test_legacy_not_matching_custom_payload_for_home_and_not_home(
hass.config.components = {"mqtt", "zone"}
assert await async_setup_component(
hass,
DOMAIN,
device_tracker.DOMAIN,
{
DOMAIN: {
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: topic},
"payload_home": payload_home,
@ -237,17 +266,17 @@ async def test_legacy_matching_source_type(
"""Test setting source type."""
await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}"
entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/paulus"
source_type = SOURCE_TYPE_BLUETOOTH
source_type = SourceType.BLUETOOTH
location = "work"
hass.config.components = {"mqtt", "zone"}
assert await async_setup_component(
hass,
DOMAIN,
device_tracker.DOMAIN,
{
DOMAIN: {
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: topic},
"source_type": source_type,
@ -257,23 +286,10 @@ async def test_legacy_matching_source_type(
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
assert hass.states.get(entity_id).attributes["source_type"] == SOURCE_TYPE_BLUETOOTH
async def test_setup_with_modern_schema(hass, mock_device_tracker_conf):
"""Test setup using the modern schema."""
dev_id = "jan"
entity_id = f"{DOMAIN}.{dev_id}"
topic = "/location/jan"
hass.config.components = {"zone"}
config = {"name": dev_id, "state_topic": topic}
await help_test_setup_manual_entity_from_yaml(hass, DOMAIN, config)
assert hass.states.get(entity_id) is not None
assert hass.states.get(entity_id).attributes["source_type"] == SourceType.BLUETOOTH
# Deprecated in HA Core 2022.6
async def test_unload_entry(
hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config, tmp_path
):
@ -281,13 +297,15 @@ async def test_unload_entry(
# setup through configuration.yaml
await mqtt_mock_entry_no_yaml_config()
dev_id = "jan"
entity_id = f"{DOMAIN}.{dev_id}"
entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/jan"
location = "home"
hass.config.components = {"mqtt", "zone"}
assert await async_setup_component(
hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: topic}}}
hass,
device_tracker.DOMAIN,
{device_tracker.DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: topic}}},
)
async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done()
@ -296,7 +314,7 @@ async def test_unload_entry(
# setup through discovery
dev_id = "piet"
subscription = "/location/#"
domain = DOMAIN
domain = device_tracker.DOMAIN
discovery_config = {
"devices": {dev_id: subscription},
"state_topic": "some-state",
@ -330,21 +348,22 @@ async def test_unload_entry(
assert discovery_setup_entity is None
# Deprecated in HA Core 2022.6
async def test_reload_entry_legacy(
hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config, tmp_path
):
"""Test reloading the config entry with manual MQTT items."""
# setup through configuration.yaml
await mqtt_mock_entry_no_yaml_config()
entity_id = f"{DOMAIN}.jan"
entity_id = f"{device_tracker.DOMAIN}.jan"
topic = "location/jan"
location = "home"
config = {
DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {"jan": topic}},
device_tracker.DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {"jan": topic}},
}
hass.config.components = {"mqtt", "zone"}
assert await async_setup_component(hass, DOMAIN, config)
assert await async_setup_component(hass, device_tracker.DOMAIN, config)
await hass.async_block_till_done()
async_fire_mqtt_message(hass, topic, location)
@ -360,6 +379,7 @@ async def test_reload_entry_legacy(
assert hass.states.get(entity_id).state == location
# Deprecated in HA Core 2022.6
async def test_setup_with_disabled_entry(
hass, mock_device_tracker_conf, caplog
) -> None:
@ -372,11 +392,11 @@ async def test_setup_with_disabled_entry(
topic = "location/jan"
config = {
DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {"jan": topic}},
device_tracker.DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {"jan": topic}},
}
hass.config.components = {"mqtt", "zone"}
await async_setup_component(hass, DOMAIN, config)
await async_setup_component(hass, device_tracker.DOMAIN, config)
await hass.async_block_till_done()
assert (

View File

@ -1,25 +1,29 @@
"""The tests for the MQTT device_tracker discovery platform."""
"""The tests for the MQTT device_tracker platform."""
from unittest.mock import patch
import pytest
from homeassistant.components import device_tracker
from homeassistant.components import device_tracker, mqtt
from homeassistant.components.mqtt.const import DOMAIN as MQTT_DOMAIN
from homeassistant.components.mqtt.discovery import ALREADY_DISCOVERED
from homeassistant.const import STATE_HOME, STATE_NOT_HOME, STATE_UNKNOWN, Platform
from homeassistant.setup import async_setup_component
from .test_common import help_test_setting_blocked_attribute_via_mqtt_json_message
from .test_common import (
help_test_setting_blocked_attribute_via_mqtt_json_message,
help_test_setup_manual_entity_from_yaml,
)
from tests.common import async_fire_mqtt_message, mock_device_registry, mock_registry
DEFAULT_CONFIG = {
mqtt.DOMAIN: {
device_tracker.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
}
}
}
@ -433,3 +437,18 @@ async def test_setting_blocked_attribute_via_mqtt_json_message(
DEFAULT_CONFIG,
None,
)
async def test_setup_with_modern_schema(hass, mock_device_tracker_conf):
"""Test setup using the modern schema."""
dev_id = "jan"
entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/jan"
config = {
mqtt.DOMAIN: {device_tracker.DOMAIN: {"name": dev_id, "state_topic": topic}}
}
await help_test_setup_manual_entity_from_yaml(hass, config)
assert hass.states.get(entity_id) is not None

View File

@ -1438,7 +1438,7 @@ async def test_clean_up_registry_monitoring(
):
"""Test registry monitoring hook is removed after a reload."""
await mqtt_mock_entry_no_yaml_config()
hooks: dict = hass.data[mqtt.const.DATA_MQTT_DISCOVERY_REGISTRY_HOOKS]
hooks: dict = hass.data["mqtt"].discovery_registry_hooks
# discover an entity that is not enabled by default
config1 = {
"name": "sbfspot_12345",

View File

@ -5,7 +5,7 @@ from unittest.mock import patch
import pytest
from voluptuous.error import MultipleInvalid
from homeassistant.components import fan
from homeassistant.components import fan, mqtt
from homeassistant.components.fan import (
ATTR_OSCILLATING,
ATTR_PERCENTAGE,
@ -59,7 +59,7 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
@ -67,14 +67,20 @@ from tests.common import async_fire_mqtt_message
from tests.components.fan import common
DEFAULT_CONFIG = {
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
}
}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[fan.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def fan_platform_only():
@ -83,18 +89,15 @@ def fan_platform_only():
yield
async def test_fail_setup_if_no_command_topic(
hass, caplog, mqtt_mock_entry_no_yaml_config
):
async def test_fail_setup_if_no_command_topic(hass, caplog):
"""Test if command fails with command topic."""
assert await async_setup_component(
hass, fan.DOMAIN, {fan.DOMAIN: {"platform": "mqtt", "name": "test"}}
assert not await async_setup_component(
hass,
mqtt.DOMAIN,
{mqtt.DOMAIN: {fan.DOMAIN: {"name": "test"}}},
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert hass.states.get("fan.test") is None
assert (
"Invalid config for [fan.mqtt]: required key not provided @ data['command_topic']"
"Invalid config for [mqtt]: required key not provided @ data['mqtt']['fan'][0]['command_topic']"
in caplog.text
)
@ -105,10 +108,10 @@ async def test_controlling_state_via_topic(
"""Test the controlling state via topic."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -135,6 +138,7 @@ async def test_controlling_state_via_topic(
"payload_reset_percentage": "rEset_percentage",
"payload_reset_preset_mode": "rEset_preset_mode",
}
}
},
)
await hass.async_block_till_done()
@ -226,11 +230,11 @@ async def test_controlling_state_via_topic_with_different_speed_range(
"""Test the controlling state via topic using an alternate speed range."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: [
{
"platform": "mqtt",
"name": "test1",
"command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic1",
@ -239,7 +243,6 @@ async def test_controlling_state_via_topic_with_different_speed_range(
"speed_range_max": 100,
},
{
"platform": "mqtt",
"name": "test2",
"command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic2",
@ -248,7 +251,6 @@ async def test_controlling_state_via_topic_with_different_speed_range(
"speed_range_max": 200,
},
{
"platform": "mqtt",
"name": "test3",
"command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic3",
@ -257,6 +259,7 @@ async def test_controlling_state_via_topic_with_different_speed_range(
"speed_range_max": 1023,
},
]
}
},
)
await hass.async_block_till_done()
@ -289,10 +292,10 @@ async def test_controlling_state_via_topic_no_percentage_topics(
"""Test the controlling state via topic without percentage topics."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -306,6 +309,7 @@ async def test_controlling_state_via_topic_no_percentage_topics(
"breeze",
],
}
}
},
)
await hass.async_block_till_done()
@ -345,10 +349,10 @@ async def test_controlling_state_via_topic_and_json_message(
"""Test the controlling state via topic and JSON message (percentage mode)."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -373,6 +377,7 @@ async def test_controlling_state_via_topic_and_json_message(
"speed_range_min": 1,
"speed_range_max": 100,
}
}
},
)
await hass.async_block_till_done()
@ -450,10 +455,10 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic(
"""Test the controlling state via topic and JSON message using a shared topic."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "shared-state-topic",
"command_topic": "command-topic",
@ -478,6 +483,7 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic(
"speed_range_min": 1,
"speed_range_max": 100,
}
}
},
)
await hass.async_block_till_done()
@ -540,10 +546,10 @@ async def test_sending_mqtt_commands_and_optimistic(
"""Test optimistic mode without state topic."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"payload_off": "StAtE_OfF",
@ -559,6 +565,7 @@ async def test_sending_mqtt_commands_and_optimistic(
"silent",
],
}
}
},
)
await hass.async_block_till_done()
@ -664,11 +671,11 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(
"""Test the controlling state via topic using an alternate speed range."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: [
{
"platform": "mqtt",
"name": "test1",
"command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic1",
@ -677,7 +684,6 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(
"speed_range_max": 3,
},
{
"platform": "mqtt",
"name": "test2",
"command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic2",
@ -686,7 +692,6 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(
"speed_range_max": 200,
},
{
"platform": "mqtt",
"name": "test3",
"command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic3",
@ -695,6 +700,7 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(
"speed_range_max": 1023,
},
]
}
},
)
await hass.async_block_till_done()
@ -771,10 +777,10 @@ async def test_sending_mqtt_commands_and_optimistic_no_legacy(
"""Test optimistic mode without state topic without legacy speed command topic."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic",
@ -785,6 +791,7 @@ async def test_sending_mqtt_commands_and_optimistic_no_legacy(
"silent",
],
}
}
},
)
await hass.async_block_till_done()
@ -902,10 +909,10 @@ async def test_sending_mqtt_command_templates_(
"""Test optimistic mode without state topic without legacy speed command topic."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"command_template": "state: {{ value }}",
@ -921,6 +928,7 @@ async def test_sending_mqtt_command_templates_(
"silent",
],
}
}
},
)
await hass.async_block_till_done()
@ -1044,10 +1052,10 @@ async def test_sending_mqtt_commands_and_optimistic_no_percentage_topic(
"""Test optimistic mode without state topic without percentage command topic."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic",
@ -1059,6 +1067,7 @@ async def test_sending_mqtt_commands_and_optimistic_no_percentage_topic(
"high",
],
}
}
},
)
await hass.async_block_till_done()
@ -1105,10 +1114,10 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
"""Test optimistic mode with state topic and turn on attributes."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -1125,6 +1134,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
],
"optimistic": True,
}
}
},
)
await hass.async_block_till_done()
@ -1355,7 +1365,7 @@ async def test_encoding_subscribable_topics(
attribute_value,
):
"""Test handling of incoming encoded payload."""
config = copy.deepcopy(DEFAULT_CONFIG[fan.DOMAIN])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][fan.DOMAIN])
config[ATTR_PRESET_MODES] = ["eco", "auto"]
config[CONF_PRESET_MODE_COMMAND_TOPIC] = "fan/some_preset_mode_command_topic"
config[CONF_PERCENTAGE_COMMAND_TOPIC] = "fan/some_percentage_command_topic"
@ -1377,10 +1387,10 @@ async def test_attributes(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test attributes."""
assert await async_setup_component(
hass,
fan.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic",
@ -1391,6 +1401,7 @@ async def test_attributes(hass, mqtt_mock_entry_with_yaml_config, caplog):
"silent",
],
}
}
},
)
await hass.async_block_till_done()
@ -1424,177 +1435,203 @@ async def test_attributes(hass, mqtt_mock_entry_with_yaml_config, caplog):
assert state.attributes.get(fan.ATTR_OSCILLATING) is False
async def test_supported_features(hass, mqtt_mock_entry_with_yaml_config):
"""Test optimistic mode without state topic."""
assert await async_setup_component(
hass,
fan.DOMAIN,
@pytest.mark.parametrize(
"name,config,success,features",
[
(
"test1",
{
fan.DOMAIN: [
{
"platform": "mqtt",
"name": "test1",
"command_topic": "command-topic",
},
True,
0,
),
(
"test2",
{
"platform": "mqtt",
"name": "test2",
"command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic",
},
True,
fan.SUPPORT_OSCILLATE,
),
(
"test3",
{
"platform": "mqtt",
"name": "test3b",
"name": "test3",
"command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic",
},
True,
fan.SUPPORT_SET_SPEED,
),
(
"test4",
{
"platform": "mqtt",
"name": "test3c1",
"name": "test4",
"command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic",
},
False,
None,
),
(
"test5",
{
"platform": "mqtt",
"name": "test3c2",
"name": "test5",
"command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["eco", "auto"],
},
True,
fan.SUPPORT_PRESET_MODE,
),
(
"test6",
{
"platform": "mqtt",
"name": "test3c3",
"name": "test6",
"command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["eco", "smart", "auto"],
},
True,
fan.SUPPORT_PRESET_MODE,
),
(
"test7",
{
"platform": "mqtt",
"name": "test4pcta",
"name": "test7",
"command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic",
},
True,
fan.SUPPORT_SET_SPEED,
),
(
"test8",
{
"platform": "mqtt",
"name": "test4pctb",
"name": "test8",
"command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic",
"percentage_command_topic": "percentage-command-topic",
},
True,
fan.SUPPORT_OSCILLATE | fan.SUPPORT_SET_SPEED,
),
(
"test9",
{
"platform": "mqtt",
"name": "test5pr_ma",
"name": "test9",
"command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["Mode1", "Mode2", "Mode3"],
},
True,
fan.SUPPORT_PRESET_MODE,
),
(
"test10",
{
"platform": "mqtt",
"name": "test5pr_mb",
"name": "test10",
"command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["whoosh", "silent", "auto"],
},
True,
fan.SUPPORT_PRESET_MODE,
),
(
"test11",
{
"platform": "mqtt",
"name": "test5pr_mc",
"name": "test11",
"command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic",
"preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["Mode1", "Mode2", "Mode3"],
},
True,
fan.SUPPORT_PRESET_MODE | fan.SUPPORT_OSCILLATE,
),
(
"test12",
{
"platform": "mqtt",
"name": "test6spd_range_a",
"name": "test12",
"command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic",
"speed_range_min": 1,
"speed_range_max": 40,
},
True,
fan.SUPPORT_SET_SPEED,
),
(
"test13",
{
"platform": "mqtt",
"name": "test6spd_range_b",
"name": "test13",
"command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic",
"speed_range_min": 50,
"speed_range_max": 40,
},
False,
None,
),
(
"test14",
{
"platform": "mqtt",
"name": "test6spd_range_c",
"name": "test14",
"command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic",
"speed_range_min": 0,
"speed_range_max": 40,
},
False,
None,
),
(
"test15",
{
"platform": "mqtt",
"name": "test7reset_payload_in_preset_modes_a",
"command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["auto", "smart", "normal", "None"],
},
False,
None,
),
(
"test16",
{
"platform": "mqtt",
"name": "test7reset_payload_in_preset_modes_b",
"name": "test16",
"command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["whoosh", "silent", "auto", "None"],
"payload_reset_preset_mode": "normal",
},
]
},
True,
fan.SUPPORT_PRESET_MODE,
),
],
)
async def test_supported_features(
hass, mqtt_mock_entry_with_yaml_config, name, config, success, features
):
"""Test optimistic mode without state topic."""
assert (
await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {fan.DOMAIN: config}}
)
is success
)
if success:
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
state = hass.states.get("fan.test1")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 0
state = hass.states.get("fan.test2")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_OSCILLATE
state = hass.states.get("fan.test3b")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_SET_SPEED
state = hass.states.get("fan.test3c1")
assert state is None
state = hass.states.get("fan.test3c2")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_PRESET_MODE
state = hass.states.get("fan.test3c3")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_PRESET_MODE
state = hass.states.get("fan.test4pcta")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_SET_SPEED
state = hass.states.get("fan.test4pctb")
assert (
state.attributes.get(ATTR_SUPPORTED_FEATURES)
== fan.SUPPORT_OSCILLATE | fan.SUPPORT_SET_SPEED
)
state = hass.states.get("fan.test5pr_ma")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_PRESET_MODE
state = hass.states.get("fan.test5pr_mb")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_PRESET_MODE
state = hass.states.get("fan.test5pr_mc")
assert (
state.attributes.get(ATTR_SUPPORTED_FEATURES)
== fan.SUPPORT_OSCILLATE | fan.SUPPORT_PRESET_MODE
)
state = hass.states.get("fan.test6spd_range_a")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_SET_SPEED
assert state.attributes.get("percentage_step") == 2.5
state = hass.states.get("fan.test6spd_range_b")
assert state is None
state = hass.states.get("fan.test6spd_range_c")
assert state is None
state = hass.states.get("fan.test7reset_payload_in_preset_modes_a")
assert state is None
state = hass.states.get("fan.test7reset_payload_in_preset_modes_b")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_PRESET_MODE
state = hass.states.get(f"fan.{name}")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == features
async def test_availability_when_connection_lost(
@ -1673,7 +1710,11 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, fan.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
fan.DOMAIN,
DEFAULT_CONFIG,
)
@ -1681,8 +1722,12 @@ async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, fan.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
fan.DOMAIN,
DEFAULT_CONFIG,
)
@ -1696,16 +1741,15 @@ async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplo
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique_id option only creates one fan per id."""
config = {
mqtt.DOMAIN: {
fan.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"command_topic": "test_topic",
@ -1713,6 +1757,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, fan.DOMAIN, config
)
@ -1869,9 +1914,9 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = fan.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
config = copy.deepcopy(DEFAULT_CONFIG)
if topic == "preset_mode_command_topic":
config["preset_modes"] = ["auto", "eco"]
config[mqtt.DOMAIN][domain]["preset_modes"] = ["auto", "eco"]
await help_test_publishing_with_custom_encoding(
hass,
@ -1890,33 +1935,45 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = fan.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = fan.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = fan.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = fan.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = fan.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -5,7 +5,7 @@ from unittest.mock import patch
import pytest
from voluptuous.error import MultipleInvalid
from homeassistant.components import humidifier
from homeassistant.components import humidifier, mqtt
from homeassistant.components.humidifier import (
ATTR_HUMIDITY,
ATTR_MODE,
@ -61,22 +61,28 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import async_fire_mqtt_message
DEFAULT_CONFIG = {
mqtt.DOMAIN: {
humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
}
}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[humidifier.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def humidifer_platform_only():
@ -126,16 +132,17 @@ async def async_set_humidity(
await hass.services.async_call(DOMAIN, SERVICE_SET_HUMIDITY, data, blocking=True)
async def test_fail_setup_if_no_command_topic(hass, mqtt_mock_entry_no_yaml_config):
async def test_fail_setup_if_no_command_topic(hass, caplog):
"""Test if command fails with command topic."""
assert await async_setup_component(
assert not await async_setup_component(
hass,
humidifier.DOMAIN,
{humidifier.DOMAIN: {"platform": "mqtt", "name": "test"}},
mqtt.DOMAIN,
{mqtt.DOMAIN: {humidifier.DOMAIN: {"name": "test"}}},
)
assert (
"Invalid config for [mqtt]: required key not provided @ data['mqtt']['humidifier'][0]['command_topic']. Got None"
in caplog.text
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert hass.states.get("humidifier.test") is None
async def test_controlling_state_via_topic(
@ -144,10 +151,10 @@ async def test_controlling_state_via_topic(
"""Test the controlling state via topic."""
assert await async_setup_component(
hass,
humidifier.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -168,6 +175,7 @@ async def test_controlling_state_via_topic(
"payload_reset_humidity": "rEset_humidity",
"payload_reset_mode": "rEset_mode",
}
}
},
)
await hass.async_block_till_done()
@ -248,10 +256,10 @@ async def test_controlling_state_via_topic_and_json_message(
"""Test the controlling state via topic and JSON message."""
assert await async_setup_component(
hass,
humidifier.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -268,6 +276,7 @@ async def test_controlling_state_via_topic_and_json_message(
"target_humidity_state_template": "{{ value_json.val }}",
"mode_state_template": "{{ value_json.val }}",
}
}
},
)
await hass.async_block_till_done()
@ -336,10 +345,10 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic(
"""Test the controlling state via topic and JSON message using a shared topic."""
assert await async_setup_component(
hass,
humidifier.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "shared-state-topic",
"command_topic": "command-topic",
@ -356,6 +365,7 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic(
"target_humidity_state_template": "{{ value_json.humidity }}",
"mode_state_template": "{{ value_json.mode }}",
}
}
},
)
await hass.async_block_till_done()
@ -414,10 +424,10 @@ async def test_sending_mqtt_commands_and_optimistic(
"""Test optimistic mode without state topic."""
assert await async_setup_component(
hass,
humidifier.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"payload_off": "StAtE_OfF",
@ -430,6 +440,7 @@ async def test_sending_mqtt_commands_and_optimistic(
"baby",
],
}
}
},
)
await hass.async_block_till_done()
@ -510,10 +521,10 @@ async def test_sending_mqtt_command_templates_(
"""Testing command templates with optimistic mode without state topic."""
assert await async_setup_component(
hass,
humidifier.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"command_template": "state: {{ value }}",
@ -527,6 +538,7 @@ async def test_sending_mqtt_command_templates_(
"sleep",
],
}
}
},
)
await hass.async_block_till_done()
@ -607,10 +619,10 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
"""Test optimistic mode with state topic and turn on attributes."""
assert await async_setup_component(
hass,
humidifier.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -625,6 +637,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
],
"optimistic": True,
}
}
},
)
await hass.async_block_till_done()
@ -737,7 +750,7 @@ async def test_encoding_subscribable_topics(
attribute_value,
):
"""Test handling of incoming encoded payload."""
config = copy.deepcopy(DEFAULT_CONFIG[humidifier.DOMAIN])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][humidifier.DOMAIN])
config["modes"] = ["eco", "auto"]
config[CONF_MODE_COMMAND_TOPIC] = "humidifier/some_mode_command_topic"
await help_test_encoding_subscribable_topics(
@ -757,10 +770,10 @@ async def test_attributes(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test attributes."""
assert await async_setup_component(
hass,
humidifier.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"mode_command_topic": "mode-command-topic",
@ -770,6 +783,7 @@ async def test_attributes(hass, mqtt_mock_entry_with_yaml_config, caplog):
"baby",
],
}
}
},
)
await hass.async_block_till_done()
@ -799,157 +813,182 @@ async def test_attributes(hass, mqtt_mock_entry_with_yaml_config, caplog):
assert state.attributes.get(humidifier.ATTR_MODE) is None
async def test_invalid_configurations(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test invalid configurations."""
assert await async_setup_component(
hass,
humidifier.DOMAIN,
@pytest.mark.parametrize(
"config,valid",
[
(
{
humidifier.DOMAIN: [
{
"platform": "mqtt",
"name": "test_valid_1",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
},
True,
),
(
{
"platform": "mqtt",
"name": "test_valid_2",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
"device_class": "humidifier",
},
True,
),
(
{
"platform": "mqtt",
"name": "test_valid_3",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
"device_class": "dehumidifier",
},
True,
),
(
{
"platform": "mqtt",
"name": "test_invalid_device_class",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
"device_class": "notsupporedSpeci@l",
},
False,
),
(
{
"platform": "mqtt",
"name": "test_mode_command_without_modes",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
"mode_command_topic": "mode-command-topic",
},
False,
),
(
{
"platform": "mqtt",
"name": "test_invalid_humidity_min_max_1",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
"min_humidity": 0,
"max_humidity": 101,
},
False,
),
(
{
"platform": "mqtt",
"name": "test_invalid_humidity_min_max_2",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
"max_humidity": 20,
"min_humidity": 40,
},
False,
),
(
{
"platform": "mqtt",
"name": "test_invalid_mode_is_reset",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
"mode_command_topic": "mode-command-topic",
"modes": ["eco", "None"],
},
]
},
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get("humidifier.test_valid_1") is not None
assert hass.states.get("humidifier.test_valid_2") is not None
assert hass.states.get("humidifier.test_valid_3") is not None
assert hass.states.get("humidifier.test_invalid_device_class") is None
assert hass.states.get("humidifier.test_mode_command_without_modes") is None
assert "not all values in the same group of inclusion" in caplog.text
caplog.clear()
assert hass.states.get("humidifier.test_invalid_humidity_min_max_1") is None
assert hass.states.get("humidifier.test_invalid_humidity_min_max_2") is None
assert hass.states.get("humidifier.test_invalid_mode_is_reset") is None
async def test_supported_features(hass, mqtt_mock_entry_with_yaml_config):
"""Test supported features."""
assert await async_setup_component(
False,
),
],
)
async def test_validity_configurations(hass, config, valid):
"""Test validity of configurations."""
assert (
await async_setup_component(
hass,
humidifier.DOMAIN,
mqtt.DOMAIN,
{mqtt.DOMAIN: {humidifier.DOMAIN: config}},
)
is valid
)
@pytest.mark.parametrize(
"name,config,success,features",
[
(
"test1",
{
humidifier.DOMAIN: [
{
"platform": "mqtt",
"name": "test1",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
},
True,
0,
),
(
"test2",
{
"platform": "mqtt",
"name": "test2",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
"mode_command_topic": "mode-command-topic",
"modes": ["eco", "auto"],
},
True,
humidifier.SUPPORT_MODES,
),
(
"test3",
{
"platform": "mqtt",
"name": "test3",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
},
True,
0,
),
(
"test4",
{
"platform": "mqtt",
"name": "test4",
"command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic",
"mode_command_topic": "mode-command-topic",
"modes": ["eco", "auto"],
},
True,
humidifier.SUPPORT_MODES,
),
(
"test5",
{
"platform": "mqtt",
"name": "test5",
"command_topic": "command-topic",
},
False,
None,
),
(
"test6",
{
"platform": "mqtt",
"name": "test6",
"target_humidity_command_topic": "humidity-command-topic",
},
]
},
False,
None,
),
],
)
async def test_supported_features(
hass, mqtt_mock_entry_with_yaml_config, name, config, success, features
):
"""Test supported features."""
assert (
await async_setup_component(
hass,
mqtt.DOMAIN,
{mqtt.DOMAIN: {humidifier.DOMAIN: config}},
)
is success
)
if success:
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
state = hass.states.get("humidifier.test1")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 0
state = hass.states.get("humidifier.test2")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == humidifier.SUPPORT_MODES
state = hass.states.get("humidifier.test3")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 0
state = hass.states.get("humidifier.test4")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == humidifier.SUPPORT_MODES
state = hass.states.get("humidifier.test5")
assert state is None
state = hass.states.get("humidifier.test6")
assert state is None
state = hass.states.get(f"humidifier.{name}")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == features
async def test_availability_when_connection_lost(
@ -1040,7 +1079,7 @@ async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
@ -1052,16 +1091,20 @@ async def test_update_with_json_attrs_bad_json(
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, humidifier.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
humidifier.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique_id option only creates one fan per id."""
config = {
mqtt.DOMAIN: {
humidifier.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"command_topic": "test_topic",
@ -1069,7 +1112,6 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"command_topic": "test_topic",
@ -1078,6 +1120,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, humidifier.DOMAIN, config
)
@ -1243,9 +1286,9 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = humidifier.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
config = copy.deepcopy(DEFAULT_CONFIG)
if topic == "mode_command_topic":
config["modes"] = ["auto", "eco"]
config[mqtt.DOMAIN][domain]["modes"] = ["auto", "eco"]
await help_test_publishing_with_custom_encoding(
hass,
@ -1264,45 +1307,56 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = humidifier.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = humidifier.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = humidifier.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_config_schema_validation(hass):
"""Test invalid platform options in the config schema do pass the config validation."""
"""Test invalid platform options in the config schema do not pass the config validation."""
platform = humidifier.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][platform])
config["name"] = "test"
del config["platform"]
CONFIG_SCHEMA({DOMAIN: {platform: config}})
CONFIG_SCHEMA({DOMAIN: {platform: [config]}})
CONFIG_SCHEMA({mqtt.DOMAIN: {platform: config}})
CONFIG_SCHEMA({mqtt.DOMAIN: {platform: [config]}})
with pytest.raises(MultipleInvalid):
CONFIG_SCHEMA({"mqtt": {"humidifier": [{"bla": "bla"}]}})
CONFIG_SCHEMA({mqtt.DOMAIN: {platform: [{"bla": "bla"}]}})
async def test_unload_config_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = humidifier.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = humidifier.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -29,11 +29,13 @@ from homeassistant.core import CoreState, HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, template
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import async_get_platforms
from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow
from .test_common import (
help_test_entry_reload_with_new_config,
help_test_reload_with_config,
help_test_setup_manual_entity_from_yaml,
)
@ -281,6 +283,7 @@ async def test_command_template_value(hass):
assert cmd_tpl.async_render(None, variables=variables) == "beer"
@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.SELECT])
async def test_command_template_variables(hass, mqtt_mock_entry_with_yaml_config):
"""Test the rendering of entity variables."""
topic = "test/select"
@ -290,15 +293,16 @@ async def test_command_template_variables(hass, mqtt_mock_entry_with_yaml_config
assert await async_setup_component(
hass,
"select",
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
"select": {
"platform": "mqtt",
"command_topic": topic,
"name": "Test Select",
"options": ["milk", "beer"],
"command_template": '{"option": "{{ value }}", "entity_id": "{{ entity_id }}", "name": "{{ name }}", "this_object_state": "{{ this.state }}"}',
}
}
},
)
await hass.async_block_till_done()
@ -1483,9 +1487,17 @@ async def test_setup_override_configuration(hass, caplog, tmp_path):
@patch("homeassistant.components.mqtt.PLATFORMS", [])
async def test_setup_manual_mqtt_with_platform_key(hass, caplog):
"""Test set up a manual MQTT item with a platform key."""
config = {"platform": "mqtt", "name": "test", "command_topic": "test-topic"}
config = {
mqtt.DOMAIN: {
"light": {
"platform": "mqtt",
"name": "test",
"command_topic": "test-topic",
}
}
}
with pytest.raises(AssertionError):
await help_test_setup_manual_entity_from_yaml(hass, "light", config)
await help_test_setup_manual_entity_from_yaml(hass, config)
assert (
"Invalid config for [mqtt]: [platform] is an invalid option for [mqtt]"
in caplog.text
@ -1495,9 +1507,9 @@ async def test_setup_manual_mqtt_with_platform_key(hass, caplog):
@patch("homeassistant.components.mqtt.PLATFORMS", [])
async def test_setup_manual_mqtt_with_invalid_config(hass, caplog):
"""Test set up a manual MQTT item with an invalid config."""
config = {"name": "test"}
config = {mqtt.DOMAIN: {"light": {"name": "test"}}}
with pytest.raises(AssertionError):
await help_test_setup_manual_entity_from_yaml(hass, "light", config)
await help_test_setup_manual_entity_from_yaml(hass, config)
assert (
"Invalid config for [mqtt]: required key not provided @ data['mqtt']['light'][0]['command_topic']."
" Got None. (See ?, line ?)" in caplog.text
@ -1507,8 +1519,8 @@ async def test_setup_manual_mqtt_with_invalid_config(hass, caplog):
@patch("homeassistant.components.mqtt.PLATFORMS", [])
async def test_setup_manual_mqtt_empty_platform(hass, caplog):
"""Test set up a manual MQTT platform without items."""
config = []
await help_test_setup_manual_entity_from_yaml(hass, "light", config)
config = {mqtt.DOMAIN: {"light": []}}
await help_test_setup_manual_entity_from_yaml(hass, config)
assert "voluptuous.error.MultipleInvalid" not in caplog.text
@ -1766,14 +1778,14 @@ async def test_delayed_birth_message(
await hass.async_block_till_done()
mqtt_component_mock = MagicMock(
return_value=hass.data["mqtt"],
spec_set=hass.data["mqtt"],
wraps=hass.data["mqtt"],
return_value=hass.data["mqtt"].client,
spec_set=hass.data["mqtt"].client,
wraps=hass.data["mqtt"].client,
)
mqtt_component_mock._mqttc = mqtt_client_mock
hass.data["mqtt"] = mqtt_component_mock
mqtt_mock = hass.data["mqtt"]
hass.data["mqtt"].client = mqtt_component_mock
mqtt_mock = hass.data["mqtt"].client
mqtt_mock.reset_mock()
async def wait_birth(topic, payload, qos):
@ -2087,20 +2099,19 @@ async def test_mqtt_ws_get_device_debug_info(
await mqtt_mock_entry_no_yaml_config()
config_sensor = {
"device": {"identifiers": ["0AFFD2"]},
"platform": "mqtt",
"state_topic": "foobar/sensor",
"unique_id": "unique",
}
config_trigger = {
"automation_type": "trigger",
"device": {"identifiers": ["0AFFD2"]},
"platform": "mqtt",
"topic": "test-topic1",
"type": "foo",
"subtype": "bar",
}
data_sensor = json.dumps(config_sensor)
data_trigger = json.dumps(config_trigger)
config_sensor["platform"] = config_trigger["platform"] = mqtt.DOMAIN
async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data_sensor)
async_fire_mqtt_message(
@ -2151,11 +2162,11 @@ async def test_mqtt_ws_get_device_debug_info_binary(
await mqtt_mock_entry_no_yaml_config()
config = {
"device": {"identifiers": ["0AFFD2"]},
"platform": "mqtt",
"topic": "foobar/image",
"unique_id": "unique",
}
data = json.dumps(config)
config["platform"] = mqtt.DOMAIN
async_fire_mqtt_message(hass, "homeassistant/camera/bla/config", data)
await hass.async_block_till_done()
@ -2397,7 +2408,9 @@ async def test_debug_info_non_mqtt(
device_id=device_entry.id,
)
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {"platform": "test"}})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {DOMAIN: {"platform": "test"}}}
)
debug_info_data = debug_info.info_for_device(hass, device_entry.id)
assert len(debug_info_data["entities"]) == 0
@ -2409,7 +2422,6 @@ async def test_debug_info_wildcard(hass, mqtt_mock_entry_no_yaml_config):
await mqtt_mock_entry_no_yaml_config()
config = {
"device": {"identifiers": ["helloworld"]},
"platform": "mqtt",
"name": "test",
"state_topic": "sensor/#",
"unique_id": "veryunique",
@ -2456,7 +2468,6 @@ async def test_debug_info_filter_same(hass, mqtt_mock_entry_no_yaml_config):
await mqtt_mock_entry_no_yaml_config()
config = {
"device": {"identifiers": ["helloworld"]},
"platform": "mqtt",
"name": "test",
"state_topic": "sensor/#",
"unique_id": "veryunique",
@ -2515,7 +2526,6 @@ async def test_debug_info_same_topic(hass, mqtt_mock_entry_no_yaml_config):
await mqtt_mock_entry_no_yaml_config()
config = {
"device": {"identifiers": ["helloworld"]},
"platform": "mqtt",
"name": "test",
"state_topic": "sensor/status",
"availability_topic": "sensor/status",
@ -2568,7 +2578,6 @@ async def test_debug_info_qos_retain(hass, mqtt_mock_entry_no_yaml_config):
await mqtt_mock_entry_no_yaml_config()
config = {
"device": {"identifiers": ["helloworld"]},
"platform": "mqtt",
"name": "test",
"state_topic": "sensor/#",
"unique_id": "veryunique",
@ -2708,6 +2717,8 @@ async def test_subscribe_connection_status(
assert mqtt_connected_calls[1] is False
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_one_deprecation_warning_per_platform(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
@ -2796,11 +2807,17 @@ async def test_publish_or_subscribe_without_valid_config_entry(hass, caplog):
@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT])
async def test_reload_entry_with_new_config(hass, tmp_path):
"""Test reloading the config entry with a new yaml config."""
config_old = [{"name": "test_old1", "command_topic": "test-topic_old"}]
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
config_old = {
"mqtt": {"light": [{"name": "test_old1", "command_topic": "test-topic_old"}]}
}
config_yaml_new = {
"mqtt": {
"light": [{"name": "test_new_modern", "command_topic": "test-topic_new"}]
},
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
"light": [
{
"platform": "mqtt",
@ -2809,7 +2826,7 @@ async def test_reload_entry_with_new_config(hass, tmp_path):
}
],
}
await help_test_setup_manual_entity_from_yaml(hass, "light", config_old)
await help_test_setup_manual_entity_from_yaml(hass, config_old)
assert hass.states.get("light.test_old1") is not None
await help_test_entry_reload_with_new_config(hass, tmp_path, config_yaml_new)
@ -2821,11 +2838,15 @@ async def test_reload_entry_with_new_config(hass, tmp_path):
@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT])
async def test_disabling_and_enabling_entry(hass, tmp_path, caplog):
"""Test disabling and enabling the config entry."""
config_old = [{"name": "test_old1", "command_topic": "test-topic_old"}]
config_old = {
"mqtt": {"light": [{"name": "test_old1", "command_topic": "test-topic_old"}]}
}
config_yaml_new = {
"mqtt": {
"light": [{"name": "test_new_modern", "command_topic": "test-topic_new"}]
},
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
"light": [
{
"platform": "mqtt",
@ -2834,7 +2855,7 @@ async def test_disabling_and_enabling_entry(hass, tmp_path, caplog):
}
],
}
await help_test_setup_manual_entity_from_yaml(hass, "light", config_old)
await help_test_setup_manual_entity_from_yaml(hass, config_old)
assert hass.states.get("light.test_old1") is not None
mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
@ -2924,7 +2945,9 @@ async def test_setup_manual_items_with_unique_ids(
hass, tmp_path, caplog, config, unique
):
"""Test setup manual items is generating unique id's."""
await help_test_setup_manual_entity_from_yaml(hass, "light", config)
await help_test_setup_manual_entity_from_yaml(
hass, {mqtt.DOMAIN: {"light": config}}
)
assert hass.states.get("light.test1") is not None
assert (hass.states.get("light.test2") is not None) == unique
@ -2965,3 +2988,68 @@ async def test_remove_unknown_conf_entry_options(hass, mqtt_client_mock, caplog)
"MQTT config entry: {'protocol'}. Add them to configuration.yaml if they "
"are needed"
) in caplog.text
@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT])
async def test_link_config_entry(hass, tmp_path, caplog):
"""Test manual and dynamically setup entities are linked to the config entry."""
config_manual = {
"mqtt": {
"light": [
{
"name": "test_manual",
"unique_id": "test_manual_unique_id123",
"command_topic": "test-topic_manual",
}
]
}
}
config_discovery = {
"name": "test_discovery",
"unique_id": "test_discovery_unique456",
"command_topic": "test-topic_discovery",
}
# set up manual item
await help_test_setup_manual_entity_from_yaml(hass, config_manual)
# set up item through discovery
async_fire_mqtt_message(
hass, "homeassistant/light/bla/config", json.dumps(config_discovery)
)
await hass.async_block_till_done()
assert hass.states.get("light.test_manual") is not None
assert hass.states.get("light.test_discovery") is not None
entity_names = ["test_manual", "test_discovery"]
# Check if both entities were linked to the MQTT config entry
mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0]
mqtt_platforms = async_get_platforms(hass, mqtt.DOMAIN)
def _check_entities():
entities = []
for mqtt_platform in mqtt_platforms:
assert mqtt_platform.config_entry is mqtt_config_entry
entities += (entity for entity in mqtt_platform.entities.values())
for entity in entities:
assert entity.name in entity_names
return len(entities)
assert _check_entities() == 2
# reload entry and assert again
await help_test_entry_reload_with_new_config(hass, tmp_path, config_manual)
# manual set up item should remain
assert _check_entities() == 1
# set up item through discovery
async_fire_mqtt_message(
hass, "homeassistant/light/bla/config", json.dumps(config_discovery)
)
await hass.async_block_till_done()
assert _check_entities() == 2
# reload manual configured items and assert again
await help_test_reload_with_config(hass, caplog, tmp_path, config_manual)
assert _check_entities() == 2

View File

@ -5,7 +5,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import vacuum
from homeassistant.components import mqtt, vacuum
from homeassistant.components.mqtt.const import CONF_COMMAND_TOPIC
from homeassistant.components.mqtt.vacuum import schema_legacy as mqttvacuum
from homeassistant.components.mqtt.vacuum.schema import services_to_strings
@ -58,7 +58,7 @@ from .test_common import (
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_bad_json,
help_test_update_with_json_attrs_not_dict,
)
@ -66,7 +66,8 @@ from tests.common import async_fire_mqtt_message
from tests.components.vacuum import common
DEFAULT_CONFIG = {
CONF_PLATFORM: "mqtt",
mqtt.DOMAIN: {
vacuum.DOMAIN: {
CONF_NAME: "mqtttest",
CONF_COMMAND_TOPIC: "vacuum/command",
mqttvacuum.CONF_SEND_COMMAND_TOPIC: "vacuum/send_command",
@ -84,9 +85,16 @@ DEFAULT_CONFIG = {
mqttvacuum.CONF_FAN_SPEED_TEMPLATE: "{{ value_json.fan_speed }}",
mqttvacuum.CONF_SET_FAN_SPEED_TOPIC: "vacuum/set_fan_speed",
mqttvacuum.CONF_FAN_SPEED_LIST: ["min", "medium", "high", "max"],
}
}
}
DEFAULT_CONFIG_2 = {vacuum.DOMAIN: {"platform": "mqtt", "name": "test"}}
DEFAULT_CONFIG_2 = {mqtt.DOMAIN: {vacuum.DOMAIN: {"name": "test"}}}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[vacuum.DOMAIN][CONF_PLATFORM] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
@ -98,9 +106,7 @@ def vacuum_platform_only():
async def test_default_supported_features(hass, mqtt_mock_entry_with_yaml_config):
"""Test that the correct supported features."""
assert await async_setup_component(
hass, vacuum.DOMAIN, {vacuum.DOMAIN: DEFAULT_CONFIG}
)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
entity = hass.states.get("vacuum.mqtttest")
@ -120,12 +126,14 @@ async def test_default_supported_features(hass, mqtt_mock_entry_with_yaml_config
async def test_all_commands(hass, mqtt_mock_entry_with_yaml_config):
"""Test simple commands to the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -202,13 +210,15 @@ async def test_commands_without_supported_features(
hass, mqtt_mock_entry_with_yaml_config
):
"""Test commands which are not supported by the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
services = mqttvacuum.STRING_TO_SERVICE["status"]
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
services, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -253,13 +263,15 @@ async def test_attributes_without_supported_features(
hass, mqtt_mock_entry_with_yaml_config
):
"""Test attributes which are not supported by the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
services = mqttvacuum.STRING_TO_SERVICE["turn_on"]
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
services, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -281,12 +293,14 @@ async def test_attributes_without_supported_features(
async def test_status(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -322,12 +336,14 @@ async def test_status(hass, mqtt_mock_entry_with_yaml_config):
async def test_status_battery(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -341,12 +357,14 @@ async def test_status_battery(hass, mqtt_mock_entry_with_yaml_config):
async def test_status_cleaning(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -360,12 +378,14 @@ async def test_status_cleaning(hass, mqtt_mock_entry_with_yaml_config):
async def test_status_docked(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -379,12 +399,14 @@ async def test_status_docked(hass, mqtt_mock_entry_with_yaml_config):
async def test_status_charging(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -398,12 +420,14 @@ async def test_status_charging(hass, mqtt_mock_entry_with_yaml_config):
async def test_status_fan_speed(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -417,12 +441,14 @@ async def test_status_fan_speed(hass, mqtt_mock_entry_with_yaml_config):
async def test_status_fan_speed_list(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -435,13 +461,15 @@ async def test_status_no_fan_speed_list(hass, mqtt_mock_entry_with_yaml_config):
If the vacuum doesn't support fan speed, fan speed list should be None.
"""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
services = ALL_SERVICES - VacuumEntityFeature.FAN_SPEED
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
services, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -451,12 +479,14 @@ async def test_status_no_fan_speed_list(hass, mqtt_mock_entry_with_yaml_config):
async def test_status_error(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -477,7 +507,7 @@ async def test_status_error(hass, mqtt_mock_entry_with_yaml_config):
async def test_battery_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test that you can use non-default templates for battery_level."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config.update(
{
mqttvacuum.CONF_SUPPORTED_FEATURES: services_to_strings(
@ -488,7 +518,9 @@ async def test_battery_template(hass, mqtt_mock_entry_with_yaml_config):
}
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -500,12 +532,14 @@ async def test_battery_template(hass, mqtt_mock_entry_with_yaml_config):
async def test_status_invalid_json(hass, mqtt_mock_entry_with_yaml_config):
"""Test to make sure nothing breaks if the vacuum sends bad JSON."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -515,82 +549,64 @@ async def test_status_invalid_json(hass, mqtt_mock_entry_with_yaml_config):
assert state.attributes.get(ATTR_STATUS) == "Stopped"
async def test_missing_battery_template(hass, mqtt_mock_entry_no_yaml_config):
async def test_missing_battery_template(hass):
"""Test to make sure missing template is not allowed."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config.pop(mqttvacuum.CONF_BATTERY_LEVEL_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
state = hass.states.get("vacuum.mqtttest")
assert state is None
assert not await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
async def test_missing_charging_template(hass, mqtt_mock_entry_no_yaml_config):
async def test_missing_charging_template(hass):
"""Test to make sure missing template is not allowed."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config.pop(mqttvacuum.CONF_CHARGING_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
state = hass.states.get("vacuum.mqtttest")
assert state is None
assert not await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
async def test_missing_cleaning_template(hass, mqtt_mock_entry_no_yaml_config):
async def test_missing_cleaning_template(hass):
"""Test to make sure missing template is not allowed."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config.pop(mqttvacuum.CONF_CLEANING_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
state = hass.states.get("vacuum.mqtttest")
assert state is None
assert not await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
async def test_missing_docked_template(hass, mqtt_mock_entry_no_yaml_config):
async def test_missing_docked_template(hass):
"""Test to make sure missing template is not allowed."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config.pop(mqttvacuum.CONF_DOCKED_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
state = hass.states.get("vacuum.mqtttest")
assert state is None
assert not await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
async def test_missing_error_template(hass, mqtt_mock_entry_no_yaml_config):
async def test_missing_error_template(hass):
"""Test to make sure missing template is not allowed."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config.pop(mqttvacuum.CONF_ERROR_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
state = hass.states.get("vacuum.mqtttest")
assert state is None
assert not await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
async def test_missing_fan_speed_template(hass, mqtt_mock_entry_no_yaml_config):
async def test_missing_fan_speed_template(hass):
"""Test to make sure missing template is not allowed."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config.pop(mqttvacuum.CONF_FAN_SPEED_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
state = hass.states.get("vacuum.mqtttest")
assert state is None
assert not await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
async def test_availability_when_connection_lost(
@ -657,44 +673,56 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
vacuum.DOMAIN,
DEFAULT_CONFIG_2,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
vacuum.DOMAIN,
DEFAULT_CONFIG_2,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
vacuum.DOMAIN,
DEFAULT_CONFIG_2,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one vacuum per unique_id."""
config = {
mqtt.DOMAIN: {
vacuum.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE",
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, config
)
@ -702,7 +730,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
async def test_discovery_removal_vacuum(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered vacuum."""
data = json.dumps(DEFAULT_CONFIG_2[vacuum.DOMAIN])
data = json.dumps(DEFAULT_CONFIG_2[mqtt.DOMAIN][vacuum.DOMAIN])
await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, data
)
@ -776,8 +804,8 @@ async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config):
async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config):
"""Test MQTT subscriptions are managed when entity_id is updated."""
config = {
mqtt.DOMAIN: {
vacuum.DOMAIN: {
"platform": "mqtt",
"name": "test",
"battery_level_topic": "test-topic",
"battery_level_template": "{{ value_json.battery_level }}",
@ -785,6 +813,7 @@ async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_co
"availability_topic": "avty-topic",
}
}
}
await help_test_entity_id_update_subscriptions(
hass,
mqtt_mock_entry_with_yaml_config,
@ -804,8 +833,8 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_c
async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT debug info."""
config = {
mqtt.DOMAIN: {
vacuum.DOMAIN: {
"platform": "mqtt",
"name": "test",
"battery_level_topic": "state-topic",
"battery_level_template": "{{ value_json.battery_level }}",
@ -813,6 +842,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
"payload_turn_on": "ON",
}
}
}
await help_test_entity_debug_info_message(
hass,
mqtt_mock_entry_no_yaml_config,
@ -875,7 +905,7 @@ async def test_publishing_with_custom_encoding(
"""Test publishing MQTT payload with different encoding."""
domain = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG)
config["supported_features"] = [
config[mqtt.DOMAIN][domain]["supported_features"] = [
"turn_on",
"turn_off",
"clean_spot",
@ -906,10 +936,12 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = vacuum.DOMAIN
config = DEFAULT_CONFIG
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -944,7 +976,8 @@ async def test_encoding_subscribable_topics(
attribute_value,
):
"""Test handling of incoming encoded payload."""
config = deepcopy(DEFAULT_CONFIG)
domain = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config[CONF_SUPPORTED_FEATURES] = [
"turn_on",
"turn_off",
@ -976,8 +1009,18 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG)
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.mqtttest")
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -2,9 +2,9 @@
Configuration for RGB Version with brightness:
light:
platform: mqtt
name: "Office Light RGB"
mqtt:
light:
- name: "Office Light RGB"
state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status"
@ -17,8 +17,9 @@ light:
Configuration for XY Version with brightness:
light:
platform: mqtt
mqtt:
light:
- platform: mqtt
name: "Office Light XY"
state_topic: "office/xy1/light/status"
command_topic: "office/xy1/light/switch"
@ -32,9 +33,9 @@ light:
config without RGB:
light:
platform: mqtt
name: "Office Light"
mqtt:
light:
- name: "Office Light"
state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status"
@ -45,9 +46,9 @@ light:
config without RGB and brightness:
light:
platform: mqtt
name: "Office Light"
mqtt:
light:
- name: "Office Light"
state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch"
qos: 0
@ -56,9 +57,9 @@ light:
config for RGB Version with brightness and scale:
light:
platform: mqtt
name: "Office Light RGB"
mqtt:
light:
- name: "Office Light RGB"
state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status"
@ -73,9 +74,9 @@ light:
config with brightness and color temp
light:
platform: mqtt
name: "Office Light Color Temp"
mqtt:
light:
- name: "Office Light Color Temp"
state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status"
@ -89,9 +90,9 @@ light:
config with brightness and effect
light:
platform: mqtt
name: "Office Light Color Temp"
mqtt:
light:
- name: "Office Light Color Temp"
state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status"
@ -108,9 +109,9 @@ light:
config for RGB Version with RGB command template:
light:
platform: mqtt
name: "Office Light RGB"
mqtt:
light:
- name: "Office Light RGB"
state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch"
rgb_state_topic: "office/rgb1/rgb/status"
@ -122,9 +123,9 @@ light:
Configuration for HS Version with brightness:
light:
platform: mqtt
name: "Office Light HS"
mqtt:
light:
- name: "Office Light HS"
state_topic: "office/hs1/light/status"
command_topic: "office/hs1/light/switch"
brightness_state_topic: "office/hs1/brightness/status"
@ -137,9 +138,9 @@ light:
Configuration with brightness command template:
light:
platform: mqtt
name: "Office Light"
mqtt:
light:
- name: "Office Light"
state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status"
@ -151,9 +152,9 @@ light:
Configuration with effect command template:
light:
platform: mqtt
name: "Office Light Color Temp"
mqtt:
light:
- name: "Office Light Color Temp"
state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch"
effect_state_topic: "office/rgb1/effect/status"
@ -172,7 +173,7 @@ from unittest.mock import call, patch
import pytest
from homeassistant.components import light
from homeassistant.components import light, mqtt
from homeassistant.components.mqtt.light.schema_basic import (
CONF_BRIGHTNESS_COMMAND_TOPIC,
CONF_COLOR_TEMP_COMMAND_TOPIC,
@ -222,21 +223,22 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import (
assert_setup_component,
async_fire_mqtt_message,
mock_restore_cache,
)
from tests.common import async_fire_mqtt_message, mock_restore_cache
from tests.components.light import common
DEFAULT_CONFIG = {
light.DOMAIN: {"platform": "mqtt", "name": "test", "command_topic": "test-topic"}
mqtt.DOMAIN: {light.DOMAIN: {"name": "test", "command_topic": "test-topic"}}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[light.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def light_platform_only():
@ -245,14 +247,15 @@ def light_platform_only():
yield
async def test_fail_setup_if_no_command_topic(hass, mqtt_mock_entry_no_yaml_config):
async def test_fail_setup_if_no_command_topic(hass, caplog):
"""Test if command fails with command topic."""
assert await async_setup_component(
hass, light.DOMAIN, {light.DOMAIN: {"platform": "mqtt", "name": "test"}}
assert not await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {light.DOMAIN: {"name": "test"}}}
)
assert (
"Invalid config for [mqtt]: required key not provided @ data['mqtt']['light'][0]['command_topic']. Got None."
in caplog.text
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert hass.states.get("light.test") is None
async def test_no_color_brightness_color_temp_hs_white_xy_if_no_topics(
@ -261,14 +264,15 @@ async def test_no_color_brightness_color_temp_hs_white_xy_if_no_topics(
"""Test if there is no color and brightness if no topic."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set",
}
}
},
)
await hass.async_block_till_done()
@ -317,7 +321,6 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi
"""Test the controlling of the state via topic."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set",
@ -344,7 +347,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi
}
color_modes = ["color_temp", "hs", "rgb", "rgbw", "rgbww", "xy"]
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -437,7 +440,6 @@ async def test_invalid_state_via_topic(hass, mqtt_mock_entry_with_yaml_config, c
"""Test handling of empty data via topic."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set",
@ -464,7 +466,7 @@ async def test_invalid_state_via_topic(hass, mqtt_mock_entry_with_yaml_config, c
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -570,13 +572,12 @@ async def test_invalid_state_via_topic(hass, mqtt_mock_entry_with_yaml_config, c
async def test_brightness_controlling_scale(hass, mqtt_mock_entry_with_yaml_config):
"""Test the brightness controlling scale."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test_scale/status",
"command_topic": "test_scale/set",
@ -587,6 +588,7 @@ async def test_brightness_controlling_scale(hass, mqtt_mock_entry_with_yaml_conf
"payload_on": "on",
"payload_off": "off",
}
}
},
)
await hass.async_block_till_done()
@ -620,13 +622,12 @@ async def test_brightness_from_rgb_controlling_scale(
hass, mqtt_mock_entry_with_yaml_config
):
"""Test the brightness controlling scale."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test_scale_rgb/status",
"command_topic": "test_scale_rgb/set",
@ -636,6 +637,7 @@ async def test_brightness_from_rgb_controlling_scale(
"payload_on": "on",
"payload_off": "off",
}
}
},
)
await hass.async_block_till_done()
@ -664,7 +666,6 @@ async def test_controlling_state_via_topic_with_templates(
"""Test the setting of the state with a template."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set",
@ -697,7 +698,7 @@ async def test_controlling_state_via_topic_with_templates(
}
color_modes = ["color_temp", "hs", "rgb", "rgbw", "rgbww", "xy"]
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -767,7 +768,6 @@ async def test_sending_mqtt_commands_and_optimistic(
"""Test the sending of command in optimistic mode."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light_rgb/set",
"brightness_command_topic": "test_light_rgb/brightness/set",
@ -798,8 +798,7 @@ async def test_sending_mqtt_commands_and_optimistic(
)
mock_restore_cache(hass, (fake_state,))
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -958,7 +957,6 @@ async def test_sending_mqtt_rgb_command_with_template(
"""Test the sending of RGB command with template."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light_rgb/set",
"rgb_command_topic": "test_light_rgb/rgb/set",
@ -970,7 +968,7 @@ async def test_sending_mqtt_rgb_command_with_template(
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -998,7 +996,6 @@ async def test_sending_mqtt_rgbw_command_with_template(
"""Test the sending of RGBW command with template."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light_rgb/set",
"rgbw_command_topic": "test_light_rgb/rgbw/set",
@ -1010,7 +1007,7 @@ async def test_sending_mqtt_rgbw_command_with_template(
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1038,7 +1035,6 @@ async def test_sending_mqtt_rgbww_command_with_template(
"""Test the sending of RGBWW command with template."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light_rgb/set",
"rgbww_command_topic": "test_light_rgb/rgbww/set",
@ -1050,7 +1046,7 @@ async def test_sending_mqtt_rgbww_command_with_template(
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1078,7 +1074,6 @@ async def test_sending_mqtt_color_temp_command_with_template(
"""Test the sending of Color Temp command with template."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light_color_temp/set",
"color_temp_command_topic": "test_light_color_temp/color_temp/set",
@ -1089,7 +1084,7 @@ async def test_sending_mqtt_color_temp_command_with_template(
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1115,7 +1110,6 @@ async def test_on_command_first(hass, mqtt_mock_entry_with_yaml_config):
"""Test on command being sent before brightness."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"brightness_command_topic": "test_light/bright",
@ -1123,7 +1117,7 @@ async def test_on_command_first(hass, mqtt_mock_entry_with_yaml_config):
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1152,14 +1146,13 @@ async def test_on_command_last(hass, mqtt_mock_entry_with_yaml_config):
"""Test on command being sent after brightness."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"brightness_command_topic": "test_light/bright",
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1188,7 +1181,6 @@ async def test_on_command_brightness(hass, mqtt_mock_entry_with_yaml_config):
"""Test on command being sent as only brightness."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"brightness_command_topic": "test_light/bright",
@ -1197,7 +1189,7 @@ async def test_on_command_brightness(hass, mqtt_mock_entry_with_yaml_config):
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1244,7 +1236,6 @@ async def test_on_command_brightness_scaled(hass, mqtt_mock_entry_with_yaml_conf
"""Test brightness scale."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"brightness_command_topic": "test_light/bright",
@ -1254,7 +1245,7 @@ async def test_on_command_brightness_scaled(hass, mqtt_mock_entry_with_yaml_conf
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1315,14 +1306,13 @@ async def test_on_command_rgb(hass, mqtt_mock_entry_with_yaml_config):
"""Test on command in RGB brightness mode."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"rgb_command_topic": "test_light/rgb",
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1406,14 +1396,13 @@ async def test_on_command_rgbw(hass, mqtt_mock_entry_with_yaml_config):
"""Test on command in RGBW brightness mode."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"rgbw_command_topic": "test_light/rgbw",
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1497,14 +1486,13 @@ async def test_on_command_rgbww(hass, mqtt_mock_entry_with_yaml_config):
"""Test on command in RGBWW brightness mode."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"rgbww_command_topic": "test_light/rgbww",
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1588,7 +1576,6 @@ async def test_on_command_rgb_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test on command in RGB brightness mode with RGB template."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"rgb_command_topic": "test_light/rgb",
@ -1596,7 +1583,7 @@ async def test_on_command_rgb_template(hass, mqtt_mock_entry_with_yaml_config):
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1626,7 +1613,6 @@ async def test_on_command_rgbw_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test on command in RGBW brightness mode with RGBW template."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"rgbw_command_topic": "test_light/rgbw",
@ -1634,7 +1620,7 @@ async def test_on_command_rgbw_template(hass, mqtt_mock_entry_with_yaml_config):
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1663,7 +1649,6 @@ async def test_on_command_rgbww_template(hass, mqtt_mock_entry_with_yaml_config)
"""Test on command in RGBWW brightness mode with RGBWW template."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"rgbww_command_topic": "test_light/rgbww",
@ -1671,7 +1656,7 @@ async def test_on_command_rgbww_template(hass, mqtt_mock_entry_with_yaml_config)
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1701,7 +1686,6 @@ async def test_on_command_white(hass, mqtt_mock_entry_with_yaml_config):
"""Test sending commands for RGB + white light."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "tasmota_B94927/cmnd/POWER",
"state_value_template": "{{ value_json.POWER }}",
@ -1721,7 +1705,7 @@ async def test_on_command_white(hass, mqtt_mock_entry_with_yaml_config):
}
color_modes = ["rgb", "white"]
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -1779,7 +1763,6 @@ async def test_explicit_color_mode(hass, mqtt_mock_entry_with_yaml_config):
"""Test explicit color mode over mqtt."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set",
@ -1807,7 +1790,7 @@ async def test_explicit_color_mode(hass, mqtt_mock_entry_with_yaml_config):
}
color_modes = ["color_temp", "hs", "rgb", "rgbw", "rgbww", "xy"]
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -1928,7 +1911,6 @@ async def test_explicit_color_mode_templated(hass, mqtt_mock_entry_with_yaml_con
"""Test templated explicit color mode over mqtt."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set",
@ -1947,7 +1929,7 @@ async def test_explicit_color_mode_templated(hass, mqtt_mock_entry_with_yaml_con
}
color_modes = ["color_temp", "hs"]
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -2010,7 +1992,6 @@ async def test_white_state_update(hass, mqtt_mock_entry_with_yaml_config):
"""Test state updates for RGB + white light."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "tasmota_B94927/tele/STATE",
"command_topic": "tasmota_B94927/cmnd/POWER",
@ -2034,7 +2015,7 @@ async def test_white_state_update(hass, mqtt_mock_entry_with_yaml_config):
}
color_modes = ["rgb", "white"]
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -2075,7 +2056,6 @@ async def test_effect(hass, mqtt_mock_entry_with_yaml_config):
"""Test effect."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light/set",
"effect_command_topic": "test_light/effect/set",
@ -2083,7 +2063,7 @@ async def test_effect(hass, mqtt_mock_entry_with_yaml_config):
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -2173,39 +2153,50 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
light.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
light.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
light.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one light per unique_id."""
config = {
mqtt.DOMAIN: {
light.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"command_topic": "test_topic",
@ -2213,6 +2204,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, config
)
@ -2230,6 +2222,8 @@ async def test_discovery_removal_light(hass, mqtt_mock_entry_no_yaml_config, cap
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_discovery_deprecated(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test discovery of mqtt light with deprecated platform option."""
await mqtt_mock_entry_no_yaml_config()
@ -2804,7 +2798,6 @@ async def test_max_mireds(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting min_mireds and max_mireds."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_max_mireds/set",
"color_temp_command_topic": "test_max_mireds/color_temp/set",
@ -2812,7 +2805,7 @@ async def test_max_mireds(hass, mqtt_mock_entry_with_yaml_config):
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -2921,11 +2914,11 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
config = copy.deepcopy(DEFAULT_CONFIG)
if topic == "effect_command_topic":
config["effect_list"] = ["random", "color_loop"]
config[mqtt.DOMAIN][domain]["effect_list"] = ["random", "color_loop"]
elif topic == "white_command_topic":
config["rgb_command_topic"] = "some-cmd-topic"
config[mqtt.DOMAIN][domain]["rgb_command_topic"] = "some-cmd-topic"
await help_test_publishing_with_custom_encoding(
hass,
@ -2946,16 +2939,18 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = light.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = light.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -3000,7 +2995,7 @@ async def test_encoding_subscribable_topics(
init_payload,
):
"""Test handling of incoming encoded payload."""
config = copy.deepcopy(DEFAULT_CONFIG[light.DOMAIN])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][light.DOMAIN])
config[CONF_EFFECT_COMMAND_TOPIC] = "light/CONF_EFFECT_COMMAND_TOPIC"
config[CONF_RGB_COMMAND_TOPIC] = "light/CONF_RGB_COMMAND_TOPIC"
config[CONF_BRIGHTNESS_COMMAND_TOPIC] = "light/CONF_BRIGHTNESS_COMMAND_TOPIC"
@ -3043,7 +3038,7 @@ async def test_encoding_subscribable_topics_brightness(
init_payload,
):
"""Test handling of incoming encoded payload for a brightness only light."""
config = copy.deepcopy(DEFAULT_CONFIG[light.DOMAIN])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][light.DOMAIN])
config[CONF_BRIGHTNESS_COMMAND_TOPIC] = "light/CONF_BRIGHTNESS_COMMAND_TOPIC"
await help_test_encoding_subscribable_topics(
@ -3066,7 +3061,6 @@ async def test_sending_mqtt_brightness_command_with_template(
"""Test the sending of Brightness command with template."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light_brightness/set",
"brightness_command_topic": "test_light_brightness/brightness/set",
@ -3077,7 +3071,7 @@ async def test_sending_mqtt_brightness_command_with_template(
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -3105,7 +3099,6 @@ async def test_sending_mqtt_effect_command_with_template(
"""Test the sending of Effect command with template."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test_light_brightness/set",
"brightness_command_topic": "test_light_brightness/brightness/set",
@ -3118,7 +3111,7 @@ async def test_sending_mqtt_effect_command_with_template(
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -3147,17 +3140,27 @@ async def test_sending_mqtt_effect_command_with_template(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = light.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -2,8 +2,9 @@
Configuration with RGB, brightness, color temp, effect, and XY:
light:
platform: mqtt_json
mqtt:
light:
schema: json
name: mqtt_json_light_1
state_topic: "home/rgb1"
command_topic: "home/rgb1/set"
@ -15,8 +16,9 @@ light:
Configuration with RGB, brightness, color temp and effect:
light:
platform: mqtt_json
mqtt:
light:
schema: json
name: mqtt_json_light_1
state_topic: "home/rgb1"
command_topic: "home/rgb1/set"
@ -27,8 +29,9 @@ light:
Configuration with RGB, brightness and color temp:
light:
platform: mqtt_json
mqtt:
light:
schema: json
name: mqtt_json_light_1
state_topic: "home/rgb1"
command_topic: "home/rgb1/set"
@ -38,8 +41,9 @@ light:
Configuration with RGB, brightness:
light:
platform: mqtt_json
mqtt:
light:
schema: json
name: mqtt_json_light_1
state_topic: "home/rgb1"
command_topic: "home/rgb1/set"
@ -48,8 +52,9 @@ light:
Config without RGB:
light:
platform: mqtt_json
mqtt:
light:
schema: json
name: mqtt_json_light_1
state_topic: "home/rgb1"
command_topic: "home/rgb1/set"
@ -79,7 +84,7 @@ from unittest.mock import call, patch
import pytest
from homeassistant.components import light
from homeassistant.components import light, mqtt
from homeassistant.components.mqtt.light.schema_basic import (
MQTT_LIGHT_ATTRIBUTES_BLOCKED,
)
@ -120,7 +125,7 @@ from .test_common import (
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_bad_json,
help_test_update_with_json_attrs_not_dict,
)
@ -128,14 +133,20 @@ from tests.common import async_fire_mqtt_message, mock_restore_cache
from tests.components.light import common
DEFAULT_CONFIG = {
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test-topic",
}
}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[light.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def light_platform_only():
@ -156,22 +167,21 @@ class JsonValidator:
return json.loads(self.jsondata) == json.loads(other)
async def test_fail_setup_if_no_command_topic(hass, mqtt_mock_entry_no_yaml_config):
async def test_fail_setup_if_no_command_topic(hass, caplog):
"""Test if setup fails with no command topic."""
assert await async_setup_component(
assert not await async_setup_component(
hass,
light.DOMAIN,
{light.DOMAIN: {"platform": "mqtt", "schema": "json", "name": "test"}},
mqtt.DOMAIN,
{mqtt.DOMAIN: {light.DOMAIN: {"schema": "json", "name": "test"}}},
)
assert (
"Invalid config for [mqtt]: required key not provided @ data['mqtt']['light'][0]['command_topic']. Got None."
in caplog.text
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert hass.states.get("light.test") is None
@pytest.mark.parametrize("deprecated", ("color_temp", "hs", "rgb", "xy"))
async def test_fail_setup_if_color_mode_deprecated(
hass, mqtt_mock_entry_no_yaml_config, deprecated
):
async def test_fail_setup_if_color_mode_deprecated(hass, caplog, deprecated):
"""Test if setup fails if color mode is combined with deprecated config keys."""
supported_color_modes = ["color_temp", "hs", "rgb", "rgbw", "rgbww", "xy"]
@ -181,27 +191,32 @@ async def test_fail_setup_if_color_mode_deprecated(
"color_mode": True,
"command_topic": "test_light_rgb/set",
"name": "test",
"platform": "mqtt",
"schema": "json",
"supported_color_modes": supported_color_modes,
}
}
config[light.DOMAIN][deprecated] = True
assert await async_setup_component(
assert not await async_setup_component(
hass,
light.DOMAIN,
config,
mqtt.DOMAIN,
{mqtt.DOMAIN: config},
)
assert (
"Invalid config for [mqtt]: color_mode must not be combined with any of"
in caplog.text
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert hass.states.get("light.test") is None
@pytest.mark.parametrize(
"supported_color_modes", [["onoff", "rgb"], ["brightness", "rgb"], ["unknown"]]
"supported_color_modes,error",
[
(["onoff", "rgb"], "Unknown error calling mqtt CONFIG_SCHEMA"),
(["brightness", "rgb"], "Unknown error calling mqtt CONFIG_SCHEMA"),
(["unknown"], "Invalid config for [mqtt]: value must be one of [<ColorMode."),
],
)
async def test_fail_setup_if_color_modes_invalid(
hass, mqtt_mock_entry_no_yaml_config, supported_color_modes
hass, caplog, supported_color_modes, error
):
"""Test if setup fails if supported color modes is invalid."""
config = {
@ -210,34 +225,32 @@ async def test_fail_setup_if_color_modes_invalid(
"color_mode": True,
"command_topic": "test_light_rgb/set",
"name": "test",
"platform": "mqtt",
"schema": "json",
"supported_color_modes": supported_color_modes,
}
}
assert await async_setup_component(
assert not await async_setup_component(
hass,
light.DOMAIN,
config,
mqtt.DOMAIN,
{mqtt.DOMAIN: config},
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert hass.states.get("light.test") is None
assert error in caplog.text
async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config):
"""Test RGB light flags brightness support."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
"rgb": True,
}
}
},
)
await hass.async_block_till_done()
@ -259,15 +272,16 @@ async def test_no_color_brightness_color_temp_if_no_topics(
"""Test for no RGB, brightness, color temp, effector XY."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"state_topic": "test_light_rgb",
"command_topic": "test_light_rgb/set",
}
}
},
)
await hass.async_block_till_done()
@ -310,10 +324,10 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi
"""Test the controlling of the state via topic."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"state_topic": "test_light_rgb",
@ -326,6 +340,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi
"hs": True,
"qos": "0",
}
}
},
)
await hass.async_block_till_done()
@ -434,20 +449,21 @@ async def test_controlling_state_via_topic2(
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"brightness": True,
"color_mode": True,
"command_topic": "test_light_rgb/set",
"effect": True,
"name": "test",
"platform": "mqtt",
"qos": "0",
"schema": "json",
"state_topic": "test_light_rgb",
"supported_color_modes": supported_color_modes,
}
}
},
)
await hass.async_block_till_done()
@ -618,10 +634,10 @@ async def test_sending_mqtt_commands_and_optimistic(
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
@ -633,6 +649,7 @@ async def test_sending_mqtt_commands_and_optimistic(
"xy": True,
"qos": 2,
}
}
},
)
await hass.async_block_till_done()
@ -755,19 +772,20 @@ async def test_sending_mqtt_commands_and_optimistic2(
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"brightness": True,
"color_mode": True,
"command_topic": "test_light_rgb/set",
"effect": True,
"name": "test",
"platform": "mqtt",
"qos": 2,
"schema": "json",
"supported_color_modes": supported_color_modes,
}
}
},
)
await hass.async_block_till_done()
@ -975,16 +993,17 @@ async def test_sending_hs_color(hass, mqtt_mock_entry_with_yaml_config):
"""Test light.turn_on with hs color sends hs color parameters."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
"brightness": True,
"hs": True,
}
}
},
)
await hass.async_block_till_done()
@ -1035,15 +1054,16 @@ async def test_sending_rgb_color_no_brightness(hass, mqtt_mock_entry_with_yaml_c
"""Test light.turn_on with hs color sends rgb color parameters."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
"rgb": True,
}
}
},
)
await hass.async_block_till_done()
@ -1090,16 +1110,17 @@ async def test_sending_rgb_color_no_brightness2(hass, mqtt_mock_entry_with_yaml_
supported_color_modes = ["rgb", "rgbw", "rgbww"]
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"color_mode": True,
"command_topic": "test_light_rgb/set",
"name": "test",
"platform": "mqtt",
"schema": "json",
"supported_color_modes": supported_color_modes,
}
}
},
)
await hass.async_block_till_done()
@ -1169,16 +1190,17 @@ async def test_sending_rgb_color_with_brightness(
"""Test light.turn_on with hs color sends rgb color parameters."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
"brightness": True,
"rgb": True,
}
}
},
)
await hass.async_block_till_done()
@ -1236,10 +1258,10 @@ async def test_sending_rgb_color_with_scaled_brightness(
"""Test light.turn_on with hs color sends rgb color parameters."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
@ -1247,6 +1269,7 @@ async def test_sending_rgb_color_with_scaled_brightness(
"brightness_scale": 100,
"rgb": True,
}
}
},
)
await hass.async_block_till_done()
@ -1302,10 +1325,10 @@ async def test_sending_scaled_white(hass, mqtt_mock_entry_with_yaml_config):
"""Test light.turn_on with scaled white."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
@ -1315,6 +1338,7 @@ async def test_sending_scaled_white(hass, mqtt_mock_entry_with_yaml_config):
"supported_color_modes": ["hs", "white"],
"white_scale": 50,
}
}
},
)
await hass.async_block_till_done()
@ -1346,16 +1370,17 @@ async def test_sending_xy_color(hass, mqtt_mock_entry_with_yaml_config):
"""Test light.turn_on with hs color sends xy color parameters."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
"brightness": True,
"xy": True,
}
}
},
)
await hass.async_block_till_done()
@ -1405,16 +1430,17 @@ async def test_effect(hass, mqtt_mock_entry_with_yaml_config):
"""Test for effect being sent when included."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
"effect": True,
"qos": 0,
}
}
},
)
await hass.async_block_till_done()
@ -1468,10 +1494,10 @@ async def test_flash_short_and_long(hass, mqtt_mock_entry_with_yaml_config):
"""Test for flash length being sent when included."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
@ -1479,6 +1505,7 @@ async def test_flash_short_and_long(hass, mqtt_mock_entry_with_yaml_config):
"flash_time_long": 15,
"qos": 0,
}
}
},
)
await hass.async_block_till_done()
@ -1530,15 +1557,16 @@ async def test_transition(hass, mqtt_mock_entry_with_yaml_config):
"""Test for transition time being sent when included."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_light_rgb/set",
"qos": 0,
}
}
},
)
await hass.async_block_till_done()
@ -1577,10 +1605,10 @@ async def test_brightness_scale(hass, mqtt_mock_entry_with_yaml_config):
"""Test for brightness scaling."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"state_topic": "test_light_bright_scale",
@ -1588,6 +1616,7 @@ async def test_brightness_scale(hass, mqtt_mock_entry_with_yaml_config):
"brightness": True,
"brightness_scale": 99,
}
}
},
)
await hass.async_block_till_done()
@ -1619,10 +1648,10 @@ async def test_white_scale(hass, mqtt_mock_entry_with_yaml_config):
"""Test for white scaling."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"state_topic": "test_light_bright_scale",
@ -1633,6 +1662,7 @@ async def test_white_scale(hass, mqtt_mock_entry_with_yaml_config):
"supported_color_modes": ["hs", "white"],
"white_scale": 50,
}
}
},
)
await hass.async_block_till_done()
@ -1675,10 +1705,10 @@ async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config):
"""Test that invalid color/brightness/etc. values are ignored."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"state_topic": "test_light_rgb",
@ -1688,6 +1718,7 @@ async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config):
"rgb": True,
"qos": "0",
}
}
},
)
await hass.async_block_till_done()
@ -1858,16 +1889,24 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
light.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
light.DOMAIN,
DEFAULT_CONFIG,
)
@ -1885,9 +1924,9 @@ async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplo
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one light per unique_id."""
config = {
mqtt.DOMAIN: {
light.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"schema": "json",
"state_topic": "test-topic",
@ -1895,7 +1934,6 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"schema": "json",
"state_topic": "test-topic",
@ -1904,6 +1942,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, config
)
@ -2031,20 +2070,14 @@ async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config):
async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config):
"""Test MQTT subscriptions are managed when entity_id is updated."""
await help_test_entity_id_update_subscriptions(
hass,
mqtt_mock_entry_with_yaml_config,
light.DOMAIN,
DEFAULT_CONFIG,
hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT discovery update when entity_id is updated."""
await help_test_entity_id_update_discovery_update(
hass,
mqtt_mock_entry_no_yaml_config,
light.DOMAIN,
DEFAULT_CONFIG,
hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG
)
@ -2065,7 +2098,6 @@ async def test_max_mireds(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting min_mireds and max_mireds."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"schema": "json",
"name": "test",
"command_topic": "test_max_mireds/set",
@ -2074,7 +2106,7 @@ async def test_max_mireds(hass, mqtt_mock_entry_with_yaml_config):
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -2120,9 +2152,9 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
config = copy.deepcopy(DEFAULT_CONFIG)
if topic == "effect_command_topic":
config["effect_list"] = ["random", "color_loop"]
config[mqtt.DOMAIN][domain]["effect_list"] = ["random", "color_loop"]
await help_test_publishing_with_custom_encoding(
hass,
@ -2143,16 +2175,18 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = light.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = light.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -2179,7 +2213,7 @@ async def test_encoding_subscribable_topics(
init_payload,
):
"""Test handling of incoming encoded payload."""
config = copy.deepcopy(DEFAULT_CONFIG[light.DOMAIN])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][light.DOMAIN])
config["color_mode"] = True
config["supported_color_modes"] = [
"color_temp",
@ -2207,8 +2241,18 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -2,8 +2,9 @@
Configuration example with all features:
light:
platform: mqtt_template
mqtt:
light:
schema: template
name: mqtt_template_light_1
state_topic: 'home/rgb1'
command_topic: 'home/rgb1/set'
@ -28,7 +29,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import light
from homeassistant.components import light, mqtt
from homeassistant.components.mqtt.light.schema_basic import (
MQTT_LIGHT_ATTRIBUTES_BLOCKED,
)
@ -70,28 +71,30 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import (
assert_setup_component,
async_fire_mqtt_message,
mock_restore_cache,
)
from tests.common import async_fire_mqtt_message, mock_restore_cache
from tests.components.light import common
DEFAULT_CONFIG = {
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"command_topic": "test-topic",
"command_on_template": "on,{{ transition }}",
"command_off_template": "off,{{ transition|d }}",
}
}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[light.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def light_platform_only():
@ -103,10 +106,9 @@ def light_platform_only():
@pytest.mark.parametrize(
"test_config",
[
({"platform": "mqtt", "schema": "template", "name": "test"},),
({"schema": "template", "name": "test"},),
(
{
"platform": "mqtt",
"schema": "template",
"name": "test",
"command_topic": "test_topic",
@ -114,7 +116,6 @@ def light_platform_only():
),
(
{
"platform": "mqtt",
"schema": "template",
"name": "test",
"command_topic": "test_topic",
@ -123,7 +124,6 @@ def light_platform_only():
),
(
{
"platform": "mqtt",
"schema": "template",
"name": "test",
"command_topic": "test_topic",
@ -132,28 +132,24 @@ def light_platform_only():
),
],
)
async def test_setup_fails(hass, mqtt_mock_entry_no_yaml_config, test_config):
async def test_setup_fails(hass, caplog, test_config):
"""Test that setup fails with missing required configuration items."""
with assert_setup_component(0, light.DOMAIN) as setup_config:
assert await async_setup_component(
assert not await async_setup_component(
hass,
light.DOMAIN,
{light.DOMAIN: test_config},
mqtt.DOMAIN,
{mqtt.DOMAIN: {light.DOMAIN: test_config}},
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert not setup_config[light.DOMAIN]
assert hass.states.get("light.test") is None
assert "Invalid config for [mqtt]" in caplog.text
async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config):
"""Test RGB light flags brightness support."""
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"command_topic": "test_light_rgb/set",
@ -163,6 +159,7 @@ async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config):
"green_template": '{{ value.split(",")[4].' 'split("-")[1] }}',
"blue_template": '{{ value.split(",")[4].' 'split("-")[2] }}',
}
}
},
)
await hass.async_block_till_done()
@ -181,13 +178,12 @@ async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config):
async def test_state_change_via_topic(hass, mqtt_mock_entry_with_yaml_config):
"""Test state change via topic."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"state_topic": "test_light_rgb",
@ -201,6 +197,7 @@ async def test_state_change_via_topic(hass, mqtt_mock_entry_with_yaml_config):
"command_off_template": "off",
"state_template": '{{ value.split(",")[0] }}',
}
}
},
)
await hass.async_block_till_done()
@ -236,13 +233,12 @@ async def test_state_brightness_color_effect_temp_change_via_topic(
hass, mqtt_mock_entry_with_yaml_config
):
"""Test state, bri, color, effect, color temp change."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"effect_list": ["rainbow", "colorloop"],
@ -264,6 +260,7 @@ async def test_state_brightness_color_effect_temp_change_via_topic(
"blue_template": '{{ value.split(",")[3].' 'split("-")[2] }}',
"effect_template": '{{ value.split(",")[4] }}',
}
}
},
)
await hass.async_block_till_done()
@ -340,13 +337,12 @@ async def test_sending_mqtt_commands_and_optimistic(
)
mock_restore_cache(hass, (fake_state,))
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"command_topic": "test_light_rgb/set",
@ -369,6 +365,7 @@ async def test_sending_mqtt_commands_and_optimistic(
"effect_template": '{{ value.split(",")[4] }}',
"qos": 2,
}
}
},
)
await hass.async_block_till_done()
@ -470,13 +467,12 @@ async def test_sending_mqtt_commands_non_optimistic_brightness_template(
hass, mqtt_mock_entry_with_yaml_config
):
"""Test the sending of command in optimistic mode."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"effect_list": ["rainbow", "colorloop"],
@ -499,6 +495,7 @@ async def test_sending_mqtt_commands_non_optimistic_brightness_template(
"blue_template": '{{ value.split(",")[3].' 'split("-")[2] }}',
"effect_template": '{{ value.split(",")[4] }}',
}
}
},
)
await hass.async_block_till_done()
@ -591,13 +588,12 @@ async def test_sending_mqtt_commands_non_optimistic_brightness_template(
async def test_effect(hass, mqtt_mock_entry_with_yaml_config):
"""Test effect sent over MQTT in optimistic mode."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"effect_list": ["rainbow", "colorloop"],
"name": "test",
@ -606,6 +602,7 @@ async def test_effect(hass, mqtt_mock_entry_with_yaml_config):
"command_off_template": "off",
"qos": 0,
}
}
},
)
await hass.async_block_till_done()
@ -644,13 +641,12 @@ async def test_effect(hass, mqtt_mock_entry_with_yaml_config):
async def test_flash(hass, mqtt_mock_entry_with_yaml_config):
"""Test flash sent over MQTT in optimistic mode."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"command_topic": "test_light_rgb/set",
@ -658,6 +654,7 @@ async def test_flash(hass, mqtt_mock_entry_with_yaml_config):
"command_off_template": "off",
"qos": 0,
}
}
},
)
await hass.async_block_till_done()
@ -693,13 +690,12 @@ async def test_flash(hass, mqtt_mock_entry_with_yaml_config):
async def test_transition(hass, mqtt_mock_entry_with_yaml_config):
"""Test for transition time being sent when included."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"command_topic": "test_light_rgb/set",
@ -707,6 +703,7 @@ async def test_transition(hass, mqtt_mock_entry_with_yaml_config):
"command_off_template": "off,{{ transition|int|d }}",
"qos": 1,
}
}
},
)
await hass.async_block_till_done()
@ -735,13 +732,12 @@ async def test_transition(hass, mqtt_mock_entry_with_yaml_config):
async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config):
"""Test that invalid values are ignored."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component(
hass,
light.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"effect_list": ["rainbow", "colorloop"],
@ -763,6 +759,7 @@ async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config):
"blue_template": '{{ value.split(",")[3].' 'split("-")[2] }}',
"effect_template": '{{ value.split(",")[4] }}',
}
}
},
)
await hass.async_block_till_done()
@ -886,32 +883,44 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
light.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
light.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, light.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
light.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one light per unique_id."""
config = {
mqtt.DOMAIN: {
light.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"schema": "template",
"state_topic": "test-topic",
@ -921,15 +930,17 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"schema": "template",
"state_topic": "test-topic",
"command_topic": "test_topic",
"state_topic": "test-topic2",
"command_topic": "test_topic2",
"command_on_template": "on,{{ transition }}",
"command_off_template": "off,{{ transition|d }}",
"unique_id": "TOTALLY_UNIQUE",
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, config
)
@ -1059,8 +1070,8 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_c
async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT debug info."""
config = {
mqtt.DOMAIN: {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"command_topic": "test-topic",
@ -1069,6 +1080,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
"state_template": '{{ value.split(",")[0] }}',
}
}
}
await help_test_entity_debug_info_message(
hass,
mqtt_mock_entry_no_yaml_config,
@ -1082,7 +1094,6 @@ async def test_max_mireds(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting min_mireds and max_mireds."""
config = {
light.DOMAIN: {
"platform": "mqtt",
"schema": "template",
"name": "test",
"command_topic": "test_max_mireds/set",
@ -1093,7 +1104,7 @@ async def test_max_mireds(hass, mqtt_mock_entry_with_yaml_config):
}
}
assert await async_setup_component(hass, light.DOMAIN, config)
assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -1139,9 +1150,9 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
config = copy.deepcopy(DEFAULT_CONFIG)
if topic == "effect_command_topic":
config["effect_list"] = ["random", "color_loop"]
config[mqtt.DOMAIN][domain]["effect_list"] = ["random", "color_loop"]
await help_test_publishing_with_custom_encoding(
hass,
@ -1162,16 +1173,18 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = light.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = light.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -1192,7 +1205,7 @@ async def test_encoding_subscribable_topics(
init_payload,
):
"""Test handling of incoming encoded payload."""
config = copy.deepcopy(DEFAULT_CONFIG[light.DOMAIN])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][light.DOMAIN])
config["state_template"] = "{{ value }}"
await help_test_encoding_subscribable_topics(
hass,
@ -1211,17 +1224,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = light.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -4,14 +4,14 @@ from unittest.mock import patch
import pytest
from homeassistant.components import lock, mqtt
from homeassistant.components.lock import (
DOMAIN as LOCK_DOMAIN,
SERVICE_LOCK,
SERVICE_OPEN,
SERVICE_UNLOCK,
STATE_LOCKED,
STATE_UNLOCKED,
SUPPORT_OPEN,
LockEntityFeature,
)
from homeassistant.components.mqtt.lock import MQTT_LOCK_ATTRIBUTES_BLOCKED
from homeassistant.const import (
@ -49,16 +49,21 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import async_fire_mqtt_message
DEFAULT_CONFIG = {
LOCK_DOMAIN: {"platform": "mqtt", "name": "test", "command_topic": "test-topic"}
mqtt.DOMAIN: {lock.DOMAIN: {"name": "test", "command_topic": "test-topic"}}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[lock.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def lock_platform_only():
@ -71,10 +76,10 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi
"""Test the controlling state via topic."""
assert await async_setup_component(
hass,
LOCK_DOMAIN,
mqtt.DOMAIN,
{
LOCK_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
lock.DOMAIN: {
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -83,6 +88,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi
"state_locked": "LOCKED",
"state_unlocked": "UNLOCKED",
}
}
},
)
await hass.async_block_till_done()
@ -110,10 +116,10 @@ async def test_controlling_non_default_state_via_topic(
"""Test the controlling state via topic."""
assert await async_setup_component(
hass,
LOCK_DOMAIN,
mqtt.DOMAIN,
{
LOCK_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
lock.DOMAIN: {
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -122,6 +128,7 @@ async def test_controlling_non_default_state_via_topic(
"state_locked": "closed",
"state_unlocked": "open",
}
}
},
)
await hass.async_block_till_done()
@ -148,10 +155,10 @@ async def test_controlling_state_via_topic_and_json_message(
"""Test the controlling state via topic and JSON message."""
assert await async_setup_component(
hass,
LOCK_DOMAIN,
mqtt.DOMAIN,
{
LOCK_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
lock.DOMAIN: {
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -161,6 +168,7 @@ async def test_controlling_state_via_topic_and_json_message(
"state_unlocked": "UNLOCKED",
"value_template": "{{ value_json.val }}",
}
}
},
)
await hass.async_block_till_done()
@ -186,10 +194,10 @@ async def test_controlling_non_default_state_via_topic_and_json_message(
"""Test the controlling state via topic and JSON message."""
assert await async_setup_component(
hass,
LOCK_DOMAIN,
mqtt.DOMAIN,
{
LOCK_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
lock.DOMAIN: {
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -199,6 +207,7 @@ async def test_controlling_non_default_state_via_topic_and_json_message(
"state_unlocked": "open",
"value_template": "{{ value_json.val }}",
}
}
},
)
await hass.async_block_till_done()
@ -224,10 +233,10 @@ async def test_sending_mqtt_commands_and_optimistic(
"""Test optimistic mode without state topic."""
assert await async_setup_component(
hass,
LOCK_DOMAIN,
mqtt.DOMAIN,
{
LOCK_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
lock.DOMAIN: {
"name": "test",
"command_topic": "command-topic",
"payload_lock": "LOCK",
@ -235,6 +244,7 @@ async def test_sending_mqtt_commands_and_optimistic(
"state_locked": "LOCKED",
"state_unlocked": "UNLOCKED",
}
}
},
)
await hass.async_block_till_done()
@ -245,7 +255,7 @@ async def test_sending_mqtt_commands_and_optimistic(
assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call(
LOCK_DOMAIN, SERVICE_LOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
lock.DOMAIN, SERVICE_LOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "LOCK", 0, False)
@ -255,7 +265,7 @@ async def test_sending_mqtt_commands_and_optimistic(
assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call(
LOCK_DOMAIN, SERVICE_UNLOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
lock.DOMAIN, SERVICE_UNLOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "UNLOCK", 0, False)
@ -271,10 +281,10 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
"""Test optimistic mode without state topic."""
assert await async_setup_component(
hass,
LOCK_DOMAIN,
mqtt.DOMAIN,
{
LOCK_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
lock.DOMAIN: {
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -284,6 +294,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
"state_unlocked": "UNLOCKED",
"optimistic": True,
}
}
},
)
await hass.async_block_till_done()
@ -294,7 +305,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call(
LOCK_DOMAIN, SERVICE_LOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
lock.DOMAIN, SERVICE_LOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "LOCK", 0, False)
@ -304,7 +315,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call(
LOCK_DOMAIN, SERVICE_UNLOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
lock.DOMAIN, SERVICE_UNLOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "UNLOCK", 0, False)
@ -320,10 +331,10 @@ async def test_sending_mqtt_commands_support_open_and_optimistic(
"""Test open function of the lock without state topic."""
assert await async_setup_component(
hass,
LOCK_DOMAIN,
mqtt.DOMAIN,
{
LOCK_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
lock.DOMAIN: {
"name": "test",
"command_topic": "command-topic",
"payload_lock": "LOCK",
@ -332,6 +343,7 @@ async def test_sending_mqtt_commands_support_open_and_optimistic(
"state_locked": "LOCKED",
"state_unlocked": "UNLOCKED",
}
}
},
)
await hass.async_block_till_done()
@ -340,10 +352,10 @@ async def test_sending_mqtt_commands_support_open_and_optimistic(
state = hass.states.get("lock.test")
assert state.state is STATE_UNLOCKED
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == SUPPORT_OPEN
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == LockEntityFeature.OPEN
await hass.services.async_call(
LOCK_DOMAIN, SERVICE_LOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
lock.DOMAIN, SERVICE_LOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "LOCK", 0, False)
@ -353,7 +365,7 @@ async def test_sending_mqtt_commands_support_open_and_optimistic(
assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call(
LOCK_DOMAIN, SERVICE_UNLOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
lock.DOMAIN, SERVICE_UNLOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "UNLOCK", 0, False)
@ -363,7 +375,7 @@ async def test_sending_mqtt_commands_support_open_and_optimistic(
assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call(
LOCK_DOMAIN, SERVICE_OPEN, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
lock.DOMAIN, SERVICE_OPEN, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "OPEN", 0, False)
@ -379,10 +391,10 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic(
"""Test open function of the lock without state topic."""
assert await async_setup_component(
hass,
LOCK_DOMAIN,
mqtt.DOMAIN,
{
LOCK_DOMAIN: {
"platform": "mqtt",
mqtt.DOMAIN: {
lock.DOMAIN: {
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -393,6 +405,7 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic(
"state_unlocked": "UNLOCKED",
"optimistic": True,
}
}
},
)
await hass.async_block_till_done()
@ -401,10 +414,10 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic(
state = hass.states.get("lock.test")
assert state.state is STATE_UNLOCKED
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == SUPPORT_OPEN
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == LockEntityFeature.OPEN
await hass.services.async_call(
LOCK_DOMAIN, SERVICE_LOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
lock.DOMAIN, SERVICE_LOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "LOCK", 0, False)
@ -414,7 +427,7 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic(
assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call(
LOCK_DOMAIN, SERVICE_UNLOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
lock.DOMAIN, SERVICE_UNLOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "UNLOCK", 0, False)
@ -424,7 +437,7 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic(
assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call(
LOCK_DOMAIN, SERVICE_OPEN, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
lock.DOMAIN, SERVICE_OPEN, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "OPEN", 0, False)
@ -439,28 +452,28 @@ async def test_availability_when_connection_lost(
):
"""Test availability after MQTT disconnection."""
await help_test_availability_when_connection_lost(
hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability without defined availability topic."""
await help_test_availability_without_topic(
hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic."""
await help_test_default_availability_payload(
hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic."""
await help_test_custom_availability_payload(
hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
@ -469,7 +482,7 @@ async def test_setting_attribute_via_mqtt_json_message(
):
"""Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_via_mqtt_json_message(
hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
@ -480,7 +493,7 @@ async def test_setting_blocked_attribute_via_mqtt_json_message(
await help_test_setting_blocked_attribute_via_mqtt_json_message(
hass,
mqtt_mock_entry_no_yaml_config,
LOCK_DOMAIN,
lock.DOMAIN,
DEFAULT_CONFIG,
MQTT_LOCK_ATTRIBUTES_BLOCKED,
)
@ -489,7 +502,7 @@ async def test_setting_blocked_attribute_via_mqtt_json_message(
async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_with_template(
hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
@ -498,7 +511,11 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, LOCK_DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
lock.DOMAIN,
DEFAULT_CONFIG,
)
@ -506,31 +523,34 @@ async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, LOCK_DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
lock.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, caplog, lock.DOMAIN, DEFAULT_CONFIG
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one lock per unique_id."""
config = {
LOCK_DOMAIN: [
mqtt.DOMAIN: {
lock.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"command_topic": "test_topic",
@ -538,8 +558,9 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, config
hass, mqtt_mock_entry_with_yaml_config, lock.DOMAIN, config
)
@ -547,7 +568,7 @@ async def test_discovery_removal_lock(hass, mqtt_mock_entry_no_yaml_config, capl
"""Test removal of discovered lock."""
data = '{ "name": "test",' ' "command_topic": "test_topic" }'
await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, LOCK_DOMAIN, data
hass, mqtt_mock_entry_no_yaml_config, caplog, lock.DOMAIN, data
)
@ -566,7 +587,7 @@ async def test_discovery_update_lock(hass, mqtt_mock_entry_no_yaml_config, caplo
"availability_topic": "availability_topic2",
}
await help_test_discovery_update(
hass, mqtt_mock_entry_no_yaml_config, caplog, LOCK_DOMAIN, config1, config2
hass, mqtt_mock_entry_no_yaml_config, caplog, lock.DOMAIN, config1, config2
)
@ -586,7 +607,7 @@ async def test_discovery_update_unchanged_lock(
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
LOCK_DOMAIN,
lock.DOMAIN,
data1,
discovery_update,
)
@ -598,49 +619,49 @@ async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog):
data1 = '{ "name": "Beer" }'
data2 = '{ "name": "Milk",' ' "command_topic": "test_topic" }'
await help_test_discovery_broken(
hass, mqtt_mock_entry_no_yaml_config, caplog, LOCK_DOMAIN, data1, data2
hass, mqtt_mock_entry_no_yaml_config, caplog, lock.DOMAIN, data1, data2
)
async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT lock device registry integration."""
await help_test_entity_device_info_with_connection(
hass, mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT lock device registry integration."""
await help_test_entity_device_info_with_identifier(
hass, mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry update."""
await help_test_entity_device_info_update(
hass, mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry remove."""
await help_test_entity_device_info_remove(
hass, mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config):
"""Test MQTT subscriptions are managed when entity_id is updated."""
await help_test_entity_id_update_subscriptions(
hass, mqtt_mock_entry_with_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_with_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT discovery update when entity_id is updated."""
await help_test_entity_id_update_discovery_update(
hass, mqtt_mock_entry_no_yaml_config, LOCK_DOMAIN, DEFAULT_CONFIG
hass, mqtt_mock_entry_no_yaml_config, lock.DOMAIN, DEFAULT_CONFIG
)
@ -649,7 +670,7 @@ async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
await help_test_entity_debug_info_message(
hass,
mqtt_mock_entry_no_yaml_config,
LOCK_DOMAIN,
lock.DOMAIN,
DEFAULT_CONFIG,
SERVICE_LOCK,
command_payload="LOCK",
@ -679,8 +700,8 @@ async def test_publishing_with_custom_encoding(
template,
):
"""Test publishing MQTT payload with different encoding."""
domain = LOCK_DOMAIN
config = DEFAULT_CONFIG[domain]
domain = lock.DOMAIN
config = DEFAULT_CONFIG
await help_test_publishing_with_custom_encoding(
hass,
@ -698,17 +719,19 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = LOCK_DOMAIN
config = DEFAULT_CONFIG[domain]
domain = lock.DOMAIN
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = LOCK_DOMAIN
config = DEFAULT_CONFIG[domain]
domain = lock.DOMAIN
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -732,8 +755,8 @@ async def test_encoding_subscribable_topics(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
LOCK_DOMAIN,
DEFAULT_CONFIG[LOCK_DOMAIN],
lock.DOMAIN,
DEFAULT_CONFIG[mqtt.DOMAIN][lock.DOMAIN],
topic,
value,
attribute,
@ -743,18 +766,28 @@ 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 = LOCK_DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
platform = lock.DOMAIN
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = LOCK_DOMAIN
config = DEFAULT_CONFIG[domain]
domain = lock.DOMAIN
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = lock.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -59,16 +59,21 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import async_fire_mqtt_message, mock_restore_cache_with_extra_data
DEFAULT_CONFIG = {
number.DOMAIN: {"platform": "mqtt", "name": "test", "command_topic": "test-topic"}
mqtt.DOMAIN: {number.DOMAIN: {"name": "test", "command_topic": "test-topic"}}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[number.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def number_platform_only():
@ -82,10 +87,10 @@ async def test_run_number_setup(hass, mqtt_mock_entry_with_yaml_config):
topic = "test/number"
await async_setup_component(
hass,
"number",
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"state_topic": topic,
"command_topic": topic,
"name": "Test Number",
@ -93,6 +98,7 @@ async def test_run_number_setup(hass, mqtt_mock_entry_with_yaml_config):
"unit_of_measurement": TEMP_FAHRENHEIT,
"payload_reset": "reset!",
}
}
},
)
await hass.async_block_till_done()
@ -131,15 +137,16 @@ async def test_value_template(hass, mqtt_mock_entry_with_yaml_config):
topic = "test/number"
await async_setup_component(
hass,
"number",
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"state_topic": topic,
"command_topic": topic,
"name": "Test Number",
"value_template": "{{ value_json.val }}",
}
}
},
)
await hass.async_block_till_done()
@ -184,15 +191,16 @@ async def test_restore_native_value(hass, mqtt_mock_entry_with_yaml_config):
)
assert await async_setup_component(
hass,
number.DOMAIN,
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"command_topic": topic,
"device_class": "temperature",
"unit_of_measurement": TEMP_FAHRENHEIT,
"name": "Test Number",
}
}
},
)
await hass.async_block_till_done()
@ -220,13 +228,14 @@ async def test_run_number_service_optimistic(hass, mqtt_mock_entry_with_yaml_con
)
assert await async_setup_component(
hass,
number.DOMAIN,
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"command_topic": topic,
"name": "Test Number",
}
}
},
)
await hass.async_block_till_done()
@ -295,14 +304,15 @@ async def test_run_number_service_optimistic_with_command_template(
)
assert await async_setup_component(
hass,
number.DOMAIN,
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"command_topic": topic,
"name": "Test Number",
"command_template": '{"number": {{ value }} }',
}
}
},
)
await hass.async_block_till_done()
@ -361,14 +371,15 @@ async def test_run_number_service(hass, mqtt_mock_entry_with_yaml_config):
assert await async_setup_component(
hass,
number.DOMAIN,
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"command_topic": cmd_topic,
"state_topic": state_topic,
"name": "Test Number",
}
}
},
)
await hass.async_block_till_done()
@ -398,15 +409,16 @@ async def test_run_number_service_with_command_template(
assert await async_setup_component(
hass,
number.DOMAIN,
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"command_topic": cmd_topic,
"state_topic": state_topic,
"name": "Test Number",
"command_template": '{"number": {{ value }} }',
}
}
},
)
await hass.async_block_till_done()
@ -493,39 +505,50 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, number.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
number.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, number.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
number.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, number.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
number.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one number per unique_id."""
config = {
mqtt.DOMAIN: {
number.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"command_topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"command_topic": "test-topic",
@ -533,6 +556,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, number.DOMAIN, config
)
@ -540,7 +564,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
async def test_discovery_removal_number(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered number."""
data = json.dumps(DEFAULT_CONFIG[number.DOMAIN])
data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][number.DOMAIN])
await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, number.DOMAIN, data
)
@ -658,10 +682,10 @@ async def test_min_max_step_attributes(hass, mqtt_mock_entry_with_yaml_config):
topic = "test/number"
await async_setup_component(
hass,
"number",
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"state_topic": topic,
"command_topic": topic,
"name": "Test Number",
@ -669,6 +693,7 @@ async def test_min_max_step_attributes(hass, mqtt_mock_entry_with_yaml_config):
"max": 110,
"step": 20,
}
}
},
)
await hass.async_block_till_done()
@ -680,25 +705,24 @@ async def test_min_max_step_attributes(hass, mqtt_mock_entry_with_yaml_config):
assert state.attributes.get(ATTR_STEP) == 20
async def test_invalid_min_max_attributes(hass, caplog, mqtt_mock_entry_no_yaml_config):
async def test_invalid_min_max_attributes(hass, caplog):
"""Test invalid min/max attributes."""
topic = "test/number"
await async_setup_component(
assert not await async_setup_component(
hass,
"number",
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"state_topic": topic,
"command_topic": topic,
"name": "Test Number",
"min": 35,
"max": 10,
}
}
},
)
await hass.async_block_till_done()
await mqtt_mock_entry_no_yaml_config()
assert f"'{CONF_MAX}' must be > '{CONF_MIN}'" in caplog.text
@ -779,16 +803,17 @@ async def test_mqtt_payload_not_a_number_warning(
):
"""Test warning for MQTT payload which is not a number."""
topic = "test/number"
await async_setup_component(
assert await async_setup_component(
hass,
"number",
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"state_topic": topic,
"command_topic": topic,
"name": "Test Number",
}
}
},
)
await hass.async_block_till_done()
@ -808,16 +833,17 @@ async def test_mqtt_payload_out_of_range_error(
topic = "test/number"
await async_setup_component(
hass,
"number",
mqtt.DOMAIN,
{
"number": {
"platform": "mqtt",
mqtt.DOMAIN: {
number.DOMAIN: {
"state_topic": topic,
"command_topic": topic,
"name": "Test Number",
"min": 5,
"max": 110,
}
}
},
)
await hass.async_block_till_done()
@ -856,7 +882,7 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = NUMBER_DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_publishing_with_custom_encoding(
hass,
@ -875,16 +901,18 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = number.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = number.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -909,8 +937,8 @@ async def test_encoding_subscribable_topics(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
"number",
DEFAULT_CONFIG["number"],
number.DOMAIN,
DEFAULT_CONFIG[mqtt.DOMAIN][number.DOMAIN],
topic,
value,
attribute,
@ -921,17 +949,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = number.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = number.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = number.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -4,7 +4,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import scene
from homeassistant.components import mqtt, scene
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON, STATE_UNKNOWN, Platform
import homeassistant.core as ha
from homeassistant.setup import async_setup_component
@ -28,14 +28,20 @@ from .test_common import (
from tests.common import mock_restore_cache
DEFAULT_CONFIG = {
mqtt.DOMAIN: {
scene.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test-topic",
"payload_on": "test-payload-on",
}
}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[scene.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def scene_platform_only():
@ -51,14 +57,15 @@ async def test_sending_mqtt_commands(hass, mqtt_mock_entry_with_yaml_config):
assert await async_setup_component(
hass,
scene.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
scene.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"payload_on": "beer on",
},
}
},
)
await hass.async_block_till_done()
@ -94,13 +101,14 @@ async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config
async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic."""
config = {
mqtt.DOMAIN: {
scene.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"payload_on": 1,
}
}
}
await help_test_default_availability_payload(
hass,
@ -116,13 +124,14 @@ async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_conf
async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic."""
config = {
mqtt.DOMAIN: {
scene.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"payload_on": 1,
}
}
}
await help_test_custom_availability_payload(
hass,
@ -138,21 +147,21 @@ async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_confi
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one scene per unique_id."""
config = {
mqtt.DOMAIN: {
scene.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE",
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, scene.DOMAIN, config
)
@ -168,8 +177,8 @@ async def test_discovery_removal_scene(hass, mqtt_mock_entry_no_yaml_config, cap
async def test_discovery_update_payload(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered scene."""
config1 = copy.deepcopy(DEFAULT_CONFIG[scene.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[scene.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][scene.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][scene.DOMAIN])
config1["name"] = "Beer"
config2["name"] = "Milk"
config1["payload_on"] = "ON"
@ -216,33 +225,45 @@ async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog):
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = scene.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = scene.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = scene.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = scene.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = scene.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -5,7 +5,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import select
from homeassistant.components import mqtt, select
from homeassistant.components.mqtt.select import MQTT_SELECT_ATTRIBUTES_BLOCKED
from homeassistant.components.select import (
ATTR_OPTION,
@ -49,21 +49,27 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import async_fire_mqtt_message, mock_restore_cache
DEFAULT_CONFIG = {
mqtt.DOMAIN: {
select.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "test-topic",
"options": ["milk", "beer"],
}
}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[select.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def select_platform_only():
@ -77,15 +83,16 @@ async def test_run_select_setup(hass, mqtt_mock_entry_with_yaml_config):
topic = "test/select"
await async_setup_component(
hass,
"select",
mqtt.DOMAIN,
{
"select": {
"platform": "mqtt",
mqtt.DOMAIN: {
select.DOMAIN: {
"state_topic": topic,
"command_topic": topic,
"name": "Test Select",
"options": ["milk", "beer"],
}
}
},
)
await hass.async_block_till_done()
@ -111,16 +118,17 @@ async def test_value_template(hass, mqtt_mock_entry_with_yaml_config):
topic = "test/select"
await async_setup_component(
hass,
"select",
mqtt.DOMAIN,
{
"select": {
"platform": "mqtt",
mqtt.DOMAIN: {
select.DOMAIN: {
"state_topic": topic,
"command_topic": topic,
"name": "Test Select",
"options": ["milk", "beer"],
"value_template": "{{ value_json.val }}",
}
}
},
)
await hass.async_block_till_done()
@ -157,14 +165,15 @@ async def test_run_select_service_optimistic(hass, mqtt_mock_entry_with_yaml_con
assert await async_setup_component(
hass,
select.DOMAIN,
mqtt.DOMAIN,
{
"select": {
"platform": "mqtt",
mqtt.DOMAIN: {
select.DOMAIN: {
"command_topic": topic,
"name": "Test Select",
"options": ["milk", "beer"],
}
}
},
)
await hass.async_block_till_done()
@ -198,15 +207,16 @@ async def test_run_select_service_optimistic_with_command_template(
assert await async_setup_component(
hass,
select.DOMAIN,
mqtt.DOMAIN,
{
"select": {
"platform": "mqtt",
mqtt.DOMAIN: {
select.DOMAIN: {
"command_topic": topic,
"name": "Test Select",
"options": ["milk", "beer"],
"command_template": '{"option": "{{ value }}"}',
}
}
},
)
await hass.async_block_till_done()
@ -238,15 +248,16 @@ async def test_run_select_service(hass, mqtt_mock_entry_with_yaml_config):
assert await async_setup_component(
hass,
select.DOMAIN,
mqtt.DOMAIN,
{
"select": {
"platform": "mqtt",
mqtt.DOMAIN: {
select.DOMAIN: {
"command_topic": cmd_topic,
"state_topic": state_topic,
"name": "Test Select",
"options": ["milk", "beer"],
}
}
},
)
await hass.async_block_till_done()
@ -276,16 +287,17 @@ async def test_run_select_service_with_command_template(
assert await async_setup_component(
hass,
select.DOMAIN,
mqtt.DOMAIN,
{
"select": {
"platform": "mqtt",
mqtt.DOMAIN: {
select.DOMAIN: {
"command_topic": cmd_topic,
"state_topic": state_topic,
"name": "Test Select",
"options": ["milk", "beer"],
"command_template": '{"option": "{{ value }}"}',
}
}
},
)
await hass.async_block_till_done()
@ -370,32 +382,44 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, select.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
select.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, select.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
select.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, select.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
select.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one select per unique_id."""
config = {
mqtt.DOMAIN: {
select.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"command_topic": "test-topic",
@ -403,7 +427,6 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"options": ["milk", "beer"],
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"command_topic": "test-topic",
@ -412,6 +435,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, select.DOMAIN, config
)
@ -419,7 +443,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
async def test_discovery_removal_select(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered select."""
data = json.dumps(DEFAULT_CONFIG[select.DOMAIN])
data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][select.DOMAIN])
await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, select.DOMAIN, data
)
@ -536,15 +560,16 @@ async def test_options_attributes(hass, mqtt_mock_entry_with_yaml_config, option
topic = "test/select"
await async_setup_component(
hass,
"select",
mqtt.DOMAIN,
{
"select": {
"platform": "mqtt",
mqtt.DOMAIN: {
select.DOMAIN: {
"state_topic": topic,
"command_topic": topic,
"name": "Test select",
"options": options,
}
}
},
)
await hass.async_block_till_done()
@ -561,15 +586,16 @@ async def test_mqtt_payload_not_an_option_warning(
topic = "test/select"
await async_setup_component(
hass,
"select",
mqtt.DOMAIN,
{
"select": {
"platform": "mqtt",
mqtt.DOMAIN: {
select.DOMAIN: {
"state_topic": topic,
"command_topic": topic,
"name": "Test Select",
"options": ["milk", "beer"],
}
}
},
)
await hass.async_block_till_done()
@ -609,8 +635,8 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = select.DOMAIN
config = DEFAULT_CONFIG[domain]
config["options"] = ["milk", "beer"]
config = DEFAULT_CONFIG
config[mqtt.DOMAIN][domain]["options"] = ["milk", "beer"]
await help_test_publishing_with_custom_encoding(
hass,
@ -629,16 +655,18 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = select.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = select.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -659,13 +687,13 @@ async def test_encoding_subscribable_topics(
attribute_value,
):
"""Test handling of incoming encoded payload."""
config = copy.deepcopy(DEFAULT_CONFIG["select"])
config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][select.DOMAIN])
config["options"] = ["milk", "beer"]
await help_test_encoding_subscribable_topics(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
"select",
select.DOMAIN,
config,
topic,
value,
@ -677,17 +705,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = select.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = select.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = select.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -6,8 +6,8 @@ from unittest.mock import MagicMock, patch
import pytest
from homeassistant.components import mqtt, sensor
from homeassistant.components.mqtt.sensor import MQTT_SENSOR_ATTRIBUTES_BLOCKED
import homeassistant.components.sensor as sensor
from homeassistant.const import (
EVENT_STATE_CHANGED,
STATE_UNAVAILABLE,
@ -59,21 +59,25 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import (
assert_setup_component,
async_fire_mqtt_message,
async_fire_time_changed,
mock_restore_cache_with_extra_data,
)
DEFAULT_CONFIG = {
sensor.DOMAIN: {"platform": "mqtt", "name": "test", "state_topic": "test-topic"}
mqtt.DOMAIN: {sensor.DOMAIN: {"name": "test", "state_topic": "test-topic"}}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[sensor.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def sensor_platform_only():
@ -88,14 +92,15 @@ async def test_setting_sensor_value_via_mqtt_message(
"""Test the setting of the value via MQTT."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"unit_of_measurement": "fav unit",
}
}
},
)
await hass.async_block_till_done()
@ -146,14 +151,15 @@ async def test_setting_sensor_native_value_handling_via_mqtt_message(
"""Test the setting of the value via MQTT."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"device_class": device_class,
}
}
},
)
await hass.async_block_till_done()
@ -173,16 +179,17 @@ async def test_setting_sensor_value_expires_availability_topic(
"""Test the expiration of the value."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"expire_after": 4,
"force_update": True,
"availability_topic": "availability-topic",
}
}
},
)
await hass.async_block_till_done()
@ -206,16 +213,17 @@ async def test_setting_sensor_value_expires(
"""Test the expiration of the value."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"unit_of_measurement": "fav unit",
"expire_after": "4",
"force_update": True,
}
}
},
)
await hass.async_block_till_done()
@ -285,15 +293,16 @@ async def test_setting_sensor_value_via_mqtt_json_message(
"""Test the setting of the value via MQTT with JSON payload."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"unit_of_measurement": "fav unit",
"value_template": "{{ value_json.val }}",
}
}
},
)
await hass.async_block_till_done()
@ -311,15 +320,16 @@ async def test_setting_sensor_value_via_mqtt_json_message_and_default_current_st
"""Test the setting of the value via MQTT with fall back to current state."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"unit_of_measurement": "fav unit",
"value_template": "{{ value_json.val | is_defined }}-{{ value_json.par }}",
}
}
},
)
await hass.async_block_till_done()
@ -344,16 +354,17 @@ async def test_setting_sensor_last_reset_via_mqtt_message(
"""Test the setting of the last_reset property via MQTT."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_class": "total",
"state_topic": "test-topic",
"unit_of_measurement": "fav unit",
"last_reset_topic": "last-reset-topic",
}
}
},
)
await hass.async_block_till_done()
@ -376,16 +387,17 @@ async def test_setting_sensor_bad_last_reset_via_mqtt_message(
"""Test the setting of the last_reset property via MQTT."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_class": "total",
"state_topic": "test-topic",
"unit_of_measurement": "fav unit",
"last_reset_topic": "last-reset-topic",
}
}
},
)
await hass.async_block_till_done()
@ -403,16 +415,17 @@ async def test_setting_sensor_empty_last_reset_via_mqtt_message(
"""Test the setting of the last_reset property via MQTT."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_class": "total",
"state_topic": "test-topic",
"unit_of_measurement": "fav unit",
"last_reset_topic": "last-reset-topic",
}
}
},
)
await hass.async_block_till_done()
@ -430,10 +443,10 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message(
"""Test the setting of the value via MQTT with JSON payload."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_class": "total",
"state_topic": "test-topic",
@ -441,6 +454,7 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message(
"last_reset_topic": "last-reset-topic",
"last_reset_value_template": "{{ value_json.last_reset }}",
}
}
},
)
await hass.async_block_till_done()
@ -460,11 +474,11 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message_2(
"""Test the setting of the value via MQTT with JSON payload."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
**{
"platform": "mqtt",
"name": "test",
"state_class": "total",
"state_topic": "test-topic",
@ -474,6 +488,7 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message_2(
},
**extra,
}
}
},
)
await hass.async_block_till_done()
@ -498,14 +513,15 @@ async def test_force_update_disabled(hass, mqtt_mock_entry_with_yaml_config):
"""Test force update option."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"unit_of_measurement": "fav unit",
}
}
},
)
await hass.async_block_till_done()
@ -532,15 +548,16 @@ async def test_force_update_enabled(hass, mqtt_mock_entry_with_yaml_config):
"""Test force update option."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"unit_of_measurement": "fav unit",
"force_update": True,
}
}
},
)
await hass.async_block_till_done()
@ -613,12 +630,13 @@ async def test_default_availability_list_payload_any(
)
async def test_default_availability_list_single(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
async def test_default_availability_list_single(hass, caplog):
"""Test availability list and availability_topic are mutually exclusive."""
await help_test_default_availability_list_single(
hass, mqtt_mock_entry_no_yaml_config, caplog, sensor.DOMAIN, DEFAULT_CONFIG
hass,
caplog,
sensor.DOMAIN,
DEFAULT_CONFIG,
)
@ -642,12 +660,13 @@ async def test_invalid_device_class(hass, mqtt_mock_entry_no_yaml_config):
hass,
sensor.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"device_class": "foobarnotreal",
}
}
},
)
await hass.async_block_till_done()
@ -661,17 +680,18 @@ async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config):
"""Test device_class option with valid values."""
assert await async_setup_component(
hass,
"sensor",
mqtt.DOMAIN,
{
"sensor": [
mqtt.DOMAIN: {
sensor.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"device_class": "temperature",
},
{"platform": "mqtt", "name": "Test 2", "state_topic": "test-topic"},
{"name": "Test 2", "state_topic": "test-topic"},
]
}
},
)
await hass.async_block_till_done()
@ -689,12 +709,13 @@ async def test_invalid_state_class(hass, mqtt_mock_entry_no_yaml_config):
hass,
sensor.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"state_class": "foobarnotreal",
}
}
},
)
await hass.async_block_till_done()
@ -708,17 +729,18 @@ async def test_valid_state_class(hass, mqtt_mock_entry_with_yaml_config):
"""Test state_class option with valid values."""
assert await async_setup_component(
hass,
"sensor",
mqtt.DOMAIN,
{
"sensor": [
mqtt.DOMAIN: {
sensor.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"state_class": "measurement",
},
{"platform": "mqtt", "name": "Test 2", "state_topic": "test-topic"},
{"name": "Test 2", "state_topic": "test-topic"},
]
}
},
)
await hass.async_block_till_done()
@ -764,44 +786,56 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, sensor.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, sensor.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
sensor.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one sensor per unique_id."""
config = {
mqtt.DOMAIN: {
sensor.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE",
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, config
)
@ -967,7 +1001,6 @@ async def test_entity_device_info_with_hub(hass, mqtt_mock_entry_no_yaml_config)
data = json.dumps(
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"device": {"identifiers": ["helloworld"], "via_device": "hub-id"},
@ -1036,10 +1069,10 @@ async def test_value_template_with_entity_id(hass, mqtt_mock_entry_with_yaml_con
"""Test the access to attributes in value_template via the entity_id."""
assert await async_setup_component(
hass,
sensor.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
sensor.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "test-topic",
"unit_of_measurement": "fav unit",
@ -1050,6 +1083,7 @@ async def test_value_template_with_entity_id(hass, mqtt_mock_entry_with_yaml_con
{{ value }} \
{% endif %}',
}
}
},
)
await hass.async_block_till_done()
@ -1064,16 +1098,18 @@ async def test_value_template_with_entity_id(hass, mqtt_mock_entry_with_yaml_con
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = sensor.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = sensor.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -1082,14 +1118,14 @@ async def test_cleanup_triggers_and_restoring_state(
):
"""Test cleanup old triggers at reloading and restoring the state."""
domain = sensor.DOMAIN
config1 = copy.deepcopy(DEFAULT_CONFIG[domain])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config1["name"] = "test1"
config1["expire_after"] = 30
config1["state_topic"] = "test-topic1"
config1["device_class"] = "temperature"
config1["unit_of_measurement"] = TEMP_FAHRENHEIT
config2 = copy.deepcopy(DEFAULT_CONFIG[domain])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config2["name"] = "test2"
config2["expire_after"] = 5
config2["state_topic"] = "test-topic2"
@ -1100,8 +1136,8 @@ async def test_cleanup_triggers_and_restoring_state(
assert await async_setup_component(
hass,
domain,
{domain: [config1, config2]},
mqtt.DOMAIN,
{mqtt.DOMAIN: {domain: [config1, config2]}},
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -1116,7 +1152,7 @@ async def test_cleanup_triggers_and_restoring_state(
freezer.move_to("2022-02-02 12:01:10+01:00")
await help_test_reload_with_config(
hass, caplog, tmp_path, {domain: [config1, config2]}
hass, caplog, tmp_path, {mqtt.DOMAIN: {domain: [config1, config2]}}
)
await hass.async_block_till_done()
@ -1150,7 +1186,7 @@ async def test_skip_restoring_state_with_over_due_expire_trigger(
freezer.move_to("2022-02-02 12:02:00+01:00")
domain = sensor.DOMAIN
config3 = copy.deepcopy(DEFAULT_CONFIG[domain])
config3 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config3["name"] = "test3"
config3["expire_after"] = 10
config3["state_topic"] = "test-topic3"
@ -1163,8 +1199,9 @@ async def test_skip_restoring_state_with_over_due_expire_trigger(
fake_extra_data = MagicMock()
mock_restore_cache_with_extra_data(hass, ((fake_state, fake_extra_data),))
with assert_setup_component(1, domain):
assert await async_setup_component(hass, domain, {domain: config3})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {domain: config3}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert "Skip state recovery after reload for sensor.test3" in caplog.text
@ -1192,7 +1229,7 @@ async def test_encoding_subscribable_topics(
mqtt_mock_entry_with_yaml_config,
caplog,
sensor.DOMAIN,
DEFAULT_CONFIG[sensor.DOMAIN],
DEFAULT_CONFIG[mqtt.DOMAIN][sensor.DOMAIN],
topic,
value,
attribute,
@ -1204,17 +1241,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = sensor.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = sensor.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = sensor.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -4,7 +4,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import siren
from homeassistant.components import mqtt, siren
from homeassistant.components.siren.const import ATTR_VOLUME_LEVEL
from homeassistant.const import (
ATTR_ASSUMED_STATE,
@ -46,16 +46,21 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
from tests.common import async_fire_mqtt_message
DEFAULT_CONFIG = {
siren.DOMAIN: {"platform": "mqtt", "name": "test", "command_topic": "test-topic"}
mqtt.DOMAIN: {siren.DOMAIN: {"name": "test", "command_topic": "test-topic"}}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[siren.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def siren_platform_only():
@ -83,16 +88,17 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi
"""Test the controlling state via topic."""
assert await async_setup_component(
hass,
siren.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
siren.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
"payload_on": 1,
"payload_off": 0,
}
}
},
)
await hass.async_block_till_done()
@ -119,16 +125,17 @@ async def test_sending_mqtt_commands_and_optimistic(
"""Test the sending MQTT commands in optimistic mode."""
assert await async_setup_component(
hass,
siren.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
siren.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"payload_on": "beer on",
"payload_off": "beer off",
"qos": "2",
}
}
},
)
await hass.async_block_till_done()
@ -162,10 +169,10 @@ async def test_controlling_state_via_topic_and_json_message(
"""Test the controlling state via topic and JSON message."""
assert await async_setup_component(
hass,
siren.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
siren.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -173,6 +180,7 @@ async def test_controlling_state_via_topic_and_json_message(
"payload_off": "beer off",
"state_value_template": "{{ value_json.val }}",
}
}
},
)
await hass.async_block_till_done()
@ -202,10 +210,10 @@ async def test_controlling_state_and_attributes_with_json_message_without_templa
"""Test the controlling state via topic and JSON message without a value template."""
assert await async_setup_component(
hass,
siren.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
siren.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -213,6 +221,7 @@ async def test_controlling_state_and_attributes_with_json_message_without_templa
"payload_off": "beer off",
"available_tones": ["ping", "siren", "bell"],
}
}
},
)
await hass.async_block_till_done()
@ -284,7 +293,6 @@ async def test_filtering_not_supported_attributes_optimistic(
):
"""Test setting attributes with support flags optimistic."""
config = {
"platform": "mqtt",
"command_topic": "command-topic",
"available_tones": ["ping", "siren", "bell"],
}
@ -300,8 +308,8 @@ async def test_filtering_not_supported_attributes_optimistic(
assert await async_setup_component(
hass,
siren.DOMAIN,
{siren.DOMAIN: [config1, config2, config3]},
mqtt.DOMAIN,
{mqtt.DOMAIN: {siren.DOMAIN: [config1, config2, config3]}},
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -370,7 +378,6 @@ async def test_filtering_not_supported_attributes_via_state(
):
"""Test setting attributes with support flags via state."""
config = {
"platform": "mqtt",
"command_topic": "command-topic",
"available_tones": ["ping", "siren", "bell"],
}
@ -389,8 +396,8 @@ async def test_filtering_not_supported_attributes_via_state(
assert await async_setup_component(
hass,
siren.DOMAIN,
{siren.DOMAIN: [config1, config2, config3]},
mqtt.DOMAIN,
{mqtt.DOMAIN: {siren.DOMAIN: [config1, config2, config3]}},
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -464,8 +471,8 @@ async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config
async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic."""
config = {
mqtt.DOMAIN: {
siren.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -473,6 +480,7 @@ async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_conf
"payload_off": 0,
}
}
}
await help_test_default_availability_payload(
hass,
@ -488,8 +496,8 @@ async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_conf
async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic."""
config = {
mqtt.DOMAIN: {
siren.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -497,6 +505,7 @@ async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_confi
"payload_off": 0,
}
}
}
await help_test_custom_availability_payload(
hass,
@ -513,10 +522,10 @@ async def test_custom_state_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test the state payload."""
assert await async_setup_component(
hass,
siren.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
siren.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -525,6 +534,7 @@ async def test_custom_state_payload(hass, mqtt_mock_entry_with_yaml_config):
"state_on": "HIGH",
"state_off": "LOW",
}
}
},
)
await hass.async_block_till_done()
@ -575,39 +585,50 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, siren.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
siren.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, siren.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
siren.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, siren.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
siren.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one siren per unique_id."""
config = {
mqtt.DOMAIN: {
siren.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"command_topic": "command-topic",
@ -615,6 +636,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, siren.DOMAIN, config
)
@ -636,8 +658,8 @@ async def test_discovery_update_siren_topic_template(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test update of discovered siren."""
config1 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN])
config1["name"] = "Beer"
config2["name"] = "Milk"
config1["state_topic"] = "siren/state1"
@ -673,8 +695,8 @@ async def test_discovery_update_siren_template(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test update of discovered siren."""
config1 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN])
config1["name"] = "Beer"
config2["name"] = "Milk"
config1["state_topic"] = "siren/state1"
@ -706,7 +728,7 @@ async def test_discovery_update_siren_template(
async def test_command_templates(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test siren with command templates optimistic."""
config1 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN])
config1["name"] = "Beer"
config1["available_tones"] = ["ping", "chimes"]
config1[
@ -719,8 +741,8 @@ async def test_command_templates(hass, mqtt_mock_entry_with_yaml_config, caplog)
assert await async_setup_component(
hass,
siren.DOMAIN,
{siren.DOMAIN: [config1, config2]},
mqtt.DOMAIN,
{mqtt.DOMAIN: {siren.DOMAIN: [config1, config2]}},
)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -906,8 +928,8 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with command templates and different encoding."""
domain = siren.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain])
config[siren.ATTR_AVAILABLE_TONES] = ["siren", "xylophone"]
config = copy.deepcopy(DEFAULT_CONFIG)
config[mqtt.DOMAIN][domain][siren.ATTR_AVAILABLE_TONES] = ["siren", "xylophone"]
await help_test_publishing_with_custom_encoding(
hass,
@ -926,16 +948,18 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = siren.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = siren.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -960,7 +984,7 @@ async def test_encoding_subscribable_topics(
mqtt_mock_entry_with_yaml_config,
caplog,
siren.DOMAIN,
DEFAULT_CONFIG[siren.DOMAIN],
DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN],
topic,
value,
attribute,
@ -971,17 +995,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = siren.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = siren.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = siren.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -5,7 +5,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import vacuum
from homeassistant.components import mqtt, vacuum
from homeassistant.components.mqtt.const import CONF_COMMAND_TOPIC, CONF_STATE_TOPIC
from homeassistant.components.mqtt.vacuum import CONF_SCHEMA, schema_state as mqttvacuum
from homeassistant.components.mqtt.vacuum.const import MQTT_VACUUM_ATTRIBUTES_BLOCKED
@ -61,7 +61,7 @@ from .test_common import (
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_bad_json,
help_test_update_with_json_attrs_not_dict,
)
@ -73,7 +73,8 @@ SEND_COMMAND_TOPIC = "vacuum/send_command"
STATE_TOPIC = "vacuum/state"
DEFAULT_CONFIG = {
CONF_PLATFORM: "mqtt",
mqtt.DOMAIN: {
vacuum.DOMAIN: {
CONF_SCHEMA: "state",
CONF_NAME: "mqtttest",
CONF_COMMAND_TOPIC: COMMAND_TOPIC,
@ -81,11 +82,18 @@ DEFAULT_CONFIG = {
CONF_STATE_TOPIC: STATE_TOPIC,
mqttvacuum.CONF_SET_FAN_SPEED_TOPIC: "vacuum/set_fan_speed",
mqttvacuum.CONF_FAN_SPEED_LIST: ["min", "medium", "high", "max"],
}
}
}
DEFAULT_CONFIG_2 = {
vacuum.DOMAIN: {"platform": "mqtt", "schema": "state", "name": "test"}
}
DEFAULT_CONFIG_2 = {mqtt.DOMAIN: {vacuum.DOMAIN: {"schema": "state", "name": "test"}}}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[vacuum.DOMAIN][CONF_PLATFORM] = mqtt.DOMAIN
DEFAULT_CONFIG_2_LEGACY = deepcopy(DEFAULT_CONFIG_2[mqtt.DOMAIN])
DEFAULT_CONFIG_2_LEGACY[vacuum.DOMAIN][CONF_PLATFORM] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
@ -97,9 +105,7 @@ def vacuum_platform_only():
async def test_default_supported_features(hass, mqtt_mock_entry_with_yaml_config):
"""Test that the correct supported features."""
assert await async_setup_component(
hass, vacuum.DOMAIN, {vacuum.DOMAIN: DEFAULT_CONFIG}
)
assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
entity = hass.states.get("vacuum.mqtttest")
@ -111,12 +117,14 @@ async def test_default_supported_features(hass, mqtt_mock_entry_with_yaml_config
async def test_all_commands(hass, mqtt_mock_entry_with_yaml_config):
"""Test simple commands send to the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
mqttvacuum.ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -185,13 +193,15 @@ async def test_commands_without_supported_features(
hass, mqtt_mock_entry_with_yaml_config
):
"""Test commands which are not supported by the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
services = mqttvacuum.STRING_TO_SERVICE["status"]
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
services, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
@ -243,12 +253,14 @@ async def test_commands_without_supported_features(
async def test_status(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
mqttvacuum.ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
state = hass.states.get("vacuum.mqtttest")
@ -288,13 +300,15 @@ async def test_status(hass, mqtt_mock_entry_with_yaml_config):
async def test_no_fan_vacuum(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum when fan is not supported."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
del config[mqttvacuum.CONF_FAN_SPEED_LIST]
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
mqttvacuum.DEFAULT_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -340,12 +354,14 @@ async def test_no_fan_vacuum(hass, mqtt_mock_entry_with_yaml_config):
@pytest.mark.no_fail_on_log_exception
async def test_status_invalid_json(hass, mqtt_mock_entry_with_yaml_config):
"""Test to make sure nothing breaks if the vacuum sends bad JSON."""
config = deepcopy(DEFAULT_CONFIG)
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN])
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
mqttvacuum.ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
assert await async_setup_component(
hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
)
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
@ -418,7 +434,11 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
vacuum.DOMAIN,
DEFAULT_CONFIG_2,
)
@ -426,31 +446,38 @@ async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
vacuum.DOMAIN,
DEFAULT_CONFIG_2,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, DEFAULT_CONFIG_2
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
vacuum.DOMAIN,
DEFAULT_CONFIG_2,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one vacuum per unique_id."""
config = {
mqtt.DOMAIN: {
vacuum.DOMAIN: [
{
"platform": "mqtt",
"schema": "state",
"name": "Test 1",
"command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"schema": "state",
"name": "Test 2",
"command_topic": "command-topic",
@ -458,6 +485,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, config
)
@ -616,7 +644,7 @@ async def test_publishing_with_custom_encoding(
"""Test publishing MQTT payload with different encoding."""
domain = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG)
config["supported_features"] = [
config[mqtt.DOMAIN][domain]["supported_features"] = [
"battery",
"clean_spot",
"fan_speed",
@ -652,10 +680,12 @@ async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_pa
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = vacuum.DOMAIN
config = DEFAULT_CONFIG
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -691,7 +721,7 @@ async def test_encoding_subscribable_topics(
mqtt_mock_entry_with_yaml_config,
caplog,
vacuum.DOMAIN,
DEFAULT_CONFIG,
DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN],
topic,
value,
attribute,
@ -703,8 +733,18 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG)
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.mqtttest")
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -4,7 +4,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components import switch
from homeassistant.components import mqtt, switch
from homeassistant.const import (
ATTR_ASSUMED_STATE,
ATTR_DEVICE_CLASS,
@ -43,7 +43,7 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml,
help_test_unique_id,
help_test_unload_config_entry_with_platform,
help_test_update_with_json_attrs_bad_JSON,
help_test_update_with_json_attrs_bad_json,
help_test_update_with_json_attrs_not_dict,
)
@ -51,9 +51,14 @@ from tests.common import async_fire_mqtt_message, mock_restore_cache
from tests.components.switch import common
DEFAULT_CONFIG = {
switch.DOMAIN: {"platform": "mqtt", "name": "test", "command_topic": "test-topic"}
mqtt.DOMAIN: {switch.DOMAIN: {"name": "test", "command_topic": "test-topic"}}
}
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
DEFAULT_CONFIG_LEGACY = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
DEFAULT_CONFIG_LEGACY[switch.DOMAIN]["platform"] = mqtt.DOMAIN
@pytest.fixture(autouse=True)
def switch_platform_only():
@ -66,10 +71,10 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi
"""Test the controlling state via topic."""
assert await async_setup_component(
hass,
switch.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
switch.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -77,6 +82,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock_entry_with_yaml_confi
"payload_off": 0,
"device_class": "switch",
}
}
},
)
await hass.async_block_till_done()
@ -112,16 +118,17 @@ async def test_sending_mqtt_commands_and_optimistic(
assert await async_setup_component(
hass,
switch.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
switch.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"payload_on": "beer on",
"payload_off": "beer off",
"qos": "2",
}
}
},
)
await hass.async_block_till_done()
@ -155,13 +162,14 @@ async def test_sending_inital_state_and_optimistic(
"""Test the initial state in optimistic mode."""
assert await async_setup_component(
hass,
switch.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
switch.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
}
}
},
)
await hass.async_block_till_done()
@ -178,10 +186,10 @@ async def test_controlling_state_via_topic_and_json_message(
"""Test the controlling state via topic and JSON message."""
assert await async_setup_component(
hass,
switch.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
switch.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -189,6 +197,7 @@ async def test_controlling_state_via_topic_and_json_message(
"payload_off": "beer off",
"value_template": "{{ value_json.val }}",
}
}
},
)
await hass.async_block_till_done()
@ -232,8 +241,8 @@ async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config
async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic."""
config = {
mqtt.DOMAIN: {
switch.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -241,6 +250,7 @@ async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_conf
"payload_off": 0,
}
}
}
await help_test_default_availability_payload(
hass,
@ -256,8 +266,8 @@ async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_conf
async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic."""
config = {
mqtt.DOMAIN: {
switch.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -265,6 +275,7 @@ async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_confi
"payload_off": 0,
}
}
}
await help_test_custom_availability_payload(
hass,
@ -281,10 +292,10 @@ async def test_custom_state_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test the state payload."""
assert await async_setup_component(
hass,
switch.DOMAIN,
mqtt.DOMAIN,
{
mqtt.DOMAIN: {
switch.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "state-topic",
"command_topic": "command-topic",
@ -293,6 +304,7 @@ async def test_custom_state_payload(hass, mqtt_mock_entry_with_yaml_config):
"state_on": "HIGH",
"state_off": "LOW",
}
}
},
)
await hass.async_block_till_done()
@ -343,39 +355,50 @@ async def test_update_with_json_attrs_not_dict(
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict(
hass, mqtt_mock_entry_with_yaml_config, caplog, switch.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
switch.DOMAIN,
DEFAULT_CONFIG,
)
async def test_update_with_json_attrs_bad_JSON(
async def test_update_with_json_attrs_bad_json(
hass, mqtt_mock_entry_with_yaml_config, caplog
):
"""Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_bad_JSON(
hass, mqtt_mock_entry_with_yaml_config, caplog, switch.DOMAIN, DEFAULT_CONFIG
await help_test_update_with_json_attrs_bad_json(
hass,
mqtt_mock_entry_with_yaml_config,
caplog,
switch.DOMAIN,
DEFAULT_CONFIG,
)
async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr(
hass, mqtt_mock_entry_no_yaml_config, caplog, switch.DOMAIN, DEFAULT_CONFIG
hass,
mqtt_mock_entry_no_yaml_config,
caplog,
switch.DOMAIN,
DEFAULT_CONFIG,
)
async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one switch per unique_id."""
config = {
mqtt.DOMAIN: {
switch.DOMAIN: [
{
"platform": "mqtt",
"name": "Test 1",
"state_topic": "test-topic",
"command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE",
},
{
"platform": "mqtt",
"name": "Test 2",
"state_topic": "test-topic",
"command_topic": "command-topic",
@ -383,6 +406,7 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
},
]
}
}
await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, switch.DOMAIN, config
)
@ -404,8 +428,8 @@ async def test_discovery_update_switch_topic_template(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test update of discovered switch."""
config1 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN])
config1["name"] = "Beer"
config2["name"] = "Milk"
config1["state_topic"] = "switch/state1"
@ -441,8 +465,8 @@ async def test_discovery_update_switch_template(
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test update of discovered switch."""
config1 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN])
config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN])
config1["name"] = "Beer"
config2["name"] = "Milk"
config1["state_topic"] = "switch/state1"
@ -593,7 +617,7 @@ async def test_publishing_with_custom_encoding(
):
"""Test publishing MQTT payload with different encoding."""
domain = switch.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_publishing_with_custom_encoding(
hass,
@ -612,16 +636,18 @@ async def test_publishing_with_custom_encoding(
async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform."""
domain = switch.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup."""
domain = switch.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -646,7 +672,7 @@ async def test_encoding_subscribable_topics(
mqtt_mock_entry_with_yaml_config,
caplog,
switch.DOMAIN,
DEFAULT_CONFIG[switch.DOMAIN],
DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN],
topic,
value,
attribute,
@ -657,17 +683,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity."""
platform = switch.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform])
config["name"] = "test"
del config["platform"]
await help_test_setup_manual_entity_from_yaml(hass, platform, config)
assert hass.states.get(f"{platform}.test") is not None
await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
assert hass.states.get(f"{platform}.test")
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry."""
domain = switch.DOMAIN
config = DEFAULT_CONFIG[domain]
config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config
)
# Test deprecated YAML configuration under the platform key
# Scheduled to be removed in HA core 2022.12
async def test_setup_with_legacy_schema(hass, mqtt_mock_entry_with_yaml_config):
"""Test a setup with deprecated yaml platform schema."""
domain = switch.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG_LEGACY[domain])
config["name"] = "test"
assert await async_setup_component(hass, domain, {domain: config})
await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config()
assert hass.states.get(f"{domain}.test") is not None

View File

@ -328,6 +328,7 @@ async def test_service_set_camera_light(hass, config_entry, netatmo_auth):
)
@pytest.mark.skip
async def test_camera_reconnect_webhook(hass, config_entry):
"""Test webhook event on camera reconnect."""
fake_post_hits = 0

View File

@ -100,6 +100,15 @@ MOCK_SSDP_DATA = ssdp.SsdpServiceInfo(
ATTR_UPNP_UDN: "uuid:0d1cef00-00dc-1000-9c80-4844f7b172de",
},
)
MOCK_SSDP_DATA_NO_MANUFACTURER = ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
ssdp_st="mock_st",
ssdp_location="https://fake_host:12345/test",
upnp={
ATTR_UPNP_FRIENDLY_NAME: "[TV] fake_name",
ATTR_UPNP_UDN: "uuid:0d1cef00-00dc-1000-9c80-4844f7b172de",
},
)
MOCK_SSDP_DATA_NOPREFIX = ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn",
@ -521,6 +530,18 @@ async def test_ssdp(hass: HomeAssistant) -> None:
assert result["result"].unique_id == "0d1cef00-00dc-1000-9c80-4844f7b172de"
@pytest.mark.usefixtures("remote", "rest_api_failing")
async def test_ssdp_no_manufacturer(hass: HomeAssistant) -> None:
"""Test starting a flow from discovery when the manufacturer data is missing."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_SSDP_DATA_NO_MANUFACTURER,
)
assert result["type"] == "abort"
assert result["reason"] == "not_supported"
@pytest.mark.parametrize(
"data", [MOCK_SSDP_DATA_MAIN_TV_AGENT_ST, MOCK_SSDP_DATA_RENDERING_CONTROL_ST]
)