From f874e62fa3bf2279fe51eefe28c1b3b01a4b883d Mon Sep 17 00:00:00 2001 From: "Dr.Blank" Date: Tue, 1 Sep 2026 14:54:16 +0530 Subject: [PATCH] Add AC buttons to LG Infrared (#180842) --- .../components/lg_infrared/button.py | 83 ++- .../components/lg_infrared/icons.json | 27 + .../components/lg_infrared/quality_scale.yaml | 5 +- .../components/lg_infrared/strings.json | 27 + .../lg_infrared/snapshots/test_button.ambr | 566 ++++++++++++++++-- tests/components/lg_infrared/test_button.py | 56 +- 6 files changed, 694 insertions(+), 70 deletions(-) diff --git a/homeassistant/components/lg_infrared/button.py b/homeassistant/components/lg_infrared/button.py index 26591353e8ce..7d938d860f25 100644 --- a/homeassistant/components/lg_infrared/button.py +++ b/homeassistant/components/lg_infrared/button.py @@ -3,11 +3,13 @@ from dataclasses import dataclass from typing import override +from infrared_protocols.codes.lg.ac import LGACCode from infrared_protocols.codes.lg.tv import LGTVCode from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.components.infrared import InfraredEmitterConsumerEntity from homeassistant.config_entries import ConfigEntry +from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -21,7 +23,7 @@ PARALLEL_UPDATES = 1 class LgIrButtonEntityDescription(ButtonEntityDescription): """Describes LG IR button entity.""" - command_code: LGTVCode + command_code: LGTVCode | LGACCode TV_BUTTON_DESCRIPTIONS: tuple[LgIrButtonEntityDescription, ...] = ( @@ -114,6 +116,70 @@ TV_BUTTON_DESCRIPTIONS: tuple[LgIrButtonEntityDescription, ...] = ( ), ) +# One-shot AC actions with no discrete on/off code, so each is a momentary button. +AC_BUTTON_DESCRIPTIONS: tuple[LgIrButtonEntityDescription, ...] = ( + LgIrButtonEntityDescription( + key="jet", translation_key="jet", command_code=LGACCode.JET + ), + LgIrButtonEntityDescription( + key="eco", translation_key="eco", command_code=LGACCode.ECO + ), + # A separate boost from JET, only offered on LG India units, so it is + # disabled by default. + LgIrButtonEntityDescription( + key="viraat", + translation_key="viraat", + command_code=LGACCode.VIRAAT, + entity_registry_enabled_default=False, + ), + LgIrButtonEntityDescription( + key="ai_convertible", + translation_key="ai_convertible", + command_code=LGACCode.AI_CONVERTIBLE, + ), + LgIrButtonEntityDescription( + key="light", + translation_key="light", + command_code=LGACCode.LIGHT_TOGGLE, + entity_category=EntityCategory.CONFIG, + ), + LgIrButtonEntityDescription( + key="wifi", + translation_key="wifi", + command_code=LGACCode.WIFI_TOGGLE, + entity_category=EntityCategory.CONFIG, + ), + LgIrButtonEntityDescription( + key="audio", + translation_key="audio", + command_code=LGACCode.AUDIO_TOGGLE, + entity_category=EntityCategory.CONFIG, + ), + LgIrButtonEntityDescription( + key="diagnose", + translation_key="diagnose", + command_code=LGACCode.DIAGNOSE, + entity_category=EntityCategory.DIAGNOSTIC, + ), + # Only flips between auto-swing and off; the climate swing dropdown supersedes it, + # so it is kept for older units but disabled by default. + LgIrButtonEntityDescription( + key="swing_v_toggle", + translation_key="swing_v_toggle", + command_code=LGACCode.SWING_V_TOGGLE, + entity_registry_enabled_default=False, + ), +) + +_DEVICE_BUTTONS: dict[LGDeviceType, tuple[LgIrButtonEntityDescription, ...]] = { + LGDeviceType.TV: TV_BUTTON_DESCRIPTIONS, + LGDeviceType.AC: AC_BUTTON_DESCRIPTIONS, +} +_DEVICE_NAMES: dict[LGDeviceType, str] = { + LGDeviceType.TV: "LG TV", + LGDeviceType.AC: "LG AC", +} + async def async_setup_entry( hass: HomeAssistant, @@ -125,11 +191,11 @@ async def async_setup_entry( return device_type = entry.data[CONF_DEVICE_TYPE] - if device_type == LGDeviceType.TV: - async_add_entities( - LgIrButton(entry, infrared_entity_id, description) - for description in TV_BUTTON_DESCRIPTIONS - ) + device_name = _DEVICE_NAMES[device_type] + async_add_entities( + LgIrButton(entry, infrared_entity_id, description, device_name) + for description in _DEVICE_BUTTONS[device_type] + ) class LgIrButton(LgIrEntity, InfraredEmitterConsumerEntity, ButtonEntity): @@ -142,9 +208,12 @@ class LgIrButton(LgIrEntity, InfraredEmitterConsumerEntity, ButtonEntity): entry: ConfigEntry, infrared_entity_id: str, description: LgIrButtonEntityDescription, + device_name: str, ) -> None: """Initialize LG IR button.""" - super().__init__(entry, unique_id_suffix=description.key) + super().__init__( + entry, unique_id_suffix=description.key, device_name=device_name + ) self._infrared_emitter_entity_id = infrared_entity_id self.entity_description = description diff --git a/homeassistant/components/lg_infrared/icons.json b/homeassistant/components/lg_infrared/icons.json index 5ca08923f9ee..785708909fd2 100644 --- a/homeassistant/components/lg_infrared/icons.json +++ b/homeassistant/components/lg_infrared/icons.json @@ -1,12 +1,24 @@ { "entity": { "button": { + "ai_convertible": { + "default": "mdi:brain" + }, + "audio": { + "default": "mdi:volume-high" + }, "back": { "default": "mdi:keyboard-backspace" }, + "diagnose": { + "default": "mdi:stethoscope" + }, "down": { "default": "mdi:arrow-down" }, + "eco": { + "default": "mdi:leaf" + }, "exit": { "default": "mdi:exit-to-app" }, @@ -34,9 +46,15 @@ "input": { "default": "mdi:import" }, + "jet": { + "default": "mdi:fan-chevron-up" + }, "left": { "default": "mdi:arrow-left" }, + "light": { + "default": "mdi:led-on" + }, "menu": { "default": "mdi:menu" }, @@ -85,8 +103,17 @@ "right": { "default": "mdi:arrow-right" }, + "swing_v_toggle": { + "default": "mdi:arrow-up-down" + }, "up": { "default": "mdi:arrow-up" + }, + "viraat": { + "default": "mdi:rocket-launch" + }, + "wifi": { + "default": "mdi:wifi" } }, "climate": { diff --git a/homeassistant/components/lg_infrared/quality_scale.yaml b/homeassistant/components/lg_infrared/quality_scale.yaml index 90bc25e6bad0..237bc59de7e6 100644 --- a/homeassistant/components/lg_infrared/quality_scale.yaml +++ b/homeassistant/components/lg_infrared/quality_scale.yaml @@ -84,10 +84,7 @@ rules: Each config entry creates a single device. entity-category: done entity-device-class: done - entity-disabled-by-default: - status: exempt - comment: | - No entities should be disabled by default. + entity-disabled-by-default: done entity-translations: done exception-translations: status: exempt diff --git a/homeassistant/components/lg_infrared/strings.json b/homeassistant/components/lg_infrared/strings.json index f9fbc8740ff7..49b6a3c9b822 100644 --- a/homeassistant/components/lg_infrared/strings.json +++ b/homeassistant/components/lg_infrared/strings.json @@ -47,12 +47,24 @@ }, "entity": { "button": { + "ai_convertible": { + "name": "AI mode" + }, + "audio": { + "name": "Toggle beep" + }, "back": { "name": "[%key:common::entity::button::back::name%]" }, + "diagnose": { + "name": "Diagnose" + }, "down": { "name": "[%key:common::entity::button::down::name%]" }, + "eco": { + "name": "Eco mode" + }, "exit": { "name": "[%key:common::entity::button::exit::name%]" }, @@ -80,9 +92,15 @@ "input": { "name": "[%key:common::entity::button::input::name%]" }, + "jet": { + "name": "Jet mode" + }, "left": { "name": "[%key:common::entity::button::left::name%]" }, + "light": { + "name": "Toggle light" + }, "menu": { "name": "[%key:common::entity::button::menu::name%]" }, @@ -131,8 +149,17 @@ "right": { "name": "[%key:common::entity::button::right::name%]" }, + "swing_v_toggle": { + "name": "Toggle vertical swing" + }, "up": { "name": "[%key:common::entity::button::up::name%]" + }, + "viraat": { + "name": "Viraat mode" + }, + "wifi": { + "name": "Start Wi-Fi pairing" } }, "climate": { diff --git a/tests/components/lg_infrared/snapshots/test_button.ambr b/tests/components/lg_infrared/snapshots/test_button.ambr index d62d2c6f0188..5dc15fe65f4f 100644 --- a/tests/components/lg_infrared/snapshots/test_button.ambr +++ b/tests/components/lg_infrared/snapshots/test_button.ambr @@ -1,5 +1,455 @@ # serializer version: 1 -# name: test_entities[button.lg_tv_back-entry] +# name: test_entities[ac][button.lg_ac_ai_mode-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': None, + 'entity_id': 'button.lg_ac_ai_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'AI mode', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'AI mode', + 'platform': 'lg_infrared', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ai_convertible', + 'unique_id': '01JTEST0000000000000000000_ai_convertible', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[ac][button.lg_ac_ai_mode-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'LG AC AI mode', + }), + 'context': , + 'entity_id': 'button.lg_ac_ai_mode', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[ac][button.lg_ac_diagnose-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.lg_ac_diagnose', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Diagnose', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Diagnose', + 'platform': 'lg_infrared', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'diagnose', + 'unique_id': '01JTEST0000000000000000000_diagnose', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[ac][button.lg_ac_diagnose-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'LG AC Diagnose', + }), + 'context': , + 'entity_id': 'button.lg_ac_diagnose', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[ac][button.lg_ac_eco_mode-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': None, + 'entity_id': 'button.lg_ac_eco_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Eco mode', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Eco mode', + 'platform': 'lg_infrared', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'eco', + 'unique_id': '01JTEST0000000000000000000_eco', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[ac][button.lg_ac_eco_mode-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'LG AC Eco mode', + }), + 'context': , + 'entity_id': 'button.lg_ac_eco_mode', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[ac][button.lg_ac_jet_mode-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': None, + 'entity_id': 'button.lg_ac_jet_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Jet mode', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Jet mode', + 'platform': 'lg_infrared', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'jet', + 'unique_id': '01JTEST0000000000000000000_jet', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[ac][button.lg_ac_jet_mode-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'LG AC Jet mode', + }), + 'context': , + 'entity_id': 'button.lg_ac_jet_mode', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[ac][button.lg_ac_start_wi_fi_pairing-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.lg_ac_start_wi_fi_pairing', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Start Wi-Fi pairing', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Start Wi-Fi pairing', + 'platform': 'lg_infrared', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'wifi', + 'unique_id': '01JTEST0000000000000000000_wifi', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[ac][button.lg_ac_start_wi_fi_pairing-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'LG AC Start Wi-Fi pairing', + }), + 'context': , + 'entity_id': 'button.lg_ac_start_wi_fi_pairing', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[ac][button.lg_ac_toggle_beep-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.lg_ac_toggle_beep', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Toggle beep', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Toggle beep', + 'platform': 'lg_infrared', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'audio', + 'unique_id': '01JTEST0000000000000000000_audio', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[ac][button.lg_ac_toggle_beep-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'LG AC Toggle beep', + }), + 'context': , + 'entity_id': 'button.lg_ac_toggle_beep', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[ac][button.lg_ac_toggle_light-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.lg_ac_toggle_light', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Toggle light', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Toggle light', + 'platform': 'lg_infrared', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'light', + 'unique_id': '01JTEST0000000000000000000_light', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[ac][button.lg_ac_toggle_light-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'LG AC Toggle light', + }), + 'context': , + 'entity_id': 'button.lg_ac_toggle_light', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[ac][button.lg_ac_toggle_vertical_swing-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': None, + 'entity_id': 'button.lg_ac_toggle_vertical_swing', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Toggle vertical swing', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Toggle vertical swing', + 'platform': 'lg_infrared', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'swing_v_toggle', + 'unique_id': '01JTEST0000000000000000000_swing_v_toggle', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[ac][button.lg_ac_toggle_vertical_swing-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'LG AC Toggle vertical swing', + }), + 'context': , + 'entity_id': 'button.lg_ac_toggle_vertical_swing', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[ac][button.lg_ac_viraat_mode-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': None, + 'entity_id': 'button.lg_ac_viraat_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Viraat mode', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Viraat mode', + 'platform': 'lg_infrared', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'viraat', + 'unique_id': '01JTEST0000000000000000000_viraat', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[ac][button.lg_ac_viraat_mode-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'LG AC Viraat mode', + }), + 'context': , + 'entity_id': 'button.lg_ac_viraat_mode', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[tv][button.lg_tv_back-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -36,7 +486,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_back-state] +# name: test_entities[tv][button.lg_tv_back-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Back', @@ -49,7 +499,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_down-entry] +# name: test_entities[tv][button.lg_tv_down-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -86,7 +536,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_down-state] +# name: test_entities[tv][button.lg_tv_down-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Down', @@ -99,7 +549,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_exit-entry] +# name: test_entities[tv][button.lg_tv_exit-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -136,7 +586,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_exit-state] +# name: test_entities[tv][button.lg_tv_exit-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Exit', @@ -149,7 +599,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_guide-entry] +# name: test_entities[tv][button.lg_tv_guide-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -186,7 +636,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_guide-state] +# name: test_entities[tv][button.lg_tv_guide-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Guide', @@ -199,7 +649,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_hdmi_1-entry] +# name: test_entities[tv][button.lg_tv_hdmi_1-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -236,7 +686,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_hdmi_1-state] +# name: test_entities[tv][button.lg_tv_hdmi_1-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV HDMI 1', @@ -249,7 +699,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_hdmi_2-entry] +# name: test_entities[tv][button.lg_tv_hdmi_2-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -286,7 +736,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_hdmi_2-state] +# name: test_entities[tv][button.lg_tv_hdmi_2-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV HDMI 2', @@ -299,7 +749,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_hdmi_3-entry] +# name: test_entities[tv][button.lg_tv_hdmi_3-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -336,7 +786,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_hdmi_3-state] +# name: test_entities[tv][button.lg_tv_hdmi_3-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV HDMI 3', @@ -349,7 +799,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_hdmi_4-entry] +# name: test_entities[tv][button.lg_tv_hdmi_4-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -386,7 +836,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_hdmi_4-state] +# name: test_entities[tv][button.lg_tv_hdmi_4-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV HDMI 4', @@ -399,7 +849,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_home-entry] +# name: test_entities[tv][button.lg_tv_home-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -436,7 +886,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_home-state] +# name: test_entities[tv][button.lg_tv_home-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Home', @@ -449,7 +899,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_info-entry] +# name: test_entities[tv][button.lg_tv_info-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -486,7 +936,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_info-state] +# name: test_entities[tv][button.lg_tv_info-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Info', @@ -499,7 +949,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_input-entry] +# name: test_entities[tv][button.lg_tv_input-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -536,7 +986,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_input-state] +# name: test_entities[tv][button.lg_tv_input-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Input', @@ -549,7 +999,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_left-entry] +# name: test_entities[tv][button.lg_tv_left-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -586,7 +1036,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_left-state] +# name: test_entities[tv][button.lg_tv_left-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Left', @@ -599,7 +1049,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_menu-entry] +# name: test_entities[tv][button.lg_tv_menu-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -636,7 +1086,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_menu-state] +# name: test_entities[tv][button.lg_tv_menu-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Menu', @@ -649,7 +1099,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_number_0-entry] +# name: test_entities[tv][button.lg_tv_number_0-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -686,7 +1136,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_number_0-state] +# name: test_entities[tv][button.lg_tv_number_0-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Number 0', @@ -699,7 +1149,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_number_1-entry] +# name: test_entities[tv][button.lg_tv_number_1-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -736,7 +1186,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_number_1-state] +# name: test_entities[tv][button.lg_tv_number_1-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Number 1', @@ -749,7 +1199,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_number_2-entry] +# name: test_entities[tv][button.lg_tv_number_2-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -786,7 +1236,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_number_2-state] +# name: test_entities[tv][button.lg_tv_number_2-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Number 2', @@ -799,7 +1249,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_number_3-entry] +# name: test_entities[tv][button.lg_tv_number_3-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -836,7 +1286,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_number_3-state] +# name: test_entities[tv][button.lg_tv_number_3-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Number 3', @@ -849,7 +1299,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_number_4-entry] +# name: test_entities[tv][button.lg_tv_number_4-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -886,7 +1336,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_number_4-state] +# name: test_entities[tv][button.lg_tv_number_4-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Number 4', @@ -899,7 +1349,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_number_5-entry] +# name: test_entities[tv][button.lg_tv_number_5-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -936,7 +1386,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_number_5-state] +# name: test_entities[tv][button.lg_tv_number_5-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Number 5', @@ -949,7 +1399,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_number_6-entry] +# name: test_entities[tv][button.lg_tv_number_6-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -986,7 +1436,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_number_6-state] +# name: test_entities[tv][button.lg_tv_number_6-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Number 6', @@ -999,7 +1449,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_number_7-entry] +# name: test_entities[tv][button.lg_tv_number_7-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -1036,7 +1486,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_number_7-state] +# name: test_entities[tv][button.lg_tv_number_7-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Number 7', @@ -1049,7 +1499,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_number_8-entry] +# name: test_entities[tv][button.lg_tv_number_8-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -1086,7 +1536,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_number_8-state] +# name: test_entities[tv][button.lg_tv_number_8-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Number 8', @@ -1099,7 +1549,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_number_9-entry] +# name: test_entities[tv][button.lg_tv_number_9-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -1136,7 +1586,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_number_9-state] +# name: test_entities[tv][button.lg_tv_number_9-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Number 9', @@ -1149,7 +1599,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_ok-entry] +# name: test_entities[tv][button.lg_tv_ok-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -1186,7 +1636,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_ok-state] +# name: test_entities[tv][button.lg_tv_ok-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV OK', @@ -1199,7 +1649,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_power-entry] +# name: test_entities[tv][button.lg_tv_power-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -1236,7 +1686,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_power-state] +# name: test_entities[tv][button.lg_tv_power-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Power', @@ -1249,7 +1699,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_power_off-entry] +# name: test_entities[tv][button.lg_tv_power_off-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -1286,7 +1736,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_power_off-state] +# name: test_entities[tv][button.lg_tv_power_off-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Power off', @@ -1299,7 +1749,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_power_on-entry] +# name: test_entities[tv][button.lg_tv_power_on-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -1336,7 +1786,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_power_on-state] +# name: test_entities[tv][button.lg_tv_power_on-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Power on', @@ -1349,7 +1799,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_right-entry] +# name: test_entities[tv][button.lg_tv_right-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -1386,7 +1836,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_right-state] +# name: test_entities[tv][button.lg_tv_right-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Right', @@ -1399,7 +1849,7 @@ 'state': 'unknown', }) # --- -# name: test_entities[button.lg_tv_up-entry] +# name: test_entities[tv][button.lg_tv_up-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -1436,7 +1886,7 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.lg_tv_up-state] +# name: test_entities[tv][button.lg_tv_up-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : 'LG TV Up', diff --git a/tests/components/lg_infrared/test_button.py b/tests/components/lg_infrared/test_button.py index 6eb329c59c52..9eed512db531 100644 --- a/tests/components/lg_infrared/test_button.py +++ b/tests/components/lg_infrared/test_button.py @@ -1,10 +1,12 @@ """Tests for the LG Infrared button platform.""" +from infrared_protocols.codes.lg.ac import LGACCode from infrared_protocols.codes.lg.tv import LGTVCode import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS +from homeassistant.components.lg_infrared.const import LGDeviceType from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er @@ -21,7 +23,8 @@ def platforms() -> list[Platform]: return [Platform.BUTTON] -@pytest.mark.usefixtures("init_integration") +@pytest.mark.parametrize("device_type", [LGDeviceType.TV, LGDeviceType.AC]) +@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") async def test_entities( hass: HomeAssistant, snapshot: SnapshotAssertion, @@ -104,3 +107,54 @@ async def test_button_availability_follows_ir_entity( """Test button becomes unavailable when IR entity is unavailable.""" entity_id = "button.lg_tv_power_on" await assert_availability_follows_source_entity(hass, entity_id, EMITTER_ENTITY_ID) + + +@pytest.mark.parametrize("device_type", [LGDeviceType.AC]) +@pytest.mark.parametrize( + ("entity_id", "expected_code"), + [ + ("button.lg_ac_jet_mode", LGACCode.JET), + ("button.lg_ac_eco_mode", LGACCode.ECO), + ("button.lg_ac_viraat_mode", LGACCode.VIRAAT), + ("button.lg_ac_ai_mode", LGACCode.AI_CONVERTIBLE), + ("button.lg_ac_toggle_light", LGACCode.LIGHT_TOGGLE), + ("button.lg_ac_start_wi_fi_pairing", LGACCode.WIFI_TOGGLE), + ("button.lg_ac_toggle_beep", LGACCode.AUDIO_TOGGLE), + ("button.lg_ac_diagnose", LGACCode.DIAGNOSE), + ("button.lg_ac_toggle_vertical_swing", LGACCode.SWING_V_TOGGLE), + ], +) +@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") +async def test_ac_button_press_sends_correct_code( + hass: HomeAssistant, + mock_infrared_emitter_entity: MockInfraredEmitterEntity, + entity_id: str, + expected_code: LGACCode, +) -> None: + """Test pressing an AC button sends the matching fixed IR code.""" + 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) == 1 + timings = mock_infrared_emitter_entity.send_command_calls[0].get_raw_timings() + assert timings == expected_code.to_command().get_raw_timings() + + +@pytest.mark.parametrize("device_type", [LGDeviceType.AC]) +@pytest.mark.parametrize("key", ["swing_v_toggle", "viraat"]) +@pytest.mark.usefixtures("init_integration") +async def test_ac_buttons_disabled_by_default( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + mock_config_entry: MockConfigEntry, + key: str, +) -> None: + """Test the buttons only some units support are registered but disabled.""" + entity_id = entity_registry.async_get_entity_id( + BUTTON_DOMAIN, "lg_infrared", f"{mock_config_entry.entry_id}_{key}" + ) + assert entity_id is not None + entry = entity_registry.async_get(entity_id) + assert entry is not None + assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION