From c9428da767e9be9dd37e7009cdc836eb7094c5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ab=C3=ADlio=20Costa?= Date: Wed, 29 Jul 2026 14:35:58 +0100 Subject: [PATCH] Use proper command set for Edifier R2000DB and R2730DB (#177472) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .../components/edifier_infrared/__init__.py | 15 ++++- .../components/edifier_infrared/button.py | 61 +++++++++++++++++++ .../edifier_infrared/config_flow.py | 2 +- .../components/edifier_infrared/const.py | 4 ++ .../edifier_infrared/media_player.py | 20 ++++++ tests/components/edifier_infrared/conftest.py | 2 +- .../edifier_infrared/test_config_flow.py | 2 + .../components/edifier_infrared/test_init.py | 44 ++++++++++++- 8 files changed, 145 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/edifier_infrared/__init__.py b/homeassistant/components/edifier_infrared/__init__.py index 9c92c66d9ebf..9afd20c9916e 100644 --- a/homeassistant/components/edifier_infrared/__init__.py +++ b/homeassistant/components/edifier_infrared/__init__.py @@ -19,7 +19,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Migrate old config entries.""" - if entry.version > 2: + if entry.version > 3: return False if entry.version == 1: @@ -38,6 +38,19 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: version=2, ) + if entry.version == 2: + data = {**entry.data} + # The R2000DB and R2730DB/RC10D1 models got their own command sets, + # split from the R1280DB one, which they were incorrectly grouped with. + command_set = MODEL_TO_COMMAND_SET[EdifierModel(data[CONF_MODEL])] + data[CONF_COMMAND_SET] = command_set.value + hass.config_entries.async_update_entry( + entry, + data=data, + unique_id=f"{command_set.value}_{data[CONF_INFRARED_ENTITY_ID]}", + version=3, + ) + return True diff --git a/homeassistant/components/edifier_infrared/button.py b/homeassistant/components/edifier_infrared/button.py index 029487f6c393..d653afecb10d 100644 --- a/homeassistant/components/edifier_infrared/button.py +++ b/homeassistant/components/edifier_infrared/button.py @@ -8,6 +8,8 @@ from infrared_protocols.codes.edifier.r1280db import EdifierR1280DBCode from infrared_protocols.codes.edifier.r1700bt_2017 import EdifierR1700BT2017Code from infrared_protocols.codes.edifier.r1700bt_pre_2017 import EdifierR1700BTPre2017Code from infrared_protocols.codes.edifier.r1700bts import EdifierR1700BTsCode +from infrared_protocols.codes.edifier.r2000db import EdifierR2000DBCode +from infrared_protocols.codes.edifier.r2730db import EdifierR2730DBCode from infrared_protocols.codes.edifier.rc20g import EdifierRC20GCode from infrared_protocols.codes.edifier.s360db import EdifierS360DBCode from infrared_protocols.codes.edifier.s3000pro import EdifierS3000ProCode @@ -114,6 +116,65 @@ COMMAND_SET_BUTTONS: dict[ command_code=EdifierR1280DBCode.COAX, ), ), + EdifierCommandSet.R2000DB: ( + EdifierIrButtonEntityDescription( + key="bluetooth", + translation_key="bluetooth", + command_code=EdifierR2000DBCode.BLUETOOTH, + ), + EdifierIrButtonEntityDescription( + key="line_1", + translation_key="line_1", + command_code=EdifierR2000DBCode.LINE_1, + ), + EdifierIrButtonEntityDescription( + key="line_2", + translation_key="line_2", + command_code=EdifierR2000DBCode.LINE_2, + ), + EdifierIrButtonEntityDescription( + key="optical", + translation_key="optical", + command_code=EdifierR2000DBCode.OPTICAL, + ), + EdifierIrButtonEntityDescription( + key="eq_classic", + translation_key="eq_classic", + command_code=EdifierR2000DBCode.EQ_CLASSIC, + ), + EdifierIrButtonEntityDescription( + key="eq_dynamic", + translation_key="eq_dynamic", + command_code=EdifierR2000DBCode.EQ_DYNAMIC, + ), + ), + EdifierCommandSet.R2730DB: ( + EdifierIrButtonEntityDescription( + key="bluetooth", + translation_key="bluetooth", + command_code=EdifierR2730DBCode.BLUETOOTH, + ), + EdifierIrButtonEntityDescription( + key="line_1", + translation_key="line_1", + command_code=EdifierR2730DBCode.LINE_1, + ), + EdifierIrButtonEntityDescription( + key="line_2", + translation_key="line_2", + command_code=EdifierR2730DBCode.LINE_2, + ), + EdifierIrButtonEntityDescription( + key="optical", + translation_key="optical", + command_code=EdifierR2730DBCode.OPTICAL, + ), + EdifierIrButtonEntityDescription( + key="coax", + translation_key="coax", + command_code=EdifierR2730DBCode.COAX, + ), + ), EdifierCommandSet.S360DB: ( EdifierIrButtonEntityDescription( key="bluetooth", diff --git a/homeassistant/components/edifier_infrared/config_flow.py b/homeassistant/components/edifier_infrared/config_flow.py index 76fde9b7c5f4..805ec1e8dc60 100644 --- a/homeassistant/components/edifier_infrared/config_flow.py +++ b/homeassistant/components/edifier_infrared/config_flow.py @@ -25,7 +25,7 @@ from .const import CONF_COMMAND_SET, CONF_INFRARED_ENTITY_ID, DOMAIN class EdifierIrConfigFlow(ConfigFlow, domain=DOMAIN): """Handle config flow for Edifier IR.""" - VERSION = 2 + VERSION = 3 MINOR_VERSION = 1 @override diff --git a/homeassistant/components/edifier_infrared/const.py b/homeassistant/components/edifier_infrared/const.py index 9575ad58fdd9..e05d126a4616 100644 --- a/homeassistant/components/edifier_infrared/const.py +++ b/homeassistant/components/edifier_infrared/const.py @@ -5,6 +5,8 @@ from infrared_protocols.codes.edifier.r1280t import EdifierR1280TCode from infrared_protocols.codes.edifier.r1700bt_2017 import EdifierR1700BT2017Code from infrared_protocols.codes.edifier.r1700bt_pre_2017 import EdifierR1700BTPre2017Code from infrared_protocols.codes.edifier.r1700bts import EdifierR1700BTsCode +from infrared_protocols.codes.edifier.r2000db import EdifierR2000DBCode +from infrared_protocols.codes.edifier.r2730db import EdifierR2730DBCode from infrared_protocols.codes.edifier.rc20g import EdifierRC20GCode from infrared_protocols.codes.edifier.s360db import EdifierS360DBCode from infrared_protocols.codes.edifier.s3000pro import EdifierS3000ProCode @@ -19,6 +21,8 @@ type EdifierCode = ( | EdifierR1700BTsCode | EdifierR1280DBCode | EdifierR1280TCode + | EdifierR2000DBCode + | EdifierR2730DBCode | EdifierS360DBCode | EdifierRC20GCode | EdifierS3000ProCode diff --git a/homeassistant/components/edifier_infrared/media_player.py b/homeassistant/components/edifier_infrared/media_player.py index b591d9bb7b5d..d5bf89731a1d 100644 --- a/homeassistant/components/edifier_infrared/media_player.py +++ b/homeassistant/components/edifier_infrared/media_player.py @@ -8,6 +8,8 @@ from infrared_protocols.codes.edifier.r1280t import EdifierR1280TCode from infrared_protocols.codes.edifier.r1700bt_2017 import EdifierR1700BT2017Code from infrared_protocols.codes.edifier.r1700bt_pre_2017 import EdifierR1700BTPre2017Code from infrared_protocols.codes.edifier.r1700bts import EdifierR1700BTsCode +from infrared_protocols.codes.edifier.r2000db import EdifierR2000DBCode +from infrared_protocols.codes.edifier.r2730db import EdifierR2730DBCode from infrared_protocols.codes.edifier.rc20g import EdifierRC20GCode from infrared_protocols.codes.edifier.s360db import EdifierS360DBCode from infrared_protocols.codes.edifier.s3000pro import EdifierS3000ProCode @@ -86,6 +88,24 @@ COMMAND_SET_COMMANDS: dict[ ), MediaPlayerEntityFeature.VOLUME_MUTE: (EdifierR1280TCode.MUTE,), }, + EdifierCommandSet.R2000DB: { + MediaPlayerEntityFeature.TURN_ON: (EdifierR2000DBCode.POWER,), + MediaPlayerEntityFeature.TURN_OFF: (EdifierR2000DBCode.POWER,), + MediaPlayerEntityFeature.VOLUME_STEP: ( + (EdifierR2000DBCode.VOLUME_UP,), + (EdifierR2000DBCode.VOLUME_DOWN,), + ), + MediaPlayerEntityFeature.VOLUME_MUTE: (EdifierR2000DBCode.MUTE,), + }, + EdifierCommandSet.R2730DB: { + MediaPlayerEntityFeature.TURN_ON: (EdifierR2730DBCode.POWER,), + MediaPlayerEntityFeature.TURN_OFF: (EdifierR2730DBCode.POWER,), + MediaPlayerEntityFeature.VOLUME_STEP: ( + (EdifierR2730DBCode.VOLUME_UP,), + (EdifierR2730DBCode.VOLUME_DOWN,), + ), + MediaPlayerEntityFeature.VOLUME_MUTE: (EdifierR2730DBCode.MUTE,), + }, EdifierCommandSet.S360DB: { MediaPlayerEntityFeature.TURN_ON: (EdifierS360DBCode.POWER,), MediaPlayerEntityFeature.TURN_OFF: (EdifierS360DBCode.POWER,), diff --git a/tests/components/edifier_infrared/conftest.py b/tests/components/edifier_infrared/conftest.py index 147074c40cc6..ccb0e0b9bd44 100644 --- a/tests/components/edifier_infrared/conftest.py +++ b/tests/components/edifier_infrared/conftest.py @@ -35,7 +35,7 @@ def mock_config_entry() -> MockConfigEntry: CONF_COMMAND_SET: EdifierCommandSet.R1700BTS.value, }, unique_id=f"r1700bts_{MOCK_INFRARED_EMITTER_ENTITY_ID}", - version=2, + version=3, ) diff --git a/tests/components/edifier_infrared/test_config_flow.py b/tests/components/edifier_infrared/test_config_flow.py index afe7dc0fd7d8..5a3f03242290 100644 --- a/tests/components/edifier_infrared/test_config_flow.py +++ b/tests/components/edifier_infrared/test_config_flow.py @@ -26,6 +26,8 @@ from tests.components.infrared import EMITTER_ENTITY_ID (EdifierModel.R1700BTS, EdifierCommandSet.R1700BTS), (EdifierModel.R1280DB, EdifierCommandSet.R1280DB), (EdifierModel.R1280T, EdifierCommandSet.R1280T), + (EdifierModel.R2000DB, EdifierCommandSet.R2000DB), + (EdifierModel.R2730DB, EdifierCommandSet.R2730DB), (EdifierModel.S360DB, EdifierCommandSet.S360DB), (EdifierModel.RC20G, EdifierCommandSet.RC20G), (EdifierModel.S3000PRO, EdifierCommandSet.S3000PRO), diff --git a/tests/components/edifier_infrared/test_init.py b/tests/components/edifier_infrared/test_init.py index 036727af2a11..c79882f9df47 100644 --- a/tests/components/edifier_infrared/test_init.py +++ b/tests/components/edifier_infrared/test_init.py @@ -45,7 +45,7 @@ async def test_setup_and_unload_entry( ], ) @pytest.mark.usefixtures("mock_infrared_emitter_entity", "mock_edifier_code_to_command") -async def test_migrate_entry_v1_to_v2( +async def test_migrate_entry_from_v1( hass: HomeAssistant, old_model: str, old_command_set: str, @@ -70,7 +70,47 @@ async def test_migrate_entry_v1_to_v2( await hass.async_block_till_done() assert entry.state is ConfigEntryState.LOADED - assert entry.version == 2 + assert entry.version == 3 assert entry.data[CONF_MODEL] == expected_model assert entry.data[CONF_COMMAND_SET] == expected_command_set assert entry.unique_id == f"{expected_command_set}_{EMITTER_ENTITY_ID}" + + +@pytest.mark.parametrize( + ("model", "old_command_set", "expected_command_set"), + [ + pytest.param("R2000DB", "r1280db", "r2000db", id="r2000db-split"), + pytest.param("R2730DB", "r1280db", "r2730db", id="r2730db-split"), + pytest.param("RC10D1", "r1280db", "r2730db", id="rc10d1-split"), + pytest.param("R1280DB", "r1280db", "r1280db", id="unchanged-model"), + ], +) +@pytest.mark.usefixtures("mock_infrared_emitter_entity", "mock_edifier_code_to_command") +async def test_migrate_entry_from_v2( + hass: HomeAssistant, + model: str, + old_command_set: str, + expected_command_set: str, +) -> None: + """Test v2 config entries are migrated to the split R2000DB command set.""" + entry = MockConfigEntry( + domain=DOMAIN, + title=f"Edifier {model} via Test IR emitter", + data={ + CONF_INFRARED_ENTITY_ID: EMITTER_ENTITY_ID, + CONF_MODEL: model, + CONF_COMMAND_SET: old_command_set, + }, + unique_id=f"{old_command_set}_{EMITTER_ENTITY_ID}", + version=2, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.LOADED + assert entry.version == 3 + assert entry.data[CONF_MODEL] == model + assert entry.data[CONF_COMMAND_SET] == expected_command_set + assert entry.unique_id == f"{expected_command_set}_{EMITTER_ENTITY_ID}"