diff --git a/homeassistant/components/edifier_infrared/__init__.py b/homeassistant/components/edifier_infrared/__init__.py index 251b388929cf..9c92c66d9ebf 100644 --- a/homeassistant/components/edifier_infrared/__init__.py +++ b/homeassistant/components/edifier_infrared/__init__.py @@ -1,9 +1,13 @@ """Edifier infrared integration for Home Assistant.""" +from infrared_protocols.codes.edifier.models import MODEL_TO_COMMAND_SET, EdifierModel + from homeassistant.config_entries import ConfigEntry -from homeassistant.const import Platform +from homeassistant.const import CONF_MODEL, Platform from homeassistant.core import HomeAssistant +from .const import CONF_COMMAND_SET, CONF_INFRARED_ENTITY_ID + PLATFORMS = [Platform.BUTTON, Platform.MEDIA_PLAYER] @@ -13,6 +17,30 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True +async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Migrate old config entries.""" + if entry.version > 2: + return False + + if entry.version == 1: + data = {**entry.data} + # The R1700BT model was renamed to R1700BT (pre-2017), and its + # command set was split from the one shared with the R1700BTs + # family, which it was incorrectly grouped with. + if data[CONF_MODEL] == "R1700BT": + data[CONF_MODEL] = EdifierModel.R1700BT_PRE_2017.value + 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=2, + ) + + return True + + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload an Edifier IR config entry.""" return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/edifier_infrared/button.py b/homeassistant/components/edifier_infrared/button.py index 10cd8d2e481e..678a9de7cb0e 100644 --- a/homeassistant/components/edifier_infrared/button.py +++ b/homeassistant/components/edifier_infrared/button.py @@ -5,7 +5,8 @@ from typing import override from infrared_protocols.codes.edifier.models import EdifierCommandSet, EdifierModel from infrared_protocols.codes.edifier.r1280db import EdifierR1280DBCode -from infrared_protocols.codes.edifier.r1700bt import EdifierR1700BTCode +from infrared_protocols.codes.edifier.r1700bt_pre_2017 import EdifierR1700BTPre2017Code +from infrared_protocols.codes.edifier.r1700bts import EdifierR1700BTsCode from infrared_protocols.codes.edifier.rc20g import EdifierRC20GCode from infrared_protocols.codes.edifier.s360db import EdifierS360DBCode from infrared_protocols.codes.edifier.s3000pro import EdifierS3000ProCode @@ -34,31 +35,43 @@ COMMAND_SET_BUTTONS: dict[ EdifierCommandSet, tuple[EdifierIrButtonEntityDescription, ...], ] = { - EdifierCommandSet.R1700BT: ( + EdifierCommandSet.R1700BT_PRE_2017: ( EdifierIrButtonEntityDescription( key="bluetooth", translation_key="bluetooth", - command_code=EdifierR1700BTCode.BLUETOOTH, + command_code=EdifierR1700BTPre2017Code.BLUETOOTH, + ), + EdifierIrButtonEntityDescription( + key="line", + translation_key="line", + command_code=EdifierR1700BTPre2017Code.LINE, + ), + ), + EdifierCommandSet.R1700BTS: ( + EdifierIrButtonEntityDescription( + key="bluetooth", + translation_key="bluetooth", + command_code=EdifierR1700BTsCode.BLUETOOTH, ), EdifierIrButtonEntityDescription( key="line_1", translation_key="line_1", - command_code=EdifierR1700BTCode.LINE_1, + command_code=EdifierR1700BTsCode.LINE_1, ), EdifierIrButtonEntityDescription( key="line_2", translation_key="line_2", - command_code=EdifierR1700BTCode.LINE_2, + command_code=EdifierR1700BTsCode.LINE_2, ), EdifierIrButtonEntityDescription( key="fx_on", translation_key="fx_on", - command_code=EdifierR1700BTCode.FX_ON, + command_code=EdifierR1700BTsCode.FX_ON, ), EdifierIrButtonEntityDescription( key="fx_off", translation_key="fx_off", - command_code=EdifierR1700BTCode.FX_OFF, + command_code=EdifierR1700BTsCode.FX_OFF, ), ), EdifierCommandSet.R1280DB: ( diff --git a/homeassistant/components/edifier_infrared/config_flow.py b/homeassistant/components/edifier_infrared/config_flow.py index 528a3b963036..76fde9b7c5f4 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 = 1 + VERSION = 2 MINOR_VERSION = 1 @override diff --git a/homeassistant/components/edifier_infrared/const.py b/homeassistant/components/edifier_infrared/const.py index 4fd4b959d7b2..fb5b48aecfd3 100644 --- a/homeassistant/components/edifier_infrared/const.py +++ b/homeassistant/components/edifier_infrared/const.py @@ -2,7 +2,8 @@ from infrared_protocols.codes.edifier.r1280db import EdifierR1280DBCode from infrared_protocols.codes.edifier.r1280t import EdifierR1280TCode -from infrared_protocols.codes.edifier.r1700bt import EdifierR1700BTCode +from infrared_protocols.codes.edifier.r1700bt_pre_2017 import EdifierR1700BTPre2017Code +from infrared_protocols.codes.edifier.r1700bts import EdifierR1700BTsCode from infrared_protocols.codes.edifier.rc20g import EdifierRC20GCode from infrared_protocols.codes.edifier.s360db import EdifierS360DBCode from infrared_protocols.codes.edifier.s3000pro import EdifierS3000ProCode @@ -12,7 +13,8 @@ CONF_INFRARED_ENTITY_ID = "infrared_entity_id" CONF_COMMAND_SET = "command_set" type EdifierCode = ( - EdifierR1700BTCode + EdifierR1700BTPre2017Code + | EdifierR1700BTsCode | EdifierR1280DBCode | EdifierR1280TCode | EdifierS360DBCode diff --git a/homeassistant/components/edifier_infrared/media_player.py b/homeassistant/components/edifier_infrared/media_player.py index 8c9963e80e9d..ea1fc90806c9 100644 --- a/homeassistant/components/edifier_infrared/media_player.py +++ b/homeassistant/components/edifier_infrared/media_player.py @@ -5,7 +5,8 @@ from typing import override from infrared_protocols.codes.edifier.models import EdifierCommandSet, EdifierModel from infrared_protocols.codes.edifier.r1280db import EdifierR1280DBCode from infrared_protocols.codes.edifier.r1280t import EdifierR1280TCode -from infrared_protocols.codes.edifier.r1700bt import EdifierR1700BTCode +from infrared_protocols.codes.edifier.r1700bt_pre_2017 import EdifierR1700BTPre2017Code +from infrared_protocols.codes.edifier.r1700bts import EdifierR1700BTsCode from infrared_protocols.codes.edifier.rc20g import EdifierRC20GCode from infrared_protocols.codes.edifier.s360db import EdifierS360DBCode from infrared_protocols.codes.edifier.s3000pro import EdifierS3000ProCode @@ -35,18 +36,25 @@ COMMAND_SET_COMMANDS: dict[ tuple[EdifierCode | tuple[EdifierCode, ...], ...], ], ] = { - EdifierCommandSet.R1700BT: { - MediaPlayerEntityFeature.TURN_ON: (EdifierR1700BTCode.POWER,), - MediaPlayerEntityFeature.TURN_OFF: (EdifierR1700BTCode.POWER,), + EdifierCommandSet.R1700BT_PRE_2017: { MediaPlayerEntityFeature.VOLUME_STEP: ( - (EdifierR1700BTCode.VOLUME_UP,), - (EdifierR1700BTCode.VOLUME_DOWN,), + (EdifierR1700BTPre2017Code.VOLUME_UP,), + (EdifierR1700BTPre2017Code.VOLUME_DOWN,), ), - MediaPlayerEntityFeature.VOLUME_MUTE: (EdifierR1700BTCode.MUTE,), - MediaPlayerEntityFeature.PLAY: (EdifierR1700BTCode.PLAY_PAUSE,), - MediaPlayerEntityFeature.PAUSE: (EdifierR1700BTCode.PLAY_PAUSE,), - MediaPlayerEntityFeature.NEXT_TRACK: (EdifierR1700BTCode.FORWARD,), - MediaPlayerEntityFeature.PREVIOUS_TRACK: (EdifierR1700BTCode.BACK,), + MediaPlayerEntityFeature.VOLUME_MUTE: (EdifierR1700BTPre2017Code.MUTE,), + }, + EdifierCommandSet.R1700BTS: { + MediaPlayerEntityFeature.TURN_ON: (EdifierR1700BTsCode.POWER,), + MediaPlayerEntityFeature.TURN_OFF: (EdifierR1700BTsCode.POWER,), + MediaPlayerEntityFeature.VOLUME_STEP: ( + (EdifierR1700BTsCode.VOLUME_UP,), + (EdifierR1700BTsCode.VOLUME_DOWN,), + ), + MediaPlayerEntityFeature.VOLUME_MUTE: (EdifierR1700BTsCode.MUTE,), + MediaPlayerEntityFeature.PLAY: (EdifierR1700BTsCode.PLAY_PAUSE,), + MediaPlayerEntityFeature.PAUSE: (EdifierR1700BTsCode.PLAY_PAUSE,), + MediaPlayerEntityFeature.NEXT_TRACK: (EdifierR1700BTsCode.FORWARD,), + MediaPlayerEntityFeature.PREVIOUS_TRACK: (EdifierR1700BTsCode.BACK,), }, EdifierCommandSet.R1280DB: { MediaPlayerEntityFeature.TURN_ON: (EdifierR1280DBCode.POWER,), diff --git a/homeassistant/components/edifier_infrared/strings.json b/homeassistant/components/edifier_infrared/strings.json index 0ed3c9e2c2a8..65ba46dd839d 100644 --- a/homeassistant/components/edifier_infrared/strings.json +++ b/homeassistant/components/edifier_infrared/strings.json @@ -48,6 +48,9 @@ "fx_on": { "name": "FX on" }, + "line": { + "name": "Line" + }, "line_1": { "name": "Line 1" }, diff --git a/homeassistant/components/infrared/manifest.json b/homeassistant/components/infrared/manifest.json index 8958e81d9eba..1c4681d5d613 100644 --- a/homeassistant/components/infrared/manifest.json +++ b/homeassistant/components/infrared/manifest.json @@ -5,5 +5,5 @@ "documentation": "https://www.home-assistant.io/integrations/infrared", "integration_type": "entity", "quality_scale": "internal", - "requirements": ["infrared-protocols==7.5.0"] + "requirements": ["infrared-protocols==8.1.0"] } diff --git a/requirements.txt b/requirements.txt index 6388e99a9bce..9e785316ce56 100644 --- a/requirements.txt +++ b/requirements.txt @@ -30,7 +30,7 @@ home-assistant-bluetooth==2.0.0 home-assistant-intents==2026.6.24 httpx==0.28.1 ifaddr==0.2.0 -infrared-protocols==7.5.0 +infrared-protocols==8.1.0 Jinja2==3.1.6 lru-dict==1.4.1 mutagen==1.48.1 diff --git a/requirements_all.txt b/requirements_all.txt index ff1577446770..5ea38a6866a9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1374,7 +1374,7 @@ influxdb-client==1.50.0 influxdb==5.3.2 # homeassistant.components.infrared -infrared-protocols==7.5.0 +infrared-protocols==8.1.0 # homeassistant.components.inkbird inkbird-ble==1.4.4 diff --git a/tests/components/edifier_infrared/conftest.py b/tests/components/edifier_infrared/conftest.py index 7cc22ec7b2a2..147074c40cc6 100644 --- a/tests/components/edifier_infrared/conftest.py +++ b/tests/components/edifier_infrared/conftest.py @@ -28,13 +28,14 @@ def mock_config_entry() -> MockConfigEntry: return MockConfigEntry( domain=DOMAIN, entry_id="01JTEST0000000000000000000", - title="Edifier R1700BT via Test IR emitter", + title="Edifier R1700BTs via Test IR emitter", data={ CONF_INFRARED_ENTITY_ID: MOCK_INFRARED_EMITTER_ENTITY_ID, - CONF_MODEL: EdifierModel.R1700BT.value, - CONF_COMMAND_SET: EdifierCommandSet.R1700BT.value, + CONF_MODEL: EdifierModel.R1700BTS.value, + CONF_COMMAND_SET: EdifierCommandSet.R1700BTS.value, }, - unique_id=f"r1700bt_{MOCK_INFRARED_EMITTER_ENTITY_ID}", + unique_id=f"r1700bts_{MOCK_INFRARED_EMITTER_ENTITY_ID}", + version=2, ) @@ -53,7 +54,12 @@ def mock_edifier_code_to_command() -> Generator[None]: """ with ( patch( - "infrared_protocols.codes.edifier.r1700bt.EdifierR1700BTCode.to_command", + "infrared_protocols.codes.edifier.r1700bt_pre_2017.EdifierR1700BTPre2017Code.to_command", + autospec=True, + side_effect=lambda self: self, + ), + patch( + "infrared_protocols.codes.edifier.r1700bts.EdifierR1700BTsCode.to_command", autospec=True, side_effect=lambda self: self, ), diff --git a/tests/components/edifier_infrared/snapshots/test_button.ambr b/tests/components/edifier_infrared/snapshots/test_button.ambr index 06ff22cd26a3..a9e4b21733ab 100644 --- a/tests/components/edifier_infrared/snapshots/test_button.ambr +++ b/tests/components/edifier_infrared/snapshots/test_button.ambr @@ -1,5 +1,5 @@ # serializer version: 1 -# name: test_entities[button.edifier_r1700bt_bluetooth-entry] +# name: test_entities[button.edifier_r1700bts_bluetooth-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -13,7 +13,7 @@ 'disabled_by': None, 'domain': 'button', 'entity_category': None, - 'entity_id': 'button.edifier_r1700bt_bluetooth', + 'entity_id': 'button.edifier_r1700bts_bluetooth', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -36,20 +36,20 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.edifier_r1700bt_bluetooth-state] +# name: test_entities[button.edifier_r1700bts_bluetooth-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - : 'Edifier R1700BT Bluetooth', + : 'Edifier R1700BTs Bluetooth', }), 'context': , - 'entity_id': 'button.edifier_r1700bt_bluetooth', + 'entity_id': 'button.edifier_r1700bts_bluetooth', 'last_changed': , 'last_reported': , 'last_updated': , 'state': 'unknown', }) # --- -# name: test_entities[button.edifier_r1700bt_fx_off-entry] +# name: test_entities[button.edifier_r1700bts_fx_off-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -63,7 +63,7 @@ 'disabled_by': None, 'domain': 'button', 'entity_category': None, - 'entity_id': 'button.edifier_r1700bt_fx_off', + 'entity_id': 'button.edifier_r1700bts_fx_off', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -86,20 +86,20 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.edifier_r1700bt_fx_off-state] +# name: test_entities[button.edifier_r1700bts_fx_off-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - : 'Edifier R1700BT FX off', + : 'Edifier R1700BTs FX off', }), 'context': , - 'entity_id': 'button.edifier_r1700bt_fx_off', + 'entity_id': 'button.edifier_r1700bts_fx_off', 'last_changed': , 'last_reported': , 'last_updated': , 'state': 'unknown', }) # --- -# name: test_entities[button.edifier_r1700bt_fx_on-entry] +# name: test_entities[button.edifier_r1700bts_fx_on-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -113,7 +113,7 @@ 'disabled_by': None, 'domain': 'button', 'entity_category': None, - 'entity_id': 'button.edifier_r1700bt_fx_on', + 'entity_id': 'button.edifier_r1700bts_fx_on', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -136,20 +136,20 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.edifier_r1700bt_fx_on-state] +# name: test_entities[button.edifier_r1700bts_fx_on-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - : 'Edifier R1700BT FX on', + : 'Edifier R1700BTs FX on', }), 'context': , - 'entity_id': 'button.edifier_r1700bt_fx_on', + 'entity_id': 'button.edifier_r1700bts_fx_on', 'last_changed': , 'last_reported': , 'last_updated': , 'state': 'unknown', }) # --- -# name: test_entities[button.edifier_r1700bt_line_1-entry] +# name: test_entities[button.edifier_r1700bts_line_1-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -163,7 +163,7 @@ 'disabled_by': None, 'domain': 'button', 'entity_category': None, - 'entity_id': 'button.edifier_r1700bt_line_1', + 'entity_id': 'button.edifier_r1700bts_line_1', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -186,20 +186,20 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.edifier_r1700bt_line_1-state] +# name: test_entities[button.edifier_r1700bts_line_1-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - : 'Edifier R1700BT Line 1', + : 'Edifier R1700BTs Line 1', }), 'context': , - 'entity_id': 'button.edifier_r1700bt_line_1', + 'entity_id': 'button.edifier_r1700bts_line_1', 'last_changed': , 'last_reported': , 'last_updated': , 'state': 'unknown', }) # --- -# name: test_entities[button.edifier_r1700bt_line_2-entry] +# name: test_entities[button.edifier_r1700bts_line_2-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -213,7 +213,7 @@ 'disabled_by': None, 'domain': 'button', 'entity_category': None, - 'entity_id': 'button.edifier_r1700bt_line_2', + 'entity_id': 'button.edifier_r1700bts_line_2', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -236,13 +236,13 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[button.edifier_r1700bt_line_2-state] +# name: test_entities[button.edifier_r1700bts_line_2-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - : 'Edifier R1700BT Line 2', + : 'Edifier R1700BTs Line 2', }), 'context': , - 'entity_id': 'button.edifier_r1700bt_line_2', + 'entity_id': 'button.edifier_r1700bts_line_2', 'last_changed': , 'last_reported': , 'last_updated': , diff --git a/tests/components/edifier_infrared/snapshots/test_media_player.ambr b/tests/components/edifier_infrared/snapshots/test_media_player.ambr index e7c299a6a670..10e7edc8c4c5 100644 --- a/tests/components/edifier_infrared/snapshots/test_media_player.ambr +++ b/tests/components/edifier_infrared/snapshots/test_media_player.ambr @@ -1,5 +1,5 @@ # serializer version: 1 -# name: test_entities[media_player.edifier_r1700bt-entry] +# name: test_entities[media_player.edifier_r1700bts-entry] EntityRegistryEntrySnapshot({ 'aliases': list([ None, @@ -14,7 +14,7 @@ 'disabled_by': None, 'domain': 'media_player', 'entity_category': None, - 'entity_id': 'media_player.edifier_r1700bt', + 'entity_id': 'media_player.edifier_r1700bts', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -37,16 +37,16 @@ 'unit_of_measurement': None, }) # --- -# name: test_entities[media_player.edifier_r1700bt-state] +# name: test_entities[media_player.edifier_r1700bts-state] StateSnapshot({ 'attributes': ReadOnlyDict({ : True, : 'speaker', - : 'Edifier R1700BT', + : 'Edifier R1700BTs', : , }), 'context': , - 'entity_id': 'media_player.edifier_r1700bt', + 'entity_id': 'media_player.edifier_r1700bts', 'last_changed': , 'last_reported': , 'last_updated': , diff --git a/tests/components/edifier_infrared/test_button.py b/tests/components/edifier_infrared/test_button.py index d97911aad4da..35427e76969f 100644 --- a/tests/components/edifier_infrared/test_button.py +++ b/tests/components/edifier_infrared/test_button.py @@ -1,6 +1,6 @@ """Tests for the Edifier Infrared button platform.""" -from infrared_protocols.codes.edifier.r1700bt import EdifierR1700BTCode +from infrared_protocols.codes.edifier.r1700bts import EdifierR1700BTsCode import pytest from syrupy.assertion import SnapshotAssertion @@ -14,7 +14,7 @@ from tests.components.common import assert_availability_follows_source_entity from tests.components.infrared import EMITTER_ENTITY_ID from tests.components.infrared.common import MockInfraredEmitterEntity -BLUETOOTH_BUTTON_ENTITY_ID = "button.edifier_r1700bt_bluetooth" +BLUETOOTH_BUTTON_ENTITY_ID = "button.edifier_r1700bts_bluetooth" @pytest.fixture @@ -37,11 +37,11 @@ async def test_entities( @pytest.mark.parametrize( ("entity_id", "expected_code"), [ - ("button.edifier_r1700bt_bluetooth", EdifierR1700BTCode.BLUETOOTH), - ("button.edifier_r1700bt_line_1", EdifierR1700BTCode.LINE_1), - ("button.edifier_r1700bt_line_2", EdifierR1700BTCode.LINE_2), - ("button.edifier_r1700bt_fx_on", EdifierR1700BTCode.FX_ON), - ("button.edifier_r1700bt_fx_off", EdifierR1700BTCode.FX_OFF), + ("button.edifier_r1700bts_bluetooth", EdifierR1700BTsCode.BLUETOOTH), + ("button.edifier_r1700bts_line_1", EdifierR1700BTsCode.LINE_1), + ("button.edifier_r1700bts_line_2", EdifierR1700BTsCode.LINE_2), + ("button.edifier_r1700bts_fx_on", EdifierR1700BTsCode.FX_ON), + ("button.edifier_r1700bts_fx_off", EdifierR1700BTsCode.FX_OFF), ], ) @pytest.mark.usefixtures("init_integration") @@ -49,7 +49,7 @@ async def test_button_press_sends_correct_code( hass: HomeAssistant, mock_infrared_emitter_entity: MockInfraredEmitterEntity, entity_id: str, - expected_code: EdifierR1700BTCode, + expected_code: EdifierR1700BTsCode, ) -> None: """Test each button press sends the correct IR code.""" await hass.services.async_call( diff --git a/tests/components/edifier_infrared/test_config_flow.py b/tests/components/edifier_infrared/test_config_flow.py index 119cfa8ad0c1..ebfecfb94ef9 100644 --- a/tests/components/edifier_infrared/test_config_flow.py +++ b/tests/components/edifier_infrared/test_config_flow.py @@ -21,7 +21,8 @@ from tests.components.infrared import EMITTER_ENTITY_ID @pytest.mark.parametrize( ("model", "expected_command_set"), [ - (EdifierModel.R1700BT, EdifierCommandSet.R1700BT), + (EdifierModel.R1700BT_PRE_2017, EdifierCommandSet.R1700BT_PRE_2017), + (EdifierModel.R1700BTS, EdifierCommandSet.R1700BTS), (EdifierModel.R1280DB, EdifierCommandSet.R1280DB), (EdifierModel.R1280T, EdifierCommandSet.R1280T), (EdifierModel.S360DB, EdifierCommandSet.S360DB), @@ -81,7 +82,7 @@ async def test_user_flow_already_configured( result["flow_id"], user_input={ CONF_INFRARED_ENTITY_ID: EMITTER_ENTITY_ID, - CONF_MODEL: EdifierModel.R1700BT.value, + CONF_MODEL: EdifierModel.R1700BTS.value, }, ) @@ -104,8 +105,8 @@ async def test_user_flow_no_emitters(hass: HomeAssistant) -> None: @pytest.mark.parametrize( ("entity_name", "expected_title"), [ - (None, "Edifier R1700BT via Test IR emitter"), - ("Living room IR", "Edifier R1700BT via Living room IR"), + (None, "Edifier R1700BTs via Test IR emitter"), + ("Living room IR", "Edifier R1700BTs via Living room IR"), ], ) async def test_user_flow_title_from_entity_name( @@ -124,7 +125,7 @@ async def test_user_flow_title_from_entity_name( result["flow_id"], user_input={ CONF_INFRARED_ENTITY_ID: EMITTER_ENTITY_ID, - CONF_MODEL: EdifierModel.R1700BT.value, + CONF_MODEL: EdifierModel.R1700BTS.value, }, ) diff --git a/tests/components/edifier_infrared/test_init.py b/tests/components/edifier_infrared/test_init.py index 8e2770e8ca0d..036727af2a11 100644 --- a/tests/components/edifier_infrared/test_init.py +++ b/tests/components/edifier_infrared/test_init.py @@ -1,9 +1,18 @@ """Tests for the Edifier Infrared integration setup.""" +import pytest + +from homeassistant.components.edifier_infrared.const import ( + CONF_COMMAND_SET, + CONF_INFRARED_ENTITY_ID, + DOMAIN, +) from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import CONF_MODEL from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry +from tests.components.infrared import EMITTER_ENTITY_ID async def test_setup_and_unload_entry( @@ -17,3 +26,51 @@ async def test_setup_and_unload_entry( await hass.async_block_till_done() assert entry.state is ConfigEntryState.NOT_LOADED + + +@pytest.mark.parametrize( + ("old_model", "old_command_set", "expected_model", "expected_command_set"), + [ + pytest.param( + "R1700BT", + "r1700bt", + "R1700BT (pre-2017)", + "r1700bt_pre_2017", + id="r1700bt-to-pre-2017", + ), + pytest.param( + "R1700BTs", "r1700bt", "R1700BTs", "r1700bts", id="r1700bts-family" + ), + pytest.param("R1280DB", "r1280db", "R1280DB", "r1280db", id="unchanged-model"), + ], +) +@pytest.mark.usefixtures("mock_infrared_emitter_entity", "mock_edifier_code_to_command") +async def test_migrate_entry_v1_to_v2( + hass: HomeAssistant, + old_model: str, + old_command_set: str, + expected_model: str, + expected_command_set: str, +) -> None: + """Test v1 config entries are migrated to the split R1700BT command sets.""" + entry = MockConfigEntry( + domain=DOMAIN, + title=f"Edifier {old_model} via Test IR emitter", + data={ + CONF_INFRARED_ENTITY_ID: EMITTER_ENTITY_ID, + CONF_MODEL: old_model, + CONF_COMMAND_SET: old_command_set, + }, + unique_id=f"{old_command_set}_{EMITTER_ENTITY_ID}", + version=1, + ) + 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 == 2 + 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}" diff --git a/tests/components/edifier_infrared/test_media_player.py b/tests/components/edifier_infrared/test_media_player.py index 5fb1779ebcb0..e737b117bcc3 100644 --- a/tests/components/edifier_infrared/test_media_player.py +++ b/tests/components/edifier_infrared/test_media_player.py @@ -1,7 +1,7 @@ """Tests for the Edifier Infrared media player platform.""" from infrared_protocols.codes.edifier.models import EdifierCommandSet, EdifierModel -from infrared_protocols.codes.edifier.r1700bt import EdifierR1700BTCode +from infrared_protocols.codes.edifier.r1700bts import EdifierR1700BTsCode from infrared_protocols.codes.edifier.rc20g import EdifierRC20GCode import pytest from syrupy.assertion import SnapshotAssertion @@ -32,7 +32,7 @@ from tests.components.common import assert_availability_follows_source_entity from tests.components.infrared import EMITTER_ENTITY_ID from tests.components.infrared.common import MockInfraredEmitterEntity -MEDIA_PLAYER_ENTITY_ID = "media_player.edifier_r1700bt" +MEDIA_PLAYER_ENTITY_ID = "media_player.edifier_r1700bts" @pytest.fixture @@ -55,15 +55,15 @@ async def test_entities( @pytest.mark.parametrize( ("service", "service_data", "expected_code"), [ - (SERVICE_TURN_ON, {}, EdifierR1700BTCode.POWER), - (SERVICE_TURN_OFF, {}, EdifierR1700BTCode.POWER), - (SERVICE_VOLUME_UP, {}, EdifierR1700BTCode.VOLUME_UP), - (SERVICE_VOLUME_DOWN, {}, EdifierR1700BTCode.VOLUME_DOWN), - (SERVICE_VOLUME_MUTE, {"is_volume_muted": True}, EdifierR1700BTCode.MUTE), - (SERVICE_MEDIA_PLAY, {}, EdifierR1700BTCode.PLAY_PAUSE), - (SERVICE_MEDIA_PAUSE, {}, EdifierR1700BTCode.PLAY_PAUSE), - (SERVICE_MEDIA_NEXT_TRACK, {}, EdifierR1700BTCode.FORWARD), - (SERVICE_MEDIA_PREVIOUS_TRACK, {}, EdifierR1700BTCode.BACK), + (SERVICE_TURN_ON, {}, EdifierR1700BTsCode.POWER), + (SERVICE_TURN_OFF, {}, EdifierR1700BTsCode.POWER), + (SERVICE_VOLUME_UP, {}, EdifierR1700BTsCode.VOLUME_UP), + (SERVICE_VOLUME_DOWN, {}, EdifierR1700BTsCode.VOLUME_DOWN), + (SERVICE_VOLUME_MUTE, {"is_volume_muted": True}, EdifierR1700BTsCode.MUTE), + (SERVICE_MEDIA_PLAY, {}, EdifierR1700BTsCode.PLAY_PAUSE), + (SERVICE_MEDIA_PAUSE, {}, EdifierR1700BTsCode.PLAY_PAUSE), + (SERVICE_MEDIA_NEXT_TRACK, {}, EdifierR1700BTsCode.FORWARD), + (SERVICE_MEDIA_PREVIOUS_TRACK, {}, EdifierR1700BTsCode.BACK), ], ) @pytest.mark.usefixtures("init_integration") @@ -72,7 +72,7 @@ async def test_media_player_action_sends_correct_code( mock_infrared_emitter_entity: MockInfraredEmitterEntity, service: str, service_data: dict[str, bool], - expected_code: EdifierR1700BTCode, + expected_code: EdifierR1700BTsCode, ) -> None: """Test each media player action sends the correct IR code.""" await hass.services.async_call(