2017-02-25 20:55:01 +00:00
""" The tests for the MQTT discovery. """
2024-03-08 18:16:21 +00:00
2023-05-09 14:36:19 +00:00
import asyncio
2022-09-09 13:24:26 +00:00
import copy
2022-02-18 12:45:25 +00:00
import json
2019-08-07 00:32:15 +00:00
from pathlib import Path
import re
2022-02-18 12:45:25 +00:00
from unittest . mock import AsyncMock , call , patch
2017-02-07 17:13:24 +00:00
2020-02-25 04:46:02 +00:00
import pytest
2020-10-07 16:30:51 +00:00
from homeassistant import config_entries
2018-09-27 14:07:56 +00:00
from homeassistant . components import mqtt
2019-08-07 00:32:15 +00:00
from homeassistant . components . mqtt . abbreviations import (
ABBREVIATIONS ,
DEVICE_ABBREVIATIONS ,
)
2024-05-06 20:32:46 +00:00
from homeassistant . components . mqtt . discovery import (
MQTT_DISCOVERY_DONE ,
MQTT_DISCOVERY_NEW ,
MQTT_DISCOVERY_NEW_COMPONENT ,
MQTT_DISCOVERY_UPDATED ,
MQTTDiscoveryPayload ,
async_start ,
)
2021-10-11 21:37:31 +00:00
from homeassistant . const import (
EVENT_STATE_CHANGED ,
STATE_ON ,
STATE_UNAVAILABLE ,
2022-02-03 15:47:24 +00:00
STATE_UNKNOWN ,
2022-06-13 20:17:10 +00:00
Platform ,
2021-10-11 21:37:31 +00:00
)
2023-02-08 19:56:27 +00:00
from homeassistant . core import Event , HomeAssistant , callback
from homeassistant . data_entry_flow import FlowResult
2023-02-09 10:53:56 +00:00
from homeassistant . helpers import device_registry as dr , entity_registry as er
2024-05-06 20:32:46 +00:00
from homeassistant . helpers . dispatcher import (
async_dispatcher_connect ,
async_dispatcher_send ,
)
2023-02-08 19:56:27 +00:00
from homeassistant . helpers . service_info . mqtt import MqttServiceInfo
2022-02-18 12:45:25 +00:00
from homeassistant . setup import async_setup_component
2024-05-06 20:32:46 +00:00
from homeassistant . util . signal_type import SignalTypeFormat
2017-02-07 17:13:24 +00:00
2023-05-08 13:37:25 +00:00
from . test_common import help_all_subscribe_calls , help_test_unload_config_entry
2022-09-09 13:24:26 +00:00
2020-10-07 16:30:51 +00:00
from tests . common import (
2022-02-18 12:45:25 +00:00
MockConfigEntry ,
2022-05-09 09:52:08 +00:00
async_capture_events ,
2020-10-07 16:30:51 +00:00
async_fire_mqtt_message ,
2023-11-24 09:56:17 +00:00
mock_config_flow ,
2023-11-16 15:55:08 +00:00
mock_platform ,
2020-10-07 16:30:51 +00:00
)
2023-02-08 19:56:27 +00:00
from tests . typing import (
MqttMockHAClientGenerator ,
MqttMockPahoClient ,
WebSocketGenerator ,
)
2020-02-25 04:46:02 +00:00
2020-10-07 12:51:06 +00:00
@pytest.mark.parametrize (
2022-07-22 11:36:43 +00:00
" mqtt_config_entry_data " ,
2020-10-07 12:51:06 +00:00
[ { mqtt . CONF_BROKER : " mock-broker " , mqtt . CONF_DISCOVERY : False } ] ,
)
2023-02-08 19:56:27 +00:00
async def test_subscribing_config_topic (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2017-02-07 17:13:24 +00:00
""" Test setting up discovery. """
2023-04-12 07:43:03 +00:00
mqtt_mock = await mqtt_mock_entry ( )
2024-03-09 11:54:10 +00:00
entry = hass . config_entries . async_entries ( mqtt . DOMAIN ) [ 0 ]
2018-09-27 14:07:56 +00:00
2019-07-31 19:25:30 +00:00
discovery_topic = " homeassistant "
2024-03-09 11:54:10 +00:00
await async_start ( hass , discovery_topic , entry )
2017-02-18 22:17:18 +00:00
2021-01-23 13:51:25 +00:00
call_args1 = mqtt_mock . async_subscribe . mock_calls [ 0 ] [ 1 ]
assert call_args1 [ 2 ] == 0
call_args2 = mqtt_mock . async_subscribe . mock_calls [ 1 ] [ 1 ]
assert call_args2 [ 2 ] == 0
topics = [ call_args1 [ 0 ] , call_args2 [ 0 ] ]
assert discovery_topic + " /+/+/config " in topics
assert discovery_topic + " /+/+/+/config " in topics
2017-02-07 17:13:24 +00:00
2021-06-23 13:53:17 +00:00
@pytest.mark.parametrize (
2023-02-15 13:09:50 +00:00
( " topic " , " log " ) ,
2021-06-23 13:53:17 +00:00
[
( " homeassistant/binary_sensor/bla/not_config " , False ) ,
( " homeassistant/binary_sensor/rörkrökare/config " , True ) ,
] ,
)
2023-02-08 19:56:27 +00:00
async def test_invalid_topic (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
caplog : pytest . LogCaptureFixture ,
topic : str ,
log : bool ,
) - > None :
2017-02-25 20:55:01 +00:00
""" Test sending to invalid topic. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2019-07-31 19:25:30 +00:00
with patch (
2020-04-03 16:05:58 +00:00
" homeassistant.components.mqtt.discovery.async_dispatcher_send "
) as mock_dispatcher_send :
2020-04-30 20:29:50 +00:00
mock_dispatcher_send = AsyncMock ( return_value = None )
2017-02-07 17:13:24 +00:00
2021-06-23 13:53:17 +00:00
async_fire_mqtt_message ( hass , topic , " {} " )
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2020-04-03 16:05:58 +00:00
assert not mock_dispatcher_send . called
2021-06-23 13:53:17 +00:00
if log :
assert (
f " Received message on illegal discovery topic ' { topic } ' " in caplog . text
)
else :
assert " Received message on illegal discovery topic ' " not in caplog . text
caplog . clear ( )
2017-02-07 17:13:24 +00:00
2023-02-08 19:56:27 +00:00
async def test_invalid_json (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
caplog : pytest . LogCaptureFixture ,
) - > None :
2017-02-07 17:13:24 +00:00
""" Test sending in invalid JSON. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2019-07-31 19:25:30 +00:00
with patch (
2020-04-03 16:05:58 +00:00
" homeassistant.components.mqtt.discovery.async_dispatcher_send "
) as mock_dispatcher_send :
2020-04-30 20:29:50 +00:00
mock_dispatcher_send = AsyncMock ( return_value = None )
2017-02-07 17:13:24 +00:00
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message (
hass , " homeassistant/binary_sensor/bla/config " , " not json "
)
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2019-07-31 19:25:30 +00:00
assert " Unable to parse JSON " in caplog . text
2020-04-03 16:05:58 +00:00
assert not mock_dispatcher_send . called
2017-02-07 17:13:24 +00:00
2024-03-09 22:48:54 +00:00
@pytest.mark.parametrize (
" domain " , [ " tag " , " device_automation " , Platform . SENSOR , Platform . LIGHT ]
)
2023-10-04 17:36:34 +00:00
@pytest.mark.no_fail_on_log_exception
async def test_discovery_schema_error (
hass : HomeAssistant ,
mqtt_mock_entry : MqttMockHAClientGenerator ,
caplog : pytest . LogCaptureFixture ,
2023-10-07 19:00:33 +00:00
domain : Platform | str ,
2023-10-04 17:36:34 +00:00
) - > None :
""" Test unexpected error JSON config. """
with patch (
2023-10-07 19:00:33 +00:00
f " homeassistant.components.mqtt. { domain } .DISCOVERY_SCHEMA " ,
2023-10-04 17:36:34 +00:00
side_effect = AttributeError ( " Attribute abc not found " ) ,
) :
await mqtt_mock_entry ( )
async_fire_mqtt_message (
hass ,
2023-10-07 19:00:33 +00:00
f " homeassistant/ { domain } /bla/config " ,
' { " name " : " Beer " , " some_topic " : " bla " } ' ,
2023-10-04 17:36:34 +00:00
)
await hass . async_block_till_done ( )
assert " AttributeError: Attribute abc not found " in caplog . text
2023-10-19 10:06:33 +00:00
async def test_invalid_config (
hass : HomeAssistant ,
mqtt_mock_entry : MqttMockHAClientGenerator ,
caplog : pytest . LogCaptureFixture ,
) - > None :
""" Test sending in JSON that violates the platform schema. """
await mqtt_mock_entry ( )
async_fire_mqtt_message (
hass ,
" homeassistant/alarm_control_panel/bla/config " ,
' { " name " : " abc " , " state_topic " : " home/alarm " , '
' " command_topic " : " home/alarm/set " , '
' " qos " : " some_invalid_value " } ' ,
)
await hass . async_block_till_done ( )
assert " Error ' expected int for dictionary value @ data[ ' qos ' ] ' " in caplog . text
2023-02-08 19:56:27 +00:00
async def test_only_valid_components (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
caplog : pytest . LogCaptureFixture ,
) - > None :
2017-02-25 20:55:01 +00:00
""" Test for a valid component. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2019-07-31 19:25:30 +00:00
with patch (
2020-04-03 16:05:58 +00:00
" homeassistant.components.mqtt.discovery.async_dispatcher_send "
) as mock_dispatcher_send :
2019-04-14 03:25:45 +00:00
invalid_component = " timer "
2018-06-25 13:13:19 +00:00
2020-04-30 20:29:50 +00:00
mock_dispatcher_send = AsyncMock ( return_value = None )
2017-02-07 17:13:24 +00:00
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message (
2020-04-04 22:33:07 +00:00
hass , f " homeassistant/ { invalid_component } /bla/config " , " {} "
2019-07-31 19:25:30 +00:00
)
2018-06-25 13:13:19 +00:00
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2018-06-25 13:13:19 +00:00
2020-04-04 22:33:07 +00:00
assert f " Integration { invalid_component } is not supported " in caplog . text
2018-06-25 13:13:19 +00:00
2020-04-03 16:05:58 +00:00
assert not mock_dispatcher_send . called
2017-02-07 17:13:24 +00:00
2023-02-08 19:56:27 +00:00
async def test_correct_config_discovery (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2017-02-25 20:55:01 +00:00
""" Test sending in correct JSON. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message (
2020-03-19 05:39:15 +00:00
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
2019-07-31 19:25:30 +00:00
)
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2017-02-07 17:13:24 +00:00
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " binary_sensor.beer " )
2017-02-07 17:13:24 +00:00
assert state is not None
2019-07-31 19:25:30 +00:00
assert state . name == " Beer "
2022-09-28 12:13:44 +00:00
assert ( " binary_sensor " , " bla " ) in hass . data [ " mqtt " ] . discovery_already_discovered
2017-06-24 07:46:41 +00:00
2023-08-24 07:50:39 +00:00
async def test_discovery_integration_info (
hass : HomeAssistant ,
mqtt_mock_entry : MqttMockHAClientGenerator ,
caplog : pytest . LogCaptureFixture ,
) - > None :
""" Test logging discovery of new and updated items. """
await mqtt_mock_entry ( )
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " , " o " : { " name " : " bla2mqtt " , " sw " : " 1.0 " } } ' ,
)
await hass . async_block_till_done ( )
state = hass . states . get ( " binary_sensor.beer " )
assert state is not None
assert state . name == " Beer "
assert (
" Found new component: binary_sensor bla from external application bla2mqtt, version: 1.0 "
in caplog . text
)
caplog . clear ( )
# Send an update and add support url
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Milk " , " state_topic " : " test-topic " , " o " : { " name " : " bla2mqtt " , " sw " : " 1.1 " , " url " : " https://bla2mqtt.example.com/support " } } ' ,
)
await hass . async_block_till_done ( )
state = hass . states . get ( " binary_sensor.beer " )
assert state is not None
assert state . name == " Milk "
assert (
" Component has already been discovered: binary_sensor bla, sending update from external application bla2mqtt, version: 1.1, support URL: https://bla2mqtt.example.com/support "
in caplog . text
)
@pytest.mark.parametrize (
" config_message " ,
[
' { " name " : " Beer " , " state_topic " : " test-topic " , " o " : " bla2mqtt " } ' ,
' { " name " : " Beer " , " state_topic " : " test-topic " , " o " : 2.0 } ' ,
' { " name " : " Beer " , " state_topic " : " test-topic " , " o " : null } ' ,
' { " name " : " Beer " , " state_topic " : " test-topic " , " o " : { " sw " : " bla2mqtt " } } ' ,
] ,
)
async def test_discovery_with_invalid_integration_info (
hass : HomeAssistant ,
mqtt_mock_entry : MqttMockHAClientGenerator ,
caplog : pytest . LogCaptureFixture ,
config_message : str ,
) - > None :
""" Test sending in correct JSON. """
await mqtt_mock_entry ( )
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
config_message ,
)
await hass . async_block_till_done ( )
state = hass . states . get ( " binary_sensor.beer " )
assert state is None
assert (
" Unable to parse origin information from discovery message, got " in caplog . text
)
2023-02-08 19:56:27 +00:00
async def test_discover_fan (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2017-09-17 09:32:22 +00:00
""" Test discovering an MQTT fan. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message (
hass ,
" homeassistant/fan/bla/config " ,
2023-02-08 19:56:27 +00:00
' { " name " : " Beer " , " command_topic " : " test_topic " } ' ,
2019-07-31 19:25:30 +00:00
)
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2017-09-17 09:32:22 +00:00
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " fan.beer " )
2017-09-17 09:32:22 +00:00
assert state is not None
2019-07-31 19:25:30 +00:00
assert state . name == " Beer "
2022-09-28 12:13:44 +00:00
assert ( " fan " , " bla " ) in hass . data [ " mqtt " ] . discovery_already_discovered
2017-09-17 09:32:22 +00:00
2023-02-08 19:56:27 +00:00
async def test_discover_climate (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
caplog : pytest . LogCaptureFixture ,
) - > None :
2018-06-25 13:13:19 +00:00
""" Test discovering an MQTT climate component. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2018-06-25 13:13:19 +00:00
data = (
' { " name " : " ClimateTest " , '
' " current_temperature_topic " : " climate/bla/current_temp " , '
' " temperature_command_topic " : " climate/bla/target_temp " } '
)
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message ( hass , " homeassistant/climate/bla/config " , data )
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2018-06-25 13:13:19 +00:00
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " climate.ClimateTest " )
2018-06-25 13:13:19 +00:00
assert state is not None
2019-07-31 19:25:30 +00:00
assert state . name == " ClimateTest "
2022-09-28 12:13:44 +00:00
assert ( " climate " , " bla " ) in hass . data [ " mqtt " ] . discovery_already_discovered
2018-06-25 13:13:19 +00:00
2022-06-02 12:24:46 +00:00
async def test_discover_alarm_control_panel (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2018-07-27 15:16:49 +00:00
""" Test discovering an MQTT alarm control panel component. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2018-07-27 15:16:49 +00:00
data = (
' { " name " : " AlarmControlPanelTest " , '
' " state_topic " : " test_topic " , '
' " command_topic " : " test_topic " } '
)
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message ( hass , " homeassistant/alarm_control_panel/bla/config " , data )
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2018-07-27 15:16:49 +00:00
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " alarm_control_panel.AlarmControlPanelTest " )
2018-07-27 15:16:49 +00:00
assert state is not None
2019-07-31 19:25:30 +00:00
assert state . name == " AlarmControlPanelTest "
2022-09-28 12:13:44 +00:00
assert ( " alarm_control_panel " , " bla " ) in hass . data [
" mqtt "
] . discovery_already_discovered
2018-07-27 15:16:49 +00:00
2021-11-08 13:02:18 +00:00
@pytest.mark.parametrize (
2023-02-15 13:09:50 +00:00
( " topic " , " config " , " entity_id " , " name " , " domain " ) ,
2021-11-08 13:02:18 +00:00
[
(
" homeassistant/alarm_control_panel/object/bla/config " ,
' { " name " : " Hello World 1 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " command_topic " : " test-topic " } ' ,
" alarm_control_panel.hello_id " ,
" Hello World 1 " ,
" alarm_control_panel " ,
) ,
(
" homeassistant/binary_sensor/object/bla/config " ,
' { " name " : " Hello World 2 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " } ' ,
" binary_sensor.hello_id " ,
" Hello World 2 " ,
" binary_sensor " ,
) ,
2021-11-09 11:04:14 +00:00
(
" homeassistant/button/object/bla/config " ,
' { " name " : " Hello World button " , " obj_id " : " hello_id " , " command_topic " : " test-topic " } ' ,
" button.hello_id " ,
" Hello World button " ,
" button " ,
) ,
2021-11-08 13:02:18 +00:00
(
" homeassistant/camera/object/bla/config " ,
' { " name " : " Hello World 3 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " topic " : " test-topic " } ' ,
" camera.hello_id " ,
" Hello World 3 " ,
" camera " ,
) ,
(
" homeassistant/climate/object/bla/config " ,
' { " name " : " Hello World 4 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " } ' ,
" climate.hello_id " ,
" Hello World 4 " ,
" climate " ,
) ,
(
" homeassistant/cover/object/bla/config " ,
' { " name " : " Hello World 5 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " } ' ,
" cover.hello_id " ,
" Hello World 5 " ,
" cover " ,
) ,
(
" homeassistant/fan/object/bla/config " ,
' { " name " : " Hello World 6 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " command_topic " : " test-topic " } ' ,
" fan.hello_id " ,
" Hello World 6 " ,
" fan " ,
) ,
(
" homeassistant/humidifier/object/bla/config " ,
' { " name " : " Hello World 7 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " target_humidity_command_topic " : " test-topic " , " command_topic " : " test-topic " } ' ,
" humidifier.hello_id " ,
" Hello World 7 " ,
" humidifier " ,
) ,
(
" homeassistant/number/object/bla/config " ,
' { " name " : " Hello World 8 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " command_topic " : " test-topic " } ' ,
" number.hello_id " ,
" Hello World 8 " ,
" number " ,
) ,
(
" homeassistant/scene/object/bla/config " ,
' { " name " : " Hello World 9 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " command_topic " : " test-topic " } ' ,
" scene.hello_id " ,
" Hello World 9 " ,
" scene " ,
) ,
(
" homeassistant/select/object/bla/config " ,
' { " name " : " Hello World 10 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " options " : [ " opt1 " , " opt2 " ], " command_topic " : " test-topic " } ' ,
" select.hello_id " ,
" Hello World 10 " ,
" select " ,
) ,
(
" homeassistant/sensor/object/bla/config " ,
' { " name " : " Hello World 11 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " } ' ,
" sensor.hello_id " ,
" Hello World 11 " ,
" sensor " ,
) ,
(
" homeassistant/switch/object/bla/config " ,
' { " name " : " Hello World 12 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " command_topic " : " test-topic " } ' ,
" switch.hello_id " ,
" Hello World 12 " ,
" switch " ,
) ,
(
" homeassistant/light/object/bla/config " ,
' { " name " : " Hello World 13 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " command_topic " : " test-topic " } ' ,
" light.hello_id " ,
" Hello World 13 " ,
" light " ,
) ,
(
" homeassistant/light/object/bla/config " ,
' { " name " : " Hello World 14 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " command_topic " : " test-topic " , " schema " : " json " } ' ,
" light.hello_id " ,
" Hello World 14 " ,
" light " ,
) ,
(
" homeassistant/light/object/bla/config " ,
' { " name " : " Hello World 15 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " command_off_template " : " template " , " command_on_template " : " template " , " command_topic " : " test-topic " , " schema " : " template " } ' ,
" light.hello_id " ,
" Hello World 15 " ,
" light " ,
) ,
(
" homeassistant/vacuum/object/bla/config " ,
' { " name " : " Hello World 16 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " schema " : " state " } ' ,
" vacuum.hello_id " ,
" Hello World 16 " ,
" vacuum " ,
) ,
(
2024-01-08 08:22:43 +00:00
" homeassistant/valve/object/bla/config " ,
' { " name " : " Hello World 17 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " } ' ,
" valve.hello_id " ,
2021-11-08 13:02:18 +00:00
" Hello World 17 " ,
2024-01-08 08:22:43 +00:00
" valve " ,
2021-11-08 13:02:18 +00:00
) ,
(
" homeassistant/lock/object/bla/config " ,
' { " name " : " Hello World 18 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " , " command_topic " : " test-topic " } ' ,
" lock.hello_id " ,
" Hello World 18 " ,
" lock " ,
) ,
(
" homeassistant/device_tracker/object/bla/config " ,
' { " name " : " Hello World 19 " , " obj_id " : " hello_id " , " state_topic " : " test-topic " } ' ,
" device_tracker.hello_id " ,
" Hello World 19 " ,
" device_tracker " ,
) ,
] ,
)
async def test_discovery_with_object_id (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
topic : str ,
config : str ,
entity_id : str ,
name : str ,
domain : str ,
) - > None :
2021-11-08 13:02:18 +00:00
""" Test discovering an MQTT entity with object_id. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2021-11-08 13:02:18 +00:00
async_fire_mqtt_message ( hass , topic , config )
await hass . async_block_till_done ( )
state = hass . states . get ( entity_id )
assert state is not None
assert state . name == name
2022-09-28 12:13:44 +00:00
assert ( domain , " object bla " ) in hass . data [ " mqtt " ] . discovery_already_discovered
2021-11-08 13:02:18 +00:00
2023-02-08 19:56:27 +00:00
async def test_discovery_incl_nodeid (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2017-06-24 07:46:41 +00:00
""" Test sending in correct JSON with optional node_id included. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message (
2020-03-19 05:39:15 +00:00
hass ,
" homeassistant/binary_sensor/my_node_id/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
2019-07-31 19:25:30 +00:00
)
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2017-06-24 07:46:41 +00:00
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " binary_sensor.beer " )
2017-06-24 07:46:41 +00:00
assert state is not None
2019-07-31 19:25:30 +00:00
assert state . name == " Beer "
2022-09-28 12:13:44 +00:00
assert ( " binary_sensor " , " my_node_id bla " ) in hass . data [
" mqtt "
] . discovery_already_discovered
2017-05-23 18:08:12 +00:00
2023-02-08 19:56:27 +00:00
async def test_non_duplicate_discovery (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
caplog : pytest . LogCaptureFixture ,
) - > None :
2017-05-23 18:08:12 +00:00
""" Test for a non duplicate component. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message (
2020-03-19 05:39:15 +00:00
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
2019-07-31 19:25:30 +00:00
)
async_fire_mqtt_message (
2020-03-19 05:39:15 +00:00
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
2019-07-31 19:25:30 +00:00
)
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2017-05-23 18:08:12 +00:00
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " binary_sensor.beer " )
state_duplicate = hass . states . get ( " binary_sensor.beer1 " )
2017-05-23 18:08:12 +00:00
assert state is not None
2019-07-31 19:25:30 +00:00
assert state . name == " Beer "
2017-05-23 18:08:12 +00:00
assert state_duplicate is None
2020-01-02 19:17:10 +00:00
assert " Component has already been discovered: binary_sensor bla " in caplog . text
2018-10-12 06:51:16 +00:00
2023-02-08 19:56:27 +00:00
async def test_removal (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2020-02-25 04:46:02 +00:00
""" Test removal of component through empty discovery message. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2020-02-25 04:46:02 +00:00
async_fire_mqtt_message (
2020-03-19 05:39:15 +00:00
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
2020-02-25 04:46:02 +00:00
)
await hass . async_block_till_done ( )
state = hass . states . get ( " binary_sensor.beer " )
assert state is not None
async_fire_mqtt_message ( hass , " homeassistant/binary_sensor/bla/config " , " " )
await hass . async_block_till_done ( )
state = hass . states . get ( " binary_sensor.beer " )
assert state is None
2023-02-08 19:56:27 +00:00
async def test_rediscover (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2020-02-25 04:46:02 +00:00
""" Test rediscover of removed component. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2020-02-25 04:46:02 +00:00
async_fire_mqtt_message (
2020-03-19 05:39:15 +00:00
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
2020-02-25 04:46:02 +00:00
)
await hass . async_block_till_done ( )
state = hass . states . get ( " binary_sensor.beer " )
assert state is not None
async_fire_mqtt_message ( hass , " homeassistant/binary_sensor/bla/config " , " " )
await hass . async_block_till_done ( )
state = hass . states . get ( " binary_sensor.beer " )
assert state is None
async_fire_mqtt_message (
2020-03-19 05:39:15 +00:00
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
2020-02-25 04:46:02 +00:00
)
await hass . async_block_till_done ( )
state = hass . states . get ( " binary_sensor.beer " )
assert state is not None
2023-02-08 19:56:27 +00:00
async def test_rapid_rediscover (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2021-01-04 11:28:17 +00:00
""" Test immediate rediscover of removed component. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2022-05-09 09:52:08 +00:00
events = async_capture_events ( hass , EVENT_STATE_CHANGED )
2021-01-04 11:28:17 +00:00
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
)
await hass . async_block_till_done ( )
state = hass . states . get ( " binary_sensor.beer " )
assert state is not None
assert len ( events ) == 1
# Removal immediately followed by rediscover
async_fire_mqtt_message ( hass , " homeassistant/binary_sensor/bla/config " , " " )
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
)
async_fire_mqtt_message ( hass , " homeassistant/binary_sensor/bla/config " , " " )
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Milk " , " state_topic " : " test-topic " } ' ,
)
await hass . async_block_till_done ( )
assert len ( hass . states . async_entity_ids ( " binary_sensor " ) ) == 1
state = hass . states . get ( " binary_sensor.milk " )
assert state is not None
assert len ( events ) == 5
# Remove the entity
assert events [ 1 ] . data [ " entity_id " ] == " binary_sensor.beer "
assert events [ 1 ] . data [ " new_state " ] is None
# Add the entity
assert events [ 2 ] . data [ " entity_id " ] == " binary_sensor.beer "
assert events [ 2 ] . data [ " old_state " ] is None
# Remove the entity
assert events [ 3 ] . data [ " entity_id " ] == " binary_sensor.beer "
assert events [ 3 ] . data [ " new_state " ] is None
# Add the entity
assert events [ 4 ] . data [ " entity_id " ] == " binary_sensor.milk "
assert events [ 4 ] . data [ " old_state " ] is None
2023-02-08 19:56:27 +00:00
async def test_rapid_rediscover_unique (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2021-01-04 11:28:17 +00:00
""" Test immediate rediscover of removed component. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2021-01-04 11:28:17 +00:00
events = [ ]
2023-02-08 19:56:27 +00:00
@callback
def test_callback ( event : Event ) - > None :
2021-01-04 11:28:17 +00:00
""" Verify event got called. """
events . append ( event )
2023-02-08 19:56:27 +00:00
hass . bus . async_listen ( EVENT_STATE_CHANGED , test_callback )
2021-01-04 11:28:17 +00:00
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla2/config " ,
' { " name " : " Ale " , " state_topic " : " test-topic " , " unique_id " : " very_unique " } ' ,
)
await hass . async_block_till_done ( )
state = hass . states . get ( " binary_sensor.ale " )
assert state is not None
assert len ( events ) == 1
# Duplicate unique_id, immediately followed by correct unique_id
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " , " unique_id " : " very_unique " } ' ,
)
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " , " unique_id " : " even_uniquer " } ' ,
)
async_fire_mqtt_message ( hass , " homeassistant/binary_sensor/bla/config " , " " )
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Milk " , " state_topic " : " test-topic " , " unique_id " : " even_uniquer " } ' ,
)
await hass . async_block_till_done ( )
assert len ( hass . states . async_entity_ids ( " binary_sensor " ) ) == 2
state = hass . states . get ( " binary_sensor.ale " )
assert state is not None
state = hass . states . get ( " binary_sensor.milk " )
assert state is not None
assert len ( events ) == 4
# Add the entity
assert events [ 1 ] . data [ " entity_id " ] == " binary_sensor.beer "
assert events [ 1 ] . data [ " old_state " ] is None
# Remove the entity
assert events [ 2 ] . data [ " entity_id " ] == " binary_sensor.beer "
assert events [ 2 ] . data [ " new_state " ] is None
# Add the entity
assert events [ 3 ] . data [ " entity_id " ] == " binary_sensor.milk "
assert events [ 3 ] . data [ " old_state " ] is None
2023-02-08 19:56:27 +00:00
async def test_rapid_reconfigure (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2022-03-30 03:26:11 +00:00
""" Test immediate reconfigure of added component. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2022-03-30 03:26:11 +00:00
events = [ ]
2023-02-08 19:56:27 +00:00
@callback
def test_callback ( event : Event ) - > None :
2022-03-30 03:26:11 +00:00
""" Verify event got called. """
events . append ( event )
2023-02-08 19:56:27 +00:00
hass . bus . async_listen ( EVENT_STATE_CHANGED , test_callback )
2022-03-30 03:26:11 +00:00
# Discovery immediately followed by reconfig
async_fire_mqtt_message ( hass , " homeassistant/binary_sensor/bla/config " , " " )
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic1 " } ' ,
)
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Milk " , " state_topic " : " test-topic2 " } ' ,
)
async_fire_mqtt_message (
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Wine " , " state_topic " : " test-topic3 " } ' ,
)
await hass . async_block_till_done ( )
assert len ( hass . states . async_entity_ids ( " binary_sensor " ) ) == 1
state = hass . states . get ( " binary_sensor.beer " )
assert state is not None
assert len ( events ) == 3
# Add the entity
assert events [ 0 ] . data [ " entity_id " ] == " binary_sensor.beer "
assert events [ 0 ] . data [ " old_state " ] is None
assert events [ 0 ] . data [ " new_state " ] . attributes [ " friendly_name " ] == " Beer "
# Update the entity
assert events [ 1 ] . data [ " entity_id " ] == " binary_sensor.beer "
assert events [ 1 ] . data [ " new_state " ] is not None
assert events [ 1 ] . data [ " old_state " ] is not None
assert events [ 1 ] . data [ " new_state " ] . attributes [ " friendly_name " ] == " Milk "
# Update the entity
assert events [ 2 ] . data [ " entity_id " ] == " binary_sensor.beer "
assert events [ 2 ] . data [ " new_state " ] is not None
assert events [ 2 ] . data [ " old_state " ] is not None
assert events [ 2 ] . data [ " new_state " ] . attributes [ " friendly_name " ] == " Wine "
2023-02-08 19:56:27 +00:00
async def test_duplicate_removal (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
caplog : pytest . LogCaptureFixture ,
) - > None :
2020-02-25 04:46:02 +00:00
""" Test for a non duplicate component. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2020-02-25 04:46:02 +00:00
async_fire_mqtt_message (
2020-03-19 05:39:15 +00:00
hass ,
" homeassistant/binary_sensor/bla/config " ,
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
2020-02-25 04:46:02 +00:00
)
await hass . async_block_till_done ( )
async_fire_mqtt_message ( hass , " homeassistant/binary_sensor/bla/config " , " " )
await hass . async_block_till_done ( )
assert " Component has already been discovered: binary_sensor bla " in caplog . text
caplog . clear ( )
async_fire_mqtt_message ( hass , " homeassistant/binary_sensor/bla/config " , " " )
await hass . async_block_till_done ( )
assert " Component has already been discovered: binary_sensor bla " not in caplog . text
2022-06-02 12:24:46 +00:00
async def test_cleanup_device (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
hass_ws_client : WebSocketGenerator ,
2023-02-09 07:19:06 +00:00
device_registry : dr . DeviceRegistry ,
entity_registry : er . EntityRegistry ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2022-02-18 12:45:25 +00:00
""" Test discvered device is cleaned up when entry removed from device. """
2023-04-12 07:43:03 +00:00
mqtt_mock = await mqtt_mock_entry ( )
2022-02-18 12:45:25 +00:00
assert await async_setup_component ( hass , " config " , { } )
ws_client = await hass_ws_client ( hass )
2020-02-25 04:46:02 +00:00
data = (
' { " device " : { " identifiers " :[ " 0AFFD2 " ]}, '
' " state_topic " : " foobar/sensor " , '
' " unique_id " : " unique " } '
)
async_fire_mqtt_message ( hass , " homeassistant/sensor/bla/config " , data )
await hass . async_block_till_done ( )
# Verify device and registry entries are created
2023-07-13 17:39:25 +00:00
device_entry = device_registry . async_get_device ( identifiers = { ( " mqtt " , " 0AFFD2 " ) } )
2020-02-25 04:46:02 +00:00
assert device_entry is not None
2023-07-21 10:52:10 +00:00
entity_entry = entity_registry . async_get ( " sensor.none_mqtt_sensor " )
2020-02-25 04:46:02 +00:00
assert entity_entry is not None
2023-07-21 10:52:10 +00:00
state = hass . states . get ( " sensor.none_mqtt_sensor " )
2020-02-25 04:46:02 +00:00
assert state is not None
2022-02-18 12:45:25 +00:00
# Remove MQTT from the device
2022-02-28 14:50:49 +00:00
mqtt_config_entry = hass . config_entries . async_entries ( mqtt . DOMAIN ) [ 0 ]
2024-04-30 10:52:33 +00:00
response = await ws_client . remove_device (
device_entry . id , mqtt_config_entry . entry_id
)
2022-02-18 12:45:25 +00:00
assert response [ " success " ]
2020-02-25 04:46:02 +00:00
await hass . async_block_till_done ( )
2021-02-14 19:42:55 +00:00
await hass . async_block_till_done ( )
2020-02-25 04:46:02 +00:00
# Verify device and registry entries are cleared
2023-07-13 17:39:25 +00:00
device_entry = device_registry . async_get_device ( identifiers = { ( " mqtt " , " 0AFFD2 " ) } )
2020-02-25 04:46:02 +00:00
assert device_entry is None
2023-07-21 10:52:10 +00:00
entity_entry = entity_registry . async_get ( " sensor.none_mqtt_sensor " )
2020-02-25 04:46:02 +00:00
assert entity_entry is None
# Verify state is removed
2023-07-21 10:52:10 +00:00
state = hass . states . get ( " sensor.none_mqtt_sensor " )
2020-02-25 04:46:02 +00:00
assert state is None
2020-07-18 04:59:18 +00:00
await hass . async_block_till_done ( )
2020-02-25 04:46:02 +00:00
# Verify retained discovery topic has been cleared
mqtt_mock . async_publish . assert_called_once_with (
" homeassistant/sensor/bla/config " , " " , 0 , True
)
2022-06-02 12:24:46 +00:00
async def test_cleanup_device_mqtt (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-02-09 07:19:06 +00:00
device_registry : dr . DeviceRegistry ,
entity_registry : er . EntityRegistry ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2022-02-18 12:45:25 +00:00
""" Test discvered device is cleaned up when removed through MQTT. """
2023-04-12 07:43:03 +00:00
mqtt_mock = await mqtt_mock_entry ( )
2022-02-18 12:45:25 +00:00
data = (
' { " device " : { " identifiers " :[ " 0AFFD2 " ]}, '
' " state_topic " : " foobar/sensor " , '
' " unique_id " : " unique " } '
)
async_fire_mqtt_message ( hass , " homeassistant/sensor/bla/config " , data )
await hass . async_block_till_done ( )
# Verify device and registry entries are created
2023-07-13 17:39:25 +00:00
device_entry = device_registry . async_get_device ( identifiers = { ( " mqtt " , " 0AFFD2 " ) } )
2022-02-18 12:45:25 +00:00
assert device_entry is not None
2023-07-21 10:52:10 +00:00
entity_entry = entity_registry . async_get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert entity_entry is not None
2023-07-21 10:52:10 +00:00
state = hass . states . get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert state is not None
async_fire_mqtt_message ( hass , " homeassistant/sensor/bla/config " , " " )
await hass . async_block_till_done ( )
await hass . async_block_till_done ( )
# Verify device and registry entries are cleared
2023-07-13 17:39:25 +00:00
device_entry = device_registry . async_get_device ( identifiers = { ( " mqtt " , " 0AFFD2 " ) } )
2022-02-18 12:45:25 +00:00
assert device_entry is None
2023-07-21 10:52:10 +00:00
entity_entry = entity_registry . async_get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert entity_entry is None
# Verify state is removed
2023-07-21 10:52:10 +00:00
state = hass . states . get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert state is None
await hass . async_block_till_done ( )
# Verify retained discovery topics have not been cleared again
mqtt_mock . async_publish . assert_not_called ( )
async def test_cleanup_device_multiple_config_entries (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
hass_ws_client : WebSocketGenerator ,
2023-02-09 07:19:06 +00:00
device_registry : dr . DeviceRegistry ,
entity_registry : er . EntityRegistry ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2022-02-18 12:45:25 +00:00
""" Test discovered device is cleaned up when entry removed from device. """
assert await async_setup_component ( hass , " config " , { } )
2022-06-02 12:24:46 +00:00
await hass . async_block_till_done ( )
2023-04-12 07:43:03 +00:00
mqtt_mock = await mqtt_mock_entry ( )
2022-02-18 12:45:25 +00:00
ws_client = await hass_ws_client ( hass )
config_entry = MockConfigEntry ( domain = " test " , data = { } )
config_entry . add_to_hass ( hass )
2023-02-09 07:19:06 +00:00
device_entry = device_registry . async_get_or_create (
2022-02-18 12:45:25 +00:00
config_entry_id = config_entry . entry_id ,
connections = { ( " mac " , " 12:34:56:AB:CD:EF " ) } ,
)
2023-02-08 19:56:27 +00:00
assert device_entry is not None
2022-02-18 12:45:25 +00:00
mqtt_config_entry = hass . config_entries . async_entries ( mqtt . DOMAIN ) [ 0 ]
sensor_config = {
" device " : { " connections " : [ [ " mac " , " 12:34:56:AB:CD:EF " ] ] } ,
" state_topic " : " foobar/sensor " ,
" unique_id " : " unique " ,
}
tag_config = {
" device " : { " connections " : [ [ " mac " , " 12:34:56:AB:CD:EF " ] ] } ,
" topic " : " test-topic " ,
}
trigger_config = {
" automation_type " : " trigger " ,
" topic " : " test-topic " ,
" type " : " foo " ,
" subtype " : " bar " ,
" device " : { " connections " : [ [ " mac " , " 12:34:56:AB:CD:EF " ] ] } ,
}
sensor_data = json . dumps ( sensor_config )
tag_data = json . dumps ( tag_config )
trigger_data = json . dumps ( trigger_config )
async_fire_mqtt_message ( hass , " homeassistant/sensor/bla/config " , sensor_data )
async_fire_mqtt_message ( hass , " homeassistant/tag/bla/config " , tag_data )
async_fire_mqtt_message (
hass , " homeassistant/device_automation/bla/config " , trigger_data
)
await hass . async_block_till_done ( )
# Verify device and registry entries are created
2023-02-09 07:19:06 +00:00
device_entry = device_registry . async_get_device (
2023-07-13 17:39:25 +00:00
connections = { ( " mac " , " 12:34:56:AB:CD:EF " ) }
2023-02-09 07:19:06 +00:00
)
2022-02-18 12:45:25 +00:00
assert device_entry is not None
assert device_entry . config_entries == {
mqtt_config_entry . entry_id ,
config_entry . entry_id ,
}
2023-07-21 10:52:10 +00:00
entity_entry = entity_registry . async_get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert entity_entry is not None
2023-07-21 10:52:10 +00:00
state = hass . states . get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert state is not None
# Remove MQTT from the device
2022-02-28 14:50:49 +00:00
mqtt_config_entry = hass . config_entries . async_entries ( mqtt . DOMAIN ) [ 0 ]
2024-04-30 10:52:33 +00:00
response = await ws_client . remove_device (
device_entry . id , mqtt_config_entry . entry_id
)
2022-02-18 12:45:25 +00:00
assert response [ " success " ]
await hass . async_block_till_done ( )
await hass . async_block_till_done ( )
# Verify device is still there but entity is cleared
2023-02-09 07:19:06 +00:00
device_entry = device_registry . async_get_device (
2023-07-13 17:39:25 +00:00
connections = { ( " mac " , " 12:34:56:AB:CD:EF " ) }
2023-02-09 07:19:06 +00:00
)
2022-02-18 12:45:25 +00:00
assert device_entry is not None
2023-07-21 10:52:10 +00:00
entity_entry = entity_registry . async_get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert device_entry . config_entries == { config_entry . entry_id }
assert entity_entry is None
# Verify state is removed
2023-07-21 10:52:10 +00:00
state = hass . states . get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert state is None
await hass . async_block_till_done ( )
# Verify retained discovery topic has been cleared
mqtt_mock . async_publish . assert_has_calls (
[
call ( " homeassistant/sensor/bla/config " , " " , 0 , True ) ,
call ( " homeassistant/tag/bla/config " , " " , 0 , True ) ,
call ( " homeassistant/device_automation/bla/config " , " " , 0 , True ) ,
] ,
any_order = True ,
)
async def test_cleanup_device_multiple_config_entries_mqtt (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-02-09 07:19:06 +00:00
device_registry : dr . DeviceRegistry ,
entity_registry : er . EntityRegistry ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2022-02-18 12:45:25 +00:00
""" Test discovered device is cleaned up when removed through MQTT. """
2023-04-12 07:43:03 +00:00
mqtt_mock = await mqtt_mock_entry ( )
2022-02-18 12:45:25 +00:00
config_entry = MockConfigEntry ( domain = " test " , data = { } )
config_entry . add_to_hass ( hass )
2023-02-09 07:19:06 +00:00
device_entry = device_registry . async_get_or_create (
2022-02-18 12:45:25 +00:00
config_entry_id = config_entry . entry_id ,
connections = { ( " mac " , " 12:34:56:AB:CD:EF " ) } ,
)
mqtt_config_entry = hass . config_entries . async_entries ( mqtt . DOMAIN ) [ 0 ]
sensor_config = {
" device " : { " connections " : [ [ " mac " , " 12:34:56:AB:CD:EF " ] ] } ,
" state_topic " : " foobar/sensor " ,
" unique_id " : " unique " ,
}
tag_config = {
" device " : { " connections " : [ [ " mac " , " 12:34:56:AB:CD:EF " ] ] } ,
" topic " : " test-topic " ,
}
trigger_config = {
" automation_type " : " trigger " ,
" topic " : " test-topic " ,
" type " : " foo " ,
" subtype " : " bar " ,
" device " : { " connections " : [ [ " mac " , " 12:34:56:AB:CD:EF " ] ] } ,
}
sensor_data = json . dumps ( sensor_config )
tag_data = json . dumps ( tag_config )
trigger_data = json . dumps ( trigger_config )
async_fire_mqtt_message ( hass , " homeassistant/sensor/bla/config " , sensor_data )
async_fire_mqtt_message ( hass , " homeassistant/tag/bla/config " , tag_data )
async_fire_mqtt_message (
hass , " homeassistant/device_automation/bla/config " , trigger_data
)
await hass . async_block_till_done ( )
# Verify device and registry entries are created
2023-02-09 07:19:06 +00:00
device_entry = device_registry . async_get_device (
2023-07-13 17:39:25 +00:00
connections = { ( " mac " , " 12:34:56:AB:CD:EF " ) }
2023-02-09 07:19:06 +00:00
)
2022-02-18 12:45:25 +00:00
assert device_entry is not None
assert device_entry . config_entries == {
mqtt_config_entry . entry_id ,
config_entry . entry_id ,
}
2023-07-21 10:52:10 +00:00
entity_entry = entity_registry . async_get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert entity_entry is not None
2023-07-21 10:52:10 +00:00
state = hass . states . get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert state is not None
# Send MQTT messages to remove
async_fire_mqtt_message ( hass , " homeassistant/sensor/bla/config " , " " )
async_fire_mqtt_message ( hass , " homeassistant/tag/bla/config " , " " )
async_fire_mqtt_message ( hass , " homeassistant/device_automation/bla/config " , " " )
await hass . async_block_till_done ( )
await hass . async_block_till_done ( )
# Verify device is still there but entity is cleared
2023-02-09 07:19:06 +00:00
device_entry = device_registry . async_get_device (
2023-07-13 17:39:25 +00:00
connections = { ( " mac " , " 12:34:56:AB:CD:EF " ) }
2023-02-09 07:19:06 +00:00
)
2022-02-18 12:45:25 +00:00
assert device_entry is not None
2023-07-21 10:52:10 +00:00
entity_entry = entity_registry . async_get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert device_entry . config_entries == { config_entry . entry_id }
assert entity_entry is None
# Verify state is removed
2023-07-21 10:52:10 +00:00
state = hass . states . get ( " sensor.none_mqtt_sensor " )
2022-02-18 12:45:25 +00:00
assert state is None
await hass . async_block_till_done ( )
# Verify retained discovery topics have not been cleared again
mqtt_mock . async_publish . assert_not_called ( )
2023-02-08 19:56:27 +00:00
async def test_discovery_expansion (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2018-10-12 06:51:16 +00:00
""" Test expansion of abbreviated discovery payload. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2018-10-12 06:51:16 +00:00
data = (
' { " ~ " : " some/base/topic " , '
' " name " : " DiscoveryExpansionTest1 " , '
' " stat_t " : " test_topic/~ " , '
2019-02-04 18:54:40 +00:00
' " cmd_t " : " ~/test_topic " , '
2021-10-11 21:37:31 +00:00
' " availability " : [ '
" { "
' " topic " : " ~/avail_item1 " , '
' " payload_available " : " available " , '
' " payload_not_available " : " not_available " '
" }, "
" { "
2022-10-08 13:36:49 +00:00
' " t " : " avail_item2/~ " , '
' " pl_avail " : " available " , '
' " pl_not_avail " : " not_available " '
2021-10-11 21:37:31 +00:00
" } "
" ], "
2019-02-04 18:54:40 +00:00
' " dev " : { '
' " ids " :[ " 5706DF " ], '
' " name " : " DiscoveryExpansionTest1 Device " , '
' " mdl " : " Generic " , '
2022-08-25 09:55:33 +00:00
' " hw " : " rev1 " , '
2019-02-04 18:54:40 +00:00
' " sw " : " 1.2.3.4 " , '
2021-03-15 19:02:02 +00:00
' " mf " : " None " , '
' " sa " : " default_area " '
2019-07-31 19:25:30 +00:00
" } "
" } "
2018-10-12 06:51:16 +00:00
)
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message ( hass , " homeassistant/switch/bla/config " , data )
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2018-10-12 06:51:16 +00:00
2021-10-11 21:37:31 +00:00
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
2023-02-08 19:56:27 +00:00
assert state and state . state == STATE_UNAVAILABLE
2021-10-11 21:37:31 +00:00
async_fire_mqtt_message ( hass , " avail_item2/some/base/topic " , " available " )
await hass . async_block_till_done ( )
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
2018-10-12 06:51:16 +00:00
assert state is not None
2019-07-31 19:25:30 +00:00
assert state . name == " DiscoveryExpansionTest1 "
2022-09-28 12:13:44 +00:00
assert ( " switch " , " bla " ) in hass . data [ " mqtt " ] . discovery_already_discovered
2022-02-03 15:47:24 +00:00
assert state . state == STATE_UNKNOWN
2018-10-12 06:51:16 +00:00
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message ( hass , " test_topic/some/base/topic " , " ON " )
2018-10-12 06:51:16 +00:00
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
2023-02-08 19:56:27 +00:00
assert state and state . state == STATE_ON
2019-04-14 03:25:45 +00:00
2021-10-11 21:37:31 +00:00
async_fire_mqtt_message ( hass , " some/base/topic/avail_item1 " , " not_available " )
await hass . async_block_till_done ( )
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
2023-02-08 19:56:27 +00:00
assert state and state . state == STATE_UNAVAILABLE
2021-10-11 21:37:31 +00:00
2019-04-14 03:25:45 +00:00
2023-02-08 19:56:27 +00:00
async def test_discovery_expansion_2 (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2021-10-29 13:59:16 +00:00
""" Test expansion of abbreviated discovery payload. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2021-10-29 13:59:16 +00:00
data = (
' { " ~ " : " some/base/topic " , '
' " name " : " DiscoveryExpansionTest1 " , '
' " stat_t " : " test_topic/~ " , '
' " cmd_t " : " ~/test_topic " , '
' " availability " : { '
2022-10-08 13:36:49 +00:00
' " t " : " ~/avail_item1 " , '
' " pl_avail " : " available " , '
' " pl_not_avail " : " not_available " '
2021-10-29 13:59:16 +00:00
" }, "
' " dev " : { '
' " ids " :[ " 5706DF " ], '
' " name " : " DiscoveryExpansionTest1 Device " , '
' " mdl " : " Generic " , '
2022-08-25 09:55:33 +00:00
' " hw " : " rev1 " , '
2021-10-29 13:59:16 +00:00
' " sw " : " 1.2.3.4 " , '
' " mf " : " None " , '
' " sa " : " default_area " '
" } "
" } "
)
async_fire_mqtt_message ( hass , " homeassistant/switch/bla/config " , data )
await hass . async_block_till_done ( )
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
2023-02-08 19:56:27 +00:00
assert state and state . state == STATE_UNAVAILABLE
2021-10-29 13:59:16 +00:00
async_fire_mqtt_message ( hass , " some/base/topic/avail_item1 " , " available " )
await hass . async_block_till_done ( )
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
assert state is not None
assert state . name == " DiscoveryExpansionTest1 "
2022-09-28 12:13:44 +00:00
assert ( " switch " , " bla " ) in hass . data [ " mqtt " ] . discovery_already_discovered
2022-02-03 15:47:24 +00:00
assert state . state == STATE_UNKNOWN
2021-10-29 13:59:16 +00:00
2023-02-08 19:56:27 +00:00
async def test_discovery_expansion_3 (
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
caplog : pytest . LogCaptureFixture ,
) - > None :
2021-10-29 13:59:16 +00:00
""" Test expansion of broken discovery payload. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2021-10-29 13:59:16 +00:00
data = (
' { " ~ " : " some/base/topic " , '
' " name " : " DiscoveryExpansionTest1 " , '
' " stat_t " : " test_topic/~ " , '
' " cmd_t " : " ~/test_topic " , '
' " availability " : " incorrect " , '
' " dev " : { '
' " ids " :[ " 5706DF " ], '
' " name " : " DiscoveryExpansionTest1 Device " , '
' " mdl " : " Generic " , '
2022-08-25 09:55:33 +00:00
' " hw " : " rev1 " , '
2021-10-29 13:59:16 +00:00
' " sw " : " 1.2.3.4 " , '
' " mf " : " None " , '
' " sa " : " default_area " '
" } "
" } "
)
async_fire_mqtt_message ( hass , " homeassistant/switch/bla/config " , data )
await hass . async_block_till_done ( )
assert hass . states . get ( " switch.DiscoveryExpansionTest1 " ) is None
# Make sure the malformed availability data does not trip up discovery by asserting
# there are schema valdiation errors in the log
2023-06-12 19:28:28 +00:00
assert " expected a dictionary @ data[ ' availability ' ][0] " in caplog . text
2021-10-29 13:59:16 +00:00
2021-12-03 19:04:05 +00:00
async def test_discovery_expansion_without_encoding_and_value_template_1 (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2021-12-03 19:04:05 +00:00
""" Test expansion of raw availability payload with a template as list. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2021-12-03 19:04:05 +00:00
data = (
' { " ~ " : " some/base/topic " , '
' " name " : " DiscoveryExpansionTest1 " , '
' " stat_t " : " test_topic/~ " , '
' " cmd_t " : " ~/test_topic " , '
' " encoding " : " " , '
' " availability " : [ { '
' " topic " : " ~/avail_item1 " , '
' " payload_available " : " 1 " , '
' " payload_not_available " : " 0 " , '
' " value_template " : " {{ value|unpack( \' b \' )}} " '
" }], "
' " dev " : { '
' " ids " :[ " 5706DF " ], '
' " name " : " DiscoveryExpansionTest1 Device " , '
' " mdl " : " Generic " , '
2022-08-25 09:55:33 +00:00
' " hw " : " rev1 " , '
2021-12-03 19:04:05 +00:00
' " sw " : " 1.2.3.4 " , '
' " mf " : " None " , '
' " sa " : " default_area " '
" } "
" } "
)
async_fire_mqtt_message ( hass , " homeassistant/switch/bla/config " , data )
await hass . async_block_till_done ( )
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
2023-02-08 19:56:27 +00:00
assert state and state . state == STATE_UNAVAILABLE
2021-12-03 19:04:05 +00:00
async_fire_mqtt_message ( hass , " some/base/topic/avail_item1 " , b " \x01 " )
await hass . async_block_till_done ( )
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
assert state is not None
assert state . name == " DiscoveryExpansionTest1 "
2022-09-28 12:13:44 +00:00
assert ( " switch " , " bla " ) in hass . data [ " mqtt " ] . discovery_already_discovered
2022-02-03 15:47:24 +00:00
assert state . state == STATE_UNKNOWN
2021-12-03 19:04:05 +00:00
async_fire_mqtt_message ( hass , " some/base/topic/avail_item1 " , b " \x00 " )
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
2023-02-08 19:56:27 +00:00
assert state and state . state == STATE_UNAVAILABLE
2021-12-03 19:04:05 +00:00
async def test_discovery_expansion_without_encoding_and_value_template_2 (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2021-12-03 19:04:05 +00:00
""" Test expansion of raw availability payload with a template directly. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2021-12-03 19:04:05 +00:00
data = (
' { " ~ " : " some/base/topic " , '
' " name " : " DiscoveryExpansionTest1 " , '
' " stat_t " : " test_topic/~ " , '
' " cmd_t " : " ~/test_topic " , '
' " availability_topic " : " ~/avail_item1 " , '
' " payload_available " : " 1 " , '
' " payload_not_available " : " 0 " , '
' " encoding " : " " , '
' " availability_template " : " {{ value | unpack( \' b \' ) }} " , '
' " dev " : { '
' " ids " :[ " 5706DF " ], '
' " name " : " DiscoveryExpansionTest1 Device " , '
' " mdl " : " Generic " , '
2022-08-25 09:55:33 +00:00
' " hw " : " rev1 " , '
2021-12-03 19:04:05 +00:00
' " sw " : " 1.2.3.4 " , '
' " mf " : " None " , '
' " sa " : " default_area " '
" } "
" } "
)
async_fire_mqtt_message ( hass , " homeassistant/switch/bla/config " , data )
await hass . async_block_till_done ( )
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
2023-02-08 19:56:27 +00:00
assert state and state . state == STATE_UNAVAILABLE
2021-12-03 19:04:05 +00:00
async_fire_mqtt_message ( hass , " some/base/topic/avail_item1 " , b " \x01 " )
await hass . async_block_till_done ( )
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
assert state is not None
assert state . name == " DiscoveryExpansionTest1 "
2022-09-28 12:13:44 +00:00
assert ( " switch " , " bla " ) in hass . data [ " mqtt " ] . discovery_already_discovered
2022-02-03 15:47:24 +00:00
assert state . state == STATE_UNKNOWN
2021-12-03 19:04:05 +00:00
async_fire_mqtt_message ( hass , " some/base/topic/avail_item1 " , b " \x00 " )
state = hass . states . get ( " switch.DiscoveryExpansionTest1 " )
2023-02-08 19:56:27 +00:00
assert state and state . state == STATE_UNAVAILABLE
2021-12-03 19:04:05 +00:00
2019-08-07 00:32:15 +00:00
ABBREVIATIONS_WHITE_LIST = [
2020-02-18 21:51:10 +00:00
# MQTT client/server/trigger settings
2023-08-24 07:50:39 +00:00
# Integration info
" CONF_SUPPORT_URL " ,
2019-08-07 00:32:15 +00:00
# Undocumented device configuration
" CONF_DEPRECATED_VIA_HUB " ,
" CONF_VIA_DEVICE " ,
# Already short
" CONF_FAN_MODE_LIST " ,
" CONF_HOLD_LIST " ,
" CONF_HS " ,
" CONF_MODE_LIST " ,
" CONF_PRECISION " ,
" CONF_QOS " ,
" CONF_SCHEMA " ,
" CONF_SWING_MODE_LIST " ,
" CONF_TEMP_STEP " ,
2022-08-18 12:21:05 +00:00
# Removed
" CONF_WHITE_VALUE " ,
2019-08-07 00:32:15 +00:00
]
2024-05-02 18:37:21 +00:00
EXCLUDED_MODULES = {
" const.py " ,
" config.py " ,
" config_flow.py " ,
" device_trigger.py " ,
" trigger.py " ,
}
2019-08-07 00:32:15 +00:00
2022-06-02 12:24:46 +00:00
async def test_missing_discover_abbreviations (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2019-08-07 00:32:15 +00:00
""" Check MQTT platforms for missing abbreviations. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2019-08-07 00:32:15 +00:00
missing = [ ]
regex = re . compile ( r " (CONF_[a-zA-Z \ d_]*) *= *[ \ ' \" ]([a-zA-Z \ d_]*)[ \ ' \" ] " )
for fil in Path ( mqtt . __file__ ) . parent . rglob ( " *.py " ) :
2024-05-02 18:37:21 +00:00
if fil . name in EXCLUDED_MODULES :
2020-08-17 16:54:56 +00:00
continue
2023-02-08 19:56:27 +00:00
with open ( fil , encoding = " utf-8 " ) as file :
2019-08-07 00:32:15 +00:00
matches = re . findall ( regex , file . read ( ) )
2024-03-13 20:28:21 +00:00
missing . extend (
f " { fil } : no abbreviation for { match [ 1 ] } ( { match [ 0 ] } ) "
for match in matches
if match [ 1 ] not in ABBREVIATIONS . values ( )
and match [ 1 ] not in DEVICE_ABBREVIATIONS . values ( )
and match [ 0 ] not in ABBREVIATIONS_WHITE_LIST
)
2019-08-07 00:32:15 +00:00
assert not missing
2022-06-02 12:24:46 +00:00
async def test_no_implicit_state_topic_switch (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2019-04-14 03:25:45 +00:00
""" Test no implicit state topic for switch. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2023-02-08 19:56:27 +00:00
data = ' { " name " : " Test1 " , " command_topic " : " cmnd " } '
2019-04-14 03:25:45 +00:00
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message ( hass , " homeassistant/switch/bla/config " , data )
2019-04-14 03:25:45 +00:00
await hass . async_block_till_done ( )
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " switch.Test1 " )
2019-04-14 03:25:45 +00:00
assert state is not None
2019-07-31 19:25:30 +00:00
assert state . name == " Test1 "
2022-09-28 12:13:44 +00:00
assert ( " switch " , " bla " ) in hass . data [ " mqtt " ] . discovery_already_discovered
2022-02-03 15:47:24 +00:00
assert state . state == STATE_UNKNOWN
2019-07-31 19:25:30 +00:00
assert state . attributes [ " assumed_state " ] is True
2019-04-14 03:25:45 +00:00
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message ( hass , " homeassistant/switch/bla/state " , " ON " )
2019-04-14 03:25:45 +00:00
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " switch.Test1 " )
2023-02-08 19:56:27 +00:00
assert state and state . state == STATE_UNKNOWN
2019-07-01 17:23:01 +00:00
2020-10-07 12:51:06 +00:00
@pytest.mark.parametrize (
2022-07-22 11:36:43 +00:00
" mqtt_config_entry_data " ,
2020-10-07 12:51:06 +00:00
[
{
mqtt . CONF_BROKER : " mock-broker " ,
2023-02-08 19:56:27 +00:00
mqtt . CONF_DISCOVERY_PREFIX : " my_home/homeassistant/register " ,
2020-10-07 12:51:06 +00:00
}
] ,
)
2022-06-02 12:24:46 +00:00
async def test_complex_discovery_topic_prefix (
2023-04-12 07:43:03 +00:00
hass : HomeAssistant , mqtt_mock_entry : MqttMockHAClientGenerator
2023-02-08 19:56:27 +00:00
) - > None :
2019-07-01 17:23:01 +00:00
""" Tests handling of discovery topic prefix with multiple slashes. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2022-07-22 11:36:43 +00:00
2019-07-31 19:25:30 +00:00
async_fire_mqtt_message (
hass ,
2020-01-02 19:17:10 +00:00
( " my_home/homeassistant/register/binary_sensor/node1/object1/config " ) ,
2020-03-19 05:39:15 +00:00
' { " name " : " Beer " , " state_topic " : " test-topic " } ' ,
2019-07-31 19:25:30 +00:00
)
2019-07-01 17:23:01 +00:00
await hass . async_block_till_done ( )
2019-07-31 19:25:30 +00:00
state = hass . states . get ( " binary_sensor.beer " )
2019-07-01 17:23:01 +00:00
assert state is not None
2019-07-31 19:25:30 +00:00
assert state . name == " Beer "
2022-09-28 12:13:44 +00:00
assert ( " binary_sensor " , " node1 object1 " ) in hass . data [
" mqtt "
] . discovery_already_discovered
2020-10-07 16:30:51 +00:00
2023-03-14 10:13:55 +00:00
@patch ( " homeassistant.components.mqtt.client.INITIAL_SUBSCRIBE_COOLDOWN " , 0.0 )
@patch ( " homeassistant.components.mqtt.client.SUBSCRIBE_COOLDOWN " , 0.0 )
2023-05-09 14:36:19 +00:00
@patch ( " homeassistant.components.mqtt.client.UNSUBSCRIBE_COOLDOWN " , 0.0 )
2020-10-07 16:30:51 +00:00
async def test_mqtt_integration_discovery_subscribe_unsubscribe (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
mqtt_client_mock : MqttMockPahoClient ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2020-10-07 16:30:51 +00:00
""" Check MQTT integration discovery subscribe and unsubscribe. """
2023-04-12 07:43:03 +00:00
mqtt_mock = await mqtt_mock_entry ( )
2023-11-16 15:55:08 +00:00
mock_platform ( hass , " comp.config_flow " , None )
2020-10-07 16:30:51 +00:00
2024-03-09 11:54:10 +00:00
entry = hass . config_entries . async_entries ( " mqtt " ) [ 0 ]
2020-10-07 16:30:51 +00:00
mqtt_mock ( ) . connected = True
with patch (
" homeassistant.components.mqtt.discovery.async_get_mqtt " ,
return_value = { " comp " : [ " comp/discovery/# " ] } ,
) :
2024-03-09 11:54:10 +00:00
await async_start ( hass , " homeassistant " , entry )
2020-10-07 16:30:51 +00:00
await hass . async_block_till_done ( )
2023-03-14 10:13:55 +00:00
await hass . async_block_till_done ( )
2024-04-22 18:09:45 +00:00
await hass . async_block_till_done ( )
2020-10-07 16:30:51 +00:00
2023-05-08 13:37:25 +00:00
assert ( " comp/discovery/# " , 0 ) in help_all_subscribe_calls ( mqtt_client_mock )
2020-10-07 16:30:51 +00:00
assert not mqtt_client_mock . unsubscribe . called
class TestFlow ( config_entries . ConfigFlow ) :
""" Test flow. """
2023-02-08 19:56:27 +00:00
async def async_step_mqtt ( self , discovery_info : MqttServiceInfo ) - > FlowResult :
2020-10-07 16:30:51 +00:00
""" Test mqtt step. """
return self . async_abort ( reason = " already_configured " )
2024-02-19 19:16:06 +00:00
assert not mqtt_client_mock . unsubscribe . called
2020-10-07 16:30:51 +00:00
2024-02-19 19:16:06 +00:00
wait_unsub = asyncio . Event ( )
2020-10-07 16:30:51 +00:00
2024-02-19 19:16:06 +00:00
def _mock_unsubscribe ( topics : list [ str ] ) - > tuple [ int , int ] :
wait_unsub . set ( )
return ( 0 , 0 )
2024-03-25 23:02:16 +00:00
with (
mock_config_flow ( " comp " , TestFlow ) ,
patch . object ( mqtt_client_mock , " unsubscribe " , side_effect = _mock_unsubscribe ) ,
2024-02-19 19:16:06 +00:00
) :
2020-10-07 16:30:51 +00:00
async_fire_mqtt_message ( hass , " comp/discovery/bla/config " , " " )
2024-02-19 19:16:06 +00:00
await wait_unsub . wait ( )
mqtt_client_mock . unsubscribe . assert_called_once_with ( [ " comp/discovery/# " ] )
2020-10-07 16:30:51 +00:00
2023-03-14 10:13:55 +00:00
@patch ( " homeassistant.components.mqtt.client.INITIAL_SUBSCRIBE_COOLDOWN " , 0.0 )
@patch ( " homeassistant.components.mqtt.client.SUBSCRIBE_COOLDOWN " , 0.0 )
2023-05-09 14:36:19 +00:00
@patch ( " homeassistant.components.mqtt.client.UNSUBSCRIBE_COOLDOWN " , 0.0 )
2022-06-02 12:24:46 +00:00
async def test_mqtt_discovery_unsubscribe_once (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
mqtt_client_mock : MqttMockPahoClient ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-08 19:56:27 +00:00
) - > None :
2020-10-07 16:30:51 +00:00
""" Check MQTT integration discovery unsubscribe once. """
2023-04-12 07:43:03 +00:00
mqtt_mock = await mqtt_mock_entry ( )
2023-11-16 15:55:08 +00:00
mock_platform ( hass , " comp.config_flow " , None )
2020-10-07 16:30:51 +00:00
2024-03-09 11:54:10 +00:00
entry = hass . config_entries . async_entries ( " mqtt " ) [ 0 ]
2020-10-07 16:30:51 +00:00
mqtt_mock ( ) . connected = True
with patch (
" homeassistant.components.mqtt.discovery.async_get_mqtt " ,
return_value = { " comp " : [ " comp/discovery/# " ] } ,
) :
2024-03-09 11:54:10 +00:00
await async_start ( hass , " homeassistant " , entry )
2020-10-07 16:30:51 +00:00
await hass . async_block_till_done ( )
2023-03-14 10:13:55 +00:00
await hass . async_block_till_done ( )
2024-04-22 18:09:45 +00:00
await hass . async_block_till_done ( )
2020-10-07 16:30:51 +00:00
2023-05-08 13:37:25 +00:00
assert ( " comp/discovery/# " , 0 ) in help_all_subscribe_calls ( mqtt_client_mock )
2020-10-07 16:30:51 +00:00
assert not mqtt_client_mock . unsubscribe . called
class TestFlow ( config_entries . ConfigFlow ) :
""" Test flow. """
2023-02-08 19:56:27 +00:00
async def async_step_mqtt ( self , discovery_info : MqttServiceInfo ) - > FlowResult :
2020-10-07 16:30:51 +00:00
""" Test mqtt step. """
2024-03-25 21:41:11 +00:00
await asyncio . sleep ( 0.1 )
2020-10-07 16:30:51 +00:00
return self . async_abort ( reason = " already_configured " )
2023-11-24 09:56:17 +00:00
with mock_config_flow ( " comp " , TestFlow ) :
2020-10-07 16:30:51 +00:00
async_fire_mqtt_message ( hass , " comp/discovery/bla/config " , " " )
async_fire_mqtt_message ( hass , " comp/discovery/bla/config " , " " )
2023-05-09 14:36:19 +00:00
await asyncio . sleep ( 0.1 )
2020-10-07 16:30:51 +00:00
await hass . async_block_till_done ( )
await hass . async_block_till_done ( )
2023-05-09 14:36:19 +00:00
mqtt_client_mock . unsubscribe . assert_called_once_with ( [ " comp/discovery/# " ] )
2022-09-09 13:24:26 +00:00
async def test_clear_config_topic_disabled_entity (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-09 07:19:06 +00:00
device_registry : dr . DeviceRegistry ,
2023-02-08 19:56:27 +00:00
caplog : pytest . LogCaptureFixture ,
) - > None :
2022-09-09 13:24:26 +00:00
""" Test the discovery topic is removed when a disabled entity is removed. """
2023-04-12 07:43:03 +00:00
mqtt_mock = await mqtt_mock_entry ( )
2022-09-09 13:24:26 +00:00
# discover an entity that is not enabled by default
config = {
" state_topic " : " homeassistant_test/sensor/sbfspot_0/sbfspot_12345/ " ,
" unique_id " : " sbfspot_12345 " ,
" enabled_by_default " : False ,
" device " : {
" identifiers " : [ " sbfspot_12345 " ] ,
2023-07-21 10:52:10 +00:00
" name " : " abc123 " ,
2022-09-09 13:24:26 +00:00
" sw_version " : " 1.0 " ,
" connections " : [ [ " mac " , " 12:34:56:AB:CD:EF " ] ] ,
} ,
}
async_fire_mqtt_message (
hass ,
" homeassistant/sensor/sbfspot_0/sbfspot_12345/config " ,
json . dumps ( config ) ,
)
await hass . async_block_till_done ( )
# discover an entity that is not unique (part 1), will be added
config_not_unique1 = copy . deepcopy ( config )
config_not_unique1 [ " name " ] = " sbfspot_12345_1 "
config_not_unique1 [ " unique_id " ] = " not_unique "
config_not_unique1 . pop ( " enabled_by_default " )
async_fire_mqtt_message (
hass ,
" homeassistant/sensor/sbfspot_0/sbfspot_12345_1/config " ,
json . dumps ( config_not_unique1 ) ,
)
# discover an entity that is not unique (part 2), will not be added
config_not_unique2 = copy . deepcopy ( config_not_unique1 )
config_not_unique2 [ " name " ] = " sbfspot_12345_2 "
async_fire_mqtt_message (
hass ,
" homeassistant/sensor/sbfspot_0/sbfspot_12345_2/config " ,
json . dumps ( config_not_unique2 ) ,
)
await hass . async_block_till_done ( )
assert " Platform mqtt does not generate unique IDs " in caplog . text
2023-07-21 10:52:10 +00:00
assert hass . states . get ( " sensor.abc123_sbfspot_12345 " ) is None # disabled
assert hass . states . get ( " sensor.abc123_sbfspot_12345_1 " ) is not None # enabled
assert hass . states . get ( " sensor.abc123_sbfspot_12345_2 " ) is None # not unique
2022-09-09 13:24:26 +00:00
# Verify device is created
2023-02-09 07:19:06 +00:00
device_entry = device_registry . async_get_device (
2023-07-13 17:39:25 +00:00
connections = { ( " mac " , " 12:34:56:AB:CD:EF " ) }
2023-02-09 07:19:06 +00:00
)
2022-09-09 13:24:26 +00:00
assert device_entry is not None
# Remove the device from the registry
2023-02-09 07:19:06 +00:00
device_registry . async_remove_device ( device_entry . id )
2022-09-09 13:24:26 +00:00
await hass . async_block_till_done ( )
await hass . async_block_till_done ( )
# Assert all valid discovery topics are cleared
assert mqtt_mock . async_publish . call_count == 2
assert (
call ( " homeassistant/sensor/sbfspot_0/sbfspot_12345/config " , " " , 0 , True )
in mqtt_mock . async_publish . mock_calls
)
assert (
call ( " homeassistant/sensor/sbfspot_0/sbfspot_12345_1/config " , " " , 0 , True )
in mqtt_mock . async_publish . mock_calls
)
async def test_clean_up_registry_monitoring (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-09 07:19:06 +00:00
device_registry : dr . DeviceRegistry ,
2023-02-08 19:56:27 +00:00
tmp_path : Path ,
) - > None :
2022-09-09 13:24:26 +00:00
""" Test registry monitoring hook is removed after a reload. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2022-09-17 19:43:42 +00:00
hooks : dict = hass . data [ " mqtt " ] . discovery_registry_hooks
2022-09-09 13:24:26 +00:00
# discover an entity that is not enabled by default
config1 = {
" name " : " sbfspot_12345 " ,
" state_topic " : " homeassistant_test/sensor/sbfspot_0/sbfspot_12345/ " ,
" unique_id " : " sbfspot_12345 " ,
" enabled_by_default " : False ,
" device " : {
" identifiers " : [ " sbfspot_12345 " ] ,
" name " : " sbfspot_12345 " ,
" sw_version " : " 1.0 " ,
" connections " : [ [ " mac " , " 12:34:56:AB:CD:EF " ] ] ,
} ,
}
# Publish it config
# Since it is not enabled_by_default the sensor will not be loaded
# it should register a hook for monitoring the entiry registry
async_fire_mqtt_message (
hass ,
" homeassistant/sensor/sbfspot_0/sbfspot_12345/config " ,
json . dumps ( config1 ) ,
)
await hass . async_block_till_done ( )
assert len ( hooks ) == 1
# Publish it again no new monitor should be started
async_fire_mqtt_message (
hass ,
" homeassistant/sensor/sbfspot_0/sbfspot_12345/config " ,
json . dumps ( config1 ) ,
)
await hass . async_block_till_done ( )
assert len ( hooks ) == 1
# Verify device is created
2023-02-09 07:19:06 +00:00
device_entry = device_registry . async_get_device (
2023-07-13 17:39:25 +00:00
connections = { ( " mac " , " 12:34:56:AB:CD:EF " ) }
2023-02-09 07:19:06 +00:00
)
2022-09-09 13:24:26 +00:00
assert device_entry is not None
# Enload the entry
# The monitoring should be cleared
2023-03-16 14:57:01 +00:00
await help_test_unload_config_entry ( hass )
2022-09-09 13:24:26 +00:00
assert len ( hooks ) == 0
async def test_unique_id_collission_has_priority (
2023-02-08 19:56:27 +00:00
hass : HomeAssistant ,
2023-04-12 07:43:03 +00:00
mqtt_mock_entry : MqttMockHAClientGenerator ,
2023-02-09 07:19:06 +00:00
entity_registry : er . EntityRegistry ,
2023-02-08 19:56:27 +00:00
) - > None :
2023-01-30 18:15:11 +00:00
""" Test the unique_id collision detection has priority over registry disabled items. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2022-09-09 13:24:26 +00:00
config = {
" state_topic " : " homeassistant_test/sensor/sbfspot_0/sbfspot_12345/ " ,
" unique_id " : " sbfspot_12345 " ,
" enabled_by_default " : False ,
" device " : {
" identifiers " : [ " sbfspot_12345 " ] ,
2023-07-21 10:52:10 +00:00
" name " : " abc123 " ,
2022-09-09 13:24:26 +00:00
" sw_version " : " 1.0 " ,
" connections " : [ [ " mac " , " 12:34:56:AB:CD:EF " ] ] ,
} ,
}
# discover an entity that is not unique and disabled by default (part 1), will be added
config_not_unique1 = copy . deepcopy ( config )
config_not_unique1 [ " name " ] = " sbfspot_12345_1 "
config_not_unique1 [ " unique_id " ] = " not_unique "
async_fire_mqtt_message (
hass ,
" homeassistant/sensor/sbfspot_0/sbfspot_12345_1/config " ,
json . dumps ( config_not_unique1 ) ,
)
# discover an entity that is not unique (part 2), will not be added, and the registry entry is cleared
config_not_unique2 = copy . deepcopy ( config_not_unique1 )
config_not_unique2 [ " name " ] = " sbfspot_12345_2 "
async_fire_mqtt_message (
hass ,
" homeassistant/sensor/sbfspot_0/sbfspot_12345_2/config " ,
json . dumps ( config_not_unique2 ) ,
)
await hass . async_block_till_done ( )
2023-07-21 10:52:10 +00:00
assert hass . states . get ( " sensor.abc123_sbfspot_12345_1 " ) is None # not enabled
assert hass . states . get ( " sensor.abc123_sbfspot_12345_2 " ) is None # not unique
2022-09-09 13:24:26 +00:00
# Verify the first entity is created
2023-07-21 10:52:10 +00:00
assert entity_registry . async_get ( " sensor.abc123_sbfspot_12345_1 " ) is not None
2022-09-09 13:24:26 +00:00
# Verify the second entity is not created because it is not unique
2023-07-21 10:52:10 +00:00
assert entity_registry . async_get ( " sensor.abc123_sbfspot_12345_2 " ) is None
2023-01-30 18:15:11 +00:00
async def test_update_with_bad_config_not_breaks_discovery (
2023-04-12 07:43:03 +00:00
hass : HomeAssistant , mqtt_mock_entry : MqttMockHAClientGenerator
2023-01-30 18:15:11 +00:00
) - > None :
""" Test a bad update does not break discovery. """
2023-04-12 07:43:03 +00:00
await mqtt_mock_entry ( )
2023-01-30 18:15:11 +00:00
# discover a sensor
config1 = {
" name " : " sbfspot_12345 " ,
" state_topic " : " homeassistant_test/sensor/sbfspot_0/state " ,
}
async_fire_mqtt_message (
hass ,
" homeassistant/sensor/sbfspot_0/config " ,
json . dumps ( config1 ) ,
)
await hass . async_block_till_done ( )
assert hass . states . get ( " sensor.sbfspot_12345 " ) is not None
# update with a breaking config
config2 = {
" name " : " sbfspot_12345 " ,
" availability " : 1 ,
" state_topic " : " homeassistant_test/sensor/sbfspot_0/state " ,
}
async_fire_mqtt_message (
hass ,
" homeassistant/sensor/sbfspot_0/config " ,
json . dumps ( config2 ) ,
)
await hass . async_block_till_done ( )
# update the state topic
config3 = {
" name " : " sbfspot_12345 " ,
" state_topic " : " homeassistant_test/sensor/sbfspot_0/new_state_topic " ,
}
async_fire_mqtt_message (
hass ,
" homeassistant/sensor/sbfspot_0/config " ,
json . dumps ( config3 ) ,
)
await hass . async_block_till_done ( )
# Send an update for the state
async_fire_mqtt_message (
hass ,
" homeassistant_test/sensor/sbfspot_0/new_state_topic " ,
" new_value " ,
)
await hass . async_block_till_done ( )
2023-02-08 19:56:27 +00:00
state = hass . states . get ( " sensor.sbfspot_12345 " )
assert state and state . state == " new_value "
2024-05-06 20:32:46 +00:00
@pytest.mark.parametrize (
" signal_message " ,
[
MQTT_DISCOVERY_NEW ,
MQTT_DISCOVERY_NEW_COMPONENT ,
MQTT_DISCOVERY_UPDATED ,
MQTT_DISCOVERY_DONE ,
] ,
)
async def test_discovery_dispatcher_signal_type_messages (
hass : HomeAssistant , signal_message : SignalTypeFormat [ MQTTDiscoveryPayload ]
) - > None :
""" Test discovery dispatcher messages. """
domain_id_tuple = ( " sensor " , " very_unique " )
test_data = { " name " : " test " , " state_topic " : " test-topic " }
calls = [ ]
def _callback ( * args ) - > None :
calls . append ( * args )
unsub = async_dispatcher_connect (
hass , signal_message . format ( * domain_id_tuple ) , _callback
)
async_dispatcher_send ( hass , signal_message . format ( * domain_id_tuple ) , test_data )
await hass . async_block_till_done ( )
assert len ( calls ) == 1
assert calls [ 0 ] == test_data
unsub ( )