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

View File

@ -58,7 +58,7 @@ class AdapterDetails(TypedDict, total=False):
address: str address: str
sw_version: str sw_version: str
hw_version: str hw_version: str | None
passive_scan: bool 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} APPLE_START_BYTES_WANTED: Final = {APPLE_DEVICE_ID_START_BYTE, APPLE_HOMEKIT_START_BYTE}
RSSI_SWITCH_THRESHOLD = 6 RSSI_SWITCH_THRESHOLD = 6
NO_RSSI_VALUE = -1000
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -83,7 +84,7 @@ def _prefer_previous_adv(
STALE_ADVERTISEMENT_SECONDS, STALE_ADVERTISEMENT_SECONDS,
) )
return False 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 advertisement is RSSI_SWITCH_THRESHOLD more, the new one is preferred
if new.source != old.source: if new.source != old.source:
_LOGGER.debug( _LOGGER.debug(

View File

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

View File

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

View File

@ -2,7 +2,7 @@
"domain": "bmw_connected_drive", "domain": "bmw_connected_drive",
"name": "BMW Connected Drive", "name": "BMW Connected Drive",
"documentation": "https://www.home-assistant.io/integrations/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"], "codeowners": ["@gerard33", "@rikroe"],
"config_flow": true, "config_flow": true,
"iot_class": "cloud_polling", "iot_class": "cloud_polling",

View File

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

View File

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

View File

@ -92,7 +92,10 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
) )
async def async_turn_off(self, **kwargs: Any) -> None: 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: try:
async with self._client: async with self._client:
await self._client.valve.close() await self._client.valve.close()
@ -103,7 +106,10 @@ class ValveControllerSwitch(ValveControllerEntity, SwitchEntity):
self.async_write_ha_state() self.async_write_ha_state()
async def async_turn_on(self, **kwargs: Any) -> None: 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: try:
async with self._client: async with self._client:
await self._client.valve.open() await self._client.valve.open()

View File

@ -3,7 +3,7 @@
"name": "HomeKit Controller", "name": "HomeKit Controller",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/homekit_controller", "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."], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."],
"bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }],
"dependencies": ["bluetooth", "zeroconf"], "dependencies": ["bluetooth", "zeroconf"],

View File

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

View File

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

View File

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

View File

@ -42,7 +42,6 @@ from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttAvailability, MqttAvailability,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -102,9 +101,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT binary sensor through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _async_setup_entity, hass, async_add_entities, config_entry=config_entry
) )

View File

@ -25,7 +25,6 @@ from .const import (
from .mixins import ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -81,9 +80,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT button through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _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 ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -105,9 +104,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT camera through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _async_setup_entity, hass, async_add_entities, config_entry=config_entry
) )

View File

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

View File

@ -50,7 +50,6 @@ from .debug_info import log_messages
from .mixins import ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -350,9 +349,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT climate device through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _async_setup_entity, hass, async_add_entities, config_entry=config_entry
) )

View File

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

View File

@ -30,16 +30,8 @@ CONF_CLIENT_CERT = "client_cert"
CONF_TLS_INSECURE = "tls_insecure" CONF_TLS_INSECURE = "tls_insecure"
CONF_TLS_VERSION = "tls_version" CONF_TLS_VERSION = "tls_version"
CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup"
DATA_MQTT = "mqtt" 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" 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_PREFIX = "homeassistant"
DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status" DEFAULT_BIRTH_WILL_TOPIC = DEFAULT_PREFIX + "/status"

View File

@ -46,7 +46,6 @@ from .debug_info import log_messages
from .mixins import ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -242,9 +241,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT cover through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _async_setup_entity, hass, async_add_entities, config_entry=config_entry
) )

View File

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

View File

@ -43,7 +43,7 @@ def _async_get_diagnostics(
device: DeviceEntry | None = None, device: DeviceEntry | None = None,
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a config entry.""" """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) redacted_config = async_redact_data(mqtt_instance.conf, REDACT_CONFIG)

View File

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

View File

@ -50,7 +50,6 @@ from .debug_info import log_messages
from .mixins import ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -241,9 +240,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT fan through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _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 ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -187,9 +186,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT humidifier through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _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 homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from ..mixins import ( from ..mixins import (
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -111,9 +110,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT lights configured under the light platform key (deprecated).""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _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 ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -102,9 +101,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT lock through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _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 from abc import abstractmethod
import asyncio import asyncio
from collections.abc import Callable, Coroutine from collections.abc import Callable, Coroutine
from dataclasses import dataclass, field
from functools import partial from functools import partial
import logging import logging
from typing import Any, Protocol, cast, final from typing import TYPE_CHECKING, Any, Protocol, cast, final
import voluptuous as vol import voluptuous as vol
@ -38,7 +39,6 @@ from homeassistant.core import (
from homeassistant.helpers import ( from homeassistant.helpers import (
config_validation as cv, config_validation as cv,
device_registry as dr, device_registry as dr,
discovery,
entity_registry as er, entity_registry as er,
) )
from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED 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 homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import debug_info, subscription from . import debug_info, subscription
from .client import async_publish from .client import MQTT, Subscription, async_publish
from .const import ( from .const import (
ATTR_DISCOVERY_HASH, ATTR_DISCOVERY_HASH,
ATTR_DISCOVERY_PAYLOAD, ATTR_DISCOVERY_PAYLOAD,
@ -70,11 +70,6 @@ from .const import (
CONF_QOS, CONF_QOS,
CONF_TOPIC, CONF_TOPIC,
DATA_MQTT, 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_ENCODING,
DEFAULT_PAYLOAD_AVAILABLE, DEFAULT_PAYLOAD_AVAILABLE,
DEFAULT_PAYLOAD_NOT_AVAILABLE, DEFAULT_PAYLOAD_NOT_AVAILABLE,
@ -98,6 +93,9 @@ from .subscription import (
) )
from .util import mqtt_config_entry_enabled, valid_subscribe_topic from .util import mqtt_config_entry_enabled, valid_subscribe_topic
if TYPE_CHECKING:
from .device_trigger import Trigger
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
AVAILABILITY_ALL = "all" AVAILABILITY_ALL = "all"
@ -274,6 +272,27 @@ def warn_for_legacy_schema(domain: str) -> Callable:
return validator 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): class SetupEntity(Protocol):
"""Protocol type for async_setup_entities.""" """Protocol type for async_setup_entities."""
@ -288,29 +307,6 @@ class SetupEntity(Protocol):
"""Define setup_entities type.""" """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( async def async_get_platform_config_from_yaml(
hass: HomeAssistant, hass: HomeAssistant,
platform_domain: str, platform_domain: str,
@ -318,8 +314,9 @@ async def async_get_platform_config_from_yaml(
) -> list[ConfigType]: ) -> list[ConfigType]:
"""Return a list of validated configurations for the domain.""" """Return a list of validated configurations for the domain."""
mqtt_data: MqttData = hass.data[DATA_MQTT]
if config_yaml is None: if config_yaml is None:
config_yaml = hass.data.get(DATA_MQTT_CONFIG) config_yaml = mqtt_data.config
if not config_yaml: if not config_yaml:
return [] return []
if not (platform_configs := config_yaml.get(platform_domain)): if not (platform_configs := config_yaml.get(platform_domain)):
@ -331,9 +328,10 @@ async def async_setup_entry_helper(
hass: HomeAssistant, hass: HomeAssistant,
domain: str, domain: str,
async_setup: partial[Coroutine[HomeAssistant, str, None]], async_setup: partial[Coroutine[HomeAssistant, str, None]],
schema: vol.Schema, discovery_schema: vol.Schema,
) -> None: ) -> None:
"""Set up entity, automation or tag creation dynamically through MQTT discovery.""" """Set up entity, automation or tag creation dynamically through MQTT discovery."""
mqtt_data: MqttData = hass.data[DATA_MQTT]
async def async_discover(discovery_payload): async def async_discover(discovery_payload):
"""Discover and add an MQTT entity, automation or tag.""" """Discover and add an MQTT entity, automation or tag."""
@ -347,7 +345,7 @@ async def async_setup_entry_helper(
return return
discovery_data = discovery_payload.discovery_data discovery_data = discovery_payload.discovery_data
try: try:
config = schema(discovery_payload) config = discovery_schema(discovery_payload)
await async_setup(config, discovery_data=discovery_data) await async_setup(config, discovery_data=discovery_data)
except Exception: except Exception:
discovery_hash = discovery_data[ATTR_DISCOVERY_HASH] discovery_hash = discovery_data[ATTR_DISCOVERY_HASH]
@ -357,12 +355,37 @@ async def async_setup_entry_helper(
) )
raise raise
hass.data.setdefault(DATA_MQTT_RELOAD_DISPATCHERS, []).append( mqtt_data.reload_dispatchers.append(
async_dispatcher_connect( async_dispatcher_connect(
hass, MQTT_DISCOVERY_NEW.format(domain, "mqtt"), async_discover 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( async def async_setup_platform_helper(
hass: HomeAssistant, hass: HomeAssistant,
@ -372,7 +395,8 @@ async def async_setup_platform_helper(
async_setup_entities: SetupEntity, async_setup_entities: SetupEntity,
) -> None: ) -> None:
"""Help to set up the platform for manual configured MQTT entities.""" """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( _LOGGER.debug(
"MQTT integration is %s, skipping setup of manually configured MQTT items while unloading the config entry", "MQTT integration is %s, skipping setup of manually configured MQTT items while unloading the config entry",
platform_domain, platform_domain,
@ -597,7 +621,10 @@ class MqttAvailability(Entity):
@property @property
def available(self) -> bool: def available(self) -> bool:
"""Return if the device is available.""" """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 return False
if not self._avail_topics: if not self._avail_topics:
return True 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.""" """Get the discovery hash from the discovery data."""
return discovery_data[ATTR_DISCOVERY_HASH] return discovery_data[ATTR_DISCOVERY_HASH]
@ -817,9 +844,8 @@ class MqttDiscoveryUpdate(Entity):
self._removed_from_hass = False self._removed_from_hass = False
if discovery_data is None: if discovery_data is None:
return return
self._registry_hooks: dict[tuple, CALLBACK_TYPE] = hass.data[ mqtt_data: MqttData = hass.data[DATA_MQTT]
DATA_MQTT_DISCOVERY_REGISTRY_HOOKS self._registry_hooks = mqtt_data.discovery_registry_hooks
]
discovery_hash: tuple[str, str] = discovery_data[ATTR_DISCOVERY_HASH] discovery_hash: tuple[str, str] = discovery_data[ATTR_DISCOVERY_HASH]
if discovery_hash in self._registry_hooks: if discovery_hash in self._registry_hooks:
self._registry_hooks.pop(discovery_hash)() self._registry_hooks.pop(discovery_hash)()
@ -897,7 +923,7 @@ class MqttDiscoveryUpdate(Entity):
def add_to_platform_abort(self) -> None: def add_to_platform_abort(self) -> None:
"""Abort adding an entity to a platform.""" """Abort adding an entity to a platform."""
if self._discovery_data is not None: 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: if self.registry_entry is not None:
self._registry_hooks[ self._registry_hooks[
discovery_hash discovery_hash

View File

@ -44,7 +44,6 @@ from .debug_info import log_messages
from .mixins import ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -138,9 +137,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT number through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _async_setup_entity, hass, async_add_entities, config_entry=config_entry
) )

View File

@ -22,7 +22,6 @@ from .mixins import (
CONF_OBJECT_ID, CONF_OBJECT_ID,
MQTT_AVAILABILITY_SCHEMA, MQTT_AVAILABILITY_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -78,9 +77,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT scene through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _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 ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -93,9 +92,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT select through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _async_setup_entity, hass, async_add_entities, config_entry=config_entry
) )

View File

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

View File

@ -51,7 +51,6 @@ from .debug_info import log_messages
from .mixins import ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -142,9 +141,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT siren through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _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 ( from .mixins import (
MQTT_ENTITY_COMMON_SCHEMA, MQTT_ENTITY_COMMON_SCHEMA,
MqttEntity, MqttEntity,
async_discover_yaml_entities,
async_setup_entry_helper, async_setup_entry_helper,
async_setup_platform_helper, async_setup_platform_helper,
warn_for_legacy_schema, warn_for_legacy_schema,
@ -101,9 +100,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT switch through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _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.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from ..mixins import ( from ..mixins import async_setup_entry_helper, async_setup_platform_helper
async_discover_yaml_entities,
async_setup_entry_helper,
async_setup_platform_helper,
)
from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE
from .schema_legacy import ( from .schema_legacy import (
DISCOVERY_SCHEMA_LEGACY, DISCOVERY_SCHEMA_LEGACY,
@ -90,9 +86,6 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up MQTT vacuum through configuration.yaml and dynamically through MQTT discovery.""" """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( setup = functools.partial(
_async_setup_entity, hass, async_add_entities, config_entry=config_entry _async_setup_entity, hass, async_add_entities, config_entry=config_entry
) )

View File

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

View File

@ -2,9 +2,10 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from collections.abc import Awaitable, Callable
from dataclasses import dataclass from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from functools import partial from functools import partial, wraps
from typing import Any from typing import Any
from regenmaschine import Client from regenmaschine import Client
@ -22,7 +23,7 @@ from homeassistant.const import (
Platform, Platform,
) )
from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
from homeassistant.helpers import ( from homeassistant.helpers import (
aiohttp_client, aiohttp_client,
config_validation as cv, config_validation as cv,
@ -152,9 +153,9 @@ class RainMachineData:
@callback @callback
def async_get_controller_for_service_call( def async_get_entry_for_service_call(
hass: HomeAssistant, call: ServiceCall hass: HomeAssistant, call: ServiceCall
) -> Controller: ) -> ConfigEntry:
"""Get the controller related to a service call (by device ID).""" """Get the controller related to a service call (by device ID)."""
device_id = call.data[CONF_DEVICE_ID] device_id = call.data[CONF_DEVICE_ID]
device_registry = dr.async_get(hass) 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: if (entry := hass.config_entries.async_get_entry(entry_id)) is None:
continue continue
if entry.domain == DOMAIN: if entry.domain == DOMAIN:
data: RainMachineData = hass.data[DOMAIN][entry_id] return entry
return data.controller
raise ValueError(f"No controller for device ID: {device_id}") 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)) entry.async_on_unload(entry.add_update_listener(async_reload_entry))
async def async_pause_watering(call: ServiceCall) -> None: def call_with_controller(update_programs_and_zones: bool = True) -> Callable:
"""Pause watering for a set number of seconds.""" """Hydrate a service call with the appropriate controller."""
controller = async_get_controller_for_service_call(hass, call)
await controller.watering.pause_all(call.data[CONF_SECONDS]) 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) 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.""" """Push weather data to the device."""
controller = async_get_controller_for_service_call(hass, call)
await controller.parsers.post_data( await controller.parsers.post_data(
{ {
CONF_WEATHER: [ 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.""" """Restrict watering for a time period."""
controller = async_get_controller_for_service_call(hass, call)
duration = call.data[CONF_DURATION] duration = call.data[CONF_DURATION]
await controller.restrictions.set_universal( await controller.restrictions.set_universal(
{ {
@ -319,30 +348,28 @@ async def async_setup_entry( # noqa: C901
"rainDelayDuration": duration.total_seconds(), "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.""" """Stop all watering."""
controller = async_get_controller_for_service_call(hass, call)
await controller.watering.stop_all() 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.""" """Unpause watering."""
controller = async_get_controller_for_service_call(hass, call)
await controller.watering.unpause_all() 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.""" """Unrestrict watering."""
controller = async_get_controller_for_service_call(hass, call)
await controller.restrictions.set_universal( await controller.restrictions.set_universal(
{ {
"rainDelayStartTime": round(as_timestamp(utcnow())), "rainDelayStartTime": round(as_timestamp(utcnow())),
"rainDelayDuration": 0, "rainDelayDuration": 0,
}, },
) )
await async_update_programs_and_zones(hass, entry)
for service_name, schema, method in ( 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: if hostname := urlparse(discovery_info.ssdp_location or "").hostname:
self._host = 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() self._abort_if_manufacturer_is_not_samsung()
# Set defaults, in case they cannot be extracted from device_info # 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 .coordinator import SwitchbotDataUpdateCoordinator
from .entity import SwitchbotEntity from .entity import SwitchbotEntity
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 0
BINARY_SENSOR_TYPES: dict[str, BinarySensorEntityDescription] = { BINARY_SENSOR_TYPES: dict[str, BinarySensorEntityDescription] = {
"calibration": BinarySensorEntityDescription( "calibration": BinarySensorEntityDescription(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -91,6 +91,16 @@ class SynoApi:
self._with_surveillance_station = bool( self._with_surveillance_station = bool(
self.dsm.apis.get(SynoSurveillanceStation.CAMERA_API_KEY) 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( LOGGER.debug(
"State of Surveillance_station during setup of '%s': %s", "State of Surveillance_station during setup of '%s': %s",
self._entry.unique_id, self._entry.unique_id,

View File

@ -73,6 +73,8 @@ STATE_CODE_TO_STATE = {
17: STATE_CLEANING, # "Zoned cleaning" 17: STATE_CLEANING, # "Zoned cleaning"
18: STATE_CLEANING, # "Segment cleaning" 18: STATE_CLEANING, # "Segment cleaning"
22: STATE_DOCKED, # "Emptying the bin" on s7+ 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" 100: STATE_DOCKED, # "Charging complete"
101: STATE_ERROR, # "Device offline" 101: STATE_ERROR, # "Device offline"
} }

View File

@ -7,7 +7,7 @@ from .backports.enum import StrEnum
MAJOR_VERSION: Final = 2022 MAJOR_VERSION: Final = 2022
MINOR_VERSION: Final = 9 MINOR_VERSION: Final = 9
PATCH_VERSION: Final = "5" PATCH_VERSION: Final = "6"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) 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 certifi>=2021.5.30
ciso8601==2.2.0 ciso8601==2.2.0
cryptography==37.0.4 cryptography==37.0.4
dbus-fast==1.4.0 dbus-fast==1.5.1
fnvhash==0.1.0 fnvhash==0.1.0
hass-nabucasa==0.55.0 hass-nabucasa==0.55.0
home-assistant-bluetooth==1.3.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( def catch_log_exception(
func: Callable[..., Coroutine[Any, Any, Any]], format_err: Callable[..., Any] func: Callable[..., Coroutine[Any, Any, Any]], format_err: Callable[..., Any]
) -> Callable[..., Coroutine[Any, Any, None]]: ) -> Callable[..., Coroutine[Any, Any, None]]:
"""Overload for Callables that return a Coroutine.""" """Overload for Coroutine that returns a Coroutine."""
@overload @overload
def catch_log_exception( def catch_log_exception(
func: Callable[..., Any], format_err: Callable[..., Any] func: Callable[..., Any], format_err: Callable[..., Any]
) -> Callable[..., None | Coroutine[Any, Any, None]]: ) -> Callable[..., None] | Callable[..., Coroutine[Any, Any, None]]:
"""Overload for Callables that return Any.""" """Overload for a callback that returns a callback."""
def catch_log_exception( def catch_log_exception(
func: Callable[..., Any], format_err: Callable[..., Any] func: Callable[..., Any], format_err: Callable[..., Any]
) -> Callable[..., None | Coroutine[Any, Any, None]]: ) -> Callable[..., None] | Callable[..., Coroutine[Any, Any, None]]:
"""Decorate a callback to catch and log exceptions.""" """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 for partials to properly determine if coroutine function
check_func = func check_func = func
while isinstance(check_func, partial): while isinstance(check_func, partial):
check_func = check_func.func 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): if asyncio.iscoroutinefunction(check_func):
async_func = cast(Callable[..., Coroutine[Any, Any, None]], func) async_func = cast(Callable[..., Coroutine[Any, Any, None]], func)

View File

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

View File

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

View File

@ -33,7 +33,7 @@ PyRMVtransport==0.3.3
PySocks==1.7.1 PySocks==1.7.1
# homeassistant.components.switchbot # homeassistant.components.switchbot
PySwitchbot==0.19.9 PySwitchbot==0.19.10
# homeassistant.components.transport_nsw # homeassistant.components.transport_nsw
PyTransportNSW==0.1.1 PyTransportNSW==0.1.1
@ -155,7 +155,7 @@ aioguardian==2022.07.0
aioharmony==0.2.9 aioharmony==0.2.9
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit==1.5.9 aiohomekit==1.5.12
# homeassistant.components.emulated_hue # homeassistant.components.emulated_hue
# homeassistant.components.http # homeassistant.components.http
@ -326,7 +326,7 @@ beautifulsoup4==4.11.1
bellows==0.33.1 bellows==0.33.1
# homeassistant.components.bmw_connected_drive # homeassistant.components.bmw_connected_drive
bimmer_connected==0.10.2 bimmer_connected==0.10.4
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bleak-retry-connector==1.17.1 bleak-retry-connector==1.17.1
@ -415,7 +415,7 @@ datadog==0.15.0
datapoint==0.9.8 datapoint==0.9.8
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
dbus-fast==1.4.0 dbus-fast==1.5.1
# homeassistant.components.debugpy # homeassistant.components.debugpy
debugpy==1.6.3 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): if isinstance(payload, str):
payload = payload.encode("utf-8") payload = payload.encode("utf-8")
msg = ReceiveMessage(topic, payload, qos, retain) 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) 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): async def test_switching_adapters_based_on_stale(hass, enable_bluetooth):
"""Test switching adapters based on the previous advertisement being stale.""" """Test switching adapters based on the previous advertisement being stale."""

View File

@ -1,6 +1,7 @@
"""Test the Bond config flow.""" """Test the Bond config flow."""
from __future__ import annotations from __future__ import annotations
import asyncio
from http import HTTPStatus from http import HTTPStatus
from typing import Any from typing import Any
from unittest.mock import MagicMock, Mock, patch 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 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): async def test_zeroconf_form_with_token_available(hass: core.HomeAssistant):
"""Test we get the discovery form when we can get the token.""" """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": { "payload": {
"devices": { "devices": {
"light.non_existing": {"online": False}, "light.non_existing": {"online": False},
"light.demo_light": {"on": False, "online": True, "brightness": 0}, "light.demo_light": {"on": False, "online": True},
"light.another_light": { "light.another_light": {
"on": True, "on": True,
"online": True, "online": True,
@ -725,7 +725,6 @@ async def test_execute_times_out(hass, report_state, on, brightness, value):
"states": { "states": {
"on": on, "on": on,
"online": True, "online": True,
"brightness": brightness,
}, },
}, },
{ {

View File

@ -5,7 +5,7 @@ from unittest.mock import patch
import pytest 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 ( from homeassistant.components.mqtt.alarm_control_panel import (
MQTT_ALARM_ATTRIBUTES_BLOCKED, MQTT_ALARM_ATTRIBUTES_BLOCKED,
) )
@ -61,58 +61,67 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, 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 from tests.components.alarm_control_panel import common
CODE_NUMBER = "1234" CODE_NUMBER = "1234"
CODE_TEXT = "HELLO_CODE" CODE_TEXT = "HELLO_CODE"
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: { alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "alarm/state", "state_topic": "alarm/state",
"command_topic": "alarm/command", "command_topic": "alarm/command",
} }
}
} }
DEFAULT_CONFIG_CODE = { DEFAULT_CONFIG_CODE = {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: { alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "alarm/state", "state_topic": "alarm/state",
"command_topic": "alarm/command", "command_topic": "alarm/command",
"code": "0123", "code": "0123",
"code_arm_required": True, "code_arm_required": True,
} }
}
} }
DEFAULT_CONFIG_REMOTE_CODE = { DEFAULT_CONFIG_REMOTE_CODE = {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: { alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "alarm/state", "state_topic": "alarm/state",
"command_topic": "alarm/command", "command_topic": "alarm/command",
"code": "REMOTE_CODE", "code": "REMOTE_CODE",
"code_arm_required": True, "code_arm_required": True,
} }
}
} }
DEFAULT_CONFIG_REMOTE_CODE_TEXT = { DEFAULT_CONFIG_REMOTE_CODE_TEXT = {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: { alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "alarm/state", "state_topic": "alarm/state",
"command_topic": "alarm/command", "command_topic": "alarm/command",
"code": "REMOTE_CODE_TEXT", "code": "REMOTE_CODE_TEXT",
"code_arm_required": True, "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) @pytest.fixture(autouse=True)
def alarm_control_panel_platform_only(): def alarm_control_panel_platform_only():
@ -123,47 +132,62 @@ def alarm_control_panel_platform_only():
yield yield
async def test_fail_setup_without_state_topic(hass, mqtt_mock_entry_no_yaml_config): @pytest.mark.parametrize(
"""Test for failing with no state topic.""" "config,valid",
with assert_setup_component(0, alarm_control_panel.DOMAIN) as config: [
assert await async_setup_component( (
hass,
alarm_control_panel.DOMAIN,
{ {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: { alarm_control_panel.DOMAIN: {
"platform": "mqtt", "name": "test",
"command_topic": "alarm/command", "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): async def test_update_state_via_state_topic(hass, mqtt_mock_entry_with_yaml_config):
"""Test updating with via state topic.""" """Test updating with via state topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
alarm_control_panel.DOMAIN, mqtt.DOMAIN,
DEFAULT_CONFIG, DEFAULT_CONFIG,
) )
await hass.async_block_till_done() 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.""" """Test ignoring updates via state topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
alarm_control_panel.DOMAIN, mqtt.DOMAIN,
DEFAULT_CONFIG, DEFAULT_CONFIG,
) )
await hass.async_block_till_done() 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.""" """Test publishing of MQTT messages when no code is configured."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
alarm_control_panel.DOMAIN, mqtt.DOMAIN,
DEFAULT_CONFIG, DEFAULT_CONFIG,
) )
await hass.async_block_till_done() 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.""" """Test publishing of MQTT messages when code is configured."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
alarm_control_panel.DOMAIN, mqtt.DOMAIN,
DEFAULT_CONFIG_CODE, DEFAULT_CONFIG_CODE,
) )
await hass.async_block_till_done() 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.""" """Test publishing of MQTT messages when remode code is configured."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
alarm_control_panel.DOMAIN, mqtt.DOMAIN,
DEFAULT_CONFIG_REMOTE_CODE, DEFAULT_CONFIG_REMOTE_CODE,
) )
await hass.async_block_till_done() 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.""" """Test publishing of MQTT messages when remote text code is configured."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
alarm_control_panel.DOMAIN, mqtt.DOMAIN,
DEFAULT_CONFIG_REMOTE_CODE_TEXT, DEFAULT_CONFIG_REMOTE_CODE_TEXT,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -405,10 +429,10 @@ async def test_publish_mqtt_with_code_required_false(
code_trigger_required = False code_trigger_required = False
""" """
config = copy.deepcopy(DEFAULT_CONFIG_CODE) 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( assert await async_setup_component(
hass, hass,
alarm_control_panel.DOMAIN, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() 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 When command_template set to output json
""" """
config = copy.deepcopy(DEFAULT_CONFIG_CODE) config = copy.deepcopy(DEFAULT_CONFIG_CODE)
config[alarm_control_panel.DOMAIN]["code"] = "0123" config[mqtt.DOMAIN][alarm_control_panel.DOMAIN]["code"] = "0123"
config[alarm_control_panel.DOMAIN][ config[mqtt.DOMAIN][alarm_control_panel.DOMAIN][
"command_template" "command_template"
] = '{"action":"{{ action }}","code":"{{ code }}"}' ] = '{"action":"{{ action }}","code":"{{ code }}"}'
assert await async_setup_component( assert await async_setup_component(
hass, hass,
alarm_control_panel.DOMAIN, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() 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.""" """Test updating with template_value via state topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
alarm_control_panel.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: { alarm_control_panel.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test-topic", "command_topic": "test-topic",
"state_topic": "test-topic", "state_topic": "test-topic",
@ -491,6 +515,7 @@ async def test_update_state_via_state_topic_template(
disarmed\ disarmed\
{% endif %}", {% endif %}",
} }
}
}, },
) )
await hass.async_block_till_done() 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): async def test_attributes_code_number(hass, mqtt_mock_entry_with_yaml_config):
"""Test attributes which are not supported by the vacuum.""" """Test attributes which are not supported by the vacuum."""
config = copy.deepcopy(DEFAULT_CONFIG) 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_attributes_remote_code_number(hass, mqtt_mock_entry_with_yaml_config):
"""Test attributes which are not supported by the vacuum.""" """Test attributes which are not supported by the vacuum."""
config = copy.deepcopy(DEFAULT_CONFIG_REMOTE_CODE) 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_attributes_code_text(hass, mqtt_mock_entry_with_yaml_config):
"""Test attributes which are not supported by the vacuum.""" """Test attributes which are not supported by the vacuum."""
config = copy.deepcopy(DEFAULT_CONFIG) 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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, hass,
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
alarm_control_panel.DOMAIN, 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, hass,
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
caplog, 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one alarm per unique_id.""" """Test unique id option only creates one alarm per unique_id."""
config = { config = {
mqtt.DOMAIN: {
alarm_control_panel.DOMAIN: [ alarm_control_panel.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_topic": "command-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( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, alarm_control_panel.DOMAIN, config 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): async def test_discovery_removal_alarm(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered alarm_control_panel.""" """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( await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, alarm_control_panel.DOMAIN, data 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 hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test update of discovered alarm_control_panel.""" """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])
config2 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN])
config1["name"] = "Beer" config1["name"] = "Beer"
config2["name"] = "Milk" config2["name"] = "Milk"
config1["state_topic"] = "alarm/state1" config1["state_topic"] = "alarm/state1"
@ -739,8 +764,8 @@ async def test_discovery_update_alarm_template(
hass, mqtt_mock_entry_no_yaml_config, caplog hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test update of discovered alarm_control_panel.""" """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])
config2 = copy.deepcopy(DEFAULT_CONFIG[alarm_control_panel.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN])
config1["name"] = "Beer" config1["name"] = "Beer"
config2["name"] = "Milk" config2["name"] = "Milk"
config1["state_topic"] = "alarm/state1" config1["state_topic"] = "alarm/state1"
@ -772,7 +797,7 @@ async def test_discovery_update_unchanged_alarm(
hass, mqtt_mock_entry_no_yaml_config, caplog hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test update of discovered alarm_control_panel.""" """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" config1["name"] = "Beer"
data1 = json.dumps(config1) data1 = json.dumps(config1)
@ -824,7 +849,7 @@ async def test_encoding_subscribable_topics(
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
caplog, caplog,
alarm_control_panel.DOMAIN, alarm_control_panel.DOMAIN,
DEFAULT_CONFIG[alarm_control_panel.DOMAIN], DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN],
topic, topic,
value, 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): async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry update.""" """Test device registry update."""
await help_test_entity_device_info_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): async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry remove.""" """Test device registry remove."""
await help_test_entity_device_info_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): async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT discovery update when entity_id is updated.""" """Test MQTT discovery update when entity_id is updated."""
await help_test_entity_id_update_discovery_update( 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.""" """Test publishing MQTT payload with different encoding."""
domain = alarm_control_panel.DOMAIN domain = alarm_control_panel.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = alarm_control_panel.DOMAIN domain = alarm_control_panel.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = alarm_control_panel.DOMAIN 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) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = alarm_control_panel.DOMAIN platform = alarm_control_panel.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = alarm_control_panel.DOMAIN domain = alarm_control_panel.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 import pytest
from homeassistant.components import binary_sensor from homeassistant.components import binary_sensor, mqtt
from homeassistant.const import ( from homeassistant.const import (
EVENT_STATE_CHANGED, EVENT_STATE_CHANGED,
STATE_OFF, STATE_OFF,
@ -45,25 +45,30 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
from tests.common import ( from tests.common import (
assert_setup_component,
async_fire_mqtt_message, async_fire_mqtt_message,
async_fire_time_changed, async_fire_time_changed,
mock_restore_cache, mock_restore_cache,
) )
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "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) @pytest.fixture(autouse=True)
def binary_sensor_platform_only(): def binary_sensor_platform_only():
@ -78,16 +83,17 @@ async def test_setting_sensor_value_expires_availability_topic(
"""Test the expiration of the value.""" """Test the expiration of the value."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"expire_after": 4, "expire_after": 4,
"force_update": True, "force_update": True,
"availability_topic": "availability-topic", "availability_topic": "availability-topic",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the value via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"payload_on": "ON", "payload_on": "ON",
"payload_off": "OFF", "payload_off": "OFF",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the value via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"payload_on": "ON", "payload_on": "ON",
"payload_off": "OFF", "payload_off": "OFF",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the value via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"payload_on": "ON", "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' "value_template": '{%if is_state(entity_id,"on")-%}OFF'
"{%-else-%}ON{%-endif%}", "{%-else-%}ON{%-endif%}",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the value via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"payload_on": "ON", "payload_on": "ON",
"payload_off": "OFF", "payload_off": "OFF",
"value_template": "{{value | upper}}", "value_template": "{{value | upper}}",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test processing a raw value via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"encoding": "", "encoding": "",
"state_topic": "test-topic", "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", "payload_off": "OFF",
"value_template": "{%if value|unpack('b')-%}ON{%else%}OFF{%-endif-%}", "value_template": "{%if value|unpack('b')-%}ON{%else%}OFF{%-endif-%}",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the value via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"payload_on": "ON", "payload_on": "ON",
"payload_off": "OFF", "payload_off": "OFF",
"value_template": '{%if value == "ABC"%}ON{%endif%}', "value_template": '{%if value == "ABC"%}ON{%endif%}',
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of a valid sensor class."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"device_class": "motion", "device_class": "motion",
"state_topic": "test-topic", "state_topic": "test-topic",
} }
}
}, },
) )
await hass.async_block_till_done() 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" 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.""" """Test the setting of an invalid sensor class."""
assert await async_setup_component( assert not await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"device_class": "abc123", "device_class": "abc123",
"state_topic": "test-topic", "state_topic": "test-topic",
} }
}
}, },
) )
await hass.async_block_till_done() assert "Invalid config for [mqtt]: expected BinarySensorDeviceClass" in caplog.text
await mqtt_mock_entry_no_yaml_config()
state = hass.states.get("binary_sensor.test")
assert state is None
async def test_availability_when_connection_lost( async def test_availability_when_connection_lost(
@ -529,28 +539,40 @@ async def test_availability_when_connection_lost(
): ):
"""Test availability after MQTT disconnection.""" """Test availability after MQTT disconnection."""
await help_test_availability_when_connection_lost( 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): async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability without defined availability topic.""" """Test availability without defined availability topic."""
await help_test_availability_without_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): async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic.""" """Test availability by default payload with defined topic."""
await help_test_default_availability_payload( 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): async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic.""" """Test availability by custom payload with defined topic."""
await help_test_custom_availability_payload( 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.""" """Test force update option."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"payload_on": "ON", "payload_on": "ON",
"payload_off": "OFF", "payload_off": "OFF",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test force update option."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"payload_on": "ON", "payload_on": "ON",
"payload_off": "OFF", "payload_off": "OFF",
"force_update": True, "force_update": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test off_delay option."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: { binary_sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"payload_on": "ON", "payload_on": "ON",
@ -642,6 +666,7 @@ async def test_off_delay(hass, mqtt_mock_entry_with_yaml_config):
"off_delay": 30, "off_delay": 30,
"force_update": True, "force_update": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_via_mqtt_json_message( 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): async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test the setting of attribute via MQTT with JSON payload.""" """Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_with_template( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, hass,
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
caplog, 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one sensor per unique_id.""" """Test unique id option only creates one sensor per unique_id."""
config = { config = {
mqtt.DOMAIN: {
binary_sensor.DOMAIN: [ binary_sensor.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
] ]
} }
}
await help_test_unique_id( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, binary_sensor.DOMAIN, config 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 hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test removal of discovered binary_sensor.""" """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( await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, binary_sensor.DOMAIN, data 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 hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test update of discovered binary_sensor.""" """Test update of discovered binary_sensor."""
config1 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN]) config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN])
config1["name"] = "Beer" config1["name"] = "Beer"
config2["name"] = "Milk" config2["name"] = "Milk"
config1["state_topic"] = "sensor/state1" 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 hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test update of discovered binary_sensor.""" """Test update of discovered binary_sensor."""
config1 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN]) config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[binary_sensor.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN])
config1["name"] = "Beer" config1["name"] = "Beer"
config2["name"] = "Milk" config2["name"] = "Milk"
config1["state_topic"] = "sensor/state1" config1["state_topic"] = "sensor/state1"
@ -861,7 +892,7 @@ async def test_encoding_subscribable_topics(
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
caplog, caplog,
binary_sensor.DOMAIN, binary_sensor.DOMAIN,
DEFAULT_CONFIG[binary_sensor.DOMAIN], DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN],
topic, topic,
value, value,
attribute, attribute,
@ -873,7 +904,7 @@ async def test_discovery_update_unchanged_binary_sensor(
hass, mqtt_mock_entry_no_yaml_config, caplog hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test update of discovered binary_sensor.""" """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" config1["name"] = "Beer"
data1 = json.dumps(config1) 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): async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT binary sensor device registry integration.""" """Test MQTT binary sensor device registry integration."""
await help_test_entity_device_info_with_connection( 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): async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT binary sensor device registry integration.""" """Test MQTT binary sensor device registry integration."""
await help_test_entity_device_info_with_identifier( 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): async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry update.""" """Test device registry update."""
await help_test_entity_device_info_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): async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry remove.""" """Test device registry remove."""
await help_test_entity_device_info_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): async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config):
"""Test MQTT subscriptions are managed when entity_id is updated.""" """Test MQTT subscriptions are managed when entity_id is updated."""
await help_test_entity_id_update_subscriptions( 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): async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT discovery update when entity_id is updated.""" """Test MQTT discovery update when entity_id is updated."""
await help_test_entity_id_update_discovery_update( 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = binary_sensor.DOMAIN domain = binary_sensor.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = binary_sensor.DOMAIN domain = binary_sensor.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) 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.""" """Test cleanup old triggers at reloading and restoring the state."""
domain = binary_sensor.DOMAIN domain = binary_sensor.DOMAIN
config1 = copy.deepcopy(DEFAULT_CONFIG[domain]) config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config1["name"] = "test1" config1["name"] = "test1"
config1["expire_after"] = 30 config1["expire_after"] = 30
config1["state_topic"] = "test-topic1" config1["state_topic"] = "test-topic1"
config2 = copy.deepcopy(DEFAULT_CONFIG[domain]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config2["name"] = "test2" config2["name"] = "test2"
config2["expire_after"] = 5 config2["expire_after"] = 5
config2["state_topic"] = "test-topic2" config2["state_topic"] = "test-topic2"
@ -1004,8 +1055,8 @@ async def test_cleanup_triggers_and_restoring_state(
assert await async_setup_component( assert await async_setup_component(
hass, hass,
binary_sensor.DOMAIN, mqtt.DOMAIN,
{binary_sensor.DOMAIN: [config1, config2]}, {mqtt.DOMAIN: {binary_sensor.DOMAIN: [config1, config2]}},
) )
await hass.async_block_till_done() await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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") freezer.move_to("2022-02-02 12:01:10+01:00")
await help_test_reload_with_config( 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.test1" in caplog.text
assert "Clean up expire after trigger for binary_sensor.test2" not 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") freezer.move_to("2022-02-02 12:02:00+01:00")
domain = binary_sensor.DOMAIN domain = binary_sensor.DOMAIN
config3 = copy.deepcopy(DEFAULT_CONFIG[domain]) config3 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config3["name"] = "test3" config3["name"] = "test3"
config3["expire_after"] = 10 config3["expire_after"] = 10
config3["state_topic"] = "test-topic3" 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,)) mock_restore_cache(hass, (fake_state,))
with assert_setup_component(1, domain): assert await async_setup_component(
assert await async_setup_component(hass, domain, {domain: config3}) hass, mqtt.DOMAIN, {mqtt.DOMAIN: {domain: config3}}
)
await hass.async_block_till_done() await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
assert "Skip state recovery after reload for binary_sensor.test3" in caplog.text 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): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = binary_sensor.DOMAIN platform = binary_sensor.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = binary_sensor.DOMAIN domain = binary_sensor.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 import pytest
from homeassistant.components import button from homeassistant.components import button, mqtt
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME, ATTR_FRIENDLY_NAME,
@ -38,14 +38,19 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
DEFAULT_CONFIG = { 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) @pytest.fixture(autouse=True)
def button_platform_only(): 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.""" """Test the sending MQTT commands."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
button.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
button.DOMAIN: { button.DOMAIN: {
"command_topic": "command-topic", "command_topic": "command-topic",
"name": "test", "name": "test",
"object_id": "test_button", "object_id": "test_button",
"payload_press": "beer press", "payload_press": "beer press",
"platform": "mqtt",
"qos": "2", "qos": "2",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the sending of MQTT commands through a command template."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
button.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
button.DOMAIN: { button.DOMAIN: {
"command_topic": "command-topic", "command_topic": "command-topic",
"command_template": '{ "{{ value }}": "{{ entity_id }}" }', "command_template": '{ "{{ value }}": "{{ entity_id }}" }',
"name": "test", "name": "test",
"payload_press": "milky_way_press", "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): async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic.""" """Test availability by default payload with defined topic."""
config = { config = {
mqtt.DOMAIN: {
button.DOMAIN: { button.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_press": 1, "payload_press": 1,
} }
} }
}
await help_test_default_availability_payload( await help_test_default_availability_payload(
hass, 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): async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic.""" """Test availability by custom payload with defined topic."""
config = { config = {
mqtt.DOMAIN: {
button.DOMAIN: { button.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_press": 1, "payload_press": 1,
} }
} }
}
await help_test_custom_availability_payload( await help_test_custom_availability_payload(
hass, hass,
@ -218,44 +227,56 @@ async def test_update_with_json_attrs_not_dict(
): ):
"""Test attributes get extracted from a JSON result.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, button.DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one button per unique_id.""" """Test unique id option only creates one button per unique_id."""
config = { config = {
mqtt.DOMAIN: {
button.DOMAIN: [ button.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"command_topic": "command-topic", "command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"command_topic": "command-topic", "command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
] ]
} }
}
await help_test_unique_id( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, button.DOMAIN, config 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): async def test_discovery_update_button(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered button.""" """Test update of discovered button."""
config1 = copy.deepcopy(DEFAULT_CONFIG[button.DOMAIN]) config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][button.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[button.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][button.DOMAIN])
config1["name"] = "Beer" config1["name"] = "Beer"
config2["name"] = "Milk" 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.""" """Test device_class option with invalid value."""
assert await async_setup_component( assert not await async_setup_component(
hass, hass,
button.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
button.DOMAIN: { button.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"device_class": "foobarnotreal", "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): async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config):
"""Test device_class option with valid values.""" """Test device_class option with valid values."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
button.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
button.DOMAIN: [ button.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"command_topic": "test-topic", "command_topic": "test-topic",
"device_class": "update", "device_class": "update",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"command_topic": "test-topic", "command_topic": "test-topic",
"device_class": "restart", "device_class": "restart",
}, },
{ {
"platform": "mqtt",
"name": "Test 3", "name": "Test 3",
"command_topic": "test-topic", "command_topic": "test-topic",
}, },
] ]
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -443,7 +459,7 @@ async def test_publishing_with_custom_encoding(
): ):
"""Test publishing MQTT payload with different encoding.""" """Test publishing MQTT payload with different encoding."""
domain = button.DOMAIN domain = button.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = button.DOMAIN domain = button.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = button.DOMAIN domain = button.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = button.DOMAIN platform = button.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = button.DOMAIN domain = button.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 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.components.mqtt.camera import MQTT_CAMERA_ATTRIBUTES_BLOCKED
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -37,15 +37,18 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
from tests.common import async_fire_mqtt_message from tests.common import async_fire_mqtt_message
DEFAULT_CONFIG = { DEFAULT_CONFIG = {mqtt.DOMAIN: {camera.DOMAIN: {"name": "test", "topic": "test_topic"}}}
camera.DOMAIN: {"platform": "mqtt", "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) @pytest.fixture(autouse=True)
@ -62,8 +65,8 @@ async def test_run_camera_setup(
topic = "test/camera" topic = "test/camera"
await async_setup_component( await async_setup_component(
hass, hass,
"camera", mqtt.DOMAIN,
{"camera": {"platform": "mqtt", "topic": topic, "name": "Test Camera"}}, {mqtt.DOMAIN: {camera.DOMAIN: {"topic": topic, "name": "Test Camera"}}},
) )
await hass.async_block_till_done() await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
@ -86,14 +89,15 @@ async def test_run_camera_b64_encoded(
topic = "test/camera" topic = "test/camera"
await async_setup_component( await async_setup_component(
hass, hass,
"camera", mqtt.DOMAIN,
{ {
"camera": { mqtt.DOMAIN: {
"platform": "mqtt", camera.DOMAIN: {
"topic": topic, "topic": topic,
"name": "Test Camera", "name": "Test Camera",
"encoding": "b64", "encoding": "b64",
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -110,7 +114,7 @@ async def test_run_camera_b64_encoded(
assert body == "grass" 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( async def test_legacy_camera_b64_encoded_with_availability(
hass, hass_client_no_auth, mqtt_mock_entry_with_yaml_config 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" topic_availability = "test/camera_availability"
await async_setup_component( await async_setup_component(
hass, hass,
"camera", mqtt.DOMAIN,
{ {
"camera": { mqtt.DOMAIN: {
"platform": "mqtt", camera.DOMAIN: {
"topic": topic, "topic": topic,
"name": "Test Camera", "name": "Test Camera",
"encoding": "b64", "encoding": "b64",
"availability": {"topic": topic_availability}, "availability": {"topic": topic_availability},
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -155,16 +160,17 @@ async def test_camera_b64_encoded_with_availability(
topic_availability = "test/camera_availability" topic_availability = "test/camera_availability"
await async_setup_component( await async_setup_component(
hass, hass,
"camera", mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
"camera": { "camera": {
"platform": "mqtt",
"topic": topic, "topic": topic,
"name": "Test Camera", "name": "Test Camera",
"encoding": "utf-8", "encoding": "utf-8",
"image_encoding": "b64", "image_encoding": "b64",
"availability": {"topic": topic_availability}, "availability": {"topic": topic_availability},
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, camera.DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one camera per unique_id.""" """Test unique id option only creates one camera per unique_id."""
config = { config = {
mqtt.DOMAIN: {
camera.DOMAIN: [ camera.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"topic": "test-topic", "topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"topic": "test-topic", "topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
] ]
} }
}
await help_test_unique_id( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, camera.DOMAIN, config 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): async def test_discovery_removal_camera(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered camera.""" """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( await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, camera.DOMAIN, data 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = camera.DOMAIN domain = camera.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = camera.DOMAIN domain = camera.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = camera.DOMAIN platform = camera.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = camera.DOMAIN domain = camera.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 pytest
import voluptuous as vol 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 import DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
ATTR_AUX_HEAT, ATTR_AUX_HEAT,
@ -16,10 +16,9 @@ from homeassistant.components.climate.const import (
ATTR_SWING_MODE, ATTR_SWING_MODE,
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_LOW,
CURRENT_HVAC_ACTIONS,
DOMAIN as CLIMATE_DOMAIN,
PRESET_ECO, PRESET_ECO,
ClimateEntityFeature, ClimateEntityFeature,
HVACAction,
HVACMode, HVACMode,
) )
from homeassistant.components.mqtt.climate import MQTT_CLIMATE_ATTRIBUTES_BLOCKED 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_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
@ -62,9 +61,10 @@ from tests.components.climate import common
ENTITY_CLIMATE = "climate.test" ENTITY_CLIMATE = "climate.test"
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
CLIMATE_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", climate.DOMAIN: {
"name": "test", "name": "test",
"mode_command_topic": "mode-topic", "mode_command_topic": "mode-topic",
"temperature_command_topic": "temperature-topic", "temperature_command_topic": "temperature-topic",
@ -84,8 +84,14 @@ DEFAULT_CONFIG = {
"activity", "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) @pytest.fixture(autouse=True)
def climate_platform_only(): def climate_platform_only():
@ -96,7 +102,7 @@ def climate_platform_only():
async def test_setup_params(hass, mqtt_mock_entry_with_yaml_config): async def test_setup_params(hass, mqtt_mock_entry_with_yaml_config):
"""Test the initial parameters.""" """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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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 assert state.attributes.get("max_temp") == DEFAULT_MAX_TEMP
async def test_preset_none_in_preset_modes( async def test_preset_none_in_preset_modes(hass, caplog):
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test the preset mode payload reset configuration.""" """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") config["preset_modes"].append("none")
assert await async_setup_component(hass, CLIMATE_DOMAIN, {CLIMATE_DOMAIN: config}) assert not await async_setup_component(
await hass.async_block_till_done() hass, mqtt.DOMAIN, {mqtt.DOMAIN: {climate.DOMAIN: config}}
await mqtt_mock_entry_no_yaml_config() )
assert "Invalid config for [climate.mqtt]: not a valid value" in caplog.text assert "Invalid config for [mqtt]: not a valid value" in caplog.text
state = hass.states.get(ENTITY_CLIMATE)
assert state is None
# AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 # 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 }}"), ("hold_mode_state_template", "{{ value_json }}"),
], ],
) )
async def test_preset_modes_deprecation_guard( async def test_preset_modes_deprecation_guard(hass, caplog, parameter, config_value):
hass, mqtt_mock_entry_no_yaml_config, caplog, parameter, config_value
):
"""Test the configuration for invalid legacy parameters.""" """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 config[parameter] = config_value
assert await async_setup_component(hass, CLIMATE_DOMAIN, {CLIMATE_DOMAIN: config}) assert not await async_setup_component(
await hass.async_block_till_done() hass, mqtt.DOMAIN, {mqtt.DOMAIN: {climate.DOMAIN: config}}
await mqtt_mock_entry_no_yaml_config() )
state = hass.states.get(ENTITY_CLIMATE) assert f"[{parameter}] is an invalid option for [mqtt]. Check: mqtt->mqtt->climate->0->{parameter}"
assert state is None
async def test_supported_features(hass, mqtt_mock_entry_with_yaml_config): async def test_supported_features(hass, mqtt_mock_entry_with_yaml_config):
"""Test the supported_features.""" """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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_get_hvac_modes(hass, mqtt_mock_entry_with_yaml_config):
"""Test that the operation list returns the correct modes.""" """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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
@ -193,7 +192,7 @@ async def test_set_operation_bad_attr_and_state(
Also check the 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_set_operation(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of new operation mode.""" """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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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): async def test_set_operation_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting operation mode in pessimistic mode.""" """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" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): 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.""" """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" 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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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): async def test_set_fan_mode_bad_attr(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test setting fan mode without required attribute.""" """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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_set_fan_mode_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of new fan mode in pessimistic mode.""" """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" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_set_fan_mode(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of new fan mode.""" """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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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): async def test_set_swing_mode_bad_attr(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test setting swing mode without required attribute.""" """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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_set_swing_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting swing mode in pessimistic mode.""" """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" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_set_swing(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of new swing mode.""" """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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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): async def test_set_target_temperature(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting the target temperature.""" """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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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 hass, mqtt_mock_entry_with_yaml_config
): ):
"""Test setting the target temperature.""" """Test setting the target temperature."""
config = copy.deepcopy(DEFAULT_CONFIG) config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["temperature_state_topic"] = "temperature-state" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_set_target_temperature_low_high(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting the low/high target temperature.""" """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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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 hass, mqtt_mock_entry_with_yaml_config
): ):
"""Test setting the low/high target temperature.""" """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_low_state_topic"] = "temperature-low-state"
config["climate"]["temperature_high_state_topic"] = "temperature-high-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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_receive_mqtt_temperature(hass, mqtt_mock_entry_with_yaml_config):
"""Test getting the current temperature via MQTT.""" """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" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_handle_action_received(hass, mqtt_mock_entry_with_yaml_config):
"""Test getting the action received via MQTT.""" """Test getting the action received via MQTT."""
config = copy.deepcopy(DEFAULT_CONFIG) config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["action_topic"] = "action" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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 assert hvac_action is None
# Redefine actions according to https://developers.home-assistant.io/docs/core/entity/climate/#hvac-action # Redefine actions according to https://developers.home-assistant.io/docs/core/entity/climate/#hvac-action
actions = ["off", "heating", "cooling", "drying", "idle", "fan"] 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: for action in actions:
async_fire_mqtt_message(hass, "action", action) async_fire_mqtt_message(hass, "action", action)
state = hass.states.get(ENTITY_CLIMATE) 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test setting of the preset mode.""" """Test setting of the preset mode."""
config = copy.deepcopy(DEFAULT_CONFIG) config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
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 hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test setting of the preset mode.""" """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" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_set_aux_pessimistic(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of the aux heating in pessimistic mode.""" """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" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_set_aux(hass, mqtt_mock_entry_with_yaml_config):
"""Test setting of the aux heating.""" """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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test availability after MQTT disconnection."""
await help_test_availability_when_connection_lost( 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): async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability without defined availability topic.""" """Test availability without defined availability topic."""
await help_test_availability_without_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): async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic.""" """Test availability by default payload with defined topic."""
await help_test_default_availability_payload( 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): async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic.""" """Test availability by custom payload with defined topic."""
await help_test_custom_availability_payload( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test getting temperature high/low with templates.""" """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_low_state_topic"] = "temperature-state"
config["climate"]["temperature_high_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_low_state_template"] = "{{ value_json.temp_low }}"
config["climate"]["temperature_high_state_template"] = "{{ value_json.temp_high }}" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_get_with_templates(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test getting various attributes with templates.""" """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 # By default, just unquote the JSON-strings
config["climate"]["value_template"] = "{{ value_json }}" config["climate"]["value_template"] = "{{ value_json }}"
config["climate"]["action_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"]["aux_state_topic"] = "aux-state"
config["climate"]["current_temperature_topic"] = "current-temperature" config["climate"]["current_temperature_topic"] = "current-temperature"
config["climate"]["preset_mode_state_topic"] = "current-preset-mode" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_set_and_templates(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test setting various attributes with templates.""" """Test setting various attributes with templates."""
config = copy.deepcopy(DEFAULT_CONFIG) config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
# Create simple templates # Create simple templates
config["climate"]["fan_mode_command_template"] = "fan_mode: {{ value }}" config["climate"]["fan_mode_command_template"] = "fan_mode: {{ value }}"
config["climate"]["preset_mode_command_template"] = "preset_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_high_command_template"] = "temp_hi: {{ value }}"
config["climate"]["temperature_low_command_template"] = "temp_lo: {{ 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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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): async def test_min_temp_custom(hass, mqtt_mock_entry_with_yaml_config):
"""Test a custom min temp.""" """Test a custom min temp."""
config = copy.deepcopy(DEFAULT_CONFIG) config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["min_temp"] = 26 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_max_temp_custom(hass, mqtt_mock_entry_with_yaml_config):
"""Test a custom max temp.""" """Test a custom max temp."""
config = copy.deepcopy(DEFAULT_CONFIG) config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["max_temp"] = 60 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_temp_step_custom(hass, mqtt_mock_entry_with_yaml_config):
"""Test a custom temp step.""" """Test a custom temp step."""
config = copy.deepcopy(DEFAULT_CONFIG) config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN])
config["climate"]["temp_step"] = 0.01 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_temperature_unit(hass, mqtt_mock_entry_with_yaml_config):
"""Test that setting temperature unit converts temperature values.""" """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"]["temperature_unit"] = "F"
config["climate"]["current_temperature_topic"] = "current_temperature" 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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.""" """Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_via_mqtt_json_message( 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( await help_test_setting_blocked_attribute_via_mqtt_json_message(
hass, hass,
mqtt_mock_entry_no_yaml_config, mqtt_mock_entry_no_yaml_config,
CLIMATE_DOMAIN, climate.DOMAIN,
DEFAULT_CONFIG, DEFAULT_CONFIG,
MQTT_CLIMATE_ATTRIBUTES_BLOCKED, 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): async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test the setting of attribute via MQTT with JSON payload.""" """Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_with_template( 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, CLIMATE_DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one climate per unique_id.""" """Test unique id option only creates one climate per unique_id."""
config = { config = {
CLIMATE_DOMAIN: [ mqtt.DOMAIN: {
climate.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"power_state_topic": "test-topic", "power_state_topic": "test-topic",
"power_command_topic": "test_topic", "power_command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"power_state_topic": "test-topic", "power_state_topic": "test-topic",
"power_command_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( 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, attribute_value,
): ):
"""Test handling of incoming encoded payload.""" """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( await help_test_encoding_subscribable_topics(
hass, hass,
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
caplog, caplog,
CLIMATE_DOMAIN, climate.DOMAIN,
config, config,
topic, topic,
value, 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): async def test_discovery_removal_climate(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered climate.""" """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( 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"} config1 = {"name": "Beer"}
config2 = {"name": "Milk"} config2 = {"name": "Milk"}
await help_test_discovery_update( 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, hass,
mqtt_mock_entry_no_yaml_config, mqtt_mock_entry_no_yaml_config,
caplog, caplog,
CLIMATE_DOMAIN, climate.DOMAIN,
data1, data1,
discovery_update, 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#" }' data1 = '{ "name": "Beer", "power_command_topic": "test_topic#" }'
data2 = '{ "name": "Milk", "power_command_topic": "test_topic" }' data2 = '{ "name": "Milk", "power_command_topic": "test_topic" }'
await help_test_discovery_broken( 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): async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT climate device registry integration.""" """Test MQTT climate device registry integration."""
await help_test_entity_device_info_with_connection( 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): async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT climate device registry integration.""" """Test MQTT climate device registry integration."""
await help_test_entity_device_info_with_identifier( 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): async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry update.""" """Test device registry update."""
await help_test_entity_device_info_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): async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry remove.""" """Test device registry remove."""
await help_test_entity_device_info_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): async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config):
"""Test MQTT subscriptions are managed when entity_id is updated.""" """Test MQTT subscriptions are managed when entity_id is updated."""
config = { config = {
CLIMATE_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", climate.DOMAIN: {
"name": "test", "name": "test",
"mode_state_topic": "test-topic", "mode_state_topic": "test-topic",
"availability_topic": "avty-topic", "availability_topic": "avty-topic",
} }
} }
}
await help_test_entity_id_update_subscriptions( await help_test_entity_id_update_subscriptions(
hass, hass,
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
CLIMATE_DOMAIN, climate.DOMAIN,
config, config,
["test-topic", "avty-topic"], ["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): async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT discovery update when entity_id is updated.""" """Test MQTT discovery update when entity_id is updated."""
await help_test_entity_id_update_discovery_update( 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): async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT debug info.""" """Test MQTT debug info."""
config = { config = {
CLIMATE_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", climate.DOMAIN: {
"name": "test", "name": "test",
"mode_command_topic": "command-topic", "mode_command_topic": "command-topic",
"mode_state_topic": "test-topic", "mode_state_topic": "test-topic",
} }
} }
}
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, hass,
mqtt_mock_entry_no_yaml_config, mqtt_mock_entry_no_yaml_config,
CLIMATE_DOMAIN, climate.DOMAIN,
config, config,
climate.SERVICE_TURN_ON, climate.SERVICE_TURN_ON,
command_topic="command-topic", 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): async def test_precision_default(hass, mqtt_mock_entry_with_yaml_config):
"""Test that setting precision to tenths works as intended.""" """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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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): async def test_precision_halves(hass, mqtt_mock_entry_with_yaml_config):
"""Test that setting precision to halves works as intended.""" """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 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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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): async def test_precision_whole(hass, mqtt_mock_entry_with_yaml_config):
"""Test that setting precision to whole works as intended.""" """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 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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test publishing MQTT payload with different encoding."""
domain = climate.DOMAIN domain = climate.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain]) config = copy.deepcopy(DEFAULT_CONFIG)
if topic != "preset_mode_command_topic": if topic != "preset_mode_command_topic":
del config["preset_mode_command_topic"] del config[mqtt.DOMAIN][domain]["preset_mode_command_topic"]
del config["preset_modes"] del config[mqtt.DOMAIN][domain]["preset_modes"]
await help_test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = CLIMATE_DOMAIN domain = climate.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = CLIMATE_DOMAIN domain = climate.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = CLIMATE_DOMAIN platform = climate.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = climate.DOMAIN domain = climate.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 hass, mqtt_mock_entry_with_yaml_config, domain, config
): ):
"""Test availability after MQTT disconnection.""" """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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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 hass, mqtt_mock_entry_with_yaml_config, domain, config
): ):
"""Test availability without defined availability topic.""" """Test availability without defined availability topic."""
assert "availability_topic" not in config[domain] assert "availability_topic" not in config[mqtt.DOMAIN][domain]
assert await async_setup_component(hass, domain, config) assert await async_setup_component(hass, mqtt.DOMAIN, config)
await hass.async_block_till_done() await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
@ -96,10 +96,10 @@ async def help_test_default_availability_payload(
""" """
# Add availability settings to config # Add availability settings to config
config = copy.deepcopy(config) config = copy.deepcopy(config)
config[domain]["availability_topic"] = "availability-topic" config[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic"
assert await async_setup_component( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -147,13 +147,13 @@ async def help_test_default_availability_list_payload(
""" """
# Add availability settings to config # Add availability settings to config
config = copy.deepcopy(config) config = copy.deepcopy(config)
config[domain]["availability"] = [ config[mqtt.DOMAIN][domain]["availability"] = [
{"topic": "availability-topic1"}, {"topic": "availability-topic1"},
{"topic": "availability-topic2"}, {"topic": "availability-topic2"},
] ]
assert await async_setup_component( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -213,14 +213,14 @@ async def help_test_default_availability_list_payload_all(
""" """
# Add availability settings to config # Add availability settings to config
config = copy.deepcopy(config) config = copy.deepcopy(config)
config[domain]["availability_mode"] = "all" config[mqtt.DOMAIN][domain]["availability_mode"] = "all"
config[domain]["availability"] = [ config[mqtt.DOMAIN][domain]["availability"] = [
{"topic": "availability-topic1"}, {"topic": "availability-topic1"},
{"topic": "availability-topic2"}, {"topic": "availability-topic2"},
] ]
assert await async_setup_component( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -281,14 +281,14 @@ async def help_test_default_availability_list_payload_any(
""" """
# Add availability settings to config # Add availability settings to config
config = copy.deepcopy(config) config = copy.deepcopy(config)
config[domain]["availability_mode"] = "any" config[mqtt.DOMAIN][domain]["availability_mode"] = "any"
config[domain]["availability"] = [ config[mqtt.DOMAIN][domain]["availability"] = [
{"topic": "availability-topic1"}, {"topic": "availability-topic1"},
{"topic": "availability-topic2"}, {"topic": "availability-topic2"},
] ]
assert await async_setup_component( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() 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( async def help_test_default_availability_list_single(
hass, hass,
mqtt_mock_entry_with_yaml_config,
caplog, caplog,
domain, domain,
config, config,
@ -345,22 +344,17 @@ async def help_test_default_availability_list_single(
""" """
# Add availability settings to config # Add availability settings to config
config = copy.deepcopy(config) config = copy.deepcopy(config)
config[domain]["availability"] = [ config[mqtt.DOMAIN][domain]["availability"] = [
{"topic": "availability-topic1"}, {"topic": "availability-topic1"},
] ]
config[domain]["availability_topic"] = "availability-topic" config[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic"
assert await async_setup_component( assert not await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, 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 ( 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 in caplog.text
) )
@ -380,12 +374,12 @@ async def help_test_custom_availability_payload(
""" """
# Add availability settings to config # Add availability settings to config
config = copy.deepcopy(config) config = copy.deepcopy(config)
config[domain]["availability_topic"] = "availability-topic" config[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic"
config[domain]["payload_available"] = "good" config[mqtt.DOMAIN][domain]["payload_available"] = "good"
config[domain]["payload_not_available"] = "nogood" config[mqtt.DOMAIN][domain]["payload_not_available"] = "nogood"
assert await async_setup_component( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -434,17 +428,17 @@ async def help_test_discovery_update_availability(
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
# Add availability settings to config # Add availability settings to config
config1 = copy.deepcopy(config) config1 = copy.deepcopy(config)
config1[domain]["availability_topic"] = "availability-topic1" config1[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic1"
config2 = copy.deepcopy(config) config2 = copy.deepcopy(config)
config2[domain]["availability"] = [ config2[mqtt.DOMAIN][domain]["availability"] = [
{"topic": "availability-topic2"}, {"topic": "availability-topic2"},
{"topic": "availability-topic3"}, {"topic": "availability-topic3"},
] ]
config3 = copy.deepcopy(config) config3 = copy.deepcopy(config)
config3[domain]["availability_topic"] = "availability-topic4" config3[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic4"
data1 = json.dumps(config1[domain]) data1 = json.dumps(config1[mqtt.DOMAIN][domain])
data2 = json.dumps(config2[domain]) data2 = json.dumps(config2[mqtt.DOMAIN][domain])
data3 = json.dumps(config3[domain]) data3 = json.dumps(config3[mqtt.DOMAIN][domain])
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1)
await hass.async_block_till_done() 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 # Add JSON attributes settings to config
config = copy.deepcopy(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( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() 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 # Add JSON attributes settings to config
config = copy.deepcopy(config) config = copy.deepcopy(config)
config[domain]["json_attributes_topic"] = "attr-topic" config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic"
data = json.dumps(config[domain]) data = json.dumps(config[mqtt.DOMAIN][domain])
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
await hass.async_block_till_done() await hass.async_block_till_done()
val = "abc123" val = "abc123"
@ -561,11 +555,13 @@ async def help_test_setting_attribute_with_template(
""" """
# Add JSON attributes settings to config # Add JSON attributes settings to config
config = copy.deepcopy(config) config = copy.deepcopy(config)
config[domain]["json_attributes_topic"] = "attr-topic" config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic"
config[domain]["json_attributes_template"] = "{{ value_json['Timer1'] | tojson }}" config[mqtt.DOMAIN][domain][
"json_attributes_template"
] = "{{ value_json['Timer1'] | tojson }}"
assert await async_setup_component( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() 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 # Add JSON attributes settings to config
config = copy.deepcopy(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( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() 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 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 hass, mqtt_mock_entry_with_yaml_config, caplog, domain, config
): ):
"""Test JSON validation of attributes. """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 # Add JSON attributes settings to config
config = copy.deepcopy(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( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -640,11 +636,11 @@ async def help_test_discovery_update_attr(
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
# Add JSON attributes settings to config # Add JSON attributes settings to config
config1 = copy.deepcopy(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 = copy.deepcopy(config)
config2[domain]["json_attributes_topic"] = "attr-topic2" config2[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic2"
data1 = json.dumps(config1[domain]) data1 = json.dumps(config1[mqtt.DOMAIN][domain])
data2 = json.dumps(config2[domain]) data2 = json.dumps(config2[mqtt.DOMAIN][domain])
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1)
await hass.async_block_till_done() 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): 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.""" """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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
assert len(hass.states.async_entity_ids(domain)) == 1 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() await hass.async_block_till_done()
assert await async_setup_component( 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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() await mqtt_mock_entry_no_yaml_config()
# Add device settings to 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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique" config["unique_id"] = "veryunique"
@ -975,7 +971,7 @@ async def help_test_entity_device_info_with_connection(
""" """
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
# Add device settings to 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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_MAC)
config["unique_id"] = "veryunique" config["unique_id"] = "veryunique"
@ -1005,7 +1001,7 @@ async def help_test_entity_device_info_remove(
"""Test device registry remove.""" """Test device registry remove."""
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
# Add device settings to 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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique" config["unique_id"] = "veryunique"
@ -1037,7 +1033,7 @@ async def help_test_entity_device_info_update(
""" """
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
# Add device settings to 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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique" 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.""" """Test MQTT subscriptions are managed when entity_id is updated."""
# Add unique_id to config # Add unique_id to config
config = copy.deepcopy(config) config = copy.deepcopy(config)
config[domain]["unique_id"] = "TOTALLY_UNIQUE" config[mqtt.DOMAIN][domain]["unique_id"] = "TOTALLY_UNIQUE"
if topics is None: if topics is None:
# Add default topics to config # Add default topics to config
config[domain]["availability_topic"] = "avty-topic" config[mqtt.DOMAIN][domain]["availability_topic"] = "avty-topic"
config[domain]["state_topic"] = "test-topic" config[mqtt.DOMAIN][domain]["state_topic"] = "test-topic"
topics = ["avty-topic", "test-topic"] topics = ["avty-topic", "test-topic"]
assert len(topics) > 0 assert len(topics) > 0
registry = mock_registry(hass, {}) registry = mock_registry(hass, {})
assert await async_setup_component( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
config, config,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1111,16 +1107,16 @@ async def help_test_entity_id_update_discovery_update(
# Add unique_id to config # Add unique_id to config
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
config = copy.deepcopy(config) config = copy.deepcopy(config)
config[domain]["unique_id"] = "TOTALLY_UNIQUE" config[mqtt.DOMAIN][domain]["unique_id"] = "TOTALLY_UNIQUE"
if topic is None: if topic is None:
# Add default topic to config # Add default topic to config
config[domain]["availability_topic"] = "avty-topic" config[mqtt.DOMAIN][domain]["availability_topic"] = "avty-topic"
topic = "avty-topic" topic = "avty-topic"
ent_registry = mock_registry(hass, {}) 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) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
await hass.async_block_till_done() 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") ent_registry.async_update_entity(f"{domain}.test", new_entity_id=f"{domain}.milk")
await hass.async_block_till_done() await hass.async_block_till_done()
config[domain]["availability_topic"] = f"{topic}_2" config[mqtt.DOMAIN][domain]["availability_topic"] = f"{topic}_2"
data = json.dumps(config[domain]) data = json.dumps(config[mqtt.DOMAIN][domain])
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(hass.states.async_entity_ids(domain)) == 1 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() await mqtt_mock_entry_no_yaml_config()
# Add device settings to 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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique" config["unique_id"] = "veryunique"
config["platform"] = "mqtt"
registry = dr.async_get(hass) 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() await mqtt_mock_entry_no_yaml_config()
# Add device settings to 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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique" config["unique_id"] = "veryunique"
@ -1256,7 +1253,7 @@ async def help_test_entity_debug_info_message(
""" """
# Add device settings to config # Add device settings to config
await mqtt_mock_entry_no_yaml_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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique" config["unique_id"] = "veryunique"
@ -1359,9 +1356,10 @@ async def help_test_entity_debug_info_remove(
""" """
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
# Add device settings to 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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique" config["unique_id"] = "veryunique"
config["platform"] = "mqtt"
registry = dr.async_get(hass) 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() await mqtt_mock_entry_no_yaml_config()
# Add device settings to 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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique" config["unique_id"] = "veryunique"
config["platform"] = "mqtt"
dev_registry = dr.async_get(hass) dev_registry = dr.async_get(hass)
ent_registry = mock_registry(hass, {}) ent_registry = mock_registry(hass, {})
@ -1461,7 +1460,7 @@ async def help_test_entity_disabled_by_default(
"""Test device registry remove.""" """Test device registry remove."""
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
# Add device settings to 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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["enabled_by_default"] = False config["enabled_by_default"] = False
config["unique_id"] = "veryunique1" config["unique_id"] = "veryunique1"
@ -1500,7 +1499,7 @@ async def help_test_entity_category(
"""Test device registry remove.""" """Test device registry remove."""
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
# Add device settings to 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["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
ent_registry = er.async_get(hass) ent_registry = er.async_get(hass)
@ -1564,7 +1563,7 @@ async def help_test_publishing_with_custom_encoding(
setup_config = [] setup_config = []
service_data = {} service_data = {}
for test_id, test_data in test_config.items(): 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( test_config_setup.update(
{ {
topic: f"cmd/{test_id}", topic: f"cmd/{test_id}",
@ -1573,7 +1572,7 @@ async def help_test_publishing_with_custom_encoding(
) )
if test_data["encoding"] is not None: if test_data["encoding"] is not None:
test_config_setup["encoding"] = test_data["encoding"] test_config_setup["encoding"] = test_data["encoding"]
if test_data["cmd_tpl"]: if template and test_data["cmd_tpl"]:
test_config_setup[ test_config_setup[
template template
] = f"{{{{ (('%.1f'|format({tpl_par}))[0] if is_number({tpl_par}) else {tpl_par}[0]) | ord | pack('b') }}}}" ] = 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 # setup test entities
assert await async_setup_component( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
{domain: setup_config}, {mqtt.DOMAIN: {domain: setup_config}},
) )
await hass.async_block_till_done() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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 hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config
): ):
"""Test reloading an MQTT platform.""" """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 # Create and test an old config of 2 entities based on the config supplied
old_config_1 = copy.deepcopy(config) old_config_1 = copy.deepcopy(config)
old_config_1["name"] = "test_old_1" old_config_1["name"] = "test_old_1"
old_config_2 = copy.deepcopy(config) old_config_2 = copy.deepcopy(config)
old_config_2["name"] = "test_old_2" 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 = copy.deepcopy(config)
old_config_3["name"] = "test_old_3" 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 = copy.deepcopy(config)
old_config_4["name"] = "test_old_4" old_config_4["name"] = "test_old_4"
old_config_4.pop("platform") old_config_4["platform"] = mqtt.DOMAIN
old_config = { old_config = {
domain: [old_config_1, old_config_2], mqtt.DOMAIN: {domain: [old_config_1, old_config_2]},
"mqtt": {domain: [old_config_3, old_config_4]}, domain: [old_config_3, old_config_4],
} }
assert await async_setup_component(hass, domain, old_config) 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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_1["name"] = "test_new_1"
new_config_2 = copy.deepcopy(config) new_config_2 = copy.deepcopy(config)
new_config_2["name"] = "test_new_2" 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 = copy.deepcopy(config)
new_config_3["name"] = "test_new_3" 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 = copy.deepcopy(config)
new_config_4["name"] = "test_new_4" new_config_4["name"] = "test_new_4"
new_config_4.pop("platform") new_config_4["platform"] = mqtt.DOMAIN
new_config_5 = copy.deepcopy(config) new_config_extra_legacy = copy.deepcopy(config)
new_config_5["name"] = "test_new_5" new_config_extra_legacy["name"] = "test_new_6"
new_config_6 = copy.deepcopy(config) new_config_extra_legacy["platform"] = mqtt.DOMAIN
new_config_6["name"] = "test_new_6"
new_config_6.pop("platform")
new_config = { new_config = {
domain: [new_config_1, new_config_2, new_config_5], mqtt.DOMAIN: {domain: [new_config_1, new_config_2, new_config_extra]},
"mqtt": {domain: [new_config_3, new_config_4, new_config_6]}, domain: [new_config_3, new_config_4, new_config_extra_legacy],
} }
await help_test_reload_with_config(hass, caplog, tmp_path, new_config) 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") 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): 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 # 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 = copy.deepcopy(config)
old_config_1["name"] = "test_old_1" old_config_1["name"] = "test_old_1"
old_config_2 = copy.deepcopy(config) 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") 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.""" """Help to test setup from yaml through configuration entry."""
calls = MagicMock() calls = MagicMock()
@ -1823,9 +1833,7 @@ async def help_test_setup_manual_entity_from_yaml(hass, platform, config):
"""Mock reload.""" """Mock reload."""
calls() calls()
config_structure = {mqtt.DOMAIN: {platform: config}} assert await async_setup_component(hass, mqtt.DOMAIN, config)
await async_setup_component(hass, mqtt.DOMAIN, config_structure)
# Mock config entry # Mock config entry
entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"})
entry.add_to_hass(hass) 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.""" """Test unloading the MQTT config entry with a specific platform domain."""
# prepare setup through configuration.yaml # prepare setup through configuration.yaml
config_setup = copy.deepcopy(config) config_setup = copy.deepcopy(config)
config_setup["name"] = "config_setup" config_setup[mqtt.DOMAIN][domain]["name"] = "config_setup"
config_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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
# prepare setup through discovery # prepare setup through discovery
discovery_setup = copy.deepcopy(config) discovery_setup = copy.deepcopy(config[mqtt.DOMAIN][domain])
discovery_setup["name"] = "discovery_setup" discovery_setup["name"] = "discovery_setup"
async_fire_mqtt_message( async_fire_mqtt_message(
hass, f"homeassistant/{domain}/bla/config", json.dumps(discovery_setup) 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"}}) assert await async_setup_component(hass, "mqtt", {"mqtt": {"broker": "bla"}})
await hass.async_block_till_done() await hass.async_block_till_done()
# do not try to reload # 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 assert len(mock_finish_setup.mock_calls) == 0
mock_try_connection.return_value = True 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 import json
from unittest.mock import patch from unittest.mock import patch
import pytest 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.config_entries import ConfigEntryDisabler
from homeassistant.const import CONF_PLATFORM, STATE_HOME, STATE_NOT_HOME, Platform from homeassistant.const import CONF_PLATFORM, STATE_HOME, STATE_NOT_HOME, Platform
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -12,7 +13,6 @@ from homeassistant.setup import async_setup_component
from .test_common import ( from .test_common import (
MockConfigEntry, MockConfigEntry,
help_test_entry_reload_with_new_config, help_test_entry_reload_with_new_config,
help_test_setup_manual_entity_from_yaml,
help_test_unload_config_entry, help_test_unload_config_entry,
) )
@ -45,7 +45,14 @@ async def test_legacy_ensure_device_tracker_platform_validation(
dev_id = "paulus" dev_id = "paulus"
topic = "/location/paulus" topic = "/location/paulus"
assert await async_setup_component( 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
@ -59,13 +66,15 @@ async def test_legacy_new_message(
"""Test new message.""" """Test new message."""
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus" dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}" entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/paulus" topic = "/location/paulus"
location = "work" location = "work"
hass.config.components = {"mqtt", "zone"} hass.config.components = {"mqtt", "zone"}
assert await async_setup_component( 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) async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -79,7 +88,7 @@ async def test_legacy_single_level_wildcard_topic(
"""Test single level wildcard topic.""" """Test single level wildcard topic."""
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus" dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}" entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
subscription = "/location/+/paulus" subscription = "/location/+/paulus"
topic = "/location/room/paulus" topic = "/location/room/paulus"
location = "work" location = "work"
@ -87,8 +96,13 @@ async def test_legacy_single_level_wildcard_topic(
hass.config.components = {"mqtt", "zone"} hass.config.components = {"mqtt", "zone"}
assert await async_setup_component( assert await async_setup_component(
hass, hass,
DOMAIN, device_tracker.DOMAIN,
{DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: subscription}}}, {
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: subscription},
}
},
) )
async_fire_mqtt_message(hass, topic, location) async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -102,7 +116,7 @@ async def test_legacy_multi_level_wildcard_topic(
"""Test multi level wildcard topic.""" """Test multi level wildcard topic."""
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus" dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}" entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
subscription = "/location/#" subscription = "/location/#"
topic = "/location/room/paulus" topic = "/location/room/paulus"
location = "work" location = "work"
@ -110,8 +124,13 @@ async def test_legacy_multi_level_wildcard_topic(
hass.config.components = {"mqtt", "zone"} hass.config.components = {"mqtt", "zone"}
assert await async_setup_component( assert await async_setup_component(
hass, hass,
DOMAIN, device_tracker.DOMAIN,
{DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: subscription}}}, {
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: subscription},
}
},
) )
async_fire_mqtt_message(hass, topic, location) async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done() 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.""" """Test not matching single level wildcard topic."""
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus" dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}" entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
subscription = "/location/+/paulus" subscription = "/location/+/paulus"
topic = "/location/paulus" topic = "/location/paulus"
location = "work" location = "work"
@ -133,8 +152,13 @@ async def test_legacy_single_level_wildcard_topic_not_matching(
hass.config.components = {"mqtt", "zone"} hass.config.components = {"mqtt", "zone"}
assert await async_setup_component( assert await async_setup_component(
hass, hass,
DOMAIN, device_tracker.DOMAIN,
{DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: subscription}}}, {
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: subscription},
}
},
) )
async_fire_mqtt_message(hass, topic, location) async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done() 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.""" """Test not matching multi level wildcard topic."""
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus" dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}" entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
subscription = "/location/#" subscription = "/location/#"
topic = "/somewhere/room/paulus" topic = "/somewhere/room/paulus"
location = "work" location = "work"
@ -156,8 +180,13 @@ async def test_legacy_multi_level_wildcard_topic_not_matching(
hass.config.components = {"mqtt", "zone"} hass.config.components = {"mqtt", "zone"}
assert await async_setup_component( assert await async_setup_component(
hass, hass,
DOMAIN, device_tracker.DOMAIN,
{DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {dev_id: subscription}}}, {
device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt",
"devices": {dev_id: subscription},
}
},
) )
async_fire_mqtt_message(hass, topic, location) async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done() 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.""" """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() await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus" dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}" entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/paulus" topic = "/location/paulus"
payload_home = "present" payload_home = "present"
payload_not_home = "not 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"} hass.config.components = {"mqtt", "zone"}
assert await async_setup_component( assert await async_setup_component(
hass, hass,
DOMAIN, device_tracker.DOMAIN,
{ {
DOMAIN: { device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt", CONF_PLATFORM: "mqtt",
"devices": {dev_id: topic}, "devices": {dev_id: topic},
"payload_home": payload_home, "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.""" """Test not matching payload does not set state to home or not_home."""
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus" dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}" entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/paulus" topic = "/location/paulus"
payload_home = "present" payload_home = "present"
payload_not_home = "not 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"} hass.config.components = {"mqtt", "zone"}
assert await async_setup_component( assert await async_setup_component(
hass, hass,
DOMAIN, device_tracker.DOMAIN,
{ {
DOMAIN: { device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt", CONF_PLATFORM: "mqtt",
"devices": {dev_id: topic}, "devices": {dev_id: topic},
"payload_home": payload_home, "payload_home": payload_home,
@ -237,17 +266,17 @@ async def test_legacy_matching_source_type(
"""Test setting source type.""" """Test setting source type."""
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
dev_id = "paulus" dev_id = "paulus"
entity_id = f"{DOMAIN}.{dev_id}" entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/paulus" topic = "/location/paulus"
source_type = SOURCE_TYPE_BLUETOOTH source_type = SourceType.BLUETOOTH
location = "work" location = "work"
hass.config.components = {"mqtt", "zone"} hass.config.components = {"mqtt", "zone"}
assert await async_setup_component( assert await async_setup_component(
hass, hass,
DOMAIN, device_tracker.DOMAIN,
{ {
DOMAIN: { device_tracker.DOMAIN: {
CONF_PLATFORM: "mqtt", CONF_PLATFORM: "mqtt",
"devices": {dev_id: topic}, "devices": {dev_id: topic},
"source_type": source_type, "source_type": source_type,
@ -257,23 +286,10 @@ async def test_legacy_matching_source_type(
async_fire_mqtt_message(hass, topic, location) async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get(entity_id).attributes["source_type"] == SOURCE_TYPE_BLUETOOTH assert hass.states.get(entity_id).attributes["source_type"] == SourceType.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
# Deprecated in HA Core 2022.6
async def test_unload_entry( async def test_unload_entry(
hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config, tmp_path 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 # setup through configuration.yaml
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
dev_id = "jan" dev_id = "jan"
entity_id = f"{DOMAIN}.{dev_id}" entity_id = f"{device_tracker.DOMAIN}.{dev_id}"
topic = "/location/jan" topic = "/location/jan"
location = "home" location = "home"
hass.config.components = {"mqtt", "zone"} hass.config.components = {"mqtt", "zone"}
assert await async_setup_component( 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) async_fire_mqtt_message(hass, topic, location)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -296,7 +314,7 @@ async def test_unload_entry(
# setup through discovery # setup through discovery
dev_id = "piet" dev_id = "piet"
subscription = "/location/#" subscription = "/location/#"
domain = DOMAIN domain = device_tracker.DOMAIN
discovery_config = { discovery_config = {
"devices": {dev_id: subscription}, "devices": {dev_id: subscription},
"state_topic": "some-state", "state_topic": "some-state",
@ -330,21 +348,22 @@ async def test_unload_entry(
assert discovery_setup_entity is None assert discovery_setup_entity is None
# Deprecated in HA Core 2022.6
async def test_reload_entry_legacy( async def test_reload_entry_legacy(
hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config, tmp_path hass, mock_device_tracker_conf, mqtt_mock_entry_no_yaml_config, tmp_path
): ):
"""Test reloading the config entry with manual MQTT items.""" """Test reloading the config entry with manual MQTT items."""
# setup through configuration.yaml # setup through configuration.yaml
await mqtt_mock_entry_no_yaml_config() await mqtt_mock_entry_no_yaml_config()
entity_id = f"{DOMAIN}.jan" entity_id = f"{device_tracker.DOMAIN}.jan"
topic = "location/jan" topic = "location/jan"
location = "home" location = "home"
config = { config = {
DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {"jan": topic}}, device_tracker.DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {"jan": topic}},
} }
hass.config.components = {"mqtt", "zone"} 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() await hass.async_block_till_done()
async_fire_mqtt_message(hass, topic, location) 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 assert hass.states.get(entity_id).state == location
# Deprecated in HA Core 2022.6
async def test_setup_with_disabled_entry( async def test_setup_with_disabled_entry(
hass, mock_device_tracker_conf, caplog hass, mock_device_tracker_conf, caplog
) -> None: ) -> None:
@ -372,11 +392,11 @@ async def test_setup_with_disabled_entry(
topic = "location/jan" topic = "location/jan"
config = { config = {
DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {"jan": topic}}, device_tracker.DOMAIN: {CONF_PLATFORM: "mqtt", "devices": {"jan": topic}},
} }
hass.config.components = {"mqtt", "zone"} 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() await hass.async_block_till_done()
assert ( 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 from unittest.mock import patch
import pytest 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.const import DOMAIN as MQTT_DOMAIN
from homeassistant.components.mqtt.discovery import ALREADY_DISCOVERED from homeassistant.components.mqtt.discovery import ALREADY_DISCOVERED
from homeassistant.const import STATE_HOME, STATE_NOT_HOME, STATE_UNKNOWN, Platform from homeassistant.const import STATE_HOME, STATE_NOT_HOME, STATE_UNKNOWN, Platform
from homeassistant.setup import async_setup_component 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 from tests.common import async_fire_mqtt_message, mock_device_registry, mock_registry
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
mqtt.DOMAIN: {
device_tracker.DOMAIN: { device_tracker.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
} }
}
} }
@ -433,3 +437,18 @@ async def test_setting_blocked_attribute_via_mqtt_json_message(
DEFAULT_CONFIG, DEFAULT_CONFIG,
None, 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.""" """Test registry monitoring hook is removed after a reload."""
await mqtt_mock_entry_no_yaml_config() 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 # discover an entity that is not enabled by default
config1 = { config1 = {
"name": "sbfspot_12345", "name": "sbfspot_12345",

View File

@ -5,7 +5,7 @@ from unittest.mock import patch
import pytest import pytest
from voluptuous.error import MultipleInvalid from voluptuous.error import MultipleInvalid
from homeassistant.components import fan from homeassistant.components import fan, mqtt
from homeassistant.components.fan import ( from homeassistant.components.fan import (
ATTR_OSCILLATING, ATTR_OSCILLATING,
ATTR_PERCENTAGE, ATTR_PERCENTAGE,
@ -59,7 +59,7 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, 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 from tests.components.fan import common
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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) @pytest.fixture(autouse=True)
def fan_platform_only(): def fan_platform_only():
@ -83,18 +89,15 @@ def fan_platform_only():
yield yield
async def test_fail_setup_if_no_command_topic( async def test_fail_setup_if_no_command_topic(hass, caplog):
hass, caplog, mqtt_mock_entry_no_yaml_config
):
"""Test if command fails with command topic.""" """Test if command fails with command topic."""
assert await async_setup_component( assert not await async_setup_component(
hass, fan.DOMAIN, {fan.DOMAIN: {"platform": "mqtt", "name": "test"}} 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 ( 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 in caplog.text
) )
@ -105,10 +108,10 @@ async def test_controlling_state_via_topic(
"""Test the controlling state via topic.""" """Test the controlling state via topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -135,6 +138,7 @@ async def test_controlling_state_via_topic(
"payload_reset_percentage": "rEset_percentage", "payload_reset_percentage": "rEset_percentage",
"payload_reset_preset_mode": "rEset_preset_mode", "payload_reset_preset_mode": "rEset_preset_mode",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic using an alternate speed range."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: [ fan.DOMAIN: [
{ {
"platform": "mqtt",
"name": "test1", "name": "test1",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic1", "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, "speed_range_max": 100,
}, },
{ {
"platform": "mqtt",
"name": "test2", "name": "test2",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic2", "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, "speed_range_max": 200,
}, },
{ {
"platform": "mqtt",
"name": "test3", "name": "test3",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic3", "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, "speed_range_max": 1023,
}, },
] ]
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic without percentage topics."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -306,6 +309,7 @@ async def test_controlling_state_via_topic_no_percentage_topics(
"breeze", "breeze",
], ],
} }
}
}, },
) )
await hass.async_block_till_done() 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).""" """Test the controlling state via topic and JSON message (percentage mode)."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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_min": 1,
"speed_range_max": 100, "speed_range_max": 100,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic and JSON message using a shared topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "shared-state-topic", "state_topic": "shared-state-topic",
"command_topic": "command-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_min": 1,
"speed_range_max": 100, "speed_range_max": 100,
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -540,10 +546,10 @@ async def test_sending_mqtt_commands_and_optimistic(
"""Test optimistic mode without state topic.""" """Test optimistic mode without state topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_off": "StAtE_OfF", "payload_off": "StAtE_OfF",
@ -559,6 +565,7 @@ async def test_sending_mqtt_commands_and_optimistic(
"silent", "silent",
], ],
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic using an alternate speed range."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: [ fan.DOMAIN: [
{ {
"platform": "mqtt",
"name": "test1", "name": "test1",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic1", "percentage_state_topic": "percentage-state-topic1",
@ -677,7 +684,6 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(
"speed_range_max": 3, "speed_range_max": 3,
}, },
{ {
"platform": "mqtt",
"name": "test2", "name": "test2",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic2", "percentage_state_topic": "percentage-state-topic2",
@ -686,7 +692,6 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(
"speed_range_max": 200, "speed_range_max": 200,
}, },
{ {
"platform": "mqtt",
"name": "test3", "name": "test3",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_state_topic": "percentage-state-topic3", "percentage_state_topic": "percentage-state-topic3",
@ -695,6 +700,7 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(
"speed_range_max": 1023, "speed_range_max": 1023,
}, },
] ]
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test optimistic mode without state topic without legacy speed command topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic", "percentage_command_topic": "percentage-command-topic",
@ -785,6 +791,7 @@ async def test_sending_mqtt_commands_and_optimistic_no_legacy(
"silent", "silent",
], ],
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test optimistic mode without state topic without legacy speed command topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"command_template": "state: {{ value }}", "command_template": "state: {{ value }}",
@ -921,6 +928,7 @@ async def test_sending_mqtt_command_templates_(
"silent", "silent",
], ],
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test optimistic mode without state topic without percentage command topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-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", "high",
], ],
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test optimistic mode with state topic and turn on attributes."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -1125,6 +1134,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
], ],
"optimistic": True, "optimistic": True,
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1355,7 +1365,7 @@ async def test_encoding_subscribable_topics(
attribute_value, attribute_value,
): ):
"""Test handling of incoming encoded payload.""" """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[ATTR_PRESET_MODES] = ["eco", "auto"]
config[CONF_PRESET_MODE_COMMAND_TOPIC] = "fan/some_preset_mode_command_topic" config[CONF_PRESET_MODE_COMMAND_TOPIC] = "fan/some_preset_mode_command_topic"
config[CONF_PERCENTAGE_COMMAND_TOPIC] = "fan/some_percentage_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.""" """Test attributes."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
fan.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
fan.DOMAIN: { fan.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"oscillation_command_topic": "oscillation-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", "silent",
], ],
} }
}
}, },
) )
await hass.async_block_till_done() 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 assert state.attributes.get(fan.ATTR_OSCILLATING) is False
async def test_supported_features(hass, mqtt_mock_entry_with_yaml_config): @pytest.mark.parametrize(
"""Test optimistic mode without state topic.""" "name,config,success,features",
assert await async_setup_component( [
hass, (
fan.DOMAIN, "test1",
{ {
fan.DOMAIN: [
{
"platform": "mqtt",
"name": "test1", "name": "test1",
"command_topic": "command-topic", "command_topic": "command-topic",
}, },
True,
0,
),
(
"test2",
{ {
"platform": "mqtt",
"name": "test2", "name": "test2",
"command_topic": "command-topic", "command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic", "oscillation_command_topic": "oscillation-command-topic",
}, },
True,
fan.SUPPORT_OSCILLATE,
),
(
"test3",
{ {
"platform": "mqtt", "name": "test3",
"name": "test3b",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic", "percentage_command_topic": "percentage-command-topic",
}, },
True,
fan.SUPPORT_SET_SPEED,
),
(
"test4",
{ {
"platform": "mqtt", "name": "test4",
"name": "test3c1",
"command_topic": "command-topic", "command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic", "preset_mode_command_topic": "preset-mode-command-topic",
}, },
False,
None,
),
(
"test5",
{ {
"platform": "mqtt", "name": "test5",
"name": "test3c2",
"command_topic": "command-topic", "command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic", "preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["eco", "auto"], "preset_modes": ["eco", "auto"],
}, },
True,
fan.SUPPORT_PRESET_MODE,
),
(
"test6",
{ {
"platform": "mqtt", "name": "test6",
"name": "test3c3",
"command_topic": "command-topic", "command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic", "preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["eco", "smart", "auto"], "preset_modes": ["eco", "smart", "auto"],
}, },
True,
fan.SUPPORT_PRESET_MODE,
),
(
"test7",
{ {
"platform": "mqtt", "name": "test7",
"name": "test4pcta",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic", "percentage_command_topic": "percentage-command-topic",
}, },
True,
fan.SUPPORT_SET_SPEED,
),
(
"test8",
{ {
"platform": "mqtt", "name": "test8",
"name": "test4pctb",
"command_topic": "command-topic", "command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic", "oscillation_command_topic": "oscillation-command-topic",
"percentage_command_topic": "percentage-command-topic", "percentage_command_topic": "percentage-command-topic",
}, },
True,
fan.SUPPORT_OSCILLATE | fan.SUPPORT_SET_SPEED,
),
(
"test9",
{ {
"platform": "mqtt", "name": "test9",
"name": "test5pr_ma",
"command_topic": "command-topic", "command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic", "preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["Mode1", "Mode2", "Mode3"], "preset_modes": ["Mode1", "Mode2", "Mode3"],
}, },
True,
fan.SUPPORT_PRESET_MODE,
),
(
"test10",
{ {
"platform": "mqtt", "name": "test10",
"name": "test5pr_mb",
"command_topic": "command-topic", "command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic", "preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["whoosh", "silent", "auto"], "preset_modes": ["whoosh", "silent", "auto"],
}, },
True,
fan.SUPPORT_PRESET_MODE,
),
(
"test11",
{ {
"platform": "mqtt", "name": "test11",
"name": "test5pr_mc",
"command_topic": "command-topic", "command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic", "oscillation_command_topic": "oscillation-command-topic",
"preset_mode_command_topic": "preset-mode-command-topic", "preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["Mode1", "Mode2", "Mode3"], "preset_modes": ["Mode1", "Mode2", "Mode3"],
}, },
True,
fan.SUPPORT_PRESET_MODE | fan.SUPPORT_OSCILLATE,
),
(
"test12",
{ {
"platform": "mqtt", "name": "test12",
"name": "test6spd_range_a",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic", "percentage_command_topic": "percentage-command-topic",
"speed_range_min": 1, "speed_range_min": 1,
"speed_range_max": 40, "speed_range_max": 40,
}, },
True,
fan.SUPPORT_SET_SPEED,
),
(
"test13",
{ {
"platform": "mqtt", "name": "test13",
"name": "test6spd_range_b",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic", "percentage_command_topic": "percentage-command-topic",
"speed_range_min": 50, "speed_range_min": 50,
"speed_range_max": 40, "speed_range_max": 40,
}, },
False,
None,
),
(
"test14",
{ {
"platform": "mqtt", "name": "test14",
"name": "test6spd_range_c",
"command_topic": "command-topic", "command_topic": "command-topic",
"percentage_command_topic": "percentage-command-topic", "percentage_command_topic": "percentage-command-topic",
"speed_range_min": 0, "speed_range_min": 0,
"speed_range_max": 40, "speed_range_max": 40,
}, },
False,
None,
),
(
"test15",
{ {
"platform": "mqtt",
"name": "test7reset_payload_in_preset_modes_a", "name": "test7reset_payload_in_preset_modes_a",
"command_topic": "command-topic", "command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic", "preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["auto", "smart", "normal", "None"], "preset_modes": ["auto", "smart", "normal", "None"],
}, },
False,
None,
),
(
"test16",
{ {
"platform": "mqtt", "name": "test16",
"name": "test7reset_payload_in_preset_modes_b",
"command_topic": "command-topic", "command_topic": "command-topic",
"preset_mode_command_topic": "preset-mode-command-topic", "preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": ["whoosh", "silent", "auto", "None"], "preset_modes": ["whoosh", "silent", "auto", "None"],
"payload_reset_preset_mode": "normal", "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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
state = hass.states.get("fan.test1") state = hass.states.get(f"fan.{name}")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 0 assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == features
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
async def test_availability_when_connection_lost( 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, fan.DOMAIN, DEFAULT_CONFIG 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique_id option only creates one fan per id.""" """Test unique_id option only creates one fan per id."""
config = { config = {
mqtt.DOMAIN: {
fan.DOMAIN: [ fan.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_topic": "test_topic", "command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_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( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, fan.DOMAIN, config 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.""" """Test publishing MQTT payload with different encoding."""
domain = fan.DOMAIN domain = fan.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain]) config = copy.deepcopy(DEFAULT_CONFIG)
if topic == "preset_mode_command_topic": 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( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = fan.DOMAIN domain = fan.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = fan.DOMAIN domain = fan.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = fan.DOMAIN platform = fan.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = fan.DOMAIN domain = fan.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 import pytest
from voluptuous.error import MultipleInvalid from voluptuous.error import MultipleInvalid
from homeassistant.components import humidifier from homeassistant.components import humidifier, mqtt
from homeassistant.components.humidifier import ( from homeassistant.components.humidifier import (
ATTR_HUMIDITY, ATTR_HUMIDITY,
ATTR_MODE, ATTR_MODE,
@ -61,22 +61,28 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
from tests.common import async_fire_mqtt_message from tests.common import async_fire_mqtt_message
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
mqtt.DOMAIN: {
humidifier.DOMAIN: { humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-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) @pytest.fixture(autouse=True)
def humidifer_platform_only(): 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) 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.""" """Test if command fails with command topic."""
assert await async_setup_component( assert not await async_setup_component(
hass, hass,
humidifier.DOMAIN, mqtt.DOMAIN,
{humidifier.DOMAIN: {"platform": "mqtt", "name": "test"}}, {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( async def test_controlling_state_via_topic(
@ -144,10 +151,10 @@ async def test_controlling_state_via_topic(
"""Test the controlling state via topic.""" """Test the controlling state via topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
humidifier.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
humidifier.DOMAIN: { humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -168,6 +175,7 @@ async def test_controlling_state_via_topic(
"payload_reset_humidity": "rEset_humidity", "payload_reset_humidity": "rEset_humidity",
"payload_reset_mode": "rEset_mode", "payload_reset_mode": "rEset_mode",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic and JSON message."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
humidifier.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
humidifier.DOMAIN: { humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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 }}", "target_humidity_state_template": "{{ value_json.val }}",
"mode_state_template": "{{ value_json.val }}", "mode_state_template": "{{ value_json.val }}",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic and JSON message using a shared topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
humidifier.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
humidifier.DOMAIN: { humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "shared-state-topic", "state_topic": "shared-state-topic",
"command_topic": "command-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 }}", "target_humidity_state_template": "{{ value_json.humidity }}",
"mode_state_template": "{{ value_json.mode }}", "mode_state_template": "{{ value_json.mode }}",
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -414,10 +424,10 @@ async def test_sending_mqtt_commands_and_optimistic(
"""Test optimistic mode without state topic.""" """Test optimistic mode without state topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
humidifier.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
humidifier.DOMAIN: { humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_off": "StAtE_OfF", "payload_off": "StAtE_OfF",
@ -430,6 +440,7 @@ async def test_sending_mqtt_commands_and_optimistic(
"baby", "baby",
], ],
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Testing command templates with optimistic mode without state topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
humidifier.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
humidifier.DOMAIN: { humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"command_template": "state: {{ value }}", "command_template": "state: {{ value }}",
@ -527,6 +538,7 @@ async def test_sending_mqtt_command_templates_(
"sleep", "sleep",
], ],
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test optimistic mode with state topic and turn on attributes."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
humidifier.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
humidifier.DOMAIN: { humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -625,6 +637,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
], ],
"optimistic": True, "optimistic": True,
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -737,7 +750,7 @@ async def test_encoding_subscribable_topics(
attribute_value, attribute_value,
): ):
"""Test handling of incoming encoded payload.""" """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["modes"] = ["eco", "auto"]
config[CONF_MODE_COMMAND_TOPIC] = "humidifier/some_mode_command_topic" config[CONF_MODE_COMMAND_TOPIC] = "humidifier/some_mode_command_topic"
await help_test_encoding_subscribable_topics( await help_test_encoding_subscribable_topics(
@ -757,10 +770,10 @@ async def test_attributes(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test attributes.""" """Test attributes."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
humidifier.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
humidifier.DOMAIN: { humidifier.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"mode_command_topic": "mode-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", "baby",
], ],
} }
}
}, },
) )
await hass.async_block_till_done() 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 assert state.attributes.get(humidifier.ATTR_MODE) is None
async def test_invalid_configurations(hass, mqtt_mock_entry_with_yaml_config, caplog): @pytest.mark.parametrize(
"""Test invalid configurations.""" "config,valid",
assert await async_setup_component( [
hass, (
humidifier.DOMAIN,
{ {
humidifier.DOMAIN: [
{
"platform": "mqtt",
"name": "test_valid_1", "name": "test_valid_1",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
}, },
True,
),
(
{ {
"platform": "mqtt",
"name": "test_valid_2", "name": "test_valid_2",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
"device_class": "humidifier", "device_class": "humidifier",
}, },
True,
),
(
{ {
"platform": "mqtt",
"name": "test_valid_3", "name": "test_valid_3",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
"device_class": "dehumidifier", "device_class": "dehumidifier",
}, },
True,
),
(
{ {
"platform": "mqtt",
"name": "test_invalid_device_class", "name": "test_invalid_device_class",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
"device_class": "notsupporedSpeci@l", "device_class": "notsupporedSpeci@l",
}, },
False,
),
(
{ {
"platform": "mqtt",
"name": "test_mode_command_without_modes", "name": "test_mode_command_without_modes",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
"mode_command_topic": "mode-command-topic", "mode_command_topic": "mode-command-topic",
}, },
False,
),
(
{ {
"platform": "mqtt",
"name": "test_invalid_humidity_min_max_1", "name": "test_invalid_humidity_min_max_1",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
"min_humidity": 0, "min_humidity": 0,
"max_humidity": 101, "max_humidity": 101,
}, },
False,
),
(
{ {
"platform": "mqtt",
"name": "test_invalid_humidity_min_max_2", "name": "test_invalid_humidity_min_max_2",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
"max_humidity": 20, "max_humidity": 20,
"min_humidity": 40, "min_humidity": 40,
}, },
False,
),
(
{ {
"platform": "mqtt",
"name": "test_invalid_mode_is_reset", "name": "test_invalid_mode_is_reset",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
"mode_command_topic": "mode-command-topic", "mode_command_topic": "mode-command-topic",
"modes": ["eco", "None"], "modes": ["eco", "None"],
}, },
] False,
}, ),
) ],
await hass.async_block_till_done() )
await mqtt_mock_entry_with_yaml_config() async def test_validity_configurations(hass, config, valid):
assert hass.states.get("humidifier.test_valid_1") is not None """Test validity of configurations."""
assert hass.states.get("humidifier.test_valid_2") is not None assert (
assert hass.states.get("humidifier.test_valid_3") is not None await async_setup_component(
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(
hass, 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", "name": "test1",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
}, },
True,
0,
),
(
"test2",
{ {
"platform": "mqtt",
"name": "test2", "name": "test2",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
"mode_command_topic": "mode-command-topic", "mode_command_topic": "mode-command-topic",
"modes": ["eco", "auto"], "modes": ["eco", "auto"],
}, },
True,
humidifier.SUPPORT_MODES,
),
(
"test3",
{ {
"platform": "mqtt",
"name": "test3", "name": "test3",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
}, },
True,
0,
),
(
"test4",
{ {
"platform": "mqtt",
"name": "test4", "name": "test4",
"command_topic": "command-topic", "command_topic": "command-topic",
"target_humidity_command_topic": "humidity-command-topic", "target_humidity_command_topic": "humidity-command-topic",
"mode_command_topic": "mode-command-topic", "mode_command_topic": "mode-command-topic",
"modes": ["eco", "auto"], "modes": ["eco", "auto"],
}, },
True,
humidifier.SUPPORT_MODES,
),
(
"test5",
{ {
"platform": "mqtt",
"name": "test5", "name": "test5",
"command_topic": "command-topic", "command_topic": "command-topic",
}, },
False,
None,
),
(
"test6",
{ {
"platform": "mqtt",
"name": "test6", "name": "test6",
"target_humidity_command_topic": "humidity-command-topic", "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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
state = hass.states.get("humidifier.test1") state = hass.states.get(f"humidifier.{name}")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 0 assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == features
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
async def test_availability_when_connection_lost( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, hass,
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
caplog, 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique_id option only creates one fan per id.""" """Test unique_id option only creates one fan per id."""
config = { config = {
mqtt.DOMAIN: {
humidifier.DOMAIN: [ humidifier.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_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", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_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( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, humidifier.DOMAIN, config 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.""" """Test publishing MQTT payload with different encoding."""
domain = humidifier.DOMAIN domain = humidifier.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain]) config = copy.deepcopy(DEFAULT_CONFIG)
if topic == "mode_command_topic": if topic == "mode_command_topic":
config["modes"] = ["auto", "eco"] config[mqtt.DOMAIN][domain]["modes"] = ["auto", "eco"]
await help_test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = humidifier.DOMAIN domain = humidifier.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = humidifier.DOMAIN domain = humidifier.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = humidifier.DOMAIN platform = humidifier.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_config_schema_validation(hass): 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 platform = humidifier.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][platform])
config["name"] = "test" config["name"] = "test"
del config["platform"] CONFIG_SCHEMA({mqtt.DOMAIN: {platform: config}})
CONFIG_SCHEMA({DOMAIN: {platform: config}}) CONFIG_SCHEMA({mqtt.DOMAIN: {platform: [config]}})
CONFIG_SCHEMA({DOMAIN: {platform: [config]}})
with pytest.raises(MultipleInvalid): 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): async def test_unload_config_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = humidifier.DOMAIN domain = humidifier.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, template from homeassistant.helpers import device_registry as dr, template
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import async_get_platforms
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from .test_common import ( from .test_common import (
help_test_entry_reload_with_new_config, help_test_entry_reload_with_new_config,
help_test_reload_with_config,
help_test_setup_manual_entity_from_yaml, 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" 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): async def test_command_template_variables(hass, mqtt_mock_entry_with_yaml_config):
"""Test the rendering of entity variables.""" """Test the rendering of entity variables."""
topic = "test/select" 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( assert await async_setup_component(
hass, hass,
"select", mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
"select": { "select": {
"platform": "mqtt",
"command_topic": topic, "command_topic": topic,
"name": "Test Select", "name": "Test Select",
"options": ["milk", "beer"], "options": ["milk", "beer"],
"command_template": '{"option": "{{ value }}", "entity_id": "{{ entity_id }}", "name": "{{ name }}", "this_object_state": "{{ this.state }}"}', "command_template": '{"option": "{{ value }}", "entity_id": "{{ entity_id }}", "name": "{{ name }}", "this_object_state": "{{ this.state }}"}',
} }
}
}, },
) )
await hass.async_block_till_done() 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", []) @patch("homeassistant.components.mqtt.PLATFORMS", [])
async def test_setup_manual_mqtt_with_platform_key(hass, caplog): async def test_setup_manual_mqtt_with_platform_key(hass, caplog):
"""Test set up a manual MQTT item with a platform key.""" """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): 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 ( assert (
"Invalid config for [mqtt]: [platform] is an invalid option for [mqtt]" "Invalid config for [mqtt]: [platform] is an invalid option for [mqtt]"
in caplog.text in caplog.text
@ -1495,9 +1507,9 @@ async def test_setup_manual_mqtt_with_platform_key(hass, caplog):
@patch("homeassistant.components.mqtt.PLATFORMS", []) @patch("homeassistant.components.mqtt.PLATFORMS", [])
async def test_setup_manual_mqtt_with_invalid_config(hass, caplog): async def test_setup_manual_mqtt_with_invalid_config(hass, caplog):
"""Test set up a manual MQTT item with an invalid config.""" """Test set up a manual MQTT item with an invalid config."""
config = {"name": "test"} config = {mqtt.DOMAIN: {"light": {"name": "test"}}}
with pytest.raises(AssertionError): 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 ( assert (
"Invalid config for [mqtt]: required key not provided @ data['mqtt']['light'][0]['command_topic']." "Invalid config for [mqtt]: required key not provided @ data['mqtt']['light'][0]['command_topic']."
" Got None. (See ?, line ?)" in caplog.text " 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", []) @patch("homeassistant.components.mqtt.PLATFORMS", [])
async def test_setup_manual_mqtt_empty_platform(hass, caplog): async def test_setup_manual_mqtt_empty_platform(hass, caplog):
"""Test set up a manual MQTT platform without items.""" """Test set up a manual MQTT platform without items."""
config = [] config = {mqtt.DOMAIN: {"light": []}}
await help_test_setup_manual_entity_from_yaml(hass, "light", config) await help_test_setup_manual_entity_from_yaml(hass, config)
assert "voluptuous.error.MultipleInvalid" not in caplog.text assert "voluptuous.error.MultipleInvalid" not in caplog.text
@ -1766,14 +1778,14 @@ async def test_delayed_birth_message(
await hass.async_block_till_done() await hass.async_block_till_done()
mqtt_component_mock = MagicMock( mqtt_component_mock = MagicMock(
return_value=hass.data["mqtt"], return_value=hass.data["mqtt"].client,
spec_set=hass.data["mqtt"], spec_set=hass.data["mqtt"].client,
wraps=hass.data["mqtt"], wraps=hass.data["mqtt"].client,
) )
mqtt_component_mock._mqttc = mqtt_client_mock mqtt_component_mock._mqttc = mqtt_client_mock
hass.data["mqtt"] = mqtt_component_mock hass.data["mqtt"].client = mqtt_component_mock
mqtt_mock = hass.data["mqtt"] mqtt_mock = hass.data["mqtt"].client
mqtt_mock.reset_mock() mqtt_mock.reset_mock()
async def wait_birth(topic, payload, qos): 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() await mqtt_mock_entry_no_yaml_config()
config_sensor = { config_sensor = {
"device": {"identifiers": ["0AFFD2"]}, "device": {"identifiers": ["0AFFD2"]},
"platform": "mqtt",
"state_topic": "foobar/sensor", "state_topic": "foobar/sensor",
"unique_id": "unique", "unique_id": "unique",
} }
config_trigger = { config_trigger = {
"automation_type": "trigger", "automation_type": "trigger",
"device": {"identifiers": ["0AFFD2"]}, "device": {"identifiers": ["0AFFD2"]},
"platform": "mqtt",
"topic": "test-topic1", "topic": "test-topic1",
"type": "foo", "type": "foo",
"subtype": "bar", "subtype": "bar",
} }
data_sensor = json.dumps(config_sensor) data_sensor = json.dumps(config_sensor)
data_trigger = json.dumps(config_trigger) 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(hass, "homeassistant/sensor/bla/config", data_sensor)
async_fire_mqtt_message( 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() await mqtt_mock_entry_no_yaml_config()
config = { config = {
"device": {"identifiers": ["0AFFD2"]}, "device": {"identifiers": ["0AFFD2"]},
"platform": "mqtt",
"topic": "foobar/image", "topic": "foobar/image",
"unique_id": "unique", "unique_id": "unique",
} }
data = json.dumps(config) data = json.dumps(config)
config["platform"] = mqtt.DOMAIN
async_fire_mqtt_message(hass, "homeassistant/camera/bla/config", data) async_fire_mqtt_message(hass, "homeassistant/camera/bla/config", data)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -2397,7 +2408,9 @@ async def test_debug_info_non_mqtt(
device_id=device_entry.id, 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) debug_info_data = debug_info.info_for_device(hass, device_entry.id)
assert len(debug_info_data["entities"]) == 0 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() await mqtt_mock_entry_no_yaml_config()
config = { config = {
"device": {"identifiers": ["helloworld"]}, "device": {"identifiers": ["helloworld"]},
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "sensor/#", "state_topic": "sensor/#",
"unique_id": "veryunique", "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() await mqtt_mock_entry_no_yaml_config()
config = { config = {
"device": {"identifiers": ["helloworld"]}, "device": {"identifiers": ["helloworld"]},
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "sensor/#", "state_topic": "sensor/#",
"unique_id": "veryunique", "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() await mqtt_mock_entry_no_yaml_config()
config = { config = {
"device": {"identifiers": ["helloworld"]}, "device": {"identifiers": ["helloworld"]},
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "sensor/status", "state_topic": "sensor/status",
"availability_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() await mqtt_mock_entry_no_yaml_config()
config = { config = {
"device": {"identifiers": ["helloworld"]}, "device": {"identifiers": ["helloworld"]},
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "sensor/#", "state_topic": "sensor/#",
"unique_id": "veryunique", "unique_id": "veryunique",
@ -2708,6 +2717,8 @@ async def test_subscribe_connection_status(
assert mqtt_connected_calls[1] is False 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( async def test_one_deprecation_warning_per_platform(
hass, mqtt_mock_entry_with_yaml_config, caplog 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]) @patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT])
async def test_reload_entry_with_new_config(hass, tmp_path): async def test_reload_entry_with_new_config(hass, tmp_path):
"""Test reloading the config entry with a new yaml config.""" """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 = { config_yaml_new = {
"mqtt": { "mqtt": {
"light": [{"name": "test_new_modern", "command_topic": "test-topic_new"}] "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": [ "light": [
{ {
"platform": "mqtt", "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 assert hass.states.get("light.test_old1") is not None
await help_test_entry_reload_with_new_config(hass, tmp_path, config_yaml_new) 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]) @patch("homeassistant.components.mqtt.PLATFORMS", [Platform.LIGHT])
async def test_disabling_and_enabling_entry(hass, tmp_path, caplog): async def test_disabling_and_enabling_entry(hass, tmp_path, caplog):
"""Test disabling and enabling the config entry.""" """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 = { config_yaml_new = {
"mqtt": { "mqtt": {
"light": [{"name": "test_new_modern", "command_topic": "test-topic_new"}] "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": [ "light": [
{ {
"platform": "mqtt", "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 assert hass.states.get("light.test_old1") is not None
mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] 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 hass, tmp_path, caplog, config, unique
): ):
"""Test setup manual items is generating unique id's.""" """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.test1") is not None
assert (hass.states.get("light.test2") is not None) == unique 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 " "MQTT config entry: {'protocol'}. Add them to configuration.yaml if they "
"are needed" "are needed"
) in caplog.text ) 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 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.const import CONF_COMMAND_TOPIC
from homeassistant.components.mqtt.vacuum import schema_legacy as mqttvacuum from homeassistant.components.mqtt.vacuum import schema_legacy as mqttvacuum
from homeassistant.components.mqtt.vacuum.schema import services_to_strings 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_setting_blocked_attribute_via_mqtt_json_message,
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, 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, 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 from tests.components.vacuum import common
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
CONF_PLATFORM: "mqtt", mqtt.DOMAIN: {
vacuum.DOMAIN: {
CONF_NAME: "mqtttest", CONF_NAME: "mqtttest",
CONF_COMMAND_TOPIC: "vacuum/command", CONF_COMMAND_TOPIC: "vacuum/command",
mqttvacuum.CONF_SEND_COMMAND_TOPIC: "vacuum/send_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_FAN_SPEED_TEMPLATE: "{{ value_json.fan_speed }}",
mqttvacuum.CONF_SET_FAN_SPEED_TOPIC: "vacuum/set_fan_speed", mqttvacuum.CONF_SET_FAN_SPEED_TOPIC: "vacuum/set_fan_speed",
mqttvacuum.CONF_FAN_SPEED_LIST: ["min", "medium", "high", "max"], 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) @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): async def test_default_supported_features(hass, mqtt_mock_entry_with_yaml_config):
"""Test that the correct supported features.""" """Test that the correct supported features."""
assert await async_setup_component( assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
hass, vacuum.DOMAIN, {vacuum.DOMAIN: DEFAULT_CONFIG}
)
await hass.async_block_till_done() await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
entity = hass.states.get("vacuum.mqtttest") 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): async def test_all_commands(hass, mqtt_mock_entry_with_yaml_config):
"""Test simple commands to the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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 hass, mqtt_mock_entry_with_yaml_config
): ):
"""Test commands which are not supported by the vacuum.""" """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"] services = mqttvacuum.STRING_TO_SERVICE["status"]
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
services, SERVICE_TO_STRING 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 hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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 hass, mqtt_mock_entry_with_yaml_config
): ):
"""Test attributes which are not supported by the vacuum.""" """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"] services = mqttvacuum.STRING_TO_SERVICE["turn_on"]
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
services, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_status(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_status_battery(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_status_cleaning(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_status_docked(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_status_charging(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_status_fan_speed(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_status_fan_speed_list(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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. 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 services = ALL_SERVICES - VacuumEntityFeature.FAN_SPEED
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
services, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_status_error(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_battery_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test that you can use non-default templates for battery_level.""" """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( config.update(
{ {
mqttvacuum.CONF_SUPPORTED_FEATURES: services_to_strings( 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): 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.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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" 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.""" """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) config.pop(mqttvacuum.CONF_BATTERY_LEVEL_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) assert not await async_setup_component(
await hass.async_block_till_done() hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
await mqtt_mock_entry_no_yaml_config() )
state = hass.states.get("vacuum.mqtttest")
assert state is None
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.""" """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) config.pop(mqttvacuum.CONF_CHARGING_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) assert not await async_setup_component(
await hass.async_block_till_done() hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
await mqtt_mock_entry_no_yaml_config() )
state = hass.states.get("vacuum.mqtttest")
assert state is None
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.""" """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) config.pop(mqttvacuum.CONF_CLEANING_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) assert not await async_setup_component(
await hass.async_block_till_done() hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
await mqtt_mock_entry_no_yaml_config() )
state = hass.states.get("vacuum.mqtttest")
assert state is None
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.""" """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) config.pop(mqttvacuum.CONF_DOCKED_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) assert not await async_setup_component(
await hass.async_block_till_done() hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
await mqtt_mock_entry_no_yaml_config() )
state = hass.states.get("vacuum.mqtttest")
assert state is None
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.""" """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) config.pop(mqttvacuum.CONF_ERROR_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) assert not await async_setup_component(
await hass.async_block_till_done() hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
await mqtt_mock_entry_no_yaml_config() )
state = hass.states.get("vacuum.mqtttest")
assert state is None
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.""" """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) config.pop(mqttvacuum.CONF_FAN_SPEED_TEMPLATE)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) assert not await async_setup_component(
await hass.async_block_till_done() hass, mqtt.DOMAIN, {mqtt.DOMAIN: {vacuum.DOMAIN: config}}
await mqtt_mock_entry_no_yaml_config() )
state = hass.states.get("vacuum.mqtttest")
assert state is None
async def test_availability_when_connection_lost( 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, vacuum.DOMAIN, DEFAULT_CONFIG_2 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one vacuum per unique_id.""" """Test unique id option only creates one vacuum per unique_id."""
config = { config = {
mqtt.DOMAIN: {
vacuum.DOMAIN: [ vacuum.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"command_topic": "test_topic", "command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"command_topic": "test_topic", "command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
] ]
} }
}
await help_test_unique_id( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, config 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): async def test_discovery_removal_vacuum(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered vacuum.""" """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( await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, vacuum.DOMAIN, data 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): async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config):
"""Test MQTT subscriptions are managed when entity_id is updated.""" """Test MQTT subscriptions are managed when entity_id is updated."""
config = { config = {
mqtt.DOMAIN: {
vacuum.DOMAIN: { vacuum.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"battery_level_topic": "test-topic", "battery_level_topic": "test-topic",
"battery_level_template": "{{ value_json.battery_level }}", "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", "availability_topic": "avty-topic",
} }
} }
}
await help_test_entity_id_update_subscriptions( await help_test_entity_id_update_subscriptions(
hass, hass,
mqtt_mock_entry_with_yaml_config, 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): async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT debug info.""" """Test MQTT debug info."""
config = { config = {
mqtt.DOMAIN: {
vacuum.DOMAIN: { vacuum.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"battery_level_topic": "state-topic", "battery_level_topic": "state-topic",
"battery_level_template": "{{ value_json.battery_level }}", "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", "payload_turn_on": "ON",
} }
} }
}
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, hass,
mqtt_mock_entry_no_yaml_config, mqtt_mock_entry_no_yaml_config,
@ -875,7 +905,7 @@ async def test_publishing_with_custom_encoding(
"""Test publishing MQTT payload with different encoding.""" """Test publishing MQTT payload with different encoding."""
domain = vacuum.DOMAIN domain = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG) config = deepcopy(DEFAULT_CONFIG)
config["supported_features"] = [ config[mqtt.DOMAIN][domain]["supported_features"] = [
"turn_on", "turn_on",
"turn_off", "turn_off",
"clean_spot", "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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = vacuum.DOMAIN domain = vacuum.DOMAIN
config = DEFAULT_CONFIG config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -944,7 +976,8 @@ async def test_encoding_subscribable_topics(
attribute_value, attribute_value,
): ):
"""Test handling of incoming encoded payload.""" """Test handling of incoming encoded payload."""
config = deepcopy(DEFAULT_CONFIG) domain = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config[CONF_SUPPORTED_FEATURES] = [ config[CONF_SUPPORTED_FEATURES] = [
"turn_on", "turn_on",
"turn_off", "turn_off",
@ -976,8 +1009,18 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = vacuum.DOMAIN 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" config["name"] = "test"
del config["platform"] assert await async_setup_component(hass, domain, {domain: config})
await help_test_setup_manual_entity_from_yaml(hass, platform, config) await hass.async_block_till_done()
assert hass.states.get(f"{platform}.test") is not None 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: Configuration for RGB Version with brightness:
light: mqtt:
platform: mqtt light:
name: "Office Light RGB" - name: "Office Light RGB"
state_topic: "office/rgb1/light/status" state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch" command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status" brightness_state_topic: "office/rgb1/brightness/status"
@ -17,8 +17,9 @@ light:
Configuration for XY Version with brightness: Configuration for XY Version with brightness:
light: mqtt:
platform: mqtt light:
- platform: mqtt
name: "Office Light XY" name: "Office Light XY"
state_topic: "office/xy1/light/status" state_topic: "office/xy1/light/status"
command_topic: "office/xy1/light/switch" command_topic: "office/xy1/light/switch"
@ -32,9 +33,9 @@ light:
config without RGB: config without RGB:
light: mqtt:
platform: mqtt light:
name: "Office Light" - name: "Office Light"
state_topic: "office/rgb1/light/status" state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch" command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status" brightness_state_topic: "office/rgb1/brightness/status"
@ -45,9 +46,9 @@ light:
config without RGB and brightness: config without RGB and brightness:
light: mqtt:
platform: mqtt light:
name: "Office Light" - name: "Office Light"
state_topic: "office/rgb1/light/status" state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch" command_topic: "office/rgb1/light/switch"
qos: 0 qos: 0
@ -56,9 +57,9 @@ light:
config for RGB Version with brightness and scale: config for RGB Version with brightness and scale:
light: mqtt:
platform: mqtt light:
name: "Office Light RGB" - name: "Office Light RGB"
state_topic: "office/rgb1/light/status" state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch" command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status" brightness_state_topic: "office/rgb1/brightness/status"
@ -73,9 +74,9 @@ light:
config with brightness and color temp config with brightness and color temp
light: mqtt:
platform: mqtt light:
name: "Office Light Color Temp" - name: "Office Light Color Temp"
state_topic: "office/rgb1/light/status" state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch" command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status" brightness_state_topic: "office/rgb1/brightness/status"
@ -89,9 +90,9 @@ light:
config with brightness and effect config with brightness and effect
light: mqtt:
platform: mqtt light:
name: "Office Light Color Temp" - name: "Office Light Color Temp"
state_topic: "office/rgb1/light/status" state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch" command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status" brightness_state_topic: "office/rgb1/brightness/status"
@ -108,9 +109,9 @@ light:
config for RGB Version with RGB command template: config for RGB Version with RGB command template:
light: mqtt:
platform: mqtt light:
name: "Office Light RGB" - name: "Office Light RGB"
state_topic: "office/rgb1/light/status" state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch" command_topic: "office/rgb1/light/switch"
rgb_state_topic: "office/rgb1/rgb/status" rgb_state_topic: "office/rgb1/rgb/status"
@ -122,9 +123,9 @@ light:
Configuration for HS Version with brightness: Configuration for HS Version with brightness:
light: mqtt:
platform: mqtt light:
name: "Office Light HS" - name: "Office Light HS"
state_topic: "office/hs1/light/status" state_topic: "office/hs1/light/status"
command_topic: "office/hs1/light/switch" command_topic: "office/hs1/light/switch"
brightness_state_topic: "office/hs1/brightness/status" brightness_state_topic: "office/hs1/brightness/status"
@ -137,9 +138,9 @@ light:
Configuration with brightness command template: Configuration with brightness command template:
light: mqtt:
platform: mqtt light:
name: "Office Light" - name: "Office Light"
state_topic: "office/rgb1/light/status" state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch" command_topic: "office/rgb1/light/switch"
brightness_state_topic: "office/rgb1/brightness/status" brightness_state_topic: "office/rgb1/brightness/status"
@ -151,9 +152,9 @@ light:
Configuration with effect command template: Configuration with effect command template:
light: mqtt:
platform: mqtt light:
name: "Office Light Color Temp" - name: "Office Light Color Temp"
state_topic: "office/rgb1/light/status" state_topic: "office/rgb1/light/status"
command_topic: "office/rgb1/light/switch" command_topic: "office/rgb1/light/switch"
effect_state_topic: "office/rgb1/effect/status" effect_state_topic: "office/rgb1/effect/status"
@ -172,7 +173,7 @@ from unittest.mock import call, patch
import pytest import pytest
from homeassistant.components import light from homeassistant.components import light, mqtt
from homeassistant.components.mqtt.light.schema_basic import ( from homeassistant.components.mqtt.light.schema_basic import (
CONF_BRIGHTNESS_COMMAND_TOPIC, CONF_BRIGHTNESS_COMMAND_TOPIC,
CONF_COLOR_TEMP_COMMAND_TOPIC, CONF_COLOR_TEMP_COMMAND_TOPIC,
@ -222,21 +223,22 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
from tests.common import ( from tests.common import async_fire_mqtt_message, mock_restore_cache
assert_setup_component,
async_fire_mqtt_message,
mock_restore_cache,
)
from tests.components.light import common from tests.components.light import common
DEFAULT_CONFIG = { 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) @pytest.fixture(autouse=True)
def light_platform_only(): def light_platform_only():
@ -245,14 +247,15 @@ def light_platform_only():
yield 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.""" """Test if command fails with command topic."""
assert await async_setup_component( assert not await async_setup_component(
hass, light.DOMAIN, {light.DOMAIN: {"platform": "mqtt", "name": "test"}} 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( 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.""" """Test if there is no color and brightness if no topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test_light_rgb/status", "state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling of the state via topic."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test_light_rgb/status", "state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set", "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"] 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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.""" """Test handling of empty data via topic."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test_light_rgb/status", "state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set", "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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_brightness_controlling_scale(hass, mqtt_mock_entry_with_yaml_config):
"""Test the brightness controlling scale.""" """Test the brightness controlling scale."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test_scale/status", "state_topic": "test_scale/status",
"command_topic": "test_scale/set", "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_on": "on",
"payload_off": "off", "payload_off": "off",
} }
}
}, },
) )
await hass.async_block_till_done() 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 hass, mqtt_mock_entry_with_yaml_config
): ):
"""Test the brightness controlling scale.""" """Test the brightness controlling scale."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test_scale_rgb/status", "state_topic": "test_scale_rgb/status",
"command_topic": "test_scale_rgb/set", "command_topic": "test_scale_rgb/set",
@ -636,6 +637,7 @@ async def test_brightness_from_rgb_controlling_scale(
"payload_on": "on", "payload_on": "on",
"payload_off": "off", "payload_off": "off",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the state with a template."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test_light_rgb/status", "state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set", "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"] 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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.""" """Test the sending of command in optimistic mode."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"brightness_command_topic": "test_light_rgb/brightness/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,)) mock_restore_cache(hass, (fake_state,))
with assert_setup_component(1, light.DOMAIN): assert await async_setup_component(hass, mqtt.DOMAIN, {mqtt.DOMAIN: config})
assert await async_setup_component(hass, light.DOMAIN, config)
await hass.async_block_till_done() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test the sending of RGB command with template."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"rgb_command_topic": "test_light_rgb/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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test the sending of RGBW command with template."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"rgbw_command_topic": "test_light_rgb/rgbw/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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test the sending of RGBWW command with template."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"rgbww_command_topic": "test_light_rgb/rgbww/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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test the sending of Color Temp command with template."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light_color_temp/set", "command_topic": "test_light_color_temp/set",
"color_temp_command_topic": "test_light_color_temp/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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test on command being sent before brightness."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"brightness_command_topic": "test_light/bright", "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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test on command being sent after brightness."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"brightness_command_topic": "test_light/bright", "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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test on command being sent as only brightness."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"brightness_command_topic": "test_light/bright", "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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test brightness scale."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"brightness_command_topic": "test_light/bright", "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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test on command in RGB brightness mode."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"rgb_command_topic": "test_light/rgb", "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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test on command in RGBW brightness mode."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"rgbw_command_topic": "test_light/rgbw", "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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test on command in RGBWW brightness mode."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"rgbww_command_topic": "test_light/rgbww", "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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test on command in RGB brightness mode with RGB template."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"rgb_command_topic": "test_light/rgb", "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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test on command in RGBW brightness mode with RGBW template."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"rgbw_command_topic": "test_light/rgbw", "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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test on command in RGBWW brightness mode with RGBWW template."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"rgbww_command_topic": "test_light/rgbww", "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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test sending commands for RGB + white light."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "tasmota_B94927/cmnd/POWER", "command_topic": "tasmota_B94927/cmnd/POWER",
"state_value_template": "{{ value_json.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"] 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 hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test explicit color mode over mqtt."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test_light_rgb/status", "state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set", "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"] 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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.""" """Test templated explicit color mode over mqtt."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test_light_rgb/status", "state_topic": "test_light_rgb/status",
"command_topic": "test_light_rgb/set", "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"] 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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.""" """Test state updates for RGB + white light."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "tasmota_B94927/tele/STATE", "state_topic": "tasmota_B94927/tele/STATE",
"command_topic": "tasmota_B94927/cmnd/POWER", "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"] 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
@ -2075,7 +2056,6 @@ async def test_effect(hass, mqtt_mock_entry_with_yaml_config):
"""Test effect.""" """Test effect."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light/set", "command_topic": "test_light/set",
"effect_command_topic": "test_light/effect/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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, light.DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one light per unique_id.""" """Test unique id option only creates one light per unique_id."""
config = { config = {
mqtt.DOMAIN: {
light.DOMAIN: [ light.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_topic": "test_topic", "command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_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( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, config 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): async def test_discovery_deprecated(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test discovery of mqtt light with deprecated platform option.""" """Test discovery of mqtt light with deprecated platform option."""
await mqtt_mock_entry_no_yaml_config() 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.""" """Test setting min_mireds and max_mireds."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_max_mireds/set", "command_topic": "test_max_mireds/set",
"color_temp_command_topic": "test_max_mireds/color_temp/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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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.""" """Test publishing MQTT payload with different encoding."""
domain = light.DOMAIN domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain]) config = copy.deepcopy(DEFAULT_CONFIG)
if topic == "effect_command_topic": 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": 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( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = light.DOMAIN domain = light.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = light.DOMAIN domain = light.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -3000,7 +2995,7 @@ async def test_encoding_subscribable_topics(
init_payload, init_payload,
): ):
"""Test handling of incoming encoded 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_EFFECT_COMMAND_TOPIC] = "light/CONF_EFFECT_COMMAND_TOPIC"
config[CONF_RGB_COMMAND_TOPIC] = "light/CONF_RGB_COMMAND_TOPIC" config[CONF_RGB_COMMAND_TOPIC] = "light/CONF_RGB_COMMAND_TOPIC"
config[CONF_BRIGHTNESS_COMMAND_TOPIC] = "light/CONF_BRIGHTNESS_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, init_payload,
): ):
"""Test handling of incoming encoded payload for a brightness only light.""" """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" config[CONF_BRIGHTNESS_COMMAND_TOPIC] = "light/CONF_BRIGHTNESS_COMMAND_TOPIC"
await help_test_encoding_subscribable_topics( 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.""" """Test the sending of Brightness command with template."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light_brightness/set", "command_topic": "test_light_brightness/set",
"brightness_command_topic": "test_light_brightness/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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test the sending of Effect command with template."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test_light_brightness/set", "command_topic": "test_light_brightness/set",
"brightness_command_topic": "test_light_brightness/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() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = light.DOMAIN platform = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = light.DOMAIN domain = light.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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: Configuration with RGB, brightness, color temp, effect, and XY:
light: mqtt:
platform: mqtt_json light:
schema: json
name: mqtt_json_light_1 name: mqtt_json_light_1
state_topic: "home/rgb1" state_topic: "home/rgb1"
command_topic: "home/rgb1/set" command_topic: "home/rgb1/set"
@ -15,8 +16,9 @@ light:
Configuration with RGB, brightness, color temp and effect: Configuration with RGB, brightness, color temp and effect:
light: mqtt:
platform: mqtt_json light:
schema: json
name: mqtt_json_light_1 name: mqtt_json_light_1
state_topic: "home/rgb1" state_topic: "home/rgb1"
command_topic: "home/rgb1/set" command_topic: "home/rgb1/set"
@ -27,8 +29,9 @@ light:
Configuration with RGB, brightness and color temp: Configuration with RGB, brightness and color temp:
light: mqtt:
platform: mqtt_json light:
schema: json
name: mqtt_json_light_1 name: mqtt_json_light_1
state_topic: "home/rgb1" state_topic: "home/rgb1"
command_topic: "home/rgb1/set" command_topic: "home/rgb1/set"
@ -38,8 +41,9 @@ light:
Configuration with RGB, brightness: Configuration with RGB, brightness:
light: mqtt:
platform: mqtt_json light:
schema: json
name: mqtt_json_light_1 name: mqtt_json_light_1
state_topic: "home/rgb1" state_topic: "home/rgb1"
command_topic: "home/rgb1/set" command_topic: "home/rgb1/set"
@ -48,8 +52,9 @@ light:
Config without RGB: Config without RGB:
light: mqtt:
platform: mqtt_json light:
schema: json
name: mqtt_json_light_1 name: mqtt_json_light_1
state_topic: "home/rgb1" state_topic: "home/rgb1"
command_topic: "home/rgb1/set" command_topic: "home/rgb1/set"
@ -79,7 +84,7 @@ from unittest.mock import call, patch
import pytest import pytest
from homeassistant.components import light from homeassistant.components import light, mqtt
from homeassistant.components.mqtt.light.schema_basic import ( from homeassistant.components.mqtt.light.schema_basic import (
MQTT_LIGHT_ATTRIBUTES_BLOCKED, MQTT_LIGHT_ATTRIBUTES_BLOCKED,
) )
@ -120,7 +125,7 @@ from .test_common import (
help_test_setting_blocked_attribute_via_mqtt_json_message, help_test_setting_blocked_attribute_via_mqtt_json_message,
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, 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, 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 from tests.components.light import common
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test-topic", "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) @pytest.fixture(autouse=True)
def light_platform_only(): def light_platform_only():
@ -156,22 +167,21 @@ class JsonValidator:
return json.loads(self.jsondata) == json.loads(other) 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.""" """Test if setup fails with no command topic."""
assert await async_setup_component( assert not await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{light.DOMAIN: {"platform": "mqtt", "schema": "json", "name": "test"}}, {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")) @pytest.mark.parametrize("deprecated", ("color_temp", "hs", "rgb", "xy"))
async def test_fail_setup_if_color_mode_deprecated( async def test_fail_setup_if_color_mode_deprecated(hass, caplog, deprecated):
hass, mqtt_mock_entry_no_yaml_config, deprecated
):
"""Test if setup fails if color mode is combined with deprecated config keys.""" """Test if setup fails if color mode is combined with deprecated config keys."""
supported_color_modes = ["color_temp", "hs", "rgb", "rgbw", "rgbww", "xy"] 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, "color_mode": True,
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"name": "test", "name": "test",
"platform": "mqtt",
"schema": "json", "schema": "json",
"supported_color_modes": supported_color_modes, "supported_color_modes": supported_color_modes,
} }
} }
config[light.DOMAIN][deprecated] = True config[light.DOMAIN][deprecated] = True
assert await async_setup_component( assert not await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
config, {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( @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( 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.""" """Test if setup fails if supported color modes is invalid."""
config = { config = {
@ -210,34 +225,32 @@ async def test_fail_setup_if_color_modes_invalid(
"color_mode": True, "color_mode": True,
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"name": "test", "name": "test",
"platform": "mqtt",
"schema": "json", "schema": "json",
"supported_color_modes": supported_color_modes, "supported_color_modes": supported_color_modes,
} }
} }
assert await async_setup_component( assert not await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
config, {mqtt.DOMAIN: config},
) )
await hass.async_block_till_done() assert error in caplog.text
await mqtt_mock_entry_no_yaml_config()
assert hass.states.get("light.test") is None
async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config): async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config):
"""Test RGB light flags brightness support.""" """Test RGB light flags brightness support."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"rgb": True, "rgb": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test for no RGB, brightness, color temp, effector XY."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"state_topic": "test_light_rgb", "state_topic": "test_light_rgb",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling of the state via topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"state_topic": "test_light_rgb", "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, "hs": True,
"qos": "0", "qos": "0",
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -434,20 +449,21 @@ async def test_controlling_state_via_topic2(
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"brightness": True, "brightness": True,
"color_mode": True, "color_mode": True,
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"effect": True, "effect": True,
"name": "test", "name": "test",
"platform": "mqtt",
"qos": "0", "qos": "0",
"schema": "json", "schema": "json",
"state_topic": "test_light_rgb", "state_topic": "test_light_rgb",
"supported_color_modes": supported_color_modes, "supported_color_modes": supported_color_modes,
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -618,10 +634,10 @@ async def test_sending_mqtt_commands_and_optimistic(
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
@ -633,6 +649,7 @@ async def test_sending_mqtt_commands_and_optimistic(
"xy": True, "xy": True,
"qos": 2, "qos": 2,
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -755,19 +772,20 @@ async def test_sending_mqtt_commands_and_optimistic2(
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"brightness": True, "brightness": True,
"color_mode": True, "color_mode": True,
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"effect": True, "effect": True,
"name": "test", "name": "test",
"platform": "mqtt",
"qos": 2, "qos": 2,
"schema": "json", "schema": "json",
"supported_color_modes": supported_color_modes, "supported_color_modes": supported_color_modes,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test light.turn_on with hs color sends hs color parameters."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"brightness": True, "brightness": True,
"hs": True, "hs": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test light.turn_on with hs color sends rgb color parameters."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"rgb": True, "rgb": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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"] supported_color_modes = ["rgb", "rgbw", "rgbww"]
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"color_mode": True, "color_mode": True,
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"name": "test", "name": "test",
"platform": "mqtt",
"schema": "json", "schema": "json",
"supported_color_modes": supported_color_modes, "supported_color_modes": supported_color_modes,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test light.turn_on with hs color sends rgb color parameters."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"brightness": True, "brightness": True,
"rgb": True, "rgb": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test light.turn_on with hs color sends rgb color parameters."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
@ -1247,6 +1269,7 @@ async def test_sending_rgb_color_with_scaled_brightness(
"brightness_scale": 100, "brightness_scale": 100,
"rgb": True, "rgb": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test light.turn_on with scaled white."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "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"], "supported_color_modes": ["hs", "white"],
"white_scale": 50, "white_scale": 50,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test light.turn_on with hs color sends xy color parameters."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"brightness": True, "brightness": True,
"xy": True, "xy": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test for effect being sent when included."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"effect": True, "effect": True,
"qos": 0, "qos": 0,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test for flash length being sent when included."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "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, "flash_time_long": 15,
"qos": 0, "qos": 0,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test for transition time being sent when included."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
"qos": 0, "qos": 0,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test for brightness scaling."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"state_topic": "test_light_bright_scale", "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": True,
"brightness_scale": 99, "brightness_scale": 99,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test for white scaling."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"state_topic": "test_light_bright_scale", "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"], "supported_color_modes": ["hs", "white"],
"white_scale": 50, "white_scale": 50,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test that invalid color/brightness/etc. values are ignored."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"state_topic": "test_light_rgb", "state_topic": "test_light_rgb",
@ -1688,6 +1718,7 @@ async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config):
"rgb": True, "rgb": True,
"qos": "0", "qos": "0",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, light.DOMAIN, DEFAULT_CONFIG 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one light per unique_id.""" """Test unique id option only creates one light per unique_id."""
config = { config = {
mqtt.DOMAIN: {
light.DOMAIN: [ light.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"schema": "json", "schema": "json",
"state_topic": "test-topic", "state_topic": "test-topic",
@ -1895,7 +1934,6 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"schema": "json", "schema": "json",
"state_topic": "test-topic", "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( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, config 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): async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config):
"""Test MQTT subscriptions are managed when entity_id is updated.""" """Test MQTT subscriptions are managed when entity_id is updated."""
await help_test_entity_id_update_subscriptions( await help_test_entity_id_update_subscriptions(
hass, hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, DEFAULT_CONFIG
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): async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT discovery update when entity_id is updated.""" """Test MQTT discovery update when entity_id is updated."""
await help_test_entity_id_update_discovery_update( await help_test_entity_id_update_discovery_update(
hass, hass, mqtt_mock_entry_no_yaml_config, light.DOMAIN, DEFAULT_CONFIG
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.""" """Test setting min_mireds and max_mireds."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "json", "schema": "json",
"name": "test", "name": "test",
"command_topic": "test_max_mireds/set", "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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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.""" """Test publishing MQTT payload with different encoding."""
domain = light.DOMAIN domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain]) config = copy.deepcopy(DEFAULT_CONFIG)
if topic == "effect_command_topic": 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( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = light.DOMAIN domain = light.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = light.DOMAIN domain = light.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -2179,7 +2213,7 @@ async def test_encoding_subscribable_topics(
init_payload, init_payload,
): ):
"""Test handling of incoming encoded 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["color_mode"] = True
config["supported_color_modes"] = [ config["supported_color_modes"] = [
"color_temp", "color_temp",
@ -2207,8 +2241,18 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = light.DOMAIN 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" config["name"] = "test"
del config["platform"] assert await async_setup_component(hass, domain, {domain: config})
await help_test_setup_manual_entity_from_yaml(hass, platform, config) await hass.async_block_till_done()
assert hass.states.get(f"{platform}.test") is not None 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: Configuration example with all features:
light: mqtt:
platform: mqtt_template light:
schema: template
name: mqtt_template_light_1 name: mqtt_template_light_1
state_topic: 'home/rgb1' state_topic: 'home/rgb1'
command_topic: 'home/rgb1/set' command_topic: 'home/rgb1/set'
@ -28,7 +29,7 @@ from unittest.mock import patch
import pytest import pytest
from homeassistant.components import light from homeassistant.components import light, mqtt
from homeassistant.components.mqtt.light.schema_basic import ( from homeassistant.components.mqtt.light.schema_basic import (
MQTT_LIGHT_ATTRIBUTES_BLOCKED, MQTT_LIGHT_ATTRIBUTES_BLOCKED,
) )
@ -70,28 +71,30 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
from tests.common import ( from tests.common import async_fire_mqtt_message, mock_restore_cache
assert_setup_component,
async_fire_mqtt_message,
mock_restore_cache,
)
from tests.components.light import common from tests.components.light import common
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test-topic", "command_topic": "test-topic",
"command_on_template": "on,{{ transition }}", "command_on_template": "on,{{ transition }}",
"command_off_template": "off,{{ transition|d }}", "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) @pytest.fixture(autouse=True)
def light_platform_only(): def light_platform_only():
@ -103,10 +106,9 @@ def light_platform_only():
@pytest.mark.parametrize( @pytest.mark.parametrize(
"test_config", "test_config",
[ [
({"platform": "mqtt", "schema": "template", "name": "test"},), ({"schema": "template", "name": "test"},),
( (
{ {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test_topic", "command_topic": "test_topic",
@ -114,7 +116,6 @@ def light_platform_only():
), ),
( (
{ {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test_topic", "command_topic": "test_topic",
@ -123,7 +124,6 @@ def light_platform_only():
), ),
( (
{ {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test_topic", "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.""" """Test that setup fails with missing required configuration items."""
with assert_setup_component(0, light.DOMAIN) as setup_config: assert not await async_setup_component(
assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{light.DOMAIN: test_config}, {mqtt.DOMAIN: {light.DOMAIN: test_config}},
) )
await hass.async_block_till_done() assert "Invalid config for [mqtt]" in caplog.text
await mqtt_mock_entry_no_yaml_config()
assert not setup_config[light.DOMAIN]
assert hass.states.get("light.test") is None
async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config): async def test_rgb_light(hass, mqtt_mock_entry_with_yaml_config):
"""Test RGB light flags brightness support.""" """Test RGB light flags brightness support."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "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] }}', "green_template": '{{ value.split(",")[4].' 'split("-")[1] }}',
"blue_template": '{{ value.split(",")[4].' 'split("-")[2] }}', "blue_template": '{{ value.split(",")[4].' 'split("-")[2] }}',
} }
}
}, },
) )
await hass.async_block_till_done() 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): async def test_state_change_via_topic(hass, mqtt_mock_entry_with_yaml_config):
"""Test state change via topic.""" """Test state change via topic."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"state_topic": "test_light_rgb", "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", "command_off_template": "off",
"state_template": '{{ value.split(",")[0] }}', "state_template": '{{ value.split(",")[0] }}',
} }
}
}, },
) )
await hass.async_block_till_done() 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 hass, mqtt_mock_entry_with_yaml_config
): ):
"""Test state, bri, color, effect, color temp change.""" """Test state, bri, color, effect, color temp change."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"effect_list": ["rainbow", "colorloop"], "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] }}', "blue_template": '{{ value.split(",")[3].' 'split("-")[2] }}',
"effect_template": '{{ value.split(",")[4] }}', "effect_template": '{{ value.split(",")[4] }}',
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -340,13 +337,12 @@ async def test_sending_mqtt_commands_and_optimistic(
) )
mock_restore_cache(hass, (fake_state,)) mock_restore_cache(hass, (fake_state,))
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "command_topic": "test_light_rgb/set",
@ -369,6 +365,7 @@ async def test_sending_mqtt_commands_and_optimistic(
"effect_template": '{{ value.split(",")[4] }}', "effect_template": '{{ value.split(",")[4] }}',
"qos": 2, "qos": 2,
} }
}
}, },
) )
await hass.async_block_till_done() 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 hass, mqtt_mock_entry_with_yaml_config
): ):
"""Test the sending of command in optimistic mode.""" """Test the sending of command in optimistic mode."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"effect_list": ["rainbow", "colorloop"], "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] }}', "blue_template": '{{ value.split(",")[3].' 'split("-")[2] }}',
"effect_template": '{{ value.split(",")[4] }}', "effect_template": '{{ value.split(",")[4] }}',
} }
}
}, },
) )
await hass.async_block_till_done() 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): async def test_effect(hass, mqtt_mock_entry_with_yaml_config):
"""Test effect sent over MQTT in optimistic mode.""" """Test effect sent over MQTT in optimistic mode."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"effect_list": ["rainbow", "colorloop"], "effect_list": ["rainbow", "colorloop"],
"name": "test", "name": "test",
@ -606,6 +602,7 @@ async def test_effect(hass, mqtt_mock_entry_with_yaml_config):
"command_off_template": "off", "command_off_template": "off",
"qos": 0, "qos": 0,
} }
}
}, },
) )
await hass.async_block_till_done() 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): async def test_flash(hass, mqtt_mock_entry_with_yaml_config):
"""Test flash sent over MQTT in optimistic mode.""" """Test flash sent over MQTT in optimistic mode."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "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", "command_off_template": "off",
"qos": 0, "qos": 0,
} }
}
}, },
) )
await hass.async_block_till_done() 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): async def test_transition(hass, mqtt_mock_entry_with_yaml_config):
"""Test for transition time being sent when included.""" """Test for transition time being sent when included."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test_light_rgb/set", "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 }}", "command_off_template": "off,{{ transition|int|d }}",
"qos": 1, "qos": 1,
} }
}
}, },
) )
await hass.async_block_till_done() 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): async def test_invalid_values(hass, mqtt_mock_entry_with_yaml_config):
"""Test that invalid values are ignored.""" """Test that invalid values are ignored."""
with assert_setup_component(1, light.DOMAIN):
assert await async_setup_component( assert await async_setup_component(
hass, hass,
light.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"effect_list": ["rainbow", "colorloop"], "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] }}', "blue_template": '{{ value.split(",")[3].' 'split("-")[2] }}',
"effect_template": '{{ value.split(",")[4] }}', "effect_template": '{{ value.split(",")[4] }}',
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, light.DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one light per unique_id.""" """Test unique id option only creates one light per unique_id."""
config = { config = {
mqtt.DOMAIN: {
light.DOMAIN: [ light.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"schema": "template", "schema": "template",
"state_topic": "test-topic", "state_topic": "test-topic",
@ -921,15 +930,17 @@ async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"schema": "template", "schema": "template",
"state_topic": "test-topic", "state_topic": "test-topic2",
"command_topic": "test_topic", "command_topic": "test_topic2",
"command_on_template": "on,{{ transition }}",
"command_off_template": "off,{{ transition|d }}",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
] ]
} }
}
await help_test_unique_id( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, light.DOMAIN, config 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): async def test_entity_debug_info_message(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT debug info.""" """Test MQTT debug info."""
config = { config = {
mqtt.DOMAIN: {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test-topic", "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] }}', "state_template": '{{ value.split(",")[0] }}',
} }
} }
}
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, hass,
mqtt_mock_entry_no_yaml_config, 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.""" """Test setting min_mireds and max_mireds."""
config = { config = {
light.DOMAIN: { light.DOMAIN: {
"platform": "mqtt",
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test_max_mireds/set", "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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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.""" """Test publishing MQTT payload with different encoding."""
domain = light.DOMAIN domain = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain]) config = copy.deepcopy(DEFAULT_CONFIG)
if topic == "effect_command_topic": 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( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = light.DOMAIN domain = light.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = light.DOMAIN domain = light.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -1192,7 +1205,7 @@ async def test_encoding_subscribable_topics(
init_payload, init_payload,
): ):
"""Test handling of incoming encoded 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 }}" config["state_template"] = "{{ value }}"
await help_test_encoding_subscribable_topics( await help_test_encoding_subscribable_topics(
hass, hass,
@ -1211,17 +1224,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = light.DOMAIN platform = light.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = light.DOMAIN domain = light.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 import pytest
from homeassistant.components import lock, mqtt
from homeassistant.components.lock import ( from homeassistant.components.lock import (
DOMAIN as LOCK_DOMAIN,
SERVICE_LOCK, SERVICE_LOCK,
SERVICE_OPEN, SERVICE_OPEN,
SERVICE_UNLOCK, SERVICE_UNLOCK,
STATE_LOCKED, STATE_LOCKED,
STATE_UNLOCKED, STATE_UNLOCKED,
SUPPORT_OPEN, LockEntityFeature,
) )
from homeassistant.components.mqtt.lock import MQTT_LOCK_ATTRIBUTES_BLOCKED from homeassistant.components.mqtt.lock import MQTT_LOCK_ATTRIBUTES_BLOCKED
from homeassistant.const import ( from homeassistant.const import (
@ -49,16 +49,21 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
from tests.common import async_fire_mqtt_message from tests.common import async_fire_mqtt_message
DEFAULT_CONFIG = { 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) @pytest.fixture(autouse=True)
def lock_platform_only(): 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.""" """Test the controlling state via topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
LOCK_DOMAIN, mqtt.DOMAIN,
{ {
LOCK_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", lock.DOMAIN: {
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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_locked": "LOCKED",
"state_unlocked": "UNLOCKED", "state_unlocked": "UNLOCKED",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
LOCK_DOMAIN, mqtt.DOMAIN,
{ {
LOCK_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", lock.DOMAIN: {
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -122,6 +128,7 @@ async def test_controlling_non_default_state_via_topic(
"state_locked": "closed", "state_locked": "closed",
"state_unlocked": "open", "state_unlocked": "open",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic and JSON message."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
LOCK_DOMAIN, mqtt.DOMAIN,
{ {
LOCK_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", lock.DOMAIN: {
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -161,6 +168,7 @@ async def test_controlling_state_via_topic_and_json_message(
"state_unlocked": "UNLOCKED", "state_unlocked": "UNLOCKED",
"value_template": "{{ value_json.val }}", "value_template": "{{ value_json.val }}",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic and JSON message."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
LOCK_DOMAIN, mqtt.DOMAIN,
{ {
LOCK_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", lock.DOMAIN: {
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -199,6 +207,7 @@ async def test_controlling_non_default_state_via_topic_and_json_message(
"state_unlocked": "open", "state_unlocked": "open",
"value_template": "{{ value_json.val }}", "value_template": "{{ value_json.val }}",
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -224,10 +233,10 @@ async def test_sending_mqtt_commands_and_optimistic(
"""Test optimistic mode without state topic.""" """Test optimistic mode without state topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
LOCK_DOMAIN, mqtt.DOMAIN,
{ {
LOCK_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", lock.DOMAIN: {
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_lock": "LOCK", "payload_lock": "LOCK",
@ -235,6 +244,7 @@ async def test_sending_mqtt_commands_and_optimistic(
"state_locked": "LOCKED", "state_locked": "LOCKED",
"state_unlocked": "UNLOCKED", "state_unlocked": "UNLOCKED",
} }
}
}, },
) )
await hass.async_block_till_done() 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) assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call( 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) 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) assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call( 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) 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.""" """Test optimistic mode without state topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
LOCK_DOMAIN, mqtt.DOMAIN,
{ {
LOCK_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", lock.DOMAIN: {
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -284,6 +294,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(
"state_unlocked": "UNLOCKED", "state_unlocked": "UNLOCKED",
"optimistic": True, "optimistic": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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) assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call( 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) 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) assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call( 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) 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.""" """Test open function of the lock without state topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
LOCK_DOMAIN, mqtt.DOMAIN,
{ {
LOCK_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", lock.DOMAIN: {
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_lock": "LOCK", "payload_lock": "LOCK",
@ -332,6 +343,7 @@ async def test_sending_mqtt_commands_support_open_and_optimistic(
"state_locked": "LOCKED", "state_locked": "LOCKED",
"state_unlocked": "UNLOCKED", "state_unlocked": "UNLOCKED",
} }
}
}, },
) )
await hass.async_block_till_done() 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") state = hass.states.get("lock.test")
assert state.state is STATE_UNLOCKED assert state.state is STATE_UNLOCKED
assert state.attributes.get(ATTR_ASSUMED_STATE) 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( 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) 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) assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call( 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) 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) assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call( 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) 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.""" """Test open function of the lock without state topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
LOCK_DOMAIN, mqtt.DOMAIN,
{ {
LOCK_DOMAIN: { mqtt.DOMAIN: {
"platform": "mqtt", lock.DOMAIN: {
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -393,6 +405,7 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic(
"state_unlocked": "UNLOCKED", "state_unlocked": "UNLOCKED",
"optimistic": True, "optimistic": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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") state = hass.states.get("lock.test")
assert state.state is STATE_UNLOCKED assert state.state is STATE_UNLOCKED
assert state.attributes.get(ATTR_ASSUMED_STATE) 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( 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) 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) assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call( 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) 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) assert state.attributes.get(ATTR_ASSUMED_STATE)
await hass.services.async_call( 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) 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.""" """Test availability after MQTT disconnection."""
await help_test_availability_when_connection_lost( 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): async def test_availability_without_topic(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability without defined availability topic.""" """Test availability without defined availability topic."""
await help_test_availability_without_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): async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic.""" """Test availability by default payload with defined topic."""
await help_test_default_availability_payload( 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): async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic.""" """Test availability by custom payload with defined topic."""
await help_test_custom_availability_payload( 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.""" """Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_via_mqtt_json_message( 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( await help_test_setting_blocked_attribute_via_mqtt_json_message(
hass, hass,
mqtt_mock_entry_no_yaml_config, mqtt_mock_entry_no_yaml_config,
LOCK_DOMAIN, lock.DOMAIN,
DEFAULT_CONFIG, DEFAULT_CONFIG,
MQTT_LOCK_ATTRIBUTES_BLOCKED, 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): async def test_setting_attribute_with_template(hass, mqtt_mock_entry_with_yaml_config):
"""Test the setting of attribute via MQTT with JSON payload.""" """Test the setting of attribute via MQTT with JSON payload."""
await help_test_setting_attribute_with_template( 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, LOCK_DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one lock per unique_id.""" """Test unique id option only creates one lock per unique_id."""
config = { config = {
LOCK_DOMAIN: [ mqtt.DOMAIN: {
lock.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_topic": "test_topic", "command_topic": "test_topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_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( 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.""" """Test removal of discovered lock."""
data = '{ "name": "test",' ' "command_topic": "test_topic" }' data = '{ "name": "test",' ' "command_topic": "test_topic" }'
await help_test_discovery_removal( 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", "availability_topic": "availability_topic2",
} }
await help_test_discovery_update( 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, hass,
mqtt_mock_entry_no_yaml_config, mqtt_mock_entry_no_yaml_config,
caplog, caplog,
LOCK_DOMAIN, lock.DOMAIN,
data1, data1,
discovery_update, discovery_update,
) )
@ -598,49 +619,49 @@ async def test_discovery_broken(hass, mqtt_mock_entry_no_yaml_config, caplog):
data1 = '{ "name": "Beer" }' data1 = '{ "name": "Beer" }'
data2 = '{ "name": "Milk",' ' "command_topic": "test_topic" }' data2 = '{ "name": "Milk",' ' "command_topic": "test_topic" }'
await help_test_discovery_broken( 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): async def test_entity_device_info_with_connection(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT lock device registry integration.""" """Test MQTT lock device registry integration."""
await help_test_entity_device_info_with_connection( 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): async def test_entity_device_info_with_identifier(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT lock device registry integration.""" """Test MQTT lock device registry integration."""
await help_test_entity_device_info_with_identifier( 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): async def test_entity_device_info_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry update.""" """Test device registry update."""
await help_test_entity_device_info_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): async def test_entity_device_info_remove(hass, mqtt_mock_entry_no_yaml_config):
"""Test device registry remove.""" """Test device registry remove."""
await help_test_entity_device_info_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): async def test_entity_id_update_subscriptions(hass, mqtt_mock_entry_with_yaml_config):
"""Test MQTT subscriptions are managed when entity_id is updated.""" """Test MQTT subscriptions are managed when entity_id is updated."""
await help_test_entity_id_update_subscriptions( 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): async def test_entity_id_update_discovery_update(hass, mqtt_mock_entry_no_yaml_config):
"""Test MQTT discovery update when entity_id is updated.""" """Test MQTT discovery update when entity_id is updated."""
await help_test_entity_id_update_discovery_update( 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( await help_test_entity_debug_info_message(
hass, hass,
mqtt_mock_entry_no_yaml_config, mqtt_mock_entry_no_yaml_config,
LOCK_DOMAIN, lock.DOMAIN,
DEFAULT_CONFIG, DEFAULT_CONFIG,
SERVICE_LOCK, SERVICE_LOCK,
command_payload="LOCK", command_payload="LOCK",
@ -679,8 +700,8 @@ async def test_publishing_with_custom_encoding(
template, template,
): ):
"""Test publishing MQTT payload with different encoding.""" """Test publishing MQTT payload with different encoding."""
domain = LOCK_DOMAIN domain = lock.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = LOCK_DOMAIN domain = lock.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = LOCK_DOMAIN domain = lock.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -732,8 +755,8 @@ async def test_encoding_subscribable_topics(
hass, hass,
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
caplog, caplog,
LOCK_DOMAIN, lock.DOMAIN,
DEFAULT_CONFIG[LOCK_DOMAIN], DEFAULT_CONFIG[mqtt.DOMAIN][lock.DOMAIN],
topic, topic,
value, value,
attribute, attribute,
@ -743,18 +766,28 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = LOCK_DOMAIN platform = lock.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = LOCK_DOMAIN domain = lock.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
from tests.common import async_fire_mqtt_message, mock_restore_cache_with_extra_data from tests.common import async_fire_mqtt_message, mock_restore_cache_with_extra_data
DEFAULT_CONFIG = { 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) @pytest.fixture(autouse=True)
def number_platform_only(): def number_platform_only():
@ -82,10 +87,10 @@ async def test_run_number_setup(hass, mqtt_mock_entry_with_yaml_config):
topic = "test/number" topic = "test/number"
await async_setup_component( await async_setup_component(
hass, hass,
"number", mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"state_topic": topic, "state_topic": topic,
"command_topic": topic, "command_topic": topic,
"name": "Test Number", "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, "unit_of_measurement": TEMP_FAHRENHEIT,
"payload_reset": "reset!", "payload_reset": "reset!",
} }
}
}, },
) )
await hass.async_block_till_done() 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" topic = "test/number"
await async_setup_component( await async_setup_component(
hass, hass,
"number", mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"state_topic": topic, "state_topic": topic,
"command_topic": topic, "command_topic": topic,
"name": "Test Number", "name": "Test Number",
"value_template": "{{ value_json.val }}", "value_template": "{{ value_json.val }}",
} }
}
}, },
) )
await hass.async_block_till_done() 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( assert await async_setup_component(
hass, hass,
number.DOMAIN, mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"command_topic": topic, "command_topic": topic,
"device_class": "temperature", "device_class": "temperature",
"unit_of_measurement": TEMP_FAHRENHEIT, "unit_of_measurement": TEMP_FAHRENHEIT,
"name": "Test Number", "name": "Test Number",
} }
}
}, },
) )
await hass.async_block_till_done() 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( assert await async_setup_component(
hass, hass,
number.DOMAIN, mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"command_topic": topic, "command_topic": topic,
"name": "Test Number", "name": "Test Number",
} }
}
}, },
) )
await hass.async_block_till_done() 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( assert await async_setup_component(
hass, hass,
number.DOMAIN, mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"command_topic": topic, "command_topic": topic,
"name": "Test Number", "name": "Test Number",
"command_template": '{"number": {{ value }} }', "command_template": '{"number": {{ value }} }',
} }
}
}, },
) )
await hass.async_block_till_done() 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( assert await async_setup_component(
hass, hass,
number.DOMAIN, mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"command_topic": cmd_topic, "command_topic": cmd_topic,
"state_topic": state_topic, "state_topic": state_topic,
"name": "Test Number", "name": "Test Number",
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -398,15 +409,16 @@ async def test_run_number_service_with_command_template(
assert await async_setup_component( assert await async_setup_component(
hass, hass,
number.DOMAIN, mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"command_topic": cmd_topic, "command_topic": cmd_topic,
"state_topic": state_topic, "state_topic": state_topic,
"name": "Test Number", "name": "Test Number",
"command_template": '{"number": {{ value }} }', "command_template": '{"number": {{ value }} }',
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, number.DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one number per unique_id.""" """Test unique id option only creates one number per unique_id."""
config = { config = {
mqtt.DOMAIN: {
number.DOMAIN: [ number.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_topic": "test-topic", "command_topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_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( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, number.DOMAIN, config 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): async def test_discovery_removal_number(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered number.""" """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( await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, number.DOMAIN, data 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" topic = "test/number"
await async_setup_component( await async_setup_component(
hass, hass,
"number", mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"state_topic": topic, "state_topic": topic,
"command_topic": topic, "command_topic": topic,
"name": "Test Number", "name": "Test Number",
@ -669,6 +693,7 @@ async def test_min_max_step_attributes(hass, mqtt_mock_entry_with_yaml_config):
"max": 110, "max": 110,
"step": 20, "step": 20,
} }
}
}, },
) )
await hass.async_block_till_done() 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 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.""" """Test invalid min/max attributes."""
topic = "test/number" topic = "test/number"
await async_setup_component( assert not await async_setup_component(
hass, hass,
"number", mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"state_topic": topic, "state_topic": topic,
"command_topic": topic, "command_topic": topic,
"name": "Test Number", "name": "Test Number",
"min": 35, "min": 35,
"max": 10, "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 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.""" """Test warning for MQTT payload which is not a number."""
topic = "test/number" topic = "test/number"
await async_setup_component( assert await async_setup_component(
hass, hass,
"number", mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"state_topic": topic, "state_topic": topic,
"command_topic": topic, "command_topic": topic,
"name": "Test Number", "name": "Test Number",
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -808,16 +833,17 @@ async def test_mqtt_payload_out_of_range_error(
topic = "test/number" topic = "test/number"
await async_setup_component( await async_setup_component(
hass, hass,
"number", mqtt.DOMAIN,
{ {
"number": { mqtt.DOMAIN: {
"platform": "mqtt", number.DOMAIN: {
"state_topic": topic, "state_topic": topic,
"command_topic": topic, "command_topic": topic,
"name": "Test Number", "name": "Test Number",
"min": 5, "min": 5,
"max": 110, "max": 110,
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -856,7 +882,7 @@ async def test_publishing_with_custom_encoding(
): ):
"""Test publishing MQTT payload with different encoding.""" """Test publishing MQTT payload with different encoding."""
domain = NUMBER_DOMAIN domain = NUMBER_DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = number.DOMAIN domain = number.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = number.DOMAIN domain = number.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -909,8 +937,8 @@ async def test_encoding_subscribable_topics(
hass, hass,
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
caplog, caplog,
"number", number.DOMAIN,
DEFAULT_CONFIG["number"], DEFAULT_CONFIG[mqtt.DOMAIN][number.DOMAIN],
topic, topic,
value, value,
attribute, attribute,
@ -921,17 +949,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = number.DOMAIN platform = number.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = number.DOMAIN domain = number.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 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 from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON, STATE_UNKNOWN, Platform
import homeassistant.core as ha import homeassistant.core as ha
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -28,14 +28,20 @@ from .test_common import (
from tests.common import mock_restore_cache from tests.common import mock_restore_cache
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
mqtt.DOMAIN: {
scene.DOMAIN: { scene.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test-topic", "command_topic": "test-topic",
"payload_on": "test-payload-on", "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) @pytest.fixture(autouse=True)
def scene_platform_only(): 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( assert await async_setup_component(
hass, hass,
scene.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
scene.DOMAIN: { scene.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_on": "beer on", "payload_on": "beer on",
}, },
}
}, },
) )
await hass.async_block_till_done() 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): async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic.""" """Test availability by default payload with defined topic."""
config = { config = {
mqtt.DOMAIN: {
scene.DOMAIN: { scene.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_on": 1, "payload_on": 1,
} }
} }
}
await help_test_default_availability_payload( await help_test_default_availability_payload(
hass, 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): async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic.""" """Test availability by custom payload with defined topic."""
config = { config = {
mqtt.DOMAIN: {
scene.DOMAIN: { scene.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_on": 1, "payload_on": 1,
} }
} }
}
await help_test_custom_availability_payload( await help_test_custom_availability_payload(
hass, 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one scene per unique_id.""" """Test unique id option only creates one scene per unique_id."""
config = { config = {
mqtt.DOMAIN: {
scene.DOMAIN: [ scene.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"command_topic": "command-topic", "command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"command_topic": "command-topic", "command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
] ]
} }
}
await help_test_unique_id( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, scene.DOMAIN, config 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): async def test_discovery_update_payload(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered scene.""" """Test update of discovered scene."""
config1 = copy.deepcopy(DEFAULT_CONFIG[scene.DOMAIN]) config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][scene.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[scene.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][scene.DOMAIN])
config1["name"] = "Beer" config1["name"] = "Beer"
config2["name"] = "Milk" config2["name"] = "Milk"
config1["payload_on"] = "ON" 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = scene.DOMAIN domain = scene.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = scene.DOMAIN domain = scene.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = scene.DOMAIN platform = scene.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = scene.DOMAIN domain = scene.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 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.mqtt.select import MQTT_SELECT_ATTRIBUTES_BLOCKED
from homeassistant.components.select import ( from homeassistant.components.select import (
ATTR_OPTION, ATTR_OPTION,
@ -49,21 +49,27 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
from tests.common import async_fire_mqtt_message, mock_restore_cache from tests.common import async_fire_mqtt_message, mock_restore_cache
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
mqtt.DOMAIN: {
select.DOMAIN: { select.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "test-topic", "command_topic": "test-topic",
"options": ["milk", "beer"], "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) @pytest.fixture(autouse=True)
def select_platform_only(): def select_platform_only():
@ -77,15 +83,16 @@ async def test_run_select_setup(hass, mqtt_mock_entry_with_yaml_config):
topic = "test/select" topic = "test/select"
await async_setup_component( await async_setup_component(
hass, hass,
"select", mqtt.DOMAIN,
{ {
"select": { mqtt.DOMAIN: {
"platform": "mqtt", select.DOMAIN: {
"state_topic": topic, "state_topic": topic,
"command_topic": topic, "command_topic": topic,
"name": "Test Select", "name": "Test Select",
"options": ["milk", "beer"], "options": ["milk", "beer"],
} }
}
}, },
) )
await hass.async_block_till_done() 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" topic = "test/select"
await async_setup_component( await async_setup_component(
hass, hass,
"select", mqtt.DOMAIN,
{ {
"select": { mqtt.DOMAIN: {
"platform": "mqtt", select.DOMAIN: {
"state_topic": topic, "state_topic": topic,
"command_topic": topic, "command_topic": topic,
"name": "Test Select", "name": "Test Select",
"options": ["milk", "beer"], "options": ["milk", "beer"],
"value_template": "{{ value_json.val }}", "value_template": "{{ value_json.val }}",
} }
}
}, },
) )
await hass.async_block_till_done() 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( assert await async_setup_component(
hass, hass,
select.DOMAIN, mqtt.DOMAIN,
{ {
"select": { mqtt.DOMAIN: {
"platform": "mqtt", select.DOMAIN: {
"command_topic": topic, "command_topic": topic,
"name": "Test Select", "name": "Test Select",
"options": ["milk", "beer"], "options": ["milk", "beer"],
} }
}
}, },
) )
await hass.async_block_till_done() 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( assert await async_setup_component(
hass, hass,
select.DOMAIN, mqtt.DOMAIN,
{ {
"select": { mqtt.DOMAIN: {
"platform": "mqtt", select.DOMAIN: {
"command_topic": topic, "command_topic": topic,
"name": "Test Select", "name": "Test Select",
"options": ["milk", "beer"], "options": ["milk", "beer"],
"command_template": '{"option": "{{ value }}"}', "command_template": '{"option": "{{ value }}"}',
} }
}
}, },
) )
await hass.async_block_till_done() 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( assert await async_setup_component(
hass, hass,
select.DOMAIN, mqtt.DOMAIN,
{ {
"select": { mqtt.DOMAIN: {
"platform": "mqtt", select.DOMAIN: {
"command_topic": cmd_topic, "command_topic": cmd_topic,
"state_topic": state_topic, "state_topic": state_topic,
"name": "Test Select", "name": "Test Select",
"options": ["milk", "beer"], "options": ["milk", "beer"],
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -276,16 +287,17 @@ async def test_run_select_service_with_command_template(
assert await async_setup_component( assert await async_setup_component(
hass, hass,
select.DOMAIN, mqtt.DOMAIN,
{ {
"select": { mqtt.DOMAIN: {
"platform": "mqtt", select.DOMAIN: {
"command_topic": cmd_topic, "command_topic": cmd_topic,
"state_topic": state_topic, "state_topic": state_topic,
"name": "Test Select", "name": "Test Select",
"options": ["milk", "beer"], "options": ["milk", "beer"],
"command_template": '{"option": "{{ value }}"}', "command_template": '{"option": "{{ value }}"}',
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, select.DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one select per unique_id.""" """Test unique id option only creates one select per unique_id."""
config = { config = {
mqtt.DOMAIN: {
select.DOMAIN: [ select.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_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"], "options": ["milk", "beer"],
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_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( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, select.DOMAIN, config 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): async def test_discovery_removal_select(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test removal of discovered select.""" """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( await help_test_discovery_removal(
hass, mqtt_mock_entry_no_yaml_config, caplog, select.DOMAIN, data 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" topic = "test/select"
await async_setup_component( await async_setup_component(
hass, hass,
"select", mqtt.DOMAIN,
{ {
"select": { mqtt.DOMAIN: {
"platform": "mqtt", select.DOMAIN: {
"state_topic": topic, "state_topic": topic,
"command_topic": topic, "command_topic": topic,
"name": "Test select", "name": "Test select",
"options": options, "options": options,
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -561,15 +586,16 @@ async def test_mqtt_payload_not_an_option_warning(
topic = "test/select" topic = "test/select"
await async_setup_component( await async_setup_component(
hass, hass,
"select", mqtt.DOMAIN,
{ {
"select": { mqtt.DOMAIN: {
"platform": "mqtt", select.DOMAIN: {
"state_topic": topic, "state_topic": topic,
"command_topic": topic, "command_topic": topic,
"name": "Test Select", "name": "Test Select",
"options": ["milk", "beer"], "options": ["milk", "beer"],
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -609,8 +635,8 @@ async def test_publishing_with_custom_encoding(
): ):
"""Test publishing MQTT payload with different encoding.""" """Test publishing MQTT payload with different encoding."""
domain = select.DOMAIN domain = select.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
config["options"] = ["milk", "beer"] config[mqtt.DOMAIN][domain]["options"] = ["milk", "beer"]
await help_test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = select.DOMAIN domain = select.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = select.DOMAIN domain = select.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) await help_test_reloadable_late(hass, caplog, tmp_path, domain, config)
@ -659,13 +687,13 @@ async def test_encoding_subscribable_topics(
attribute_value, attribute_value,
): ):
"""Test handling of incoming encoded payload.""" """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"] config["options"] = ["milk", "beer"]
await help_test_encoding_subscribable_topics( await help_test_encoding_subscribable_topics(
hass, hass,
mqtt_mock_entry_with_yaml_config, mqtt_mock_entry_with_yaml_config,
caplog, caplog,
"select", select.DOMAIN,
config, config,
topic, topic,
value, value,
@ -677,17 +705,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = select.DOMAIN platform = select.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = select.DOMAIN domain = select.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 import pytest
from homeassistant.components import mqtt, sensor
from homeassistant.components.mqtt.sensor import MQTT_SENSOR_ATTRIBUTES_BLOCKED from homeassistant.components.mqtt.sensor import MQTT_SENSOR_ATTRIBUTES_BLOCKED
import homeassistant.components.sensor as sensor
from homeassistant.const import ( from homeassistant.const import (
EVENT_STATE_CHANGED, EVENT_STATE_CHANGED,
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
@ -59,21 +59,25 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
from tests.common import ( from tests.common import (
assert_setup_component,
async_fire_mqtt_message, async_fire_mqtt_message,
async_fire_time_changed, async_fire_time_changed,
mock_restore_cache_with_extra_data, mock_restore_cache_with_extra_data,
) )
DEFAULT_CONFIG = { 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) @pytest.fixture(autouse=True)
def sensor_platform_only(): 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.""" """Test the setting of the value via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"unit_of_measurement": "fav unit", "unit_of_measurement": "fav unit",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the value via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"device_class": device_class, "device_class": device_class,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the expiration of the value."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"expire_after": 4, "expire_after": 4,
"force_update": True, "force_update": True,
"availability_topic": "availability-topic", "availability_topic": "availability-topic",
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -206,16 +213,17 @@ async def test_setting_sensor_value_expires(
"""Test the expiration of the value.""" """Test the expiration of the value."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"unit_of_measurement": "fav unit", "unit_of_measurement": "fav unit",
"expire_after": "4", "expire_after": "4",
"force_update": True, "force_update": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the value via MQTT with JSON payload."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"unit_of_measurement": "fav unit", "unit_of_measurement": "fav unit",
"value_template": "{{ value_json.val }}", "value_template": "{{ value_json.val }}",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the value via MQTT with fall back to current state."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"unit_of_measurement": "fav unit", "unit_of_measurement": "fav unit",
"value_template": "{{ value_json.val | is_defined }}-{{ value_json.par }}", "value_template": "{{ value_json.val | is_defined }}-{{ value_json.par }}",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the last_reset property via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_class": "total", "state_class": "total",
"state_topic": "test-topic", "state_topic": "test-topic",
"unit_of_measurement": "fav unit", "unit_of_measurement": "fav unit",
"last_reset_topic": "last-reset-topic", "last_reset_topic": "last-reset-topic",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the last_reset property via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_class": "total", "state_class": "total",
"state_topic": "test-topic", "state_topic": "test-topic",
"unit_of_measurement": "fav unit", "unit_of_measurement": "fav unit",
"last_reset_topic": "last-reset-topic", "last_reset_topic": "last-reset-topic",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the last_reset property via MQTT."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_class": "total", "state_class": "total",
"state_topic": "test-topic", "state_topic": "test-topic",
"unit_of_measurement": "fav unit", "unit_of_measurement": "fav unit",
"last_reset_topic": "last-reset-topic", "last_reset_topic": "last-reset-topic",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the value via MQTT with JSON payload."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_class": "total", "state_class": "total",
"state_topic": "test-topic", "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_topic": "last-reset-topic",
"last_reset_value_template": "{{ value_json.last_reset }}", "last_reset_value_template": "{{ value_json.last_reset }}",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the setting of the value via MQTT with JSON payload."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
**{ **{
"platform": "mqtt",
"name": "test", "name": "test",
"state_class": "total", "state_class": "total",
"state_topic": "test-topic", "state_topic": "test-topic",
@ -474,6 +488,7 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message_2(
}, },
**extra, **extra,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test force update option."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"unit_of_measurement": "fav unit", "unit_of_measurement": "fav unit",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test force update option."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"unit_of_measurement": "fav unit", "unit_of_measurement": "fav unit",
"force_update": True, "force_update": True,
} }
}
}, },
) )
await hass.async_block_till_done() 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( async def test_default_availability_list_single(hass, caplog):
hass, mqtt_mock_entry_no_yaml_config, caplog
):
"""Test availability list and availability_topic are mutually exclusive.""" """Test availability list and availability_topic are mutually exclusive."""
await help_test_default_availability_list_single( 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, hass,
sensor.DOMAIN, sensor.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"device_class": "foobarnotreal", "device_class": "foobarnotreal",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test device_class option with valid values."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
"sensor", mqtt.DOMAIN,
{ {
"sensor": [ mqtt.DOMAIN: {
sensor.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"device_class": "temperature", "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() await hass.async_block_till_done()
@ -689,12 +709,13 @@ async def test_invalid_state_class(hass, mqtt_mock_entry_no_yaml_config):
hass, hass,
sensor.DOMAIN, sensor.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"state_class": "foobarnotreal", "state_class": "foobarnotreal",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test state_class option with valid values."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
"sensor", mqtt.DOMAIN,
{ {
"sensor": [ mqtt.DOMAIN: {
sensor.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"state_class": "measurement", "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() 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, sensor.DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one sensor per unique_id.""" """Test unique id option only creates one sensor per unique_id."""
config = { config = {
mqtt.DOMAIN: {
sensor.DOMAIN: [ sensor.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
] ]
} }
}
await help_test_unique_id( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, sensor.DOMAIN, config 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( data = json.dumps(
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"device": {"identifiers": ["helloworld"], "via_device": "hub-id"}, "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.""" """Test the access to attributes in value_template via the entity_id."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
sensor.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
sensor.DOMAIN: { sensor.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "test-topic", "state_topic": "test-topic",
"unit_of_measurement": "fav unit", "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 }} \ {{ value }} \
{% endif %}', {% endif %}',
} }
}
}, },
) )
await hass.async_block_till_done() 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = sensor.DOMAIN domain = sensor.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = sensor.DOMAIN domain = sensor.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) 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.""" """Test cleanup old triggers at reloading and restoring the state."""
domain = sensor.DOMAIN domain = sensor.DOMAIN
config1 = copy.deepcopy(DEFAULT_CONFIG[domain]) config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config1["name"] = "test1" config1["name"] = "test1"
config1["expire_after"] = 30 config1["expire_after"] = 30
config1["state_topic"] = "test-topic1" config1["state_topic"] = "test-topic1"
config1["device_class"] = "temperature" config1["device_class"] = "temperature"
config1["unit_of_measurement"] = TEMP_FAHRENHEIT config1["unit_of_measurement"] = TEMP_FAHRENHEIT
config2 = copy.deepcopy(DEFAULT_CONFIG[domain]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config2["name"] = "test2" config2["name"] = "test2"
config2["expire_after"] = 5 config2["expire_after"] = 5
config2["state_topic"] = "test-topic2" config2["state_topic"] = "test-topic2"
@ -1100,8 +1136,8 @@ async def test_cleanup_triggers_and_restoring_state(
assert await async_setup_component( assert await async_setup_component(
hass, hass,
domain, mqtt.DOMAIN,
{domain: [config1, config2]}, {mqtt.DOMAIN: {domain: [config1, config2]}},
) )
await hass.async_block_till_done() await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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") freezer.move_to("2022-02-02 12:01:10+01:00")
await help_test_reload_with_config( 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() 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") freezer.move_to("2022-02-02 12:02:00+01:00")
domain = sensor.DOMAIN domain = sensor.DOMAIN
config3 = copy.deepcopy(DEFAULT_CONFIG[domain]) config3 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain])
config3["name"] = "test3" config3["name"] = "test3"
config3["expire_after"] = 10 config3["expire_after"] = 10
config3["state_topic"] = "test-topic3" config3["state_topic"] = "test-topic3"
@ -1163,8 +1199,9 @@ async def test_skip_restoring_state_with_over_due_expire_trigger(
fake_extra_data = MagicMock() fake_extra_data = MagicMock()
mock_restore_cache_with_extra_data(hass, ((fake_state, fake_extra_data),)) mock_restore_cache_with_extra_data(hass, ((fake_state, fake_extra_data),))
with assert_setup_component(1, domain): assert await async_setup_component(
assert await async_setup_component(hass, domain, {domain: config3}) hass, mqtt.DOMAIN, {mqtt.DOMAIN: {domain: config3}}
)
await hass.async_block_till_done() await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
assert "Skip state recovery after reload for sensor.test3" in caplog.text 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, mqtt_mock_entry_with_yaml_config,
caplog, caplog,
sensor.DOMAIN, sensor.DOMAIN,
DEFAULT_CONFIG[sensor.DOMAIN], DEFAULT_CONFIG[mqtt.DOMAIN][sensor.DOMAIN],
topic, topic,
value, value,
attribute, attribute,
@ -1204,17 +1241,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = sensor.DOMAIN platform = sensor.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = sensor.DOMAIN domain = sensor.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 import pytest
from homeassistant.components import siren from homeassistant.components import mqtt, siren
from homeassistant.components.siren.const import ATTR_VOLUME_LEVEL from homeassistant.components.siren.const import ATTR_VOLUME_LEVEL
from homeassistant.const import ( from homeassistant.const import (
ATTR_ASSUMED_STATE, ATTR_ASSUMED_STATE,
@ -46,16 +46,21 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, help_test_update_with_json_attrs_not_dict,
) )
from tests.common import async_fire_mqtt_message from tests.common import async_fire_mqtt_message
DEFAULT_CONFIG = { 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) @pytest.fixture(autouse=True)
def siren_platform_only(): 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.""" """Test the controlling state via topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
siren.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
siren.DOMAIN: { siren.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_on": 1, "payload_on": 1,
"payload_off": 0, "payload_off": 0,
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the sending MQTT commands in optimistic mode."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
siren.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
siren.DOMAIN: { siren.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_on": "beer on", "payload_on": "beer on",
"payload_off": "beer off", "payload_off": "beer off",
"qos": "2", "qos": "2",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic and JSON message."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
siren.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
siren.DOMAIN: { siren.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -173,6 +180,7 @@ async def test_controlling_state_via_topic_and_json_message(
"payload_off": "beer off", "payload_off": "beer off",
"state_value_template": "{{ value_json.val }}", "state_value_template": "{{ value_json.val }}",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic and JSON message without a value template."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
siren.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
siren.DOMAIN: { siren.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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", "payload_off": "beer off",
"available_tones": ["ping", "siren", "bell"], "available_tones": ["ping", "siren", "bell"],
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test setting attributes with support flags optimistic."""
config = { config = {
"platform": "mqtt",
"command_topic": "command-topic", "command_topic": "command-topic",
"available_tones": ["ping", "siren", "bell"], "available_tones": ["ping", "siren", "bell"],
} }
@ -300,8 +308,8 @@ async def test_filtering_not_supported_attributes_optimistic(
assert await async_setup_component( assert await async_setup_component(
hass, hass,
siren.DOMAIN, mqtt.DOMAIN,
{siren.DOMAIN: [config1, config2, config3]}, {mqtt.DOMAIN: {siren.DOMAIN: [config1, config2, config3]}},
) )
await hass.async_block_till_done() await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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.""" """Test setting attributes with support flags via state."""
config = { config = {
"platform": "mqtt",
"command_topic": "command-topic", "command_topic": "command-topic",
"available_tones": ["ping", "siren", "bell"], "available_tones": ["ping", "siren", "bell"],
} }
@ -389,8 +396,8 @@ async def test_filtering_not_supported_attributes_via_state(
assert await async_setup_component( assert await async_setup_component(
hass, hass,
siren.DOMAIN, mqtt.DOMAIN,
{siren.DOMAIN: [config1, config2, config3]}, {mqtt.DOMAIN: {siren.DOMAIN: [config1, config2, config3]}},
) )
await hass.async_block_till_done() await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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): async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic.""" """Test availability by default payload with defined topic."""
config = { config = {
mqtt.DOMAIN: {
siren.DOMAIN: { siren.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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, "payload_off": 0,
} }
} }
}
await help_test_default_availability_payload( await help_test_default_availability_payload(
hass, 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): async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic.""" """Test availability by custom payload with defined topic."""
config = { config = {
mqtt.DOMAIN: {
siren.DOMAIN: { siren.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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, "payload_off": 0,
} }
} }
}
await help_test_custom_availability_payload( await help_test_custom_availability_payload(
hass, hass,
@ -513,10 +522,10 @@ async def test_custom_state_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test the state payload.""" """Test the state payload."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
siren.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
siren.DOMAIN: { siren.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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_on": "HIGH",
"state_off": "LOW", "state_off": "LOW",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, siren.DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one siren per unique_id.""" """Test unique id option only creates one siren per unique_id."""
config = { config = {
mqtt.DOMAIN: {
siren.DOMAIN: [ siren.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_topic": "command-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( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, siren.DOMAIN, config 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 hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test update of discovered siren.""" """Test update of discovered siren."""
config1 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN]) config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN])
config1["name"] = "Beer" config1["name"] = "Beer"
config2["name"] = "Milk" config2["name"] = "Milk"
config1["state_topic"] = "siren/state1" config1["state_topic"] = "siren/state1"
@ -673,8 +695,8 @@ async def test_discovery_update_siren_template(
hass, mqtt_mock_entry_no_yaml_config, caplog hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test update of discovered siren.""" """Test update of discovered siren."""
config1 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN]) config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[siren.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN])
config1["name"] = "Beer" config1["name"] = "Beer"
config2["name"] = "Milk" config2["name"] = "Milk"
config1["state_topic"] = "siren/state1" 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): async def test_command_templates(hass, mqtt_mock_entry_with_yaml_config, caplog):
"""Test siren with command templates optimistic.""" """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["name"] = "Beer"
config1["available_tones"] = ["ping", "chimes"] config1["available_tones"] = ["ping", "chimes"]
config1[ config1[
@ -719,8 +741,8 @@ async def test_command_templates(hass, mqtt_mock_entry_with_yaml_config, caplog)
assert await async_setup_component( assert await async_setup_component(
hass, hass,
siren.DOMAIN, mqtt.DOMAIN,
{siren.DOMAIN: [config1, config2]}, {mqtt.DOMAIN: {siren.DOMAIN: [config1, config2]}},
) )
await hass.async_block_till_done() await hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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.""" """Test publishing MQTT payload with command templates and different encoding."""
domain = siren.DOMAIN domain = siren.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[domain]) config = copy.deepcopy(DEFAULT_CONFIG)
config[siren.ATTR_AVAILABLE_TONES] = ["siren", "xylophone"] config[mqtt.DOMAIN][domain][siren.ATTR_AVAILABLE_TONES] = ["siren", "xylophone"]
await help_test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = siren.DOMAIN domain = siren.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = siren.DOMAIN domain = siren.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) 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, mqtt_mock_entry_with_yaml_config,
caplog, caplog,
siren.DOMAIN, siren.DOMAIN,
DEFAULT_CONFIG[siren.DOMAIN], DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN],
topic, topic,
value, value,
attribute, attribute,
@ -971,17 +995,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = siren.DOMAIN platform = siren.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = siren.DOMAIN domain = siren.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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 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.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 import CONF_SCHEMA, schema_state as mqttvacuum
from homeassistant.components.mqtt.vacuum.const import MQTT_VACUUM_ATTRIBUTES_BLOCKED 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_setting_blocked_attribute_via_mqtt_json_message,
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, 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, help_test_update_with_json_attrs_not_dict,
) )
@ -73,7 +73,8 @@ SEND_COMMAND_TOPIC = "vacuum/send_command"
STATE_TOPIC = "vacuum/state" STATE_TOPIC = "vacuum/state"
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
CONF_PLATFORM: "mqtt", mqtt.DOMAIN: {
vacuum.DOMAIN: {
CONF_SCHEMA: "state", CONF_SCHEMA: "state",
CONF_NAME: "mqtttest", CONF_NAME: "mqtttest",
CONF_COMMAND_TOPIC: COMMAND_TOPIC, CONF_COMMAND_TOPIC: COMMAND_TOPIC,
@ -81,11 +82,18 @@ DEFAULT_CONFIG = {
CONF_STATE_TOPIC: STATE_TOPIC, CONF_STATE_TOPIC: STATE_TOPIC,
mqttvacuum.CONF_SET_FAN_SPEED_TOPIC: "vacuum/set_fan_speed", mqttvacuum.CONF_SET_FAN_SPEED_TOPIC: "vacuum/set_fan_speed",
mqttvacuum.CONF_FAN_SPEED_LIST: ["min", "medium", "high", "max"], mqttvacuum.CONF_FAN_SPEED_LIST: ["min", "medium", "high", "max"],
}
}
} }
DEFAULT_CONFIG_2 = { DEFAULT_CONFIG_2 = {mqtt.DOMAIN: {vacuum.DOMAIN: {"schema": "state", "name": "test"}}}
vacuum.DOMAIN: {"platform": "mqtt", "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) @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): async def test_default_supported_features(hass, mqtt_mock_entry_with_yaml_config):
"""Test that the correct supported features.""" """Test that the correct supported features."""
assert await async_setup_component( assert await async_setup_component(hass, mqtt.DOMAIN, DEFAULT_CONFIG)
hass, vacuum.DOMAIN, {vacuum.DOMAIN: DEFAULT_CONFIG}
)
await hass.async_block_till_done() await hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
entity = hass.states.get("vacuum.mqtttest") 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): async def test_all_commands(hass, mqtt_mock_entry_with_yaml_config):
"""Test simple commands send to the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
mqttvacuum.ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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 hass, mqtt_mock_entry_with_yaml_config
): ):
"""Test commands which are not supported by the vacuum.""" """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"] services = mqttvacuum.STRING_TO_SERVICE["status"]
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
services, SERVICE_TO_STRING 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 hass.async_block_till_done()
mqtt_mock = await mqtt_mock_entry_with_yaml_config() 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): async def test_status(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
mqttvacuum.ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() await mqtt_mock_entry_with_yaml_config()
state = hass.states.get("vacuum.mqtttest") 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): async def test_no_fan_vacuum(hass, mqtt_mock_entry_with_yaml_config):
"""Test status updates from the vacuum when fan is not supported.""" """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] del config[mqttvacuum.CONF_FAN_SPEED_LIST]
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
mqttvacuum.DEFAULT_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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 @pytest.mark.no_fail_on_log_exception
async def test_status_invalid_json(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.""" """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( config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
mqttvacuum.ALL_SERVICES, SERVICE_TO_STRING 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 hass.async_block_till_done()
await mqtt_mock_entry_with_yaml_config() 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, vacuum.DOMAIN, DEFAULT_CONFIG_2 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one vacuum per unique_id.""" """Test unique id option only creates one vacuum per unique_id."""
config = { config = {
mqtt.DOMAIN: {
vacuum.DOMAIN: [ vacuum.DOMAIN: [
{ {
"platform": "mqtt",
"schema": "state", "schema": "state",
"name": "Test 1", "name": "Test 1",
"command_topic": "command-topic", "command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"schema": "state", "schema": "state",
"name": "Test 2", "name": "Test 2",
"command_topic": "command-topic", "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( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, vacuum.DOMAIN, config 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.""" """Test publishing MQTT payload with different encoding."""
domain = vacuum.DOMAIN domain = vacuum.DOMAIN
config = deepcopy(DEFAULT_CONFIG) config = deepcopy(DEFAULT_CONFIG)
config["supported_features"] = [ config[mqtt.DOMAIN][domain]["supported_features"] = [
"battery", "battery",
"clean_spot", "clean_spot",
"fan_speed", "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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = vacuum.DOMAIN domain = vacuum.DOMAIN
config = DEFAULT_CONFIG config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) 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, mqtt_mock_entry_with_yaml_config,
caplog, caplog,
vacuum.DOMAIN, vacuum.DOMAIN,
DEFAULT_CONFIG, DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN],
topic, topic,
value, value,
attribute, attribute,
@ -703,8 +733,18 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = vacuum.DOMAIN 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" config["name"] = "test"
del config["platform"] assert await async_setup_component(hass, domain, {domain: config})
await help_test_setup_manual_entity_from_yaml(hass, platform, config) await hass.async_block_till_done()
assert hass.states.get(f"{platform}.test") is not None 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 import pytest
from homeassistant.components import switch from homeassistant.components import mqtt, switch
from homeassistant.const import ( from homeassistant.const import (
ATTR_ASSUMED_STATE, ATTR_ASSUMED_STATE,
ATTR_DEVICE_CLASS, ATTR_DEVICE_CLASS,
@ -43,7 +43,7 @@ from .test_common import (
help_test_setup_manual_entity_from_yaml, help_test_setup_manual_entity_from_yaml,
help_test_unique_id, help_test_unique_id,
help_test_unload_config_entry_with_platform, 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, 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 from tests.components.switch import common
DEFAULT_CONFIG = { 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) @pytest.fixture(autouse=True)
def switch_platform_only(): 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.""" """Test the controlling state via topic."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
switch.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
switch.DOMAIN: { switch.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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, "payload_off": 0,
"device_class": "switch", "device_class": "switch",
} }
}
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -112,16 +118,17 @@ async def test_sending_mqtt_commands_and_optimistic(
assert await async_setup_component( assert await async_setup_component(
hass, hass,
switch.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
switch.DOMAIN: { switch.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
"payload_on": "beer on", "payload_on": "beer on",
"payload_off": "beer off", "payload_off": "beer off",
"qos": "2", "qos": "2",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the initial state in optimistic mode."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
switch.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
switch.DOMAIN: { switch.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"command_topic": "command-topic", "command_topic": "command-topic",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test the controlling state via topic and JSON message."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
switch.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
switch.DOMAIN: { switch.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
@ -189,6 +197,7 @@ async def test_controlling_state_via_topic_and_json_message(
"payload_off": "beer off", "payload_off": "beer off",
"value_template": "{{ value_json.val }}", "value_template": "{{ value_json.val }}",
} }
}
}, },
) )
await hass.async_block_till_done() 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): async def test_default_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by default payload with defined topic.""" """Test availability by default payload with defined topic."""
config = { config = {
mqtt.DOMAIN: {
switch.DOMAIN: { switch.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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, "payload_off": 0,
} }
} }
}
await help_test_default_availability_payload( await help_test_default_availability_payload(
hass, 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): async def test_custom_availability_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test availability by custom payload with defined topic.""" """Test availability by custom payload with defined topic."""
config = { config = {
mqtt.DOMAIN: {
switch.DOMAIN: { switch.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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, "payload_off": 0,
} }
} }
}
await help_test_custom_availability_payload( await help_test_custom_availability_payload(
hass, hass,
@ -281,10 +292,10 @@ async def test_custom_state_payload(hass, mqtt_mock_entry_with_yaml_config):
"""Test the state payload.""" """Test the state payload."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
switch.DOMAIN, mqtt.DOMAIN,
{ {
mqtt.DOMAIN: {
switch.DOMAIN: { switch.DOMAIN: {
"platform": "mqtt",
"name": "test", "name": "test",
"state_topic": "state-topic", "state_topic": "state-topic",
"command_topic": "command-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_on": "HIGH",
"state_off": "LOW", "state_off": "LOW",
} }
}
}, },
) )
await hass.async_block_till_done() 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.""" """Test attributes get extracted from a JSON result."""
await help_test_update_with_json_attrs_not_dict( 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 hass, mqtt_mock_entry_with_yaml_config, caplog
): ):
"""Test attributes get extracted from a JSON result.""" """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, switch.DOMAIN, DEFAULT_CONFIG 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): async def test_discovery_update_attr(hass, mqtt_mock_entry_no_yaml_config, caplog):
"""Test update of discovered MQTTAttributes.""" """Test update of discovered MQTTAttributes."""
await help_test_discovery_update_attr( 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): async def test_unique_id(hass, mqtt_mock_entry_with_yaml_config):
"""Test unique id option only creates one switch per unique_id.""" """Test unique id option only creates one switch per unique_id."""
config = { config = {
mqtt.DOMAIN: {
switch.DOMAIN: [ switch.DOMAIN: [
{ {
"platform": "mqtt",
"name": "Test 1", "name": "Test 1",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_topic": "command-topic", "command_topic": "command-topic",
"unique_id": "TOTALLY_UNIQUE", "unique_id": "TOTALLY_UNIQUE",
}, },
{ {
"platform": "mqtt",
"name": "Test 2", "name": "Test 2",
"state_topic": "test-topic", "state_topic": "test-topic",
"command_topic": "command-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( await help_test_unique_id(
hass, mqtt_mock_entry_with_yaml_config, switch.DOMAIN, config 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 hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test update of discovered switch.""" """Test update of discovered switch."""
config1 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN]) config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN])
config1["name"] = "Beer" config1["name"] = "Beer"
config2["name"] = "Milk" config2["name"] = "Milk"
config1["state_topic"] = "switch/state1" config1["state_topic"] = "switch/state1"
@ -441,8 +465,8 @@ async def test_discovery_update_switch_template(
hass, mqtt_mock_entry_no_yaml_config, caplog hass, mqtt_mock_entry_no_yaml_config, caplog
): ):
"""Test update of discovered switch.""" """Test update of discovered switch."""
config1 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN]) config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN])
config2 = copy.deepcopy(DEFAULT_CONFIG[switch.DOMAIN]) config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN])
config1["name"] = "Beer" config1["name"] = "Beer"
config2["name"] = "Milk" config2["name"] = "Milk"
config1["state_topic"] = "switch/state1" config1["state_topic"] = "switch/state1"
@ -593,7 +617,7 @@ async def test_publishing_with_custom_encoding(
): ):
"""Test publishing MQTT payload with different encoding.""" """Test publishing MQTT payload with different encoding."""
domain = switch.DOMAIN domain = switch.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_publishing_with_custom_encoding( await help_test_publishing_with_custom_encoding(
hass, 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): async def test_reloadable(hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path):
"""Test reloading the MQTT platform.""" """Test reloading the MQTT platform."""
domain = switch.DOMAIN domain = switch.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_reloadable( await help_test_reloadable(
hass, mqtt_mock_entry_with_yaml_config, caplog, tmp_path, domain, config 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): async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path):
"""Test reloading the MQTT platform with late entry setup.""" """Test reloading the MQTT platform with late entry setup."""
domain = switch.DOMAIN domain = switch.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG_LEGACY[domain]
await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) 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, mqtt_mock_entry_with_yaml_config,
caplog, caplog,
switch.DOMAIN, switch.DOMAIN,
DEFAULT_CONFIG[switch.DOMAIN], DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN],
topic, topic,
value, value,
attribute, attribute,
@ -657,17 +683,27 @@ async def test_encoding_subscribable_topics(
async def test_setup_manual_entity_from_yaml(hass): async def test_setup_manual_entity_from_yaml(hass):
"""Test setup manual configured MQTT entity.""" """Test setup manual configured MQTT entity."""
platform = switch.DOMAIN platform = switch.DOMAIN
config = copy.deepcopy(DEFAULT_CONFIG[platform]) await help_test_setup_manual_entity_from_yaml(hass, DEFAULT_CONFIG)
config["name"] = "test" assert hass.states.get(f"{platform}.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
async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path):
"""Test unloading the config entry.""" """Test unloading the config entry."""
domain = switch.DOMAIN domain = switch.DOMAIN
config = DEFAULT_CONFIG[domain] config = DEFAULT_CONFIG
await help_test_unload_config_entry_with_platform( await help_test_unload_config_entry_with_platform(
hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config 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): async def test_camera_reconnect_webhook(hass, config_entry):
"""Test webhook event on camera reconnect.""" """Test webhook event on camera reconnect."""
fake_post_hits = 0 fake_post_hits = 0

View File

@ -100,6 +100,15 @@ MOCK_SSDP_DATA = ssdp.SsdpServiceInfo(
ATTR_UPNP_UDN: "uuid:0d1cef00-00dc-1000-9c80-4844f7b172de", 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( MOCK_SSDP_DATA_NOPREFIX = ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn", 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" 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( @pytest.mark.parametrize(
"data", [MOCK_SSDP_DATA_MAIN_TV_AGENT_ST, MOCK_SSDP_DATA_RENDERING_CONTROL_ST] "data", [MOCK_SSDP_DATA_MAIN_TV_AGENT_ST, MOCK_SSDP_DATA_RENDERING_CONTROL_ST]
) )