mirror of
https://github.com/home-assistant/core.git
synced 2025-07-29 08:07:45 +00:00
Compare commits
6 Commits
f9bb7e404e
...
e7994b3da1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e7994b3da1 | ||
![]() |
b88bf74e13 | ||
![]() |
8355727eb1 | ||
![]() |
c7290908cc | ||
![]() |
1307cd4b10 | ||
![]() |
c0b2193718 |
@ -104,7 +104,7 @@ class LazyState(State):
|
||||
return self._last_updated_ts
|
||||
|
||||
@cached_property
|
||||
def last_changed_timestamp(self) -> float: # type: ignore[override]
|
||||
def last_changed_timestamp(self) -> float:
|
||||
"""Last changed timestamp."""
|
||||
ts = self._last_changed_ts or self._last_updated_ts
|
||||
if TYPE_CHECKING:
|
||||
@ -112,7 +112,7 @@ class LazyState(State):
|
||||
return ts
|
||||
|
||||
@cached_property
|
||||
def last_reported_timestamp(self) -> float: # type: ignore[override]
|
||||
def last_reported_timestamp(self) -> float:
|
||||
"""Last reported timestamp."""
|
||||
ts = self._last_reported_ts or self._last_updated_ts
|
||||
if TYPE_CHECKING:
|
||||
|
@ -22,34 +22,34 @@ from homeassistant.helpers.network import is_internal_request
|
||||
from .const import UNPLAYABLE_TYPES
|
||||
|
||||
LIBRARY = [
|
||||
"Favorites",
|
||||
"Artists",
|
||||
"Albums",
|
||||
"Tracks",
|
||||
"Playlists",
|
||||
"Genres",
|
||||
"New Music",
|
||||
"Album Artists",
|
||||
"Apps",
|
||||
"Radios",
|
||||
"favorites",
|
||||
"artists",
|
||||
"albums",
|
||||
"tracks",
|
||||
"playlists",
|
||||
"genres",
|
||||
"new music",
|
||||
"album artists",
|
||||
"apps",
|
||||
"radios",
|
||||
]
|
||||
|
||||
MEDIA_TYPE_TO_SQUEEZEBOX: dict[str | MediaType, str] = {
|
||||
"Favorites": "favorites",
|
||||
"Artists": "artists",
|
||||
"Albums": "albums",
|
||||
"Tracks": "titles",
|
||||
"Playlists": "playlists",
|
||||
"Genres": "genres",
|
||||
"New Music": "new music",
|
||||
"Album Artists": "album artists",
|
||||
"favorites": "favorites",
|
||||
"artists": "artists",
|
||||
"albums": "albums",
|
||||
"tracks": "titles",
|
||||
"playlists": "playlists",
|
||||
"genres": "genres",
|
||||
"new music": "new music",
|
||||
"album artists": "album artists",
|
||||
MediaType.ALBUM: "album",
|
||||
MediaType.ARTIST: "artist",
|
||||
MediaType.TRACK: "title",
|
||||
MediaType.PLAYLIST: "playlist",
|
||||
MediaType.GENRE: "genre",
|
||||
"Apps": "apps",
|
||||
"Radios": "radios",
|
||||
MediaType.APPS: "apps",
|
||||
"radios": "radios",
|
||||
}
|
||||
|
||||
SQUEEZEBOX_ID_BY_TYPE: dict[str | MediaType, str] = {
|
||||
@ -58,22 +58,20 @@ SQUEEZEBOX_ID_BY_TYPE: dict[str | MediaType, str] = {
|
||||
MediaType.TRACK: "track_id",
|
||||
MediaType.PLAYLIST: "playlist_id",
|
||||
MediaType.GENRE: "genre_id",
|
||||
"Favorites": "item_id",
|
||||
"favorites": "item_id",
|
||||
MediaType.APPS: "item_id",
|
||||
}
|
||||
|
||||
CONTENT_TYPE_MEDIA_CLASS: dict[str | MediaType, dict[str, MediaClass | str]] = {
|
||||
"Favorites": {"item": MediaClass.DIRECTORY, "children": MediaClass.TRACK},
|
||||
"Apps": {"item": MediaClass.DIRECTORY, "children": MediaClass.APP},
|
||||
"Radios": {"item": MediaClass.DIRECTORY, "children": MediaClass.APP},
|
||||
"App": {"item": MediaClass.DIRECTORY, "children": MediaClass.TRACK},
|
||||
"Artists": {"item": MediaClass.DIRECTORY, "children": MediaClass.ARTIST},
|
||||
"Albums": {"item": MediaClass.DIRECTORY, "children": MediaClass.ALBUM},
|
||||
"Tracks": {"item": MediaClass.DIRECTORY, "children": MediaClass.TRACK},
|
||||
"Playlists": {"item": MediaClass.DIRECTORY, "children": MediaClass.PLAYLIST},
|
||||
"Genres": {"item": MediaClass.DIRECTORY, "children": MediaClass.GENRE},
|
||||
"New Music": {"item": MediaClass.DIRECTORY, "children": MediaClass.ALBUM},
|
||||
"Album Artists": {"item": MediaClass.DIRECTORY, "children": MediaClass.ARTIST},
|
||||
"favorites": {"item": MediaClass.DIRECTORY, "children": MediaClass.TRACK},
|
||||
"radios": {"item": MediaClass.DIRECTORY, "children": MediaClass.APP},
|
||||
"artists": {"item": MediaClass.DIRECTORY, "children": MediaClass.ARTIST},
|
||||
"albums": {"item": MediaClass.DIRECTORY, "children": MediaClass.ALBUM},
|
||||
"tracks": {"item": MediaClass.DIRECTORY, "children": MediaClass.TRACK},
|
||||
"playlists": {"item": MediaClass.DIRECTORY, "children": MediaClass.PLAYLIST},
|
||||
"genres": {"item": MediaClass.DIRECTORY, "children": MediaClass.GENRE},
|
||||
"new music": {"item": MediaClass.DIRECTORY, "children": MediaClass.ALBUM},
|
||||
"album artists": {"item": MediaClass.DIRECTORY, "children": MediaClass.ARTIST},
|
||||
MediaType.ALBUM: {"item": MediaClass.ALBUM, "children": MediaClass.TRACK},
|
||||
MediaType.ARTIST: {"item": MediaClass.ARTIST, "children": MediaClass.ALBUM},
|
||||
MediaType.TRACK: {"item": MediaClass.TRACK, "children": ""},
|
||||
@ -91,17 +89,15 @@ CONTENT_TYPE_TO_CHILD_TYPE: dict[
|
||||
MediaType.PLAYLIST: MediaType.PLAYLIST,
|
||||
MediaType.ARTIST: MediaType.ALBUM,
|
||||
MediaType.GENRE: MediaType.ARTIST,
|
||||
"Artists": MediaType.ARTIST,
|
||||
"Albums": MediaType.ALBUM,
|
||||
"Tracks": MediaType.TRACK,
|
||||
"Playlists": MediaType.PLAYLIST,
|
||||
"Genres": MediaType.GENRE,
|
||||
"Favorites": None, # can only be determined after inspecting the item
|
||||
"Apps": MediaClass.APP,
|
||||
"Radios": MediaClass.APP,
|
||||
"App": None, # can only be determined after inspecting the item
|
||||
"New Music": MediaType.ALBUM,
|
||||
"Album Artists": MediaType.ARTIST,
|
||||
"artists": MediaType.ARTIST,
|
||||
"albums": MediaType.ALBUM,
|
||||
"tracks": MediaType.TRACK,
|
||||
"playlists": MediaType.PLAYLIST,
|
||||
"genres": MediaType.GENRE,
|
||||
"favorites": None, # can only be determined after inspecting the item
|
||||
"radios": MediaClass.APP,
|
||||
"new music": MediaType.ALBUM,
|
||||
"album artists": MediaType.ARTIST,
|
||||
MediaType.APPS: MediaType.APP,
|
||||
MediaType.APP: MediaType.TRACK,
|
||||
}
|
||||
@ -173,7 +169,7 @@ def _build_response_known_app(
|
||||
|
||||
|
||||
def _build_response_favorites(item: dict[str, Any]) -> BrowseMedia:
|
||||
"""Build item for Favorites."""
|
||||
"""Build item for favorites."""
|
||||
if "album_id" in item:
|
||||
return BrowseMedia(
|
||||
media_content_id=str(item["album_id"]),
|
||||
@ -183,21 +179,21 @@ def _build_response_favorites(item: dict[str, Any]) -> BrowseMedia:
|
||||
can_expand=True,
|
||||
can_play=True,
|
||||
)
|
||||
if item["hasitems"] and not item["isaudio"]:
|
||||
if item.get("hasitems") and not item.get("isaudio"):
|
||||
return BrowseMedia(
|
||||
media_content_id=item["id"],
|
||||
title=item["title"],
|
||||
media_content_type="Favorites",
|
||||
media_class=CONTENT_TYPE_MEDIA_CLASS["Favorites"]["item"],
|
||||
media_content_type="favorites",
|
||||
media_class=CONTENT_TYPE_MEDIA_CLASS["favorites"]["item"],
|
||||
can_expand=True,
|
||||
can_play=False,
|
||||
)
|
||||
return BrowseMedia(
|
||||
media_content_id=item["id"],
|
||||
title=item["title"],
|
||||
media_content_type="Favorites",
|
||||
media_content_type="favorites",
|
||||
media_class=CONTENT_TYPE_MEDIA_CLASS[MediaType.TRACK]["item"],
|
||||
can_expand=item["hasitems"],
|
||||
can_expand=bool(item.get("hasitems")),
|
||||
can_play=bool(item["isaudio"] and item.get("url")),
|
||||
)
|
||||
|
||||
@ -220,7 +216,7 @@ def _get_item_thumbnail(
|
||||
item_type, item["id"], artwork_track_id
|
||||
)
|
||||
|
||||
elif search_type in ["Apps", "Radios"]:
|
||||
elif search_type in ["apps", "radios"]:
|
||||
item_thumbnail = player.generate_image_url(item["icon"])
|
||||
if item_thumbnail is None:
|
||||
item_thumbnail = item.get("image_url") # will not be proxied by HA
|
||||
@ -265,10 +261,10 @@ async def build_item_response(
|
||||
for item in result["items"]:
|
||||
# Force the item id to a string in case it's numeric from some lms
|
||||
item["id"] = str(item.get("id", ""))
|
||||
if search_type == "Favorites":
|
||||
if search_type == "favorites":
|
||||
child_media = _build_response_favorites(item)
|
||||
|
||||
elif search_type in ["Apps", "Radios"]:
|
||||
elif search_type in ["apps", "radios"]:
|
||||
# item["cmd"] contains the name of the command to use with the cli for the app
|
||||
# add the command to the dictionaries
|
||||
if item["title"] == "Search" or item.get("type") in UNPLAYABLE_TYPES:
|
||||
@ -364,11 +360,11 @@ async def library_payload(
|
||||
assert media_class["children"] is not None
|
||||
library_info["children"].append(
|
||||
BrowseMedia(
|
||||
title=item,
|
||||
title=item.title(),
|
||||
media_class=media_class["children"],
|
||||
media_content_id=item,
|
||||
media_content_type=item,
|
||||
can_play=item not in ["Favorites", "Apps", "Radios"],
|
||||
can_play=item not in ["favorites", "apps", "radios"],
|
||||
can_expand=True,
|
||||
)
|
||||
)
|
||||
|
@ -446,6 +446,9 @@ class SqueezeBoxMediaPlayerEntity(SqueezeboxEntity, MediaPlayerEntity):
|
||||
"""Send the play_media command to the media player."""
|
||||
index = None
|
||||
|
||||
if media_type:
|
||||
media_type = media_type.lower()
|
||||
|
||||
enqueue: MediaPlayerEnqueue | None = kwargs.get(ATTR_MEDIA_ENQUEUE)
|
||||
|
||||
if enqueue == MediaPlayerEnqueue.ADD:
|
||||
@ -617,6 +620,9 @@ class SqueezeBoxMediaPlayerEntity(SqueezeboxEntity, MediaPlayerEntity):
|
||||
media_content_id,
|
||||
)
|
||||
|
||||
if media_content_type:
|
||||
media_content_type = media_content_type.lower()
|
||||
|
||||
if media_content_type in [None, "library"]:
|
||||
return await library_payload(self.hass, self._player, self._browse_data)
|
||||
|
||||
|
92
homeassistant/components/whirlpool/quality_scale.yaml
Normal file
92
homeassistant/components/whirlpool/quality_scale.yaml
Normal file
@ -0,0 +1,92 @@
|
||||
rules:
|
||||
# Bronze
|
||||
action-setup:
|
||||
status: exempt
|
||||
comment: |
|
||||
The integration does not provide any additional actions.
|
||||
appropriate-polling: done
|
||||
brands: done
|
||||
common-modules: done
|
||||
config-flow-test-coverage: done
|
||||
config-flow: done
|
||||
dependency-transparency: done
|
||||
docs-actions:
|
||||
status: exempt
|
||||
comment: |
|
||||
This integration does not provide additional actions.
|
||||
docs-high-level-description: done
|
||||
docs-installation-instructions: done
|
||||
docs-removal-instructions: done
|
||||
entity-event-setup: done
|
||||
entity-unique-id: done
|
||||
has-entity-name: done
|
||||
runtime-data: done
|
||||
test-before-configure: done
|
||||
test-before-setup:
|
||||
status: todo
|
||||
comment: |
|
||||
When fetch_appliances fails, ConfigEntryNotReady should be raised.
|
||||
unique-config-entry: done
|
||||
# Silver
|
||||
action-exceptions:
|
||||
status: todo
|
||||
comment: |
|
||||
- The calls to the api can be changed to return bool, and services can then raise HomeAssistantError
|
||||
- Current services raise ValueError and should raise ServiceValidationError instead.
|
||||
config-entry-unloading: done
|
||||
docs-configuration-parameters:
|
||||
status: exempt
|
||||
comment: Integration has no configuration parameters
|
||||
docs-installation-parameters: todo
|
||||
entity-unavailable: done
|
||||
integration-owner: done
|
||||
log-when-unavailable: todo
|
||||
parallel-updates: todo
|
||||
reauthentication-flow: done
|
||||
test-coverage:
|
||||
status: todo
|
||||
comment: |
|
||||
- Test helper init_integration() does not set a unique_id
|
||||
- Merge test_setup_http_exception and test_setup_auth_account_locked
|
||||
- The climate platform is at 94%
|
||||
|
||||
# Gold
|
||||
devices: done
|
||||
diagnostics: done
|
||||
discovery-update-info:
|
||||
status: exempt
|
||||
comment: |
|
||||
This integration is a cloud service and thus does not support discovery.
|
||||
discovery:
|
||||
status: exempt
|
||||
comment: |
|
||||
This integration is a cloud service and thus does not support discovery.
|
||||
docs-data-update: todo
|
||||
docs-examples: todo
|
||||
docs-known-limitations: todo
|
||||
docs-supported-devices: done
|
||||
docs-supported-functions: done
|
||||
docs-troubleshooting: todo
|
||||
docs-use-cases: todo
|
||||
dynamic-devices: todo
|
||||
entity-category: done
|
||||
entity-device-class:
|
||||
status: todo
|
||||
comment: The "unknown" state should not be part of the enum for the dispense level sensor.
|
||||
entity-disabled-by-default: done
|
||||
entity-translations: done
|
||||
exception-translations: todo
|
||||
icon-translations:
|
||||
status: todo
|
||||
comment: |
|
||||
Time remaining sensor still has hardcoded icon.
|
||||
reconfiguration-flow: todo
|
||||
repair-issues:
|
||||
status: exempt
|
||||
comment: No known use cases for repair issues or flows, yet
|
||||
stale-devices: todo
|
||||
|
||||
# Platinum
|
||||
async-dependency: done
|
||||
inject-websession: done
|
||||
strict-typing: todo
|
@ -1072,7 +1072,7 @@ class TemplateStateBase(State):
|
||||
raise KeyError
|
||||
|
||||
@under_cached_property
|
||||
def entity_id(self) -> str: # type: ignore[override]
|
||||
def entity_id(self) -> str:
|
||||
"""Wrap State.entity_id.
|
||||
|
||||
Intentionally does not collect state
|
||||
@ -1128,7 +1128,7 @@ class TemplateStateBase(State):
|
||||
return self._state.object_id
|
||||
|
||||
@property
|
||||
def name(self) -> str: # type: ignore[override]
|
||||
def name(self) -> str:
|
||||
"""Wrap State.name."""
|
||||
self._collect_state()
|
||||
return self._state.name
|
||||
|
@ -10,9 +10,10 @@
|
||||
astroid==3.3.9
|
||||
coverage==7.6.12
|
||||
freezegun==1.5.1
|
||||
go2rtc-client==0.1.2
|
||||
license-expression==30.4.1
|
||||
mock-open==1.4.0
|
||||
mypy-dev==1.16.0a7
|
||||
mypy-dev==1.16.0a8
|
||||
pre-commit==4.0.0
|
||||
pydantic==2.11.3
|
||||
pylint==3.3.6
|
||||
|
@ -1100,7 +1100,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [
|
||||
"weatherkit",
|
||||
"webmin",
|
||||
"wemo",
|
||||
"whirlpool",
|
||||
"whois",
|
||||
"wiffi",
|
||||
"wilight",
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Lamarzocco session fixtures."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from bleak.backends.device import BLEDevice
|
||||
from pylamarzocco.const import ModelName
|
||||
@ -22,15 +22,6 @@ from . import SERIAL_DICT, USER_INPUT, async_init_integration
|
||||
from tests.common import MockConfigEntry, load_json_object_fixture
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.lamarzocco.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry(
|
||||
hass: HomeAssistant, mock_lamarzocco: MagicMock
|
||||
|
@ -18,7 +18,6 @@ from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_plat
|
||||
|
||||
async def test_binary_sensors(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
|
@ -27,6 +27,15 @@ from . import USER_INPUT, async_init_integration, get_bluetooth_service_info
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.lamarzocco.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
||||
|
||||
async def __do_successful_user_step(
|
||||
hass: HomeAssistant, result: ConfigFlowResult, mock_cloud_client: MagicMock
|
||||
) -> ConfigFlowResult:
|
||||
@ -47,25 +56,24 @@ async def __do_sucessful_machine_selection_step(
|
||||
) -> None:
|
||||
"""Successfully configure the machine selection step."""
|
||||
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{CONF_MACHINE: "GS012345"},
|
||||
)
|
||||
|
||||
assert result3["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
assert result3["title"] == "GS012345"
|
||||
assert result3["data"] == {
|
||||
assert result["title"] == "GS012345"
|
||||
assert result["data"] == {
|
||||
**USER_INPUT,
|
||||
CONF_TOKEN: None,
|
||||
}
|
||||
assert result3["result"].unique_id == "GS012345"
|
||||
assert result["result"].unique_id == "GS012345"
|
||||
|
||||
|
||||
async def test_form(
|
||||
hass: HomeAssistant,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_setup_entry: Generator[AsyncMock],
|
||||
) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@ -75,13 +83,12 @@ async def test_form(
|
||||
assert result["errors"] == {}
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result2 = await __do_successful_user_step(hass, result, mock_cloud_client)
|
||||
await __do_sucessful_machine_selection_step(hass, result2)
|
||||
result = await __do_successful_user_step(hass, result, mock_cloud_client)
|
||||
await __do_sucessful_machine_selection_step(hass, result)
|
||||
|
||||
|
||||
async def test_form_abort_already_configured(
|
||||
hass: HomeAssistant,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test we abort if already configured."""
|
||||
@ -93,25 +100,25 @@ async def test_form_abort_already_configured(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
USER_INPUT,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["step_id"] == "machine_selection"
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "machine_selection"
|
||||
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_MACHINE: "GS012345",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result3["type"] is FlowResultType.ABORT
|
||||
assert result3["reason"] == "already_configured"
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -124,7 +131,6 @@ async def test_form_abort_already_configured(
|
||||
async def test_form_invalid_auth(
|
||||
hass: HomeAssistant,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_setup_entry: Generator[AsyncMock],
|
||||
side_effect: Exception,
|
||||
error: str,
|
||||
) -> None:
|
||||
@ -135,25 +141,24 @@ async def test_form_invalid_auth(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
USER_INPUT,
|
||||
)
|
||||
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["errors"] == {"base": error}
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": error}
|
||||
assert len(mock_cloud_client.list_things.mock_calls) == 1
|
||||
|
||||
# test recovery from failure
|
||||
mock_cloud_client.list_things.side_effect = None
|
||||
result2 = await __do_successful_user_step(hass, result, mock_cloud_client)
|
||||
await __do_sucessful_machine_selection_step(hass, result2)
|
||||
result = await __do_successful_user_step(hass, result, mock_cloud_client)
|
||||
await __do_sucessful_machine_selection_step(hass, result)
|
||||
|
||||
|
||||
async def test_form_no_machines(
|
||||
hass: HomeAssistant,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_setup_entry: Generator[AsyncMock],
|
||||
) -> None:
|
||||
"""Test we don't have any devices."""
|
||||
|
||||
@ -164,20 +169,20 @@ async def test_form_no_machines(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
USER_INPUT,
|
||||
)
|
||||
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["errors"] == {"base": "no_machines"}
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "no_machines"}
|
||||
assert len(mock_cloud_client.list_things.mock_calls) == 1
|
||||
|
||||
# test recovery from failure
|
||||
mock_cloud_client.list_things.return_value = original_return
|
||||
|
||||
result2 = await __do_successful_user_step(hass, result, mock_cloud_client)
|
||||
await __do_sucessful_machine_selection_step(hass, result2)
|
||||
result = await __do_successful_user_step(hass, result, mock_cloud_client)
|
||||
await __do_sucessful_machine_selection_step(hass, result)
|
||||
|
||||
|
||||
async def test_reauth_flow(
|
||||
@ -194,14 +199,14 @@ async def test_reauth_flow(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "new_password"},
|
||||
)
|
||||
|
||||
assert result2["type"] is FlowResultType.ABORT
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
await hass.async_block_till_done()
|
||||
assert result2["reason"] == "reauth_successful"
|
||||
assert result["reason"] == "reauth_successful"
|
||||
assert len(mock_cloud_client.list_things.mock_calls) == 1
|
||||
assert mock_config_entry.data[CONF_PASSWORD] == "new_password"
|
||||
|
||||
@ -210,7 +215,6 @@ async def test_reconfigure_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_setup_entry: Generator[AsyncMock],
|
||||
) -> None:
|
||||
"""Testing reconfgure flow."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
@ -220,7 +224,7 @@ async def test_reconfigure_flow(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "reconfigure"
|
||||
|
||||
result2 = await __do_successful_user_step(hass, result, mock_cloud_client)
|
||||
result = await __do_successful_user_step(hass, result, mock_cloud_client)
|
||||
service_info = get_bluetooth_service_info(ModelName.GS3_MP, "GS012345")
|
||||
|
||||
with (
|
||||
@ -229,24 +233,24 @@ async def test_reconfigure_flow(
|
||||
return_value=[service_info],
|
||||
),
|
||||
):
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_MACHINE: "GS012345",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result3["type"] is FlowResultType.FORM
|
||||
assert result3["step_id"] == "bluetooth_selection"
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "bluetooth_selection"
|
||||
|
||||
result4 = await hass.config_entries.flow.async_configure(
|
||||
result3["flow_id"],
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_MAC: service_info.address},
|
||||
)
|
||||
|
||||
assert result4["type"] is FlowResultType.ABORT
|
||||
assert result4["reason"] == "reconfigure_successful"
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
|
||||
assert mock_config_entry.title == "My LaMarzocco"
|
||||
assert mock_config_entry.data == {
|
||||
@ -259,7 +263,6 @@ async def test_bluetooth_discovery(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_setup_entry: Generator[AsyncMock],
|
||||
) -> None:
|
||||
"""Test bluetooth discovery."""
|
||||
service_info = get_bluetooth_service_info(
|
||||
@ -274,14 +277,14 @@ async def test_bluetooth_discovery(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
USER_INPUT,
|
||||
)
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
assert result2["title"] == "GS012345"
|
||||
assert result2["data"] == {
|
||||
assert result["title"] == "GS012345"
|
||||
assert result["data"] == {
|
||||
**USER_INPUT,
|
||||
CONF_MAC: "aa:bb:cc:dd:ee:ff",
|
||||
CONF_TOKEN: "dummyToken",
|
||||
@ -291,8 +294,6 @@ async def test_bluetooth_discovery(
|
||||
async def test_bluetooth_discovery_already_configured(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_setup_entry: Generator[AsyncMock],
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test bluetooth discovery."""
|
||||
@ -312,7 +313,6 @@ async def test_bluetooth_discovery_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_setup_entry: Generator[AsyncMock],
|
||||
) -> None:
|
||||
"""Test bluetooth discovery errors."""
|
||||
service_info = get_bluetooth_service_info(
|
||||
@ -330,24 +330,24 @@ async def test_bluetooth_discovery_errors(
|
||||
original_return = deepcopy(mock_cloud_client.list_things.return_value)
|
||||
mock_cloud_client.list_things.return_value[0].serial_number = "GS98765"
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
USER_INPUT,
|
||||
)
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["errors"] == {"base": "machine_not_found"}
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "machine_not_found"}
|
||||
assert len(mock_cloud_client.list_things.mock_calls) == 1
|
||||
|
||||
mock_cloud_client.list_things.return_value = original_return
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
USER_INPUT,
|
||||
)
|
||||
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
assert result2["title"] == "GS012345"
|
||||
assert result2["data"] == {
|
||||
assert result["title"] == "GS012345"
|
||||
assert result["data"] == {
|
||||
**USER_INPUT,
|
||||
CONF_MAC: "aa:bb:cc:dd:ee:ff",
|
||||
CONF_TOKEN: None,
|
||||
@ -357,8 +357,6 @@ async def test_bluetooth_discovery_errors(
|
||||
async def test_dhcp_discovery(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_setup_entry: Generator[AsyncMock],
|
||||
) -> None:
|
||||
"""Test dhcp discovery."""
|
||||
|
||||
@ -375,12 +373,12 @@ async def test_dhcp_discovery(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
USER_INPUT,
|
||||
)
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["data"] == {
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
**USER_INPUT,
|
||||
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
|
||||
CONF_TOKEN: None,
|
||||
@ -389,8 +387,6 @@ async def test_dhcp_discovery(
|
||||
|
||||
async def test_dhcp_discovery_abort_on_hostname_changed(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test dhcp discovery aborts when hostname was changed manually."""
|
||||
@ -411,7 +407,6 @@ async def test_dhcp_discovery_abort_on_hostname_changed(
|
||||
async def test_dhcp_already_configured_and_update(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test discovered IP address change."""
|
||||
@ -436,9 +431,7 @@ async def test_dhcp_already_configured_and_update(
|
||||
|
||||
async def test_options_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_setup_entry: Generator[AsyncMock],
|
||||
) -> None:
|
||||
"""Test options flow."""
|
||||
await async_init_integration(hass, mock_config_entry)
|
||||
@ -449,7 +442,7 @@ async def test_options_flow(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result2 = await hass.config_entries.options.async_configure(
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_USE_BLUETOOTH: False,
|
||||
@ -457,7 +450,7 @@ async def test_options_flow(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["data"] == {
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
CONF_USE_BLUETOOTH: False,
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ from tests.common import MockConfigEntry
|
||||
async def test_load_unload_config_entry(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_lamarzocco: MagicMock,
|
||||
) -> None:
|
||||
"""Test loading and unloading the integration."""
|
||||
await async_init_integration(hass, mock_config_entry)
|
||||
@ -111,7 +110,6 @@ async def test_invalid_auth(
|
||||
|
||||
async def test_v1_migration_fails(
|
||||
hass: HomeAssistant,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_lamarzocco: MagicMock,
|
||||
) -> None:
|
||||
"""Test v1 -> v2 Migration."""
|
||||
@ -131,7 +129,6 @@ async def test_v1_migration_fails(
|
||||
|
||||
async def test_v2_migration(
|
||||
hass: HomeAssistant,
|
||||
mock_cloud_client: MagicMock,
|
||||
mock_lamarzocco: MagicMock,
|
||||
) -> None:
|
||||
"""Test v2 -> v3 Migration."""
|
||||
@ -256,7 +253,6 @@ async def test_websocket_closed_on_unload(
|
||||
async def test_gateway_version_issue(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_cloud_client: MagicMock,
|
||||
version: str,
|
||||
issue_exists: bool,
|
||||
|
@ -25,7 +25,6 @@ from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
async def test_switches(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
|
@ -19,7 +19,6 @@ from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
async def test_update(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
|
@ -65,21 +65,21 @@ async def test_async_browse_media_root(
|
||||
assert response["success"]
|
||||
result = response["result"]
|
||||
for idx, item in enumerate(result["children"]):
|
||||
assert item["title"] == LIBRARY[idx]
|
||||
assert item["title"].lower() == LIBRARY[idx]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("category", "child_count"),
|
||||
[
|
||||
("Favorites", 4),
|
||||
("Artists", 4),
|
||||
("Albums", 4),
|
||||
("Playlists", 4),
|
||||
("Genres", 4),
|
||||
("New Music", 4),
|
||||
("Album Artists", 4),
|
||||
("Apps", 3),
|
||||
("Radios", 3),
|
||||
("favorites", 4),
|
||||
("artists", 4),
|
||||
("albums", 4),
|
||||
("playlists", 4),
|
||||
("genres", 4),
|
||||
("new music", 4),
|
||||
("album artists", 4),
|
||||
("apps", 3),
|
||||
("radios", 3),
|
||||
],
|
||||
)
|
||||
async def test_async_browse_media_with_subitems(
|
||||
|
@ -3,6 +3,7 @@
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
from whirlpool.washerdryer import MachineState
|
||||
@ -58,6 +59,7 @@ async def test_washer_dryer_time_sensor(
|
||||
entity_id: str,
|
||||
mock_fixture: str,
|
||||
request: pytest.FixtureRequest,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test Washer/Dryer end time sensors."""
|
||||
now = utcnow()
|
||||
@ -113,7 +115,8 @@ async def test_washer_dryer_time_sensor(
|
||||
|
||||
# Test that periodic updates call the API to fetch data
|
||||
mock_instance.fetch_data.reset_mock()
|
||||
async_fire_time_changed(hass, utcnow() + SCAN_INTERVAL)
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
mock_instance.fetch_data.assert_called_once()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user