From dc165e825c9c9a6acde8a2f09fd71641d2ecc0c2 Mon Sep 17 00:00:00 2001 From: Glenn de Haan Date: Wed, 25 Feb 2026 12:08:33 +0100 Subject: [PATCH] Add set_audio_unmute function. Add set_earc_unmute function. Add additional tests. Updated README.md --- README.md | 36 +++++++++++----------- pyproject.toml | 2 +- src/hdfury/api.py | 8 +++++ tests/test_api.py | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index c44fea6..39930e1 100644 --- a/README.md +++ b/README.md @@ -71,23 +71,25 @@ HDFuryAPI(host: str, session: aiohttp.ClientSession | None = None) #### Command Methods -| Method | Description | -|-----------------------------------------------------------|------------------------------------| -| `issue_reboot()` | Reboot the device | -| `issue_hotplug()` | Trigger HDMI hotplug | -| `set_operation_mode(mode)` | Set device operation mode | -| `set_port_selection(tx0, tx1)` | Set HDMI input routing for TX0/TX1 | -| `set_auto_switch_inputs(state)` | Toggle auto input switching | -| `set_htpc_mode_rx0(state)` → `set_htpc_mode_rx3(state)` | Enable/disable HTPC mode per input | -| `set_mute_tx0_audio(state)` → `set_mute_tx1_audio(state)` | Mute/unmute TX audio | -| `set_tx0_force_5v(state)` → `set_tx1_force_5v(state)` | Force +5V output on TX ports | -| `set_oled(state)` | Enable/disable OLED display | -| `set_oled_fade(value)` | Set OLED fade timeout (seconds) | -| `set_ir_active(state)` | Enable/disable IR receiver | -| `set_relay(state)` | Enable/disable relay state | -| `set_cec(state)` | Enable/disable global CEC | -| `set_cec_rx0(state)` → `set_cec_rx3(state)` | Enable/disable CEC per input | -| `set_reboot_timer(value)` | Set reboot timer (hours) | +| Method | Description | +|-----------------------------------------------------------|--------------------------------------------| +| `issue_reboot()` | Reboot the device | +| `issue_hotplug()` | Trigger HDMI hotplug | +| `set_operation_mode(mode)` | Set device operation mode | +| `set_port_selection(tx0, tx1)` | Set HDMI input routing for TX0/TX1 | +| `set_auto_switch_inputs(state)` | Toggle auto input switching | +| `set_htpc_mode_rx0(state)` → `set_htpc_mode_rx3(state)` | Enable/disable HTPC mode per input | +| `set_mute_tx0_audio(state)` → `set_mute_tx1_audio(state)` | Mute/unmute TX audio | +| `set_audio_unmute(value)` | Set SINK audio unmute delay (milliseconds) | +| `set_earc_unmute(value)` | Set eARC audio unmute delay (milliseconds) | +| `set_tx0_force_5v(state)` → `set_tx1_force_5v(state)` | Force +5V output on TX ports | +| `set_oled(state)` | Enable/disable OLED display | +| `set_oled_fade(value)` | Set OLED fade timeout (seconds) | +| `set_ir_active(state)` | Enable/disable IR receiver | +| `set_relay(state)` | Enable/disable relay state | +| `set_cec(state)` | Enable/disable global CEC | +| `set_cec_rx0(state)` → `set_cec_rx3(state)` | Enable/disable CEC per input | +| `set_reboot_timer(value)` | Set reboot timer (hours) | #### Cleanup diff --git a/pyproject.toml b/pyproject.toml index 35a17ee..2b75c17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "hdfury" -version = "1.5.0" +version = "1.6.0" description = "Asynchronous Python client for HDFury devices" authors = [ { name = "Glenn de Haan", email = "glenn@dehaan.cloud" } diff --git a/src/hdfury/api.py b/src/hdfury/api.py index 92c5d06..c91ce81 100644 --- a/src/hdfury/api.py +++ b/src/hdfury/api.py @@ -120,6 +120,14 @@ async def set_auto_switch_inputs(self, state: str) -> None: """Send auto switch inputs command to the device.""" await self._send_command("autosw", self._normalize_state(state, output="text")) + async def set_audio_unmute(self, state: str) -> None: + """Send audio unmute (milliseconds) command to the device.""" + await self._send_command("unmutecnt", state) + + async def set_earc_unmute(self, state: str) -> None: + """Send earc unmute (milliseconds) command to the device.""" + await self._send_command("earcunmutecnt", state) + async def set_htpc_mode_rx0(self, state: str) -> None: """Send htpc mode rx0 command to the device.""" await self._send_command("htpcmode0", self._normalize_state(state, output="text")) diff --git a/tests/test_api.py b/tests/test_api.py index 153b990..07dcaf0 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -249,6 +249,36 @@ async def test_set_auto_switch_inputs(client: HDFuryAPI, endpoint: str, method: await getattr(client, method)(value) +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("endpoint", "method", "value"), + [ + ("unmutecnt", "set_audio_unmute", "30"), + ("unmutecnt", "set_audio_unmute", "60"), + ], +) +async def test_set_audio_unmute(client: HDFuryAPI, endpoint: str, method: str, value: str): + """Verify set_audio_unmute sends the correct command for each Audio Unmute state.""" + with aioresponses() as mock: + mock.get(f"http://192.168.1.123/cmd?{endpoint}={value}", status=200) + + await getattr(client, method)(value) + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("endpoint", "method", "value"), + [ + ("earcunmutecnt", "set_earc_unmute", "30"), + ("earcunmutecnt", "set_earc_unmute", "60"), + ], +) +async def test_set_earc_unmute(client: HDFuryAPI, endpoint: str, method: str, value: str): + """Verify set_earc_unmute sends the correct command for each eARC Unmute state.""" + with aioresponses() as mock: + mock.get(f"http://192.168.1.123/cmd?{endpoint}={value}", status=200) + + await getattr(client, method)(value) + @pytest.mark.asyncio @pytest.mark.parametrize( ("endpoint", "method", "value"), @@ -287,6 +317,23 @@ async def test_set_mute_tx_audio(client: HDFuryAPI, endpoint: str, method: str, await getattr(client, method)(value) +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("endpoint", "method", "value"), + [ + ("tx0plus5", "set_tx0_force_5v", "on"), + ("tx1plus5", "set_tx1_force_5v", "on"), + ("tx0plus5", "set_tx0_force_5v", "off"), + ("tx1plus5", "set_tx1_force_5v", "off"), + ], +) +async def test_set_tx_force_5v(client: HDFuryAPI, endpoint: str, method: str, value: str): + """Verify set_tx_force_5v commands are sent correctly for each transmitter and state.""" + with aioresponses() as mock: + mock.get(f"http://192.168.1.123/cmd?{endpoint}={value}", status=200) + + await getattr(client, method)(value) + @pytest.mark.asyncio @pytest.mark.parametrize( ("endpoint", "method", "value"), @@ -302,6 +349,21 @@ async def test_set_oled(client: HDFuryAPI, endpoint: str, method: str, value: st await getattr(client, method)(value) +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("endpoint", "method", "value"), + [ + ("oledfade", "set_oled_fade", "30"), + ("oledfade", "set_oled_fade", "60"), + ], +) +async def test_set_oled_fade(client: HDFuryAPI, endpoint: str, method: str, value: str): + """Verify set_oled_fade sends the correct command for each OLED fade state.""" + with aioresponses() as mock: + mock.get(f"http://192.168.1.123/cmd?{endpoint}={value}", status=200) + + await getattr(client, method)(value) + @pytest.mark.asyncio @pytest.mark.parametrize( ("endpoint", "method", "value"), @@ -336,6 +398,7 @@ async def test_set_relay(client: HDFuryAPI, endpoint: str, method: str, value: s @pytest.mark.parametrize( ("endpoint", "method", "value"), [ + ("cec", "set_cec", "off"), ("cec0en", "set_cec_rx0", "1"), ("cec1en", "set_cec_rx1", "1"), ("cec2en", "set_cec_rx2", "0"), @@ -349,6 +412,21 @@ async def test_set_cec_rx(client: HDFuryAPI, endpoint: str, method: str, value: await getattr(client, method)(value) +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("endpoint", "method", "value"), + [ + ("reboottimer", "set_reboot_timer", "30"), + ("reboottimer", "set_reboot_timer", "60"), + ], +) +async def test_set_reboot_timer(client: HDFuryAPI, endpoint: str, method: str, value: str): + """Verify set_reboot_timer sends the correct command for each Reboot Timer state.""" + with aioresponses() as mock: + mock.get(f"http://192.168.1.123/cmd?{endpoint}={value}", status=200) + + await getattr(client, method)(value) + # --------------------------------------------------------------------------- # Debounce behavior # ---------------------------------------------------------------------------