Add MQTT button as entity platform on MQTT subentries (#144204)

This commit is contained in:
Jan Bouwhuis
2025-05-06 13:57:27 +02:00
committed by GitHub
parent deaaf2f082
commit d0ed8b67c4
6 changed files with 100 additions and 3 deletions

View File

@ -14,7 +14,13 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import ConfigType
from .config import DEFAULT_RETAIN, MQTT_BASE_SCHEMA
from .const import CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, CONF_RETAIN
from .const import (
CONF_COMMAND_TEMPLATE,
CONF_COMMAND_TOPIC,
CONF_PAYLOAD_PRESS,
CONF_RETAIN,
DEFAULT_PAYLOAD_PRESS,
)
from .entity import MqttEntity, async_setup_entity_entry_helper
from .models import MqttCommandTemplate
from .schemas import MQTT_ENTITY_COMMON_SCHEMA
@ -22,9 +28,7 @@ from .util import valid_publish_topic
PARALLEL_UPDATES = 0
CONF_PAYLOAD_PRESS = "payload_press"
DEFAULT_NAME = "MQTT Button"
DEFAULT_PAYLOAD_PRESS = "PRESS"
PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend(
{

View File

@ -26,6 +26,7 @@ from cryptography.x509 import load_der_x509_certificate, load_pem_x509_certifica
import voluptuous as vol
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.button import ButtonDeviceClass
from homeassistant.components.file_upload import process_uploaded_file
from homeassistant.components.hassio import AddonError, AddonManager, AddonState
from homeassistant.components.light import (
@ -163,6 +164,7 @@ from .const import (
CONF_OPTIONS,
CONF_PAYLOAD_AVAILABLE,
CONF_PAYLOAD_NOT_AVAILABLE,
CONF_PAYLOAD_PRESS,
CONF_QOS,
CONF_RED_TEMPLATE,
CONF_RETAIN,
@ -206,6 +208,7 @@ from .const import (
DEFAULT_PAYLOAD_NOT_AVAILABLE,
DEFAULT_PAYLOAD_OFF,
DEFAULT_PAYLOAD_ON,
DEFAULT_PAYLOAD_PRESS,
DEFAULT_PORT,
DEFAULT_PREFIX,
DEFAULT_PROTOCOL,
@ -309,6 +312,7 @@ KEY_UPLOAD_SELECTOR = FileSelector(
# Subentry selectors
SUBENTRY_PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.LIGHT,
Platform.NOTIFY,
Platform.SENSOR,
@ -353,6 +357,14 @@ BINARY_SENSOR_DEVICE_CLASS_SELECTOR = SelectSelector(
sort=True,
)
)
BUTTON_DEVICE_CLASS_SELECTOR = SelectSelector(
SelectSelectorConfig(
options=[device_class.value for device_class in ButtonDeviceClass],
mode=SelectSelectorMode.DROPDOWN,
translation_key="device_class_button",
sort=True,
)
)
SENSOR_STATE_CLASS_SELECTOR = SelectSelector(
SelectSelectorConfig(
options=[device_class.value for device_class in SensorStateClass],
@ -546,6 +558,13 @@ PLATFORM_ENTITY_FIELDS = {
validator=str,
),
},
Platform.BUTTON.value: {
CONF_DEVICE_CLASS: PlatformField(
selector=BUTTON_DEVICE_CLASS_SELECTOR,
required=False,
validator=str,
),
},
Platform.NOTIFY.value: {},
Platform.SENSOR.value: {
CONF_DEVICE_CLASS: PlatformField(
@ -634,6 +653,29 @@ PLATFORM_MQTT_FIELDS = {
section="advanced_settings",
),
},
Platform.BUTTON.value: {
CONF_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=True,
validator=valid_publish_topic,
error="invalid_publish_topic",
),
CONF_COMMAND_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
),
CONF_PAYLOAD_PRESS: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=str,
default=DEFAULT_PAYLOAD_PRESS,
),
CONF_RETAIN: PlatformField(
selector=BOOLEAN_SELECTOR, required=False, validator=bool
),
},
Platform.NOTIFY.value: {
CONF_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
@ -1206,6 +1248,7 @@ ENTITY_CONFIG_VALIDATOR: dict[
Callable[[dict[str, Any]], dict[str, str]] | None,
] = {
Platform.BINARY_SENSOR.value: None,
Platform.BUTTON.value: None,
Platform.LIGHT.value: validate_light_platform_config,
Platform.NOTIFY.value: None,
Platform.SENSOR.value: validate_sensor_platform_config,

View File

@ -109,6 +109,7 @@ CONF_OFF_DELAY = "off_delay"
CONF_ON_COMMAND_TYPE = "on_command_type"
CONF_PAYLOAD_CLOSE = "payload_close"
CONF_PAYLOAD_OPEN = "payload_open"
CONF_PAYLOAD_PRESS = "payload_press"
CONF_PAYLOAD_STOP = "payload_stop"
CONF_POSITION_CLOSED = "position_closed"
CONF_POSITION_OPEN = "position_open"
@ -188,6 +189,7 @@ DEFAULT_PAYLOAD_NOT_AVAILABLE = "offline"
DEFAULT_PAYLOAD_OFF = "OFF"
DEFAULT_PAYLOAD_ON = "ON"
DEFAULT_PAYLOAD_OPEN = "OPEN"
DEFAULT_PAYLOAD_PRESS = "PRESS"
DEFAULT_PORT = 1883
DEFAULT_RETAIN = False
DEFAULT_WS_HEADERS: dict[str, str] = {}

View File

@ -258,6 +258,7 @@
"optimistic": "Optimistic",
"payload_off": "Payload \"off\"",
"payload_on": "Payload \"on\"",
"payload_press": "Payload \"press\"",
"qos": "QoS",
"red_template": "Red template",
"retain": "Retain",
@ -282,6 +283,7 @@
"optimistic": "Flag that defines if the {platform} entity works in optimistic mode. [Learn more.]({url}#optimistic)",
"payload_off": "The payload that represents the \"off\" state.",
"payload_on": "The payload that represents the \"on\" state.",
"payload_press": "The payload to send when the button is triggered.",
"qos": "The QoS value a {platform} entity should use.",
"red_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract red color from the state payload value. Expected result of the template is an integer from 0-255 range.",
"retain": "Select if values published by the {platform} entity should be retained at the MQTT broker.",
@ -634,6 +636,13 @@
"window": "[%key:component::binary_sensor::entity_component::window::name%]"
}
},
"device_class_button": {
"options": {
"identify": "[%key:component::button::entity_component::identify::name%]",
"restart": "[%key:common::action::restart%]",
"update": "[%key:component::button::entity_component::update::name%]"
}
},
"device_class_sensor": {
"options": {
"apparent_power": "[%key:component::sensor::entity_component::apparent_power::name%]",
@ -717,6 +726,7 @@
"platform": {
"options": {
"binary_sensor": "[%key:component::binary_sensor::title%]",
"button": "[%key:component::button::title%]",
"light": "[%key:component::light::title%]",
"notify": "[%key:component::notify::title%]",
"sensor": "[%key:component::sensor::title%]",

View File

@ -80,6 +80,18 @@ MOCK_SUBENTRY_BINARY_SENSOR_COMPONENT = {
"entity_picture": "https://example.com/5b06357ef8654e8d9c54cee5bb0e939b",
},
}
MOCK_SUBENTRY_BUTTON_COMPONENT = {
"365d05e6607c4dfb8ae915cff71a954b": {
"platform": "button",
"name": "Restart",
"device_class": "restart",
"command_topic": "test-topic",
"payload_press": "PRESS",
"command_template": "{{ value }}",
"retain": False,
"entity_picture": "https://example.com/365d05e6607c4dfb8ae915cff71a954b",
},
}
MOCK_SUBENTRY_NOTIFY_COMPONENT1 = {
"363a7ecad6be4a19b939a016ea93e994": {
"platform": "notify",
@ -205,6 +217,10 @@ MOCK_BINARY_SENSOR_SUBENTRY_DATA_SINGLE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 2}},
"components": MOCK_SUBENTRY_BINARY_SENSOR_COMPONENT,
}
MOCK_BUTTON_SUBENTRY_DATA_SINGLE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 2}},
"components": MOCK_SUBENTRY_BUTTON_COMPONENT,
}
MOCK_NOTIFY_SUBENTRY_DATA_SINGLE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 1}},
"components": MOCK_SUBENTRY_NOTIFY_COMPONENT1,

View File

@ -34,6 +34,7 @@ from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .common import (
MOCK_BINARY_SENSOR_SUBENTRY_DATA_SINGLE,
MOCK_BUTTON_SUBENTRY_DATA_SINGLE,
MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE,
MOCK_NOTIFY_SUBENTRY_DATA_MULTI,
MOCK_NOTIFY_SUBENTRY_DATA_NO_NAME,
@ -2677,6 +2678,26 @@ async def test_migrate_of_incompatible_config_entry(
),
"Milk notifier Hatch",
),
(
MOCK_BUTTON_SUBENTRY_DATA_SINGLE,
{"name": "Milk notifier", "mqtt_settings": {"qos": 2}},
{"name": "Restart"},
{"device_class": "restart"},
(),
{
"command_topic": "test-topic",
"command_template": "{{ value }}",
"payload_press": "PRESS",
"retain": False,
},
(
(
{"command_topic": "test-topic#invalid"},
{"command_topic": "invalid_publish_topic"},
),
),
"Milk notifier Restart",
),
(
MOCK_NOTIFY_SUBENTRY_DATA_SINGLE,
{"name": "Milk notifier", "mqtt_settings": {"qos": 1}},
@ -2853,6 +2874,7 @@ async def test_migrate_of_incompatible_config_entry(
],
ids=[
"binary_sensor",
"button",
"notify_with_entity_name",
"notify_no_entity_name",
"sensor_options",