Skip to content

Commit cd585df

Browse files
committed
Add switch as entity platform on MQTT subentries
1 parent a34d020 commit cd585df

File tree

4 files changed

+102
-6
lines changed

4 files changed

+102
-6
lines changed

homeassistant/components/mqtt/config_flow.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
SensorDeviceClass,
3434
SensorStateClass,
3535
)
36+
from homeassistant.components.switch import SwitchDeviceClass
3637
from homeassistant.config_entries import (
3738
SOURCE_RECONFIGURE,
3839
ConfigEntry,
@@ -55,6 +56,7 @@
5556
CONF_DISCOVERY,
5657
CONF_HOST,
5758
CONF_NAME,
59+
CONF_OPTIMISTIC,
5860
CONF_PASSWORD,
5961
CONF_PAYLOAD,
6062
CONF_PLATFORM,
@@ -234,7 +236,7 @@
234236
)
235237

236238
# Subentry selectors
237-
SUBENTRY_PLATFORMS = [Platform.NOTIFY, Platform.SENSOR]
239+
SUBENTRY_PLATFORMS = [Platform.NOTIFY, Platform.SENSOR, Platform.SWITCH]
238240
SUBENTRY_PLATFORM_SELECTOR = SelectSelector(
239241
SelectSelectorConfig(
240242
options=[platform.value for platform in SUBENTRY_PLATFORMS],
@@ -287,6 +289,15 @@
287289
NumberSelectorConfig(mode=NumberSelectorMode.BOX, min=0)
288290
)
289291

292+
# Switch specific selectors
293+
SWITCH_DEVICE_CLASS_SELECTOR = SelectSelector(
294+
SelectSelectorConfig(
295+
options=[device_class.value for device_class in SwitchDeviceClass],
296+
mode=SelectSelectorMode.DROPDOWN,
297+
translation_key="device_class_switch",
298+
)
299+
)
300+
290301

291302
@dataclass(frozen=True)
292303
class PlatformField:
@@ -350,6 +361,9 @@ def unit_of_measurement_selector(user_data: dict[str, Any | None]) -> Selector:
350361
conditions=({"device_class": "enum"},),
351362
),
352363
},
364+
Platform.SWITCH.value: {
365+
CONF_DEVICE_CLASS: PlatformField(SWITCH_DEVICE_CLASS_SELECTOR, False, str),
366+
},
353367
}
354368
PLATFORM_MQTT_FIELDS = {
355369
Platform.NOTIFY.value: {
@@ -379,13 +393,30 @@ def unit_of_measurement_selector(user_data: dict[str, Any | None]) -> Selector:
379393
EXIRE_AFTER_SELECTOR, False, cv.positive_int, section="advanced_settings"
380394
),
381395
},
396+
Platform.SWITCH.value: {
397+
CONF_COMMAND_TOPIC: PlatformField(
398+
TEXT_SELECTOR, True, valid_publish_topic, "invalid_publish_topic"
399+
),
400+
CONF_COMMAND_TEMPLATE: PlatformField(
401+
TEMPLATE_SELECTOR, False, cv.template, "invalid_template"
402+
),
403+
CONF_STATE_TOPIC: PlatformField(
404+
TEXT_SELECTOR, False, valid_subscribe_topic, "invalid_subscribe_topic"
405+
),
406+
CONF_VALUE_TEMPLATE: PlatformField(
407+
TEMPLATE_SELECTOR, False, cv.template, "invalid_template"
408+
),
409+
CONF_RETAIN: PlatformField(BOOLEAN_SELECTOR, False, bool),
410+
CONF_OPTIMISTIC: PlatformField(BOOLEAN_SELECTOR, False, bool),
411+
},
382412
}
383413
ENTITY_CONFIG_VALIDATOR: dict[
384414
str,
385415
Callable[[dict[str, Any], dict[str, str], list[str] | None], dict[str, Any]] | None,
386416
] = {
387417
Platform.NOTIFY.value: None,
388418
Platform.SENSOR.value: validate_sensor_state_and_device_class_config,
419+
Platform.SWITCH.value: None,
389420
}
390421

391422
MQTT_DEVICE_PLATFORM_FIELDS = {

homeassistant/components/mqtt/strings.json

+14-3
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,9 @@
246246
"value_template": "Value template",
247247
"last_reset_value_template": "Last reset value template",
248248
"force_update": "Force update",
249-
"retain": "Retain"
249+
"optimistic": "Optimistic",
250+
"retain": "Retain",
251+
"qos": "QoS"
250252
},
251253
"data_description": {
252254
"command_topic": "The publishing topic that will be used to control the {platform} entity. [Learn more.]({url}#command_topic)",
@@ -255,7 +257,9 @@
255257
"value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the {platform} entity value.",
256258
"last_reset_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the last reset. When Last reset template is set, the State class option must be Total. [Learn more.]({url}#last_reset_value_template)",
257259
"force_update": "Sends update events even if the value hasn’t changed. Useful if you want to have meaningful value graphs in history. [Learn more.]({url}#force_update)",
258-
"retain": "Select if values published by the {platform} entity should be retained at the MQTT broker."
260+
"optimistic": "Flag that defines if the {platform} entity works in optimistic mode. [Learn more.]({url}#optimistic)",
261+
"retain": "Select if values published by the {platform} entity should be retained at the MQTT broker.",
262+
"qos": "The QoS value {platform} entity should use."
259263
},
260264
"sections": {
261265
"advanced_settings": {
@@ -462,10 +466,17 @@
462466
"wind_speed": "[%key:component::sensor::entity_component::wind_speed::name%]"
463467
}
464468
},
469+
"device_class_switch": {
470+
"options": {
471+
"outlet": "[%key:component::switch::entity_component::outlet::name%]",
472+
"switch": "[%key:component::switch::title%]"
473+
}
474+
},
465475
"platform": {
466476
"options": {
467477
"notify": "Notify",
468-
"sensor": "Sensor"
478+
"sensor": "Sensor",
479+
"switch": "Switch"
469480
}
470481
},
471482
"set_ca_cert": {

tests/components/mqtt/common.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,19 @@
125125
"entity_picture": "https://example.com/e9261f6feed443e7b7d5f3fbe2a47412",
126126
},
127127
}
128+
MOCK_SUBENTRY_SWITCH_COMPONENT = {
129+
"3faf1318016c46c5aea26707eeb6f12e": {
130+
"platform": "switch",
131+
"name": "Outlet",
132+
"device_class": "outlet",
133+
"command_topic": "test-topic",
134+
"state_topic": "test-topic",
135+
"command_template": "{{ value }}",
136+
"value_template": "{{ value_json.value }}",
137+
"entity_picture": "https://example.com/3faf1318016c46c5aea26707eeb6f12e",
138+
"optimistic": True,
139+
},
140+
}
128141

129142
# Bogus light component just for code coverage
130143
# Note that light cannot be setup through the UI yet
@@ -223,7 +236,17 @@
223236
},
224237
"components": MOCK_SUBENTRY_SENSOR_COMPONENT_LAST_RESET,
225238
}
226-
239+
MOCK_SWITCH_SUBENTRY_DATA_SINGLE_STATE_CLASS = {
240+
"device": {
241+
"name": "Test switch",
242+
"sw_version": "1.0",
243+
"hw_version": "2.1 rev a",
244+
"model": "Model XL",
245+
"model_id": "mn002",
246+
"configuration_url": "https://example.com",
247+
},
248+
"components": MOCK_SUBENTRY_SWITCH_COMPONENT,
249+
}
227250
MOCK_SUBENTRY_DATA_BAD_COMPONENT_SCHEMA = {
228251
"device": {
229252
"name": "Milk notifier",
@@ -246,7 +269,8 @@
246269
},
247270
"components": MOCK_SUBENTRY_NOTIFY_COMPONENT1
248271
| MOCK_SUBENTRY_NOTIFY_COMPONENT2
249-
| MOCK_SUBENTRY_LIGHT_COMPONENT,
272+
| MOCK_SUBENTRY_LIGHT_COMPONENT
273+
| MOCK_SUBENTRY_SWITCH_COMPONENT,
250274
} | MOCK_SUBENTRY_AVAILABILITY_DATA
251275
_SENTINEL = object()
252276

tests/components/mqtt/test_config_flow.py

+30
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
MOCK_SENSOR_SUBENTRY_DATA_SINGLE,
4040
MOCK_SENSOR_SUBENTRY_DATA_SINGLE_LAST_RESET_TEMPLATE,
4141
MOCK_SENSOR_SUBENTRY_DATA_SINGLE_STATE_CLASS,
42+
MOCK_SWITCH_SUBENTRY_DATA_SINGLE_STATE_CLASS,
4243
)
4344

4445
from tests.common import MockConfigEntry, MockMqttReasonCode
@@ -2733,12 +2734,41 @@ async def test_migrate_of_incompatible_config_entry(
27332734
(),
27342735
"Test sensor Energy",
27352736
),
2737+
(
2738+
MOCK_SWITCH_SUBENTRY_DATA_SINGLE_STATE_CLASS,
2739+
{"name": "Test switch", "mqtt_settings": {"qos": 0}},
2740+
{"name": "Outlet"},
2741+
{"device_class": "outlet"},
2742+
(),
2743+
{
2744+
"command_topic": "test-topic",
2745+
"command_template": "{{ value }}",
2746+
"state_topic": "test-topic",
2747+
"value_template": "{{ value_json.value }}",
2748+
"optimistic": True,
2749+
},
2750+
(
2751+
(
2752+
{"command_topic": "test-topic#invalid"},
2753+
{"command_topic": "invalid_publish_topic"},
2754+
),
2755+
(
2756+
{
2757+
"command_topic": "test-topic",
2758+
"state_topic": "test-topic#invalid",
2759+
},
2760+
{"state_topic": "invalid_subscribe_topic"},
2761+
),
2762+
),
2763+
"Test switch Outlet",
2764+
),
27362765
],
27372766
ids=[
27382767
"notify_with_entity_name",
27392768
"notify_no_entity_name",
27402769
"sensor_options",
27412770
"sensor_total",
2771+
"switch",
27422772
],
27432773
)
27442774
async def test_subentry_configflow(

0 commit comments

Comments
 (0)