From 229e009dfd9a3d92617e7f1010a69ed86b47ea2d Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Mon, 6 Jul 2026 07:58:01 -0400 Subject: [PATCH] Add command aliases to Vizio remote platform (#166839) Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- homeassistant/components/vizio/remote.py | 31 ++++++++++++++++++++++++ tests/components/vizio/test_remote.py | 22 ++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/vizio/remote.py b/homeassistant/components/vizio/remote.py index 3b44dffbd3a4..d5b86fc2c882 100644 --- a/homeassistant/components/vizio/remote.py +++ b/homeassistant/components/vizio/remote.py @@ -23,6 +23,32 @@ from .coordinator import VizioConfigEntry, VizioDeviceCoordinator PARALLEL_UPDATES = 0 +# Maps native pyvizio key names to human-friendly aliases. +# Keys are uppercase native names (e.g. "CC_TOGGLE"), values are lists of lowercase aliases. +REMOTE_KEY_ALIASES: dict[str, list[str]] = { + "CC_TOGGLE": ["closed_captions", "cc"], + "CH_DOWN": ["channel_down"], + "CH_PREV": ["previous_channel"], + "CH_UP": ["channel_up"], + "INPUT_NEXT": ["next_input"], + "MUTE_TOGGLE": ["mute", "toggle_mute"], + "OK": ["enter", "select"], + "PIC_MODE": ["picture_mode"], + "PIC_SIZE": ["picture_size"], + "POW_OFF": ["off", "power_off"], + "POW_ON": ["on", "power_on"], + "POW_TOGGLE": ["power", "power_toggle", "toggle_power"], + "SEEK_BACK": ["reverse", "rewind"], + "SEEK_FWD": ["forward", "fast_forward", "ff"], + "VOL_DOWN": ["volume_down"], + "VOL_UP": ["volume_up"], +} + +# Invert aliases into {alias: native_key} for O(1) lookup +_ALIAS_LOOKUP: dict[str, str] = { + alias: key for key, aliases in REMOTE_KEY_ALIASES.items() for alias in aliases +} + async def async_setup_entry( hass: HomeAssistant, @@ -49,7 +75,12 @@ class VizioRemote(CoordinatorEntity[VizioDeviceCoordinator], RemoteEntity): self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, unique_id)}) self._device = coordinator.device valid_keys = set(self._device.get_remote_keys_list()) + # Map lowercased native keys to their original uppercase pyvizio names self._command_map: dict[str, str] = {key.lower(): key for key in valid_keys} + # Add aliases only for native keys this device actually supports + for alias, target in _ALIAS_LOOKUP.items(): + if target in valid_keys: + self._command_map[alias] = target @property @override diff --git a/tests/components/vizio/test_remote.py b/tests/components/vizio/test_remote.py index 3c7e3e410265..d3fe4a900c7c 100644 --- a/tests/components/vizio/test_remote.py +++ b/tests/components/vizio/test_remote.py @@ -110,6 +110,12 @@ async def test_turn_on_off( ("BACK", "BACK"), ("ch_up", "CH_UP"), ("SMARTCAST", "SMARTCAST"), + # Aliases + ("closed_captions", "CC_TOGGLE"), + ("channel_up", "CH_UP"), + ("enter", "OK"), + ("volume_down", "VOL_DOWN"), + ("VoLuMe_DoWn", "VOL_DOWN"), ], ) @pytest.mark.usefixtures("vizio_connect", "vizio_update") @@ -160,9 +166,13 @@ async def test_send_command_tv_invalid( @pytest.mark.parametrize( ("command", "expected_key"), [ + # Native keys (one lowercase variant tested) ("MUTE_TOGGLE", "MUTE_TOGGLE"), ("pause", "PAUSE"), ("VOL_UP", "VOL_UP"), + # Aliases (only those whose target is a speaker key) + ("mute", "MUTE_TOGGLE"), + ("volume_down", "VOL_DOWN"), ], ) @pytest.mark.usefixtures("vizio_connect", "vizio_update") @@ -189,7 +199,17 @@ async def test_send_command_speaker_valid( mock_remote.assert_called_once_with(expected_key, log_api_exception=False) -@pytest.mark.parametrize("command", ["MENU", "CH_UP", "INVALID_KEY"]) +@pytest.mark.parametrize( + "command", + [ + "MENU", + "CH_UP", + # TV-only alias + "channel_up", + # Completely invalid + "INVALID_KEY", + ], +) @pytest.mark.usefixtures("vizio_connect", "vizio_update") async def test_send_command_speaker_invalid( hass: HomeAssistant,