Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1963268
Improve Monzo API errors, icon, and test coverage (#180320)
JakeMartin-ICL Aug 29, 2026
0f7daec
Add missing icons to switches for Midea (#180596)
chemelli74 Aug 29, 2026
c93f3d3
Add initial update platform to Proxmox (#167620)
CoMPaTech Aug 29, 2026
6a5d047
Add diagnostics to SolarEdge Modbus (#180599)
frenck Aug 29, 2026
9575dd0
Migrate Lyngdorf core to the lyngdorf 2.0 API (#180529)
fishloa Aug 29, 2026
51765a1
Fix deviceinfo missing mac and s/n for Midea (#180594)
chemelli74 Aug 29, 2026
d8c3a5d
Remove the vendor status sensor from SolarEdge Modbus (#180607)
frenck Aug 29, 2026
754691c
Gate the Sofar waiting-ends sensor on an active countdown (#180605)
darkrain-nl Aug 29, 2026
193aa51
Update Mealie minimum to 3.2 (#180609)
andrew-codechimp Aug 29, 2026
6b0e635
Ask MCP authorization servers for a refresh token (#180604)
frenck Aug 29, 2026
a84fd45
Add MQTT token pairing to Victron GX (#180590)
tomer-w Aug 29, 2026
eeec7a5
Refresh the Alexa to-do list after writing to it (#180598)
frenck Aug 29, 2026
92983e4
Fix SmartThings AC temperature range unit conversion (#180299)
StellarSea Aug 29, 2026
325bc5f
FIx airflow and power rate availability for Midea (#180613)
chemelli74 Aug 29, 2026
3047889
Report a sleeping Roborock Q7 job as paused (#180432)
ximex Aug 29, 2026
5be2457
Add Besen sensor platform (#180585)
moryoav Aug 29, 2026
790cada
Remove stale xfail in hdmi_cec switch status test (#180532)
Kjubikstronk Aug 29, 2026
0cae37c
Manage the Peblar autocharge vehicle list (#180606)
frenck Aug 29, 2026
7ceb816
Add meter support to SolarEdge Modbus (#180572)
frenck Aug 29, 2026
129ff23
Handle malformed ViCare payloads as an update failure (#180407)
lackas Aug 29, 2026
cba8d3d
Translate the update install and skip errors (#180615)
frenck Aug 29, 2026
6e287f4
Add serial support to SolarEdge Modbus (#180580)
frenck Aug 29, 2026
8094598
Use Home Assistant's shared aiohttp session in Lyngdorf (#180620)
fishloa Aug 29, 2026
1472391
Add DHCP discovery to the Imou integration (#180083)
Imou-OpenPlatform Aug 29, 2026
eac53b5
Add support for priority to SMTP integration (#180358)
tr4nt0r Aug 29, 2026
e05cb08
Recognize AirGradient 0-1PS as an outdoor model (#180313)
samuelbles07 Aug 29, 2026
8813f31
Deprecate Google Wifi uptime sensor (#177525)
EReaso Aug 29, 2026
037d2cf
aws_s3: warmup loader caches to remove blocking listdir calls (#180413)
patrickvorgers Aug 29, 2026
ddee1c2
Add select entities for Sofar charger mode and EPS mode (#180385)
darkrain-nl Aug 29, 2026
84c8c76
Raise Sofar to the silver quality scale (#180292)
darkrain-nl Aug 29, 2026
b999195
Add Persang Infrared integration (#178107)
Dr-Blank Aug 29, 2026
592f806
Specify integration domain in deprecation notice (#180628)
gjohansson-ST Aug 29, 2026
3f020ad
Bump google-maps-routing to 0.11.0 (#180622)
silamon Aug 29, 2026
99ae47a
Fix swallowed exceptions in action handlers for MJPEG IP Camera (#180…
mstu01 Aug 29, 2026
599f9b5
Migrate the Lyngdorf number platform to the lyngdorf 2.0 API (#180627)
fishloa Aug 29, 2026
1273fd5
Fix Growatt V1 total output power (#176513)
HansKDev Aug 29, 2026
5f77b4b
Add water_heater platform to Plugwise (#177480)
bouwew Aug 29, 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
2 changes: 2 additions & 0 deletions CODEOWNERS

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

14 changes: 7 additions & 7 deletions homeassistant/components/airgradient/const.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -71,6 +71,11 @@ class ModelCapabilities:
}
)

OUTDOOR_CAPABILITIES = ModelCapabilities(
config=COMMON_LEGACY_CONFIG,
actions=frozenset({CO2_CALIBRATION}),
)

MODEL_CAPABILITIES: tuple[tuple[str, ModelCapabilities], ...] = (
(
"I-9PSL",
Expand All@@ -86,13 +91,8 @@ class ModelCapabilities:
actions=frozenset({CO2_CALIBRATION, LED_BAR_TEST}),
),
),
(
"O-1",
ModelCapabilities(
config=COMMON_LEGACY_CONFIG,
actions=frozenset({CO2_CALIBRATION}),
),
),
("O-1", OUTDOOR_CAPABILITIES),
("0-1PS", OUTDOOR_CAPABILITIES),
(
"DIY",
ModelCapabilities(
Expand Down
56 changes: 40 additions & 16 deletions homeassistant/components/alexa_devices/coordinator.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
"""Support for Alexa Devices."""

fromasyncioimportLock
fromcollections.abcimportAsyncGenerator
fromcontextlibimportasynccontextmanager
fromdatetimeimporttimedelta
Expand DownExpand Up@@ -167,6 +168,7 @@ def __init__(
}

self._todo_list_items: dict[str, dict[str, AmazonListItem]] = {}
self._todo_refresh_lock=Lock()
self.api.on_todo_event.append(self.todo_event_handler)
self.api.on_todo_event.freeze()

Expand DownExpand Up@@ -308,23 +310,45 @@ async def sync_todo_list_items(self) -> None:
todo_list.id
] =awaitself.api.get_todo_list_items(todo_list.id)

asyncdefrefresh_todo_list_items(self, list_id: str) ->None:
"""Refresh the cached items of a single to-do list.
Cached items are otherwise only filled by the initial sync and by
pushed events, so a write of our own needs a pull to become visible.
The pulls are serialized, as an older answer landing last would leave
the cache behind with nothing to repair it.
"""
asyncwithself._todo_refresh_lock, alexa_api_call(self):
self._todo_list_items[list_id] =awaitself.api.get_todo_list_items(list_id)

# Reading the list back proves the API answers again
self.last_update_success=True

self.async_update_listeners()

asyncdeftodo_event_handler(self, list_event: AmazonListEvent) ->None:
"""Handle changes on To-Do lists."""
iflist_event.type==AmazonListEventType.DELETED:
self._todo_list_items.get(list_event.list_id, {}).pop(
list_event.item_id, None
)
elif (
list_event.type
in (AmazonListEventType.UPDATED, AmazonListEventType.CREATED)
) andlist_event.items:
iflist_event.list_idnotinself._todo_list_items:
# List was newly created after initial sync
self._todo_list_items[list_event.list_id] = {}

self._todo_list_items[list_event.list_id][list_event.item_id] = (
list_event.items
)
"""Handle changes on To-Do lists.
Takes the refresh lock, so an event arriving while a list is being
read back is applied on top of that read instead of under it.
"""
asyncwithself._todo_refresh_lock:
iflist_event.type==AmazonListEventType.DELETED:
self._todo_list_items.get(list_event.list_id, {}).pop(
list_event.item_id, None
)
elif (
list_event.type
in (AmazonListEventType.UPDATED, AmazonListEventType.CREATED)
) andlist_event.items:
iflist_event.list_idnotinself._todo_list_items:
# List was newly created after initial sync
self._todo_list_items[list_event.list_id] = {}

self._todo_list_items[list_event.list_id][list_event.item_id] = (
list_event.items
)

self.async_update_listeners()

Expand Down
110 changes: 65 additions & 45 deletions homeassistant/components/alexa_devices/todo.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -126,32 +126,38 @@ async def async_create_todo_item(self, item: TodoItem) -> None:
self._list.name,
)

await self.coordinator.refresh_todo_list_items(self._list.id)

@override
async def async_delete_todo_items(self, uids: list[str]) -> None:
"""Delete items from the to-do list."""
LOGGER.debug("Called async_delete_todo_items for %s item(s)", len(uids))

list_items_lookup = self.coordinator.todo_list_items[self._list.id]

for uid in uids:
existing_item = list_items_lookup[uid]
try:
for uid in uids:
existing_item = list_items_lookup[uid]

LOGGER.debug(
"Deleting item %s (ID: %s) with version %s",
existing_item.name,
uid,
existing_item.version,
)
async with alexa_api_call(self.coordinator):
await self.coordinator.api.delete_todo_list_item(
self._list.id, uid, existing_item.version
LOGGER.debug(
"Deleting item %s (ID: %s) with version %s",
existing_item.name,
uid,
existing_item.version,
)
LOGGER.debug(
"Successfully deleted item %s (ID: %s) with version %s",
existing_item.name,
uid,
existing_item.version,
)
async with alexa_api_call(self.coordinator):
await self.coordinator.api.delete_todo_list_item(
self._list.id, uid, existing_item.version
)
LOGGER.debug(
"Successfully deleted item %s (ID: %s) with version %s",
existing_item.name,
uid,
existing_item.version,
)
finally:
# A later delete can fail after an earlier one went through
await self.coordinator.refresh_todo_list_items(self._list.id)

@override
async def async_update_todo_item(self, item: TodoItem) -> None:
Expand All@@ -166,39 +172,53 @@ async def async_update_todo_item(self, item: TodoItem) -> None:

existing_item = list_items_lookup[item.uid]

if has_completed_changed := (
has_completed_changed = (
existing_item.status == AmazonListItemStatus.COMPLETE
) != (item.status == TodoItemStatus.COMPLETED):
# Update the checked status
LOGGER.debug(
"Updating item %s with checked status %s", item.uid, item.status
)
) != (item.status == TodoItemStatus.COMPLETED)
has_renamed = existing_item.name != item.summary

async with alexa_api_call(self.coordinator):
await self.coordinator.api.set_todo_list_item_checked_status(
self._list.id,
item.uid,
item.status == TodoItemStatus.COMPLETED,
existing_item.version,
if not has_completed_changed and not has_renamed:
return

try:
if has_completed_changed:
# Update the checked status
LOGGER.debug(
"Updating item %s with checked status %s", item.uid, item.status
)

LOGGER.debug(
"Successfully updated item %s with checked status %s",
item.uid,
item.status,
)
async with alexa_api_call(self.coordinator):
await self.coordinator.api.set_todo_list_item_checked_status(
self._list.id,
item.uid,
item.status == TodoItemStatus.COMPLETED,
existing_item.version,
)

LOGGER.debug(
"Successfully updated item %s with checked status %s",
item.uid,
item.status,
)

if existing_item.name != item.summary:
# Name has changed, update it
LOGGER.debug("Updating item %s with new name %s", item.uid, item.summary)
if has_renamed:
# Name has changed, update it
LOGGER.debug(
"Updating item %s with new name %s", item.uid, item.summary
)

# If both have changed -> Increase item version by 1
version = existing_item.version + int(has_completed_changed)
# If both have changed -> Increase item version by 1
version = existing_item.version + int(has_completed_changed)

async with alexa_api_call(self.coordinator):
await self.coordinator.api.rename_todo_list_item(
self._list.id, item.uid, item.summary, version
async with alexa_api_call(self.coordinator):
await self.coordinator.api.rename_todo_list_item(
self._list.id, item.uid, item.summary, version
)
LOGGER.debug(
"Successfully updated item %s with new name %s",
item.uid,
item.summary,
)
LOGGER.debug(
"Successfully updated item %s with new name %s", item.uid, item.summary
)
finally:
# A rename can fail after the status change went through
await self.coordinator.refresh_todo_list_items(self._list.id)
2 changes: 2 additions & 0 deletions homeassistant/components/aws_s3/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@
import logging
from typing import cast

from aiobotocore.config import AioConfig
from aiobotocore.session import AioSession
from botocore.exceptions import ClientError, ConnectionError, ParamValidationError

Expand DownExpand Up@@ -37,6 +38,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: S3ConfigEntry) -> bool:
endpoint_url=data.get(CONF_ENDPOINT_URL),
aws_secret_access_key=data[CONF_SECRET_ACCESS_KEY],
aws_access_key_id=data[CONF_ACCESS_KEY_ID],
config=AioConfig(warm_up_loader_caches=True),
).__aenter__()
await client.head_bucket(Bucket=data[CONF_BUCKET])
except ClientError as err:
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/aws_s3/config_flow.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@
from typing import Any, override
from urllib.parse import urlparse

from aiobotocore.config import AioConfig
from aiobotocore.session import AioSession
from botocore.exceptions import ClientError, ConnectionError, ParamValidationError
import voluptuous as vol
Expand DownExpand Up@@ -77,6 +78,7 @@ async def async_step_user(
endpoint_url=user_input.get(CONF_ENDPOINT_URL),
aws_secret_access_key=user_input[CONF_SECRET_ACCESS_KEY],
aws_access_key_id=user_input[CONF_ACCESS_KEY_ID],
config=AioConfig(warm_up_loader_caches=True),
) as client:
await client.head_bucket(Bucket=user_input[CONF_BUCKET])
except ClientError:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/besen/const.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,4 +7,4 @@
DOMAIN: Final = "besen"
NAME: Final = "Besen"

PLATFORMS: Final = [Platform.SWITCH]
PLATFORMS: Final = [Platform.SENSOR, Platform.SWITCH]
2 changes: 1 addition & 1 deletion homeassistant/components/besen/manifest.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,5 +14,5 @@
"integration_type": "device",
"iot_class": "local_push",
"quality_scale": "bronze",
"requirements": ["besen==0.3.4"]
"requirements": ["besen==0.4.0"]
}
Loading
Loading