Skip to content

Commit

Permalink
Improve lists for MQTT integration (home-assistant#113184)
Browse files Browse the repository at this point in the history
* Improve lists for MQTT integration

* Extra diagnostics tests

* Revert changes where the original version was probably faster

* Revert change to gather and await in series
  • Loading branch information
jbouwh authored Mar 13, 2024
1 parent b1346f3 commit 488dae4
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 120 deletions.
8 changes: 5 additions & 3 deletions homeassistant/components/mqtt/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,9 +812,11 @@ def _matching_subscriptions(self, topic: str) -> list[Subscription]:
subscriptions: list[Subscription] = []
if topic in self._simple_subscriptions:
subscriptions.extend(self._simple_subscriptions[topic])
for subscription in self._wildcard_subscriptions:
if subscription.matcher(topic):
subscriptions.append(subscription)
subscriptions.extend(
subscription
for subscription in self._wildcard_subscriptions
if subscription.matcher(topic)
)
return subscriptions

@callback
Expand Down
24 changes: 10 additions & 14 deletions homeassistant/components/mqtt/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,15 +661,12 @@ def _setup_from_config(self, config: ConfigType) -> None:
self._optimistic or CONF_PRESET_MODE_STATE_TOPIC not in config
)

value_templates: dict[str, Template | None] = {}
for key in VALUE_TEMPLATE_KEYS:
value_templates[key] = None
if CONF_VALUE_TEMPLATE in config:
value_templates = {
key: config.get(CONF_VALUE_TEMPLATE) for key in VALUE_TEMPLATE_KEYS
}
for key in VALUE_TEMPLATE_KEYS & config.keys():
value_templates[key] = config[key]
value_templates: dict[str, Template | None] = {
key: config.get(CONF_VALUE_TEMPLATE) for key in VALUE_TEMPLATE_KEYS
}
value_templates.update(
{key: config[key] for key in VALUE_TEMPLATE_KEYS & config.keys()}
)
self._value_templates = {
key: MqttValueTemplate(
template,
Expand All @@ -678,11 +675,10 @@ def _setup_from_config(self, config: ConfigType) -> None:
for key, template in value_templates.items()
}

self._command_templates = {}
for key in COMMAND_TEMPLATE_KEYS:
self._command_templates[key] = MqttCommandTemplate(
config.get(key), entity=self
).async_render
self._command_templates = {
key: MqttCommandTemplate(config.get(key), entity=self).async_render
for key in COMMAND_TEMPLATE_KEYS
}

support = ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF
if (self._topic[CONF_TEMP_STATE_TOPIC] is not None) or (
Expand Down
31 changes: 17 additions & 14 deletions homeassistant/components/mqtt/debug_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,14 @@ def info_for_config_entry(hass: HomeAssistant) -> dict[str, list[Any]]:
mqtt_data = get_mqtt_data(hass)
mqtt_info: dict[str, list[Any]] = {"entities": [], "triggers": []}

for entity_id in mqtt_data.debug_info_entities:
mqtt_info["entities"].append(_info_for_entity(hass, entity_id))
mqtt_info["entities"].extend(
_info_for_entity(hass, entity_id) for entity_id in mqtt_data.debug_info_entities
)

for trigger_key in mqtt_data.debug_info_triggers:
mqtt_info["triggers"].append(_info_for_trigger(hass, trigger_key))
mqtt_info["triggers"].extend(
_info_for_trigger(hass, trigger_key)
for trigger_key in mqtt_data.debug_info_triggers
)

return mqtt_info

Expand All @@ -259,16 +262,16 @@ def info_for_device(hass: HomeAssistant, device_id: str) -> dict[str, list[Any]]
entries = er.async_entries_for_device(
entity_registry, device_id, include_disabled_entities=True
)
for entry in entries:
if entry.entity_id not in mqtt_data.debug_info_entities:
continue

mqtt_info["entities"].append(_info_for_entity(hass, entry.entity_id))

for trigger_key, trigger in mqtt_data.debug_info_triggers.items():
if trigger["device_id"] != device_id:
continue
mqtt_info["entities"].extend(
_info_for_entity(hass, entry.entity_id)
for entry in entries
if entry.entity_id in mqtt_data.debug_info_entities
)

mqtt_info["triggers"].append(_info_for_trigger(hass, trigger_key))
mqtt_info["triggers"].extend(
_info_for_trigger(hass, trigger_key)
for trigger_key, trigger in mqtt_data.debug_info_triggers.items()
if trigger["device_id"] == device_id
)

return mqtt_info
16 changes: 6 additions & 10 deletions homeassistant/components/mqtt/device_trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,24 +353,20 @@ async def async_get_triggers(
) -> list[dict[str, str]]:
"""List device triggers for MQTT devices."""
mqtt_data = get_mqtt_data(hass)
triggers: list[dict[str, str]] = []

if not mqtt_data.device_triggers:
return triggers
return []

for trig in mqtt_data.device_triggers.values():
if trig.device_id != device_id or trig.topic is None:
continue

trigger = {
return [
{
**MQTT_TRIGGER_BASE,
"device_id": device_id,
"type": trig.type,
"subtype": trig.subtype,
}
triggers.append(trigger)

return triggers
for trig in mqtt_data.device_triggers.values()
if trig.device_id == device_id and trig.topic is not None
]


async def async_attach_trigger(
Expand Down
64 changes: 34 additions & 30 deletions homeassistant/components/mqtt/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,36 +95,40 @@ def _async_device_as_dict(hass: HomeAssistant, device: DeviceEntry) -> dict[str,
include_disabled_entities=True,
)

for entity_entry in entities:
def _state_dict(entity_entry: er.RegistryEntry) -> dict[str, Any] | None:
state = hass.states.get(entity_entry.entity_id)
state_dict = None
if state:
state_dict = dict(state.as_dict())

# The context doesn't provide useful information in this case.
state_dict.pop("context", None)

entity_domain = split_entity_id(state.entity_id)[0]

# Retract some sensitive state attributes
if entity_domain == device_tracker.DOMAIN:
state_dict["attributes"] = async_redact_data(
state_dict["attributes"], REDACT_STATE_DEVICE_TRACKER
)

data["entities"].append(
{
"device_class": entity_entry.device_class,
"disabled_by": entity_entry.disabled_by,
"disabled": entity_entry.disabled,
"entity_category": entity_entry.entity_category,
"entity_id": entity_entry.entity_id,
"icon": entity_entry.icon,
"original_device_class": entity_entry.original_device_class,
"original_icon": entity_entry.original_icon,
"state": state_dict,
"unit_of_measurement": entity_entry.unit_of_measurement,
}
)
if not state:
return None

state_dict = dict(state.as_dict())

# The context doesn't provide useful information in this case.
state_dict.pop("context", None)

entity_domain = split_entity_id(state.entity_id)[0]

# Retract some sensitive state attributes
if entity_domain == device_tracker.DOMAIN:
state_dict["attributes"] = async_redact_data(
state_dict["attributes"], REDACT_STATE_DEVICE_TRACKER
)
return state_dict

data["entities"].extend(
{
"device_class": entity_entry.device_class,
"disabled_by": entity_entry.disabled_by,
"disabled": entity_entry.disabled,
"entity_category": entity_entry.entity_category,
"entity_id": entity_entry.entity_id,
"icon": entity_entry.icon,
"original_device_class": entity_entry.original_device_class,
"original_icon": entity_entry.original_icon,
"state": state_dict,
"unit_of_measurement": entity_entry.unit_of_measurement,
}
for entity_entry in entities
if (state_dict := _state_dict(entity_entry)) is not None
)

return data
19 changes: 11 additions & 8 deletions homeassistant/components/mqtt/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,14 +403,17 @@ async def async_integration_message_received(
):
mqtt_data.integration_unsubscribe.pop(key)()

for topic in topics:
key = f"{integration}_{topic}"
mqtt_data.integration_unsubscribe[key] = await mqtt.async_subscribe(
hass,
topic,
functools.partial(async_integration_message_received, integration),
0,
)
mqtt_data.integration_unsubscribe.update(
{
f"{integration}_{topic}": await mqtt.async_subscribe(
hass,
topic,
functools.partial(async_integration_message_received, integration),
0,
)
for topic in topics
}
)


async def async_stop(hass: HomeAssistant) -> None:
Expand Down
19 changes: 9 additions & 10 deletions homeassistant/components/mqtt/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,25 +319,24 @@ def _setup_from_config(self, config: ConfigType) -> None:
ATTR_PRESET_MODE: config.get(CONF_PRESET_MODE_COMMAND_TEMPLATE),
ATTR_OSCILLATING: config.get(CONF_OSCILLATION_COMMAND_TEMPLATE),
}
self._command_templates = {}
for key, tpl in command_templates.items():
self._command_templates[key] = MqttCommandTemplate(
tpl, entity=self
).async_render
self._command_templates = {
key: MqttCommandTemplate(tpl, entity=self).async_render
for key, tpl in command_templates.items()
}

self._value_templates = {}
value_templates: dict[str, Template | None] = {
CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE),
ATTR_DIRECTION: config.get(CONF_DIRECTION_VALUE_TEMPLATE),
ATTR_PERCENTAGE: config.get(CONF_PERCENTAGE_VALUE_TEMPLATE),
ATTR_PRESET_MODE: config.get(CONF_PRESET_MODE_VALUE_TEMPLATE),
ATTR_OSCILLATING: config.get(CONF_OSCILLATION_VALUE_TEMPLATE),
}
for key, tpl in value_templates.items():
self._value_templates[key] = MqttValueTemplate(
tpl,
entity=self,
self._value_templates = {
key: MqttValueTemplate(
tpl, entity=self
).async_render_with_possible_json_value
for key, tpl in value_templates.items()
}

def _prepare_subscribe_topics(self) -> None:
"""(Re)Subscribe to topics."""
Expand Down
16 changes: 8 additions & 8 deletions homeassistant/components/mqtt/humidifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,30 +254,30 @@ def _setup_from_config(self, config: ConfigType) -> None:
)
self._optimistic_mode = optimistic or self._topic[CONF_MODE_STATE_TOPIC] is None

self._command_templates = {}
command_templates: dict[str, Template | None] = {
CONF_STATE: config.get(CONF_COMMAND_TEMPLATE),
ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_COMMAND_TEMPLATE),
ATTR_MODE: config.get(CONF_MODE_COMMAND_TEMPLATE),
}
for key, tpl in command_templates.items():
self._command_templates[key] = MqttCommandTemplate(
tpl, entity=self
).async_render
self._command_templates = {
key: MqttCommandTemplate(tpl, entity=self).async_render
for key, tpl in command_templates.items()
}

self._value_templates = {}
value_templates: dict[str, Template | None] = {
ATTR_ACTION: config.get(CONF_ACTION_TEMPLATE),
ATTR_CURRENT_HUMIDITY: config.get(CONF_CURRENT_HUMIDITY_TEMPLATE),
CONF_STATE: config.get(CONF_STATE_VALUE_TEMPLATE),
ATTR_HUMIDITY: config.get(CONF_TARGET_HUMIDITY_STATE_TEMPLATE),
ATTR_MODE: config.get(CONF_MODE_STATE_TEMPLATE),
}
for key, tpl in value_templates.items():
self._value_templates[key] = MqttValueTemplate(
self._value_templates = {
key: MqttValueTemplate(
tpl,
entity=self,
).async_render_with_possible_json_value
for key, tpl in value_templates.items()
}

def add_subscription(
self,
Expand Down
16 changes: 10 additions & 6 deletions homeassistant/components/mqtt/siren.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,13 @@ async def async_turn_off(self, **kwargs: Any) -> None:

def _update(self, data: SirenTurnOnServiceParameters) -> None:
"""Update the extra siren state attributes."""
for attribute, support in SUPPORTED_ATTRIBUTES.items():
if self._attr_supported_features & support and attribute in data:
data_attr = data[attribute] # type: ignore[literal-required]
if self._extra_attributes.get(attribute) == data_attr:
continue
self._extra_attributes[attribute] = data_attr
self._extra_attributes.update(
{
attribute: data_attr
for attribute, support in SUPPORTED_ATTRIBUTES.items()
if self._attr_supported_features & support
and attribute in data
and (data_attr := data[attribute]) # type: ignore[literal-required]
!= self._extra_attributes.get(attribute)
}
)
27 changes: 11 additions & 16 deletions homeassistant/components/mqtt/water_heater.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,28 +226,23 @@ def _setup_from_config(self, config: ConfigType) -> None:
if self._topic[CONF_MODE_STATE_TOPIC] is None or self._optimistic:
self._attr_current_operation = STATE_OFF

value_templates: dict[str, Template | None] = {}
for key in VALUE_TEMPLATE_KEYS:
value_templates[key] = None
if CONF_VALUE_TEMPLATE in config:
value_templates = {
key: config.get(CONF_VALUE_TEMPLATE) for key in VALUE_TEMPLATE_KEYS
}
for key in VALUE_TEMPLATE_KEYS & config.keys():
value_templates[key] = config[key]
value_templates: dict[str, Template | None] = {
key: config.get(CONF_VALUE_TEMPLATE) for key in VALUE_TEMPLATE_KEYS
}
value_templates.update(
{key: config[key] for key in VALUE_TEMPLATE_KEYS & config.keys()}
)
self._value_templates = {
key: MqttValueTemplate(
template,
entity=self,
template, entity=self
).async_render_with_possible_json_value
for key, template in value_templates.items()
}

self._command_templates = {}
for key in COMMAND_TEMPLATE_KEYS:
self._command_templates[key] = MqttCommandTemplate(
config.get(key), entity=self
).async_render
self._command_templates = {
key: MqttCommandTemplate(config.get(key), entity=self).async_render
for key in COMMAND_TEMPLATE_KEYS
}

support = WaterHeaterEntityFeature(0)
if (self._topic[CONF_TEMP_STATE_TOPIC] is not None) or (
Expand Down
Loading

0 comments on commit 488dae4

Please sign in to comment.