Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d7fba1b
Mock the Google API in the Google Generative AI diagnostics test (#17…
balloob Aug 22, 2026
cdc96c0
Send go2rtc credentials as an Authorization header (#179838)
balloob Aug 22, 2026
cb8b0a9
Use encode_basic_auth for the Lyric token request (#179831)
balloob Aug 22, 2026
befe43a
Send BleBox credentials as an Authorization header (#179837)
balloob Aug 22, 2026
b540c39
Keep the mocked Xbox token long enough for PyJWT (#179829)
balloob Aug 22, 2026
1027d4f
Lower log level for missing TCL passages in Data Grand Lyon (#179828)
Crocmagnon Aug 22, 2026
fc4e690
Bump pyrisco to 0.8.1 (#179818)
Wall-E-5 Aug 22, 2026
6927877
Bump midea-local to 10.0.0 (#179842)
chemelli74 Aug 22, 2026
02a4014
Reolink tamper entities (#179750)
starkillerOG Aug 22, 2026
e74ebb5
Mock the supervisor root info in the http ban test (#179862)
balloob Aug 22, 2026
aa23eb9
Give the mocked cloud subscription info a real payload (#179865)
balloob Aug 22, 2026
451ac3f
Mock the add-on store info in the Z-Wave USB discovery test (#179864)
balloob Aug 22, 2026
7cbd094
Mock the Aprilaire client stop_listen as a sync call (#179863)
balloob Aug 22, 2026
c48e9d9
Mock the add-on store info in the Yellow multipan uninstall test (#17…
balloob Aug 22, 2026
6b2607a
Pass the bluetooth detection callback to the scanner constructor (#17…
balloob Aug 22, 2026
a3394bb
Give the mocked Shelly device a real ble_getconfig result (#179859)
balloob Aug 22, 2026
eda26b3
Mock the API client in the ESPHome websocket API test (#179841)
balloob Aug 22, 2026
f91a6f6
Forward service call context to entity in script.turn_off (#179539)
balloob Aug 22, 2026
bb78cab
Disable DBus probing for all tests, not just the bluetooth ones (#179…
balloob Aug 22, 2026
2e1b5de
Send rest credentials as an Authorization header (#179856)
balloob Aug 22, 2026
bd7685f
Register the custom pytest marks instead of ignoring the warning (#17…
balloob Aug 22, 2026
eee9906
Send MJPEG camera credentials as an Authorization header (#179854)
balloob Aug 22, 2026
207d384
Make the mocked IMAP IDLE result report done() synchronously (#179822)
balloob Aug 22, 2026
5ca5794
Fix OVHcloud AI Endpoints forgetting an empty LLM API selection (#179…
balloob Aug 22, 2026
5610706
Bump reolink_aio to 0.21.10 (#179851)
starkillerOG Aug 22, 2026
59618df
Increase Reolink track limit (#179850)
starkillerOG Aug 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions homeassistant/components/blebox/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ def get_maybe_authenticated_session(
) -> aiohttp.ClientSession:
"""Return proper session object."""
if username and password:
auth = aiohttp.BasicAuth(login=username, password=password)
return async_create_clientsession(hass, auth=auth)
return async_create_clientsession(
hass,
headers={"Authorization": aiohttp.encode_basic_auth(username, password)},
)

return async_get_clientsession(hass)
3 changes: 2 additions & 1 deletion homeassistant/components/data_grand_lyon/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ async def _async_update_data(self) -> dict[str, list[TclPassage]]:
if sorted_passages:
stops[subentry.subentry_id] = sorted_passages
else:
LOGGER.warning(
# Expected outside service hours, e.g. at night
LOGGER.debug(
"No TCL passages found for subentry %s",
subentry.subentry_id,
)
Expand Down
11 changes: 5 additions & 6 deletions homeassistant/components/go2rtc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from tempfile import mkdtemp
from typing import override

from aiohttp import BasicAuth, ClientSession, UnixConnector
from aiohttp import ClientSession, UnixConnector, encode_basic_auth
from aiohttp.client_exceptions import ClientConnectionError, ServerConnectionError
from awesomeversion import AwesomeVersion
from go2rtc_client import Go2RtcRestClient
Expand Down Expand Up @@ -153,14 +153,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
password = token_hex()
_LOGGER.debug("Generated random credentials for go2rtc server")

auth = BasicAuth(username, password)
# HA will manage the binary
temp_dir = mkdtemp(prefix="go2rtc-")
# Manually created session (not using the helper) needs to be closed manually
# See on_stop listener below
session = ClientSession(
connector=UnixConnector(path=get_go2rtc_unix_socket_path(temp_dir)),
auth=auth,
headers={"Authorization": encode_basic_auth(username, password)},
)
server = Server(
hass,
Expand All @@ -186,9 +185,9 @@ async def on_stop(event: Event) -> None:

url = HA_MANAGED_URL
elif username and password:
# Create session with BasicAuth if credentials are provided
auth = BasicAuth(username, password)
session = async_create_clientsession(hass, auth=auth)
session = async_create_clientsession(
hass, headers={"Authorization": encode_basic_auth(username, password)}
)
else:
session = async_get_clientsession(hass)

Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/lyric/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import cast, override

from aiohttp import BasicAuth, ClientSession
from aiohttp import ClientSession, encode_basic_auth
from aiolyric.client import LyricClient

from homeassistant.components.application_credentials import AuthImplementation
Expand Down Expand Up @@ -63,7 +63,7 @@ async def _token_request(self, data: dict) -> dict:
data["client_secret"] = self.client_secret

headers = {
"Authorization": BasicAuth(self.client_id, self.client_secret).encode(),
"Authorization": encode_basic_auth(self.client_id, self.client_secret),
"Content-Type": "application/x-www-form-urlencoded",
}

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/midea/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "local_polling",
"loggers": ["midealocal"],
"quality_scale": "bronze",
"requirements": ["midea-local==9.0.0"]
"requirements": ["midea-local==10.0.0"]
}
14 changes: 10 additions & 4 deletions homeassistant/components/mjpeg/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,17 @@ def __init__(
self._mjpeg_url = mjpeg_url
self._still_image_url = still_image_url

self._auth = None
self._auth_headers: dict[str, str] | None = None
if (
self._username
and self._password
and self._authentication == HTTP_BASIC_AUTHENTICATION
):
self._auth = aiohttp.BasicAuth(self._username, password=self._password)
self._auth_headers = {
"Authorization": aiohttp.encode_basic_auth(
self._username, self._password
)
}
self._verify_ssl = verify_ssl

if unique_id is not None:
Expand Down Expand Up @@ -145,7 +149,9 @@ async def async_camera_image(
websession = async_get_clientsession(self.hass, verify_ssl=self._verify_ssl)
try:
async with asyncio.timeout(TIMEOUT):
response = await websession.get(self._still_image_url, auth=self._auth)
response = await websession.get(
self._still_image_url, headers=self._auth_headers
)

return await response.read()

Expand Down Expand Up @@ -223,6 +229,6 @@ async def handle_async_mjpeg_stream(

# connect to stream
websession = async_get_clientsession(self.hass, verify_ssl=self._verify_ssl)
stream_coro = websession.get(self._mjpeg_url, auth=self._auth)
stream_coro = websession.get(self._mjpeg_url, headers=self._auth_headers)

return await async_aiohttp_proxy_web(self.hass, request, stream_coro)
6 changes: 5 additions & 1 deletion homeassistant/components/motioneye/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,11 @@ def _set_mjpeg_camera_state_for_camera(self, camera: dict[str, Any]) -> None:
self._authentication == HTTP_BASIC_AUTHENTICATION
and self._username is not None
):
self._auth = aiohttp.BasicAuth(self._username, password=self._password)
self._auth_headers = {
"Authorization": aiohttp.encode_basic_auth(
self._username, self._password
)
}

def _is_acceptable_streaming_camera(self) -> bool:
"""Determine if a camera is streaming/usable."""
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/ovhcloud_ai_endpoints/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ async def async_step_reconfigure(
existing = subentry.data

if user_input is not None:
if not user_input.get(CONF_LLM_HASS_API):
if user_input.get(CONF_LLM_HASS_API) is None:
user_input.pop(CONF_LLM_HASS_API, None)
user_input[CONF_MODEL] = existing[CONF_MODEL]
return self.async_update_and_abort(
Expand Down Expand Up @@ -221,7 +221,7 @@ async def async_step_init(
return self.async_abort(reason="entry_not_loaded")

if user_input is not None:
if not user_input.get(CONF_LLM_HASS_API):
if user_input.get(CONF_LLM_HASS_API) is None:
user_input.pop(CONF_LLM_HASS_API, None)
return self.async_create_entry(
title=user_input[CONF_MODEL], data=user_input
Expand Down
8 changes: 8 additions & 0 deletions homeassistant/components/reolink/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ class ReolinkIndexBinarySensorEntityDescription(
value=lambda api, ch: api.ai_detected(ch, "cry"),
supported=lambda api, ch: api.ai_supported(ch, "cry"),
),
ReolinkBinarySensorEntityDescription(
key="tamper",
translation_key="tamper",
cmd_id=[33],
device_class=BinarySensorDeviceClass.TAMPER,
value=lambda api, ch: api.baichuan.tamper_state(ch),
supported=lambda api, ch: api.supported(ch, "tamper"),
),
)

BINARY_SENSORS = (
Expand Down
12 changes: 12 additions & 0 deletions homeassistant/components/reolink/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@
"on": "mdi:package-variant-closed-check"
}
},
"tamper": {
"default": "mdi:shield-check",
"state": {
"on": "mdi:shield-alert"
}
},
"vehicle": {
"default": "mdi:car-off",
"state": {
Expand Down Expand Up @@ -634,6 +640,12 @@
},
"siren_on_event": {
"default": "mdi:alarm-light"
},
"tamper_enabled": {
"default": "mdi:shield-check",
"state": {
"off": "mdi:shield"
}
}
},
"time": {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/reolink/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
"iot_class": "local_push",
"loggers": ["reolink_aio"],
"quality_scale": "platinum",
"requirements": ["reolink-aio==0.21.9"]
"requirements": ["reolink-aio==0.21.10"]
}
4 changes: 2 additions & 2 deletions homeassistant/components/reolink/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ class ReolinkChimeNumberEntityDescription(
entity_registry_enabled_default=False,
native_step=1,
native_min_value=-1,
native_max_value=2700,
native_max_value=6000,
supported=lambda api, ch: api.supported(ch, "auto_track_limit"),
value=lambda api, ch: api.auto_track_limit_left(ch),
method=lambda api, ch, value: api.set_auto_track_limit(ch, left=int(value)),
Expand All @@ -585,7 +585,7 @@ class ReolinkChimeNumberEntityDescription(
entity_registry_enabled_default=False,
native_step=1,
native_min_value=-1,
native_max_value=2700,
native_max_value=6000,
supported=lambda api, ch: api.supported(ch, "auto_track_limit"),
value=lambda api, ch: api.auto_track_limit_right(ch),
method=lambda api, ch, value: api.set_auto_track_limit(ch, right=int(value)),
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/reolink/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,9 @@
},
"siren_on_event": {
"name": "Siren on event"
},
"tamper_enabled": {
"name": "Tamper alarm"
}
},
"time": {
Expand Down
11 changes: 11 additions & 0 deletions homeassistant/components/reolink/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,17 @@ class ReolinkSwitchIndexEntityDescription(
value=lambda api, ch: api.pir_reduce_alarm(ch) is True,
method=lambda api, ch, value: api.set_pir(ch, reduce_alarm=value),
),
ReolinkSwitchEntityDescription(
key="tamper_enabled",
cmd_key="763",
cmd_id=763,
translation_key="tamper_enabled",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
supported=lambda api, ch: api.supported(ch, "tamper"),
value=lambda api, ch: api.baichuan.tamper_enabled(ch) is True,
method=lambda api, ch, value: api.baichuan.set_tamper(ch, enable=value),
),
ReolinkSwitchEntityDescription(
key="privacy_mode",
always_available=True,
Expand Down
29 changes: 16 additions & 13 deletions homeassistant/components/rest/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import aiohttp
from aiohttp import hdrs
from multidict import CIMultiDictProxy
from multidict import CIMultiDict, CIMultiDictProxy
import xmltodict

from homeassistant.core import HomeAssistant
Expand All @@ -30,7 +30,7 @@ def __init__(
method: str,
resource: str,
encoding: str,
auth: aiohttp.DigestAuthMiddleware | aiohttp.BasicAuth | tuple[str, str] | None,
auth: aiohttp.DigestAuthMiddleware | tuple[str, str] | None,
headers: dict[str, str] | None,
params: dict[str, str] | None,
data: str | None,
Expand All @@ -45,13 +45,13 @@ def __init__(
self._encoding = encoding
self._force_use_set_encoding = False

# Convert auth tuple to aiohttp.BasicAuth if needed
# Convert an auth tuple to a basic Authorization header if needed
self._basic_auth: str | None = None
self._digest_auth: aiohttp.DigestAuthMiddleware | None = None
if isinstance(auth, tuple) and len(auth) == 2:
self._auth: aiohttp.BasicAuth | aiohttp.DigestAuthMiddleware | None = (
aiohttp.BasicAuth(auth[0], auth[1], encoding="utf-8")
)
else:
self._auth = auth
self._basic_auth = aiohttp.encode_basic_auth(auth[0], auth[1])
elif isinstance(auth, aiohttp.DigestAuthMiddleware):
self._digest_auth = auth

self._headers = headers
self._params = params
Expand Down Expand Up @@ -137,11 +137,14 @@ async def async_update(self, log_errors: bool = True) -> None:
"timeout": self._timeout,
}

# Handle authentication
if isinstance(self._auth, aiohttp.BasicAuth):
request_kwargs["auth"] = self._auth
elif isinstance(self._auth, aiohttp.DigestAuthMiddleware):
request_kwargs["middlewares"] = (self._auth,)
# Handle authentication. A configured Authorization header wins,
# whatever its casing, so setdefault runs on a CIMultiDict.
if self._basic_auth is not None:
headers = CIMultiDict(rendered_headers or {})
headers.setdefault(hdrs.AUTHORIZATION, self._basic_auth)
request_kwargs["headers"] = headers
elif self._digest_auth is not None:
request_kwargs["middlewares"] = (self._digest_auth,)

# Handle data/content
if self._request_data:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/risco/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/risco",
"iot_class": "local_push",
"loggers": ["pyrisco"],
"requirements": ["pyrisco==0.8.0"]
"requirements": ["pyrisco==0.8.1"]
}
3 changes: 3 additions & 0 deletions homeassistant/components/script/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ async def turn_off_service(service: ServiceCall) -> None:
if not script_entities:
return

for script_entity in script_entities:
script_entity.async_set_context(service.context)

await asyncio.wait(
[
create_eager_task(script_entity.async_turn_off())
Expand Down
5 changes: 0 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -458,11 +458,6 @@ filterwarnings = [
# Modify app state for testing
"ignore:Changing state of started or joined application is deprecated:DeprecationWarning:tests.components.http.test_ban",

# -- Tests
# Ignore custom pytest marks
"ignore:Unknown pytest.mark.disable_autouse_fixture:pytest.PytestUnknownMarkWarning:tests.components.met",
"ignore:Unknown pytest.mark.dataset:pytest.PytestUnknownMarkWarning:tests.components.screenlogic",

# -- DeprecationWarning already fixed in our codebase
# https://github.com/kurtmckee/feedparser/ - 6.0.12
"ignore:.*a temporary mapping .* from `updated_parsed` to `published_parsed` if `updated_parsed` doesn't exist:DeprecationWarning:feedparser.util",
Expand Down
6 changes: 3 additions & 3 deletions requirements_all.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion tests/components/aprilaire/test_init.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Tests for the Aprilaire integration setup."""

from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, MagicMock, patch

from pyaprilaire.const import Attribute
from syrupy.assertion import SnapshotAssertion
Expand All @@ -27,6 +27,7 @@ async def test_device_registry(
config_entry.add_to_hass(hass)

client = AsyncMock()
client.stop_listen = MagicMock()
client.data = {
Attribute.MAC_ADDRESS: "1234567890ab",
Attribute.NAME: "Aprilaire",
Expand Down
Loading
Loading