Skip to content
Merged

2025.12.5 #159934

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2516e80
Disable quoted cookies for compatibility with older SolarLog devices …
dontinelli Dec 22, 2025
8508d48
Normalize unique ID in WLED (#157901)
mik-laj Dec 23, 2025
ea9ac1d
Change Samsung TV WoL turn_on log from warning to debug (#158676)
ptarjan Dec 23, 2025
cdb7b9c
Fix ZeroDivisionError for inverse unit conversions (#159161)
ReneNulschDE Dec 23, 2025
35ba9c7
Add openid scope and update OAuth2 url:s in senz integration (#159265)
astrandb Dec 29, 2025
d8b2d02
Create issue for Sonos when Sonos system does not have UPnP enabled (…
PeteRager Dec 23, 2025
cba3313
Bump insteon panel to 0.6.0 to fix dialog button issues (#159449)
teharris1 Dec 20, 2025
fc5c31b
Bump yalexs-ble to 3.2.2 (#158124)
bdraco Dec 6, 2025
0f61b68
Bump yalexs-ble to 3.2.4 (#159476)
bdraco Dec 20, 2025
960049e
Improve date handling in UniFi Protect media source (#159491)
RaHehl Dec 23, 2025
e16335f
Redact additional unnecessary diagnostic fields (#159546)
allenporter Dec 21, 2025
584687f
Bump melissa to 3.0.3 (#159557)
kennedyshead Dec 22, 2025
4c60746
Bump valbusaio to 2025.12.0 (#159578)
cereal2nd Dec 22, 2025
6db227e
Bump uiprotect to 7.33.3 (#159593)
RaHehl Dec 22, 2025
efee515
Fix Ecoforest unknown alarm translation key (#159594)
maylikenoother Dec 22, 2025
9595cf3
Bump axis to v66 fixing an issue with latest xmltodict (#159604)
Kane610 Dec 22, 2025
3b0ebca
Bump python-roborock to 3.20.1 (#159621)
allenporter Dec 23, 2025
e329eab
Bump python-roborock to 3.21.1 (#159660)
allenporter Dec 24, 2025
7d17f0a
Fix Roborock repair issue behavior (#159718)
allenporter Dec 24, 2025
5aa0eef
Start reauth when roborock notices the MQTT session is unauthorized (…
allenporter Dec 25, 2025
e9d2f6a
Add state_class to Nuki battery sensor (#159756)
victor-gurbani Dec 26, 2025
de97a94
Bump version to 2025.12.5
frenck Dec 29, 2025
5df0385
CI fix: Exempt caio from license check (#159866)
zweckj Dec 28, 2025
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
2 changes: 1 addition & 1 deletion homeassistant/components/august/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.2.1"]
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.2.4"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/axis/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["axis"],
"requirements": ["axis==65"],
"requirements": ["axis==66"],
"ssdp": [
{
"manufacturer": "AXIS"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/dynalite/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ async def async_register_dynalite_frontend(hass: HomeAssistant):
frontend_url_path=DOMAIN,
config_panel_domain=DOMAIN,
webcomponent_name="dynalite-panel",
module_url=f"{URL_BASE}/entrypoint-{build_id}.js",
module_url=f"{URL_BASE}/entrypoint.{build_id}.js",
embed_iframe=True,
require_admin=True,
)
2 changes: 1 addition & 1 deletion homeassistant/components/ecoforest/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"cpu_overheating": "CPU overheating",
"none": "None",
"pellets": "Pellets",
"unkownn": "Unknown alarm"
"unknown": "Unknown alarm"
}
},
"convector_air_flow": {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/insteon/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ async def async_register_insteon_frontend(hass: HomeAssistant):
frontend_url_path=DOMAIN,
webcomponent_name="insteon-frontend",
config_panel_domain=DOMAIN,
module_url=f"{URL_BASE}/entrypoint-{build_id}.js",
module_url=f"{URL_BASE}/entrypoint.{build_id}.js",
embed_iframe=True,
require_admin=True,
)
2 changes: 2 additions & 0 deletions homeassistant/components/insteon/api/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ async def websocket_reset_properties(
notify_device_not_found(connection, msg, INSTEON_DEVICE_NOT_FOUND)
return

for prop in device.configuration.values():
prop.new_value = None
for prop in device.operating_flags:
device.operating_flags[prop].new_value = None
for prop in device.properties:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/insteon/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"loggers": ["pyinsteon", "pypubsub"],
"requirements": [
"pyinsteon==1.6.3",
"insteon-frontend-home-assistant==0.5.0"
"insteon-frontend-home-assistant==0.6.0"
],
"single_config_entry": true,
"usb": [
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/melissa/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"iot_class": "cloud_polling",
"loggers": ["melissa"],
"quality_scale": "legacy",
"requirements": ["py-melissa-climate==3.0.2"]
"requirements": ["py-melissa-climate==3.0.3"]
}
7 changes: 6 additions & 1 deletion homeassistant/components/nuki/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

from pynuki.device import NukiDevice

from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE, EntityCategory
from homeassistant.core import HomeAssistant
Expand Down Expand Up @@ -34,6 +38,7 @@ class NukiBatterySensor(NukiEntity[NukiDevice], SensorEntity):
_attr_has_entity_name = True
_attr_native_unit_of_measurement = PERCENTAGE
_attr_device_class = SensorDeviceClass.BATTERY
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_entity_category = EntityCategory.DIAGNOSTIC

@property
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/roborock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: RoborockConfigEntry) ->
show_background=entry.options.get(CONF_SHOW_BACKGROUND, False),
map_scale=MAP_SCALE,
),
mqtt_session_unauthorized_hook=lambda: entry.async_start_reauth(hass),
)
except RoborockInvalidCredentials as err:
raise ConfigEntryAuthFailed(
Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/roborock/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def __init__(
# Tracks the last successful update to control when we report failure
# to the base class. This is reset on successful data update.
self._last_update_success_time: datetime | None = None
self._has_connected_locally: bool = False

@cached_property
def dock_device_info(self) -> DeviceInfo:
Expand Down Expand Up @@ -180,7 +181,8 @@ async def update_map(self) -> None:
async def _verify_api(self) -> None:
"""Verify that the api is reachable."""
if self._device.is_connected:
if self._device.is_local_connected:
self._has_connected_locally |= self._device.is_local_connected
if self._has_connected_locally:
async_delete_issue(
self.hass, DOMAIN, f"cloud_api_used_{self.duid_slug}"
)
Expand Down Expand Up @@ -223,6 +225,7 @@ async def _update_device_prop(self) -> None:

async def _async_update_data(self) -> DeviceState:
"""Update data via library."""
await self._verify_api()
try:
# Update device props and standard api information
await self._update_device_prop()
Expand Down
17 changes: 16 additions & 1 deletion homeassistant/components/roborock/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,22 @@

_LOGGER = logging.getLogger(__name__)

TO_REDACT_CONFIG = ["token", "sn", "rruid", CONF_UNIQUE_ID, "username", "uid"]
TO_REDACT_CONFIG = [
"token",
"sn",
"rruid",
CONF_UNIQUE_ID,
"username",
"uid",
"h",
"k",
"s",
"u",
"avatarurl",
"nickname",
"tuyaUuid",
"extra",
]


async def async_get_config_entry_diagnostics(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/roborock/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"loggers": ["roborock"],
"quality_scale": "silver",
"requirements": [
"python-roborock==3.19.0",
"python-roborock==3.21.0",
"vacuum-map-parser-roborock==0.1.4"
]
}
6 changes: 2 additions & 4 deletions homeassistant/components/samsungtv/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,8 @@ async def async_turn_on(self, **kwargs: Any) -> None:
LOGGER.debug("Attempting to turn on %s via automation", self.entity_id)
await self._turn_on_action.async_run(self.hass, self._context)
elif self._mac:
LOGGER.warning(
"Attempting to turn on %s via Wake-On-Lan; if this does not work, "
"please ensure that Wake-On-Lan is available for your device or use "
"a turn_on automation",
LOGGER.debug(
"Attempting to turn on %s via Wake-On-Lan",
self.entity_id,
)
await self.hass.async_add_executor_job(self._wake_on_lan)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/senz/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from http import HTTPStatus
import logging

from aiosenz import SENZAPI, Thermostat
from httpx import HTTPStatusError, RequestError
import jwt
from pysenz import SENZAPI, Thermostat

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/senz/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

from typing import cast

from aiosenz import AbstractSENZAuth
from httpx import AsyncClient
from pysenz import AbstractSENZAuth

from homeassistant.helpers import config_entry_oauth2_flow

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/senz/application_credentials.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Application credentials platform for senz."""

from aiosenz import AUTHORIZATION_ENDPOINT, TOKEN_ENDPOINT
from pysenz import AUTHORIZATION_ENDPOINT, TOKEN_ENDPOINT

from homeassistant.components.application_credentials import AuthorizationServer
from homeassistant.core import HomeAssistant
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/senz/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

from typing import Any

from aiosenz import MODE_AUTO, Thermostat
from httpx import RequestError
from pysenz import MODE_AUTO, Thermostat

from homeassistant.components.climate import (
ClimateEntity,
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/senz/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def logger(self) -> logging.Logger:
@property
def extra_authorize_data(self) -> dict:
"""Extra data that needs to be appended to the authorize url."""
return {"scope": "restapi offline_access"}
return {"scope": "restapi offline_access openid"}

async def async_step_reauth(
self, entry_data: Mapping[str, Any]
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/senz/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"dependencies": ["application_credentials"],
"documentation": "https://www.home-assistant.io/integrations/senz",
"iot_class": "cloud_polling",
"loggers": ["aiosenz"],
"requirements": ["aiosenz==1.0.0"]
"loggers": ["pysenz"],
"requirements": ["pysenz==1.0.2"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/senz/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from collections.abc import Callable
from dataclasses import dataclass

from aiosenz import Thermostat
from pysenz import Thermostat

from homeassistant.components.sensor import (
SensorDeviceClass,
Expand Down
7 changes: 5 additions & 2 deletions homeassistant/components/solarlog/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import logging
from urllib.parse import ParseResult, urlparse

from aiohttp import CookieJar
from solarlog_cli.solarlog_connector import SolarLogConnector
from solarlog_cli.solarlog_exceptions import (
SolarLogAuthenticationError,
Expand All @@ -20,7 +21,7 @@
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import slugify

Expand Down Expand Up @@ -63,7 +64,9 @@ def __init__(self, hass: HomeAssistant, config_entry: SolarlogConfigEntry) -> No
self.host,
tz=hass.config.time_zone,
password=password,
session=async_get_clientsession(hass),
session=async_create_clientsession(
hass, cookie_jar=CookieJar(quote_cookie=False)
),
)

async def _async_setup(self) -> None:
Expand Down
64 changes: 56 additions & 8 deletions homeassistant/components/sonos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
import asyncio
import datetime
from functools import partial
from http import HTTPStatus
from ipaddress import AddressValueError, IPv4Address
import logging
import socket
from typing import Any, cast
from urllib.parse import urlparse

from aiohttp import ClientError
from requests.exceptions import Timeout
from requests.exceptions import HTTPError, Timeout
from soco import events_asyncio, zonegroupstate
import soco.config as soco_config
from soco.core import SoCo
Expand Down Expand Up @@ -54,6 +55,8 @@
SUB_FAIL_ISSUE_ID,
SUB_FAIL_URL,
SUBSCRIPTION_TIMEOUT,
UPNP_DOCUMENTATION_URL,
UPNP_ISSUE_ID,
UPNP_ST,
)
from .exception import SonosUpdateError
Expand Down Expand Up @@ -184,6 +187,32 @@ def is_device_invisible(self, ip_address: str) -> bool:
"""Check if device at provided IP is known to be invisible."""
return any(x for x in self._known_invisible if x.ip_address == ip_address)

async def _process_http_connection_error(
self, err: HTTPError, ip_address: str
) -> None:
"""Process HTTP Errors when connecting to a Sonos speaker."""
response = err.response
# When UPnP is disabled, Sonos returns HTTP 403 Forbidden error.
# Create issue advising user to enable UPnP on Sonos system.
if response is not None and response.status_code == HTTPStatus.FORBIDDEN:
ir.async_create_issue(
self.hass,
DOMAIN,
f"{UPNP_ISSUE_ID}_{ip_address}",
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="upnp_disabled",
translation_placeholders={
"device_ip": ip_address,
"documentation_url": UPNP_DOCUMENTATION_URL,
},
)
_LOGGER.error(
"HTTP error connecting to Sonos speaker at %s: %s",
ip_address,
err,
)

async def async_subscribe_to_zone_updates(self, ip_address: str) -> None:
"""Test subscriptions and create SonosSpeakers based on results."""
try:
Expand All @@ -195,13 +224,29 @@ async def async_subscribe_to_zone_updates(self, ip_address: str) -> None:
)
return
soco = SoCo(ip_address)
# Cache now to avoid household ID lookup during first ZoneGroupState processing
await self.hass.async_add_executor_job(
getattr,
soco,
"household_id",
)
sub = await soco.zoneGroupTopology.subscribe()
try:
# Cache now to avoid household ID lookup during first ZoneGroupState processing
await self.hass.async_add_executor_job(
getattr,
soco,
"household_id",
)
sub = await soco.zoneGroupTopology.subscribe()
except HTTPError as err:
await self._process_http_connection_error(err, ip_address)
return
except (
OSError,
SoCoException,
Timeout,
TimeoutError,
) as err:
_LOGGER.error(
"Error connecting to discovered Sonos speaker at %s: %s",
ip_address,
err,
)
return

@callback
def _async_add_visible_zones(subscription_succeeded: bool = False) -> None:
Expand Down Expand Up @@ -390,6 +435,9 @@ async def async_poll_manual_hosts(
sync_get_visible_zones,
soco,
)
except HTTPError as err:
await self._process_http_connection_error(err, ip_addr)
continue
except (
OSError,
SoCoException,
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/sonos/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
Platform.SWITCH,
]

UPNP_ISSUE_ID = "upnp_disabled"
UPNP_DOCUMENTATION_URL = "https://www.home-assistant.io/integrations/sonos/#403-error-when-setting-up-the-integration"

SUB_FAIL_ISSUE_ID = "subscriptions_failed"
SUB_FAIL_URL = "https://www.home-assistant.io/integrations/sonos/#network-requirements"

Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/sonos/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@
"subscriptions_failed": {
"description": "Falling back to polling, functionality may be limited.\n\nSonos device at {device_ip} cannot reach Home Assistant at {listener_address}.\n\nSee our [documentation]({sub_fail_url}) for more information on how to solve this issue.",
"title": "Networking error: subscriptions failed"
},
"upnp_disabled": {
"description": "Unable to connect to Sonos speaker at {device_ip}.\n\nPlease ensure UPnP is enabled on your Sonos system.\n\nOpen the Sonos app on your phone or tablet. Go to Account > Privacy and Security > UPnP. Enable the UPnP setting. Once UPnP is enabled, return to Home Assistant and reload the Sonos integration. The connection should now succeed. See our [documentation]({documentation_url}) for steps to resolve this issue.",
"title": "Networking error: UPnP disabled"
}
},
"services": {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/unifiprotect/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"integration_type": "hub",
"iot_class": "local_push",
"loggers": ["uiprotect", "unifi_discovery"],
"requirements": ["uiprotect==7.33.2", "unifi-discovery==1.2.0"],
"requirements": ["uiprotect==7.33.3", "unifi-discovery==1.2.0"],
"ssdp": [
{
"manufacturer": "Ubiquiti Networks",
Expand Down
Loading
Loading