Log transmitted MQTT messages (#65550)

This commit is contained in:
Erik Montnemery 2022-02-04 17:35:32 +01:00 committed by GitHub
parent a95988c970
commit 8245ff7473
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 341 additions and 177 deletions

View file

@ -1184,7 +1184,7 @@ def _matcher_for_topic(subscription: str) -> Any:
async def websocket_mqtt_info(hass, connection, msg): async def websocket_mqtt_info(hass, connection, msg):
"""Get MQTT debug info for device.""" """Get MQTT debug info for device."""
device_id = msg["device_id"] device_id = msg["device_id"]
mqtt_info = await debug_info.info_for_device(hass, device_id) mqtt_info = debug_info.info_for_device(hass, device_id)
connection.send_result(msg["id"], mqtt_info) connection.send_result(msg["id"], mqtt_info)

View file

@ -328,8 +328,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity):
"""Publish via mqtt.""" """Publish via mqtt."""
variables = {"action": action, "code": code} variables = {"action": action, "code": code}
payload = self._command_template(None, variables=variables) payload = self._command_template(None, variables=variables)
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config[CONF_COMMAND_TOPIC], self._config[CONF_COMMAND_TOPIC],
payload, payload,
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -112,8 +112,7 @@ class MqttButton(MqttEntity, ButtonEntity):
This method is a coroutine. This method is a coroutine.
""" """
payload = self._command_template(self._config[CONF_PAYLOAD_PRESS]) payload = self._command_template(self._config[CONF_PAYLOAD_PRESS])
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config[CONF_COMMAND_TOPIC], self._config[CONF_COMMAND_TOPIC],
payload, payload,
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -708,8 +708,7 @@ class MqttClimate(MqttEntity, ClimateEntity):
async def _publish(self, topic, payload): async def _publish(self, topic, payload):
if self._topic[topic] is not None: if self._topic[topic] is not None:
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[topic], self._topic[topic],
payload, payload,
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -534,8 +534,7 @@ class MqttCover(MqttEntity, CoverEntity):
This method is a coroutine. This method is a coroutine.
""" """
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config.get(CONF_COMMAND_TOPIC), self._config.get(CONF_COMMAND_TOPIC),
self._config[CONF_PAYLOAD_OPEN], self._config[CONF_PAYLOAD_OPEN],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -556,8 +555,7 @@ class MqttCover(MqttEntity, CoverEntity):
This method is a coroutine. This method is a coroutine.
""" """
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config.get(CONF_COMMAND_TOPIC), self._config.get(CONF_COMMAND_TOPIC),
self._config[CONF_PAYLOAD_CLOSE], self._config[CONF_PAYLOAD_CLOSE],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -578,8 +576,7 @@ class MqttCover(MqttEntity, CoverEntity):
This method is a coroutine. This method is a coroutine.
""" """
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config.get(CONF_COMMAND_TOPIC), self._config.get(CONF_COMMAND_TOPIC),
self._config[CONF_PAYLOAD_STOP], self._config[CONF_PAYLOAD_STOP],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -599,8 +596,7 @@ class MqttCover(MqttEntity, CoverEntity):
"tilt_max": self._config.get(CONF_TILT_MAX), "tilt_max": self._config.get(CONF_TILT_MAX),
} }
tilt_payload = self._set_tilt_template(tilt_open_position, variables=variables) tilt_payload = self._set_tilt_template(tilt_open_position, variables=variables)
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config.get(CONF_TILT_COMMAND_TOPIC), self._config.get(CONF_TILT_COMMAND_TOPIC),
tilt_payload, tilt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -627,8 +623,7 @@ class MqttCover(MqttEntity, CoverEntity):
tilt_payload = self._set_tilt_template( tilt_payload = self._set_tilt_template(
tilt_closed_position, variables=variables tilt_closed_position, variables=variables
) )
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config.get(CONF_TILT_COMMAND_TOPIC), self._config.get(CONF_TILT_COMMAND_TOPIC),
tilt_payload, tilt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -657,8 +652,7 @@ class MqttCover(MqttEntity, CoverEntity):
} }
tilt = self._set_tilt_template(tilt, variables=variables) tilt = self._set_tilt_template(tilt, variables=variables)
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config.get(CONF_TILT_COMMAND_TOPIC), self._config.get(CONF_TILT_COMMAND_TOPIC),
tilt, tilt,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -685,8 +679,7 @@ class MqttCover(MqttEntity, CoverEntity):
} }
position = self._set_position_template(position, variables=variables) position = self._set_position_template(position, variables=variables)
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config.get(CONF_SET_POSITION_TOPIC), self._config.get(CONF_SET_POSITION_TOPIC),
position, position,
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -3,13 +3,18 @@ from __future__ import annotations
from collections import deque from collections import deque
from collections.abc import Callable from collections.abc import Callable
import datetime as dt
from functools import wraps from functools import wraps
from typing import Any from typing import Any
import attr
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.util import dt as dt_util
from .const import ATTR_DISCOVERY_PAYLOAD, ATTR_DISCOVERY_TOPIC from .const import ATTR_DISCOVERY_PAYLOAD, ATTR_DISCOVERY_TOPIC
from .models import MessageCallbackType from .models import MessageCallbackType, PublishPayloadType
DATA_MQTT_DEBUG_INFO = "mqtt_debug_info" DATA_MQTT_DEBUG_INFO = "mqtt_debug_info"
STORED_MESSAGES = 10 STORED_MESSAGES = 10
@ -42,6 +47,42 @@ def log_messages(
return _decorator return _decorator
@attr.s(slots=True, frozen=True)
class TimestampedPublishMessage:
"""MQTT Message."""
topic: str = attr.ib()
payload: PublishPayloadType = attr.ib()
qos: int = attr.ib()
retain: bool = attr.ib()
timestamp: dt.datetime = attr.ib(default=None)
def log_message(
hass: HomeAssistant,
entity_id: str,
topic: str,
payload: PublishPayloadType,
qos: int,
retain: bool,
) -> None:
"""Log an outgoing MQTT message."""
debug_info = hass.data.setdefault(
DATA_MQTT_DEBUG_INFO, {"entities": {}, "triggers": {}}
)
entity_info = debug_info["entities"].setdefault(
entity_id, {"subscriptions": {}, "discovery_data": {}, "transmitted": {}}
)
if topic not in entity_info["transmitted"]:
entity_info["transmitted"][topic] = {
"messages": deque([], STORED_MESSAGES),
}
msg = TimestampedPublishMessage(
topic, payload, qos, retain, timestamp=dt_util.utcnow()
)
entity_info["transmitted"][topic]["messages"].append(msg)
def add_subscription(hass, message_callback, subscription): def add_subscription(hass, message_callback, subscription):
"""Prepare debug data for subscription.""" """Prepare debug data for subscription."""
if entity_id := getattr(message_callback, "__entity_id", None): if entity_id := getattr(message_callback, "__entity_id", None):
@ -49,7 +90,7 @@ def add_subscription(hass, message_callback, subscription):
DATA_MQTT_DEBUG_INFO, {"entities": {}, "triggers": {}} DATA_MQTT_DEBUG_INFO, {"entities": {}, "triggers": {}}
) )
entity_info = debug_info["entities"].setdefault( entity_info = debug_info["entities"].setdefault(
entity_id, {"subscriptions": {}, "discovery_data": {}} entity_id, {"subscriptions": {}, "discovery_data": {}, "transmitted": {}}
) )
if subscription not in entity_info["subscriptions"]: if subscription not in entity_info["subscriptions"]:
entity_info["subscriptions"][subscription] = { entity_info["subscriptions"][subscription] = {
@ -80,7 +121,7 @@ def add_entity_discovery_data(hass, discovery_data, entity_id):
DATA_MQTT_DEBUG_INFO, {"entities": {}, "triggers": {}} DATA_MQTT_DEBUG_INFO, {"entities": {}, "triggers": {}}
) )
entity_info = debug_info["entities"].setdefault( entity_info = debug_info["entities"].setdefault(
entity_id, {"subscriptions": {}, "discovery_data": {}} entity_id, {"subscriptions": {}, "discovery_data": {}, "transmitted": {}}
) )
entity_info["discovery_data"] = discovery_data entity_info["discovery_data"] = discovery_data
@ -118,10 +159,10 @@ def remove_trigger_discovery_data(hass, discovery_hash):
hass.data[DATA_MQTT_DEBUG_INFO]["triggers"][discovery_hash]["discovery_data"] = None hass.data[DATA_MQTT_DEBUG_INFO]["triggers"][discovery_hash]["discovery_data"] = None
async def info_for_device(hass, device_id): def info_for_device(hass, device_id):
"""Get debug info for a device.""" """Get debug info for a device."""
mqtt_info = {"entities": [], "triggers": []} mqtt_info = {"entities": [], "triggers": []}
entity_registry = await hass.helpers.entity_registry.async_get_registry() entity_registry = er.async_get(hass)
entries = hass.helpers.entity_registry.async_entries_for_device( entries = hass.helpers.entity_registry.async_entries_for_device(
entity_registry, device_id, include_disabled_entities=True entity_registry, device_id, include_disabled_entities=True
@ -150,6 +191,22 @@ async def info_for_device(hass, device_id):
} }
for topic, subscription in entity_info["subscriptions"].items() for topic, subscription in entity_info["subscriptions"].items()
] ]
transmitted = [
{
"topic": topic,
"messages": [
{
"payload": str(msg.payload),
"qos": msg.qos,
"retain": msg.retain,
"time": msg.timestamp,
"topic": msg.topic,
}
for msg in list(subscription["messages"])
],
}
for topic, subscription in entity_info["transmitted"].items()
]
discovery_data = { discovery_data = {
"topic": entity_info["discovery_data"].get(ATTR_DISCOVERY_TOPIC, ""), "topic": entity_info["discovery_data"].get(ATTR_DISCOVERY_TOPIC, ""),
"payload": entity_info["discovery_data"].get(ATTR_DISCOVERY_PAYLOAD, ""), "payload": entity_info["discovery_data"].get(ATTR_DISCOVERY_PAYLOAD, ""),
@ -159,6 +216,7 @@ async def info_for_device(hass, device_id):
"entity_id": entry.entity_id, "entity_id": entry.entity_id,
"subscriptions": subscriptions, "subscriptions": subscriptions,
"discovery_data": discovery_data, "discovery_data": discovery_data,
"transmitted": transmitted,
} }
) )

View file

@ -544,8 +544,7 @@ class MqttFan(MqttEntity, FanEntity):
This method is a coroutine. This method is a coroutine.
""" """
mqtt_payload = self._command_templates[CONF_STATE](self._payload["STATE_ON"]) mqtt_payload = self._command_templates[CONF_STATE](self._payload["STATE_ON"])
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_COMMAND_TOPIC], self._topic[CONF_COMMAND_TOPIC],
mqtt_payload, mqtt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -566,8 +565,7 @@ class MqttFan(MqttEntity, FanEntity):
This method is a coroutine. This method is a coroutine.
""" """
mqtt_payload = self._command_templates[CONF_STATE](self._payload["STATE_OFF"]) mqtt_payload = self._command_templates[CONF_STATE](self._payload["STATE_OFF"])
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_COMMAND_TOPIC], self._topic[CONF_COMMAND_TOPIC],
mqtt_payload, mqtt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -587,8 +585,7 @@ class MqttFan(MqttEntity, FanEntity):
percentage_to_ranged_value(self._speed_range, percentage) percentage_to_ranged_value(self._speed_range, percentage)
) )
mqtt_payload = self._command_templates[ATTR_PERCENTAGE](percentage_payload) mqtt_payload = self._command_templates[ATTR_PERCENTAGE](percentage_payload)
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_PERCENTAGE_COMMAND_TOPIC], self._topic[CONF_PERCENTAGE_COMMAND_TOPIC],
mqtt_payload, mqtt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -611,8 +608,7 @@ class MqttFan(MqttEntity, FanEntity):
mqtt_payload = self._command_templates[ATTR_PRESET_MODE](preset_mode) mqtt_payload = self._command_templates[ATTR_PRESET_MODE](preset_mode)
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_PRESET_MODE_COMMAND_TOPIC], self._topic[CONF_PRESET_MODE_COMMAND_TOPIC],
mqtt_payload, mqtt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -638,8 +634,7 @@ class MqttFan(MqttEntity, FanEntity):
self._payload["OSCILLATE_OFF_PAYLOAD"] self._payload["OSCILLATE_OFF_PAYLOAD"]
) )
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_OSCILLATION_COMMAND_TOPIC], self._topic[CONF_OSCILLATION_COMMAND_TOPIC],
mqtt_payload, mqtt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -419,8 +419,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
This method is a coroutine. This method is a coroutine.
""" """
mqtt_payload = self._command_templates[CONF_STATE](self._payload["STATE_ON"]) mqtt_payload = self._command_templates[CONF_STATE](self._payload["STATE_ON"])
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_COMMAND_TOPIC], self._topic[CONF_COMMAND_TOPIC],
mqtt_payload, mqtt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -437,8 +436,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
This method is a coroutine. This method is a coroutine.
""" """
mqtt_payload = self._command_templates[CONF_STATE](self._payload["STATE_OFF"]) mqtt_payload = self._command_templates[CONF_STATE](self._payload["STATE_OFF"])
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_COMMAND_TOPIC], self._topic[CONF_COMMAND_TOPIC],
mqtt_payload, mqtt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -455,8 +453,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
This method is a coroutine. This method is a coroutine.
""" """
mqtt_payload = self._command_templates[ATTR_HUMIDITY](humidity) mqtt_payload = self._command_templates[ATTR_HUMIDITY](humidity)
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_TARGET_HUMIDITY_COMMAND_TOPIC], self._topic[CONF_TARGET_HUMIDITY_COMMAND_TOPIC],
mqtt_payload, mqtt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -479,8 +476,7 @@ class MqttHumidifier(MqttEntity, HumidifierEntity):
mqtt_payload = self._command_templates[ATTR_MODE](mode) mqtt_payload = self._command_templates[ATTR_MODE](mode)
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_MODE_COMMAND_TOPIC], self._topic[CONF_MODE_COMMAND_TOPIC],
mqtt_payload, mqtt_payload,
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -833,8 +833,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
async def publish(topic, payload): async def publish(topic, payload):
"""Publish an MQTT message.""" """Publish an MQTT message."""
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[topic], self._topic[topic],
payload, payload,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -1081,8 +1080,7 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity):
This method is a coroutine. This method is a coroutine.
""" """
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_COMMAND_TOPIC], self._topic[CONF_COMMAND_TOPIC],
self._payload["off"], self._payload["off"],
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -638,8 +638,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
self._white_value = kwargs[ATTR_WHITE_VALUE] self._white_value = kwargs[ATTR_WHITE_VALUE]
should_update = True should_update = True
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_COMMAND_TOPIC], self._topic[CONF_COMMAND_TOPIC],
json.dumps(message), json.dumps(message),
self._config[CONF_QOS], self._config[CONF_QOS],
@ -664,8 +663,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
self._set_flash_and_transition(message, **kwargs) self._set_flash_and_transition(message, **kwargs)
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topic[CONF_COMMAND_TOPIC], self._topic[CONF_COMMAND_TOPIC],
json.dumps(message), json.dumps(message),
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -392,8 +392,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity):
if ATTR_TRANSITION in kwargs: if ATTR_TRANSITION in kwargs:
values["transition"] = kwargs[ATTR_TRANSITION] values["transition"] = kwargs[ATTR_TRANSITION]
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topics[CONF_COMMAND_TOPIC], self._topics[CONF_COMMAND_TOPIC],
self._templates[CONF_COMMAND_ON_TEMPLATE].async_render( self._templates[CONF_COMMAND_ON_TEMPLATE].async_render(
parse_result=False, **values parse_result=False, **values
@ -418,8 +417,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity):
if ATTR_TRANSITION in kwargs: if ATTR_TRANSITION in kwargs:
values["transition"] = kwargs[ATTR_TRANSITION] values["transition"] = kwargs[ATTR_TRANSITION]
await mqtt.async_publish( await self.async_publish(
self.hass,
self._topics[CONF_COMMAND_TOPIC], self._topics[CONF_COMMAND_TOPIC],
self._templates[CONF_COMMAND_OFF_TEMPLATE].async_render( self._templates[CONF_COMMAND_OFF_TEMPLATE].async_render(
parse_result=False, **values parse_result=False, **values

View file

@ -179,8 +179,7 @@ class MqttLock(MqttEntity, LockEntity):
This method is a coroutine. This method is a coroutine.
""" """
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config[CONF_COMMAND_TOPIC], self._config[CONF_COMMAND_TOPIC],
self._config[CONF_PAYLOAD_LOCK], self._config[CONF_PAYLOAD_LOCK],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -197,8 +196,7 @@ class MqttLock(MqttEntity, LockEntity):
This method is a coroutine. This method is a coroutine.
""" """
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config[CONF_COMMAND_TOPIC], self._config[CONF_COMMAND_TOPIC],
self._config[CONF_PAYLOAD_UNLOCK], self._config[CONF_PAYLOAD_UNLOCK],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -215,8 +213,7 @@ class MqttLock(MqttEntity, LockEntity):
This method is a coroutine. This method is a coroutine.
""" """
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config[CONF_COMMAND_TOPIC], self._config[CONF_COMMAND_TOPIC],
self._config[CONF_PAYLOAD_OPEN], self._config[CONF_PAYLOAD_OPEN],
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -42,7 +42,7 @@ from homeassistant.helpers.entity import (
) )
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from . import DATA_MQTT, MqttValueTemplate, debug_info, publish, subscription from . import DATA_MQTT, MqttValueTemplate, async_publish, debug_info, subscription
from .const import ( from .const import (
ATTR_DISCOVERY_HASH, ATTR_DISCOVERY_HASH,
ATTR_DISCOVERY_PAYLOAD, ATTR_DISCOVERY_PAYLOAD,
@ -51,13 +51,14 @@ from .const import (
CONF_ENCODING, CONF_ENCODING,
CONF_QOS, CONF_QOS,
CONF_TOPIC, CONF_TOPIC,
DEFAULT_ENCODING,
DEFAULT_PAYLOAD_AVAILABLE, DEFAULT_PAYLOAD_AVAILABLE,
DEFAULT_PAYLOAD_NOT_AVAILABLE, DEFAULT_PAYLOAD_NOT_AVAILABLE,
DOMAIN, DOMAIN,
MQTT_CONNECTED, MQTT_CONNECTED,
MQTT_DISCONNECTED, MQTT_DISCONNECTED,
) )
from .debug_info import log_messages from .debug_info import log_message, log_messages
from .discovery import ( from .discovery import (
MQTT_DISCOVERY_DONE, MQTT_DISCOVERY_DONE,
MQTT_DISCOVERY_NEW, MQTT_DISCOVERY_NEW,
@ -65,7 +66,7 @@ from .discovery import (
clear_discovery_hash, clear_discovery_hash,
set_discovery_hash, set_discovery_hash,
) )
from .models import ReceiveMessage from .models import PublishPayloadType, ReceiveMessage
from .subscription import ( from .subscription import (
async_prepare_subscribe_topics, async_prepare_subscribe_topics,
async_subscribe_topics, async_subscribe_topics,
@ -552,7 +553,7 @@ class MqttDiscoveryUpdate(Entity):
# Clear the discovery topic so the entity is not rediscovered after a restart # Clear the discovery topic so the entity is not rediscovered after a restart
discovery_topic = self._discovery_data[ATTR_DISCOVERY_TOPIC] discovery_topic = self._discovery_data[ATTR_DISCOVERY_TOPIC]
publish(self.hass, discovery_topic, "", retain=True) await async_publish(self.hass, discovery_topic, "", retain=True)
@callback @callback
def add_to_platform_abort(self) -> None: def add_to_platform_abort(self) -> None:
@ -709,6 +710,25 @@ class MqttEntity(
await MqttAvailability.async_will_remove_from_hass(self) await MqttAvailability.async_will_remove_from_hass(self)
await MqttDiscoveryUpdate.async_will_remove_from_hass(self) await MqttDiscoveryUpdate.async_will_remove_from_hass(self)
async def async_publish(
self,
topic: str,
payload: PublishPayloadType,
qos: int = 0,
retain: bool = False,
encoding: str = DEFAULT_ENCODING,
):
"""Publish message to an MQTT topic."""
log_message(self.hass, self.entity_id, topic, payload, qos, retain)
await async_publish(
self.hass,
topic,
payload,
qos,
retain,
encoding,
)
@staticmethod @staticmethod
@abstractmethod @abstractmethod
def config_schema(): def config_schema():

View file

@ -257,8 +257,7 @@ class MqttNumber(MqttEntity, NumberEntity, RestoreEntity):
self._current_number = current_number self._current_number = current_number
self.async_write_ha_state() self.async_write_ha_state()
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config[CONF_COMMAND_TOPIC], self._config[CONF_COMMAND_TOPIC],
payload, payload,
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -183,8 +183,7 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity):
self._attr_current_option = option self._attr_current_option = option
self.async_write_ha_state() self.async_write_ha_state()
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config[CONF_COMMAND_TOPIC], self._config[CONF_COMMAND_TOPIC],
payload, payload,
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -328,8 +328,7 @@ class MqttSiren(MqttEntity, SirenEntity):
else json.dumps(template_variables) else json.dumps(template_variables)
) )
if payload and payload not in PAYLOAD_NONE: if payload and payload not in PAYLOAD_NONE:
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config[topic], self._config[topic],
payload, payload,
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -197,8 +197,7 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity):
This method is a coroutine. This method is a coroutine.
""" """
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config[CONF_COMMAND_TOPIC], self._config[CONF_COMMAND_TOPIC],
self._config[CONF_PAYLOAD_ON], self._config[CONF_PAYLOAD_ON],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -215,8 +214,7 @@ class MqttSwitch(MqttEntity, SwitchEntity, RestoreEntity):
This method is a coroutine. This method is a coroutine.
""" """
await mqtt.async_publish( await self.async_publish(
self.hass,
self._config[CONF_COMMAND_TOPIC], self._config[CONF_COMMAND_TOPIC],
self._config[CONF_PAYLOAD_OFF], self._config[CONF_PAYLOAD_OFF],
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -388,8 +388,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
if self.supported_features & SUPPORT_TURN_ON == 0: if self.supported_features & SUPPORT_TURN_ON == 0:
return return
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._payloads[CONF_PAYLOAD_TURN_ON], self._payloads[CONF_PAYLOAD_TURN_ON],
self._qos, self._qos,
@ -404,8 +403,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
if self.supported_features & SUPPORT_TURN_OFF == 0: if self.supported_features & SUPPORT_TURN_OFF == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._payloads[CONF_PAYLOAD_TURN_OFF], self._payloads[CONF_PAYLOAD_TURN_OFF],
self._qos, self._qos,
@ -420,8 +418,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
if self.supported_features & SUPPORT_STOP == 0: if self.supported_features & SUPPORT_STOP == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._payloads[CONF_PAYLOAD_STOP], self._payloads[CONF_PAYLOAD_STOP],
self._qos, self._qos,
@ -436,8 +433,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
if self.supported_features & SUPPORT_CLEAN_SPOT == 0: if self.supported_features & SUPPORT_CLEAN_SPOT == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._payloads[CONF_PAYLOAD_CLEAN_SPOT], self._payloads[CONF_PAYLOAD_CLEAN_SPOT],
self._qos, self._qos,
@ -452,8 +448,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
if self.supported_features & SUPPORT_LOCATE == 0: if self.supported_features & SUPPORT_LOCATE == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._payloads[CONF_PAYLOAD_LOCATE], self._payloads[CONF_PAYLOAD_LOCATE],
self._qos, self._qos,
@ -468,8 +463,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
if self.supported_features & SUPPORT_PAUSE == 0: if self.supported_features & SUPPORT_PAUSE == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._payloads[CONF_PAYLOAD_START_PAUSE], self._payloads[CONF_PAYLOAD_START_PAUSE],
self._qos, self._qos,
@ -484,8 +478,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
if self.supported_features & SUPPORT_RETURN_HOME == 0: if self.supported_features & SUPPORT_RETURN_HOME == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._payloads[CONF_PAYLOAD_RETURN_TO_BASE], self._payloads[CONF_PAYLOAD_RETURN_TO_BASE],
self._qos, self._qos,
@ -502,8 +495,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
) or fan_speed not in self._fan_speed_list: ) or fan_speed not in self._fan_speed_list:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._set_fan_speed_topic, self._set_fan_speed_topic,
fan_speed, fan_speed,
self._qos, self._qos,
@ -523,8 +515,7 @@ class MqttVacuum(MqttEntity, VacuumEntity):
message = json.dumps(message) message = json.dumps(message)
else: else:
message = command message = command
await mqtt.async_publish( await self.async_publish(
self.hass,
self._send_command_topic, self._send_command_topic,
message, message,
self._qos, self._qos,

View file

@ -260,8 +260,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
"""Start the vacuum.""" """Start the vacuum."""
if self.supported_features & SUPPORT_START == 0: if self.supported_features & SUPPORT_START == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._config[CONF_PAYLOAD_START], self._config[CONF_PAYLOAD_START],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -273,8 +272,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
"""Pause the vacuum.""" """Pause the vacuum."""
if self.supported_features & SUPPORT_PAUSE == 0: if self.supported_features & SUPPORT_PAUSE == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._config[CONF_PAYLOAD_PAUSE], self._config[CONF_PAYLOAD_PAUSE],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -286,8 +284,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
"""Stop the vacuum.""" """Stop the vacuum."""
if self.supported_features & SUPPORT_STOP == 0: if self.supported_features & SUPPORT_STOP == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._config[CONF_PAYLOAD_STOP], self._config[CONF_PAYLOAD_STOP],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -301,8 +298,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
fan_speed not in self._fan_speed_list fan_speed not in self._fan_speed_list
): ):
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._set_fan_speed_topic, self._set_fan_speed_topic,
fan_speed, fan_speed,
self._config[CONF_QOS], self._config[CONF_QOS],
@ -314,8 +310,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
"""Tell the vacuum to return to its dock.""" """Tell the vacuum to return to its dock."""
if self.supported_features & SUPPORT_RETURN_HOME == 0: if self.supported_features & SUPPORT_RETURN_HOME == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._config[CONF_PAYLOAD_RETURN_TO_BASE], self._config[CONF_PAYLOAD_RETURN_TO_BASE],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -327,8 +322,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
"""Perform a spot clean-up.""" """Perform a spot clean-up."""
if self.supported_features & SUPPORT_CLEAN_SPOT == 0: if self.supported_features & SUPPORT_CLEAN_SPOT == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._config[CONF_PAYLOAD_CLEAN_SPOT], self._config[CONF_PAYLOAD_CLEAN_SPOT],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -340,8 +334,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
"""Locate the vacuum (usually by playing a song).""" """Locate the vacuum (usually by playing a song)."""
if self.supported_features & SUPPORT_LOCATE == 0: if self.supported_features & SUPPORT_LOCATE == 0:
return None return None
await mqtt.async_publish( await self.async_publish(
self.hass,
self._command_topic, self._command_topic,
self._config[CONF_PAYLOAD_LOCATE], self._config[CONF_PAYLOAD_LOCATE],
self._config[CONF_QOS], self._config[CONF_QOS],
@ -359,8 +352,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity):
message = json.dumps(message) message = json.dumps(message)
else: else:
message = command message = command
await mqtt.async_publish( await self.async_publish(
self.hass,
self._send_command_topic, self._send_command_topic,
message, message,
self._config[CONF_QOS], self._config[CONF_QOS],

View file

@ -771,7 +771,12 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, alarm_control_panel.DOMAIN, DEFAULT_CONFIG hass,
mqtt_mock,
alarm_control_panel.DOMAIN,
DEFAULT_CONFIG,
alarm_control_panel.SERVICE_ALARM_DISARM,
command_payload="DISARM",
) )

View file

@ -868,7 +868,7 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG hass, mqtt_mock, binary_sensor.DOMAIN, DEFAULT_CONFIG, None
) )

View file

@ -18,6 +18,7 @@ from .test_common import (
help_test_discovery_update, help_test_discovery_update,
help_test_discovery_update_attr, help_test_discovery_update_attr,
help_test_discovery_update_unchanged, help_test_discovery_update_unchanged,
help_test_entity_debug_info_message,
help_test_entity_device_info_remove, help_test_entity_device_info_remove,
help_test_entity_device_info_update, help_test_entity_device_info_update,
help_test_entity_device_info_with_connection, help_test_entity_device_info_with_connection,
@ -302,6 +303,19 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
) )
async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info."""
await help_test_entity_debug_info_message(
hass,
mqtt_mock,
button.DOMAIN,
DEFAULT_CONFIG,
button.SERVICE_PRESS,
command_payload="PRESS",
state_topic=None,
)
async def test_invalid_device_class(hass, mqtt_mock): async def test_invalid_device_class(hass, mqtt_mock):
"""Test device_class option with invalid value.""" """Test device_class option with invalid value."""
assert await async_setup_component( assert await async_setup_component(

View file

@ -237,7 +237,13 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, camera.DOMAIN, DEFAULT_CONFIG, "test_topic", b"ON" hass,
mqtt_mock,
camera.DOMAIN,
DEFAULT_CONFIG,
None,
state_topic="test_topic",
state_payload=b"ON",
) )

View file

@ -1240,11 +1240,19 @@ async def test_entity_debug_info_message(hass, mqtt_mock):
CLIMATE_DOMAIN: { CLIMATE_DOMAIN: {
"platform": "mqtt", "platform": "mqtt",
"name": "test", "name": "test",
"mode_command_topic": "command-topic",
"mode_state_topic": "test-topic", "mode_state_topic": "test-topic",
} }
} }
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, CLIMATE_DOMAIN, config, "test-topic" hass,
mqtt_mock,
CLIMATE_DOMAIN,
config,
climate.SERVICE_TURN_ON,
command_topic="command-topic",
command_payload="heat",
state_topic="test-topic",
) )

View file

@ -43,6 +43,8 @@ DEFAULT_CONFIG_DEVICE_INFO_MAC = {
"configuration_url": "http://example.com", "configuration_url": "http://example.com",
} }
_SENTINEL = object()
async def help_test_availability_when_connection_lost(hass, mqtt_mock, domain, config): async def help_test_availability_when_connection_lost(hass, mqtt_mock, domain, config):
"""Test availability after MQTT disconnection.""" """Test availability after MQTT disconnection."""
@ -1110,7 +1112,7 @@ async def help_test_entity_debug_info(hass, mqtt_mock, domain, config):
device = registry.async_get_device({("mqtt", "helloworld")}) device = registry.async_get_device({("mqtt", "helloworld")})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"]) == 1 assert len(debug_info_data["entities"]) == 1
assert ( assert (
debug_info_data["entities"][0]["discovery_data"]["topic"] debug_info_data["entities"][0]["discovery_data"]["topic"]
@ -1121,6 +1123,7 @@ async def help_test_entity_debug_info(hass, mqtt_mock, domain, config):
assert {"topic": "test-topic", "messages": []} in debug_info_data["entities"][0][ assert {"topic": "test-topic", "messages": []} in debug_info_data["entities"][0][
"subscriptions" "subscriptions"
] ]
assert debug_info_data["entities"][0]["transmitted"] == []
assert len(debug_info_data["triggers"]) == 0 assert len(debug_info_data["triggers"]) == 0
@ -1143,7 +1146,7 @@ async def help_test_entity_debug_info_max_messages(hass, mqtt_mock, domain, conf
device = registry.async_get_device({("mqtt", "helloworld")}) device = registry.async_get_device({("mqtt", "helloworld")})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) == 1 assert len(debug_info_data["entities"][0]["subscriptions"]) == 1
assert {"topic": "test-topic", "messages": []} in debug_info_data["entities"][0][ assert {"topic": "test-topic", "messages": []} in debug_info_data["entities"][0][
"subscriptions" "subscriptions"
@ -1155,7 +1158,7 @@ async def help_test_entity_debug_info_max_messages(hass, mqtt_mock, domain, conf
for i in range(0, debug_info.STORED_MESSAGES + 1): for i in range(0, debug_info.STORED_MESSAGES + 1):
async_fire_mqtt_message(hass, "test-topic", f"{i}") async_fire_mqtt_message(hass, "test-topic", f"{i}")
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) == 1 assert len(debug_info_data["entities"][0]["subscriptions"]) == 1
assert ( assert (
len(debug_info_data["entities"][0]["subscriptions"][0]["messages"]) len(debug_info_data["entities"][0]["subscriptions"][0]["messages"])
@ -1177,9 +1180,18 @@ async def help_test_entity_debug_info_max_messages(hass, mqtt_mock, domain, conf
async def help_test_entity_debug_info_message( async def help_test_entity_debug_info_message(
hass, mqtt_mock, domain, config, topic=None, payload=None hass,
mqtt_mock,
domain,
config,
service,
command_topic=_SENTINEL,
command_payload=_SENTINEL,
state_topic=_SENTINEL,
state_payload=_SENTINEL,
service_parameters=None,
): ):
"""Test debug_info message overflow. """Test debug_info.
This is a test helper for MQTT debug_info. This is a test helper for MQTT debug_info.
""" """
@ -1188,13 +1200,21 @@ async def help_test_entity_debug_info_message(
config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID) config["device"] = copy.deepcopy(DEFAULT_CONFIG_DEVICE_INFO_ID)
config["unique_id"] = "veryunique" config["unique_id"] = "veryunique"
if topic is None: if command_topic is _SENTINEL:
# Add default topic to config
config["command_topic"] = "command-topic"
command_topic = "command-topic"
if command_payload is _SENTINEL:
command_payload = "ON"
if state_topic is _SENTINEL:
# Add default topic to config # Add default topic to config
config["state_topic"] = "state-topic" config["state_topic"] = "state-topic"
topic = "state-topic" state_topic = "state-topic"
if payload is None: if state_payload is _SENTINEL:
payload = "ON" state_payload = "ON"
registry = dr.async_get(hass) registry = dr.async_get(hass)
@ -1205,31 +1225,69 @@ async def help_test_entity_debug_info_message(
device = registry.async_get_device({("mqtt", "helloworld")}) device = registry.async_get_device({("mqtt", "helloworld")})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1
assert {"topic": topic, "messages": []} in debug_info_data["entities"][0][
"subscriptions"
]
start_dt = datetime(2019, 1, 1, 0, 0, 0) start_dt = datetime(2019, 1, 1, 0, 0, 0)
with patch("homeassistant.util.dt.utcnow") as dt_utcnow:
dt_utcnow.return_value = start_dt
async_fire_mqtt_message(hass, topic, payload)
debug_info_data = await debug_info.info_for_device(hass, device.id) if state_topic is not None:
assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1 assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1
assert { assert {"topic": state_topic, "messages": []} in debug_info_data["entities"][0][
"topic": topic, "subscriptions"
"messages": [ ]
with patch("homeassistant.util.dt.utcnow") as dt_utcnow:
dt_utcnow.return_value = start_dt
async_fire_mqtt_message(hass, state_topic, state_payload)
debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1
assert {
"topic": state_topic,
"messages": [
{
"payload": str(state_payload),
"qos": 0,
"retain": False,
"time": start_dt,
"topic": state_topic,
}
],
} in debug_info_data["entities"][0]["subscriptions"]
expected_transmissions = []
if service:
# Trigger an outgoing MQTT message
with patch("homeassistant.util.dt.utcnow") as dt_utcnow:
dt_utcnow.return_value = start_dt
if service:
service_data = {ATTR_ENTITY_ID: f"{domain}.test"}
if service_parameters:
service_data.update(service_parameters)
await hass.services.async_call(
domain,
service,
service_data,
blocking=True,
)
expected_transmissions = [
{ {
"payload": str(payload), "topic": command_topic,
"qos": 0, "messages": [
"retain": False, {
"time": start_dt, "payload": str(command_payload),
"topic": topic, "qos": 0,
"retain": False,
"time": start_dt,
"topic": command_topic,
}
],
} }
], ]
} in debug_info_data["entities"][0]["subscriptions"]
debug_info_data = debug_info.info_for_device(hass, device.id)
assert debug_info_data["entities"][0]["transmitted"] == expected_transmissions
async def help_test_entity_debug_info_remove(hass, mqtt_mock, domain, config): async def help_test_entity_debug_info_remove(hass, mqtt_mock, domain, config):
@ -1251,7 +1309,7 @@ async def help_test_entity_debug_info_remove(hass, mqtt_mock, domain, config):
device = registry.async_get_device({("mqtt", "helloworld")}) device = registry.async_get_device({("mqtt", "helloworld")})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"]) == 1 assert len(debug_info_data["entities"]) == 1
assert ( assert (
debug_info_data["entities"][0]["discovery_data"]["topic"] debug_info_data["entities"][0]["discovery_data"]["topic"]
@ -1269,7 +1327,7 @@ async def help_test_entity_debug_info_remove(hass, mqtt_mock, domain, config):
async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", "") async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", "")
await hass.async_block_till_done() await hass.async_block_till_done()
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"]) == 0 assert len(debug_info_data["entities"]) == 0
assert len(debug_info_data["triggers"]) == 0 assert len(debug_info_data["triggers"]) == 0
assert entity_id not in hass.data[debug_info.DATA_MQTT_DEBUG_INFO]["entities"] assert entity_id not in hass.data[debug_info.DATA_MQTT_DEBUG_INFO]["entities"]
@ -1295,7 +1353,7 @@ async def help_test_entity_debug_info_update_entity_id(hass, mqtt_mock, domain,
device = dev_registry.async_get_device({("mqtt", "helloworld")}) device = dev_registry.async_get_device({("mqtt", "helloworld")})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"]) == 1 assert len(debug_info_data["entities"]) == 1
assert ( assert (
debug_info_data["entities"][0]["discovery_data"]["topic"] debug_info_data["entities"][0]["discovery_data"]["topic"]
@ -1313,7 +1371,7 @@ async def help_test_entity_debug_info_update_entity_id(hass, mqtt_mock, domain,
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"]) == 1 assert len(debug_info_data["entities"]) == 1
assert ( assert (
debug_info_data["entities"][0]["discovery_data"]["topic"] debug_info_data["entities"][0]["discovery_data"]["topic"]

View file

@ -2521,7 +2521,12 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, cover.DOMAIN, DEFAULT_CONFIG hass,
mqtt_mock,
cover.DOMAIN,
DEFAULT_CONFIG,
SERVICE_OPEN_COVER,
command_payload="OPEN",
) )

View file

@ -1246,7 +1246,7 @@ async def test_trigger_debug_info(hass, mqtt_mock):
) )
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"]) == 0 assert len(debug_info_data["entities"]) == 0
assert len(debug_info_data["triggers"]) == 2 assert len(debug_info_data["triggers"]) == 2
topic_map = { topic_map = {
@ -1268,7 +1268,7 @@ async def test_trigger_debug_info(hass, mqtt_mock):
async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", "") async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", "")
await hass.async_block_till_done() await hass.async_block_till_done()
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"]) == 0 assert len(debug_info_data["entities"]) == 0
assert len(debug_info_data["triggers"]) == 1 assert len(debug_info_data["triggers"]) == 1
assert ( assert (

View file

@ -1724,7 +1724,7 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG hass, mqtt_mock, fan.DOMAIN, DEFAULT_CONFIG, fan.SERVICE_TURN_ON
) )

View file

@ -1102,7 +1102,7 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG hass, mqtt_mock, humidifier.DOMAIN, DEFAULT_CONFIG, humidifier.SERVICE_TURN_ON
) )

View file

@ -1533,6 +1533,7 @@ async def test_mqtt_ws_get_device_debug_info(
"payload": config, "payload": config,
"topic": "homeassistant/sensor/bla/config", "topic": "homeassistant/sensor/bla/config",
}, },
"transmitted": [],
} }
], ],
"triggers": [], "triggers": [],
@ -1595,6 +1596,7 @@ async def test_mqtt_ws_get_device_debug_info_binary(
"payload": config, "payload": config,
"topic": "homeassistant/camera/bla/config", "topic": "homeassistant/camera/bla/config",
}, },
"transmitted": [],
} }
], ],
"triggers": [], "triggers": [],
@ -1662,7 +1664,7 @@ async def test_debug_info_multiple_devices(hass, mqtt_mock):
device = registry.async_get_device({("mqtt", id)}) device = registry.async_get_device({("mqtt", id)})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
if d["domain"] != "device_automation": if d["domain"] != "device_automation":
assert len(debug_info_data["entities"]) == 1 assert len(debug_info_data["entities"]) == 1
assert len(debug_info_data["triggers"]) == 0 assert len(debug_info_data["triggers"]) == 0
@ -1739,7 +1741,7 @@ async def test_debug_info_multiple_entities_triggers(hass, mqtt_mock):
device_id = config[0]["config"]["device"]["identifiers"][0] device_id = config[0]["config"]["device"]["identifiers"][0]
device = registry.async_get_device({("mqtt", device_id)}) device = registry.async_get_device({("mqtt", device_id)})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"]) == 2 assert len(debug_info_data["entities"]) == 2
assert len(debug_info_data["triggers"]) == 2 assert len(debug_info_data["triggers"]) == 2
@ -1786,7 +1788,7 @@ async def test_debug_info_non_mqtt(hass, device_reg, entity_reg):
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {"platform": "test"}}) assert await async_setup_component(hass, DOMAIN, {DOMAIN: {"platform": "test"}})
debug_info_data = await debug_info.info_for_device(hass, device_entry.id) debug_info_data = debug_info.info_for_device(hass, device_entry.id)
assert len(debug_info_data["entities"]) == 0 assert len(debug_info_data["entities"]) == 0
assert len(debug_info_data["triggers"]) == 0 assert len(debug_info_data["triggers"]) == 0
@ -1810,7 +1812,7 @@ async def test_debug_info_wildcard(hass, mqtt_mock):
device = registry.async_get_device({("mqtt", "helloworld")}) device = registry.async_get_device({("mqtt", "helloworld")})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1 assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1
assert {"topic": "sensor/#", "messages": []} in debug_info_data["entities"][0][ assert {"topic": "sensor/#", "messages": []} in debug_info_data["entities"][0][
"subscriptions" "subscriptions"
@ -1821,7 +1823,7 @@ async def test_debug_info_wildcard(hass, mqtt_mock):
dt_utcnow.return_value = start_dt dt_utcnow.return_value = start_dt
async_fire_mqtt_message(hass, "sensor/abc", "123") async_fire_mqtt_message(hass, "sensor/abc", "123")
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1 assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1
assert { assert {
"topic": "sensor/#", "topic": "sensor/#",
@ -1856,7 +1858,7 @@ async def test_debug_info_filter_same(hass, mqtt_mock):
device = registry.async_get_device({("mqtt", "helloworld")}) device = registry.async_get_device({("mqtt", "helloworld")})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1 assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1
assert {"topic": "sensor/#", "messages": []} in debug_info_data["entities"][0][ assert {"topic": "sensor/#", "messages": []} in debug_info_data["entities"][0][
"subscriptions" "subscriptions"
@ -1871,7 +1873,7 @@ async def test_debug_info_filter_same(hass, mqtt_mock):
dt_utcnow.return_value = dt2 dt_utcnow.return_value = dt2
async_fire_mqtt_message(hass, "sensor/abc", "123") async_fire_mqtt_message(hass, "sensor/abc", "123")
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) == 1 assert len(debug_info_data["entities"][0]["subscriptions"]) == 1
assert len(debug_info_data["entities"][0]["subscriptions"][0]["messages"]) == 2 assert len(debug_info_data["entities"][0]["subscriptions"][0]["messages"]) == 2
assert { assert {
@ -1915,7 +1917,7 @@ async def test_debug_info_same_topic(hass, mqtt_mock):
device = registry.async_get_device({("mqtt", "helloworld")}) device = registry.async_get_device({("mqtt", "helloworld")})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1 assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1
assert {"topic": "sensor/status", "messages": []} in debug_info_data["entities"][0][ assert {"topic": "sensor/status", "messages": []} in debug_info_data["entities"][0][
"subscriptions" "subscriptions"
@ -1926,7 +1928,7 @@ async def test_debug_info_same_topic(hass, mqtt_mock):
dt_utcnow.return_value = start_dt dt_utcnow.return_value = start_dt
async_fire_mqtt_message(hass, "sensor/status", "123", qos=0, retain=False) async_fire_mqtt_message(hass, "sensor/status", "123", qos=0, retain=False)
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) == 1 assert len(debug_info_data["entities"][0]["subscriptions"]) == 1
assert { assert {
"payload": "123", "payload": "123",
@ -1966,7 +1968,7 @@ async def test_debug_info_qos_retain(hass, mqtt_mock):
device = registry.async_get_device({("mqtt", "helloworld")}) device = registry.async_get_device({("mqtt", "helloworld")})
assert device is not None assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1 assert len(debug_info_data["entities"][0]["subscriptions"]) >= 1
assert {"topic": "sensor/#", "messages": []} in debug_info_data["entities"][0][ assert {"topic": "sensor/#", "messages": []} in debug_info_data["entities"][0][
"subscriptions" "subscriptions"
@ -1979,7 +1981,7 @@ async def test_debug_info_qos_retain(hass, mqtt_mock):
async_fire_mqtt_message(hass, "sensor/abc", "123", qos=1, retain=True) async_fire_mqtt_message(hass, "sensor/abc", "123", qos=1, retain=True)
async_fire_mqtt_message(hass, "sensor/abc", "123", qos=2, retain=False) async_fire_mqtt_message(hass, "sensor/abc", "123", qos=2, retain=False)
debug_info_data = await debug_info.info_for_device(hass, device.id) debug_info_data = debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"][0]["subscriptions"]) == 1 assert len(debug_info_data["entities"][0]["subscriptions"]) == 1
assert { assert {
"payload": "123", "payload": "123",

View file

@ -747,14 +747,14 @@ async def test_entity_debug_info_message(hass, mqtt_mock):
vacuum.DOMAIN: { vacuum.DOMAIN: {
"platform": "mqtt", "platform": "mqtt",
"name": "test", "name": "test",
"battery_level_topic": "test-topic", "battery_level_topic": "state-topic",
"battery_level_template": "{{ value_json.battery_level }}", "battery_level_template": "{{ value_json.battery_level }}",
"command_topic": "command-topic", "command_topic": "command-topic",
"availability_topic": "avty-topic", "payload_turn_on": "ON",
} }
} }
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, vacuum.DOMAIN, config, "test-topic" hass, mqtt_mock, vacuum.DOMAIN, config, vacuum.SERVICE_TURN_ON
) )

View file

@ -3343,7 +3343,7 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG, light.SERVICE_TURN_ON
) )

View file

@ -1894,7 +1894,13 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, light.DOMAIN, DEFAULT_CONFIG, payload='{"state":"ON"}' hass,
mqtt_mock,
light.DOMAIN,
DEFAULT_CONFIG,
light.SERVICE_TURN_ON,
command_payload='{"state": "ON"}',
state_payload='{"state":"ON"}',
) )

View file

@ -1082,12 +1082,14 @@ async def test_entity_debug_info_message(hass, mqtt_mock):
"schema": "template", "schema": "template",
"name": "test", "name": "test",
"command_topic": "test-topic", "command_topic": "test-topic",
"command_on_template": "on,{{ transition }}", "command_on_template": "ON",
"command_off_template": "off,{{ transition|d }}", "command_off_template": "off,{{ transition|d }}",
"state_template": '{{ value.split(",")[0] }}', "state_template": '{{ value.split(",")[0] }}',
} }
} }
await help_test_entity_debug_info_message(hass, mqtt_mock, light.DOMAIN, config) await help_test_entity_debug_info_message(
hass, mqtt_mock, light.DOMAIN, config, light.SERVICE_TURN_ON
)
async def test_max_mireds(hass, mqtt_mock): async def test_max_mireds(hass, mqtt_mock):

View file

@ -590,7 +590,12 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, LOCK_DOMAIN, DEFAULT_CONFIG hass,
mqtt_mock,
LOCK_DOMAIN,
DEFAULT_CONFIG,
SERVICE_LOCK,
command_payload="LOCK",
) )

View file

@ -541,7 +541,14 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, number.DOMAIN, DEFAULT_CONFIG, payload="1" hass,
mqtt_mock,
number.DOMAIN,
DEFAULT_CONFIG,
SERVICE_SET_VALUE,
service_parameters={ATTR_VALUE: 45},
command_payload="45",
state_payload="1",
) )

View file

@ -475,7 +475,14 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, select.DOMAIN, DEFAULT_CONFIG, payload="milk" hass,
mqtt_mock,
select.DOMAIN,
DEFAULT_CONFIG,
select.SERVICE_SELECT_OPTION,
service_parameters={ATTR_OPTION: "beer"},
command_payload="beer",
state_payload="milk",
) )

View file

@ -906,7 +906,7 @@ async def test_entity_debug_info_max_messages(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG, None
) )

View file

@ -802,7 +802,12 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, siren.DOMAIN, DEFAULT_CONFIG hass,
mqtt_mock,
siren.DOMAIN,
DEFAULT_CONFIG,
siren.SERVICE_TURN_ON,
command_payload='{"state": "ON"}',
) )

View file

@ -510,7 +510,13 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, vacuum.DOMAIN, DEFAULT_CONFIG_2, payload="{}" hass,
mqtt_mock,
vacuum.DOMAIN,
DEFAULT_CONFIG_2,
vacuum.SERVICE_START,
command_payload="start",
state_payload="{}",
) )

View file

@ -499,7 +499,7 @@ async def test_entity_id_update_discovery_update(hass, mqtt_mock):
async def test_entity_debug_info_message(hass, mqtt_mock): async def test_entity_debug_info_message(hass, mqtt_mock):
"""Test MQTT debug info.""" """Test MQTT debug info."""
await help_test_entity_debug_info_message( await help_test_entity_debug_info_message(
hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG hass, mqtt_mock, switch.DOMAIN, DEFAULT_CONFIG, switch.SERVICE_TURN_ON
) )