diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2e981a6eef4a..fd63670249a4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1326,7 +1326,7 @@ jobs: pattern: coverage-* - name: Upload coverage to Codecov if: needs.info.outputs.test_full_suite == 'true' - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: fail_ci_if_error: true flags: full-suite @@ -1485,7 +1485,7 @@ jobs: pattern: coverage-* - name: Upload coverage to Codecov if: needs.info.outputs.test_full_suite == 'false' - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} # zizmor: ignore[secrets-outside-env] @@ -1513,7 +1513,7 @@ jobs: with: pattern: test-results-* - name: Upload test results to Codecov - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: report_type: test_results fail_ci_if_error: true diff --git a/homeassistant/components/alexa_devices/button.py b/homeassistant/components/alexa_devices/button.py index 1eebe201c821..8a30f8337836 100644 --- a/homeassistant/components/alexa_devices/button.py +++ b/homeassistant/components/alexa_devices/button.py @@ -6,7 +6,7 @@ from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.util import slugify -from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator +from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator, alexa_api_call from .entity import AmazonServiceEntity # Coordinator is used to centralize the data updates @@ -49,4 +49,5 @@ class AmazonRoutineButton(AmazonServiceEntity, ButtonEntity): async def async_press(self) -> None: """Handle button press action.""" - await self.coordinator.api.call_routine(self._routine) + async with alexa_api_call(self.coordinator): + await self.coordinator.api.call_routine(self._routine) diff --git a/homeassistant/components/alexa_devices/coordinator.py b/homeassistant/components/alexa_devices/coordinator.py index 4a67f5758172..98d2506a4ddd 100644 --- a/homeassistant/components/alexa_devices/coordinator.py +++ b/homeassistant/components/alexa_devices/coordinator.py @@ -1,5 +1,7 @@ """Support for Alexa Devices.""" +from collections.abc import AsyncGenerator +from contextlib import asynccontextmanager from datetime import timedelta from aioamazondevices.api import AmazonEchoApi @@ -19,7 +21,11 @@ from aiohttp import ClientSession from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.exceptions import ( + ConfigEntryAuthFailed, + ConfigEntryNotReady, + HomeAssistantError, +) from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -29,6 +35,65 @@ from .const import _LOGGER, CONF_LOGIN_DATA, DOMAIN SCAN_INTERVAL = 300 + +@asynccontextmanager +async def alexa_api_call( + coordinator: DataUpdateCoordinator | None = None, +) -> AsyncGenerator[None]: + """Handle common Alexa API exceptions as HomeAssistantError.""" + try: + yield + except CannotAuthenticate as err: + if coordinator: + coordinator.last_update_success = False + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="invalid_auth", + translation_placeholders={"error": repr(err)}, + ) from err + except CannotConnect as err: + if coordinator: + coordinator.last_update_success = False + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="cannot_connect_with_error", + translation_placeholders={"error": repr(err)}, + ) from err + except (CannotRetrieveData, ValueError) as err: + if coordinator: + coordinator.last_update_success = False + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="cannot_retrieve_data_with_error", + translation_placeholders={"error": repr(err)}, + ) from err + + +@asynccontextmanager +async def alexa_config_entry_errors() -> AsyncGenerator[None]: + """Handle common Alexa API exceptions as ConfigEntry errors.""" + try: + yield + except CannotAuthenticate as err: + raise ConfigEntryAuthFailed( + translation_domain=DOMAIN, + translation_key="invalid_auth", + translation_placeholders={"error": repr(err)}, + ) from err + except (CannotConnect, TimeoutError) as err: + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="cannot_connect_with_error", + translation_placeholders={"error": repr(err)}, + ) from err + except (CannotRetrieveData, ValueError, KeyError, StopIteration) as err: + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="cannot_retrieve_data_with_error", + translation_placeholders={"error": repr(err)}, + ) from err + + type AmazonConfigEntry = ConfigEntry[AmazonDevicesCoordinator] @@ -113,6 +178,12 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]): translation_key="invalid_auth", translation_placeholders={"error": repr(err)}, ) from err + except ValueError as err: + raise UpdateFailed( + translation_domain=DOMAIN, + translation_key="cannot_retrieve_data_with_error", + translation_placeholders={"error": repr(err)}, + ) from err else: current_devices = set(data.keys()) if stale_devices := self.previous_devices - current_devices: @@ -169,26 +240,8 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]): async def sync_history_state(self) -> None: """Sync history state.""" - try: + async with alexa_config_entry_errors(): self._vocal_records = await self.api.sync_history_state() - except CannotAuthenticate as e: - raise ConfigEntryAuthFailed( - translation_domain=DOMAIN, - translation_key="invalid_auth", - translation_placeholders={"error": repr(e)}, - ) from e - except CannotConnect as e: - raise ConfigEntryNotReady( - translation_domain=DOMAIN, - translation_key="cannot_connect_with_error", - translation_placeholders={"error": repr(e)}, - ) from e - except BaseException as e: - raise ConfigEntryNotReady( - translation_domain=DOMAIN, - translation_key="cannot_retrieve_data_with_error", - translation_placeholders={"error": repr(e)}, - ) from e async def history_state_event_handler( self, vocal_records: dict[str, AmazonVocalRecord] @@ -204,26 +257,8 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]): async def sync_media_state(self) -> None: """Sync media state.""" - try: + async with alexa_config_entry_errors(): await self.api.sync_media_state() - except CannotAuthenticate as err: - raise ConfigEntryAuthFailed( - translation_domain=DOMAIN, - translation_key="invalid_auth", - translation_placeholders={"error": repr(err)}, - ) from err - except (CannotConnect, TimeoutError) as err: - raise ConfigEntryNotReady( - translation_domain=DOMAIN, - translation_key="cannot_connect_with_error", - translation_placeholders={"error": repr(err)}, - ) from err - except (CannotRetrieveData, ValueError) as err: - raise ConfigEntryNotReady( - translation_domain=DOMAIN, - translation_key="cannot_retrieve_data_with_error", - translation_placeholders={"error": repr(err)}, - ) from err async def media_state_event_handler( self, media_state: dict[str, AmazonMediaState] diff --git a/homeassistant/components/alexa_devices/media_player.py b/homeassistant/components/alexa_devices/media_player.py index 34fdeeed01da..9aaf0bd19a41 100644 --- a/homeassistant/components/alexa_devices/media_player.py +++ b/homeassistant/components/alexa_devices/media_player.py @@ -22,9 +22,8 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import _LOGGER -from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator +from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator, alexa_api_call from .entity import AmazonEntity -from .utils import alexa_api_call PARALLEL_UPDATES = 1 @@ -216,16 +215,15 @@ class AlexaDevicesMediaPlayer(AmazonEntity, MediaPlayerEntity): provider = media_type.value if isinstance(media_type, MediaType) else media_type await self.async_call_alexa_music(media_id, provider) - @alexa_api_call async def async_call_alexa_music( self, search_phrase: str, provider_id: str ) -> None: """Call alexa music.""" - await self.coordinator.api.call_alexa_music( - self.device, search_phrase, provider_id - ) + async with alexa_api_call(self.coordinator): + await self.coordinator.api.call_alexa_music( + self.device, search_phrase, provider_id + ) - @alexa_api_call async def async_set_device_volume(self, volume: int) -> None: """Set the device volume.""" _LOGGER.debug( @@ -233,7 +231,8 @@ class AlexaDevicesMediaPlayer(AmazonEntity, MediaPlayerEntity): self.device.serial_number, volume, ) - await self.coordinator.api.set_device_volume(self.device, volume) + async with alexa_api_call(self.coordinator): + await self.coordinator.api.set_device_volume(self.device, volume) async def async_set_volume_level(self, volume: float) -> None: """Set the volume level (0.0 to 1.0).""" @@ -263,12 +262,12 @@ class AlexaDevicesMediaPlayer(AmazonEntity, MediaPlayerEntity): await self.async_set_volume_level(target_volume / 100) self._prev_volume = None - @alexa_api_call async def _send_media_command(self, command: AmazonMediaControls) -> None: _LOGGER.debug( "Sending media command '%s' to %s", command, self.device.serial_number ) - await self.coordinator.api.send_media_command(self.device, command) + async with alexa_api_call(self.coordinator): + await self.coordinator.api.send_media_command(self.device, command) async def async_media_stop(self) -> None: """Send stop command.""" diff --git a/homeassistant/components/alexa_devices/notify.py b/homeassistant/components/alexa_devices/notify.py index c810275afa30..c36c7a6bd2b4 100644 --- a/homeassistant/components/alexa_devices/notify.py +++ b/homeassistant/components/alexa_devices/notify.py @@ -12,9 +12,8 @@ from homeassistant.components.notify import NotifyEntity, NotifyEntityDescriptio from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .coordinator import AmazonConfigEntry +from .coordinator import AmazonConfigEntry, alexa_api_call from .entity import AmazonEntity -from .utils import alexa_api_call PARALLEL_UPDATES = 1 @@ -80,10 +79,11 @@ class AmazonNotifyEntity(AmazonEntity, NotifyEntity): entity_description: AmazonNotifyEntityDescription - @alexa_api_call async def async_send_message( self, message: str, title: str | None = None, **kwargs: Any ) -> None: """Send a message.""" - - await self.entity_description.method(self.coordinator.api, self.device, message) + async with alexa_api_call(self.coordinator): + await self.entity_description.method( + self.coordinator.api, self.device, message + ) diff --git a/homeassistant/components/alexa_devices/services.py b/homeassistant/components/alexa_devices/services.py index 06beb5258f3e..1a4eca3844b2 100644 --- a/homeassistant/components/alexa_devices/services.py +++ b/homeassistant/components/alexa_devices/services.py @@ -11,7 +11,7 @@ from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers import config_validation as cv, device_registry as dr from .const import DOMAIN, INFO_SKILLS_MAPPING -from .coordinator import AmazonConfigEntry +from .coordinator import AmazonConfigEntry, alexa_api_call ATTR_TEXT_COMMAND = "text_command" ATTR_SOUND = "sound" @@ -85,13 +85,15 @@ async def _async_execute_action(call: ServiceCall, attribute: str) -> None: translation_key="invalid_sound_value", translation_placeholders={"sound": value}, ) - await coordinator.api.call_alexa_sound( - coordinator.data[device.serial_number], value - ) + async with alexa_api_call(): + await coordinator.api.call_alexa_sound( + coordinator.data[device.serial_number], value + ) elif attribute == ATTR_TEXT_COMMAND: - await coordinator.api.call_alexa_text_command( - coordinator.data[device.serial_number], value - ) + async with alexa_api_call(): + await coordinator.api.call_alexa_text_command( + coordinator.data[device.serial_number], value + ) elif attribute == ATTR_INFO_SKILL: info_skill = INFO_SKILLS_MAPPING.get(value) if info_skill not in ALEXA_INFO_SKILLS: @@ -100,9 +102,10 @@ async def _async_execute_action(call: ServiceCall, attribute: str) -> None: translation_key="invalid_info_skill_value", translation_placeholders={"info_skill": value}, ) - await coordinator.api.call_alexa_info_skill( - coordinator.data[device.serial_number], info_skill - ) + async with alexa_api_call(): + await coordinator.api.call_alexa_info_skill( + coordinator.data[device.serial_number], info_skill + ) async def async_send_sound_notification(call: ServiceCall) -> None: diff --git a/homeassistant/components/alexa_devices/switch.py b/homeassistant/components/alexa_devices/switch.py index 274daacfcf6d..54b28e49ad25 100644 --- a/homeassistant/components/alexa_devices/switch.py +++ b/homeassistant/components/alexa_devices/switch.py @@ -14,13 +14,9 @@ from homeassistant.components.switch import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .coordinator import AmazonConfigEntry +from .coordinator import AmazonConfigEntry, alexa_api_call from .entity import AmazonEntity -from .utils import ( - alexa_api_call, - async_remove_dnd_from_virtual_group, - async_update_unique_id, -) +from .utils import async_remove_dnd_from_virtual_group, async_update_unique_id PARALLEL_UPDATES = 1 @@ -90,7 +86,6 @@ class AmazonSwitchEntity(AmazonEntity, SwitchEntity): entity_description: AmazonSwitchEntityDescription - @alexa_api_call async def _switch_set_state(self, state: bool) -> None: """Set desired switch state.""" method = getattr(self.coordinator.api, self.entity_description.method) @@ -98,7 +93,8 @@ class AmazonSwitchEntity(AmazonEntity, SwitchEntity): if TYPE_CHECKING: assert method is not None - await method(self.device, state) + async with alexa_api_call(self.coordinator): + await method(self.device, state) self.coordinator.data[self.device.serial_number].sensors[ self.entity_description.key ].value = state diff --git a/homeassistant/components/alexa_devices/utils.py b/homeassistant/components/alexa_devices/utils.py index 691ce68549ba..1a10d6b0f27a 100644 --- a/homeassistant/components/alexa_devices/utils.py +++ b/homeassistant/components/alexa_devices/utils.py @@ -1,54 +1,19 @@ """Utils for Alexa Devices.""" -from collections.abc import Awaitable, Callable, Coroutine -from functools import wraps -from typing import Any, Concatenate - from aioamazondevices.const.devices import SPEAKER_GROUP_FAMILY from aioamazondevices.const.schedules import ( NOTIFICATION_ALARM, NOTIFICATION_REMINDER, NOTIFICATION_TIMER, ) -from aioamazondevices.exceptions import CannotConnect, CannotRetrieveData from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.entity_registry as er from .const import _LOGGER, DOMAIN from .coordinator import AmazonDevicesCoordinator -from .entity import AmazonEntity - - -def alexa_api_call[_T: AmazonEntity, **_P]( - func: Callable[Concatenate[_T, _P], Awaitable[None]], -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: - """Catch Alexa API call exceptions.""" - - @wraps(func) - async def cmd_wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None: - """Wrap all command methods.""" - try: - await func(self, *args, **kwargs) - except CannotConnect as err: - self.coordinator.last_update_success = False - raise HomeAssistantError( - translation_domain=DOMAIN, - translation_key="cannot_connect_with_error", - translation_placeholders={"error": repr(err)}, - ) from err - except CannotRetrieveData as err: - self.coordinator.last_update_success = False - raise HomeAssistantError( - translation_domain=DOMAIN, - translation_key="cannot_retrieve_data_with_error", - translation_placeholders={"error": repr(err)}, - ) from err - - return cmd_wrapper async def async_update_unique_id( diff --git a/homeassistant/components/assist_pipeline/pipeline.py b/homeassistant/components/assist_pipeline/pipeline.py index 10ec91dace20..65939a1aec5d 100644 --- a/homeassistant/components/assist_pipeline/pipeline.py +++ b/homeassistant/components/assist_pipeline/pipeline.py @@ -1816,6 +1816,11 @@ class PipelineInput: await self.run.text_to_speech(tts_input) except PipelineError as err: + if self.run.tts_stream: + # Clean up TTS stream + self.run.tts_stream.delete() + self.run.tts_stream = None + self.run.process_event( PipelineEvent( PipelineEventType.ERROR, @@ -1885,15 +1890,17 @@ class PipelineInput: ): prepare_tasks.append(self.run.prepare_recognize_intent(self.session)) + if prepare_tasks: + await asyncio.gather(*prepare_tasks) + + # Do TTS prepare separately so we don't create a ResultStream if the + # pipeline is invalid. if ( start_stage_index <= PIPELINE_STAGE_ORDER.index(PipelineStage.TTS) <= end_stage_index ): - prepare_tasks.append(self.run.prepare_text_to_speech()) - - if prepare_tasks: - await asyncio.gather(*prepare_tasks) + await self.run.prepare_text_to_speech() class PipelinePreferred(CollectionError): diff --git a/homeassistant/components/daikin/manifest.json b/homeassistant/components/daikin/manifest.json index 4b07dc1d98e8..dfb353b8719e 100644 --- a/homeassistant/components/daikin/manifest.json +++ b/homeassistant/components/daikin/manifest.json @@ -7,6 +7,6 @@ "integration_type": "device", "iot_class": "local_polling", "loggers": ["pydaikin"], - "requirements": ["pydaikin==2.17.2"], + "requirements": ["pydaikin==2.18.1"], "zeroconf": ["_dkapi._tcp.local."] } diff --git a/homeassistant/components/esphome/entry_data.py b/homeassistant/components/esphome/entry_data.py index 8d3e2ff1547a..7dd004c2af83 100644 --- a/homeassistant/components/esphome/entry_data.py +++ b/homeassistant/components/esphome/entry_data.py @@ -166,6 +166,8 @@ class RuntimeEntryData: ) loaded_platforms: set[Platform] = field(default_factory=set) platform_load_lock: asyncio.Lock = field(default_factory=asyncio.Lock) + # Set once the first connection has finished scanner setup or teardown. + first_connect_done: asyncio.Event = field(default_factory=asyncio.Event) _storage_contents: StoreData | None = None _pending_storage: Callable[[], StoreData] | None = None assist_pipeline_update_callbacks: list[CALLBACK_TYPE] = field(default_factory=list) diff --git a/homeassistant/components/esphome/manager.py b/homeassistant/components/esphome/manager.py index 2a6d38471722..3eeaed77be5e 100644 --- a/homeassistant/components/esphome/manager.py +++ b/homeassistant/components/esphome/manager.py @@ -1,11 +1,12 @@ """Manager for esphome devices.""" +import asyncio import base64 from functools import partial import logging import secrets import struct -from typing import TYPE_CHECKING, Any, NamedTuple +from typing import TYPE_CHECKING, Any, Final, NamedTuple from aioesphomeapi import ( APIClient, @@ -106,6 +107,9 @@ if TYPE_CHECKING: _LOGGER = logging.getLogger(__name__) +# Max time to wait at startup for a BLE proxy to register its scanner. +STARTUP_SCANNER_WAIT: Final = 3.0 + LOG_LEVEL_TO_LOGGER = { LogLevel.LOG_LEVEL_NONE: logging.DEBUG, LogLevel.LOG_LEVEL_ERROR: logging.ERROR, @@ -677,6 +681,8 @@ class ESPHomeManager: hass, device_info.bluetooth_mac_address or device_info.mac_address ) + entry_data.first_connect_done.set() + if device_info.voice_assistant_feature_flags_compat(api_version) and ( Platform.ASSIST_SATELLITE not in entry_data.loaded_platforms ): @@ -988,6 +994,21 @@ class ESPHomeManager: await reconnect_logic.start() + # Wait for a cached BLE proxy to register its scanner before finishing setup. + if ( + device_info := entry_data.device_info + ) is not None and device_info.bluetooth_proxy_feature_flags_compat( + entry_data.api_version + ): + try: + async with asyncio.timeout(STARTUP_SCANNER_WAIT): + await entry_data.first_connect_done.wait() + except TimeoutError: + _LOGGER.debug( + "%s: Timed out waiting for Bluetooth scanner to register", + self.host, + ) + @callback def _async_setup_device_registry( diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 83d3f469ca53..3209faed47f2 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -21,5 +21,5 @@ "integration_type": "system", "preview_features": { "winter_mode": {} }, "quality_scale": "internal", - "requirements": ["home-assistant-frontend==20260527.4"] + "requirements": ["home-assistant-frontend==20260527.5"] } diff --git a/homeassistant/components/gardena_bluetooth/__init__.py b/homeassistant/components/gardena_bluetooth/__init__.py index f7b116aa2c07..4526c9c852b3 100644 --- a/homeassistant/components/gardena_bluetooth/__init__.py +++ b/homeassistant/components/gardena_bluetooth/__init__.py @@ -1,11 +1,13 @@ """The Gardena Bluetooth integration.""" +from contextlib import suppress import logging from bleak.backends.device import BLEDevice from gardena_bluetooth.client import CachedConnection, Client -from gardena_bluetooth.const import ProductType -from gardena_bluetooth.scan import async_get_manufacturer_data +from gardena_bluetooth.const import ScanService +from gardena_bluetooth.parse import ManufacturerData, ProductType +from habluetooth import BluetoothServiceInfoBleak from homeassistant.components import bluetooth from homeassistant.const import CONF_ADDRESS, Platform @@ -30,6 +32,64 @@ PLATFORMS: list[Platform] = [ ] LOGGER = logging.getLogger(__name__) DISCONNECT_DELAY = 5 +PRODUCTS_SCAN_TIMEOUT = 10 +PRODUCT_TYPE_TIMEOUT = 30 + + +async def async_get_product_type(hass: HomeAssistant, address: str) -> ProductType: + """Get a product type for the given address.""" + + data = ManufacturerData() + + def _data_callback(info: BluetoothServiceInfoBleak) -> bool: + LOGGER.debug("Processing advertisement from %s: %s", info.address, info) + if info.device.address != address: + return False + + data.update(info.manufacturer_data.get(ManufacturerData.company, b"")) + return data.product_type is not ProductType.UNKNOWN + + with suppress(TimeoutError): + await bluetooth.async_process_advertisements( + hass, + _data_callback, + bluetooth.BluetoothCallbackMatcher( + address=address, manufacturer_id=ManufacturerData.company + ), + mode=bluetooth.BluetoothScanningMode.ACTIVE, + timeout=PRODUCT_TYPE_TIMEOUT, + ) + return data.product_type + + +async def async_get_products(hass: HomeAssistant) -> dict[str, ManufacturerData]: + """Get all products that are currently advertising.""" + products: dict[str, ManufacturerData] = {} + + def _data_callback(info: BluetoothServiceInfoBleak) -> bool: + LOGGER.debug("Processing advertisement from %s: %s", info.address, info) + if ScanService not in info.service_uuids: + return False + + raw = info.manufacturer_data.get(ManufacturerData.company, b"") + if (data := products.get(info.device.address)) is None: + data = ManufacturerData() + products[info.device.address] = data + + data.update(raw) + return False + + with suppress(TimeoutError): + await bluetooth.async_process_advertisements( + hass, + _data_callback, + bluetooth.BluetoothCallbackMatcher( + manufacturer_id=ManufacturerData.company + ), + mode=bluetooth.BluetoothScanningMode.ACTIVE, + timeout=PRODUCTS_SCAN_TIMEOUT, + ) + return products def get_connection(hass: HomeAssistant, address: str) -> CachedConnection: @@ -53,12 +113,7 @@ async def async_setup_entry( address = entry.data[CONF_ADDRESS] - try: - mfg_data = await async_get_manufacturer_data({address}) - except TimeoutError as exc: - raise ConfigEntryNotReady("Unable to find product type") from exc - - product_type = mfg_data[address].product_type + product_type = await async_get_product_type(hass, address) if product_type is ProductType.UNKNOWN: raise ConfigEntryNotReady("Unable to find product type") diff --git a/homeassistant/components/gardena_bluetooth/config_flow.py b/homeassistant/components/gardena_bluetooth/config_flow.py index c7a1a1adbfc3..f7a18157bd68 100644 --- a/homeassistant/components/gardena_bluetooth/config_flow.py +++ b/homeassistant/components/gardena_bluetooth/config_flow.py @@ -4,21 +4,17 @@ import logging from typing import Any from gardena_bluetooth.client import Client -from gardena_bluetooth.const import PRODUCT_NAMES, DeviceInformation, ScanService +from gardena_bluetooth.const import PRODUCT_NAMES, DeviceInformation from gardena_bluetooth.exceptions import CharacteristicNotFound, CommunicationFailure -from gardena_bluetooth.parse import ManufacturerData, ProductType -from gardena_bluetooth.scan import async_get_manufacturer_data +from gardena_bluetooth.parse import ProductType import voluptuous as vol -from homeassistant.components.bluetooth import ( - BluetoothServiceInfo, - async_discovered_service_info, -) +from homeassistant.components.bluetooth import BluetoothServiceInfo from homeassistant.config_entries import ConfigFlow, ConfigFlowResult from homeassistant.const import CONF_ADDRESS from homeassistant.data_entry_flow import AbortFlow -from . import get_connection +from . import async_get_product_type, async_get_products, get_connection from .const import DOMAIN _LOGGER = logging.getLogger(__name__) @@ -33,17 +29,6 @@ _SUPPORTED_PRODUCT_TYPES = { } -def _is_supported(discovery_info: BluetoothServiceInfo): - """Check if device is supported.""" - if ScanService not in discovery_info.service_uuids: - return False - - if discovery_info.manufacturer_data.get(ManufacturerData.company) is None: - _LOGGER.debug("Missing manufacturer data: %s", discovery_info) - return False - return True - - class GardenaBluetoothConfigFlow(ConfigFlow, domain=DOMAIN): """Handle a config flow for Gardena Bluetooth.""" @@ -75,8 +60,7 @@ class GardenaBluetoothConfigFlow(ConfigFlow, domain=DOMAIN): ) -> ConfigFlowResult: """Handle the bluetooth discovery step.""" _LOGGER.debug("Discovered device: %s", discovery_info) - data = await async_get_manufacturer_data({discovery_info.address}) - product_type = data[discovery_info.address].product_type + product_type = await async_get_product_type(self.hass, discovery_info.address) if product_type not in _SUPPORTED_PRODUCT_TYPES: return self.async_abort(reason="no_devices_found") @@ -117,22 +101,16 @@ class GardenaBluetoothConfigFlow(ConfigFlow, domain=DOMAIN): self._abort_if_unique_id_configured() return await self.async_step_confirm() - current_addresses = self._async_current_ids(include_ignore=False) - candidates = set() - for discovery_info in async_discovered_service_info(self.hass): - address = discovery_info.address - if address in current_addresses or not _is_supported(discovery_info): - continue - candidates.add(address) - - data = await async_get_manufacturer_data(candidates) - for address, mfg_data in data.items(): - if mfg_data.product_type not in _SUPPORTED_PRODUCT_TYPES: - continue - self.devices[address] = PRODUCT_NAMES[mfg_data.product_type] + current = self._async_current_ids(include_ignore=False) + devices = await async_get_products(self.hass) # Keep selection sorted by address to ensure stable tests - self.devices = dict(sorted(self.devices.items(), key=lambda x: x[0])) + self.devices = { + address: PRODUCT_NAMES[data.product_type] + for address in sorted(devices) + if address not in current + and (data := devices[address]).product_type in _SUPPORTED_PRODUCT_TYPES + } if not self.devices: return self.async_abort(reason="no_devices_found") diff --git a/homeassistant/components/homeassistant_connect_zbt2/__init__.py b/homeassistant/components/homeassistant_connect_zbt2/__init__.py index 0d7edab7a529..166744eccc8b 100644 --- a/homeassistant/components/homeassistant_connect_zbt2/__init__.py +++ b/homeassistant/components/homeassistant_connect_zbt2/__init__.py @@ -135,7 +135,24 @@ async def async_migrate_entry( ) if canonical.entry_id != config_entry.entry_id: - # The canonical entry's migration will remove this duplicate. + if canonical.minor_version < 2: + # The canonical entry has not been migrated yet and its + # migration will remove this duplicate. + return False + + # The canonical entry is already fully migrated and will not run + # a migration that removes this duplicate, so remove it here. The + # entry can't remove itself while its setup lock is held, so + # schedule the removal instead. + _LOGGER.debug( + "Removing duplicate config entry %s for serial %s in favor of %s", + config_entry.entry_id, + serial_number, + canonical.entry_id, + ) + hass.async_create_task( + hass.config_entries.async_remove(config_entry.entry_id) + ) return False for duplicate in duplicates: diff --git a/homeassistant/components/homeassistant_sky_connect/__init__.py b/homeassistant/components/homeassistant_sky_connect/__init__.py index f39f42f15747..16e2ce377ed4 100644 --- a/homeassistant/components/homeassistant_sky_connect/__init__.py +++ b/homeassistant/components/homeassistant_sky_connect/__init__.py @@ -239,7 +239,24 @@ async def async_migrate_entry( ) if canonical.entry_id != config_entry.entry_id: - # The canonical entry's migration will remove this duplicate. + if canonical.minor_version < 5: + # The canonical entry has not been migrated yet and its + # migration will remove this duplicate. + return False + + # The canonical entry is already fully migrated and will not run + # a migration that removes this duplicate, so remove it here. The + # entry can't remove itself while its setup lock is held, so + # schedule the removal instead. + _LOGGER.warning( + "Removing duplicate config entry %s for serial %s in favor of %s", + config_entry.entry_id, + serial_number, + canonical.entry_id, + ) + hass.async_create_task( + hass.config_entries.async_remove(config_entry.entry_id) + ) return False for duplicate in duplicates: diff --git a/homeassistant/components/mitsubishi_comfort/manifest.json b/homeassistant/components/mitsubishi_comfort/manifest.json index 4bb6c9759090..464c4ffd2249 100644 --- a/homeassistant/components/mitsubishi_comfort/manifest.json +++ b/homeassistant/components/mitsubishi_comfort/manifest.json @@ -8,5 +8,5 @@ "integration_type": "hub", "iot_class": "local_polling", "quality_scale": "bronze", - "requirements": ["mitsubishi-comfort==0.3.0"] + "requirements": ["mitsubishi-comfort==0.3.1"] } diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 47c6cf27bc24..76d37b98dfeb 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -412,6 +412,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def _reload_config(call: ServiceCall) -> None: """Reload the platforms.""" + if not mqtt_config_entry_enabled(hass): + _LOGGER.debug( + "Skipped reloading MQTT integration, " + "the MQTT config entry is not enabled" + ) + return entry: ConfigEntry = next(iter(hass.config_entries.async_entries(DOMAIN))) mqtt_data = hass.data[DATA_MQTT] diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index 9b560db52e35..c8a1ba449e73 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -159,11 +159,14 @@ class OpenThermGatewayHub: _LOGGER.debug("Received report: %s", status) async_dispatcher_send(self.hass, self.update_signal, status) + boiler_manufacturer = status[OpenThermDataSource.BOILER].get( + gw_vars.DATA_SLAVE_MEMBERID + ) dev_reg.async_update_device( boiler_device.id, - manufacturer=status[OpenThermDataSource.BOILER].get( - gw_vars.DATA_SLAVE_MEMBERID - ), + manufacturer=str(boiler_manufacturer) + if boiler_manufacturer is not None + else None, model_id=status[OpenThermDataSource.BOILER].get( gw_vars.DATA_SLAVE_PRODUCT_TYPE ), @@ -175,11 +178,14 @@ class OpenThermGatewayHub: ), ) + thermostat_manufacturer = status[OpenThermDataSource.THERMOSTAT].get( + gw_vars.DATA_MASTER_MEMBERID + ) dev_reg.async_update_device( thermostat_device.id, - manufacturer=status[OpenThermDataSource.THERMOSTAT].get( - gw_vars.DATA_MASTER_MEMBERID - ), + manufacturer=str(thermostat_manufacturer) + if thermostat_manufacturer is not None + else None, model_id=status[OpenThermDataSource.THERMOSTAT].get( gw_vars.DATA_MASTER_PRODUCT_TYPE ), diff --git a/homeassistant/components/opower/manifest.json b/homeassistant/components/opower/manifest.json index f8c8490b5086..2d1174bccba0 100644 --- a/homeassistant/components/opower/manifest.json +++ b/homeassistant/components/opower/manifest.json @@ -9,5 +9,5 @@ "iot_class": "cloud_polling", "loggers": ["opower"], "quality_scale": "platinum", - "requirements": ["opower==0.18.2"] + "requirements": ["opower==0.18.4"] } diff --git a/homeassistant/components/philips_js/config_flow.py b/homeassistant/components/philips_js/config_flow.py index 4b1147686eed..597341810f77 100644 --- a/homeassistant/components/philips_js/config_flow.py +++ b/homeassistant/components/philips_js/config_flow.py @@ -45,8 +45,8 @@ USER_SCHEMA = vol.Schema( ): str, vol.Required( CONF_API_VERSION, - default=1, - ): vol.In([1, 5, 6]), + default="1", + ): vol.In(["1", "5", "6"]), } ) @@ -223,7 +223,7 @@ class PhilipsJSConfigFlow(ConfigFlow, domain=DOMAIN): self._current = user_input try: await self._async_attempt_prepare( - user_input[CONF_HOST], user_input[CONF_API_VERSION], False + user_input[CONF_HOST], int(user_input[CONF_API_VERSION]), False ) except GeneralFailure as exc: LOGGER.error(exc) diff --git a/homeassistant/components/plugwise/climate.py b/homeassistant/components/plugwise/climate.py index 6fd2e14d0265..52a7553505f6 100644 --- a/homeassistant/components/plugwise/climate.py +++ b/homeassistant/components/plugwise/climate.py @@ -155,9 +155,9 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity, RestoreEntity): ) @property - def current_temperature(self) -> float: + def current_temperature(self) -> float | None: """Return the current temperature.""" - return self.device["sensors"]["temperature"] + return self.device["sensors"].get("temperature") @property def target_temperature(self) -> float: diff --git a/homeassistant/components/plugwise/manifest.json b/homeassistant/components/plugwise/manifest.json index b17edb50835e..775fa049de6a 100644 --- a/homeassistant/components/plugwise/manifest.json +++ b/homeassistant/components/plugwise/manifest.json @@ -8,6 +8,6 @@ "iot_class": "local_polling", "loggers": ["plugwise"], "quality_scale": "platinum", - "requirements": ["plugwise==1.11.3"], + "requirements": ["plugwise==1.11.4"], "zeroconf": ["_plugwise._tcp.local."] } diff --git a/homeassistant/components/renault/manifest.json b/homeassistant/components/renault/manifest.json index a11c1ad36d26..b82b37f1f41b 100644 --- a/homeassistant/components/renault/manifest.json +++ b/homeassistant/components/renault/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "loggers": ["renault_api"], "quality_scale": "silver", - "requirements": ["renault-api==0.5.11"] + "requirements": ["renault-api==0.5.12"] } diff --git a/homeassistant/components/reolink/host.py b/homeassistant/components/reolink/host.py index 42f6b21c4966..ab80e1396a2d 100644 --- a/homeassistant/components/reolink/host.py +++ b/homeassistant/components/reolink/host.py @@ -369,7 +369,11 @@ class ReolinkHost: ) # start long polling if ONVIF push failed immediately - if not self._onvif_push_supported and not self._api.baichuan.privacy_mode(): + if ( + self._onvif_long_poll_supported + and not self._onvif_push_supported + and not self._api.baichuan.privacy_mode() + ): _LOGGER.debug( "Camera model %s does not support ONVIF push," " using ONVIF long polling instead", @@ -378,14 +382,8 @@ class ReolinkHost: try: await self._async_start_long_polling(initial=True) except NotSupportedError: - _LOGGER.debug( - "Camera model %s does not support ONVIF long" - " polling, using fast polling instead", - self._api.model, - ) self._onvif_long_poll_supported = False await self._api.unsubscribe() - await self._async_poll_all_motion() else: self._cancel_long_poll_check = async_call_later( self._hass, @@ -393,6 +391,13 @@ class ReolinkHost: self._async_check_onvif_long_poll, ) + if not self._onvif_long_poll_supported: + _LOGGER.debug( + "Camera model %s does not support ONVIF push and long polling, using fast polling instead", + self._api.model, + ) + await self._async_poll_all_motion() + self._cancel_tcp_push_check = None async def _async_check_onvif(self, *_: Any) -> None: @@ -822,7 +827,7 @@ class ReolinkHost: return try: - if self._api.session_active: + if self._api.session_active and not self._api.baichuan.privacy_mode(): await self._api.get_motion_state_all_ch() except ReolinkError as err: if not self._fast_poll_error: @@ -834,7 +839,7 @@ class ReolinkHost: ) self._fast_poll_error = True else: - if self._api.session_active: + if self._api.session_active and not self._api.baichuan.privacy_mode(): self._fast_poll_error = False finally: # schedule next poll diff --git a/homeassistant/components/shelly/__init__.py b/homeassistant/components/shelly/__init__.py index 02ebbdc530f7..c46e89fc55bd 100644 --- a/homeassistant/components/shelly/__init__.py +++ b/homeassistant/components/shelly/__init__.py @@ -1,5 +1,6 @@ """The Shelly integration.""" +import asyncio from functools import partial from typing import Final @@ -73,6 +74,7 @@ from .utils import ( get_http_port, get_rpc_scripts_event_types, get_ws_context, + is_rpc_ble_scanner_supported, remove_empty_sub_devices, remove_stale_blu_trv_devices, ) @@ -114,6 +116,9 @@ COAP_SCHEMA: Final = vol.Schema( ) CONFIG_SCHEMA: Final = vol.Schema({DOMAIN: COAP_SCHEMA}, extra=vol.ALLOW_EXTRA) +# Max time to wait at startup for a BLE proxy to register its scanner. +STARTUP_SCANNER_WAIT: Final = 3.0 + async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Shelly component.""" @@ -365,6 +370,21 @@ async def _async_setup_rpc_entry(hass: HomeAssistant, entry: ShellyConfigEntry) runtime_data.rpc = ShellyRpcCoordinator(hass, entry, device) runtime_data.rpc.async_setup() + + if ( + is_rpc_ble_scanner_supported(entry) + and entry.options.get(CONF_BLE_SCANNER_MODE, BLEScannerMode.DISABLED) + != BLEScannerMode.DISABLED + ): + # Wait for the proxy to register its scanner before finishing setup. + try: + async with asyncio.timeout(STARTUP_SCANNER_WAIT): + await runtime_data.rpc.ble_scanner_setup_done.wait() + except TimeoutError: + LOGGER.debug( + "%s: Timed out waiting for BLE scanner to register", entry.title + ) + runtime_data.rpc_poll = ShellyRpcPollingCoordinator(hass, entry, device) await hass.config_entries.async_forward_entry_setups( entry, runtime_data.platforms diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 8b4578f0e1fe..3788955fea0a 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -521,6 +521,8 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]): super().__init__(hass, entry, device, update_interval) self.connected = False + # Set once BLE scanner setup has been attempted after connecting. + self.ble_scanner_setup_done = asyncio.Event() self._disconnected_callbacks: list[CALLBACK_TYPE] = [] self._connection_lock = asyncio.Lock() self._event_listeners: list[Callable[[dict[str, Any]], None]] = [] @@ -759,27 +761,30 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]): async def _async_connect_ble_scanner(self) -> None: """Connect BLE scanner.""" - ble_scanner_mode = self.config_entry.options.get( - CONF_BLE_SCANNER_MODE, BLEScannerMode.DISABLED - ) - if ble_scanner_mode == BLEScannerMode.DISABLED and self.connected: - await async_stop_scanner(self.device) - async_remove_scanner(self.hass, self.bluetooth_source) - return - if await async_ensure_ble_enabled(self.device): - # BLE enable required a reboot, don't bother connecting - # the scanner since it will be disconnected anyway - LOGGER.debug( - "Device %s BLE enable required a reboot, skipping scanner connect", - self.name, + try: + ble_scanner_mode = self.config_entry.options.get( + CONF_BLE_SCANNER_MODE, BLEScannerMode.DISABLED ) - return - assert self.device_id is not None - self._disconnected_callbacks.append( - await async_connect_scanner( - self.hass, self, ble_scanner_mode, self.device_id + if ble_scanner_mode == BLEScannerMode.DISABLED and self.connected: + await async_stop_scanner(self.device) + async_remove_scanner(self.hass, self.bluetooth_source) + return + if await async_ensure_ble_enabled(self.device): + # BLE enable required a reboot, don't bother connecting + # the scanner since it will be disconnected anyway + LOGGER.debug( + "Device %s BLE enable required a reboot, skipping scanner connect", + self.name, + ) + return + assert self.device_id is not None + self._disconnected_callbacks.append( + await async_connect_scanner( + self.hass, self, ble_scanner_mode, self.device_id + ) ) - ) + finally: + self.ble_scanner_setup_done.set() @callback def _async_handle_rpc_device_online(self) -> None: diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index 7ac27001cc8c..ec8a93a2a5d8 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -663,9 +663,9 @@ def is_view_for_platform(config: dict[str, Any], key: str, platform: str) -> boo def get_virtual_component_unit(config: dict[str, Any]) -> str | None: """Return the unit of a virtual component. - If the unit is not set, the device sends an empty string + If the unit is not set, the device sends an empty string or the key may be absent. """ - unit = config["meta"]["ui"]["unit"] + unit = config["meta"]["ui"].get("unit") return DEVICE_UNIT_MAP.get(unit, unit) if unit else None diff --git a/homeassistant/components/squeezebox/media_player.py b/homeassistant/components/squeezebox/media_player.py index 0e7484b96a43..969a07f1fba3 100644 --- a/homeassistant/components/squeezebox/media_player.py +++ b/homeassistant/components/squeezebox/media_player.py @@ -74,7 +74,7 @@ ATTR_QUERY_RESULT = "query_result" _LOGGER = logging.getLogger(__name__) -PARALLEL_UPDATES = 1 +PARALLEL_UPDATES = 0 ATTR_OTHER_PLAYER = "other_player" diff --git a/homeassistant/components/squeezebox/switch.py b/homeassistant/components/squeezebox/switch.py index 315228d3ffdc..33bf9dc47bc9 100644 --- a/homeassistant/components/squeezebox/switch.py +++ b/homeassistant/components/squeezebox/switch.py @@ -22,7 +22,7 @@ from .entity import SqueezeboxEntity _LOGGER = logging.getLogger(__name__) -PARALLEL_UPDATES = 1 +PARALLEL_UPDATES = 0 async def async_setup_entry( diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 7b957b3c8163..28bb17ffbb08 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -613,6 +613,10 @@ class ResultStream: async for chunk in converted_audio: yield chunk + def delete(self) -> None: + """Remove the result stream from the manager.""" + self._manager.async_delete_result_stream(self.token) + def _hash_options(options: dict) -> str: """Hashes an options dictionary.""" @@ -809,6 +813,11 @@ class SpeechManager: stream.last_used = monotonic() return stream + @callback + def async_delete_result_stream(self, token: str) -> None: + """Delete a result stream given a token.""" + self.token_to_stream.pop(token, None) + @callback def async_create_result_stream( self, diff --git a/homeassistant/components/v2c/diagnostics.py b/homeassistant/components/v2c/diagnostics.py index 38250ca2e8fb..4721580eed67 100644 --- a/homeassistant/components/v2c/diagnostics.py +++ b/homeassistant/components/v2c/diagnostics.py @@ -1,6 +1,6 @@ """Diagnostics support for V2C.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, cast from homeassistant.components.diagnostics import async_redact_data from homeassistant.const import CONF_HOST @@ -21,11 +21,11 @@ async def async_get_config_entry_diagnostics( assert coordinator.evse coordinator_data = coordinator.evse.data - evse_raw_data = coordinator.evse.raw_data + evse_raw_data = cast(dict[str, Any], coordinator.evse.raw_data) return { "config_entry": async_redact_data(entry.as_dict(), TO_REDACT), "data": str(coordinator_data), - "raw_data": evse_raw_data["content"].decode("utf-8"), # type: ignore[attr-defined] + "raw_data": evse_raw_data["content"].decode("utf-8"), "host_status": evse_raw_data["status_code"], } diff --git a/homeassistant/components/v2c/manifest.json b/homeassistant/components/v2c/manifest.json index 2cabf8952e19..1ebedd49eb19 100644 --- a/homeassistant/components/v2c/manifest.json +++ b/homeassistant/components/v2c/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/v2c", "integration_type": "device", "iot_class": "local_polling", - "requirements": ["pytrydan==1.0.0"] + "requirements": ["pytrydan==1.0.1"] } diff --git a/homeassistant/components/wyoming/assist_satellite.py b/homeassistant/components/wyoming/assist_satellite.py index c2edf1ed8f63..f86f01858521 100644 --- a/homeassistant/components/wyoming/assist_satellite.py +++ b/homeassistant/components/wyoming/assist_satellite.py @@ -348,6 +348,8 @@ class WyomingAssistSatellite(WyomingSatelliteEntity, AssistSatelliteEntity): # Use ffmpeg to convert to raw PCM audio with the appropriate format proc = await asyncio.create_subprocess_exec( self._ffmpeg_manager.binary, + "-protocol_whitelist", + "http,https,file,tcp,tls", "-i", announcement.media_id, "-f", diff --git a/homeassistant/components/yardian/manifest.json b/homeassistant/components/yardian/manifest.json index b76f4122c788..5a4f8525587b 100644 --- a/homeassistant/components/yardian/manifest.json +++ b/homeassistant/components/yardian/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/yardian", "integration_type": "device", "iot_class": "local_polling", - "requirements": ["pyyardian==1.3.3"] + "requirements": ["pyyardian==1.4.0"] } diff --git a/homeassistant/components/zinvolt/binary_sensor.py b/homeassistant/components/zinvolt/binary_sensor.py index 52e44ec7c77e..9e4028198f41 100644 --- a/homeassistant/components/zinvolt/binary_sensor.py +++ b/homeassistant/components/zinvolt/binary_sensor.py @@ -30,7 +30,7 @@ POINT_ENTITIES = { class ZinvoltBatteryStateDescription(BinarySensorEntityDescription): """Binary sensor description for Zinvolt battery state.""" - is_on_fn: Callable[[ZinvoltData], bool] + is_on_fn: Callable[[ZinvoltData], bool | None] SENSORS: tuple[ZinvoltBatteryStateDescription, ...] = ( @@ -84,7 +84,7 @@ class ZinvoltBatteryStateBinarySensor(ZinvoltEntity, BinarySensorEntity): ) @property - def is_on(self) -> bool: + def is_on(self) -> bool | None: """Return the state of the binary sensor.""" return self.entity_description.is_on_fn(self.coordinator.data) diff --git a/homeassistant/components/zinvolt/entity.py b/homeassistant/components/zinvolt/entity.py index 932e18fb0957..0e90de132acb 100644 --- a/homeassistant/components/zinvolt/entity.py +++ b/homeassistant/components/zinvolt/entity.py @@ -1,6 +1,6 @@ """Base entity for Zinvolt integration.""" -from zinvolt.models import Unit +from zinvolt.models import OnlineStatus, Unit from homeassistant.const import ATTR_VIA_DEVICE from homeassistant.helpers.device_registry import DeviceInfo @@ -25,6 +25,15 @@ class ZinvoltEntity(CoordinatorEntity[ZinvoltDeviceCoordinator]): serial_number=coordinator.data.battery.serial_number, ) + @property + def available(self) -> bool: + """Return if the entity is available.""" + return ( + super().available + and self.coordinator.data.battery.current_power.online_status + is OnlineStatus.ONLINE + ) + class ZinvoltUnitEntity(ZinvoltEntity): """Base entity for Zinvolt units.""" diff --git a/homeassistant/components/zinvolt/manifest.json b/homeassistant/components/zinvolt/manifest.json index a73f18e6c80b..0157193ba02b 100644 --- a/homeassistant/components/zinvolt/manifest.json +++ b/homeassistant/components/zinvolt/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "loggers": ["zinvolt"], "quality_scale": "bronze", - "requirements": ["zinvolt==0.4.3"] + "requirements": ["zinvolt==1.0.0"] } diff --git a/homeassistant/components/zinvolt/number.py b/homeassistant/components/zinvolt/number.py index 5fcc7e01199b..83f799fc4555 100644 --- a/homeassistant/components/zinvolt/number.py +++ b/homeassistant/components/zinvolt/number.py @@ -34,7 +34,11 @@ NUMBERS: tuple[ZinvoltBatteryStateDescription, ...] = ( entity_category=EntityCategory.CONFIG, device_class=NumberDeviceClass.POWER, native_unit_of_measurement=UnitOfPower.WATT, - value_fn=lambda state: state.battery.global_settings.max_output, + value_fn=lambda state: ( + 2000 + if state.battery.global_settings.max_output_unlocked + else state.battery.global_settings.max_output + ), set_value_fn=lambda client, battery_id, value: client.set_max_output( battery_id, value ), diff --git a/homeassistant/components/zinvolt/sensor.py b/homeassistant/components/zinvolt/sensor.py index 58633cf78dc5..626204417e4f 100644 --- a/homeassistant/components/zinvolt/sensor.py +++ b/homeassistant/components/zinvolt/sensor.py @@ -21,7 +21,7 @@ from .entity import ZinvoltEntity class ZinvoltBatteryStateDescription(SensorEntityDescription): """Sensor description for Zinvolt battery state.""" - value_fn: Callable[[ZinvoltData], float] + value_fn: Callable[[ZinvoltData], float | None] SENSORS: tuple[ZinvoltBatteryStateDescription, ...] = ( @@ -37,7 +37,13 @@ SENSORS: tuple[ZinvoltBatteryStateDescription, ...] = ( device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfPower.WATT, - value_fn=lambda state: 0 - state.battery.current_power.power_socket_output, + value_fn=( + lambda state: ( + None + if state.battery.current_power.power_socket_output is None + else 0 - state.battery.current_power.power_socket_output + ) + ), ), ) @@ -74,6 +80,6 @@ class ZinvoltBatteryStateSensor(ZinvoltEntity, SensorEntity): ) @property - def native_value(self) -> float: + def native_value(self) -> float | None: """Return the state of the sensor.""" return self.entity_description.value_fn(self.coordinator.data) diff --git a/homeassistant/const.py b/homeassistant/const.py index 5128a515a144..1d0766aa095d 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2026 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "1" +PATCH_VERSION: Final = "2" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 14, 2) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index d6d831709ef9..cf4ade953aa6 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -39,7 +39,7 @@ habluetooth==6.8.1 hass-nabucasa==2.2.0 hassil==3.5.0 home-assistant-bluetooth==2.0.0 -home-assistant-frontend==20260527.4 +home-assistant-frontend==20260527.5 home-assistant-intents==2026.6.1 httpx==0.28.1 ifaddr==0.2.0 diff --git a/pylint/plugins/pylint_home_assistant/generated/mdi_icons.py b/pylint/plugins/pylint_home_assistant/generated/mdi_icons.py index 6887f2debfac..7fd5b190c602 100644 --- a/pylint/plugins/pylint_home_assistant/generated/mdi_icons.py +++ b/pylint/plugins/pylint_home_assistant/generated/mdi_icons.py @@ -5,7 +5,7 @@ To update, run python3 -m script.hassfest from typing import Final -FRONTEND_VERSION: Final[str] = "20260527.4" +FRONTEND_VERSION: Final[str] = "20260527.5" MDI_ICONS: Final[set[str]] = { "ab-testing", diff --git a/pyproject.toml b/pyproject.toml index 43486fd60dc8..41e9ebcf76d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2026.6.1" +version = "2026.6.2" license = "Apache-2.0" license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"] description = "Open-source home automation platform running on Python 3." diff --git a/requirements_all.txt b/requirements_all.txt index 77f0151371bf..95b6e379db33 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1266,7 +1266,7 @@ hole==0.9.0 holidays==0.98 # homeassistant.components.frontend -home-assistant-frontend==20260527.4 +home-assistant-frontend==20260527.5 # homeassistant.components.conversation home-assistant-intents==2026.6.1 @@ -1577,7 +1577,7 @@ millheater==0.14.1 minio==7.1.12 # homeassistant.components.mitsubishi_comfort -mitsubishi-comfort==0.3.0 +mitsubishi-comfort==0.3.1 # homeassistant.components.moat moat-ble==0.1.1 @@ -1776,7 +1776,7 @@ openwrt-luci-rpc==1.1.17 openwrt-ubus-rpc==0.0.2 # homeassistant.components.opower -opower==0.18.2 +opower==0.18.4 # homeassistant.components.oralb oralb-ble==1.1.0 @@ -1843,7 +1843,7 @@ plexauth==0.0.6 plexwebsocket==0.0.14 # homeassistant.components.plugwise -plugwise==1.11.3 +plugwise==1.11.4 # homeassistant.components.serial_pm pmsensor==0.4 @@ -2081,7 +2081,7 @@ pycsspeechtts==1.0.8 pycync==0.5.0 # homeassistant.components.daikin -pydaikin==2.17.2 +pydaikin==2.18.1 # homeassistant.components.danfoss_air pydanfossair==0.1.0 @@ -2773,7 +2773,7 @@ pytradfri[async]==9.0.1 pytrafikverket==1.1.1 # homeassistant.components.v2c -pytrydan==1.0.0 +pytrydan==1.0.1 # homeassistant.components.uptimerobot pyuptimerobot==25.0.0 @@ -2824,7 +2824,7 @@ pyws66i==1.1 pyxeoma==1.4.2 # homeassistant.components.yardian -pyyardian==1.3.3 +pyyardian==1.4.0 # homeassistant.components.qrcode pyzbar==0.1.9 @@ -2872,7 +2872,7 @@ refoss-ha==1.2.5 regenmaschine==2024.03.0 # homeassistant.components.renault -renault-api==0.5.11 +renault-api==0.5.12 # homeassistant.components.renson renson-endura-delta==1.7.2 @@ -3451,7 +3451,7 @@ zhong-hong-hvac==1.0.13 ziggo-mediabox-xl==1.1.0 # homeassistant.components.zinvolt -zinvolt==0.4.3 +zinvolt==1.0.0 # homeassistant.components.zoneminder zm-py==0.5.4 diff --git a/tests/components/alexa_devices/test_coordinator.py b/tests/components/alexa_devices/test_coordinator.py index 427d38e3a4b2..cc81b994c8bf 100644 --- a/tests/components/alexa_devices/test_coordinator.py +++ b/tests/components/alexa_devices/test_coordinator.py @@ -91,6 +91,48 @@ async def test_coordinator_load_previous_devices_from_registry( assert coordinator.previous_devices == {TEST_DEVICE_1_SN} +@pytest.mark.parametrize( + ("side_effect", "expected_state"), + [ + pytest.param( + CannotConnect, + ConfigEntryState.SETUP_RETRY, + id="cannot_connect", + ), + pytest.param( + CannotRetrieveData, + ConfigEntryState.SETUP_RETRY, + id="cannot_retrieve_data", + ), + pytest.param( + CannotAuthenticate, + ConfigEntryState.SETUP_ERROR, + id="cannot_authenticate", + ), + pytest.param( + ValueError, + ConfigEntryState.SETUP_RETRY, + id="value_error", + ), + ], +) +async def test_async_update_data_errors( + hass: HomeAssistant, + mock_amazon_devices_client: AsyncMock, + mock_config_entry: MockConfigEntry, + side_effect: type[Exception], + expected_state: ConfigEntryState, +) -> None: + """Test _async_update_data error handling.""" + mock_amazon_devices_client.get_devices_data.side_effect = side_effect + + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert mock_config_entry.state is expected_state + + @pytest.mark.parametrize( ("side_effect", "expected_state"), [ diff --git a/tests/components/alexa_devices/test_utils.py b/tests/components/alexa_devices/test_utils.py index 6a31e55fb1d1..81cf3bfed96b 100644 --- a/tests/components/alexa_devices/test_utils.py +++ b/tests/components/alexa_devices/test_utils.py @@ -3,7 +3,11 @@ from unittest.mock import AsyncMock from aioamazondevices.const.devices import SPEAKER_GROUP_FAMILY -from aioamazondevices.exceptions import CannotConnect, CannotRetrieveData +from aioamazondevices.exceptions import ( + CannotAuthenticate, + CannotConnect, + CannotRetrieveData, +) import pytest from homeassistant.components.alexa_devices.const import DOMAIN @@ -25,15 +29,37 @@ ENTITY_ID = "switch.echo_test_do_not_disturb" @pytest.mark.parametrize( ("side_effect", "key", "error"), [ - (CannotConnect, "cannot_connect_with_error", "CannotConnect()"), - (CannotRetrieveData, "cannot_retrieve_data_with_error", "CannotRetrieveData()"), + pytest.param( + CannotAuthenticate, + "invalid_auth", + "CannotAuthenticate()", + id="cannot_authenticate", + ), + pytest.param( + CannotConnect, + "cannot_connect_with_error", + "CannotConnect()", + id="cannot_connect", + ), + pytest.param( + CannotRetrieveData, + "cannot_retrieve_data_with_error", + "CannotRetrieveData()", + id="cannot_retrieve_data", + ), + pytest.param( + ValueError, + "cannot_retrieve_data_with_error", + "ValueError()", + id="value_error", + ), ], ) async def test_alexa_api_call_exceptions( hass: HomeAssistant, mock_amazon_devices_client: AsyncMock, mock_config_entry: MockConfigEntry, - side_effect: Exception, + side_effect: type[Exception], key: str, error: str, ) -> None: diff --git a/tests/components/assist_pipeline/test_pipeline.py b/tests/components/assist_pipeline/test_pipeline.py index 922b51e3f636..f4c2ae068bcf 100644 --- a/tests/components/assist_pipeline/test_pipeline.py +++ b/tests/components/assist_pipeline/test_pipeline.py @@ -2274,3 +2274,86 @@ async def test_stt_vad_enabled_based_on_audio_processing( # VAD should NOT be created when requires_external_vad is False mock_vad.assert_not_called() + + +async def test_invalid_pipeline_does_not_create_tts_stream( + hass: HomeAssistant, + mock_wake_word_provider_entity: MockWakeWordEntity, + init_components, +) -> None: + """Test that an invalid pipeline won't create a TTS ResultStream.""" + pipeline = async_get_pipeline(hass, None) + await async_update_pipeline(hass, pipeline, stt_engine="does-not-exist") + + async def audio_data() -> AsyncGenerator[bytes]: + yield make_10ms_chunk(b"not used") + + with patch.object( + mock_wake_word_provider_entity, + "async_process_audio_stream", + side_effect=assist_pipeline.error.WakeWordTimeoutError( + code="timeout", message="timeout" + ), + ): + await assist_pipeline.async_pipeline_from_audio_stream( + hass, + context=Context(), + event_callback=lambda event: None, + stt_metadata=stt.SpeechMetadata( + language="", + format=stt.AudioFormats.WAV, + codec=stt.AudioCodecs.PCM, + bit_rate=stt.AudioBitRates.BITRATE_16, + sample_rate=stt.AudioSampleRates.SAMPLERATE_16000, + channel=stt.AudioChannels.CHANNEL_MONO, + ), + stt_stream=audio_data(), + start_stage=assist_pipeline.PipelineStage.STT, + end_stage=assist_pipeline.PipelineStage.TTS, + audio_settings=assist_pipeline.AudioSettings(is_vad_enabled=False), + ) + + assert len(hass.data[tts.DATA_TTS_MANAGER].token_to_stream) == 0 + + +async def test_pipeline_error_before_tts_does_not_leak_result_stream( + hass: HomeAssistant, + mock_wake_word_provider_entity: MockWakeWordEntity, + init_components, +) -> None: + """Test that a pipeline error before TTS will not leak a ResultStream.""" + + async def audio_data() -> AsyncGenerator[bytes]: + yield make_10ms_chunk(b"not used") + + with patch.object( + mock_wake_word_provider_entity, + "async_process_audio_stream", + side_effect=assist_pipeline.error.WakeWordTimeoutError( + code="timeout", message="timeout" + ), + ): + for i in range(10): + with patch("secrets.token_urlsafe", return_value=f"mocked-token-{i}"): + await assist_pipeline.async_pipeline_from_audio_stream( + hass, + context=Context(), + event_callback=lambda event: None, + stt_metadata=stt.SpeechMetadata( + language="", + format=stt.AudioFormats.WAV, + codec=stt.AudioCodecs.PCM, + bit_rate=stt.AudioBitRates.BITRATE_16, + sample_rate=stt.AudioSampleRates.SAMPLERATE_16000, + channel=stt.AudioChannels.CHANNEL_MONO, + ), + stt_stream=audio_data(), + start_stage=assist_pipeline.PipelineStage.WAKE_WORD, + end_stage=assist_pipeline.PipelineStage.TTS, + wake_word_settings=assist_pipeline.WakeWordSettings( + audio_seconds_to_buffer=1.5 + ), + audio_settings=assist_pipeline.AudioSettings(is_vad_enabled=False), + ) + + assert len(hass.data[tts.DATA_TTS_MANAGER].token_to_stream) == 0 diff --git a/tests/components/esphome/test_manager.py b/tests/components/esphome/test_manager.py index e6913e5e4228..15bda2017546 100644 --- a/tests/components/esphome/test_manager.py +++ b/tests/components/esphome/test_manager.py @@ -9,7 +9,9 @@ from unittest.mock import AsyncMock, Mock, call, patch from aioesphomeapi import ( APIClient, APIConnectionError, + APIVersion, AreaInfo, + BluetoothProxyFeature, DeviceInfo, EncryptionPlaintextAPIError, ExecuteServiceResponse, @@ -3274,3 +3276,124 @@ async def test_service_registration_response_types( hass.services.supports_response(DOMAIN, "test_status_service") == SupportsResponse.NONE ) + + +def _create_cached_bluetooth_proxy_entry( + hass: HomeAssistant, + hass_storage: dict[str, Any], + bluetooth_proxy_feature_flags: BluetoothProxyFeature, +) -> tuple[MockConfigEntry, DeviceInfo]: + """Create an entry with cached device info so setup knows the proxy state.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="11:22:33:44:55:aa", + data={ + CONF_HOST: "test.local", + CONF_PORT: 6053, + CONF_PASSWORD: "", + CONF_BLUETOOTH_MAC_ADDRESS: "AA:BB:CC:DD:EE:FC", + }, + ) + entry.add_to_hass(hass) + device_info = DeviceInfo( + name="test", + mac_address="11:22:33:44:55:AA", + bluetooth_mac_address="AA:BB:CC:DD:EE:FC", + bluetooth_proxy_feature_flags=bluetooth_proxy_feature_flags, + ) + storage_key = f"{DOMAIN}.{entry.entry_id}" + hass_storage[storage_key] = { + "version": 1, + "minor_version": 1, + "key": storage_key, + "data": { + "device_info": device_info.to_dict(), + "api_version": APIVersion(1, 9).to_dict(), + }, + } + return entry, device_info + + +@pytest.mark.usefixtures("mock_zeroconf") +async def test_bluetooth_proxy_waits_for_scanner_at_startup( + hass: HomeAssistant, + mock_client: APIClient, + hass_storage: dict[str, Any], +) -> None: + """Test setup waits for a cached bluetooth proxy to register its scanner.""" + entry, device_info = _create_cached_bluetooth_proxy_entry( + hass, hass_storage, BluetoothProxyFeature.PASSIVE_SCAN + ) + connect_event = asyncio.Event() + reached_connect = asyncio.Event() + + async def _block_until_released() -> tuple[DeviceInfo, list[Any], list[Any]]: + reached_connect.set() + await connect_event.wait() + return (device_info, [], []) + + mock_client.device_info_and_list_entities = _block_until_released + + setup_task = hass.async_create_task(hass.config_entries.async_setup(entry.entry_id)) + async with asyncio.timeout(2): + await reached_connect.wait() + + # Setup must still be waiting for the scanner to be registered. + assert not setup_task.done() + + connect_event.set() + async with asyncio.timeout(2): + assert await setup_task is True + assert entry.runtime_data.first_connect_done.is_set() + + +@pytest.mark.usefixtures("mock_zeroconf") +async def test_bluetooth_proxy_startup_wait_times_out( + hass: HomeAssistant, + mock_client: APIClient, + hass_storage: dict[str, Any], +) -> None: + """Test setup finishes if a cached bluetooth proxy never connects.""" + entry, device_info = _create_cached_bluetooth_proxy_entry( + hass, hass_storage, BluetoothProxyFeature.PASSIVE_SCAN + ) + connect_event = asyncio.Event() + + async def _never_returns() -> tuple[DeviceInfo, list[Any], list[Any]]: + await connect_event.wait() + return (device_info, [], []) + + mock_client.device_info_and_list_entities = _never_returns + + with patch("homeassistant.components.esphome.manager.STARTUP_SCANNER_WAIT", 0.05): + async with asyncio.timeout(2): + assert await hass.config_entries.async_setup(entry.entry_id) is True + + connect_event.set() + await hass.async_block_till_done() + + +@pytest.mark.usefixtures("mock_zeroconf") +async def test_non_bluetooth_device_does_not_wait_at_startup( + hass: HomeAssistant, + mock_client: APIClient, + hass_storage: dict[str, Any], +) -> None: + """Test setup does not wait for a device that is not a bluetooth proxy.""" + entry, device_info = _create_cached_bluetooth_proxy_entry( + hass, hass_storage, BluetoothProxyFeature(0) + ) + connect_event = asyncio.Event() + + async def _never_returns() -> tuple[DeviceInfo, list[Any], list[Any]]: + await connect_event.wait() + return (device_info, [], []) + + mock_client.device_info_and_list_entities = _never_returns + + # The connection is blocked, but without proxy flags setup must not wait. + async with asyncio.timeout(2): + assert await hass.config_entries.async_setup(entry.entry_id) is True + + connect_event.set() + await hass.async_block_till_done() diff --git a/tests/components/gardena_bluetooth/conftest.py b/tests/components/gardena_bluetooth/conftest.py index 3a4450afe535..89bca195b63f 100644 --- a/tests/components/gardena_bluetooth/conftest.py +++ b/tests/components/gardena_bluetooth/conftest.py @@ -11,11 +11,10 @@ from gardena_bluetooth.client import Client from gardena_bluetooth.const import DeviceInformation from gardena_bluetooth.exceptions import CharacteristicNotFound from gardena_bluetooth.parse import Characteristic, Service -from gardena_bluetooth.scan import ( - async_get_manufacturer_data as _async_get_manufacturer_data, -) import pytest +from homeassistant.components import bluetooth +from homeassistant.components.gardena_bluetooth import async_get_product_type from homeassistant.components.gardena_bluetooth.const import DOMAIN from homeassistant.components.gardena_bluetooth.coordinator import SCAN_INTERVAL from homeassistant.core import HomeAssistant @@ -178,23 +177,49 @@ def enable_all_entities(entity_registry_enabled_by_default: None) -> None: @pytest.fixture -def manufacturer_request_event() -> Generator[asyncio.Event]: - """Track manufacturer data requests with an event.""" +def get_product_type_event() -> Generator[asyncio.Event]: + """Track product type data requests with an event.""" event = asyncio.Event() async def _get(*args, **kwargs): event.set() - return await _async_get_manufacturer_data(*args, **kwargs) + return await async_get_product_type(*args, **kwargs) with ( patch( - "homeassistant.components.gardena_bluetooth.async_get_manufacturer_data", + "homeassistant.components.gardena_bluetooth.async_get_product_type", wraps=_get, ), patch( - "homeassistant.components.gardena_bluetooth.config_flow.async_get_manufacturer_data", + "homeassistant.components.gardena_bluetooth.config_flow.async_get_product_type", wraps=_get, ), ): yield event + + +@pytest.fixture +def constant_advertisements() -> Generator[None]: + """Ensure async_process_advertisements only return a constant list.""" + + async def _advertisements( + hass: HomeAssistant, + callback: bluetooth.models.ProcessAdvertisementCallback, + match_dict: bluetooth.match.BluetoothCallbackMatcher, + mode: bluetooth.BluetoothScanningMode, + timeout: int, + ) -> bluetooth.BluetoothServiceInfoBleak: + + last = None + for advertisement in bluetooth.async_discovered_service_info(hass): + callback(advertisement) + last = advertisement + if not last: + raise TimeoutError + return last + + with ( + patch.object(bluetooth, "async_process_advertisements", new=_advertisements), + ): + yield diff --git a/tests/components/gardena_bluetooth/test_config_flow.py b/tests/components/gardena_bluetooth/test_config_flow.py index 9679fe06a8f1..c838bdacc7e5 100644 --- a/tests/components/gardena_bluetooth/test_config_flow.py +++ b/tests/components/gardena_bluetooth/test_config_flow.py @@ -1,7 +1,5 @@ """Test the Gardena Bluetooth config flow.""" -import asyncio -from collections.abc import Awaitable, Callable from unittest.mock import Mock from gardena_bluetooth.exceptions import CharacteristicNotFound @@ -26,13 +24,10 @@ from . import ( from tests.common import MockConfigEntry from tests.components.bluetooth import inject_bluetooth_service_info -pytestmark = pytest.mark.usefixtures("mock_setup_entry") +pytestmark = pytest.mark.usefixtures("mock_setup_entry", "constant_advertisements") -async def test_user_selection( - hass: HomeAssistant, - snapshot: SnapshotAssertion, -) -> None: +async def test_user_selection(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None: """Test we can select a device.""" inject_bluetooth_service_info(hass, WATER_TIMER_SERVICE_INFO) @@ -138,9 +133,6 @@ async def test_no_valid_devices( async def test_timeout_manufacturer_data( hass: HomeAssistant, - snapshot: SnapshotAssertion, - scan_step: Callable[[], Awaitable[None]], - manufacturer_request_event: asyncio.Event, ) -> None: """Test the flow aborts with no_devices_found. @@ -149,24 +141,9 @@ async def test_timeout_manufacturer_data( """ inject_bluetooth_service_info(hass, MISSING_PRODUCT_SERVICE_INFO) - - # The injected advertisement starts a bluetooth discovery flow which also - # calls async_get_manufacturer_data. Drain it first so it doesn't race - # with the user flow's own request. - await manufacturer_request_event.wait() - await scan_step() - await hass.async_block_till_done(wait_background_tasks=True) - manufacturer_request_event.clear() - - async with asyncio.TaskGroup() as tg: - task = tg.create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - ) - await manufacturer_request_event.wait() - await scan_step() - result = await task + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) assert result.get("type") == "abort" assert result.get("reason") == "no_devices_found" diff --git a/tests/components/gardena_bluetooth/test_init.py b/tests/components/gardena_bluetooth/test_init.py index d9573e220e5a..9b5caf402695 100644 --- a/tests/components/gardena_bluetooth/test_init.py +++ b/tests/components/gardena_bluetooth/test_init.py @@ -72,6 +72,7 @@ from tests.components.bluetooth import inject_bluetooth_service_info ), ], ) +@pytest.mark.usefixtures("constant_advertisements") async def test_setup( hass: HomeAssistant, device_registry: dr.DeviceRegistry, @@ -99,7 +100,7 @@ async def test_setup_delayed_product( device_registry: dr.DeviceRegistry, mock_entry: MockConfigEntry, mock_read_char_raw: dict[str, bytes], - manufacturer_request_event: asyncio.Event, + get_product_type_event: asyncio.Event, snapshot: SnapshotAssertion, ) -> None: """Test setup creates expected devices.""" @@ -107,15 +108,16 @@ async def test_setup_delayed_product( mock_read_char_raw[Battery.battery_level.uuid] = Battery.battery_level.encode(100) mock_entry.add_to_hass(hass) + await hass.async_block_till_done() - manufacturer_request_event.clear() + get_product_type_event.clear() async with asyncio.TaskGroup() as tg: setup_task = tg.create_task( hass.config_entries.async_setup(mock_entry.entry_id) ) - await manufacturer_request_event.wait() + await get_product_type_event.wait() assert mock_entry.state is ConfigEntryState.SETUP_IN_PROGRESS inject_bluetooth_service_info(hass, MISSING_MANUFACTURER_DATA_SERVICE_INFO) inject_bluetooth_service_info(hass, WATER_TIMER_SERVICE_INFO) @@ -123,6 +125,7 @@ async def test_setup_delayed_product( assert await setup_task is True +@pytest.mark.usefixtures("constant_advertisements") async def test_setup_retry( hass: HomeAssistant, mock_entry: MockConfigEntry, mock_client: Mock ) -> None: diff --git a/tests/components/gardena_bluetooth/test_number.py b/tests/components/gardena_bluetooth/test_number.py index dd154af9c4b6..fae6ecf63f1b 100644 --- a/tests/components/gardena_bluetooth/test_number.py +++ b/tests/components/gardena_bluetooth/test_number.py @@ -26,6 +26,8 @@ from . import AQUA_CONTOUR_SERVICE_INFO, WATER_TIMER_SERVICE_INFO, setup_entry from tests.common import MockConfigEntry +pytestmark = pytest.mark.usefixtures("constant_advertisements") + @pytest.mark.parametrize( ("service_info", "uuid", "raw", "entity_id"), diff --git a/tests/components/gardena_bluetooth/test_select.py b/tests/components/gardena_bluetooth/test_select.py index ba2e67d52609..c8ffdd002c80 100644 --- a/tests/components/gardena_bluetooth/test_select.py +++ b/tests/components/gardena_bluetooth/test_select.py @@ -25,6 +25,8 @@ from . import AQUA_CONTOUR_SERVICE_INFO, setup_entry from tests.common import MockConfigEntry, snapshot_platform +pytestmark = pytest.mark.usefixtures("constant_advertisements") + @pytest.fixture def mock_chars(mock_read_char_raw): diff --git a/tests/components/gardena_bluetooth/test_sensor.py b/tests/components/gardena_bluetooth/test_sensor.py index cf2867f9ace1..58bc06170546 100644 --- a/tests/components/gardena_bluetooth/test_sensor.py +++ b/tests/components/gardena_bluetooth/test_sensor.py @@ -27,6 +27,8 @@ from . import AQUA_CONTOUR_SERVICE_INFO, WATER_TIMER_SERVICE_INFO, setup_entry from tests.common import MockConfigEntry, snapshot_platform +pytestmark = pytest.mark.usefixtures("constant_advertisements") + @pytest.mark.parametrize( ("service_info", "uuid", "raw", "entity_id"), diff --git a/tests/components/gardena_bluetooth/test_switch.py b/tests/components/gardena_bluetooth/test_switch.py index 4a29f8f91ae5..e9cdbffaf35c 100644 --- a/tests/components/gardena_bluetooth/test_switch.py +++ b/tests/components/gardena_bluetooth/test_switch.py @@ -20,6 +20,8 @@ from . import setup_entry from tests.common import MockConfigEntry +pytestmark = pytest.mark.usefixtures("constant_advertisements") + @pytest.fixture def mock_switch_chars(mock_read_char_raw): diff --git a/tests/components/gardena_bluetooth/test_text.py b/tests/components/gardena_bluetooth/test_text.py index 1ca52d61f370..da0d49bd5470 100644 --- a/tests/components/gardena_bluetooth/test_text.py +++ b/tests/components/gardena_bluetooth/test_text.py @@ -20,6 +20,8 @@ from . import AQUA_CONTOUR_SERVICE_INFO, setup_entry from tests.common import snapshot_platform +pytestmark = pytest.mark.usefixtures("constant_advertisements") + @pytest.mark.parametrize( ("service_info", "raw"), diff --git a/tests/components/gardena_bluetooth/test_valve.py b/tests/components/gardena_bluetooth/test_valve.py index 411778658f4d..1fb62c8bcba8 100644 --- a/tests/components/gardena_bluetooth/test_valve.py +++ b/tests/components/gardena_bluetooth/test_valve.py @@ -20,6 +20,8 @@ from . import setup_entry from tests.common import MockConfigEntry +pytestmark = pytest.mark.usefixtures("constant_advertisements") + @pytest.fixture def mock_switch_chars(mock_read_char_raw): diff --git a/tests/components/homeassistant_connect_zbt2/test_init.py b/tests/components/homeassistant_connect_zbt2/test_init.py index 3626446a198f..d13147f328b4 100644 --- a/tests/components/homeassistant_connect_zbt2/test_init.py +++ b/tests/components/homeassistant_connect_zbt2/test_init.py @@ -234,6 +234,75 @@ async def test_config_entry_migration_v2_prefers_active_entry( assert active_entry.unique_id == serial_number +async def test_config_entry_migration_v2_removes_duplicates_of_migrated_entry( + hass: HomeAssistant, +) -> None: + """Test v1.2 migration removes duplicates of an already migrated entry. + + A migrated entry (minor version 2) never runs the migration again, so the + remaining minor version 1 duplicates have to remove themselves instead of + relying on the canonical entry's migration to remove them. + """ + serial_number = "E072A1D90104" + data = { + "device": "/dev/serial/by-id/usb-Nabu_Casa_ZBT-2_E072A1D90104-if00", + "firmware": "spinel", + "firmware_version": ( + "SL-OPENTHREAD/2.7.2.0_GitHub-fb0446f53; EFR32; Feb 24 2026 00:58:55" + ), + "manufacturer": "Nabu Casa", + "pid": "831A", + "product": "ZBT-2", + "serial_number": serial_number, + "vid": "303A", + } + + older_entry = MockConfigEntry( + domain=DOMAIN, + unique_id="303A:831A_E072A1D90104_Nabu Casa_ZBT-2 - Nabu Casa ZBT-2", + source="usb", + data=dict(data), + version=1, + minor_version=1, + ) + older_entry.add_to_hass(hass) + + duplicate_entry = MockConfigEntry( + domain=DOMAIN, + unique_id="303A:831A_E072A1D90104_Nabu Casa_ZBT-2", + source="import", + data=dict(data), + version=1, + minor_version=1, + ) + duplicate_entry.add_to_hass(hass) + + migrated_entry = MockConfigEntry( + domain=DOMAIN, + unique_id=serial_number, + source="import", + data=dict(data), + version=1, + minor_version=2, + ) + migrated_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_connect_zbt2.os.path.exists", + return_value=True, + ): + await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + + remaining_entries = hass.config_entries.async_entries(DOMAIN) + assert len(remaining_entries) == 1 + assert remaining_entries[0].entry_id == migrated_entry.entry_id + assert remaining_entries[0].minor_version == 2 + assert remaining_entries[0].unique_id == serial_number + assert hass.config_entries.async_get_entry(older_entry.entry_id) is None + assert hass.config_entries.async_get_entry(duplicate_entry.entry_id) is None + + async def test_setup_fails_on_missing_usb_port(hass: HomeAssistant) -> None: """Test setup failing when the USB port is missing.""" diff --git a/tests/components/homeassistant_sky_connect/test_init.py b/tests/components/homeassistant_sky_connect/test_init.py index 5b5e105bb9b6..d4ef27b317f3 100644 --- a/tests/components/homeassistant_sky_connect/test_init.py +++ b/tests/components/homeassistant_sky_connect/test_init.py @@ -306,6 +306,79 @@ async def test_config_entry_migration_v5_prefers_active_entry( assert active_entry.unique_id == serial_number +async def test_config_entry_migration_v5_removes_duplicates_of_migrated_entry( + hass: HomeAssistant, +) -> None: + """Test v1.5 migration removes duplicates of an already migrated entry. + + A migrated entry (minor version 5) never runs the migration again, so the + remaining minor version 4 duplicates have to remove themselves instead of + relying on the canonical entry's migration to remove them. + """ + serial_number = "9e2adbd75b8beb119fe564a0f320645d" + data = { + "description": "SkyConnect v1.0", + "device": ( + "/dev/serial/by-id/" + "usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0" + ), + "vid": "10C4", + "pid": "EA60", + "serial_number": serial_number, + "manufacturer": "Nabu Casa", + "product": "SkyConnect v1.0", + "firmware": "ezsp", + "firmware_version": "7.4.4.0", + } + + older_entry = MockConfigEntry( + domain=DOMAIN, + unique_id=( + "10C4:EA60_9e2adbd75b8beb119fe564a0f320645d_Nabu Casa_SkyConnect v1.0" + ), + source="usb", + data=dict(data), + version=1, + minor_version=4, + ) + older_entry.add_to_hass(hass) + + duplicate_entry = MockConfigEntry( + domain=DOMAIN, + unique_id="10C4:EA60_9e2adbd75b8beb119fe564a0f320645d_Nabu_Casa_SkyConnect", + source="import", + data=dict(data), + version=1, + minor_version=4, + ) + duplicate_entry.add_to_hass(hass) + + migrated_entry = MockConfigEntry( + domain=DOMAIN, + unique_id=serial_number, + source="import", + data=dict(data), + version=1, + minor_version=5, + ) + migrated_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homeassistant_sky_connect.os.path.exists", + return_value=True, + ): + await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + + remaining = hass.config_entries.async_entries(DOMAIN) + assert len(remaining) == 1 + assert remaining[0].entry_id == migrated_entry.entry_id + assert remaining[0].minor_version == 5 + assert remaining[0].unique_id == serial_number + assert hass.config_entries.async_get_entry(older_entry.entry_id) is None + assert hass.config_entries.async_get_entry(duplicate_entry.entry_id) is None + + async def test_setup_fails_on_missing_usb_port(hass: HomeAssistant) -> None: """Test setup failing when the USB port is missing.""" diff --git a/tests/components/mqtt/common.py b/tests/components/mqtt/common.py index 0af4ebee425c..4eefc05b59f4 100644 --- a/tests/components/mqtt/common.py +++ b/tests/components/mqtt/common.py @@ -17,6 +17,7 @@ from homeassistant import config as module_hass_config from homeassistant.components import mqtt from homeassistant.components.mqtt import debug_info from homeassistant.components.mqtt.const import ( + DOMAIN, MQTT_CONNECTION_STATE, SUPPORTED_COMPONENTS, ) @@ -1004,12 +1005,10 @@ def help_custom_config( config: ConfigType = copy.deepcopy(mqtt_base_config) entity_instances: list[ConfigType] = [] for instance in mqtt_entity_configs: - base: ConfigType = copy.deepcopy( - mqtt_base_config[mqtt.DOMAIN][mqtt_entity_domain] - ) + base: ConfigType = copy.deepcopy(mqtt_base_config[DOMAIN][mqtt_entity_domain]) base.update(instance) entity_instances.append(base) - config[mqtt.DOMAIN][mqtt_entity_domain] = entity_instances + config[DOMAIN][mqtt_entity_domain] = entity_instances return config @@ -1038,7 +1037,7 @@ async def help_test_availability_without_topic( config: ConfigType, ) -> None: """Test availability without defined availability topic.""" - assert "availability_topic" not in config[mqtt.DOMAIN][domain] + assert "availability_topic" not in config[DOMAIN][domain] await mqtt_mock_entry() await hass.async_block_till_done() @@ -1061,7 +1060,7 @@ async def help_test_default_availability_payload( """ # Add availability settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic" + config[DOMAIN][domain]["availability_topic"] = "availability-topic" with patch("homeassistant.config.load_yaml_config_file", return_value=config): await mqtt_mock_entry() @@ -1108,7 +1107,7 @@ async def help_test_default_availability_list_payload( """ # Add availability settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["availability"] = [ + config[DOMAIN][domain]["availability"] = [ {"topic": "availability-topic1"}, {"topic": "availability-topic2"}, ] @@ -1167,8 +1166,8 @@ async def help_test_default_availability_list_payload_all( """ # Add availability settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["availability_mode"] = "all" - config[mqtt.DOMAIN][domain]["availability"] = [ + config[DOMAIN][domain]["availability_mode"] = "all" + config[DOMAIN][domain]["availability"] = [ {"topic": "availability-topic1"}, {"topic": "availability-topic2"}, ] @@ -1228,8 +1227,8 @@ async def help_test_default_availability_list_payload_any( """ # Add availability settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["availability_mode"] = "any" - config[mqtt.DOMAIN][domain]["availability"] = [ + config[DOMAIN][domain]["availability_mode"] = "any" + config[DOMAIN][domain]["availability"] = [ {"topic": "availability-topic1"}, {"topic": "availability-topic2"}, ] @@ -1284,10 +1283,10 @@ async def help_test_default_availability_list_single( """ # Add availability settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["availability"] = [ + config[DOMAIN][domain]["availability"] = [ {"topic": "availability-topic1"}, ] - config[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic" + config[DOMAIN][domain]["availability_topic"] = "availability-topic" with ( patch("homeassistant.config.load_yaml_config_file", return_value=config), @@ -1316,9 +1315,9 @@ async def help_test_custom_availability_payload( """ # Add availability settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic" - config[mqtt.DOMAIN][domain]["payload_available"] = "good" - config[mqtt.DOMAIN][domain]["payload_not_available"] = "nogood" + config[DOMAIN][domain]["availability_topic"] = "availability-topic" + config[DOMAIN][domain]["payload_available"] = "good" + config[DOMAIN][domain]["payload_not_available"] = "nogood" with patch("homeassistant.config.load_yaml_config_file", return_value=config): await mqtt_mock_entry() @@ -1362,17 +1361,17 @@ async def help_test_discovery_update_availability( await mqtt_mock_entry() # Add availability settings to config config1 = copy.deepcopy(config) - config1[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic1" + config1[DOMAIN][domain]["availability_topic"] = "availability-topic1" config2 = copy.deepcopy(config) - config2[mqtt.DOMAIN][domain]["availability"] = [ + config2[DOMAIN][domain]["availability"] = [ {"topic": "availability-topic2"}, {"topic": "availability-topic3"}, ] config3 = copy.deepcopy(config) - config3[mqtt.DOMAIN][domain]["availability_topic"] = "availability-topic4" - data1 = json.dumps(config1[mqtt.DOMAIN][domain]) - data2 = json.dumps(config2[mqtt.DOMAIN][domain]) - data3 = json.dumps(config3[mqtt.DOMAIN][domain]) + config3[DOMAIN][domain]["availability_topic"] = "availability-topic4" + data1 = json.dumps(config1[DOMAIN][domain]) + data2 = json.dumps(config2[DOMAIN][domain]) + data3 = json.dumps(config3[DOMAIN][domain]) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1) await hass.async_block_till_done() @@ -1439,7 +1438,7 @@ async def help_test_setting_attribute_via_mqtt_json_message( """ # Add JSON attributes settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic" + config[DOMAIN][domain]["json_attributes_topic"] = "attr-topic" with patch("homeassistant.config.load_yaml_config_file", return_value=config): await mqtt_mock_entry() @@ -1465,8 +1464,8 @@ async def help_test_setting_blocked_attribute_via_mqtt_json_message( # Add JSON attributes settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic" - data = json.dumps(config[mqtt.DOMAIN][domain]) + config[DOMAIN][domain]["json_attributes_topic"] = "attr-topic" + data = json.dumps(config[DOMAIN][domain]) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() val = "abc123" @@ -1494,8 +1493,8 @@ async def help_test_setting_attribute_with_template( """ # Add JSON attributes settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic" - config[mqtt.DOMAIN][domain]["json_attributes_template"] = ( + config[DOMAIN][domain]["json_attributes_topic"] = "attr-topic" + config[DOMAIN][domain]["json_attributes_template"] = ( "{{ value_json['Timer1'] | tojson }}" ) with patch("homeassistant.config.load_yaml_config_file", return_value=config): @@ -1524,7 +1523,7 @@ async def help_test_update_with_json_attrs_not_dict( """ # Add JSON attributes settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic" + config[DOMAIN][domain]["json_attributes_topic"] = "attr-topic" with patch("homeassistant.config.load_yaml_config_file", return_value=config): await mqtt_mock_entry() @@ -1548,7 +1547,7 @@ async def help_test_update_with_json_attrs_bad_json( """ # Add JSON attributes settings to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic" + config[DOMAIN][domain]["json_attributes_topic"] = "attr-topic" with patch("homeassistant.config.load_yaml_config_file", return_value=config): await mqtt_mock_entry() @@ -1572,11 +1571,11 @@ async def help_test_discovery_update_attr( await mqtt_mock_entry() # Add JSON attributes settings to config config1 = copy.deepcopy(config) - config1[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic1" + config1[DOMAIN][domain]["json_attributes_topic"] = "attr-topic1" config2 = copy.deepcopy(config) - config2[mqtt.DOMAIN][domain]["json_attributes_topic"] = "attr-topic2" - data1 = json.dumps(config1[mqtt.DOMAIN][domain]) - data2 = json.dumps(config2[mqtt.DOMAIN][domain]) + config2[DOMAIN][domain]["json_attributes_topic"] = "attr-topic2" + data1 = json.dumps(config1[DOMAIN][domain]) + data2 = json.dumps(config2[DOMAIN][domain]) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1) await hass.async_block_till_done() @@ -1895,7 +1894,7 @@ async def help_test_entity_device_info_with_identifier( """ await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["unique_id"] = "veryunique" @@ -1931,7 +1930,7 @@ async def help_test_entity_device_info_with_connection( """ await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_MAC) config["unique_id"] = "veryunique" @@ -1966,7 +1965,7 @@ async def help_test_entity_device_info_remove( """Test device registry remove.""" await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["unique_id"] = "veryunique" @@ -1979,14 +1978,14 @@ async def help_test_entity_device_info_remove( device = dev_registry.async_get_device(identifiers={("mqtt", "helloworld")}) assert device is not None - assert ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, "veryunique") + assert ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique") async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", "") await hass.async_block_till_done() device = dev_registry.async_get_device(identifiers={("mqtt", "helloworld")}) assert device is None - assert not ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, "veryunique") + assert not ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique") async def help_test_entity_device_info_update( @@ -2001,7 +2000,7 @@ async def help_test_entity_device_info_update( """ await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["unique_id"] = "veryunique" @@ -2039,7 +2038,7 @@ async def help_test_entity_name( """ await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["unique_id"] = "veryunique" expected_entity_name = "test" @@ -2074,12 +2073,12 @@ async def help_test_entity_id_update_subscriptions( """Test MQTT subscriptions are managed when entity_id is updated.""" # Add unique_id to config config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["unique_id"] = "TOTALLY_UNIQUE" + config[DOMAIN][domain]["unique_id"] = "TOTALLY_UNIQUE" if topics is None: # Add default topics to config - config[mqtt.DOMAIN][domain]["availability_topic"] = "avty-topic" - config[mqtt.DOMAIN][domain]["state_topic"] = "test-topic" + config[DOMAIN][domain]["availability_topic"] = "avty-topic" + config[DOMAIN][domain]["state_topic"] = "test-topic" topics = ["avty-topic", "test-topic"] assert len(topics) > 0 entity_registry = er.async_get(hass) @@ -2130,15 +2129,15 @@ async def help_test_entity_id_update_discovery_update( # Add unique_id to config await mqtt_mock_entry() config = copy.deepcopy(config) - config[mqtt.DOMAIN][domain]["unique_id"] = "TOTALLY_UNIQUE" + config[DOMAIN][domain]["unique_id"] = "TOTALLY_UNIQUE" if topic is None: # Add default topic to config - config[mqtt.DOMAIN][domain]["availability_topic"] = "avty-topic" + config[DOMAIN][domain]["availability_topic"] = "avty-topic" topic = "avty-topic" entity_registry = er.async_get(hass) - data = json.dumps(config[mqtt.DOMAIN][domain]) + data = json.dumps(config[DOMAIN][domain]) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() @@ -2155,8 +2154,8 @@ async def help_test_entity_id_update_discovery_update( ) await hass.async_block_till_done() - config[mqtt.DOMAIN][domain]["availability_topic"] = f"{topic}_2" - data = json.dumps(config[mqtt.DOMAIN][domain]) + config[DOMAIN][domain]["availability_topic"] = f"{topic}_2" + data = json.dumps(config[DOMAIN][domain]) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() assert len(hass.states.async_entity_ids(domain)) == 1 @@ -2178,7 +2177,7 @@ async def help_test_entity_debug_info( """ await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["unique_id"] = "veryunique" config["platform"] = "mqtt" @@ -2219,7 +2218,7 @@ async def help_test_entity_debug_info_max_messages( """ await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["unique_id"] = "veryunique" @@ -2282,7 +2281,7 @@ async def help_test_entity_debug_info_message( """ # Add device settings to config await mqtt_mock_entry() - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["unique_id"] = "veryunique" @@ -2385,7 +2384,7 @@ async def help_test_entity_debug_info_remove( """ await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["unique_id"] = "veryunique" config["platform"] = "mqtt" @@ -2438,7 +2437,7 @@ async def help_test_entity_debug_info_update_entity_id( """ await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["unique_id"] = "veryunique" config["platform"] = "mqtt" @@ -2502,7 +2501,7 @@ async def help_test_entity_disabled_by_default( """Test device registry remove.""" await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["enabled_by_default"] = False config["unique_id"] = "veryunique1" @@ -2514,7 +2513,7 @@ async def help_test_entity_disabled_by_default( data = json.dumps(config) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla1/config", data) await hass.async_block_till_done() - entity_id = ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, "veryunique1") + entity_id = ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique1") assert entity_id is not None and hass.states.get(entity_id) is None assert dev_registry.async_get_device(identifiers={("mqtt", "helloworld")}) @@ -2524,14 +2523,14 @@ async def help_test_entity_disabled_by_default( data = json.dumps(config) async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla2/config", data) await hass.async_block_till_done() - entity_id = ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, "veryunique2") + entity_id = ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique2") assert entity_id is not None and hass.states.get(entity_id) is not None # Remove the enabled entity, both entities and the device should be removed async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla2/config", "") await hass.async_block_till_done() - assert not ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, "veryunique1") - assert not ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, "veryunique2") + assert not ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique1") + assert not ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique2") assert not dev_registry.async_get_device(identifiers={("mqtt", "helloworld")}) @@ -2544,7 +2543,7 @@ async def help_test_entity_category( """Test device registry remove.""" await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) ent_registry = er.async_get(hass) @@ -2555,7 +2554,7 @@ async def help_test_entity_category( data = json.dumps(config) async_fire_mqtt_message(hass, f"homeassistant/{domain}/{unique_id}/config", data) await hass.async_block_till_done() - entity_id = ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, unique_id) + entity_id = ent_registry.async_get_entity_id(domain, DOMAIN, unique_id) assert entity_id is not None and hass.states.get(entity_id) entry = ent_registry.async_get(entity_id) assert entry is not None and entry.entity_category is None @@ -2567,7 +2566,7 @@ async def help_test_entity_category( data = json.dumps(config) async_fire_mqtt_message(hass, f"homeassistant/{domain}/{unique_id}/config", data) await hass.async_block_till_done() - entity_id = ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, unique_id) + entity_id = ent_registry.async_get_entity_id(domain, DOMAIN, unique_id) assert entity_id is not None and hass.states.get(entity_id) entry = ent_registry.async_get(entity_id) assert entry is not None and entry.entity_category == EntityCategory.DIAGNOSTIC @@ -2579,7 +2578,7 @@ async def help_test_entity_category( data = json.dumps(config) async_fire_mqtt_message(hass, f"homeassistant/{domain}/{unique_id}/config", data) await hass.async_block_till_done() - assert not ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, unique_id) + assert not ent_registry.async_get_entity_id(domain, DOMAIN, unique_id) async def help_test_entity_icon_and_entity_picture( @@ -2591,7 +2590,7 @@ async def help_test_entity_icon_and_entity_picture( """Test entity picture and icon.""" await mqtt_mock_entry() # Add device settings to config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) ent_registry = er.async_get(hass) @@ -2602,7 +2601,7 @@ async def help_test_entity_icon_and_entity_picture( data = json.dumps(config) async_fire_mqtt_message(hass, f"homeassistant/{domain}/{unique_id}/config", data) await hass.async_block_till_done() - entity_id = ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, unique_id) + entity_id = ent_registry.async_get_entity_id(domain, DOMAIN, unique_id) state = hass.states.get(entity_id) assert entity_id is not None and state assert state.attributes.get("icon") is None @@ -2615,7 +2614,7 @@ async def help_test_entity_icon_and_entity_picture( data = json.dumps(config) async_fire_mqtt_message(hass, f"homeassistant/{domain}/{unique_id}/config", data) await hass.async_block_till_done() - entity_id = ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, unique_id) + entity_id = ent_registry.async_get_entity_id(domain, DOMAIN, unique_id) state = hass.states.get(entity_id) assert entity_id is not None and state assert state.attributes.get("icon") is None @@ -2629,7 +2628,7 @@ async def help_test_entity_icon_and_entity_picture( data = json.dumps(config) async_fire_mqtt_message(hass, f"homeassistant/{domain}/{unique_id}/config", data) await hass.async_block_till_done() - entity_id = ent_registry.async_get_entity_id(domain, mqtt.DOMAIN, unique_id) + entity_id = ent_registry.async_get_entity_id(domain, DOMAIN, unique_id) state = hass.states.get(entity_id) assert entity_id is not None and state assert state.attributes.get("icon") == "mdi:emoji-happy-outline" @@ -2662,7 +2661,7 @@ async def help_test_publishing_with_custom_encoding( setup_config = [] service_data = {} for test_id, test_data in test_config.items(): - test_config_setup: dict[str, Any] = copy.copy(config[mqtt.DOMAIN][domain]) + test_config_setup: dict[str, Any] = copy.copy(config[DOMAIN][domain]) test_config_setup.update( { topic: f"cmd/{test_id}", @@ -2802,7 +2801,7 @@ async def help_test_reloadable( ) -> None: """Test reloading an MQTT platform.""" # Set up with empty config - config = copy.deepcopy(config[mqtt.DOMAIN][domain]) + config = copy.deepcopy(config[DOMAIN][domain]) # Create and test an old config of 2 entities based on the config supplied old_config_1 = copy.deepcopy(config) old_config_1["name"] = "test_old_1" @@ -2810,11 +2809,11 @@ async def help_test_reloadable( old_config_2["name"] = "test_old_2" old_config = { - mqtt.DOMAIN: {domain: [old_config_1, old_config_2]}, + DOMAIN: {domain: [old_config_1, old_config_2]}, } # Start the MQTT entry with the old config entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, @@ -2838,7 +2837,7 @@ async def help_test_reloadable( new_config_extra["name"] = "test_new_3" new_config = { - mqtt.DOMAIN: {domain: [new_config_1, new_config_2, new_config_extra]}, + DOMAIN: {domain: [new_config_1, new_config_2, new_config_extra]}, } with patch("homeassistant.config.load_yaml_config_file", return_value=new_config): # Reload the mqtt entry with the new config @@ -2859,7 +2858,7 @@ async def help_test_reloadable( async def help_test_unload_config_entry(hass: HomeAssistant) -> None: """Test unloading the MQTT config entry.""" - mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] assert mqtt_config_entry.state is ConfigEntryState.LOADED assert await hass.config_entries.async_unload(mqtt_config_entry.entry_id) @@ -2878,14 +2877,14 @@ async def help_test_unload_config_entry_with_platform( """Test unloading the MQTT config entry with a specific platform domain.""" # prepare setup through configuration.yaml config_setup: dict[str, dict[str, Any]] = copy.deepcopy(config) - config_setup[mqtt.DOMAIN][domain]["name"] = "config_setup" + config_setup[DOMAIN][domain]["name"] = "config_setup" config_name = config_setup with patch("homeassistant.config.load_yaml_config_file", return_value=config_name): await mqtt_mock_entry() # prepare setup through discovery - discovery_setup = copy.deepcopy(config[mqtt.DOMAIN][domain]) + discovery_setup = copy.deepcopy(config[DOMAIN][domain]) discovery_setup["name"] = "discovery_setup" async_fire_mqtt_message( hass, f"homeassistant/{domain}/bla/config", json.dumps(discovery_setup) diff --git a/tests/components/mqtt/conftest.py b/tests/components/mqtt/conftest.py index b0d2c42803bd..7f40bc03d6fe 100644 --- a/tests/components/mqtt/conftest.py +++ b/tests/components/mqtt/conftest.py @@ -10,6 +10,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from homeassistant.components import mqtt +from homeassistant.components.mqtt import DOMAIN from homeassistant.components.mqtt.models import MessageCallbackType, ReceiveMessage from homeassistant.components.mqtt.util import EnsureJobAfterCooldown from homeassistant.const import EVENT_HOMEASSISTANT_STARTED @@ -115,14 +116,14 @@ async def setup_with_birth_msg_client_mock( patch("homeassistant.components.mqtt.client.SUBSCRIBE_COOLDOWN", 0.0), ): entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data=mqtt_config_entry_data or {mqtt.CONF_BROKER: "test-broker"}, options=mqtt_config_entry_options or {}, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) entry.add_to_hass(hass) - hass.config.components.add(mqtt.DOMAIN) + hass.config.components.add(DOMAIN) assert await hass.config_entries.async_setup(entry.entry_id) hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) diff --git a/tests/components/mqtt/test_alarm_control_panel.py b/tests/components/mqtt/test_alarm_control_panel.py index d7f5367edff8..41161a04aee9 100644 --- a/tests/components/mqtt/test_alarm_control_panel.py +++ b/tests/components/mqtt/test_alarm_control_panel.py @@ -8,7 +8,7 @@ from unittest.mock import patch import pytest -from homeassistant.components import alarm_control_panel, mqtt +from homeassistant.components import alarm_control_panel from homeassistant.components.alarm_control_panel import ( AlarmControlPanelEntityFeature, AlarmControlPanelState, @@ -16,6 +16,7 @@ from homeassistant.components.alarm_control_panel import ( from homeassistant.components.mqtt.alarm_control_panel import ( MQTT_ALARM_ATTRIBUTES_BLOCKED, ) +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.models import PublishPayloadType from homeassistant.const import ( ATTR_CODE, @@ -83,7 +84,7 @@ DEFAULT_FEATURES = ( ) DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { alarm_control_panel.DOMAIN: { "name": "test", "state_topic": "alarm/state", @@ -93,7 +94,7 @@ DEFAULT_CONFIG = { } DEFAULT_CONFIG_CODE_NOT_REQUIRED = { - mqtt.DOMAIN: { + DOMAIN: { alarm_control_panel.DOMAIN: { "name": "test", "state_topic": "alarm/state", @@ -104,7 +105,7 @@ DEFAULT_CONFIG_CODE_NOT_REQUIRED = { } DEFAULT_CONFIG_CODE = { - mqtt.DOMAIN: { + DOMAIN: { alarm_control_panel.DOMAIN: { "name": "test", "state_topic": "alarm/state", @@ -116,7 +117,7 @@ DEFAULT_CONFIG_CODE = { } DEFAULT_CONFIG_REMOTE_CODE = { - mqtt.DOMAIN: { + DOMAIN: { alarm_control_panel.DOMAIN: { "name": "test", "state_topic": "alarm/state", @@ -128,7 +129,7 @@ DEFAULT_CONFIG_REMOTE_CODE = { } DEFAULT_CONFIG_REMOTE_CODE_TEXT = { - mqtt.DOMAIN: { + DOMAIN: { alarm_control_panel.DOMAIN: { "name": "test", "state_topic": "alarm/state", @@ -151,7 +152,7 @@ def does_not_raise(): [ ( { - mqtt.DOMAIN: { + DOMAIN: { alarm_control_panel.DOMAIN: { "name": "test", "command_topic": "alarm/command", @@ -162,7 +163,7 @@ def does_not_raise(): ), ( { - mqtt.DOMAIN: { + DOMAIN: { alarm_control_panel.DOMAIN: { "name": "test", "state_topic": "alarm/state", @@ -173,7 +174,7 @@ def does_not_raise(): ), ( { - mqtt.DOMAIN: { + DOMAIN: { alarm_control_panel.DOMAIN: { "name": "test", "command_topic": "alarm/command", @@ -753,7 +754,7 @@ async def test_disarm_publishes_mqtt_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { alarm_control_panel.DOMAIN: { "name": "test", "command_topic": "test-topic", @@ -953,7 +954,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { alarm_control_panel.DOMAIN: [ { "name": "Test 1", @@ -983,7 +984,7 @@ async def test_discovery_removal_alarm( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test removal of discovered alarm_control_panel.""" - data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN]) + data = json.dumps(DEFAULT_CONFIG[DOMAIN][alarm_control_panel.DOMAIN]) await help_test_discovery_removal( hass, mqtt_mock_entry, alarm_control_panel.DOMAIN, data ) @@ -993,8 +994,8 @@ async def test_discovery_update_alarm_topic_and_template( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered alarm_control_panel.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][alarm_control_panel.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][alarm_control_panel.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" config1["state_topic"] = "alarm/state1" @@ -1027,8 +1028,8 @@ async def test_discovery_update_alarm_template( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered alarm_control_panel.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][alarm_control_panel.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][alarm_control_panel.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" config1["state_topic"] = "alarm/state1" @@ -1059,7 +1060,7 @@ async def test_discovery_update_unchanged_alarm( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered alarm_control_panel.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][alarm_control_panel.DOMAIN]) config1["name"] = "Beer" data1 = json.dumps(config1) @@ -1109,7 +1110,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, alarm_control_panel.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][alarm_control_panel.DOMAIN], + DEFAULT_CONFIG[DOMAIN][alarm_control_panel.DOMAIN], topic, value, ) diff --git a/tests/components/mqtt/test_binary_sensor.py b/tests/components/mqtt/test_binary_sensor.py index 55349c3845c4..084c680c90a6 100644 --- a/tests/components/mqtt/test_binary_sensor.py +++ b/tests/components/mqtt/test_binary_sensor.py @@ -11,7 +11,8 @@ from freezegun import freeze_time from freezegun.api import FrozenDateTimeFactory import pytest -from homeassistant.components import binary_sensor, mqtt +from homeassistant.components import binary_sensor +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import ( EVENT_STATE_CHANGED, STATE_OFF, @@ -63,7 +64,7 @@ from tests.common import ( from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -76,7 +77,7 @@ DEFAULT_CONFIG = { "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -111,7 +112,7 @@ async def test_setting_sensor_value_expires_availability_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -288,7 +289,7 @@ async def test_expiration_on_discovery_and_discovery_update_of_binary_sensor( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -326,7 +327,7 @@ async def test_setting_sensor_value_via_mqtt_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -370,7 +371,7 @@ async def test_invalid_sensor_value_via_mqtt_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -405,7 +406,7 @@ async def test_setting_sensor_value_via_mqtt_message_and_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -446,7 +447,7 @@ async def test_setting_sensor_value_via_mqtt_message_and_template2( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "encoding": "", @@ -485,7 +486,7 @@ async def test_setting_sensor_value_via_mqtt_msg_and_template_and_raw_state_enco "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -520,7 +521,7 @@ async def test_setting_sensor_value_via_mqtt_message_empty_template( [ ( { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "device_class": "motion", @@ -532,7 +533,7 @@ async def test_setting_sensor_value_via_mqtt_message_empty_template( ), ( { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "device_class": None, @@ -560,7 +561,7 @@ async def test_valid_device_class( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "device_class": "abc123", @@ -622,7 +623,7 @@ async def test_custom_availability_payload( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -661,7 +662,7 @@ async def test_force_update_disabled( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -701,7 +702,7 @@ async def test_force_update_enabled( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -801,7 +802,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { binary_sensor.DOMAIN: [ { "name": "Test 1", @@ -829,7 +830,7 @@ async def test_discovery_removal_binary_sensor( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test removal of discovered binary_sensor.""" - data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN]) + data = json.dumps(DEFAULT_CONFIG[DOMAIN][binary_sensor.DOMAIN]) await help_test_discovery_removal(hass, mqtt_mock_entry, binary_sensor.DOMAIN, data) @@ -837,8 +838,8 @@ async def test_discovery_update_binary_sensor_topic_template( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered binary_sensor.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][binary_sensor.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][binary_sensor.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" config1["state_topic"] = "sensor/state1" @@ -873,8 +874,8 @@ async def test_discovery_update_binary_sensor_template( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered binary_sensor.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][binary_sensor.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][binary_sensor.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" config1["state_topic"] = "sensor/state1" @@ -929,7 +930,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, binary_sensor.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN], + DEFAULT_CONFIG[DOMAIN][binary_sensor.DOMAIN], topic, value, attribute, @@ -941,7 +942,7 @@ async def test_discovery_update_unchanged_binary_sensor( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered binary_sensor.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][binary_sensor.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][binary_sensor.DOMAIN]) config1["name"] = "Beer" data1 = json.dumps(config1) @@ -1097,9 +1098,7 @@ async def test_cleanup_triggers_and_restoring_state( freezer.move_to("2022-02-02 12:01:10+01:00") - await help_test_reload_with_config( - hass, caplog, tmp_path, {mqtt.DOMAIN: hass_config} - ) + await help_test_reload_with_config(hass, caplog, tmp_path, {DOMAIN: hass_config}) state = hass.states.get("binary_sensor.test1") assert state.state == state1 @@ -1137,7 +1136,7 @@ async def test_skip_restoring_state_with_over_due_expire_trigger( freezer.move_to("2022-02-02 12:02:00+01:00") domain = binary_sensor.DOMAIN - config3: ConfigType = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][domain]) + config3: ConfigType = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][domain]) config3["name"] = "test3" config3["expire_after"] = 10 config3["state_topic"] = "test-topic3" diff --git a/tests/components/mqtt/test_button.py b/tests/components/mqtt/test_button.py index 8cf3854bf2a2..d8db80cb2190 100644 --- a/tests/components/mqtt/test_button.py +++ b/tests/components/mqtt/test_button.py @@ -6,7 +6,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import button, mqtt +from homeassistant.components import button +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -42,7 +43,7 @@ from .common import ( from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {button.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {button.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } @@ -51,7 +52,7 @@ DEFAULT_CONFIG = { "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { button.DOMAIN: { "command_topic": "command-topic", "name": "test", @@ -93,7 +94,7 @@ async def test_sending_mqtt_commands( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { button.DOMAIN: { "command_topic": "command-topic", "name": "test", @@ -110,7 +111,7 @@ async def test_sending_mqtt_commands( } }, { - mqtt.DOMAIN: { + DOMAIN: { button.DOMAIN: { "command_topic": "command-topic", "name": "test", @@ -152,7 +153,7 @@ async def test_sending_mqtt_commands_with_message_expiry_interval( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { button.DOMAIN: { "command_topic": "command-topic", "command_template": '{ "{{ value }}": "{{ entity_id }}" }', @@ -215,7 +216,7 @@ async def test_default_availability_payload( ) -> None: """Test availability by default payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { button.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -233,7 +234,7 @@ async def test_custom_availability_payload( ) -> None: """Test availability by custom payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { button.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -309,7 +310,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { button.DOMAIN: [ { "name": "Test 1", @@ -345,8 +346,8 @@ async def test_discovery_update_button( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered button.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][button.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][button.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][button.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][button.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" @@ -446,7 +447,7 @@ async def test_entity_debug_info_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { button.DOMAIN: { "name": "test", "command_topic": "test-topic", @@ -470,7 +471,7 @@ async def test_invalid_device_class( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { button.DOMAIN: [ { "name": "Test 1", diff --git a/tests/components/mqtt/test_camera.py b/tests/components/mqtt/test_camera.py index b5971adcb925..6583a552820c 100644 --- a/tests/components/mqtt/test_camera.py +++ b/tests/components/mqtt/test_camera.py @@ -7,8 +7,9 @@ from unittest.mock import patch import pytest -from homeassistant.components import camera, mqtt +from homeassistant.components import camera from homeassistant.components.mqtt.camera import MQTT_CAMERA_ATTRIBUTES_BLOCKED +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.core import HomeAssistant from .common import ( @@ -45,12 +46,12 @@ from tests.typing import ( MqttMockPahoClient, ) -DEFAULT_CONFIG = {mqtt.DOMAIN: {camera.DOMAIN: {"name": "test", "topic": "test_topic"}}} +DEFAULT_CONFIG = {DOMAIN: {camera.DOMAIN: {"name": "test", "topic": "test_topic"}}} @pytest.mark.parametrize( "hass_config", - [{mqtt.DOMAIN: {camera.DOMAIN: {"topic": "test/camera", "name": "Test Camera"}}}], + [{DOMAIN: {camera.DOMAIN: {"topic": "test/camera", "name": "Test Camera"}}}], ) async def test_run_camera_setup( hass: HomeAssistant, @@ -76,7 +77,7 @@ async def test_run_camera_setup( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { camera.DOMAIN: { "topic": "test/camera", "name": "Test Camera", @@ -110,7 +111,7 @@ async def test_run_camera_b64_encoded( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { "camera": { "topic": "test/camera", "name": "Test Camera", @@ -250,7 +251,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { camera.DOMAIN: [ { "name": "Test 1", @@ -278,7 +279,7 @@ async def test_discovery_removal_camera( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test removal of discovered camera.""" - data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][camera.DOMAIN]) + data = json.dumps(DEFAULT_CONFIG[DOMAIN][camera.DOMAIN]) await help_test_discovery_removal(hass, mqtt_mock_entry, camera.DOMAIN, data) diff --git a/tests/components/mqtt/test_client.py b/tests/components/mqtt/test_client.py index 046e3bb15581..9d9dd713822a 100644 --- a/tests/components/mqtt/test_client.py +++ b/tests/components/mqtt/test_client.py @@ -15,7 +15,7 @@ import pytest from homeassistant.components import mqtt from homeassistant.components.mqtt.client import RECONNECT_INTERVAL_SECONDS -from homeassistant.components.mqtt.const import SUPPORTED_COMPONENTS +from homeassistant.components.mqtt.const import DOMAIN, SUPPORTED_COMPONENTS from homeassistant.components.mqtt.models import MessageCallbackType, ReceiveMessage from homeassistant.config_entries import ConfigEntryDisabler, ConfigEntryState from homeassistant.const import ( @@ -99,7 +99,7 @@ async def test_mqtt_await_ack_at_disconnect(hass: HomeAssistant) -> None: ) mqtt_client.publish = MagicMock(return_value=FakeInfo()) entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={ "certificate": "auto", mqtt.CONF_BROKER: "test-broker", @@ -276,7 +276,7 @@ async def test_message_expiry_interval_fails_for_legacy_protocols( await mqtt.async_publish( hass, "test-topic", "test-payload", 2, True, message_expiry_interval=60 ) - assert exc.value.translation_domain == mqtt.DOMAIN + assert exc.value.translation_domain == DOMAIN assert exc.value.translation_key == "mqtt_message_expiry_interval_not_supported" assert exc.value.translation_placeholders == { "topic": "test-topic", @@ -399,7 +399,7 @@ async def test_subscribe_mqtt_config_entry_disabled( """Test the subscription of a topic when MQTT config entry is disabled.""" mqtt_mock.connected = True - mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] mqtt_config_entry_state = mqtt_config_entry.state assert mqtt_config_entry_state is ConfigEntryState.LOADED @@ -1340,7 +1340,7 @@ async def test_initial_setup_logs_error( ) -> None: """Test for setup failure if initial client connection fails.""" entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, @@ -1487,7 +1487,7 @@ async def test_publish_error( ) -> None: """Test publish error.""" entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, @@ -1681,7 +1681,7 @@ async def test_handle_mqtt_timeout_on_callback( ) entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, @@ -1718,7 +1718,7 @@ async def test_setup_raises_config_entry_not_ready_if_no_connect_broker( ) -> None: """Test for setup failure if connection to broker is missing.""" entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, @@ -1863,14 +1863,14 @@ async def test_custom_birth_message( """Test sending birth message.""" entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data=mqtt_config_entry_data, options=mqtt_config_entry_options, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) entry.add_to_hass(hass) - hass.config.components.add(mqtt.DOMAIN) + hass.config.components.add(DOMAIN) assert await hass.config_entries.async_setup(entry.entry_id) mock_debouncer.clear() hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) @@ -1913,14 +1913,14 @@ async def test_no_birth_message( ) -> None: """Test disabling birth message.""" entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data=mqtt_config_entry_data, options=mqtt_config_entry_options, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) entry.add_to_hass(hass) - hass.config.components.add(mqtt.DOMAIN) + hass.config.components.add(DOMAIN) mock_debouncer.clear() assert await hass.config_entries.async_setup(entry.entry_id) # Wait for discovery cooldown @@ -1953,14 +1953,14 @@ async def test_delayed_birth_message( await hass.async_block_till_done() birth = asyncio.Event() entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data=mqtt_config_entry_data, options=mqtt_config_entry_options, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) entry.add_to_hass(hass) - hass.config.components.add(mqtt.DOMAIN) + hass.config.components.add(DOMAIN) assert await hass.config_entries.async_setup(entry.entry_id) @callback @@ -2033,14 +2033,14 @@ async def test_custom_will_message( ) -> None: """Test will message.""" entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data=mqtt_config_entry_data, options=mqtt_config_entry_options, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) entry.add_to_hass(hass) - hass.config.components.add(mqtt.DOMAIN) + hass.config.components.add(DOMAIN) assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() @@ -2071,14 +2071,14 @@ async def test_no_will_message( ) -> None: """Test will message.""" entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data=mqtt_config_entry_data, options=mqtt_config_entry_options, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) entry.add_to_hass(hass) - hass.config.components.add(mqtt.DOMAIN) + hass.config.components.add(DOMAIN) assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() @@ -2619,7 +2619,7 @@ async def test_subscriptions_id_generation(hass: HomeAssistant) -> None: assert new_id_3 == mqtt.models.MAX_28BIT with pytest.raises(HomeAssistantError) as exc: generator.get_or_generate("test4/#") - assert exc.value.translation_domain == mqtt.DOMAIN + assert exc.value.translation_domain == DOMAIN assert exc.value.translation_key == "mqtt_max_subscription_id_reached" generator.release("test2/#") @@ -2629,7 +2629,7 @@ async def test_subscriptions_id_generation(hass: HomeAssistant) -> None: with pytest.raises(HomeAssistantError) as exc: generator.get_or_generate("test5/#") - assert exc.value.translation_domain == mqtt.DOMAIN + assert exc.value.translation_domain == DOMAIN assert exc.value.translation_key == "mqtt_max_subscription_id_reached" generator.release("test1/#") @@ -2642,5 +2642,5 @@ async def test_subscriptions_id_generation(hass: HomeAssistant) -> None: with pytest.raises(HomeAssistantError) as exc: generator.get_or_generate("test7/#") - assert exc.value.translation_domain == mqtt.DOMAIN + assert exc.value.translation_domain == DOMAIN assert exc.value.translation_key == "mqtt_max_subscription_id_reached" diff --git a/tests/components/mqtt/test_climate.py b/tests/components/mqtt/test_climate.py index 5888babc52ee..fa715bfbf57a 100644 --- a/tests/components/mqtt/test_climate.py +++ b/tests/components/mqtt/test_climate.py @@ -8,7 +8,7 @@ from unittest.mock import call, patch import pytest import voluptuous as vol -from homeassistant.components import climate, mqtt +from homeassistant.components import climate from homeassistant.components.climate import ( ATTR_CURRENT_HUMIDITY, ATTR_CURRENT_TEMPERATURE, @@ -34,6 +34,7 @@ from homeassistant.components.mqtt.climate import ( ) from homeassistant.components.mqtt.const import ( DEFAULT_CLIMATE_INITIAL_TEMPERATURE as DEFAULT_INITIAL_TEMPERATURE, + DOMAIN, ) from homeassistant.const import ATTR_TEMPERATURE, STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -83,7 +84,7 @@ from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient ENTITY_CLIMATE = "climate.test" DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: { "name": "test", "mode_command_topic": "mode-topic", @@ -1421,7 +1422,7 @@ async def test_get_target_temperature_low_high_with_templates( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: { "name": "test", "mode_command_topic": "mode-topic", @@ -1588,7 +1589,7 @@ async def test_get_with_templates( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: { "name": "test", "mode_command_topic": "mode-topic", @@ -2021,7 +2022,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: [ { "name": "Test 1", @@ -2074,7 +2075,7 @@ async def test_encoding_subscribable_topics( attribute_value: Any, ) -> None: """Test handling of incoming encoded payload.""" - config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][climate.DOMAIN]) + config = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][climate.DOMAIN]) await help_test_encoding_subscribable_topics( hass, mqtt_mock_entry, @@ -2091,7 +2092,7 @@ async def test_discovery_removal_climate( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test removal of discovered climate.""" - data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][climate.DOMAIN]) + data = json.dumps(DEFAULT_CONFIG[DOMAIN][climate.DOMAIN]) await help_test_discovery_removal(hass, mqtt_mock_entry, climate.DOMAIN, data) @@ -2172,7 +2173,7 @@ async def test_entity_id_update_subscriptions( ) -> None: """Test MQTT subscriptions are managed when entity_id is updated.""" config = { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: { "name": "test", "mode_state_topic": "test-topic", @@ -2199,7 +2200,7 @@ async def test_entity_debug_info_message( ) -> None: """Test MQTT debug info.""" config = { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: { "name": "test", "mode_command_topic": "command-topic", @@ -2362,8 +2363,8 @@ async def test_publishing_with_custom_encoding( domain = climate.DOMAIN config = copy.deepcopy(DEFAULT_CONFIG) if topic != "preset_mode_command_topic": - del config[mqtt.DOMAIN][domain]["preset_mode_command_topic"] - del config[mqtt.DOMAIN][domain]["preset_modes"] + del config[DOMAIN][domain]["preset_mode_command_topic"] + del config[DOMAIN][domain]["preset_modes"] await help_test_publishing_with_custom_encoding( hass, @@ -2384,7 +2385,7 @@ async def test_publishing_with_custom_encoding( [ ( # test_valid_humidity_min_max { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: { "name": "test", "min_humidity": 20, @@ -2396,7 +2397,7 @@ async def test_publishing_with_custom_encoding( ), ( # test_invalid_humidity_min_max_1 { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: { "name": "test", "min_humidity": 0, @@ -2408,7 +2409,7 @@ async def test_publishing_with_custom_encoding( ), ( # test_invalid_humidity_min_max_2 { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: { "name": "test", "max_humidity": 20, @@ -2420,7 +2421,7 @@ async def test_publishing_with_custom_encoding( ), ( # test_valid_humidity_state { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: { "name": "test", "target_humidity_state_topic": "humidity-state", @@ -2432,7 +2433,7 @@ async def test_publishing_with_custom_encoding( ), ( # test_invalid_humidity_state { - mqtt.DOMAIN: { + DOMAIN: { climate.DOMAIN: { "name": "test", "target_humidity_state_topic": "humidity-state", diff --git a/tests/components/mqtt/test_config_flow.py b/tests/components/mqtt/test_config_flow.py index 59070e59bd1b..5c215c32b2c9 100644 --- a/tests/components/mqtt/test_config_flow.py +++ b/tests/components/mqtt/test_config_flow.py @@ -21,7 +21,7 @@ from homeassistant.components.mqtt.config_flow import ( PWD_NOT_CHANGED, TRANSLATION_DESCRIPTION_PLACEHOLDERS, ) -from homeassistant.components.mqtt.const import CONF_DISCOVERY_QOS +from homeassistant.components.mqtt.const import CONF_DISCOVERY_QOS, DOMAIN from homeassistant.components.mqtt.util import learn_more_url from homeassistant.config_entries import ConfigSubentry, ConfigSubentryData from homeassistant.const import ( @@ -541,7 +541,7 @@ async def test_manual_config_set( ) # Check config entry got setup assert len(mock_finish_setup.mock_calls) == 1 - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] assert config_entry.title == "127.0.0.1" @@ -563,7 +563,7 @@ async def test_user_single_instance(hass: HomeAssistant) -> None: async def test_hassio_already_configured(hass: HomeAssistant) -> None: """Test we only allow a single config flow.""" MockConfigEntry( - domain="mqtt", + domain=DOMAIN, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ).add_to_hass(hass) @@ -578,14 +578,14 @@ async def test_hassio_already_configured(hass: HomeAssistant) -> None: async def test_hassio_ignored(hass: HomeAssistant) -> None: """Test we supervisor discovered instance can be ignored.""" MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, source=config_entries.SOURCE_IGNORE, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ).add_to_hass(hass) result = await hass.config_entries.flow.async_init( - mqtt.DOMAIN, + DOMAIN, data=HassioServiceInfo( config={ "addon": "Mosquitto", @@ -1101,7 +1101,7 @@ async def test_option_flow( "homeassistant.config.async_hass_config_yaml", AsyncMock(return_value={}) ) as yaml_mock: await mqtt_mock_entry() - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] result = await hass.config_entries.options.async_init(config_entry.entry_id) assert result["type"] is FlowResultType.FORM @@ -1258,7 +1258,7 @@ async def test_bad_certificate( test_input.pop(mqtt.CONF_CLIENT_KEY) mqtt_mock = await mqtt_mock_entry() - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] # Add at least one advanced option to get the full form hass.config_entries.async_update_entry( config_entry, @@ -1335,7 +1335,7 @@ async def test_keepalive_validation( mqtt_mock = await mqtt_mock_entry() mock_try_connection.return_value = True - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] # Add at least one advanced option to get the full form hass.config_entries.async_update_entry( config_entry, @@ -1376,7 +1376,7 @@ async def test_disable_birth_will( """Test disabling birth and will.""" await mqtt_mock_entry() mock_try_connection.return_value = True - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] hass.config_entries.async_update_entry( config_entry, data={ @@ -1441,7 +1441,7 @@ async def test_invalid_discovery_prefix( """Test setting an invalid discovery prefix.""" mqtt_mock = await mqtt_mock_entry() mock_try_connection.return_value = True - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] hass.config_entries.async_update_entry( config_entry, data={ @@ -1506,7 +1506,7 @@ async def test_option_flow_default_suggested_values( ) -> None: """Test config flow options has default/suggested values.""" await mqtt_mock_entry() - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] hass.config_entries.async_update_entry( config_entry, data={ @@ -1653,7 +1653,7 @@ async def test_step_reauth( # Prepare the config entry config_entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data=test_input, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, @@ -1734,7 +1734,7 @@ async def test_step_hassio_reauth( # Prepare the config entry config_entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data=entry_data, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, @@ -1822,7 +1822,7 @@ async def test_step_hassio_reauth_no_discovery_info( # Prepare the config entry config_entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data=entry_data, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, @@ -1853,7 +1853,7 @@ async def test_reconfigure_user_connection_fails( ) -> None: """Test if connection cannot be made.""" config_entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) @@ -1891,7 +1891,7 @@ async def test_options_bad_birth_message_fails( ) -> None: """Test bad birth message.""" config_entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) @@ -1929,7 +1929,7 @@ async def test_options_bad_will_message_fails( ) -> None: """Test bad will message.""" config_entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) @@ -1974,7 +1974,7 @@ async def test_try_connection_with_advanced_parameters( ) -> None: """Test config flow with advanced parameters from config.""" config_entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) @@ -2112,7 +2112,7 @@ async def test_setup_with_advanced_settings( file_id = mock_process_uploaded_file.file_id config_entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) @@ -2298,7 +2298,7 @@ async def test_setup_with_certificates( file_id = mock_process_uploaded_file.file_id config_entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) @@ -2419,7 +2419,7 @@ async def test_change_websockets_transport_to_tcp( ) -> None: """Test reconfiguration flow changing websockets transport settings.""" config_entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) @@ -2488,7 +2488,7 @@ async def test_reconfigure_flow_form( ) -> None: """Test reconfigure flow.""" await mqtt_mock_entry() - entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "broker" @@ -2542,7 +2542,7 @@ async def test_reconfigure_no_changed_password( ) -> None: """Test reconfigure flow.""" await mqtt_mock_entry() - entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "broker" @@ -2603,7 +2603,7 @@ async def test_migrate_config_entry( expected_minor_version: int, ) -> None: """Test migrating a config entry.""" - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] # Mock to a migratable or compatbible config entry version hass.config_entries.async_update_entry( config_entry, @@ -2645,7 +2645,7 @@ async def test_migrate_of_incompatible_config_entry( options: dict[str, Any], ) -> None: """Test migrating a config entry.""" - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] # Mock an incompatible config entry version hass.config_entries.async_update_entry( config_entry, @@ -4109,7 +4109,7 @@ async def test_subentry_configflow( component = next(iter(config_subentries_data["components"].values())) await mqtt_mock_entry() - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] result = await hass.config_entries.subentries.async_init( (config_entry.entry_id, "device"), @@ -4266,7 +4266,7 @@ async def test_subentry_reconfigure_remove_entity( ) -> None: """Test the subentry ConfigFlow reconfigure removing an entity.""" await mqtt_mock_entry() - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id: str subentry: ConfigSubentry subentry_id, subentry = next(iter(config_entry.subentries.items())) @@ -4275,7 +4275,7 @@ async def test_subentry_reconfigure_remove_entity( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device is not None # assert we have an entity for all subentry components @@ -4294,7 +4294,7 @@ async def test_subentry_reconfigure_remove_entity( unique_entity_id = f"{subentry_id}_{key}" entity_id = entity_registry.async_get_entity_id( domain=component["platform"], - platform=mqtt.DOMAIN, + platform=DOMAIN, unique_id=unique_entity_id, ) assert entity_id is not None @@ -4355,7 +4355,7 @@ async def test_subentry_reconfigure_remove_entity( unique_entity_id = f"{subentry_id}_{object_list[1]}" entity_id = entity_registry.async_get_entity_id( domain=components[object_list[1]]["platform"], - platform=mqtt.DOMAIN, + platform=DOMAIN, unique_id=unique_entity_id, ) assert entity_id is None @@ -4390,7 +4390,7 @@ async def test_subentry_reconfigure_edit_entity_multi_entitites( ) -> None: """Test the subentry ConfigFlow reconfigure with multi entities.""" await mqtt_mock_entry() - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id: str subentry: ConfigSubentry subentry_id, subentry = next(iter(config_entry.subentries.items())) @@ -4400,7 +4400,7 @@ async def test_subentry_reconfigure_edit_entity_multi_entitites( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device is not None # assert we have an entity for all subentry components @@ -4418,7 +4418,7 @@ async def test_subentry_reconfigure_edit_entity_multi_entitites( for key in components: unique_entity_id = f"{subentry_id}_{key}" entity_id = entity_registry.async_get_entity_id( - domain="notify", platform=mqtt.DOMAIN, unique_id=unique_entity_id + domain="notify", platform=DOMAIN, unique_id=unique_entity_id ) assert entity_id is not None entity_entry = entity_registry.async_get(entity_id) @@ -4832,7 +4832,7 @@ async def test_subentry_reconfigure_edit_entity_single_entity( ) -> None: """Test the subentry ConfigFlow reconfigure with single entity.""" await mqtt_mock_entry() - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id: str subentry: ConfigSubentry subentry_id, subentry = next(iter(config_entry.subentries.items())) @@ -4841,7 +4841,7 @@ async def test_subentry_reconfigure_edit_entity_single_entity( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device is not None # assert we have an entity for the subentry component @@ -4853,7 +4853,7 @@ async def test_subentry_reconfigure_edit_entity_single_entity( unique_entity_id = f"{subentry_id}_{component_id}" entity_id = entity_registry.async_get_entity_id( - domain=component["platform"], platform=mqtt.DOMAIN, unique_id=unique_entity_id + domain=component["platform"], platform=DOMAIN, unique_id=unique_entity_id ) assert entity_id is not None entity_entry = entity_registry.async_get(entity_id) @@ -4971,7 +4971,7 @@ async def test_subentry_reconfigure_edit_entity_reset_fields( ) -> None: """Test the subentry ConfigFlow reconfigure resets filtered out fields.""" await mqtt_mock_entry() - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id: str subentry: ConfigSubentry subentry_id, subentry = next(iter(config_entry.subentries.items())) @@ -4980,7 +4980,7 @@ async def test_subentry_reconfigure_edit_entity_reset_fields( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device is not None # assert we have an entity for the subentry component @@ -4993,7 +4993,7 @@ async def test_subentry_reconfigure_edit_entity_reset_fields( unique_entity_id = f"{subentry_id}_{component_id}" entity_id = entity_registry.async_get_entity_id( - domain=component["platform"], platform=mqtt.DOMAIN, unique_id=unique_entity_id + domain=component["platform"], platform=DOMAIN, unique_id=unique_entity_id ) assert entity_id is not None entity_entry = entity_registry.async_get(entity_id) @@ -5106,7 +5106,7 @@ async def test_subentry_reconfigure_add_entity( ) -> None: """Test the subentry ConfigFlow reconfigure and add an entity.""" await mqtt_mock_entry() - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id: str subentry: ConfigSubentry subentry_id, subentry = next(iter(config_entry.subentries.items())) @@ -5115,7 +5115,7 @@ async def test_subentry_reconfigure_add_entity( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device is not None # assert we have an entity for the subentry component @@ -5124,7 +5124,7 @@ async def test_subentry_reconfigure_add_entity( component_id_1, component1 = next(iter(components.items())) unique_entity_id = f"{subentry_id}_{component_id_1}" entity_id = entity_registry.async_get_entity_id( - domain=component1["platform"], platform=mqtt.DOMAIN, unique_id=unique_entity_id + domain=component1["platform"], platform=DOMAIN, unique_id=unique_entity_id ) assert entity_id is not None entity_entry = entity_registry.async_get(entity_id) @@ -5212,7 +5212,7 @@ async def test_subentry_reconfigure_update_device_properties( ) -> None: """Test the subentry ConfigFlow reconfigure and update device properties.""" await mqtt_mock_entry() - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id: str subentry: ConfigSubentry subentry_id, subentry = next(iter(config_entry.subentries.items())) @@ -5221,7 +5221,7 @@ async def test_subentry_reconfigure_update_device_properties( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device is not None # assert we have an entity for all subentry components @@ -5367,7 +5367,7 @@ async def test_subentry_reconfigure_availablity( ) -> None: """Test the subentry ConfigFlow reconfigure and update device properties.""" await mqtt_mock_entry() - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id: str subentry: ConfigSubentry subentry_id, subentry = next(iter(config_entry.subentries.items())) @@ -5495,7 +5495,7 @@ async def test_subentry_reconfigure_export_settings( ) -> None: """Test the subentry ConfigFlow reconfigure export feature.""" await mqtt_mock_entry() - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id: str subentry: ConfigSubentry subentry_id, subentry = next(iter(config_entry.subentries.items())) @@ -5504,7 +5504,7 @@ async def test_subentry_reconfigure_export_settings( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device is not None # assert we entity for all subentry components @@ -5561,7 +5561,7 @@ async def test_subentry_configflow_section_feature( ) -> None: """Test subentry sections are hidden with no configurable options.""" await mqtt_mock_entry() - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] result = await hass.config_entries.subentries.async_init( (config_entry.entry_id, "device"), diff --git a/tests/components/mqtt/test_cover.py b/tests/components/mqtt/test_cover.py index b8cbc5d4f176..29d0c15d3ea1 100644 --- a/tests/components/mqtt/test_cover.py +++ b/tests/components/mqtt/test_cover.py @@ -6,7 +6,7 @@ from unittest.mock import patch import pytest -from homeassistant.components import cover, mqtt +from homeassistant.components import cover from homeassistant.components.cover import ( ATTR_CURRENT_POSITION, ATTR_CURRENT_TILT_POSITION, @@ -14,7 +14,7 @@ from homeassistant.components.cover import ( ATTR_TILT_POSITION, CoverState, ) -from homeassistant.components.mqtt.const import CONF_STATE_TOPIC +from homeassistant.components.mqtt.const import CONF_STATE_TOPIC, DOMAIN from homeassistant.components.mqtt.cover import ( CONF_GET_POSITION_TEMPLATE, CONF_GET_POSITION_TOPIC, @@ -81,16 +81,14 @@ from .common import ( from tests.common import async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient -DEFAULT_CONFIG = { - mqtt.DOMAIN: {cover.DOMAIN: {"name": "test", "state_topic": "test-topic"}} -} +DEFAULT_CONFIG = {DOMAIN: {cover.DOMAIN: {"name": "test", "state_topic": "test-topic"}}} @pytest.mark.parametrize( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -134,7 +132,7 @@ async def test_state_via_state_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -180,7 +178,7 @@ async def test_opening_and_closing_state_via_custom_state_payload( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "position-topic", @@ -234,7 +232,7 @@ async def test_open_closed_state_from_position_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "position-topic", @@ -295,7 +293,7 @@ async def test_open_closed_state_from_position_optimistic_alt_positions( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "tilt_command_topic": "set-position-topic", @@ -356,7 +354,7 @@ async def test_tilt_open_closed_toggle_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "tilt_command_topic": "set-position-topic", @@ -421,7 +419,7 @@ async def test_tilt_open_closed_toggle_optimistic_alt_positions( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "get-position-topic", @@ -462,7 +460,7 @@ async def test_position_via_position_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -503,7 +501,7 @@ async def test_state_via_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -546,7 +544,7 @@ async def test_state_via_template_and_entity_id( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -592,7 +590,7 @@ async def test_state_via_template_with_json_value( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "get-position-topic", @@ -638,7 +636,7 @@ async def test_position_via_template_and_entity_id( [ ( { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: {"name": "test", "qos": 0, "command_topic": "abc"} } }, @@ -646,7 +644,7 @@ async def test_position_via_template_and_entity_id( ), ( { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "qos": 0, @@ -660,7 +658,7 @@ async def test_position_via_template_and_entity_id( # ({"set_position_topic": "abc"}, True), - not a valid configuration ( { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "qos": 0, @@ -673,7 +671,7 @@ async def test_position_via_template_and_entity_id( ), ( { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "qos": 0, @@ -685,7 +683,7 @@ async def test_position_via_template_and_entity_id( ), ( { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "qos": 0, @@ -716,7 +714,7 @@ async def test_optimistic_flag( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -784,7 +782,7 @@ async def test_optimistic_state_change( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "optimistic": True, @@ -859,7 +857,7 @@ async def test_optimistic_state_change_with_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -894,7 +892,7 @@ async def test_send_open_cover_command( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -929,7 +927,7 @@ async def test_send_close_cover_command( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -965,7 +963,7 @@ async def test_send_stop_cover_command( [ ( { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -979,7 +977,7 @@ async def test_send_stop_cover_command( ), ( { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -1021,7 +1019,7 @@ async def test_send_stop_tilt_command( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "get-position-topic", @@ -1076,7 +1074,7 @@ async def test_current_cover_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "get-position-topic", @@ -1142,7 +1140,7 @@ async def test_current_cover_position_inverted( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1167,7 +1165,7 @@ async def test_optimistic_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "get-position-topic", @@ -1209,7 +1207,7 @@ async def test_position_update( [ ( { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "get-position-topic", @@ -1229,7 +1227,7 @@ async def test_position_update( ), ( { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "get-position-topic", @@ -1274,7 +1272,7 @@ async def test_set_position_templated( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "get-position-topic", @@ -1322,7 +1320,7 @@ async def test_set_position_templated_and_attributes( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "get-position-topic", @@ -1363,7 +1361,7 @@ async def test_set_tilt_templated( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "get-position-topic", @@ -1457,7 +1455,7 @@ async def test_set_tilt_templated_and_attributes( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "state-topic", @@ -1493,7 +1491,7 @@ async def test_set_position_untemplated( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "position_topic": "state-topic", @@ -1531,7 +1529,7 @@ async def test_set_position_untemplated_custom_percentage_range( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "qos": 0, @@ -1558,7 +1556,7 @@ async def test_no_command_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1584,7 +1582,7 @@ async def test_no_payload_close( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1610,7 +1608,7 @@ async def test_no_payload_open( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1636,7 +1634,7 @@ async def test_no_payload_stop( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "command_topic": "test", "name": "test", @@ -1664,7 +1662,7 @@ async def test_with_command_topic_and_tilt( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -1695,7 +1693,7 @@ async def test_tilt_defaults( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -1785,7 +1783,7 @@ async def test_tilt_via_invocation_defaults( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -1877,7 +1875,7 @@ async def test_tilt_given_value( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -1957,7 +1955,7 @@ async def test_tilt_given_value_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2039,7 +2037,7 @@ async def test_tilt_given_value_altered_range( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2080,7 +2078,7 @@ async def test_tilt_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2124,7 +2122,7 @@ async def test_tilt_via_topic_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2177,7 +2175,7 @@ async def test_tilt_via_topic_template_json_value( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2227,7 +2225,7 @@ async def test_tilt_via_topic_altered_range( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2264,7 +2262,7 @@ async def test_tilt_status_out_of_range_warning( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2299,7 +2297,7 @@ async def test_tilt_status_not_numeric_warning( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2349,7 +2347,7 @@ async def test_tilt_via_topic_altered_range_inverted( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2402,7 +2400,7 @@ async def test_tilt_via_topic_template_altered_range( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2440,7 +2438,7 @@ async def test_tilt_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2479,7 +2477,7 @@ async def test_tilt_position_templated( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2559,7 +2557,7 @@ async def test_custom_availability_payload( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "device_class": "garage", @@ -2583,7 +2581,7 @@ async def test_valid_device_class( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "device_class": "abc123", @@ -2669,7 +2667,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: [ { "name": "Test 1", @@ -2807,7 +2805,7 @@ async def test_entity_debug_info_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2868,7 +2866,7 @@ async def test_state_and_position_topics_state_not_set_via_position_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2925,7 +2923,7 @@ async def test_set_state_via_position_using_stopped_state( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -2963,7 +2961,7 @@ async def test_position_via_position_topic_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -3010,7 +3008,7 @@ async def test_position_via_position_topic_template_json_value( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -3053,7 +3051,7 @@ async def test_position_template_with_entity_id( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -3084,7 +3082,7 @@ async def test_position_via_position_topic_template_return_json( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -3117,7 +3115,7 @@ async def test_position_via_position_topic_template_return_json_warning( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -3165,7 +3163,7 @@ async def test_position_and_tilt_via_position_topic_template_return_json( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -3213,7 +3211,7 @@ async def test_position_via_position_topic_template_all_variables( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -3271,7 +3269,7 @@ async def test_set_state_via_stopped_state_no_position_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -3301,7 +3299,7 @@ async def test_position_via_position_topic_template_return_invalid_json( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -3328,7 +3326,7 @@ async def test_set_position_topic_without_get_position_topic_error( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -3354,7 +3352,7 @@ async def test_value_template_without_state_topic_error( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -3381,7 +3379,7 @@ async def test_position_template_without_position_topic_error( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -3407,7 +3405,7 @@ async def test_set_position_template_without_set_position_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -3433,7 +3431,7 @@ async def test_tilt_command_template_without_tilt_command_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { cover.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -3494,7 +3492,7 @@ async def test_publishing_with_custom_encoding( """Test publishing MQTT payload with different encoding.""" domain = cover.DOMAIN config = deepcopy(DEFAULT_CONFIG) - config[mqtt.DOMAIN][domain]["position_topic"] = "some-position-topic" + config[DOMAIN][domain]["position_topic"] = "some-position-topic" await help_test_publishing_with_custom_encoding( hass, @@ -3541,7 +3539,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, cover.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][cover.DOMAIN], + DEFAULT_CONFIG[DOMAIN][cover.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_date.py b/tests/components/mqtt/test_date.py index b69590adf19e..73ae486a2e61 100644 --- a/tests/components/mqtt/test_date.py +++ b/tests/components/mqtt/test_date.py @@ -6,7 +6,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import date, mqtt +from homeassistant.components import date +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -44,7 +45,7 @@ from tests.common import async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {date.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {date.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } @@ -64,7 +65,7 @@ async def async_set_value( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { date.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -116,7 +117,7 @@ async def test_controlling_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { date.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -155,7 +156,7 @@ async def test_controlling_validation_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { date.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -219,7 +220,7 @@ async def test_default_availability_payload( ) -> None: """Test availability by default payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { date.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -237,7 +238,7 @@ async def test_custom_availability_payload( ) -> None: """Test availability by custom payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { date.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -313,7 +314,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { date.DOMAIN: [ { "name": "Test 1", @@ -492,7 +493,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, date.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][date.DOMAIN], + DEFAULT_CONFIG[DOMAIN][date.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_datetime.py b/tests/components/mqtt/test_datetime.py index 0240d0eaef72..e4d29443ed2e 100644 --- a/tests/components/mqtt/test_datetime.py +++ b/tests/components/mqtt/test_datetime.py @@ -8,7 +8,8 @@ from dateutil.tz import UTC from freezegun import freeze_time import pytest -from homeassistant.components import datetime, mqtt +from homeassistant.components import datetime +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -46,7 +47,7 @@ from tests.common import async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {datetime.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {datetime.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } @@ -68,7 +69,7 @@ async def async_set_value( [ ( { - mqtt.DOMAIN: { + DOMAIN: { datetime.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -84,7 +85,7 @@ async def async_set_value( ), ( { - mqtt.DOMAIN: { + DOMAIN: { datetime.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -143,7 +144,7 @@ async def test_controlling_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { datetime.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -186,7 +187,7 @@ async def test_controlling_validation_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { datetime.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -224,7 +225,7 @@ async def test_ambiguous_date_time_state_update( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { datetime.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -257,7 +258,7 @@ async def test_date_time_with_invalid_timezone_identifier( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { datetime.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -324,7 +325,7 @@ async def test_default_availability_payload( ) -> None: """Test availability by default payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { datetime.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -348,7 +349,7 @@ async def test_custom_availability_payload( ) -> None: """Test availability by custom payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { datetime.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -430,7 +431,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { datetime.DOMAIN: [ { "name": "Test 1", @@ -611,7 +612,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, datetime.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][datetime.DOMAIN], + DEFAULT_CONFIG[DOMAIN][datetime.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_device_tracker.py b/tests/components/mqtt/test_device_tracker.py index 66d8334a3003..7768c2250c21 100644 --- a/tests/components/mqtt/test_device_tracker.py +++ b/tests/components/mqtt/test_device_tracker.py @@ -5,7 +5,7 @@ from datetime import UTC, datetime from freezegun.api import FrozenDateTimeFactory import pytest -from homeassistant.components import device_tracker, mqtt +from homeassistant.components import device_tracker from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import STATE_HOME, STATE_NOT_HOME, STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -27,7 +27,7 @@ from tests.typing import ( ) DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { device_tracker.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -693,7 +693,7 @@ async def test_setting_blocked_attribute_via_mqtt_json_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { device_tracker.DOMAIN: {"name": "jan", "state_topic": "/location/jan"} } } diff --git a/tests/components/mqtt/test_diagnostics.py b/tests/components/mqtt/test_diagnostics.py index d39816752ad1..b529bb9bcd9e 100644 --- a/tests/components/mqtt/test_diagnostics.py +++ b/tests/components/mqtt/test_diagnostics.py @@ -6,6 +6,7 @@ from unittest.mock import ANY import pytest from homeassistant.components import mqtt +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er @@ -29,7 +30,7 @@ async def test_entry_diagnostics( ) -> None: """Test config entry diagnostics.""" mqtt_mock = await mqtt_mock_entry() - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] mqtt_mock.connected = True await get_diagnostics_for_config_entry(hass, hass_client, config_entry) @@ -167,7 +168,7 @@ async def test_redact_diagnostics( expected_config["data"]["password"] = "**REDACTED**" expected_config["data"]["username"] = "**REDACTED**" - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] mqtt_mock.connected = True # Discover a device with a device tracker diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index aa94e8dc1343..1d64f4742af1 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -18,7 +18,7 @@ from homeassistant.components.mqtt.abbreviations import ( ABBREVIATIONS, DEVICE_ABBREVIATIONS, ) -from homeassistant.components.mqtt.const import SUPPORTED_COMPONENTS +from homeassistant.components.mqtt.const import DOMAIN, SUPPORTED_COMPONENTS from homeassistant.components.mqtt.discovery import ( MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_NEW, @@ -207,7 +207,7 @@ async def test_subscribing_config_topic( ) -> None: """Test setting up discovery.""" mqtt_mock = await mqtt_mock_entry() - entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry = hass.config_entries.async_entries(DOMAIN)[0] discovery_topic = "homeassistant" await async_start(hass, discovery_topic, entry) @@ -1754,7 +1754,7 @@ async def test_cleanup_device_manual( assert state is not None # Remove MQTT from the device - mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] mock_debouncer.clear() response = await ws_client.remove_device( device_entry.id, mqtt_config_entry.entry_id @@ -2018,7 +2018,7 @@ async def test_cleanup_device_multiple_config_entries( ) assert device_entry is not None - mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] sensor_config = { "device": {"connections": [["mac", "12:34:56:AB:CD:EF"]]}, @@ -2063,7 +2063,7 @@ async def test_cleanup_device_multiple_config_entries( assert state is not None # Remove MQTT from the device - mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] response = await ws_client.remove_device( device_entry.id, mqtt_config_entry.entry_id ) @@ -2136,7 +2136,7 @@ async def test_cleanup_device_multiple_config_entries_mqtt( connections={("mac", "12:34:56:AB:CD:EF")}, ) - mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] sensor_config = { "device": {"connections": [["mac", "12:34:56:AB:CD:EF"]]}, @@ -2599,7 +2599,7 @@ async def test_mqtt_integration_discovery_flow_fitering_on_redundant_payload( birth.set() entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "mock-broker"}, options=ENTRY_DEFAULT_BIRTH_MESSAGE, version=mqtt.CONFIG_ENTRY_VERSION, @@ -2670,7 +2670,7 @@ async def test_mqtt_discovery_flow_starts_once( birth.set() entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "mock-broker"}, options=ENTRY_DEFAULT_BIRTH_MESSAGE, version=mqtt.CONFIG_ENTRY_VERSION, @@ -2808,7 +2808,7 @@ async def test_mqtt_discovery_flow_subscribes_atr_configured_qos( birth.set() entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "mock-broker"}, options=mqtt_config_entry_options, version=mqtt.CONFIG_ENTRY_VERSION, diff --git a/tests/components/mqtt/test_event.py b/tests/components/mqtt/test_event.py index c4ab7f5309aa..fdd227495e0d 100644 --- a/tests/components/mqtt/test_event.py +++ b/tests/components/mqtt/test_event.py @@ -7,7 +7,8 @@ from unittest.mock import patch from freezegun.api import FrozenDateTimeFactory import pytest -from homeassistant.components import event, mqtt +from homeassistant.components import event +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.event import MQTT_EVENT_ATTRIBUTES_BLOCKED from homeassistant.const import STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -56,7 +57,7 @@ from tests.common import MockConfigEntry, async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { event.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -186,7 +187,7 @@ async def test_setting_event_value_with_invalid_payload( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { event.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -319,7 +320,7 @@ async def test_discovery_update_availability( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { event.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -405,7 +406,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { event.DOMAIN: [ { "name": "Test 1", @@ -630,7 +631,7 @@ async def test_entity_category( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { event.DOMAIN: { "name": "test", "state_topic": "test-topic", diff --git a/tests/components/mqtt/test_fan.py b/tests/components/mqtt/test_fan.py index 643eba68bdc0..0041ab22fe3b 100644 --- a/tests/components/mqtt/test_fan.py +++ b/tests/components/mqtt/test_fan.py @@ -7,7 +7,7 @@ from unittest.mock import patch import pytest from voluptuous.error import MultipleInvalid -from homeassistant.components import fan, mqtt +from homeassistant.components import fan from homeassistant.components.fan import ( ATTR_DIRECTION, ATTR_OSCILLATING, @@ -16,6 +16,7 @@ from homeassistant.components.fan import ( ATTR_PRESET_MODES, NotValidPresetModeError, ) +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.fan import ( CONF_DIRECTION_COMMAND_TOPIC, CONF_DIRECTION_STATE_TOPIC, @@ -72,7 +73,7 @@ from tests.components.fan import common from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -82,7 +83,7 @@ DEFAULT_CONFIG = { } -@pytest.mark.parametrize("hass_config", [{mqtt.DOMAIN: {fan.DOMAIN: {"name": "test"}}}]) +@pytest.mark.parametrize("hass_config", [{DOMAIN: {fan.DOMAIN: {"name": "test"}}}]) @pytest.mark.usefixtures("hass") async def test_fail_setup_if_no_command_topic( caplog: pytest.LogCaptureFixture, mqtt_mock_entry: MqttMockHAClientGenerator @@ -96,7 +97,7 @@ async def test_fail_setup_if_no_command_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -232,7 +233,7 @@ async def test_controlling_state_via_topic( help_custom_config( fan.DOMAIN, { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "command_topic": "command-topic", "percentage_command_topic": "percentage-command-topic", @@ -295,7 +296,7 @@ async def test_controlling_state_via_topic_with_different_speed_range( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -354,7 +355,7 @@ async def test_controlling_state_via_topic_no_percentage_topics( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -472,7 +473,7 @@ async def test_controlling_state_via_topic_and_json_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "state_topic": "shared-state-topic", @@ -587,7 +588,7 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -734,7 +735,7 @@ async def test_sending_mqtt_commands_and_optimistic( help_custom_config( fan.DOMAIN, { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test1", "command_topic": "command-topic", @@ -842,7 +843,7 @@ async def test_sending_mqtt_commands_with_alternate_speed_range( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -989,7 +990,7 @@ async def test_sending_mqtt_commands_and_optimistic_no_legacy( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1197,7 +1198,7 @@ async def test_sending_mqtt_command_templates_( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1260,7 +1261,7 @@ async def test_sending_mqtt_commands_and_optimistic_no_percentage_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -1576,7 +1577,7 @@ async def test_encoding_subscribable_topics( attribute_value: Any, ) -> None: """Test handling of incoming encoded payload.""" - config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][fan.DOMAIN]) + config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][fan.DOMAIN]) config[ATTR_PRESET_MODES] = ["eco", "auto"] config[CONF_PRESET_MODE_COMMAND_TOPIC] = "fan/some_preset_mode_command_topic" config[CONF_PERCENTAGE_COMMAND_TOPIC] = "fan/some_percentage_command_topic" @@ -1598,7 +1599,7 @@ async def test_encoding_subscribable_topics( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1672,7 +1673,7 @@ async def test_attributes( ( "test1", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test1", "command_topic": "command-topic", @@ -1686,7 +1687,7 @@ async def test_attributes( ( "test2", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test2", "command_topic": "command-topic", @@ -1703,7 +1704,7 @@ async def test_attributes( ( "test3", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test3", "command_topic": "command-topic", @@ -1720,7 +1721,7 @@ async def test_attributes( ( "test4", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test4", "command_topic": "command-topic", @@ -1735,7 +1736,7 @@ async def test_attributes( ( "test5", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test5", "command_topic": "command-topic", @@ -1753,7 +1754,7 @@ async def test_attributes( ( "test6", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test6", "command_topic": "command-topic", @@ -1771,7 +1772,7 @@ async def test_attributes( ( "test7", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test7", "command_topic": "command-topic", @@ -1788,7 +1789,7 @@ async def test_attributes( ( "test8", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test8", "command_topic": "command-topic", @@ -1807,7 +1808,7 @@ async def test_attributes( ( "test9", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test9", "command_topic": "command-topic", @@ -1825,7 +1826,7 @@ async def test_attributes( ( "test10", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test10", "command_topic": "command-topic", @@ -1843,7 +1844,7 @@ async def test_attributes( ( "test11", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test11", "command_topic": "command-topic", @@ -1863,7 +1864,7 @@ async def test_attributes( ( "test12", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test12", "command_topic": "command-topic", @@ -1882,7 +1883,7 @@ async def test_attributes( ( "test13", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test13", "command_topic": "command-topic", @@ -1899,7 +1900,7 @@ async def test_attributes( ( "test14", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test14", "command_topic": "command-topic", @@ -1916,7 +1917,7 @@ async def test_attributes( ( "test15", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test15", "command_topic": "command-topic", @@ -1932,7 +1933,7 @@ async def test_attributes( ( "test16", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test16", "command_topic": "command-topic", @@ -1951,7 +1952,7 @@ async def test_attributes( ( "test17", { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: { "name": "test17", "command_topic": "command-topic", @@ -2096,7 +2097,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { fan.DOMAIN: [ { "name": "Test 1", @@ -2293,7 +2294,7 @@ async def test_publishing_with_custom_encoding( domain = fan.DOMAIN config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG) if topic == "preset_mode_command_topic": - config[mqtt.DOMAIN][domain]["preset_modes"] = ["auto", "eco"] + config[DOMAIN][domain]["preset_modes"] = ["auto", "eco"] await help_test_publishing_with_custom_encoding( hass, diff --git a/tests/components/mqtt/test_humidifier.py b/tests/components/mqtt/test_humidifier.py index 075be7b6efa9..3d35ddfe9e62 100644 --- a/tests/components/mqtt/test_humidifier.py +++ b/tests/components/mqtt/test_humidifier.py @@ -7,7 +7,7 @@ from unittest.mock import patch import pytest from voluptuous.error import MultipleInvalid -from homeassistant.components import humidifier, mqtt +from homeassistant.components import humidifier from homeassistant.components.humidifier import ( ATTR_CURRENT_HUMIDITY, ATTR_HUMIDITY, @@ -16,7 +16,7 @@ from homeassistant.components.humidifier import ( SERVICE_SET_MODE, HumidifierAction, ) -from homeassistant.components.mqtt.const import CONF_CURRENT_HUMIDITY_TOPIC +from homeassistant.components.mqtt.const import CONF_CURRENT_HUMIDITY_TOPIC, DOMAIN from homeassistant.components.mqtt.humidifier import ( CONF_MODE_COMMAND_TOPIC, CONF_MODE_STATE_TOPIC, @@ -71,7 +71,7 @@ from tests.common import async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -133,7 +133,7 @@ async def async_set_humidity( @pytest.mark.parametrize( - "hass_config", [{mqtt.DOMAIN: {humidifier.DOMAIN: {"name": "test"}}}] + "hass_config", [{DOMAIN: {humidifier.DOMAIN: {"name": "test"}}}] ) @pytest.mark.usefixtures("hass") async def test_fail_setup_if_no_command_topic( @@ -148,7 +148,7 @@ async def test_fail_setup_if_no_command_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "action_topic": "action-topic", @@ -308,7 +308,7 @@ async def test_controlling_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "action_topic": "action-topic", @@ -449,7 +449,7 @@ async def test_controlling_state_via_topic_and_json_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "state_topic": "shared-state-topic", @@ -528,7 +528,7 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -627,7 +627,7 @@ async def test_sending_mqtt_commands_and_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -731,7 +731,7 @@ async def test_sending_mqtt_command_templates_( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -870,9 +870,7 @@ async def test_encoding_subscribable_topics( attribute_value: Any, ) -> None: """Test handling of incoming encoded payload.""" - config: dict[str, Any] = copy.deepcopy( - DEFAULT_CONFIG[mqtt.DOMAIN][humidifier.DOMAIN] - ) + config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][humidifier.DOMAIN]) config["modes"] = ["eco", "auto"] config[CONF_MODE_COMMAND_TOPIC] = "humidifier/some_mode_command_topic" await help_test_encoding_subscribable_topics( @@ -891,7 +889,7 @@ async def test_encoding_subscribable_topics( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -941,7 +939,7 @@ async def test_attributes( [ ( # test valid case 1 { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -953,7 +951,7 @@ async def test_attributes( ), ( # test valid case 2 { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -966,7 +964,7 @@ async def test_attributes( ), ( # test valid case 3 { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -979,7 +977,7 @@ async def test_attributes( ), ( # test valid case 4 { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -992,7 +990,7 @@ async def test_attributes( ), ( # test invalid device_class { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1005,7 +1003,7 @@ async def test_attributes( ), ( # test mode_command_topic without modes { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1018,7 +1016,7 @@ async def test_attributes( ), ( # test invalid humidity min max case 1 { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1032,7 +1030,7 @@ async def test_attributes( ), ( # test invalid humidity min max case 2 { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1046,7 +1044,7 @@ async def test_attributes( ), ( # test invalid mode, is reset payload { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -1075,7 +1073,7 @@ async def test_validity_configurations( ( "test1", { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test1", "command_topic": "command-topic", @@ -1089,7 +1087,7 @@ async def test_validity_configurations( ( "test2", { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test2", "command_topic": "command-topic", @@ -1105,7 +1103,7 @@ async def test_validity_configurations( ( "test3", { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test3", "command_topic": "command-topic", @@ -1119,7 +1117,7 @@ async def test_validity_configurations( ( "test4", { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test4", "command_topic": "command-topic", @@ -1135,7 +1133,7 @@ async def test_validity_configurations( ( "test5", { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test5", "command_topic": "command-topic", @@ -1148,7 +1146,7 @@ async def test_validity_configurations( ( "test6", { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: { "name": "test6", "target_humidity_command_topic": "humidity-command-topic", @@ -1291,7 +1289,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { humidifier.DOMAIN: [ { "name": "Test 1", @@ -1494,7 +1492,7 @@ async def test_publishing_with_custom_encoding( domain = humidifier.DOMAIN config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG) if topic == "mode_command_topic": - config[mqtt.DOMAIN][domain]["modes"] = ["auto", "eco"] + config[DOMAIN][domain]["modes"] = ["auto", "eco"] await help_test_publishing_with_custom_encoding( hass, diff --git a/tests/components/mqtt/test_image.py b/tests/components/mqtt/test_image.py index cd0c37f6e45c..89457cf7bd3b 100644 --- a/tests/components/mqtt/test_image.py +++ b/tests/components/mqtt/test_image.py @@ -10,7 +10,8 @@ import httpx import pytest import respx -from homeassistant.components import image, mqtt +from homeassistant.components import image +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -49,15 +50,13 @@ from tests.typing import ( MqttMockPahoClient, ) -DEFAULT_CONFIG = { - mqtt.DOMAIN: {image.DOMAIN: {"name": "test", "image_topic": "test_topic"}} -} +DEFAULT_CONFIG = {DOMAIN: {image.DOMAIN: {"name": "test", "image_topic": "test_topic"}}} @pytest.mark.freeze_time("2023-04-01 00:00:00+00:00") @pytest.mark.parametrize( "hass_config", - [{mqtt.DOMAIN: {image.DOMAIN: {"image_topic": "test/image", "name": "Test"}}}], + [{DOMAIN: {image.DOMAIN: {"image_topic": "test/image", "name": "Test"}}}], ) async def test_run_image_setup( hass: HomeAssistant, @@ -93,7 +92,7 @@ async def test_run_image_setup( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { image.DOMAIN: { "image_topic": "test/image", "name": "Test", @@ -147,7 +146,7 @@ async def test_run_image_b64_encoded( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { "image": { "image_topic": "test/image", "name": "Test", @@ -200,7 +199,7 @@ async def test_image_b64_encoded_with_availability( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { "image": { "url_topic": "test/image", "name": "Test", @@ -281,7 +280,7 @@ async def test_image_from_url( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { "image": { "url_topic": "test/image", "name": "Test", @@ -334,7 +333,7 @@ async def test_image_from_url_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { "image": { "url_topic": "test/image", "name": "Test", @@ -402,7 +401,7 @@ async def test_image_from_url_content_type( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { "image": { "url_topic": "test/image", "name": "Test", @@ -462,7 +461,7 @@ async def test_image_from_url_fails( [ ( { - mqtt.DOMAIN: { + DOMAIN: { "image": { "url_topic": "test/image", "content_type": "image/jpg", @@ -475,7 +474,7 @@ async def test_image_from_url_fails( ), ( { - mqtt.DOMAIN: { + DOMAIN: { "image": { "url_topic": "test/image", "image_topic": "test/image-data-topic", @@ -488,7 +487,7 @@ async def test_image_from_url_fails( ), ( { - mqtt.DOMAIN: { + DOMAIN: { "image": { "name": "Test", "encoding": "utf-8", @@ -601,7 +600,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { image.DOMAIN: [ { "name": "Test 1", @@ -629,7 +628,7 @@ async def test_discovery_removal_image( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test removal of discovered image.""" - data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][image.DOMAIN]) + data = json.dumps(DEFAULT_CONFIG[DOMAIN][image.DOMAIN]) await help_test_discovery_removal(hass, mqtt_mock_entry, image.DOMAIN, data) @@ -806,7 +805,7 @@ async def test_skipped_async_ha_write_state( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { image.DOMAIN: { "name": "test", "url_topic": "test-topic", diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 9335af092a3e..ef476d1d782c 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -18,6 +18,7 @@ import voluptuous as vol from homeassistant import core as ha from homeassistant.components import mqtt from homeassistant.components.mqtt import debug_info +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.models import ( MessageCallbackType, MqttCommandTemplateException, @@ -272,7 +273,7 @@ async def test_service_call_without_topic_does_not_publish( mqtt_mock = await mqtt_mock_entry() with pytest.raises(vol.Invalid): await hass.services.async_call( - mqtt.DOMAIN, + DOMAIN, mqtt.SERVICE_PUBLISH, {}, blocking=True, @@ -284,7 +285,7 @@ async def test_service_call_mqtt_entry_does_not_publish( hass: HomeAssistant, mqtt_client_mock: MqttMockPahoClient ) -> None: """Test the service call if topic is missing.""" - assert await async_setup_component(hass, mqtt.DOMAIN, {}) + assert await async_setup_component(hass, DOMAIN, {}) with pytest.raises( ServiceValidationError, match=( @@ -292,7 +293,7 @@ async def test_service_call_mqtt_entry_does_not_publish( ), ): await hass.services.async_call( - mqtt.DOMAIN, + DOMAIN, mqtt.SERVICE_PUBLISH, { mqtt.ATTR_TOPIC: "test_topic", @@ -312,7 +313,7 @@ async def test_service_call_with_template_topic_renders_invalid_topic( mqtt_mock = await mqtt_mock_entry() with pytest.raises(vol.Invalid) as exc: await hass.services.async_call( - mqtt.DOMAIN, + DOMAIN, mqtt.SERVICE_PUBLISH, { mqtt.ATTR_TOPIC: "test/{{ '+' if True else 'topic' }}/topic", @@ -356,7 +357,7 @@ async def test_mqtt_publish_action_call_with_raw_data( """ mqtt_mock = await mqtt_mock_entry() await hass.services.async_call( - mqtt.DOMAIN, + DOMAIN, mqtt.SERVICE_PUBLISH, { mqtt.ATTR_TOPIC: "test/topic", @@ -372,7 +373,7 @@ async def test_mqtt_publish_action_call_with_raw_data( "homeassistant.components.mqtt.models.literal_eval" ) as literal_eval_mock: await hass.services.async_call( - mqtt.DOMAIN, + DOMAIN, mqtt.SERVICE_PUBLISH, { mqtt.ATTR_TOPIC: "test/topic", @@ -383,7 +384,7 @@ async def test_mqtt_publish_action_call_with_raw_data( literal_eval_mock.assert_not_called() await hass.services.async_call( - mqtt.DOMAIN, + DOMAIN, mqtt.SERVICE_PUBLISH, { mqtt.ATTR_TOPIC: "test/topic", @@ -404,7 +405,7 @@ async def test_service_call_with_ascii_qos_retain_flags( """ mqtt_mock = await mqtt_mock_entry() await hass.services.async_call( - mqtt.DOMAIN, + DOMAIN, mqtt.SERVICE_PUBLISH, { mqtt.ATTR_TOPIC: "test/topic", @@ -423,7 +424,7 @@ async def test_service_call_with_ascii_qos_retain_flags( # Test service call without payload await hass.services.async_call( - mqtt.DOMAIN, + DOMAIN, mqtt.SERVICE_PUBLISH, { mqtt.ATTR_TOPIC: "test/topic", @@ -485,7 +486,7 @@ async def test_publish_action_with_message_expiry_interval( """Test the publish action message expiry interval option.""" mqtt_mock = await mqtt_mock_entry() await hass.services.async_call( - mqtt.DOMAIN, + DOMAIN, mqtt.SERVICE_PUBLISH, { mqtt.ATTR_TOPIC: "test/topic", @@ -708,7 +709,7 @@ def test_entity_device_info_schema() -> None: "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { "sensor": [ { "name": "test-sensor", @@ -807,13 +808,13 @@ async def test_reload_entry_with_restored_subscriptions( """Test reloading the config entry with with subscriptions restored.""" # Setup the MQTT entry entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) entry.add_to_hass(hass) - hass.config.components.add(mqtt.DOMAIN) + hass.config.components.add(DOMAIN) with patch("homeassistant.config.load_yaml_config_file", return_value={}): await hass.config_entries.async_setup(entry.entry_id) @@ -869,11 +870,27 @@ async def test_reload_entry_with_restored_subscriptions( assert recorded_calls[1].payload == "wild-card-payload3" +@pytest.mark.usefixtures("mqtt_client_mock") +async def test_reload_without_entry( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test reloading without a valid config entry set up.""" + # Setup the MQTT integration without entry + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + with caplog.at_level(logging.DEBUG): + await hass.services.async_call(DOMAIN, SERVICE_RELOAD, {}, blocking=True) + assert ( + "Skipped reloading MQTT integration, the MQTT config entry is not enabled" + in caplog.text + ) + + @pytest.mark.parametrize( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { "light": { "platform": "mqtt", "name": "test", @@ -894,7 +911,7 @@ async def test_setup_manual_mqtt_with_platform_key( ) -@pytest.mark.parametrize("hass_config", [{mqtt.DOMAIN: {"light": {"name": "test"}}}]) +@pytest.mark.parametrize("hass_config", [{DOMAIN: {"light": {"name": "test"}}}]) async def test_setup_manual_mqtt_with_invalid_config( mqtt_mock_entry: MqttMockHAClientGenerator, caplog: pytest.LogCaptureFixture ) -> None: @@ -916,13 +933,13 @@ async def test_default_entry_setting_are_applied( # Config entry data is incomplete but valid according the schema entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={"broker": "test-broker", "port": 1234}, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) entry.add_to_hass(hass) - hass.config.components.add(mqtt.DOMAIN) + hass.config.components.add(DOMAIN) assert await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() @@ -1122,7 +1139,7 @@ async def test_mqtt_ws_remove_discovered_device( assert device_entry is not None client = await hass_ws_client(hass) - mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] response = await client.remove_device(device_entry.id, mqtt_config_entry.entry_id) assert response["success"] @@ -1460,7 +1477,7 @@ async def test_debug_info_non_mqtt( ) assert await async_setup_component( - hass, mqtt.DOMAIN, {mqtt.DOMAIN: {domain: {"platform": "test"}}} + hass, DOMAIN, {DOMAIN: {domain: {"platform": "test"}}} ) debug_info_data = debug_info.info_for_device(hass, device_entry.id) @@ -1759,20 +1776,20 @@ async def test_unload_config_entry( ) -> None: """Test unloading the MQTT entry.""" entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "test-broker", CONF_PROTOCOL: "5"}, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, ) entry.add_to_hass(hass) - assert await async_setup_component(hass, mqtt.DOMAIN, {}) - assert hass.services.has_service(mqtt.DOMAIN, "dump") - assert hass.services.has_service(mqtt.DOMAIN, "publish") + assert await async_setup_component(hass, DOMAIN, {}) + assert hass.services.has_service(DOMAIN, "dump") + assert hass.services.has_service(DOMAIN, "publish") # Make sure the debouncer finishes await mock_debouncer.wait() - mqtt_config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] assert mqtt_config_entry.state is ConfigEntryState.LOADED # Publish just before unloading to test await cleanup @@ -1802,8 +1819,8 @@ async def test_unload_config_entry( assert new_mqtt_config_entry.state is ConfigEntryState.NOT_LOADED await hass.async_block_till_done(wait_background_tasks=True) - assert hass.services.has_service(mqtt.DOMAIN, "dump") - assert hass.services.has_service(mqtt.DOMAIN, "publish") + assert hass.services.has_service(DOMAIN, "dump") + assert hass.services.has_service(DOMAIN, "publish") assert "No ACK from MQTT server" not in caplog.text @@ -1811,9 +1828,9 @@ async def test_publish_or_subscribe_without_valid_config_entry( hass: HomeAssistant, record_calls: MessageCallbackType ) -> None: """Test internal publish function with bad use cases.""" - assert await async_setup_component(hass, mqtt.DOMAIN, {}) - assert hass.services.has_service(mqtt.DOMAIN, "dump") - assert hass.services.has_service(mqtt.DOMAIN, "publish") + assert await async_setup_component(hass, DOMAIN, {}) + assert hass.services.has_service(DOMAIN, "dump") + assert hass.services.has_service(DOMAIN, "publish") with pytest.raises(HomeAssistantError): await mqtt.async_publish( hass, "some-topic", "test-payload", qos=0, retain=False, encoding=None @@ -1846,7 +1863,7 @@ async def test_disabling_and_enabling_entry( ) -> None: """Test disabling and enabling the config entry.""" await mqtt_mock_entry() - entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry = hass.config_entries.async_entries(DOMAIN)[0] assert entry.state is ConfigEntryState.LOADED # Late discovery of a mqtt entity config_tag = ( @@ -1913,7 +1930,7 @@ async def test_disabling_and_enabling_entry( [ ( { - mqtt.DOMAIN: { + DOMAIN: { "light": [ { "name": "test1", @@ -1932,7 +1949,7 @@ async def test_disabling_and_enabling_entry( ), ( { - mqtt.DOMAIN: { + DOMAIN: { "light": [ { "name": "test1", @@ -2019,8 +2036,8 @@ async def test_link_config_entry( 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) + mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] + mqtt_platforms = async_get_platforms(hass, DOMAIN) @callback def _check_entities() -> int: @@ -2100,12 +2117,12 @@ async def test_reload_config_entry( await hass.async_block_till_done() assert hass.states.get("sensor.test_discovery") is not None - entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry = hass.config_entries.async_entries(DOMAIN)[0] @callback def _check_entities() -> int: entities: list[Entity] = [] - mqtt_platforms = async_get_platforms(hass, mqtt.DOMAIN) + mqtt_platforms = async_get_platforms(hass, DOMAIN) for mqtt_platform in mqtt_platforms: assert mqtt_platform.config_entry is entry entities += (entity for entity in mqtt_platform.entities.values()) @@ -2439,16 +2456,14 @@ async def test_mqtt_integration_level_imports(attr: str) -> None: @pytest.mark.usefixtures("mqtt_client_mock") @pytest.mark.parametrize( - "hass_config", [{mqtt.DOMAIN: {"sensor": {"state_topic": "test-topic"}}}] + "hass_config", [{DOMAIN: {"sensor": {"state_topic": "test-topic"}}}] ) async def test_yaml_config_without_entry( hass: HomeAssistant, hass_config: ConfigType, issue_registry: ir.IssueRegistry ) -> None: """Test a repair issue is created for YAML setup without an active config entry.""" - await async_setup_component(hass, mqtt.DOMAIN, hass_config) - issue = issue_registry.async_get_issue( - mqtt.DOMAIN, "yaml_setup_without_active_setup" - ) + await async_setup_component(hass, DOMAIN, hass_config) + issue = issue_registry.async_get_issue(DOMAIN, "yaml_setup_without_active_setup") assert issue is not None assert ( issue.learn_more_url == "https://www.home-assistant.io/integrations/mqtt/" @@ -2457,7 +2472,7 @@ async def test_yaml_config_without_entry( @pytest.mark.parametrize( - "hass_config", [{mqtt.DOMAIN: {"sensor": {"state_topic": "test-topic"}}}] + "hass_config", [{DOMAIN: {"sensor": {"state_topic": "test-topic"}}}] ) async def test_yaml_config_with_active_mqtt_config_entry( hass: HomeAssistant, @@ -2467,9 +2482,7 @@ async def test_yaml_config_with_active_mqtt_config_entry( ) -> None: """Test no repair issue is created for YAML setup with an active config entry.""" await mqtt_mock_entry() - issue = issue_registry.async_get_issue( - mqtt.DOMAIN, "yaml_setup_without_active_setup" - ) + issue = issue_registry.async_get_issue(DOMAIN, "yaml_setup_without_active_setup") state = hass.states.get("sensor.mqtt_sensor") assert state is not None assert issue is None @@ -2520,7 +2533,7 @@ async def test_mqtt_protocol_successful_migration_to_v5( "The MQTT protocol version was successfully updated to version 5" in caplog.text ) - entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry = hass.config_entries.async_entries(DOMAIN)[0] assert entry.data[mqtt.CONF_PROTOCOL] == mqtt.PROTOCOL_5 @@ -2582,7 +2595,7 @@ async def test_mqtt_protocol_failed_migration_to_v5( issue_registry = ir.async_get(hass) assert len(issue_registry.issues) == 1 - issue = issue_registry.async_get_issue(mqtt.DOMAIN, "protocol_5_migration") + issue = issue_registry.async_get_issue(DOMAIN, "protocol_5_migration") assert issue is not None assert issue.translation_key == "protocol_5_migration" assert issue.translation_placeholders == { @@ -2593,5 +2606,5 @@ async def test_mqtt_protocol_failed_migration_to_v5( "The MQTT protocol version was successfully updated to version 5" not in caplog.text ) - entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry = hass.config_entries.async_entries(DOMAIN)[0] assert entry.data.get(mqtt.CONF_PROTOCOL, mqtt.PROTOCOL_311) == current_protocol diff --git a/tests/components/mqtt/test_lawn_mower.py b/tests/components/mqtt/test_lawn_mower.py index 77618c818447..534ade49f51f 100644 --- a/tests/components/mqtt/test_lawn_mower.py +++ b/tests/components/mqtt/test_lawn_mower.py @@ -7,7 +7,7 @@ from unittest.mock import patch import pytest -from homeassistant.components import lawn_mower, mqtt +from homeassistant.components import lawn_mower from homeassistant.components.lawn_mower import ( DOMAIN as LAWN_MOWER_DOMAIN, SERVICE_DOCK, @@ -15,6 +15,7 @@ from homeassistant.components.lawn_mower import ( SERVICE_START_MOWING, LawnMowerEntityFeature, ) +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.lawn_mower import MQTT_LAWN_MOWER_ATTRIBUTES_BLOCKED from homeassistant.const import ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, STATE_UNKNOWN from homeassistant.core import HomeAssistant, State @@ -63,7 +64,7 @@ DEFAULT_FEATURES = ( ) DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "name": "test", "dock_command_topic": "dock-test-topic", @@ -78,7 +79,7 @@ DEFAULT_CONFIG = { "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "activity_state_topic": "test/lawn_mower_stat", "dock_command_topic": "dock-test-topic", @@ -135,7 +136,7 @@ async def test_run_lawn_mower_setup_and_state_updates( ), ( { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "pause_command_topic": "pause-test-topic", "name": "test", @@ -146,7 +147,7 @@ async def test_run_lawn_mower_setup_and_state_updates( ), ( { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "dock_command_topic": "dock-test-topic", "start_mowing_command_topic": "start_mowing-test-topic", @@ -175,7 +176,7 @@ async def test_supported_features( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "activity_state_topic": "test/lawn_mower_stat", "name": "Test Lawn Mower", @@ -289,7 +290,7 @@ async def test_run_lawn_mower_service_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "pause_command_topic": "test/lawn_mower_pause_cmd", "name": "Test Lawn Mower", @@ -315,7 +316,7 @@ async def test_restore_lawn_mower_from_invalid_state( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "name": "Test Lawn Mower", "dock_command_topic": "test/lawn_mower_dock_cmd", @@ -501,7 +502,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: [ { "name": "Test 1", @@ -529,7 +530,7 @@ async def test_discovery_removal_lawn_mower( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test removal of discovered lawn_mower.""" - data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][lawn_mower.DOMAIN]) + data = json.dumps(DEFAULT_CONFIG[DOMAIN][lawn_mower.DOMAIN]) await help_test_discovery_removal(hass, mqtt_mock_entry, lawn_mower.DOMAIN, data) @@ -628,7 +629,7 @@ async def test_entity_id_update_subscriptions( ) -> None: """Test MQTT subscriptions are managed when entity_id is updated.""" config = { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "name": "test", "activity_state_topic": "test-topic", @@ -687,7 +688,7 @@ async def test_entity_debug_info_message( ) -> None: """Test MQTT debug info.""" config = { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "activity_state_topic": "test/lawn_mower_stat", "dock_command_topic": "dock-test-topic", @@ -714,7 +715,7 @@ async def test_entity_debug_info_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "dock_command_topic": "dock-test-topic", "pause_command_topic": "pause-test-topic", @@ -826,9 +827,7 @@ async def test_encoding_subscribable_topics( attribute_value: Any, ) -> None: """Test handling of incoming encoded payload.""" - config: dict[str, Any] = copy.deepcopy( - DEFAULT_CONFIG[mqtt.DOMAIN][lawn_mower.DOMAIN] - ) + config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][lawn_mower.DOMAIN]) config["actions"] = ["milk", "beer"] await help_test_encoding_subscribable_topics( hass, @@ -935,7 +934,7 @@ async def test_skipped_async_ha_write_state( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lawn_mower.DOMAIN: { "name": "test", "activity_state_topic": "test-topic", diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index 755c2dbfd1da..071505d33062 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -192,7 +192,8 @@ from unittest.mock import call, patch import pytest -from homeassistant.components import light, mqtt +from homeassistant.components import light +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.light.schema_basic import ( CONF_BRIGHTNESS_COMMAND_TOPIC, CONF_COLOR_TEMP_COMMAND_TOPIC, @@ -246,13 +247,11 @@ from tests.components.light import common from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {light.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {light.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } -@pytest.mark.parametrize( - "hass_config", [{mqtt.DOMAIN: {light.DOMAIN: {"name": "test"}}}] -) +@pytest.mark.parametrize("hass_config", [{DOMAIN: {light.DOMAIN: {"name": "test"}}}]) async def test_fail_setup_if_no_command_topic( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator, @@ -267,7 +266,7 @@ async def test_fail_setup_if_no_command_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "test_light_rgb/status", @@ -421,7 +420,7 @@ async def test_no_min_max_kelvin( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "test_light_rgb/status", @@ -548,7 +547,7 @@ async def test_controlling_state_via_topic( [ ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "test_light_color_temp/status", @@ -574,7 +573,7 @@ async def test_controlling_state_via_topic( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "test_light_color_temp/status", @@ -803,7 +802,7 @@ async def test_received_rgbx_values_set_state_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "test_light_rgb/status", @@ -935,7 +934,7 @@ async def test_invalid_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "test_scale/status", @@ -985,7 +984,7 @@ async def test_brightness_controlling_scale( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "test_scale_rgb/status", @@ -1059,7 +1058,7 @@ async def test_brightness_from_rgb_controlling_scale( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "test_light_rgb/status", @@ -1179,7 +1178,7 @@ async def test_controlling_state_via_topic_with_templates( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light_rgb/set", @@ -1445,7 +1444,7 @@ async def test_sending_mqtt_commands_and_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light_rgb/set", @@ -1494,7 +1493,7 @@ async def test_sending_mqtt_rgb_command_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light_rgb/set", @@ -1543,7 +1542,7 @@ async def test_sending_mqtt_rgbw_command_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light_rgb/set", @@ -1593,7 +1592,7 @@ async def test_sending_mqtt_rgbww_command_with_template( [ ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light_color_temp/set", @@ -1614,7 +1613,7 @@ async def test_sending_mqtt_rgbww_command_with_template( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light_color_temp/set", @@ -1674,7 +1673,7 @@ async def test_sending_mqtt_color_temp_command_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -1718,7 +1717,7 @@ async def test_on_command_first( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -1761,7 +1760,7 @@ async def test_on_command_last( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -1826,7 +1825,7 @@ async def test_on_command_brightness( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -1908,7 +1907,7 @@ async def test_on_command_brightness_scaled( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -2010,7 +2009,7 @@ async def test_on_command_rgb( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -2126,7 +2125,7 @@ async def test_on_command_rgbw( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -2246,7 +2245,7 @@ async def test_on_command_rgbww( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -2293,7 +2292,7 @@ async def test_on_command_rgb_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -2345,7 +2344,7 @@ async def test_on_command_rgbw_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -2397,7 +2396,7 @@ async def test_on_command_rgbww_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "tasmota_B94927/cmnd/POWER", @@ -2509,7 +2508,7 @@ async def test_on_command_white( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "test_light_rgb/status", @@ -2664,7 +2663,7 @@ async def test_explicit_color_mode( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "test_light_rgb/status", @@ -2752,7 +2751,7 @@ async def test_explicit_color_mode_templated( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "state_topic": "tasmota_B94927/tele/STATE", @@ -2825,7 +2824,7 @@ async def test_white_state_update( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light/set", @@ -2976,7 +2975,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: [ { "name": "Test 1", @@ -3636,7 +3635,7 @@ async def test_entity_debug_info_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_max_mireds/set", @@ -3760,9 +3759,9 @@ async def test_publishing_with_custom_encoding( domain = light.DOMAIN config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG) if topic == "effect_command_topic": - config[mqtt.DOMAIN][domain]["effect_list"] = ["random", "color_loop"] + config[DOMAIN][domain]["effect_list"] = ["random", "color_loop"] elif topic == "white_command_topic": - config[mqtt.DOMAIN][domain]["rgb_command_topic"] = "some-cmd-topic" + config[DOMAIN][domain]["rgb_command_topic"] = "some-cmd-topic" await help_test_publishing_with_custom_encoding( hass, @@ -3847,7 +3846,7 @@ async def test_encoding_subscribable_topics( init_payload: tuple[str, str] | None, ) -> None: """Test handling of incoming encoded payload.""" - config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][light.DOMAIN]) + config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][light.DOMAIN]) config[CONF_EFFECT_COMMAND_TOPIC] = "light/CONF_EFFECT_COMMAND_TOPIC" config[CONF_RGB_COMMAND_TOPIC] = "light/CONF_RGB_COMMAND_TOPIC" config[CONF_BRIGHTNESS_COMMAND_TOPIC] = "light/CONF_BRIGHTNESS_COMMAND_TOPIC" @@ -3888,7 +3887,7 @@ async def test_encoding_subscribable_topics_brightness( init_payload: tuple[str, str] | None, ) -> None: """Test handling of incoming encoded payload for a brightness only light.""" - config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][light.DOMAIN]) + config = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][light.DOMAIN]) config[CONF_BRIGHTNESS_COMMAND_TOPIC] = "light/CONF_BRIGHTNESS_COMMAND_TOPIC" await help_test_encoding_subscribable_topics( @@ -3908,7 +3907,7 @@ async def test_encoding_subscribable_topics_brightness( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light_brightness/set", @@ -3962,7 +3961,7 @@ async def test_sending_mqtt_brightness_command_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light_brightness/set", @@ -4017,7 +4016,7 @@ async def test_sending_mqtt_effect_command_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light_hs/set", @@ -4065,7 +4064,7 @@ async def test_sending_mqtt_hs_command_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "name": "test", "command_topic": "test_light_xy/set", diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index 274f8ddf0ad0..1328862de7e1 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -88,7 +88,8 @@ from unittest.mock import call, patch import pytest -from homeassistant.components import light, mqtt +from homeassistant.components import light +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.light.schema_basic import ( MQTT_LIGHT_ATTRIBUTES_BLOCKED, ) @@ -139,7 +140,7 @@ from tests.components.light import common from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -150,7 +151,7 @@ DEFAULT_CONFIG = { COLOR_MODES_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "brightness": True, "effect": True, @@ -262,7 +263,7 @@ async def test_simple_on_off_light( @pytest.mark.parametrize( - "hass_config", [{mqtt.DOMAIN: {light.DOMAIN: {"schema": "json", "name": "test"}}}] + "hass_config", [{DOMAIN: {light.DOMAIN: {"schema": "json", "name": "test"}}}] ) async def test_fail_setup_if_no_command_topic( mqtt_mock_entry: MqttMockHAClientGenerator, @@ -391,7 +392,7 @@ async def test_controlling_state_with_unknown_color_mode( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -447,7 +448,7 @@ async def test_no_color_brightness_color_temp_if_no_topics( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -458,7 +459,7 @@ async def test_no_color_brightness_color_temp_if_no_topics( } }, { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -518,7 +519,7 @@ async def test_brightness_only( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -529,7 +530,7 @@ async def test_brightness_only( } }, { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -570,7 +571,7 @@ async def test_single_color_mode_turn_on( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -580,7 +581,7 @@ async def test_single_color_mode_turn_on( } }, { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -627,7 +628,7 @@ async def test_restore_state_with_none_color_mode( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -687,7 +688,7 @@ async def test_color_temp_only( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -778,7 +779,7 @@ async def test_controlling_state_color_temp_kelvin( [ ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -791,7 +792,7 @@ async def test_controlling_state_color_temp_kelvin( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -806,7 +807,7 @@ async def test_controlling_state_color_temp_kelvin( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -821,7 +822,7 @@ async def test_controlling_state_color_temp_kelvin( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -836,7 +837,7 @@ async def test_controlling_state_color_temp_kelvin( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -1048,7 +1049,7 @@ async def test_controlling_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "command_topic": "test_light_rgb/set", "effect": True, @@ -1296,7 +1297,7 @@ async def test_sending_mqtt_commands_and_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -1361,7 +1362,7 @@ async def test_sending_hs_color( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -1422,7 +1423,7 @@ async def test_sending_rgb_color_no_brightness( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "command_topic": "test_light_rgb/set", "name": "test", @@ -1507,7 +1508,7 @@ async def test_sending_rgb_color_no_brightness2( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -1579,7 +1580,7 @@ async def test_sending_rgb_color_with_brightness( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -1653,7 +1654,7 @@ async def test_sending_rgb_color_with_scaled_brightness( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -1710,7 +1711,7 @@ async def test_sending_scaled_white( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -1774,7 +1775,7 @@ async def test_sending_xy_color( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -1848,7 +1849,7 @@ async def test_effect( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -1931,7 +1932,7 @@ async def test_flash_short_and_long( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -1985,7 +1986,7 @@ async def test_transition( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -2048,7 +2049,7 @@ async def test_brightness_scale( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -2246,7 +2247,7 @@ async def test_update_discovery_with_members_without_init( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -2537,7 +2538,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: [ { "name": "Test 1", @@ -2713,7 +2714,7 @@ async def test_entity_debug_info_message( [ ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -2728,7 +2729,7 @@ async def test_entity_debug_info_message( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -2743,7 +2744,7 @@ async def test_entity_debug_info_message( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -2758,7 +2759,7 @@ async def test_entity_debug_info_message( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "json", "name": "test", @@ -2826,7 +2827,7 @@ async def test_publishing_with_custom_encoding( domain = light.DOMAIN config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG) if topic == "effect_command_topic": - config[mqtt.DOMAIN][domain]["effect_list"] = ["random", "color_loop"] + config[DOMAIN][domain]["effect_list"] = ["random", "color_loop"] await help_test_publishing_with_custom_encoding( hass, @@ -2878,7 +2879,7 @@ async def test_encoding_subscribable_topics( init_payload: tuple[str, str] | None, ) -> None: """Test handling of incoming encoded payload.""" - config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][light.DOMAIN]) + config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][light.DOMAIN]) config["color_mode"] = True config["supported_color_modes"] = [ "color_temp", diff --git a/tests/components/mqtt/test_light_template.py b/tests/components/mqtt/test_light_template.py index 8147a3d2109f..2850f8d8dfd4 100644 --- a/tests/components/mqtt/test_light_template.py +++ b/tests/components/mqtt/test_light_template.py @@ -31,7 +31,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import light, mqtt +from homeassistant.components import light +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.light.schema_basic import ( MQTT_LIGHT_ATTRIBUTES_BLOCKED, ) @@ -81,7 +82,7 @@ from tests.components.light import common from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -96,10 +97,10 @@ DEFAULT_CONFIG = { @pytest.mark.parametrize( "hass_config", [ - ({mqtt.DOMAIN: {light.DOMAIN: {"schema": "template", "name": "test"}}},), + ({DOMAIN: {light.DOMAIN: {"schema": "template", "name": "test"}}},), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -110,7 +111,7 @@ DEFAULT_CONFIG = { ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -122,7 +123,7 @@ DEFAULT_CONFIG = { ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -149,7 +150,7 @@ async def test_setup_fails( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -185,7 +186,7 @@ async def test_rgb_light( [ ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -204,7 +205,7 @@ async def test_rgb_light( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -254,7 +255,7 @@ async def test_single_color_mode( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -309,7 +310,7 @@ async def test_state_change_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -427,7 +428,7 @@ async def test_state_brightness_color_effect_temp_change_via_topic( [ ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -459,7 +460,7 @@ async def test_state_brightness_color_effect_temp_change_via_topic( ), ( { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -626,7 +627,7 @@ async def test_sending_mqtt_commands_and_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -765,7 +766,7 @@ async def test_sending_mqtt_commands_non_optimistic_brightness_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "effect_list": ["rainbow", "colorloop"], @@ -820,7 +821,7 @@ async def test_effect( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -871,7 +872,7 @@ async def test_flash( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -915,7 +916,7 @@ async def test_transition( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -1113,7 +1114,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: [ { "name": "Test 1", @@ -1280,7 +1281,7 @@ async def test_entity_debug_info_message( ) -> None: """Test MQTT debug info.""" config = { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -1304,7 +1305,7 @@ async def test_entity_debug_info_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { light.DOMAIN: { "schema": "template", "name": "test", @@ -1368,7 +1369,7 @@ async def test_publishing_with_custom_encoding( domain = light.DOMAIN config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG) if topic == "effect_command_topic": - config[mqtt.DOMAIN][domain]["effect_list"] = ["random", "color_loop"] + config[DOMAIN][domain]["effect_list"] = ["random", "color_loop"] await help_test_publishing_with_custom_encoding( hass, @@ -1411,7 +1412,7 @@ async def test_encoding_subscribable_topics( init_payload, ) -> None: """Test handling of incoming encoded payload.""" - config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][light.DOMAIN]) + config = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][light.DOMAIN]) config["state_template"] = "{{ value }}" await help_test_encoding_subscribable_topics( hass, diff --git a/tests/components/mqtt/test_lock.py b/tests/components/mqtt/test_lock.py index 27b529152374..b479ac97419f 100644 --- a/tests/components/mqtt/test_lock.py +++ b/tests/components/mqtt/test_lock.py @@ -5,7 +5,7 @@ from unittest.mock import patch import pytest -from homeassistant.components import lock, mqtt +from homeassistant.components import lock from homeassistant.components.lock import ( SERVICE_LOCK, SERVICE_OPEN, @@ -13,6 +13,7 @@ from homeassistant.components.lock import ( LockEntityFeature, LockState, ) +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.lock import MQTT_LOCK_ATTRIBUTES_BLOCKED from homeassistant.const import ( ATTR_ASSUMED_STATE, @@ -58,11 +59,11 @@ from tests.common import async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {lock.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {lock.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } CONFIG_WITH_STATES = { - mqtt.DOMAIN: { + DOMAIN: { lock.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -324,7 +325,7 @@ async def test_controlling_non_default_state_via_topic_and_json_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lock.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -376,7 +377,7 @@ async def test_sending_mqtt_commands_and_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lock.DOMAIN: { "name": "test", "code_format": "^\\d{4}$", @@ -437,7 +438,7 @@ async def test_sending_mqtt_commands_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lock.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -491,7 +492,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lock.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -557,7 +558,7 @@ async def test_sending_mqtt_commands_support_open_and_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lock.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -625,7 +626,7 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lock.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -835,7 +836,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { lock.DOMAIN: [ { "name": "Test 1", @@ -1053,7 +1054,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, lock.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][lock.DOMAIN], + DEFAULT_CONFIG[DOMAIN][lock.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_mixins.py b/tests/components/mqtt/test_mixins.py index 3b9560582fdd..3d55ec8ffe9a 100644 --- a/tests/components/mqtt/test_mixins.py +++ b/tests/components/mqtt/test_mixins.py @@ -6,6 +6,7 @@ from unittest.mock import call, patch import pytest from homeassistant.components import mqtt, sensor +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.sensor import DEFAULT_NAME as DEFAULT_SENSOR_NAME from homeassistant.config_entries import ConfigSubentryData from homeassistant.const import ( @@ -36,7 +37,7 @@ from tests.typing import MqttMockHAClientGenerator "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -104,7 +105,7 @@ async def test_availability_with_shared_state_topic( [ ( # default_entity_name_without_device_name { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "state_topic": "test-topic", "unique_id": "veryunique", @@ -119,7 +120,7 @@ async def test_availability_with_shared_state_topic( ), ( # default_entity_name_with_device_name { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "state_topic": "test-topic", "unique_id": "veryunique", @@ -134,7 +135,7 @@ async def test_availability_with_shared_state_topic( ), ( # name_follows_device_class { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "state_topic": "test-topic", "unique_id": "veryunique", @@ -150,7 +151,7 @@ async def test_availability_with_shared_state_topic( ), ( # name_follows_device_class_without_device_name { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "state_topic": "test-topic", "unique_id": "veryunique", @@ -166,7 +167,7 @@ async def test_availability_with_shared_state_topic( ), ( # name_overrides_device_class { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "MySensor", "state_topic": "test-topic", @@ -183,7 +184,7 @@ async def test_availability_with_shared_state_topic( ), ( # name_set_no_device_name_set { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "MySensor", "state_topic": "test-topic", @@ -200,7 +201,7 @@ async def test_availability_with_shared_state_topic( ), ( # none_entity_name_with_device_name { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": None, "state_topic": "test-topic", @@ -217,7 +218,7 @@ async def test_availability_with_shared_state_topic( ), ( # none_entity_name_without_device_name { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": None, "state_topic": "test-topic", @@ -234,7 +235,7 @@ async def test_availability_with_shared_state_topic( ), ( # entity_name_and_device_name_the_same { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "Hello world", "state_topic": "test-topic", @@ -254,7 +255,7 @@ async def test_availability_with_shared_state_topic( ), ( # entity_name_startswith_device_name1 { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "World automation", "state_topic": "test-topic", @@ -274,7 +275,7 @@ async def test_availability_with_shared_state_topic( ), ( # entity_name_startswith_device_name2 { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "world automation", "state_topic": "test-topic", @@ -328,7 +329,7 @@ async def test_default_entity_and_device_name( await hass.async_block_till_done() entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_PROTOCOL: "5"}, version=mqtt.CONFIG_ENTRY_VERSION, minor_version=mqtt.CONFIG_ENTRY_MINOR_VERSION, @@ -417,7 +418,7 @@ async def test_name_attribute_is_set_or_not( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -427,7 +428,7 @@ async def test_name_attribute_is_set_or_not( } }, { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -439,7 +440,7 @@ async def test_name_attribute_is_set_or_not( } }, { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -473,7 +474,7 @@ async def test_value_template_fails( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -501,7 +502,7 @@ async def test_registry_enabled_by_default( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -647,7 +648,7 @@ async def test_loading_subentries( ) -> None: """Test loading subentries.""" await mqtt_mock_entry() - entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id = next(iter(entry.subentries)) # Each subentry has one device device = device_registry.async_get_device({("mqtt", subentry_id)}) @@ -658,7 +659,7 @@ async def test_loading_subentries( platform = component["platform"] entity_id = f"{platform}.{slugify(device.name)}_{slugify(component['name'])}" entity_entry_entity_id = entity_registry.async_get_entity_id( - platform, mqtt.DOMAIN, f"{subentry_id}_{object_id}" + platform, DOMAIN, f"{subentry_id}_{object_id}" ) assert entity_entry_entity_id == entity_id state = hass.states.get(entity_id) @@ -699,7 +700,7 @@ async def test_loading_subentry_with_bad_component_schema( ) -> None: """Test loading subentries.""" await mqtt_mock_entry() - entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id = next(iter(entry.subentries)) # Each subentry has one device device = device_registry.async_get_device({("mqtt", subentry_id)}) @@ -729,7 +730,7 @@ async def test_qos_on_mqtt_device_from_subentry( ) -> None: """Test QoS is set correctly on entities from MQTT device.""" mqtt_mock = await mqtt_mock_entry() - entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id = next(iter(entry.subentries)) # Each subentry has one device device = device_registry.async_get_device({("mqtt", subentry_id)}) diff --git a/tests/components/mqtt/test_notify.py b/tests/components/mqtt/test_notify.py index 758834994cb6..d424a209b99a 100644 --- a/tests/components/mqtt/test_notify.py +++ b/tests/components/mqtt/test_notify.py @@ -6,7 +6,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt, notify +from homeassistant.components import notify +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.notify import ATTR_MESSAGE from homeassistant.const import ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -41,7 +42,7 @@ from .common import ( from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {notify.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {notify.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } @@ -50,7 +51,7 @@ DEFAULT_CONFIG = { "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { notify.DOMAIN: { "command_topic": "command-topic", "name": "test", @@ -90,7 +91,7 @@ async def test_sending_mqtt_commands( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { notify.DOMAIN: { "command_topic": "command-topic", "command_template": '{ "{{ entity_id }}": "{{ value }}" }', @@ -231,7 +232,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { notify.DOMAIN: [ { "name": "Test 1", @@ -267,8 +268,8 @@ async def test_discovery_update_notify( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered notify.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][notify.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][notify.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][notify.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][notify.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" diff --git a/tests/components/mqtt/test_number.py b/tests/components/mqtt/test_number.py index bc35312a8532..9d573725a341 100644 --- a/tests/components/mqtt/test_number.py +++ b/tests/components/mqtt/test_number.py @@ -6,7 +6,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt, number +from homeassistant.components import number +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.number import ( CONF_MAX, CONF_MIN, @@ -69,7 +70,7 @@ from tests.common import async_fire_mqtt_message, mock_restore_cache_with_extra_ from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {number.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {number.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } @@ -78,7 +79,7 @@ DEFAULT_CONFIG = { [ ( { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -95,7 +96,7 @@ DEFAULT_CONFIG = { ), ( { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -112,7 +113,7 @@ DEFAULT_CONFIG = { ), ( { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -163,7 +164,7 @@ async def test_run_number_setup( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -260,7 +261,7 @@ async def test_native_value_validation( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "name": "test", "command_topic": "test-topic-cmd", @@ -316,7 +317,7 @@ async def test_equivalent_unit_of_measurement( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -368,7 +369,7 @@ async def test_value_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "command_topic": "test/number", "device_class": "temperature", @@ -406,7 +407,7 @@ async def test_restore_native_value( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "command_topic": "test/number", "name": "Test Number", @@ -489,7 +490,7 @@ async def test_run_number_service_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "command_topic": "test/number", "name": "Test Number", @@ -572,7 +573,7 @@ async def test_run_number_service_optimistic_with_command_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "command_topic": "test/number/set", "state_topic": "test/number", @@ -612,7 +613,7 @@ async def test_run_number_service( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "command_topic": "test/number/set", "state_topic": "test/number", @@ -753,7 +754,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: [ { "name": "Test 1", @@ -783,7 +784,7 @@ async def test_discovery_removal_number( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test removal of discovered number.""" - data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][number.DOMAIN]) + data = json.dumps(DEFAULT_CONFIG[DOMAIN][number.DOMAIN]) await help_test_discovery_removal(hass, mqtt_mock_entry, number.DOMAIN, data) @@ -910,7 +911,7 @@ async def test_entity_debug_info_message( [ ( { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -927,7 +928,7 @@ async def test_entity_debug_info_message( ), ( { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -963,7 +964,7 @@ async def test_min_max_step_attributes( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -988,7 +989,7 @@ async def test_invalid_min_max_attributes( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -1013,7 +1014,7 @@ async def test_default_mode( [ ( { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -1026,7 +1027,7 @@ async def test_default_mode( ), ( { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -1039,7 +1040,7 @@ async def test_default_mode( ), ( { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -1069,7 +1070,7 @@ async def test_mode( [ ( { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -1082,7 +1083,7 @@ async def test_mode( ), ( { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -1110,7 +1111,7 @@ async def test_invalid_mode( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -1141,7 +1142,7 @@ async def test_mqtt_payload_not_a_number_warning( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { number.DOMAIN: { "state_topic": "test/state_number", "command_topic": "test/cmd_number", @@ -1242,7 +1243,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, number.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][number.DOMAIN], + DEFAULT_CONFIG[DOMAIN][number.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_repairs.py b/tests/components/mqtt/test_repairs.py index bc7b9dd42945..0b110172ab36 100644 --- a/tests/components/mqtt/test_repairs.py +++ b/tests/components/mqtt/test_repairs.py @@ -7,7 +7,7 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.config_entries import ConfigSubentry, ConfigSubentryData from homeassistant.const import SERVICE_RELOAD from homeassistant.core import HomeAssistant @@ -34,7 +34,7 @@ async def help_setup_yaml(hass: HomeAssistant, config: dict[str, str]) -> None: return_value=parse_yaml(config["yaml"]), ): await hass.services.async_call( - mqtt.DOMAIN, + DOMAIN, SERVICE_RELOAD, {}, blocking=True, @@ -80,7 +80,7 @@ async def test_subentry_reconfigure_export_settings( ) -> None: """Test the subentry ConfigFlow YAML export with migration to YAML.""" await mqtt_mock_entry() - config_entry: MockConfigEntry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id: str subentry: ConfigSubentry subentry_id, subentry = next(iter(config_entry.subentries.items())) @@ -89,7 +89,7 @@ async def test_subentry_reconfigure_export_settings( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device.config_entries_subentries[config_entry.entry_id] == {subentry_id} assert device is not None @@ -135,7 +135,7 @@ async def test_subentry_reconfigure_export_settings( await setup_helper(hass, suggested_values_from_schema) # Assert the subentry device was not effected by the exported configs - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device.config_entries_subentries[config_entry.entry_id] == {subentry_id} assert device is not None @@ -145,13 +145,13 @@ async def test_subentry_reconfigure_export_settings( assert len(events) == 1 issue_id = events[0].data["issue_id"] issue_registry = ir.async_get(hass) - repair_issue = issue_registry.async_get_issue(mqtt.DOMAIN, issue_id) + repair_issue = issue_registry.async_get_issue(DOMAIN, issue_id) assert repair_issue.translation_key == translation_key await async_process_repairs_platforms(hass) client = await hass_client() - data = await start_repair_fix_flow(client, mqtt.DOMAIN, issue_id) + data = await start_repair_fix_flow(client, DOMAIN, issue_id) flow_id = data["flow_id"] assert data["description_placeholders"] == {"name": "Milk notifier"} @@ -161,7 +161,7 @@ async def test_subentry_reconfigure_export_settings( assert data["type"] == "create_entry" # Assert the subentry is removed and no other entity has linked the device - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device is None await hass.async_block_till_done(wait_background_tasks=True) @@ -174,6 +174,6 @@ async def test_subentry_reconfigure_export_settings( # The MQTT device was now set up from the new source await hass.async_block_till_done(wait_background_tasks=True) - device = device_registry.async_get_device(identifiers={(mqtt.DOMAIN, subentry_id)}) + device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) assert device.config_entries_subentries[config_entry.entry_id] == {None} assert device is not None diff --git a/tests/components/mqtt/test_scene.py b/tests/components/mqtt/test_scene.py index 23aa57db1a5e..f04b65498f7b 100644 --- a/tests/components/mqtt/test_scene.py +++ b/tests/components/mqtt/test_scene.py @@ -6,7 +6,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt, scene +from homeassistant.components import scene +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON, STATE_UNKNOWN from homeassistant.core import HomeAssistant, State @@ -41,7 +42,7 @@ from tests.common import mock_restore_cache from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { scene.DOMAIN: { "name": "test", "command_topic": "test-topic", @@ -55,7 +56,7 @@ DEFAULT_CONFIG = { "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { scene.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -110,7 +111,7 @@ async def test_default_availability_payload( ) -> None: """Test availability by default payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { scene.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -134,7 +135,7 @@ async def test_custom_availability_payload( ) -> None: """Test availability by custom payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { scene.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -158,7 +159,7 @@ async def test_custom_availability_payload( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { scene.DOMAIN: [ { "name": "Test 1", @@ -194,8 +195,8 @@ async def test_discovery_update_payload( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered scene.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][scene.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][scene.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][scene.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][scene.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" config1["payload_on"] = "ON" diff --git a/tests/components/mqtt/test_select.py b/tests/components/mqtt/test_select.py index 5936da384735..587bc070f0cc 100644 --- a/tests/components/mqtt/test_select.py +++ b/tests/components/mqtt/test_select.py @@ -9,7 +9,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt, select +from homeassistant.components import select +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.select import MQTT_SELECT_ATTRIBUTES_BLOCKED from homeassistant.components.select import ( ATTR_OPTION, @@ -57,7 +58,7 @@ from tests.common import async_fire_mqtt_message, mock_restore_cache from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { select.DOMAIN: { "name": "test", "command_topic": "test-topic", @@ -70,7 +71,7 @@ DEFAULT_CONFIG = { def _test_run_select_setup_params(topic: str) -> Generator[tuple[ConfigType, str]]: yield ( { - mqtt.DOMAIN: { + DOMAIN: { select.DOMAIN: { "state_topic": topic, "command_topic": "test/select_cmd", @@ -127,7 +128,7 @@ async def test_run_select_setup( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { select.DOMAIN: { "state_topic": "test/select_stat", "command_topic": "test/select_cmd", @@ -171,7 +172,7 @@ async def test_value_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { select.DOMAIN: { "command_topic": "test/select_cmd", "name": "Test Select", @@ -213,7 +214,7 @@ async def test_run_select_service_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { select.DOMAIN: { "command_topic": "test/select_cmd", "name": "Test Select", @@ -256,7 +257,7 @@ async def test_run_select_service_optimistic_with_command_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { select.DOMAIN: { "command_topic": "test/select/set", "state_topic": "test/select", @@ -297,7 +298,7 @@ async def test_run_select_service( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { select.DOMAIN: { "command_topic": "test/select/set", "state_topic": "test/select", @@ -441,7 +442,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { select.DOMAIN: [ { "name": "Test 1", @@ -473,7 +474,7 @@ async def test_discovery_removal_select( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test removal of discovered select.""" - data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][select.DOMAIN]) + data = json.dumps(DEFAULT_CONFIG[DOMAIN][select.DOMAIN]) await help_test_discovery_removal(hass, mqtt_mock_entry, select.DOMAIN, data) @@ -605,7 +606,7 @@ def _test_options_attributes_options_config( for option in request: yield ( { - mqtt.DOMAIN: { + DOMAIN: { select.DOMAIN: { "command_topic": "test/select/set", "state_topic": "test/select", @@ -636,7 +637,7 @@ async def test_options_attributes( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { select.DOMAIN: { "state_topic": "test/select_stat", "command_topic": "test/select_cmd", @@ -690,7 +691,7 @@ async def test_publishing_with_custom_encoding( """Test publishing MQTT payload with different encoding.""" domain = select.DOMAIN config = copy.deepcopy(DEFAULT_CONFIG) - config[mqtt.DOMAIN][domain]["options"] = ["milk", "beer"] + config[DOMAIN][domain]["options"] = ["milk", "beer"] await help_test_publishing_with_custom_encoding( hass, @@ -731,7 +732,7 @@ async def test_encoding_subscribable_topics( attribute_value: Any, ) -> None: """Test handling of incoming encoded payload.""" - config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][select.DOMAIN]) + config = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][select.DOMAIN]) config["options"] = ["milk", "beer"] await help_test_encoding_subscribable_topics( hass, diff --git a/tests/components/mqtt/test_sensor.py b/tests/components/mqtt/test_sensor.py index e2c992d6db20..90a3a339c122 100644 --- a/tests/components/mqtt/test_sensor.py +++ b/tests/components/mqtt/test_sensor.py @@ -13,7 +13,8 @@ from freezegun import freeze_time from freezegun.api import FrozenDateTimeFactory import pytest -from homeassistant.components import mqtt, sensor +from homeassistant.components import sensor +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.sensor import MQTT_SENSOR_ATTRIBUTES_BLOCKED from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, @@ -84,7 +85,7 @@ from tests.common import ( from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {sensor.DOMAIN: {"name": "test", "state_topic": "test-topic"}} + DOMAIN: {sensor.DOMAIN: {"name": "test", "state_topic": "test-topic"}} } @@ -92,7 +93,7 @@ DEFAULT_CONFIG = { "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -122,7 +123,7 @@ async def test_setting_sensor_value_via_mqtt_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -164,7 +165,7 @@ async def test_setting_enum_sensor_value_via_mqtt_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -200,7 +201,7 @@ async def test_handling_undecoded_sensor_value( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -363,7 +364,7 @@ async def test_setting_sensor_native_value_handling_via_mqtt_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -420,7 +421,7 @@ async def test_setting_numeric_sensor_native_value_handling_via_mqtt_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -454,7 +455,7 @@ async def test_setting_sensor_value_expires_availability_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -550,7 +551,7 @@ async def expires_helper(hass: HomeAssistant) -> None: "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -582,7 +583,7 @@ async def test_setting_sensor_value_via_mqtt_json_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -617,7 +618,7 @@ async def test_setting_sensor_value_via_mqtt_json_message_and_default_current_st "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_class": "total", @@ -667,7 +668,7 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_class": "total", @@ -705,7 +706,7 @@ async def test_setting_sensor_last_reset_via_mqtt_json_message_2( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -742,7 +743,7 @@ async def test_force_update_disabled( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -865,7 +866,7 @@ async def test_discovery_update_availability( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -887,7 +888,7 @@ async def test_invalid_device_class( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -912,7 +913,7 @@ async def test_invalid_unit_of_measurement( ) # A repair issue was logged for the failing YAML config assert len(events) == 1 - assert events[0].data["domain"] == mqtt.DOMAIN + assert events[0].data["domain"] == DOMAIN # Assert the sensor is not created state = hass.states.get("sensor.test") assert state is None @@ -923,7 +924,7 @@ async def test_invalid_unit_of_measurement( [ pytest.param( { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -939,7 +940,7 @@ async def test_invalid_unit_of_measurement( ), pytest.param( { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -955,7 +956,7 @@ async def test_invalid_unit_of_measurement( ), pytest.param( { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -971,7 +972,7 @@ async def test_invalid_unit_of_measurement( ), pytest.param( { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -987,7 +988,7 @@ async def test_invalid_unit_of_measurement( ), pytest.param( { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -1003,7 +1004,7 @@ async def test_invalid_unit_of_measurement( ), pytest.param( { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -1061,7 +1062,7 @@ async def test_device_class_with_equivalent_unit_of_measurement_received( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -1115,7 +1116,7 @@ async def test_equivalent_unit_of_measurement_received_without_device_class( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: [ { "name": "Test 1", @@ -1191,7 +1192,7 @@ async def test_valid_device_class_and_uom( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -1213,7 +1214,7 @@ async def test_invalid_state_class( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -1240,7 +1241,7 @@ async def test_invalid_state_class_with_unit_of_measurement( [ ( { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -1254,7 +1255,7 @@ async def test_invalid_state_class_with_unit_of_measurement( ), ( { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -1268,7 +1269,7 @@ async def test_invalid_state_class_with_unit_of_measurement( ), ( { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -1294,7 +1295,7 @@ async def test_invalid_options_config( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: [ { "name": "Test 1", @@ -1402,7 +1403,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: [ { "name": "Test 1", @@ -1678,7 +1679,7 @@ async def test_entity_category( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { sensor.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -1838,7 +1839,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, sensor.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][sensor.DOMAIN], + DEFAULT_CONFIG[DOMAIN][sensor.DOMAIN], topic, value, attribute, @@ -2023,7 +2024,7 @@ async def test_value_incorrect_state_class_config( ) caplog.clear() - config_payload = hass_config[mqtt.DOMAIN][sensor.DOMAIN][0] + config_payload = hass_config[DOMAIN][sensor.DOMAIN][0] async_fire_mqtt_message( hass, "homeassistant/sensor/bla/config", json.dumps(config_payload) ) diff --git a/tests/components/mqtt/test_siren.py b/tests/components/mqtt/test_siren.py index 8eca67bbd9f8..7363277ed6f8 100644 --- a/tests/components/mqtt/test_siren.py +++ b/tests/components/mqtt/test_siren.py @@ -6,7 +6,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt, siren +from homeassistant.components import siren +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.siren import ATTR_VOLUME_LEVEL from homeassistant.const import ( ATTR_ASSUMED_STATE, @@ -55,7 +56,7 @@ from tests.common import async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {siren.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {siren.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } @@ -82,7 +83,7 @@ async def async_turn_off( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { siren.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -119,7 +120,7 @@ async def test_controlling_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { siren.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -164,7 +165,7 @@ async def test_sending_mqtt_commands_and_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { siren.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -205,7 +206,7 @@ async def test_controlling_state_via_topic_and_json_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { siren.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -292,7 +293,7 @@ async def test_controlling_state_and_attributes_with_json_message_without_templa help_custom_config( siren.DOMAIN, { - mqtt.DOMAIN: { + DOMAIN: { siren.DOMAIN: { "command_topic": "command-topic", } @@ -387,7 +388,7 @@ async def test_filtering_not_supported_attributes_optimistic( help_custom_config( siren.DOMAIN, { - mqtt.DOMAIN: { + DOMAIN: { siren.DOMAIN: { "command_topic": "command-topic", } @@ -495,7 +496,7 @@ async def test_default_availability_payload( ) -> None: """Test availability by default payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { siren.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -521,7 +522,7 @@ async def test_custom_availability_payload( ) -> None: """Test availability by custom payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { siren.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -547,7 +548,7 @@ async def test_custom_availability_payload( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { siren.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -644,7 +645,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { siren.DOMAIN: [ { "name": "Test 1", @@ -686,8 +687,8 @@ async def test_discovery_update_siren_topic_template( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered siren.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][siren.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][siren.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" config1["state_topic"] = "siren/state1" @@ -722,8 +723,8 @@ async def test_discovery_update_siren_template( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered siren.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][siren.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][siren.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" config1["state_topic"] = "siren/state1" @@ -987,7 +988,7 @@ async def test_publishing_with_custom_encoding( """Test publishing MQTT payload with command templates and different encoding.""" domain = siren.DOMAIN config: dict[str, Any] = copy.deepcopy(DEFAULT_CONFIG) - config[mqtt.DOMAIN][domain][siren.ATTR_AVAILABLE_TONES] = ["siren", "xylophone"] + config[DOMAIN][domain][siren.ATTR_AVAILABLE_TONES] = ["siren", "xylophone"] await help_test_publishing_with_custom_encoding( hass, @@ -1029,7 +1030,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, siren.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][siren.DOMAIN], + DEFAULT_CONFIG[DOMAIN][siren.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_switch.py b/tests/components/mqtt/test_switch.py index e2474363ea48..6bfdfdf2ad0e 100644 --- a/tests/components/mqtt/test_switch.py +++ b/tests/components/mqtt/test_switch.py @@ -6,7 +6,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt, switch +from homeassistant.components import switch +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import ( ATTR_ASSUMED_STATE, ATTR_DEVICE_CLASS, @@ -52,7 +53,7 @@ from tests.components.switch import common from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {switch.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {switch.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } @@ -61,7 +62,7 @@ DEFAULT_CONFIG = { [ ( { - mqtt.DOMAIN: { + DOMAIN: { switch.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -76,7 +77,7 @@ DEFAULT_CONFIG = { ), ( { - mqtt.DOMAIN: { + DOMAIN: { switch.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -124,7 +125,7 @@ async def test_controlling_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { switch.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -171,7 +172,7 @@ async def test_sending_mqtt_commands_and_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { switch.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -195,7 +196,7 @@ async def test_sending_inital_state_and_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { switch.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -239,7 +240,7 @@ async def test_sending_mqtt_commands_with_command_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { switch.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -302,7 +303,7 @@ async def test_default_availability_payload( ) -> None: """Test availability by default payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { switch.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -328,7 +329,7 @@ async def test_custom_availability_payload( ) -> None: """Test availability by custom payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { switch.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -354,7 +355,7 @@ async def test_custom_availability_payload( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { switch.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -451,7 +452,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { switch.DOMAIN: [ { "name": "Test 1", @@ -493,8 +494,8 @@ async def test_discovery_update_switch_topic_template( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered switch.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][switch.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][switch.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" config1["state_topic"] = "switch/state1" @@ -529,8 +530,8 @@ async def test_discovery_update_switch_template( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test update of discovered switch.""" - config1 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN]) - config2 = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN]) + config1 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][switch.DOMAIN]) + config2 = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][switch.DOMAIN]) config1["name"] = "Beer" config2["name"] = "Milk" config1["state_topic"] = "switch/state1" @@ -737,7 +738,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, switch.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][switch.DOMAIN], + DEFAULT_CONFIG[DOMAIN][switch.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_text.py b/tests/components/mqtt/test_text.py index 637a27c463ec..0276ebf6955e 100644 --- a/tests/components/mqtt/test_text.py +++ b/tests/components/mqtt/test_text.py @@ -5,7 +5,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt, text +from homeassistant.components import text +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -44,7 +45,7 @@ from tests.common import async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {text.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {text.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } @@ -64,7 +65,7 @@ async def async_set_value( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -106,7 +107,7 @@ async def test_controlling_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -163,7 +164,7 @@ async def test_forced_text_length( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -237,7 +238,7 @@ async def test_controlling_validation_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -260,7 +261,7 @@ async def test_attribute_validation_max_greater_then_min( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -283,7 +284,7 @@ async def test_attribute_validation_max_not_greater_then_max_state_length( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -313,7 +314,7 @@ async def test_validation_payload_greater_then_max_state_length( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -356,7 +357,7 @@ async def test_sending_mqtt_commands_and_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -421,7 +422,7 @@ async def test_default_availability_payload( ) -> None: """Test availability by default payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -439,7 +440,7 @@ async def test_custom_availability_payload( ) -> None: """Test availability by custom payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -515,7 +516,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { text.DOMAIN: [ { "name": "Test 1", @@ -758,7 +759,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, text.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][text.DOMAIN], + DEFAULT_CONFIG[DOMAIN][text.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_time.py b/tests/components/mqtt/test_time.py index 728bd202eaff..aa2a3d688e54 100644 --- a/tests/components/mqtt/test_time.py +++ b/tests/components/mqtt/test_time.py @@ -6,7 +6,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt, time +from homeassistant.components import time +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.const import ATTR_ASSUMED_STATE, ATTR_ENTITY_ID, STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -44,7 +45,7 @@ from tests.common import async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: {time.DOMAIN: {"name": "test", "command_topic": "test-topic"}} + DOMAIN: {time.DOMAIN: {"name": "test", "command_topic": "test-topic"}} } @@ -64,7 +65,7 @@ async def async_set_value( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { time.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -116,7 +117,7 @@ async def test_controlling_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { time.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -155,7 +156,7 @@ async def test_controlling_validation_state_via_topic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { time.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -221,7 +222,7 @@ async def test_default_availability_payload( ) -> None: """Test availability by default payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { time.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -239,7 +240,7 @@ async def test_custom_availability_payload( ) -> None: """Test availability by custom payload with defined topic.""" config = { - mqtt.DOMAIN: { + DOMAIN: { time.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -315,7 +316,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { time.DOMAIN: [ { "name": "Test 1", @@ -494,7 +495,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, time.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][time.DOMAIN], + DEFAULT_CONFIG[DOMAIN][time.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_update.py b/tests/components/mqtt/test_update.py index 1092d056cb54..f22595e77cae 100644 --- a/tests/components/mqtt/test_update.py +++ b/tests/components/mqtt/test_update.py @@ -6,7 +6,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt, update +from homeassistant.components import update +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.update import DOMAIN as UPDATE_DOMAIN, SERVICE_INSTALL from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNKNOWN from homeassistant.core import HomeAssistant @@ -42,7 +43,7 @@ from tests.common import async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "name": "test", "state_topic": "test-topic", @@ -59,7 +60,7 @@ DEFAULT_CONFIG = { [ ( { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/installed-version", "latest_version_topic": "test/latest-version", @@ -76,7 +77,7 @@ DEFAULT_CONFIG = { ), ( { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/installed-version", "latest_version_topic": "test/latest-version", @@ -132,7 +133,7 @@ async def test_run_update_setup( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/installed-version", "latest_version_topic": "test/latest-version", @@ -182,7 +183,7 @@ async def test_run_update_setup_float( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/installed-version", "value_template": "{{ value_json.installed }}", @@ -227,7 +228,7 @@ async def test_value_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/update", "value_template": ( @@ -298,7 +299,7 @@ async def test_errornous_value_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/installed-version", "value_template": "{{ value_json.installed }}", @@ -343,7 +344,7 @@ async def test_value_template_float( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/state-topic", "name": "Test Update", @@ -371,7 +372,7 @@ async def test_empty_json_state_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/state-topic", "name": "Test Update", @@ -424,7 +425,7 @@ async def test_invalid_json_state_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/state-topic", "name": "Test Update", @@ -520,7 +521,7 @@ async def test_json_state_message( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/state-topic", "value_template": '{{ {"installed_version": value_json.installed, ' @@ -561,7 +562,7 @@ async def test_json_state_message_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: { "state_topic": "test/installed-version", "latest_version_topic": "test/latest-version", @@ -695,7 +696,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { update.DOMAIN: [ { "name": "Bear", @@ -725,7 +726,7 @@ async def test_discovery_removal_update( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test removal of discovered update.""" - data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][update.DOMAIN]) + data = json.dumps(DEFAULT_CONFIG[DOMAIN][update.DOMAIN]) await help_test_discovery_removal(hass, mqtt_mock_entry, update.DOMAIN, data) diff --git a/tests/components/mqtt/test_util.py b/tests/components/mqtt/test_util.py index bbe9edb3852d..5eb2d5392c15 100644 --- a/tests/components/mqtt/test_util.py +++ b/tests/components/mqtt/test_util.py @@ -11,6 +11,7 @@ from unittest.mock import MagicMock, patch import pytest from homeassistant.components import mqtt +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.models import MessageCallbackType from homeassistant.components.mqtt.util import EnsureJobAfterCooldown from homeassistant.config_entries import ConfigEntryDisabler, ConfigEntryState @@ -222,7 +223,7 @@ async def test_waiting_for_client_not_loaded( await hass.async_block_till_done() entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={"broker": "test-broker"}, state=ConfigEntryState.NOT_LOADED, version=mqtt.CONFIG_ENTRY_VERSION, @@ -263,7 +264,7 @@ async def test_waiting_for_client_loaded( assert await mqtt.async_wait_for_mqtt_client(hass) unsub = await mqtt.async_subscribe(hass, "test_topic", lambda msg: None) - entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + entry = hass.config_entries.async_entries(DOMAIN)[0] assert entry.state is ConfigEntryState.LOADED await _async_just_in_time_subscribe() @@ -281,7 +282,7 @@ async def test_waiting_for_client_entry_fails( await hass.async_block_till_done() entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={"broker": "test-broker"}, state=ConfigEntryState.NOT_LOADED, version=mqtt.CONFIG_ENTRY_VERSION, @@ -311,7 +312,7 @@ async def test_waiting_for_client_setup_fails( await hass.async_block_till_done() entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={"broker": "test-broker"}, state=ConfigEntryState.NOT_LOADED, version=mqtt.CONFIG_ENTRY_VERSION, @@ -340,7 +341,7 @@ async def test_waiting_for_client_timeout( await hass.async_block_till_done() entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={"broker": "test-broker"}, state=ConfigEntryState.NOT_LOADED, version=mqtt.CONFIG_ENTRY_VERSION, @@ -361,7 +362,7 @@ async def test_waiting_for_client_with_disabled_entry( await hass.async_block_till_done() entry = MockConfigEntry( - domain=mqtt.DOMAIN, + domain=DOMAIN, data={"broker": "test-broker"}, state=ConfigEntryState.NOT_LOADED, version=mqtt.CONFIG_ENTRY_VERSION, diff --git a/tests/components/mqtt/test_vacuum.py b/tests/components/mqtt/test_vacuum.py index bd3d97c2269b..7863e27be641 100644 --- a/tests/components/mqtt/test_vacuum.py +++ b/tests/components/mqtt/test_vacuum.py @@ -7,9 +7,13 @@ from unittest.mock import call, patch import pytest -from homeassistant.components import mqtt, vacuum +from homeassistant.components import vacuum from homeassistant.components.mqtt import vacuum as mqttvacuum -from homeassistant.components.mqtt.const import CONF_COMMAND_TOPIC, CONF_STATE_TOPIC +from homeassistant.components.mqtt.const import ( + CONF_COMMAND_TOPIC, + CONF_STATE_TOPIC, + DOMAIN, +) from homeassistant.components.mqtt.vacuum import ( ALL_SERVICES, MQTT_VACUUM_ATTRIBUTES_BLOCKED, @@ -80,7 +84,7 @@ SEND_COMMAND_TOPIC = "vacuum/send_command" STATE_TOPIC = "vacuum/state" DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { vacuum.DOMAIN: { CONF_NAME: "mqtttest", CONF_COMMAND_TOPIC: COMMAND_TOPIC, @@ -93,7 +97,7 @@ DEFAULT_CONFIG = { } CONFIG_CLEAN_SEGMENTS = { - mqtt.DOMAIN: { + DOMAIN: { vacuum.DOMAIN: { CONF_NAME: "test", CONF_STATE_TOPIC: STATE_TOPIC, @@ -103,7 +107,7 @@ CONFIG_CLEAN_SEGMENTS = { } } -DEFAULT_CONFIG_2 = {mqtt.DOMAIN: {vacuum.DOMAIN: {"name": "test"}}} +DEFAULT_CONFIG_2 = {DOMAIN: {vacuum.DOMAIN: {"name": "test"}}} CONFIG_ALL_SERVICES = help_custom_config( vacuum.DOMAIN, @@ -363,10 +367,10 @@ async def test_clean_segments_command( mqtt_mock_entry: MqttMockHAClientGenerator, ) -> None: """Test cleaning segments and repair flow.""" - config_entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + config_entry = hass.config_entries.async_entries(DOMAIN)[0] entity_registry.async_get_or_create( vacuum.DOMAIN, - mqtt.DOMAIN, + DOMAIN, "veryunique", config_entry=config_entry, suggested_object_id="test", @@ -549,7 +553,7 @@ async def test_removing_clean_segments_command_topic_resets_feature( """ await mqtt_mock_entry() - config_with_clean_segments_command_topic = CONFIG_CLEAN_SEGMENTS[mqtt.DOMAIN][ + config_with_clean_segments_command_topic = CONFIG_CLEAN_SEGMENTS[DOMAIN][ vacuum.DOMAIN ] async_fire_mqtt_message( @@ -617,7 +621,7 @@ async def test_clean_area_feature_preserved_on_config_update( """ await mqtt_mock_entry() - config = CONFIG_CLEAN_SEGMENTS[mqtt.DOMAIN][vacuum.DOMAIN] + config = CONFIG_CLEAN_SEGMENTS[DOMAIN][vacuum.DOMAIN] async_fire_mqtt_message( hass, "homeassistant/vacuum/bla/config", @@ -865,7 +869,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { vacuum.DOMAIN: [ { "name": "Test 1", @@ -1036,7 +1040,7 @@ async def test_publishing_with_custom_encoding( """Test publishing MQTT payload with different encoding.""" domain = vacuum.DOMAIN config = deepcopy(DEFAULT_CONFIG) - config[mqtt.DOMAIN][domain]["supported_features"] = [ + config[DOMAIN][domain]["supported_features"] = [ "clean_spot", "fan_speed", "locate", @@ -1101,7 +1105,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, vacuum.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][vacuum.DOMAIN], + DEFAULT_CONFIG[DOMAIN][vacuum.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_valve.py b/tests/components/mqtt/test_valve.py index 9b19f391cd9f..49b9ef788240 100644 --- a/tests/components/mqtt/test_valve.py +++ b/tests/components/mqtt/test_valve.py @@ -5,7 +5,8 @@ from unittest.mock import patch import pytest -from homeassistant.components import mqtt, valve +from homeassistant.components import valve +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.valve import ( MQTT_VALVE_ATTRIBUTES_BLOCKED, ValveEntityFeature, @@ -62,7 +63,7 @@ from tests.common import async_fire_mqtt_message from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "command_topic": "command-topic", "state_topic": "test-topic", @@ -72,7 +73,7 @@ DEFAULT_CONFIG = { } DEFAULT_CONFIG_REPORTS_POSITION = { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -87,7 +88,7 @@ DEFAULT_CONFIG_REPORTS_POSITION = { "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -138,7 +139,7 @@ async def test_state_via_state_topic_no_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -181,7 +182,7 @@ async def test_state_via_state_topic_with_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -227,7 +228,7 @@ async def test_state_via_state_topic_with_position_template( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -292,7 +293,7 @@ async def test_state_via_state_topic_through_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -347,7 +348,7 @@ async def test_opening_closing_state_is_reset( [ ( { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -361,7 +362,7 @@ async def test_opening_closing_state_is_reset( ), ( { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -402,7 +403,7 @@ async def test_invalid_state_updates( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -464,7 +465,7 @@ async def test_state_via_state_trough_position_with_alt_range( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -611,7 +612,7 @@ async def test_open_close_payload_config_not_allowed( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -622,7 +623,7 @@ async def test_open_close_payload_config_not_allowed( } }, { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -671,7 +672,7 @@ async def test_controlling_valve_by_state_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -722,7 +723,7 @@ async def test_controlling_valve_by_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -769,7 +770,7 @@ async def test_controlling_valve_by_set_valve_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -824,7 +825,7 @@ async def test_controlling_valve_optimistic_by_set_valve_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -873,7 +874,7 @@ async def test_controlling_valve_with_alt_range_by_set_valve_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -924,7 +925,7 @@ async def test_controlling_valve_with_alt_range_by_position( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -936,7 +937,7 @@ async def test_controlling_valve_with_alt_range_by_position( } }, { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "command_topic": "command-topic", @@ -989,7 +990,7 @@ async def test_controlling_valve_by_position_optimistic( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "state_topic": "state-topic", @@ -1085,7 +1086,7 @@ async def test_custom_availability_payload( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "device_class": "water", @@ -1109,7 +1110,7 @@ async def test_valid_device_class( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: { "name": "test", "device_class": "abc123", @@ -1193,7 +1194,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { valve.DOMAIN: [ { "name": "Test 1", @@ -1396,7 +1397,7 @@ async def test_encoding_subscribable_topics( hass, mqtt_mock_entry, valve.DOMAIN, - DEFAULT_CONFIG[mqtt.DOMAIN][valve.DOMAIN], + DEFAULT_CONFIG[DOMAIN][valve.DOMAIN], topic, value, attribute, diff --git a/tests/components/mqtt/test_water_heater.py b/tests/components/mqtt/test_water_heater.py index 3c6c4a50e4d1..4b72fab68b4b 100644 --- a/tests/components/mqtt/test_water_heater.py +++ b/tests/components/mqtt/test_water_heater.py @@ -8,7 +8,8 @@ from unittest.mock import call, patch import pytest import voluptuous as vol -from homeassistant.components import mqtt, water_heater +from homeassistant.components import water_heater +from homeassistant.components.mqtt.const import DOMAIN from homeassistant.components.mqtt.water_heater import ( MQTT_WATER_HEATER_ATTRIBUTES_BLOCKED, ) @@ -96,7 +97,7 @@ _DEFAULT_MAX_TEMP_CELSIUS = round( DEFAULT_CONFIG = { - mqtt.DOMAIN: { + DOMAIN: { water_heater.DOMAIN: { "name": "test", "mode_command_topic": "mode-topic", @@ -553,7 +554,7 @@ async def test_custom_availability_payload( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { water_heater.DOMAIN: { "name": "test", "mode_command_topic": "mode-topic", @@ -618,7 +619,7 @@ async def test_get_with_templates( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { water_heater.DOMAIN: { "name": "test", "mode_command_topic": "mode-topic", @@ -867,7 +868,7 @@ async def test_discovery_update_attr( "hass_config", [ { - mqtt.DOMAIN: { + DOMAIN: { water_heater.DOMAIN: [ { "name": "Test 1", @@ -910,7 +911,7 @@ async def test_encoding_subscribable_topics( attribute_value: Any, ) -> None: """Test handling of incoming encoded payload.""" - config = copy.deepcopy(DEFAULT_CONFIG[mqtt.DOMAIN][water_heater.DOMAIN]) + config = copy.deepcopy(DEFAULT_CONFIG[DOMAIN][water_heater.DOMAIN]) await help_test_encoding_subscribable_topics( hass, mqtt_mock_entry, @@ -927,7 +928,7 @@ async def test_discovery_removal_water_heater( hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator ) -> None: """Test removal of discovered water heater.""" - data = json.dumps(DEFAULT_CONFIG[mqtt.DOMAIN][water_heater.DOMAIN]) + data = json.dumps(DEFAULT_CONFIG[DOMAIN][water_heater.DOMAIN]) await help_test_discovery_removal(hass, mqtt_mock_entry, water_heater.DOMAIN, data) @@ -1008,7 +1009,7 @@ async def test_entity_id_update_subscriptions( ) -> None: """Test MQTT subscriptions are managed when entity_id is updated.""" config = { - mqtt.DOMAIN: { + DOMAIN: { water_heater.DOMAIN: { "name": "test", "mode_state_topic": "test-topic", @@ -1035,7 +1036,7 @@ async def test_entity_debug_info_message( ) -> None: """Test MQTT debug info.""" config = { - mqtt.DOMAIN: { + DOMAIN: { water_heater.DOMAIN: { "name": "test", "mode_command_topic": "command-topic", diff --git a/tests/components/philips_js/__init__.py b/tests/components/philips_js/__init__.py index 09ad61eb99e0..453f4febaab8 100644 --- a/tests/components/philips_js/__init__.py +++ b/tests/components/philips_js/__init__.py @@ -57,6 +57,7 @@ MOCK_SYSTEM_UNPAIRED = { MOCK_USERINPUT = { "host": "1.1.1.1", + "api_version": "1", } MOCK_CONFIG = { diff --git a/tests/components/plugwise/test_climate.py b/tests/components/plugwise/test_climate.py index 8d7e15fc404b..357ea12ab492 100644 --- a/tests/components/plugwise/test_climate.py +++ b/tests/components/plugwise/test_climate.py @@ -9,6 +9,7 @@ import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.components.climate import ( + ATTR_CURRENT_TEMPERATURE, ATTR_HVAC_ACTION, ATTR_HVAC_MODE, ATTR_HVAC_MODES, @@ -24,7 +25,13 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.components.plugwise.climate import PlugwiseClimateExtraStoredData -from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_OFF, STATE_ON +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_TEMPERATURE, + STATE_OFF, + STATE_ON, + STATE_UNAVAILABLE, +) from homeassistant.core import HomeAssistant, State from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.helpers import entity_registry as er @@ -680,3 +687,22 @@ async def test_anna_p1_climate_snapshot( ) -> None: """Test Anna P1 climate snapshot.""" await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id) + + +@pytest.mark.parametrize("chosen_env", ["m_adam_cooling"], indirect=True) +@pytest.mark.parametrize("cooling_present", [False], indirect=True) +async def test_tom_without_temperature_measurement( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_smile_adam_heat_cool: MagicMock, +) -> None: + """Test Tom without temperature measurement.""" + data = mock_smile_adam_heat_cool.async_update.return_value + del data["f871b8c4d63549319221e294e4f88074"]["sensors"]["temperature"] + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert (state := hass.states.get("climate.bathroom")) is not None + assert state.state != STATE_UNAVAILABLE + assert state.attributes[ATTR_CURRENT_TEMPERATURE] is None diff --git a/tests/components/reolink/test_host.py b/tests/components/reolink/test_host.py index bd8ea3eec089..475dc1655587 100644 --- a/tests/components/reolink/test_host.py +++ b/tests/components/reolink/test_host.py @@ -301,6 +301,54 @@ async def test_ONVIF_not_supported( assert config_entry.state is ConfigEntryState.LOADED +async def test_immediate_fast_polling_ONVIF_not_supported( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + config_entry: MockConfigEntry, + reolink_host: MagicMock, +) -> None: + """Test immediate fast polling if ONVIF not supported.""" + + def test_supported(ch, key): + """Test supported function.""" + if key == "ONVIF": + return False + return True + + reolink_host.supported = test_supported + + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert config_entry.state is ConfigEntryState.LOADED + + freezer.tick(timedelta(seconds=FIRST_TCP_PUSH_TIMEOUT)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + # ONVIF push and long poll subscription not called + assert not reolink_host.subscribe.called + # Fast polling called + assert reolink_host.get_motion_state_all_ch.called + + # test fast polling paused when privacy mode activated + reolink_host.baichuan.privacy_mode.return_value = True + reolink_host.get_motion_state_all_ch.reset_mock() + assert not reolink_host.get_motion_state_all_ch.called + freezer.tick(timedelta(seconds=POLL_INTERVAL_NO_PUSH)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert not reolink_host.get_motion_state_all_ch.called + + # test fast polling resumes when privacy mode deactivated + reolink_host.baichuan.privacy_mode.return_value = False + freezer.tick(timedelta(seconds=POLL_INTERVAL_NO_PUSH)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert reolink_host.get_motion_state_all_ch.called + + async def test_renew( hass: HomeAssistant, freezer: FrozenDateTimeFactory, diff --git a/tests/components/shelly/test_init.py b/tests/components/shelly/test_init.py index 0eaa3192ca10..a2c567299fdd 100644 --- a/tests/components/shelly/test_init.py +++ b/tests/components/shelly/test_init.py @@ -1,5 +1,6 @@ """Test cases for the Shelly component.""" +import asyncio from ipaddress import IPv4Address from typing import Any from unittest.mock import AsyncMock, Mock, call, patch @@ -769,3 +770,108 @@ async def test_empty_device_removal( # verify that the empty sub-device is removed assert device_registry.async_get(sub_device_entry.id) is None + + +async def test_rpc_waits_for_ble_scanner_at_startup( + hass: HomeAssistant, + mock_rpc_device: Mock, +) -> None: + """Test setup waits for the bluetooth scanner to be registered.""" + connect_event = asyncio.Event() + reached_connect = asyncio.Event() + + async def _block_until_released(*args: Any, **kwargs: Any) -> Mock: + reached_connect.set() + await connect_event.wait() + return Mock() + + entry = await init_integration( + hass, + 2, + options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE}, + skip_setup=True, + ) + + with patch( + "homeassistant.components.shelly.coordinator.async_connect_scanner", + _block_until_released, + ): + setup_task = hass.async_create_task( + hass.config_entries.async_setup(entry.entry_id) + ) + async with asyncio.timeout(2): + await reached_connect.wait() + + # Setup must still be waiting for the scanner to be registered. + assert not setup_task.done() + + connect_event.set() + async with asyncio.timeout(2): + assert await setup_task is True + + await hass.async_block_till_done() + assert entry.state is ConfigEntryState.LOADED + + +async def test_rpc_ble_scanner_startup_wait_times_out( + hass: HomeAssistant, + mock_rpc_device: Mock, +) -> None: + """Test setup finishes if the bluetooth scanner never registers.""" + connect_event = asyncio.Event() + + async def _never_returns(*args: Any, **kwargs: Any) -> Mock: + await connect_event.wait() + return Mock() + + entry = await init_integration( + hass, + 2, + options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE}, + skip_setup=True, + ) + + with ( + patch( + "homeassistant.components.shelly.coordinator.async_connect_scanner", + _never_returns, + ), + patch("homeassistant.components.shelly.STARTUP_SCANNER_WAIT", 0.05), + ): + async with asyncio.timeout(5): + assert await hass.config_entries.async_setup(entry.entry_id) is True + + assert entry.state is ConfigEntryState.LOADED + connect_event.set() + await hass.async_block_till_done() + + +async def test_rpc_disabled_ble_scanner_does_not_wait_at_startup( + hass: HomeAssistant, + mock_rpc_device: Mock, +) -> None: + """Test setup does not wait when the BLE scanner is disabled.""" + block_event = asyncio.Event() + + async def _block_forever(self: Any) -> None: + await block_event.wait() + + entry = await init_integration( + hass, + 2, + options={CONF_BLE_SCANNER_MODE: BLEScannerMode.DISABLED}, + skip_setup=True, + ) + + with patch( + "homeassistant.components.shelly.coordinator.ShellyRpcCoordinator" + "._async_connect_ble_scanner", + _block_forever, + ): + # Scanner setup is blocked, but a disabled proxy must not wait for it. + async with asyncio.timeout(2): + assert await hass.config_entries.async_setup(entry.entry_id) is True + + assert entry.state is ConfigEntryState.LOADED + block_event.set() + await hass.async_block_till_done() diff --git a/tests/components/shelly/test_sensor.py b/tests/components/shelly/test_sensor.py index ed394d349b84..85212e9e3fb5 100644 --- a/tests/components/shelly/test_sensor.py +++ b/tests/components/shelly/test_sensor.py @@ -1390,6 +1390,32 @@ async def test_rpc_device_virtual_number_sensor( assert state.state == "56.7" +async def test_rpc_device_virtual_number_sensor_no_unit( + hass: HomeAssistant, + mock_rpc_device: Mock, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test a virtual number sensor with no unit key in meta.ui.""" + config = deepcopy(mock_rpc_device.config) + config["number:203"] = { + "name": "Virtual number sensor", + "min": 0, + "max": 100, + "meta": {"ui": {"step": 0.1, "view": "label"}}, + } + monkeypatch.setattr(mock_rpc_device, "config", config) + + status = deepcopy(mock_rpc_device.status) + status["number:203"] = {"value": 34.5} + monkeypatch.setattr(mock_rpc_device, "status", status) + + await init_integration(hass, 3) + + assert (state := hass.states.get("sensor.test_name_virtual_number_sensor")) + assert state.state == "34.5" + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None + + @pytest.mark.usefixtures("disable_async_remove_shelly_rpc_entities") async def test_rpc_remove_number_virtual_sensor_when_mode_field( hass: HomeAssistant, diff --git a/tests/components/wyoming/test_satellite.py b/tests/components/wyoming/test_satellite.py index 1c7d191137ab..6acdd6f854cd 100644 --- a/tests/components/wyoming/test_satellite.py +++ b/tests/components/wyoming/test_satellite.py @@ -1670,6 +1670,28 @@ async def test_announce( mock_proc = MagicMock() mock_proc.stdout.read = AsyncMock(side_effect=[pcm_audio, b""]) + async def create_subprocess_exec(*args, **kwargs): + # Verify ffmpeg is called with a list of allowed protocols before -i + protocol_arg_idx: int | None = None + input_arg_idx: int | None = None + for arg_idx, arg in enumerate(args): + if arg == "-protocol_whitelist": + protocol_arg_idx = arg_idx + elif arg == "-i": + input_arg_idx = arg_idx + + assert protocol_arg_idx is not None + assert input_arg_idx is not None + assert protocol_arg_idx < input_arg_idx, ( + "-protocol_whitelist must appear before -i" + ) + assert (protocol_arg_idx + 1) < len(args) + + allowed_protocols = set(args[protocol_arg_idx + 1].split(",")) + assert allowed_protocols == {"file", "http", "https", "tcp", "tls"} + + return mock_proc + with ( patch( "homeassistant.components.wyoming.data.load_wyoming_info", @@ -1683,10 +1705,7 @@ async def test_announce( "homeassistant.components.assist_satellite.entity.async_process_play_media_url", new=async_process_play_media_url, ), - patch( - "asyncio.create_subprocess_exec", - return_value=mock_proc, - ), + patch("asyncio.create_subprocess_exec", new=create_subprocess_exec), ): entry = await setup_config_entry(hass) device: SatelliteDevice = entry.runtime_data.device diff --git a/tests/components/zinvolt/fixtures/current_state_offline.json b/tests/components/zinvolt/fixtures/current_state_offline.json new file mode 100644 index 000000000000..cda7fb6bdb92 --- /dev/null +++ b/tests/components/zinvolt/fixtures/current_state_offline.json @@ -0,0 +1,35 @@ +{ + "sn": "ZVG011025120088", + "name": "ZVG011025120088", + "onlineStatus": "OFFLINE", + "currentPower": { + "onlineStatus": "OFFLINE" + }, + "smartMode": "CHARGED", + "globalSettings": { + "maxOutput": 800, + "maxOutputLimit": 800, + "maxOutputUnlocked": false, + "batHighCap": 100, + "batUseCap": 10, + "maxChargePower": 800, + "feedModePower": { + "modeType": "FIXED", + "fixedPower": 200, + "pvFeedLimitPower": 800, + "equips": [] + }, + "haveElectricityPrices": false, + "standbyTime": 60 + }, + "tips": [], + "bpd": 0, + "statistic": { + "co2": 0, + "saveAmount": 0, + "totalCapacity": 0 + }, + "isShowStatistic": false, + "meterReaders": [], + "remindManualSocCalibration": true +} diff --git a/tests/components/zinvolt/snapshots/test_diagnostics.ambr b/tests/components/zinvolt/snapshots/test_diagnostics.ambr index 215205463528..aa6cc6b26114 100644 --- a/tests/components/zinvolt/snapshots/test_diagnostics.ambr +++ b/tests/components/zinvolt/snapshots/test_diagnostics.ambr @@ -38,7 +38,7 @@ 'max_power': 800, 'on_grid': True, 'online_status': 'ONLINE', - 'output_current': 4, + 'output_current': 4.0, 'photovoltaic_power': 0, 'power_socket_output': -19, 'state_of_charge': 100.0, diff --git a/tests/components/zinvolt/test_binary_sensor.py b/tests/components/zinvolt/test_binary_sensor.py index 72e14bcd466b..32a855ce5986 100644 --- a/tests/components/zinvolt/test_binary_sensor.py +++ b/tests/components/zinvolt/test_binary_sensor.py @@ -3,14 +3,16 @@ from unittest.mock import AsyncMock, patch from syrupy.assertion import SnapshotAssertion +from zinvolt.models import BatteryState -from homeassistant.const import Platform +from homeassistant.components.zinvolt.const import DOMAIN +from homeassistant.const import STATE_UNAVAILABLE, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from . import setup_integration -from tests.common import MockConfigEntry, snapshot_platform +from tests.common import MockConfigEntry, async_load_fixture, snapshot_platform async def test_all_entities( @@ -25,3 +27,20 @@ async def test_all_entities( await setup_integration(hass, mock_config_entry) await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +async def test_offline_battery_on_grid_unknown( + hass: HomeAssistant, + mock_zinvolt_client: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """on_grid is unknown when the battery is offline.""" + mock_zinvolt_client.get_battery_status.return_value = BatteryState.from_json( + await async_load_fixture(hass, "current_state_offline.json", DOMAIN) + ) + await setup_integration(hass, mock_config_entry) + + assert ( + hass.states.get("binary_sensor.zinvolt_batterij_grid_connection").state + == STATE_UNAVAILABLE + ) diff --git a/tests/components/zinvolt/test_sensor.py b/tests/components/zinvolt/test_sensor.py index 20e5e5c029b1..8f4714d437a5 100644 --- a/tests/components/zinvolt/test_sensor.py +++ b/tests/components/zinvolt/test_sensor.py @@ -3,14 +3,16 @@ from unittest.mock import AsyncMock, patch from syrupy.assertion import SnapshotAssertion +from zinvolt.models import BatteryState -from homeassistant.const import Platform +from homeassistant.components.zinvolt.const import DOMAIN +from homeassistant.const import STATE_UNAVAILABLE, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from . import setup_integration -from tests.common import MockConfigEntry, snapshot_platform +from tests.common import MockConfigEntry, async_load_fixture, snapshot_platform async def test_all_entities( @@ -25,3 +27,18 @@ async def test_all_entities( await setup_integration(hass, mock_config_entry) await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +async def test_offline_battery_sensors_unknown( + hass: HomeAssistant, + mock_zinvolt_client: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Sensors report unknown when the battery is offline.""" + mock_zinvolt_client.get_battery_status.return_value = BatteryState.from_json( + await async_load_fixture(hass, "current_state_offline.json", DOMAIN) + ) + await setup_integration(hass, mock_config_entry) + + assert hass.states.get("sensor.zinvolt_batterij_battery").state == STATE_UNAVAILABLE + assert hass.states.get("sensor.zinvolt_batterij_power").state == STATE_UNAVAILABLE