diff --git a/homeassistant/components/braviatv/manifest.json b/homeassistant/components/braviatv/manifest.json index 0c8f90ac3d7a..532ee681502a 100644 --- a/homeassistant/components/braviatv/manifest.json +++ b/homeassistant/components/braviatv/manifest.json @@ -8,7 +8,7 @@ "integration_type": "device", "iot_class": "local_polling", "loggers": ["pybravia"], - "requirements": ["pybravia==0.4.1"], + "requirements": ["pybravia==0.5.1"], "ssdp": [ { "manufacturer": "Sony Corporation", diff --git a/homeassistant/components/enphase_envoy/diagnostics.py b/homeassistant/components/enphase_envoy/diagnostics.py index 77d7c2a4dc97..d52c4c14e049 100644 --- a/homeassistant/components/enphase_envoy/diagnostics.py +++ b/homeassistant/components/enphase_envoy/diagnostics.py @@ -79,7 +79,9 @@ async def _get_fixture_collection(envoy: Envoy, serial: str) -> dict[str, Any]: try: response: ClientResponse = await envoy.request(end_point) fixture_data[end_point] = ( - (await response.text()).replace("\n", "").replace(serial, CLEAN_TEXT) + (await response.text(errors="replace")) + .replace("\n", "") + .replace(serial, CLEAN_TEXT) ) fixture_data[f"{end_point}_log"] = json_dumps( { diff --git a/homeassistant/components/lamarzocco/manifest.json b/homeassistant/components/lamarzocco/manifest.json index 0abd73db29a1..3976349c4673 100644 --- a/homeassistant/components/lamarzocco/manifest.json +++ b/homeassistant/components/lamarzocco/manifest.json @@ -37,5 +37,5 @@ "iot_class": "cloud_push", "loggers": ["pylamarzocco"], "quality_scale": "platinum", - "requirements": ["pylamarzocco==2.4.2"] + "requirements": ["pylamarzocco==2.4.3"] } diff --git a/homeassistant/components/proxmoxve/__init__.py b/homeassistant/components/proxmoxve/__init__.py index 2f969fae5bed..666394485c39 100644 --- a/homeassistant/components/proxmoxve/__init__.py +++ b/homeassistant/components/proxmoxve/__init__.py @@ -193,9 +193,9 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ProxmoxConfigEntry) -> if "@" in data.get(CONF_USERNAME, ""): username, realm = data[CONF_USERNAME].split("@", 1) data[CONF_USERNAME] = username - data[CONF_REALM] = realm.lower() + data[CONF_REALM] = realm - realm = data[CONF_REALM].lower() + realm = data[CONF_REALM] # If the realm is one of the base providers, # set the provider to match the realm. diff --git a/homeassistant/components/proxmoxve/common.py b/homeassistant/components/proxmoxve/common.py index 73dee442372a..a32a38734e9d 100644 --- a/homeassistant/components/proxmoxve/common.py +++ b/homeassistant/components/proxmoxve/common.py @@ -16,7 +16,7 @@ def sanitize_config_entry(input_data: Mapping[str, Any]) -> dict[str, Any]: realm = provider.lower() if provider == AUTH_OTHER: - realm = data[CONF_REALM].lower() + realm = data[CONF_REALM] data[CONF_REALM] = realm data[CONF_USERNAME] = f"{username}@{realm}" diff --git a/homeassistant/components/yoto/manifest.json b/homeassistant/components/yoto/manifest.json index 14d96dfbd4f3..0d41784390be 100644 --- a/homeassistant/components/yoto/manifest.json +++ b/homeassistant/components/yoto/manifest.json @@ -10,5 +10,5 @@ "iot_class": "cloud_push", "loggers": ["yoto_api"], "quality_scale": "platinum", - "requirements": ["yoto-api==4.3.1"] + "requirements": ["yoto-api==4.3.2"] } diff --git a/homeassistant/const.py b/homeassistant/const.py index 68f0ec1366cb..62bb20178da5 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -22,7 +22,7 @@ if TYPE_CHECKING: APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2026 MINOR_VERSION: Final = 7 -PATCH_VERSION: Final = "3" +PATCH_VERSION: Final = "4" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 14, 2) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 50ee981adf9d..a844fc33446a 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -6,7 +6,7 @@ aiodns==4.0.4 aiogithubapi==26.0.0 aiohttp-asyncmdnsresolver==0.2.0 aiohttp-fast-zlib==0.3.0 -aiohttp==3.14.1 +aiohttp==3.14.3 aiohttp_cors==0.8.1 aiousbwatcher==1.1.2 aiozoneinfo==0.2.3 @@ -75,7 +75,7 @@ voluptuous-openapi==0.4.1 voluptuous-serialize==2.7.0 voluptuous==0.15.2 webrtc-models==0.3.0 -yarl==1.24.2 +yarl==1.24.5 zeroconf==0.150.0 # Constrain pycryptodome to avoid vulnerability diff --git a/pyproject.toml b/pyproject.toml index 67982b512877..d6e3e435c508 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2026.7.3" +version = "2026.7.4" license = "Apache-2.0" license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"] description = "Open-source home automation platform running on Python 3." @@ -28,7 +28,7 @@ dependencies = [ # module level in `bootstrap.py` and its requirements thus need to be in # requirements.txt to ensure they are always installed "aiogithubapi==26.0.0", - "aiohttp==3.14.1", + "aiohttp==3.14.3", "aiohttp_cors==0.8.1", "aiohttp-fast-zlib==0.3.0", "aiohttp-asyncmdnsresolver==0.2.0", @@ -78,7 +78,7 @@ dependencies = [ "voluptuous==0.15.2", "voluptuous-serialize==2.7.0", "voluptuous-openapi==0.4.1", - "yarl==1.24.2", + "yarl==1.24.5", "webrtc-models==0.3.0", "zeroconf==0.150.0", ] diff --git a/requirements.txt b/requirements.txt index 6673d88bdedd..bc15bb1849ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ aiodns==4.0.4 aiogithubapi==26.0.0 aiohttp-asyncmdnsresolver==0.2.0 aiohttp-fast-zlib==0.3.0 -aiohttp==3.14.1 +aiohttp==3.14.3 aiohttp_cors==0.8.1 aiozoneinfo==0.2.3 annotatedyaml==1.0.2 @@ -60,5 +60,5 @@ voluptuous-openapi==0.4.1 voluptuous-serialize==2.7.0 voluptuous==0.15.2 webrtc-models==0.3.0 -yarl==1.24.2 +yarl==1.24.5 zeroconf==0.150.0 diff --git a/requirements_all.txt b/requirements_all.txt index 01f89263081c..0ce28e05d339 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2048,7 +2048,7 @@ pyblu==2.0.8 pybotvac==0.0.29 # homeassistant.components.braviatv -pybravia==0.4.1 +pybravia==0.5.1 # homeassistant.components.nissan_leaf pycarwings2==2.14 @@ -2298,7 +2298,7 @@ pykwb==0.0.8 pylacrosse==0.4 # homeassistant.components.lamarzocco -pylamarzocco==2.4.2 +pylamarzocco==2.4.3 # homeassistant.components.lastfm pylast==5.1.0 @@ -3414,7 +3414,7 @@ yeelightsunflower==0.0.10 yolink-api==0.6.5 # homeassistant.components.yoto -yoto-api==4.3.1 +yoto-api==4.3.2 # homeassistant.components.youless youless-api==2.2.0 diff --git a/tests/components/proxmoxve/conftest.py b/tests/components/proxmoxve/conftest.py index 1decc74ac46c..9ac0b312633b 100644 --- a/tests/components/proxmoxve/conftest.py +++ b/tests/components/proxmoxve/conftest.py @@ -64,8 +64,8 @@ MOCK_TEST_TOKEN_CONFIG = { MOCK_TEST_OTHER_CONFIG = { **MOCK_TEST_CONFIG, CONF_AUTH_METHOD: "other", - CONF_REALM: "test_realm", - CONF_USERNAME: "test_user@test_realm", + CONF_REALM: "Test_Realm", + CONF_USERNAME: "test_user@Test_Realm", } MOCK_TEST_TOKEN_OTHER_CONFIG = { @@ -74,8 +74,8 @@ MOCK_TEST_TOKEN_OTHER_CONFIG = { CONF_TOKEN_ID: "test_token_id", CONF_TOKEN_SECRET: "test_token_secret", CONF_AUTH_METHOD: "other", - CONF_REALM: "test_realm", - CONF_USERNAME: "test_user@test_realm", + CONF_REALM: "Test_Realm", + CONF_USERNAME: "test_user@Test_Realm", } diff --git a/tests/components/proxmoxve/test_config_flow.py b/tests/components/proxmoxve/test_config_flow.py index c6814ca95717..aaf4aa0dd5ec 100644 --- a/tests/components/proxmoxve/test_config_flow.py +++ b/tests/components/proxmoxve/test_config_flow.py @@ -75,7 +75,7 @@ MOCK_USER_STEP_OTHER = { MOCK_USER_AUTH_STEP_OTHER = { **MOCK_USER_AUTH_STEP_PASSWORD, - CONF_REALM: "test_realm", + CONF_REALM: "Test_Realm", } # Other authentication method with realm and token @@ -86,7 +86,7 @@ MOCK_USER_STEP_OTHER_TOKEN = { MOCK_USER_AUTH_STEP_OTHER_TOKEN = { **MOCK_USER_AUTH_STEP_TOKEN, - CONF_REALM: "test_realm", + CONF_REALM: "Test_Realm", } MOCK_USER_SETUP = {CONF_NODES: ["pve1"]} @@ -724,7 +724,7 @@ async def test_full_flow_reauth_token_other( result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={ - CONF_REALM: "test_realm", + CONF_REALM: "Test_Realm", CONF_TOKEN_ID: "test_token_id", CONF_TOKEN_SECRET: "new_token_secret", }, diff --git a/tests/components/proxmoxve/test_init.py b/tests/components/proxmoxve/test_init.py index 83c6a0a5139e..68ccca4f5916 100644 --- a/tests/components/proxmoxve/test_init.py +++ b/tests/components/proxmoxve/test_init.py @@ -9,6 +9,7 @@ import requests from requests.exceptions import ConnectTimeout, SSLError from homeassistant.components.proxmoxve.const import ( + AUTH_OTHER, AUTH_PAM, CONF_AUTH_METHOD, CONF_CONTAINERS, @@ -165,25 +166,56 @@ async def test_setup_exceptions( assert mock_config_entry.state is expected_state +@pytest.mark.parametrize( + ("mock_config_entry", "expected_auth_method", "expected_realm"), + [ + ( + MockConfigEntry( + domain=DOMAIN, + version=1, + unique_id="1", + data={ + CONF_HOST: "http://test_host", + CONF_PORT: 8006, + CONF_REALM: "pam", + CONF_USERNAME: "test_user@pam", + CONF_PASSWORD: "test_password", + CONF_VERIFY_SSL: True, + }, + ), + AUTH_PAM, + "pam", + ), + ( + MockConfigEntry( + domain=DOMAIN, + version=1, + unique_id="1", + data={ + CONF_HOST: "http://test_host", + CONF_PORT: 8006, + CONF_REALM: "Test_Realm", + CONF_USERNAME: "test_user@Test_Realm", + CONF_PASSWORD: "test_password", + CONF_VERIFY_SSL: True, + }, + ), + AUTH_OTHER, + "Test_Realm", + ), + ], +) async def test_migration_v1_to_v3( hass: HomeAssistant, entity_registry: er.EntityRegistry, device_registry: dr.DeviceRegistry, + mock_config_entry: MockConfigEntry, + expected_auth_method: str, + expected_realm: str, ) -> None: - """Test migration from version 1.""" - entry = MockConfigEntry( - domain=DOMAIN, - version=1, - unique_id="1", - data={ - CONF_HOST: "http://test_host", - CONF_PORT: 8006, - CONF_REALM: "pam", - CONF_USERNAME: "test_user@pam", - CONF_PASSWORD: "test_password", - CONF_VERIFY_SSL: True, - }, - ) + """Test migration from version 1 to 3.""" + entry = mock_config_entry + entry.add_to_hass(hass) assert entry.version == 1 @@ -227,6 +259,8 @@ async def test_migration_v1_to_v3( await hass.async_block_till_done() assert entry.version == 3 + assert entry.data[CONF_AUTH_METHOD] == expected_auth_method + assert entry.data[CONF_REALM] == expected_realm vm_entity_after = entity_registry.async_get(vm_entity.entity_id) container_entity_after = entity_registry.async_get(container_entity.entity_id) @@ -235,6 +269,97 @@ async def test_migration_v1_to_v3( assert container_entity_after.unique_id == f"{entry.entry_id}_200_status" +@pytest.mark.parametrize( + ("mock_config_entry", "expected_auth_method", "expected_realm"), + [ + ( + MockConfigEntry( + domain=DOMAIN, + version=2, + unique_id="1", + data={ + CONF_HOST: "http://test_host", + CONF_PORT: 8006, + CONF_REALM: "pam", + CONF_USERNAME: "test_user@pam", + CONF_PASSWORD: "test_password", + CONF_VERIFY_SSL: True, + }, + ), + AUTH_PAM, + "pam", + ), + ( + MockConfigEntry( + domain=DOMAIN, + version=2, + unique_id="1", + data={ + CONF_HOST: "http://test_host", + CONF_PORT: 8006, + CONF_REALM: "Test_Realm", + CONF_USERNAME: "test_user@Test_Realm", + CONF_PASSWORD: "test_password", + CONF_VERIFY_SSL: True, + }, + ), + AUTH_OTHER, + "Test_Realm", + ), + ( + MockConfigEntry( + domain=DOMAIN, + version=2, + unique_id="1", + data={ + CONF_HOST: "http://test_host", + CONF_PORT: 8006, + CONF_USERNAME: "test_user@pam", + CONF_PASSWORD: "test_password", + CONF_VERIFY_SSL: True, + }, + ), + AUTH_PAM, + "pam", + ), + ( + MockConfigEntry( + domain=DOMAIN, + version=2, + unique_id="1", + data={ + CONF_HOST: "http://test_host", + CONF_PORT: 8006, + CONF_USERNAME: "test_user@Test_Realm", + CONF_PASSWORD: "test_password", + CONF_VERIFY_SSL: True, + }, + ), + AUTH_OTHER, + "Test_Realm", + ), + ], +) +async def test_migration_v2_to_v3( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + expected_auth_method: str, + expected_realm: str, +) -> None: + """Test migration from version 2 to 3.""" + entry = mock_config_entry + + entry.add_to_hass(hass) + assert entry.version == 2 + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.version == 3 + assert entry.data[CONF_AUTH_METHOD] == expected_auth_method + assert entry.data[CONF_REALM] == expected_realm + + async def test_offline_node( hass: HomeAssistant, mock_proxmox_client: MagicMock, @@ -253,61 +378,6 @@ async def test_offline_node( assert state.state == STATE_OFF -async def test_migration_v2_to_v3( - hass: HomeAssistant, -) -> None: - """Test migration from version 2 to 3.""" - entry = MockConfigEntry( - domain=DOMAIN, - version=2, - unique_id="1", - data={ - CONF_HOST: "http://test_host", - CONF_PORT: 8006, - CONF_REALM: "pam", - CONF_USERNAME: "test_user@pam", - CONF_PASSWORD: "test_password", - CONF_VERIFY_SSL: True, - }, - ) - entry.add_to_hass(hass) - assert entry.version == 2 - - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - assert entry.version == 3 - assert entry.data[CONF_AUTH_METHOD] == AUTH_PAM - assert entry.data[CONF_REALM] == AUTH_PAM - - -async def test_migration_v2_to_v3_without_realm( - hass: HomeAssistant, -) -> None: - """Test migration from version 2 to 3.""" - entry = MockConfigEntry( - domain=DOMAIN, - version=2, - unique_id="1", - data={ - CONF_HOST: "http://test_host", - CONF_PORT: 8006, - CONF_USERNAME: "test_user@pam", - CONF_PASSWORD: "test_password", - CONF_VERIFY_SSL: True, - }, - ) - entry.add_to_hass(hass) - assert entry.version == 2 - - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - assert entry.version == 3 - assert entry.data[CONF_AUTH_METHOD] == AUTH_PAM - assert entry.data[CONF_REALM] == AUTH_PAM - - async def test_new_vm_creates_entity( hass: HomeAssistant, mock_proxmox_client: MagicMock,