Compare commits

...

6 Commits

Author SHA1 Message Date
Abílio Costa
e7994b3da1
Fix missing go2rtc dependency in non-docker setups (#143172) 2025-04-17 10:03:47 -10:00
Josef Zweck
b88bf74e13
Cleanup lamarzocco tests (#143176) 2025-04-17 20:53:47 +02:00
peteS-UK
8355727eb1
Fix for media content type case in Squeezebox (#143099) 2025-04-17 18:56:28 +02:00
Marc Mueller
c7290908cc
Update mypy-dev 1.16.0a8 (#143166) 2025-04-17 18:13:00 +02:00
Abílio Costa
1307cd4b10
Add bronze quality scale for Whirlpool (#142752) 2025-04-17 15:31:12 +01:00
Abílio Costa
c0b2193718
Use freezer for time change in Whirlpool config flow test (#143162) 2025-04-17 16:14:21 +02:00
15 changed files with 231 additions and 157 deletions

View File

@ -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:

View File

@ -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,
)
)

View File

@ -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)

View 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

View File

@ -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

View File

@ -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

View File

@ -1100,7 +1100,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [
"weatherkit",
"webmin",
"wemo",
"whirlpool",
"whois",
"wiffi",
"wilight",

View File

@ -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

View File

@ -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,

View File

@ -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,
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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(

View File

@ -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()