mirror of
https://github.com/home-assistant/core.git
synced 2026-05-15 01:51:45 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 361e62d33c |
@@ -24,6 +24,8 @@ PARALLEL_UPDATES = 0
|
||||
class EsphomeInfraredEntity(EsphomeEntity[InfraredInfo, EntityState], InfraredEntity):
|
||||
"""ESPHome infrared entity using native API."""
|
||||
|
||||
_attr_supported_commands = frozenset({InfraredCommand})
|
||||
|
||||
@callback
|
||||
def _on_device_update(self) -> None:
|
||||
"""Call when device updates or entry data changes."""
|
||||
|
||||
@@ -25,6 +25,7 @@ from .const import DOMAIN
|
||||
|
||||
__all__ = [
|
||||
"DOMAIN",
|
||||
"InfraredCommand",
|
||||
"InfraredEntity",
|
||||
"InfraredEntityDescription",
|
||||
"async_get_emitters",
|
||||
@@ -79,7 +80,8 @@ async def async_send_command(
|
||||
"""Send an IR command to the specified infrared entity.
|
||||
|
||||
Raises:
|
||||
HomeAssistantError: If the infrared entity is not found.
|
||||
HomeAssistantError: If the infrared entity is not found, or if the entity
|
||||
does not support the given command type.
|
||||
"""
|
||||
component = hass.data.get(DATA_COMPONENT)
|
||||
if component is None:
|
||||
@@ -98,6 +100,16 @@ async def async_send_command(
|
||||
translation_placeholders={"entity_id": entity_id},
|
||||
)
|
||||
|
||||
if not isinstance(command, tuple(entity.supported_commands)):
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="command_not_supported",
|
||||
translation_placeholders={
|
||||
"entity_id": entity_id,
|
||||
"command_type": type(command).__name__,
|
||||
},
|
||||
)
|
||||
|
||||
if context is not None:
|
||||
entity.async_set_context(context)
|
||||
|
||||
@@ -114,9 +126,15 @@ class InfraredEntity(RestoreEntity):
|
||||
entity_description: InfraredEntityDescription
|
||||
_attr_should_poll = False
|
||||
_attr_state: None = None
|
||||
_attr_supported_commands: frozenset[type[InfraredCommand]] = frozenset()
|
||||
|
||||
__last_command_sent: str | None = None
|
||||
|
||||
@property
|
||||
def supported_commands(self) -> frozenset[type[InfraredCommand]]:
|
||||
"""Return the command types this entity can transmit."""
|
||||
return self._attr_supported_commands
|
||||
|
||||
@property
|
||||
@final
|
||||
def state(self) -> str | None:
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
},
|
||||
"entity_not_found": {
|
||||
"message": "Infrared entity `{entity_id}` not found"
|
||||
},
|
||||
"command_not_supported": {
|
||||
"message": "Infrared entity `{entity_id}` does not support commands of type `{command_type}`"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ class DemoInfrared(InfraredEntity):
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_should_poll = False
|
||||
_attr_supported_commands = frozenset({infrared_protocols.Command})
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -33,6 +33,7 @@ class SmInfraredEntity(SmEntity, InfraredEntity):
|
||||
"""Representation of a SLZB-Ultima infrared."""
|
||||
|
||||
_attr_translation_key = "infrared_emitter"
|
||||
_attr_supported_commands = frozenset({InfraredCommand})
|
||||
|
||||
def __init__(self, coordinator: SmDataUpdateCoordinator) -> None:
|
||||
"""Initialize the SLZB-Ultima infrared."""
|
||||
|
||||
@@ -21,6 +21,7 @@ class MockInfraredEntity(InfraredEntity):
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = "Test IR transmitter"
|
||||
_attr_supported_commands = frozenset({InfraredCommand})
|
||||
|
||||
def __init__(self, unique_id: str) -> None:
|
||||
"""Initialize mock entity."""
|
||||
|
||||
@@ -125,6 +125,30 @@ async def test_async_send_command_component_not_loaded(hass: HomeAssistant) -> N
|
||||
await async_send_command(hass, "infrared.some_entity", command)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
async def test_async_send_command_unsupported_command(
|
||||
hass: HomeAssistant,
|
||||
mock_infrared_entity: MockInfraredEntity,
|
||||
) -> None:
|
||||
"""Test async_send_command raises error when command is not supported."""
|
||||
mock_infrared_entity._attr_supported_commands = frozenset()
|
||||
component = hass.data[DATA_COMPONENT]
|
||||
await component.async_add_entities([mock_infrared_entity])
|
||||
|
||||
command = NECCommand(address=0x04FB, command=0x08F7, modulation=38000)
|
||||
|
||||
with pytest.raises(
|
||||
HomeAssistantError,
|
||||
match=(
|
||||
"Infrared entity `infrared.test_ir_transmitter` does not support "
|
||||
"commands of type `NECCommand`"
|
||||
),
|
||||
):
|
||||
await async_send_command(hass, mock_infrared_entity.entity_id, command)
|
||||
|
||||
assert len(mock_infrared_entity.send_command_calls) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("restored_value", "expected_state"),
|
||||
[
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import patch
|
||||
|
||||
from infrared_protocols import Command as InfraredCommand
|
||||
@@ -34,6 +33,7 @@ class MockInfraredEntity(InfraredEntity):
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = "Test IR transmitter"
|
||||
_attr_supported_commands = frozenset({InfraredCommand})
|
||||
|
||||
def __init__(self, unique_id: str) -> None:
|
||||
"""Initialize mock entity."""
|
||||
@@ -72,26 +72,11 @@ def platforms() -> list[Platform]:
|
||||
return PLATFORMS
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_make_lg_tv_command() -> Generator[None]:
|
||||
"""Patch make_command to return the LGTVCode directly.
|
||||
|
||||
This allows tests to assert on the high-level code enum value
|
||||
rather than the raw NEC timings.
|
||||
"""
|
||||
with patch(
|
||||
"homeassistant.components.lg_infrared.entity.make_lg_tv_command",
|
||||
side_effect=lambda code, **kwargs: code,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def init_integration(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_infrared_entity: MockInfraredEntity,
|
||||
mock_make_lg_tv_command: None,
|
||||
platforms: list[Platform],
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the LG Infrared integration for testing."""
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from infrared_protocols import NECCommand
|
||||
from infrared_protocols.codes.lg.tv import LGTVCode
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
@@ -95,7 +96,9 @@ async def test_button_press_sends_correct_code(
|
||||
)
|
||||
|
||||
assert len(mock_infrared_entity.send_command_calls) == 1
|
||||
assert mock_infrared_entity.send_command_calls[0] == expected_code
|
||||
sent = mock_infrared_entity.send_command_calls[0]
|
||||
assert isinstance(sent, NECCommand)
|
||||
assert sent.command == expected_code
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from infrared_protocols import NECCommand
|
||||
from infrared_protocols.codes.lg.tv import LGTVCode
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
@@ -92,7 +93,9 @@ async def test_media_player_action_sends_correct_code(
|
||||
)
|
||||
|
||||
assert len(mock_infrared_entity.send_command_calls) == 1
|
||||
assert mock_infrared_entity.send_command_calls[0] == expected_code
|
||||
sent = mock_infrared_entity.send_command_calls[0]
|
||||
assert isinstance(sent, NECCommand)
|
||||
assert sent.command == expected_code
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
|
||||
Reference in New Issue
Block a user