Add button platform to LED Infrared integration (#176456)

This commit is contained in:
Manu
2026-07-27 15:31:26 +02:00
committed by GitHub
parent 7f8ac67264
commit 6aea1cec23
6 changed files with 308 additions and 1 deletions
@@ -4,7 +4,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
PLATFORMS: list[Platform] = [Platform.EVENT, Platform.LIGHT]
PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.EVENT, Platform.LIGHT]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@@ -0,0 +1,61 @@
"""Button platform for LED Infrared integration."""
from typing import override
from homeassistant.components.button import ButtonEntity
from homeassistant.components.infrared import InfraredEmitterConsumerEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import CONF_DEVICE_TYPE, CONF_INFRARED_ENTITY_ID, LEDIrDeviceType
from .entity import LEDIrBaseEntity
PARALLEL_UPDATES = 1
SUPPORTED_BUTTONS = {
LEDIrDeviceType.GENERIC_24_KEY: ["brightness_up", "brightness_down"],
LEDIrDeviceType.GENERIC_13_KEY: ["brightness_up", "brightness_down", "timer"],
}
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up platform from config entry."""
if not (infrared_entity_id := entry.data.get(CONF_INFRARED_ENTITY_ID)):
return
async_add_entities(
[
LEDIrButtonEntity(
entry, entry.data[CONF_DEVICE_TYPE], infrared_entity_id, key
)
for key in SUPPORTED_BUTTONS.get(entry.data[CONF_DEVICE_TYPE], [])
]
)
class LEDIrButtonEntity(LEDIrBaseEntity, InfraredEmitterConsumerEntity, ButtonEntity):
"""Represents a LED Infrared button entity."""
def __init__(
self,
entry: ConfigEntry,
device_type: LEDIrDeviceType,
infrared_entity_id: str,
key: str,
) -> None:
"""Initialize the entity."""
super().__init__(entry, device_type)
self._infrared_emitter_entity_id = infrared_entity_id
self._attr_unique_id = f"{entry.entry_id}_{key}"
self._key = key
self._attr_translation_key = key
@override
async def async_press(self) -> None:
"""Press the button."""
await self._send_command(self._codes[self._key.upper()].to_command())
@@ -1,5 +1,16 @@
{
"entity": {
"button": {
"brightness_down": {
"default": "mdi:brightness-5"
},
"brightness_up": {
"default": "mdi:brightness-7"
},
"timer": {
"default": "mdi:timer"
}
},
"event": {
"received_command": {
"default": "mdi:remote"
@@ -37,6 +37,17 @@
}
},
"entity": {
"button": {
"brightness_down": {
"name": "Brightness down"
},
"brightness_up": {
"name": "Brightness up"
},
"timer": {
"name": "Timer"
}
},
"event": {
"received_command": {
"name": "Received command",
@@ -0,0 +1,101 @@
# serializer version: 1
# name: test_setup[button.led_infrared_via_test_ir_emitter_brightness_down-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': None,
'entity_id': 'button.led_infrared_via_test_ir_emitter_brightness_down',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Brightness down',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Brightness down',
'platform': 'led_infrared',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'brightness_down',
'unique_id': '1234567890_brightness_down',
'unit_of_measurement': None,
})
# ---
# name: test_setup[button.led_infrared_via_test_ir_emitter_brightness_down-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'LED Infrared via Test IR emitter Brightness down',
}),
'context': <ANY>,
'entity_id': 'button.led_infrared_via_test_ir_emitter_brightness_down',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_setup[button.led_infrared_via_test_ir_emitter_brightness_up-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': None,
'entity_id': 'button.led_infrared_via_test_ir_emitter_brightness_up',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Brightness up',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Brightness up',
'platform': 'led_infrared',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'brightness_up',
'unique_id': '1234567890_brightness_up',
'unit_of_measurement': None,
})
# ---
# name: test_setup[button.led_infrared_via_test_ir_emitter_brightness_up-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'LED Infrared via Test IR emitter Brightness up',
}),
'context': <ANY>,
'entity_id': 'button.led_infrared_via_test_ir_emitter_brightness_up',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
@@ -0,0 +1,123 @@
"""Tests for the LED Infrared button platform."""
from collections.abc import Generator
from unittest.mock import patch
from infrared_protocols.codes.generic.led import Generic13KeyCode, Generic24KeyCode
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.components.led_infrared.const import (
CONF_DEVICE_TYPE,
CONF_INFRARED_ENTITY_ID,
DOMAIN,
LEDIrDeviceType,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, snapshot_platform
from tests.components.infrared import EMITTER_ENTITY_ID
from tests.components.infrared.common import MockInfraredEmitterEntity
@pytest.fixture(autouse=True)
def button_only() -> Generator[None]:
"""Enable only the button platform."""
with patch(
"homeassistant.components.led_infrared.PLATFORMS",
[Platform.BUTTON],
):
yield
@pytest.mark.usefixtures("mock_infrared_emitter_entity")
async def test_setup(
hass: HomeAssistant,
config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
) -> None:
"""Snapshot test states of button platform."""
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert config_entry.state is ConfigEntryState.LOADED
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
@pytest.mark.parametrize(
("device_type", "key", "expected_codes"),
[
(
LEDIrDeviceType.GENERIC_24_KEY,
"brightness_up",
[Generic24KeyCode.BRIGHTNESS_UP],
),
(
LEDIrDeviceType.GENERIC_24_KEY,
"brightness_down",
[Generic24KeyCode.BRIGHTNESS_DOWN],
),
(
LEDIrDeviceType.GENERIC_13_KEY,
"brightness_up",
[Generic13KeyCode.BRIGHTNESS_UP],
),
(
LEDIrDeviceType.GENERIC_13_KEY,
"brightness_down",
[Generic13KeyCode.BRIGHTNESS_DOWN],
),
(
LEDIrDeviceType.GENERIC_13_KEY,
"timer",
[Generic13KeyCode.TIMER],
),
],
)
@pytest.mark.usefixtures("infrared_codes")
async def test_button_press(
hass: HomeAssistant,
mock_infrared_emitter_entity: MockInfraredEmitterEntity,
entity_registry: er.EntityRegistry,
device_type: LEDIrDeviceType,
key: str,
expected_codes: list[Generic24KeyCode | Generic13KeyCode],
) -> None:
"""Test button press action."""
config_entry = MockConfigEntry(
domain=DOMAIN,
title="LED Infrared",
entry_id="1234567890",
data={
CONF_DEVICE_TYPE: device_type,
CONF_INFRARED_ENTITY_ID: EMITTER_ENTITY_ID,
},
)
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert config_entry.state is ConfigEntryState.LOADED
entity_id = entity_registry.async_get_entity_id(
BUTTON_DOMAIN, DOMAIN, f"{config_entry.entry_id}_{key}"
)
assert entity_id is not None
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)
assert len(mock_infrared_emitter_entity.send_command_calls) == len(expected_codes)
assert mock_infrared_emitter_entity.send_command_calls == expected_codes