mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 00:39:22 +00:00
Compare commits
8 Commits
add-includ
...
mqtt-entit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3e4676b4c | ||
|
|
f852220282 | ||
|
|
5dd3bf04eb | ||
|
|
b0c2fdc57b | ||
|
|
617d44ffcf | ||
|
|
8fb8eed1c8 | ||
|
|
1ddbd4755b | ||
|
|
3bd76294dc |
@@ -73,6 +73,7 @@ ABBREVIATIONS = {
|
||||
"fan_mode_stat_t": "fan_mode_state_topic",
|
||||
"frc_upd": "force_update",
|
||||
"g_tpl": "green_template",
|
||||
"grp": "group",
|
||||
"hs_cmd_t": "hs_command_topic",
|
||||
"hs_cmd_tpl": "hs_command_template",
|
||||
"hs_stat_t": "hs_state_topic",
|
||||
|
||||
@@ -10,6 +10,7 @@ from homeassistant.helpers import config_validation as cv
|
||||
from .const import (
|
||||
CONF_COMMAND_TOPIC,
|
||||
CONF_ENCODING,
|
||||
CONF_GROUP,
|
||||
CONF_QOS,
|
||||
CONF_RETAIN,
|
||||
CONF_STATE_TOPIC,
|
||||
@@ -23,6 +24,7 @@ from .util import valid_publish_topic, valid_qos_schema, valid_subscribe_topic
|
||||
SCHEMA_BASE = {
|
||||
vol.Optional(CONF_QOS, default=DEFAULT_QOS): valid_qos_schema,
|
||||
vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string,
|
||||
vol.Optional(CONF_GROUP): vol.All(cv.ensure_list, [cv.string]),
|
||||
}
|
||||
|
||||
MQTT_BASE_SCHEMA = vol.Schema(SCHEMA_BASE)
|
||||
|
||||
@@ -110,6 +110,7 @@ CONF_FLASH_TIME_SHORT = "flash_time_short"
|
||||
CONF_GET_POSITION_TEMPLATE = "position_template"
|
||||
CONF_GET_POSITION_TOPIC = "position_topic"
|
||||
CONF_GREEN_TEMPLATE = "green_template"
|
||||
CONF_GROUP = "group"
|
||||
CONF_HS_COMMAND_TEMPLATE = "hs_command_template"
|
||||
CONF_HS_COMMAND_TOPIC = "hs_command_topic"
|
||||
CONF_HS_STATE_TOPIC = "hs_state_topic"
|
||||
|
||||
@@ -79,6 +79,7 @@ from .const import (
|
||||
CONF_ENABLED_BY_DEFAULT,
|
||||
CONF_ENCODING,
|
||||
CONF_ENTITY_PICTURE,
|
||||
CONF_GROUP,
|
||||
CONF_HW_VERSION,
|
||||
CONF_IDENTIFIERS,
|
||||
CONF_JSON_ATTRS_TEMPLATE,
|
||||
@@ -136,6 +137,7 @@ MQTT_ATTRIBUTES_BLOCKED = {
|
||||
"device_class",
|
||||
"device_info",
|
||||
"entity_category",
|
||||
"entity_id",
|
||||
"entity_picture",
|
||||
"entity_registry_enabled_default",
|
||||
"extra_state_attributes",
|
||||
@@ -475,6 +477,8 @@ class MqttAttributesMixin(Entity):
|
||||
def __init__(self, config: ConfigType) -> None:
|
||||
"""Initialize the JSON attributes mixin."""
|
||||
self._attributes_sub_state: dict[str, EntitySubscription] = {}
|
||||
if CONF_GROUP in config:
|
||||
self._attr_included_unique_ids = config[CONF_GROUP]
|
||||
self._attributes_config = config
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
@@ -546,7 +550,7 @@ class MqttAttributesMixin(Entity):
|
||||
_LOGGER.warning("Erroneous JSON: %s", payload)
|
||||
else:
|
||||
if isinstance(json_dict, dict):
|
||||
filtered_dict = {
|
||||
filtered_dict: dict[str, Any] = {
|
||||
k: v
|
||||
for k, v in json_dict.items()
|
||||
if k not in MQTT_ATTRIBUTES_BLOCKED
|
||||
|
||||
@@ -82,6 +82,7 @@ light:
|
||||
"""
|
||||
|
||||
import copy
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import call, patch
|
||||
|
||||
@@ -100,6 +101,7 @@ from homeassistant.const import (
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util.json import json_loads
|
||||
|
||||
from .common import (
|
||||
@@ -169,6 +171,39 @@ COLOR_MODES_CONFIG = {
|
||||
}
|
||||
}
|
||||
|
||||
GROUP_MEMBER_1_TOPIC = "homeassistant/light/member_1/config"
|
||||
GROUP_MEMBER_2_TOPIC = "homeassistant/light/member_2/config"
|
||||
GROUP_TOPIC = "homeassistant/light/group/config"
|
||||
GROUP_DISCOVERY_MEMBER_1_CONFIG = json.dumps(
|
||||
{
|
||||
"schema": "json",
|
||||
"command_topic": "test-command-topic-member1",
|
||||
"unique_id": "very_unique_member1",
|
||||
"name": "member1",
|
||||
"default_entity_id": "light.member1",
|
||||
}
|
||||
)
|
||||
GROUP_DISCOVERY_MEMBER_2_CONFIG = json.dumps(
|
||||
{
|
||||
"schema": "json",
|
||||
"command_topic": "test-command-topic-member2",
|
||||
"unique_id": "very_unique_member2",
|
||||
"name": "member2",
|
||||
"default_entity_id": "light.member2",
|
||||
}
|
||||
)
|
||||
GROUP_DISCOVERY_LIGHT_GROUP_CONFIG = json.dumps(
|
||||
{
|
||||
"schema": "json",
|
||||
"command_topic": "test-command-topic-group",
|
||||
"state_topic": "test-state-topic-group",
|
||||
"unique_id": "very_unique_group",
|
||||
"name": "group",
|
||||
"default_entity_id": "light.group",
|
||||
"group": ["very_unique_member1", "very_unique_member2"],
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class JsonValidator:
|
||||
"""Helper to compare JSON."""
|
||||
@@ -1859,6 +1894,86 @@ async def test_white_scale(
|
||||
assert state.attributes.get("brightness") == 129
|
||||
|
||||
|
||||
async def test_light_group_discovery_members_before_group(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
"""Test the discovery of a light group and linked entity IDs.
|
||||
|
||||
The members are discovered first, so they are known in the entity registry.
|
||||
"""
|
||||
await mqtt_mock_entry()
|
||||
# Discover light group members
|
||||
async_fire_mqtt_message(hass, GROUP_MEMBER_1_TOPIC, GROUP_DISCOVERY_MEMBER_1_CONFIG)
|
||||
async_fire_mqtt_message(hass, GROUP_MEMBER_2_TOPIC, GROUP_DISCOVERY_MEMBER_2_CONFIG)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Discover group
|
||||
async_fire_mqtt_message(hass, GROUP_TOPIC, GROUP_DISCOVERY_LIGHT_GROUP_CONFIG)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("light.member1") is not None
|
||||
assert hass.states.get("light.member2") is not None
|
||||
group_state = hass.states.get("light.group")
|
||||
assert group_state is not None
|
||||
assert group_state.attributes.get("entity_id") == ["light.member1", "light.member2"]
|
||||
|
||||
|
||||
async def test_light_group_discovery_group_before_members(
|
||||
hass: HomeAssistant,
|
||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test the discovery of a light group and linked entity IDs.
|
||||
|
||||
The group is discovered first, so the group members are
|
||||
not (all) known yet in the entity registry.
|
||||
The entity property should be updates as soon as member entities
|
||||
are discovered, updated or removed.
|
||||
"""
|
||||
await mqtt_mock_entry()
|
||||
|
||||
# Discover group
|
||||
async_fire_mqtt_message(hass, GROUP_TOPIC, GROUP_DISCOVERY_LIGHT_GROUP_CONFIG)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Discover light group members
|
||||
async_fire_mqtt_message(hass, GROUP_MEMBER_1_TOPIC, GROUP_DISCOVERY_MEMBER_1_CONFIG)
|
||||
async_fire_mqtt_message(hass, GROUP_MEMBER_2_TOPIC, GROUP_DISCOVERY_MEMBER_2_CONFIG)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("light.member1") is not None
|
||||
assert hass.states.get("light.member2") is not None
|
||||
|
||||
group_state = hass.states.get("light.group")
|
||||
assert group_state is not None
|
||||
assert group_state.attributes.get("entity_id") == ["light.member1", "light.member2"]
|
||||
|
||||
# Remove member 1
|
||||
async_fire_mqtt_message(hass, GROUP_MEMBER_1_TOPIC, "")
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("light.member1") is None
|
||||
assert hass.states.get("light.member2") is not None
|
||||
|
||||
group_state = hass.states.get("light.group")
|
||||
assert group_state is not None
|
||||
assert group_state.attributes.get("entity_id") == ["light.member2"]
|
||||
|
||||
# Rename member 2
|
||||
entity_registry.async_update_entity(
|
||||
"light.member2", new_entity_id="light.member2_updated"
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
group_state = hass.states.get("light.group")
|
||||
assert group_state is not None
|
||||
assert group_state.attributes.get("entity_id") == ["light.member2_updated"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[
|
||||
@@ -2040,7 +2155,7 @@ async def test_custom_availability_payload(
|
||||
)
|
||||
|
||||
|
||||
async def test_setting_attribute_via_mqtt_json_message(
|
||||
async def test_setting_attribute_via_mqtt_json_message_single_light(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
"""Test the setting of attribute via MQTT with JSON payload."""
|
||||
@@ -2049,6 +2164,51 @@ async def test_setting_attribute_via_mqtt_json_message(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[
|
||||
help_custom_config(
|
||||
light.DOMAIN,
|
||||
DEFAULT_CONFIG,
|
||||
(
|
||||
{
|
||||
"unique_id": "very_unique_member_1",
|
||||
"name": "Part 1",
|
||||
"default_entity_id": "light.member_1",
|
||||
},
|
||||
{
|
||||
"unique_id": "very_unique_member_2",
|
||||
"name": "Part 2",
|
||||
"default_entity_id": "light.member_2",
|
||||
},
|
||||
{
|
||||
"unique_id": "very_unique_group",
|
||||
"name": "My group",
|
||||
"default_entity_id": "light.my_group",
|
||||
"json_attributes_topic": "attr-topic",
|
||||
"group": [
|
||||
"very_unique_member_1",
|
||||
"very_unique_member_2",
|
||||
"member_3_not_exists",
|
||||
],
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
async def test_setting_attribute_via_mqtt_json_message_light_group(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
"""Test the setting of attribute via MQTT with JSON payload."""
|
||||
await mqtt_mock_entry()
|
||||
|
||||
async_fire_mqtt_message(hass, "attr-topic", '{ "val": "100" }')
|
||||
state = hass.states.get("light.my_group")
|
||||
|
||||
assert state and state.attributes.get("val") == "100"
|
||||
assert state.attributes.get("entity_id") == ["light.member_1", "light.member_2"]
|
||||
|
||||
|
||||
async def test_setting_blocked_attribute_via_mqtt_json_message(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user