Skip to content

Commit 0c9e92f

Browse files
authored
Add MQTT text subentry support (home-assistant#156686)
1 parent bfdff46 commit 0c9e92f

File tree

6 files changed

+195
-7
lines changed

6 files changed

+195
-7
lines changed

homeassistant/components/mqtt/config_flow.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@
239239
CONF_OSCILLATION_COMMAND_TOPIC,
240240
CONF_OSCILLATION_STATE_TOPIC,
241241
CONF_OSCILLATION_VALUE_TEMPLATE,
242+
CONF_PATTERN,
242243
CONF_PAYLOAD_ARM_AWAY,
243244
CONF_PAYLOAD_ARM_CUSTOM_BYPASS,
244245
CONF_PAYLOAD_ARM_HOME,
@@ -465,6 +466,7 @@
465466
Platform.SENSOR,
466467
Platform.SIREN,
467468
Platform.SWITCH,
469+
Platform.TEXT,
468470
]
469471

470472
_CODE_VALIDATION_MODE = {
@@ -819,6 +821,16 @@
819821
mode=SelectSelectorMode.DROPDOWN,
820822
)
821823
)
824+
TEXT_MODE_SELECTOR = SelectSelector(
825+
SelectSelectorConfig(
826+
options=[TextSelectorType.TEXT.value, TextSelectorType.PASSWORD.value],
827+
mode=SelectSelectorMode.DROPDOWN,
828+
translation_key="text_mode",
829+
)
830+
)
831+
TEXT_SIZE_SELECTOR = NumberSelector(
832+
NumberSelectorConfig(min=0, max=255, step=1, mode=NumberSelectorMode.BOX)
833+
)
822834

823835

824836
@callback
@@ -1151,6 +1163,22 @@ def validate_sensor_platform_config(
11511163
return errors
11521164

11531165

1166+
@callback
1167+
def validate_text_platform_config(
1168+
config: dict[str, Any],
1169+
) -> dict[str, str]:
1170+
"""Validate the text entity options."""
1171+
errors: dict[str, str] = {}
1172+
if (
1173+
CONF_MIN in config
1174+
and CONF_MAX in config
1175+
and config[CONF_MIN] > config[CONF_MAX]
1176+
):
1177+
errors["text_advanced_settings"] = "max_below_min"
1178+
1179+
return errors
1180+
1181+
11541182
ENTITY_CONFIG_VALIDATOR: dict[
11551183
str,
11561184
Callable[[dict[str, Any]], dict[str, str]] | None,
@@ -1170,6 +1198,7 @@ def validate_sensor_platform_config(
11701198
Platform.SENSOR: validate_sensor_platform_config,
11711199
Platform.SIREN: None,
11721200
Platform.SWITCH: None,
1201+
Platform.TEXT: validate_text_platform_config,
11731202
}
11741203

11751204

@@ -1430,6 +1459,7 @@ class PlatformField:
14301459
selector=SWITCH_DEVICE_CLASS_SELECTOR, required=False
14311460
),
14321461
},
1462+
Platform.TEXT: {},
14331463
}
14341464
PLATFORM_MQTT_FIELDS: dict[Platform, dict[str, PlatformField]] = {
14351465
Platform.ALARM_CONTROL_PANEL: {
@@ -3298,6 +3328,58 @@ class PlatformField:
32983328
CONF_RETAIN: PlatformField(selector=BOOLEAN_SELECTOR, required=False),
32993329
CONF_OPTIMISTIC: PlatformField(selector=BOOLEAN_SELECTOR, required=False),
33003330
},
3331+
Platform.TEXT: {
3332+
CONF_COMMAND_TOPIC: PlatformField(
3333+
selector=TEXT_SELECTOR,
3334+
required=True,
3335+
validator=valid_publish_topic,
3336+
error="invalid_publish_topic",
3337+
),
3338+
CONF_COMMAND_TEMPLATE: PlatformField(
3339+
selector=TEMPLATE_SELECTOR,
3340+
required=False,
3341+
validator=validate(cv.template),
3342+
error="invalid_template",
3343+
),
3344+
CONF_STATE_TOPIC: PlatformField(
3345+
selector=TEXT_SELECTOR,
3346+
required=False,
3347+
validator=valid_subscribe_topic,
3348+
error="invalid_subscribe_topic",
3349+
),
3350+
CONF_VALUE_TEMPLATE: PlatformField(
3351+
selector=TEMPLATE_SELECTOR,
3352+
required=False,
3353+
validator=validate(cv.template),
3354+
error="invalid_template",
3355+
),
3356+
CONF_RETAIN: PlatformField(selector=BOOLEAN_SELECTOR, required=False),
3357+
CONF_MIN: PlatformField(
3358+
selector=TEXT_SIZE_SELECTOR,
3359+
required=True,
3360+
default=0,
3361+
section="text_advanced_settings",
3362+
),
3363+
CONF_MAX: PlatformField(
3364+
selector=TEXT_SIZE_SELECTOR,
3365+
required=True,
3366+
default=255,
3367+
section="text_advanced_settings",
3368+
),
3369+
CONF_MODE: PlatformField(
3370+
selector=TEXT_MODE_SELECTOR,
3371+
required=True,
3372+
default=TextSelectorType.TEXT.value,
3373+
section="text_advanced_settings",
3374+
),
3375+
CONF_PATTERN: PlatformField(
3376+
selector=TEXT_SELECTOR,
3377+
required=False,
3378+
validator=validate(cv.is_regex),
3379+
error="invalid_regular_expression",
3380+
section="text_advanced_settings",
3381+
),
3382+
},
33013383
}
33023384
MQTT_DEVICE_PLATFORM_FIELDS = {
33033385
ATTR_NAME: PlatformField(selector=TEXT_SELECTOR, required=True),

homeassistant/components/mqtt/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@
138138
CONF_OSCILLATION_COMMAND_TEMPLATE = "oscillation_command_template"
139139
CONF_OSCILLATION_STATE_TOPIC = "oscillation_state_topic"
140140
CONF_OSCILLATION_VALUE_TEMPLATE = "oscillation_value_template"
141+
CONF_PATTERN = "pattern"
141142
CONF_PAYLOAD_ARM_AWAY = "payload_arm_away"
142143
CONF_PAYLOAD_ARM_CUSTOM_BYPASS = "payload_arm_custom_bypass"
143144
CONF_PAYLOAD_ARM_HOME = "payload_arm_home"

homeassistant/components/mqtt/strings.json

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,21 @@
970970
"temperature_state_topic": "The MQTT topic to subscribe for changes of the target temperature. [Learn more.]({url}#temperature_state_topic)"
971971
},
972972
"name": "Target temperature settings"
973+
},
974+
"text_advanced_settings": {
975+
"data": {
976+
"max": "Maximum length",
977+
"min": "Mininum length",
978+
"mode": "Mode",
979+
"pattern": "Pattern"
980+
},
981+
"data_description": {
982+
"max": "Maximum length of the text input",
983+
"min": "Mininum length of the text input",
984+
"mode": "Mode of the text input",
985+
"pattern": "A valid regex pattern"
986+
},
987+
"name": "Advanced text settings"
973988
}
974989
},
975990
"title": "Configure MQTT device \"{mqtt_device}\""
@@ -1387,7 +1402,8 @@
13871402
"select": "[%key:component::select::title%]",
13881403
"sensor": "[%key:component::sensor::title%]",
13891404
"siren": "[%key:component::siren::title%]",
1390-
"switch": "[%key:component::switch::title%]"
1405+
"switch": "[%key:component::switch::title%]",
1406+
"text": "[%key:component::text::title%]"
13911407
}
13921408
},
13931409
"set_ca_cert": {
@@ -1424,6 +1440,12 @@
14241440
"none": "No target temperature",
14251441
"single": "Single target temperature"
14261442
}
1443+
},
1444+
"text_mode": {
1445+
"options": {
1446+
"password": "[%key:common::config_flow::data::password%]",
1447+
"text": "[%key:component::text::entity_component::_::state_attributes::mode::state::text%]"
1448+
}
14271449
}
14281450
},
14291451
"services": {

homeassistant/components/mqtt/text.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@
2727

2828
from . import subscription
2929
from .config import MQTT_RW_SCHEMA
30-
from .const import CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, CONF_STATE_TOPIC
30+
from .const import (
31+
CONF_COMMAND_TEMPLATE,
32+
CONF_COMMAND_TOPIC,
33+
CONF_MAX,
34+
CONF_MIN,
35+
CONF_PATTERN,
36+
CONF_STATE_TOPIC,
37+
)
3138
from .entity import MqttEntity, async_setup_entity_entry_helper
3239
from .models import (
3340
MqttCommandTemplate,
@@ -42,12 +49,7 @@
4249

4350
PARALLEL_UPDATES = 0
4451

45-
CONF_MAX = "max"
46-
CONF_MIN = "min"
47-
CONF_PATTERN = "pattern"
48-
4952
DEFAULT_NAME = "MQTT Text"
50-
DEFAULT_PAYLOAD_RESET = "None"
5153

5254
MQTT_TEXT_ATTRIBUTES_BLOCKED = frozenset(
5355
{

tests/components/mqtt/common.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,23 @@
600600
"optimistic": True,
601601
},
602602
}
603+
MOCK_SUBENTRY_TEXT_COMPONENT = {
604+
"09261f6feed443e7b7d5f3fbe2a47413": {
605+
"platform": "text",
606+
"name": "MOTD",
607+
"entity_category": None,
608+
"command_topic": "test-topic",
609+
"command_template": "{{ value }}",
610+
"state_topic": "test-topic",
611+
"min": 0.0,
612+
"max": 10.0,
613+
"mode": "password",
614+
"pattern": "^[a-z_]*$",
615+
"value_template": "{{ value_json.value }}",
616+
"retain": False,
617+
"entity_picture": "https://example.com/09261f6feed443e7b7d5f3fbe2a47413",
618+
},
619+
}
603620

604621
MOCK_SUBENTRY_AVAILABILITY_DATA = {
605622
"availability": {
@@ -725,6 +742,10 @@
725742
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
726743
"components": MOCK_SUBENTRY_SWITCH_COMPONENT,
727744
}
745+
MOCK_TEXT_SUBENTRY_DATA = {
746+
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
747+
"components": MOCK_SUBENTRY_TEXT_COMPONENT,
748+
}
728749
MOCK_SUBENTRY_DATA_BAD_COMPONENT_SCHEMA = {
729750
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
730751
"components": MOCK_SUBENTRY_NOTIFY_BAD_SCHEMA,

tests/components/mqtt/test_config_flow.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
MOCK_SENSOR_SUBENTRY_DATA_STATE_CLASS,
6363
MOCK_SIREN_SUBENTRY_DATA,
6464
MOCK_SWITCH_SUBENTRY_DATA,
65+
MOCK_TEXT_SUBENTRY_DATA,
6566
)
6667

6768
from tests.common import MockConfigEntry, MockMqttReasonCode, get_schema_suggested_value
@@ -3720,6 +3721,65 @@ async def test_migrate_of_incompatible_config_entry(
37203721
"Milk notifier Outlet",
37213722
id="switch",
37223723
),
3724+
pytest.param(
3725+
MOCK_TEXT_SUBENTRY_DATA,
3726+
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
3727+
{"name": "MOTD"},
3728+
{},
3729+
(),
3730+
{
3731+
"command_topic": "test-topic",
3732+
"command_template": "{{ value }}",
3733+
"state_topic": "test-topic",
3734+
"value_template": "{{ value_json.value }}",
3735+
"retain": False,
3736+
"text_advanced_settings": {
3737+
"min": 0,
3738+
"max": 10,
3739+
"mode": "password",
3740+
"pattern": "^[a-z_]*$",
3741+
},
3742+
},
3743+
(
3744+
(
3745+
{"command_topic": "test-topic#invalid"},
3746+
{"command_topic": "invalid_publish_topic"},
3747+
),
3748+
(
3749+
{
3750+
"command_topic": "test-topic",
3751+
"state_topic": "test-topic#invalid",
3752+
},
3753+
{"state_topic": "invalid_subscribe_topic"},
3754+
),
3755+
(
3756+
{
3757+
"command_topic": "test-topic",
3758+
"text_advanced_settings": {
3759+
"min": 20,
3760+
"max": 10,
3761+
"mode": "password",
3762+
"pattern": "^[a-z_]*$",
3763+
},
3764+
},
3765+
{"text_advanced_settings": "max_below_min"},
3766+
),
3767+
(
3768+
{
3769+
"command_topic": "test-topic",
3770+
"text_advanced_settings": {
3771+
"min": 0,
3772+
"max": 10,
3773+
"mode": "password",
3774+
"pattern": "(",
3775+
},
3776+
},
3777+
{"text_advanced_settings": "invalid_regular_expression"},
3778+
),
3779+
),
3780+
"Milk notifier MOTD",
3781+
id="text",
3782+
),
37233783
],
37243784
)
37253785
async def test_subentry_configflow(

0 commit comments

Comments
 (0)