2020-04-01 17:00:40 +00:00
|
|
|
"""Helper to handle a set of topics to subscribe to."""
|
2021-09-29 14:15:36 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2020-04-01 17:00:40 +00:00
|
|
|
from collections import deque
|
2021-09-29 14:15:36 +00:00
|
|
|
from collections.abc import Callable
|
2020-04-01 17:00:40 +00:00
|
|
|
from functools import wraps
|
2021-09-29 14:15:36 +00:00
|
|
|
from typing import Any
|
2020-04-01 17:00:40 +00:00
|
|
|
|
2021-04-22 21:53:37 +00:00
|
|
|
from homeassistant.core import HomeAssistant
|
2020-04-01 17:00:40 +00:00
|
|
|
|
|
|
|
from .const import ATTR_DISCOVERY_PAYLOAD, ATTR_DISCOVERY_TOPIC
|
|
|
|
from .models import MessageCallbackType
|
|
|
|
|
|
|
|
DATA_MQTT_DEBUG_INFO = "mqtt_debug_info"
|
|
|
|
STORED_MESSAGES = 10
|
|
|
|
|
|
|
|
|
2021-07-05 08:33:12 +00:00
|
|
|
def log_messages(
|
|
|
|
hass: HomeAssistant, entity_id: str
|
|
|
|
) -> Callable[[MessageCallbackType], MessageCallbackType]:
|
2020-04-01 17:00:40 +00:00
|
|
|
"""Wrap an MQTT message callback to support message logging."""
|
|
|
|
|
|
|
|
def _log_message(msg):
|
|
|
|
"""Log message."""
|
|
|
|
debug_info = hass.data[DATA_MQTT_DEBUG_INFO]
|
2020-04-07 16:38:22 +00:00
|
|
|
messages = debug_info["entities"][entity_id]["subscriptions"][
|
|
|
|
msg.subscribed_topic
|
2020-05-01 05:34:44 +00:00
|
|
|
]["messages"]
|
2020-04-21 23:48:04 +00:00
|
|
|
if msg not in messages:
|
|
|
|
messages.append(msg)
|
2020-04-01 17:00:40 +00:00
|
|
|
|
2021-07-05 08:33:12 +00:00
|
|
|
def _decorator(msg_callback: MessageCallbackType) -> MessageCallbackType:
|
2020-04-01 17:00:40 +00:00
|
|
|
@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
|
|
|
|
|
|
|
|
|
2020-04-07 16:38:22 +00:00
|
|
|
def add_subscription(hass, message_callback, subscription):
|
|
|
|
"""Prepare debug data for subscription."""
|
2021-10-31 18:01:16 +00:00
|
|
|
if entity_id := getattr(message_callback, "__entity_id", None):
|
2020-04-01 17:00:40 +00:00
|
|
|
debug_info = hass.data.setdefault(
|
|
|
|
DATA_MQTT_DEBUG_INFO, {"entities": {}, "triggers": {}}
|
|
|
|
)
|
|
|
|
entity_info = debug_info["entities"].setdefault(
|
2020-04-07 16:38:22 +00:00
|
|
|
entity_id, {"subscriptions": {}, "discovery_data": {}}
|
2020-04-01 17:00:40 +00:00
|
|
|
)
|
2020-05-01 05:34:44 +00:00
|
|
|
if subscription not in entity_info["subscriptions"]:
|
|
|
|
entity_info["subscriptions"][subscription] = {
|
|
|
|
"count": 0,
|
|
|
|
"messages": deque([], STORED_MESSAGES),
|
|
|
|
}
|
|
|
|
entity_info["subscriptions"][subscription]["count"] += 1
|
2020-04-01 17:00:40 +00:00
|
|
|
|
|
|
|
|
2020-04-07 16:38:22 +00:00
|
|
|
def remove_subscription(hass, message_callback, subscription):
|
2020-05-01 05:34:44 +00:00
|
|
|
"""Remove debug data for subscription if it exists."""
|
2020-04-01 17:00:40 +00:00
|
|
|
entity_id = getattr(message_callback, "__entity_id", None)
|
|
|
|
if entity_id and entity_id in hass.data[DATA_MQTT_DEBUG_INFO]["entities"]:
|
2020-05-01 05:34:44 +00:00
|
|
|
hass.data[DATA_MQTT_DEBUG_INFO]["entities"][entity_id]["subscriptions"][
|
2020-04-07 16:38:22 +00:00
|
|
|
subscription
|
2020-05-01 05:34:44 +00:00
|
|
|
]["count"] -= 1
|
|
|
|
if not hass.data[DATA_MQTT_DEBUG_INFO]["entities"][entity_id]["subscriptions"][
|
|
|
|
subscription
|
|
|
|
]["count"]:
|
|
|
|
hass.data[DATA_MQTT_DEBUG_INFO]["entities"][entity_id]["subscriptions"].pop(
|
|
|
|
subscription
|
|
|
|
)
|
2020-04-01 17:00:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
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(
|
2020-04-07 16:38:22 +00:00
|
|
|
entity_id, {"subscriptions": {}, "discovery_data": {}}
|
2020-04-01 17:00:40 +00:00
|
|
|
)
|
|
|
|
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(
|
2020-11-27 08:03:44 +00:00
|
|
|
entity_registry, device_id, include_disabled_entities=True
|
2020-04-01 17:00:40 +00:00
|
|
|
)
|
|
|
|
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]
|
2020-04-07 16:38:22 +00:00
|
|
|
subscriptions = [
|
|
|
|
{
|
|
|
|
"topic": topic,
|
|
|
|
"messages": [
|
2020-05-01 15:41:57 +00:00
|
|
|
{
|
|
|
|
"payload": msg.payload,
|
|
|
|
"qos": msg.qos,
|
|
|
|
"retain": msg.retain,
|
|
|
|
"time": msg.timestamp,
|
|
|
|
"topic": msg.topic,
|
|
|
|
}
|
2020-05-01 05:34:44 +00:00
|
|
|
for msg in list(subscription["messages"])
|
2020-04-07 16:38:22 +00:00
|
|
|
],
|
|
|
|
}
|
2020-05-01 05:34:44 +00:00
|
|
|
for topic, subscription in entity_info["subscriptions"].items()
|
2020-04-01 17:00:40 +00:00
|
|
|
]
|
|
|
|
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,
|
2020-04-07 16:38:22 +00:00
|
|
|
"subscriptions": subscriptions,
|
2020-04-01 17:00:40 +00:00
|
|
|
"discovery_data": discovery_data,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
for trigger in mqtt_debug_info["triggers"].values():
|
2021-05-20 07:27:38 +00:00
|
|
|
if trigger["device_id"] != device_id or trigger["discovery_data"] is None:
|
2020-04-01 17:00:40 +00:00
|
|
|
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
|