parent
eff9b2a1a0
commit
fb93b79b12
11 changed files with 547 additions and 3 deletions
|
@ -45,7 +45,8 @@ from homeassistant.util.async_ import run_callback_threadsafe
|
|||
from homeassistant.util.logging import catch_log_exception
|
||||
|
||||
# Loading the config flow file will register the flow
|
||||
from . import config_flow, discovery, server # noqa: F401 pylint: disable=unused-import
|
||||
from . import config_flow # noqa: F401 pylint: disable=unused-import
|
||||
from . import debug_info, discovery, server
|
||||
from .const import (
|
||||
ATTR_DISCOVERY_HASH,
|
||||
ATTR_DISCOVERY_TOPIC,
|
||||
|
@ -56,6 +57,7 @@ from .const import (
|
|||
DEFAULT_QOS,
|
||||
PROTOCOL_311,
|
||||
)
|
||||
from .debug_info import log_messages
|
||||
from .discovery import MQTT_DISCOVERY_UPDATED, clear_discovery_hash, set_discovery_hash
|
||||
from .models import Message, MessageCallbackType, PublishPayloadType
|
||||
from .subscription import async_subscribe_topics, async_unsubscribe_topics
|
||||
|
@ -513,6 +515,7 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
|
|||
|
||||
websocket_api.async_register_command(hass, websocket_subscribe)
|
||||
websocket_api.async_register_command(hass, websocket_remove_device)
|
||||
websocket_api.async_register_command(hass, websocket_mqtt_info)
|
||||
|
||||
if conf is None:
|
||||
# If we have a config entry, setup is done by that config entry.
|
||||
|
@ -1058,6 +1061,7 @@ class MqttAttributes(Entity):
|
|||
attr_tpl.hass = self.hass
|
||||
|
||||
@callback
|
||||
@log_messages(self.hass, self.entity_id)
|
||||
def attributes_message_received(msg: Message) -> None:
|
||||
try:
|
||||
payload = msg.payload
|
||||
|
@ -1122,6 +1126,7 @@ class MqttAvailability(Entity):
|
|||
"""(Re)Subscribe to topics."""
|
||||
|
||||
@callback
|
||||
@log_messages(self.hass, self.entity_id)
|
||||
def availability_message_received(msg: Message) -> None:
|
||||
"""Handle a new received MQTT availability message."""
|
||||
if msg.payload == self._avail_config[CONF_PAYLOAD_AVAILABLE]:
|
||||
|
@ -1207,6 +1212,7 @@ class MqttDiscoveryUpdate(Entity):
|
|||
_LOGGER.info(
|
||||
"Got update for entity with hash: %s '%s'", discovery_hash, payload,
|
||||
)
|
||||
debug_info.update_entity_discovery_data(self.hass, payload, self.entity_id)
|
||||
if not payload:
|
||||
# Empty payload: Remove component
|
||||
_LOGGER.info("Removing component: %s", self.entity_id)
|
||||
|
@ -1219,6 +1225,9 @@ class MqttDiscoveryUpdate(Entity):
|
|||
await self._discovery_update(payload)
|
||||
|
||||
if discovery_hash:
|
||||
debug_info.add_entity_discovery_data(
|
||||
self.hass, self._discovery_data, self.entity_id
|
||||
)
|
||||
# Set in case the entity has been removed and is re-added
|
||||
set_discovery_hash(self.hass, discovery_hash)
|
||||
self._remove_signal = async_dispatcher_connect(
|
||||
|
@ -1242,6 +1251,7 @@ class MqttDiscoveryUpdate(Entity):
|
|||
def _cleanup_on_remove(self) -> None:
|
||||
"""Stop listening to signal and cleanup discovery data."""
|
||||
if self._discovery_data and not self._removed_from_hass:
|
||||
debug_info.remove_entity_data(self.hass, self.entity_id)
|
||||
clear_discovery_hash(self.hass, self._discovery_data[ATTR_DISCOVERY_HASH])
|
||||
self._removed_from_hass = True
|
||||
|
||||
|
@ -1303,6 +1313,18 @@ class MqttEntityDeviceInfo(Entity):
|
|||
return device_info_from_config(self._device_config)
|
||||
|
||||
|
||||
@websocket_api.websocket_command(
|
||||
{vol.Required("type"): "mqtt/device/debug_info", vol.Required("device_id"): str}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
async def websocket_mqtt_info(hass, connection, msg):
|
||||
"""Get MQTT debug info for device."""
|
||||
device_id = msg["device_id"]
|
||||
mqtt_info = await debug_info.info_for_device(hass, device_id)
|
||||
|
||||
connection.send_result(msg["id"], mqtt_info)
|
||||
|
||||
|
||||
@websocket_api.websocket_command(
|
||||
{vol.Required("type"): "mqtt/device/remove", vol.Required("device_id"): str}
|
||||
)
|
||||
|
|
|
@ -4,6 +4,7 @@ CONF_DISCOVERY = "discovery"
|
|||
DEFAULT_DISCOVERY = False
|
||||
|
||||
ATTR_DISCOVERY_HASH = "discovery_hash"
|
||||
ATTR_DISCOVERY_PAYLOAD = "discovery_payload"
|
||||
ATTR_DISCOVERY_TOPIC = "discovery_topic"
|
||||
CONF_STATE_TOPIC = "state_topic"
|
||||
PROTOCOL_311 = "3.1.1"
|
||||
|
|
146
homeassistant/components/mqtt/debug_info.py
Normal file
146
homeassistant/components/mqtt/debug_info.py
Normal file
|
@ -0,0 +1,146 @@
|
|||
"""Helper to handle a set of topics to subscribe to."""
|
||||
from collections import deque
|
||||
from functools import wraps
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
from .const import ATTR_DISCOVERY_PAYLOAD, ATTR_DISCOVERY_TOPIC
|
||||
from .models import MessageCallbackType
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_MQTT_DEBUG_INFO = "mqtt_debug_info"
|
||||
STORED_MESSAGES = 10
|
||||
|
||||
|
||||
def log_messages(hass: HomeAssistantType, entity_id: str) -> MessageCallbackType:
|
||||
"""Wrap an MQTT message callback to support message logging."""
|
||||
|
||||
def _log_message(msg):
|
||||
"""Log message."""
|
||||
debug_info = hass.data[DATA_MQTT_DEBUG_INFO]
|
||||
messages = debug_info["entities"][entity_id]["topics"][msg.topic]
|
||||
messages.append(msg.payload)
|
||||
|
||||
def _decorator(msg_callback: MessageCallbackType):
|
||||
@wraps(msg_callback)
|
||||
def wrapper(msg: Any) -> None:
|
||||
"""Log message."""
|
||||
_log_message(msg)
|
||||
msg_callback(msg)
|
||||
|
||||
setattr(wrapper, "__entity_id", entity_id)
|
||||
return wrapper
|
||||
|
||||
return _decorator
|
||||
|
||||
|
||||
def add_topic(hass, message_callback, topic):
|
||||
"""Prepare debug data for topic."""
|
||||
entity_id = getattr(message_callback, "__entity_id", None)
|
||||
if entity_id:
|
||||
debug_info = hass.data.setdefault(
|
||||
DATA_MQTT_DEBUG_INFO, {"entities": {}, "triggers": {}}
|
||||
)
|
||||
entity_info = debug_info["entities"].setdefault(
|
||||
entity_id, {"topics": {}, "discovery_data": {}}
|
||||
)
|
||||
entity_info["topics"][topic] = deque([], STORED_MESSAGES)
|
||||
|
||||
|
||||
def remove_topic(hass, message_callback, topic):
|
||||
"""Remove debug data for topic."""
|
||||
entity_id = getattr(message_callback, "__entity_id", None)
|
||||
if entity_id and entity_id in hass.data[DATA_MQTT_DEBUG_INFO]["entities"]:
|
||||
hass.data[DATA_MQTT_DEBUG_INFO]["entities"][entity_id]["topics"].pop(topic)
|
||||
|
||||
|
||||
def add_entity_discovery_data(hass, discovery_data, entity_id):
|
||||
"""Add discovery data."""
|
||||
debug_info = hass.data.setdefault(
|
||||
DATA_MQTT_DEBUG_INFO, {"entities": {}, "triggers": {}}
|
||||
)
|
||||
entity_info = debug_info["entities"].setdefault(
|
||||
entity_id, {"topics": {}, "discovery_data": {}}
|
||||
)
|
||||
entity_info["discovery_data"] = discovery_data
|
||||
|
||||
|
||||
def update_entity_discovery_data(hass, discovery_payload, entity_id):
|
||||
"""Update discovery data."""
|
||||
entity_info = hass.data[DATA_MQTT_DEBUG_INFO]["entities"][entity_id]
|
||||
entity_info["discovery_data"][ATTR_DISCOVERY_PAYLOAD] = discovery_payload
|
||||
|
||||
|
||||
def remove_entity_data(hass, entity_id):
|
||||
"""Remove discovery data."""
|
||||
hass.data[DATA_MQTT_DEBUG_INFO]["entities"].pop(entity_id)
|
||||
|
||||
|
||||
def add_trigger_discovery_data(hass, discovery_hash, discovery_data, device_id):
|
||||
"""Add discovery data."""
|
||||
debug_info = hass.data.setdefault(
|
||||
DATA_MQTT_DEBUG_INFO, {"entities": {}, "triggers": {}}
|
||||
)
|
||||
debug_info["triggers"][discovery_hash] = {
|
||||
"device_id": device_id,
|
||||
"discovery_data": discovery_data,
|
||||
}
|
||||
|
||||
|
||||
def update_trigger_discovery_data(hass, discovery_hash, discovery_payload):
|
||||
"""Update discovery data."""
|
||||
trigger_info = hass.data[DATA_MQTT_DEBUG_INFO]["triggers"][discovery_hash]
|
||||
trigger_info["discovery_data"][ATTR_DISCOVERY_PAYLOAD] = discovery_payload
|
||||
|
||||
|
||||
def remove_trigger_discovery_data(hass, discovery_hash):
|
||||
"""Remove discovery data."""
|
||||
hass.data[DATA_MQTT_DEBUG_INFO]["triggers"][discovery_hash]["discovery_data"] = None
|
||||
|
||||
|
||||
async def info_for_device(hass, device_id):
|
||||
"""Get debug info for a device."""
|
||||
mqtt_info = {"entities": [], "triggers": []}
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
entries = hass.helpers.entity_registry.async_entries_for_device(
|
||||
entity_registry, device_id
|
||||
)
|
||||
mqtt_debug_info = hass.data.setdefault(
|
||||
DATA_MQTT_DEBUG_INFO, {"entities": {}, "triggers": {}}
|
||||
)
|
||||
for entry in entries:
|
||||
if entry.entity_id not in mqtt_debug_info["entities"]:
|
||||
continue
|
||||
|
||||
entity_info = mqtt_debug_info["entities"][entry.entity_id]
|
||||
topics = [
|
||||
{"topic": topic, "messages": list(messages)}
|
||||
for topic, messages in entity_info["topics"].items()
|
||||
]
|
||||
discovery_data = {
|
||||
"topic": entity_info["discovery_data"].get(ATTR_DISCOVERY_TOPIC, ""),
|
||||
"payload": entity_info["discovery_data"].get(ATTR_DISCOVERY_PAYLOAD, ""),
|
||||
}
|
||||
mqtt_info["entities"].append(
|
||||
{
|
||||
"entity_id": entry.entity_id,
|
||||
"topics": topics,
|
||||
"discovery_data": discovery_data,
|
||||
}
|
||||
)
|
||||
|
||||
for trigger in mqtt_debug_info["triggers"].values():
|
||||
if trigger["device_id"] != device_id:
|
||||
continue
|
||||
|
||||
discovery_data = {
|
||||
"topic": trigger["discovery_data"][ATTR_DISCOVERY_TOPIC],
|
||||
"payload": trigger["discovery_data"][ATTR_DISCOVERY_PAYLOAD],
|
||||
}
|
||||
mqtt_info["triggers"].append({"discovery_data": discovery_data})
|
||||
|
||||
return mqtt_info
|
|
@ -26,6 +26,7 @@ from . import (
|
|||
CONF_QOS,
|
||||
DOMAIN,
|
||||
cleanup_device_registry,
|
||||
debug_info,
|
||||
)
|
||||
from .discovery import MQTT_DISCOVERY_UPDATED, clear_discovery_hash
|
||||
|
||||
|
@ -183,6 +184,7 @@ async def async_setup_trigger(hass, config, config_entry, discovery_data):
|
|||
if not payload:
|
||||
# Empty payload: Remove trigger
|
||||
_LOGGER.info("Removing trigger: %s", discovery_hash)
|
||||
debug_info.remove_trigger_discovery_data(hass, discovery_hash)
|
||||
if discovery_id in hass.data[DEVICE_TRIGGERS]:
|
||||
device_trigger = hass.data[DEVICE_TRIGGERS][discovery_id]
|
||||
device_trigger.detach_trigger()
|
||||
|
@ -192,6 +194,7 @@ async def async_setup_trigger(hass, config, config_entry, discovery_data):
|
|||
else:
|
||||
# Non-empty payload: Update trigger
|
||||
_LOGGER.info("Updating trigger: %s", discovery_hash)
|
||||
debug_info.update_trigger_discovery_data(hass, discovery_hash, payload)
|
||||
config = TRIGGER_DISCOVERY_SCHEMA(payload)
|
||||
await _update_device(hass, config_entry, config)
|
||||
device_trigger = hass.data[DEVICE_TRIGGERS][discovery_id]
|
||||
|
@ -230,6 +233,9 @@ async def async_setup_trigger(hass, config, config_entry, discovery_data):
|
|||
await hass.data[DEVICE_TRIGGERS][discovery_id].update_trigger(
|
||||
config, discovery_hash, remove_signal
|
||||
)
|
||||
debug_info.add_trigger_discovery_data(
|
||||
hass, discovery_hash, discovery_data, device.id
|
||||
)
|
||||
|
||||
|
||||
async def async_device_removed(hass: HomeAssistant, device_id: str):
|
||||
|
@ -241,6 +247,7 @@ async def async_device_removed(hass: HomeAssistant, device_id: str):
|
|||
discovery_hash = device_trigger.discovery_data[ATTR_DISCOVERY_HASH]
|
||||
discovery_topic = device_trigger.discovery_data[ATTR_DISCOVERY_TOPIC]
|
||||
|
||||
debug_info.remove_trigger_discovery_data(hass, discovery_hash)
|
||||
device_trigger.detach_trigger()
|
||||
clear_discovery_hash(hass, discovery_hash)
|
||||
device_trigger.remove_signal()
|
||||
|
|
|
@ -11,7 +11,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
|
|||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
from .abbreviations import ABBREVIATIONS, DEVICE_ABBREVIATIONS
|
||||
from .const import ATTR_DISCOVERY_HASH, ATTR_DISCOVERY_TOPIC
|
||||
from .const import ATTR_DISCOVERY_HASH, ATTR_DISCOVERY_PAYLOAD, ATTR_DISCOVERY_TOPIC
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -135,6 +135,7 @@ async def async_start(
|
|||
setattr(payload, "__configuration_source__", f"MQTT (topic: '{topic}')")
|
||||
discovery_data = {
|
||||
ATTR_DISCOVERY_HASH: discovery_hash,
|
||||
ATTR_DISCOVERY_PAYLOAD: payload,
|
||||
ATTR_DISCOVERY_TOPIC: topic,
|
||||
}
|
||||
setattr(payload, "discovery_data", discovery_data)
|
||||
|
|
|
@ -35,6 +35,7 @@ from . import (
|
|||
MqttEntityDeviceInfo,
|
||||
subscription,
|
||||
)
|
||||
from .debug_info import log_messages
|
||||
from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -137,6 +138,7 @@ class MqttSensor(
|
|||
template.hass = self.hass
|
||||
|
||||
@callback
|
||||
@log_messages(self.hass, self.entity_id)
|
||||
def message_received(msg):
|
||||
"""Handle new MQTT messages."""
|
||||
payload = msg.payload
|
||||
|
|
|
@ -8,6 +8,7 @@ from homeassistant.components import mqtt
|
|||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
from homeassistant.loader import bind_hass
|
||||
|
||||
from . import debug_info
|
||||
from .const import DEFAULT_QOS
|
||||
from .models import MessageCallbackType
|
||||
|
||||
|
@ -18,6 +19,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||
class EntitySubscription:
|
||||
"""Class to hold data about an active entity topic subscription."""
|
||||
|
||||
hass = attr.ib(type=HomeAssistantType)
|
||||
topic = attr.ib(type=str)
|
||||
message_callback = attr.ib(type=MessageCallbackType)
|
||||
unsubscribe_callback = attr.ib(type=Optional[Callable[[], None]])
|
||||
|
@ -31,11 +33,16 @@ class EntitySubscription:
|
|||
|
||||
if other is not None and other.unsubscribe_callback is not None:
|
||||
other.unsubscribe_callback()
|
||||
# Clear debug data if it exists
|
||||
debug_info.remove_topic(self.hass, other.message_callback, other.topic)
|
||||
|
||||
if self.topic is None:
|
||||
# We were asked to remove the subscription or not to create it
|
||||
return
|
||||
|
||||
# Prepare debug data
|
||||
debug_info.add_topic(self.hass, self.message_callback, self.topic)
|
||||
|
||||
self.unsubscribe_callback = await mqtt.async_subscribe(
|
||||
hass, self.topic, self.message_callback, self.qos, self.encoding
|
||||
)
|
||||
|
@ -77,6 +84,7 @@ async def async_subscribe_topics(
|
|||
unsubscribe_callback=None,
|
||||
qos=value.get("qos", DEFAULT_QOS),
|
||||
encoding=value.get("encoding", "utf-8"),
|
||||
hass=hass,
|
||||
)
|
||||
# Get the current subscription state
|
||||
current = current_subscriptions.pop(key, None)
|
||||
|
@ -87,6 +95,8 @@ async def async_subscribe_topics(
|
|||
for remaining in current_subscriptions.values():
|
||||
if remaining.unsubscribe_callback is not None:
|
||||
remaining.unsubscribe_callback()
|
||||
# Clear debug data if it exists
|
||||
debug_info.remove_topic(hass, remaining.message_callback, remaining.topic)
|
||||
|
||||
return new_state
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import json
|
|||
from unittest.mock import ANY
|
||||
|
||||
from homeassistant.components import mqtt
|
||||
from homeassistant.components.mqtt import debug_info
|
||||
from homeassistant.components.mqtt.discovery import async_start
|
||||
from homeassistant.const import ATTR_ASSUMED_STATE, STATE_UNAVAILABLE
|
||||
|
||||
|
@ -519,3 +520,232 @@ async def help_test_entity_id_update_discovery_update(
|
|||
async_fire_mqtt_message(hass, f"{topic}_2", "online")
|
||||
state = hass.states.get(f"{domain}.milk")
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def help_test_entity_debug_info(hass, mqtt_mock, domain, config):
|
||||
"""Test debug_info.
|
||||
|
||||
This is a test helper for MQTT debug_info.
|
||||
"""
|
||||
# Add device settings to config
|
||||
config = copy.deepcopy(config[domain])
|
||||
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
|
||||
config["unique_id"] = "veryunique"
|
||||
|
||||
entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
||||
entry.add_to_hass(hass)
|
||||
await async_start(hass, "homeassistant", {}, entry)
|
||||
registry = await hass.helpers.device_registry.async_get_registry()
|
||||
|
||||
data = json.dumps(config)
|
||||
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
device = registry.async_get_device({("mqtt", "helloworld")}, set())
|
||||
assert device is not None
|
||||
|
||||
debug_info_data = await debug_info.info_for_device(hass, device.id)
|
||||
assert len(debug_info_data["entities"]) == 1
|
||||
assert (
|
||||
debug_info_data["entities"][0]["discovery_data"]["topic"]
|
||||
== f"homeassistant/{domain}/bla/config"
|
||||
)
|
||||
assert debug_info_data["entities"][0]["discovery_data"]["payload"] == config
|
||||
assert len(debug_info_data["entities"][0]["topics"]) == 1
|
||||
assert {"topic": "test-topic", "messages": []} in debug_info_data["entities"][0][
|
||||
"topics"
|
||||
]
|
||||
assert len(debug_info_data["triggers"]) == 0
|
||||
|
||||
|
||||
async def help_test_entity_debug_info_max_messages(hass, mqtt_mock, domain, config):
|
||||
"""Test debug_info message overflow.
|
||||
|
||||
This is a test helper for MQTT debug_info.
|
||||
"""
|
||||
# Add device settings to config
|
||||
config = copy.deepcopy(config[domain])
|
||||
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
|
||||
config["unique_id"] = "veryunique"
|
||||
|
||||
entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
||||
entry.add_to_hass(hass)
|
||||
await async_start(hass, "homeassistant", {}, entry)
|
||||
registry = await hass.helpers.device_registry.async_get_registry()
|
||||
|
||||
data = json.dumps(config)
|
||||
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
device = registry.async_get_device({("mqtt", "helloworld")}, set())
|
||||
assert device is not None
|
||||
|
||||
debug_info_data = await debug_info.info_for_device(hass, device.id)
|
||||
assert len(debug_info_data["entities"][0]["topics"]) == 1
|
||||
assert {"topic": "test-topic", "messages": []} in debug_info_data["entities"][0][
|
||||
"topics"
|
||||
]
|
||||
|
||||
for i in range(0, debug_info.STORED_MESSAGES + 1):
|
||||
async_fire_mqtt_message(hass, "test-topic", f"{i}")
|
||||
|
||||
debug_info_data = await debug_info.info_for_device(hass, device.id)
|
||||
assert len(debug_info_data["entities"][0]["topics"]) == 1
|
||||
assert (
|
||||
len(debug_info_data["entities"][0]["topics"][0]["messages"])
|
||||
== debug_info.STORED_MESSAGES
|
||||
)
|
||||
messages = [f"{i}" for i in range(1, debug_info.STORED_MESSAGES + 1)]
|
||||
assert {"topic": "test-topic", "messages": messages} in debug_info_data["entities"][
|
||||
0
|
||||
]["topics"]
|
||||
|
||||
|
||||
async def help_test_entity_debug_info_message(
|
||||
hass, mqtt_mock, domain, config, topic=None, payload=None
|
||||
):
|
||||
"""Test debug_info message overflow.
|
||||
|
||||
This is a test helper for MQTT debug_info.
|
||||
"""
|
||||
# Add device settings to config
|
||||
config = copy.deepcopy(config[domain])
|
||||
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
|
||||
config["unique_id"] = "veryunique"
|
||||
|
||||
if topic is None:
|
||||
# Add default topic to config
|
||||
config["state_topic"] = "state-topic"
|
||||
topic = "state-topic"
|
||||
|
||||
if payload is None:
|
||||
payload = "ON"
|
||||
|
||||
entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
||||
entry.add_to_hass(hass)
|
||||
await async_start(hass, "homeassistant", {}, entry)
|
||||
registry = await hass.helpers.device_registry.async_get_registry()
|
||||
|
||||
data = json.dumps(config)
|
||||
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
device = registry.async_get_device({("mqtt", "helloworld")}, set())
|
||||
assert device is not None
|
||||
|
||||
debug_info_data = await debug_info.info_for_device(hass, device.id)
|
||||
assert len(debug_info_data["entities"][0]["topics"]) >= 1
|
||||
assert {"topic": topic, "messages": []} in debug_info_data["entities"][0]["topics"]
|
||||
|
||||
async_fire_mqtt_message(hass, topic, payload)
|
||||
|
||||
debug_info_data = await debug_info.info_for_device(hass, device.id)
|
||||
assert len(debug_info_data["entities"][0]["topics"]) >= 1
|
||||
assert {"topic": topic, "messages": [payload]} in debug_info_data["entities"][0][
|
||||
"topics"
|
||||
]
|
||||
|
||||
|
||||
async def help_test_entity_debug_info_remove(hass, mqtt_mock, domain, config):
|
||||
"""Test debug_info.
|
||||
|
||||
This is a test helper for MQTT debug_info.
|
||||
"""
|
||||
# Add device settings to config
|
||||
config = copy.deepcopy(config[domain])
|
||||
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
|
||||
config["unique_id"] = "veryunique"
|
||||
|
||||
entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
||||
entry.add_to_hass(hass)
|
||||
await async_start(hass, "homeassistant", {}, entry)
|
||||
registry = await hass.helpers.device_registry.async_get_registry()
|
||||
|
||||
data = json.dumps(config)
|
||||
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
device = registry.async_get_device({("mqtt", "helloworld")}, set())
|
||||
assert device is not None
|
||||
|
||||
debug_info_data = await debug_info.info_for_device(hass, device.id)
|
||||
assert len(debug_info_data["entities"]) == 1
|
||||
assert (
|
||||
debug_info_data["entities"][0]["discovery_data"]["topic"]
|
||||
== f"homeassistant/{domain}/bla/config"
|
||||
)
|
||||
assert debug_info_data["entities"][0]["discovery_data"]["payload"] == config
|
||||
assert len(debug_info_data["entities"][0]["topics"]) == 1
|
||||
assert {"topic": "test-topic", "messages": []} in debug_info_data["entities"][0][
|
||||
"topics"
|
||||
]
|
||||
assert len(debug_info_data["triggers"]) == 0
|
||||
assert debug_info_data["entities"][0]["entity_id"] == f"{domain}.test"
|
||||
entity_id = debug_info_data["entities"][0]["entity_id"]
|
||||
|
||||
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", "")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
debug_info_data = await debug_info.info_for_device(hass, device.id)
|
||||
assert len(debug_info_data["entities"]) == 0
|
||||
assert len(debug_info_data["triggers"]) == 0
|
||||
assert entity_id not in hass.data[debug_info.DATA_MQTT_DEBUG_INFO]["entities"]
|
||||
|
||||
|
||||
async def help_test_entity_debug_info_update_entity_id(hass, mqtt_mock, domain, config):
|
||||
"""Test debug_info.
|
||||
|
||||
This is a test helper for MQTT debug_info.
|
||||
"""
|
||||
# Add device settings to config
|
||||
config = copy.deepcopy(config[domain])
|
||||
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
|
||||
config["unique_id"] = "veryunique"
|
||||
|
||||
entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
||||
entry.add_to_hass(hass)
|
||||
await async_start(hass, "homeassistant", {}, entry)
|
||||
dev_registry = await hass.helpers.device_registry.async_get_registry()
|
||||
ent_registry = mock_registry(hass, {})
|
||||
|
||||
data = json.dumps(config)
|
||||
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
device = dev_registry.async_get_device({("mqtt", "helloworld")}, set())
|
||||
assert device is not None
|
||||
|
||||
debug_info_data = await debug_info.info_for_device(hass, device.id)
|
||||
assert len(debug_info_data["entities"]) == 1
|
||||
assert (
|
||||
debug_info_data["entities"][0]["discovery_data"]["topic"]
|
||||
== f"homeassistant/{domain}/bla/config"
|
||||
)
|
||||
assert debug_info_data["entities"][0]["discovery_data"]["payload"] == config
|
||||
assert debug_info_data["entities"][0]["entity_id"] == f"{domain}.test"
|
||||
assert len(debug_info_data["entities"][0]["topics"]) == 1
|
||||
assert {"topic": "test-topic", "messages": []} in debug_info_data["entities"][0][
|
||||
"topics"
|
||||
]
|
||||
assert len(debug_info_data["triggers"]) == 0
|
||||
|
||||
ent_registry.async_update_entity(f"{domain}.test", new_entity_id=f"{domain}.milk")
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
debug_info_data = await debug_info.info_for_device(hass, device.id)
|
||||
assert len(debug_info_data["entities"]) == 1
|
||||
assert (
|
||||
debug_info_data["entities"][0]["discovery_data"]["topic"]
|
||||
== f"homeassistant/{domain}/bla/config"
|
||||
)
|
||||
assert debug_info_data["entities"][0]["discovery_data"]["payload"] == config
|
||||
assert debug_info_data["entities"][0]["entity_id"] == f"{domain}.milk"
|
||||
assert len(debug_info_data["entities"][0]["topics"]) == 1
|
||||
assert {"topic": "test-topic", "messages": []} in debug_info_data["entities"][0][
|
||||
"topics"
|
||||
]
|
||||
assert len(debug_info_data["triggers"]) == 0
|
||||
assert (
|
||||
f"{domain}.test" not in hass.data[debug_info.DATA_MQTT_DEBUG_INFO]["entities"]
|
||||
)
|
||||
|
|
|
@ -4,7 +4,7 @@ import json
|
|||
import pytest
|
||||
|
||||
import homeassistant.components.automation as automation
|
||||
from homeassistant.components.mqtt import DOMAIN
|
||||
from homeassistant.components.mqtt import DOMAIN, debug_info
|
||||
from homeassistant.components.mqtt.device_trigger import async_attach_trigger
|
||||
from homeassistant.components.mqtt.discovery import async_start
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
@ -1104,3 +1104,44 @@ async def test_cleanup_device_with_entity2(hass, device_reg, entity_reg, mqtt_mo
|
|||
# Verify device registry entry is cleared
|
||||
device_entry = device_reg.async_get_device({("mqtt", "helloworld")}, set())
|
||||
assert device_entry is None
|
||||
|
||||
|
||||
async def test_trigger_debug_info(hass, mqtt_mock):
|
||||
"""Test debug_info.
|
||||
|
||||
This is a test helper for MQTT debug_info.
|
||||
"""
|
||||
entry = MockConfigEntry(domain=DOMAIN)
|
||||
entry.add_to_hass(hass)
|
||||
await async_start(hass, "homeassistant", {}, entry)
|
||||
registry = await hass.helpers.device_registry.async_get_registry()
|
||||
|
||||
config = {
|
||||
"platform": "mqtt",
|
||||
"automation_type": "trigger",
|
||||
"topic": "test-topic",
|
||||
"type": "foo",
|
||||
"subtype": "bar",
|
||||
"device": {
|
||||
"connections": [["mac", "02:5b:26:a8:dc:12"]],
|
||||
"manufacturer": "Whatever",
|
||||
"name": "Beer",
|
||||
"model": "Glass",
|
||||
"sw_version": "0.1-beta",
|
||||
},
|
||||
}
|
||||
data = json.dumps(config)
|
||||
async_fire_mqtt_message(hass, "homeassistant/device_automation/bla/config", data)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
device = registry.async_get_device(set(), {("mac", "02:5b:26:a8:dc:12")})
|
||||
assert device is not None
|
||||
|
||||
debug_info_data = await debug_info.info_for_device(hass, device.id)
|
||||
assert len(debug_info_data["entities"]) == 0
|
||||
assert len(debug_info_data["triggers"]) == 1
|
||||
assert (
|
||||
debug_info_data["triggers"][0]["discovery_data"]["topic"]
|
||||
== "homeassistant/device_automation/bla/config"
|
||||
)
|
||||
assert debug_info_data["triggers"][0]["discovery_data"]["payload"] == config
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""The tests for the MQTT component."""
|
||||
from datetime import timedelta
|
||||
import json
|
||||
import ssl
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
@ -934,3 +935,48 @@ async def test_mqtt_ws_remove_non_mqtt_device(
|
|||
response = await client.receive_json()
|
||||
assert not response["success"]
|
||||
assert response["error"]["code"] == websocket_api.const.ERR_NOT_FOUND
|
||||
|
||||
|
||||
async def test_mqtt_ws_get_device_debug_info(
|
||||
hass, device_reg, hass_ws_client, mqtt_mock
|
||||
):
|
||||
"""Test MQTT websocket device debug info."""
|
||||
config_entry = MockConfigEntry(domain=mqtt.DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
await async_start(hass, "homeassistant", {}, config_entry)
|
||||
|
||||
config = {
|
||||
"device": {"identifiers": ["0AFFD2"]},
|
||||
"platform": "mqtt",
|
||||
"state_topic": "foobar/sensor",
|
||||
"unique_id": "unique",
|
||||
}
|
||||
data = json.dumps(config)
|
||||
|
||||
async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Verify device entry is created
|
||||
device_entry = device_reg.async_get_device({("mqtt", "0AFFD2")}, set())
|
||||
assert device_entry is not None
|
||||
|
||||
client = await hass_ws_client(hass)
|
||||
await client.send_json(
|
||||
{"id": 5, "type": "mqtt/device/debug_info", "device_id": device_entry.id}
|
||||
)
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
expected_result = {
|
||||
"entities": [
|
||||
{
|
||||
"entity_id": "sensor.mqtt_sensor",
|
||||
"topics": [{"topic": "foobar/sensor", "messages": []}],
|
||||
"discovery_data": {
|
||||
"payload": config,
|
||||
"topic": "homeassistant/sensor/bla/config",
|
||||
},
|
||||
}
|
||||
],
|
||||
"triggers": [],
|
||||
}
|
||||
assert response["result"] == expected_result
|
||||
|
|
|
@ -19,6 +19,11 @@ from .test_common import (
|
|||
help_test_discovery_removal,
|
||||
help_test_discovery_update,
|
||||
help_test_discovery_update_attr,
|
||||
help_test_entity_debug_info,
|
||||
help_test_entity_debug_info_max_messages,
|
||||
help_test_entity_debug_info_message,
|
||||
help_test_entity_debug_info_remove,
|
||||
help_test_entity_debug_info_update_entity_id,
|
||||
help_test_entity_device_info_remove,
|
||||
help_test_entity_device_info_update,
|
||||
help_test_entity_device_info_with_connection,
|
||||
|
@ -437,3 +442,36 @@ async def test_entity_device_info_with_hub(hass, mqtt_mock):
|
|||
device = registry.async_get_device({("mqtt", "helloworld")}, set())
|
||||
assert device is not None
|
||||
assert device.via_device_id == hub.id
|
||||
|
||||
|
||||
async def test_entity_debug_info(hass, mqtt_mock):
|
||||
"""Test MQTT sensor debug info."""
|
||||
await help_test_entity_debug_info(hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG)
|
||||
|
||||
|
||||
async def test_entity_debug_info_max_messages(hass, mqtt_mock):
|
||||
"""Test MQTT sensor debug info."""
|
||||
await help_test_entity_debug_info_max_messages(
|
||||
hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG
|
||||
)
|
||||
|
||||
|
||||
async def test_entity_debug_info_message(hass, mqtt_mock):
|
||||
"""Test MQTT debug info."""
|
||||
await help_test_entity_debug_info_message(
|
||||
hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG
|
||||
)
|
||||
|
||||
|
||||
async def test_entity_debug_info_remove(hass, mqtt_mock):
|
||||
"""Test MQTT sensor debug info."""
|
||||
await help_test_entity_debug_info_remove(
|
||||
hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG
|
||||
)
|
||||
|
||||
|
||||
async def test_entity_debug_info_update_entity_id(hass, mqtt_mock):
|
||||
"""Test MQTT sensor debug info."""
|
||||
await help_test_entity_debug_info_update_entity_id(
|
||||
hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue