Add MQTT binary_sensor as entity platform on MQTT subentries (#144142)

Add MQTT binary_sensor subentry support
This commit is contained in:
Jan Bouwhuis 2025-05-06 10:23:19 +02:00 committed by GitHub
parent 60846434d3
commit 46df29b390
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 141 additions and 5 deletions

View File

@ -35,7 +35,7 @@ from homeassistant.util import dt as dt_util
from . import subscription
from .config import MQTT_RO_SCHEMA
from .const import CONF_STATE_TOPIC, PAYLOAD_NONE
from .const import CONF_OFF_DELAY, CONF_STATE_TOPIC, PAYLOAD_NONE
from .entity import MqttAvailabilityMixin, MqttEntity, async_setup_entity_entry_helper
from .models import MqttValueTemplate, ReceiveMessage
from .schemas import MQTT_ENTITY_COMMON_SCHEMA
@ -45,7 +45,6 @@ _LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
DEFAULT_NAME = "MQTT Binary sensor"
CONF_OFF_DELAY = "off_delay"
DEFAULT_PAYLOAD_OFF = "OFF"
DEFAULT_PAYLOAD_ON = "ON"
DEFAULT_FORCE_UPDATE = False

View File

@ -25,6 +25,7 @@ from cryptography.hazmat.primitives.serialization import (
from cryptography.x509 import load_der_x509_certificate, load_pem_x509_certificate
import voluptuous as vol
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.file_upload import process_uploaded_file
from homeassistant.components.hassio import AddonError, AddonManager, AddonState
from homeassistant.components.light import (
@ -157,6 +158,7 @@ from .const import (
CONF_LAST_RESET_VALUE_TEMPLATE,
CONF_MAX_KELVIN,
CONF_MIN_KELVIN,
CONF_OFF_DELAY,
CONF_ON_COMMAND_TYPE,
CONF_OPTIONS,
CONF_PAYLOAD_AVAILABLE,
@ -305,7 +307,13 @@ KEY_UPLOAD_SELECTOR = FileSelector(
)
# Subentry selectors
SUBENTRY_PLATFORMS = [Platform.LIGHT, Platform.NOTIFY, Platform.SENSOR, Platform.SWITCH]
SUBENTRY_PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.LIGHT,
Platform.NOTIFY,
Platform.SENSOR,
Platform.SWITCH,
]
SUBENTRY_PLATFORM_SELECTOR = SelectSelector(
SelectSelectorConfig(
options=[platform.value for platform in SUBENTRY_PLATFORMS],
@ -337,6 +345,14 @@ SENSOR_DEVICE_CLASS_SELECTOR = SelectSelector(
sort=True,
)
)
BINARY_SENSOR_DEVICE_CLASS_SELECTOR = SelectSelector(
SelectSelectorConfig(
options=[device_class.value for device_class in BinarySensorDeviceClass],
mode=SelectSelectorMode.DROPDOWN,
translation_key="device_class_binary_sensor",
sort=True,
)
)
SENSOR_STATE_CLASS_SELECTOR = SelectSelector(
SelectSelectorConfig(
options=[device_class.value for device_class in SensorStateClass],
@ -354,7 +370,7 @@ OPTIONS_SELECTOR = SelectSelector(
SUGGESTED_DISPLAY_PRECISION_SELECTOR = NumberSelector(
NumberSelectorConfig(mode=NumberSelectorMode.BOX, min=0, max=9)
)
EXPIRE_AFTER_SELECTOR = NumberSelector(
TIMEOUT_SELECTOR = NumberSelector(
NumberSelectorConfig(mode=NumberSelectorMode.BOX, min=0)
)
@ -523,6 +539,13 @@ COMMON_ENTITY_FIELDS = {
}
PLATFORM_ENTITY_FIELDS = {
Platform.BINARY_SENSOR.value: {
CONF_DEVICE_CLASS: PlatformField(
selector=BINARY_SENSOR_DEVICE_CLASS_SELECTOR,
required=False,
validator=str,
),
},
Platform.NOTIFY.value: {},
Platform.SENSOR.value: {
CONF_DEVICE_CLASS: PlatformField(
@ -573,6 +596,44 @@ PLATFORM_ENTITY_FIELDS = {
},
}
PLATFORM_MQTT_FIELDS = {
Platform.BINARY_SENSOR.value: {
CONF_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=True,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
),
CONF_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
),
CONF_PAYLOAD_OFF: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=str,
default=DEFAULT_PAYLOAD_OFF,
),
CONF_PAYLOAD_ON: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=str,
default=DEFAULT_PAYLOAD_ON,
),
CONF_EXPIRE_AFTER: PlatformField(
selector=TIMEOUT_SELECTOR,
required=False,
validator=cv.positive_int,
section="advanced_settings",
),
CONF_OFF_DELAY: PlatformField(
selector=TIMEOUT_SELECTOR,
required=False,
validator=cv.positive_int,
section="advanced_settings",
),
},
Platform.NOTIFY.value: {
CONF_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
@ -611,7 +672,7 @@ PLATFORM_MQTT_FIELDS = {
conditions=({CONF_STATE_CLASS: "total"},),
),
CONF_EXPIRE_AFTER: PlatformField(
selector=EXPIRE_AFTER_SELECTOR,
selector=TIMEOUT_SELECTOR,
required=False,
validator=cv.positive_int,
section="advanced_settings",
@ -1144,6 +1205,7 @@ ENTITY_CONFIG_VALIDATOR: dict[
str,
Callable[[dict[str, Any]], dict[str, str]] | None,
] = {
Platform.BINARY_SENSOR.value: None,
Platform.LIGHT.value: validate_light_platform_config,
Platform.NOTIFY.value: None,
Platform.SENSOR.value: validate_sensor_platform_config,

View File

@ -105,6 +105,7 @@ CONF_MODE_COMMAND_TOPIC = "mode_command_topic"
CONF_MODE_LIST = "modes"
CONF_MODE_STATE_TEMPLATE = "mode_state_template"
CONF_MODE_STATE_TOPIC = "mode_state_topic"
CONF_OFF_DELAY = "off_delay"
CONF_ON_COMMAND_TYPE = "on_command_type"
CONF_PAYLOAD_CLOSE = "payload_close"
CONF_PAYLOAD_OPEN = "payload_open"

View File

@ -300,6 +300,7 @@
"flash_time_short": "Flash time short",
"max_kelvin": "Max Kelvin",
"min_kelvin": "Min Kelvin",
"off_delay": "OFF delay",
"transition": "Transition support"
},
"data_description": {
@ -309,6 +310,7 @@
"flash_time_short": "The duration, in seconds, of a \"short\" flash.",
"max_kelvin": "The maximum color temperature in Kelvin.",
"min_kelvin": "The minimum color temperature in Kelvin.",
"off_delay": "For sensors that only send \"on\" state updates (like PIRs), this variable sets a delay in seconds after which the sensors state will be updated back to \"off\".",
"transition": "Enable the transition feature for this light"
}
},
@ -600,6 +602,38 @@
}
},
"selector": {
"device_class_binary_sensor": {
"options": {
"battery": "[%key:component::binary_sensor::entity_component::battery::name%]",
"battery_charging": "[%key:component::binary_sensor::entity_component::battery_charging::name%]",
"carbon_monoxide": "[%key:component::binary_sensor::entity_component::carbon_monoxide::name%]",
"cold": "[%key:component::binary_sensor::entity_component::cold::name%]",
"connectivity": "[%key:component::binary_sensor::entity_component::connectivity::name%]",
"door": "[%key:component::binary_sensor::entity_component::door::name%]",
"garage_door": "[%key:component::binary_sensor::entity_component::garage_door::name%]",
"gas": "[%key:component::binary_sensor::entity_component::gas::name%]",
"heat": "[%key:component::binary_sensor::entity_component::heat::name%]",
"light": "[%key:component::binary_sensor::entity_component::light::name%]",
"lock": "[%key:component::binary_sensor::entity_component::lock::name%]",
"moisture": "[%key:component::binary_sensor::entity_component::moisture::name%]",
"motion": "[%key:component::binary_sensor::entity_component::motion::name%]",
"moving": "[%key:component::binary_sensor::entity_component::moving::name%]",
"occupancy": "[%key:component::binary_sensor::entity_component::occupancy::name%]",
"opening": "[%key:component::binary_sensor::entity_component::opening::name%]",
"plug": "[%key:component::binary_sensor::entity_component::plug::name%]",
"power": "[%key:component::binary_sensor::entity_component::power::name%]",
"presence": "[%key:component::binary_sensor::entity_component::presence::name%]",
"problem": "[%key:component::binary_sensor::entity_component::problem::name%]",
"running": "[%key:component::binary_sensor::entity_component::running::name%]",
"safety": "[%key:component::binary_sensor::entity_component::safety::name%]",
"smoke": "[%key:component::binary_sensor::entity_component::smoke::name%]",
"sound": "[%key:component::binary_sensor::entity_component::sound::name%]",
"tamper": "[%key:component::binary_sensor::entity_component::tamper::name%]",
"update": "[%key:component::binary_sensor::entity_component::update::name%]",
"vibration": "[%key:component::binary_sensor::entity_component::vibration::name%]",
"window": "[%key:component::binary_sensor::entity_component::window::name%]"
}
},
"device_class_sensor": {
"options": {
"apparent_power": "[%key:component::sensor::entity_component::apparent_power::name%]",
@ -682,6 +716,7 @@
},
"platform": {
"options": {
"binary_sensor": "[%key:component::binary_sensor::title%]",
"light": "[%key:component::light::title%]",
"notify": "[%key:component::notify::title%]",
"sensor": "[%key:component::sensor::title%]",

View File

@ -66,6 +66,20 @@ DEFAULT_CONFIG_DEVICE_INFO_MAC = {
"configuration_url": "http://example.com",
}
MOCK_SUBENTRY_BINARY_SENSOR_COMPONENT = {
"5b06357ef8654e8d9c54cee5bb0e939b": {
"platform": "binary_sensor",
"name": "Hatch",
"device_class": "door",
"state_topic": "test-topic",
"payload_on": "ON",
"payload_off": "OFF",
"expire_after": 1200,
"off_delay": 5,
"value_template": "{{ value_json.value }}",
"entity_picture": "https://example.com/5b06357ef8654e8d9c54cee5bb0e939b",
},
}
MOCK_SUBENTRY_NOTIFY_COMPONENT1 = {
"363a7ecad6be4a19b939a016ea93e994": {
"platform": "notify",
@ -187,6 +201,10 @@ MOCK_NOTIFY_SUBENTRY_DATA_MULTI = {
"components": MOCK_SUBENTRY_NOTIFY_COMPONENT1 | MOCK_SUBENTRY_NOTIFY_COMPONENT2,
} | MOCK_SUBENTRY_AVAILABILITY_DATA
MOCK_BINARY_SENSOR_SUBENTRY_DATA_SINGLE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 2}},
"components": MOCK_SUBENTRY_BINARY_SENSOR_COMPONENT,
}
MOCK_NOTIFY_SUBENTRY_DATA_SINGLE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 1}},
"components": MOCK_SUBENTRY_NOTIFY_COMPONENT1,

View File

@ -33,6 +33,7 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .common import (
MOCK_BINARY_SENSOR_SUBENTRY_DATA_SINGLE,
MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE,
MOCK_NOTIFY_SUBENTRY_DATA_MULTI,
MOCK_NOTIFY_SUBENTRY_DATA_NO_NAME,
@ -2657,6 +2658,25 @@ async def test_migrate_of_incompatible_config_entry(
"entity_name",
),
[
(
MOCK_BINARY_SENSOR_SUBENTRY_DATA_SINGLE,
{"name": "Milk notifier", "mqtt_settings": {"qos": 2}},
{"name": "Hatch"},
{"device_class": "door"},
(),
{
"state_topic": "test-topic",
"value_template": "{{ value_json.value }}",
"advanced_settings": {"expire_after": 1200, "off_delay": 5},
},
(
(
{"state_topic": "test-topic#invalid"},
{"state_topic": "invalid_subscribe_topic"},
),
),
"Milk notifier Hatch",
),
(
MOCK_NOTIFY_SUBENTRY_DATA_SINGLE,
{"name": "Milk notifier", "mqtt_settings": {"qos": 1}},
@ -2832,6 +2852,7 @@ async def test_migrate_of_incompatible_config_entry(
),
],
ids=[
"binary_sensor",
"notify_with_entity_name",
"notify_no_entity_name",
"sensor_options",