Skip to content

Commit d1c7425

Browse files
committed
Add switch as entity platform on MQTT subentries
1 parent a318b3b commit d1c7425

File tree

4 files changed

+99
-4
lines changed

4 files changed

+99
-4
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,
@@ -233,7 +235,7 @@
233235
)
234236

235237
# Subentry selectors
236-
SUBENTRY_PLATFORMS = [Platform.NOTIFY, Platform.SENSOR]
238+
SUBENTRY_PLATFORMS = [Platform.NOTIFY, Platform.SENSOR, Platform.SWITCH]
237239
SUBENTRY_PLATFORM_SELECTOR = SelectSelector(
238240
SelectSelectorConfig(
239241
options=[platform.value for platform in SUBENTRY_PLATFORMS],
@@ -286,6 +288,15 @@
286288
NumberSelectorConfig(mode=NumberSelectorMode.BOX, min=0)
287289
)
288290

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

290301
@dataclass(frozen=True)
291302
class PlatformField:
@@ -352,6 +363,9 @@ def unit_of_measurement_selector(user_data: dict[str, Any | None]) -> Selector:
352363
conditions=({"device_class": "enum"},),
353364
),
354365
},
366+
Platform.SWITCH.value: {
367+
CONF_DEVICE_CLASS: PlatformField(SWITCH_DEVICE_CLASS_SELECTOR, False, str),
368+
},
355369
}
356370
PLATFORM_MQTT_FIELDS = {
357371
Platform.NOTIFY.value: {
@@ -381,13 +395,30 @@ def unit_of_measurement_selector(user_data: dict[str, Any | None]) -> Selector:
381395
EXIRE_AFTER_SELECTOR, False, cv.positive_int, section="advanced_settings"
382396
),
383397
},
398+
Platform.SWITCH.value: {
399+
CONF_COMMAND_TOPIC: PlatformField(
400+
TEXT_SELECTOR, True, valid_publish_topic, "invalid_publish_topic"
401+
),
402+
CONF_COMMAND_TEMPLATE: PlatformField(
403+
TEMPLATE_SELECTOR, False, cv.template, "invalid_template"
404+
),
405+
CONF_STATE_TOPIC: PlatformField(
406+
TEXT_SELECTOR, False, valid_subscribe_topic, "invalid_subscribe_topic"
407+
),
408+
CONF_VALUE_TEMPLATE: PlatformField(
409+
TEMPLATE_SELECTOR, False, cv.template, "invalid_template"
410+
),
411+
CONF_RETAIN: PlatformField(BOOLEAN_SELECTOR, False, bool),
412+
CONF_OPTIMISTIC: PlatformField(BOOLEAN_SELECTOR, False, bool),
413+
},
384414
}
385415
ENTITY_CONFIG_VALIDATOR: dict[
386416
str,
387417
Callable[[dict[str, Any], dict[str, str], list[str] | None], dict[str, Any]] | None,
388418
] = {
389419
Platform.NOTIFY.value: None,
390420
Platform.SENSOR.value: validate_sensor_state_and_device_class_config,
421+
Platform.SWITCH.value: None,
391422
}
392423

393424
MQTT_DEVICE_SCHEMA = vol.Schema(

homeassistant/components/mqtt/strings.json

+10-1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@
235235
"value_template": "Value template",
236236
"last_reset_value_template": "Last reset value template",
237237
"force_update": "Force update",
238+
"optimistic": "Optimistic",
238239
"retain": "Retain",
239240
"qos": "QoS"
240241
},
@@ -245,6 +246,7 @@
245246
"value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the {platform} entity value.",
246247
"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)",
247248
"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)",
249+
"optimistic": "Flag that defines if the {platform} entity works in optimistic mode. [Learn more..]({url}#optimistic)",
248250
"retain": "Select if values published by the {platform} entity should be retained at the MQTT broker.",
249251
"qos": "The QoS value {platform} entity should use."
250252
},
@@ -452,10 +454,17 @@
452454
"wind_speed": "[%key:component::sensor::entity_component::wind_speed::name%]"
453455
}
454456
},
457+
"device_class_switch": {
458+
"options": {
459+
"outlet": "[%key:component::switch::entity_component::outlet::name%]",
460+
"switch": "[%key:component::switch::title%]"
461+
}
462+
},
455463
"platform": {
456464
"options": {
457465
"notify": "Notify",
458-
"sensor": "Sensor"
466+
"sensor": "Sensor",
467+
"switch": "Switch"
459468
}
460469
},
461470
"set_ca_cert": {

tests/components/mqtt/common.py

+27-2
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,20 @@
131131
"qos": 0,
132132
},
133133
}
134+
MOCK_SUBENTRY_SWITCH_COMPONENT = {
135+
"3faf1318016c46c5aea26707eeb6f12e": {
136+
"platform": "switch",
137+
"name": "Outlet",
138+
"device_class": "outlet",
139+
"qos": 0,
140+
"command_topic": "test-topic",
141+
"state_topic": "test-topic",
142+
"command_template": "{{ value }}",
143+
"value_template": "{{ value_json.value }}",
144+
"entity_picture": "https://example.com/3faf1318016c46c5aea26707eeb6f12e",
145+
"optimistic": True,
146+
},
147+
}
134148

135149
# Bogus light component just for code coverage
136150
# Note that light cannot be setup through the UI yet
@@ -230,7 +244,17 @@
230244
},
231245
"components": MOCK_SUBENTRY_SENSOR_COMPONENT_LAST_RESET,
232246
}
233-
247+
MOCK_SWITCH_SUBENTRY_DATA_SINGLE_STATE_CLASS = {
248+
"device": {
249+
"name": "Test switch",
250+
"sw_version": "1.0",
251+
"hw_version": "2.1 rev a",
252+
"model": "Model XL",
253+
"model_id": "mn002",
254+
"configuration_url": "https://example.com",
255+
},
256+
"components": MOCK_SUBENTRY_SWITCH_COMPONENT,
257+
}
234258
MOCK_SUBENTRY_DATA_BAD_COMPONENT_SCHEMA = {
235259
"device": {
236260
"name": "Milk notifier",
@@ -253,7 +277,8 @@
253277
},
254278
"components": MOCK_SUBENTRY_NOTIFY_COMPONENT1
255279
| MOCK_SUBENTRY_NOTIFY_COMPONENT2
256-
| MOCK_SUBENTRY_LIGHT_COMPONENT,
280+
| MOCK_SUBENTRY_LIGHT_COMPONENT
281+
| MOCK_SUBENTRY_SWITCH_COMPONENT,
257282
} | MOCK_SUBENTRY_AVAILABILITY_DATA
258283
_SENTINEL = object()
259284

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
@@ -2731,12 +2732,41 @@ async def test_migrate_of_incompatible_config_entry(
27312732
(),
27322733
"Test sensor Energy",
27332734
),
2735+
(
2736+
MOCK_SWITCH_SUBENTRY_DATA_SINGLE_STATE_CLASS,
2737+
{"name": "Outlet"},
2738+
{"device_class": "outlet"},
2739+
(),
2740+
{
2741+
"command_topic": "test-topic",
2742+
"command_template": "{{ value }}",
2743+
"state_topic": "test-topic",
2744+
"value_template": "{{ value_json.value }}",
2745+
"qos": 0,
2746+
"optimistic": True,
2747+
},
2748+
(
2749+
(
2750+
{"command_topic": "test-topic#invalid"},
2751+
{"command_topic": "invalid_publish_topic"},
2752+
),
2753+
(
2754+
{
2755+
"command_topic": "test-topic",
2756+
"state_topic": "test-topic#invalid",
2757+
},
2758+
{"state_topic": "invalid_subscribe_topic"},
2759+
),
2760+
),
2761+
"Test switch Outlet",
2762+
),
27342763
],
27352764
ids=[
27362765
"notify_with_entity_name",
27372766
"notify_no_entity_name",
27382767
"sensor_options",
27392768
"sensor_total",
2769+
"switch",
27402770
],
27412771
)
27422772
async def test_subentry_configflow(

0 commit comments

Comments
 (0)