mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 09:23:17 -04:00
Fix changes in Xbox API error response (#182100)
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
from collections.abc import Awaitable, Callable, Coroutine
|
||||
from functools import wraps
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
from typing import Any, Concatenate, override
|
||||
|
||||
@@ -191,15 +190,16 @@ class XboxMediaPlayer(XboxConsoleBaseEntity, MediaPlayerEntity):
|
||||
@override
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn the media player on."""
|
||||
try:
|
||||
await self.client.smartglass.wake_up(self._console.id)
|
||||
except HTTPStatusError as e:
|
||||
if e.response.status_code == HTTPStatus.NOT_FOUND:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="turn_on_failed",
|
||||
) from e
|
||||
raise
|
||||
if (
|
||||
err := await self.client.smartglass.wake_up(self._console.id)
|
||||
).status.error_code != "OK":
|
||||
_LOGGER.debug(
|
||||
"Xbox error: %s (%s)", err.status.error_message, err.status.error_code
|
||||
)
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="turn_on_failed",
|
||||
)
|
||||
|
||||
@exception_handler
|
||||
@override
|
||||
|
||||
@@ -3,13 +3,16 @@
|
||||
import asyncio
|
||||
from collections.abc import Awaitable, Callable, Coroutine, Iterable
|
||||
from functools import wraps
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
from typing import Any, Concatenate, override
|
||||
|
||||
from httpx import HTTPStatusError, RequestError, TimeoutException
|
||||
from pythonxbox.api.provider.smartglass import SmartglassProvider
|
||||
from pythonxbox.api.provider.smartglass.models import InputKeyType, PowerState
|
||||
from pythonxbox.api.provider.smartglass.models import (
|
||||
CommandResponse,
|
||||
InputKeyType,
|
||||
PowerState,
|
||||
)
|
||||
|
||||
from homeassistant.components.remote import (
|
||||
ATTR_DELAY_SECS,
|
||||
@@ -29,7 +32,12 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
MAP_COMMAND: dict[str, Callable[[SmartglassProvider], Callable]] = {
|
||||
MAP_COMMAND: dict[
|
||||
str,
|
||||
Callable[
|
||||
[SmartglassProvider], Callable[[str], Coroutine[Any, Any, CommandResponse]]
|
||||
],
|
||||
] = {
|
||||
"WakeUp": lambda x: x.wake_up,
|
||||
"TurnOff": lambda x: x.turn_off,
|
||||
"Reboot": lambda x: x.reboot,
|
||||
@@ -120,15 +128,16 @@ class XboxRemote(XboxConsoleBaseEntity, RemoteEntity):
|
||||
@override
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the Xbox on."""
|
||||
try:
|
||||
await self.client.smartglass.wake_up(self._console.id)
|
||||
except HTTPStatusError as e:
|
||||
if e.response.status_code == HTTPStatus.NOT_FOUND:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="turn_on_failed",
|
||||
) from e
|
||||
raise
|
||||
if (
|
||||
err := await self.client.smartglass.wake_up(self._console.id)
|
||||
).status.error_code != "OK":
|
||||
_LOGGER.debug(
|
||||
"Xbox error: %s (%s)", err.status.error_message, err.status.error_code
|
||||
)
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="turn_on_failed",
|
||||
)
|
||||
|
||||
@exception_handler
|
||||
@override
|
||||
@@ -149,9 +158,20 @@ class XboxRemote(XboxConsoleBaseEntity, RemoteEntity):
|
||||
button = InputKeyType(single_command)
|
||||
await self.client.smartglass.press_button(self._console.id, button)
|
||||
elif single_command in MAP_COMMAND:
|
||||
await MAP_COMMAND[single_command](self.client.smartglass)(
|
||||
self._console.id
|
||||
)
|
||||
if (
|
||||
err := await MAP_COMMAND[single_command](
|
||||
self.client.smartglass
|
||||
)(self._console.id)
|
||||
).status.error_code != "OK":
|
||||
_LOGGER.debug(
|
||||
"Xbox error: %s (%s)",
|
||||
err.status.error_message,
|
||||
err.status.error_code,
|
||||
)
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="command_failed",
|
||||
)
|
||||
else:
|
||||
await self.client.smartglass.insert_text(
|
||||
self._console.id, single_command.removeprefix("text:")
|
||||
|
||||
@@ -156,6 +156,9 @@
|
||||
"auth_exception": {
|
||||
"message": "Xbox Network authentication failed, re-authentication required"
|
||||
},
|
||||
"command_failed": {
|
||||
"message": "Command failed."
|
||||
},
|
||||
"media_not_found": {
|
||||
"message": "The requested media could not be found."
|
||||
},
|
||||
|
||||
@@ -10,6 +10,7 @@ from pythonxbox.api.provider.gameclips.models import GameclipsResponse
|
||||
from pythonxbox.api.provider.people.models import PeopleResponse
|
||||
from pythonxbox.api.provider.screenshots.models import ScreenshotResponse
|
||||
from pythonxbox.api.provider.smartglass.models import (
|
||||
CommandResponse,
|
||||
InstalledPackagesList,
|
||||
SmartglassConsoleList,
|
||||
SmartglassConsoleStatus,
|
||||
@@ -153,6 +154,22 @@ def mock_xbox_live_client() -> Generator[AsyncMock]:
|
||||
client.smartglass.get_installed_apps.return_value = InstalledPackagesList(
|
||||
**load_json_object_fixture("smartglass_installed_applications.json", DOMAIN)
|
||||
)
|
||||
command_response = CommandResponse(
|
||||
**load_json_object_fixture("smartglass_command_response.json", DOMAIN)
|
||||
)
|
||||
client.smartglass.wake_up.return_value = command_response
|
||||
client.smartglass.turn_off.return_value = command_response
|
||||
client.smartglass.reboot.return_value = command_response
|
||||
client.smartglass.mute.return_value = command_response
|
||||
client.smartglass.unmute.return_value = command_response
|
||||
client.smartglass.play.return_value = command_response
|
||||
client.smartglass.pause.return_value = command_response
|
||||
client.smartglass.previous.return_value = command_response
|
||||
client.smartglass.next.return_value = command_response
|
||||
client.smartglass.go_home.return_value = command_response
|
||||
client.smartglass.go_back.return_value = command_response
|
||||
client.smartglass.show_guide_tab.return_value = command_response
|
||||
client.smartglass.show_tv_guide.return_value = command_response
|
||||
|
||||
client.catalog = AsyncMock()
|
||||
client.catalog.get_product_from_alternate_id.return_value = CatalogResponse(
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"result": null,
|
||||
"uiText": null,
|
||||
"destination": {
|
||||
"id": "AAAAAAAAAAAAAAAA",
|
||||
"name": "Xbox One S",
|
||||
"powerState": "ConnectedStandby",
|
||||
"consoleType": "XboxOneS",
|
||||
"osVersion": "10.0.26100.9426"
|
||||
},
|
||||
"userInfo": null,
|
||||
"opId": "11111111111111111111111111111111",
|
||||
"status": {
|
||||
"errorCode": "OK",
|
||||
"errorMessage": null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"result": null,
|
||||
"uiText": null,
|
||||
"destination": null,
|
||||
"userInfo": null,
|
||||
"opId": "1111111111111111111111",
|
||||
"status": {
|
||||
"errorCode": "ErrorCallingWNS",
|
||||
"errorMessage": "Send command failed"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Test the Xbox media_player platform."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from http import HTTPStatus
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
@@ -9,6 +8,7 @@ from httpx import HTTPStatusError, RequestError, TimeoutException
|
||||
import pytest
|
||||
from pythonxbox.api.provider.catalog.models import CatalogResponse
|
||||
from pythonxbox.api.provider.smartglass.models import (
|
||||
CommandResponse,
|
||||
SmartglassConsoleStatus,
|
||||
VolumeDirection,
|
||||
)
|
||||
@@ -320,10 +320,10 @@ async def test_media_player_turn_on_failed(
|
||||
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
xbox_live_client.smartglass.wake_up.side_effect = (
|
||||
HTTPStatusError(
|
||||
"", request=Mock(), response=Mock(status_code=HTTPStatus.NOT_FOUND)
|
||||
),
|
||||
xbox_live_client.smartglass.wake_up.return_value = CommandResponse(
|
||||
**await async_load_json_object_fixture(
|
||||
hass, "smartglass_command_response_error.json", DOMAIN
|
||||
) # type: ignore[reportArgumentType]
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError) as e:
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
"""Test the Xbox remote platform."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from httpx import HTTPStatusError, RequestError, TimeoutException
|
||||
import pytest
|
||||
from pythonxbox.api.provider.smartglass.models import InputKeyType
|
||||
from pythonxbox.api.provider.smartglass.models import CommandResponse, InputKeyType
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.remote import (
|
||||
@@ -14,6 +13,7 @@ from homeassistant.components.remote import (
|
||||
DOMAIN as REMOTE_DOMAIN,
|
||||
SERVICE_SEND_COMMAND,
|
||||
)
|
||||
from homeassistant.components.xbox.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
ATTR_COMMAND,
|
||||
@@ -26,7 +26,12 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import Mock, MockConfigEntry, snapshot_platform
|
||||
from tests.common import (
|
||||
Mock,
|
||||
MockConfigEntry,
|
||||
async_load_json_object_fixture,
|
||||
snapshot_platform,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -262,18 +267,61 @@ async def test_send_command_exceptions(
|
||||
assert e.value.translation_key == translation_key
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("command", "call_method"),
|
||||
[
|
||||
("WakeUp", "wake_up"),
|
||||
("TurnOff", "turn_off"),
|
||||
("Reboot", "reboot"),
|
||||
("Mute", "mute"),
|
||||
("Unmute", "unmute"),
|
||||
("Play", "play"),
|
||||
("Pause", "pause"),
|
||||
("Previous", "previous"),
|
||||
("Next", "next"),
|
||||
("GoHome", "go_home"),
|
||||
("GoBack", "go_back"),
|
||||
("ShowGuideTab", "show_guide_tab"),
|
||||
("ShowGuide", "show_tv_guide"),
|
||||
],
|
||||
)
|
||||
async def test_send_command_failed(
|
||||
hass: HomeAssistant,
|
||||
xbox_live_client: AsyncMock,
|
||||
config_entry: MockConfigEntry,
|
||||
command: str,
|
||||
call_method: str,
|
||||
) -> None:
|
||||
"""Test remote send command failed error."""
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
getattr(xbox_live_client.smartglass, call_method).return_value = CommandResponse(
|
||||
**await async_load_json_object_fixture(
|
||||
hass, "smartglass_command_response_error.json", DOMAIN
|
||||
) # type: ignore[reportArgumentType]
|
||||
)
|
||||
with pytest.raises(HomeAssistantError) as e:
|
||||
await hass.services.async_call(
|
||||
REMOTE_DOMAIN,
|
||||
SERVICE_SEND_COMMAND,
|
||||
{ATTR_COMMAND: command, ATTR_DELAY_SECS: 0},
|
||||
target={ATTR_ENTITY_ID: "remote.xone"},
|
||||
blocking=True,
|
||||
)
|
||||
assert e.value.translation_key == "command_failed"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "translation_key"),
|
||||
[
|
||||
(TimeoutException(""), "timeout_exception"),
|
||||
(RequestError("", request=Mock()), "request_exception"),
|
||||
(HTTPStatusError("", request=Mock(), response=Mock()), "request_exception"),
|
||||
(
|
||||
HTTPStatusError(
|
||||
"", request=Mock(), response=Mock(status_code=HTTPStatus.NOT_FOUND)
|
||||
),
|
||||
"turn_on_failed",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_turn_on_exceptions(
|
||||
@@ -292,6 +340,7 @@ async def test_turn_on_exceptions(
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
xbox_live_client.smartglass.wake_up.side_effect = exception
|
||||
|
||||
with pytest.raises(HomeAssistantError) as e:
|
||||
await hass.services.async_call(
|
||||
REMOTE_DOMAIN,
|
||||
@@ -302,6 +351,34 @@ async def test_turn_on_exceptions(
|
||||
assert e.value.translation_key == translation_key
|
||||
|
||||
|
||||
async def test_turn_on_failed(
|
||||
hass: HomeAssistant,
|
||||
xbox_live_client: AsyncMock,
|
||||
config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test remote turn on exceptions."""
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
xbox_live_client.smartglass.wake_up.return_value = CommandResponse(
|
||||
**await async_load_json_object_fixture(
|
||||
hass, "smartglass_command_response_error.json", DOMAIN
|
||||
) # type: ignore[reportArgumentType]
|
||||
)
|
||||
with pytest.raises(HomeAssistantError) as e:
|
||||
await hass.services.async_call(
|
||||
REMOTE_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
target={ATTR_ENTITY_ID: "remote.xone"},
|
||||
blocking=True,
|
||||
)
|
||||
assert e.value.translation_key == "turn_on_failed"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "translation_key"),
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user