Skip to content
Merged
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
0a758ee
Add reconfigure flow to Gatus (#176646)
TN-1 Jul 17, 2026
a80f068
Use modern device registry API to remove devices (part 4) (#176673)
emontnemery Jul 17, 2026
d1b8881
change suez price to price / m³ (#176611)
alaunay Jul 17, 2026
40e243a
Add LiteLLM integration (#172960)
luismalves Jul 17, 2026
061d5ff
Fix logging level per Mikrotik (#176655)
chemelli74 Jul 17, 2026
ee03b91
Mark Netatmo entities unavailable when device or service is unreachab…
cgtobi Jul 17, 2026
26f2868
Use modern device registry API to remove devices (part 2) (#176671)
emontnemery Jul 17, 2026
880fa62
Use modern device registry API to remove devices (part 1) (#176669)
emontnemery Jul 17, 2026
a899b1e
Use modern device registry API to remove devices (part 3) (#176672)
emontnemery Jul 17, 2026
b3b2e35
Speed up entity_registry.async_entries_for_device (#176653)
emontnemery Jul 17, 2026
cea06f5
Add vibration conditions (#176598)
nielsrowinbik Jul 17, 2026
76ae70b
Add TextSelector in config flow SMA (#176660)
erwindouna Jul 17, 2026
e893a22
Make repairs not persistent in FRITZ!Box Tools (#176623)
mib1185 Jul 17, 2026
0d3254d
Update ruff (#176645)
renovate[bot] Jul 17, 2026
27ea379
Bump pyairnow to 1.4.0 for AirNow 2026 API endpoints (#176622)
derekcentrico Jul 17, 2026
176c773
Use modern device registry API for device move in wolflink (#176665)
emontnemery Jul 17, 2026
9a7107d
Bump python-duco-connectivity to 0.10.0 (#176685)
ronaldvdmeer Jul 17, 2026
ddf73de
Add manufacturer, model, and model_id filtering to entity filter sele…
Skaronator Jul 17, 2026
cb6a518
Deprecate no longer working device helpers (#176696)
emontnemery Jul 17, 2026
1492fc3
Refactor imou to use entity descriptions (#176675)
joostlek Jul 17, 2026
ccf7fcb
Bump pyportainer to 1.0.42 (#176694)
erwindouna Jul 17, 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: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.20
rev: v0.15.21
hooks:
- id: ruff-check
args:
Expand Down
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ homeassistant.components.lifx.*
homeassistant.components.light.*
homeassistant.components.linkplay.*
homeassistant.components.litejet.*
homeassistant.components.litellm.*
homeassistant.components.litterrobot.*
homeassistant.components.llama_cpp.*
homeassistant.components.local_ip.*
Expand Down
2 changes: 2 additions & 0 deletions CODEOWNERS

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

4 changes: 1 addition & 3 deletions homeassistant/components/aidot/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,4 @@ def _purge_deleted_lists(self) -> None:
):
if not set(device.identifiers) & identifiers:
_LOGGER.debug("Removing obsolete device entry %s", device.name)
device_reg.async_update_device(
device.id, remove_config_entry_id=self.config_entry.entry_id
)
device_reg.async_remove_device(device.id)
3 changes: 1 addition & 2 deletions homeassistant/components/airnow/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,10 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> bool:

lat = data[CONF_LATITUDE]
lng = data[CONF_LONGITUDE]
distance = data[CONF_RADIUS]

# Check that the provided latitude/longitude provide a response
try:
test_data = await client.observations.latLong(lat, lng, distance=distance)
test_data = await client.observations.latLong(lat, lng)

except InvalidKeyError as exc:
raise InvalidAuth from exc
Expand Down
1 change: 0 additions & 1 deletion homeassistant/components/airnow/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ async def _async_update_data(self) -> dict[str, Any]:
obs = await self.airnow.observations.latLong(
self.latitude,
self.longitude,
distance=self.distance,
)

except (AirNowError, ClientConnectorError, InvalidJsonError) as error:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/airnow/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["pyairnow"],
"requirements": ["pyairnow==1.3.1"]
"requirements": ["pyairnow==1.4.0"]
}
4 changes: 1 addition & 3 deletions homeassistant/components/aladdin_connect/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,4 @@ def remove_stale_devices(
break

if device_id and device_id not in all_device_ids:
device_registry.async_update_device(
device_entry.id, remove_config_entry_id=config_entry.entry_id
)
device_registry.async_remove_device(device_entry.id)
4 changes: 1 addition & 3 deletions homeassistant/components/bang_olufsen/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ async def async_setup_entry(
if device.model == BeoModel.BEOREMOTE_ONE and device.serial_number not in {
remote.serial_number for remote in remotes
}:
device_registry.async_update_device(
device.id, remove_config_entry_id=config_entry.entry_id
)
device_registry.async_remove_device(device.id)

async_add_entities(new_entities=entities)

Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/bring/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,7 @@ def _purge_deleted_lists(self) -> None:
):
if not set(device.identifiers) & identifiers:
_LOGGER.debug("Removing obsolete device entry %s", device.name)
device_reg.async_update_device(
device.id, remove_config_entry_id=self.config_entry.entry_id
)
device_reg.async_remove_device(device.id)


class BringActivityCoordinator(BringBaseCoordinator[dict[str, BringActivityData]]):
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/duco/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"iot_class": "local_polling",
"loggers": ["duco_connectivity"],
"quality_scale": "platinum",
"requirements": ["python-duco-connectivity==0.9.0"],
"requirements": ["python-duco-connectivity==0.10.0"],
"zeroconf": [
{
"name": "duco [[][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][]].*",
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/emby/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
CONF_HOST,
CONF_PORT,
CONF_SSL,
DEVICE_DEFAULT_NAME,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
)
Expand Down Expand Up @@ -179,7 +178,7 @@ def supports_remote_control(self):
@override
def name(self):
"""Return the name of the device."""
return f"Emby {self.device.name}" or DEVICE_DEFAULT_NAME
return f"Emby {self.device.name}"

@property
@override
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/fritz/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def repair_issue_cleanup(hass: HomeAssistant, avm_wrapper: AvmWrapper) -> None:
domain=DOMAIN,
issue_id="deprecated_cleanup_button",
is_fixable=False,
is_persistent=True,
is_persistent=False,
severity=ir.IssueSeverity.WARNING,
translation_key="deprecated_cleanup_button",
translation_placeholders={"removal_version": "2026.11.0"},
Expand All @@ -114,7 +114,7 @@ def repair_issue_firmware_update(hass: HomeAssistant, avm_wrapper: AvmWrapper) -
domain=DOMAIN,
issue_id="deprecated_firmware_update_button",
is_fixable=False,
is_persistent=True,
is_persistent=False,
severity=ir.IssueSeverity.WARNING,
translation_key="deprecated_firmware_update_button",
translation_placeholders={"removal_version": "2026.11.0"},
Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/fritz/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,9 +743,7 @@ async def async_trigger_cleanup(self) -> None:
):
if not any(con in device.connections for con in valid_connections):
_LOGGER.debug("Removing obsolete device entry %s", device.name)
device_reg.async_update_device(
device.id, remove_config_entry_id=config_entry.entry_id
)
device_reg.async_remove_device(device.id)

fritz_data = self.hass.data[FRITZ_DATA_KEY]

Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/fritzbox/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,7 @@ def cleanup_removed_devices(self, data: FritzboxCoordinatorData) -> None:
):
if not set(device.identifiers) & identifiers:
LOGGER.debug("Removing obsolete device entry %s", device.name)
device_reg.async_update_device(
device.id, remove_config_entry_id=self.config_entry.entry_id
)
device_reg.async_remove_device(device.id)

def _update_fritz_devices(self) -> FritzboxCoordinatorData:
"""Update all fritzbox device data."""
Expand Down
82 changes: 57 additions & 25 deletions homeassistant/components/gatus/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,33 +46,25 @@ async def async_step_user(
errors: dict[str, str] = {}

if user_input is not None:
user_input[CONF_URL] = str(
URL(user_input[CONF_URL])
.with_query(None)
.with_fragment(None)
.with_user(None)
.with_password(None)
).rstrip("/")

self._async_abort_entries_match({CONF_URL: user_input[CONF_URL]})

try:
url = URL(user_input[CONF_URL])
except ValueError:
errors["base"] = "invalid_url"
await validate_input(self.hass, user_input)
except CannotConnect:
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected exception during Gatus setup")
errors["base"] = "unknown"
else:
if url.scheme not in {"http", "https"} or not url.host:
errors["base"] = "invalid_url"
else:
normalized_url = str(
url.with_query(None)
.with_fragment(None)
.with_user(None)
.with_password(None)
).rstrip("/")
user_input[CONF_URL] = normalized_url

self._async_abort_entries_match({CONF_URL: normalized_url})

try:
await validate_input(self.hass, user_input)
except CannotConnect:
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected exception during Gatus setup")
errors["base"] = "unknown"
else:
return self.async_create_entry(title="Gatus", data=user_input)
return self.async_create_entry(title="Gatus", data=user_input)

return self.async_show_form(
step_id="user",
Expand All @@ -82,6 +74,46 @@ async def async_step_user(
errors=errors,
)

async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration of an existing entry."""
errors: dict[str, str] = {}
reconfigure_entry = self._get_reconfigure_entry()

if user_input is not None:
url = URL(user_input[CONF_URL])
user_input[CONF_URL] = str(
url.with_query(None)
.with_fragment(None)
.with_user(None)
.with_password(None)
).rstrip("/")

if user_input[CONF_URL] != reconfigure_entry.data[CONF_URL]:
self._async_abort_entries_match({CONF_URL: user_input[CONF_URL]})

try:
await validate_input(self.hass, user_input)
except CannotConnect:
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected exception during Gatus reconfigure")
errors["base"] = "unknown"
else:
return self.async_update_reload_and_abort(
reconfigure_entry,
data_updates=user_input,
)

return self.async_show_form(
step_id="reconfigure",
data_schema=self.add_suggested_values_to_schema(
STEP_USER_DATA_SCHEMA, user_input or reconfigure_entry.data
),
errors=errors,
)


class CannotConnect(HomeAssistantError):
"""Error to indicate we cannot connect to the server."""
2 changes: 1 addition & 1 deletion homeassistant/components/gatus/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ rules:
icon-translations:
status: exempt
comment: Entities use the connectivity device class for their icon and define no custom icons.
reconfiguration-flow: todo
reconfiguration-flow: done
repair-issues:
status: exempt
comment: Integration does not require user intervention repairs.
Expand Down
13 changes: 11 additions & 2 deletions homeassistant/components/gatus/strings.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_url": "Please enter a valid absolute URL (e.g., http://192.168.1.50:8080)",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"step": {
"reconfigure": {
"data": {
"url": "[%key:common::config_flow::data::url%]"
},
"data_description": {
"url": "[%key:component::gatus::config::step::user::data_description::url%]"
},
"description": "[%key:component::gatus::config::step::user::description%]"
},
"user": {
"data": {
"url": "[%key:common::config_flow::data::url%]"
Expand Down
5 changes: 1 addition & 4 deletions homeassistant/components/growatt_server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,10 +453,7 @@ async def _async_scan_for_new_devices(_now: datetime.datetime) -> None:
for device_sn in device_domain_ids:
if coordinator := runtime_data.devices.pop(device_sn, None):
await coordinator.async_shutdown()
device_registry.async_update_device(
device_entry.id,
remove_config_entry_id=config_entry.entry_id,
)
device_registry.async_remove_device(device_entry.id)

# Add new devices
new_coordinators: list[GrowattCoordinator] = []
Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/home_connect/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: HomeConnectConfigEntry)

for device in device_entries:
if not device.identifiers.intersection(appliances_identifiers):
device_registry.async_update_device(
device.id, remove_config_entry_id=entry.entry_id
)
device_registry.async_remove_device(device.id)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

Expand Down
5 changes: 1 addition & 4 deletions homeassistant/components/homee/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,7 @@ async def _connection_update_callback(connected: bool) -> None:
)
if not is_node_present:
_LOGGER.info("Removing device %s", device.name)
device_registry.async_update_device(
device_id=device.id,
remove_config_entry_id=entry.entry_id,
)
device_registry.async_remove_device(device.id)

# Remove device at runtime when node is removed in homee
async def _remove_node_callback(node: HomeeNode, add: bool) -> None:
Expand Down
6 changes: 2 additions & 4 deletions homeassistant/components/honeywell/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,8 @@ def remove_stale_devices(
# If device_id is None an invalid device entry was
# found for this config entry. If the device_id is not
# in existing device ids it's a stale device entry.
# Remove config entry from this device entry in either case.
device_registry.async_update_device(
device_entry.id, remove_config_entry_id=config_entry.entry_id
)
# Remove the device entry in either case.
device_registry.async_remove_device(device_entry.id)


class HoneywellUSThermostat(ClimateEntity):
Expand Down
10 changes: 3 additions & 7 deletions homeassistant/components/hydrawise/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,13 @@ def _add_remove_zones(self) -> None:
if removed_zones := previous_zones - current_zones:
LOGGER.debug("Removed zones: %s", ", ".join(removed_zones))
for zone_id in removed_zones:
device_registry.async_update_device(
device_id=previous_zones_by_id[zone_id].id,
remove_config_entry_id=self.config_entry.entry_id,
)
device_registry.async_remove_device(previous_zones_by_id[zone_id].id)

if removed_controllers := previous_controllers - current_controllers:
LOGGER.debug("Removed controllers: %s", ", ".join(removed_controllers))
for controller_id in removed_controllers:
device_registry.async_update_device(
device_id=previous_controllers_by_id[controller_id].id,
remove_config_entry_id=self.config_entry.entry_id,
device_registry.async_remove_device(
previous_controllers_by_id[controller_id].id
)

if new_controller_ids := current_controllers - previous_controllers:
Expand Down
Loading
Loading