Skip to content
Merged

2025.12.2 #158274

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
660a14e
fix Lutron Caseta smart away subscription (#158082)
omrishiv Dec 8, 2025
aac412f
Fix legacy template entity_id field in migration (#158105)
Petro31 Dec 8, 2025
f7f7f9a
Revert "Remove Shelly redundant device entry check for sleepy devices…
thecode Dec 6, 2025
a933d4a
Ensure Roborock disconnects mqtt on unload/stop (#158144)
allenporter Dec 8, 2025
9824bdc
Fix secure URLs for promotional game media in Xbox integration (#158162)
tr4nt0r Dec 7, 2025
ffebbab
Add program id codes for Miele WQ1000 (#158175)
astrandb Dec 7, 2025
81e47f6
Bump pymiele dependency to 0.6.1 (#158177)
astrandb Dec 7, 2025
50eee75
Bump asusrouter to 1.21.1 (#158192)
Vaskivskyi Dec 8, 2025
f049c42
Bump HueBLE to 2.1.0 (#158197)
flip-dots Dec 8, 2025
eaef016
Bump python-roborock to 3.10.10 (#158212)
allenporter Dec 8, 2025
7f37412
Be more specific about winter mode in the description (#158230)
piitaya Dec 8, 2025
e4f1565
Fix description placeholders for system_bridge (#158232)
jbouwh Dec 8, 2025
7492b5b
Bump google air quality api to 2.0.0 (#158234)
Thomas55555 Dec 8, 2025
886e2b0
Fix zwave_js service description placeholders (#158236)
epenet Dec 8, 2025
68711b2
Fix yeelight service description placeholders (#158239)
epenet Dec 8, 2025
ab0811f
Fix teslemetry service description placeholders (#158240)
epenet Dec 8, 2025
1a11b92
Fix multiple top-level support for template integration (#158244)
Petro31 Dec 8, 2025
ac0a544
Bump yt-dlp to 2025.12.08 (#158253)
andreimoraru Dec 8, 2025
24de26c
Update frontend to 20251203.2 (#158259)
piitaya Dec 8, 2025
201c378
Skip check for onboarding done in Music Assistant integration (#158270)
marcelveldt Dec 8, 2025
a2b5744
Bump version to 2025.12.2
frenck Dec 8, 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/asuswrt/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aioasuswrt", "asusrouter", "asyncssh"],
"requirements": ["aioasuswrt==1.5.1", "asusrouter==1.21.0"]
"requirements": ["aioasuswrt==1.5.1", "asusrouter==1.21.1"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/frontend/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@
"winter_mode": {}
},
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20251203.1"]
"requirements": ["home-assistant-frontend==20251203.2"]
}
6 changes: 3 additions & 3 deletions homeassistant/components/frontend/strings.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"preview_features": {
"winter_mode": {
"description": "Adds falling snowflakes on your screen. Get your home ready for winter! ❄️",
"disable_confirmation": "Snowflakes will no longer fall on your screen. You can re-enable this at any time in labs settings.",
"enable_confirmation": "Snowflakes will start falling on your screen. You can turn this off at any time in labs settings.",
"description": "Adds falling snowflakes on your screen. Get your home ready for winter! ❄️\n\nIf you have animations disabled in your device accessibility settings, this feature will not work.",
"disable_confirmation": "Snowflakes will no longer fall on your screen. You can re-enable this at any time in Labs settings.",
"enable_confirmation": "Snowflakes will start falling on your screen. You can turn this off at any time in Labs settings.",
"name": "Winter mode"
}
},
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/google_air_quality/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ async def _validate_input(
description_placeholders: dict[str, str],
) -> bool:
try:
await api.async_air_quality(
await api.async_get_current_conditions(
lat=user_input[CONF_LOCATION][CONF_LATITUDE],
long=user_input[CONF_LOCATION][CONF_LONGITUDE],
lon=user_input[CONF_LOCATION][CONF_LONGITUDE],
)
except GoogleAirQualityApiError as err:
errors["base"] = "cannot_connect"
Expand Down
10 changes: 6 additions & 4 deletions homeassistant/components/google_air_quality/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from google_air_quality_api.api import GoogleAirQualityApi
from google_air_quality_api.exceptions import GoogleAirQualityApiError
from google_air_quality_api.model import AirQualityData
from google_air_quality_api.model import AirQualityCurrentConditionsData

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
Expand All @@ -23,7 +23,9 @@
type GoogleAirQualityConfigEntry = ConfigEntry[GoogleAirQualityRuntimeData]


class GoogleAirQualityUpdateCoordinator(DataUpdateCoordinator[AirQualityData]):
class GoogleAirQualityUpdateCoordinator(
DataUpdateCoordinator[AirQualityCurrentConditionsData]
):
"""Coordinator for fetching Google AirQuality data."""

config_entry: GoogleAirQualityConfigEntry
Expand All @@ -48,10 +50,10 @@ def __init__(
self.lat = subentry.data[CONF_LATITUDE]
self.long = subentry.data[CONF_LONGITUDE]

async def _async_update_data(self) -> AirQualityData:
async def _async_update_data(self) -> AirQualityCurrentConditionsData:
"""Fetch air quality data for this coordinate."""
try:
return await self.client.async_air_quality(self.lat, self.long)
return await self.client.async_get_current_conditions(self.lat, self.long)
except GoogleAirQualityApiError as ex:
_LOGGER.debug("Cannot fetch air quality data: %s", str(ex))
raise UpdateFailed(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/google_air_quality/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["google_air_quality_api"],
"quality_scale": "bronze",
"requirements": ["google_air_quality_api==1.1.3"]
"requirements": ["google_air_quality_api==2.0.0"]
}
18 changes: 10 additions & 8 deletions homeassistant/components/google_air_quality/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from dataclasses import dataclass
import logging

from google_air_quality_api.model import AirQualityData
from google_air_quality_api.model import AirQualityCurrentConditionsData

from homeassistant.components.sensor import (
SensorDeviceClass,
Expand Down Expand Up @@ -33,15 +33,17 @@
class AirQualitySensorEntityDescription(SensorEntityDescription):
"""Describes Air Quality sensor entity."""

exists_fn: Callable[[AirQualityData], bool] = lambda _: True
options_fn: Callable[[AirQualityData], list[str] | None] = lambda _: None
value_fn: Callable[[AirQualityData], StateType]
native_unit_of_measurement_fn: Callable[[AirQualityData], str | None] = (
exists_fn: Callable[[AirQualityCurrentConditionsData], bool] = lambda _: True
options_fn: Callable[[AirQualityCurrentConditionsData], list[str] | None] = (
lambda _: None
)
translation_placeholders_fn: Callable[[AirQualityData], dict[str, str]] | None = (
None
)
value_fn: Callable[[AirQualityCurrentConditionsData], StateType]
native_unit_of_measurement_fn: Callable[
[AirQualityCurrentConditionsData], str | None
] = lambda _: None
translation_placeholders_fn: (
Callable[[AirQualityCurrentConditionsData], dict[str, str]] | None
) = None


AIR_QUALITY_SENSOR_TYPES: tuple[AirQualitySensorEntityDescription, ...] = (
Expand Down
13 changes: 10 additions & 3 deletions homeassistant/components/hue_ble/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import logging

from HueBLE import HueBleLight
from HueBLE import ConnectionError, HueBleError, HueBleLight

from homeassistant.components.bluetooth import (
async_ble_device_from_address,
Expand Down Expand Up @@ -38,8 +38,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: HueBLEConfigEntry) -> bo

light = HueBleLight(ble_device)

if not await light.connect() or not await light.poll_state():
raise ConfigEntryNotReady("Device found but unable to connect.")
try:
await light.connect()
await light.poll_state()
except ConnectionError as e:
raise ConfigEntryNotReady("Device found but unable to connect.") from e
except HueBleError as e:
raise ConfigEntryNotReady(
"Device found and connected but unable to poll values from it."
) from e

entry.runtime_data = light

Expand Down
41 changes: 16 additions & 25 deletions homeassistant/components/hue_ble/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import logging
from typing import Any

from HueBLE import HueBleLight
from HueBLE import ConnectionError, HueBleError, HueBleLight, PairingError
import voluptuous as vol

from homeassistant.components import bluetooth
Expand All @@ -20,7 +20,7 @@
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr

from .const import DOMAIN, URL_PAIRING_MODE
from .const import DOMAIN, URL_FACTORY_RESET, URL_PAIRING_MODE
from .light import get_available_color_modes

_LOGGER = logging.getLogger(__name__)
Expand All @@ -41,32 +41,22 @@ async def validate_input(hass: HomeAssistant, address: str) -> Error | None:

try:
light = HueBleLight(ble_device)

await light.connect()

if light.authenticated is None:
_LOGGER.warning(
"Unable to determine if light authenticated, proceeding anyway"
)
elif not light.authenticated:
return Error.INVALID_AUTH

if not light.connected:
return Error.CANNOT_CONNECT

try:
get_available_color_modes(light)
except HomeAssistantError:
return Error.NOT_SUPPORTED

_, errors = await light.poll_state()
if len(errors) != 0:
_LOGGER.warning("Errors raised when connecting to light: %s", errors)
return Error.CANNOT_CONNECT

except Exception:
get_available_color_modes(light)
await light.poll_state()

except ConnectionError as e:
_LOGGER.exception("Error connecting to light")
return (
Error.INVALID_AUTH
if type(e.__cause__) is PairingError
else Error.CANNOT_CONNECT
)
except HueBleError:
_LOGGER.exception("Unexpected error validating light connection")
return Error.UNKNOWN
except HomeAssistantError:
return Error.NOT_SUPPORTED
else:
return None
finally:
Expand Down Expand Up @@ -129,6 +119,7 @@ async def async_step_confirm(
CONF_NAME: self._discovery_info.name,
CONF_MAC: self._discovery_info.address,
"url_pairing_mode": URL_PAIRING_MODE,
"url_factory_reset": URL_FACTORY_RESET,
},
)

Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/hue_ble/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

DOMAIN = "hue_ble"
URL_PAIRING_MODE = "https://www.home-assistant.io/integrations/hue_ble#initial-setup"
URL_FACTORY_RESET = "https://www.philips-hue.com/en-gb/support/article/how-to-factory-reset-philips-hue-lights/000004"
2 changes: 1 addition & 1 deletion homeassistant/components/hue_ble/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def _state_change_callback(self) -> None:

async def async_update(self) -> None:
"""Fetch latest state from light and make available via properties."""
await self._api.poll_state(run_callbacks=True)
await self._api.poll_state()

async def async_turn_on(self, **kwargs: Any) -> None:
"""Set properties then turn the light on."""
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/hue_ble/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"iot_class": "local_push",
"loggers": ["bleak", "HueBLE"],
"quality_scale": "bronze",
"requirements": ["HueBLE==1.0.8"]
"requirements": ["HueBLE==2.1.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/hue_ble/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"step": {
"confirm": {
"description": "Do you want to set up {name} ({mac})?. Make sure the light is [made discoverable to voice assistants]({url_pairing_mode})."
"description": "Do you want to set up {name} ({mac})?. Make sure the light is [made discoverable to voice assistants]({url_pairing_mode}) or has been [factory reset]({url_factory_reset})."
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion homeassistant/components/lutron_caseta/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ def unique_id(self) -> str:
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
await super().async_added_to_hass()
self._smartbridge.add_smart_away_subscriber(self._handle_bridge_update)
self._smartbridge.add_smart_away_subscriber(self._handle_smart_away_update)

def _handle_smart_away_update(self, smart_away_state: str | None = None) -> None:
"""Handle updated smart away state from the bridge."""
self.async_write_ha_state()

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn Smart Away on."""
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/media_extractor/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"iot_class": "calculated",
"loggers": ["yt_dlp"],
"quality_scale": "internal",
"requirements": ["yt-dlp[default]==2025.11.12"],
"requirements": ["yt-dlp[default]==2025.12.08"],
"single_config_entry": true
}
43 changes: 27 additions & 16 deletions homeassistant/components/miele/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ class ProgramPhaseWashingMachine(MieleEnum, missing_to_none=True):
drying = 280
disinfecting = 285
flex_load_active = 11047
automatic_start = 11044


class ProgramPhaseTumbleDryer(MieleEnum, missing_to_none=True):
Expand Down Expand Up @@ -451,19 +452,19 @@ class WashingMachineProgramId(MieleEnum, missing_to_none=True):
"""Program Id codes for washing machines."""

no_program = 0, -1
cottons = 1
cottons = 1, 10001
minimum_iron = 3
delicates = 4
woollens = 8
silks = 9
delicates = 4, 10022
woollens = 8, 10040
silks = 9, 10042
starch = 17
rinse = 18
drain_spin = 21
curtains = 22
shirts = 23
rinse = 18, 10058
drain_spin = 21, 10036
curtains = 22, 10055
shirts = 23, 10038
denim = 24, 123
proofing = 27
sportswear = 29
proofing = 27, 10057
sportswear = 29, 10052
automatic_plus = 31
outerwear = 37
pillows = 39
Expand All @@ -472,19 +473,29 @@ class WashingMachineProgramId(MieleEnum, missing_to_none=True):
rinse_out_lint = 48 # washer-dryer
dark_garments = 50
separate_rinse_starch = 52
first_wash = 53
first_wash = 53, 10053
cottons_hygiene = 69
steam_care = 75 # washer-dryer
freshen_up = 76 # washer-dryer
trainers = 77
clean_machine = 91
down_duvets = 95
express_20 = 122
trainers = 77, 10056
clean_machine = 91, 10067
down_duvets = 95, 10050
express_20 = 122, 10029
down_filled_items = 129
cottons_eco = 133
quick_power_wash = 146, 10031
eco_40_60 = 190, 10007
normal = 10001
bed_linen = 10047
easy_care = 10016
dark_jeans = 10048
outdoor_garments = 10049
game_pieces = 10070
stuffed_toys = 10069
pre_ironing = 10059
trainers_refresh = 10066
smartmatic = 10068
cottonrepair = 10065
powerfresh = 10075


class DishWasherProgramId(MieleEnum, missing_to_none=True):
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/miele/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"iot_class": "cloud_push",
"loggers": ["pymiele"],
"quality_scale": "platinum",
"requirements": ["pymiele==0.6.0"],
"requirements": ["pymiele==0.6.1"],
"single_config_entry": true,
"zeroconf": ["_mieleathome._tcp.local."]
}
Loading
Loading