From a5eda3faf130d725e34a18daa4b47ec2b8d073c3 Mon Sep 17 00:00:00 2001 From: Regev Brody Date: Mon, 3 Feb 2025 18:00:36 +0200 Subject: [PATCH 01/10] Bump python-roborock to 2.11.1 (#137244) --- homeassistant/components/roborock/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/roborock/manifest.json b/homeassistant/components/roborock/manifest.json index 76d7ab98a34..db2654d4baa 100644 --- a/homeassistant/components/roborock/manifest.json +++ b/homeassistant/components/roborock/manifest.json @@ -7,7 +7,7 @@ "iot_class": "local_polling", "loggers": ["roborock"], "requirements": [ - "python-roborock==2.9.7", + "python-roborock==2.11.1", "vacuum-map-parser-roborock==0.1.2" ] } diff --git a/requirements_all.txt b/requirements_all.txt index 18ebb5d4a09..505d9351f68 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2449,7 +2449,7 @@ python-rabbitair==0.0.8 python-ripple-api==0.0.3 # homeassistant.components.roborock -python-roborock==2.9.7 +python-roborock==2.11.1 # homeassistant.components.smarttub python-smarttub==0.0.38 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 575e6f6b404..0a1e1a7433d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1982,7 +1982,7 @@ python-picnic-api==1.1.0 python-rabbitair==0.0.8 # homeassistant.components.roborock -python-roborock==2.9.7 +python-roborock==2.11.1 # homeassistant.components.smarttub python-smarttub==0.0.38 From 30af9057d1a738dca53a835cb0e2e0861dfd8b86 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 3 Feb 2025 17:06:02 +0100 Subject: [PATCH 02/10] Ensure random temp dir is used during MQTT CI tests (#137221) --- tests/components/mqtt/conftest.py | 2 +- tests/components/mqtt/test_util.py | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/components/mqtt/conftest.py b/tests/components/mqtt/conftest.py index 2a1e4012f51..87bbcecebe5 100644 --- a/tests/components/mqtt/conftest.py +++ b/tests/components/mqtt/conftest.py @@ -38,7 +38,7 @@ def temp_dir_prefix() -> str: return "test" -@pytest.fixture +@pytest.fixture(autouse=True) def mock_temp_dir(temp_dir_prefix: str) -> Generator[str]: """Mock the certificate temp directory.""" with patch( diff --git a/tests/components/mqtt/test_util.py b/tests/components/mqtt/test_util.py index dd72902056d..df91764b0fb 100644 --- a/tests/components/mqtt/test_util.py +++ b/tests/components/mqtt/test_util.py @@ -4,7 +4,6 @@ import asyncio from collections.abc import Callable from datetime import timedelta from pathlib import Path -from random import getrandbits import shutil import tempfile from unittest.mock import MagicMock, patch @@ -199,7 +198,6 @@ async def test_reading_non_exitisting_certificate_file() -> None: ) -@pytest.mark.parametrize("temp_dir_prefix", "unknown") async def test_return_default_get_file_path( hass: HomeAssistant, mock_temp_dir: str ) -> None: @@ -211,12 +209,8 @@ async def test_return_default_get_file_path( and mqtt.util.get_file_path("some_option", "mydefault") == "mydefault" ) - with patch( - "homeassistant.components.mqtt.util.TEMP_DIR_NAME", - f"home-assistant-mqtt-other-{getrandbits(10):03x}", - ) as temp_dir_name: - tempdir = Path(tempfile.gettempdir()) / temp_dir_name - assert await hass.async_add_executor_job(_get_file_path, tempdir) + temp_dir = Path(tempfile.gettempdir()) / mock_temp_dir + assert await hass.async_add_executor_job(_get_file_path, temp_dir) async def test_waiting_for_client_not_loaded( From 9856340a338039d3b030395ccfa71ece41d5dbd0 Mon Sep 17 00:00:00 2001 From: Aaron Godfrey Date: Mon, 3 Feb 2025 08:06:21 -0800 Subject: [PATCH 03/10] Bump todist-api-python to 2.1.7 (#136549) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Allen Porter Co-authored-by: J. Diego Rodríguez Royo --- homeassistant/components/todoist/calendar.py | 5 ++--- homeassistant/components/todoist/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/todoist/conftest.py | 2 ++ 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/todoist/calendar.py b/homeassistant/components/todoist/calendar.py index 94581439ae9..8c61394d300 100644 --- a/homeassistant/components/todoist/calendar.py +++ b/homeassistant/components/todoist/calendar.py @@ -541,9 +541,8 @@ class TodoistProjectData: return None # All task Labels (optional parameter). - task[LABELS] = [ - label.name for label in self._labels if label.name in data.labels - ] + labels = data.labels or [] + task[LABELS] = [label.name for label in self._labels if label.name in labels] if self._label_whitelist and ( not any(label in task[LABELS] for label in self._label_whitelist) ): diff --git a/homeassistant/components/todoist/manifest.json b/homeassistant/components/todoist/manifest.json index 72d76108353..791f5642aad 100644 --- a/homeassistant/components/todoist/manifest.json +++ b/homeassistant/components/todoist/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/todoist", "iot_class": "cloud_polling", "loggers": ["todoist"], - "requirements": ["todoist-api-python==2.1.2"] + "requirements": ["todoist-api-python==2.1.7"] } diff --git a/requirements_all.txt b/requirements_all.txt index 505d9351f68..35abba78229 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2893,7 +2893,7 @@ tilt-ble==0.2.3 tmb==0.0.4 # homeassistant.components.todoist -todoist-api-python==2.1.2 +todoist-api-python==2.1.7 # homeassistant.components.tolo tololib==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0a1e1a7433d..b7a693b4052 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2321,7 +2321,7 @@ thinqconnect==1.0.2 tilt-ble==0.2.3 # homeassistant.components.todoist -todoist-api-python==2.1.2 +todoist-api-python==2.1.7 # homeassistant.components.tolo tololib==1.1.0 diff --git a/tests/components/todoist/conftest.py b/tests/components/todoist/conftest.py index 4b2bfea2e30..84f0fa740e9 100644 --- a/tests/components/todoist/conftest.py +++ b/tests/components/todoist/conftest.py @@ -70,6 +70,7 @@ def make_api_task( section_id=None, url="https://todoist.com", sync_id=None, + duration=None, ) @@ -94,6 +95,7 @@ def mock_api(tasks: list[Task]) -> AsyncMock: url="", is_inbox_project=False, is_team_inbox=False, + can_assign_tasks=False, order=1, parent_id=None, view_style="list", From 94daeffe44dbb605cf3ae5f1547b8ddc9af314bc Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 3 Feb 2025 17:10:39 +0100 Subject: [PATCH 04/10] Add Ublockout virtual integration of MotionBlinds (#137179) --- homeassistant/components/ublockout/__init__.py | 1 + homeassistant/components/ublockout/manifest.json | 6 ++++++ homeassistant/generated/integrations.json | 5 +++++ 3 files changed, 12 insertions(+) create mode 100644 homeassistant/components/ublockout/__init__.py create mode 100644 homeassistant/components/ublockout/manifest.json diff --git a/homeassistant/components/ublockout/__init__.py b/homeassistant/components/ublockout/__init__.py new file mode 100644 index 00000000000..87127e331da --- /dev/null +++ b/homeassistant/components/ublockout/__init__.py @@ -0,0 +1 @@ +"""Virtual integration: Ublockout.""" diff --git a/homeassistant/components/ublockout/manifest.json b/homeassistant/components/ublockout/manifest.json new file mode 100644 index 00000000000..d5ef46b8ad2 --- /dev/null +++ b/homeassistant/components/ublockout/manifest.json @@ -0,0 +1,6 @@ +{ + "domain": "ublockout", + "name": "Ublockout", + "integration_type": "virtual", + "supported_by": "motion_blinds" +} diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 49546265f17..a14290b9e54 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -6741,6 +6741,11 @@ "integration_type": "virtual", "supported_by": "overkiz" }, + "ublockout": { + "name": "Ublockout", + "integration_type": "virtual", + "supported_by": "motion_blinds" + }, "uk_transport": { "name": "UK Transport", "integration_type": "hub", From ce5be8686ac638fab9f229b3e4876a143986f667 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 3 Feb 2025 17:18:30 +0100 Subject: [PATCH 05/10] Add Heicko virtual motionblinds integration (#137191) --- homeassistant/components/heicko/__init__.py | 1 + homeassistant/components/heicko/manifest.json | 6 ++++++ homeassistant/generated/integrations.json | 5 +++++ 3 files changed, 12 insertions(+) create mode 100644 homeassistant/components/heicko/__init__.py create mode 100644 homeassistant/components/heicko/manifest.json diff --git a/homeassistant/components/heicko/__init__.py b/homeassistant/components/heicko/__init__.py new file mode 100644 index 00000000000..65c527f5252 --- /dev/null +++ b/homeassistant/components/heicko/__init__.py @@ -0,0 +1 @@ +"""Virtual integration: Heicko.""" diff --git a/homeassistant/components/heicko/manifest.json b/homeassistant/components/heicko/manifest.json new file mode 100644 index 00000000000..d8f939a5bed --- /dev/null +++ b/homeassistant/components/heicko/manifest.json @@ -0,0 +1,6 @@ +{ + "domain": "heicko", + "name": "Heicko", + "integration_type": "virtual", + "supported_by": "motion_blinds" +} diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index a14290b9e54..021c77fec6a 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -2521,6 +2521,11 @@ "config_flow": false, "iot_class": "local_polling" }, + "heicko": { + "name": "Heicko", + "integration_type": "virtual", + "supported_by": "motion_blinds" + }, "heiwa": { "name": "Heiwa", "integration_type": "virtual", From c5e60045b42b158c495ba198576e799422d34068 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 3 Feb 2025 17:21:28 +0100 Subject: [PATCH 06/10] Add Smart Rollos virtual motionblinds integration (#137190) --- homeassistant/components/smart_rollos/__init__.py | 1 + homeassistant/components/smart_rollos/manifest.json | 6 ++++++ homeassistant/generated/integrations.json | 5 +++++ 3 files changed, 12 insertions(+) create mode 100644 homeassistant/components/smart_rollos/__init__.py create mode 100644 homeassistant/components/smart_rollos/manifest.json diff --git a/homeassistant/components/smart_rollos/__init__.py b/homeassistant/components/smart_rollos/__init__.py new file mode 100644 index 00000000000..d4bb8c7fb1b --- /dev/null +++ b/homeassistant/components/smart_rollos/__init__.py @@ -0,0 +1 @@ +"""Virtual integration: Smart Rollos.""" diff --git a/homeassistant/components/smart_rollos/manifest.json b/homeassistant/components/smart_rollos/manifest.json new file mode 100644 index 00000000000..f093f740bd6 --- /dev/null +++ b/homeassistant/components/smart_rollos/manifest.json @@ -0,0 +1,6 @@ +{ + "domain": "smart_rollos", + "name": "Smart Rollos", + "integration_type": "virtual", + "supported_by": "motion_blinds" +} diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 021c77fec6a..57b58e60ed6 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -5811,6 +5811,11 @@ "config_flow": true, "iot_class": "cloud_polling" }, + "smart_rollos": { + "name": "Smart Rollos", + "integration_type": "virtual", + "supported_by": "motion_blinds" + }, "smarther": { "name": "Smarther", "integration_type": "virtual", From b6607031179755e92390d27f18dd7d7ef097b5c3 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Mon, 3 Feb 2025 17:28:54 +0100 Subject: [PATCH 07/10] Fix eheimdigital sw_version mock (#137255) --- tests/components/eheimdigital/conftest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/components/eheimdigital/conftest.py b/tests/components/eheimdigital/conftest.py index ef52eade9ae..afb97b97569 100644 --- a/tests/components/eheimdigital/conftest.py +++ b/tests/components/eheimdigital/conftest.py @@ -34,6 +34,7 @@ def classic_led_ctrl_mock(): ) classic_led_ctrl_mock.name = "Mock classicLEDcontrol+e" classic_led_ctrl_mock.aquarium_name = "Mock Aquarium" + classic_led_ctrl_mock.sw_version = "1.0.0_1.0.0" classic_led_ctrl_mock.light_mode = LightMode.DAYCL_MODE classic_led_ctrl_mock.light_level = (10, 39) return classic_led_ctrl_mock @@ -47,6 +48,7 @@ def heater_mock(): heater_mock.device_type = EheimDeviceType.VERSION_EHEIM_EXT_HEATER heater_mock.name = "Mock Heater" heater_mock.aquarium_name = "Mock Aquarium" + heater_mock.sw_version = "1.0.0_1.0.0" heater_mock.temperature_unit = HeaterUnit.CELSIUS heater_mock.current_temperature = 24.2 heater_mock.target_temperature = 25.5 From a41566611e0ec24b93fca6a15b96f7de463b5502 Mon Sep 17 00:00:00 2001 From: Josef Zweck Date: Mon, 3 Feb 2025 17:30:27 +0100 Subject: [PATCH 08/10] Bump onedrive-personal-sdk to 0.0.2 (#137252) --- homeassistant/components/onedrive/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/onedrive/manifest.json b/homeassistant/components/onedrive/manifest.json index 767426058c1..263c73a9f69 100644 --- a/homeassistant/components/onedrive/manifest.json +++ b/homeassistant/components/onedrive/manifest.json @@ -9,5 +9,5 @@ "iot_class": "cloud_polling", "loggers": ["onedrive_personal_sdk"], "quality_scale": "bronze", - "requirements": ["onedrive-personal-sdk==0.0.1"] + "requirements": ["onedrive-personal-sdk==0.0.2"] } diff --git a/requirements_all.txt b/requirements_all.txt index 35abba78229..676e8b4348c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1556,7 +1556,7 @@ omnilogic==0.4.5 ondilo==0.5.0 # homeassistant.components.onedrive -onedrive-personal-sdk==0.0.1 +onedrive-personal-sdk==0.0.2 # homeassistant.components.onvif onvif-zeep-async==3.2.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b7a693b4052..eb4ed113467 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1304,7 +1304,7 @@ omnilogic==0.4.5 ondilo==0.5.0 # homeassistant.components.onedrive -onedrive-personal-sdk==0.0.1 +onedrive-personal-sdk==0.0.2 # homeassistant.components.onvif onvif-zeep-async==3.2.5 From 58b7be7c2ffa1125c6d7bcc98d7617efbf1a86a0 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 3 Feb 2025 17:33:03 +0100 Subject: [PATCH 09/10] Check for errors when creating backups using supervisor (#137220) * Check for errors when creating backups using supervisor * Improve error reporting when there's no backup reference --- homeassistant/components/hassio/backup.py | 9 ++++-- tests/components/hassio/test_backup.py | 37 ++++++++++++++++++----- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/hassio/backup.py b/homeassistant/components/hassio/backup.py index 34d1c62aed7..4aad984cc54 100644 --- a/homeassistant/components/hassio/backup.py +++ b/homeassistant/components/hassio/backup.py @@ -354,6 +354,7 @@ class SupervisorBackupReaderWriter(BackupReaderWriter): """Wait for a backup to complete.""" backup_complete = asyncio.Event() backup_id: str | None = None + create_errors: list[dict[str, str]] = [] @callback def on_job_progress(data: Mapping[str, Any]) -> None: @@ -361,6 +362,7 @@ class SupervisorBackupReaderWriter(BackupReaderWriter): nonlocal backup_id if data.get("done") is True: backup_id = data.get("reference") + create_errors.extend(data.get("errors", [])) backup_complete.set() unsub = self._async_listen_job_events(backup.job_id, on_job_progress) @@ -369,8 +371,11 @@ class SupervisorBackupReaderWriter(BackupReaderWriter): await backup_complete.wait() finally: unsub() - if not backup_id: - raise BackupReaderWriterError("Backup failed") + if not backup_id or create_errors: + # We should add more specific error handling here in the future + raise BackupReaderWriterError( + f"Backup failed: {create_errors or 'no backup_id'}" + ) async def open_backup() -> AsyncIterator[bytes]: try: diff --git a/tests/components/hassio/test_backup.py b/tests/components/hassio/test_backup.py index f35ddeaabbd..ab3335e00dc 100644 --- a/tests/components/hassio/test_backup.py +++ b/tests/components/hassio/test_backup.py @@ -1360,11 +1360,40 @@ async def test_reader_writer_create_partial_backup_error( assert supervisor_client.backups.partial_backup.call_count == 1 +@pytest.mark.parametrize( + "supervisor_event", + [ + # Missing backup reference + { + "event": "job", + "data": { + "done": True, + "uuid": TEST_JOB_ID, + }, + }, + # Errors + { + "event": "job", + "data": { + "done": True, + "errors": [ + { + "type": "BackupMountDownError", + "message": "test_mount is down, cannot back-up to it", + } + ], + "uuid": TEST_JOB_ID, + "reference": "test_slug", + }, + }, + ], +) @pytest.mark.usefixtures("hassio_client", "setup_integration") async def test_reader_writer_create_missing_reference_error( hass: HomeAssistant, hass_ws_client: WebSocketGenerator, supervisor_client: AsyncMock, + supervisor_event: dict[str, Any], ) -> None: """Test missing reference error when generating a backup.""" client = await hass_ws_client(hass) @@ -1395,13 +1424,7 @@ async def test_reader_writer_create_missing_reference_error( assert supervisor_client.backups.partial_backup.call_count == 1 await client.send_json_auto_id( - { - "type": "supervisor/event", - "data": { - "event": "job", - "data": {"done": True, "uuid": TEST_JOB_ID}, - }, - } + {"type": "supervisor/event", "data": supervisor_event} ) response = await client.receive_json() assert response["success"] From 28edbdc107818e5872fa060c8c815520902eaa0d Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Mon, 3 Feb 2025 11:07:45 -0600 Subject: [PATCH 10/10] Clear extra system prompt on start_conversation error (#137254) * Clear extra system prompt on start_conversation error * Update homeassistant/components/assist_satellite/entity.py Co-authored-by: Paulus Schoutsen --------- Co-authored-by: Paulus Schoutsen --- .../components/assist_satellite/entity.py | 5 ++ tests/components/voip/test_voip.py | 87 +++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/homeassistant/components/assist_satellite/entity.py b/homeassistant/components/assist_satellite/entity.py index c901bc7d928..902cf731a5d 100644 --- a/homeassistant/components/assist_satellite/entity.py +++ b/homeassistant/components/assist_satellite/entity.py @@ -274,6 +274,11 @@ class AssistSatelliteEntity(entity.Entity): try: await self.async_start_conversation(announcement) + except Exception: + # Clear prompt on error + self._conversation_id = None + self._extra_system_prompt = None + raise finally: self._is_announcing = False diff --git a/tests/components/voip/test_voip.py b/tests/components/voip/test_voip.py index 442f4a62392..3e3e5337417 100644 --- a/tests/components/voip/test_voip.py +++ b/tests/components/voip/test_voip.py @@ -1084,3 +1084,90 @@ async def test_start_conversation( # Wait for TTS await tts_sent.wait() await conversation_task + + +@pytest.mark.usefixtures("socket_enabled") +async def test_start_conversation_user_doesnt_pick_up( + hass: HomeAssistant, + voip_devices: VoIPDevices, + voip_device: VoIPDevice, +) -> None: + """Test start conversation when the user doesn't pick up.""" + assert await async_setup_component(hass, "voip", {}) + + pipeline = assist_pipeline.Pipeline( + conversation_engine="test engine", + conversation_language="en", + language="en", + name="test pipeline", + stt_engine="test stt", + stt_language="en", + tts_engine="test tts", + tts_language="en", + tts_voice=None, + wake_word_entity=None, + wake_word_id=None, + ) + + satellite = async_get_satellite_entity(hass, voip.DOMAIN, voip_device.voip_id) + assert isinstance(satellite, VoipAssistSatellite) + assert ( + satellite.supported_features + & assist_satellite.AssistSatelliteEntityFeature.START_CONVERSATION + ) + + # Protocol has already been mocked, but "outgoing_call" is not async + mock_protocol: AsyncMock = hass.data[DOMAIN].protocol + mock_protocol.outgoing_call = Mock() + + pipeline_started = asyncio.Event() + + async def async_pipeline_from_audio_stream( + hass: HomeAssistant, + context: Context, + *args, + conversation_extra_system_prompt: str | None = None, + **kwargs, + ): + # System prompt should be not be set due to timeout (user not picking up) + assert conversation_extra_system_prompt is None + + pipeline_started.set() + + with ( + patch( + "homeassistant.components.assist_satellite.entity.async_get_pipeline", + return_value=pipeline, + ), + patch( + "homeassistant.components.voip.assist_satellite.VoipAssistSatellite.async_start_conversation", + side_effect=TimeoutError, + ), + patch( + "homeassistant.components.assist_satellite.entity.async_pipeline_from_audio_stream", + new=async_pipeline_from_audio_stream, + ), + patch( + "homeassistant.components.assist_satellite.entity.tts_generate_media_source_id", + return_value="test media id", + ), + ): + satellite.transport = Mock() + + # Error should clear system prompt + with pytest.raises(TimeoutError): + await hass.services.async_call( + assist_satellite.DOMAIN, + "start_conversation", + { + "entity_id": satellite.entity_id, + "start_message": "test announcement", + "extra_system_prompt": "test prompt", + }, + blocking=True, + ) + + # Trigger a pipeline so we can check if the system prompt was cleared + satellite.on_chunk(bytes(_ONE_SECOND)) + async with asyncio.timeout(1): + await pipeline_started.wait()